fix: render the OP_RETURN payload verdicts on the public inspect page - #597
Open
Zyrtnin wants to merge 1 commit into
Open
fix: render the OP_RETURN payload verdicts on the public inspect page#597Zyrtnin wants to merge 1 commit into
Zyrtnin wants to merge 1 commit into
Conversation
The browser page rendered NONE of the OP_RETURN payload fields. A forged HashMark v2 showed as an authoritative-looking `OP_RETURN-HASHMARK-V2` badge with no signer and NO VERDICT: the affirmative half of the record survived and the part that contradicts it did not. Same defect I just fixed in the CLI, on the surface with the widest audience and no install step. The data was never the problem. glue.py re-uses the same classify_raw_tx the CLI uses, so hashmark, attestation, message and relationships were all in the payload, crossing the bridge, and discarded by the renderer. `renderOutputRow` already carried the rule in a comment - "a field that only the standalone-script card shows is a field most readers never see" - so this is the third instance of the shape that comment was written about. There is now ONE `appendOpReturnPayload` used by both cards rather than a second copy. Two smaller things the rename broke, both found by reading rather than reported: the title map and `_structuralQualifierNote` key on the literal "op_return", so renaming the type to `op_return-hashmark-v2` silently dropped the card title AND the "unspendable data carrier" note from exactly the outputs that had just gained content. WHY THE DRIFT GUARD DID NOT CATCH IT. tests/web asserts that every key the classifier emits appears in the rendered text - which is structural about FIELDS and hand-kept about SHAPES. The check only ever saw the payloads that `_SHAPE_NAMES` produces, and no shape in that tuple produced a hashmark or a message, so "every key must be rendered" was vacuously true for the keys that had just been added. A hand-kept list of what a guard covers, for the third time in this session. Fixed at that level too: the corpus is now checked against the `type` values recovered from the classifier's own source, both directions - a type with no corpus shape means every test in that file is blind to it, and a corpus shape whose type no longer exists is guarding nothing. The v2 shape carries a random signature deliberately, so it decodes and does NOT verify: "does not verify" is the line that was missing from this page entirely, and a corpus of only valid records would not have demanded it. The guard also now recurses into nested payload blocks rather than asserting the repr of a dict, which no renderer could ever emit. Leaves rendered as prose are listed with reasons, the same rule the top-level omission tables already use. Also fixed: glue.py truncated `hex` and `data_hex` to 200 chars while inspect.js told the reader "the JSON drawer carries the full bytes", so the drawer and the Copy JSON button held a prefix, beside a header reporting the true byte count. Newly material rather than untidy - `data_hex` is now the only place a HashMark or msg record's raw bytes appear, and the UI points at it. The row stays scannable because inspect.js truncates for DISPLAY at 64 chars on its own, which is the right layer: only the display needs to be short. Verified by planting three defects, each caught: removing the shared renderer from either card (2 failures each), and forgetting to add the new shapes to the corpus, which the coverage check names explicitly. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The browser page rendered none of the OP_RETURN payload fields. A forged HashMark v2 showed as an authoritative-looking
OP_RETURN-HASHMARK-V2badge with no signer and no verdict — the affirmative half of the record survived and the part that contradicts it did not. Same defect as the CLI one in #594, on the surface with the widest audience and no install step.The data was never the problem:
glue.pyre-uses the sameclassify_raw_txthe CLI uses, sohashmark,attestation,messageandrelationshipswere all in the payload, crossing the bridge, and discarded by the renderer.renderOutputRowalready carried the rule in a comment — "a field that only the standalone-script card shows is a field most readers never see" — so this is the third instance of the shape that comment was written about. There is now oneappendOpReturnPayloadused by both cards.Two smaller things the type rename broke, found by reading rather than reported: the title map and
_structuralQualifierNotekey on the literal"op_return", so renaming toop_return-hashmark-v2silently dropped the card title and the "unspendable data carrier" note from exactly the outputs that had just gained content.Why the drift guard didn't catch it
tests/webasserts that every key the classifier emits appears in the rendered text. That is structural about fields and hand-kept about shapes — the check only ever saw payloads that_SHAPE_NAMESproduces, and no shape in that tuple produced a hashmark or a message. So "every key must be rendered" was vacuously true for the keys that had just been added.A hand-kept list of what a guard covers, for the third time in this session (after
MarginPolicy's validated fields and the vendored-source digest list).Fixed at that level: the corpus is now checked against the
typevalues recovered from the classifier's own source, both directions — a type with no corpus shape means every test in that file is blind to it, and a corpus shape whose type no longer exists is guarding nothing.The v2 shape carries a random signature deliberately, so it decodes and does not verify. "Does not verify" is the line that was missing from this page entirely, and a corpus of only valid records would not have demanded it.
The guard also recurses into nested payload blocks now, rather than asserting the repr of a dict (which no renderer could emit). Leaves rendered as prose are listed with reasons, the same rule the top-level omission tables already use.
Also fixed
glue.pytruncatedhexanddata_hexto 200 chars whileinspect.jstold the reader "the JSON drawer carries the full bytes" — so the drawer, and the Copy JSON button, held a prefix, next to a header reporting the true byte count. Newly material rather than untidy:data_hexis now the only place a HashMark ormsgrecord's raw bytes appear, and the UI points at it. The row stays scannable becauseinspect.jstruncates for display at 64 chars independently — the right layer, since only the display needs to be short.Verification
Three plants, each caught: removing the shared renderer from either card (2 failures each), and forgetting to add the new shapes to the corpus — which the coverage check names explicitly:
Full suite: 11,002 passed, 192 skipped, 1 xfailed.
🤖 Generated with Claude Code