Skip to content

Commit 447e2e8

Browse files
os-justinclaude
andauthored
fix(pm): teach footerReAnchoring the second re-anchor shape — a moved newline is not a lost byte (#18758)
Fixes #18709 `Clause-②: no` `post-stamped` exited 4 (`EXIT_NOT_STORED`) on every artefact a seat wrote carrying its own attribution footer — the block this fleet's harness rule requires verbatim on every GitHub post. The platform stores such a body with its trailing newline **moved** to before the footer's own rule: sent `X\n\n---\n_Generated by …_\n`, stored `X\n\n\n---\n_Generated by …_`. Equal length, one byte moved, zero lost, byte-identical before the offset. `footerReAnchoring` recognised only an **appended** footer — the stored body being the sent body, or its newline-trimmed form, followed by exactly `PLATFORM_COMMENT_FOOTER` — so a body that *already ended* in the footer matched neither arm, the first difference landed inside the sent body, and `sentBodyLanded` read the move as a lost byte. A write that landed whole, reported as a write that did not: a caller obeying the new contract ("exit 4 ⇒ read the artefact, do not retry") stops on every write, and one that ignores it has learned to ignore exit 4 — the state PR #18690 existed to end. It also induces duplicate writes. This is the reverse-direction residue of the fix for #18663: a predicate tightened on one side must be re-measured on the other. ## The before-readings, from the live artefacts Fetched through the REST proxy and fed to `classifyReadBack` / `sentBodyLanded` on `main` `30bac2880`. The sent side is the landing-record shape (the body as the seat sent it, ending in the footer block plus one newline); each row's byte counts and first-differing offset reproduce the reading recorded on the card independently. | artefact | mode | sent | stored | first difference | class | `footerReAnchored` | `sentBodyLanded` | exit | |---|---|--:|--:|--:|---|---|---|--:| | comment 5717446818 on #18426 | `--comment` | 1591 | 1591 | byte 1534 | `mutated` | `false` | `false` | **4** | | comment 5718507419 on #6023 | `--comment` | 3633 | 3633 | byte 3576 | `mutated` | `false` | `false` | **4** | | body of #18739 | `--body` | 6255 | 6255 | byte 6198 | `mutated` | `false` | `false` | **4** | | body of #18740 | `--body` | 5155 | 5155 | byte 5098 | `mutated` | `false` | `false` | **4** | The tail on every one of them, byte-exact: ``` sent tail: ' … act.\n\n---\n_Generated by [Claude Code](https://claude.ai/code)_\n' stored tail: ' … act.\n\n\n---\n_Generated by [Claude Code](https://claude.ai/code)_' ``` Two measurement notes, both of which change what a reader should conclude: - **The simpler shape is not the live one.** Sent body plus footer plus newline against stored body plus footer, with no blank-line insertion, classifies as `trailing-newline-stripped` and has always exited 0. Reproducing that shape offline proves nothing about this defect. - **The `--body` half needed no mode gate of its own, and the card's evidence comment is imprecise on the mechanism.** Comment 5718793488 reads `classifyReadBack`'s "the comment-only footer rule must be asked for by name" as "in body mode the footer rule is not consulted at all". Measured: that gate is on the **class** alone. `classifyReadBack` records `footerReAnchored` whatever the mode, and that is the field `$?` is decided from — `mode: 'body'` on the appended shape already answers `footerReAnchored: true`, `sentBodyLanded: true`. So the two halves really are one bug, the conclusion in that comment holds, and widening the one predicate reaches both modes in one edit with no mode gate added or removed. The misreading is now said out loud in the function's own docblock, because it has been made. A fifth artefact, comment 5718451961 on #17183, shows the same shape but reads back at 5026 bytes today against the 3480 recorded — it was rewritten after that reading, so it is **not** pinned. Recorded here as a measurement note, not a finding. ## The predicate: two arms, both exact bytes `footerReAnchoring` now answers which of two **measured** shapes the difference is, or `null`: - **`appended`** — unchanged. The sent body carried no footer of its own, and the stored body is it, or its newline-trimmed form, followed by exactly `PLATFORM_COMMENT_FOOTER`. - **`re-anchored`** — new. The sent body already ended in the footer block with trailing newline(s) after it, and the stored body is that same body with those newlines removed and exactly one newline inserted immediately before the block. Both arms are one `===` against a candidate **built from the sent bytes** — never a pattern, never a length. Everything before the block and every byte of the block itself is compared literally. The re-anchor arm additionally requires the sent body to have carried trailing newline(s) of its own: the moved newline is one the act sent, and a stored body that gained a newline from nowhere is a cell nobody has measured. The **class vocabulary is untouched**. The re-anchored shape stays `mutated` in both modes, because `footer-appended` says "the stored body is that body **plus** exactly the platform's comment footer" and a moved newline added nothing — naming it with that word would make the vocabulary say something untrue about the bytes. `--json` still reports `body_mutated: true`. What changed is what `$?` says, and one sentence of the status line. **The status line.** For a difference this tool has already measured as benign, the read-back's first line no longer carries the prescription "Read the artefact before trusting it: the sanitizer eats tag-shaped fragments" — it reads "the platform NORMALISED its own footer block and took nothing away. Class MUTATED, and nothing here to go read the artefact for", and the third line names which normalisation it was. That prescription is what sent the skills seat to re-read every exit-4 artefact by hand since the first shape landed; the header now records that interim reading as retired, and why nobody should make it by eye again (by eye it cannot tell a moved newline from a substitution the same length). **Not folded in: #18693.** Read it. Its remedy is to move the body-mode footer append and the trailing-rule re-anchor out of `mutated` into a benign **class**, and to re-pin the control at the old :1662. That is a different decision on a different surface — the class vocabulary, the `--json` field and the header's "unmeasured cell" declaration are all untouched here, and the pinned control that the body-mode append stays `mutated` still passes. It remains the next card on this file. ## The pins New battery, `the re-anchored footer: a newline the platform MOVED is not a byte lost`, 39 cases, floor pinned at 39 (probed: at 40 the floor fires and names the battery, so 39 is exact and not merely under). - **The live read-backs.** Each of the four artefacts above is pinned with its own byte count, its own bytes at and around the difference (the last 24 bytes before the block, taken from the fetched artefact rather than retyped — including the multi-byte tails of #18739/#18740 and 5718507419), and the offset that read-back recorded. Each asserts: both sides equal the recorded byte count; the first difference is at the recorded byte; exit 0; `footerReAnchored` true with shape `re-anchored`. The shared head is filler because every byte of it is identical on both sides by construction, and the pinned offset is what proves `firstDifferingByte` walked all of it. - **Both modes**, on the same bytes, plus a caller naming no mode at all. - **The appended arm kept**: it still names itself `appended`, the comment-mode `footer-appended` class still answers to that arm alone, the body-mode append still exits 0 with its warning kept, and its line still says "appended", never the re-anchor's words. - **The exit-4 controls**, none of which loosened: a byte **lost** before the rule; a byte **changed** before the rule at equal length (with an explicit assertion that the two sides *are* equal length, so "equal length" is demonstrably not the test); a loss **inside** the footer block; a footer whose link was rewritten; a sanitizer chew **under** a moved newline (the re-anchor masks nothing); a newline that came from nowhere; a newline inserted somewhere other than immediately before the block; a truncation that happens to end in the block. All exit 4. - **`unreadable` stays exit 0 and UNVERIFIED**, as the header declares — this rule did not widen into it. - **The status line**: names the re-anchor, drops the prescription, keeps the offset line whole. ## The ablation Reverting the one arm that recognises the moved-newline shape, at `9dd5db202a`: ``` HEAD blob hash: 219700f marker occurrences BEFORE mutation: 1 marker occurrences AFTER mutation: 0 mutated blob hash: 4d7121495543616d9c14ce4386871d36ae7e82a4 ON-DISK PROOF: marker 1 -> 0, hash 219700f… -> 4d71214955… ABLATED self-test exit: 1 ✗ post-stamped self-test: 14 of 347 case(s) failed, 0 floor problem(s). restored blob hash: 219700f git diff HEAD after restore: (empty — no path listed) RESTORED self-test exit: 0 ✓ post-stamped self-test: 347 cases pass across 13 batteries ``` **Exactly the new cases go red and the old ones stay green.** 14 of 347 fail, all 14 in the new battery, all of them about the re-anchored shape: for each of the four live artefacts, "it LANDED: exit 0" and "measured as the re-anchor by name"; plus "ONE predicate covers both modes", "a caller naming NO mode gets it too", the three status-line cases, and "the predicate answers the SHAPE". The other 333 pass — every one of the 308 pre-existing cases, and 25 of the 39 new ones. The 25 new survivors are the right ones: the byte-count and offset assertions are properties of the fixture, and **every exit-4 control stays green with the arm deleted**, which is what a control is for — a control that went red here would have been testing the arm, not the contract. 0 floor problems: the battery still registers 39, so nothing was hidden by a case that stopped running. Restore is proven two ways, by blob hash equality against `HEAD` and by an empty `git diff HEAD`, not by an exit code; the script carries `trap … EXIT INT TERM` with absolute paths and treats an empty hash as failure. ## Self-test 308 cases across 12 batteries → **347 across 13**. ## Derived gates `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` from the worktree, no hand-fed path list; change set 1 path, `scripts/pm/post-stamped.mjs`. All 29 run at `9dd5db202a`, each exit code captured with redirect-then-`$?`, **all 0**: ``` 0 node scripts/check-ci-filter-parity.mjs 0 node scripts/check-closing-keyword-parity.mjs 0 node scripts/check-closing-keyword-parity.mjs --self-test 0 node scripts/check-comment-mask-corpus.mjs 0 node scripts/check-declaration-mirrors.mjs 0 node scripts/check-declaration-mirrors.mjs --self-test 0 node scripts/check-scripts-symbol-anchors.mjs 0 node scripts/check-scripts-symbol-anchors.mjs --self-test 0 node scripts/check-self-test-wired.mjs 0 node scripts/check-self-test-wired.mjs --self-test 0 node scripts/check-self-test-workflow-commands.mjs 0 node scripts/check-self-test-workflow-commands.mjs --self-test 0 node scripts/check-whole-set-label-write.mjs 0 node scripts/check-whole-set-label-write.mjs --self-test 0 node scripts/pm/bare-root-worklist.mjs --self-test 0 pnpm check:agent-test-spelling 0 pnpm check:bash32-floor 0 pnpm check:cli-command-ids 0 pnpm check:cross-package-test-inputs 0 pnpm check:driver-memory-census 0 pnpm check:entry-guard 0 pnpm check:nul-bytes 0 pnpm check:parse-guard 0 pnpm check:pm-dispatch-gates 0 pnpm check:pm-post-stamped 0 pnpm check:pnpm-filter-targets 0 pnpm check:ratchet-remedy-authority 0 pnpm check:refd-timer-probe 0 pnpm check:watch-hint-literal ``` Reconciled with `--ran`: **29 derived, 29 run, 0 NOT-MEASURED, 0 UNRUN** — a derived zero, since all 29 records carry an exit code. Repo-wide `pnpm lint` (`eslint . --no-inline-config`): **exit 0**, no findings. `origin/main` was merged twice while this ran and the derived family list came back byte-identical both times (29, no additions, no drops), including across PR #18735's change to `dispatch-gates.mjs` itself; the last derivation and every reading above are from `9dd5db202a` with a clean tree. `origin/main` keeps moving faster than a local sweep — CI on this PR is the authority on convergence. `skip-changeset`: `scripts/pm/**` is not shipped by any package's `files[]`; nothing published moves. --- _Generated by [Claude Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 74327d3 commit 447e2e8

1 file changed

Lines changed: 213 additions & 33 deletions

File tree

0 commit comments

Comments
 (0)