Files
Edward Cheng-I Wu e7e775a0e1 feat(v3.6.7 Step 6 Phase 6.7): downstream agent partial inversion sweep + INV-1/2/3 lint (10 codex rounds, 0 P1) (#66)
* feat(v3.6.7 Step 6 Phase 6.7): downstream agent partial inversion sweep + INV-1/2/3 lint

Implements Phase 6.7 deliverable per spec §10 (line 2391-2403):

Prompt edits per §6.2 sweep table:
- synthesis_agent.md line 164: remove "Cross-model audit follows ..."
  sentence (Clause 2(b)+(d) violation); append canonical Clause 1 line
  bullet to PATTERN PROTECTION block.
- research_architect_agent.md line 190: remove "Cross-model audit covers
  these via dimension §3.5 ..." sentence (Clause 2(b)+(d) violation);
  append canonical Clause 1 line.
- report_compiler_agent.md line 172: remove "Cross-model audit covers
  these via dimension §3.7 ..." sentence; merge prior line 177 (trim
  "The orchestrator runs codex audit afterward" tail) and line 178
  ("Output metadata must not claim audit-passed state") into the single
  canonical Clause 1 line bullet.

Manifest (new): scripts/v3_6_7_inversion_manifest.json
- Three-file scope per §6.3; locks the v3.6.7 sweep boundary so
  widening to a fourth file requires explicit §9 L2 resolution.

Lint extension: scripts/check_v3_6_7_pattern_protection.py
- INV-1: canonical Clause 1 line appears exactly once in each manifest
  file's PATTERN PROTECTION block (presence + uniqueness).
- INV-2: zero hits across four Clause 2 disclosure regex patterns
  (a) the orchestrator + audit, (b) cross-model audit follows/covers
  + codex_audit_multifile_template, (c) audit afterwards/will be run/
  is dispatched, (d) downstream audit / this output is/will be audited.
  All compiled with re.IGNORECASE per §6.3.
- INV-3: scans deep-research/agents/ + academic-pipeline/agents/ for
  the canonical line; any hit outside the manifest fails (defends
  against accidental sweep widening, guards §9 L2 deferred question).
- C3 regex consolidation: prior C1-C3 had two separate regexes for
  (anti-fake-audit pair) and (output-metadata audit-passed prohibition)
  bullets. Phase 6.7 merges them into a single whole-line regex matching
  the canonical Clause 1 line so all three prohibition tokens
  (DO NOT × 2 + must not) sit inside the matched span — the negation
  post-filter no longer flags a sibling prohibition as a weakener
  (B2 R3-001 span-restricted exemption).

Tests: scripts/test_check_v3_6_7_pattern_protection.py (+12 tests)
- INV-1 negative × 4: canonical line deletion (synthesis / architect /
  compiler) + duplication (synthesis).
- INV-2 negative × 5: inject one canonical violation phrase per pattern
  (a)/(b)/(c) and two for (d)'s alternation.
- INV-3 negative × 3: canonical line in non-manifest agent file
  (bibliography_agent), manifest shrunk to two files, manifest extra
  entry pointing to a file without the canonical line.
- Removed obsolete R3MutationTests.test_r3_001_orchestrator_does_not_run_fails
  whose mutation source ("The orchestrator runs codex audit afterward.")
  no longer exists post-sweep.

Verification gate per spec §10 line 2403:
- check_v3_6_7_pattern_protection.py: 12/12 passed (9 existing checks +
  INV-1/2/3 aggregated)
- test suite: 40/40 tests green (28 existing - 1 obsolete + 13 new INV tests
  including BaselineTest)

Phase 6.6 PR #65 was shipped standalone; Phase 6.7 follows as a separate
PR per spec §10 line 2401's bundling recommendation deferred (Phase 6.6
shipped first due to budget reconciliation patch in PR #64).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(v3.6.7 Phase 6.7 tests): update C3 audit-passed deletion mutation source post-canonical-line merge

Phase 6.7 merged line 178 ("Output metadata must not claim audit-passed
state.") into the canonical Clause 1 line bullet. The standalone bullet
form (preceded by `\n- `) no longer exists, so the prior
test_c3_audit_passed_sentence_deleted_fails mutation source string is
not found and the test errors out. Update the mutation to delete just
the audit-passed segment from within the merged canonical line bullet;
the assertion (lint must fail) is unchanged.

Verified: 40/40 tests pass after this fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(v3.6.7 Phase 6.7 R1): close 3 codex P2 INV bypass findings

Codex R1 review (model=gpt-5.5 + xhigh, --base main) surfaced three P2
findings against the initial INV-1/INV-2/INV-3 implementation. All three
allowed full lint PASS while the spec §6.3 invariant was violated.

R1-001 P2 — INV-2 wrapped disclosure bypass
- Pre-fix: 4 INV-2 patterns compiled with `re.IGNORECASE` only. `.*`
  did not cross newlines, so a Markdown soft-wrap of a forbidden Clause
  2 sentence (e.g. "the orchestrator" on one line, "audit" on the
  next) reported PASS despite the disclosure being intact.
- Fix: compile all four INV-2 patterns with `re.IGNORECASE | re.DOTALL`
  so `.` crosses newlines.

R1-002 P2 — INV-1 canonical-with-tail-weakener bypass
- Pre-fix: CANONICAL_CLAUSE_1_RE ended at `state\b`, so an agent prompt
  could keep the canonical bullet's three sentences yet append a tail
  weakener (` if feasible.`, `; this is recommended.`) and INV-1 still
  matched the prefix. The spec §6.3 INV-1 rule requires the bullet
  text to match verbatim, which means anchored to the sentence end.
- Fix: re-compile with `re.MULTILINE` and end the pattern at
  `state\.\s*$` so the third sentence must be the bullet's terminal
  content. Apply the parallel `state\.(?=\s|\Z)` anchor to the C3
  whole-line regex inside the C1-C3 Check (defense in depth — the
  pre-existing `_ALWAYS_NEGATION_PATTERNS` already lists `if feasible`
  / `when possible` / `where feasible` etc., but the regex anchor
  closes the gap before negation post-filter is even consulted).

R1-003 P2 — Manifest copy-paste widening attack
- Pre-fix: `_load_inversion_manifest()` only validated that `scope ==
  "v3.6.7-only"` and `files` was a list of strings. An attacker (or
  well-intentioned editor) could append a fourth file to the manifest
  AND copy the canonical bullet into that file's PATTERN PROTECTION
  block. INV-1 then passed for all four files (canonical present
  everywhere). INV-3 silently skipped the new file because it was now
  in `manifest_set`. End result: full lint PASS, sweep silently
  widened, spec §9 L2 deferred question marked decided without
  resolution.
- Fix: introduce module-level `EXPECTED_MANIFEST_FILES` tuple frozen at
  the v3.6.7 three-file scope. `_load_inversion_manifest()` now
  validates `set(files) == set(EXPECTED_MANIFEST_FILES)` (exact match,
  no superset / no drift) AND uniqueness (`len(files) ==
  len(set(files))`). Per spec §6.3 line 1807, future v3.6.8+ widening
  must land its own version-tagged manifest with its own scope tag,
  not retroactively edit v3.6.7's manifest.

Tests added (4): each closure pair has at least one mutation regression
test in CodexR1MutationTests covering the bypass codex demonstrated:
- test_r1_p2_inv2_wrapped_disclosure_fails (R1-001)
- test_r1_p2_inv1_canonical_with_tail_weakener_fails (R1-002)
- test_r1_p2_manifest_widening_with_canonical_copy_fails (R1-003)
- test_r1_p2_manifest_duplicate_entry_fails (R1-003 defense in depth)

Verified: 44/44 tests pass (40 prior + 4 new R1 tests). 12/12 lint
checks pass on clean working tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(v3.6.7 Phase 6.7 R2): close 2 codex P2 INV bullet-extraction findings

Codex R2 review (model=gpt-5.5 + xhigh, --base main, after R1 fixes
landed) surfaced two more P2 findings — both about the granularity
mismatch between the spec's per-bullet semantics and the regex's
substring/cross-bullet matching.

R2-001 P2 — INV-1 false negative on bullet-prefix weakener
- Pre-R2: CANONICAL_CLAUSE_1_RE was a regex search-anywhere with
  re.MULTILINE + `\.\s*$`. A bullet of the form `- When feasible, DO
  NOT simulate ...` carried the canonical sentences as a substring;
  the regex still matched the suffix and INV-1 reported PASS. The
  spec §6.3 INV-1 rule says "exactly one bullet whose text matches
  the canonical Clause 1 line verbatim" — substring match is not
  verbatim.
- Pre-R2 dual: the same anchor rejected harmless soft-wraps (e.g. a
  Markdown reflow line break between `run` and `codex/external`).
- Fix: introduce `_iter_bullets()` helper that walks `^- ` markers,
  collapses each bullet's whitespace runs (including newlines from
  soft wraps) to single spaces, and yields normalized bullet text.
  INV-1 now compares each bullet's normalized text byte-for-byte
  against `CANONICAL_CLAUSE_1_TEXT`. Soft-wrap tolerance preserved;
  prefix weakener rejected; CANONICAL_CLAUSE_1_RE retired.

