Skip to content

fix: say precisely what the soulbound verdict establishes, on both surfaces - #605

Merged
Zyrtnin merged 3 commits into
mainfrom
fix/cli-soulbound-verdict-precision
Sep 4, 2026
Merged

fix: say precisely what the soulbound verdict establishes, on both surfaces#605
Zyrtnin merged 3 commits into
mainfrom
fix/cli-soulbound-verdict-precision

Conversation

@Zyrtnin

@Zyrtnin Zyrtnin commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Two claims the CLI made that the code does not support. Stacked on #604 (shares glyph_inspect.py).

1. The tx-listing row dropped every qualifier

It printed variant=… (non-transferable at consensus) and nothing else, while the classifier attaches a note:

does NOT verify that ref names a live Glyph singleton, that the singleton is actually held here, or that the covenant is free of defects — the covenant is a pre-external-audit prototype

The standalone script card prints that in full. The tx listing is the path most people meet the tool on, and "non-transferable at consensus" is exactly the sentence a credential or swap gate would over-trust. The sibling container-legacy branch already points the reader onward; soulbound didn't.

2. Both variants were called "a byte-identical self-clone"

The fixed-index builder does compare whole scripts (OP_OUTPUTBYTECODE / OP_UTXOBYTECODE).

The composable one compares code-script hashesOP_CODESCRIPTBYTECODE_UTXO OP_HASH256 + OP_CODESCRIPTHASHOUTPUTCOUNT_OUTPUTS — and its own docstring says "code-identical clone", not byte-identical.

