Repository navigation
fix(pm): post-stamped exits non-zero when the platform did not store the body - #18690
Merged
os-justin merged 2 commits intoSep 17, 2026
Merged
Conversation
…the body The read-back already detected the mutation and printed it; the exit code stayed 0, so a caller obeying every discipline the header prescribes — no pipe, `$?` checked — walked on as if the body had landed. The verdict now carries an exit code. One question decides it: did every byte this act sent reach the platform? The three declared normalisations all keep the sent body whole and still exit 0. `mutated` splits in two on an exact-bytes measurement (`footerReAnchored`, one spelling shared with the classifier's comment-only footer class): a footer the platform re-anchored took nothing away and keeps its 0, everything else is EXIT_NOT_STORED (4). ⛔ classifyReadBack is untouched — the class, the warning line, the offset and `body_mutated` are all as pinned. What changed is what `$?` says. Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu Co-authored-by: Claude <noreply@anthropic.com>
…st-stamped-body-unstored-exit
os-justin
marked this pull request as ready for review
September 17, 2026 15:16
This was referenced Sep 17, 2026
os-justin
deleted the
claude/issue-18663-post-stamped-body-unstored-exit
branch
September 17, 2026 15:58
This was referenced Sep 17, 2026
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 28, 2026
…ne, and the arming echo is not the landing method (objectstack-ai#18713) Fixes objectstack-ai#18461 `Clause-②: no` One file: `.claude/skills/pm-dispatch/references/platform-readings.md`, held at **466 / 466** (headroom 0, widest line 120 B). No ceiling raised. Net line change **0**: one row rewritten in place, one row added, one payment taken in the same file. `skip-changeset` — `.claude/**` is shipped by no package's `files[]`, so nothing published moves. Reserved rows are untouched and verified by content: `:10`–`:12` and `:431` (objectstack-ai#18469 PR-A's pending patch), `:209` (PR objectstack-ai#18666's one-line change, awaiting the maintainer — it rides at `:208` after the payment, byte identical), and the nine rows PR objectstack-ai#18689 adopted an hour before this branch (`:28`, `:85`, `:249`, `:277`–`:278`, `:291`, `:347`, `:357`, `:393`, `:410` — none appears in any hunk of this diff). ## Row ② — rewritten in place at `:50` Ruling reference: the maintainer's letter **A** on objectstack-ai#18421, comment 5716260764 (2026-09-17T14:41Z, 「同意」). Its operative conclusion is the one this row carries: **`auto_merge.merge_method` is not a reading of what will land** — the landing shape is the merge queue's to decide. **before** (120 B, the falsified form): ```text - 挂上的 auto-merge 存的方法恒为 `merge`,不论请求了什么;REST `auto_merge.merge_method` 读回 `merge`。 ``` **after** (117 B): ```text - auto-merge 回读 `merge_method` 不恒定:同 `SQUASH` 载荷 `merge`/`squash` 皆现,⛔ 非落地方法判据。 ``` **Why 「不恒定」 and not either constant.** 「恒为 `merge`」 is falsified by this card's four PRs; 「恒为 `squash`」 is falsified by the same table's first row and by the ruling's own measurement. Same endpoint (`PUT .../pulls/{n}/ccr/auto_merge`), same `{"merge_method":"SQUASH"}` payload: | PR | echoed `merge_method` | landed as | |---|---|---| | objectstack-ai#18395 | `merge` | single-parent squash | | objectstack-ai#18392 | `squash` | single-parent squash | | objectstack-ai#18416 | `squash` | single-parent squash | | objectstack-ai#18445 | `squash` | single-parent squash | The objectstack-ai#18421 ruling measured the same endpoint answering `merge` (both arming channels, MCP and CCR), and the dispatching seat's own arms today echoed `squash` on PR objectstack-ai#18689 / objectstack-ai#18690 / objectstack-ai#18700 and `merge` on earlier ones. Four landings, two echoes, one landing shape ⇒ the echo varies and decides nothing. A row asserting either constant would teach a seat to investigate a read-back that is simply not a signal. **Neighbours left as they are.** `:49` is about the bare `PATCH` draft bit — unrelated. `:51` (「仓库 `allow_merge_commit:false` 时同样读回 `merge`」) does not restate 「恒」: it is one conditioned measurement, and 「同样」 now attaches to `merge`, which the rewritten row names as one of the two observed values. `:53`–`:55` already carry the landing half (the branch rule decides; three carriers agreeing proves nothing) and are untouched — the row above them no longer contradicts them. ## Row ① — added at `:272` **after** (118 B): ```text - 多标签页还静默截断:`totalCount` 231 而 `returned` 30,单标签同车道 24/24 ⇒ 求交读成空车道。 ``` **Placement — a declared deviation from the dispatch.** The dispatch named the `:247`–`:248` neighbourhood (the MCP `list_issues` rows). The OR half of this reading is already in the file, in the 读数陷阱 cluster at `:269`–`:274`: `:269` 「`labels` 数组是并集」, `:270` 「⛔ 永不读作交集」, `:272` 「失效全静默」, `:273` 「正确读法 = 整车道单标签一次读全加本地对 labels 求交」. Writing the new row at `:247` would have put a second home for the labels-OR fact six rows away from the first — the same duplication the dispatch forbids for row ② (one row for one fact, never two). So the row is placed where the fact it compounds already lives: directly between the silent-failure row `:272` and the prescription `:273`, which it is the reason for. The fact delivered is unchanged; only the address is. **What the row adds over `:269`–`:274`.** Those rows carry the OR semantics and the prescription. They do not carry the **truncation**, and the two compound: the returned page is a fraction of the set with nothing in the response saying which fraction, so a local intersection taken over *that page* can return zero and read as 「本车道无活」. The measurement is inside the row: `labels=["domain:cli","pm:queue"]` answered `totalCount=231` with `returned=30`, while the single-label query on the same lane in the same minute answered 24 / 24 — complete, and intersectable. That is what makes `:273` load-bearing rather than a style preference. ## The payment — one dedup, `:202`+`:203` become one row | payment | before | after | fact lost | |---|---|---|---| | `:202`+`:203` | 「换道:探针绿走 REST 列表端点 `GET /repos/{o}/{r}/issues?state=open&labels=a,b&per_page=N`。」 + 「它走 core 桶且 `labels` 是真 AND;⛔ 完整性自证靠 `&page=N` 加总数核对。」 | 「换道:探针绿走 REST 列表端点列卡,走 core 桶且 `labels` 真 AND;拼写与自证见 `rest-channel.md`。」 (118 B) | the inline endpoint spelling and the `&page=N`-plus-total spelling — both held verbatim in `rest-channel.md` 〈读侧〉, which `:137` already names as their single home (「逐操作通道归属、写侧配方与队列路由三读法见 `rest-channel.md`,⛔ 不在本表复述」). The two facts the row keeps inline are the ones a seat needs before it switches channel; `labels` being a true AND on REST is also stated in-file at `:274`, and the core bucket at `:115` / `:128`. | Net: 3 lines changed, 466 in, 466 out. ## Gate readings `node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/references/platform-readings.md` exits **3 = GOVERNED** (`.claude/**` ×1) — recorded as the verdict it is. This PR stays draft; no seat flips it ready, enqueues it or arms auto-merge. The references tier applies: the skills seat's `## Contract review` record goes on this thread, and the seat lands it. - `pnpm check:pm-skill-ratchet` :: exit 0 — 「platform-readings.md is 466 lines (ceiling 466; headroom 0)」, 「widest table row is 0 bytes (pin 0; headroom 0)」; no line over 120 B (`awk 'length > 120'` returns nothing). - `pnpm check:skill-frame-sync` :: exit 0 — 「the one declared copy of the decision frame is internally coherent … 4 axes … 74 markdown files scanned for undeclared copies」. - `pnpm lint` (repo-wide, `eslint . --no-inline-config`) :: exit 0. - `grep -naP` for control characters over the file: no hits. No tag-shaped fragment in either new row. ### Derived gate list, with exit codes Derived from the worktree with `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` (no hand-fed paths; change set 1 path vs merge base `72dd95fa5`), every command run, each exit code captured by redirect-then-`$?`: ```text 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/pm/check-governed-queue-guard.mjs --self-test :: exit 0 node scripts/pm/check-harness-current.mjs --self-test :: exit 0 pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0 pnpm check:agent-test-spelling :: exit 0 pnpm check:doc-authoring :: exit 0 pnpm check:driver-memory-census :: exit 0 pnpm check:nul-bytes :: exit 0 pnpm check:pm-governed-merges :: exit 0 pnpm check:pm-half-states :: exit 0 pnpm check:pm-skill-id-lint :: exit 0 pnpm check:pm-skill-ratchet :: exit 0 pnpm check:refd-timer-probe :: exit 0 pnpm check:required-contexts :: exit 0 pnpm check:skill-frame-sync :: exit 0 pnpm check:watch-hint-literal :: exit 0 pnpm check:pm-settings-deny-roster :: exit 0 (outside the derivation; run because its roster lives under .claude, which this path is in) ``` `check:doc-formula-expressions` first exited **3 — PREREQUISITE NOT MET** (`@objectstack/formula` and `@objectstack/lint` not built, 「Nothing was measured」). It was re-run to a real verdict after `pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint` through `scripts/pm/os-verify-lock.sh` (VERDICT command-exit 0, held 1s, waited 0s); only the exit 0 is recorded above. Reconciliation: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.txt` → 「18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED (a DERIVED zero — all 18 recorded an exit code and none of them is 3)」. Outside that union: the 53 artifact-roster families, 11 wide-population families, 14 changeset-pending families and the CI-only families (`--verify-required-set`, `check-half-states --provenance`) — CI's, not this branch's, and named here so their absence is not read as a clearance. ## Out of scope, reported not written The card's third item — `scripts/pm/check-expected-skips.mjs` cannot run in a seat container (`ERR_MODULE_NOT_FOUND: Cannot find package 'yaml'`) — is not a references line and is not written here. In this worktree, after `pnpm install`, `yaml@2.9.0` is present; the crash is a property of the container the seat runs it from, not of the script. _Generated by [Claude Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_ --- _Generated by [Claude Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_ Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal
pushed a commit
to akarma-synetal/framework
that referenced
this pull request
Sep 28, 2026
… newline is not a lost byte (objectstack-ai#18758) Fixes objectstack-ai#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 objectstack-ai#18690 existed to end. It also induces duplicate writes. This is the reverse-direction residue of the fix for objectstack-ai#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 objectstack-ai#18426 | `--comment` | 1591 | 1591 | byte 1534 | `mutated` | `false` | `false` | **4** | | comment 5718507419 on objectstack-ai#6023 | `--comment` | 3633 | 3633 | byte 3576 | `mutated` | `false` | `false` | **4** | | body of objectstack-ai#18739 | `--body` | 6255 | 6255 | byte 6198 | `mutated` | `false` | `false` | **4** | | body of objectstack-ai#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 objectstack-ai#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: objectstack-ai#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 objectstack-ai#18739/objectstack-ai#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 objectstack-ai#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>
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.
Fixes #18663
Clause-②: no
The defect
scripts/pm/post-stamped.mjsalready DETECTED the write the platform never stored — it read theartefact back, classified the difference as
mutated, printed the byte counts and the firstdiffering offset — and then returned
0. On the filed hit (a seat-post body refresh, 2026-09-17)it sent 263,533 bytes, the platform stored 257,945 — the byte count of the version BEFORE that
write — with the first difference at byte 6792, where the new block began: the old body had been
kept whole and not one byte of the new one was there. A caller obeying every discipline this file's
own header prescribes — no pipe,
$?captured before any pipe — was still told the body hadlanded, and went on to post the comments that say the conclusion had already been written into it.
That is the opposite direction from the pipeline trap the header already warns about: that one is
about a caller throwing the code away, this was the tool handing out a zero.
The discriminating predicate, and the verdict fields it reads
One question decides the exit code: did every byte this act sent reach the platform?
All three benign classes answer yes by construction and are untouched —
identicalby definition,trailing-newline-strippedgives up only newlines the platform does not keep,footer-appendedadds without removing.
mutatedis the only class the rule reads, and it splits in two:Two fields of
readBackVerdict's own verdict object, and nothing else:readBack.classclassifyReadBackis not touchedreadBack.footerReAnchoredPLATFORM_COMMENT_FOOTER.footerReAnchoring()is the one spelling of that comparison, shared with the classifier's comment-onlyfooter-appendedclass rather than written a second time⛔ Not a byte-count heuristic. The platform normalises blank lines around a trailing rule in both
directions, so a length comparison answers a different question: "stored is shorter" is neither
necessary (a re-anchored footer is LONGER) nor sufficient (an equal-length substitution loses just
as much). ⛔ And it does not need the pre-write body, which only
--bodyever holds: "the platformkept the old one" is one INSTANCE of the class, not its definition — whatever is stored, a byte
that differs INSIDE the body this act sent is a byte this act did not get onto the platform. The
same predicate therefore covers the filed hit, a truncation, a sanitizer substitution, and a
--commentwrite.--commentdoes share the verdict:mainbuilds onereadBackVerdictfor both acts and passesmode, so the same predicate judges both. A comment the sanitizer chewed now exits non-zero; thecomment footer append it is built to forgive is already clean one class earlier.
⛔ What did NOT change:
classifyReadBack's classes and exemptions, the warning line, the offsetline,
body_mutatedin--json, what is SENT, and the stamp contract. There is no retry logic.The one shape that must stay at 0, and does. A
--bodyrefresh where the platform appends its58-byte footer classifies as
mutated— the issue-body footer cell is unmeasured, and this PR doesnot forgive it in the classifier. It exits 0 because nothing was lost. Measured on the fixture,
byte-for-byte the shape the seat hits on every seat-post refresh:
The exit register, before and after
PREREQUISITE NOT MET— no route, no token. No act at allEXIT_NOT_STORED— written, and the platform did NOT store itWhat a caller does with a 4 is in the header and in the tool's own stderr report: re-read the
artefact; ⛔ do not retry blindly — the measured hit was a size refusal the platform never
reported, so an identical second write reproduces it exactly and a retry loop writes that failure
into the card over and over.
⛔
unreadableis deliberately NOT widened into the new code. "The platform returned no readablebody" is a failure to VERIFY, not a measured failure to store; it keeps its
UNVERIFIEDline andits 0 until somebody measures what that cell means — the same reason the body-footer cell is not
forgiven in the classifier. The boundary is recorded in the header, not quietly crossed.
Pins
New battery in the file's
--self-test, registered the way the ten existing ones are (battery()opens it,
t()attributes to the one most recently opened, the floor is evaluated last):$?, or it reaches nobody」SELF_TEST_BATTERIESSELF_TEST_BATTERY_FLOOR: 10 → 11What they pin: the filed shape (stored = the pre-write body, first difference inside the sent body)
⇒ 4 · a body-mode footer re-anchoring ⇒ 0, and still
mutatedwith its offset · a re-anchoring overa stripped trailing newline ⇒ 0 · a trailing-newline strip ⇒ 0 · an identical read-back ⇒ 0 · a real
mutation underneath an appended footer ⇒ 4 (the footer masks no loss) · a chewed
--commentread-back ⇒ 4, the
--commentfooter append ⇒ 0 · a truncation ⇒ 4 ·unreadable⇒ 0, not widened ·the five register values distinct · the footer measured as exactly the declared bytes and not as a
58-byte delta (58 bytes of
xanswersnull).Ablation
From the committed fix, on disk, under a
trap, with absolute paths: the predicate's last linereturn readBack.footerReAnchored === true;replaced byreturn true;— which is exactly what thetool did before this PR.
Direction predicted before the run and observed: turns red, and only in the new battery — the ten
existing batteries stay green, so the ablation moves exactly what it claims to move. The six that
fall are the six that read
exit/landedon a lost body; the footer-re-anchoring controls staygreen under the ablation by construction (a predicate that always answers "landed" still exits 0
there), which is why the filed-shape pins and not the controls are the ones that prove the fix.
Restore is proven by blob hash and by an empty
git diff HEAD, not by an exit code;git checkout HEAD -- PATHnames HEAD explicitly so a polluted index cannot serve the mutation back.Self-test
233 → 257 cases, 10 → 11 batteries.
Derived gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackfrom the worktreeat
2f7df2abc(no hand-fed path list; change set =scripts/pm/post-stamped.mjs, 1 path).29 derived, 29 run, 0 NOT-MEASURED, 0 UNRUN; every exit code captured with redirect-then-
$?,never across a pipe. Reconciled with
--ran:All 29 exited 0:
Plus the repo-wide scan the dispatch asked for, run whole and not narrowed:
And, beyond
pnpm check:nul-bytes(exit 0 above), the author-side control-byte scan on the onechanged file:
grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' scripts/pm/post-stamped.mjs→ nohits (exit 1).
⛔ Not a complete account of what CI runs here: the derivation also names 53 artifact-roster
families, 11 declared-wide-population families, 14 pending-changeset families and 1 path-scheduled
CI job, each outside the 29 and each printed under its own heading by a run without
--commands.skip-changeset:scripts/pm/**is repo tooling and is in no package'sfiles[]— nothingpublished moves.
Acceptance notes
synthesises a footer for a footer-less ISSUE BODY is unmeasured … a footer on a body read-back
stays MUTATED until somebody measures it」 — and somebody now has: the skills seat reports hitting
exactly that shape (sent N, stored N+58, the difference being the declared footer) on every
seat-post refresh. The file's own stated condition for moving the cell is met and the cell has not
moved, so
--bodykeeps printing a MUTATED warning on nearly every write, which is the noise[finding] post-stamped:
readBackVerdictcompares stored to sent byte-for-byte, so the platform's trailing-newline strip (and the footer append on comments) reads as 「the platform MUTATED the body」 on nearly every write — the one warning meant for the sanitizer is now noise #18296 removed for the other classes. This PR deliberately does not move it (the card pins theexemptions as they are) and closes only the exit-code half. Dedupe words:
post-stamped,classifyReadBack,footer-appended, issue body, unmeasured cell,platform-readings.unreadableread-back still exits 0, so a caller checking$?walkson with a write the tool could not verify. No repro is in hand — the platform would have to
return a body-less object — so it is an observation, not a defect card; the boundary is now
written into the header rather than left implicit. Successor: whoever lands the card above, who
edits this same function.
Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
Generated by Claude Code