Repository navigation
fix(pm): post-stamped refuses a declared reading at the two positions H56 reads - #19632
Conversation
… H56 reads The positional walk ran over `maskQuotedStamps`'d text, where a declared reading is blanked, so a quoted-reading token on the OPENING line or on a subscript reading-time line passed the write side — and the header's refusal text offered it there by name. `substituteTokens` renders a declaration as its payload and nothing else, so the board carries bare digits at a position the read-side patrol reads as the writing act's own clock, and files a row on it. The positional judgement now runs over the body `substituteTokens` will actually send, using the patrol's own imported reader: a stamp standing at either position that is not this act's clock is refused whichever spelling put it there. A stamp equal to this act's clock, and a position the render leaves carrying more than one stamp, are both left alone — the first is the bytes the act-clock token would have written, the second is the patrol's own holdout. Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc Co-authored-by: Claude <noreply@anthropic.com>
Seventeen cases asserting that what post-stamped does with a body and what H56 would file on the bytes that body renders to are the same answer: a declared reading on the opening line and on a subscript reading-time line are refused and would be filed; the act-clock token at either position, a declaration one line down, a position the render leaves carrying two stamps, and a declaration whose value is this act's own clock are accepted and leave the patrol silent. The reader and the row are the patrol's own, imported. The one-line fixtures that put a declaration in the opening position by accident are moved off it; the one that pinned the acceptance itself is replaced by the pair that reads in both directions. Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc Co-authored-by: Claude <noreply@anthropic.com>
The table of what the rule buys becomes prose, and the comment at the walk stops restating the two shapes the header already names. Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: ① Derived judgments(a) The walk's input — RIGHT. (b) The two exemptions — parity-preserving, one declared asymmetry. The multi-stamp hold-out is not code at all: it falls out of the imported reader, so it cannot drift from H56. It is not a typing hole — a hand-typed bare stamp beside a declaration is still caught, because the bare walk runs on (c) The (d) The flipped self-test (:3054) — the old assertion was the defect. It pinned as intended behaviour the exact body the card measured filed twice on live artefacts written by following this file's own prescription. No legitimate use lost: the remedy is the one the card's author already applied by hand, and no artefact was found whose opening line must carry a reading. Bonus fix: the old row passed no (e) The header rewrite — claim now true; one prescription still offers the declared token at a position. The parity sentence at :42-44 is now one-directional (patrol-could-file implies refused-before-the-write) and holds at the body level, including under the (c) gate. The POSITIONAL row (:46-50) and the refusal text (:1396-1398) no longer offer the declared spelling AT the position. But (f) (g) Nothing narrowed beyond the two positions. The walk iterates only the imported reader's hits. The five moved fixtures are one-liners whose declaration sat on the opening line only because they were one-liners, and each keeps its own subject. Two of them were genuine accepted-form narrowings (a reading of something else on the opening line, minute and seconds grain) and the patrol agrees with both, so the moves are right rather than a batch respelling. The one-liner left in place is the exemption pin. The only narrowing the patrol does not agree with is (f). Battery count checks out: 17 rows in the block at :3933, pinned at 17 at :2983. ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #18995
scripts/pm/post-stamped.mjsclaimed in its own header that its positional refusal "refuses exactly what the read-side patrol would file". The card falsified that twice, on live artefacts written by following the header's own prescription. This makes the claim true.The defect, reproduced on the base
The positional walk ran the patrol's
h56StampedReadingsovermaskQuotedStamps'd text, where a declared reading is blanked. So a declared-reading token (theWASspelling) on the artefact's OPENING line, or on a subscript reading-time line, passed the write side — and the POSITIONAL refusal text offered that spelling there by name.substituteTokensthen renders a declaration as its payload and nothing else, so the declaration is spent at render time and the board carries bare digits at a position H56 reads as the writing act's own clock.Measured on
744a0a3before the change, with the card's own two numbers:2026-09-18T09:04Z2026-09-19T07:58ZBoth PM mechanism assumptions hold as stated. H1:
substituteTokensemits the payload alone, so the declaration is erased at post time and no downstream reader can recover it. H2: the positional walk over masked text finds 0 readings on that opening line while the rendered body carries 1, on both positions.The change
The positional judgement now runs over the body
substituteTokenswill actually send, through the same imported reader. A stamp standing at either position that is not this act's own clock is refused whichever spelling put it there — a new refusal kindpositional-declared, whose remedy is the act-clock token at the position with the quoted reading moved into the body. The bare-stamp POSITIONAL remedy stops offering the declared route AT the position and prescribes it in the body instead, so no refusal text prescribes a refused remedy.Two shapes are deliberately left alone, both because the patrol leaves them alone:
The bare scan is untouched and stays value-free: a typed stamp equal to this act's own minute is still typed, and is still refused. A declaration the value rules already refused is not ALSO filed here — one typo, one refusal, the rule this file already states for the calendar and direction checks.
Parity pin
A new declared self-test battery, "the two positions: a declaration renders as bare digits, and the patrol reads digits", registers 17 cases and is pinned at 17 in
SELF_TEST_BATTERIES(floor rules unchanged; the roster floor stays at 15). Every row asserts one body against BOTH halves: whatstampRefusalsdoes with it, and whath56EstimatedStamp— imported, not restated — would file on the bytes that body renders to. Declared reading on the opening line and on a subscript line: refused, and would be filed. Act-clock token at either position, the same declaration one line down, a rendered position carrying two stamps, and a declaration whose value is this act's clock: accepted, and the patrol is silent.Fixture triage, per disposition rather than a batch respelling: the one case that pinned the acceptance itself ("a DECLARED quoted stamp is accepted even in the opening position") is replaced by the pair that reads in both directions; five one-line fixtures whose subject is the calendar or direction rule, and which put a declaration in the opening position only because they were one-liners, are moved off that line.
Reverse verification — one-time, nothing left in the tree
Both legs ran from the committed state through
scripts/ablation-replace.mjs, so the mutation is proven on disk (anchor count and blob hash) rather than by an editor's exit code.Leg A — revert the walk's input to the masked text, which is exactly the pre-fix behaviour:
The seven are the parity rows by name, including "THE FILED REPRO", "PARITY", and "the SECOND position the card measured". Direction observed: turned red, the ordinary direction.
Leg B — remove the act-clock exemption (
hit.stamp === now). The suite goes red by CRASH, at an existing unrelated case whose body stops rendering, which is itself the evidence the exemption is load-bearing; it is reported as a crash, not as a row-level reading.Both legs restored by the tool and re-checked by hand: worktree blob equals the HEAD blob and
git diff HEADis empty.Gates, at
d12550dDerived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands(no paths; the script takes its own change set from the merge base) and reconciled with--ran:Every one exited 0,
pnpm check:pm-post-stamped(the file's own--self-test, 562 cases across 19 batteries) andpnpm check:nul-bytesamong them. Three families outside the derivation were run because this diff reaches them through imports:close-cards.mjs --self-test(102 cases, it importsrenderBody),write-pace.mjs --self-test(69 cases) andcheck-half-states.mjs --self-test(5160 cases, the other half of this contract) — all exit 0.The 55 artifact-roster families, the 11 wide-population families, the 14 pending-changeset families and the one path-scheduled CI job the derivation names are outside that total and are CI's.
Lint, narrowed and declared
pnpm lintiseslint . --no-inline-configover the whole tree and belongs to CI. What ran here is the same binary and the same flag over the one changed file, with the three readings that make a narrowing a measurement rather than a skip:eslint .reaches undereslint.config.mjs— read from that config, not guessed;--format jsonreports 1 file linted, 0 errors, 0 warnings, andgit diff --name-onlyagainst the merge base names exactly that 1 file;eslint.config.mjs, which never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file, test or not." So nothing in this diff can move the verdict on a file it did not touch, and the narrowing excludes nothing.turbo ls --affectedis not cited: the repo-root package is not in the turbo graph at all, so this path is not on it.Acceptance notes
quoted-not-a-stampraised by quoting the token verbatim, plus UNKNOWN-opener refusals when the token was split across backticks. Measured here with the spellings the card lists: the placeholder form inside a code span beside an act-clock token is ACCEPTED, and so is the split-into-backticks workaround (0 openers flagged). The residue — a declared token carrying REAL DIGITS, quoted beside the act-clock token, still taking the MIXED refusal — is a deliberate decision recorded in the header of that file ("deliberately NOT made quote-aware"), and its fenced-block half belongs topost-stampedrefuses a{{WAS:}}-declared stamp inside a fenced code block, and its refusal prescribes the declaration that is already there #19319, which remains open, is p3 andpm:queue, and is out of scope here. Nothing in this PR touches it.skip-changeset:scripts/pm/**is on the fast-track list and publishes nothing from any released package; no packagefiles[]ships this path.check-half-states.mjsis imported only;h56EstimatedStampis the one symbol this change adds to the import list, for the parity pin.How a caller sees it
A body whose opening line declares a reading of something else is refused before any request, with the position named, the declaration quoted, the digits it would have rendered to shown, and the remedy spelled: the act-clock token there, the reading in the body.
Generated by Claude Code