Skip to content

skills(pm-dispatch): a seated session reads whether origin/main touched its charter before its first write, and re-reads on a hit - #17136

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-17007-charter-reread-at-round-open
Sep 9, 2026
Merged

skills(pm-dispatch): a seated session reads whether origin/main touched its charter before its first write, and re-reads on a hit#17136
os-zhuang merged 1 commit into
mainfrom
claude/issue-17007-charter-reread-at-round-open

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #17007

What this changes

.claude/skills/pm-dispatch/SKILL.md 〈入口与角色〉, the round-open sequence: the marker line :89 becomes two lines :89–:90, and the seat-post block's restatement at :286 leaves the file — 812/812 lines, every touched line ≤120 bytes. references/core-rules.md :20 absorbs the twin in place — 151/151. references/seat-post-protocol.md is untouched (91/91): measured, it carries no round-open marker line (grep -n 开轮 references/seat-post-protocol.md → 0 hits; the only marker lines in the corpus are SKILL.md :89 and its digest at core-rules :20), so the marker's content spec lives on SKILL.md :90 alone and no third copy is created — dispatch assumption B held.

Before (0da638cd), widths by printf '%s' LINE | wc -c:

SKILL.md :89   - 互斥清 ⇒ 先在座位贴留一行开轮标记(session ID + fire 时刻)再跑轮。                          90 B
SKILL.md :286  - 归档自己派出的会话是不可移交的义务。                                                       56 B
core-rules :20 - 未收班活动任一未满一轮即自退,总监席除外;有收班简报即坐席,坐席后先写开轮标记。         116 B

After (7e75fb9f):

SKILL.md :89   - 互斥清 ⇒ fetch 后读三章程文件(本文、core-rules、本席章程)在 `origin/main` 的最新触碰 sha。   119 B
SKILL.md :90   - 异于上一开轮标记即先重读;留开轮标记(session ID + fire 时刻 + 该触碰,注明重读)再跑轮。       117 B
core-rules :20 - 未收班活动任一未满一轮即自退,总监席除外;有简报即坐席,坐席后核章程再写开轮标记。          119 B

Gloss: mutex clear ⇒ fetch, then read the newest touch (commit sha) of the three charter files the seat loaded — this file, core-rules, its own lane charter — on origin/main. If it differs from the one the seat's previous round-open marker recorded, re-read those files before anything else; then leave the marker (session ID + fire time + that touch, noting a re-read) and run the round. The digest line names the step (after seating, verify the charter, then write the marker); 有简报即坐席 is the same clause with 收班 dropped — :19 names 收班简报 immediately above it. The sequence is the one the dispatch route suggested: mutex clear ⇒ charter-touch reading ⇒ re-read on a hit ⇒ marker naming the re-read. The marker's own fire timestamp stays; no seating time is needed (next section).

The mechanism: a content reading, not a time window — dispatch assumption A measured, and partly falsified

  • The helper scripts/pm/git-history.mjs does give a one-line, refusal-capable windowed reading: log --since=DATE --ref=origin/main --path=P (repeatable), exit 2 with empty stdout when the shallow floor sits inside the window, auto-deepen otherwise. It answers exactly the shape the card spelled (git log origin/main --since=SEATING -- PATHS).
  • That shape carries a second trap the card did not name. Measured on this mainline — REST merged_at against the local %cI of each merge_commit_sha, the 29 most recently merged PRs: every landed commit is dated 19.6–34.1 min before merged_at (the queue builds the commit at enqueue; main receives it at landing). A window keyed to the marker's fire time — the sliding form the route suggested — is blind to a governed merge whose commit predates fire N but lands after it: absent at N (not on main yet), absent at N+1 (dated below the window start). The card's literal fixed start --since=SEATING has no blind spot but re-hits on every fire after the first governed merge — the hourly read-through the card forbids.
  • The repo already rules on this inversion: scripts/pm/check-governed-merges.mjs header, 「The WINDOW is landing order, not committer dates」 — ruled 2026-08-27 on finding(check-governed-merges): the merge queue's build-to-land skew can put a governed landing BELOW a round's --since boundary, where it is silently absent from the audit list #12633, topological --since-ref preferred, a bare --since backed off by a declared 3,600 s budget. Today's skew (up to 2,046 s) exceeds the 874 s maximum that header re-measured on 2026-08-27 and sits inside its budget.
  • So the criterion reads the tip, topologically: git fetch origin main, then git log -1 --format=%h origin/main -- PATHS, compared with the value on the seat's previous marker (today's reading for SKILL.md: 3045b6fa). No window ⇒ no floor question: a new touch is always above any shallow floor, and a floor ABOVE the recorded touch reads the graft boundary's own sha — measured on this depth-1305 clone (floor 1af82861, 2026-09-01): git log -1 --format=%h origin/main -- LICENSE, a path untouched in the visible window, prints 1af82861, because a shallow boundary has no parent and git lists it as introducing every path — which mismatches the recorded sha and lands on the safe side: a spurious re-read, never a false clean. The one stale-clean path is an unfetched ref, which is why the line says fetch 后. The helper is therefore not named: there is no windowed question left to ask. ⛔ No script added or changed.

