From 6b95c870063b2c11688537de2a0b62f70f28912b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 29 Oct 2026 15:34:39 +0000 Subject: [PATCH 1/3] docs(checklist-test): a QA security finding gets no card and goes privately to the PM session, runners only test, and runner dispatches state effort by card shape RUNNER.md rule 2 and the checklist-test skill carry one rule in two places. A security finding (an authentication or authorization hole, or any finding whose reproduction is an exploit recipe) now gets no GitHub card at all; the run record keeps one row, and the runner reports the reproduction privately to the session that dispatched it. That PM session dispatches the fix through pm-dispatch and owns every PR the run produces. Every write and every issue or PR handling step of a run goes through the pm-dispatch doors; a refused door stages the writes and hands them to the dispatching session. The skill's section 2 now states runner effort by card shape (high for a run card, xhigh only for a security-fix os-dev and adversarial re-verification), how each backend takes it, and the spend line. os-dev.md gains the no-card report path in two existing lines. The checklist-test ceiling rises 246 to 259 under the ruling of record that orders the text. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_016WV4cxs6a1nUyBHTEo84qV --- .claude/agents/os-dev.md | 4 +- .claude/skills/checklist-test/SKILL.md | 37 +++++++++----- docs/qa/platform-checklist/RUNNER.md | 64 +++++++++++++++---------- scripts/pm/check-skill-line-ratchet.mjs | 7 ++- 4 files changed, 71 insertions(+), 41 deletions(-) diff --git a/.claude/agents/os-dev.md b/.claude/agents/os-dev.md index 4fa3e220100..9a43a10a19a 100644 --- a/.claude/agents/os-dev.md +++ b/.claude/agents/os-dev.md @@ -12,8 +12,8 @@ model: opus -你是 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 有约束力,第一次编辑前先读它。 diff --git a/.claude/skills/checklist-test/SKILL.md b/.claude/skills/checklist-test/SKILL.md index cfaafbda25f..8b63114405f 100644 --- a/.claude/skills/checklist-test/SKILL.md +++ b/.claude/skills/checklist-test/SKILL.md @@ -102,8 +102,16 @@ Tier 1 批量清掉,剩下的预算才对得起 Tier 2。 `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --tier ` 的输出、 ⛔ 不凭记忆(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` @@ -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 · (<已判定>/<总数>) · · · `:唯一 形状、严格解析、分数**强制**;语法与退役写法见 RUNNER.md「The record title」。 @@ -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`。 @@ -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**(安全发现 = 认证 / 授权漏洞,或复现即利用配方的发 + 现):连只写 detail withheld 的卡也 ⛔ 不立,也不在锚卡、跟踪卡提它。运行记录只留一行 + `area.item · 条款 · security finding — detail withheld, reported to PM`,算已完成判定、标题计 + `FAIL`;复现经会话间消息私下报给**派发它的 PM 会话**,由它经 pm-dispatch 派修复:席内 `os-dev` + 子代理、`xhigh`,复现只进派发令,PR、提交与 changeset 只写加固;要定契约的交维护者。 +- **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,不要糊在一起。 diff --git a/docs/qa/platform-checklist/RUNNER.md b/docs/qa/platform-checklist/RUNNER.md index f3acb791a60..f8ab78d7602 100644 --- a/docs/qa/platform-checklist/RUNNER.md +++ b/docs/qa/platform-checklist/RUNNER.md @@ -78,22 +78,34 @@ 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. **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, reports the path and hands off to the dispatching session, which + reads them and posts them through the same doors in the staged order. ⛔ A staged file + never carries a security reproduction; that travels by message only. (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)`, @@ -763,10 +775,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): @@ -860,12 +873,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. diff --git a/scripts/pm/check-skill-line-ratchet.mjs b/scripts/pm/check-skill-line-ratchet.mjs index 6b6eeb3fe2a..5285944732e 100644 --- a/scripts/pm/check-skill-line-ratchet.mjs +++ b/scripts/pm/check-skill-line-ratchet.mjs @@ -1559,7 +1559,12 @@ export const CEILINGS = new Map([ // maintainer's 「以上应该写入你的 checklist skills」, recorded verbatim with the // rulings it orders in comment 6433562861 — +8 measured lines (write doors, // file-every-defect, auth/authz fix dispatch, QA run is not a lane card). - ['.claude/skills/checklist-test/SKILL.md', 246], + // 246 → 259: the ruling of record that orders text into the file — the + // maintainer's 2026-10-29 words for #27382 and #27390, collected verbatim in + // comment 6500750765 — +13 measured lines (a security finding gets no card and + // the PM session dispatches its fix, the runner only tests, runner effort by + // card shape per backend, the spend line). + ['.claude/skills/checklist-test/SKILL.md', 259], ['.claude/skills/checklist-author/SKILL.md', 62], // 157 → 140 (#25753): lines that named objectui as a sibling repo retired; landed count. ['.claude/skills/dogfood-verification/SKILL.md', 140], From 2d15ca6c49115d8129dccbd625ee221f74bb1db9 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 29 Oct 2026 16:01:56 +0000 Subject: [PATCH 2/3] docs(qa): three checklist items name rule 2's security-finding row, not the retired card wording MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit access-security.platform-owner-email-anchor, identity-auth.two-factor-enrollment-reveal and identity-auth.two-factor-verify-to-activate pointed at RUNNER rule 2's carve-out in the retired shape ("item + clause + detail withheld pending maintainer", and "existence published"). Each now names what rule 2 says after this PR: no card, and one run-record row `area.item · clause · security finding — detail withheld, reported to PM`. Revision bumped with a history row on each; no clause, oracle or status changed. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_016WV4cxs6a1nUyBHTEo84qV --- .../areas/access-security.json | 10 +++++++-- .../areas/identity-auth.json | 22 ++++++++++++++----- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/docs/qa/platform-checklist/areas/access-security.json b/docs/qa/platform-checklist/areas/access-security.json index 78346ce0d44..58286d5bed8 100644 --- a/docs/qa/platform-checklist/areas/access-security.json +++ b/docs/qa/platform-checklist/areas/access-security.json @@ -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": [ @@ -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" @@ -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" } ] }, diff --git a/docs/qa/platform-checklist/areas/identity-auth.json b/docs/qa/platform-checklist/areas/identity-auth.json index 40d5b951cdf..02a116514ed 100644 --- a/docs/qa/platform-checklist/areas/identity-auth.json +++ b/docs/qa/platform-checklist/areas/identity-auth.json @@ -1396,7 +1396,7 @@ "title": "TOTP enrollment reveal: /two-factor/enable returns a parseable otpauth:// URI + backup codes, the declared reveal dialog's paths resolve against that exact shape, and the live re-reveal endpoint's behavior is recorded", "since": "v16", "status": "active", - "revision": 3, + "revision": 4, "priority": "P1", "surface": "api", "personas": ["a signed-in user with a known password enrolling their own second factor"], @@ -1450,7 +1450,7 @@ { "clause": "OBSERVE-AND-RECORD — the live re-reveal endpoint is recorded, not assumed: POST /api/v1/auth/two-factor/get-totp-uri is a mounted route that hands the totpURI back AFTER enrollment — measured password-gated and returning the SAME secret (the identity-auth slice report on #23878 (6216631966)). The enable dialog's 'shown only once' copy covers the backup codes only, so the re-reveal breaks no promise the console makes; whether a re-revealable TOTP secret is accepted design is a ruling owed, not this clause's verdict. The run records the behaviour (password-gated? same secret? before and after verification?) — this clause is satisfied by an accurate record, not by any particular outcome", "oracle": "api", - "verify": "drive POST /two-factor/get-totp-uri with and without the correct password, before and after the enrollment is verified; record each status and whether the returned URI carries the same secret. If it re-reveals with NO password gate, that is an authentication finding — RUNNER rule 2's carve-out applies (existence published, recipe withheld)", + "verify": "drive POST /two-factor/get-totp-uri with and without the correct password, before and after the enrollment is verified; record each status and whether the returned URI carries the same secret. If it re-reveals with NO password gate, that is an authentication finding — RUNNER rule 2's carve-out applies (no card; one run-record row, recipe withheld)", "evidence": "the recorded behavior table (secrets redacted)" }, { @@ -1463,7 +1463,7 @@ "negative": [ "enable with a wrong password succeeding — or an anonymous enable answering 2xx — is a FAIL: the password check is the proof-of-presence that stops a stolen cookie from enrolling an attacker's authenticator", "scoring ANY clause with the guard clause unmet is the false positive this item was authored against: an unarmed boot 404s the whole family, and a runner who skips the guard ticks green boxes on an absent surface (absence-inference, both directions)", - "if get-totp-uri re-reveals the secret with no password gate, do NOT publish the reproduction — authentication carve-out, RUNNER rule 2: item + clause + 'detail withheld pending maintainer'" + "if get-totp-uri re-reveals the secret with no password gate, do NOT publish the reproduction — authentication carve-out, RUNNER rule 2: no card, one run-record row `area.item · clause · security finding — detail withheld, reported to PM`" ], "traps": ["dispatcher-vs-hono-route", "absence-inference"], "source": [ @@ -1489,6 +1489,12 @@ "date": "2026-10-17", "change": "steps[6] and acceptance[4].clause corrected (checklist-accuracy finding of the identity-auth slice report on #23878 (6216631966)). Both read the enable dialog's 'shown only once' as a promise about the secret, but the copy says 'The backup codes are shown only once' — so a password-gated get-totp-uri that re-reveals the same secret contradicts no copy. The clause records the measured behaviour and leaves the design question (is a re-revealable secret acceptable?) to a ruling. Wording only: the step's request and the oracle are unchanged", "ref": "#24036" + }, + { + "revision": 4, + "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[2] quoted the retired 'detail withheld pending maintainer' row and now names the new one; acceptance[4].verify said 'existence published', which read as the retired card, and now says no card. The same PR re-words every item string that quoted the retired row. No clause, oracle or status changed", + "ref": "#27642" } ] }, @@ -1497,7 +1503,7 @@ "title": "Enrollment is inert until one correct TOTP verifies on the session lane — and the run records what sys_two_factor.verified actually carries in between", "since": "v16", "status": "active", - "revision": 3, + "revision": 4, "priority": "P1", "surface": "api", "personas": ["a signed-in user mid-enrollment (session lane)", "the same user at the sign-in challenge (cookie lane)"], @@ -1538,7 +1544,7 @@ { "clause": "LATENT-DEFAULT PROBE (observe-and-flag, auth-integrity — NOT a confirmed defect): record which value sys_two_factor.verified actually carries immediately post-enable. The suspicion it encodes: the object declares verified with defaultValue: true (sys-two-factor.object.ts) while better-auth enrolls verified:false, and AUTH_TWO_FACTOR_SCHEMA maps only the four RENAMED fields (backupCodes/userId/failedVerificationCount/lockedUntil — auth-schema-config.ts; `verified` needs no rename, so its absence from the map is not itself the defect). IF better-auth ever omits the column on insert, the ObjectQL default would mark an unverified enrollment active. The clause is satisfied by the accurate record; verified===false post-enable is the healthy reading", "oracle": "api", - "verify": "the system-context ObjectQL read of the row between enable and verify: capture `verified` verbatim. If it reads TRUE pre-confirmation AND clause 2's sign-in lane accepts the factor, that is an authentication-integrity finding — RUNNER rule 2's carve-out governs its publication (item + clause + detail withheld pending maintainer)", + "verify": "the system-context ObjectQL read of the row between enable and verify: capture `verified` verbatim. If it reads TRUE pre-confirmation AND clause 2's sign-in lane accepts the factor, that is an authentication-integrity finding — RUNNER rule 2's carve-out governs its publication (no card; one run-record row `area.item · clause · security finding — detail withheld, reported to PM`)", "evidence": "the row read (timestamped between enable and verify)" }, { @@ -1575,6 +1581,12 @@ "date": "2026-10-27", "change": "fixtures.requires[1] names the DB-file read as the live-boot equivalent of the system-context read. Checklist-accuracy finding of the 18.2.0 wave 2 (QA run #26476, release-18.2.0-w2-manual-api, at 756220c4; wave anchor #25807, slice A): the run read sys_two_factor.verified from the DB file, which the item did not name. No clause changed", "ref": "#25807" + }, + { + "revision": 4, + "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. acceptance[2].verify 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" } ] }, From 4e5517ca4d5a7f9256f9f46ef698cc8872df6f8c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 29 Oct 2026 18:01:25 +0000 Subject: [PATCH 3/3] docs(checklist-test): a refused write door hands off the staged text itself, and a security row closes on its own Contract-review findings on the previous head. - A runner whose own permission check refuses a write door now hands the dispatching session the path and the staged text, in its final report or by session-to-session message. The dispatching session cannot read a file in a cloud runner's container, so a path alone was not a hand-off. This is written in both places: RUNNER rule 2 and the skill's section 4. - A runner with no session-to-session channel keeps the reproduction in its own session and reports the hand-off as blocked, without the reproduction. This is also written in both places. - Section 5's four-way disposition now says it is exhaustive except for a security finding, whose row is its own disposition: no card and no Extracted line. - Section 2 says the PM dispatches a security fix, not a fix card. - os-dev: a no-card dispatch order names the branch, the report's issue field is null, and the description admits a no-card order. Line counts are unchanged: 259 against 259 and 402 against 402. Co-authored-by: Claude Claude-Session: https://claude.ai/code/session_016WV4cxs6a1nUyBHTEo84qV --- .claude/agents/os-dev.md | 10 +++++----- .claude/skills/checklist-test/SKILL.md | 24 ++++++++++++------------ docs/qa/platform-checklist/RUNNER.md | 14 +++++++++----- 3 files changed, 26 insertions(+), 22 deletions(-) diff --git a/.claude/agents/os-dev.md b/.claude/agents/os-dev.md index 9a43a10a19a..d662fbba661 100644 --- a/.claude/agents/os-dev.md +++ b/.claude/agents/os-dev.md @@ -1,10 +1,10 @@ --- 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 --- @@ -12,7 +12,7 @@ model: opus -你是 ObjectStack 开发 agent,由 PM 派发,恰好带一张 GitHub issue;无卡派发时派发令即卡。 +你是 ObjectStack 开发 agent,由 PM 派发,恰好带一张 GitHub issue;无卡时派发令即卡并点名分支。 交付物两件:该 issue 实现完毕并推成 draft PR,加下方的 JSON 报告;无卡则无 `Fixes #`,仅交终报。 报告交付两次,GitHub 优先:同一段 JSON 先作 issue 评论,首行单独一行是纯文本 `os-dev-report`。 ⛔ 不用 HTML 注释写标记。再作为终报消息:PM 机械解析它,终报消息就是 JSON 本身。 @@ -359,9 +359,9 @@ model: opus ```json { - "issue": , + "issue": , "status": "done | rework | blocked | needs_decision | reading", - "branch": "claude/issue--", + "branch": "claude/issue-- | the branch a no-card dispatch order names", "pr": "", "session": "session_ — this run's harness-stamped id (transcript Claude-Session: line; subagent = parent's)", "premise_still_valid": true, diff --git a/.claude/skills/checklist-test/SKILL.md b/.claude/skills/checklist-test/SKILL.md index 8b63114405f..e714937f039 100644 --- a/.claude/skills/checklist-test/SKILL.md +++ b/.claude/skills/checklist-test/SKILL.md @@ -111,7 +111,7 @@ Tier 1 批量清掉,剩下的预算才对得起 Tier 2。 - **花费行。** 波次锚卡的派发记录与每个 runner 终报带各卡报告时的花费(`get_session` 的 `external_metadata.usage.cost_usd`),供下一波校准。 - **QA 运行不是车道卡。** runner(子代理或云卡)由发起本轮的会话直接派,⛔ 不走车道认领 - 与座位贴;pm-dispatch 管写入门(§4)、安全修复派卡与 PR 持有(Guardrails)。 + 与座位贴;pm-dispatch 管写入门(§4)、安全修复派发与 PR 持有(Guardrails)。 - **没有子代理工具时,顺序跑 —— 并在运行记录里声明这一轮是顺序执行的。** 规则与它 的论证住在 `checklist-author` 技能(维护者所定,一处成文);⛔ 不留第二份拷贝。 - 忠实执行每项的 `steps`,按各自声明的 `oracle` 判定每条 `acceptance` 与 `negative` @@ -135,10 +135,10 @@ expected-fail 探针 —— 永不在真实缺陷上把条款打成绿。 助,随运行环境一起丢弃。持久报告需要的是**复现规则,不是图片**。 一轮的每次 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`。写入门被本会话权限拦下 ⛔ 不绕行:待发文本落文件,路径交派发会话,由它读过照序 -经同一写入门补发;安全复现 ⛔ 不落文件 —— 写入未落地即未收口。记录这样写: +`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 · (<已判定>/<总数>) · · · `:唯一 形状、严格解析、分数**强制**;语法与退役写法见 RUNNER.md「The record title」。 @@ -184,9 +184,9 @@ expected-fail 探针 —— 永不在真实缺陷上把条款打成绿。 | **过期规格** | 两边都按各自的声明行事,条款钉的是已被裁决改掉的旧形状 | 立 re-point 卡:把条款重新指向现行规格,引裁决 | | **配对无效** | 该项与这个 subject / fixture 根本不成立(错配的 revision、不适用的环境) | 记 `blocked` 说明,⛔ 不立卡 | -四选一是**穷尽的**:落不进任何一格的 `fail` 说明你还没判完 —— ⛔ 永不把它留成一条无 -处 -置的红行。 +除安全发现外,四选一是**穷尽的**:落不进任何一格的 `fail` 说明你还没判完 —— ⛔ 永不把 +它留成一条无处置的红行。安全发现那一行本身即处置:⛔ 不立卡、不写 `Extracted:` 行,修复 +由派发它的 PM 会话派(Guardrails)。 与 §4「清单准确性发现归波次锚卡」的分界,是**有没有一条 `fail` 要清**:由 `fail` 判出 的断言缺陷 / 过期规格各自立卡(那条红行得有人负责变绿),运行中顺带看到的清单准确性问 @@ -246,10 +246,10 @@ QA-source: #运行记录号 · area.item · 条款 - **每个产品问题同一轮经 §4 立 issue。** `fail` 条款与条款外探测同样,⛔ 不只留在运行记 录或聊天;安全发现按下一条。 - **安全发现不立卡,复现不上 GitHub**(安全发现 = 认证 / 授权漏洞,或复现即利用配方的发 - 现):连只写 detail withheld 的卡也 ⛔ 不立,也不在锚卡、跟踪卡提它。运行记录只留一行 - `area.item · 条款 · security finding — detail withheld, reported to PM`,算已完成判定、标题计 - `FAIL`;复现经会话间消息私下报给**派发它的 PM 会话**,由它经 pm-dispatch 派修复:席内 `os-dev` - 子代理、`xhigh`,复现只进派发令,PR、提交与 changeset 只写加固;要定契约的交维护者。 + 现):⛔ 不立任何卡,锚卡、跟踪卡不提;复现经会话间消息私报给**派发它的 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 是**同一条规则写在两处**,⛔ 两边都不是对另一边的优先 diff --git a/docs/qa/platform-checklist/RUNNER.md b/docs/qa/platform-checklist/RUNNER.md index f8ab78d7602..cd437a917f3 100644 --- a/docs/qa/platform-checklist/RUNNER.md +++ b/docs/qa/platform-checklist/RUNNER.md @@ -86,8 +86,10 @@ selector handed you has a verdict; planned ids are listed, not chased. 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. **That + **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 @@ -99,9 +101,11 @@ selector handed you has a verdict; planned ids are listed, not chased. 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, reports the path and hands off to the dispatching session, which - reads them and posts them through the same doors in the staged order. ⛔ A staged file - never carries a security reproduction; that travels by message only. (Maintainer + 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