feat(rfc-008): P2a — validate-schemas.mjs + shared negative corpus (#368) + path-contain + CI wiring#381
Conversation
…him) git mv tests/lib/mini-jsonschema.mjs -> scripts/lib/ (engine for the shipped M1/M2 validator, D2 one-engine); 7-line re-export shim keeps the P0 importer unchanged (version-hash P1b precedent). Fixes two stale cross-refs in the same commit (reviewer F-E): the "official-meta-schema validation is owned by P2" sentence (false under locked D2) and json-instance-validate.mjs's tests/lib path. test-p0-schemas: 117/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ite (#368) Extract the 14 inline negatives to tests/fixtures/schema-negative-corpus.json (one fixture, two consumers — validate-schemas.mjs reads the same file). §4 now applies the fail-closed shape contract: parse, array, string name + schema key, unique names (reviewer F-D), >=14 non-vacuity — guards run unconditionally outside the read try/catch (planner F5). test-p0-schemas: 122/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gistry contained/resolveContained/UsageError move verbatim to scripts/lib/ so P2b's validate-bp-contract imports the same predicate (no second hand-rolled containment). UsageError -> exit-2 boundary stays in the caller (N-7). New tests/test-path-contain.mjs pins the axis matrix (lexical/absolute/ENOENT/ symlink-escape/symlink-into/dir-link/ELOOP + FP controls; win32 loud-skip): 14/0. Regression lock test-plugin-registry: 200/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Shipped CI validator: every repo schema doc lints as a valid 2020-12 document via the ONE engine (scripts/lib/mini-jsonschema.mjs, D2) and the shared negative corpus is re-asserted engine-rejected (#368 — divergence is a loud CI failure). Fail-closed: no-follow Dirent discovery over patterns/plugins/schemas (bare schema.json spelling included, F-B), MIN_SCHEMA_DOCS=17 vacuity guard, out-of-root sweep (F1), symlinked docs are violations, crash exits 2 never 1 (F4). Closed CLI: --project/--json/--help. tests/test-validate-schemas.mjs: real-repo positive + Rule-14 contract cross-check + Class A negative matrix in a verbatimSymlinks sandbox (F-A) with hermeticity + dangling-link assertions (F2b); win32 loud-skip (F-F). 34/0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New plugin-validate.yml: validate-schemas (against $GITHUB_WORKSPACE) + test-validate-schemas + test-p0-schemas (the #368 corpus co-consumer — a linter/validator divergence now fails CI) + test-path-contain + test-plugin-registry. One step per suite for precise attribution. Remaining plugin-family suites stay tracked in #377, named in the workflow comment. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
F3 MAJOR: drop plugin-validate.yml path filters (approved-plan deviation — required check + path filter hangs non-matching PRs on Expected; per-file lib list silently stops running on import-graph growth). F1+F2 class fix in walkNoFollow: doc-NAMED entries judged by kind before any skip rule — regular file (even dot-prefixed) is discovered, anything else is a doc-regular-file violation; dot-skip narrowed to directories. F4: direct-run guard uses pathToFileURL (raw file:// compare fail-opens on URL-encoding paths) here AND at the pre-existing validate-plugin-registry site. F5/F6: shape-violating corpus entries skipped in both consumers' reject loops without crashing the summary; string-name guard. +3 regression tests (doc-named dir, hidden .schema.json, spaced argv[1]). Suites: 40/0, 122/0, 200/0, 14/0. Review: negative-scenario-reviewer HOLD round 1, episode 20260609-233758-hold-1-major-ci-path-filter-plan-deviati-f64b. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d-2 FU-2) cpSync lib/ instead of a hand-list so import-graph growth never becomes copy-list churn (staleness was already fail-loud). 40/0. FU-1 (symlinked argv[1] guard, 3-site class) tracked as #380 with the 5-field block. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
lantiscooperdev
left a comment
There was a problem hiding this comment.
Substantive review (bot surface for the Rule-17 record; user approval still required)
Reviewed the full diff (7 commits, main@8f1e880..ab10611) against the approved P2a plan (.review-store/p2a-plan.md) and the RFC-008 M1/M2 contract (L493-498). This PR went through the complete Rule-18 chain before reaching here; this comment consolidates what was verified and what remains open.
Verified
- One-engine invariant (D2).
scripts/validate-schemas.mjsimportslintSchemafrom the promotedscripts/lib/mini-jsonschema.mjs— the same module behindtests/test-p0-schemas.mjs(via thetests/lib/shim). No second hand-rolled validator exists; the #368 corpus check guards the next refactor that forks them. The shim preserves the P0 importer byte-for-byte (verified: §1-§3 of test-p0-schemas unchanged, 122/0). - Fail-closed discovery. Doc-named entries are judged by kind before any skip rule: regular files (incl. dot-prefixed) are linted; symlinks/dirs/FIFOs are
doc-regular-fileviolations (round-1 F1/F2 class fix, verified by round-2 probes D/E — dot-dir named.weird.schema.json/and a FIFO both exit 1). Bareschema.jsonspelling covered in discovery AND sweep (plan F-B).MIN_SCHEMA_DOCS=17+ out-of-root sweep close the orphan/vacuity axes (plan F1). - Exit taxonomy. Exit 1 is reachable only via the violation tally; UsageError and internal crashes exit 2 (
{status:"error"}JSON) — a crash can't masquerade as "checked and found violations". Direct-run guard usespathToFileURLat both validator sites (round-1 F4; the rawfile://compare fail-opened on a spaced path with exit 0 + empty output — now a regression test). - Extraction is behavior-preserving.
path-contain.mjsis a verbatim move;test-plugin-registry200/0 is the regression lock; the new 14-case axis suite pins ENOENT/symlink-escape/symlink-into/ELOOP semantics plus FP controls. - CI (Rule 13).
plugin-validate.ymlruns unfiltered on PRs + main pushes, one step per suite. The hand-curated path filter from the first draft was removed after review (round-1 F3 MAJOR — a path-filtered required check leaves non-matching PRs hanging on "Expected").
Review chain (artifacts)
Plan: negative-scenario-planner 9-axis ACCEPT-with-amendments (…224010-ed2c) → second-opinion claude-subagent R1 ACCEPT-with-FU converged (…225125-709a). Code: negative-scenario-reviewer R1 HOLD, 10 executed probes (…233758-f64b) → all six findings fixed in 649e3fd → R2 ACCEPT-with-FU converged: true (…234818-cbc1).
Open residuals (tracked, non-blocking)
- #380 — direct-run guard fail-opens under symlinked argv[1] (3-site class, 5-field block in the issue). CI invokes by real path; gate unaffected.
- #377 — remaining plugin-family suites need step rows in the new workflow (named in the workflow comment).
- Required-check flip for
Plugin + schema validationis UI-side after this merges.
No blocking findings from this pass. Recommend: verify the plugin-validate workflow goes green on this PR before approving.
… out of the 30-day window test-checkpoints-migration seeded a violation hardcoded at 2026-05-09; shouldArmBp001Checkpoint (em-recall.mjs) matches `e.date >= cutoffStr` over a 30-day window, so the fixture aged out on 2026-06-08 and the advisory assert failed CI on a zero-diff PR (#381 — last green main run 2026-06-07). Seed yesterday's date instead; the fixture can never age out again. 8/8. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…rs (#388) Apply the deferred P2-plan-review findings (F-2, F-5, F-6, F-9, F-10, N-3, N-4) as documentation-only edits, correcting RFC-008 spec text to match the validators merged in P2a (#381) and P2b (#384). - F-5: reword the validate-schemas.mjs "meta-meta/official-meta-schema" over-claim to keyword-grammar doc-validity linter (5 locations incl. two line-wrapped in P2-bp-contracts.md). - N-3: drop the vacuous bp-XXX arms from assertions 7 + 11 (contracts carry hashes, not label/event ids; real binding is the version-hash equality). - F-2: document event-id uniqueness in assertion 12 (events mirror of assertion 6) — matches shipped validate-bp-contract.mjs. - N-4: assertion 8 reworded to the shipped set-difference invariant (merge-base, fail-closed, bootstrap carve-out); bp set = 11 (bp-007 absent). - F-6: drop the never-shipped validate-taxonomy-schema.mjs from P2-bp-contracts. - F-9: tests/lib/{mini-jsonschema,version-hash} refs updated to scripts/lib (promoted P1b/P2a) — rewrite in living docs, annotate in historical rows. - F-10: P2 phase-table counts 12/2 -> 39/5 (git-stat grounded). Plan second-opinion reviewed via harness (codex): R1 HOLD (one same-class F-5 miss) -> R2 ACCEPT converged. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
RFC-008 P2a — validate-schemas.mjs + shared negative corpus + path-contain extraction + CI wiring
First slice of P2 (plan approved 2026-06-08, episode
20260608-131052-…-e103; P2a's own Rule-18 plan:.review-store/p2a-plan.md, approved 2026-06-10). Serves R3/R4 via the M1/M2 normative contract (RFC-008 L493-498).Closes #368
What ships (7 commits)
tests/lib/mini-jsonschema.mjs→scripts/lib/+ 7-line re-export shim (version-hash P1b precedent). Two stale cross-refs fixed in the same commit.tests/fixtures/schema-negative-corpus.json— the 14 P0 negatives extracted into ONE fixture with a fail-closed shape contract (parse / array / string name + schema key / unique names / ≥14).tests/test-p0-schemas.mjs§4 rewrite — consumes the fixture; guards run unconditionally outside the read try/catch. 122/0.scripts/validate-schemas.mjs(new, M1/M2) — every repo schema doc lints as a valid 2020-12 document via the ONE engine (D2); the shared corpus is re-asserted engine-rejected (P2: share one negative corpus between P0 linter and validate-schemas.mjs (RFC-008 line 1188) #368 acceptance: divergence = loud CI failure). Fail-closed: no-follow Dirent discovery (doc-named non-regular entries are violations; bareschema.jsonspelling included),MIN_SCHEMA_DOCS=17vacuity guard, out-of-root sweep, crash → exit 2 never 1. Closed CLI (--project/--json/--help).scripts/lib/path-contain.mjs—contained/resolveContained/UsageErrorextracted verbatim fromvalidate-plugin-registry.mjs(P2b's validator becomes the second importer; N-7 exit-2 boundary stays with callers). Axis test suite 14/0; regression locktest-plugin-registry200/0..github/workflows/plugin-validate.yml— validate-schemas + 4 suites, one step each, no path filters. Remaining plugin-family suites stay tracked in CI: wire the plugin-registry + gauntlet + binding test family into a workflow (Rule 13) #377 (named in the workflow comment).Review provenance (Rule 18)
20260609-224010-…-ed2c); second-opinion harness round 1 (claude-subagent) → ACCEPT-with-FU, converged (request…224500-e665→ reply…225125-…709a). All folds in the plan body.20260609-233758-…-f64b: F3 MAJOR CI path-filter plan deviation + F1/F2 walker leniencies + F4 direct-run guard + F5/F6) → all six fixed in649e3fd→ round 2 ACCEPT-with-FU, converged: true (episode20260609-234818-…-cbc1; second-order probes on the fix surface all fail-closed).Step-9 disposition
ab10611).E2E (local, post-fix)
validate-schemas --project <repo>= OK (17 docs, 14 corpus negatives, 38 checks) · test-validate-schemas 40/0 · test-p0-schemas 122/0 · test-path-contain 14/0 · test-plugin-registry 200/0. All five workflow steps green locally.Follow-ups for the user
Plugin + schema validationa required check is UI-side (per CI: wire the plugin-registry + gauntlet + binding test family into a workflow (Rule 13) #377 acceptance).🤖 Generated with Claude Code