Skip to content

IR V2: Twenty-eight provisions, marked and indexed - #40

Open
afogel wants to merge 2 commits into
claude/focused-allen-h1d4u6from
ir/v2
Open

afogel wants to merge 2 commits into
claude/focused-allen-h1d4u6from
ir/v2

Conversation

@afogel

@afogel afogel commented Sep 13, 2026

Copy link
Copy Markdown
Owner

Second slice of the Normative IR (docs/shaping/normative-ir-slices.md §V2), stacked on #39. Retarget to main once V1 merges.

Demo: bun run ir markers apply && bun run ir extract && bun run ir render produces ir/dist/provision-index.md: twenty-eight provisions with opaque IDs, node type, level, actor, profile, evidence class, modality, and verbatim text. bun run ir census shows the unbound count fall from 197 to 174.

What it delivers

  • The staging overlay (E3). ir/markers/overlay.yaml places each provision's anchor and terminator by verbatim quote against the pinned corpus. quote marks one run of text; start + end mark a span across blocks, and start must be unique in the file so an ambiguous quote fails rather than binding the first match. The spec submodule is untouched.
  • The generated/authored seam. ir/manifest/provisions.json is written only by acs-ir extract; ir/provisions/<ID>.yaml is written only by a person; loadCatalog() is the one join, on ID. Every generated file has --check, and CI now runs the census, markers, extract and render checks.
  • Marker pairing (N22), pulled forward from V4 as the X3 spike required: an unpaired, mismatched, or nested marker fails before anything is extracted.
  • IDs. ACS-{REQ|DEF|INV|EXC}-NNNN, four digits, allocated by acs-ir ids next <type> from a counter that never rewinds, with a tombstone file for retired numbers.
  • Twenty-eight specimens, one per row of the specimen table plus the cases that follow from them: 24 Requirements, 2 Definitions, 1 Invariant, 1 Exclusion, across all four block types, with three spans crossing blocks, two sentences each yielding two provisions, ten keyword-free provisions, the §8.5 permission / conditional-obligation / SHOULD triple, and the approver-authentication restatement pair with restates pointing from the pillar copy to the concept page.

What the mechanism found

  • defer-details.json does not require timeout_decision, which §6 says DEFER MUST include (ACS-REQ-0004).
  • The trust enum §7.1 constrains is reserved in prose but is not a field of provenance.json, so there is no schema pointer to cite (ACS-REQ-0010).
  • §10's "A verifier MUST recompute" binds whichever party checks a signature; the actor field holds one value (ACS-REQ-0018). Carried to V5.
  • One editorial call recorded: provenance.md:19 is typed a Definition per the specimen table, where X5 had read it as an Invariant (ACS-DEF-0002).

Both schema findings are added to the upstream findings table in the slices doc.

Verification

  • bun run typecheck passes.
  • bun test ir: 111 tests pass, including the real catalog (28 entries, no join problems, every record reviewed against its current text, every ID allocated) and the census reference numbers (23 bound, 11 excluded, 174 unbound).
  • bun run ir extract --check, render --check, and census --check all report up to date.

🤖 Generated with Claude Code

https://claude.ai/code/session_01BX56nQPAsqYihopWfWy1xm

The second slice of the Normative IR (V2 in docs/shaping/normative-ir-slices.md),
stacked on V1. `acs-ir markers apply` materializes a marked copy of the corpus
from ir/markers/overlay.yaml, `acs-ir extract` reads each anchor-to-terminator
span into ir/manifest/provisions.json, and `acs-ir render` joins that manifest
to the authored records in ir/provisions/ and writes ir/dist/provision-index.md.
The census now binds every keyword occurrence inside a resolved span, so the
unbound count falls from 197 to 174.

The generated/authored seam is established here: the manifest is written only
by the extractor, the records only by a person, and loadCatalog() is the one
place they meet, joined on ID. Every generated file has a --check that CI runs.

Twenty-eight specimens rather than twenty, one per row of the specimen table
plus the cases that fall out of them: §6's table body as one table-borne
provision, §8.6's sentence split into its two MUSTs, §8.4's MUST and SHOULD
addressed separately, and §8.5's permission, conditional obligation and SHOULD.
Three spans cross blocks, which is why the overlay carries start/end beside
quote, with start required to be unique so an ambiguous quote fails rather than
binding the first match. Marker pairing (N22) is pulled forward from V4 as X3
required: an unpaired, mismatched or nested marker fails before extraction.

Put through real prose, the mechanism found two spec/schema disagreements
(defer-details.json does not require timeout_decision; the trust enum §7.1
constrains is not a provenance.json field) and one vocabulary gap (actor cannot
say "either party"). All three are recorded in the records and the slices doc
rather than smoothed over.

IDs are ACS-{REQ|DEF|INV|EXC}-NNNN with four digits, allocated from a monotonic
counter that never rewinds; text_hash is SHA-256 over whitespace-normalized
text; section_slug follows Python-Markdown's slugify so it equals the anchor
MkDocs publishes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BX56nQPAsqYihopWfWy1xm
The repository's .gitignore excludes every dist/ directory, so
ir/dist/provision-index.md (and rules.dl from V5 on) never reached git
even though `render --check` and `compile --check` require the committed
copy. Re-include ir/dist/ and commit the files the checks compare against.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BX56nQPAsqYihopWfWy1xm
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