Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .claude/agents/os-dev.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
---
name: os-dev
description: >
Developer agent for exactly ONE GitHub issue, dispatched by the /pm-dispatch
Developer agent for exactly ONE GitHub issue (or one no-card order), dispatched by the /pm-dispatch
PM loop. Implements the issue end-to-end in a dedicated worktree — branch,
code, tests, changeset, push, draft PR — and returns a structured JSON
report to the PM. Use only with a single fully-specified issue as input;
report to the PM. Use only with a single fully-specified issue or order as input;
never for open-ended or multi-issue work.
model: opus
---

<!-- `model: opus` 是刻意钉死的下限,不是上限 —— PM 的逐单定档优先。完整解析顺序、
批量压死事故与两个陷阱(环境变量、允许名单)住 check:agent-model-declared 门禁脚本头部。 -->

你是 ObjectStack 开发 agent,由 PM 派发,恰好带一张 GitHub issue。
交付物两件:该 issue 实现完毕并推成 draft PR,加下方的 JSON 报告。
你是 ObjectStack 开发 agent,由 PM 派发,恰好带一张 GitHub issue;无卡时派发令即卡并点名分支。
交付物两件:该 issue 实现完毕并推成 draft PR,加下方的 JSON 报告;无卡则无 `Fixes #`,仅交终报。
报告交付两次,GitHub 优先:同一段 JSON 先作 issue 评论,首行单独一行是纯文本 `os-dev-report`。
⛔ 不用 HTML 注释写标记。再作为终报消息:PM 机械解析它,终报消息就是 JSON 本身。
仓库根的 AGENTS.md 有约束力,第一次编辑前先读它。
Expand Down Expand Up @@ -359,9 +359,9 @@ model: opus

```json
{
"issue": <n>,
"issue": <n | null — null only on a no-card dispatch>,
"status": "done | rework | blocked | needs_decision | reading",
"branch": "claude/issue-<n>-<slug>",
"branch": "claude/issue-<n>-<slug> | the branch a no-card dispatch order names",
"pr": "<url or null>",
"session": "session_<id> — this run's harness-stamped id (transcript Claude-Session: line; subagent = parent's)",
"premise_still_valid": true,
Expand Down
43 changes: 28 additions & 15 deletions .claude/skills/checklist-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,16 @@ Tier 1 批量清掉,剩下的预算才对得起 Tier 2。
`node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier <paths>` 的输出、
⛔ 不凭记忆(floor sonnet · default opus · ceiling fable);每个给:该项 JSON、RUNNER.md、dogfood 技
能、自己的端口/DB、结果不进仓规则(§4)。
- **effort 按卡形状定,派发写明,⛔ 不留空。** runner 卡(Tier 1 pin 批、手工 API/CLI 与浏览器
组、P0 冒烟、UX 走查)一律 `high`;`xhigh` 只给修安全发现的 `os-dev` 与争议判定的对抗复核。
- **按后端传档。** `Agent` 子代理显式传 `effort`(本条即传参的依据)。云卡(`create_session`)不收
effort,卡的会话记录也不带 `effort_level`,跑在哪档读不回(实测):派发记录写「未指定」并附
发起会话 `get_session` 读到的档位;它高于卡形状时照 pm-dispatch `dispatch-runbook.md`
〈云卡五课〉五之 ② 行办,或改开项目线程、开时给档位(实测按所给,不随父会话)。
- **花费行。** 波次锚卡的派发记录与每个 runner 终报带各卡报告时的花费(`get_session` 的
`external_metadata.usage.cost_usd`),供下一波校准。
- **QA 运行不是车道卡。** runner(子代理或云卡)由发起本轮的会话直接派,⛔ 不走车道认领
与座位贴;pm-dispatch 只管写入门(§4)与安全修复派卡(Guardrails)。
与座位贴;pm-dispatch 管写入门(§4)、安全修复派发与 PR 持有(Guardrails)。
- **没有子代理工具时,顺序跑 —— 并在运行记录里声明这一轮是顺序执行的。** 规则与它
的论证住在 `checklist-author` 技能(维护者所定,一处成文);⛔ 不留第二份拷贝。
- 忠实执行每项的 `steps`,按各自声明的 `oracle` 判定每条 `acceptance` 与 `negative`
Expand All @@ -126,10 +134,11 @@ expected-fail 探针 —— 永不在真实缺陷上把条款打成绿。
**issue 是纯文本 —— 永不放图。** 截图只为让你和子代理**现场**得出判定;它们是判断辅
助,随运行环境一起丢弃。持久报告需要的是**复现规则,不是图片**。

GitHub 写入只走 pm-dispatch 写入门:仓根直调 `scripts/pm/issue-create.mjs`、`post-stamped.mjs`、
`close-cards.mjs`,批量先 `write-pace.mjs --announce-batch N`,云容器内自走 fleet relay;⛔ 永不 MCP
`issue_write`(锁 1 已拒)或裸 `curl`。写入被会话权限拦下 ⛔ 不绕行:待发文本落文件、终报给
出路径与正文,放行后照序补发 —— 写入未落地即未收口。记录这样写:
一轮的每次 GitHub 写入与 issue / PR 处理(抽取、查重、收口)只走 pm-dispatch 写入门:仓根直调
`scripts/pm/issue-create.mjs`、`post-stamped.mjs`、`close-cards.mjs`,批量先 `write-pace.mjs --announce-batch N`,云
容器自走 fleet relay;⛔ 永不 MCP `issue_write`(锁 1 已拒)或裸 `curl`。写入门被本会话权限拦下
⛔ 不绕行:待发文本落盘,路径与正文随终报或会话间消息交派发会话,由它照序经原写入门
补发;其中 ⛔ 无安全复现 —— 写入未落地即未收口。记录这样写:

- **标题** —— `QA run · <selector> (<已判定>/<总数>) · <sha8> · <date> · <counts>`:唯一
形状、严格解析、分数**强制**;语法与退役写法见 RUNNER.md「The record title」。
Expand All @@ -149,7 +158,7 @@ GitHub 写入只走 pm-dispatch 写入门:仓根直调 `scripts/pm/issue-create.
取为独立 issue:标题自含、复现与机制条目化、指回本 run issue 取全量证据;缺陷卡不
挂 `qa-run`,正常进分诊首触。清单准确性发现与 fixture 缺口归波次锚卡(sweep 跟踪
issue)收口,⛔ 不抽取;环境阻塞记录在案即可。逐条 `fail` 的四分法处置、收口评论的
机器行与抽取卡的溯源行见 §5。⛔ 认证 / 授权漏洞的抽取卡不写复现,见 Guardrails。
机器行与抽取卡的溯源行见 §5。⛔ 安全发现不抽取、不立任何卡,见 Guardrails。
- **完成判据** —— 一个区只有在**每一项都有判定**时才算跑完。跑不完的一轮照常立
单:交出已有的判定,**外加一份点名未触达项的交接清单**,标题按上面的分数如实写。
`not-run` 是诚实判定 —— 判 1 项、11 项没跑就写 `(1/12) · … · 11 NOT-RUN`。
Expand All @@ -175,9 +184,9 @@ GitHub 写入只走 pm-dispatch 写入门:仓根直调 `scripts/pm/issue-create.
| **过期规格** | 两边都按各自的声明行事,条款钉的是已被裁决改掉的旧形状 | 立 re-point 卡:把条款重新指向现行规格,引裁决 |
| **配对无效** | 该项与这个 subject / fixture 根本不成立(错配的 revision、不适用的环境) | 记 `blocked` 说明,⛔ 不立卡 |

四选一是**穷尽的**:落不进任何一格的 `fail` 说明你还没判完 —— ⛔ 永不把它留成一条无
处
置的红行。
除安全发现外,四选一是**穷尽的**:落不进任何一格的 `fail` 说明你还没判完 —— ⛔ 永不把
它留成一条无处置的红行。安全发现那一行本身即处置:⛔ 不立卡、不写 `Extracted:` 行,修复
由派发它的 PM 会话派(Guardrails)。

与 §4「清单准确性发现归波次锚卡」的分界,是**有没有一条 `fail` 要清**:由 `fail` 判出
的断言缺陷 / 过期规格各自立卡(那条红行得有人负责变绿),运行中顺带看到的清单准确性问
Expand Down Expand Up @@ -235,12 +244,16 @@ QA-source: #运行记录号 · area.item · 条款
或记 `blocked(environment)`;证到一半的项是 `partial`,不是 `pass`。带证据的
blocked 判定是一次成功运行;伪造的 pass 不是。
- **每个产品问题同一轮经 §4 立 issue。** `fail` 条款与条款外探测同样,⛔ 不只留在运行记
录或聊天;认证 / 授权类按下一条。
- **认证 / 授权漏洞的复现,永不发布到 GitHub 的任何地方** —— 不进运行 issue,不进跟踪
卡,不进评论。卡只载项 id、条款与 `detail withheld pending maintainer`,复现留在会话里;修复直
接经 pm-dispatch 派卡:席内 `mode:subagent` 派 `os-dev`,复现只进派发令,⛔ 不等维护者;runner 无
派发通道时由派它的会话接手。⚠ 这与 RUNNER 规则 2 是**同一条规则写在两处**,⛔ 两边
都不是对另一边的优先级主张;完整论证与它的维护者裁定在 RUNNER。
录或聊天;安全发现按下一条。
- **安全发现不立卡,复现不上 GitHub**(安全发现 = 认证 / 授权漏洞,或复现即利用配方的发
现):⛔ 不立任何卡,锚卡、跟踪卡不提;复现经会话间消息私报给**派发它的 PM 会话**(无此
通道则留本会话,终报报 blocked),由它经 pm-dispatch 派修复:席内 `os-dev` 子代理、`xhigh`,复现只
进派发令,PR、提交与 changeset 只写加固;要定契约的交维护者。运行记录只留一行
`area.item · 条款 · security finding — detail withheld, reported to PM`,算已完成判定、标题计 `FAIL`。
- **runner 只测试。** 记录、缺陷卡、评论与 issue / PR 的每步处理都走 §4 写入门;⛔ 不派
`os-dev`,⛔ 不驱动 PR:一轮产出的每个 PR 归派发的 PM 会话复核、欠时契约复审、翻 ready 与
落地。⚠ 这两条与 RUNNER 规则 2 是**同一条规则写在两处**,⛔ 两边都不是对另一边的优先
级主张;完整论证与它的维护者裁定在 RUNNER。
- **不把 blocked 项当可运行的跑** —— 解析器隐藏它们正为此。
- **一个选择器、一次运行、一张 issue;验证重测不立 issue,证据进 PR 正文(§5)。** release
sweep 把 `since:vN` 与 `priority:P0` 作为分开的两轮跑 → 两张 issue,不要糊在一起。
68 changes: 42 additions & 26 deletions docs/qa/platform-checklist/RUNNER.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,22 +78,38 @@ selector handed you has a verdict; planned ids are listed, not chased.
issue. A `fail` with no reproduction rule in its issue is not a completed verdict.
(The screenshot that convinced you is a live judgment aid, not report content —
describe what it showed in one line; never attach it.)
- **Carve-out — authentication and authorization findings only.** ⛔ Never publish a
reproduction for an authentication or authorization hole **anywhere on GitHub**: not
the run issue, not a tracking or extracted card, not a comment. Relocating it is not a
mitigation — a tracking card is a public issue in a public repo just the same. Such a
`fail` **is a completed verdict** when it records the item id, the clause, and `detail
withheld pending maintainer`; the reproduction stays in the runner session. The fix is
**dispatched directly through `pm-dispatch`**, never waiting on the maintainer: a card
carrying only the item id, the clause and `detail withheld pending maintainer`, then an
in-seat `mode:subagent` `os-dev` dispatch (a disclosure-discipline card) whose prompt
alone carries the reproduction. A one-shot runner with no dispatch channel hands that to
the session that dispatched it. Existence published, recipe withheld, is a complete and
actionable report — getting a defect fixed never requires handing anyone a working
exploit. (Maintainer disclosure ruling, 2026-08-18, recorded on #9387; fix-dispatch
ruling, 2026-10-27, recorded on #25807; the `checklist-test` skill states the same
guardrail in the same terms — one rule written in both places, not a precedence claim
by either.)
- **Carve-out — security findings.** A security finding is an authentication or
authorization hole, or any finding whose reproduction is an exploit recipe. ⛔ It gets
**no GitHub card at all**: no withheld-detail card, no tracking or extracted card, and
no line about it on the wave's anchor. A card that withholds the recipe still announces,
in a public repository, that a named item carries a hole before its fix lands. ⛔ Its
reproduction appears nowhere on GitHub: not the run issue, not a card, not a comment,
not a PR. The run record carries one row,
`area.item · clause · security finding — detail withheld, reported to PM`; that row
**is a completed verdict**, and the title counts it as a `FAIL`. The runner reports the
reproduction **privately** to the session that dispatched it, in a session-to-session
message, and keeps it nowhere else; a runner with no such channel keeps it in its own
session and reports the hand-off as blocked, without the reproduction. **That
dispatching PM session** decides the fix and dispatches it through `pm-dispatch`: an
in-seat `os-dev` subagent at `xhigh` whose dispatch prompt alone carries the
reproduction, and whose PR, commit and changeset describe a hardening without the
exploit steps. A finding that needs a contract decision first goes to the maintainer
instead. Getting a defect fixed never requires handing anyone a working exploit.
- **The runner only tests.** Its record, its defect cards, its comments, and every issue
or PR handling step of the run (extraction, dedupe, close-out) go through the
`pm-dispatch` write doors (`scripts/pm/`). ⛔ It never spawns `os-dev` and never drives
a PR: the dispatching PM session owns every PR the run produces, through review,
contract review where it is owed, readiness and landing. When the runner's own
permission check refuses a write door, it does not route around it: it stages the
writes in a file and hands off to the dispatching session with the path **and the
staged text itself**, in its final report or by session-to-session message, because
the dispatching session cannot read a file in a cloud runner's container. That session
posts them through the same doors in the staged order. ⛔ The staged text never carries
a security reproduction; that travels only in the private report. (Maintainer
disclosure ruling, 2026-08-18, recorded on #9387; fix-dispatch ruling, 2026-10-27,
recorded on #25807; no-card, runner-only-tests and PM-dispatches-the-fix rulings,
2026-10-29, recorded on #27377; the `checklist-test` skill states the same guardrail in
the same terms — one rule written in both places, not a precedence claim by either.)
**No other failure class is softened by this**: everything else owes its reproduction
rule in full.
3. **Classify blockers honestly.** Missing seed/persona/fixture → `blocked(fixture)`,
Expand Down Expand Up @@ -763,10 +779,11 @@ its README.
reach a verdict — never report artifacts. What the report carries for a defect is the
**reproduction rule**: ordered steps / API calls (method · path · body) / the
ref-targeted selector path + expected-vs-actual from the oracle, enough to re-hit it on a
fresh boot with no picture — **except under rule 2's authentication/authorization
carve-out**, where the report carries the item, the clause and `detail withheld pending
maintainer`, and nothing else. A clause whose oracle was a screenshot is recorded as a
one-line text description of what it showed, not a link.
fresh boot with no picture — **except under rule 2's security-finding carve-out**, where
the report carries the one row
`area.item · clause · security finding — detail withheld, reported to PM` and nothing else.
A clause whose oracle was a screenshot is recorded as a one-line text description of what it
showed, not a link.

The in-environment JSON scratch (RUNNER shape, never committed):

Expand Down Expand Up @@ -860,12 +877,11 @@ closing out the report includes the extraction, owed by the runner:
- **Product defects found during the run**: at close-out, extract each one into its own
standalone issue — self-contained title, reproduction and mechanism itemized in the
card, a pointer back to the run record for the full evidence chain. A defect card does
not carry `qa-run`; it enters triage first-touch normally. ⛔ An authentication or
authorization defect is extracted under rule 2's carve-out — item, clause, `detail
withheld pending maintainer`, no recipe — and its fix is dispatched at once (rule 2). The
extracted card is public too. Every product defect the run meets, a clause `fail` or an
off-clause probe finding alike, is extracted in the same run — ⛔ never left only in the
run record or the chat.
not carry `qa-run`; it enters triage first-touch normally. ⛔ A security finding is never
extracted: it gets no card at all, and its reproduction goes privately to the dispatching
session, which dispatches the fix (rule 2's carve-out). Every other product defect the
run meets, a clause `fail` or an off-clause probe finding alike, is extracted in the same
run — ⛔ never left only in the run record or the chat.
- **Checklist-accuracy findings and fixture gaps** close out through the wave's anchor
card (the sweep's tracking issue) — ⛔ not extracted.
- **Environment blockers**: recorded in the run record is enough.
10 changes: 8 additions & 2 deletions docs/qa/platform-checklist/areas/access-security.json
Original file line number Diff line number Diff line change
Expand Up @@ -3045,7 +3045,7 @@
"title": "OS_PLATFORM_OWNER_EMAIL platform-admin anchor: only a VERIFIED stored-email match confers PLATFORM_ADMIN — unset, unverified match, non-match and malformed lists each confer nothing, fail closed",
"since": "v17",
"status": "active",
"revision": 11,
"revision": 12,
"priority": "P1",
"surface": "api",
"personas": [
Expand Down Expand Up @@ -3109,7 +3109,7 @@
}
],
"negative": [
"no runtime write reaches the anchor: OS_PLATFORM_OWNER_EMAIL is declared a NON-TABLE derivation input (admin-standing-surface.ts) — revocation is a configuration change plus a process roll, by design. Any in-product surface found mutating who is a platform administrator is an authorization finding: record it under RUNNER rule 2's carve-out (item, clause, detail withheld pending maintainer), never with a reproduction",
"no runtime write reaches the anchor: OS_PLATFORM_OWNER_EMAIL is declared a NON-TABLE derivation input (admin-standing-surface.ts) — revocation is a configuration change plus a process roll, by design. Any in-product surface found mutating who is a platform administrator is an authorization finding: record it under RUNNER rule 2's carve-out (no card; one run-record row `area.item · clause · security finding — detail withheld, reported to PM`), never with a reproduction",
"the derivation reads the caller's OWN STORED sys_user row, never the session-seeded grants.email — the ⭐ P1 pin of the leg. That property is asserted by the unit pin (the '⭐ … reads the STORED row, never the seeded email' describe); do not attempt to re-prove it against a live server",
"run every refused probe as the seeded admin too: a 200 proves each refusal keyed on the member's standing, not on a broken route (wrong-persona, both sides)",
"out of scope here: the WALLED-posture boot refusal when the variable is unset (#11184) is plugin-auth's separate door — do not score it on this item"
Expand Down Expand Up @@ -3209,6 +3209,12 @@
"date": "2026-10-27",
"change": "automated.kind unit -> dogfood (#26571 slice 3; the slice-2 ACCEPT noted it): revision 10's ref already leads with a dogfood pin over the in-process HTTP stack, with the unit pins beneath. No ref, clause or status changed",
"ref": "#26571"
},
{
"revision": 12,
"date": "2026-10-29",
"change": "RUNNER rule 2's security-finding carve-out re-worded (#27382; maintainer rulings of 2026-10-29, recorded on #27377): a security finding gets no GitHub card at all, the run record carries the one row `area.item · clause · security finding — detail withheld, reported to PM`, and the reproduction goes privately to the dispatching PM session. negative[0] quoted the retired 'detail withheld pending maintainer' row and now names the new one. The same PR re-words every item string that quoted the retired row. No clause, oracle or status changed",
"ref": "#27642"
}
]
},
Expand Down
Loading
Loading