Commit Graph

2 Commits

Author SHA1 Message Date
Bo 6f75ab208f test(gates): record RED for the ratchet fail-open and all-parent head scope
Two fresh-Sol findings against scripts/check-skill-python-ratchet.sh, pinned as
failing witnesses before any source change.

1. The caller swallows the collector contract. scripts/lib/ratchet.sh documents
   every chosen collection command as fail-closed rc 2 ("refusing to certify an
   unchecked change set"), but the gate calls
   `ratchet_changed_files_status ... 2>/dev/null || true`. A dead Git read
   therefore becomes an EMPTY changed set, and an empty changed set certifies
   "no new Python" over a diff nobody looked at. Reproduced with a diff-tree
   shim over a tree carrying a real unpinned violation: the gate prints the
   green PASS banner and exits 0.

2. head scope claims first-parent semantics and does not have them.
   `git diff-tree -m --first-parent` does not mean "diff only against parent 1"
   -- `-m` splits a merge into one block PER PARENT and emits them all. The
   parent-2 block lists paths already settled on the first-parent line, so a
   merge re-attributes them to itself.

RED witnesses added:
  N7  gate: a failing changed-scope collector must exit 2, never a silent PASS
  N8  gate: a merge must not re-attribute parent-2-block Python
  N9  gate: a merge must still catch its real first-parent introduction (guard
      against over-correcting; passes today and must keep passing)
  lib: changed_files/_status head scope on a merge excludes the parent-2 block

N7, N8 and the lib case fail at this commit. N9 passes.

The pre-existing lib merge test cannot discriminate: its first-parent line gains
nothing after the branch point, so the parent-2 block is empty under either
reading. The new case uses a fixture where the two readings differ.

Refs: /tmp/agentops-adr0016-go-promotion-review-sol.md (REQUEST_CHANGES, "Two
ratchet corrections are still required")
2026-07-27 23:47:09 -04:00
Bo aceeb6f10f feat(gates): enforce ADR-0016 shipped-Python rule; fix RPI/Validate digest disagreement (#995)
Two fixes from the 2026-07-25 skill-overhaul progress review, both independent
of the unlanded tranche fleet.

1. skill.python-ratchet — the gate ADR-0016 said it already had. Section 3
   fixed "Python never ships in skills" on 2026-07-18 and called its own
   violation "a gate failure, not a style nit", while zero checks enforced it.
   Now a blocking shrink-only ratchet over skills/*/scripts/**/*.py: the 24
   current files are pinned, new ones hard-fail, a promoted file must be
   pruned, and the growth guard rejects a change that allowlists itself. The
   surviving count prints on every run. ADR-0016 records the enforcing check by
   name and the skills/*/tests/** carve-out with its rationale, so the
   exemption cannot decay into the same inert prose.

2. The RPI/Validate acceptance-digest disagreement. run_once.py digested a
   canonical-JSON re-serialization of the parsed intent mapping, validate.py
   digested the raw intent bytes, and run_once.py hard-compared the two, so the
   composed contract could not succeed. Both suites were green because RPI's
   test mocked Validate with RPI's own digest function. RPI stops being a
   second digest authority: it carries the digest Plan declares over the bytes
   Plan snapshotted and cross-checks Validate's independently re-derived value.

3. The cathedral-cut probe, the same blind spot found again. It set
   intent_bytes = canonical_bytes(resolved_intent) — precisely the one input
   where the two digests coincide — so like the unit test it was built around
   the coincidence and could not observe the bug.

Test shape is liveness-first: every gate negative is a seeded witness the gate
must be shown to FAIL on (12/12 bats), and the digest fix landed against a RED
witness that reproduces the real production error.
2026-07-25 18:37:41 -04:00