--- name: slice-closeout description: 交付期第 11 步,切片收口:主代理自跑类型检查与 lint,对照 spec 验收标准与不变式逐条核,结构读数与差分变异只打开工包写的关键位置,去 AI slop,在真实产物上跑一次;整程改动的全量读数在全部切片收口之后、交付声明之前做一次;全部用确定性上下文验收,不做对抗性评审。一个切片的 TDD 实施完成、全部切片收口完,或人说「收口」「验收这个切片」「全量读数」时加载。切片还在实施中不加载。 --- # 切片收口 收口是验收,不是挑刺。拿到的上下文是 spec、不变式、本切片的改动和读数,核的是承诺的有没有做到、守的有没有守住。填[收口清单](closeout-checklist.md),六步按顺序,前一步红了不进下一步。 ## 两个尺度 结构读数与差分变异分两个尺度做,和 `context-pack` 的信息包正好反过来:信息包开工前整理一次全量,之后每个切片只补增量;读数每个切片只守关键位置,整程的改动等全部切片收口之后、交付之前全量跑一次。每片都按全部改动跑,同一批函数会被一片一片地重算,算的大多又不是错了代价大的地方,推进被拖慢;关键位置每片守住,其余的最后一次补齐。 | 尺度 | 什么时候 | 范围 | 基线 | | --- | --- | --- | --- | | 切片收口 | 每个切片收口的第 3、4 步 | 开工包写的关键位置:其中本片改动的函数,加上点名的关键文件 | 本切片的起点提交;修复轮是上一轮收口头 | | 一程全量 | 全部切片收口之后、交付声明之前,一次 | 整程改动的全部函数;多包仓库按改动的成员各一次 | spec 抬头的起点提交 | 关键位置是本切片碰到的、错了代价最大的地方:判据与不变式的守护点(spec 不变式对照「由什么守」那一列)、状态转移、并发、失败与清理分支(spec 失败语义表)、对外契约的边界。开工包的「技术细节设计与验证」写明它们的路径和为什么关键。本片没有就写「无关键位置:<理由>」,第 3、4 步写「本片不需要:无关键位置」,留给一程全量。 ## 顺序 ### 1 类型检查与 lint,再跑改动成员的合入检查 主代理自己在候选头上跑,命令从项目接入点取。不论代码由谁写,都不采用写者报告的结果。有失败先修,不进下一步。 接着跑接入点「改动成员的合入检查」:与项目合入检查同口径、只限本片改动的成员,含它们的全部测试层(慢的那层也在内)。合入检查是最贵的一道,不该是第一个发现问题的地方;改动成员自己的检查在这里先过,合入检查才只剩跨成员的组合问题。接入点没给这一格时跑项目的合入检查本身,并在收口记录里写明。 ### 2 对照 spec 与不变式 只判 spec 已经写明的验收标准和不变式对照。每一条结论是达到或未达到;未达到的写明缺哪份证据或哪件产物。spec 没写的标准不补。某条未达到时不先重跑:写明根因在哪一处,修完只复跑这处改动能影响的检查。先答三问:应然、实然、差异。做法加载技能 `think-ought-vs-is`。形式化过的性质核契约测试是否在、是否变红过;按精化对照表逐行核,每一行有守它的类型或结果变体,也有用例。实现有没有遵守已采用的架构、技术设计与规格:问题要带反例,或指出缺的行为,测试通过不代替这一面。 本程更正过的裁定与 spec 原文冲突的,另列「冲突项」:写裁定、与之冲突的 spec 原句。冲突项不是达到或未达到,由主代理改 spec 后再核。 证据要贴出来:命令、输出尾部、测量摘要或日志摘要。只写结论不算证据。每个结论带证据,证据要真的检验了它声称检验的东西。做法加载技能 `think-evidence`。 证据要能检出错误的结果,否则通过不说明什么: - 判据取自契约与 spec,不取被测实现自己报的结果;只断言「回调被调用了」不算验证了行为。 - 测量用的工具与夹具自己也要能检出错误:先确认它在故意做坏的输入上会报。不在夹具里补造产品行为再用它证明产品具备这个行为。 - 解析子进程输出的测试固定会改变输出字节的设置(颜色、语言区域、哈希缩写长度),再在不利的设置下跑一次。 - 有限模型检查、替身上的测试、真实组合上的运行分开陈述;普通的成功退出不等于证明成立。 - 不降阈值、不删断言、不跳过用例、不把预期的错误标成成功来换通过。 第 3、4 步跑得久时放后台,等完成通知,不循环等。等的时候只做下一片的开工包,不动下一片的代码;读数出来有要处置的,先回来处置完这一片。 ### 3 结构读数 只摘本切片关键位置里改动函数的圈复杂度、认知复杂度与 CRAP,用本技能自带的 [`scripts/metrics.mjs`](scripts/metrics.mjs),定义见[结构读数与差分变异的模型](metrics-model.md)。范围用 `--scope <关键位置的路径,…>`,覆盖率文件从项目接入点取,基线是本切片的起点提交。读数是抽查线索,CRAP 低不表示行为正确。CRAP 与圈复杂度一样有提醒线与红线(`--crap ,`,与 `--cc` 同形),线从项目接入点取。多包仓库可一次给几份覆盖文件(`--lcov a,b,c`),同一源文件取各行、各分支命中的并。同一文件里重名的函数按名字加序号配基线,配不上的记「拿不准」、不报变差。表格全部列出,变差的(过线且新出现或比基线涨了)标「!」排在前面:收口逐条交代这些;原来就过线、没有变差的照列不解释,不替历史交代。命令失败时交回命令和输出,不手算冒充工具读数。改动不在度量范围内时写「未覆盖」,逐个列出未覆盖的文件,并给出标明是手算的替代证据。依赖方向的违规与环原样列出。 ### 4 差分变异 有关键位置就要跑,用本技能自带的 [`scripts/mutation.mjs`](scripts/mutation.mjs)。项目的范围从项目接入点取,本片的范围从开工包的关键位置取。它由 Stryker 执行,项目要用 vitest 并装着 Stryker(项目自己装,或 `npm ci --prefix <本技能的 scripts 目录>`);一轮耗时约等于初跑一遍加每个变异体只跑碰到它的用例,超时的变异体一轮后单独复核,抬头写「复核 N」。别的运行器暂不支持,工具会退出并说明。多包仓库(根下没有 vitest 配置、每个成员各有一份)按改动的成员各调一次 `--project <成员目录>`,输出路径仍相对 Git 根;成员的用例导入自己的构建产物(例如 `dist/`)时再加 `--build-command <构建命令>`,否则用例跑的是没插桩的产物,变异体碰不到。只在本切片的最终头上跑一次,修复轮一开始,就停掉旧头上还在跑的。缺省只变异改动的行:差分变异回答「这次的改动有没有被测试守住」,函数里没改的行测的是改动之前的代码,它们的存活是旧欠账,按本步的规则要逐条处置,等于把圈外的活推给这次改动。要顺手还一个函数的旧账时才给 `--whole-units`,范围扩到改动行所在单元自己的全部行,这一轮的存活里有多少落在没改的行上,收口记录里分开写。范围只取关键位置:`--scope <关键位置的路径,…>` 只收其中的改动行,基线第一轮用本切片的起点提交,修复轮用 `--base <上一轮收口头>` 收窄到修复改的行;判据与状态机所在的关键文件用 `--files <路径,…>` 点名,点名的文件全部有名单元都打,不论改没改。起变异前先在沙箱里用同一份配置把测试跑一遍,没跑通时整个成员测量无效并列出没跑通的测试文件;只跑某一层测试用 `--vitest-config <那一层的配置>`;给了 `--whole-units` 时,单元自有行数超过 `--unit-max-lines`(缺省 200)只变异改动行与包住它的最小嵌套函数,抬头写明收窄了哪些;超时复核最多 `--recheck-limit` 个(缺省 8),超出的记「超时未复核」,成员测量无效;有运行时报错的位点时成员测量无效。增量由 Stryker 负责:工具、vitest 版本与配置文件的内容、测试文件、Stryker 版本、给了构建命令时的构建产物(`--build-output`,缺省 `dist`)都没变时沿用上一轮的结果,抬头写「沿用 N」;指纹变了整轮重跑。先只列范围(`--plan-only`),核对后再正式运行。存活与无覆盖原样列出;命令失败交回命令和输出,不把未跑写成零存活。把存活判为等价变异之前,先说明能否改夹具或代码让它可判定。 ### 5 去 AI slop 加载技能 `pstack:deslop`;写的散文加载技能 `pstack:unslop`。 ### 6 证明可用 在真实产物上跑一次本切片的行为:起服务、跑命令、打开页面,读实际的值。加载技能 `pstack:principle-prove-it-works`。 ## 一程全量读数 全部切片收口之后、交付声明之前,在一程的最终头上做一次。 - 基线是 spec 抬头的起点提交(`--base <起点>`)。范围是项目接入点给的范围,不按关键位置收窄。多包仓库按整程改动的成员,差分变异各调一次 `--project`。Stryker 的增量照旧生效。 - 读法与切片收口相同:变差逐条交代,存活与无覆盖原样列出。关键位置之外的存活三选一:补用例;判为等价变异(先说明能否让它可判定);不变式排除的记「不变式排除」,不补。改了代码的回到第 1 步,再在新头上用 `--base <全量读数时的头>` 复跑改动的行。 - 项目的合入检查已经按整程改动跑同样的读数时,接入点写明那条命令,这里贴它的结果,不跑第二遍。 - 记录填[收口清单](closeout-checklist.md)末尾的「一程全量读数」,交付声明指向它。 ## 两轮不收敛 连续两轮出现同一类阻断时,停止逐个补洞。先质疑判定本身:这一类承诺是否正确、合理、本阶段必需。不必需的缩窄承诺,工具对这一类不再下确定结论,交人工复核或按未达到记录。仍要修的,先答三问:应然、实然、差异。做法加载技能 `think-ought-vs-is`。再按 `delegate-brief`「升级」换路径。 ## 不做的 - 不做对抗性评审,不派子代理「找问题」「挑战设计」。对抗性评审的上下文和实施方不对齐,带来过度设计、过度优化和鸡毛蒜皮。 - 不为读数本身优化。圈复杂度高但行为正确、测试在,先记下,不在收口里重构;要重构归收口之前的做好一轮。 - 不接受放宽或删除已有断言的改动,spec 明确要求改写的除外。 ## 常驻要求 对自己和派出去的子代理同样按[写代码的四条](../single-session-dev-flow/SKILL.md#写代码的四条)要求代码品味、架构优雅与代码整洁;判设计与实践的完整依据加载技能 `ub-mode`;TypeScript 的写法加载技能 `pstack:typescript-best-practices`。 做好一轮在收口之前单独做完,按结构读数、差分变异与品味核对单收拾本片的改动,核对单的答案写进收口清单。先做对,再单独做好一轮。做法加载技能 `think-right-then-well`。 ## 外部技能 - 对照标准的评审加载技能 `mattpocock-skills:code-review`,只用它的标准面。 - 派只读评审者时按 `delegate-brief`「派评审者时」。 ## 收口之后 切片级清理按 `cleanup`:只判断还要不要,下一片用不到的直接删,要用的原地留,Git 历史与汇报件就是留痕,所以不打包,也不在文档里写「待删」「已删」。向人汇报按 `progress-artifacts`,提交。提交后按 plan 的「文档维护表」逐份核对给人看的文档:进度总览、本片收口记录、上下文包增量、汇报件、本片改了行为时对应的正式文档;更新的只改该文档对应的节,不整份重写,改完把维护表该行的「最近更新」改成本次时刻。最后跑 `spec-and-plan` 的 `scripts/check-plan.mjs` 核 plan 与 spec 的结构,退出 0 才算收口。