From d49a51024003f9991de72832e9382ab44a5102fe Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 04:10:59 +0000 Subject: [PATCH 1/3] qa(checklist-test): writes go through the pm-dispatch doors, every defect a run meets is filed, an auth/authz fix is dispatched at once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - §4: every GitHub write of a run (record, defect cards, comments, closes) goes through scripts/pm/ issue-create · post-stamped · close-cards behind write-pace; in a cloud container they ride the fleet relay. A write the session refuses is not worked around: the texts go to files and are posted in order once the maintainer clears it. - §2: a QA run is not a lane card; its runners (subagents or one cloud session per card) are dispatched by the session that starts the round, with no lane claim or seat-post act. - Guardrails + RUNNER rule 2 (one rule, both places): every product defect the run meets, clause fail or off-clause probe alike, is filed in the same run; an authentication/authorization finding gets a withheld-detail card and its fix is dispatched at once through pm-dispatch (in-seat os-dev, reproduction only in the dispatch prompt), never waiting on the maintainer. Maintainer rulings, 18.2.0 wave-2 session, recorded on #25807. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_012YjcDon6BvR9MN93ZsFq3U --- .claude/skills/checklist-test/SKILL.md | 22 ++++++++++++++++++---- docs/qa/platform-checklist/RUNNER.md | 22 +++++++++++++++------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/.claude/skills/checklist-test/SKILL.md b/.claude/skills/checklist-test/SKILL.md index e13930a42ca..b599404bbeb 100644 --- a/.claude/skills/checklist-test/SKILL.md +++ b/.claude/skills/checklist-test/SKILL.md @@ -102,6 +102,9 @@ 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)。 +- **QA 运行不是车道卡。** runner(子代理或云卡 `create_session`,一卡一容器)由发起这一 + 轮的会话按本技能直接派,⛔ 不走车道认领、座位贴与 `domain:*` 归属;pm-dispatch 只管 + 它的写入门(§4)与安全修复派卡(Guardrails)。 - **没有子代理工具时,顺序跑 —— 并在运行记录里声明这一轮是顺序执行的。** 规则与它 的论证住在 `checklist-author` 技能(维护者所定,一处成文);⛔ 不留第二份拷贝。 - 忠实执行每项的 `steps`,按各自声明的 `oracle` 判定每条 `acceptance` 与 `negative` @@ -124,7 +127,14 @@ expected-fail 探针 —— 永不在真实缺陷上把条款打成绿。 **issue 是纯文本 —— 永不放图。** 截图只为让你和子代理**现场**得出判定;它们是判断辅 助,随运行环境一起丢弃。持久报告需要的是**复现规则,不是图片**。 -经 REST 代理 `POST .../issues` 立单(`Content-Type: application/json`;⛔ 永不 MCP `issue_write`,锁 1 已拒): +一切 GitHub 写入只走 pm-dispatch 的写入门:`scripts/pm/issue-create.mjs` 立记录与缺陷卡、 +`scripts/pm/post-stamped.mjs` 写评论、`scripts/pm/close-cards.mjs` 关单,批量先 +`scripts/pm/write-pace.mjs --announce-batch N`;从仓根直接调用,⛔ 不加 `cd … &&` 前缀。云容器 +里它们自走 fleet relay(`objectstack-fleet[bot]`)。⛔ 永不 MCP `issue_write`(锁 1 已拒),⛔ 永不裸 +`curl`。写入被会话权限拦下时 ⛔ 不绕行:把全部待发文本落文件,最后一条消息给出路径与正文, +由维护者放行后照原顺序补发 —— 写入没落地,这一轮就没收口。 + +记录这样写: - **标题** —— `QA run · (<已判定>/<总数>) · · · `:唯一 形状、严格解析、分数**强制**;语法与退役写法见 RUNNER.md「The record title」。 @@ -229,10 +239,14 @@ QA-source: #运行记录号 · area.item · 条款 - **不伪造覆盖。** 缺 fixture → `blocked(fixture)` 带理由;console 没构建 → 构建它 或记 `blocked(environment)`;证到一半的项是 `partial`,不是 `pass`。带证据的 blocked 判定是一次成功运行;伪造的 pass 不是。 +- **测试中遇到的每个产品问题都立 issue** —— `fail` 条款与条款外探测所得同样,同一轮经 §4 写入 + 门立卡;⛔ 不只写进运行记录或聊天。认证 / 授权类按下一条走。 - **认证 / 授权漏洞的复现,永不发布到 GitHub 的任何地方** —— 不进运行 issue,不进跟踪 - 卡,不进评论。记下项 id、条款与 `detail withheld pending maintainer`,复现留在会话里,本轮以该报 - 告终局,⛔ 不等维护者。⚠ 这与 RUNNER 规则 2 是**同一条规则写在两处**,⛔ 两边都不是对 - 另一边的优先级主张;完整论证与它的维护者裁定在 RUNNER。 + 卡,不进评论。记下项 id、条款与 `detail withheld pending maintainer`,复现留在会话里;修复 + **直接经 pm-dispatch 派卡**:立一张只载项 id、条款与 `detail withheld pending maintainer` + 的卡,再以席内 `mode:subagent`(披露纪律卡)派 `os-dev`,复现只进派发令,⛔ 不等维护者。 + 一次性 runner 无派发通道时,由派它的会话接手派卡。⚠ 这与 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 f2257b7afb0..086d29ea78a 100644 --- a/docs/qa/platform-checklist/RUNNER.md +++ b/docs/qa/platform-checklist/RUNNER.md @@ -83,12 +83,17 @@ selector handed you has a verdict; planned ids are listed, not chased. 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, and the run - ends there with that report — a one-shot runner has no channel to wait on. 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; the `checklist-test` skill states the same guardrail in - the same terms — one rule written in both places, not a precedence claim by either.) + 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.) **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)`, @@ -831,7 +836,10 @@ closing out the report includes the extraction, owed by the runner: 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. The extracted card is public too. + 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. - **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. From 1f360f719e96dd8f16a8c308b0d186b5a878956e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 08:39:03 +0000 Subject: [PATCH 2/3] qa(checklist): record-discussion-mentions names both reads the mention gate now asks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mention recipient gate admits a mentioned user only when they may read the comment as well as its record (#26497). personas[1] says the @mention target must hold read on sys_comment; acceptance[9] and negative[6] name both reads; revision 7 → 8 with a history row. Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_012YjcDon6BvR9MN93ZsFq3U --- .../qa/platform-checklist/areas/records-forms.json | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/qa/platform-checklist/areas/records-forms.json b/docs/qa/platform-checklist/areas/records-forms.json index e359b74abf6..cf9ded73eb3 100644 --- a/docs/qa/platform-checklist/areas/records-forms.json +++ b/docs/qa/platform-checklist/areas/records-forms.json @@ -2814,12 +2814,12 @@ "title": "Record discussion: an @mention comment reconciles optimistically, persists to sys_comment, interleaves with activity, pings the mentioned user's bell — and the feed filter shows exactly each mode's subset without dropping author-extended activity", "since": "v16", "status": "active", - "revision": 7, + "revision": 8, "priority": "P1", "surface": "browser", "personas": [ "seeded admin (admin@objectos.ai / admin123) — the comment author", - "a second user, minted by the admin with POST /api/v1/auth/admin/create-user — the @mention TARGET. A stock showcase boot is invite_only, so a fresh POST /api/v1/auth/sign-up/email answers 403 SELF_REGISTRATION_CLOSED (steps[13]; RUNNER.md, 'A stock showcase boot refuses self-registration')" + "a second user, minted by the admin with POST /api/v1/auth/admin/create-user — the @mention TARGET. The target must hold a permission set that grants read on `sys_comment` as well as being able to read the record: the producer gate admits a mentioned user only when both reads succeed, so a target with no comment read is withheld (`cannot_read_thread`) and every bell clause scores against the wrong persona. A stock showcase boot is invite_only, so a fresh POST /api/v1/auth/sign-up/email answers 403 SELF_REGISTRATION_CLOSED (steps[13]; RUNNER.md, 'A stock showcase boot refuses self-registration')" ], "fixtures": { "app": "showcase", @@ -2910,7 +2910,7 @@ "evidence": "the 'all' feed screenshot showing the custom-type row (or the pin output + the substitution note)" }, { - "clause": "an @mention notification reaches a mentioned user only when that user can READ the comment's record — whether the mention names them by user id or by email — and the author is never notified: the collab.mention notification carries the comment's preview and a deep link to the record, so it reaches exactly the readers the thread itself reaches. The gate sits at the producer (plugin-audit's mention writer), judged in each mentioned user's own envelope by the same grant resolver, organization-claim vetting and parent-read evaluator a request uses, so the tenant wall, sharing, RLS and object CRUD decide (#24785, fixed by #24794)", + "clause": "an @mention notification reaches a mentioned user only when that user can READ both the comment's record and the comment itself — whether the mention names them by user id or by email — and the author is never notified: the collab.mention notification carries the comment's preview and a deep link to the record, so it reaches exactly the readers the thread itself reaches. The gate sits at the producer (plugin-audit's mention writer), judged in each mentioned user's own envelope by the same grant resolver, organization-claim vetting and parent-read evaluator a request uses, so the tenant wall, sharing, RLS and object CRUD decide (#24785, fixed by #24794)", "oracle": "api", "verify": "step-14 inbox reads: the member gains NO collab.mention for either comment on the private note (by id, by email), gains exactly one for the account comment (data.notifications[] row of type collab.mention, actionUrl '/showcase_account/ID'), unreadCount up by one, with that deep link naming the account record and that record readable by the member (200); the admin's inbox gains none for any of the three, though the account comment named the admin. Each recipient reads only their own inbox, so the count is taken per user over GET /api/v1/notifications, never from sys_notification as admin alone. Each mentioned user withheld for want of read access is logged once at warn — '[audit] mention notification withheld' with reason cannot_read_source, the user id (the email mention resolved to it) and the comment id, never the comment text — record both lines; the author is dropped without a line. On a walled boot the cross-organization user's inbox is unchanged. Score the presence or absence of a collab.mention row per recipient and comment, not the notification's wording", "evidence": "both users' inbox reads before and after, the member's two access reads, the three comment create responses, and the withheld-recipient log lines" @@ -2923,7 +2923,7 @@ "a re-thread that is refused but LEAVES the row moved (or is accepted outright) is a FAIL — #10170 registered the gate on beforeUpdate precisely because a caller barred from creating a comment on a feeds-disabled object could otherwise re-point an existing one into it", "scoring either FEEDS_DISABLED clause from the ENABLED control alone is a FAIL — the control exists to prove the refusal is attributable to the capability gate rather than to record access (RECORD_NOT_ACCESSIBLE, comment-access-hooks.ts), so a run that reports the 403 without the 2xx control has not measured the gate", "a filter mode showing a row of another mode's type — or dropping one of its own — is a FAIL; and an unknown activity type absent from the 'all' feed is a FAIL (the objectui#5840 shape: stored, queryable, invisible), while its absence from the three NARROWING modes is correct behavior a run must not file as the drop", - "a collab.mention notification reaching a mentioned user who cannot read the comment's record is a FAIL in either spelling of the mention (user id or email): the notification is a copy of what the thread shows, preview and deep link included (#24785). A notification withheld from a mentioned user who CAN read the record is a FAIL the other way: the gate is read access, not organization membership or the author's own view" + "a collab.mention notification reaching a mentioned user who cannot read the comment's record, or cannot read the comment itself, is a FAIL in either spelling of the mention (user id or email): the notification is a copy of what the thread shows, preview and deep link included (#24785). A notification withheld from a mentioned user who CAN read the record is a FAIL the other way: the gate is read access, not organization membership or the author's own view" ], "variants": [ "all — the full merged comment + activity feed", @@ -2999,6 +2999,12 @@ "date": "2026-10-26", "change": "automated.ref, from Tier 1 run #25815 (6679644a, pin b41ed7f68956), re-read on main cc378e93. (1) acceptance[5]: the comments-permission-matrix FEEDS_DISABLED case asserts 403 + code only and never reads the thread's zero rows; the ref says so. (2) acceptance[8] names recordActivityFeed.test.ts as its fallback oracle, but the ref did not, so a ref-driven run never ran it; it is added, at ui/packages/plugin-detail/src/renderers/__tests__/. The run's notes on acceptance[6] / negative[3] and acceptance[9] confirm the clause and ref text as written (engine-level pins, partial at most). No clause changed", "ref": "#25846" + }, + { + "revision": 8, + "date": "2026-10-27", + "change": "The mention recipient gate now also asks each mentioned user's own read of the comment (fix landed in #26497 for #26453). personas[1] states that the @mention target must hold read on sys_comment as well as the record; acceptance[9] and negative[6] name both reads. No pin changed.", + "ref": "#26497" } ] }, From 1717dffd0e3abf7b48cca30ad073428ecd11f134 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 09:00:53 +0000 Subject: [PATCH 3/3] qa(checklist-test): fit the new rules under the line ratchet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compress the new checklist-test text to +8 net lines with every line under the 120-byte budget, and raise the file's CEILINGS row 238 → 246 by that measured delta, on the ruling of record that orders the text (comment 6433562861: 「以上应该写入你的 checklist skills」). Co-Authored-By: Claude Claude-Session: https://claude.ai/code/session_012YjcDon6BvR9MN93ZsFq3U --- .claude/skills/checklist-test/SKILL.md | 30 ++++++++++--------------- scripts/pm/check-skill-line-ratchet.mjs | 6 ++++- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/.claude/skills/checklist-test/SKILL.md b/.claude/skills/checklist-test/SKILL.md index b599404bbeb..3e9d15ae5f9 100644 --- a/.claude/skills/checklist-test/SKILL.md +++ b/.claude/skills/checklist-test/SKILL.md @@ -102,9 +102,8 @@ 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)。 -- **QA 运行不是车道卡。** runner(子代理或云卡 `create_session`,一卡一容器)由发起这一 - 轮的会话按本技能直接派,⛔ 不走车道认领、座位贴与 `domain:*` 归属;pm-dispatch 只管 - 它的写入门(§4)与安全修复派卡(Guardrails)。 +- **QA 运行不是车道卡。** runner(子代理或云卡)由发起本轮的会话直接派,⛔ 不走车道认领 + 与座位贴;pm-dispatch 只管写入门(§4)与安全修复派卡(Guardrails)。 - **没有子代理工具时,顺序跑 —— 并在运行记录里声明这一轮是顺序执行的。** 规则与它 的论证住在 `checklist-author` 技能(维护者所定,一处成文);⛔ 不留第二份拷贝。 - 忠实执行每项的 `steps`,按各自声明的 `oracle` 判定每条 `acceptance` 与 `negative` @@ -127,14 +126,10 @@ expected-fail 探针 —— 永不在真实缺陷上把条款打成绿。 **issue 是纯文本 —— 永不放图。** 截图只为让你和子代理**现场**得出判定;它们是判断辅 助,随运行环境一起丢弃。持久报告需要的是**复现规则,不是图片**。 -一切 GitHub 写入只走 pm-dispatch 的写入门:`scripts/pm/issue-create.mjs` 立记录与缺陷卡、 -`scripts/pm/post-stamped.mjs` 写评论、`scripts/pm/close-cards.mjs` 关单,批量先 -`scripts/pm/write-pace.mjs --announce-batch N`;从仓根直接调用,⛔ 不加 `cd … &&` 前缀。云容器 -里它们自走 fleet relay(`objectstack-fleet[bot]`)。⛔ 永不 MCP `issue_write`(锁 1 已拒),⛔ 永不裸 -`curl`。写入被会话权限拦下时 ⛔ 不绕行:把全部待发文本落文件,最后一条消息给出路径与正文, -由维护者放行后照原顺序补发 —— 写入没落地,这一轮就没收口。 - -记录这样写: +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`。写入被会话权限拦下 ⛔ 不绕行:待发文本落文件、终报给 +出路径与正文,放行后照序补发 —— 写入未落地即未收口。记录这样写: - **标题** —— `QA run · (<已判定>/<总数>) · · · `:唯一 形状、严格解析、分数**强制**;语法与退役写法见 RUNNER.md「The record title」。 @@ -239,14 +234,13 @@ QA-source: #运行记录号 · area.item · 条款 - **不伪造覆盖。** 缺 fixture → `blocked(fixture)` 带理由;console 没构建 → 构建它 或记 `blocked(environment)`;证到一半的项是 `partial`,不是 `pass`。带证据的 blocked 判定是一次成功运行;伪造的 pass 不是。 -- **测试中遇到的每个产品问题都立 issue** —— `fail` 条款与条款外探测所得同样,同一轮经 §4 写入 - 门立卡;⛔ 不只写进运行记录或聊天。认证 / 授权类按下一条走。 +- **每个产品问题同一轮经 §4 立 issue。** `fail` 条款与条款外探测同样,⛔ 不只留在运行记 + 录或聊天;认证 / 授权类按下一条。 - **认证 / 授权漏洞的复现,永不发布到 GitHub 的任何地方** —— 不进运行 issue,不进跟踪 - 卡,不进评论。记下项 id、条款与 `detail withheld pending maintainer`,复现留在会话里;修复 - **直接经 pm-dispatch 派卡**:立一张只载项 id、条款与 `detail withheld pending maintainer` - 的卡,再以席内 `mode:subagent`(披露纪律卡)派 `os-dev`,复现只进派发令,⛔ 不等维护者。 - 一次性 runner 无派发通道时,由派它的会话接手派卡。⚠ 这与 RUNNER 规则 2 是**同一条规则写 - 在两处**,⛔ 两边都不是对另一边的优先级主张;完整论证与它的维护者裁定在 RUNNER。 + 卡,不进评论。卡只载项 id、条款与 `detail withheld pending maintainer`,复现留在会话里;修复直 + 接经 pm-dispatch 派卡:席内 `mode:subagent` 派 `os-dev`,复现只进派发令,⛔ 不等维护者;runner 无 + 派发通道时由派它的会话接手。⚠ 这与 RUNNER 规则 2 是**同一条规则写在两处**,⛔ 两边 + 都不是对另一边的优先级主张;完整论证与它的维护者裁定在 RUNNER。 - **不把 blocked 项当可运行的跑** —— 解析器隐藏它们正为此。 - **一个选择器、一次运行、一张 issue;验证重测不立 issue,证据进 PR 正文(§5)。** release sweep 把 `since:vN` 与 `priority:P0` 作为分开的两轮跑 → 两张 issue,不要糊在一起。 diff --git a/scripts/pm/check-skill-line-ratchet.mjs b/scripts/pm/check-skill-line-ratchet.mjs index bce8b1d5007..f4972939f81 100644 --- a/scripts/pm/check-skill-line-ratchet.mjs +++ b/scripts/pm/check-skill-line-ratchet.mjs @@ -1547,7 +1547,11 @@ export const CEILINGS = new Map([ // that use them too — the erosion mechanism the ratchet exists to stop // isn't specific to the pm-dispatch surface. Set at current counts on // `origin/main` (headroom 0, same convention as the entries above). - ['.claude/skills/checklist-test/SKILL.md', 238], + // 238 → 246: the ruling of record that orders text into the file — the + // 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], ['.claude/skills/checklist-author/SKILL.md', 62], ['.claude/skills/dogfood-verification/SKILL.md', 157], // 328 → 334 (#10848): maintainer-ruled (2026-08-22, Option A) — convention 5