Skip to content

V7: Conformance matrix - #16

Merged
afogel merged 29 commits into
slice/v6from
slice/v7
Sep 6, 2026
Merged

afogel merged 29 commits into
slice/v6from
slice/v7

Conversation

@afogel

@afogel afogel commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Closes #8

Stacked on slice/v6. Review the diff against that base, not main. Slice 7 of 8.

What this slice is for

Eight AGT intervention points × five AGT verdicts, every cell resolved: expressed, guardian-only, or unexpressed with a named reason. Trace is measured as something this implementation does not claim.

Without it, you have demos and anecdotes. There is no scorecard a reader can argue with, and no declared “we claim this / we do not claim that.”

V1–V6 are what the harness measures. This slice is the deliverable those slices exist to make possible. V8 is what keeps the matrix from silently rotting when AGT moves.

What a reviewer is looking at

bun run conformance starts an in-process Guardian, talks to the pinned AGT bridge, and prints three separate artifacts: a coverage matrix, a mapping table, and trace-pillar rows. The mapping in mapping.yaml is a declaration this repo authored; the matrix is what the harness measured against the live runtime. They are not the same table.

Honest gaps stay named: pre_model_call / post_model_call have no ACS v0.1.0 target; several Trace attributes have no required wire field, so a downstream consumer of the ACS wire cannot emit a conformant trace; AGT warn is guardian-only (the stock gate reads a drift score no v0.1.0 payload carries).

This implementation does not claim acs-trace, acs-inspect, acs-provenance, acs-crypto, or acs-audit. It does not build an OTel exporter.

What ships

  • packages/conformance/ — intervention-point and verdict round trips, identity check, failure-domain split, Trace-pillar check
  • Separate renderers for matrix, mapping table, and trace rows (nothing named renderMatrix, so an upstream diff cannot later arrive as a column of this table)
  • Outbound response validation (inbound requests were already checked)
  • scripts/run-conformance.sh

Captured run: docs/demos/v7-runbook.md.

@afogel afogel changed the title V7: Session state and provenance carriage V7: Conformance matrix Aug 9, 2026
@afogel
afogel force-pushed the slice/v7 branch 2 times, most recently from dbf156f to 9e03ab3 Compare August 10, 2026 07:13
@afogel
afogel force-pushed the slice/v7 branch 2 times, most recently from 550e7c2 to 6b84c70 Compare August 11, 2026 11:07
…ables disagreeing with their own graph

Fix round 1. The split's consequence for V8 was recorded only in a
gitignored report, and three tables said things their neighbours did not.

**The obligation the split created is now written where a reader meets
it.** Adding N53 to SS V8's table left V8's own affordance list short an
entry -- item 6c's defect reproduced one slice over, discoverable only by
diffing two tables. SS V8 now carries a note saying N53 is new scope
added by V7's split, that this slice's affordances are U31, N45, N46,
N53, S12, and that `slices/v8/README.md` carries the same five. SS V7's
split note says the split reaches V8 rather than only that the row sits
elsewhere: until now U31 was rendered by a function V7 built and V8
inherited. Detail C's N53 row says "V8 builds it and V8 renders it" with
the reason -- its only input is V8's `diffSurfaces()`, so V7 can neither
build nor test it.

**N40's `Wires Out` was missing `→ N49`, and predates this task.**
Verified against `d3601d2`, the V7 scaffold commit: both affordance
tables have said `→ N41, → N42, → N43, → N44` since before round 4, while
the mermaid at `shaping.md:497` has carried `N40 --> N49` all along. A
reader working from the table alone wires four checks, never runs the
trace-pillar check, and the Trace non-claim drops out of the published
matrix -- which is D10's whole deliverable. The tables now match the
graph. Not caused here; fixed here because this task's subject is that
these two surfaces agree.

