Commit c3bcd57
committed
feat(spec-drift): reconcile marketplace-catalog and enforce the last field-level surface
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, #2391 parent fe393b7 commit c3bcd57
7 files changed
Lines changed: 234 additions & 124 deletions
File tree
- scripts
- tests
- specs
- _vendor/upstream/marketplace-catalog
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
670 | 670 | | |
671 | 671 | | |
672 | 672 | | |
| 673 | + | |
| 674 | + | |
| 675 | + | |
| 676 | + | |
| 677 | + | |
| 678 | + | |
| 679 | + | |
673 | 680 | | |
674 | 681 | | |
675 | | - | |
| 682 | + | |
676 | 683 | | |
677 | | - | |
| 684 | + | |
678 | 685 | | |
679 | | - | |
| 686 | + | |
680 | 687 | | |
681 | 688 | | |
682 | 689 | | |
| |||
1055 | 1062 | | |
1056 | 1063 | | |
1057 | 1064 | | |
1058 | | - | |
1059 | | - | |
| 1065 | + | |
| 1066 | + | |
1060 | 1067 | | |
1061 | | - | |
1062 | | - | |
| 1068 | + | |
| 1069 | + | |
1063 | 1070 | | |
1064 | | - | |
| 1071 | + | |
1065 | 1072 | | |
1066 | 1073 | | |
1067 | 1074 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
137 | 137 | | |
138 | 138 | | |
139 | 139 | | |
140 | | - | |
| 140 | + | |
141 | 141 | | |
142 | | - | |
143 | | - | |
144 | | - | |
145 | | - | |
146 | | - | |
147 | | - | |
148 | | - | |
149 | | - | |
150 | | - | |
151 | | - | |
152 | | - | |
153 | | - | |
154 | | - | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
155 | 155 | | |
156 | | - | |
| 156 | + | |
157 | 157 | | |
158 | 158 | | |
159 | 159 | | |
| |||
0 commit comments