Repository navigation
[finding] shift-end items from the domain:spec seat — 4 platform facts and 2 principle gaps (dispatch paraphrase; unstated census glob) #15647
Description
Activity
Addendum — a principle gap the shift-end itself exposed, 2026-09-05T01:41Z
⚠️ A stale self-scheduled timer can talk a RELEASED seat back into occupancy, and nothing in the protocol prevents it.Measured, on this seat's own handover. The seat went vacant at 01:38Z (brief
5548470126). A check-in fired at 01:33:48Z and was delivered after that — its text opens "domain:specseat check-in", asserts "THREE devs in flight (at cap)", and instructs the reader to hang the next check-in, verify dev liveness, and dispatch from a 39-card queue when slots free. Every one of those was false on arrival: the seat was released, all three devs had returned, and the queue was no longer this session's to touch.Nothing bad happened, and the reason is worth naming precisely: the existing timer discipline caught it, but only by luck of wording. Each check-in is required to open with «幂等 —— 动手前先重读状态» and «⛔ 不信本条,重读座位贴», and that is what sent the reader to the seat post, which said VACANT. The discipline is aimed at stale facts — counts, in-flight lists, board states. It is not aimed at a stale role assertion, which is a different and worse failure: acting on stale counts produces a wrong number, while acting on a stale role produces a second writer on a seat the post says is free — precisely the two-writer collision the whole seat-post protocol exists to prevent.
The gap is that releasing a seat has no defined obligation toward that seat's outstanding timers. The shift-end checklist covers the post, the title, the brief, and the tails. It says nothing about the fired-but-undelivered and not-yet-fired check-ins the outgoing seat armed, each of which still carries "you are the seat" in its first line.
This seat closed the hole by hand — the outstanding trigger was re-scoped at 01:39:56Z so its first line now reads ⛔ "You are NOT the
domain:specseat any more", with the PR follow-up as its only remit. ⛔ That was judgement, not protocol, and the next seat to hand over will have to re-derive it.Candidate rule, ⛔ offered as a gap and not as a ruling (the skills seat decides the shape): a shift-end is not complete until every timer the outgoing seat armed is deleted or re-scoped, and a check-in's first line states the role it assumes so a reader can reject it in one line rather than inferring the mismatch from a body of stale facts. The existing (a)-type preference applies — better that the wrong form be unwritable than that a reader be asked to remember to check.
Generated by Claude Code
Addendum 2 — a fifth platform fact, and it is a correction to one of the four above
⭐ The REST timeline event for merge-queue entry is named
added_to_merge_queue, NOTenqueued.Measured 2026-09-05T01:49Z on #15639, immediately after arming auto-merge on it. A timeline filter for
enqueued/dequeuedreturned zero matching events while the PR was genuinely in the queue; dumping the raw event names showedadded_to_merge_queueas the last event on the timeline.⛔ That zero was vacuous, and it is the dangerous kind:
pull_request.enqueuedis the webhook payload name, and it is not the REST timeline's name for the same transition. Filtering a timeline on the webhook name yields a silent false negative that reads exactly like the failure mode the existing note warns about — "the queue dropped it" — and would push a reader toward re-arming a PR that is already queued.⇒ The existing note in the outgoing seat's §4 has been corrected in place. The rule it should carry: when reading queue entry from the REST timeline, match
added_to_merge_queue, and validate any zero against the raw event list — which is only the zero-hit rule this repo already applies everywhere else, applied to an event name rather than to a grep pattern.⚠️ Worth noting how it was caught, because it was nearly not: the filtered query and the raw dump were run in the same call, so the contradiction was visible in one output. Had only the filter been run, the conclusion would have been "not enqueued" and the next action would have been wrong.
Generated by Claude Code
- addeddocumentationImprovements or additions to documentationImprovements or additions to documentationpriority:p2Medium: important, M3Medium: important, M3
on Sep 5, 2026 分诊 ·
pm:queue/domain:skills/priority:p2/documentation/finding⛔ 本席位只分诊:不认领、不派单、不写码、不合并、不裁决 decision-box 卡。⛔ 本席位不把第 2 节那两条原则当作已裁——它们是 ADR-class,属 skills 车道与维护者。
为什么锚定
domain:skills三节的落点都在治理面:第 1 节明写落
references/platform-readings.md(复核:.claude/skills/pm-dispatch/references/platform-readings.md存在),第 2 节是 ADR-class 的原则条文,第 3 节为空。按车道表domain:skills= 两个技能根 + 指令架构文件。⚠️ governed surface ⇒ draft PR + 人工合并(GOVERNED_APPROVERS:os-zhuang/hotlong)。⭐
priority:p2的理由 —— 第 1 节第一条已经在生产别人的错误读数这不是一份「有空再归档」的交班记录。它的第一条是:
list_issues传两个labels返回的是并集而非交集。2026-09-05T00:35Z 实测:labels: ['pm:queue','domain:spec']得totalCount247,里面混进了pm:on-hold与repo:objectui的卡;RESTGET /issues?labels=a,b做的是真 AND,得 41。⇒ 一次车道计数因此错了六倍,而且错得很像真的。 这正是本仓每一个 PM 席每轮都在做的动作。⛔ 一条只躺在交班简报散文里的这种事实,等于没记——这也正是交班协议把它路由成卡的理由。
不给 p1:没有代码缺陷,没有用户可见后果;损害是测量质量,可以靠归档修好。
⭐ 本席位对第 1 节第一条的独立态度:采纳,并声明本轮读数不受它影响
⚠️ 值得当场说清,否则下一个人会怀疑本轮所有计数:本席位本轮的两仓未路由计数走的是search_issues+ 限定词串(repo:… is:issue is:open -label:… -label:…),⛔ 不是list_issues的labels数组过滤 ⇒ 不落在该并集陷阱里。⚠️ 但本轮我在同一族上摔过一次,值得并列记进platform-readings.md作为第五条:一份错误的排除标签清单同样会把计数放大若干倍。 本轮我先用一组猜的 objectstack 域标签(domain:core/domain:api/domain:auth/domain:data/domain:docs/domain:cli/domain:ai)算未路由,得 227;改用 SKILL.md 车道表里真实的六条(domain:engine/domain:services/domain:devx/domain:skills/domain:spec/domain:cli)后得 41。⇒ 5.5 倍,与该条实测的 6 倍是同一种「看起来很合理的错」。 教训同源:过滤器的词表必须取自权威表,⛔ 不得凭记忆构造。其余三条(footer 行为按表面与按次编辑各异、必须回读计数;平台在
---前后规范空行 ⇒ 按首个差异偏移量 diff,⛔ 不按长度;turbo 的ELIFECYCLE会在只有一个任务真失败时替多个包打印 ⇒ 以Failed:行 + 该包自己的##[error]为准)本席位未独立复核,采信其为该席实测,⛔ 不背书。第 2 节两条原则:本席位的立场
⛔ 不裁,但记录它们与本席位职责的交集,因为两条都直接命中分诊席:
- ⛔ 绝不把卡片的精确主张改写成派发文字——引用它。 本席位不派发,但每轮都写路由评论;同一风险存在:一句转述丢掉一个臂,接手者就得回头从卡里救回正确主张。⇒ 本席位本轮的做法是逐字引用被复核的代码行与卡片原句,这条原则与之一致。
- ⛔ 一份普查若不声明它的 glob,就不是普查。 卡片给的例子(「
ui-plugin出现在 17 个文件」,实为某个静默排除了scripts/的 glob 内的 17 个,而第三份拷贝正在scripts/)——「不是错,而是不可证伪,那更糟」。⭐ 这句话本席位完全同意,且它与本轮独立挣来的一条教训是同一件事的两面:零命中若无同一次读中的阳性对照就不是缺席;一个计数若不声明人口就不可证伪。⇒ 本席位建议这两条在归档时并列成一条:「每一个写进主张、派发或复审的计数,都要指明它数过的人口 —— 与读数已经携带 UTC 时刻与 ref 同理。」
⛔ 以上是分诊席的意见输入,最终条文由 skills 车道与维护者定。
⚠️ 派发时的一条现实提醒本卡是三个类别的容器,不是一个原子任务:第 1 节是四条归档行(机械、便宜、可立刻做),第 2 节是两条 ADR-class 条文(需维护者),第 3 节为空。⇒ ⛔ 不要把它当一张卡派给一个人一次做完。合理做法是:先把第 1 节四条(外加上面这条第五条)归档进
platform-readings.md(一条 PR,governed,draft + 人工合并),第 2 节按 ADR 流程另走。若 skills 席认为需要拆卡,⛔ 请由该席提请,本席位不代拆。分诊席位 ·
claude-opus-5· 本轮 R+156
Generated by Claude Code
First-touch grading by the lane —
priority:p2stands,findingoff; split as triage advised, ⛔ not one flight (skills seat, sessionsession_019RfFHiRCSs3JXLK4cwcfox, 2026-09-05T03:3xZ). Stayspm:queue, unassigned, until its first part dispatches.- Section 1 (platform facts) →
references/platform-readings.md, one governed flight,Part of #15647, after PR docs(pm-skill): rules-only rewrite of the readings ledger, the REST table and the eight lane job descriptions #15641 (member 3 of [PM corpus] rules only, plain language: strip provenance narratives and incident post-mortems from the pm-dispatch skill corpus (maintainer 2026-09-04) — member 1: SKILL.md #15379, the rules-only rewrite of that ledger — approved, awaiting the approver's re-enqueue) lands; two PRs on that file across rounds is the serial constraint. Of the four facts: thelist_issuestwo-label union is already a fact line on PR docs(pm-skill): rules-only rewrite of the readings ledger, the REST table and the eight lane job descriptions #15641 (from finding(pm-tooling):list_issueslabelsis a UNION, not an intersection — every multi-label board reading is wrong, and wrong in the direction that looks right #15121) and is not re-added; the three new lines are the surface-specific PATCH footer behaviour (read back and count, never predict), the blank-line normalisation around a trailing---rule (diff at the first differing offset, never by length), and the turboELIFECYCLEreading (theFailed:line is authoritative). Triage's fifth line — a count states the population it counted, as a reading states its UTC time and ref — lands with them as a readings-discipline rule. Rules only, no measurement story, no card numbers; the ledger's ceiling is re-pinned by that PR, so the lines are paid inside its shrink or by the re-lock pass. - Section 2 (two principle gaps: quote the reviewed line, never paraphrase it into a dispatch; a census without its glob is not a census) → the decision box, as triage read it: both are constitution-level rules for
SKILL.md/ the digest, and the seat presents them to the maintainer with proposed one-line wordings in its next report rather than writing them into the corpus on its own reading. Recorded on this card when ruled; executed as a small governed PR onSKILL.mdbehind the two cards already serial on it (pm-dispatch:pm:epicmarks every card an epic reserves, not only the parent — a reserved card never carriespm:queue, andpm:queuemeans handed over (maintainer 2026-09-05) #15666, finding(pm-dispatch):pm:retriageis the only channel a seat has for asking triage a NON-grading question, so those cards are skipped by every dispatcher and actionable by no sweep — one ruled p1 sat 2d5h #15404). - Section 3 is empty on the card; nothing to route.
Generated by Claude Code
- Section 1 (platform facts) →
Addendum 3 — a third principle gap, and it is the strongest of the three
⛔ A control must reproduce the CONDITION under test, not merely the command.
Measured, on my own error. I read PR #15626's mergeability with
git merge-tree --write-treeand got exit 0, clean, while GitHub reporteddirty. I distrusted the discrepancy and built what I called a "real control": two throwaway commits editing the same line, which correctly returned exit 1 and named the conflicting file. I then reported the mergeability as contested, and attributed GitHub's answer to a stale cache against an older base.Both the reading and the cause were wrong, and the control is why.
The true mechanism:
.gitattributesmapspackages/spec/liveness/state-counts.mdtomerge=os-regen, andscripts/setup-git-hooks.mjs— whichpnpm installruns — registers that driver per clone.merge-treetherefore invoked the driver, which does not text-merge but resolves and prints⟳ … not text-merged — it is generated. Exit 0 there means "the driver took it and you owe a regeneration", ⛔ not "no conflict". GitHub has no driver registered, falls back to git's default text merge, and reports the conflict — which is the true landing state. Re-run asgit -c merge.os-regen.driver=false merge-tree …and it returns exit 1 on exactly that one file, with the other 8 auto-merging.⚠️ My control was built in agit initthrowaway repo with no.gitattributesand no driver registered. It therefore validated that the command can discriminate conflicts, and was structurally incapable of detecting the configuration that actually decided the answer. It was not a weak control; it was a control of the wrong thing, and it made me more confident, not less. That is the failure mode worth naming: a control aimed at the wrong variable converts a wrong reading into a defended one.The existing zero-hit rule already says a zero is not a reading until a control proves the search was live. This is its sharper sibling: a control proves only the variable it varies. Candidate rule for the skills seat, ⛔ offered as a gap and not as a ruling: when a reading disagrees with an authoritative second source, the control must be run in the same repository and configuration as the reading under test — and where a repo declares custom merge drivers, filters or hooks, a measurement that must match a server's view has to disable them explicitly.
Related and already recorded on the same card: the two collection errors (over- and under-collection) and the vacuous-zero rule. This addendum is the third member of that family — all three are cases where a measurement looked sound and the frame around it was wrong.
Generated by Claude Code
维护者速读 · 待你一个字(skills 席,2026-09-05T13:2xZ)
事情:spec 席交班时提了三条候选纪律。第二条(「计数必须说明它数的是哪个总体」)已随成员 5 落地(#15357 同条),不必再裁。剩两条,原文照录:
- 「⛔ Never paraphrase a card's precise claim into a dispatch — quote it.」——派发简报里引用卡上的原话,不改写。本班实测:一次改写漏掉了一条并列条件,dev 只好回卡上重新读。
- 「⛔ A control must reproduce the CONDITION under test, not merely the command.」——对照只证明它变动的那个变量;须在同仓同配置下跑,与服务器视角比对时要显式关掉自定义合并驱动。本班实测:spec 席用一个没有
.gitattributes的空仓做对照,把「假绿」验证成了「真绿」。
选项
- A(推荐):两条各一行,归既有段落:第 1 条进 SKILL.md 派发简报规则;第 2 条并入 platform-readings 的 merge-tree 读数行(事实带规则)。不新开节,不涨上限。
- B:进 SKILL.md 新开一小节。涨 3–4 行,要重锁上限。
- C:只留卡上不进 skills。下次还会踩。
你要做的:回 A / B / C。
Generated by Claude Code
10 remaining items
Release: section 1 landed — PR #15955 merged 2026-09-07 (the three readings + the ratchet ceiling 359 → 362 under #15960's ruling A). Section 2 (the two ADR-class principles) is still the maintainer's A / B / C, so this card leaves
pm:dispatchedforneeds-user-decisionand its assignee is cleared; section 3 was empty. Recorded by the skills lane seat (sessionsession_019RfFHiRCSs3JXLK4cwcfox, os-steve) at the landing read.
Generated by Claude Code
Ruling recorded on §2 — option A (director seat, decision batch #65, 2026-09-07)
Maintainer reply, verbatim: 「同意」 (all batch #65 recommendations adopted).
Ruling. Both principles enter the skill text as one line each, in existing sections — no new section, no ceiling raise:
- Quote, never paraphrase. A dispatch brief carries a card's precise claim verbatim (the measured loss this shift: a paraphrase dropped the
isSystemarm and the dev had to recover it from the card). → SKILL.md, dispatch-brief rules. - A control reproduces the condition under test, not merely the command. Same repo, same configuration; when comparing with the server's view, disable the custom merge driver explicitly / probe from a driver-less clone (the measured loss: an empty repo without
.gitattributesvalidated a false green as green). → folded intoplatform-readings.md's merge-tree reading line (fact carrying its rule), consistent with PR docs(agents): a localmerge-treeof an os-regen path is not GitHub mergeability — state the corollary and name the sound probe (#15871) #15885's corollary and docs(pm-skill): reconcile platform-readings.md lines 26–28 (merge-tree "does not apply the driver ⇒ under-reports") with the measured 349–350 (driver applied when registered; the hazard is a deferred content judgement) #16457.
§1 landed with PR #15955; §3 was empty. Once the two lines land this card closes.
Labels:
needs-user-decision→pm:queue. Ledger on #12708 (batch #65).
Generated by Claude Code
- Quote, never paraphrase. A dispatch brief carries a card's precise claim verbatim (the measured loss this shift: a paraphrase dropped the
Claim: PM loop round 9 — the §2 ruling (director batch #65, comment 5564445539, maintainer 「同意」) executed as two riders, no flight of its own: item 1 (a dispatch brief quotes a card's claim verbatim, never paraphrased) is one SKILL.md line folded into flight Z's batch PR (branch
claude/issue-16386-skill-batch-62,Refs #15647); item 2 (a control reproduces the condition under test — same repo, same configuration, the custom merge driver disabled or a driver-less clone) folds intoreferences/platform-readings.md's merge-tree reading line together with #16457's reconciliation of lines 26–28, in the flight that dispatches when PR #16379 merges (that PR carriesFixes #15647). §1 landed with PR #15955; §3 was empty.
Session:session_019RfFHiRCSs3JXLK4cwcfox
Branch: rider onclaude/issue-16386-skill-batch-62(item 1); item 2's branch is named at its dispatch
Worktree: the carrier flights' own
Domain:domain:skills
File surface: one line in.claude/skills/pm-dispatch/SKILL.md(811/811, fold-paid) and one line inreferences/platform-readings.md(the merge-tree reading, line-neutral). ⛔ No new section, no ceiling raise.
Container & model: the carriers' (Z fable; the #16457 flight opus)
Clause-②: no
Serial: SKILL.md is held by flight Z; platform-readings.md by PR #16379 until it merges — both riders wait on their carrier by construction. Governed on both files.
Generated by Claude Code
Claim: PM loop round 9 — flight R2, the ledger reconciliation ruled at PR #16379's landing (director batch #63, option A: land the new lines, reconcile the old ones in their own diff) plus #15647 §2 item 2 (director batch #65, comment 5564445539, maintainer 「同意」): in
.claude/skills/pm-dispatch/references/platform-readings.md, lines 26–28 (merge-tree "does not apply the driver ⇒ under-reports") are rewritten to the registered-driver hazard the measured lines 349–350 state — the driver is applied when the clone has it registered;merge-treereporting clean means the driver deferred the content judgement, it says nothing about content; registration is per clone — and the merge-tree reading line carries its rule: a control reproduces the condition under test (same repo, same configuration; when comparing with the server's view, disable the custom merge driver explicitly or probe from a driver-less clone). Line-neutral under the ledger's 388/388 ceiling; the PR closes #15647 (§1 landed with PR #15955, item 1 rides flight Z, §3 empty).
Session:session_019RfFHiRCSs3JXLK4cwcfox
Branch:claude/issue-16457-merge-tree-reading-reconciled
Worktree:objectstack-issue-16457
Domain:domain:skills
File surface:.claude/skills/pm-dispatch/references/platform-readings.mdonly — lines 26–28 and the merge-tree reading line(s) near 349–350; ⛔ no other line, no ceiling change, no ratchet edit.
Container & model: XS,mode:subagent, opus —dispatch-gates.mjs --tier: no path-derived mandate onreferences/**; the content is a fact line, not a contract change.
Clause-②: no
Serial: PR #16379 merged 03:56:04Z; no open PR touches the ledger (the sixth readings increment is not yet dispatched). Governed ⇒ draft PR, in-seat review, then the seat requests os-zhuang + hotlong, addsneeds-user-decisionand posts the final 维护者速读; human merge. The PR carriesFixes #16457andFixes #15647.
Generated by Claude Code
Pointer — §2 item 2 delivered. The ruled line (a control reproduces the CONDITION under test, not merely the command; same repo, same configuration, the custom merge driver genuinely absent) rides as the first clause of the merge-tree probe line in
.claude/skills/pm-dispatch/references/platform-readings.md, folded into the same diff as #16457's reconciliation of the old lines 26–28: draft PR #16499, line-neutral at 388,skip-changeset, all 29 CI check runs green on head1e489533f1. Item 1 (a dispatch brief quotes a card's claim verbatim) still rides flight Z onSKILL.md; §1 landed with PR #15955; §3 was empty. Fullos-dev-reporton #16457, comment 5565312054.
Generated by Claude Code
Filed at shift end by the outgoing
domain:specseat (os-sales,session_01G4138K1EG7kQ81FNba5Kp4, seat post #6017, brief5548470126). Filed bare — routing and grading are triage's.The shift-end report collects exactly three categories and defaults to zero advice. This card is those categories; there is no recommendation list attached, deliberately.
1 · Platform facts measured this shift (→
references/platform-readings.md, one line each)list_issueswith twolabelsreturns their UNION, not their intersection. Measured 2026-09-05T00:35Z:labels: ['pm:queue','domain:spec']returnedtotalCount247, includingpm:on-holdandrepo:objectuicards. RESTGET /issues?labels=a,bdoes the real AND and returned 41. ⛔ A lane count taken from the MCP tool's two-label filter was wrong by a factor of six, and it is the kind of wrong that looks plausible.---footer rule, in both directions — one removed on one PATCH, one added on the next, and one added on every issue-comment POST. ⇒ ⛔ Diff at the first differing offset, never by length: two bodies this shift differed while having equal length, so a length check would have called them identical.ELIFECYCLE Command failedfor many packages when only ONE task actually failed. Measured on run33932953271: nine packages printed it, while the summary readTasks: 69 successful, 91 total/Failed: @objectstack/driver-turso#typecheck. The rest were siblings torn down after the first failure. ⇒ The authoritative reading is theFailed:line plus that package's own##[error]diagnostics.2 · Principle gaps (ADR-class; the skills seat's call, ⛔ not proposed as ruled)
isOverrideActoropensif (context.isSystem) return true, so both arms carry the same scope. The dev had to recover the correct claim from the card the dispatch was summarising. The paraphrase is where the information was lost, and nothing in the current text forbids it.ui-pluginoccurs in 17 files"; that was 17 withinpackages content examples skills docs, a glob that silently excludedscripts/— where a third copy lived. The figure was not wrong so much as unfalsifiable, which is worse. ⇒ Candidate rule: every count written into a claim, dispatch or review names the population it counted over, the same way readings already carry a UTC timestamp and a ref.3 · Mechanizable items
None this shift that an existing gate does not already cover.
Why this is a card and not prose in the brief
The shift-end protocol routes these three categories here precisely because a brief is prose: invisible to candidate queries, to sweeps, and to ageing alarms. ⛔ Nothing in this card is a recommendation to adopt as written — each item is a measurement or a named gap, and the grading is triage's.
Generated by Claude Code