fix(spec-drift): close nine defects an adversarial review found in the freshness gate, two of them P1 - #239
Merged
Conversation
…e freshness gate, two of them P1 #237 shipped green with a mutation-tested lock and still carried two P1s. An adversarial review of the merged commit found nine defects; all nine reproduce, all nine are fixed here, each with a regression lock that was mutation-tested by reverting the fix. The review happened because no AI reviewer runs on these repos — Gemini is sunset and Greptile is dark — so #237 had exactly one pair of eyes. That was the actual gap. THE TWO P1s ARE THE SAME BUG THIS GATE EXISTS TO END, RE-ENTERING BY A DOOR THE LOCK DID NOT WATCH F1 — the registry can name a checker in the WRONG MODE. `--surface` is a top-level arg, so `--check --surface X` is accepted, silently ignores the surface, and exits 0 with "OK". Swap `--check-fresh` for `--check` in any `semantic_coverage.checker` and the driver prints an authoritative all-green board — including `plugin-marketplaces`, which has real outstanding findings. Verified: check-surface-registry.py returned exit 0 on that registry, and so did the test suite, because both validated only that `checker[0]` is an existing FILE. That matters here specifically because the registry is the file humans are told to edit ("flipping a surface to `failing` is a one-line registry edit"). The next person who flips a surface, sees red, and "fixes" the flag gets a green board and no gate objects. Fixed in two layers: the registry gate now pins each checker's freshness-mode flag (and rejects any other mode flag, `--write` included — that would have the gate MUTATE the baseline it guards), and the extractors now REJECT `--surface` outside `--check-fresh` rather than ignoring it. Chose an explicit in-script allowlist over letting the registry self-declare its own mode, because a config that attests to its own correctness is the thing being defended against. F2 — `plugin-manifest` asserted a silently TRUNCATED closed enum. Its `_enum_from_description` had no closedness guard at all: "One of `command` (Claude Code v2.1.200+), `http`, or `mcp_tool`." -> ['command'] "One of `command`, `http`, or {/* min-version: … */}`mcp_tool`." -> ['command','http'] This is the same clause-truncation that broke permissionMode, but the three siblings behave OPPOSITELY under it: agent-definition and hook-config decline (residue guard), plugin-manifest asserted confident nonsense. A short enum is worse than an absence — the projection is the field-diff baseline, so the next report reads as a legitimate value REMOVAL and sends a human to reconcile a phantom. Not hypothetical: the captured plugins-reference page already carries MDX comments in table cells; it just has not landed on a `One of` row yet. Fixed, and the root cause fixed properly rather than papered over: a period followed by a digit is a VERSION NUMBER, not a sentence end. That one shared rule (`captured_source.clause_end`) turns all three cases correct — the closed set stays closed, and both truncating cases now recover all three values. THE OTHER SEVEN F3/F9 the driver exited "CLEAN (exit 0) over 0 field-level surface(s)" when the map was empty — flatly contradicting its own docstring. Now INOPERABLE, plus a declared `semantic_coverage_floor` so SHRINKING coverage is a reviewed edit rather than a quietly smaller green board. F4 bare next()/json.loads raise on a malformed capture -> python exits 1 -> the driver read DRIFT, so the watcher would open a RECONCILIATION issue for a PARSER breakage. Now INOPERABLE, preserving the 0/1/2 distinction. F5 a 30-entry enum gaining one member rendered two sides byte-identical for all 400 visible characters — the reader saw a differing byte COUNT. That is the single most likely real drift on `claude-hooks`, an ENFORCED surface, i.e. unreadable exactly when it mattered. Scalar lists now diff element-wise. F6 `True == 1` and `1 == 1.0`, so a bool becoming an int diffed to nothing. Type changes are now always findings. F7 the registry gate died with `TypeError: unhashable type` on a list-valued `enforcement` instead of naming the problem. F8 `re.DOTALL` on the MDX stripper is inert today and loaded tomorrow — an unbalanced `{/*` opening a 16-line block exists in the captured mcp-config doc. Now line-bounded, with the reason recorded. PLUS A GAP THE REVIEW SURFACED IN ITS OWN EVIDENCE Nothing verified that a vendor-meta.json describes the bytes on disk. `--check` proves the projection derives from the FILES and never reads sha256 at all — so a hand re-vendor recording a wrong hash passed every gate while the provenance quietly described a file that was not there. Three reference docs were re-vendored BY HAND during this work, each editing sha256 and bytes in a text editor, so that is the concrete shape of the risk. New scripts/check-vendor-meta-integrity.py verifies every declared file exists, its sha256 and byte count match, and no UNDECLARED file sits in a capture dir (an input with no provenance is one vendor-meta edit from being parsed as authority). Wired into ci.yml + the scripts-tests job. Demonstrated: corrupting a recorded sha fails the new gate while `--check` still prints "OK". VERIFICATION - All five extractors --check + --self-test OK, and NO projection changed — the parser fixes are provably behaviour-preserving on today's pages. - pytest scripts/tests/ -q: 226 passed (33 new). - Mutation-tested by reverting each fix in turn; every one turns the suite red: F1a 4 failed · F1b 2 · F2 2 · F3 1 · F5 1 · F6 4 · F8 2. - The mutation sweep also caught a REAL stale expectation in the #237 suite (test_each_diff_class_is_detected expected CHANGED_VALUE where element-wise diffing now correctly emits LIST_CHANGED). Updated the expectation, not the behaviour. - check-surface-registry OK; check-vendor-meta-integrity OK (5 captures, 23 files); projection-freshness exit 0; detector-health + sak-dashboard --check OK; audit-harness verify OK. Refs #237
jeremylongshore
added a commit
that referenced
this pull request
Jul 23, 2026
Brings in #239 (nine review defects) and #240 (CodeQL scoped to authored code), the latter being what clears this branch's only remaining red check — a high-severity js/incomplete-sanitization alert inside specs/_vendor/mcp-spec-docs/snapshot.html, a captured third-party page this branch carries as evidence and must not edit. Verified after merging: no conflicts; projection-freshness exit 0 with all four enforced surfaces CLEAN; pytest scripts/tests/ passes; the derived dashboards and vendor-meta provenance all check OK. # Conflicts: # specs/detector-health.json
jeremylongshore
added a commit
that referenced
this pull request
Jul 23, 2026
…field-level surface (#241) Closes the loop opened in #237: all five field-level surfaces are now ENFORCED freshness gates, and all five are clean. CLEAN agentskills-spec enforced CLEAN claude-hooks enforced CLEAN plugins-reference enforced CLEAN sub-agents enforced CLEAN plugin-marketplaces enforced <- this change projection-freshness: CLEAN (exit 0) over 5 field-level surface(s) WHAT WAS BLOCKING IT Not a kernel fold-in — that framing was wrong and was corrected earlier. What actually blocked it was capture skew: main carried a 2026-06-30 plugin-marketplaces snapshot while the promotion PR (#236) carried 2026-07-23, and the findings were measured against the newer one. Reconciling against the older bytes would have turned an ENFORCED gate red the moment #236 merged — a DAILY red on the watcher, which is precisely how a lane stops being read. #236 has now merged, so the reconciliation lands against the bytes main actually holds. THE FOUR FINDINGS, VERIFIED BEFORE THE RE-VENDOR, NOT ASSUMED 1. catalog.renames — object; former-name to current-name migration map; v2.1.193+ 2. plugin_entry.relevance — object; org-allowlisted suggestion signals; v2.1.152+ 3. relevance joins the documented marketplace_specific_fields list 4. catalog.reserved_names gains `first-party-plugins` and `healthcare` Each has its own table row AND its own doc section on the captured page, and is absent from the frozen one. (4) IS THE MATERIAL ONE AND IS NOT BOOKKEEPING The page now states Claude Code re-checks reserved names on EVERY marketplace load, not only on add — so a marketplace already registered under a newly-reserved name STOPS LOADING and reports an untrusted source. Before v2.1.205 it kept loading. That is a live behavioural change to how a marketplace can fail. Estate exposure was checked, not assumed: no marketplace.json under the projects tree uses either new name. The IS marketplace is `claude-code-plugins-plus`, which is not reserved — but it IS a suffix-extension of the reserved `claude-code-plugins`, and the same paragraph also blocks names that "impersonate official marketplaces" by prose judgement. Recorded as a watch item, not a break. This finding is the clearest argument yet for the whole track: it is a real, dated, operational risk to our own marketplace that surfaced only because a gate could finally see current bytes. NO KERNEL EDIT IMPLIED upstream-base/marketplace-catalog.v1.json is a required-set FLOOR: it models required:[name, owner, plugins] plus the constraints its negative corpus tests, enumerates no optional plugin-entry fields at all, and leaves additionalProperties permissive. Both new fields are optional, so they already validate; adding them would make the schema LOOK current while remaining a floor. PINNED EXPECTATIONS MOVED, WHICH IS THE MECHANISM WORKING The extractor's self-test failed loudly on four hard-coded expectations (8->9 top-level fields, 14->16 reserved names, 20->21 entry fields with standard-metadata 12->13, and `renames`/`relevance` joining the doc-fields-not-in-kernel divergences). Those sets exist precisely so a re-capture that shifts a finding cannot pass silently and a human must reconcile. Updated them as that human, with the reasoning recorded in vendor-meta and the registry note rather than as bare number edits. A TEST RETIRED HONESTLY test_undispositioned_contracts_still_report_their_known_drift asserted marketplace-catalog still reported DRIFT, and was written to fail if that went green WITHOUT the registry flipping to `failing`. The registry flipped, so the premise retired. Chose to fold the contract into test_reconciled_contracts_are_clean and document the retirement in place, rather than delete the test to make a suite pass. The anti-vacuity property it guarded is unchanged: test_check_fresh_fires_on_a_perturbed_capture still proves the check fires on real captured bytes per contract, and test_the_real_coverage_map_runs_and_agrees_with_the_registry still requires every `failing` surface to actually be clean. VERIFICATION - All five extractors --check + --self-test OK, including the reconciled kernel cross-check whose agreements AND divergences now match their updated sets exactly. - projection-freshness exit 0, 5/5 enforced CLEAN. - pytest scripts/tests/ -q: 226 passed. - check-surface-registry OK; check-vendor-meta-integrity OK (5 captures, 23 files, every sha256 + byte count matching); detector-health and sak-dashboard --check OK; audit-harness verify OK after init. Refs #236, #237, #239 Co-authored-by: jeremylongshore <jeremylongshore@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Closes nine defects an adversarial review found in the freshness gate #237 shipped — two P1 — plus a provenance gap the review surfaced in its own evidence. Every finding reproduced first, fixed, then locked by a test that was mutation-verified by reverting the fix.
Why this review happened
Per the repo's own posture note, no AI reviewer runs here — Gemini is sunset, Greptile is dark. #237 merged green with a mutation-tested lock and exactly one pair of eyes. That was the real gap, and it cost two P1s.
The two P1s are the same bug this gate exists to end, re-entering by a door the lock didn't watch
F1 — the registry can name a checker in the wrong MODE
--surfaceis a top-level parser arg, so the wrong mode accepts and silently ignores it:Swap
--check-fresh→--checkin anysemantic_coverage.checkerand the driver prints an authoritative all-green board — includingplugin-marketplaces, which has real outstanding findings:check-surface-registry.pyreturned exit 0 on that registry. So did the test suite. Both validated only thatchecker[0]is an existing file — never what the argv means.That lands here specifically because the registry is the file humans are told to edit ("flipping a surface to
failingis a one-line registry edit"). The next person who flips a surface, sees red, and "fixes" the flag gets a green board and no gate objects.Fixed in two layers — the registry gate pins each checker's freshness-mode flag and rejects any other mode flag (
--writeincluded, which would have the gate mutate the baseline it guards), and the extractors now reject--surfaceoutside--check-freshinstead of ignoring it.Chose an explicit in-script allowlist over letting the registry declare its own mode, because a config that attests to its own correctness is the thing being defended against.
F2 —
plugin-manifestasserted a silently truncated closed enumNo closedness guard at all:
One of `command` (Claude Code v2.1.200+), `http`, or `mcp_tool`.['command']One of `command`, `http`, or {/* min-version: … */}`mcp_tool`.['command', 'http']Same clause-truncation that broke
permissionMode— but the three siblings behave oppositely: agent-definition and hook-config decline (residue guard); only this one produced confident nonsense.A short enum is worse than an absence: the projection is the field-diff baseline, so the next report reads as a legitimate value removal and sends a human to reconcile a phantom. Not hypothetical — the captured
plugins-referencepage already carries MDX comments in table cells; it just hasn't landed on aOne ofrow yet.Root cause fixed, not papered over: a period followed by a digit is a version number, not a sentence end. One shared rule (
captured_source.clause_end) makes all three cases correct:The other seven
CLEAN (exit 0) over 0 field-level surface(s)— contradicting its own docstringsemantic_coverage_floorso shrinking coverage is a reviewed edit, not a quietly smaller green boardnext()/json.loadson a malformed capture → exit 1 → driver read DRIFT, so the watcher would open a reconciliation issue for a parser breakageclaude-hooks, an enforced surfaceLIST_CHANGED: events.enum: +["WorktreeRemove"]True == 1,1 == 1.0— a bool becoming an int diffed to nothingTypeError: unhashable typeinstead of naming the problemre.DOTALLon the MDX stripper — inert today, and an unbalanced{/*opening a 16-line block exists in the capturedmcp-configdocPlus a gap the review surfaced in its own evidence
Nothing verified that
vendor-meta.jsondescribes the bytes on disk.--checkproves the projection derives from the files and never readssha256at all — so a hand re-vendor recording a wrong hash passed every gate while the provenance quietly described a file that wasn't there. Three reference docs were re-vendored by hand during this work, each editingsha256andbytesin a text editor. That's the concrete shape of the risk.New
scripts/check-vendor-meta-integrity.py: every declared file exists,sha256+bytesmatch, and no undeclared file sits in a capture dir (an input with no provenance is one vendor-meta edit from being parsed as authority). Wired intoci.yml+ the scripts-tests job.Demonstrated — corrupting a recorded sha fails the new gate while the gate people believe covers it still says OK:
Verification
--check+--self-testpytest scripts/tests/ -qcheck-surface-registry/check-vendor-meta-integrityprojection-freshnessdetector-health/sak-dashboard--checkaudit-harness verifyinitMutation-tested by reverting each fix in turn — every one turns the suite red:
The sweep also caught a real stale expectation in #237's own suite (
test_each_diff_class_is_detectedexpectedCHANGED_VALUEwhere element-wise diffing now correctly emitsLIST_CHANGED). Updated the expectation, not the behaviour.Risk
semantic_coverage_flooris new registry state;check-surface-registry.pyrequires it, so a registry missing it fails loudly rather than silently disabling the floor.captured_source.py(F5+F6, both in_walk).Follow-up
hook-config's_enum_from_descriptionstill renders an added value as a vanishingenum(it declines rather than recording candidates). Deliberately not changed here: its clause selection falls back to the whole description, so open-candidates there produced 14 keys of pure noise when measured. Fixing it needs its clause selection anchored first.Refs
Refs #237