peaks-loop 4.0.50 → 4.0.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README-en.md +1 -1
  3. package/README.md +1 -1
  4. package/dist/cli/commands/share-commands.js +85 -18
  5. package/dist/services/dispatch/sub-agent-dispatcher.d.ts +11 -30
  6. package/dist/services/dispatch/sub-agent-dispatcher.js +5 -48
  7. package/dist/services/ide/adapters/claude-code-adapter.js +0 -1
  8. package/dist/services/ide/adapters/codex-adapter.js +1 -2
  9. package/dist/services/ide/adapters/cursor-adapter.js +1 -2
  10. package/dist/services/ide/adapters/hermes-adapter.js +1 -2
  11. package/dist/services/ide/adapters/openclaw-adapter.js +1 -2
  12. package/dist/services/ide/adapters/qoder-adapter.js +1 -2
  13. package/dist/services/ide/adapters/tongyi-lingma-adapter.js +1 -2
  14. package/dist/services/ide/adapters/trae-adapter.js +1 -2
  15. package/dist/services/ide/adapters/zcode-adapter.js +0 -1
  16. package/dist/services/ide/ide-types.d.ts +0 -2
  17. package/package.json +5 -5
  18. package/skills/bee/peaks-perf-audit/SKILL.md +2 -2
  19. package/skills/bee/peaks-perf-audit/references/audit-protocol.md +1 -1
  20. package/skills/bee/peaks-prd/SKILL.md +3 -3
  21. package/skills/bee/peaks-prd/references/prd-for-multi-pass.md +1 -1
  22. package/skills/bee/peaks-prd/references/workflow.md +1 -1
  23. package/skills/bee/peaks-qa/SKILL.md +6 -6
  24. package/skills/bee/peaks-qa/references/external-capability-guidance.md +1 -1
  25. package/skills/bee/peaks-qa/references/qa-fanout-contract.md +1 -1
  26. package/skills/bee/peaks-qa/references/reading-handoff-frontmatter.md +2 -2
  27. package/skills/bee/peaks-rd/SKILL.md +2 -2
  28. package/skills/bee/peaks-rd/references/code-reviewer-4dim-hint.md +1 -1
  29. package/skills/bee/peaks-rd/references/external-references.md +1 -1
  30. package/skills/bee/peaks-rd/references/mandatory-perf-baseline.md +1 -1
  31. package/skills/bee/peaks-rd/references/ocr-multilang-1.8.md +2 -2
  32. package/skills/bee/peaks-rd/references/parallel-review-fanout.md +2 -2
  33. package/skills/bee/peaks-rd/references/rd-fanout-contracts.md +11 -8
  34. package/skills/bee/peaks-rd/references/rd-runbook.md +1 -1
  35. package/skills/bee/peaks-rd/references/rd-sub-agent-dispatch.md +7 -7
  36. package/skills/bee/peaks-rd/references/rd-transition-gates.md +1 -1
  37. package/skills/bee/peaks-rd/references/reading-v2-slice-results.md +1 -1
  38. package/skills/bee/peaks-rd/references/v2-12-fanout-collapse.md +7 -5
  39. package/skills/bee/peaks-rd/references/writing-handoff-frontmatter.md +3 -3
  40. package/skills/bee/peaks-reviewer/SKILL.md +1 -1
  41. package/skills/bee/peaks-security-audit/SKILL.md +3 -3
  42. package/skills/bee/peaks-security-audit/references/audit-protocol.md +1 -1
  43. package/skills/bee/peaks-txt/references/context-capsule.md +1 -1
  44. package/skills/bee/peaks-ui/SKILL.md +1 -1
  45. package/skills/peaks-audit/SKILL.md +1 -1
  46. package/skills/peaks-code/SKILL.md +8 -8
  47. package/skills/peaks-code/references/context-governance.md +1 -1
  48. package/skills/peaks-code/references/dag-orchestrator.md +3 -4
  49. package/skills/peaks-code/references/external-references.md +1 -1
  50. package/skills/peaks-code/references/external-skill-invocation.md +2 -2
  51. package/skills/peaks-code/references/fanout-mandatory.md +3 -3
  52. package/skills/peaks-code/references/frontend-only-mode.md +2 -2
  53. package/skills/peaks-code/references/gstack-integration.md +1 -1
  54. package/skills/peaks-code/references/micro-cycle.md +1 -1
  55. package/skills/peaks-code/references/periodic-checkpoint.md +2 -2
  56. package/skills/peaks-code/references/project-scan-checklist.md +1 -1
  57. package/skills/peaks-code/references/resume-detection.md +1 -1
  58. package/skills/peaks-code/references/runbook.md +3 -3
  59. package/skills/peaks-code/references/session-overload-signal-index.md +2 -2
  60. package/skills/peaks-code/references/startup-sequence.md +16 -16
  61. package/skills/peaks-code/references/step-11-memory-sediment.md +2 -2
  62. package/skills/peaks-code/references/sub-agent-dispatch.md +7 -6
  63. package/skills/peaks-code/references/swarm-dispatch-contract.md +1 -1
  64. package/skills/peaks-code/references/workflow-gates-and-types.md +3 -3
  65. package/skills/peaks-code/references/worktree-governance.md +1 -1
  66. package/skills/peaks-final-review/SKILL.md +3 -3
  67. package/skills/peaks-ide/references/audit-log-helper.md +5 -4
  68. package/skills/peaks-resume/SKILL.md +1 -1
  69. package/skills/peaks-slice-decompose/SKILL.md +4 -4
  70. package/skills/peaks-slice-decompose/references/cross-pass-edge-interpretation.md +1 -1
  71. package/skills/peaks-slice-decompose/references/granularity-decision.md +1 -1
  72. package/skills/peaks-slice-decompose/references/v2-schema.md +2 -2
  73. package/skills/peaks-solo/SKILL.md +1 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.0.51 — 2026-09-16 (子代理 await 永远报不出结果 + 49 处"声称有测试守护、测试早已删除" + 9 个恒真断言 + 三个不可达导出)
