@thincoder/core 0.10.10 → 0.10.11

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 (70) hide show
  1. package/CHANGELOG.md +28 -2
  2. package/agent/completion.mjs +4 -1
  3. package/agent/family-tools.mjs +3 -3
  4. package/agent/helpers.mjs +13 -11
  5. package/agent/light-round.mjs +1 -4
  6. package/agent/record-results.mjs +6 -1
  7. package/agent/setup-reminders.mjs +1 -1
  8. package/agent/unbooked-pen.mjs +62 -0
  9. package/agent-tools/advisor.mjs +15 -31
  10. package/agent-tools/audit-block.mjs +16 -62
  11. package/agent-tools/batch-gate.mjs +10 -18
  12. package/agent-tools/context.mjs +1 -1
  13. package/agent-tools/design-token.mjs +9 -2
  14. package/agent-tools/digest-budget.mjs +2 -0
  15. package/agent-tools/doc-check.mjs +3 -14
  16. package/agent-tools/manifest.mjs +3 -4
  17. package/agent-tools/review-facts.mjs +35 -81
  18. package/agent-tools/settings.mjs +65 -5
  19. package/agent-tools/spawn-gates.mjs +68 -31
  20. package/agent-tools/subagent-async.mjs +6 -3
  21. package/agent-tools/subagent-spawn.mjs +42 -61
  22. package/agent-tools/subagent.mjs +15 -15
  23. package/browser/launch.mjs +14 -3
  24. package/browser/queue.mjs +31 -3
  25. package/browser/session.mjs +4 -0
  26. package/config-defaults.mjs +8 -0
  27. package/config-presets.mjs +1 -1
  28. package/config.mjs +60 -11
  29. package/doc-check/anchors.mjs +10 -9
  30. package/doc-check/index.mjs +27 -23
  31. package/doc-check/paragraph.mjs +119 -0
  32. package/fts-text.mjs +4 -1
  33. package/ledger-cmd.mjs +17 -4
  34. package/ledger-db.mjs +14 -8
  35. package/ledger.mjs +1 -1
  36. package/manifest-discovery.mjs +19 -10
  37. package/manifest.mjs +17 -5
  38. package/model-context.mjs +98 -0
  39. package/model-specs.mjs +110 -37
  40. package/package.json +2 -2
  41. package/permission.mjs +3 -1
  42. package/prompts/advisor-design.md +15 -0
  43. package/prompts/common.md +1 -0
  44. package/prompts/discipline-engineering.md +27 -6
  45. package/prompts/discipline-normal.md +7 -7
  46. package/prompts/persona-eng-designer.md +16 -4
  47. package/prompts/persona-engineering.md +16 -27
  48. package/provider/anthropic.mjs +127 -4
  49. package/provider/core.mjs +16 -6
  50. package/provider/google.mjs +8 -8
  51. package/provider/list-models.mjs +37 -5
  52. package/provider/normalize.mjs +2 -2
  53. package/provider/responses.mjs +7 -7
  54. package/provider/sse.mjs +6 -5
  55. package/proxy-transport.mjs +5 -3
  56. package/proxy.mjs +2 -1
  57. package/session-guard.mjs +7 -1
  58. package/session-lifecycle.mjs +23 -8
  59. package/session-segments.mjs +1 -0
  60. package/session-slot-claims.mjs +4 -0
  61. package/session-slot-write.mjs +6 -0
  62. package/session-slots-manifest.mjs +4 -0
  63. package/token-ttl.mjs +10 -0
  64. package/tool-docs/advisor.md +24 -5
  65. package/tool-docs/doc_check.md +23 -16
  66. package/tool-docs/ledger.md +2 -2
  67. package/tool-docs/subagent.md +41 -16
  68. package/tools/web-deepseek.mjs +26 -11
  69. package/doc-check/version.mjs +0 -84
  70. package/doc-check/width.mjs +0 -87
package/model-specs.mjs CHANGED
@@ -7,8 +7,10 @@
7
7
  * config-presets.mjs — the core's single preset face, re-exported by config.mjs.)
8
8
  */
9
9
 
10
+ import { listedContextTokens, manualContextK, warnContextOnce } from "./model-context.mjs"
11
+
10
12
  /**
11
- * Known model capability spec table (prefix match, longer first).
13
+ * Known model capability spec table (prefix match within the model's last "/"-segment, longer first).
12
14
  * Used for compaction threshold derivation, continuation protocol selection, and capability-aware optimization.
13
15
  *
14
16
  * context: context window (tokens)
@@ -42,8 +44,6 @@ const MODEL_SPECS = [
42
44
  ["deepseek-v4-flash-vision-exp", { context: 1_000_000, maxOutput: 384_000, thinking: true, prefixMode: true, thinkApi: "type", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"], tempRange: [0, 2], multimodal: true }],
43
45
  // Kimi series
44
46
  ["kimi-k3", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, multimodal: true, thinkApi: "effort", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"] }],
45
- // Qwen router prefixes model IDs with provider namespace: kimi/kimi-k3 → kimi-k3 (IK7K4V)
46
- ["kimi/kimi-k3", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, multimodal: true, thinkApi: "effort", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"] }],
47
47
  // Kimi For Coding endpoint uses the short model ID "k3" (same specs as kimi-k3) — IK5VGJ
48
48
  ["k3", { context: 1_000_000, maxOutput: 131_072, thinking: true, partialMode: true, multimodal: true, thinkApi: "effort", reasoningEcho: "required", reasoningEffortEnum: ["low", "high", "max"] }],
49
49
  // kimi-for-coding / k3-256k / kimi-for-coding-highspeed(Kimi For Coding 端点三名 · 2026-09-25 批 §14.2 #7-9 · 活测 = §1.4 服务端 200 /models):
@@ -249,36 +249,32 @@ const MODEL_SPECS = [
249
249
  ]
250
250
  const DEFAULT_SPEC = { context: 128_000, maxOutput: 32_000 }
251
251
 
252
- /** Look up spec by model name prefix (case-insensitive), conservative default for unknown models.
252
+ /** 查表规则(单源 = PROVIDER.md §6.9 · 用户 2026-10-23 17:34 裁定「匹配 / 拆分以后的最后一段」):
253
253
  *
254
- * Vendor-namespace stripping (2026-09-04;末段形 2026-10-10——用户裁定「只取最后一段」):第三方
255
- * token 市场(roapi/new-api/one-api/aiproxy 聚合网关)惯例在模型名前加厂商前缀(zhipu/glm-5.3、
256
- * openai/gpt-4o),链式网关下可叠多段(测试服务器 qwen/ZHIPU/GLM-5.3)。完整名未命中且含 "/" 时,
257
- * 取最后一个 "/" 之后的段再按前缀匹配一次——单段 ZHIPU/GLM-5.3 → glm-5.3(两法等价)、多段
258
- * qwen/ZHIPU/GLM-5.3 → glm-5.3 均命中真实规格,不再降级 128K 默认。kimi/kimi-k3 的显式 alias
259
- * 行保留为文档锚(发送路径 provider.core isRouter 依赖含 "/" 判定),通用机制已覆盖同类。 */
254
+ * 模型名匹配 = **末段唯一**:按 "/" 拆开只拿最后一段查表(单段名末段 = 自身),段内仍按前缀匹配
255
+ * (`glm-5.3-flashx` 命中 `glm-5.3` 行)。完整名**不再先扫**——旧法把 `glm-5.3/zzz` 误判成
256
+ * `glm-5.3`(1M 上下文 ⇒ 压缩永不触发 ⇒ 溢出);新法取末段 `zzz` ⇒ 未命中 ⇒ DEFAULT_SPEC + 告警。
257
+ * 聚合网关惯例 `vendor/model`(roapi/new-api/one-api/aiproxy)因此天然命中:`zhipu/glm-5.3` /
258
+ * 链式 `qwen/ZHIPU/GLM-5.3` ⇒ `glm-5.3`。末段为空(`foo/`)⇒ 未命中(空串不做匹配)。 */
260
259
  const warnedModels = new Set() // warn once per model name — spec lookup runs on every request (hot path)
261
260
  // Pre-sorted once at module scope — spec lookup runs on every request (agent, provider core,
262
261
  // context, auto-think, TUI rendering); re-sorting per call was wasteful.
263
262
  const SORTED_SPECS = [...MODEL_SPECS].sort((a, b) => b[0].length - a[0].length)
264
263
 
265
- /** Single table lookup shared by specForModel / specMatch — prefix match (case-insensitive)
266
- * with vendor-namespace stripping. Returns null on a miss (DEFAULT_SPEC is the caller-side
267
- * fallback and is deliberately NOT returned here — `matched` needs the miss itself).
264
+ /** Single table lookup shared by specForModel / specMatch — prefix match (case-insensitive) on the
265
+ * model's LAST "/"-segment only (末段唯一 · PROVIDER.md §6.9). The full name is NOT scanned first:
266
+ * `glm-5.3/zzz` takes `zzz` and misses (the old full-name-first scan mis-hit `glm-5.3`).
267
+ * Returns null on a miss (DEFAULT_SPEC is the caller-side fallback and is deliberately NOT
268
+ * returned here — `matched` needs the miss itself).
268
269
  * 非串 model(外端脏载 / 手工档)归一等价空串 ⇒ 未命中 ⇒ 调用侧 DEFAULT_SPEC(#638 查表全性:
269
270
  * 下游无类型门——`.toLowerCase` 只认串,非串归一后本函数恒 total,同 providerSpec 文档句)。 */
270
271
  function lookupSpec(model) {
271
272
  const m = (typeof model === "string" ? model : "").toLowerCase()
272
- for (const [prefix, spec] of SORTED_SPECS) {
273
- if (m.startsWith(prefix.toLowerCase())) return spec
274
- }
275
- // Vendor-namespace strip: take the LAST "/"-separated segment — retry the prefix match on it.
276
273
  const slash = m.lastIndexOf("/")
277
- if (slash > 0) {
278
- const bare = m.slice(slash + 1)
279
- for (const [prefix, spec] of SORTED_SPECS) {
280
- if (bare.startsWith(prefix.toLowerCase())) return spec
281
- }
274
+ const bare = slash >= 0 ? m.slice(slash + 1) : m // 末段唯一;单段名 = 自身
275
+ if (!bare) return null // 末段为空 ⇒ 未命中(空串不做前缀匹配)
276
+ for (const [prefix, spec] of SORTED_SPECS) {
277
+ if (bare.startsWith(prefix.toLowerCase())) return spec
282
278
  }
283
279
  return null
284
280
  }
@@ -312,27 +308,104 @@ export function specMatch(model) {
312
308
  return { spec: DEFAULT_SPEC, matched: false }
313
309
  }
314
310
 
311
+ /** Context window 展示串,**收 token**(核单源——三端读面同一格式器出词,R31④):`128K` ∥ `1M`。
312
+ * 本批由 CLI 私有 `fmtContextK` 上提——三端各自拼串会漂。服务链上 L3/L4 值(token)的展示;
313
+ * 人工声明(K)的展示走 `formatDeclaredContextK`,一个换算方向一个入口,不混用。 */
314
+ export function formatContextK(tokens) {
315
+ if (tokens >= 1_000_000) return `${Math.round(tokens / 1_048_576)}M`
316
+ return `${Math.round(tokens / 1024)}K`
317
+ }
318
+
319
+ /** 人工声明上下文的展示串,**收 K**(`modelContext` ∥ 旧键 `context` 的存储单位):`128` ⇒ `128K`、
320
+ * `1024` ⇒ `1M`。K→token 的展示换算**只此一处**(内部转 token 再交给 `formatContextK`)——三端读面
321
+ * 把已声明原值直接交进来,不自己换算、不自己拼 K 串(PROVIDER.md §6.15「读面回显」· R31④)。
322
+ * 前提:`k` 是先过 `positiveInt` 的正整数(与读链同一谓词)——非法值本函数不复判。 */
323
+ export function formatDeclaredContextK(k) {
324
+ return formatContextK(k * 1024)
325
+ }
326
+
315
327
  /**
316
- * providerSpec(provider) — spec with a provider-level context override (PROVIDER.md §15, 2026-09-02).
328
+ * providerSpec(provider) — spec with the context resolution chain (PROVIDER.md §6.15, 2026-10-23
329
+ * 五级链). `providerSpecResolution` = the single traversal; `providerSpec` = `resolution.spec`.
330
+ *
331
+ * Chain (first available wins) — 单位差只在这里乘一次,且**解析乘点**全仓唯一(本函数内 L1 ∥ L2 共用;
332
+ * 展示面的另一次在 `formatDeclaredContextK`,不回写解析值):
333
+ * L1 `providers[].modelContext[模型]`(人工声明,K) → ×1024
334
+ * L2 `providers[].context`(旧键,K;仅未被 L1 点名的模型) → ×1024(同一乘点)
335
+ * L3 `/models` 自报(三字段任一,token) → 不乘
336
+ * L4 `MODEL_SPECS` 静态表(token) → 不乘
337
+ * L5 `DEFAULT_SPEC`(128K,token) → 不乘
338
+ *
339
+ * L2 位于 L3 之上:旧键也是人工声明(2026-10-23 17:30 裁定「人工声明 > 列表」),只是粒度不对、
340
+ * 单位事故出过两次;`modelContext` 点名的模型一律走 L1,L2 不参与。L2 实际被用到时告警一次
341
+ * (provider 名去重——装载期告警会在 L1 已完全覆盖时误报)。
317
342
  *
318
- * providers[].context is configured in K units (128 = 128K = 131072 tokens) and overrides the
319
- * MODEL_SPECS value for THIS provider only — the same model can have different real context
320
- * windows on different endpoints (official vs local deployment). The ×1024 conversion happens
321
- * HERE and nowhere else.
343
+ * 量级守卫(R32):L1 ∥ L2 的 K 值换算后 > 静态表 ×4(表无行时 = 4 × 128K)⇒ 照用 + 告警一次,
344
+ * 不截断、不拒写——与 L3 的 4× 守卫对称。守卫住在核里:值可能来自手改 JSON(不经任何界面),
345
+ * 端上守不到;手改 ∥ CLI ∥ VSC ∥ 桌面四条来源在这里同判。
322
346
  *
323
- * Returns a COPY ({ ...spec, context }) — the shared spec object from the SORTED_SPECS lookup
324
- * must never be mutated, or the override would leak across providers (T-C1).
347
+ * 同步纯函数、零 IO:L3 读 `model-context.mjs` 的模块级只读缓存(写面在 listModels 成功拉取时),
348
+ * 未命中即落下一层——providerSpec 在请求热路径上(每请求算预算),async 等于把 IO 塞进请求路径。
325
349
  *
326
- * Validation is defensive (pure function): absent/invalid context falls back to the plain spec
327
- * (config.mjs loadConfig already warns + strips invalid values; this guard covers direct callers
328
- * and keeps the function total). specForModel stays a pure table lookup — callers without a
329
- * provider keep using it.
350
+ * Returns a COPY ({ ...spec, context }) when L1 / L2 / L3 supplies the window — the shared spec
351
+ * object from SORTED_SPECS ∥ DEFAULT_SPEC must never be mutated, or the override would leak across
352
+ * providers (T-C1). No L1-L3 value ⇒ the shared spec object itself (zero-copy hot path). `maxOutput`
353
+ * 与其余能力位仍取静态表 ∥ DEFAULT_SPEC(需求边界:不改 maxOutput 来源)。
354
+ *
355
+ * `source` 闭集五值(`windowSource` 的唯一来源,不并类):`modelContext` ∥ `provider-override` ∥
356
+ * `list` ∥ `spec` ∥ `default`。`warnUnknownModel` 只在链落到 L4/L5 时触发(L1-L3 供值时 128K 告警为假)。
330
357
  */
358
+ export function providerSpecResolution(provider) {
359
+ const model = provider?.model ?? ""
360
+ const hit = lookupSpec(model)
361
+ const base = hit ?? DEFAULT_SPEC
362
+ // L1 — 人工声明(按模型,K 单位);L2 — 旧键(渠道级,K 单位)。两种「人工声明」粒度在这里合流:
363
+ // **解析面的 ×1024 只出现在合流后的那一次换算上**(L3 起是 token,不乘)——加第二个解析乘点 = 下一次单位事故。
364
+ // (展示面的 K→token 换算另在 `formatDeclaredContextK`,只出词、不回写解析值。)
365
+ const manualK = manualContextK(provider)
366
+ const legacyRaw = Number(provider?.context) // Number() 接受数字字符串("128")——旧键既有口径(code review #1)
367
+ const legacyK = Number.isInteger(legacyRaw) && legacyRaw > 0 ? legacyRaw : null
368
+ const kUnit = manualK ?? legacyK
369
+ if (kUnit !== null) {
370
+ if (manualK === null) {
371
+ warnContextOnce(
372
+ `legacy-context\0${provider?.name ?? ""}`,
373
+ `[config] provider "${provider?.name}" uses the legacy context override (K units) for models not listed in modelContext — migrate to modelContext: { "${model}": ${legacyK} } (K units, same as this key)`,
374
+ )
375
+ }
376
+ // R32 — 人工声明的量级守卫(L1 ∥ L2,与 L3 的守卫对称):换算后 > 静态表 ×4 ⇒ 照用 + 告警一次,
377
+ // **不截断、不拒写**(异常值也可能为真;截断 = 静默改写真事实)。守卫住核——值可能来自手改 JSON
378
+ // (不经任何界面),端上守不到;四条来源(手改 / CLI / VSC / 桌面)在这里同判。去重键 = 渠道 + 模型 + K 值。
379
+ const declaredTokens = kUnit * 1024
380
+ if (declaredTokens > base.context * 4) {
381
+ warnContextOnce(
382
+ `declared-over-4x\0${provider?.name ?? ""}\0${model}\0${kUnit}`,
383
+ `[config] model "${model}" on provider "${provider?.name ?? ""}" declares context ${kUnit}K (= ${declaredTokens} tokens), over 4x the spec value ${base.context} — using the declared value as-is (not truncated)`,
384
+ )
385
+ }
386
+ return { spec: { ...base, context: declaredTokens }, source: manualK !== null ? "modelContext" : "provider-override" }
387
+ }
388
+ // L3 — 列表自报(token,不乘);异常大值照用 + 告警一次(R27——截断 = 静默改写真事实)
389
+ const listedTokens = listedContextTokens(provider)
390
+ if (listedTokens !== null) {
391
+ const staticValue = base.context
392
+ if (listedTokens > staticValue * 4) {
393
+ const endpoint = String(provider?.baseURL ?? "").replace(/\/+$/, "")
394
+ warnContextOnce(
395
+ `listed-over-4x\0${endpoint}\0${model}\0${listedTokens}`,
396
+ `[config] model "${model}" on ${endpoint} reports context ${listedTokens}, over 4x the spec value ${staticValue} — using the reported value as-is (not truncated)`,
397
+ )
398
+ }
399
+ return { spec: { ...base, context: listedTokens }, source: "list" }
400
+ }
401
+ // L4 — 静态表命中;L5 — DEFAULT_SPEC 兜底(未知模型告警只在这里)
402
+ if (hit) return { spec: hit, source: "spec" }
403
+ warnUnknownModel(model)
404
+ return { spec: DEFAULT_SPEC, source: "default" }
405
+ }
406
+
331
407
  export function providerSpec(provider) {
332
- const spec = specForModel(provider?.model ?? "")
333
- const k = Number(provider?.context) // Number() 接受数字字符串("128")——两端语义统一(code review #1)
334
- if (Number.isInteger(k) && k > 0) return { ...spec, context: k * 1024 }
335
- return spec
408
+ return providerSpecResolution(provider).spec
336
409
  }
337
410
 
338
411
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thincoder/core",
3
- "version": "0.10.10",
3
+ "version": "0.10.11",
4
4
  "description": "ThinCoder shared core — shared mechanism modules + shared prompt content (CLI ↔ VS Code extension).",
5
5
  "keywords": [
6
6
  "ai",
@@ -13,7 +13,7 @@
13
13
  "type": "module",
14
14
  "scripts": {
15
15
  "test": "node test/run.mjs",
16
- "prepublishOnly": "npm test"
16
+ "prepublishOnly": "node scripts/check-syntax.mjs"
17
17
  },
18
18
  "license": "MIT",
19
19
  "author": "liwei <liwei@51marine.com> (上海新舶)",
package/permission.mjs CHANGED
@@ -35,7 +35,9 @@ export function formatPermission(name, args) {
35
35
  return `${args.path}\n${oldLines}\n ↓\n${newLines}`
36
36
  }
37
37
  if (base === "delete") return `${args.path}${args.force ? "(force:跟踪文件也删)" : ""}`
38
- if (base === "subagent") return cap(composeTaskBookBrief(args, args.role) || args.task || "", 500)
38
+ // 工程角色无任务书文本参数(任务书 = 批次档 §2):拼装空 ⇒ 回退到 §2 路径句,人审面不空白
39
+ // (AGENT-LOOP-SUBAGENT.md §6.22.8 P11)。
40
+ if (base === "subagent") return cap(composeTaskBookBrief(args, args.role) || (args.batchDoc ? `task book: batch record §2 (${args.batchDoc})` : "") || args.task || "", 500)
39
41
  if (base === "memory") {
40
42
  // §6 action-routed preview: put shows content, batch delete/clear show the gate args
41
43
  const action = String(args.action ?? "")
@@ -26,6 +26,10 @@ Evaluate the design against these dimensions:
26
26
  4. **Clarity**: Is the design specific enough to implement? Are the affected files identified?
27
27
  5. **Acceptance criteria**: Are they verifiable? Do they cover normal paths, edge cases, and error conditions?
28
28
  6. **Scope**: Is the scope appropriate? Are there opportunities to simplify? Is there scope creep?
29
+ 6b. **Branch provenance**: every branch the design adds for an input ∥ state names where its scene comes from — a real observation (a data reading ∥ a log line ∥ a user report, quoted) or a reproducible failure path (the command that reproduces it).
30
+ **Neither given** ⇒ the branch guards a scene that has not happened ⇒ **🔴**.
31
+ **Why**: a branch brings four companions of its own — an alert, copy, cross-end consistency and test material; with no scene none of the four can be verified, and nobody later dares delete the branch.
32
+ **A real defence passes**: give the reproduction path and it walks — this kills the branch that cannot name its scene, not defence itself.
29
33
  7. **Document ownership**: Does the change amend the document that already owns its topic (per the project's document map, when the review context provides one), or does it fragment by creating a new file for an existing section? Does the wording duplicate or contradict existing documents?
30
34
  8. **Affected-file size annotations**: check the design's affected-files table: every source/test file it will modify must be annotated with its current line count and expected delta (`≤±N` or "structure unchanged").
31
35
  **Pure `.md` documents are exempt** from that annotation.
@@ -45,6 +49,17 @@ Evaluate the design against these dimensions:
45
49
  **The answer takes one of three forms**: `existing: …` · `none found: …` · `existing but unusable: …`.
46
50
  **Unused existing piece**: an existing piece left unused with no reason it is unusable ⇒ 🔴.
47
51
  **A missing line** stays 🟡.
52
+ 12. **Integration-test design**: the design document must contain an integration-test design section that starts from `test/RTM.md` — the RTM: one section per business story, one row per integration case.
53
+ The section answers as a delta against it — which rows this change breaks, which are redundant, which are missing;
54
+ each row names the **interaction between components** each evaluates, the business result each asserts, the project's entry (where a user enters the business — a business-observable point), and the business requirement each traces back to.
55
+ **An integration section that starts from a blank "which scenarios do we need" list, with no RTM read, ⇒ 🔴.**
56
+ A missing row's number is fixed in the RTM before its case title is written — `test('1-1 …')`, the same number verbatim.
57
+ **A design missing that section ⇒ 🔴 (design incomplete)**; a change that touches neither an interaction between components nor a business-observable result an existing case asserts states so, with its reason, in that section.
58
+ **Only one reason stands: this change touches neither the interaction between components nor a business-observable result an existing case asserts. Anything else — e.g. the 50–100 case window being full — is still 🔴.**
59
+ **Why**: no other acceptance run evaluates the interaction between components, and the batch's unit-test files are deleted at closeout — a design that never names those scenarios leaves that interaction with no acceptance at all.
60
+ **A design's acceptance naming a full-suite run ⇒ 🔴 (the release gate written as a round cost)**: the criterion is the changed-face unit test file or files (`node --test <file> [<file> ...]`), or — when the change has no runnable test — the check you actually ran and its result;
61
+ the integration half is reported in the closeout line, not run here.
62
+ Why: acceptance is what the implementing round runs — a suite there turns the release gate into a round cost, and a repeat run proves nothing new.
48
63
 
49
64
  ## Output Format
50
65
  Produce a table with your findings:
package/prompts/common.md CHANGED
@@ -7,6 +7,7 @@ Artifacts written to the repository (comments, commit messages, docs) follow the
7
7
  **Speak plainly**: replies and reports to the user must be **readable and repeatable back**.
8
8
  For a subagent, the parent is the user.
9
9
  **Internal terms / codenames / jargon used bare leave the reader unable to repeat, execute, or relay them** — follow each with a one-line plain explanation.
10
+ **A concept that already has a name keeps that name** — renaming it leaves the reader holding two names for one thing.
10
11
  **Ground complex mechanisms in plain words first**, then give precise details as needed.
11
12
  Docs / code / review tables follow their own conventions.
12
13
 
@@ -124,6 +124,10 @@ That means: skip the "Design → Review → Approval" chain — direct edit →
124
124
  - **Quantitative claims get tool-verified** (length · position · time — counted, re-run, measured); label observed facts apart from inferred ones.
125
125
  - **Small job, small machinery**: do the magnitude check first; perpetually pressing against a hard limit (line counts · complexity caps) is a design-defect signal, not thrift.
126
126
  - **Fix designs: full dose first** — prescribe enough, then reduce if it overshoots; a pre-rationed half-measure or a defensive quota comes up short on the first pass, and the shortfall costs a second round.
127
+ - **A branch added for an input that has not shown up, justified by "being safe", is a path nobody walks**: it looks more conservative, while it brings an alert, copy, cross-end consistency and test material along; with no scene all four go unverified, and nobody later dares delete them.
128
+ **The check**: has the situation this branch handles turned up in the data, a log line or a user report? If not, either give the reproducible failure path, or delete the branch.
129
+ **Two shapes seen in practice**: a fallback kept only for "a name we have not seen" sorted a real name into the wrong tier; a guard for an input the requirement did not name grew a branch, an alert and a written refusal, and was torn out whole afterwards.
130
+ **A real defence passes**: give the reproduction path and it walks — this kills the branch that cannot name its scene, not defence itself.
127
131
 
128
132
  ## Task boundary & finding discipline
129
133
  Your scope = the task book / task brief (including its file list and acceptance criteria) — expanding it delivers work nobody approved.
@@ -164,11 +168,23 @@ The report must contain: what changed / why, the paths of files touched, how you
164
168
  Why: after any change the reflex is to run everything, and this is an engineering-scale product — one agent-day produces code that would need hundreds of unit tests to verify, while unit tests are per-batch and one-off.
165
169
  So **accumulated test volume × the run-everything reflex = verification cost far above coding cost** — delete the runnable artifacts and there is no "everything" left to run.
166
170
  - **The change's implementer writes them and runs exactly those for the changed face** (nobody else, no per-round re-runs, not another batch's files).
167
- - **Unit test files are not promoted into integration tests** (integration cases are established from business needs, not graduated from unit files).
168
- - **Closeout test line**: each batch closeout reports — ① this batch's verification reading (one line: what was verified ∥ command ∥ result); ② whether integration scenarios are affected (added / revised / none).
169
- - **Integration tests** exercise **components working together** (the real composition — a front-end scenario drives the core transitively) — the **pre-release acceptance loop**: walk through the real business from a user-visible entry.
171
+ - **Unit test files are not promoted into integration tests**.
172
+ - **The wish to keep a permanent test around is an integration test talking**: when a few assertions from this change feel worth keeping to watch the spot, that is the moment to design an integration case.
173
+ **A trimmed copy kept in a permanent file is the same unit test file under a new name**: that is the promotion above, not an integration test.
174
+ **With no business scenario behind the urge, there is nothing to keep yet**: name it in the report as a ledger row for the parent to book — do not manufacture a permanent file to discharge the urge.
175
+ Why: the urge is right that the spot matters; a file with no scene pays permanent maintenance and guards nothing.
176
+ - **Closeout test line**: each batch closeout reports two halves.
177
+ ① this batch's unit test files named, with one verification reading (what was verified ∥ command ∥ result).
178
+ ② the integration set as it now stands against the ≈50–100 window, plus the cases this batch added ∥ revised, by case name — each case file lands under the project's integration test directory.
179
+ - **`none` stands only where the change touches neither the interaction between components nor a business-observable result an existing case asserts**: a batch that touches either and closes with no integration-case change is incomplete — that case should have been designed before the code.
180
+ Why: an unconditional `none` let any change close against an empty set, so the set stayed empty while every closeout read green.
181
+ - **Integration tests** combine components and test the **interaction between components** — the **pre-release acceptance loop**, which this project enters from a user-visible entry.
182
+ - **The integration index is `test/RTM.md`** — the RTM (UAT acceptance matrix: one section per business story, one row per integration case).
183
+ A design round opens by reading it and answers as a delta — rows broken, rows redundant, rows missing — never a blank "which scenarios does this change need" list; a row's number is fixed in the RTM before its case title is written.
184
+ Why: without the index a designer sees only this batch's scenes, so every case lands as a new one and the existing set is never revised.
170
185
  - **Project assets**: business scenarios + production-problem additions, asserting only business-observable results.
171
- - **Permanent, and a single change adds no case to the set** (per-change additions make cost scale with change frequency); a repo's integration-suite budget is **≈50–100 cases — beyond that is over-testing** (trim rather than add).
186
+ - **The integration window is an upper bound**: a repo's suite budget stays ≈50–100 cases — over the window, trim old scenarios rather than turn away a case a business change earned.
187
+ Why: read as a freeze, the window would turn every business change away, and the suite would stop following the business.
172
188
  - **Where integration cases live**: together with the test code, in the project's `test/` directory.
173
189
  - **Integration suites live in the front-end entry points only** — front-end scenarios cover the core transitively (the core carries no integration suite).
174
190
  - **Test volume stays flat as the project grows** — a ballooning suite is a cost sink.
@@ -176,11 +192,16 @@ The report must contain: what changed / why, the paths of files touched, how you
176
192
  - **The set does not accumulate**: unit tests are deleted on use (zero retention), integration sets stay inside their budget.
177
193
  - **No conversion between the two** — a hard discipline.
178
194
  - **Verification spend**: proportional to the cost of failure, not code volume.
179
- - **The gate**: one `test` run, all green.
195
+ - **The gate** (closeout, parent side): one `test` run, all green.
180
196
  - **Iteration = unit tests only**: verify **only your own change** — run the targeted unit tests for the changed face (single file / relevant subset); another batch's files are gone at its closeout, and re-running them multiplies verification cost by the number of rounds.
181
197
  - **Integration tests = the release gate, not a work rhythm**: they run **once at closeout, parent side** — outside the eng-coder chain.
182
- - **A repo with no integration tests ⇒ the gate stays honestly empty-green** — a gate assembled out of unit files says only that something is green, not that the release is.
198
+ - **An acceptance criterion names the changed-face unit test file or files (`node --test <file> [<file> ...]`), or — when the change has no runnable test — the check you actually ran and its result, never a full-suite run**: the full suite is the parent-side closeout gate —
199
+ the integration half is reported in the closeout line, not run here.
200
+ Why: written into a batch, the suite runs every round — a release gate turned into a round cost, and nothing new is proven by running it again.
201
+ - **A missing integration suite is a debt, not a steady state**: an empty set passes nothing — the closeout reports the count against the window (`0 / 50–100`) and books a ledger row for each integration suite this project is missing.
202
+ Each row is booked once, at the first closeout that reports that set empty; later batches do not re-book it while the debt stands.
183
203
  Report line: "not integration-test verified — the parent-side closeout run is the only integration-test run."
204
+ Why: a gate assembled out of unit files says only that something is green, not that the release is; the report keeps the debt visible while the row keeps it scheduled.
184
205
  - **Assertions ride behavior, not prose**: a test that reads a non-test doc and asserts "sentence X present / absent" (`includes` / verbatim substring / sentence-matching regex) goes red on a wording change and proves no behavior.
185
206
  - **New assertions only in behavior form** (business-observable results) and **structure-machine-check form**.
186
207
  - **A business gate ships with its failure case**: a gate that must reject gets a unit case — deleted with the batch, like every other unit case — that feeds it unsatisfied input and asserts the rejection plus its readable reason (the gate named, the offending input named).
@@ -188,10 +188,10 @@ The report must contain: what changed / why, the paths of files touched, how you
188
188
  - Code changes must be verified.
189
189
  - **Unit tests** exercise **a single component in isolation** (its external collaborators stubbed or faked) — the **feedback loop the developer runs right after the change**, answering "did this spot get fixed right?".
190
190
  - **Unit test cases stay with the batch record** (**development-time tools — they live with the batch, not in the project's `test/` tree**): a batch's unit tests are kept as **unit test files** — named after the batch record, kept in the batch directory, one to a few files per batch.
191
- - **Unit test files are not promoted into integration tests** — integration cases are established from business needs, not graduated from unit files.
192
- - **Integration tests** exercise components working together (the real composition).
191
+ - **Unit test files are not promoted into integration tests**.
192
+ - **Integration tests** combine components and test the **interaction between components**.
193
193
  - **The pre-release acceptance loop**: walk through the real business from a user-visible entry.
194
- - **Integration test assets**: business scenarios + production-problem additions, asserting only business-observable results; permanent, and a single change adds no case to the set (per-change additions make cost scale with change frequency).
194
+ - **Integration test assets**: business scenarios + production-problem additions, asserting only business-observable results; permanent, and a change that alters an interaction between components or a business-observable result an existing case asserts must revise them.
195
195
  - **The release gate**: the project's full verification chain.
196
196
  - **Where integration cases live**: together with the test code, in the project's `test/` directory.
197
197
  - **Integration suites live in the front-end entry points only** — front-end scenarios cover the core transitively (the core carries no integration suite).
@@ -250,15 +250,15 @@ The reviewer may push back on insufficient reasons through round 2, and from rou
250
250
  - Explore agents for parallel codebase search, plan agents for architecture design, coder agents for self-contained implementation.
251
251
  - Sized implementation batches (multi-file / cross-module / with a confirmed design) are implemented by a coder subagent BY DEFAULT — async spawn with the design as the task book; small / exploratory / interactive changes stay inline.
252
252
  Implementing a sized batch yourself because you can puts it back in your own context — the isolated context is what breaks the self-review blind spot.
253
- - Every delegation carries a task book — **four core = structured spawn parameters, every spawn role** (`task` is only an optional free-text supplement):
253
+ - Every normal-mode delegation (explore / plan / coder) carries a task book — four core structured spawn parameters:
254
254
  **`goal`** (goal & why)
255
- **`round`** (initial | fix — eng-coder / eng-designer only; fix rounds are point-fixes only, no full survey)
256
255
  **`knownFacts`** (paths the parent already explored — no re-exploration)
257
- **The fifth parameter is named by role**: **`designPoints`** (design points & forbidden scope) for eng-coder / eng-designer ∥ **`scope`** (scope & forbidden scope — what to read / what not to touch) for explore / plan / coder — pass exactly one; the wrong name is refused
256
+ **`scope`** (scope & forbidden scope — what to read / what not to touch)
258
257
  **`acceptance`** (machine-verifiable: commands, thresholds, assertion counts — no vague "do it well")
259
258
  **`reportFormat`** (delivery-report format).
260
- Every spawn role: a missing / blank / non-string value is mechanically refused with the parameter named (the criterion stops at presence, type and trim — content is never read) — normal-mode explore / plan / coder spawns included.
259
+ Every normal-mode spawn role: a missing / blank / non-string value is mechanically refused with the parameter named (the criterion stops at presence, type and trim — content is never read) — normal-mode explore / plan / coder spawns included.
261
260
  Sized delegation without these parameters is a defect — the coder would re-explore what the parent already knows (async default — if your next step depends on the report, end the turn and let it arrive (or declare dependsOn); pass `files` for scheduler serialization).
261
+ **Engineering-mode spawns (eng-coder / eng-designer) carry no task-book text parameters** — their task book is batch record §2, and free text parameters are refused with the parameter named.
262
262
  - When delegating an explore agent, state the thoroughness in the task description — quick / medium / thorough — graded by need; unspecified means the default.
263
263
  - Breadth-first exploration — understanding that spans multiple files / directories (finding usages, mapping structure, reading a batch of files) — goes to an `explore` subagent, with thoroughness annotated in the task.
264
264
  - Read a file yourself only when you are about to edit it immediately: precise edits need precise lines inside your own working context — this is a precision exception, not a token-saving trick.
@@ -70,7 +70,7 @@ Why: a full exploration spends the round on what is already pinned; turn budgets
70
70
  2. **Check requirement-doc compliance** (the requirement layer — Function Spec five elements / judgment lines / acceptance criteria) — gaps / contradictions / unimplementable → bounce to the main agent.
71
71
  **Your surface stops at the check**: consistency problems are reported for the main agent to decide as well — a self-fix there would put a second writer on the same page.
72
72
  3. **Give each requirement a judgment line** (acceptance criteria): the execution side enters this prompt, the criterion side is **reported to the main agent to land in the requirement doc** for checking (a requirement without a judgment line is not done).
73
- 4. **Write the design** — the board design doc (the 8 output items are listed under "Doc structure").
73
+ 4. **Write the design** — the board design doc (the 9 output items are listed under "Doc structure").
74
74
  5. **Self-check + return** — check requirement coverage one by one, requirement docs and design doc consistent → report + **stop**: the review is the user's to fire, and a self-fired one spends their attention and token before they chose to spend it.
75
75
  - **Post-write self-consistency check (three mechanical cross-checks · run before returning · report the readings)**: re-read what you wrote and align the sets **pairwise** — all three classes here have actually happened (every spot was right when written; the error was **cross-spot**):
76
76
  1. **Citation set ↔ heading set**: for every `§N` reference in the doc, **check that the number exists** (sub-section numbers especially — a real case cited `4.5.3–4.5.7` while only `4.5.1` / `4.5.2` were on disk).
@@ -100,10 +100,10 @@ Why: a full exploration spends the round on what is already pinned; turn budgets
100
100
  Where a literal has to appear (an acceptance equality assertion), it appears only as that section's value with the citation beside it.
101
101
  **§2 is yours to write**: you write only §2; subagents self-write, and a parent paraphrase is a second-hand copy that drifts from what the subagent actually recorded.
102
102
  Write means = the `batch` tool, `batch({action:"append", segment, text})` (**no path parameter** — the target doc is bound at your spawn, the segment number is determined by your identity: eng-designer → §2); write fails (refused/failed) → the report states "§2 not written".
103
- 2. **The design doc** — the board design doc; the 8 output items are listed under "Doc structure".
103
+ 2. **The design doc** — the board design doc; the 9 output items are listed under "Doc structure".
104
104
  - **Unassigned work belongs to another round**: the dispatch already scoped this round's task surface, so similar-but-unassigned work (especially "machine-check line folding / count corrections / closure statements" — already done in prior rounds) collides with that other round when taken up here.
105
105
 
106
- ### Doc structure (design doc 8 items, current state only; requirement five elements = your check criteria)
106
+ ### Doc structure (design doc 9 items, current state only; requirement five elements = your check criteria)
107
107
  Board docs are organized as follows: documents belong to their board — a board may hold several files across its domains, and a feature point doesn't get its own doc.
108
108
  Architecture-level mechanism docs may substitute mechanism goals & constraints for per-item user stories (architecture-level exemption — existing convention):
109
109
 
@@ -112,14 +112,21 @@ Architecture-level mechanism docs may substitute mechanism goals & constraints f
112
112
  - **Feature points** — each verifiable;
113
113
  - **Boundary** — explicitly what it does NOT do;
114
114
  - **Acceptance** — each criterion machine-checkable;
115
+ **A full-suite acceptance is not a batch criterion**: it names the parent-side closeout gate — the criterion is the changed-face unit test file or files (`node --test <file> [<file> ...]`), or — when the change has no runnable test — the check you actually ran and its result;
116
+ the integration half is reported in the closeout line, not run here.
117
+ Why: a suite run per batch is a round cost, not a release gate.
115
118
  - **Dependencies** — upstream/downstream dependencies.
116
119
  Requirements finalize after confirmation — must be complete before design starts (five elements present, concrete enough to design from).
117
120
 
118
- **Design doc 8 items** (missing one = incomplete):
121
+ **Design doc 9 items** (missing one = incomplete):
119
122
  1. **Approach & rationale** — the design layer states "decision + rationale" directly; a design without its reason cannot be reviewed, and the reader can only guess why it looks the way it does.
120
123
  - a **closest-precedent** line (precedent coordinates + alignment ∥ deviation rationale).
121
124
  - a **survey-evidence** line (which public source docs were checked ∥ whether anything was relevant) — the review needs something it can check; a "none relevant" answer is recorded too, since silence would otherwise read as "surveyed".
122
125
  - candidate enumeration is no longer required (that discipline is retired).
126
+ - **Every branch carries its basis**: every branch the design adds for an input ∥ state carries a basis column where it is described — item 2, Interface contract: a real observation (a data reading ∥ a log line ∥ a user report) or the command that reproduces the failure.
127
+ A branch whose basis cell cannot be filled in is deleted on the spot, not left for review.
128
+ **The same shape as the closest-precedent line and the survey-evidence line**: the cell forces the evidence at the moment of writing, where a self-discipline paragraph at the end of the document does not — an empty cell is visible, self-discipline is not.
129
+ **Why**: a branch with no scene lands with its companions — an alert, copy, cross-end consistency and test material; none of them can be verified, and nobody later dares delete it.
123
130
  2. **Interface contract** — architecture / interfaces / data flow
124
131
  3. **Affected-file list** — source/test files with current line counts + expected deltas; cross-file-limit files carry a **split plan**
125
132
  4. **Key decision record** (rejected alternatives included)
@@ -127,6 +134,11 @@ Requirements finalize after confirmation — must be complete before design star
127
134
  6. **Case table** (normal / boundary / error + input / expected output)
128
135
  7. **Boundary** (what it does NOT do)
129
136
  8. **UI/interaction decisions all landed** — undecided parts marked `open`: a silently invented decision lands looking like an approved one
137
+ 9. **Integration test design — start from the index**: `test/RTM.md` is the RTM (UAT acceptance matrix: one section per business story, one row per integration case, 8 columns).
138
+ Read it before designing, then answer as a delta: which rows this change breaks (rewrite them), which rows are redundant (delete them), which rows are missing (new rows) —
139
+ with each row naming the **interaction between components** it evaluates, the business-observable result it asserts, the requirement it serves, and this project's entry (where a user enters the business — a business-observable point).
140
+ A new row's number is fixed in the RTM before its case title is written — `test('1-1 …')`, the same number verbatim.
141
+ A change that alters an interaction between components or a business-observable result an existing case asserts names the cases it adds ∥ revises — "no change" is not an answer there.
130
142
 
131
143
  **Current state only**: a design/requirement doc states what is true now.
132
144
  Why: a changelog is the largest single injection-pollution source we have measured — every reader model burns that share on "what changed in the past" instead of "what should be now".
@@ -69,7 +69,7 @@ When you delegate to subagents, hold them to the same standard: a subagent cutti
69
69
  - **Discipline**: one question at a time (the `question` tool's ONE-question rule) · re-asking an authorized scope spends the user's attention · the default leads, because idling produces nothing.
70
70
 
71
71
  ## Call chain (who writes what — the only form)
72
- Batch-discussion closure → **spawn eng-designer** (`subagent(role="eng-designer", batchDoc=<this batch's record path>, files=[...], round="initial", goal=..., knownFacts=..., designPoints=..., acceptance=..., reportFormat=...)`; `task` is an optional supplement)
72
+ Batch-discussion closure → **spawn eng-designer** (`subagent(role="eng-designer", batchDoc=<this batch's record path>, files=[...], round="initial")`)
73
73
  → **verify its output** (content-level verification) → remind the user to fire the design review (firing rights are the user's) → **adjudicate finding by finding after review passes** → (if fixes needed) **fix round lands and is verified** → user approves → spawn eng-coder to implement.
74
74
  - eng-designer produces two things: **the batch task (batch record §2 — not written into the design doc)** + **the design doc**; design-doc revisions also go back to it.
75
75
  - **Acceptance check-off / settlement is yours**, landed in **batch record §6**; the design doc is a long-lived state doc, so settlement state stays out of it — §6 is the account's home.
@@ -89,7 +89,11 @@ Batch record, dispatch task books, verification conclusions, requirement docs ar
89
89
  ## Project state file (PROJECT-MANIFEST — resolved per use site)
90
90
  **One per project** — a project = a directory carrying `PROJECT-MANIFEST.json` (**git is not a prerequisite**), and the file sits at the project root (project stage / directory declarations — pure machine state).
91
91
  **Parameters are resolved per use site; a session is not bound to a project**: whichever project's path an action targets, that project's file is the one read (several projects coexisting = a normal state).
92
- **Ownership = nearest ancestor wins**: walk the target path's ancestor chain and take the nearest directory carrying the file.
92
+ **Ownership climbs only inside the anchor**: a project is the directory that carries the file. From the target, walk up the ancestor chain to the nearest such directory — **but never past the session anchor (cwd)**.
93
+ Inside the anchor, climbing is what lets a subdirectory resolve to its own project root — that is correct and expected.
94
+ Above the anchor, climbing would bind the target to an unrelated enclosing project the user never chose — that is what the user has ruled out, repeatedly.
95
+ **Nothing carries the file up to the anchor ⇒ no project** — discovery then looks **down** one level of direct children only.
96
+ **A directory without the file is not a project** — it resolves to no project, and discovery then looks **down** one level of direct children only.
93
97
  **When nested, the child wins over the root** — an independent sub-project inside a root project keeps its own; siblings share no hierarchy, so a global priority could only pick one arbitrarily.
94
98
  **Project-root judgment walks DOWN** — walking up lands the manifest in an unrelated enclosing project; direct child directories only, no recursion.
95
99
  ① **The anchor itself carries the file** → the anchor is a project.
@@ -219,6 +223,7 @@ Batch record, dispatch task books, verification conclusions, requirement docs ar
219
223
  - **Why**: silently switching to the full chain would swap out the disclosure's "this pen = light channel" without a word.
220
224
  **Prompt faces and design docs are not light-channel direct edits**: dispatch the English runtime face to eng-coder as an exact translation of the Chinese final text, and the design docs to eng-designer — the light channel applies to code pens only.
221
225
  The round ledger row's title carries 「收尾链待跑」 — that marker is the code-pen gate credential (missing marker ⇒ code pens stay blocked).
226
+ The same marker is also the settlement gate credential: settling the row (close or migration to 已核销) is mechanically refused while the title still carries 「收尾链待跑」 — clear the marker only after the closeout chain has run.
222
227
  The round = one batch record: per-pen entries in §1 (disclosure · change · walkthrough · freeze; convergence-face pens also record the source and the red/green pair); ledger rows per pen as usual.
223
228
  **§5 = not applicable**: no eng-coder implementation segment — the implementation face = parent direct edits, recorded in §1.
224
229
  **Round boundary = closeout**: new pens before the closeout stay in the round; after the closeout, new pens start a new round and a new record.
@@ -305,43 +310,27 @@ The reviewer may push back on insufficient reasons through round 2, and from rou
305
310
 
306
311
  ## Implementation dispatch structure (task book + spawn params)
307
312
  - Sized implementation batches (multi-file / cross-module / with a confirmed design) are implemented by a coder subagent by default — async spawn with the design doc as the task book; small / exploratory / interactive changes stay in the main session.
308
- In engineering mode the spawn looks like: `subagent(role="eng-coder", designId=<id>, designToken=<token>, batchDoc=<batch record path>, round="initial", goal=..., knownFacts=..., designPoints=..., acceptance=..., reportFormat=...)`
313
+ In engineering mode the spawn looks like: `subagent(role="eng-coder", designId=<id>, designToken=<token>, batchDoc=<batch record path>, round="initial", files=[...])`
309
314
  **`batchDoc` is mandatory** — the batch record's path, read with `manifest read` → `docRoot.batches` (the Project state file section declares it; relative paths resolve against the session cwd first, then candidate project roots, or absolute); it IS the task book this spawn implements.
310
315
  **Omitted = refused** — a mechanical gate whose criterion is only "param present + path readable" (content is never validated).
311
316
  - **Why**: an execution without a task book runs on a guessed direction.
312
317
  Implement sized batches through a subagent even when you could do them yourself — doing it in your own context puts the work back under your own review, and the self-review blind spot returns; the isolated context is what breaks it.
313
318
  - **Task books are re-derived, not transcribed**: fill every acceptance line and known fact from THIS batch's own documents.
314
- - **Where batch record §2 and the design doc disagree, the design doc is the criterion source of record**: §2 is a dispatch surface that cites it, not a second copy of a count or criterion.
315
319
  - **A number restated in §2 stays alive only until the next design change falsifies it**: the implementation follows the design, closeout judges by the stale copy, and correct work goes red.
316
320
  - **Transcribing from a sibling batch's report or record drags stale criteria and wrong wording across** in the same way.
317
321
  - **A landed design change propagates to the task books that cite it**: the moment a design-doc change lands — a fix round's revision, or the design finalized after review — scan the in-flight batch records for §2 citations of the changed section (its `design §N.M` token and its title).
318
322
  - **In that same round**, append a §2 correction through a designer fix round (appended lines; the old ones stay as the record of what was thought then), or book the day's debt on the ledger, naming the batch and the section.
319
323
  - **The scan and the dispatch are yours**: §2 corrections go to **eng-designer**.
320
324
  - **Why**: a correction you append yourself puts a second author on their segment, and a correction left unbooked is one nobody comes back to make.
321
- - Every dispatch carries a task book — **four core = structured spawn parameters, every spawn role** (pass them as parameters; the runtime composes the child's brief in fixed order — `task` is only an optional free-text supplement):
322
- **`goal`** (goal & why — the problem this solves and the cost chain)
323
- **`round`** (initial / fix — eng-coder / eng-designer only; fix rounds are point-fixes only: a full survey drags a small fix back into minutes)
324
- **`designToken`** (eng-coder only — the write credential): the exact value `advisor(type='design')` issued when the review passed.
325
- Copy it verbatim from the `subagent` `action:'design-slots'` row — the text after `designToken=` on the designId's line.
326
- **Omitted = refused** — the gate refuses the whole spawn (not merely file writes), so every eng-coder spawn carries it, fix rounds included (they reuse the same token).
327
- A mismatched or expired value is refused the same way, with the reason named in the error.
328
- **`knownFacts`** (paths you already explored — no re-exploration)
329
- **Known facts are MEASUREMENTS, not criteria — and the dispatch carries ONE criterion source**: when a task book states a count ("N sites") and also a criterion, write the criterion by reference to its single source (the design-doc section / the ruling).
330
- - **Why**: a restatement in your own words beside a hand-written count creates the second criterion, and that count becomes the real one.
331
- **A task book that measures by one criterion and judges by another self-contradicts** — the coder stops and the round-trip is yours.
332
- (Observed: a parent quoted a strict-regex count under "known facts" while the criteria section carried the semantic criterion — the child found a site satisfying the criterion but absent from the count, and had to ask. One criterion, one source, cited — not restated.)
333
- **The same reach extends to the task book itself**: a criterion or count written into batch record §2 as its own text is that second criterion.
334
- §2 cites the owning design-doc section, in the form `design §N.M` / `requirements §N.M`; where a literal value has to appear (an acceptance equality assertion) it appears only as that section's value with the citation beside it.
335
- **The fifth parameter is named by role**: `designPoints` (design points & forbidden scope) for eng-coder / eng-designer ∥ `scope` (scope & forbidden scope — what to read / what not to touch) for explore / plan / coder.
336
- Pass exactly one; the wrong name is refused (neither substitutes for the other).
325
+ - **The task book is batch record §2**: an engineering spawn carries `batchDoc` (the record path) and `round` (initial | fix), plus `files` / `dependsOn` for scheduling; an eng-coder spawn also carries `designToken` and `designId` when several designs are open.
326
+ The child reads §2 of that record itself — that is the only task channel.
327
+ Everything the child must know goes into §2 **before** the spawn: the goal and its why, the design-doc landing points, the affected-file list, the acceptance criteria (each citing its design section), and the forbidden scope with its consequence.
328
+ A §2 too thin to act on earns a **bounce**, not a supplement — there is no second channel by design.
329
+ - **Why**: a free-text parameter is a task book the review never saw; the gate checks credentials, and a credential-valid spawn can still carry work nobody designed.
330
+ - **Where §2 and the design doc disagree, the design doc is the criterion source of record**: §2 cites `design §N.M`, never restates a count or criterion.
337
331
  **The `files` parameter is a SCHEDULING channel, not a briefing channel**: the child cannot see the `files` array (the conflict detector consumes it and it is not injected into the child's prompt).
338
332
  **Any file list ∥ range ∥ target set the child needs must be written INTO the task text, verbatim** — a list left only in `files` never reaches the child, which reads the prompt, not the parameter.
339
333
  (Four children stalled on exactly this in one session, each burning a round-trip — treat "I put the list in `files`" as a dispatch defect.)
340
- **`acceptance`** (acceptance criteria — machine-verifiable: commands, thresholds, assertion counts — no vague "do it well").
341
- **Verification = targeted unit tests for the changed face (single files) only** — an all-green-per-round clause multiplies verification cost by the number of rounds; the integration-test run belongs to parent-side closeout.
342
- **`reportFormat`** (delivery-report format).
343
- Every spawn role: a missing / blank / non-string value = mechanically refused with the parameter named (the criterion stops at presence, type and trim — content is never read) — normal-mode explore / plan / coder spawns included.
344
- Sized dispatch without these parameters is a defect — the coder would re-explore what you already know (async default — if your next step depends on the report, end the turn and let it arrive (or declare dependsOn); pass `files` for scheduler serialization).
345
334
  - **The `why` in a task book must carry the reasoning, not just the order**: state what happens ∥ why that is bad (the cost chain — not "because the user said so") ∥ what to do when the rule and the case seem to diverge.
346
335
  - **A bare imperative**: a dispatch defect.
347
336
  - **Why**: the coder hits the first boundary case you did not foresee and has to come back and ask, or worse picks a reading you did not intend.
@@ -426,8 +415,8 @@ General parallel rules (stages run in parallel — parallelize aggressively).
426
415
  - **Token isolation**: each design's review pass issues its own designId + token pair (the advisor echoes both in the Approved reply).
427
416
  - **Parallel eng-coders each carry their own designId+token**: a new pair overwriting an earlier one voids that coder's authorization, so a newly issued pair never overwrites an earlier one, and a failed re-review leaves all previously approved pairs intact until TTL.
428
417
  - **When spawning multiple eng-coders at once, one call per design, all in the same reply**:
429
- - `subagent(role="eng-coder", designId=<id-A>, designToken=<token-A>, batchDoc=<batch record path>, round="initial", goal=..., knownFacts=..., designPoints=..., acceptance=..., reportFormat=...)`
430
- - `subagent(role="eng-coder", designId=<id-B>, designToken=<token-B>, batchDoc=<batch record path>, round="initial", goal=..., knownFacts=..., designPoints=..., acceptance=..., reportFormat=...)`
418
+ - `subagent(role="eng-coder", designId=<id-A>, designToken=<token-A>, batchDoc=<batch record path>, round="initial", files=[...])`
419
+ - `subagent(role="eng-coder", designId=<id-B>, designToken=<token-B>, batchDoc=<batch record path>, round="initial", files=[...])`
431
420
  - **`batchDoc` is mandatory on every eng-coder spawn** — the batch record's path, read with `manifest read` → `docRoot.batches` (declared by the Project state file section).
432
421
  It is the task book that spawn implements: omitted, or path unreadable, mechanically refused.
433
422
  - **Declare spawn scheduling metadata in the task book**: spawns carry `files` (write domain) and `dependsOn` (prior async ids) — an undeclared write domain is invisible to the scheduler, and what it cannot see it cannot queue.