Repository navigation
docs(os-dev, pm-dispatch): a breaking PR runs the full dogfood battery on its final head; the review checklist reads the field before arming - #24917
Conversation
…y on its final head and reports it; the checklist reads the field before arming os-dev.md: the trigger-less "dogfood once on the final head" clause gains its trigger (a `!` title or a `(narrowing)` arm on the Clause-② line, either value), the CI-equivalent command (closure build, then the unsharded `pnpm --filter @objectstack/dogfood test` under the verify lock), the red disposition (own stale fixture fixed in the same PR, anything else reported, never skipped or narrowed), a `dogfood_battery` report field, and the nohup/tail --pid form generalised to every run longer than the foreground cap. Four non-rule lines pay for the four added lines. review-checklist.md reads the field before ready/arm/queue; one pointer line whose rule lives in execution-duties.md pays for it. Both ratchets stay at 404/404 and 77/77. Claude-Session: https://claude.ai/code/session_01M82feuetLzrCiBz41BJb7L Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs: card #24890 body and both comments (the claim and the os-dev-report), PR #24917 body, file list (2 files, +7/−7) and net diff against main, and the check-runs on the head. The branch is one commit off merge-base a8b44d4; head repo = base repo (not a fork); the PR is draft with no auto-merge. The two Check-runs on the head, read 2026-10-21T00:42Z (identical to the first reading on 2026-10-20): all 40 completed, none in progress, none failed. The required five: Lint & Repo Gates success · TypeScript Type Check success · Test Core success · Governed Surface Queue Guard success · Build Core skipped (path-filtered by the ① Derived judgmentsThe diff changes rules text, not a schema: its accept set is what a dev and the landing seat are now told to do and not do. Each changed rule, judged against its readers and the cited code:
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
席位核对ACCEPT ·
Generated by Claude Code |
|
Director record · governed landing recognised · summon #45 continuation 3, Audit window Put to the maintainer in this session's chat beside batch #381, with one line on what changed: a dispatched breaking PR (a |
Fixes #24890
Clause-②: no
Summary
A dispatched breaking change — a
!in the PR title, or a(narrowing)arm on theClause-②:line — now owes the full dogfood battery on its final head, records the result in a newdogfood_batteryreport field, and the review checklist reads that field before ready / auto-merge / queue. The rule is stated once, in.claude/agents/os-dev.md, by extending the clause that already said 「dogfood 只在终 head 跑一次」; the checklist line reads it. No CI, workflow, gate or script changes; two governed.claude/**files, both at their line ceilings before and after (404/404, 77/77).Where the rule landed, and why there (H2, H5)
.claude/agents/os-dev.mdL279 read 「PR 开出后合 main 只因冲突、main 致 CI 红、出队,后只跑 §10 复检;dogfood 只在终 head 跑一次。」 Commitb8762c089introduced that clause together with AGENTS.md §10's "dogfood once, on the final head"; the parent tree's only other "dogfood" in AGENTS.md is §7's full-battery enumeration ("main's hourly and push runs carry the full battery — dogfood, e2e, …"). So the word there names the Dogfood Regression Gate suite, with no trigger and no command. The clause is split off L279 and extended in place into three rule lines (trigger · command · red disposition), so the rule stays stated once. AGENTS.md §10 itself is Tier H and outside the file surface; it keeps its trigger-less sentence, and the role file now carries the trigger.dogfood_batteryis added to the JSON template in the same file (SKILL.md 〈报告契约〉: 「终报 JSON 形状住.claude/agents/os-dev.md,⛔ 不抄第二份」). The report-marker readers inscripts/pm/check-half-states.mjsread only the marker line andopen_questions, so an added key is additive.references/review-checklist.md〈测试与门禁证据〉, whose last block is already the seat's pre-arm read (「翻 ready、挂 auto-merge 或入队前…」, 「每个门禁读数先钉到 PR 当前 head」, 「dev 本地并集钉 head」). The new line sits at its end.landing-operations.md〈入队与落地〉 decides the 条款② gate and the queue mechanics, not the dev-evidence read, and its ratchet is 101/101 — no line goes there, no second home.execution-duties.mdanddispatch-runbook.mdare untouched: the standing clause is unconditional, and os-dev.md 〈标准条款住在这里〉 says unconditional clauses live in the role file, not in the dispatch brief.The trigger spelling (H1 — a correction to the card)
The card writes the declaration as
Clause-②: yes (narrowing); the dispatch brief's hypothesis namesClause-②: no (narrowing). Both are legal spellings, and neither is the trigger by itself:scripts/pm/clause2-line.mjs(the fleet's one reader) closes the value set atyes | noand the arm set at(widening) | (narrowing), and documents the four combinations:yes (narrowing)= "a diff that widens one surface and narrows another. Both facts are true and both are read";no (narrowing)= "NOT a widening, but breaking. This is the whole point of the arm";no (widening)= malformed.scripts/pm/record-recognisers.mjsreads the narrowing face from "Anarrowingarm on ANY of them ⇒ the narrowing face (yes (narrowing)included)".(narrowing)is BREAKING"..changeset/24583-7b3-organizationless-counter-retired.md(yes (narrowing)),.changeset/24765-team-sessions-related-list.mdand.changeset/24813-adopt-orphans-readonly-package.md(no (narrowing)).So the rule is written against the ARM, value-agnostic: 「
Clause-②:带(narrowing)臂(yes/no 皆算)」. The card'syes (narrowing)is one of the two spellings, not the only one; no third spelling was introduced. Related observation (no change here):references/contract-review.mdL12 spells onlyno (narrowing)for the tier derivation while the recogniser reads either value — noted under Acceptance notes.The command (H4)
packages/qa/dogfood/package.json:"test": "vitest run".vitest.config.ts:shard: process.env.OS_TEST_SHARDis the only shard mechanism; two projects (shared-showcase,isolated),OS_CLOUD_URL: 'off'andOS_REGISTRY_LOG: 'warn'set per project inside the config.dogfoodjob (Dogfood Regression Gate (n/3)): builds the closure withpnpm turbo run build --filter=@objectstack/dogfood, then runsOS_TEST_SHARD=n/3 pnpm turbo run test --filter=@objectstack/dogfood— the sametestscript, sharded only by that variable — and grades the log withscripts/check-test-completeness.mjs.a8b44d407inpackages/qa/dogfood:npx vitest list --filesOnlyprints 287 files;find -name '*.test.ts'counts 287 (265 of them*.dogfood.test.ts). The plainpnpm --filter @objectstack/dogfood testwith noOS_TEST_SHARDtherefore runs the whole set — the union of the three CI shards — so the card's command IS the full battery, provided the closure is built first and nothing shards or narrows it. The rule says so: 「命令同 CI:按 ① 建闭包,锁下pnpm --filter @objectstack/dogfood test;⛔ 无OS_TEST_SHARD、无路径」 (① = the closure-build rule in 〈本地验证范围〉).check:pm-dispatch-gatescase to 「超前台上限者(check:pm-dispatch-gates、全量 dogfood):nohup … > LOG 2>&1 &后tail --pid」. The verify lock still applies (rule 1 of 〈资源纪律〉 — the battery is a heavy run), so the backgrounded command is the lock wrapper itself, and the lock's VERDICT line goes into the report field.!/narrowing change, so it owes no run, and a 287-file boot battery on the shared box was not spent on a reading the rule does not depend on.Paying the ratchets (H3)
Both files sat at zero headroom. Every added line is paid by deleting a line that states no rule, each named with where its content still lives. No re-wrap was used as payment; the four added prose lines measure 119 / 120 / 119 / 118 bytes against the 120-byte cap, and the JSON line sits in a fence, which the cap exempts.
.claude/agents/os-dev.md— 404 → 404 (+3 rule lines, +1 JSON template line, −4; L88 and L279 rewritten in place):dist/).premise_still_valid: false, 「报告模板是工具,不是真相」).pnpm check:pm-dispatch-gates亦属check:*:不走锁,…」 → 「超前台上限者(check:pm-dispatch-gates、全量 dogfood):…」 — thecheck:*/ no-lock classification is L87's, by name; the nohup + tail form is generalised rather than stated twice..claude/skills/pm-dispatch/references/review-checklist.md— 77 → 77 (+1, −1):landing-operations.md。」 — the rule lives inexecution-duties.md〈复核〉 「你是记录在案的复核人:对 GitHub 核验,⛔ 不对报告的自述核验」 and the pointer in its preamble 「落地窗口另读landing-operations.md」; the first clause is narrative.Verification
Local union, run after the final commit on
bd8baf269(working tree clean,git status --porcelainempty), every exit code captured before any pipe:dispatch-gates --commandsprinted it)node scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjsnode scripts/pm/check-governed-queue-guard.mjs --self-testnode scripts/pm/check-harness-current.mjs --self-testpnpm --filter @objectstack/lint run check:doc-formula-expressions@objectstack/formula/@objectstack/lintunbuilt — built under the verify lock, VERDICT command-exit 0, held 2s, then re-run)pnpm check:agent-model-declaredpnpm check:agent-test-spellingpnpm check:commit-card-trailerspnpm check:cross-package-test-inputspnpm check:doc-authoringpnpm check:driver-memory-censuspnpm check:gitlink-declaredpnpm check:nul-bytespnpm check:pm-governed-mergespnpm check:pm-skill-id-lintpnpm check:pm-skill-ratchet.claude/agents/os-dev.mdis 404 lines (ceiling 404; headroom 0) ·review-checklist.mdis 77 lines (ceiling 77; headroom 0)pnpm check:refd-timer-probepnpm check:required-contextspnpm check:skill-frame-syncpnpm check:watch-hint-literalnode scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack→ exit 0: "Run reconciliation — 21 derived, 21 run, 0 NOT-MEASURED, 0 UNRUN."node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackwith no paths derived the same 21 commands the dispatch brief listed;--ranreconciliation below.test:repoand every CI family are read from this PR's check-runs by the seat, not re-run here.skip-changesetis declared on this PR:.claude/**is on the fast lane, nothing published moves.Acceptance notes
references/contract-review.mdL12 names onlyClause-②: no (narrowing)as the narrowing spelling that lifts the tier, whilerecord-recognisers.mjslifts it on the arm whatever the value (yes (narrowing)included). Prose narrower than its reader; a governed reference outside this card's surface. Observation — carrier: none.os-dev.md「证据已销毁(评论、卡或 PR 答 404)的复核项记 NOT MEASURED…」 andreview-checklist.md「复核项证据已销毁(…)⇒ 记 NOT MEASURED…」 state one rule on both sides; each has its own reader (dev / seat), so neither was spent here. Observation — carrier: none.vitest list --filesOnlyprints 287 underOS_TEST_SHARD=1/3,2/3and3/3alike: listing is not sharded, onlyrunis. Not a defect; a reason the rule counts files from the plain list.维护者速读(草稿)
改了什么:给开发 agent 的角色文件加了一条规则:PR 标题带
!、或 changeset 的Clause-②:行带(narrowing)臂(yes/no 皆算)的卡,在最终 head 上跑一次全量 dogfood(与 CI 同一条命令、不分片不缩范围),结果写进终报的新字段dogfood_battery;复核清单在翻 ready / 挂 auto-merge / 入队前核这个字段。红的处置照卡:是本改动自己的后果(陈旧 fixture)就同 PR 修,否则照实报,永不跳过或收窄。两个文件各删了等量不含规则的文字抵账,行数不变。为什么改:推送 CI 和合并队列只测被改到的包,带
!的破坏性改动会让没被改到的包里的 dogfood 测试在合并后的整点全量跑才变红(18.x 已发生两次,#24780 与 #24879)。维护者问「如何避免类似的问题」并「立卡」;卡上的「Done when」是裁决,本 PR 照它落地。卡上写的触发拼写yes (narrowing)只是两种合法拼写之一,规则按「臂」写,两种都算。风险与代价(含回滚):纯流程文字,不动 CI、workflow、门禁或脚本。代价是带
!或收窄声明的卡多一次锁下的全量 dogfood 运行(CI 分片跑约 7 分半,本地单机预计超过十分钟),其它卡零成本。回滚 = revert 本 PR 的一个 commit,无数据与发布影响。席位意见:
你要做的:本 PR 只碰
.claude/**,属 Tier S:席位达档复核 PASS 后由席位入队,无需你动手。如对触发条件(按臂算、不按值算)或字段名dogfood_battery有异议,在卡上回一句即可。Generated by Claude Code