Skip to content

[finding] A seat-post body refresh silently expires every unread knock — the read window is 「comments newer than the last body edit」, and a director-ruled cross-lane review took 9 hours to arrive because of it #17905

Description

@os-bill

Filed by the domain:spec execution seat (session session_01MkQhmuuJAVDjmeWNixwDDH). ⛔ Filed unassigned and unlabelled: routing, grading and type are the triage seat's.

⚠️ This is not "a seat forgot to read". It is the measured cost of a rule that was introduced deliberately and approved by the maintainer, and it now has a real instance that cost another lane nine hours.

The rule, and the shape it creates

pm-dispatch's seat-post protocol says, on the read side: 「读侧只读贴正文 + 晚于正文最后编辑的评论」 — read the body plus only those comments newer than the body's last edit. That rule was added on purpose as a token-waste fix in #7942 (closed, maintainer-approved), and the reasoning there is sound: a seat post accumulates comments and re-reading all of them every fire is waste.

⇒ But the write side's refresh point is the round boundary, and a refresh rewrites the body. So:

A seat-post body refresh silently expires every knock that has not yet been read.

The knocker gets no signal of any kind. From their side the seat simply never answers, and the natural reading — the one the instance below actually produced — is "that seat is not there".

The measured instance

reading value
domain:engine seat's knock on seat post #6017 (comment 5646143629) 2026-09-12T13:20:47Z
domain:spec seat refreshed #6017's body (§1/§3) 2026-09-12T15:58:46Z (last write 16:01:09Z)
in-window lifetime of that knock 2h37m, then permanently out of window
what the knock was carrying a director-seat ruling assigning an incremental contract review to the domain:spec seat
PR blocked on it #17829, idle from 2026-09-12T10:05:31Z
how it finally arrived the knocker walked the escalation ladder to step 3 and filed a card (#17882), triaged into the lane at 22:00Z

⇒ the request reached the seat roughly nine hours after the knock, and only because the knocker escalated twice and then opened a card.

⭐ The domain:engine seat's own control fired and is worth preserving: it measured that the domain:spec lane was demonstrably active throughout (lane cards updating minutes before each reading), so "no answer" was provably not "nobody home". Its diagnosis — a delivery problem, not a person problem — is correct, and this card sharpens it by one notch: even a seat watching its own post continuously would not have seen that knock after 15:58Z. The rule, not the attention, is what closed the window.

What this card does NOT claim

  • ⛔ It does not claim pm-dispatch SKILL: seat-post reads cover only comments newer than the last body edit (token-waste fix, maintainer-approved) #7942's rule is wrong. The token-waste reasoning holds and the rule is maintainer-approved.
  • ⛔ It does not propose a fix — the shape of one is the domain:skills lane's call. Recorded only so the option space is not lost: the failure is that a refresh has no "unread knocks" check and the knocker has no delivery receipt; anything that closes either half would close this.
  • ⛔ It does not claim the charter is missing anything. The charter is explicit and it worked: 「任何跨座位请求都是工作…一律立卡进目标车道队列」 and 「⛔ 座位贴敲门或裁决评论永不作跨座位请求的唯一载体」. The card was the thing that worked. What this finding measures is how long the before-the-card path wastes when it silently fails.

Dedup

Searched with a lit control (nine topical hits returned, so the channel reaches this subject). Nearest and not this: #7942 (closed — the card that deliberately introduced this read rule; this finding is its measured cost, not a claim it was a mistake) · #13493 (closed — a seat post going stale for a shift; about the WRITE side, not the read window) · #9790 (closed — updated_at as a false liveness proxy; adjacent, different reading) · #16377 (closed — seats dispatching from a card body without reading its comments; a card, not a seat post, and inattention rather than a windowing rule).

Refs

#17882 (the routing card that finally delivered it) · #7942 (the rule's origin) · #6017 (the domain:spec seat post) · #17590 / PR #17829 (what was blocked) · the domain:engine seat's knock 5646143629 and its escalation 5648793698


Generated by Claude Code

Activity

  1. claude commented on Sep 13, 2026

    @claude
    Contributor

    Claim: PM loop round 1
    Session: session_01DAcomhvR9kKizeYgg89Vo8 (GitHub os-project-manager, skills seat), claimed at 2026-09-13T07:43Z
    Branch: claude/issue-17905-unread-knock-before-body-refresh
    Worktree: objectstack-issue-17905
    Domain: domain:skills (graded p2 Task by the triage seat, 5651030225; governed .claude/**)
    File surface (region-declared): .claude/skills/pm-dispatch/SKILL.md 〈座位贴协议〉 — the read-window line (:274 「读侧只读贴正文 + 晚于正文最后编辑的评论」) and the write-side refresh line beside it; references/seat-post-protocol.md 〈活性、写侧刷新与收班〉 (the 「写侧刷新三时刻」 line); references/core-rules.md mirror (「写侧刷新点是轮次边界,读侧只读正文加晚于它的评论」). One rule: a body refresh is preceded by reading the comment tail to its end and acknowledging every unread knock (a receipt line, or carry-over into the body's notes), so a refresh cannot void an unread request; ⛔ the read-window rule itself (#7942) is not cancelled — if the only fix is to cancel it, stop and report. Optional mechanical half, same PR if taken: scripts/pm/post-stamped.mjs --body refuses a body write while comments newer than the post's last body edit are unacknowledged (an explicit acknowledge flag), with a --self-test case in both directions. Net line budget: SKILL.md 0 (812/812), core-rules.md 0 (151/151), seat-post-protocol.md per its ratchet; ⛔ no re-wrap as currency; ⛔ not dispatch-runbook.md (#17935's face this wave).
    Container & model: M, mode:subagent, model: fable (claude-fable-5-1 — --tier at bdb247d9: pm-dispatch SKILL.md clause ①, MANDATORY); seat review at the contract-review tier; governed ⇒ draft PR at the human terminal with the four-piece, ⛔ never flipped ready by the seat
    Clause-②: no
    Thread-read: 5651030225 (triage grading, 2026-09-13T04:01Z — the newest comment at this claim)
    Serial constraints cleared: SKILL.md chain — #17742 (the chain head) is deferred behind #17942 by a note on its card, so SKILL.md is free at this claim; no open PR touches SKILL.md, core-rules.md, seat-post-protocol.md or post-stamped.mjs (19 file lists read at 2026-09-13T06:58Z; PR #17515 / #17803 touch references/decision-analysis.md / platform-readings.md); verify lock free; H17 index ∩ this face = ∅; #17935 (same wave) is fenced to dispatch-runbook.md only — disjoint.


    Generated by Claude Code

  2. claude commented on Sep 13, 2026

    @claude
    Contributor

    os-dev-report

    {
    "issue": 17905,
    "status": "done",
    "branch": "claude/issue-17905-unread-knock-before-body-refresh",
    "pr": "#17957",
    "premise_still_valid": true,
    "summary": "Delivered the write-side half of acceptance item 1 (5651030225), text and mechanism, in one governed draft PR at 68e8bcc. Text: SKILL.md :274 keeps 「读侧只读贴正文 + 晚于正文最后编辑的评论」 byte-for-byte and continues in place 「;刷新前读完评论、回执未读敲门,再刷。」 (112 B, no line added, 812/812); core-rules.md :74 mirrors it 「…读侧只读正文加晚于它的评论;刷新前读完评论、回执未读敲门。」 (118 B, 151/151; the clause 「热文件串行队是正文具名段」 leaves that line to fit the 120-byte cap — :71 already lists 热文件串行队 as one of the four fixed sections, SKILL.md :275 untouched); seat-post-protocol.md gains one line under 写侧刷新三时刻 「刷新前回执未读敲门;post-stamped.mjs --body 拦截:评论晚于正文末次戳 ⇒ --ack-through=ID。」 (116 B) paid by deleting the pointer 「会话句柄是账号作用域的,细则见 dispatch-runbook.md。」 whose fact lives in full at dispatch-runbook.md :133 (91 → 90 lines, ceiling 91 untouched, no re-wrap, dispatch-runbook.md not touched). Mechanism: scripts/pm/post-stamped.mjs --body=N reads the card and its comment tail before writing and REFUSES (exit 2, nothing written) while comments newer than the body's newest protocol stamp exist and --ack-through=ID does not name the newest of them; an older or unknown id is refused with the trailing comments listed; no newer comment ⇒ no flag and an unchanged write; --comment and --dry-run untouched. P2 measured before design: the REST issue object has no body-edit field, the timeline has no body-edit event, POST /graphql answers 403 ⇒ the last-write instant is the body's own newest stamp, a lower bound that over-includes, never under (seat post 6015 reads 2026-09-12T00:35Z against a 2026-09-13 rewrite). #7942's read window is not cancelled; 6015/6017 comments were read, never written. P4 re-check: 7 PRs opened after 06:58Z, none touches the four files; origin/main moved 3 commits since bdb247d, none on this surface. The decision frame was not needed (no fork); its md5 at 84e6b05 is reported in decision_frame_md5.",
    "tests": "node scripts/pm/post-stamped.mjs --self-test ⇒ '✓ post-stamped self-test: 117 cases pass across 8 batteries — offline, no network, no token.' (new battery 'the unread-knock check: a refresh cannot void what nobody read', 23 cases, plus 4 CLI cases for --ack-through; battery floor 7 → 8). Live fixture on PR #17957 (a PR is an issue to REST, same code path as a seat post): step 1 knock comment 5652164685 posted 2026-09-13T08:16:48Z via --comment (drift 0); step 2 --body without ack ⇒ exit 2 'post-stamped: REFUSED — 1 comment(s) on #17957 are newer than the body's last write stamp (2026-09-13T08:16Z) and this refresh acknowledges none of them. Nothing was written.' + '…re-run with --ack-through=5652164685 — the newest comment.' + the listed row '1. 5652164685 · 2026-09-13T08:16:48Z · claude[bot] · Fixture knock…'; body read back byte-identical (9436 B), updated_at unchanged; step 3 --ack-through=5652164685 ⇒ exit 0 'unread check: 1 comment(s) newer than the body's last write stamp 2026-09-13T08:16Z, acknowledged through 5652164685 (the newest)'; step 4 no flag ⇒ exit 0 'unread check: no comment newer than the body's last write stamp 2026-09-13T08:19Z — nothing to acknowledge'. Gates: derived 40 commands with dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at 68e8bcc (no paths), all run with exit captured before any pipe, reconciled: '✓ dispatch-gates --ran: 40 derived famil(ies) accounted for — 40 run, 0 NOT-MEASURED'. Acceptance greps BASE bdb247d → HEAD 68e8bcc: 「回执未读敲门」 SKILL.md 0→1 · core-rules.md 0→1 · seat-post-protocol.md 0→1; lit controls 「晚于正文最后编辑的评论」 SKILL.md 2→2, 「读侧只读正文加晚于它的评论」 core-rules 1→1; --ack-through seat-post-protocol 0→1. Ablation: none performed — no dist is involved (the tool runs from source) and both directions were observed live (step 2 refused vs steps 3/4 accepted) and offline; no guard-removal ablation applies.",
    "gates": {
    "check:pm-skill-ratchet": "✓ exit 0 — SKILL.md 812/812, core-rules.md 151/151, seat-post-protocol.md 90/91, every line within 120 bytes",
    "check:pm-skill-id-lint": "✓ 27 files clean",
    "check:skill-frame-sync": "✓",
    "check:pm-governed-prose": "✓",
    "check:nul-bytes": "✓",
    "check:refd-timer-probe": "✓ 6686 files swept",
    "check:pm-post-stamped": "✓ 117 cases / 8 batteries (owed: the script changed)",
    "check:pm-dispatch-gates": "✓ 1678 cases (ran past the 10-minute foreground cap as its header says it does; harness-detached; log read through to the verdict line)",
    "check:doc-formula-expressions (@objectstack/lint)": "first pass exit 3 PREREQUISITE NOT MET (unbuilt package, NOT MEASURED); re-run after the lock-wrapped prerequisite build (turbo, 4/4 cache hits): ✓ 58 self-test cases, 22 examples clean",
    "other 31 derived commands": "✓ all exit 0 (reference-carrier-shape, ci-filter-parity, closing-keyword-parity, comment-mask-corpus, declaration-mirrors, scripts-symbol-anchors, self-test-wired, self-test-workflow-commands, whole-set-label-write, bare-root-worklist, governed-queue-guard, report-test-timings, agent-test-spelling, bash32-floor, cli-command-ids, cross-package-test-inputs, doc-authoring, driver-memory-census, entry-guard, parse-guard, pm-governed-merges, pnpm-filter-targets, ratchet-remedy-authority, watch-hint-literal, with their --self-test where listed)",
    "--ran reconciliation": "✓ 40 derived, 40 run, 0 NOT-MEASURED, 0 UNRUN",
    "not run locally by declaration": "whole-repo scans (pnpm lint, typecheck/test farms) — CI owns them; no package src or test touched, so no ① build / ② package test owed",
    "CI on the PR": "in_progress — not waited on"
    },
    "line_budget": {
    ".claude/skills/pm-dispatch/SKILL.md": "812/812 (one line edited in place, 112 B)",
    ".claude/skills/pm-dispatch/references/core-rules.md": "151/151 (one line edited in place, 118 B)",
    ".claude/skills/pm-dispatch/references/seat-post-protocol.md": "90/91 (+1 line 116 B, −1 pointer line and its blank; ceiling untouched)"
    },
    "files_changed": [
    ".claude/skills/pm-dispatch/SKILL.md",
    ".claude/skills/pm-dispatch/references/core-rules.md",
    ".claude/skills/pm-dispatch/references/seat-post-protocol.md",
    "scripts/pm/post-stamped.mjs"
    ],
    "labels": "skip-changeset applied via REST POST /issues/17957/labels; read back: documentation, size/m, skip-changeset (the first two are the bots'); no changeset — .claude/** and scripts/pm/** publish nothing",
    "pr_state": "draft, base main, first line 'Fixes #17905'; never flipped ready, never queued, no auto-merge, no review; description carries 「## 维护者速读(草稿)」 (five parts, 席位意见 blank)",
    "deviations": [
    "PR description footer: the edit route (PATCH /issues on a PR number) appended the platform's bare footer under the sent session-URL block (+58 B, two footers after fixture step 3) — the reading platform-readings records for the PATCH route; the final write (step 4) sent no footer and read back exactly one (the platform's bare form); durable attribution is a sentence in the description prose naming the session URL.",
    "The derived gate loop ran past the 10-minute foreground cap on check:pm-dispatch-gates and was detached by the harness (not a watcher I started); its log was read through to '✓ dispatch-gates self-test: 1678 cases pass.'; nothing is left running.",
    "check:doc-formula-expressions recorded exit 3 (PREREQUISITE NOT MET) on the first pass; the reconciliation record carries the measured exit 0 re-run after the lock-wrapped prerequisite build.",
    "core-rules.md :74 drops the clause 「热文件串行队是正文具名段」 to stay within the 120-byte line cap; :71 of the same file keeps the fact and SKILL.md :275 is untouched.",
    "Commit trailer pair uses the repo's model-free form (AGENTS.md) rather than the harness reminder's model-named Co-Authored-By; the pre-push hook confirmed no model identifier."
    ],
    "decision_frame_md5": "3327d02c56f8a0eca88569dad2270f32 at 84e6b05 (PM's stated 3327d02c56f8a0eca88569dad2270f32: match)",
    "mcp_calls": "0 — REST only (curl) for the card, comments, PR creation, labels and read-backs; the tool's own writes went through post-stamped.mjs (fetch)",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed: seat post 6015's body carries 28 protocol stamps whose newest is 2026-09-12T00:35Z while its last rewrite was on 2026-09-13 (that rewrite wrote a date, not a stamp), so the first checked refresh there will list every comment since that stamp and ask for the newest id — the conservative direction, one flag. Bearer: the triage seat at its next refresh; no card.",
    "noted, not filed: pnpm --filter @objectstack/lint run check:doc-formula-expressions answers exit 3 PREREQUISITE NOT MET on a fresh worktree until @objectstack/formula and @objectstack/lint are built; the gate's own text prescribes the fix. Bearer: none.",
    "noted, not filed: the read-side / knocker-side half (a delivery receipt for the knocker) is not built; acceptance item 1 asks for at least one half and the write side is the one enforceable without GraphQL or a new dependency. Bearer: PM, if the other half is wanted as its own card."
    ]
    }


    Generated by Claude Code

  3. claude commented on Sep 13, 2026

    @claude
    Contributor

    ACCEPT — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8, GitHub os-project-manager), contract-review tier, read on PR #17957 head 68e8bcc8 at 2026-09-13T08:28Z (report read after the head; PR opened by the dev at 2026-09-13T08:16Z, fable-tier build per the SKILL.md path mandate).

    Scope against the claim (5652020801): the write-side half of acceptance item 1 — rule text in SKILL.md :274 and core-rules.md :74 (same PR, same change), one mechanism line in seat-post-protocol.md, and the optional mechanical half built in scripts/pm/post-stamped.mjs (--body refuses, exit 2 and nothing written, while comments newer than the body's newest protocol stamp exist and --ack-through=ID does not name the newest; --comment and --dry-run untouched). 812 → 812, 151 → 151, 91 → 90 (ceiling untouched), edited lines 112 / 118 / 116 B, no re-wrap as currency. Two density payments read and accepted: core-rules.md :74 drops 「热文件串行队是正文具名段」 (kept at SKILL.md :275 and in :71's four sections); seat-post-protocol.md drops the session-handle pointer whose fact stands at dispatch-runbook.md :133.

    Read on the head, not the report: 「回执未读敲门」 0 → 1 in each of the three rule files; controls 「晚于正文最后编辑的评论」 2 → 2, 「读侧只读正文加晚于它的评论」 1 → 1; ack-through 0 → 1 in the protocol file; post-stamped.mjs --self-test 117 cases / 8 batteries pass and check-skill-line-ratchet exit 0, both seat-run on the head in a throwaway worktree; control-character scan clean; single commit; Fixes #17905 once, one footer, ## 维护者速读(草稿) present; skip-changeset on the PR. origin/main moved to 9ccc4179 after the branch point without touching the four files. Dev gates: 40 derived / 40 run / 0 unrun, reconciled with --ran; check:doc-formula-expressions first exit 3 (PREREQUISITE NOT MET) → built under the lock → exit 0; the live fixture on the PR (knock 5652164685 → refused → acknowledged → no-flag no-op) observed both directions. Seat-run check-clause2-carriers --pair 17957 exit 0. CI on 68e8bcc8 at review time: 31 success / 17 skipped, Lint & Repo Gates running.

    Contract review of record: PASS, comment 5652205137 on the PR (head 68e8bcc8).

    Declared deviations accepted: PATCH-route footer duplication healed to one; the gate loop's foreground cap on check:pm-dispatch-gates (read to its verdict line); model-free trailer per AGENTS.md. Out-of-scope notes (carriers as the dev named them): the knocker-side receipt is not built — the write side closes the filed shape, no card unless the read side is wanted; seat post #6015's newest stamp predates its last rewrite, so its first checked refresh lists every comment since (conservative, one flag) — bearer the triage seat; the fresh-worktree exit-3 of check:doc-formula-expressions — bearer none.

    Governed ⇒ human terminal: needs-user-decision on PR #17957 (read back), 速读终稿 5652205261 posted there, reviews requested from os-zhuang and hotlong (read back). ⛔ Not flipped ready, queued or approved by this seat. The card stays pm:dispatched + assigned until MERGED. Hot-file consequence recorded on the seat post: SKILL.md / core-rules.md / seat-post-protocol.md are occupied by this PR until it lands; the charter-revision PR (chain head #17942) queues behind it.


    Generated by Claude Code

  4. claude commented on Sep 13, 2026

    @claude
    Contributor

    LANDED — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T11:15Z. PR #17957 merged through the merge queue at 2026-09-13T10:39Z as 1d811f5c (merged_by os-zhuang, whose approval 5190394706 on head 68e8bcc8 at 10:14Z was the ruling-C authorization; the maintainer also flipped ready and armed auto-merge, the seat did the four readings and cleared the label — 5652669857). Two readings at 2026-09-13T11:13Z: the queue ref gh-readonly-queue/main/pr-17957-* is gone (the only queue ref is pr-17986), and origin/main carries the content — git log origin/main --oneline -80 has (#17957) 1 with control (#17977) 1. Card closed by the PR's Fixes line; pm:dispatched residue and the assignee stripped in this pass and read back. Contract review of record 5652205137 (head 68e8bcc8, unchanged to landing); 速读 5652205261; ACCEPT 5652216958. SKILL.md / core-rules.md / seat-post-protocol.md / post-stamped.mjs are free again for the charter-revision PR.


    Generated by Claude Code

  5. added a commit that references this issue on Sep 17, 2026
    1d811f5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions