fix: reject modal P1 scope claims
This commit is contained in:
@@ -545,22 +545,48 @@ elif mutation == "p1-scope-inversion":
|
|||||||
1,
|
1,
|
||||||
)
|
)
|
||||||
elif mutation.startswith("p1-append-"):
|
elif mutation.startswith("p1-append-"):
|
||||||
claims = {
|
operations = {
|
||||||
"p1-append-acquisition": "P1 owns Evidence acquisition.",
|
"acquisition": ("acquire Evidence", "acquires Evidence", "Evidence acquisition"),
|
||||||
"p1-append-materialization": "P1 owns Evidence materialization.",
|
"materialization": (
|
||||||
"p1-append-extraction": "P1 owns Evidence extraction.",
|
"materialize Evidence", "materializes Evidence", "Evidence materialization"
|
||||||
"p1-append-preprocessing": "P1 owns Evidence preprocessing.",
|
),
|
||||||
"p1-append-embeddings": "P1 owns Evidence embeddings.",
|
"extraction": ("extract Evidence", "extracts Evidence", "Evidence extraction"),
|
||||||
"p1-append-qdrant-writes": "P1 owns Evidence Qdrant writes.",
|
"preprocessing": (
|
||||||
"p1-append-indexing": "P1 owns Evidence indexing.",
|
"preprocess Evidence", "preprocesses Evidence", "Evidence preprocessing"
|
||||||
"p1-append-active": "P1 owns Evidence `ACTIVE` publication.",
|
),
|
||||||
"p1-append-retention": "P1 owns Evidence retention.",
|
"embeddings": ("create embeddings", "creates embeddings", "Evidence embeddings"),
|
||||||
"p1-append-gc": "P1 owns Evidence GC.",
|
"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)
|
forms = (
|
||||||
if claim is None:
|
"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}")
|
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":
|
elif mutation == "config-ordering":
|
||||||
changed = original.replace(
|
changed = original.replace(
|
||||||
"tht config check -c <path>", "tht -c <path> config check", 1
|
"tht config check -c <path>", "tht -c <path> config check", 1
|
||||||
@@ -593,6 +619,38 @@ PY
|
|||||||
fi
|
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() {
|
expect_guide_rejected() {
|
||||||
local label="$1" validator="$2" source_guide="$3" relative_path="$4"
|
local label="$1" validator="$2" source_guide="$3" relative_path="$4"
|
||||||
local mutation="$5" expected_error="$6"
|
local mutation="$5" expected_error="$6"
|
||||||
@@ -1001,36 +1059,51 @@ expect_evidence_fixture_rejected \
|
|||||||
expect_evidence_fixture_rejected \
|
expect_evidence_fixture_rejected \
|
||||||
"P1 Evidence scope inversion" docs/contracts/workspace-evidence-v3.md p1-scope-inversion \
|
"P1 Evidence scope inversion" docs/contracts/workspace-evidence-v3.md p1-scope-inversion \
|
||||||
"P1 scope violation"
|
"P1 scope violation"
|
||||||
expect_evidence_fixture_rejected \
|
p1_operations=(
|
||||||
"appended P1 acquisition claim" docs/contracts/workspace-evidence-v3.md p1-append-acquisition \
|
acquisition materialization extraction preprocessing embeddings
|
||||||
"P1 scope violation"
|
qdrant-writes indexing active retention gc
|
||||||
expect_evidence_fixture_rejected \
|
)
|
||||||
"appended P1 materialization claim" docs/contracts/workspace-evidence-v3.md p1-append-materialization \
|
p1_positive_forms=(
|
||||||
"P1 scope violation"
|
base third-person can may must will should
|
||||||
expect_evidence_fixture_rejected \
|
adverb-before-modal adverb-after-modal ownership
|
||||||
"appended P1 extraction claim" docs/contracts/workspace-evidence-v3.md p1-append-extraction \
|
)
|
||||||
"P1 scope violation"
|
for operation in "${p1_operations[@]}"; do
|
||||||
expect_evidence_fixture_rejected \
|
for form in "${p1_positive_forms[@]}"; do
|
||||||
"appended P1 preprocessing claim" docs/contracts/workspace-evidence-v3.md p1-append-preprocessing \
|
expect_evidence_fixture_rejected \
|
||||||
"P1 scope violation"
|
"appended P1 ${operation} ${form} claim" \
|
||||||
expect_evidence_fixture_rejected \
|
docs/contracts/workspace-evidence-v3.md "p1-append-${operation}-${form}" \
|
||||||
"appended P1 embeddings claim" docs/contracts/workspace-evidence-v3.md p1-append-embeddings \
|
"P1 scope violation"
|
||||||
"P1 scope violation"
|
done
|
||||||
expect_evidence_fixture_rejected \
|
done
|
||||||
"appended P1 Qdrant writes claim" docs/contracts/workspace-evidence-v3.md p1-append-qdrant-writes \
|
p1_safe_bases=(
|
||||||
"P1 scope violation"
|
"acquire Evidence" "materialize Evidence" "extract Evidence" "preprocess Evidence"
|
||||||
expect_evidence_fixture_rejected \
|
"create embeddings" "write embeddings to Qdrant" "index Evidence" 'publish `ACTIVE`'
|
||||||
"appended P1 indexing claim" docs/contracts/workspace-evidence-v3.md p1-append-indexing \
|
"retain Evidence" "garbage-collect Evidence"
|
||||||
"P1 scope violation"
|
)
|
||||||
expect_evidence_fixture_rejected \
|
p1_safe_third_person=(
|
||||||
"appended P1 ACTIVE publication claim" docs/contracts/workspace-evidence-v3.md p1-append-active \
|
"acquires Evidence" "materializes Evidence" "extracts Evidence" "preprocesses Evidence"
|
||||||
"P1 scope violation"
|
"creates embeddings" "writes embeddings to Qdrant" "indexes Evidence" 'publishes `ACTIVE`'
|
||||||
expect_evidence_fixture_rejected \
|
"retains Evidence" "garbage-collects Evidence"
|
||||||
"appended P1 retention claim" docs/contracts/workspace-evidence-v3.md p1-append-retention \
|
)
|
||||||
"P1 scope violation"
|
p1_safe_ownership=(
|
||||||
expect_evidence_fixture_rejected \
|
"Evidence acquisition" "Evidence materialization" "Evidence extraction"
|
||||||
"appended P1 garbage collection claim" docs/contracts/workspace-evidence-v3.md p1-append-gc \
|
"Evidence preprocessing" "Evidence embeddings" "Evidence Qdrant writes"
|
||||||
"P1 scope violation"
|
"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 \
|
expect_evidence_fixture_rejected \
|
||||||
"config check option reordered" docs/contracts/workspace-evidence-v3.md config-ordering \
|
"config check option reordered" docs/contracts/workspace-evidence-v3.md config-ordering \
|
||||||
"exact config-check ordering missing"
|
"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")):
|
if not all(token in p6 for token in ("commit-addressed materialization", "realpath", "recursive containment", "nested-symlink", "race")):
|
||||||
raise SystemExit("missing P6 materialization ownership")
|
raise SystemExit("missing P6 materialization ownership")
|
||||||
no_scope = "P1 performs no acquisition, extraction, preprocessing/indexing, embeddings, Qdrant writes, `ACTIVE` publication, retention, or GC."
|
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(
|
positive_p1_operation = re.compile(
|
||||||
r"""\bP1\b(?:\s+(?:also|then|now|directly|itself))*\s+(?:
|
rf"""\bP1\b{p1_adverbs}\s+(?:
|
||||||
(?:will\s+|must\s+|may\s+|can\s+)?(?:
|
(?:(?:can|may|must|will|should|does){p1_adverbs}\s+){p1_base_operation}|
|
||||||
acquires?|materializes?|extracts?|preprocesses?|indexes?|retains?|
|
{p1_third_person_operation}|
|
||||||
(?:creates?|generates?)\s+embeddings?|
|
{p1_ownership}
|
||||||
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
|
|
||||||
)
|
|
||||||
)\b""",
|
)\b""",
|
||||||
re.IGNORECASE | re.VERBOSE,
|
re.IGNORECASE | re.VERBOSE,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user