**SS V7 declares the store it reads.** V6's slice table lists three
stores and V7's listed none, though the mermaid has `S10 -.-> N41`, `-.->
N42` and `-.-> N48`, Detail C's shared-store row names all three as
readers, and this slice's own commitment 2 names S10 by ID. V7 declares no
store of its own, which is the honest reason -- so the row it gains is
the shared one, in the form SS V1 uses, and the README's affordance line
says both halves rather than being silently asymmetric with its sibling.

Two smaller ones. `README.md:58` scoped V7 as a coverage matrix and
dropped the mapping table (U32/N48) that C5 names as half the published
artifact; it now uses the two-noun form this branch already wrote at
`shaping.md:153`. `slices.md:404` still said `N49` measures and `U33`
renders, the one sentence item 6c quotes -- reading it before the table
collapses N52 out and inverts commitment 4. It now names all three.
…t yet meet

The note added to close the split's cross-slice gap ended by asserting
that slices/v8/README.md already carries the five affordances. It does
not -- it was written before the split and lists four -- so the sentence
meant to prevent an under-declared README was itself the reason a reader
would skip fixing it.
Commitment 2 of slices/v7/README.md splits three names apart: mapping.yaml
declares, the matrix measures, and neither may wear the other's job. Four
sentences still collapsed them, one of them in the runtime's own source.

- README.md's V7 row said "the matrix is the declaration". R5.3 is a
  statement this project makes and can be wrong about; the matrix is what
  catches it. Now the declaration, with a measurement attached to each line.
- §V7 said "R5.3 lands here: the matrix *is* the profile declaration",
  which is where the README's line came from. Retraction recorded there.
- C5 said "test matrix" -- a third name for the 8 x 5, in the one
  commitment whose subject is which two artifacts get published.
- The order rationale still said V7 "can only be green", the success name
  §V7 retracted for making the matrix redefine its own claim.
- map-verdict.ts, server.ts and server.test.ts each said the
  intervention_points table is what V7's conformance matrix publishes. It
  is what V7's mapping table publishes; the matrix measures round trips
  through it.

Two freezes added ahead of the first V7 file:

5. The harness imports the Guardian and calls it, and the Inspector's
   import ban does not transfer -- R5.1/R5.2 are claims about a third
   party reading the wire, C2/R5.3 are claims about this runtime, and
   calling the runtime is the evidence for those. The bound runs the other
   way: the 8 rows and 5 columns come from the pinned SDK's own
   InterventionPoint and Decision consts, never from a list the Guardian
   hardcodes, or the matrix measures the Guardian against itself.

6. N43 is a recomputation check, and nothing here is named for a field ACS
   v0.1.0 does not have.

Two findings recorded in §V7, both measured while answering the freeze:
R1.4's cell resolves green-for-the-Guardian, red-for-a-wire-consumer like
the warn column and D10's Trace attributes -- v0.1.0 carries no
action-identity field on any of its 43 schemas -- and N43 has no input
today, because PolicyBridge.evaluate drops the identities the SDK returns.

795 pass / 1 skip / 0 fail, typecheck clean.

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
The plan is a projection of §V7, not a second place to decide things, so
every decision it took is amended back into the slices doc in this commit.

N43's seam -- a second message on the role, not a fatter answer.
PolicyBridge gains evaluateWithEvidence(), and evaluate() is implemented in
terms of it: one call into the SDK, the narrow answer provably a projection
of the wide one, and server.ts untouched. Widening evaluate's RETURN would
re-create the bag the PR #10 review removed, with the harness as its only
new reader. A separate factory would create a second path to AGT, and a
conformance harness certifying the path production does not take.

Upstream issues -- not V7's. The slice lands the measured cells and the
stated consequence; filing stays a separate deliberate act.

Measured while planning, against the pinned SDK rather than read from AGT's
docs: the identity is SHA-256 of key-sorted, whitespace-free JSON of the
policy input, and enforced_identity is the same hash after replacing
policy_target.value ALONE. The snapshot's copy of that leaf is not updated
-- so AGT's identity binds to the policy target it rewrote, not to the
document the host executes. That makes R1.4 the third finding to resolve
green-for-the-Guardian / red-for-a-wire-consumer, after R1.3's annotations
and D10's Trace attributes, and it sharpens the v0.2 ask: adding
enforced_identity to AcsResult is necessary and not sufficient, because the
wire never carries the policy input a host would check it against. The
(a)/(b) fork is recorded rather than resolved.

Also amended: R1.4 now reads "Must-have, qualified" on R1.3's precedent,
with a Fit Check note; a cell has three answers and guardian_only is named;
the handshake response is unexpressible against response-envelope.json and
the validator reports rather than throws; N40's runner is `conformance`,
since every other package here is a bare noun; N47's retired name is gated
rather than merely retired; and risk row 7 no longer implies the Node SDK
alone made N43 reachable.

795 pass / 1 skip / 0 fail, typecheck clean.

Slice: #8

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
… reporter

Task 2 review round 1 fixes:
- unexpressible is now reported on stderr (naming method and reason),
  matching what the comment claims rather than being computed and discarded
- the response-validation reporter in handleAcsRequest is wrapped in
  try/catch, matching envelope-log-sink.ts's own "total means total"
- reworded both report messages so the composite line stays true whether
  a response actually failed the schema or was never checked at all
- moved isServerHelloResponse inside validateResponse's own try/catch, so
  "reports, never throws" covers the whole function body
- corrected the strictRequired mechanism comment: Ajv's check does see
  across the not/oneOf boundary via inherited definedProperties: false
  positive is a keyword-compile-order artifact (not/oneOf compile before
  properties), not a boundary the check cannot see across
- fixed test 2's fixture to use a schema-valid request_id and assert on
  pointer/message, so it fails for the reason its name claims
- added a fixture proving isServerHelloResponse's AND is load-bearing: a
  decision response missing only `decision` reports invalid, not
  unexpressible

Slice: #8

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
…ement caught

The shaping round recorded that `AcsResult` has no `evaluator` field, and that
`confidence`, `evaluator_version` and `model_id` do not exist on it either. That
propagated into V7's plan and into this task's brief.

It is wrong. `response-envelope.json`'s `$defs.AcsResult.properties.metadata`
declares exactly `evaluator`, `evaluator_version`, `evaluation_duration_ms`,
`model_id` and `confidence`, and says why: "ACS-defined evaluator and
observability metadata -- keeping the split clean lets Trace consumers key on a
stable shape." The fields exist, and they exist for this.

The cells stay red. Neither `metadata` nor its members are required, and
`AcsResult.required` is `[type, acs_version, request_id, decision]`. So the
attributes are unemittable by a wire consumer for the reason `acs.capability` is
-- present but optional -- and not for the reason four documents gave.

That changes what V7 publishes, in the direction of a sharper claim. The shape
the three findings share is optionality, not absence: R1.3's `annotations`,
D10's Trace attributes and R1.4's identity are all things the envelope may carry
and is never obliged to. Only the identity is genuinely missing. The v0.2 ask
narrows from "add these fields" to "require the ones already there, and add the
one that is not."

And this implementation leaves the slot empty: `AcsDecision` declares no
`metadata` member and `mapVerdict` never sets one, so every decision this
Guardian sends omits an evaluator identity the spec already has a home for.
Available today, not a v0.2 ask -- filed for a later slice rather than absorbed
here.

N49 resolves each row against the schema at runtime rather than transcribing the
expected answers, which is the only reason this surfaced.

Slice: #8 · N49

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
…ribute name

The facts file said acs.evaluator, acs.confidence, acs.evaluator_version and
acs.model_id have no property on AcsResult at all. That was derived from a
top-level property listing and never descended into metadata, and it was
wrong: response-envelope.json's $defs.AcsResult.properties.metadata declares
all four. checkTracePillar's own decision-attribute site derivation
(attribute.slice("acs.".length)) reproduced that wrong answer mechanically
for the same four, because it assumed every decision_event attribute is a
direct AcsResult sibling.

The verdict does not move -- metadata is optional on AcsResult and declares
no required list of its own, so all four stay unemittable -- but the reason
does, from "absent entirely" to "present but optional", the distinction this
task's whole rule turns on. Replaced the mechanical derivation with an
explicit, throwing site table (decisionAttributeSite), mirroring
stepAttributeSite: a future decision_event attribute this table does not
name now throws instead of silently degrading to "no wire source".

Two tests pinned the wrong answer and now pin the right one. Two more were
weaker than their names: the row-count test now asserts the exact span
multiset, not just the total; a new test ties the frozen six-method scope to
mapping.yaml's own non-null acs_method values so it is self-checking rather
than true-for-now. render.ts's own doc comment cited acs.evaluator as its
example of the null-wireSource case, which stopped being true once the fix
landed -- none of today's 17 rows takes that branch any more, so the
RenderOptions.color test now uses a synthetic fixture that does, and the
comment says so instead of citing a row that no longer demonstrates it.
Documented, not modeled: resolveField still does not walk AcsResult's allOf
(acs.reasoning reads "optional" under the rule as stated, true only for
`allow`), and acs.provenance.origin is measured under a narrower,
now-explained rule than the other 16.

Slice: #8 · N49, N52 · review round 1 fix

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
…nt retired them

Three sites in the shaping doc, the slices doc and this slice's plan still
carried numbers V7's own measurement had replaced. Each is corrected against the
capture in docs/demos/v7-runbook.md rather than against the sentence beside it.

- Shaping doc, D10's decision row: "two of its required span attributes are
  declared on the wire but never required there" -- the evidence table three
  lines below it has three rows covering five attributes, and N49 measured six
  once acs.reasoning is included. Corrected to six, with both prior wordings
  named, since this row has now been corrected twice.

- Slices doc, sec V7: "Expect two cells to be honestly red -- now four" was a
  planning expectation the published matrix superseded, and it counted D10's
  Trace attributes as two of the four. Both halves retired. A point with no ACS
  v0.1.0 target is red at all five AGT verdicts, so D4's two model-call points
  are ten unexpressed cells rather than two; four more are unexpressed at
  transform, where the point declares no modifications rule. Counted off the
  capture: 18 expressed, 8 guardian_only, 14 unexpressed. And a Trace attribute
  is a U33 row, not a cell of the 8 x 5 -- commitment 4 froze that distinction
  after this sentence was written.

- The plan's embedded cells.ts comment, which no longer matched the file after
  the same phrase was corrected in packages/conformance/src/cells.ts.

Two review passes read the slices-doc sentence and cleared it, both asking
whether its phrase belonged to the stale-count class rather than whether the
whole sentence survived the measurement. It did not.

Slice: #8

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
…opped line

The whole-branch review found the matrix itself sound but the prose around it
carrying sentences that outlived their own measurement. Three classes of fix:

Retracted vocabulary still in use. The demo sentence in four places (both
READMEs, the master slices doc's summary table, the runbook's quote) still
said "green"/"red" after this slice retracted those as status names in favor
of expressed/guardian_only/unexpressed; the same slip recurred in the Detail C
affordance table's N49 row ("a cell is green"), which also called a trace row
a "cell" when a trace row is not a cell of the 8x5 (commitment 4).

Stale counts, re-measured. Ten `it` blocks in test/invariants.test.ts, not
eight -- the ninth is V5's own review-round-3 hookmap gate, the tenth is V7's
renderMatrix gate; both README.md's tally and its enumeration now say so. The
three-independent-findings split (warn/Trace/identity) is one optionality and
two absences, not "two optionality, one absence" -- both documents of record
said it backwards, and the shaping doc's copy gets an explicit second-
correction note since this is its second miscount. D10's own evidence table
has three red rows covering five attributes, not two; four of those five sit
under `metadata`, not three. `identity` occurs four times across two spec
files, not twice. Inbound requests compile against three schemas, not "all
43" -- validate-response.ts had newly written the phrasing its own sibling
module had just retracted; the master doc's parallel claim gets the same
correction, credited to this slice's own measurement.

Evidence gaps closed. verify:zero-diff's root-README claims read as though the
check currently passes; it proves R3.4 only with HEAD at slice/v5, and a bare
run from any later HEAD -- including this one -- fails for reasons later
slices legitimately added, which the runbook already captures. The runbook's
own capture of that failure was itself short one line (the wrapper's own exit
message), fixed by re-running the command rather than hand-editing the block.
The matrix's escalate column and two transform cells publish a check-mark
under the matrix's stated subject (ACS's expressive power) with no footnote
saying this Guardian's own ask/modify responses are separately measured
schema-invalid; the runbook's "What was not verified" section now says so.

Plus a page of one-line corrections in the same vein: a trace-pillar row
count, a dead import, an "above" that should read "below", a test name
claiming more than its stub measures, a citation to a slice README that does
not exist yet, and a few dangling cross-references.

Verified: bun test (880 pass), bun run typecheck, bun run verify:pin all
clean; bun run conformance and verify:zero-diff both re-run and diffed
byte-identical against their runbook captures.

Slice: #8

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
…m a third time

The final review named four sites carrying the retracted green/red status names
and two carrying verify:zero-diff in the present tense. Fixing those surfaced
three more of the same, in documents this slice had already amended.

- README's V5 status paragraph said verify:zero-diff "checks mechanically",
  the same present tense the review corrected in the Verify section and the V1
  claims table. It checked, with HEAD at slice/v5, which is the only place the
  claim is measurable.

- The shaping doc's two Fit Check notes described a V7 matrix cell as "green for
  the Guardian, red for a wire consumer". The matrix publishes no green and no
  red; the status is guardian_only. Same in the slices doc's own warn paragraph,
  which is where one of the three findings is defined.

- R1.4's Fit Check note ended "the identity half of the claim is the only one
  where the field is genuinely missing". R1.3's annotations input is missing
  too: no ACS v0.1.0 payload carries a field a drift score could be derived
  from. So one of the three findings is optionality and two are absence. This
  is the third pass on that sentence -- it first said absence, was corrected to
  optionality during Task 8, and was over-corrected in the process. What the
  three share is not one mechanism but one consequence: a wire consumer cannot
  rely on the evidence, twice because it is absent and once because it is
  optional.

Left alone deliberately: the same green/red shorthand in V6's plan, in
packages/guardian/src/ifc-labels.ts, and in the slices doc's V6 section. Those
describe V6's finding in V6's vocabulary and are that slice's to retire.

Slice: #8

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
…ine number

The scoped re-review of the previous fix found five residuals, four of them in
the paragraphs immediately beside text that round had just corrected.

- Two live sentences still called a V7 matrix cell "green for the Guardian, red
  for a wire consumer" -- the shaping doc's R1.3 evidence note and the slices
  doc's R1.4 paragraph. The status is guardian_only.

- D10's evidence table headed its third column "Cell" and its prose called its
  rows cells. They are U33 trace rows, which commitment 4 keeps out of the 8 x 5
  entirely. The column now asks the question it answers, and the note says which
  marks belong to which table.

- Two citations in slices/v7/README.md pointed at slices-doc line numbers that
  the previous commit's own inserted paragraph pushed down by two, so both
  addressed the wrong note. They now cite the paragraphs by their titles. A line
  number into another file is a citation that goes stale when anything above it
  is edited, which is what happened here within one commit.

Slice: #8

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
Comments across packages/conformance, three files in packages/guardian, and
test/invariants.test.ts carried review archaeology ("PR #10 review, Critical",
"review round 1, Important N", "the brief's Step 3"), internal shaping
identifiers (N40-N52, R1.x, R2.4, R3.2, R5.3, C2, D4, U21/U30/U32/U33,
commitment N, §V1-V7) that resolve only against documents not in front of a
reader, and prose dense enough to need decoding. Test names lost the same
identifiers, restated in words where dropping them would leave the name
vague.

Every measured fact this slice depends on is untouched: cell-status names,
coordinate and row counts, the rendered coverage-matrix and trace-pillar text
the tests compare against, and every string literal used as fixture data or
an assertion value. Thrown-error messages and one console.error kept their
diagnostic and lost only the bare identifier prefix.

Verified: bun run typecheck clean, bun test 880 pass / 1 skip / 0 fail,
bun run conformance exits 0 with output unchanged.

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
Sixteen comments pointed at other files by line number. Every one checked was
already wrong: server.ts:664 named extractId rather than evaluateStep's catch,
validate-envelope.ts:359 was a blank line, and guardian-client.ts:70 pointed at
a struct field in the original slice too. A citation that drifts is worse than
none, because a reader trusts it.

Each now names the function or constant it means, which does not move when the
lines above it change.

Claude-Session: https://claude.ai/code/session_019qZbQWyJHrYG7UodpyKjYr
Critical -- the response check reported four different facts through a
boolean called valid. The catch arm put "never checked" into
valid: false, the same shape as "checked and rejected", so a deployment
whose schema registry cannot build looked like one sending invalid
responses. It is now a status: checked_valid, checked_invalid,
unexpressible, unchecked, and server.ts reports each on its own line.
The function is checkResponse, not validateResponse, because inbound
validateEnvelope throws and stops the request while this reports and
still sends -- and the claim that the coverage matrix carries the same
cell is dropped, since a handshake is not a point-by-verdict coordinate
and unexpressible is not the matrix's unexpressed.

The intervention-point check never read a verdict, yet stamped every
resolved point expressed across all five columns; mergeCells then hid
those stamps behind a worse status, which made the published grid honest
and that check's own output a lie if read alone. It answers per point
now, and coverageCellsFromInterventionPoints emits cells only where the
question resolves the whole column set.

Two more names hid a second job. roundTrip reclassified warn as
guardian_only, which is a drift-score coverage finding rather than part
of a round trip; it is a named second step. One function was one name
over two measurements, now checkInputIdentity and checkEnforcedBinding
over one shared evaluation, with coverageCellsFromIdentity named as the
projection stage it is.

PolicyBridge and EvidenceBridge are separate roles. Production tells
evaluate and nothing else, so a Guardian stand-in no longer implements a
measurement question production never asks; createBridge answers with
both. checkDenyFailsClosed is named for what it returns, is told a
StartedGuardian and the already-loaded mapping rather than a URL string
and a second read of mapping.yaml, and imports the dispatch code from
the Guardian instead of copying the number it is measuring. CoverageMatrix
and MappingTable now occupy their nouns, and PINNED_AGT_CLONE stops
spending the upstream stem on the pinned ref.

The same pass retires the planning identifiers from this package. They
were worst here because the harness PRINTS them: the four checks were
called N41 to N44 in measuredBy and in the legs report, the section
headings named U30, U32 and U33, and a published footnote cited a domain
number defined only in a module header. Every one of them is now the
thing it names, so the published matrix can be read by someone who has
never seen a planning document.
Renaming the clone's environment variable to PINNED_AGT_CLONE moved the
reader but not the writer: scripts/run-conformance.sh still exported
UPSTREAM_AGT_CLONE, so `bun run conformance` reported the policy-input
schema check as DID NOT RUN while looking like a complete run. That is
the failure mode this check is built to avoid -- the module self-skips
by design when the variable is absent, so a stale writer degrades
silently instead of erroring.

Caught by running the script rather than the suite: `bun test` never
sets the variable, so no test could have seen it. The script now reports
RAN -- validated pre_tool_call, post_tool_call.

Two comments in server.test.ts pointed at validate-response.ts and a
validateResponse function, neither of which exists after the rename.
The coverage matrix filed both under `unexpressed`, and the exit rule
matched that status together with the one literal reason `mergeCells`
writes for an untouched coordinate. So the only thing that could ever
fail `bun run conformance` was a bug in the merge's own wiring: a verdict
that stopped surviving its own round trip, a point whose declared ACS
method resolved somewhere else, a resolver that threw -- each carried a
different reason and was counted as a resolved cell. `mapping.yaml`
could ship broken and the instrument that exists to measure it exited 0.

Split the classes where they are decidable rather than flipping the rule,
which would have failed the honest gaps too and pressed the matrix towards
being all-expressed. `CellStatus` gains `contract_violated`, ranked worst
in the merge; `verdicts.ts` and `intervention-points.ts` resolve a checked
declaration that does not hold to it and keep `unexpressed` for the answers
that are about ACS. Which of the two a mapVerdict throw is comes from
reading the declaration -- a row with no `modifications` rule predicts the
refusal, and mapping.yaml says so in the table -- never from matching the
error's text.

`resolveExitCode` now compares statuses and reads a hole off `measuredBy`
being empty, so no rewording can silence either half, and an `expressed`
cell no check claims fails too. The renderer shows the new status with its
own glyph and says on the table's face which one exits non-zero; the real
`mapping.yaml` still exits 0, and both halves of that are now asserted.
@afogel
afogel marked this pull request as ready for review September 6, 2026 10:14
@afogel
afogel merged commit 0156d78 into main Sep 6, 2026
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.

V7: Conformance matrix

1 participant