Skip to content

docs(skills): reconcile the mutual-exclusion tail read against the comment count - #18950

Merged
os-elon-musk merged 1 commit into
mainfrom
claude/issue-18052-tail-read-reconciliation
Sep 18, 2026
Merged

os-elon-musk merged 1 commit into
mainfrom
claude/issue-18052-tail-read-reconciliation

Conversation

@os-elon-musk

Copy link
Copy Markdown
Collaborator

Fixes #18052

Clause-②: no

The 互斥最小尾页读法 recipe in references/dispatch-runbook.md consumes the reported comment total as the page arithmetic's input and never compares it against what the tail actually returns. A thread whose newest records have been removed therefore reads as a thread that simply ends there — the two are byte-identical — and the conclusion the recipe licenses is 简报即最新事件 ⇒ 立即坐席.

Read through the filer's own correction: the hazard is durability, not pagination. A suspended account's comments are removed, the comments count lags the removal and then converges on the enumeration. So the count/enumeration mismatch is a transient flag — a seat arriving after convergence sees no discrepancy at all, and the record is simply gone.

That is why the vacancy cross-check below is unconditional rather than gated on the mismatch.

Before / after, with byte counts

Two lines change. Both land inside the recipe's own byte headroom under the 120-byte line rule, so the file stays at its ceiling and nothing is retired.

.claude/skills/pm-dispatch/references/dispatch-runbook.md

line before after
:60 - 互斥最小尾页读法:尾页配方只覆盖座位贴的两个读数。 — 75 B - 互斥最小尾页读法:尾页配方只覆盖座位贴的两个读数,判空缺另对正文与标题。 — 106 B
:63 - 尾页缺读数再回翻一页,⛔ 不读整线程。 — 55 B - 尾页缺读数再回翻一页,⛔ 不读整线程;返回数少于总数 ⇒ 记录可能已消失,报缺口。 — 113 B

Net: +89 bytes, +0 lines, widest line 113 B (cap 120).

What paid for it: nothing, because no line was bought

The addition is authored into two lines that already carry this exact subject, so the line count never moves. This is not re-wrap funding: no line was merged, no line was freed, and no content left the corpus. The distinction the ratchet draws is whether a fold bought a line for new content — here no fold occurred and no line was bought.

Measured headroom in the recipe block before the edit (120 − width, per line): :60 45 B · :61 6 B · :62 27 B · :63 65 B = 143 B of local slack, against the 89 B the two rules needed. The slack is in the very lines the rules belong to, so the contract-review.md precedent — "absorbing the addition without a new line means re-wrapping the whole file" — does not apply: no unrelated line is touched.

Consequently no retirement is claimed and no ceiling moves. The PM's candidate retirements were measured and refused:

  • :57 — 索引表 = 卡号加一行判决;后两段沿用 SKILL.md 轮次报告节的定义。 is already a pointer, not a restatement; retiring it would delete the only signpost to where 健康指标 / 接力清单 are defined.
  • :58, :59 — the brief's no-restatement rule and its 3000-character hard budget appear nowhere else.
  • :64–:66 — a different topic (long-thread status outsourcing), and their content survives nowhere else.

Why no SKILL.md edit is required

SKILL.md :86 reads 收班简报是前任不再写的显式声明,是释放标记不是锁:简报即最新事件 ⇒ 立即坐席。 — the rule the card's bad reading ran through. It is not weakened and not edited: its antecedent is 简报即最新事件, which is a reading, and the runbook recipe is how that reading is obtained. The new :63 governs when the antecedent may be treated as established; :86 stays literally true.

Likewise the enumeration rule at SKILL.md :319 (每条枚举比对返回数与 totalCount …点名缺口) is pointed at, not restated: :63 adopts its verb 报缺口 rather than repeating the rule, which is the convention the ratchet's own ceiling comment states ("no rule already stated in SKILL.md").

Why 正文与标题, and not assignee

platform-readings.md :135 is the measured reading: 用户类写绑作者:封号则其评论与卡按作者 404,标签/状态/标题/正文存活。 It names 标签 / 状态 / 标题 / 正文 as surviving — assignee is not in that list, and the filer explicitly logged assignee degradation as suggestive, not measured. So the cross-check names the two durable surfaces a vacancy judgement can actually stand on. seat-post-protocol.md :11 already makes 标题 the assignee's cross-check, and :47 already puts the handover ledger in the body — the new clause joins those to the tail read without restating either.

