Repository navigation
Read the os-dev report open_questions array, and give a residual question its own card - #17091
Conversation
…l row The dev report contract carries an `open_questions` array and nothing read it. The decision inbox is the `needs-user-decision` label, so a question written into a report comment's JSON body was filed, well-formed and unreachable. H52 reads the newest `os-dev-report` on every listed open card whose thread is already in hand and reports a non-empty `open_questions` on a card carrying no `needs-user-decision`. It buys no request (H47's shape) and writes nothing. The residual-question rule lands beside it in the dispatch runbook: questions raised while executing a ruling go on a new card linking the ruled one, never back onto the ruled card as a re-hung label. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HxLw5aKDPR5RJgyUR7Exkd
Both corrections came out of the first census rather than from review. The reader required a ```json fence. Of the 13 open cards whose newest report it could not parse, most carried the object with no fence at all, and one was a report whose own `status` was `needs_decision` — the exact card the row exists to find, reading as UNJUDGED because of a markdown fence. A fence is not part of the report contract. The open board's unparseable tail falls 13 -> 4. The marker was case-sensitive on ACCEPT_VERDICT_MARKER's reasoning. The census found five shipped reports opening `OS-DEV-REPORT`, so CLAIM_COMMENT_MARKER's precedent applies instead and the flag is a measurement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HxLw5aKDPR5RJgyUR7Exkd
…en-questions-reader
…spatch Both were written before the census widened the row and were left behind by it. `SWEEP_COUNT_KEYS` described `openQuestionCandidates` as OPEN `pm:dispatched` cards and the self-test block headed its population cases the same way, while `h52SpeaksAbout` takes every open card on purpose — the census measured 52 of 63 live carriers outside `pm:dispatched`, and the cases directly under that heading assert exactly that. Claude-Session: https://claude.ai/code/session_01HxLw5aKDPR5RJgyUR7Exkd Co-authored-by: Claude <noreply@anthropic.com>
The rule sentence needed two lines in a file at its ceiling, and the previous round bought them by joining two unrelated line pairs — one of them a pure re-wrap that deleted nothing. Line merging is not currency for the ratchet. Both merges are reverted and the two lines are paid for inside the section that gains them, by deleting content `SKILL.md` already carries: the two-carriers line (「一条裁决已记录的权威载体 = …摘要台账」 plus 「卡上裁决评论是详注」) and the 「时间戳加回忆不是引用」 clause, whose one surviving increment — an id can be carried and still not be retrievable — folds into the line above it, whose subject it already is. The runbook holds increments over `SKILL.md`, so a line `SKILL.md` states is not one. 241 lines before and after, ceiling 241, widest new line 110 bytes. Claude-Session: https://claude.ai/code/session_01HxLw5aKDPR5RJgyUR7Exkd Co-authored-by: Claude <noreply@anthropic.com>
维护者速读 — PR #17091(#16662,skills 席终稿,2026-09-09T07:40Z)改了什么 — 半状态巡查脚本新增一行 report-only 的 H52:一张 open 卡上最新的 为什么改 — dev 报告里写在 风险与代价(含回滚) — 脚本只报不写;回滚 = revert 本 PR。已知的刻意过报:只在散文里答过的问题会一直报,直到后续报告清空数组或标签落地(docblock 与自测钉住了这个选择)。这条飞行的第一位 dev 03:29Z 限流死亡,续飞的 dev 保留了它的全部产出,并把 runbook 的付账从「合并两对不相干的行」改回「删两句主文件已有的复述」。 席位意见 — 接受。本席在分支头上复核:自测 2856 例通过;消融使 25 例转红、阴性对照保持绿;runbook 241/241、被删两句在 SKILL.md :732–734 都在;id-lint、frame-sync 绿;控制字节 0; 你要做的 — 合并;或由 os-zhuang / hotlong 批准,本席随即入队。另有一项交给下一任席位、不需要你动:那 65 张卡 / 126 个问题的路由。 Generated by Claude Code |
Fixes #16662
The
os-devreport contract carries anopen_questionsarray. A dev that hits a question onlythe maintainer can answer writes it there, with options and a recommendation, exactly as the
contract asks. Nothing read that array. The decision inbox is the
needs-user-decisionlabel, and a question inside a report comment's JSON body carries no label — so the question was
filed, well-formed and unreachable: not in the inbox, not in any candidate query, not in any
staleness alert, while the card kept a
pm:*state label that reads as work in flight on everyboard.
This PR ships the reader, and the rule that lets a question outlive the card it was raised
on. ⛔ It does not touch the report contract: the array is the right place for the question.
What landed
scripts/pm/check-half-states.mjs— H52, one report-only patrol row. For the NEWESTos-dev-reporton an open card whose complete comment thread is already in hand, a non-emptyopen_questionson a card carrying noneeds-user-decisionis a row naming the report, itsstamp, how many questions it holds and the first of them. Registered in
HALF_STATE_FAMILY_BANDas astallrow, with a three-number coverage clause(
openQuestionCandidates/openQuestionJudged/openQuestionUnparsed) rendered on everyrun.
.claude/skills/pm-dispatch/references/dispatch-runbook.md— the residual-question rule,two lines in
## 落卡与裁决记录细则: questions raised while executing a ruling go on a newcard linking the ruled one, ⛔ never back onto the ruled card as a re-hung label, plus the two
reasons.
The row buys nothing:
commentCache.getnever fetches, so it is judged at the FOOT of thesweep, after every pass that fills the cache — and above all after H50's completing walk, which
lands the LAST page for every open
pm:dispatchedcard. That placement is load-bearing: areport is posted at the END of a dev round, so on a long thread a first page would miss it, and
miss it silently. A card whose thread nobody bought, or whose thread is in hand but not
complete, or whose newest report payload does not parse, is UNJUDGED — counted apart, never
folded into clean.
Why the rule sentence sits in the runbook and not in
state-machine.mdThe claim named
state-machine.mdordecision-analysis.md, "whichever already carries theruled-card rule". Re-checked on this tree: neither carries it —
grepfor 呈递 / 已裁卡 overSKILL.mdand everyreferences/*.mdreturns the rule nowhere, so that half of the claim'spremise does not hold and the file had to be chosen on a reader instead.
The runbook has a NAMED reader at exactly the moment the rule fires.
SKILL.md:730, inside落卡/升级流程 ③ (「答复/代裁到手,四件同笔」 — i.e. a ruling has arrived and is being executed),
points at
references/dispatch-runbook.mdfor that step's cases and its two mechanical flags.The section the sentence landed in,
## 落卡与裁决记录细则, already carries the decision-boxneighbours it belongs beside (出决策箱须引可取回的裁决 id; the read-side 决策复读 rule) and the two
report-only flags that this very patrol owns.
state-machine.mdis the per-state line contractfor the six labels and has no ruling-execution step;
decision-analysis.mdscopes itself to howa decision analysis is WRITTEN. So the sentence stays where the previous round put it.
Acceptance notes
The triage ruling's six-item 验收口径 (comment 5578847560), verbatim, each with its evidence:
Posted as comment 5597739635 on the card. 587 open cards read; 65 carriers holding 126
questions, none of them in the inbox. The full list is in that comment, card by card.
Same comment, section 2: 2,107 closed
pm:dispatchedcards read — 255 carriers holding 436questions, i.e. the closed half is larger than the open half and permanently out of reach of
any open query. The standing row is open-only by design and says so in its own coverage clause;
that gap is what the residual-question rule exists to cover.
Red: all 65 census carriers make the shipped predicate return a finding sentence, and a LIVE
sweep on this tree filed 60 rows, every one independently confirmed by the census (zero
uncorroborated rows). Green: the one open carrier that DOES carry
needs-user-decision(#17041,2 questions) is silent; an earlier census at 03:07Z, with a fuller inbox, read 9 labelled
carriers and 0 reported.
Two lines in the runbook, both reasons stated:
.claude/agents/os-dev.mdis not in the diff — two files changed, nothing else.Both are pinned self-test cases (
H52 negative control: …) AND measured live: 22 open reportsand 615 closed reports carry an EMPTY
open_questionsand produce zero rows between them; thelabelled carrier produces none either. A third state is kept apart from both: a payload that
does not PARSE is UNJUDGED, never an empty array (3 open, 126 closed).
The successor's first item — #13973's three residuals are ANSWERED, not lost
The triage ruling asked whoever picked this card up to establish where the three
open_questionsof comment 5573240453 went, because a third instance would mean the defect hadstruck again while this card was open. Comment 5577121900 (2026-09-08T00:17:34Z, director
seat, decision batch #80) records the maintainer's reply and rules all three A, A, A — leave
as landed, item by item. ⇒ No third instance.
report,
isTriageRulingComment's anchor set does not match it, and teaching the row torecognise a ruling would be a change to H33 rather than an addition beside it. A card answered
only in prose keeps firing until a later report writes
open_questions: []or the label lands —both repairs the board needs anyway, and the row's sentence asks for whichever applies.
Verification
Gate union — 42 derived, 42 run, 42 green. Derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no pathspassed; the script took its own change set from the merge base) at HEAD
75b5ec0a7, exit codescaptured by redirect before any pipe, then reconciled:
Named in the dispatch and green:
check:pm-half-states(self-test, 2856 cases pass),check:pm-skill-ratchet,check:pm-skill-id-lint,check:skill-frame-sync,check:pm-governed-merges,check:nul-bytes, pluscheck:pm-dispatch-gates.Ablation — the row can fail.
h52OpenQuestionsUnroutedwas made to returnnullunconditionally, the mutation proved on disk before the run (HEAD blob
29babc062217aed2c77c658fcb1953c24cf1d5c0, mutated blob55dac2ba3afeaf1f3e8484cda5c08f2a54d4b552, marker occurrences 0 → 1), and the self-test wentred: 25 of 2856 cases failed, led by
✗ H52 fires: a non-empty open_questions on a card with no needs-user-decision. Restored withgit checkout HEAD -- scripts/pm/check-half-states.mjsunder anEXIT INT TERMtrap and provedbyte-identical (worktree blob back to
29babc06…,git diff HEADempty, marker occurrences 0),then the self-test re-run green at 2856/2856. Negative-control cases stay green under the
mutation, which is what makes the 25 the row's own.
Line budget (governed text).
.claude/skills/pm-dispatch/references/dispatch-runbook.md241 → 241 lines (ceiling 241, headroom 0), widest new line 110 bytes against the 120-byte
cap. ⛔ No ceiling raise and ⛔ no re-wrap: the two lines are paid for inside the section that
gains them, by deleting content
SKILL.mdalready carries — the two-carriers line(「一条裁决已记录的权威载体 = …摘要台账」 + 「卡上裁决评论是详注」, SKILL.md) and the
「时间戳加回忆不是引用」 clause (SKILL.md), whose one surviving increment — an id can be carried
and still not be retrievable — folds into the line above it, whose subject it already is. The
runbook's own header scopes it to increments over
SKILL.md, so a lineSKILL.mdstates isnot one. An earlier draft of this branch bought the same two lines by joining two unrelated
line pairs; that is a re-wrap, it deletes nothing, and it was reverted. No
skills/**(published package) file is in the diff, so that surface's two-reading budget does not apply.
Governed surface.
⇒ Draft, human merge. ⛔ Not readied, ⛔ not enqueued, ⛔ no auto-merge.
skip-changeset: nothingpublished moves —
scripts/pm/**and.claude/**are both off every package'sfiles[].维护者速读(草稿)
改了什么。 给"开发 agent 报告里那个写给您的问题"装了一个读者。过去 dev 把只有您能拍板的问题
写进报告的
open_questions数组里,而收件箱只认needs-user-decision这枚标签 —— 数组里的问题没有标签,于是它被规规矩矩地提交、然后谁也看不见。现在半状态巡查每轮会把这类卡列出来。同时在 PM
协议里写死一条规则:执行您的裁决过程中新冒出来的问题,另开一张卡并回链已裁的那张,⛔ 不许把标签重新
挂回已裁卡。
为什么改。 这不是"整洁"问题,是"您应得的一个决定从未到达"。本次普查第一次量出了洞的大小:
65 张开放卡上压着 126 个问题,一个都不在收件箱里;已关闭的卡上还有 255 张、436 个问题 ——
那一半连查询都查不到,因为卡一关,标签就跟着走了。已知最长的一例压了 17 天,而那张卡对应的部署
曾整整一天返回 503。这类失效不会自曝:dev 尽了责、报告通过校验、PM 接收了 —— 三个成功信号,一个
错误答案。
风险与代价(含回滚)。 代价接近零:这一行不额外发一次请求,只读别的巡查项已经买回来的评论
线程,读不到就明说"未判定",⛔ 不冒充干净;它也不写任何标签、不阻塞任何东西,只是巡查报告里
多一段清单。有一个刻意保留的过度报告:如果某个问题您已在卡上用中文回答、而没有人再补一份报告
或挂标签,这张卡会继续被列出来 —— 这是有意选的方向,因为它要求的两种收尾(该进收件箱的进收件箱,
已答的记一笔)都是看板本来就欠的。回滚成本:删掉这一行即可,零迁移、零数据形状变更;协议那两行同理。
席位意见。 (留空,待席位定稿)
你要做的。 只有一个动作:人工合并这个 PR(受管面,⛔ 不入队、⛔ 不自动合并)。合并之后,
普查列出的那 65 张卡会由 PM 席位按新规则逐张归位 —— 不需要您逐张看。
🤖 Generated with Claude Code
https://claude.ai/code/session_01HxLw5aKDPR5RJgyUR7Exkd
Generated by Claude Code