Skip to content

feat(rfc-008): P2a — validate-schemas.mjs + shared negative corpus (#368) + path-contain + CI wiring#381

Merged
lantiscooperdev merged 8 commits into
mainfrom
feat/rfc-008-p2a
Jun 10, 2026
Merged

feat(rfc-008): P2a — validate-schemas.mjs + shared negative corpus (#368) + path-contain + CI wiring#381
lantiscooperdev merged 8 commits into
mainfrom
feat/rfc-008-p2a

Conversation

@lantisprime

@lantisprime lantisprime commented Jun 9, 2026

Copy link
Copy Markdown
Owner

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)

  1. Promote tests/lib/mini-jsonschema.mjsscripts/lib/ + 7-line re-export shim (version-hash P1b precedent). Two stale cross-refs fixed in the same commit.
  2. 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).
  3. tests/test-p0-schemas.mjs §4 rewrite — consumes the fixture; guards run unconditionally outside the read try/catch. 122/0.
  4. 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; bare schema.json spelling included), MIN_SCHEMA_DOCS=17 vacuity guard, out-of-root sweep, crash → exit 2 never 1. Closed CLI (--project/--json/--help).
  5. scripts/lib/path-contain.mjscontained/resolveContained/UsageError extracted verbatim from validate-plugin-registry.mjs (P2b's validator becomes the second importer; N-7 exit-2 boundary stays with callers). Axis test suite 14/0; regression lock test-plugin-registry 200/0.
  6. .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).
  7. Review-fold + FU commits (see below).

Review provenance (Rule 18)

  • Plan-time: negative-scenario-planner 9-axis walk → ACCEPT-with-amendments (episode 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.
  • Step-6 code review: negative-scenario-reviewer round 1 HOLD (episode 20260609-233758-…-f64b: F3 MAJOR CI path-filter plan deviation + F1/F2 walker leniencies + F4 direct-run guard + F5/F6) → all six fixed in 649e3fd → round 2 ACCEPT-with-FU, converged: true (episode 20260609-234818-…-cbc1; second-order probes on the fix surface all fail-closed).

Step-9 disposition

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

🤖 Generated with Claude Code

lantisprime and others added 7 commits June 10, 2026 06:58
…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 lantiscooperdev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. One-engine invariant (D2). scripts/validate-schemas.mjs imports lintSchema from the promoted scripts/lib/mini-jsonschema.mjs — the same module behind tests/test-p0-schemas.mjs (via the tests/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).
  2. 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-file violations (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). Bare schema.json spelling covered in discovery AND sweep (plan F-B). MIN_SCHEMA_DOCS=17 + out-of-root sweep close the orphan/vacuity axes (plan F1).
  3. 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 uses pathToFileURL at both validator sites (round-1 F4; the raw file:// compare fail-opened on a spaced path with exit 0 + empty output — now a regression test).
  4. Extraction is behavior-preserving. path-contain.mjs is a verbatim move; test-plugin-registry 200/0 is the regression lock; the new 14-case axis suite pins ENOENT/symlink-escape/symlink-into/ELOOP semantics plus FP controls.
  5. CI (Rule 13). plugin-validate.yml runs 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 validation is 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>
@lantiscooperdev lantiscooperdev merged commit 176d6c2 into main Jun 10, 2026
2 checks passed
@lantisprime lantisprime deleted the feat/rfc-008-p2a branch June 10, 2026 00:05
lantiscooperdev pushed a commit that referenced this pull request Jun 13, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

P2: share one negative corpus between P0 linter and validate-schemas.mjs (RFC-008 line 1188)

2 participants