The two coincide only because neither builder emits OP_STATESEPARATOR, so the code script is the whole script. That is not a detail to paper over: code-script equality with a state prefix is precisely the shape that lets the owner change between hops, which classify_soulbound now reports as MUTABLE_STATE_COVENANT (#600). The composable card now names the weaker constraint and says why it is sufficient here, so the distinction is visible rather than assumed.

No exploit is claimed for (2) — with no state separator a clone must still be byte-identical in practice. The defect is that the renderer asserts a stronger property than the variant's bytes impose, on a credential primitive, in the surface a human reads.

Verification

Both planted: calling composable byte-identical again fails 2; dropping the tx-listing qualifier fails 1.

Worth recording — the first attempt at that second restore was eaten by the shell. Backticks in the replacement string became command substitution, so the plant stayed live while the command printed an error that looked like a test result. Restored via a heredoc and confirmed with git diff. Same hazard as backticks in git commit -m.

Full suite: 11,016 passed, 192 skipped, 1 xfailed.

🤖 Generated with Claude Code

Mudwood Labs and others added 2 commits September 3, 2026 21:47
`docs/concepts/glyph-inspect-tool.md` listed this under the tool's threat model:

  Token names and tickers are run through the TR39 confusables skeleton check
  (`looks_confusable_with_latin`) — a Cyrillic-spoofed "USDC" is flagged with a
  warning banner before the user sees the rendered metadata.

`looks_confusable_with_latin` had NO PRODUCTION CALLER. Repo-wide it was its own
definition, a facade re-export, and its tests. The CLI ran no confusables check
at all; the browser page used a weaker script-mixing heuristic that fires on any
wholly non-Latin name.

Wired rather than downgraded, because the doc describes a real protection and the
function already exists and is tested. Computed once in `_inspect_core` so both
surfaces read one verdict, and the CLI prints it ABOVE the field it applies to —
a look-alike warning printed after the name is one the reader has already acted
on.

IT REPORTS MIMICRY, NOT FOREIGNNESS. "トークン" and "中文" are in the checker's
own not-flagged examples; "USDС" with a Cyrillic С, "раypal" and small-caps
"ᴜꜱᴅᴄ" are flagged. A warning firing on every legitimate Japanese token is the
false positive that trains a reader to ignore the real one, which this repo names
as a hazard in as many words elsewhere.

A COLLISION FOUND BY LOOKING. `glue.py` produces warnings under the same
`display_warnings` key from its heuristic and ASSIGNED it, so on the browser path
the stronger check's findings would have been silently replaced. Two producers,
one field name, last writer wins. It now seeds from the classifier and uses
setdefault, so the more specific TR39 reason survives where both fire.

MY OWN TEST SUITE FAILED THE RULE IT WAS WRITTEN FOR. The first version called
`_confusable_warnings` directly and nothing else: unwiring it from the classifier
— restoring the exact defect, a capability with no production caller — left all
16 tests green. A suite about a reachability bug that could not detect one.
Reachability tests now build a real reveal transaction and read the warning out
of `_classify_raw_tx`; that plant fails 1.

Three plants, each caught: unwiring the classifier, dropping the CLI rendering,
and restoring the bridge overwrite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two claims the CLI made that the code does not support.

1. THE TX-LISTING ROW DROPPED EVERY QUALIFIER. It printed
   `variant=... (non-transferable at consensus)` and nothing else, while the
   classifier attaches a `note` saying the verdict "does NOT verify that ref names
   a live Glyph singleton, that the singleton is actually held here, or that the
   covenant is free of defects - the covenant is a pre-external-audit prototype".
   The standalone script card prints that in full; the tx listing is the path most
   people meet the tool on, and "non-transferable at consensus" is exactly the
   sentence a credential or swap gate would over-trust. The sibling
   container-legacy branch already points the reader onward; soulbound did not.

2. BOTH VARIANTS WERE CALLED "a byte-identical self-clone". The fixed-index
   builder does compare whole scripts (OP_OUTPUTBYTECODE / OP_UTXOBYTECODE). The
   COMPOSABLE one compares CODE-SCRIPT HASHES - `OP_CODESCRIPTBYTECODE_UTXO
   OP_HASH256` + `OP_CODESCRIPTHASHOUTPUTCOUNT_OUTPUTS` - and its own docstring
   says "code-identical clone", not byte-identical.

   The two coincide only because neither builder emits OP_STATESEPARATOR, so the
   code script IS the whole script. That is not a detail to paper over: code-script
   equality WITH a state prefix is precisely the shape that lets the owner change
   between hops, which `classify_soulbound` now reports as MUTABLE_STATE_COVENANT.
   The composable card now names the weaker constraint AND says why it is
   sufficient here, so the distinction is visible rather than assumed.

No exploit is claimed for (2) - with no state separator a clone must still be
byte-identical in practice. The defect is that the renderer asserts a stronger
property than the variant's bytes impose, on a credential primitive, in the
surface a human reads.

Verified by planting both: calling composable byte-identical again fails 2, and
dropping the tx-listing qualifier fails 1.

Noted for the next restore: the first attempt at that second restore was eaten by
the shell - backticks in the replacement string became command substitution and
the plant stayed live while the command reported an error that looked like a test
result. Restored via a heredoc and confirmed with git diff.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ew tests

Per-hunk: where this branch carried the superseded unconditional
`display_warnings` line and the empty-dict test contract, main's conditional form
wins; where it adds the soulbound-verdict tests main does not have, it wins.
@Zyrtnin
Zyrtnin merged commit 7e27ab9 into main Sep 4, 2026
13 checks passed
@Zyrtnin
Zyrtnin deleted the fix/cli-soulbound-verdict-precision branch September 4, 2026 08:35
Zyrtnin pushed a commit that referenced this pull request Sep 4, 2026
This branch also predates the conditional `display_warnings` form; main's wins,
and this branch's dMint cap tests are kept.
Zyrtnin added a commit that referenced this pull request Sep 4, 2026
Stacked on #605. The CLI printed `total supply: N` for a dMint contract,
where N is `max_height × reward`. That is **this contract's** cap.

A dMint token commonly deploys several parallel contracts against **one
`token_ref`**, so the token's supply is the sum across them — and the
browser tool computes `reward × max_height × N` for the same token.
**Two surfaces of one tool answered "what is this token's supply" with
figures differing by the parallel-contract count**, and the CLI's
carried no qualifier.

The "if all mints succeed" hedge existed — in a code comment, where no
reader sees it.

This renderer is handed one contract script and can never see the
others, so the fix is to **name the quantity** rather than attempt a
total it has no way to know:

```
  this contract's cap: 1,050,000,000 photons (21,000 mints x 50,000)
  (the cap IF every mint succeeds, and for THIS contract only —
   a token may deploy several contracts against one token_ref,
   and its supply is the sum across them)
```

The arithmetic is unchanged and still shown — it is right for the one
contract, which was never the defect.

An existing CLI test pinned the old label and is **updated rather than
deleted**, with the reasoning in place. Its docstring said "the derived
total supply line" and is aligned too: the sentence that names the thing
is part of the claim.

Planted the bare label back — 3 failures. Full suite: 11,021 passed, 192
skipped, 1 xfailed.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Mudwood Labs <opensource@mudwoodlabs.com>
Co-authored-by: Claude Opus 5 (1M context) <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