Skip to content

IR V4: The spec PR polices itself - #42

Open
afogel wants to merge 2 commits into
ir/v3from
ir/v4
Open

afogel wants to merge 2 commits into
ir/v3from
ir/v4

Conversation

@afogel

@afogel afogel commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Fourth slice of the Normative IR (docs/shaping/normative-ir-slices.md §V4), stacked on #41.

Demo: on a copy of the marked corpus, add a sentence containing MUST with no marker and delete a marked provision without tombstoning its ID. bun run ir lint fails and the comment it writes lists both: the unmarked statement with file:line, and the ID that needs a tombstone. Captured output is in ir/slices/v4/README.md; ir/test/spec-lint.test.ts performs the same edits.

What it delivers

  • specLint() (N20) against a baseline. The committed manifest and census, or with --baseline <dir> the base branch's copies, so a PR is judged by what it changed rather than by what its author regenerated. That is what makes "new unmarked keyword" a meaningful check while 174 occurrences are still unbound by design.
  • Rules. Unpaired, mismatched or nested markers (N22); an RFC 2119 occurrence unbound now and not unbound in the baseline (N21, U25); a provision gone from the corpus or a withdrawn record with no tombstone, or a tombstoned ID back in use (N23, U24); duplicate or never-allocated IDs (N24); a test citing an unknown ID (N25); an unresolvable or unpinned schema ref (N26). All fail. A moved subschema, or V3's changed text, dependency or restatement, is needs-review (U22). Provisions added with no test are reported (U23).
  • Schema refs pin a hash (R2.8). pinned is the SHA-256 of the canonicalized subschema at the JSON Pointer. The seven records with refs carry eight pins.
  • The census reads prose, never markers. unmark() strips markers and re-bases spans, so lint-time context windows equal the committed census's and an occurrence's identity survives a reflow elsewhere.
  • CI (N61). checks.yml runs bun run ir lint with the tree as its own baseline. New spec-lint.yml runs per PR and weekly, materializes the base branch's manifest and census with git show, lints against them, and posts one normative-impact comment updated in place. It is the only workflow with write access, and only to PR comments; fork PRs get the log and the red check.

Verification

  • bun run typecheck passes; bun test ir: 132 tests pass.
  • bun run ir lint on the tree: no failures, nothing to review, exit 0.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BX56nQPAsqYihopWfWy1xm

The fourth slice of the Normative IR (V4 in docs/shaping/normative-ir-slices.md),
stacked on V3. `acs-ir lint` is now spec-lint (E9): one pass over the marked
corpus, the records, the allocation files, the conformance tests and the
pinned schemas, judged against a baseline, the committed manifest and census,
or with --baseline the base branch's copies, so a pull request is judged by
what it changed rather than by what its author regenerated.

Failures: unpaired, mismatched or nested markers; an RFC 2119 occurrence that
is unbound now and was not unbound in the baseline; a provision gone from the
corpus, or a withdrawn record, with no tombstone; a tombstoned ID back in use;
a duplicate or never-allocated ID; a test citing an ID that is neither live nor
tombstoned; a schema ref whose pointer does not resolve or that pins no hash.
Needs-review: a moved subschema, alongside V3's changed text, dependency and
restatement. Both exit 1; a failure is fixed in the tree, needs-review is
cleared by classifying the change and updating the record.

The baseline is what makes "new unmarked keyword" mean something while 174
occurrences are still unbound by design. An occurrence's identity across edits
is its source, keyword and context window, so a reflow elsewhere does not make
it new and an edit to its own sentence does. The census the lint runs reads the
marked corpus with the markers stripped and the spans re-based, so its context
windows equal the committed census's.

Schema refs now pin the SHA-256 of the canonicalized subschema (R2.8); the
seven records with refs carry eight pins. spec-lint.yml lints each pull request
against its base branch and posts one impact comment, updated in place; it is
the only workflow with write access, and only to pull-request comments.

The demo runs as a test on the marked corpus: an unmarked MUST and a deleted
provision both fail, named with file:line and ID in the terminal report and in
the comment.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BX56nQPAsqYihopWfWy1xm
@github-actions

Copy link
Copy Markdown

Normative impact (ACS 0.1.2 at 6fce2a0)

No failures, nothing to review.

Changed provisions and their tests (U22)

None.

Added provisions with no conformance test (U23)

None.

Removed provisions missing a tombstone (U24)

None.

Unmarked normative statements (U25)

None.

@afogel
afogel added this pull request to stack #48 September 14, 2026 14:18
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.

2 participants