关于 qa-gate
qa-gate 是一项技能,通过面向用户的公开接口,按规格验证已完成的功能。它指导 Agent 执行可续接的 QA、收集证据、区分阻塞原因并给出最终报告结论。
主要功能
- 01通过公开接口验收
- 02逐行更新并续接报告
- 03收集并验证证据
- 04区分阻塞原因与结论
- 05使用 Agent 工具及已连接服务
适用场景
- 01验证已完成的功能要求
- 02续接中断的 QA 检查
- 03记录受阻的验收项
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ marcioaltoe/ skills --skill qa-gate安装后在 Claude Code 中输入/qa-gate确认已加载。
详细介绍
QA 门禁
通过实际操作真实用户会使用的公共接口,根据功能规格中的承诺验证组装完成的功能。Daemon 会从 _tasks.md 前置元数据指定的唯一已编写终端 qa Task 运行此技能;它属于 Task Graph,而非每次运行时的请求。该节点依赖所有非 QA 叶节点,因此只有在它所收尾的图完成后才会变为可运行。此技能负责完整的门禁流程:计划、执行、证据、发现、报告和最终判定。直接使用 Agent 的工具和已连接的服务;不要加载其他 QA 技能,也不要创建单独的动态 QA 树。
不可妥协的要求
- 真实用户路径。 通过目标用户实际使用的相同前端、API、CLI、数据工作流或文档化操作路径进入。内部辅助程序和代码检查可以帮助诊断故障,但不能证明用户故事通过。
- 超越乐观状态的证明。 通过意味着必须观察到预期结果,通过全新加载或其他公共读取路径进行独立确认,在相关情况下确认刷新或重启后仍可持久保存,并捕获证据。
- 可恢复的证据。 在首次检查之前创建带日期的报告,并将每一行都设为
pending。每完成一行就更新报告,以便中断后能从磁盘恢复,而无需重复已完成的工作。 - 明确判定。 种子报告从
pending开始。填写前置元数据块和 Results 行时,保留种子报告的前置元数据,且仅保留这一处前置元数据。每个计划行最终都必须为pass、fail、blocked或skipped;报告结束时pending行数必须为 0。未记录任何 QA 行的报告无法确定门禁结果;前置元数据为空或重复的报告也无法确定门禁结果。 - 区分阻塞原因。 若有证据证明某行因环境原因无法执行,则记录为
blocked (environment: <cause>),并计入rows_blocked_environment;若某行因发现的问题而停止,则记录为blocked (finding: <id> — waits on <named failure>),并计入rows_blocked_finding;若某行因匹配的、运行前 Spec 声明中的原因而无法执行,则记录为blocked (declared: <criterion>),并计入rows_blocked_declared。三种原因必须分别记录:不得混为一谈,以使报告或判定看起来更整洁。 - 二进制文件身份。 审计者字段由 Daemon 管理:保持种子中的
auditing_binary和auditor_staleness行不变。将运行每个公共 CLI 行的二进制文件的--version行记录为user_flow_binary。在 Roundfix 自审计中,使用make build从被审计的 Run Worktree 构建该二进制文件,公共 CLI 行只能使用./bin/roundfix运行,绝不能使用 PATH 中找到的roundfix。
可运行证据
将可运行证据存放在 .go.txt 等非编译扩展名的文件中,绝不能存为仓库门禁会编译或格式化的源文件。后续通过时需跳过已编译的源文件,并记录每个跳过的路径。
证据文件必须是文本或图像
qa/evidence/ 下的文件必须是文本或图像。截图使用 PNG、JPEG 或 SVG。Daemon 会根据内容接受文本(包括 SVG)、PNG 和 JPEG,而不受文件名影响;拒绝将非图像二进制文件提交到该目录。视频、跟踪存档、PDF、数据库或构建输出应记录为文本,或存放在 qa/evidence/ 之外;若提交被拒绝,QA Task 将判定为 failed,其报告和证据也不会提交到 Run Worktree。
证据中不得包含机密
记录环境变量名称,绝不记录其值,并记录变量是否已设置,格式为 <NAME> set 或 <NAME> not set。绝不持久化会打印环境的命令输出,包括 env、printenv、set、export -p、declare -x、make -p 和 make --print-data-base。绝不在证据中写入类似机密的示例。Secret Match 只记录路径、行号和类型,绝不为了获取密钥而重新加载 shell 配置文件。若文件匹配 Secret Pattern,Daemon 会拒绝提交 QA Report。
QA 结算
相同的结果会结算已编写的 qa Task,并决定归档留下什么:
| 结果 | 结算 | 归档 |
|---|---|---|
pass | 将 QA Task 结算为 completed;当报告中没有不允许的阻塞行时,使 Spec 符合归档条件。 | Archive Record,其中注明 QA Report 和判定结果;Spec、其 QA 报告和证据保留在 Git 中,位于该记录的 source_revision。 |
符合条件的声明式 partial | 当没有行失败、被跳过或因发现问题而阻塞,每个声明为阻塞的行都由匹配的 ## Unreachable Acceptance 声明覆盖,并且每个环境阻塞行都是 PR 前置的 Pull Request 行、记录为 blocked (environment: no open Pull Request) 且在其来源信息中注明 Pull Request 行,或是 Run 沙箱无法访问的外部证据行、记录为 blocked (environment: network denied: <host>) 且在其来源信息中注明外部证据行时,将 QA Task 结算为 completed。这两类行都不需要 Unreachable Acceptance 声明;如果未满足的行仅包含这两类行,该 partial 也符合条件。 | Archive Record,其中注明 QA Report 和判定结果,并将声明的 satisfied-by 记录标为 unproven;Spec、其 QA 报告和证据保留在 Git 中,位于该记录的 source_revision。 |
environment-blocked | 保持该行阻塞;如果等效证据满足环境策略,报告仍可结算为 pass。 | 单独不会归档任何内容;符合条件的报告可归档 Spec。 |
failed | QA Task 保持未解决状态,除非适用经授权的覆盖,否则拒绝归档。 | 无。 |
missing | QA Task 保持未解决状态,除非适用经授权的覆盖,否则拒绝归档。 | 无。 |
override | 不更改 QA Task 状态或报告判定结果;即使 QA 失败或缺失,也会在明确授权下结算归档。 | 包含 qa_override、qa_override_approval、qa_override_reason、qa_override_qa_outcome、QA Task 未完成时的 qa_override_qa_task_status,以及 qa_override_revision 的 Archive Record;QA Task 和报告在 Git 中保持不变,位于该记录的 source_revision。 |
Spec 授权要求保持离线的行
上述符合条件的声明式 partial 也接受一种外部证据行:由于 Spec 自身的授权禁止查询,门禁未查询该行。此类行记录为 blocked (environment: <subject> prohibited by Spec authorization),且在其来源信息中注明外部证据行。subject 用授权原文说明其禁止的内容:网络查询使用 network,或使用 another repository;长度为 1 至 80 个字符,且不含括号。与网络访问被拒绝的行一样,此类行不会影响符合条件的 partial 判定,也不需要 Unreachable Acceptance 声明;每行最多计入一种豁免类型,结算、roundfix qa-report accept 和 roundfix archive 都通过同一策略读取此类行(ADR-0276,ADR-0335)。
1. 确定范围和前置条件
确定 docs/specs/<slug>/,然后读取 _tasks.md、_prd.md、所有 task_NN.md,以及 qa/ 下的所有既有文件。确认清单的 qa: 字段指向当前 type: qa Task,并且该节点是终端节点,且依赖每个非 QA 叶节点。缺失或不匹配的已编写节点属于图缺陷;不要在该节点之外运行门禁。
在门禁处呈现 Transcript
对于 TechSpec 中指定的每个 Surface Transcript,添加一行 QA 矩阵。该行通过已构建的产品重放 transcript 中的命令,并根据 TechSpec 的匹配规则比较标准输出、标准错误和退出代码:一行 ... 匹配零行或多行连续内容,尖括号中的文本匹配同一行中的一个或多个字符,其他所有内容都必须完全匹配。分别比较标准输出和标准错误;退出代码始终必须完全匹配。在该行的来源信息中注明 transcript 名称,并保留观察到的输出作为证据。
-
干净的编写检查是门禁的前提条件,不能替代门禁。对于每个处于活动状态且非 legacy 的 Spec,运行:
roundfix spec check <slug> --strict门禁保留这种非探测形式,因为探测会询问某条命令在其工作尚未完成前是否已通过,而门禁是在所有实现 Task 完成后运行的。探测不会探测已完成的 Task,并会将其命令报告为
not probed,因此无事可问。三个编写技能会在工作仍处于编写阶段、这个问题尚可回答时使用--run-verification。如果命令失败,请在构建矩阵前停止,并在停止前写明拒绝原因。此时停止的门禁没有测量任何要求,因此其完整报告就是拒绝说明:
--- verdict: fail build: "<audited-commit>" rows_blocked_precondition: 1 auditing_binary: "<version-and-build-identity>" auditor_staleness: "<state>: <reason>" rows_blocked_environment: 0 rows_blocked_finding: 0 rows_blocked_declared: 0 precondition_check: "roundfix spec check <slug> --strict" precondition_reason: "<every refusing code and the sentence beside it>" --- # QA Report ## Results | # | Status | Provenance | | - | --- | --- | | 0 | blocked | precondition | ## Precondition refusal - check: roundfix spec check <slug> --strict - reason: <every refusing code and the sentence beside it>行
0就是整个矩阵:状态为blocked,因为没有运行任何内容;来源信息为precondition,因为这次停止是拒绝,而非测量。在precondition_reason中注明每个拒绝的SC-*代码,先写代码,再写其说明句,并用;连接,压缩为一行,以便维护者阅读报告时知道应修复 Spec 还是代码库。请用列表文字说明该行,切勿在## Results下再放第二张表。空的 Results 表不可接受:它会导致后续每次运行都因这份报告而被拒绝,而不是因 Spec 被拒绝;规定的修复方式——将计划中的每一行具体落实——是从未构建矩阵的运行无法执行的。在此处停止的门禁不得写入其他任何内容:不得写矩阵,也不得为从未执行的检查写行。
与完整报告一样,拒绝说明也要将
build记录为门禁所审计的提交,并加上引号,以便读者将此次拒绝与分支上更早一次 Run 留下的拒绝区分开来。若拒绝说明已由 Daemon 写入,其中已包含该值;请保留该值。如果命令通过,请在报告的范围和环境中记录干净结果,但不要在下方表格中创建重复编写规则的 QA 矩阵行。已完成或已归档的 legacy Spec 不受强制补填约束的要求;请保持每个 legacy 产物的字节级内容不变,并保留任何没有干净编写结果可替换的治理行。仅凭缺少 Project Constraints 部分,并不能证明其属于 legacy。 | 从 QA 矩阵中移除的编写规则 |
spec check等效规则 | | --- | --- | | 编写的 Task Graph 可解析,能将 PRD 承诺映射到 Task,并能解析声明的存储库路径。 |SC-COVERAGE-UNTASKED所需的 Task Graph 加载,以及SC-COVERAGE-UNTASKED和SC-REF-UNRESOLVED| | PRD 承诺映射到 TechSpec。 |SC-COVERAGE-UNMAPPED| | 每个必需的 Project Constraint 行均存在。 |SC-CONSTRAINT-MISSING| | 声明的 Project Constraint 适用性附有理由。 |SC-CONSTRAINT-UNREASONED| | Project Constraint 行引用有效来源。 |SC-CONSTRAINT-SOURCE| | 引用的工具授权明确指出 Spec。 |SC-TOOLING-UNAUTHORIZED| | 适用的工具权限声明了受限文件范围。 |SC-TOOLING-UNBOUNDED| | 工具授权记录以可读字段说明授予的权限。 |SC-TOOLING-UNTYPED| | 每个待处理的非 QA Task 对 Governed Path 的声明,都出现在授权记录和每个已提供的 Tooling 权限行中。 |SC-TOOLING-UNDECLARED| | 每个提及 CLI 界面的待处理非 QA Task,也会在该 Task 或其传递依赖项中提及一份指南。 |SC-CLI-UNDOCUMENTED| | 已生效的 ADR 义务均有列出,相关决策均已纳入考虑,且归属明确的声明与引用记录相符。 |SC-ADR-UNLISTED、SC-ADR-RELATED和SC-CITATION-UNSUPPORTED| | Task 要求彼此不矛盾,已声明演练,且 Verification 能区分 Task 工作与无工作。 |SC-REQUIREMENT-CONTRADICTORY、SC-REHEARSAL-UNDECLARED和SC-VERIFY-WORK-INDEPENDENT| | 通过 TechSpec 的 Vocabulary Contract 记录输出词汇。 |SC-VOCABULARY-UNDOCUMENTED| | 存储库循环声明与 Finding、Rollup、归档及升级后的 Backlog 生命周期保持一致。 |SC-LOOP-ORDER-DIVERGENT、SC-FINDING-LIFECYCLE、SC-ROLLUP-MEMBER、SC-ARCHIVE-LICENSE和SC-BACKLOG-UNMOVED|
检查器跳过列表中列出的检测器并未运行。它不算等效规则,因此应保留相应的 QA 行。不要类推扩展此映射:检查器未解析的适用性、缺失的工具授权、外部证据、Non-Goals、报告契约、当前 Task 状态,以及实时控制或时间顺序行为,都应继续留在门禁中,除非某条具名检查器规则明确判定它们。
- 将依赖提交的工具审计保留为矩阵行。找出每个实际创建、编辑、重命名、移动或删除仓库工具配置、脚本、忽略文件、插件声明或版本固定项的 Task。机械阶段会审计交付基线与审计头部之间的每个 Task 提交,其授权审计表会列出每个已审计提交。仅当该范围内的 Task 提交未列入表中且更改了仓库工具路径时,才按命令审计该提交。当报告的机械跳过项写明
Task commits of earlier Runs时,按命令审计每个 Task 提交。针对每行命令,使用 Daemon 所拥有的 Task 提交中的实际路径以及当前工作树差异执行命令,然后按时间顺序解析授权、前置修复和后续修复提交。对每项已提交的更改,使用git diff-tree --no-commit-id --name-only -r <commit>,而不是相信报告中的文件列表。 - 在同一失败命令行中,报告所有提交后的授权形式问题:授权缺失、过晚或不可追溯;授权或前置修复被合入 Task 提交;后续修复被合入导致问题的更改,或其顺序早于该更改;声称的派生固定项没有经批准的再生成命令提供可复现证据;以及精确限定列表与分配的 Task 文件之外的所有路径。不要停在第一个问题上,把其余问题留待另一次 QA 重跑。矩阵建立后,某项发现只会阻止依赖它的行。
- QA 只编写其报告和证据。它绝不会更改 Task 状态或 Task Graph 依赖项。
- 要求编写的
qaTask 的每个依赖项都为completed。如果任何依赖项仍未完成,则停止并列出它们。Daemon 拥有当前门禁 Task 的状态,并根据最终判定结算其状态。只有当编写的 QA Task 明确限定范围时,才运行部分门禁;将其范围和最终判定标记为partial。机械阶段在矩阵建立前不提供 Agent Session 的做法保持不变:阻断性的机器事实仍会在构建矩阵前令门禁停止。已声明的矩阵属于完整门禁,可达到pass;声明矩阵不会使运行变成部分运行。partial仅适用于明确限定范围的qaTask,或门禁未运行的计划行。 - 找出 PRD 声明的界面、用户故事、核心功能、验收标准、用户体验状态和非目标。
- 在运行前,阅读 PRD 的
## Unreachable Acceptance部分中的每个条目。将每项声明视为作者提出的待测试主张,而非证明,也不是省略该行的许可。门禁只能将阻塞行与这些运行前声明之一对应;绝不能自行声明不可达,也不能从失败尝试中推断出声明。 - 阅读每个 Task 的
## Result。只有当 Task 级标准指向具名且可复现的证据,并且其依赖的静态检查通过时,才认可该标准。无关的静态检查失败不会使该证据失效。将实时 QA 精力用于已组装的用户旅程、跨 Task 衔接、持久化、故障行为和范围蔓延。 - 重跑时,保留 Daemon 将每行设为
carried (established by: …; head: …)的状态:这些行计为通过,且不再执行。执行其他所有行,从种子## Row carry-forward部分列为re-run的行开始。
覆盖完整且闭合时,范围即为完整。
当某项 Requirement 根据 Task 的声明审计其更改文件时,Task 文件在 ## Recorded paths 下列出的路径视为已声明,并应在该行中具名。受治理路径仍需获得授权。
2. 构建 QA 矩阵并打开报告
执行前,创建一个可避免冲突的报告路径:
当天的第一份报告使用 docs/specs/<slug>/qa/qa-report-YYYY-MM-DD.md,同一天重新运行时使用
qa-report-YYYY-MM-DD-NN.md,后缀取该日期现有最高后缀加一;绝不填补更早的空缺。
同一天的后缀只能是数字;绝不使用范围或构建 slug。只有报告对应同一构建时,才继续使用现有的
status: in-progress 报告;否则创建下一个数字后缀的同级报告,并保留旧报告作为历史记录。
规划 Pull Request 测试流程前,先阅读 Roundfix QA 提示词中的 Pull Request 事实:
默认情况下,Pull Request 行使用等效证据路径。根据 ADR-0088,已撰写的门禁在 Open Pull Request 创建之前运行,因此 Specs 无需再花费资源重新发现这一安排。保留矩阵中的该行,并为 Pull Request 原本会检查的每项控制记录等效的观察证据或监督证据,同时记录该行的环境原因:
- 审批 — 谁接受了更改,或未获得任何接受;
- 检查和状态 — 仓库验证以及任何必需检查的本地运行结果;
- 未解决的审查讨论串 — 确认没有未解决项,或指出有哪些;
- Merge-Ready 接受 — 关于预期 head 的 Review Source 证据,或确认目前尚无此类证据;
- 审查产物的祖先关系 — 证据所属的 head,以及它是否就是该行所声称的 head。
绝不能默默通过或省略该行。没有等效证据的控制项会使该行保持阻塞;ADR-0080 不会认可缺少证据的测试流程,而列出的控制项未得到答复,正是这种情况。
- 当事实指出存在 Open Pull Request 时,这些测试流程可以运行。通过现有的
gh和 Review Source 边界,以只读方式检查该 Pull Request。按 Spec 要求检查审批、检查和状态证据、未解决的审查讨论串、Merge-Ready 接受,以及 Daemon 单独的审查产物提交祖先关系。绝不能修改 Pull Request、解决讨论串、提交更改或推送。 - 当事实指出没有 Open Pull Request 时,将每项 Pull Request 测试流程记录为
blocked (environment: no open Pull Request),并计入rows_blocked_environment。预 PR 的 Pull Request 行不会决定符合条件的部分,也无需声明 Unreachable Acceptance;在其来源信息中注明 Pull Request 行。Pull Request 行的来源条目可以在Pull Request row后附加说明。当事实指出无法确认 Pull Request 时,其缺失尚未得到证实:将原因记录为blocked (environment: Pull Request unresolved),绝不能将其描述为已确认不存在。不要尝试从 Run Worktree 分支解析 Pull Request:该 Run 专属分支绝不会被推送,且自身没有 Pull Request。
覆盖来源
每个以 MUST verify 或 MUST run 开头的编号 qa Task Requirement,都是一项已声明的验证 Requirement。其他所有 Requirement 都约束门禁的运行方式。
当 qa Task 至少包含一项已声明的验证 Requirement 时,这些 Requirement 即为矩阵的覆盖来源。若某项已声明的 Requirement 验证了所命名 Tasks 的每项 Acceptance Criterion,则每项 criterion 都是独立的来源。
无论是已声明矩阵还是默认矩阵,都必须覆盖以下不可豁免的来源:
- 外部证据行;
- Pull Request 行,走等效证据路径时;
- 仓库 Verification;
- PRD 的
## Unreachable Acceptance部分下的每项声明; - 声明了
frontend时的 frontend 检查。
没有已声明验证 Requirement 的 qa Task,使用有界默认方案。
其覆盖来源包括:
- PRD 用户故事和 Goals;
- 每个非 QA Task 的 Acceptance Criteria;
- 每项已声明的有意中断;
- 上述不可豁免的来源。
只有当 Non-Goal 指明 Spec 可能交付的可观测对象时,它才会成为来源。其行只读取 Spec 更改过的路径以及 Non-Goal 指明的界面。
覆盖与来源记录
覆盖必须完整、封闭且明确列名:
- 每行都必须在其来源记录中列出所覆盖的每个覆盖来源。
- 每个覆盖来源都必须至少出现在一行的来源记录中。
- 任何一行的来源记录都不得列出覆盖来源以外的来源。
- 可以将多个可由同一项观察确认的来源合并到一行。
- 任何一行的结果都不得根据其他行计算得出。
- 任何一行都不得重新检查 Mechanical Refusal Code。
对每行,记录其来源记录中覆盖的来源、参与者、入口点、界面、步骤、预期可观测结果、独立确认、持久性检查、证据路径,以及状态 pending。按用户影响和爆炸半径排列各行。高风险旅程的行为探测仅保留在有界默认方案中。对于该默认方案中的每个高风险旅程,选择 2-5 项相关探测:重复提交、操作过程中刷新或返回、深层链接/重新打开、无效或顺序错误的输入、会话过期、离线/重新连接、多个并发标签页,或语言区域/无障碍设置变更。选择与功能相关的探测;无关探测会造成干扰。
在来源记录中使用 qa Task 自身的标识符,例如“Requirement 3”、“Task 01 criterion 2”和“repository Verification”。
行输入声明
Agent 执行的每一行都必须在其详细证据块中声明一个非空且带类型的 inputs: 列表。该行的输入在规划为 pending 时即固定,并限定为该行读取的内容。将证据块放在 ### <row-id> 标题下,且行标识符必须与 Results 表中的 # 单元格匹配;每行使用一个带围栏的 yaml 代码块。位于其他标题下的代码块将被忽略,且该行不会被保留。每个条目都包含 kind 和 ref:
inputs:
- kind: repository_path
ref: <repository-relative path or glob>
每项会影响该行判断结果的输入都必须有一个条目:
| 类型 | 适用情况 |
|---|---|
repository_path | 证据取决于当前仓库中某个字面路径或 glob 下的内容。 |
external_repository | 证据取决于另一个仓库中的内容或状态。该行永远不可保留。 |
live_service | 证据取决于从实时服务观察到的状态。该行永远不可保留。 |
elapsed_time | 证据取决于经过的时间、时效、持续时间或时间窗口。该行永远不可保留。 |
commit_range | 该行读取 Task 提交、提交的授权,或其更改文件范围。该行永远不可保留。 |
声明时应采取保守原则:列出该行读取的每个来源,包括每个构建二进制文件所编译自的来源以及模块清单。列出所有适用类型。包含任何非仓库输入的混合列表永远不可保留。未声明 inputs: 或列表为空的行,不得执行,必须重新观察,因此向前沿用始终是选择启用并默认拒绝的。
不带 inputs: 的报告,其行为与当前完全一致;现有行、计数、状态、判定规则和报告命名均不变。未来某轮结束时,若某个 pass 行的声明仅包含仓库输入、其祖先关系已得到证明,且其证据在后续头提交中逐字节相同,则该行会被物化为 carried (established by: <report>; head: <sha>),且不会重新观测。
某行执行后若输入增加,则不可结转。不要在执行后添加输入以使其符合结转条件。
如果某个 gate 在行尚未声明输入时编写的报告上重新打开,则会重新执行该报告记录的每一行中不带 inputs: 的行;此类行绝不可结转。某行在新一轮中再次执行时,若该轮将其规划为 pending,则该行应声明本轮的输入。这是一次具有自身输入的新观测,并非执行后添加输入;绝不要向较早报告中的行添加 inputs:。
Agent 从不写入 evidence_snapshots;Daemon 会在轮次结束后记录它们。指向仓库 Verification 或 Pull Request 行的行始终会被观测,声明了 commit_range 的行也始终会被观测。
有一行是 Spec 的外部证据行:即依赖源自 Spec 自身产物之外证据的验收行——例如 Spec 未构建的仓库、Spec 未设计的测量,或已发表的文献。在该行中准确记录证据来源,让后续读者能够找到同一来源,以免结果被误读为对 Spec 自身前提的演练。如果 Run 沙箱禁止访问网络以进行查证,则将该行记录为 blocked (environment: network denied: <host>),注明查证尝试访问的主机,并在其来源追溯信息中保留一项 outside-evidence row。这表示未能获取来源,且绝不判定为符合条件的 partial。如果 Spec 自身的 _authorization.md 禁止该查证,则不要尝试:将该行记录为 blocked (environment: <subject> prohibited by Spec authorization),其中 <subject> 用授权文本中的措辞说明被禁止的事项,例如网络查证中的 network,长度为 1 到 80 个字符,且不包含括号。在其来源追溯信息中保留一项 outside-evidence row,并在报告中注明禁止该查证的授权文本。此情形同样绝不判定为符合条件的 partial(ADR-0276、ADR-0335)。该行可以离线查证的来源,例如 PRD 中引用的段落或 docs/references/ 下的文档,应当进行查证,不得标记为 blocked。任何其他被标记为 blocked 的外部证据行仍会阻止 Pull Request 准备工作。将每个被标记为 blocked 的外部证据行都计入 rows_blocked_environment;不得丢弃该行,也不得用 Spec 编写的证据来满足它。任务编写不会因此停滞——分解过程会记录该被阻塞的行并继续推进——因此,这项义务落在 gate 阶段,由 Spec 在此处说明情况。参见 ADR-0104。
当每个覆盖来源至少出现在一个行的来源追溯信息中、没有任何行的来源追溯信息指向覆盖来源之外的来源、每个选定的默认探测都有理由,且报告包含完整的 pending 矩阵时,计划即为完整。
3. 先运行静态 gate
闸门中的术语表
严格前置条件会报告所有术语表缺口。对于术语表声明新增或修改的每个术语,报告都会记录工作完成后术语表是否对其作出了定义。
在由守护进程分配的闸门中,守护进程已运行仓库验证,并在提示词中给出了结果;请记录该结果,不要再次运行仓库验证。在独立闸门中,运行仓库选定的验证。如果选定的验证完全无法运行,则将验证闸门记录为受阻。运行后失败的格式检查、测试或构建检查属于 fail,而非受阻——记录前请根据下文中代码原因与环境原因的区分进行分类。
仓库验证超时或间歇性失败时,应记录为失败,绝不能记为环境受阻。声称存在争用、无法复现的运行结果,或在未更改的交付目标上出现相同失败,都不能证明是环境原因。只有导致命令完全无法运行的原因才属于环境原因。
当 qa 任务指定了仓库验证之外的分析器时,请对更改过的包运行该分析器。若交付目标上的诊断相同,则应将其记录为已观察到,并注明负责该问题的规格;此诊断不会导致规格失败。无论仓库验证失败是否为既有问题,该失败仍会造成阻塞。
命令失败时,请先诊断其原因,再继续:
- 代码原因: 矩阵建立后,某项发现只会阻塞依赖它的行。记录发现,并追踪失败的检查与矩阵行之间的关系,找出哪些行的入口点、可观察项或证据依赖于失败内容。仅将受影响的行标记为
blocked (finding: <id> — waits on <named failing check>);继续执行所有结果仍然有效且不受影响的行。只有在每一行确实都依赖该检查时,失败才可能阻塞整个矩阵,例如构建失败导致没有可运行的产物;此时应在每一行中记录该依赖,而不是只针对整个矩阵声明一次。 - 环境原因: 对于仓库验证以外的检查,请通过错误信息或在未更改的基线上复现来证明存在限制;将受影响的行记录为
blocked (environment: <cause>),继续执行仍然有效的检查,并遵循第 6 节中针对带类型的受阻原因的判定规则。
静态闸门在以下条件满足时即告完成:检查通过,或所有失败均已分类;每个受影响的行都注明其等待的检查;每个由环境原因导致的受阻项都有具体证据和明确的解除阻塞要求;并且已确定其余可安全运行的行。
4. 实际运行流程
通过类似生产环境的入口启动应用。记录构建版本或提交、命令、URL 或二进制文件、数据设置、凭据配置,以及所有一致性偏差。
对矩阵中的每一行,重复以下流程:
- 通过真实入口,以指定的参与者身份进入。
- 通过公开界面执行操作,并观察产品面向用户的响应。
- 通过重新加载、重启、深层链接、第二个公开端点/命令,或其他面向用户的读取路径,确认结果。
- 在目标状态和每处偏差发生时收集证据。将屏幕截图以及重点文本或图像材料保存到
qa/evidence/<YYYY-MM-DD>-<run-slug>/;在报告中保留准确命令和简明输出。 - 立即更新该行。卡住、控件无响应、无限旋转指示器或状态消失,都属于发现项,而不是绕过产品问题的理由。
当某一行无法达到预期的可观察状态时,将该行及已证实的原因与运行前声明进行比较。只有当该行符合某项已声明的标准,且观察到的限制与该声明所述原因一致时,才使用 blocked (declared: <criterion>)。其他情况应根据实际原因标记为环境阻塞或发现项阻塞。若阻塞行没有匹配的声明,则保留其现有原因,并按照现有判定规则继续阻塞。
如果门禁能够运行某个已声明的行,就按常规执行,并将该声明报告为错误声明行发现项。不要仅仅因为流程通过,就接受、忽略或移除该声明:可运行的流程证明 Spec 的说法有误,而这一发现会阻止 pass。
界面协议
| 界面 | 执行方式 | 最低证据要求 |
|---|---|---|
frontend | 通过真实浏览器执行每位参与者的流程;导航或 DOM 发生变化后,重新读取交互状态。 | 关键状态和失败屏幕截图、刷新/深层链接后的独立确认、控制台错误/警告、失败的网络请求。 |
backend | 向运行中的、文档说明的 API 发送真实请求,包括指定的失败模式。 | 准确请求、状态码、负载,以及确认副作用的公开读取。 |
cli | 像操作员实际使用时那样,运行已安装或构建好的命令。 | 命令、退出代码、输出,以及用于确认状态的第二条命令/读取操作。 |
data | 对临时数据库执行真实迁移或同步,并重新运行一次。 | 结构/行检查、保留的差异、副作用和幂等性证据。 |
infra | 通过符合部署方式的安全临时/试运行路径执行或检查。 | 命令输出、生成的配置/状态,以及一致性差距。 |
docs | 不依赖文档未说明的知识,按照目标读者的方式遵循文档操作。 | 尝试过的命令、结果,以及缺失或误导的步骤。 |
前端全面检查
当声明了 frontend 时,矩阵中必须涵盖以下所有内容:
- 视口和输入: 使用具名断点,或移动端 375×812、平板 768×1024 和桌面端 1440×900。在每种尺寸下完成主要流程;检查内容裁切、元素重叠、水平滚动、仅使用键盘操作、焦点可见性,以及关键屏幕在 200% 缩放时的表现。
- 状态和恢复: 检查 PRD 中的空状态、加载状态、成功状态、验证状态和错误状态。异步操作应及时反馈;失败时应保留输入;恢复操作应提供明确的下一步。
- 无障碍性和语言: 输入项和控件应有无障碍名称;标题和焦点顺序应连贯;不能只依赖颜色传达信息;图像应有适当的替代文本;产品文案应使用规范中的术语。
- 运行时健康状况: 检查过的流程不应留下无法解释的控制台错误/警告或失败的请求。如有条件,应运行针对性的无障碍性或 Lighthouse 审计;如无法运行,应说明原因。
对于 TUI,应将同样的检查映射到小型/大型/极端尺寸的终端、键盘流程、焦点/跟随行为、不依赖颜色的可读性、输出对齐,以及干净的中断处理。
沙箱内的浏览器驱动程序
当由守护进程支持的浏览器工具(如 agent-browser)无法在 Run
沙箱内启动时,如果会话提供了 chrome-devtools MCP 服务,则由 gate 通过该服务驱动浏览器,因为该服务运行在 Agent
沙箱之外。记录每一行浏览器检查所用的驱动程序。两种驱动程序都不可用时,该行标记为 blocked (environment: <error>),不得循环重试。
5. 对结果和发现进行分类
为每一行指定且仅指定一种最终状态:
pass:预期的可观察结果、独立确认、持久性检查和证据均已具备;fail:观察到的产品行为与规范矛盾,或发布了 Non-Goal;blocked (environment: <cause>):gate 环境导致该流程无法继续;附上证明、解除阻塞所需的确切操作,以及可用的等效观察或监督证据;blocked (finding: <id> — waits on <named failure>):某项发现阻止该流程达到预期结果;指出该行所依赖的失败检查或行为,然后关联该发现及受影响的证据;blocked (declared: <criterion>):由于相应的运行前 Spec 声明中所述原因,该行无法继续;关联该声明、gate 无法到达目标的证明,以及可满足该条件的明确人工操作;skipped:仅当 gate 明确为部分 gate 或基于风险进行删减时允许;说明原因及仍未验证的内容。
按用户影响对每项发现进行分类:
Blocks-Completion:用户无法完成能够带来价值的流程;Data-Loss:用户数据遭到破坏、损坏或被悄然丢弃;Trust-Damage:相互矛盾、无法访问或无法恢复的行为削弱了用户信心;Friction:流程虽能完成,但伴随可避免的困惑、延迟或重复操作;Cosmetic:呈现方式存在差异,但不影响完成流程或信任。
对于每项失败,记录用户角色、流程步骤、预期行为和实际行为、从公开入口开始的复现步骤、证据、影响等级以及受影响的行。在将发现命名为新问题前,先搜索之前的报告;对于重复出现或回归的症状,应标明其重复或回归情况,不要将其拆分为独立问题。
修复代码前,先完成计划中的全面检查。构建发生变化后,剩余的比较即失效。修复后,启动新一轮测试或明确标记构建版本,重新运行静态 gate、失败的流程以及一个相邻的 canary。
6. 关闭报告
使用以下结构:
---
spec: <slug>
date: YYYY-MM-DD
build: <audited-commit-or-artifact>
auditing_binary: "<version-and-build-identity>"
auditor_staleness: "<state>: <reason>" # state is current|stale|unknown; the reason names the signal that answered, such as commit ancestry or the declared tree version
user_flow_binary: "<the --version line of the binary that ran public-CLI rows>"
status: in-progress # in-progress | closed
verdict: pending # pending | pass | fail | partial
rows_blocked_environment: 0
rows_blocked_finding: 0
rows_blocked_declared: 0
surfaces: [frontend, backend]
---
# QA report — <feature>
## Scope and environment
## Static gate
## Results
| # | Status | Provenance |
| - | --- | --- |
For each row, put the story or criterion, actor, surface, steps, and evidence
in a block below the table.
## Findings
## Blocked and skipped
## Coverage
## Final verdict
以 status: closed 结束,并机械地应用以下判定:
- 若任何行失败,或门禁发现某行声明错误,则判定为
fail; - 若没有行失败且声明均正确,但有任何行被声明为受阻、发现为受阻或已跳过,QA Task 定义了部分运行,或环境受阻的行缺少等效的观察证据或监督证据,则判定为
partial; - 若每个可运行行均已通过,每个环境受阻行均记录了原因及等效的观察证据或监督证据,没有任何行被声明为受阻、发现为受阻或已跳过,声明均正确,且所有证据路径均有效,则判定为
pass。rows_blocked_environment计数非零本身不会阻止判定为pass;rows_blocked_finding或rows_blocked_declared计数非零则会阻止。
将 rows_blocked_environment、rows_blocked_finding 和
rows_blocked_declared 设为各类受阻原因对应行的准确数量。
每份已关闭报告都必须保留这三个键,即使任何计数为零。
若报告包含第 1 节中的前置条件拒绝行,还应包含 rows_blocked_precondition,其值为来源标记为 precondition 的 blocked 行的准确数量,并包含 precondition_check 和 precondition_reason。已执行到矩阵的门禁不得写入这三个键中的任何一个:拒绝行是额外的结构,并非每份报告都必须包含的第四个计数。
若结果为 pass,或为符合 QA 结算表定义的合格声明 partial,则门禁允许准备 Pull Request。对于其他结果,请说明重新运行前必须更改或核实的内容。在守护进程分配的 Roundfix QA 步骤中,写入报告,但绝不要提交或推送;QA 报告的提交由守护进程负责。分配的步骤也可能在沙箱中运行:若工作区之外的操作因权限错误而失败(例如写入 $HOME 或嵌套工具状态),应立即将其归类为环境导致,将受影响的行标记为 blocked (environment: <error>),然后继续——切勿反复重试被沙箱拒绝的操作——并在环境记录中注明哪些检查需要在具有完整访问权限的会话中进行。若 Run 沙箱的网络策略拒绝了外部证据查找,则写入 blocked (environment: network denied: <host>),并记录尝试访问的主机及 outside-evidence row 来源项;注明未能访问该来源,并应用上述单条规则判定:若 Pull Request 行针对每项控制都有等效证据,仍可判定为 pass;若没有,则报告以 partial 结案,且在没有其他未满足行时符合资格。
决策示例
- UI 显示“已保存”,但刷新后记录消失:
fail、Data-Loss;乐观提示不能作为证明。 - 完整套件通过,但真正的 OAuth 需要由人工控制的账户,且不存在等效的受监督证据:受影响的行标记为
blocked (environment: human-controlled account),总体结果为partial,并提供确切的人工验证步骤。 - Spec 声明无法执行真实发布,并且 gate 证明其隔离环境无法执行这一不可逆操作:对应的行标记为
blocked (declared: publish the real release),rows_blocked_declared递增,判定结果为partial。 - Spec 声明某个旅程无法执行,但 gate 能够运行它:运行该旅程,记录一项错误声明行的问题,并返回
fail,即使该旅程的产品行为通过也一样。 - 治理检查失败,但不影响可运行应用的行为:只阻塞那些依赖该指定检查的治理行,并继续执行功能旅程。
- 外部证据行列出的仓库不在此环境中:如果 Run 沙箱拒绝网络访问,则记录
blocked (environment: network denied: <host>),注明尝试访问的主机,并添加outside-evidence row来源项;这表示未能访问该来源,且绝不能据此判定为符合条件的部分通过。如果 Spec 自身的授权禁止此项查询,则记录blocked (environment: <subject> prohibited by Spec authorization);<subject>用授权文本中的措辞说明授权禁止的事项,例如网络查询时使用network,长度为 1 至 80 个字符,且不得包含括号。保留相同的来源项,并在报告中注明授权文本;这也绝不能据此判定为符合条件的部分通过。任何其他被阻塞的外部证据行仍会阻止 Pull Request 准备工作。将其计入rows_blocked_environment——绝不能用 Spec 编写的演练替代。 - 提示词指定了一个 Open Pull Request,且只读观察证明它已获批准、已接受 Merge-Ready,并且具有评审制品沿袭关系:无需提交、推送或更改 Pull Request 的权限,即可让这些 Pull Request 旅程通过。
- 任务 Result 指明某项单元测试通过,同时组装后的浏览器旅程也在刷新后仍然保留状态,并附有屏幕截图:认可该任务标准,并依据实时证据判定用户故事行通过。
归档记录的结果
在 QA Report 中撰写一个 ## Outcome 段落,说明归档记录将记载的结果。在 Delivery Queue Run 中,使用 roundfix archive <slug> --plan --offline 列出切分项及其候选内容。QA Task 绝不会联系评审者。检查候选内容是否包含可复用的知识,并在归档时通过 --promote <path> 将已确认的知识复制到 QA 提交中的 docs/references/。
反模式
- 仅凭测试、源代码检查、路由渲染或乐观 UI 就判定通过。
- 仅在运行后编写报告,导致无法恢复运行且遗漏未覆盖的行。
- 因为快乐路径通过,就跳过失败状态、行为探测或响应式检查。
- 捕获数百张屏幕截图,而不是目标状态和差异。
- 将环境拒绝视为代码失败,或在未记录原因且没有等效证据的情况下给予认可。
- 将失败弱化为备注、让行保持待处理状态,或在没有矩阵行的情况下声称已覆盖。
- 在整个检查过程中进行修复,却仍声称每一行都是针对同一个构建运行的。