Skip to content

fix(pm): make the stamp contract quotable — Markdown code is a quotation, rendered as written - #18730

Merged
os-justin merged 1 commit into
mainfrom
claude/issue-18543-post-stamped-quoted-token
Sep 17, 2026
Merged

os-justin merged 1 commit into
mainfrom
claude/issue-18543-post-stamped-quoted-token

Conversation

@os-justin

@os-justin os-justin commented Sep 17, 2026

Copy link
Copy Markdown
Collaborator

Fixes #18543

Clause-②: no

scripts/pm/post-stamped.mjs only. Non-governed scripts/pm/**; skip-changeset (nothing in any package's files[] moves).

The defect, in one paragraph

The tool substituted the act-clock token wherever it appeared, including inside a passage that was quoting the token itself — because substitution ran on bytes and a fence was treated as a rendering instruction with no meaning for it. A seat writing about the stamp contract therefore could not quote this tool's own diagnostic through this tool: the quotation came back rewritten, the write succeeded, the read-back reported clean, and the only signal was a substitution count that nothing compared to intent. Measured three times inside one hour, by two seats: a false quotation published on #14251; the same hit in the sentence describing it and again in the comment reporting that (5707634696); and the dispatch claim for this card refused [quoted-not-a-stamp] for spelling the contract's own documentation placeholder (5717851201). The triage seat carries a hand-written workaround in its standing prompt — 「⛔ 描述这个语法时不要写出令牌字面形状,会被当真令牌解析」 — which is what this change retires.

Before-readings — this tree at f6189a43f9, offline, through --dry-run

All three taken before any edit. Every token below is written in the new quoting spelling, so this PR body is itself a demonstration; the inputs are shown as they were typed.

(a) The card's measured instance — the token in an inline code span inside a blockquote. Input: a blockquote quoting this tool's own status line, with the token inside backticks, plus one live token in a subscript line.

post-stamped: DRY RUN — nothing was written. 2 token(s) substituted with `2026-09-17T16:34Z`, 0 quoted stamp(s) rendered verbatim.
...
> `post-stamped` reported it faithfully: 「*stamp: none substituted — this body carried no `2026-09-17T16:34Z`, so there is no clock to check*」

⇒ substituted inside the quotation; substitutions: 2 where the author meant 1. Exit 0. A sentence this tool never printed, published as a quotation of it.

(b1) UNESTABLISHED POINT 1, now measured — the quoted-stamp form inside a quotation. Input: > The second spelling is + the quoted-stamp token in backticks, plus one live token.

post-stamped: DRY RUN — nothing was written. 1 token(s) substituted with `2026-09-17T16:34Z`, 1 quoted stamp(s) rendered verbatim.
...
> The second spelling is `2026-09-08T14:00Z` — a reading of something else.

worse than (a): the braces vanish, so the quotation no longer even shows which route it was about, and the status line's 1 quoted is byte-identical to what a legitimate declared reading prints. There is no count to compare against intent at all.

(b2) UNESTABLISHED POINT 2, now measured — a fenced code block. Input: the card's own re-check recipe inside a triple-backtick block, plus one live token.

post-stamped: DRY RUN — nothing was written. 2 token(s) substituted with `2026-09-17T16:34Z`, 0 quoted stamp(s) rendered verbatim.
...
printf 'quoting the token: 2026-09-17T16:34Z\n' | node scripts/pm/post-stamped.mjs --dry-run --comment 18543

⇒ a fence was not a quotation: substituted exactly like prose. So "no substitution inside fenced code" was a real behaviour change to measure, and fencing alone would not have covered (a), which was an inline span.

(c) The claim's refusal. Input: the quoted-stamp form with an ellipsis payload, inside backticks.

post-stamped: REFUSED — 1 stamp-contract problem(s) in the body. Nothing was written.
  1. [quoted-not-a-stamp] ... does not declare a stamp. The quoted route renders a reading of something else VERBATIM, so its contents must be one `YYYY-MM-DDThh:mmZ` and nothing else — it is a declaration, not a free-text escape from the contract.

Exit 2. The same refusal fires on the documentation placeholder YYYY-MM-DDThh:mmZ, which is the spelling this file's own refusal text uses — so the tool could not quote its own refusal back through itself.

The chosen spelling: Markdown code is a quotation

INSIDE A QUOTED SPAN THIS TOOL RENDERS TEXT, NOT TOKENS.

A quoted span is one of Markdown's two code constructs: a fenced code block or a backtick code span. Inside one, exactly three rules stand down — substitution, the opener scan, and the quoted-stamp validation. Nothing was added to the token contract: it still has exactly two spellings, and there is still no flag that turns it off.

The four axes

  • 实际业务需求. Measured, not speculative: three live hits inside one hour across two seats, a false quotation standing on a public card until someone re-read it, and a hand-written workaround living in the triage seat's standing prompt for dozens of rounds. All four measurements above are code constructs — an inline span, a fenced block, a backticked declaration — so the structural rule is the one the evidence actually points at. A fence-only rule would have missed (a), the card's own instance.
  • 项目长远合理性. Contract-first and no workaround: the quoting rule is Markdown's own, not a second vocabulary this tool invents and then has to document, and the token contract is untouched. The sustainable version of "explain the contract on a card" is that the contract is quotable; the temporary version is every seat carrying its own prompt-level workaround, which is the state being retired.
  • 防 AI 写代码犯错. This is the axis that decides it. A structural rule is visible in the stored artefact: a reader who has never heard of this tool sees backticks and knows the passage is quoted, in the spelling every other quotation on the board already uses. A magic word is invisible and has to be memorised — and would itself need quoting the first time anybody documented it. The contract also tightens rather than loosens: quoting suppresses what renders a token, never what judges a stamp a human typed, so no stamp can hide behind backticks, and the one direction that could have weakened a refusal (making the MIXED trigger quote-aware) is deliberately not taken.
  • 创业阶段不扩散需求. Zero new surface: no third token, no flag, no option, no config. One predicate (insideQuotedSpan) and one span scanner, consumed by the four rules that already existed. No transition window and no double spelling — the old behaviour is gone in the same commit, which is what 「短期不考虑渐进」 asks for.

Rejected, one line each

  • --expect-now=N (the card's candidate A). Not a quotation spelling at all — it refuses the body instead of letting the quotation be written, so the seat still cannot quote the diagnostic; and being opt-in, it protects only the author who already remembered.
  • An escape spelling (candidate C). A third spelling in a contract whose whole value is that it has exactly two, invisible to a reader who does not know the tool, and subject to the same regress the moment somebody has to quote it.
  • An explicit opt-out flag. The one thing this file says there is no flag for, and it leaves no trace in the artefact of why a token was not substituted — a reader cannot tell a quotation from a bug.

What it does NOT suppress — the load-bearing asymmetry

Quoting changes what is rendered, never what was authored. A stamp inside a fence is still digits on the board.

rule inside a quoted span why
POSITIONAL reads every line, code included a bare stamp in a code span on the opening line is the act's own time typed by hand, backticks or not
MIXED triggers on the act-clock token appearing anywhere, quoted or not an author who spells the token knows it exists; deliberately NOT made quote-aware, because that is the one direction this change could have weakened a refusal
MASKING maskQuotedStamps blanks a quoted-stamp token only where it IS a token otherwise a hand-typed stamp could hide from the bare-stamp scan behind backticks — the accident this rule may never buy

A bare stamp inside the quoting spelling is REFUSED, exactly as in prose (pinned, both positions). One body changes direction as a consequence: the quoted-stamp form carrying real digits beside a live token used to be accepted and rendered as bare digits — that acceptance was defect (b1) — and is now MIXED-refused. Because the old remedy text would then prescribe a route that cannot work inside a quotation, the refusal gained a clause naming the quotation and pointing at the placeholder form; a stamp with even one unquoted occurrence still gets the ordinary remedy, with no such clause.

The status line no longer leaves a count as the only signal

Before, the line carried the substitution count, the token spelling and a quoted count, and stopped there — the card quotes that form. After, the same line carries a third number, and on the same body as before-reading (a) its new tail reads:

· verbatim: 1 opener(s) inside a quoted span, left exactly as written

So (a) now reports one substituted and one left verbatim, where the filed run could only say 2. Those two numbers are what (a) had no way to produce. --json gains verbatim, and the DRY RUN line spells the same three counts through one renderer (substitutionSummary), so the three outputs cannot describe one render three ways.

After-readings — same four inputs, same tree, after the change

  • (a) exit 0 — one substituted, zero quoted, verbatim: 1; the quotation comes back byte-identical and the act's own token, outside it, still gets the clock.
  • (b1) exit 2 — MIXED, with the quoted-span clause. The argued outcome: real digits inside a quotation are digits on the board.
  • (b2) exit 0 — one substituted, zero quoted, verbatim: 1; the fenced recipe survives intact.
  • (c) exit 0 — the ellipsis payload and the documentation placeholder are both quoted verbatim, not refused. The dispatch claim for this card would now pass byte-for-byte, which is the retirement of the triage seat's standing workaround.

Pins

New battery, the quoting spelling: Markdown code is a quotation, and a quotation is rendered as written, 51 cases, registered in SELF_TEST_BATTERIES with the battery floor raised 11 → 12. It covers, in order: the card's blockquote-inline-span instance rendering verbatim and its before-reading control (the same body with the backticks removed is still substituted, 2); the fenced block, a tilde fence, an info string, a backtick-carrying info string (not a fence), an unclosed fence, a fence inside a blockquote, the blockquote ending it, and a four-space indented block being explicitly not the spelling; the claim's ellipsis refusal now rendering verbatim with its prose control still refused; the quoted-stamp form inside the spelling (rendered as the token, counted verbatim, not as a quoted stamp) with its prose control still rendering from the declaration; an unknown token name and an unclosed opener inside a quotation being text, both still refused in prose; the bare-stamp asymmetry in both positions, the mask rule, the quote-unaware MIXED trigger, and the remedy clause with its one-unquoted-occurrence control; the code-span mechanics (unmatched run, double run, one-line-only search, two spans on a line, a backtick run inside a fence); and the prose control asserting byte-identical output with verbatim: 0.

One pre-existing case was flipped rather than added — the opener-scan battery's ⛔ …and a token inside backticks is STILL substituted — a fence is not an escape, which is precisely the sentence this card retires. Every other existing case is untouched and green: the first run after the implementation, before any new case existed, reported 1 of 257 case(s) failed — that one — so the 256 others are byte-identical in behaviour.

Self-test line: 257 cases across 11 batteries → 308 cases across 12 batteries.

Ablation

Committed the fix first, then reverted the single predicate that recognises the quoting spelling — insideQuotedSpan made to answer false always, one line, with every other line of the change left in place. The mutation was proved on disk by occurrence count before the run and the restore proved by hash against the HEAD blob, under a trap … EXIT INT TERM holding an absolute path. The result is recorded below with both legs' numbers. (No build step is involved: this file is run directly by node, resolves through no package exports, and has no dist/, so ablation-dist-preflight.mjs has nothing to preflight.)

leg worktree blob on-disk proof self-test
before (= HEAD blob) baba105b5625fc16 predicate body present ×1, marker ×0 308 pass / 12 batteries, exit 0
ABLATED — insideQuotedSpan answers false 62cb814417c1fd1a predicate body ×0, marker ×1 24 of 308 failed, exit 1
restored — git checkout HEAD -- ABSOLUTE_PATH baba105b5625fc16 (identical to the HEAD blob) marker ×0, git diff HEAD 0 lines 308 pass / 12 batteries, exit 0

Exactly the new cases red, the old ones green. All 24 belong to this change: 23 of the new battery's 51, plus the one flipped opener-scan case. Zero pre-existing cases went red. The other 28 new-battery cases are CONTROLS — prose behaviour, the before-reading controls, the prose-still-refused halves — and stay green under the ablation by design, which is what a control is for.

One failure detail is worth reading on its own: the counts case fails with sub=2 verb=0, which is byte-for-byte the filed reading substitutions: 2 from before-reading (a). The ablation does not merely turn the pins red; it reproduces the card's measured defect exactly.

Gates

Derived from this worktree with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no hand-fed path list; change set = 1 path, scripts/pm/post-stamped.mjs, three-dot against merge base f6189a43f). Every derived command run, exit code captured with redirect-then-$?, then reconciled with --ran.

29 derived, 29 run, every one exit 0.

  • node scripts/check-ci-filter-parity.mjs :: exit 0
  • node scripts/check-closing-keyword-parity.mjs :: exit 0
  • node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
  • node scripts/check-comment-mask-corpus.mjs :: exit 0
  • node scripts/check-declaration-mirrors.mjs :: exit 0
  • node scripts/check-declaration-mirrors.mjs --self-test :: exit 0
  • node scripts/check-scripts-symbol-anchors.mjs :: exit 0
  • node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0
  • node scripts/check-self-test-wired.mjs :: exit 0
  • node scripts/check-self-test-wired.mjs --self-test :: exit 0
  • node scripts/check-self-test-workflow-commands.mjs :: exit 0
  • node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0
  • node scripts/check-whole-set-label-write.mjs :: exit 0
  • node scripts/check-whole-set-label-write.mjs --self-test :: exit 0
  • node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0
  • pnpm check:agent-test-spelling :: exit 0
  • pnpm check:bash32-floor :: exit 0
  • pnpm check:cli-command-ids :: exit 0
  • pnpm check:cross-package-test-inputs :: exit 0
  • pnpm check:driver-memory-census :: exit 0
  • pnpm check:entry-guard :: exit 0
  • pnpm check:nul-bytes :: exit 0
  • pnpm check:parse-guard :: exit 0
  • pnpm check:pm-dispatch-gates :: exit 0
  • pnpm check:pm-post-stamped :: exit 0
  • pnpm check:pnpm-filter-targets :: exit 0
  • pnpm check:ratchet-remedy-authority :: exit 0
  • pnpm check:refd-timer-probe :: exit 0
  • pnpm check:watch-hint-literal :: exit 0

Reconciled: dispatch-gates --ran29 derived famil(ies) accounted for — 29 run, 0 NOT-MEASURED (a DERIVED zero — all 29 recorded an exit code and none of them is 3), exit 0.

Repo-wide, as PR #18654 did: pnpm lint (eslint . --no-inline-config) exit 0, run at d7dffeb045, which is this branch's final commit — no commit has landed on the branch since.

⚠️ NOT MEASURED, declared rather than claimed: the 53 artifact-roster families, the 11 declared wide-population families, the 14 pending-changeset families, the 2 families taking a value from the workflow and the 1 path-scheduled CI job are each outside the derived 29 and are CI's. dispatch-gates prints every one of them under its own heading; none is read here as a clearance.

This body passes the tool it is fixing

node scripts/pm/post-stamped.mjs --dry-run --comment=18543 --file=THIS_BODYexit 0. Its first draft did not: it quoted the act-clock token literally and carried the concrete stamps from the before-readings, and the MIXED refusal fired twice — correctly, and with the new quoted-span clause naming the quotation each time. That shape is the measured cost of keeping the MIXED trigger quote-unaware, and it is the rule holding rather than a gap: every quotation above is verbatim, and what changed to clear the refusal was spelling the token in words outside the quoted evidence, not editing the evidence.

Not in scope

#18693 (the header's unmeasured issue-body footer cell) and #18709 (exit 4 on the seat's own footer re-anchor) are separate cards queued on this file behind this one; both were read and neither is addressed here. classifyReadBack, footerReAnchoring and sentBodyLanded are untouched, and nothing about what is SENT changed except the rendering of quoted tokens. No retry logic, nothing under .claude/**.


Generated by Claude Code

…ion, rendered as written

`post-stamped` substituted the act-clock token wherever it appeared, a passage
QUOTING the token included, so a seat could not quote the tool's own diagnostic
through the tool. Measured three times inside one hour on live artefacts: a
false quotation published on #14251 (an inline code span inside a blockquote),
the same hit in the sentence describing it and again in the comment reporting
that, plus a dispatch claim refused `[quoted-not-a-stamp]` for spelling the
contract's own documentation placeholder.

The remedy is structural, not a third spelling and not a flag: inside Markdown
code — a fenced block or a backtick span — this tool renders TEXT. Substitution,
the opener scan and the quoted-stamp validation all stand down there; a reader of
the stored artefact sees the rule without knowing the tool exists.

It suppresses nothing that judges a stamp a human typed. The positional scan
reads every line, code included; the MIXED trigger stays deliberately
quote-unaware; and `maskQuotedStamps` blanks a quoted-stamp token only where it
IS a token, so no stamp can hide from the contract behind backticks.

The status line now reports how many openers were left verbatim beside how many
were substituted, so the count is no longer the only signal an author has to
compare against intent.

Self-test: 257 cases across 11 batteries -> 308 across 12.

Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
Co-authored-by: Claude <noreply@anthropic.com>
@os-justin os-justin added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 17, 2026 — with Claude
@os-justin
os-justin marked this pull request as ready for review September 17, 2026 17:31
@os-justin
os-justin added this pull request to the merge queue Sep 17, 2026
Merged via the queue into main with commit 30bac28 Sep 17, 2026
44 checks passed
@os-justin
os-justin deleted the claude/issue-18543-post-stamped-quoted-token branch September 17, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/l skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants