You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(S2): step 9 — the five HOLD blockers (and a latent step-8 test-harness defect)
1. THE TEST MODULE KILLED THE AGGREGATE RUNNER. test_gate_patch.py ran its checks at import
and ended with sys.exit(), so when run_tests.py imported it every module discovered
after it — and the aggregate return code — was silently dropped: a green job that had
"turned the lights off in the building". The SAME latent defect was already in the
merged step-8 test_patch_bundle.py (it sorts AFTER test_gate_patch, so it had never
actually run in the aggregate). Both now expose `def run() -> int` with no import-time
sys.exit and an `if __name__ == "__main__"` guard. A new tests/test_harness_contract.py
STATICALLY proves (via ast) that every sibling test_*.py exposes run() and never calls
sys.exit / raise SystemExit at module scope — so this class of bug cannot recur. With
the fix the suite now reaches its summary and both modules gate: step 8 = 79 checks,
step 9 = 63, harness = 29, all under one run_tests return code (verified: an injected
failure flips rc to 1).
2. FULL FROZEN-CANDIDATE VALIDATION. The exact key set was held but not every value. The
gate now validates every string field (diagnostic_code, enclosing_member, event_identity,
...), occurrence_ordinal as a non-negative int, and the full teardown block — exact keys,
status in {none,exact,ambiguous}, and each teardown candidate's exact keys with its 6-int
span. A re-hashed bundle with `event_identity: 123` or a malformed teardown now refuses
at AUTHORITY_BINDING (fixtures added for event_identity/occurrence_ordinal wrong type,
teardown extra key / unknown status / malformed candidate / bad span shape).
3. EXACT DIRECTORY LAYOUT + BUNDLE-ROOT lstat. The layout walk returned only files, so an
extra/hidden/nested EMPTY directory rode through, and --bundle was realpath'd first so a
symlinked bundle root was accepted. The walk now returns (dirs, files) and the postimage
subtree must equal EXACTLY rel's ancestor dirs plus rel; --bundle is lstat'd before
realpath and a symlink/reparse root is refused. Fixtures: extra/hidden/nested empty dir,
symlinked bundle root — all BUNDLE_LAYOUT.
4. A TRULY CLAIMED WORKDIR + RE-PROVEN PUBLICATION. _prepare_out only checked a name; the
dir was created later with a plain makedirs. A new _claim_workdir creates the
unpredictable dir immediately (mode 0700 on POSIX, in the mkdir itself) and PROVES it:
not a link/reparse, realpath == itself under the platform-aware comparison, empty. And
_publish re-resolves the out-dir parent, re-confirms it is off the source tree, and
re-confirms no final out exists — immediately before the atomic rename, not only at
parse time.
5. TIGHTER PATCH GRAMMAR. The no-newline marker was allowed after any seen body line and
any number of times. It is now stateful: it must sit immediately after an eligible
( / - / + line, marks the LAST line of its side(s) (a later line of a closed side is
refused), and appears at most once per side. Range bounds now cover the zero-length
(insertion) case: old_len>0 requires 1 <= old_start and old_start+old_len-1 <= preimage
lines (so a non-zero range starting at 0 is refused); old_len==0 requires
0 <= old_start <= preimage lines (so an insertion past EOF is refused rather than dying
later as APPLY_CHECK). Fixtures: marker before any line, duplicate marker, marker with
more of the same side after it, insertion past the preimage, non-zero range at 0, plus
positive cases (a well-placed marker, a top insertion).
The step-8 change is strictly the test-harness run() wrapper — no test logic, no Steps 4-8
semantics/schema touched. All other Step 9 architecture (authority model, six snapshots,
manifest schema, git environment, baseline index, evidence schema, empty-patch policy,
taxonomy) is unchanged. Steps 10-12 not started.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JxKjqdGEFzq4UzZupw379G
0 commit comments