4
+
5
+ **Highlights**:
6
+
7
+ 1. **`peaks sub-agent await` 永远不可能报告任何结果。** `share-commands.ts` 把 `recordPaths` **硬编码成空数组**,而 `await-batch.ts` 在它为空时**直接短路** —— 于是这个命令每次都走空分支,静默返回。文件自己的注释就承认了这个状态(*"`recordPaths: []` case"*)。现在:新增 `resolveBatchRecords` 扫 `.peaks/_sub_agents/<sid>/dispatch-*.json` 并按 record **自己的 `batchId`** 匹配 —— 用的是 `finalize --batch` 与 `findBatchRecords` **已经在用的同一套约定**,没有另发明路径约定;`dispatchCount` 是真实计数;**零匹配 → `NO_DISPATCH_RECORDS` + exit 1**;ok 信封新增 `unreadableRecords` 与告警,**不可读的候选不再沉默**。反例对照:把 `recordPaths: []` 重新注入 → 新增的 7 个测试**全部失败**;还原 → 7/7 通过。
8
+
9
+ 2. **49 处在文档里声称"由某个测试守护",而那些测试早就被删了 —— 包括守卫自己文件头里写的那次提交删掉的那个。** 引用完整性守卫原先的语料是 `CLAUDE.md` / `.peaks/PROJECT.md` / `README.md` / `.peaks/standards` —— **不含 `skills/**`**,而它的锚点正则里明明列着 `skills/`。把 `skills/**` 加进语料后,**守卫自己吐出 178 处悬空引用 / 64 文件**(此前临时扫描器估的 95 **低了近一倍**,且它归因的 `de0872b7` 只占 11 处)。修了 93 处后剩 85 处,**全部逐条处置、零白名单化**:其中 **43 处引用 21 个测试** —— 1 个删于 `457b9a87`(**正是守卫文件头自称"它存在的理由"的那次提交**)、20 个删于 `f17aa377`(*"delete 559 legacy unit tests"*)、**2 个从未在任何 commit 里存在过**;另有 6 处悬空文档互引。**每一条都逐站改写,不做批量正则替换**("由 X 守护"与"见 X"语义不同)。**残留如实披露而非关闭**:这 49 站现在如实说明"无物钉住它",并逐个点名了真实的覆盖缺口 —— `isDeprecatedReviewer` / `RD_DEPRECATED_REVIEWERS`、prereq 向后兼容形态、G11.5 标题锁、20-次调用节奏、handoff frontmatter(`gateEvidence` 在 `src/` 里**无人读取**)、superpowers 四条款规则、`peaks-ide` 审计日志助手。**未补测试(超出本片范围),也未放宽或删除任何一个。**
10
+
11
+ 3. **9 个 `supportsScope` 断言全部恒真,且唯一消费者是一个循环论证的测试。** 接口声明 `supportsScope(scope: 'project' | 'global')`,9 个 adapter 的实现是 1 个 `() => true` 加 8 个逐字相同、**在类型上必然为真**的表达式 —— 没有一个能返回 `false`。**生产零调用者**:全仓唯一消费者是一个把它"characterise"成两个 scope 都返回 true 的新测试。**删除**(接口 + 9 实现 + 那个测试)。理由:参数类型即值域、无 adapter 只支持单一 scope(9/9 `resolveSettingsFile` 双 scope 对称),且真实属性已被 `adapter-runtime-surfaces.test.ts` 钉住。
12
+
13
+ 4. **两个不可达导出与一处死参数。** `traeCnSubAgentDispatcher` 有导出、有测试、**没注册到任何 adapter**(8 个 adapter 分别注册了 claudeCode / codex / cursor / trae / null —— 且**没有 `trae-cn` adapter**,`IdeId` 里也没有)。它唯一可引的"支持"证据是 postinstall 的目录映射,**不是适配器注册** —— 给它建 adapter 等于**发明一条不存在的接线**,故删除。`awaitByLlmFallback` 生产零调用者(它的"向后兼容测试"只证明它自己存在 —— 与第 3 条同型的循环),且其注释里承诺的目标在 1.3 已兑现,故删除。另报出:`PollDispatchRecordsOptions.ide` **从未被 `pollDispatchRecords` 读取**(既有死表面,属三项目标之外,仅报出未修)。共 15 文件 **+61/−170**。
14
+
15
+ 5. **守卫的射程缺口与被裁掉的候选一起被记录。** 本片为 31 处误报新增了 5 条**带理由**的候选排除规则(省略号族、`./` 消费者根、`<…>` 模板、illustration 提示、`isDocumentRelative`),为 5 处运行时路径新增 `RUNTIME_STATE_PREFIXES`(`_runtime/` / `cron/` / `cache/`,各带理由)—— `OPTIONAL_RUNTIME_PATHS` **未增长(仍 2 条)**。**同时如实报出两个未修的射程缺口**:裸文件名引用(如 `skills-skill-md-naming.test.ts`)与围栏 ` ```bash ` 代码块对候选规则**不可见**。守卫的注入验证含**一条负对照**:同一路径写成 `mappers/…` 保持绿 —— 那正是本片唯一收窄候选集的地方,公开标注以免被读成"全都能抓"。
16
+
3
17
  ## 4.0.50 — 2026-09-16 (自审把 29% 从分母里排除 + 反漂移闸门报 15/15 而只跑了 1 条 + 一条"no exceptions"的红线没有任何执行者 + 五个测试套件从未运行过 + macOS 从未进 CI)
4
18
 
5
19
  **Highlights**:
package/README-en.md CHANGED
@@ -140,7 +140,7 @@ Every lane opens with **one slash command**.
140
140
 
141
141
  | | |
142
142
  | --- | --- |
143
- | **Latest** | [![npm](https://img.shields.io/npm/v/peaks-loop?style=for-the-badge&logo=npm&logoColor=white&color=cb3837)](https://www.npmjs.com/package/peaks-loop) — 4.0.50 (2026-09-16) |
143
+ | **Latest** | [![npm](https://img.shields.io/npm/v/peaks-loop?style=for-the-badge&logo=npm&logoColor=white&color=cb3837)](https://www.npmjs.com/package/peaks-loop) — 4.0.51 (2026-09-16) |
144
144
  | **Domains** | Code (`peaks-code`) · Content (`peaks-content`) · Project health (`peaks-doctor`) · Issue sweep (`peaks-issue-fix-orchestrator`) · Custom SOP (`peaks-sop`) · Cross-domain primitives (`peaks-solo` dispatcher · `peaks-resume` · `peaks-status` · `peaks-test` · `peaks-slice-decompose`) |
145
145
  | **Sediment pool** | `~/.peaks/` local pool · twice-clean runs auto-promote to a bee · broken runs come back for you to redefine · the bee grows with your taste |
146
146
  | **Test suite** | 285+ cases · 4 packages (peaks-loop / peaks-loop-mut / peaks-loop-shared-channel / peaks-loop-shared) · **0 timeouts** · 14 BDD caller-binding edge cases |
package/README.md CHANGED
@@ -140,7 +140,7 @@ npm i -g peaks-loop
140
140
 
141
141
  | | |
142
142
  | --- | --- |
143
- | **最新版本** | [![npm](https://img.shields.io/npm/v/peaks-loop?style=for-the-badge&logo=npm&logoColor=white&color=cb3837)](https://www.npmjs.com/package/peaks-loop) — 4.0.50(2026-09-16) |
143
+ | **最新版本** | [![npm](https://img.shields.io/npm/v/peaks-loop?style=for-the-badge&logo=npm&logoColor=white&color=cb3837)](https://www.npmjs.com/package/peaks-loop) — 4.0.51(2026-09-16) |
144
144
  | **覆盖域** | 代码(`peaks-code`) · 内容(`peaks-content`) · 项目健康(`peaks-doctor`) · 批量修 issue(`peaks-issue-fix-orchestrator`) · 自定义 SOP(`peaks-sop`) · 通用原语(`peaks-solo` 分诊 / `peaks-resume` 续 / `peaks-status` 看 / `peaks-test` 测 / `peaks-slice-decompose` 切片) |
145
145
  | **沉淀池** | `~/.peaks/` 本地池 · 跑两次自动晋升成 bee · 跑翻车让你重定义 · bee 跟着你的口味长 |
146
146
  | **测试套件** | 1096 cases · 4 packages (peaks-loop 1015 / runtime 39 / mut 22 / shared-channel 20) · **CI 首次全绿**(ubuntu + windows) · 14 BDD caller-binding coverage |
@@ -1,5 +1,5 @@
1
- import { realpathSync as realpathSyncNative } from 'node:fs';
2
- import { resolve } from 'node:path';
1
+ import { existsSync, readdirSync, realpathSync as realpathSyncNative } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
3
  import { fail, getErrorMessage, ok } from 'peaks-loop-shared/result';
4
4
  import { addJsonOption, printResult } from '../cli-helpers.js';
5
5
  import { readSharedChannel, writeSharedEntry, SHARED_CHANNEL_SOFT_VALUE_WARN } from 'peaks-loop-shared-channel';
@@ -282,19 +282,39 @@ export function registerAwaitCommand(parent, io) {
282
282
  process.exitCode = 1;
283
283
  return;
284
284
  }
285
- // No record-path index is kept for DAG-dispatched batches yet; the caller
286
- // is expected to have a single shared record directory. We pass the empty
287
- // list — the runner tracks outcomes through its own contract-store writes,
288
- // so the dispatcher reaches no slot and reports no results (see the
289
- // `recordPaths: []` case in tests/unit/services/dispatch/
290
- // sub-agent-dispatchers.test.ts, which pins that empty shape).
291
- const input = {
292
- batchId: options.batch,
293
- dispatchCount: 1,
294
- recordPaths: [],
295
- ...(timeoutMs !== undefined ? { timeoutMs } : {})
296
- };
297
285
  try {
286
+ // Slice 2026-09-16-n1-await-reports: resolve the batch's records from the
287
+ // session's dispatch directory. `recordPaths` used to be hardcoded to
288
+ // `[]`, and `awaitBatch` short-circuits on an empty list
289
+ // (await-batch.ts:134) — so `await` reported zero results and exited 0
290
+ // for every batch, forever. A batch we cannot locate is now a failure,
291
+ // not a success with nothing in it.
292
+ const { readRecord } = await import('../../services/dispatch/dispatch-record-writer.js');
293
+ const scan = resolveBatchRecords({
294
+ projectRoot,
295
+ sessionId: sid,
296
+ batchId: options.batch,
297
+ readOne: readRecord
298
+ });
299
+ if (scan.recordPaths.length === 0) {
300
+ const unreadableNote = scan.unreadable.length > 0
301
+ ? ` ${scan.unreadable.length} dispatch record(s) there could not be read, so they could not be matched to this batch: ${scan.unreadable.join(', ')}`
302
+ : '';
303
+ printResult(io, fail('sub-agent.await', 'NO_DISPATCH_RECORDS', `No dispatch record with batchId=${options.batch} under ${scan.sessionDir}.${unreadableNote}`, {
304
+ ok: false,
305
+ batchId: options.batch,
306
+ sessionDir: scan.sessionDir,
307
+ unreadableRecords: scan.unreadable
308
+ }, [awaitErrorNextActions('NO_DISPATCH_RECORDS')]), asJson);
309
+ process.exitCode = 1;
310
+ return;
311
+ }
312
+ const input = {
313
+ batchId: options.batch,
314
+ dispatchCount: scan.recordPaths.length,
315
+ recordPaths: scan.recordPaths,
316
+ ...(timeoutMs !== undefined ? { timeoutMs } : {})
317
+ };
298
318
  const results = await dispatcher.awaitBatch(input);
299
319
  const summary = summarizeBatchResults(results);
300
320
  printResult(io, ok('sub-agent.await', {
@@ -303,15 +323,18 @@ export function registerAwaitCommand(parent, io) {
303
323
  batchId: options.batch,
304
324
  ide: dispatcher.label,
305
325
  results,
306
- summary
307
- }, [], [
326
+ summary,
327
+ unreadableRecords: scan.unreadable
328
+ }, scan.unreadable.length > 0
329
+ ? [`${scan.unreadable.length} unreadable dispatch record(s) in this session were skipped and are NOT part of the results: ${scan.unreadable.join(', ')}`]
330
+ : [], [
308
331
  // Slice 2026-09-15-s9: corrected. This used to tell users that the
309
332
  // four non-Claude IDEs would report `awaitByLlm: <ide> 1.2 fallback`,
310
333
  // the slice-1.2 marker that slice 1.3 replaced with a real
311
334
  // file-polling await. The text survived because nothing tested it —
312
335
  // no adapter produces that note any more (asserted in
313
- // sub-agent-dispatchers.test.ts), and the only code that still emits
314
- // it, `awaitByLlmFallback`, has no callers.
336
+ // sub-agent-dispatchers.test.ts), and the emitter that produced it,
337
+ // `awaitByLlmFallback`, has since been removed.
315
338
  `Each non-claude-code IDE labels its own results (see the \`note\` field), so a timed-out slot is attributable to the adapter it came from.`
316
339
  ]), asJson);