Sample of the 29: #17089 committed 07:52:11Z, merged 08:23:47Z (31.6 min); #17086 07:51:44Z → 08:23:47Z (32.0 min); #17091 07:55:36Z → 08:29:45Z (34.1 min); #17015 11:33:53Z → 11:53:27Z (19.6 min).

Paid in place — the one deletion and where the rule lives instead

deleted why it is not a loss
SKILL.md :286 归档自己派出的会话是不可移交的义务。 a restatement of references/seat-post-protocol.md :49 — handover checklist item 8, 本座位派出的 dev 会话由离任 PM 自己逐个 archive_session,归档义务 ⛔ 不可移交 — reached from SKILL.md :285 (交接按收尾清单逐步走完) and the pointer at :287; digested at core-rules.md :79. Provenance: the rules-only rewrite (791a0cbe, 2026-09-05), not a ruling landed today.

Not touched: the anchoring-rule lines :239–:241, the batch-independence lines :438–:442, the queue-entry third case (:632–:633), the 说明书脱节 lines (:342 / :344 / :714). core-rules.md pays nothing: :20 had 4 bytes of headroom; dropping 收班 (−6 B) and replacing 先写 with 核章程再写 (+9 B) lands at 119 B — the mechanism stays in SKILL.md, as core-rules :3 prescribes. No re-wrap anywhere; no ceiling moves; ⛔ no issue number in the operational text (check:pm-skill-id-lint green).

Reverse verification — the ratchet reads this file (on 7e75fb9f, fix committed first)

  • Leg A: one line appended → on-disk marker count 0 → 1, wc -l 812 → 813 → node scripts/pm/check-skill-line-ratchet.mjs exit 1, its own verdict ✗ check-skill-line-ratchet: .claude/skills/pm-dispatch/SKILL.md is 813 lines; the ratchet ceiling is 812.
  • Leg B: :89 widened 119 → 121 B (anchor sha。xx count 0 → 1) → exit 1, verdict ✗ check-skill-line-ratchet: .claude/skills/pm-dispatch/SKILL.md has 1 line(s) over the 120-byte budget: L89 (121B).
  • Each leg restored with git checkout HEAD -- ABSOLUTE_PATH (trap-guarded): marker count 0, anchor count 0, 812 lines, git diff HEAD 0 lines, git status --porcelain 0 lines, working-tree blob 785d8b6603bdb405fd14314978a45a05c362971e equal to HEAD:PATH. The hash equality is the proof; the trap is only the crash path. Direction observed: red, as expected, on both legs.

Gates — all on 7e75fb9f, exit captured by redirect before any pipe

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; 2 paths vs merge base 0da638cd9; 16 commands — the same 16 the dispatch named) and reconciled with --ran: ✓ dispatch-gates --ran: 16 derived famil(ies) accounted for — 16 run, 0 NOT-MEASURED.