R2-002 P2 — INV-2 false positive across bullets
- Pre-R2: INV-2(a) `\bthe orchestrator\b.*\baudit\b` (and INV-2(b))
  compiled with re.IGNORECASE | re.DOTALL applied to raw block text.
  The unbounded `.*` would match across bullet boundaries — a benign
  `the orchestrator` mention in one bullet plus the canonical bullet's
  `audit step` mention in another would false-positive even though
  no single sentence said the orchestrator runs an audit.
- Fix: `_inv2_check_file` now iterates `_iter_bullets(block)` and
  applies each pattern against one bullet's normalized text at a
  time. The canonical Clause 1 bullet is skipped (it inherently
  contains "audit step" / "audit-passed state" tokens but is not a
  disclosure). Patterns now compile with re.IGNORECASE only — DOTALL
  is unnecessary because soft-wrap tolerance comes from the
  whitespace-normalization step.

R2-derived consistency: INV-3 also now operates at bullet
granularity. Scanning the whole file as bullet sequence eliminates
false positives where a non-manifest agent prompt happens to discuss
the canonical wording in prose (a heading or paragraph mention does
not count as the canonical bullet).

Tests added (4): each closure has at least one regression test in
CodexR2MutationTests, plus one positive test for the soft-wrap
tolerance side of R2-001 and one for the cross-bullet false-positive
side of R2-002. Defense-in-depth test confirms a real in-bullet
INV-2 violation still fails after the per-bullet refactor.

Verified: 48/48 tests pass (44 prior + 4 new R2 tests). 12/12 lint
checks pass on clean working tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(v3.6.7 Phase 6.7 R3): close codex P2 INV-2 prose-paragraph regression

Codex R3 review (model=gpt-5.5 + xhigh, --base main, after R2 fixes
landed) surfaced one P2 — the R2 per-bullet refactor of INV-2 dropped
coverage of the intro paragraph, which is exactly where the Phase 6.7
sweep removed Clause 2 disclosures.

R3-001 P2 — INV-2 misses non-bullet prose disclosures
- Pre-R3: `_inv2_check_file` iterated only bullets (`_iter_bullets`).
  But the three sentences removed by the §6.2 sweep
  (`Cross-model audit follows ... codex_audit_multifile_template.md`)
  were intro-paragraph prose, not bullets. The original forbidden
  pipeline leak could be re-added to the intro paragraph and lint
  silently passed all 12 checks.
- Fix: introduce `_iter_block_segments(block)` helper that yields
  `(offset, kind, normalized_text)` for every matched-content
  segment — both `prose` paragraphs (intro paragraph; future inline
  paragraphs) AND `bullet` items. INV-2 now iterates segments,
  whitespace-normalizing each (preserving R2's soft-wrap tolerance
  and bounded-`.*` invariant), and applies the four patterns against
  one segment at a time.

Heading line `## PATTERN PROTECTION (v3.6.7)` is excluded — it is
content marker, not contract content. The canonical Clause 1 bullet
is still skipped (it inherently carries `audit step` /
`audit-passed state` tokens; it is the required prohibition, not a
disclosure).

Tests added (1): `test_r3_p2_inv2_intro_paragraph_disclosure_fails`
re-introduces the synthesis_agent intro paragraph's original
`Cross-model audit follows ... codex_audit_multifile_template.md`
sentence and asserts INV-2 catches it as a `prose` violation. The
diagnostic includes the segment kind for debuggability.

Verified: 49/49 tests pass. 12/12 lint checks pass on clean working
tree. Manual probe confirms R3 attack vector (re-add Clause 2 prose
to intro paragraph) now fails with "[INV-2(b)] ... Clause 2
violation in prose: ...".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(v3.6.7 Phase 6.7 R4): close codex P2 INV-2 post-bullet prose regression

Codex R4 review (model=gpt-5.5 + xhigh, --base main, after R3 fixes
landed) surfaced one more P2 — R3's segmentation only walked prose
paragraphs in `block[:first_bullet]`, missing prose appended after
the canonical bullet.

R4-001 P2 — INV-2 misses post-bullet prose disclosures
- Pre-R4: `_iter_block_segments` extracted prose paragraphs from
  `block[:first_bullet]` only (intro paragraph + paragraphs before
  the first `- ` marker), then appended bullet items. Any prose
  appended AFTER the bullet list — e.g. a trailing paragraph saying
  "The orchestrator runs codex audit afterward." after the canonical
  bullet — fell outside the segmentation window and silently passed
  the four INV-2 patterns.
- Fix: rewrite `_iter_block_segments` to paragraph-split the ENTIRE
  block (`re.split(r"\n\s*\n", block)`) and classify each non-empty
  paragraph as either a bullet group (starts with `- `, walked via
  `_iter_bullets`) or a prose paragraph. Bullet-vs-prose discrimination
  is uniform across the whole block, so:
    * intro paragraph → prose
    * first bullet group → bullets (one or more)
    * inserted paragraph between bullet groups → prose
    * trailing paragraph after last bullet → prose
  The promise is now: every non-heading-non-empty character in the
  block lands in one segment of the segmentation, no carve-out.

Tests added (1): `test_r4_p2_inv2_post_bullet_prose_disclosure_fails`
appends "The orchestrator runs codex audit afterward." as a prose
paragraph immediately after the canonical bullet in synthesis_agent
and asserts lint catches it as a Clause 2 prose violation.

Verified: 50/50 tests pass. 12/12 lint checks pass on clean working
tree. Manual probe confirms R4 attack vector now fails with
"[INV-2(a)] ... Clause 2 violation in prose: 'The orchestrator runs
codex audit afterward.'".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(v3.6.7 Phase 6.7 R5): close codex P2 C3 regex soft-wrap intolerance

Codex R5 review (model=gpt-5.5 + xhigh, --base main, after R4 fixes
landed) surfaced one P2 — the C3 Check regex still used literal
spaces inside sentences while INV-1 had been refactored to
whitespace-tolerant exact compare. CI failed on harmless Markdown
soft-wraps inside the canonical bullet.

R5-001 P2 — C3 regex rejects soft-wrapped canonical bullet
- Pre-R5: C3 regex used `\s+` only between sentences but literal
  single spaces inside each sentence. A soft-wrap inside a sentence
  (e.g. between `run` and `codex/external`) caused C3 to FAIL even
  though INV-1's bullet-text whitespace normalization correctly
  accepted it. Result: CI failed on benign Markdown reflow despite
  the v3.6.7 contract not being violated.
- Fix: every inter-token space inside the C3 regex now accepts
  `\s+`. Soft-wrap tolerance is now uniform across:
    1. INV-1 (`_iter_bullets` whitespace-normalization)
    2. C3 Check regex (this fix)
  The `\.(?=\s|\Z)` trailing anchor (R1 closure) is preserved so a
  tail weakener like ` if feasible.` is still rejected.

Tests added (1): `test_r5_p2_compiler_softwrap_canonical_passes`
mutates report_compiler's canonical bullet to wrap between `run`
and `codex/external` and asserts lint passes (rc=0). Mirror of R2's
synthesis-side soft-wrap positive test, now covering the C3 surface.

Verified: 51/51 tests pass. 12/12 lint checks pass on clean working
tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(v3.6.7 Phase 6.7 R6): close codex P2 INV-1 weakened-duplicate bypass

Codex R6 review (model=gpt-5.5 + xhigh, --base main, after R5 fixes
landed) surfaced one P2 — INV-1's "exactly one bullet equals canonical"
contract counted ONLY exact matches, leaving room for a duplicate
weakened variant alongside the exact canonical bullet.

R6-001 P2 — INV-1 ignores weakened duplicates
- Pre-R6: `_inv1_check_file` counted bullets whose normalized text
  equaled CANONICAL_CLAUSE_1_TEXT and required count == 1. An attacker
  could keep the exact canonical bullet AND add a second near-canonical
  variant (`- When feasible, DO NOT simulate ...` / `... audit-passed
  state if feasible.`); the exact-count stayed at 1, lint passed, and
  the block was left with two contradictory bullets the agent might
  internalize. The spec §6.3 INV-1 wording "exactly one bullet whose
  text matches the canonical Clause 1 line verbatim" implicitly forbids
  weakened duplicates.
- Fix: introduce `_is_clause_1_like` helper using fragment markers
  (`do not simulate`, `do not claim to have run`, `audit-passed
  state`) to identify any bullet that reads as a Clause 1 variant.
  `_inv1_check_file` now collects two bullet sets:
    * exact: bullets whose normalized text equals canonical
    * weakened: Clause 1-like bullets that are NOT exact
  Lint fails if `weakened` is non-empty (any near-duplicate present)
  OR if `len(exact) != 1`. Diagnostic enumerates the offending
  weakened bullets so the editor sees exactly which ones to fix.

The fragment markers are load-bearing pieces of the canonical
sentence — no other v3.6.7 PATTERN PROTECTION bullet legitimately
carries them, so the heuristic does not false-positive on the A1-A5 /
B1-B5 / C1-C2 obligation bullets.

Tests added (2): `test_r6_p2_inv1_weakened_duplicate_alongside_canonical_fails`
covers the prefix-weakener variant; `test_r6_p2_inv1_tail_weakened_duplicate_fails`
covers the tail-weakener variant (defense in depth).

Verified: 53/53 tests pass. 12/12 lint checks pass on clean working
tree. Manual probe confirms both R6 attack vectors now fail with
clear diagnostic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(v3.6.7 Phase 6.7 R7): close codex P2 INV-3 weakened-Clause-1 scope-leak bypass

Codex R7 review (model=gpt-5.5 + xhigh, --base main, after R6 fixes
landed) surfaced one P2 — INV-3 had the same weakened-duplicate
asymmetry that R6 closed inside manifest files: INV-3 rejected only
exact canonical bullets in non-manifest prompts, so a near-canonical
bullet outside the manifest still widened the v3.6.7 prohibition
scope silently.

R7-001 P2 — INV-3 misses Clause 1-like bullets outside the manifest
- Pre-R7: `_inv3_check` checked `bt == CANONICAL_CLAUSE_1_TEXT` for
  each bullet in non-manifest agent files. A near-canonical variant
  (`- When feasible, DO NOT simulate ...`) was not equal-byte to
  canonical, so INV-3 passed even though the same prohibition
  vocabulary was being smuggled into a fourth file.