317
340
  }
@@ -331,6 +354,9 @@ function awaitErrorNextActions(code) {
331
354
  if (code === 'IDE_NOT_SUPPORTED') {
332
355
  return 'Switch to claude-code, or rely on LLM-side await for non-claude-code IDEs in slice 1.3.';
333
356
  }
357
+ if (code === 'NO_DISPATCH_RECORDS') {
358
+ return 'Check --session-id / --project: records live under .peaks/_sub_agents/<sessionId>/. Use the batchId exactly as printed by the dispatch envelope.';
359
+ }
334
360
  return 'See error message; check that --batch matches the dispatch envelope and --timeout is a positive integer ms.';
335
361
  }
336
362
  /**
@@ -351,6 +377,47 @@ function safeRecordPath(p) {
351
377
  return p;
352
378
  }
353
379
  }
380
+ /**
381
+ * Slice 2026-09-16-n1-await-reports: which on-disk records belong to a batch.
382
+ *
383
+ * Reuses the conventions already in the repo instead of inventing a new one:
384
+ * - records live at `.peaks/_sub_agents/<sid>/dispatch-<rid>-<ts>.json`
385
+ * (`dispatchRecordPath`, src/services/security/safe-settings-path.ts);
386
+ * - a record's batch is its own `batchId` field, written by
387
+ * `writeInitialDispatchRecord`. The `--batch` branch of `finalize` (below)
388
+ * and `findBatchRecords` in heartbeat-watch-command.ts resolve a batch the
389
+ * same way: scan the session dir, filter `dispatch-*.json`, compare field.
390
+ *
391
+ * `unreadable` lists the `dispatch-*.json` candidates whose batch could NOT be
392
+ * determined. The caller must not fold them into "no such batch": a record it
393
+ * cannot read is not evidence that the batch is empty.
394
+ */
395
+ function resolveBatchRecords(input) {
396
+ const sessionDir = resolve(input.projectRoot, '.peaks', '_sub_agents', input.sessionId);
397
+ const recordPaths = [];
398
+ const unreadable = [];
399
+ if (!existsSync(sessionDir))
400
+ return { sessionDir, recordPaths, unreadable };
401
+ for (const name of readdirSync(sessionDir)) {
402
+ // `active-dispatches.json` (the index) and `batch-<uuid>.counter.json` are
403
+ // not records; neither carries a `version`, so `readRecord` would reject
404
+ // them as "version mismatch". Same filter as the `--batch` branch below.
405
+ if (!name.startsWith('dispatch-') || !name.endsWith('.json'))
406
+ continue;
407
+ const recordPath = safeRecordPath(join(sessionDir, name));
408
+ let batchId;
409
+ try {
410
+ batchId = input.readOne(recordPath).batchId;
411
+ }
412
+ catch {
413
+ unreadable.push(recordPath);
414
+ continue;
415
+ }
416
+ if (batchId === input.batchId)
417
+ recordPaths.push(recordPath);
418
+ }
419
+ return { sessionDir, recordPaths, unreadable };
420
+ }
354
421
  export function registerFinalizeCommand(parent, io) {
355
422
  addJsonOption(parent
356
423
  .command('finalize')
@@ -139,22 +139,13 @@ export declare const claudeCodeSubAgentDispatcher: SubAgentDispatcher;
139
139
  * stays the same; only the per-IDE wiring breaks (intentionally).
140
140
  */
141
141
  export declare const traeSubAgentDispatcher: SubAgentDispatcher;
142
- /**
143
- * Trae-CN dispatcher. Mirrors Trae's shape with a separate label so
144
- * the CLI's IDE detection can distinguish a Trae install region
145
- * (Trae-CN differs in skill install path / log location only;
146
- * dispatch surface is identical per slice #011 framework rule).
147
- *
148
- * Slice 1.3: real `awaitBatch` — same polling core as Trae.
149
- */
150
- export declare const traeCnSubAgentDispatcher: SubAgentDispatcher;
151
142
  /**
152
143
  * Codex (OpenAI CLI IDE) dispatcher.
153
144
  *
154
145
  * Slice #13 noted Codex's sub-agent tool name is TBD; per slice #009
155
146
  * rationale, the dispatcher mirrors Claude Code's shape so the
156
- * adapter contract stays uniform. Slice 1.3 promotes Codex from
157
- * `awaitByLlmFallback` to a real `awaitBatch` (file-based polling,
147
+ * adapter contract stays uniform. Slice 1.3 promotes Codex from the
148
+ * 1.2 marker to a real `awaitBatch` (file-based polling,
158
149
  * Codex default 45s — Codex's documented heartbeat is slightly
159
150
  * slower per slice #13 R-3).
160
151
  */
@@ -201,36 +192,26 @@ export declare class SubAgentNotSupportedError extends Error {
201
192
  */
202
193
  export declare function awaitClaudeCodeBatch(input: SubAgentAwaitBatchInput): Promise<readonly SubAgentBatchResult[]>;
203
194
  /**
204
- * Slice 1.3 — shared per-IDE polling core for trae / trae-cn / codex /
205
- * cursor. Same polling loop shape as `awaitClaudeCodeBatch`, with
206
- * per-IDE default timeout + note prefix. The 4 IDEs differ only in
207
- * (a) `defaultTimeoutMs` (Trae / Trae-CN / Cursor = 30s, Codex = 45s
208
- * per slice #13 R-3) and (b) the `note` label surfaced when an IDE
209
- * times out (so 1.4 dogfood can attribute a timeout to the right
210
- * IDE).
195
+ * Slice 1.3 — shared per-IDE polling core for trae / codex / cursor.
196
+ * Same polling loop shape as `awaitClaudeCodeBatch`, with per-IDE
197
+ * default timeout + note prefix. The 3 IDEs differ only in
198
+ * (a) `defaultTimeoutMs` (Trae / Cursor = 30s, Codex = 45s per slice
199
+ * #13 R-3) and (b) the `note` label surfaced when an IDE times out
200
+ * (so 1.4 dogfood can attribute a timeout to the right IDE).
211
201
  *
212
- * MVP rationale (per Karpathy §2 Simplicity First): the 4 IDEs
202
+ * MVP rationale (per Karpathy §2 Simplicity First): the 3 IDEs
213
203
  * currently share the same file-based polling transport. The only
214
204
  * per-IDE distinction is the timeout + label. Future per-IDE
215
205
  * divergence (real IPC / shell hooks) is a 1.4 dogfood concern —
216
206
  * here we keep the dispatcher interface uniform while each IDE's
217
- * `awaitBatch` is now a real implementation rather than the 1.2
218
- * `awaitByLlmFallback` marker.
207
+ * `awaitBatch` is a real implementation.
219
208
  */
220
209
  export interface PollDispatchRecordsOptions {
221
- readonly ide: 'trae' | 'trae-cn' | 'codex' | 'cursor';
210
+ readonly ide: 'trae' | 'codex' | 'cursor';
222
211
  readonly defaultTimeoutMs: number;
223
212
  readonly notePrefix: string;
224
213
  }
225
214
  export declare function pollDispatchRecords(input: SubAgentAwaitBatchInput, opts: PollDispatchRecordsOptions): Promise<readonly SubAgentBatchResult[]>;
226
- /**
227
- * 1.2 fallback for trae / trae-cn / codex / cursor. Deprecated by
228
- * slice 1.3 — kept exported for legacy callers + back-compat tests
229
- * (the 1.2 marker is still a valid envelope shape; the 1.4 dogfood
230
- * tests can compare the marker note vs the 1.3 real note to verify
231
- * per-IDE attribution).
232
- */
233
- export declare function awaitByLlmFallback(input: SubAgentAwaitBatchInput, ide: string): Promise<readonly SubAgentBatchResult[]>;
234
215
  /**
235
216
  * In-process awaiter registration hook (used by `peaks sub-agent dispatch`
236
217
  * to attach a resolver; not part of the dispatcher public surface).
@@ -106,38 +106,13 @@ export const traeSubAgentDispatcher = {
106
106
  notePrefix: 'trae 1.3 real awaitBatch'
107
107
  })
108
108
  };
109
- /**
110
- * Trae-CN dispatcher. Mirrors Trae's shape with a separate label so
111
- * the CLI's IDE detection can distinguish a Trae install region
112
- * (Trae-CN differs in skill install path / log location only;
113
- * dispatch surface is identical per slice #011 framework rule).
114
- *
115
- * Slice 1.3: real `awaitBatch` — same polling core as Trae.
116
- */
117
- export const traeCnSubAgentDispatcher = {
118
- label: 'trae-cn',
119
- supportsRole: (role) => role.length > 0,
120
- buildToolCall: ({ role, prompt, requestId }) => ({
121
- name: 'Task',
122
- args: {
123
- subagent_type: 'general-purpose',
124
- description: `${role} for rid=${requestId}`,
125
- prompt,
126
- },
127
- }),
128
- awaitBatch: async (input) => pollDispatchRecords(input, {
129
- ide: 'trae-cn',
130
- defaultTimeoutMs: 30_000,
131
- notePrefix: 'trae-cn 1.3 real awaitBatch'
132
- })
133
- };
134
109
  /**
135
110
  * Codex (OpenAI CLI IDE) dispatcher.
136
111
  *
137
112
  * Slice #13 noted Codex's sub-agent tool name is TBD; per slice #009
138
113
  * rationale, the dispatcher mirrors Claude Code's shape so the
139
- * adapter contract stays uniform. Slice 1.3 promotes Codex from
140
- * `awaitByLlmFallback` to a real `awaitBatch` (file-based polling,
114
+ * adapter contract stays uniform. Slice 1.3 promotes Codex from the
115
+ * 1.2 marker to a real `awaitBatch` (file-based polling,
141
116
  * Codex default 45s — Codex's documented heartbeat is slightly
142
117
  * slower per slice #13 R-3).
143
118
  */
@@ -245,14 +220,14 @@ export async function awaitClaudeCodeBatch(input) {
245
220
  // service. The back-compat envelope shape is preserved (one
246
221
  // `SubAgentBatchResult` per record path) so the S3 characterization
247
222
  // test stays green; the underlying loop is identical to the trae /
248
- // trae-cn / codex / cursor wrappers below. The new typed outcome
223
+ // codex / cursor wrappers below. The new typed outcome
249
224
  // lives on the unified service; the S4 fail-fast test pins it.
250
225
  //
251
226
  // Slice 2026-07-30-nightshift: claude-code does NOT use a
252
227
  // per-IDE note prefix. The 1.4 dogfood contract says the done
253
228
  // note is `null` (raw outcome) and the failed note is the raw
254
- // `outcome` string with no prefix. The 4 non-Claude IDEs
255
- // (trae / trae-cn / codex / cursor) prefix the note with their
229
+ // `outcome` string with no prefix. The 3 non-Claude IDEs
230
+ // (trae / codex / cursor) prefix the note with their
256
231
  // per-IDE label so cross-IDE attribution is visible to the LLM.
257
232
  // Passing no `notePrefix` here keeps the legacy contract.
258
233
  const unified = await awaitBatchUnified(input.dispatchCount, input.recordPaths, input.timeoutMs, { defaultTimeoutMs: 60_000 });
@@ -299,24 +274,6 @@ export async function pollDispatchRecords(input, opts) {
299
274
  });
300
275
  return unified.results;
301
276
  }
302
- /**
303
- * 1.2 fallback for trae / trae-cn / codex / cursor. Deprecated by
304
- * slice 1.3 — kept exported for legacy callers + back-compat tests
305
- * (the 1.2 marker is still a valid envelope shape; the 1.4 dogfood
306
- * tests can compare the marker note vs the 1.3 real note to verify
307
- * per-IDE attribution).
308
- */
309
- export async function awaitByLlmFallback(input, ide) {
310
- const startedAt = Date.now();
311
- return input.recordPaths.map((p, i) => ({
312
- dispatchIndex: i,
313
- recordPath: p,
314
- status: 'timeout',
315
- durationMs: 0,
316
- note: `awaitByLlm: ${ide} 1.2 fallback (real impl in 1.3)`
317
- }));
318
- void startedAt;
319
- }
320
277
  /**
321
278
  * In-process awaiter registration hook (used by `peaks sub-agent dispatch`
322
279
  * to attach a resolver; not part of the dispatcher public surface).
@@ -591,7 +591,6 @@ export const CLAUDE_CODE_ADAPTER = {
591
591
  const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
592
592
  return join(root, '.claude', 'settings.json');
593
593
  },
594
- supportsScope: () => true,
595
594
  },
596
595
  envVar: 'CLAUDE_PROJECT_DIR',
597
596
  hookEvent: 'PreToolUse',
@@ -54,8 +54,7 @@ export const CODEX_ADAPTER = {
54
54
  resolveSettingsFile: (scope, projectRoot) => {
55
55
  const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
56
56
  return join(root, '.codex', 'settings.json');
57
- },
58
- supportsScope: (scope) => scope === 'project' || scope === 'global'
57
+ }
59
58
  },
60
59
  envVar: 'CODEX_PROJECT_DIR', // UNVERIFIED — see slice #13 PRD R-3; fallback path still works
61
60
  hookEvent: 'pre_tool_use', // UNVERIFIED — Codex lowercase snake_case pattern; re-verify against Codex docs
@@ -44,8 +44,7 @@ export const CURSOR_ADAPTER = {
44
44
  resolveSettingsFile: (scope, projectRoot) => {
45
45
  const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
46
46
  return join(root, '.cursor', 'settings.json');
47
- },
48
- supportsScope: (scope) => scope === 'project' || scope === 'global'
47
+ }
49
48
  },
50
49
  envVar: 'CURSOR_PROJECT_DIR', // UNVERIFIED — see slice #12 PRD R-2; fallback path still works
51
50
  hookEvent: 'beforeShellExecution', // UNVERIFIED — see slice #12 PRD R-1; re-verify against Cursor's published hook schema
@@ -29,8 +29,7 @@ export const HERMES_ADAPTER = {
29
29
  resolveSettingsFile: (scope, projectRoot) => {
30
30
  const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
31
31
  return join(root, '.hermes', 'settings.json');
32
- },
33
- supportsScope: (scope) => scope === 'project' || scope === 'global'
32
+ }
34
33
  },
35
34
  envVar: 'HERMES_PROJECT_DIR', // UNVERIFIED
36
35
  hookEvent: 'PreToolUse', // UNVERIFIED
@@ -22,8 +22,7 @@ export const OPENCLAW_ADAPTER = {
22
22
  resolveSettingsFile: (scope, projectRoot) => {
23
23
  const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
24
24
  return join(root, '.openclaw', 'settings.json');
25
- },
26
- supportsScope: (scope) => scope === 'project' || scope === 'global'
25
+ }
27
26
  },
28
27
  envVar: 'OPENCLAW_PROJECT_DIR', // UNVERIFIED
29
28
  hookEvent: 'PreToolUse', // UNVERIFIED
@@ -33,8 +33,7 @@ export const QODER_ADAPTER = {
33
33
  resolveSettingsFile: (scope, projectRoot) => {
34
34
  const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
35
35
  return join(root, '.qoder', 'settings.json');
36
- },
37
- supportsScope: (scope) => scope === 'project' || scope === 'global'
36
+ }
38
37
  },
39
38
  envVar: 'QODER_PROJECT_DIR', // UNVERIFIED
40
39
  hookEvent: 'PreToolUse', // UNVERIFIED
@@ -24,8 +24,7 @@ export const TONGYI_LINGMA_ADAPTER = {
24
24
  resolveSettingsFile: (scope, projectRoot) => {
25
25
  const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
26
26
  return join(root, '.lingma', 'settings.json');
27
- },
28
- supportsScope: (scope) => scope === 'project' || scope === 'global'
27
+ }
29
28
  },
30
29
  envVar: 'TONGYI_LINGMA_PROJECT_DIR', // UNVERIFIED
31
30
  hookEvent: 'PreToolUse', // UNVERIFIED
@@ -50,8 +50,7 @@ export const TRAE_ADAPTER = {
50
50
  resolveSettingsFile: (scope, projectRoot) => {
51
51
  const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
52
52
  return join(root, '.trae', 'settings.json');
53
- },
54
- supportsScope: (scope) => scope === 'project' || scope === 'global'
53
+ }
55
54
  },
56
55
  envVar: 'TRAE_PROJECT_DIR',
57
56
  hookEvent: 'beforeToolCall', // VERIFIED against Trae 1.x fixture — slice 009-009-2026-06-07-trae-dogfood (2026-06-07); fixture at tests/fixtures/trae/trae-1x-payload.json
@@ -159,7 +159,6 @@ export const ZCODE_ADAPTER = {
159
159
  const root = scope === 'global' ? homedir() : resolve(projectRoot ?? homedir());
160
160
  return join(root, '.zcode', 'settings.json');
161
161
  },
162
- supportsScope: (scope) => scope === 'project' || scope === 'global',
163
162
  },
164
163
  // UNVERIFIED — z-code 桌面应用未公开 `ZCODE_PROJECT_DIR` env var,这里
165
164
  // 用占位符,RD 阶段假设 z-code 借用了 Anthropic-compatible 协议并可能
@@ -41,8 +41,6 @@ export interface IdeSettingsLocation {
41
41
  readonly localSettingsFileName?: string;
42
42
  /** 解析出 settings.json 绝对路径 */
43
43
  resolveSettingsFile(scope: 'project' | 'global', projectRoot: string | undefined): string;
44
- /** 该 IDE 是否支持此 scope(用于清晰报错) */
45
- supportsScope(scope: 'project' | 'global'): boolean;
46
44
  }
47
45
  /**
48
46
  * Slim IDE adapter 描述。每 IDE 一个静态常量(无需 DI)。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "peaks-loop",
3
- "version": "4.0.50",
3
+ "version": "4.0.51",
4
4
  "description": "Loop Engineering CLI — workflow primitive / loop guards / evaluators / slice orchestration",
5
5
  "author": "SquabbyZ",
6
6
  "keywords": [
@@ -102,10 +102,10 @@
102
102
  "picomatch": "4.0.4",
103
103
  "yaml": "^2.9.0",
104
104
  "zod": "^4.4.3",
105
- "peaks-loop-mut": "0.1.48",
106
- "peaks-loop-internal-runtime": "0.0.35",
107
- "peaks-loop-shared-channel": "0.0.52",
108
- "peaks-loop-shared": "0.0.84"
105
+ "peaks-loop-internal-runtime": "0.0.36",
106
+ "peaks-loop-mut": "0.1.49",
107
+ "peaks-loop-shared": "0.0.85",
108
+ "peaks-loop-shared-channel": "0.0.53"
109
109
  },
110
110
  "devDependencies": {
111
111
  "@changesets/cli": "2.31.1",
@@ -184,8 +184,8 @@ required fixes deduped by `(file, line, hint)`).
184
184
  - Schema: `.peaks/project-scan/audit-output-schema.md`
185
185
  - Service: `src/services/audit-independent/perf-audit-service.ts`
186
186
  - CLI: `src/cli/commands/perf-audit-commands.ts`
187
- - Tests: `tests/unit/services/audit-independent/perf-audit-service.test.ts`
188
- - Companion: `skills/peaks-security-audit/SKILL.md` (symmetric structure)
187
+ - Tests: none — the unit suite for the service was deleted in `f17aa377`.
188
+ - Companion: `skills/bee/peaks-security-audit/SKILL.md` (symmetric structure)
189
189
 
190
190
  ## Karpathy alignment
191
191
 
@@ -170,4 +170,4 @@ section is the source of new sediment rows; the format is:
170
170
  - Service: `src/services/audit-independent/perf-audit-service.ts`
171
171
  - Template: `.peaks/project-scan/perf-template.md`
172
172
  - Schema: `.peaks/project-scan/audit-output-schema.md`
173
- - Companion: `skills/peaks-security-audit/references/audit-protocol.md`
173
+ - Companion: `skills/bee/peaks-security-audit/references/audit-protocol.md`
@@ -147,7 +147,7 @@ PRD 模板在原 7 段基础上,增加 4 个**质量前置必填块**(user 写,A
147
147
  3. **UI 装配意图**(页面模式 / 关键交互 / 信息密度;**不画 ASCII 视觉**,视觉由选定的开源组件库自动决定)
148
148
  4. **上游基线**(fork 场景必填:当前 fork 基于哪个 tag / 偏离度 / 业务 patch 集)
149
149
 
150
- **反伪选择原则:** user 不参与技术决策(库/框架/语言/性能/安全/架构),只写业务/产品意图。AI 据业务场景块选库选架构。详见 `.peaks/memory/peaks-loop-prd-template-design.md`。
150
+ **反伪选择原则:** user 不参与技术决策(库/框架/语言/性能/安全/架构),只写业务/产品意图。AI 据业务场景块选库选架构。详见 `.peaks/memory/peaks-cli-prd-template-design.md`。
151
151
 
152
152
  ## Project-scan gate (v2.11.0 D3 — read before brainstorm)
153
153
 
@@ -264,7 +264,7 @@ Use gstack as a concrete workflow reference for the product-facing parts of `Thi
264
264
 
265
265
  ## Authenticated product document workflow
266
266
 
267
- When the source PRD is an authenticated web document such as Feishu/Lark, use the Playwright MCP headed-browser surface rather than unauthenticated fetch tools. Chrome DevTools MCP is a secondary surface that only connects to an already-running Chrome (`--remote-debugging-port=9222`); it does not launch a browser on its own. The canonical browser workflow lives in `peaks-code/references/browser-workflow.md`; the rules below are the PRD-specific application.
267
+ When the source PRD is an authenticated web document such as Feishu/Lark, use the Playwright MCP headed-browser surface rather than unauthenticated fetch tools. Chrome DevTools MCP is a secondary surface that only connects to an already-running Chrome (`--remote-debugging-port=9222`); it does not launch a browser on its own. The canonical browser workflow lives in `../../peaks-code/references/browser-workflow.md`; the rules below are the PRD-specific application.
268
268
 
269
269
  1. Confirm Playwright MCP is installed: check the LLM tool list for any Playwright MCP entry in the LLM tool list. If absent, the user installs via `claude mcp add playwright -- npx @playwright/mcp@latest` (Claude Code) or the IDE-native install command. Do not hand-edit `.claude/settings.json`.
270
270
  2. Before navigation, verify the user-provided document URL uses `https:` and belongs to an approved Feishu/Lark tenant domain such as `*.feishu.cn`, `*.larksuite.com`, `*.larksuite.com.cn`, or a project-configured tenant. Reject `file:`, `data:`, `javascript:`, `http:`, localhost, loopback, link-local, private IP, and raw IP hosts unless the user explicitly approves a controlled local test target.
@@ -294,7 +294,7 @@ Summarize business context only when it changes implementation priority, scope,
294
294
 
295
295
  When the user explicitly says the target is a frontend project, transform the product document into frontend implementation inputs before RD starts:
296
296
 
297
- > **v2.15.0+ 校准:** UI 视觉 = 开源组件库装配(shadcn / Radix / antd / mui),不是原创设计。prd 不画 ASCII 视觉,只描述"页面模式 + 关键交互 + 信息密度"。AI 据此选组件库 + 装配。详见 `.peaks/memory/peaks-loop-prd-template-design.md`。
297
+ > **v2.15.0+ 校准:** UI 视觉 = 开源组件库装配(shadcn / Radix / antd / mui),不是原创设计。prd 不画 ASCII 视觉,只描述"页面模式 + 关键交互 + 信息密度"。AI 据此选组件库 + 装配。详见 `.peaks/memory/peaks-cli-prd-template-design.md`。
298
298
 
299
299
  1. identify target pages, routes, components, forms, tables, modals, empty/loading/error states, permissions, data dependencies, edge cases, and affected user flows;
300
300
  2. separate frontend-only work from API/backend联调 assumptions;
@@ -46,4 +46,4 @@ Single-service feature → single-pass PRD. Do not force a Pass 2 layer if the w
46
46
  - Pass 2 ACs each name a file path under one of the Pass 1 services.
47
47
  - A `peaks slice audit-prd --rid <rid>` (when present) returns zero cross-cutting flags.
48
48
 
49
- See `../peaks-slice-decompose/SKILL.md` for the envelope contract and `../peaks-rd/references/reading-v2-slice-results.md` for the RD-side read.
49
+ See `../../../peaks-slice-decompose/SKILL.md` for the envelope contract and `../../peaks-rd/references/reading-v2-slice-results.md` for the RD-side read.
@@ -15,7 +15,7 @@ When the product source is an authenticated Feishu/Lark/wiki document:
15
15
  7. Do not persist login URLs, redirect URLs, cookies, request or response headers, session tokens, tokens, storage state, QR payloads, raw browser state, raw network logs, browser traces, or screenshots/logs containing PII or SSO/MFA material into artifacts; redact sensitive evidence before writing `.peaks` outputs.
16
16
  8. If access remains blocked, record only a redacted document identifier, a sanitized state category such as `login-required`, `mfa-required`, or `access-denied`, and the exact user action needed.
17
17
 
18
- Canonical browser workflow: `../../peaks-code/references/browser-workflow.md`.
18
+ Canonical browser workflow: `../../../peaks-code/references/browser-workflow.md`.
19
19
 
20
20
  ## Implementation-oriented analysis
21
21
 
@@ -47,7 +47,7 @@ denied again after a long pause. That is the gate resetting, not you regressing.
47
47
 
48
48
  > **Read once at the top of this file; the rest of the skill is written against it.**
49
49
 
50
- The `.peaks/` workspace is partitioned by a **single scope axis** (session-id, at `.peaks/_runtime/<sessionId>/...`) with a nested **sub-agent axis** under `.peaks/_sub_agents/<sessionId>/...`. Use `<sessionId>` placeholders (NEVER bare `<sid>`). The peaks-loop change-id axis was removed in slice `2026-06-29-change-id-root-removal`; reviewable artifacts now live under `.peaks/_runtime/<sessionId>/<role>/...` only. OpenSpec's independent `openspec/changes/<change-id>/` vocabulary (L4) is preserved untouched. CLI mapping: session-id → `peaks session *`; sub-agent → `peaks sub-agent *`. Regression test `tests/unit/skills/skills-skill-md-naming.test.ts` enforces (a) zero bare `<sid>`, (b) every `.peaks/_runtime/<X>/` has an axis label, (c) this callout is present.
50
+ The `.peaks/` workspace is partitioned by a **single scope axis** (session-id, at `.peaks/_runtime/<sessionId>/...`) with a nested **sub-agent axis** under `.peaks/_sub_agents/<sessionId>/...`. Use `<sessionId>` placeholders (NEVER bare `<sid>`). The peaks-loop change-id axis was removed in slice `2026-06-29-change-id-root-removal`; reviewable artifacts now live under `.peaks/_runtime/<sessionId>/<role>/...` only. OpenSpec's independent `openspec/changes/<change-id>/` vocabulary (L4) is preserved untouched. CLI mapping: session-id → `peaks session *`; sub-agent → `peaks sub-agent *`. Normative by documentation only — the guard that once asserted (a) zero bare `<sid>`, (b) an axis label on every runtime path, and (c) this callout's presence was deleted in `457b9a87`. Comply by convention.
51
51
 
52
52
  ## peaks-context auto-build (v3.0)
53
53
 
@@ -92,7 +92,7 @@ The canonical scope dir for this request is provided as `envelope.data.scopeDir`
92
92
 
93
93
  When this skill is launched as a sub-agent via `peaks sub-agent dispatch <role>` (then the LLM executes the returned toolCall) from `peaks-code`, the following sections of THIS skill are **suspended** for the sub-agent run: Session id, Skill presence, Workspace initialization, Mode selection, Statusline install. The sub-agent must NOT call `peaks request init` (Code already initialised the slot), and must write `.peaks/_runtime/<sessionId>/qa/test-cases/<rid>.md` with test cases that link to PRD acceptance items. Return only a compact JSON envelope.
94
94
 
95
- > **v2.15.0+ 校准:** 每个 slice 完成,user 必介入做**业务审阅**(4-5 项业务/产品清单:业务流程 / 需求覆盖 / 边界 case / UI 装配 / 能合入下版吗),**不是技术审阅**。业务审过 → 进 final;业务不通过 → 返工。详见 `.peaks/memory/peaks-loop-slice-review-and-qa-perspective.md`。
95
+ > **v2.15.0+ 校准:** 每个 slice 完成,user 必介入做**业务审阅**(4-5 项业务/产品清单:业务流程 / 需求覆盖 / 边界 case / UI 装配 / 能合入下版吗),**不是技术审阅**。业务审过 → 进 final;业务不通过 → 返工。详见 `.peaks/memory/peaks-cli-slice-review-and-qa-perspective.md`。
96
96
 
97
97
  → see `references/qa-sub-agent-dispatch.md` for the full contract + hard prohibitions.
98
98
 
@@ -106,7 +106,7 @@ Project-level security + perf plans live at `.peaks/_runtime/<sessionId>/qa/secu
106
106
 
107
107
  When peaks-qa is the **main loop** (i.e. it is the active skill and is about to run its own sub-agent dispatch, rather than being a sub-agent itself), it fans out only the **business verification** sub-agent: `qa-business`. Security and performance review are **NOT** peaks-qa's responsibility in v2.11.0 — they are owned by peaks-rd's 4-way audit fan-out (code-review + security-review + perf-baseline + karpathy-review) and the rd-side evidence files (`audit/security-<rid>.md`, `audit/perf-<rid>.md`). peaks-qa reads those files by reference; it does NOT re-do them.
108
108
 
109
- > **v2.15.0+ 校准:** `qa-business` 只跑业务/产品视角的 6 项验收清单(业务流程 / 需求覆盖 / 边界 case / UI 装配 / 异常态语调 / 能上线吗),**不跑技术指标**(覆盖率 / 性能 / 安全)。技术指标由 RD 4-way fan-out 自决,QA 只读 `audit/security-<rid>.md` + `audit/perf-<rid>.md`。详见 `.peaks/memory/peaks-loop-slice-review-and-qa-perspective.md`。
109
+ > **v2.15.0+ 校准:** `qa-business` 只跑业务/产品视角的 6 项验收清单(业务流程 / 需求覆盖 / 边界 case / UI 装配 / 异常态语调 / 能上线吗),**不跑技术指标**(覆盖率 / 性能 / 安全)。技术指标由 RD 4-way fan-out 自决,QA 只读 `audit/security-<rid>.md` + `audit/perf-<rid>.md`。详见 `.peaks/memory/peaks-cli-slice-review-and-qa-perspective.md`。
110
110
 
111
111
  If the PRD or project warrants it, subdivide `qa-business` further into roles like `qa-business-api` / `qa-business-frontend` / `qa-business-regression`. Subdivision must stay ≤ 2 levels deep (RL-4).
112
112
 
@@ -182,7 +182,7 @@ QA must generate test cases, not merely inspect existing ones. Every QA invocati
182
182
 
183
183
  **Pre-drafted test cases (slice 004 optimization):** when peaks-rd's 4-way parallel fan-out ran a `qa-test-cases-writer` sub-agent, the test plan is pre-drafted at `.peaks/_runtime/<sessionId>/qa/test-cases/<rid>.md` and shipped through the rd:qa-handoff gate. QA main loop is aware of this and treats the pre-drafted file as the canonical starting point. **Missing** the pre-drafted file (sub-agent failed, or the slice was a config/docs/chore that did not fan out) → QA drafts it inline as before, falling back to the standard generation flow.
184
184
 
185
- > **v2.15.0+ 校准:** 业务验证层(user 必审) = 4-5 项业务/产品清单。技术验证层(AI 自决) = 覆盖率 / P99 / 安全扫描 / 自动化。两层解耦:技术不过 → AI 内部重跑;业务不过 → user 反馈修。user **不看**技术指标。详见 `.peaks/memory/peaks-loop-slice-review-and-qa-perspective.md` G5。
185
+ > **v2.15.0+ 校准:** 业务验证层(user 必审) = 4-5 项业务/产品清单。技术验证层(AI 自决) = 覆盖率 / P99 / 安全扫描 / 自动化。两层解耦:技术不过 → AI 内部重跑;业务不过 → user 反馈修。user **不看**技术指标。详见 `.peaks/memory/peaks-cli-slice-review-and-qa-perspective.md` G5。
186
186
 
187
187
  → see `references/test-case-generation.md` for the full format + acceptance-linkage contract.
188
188
 
@@ -198,7 +198,7 @@ QA cannot pass a change until the report contains evidence for every applicable
198
198
 
199
199
  If Playwright MCP is unavailable, the LLM checks its own tool list for the Playwright MCP server entry; if absent, the LLM tells the user the install command (`claude mcp add playwright -- npx @playwright/mcp@latest` for Claude Code) and marks the gate blocked with the missing capability. Screenshots, logs, manual steps, or other tools must not substitute for the mandatory frontend browser gate. Do not silently downgrade frontend validation to API-only testing.
200
200
 
201
- > **v2.15.0+ 校准:** 存量项目无 UT 兜底,QA 验证必须有"轻量回归"(G14):5-10 分钟跑 10 条关键路径(关键路径来源:prd 业务场景块 / 老板强调的流程 / 历史事故 / G13 影响面扫描),**不跑完整 E2E 1-2 小时**。上线后必须走"观察期"(G15):灰度 → 监控 → 反馈聚合 → 紧急修复 → 修复回灌关键路径(防下次再犯)。详见 `.peaks/memory/peaks-loop-fast-iteration-quality-loop.md`。
201
+ > **v2.15.0+ 校准:** 存量项目无 UT 兜底,QA 验证必须有"轻量回归"(G14):5-10 分钟跑 10 条关键路径(关键路径来源:prd 业务场景块 / 老板强调的流程 / 历史事故 / G13 影响面扫描),**不跑完整 E2E 1-2 小时**。上线后必须走"观察期"(G15):灰度 → 监控 → 反馈聚合 → 紧急修复 → 修复回灌关键路径(防下次再犯)。详见 `.peaks/memory/peaks-cli-fast-iteration-quality-loop.md`。
202
202
 
203
203
  ## Local intermediate artifacts
204
204
 
@@ -297,4 +297,4 @@ QA contracts to assert on the L2 surface (a minimal acceptance test set):
297
297
 
298
298
  ## Sub-role detached mode (Phase C, slice 2026-08-10)
299
299
 
300
- QA sub-roles (`qa-business`, `qa-perf`, `qa-security`, `qa-business-api`, `qa-business-frontend`, `qa-business-regression`) accept `--mode detached --vendor <vendor>` for parallel test-case execution. Detached mode spawns a real OS process via `peaks sub-agent dispatch --mode detached`, isolated from the orchestrator session. Use detached mode when the test-case writer's expected runtime exceeds 60s OR it processes ≥ 20 source files. The default remains `in-process` (backward compat; existing 106+ dispatch tests untouched). When `--mode detached` is used, the child QA agent receives the `<peaks-auto-compact>` marker (corrected 2026-09-13, rid `2026-09-13-defects-e`): it asks the child to persist durable state to `.peaks/_runtime/<sid>/detached/<rid>/compact/<n>.json` before its own harness compacts the session — the child cannot compact itself, and nothing is spliced back into its prompt. See `peaks-code/references/sub-agent-dispatch.md` §"Detached Mode" for the full contract.
300
+ QA sub-roles (`qa-business`, `qa-perf`, `qa-security`, `qa-business-api`, `qa-business-frontend`, `qa-business-regression`) accept `--mode detached --vendor <vendor>` for parallel test-case execution. Detached mode spawns a real OS process via `peaks sub-agent dispatch --mode detached`, isolated from the orchestrator session. Use detached mode when the test-case writer's expected runtime exceeds 60s OR it processes ≥ 20 source files. The default remains `in-process` (backward compat; existing 106+ dispatch tests untouched). When `--mode detached` is used, the child QA agent receives the `<peaks-auto-compact>` marker (corrected 2026-09-13, rid `2026-09-13-defects-e`): it asks the child to persist durable state to `.peaks/_runtime/<sid>/detached/<rid>/compact/<n>.json` before its own harness compacts the session — the child cannot compact itself, and nothing is spliced back into its prompt. See `skills/peaks-code/references/sub-agent-dispatch.md` §"Detached Mode" for the full contract.
@@ -5,5 +5,5 @@
5
5
  - Playwright MCP is the required path for controlled headed browser and E2E validation (it launches a headed browser on demand). The LLM runtime exposes the Playwright tools under its own server-and-tool namespace (the Playwright MCP); QA invokes them by name from the LLM's tool list. (peaks-loop no longer auto-installs MCPs as of slice #016; the user runs `claude mcp add playwright -- npx @playwright/mcp@latest` themselves when the tool list is empty.)
6
6
  - Chrome DevTools MCP is an optional secondary surface for CDP inspection (console, network, performance) of an already-running Chrome started with `--remote-debugging-port=9222`; it does NOT launch a browser on its own. The LLM invokes Chrome DevTools MCP tools directly when present in the tool list.
7
7
  - Agent Browser can support browser walkthroughs, but never submit forms, purchase, delete, or mutate authenticated state without explicit confirmation.
8
- - Canonical browser workflow (URL allow-list, login handoff, sanitization rules, tool mapping): `peaks-code/references/browser-workflow.md`.
8
+ - Canonical browser workflow (URL allow-list, login handoff, sanitization rules, tool mapping): `skills/peaks-code/references/browser-workflow.md`.
9
9
  - If Playwright MCP is not installed and the user does not authorize installation, mark frontend browser validation blocked; screenshots, logs, manual steps, or other tools must not substitute for the mandatory headed browser gate.