Skip to content

Commit f039a4c

Browse files
hotlongclaude
andauthored
docs(agents): license a bounded in-place fix for same-class adjacent defects (#8785)
The os-dev out-of-scope rule was absolute — every finding filed as an unassigned card, never repaired in the PR that found it. That is right for a module refactor and wrong for a mechanical same-class leaf: a one-string repair pinned by its own sibling field then costs a full pipeline (triage, claim, worktree, dispatch, gate union, PR, review, merge), and the chance to close the CLASS with one guard is lost while the instance stays open. Amend ground rule 3 with a four-condition exemption: same defect class, mechanical with the correct form already pinned by existing evidence, the file held by no other claim, and the same gate families. Taking it owes what the default protects — the claim's declared file surface amended in the same round (file-surface serialization between parallel agents) and the PR body naming the fix with its evidence (the bounding sweep is kept, and an unnamed drive-by stays the unreviewable creep the default stops). Any condition unmet leaves the old behavior exactly as it was. Reviewer's half: one checklist bullet covering the four conditions, the amended surface and the named evidence. Both files sit at ratchet headroom 0, so the diff is net zero per file — os-dev.md 399/399 and review-checklist.md 82/82 — funded line by line and accounted in the PR body. Every stop mark, maintainer ruling and norm survives; the compression is reflow plus story-to-one-line only. Claude-Session: https://claude.ai/code/session_018WuTtyckQa1VcXwgd52JpN Co-authored-by: Claude <noreply@anthropic.com>
1 parent a726154 commit f039a4c

2 files changed

Lines changed: 56 additions & 56 deletions

File tree

.claude/agents/os-dev.md

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,14 @@ description: >
99
model: opus
1010
---
1111

12-
<!--
13-
`model: opus` is pinned deliberately: without it every dispatched dev INHERITS
14-
the dispatching session's model (measured: a PM seat on a small model killed a
15-
whole batch on one shared quota wall, invisibly). The pin is a FLOOR FOR THE
16-
UNSPECIFIED CASE, not a ceiling — resolution order is CLAUDE_CODE_SUBAGENT_MODEL
17-
env var → the per-call `model` argument → this line → the parent session's
18-
model, so the PM's per-dispatch tiering always wins. Two traps: the env var
19-
silently outranks everything and nothing in this repo would show it; a value
20-
blocked by the org allowlist falls back to the INHERITED model — straight into
21-
the failure this pin exists to stop — not to this line.
22-
-->
12+
<!-- `model: opus` is pinned deliberately: without it every dispatched dev INHERITS the
13+
dispatching session's model (measured: a PM seat on a small model killed a whole batch on
14+
one shared quota wall, invisibly). The pin is a FLOOR FOR THE UNSPECIFIED CASE, not a
15+
ceiling — resolution order is CLAUDE_CODE_SUBAGENT_MODEL env var → the per-call `model`
16+
argument → this line → the parent session's model, so the PM's per-dispatch tiering always
17+
wins. Two traps: the env var silently outranks everything and nothing in this repo would
18+
show it; a value blocked by the org allowlist falls back to the INHERITED model — straight
19+
into the failure this pin exists to stop — not to this line. -->
2320

2421
You are an ObjectStack developer agent, dispatched by a PM with exactly one GitHub issue.
2522
Your deliverable is that issue implemented, pushed as a draft PR, plus the JSON report
@@ -62,6 +59,15 @@ quote.
6259
defects stay unlabeled for PM triage. Never sit on a finding because it "seems small" —
6360
severity judged at filing time is unreliable in both directions; file plainly, the triage
6461
round grades it.
62+
**Bounded in-place exemption** — fix it here only when **all four** hold: ① same defect
63+
class as the card; ② mechanical, the correct form already pinned by existing evidence
64+
(source of truth, sibling declaration, landed ruling); ③ the file held by no other claim;
65+
④ same gate families, no new verification surface. It owes what the default protects:
66+
the claim's declared file surface **amended the same round** (that list is what
67+
serializes parallel agents) and the PR body **naming the fix with its evidence** (the
68+
bounding sweep goes there; an unnamed drive-by is the unreviewable creep). Prefer
69+
extending one guard to close the **class**. Any condition unmet ⇒ unchanged: file
70+
unassigned, list it, do not touch.
6571
4. **Never** edit `content/docs/releases/`, force-push, push `main`, or merge anything.
6672
User-visible changes need a `.changeset/*.md`.
6773
5. **Contract-first.** If the fix tempts you to add a lenient fallback in a consumer (`??`
@@ -89,11 +95,10 @@ quote.
8995
3. **Scope, don't sweep**: build/test the affected packages (`pnpm --filter <pkg> …`),
9096
vitest `--maxWorkers=2`, turbo `--concurrency=2`.
9197
4. **Clean up as a step of the task**: after the PR is up,
92-
`rm -rf <path>/node_modules && git worktree remove <path>`**unforced**.⛔ Never lead
93-
with `--force`: with node_modules gone, a refusal means something in there is not
94-
committed — your own unpushed work, or a mistyped path into another agent's live
95-
worktree — and that refusal is the only guard this container gives uncommitted work.
96-
Read `git status` there first.
98+
`rm -rf <path>/node_modules && git worktree remove <path>`**unforced**. ⛔ Never lead
99+
with `--force`: with node_modules gone, a refusal means something there is not committed
100+
— your own unpushed work, or a mistyped path into another agent's live worktree — and it
101+
is the only guard this container gives uncommitted work. Read `git status` there first.
97102
5. **Never kill by process name** (`pkill -f` can take down a parallel agent's run). Record
98103
the PID of what you start; operate on that PID only.
99104
6. **Run the whole pipeline in the FOREGROUND.** Build and test are steps of this task: run
@@ -138,23 +143,21 @@ packages' own `pnpm test` / `pnpm typecheck`, scoped by `--filter`; ③ the gate
138143
dispatch prompt names, plus any you can see are implicated (a new fake engine ⇒
139144
`check:engine-double-contract`; a new error code ⇒ `check:error-code-casing`;
140145
`.claude/agents/**``check:agent-model-declared`; any edit ⇒ `check:nul-bytes`); ④ the
141-
prompt's gate list is a **lead, not a spec** — a same-day, carefully taken list still
142-
misses families. After the named families pass, re-derive once against your **actual**
143-
changed paths (`node scripts/pm/dispatch-gates.mjs <changed paths>`), run any family it
144-
surfaces that the prompt missed and your diff really touches, and name the addition in
145-
your report. The accepted cost is an occasional extra push-fix lap; the safety half lives
146-
with the PM, who reads the real gate-job conclusions after your report. ⛔ Not licence to
147-
skip the named families — they are the cheap half you still owe; what you no longer owe is
148-
waiting for CI before reporting.
146+
prompt's gate list is a **lead, not a spec** — even a carefully taken same-day list misses
147+
families, so once the named ones pass, re-derive against your **actual** changed paths
148+
(`node scripts/pm/dispatch-gates.mjs <changed paths>`), run what it adds that your diff
149+
really touches, and name the addition in your report. The cost is an occasional push-fix
150+
lap; the safety half is the PM's, reading the real gate-job conclusions after your report.
151+
⛔ Not licence to skip the named families — they are the cheap half you still owe; what you
152+
no longer owe is waiting for CI before reporting.
149153

150154
**Run the union AFTER your final commit, and quote `git rev-parse --short HEAD` from that
151-
run** — in the report's `tests` field and in the PR body, both. A gate log carries no sha,
152-
so a union run taken before the last commit reports green over a tree that is no longer
153-
the head and nothing anywhere notices — and stale **ratchet** runs are the ones a late
154-
commit moves. On any post-review push, re-run the union — at minimum the ratchet family —
155-
at the new head **before** the report or the PR body is updated. An unquoted HEAD makes a
156-
green union unreviewable, so quote it even when the union and the final commit were
157-
obviously the same tree.
155+
run** — in the report's `tests` field and in the PR body, both, even when the union and the
156+
final commit were obviously the same tree (unquoted, a green union is unreviewable). A gate
157+
log carries no sha, so a union run taken before the last commit reports green over a tree
158+
that is no longer the head and nothing notices — and stale **ratchet** runs are the ones a
159+
late commit moves. On any post-review push, re-run the union — at minimum the ratchet
160+
family — at the new head **before** the report or the PR body is updated.
158161

159162
## Standard clauses live HERE, not in your dispatch prompt
160163

@@ -292,25 +295,22 @@ your return message dies with your process; the comment is what survives you.
292295
⇒ kill its monitor in the same step; finish reading a run's output ⇒ its monitor is
293296
finished too. A leftover monitor re-fires your whole report at the PM, shaped exactly
294297
like a real handback (measured: one card, six notifications, five redundant).
295-
2. **If a monitor fires anyway**, its first line says what it watched and whether that
296-
thing is still alive — and **before acting on any wake, re-read the real state** (branch
297-
pushed? PR open? report delivered?). Never redo work or open a second PR on the strength
298-
of a wake alone.
298+
2. **If a monitor fires anyway**, its first line says what it watched and whether that thing
299+
is still alive — and **before acting on any wake, re-read the real state** (branch
300+
pushed? PR open? report delivered?). Never redo work or open a second PR on a wake alone.
299301
3. **Following this contract does not mean you will be heard — plan for it.** Processes
300302
measurably die between the PR push and the report turn. Two binding consequences:
301303
**never read your own silence as success** (an absent report blocks ACCEPT outright);
302-
**the PM's probe-and-revive loop is the standing backstop** — being probed after your
303-
PR is open is the normal shape of this failure, not a reprimand. On a probe, re-read
304-
state and deliver the report from your transcript: every such death so far was fully
305-
recoverable with zero work lost. The cost is latency, not correctness — ⛔ never
306-
"recover" by redoing the work.
307-
4. **The self-check before every turn you are about to end**: *does my last message
308-
describe a wake-up I expect from a process I do not own?* If yes — a queued lock,
309-
another agent's build, a watcher that already detached — that wake-up is not coming and
310-
you are about to stall; keep the turn alive and collect the exit code yourself. The
311-
report is never a violation of this check: it ends the turn on a **result**,
312-
`in_progress` gate status included, not on a promise that something else will resume
313-
you. The only wait you may end a turn on is one your report calls `blocked` and names.
304+
**the PM's probe-and-revive loop is the standing backstop** — being probed after your PR
305+
is open is the normal shape of this failure, not a reprimand. On a probe, re-read state
306+
and deliver the report from your transcript (every such death was recoverable with zero
307+
work lost; the cost is latency, not correctness): ⛔ never "recover" by redoing the work.
308+
4. **The self-check before every turn you are about to end**: *does my last message describe
309+
a wake-up I expect from a process I do not own?* If yes — a queued lock, another agent's
310+
build, a watcher that already detached — it is not coming and you are about to stall; keep
311+
the turn alive and collect the exit code yourself. The report never violates it: it ends
312+
the turn on a **result** (`in_progress` included), not a promise that something else
313+
resumes you. The only turn-ending wait is one your report calls `blocked` and names.
314314

315315
## When to stop instead of code
316316

.claude/skills/pm-dispatch/references/review-checklist.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44
自述核验;本表逐项展开每份 dev 报告的复核判据。⛔ 不引用 issue 编号。
55

66
- **PR 形态**:存在、是 draft、目标 `main`、正文首行引用卡片 —— **`Fixes #<n>`
7-
当合并应当关卡**;只落地了可实施一半(另一半在决策箱或按范围排除)⇒ 必须
8-
`Part of #<n>`,否则合并会静默关掉一张正躺在决策箱里的卡,而
9-
`needs-user-decision` 的收件箱过滤只看 open issue。翻 ready 之前亲核首行,别只
10-
信报告。
7+
当合并应当关卡**;只落地了可实施一半(另一半在决策箱或按范围排除)⇒ 必须 `Part of
8+
#<n>`,否则合并会静默关掉一张正躺在决策箱里的卡,而 `needs-user-decision` 的收件
9+
箱过滤只看 open issue。翻 ready 之前亲核首行,别只信报告。
1110
- **`Part of` PR 翻 ready 前,再扫一遍正文的闭合关键词形状**(解析器行为与安全写
1211
法见平台读数事实表):⛔ 永不把闭合关键词放在另一张 open 卡编号旁 —— 为防止误关
1312
而写的否定句恰恰就是执行误关的那句。半状态巡查器的矛盾检测只巡开着的 PR,翻
@@ -19,11 +18,12 @@
1918
认没有别的卡被一并关掉**(闭合关键词解析器不理会否定句,body 与 commit
2019
message 分开解析,细则见平台读数事实表);误关的卡以 completed 状态对一切「只看
2120
open」的过滤隐身,这一读是唯一能兜住它的机械检查。
22-
- **范围检查**(取 changed files,⛔ 不看报告自述):无 `content/docs/releases/`
23-
改动、用户可见改动有 changeset、无与卡无关的文件。Tests/docs-only PR 走
24-
`skip-changeset` 标签,不走空 changeset(空 changeset 滞留发布);含读者可见生
25-
成产物时 dev 选 changeset 是对的 —— 以 PR 正文说明的理由为准,两条路都有效,别
26-
来回改。
21+
- **范围检查**(取 changed files,⛔ 不看报告自述):无 `content/docs/releases/`
22+
动、用户可见改动有 changeset、无与卡无关的文件。Tests/docs-only PR 走
23+
`skip-changeset` 标签,不走空 changeset(空 changeset 滞留发布);含读者可见生成产
24+
物时 dev 选 changeset 是对的 —— 以 PR 正文说明的理由为准,两条路都有效,别来回改。
25+
- **就地修了范围外的邻接缺陷?**四条件逐条核(同缺陷类·机械·文件无他人认领·同门禁
26+
族),再核 claim 文件面同轮已修订、PR 正文点名该修并载证据;缺一条即判 REWORK。
2727
- **改动触及的每个包,`private: false` 即已发布 ⇒ 核 changeset 在不在**:判据是包
2828
的发布状态(读 `package.json`,十秒),不是改动大小,也不是「用户可见」的感觉判
2929
断 —— 那个判断 dev 在时间压力下会乐观化。⛔ 缺了不入队 —— 合进 main 却永不发

0 commit comments

Comments
 (0)