- Fix: replace exact equality with `_is_clause_1_like` (the helper
  R6 added for INV-1's weakened-duplicate detection). The same
  fragment markers (`do not simulate`, `do not claim to have run`,
  `audit-passed state`) now flag any Clause 1-like bullet anywhere
  in a non-manifest agent file. Diagnostic enumerates offending
  bullets so the editor can identify exactly what to remove.

The asymmetry between INV-1 (R6) and INV-3 (R7) is now closed:
both use the same Clause 1-like heuristic, so neither path can be
bypassed by the weakened-duplicate attack class.

Tests added (1): `test_r7_p2_inv3_weakened_clause_1_in_non_manifest_fails`
appends a `When feasible, ...` near-canonical bullet to
bibliography_agent.md (not in manifest) and asserts INV-3 fails.

Verified: 54/54 tests pass. 12/12 lint checks pass on clean working
tree. Manual probe confirms R7 attack vector now fails with
"[INV-3] ... Offending bullet(s): ['When feasible, DO NOT simulate ...']".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(v3.6.7 Phase 6.7 R8): close 2 codex P2 INV-3 over-broad / under-broad findings

Codex R8 review (model=gpt-5.5 + xhigh, --base main, after R7 fixes
landed) surfaced two P2 findings — a false-positive on generic
anti-fabrication guidance AND a false-negative on prose-form
canonical pasting.

R8-001 P2 — `_is_clause_1_like` over-broad on generic prohibition
- Pre-R8: any bullet containing `do not simulate` or `do not claim
  to have run` was flagged Clause 1-like. These are common
  anti-fabrication phrases used in many agent prompts. A legitimate
  bibliography guidance bullet `- Do not simulate data or sources.`
  would false-positive INV-3 and prevent ordinary anti-fabrication
  policies from being added to non-manifest agents.
- Fix: split fragments into two tiers:
    _CLAUSE_1_AUDIT_FRAGMENTS = ("audit step", "audit-passed state",
                                  "codex/external review")
    _CLAUSE_1_GENERIC_FRAGMENTS = ("do not simulate",
                                    "do not claim to have run")
  `_is_clause_1_like` now requires at least one audit-specific
  fragment. Generic fragments alone no longer trip the check, so
  unrelated anti-fabrication guidance can ride elsewhere.

R8-002 P2 — INV-3 misses prose-form canonical pasting
- Pre-R8: `_inv3_check` only walked `- ` bullets. A non-manifest
  prompt that pasted the exact canonical sentence as a normal
  paragraph (not a Markdown list item) silently passed lint, even
  though the prohibition was widened.
- Fix: add a paragraph-level scan inside `_inv3_check`. For each
  non-bullet, non-heading paragraph in a non-manifest agent file,
  whitespace-normalize and compare against `CANONICAL_CLAUSE_1_TEXT`
  byte-for-byte. Prose match uses exact equality (not the
  Clause 1-like heuristic) so a *discussion* of the canonical
  wording in prose (e.g. quoting it for context) only fails when
  the line is actually re-introduced as contract content.

Diagnostic enumerates offenders separately as `bullet:` vs `prose:`
so the editor sees exactly which form to fix.

Tests added (2):
- `test_r8_p2_generic_do_not_simulate_in_non_manifest_passes`
  injects `- Do not simulate data or sources.` into
  bibliography_agent and asserts lint passes (no false positive).
- `test_r8_p2_inv3_canonical_as_prose_in_non_manifest_fails`
  injects the canonical sentence as a prose paragraph (not a
  bullet) into bibliography_agent and asserts lint fails with
  `prose: ...` offender diagnostic.

Verified: 56/56 tests pass. 12/12 lint checks pass on clean working
tree. Two manual probes confirm both attack vectors land correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(v3.6.7 Phase 6.7 R9): close 2 codex P2 INV-3 architectural rewind

Codex R9 review (model=gpt-5.5 + xhigh, --base main, after R8 fixes
landed) surfaced that R7+R8's extension of the Clause 1-like heuristic
to INV-3 was over-extension. Spec §6.3 INV-3 wording says "canonical
Clause 1 line found outside the manifest" — that means the actual
sentence, not Clause 1-like variants. Variants outside the manifest
are a v3.6.8+ concern, not a v3.6.7 INV-3 contract.

R9-001 P2 — INV-3 over-broad on audit-related bullets outside manifest
- Pre-R9: INV-3 applied `_is_clause_1_like` to every bullet in non-
  manifest agents. A legitimate process-flow bullet like
  `- Review each audit step before finalizing.` triggered the lint
  even though it has no prohibition semantics. The heuristic was
  designed for INV-1's manifest-internal weakened-duplicate detection
  (where any `audit step` mention SHOULD equal canonical), not for
  scope-widening detection in unrelated agents.
- Fix: INV-3 now matches bullets via byte-exact whitespace-normalized
  equality with `CANONICAL_CLAUSE_1_TEXT`. The Clause 1-like heuristic
  remains in `_is_clause_1_like` for INV-1's R6+R8 contracts.

R9-002 P2 — INV-3 prose scan missed canonical after heading with no blank line
- Pre-R9: prose scan used `re.split(r"\n\s*\n", text)` to extract
  paragraphs and filtered out paragraphs starting with `## `. When
  an editor pasted `## PATTERN PROTECTION (v3.6.7)\nDO NOT simulate
  ...` (heading immediately followed by canonical sentence with no
  intervening blank line), the entire chunk was one paragraph that
  started with `## ` and was filtered out — canonical sentence
  silently slipped past INV-3.
- Fix: replace paragraph-split with line-level heading-strip plus
  whitespace-collapse. All non-heading non-bullet lines join with
  single spaces; the canonical sentence is detected as a substring.
  Heading adjacency no longer bypasses the scan.

Test updates:
- Renamed `CodexR7MutationTests` → `CodexR7R9MutationTests` and
  inverted the R7 test's expectation: a weakened Clause 1 variant
  outside the manifest now PASSES (not fails). Documents the R7→R9
  architectural rewind.
- Added `CodexR9MutationTests` with two regression tests covering
  R9-001 (audit-step bullet not equal to canonical passes) and
  R9-002 (canonical-after-heading-no-blank-line fails).

Verified: 57/57 tests pass. 12/12 lint checks pass on clean working
tree. Two manual probes confirm both R9 attack/non-attack vectors
land correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 12:42:36 +08:00

10 lines
347 B
JSON

{
"scope": "v3.6.7-only",
"rationale_doc": "docs/design/2026-04-30-ars-v3.6.7-step-6-orchestrator-hooks-spec.md#6-partial-inversion-rule-downstream-agent-prompt-edits",
"files": [
"deep-research/agents/synthesis_agent.md",
"deep-research/agents/research_architect_agent.md",
"deep-research/agents/report_compiler_agent.md"
]
}