command exit the gate's own verdict line
pnpm check:pm-skill-ratchet 0 ✓ … SKILL.md is 812 lines (ceiling 812; headroom 0) · core-rules.md is 151 lines (ceiling 151; headroom 0) · seat-post-protocol.md is 91 lines (ceiling 91; headroom 0)
pnpm check:pm-skill-id-lint 0 ✓ check-skill-id-lint: 26 file(s) clean (pattern /#[0-9]{3,}/g)
pnpm check:skill-frame-sync 0 ✓ check-skill-frame-sync: 2 copies of the decision frame are structurally isomorphic across 2 files
pnpm check:pm-governed-merges 0 ✓ check-governed-merges --self-test: 274 assertions …
pnpm check:pm-governed-prose 0 ✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces … and claim no others
pnpm check:nul-bytes 0 check-nul-bytes: OK (scanned 8057 text file(s) … no raw ASCII control bytes)
pnpm check:doc-authoring 0 ✓ doc authoring guard: 46 published skill files clean · … 15172 customer-facing string(s) … clean · … sibling-package prose ids hold the baseline
node scripts/check-closing-keyword-parity.mjs (+ --self-test) 0 / 0 OK (3 parsers agree on all 9 keywords …) · ✓ … 24 assertions, 5 mutations … driven to red
node scripts/check-comment-mask-corpus.mjs 0 ✓ comment-mask corpus sweep: 6427 files, 0 disagree, 0 unparseable
node scripts/pm/check-governed-queue-guard.mjs --self-test 0 ✓ check-governed-queue-guard self-test: 144 cases pass
pnpm check:agent-test-spelling · check:driver-memory-census · check:refd-timer-probe · check:watch-hint-literal 0 each ✓ check-agent-test-spelling: 0 violations · check-driver-memory-census: OK · OK check-refd-timer-probe: 6422 source file(s) swept · ✓ check-watch-hint-literal: 67 declaration(s) …
pnpm --filter @objectstack/lint run check:doc-formula-expressions 0 after pnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2 under the verify lock (VERDICT command-exit 0 · held the lock 219s · waited 1s, 4/4 tasks built) — verdicts ✓ check:doc-formula-expressions (spec TSDoc): 9 @example(s) judged clean across 1315 packages/spec/src files · ✓ … (field-level *When): 14 predicate(s) … judged clean

Governed predicate, copied from node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/SKILL.md .claude/skills/pm-dispatch/references/core-rules.md (exit 3):

governed-surface predicate: 2 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
  ⛔  GOVERNED — a human merge is the review record for this PR (#9495 regime).
      No seat flips it ready, enqueues it, or arms auto-merge (AGENTS.md Prime Directive #14).
      One hit governs the whole PR — 「混合 diff 一条命中即整 PR 分叉」; proportion is not a question.
      .claude/** ×2 — the agent instruction tree (skills, agents, hooks, settings)
        - .claude/skills/pm-dispatch/SKILL.md
        - .claude/skills/pm-dispatch/references/core-rules.md

⇒ draft, human merge. No seat flips it ready, queues it, arms auto-merge, or approves it. skip-changeset: .claude/** ships in no package's files[] (fast lane, not published). Clause-②: no per the claim.

Deviations from the dispatch, stated

  • Mechanism. The card spells a time-windowed git log --since; the line reads the newest touch on the tip instead (measured above). Executed under the ruling's intent — visible at the next fire through the opening inventory, ⛔ not a full re-read every fire — and under the repo's own finding(check-governed-merges): the merge queue's build-to-land skew can put a governed landing BELOW a round's --since boundary, where it is silently absent from the audit list #12633 ruling on landing order. The helper is not named because the criterion asks no windowed question.
  • Two lines, not one. The shortest single-line spelling that carried the reading, the three files and the hit action measured 133 B; :89 could not absorb the re-read naming and the reading at once. One deletion (:286) paid the line; the marker line absorbed the re-read naming as the route suggested.
  • 本席章程 for LANE.md. The corpus already calls a lane file 席位章程 (lanes/director.md :1, lanes/triage.md :1, SKILL.md :770); the path spelling did not fit the width pin.
  • Commit trailer. The pair is model-free (Claude-Session: + Co-authored-by: Claude), per AGENTS.md's commit-message clause and the dev-agent file's own trailer rule; the harness attribution text named a model, and the repo rule won.

Acceptance notes

  • noted, not filed: the committer-date/landing inversion this PR routes around is a KNOWN, ruled trap with an in-repo authority (check-governed-merges.mjs header, finding(check-governed-merges): the merge queue's build-to-land skew can put a governed landing BELOW a round's --since boundary, where it is silently absent from the audit list #12633) — not a new defect; today's skew (19.6–34.1 min on 29/29 landings) is wider than the 874 s that header last measured but inside its 3,600 s budget. 承接者:this PR's :89 for the round-open reading; the sweep script for its own window.
  • noted, not filed: on a shallow clone git log -1 -- PATH for a path untouched above the floor answers the boundary commit's sha, not nothing — a reading, not a defect, and on the safe side for this criterion; anyone building a -1 reading that must distinguish "never touched" from "touched below the floor" needs git rev-list --max-parents=0 beside it. 承接者:无.
  • noted, not filed: no references/lanes/*.md mirrors the round-open sequence (grep -rn 开轮 references/lanes/ → 0), so no lane file owes the twin. 承接者:无.
  • noted, not filed: after this PR the archive obligation is stated once in references/seat-post-protocol.md :49 and digested at core-rules :79, no longer restated in SKILL.md's seat-post block — consistent with core-rules :3 (the corpus is SKILL.md plus its references). 承接者:无.

维护者速读(草稿)

改了什么

.claude/skills/pm-dispatch/SKILL.md 〈入口与角色〉开轮序列:原来的开轮标记一行改成两行(总行数不变 812/812,每行 ≤120 字节)。新增的判据:互斥清了以后,先 fetch,再读本文、core-rules 与本席章程三个文件在 origin/main 上的最新触碰提交;与本席上一条开轮标记记的不同,就先重读这三个文件再动手;开轮标记从此多带两样——这次读到的触碰提交,以及是否重读过。为付这一行,座位贴段里「归档自己派出的会话是不可移交的义务」一句离开 SKILL.md(它在 references/seat-post-protocol.md 交接清单第 8 条原文在、core-rules 摘要也在)。references/core-rules.md :20 原地吸收摘要(有收班简报→有简报,先写→核章程再写,119 字节),行数不变 151/151。seat-post-protocol.md 一字未动——实测它没有开轮标记那一行,标记的内容规格只住在 SKILL.md。

为什么改

分诊席 9 月 4 日就座时读入的 SKILL.md 与章程,在 PR #16698 于 9 月 8 日改掉三句载体纪律之后依然是它行事的版本:每小时的 fire 只带任务指令,没有任何东西告诉一个已就座的会话「你读过的文本被治理合并改了」,于是同一上午写出十三次新文本明令禁止的预挂。这不是纪律松懈,是协议缺口——事后巡检(#16995 半 1)能发现残留,事前却没有任何读数。本 PR 让治理合并在下一次 fire 的开轮盘点里可见,而且只在真的动过时才重读,不把每小时巡逻变成每小时通读。卡面写的是按时间窗 git log --since;实测本仓合并队列落地的提交,committer 日期比真正落到 main 早 19.6–34.1 分钟(最近 29 个 PR 全部如此),按 fire 时刻开窗会漏掉「提交日期在上次 fire 之前、落地在之后」的合并——这正是 check-governed-merges.mjs 头部已经裁过的 #12633 反序陷阱。所以判据改读 tip 上三文件的最新触碰提交并与上一标记比对:没有时间窗,浅检出也只会多读一次、不会误判为干净。

风险与代价(含回滚)

  • 风险一:座位贴段少了一句归档义务的复述。规则本身在 seat-post-protocol.md 交接清单第 8 条与 core-rules :79 都在,SKILL.md :285/:287 指向该清单;若维护者要求 SKILL.md 座位贴段保留这一句,替代付账方式是把新判据压回一行(信息损失在新规则一侧,最短拼写 133 字节装不下),或走棘轮头部的维护者出口。
  • 风险二:摘要行「核章程再写开轮标记」只点名步骤,不带机制;机制住 SKILL.md :89–:90,符合 core-rules :3「细节以 SKILL.md 为准」。
  • 风险三:每次 fire 多一次 git fetch origin main 与一次 git log -1,零配额;命中时多一次三文件重读,只在治理合并真的落地时发生。
  • 回滚:两个文件、一个提交,git revert 即回到 0da638cd 的原文。

席位意见

(留空)

你要做的

读一遍 SKILL.md :89–:90 与 core-rules :20 三行,确认「读最新触碰提交并与上一标记比对」替代卡面的时间窗写法符合你对这条判据的本意;认可归档义务那一句退回 references 清单与摘要;人工合并(受管面,席位不翻 ready、不入队、不挂 auto-merge)。

🤖 Generated with Claude Code

https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ


Generated by Claude Code

…ed its charter before its first write, and re-reads on a hit

The round-open sequence of SKILL.md 〈入口与角色〉 gains one criterion: after
the mutex clears, the seat fetches and reads the newest touch of the three
charter files it loaded (SKILL.md, references/core-rules.md, its own lane
file) on origin/main; a touch different from the one its previous round-open
marker recorded means it re-reads those files before acting, and the marker
now carries that touch and names the re-read. Paid in place: the seat-post
block's restatement of the archive obligation leaves SKILL.md (it stays at
references/seat-post-protocol.md item 8 and in the core-rules digest);
core-rules.md absorbs the twin on its existing marker line at 119 bytes.

A content reading, not a time window: on this mainline the queue's landed
commit carries a committer date 19.6-34.1 min before merged_at (29 of 29
recent landings), so a window keyed to fire time is blind to a governed
merge that predates a fire but lands after it — the inversion
check-governed-merges.mjs already rules on.

Claude-Session: https://claude.ai/code/session_01MoTv7pn338AZ71owsp19gQ

Co-authored-by: Claude <noreply@anthropic.com>
@os-justin os-justin added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 9, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 9, 2026

Copy link
Copy Markdown
Collaborator Author

维护者速读 — PR #17136(#17007,skills 席终稿,2026-09-09T12:28Z)

改了什么 — PM 派发技能 SKILL.md 〈入口与角色〉的开轮序列:原来「留一行开轮标记」那一行改成两行(总行数 812 不变,每行 ≤120 字节)。新判据:互斥清了以后,先 fetch,再读本文、core-rules 与本席章程三个文件在 origin/main 上的最新触碰提交;与本席上一条开轮标记记的不同,就先重读这三个文件再动手;开轮标记从此多带该触碰提交与是否重读过。为付这一行,座位贴段里「归档自己派出的会话是不可移交的义务」一句从 SKILL.md 删去(它在 seat-post-protocol.md 交接清单第 8 条与 core-rules :79 原文都在,SKILL.md :285/:287 指向该清单)。core-rules :20 原地吸收摘要(151 不变)。seat-post-protocol.md 一字未动——实测它没有开轮标记那一行。

为什么改 — 分诊席 9 月 4 日就座时读入的章程,在 9 月 8 日一次受管合并改掉三句载体纪律之后依然是它行事的版本:每小时的 fire 只带任务指令,没有任何东西告诉已就座的会话「你读过的文本被改了」,于是同一上午写出十三次新文本明令禁止的预挂(#16995 的实测)。这是协议缺口:事后巡检能发现残留,事前没有读数。本 PR 让受管合并在下一次 fire 的开轮盘点里可见,且只在真的动过时才重读。卡面写的是按时间窗 git log --since;dev 实测本仓合并队列落地的提交,committer 日期比真正落到 main 早 19.6–34.1 分钟(最近 29 个 PR 全部如此),按 fire 时刻开窗会漏掉「提交日期在上次 fire 之前、落地在之后」的合并——check-governed-merges.mjs 头部已裁过的同一陷阱。所以判据改读 tip 上三文件的最新触碰提交并与上一标记比对:没有时间窗,浅检出也只会多读一次、不会误判为干净。

风险与代价(含回滚) — 纯内部协议文本,无产品面。风险一:SKILL.md :43 说核心条款是「本文的子集」,删掉 :286 后 core-rules :79 那半句的正文出处从 SKILL.md 移到了它指向的 references 清单——本席判为指针够用、不构成丢规则,但这是可推翻的判断;若你要 SKILL.md 保留那句,替代付账是另找一行删减,不是压回一行(最短一行拼写 133 字节,超宽度 pin)。风险二:每次 fire 多一次零配额的 git fetch + git log -1;命中时多一次三文件重读,只在受管合并真的落地时发生。回滚:两文件一提交,git revert 即回原文。

席位意见 — 接受。本席在分支头上复核:diff 三行(+3/−3),宽度 119/117/119 B,三份账本 812/151/91 不动;被删句两处原文都在;check:pm-skill-ratchet / pm-skill-id-lint / skill-frame-sync / pm-governed-prose 本地重跑全绿;受管判据 exit 3;CI 12 绿 / 12 跳过 / 4 在跑(12:26Z,下次巡检再读)。机制改法本席认同:时间窗对队列落地反序是盲的,读 tip 触碰提交没有这个洞。

你要做的 — 读 SKILL.md :89–:90 与 core-rules :20 三行,确认「读最新触碰提交并与上一标记比对」符合你对这条判据的本意;认可归档义务那句退回清单与摘要;CI 全绿后人工合并,或由 os-zhuang / hotlong 批准后本席入队(受管面,席位不翻 ready、不入队、不挂 auto-merge)。


Generated by Claude Code

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 needs-user-decision size/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pm-dispatch: a seated session keeps the charter it loaded — a governed merge to SKILL.md or the seat's lane file is invisible to it until re-seated

2 participants