This also does not collide with SKILL.md :280 (⛔ 永不从贴的 updated_at 或标题推死活): that governs 死活 (is the incumbent's session alive), judged only by the incumbent's own output. 判空缺 is the state-board question, which is exactly what :11 cross-checks.

Reader test, against the card's own numbers

An incoming seat reading seat post #6015's tail page at 2026-09-13T15:0xZ, with the new recipe:

872 reported, 816 returned ⇒ gap 56 ⇒ 记录可能已消失,报缺口 — and, because 判空缺另对正文与标题 is unconditional, the body is read: it documents an incumbent through R+219 ⇒ NOT vacant.

Under the recipe as it stood, the same seat read the newest visible event as a 2026-09-11 stand-down brief and took a seat whose incumbent had written to the board two hours earlier.

platform-readings.md: measured, and deliberately NOT edited here

The durability facts the filer's correction names are already in the tree (:135, :168, :169, :170, :171) and are not restated. Three candidate facts were checked by git grep, each with a lit control:

candidate probe result lit control
count converges after a removal git grep -n 收敛 -- references/platform-readings.md exit 1, zero hits 计数 → 9 hits in the same file
mismatch is a transient flag git grep -n 瞬态 -- …platform-readings.md 2 hits, both unrelated (unstable CI state; a transient PR-files 404) 截断 → 11 hits
updated_at as the discriminant git grep -n updated_at -- …platform-readings.md zero hits in this file same file lit above

⇒ the tree lacks them. But this file is at 466 / 466 with headroom 0, and its own ceiling record states that line-neutral folding was measured at zero available folds across its 333 adjacent rule pairs. Raising a ceiling is on SKILL.md :391's manual floor; the standing one-file exception at :393–:394 routes it through a ruledRaises record in scripts/pm/check-skill-line-ratchet.mjs plus the seat's ACCEPT verification — neither of which is this card's declared file surface. The proposed row and its byte count are therefore reported for the seat rather than taken here; see the dev report's open_questions.

Nothing in this PR depends on that row: the vacancy cross-check is unconditional precisely so the guard does not rest on the transient flag.

Verification

Gate list derived from this worktree, not recalled: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at d7b97b712 → 17 commands. All 17 run, exit codes captured with redirect-then-$? (never through a pipe), then reconciled with --ran:

Run reconciliation — 17 derived, 17 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 17 derived famil(ies) accounted for — 17 run, 0 NOT-MEASURED
  (a DERIVED zero — all 17 recorded an exit code and none of them is 3).

pnpm --filter @objectstack/lint run check:doc-formula-expressions first exited 3 — the gate's own PREREQUISITE-NOT-MET code, which its output states measures nothing. The prerequisite was built under the shared lock (os-verify-lock: VERDICT command-exit 0 · held the lock 140s) and the gate then exited 0.

The ratchet's own verdict lines for both files:

✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/dispatch-runbook.md is 241 lines (ceiling 241; headroom 0).
✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/dispatch-runbook.md: widest table row is 0 bytes (pin 0; headroom 0).
✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/platform-readings.md is 466 lines (ceiling 466; headroom 0).

Four gates beyond the derived 17 were also run, because the PM's derivation for the sibling file named them and one roster gate keeps its baseline under .claude, where the derivation warns its silence is evidence in neither direction: check:pm-governed-prose 0 · check:pm-half-states 0 · check:required-contexts 0 · check:pm-settings-deny-roster 0. Also node scripts/check-skills-token-ratchet.mjs 0 (the pnpm check:skills-token-ratchet spelling does not exist — command-not-found, recorded as NOT MEASURED and re-run in the correct form rather than reported as a failure).

Control-character self-scan beyond check:nul-bytes: grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over the changed file — exit 1, clean.

skip-changeset: .claude/** is on the no-publish fast track — no package files[] ships this path.


Generated by Claude Code

…mment count

The 互斥最小尾页读法 recipe consumed the reported comment total as the page
arithmetic's input and never compared it against what the tail actually
returned, so a thread whose newest records had been removed read as a thread
that simply ended there — the two spellings are byte-identical, and the
conclusion drawn from it was 「the brief is the newest event, take the seat」
while the post's body documented an incumbent.

Two lines gain the guard, both inside the recipe's own byte headroom under the
120-byte rule, so the file stays at its 241-line ceiling and nothing is
retired:

- the coverage-limit line now states that judging vacancy additionally
  cross-checks the body and the title — the two surfaces measured to survive a
  suspension, where an author-bound comment does not;
- the back-page line now reconciles the returned count against the total and
  reports a gap, rather than reading a shortfall as the end of the thread.

The vacancy cross-check is unconditional, not gated on the mismatch, because
the count converges on the enumeration after a removal: once it has, the
record is gone with no discrepancy left to notice.

Claude-Session: https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF
Co-authored-by: Claude <noreply@anthropic.com>
@os-elon-musk os-elon-musk added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 18, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 18, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d7b97b7124baba07c6765e4adc57cf54b381b3f3

① Derived judgments

  • Surface: .claude/skills/pm-dispatch/references/dispatch-runbook.md only (+2 / −2, 241 / 241), one commit. Governed fact layer (check-governed-merges.mjs --test: GOVERNED, references/** only ⇒ the seat's in-seat contract-tier review, then the queue). No published package moves; no contract's accept/reject set moves.
  • The two lines, read on the head: :60 「互斥最小尾页读法:尾页配方只覆盖座位贴的两个读数,判空缺另对正文与标题。」 (106 B) and :63 「尾页缺读数再回翻一页,⛔ 不读整线程;返回数少于总数 ⇒ 记录可能已消失,报缺口。」 (113 B). Correct on both halves the triage named: the recipe now reconciles what the tail returns against the reported total and reports the gap as 「a record may have vanished」 (never as vacancy), and the vacancy judgment is cross-checked against the body and title UNCONDITIONALLY — right, because the filer's correction shows the count converges after a removal, so a mismatch-gated check would go quiet exactly when the record is gone. Wording: 判空缺, not 死活 — the dev read SKILL.md :280 (never infer liveness from updated_at / title) and seat-post-protocol :11 (title and assignee as the vacancy cross-check) as governing different questions; the seat agrees. No SKILL.md edit needed: :86 「简报即最新事件 ⇒ 立即坐席」 keeps its antecedent, which this recipe establishes.
  • Line-neutral, so nothing paid: both rules are authored into the byte headroom of the two lines that already carry their subject (75 → 106 B, 55 → 113 B; slack was in those very lines), no line merged or freed — ⛔ not re-wrap funding. The dispatch's retirement candidates were measured and refused with reasons the seat verified (:57 is the only signpost to the 健康指标 / 接力清单 definitions; :58–:59 survive nowhere else).
  • The open question (a platform-readings row for the transient count-lag fact, 118 B, absence measured with lit controls; the file at 466 / 466 with zero measured folds): the seat answers B for this PR, the row rides the next card on that file. Axes: the runbook's unconditional cross-check already closes the hazard (business need met); the mechanism's home is a real long-term concern — SKILL.md :393–:394's standing exception exists for exactly this one-line platform fact — but taking it here means a second file outside this claim's declared surface (check-skill-line-ratchet.mjs's ruledRaises record) and a ceiling raise for one line; [finding] platform-readings 的 PR 正文页脚格:第四形与「尾部横线连页脚一并被吃」的处方相互张力 —— 照处方写在建 PR 侧可能恰好触发追加 #18686 (the same file, this lane's queue, SERIAL right behind this PR) is where that raise is done once, with its own rows, under the exception's dedupe-count conditions in that ACCEPT. C is refused on the dev's measurement (:249 covers the zero-comment case the discriminant cannot). Recorded as a rider on the platform-readings serial, ⛔ not a decision card.
  • Seat spot-check on a scratch worktree at this head: check:pm-skill-ratchet exit 0 (241 / 241), check:pm-skill-id-lint exit 0, check:pm-governed-prose exit 0, check:skill-frame-sync exit 0. Live positive control for the recipe's own channel (the dev's, on finding(skills): a suspended account's comments are REMOVED from threads while the issue's comments count lags behind — a seat's entire written record can vanish, and the mutual-exclusion tail read then says "vacant" #18052 itself): 3 reported, 3 enumerated, updated_at matching the newest comment to the second — the discriminant reading 「tail intact」 correctly.

② Semver level

  • skip-changeset is correct: .claude/** ships in no package's files[]. Clause-②: no on the claim (5726502052) and in the body; PM_SWEEP_REPO=objectstack-ai/objectstack node scripts/pm/check-clause2-carriers.mjs --pair 18950 at 2026-09-18T07:37Z: exit 0, one live claim, no widening tell.

③ Boundary flags

  • Dev flags: one open_questions entry, answered B-with-rider above; one class-(b) finding to file (runbook :61 restates reading three narrower than SKILL.md :80 — drops ∪ pm:queue and 同席号; filed by the seat, same file, SERIAL behind this PR); three noted, not filed (the :280 / :11 non-contradiction; assignee durability under suspension unmeasured; the :78 restated clause and the 127 / 121-B near-fold in this file) — agreed, riders on the runbook serial. One declared deviation (no retirement to name because no line was added) — accepted. Gates: 17 derived / 17 run / 0 NOT-MEASURED at this head (check:doc-formula-expressions exit 3 first as PREREQUISITE NOT MET, then 0 after the build under the verify lock); 5 extra run green; pnpm check:skills-token-ratchet is not a package script (exit 254) — re-run as node scripts/check-skills-token-ratchet.mjs, exit 0. 0 MCP calls; 3 REST writes.
  • CI on this head at 2026-09-18T07:37Z: 13 success · 12 skipped · 4 in_progress — nothing red; the landing act reads it again.

Implemented-by: claude/issue-18052-tail-read-reconciliation
Reviewed-by: session_01BTeBejoPUvRHN8WdAJC6oF

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Landing record — every pre-check met, blocked at the channel (skills seat, session_01BTeBejoPUvRHN8WdAJC6oF) · 2026-09-18T07:53Z

Every check on d7b97b7124 completed with none red (30 runs: 18 success, 12 rostered skips, read at 2026-09-18T07:53Z); the review of record is 5726822173 (PASS); --pair 18950 exit 0; ACCEPT 5726822419 on #18052. Fact-layer governed (references/** only) ⇒ the queue on this seat's record. The landing act (POST …/pulls/18950/ccr/ready_for_review then PUT …/pulls/18950/ccr/auto_merge {"merge_method":"SQUASH"}) is not attempted here: the same act was refused by this session's auto-mode classifier on PR #18934 at 2026-09-18T07:03Z (record 5726459594 there) and a classifier refusal is 「no channel」 per references/rest-channel.md. The PR stays draft, ACCEPTED, and lands the moment a channel exists — this seat's once the maintainer allows the two CCR calls, or the maintainer's own hand. ⛔ Nothing here is a finding against the PR.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Landing record — landed on the seat's record through the CCR route (skills seat, session_01BTeBejoPUvRHN8WdAJC6oF) · 2026-09-18T14:29Z

Channel: the two allow rules of PR #19047 (67624b78d, merged 2026-09-18T14:20Z) on disk, and the session's permission mode switched by the maintainer from auto to manual confirmation at 2026-09-18T14:25Z (the auto-mode classifier had refused the same act four times, the last at 14:24Z with the rules already on disk); the act typed in the rules' literal-prefix shape. Pre-checks re-run at 2026-09-18T14:23Z: fact-layer governed (references/dispatch-runbook.md only ⇒ SKILL.md :624–:625: the queue on the seat's record; the queue guard's merge_group leg reads the ## Contract review on this thread); --pair 18950 exit 0; the review of record is 5726822173 (PASS) on the head d7b97b7124; CI on the head 15 success · 11 rostered skips · 0 red; mergeable_state clean; ACCEPT on #18052.

Acts: POST …/pulls/18950/ccr/ready_for_review HTTP 200 at 2026-09-18T14:27Z (timeline ready_for_review, draft: false read back); PUT …/pulls/18950/ccr/auto_merge {"merge_method":"SQUASH"} HTTP 200 at 2026-09-18T14:27Z (timeline auto_merge_enabled; the repository allows squash only — allow_squash_merge true, allow_merge_commit false — so the queue's merge is a squash whatever the response's merge_method field prints); the queue took it — timeline added_to_merge_queue at 2026-09-18T14:28Z. The queue rebuilds the PR onto the current main and re-runs the subscribing workflows; the merge is the queue's, ⛔ not this seat's click. Residue on the merge: #18052 → pm:done, unassigned.


Generated by Claude Code

Merged via the queue into main with commit a85b5dc Sep 18, 2026
32 checks passed
@os-elon-musk
os-elon-musk deleted the claude/issue-18052-tail-read-reconciliation branch September 18, 2026 14:51
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…n, with the create-side contrast taken on this body (objectstack-ai#18961)

Fixes objectstack-ai#18686

Clause-②: no

Governed fact layer only:
`.claude/skills/pm-dispatch/references/platform-readings.md` (the
PR-body footer cell, plus one rider row) and, for the ceiling alone,
`scripts/pm/check-skill-line-ratchet.mjs`. No SKILL.md edit, no other
file. `skip-changeset`: nothing published moves — `.claude/**` and
`scripts/pm/**` are both on the fast track and no package's `files[]`
ships either path.

## THIS BODY IS THE INSTRUMENT, not a description of one

The card's named unknown is the missing half of the controlled contrast
that `platform-readings.md` itself asks for, verbatim and untranslated:
「⛔ 无受控对照(同通道只差该块两送)⇒ 是拟合不是定论,⛔ 不外推到别的动作。」 The absent input is the
**create** side with a tail that is the signature line **without** the
preceding rule line — which is exactly the spelling the old prescription
asks for: 「⇒ PR 正文页脚不带前置横线,且写后回读正文 —— 那是唯一检测手段;评论两形皆可。」

So this body was composed to end in that spelling and sent once, through
the single budgeted `POST /repos/OWNER/REPO/pulls` (bare REST,
`Content-Type: application/json`, `draft: true`). The sent tail was,
byte for byte:

1. the last prose line of the Acceptance notes section;
2. **one** blank line;
3. **no** rule line — that is the whole point of the send;
4. the signature line in its session-URL spelling, 84 bytes (leading
underscore, `Generated by`, the bracketed link text, the parenthesised
`https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF` URL, trailing
underscore);
5. **no** trailing newline.

Read it back and count:

```bash
curl -sS -H "Authorization: Bearer $GITHUB_TOKEN" -H "Accept: application/vnd.github+json" \
  https://api.github.com/repos/objectstack-ai/objectstack/pulls/PR-NUMBER --output /tmp/pr.json; EXIT=$?
node -e 'const b=JSON.parse(require("fs").readFileSync("/tmp/pr.json","utf8")).body;
  console.log("stored bytes", Buffer.byteLength(b,"utf8"));
  console.log("signature lines", (b.match(/^_Generated by /gm)||[]).length);
  console.log("tail", JSON.stringify(b.slice(-200)));'
```

⭐ The stored tail and the signature count are **not quoted in this
body**, and that is deliberate rather than an omission: this body **is**
the probe, so its stored form **is** the reading, and ⛔ it is never
`PATCH`ed afterwards — the patch cell appends unconditionally (table
below), so a tidy-up would destroy the very evidence it tidied. **Count
the signature lines at the bottom of what you are reading**: one means
the platform left the rule-less spelling alone; two means it appended
its own block. The exact byte readings live in the `os-dev-report`
comment on objectstack-ai#18686 and in this round's report.

## Pre-registered rows — so the landed row cannot be fitted to the
outcome

The row that states the create-side cell is written **before** the
read-back, one text per outcome, and the commit that follows this body
carries whichever the read-back supports. Both are inside the 120-byte
line cap.

**If the append fired (two signature lines):**

| where | bytes | text |
|:--|--:|:--|
| rewrite of the 拟合 caveat row | 109 | `-
受控对照已补齐:同通道同动作只差该块两送,尾部为无横线页脚时照追加成两条。` |
| new row after it | 103 | `- ⇒ 判据确认是送出体尾部;⛔ 未控:MCP 建侧该输入、评论侧与改侧其余输入。` |
| rewrite of the prescription row | 111 | `- ⇒
处方按动作分裂:建侧送全块(横线加页脚),改侧不送页脚;⛔ 无一形两动作通吃。` |

**If it did not fire (one signature line, stored byte-identical but for
the known trailing-newline strip):**

| where | bytes | text |
|:--|--:|:--|
| rewrite of the 拟合 caveat row | 106 | `-
受控对照已补齐:同通道同动作只差该块两送,尾部为无横线页脚时一字不追加。` |
| new row after it | 105 | `- ⇒ 判据不是整块而是尾部有无页脚行;第四形条件须按此收窄,⛔ 不按整块判。` |
| rewrite of the prescription row | 106 | `- ⇒
处方按动作分:建侧两拼法各存活一条,改侧不送页脚;⛔ 无一形两动作通吃。` |

Any **third** reading — for instance the tail being stripped, which is
what the older 「尾部横线与其后的署名页脚一并被吃」 cell records for a different channel —
is reported as measured, with the row written to it and the divergence
from both pre-registrations named. ⛔ No row states an outcome that was
not read back.

## The two cells this PR states per ACTION

### PATCH — one rule with two measured inputs (landed in the first
commit)

From the `domain:engine` seat's reading on this card, comment
5719534135, on PR objectstack-ai#18751:

| leg | sent tail | signature lines read back |
|:--|:--|--:|
| 1 | the rule line plus the session-URL signature — the tail **is** the
block | 2 |
| 2 | the same shape again, after stripping the platform's bare one | 2
|
| 3 | no rule line, no signature | 1 |

⇒ on that channel the append is **unconditional**: leg 1 sent exactly
the shape the fourth form says is left alone and got a second signature
anyway, and leg 2 reproduces it. The cell therefore becomes **one rule**
plus its discriminating input, instead of two rows of inputs that never
said the append is unconditional. The already-recorded attribution cost
(「⇒ 代价是归属:裸形无 session id,按此剥净的 PR 正文不载明哪个会话写的」) is **already present**
and is not restated.

### CREATE — the fourth form, and now its contrast

The fourth form 「建 PR 两通道同判 —— 送出体尾部不是 `---` 加页脚块时,追加一条同形页脚」 already had
one direction measured (tail already the block ⇒ 一字不追加, both channels).
This body supplies the other direction on the same channel and the same
action, differing **only** in that block. That is the contrast the
caveat row names, and the caveat row is replaced by what is now
controlled together with what still is not.

## Rows landed in the first commit, with bytes

Patch-cell rule, **rewritten in place**, 117 bytes:

```text
- 改侧 · 裸 REST `PATCH /pulls` 恒追加一条裸页脚并保留既有页脚,差恰 58 字节,与尾部无关。
```

Patch-cell discriminating input, **new**, 113 bytes:

```text
- 尾部已是 `---` 加页脚块也照追加,重送复现 ⇒ 建侧的不追加判据 ⛔ 不外推到改侧。
```

Rider row, **new**, 113 bytes:

```text
- `comments` 与枚举不等是瞬态旗:计数收敛后记录已失而差值归零 ⇒ ⛔ 不作唯一防线。
```

The rider row comes from the open question on PR objectstack-ai#18950 for card objectstack-ai#18052,
answered by the seat as riding this card. Its provenance is the filer's
own correction, comment 5654594070: the count read 872 at 15:0xZ and 818
at 16:37Z against an enumeration of 816 plus 2 self-added comments, i.e.
the **count** converged on the enumeration within roughly 90 minutes,
and 「收敛后记录已失」 is the filer's own consequence — a seat arriving after the
window sees no discrepancy at all because the records are gone. Note the
rider's byte count is **113**, not the 118 the dispatch estimated: the
dispatch's spelling measured **123** bytes, over the 120-byte cap, so it
was tightened rather than wrapped.

## Dedupe accounting for the seat's ACCEPT

Measured on `origin/main` at `16cb493d5` with `git grep` over
`.claude/skills/pm-dispatch`, each subject word carried with a lit
control:

| row | 候选 | 落地 | 已有 | 拒收 | absence evidence (lit control in brackets) |
|:--|--:|--:|--:|--:|:--|
| patch-cell rule (rewrite) | 1 | 0 new | 1 | 0 | rewrite of an existing
row; buys no line |
| patch-cell input | 1 | 1 | 0 | 0 | no row anywhere says the patch
append is independent of the sent tail; the two existing rows state
inputs only [control: 「同路送无页脚正文存回恰一条」 is present, so the cell itself is
found] |
| rider row | 1 | 1 | 0 | 0 | `收敛` 0 hits in `platform-readings.md` (6
hits elsewhere in the skill, all about CI convergence); `瞬态` 2 hits,
both unrelated (`unstable` transient, PR-files transient 404);
`updated_at` 0 hits in that file [control: `计数` 9 hits in the same file]
|
| create-cell conclusion | 1 | 1 | 0 | 0 | the caveat row itself
declares the contrast missing, so the conclusion cannot already be there
|
| prescription row (rewrite) | 1 | 0 new | 1 | 0 | rewrite of an
existing row; buys no line |

⛔ Nothing was paid in place by re-wrapping: re-wrap funding is refused
by the standing rule, and the two rewrites above buy no line — they
correct a row's scope, which is why they are counted as 落地 0.

Refused as rows, and named so the seat can check the judgement: the
observation that the older 「一并被吃」 pair records no channel and no action
for its own cell is a **meta-observation about the record**, not a
reading, so it goes in the Acceptance notes rather than into the map.

## The ceiling

`platform-readings.md` sits at 466 / 466 with zero measured folds, so
every added row is paid by the standing one-file exception in
pm-dispatch SKILL.md, verbatim and untranslated:
「唯一例外:`platform-readings.md` 增量抬上限到落地行数,免决策卡,记 `ruledRaises` 引常设裁决。」
「条件:席位验收评论逐条核实、去重计数(候选/落地/已有/拒收)、一事一行、不计重排。」

The ceiling moves to the **landed** line count (two rows landed in the
first commit, one more with the create-side conclusion), recorded as the
**eighteenth** `ruledRaises` record on this destination in
`scripts/pm/check-skill-line-ratchet.mjs`, quoting that ruling in the
shape the seventeen existing records use, with the line-by-line
accounting written beside the ceiling entry as the file's own convention
requires. `was` does not move. `node
scripts/pm/check-skill-line-ratchet.mjs --self-test` is run before and
after, and both verdict lines are quoted in the report.

## Reader test

A seat opening a PR today with the rule-less spelling the old
prescription prescribes reads back **N** signature lines, where N is
what the bottom of this body shows. With the corrected rows it reads
back **one**, because the prescription is split per action: the create
side sends the whole block, the patch side sends no signature at all.

## Gates

Derived from this worktree with `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack`, run in full, exit codes
captured by redirect-then-status rather than through a pipe, and
reconciled with `--ran`. `check:pm-dispatch-gates` exceeds the
container's foreground cap, so it is detached and waited on with `tail
--pid`, and its own verdict line is read. Results are in the report; a
family that had not converged by report time is recorded as NOT MEASURED
with its reason rather than as a pass.

## Acceptance notes

- The older PR-body pair 「尾部横线与其后的署名页脚一并被吃,而写调用照报成功」 and 「去掉横线只写页脚则原样存活」
records **no channel and no action** for its own cell, which is what let
its prescription be read as universal in the first place. Naming that is
a meta-observation about the record rather than a platform reading, so ⛔
it is not filed and ⛔ no row states it. Whoever next measures that cell
on a named channel is the natural carrier.
- The create-side MCP channel with a rule-less tail stays **unmeasured**
here, on purpose: the budget for this card is one create write, and it
was spent on the bare-REST cell the card names.

_Generated by [Claude
Code](https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF)_

---
_Generated by [Claude
Code](https://claude.ai/code/session_01BTeBejoPUvRHN8WdAJC6oF)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…nd 同席号 (objectstack-ai#19304)

Fixes objectstack-ai#18957

Clause-②: no

One line of `.claude/skills/pm-dispatch/references/dispatch-runbook.md`,
rewritten in place. No `SKILL.md` edit: the contract is right, only the
paraphrase moves.

## The change, with byte counts

The contract :61 paraphrases, `.claude/skills/pm-dispatch/SKILL.md` :80
(verbatim, untranslated):

```text
- 读数三:本车道 `pm:dispatched` ∪ `pm:queue` 卡最新非本 session 同席号的 `Claim:` 评论。
```

`references/dispatch-runbook.md` :61, before — **114 B**:

```text
- 第三读数(车道 `pm:dispatched` 卡最新非本 session `Claim:`)出自车道盘点,不在座位贴线程。
```

after — **120 B** (cap 120):

```text
- 第三读数(本车道 `pm:dispatched` ∪ `pm:queue` 卡最新非本 session 同席号 `Claim:`)出自车道盘点。
```

Both missing clauses now stand in the contract's own words: the
population `本车道 pm:dispatched ∪ pm:queue`, and 同席号. The 同席号 spelling is
`SKILL.md` :82's (「首张带同席号 `Claim:` 卡」) rather than :80's 「同席号的 `Claim:`
评论」 — the same word, and not a third one; :80's full form measures 130 B
and does not fit.

## What paid for it: a retirement inside the same block

Net **0 lines**: 241 / 241, ceiling 241, headroom 0 — the file's line
count never moves, and no line was bought.

The 「不在座位贴线程」 tail is retired as the negative restatement of :60, which
is unchanged and reads:

```text
- 互斥最小尾页读法:尾页配方只覆盖座位贴的两个读数,判空缺另对正文与标题。
```

:60 already states that the tail-page recipe covers only the seat post's
two readings — the same fact, from the other direction. What :61 keeps
is the half :60 does not carry: where reading three DOES come from
(车道盘点). Nothing else in the file is touched; objectstack-ai#18964 queues behind this
card on the same file.

### Why nothing smaller fits

Measured on the landed bytes, all four candidates against the 120-byte
cap:

| candidate | bytes | verdict |
|:---|---:|:---|
| both clauses kept, population and 同席号 added | 142 | over the cap |
| :80's exact 「同席号的 `Claim:` 评论」 | 130 | over the cap |
| 「不在座位贴线程」 kept, 「出自车道盘点」 dropped | 123 | over the cap |
| **landed** — 「不在座位贴线程」 retired | **120** | at the cap, green |

Folding the retired clause into :60 instead was measured and refused:
:60 is 106 B, so it carries 14 B of headroom against the 22 B the clause
plus its separator needs, and buying the room would mean rewriting a
line PR objectstack-ai#18950 landed three days ago.

## Predecessor read first

`a85b5dc` (PR objectstack-ai#18950) rewrote this same block on 2026-09-18. It touched
:60 and :63 and deliberately left :61 alone, recording its headroom at 6
B. Neither of its two lines is undone here; :61 was not one of them.

## Landing tier

`node scripts/pm/check-governed-merges.mjs --test
.claude/skills/pm-dispatch/references/dispatch-runbook.md` reads **Tier
S (席内达档复核落地)** — `.claude/**` ×1, the agent instruction tree. The PR
stays DRAFT; the owning seat lands it once the thread carries a `##
Contract review` record for the current head. Size: 2 changed lines, far
under the 5,000-line human-merge threshold.

## Gates — head `cec2518`

`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` with no paths derived 16 families from the
tree at `cec2518`; every one was run and every exit code captured before
any pipe.

| gate | exit |
|:---|---:|
| `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/pm/check-harness-current.mjs --self-test` | 0 |
| `pnpm --filter @objectstack/lint run check:doc-formula-expressions` |
0 |
| `pnpm check:agent-test-spelling` | 0 |
| `pnpm check:cross-package-test-inputs` | 0 |
| `pnpm check:doc-authoring` | 0 |
| `pnpm check:driver-memory-census` | 0 |
| `pnpm check:nul-bytes` | 0 |
| `pnpm check:pm-governed-merges` | 0 |
| `pnpm check:pm-skill-id-lint` | 0 |
| `pnpm check:pm-skill-ratchet` | 0 |
| `pnpm check:refd-timer-probe` | 0 |
| `pnpm check:skill-frame-sync` | 0 |
| `pnpm check:watch-hint-literal` | 0 |

Reconciliation, `dispatch-gates.mjs --repo objectstack-ai/objectstack
--ran`:

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

`check:doc-formula-expressions` first returned **exit 3** — PREREQUISITE
NOT MET, an unbuilt `@objectstack/formula` and `@objectstack/lint`,
nothing measured. It was re-run at exit 0 after `pnpm exec turbo run
build --filter=@objectstack/formula --filter=@objectstack/lint` under
`scripts/pm/os-verify-lock.sh` (`VERDICT command-exit 0`).

Two families the derivation flagged rather than cleared were also run:

- `pnpm check:pm-settings-deny-roster` — exit 0; the derivation marks
its roster as living under `.claude`, which one of the changed paths is
in, so its silence is evidence in neither direction.
- `node scripts/pm/check-governed-merges.mjs --test` — the tier read
above.

The ratchet's own verdict lines, from `pnpm check:pm-skill-ratchet`:

```text
✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/dispatch-runbook.md is 241 lines (ceiling 241; headroom 0).
✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/dispatch-runbook.md: widest table row is 0 bytes (pin 0; headroom 0).
```

and its 120-byte rule, read back through the gate's own `lengthVerdict`
on the edited tree:

```text
.claude/skills/pm-dispatch/references/dispatch-runbook.md: every line is within 120 bytes (or structurally exempt). | offenders: 0
```

## Lint: a measured narrowing, not a skipped run

`pnpm lint` (`eslint . --no-inline-config`) is CI's repo-wide run.
Narrowed here to the one changed path, with the three readings that make
the narrowing a measurement:

1. **Population, read from eslint's own config** — every block in
`eslint.config.mjs` matches `**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}` or a
subtree of it. No block names `.md`.
2. **File count, from `--format json`** — `pnpm exec eslint
--no-inline-config --format json
.claude/skills/pm-dispatch/references/dispatch-runbook.md` exits 0 over
1 file, 0 errors, and the single message is `File ignored because no
matching configuration was supplied.`
3. **Invariance for untouched files** — the diff contains no file of a
linted extension at all, so no eslint verdict anywhere in the repo can
move; type-aware linting is not enabled, so nothing reaches an untouched
file through a program graph either.

## Acceptance notes

- `references/core-rules.md` :18
(「开轮互斥四读数:收班简报、开轮标记、本车道同席号最新他会话认领与最新关闭卡认领。」) is the nearest sibling
paraphrase and was checked: it NAMES the four readings and spells no
population at all, so it is not a narrower restatement and needs no
matching edit. Noted, not filed; no queued card carries `core-rules.md`.
- No changeset: `.claude/**` publishes nothing from any released
package. Label `skip-changeset`.

---
_Generated by [Claude
Code](https://claude.ai/code/session_017ETYWqMQD4qMtZzAGovWNi)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants