fix: reject modal P1 scope claims
This commit is contained in:
@@ -545,22 +545,48 @@ elif mutation == "p1-scope-inversion":
|
||||
1,
|
||||
)
|
||||
elif mutation.startswith("p1-append-"):
|
||||
claims = {
|
||||
"p1-append-acquisition": "P1 owns Evidence acquisition.",
|
||||
"p1-append-materialization": "P1 owns Evidence materialization.",
|
||||
"p1-append-extraction": "P1 owns Evidence extraction.",
|
||||
"p1-append-preprocessing": "P1 owns Evidence preprocessing.",
|
||||
"p1-append-embeddings": "P1 owns Evidence embeddings.",
|
||||
"p1-append-qdrant-writes": "P1 owns Evidence Qdrant writes.",
|
||||
"p1-append-indexing": "P1 owns Evidence indexing.",
|
||||
"p1-append-active": "P1 owns Evidence `ACTIVE` publication.",
|
||||
"p1-append-retention": "P1 owns Evidence retention.",
|
||||
"p1-append-gc": "P1 owns Evidence GC.",
|
||||
operations = {
|
||||
"acquisition": ("acquire Evidence", "acquires Evidence", "Evidence acquisition"),
|
||||
"materialization": (
|
||||
"materialize Evidence", "materializes Evidence", "Evidence materialization"
|
||||
),
|
||||
"extraction": ("extract Evidence", "extracts Evidence", "Evidence extraction"),
|
||||
"preprocessing": (
|
||||
"preprocess Evidence", "preprocesses Evidence", "Evidence preprocessing"
|
||||
),
|
||||
"embeddings": ("create embeddings", "creates embeddings", "Evidence embeddings"),
|
||||
"qdrant-writes": (
|
||||
"write embeddings to Qdrant", "writes embeddings to Qdrant",
|
||||
"Evidence Qdrant writes",
|
||||
),
|
||||
"indexing": ("index Evidence", "indexes Evidence", "Evidence indexing"),
|
||||
"active": ("publish `ACTIVE`", "publishes `ACTIVE`", "Evidence `ACTIVE` publication"),
|
||||
"retention": ("retain Evidence", "retains Evidence", "Evidence retention"),
|
||||
"gc": ("garbage-collect Evidence", "garbage-collects Evidence", "Evidence GC"),
|
||||
}
|
||||
claim = claims.get(mutation)
|
||||
if claim is None:
|
||||
forms = (
|
||||
"base", "third-person", "can", "may", "must", "will", "should",
|
||||
"adverb-before-modal", "adverb-after-modal", "ownership",
|
||||
)
|
||||
suffix = mutation.removeprefix("p1-append-")
|
||||
operation = next((name for name in operations if suffix.startswith(f"{name}-")), None)
|
||||
form = suffix.removeprefix(f"{operation}-") if operation else ""
|
||||
if operation is None or form not in forms:
|
||||
raise SystemExit(f"unknown P1 append mutation: {mutation}")
|
||||
changed = original + f"\n{claim}\n"
|
||||
base, third_person, ownership = operations[operation]
|
||||
claims = {
|
||||
"base": f"P1 does {base}.",
|
||||
"third-person": f"P1 {third_person}.",
|
||||
"can": f"P1 can {base}.",
|
||||
"may": f"P1 may {base}.",
|
||||
"must": f"P1 must {base}.",
|
||||
"will": f"P1 will {base}.",
|
||||
"should": f"P1 should {base}.",
|
||||
"adverb-before-modal": f"P1 directly may {base}.",
|
||||
"adverb-after-modal": f"P1 may directly {base}.",
|
||||
"ownership": f"P1 owns {ownership}.",
|
||||
}
|
||||
changed = original + f"\n{claims[form]}\n"
|
||||
elif mutation == "config-ordering":
|
||||
changed = original.replace(
|
||||
"tht config check -c <path>", "tht -c <path> config check", 1
|
||||
@@ -593,6 +619,38 @@ PY
|
||||
fi
|
||||
}
|
||||
|
||||
expect_evidence_claim_accepted() {
|
||||
local label="$1" claim="$2"
|
||||
local fixture_root="$negative_root/evidence-safe-${label// /-}"
|
||||
local fixture_output="$fixture_root/output"
|
||||
|
||||
mkdir -p \
|
||||
"$fixture_root/deploy/workspaces" \
|
||||
"$fixture_root/docs/contracts" \
|
||||
"$fixture_root/docs/install/examples"
|
||||
cp "$root/deploy/workspaces/example.yaml" "$fixture_root/deploy/workspaces/example.yaml"
|
||||
cp "$root/deploy/workspaces/psd.yaml.example" "$fixture_root/deploy/workspaces/psd.yaml.example"
|
||||
cp "$root/docs/contracts/workspace-evidence-v3.md" \
|
||||
"$fixture_root/docs/contracts/workspace-evidence-v3.md"
|
||||
cp "$root/docs/install/local-workspace-registry.md" \
|
||||
"$fixture_root/docs/install/local-workspace-registry.md"
|
||||
cp "$root/docs/install/server-workspace-registry.md" \
|
||||
"$fixture_root/docs/install/server-workspace-registry.md"
|
||||
cp "$root/docs/install/examples/workspace-bindings.env.example" \
|
||||
"$fixture_root/docs/install/examples/workspace-bindings.env.example"
|
||||
printf '\n%s\n' "$claim" >>"$fixture_root/docs/contracts/workspace-evidence-v3.md"
|
||||
|
||||
set +e
|
||||
verify_workspace_evidence_contract "$fixture_root" >"$fixture_output" 2>&1
|
||||
local status=$?
|
||||
set -e
|
||||
if [[ $status -ne 0 ]]; then
|
||||
echo "safe Evidence fixture rejected: $label" >&2
|
||||
cat "$fixture_output" >&2
|
||||
negative_failures=$((negative_failures + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
expect_guide_rejected() {
|
||||
local label="$1" validator="$2" source_guide="$3" relative_path="$4"
|
||||
local mutation="$5" expected_error="$6"
|
||||
@@ -1001,36 +1059,51 @@ expect_evidence_fixture_rejected \
|
||||
expect_evidence_fixture_rejected \
|
||||
"P1 Evidence scope inversion" docs/contracts/workspace-evidence-v3.md p1-scope-inversion \
|
||||
"P1 scope violation"
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 acquisition claim" docs/contracts/workspace-evidence-v3.md p1-append-acquisition \
|
||||
"P1 scope violation"
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 materialization claim" docs/contracts/workspace-evidence-v3.md p1-append-materialization \
|
||||
"P1 scope violation"
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 extraction claim" docs/contracts/workspace-evidence-v3.md p1-append-extraction \
|
||||
"P1 scope violation"
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 preprocessing claim" docs/contracts/workspace-evidence-v3.md p1-append-preprocessing \
|
||||
"P1 scope violation"
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 embeddings claim" docs/contracts/workspace-evidence-v3.md p1-append-embeddings \
|
||||
"P1 scope violation"
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 Qdrant writes claim" docs/contracts/workspace-evidence-v3.md p1-append-qdrant-writes \
|
||||
"P1 scope violation"
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 indexing claim" docs/contracts/workspace-evidence-v3.md p1-append-indexing \
|
||||
"P1 scope violation"
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 ACTIVE publication claim" docs/contracts/workspace-evidence-v3.md p1-append-active \
|
||||
"P1 scope violation"
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 retention claim" docs/contracts/workspace-evidence-v3.md p1-append-retention \
|
||||
"P1 scope violation"
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 garbage collection claim" docs/contracts/workspace-evidence-v3.md p1-append-gc \
|
||||
"P1 scope violation"
|
||||
p1_operations=(
|
||||
acquisition materialization extraction preprocessing embeddings
|
||||
qdrant-writes indexing active retention gc
|
||||
)
|
||||
p1_positive_forms=(
|
||||
base third-person can may must will should
|
||||
adverb-before-modal adverb-after-modal ownership
|
||||
)
|
||||
for operation in "${p1_operations[@]}"; do
|
||||
for form in "${p1_positive_forms[@]}"; do
|
||||
expect_evidence_fixture_rejected \
|
||||
"appended P1 ${operation} ${form} claim" \
|
||||
docs/contracts/workspace-evidence-v3.md "p1-append-${operation}-${form}" \
|
||||
"P1 scope violation"
|
||||
done
|
||||
done
|
||||
p1_safe_bases=(
|
||||
"acquire Evidence" "materialize Evidence" "extract Evidence" "preprocess Evidence"
|
||||
"create embeddings" "write embeddings to Qdrant" "index Evidence" 'publish `ACTIVE`'
|
||||
"retain Evidence" "garbage-collect Evidence"
|
||||
)
|
||||
p1_safe_third_person=(
|
||||
"acquires Evidence" "materializes Evidence" "extracts Evidence" "preprocesses Evidence"
|
||||
"creates embeddings" "writes embeddings to Qdrant" "indexes Evidence" 'publishes `ACTIVE`'
|
||||
"retains Evidence" "garbage-collects Evidence"
|
||||
)
|
||||
p1_safe_ownership=(
|
||||
"Evidence acquisition" "Evidence materialization" "Evidence extraction"
|
||||
"Evidence preprocessing" "Evidence embeddings" "Evidence Qdrant writes"
|
||||
"Evidence indexing" 'Evidence `ACTIVE` publication' "Evidence retention" "Evidence GC"
|
||||
)
|
||||
for index in "${!p1_operations[@]}"; do
|
||||
operation="${p1_operations[$index]}"
|
||||
base="${p1_safe_bases[$index]}"
|
||||
third_person="${p1_safe_third_person[$index]}"
|
||||
ownership="${p1_safe_ownership[$index]}"
|
||||
expect_evidence_claim_accepted "negative P1 ${operation} cannot" "P1 cannot ${base}."
|
||||
expect_evidence_claim_accepted "negative P1 ${operation} must not" "P1 must not ${base}."
|
||||
expect_evidence_claim_accepted "negative P1 ${operation} does not" "P1 does not ${base}."
|
||||
expect_evidence_claim_accepted "negative P1 ${operation} never" "P1 never ${third_person}."
|
||||
expect_evidence_claim_accepted \
|
||||
"later plan ${operation}" "A later plan may assign ${ownership} to P1."
|
||||
expect_evidence_claim_accepted "P2 ${operation}" "P2 may directly ${base}."
|
||||
expect_evidence_claim_accepted "P6 ${operation}" "P6 ${third_person}."
|
||||
done
|
||||
expect_evidence_fixture_rejected \
|
||||
"config check option reordered" docs/contracts/workspace-evidence-v3.md config-ordering \
|
||||
"exact config-check ordering missing"
|
||||
|
||||
@@ -406,21 +406,35 @@ if not all(token in p1 for token in ("lexical URI", "Git tree", "same commit", "
|
||||
if not all(token in p6 for token in ("commit-addressed materialization", "realpath", "recursive containment", "nested-symlink", "race")):
|
||||
raise SystemExit("missing P6 materialization ownership")
|
||||
no_scope = "P1 performs no acquisition, extraction, preprocessing/indexing, embeddings, Qdrant writes, `ACTIVE` publication, retention, or GC."
|
||||
p1_adverbs = r"(?:\s+(?:also|then|now|directly|itself))*"
|
||||
p1_base_operation = r"""(?:
|
||||
acquire|materialize|extract|preprocess|index|retain|
|
||||
(?:create|generate)\s+embeddings?|
|
||||
write\s+(?:embeddings?\s+)?to\s+Qdrant|
|
||||
publish\s+`?ACTIVE\b`?|
|
||||
garbage[- ]collect|
|
||||
(?:run|perform)\s+(?:retention|GC|garbage[ -]collection)
|
||||
)"""
|
||||
p1_third_person_operation = r"""(?:
|
||||
acquires|materializes|extracts|preprocesses|indexes|retains|
|
||||
(?:creates|generates)\s+embeddings?|
|
||||
writes\s+(?:embeddings?\s+)?to\s+Qdrant|
|
||||
publishes\s+`?ACTIVE\b`?|
|
||||
garbage[- ]collects|
|
||||
(?:runs|performs)\s+(?:retention|GC|garbage[ -]collection)
|
||||
)"""
|
||||
p1_ownership = r"""(?:
|
||||
(?:owns|handles|performs)|is\s+responsible\s+for
|
||||
)\s+(?:Evidence\s+)?(?:
|
||||
acquisition|materialization|extraction|preprocessing|embeddings?|
|
||||
Qdrant\s+writes?|indexing|`?ACTIVE`?\s+publication|retention|GC|
|
||||
garbage[ -]collection
|
||||
)"""
|
||||
positive_p1_operation = re.compile(
|
||||
r"""\bP1\b(?:\s+(?:also|then|now|directly|itself))*\s+(?:
|
||||
(?:will\s+|must\s+|may\s+|can\s+)?(?:
|
||||
acquires?|materializes?|extracts?|preprocesses?|indexes?|retains?|
|
||||
(?:creates?|generates?)\s+embeddings?|
|
||||
writes?\s+(?:embeddings?\s+)?to\s+Qdrant|
|
||||
publishes?\s+`?ACTIVE\b`?|
|
||||
garbage[- ]collects?|
|
||||
(?:runs?|performs?)\s+(?:retention|GC|garbage[ -]collection)
|
||||
)|
|
||||
(?:owns?|handles?|performs?|is\s+responsible\s+for)\s+(?:Evidence\s+)?(?:
|
||||
acquisition|materialization|extraction|preprocessing|embeddings?|
|
||||
Qdrant\s+writes?|indexing|`?ACTIVE`?\s+publication|retention|GC|
|
||||
garbage[ -]collection
|
||||
)
|
||||
rf"""\bP1\b{p1_adverbs}\s+(?:
|
||||
(?:(?:can|may|must|will|should|does){p1_adverbs}\s+){p1_base_operation}|
|
||||
{p1_third_person_operation}|
|
||||
{p1_ownership}
|
||||
)\b""",
|
||||
re.IGNORECASE | re.VERBOSE,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user