Skip to content

hold graph rows members to their exact spelling - #131

Merged
kikashy merged 2 commits into
mainfrom
graph-rows-exact
Aug 24, 2026
Merged

hold graph rows members to their exact spelling#131
kikashy merged 2 commits into
mainfrom
graph-rows-exact

Conversation

@kikashy

@kikashy kikashy commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Closes a live defect found during #128 design recon (empirically confirmed): the graph rows loader relied on DisallowUnknownFields alone, and encoding/json case-folds member names — {"Cases":[…]} and {"ID":…} were accepted and silently read as the members they are not. This is the third instance of the class (pack matrix closed it under ADR-0025; the MCP argument decoders under ADR-0028), live here because the rows format predates both.

The hold runs on the document and every row before the decoder: a case-variant is refused naming the exact spelling; a stranger is refused as unknown. Table-driven tests pin both refusal shapes plus the fixture still loading; the hold is mutation-verified (removing it re-admits the folded document). The existing rot-pin (expectedHandoffTarget refused on graph rows) is unaffected and now produced by the hold with the same code.

#128's graphMatrixVersion gating will rest on "a matrix is a closed input" — this makes that premise true first.

Material-decision impact: public-surface; review: round to be posted on this PR

🤖 Generated with Claude Code

kikashy and others added 2 commits August 24, 2026 05:44
encoding/json case-folds member names, so a rows document carrying
"Cases" or "ID" bound past DisallowUnknownFields and was silently
read as the members it is not — the same hole the pack matrix closed
under ADR-0025 (a matrix is a closed input) and the argument decoders
closed under ADR-0028, live here since the rows format predates both.
The hold runs on the document and on every row before the decoder,
refusing a case-variant by naming the exact spelling and a stranger as
the unknown member it is. Found during design recon for #128, whose
graphMatrixVersion gating would rest on a closedness premise this
defect made false.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Brian Jin <35789537+kikashy@users.noreply.github.com>
The review found the same case-fold one level deeper than the row
members: DecodeDisposition — the one gate every expected disposition
passes, corpus, pack matrix, and graph rows alike — relied on the
strict decoder, so "Kind", "OutcomeID", or a handoff carrying
"State" bound and canonicalized as the members they are not, on the
pack surface as much as this one, including beside the canonical
spelling, which the fold silently overwrote. The gate now holds the
disposition's members and its handoff member's to their exact §8.3
spellings, once, for every reader; expectedNodes keys are node ids and
are deliberately not held. And a cases element that is not a JSON
object is refused as the shape defect it is, rather than decoding to a
zero row misdiagnosed as declaring no id.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Brian Jin <35789537+kikashy@users.noreply.github.com>
@kikashy

kikashy commented Aug 24, 2026

Copy link
Copy Markdown
Contributor Author

Cross-vendor review — disposition table

Reviewer: Codex (codex-cli 0.145.0). Reviewed SHA: 4eedd53 (full output captured to file). 2 findings, both confirmed, applied in 91b5c97 under the dispositioned-finding exception.

# Finding Severity Disposition
1 The same fold lives one level deeper: DecodeDisposition — the one gate every expected disposition passes, on the pack matrix as much as this surface — accepted "Kind", "OutcomeID", "Handoff":{"State"}, including beside the canonical spelling, which the fold silently overwrote medium Fixed at the gate, per its own "one gate, however many readers" principle: exact §8.3 member spellings held at the disposition and inside its handoff member; expectedNodes keys deliberately not held (they are node ids). Six-case table incl. the alias-beside-canonical overwrite; mutation-verified; changelog states the pack-surface effect
2 cases:[null] decoded to a zero row misdiagnosed as "declares no id" low Fixed — a non-object element is refused as the shape defect it is (JPS-GRAPH-ROWS-SHAPE), matching the pack matrix's precedent; pinned by test

Gate green at 91b5c97 (full suite + bundled conformance). Ready to merge.

🤖 Generated with Claude Code

@kikashy
kikashy merged commit b801857 into main Aug 24, 2026
6 checks passed
@kikashy
kikashy deleted the graph-rows-exact branch August 24, 2026 09:58
@github-actions

Copy link
Copy Markdown

🎉 Thank you for your contribution to Judgment Pack!

If you’d like another task, choose a path that interests you:

@kikashy kikashy mentioned this pull request Aug 24, 2026
kikashy added a commit that referenced this pull request Aug 24, 2026
The release completing the wire-only client's upstream asks, all found
building the desk against v0.18.0: the document-digest binding on every
graph matrix run and validation (ADR-0030, #132), per-node evaluation
traces on request (ADR-0031, #127), the handoff-target assertion on graph
rows discharging ADR-0025's deferral (ADR-0032, #128), and the graph rows
spelling holds (#131). CHANGELOG's Unreleased section becomes the 0.19.0
entry.

Signed-off-by: Brian Jin <35789537+kikashy@users.noreply.github.com>
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.

1 participant