agents(os-dev): the one foreground wait shape, step ① on the file surface, one skip-changeset criterion, the anchor's only writer — five cards, one file, 403/403 - #16594
Merged
Conversation
… turn for a background notification Three lines rewritten in place under the 403 ceiling (net 0), all in the clean-exit section: - The two shape lines fold into one: the only shape that is both anti-poll and non-stop is a single foreground blocking wait — run it to completion in the foreground, or block on the job's pid. The old second line named the harness's Monitor as the compliant shape; Monitor's own contract is a background event stream that notifies a RUNNING turn, so a dev who followed that line armed a Monitor and had nothing left to do in the foreground but end the turn. - The freed line carries the report-time rule: a gate that outlives the turn is read from its log (the redirect target) at the moment the report is written, and an unfinished one is recorded NOT MEASURED with the reason. - The "completion notice is itself the stop" tail becomes the explicit fact and prohibition: never end the turn to wait for a Monitor or background notification — a finished turn is a stopped agent, notifications reach only a running turn. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX
…nd is a no-op on an empty closure Two lines rewritten in place, one rider line added, one restatement folded (net 0 at 403): - ① is conditioned on the file surface: the dependency-closure build runs only when the diff touches a package; a diff touching none reports that there is no ①. On a graph-root package (`packages/spec` has no workspace dependency) the closure is empty, so ① runs nothing — gates that read `dist/` need the package itself built first. The old "skipping it produces failures that look like your change broke an import" tail is the false-red lesson the build-before-judging pair already states. - Rider beside the consumer-sweep lines: a retired key's sweep defaults to the retirement playbook's tree-scoped absence pin instead of rebuilding the consumer closure for one absence check. - Folded: "the report does not violate this clause" — the clause's own header already says a turn ends only at report delivery. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX
…s — with the path list as its fast path Two lines rewritten, one measurement line added, one restatement folded (net 0 at 403): - The label has one criterion: nothing published moves, where published is what each package's `files[]` actually ships. The closed path list is demoted to the fast path — those paths publish nothing by definition — and everything else is measured: build, then grep the paths `files[]` lists for the symbol with a positive control; the symbol at zero hits while the control hits means unpublished. - Folded: "waiting for the lock, a gate batch or any slow step blocks in the foreground within the turn; the lock script waits by itself" — stated by the foreground-pipeline rule, the lock's single-call budget line and the one foreground wait shape. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX
…authorable-surface-base, never by a plain build Two lines rewritten in place (net 0 at 403); the prohibition line between them is untouched: - The causal claim narrows to what build-schemas.ts does: the deletion gate's in-tree anchor is written only in `--update-base` mode (`gen:authorable-surface-base`), never on a plain build — measured here by a plain `gen:schema` run that left `git status` empty. - Consequence stated where the old clause pointed the other way: on a plain build a move of that file is a finding, not an artifact. The "never revert it, never hand-edit it, `check:authorable-surface` green is the assertion" line survives verbatim. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX
Collaborator
Author
维护者速读(终稿 · skills 席,2026-09-07T11:5xZ)
Generated by Claude Code |
This was referenced Sep 7, 2026
os-zhuang
approved these changes
Sep 7, 2026
os-zhuang
marked this pull request as ready for review
September 7, 2026 12:08
os-zhuang
enabled auto-merge
September 7, 2026 12:08
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #16332
Fixes #16388
Fixes #13432
Refs #13300
Fixes #16201
Five dev-contract gaps on
.claude/agents/os-dev.md, one commit per card, the 403/403 ceiling held (headroom 0): 11 lines changed, 11 removed, and every added line paid by a same-file fold of a restated line (table below). Rules register, every line at or under 120 bytes, no issue numbers in the file, frontmatter and the report JSON field set untouched. Baseffca0df9, headae47456a. Angle-bracket placeholders are spelled out in this body (PKG) because the platform sanitizer eats them; the file itself keeps the original spelling.What changed, per card
#16332 — never end the turn for a background notification (commit 17fdc53)
Measured carrier. Old line 341 「形状是 Monitor 带 until 条件,或干脆在前台把那套件跑完。」 named the harness's Monitor as the compliant shape, one line under old 340, which said the only compliant shape is 一次前台阻塞等待. Monitor's own tool contract is a background event stream — "you keep working and notifications arrive in the chat" — which notifies a RUNNING turn; a subagent that ends its turn is reported to its parent as finished and nothing wakes it. All seven recorded final messages name a Monitor or background notification as the wake source, so the seven devs followed old 341 rather than violated it. That line was the carrier to rewrite; a line added beside it would have left two instructions in conflict. Choice: 改写载体, net 0 lines.
tail --pid等它退出。」#16388 — step ① exists only when the diff touches a package (commit d543465)
Premise re-measured on current main. The "build spec" step of #16104 section 3 is today's ①,
pnpm --filter 'PKG^...' build, read on a spec card: it builds the package's dependencies, andpackages/spechas no workspace dependency, so on a spec card ① runs nothing and exits 0. The file's own wording decides the option: ① is a prerequisite (build the closure before testing and before any gate that readsdist/) which the dispatch-gates derivation at ④ does not perform, so replacing ① with the derivation would delete a real step. Landed: conditioning on the file surface.pnpm --filter 'PKG^...' build建依赖闭包;不触任何包则报告无 ①。」packages/spec无 workspace 依赖)时 ① 空跑;读dist/的门禁前先 build 本包。」packages/spec/src— condition it on the file surface or replace it by the dispatch-gates derivation (#16104 section 3.1) #16388) at new 190, paid by the fold of old 347: 「退役键的清扫默认走退役 playbook 的 tree-scoped 缺席 pin,不为一次缺席检查重建消费者闭包。」 The instrument is the playbook's own rule (.claude/skills/spec-property-retirement/SKILL.mdline 274: 缺席 pin 一律 tree-scoped,永不 file-scoped).--affectedprescription left this file in PR agents(os-dev): narrow three test obligations — one-time ablation proofs, import-side tests only on public-surface change, no wording pins #16460, so there is no carrier; the empty-closure fact it rests on is what new 129 states.#13432 — one
skip-changesetcriterion (commit 1a1e984)skip-changeset唯一判据:没有已发布的东西移动;已发布 = 各包files[]实际发运的内容。」 This is the E3 rider sentence (comment 5565008895 on os-dev: the "build spec" verification step passes without measuring anything offpackages/spec/src— condition it on the file surface or replace it by the dispatch-gates derivation (#16104 section 3.1) #16388) landing once; the review checklist carries the full form.docs/adr/**·.claude/**·scripts/pm/**· 仓根配置 · 私有包 · 注释,不发布。」files[]所列路径找符号,带正控;符号零命中、正控命中 ⇒ 不发布。」 Paid by the fold of old 105.#13300 — not planned, with the reading (no commit)
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack packages/spec/src/data/filter.zod.tsonffca0df9lists 10 of the 14 entries incheck-generated.ts'sNO_GENERATORledger — the auditscheck:generatedprints as deliberately not run — each as its own command:check:empty-state,check:yaml-examples,check:variant-docs,check:duration-unit-keys,check:exported-any,check:dual-source-exports,check:entry-nameability,check:browser-reachable-entries,check:objectui-pin-citations,check:llms-txt(the one that bit in the origin measurement).check:generateditself is never a derived command:--residueplaces it in the residue, and the tool's own header (line 7680) records why — its path literals are package-root-anchored — and that this is filed separately. So the trigger the card's obligation names ("whencheck:generatedis in the derived family") cannot occur under the derivation the contract already prescribes at ④ with--ranreconciliation: disposition 1 has landed for the audits, and the residue names the rest. Not planned; the seat records this reading on the card. Residue worth knowing: for a schema pathcheck:skill-examples,check:template-manifests,check:error-code-provenanceandcheck:scripts-typecheckare residue entries rather than derived commands — the first two parse against live spec schemas.#16201 — the anchor's only writer (commit ae47456)
Measured:
pnpm --filter @objectstack/spec gen:schemaunder the verify lock (「VERDICT command-exit 0」) leftgit status --porcelainempty —authorable-surface.base.jsondid not move — and the generator's own output names re-anchoring as "a deliberate act with its own reviewed diff —pnpm --filter @objectstack/spec gen:authorable-surface-base". The write inpackages/spec/scripts/build-schemas.tssits underif (UPDATE_BASE)(line 2192). Wording converged on the doc comment PR #16199 gives that file: the writer is the generator "only in its--update-basemode (gen:authorable-surface-base), never on a plain build".packages/spec:authorable-surface.base.json只由gen:authorable-surface-base写,普通构建从不写。」check:authorable-surface绿。」baseRev允许滞后,一行信息不是错误;普通构建下该文件动了是 finding,不是产物。」Fold table — every added line paid by a deleted restatement
Decision analysis — the two open choices, on the four axes
#16332: rewrite the carrier, or add a line beside it.
#16388: condition ① on the file surface, or replace it with the derivation.
dist/-reading gate need (the closure build); the derivation lists gates and builds nothing, so replacing would remove a real step — the vacuous case is exactly the empty closure, which the condition now names.dist/-reading gate would otherwise read a stale build.Verification (head
ae47456a; exit codes captured before any pipe; the gate's own verdict line quoted)pnpm check:pm-skill-ratchetexit 0 — 「✓ check-skill-line-ratchet: .claude/agents/os-dev.md is 403 lines (ceiling 403; headroom 0).」 and 「widest table row is 0 bytes (pin 0; headroom 0).」pnpm check:pm-skill-id-lintexit 0 — 「✓ check-skill-id-lint self-test: 14 cases pass.」pnpm check:skill-frame-syncexit 0 — 「13 cases pass, plus 5 dispatch-gates declaration cases and 3 scan-population cases」pnpm check:nul-bytesexit 0 ·pnpm check:agent-model-declaredexit 0 (「os-dev.md → opus」) ·pnpm check:pm-governed-proseexit 0 ·pnpm check:pm-half-statesexit 0 (「2636 cases pass」).node scripts/pm/check-governed-merges.mjs --test .claude/agents/os-dev.mdexit 3 GOVERNED — 「.claude/** ×1 — the agent instruction tree」.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack(no paths; the changed set from the merge base) → 16 commands, every one run;--ran→ 「16 derived, 16 run, 0 NOT-MEASURED, 0 UNRUN」.pnpm --filter @objectstack/lint run check:doc-formula-expressionsfirst answered 「PREREQUISITE NOT MET」 (exit 3, NOT MEASURED, not red) until@objectstack/formulaand@objectstack/lintwere built under the lock; after both were built under the lock (turbo run build, 「Tasks: 4 successful, 4 total」) the rerun exits 0 — 「✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 434 files / 1371 TS blocks judged clean by @objectstack/formula.」pnpm lintunder the verify lock onae47456a: 「VERDICT command-exit 0 · held the lock 85s」. (eslint'sfiles:populations are all TS/JS globs; the changed.mdreports 「File ignored because no matching configuration was supplied」, so the full run is the reading, not a narrowing.)merge.os-regen.driverunset, config exit 1)merge-tree --write-tree --name-only ffca0df9 ae47456areturns a clean tree with no conflicting path; all 23 open PR heads (listed after the push) diff 0 lines on this file against their merge base withorigin/main.grep -naPover the drafts exits 1)..claude/**publishes nothing from any package;skip-changesetapplied at open.验收备注 (observations, not filed)
dispatch-gates.mjs's header (line 7680) already recordscheck:generated's under-match forpackages/spec/srcas filed separately;check:skill-examplesandcheck:template-manifestssit in--residuefor a spec zod path although both parse against live spec schemas. 承接者: the card that header points at (its number is not stated there).gen:schemamid-merge; the anchor rolls back to the old fork point) keep a mechanism sentence that--update-basenow refuses on its own (build-schemas.tsline 203); the prescription — commit the merge, thenscripts/pm/os-regen-merge.sh— is still right and matches AGENTS.md's merge-driver section. Outside the card, left untouched. 承接者:无.packages/spec/src— condition it on the file surface or replace it by the dispatch-gates derivation (#16104 section 3.1) #16388 above).维护者速读(草稿)
改了什么 — 开发 agent 契约
.claude/agents/os-dev.md改了 11 行,总行数不变(403/403)。四张卡各落一条规则:① 永不结束回合去等后台通知;跑得比回合长的门禁在写报告那一刻读它的日志,没跑完的记 NOT MEASURED 并写原因。② 「先 build 依赖闭包」这一步只在 diff 触到某个包时才存在;spec 这种没有 workspace 依赖的包,这一步是空跑,读 dist 的门禁前要先 build 本包。③skip-changeset只剩一条判据 —— 有没有已发布的东西移动(已发布 = 各包files[]实际发运的内容);路径清单降为快速通道,并写明怎么测(构建后 grep,带正控)。④ 纠正一句错话:authorable-surface.base.json只由gen:authorable-surface-base写,普通构建从不写;普通构建下它动了是 finding。第五张卡(#13300)实测后不需要改文件:派生工具已经把那些审计列成独立命令。为什么改 — 24 小时内七个 dev 因为同一句话停摆,每次都要 PM 手动唤醒;spec 卡上的「构建」步骤什么都没测却读成绿;changeset 标签有两条互相矛盾的判据,dev 每次都得来问;锚点文件那句话把「删除门禁的锚」说成「预期会动的产物」,方向正好反了。
风险与代价(含回滚) — 只改契约文本,不碰代码与发布物,
skip-changeset。风险在措辞:若新句子仍被误读,代价是再一轮 PM 唤醒,不影响运行时。回滚 = revert 本 PR 四个 commit 中的任一个,它们互相独立。席位意见 — (留空)
你要做的 — 受管面(
.claude/**),需要你人工合并;读一遍 fold 表,确认被删的五句话的内容都还在。Generated by Claude Code