add-coder 0.3.38 → 0.3.40

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 (78) hide show
  1. package/README.en.md +16 -3
  2. package/README.md +16 -3
  3. package/dist/index.js +421 -70
  4. package/package.json +3 -2
  5. package/templates/.add-coder-src-hash.json +74 -61
  6. package/templates/adapters/claude/hooks/post-tool-use.mjs +1 -1
  7. package/templates/adapters/claude/hooks/prompt-submit.mjs +1 -1
  8. package/templates/adapters/claude/hooks/session-start.mjs +25 -5
  9. package/templates/adapters/claude/hooks/stop-check.mjs +2 -2
  10. package/templates/adapters/codex/hooks/post-tool-use.mjs +1 -1
  11. package/templates/adapters/codex/hooks/prompt-submit.mjs +1 -1
  12. package/templates/adapters/codex/hooks/session-start.mjs +25 -5
  13. package/templates/adapters/codex/hooks/stop-check.mjs +2 -2
  14. package/templates/adapters/qoder/hooks/post-tool-use.mjs +1 -1
  15. package/templates/adapters/qoder/hooks/prompt-submit.mjs +1 -1
  16. package/templates/adapters/qoder/hooks/session-start.mjs +25 -5
  17. package/templates/adapters/qoder/hooks/stop-check.mjs +2 -2
  18. package/templates/adapters/trae/hooks/post-tool-use.mjs +1 -1
  19. package/templates/adapters/trae/hooks/prompt-submit.mjs +1 -1
  20. package/templates/adapters/trae/hooks/session-start.mjs +25 -5
  21. package/templates/adapters/trae/hooks/stop-check.mjs +2 -2
  22. package/templates/adapters/vscode/hooks/post-tool-use.mjs +1 -1
  23. package/templates/adapters/vscode/hooks/prompt-submit.mjs +1 -1
  24. package/templates/adapters/vscode/hooks/session-start.mjs +25 -5
  25. package/templates/adapters/vscode/hooks/stop-check.mjs +2 -2
  26. package/templates/core/AGENTS.md +46 -0
  27. package/templates/core/docs/ADD-governance-claude-code.md +52 -0
  28. package/templates/core/docs/ADD-governance-codex.md +62 -0
  29. package/templates/core/docs/ADD-governance-qoder-cn.md +30 -0
  30. package/templates/core/docs/ADD-governance-trae.md +42 -0
  31. package/templates/core/docs/ADD-governance-vscode-copilot.md +88 -0
  32. package/templates/core/governance/rules.ts +2 -2
  33. package/templates/core/governance/session-start-guard.ts +28 -4
  34. package/templates/core/prisma/add.prisma +4 -0
  35. package/templates/core/scripts/mcp-server/resources/hitl-approval-widget.ts +4 -2
  36. package/templates/core/scripts/mcp-server/resources/index.ts +2 -0
  37. package/templates/core/scripts/mcp-server/resources/widget-probe.ts +53 -0
  38. package/templates/core/scripts/mcp-server/shared/db-types.ts +5 -1
  39. package/templates/core/scripts/mcp-server/shared/dev-operation.ts +97 -0
  40. package/templates/core/scripts/mcp-server/shared/hitl-ui.ts +46 -1
  41. package/templates/core/scripts/mcp-server/shared/memory/jobs/snapshot.ts +9 -0
  42. package/templates/core/scripts/mcp-server/shared/memory/retrieval/cjk-segmenter.ts +140 -0
  43. package/templates/core/scripts/mcp-server/shared/memory/retrieval/cjk-tokenize.ts +62 -0
  44. package/templates/core/scripts/mcp-server/shared/memory/retrieval/fts/pg.ts +68 -16
  45. package/templates/core/scripts/mcp-server/shared/memory/retrieval/fts/sqlite-fts5.sql +3 -3
  46. package/templates/core/scripts/mcp-server/shared/memory/retrieval/fts/sqlite.ts +23 -6
  47. package/templates/core/scripts/mcp-server/shared/memory/retrieval/fts-fingerprint.ts +116 -0
  48. package/templates/core/scripts/mcp-server/shared/memory/retrieval/query-terms.ts +9 -25
  49. package/templates/core/scripts/mcp-server/shared/memory/switches.ts +4 -2
  50. package/templates/core/scripts/mcp-server/shared/plan-lifecycle.ts +16 -3
  51. package/templates/core/scripts/mcp-server/shared/server-capabilities.ts +65 -0
  52. package/templates/core/scripts/mcp-server/tools/audit.ts +16 -34
  53. package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_completeness.ts +17 -4
  54. package/templates/core/scripts/mcp-server/tools/gateway/check_add_route_status.ts +34 -6
  55. package/templates/core/scripts/mcp-server/tools/gateway/check_dps.ts +15 -25
  56. package/templates/core/scripts/mcp-server/tools/gateway/check_rahs.ts +23 -26
  57. package/templates/core/scripts/mcp-server/tools/gateway/check_spec_sync.ts +56 -38
  58. package/templates/core/scripts/mcp-server/tools/gateway/plan-resolve.ts +270 -0
  59. package/templates/core/scripts/mcp-server/tools/hitl.ts +18 -8
  60. package/templates/core/scripts/mcp-server/tools/index.ts +7 -2
  61. package/templates/core/scripts/mcp-server/tools/memory.ts +235 -1
  62. package/templates/core/scripts/mcp-server/tools/plan-lifecycle.ts +219 -0
  63. package/templates/core/scripts/mcp-server/tools/plan.ts +2 -0
  64. package/templates/core/scripts/mcp-server/tools/widget-probe.ts +53 -0
  65. package/templates/core/scripts/mcp-server.ts +5 -1
  66. package/templates/core/scripts/memory/backfill-search-text.ts +132 -0
  67. package/templates/core/scripts/memory/memory-jobs.ts +124 -0
  68. package/templates/core/scripts/plan-close.ts +85 -0
  69. package/templates/core/skills/add-paradigm/SKILL.md +19 -4
  70. package/templates/core/skills/session-init/SKILL.md +27 -0
  71. package/templates/core/templates/add-route-template-heavyweight.md +2 -0
  72. package/templates/core/templates/add-route-template.md +4 -0
  73. package/templates/core/templates/checklist-template.md +2 -0
  74. package/templates/core/templates/handoff-multi-round-template.md +2 -0
  75. package/templates/core/templates/review-implementation-template.md +18 -0
  76. package/templates/core/templates/review-implementation-template.schema.json +7 -1
  77. package/templates/core/templates/simple-plan-template.md +4 -0
  78. package/templates/core/vocabulary/add-governance-vocabulary.md +2 -0
@@ -488,7 +488,7 @@ import { join as join3 } from "node:path";
488
488
 
489
489
  // templates/core/scripts/mcp-server/shared/memory/switches.ts
490
490
  function recallMode(env = process.env) {
491
- const v = (env.ADD_MEMORY_RECALL_MODE ?? "shadow").toLowerCase();
491
+ const v = (env.ADD_MEMORY_RECALL_MODE ?? "inject").toLowerCase();
492
492
  return v === "off" || v === "inject" ? v : "shadow";
493
493
  }
494
494
  function memoryMaxTokens(env = process.env) {
@@ -584,19 +584,39 @@ var SessionStartGuard = class {
584
584
  * shadow → 仅提示快照存在(召回可执行并落审计,但不注入上下文)
585
585
  * inject → 读预计算快照注入(新鲜度 ≤7 天,token 预算截断,带来源边界标签)
586
586
  * 同步 Hook ≤200ms 约束:只读文件,任何异常 fail-open 静默跳过。
587
+ *
588
+ * [2026-09-21 接线修复] 三态必须**可区分**,且"未接线/已过期"不再静默:
589
+ * 原实现 `if (!existsSync(file)) return` 会让"没跑 job"与"没有已治理记忆"、
590
+ * 以及"档位没开"三种情况在会话里完全无痕,用户与模型都无从判断记忆是否接上。
587
591
  */
588
592
  emitMemoryL1() {
589
593
  try {
590
594
  const mode = recallMode();
591
595
  if (mode === "off" || !this.magicDir) return;
592
596
  const file = join3(this.projectDir, this.magicDir, MEMORY_DIR_NAME, L1_SNAPSHOT_FILE);
593
- if (!existsSync3(file)) return;
597
+ if (!existsSync3(file)) {
598
+ process.stdout.write(
599
+ `[Memory] \u26A0\uFE0F L1 \u5FEB\u7167\u672A\u63A5\u7EBF\uFF08${file} \u4E0D\u5B58\u5728\uFF09\u2014\u2014\u5F53\u524D\u6863\u4F4D ${mode}\u3002\u751F\u6210\u5FEB\u7167\uFF1A\u6267\u884C \`npx add-coder sync\`\u3001\u8C03\u7528 MCP \u5DE5\u5177 refresh_memory_snapshots\u3001\u6216\u8FD0\u884C \`${this.magicDir}/scripts/memory/memory-jobs.ts refresh-l1\`\uFF1B\u6CE8\u5165\u5F00\u5173\uFF1AADD_MEMORY_RECALL_MODE=inject\uFF08\u9ED8\u8BA4\uFF09\u3002\u8BE6\u89C1 ${this.magicDir}/docs/ADD-governance-codex.md
600
+ `
601
+ );
602
+ return;
603
+ }
594
604
  if (mode === "shadow") {
595
- process.stdout.write(`[Memory] L1 \u5FEB\u7167\u5DF2\u751F\u6210\uFF08shadow \u6A21\u5F0F\u672A\u6CE8\u5165\uFF09\u3002\u9700\u8981\u65F6\u8C03\u7528 recall_memory \u663E\u5F0F\u53EC\u56DE: ${file}
596
- `);
605
+ process.stdout.write(
606
+ `[Memory] \u5F53\u524D\u6863\u4F4D shadow\uFF1A\u53EC\u56DE\u53EF\u6267\u884C\u5E76\u843D\u5BA1\u8BA1\uFF0C\u4F46**\u4E0D\u6CE8\u5165**\u4E0A\u4E0B\u6587\uFF08\u5FEB\u7167\u5DF2\u751F\u6210: ${file}\uFF09\u3002\u9700\u8981\u6CE8\u5165\u65F6\u53BB\u6389 ADD_MEMORY_RECALL_MODE \u6216\u8BBE\u4E3A inject\uFF08\u9ED8\u8BA4\uFF09\uFF1B\u4E5F\u53EF\u76F4\u63A5\u8C03\u7528 recall_memory \u663E\u5F0F\u53EC\u56DE\u3002
607
+ `
608
+ );
609
+ return;
610
+ }
611
+ const ageMs = Date.now() - statSync(file).mtimeMs;
612
+ if (ageMs > L1_SNAPSHOT_TTL_MS) {
613
+ const days = Math.floor(ageMs / 864e5);
614
+ process.stdout.write(
615
+ `[Memory] \u26A0\uFE0F L1 \u5FEB\u7167\u5DF2\u8FC7\u671F\uFF08${days} \u5929 > TTL 7 \u5929\uFF0C\u672A\u6CE8\u5165\uFF09\uFF1A${file}\u2014\u2014\u5237\u65B0\uFF1A\u8C03\u7528 refresh_memory_snapshots \u6216\u8FD0\u884C \`${this.magicDir}/scripts/memory/memory-jobs.ts refresh-l1\`
616
+ `
617
+ );
597
618
  return;
598
619
  }
599
- if (Date.now() - statSync(file).mtimeMs > L1_SNAPSHOT_TTL_MS) return;
600
620
  const text = readFileSync3(file, "utf-8");
601
621
  const budget = memoryMaxTokens();
602
622
  if (estimateTokens(text) <= budget) {
@@ -36,7 +36,7 @@ var context = {
36
36
  {
37
37
  "id": "has_add_dev_unclosed",
38
38
  "consumed": true,
39
- "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 handoff \u540C\u76EE\u5F55 devlog-{plan}-v{n}.md\n \u683C\u5F0F: # Devlog: {plan}\\n \u65E5\u671F / Plan / \u8F6E\u6B21 / \u672C\u8F6E\u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 \u67E5\u540C\u76EE\u5F55 devlog-*.md \u2192 query_audit_logs\n"
39
+ "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 \u8C03\u7528 `record_dev_operation`\uFF08MCP\uFF09\u843D\u5E93\u5BA1\u8BA1\uFF1B\u5E76\u628A\u672C\u8F6E\u9A8C\u6536\u8BB0\u5F55\uFF08\u65E5\u671F / Plan / \u8F6E\u6B21 / \u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\uFF09\u5199\u8FDB handoff \u7684\u9A8C\u6536\u6BB5\u843D\u2014\u2014**\u4E0D\u65B0\u5EFA devlog \u6587\u4EF6**\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\uFF08\u771F\u5B9E cuid\uFF09\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 query_audit_logs({ planKeyword })\uFF08devlog \u5373 DevOperation \u8BB0\u5F55\uFF0C\u65E0\u72EC\u7ACB\u6587\u4EF6\uFF09\n"
40
40
  },
41
41
  {
42
42
  "id": "has_add_dev_closed",
@@ -48,7 +48,7 @@ var context = {
48
48
  {
49
49
  "adapter": "qoder",
50
50
  "id": "has_add_dev_unclosed",
51
- "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 handoff \u540C\u76EE\u5F55 devlog-{plan}-v{n}.md\n \u683C\u5F0F: # Devlog: {plan}\\n \u65E5\u671F / Plan / \u8F6E\u6B21 / \u672C\u8F6E\u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 \u67E5\u540C\u76EE\u5F55 devlog-*.md \u2192 query_audit_logs\n"
51
+ "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 \u8C03\u7528 `record_dev_operation`\uFF08MCP\uFF09\u843D\u5E93\u5BA1\u8BA1\uFF1B\u5E76\u628A\u672C\u8F6E\u9A8C\u6536\u8BB0\u5F55\uFF08\u65E5\u671F / Plan / \u8F6E\u6B21 / \u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\uFF09\u5199\u8FDB handoff \u7684\u9A8C\u6536\u6BB5\u843D\u2014\u2014**\u4E0D\u65B0\u5EFA devlog \u6587\u4EF6**\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\uFF08\u771F\u5B9E cuid\uFF09\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 query_audit_logs({ planKeyword })\uFF08devlog \u5373 DevOperation \u8BB0\u5F55\uFF0C\u65E0\u72EC\u7ACB\u6587\u4EF6\uFF09\n"
52
52
  }
53
53
  ],
54
54
  "templates": {
@@ -240,7 +240,7 @@ var AuditBridge = class {
240
240
 
241
241
  // templates/core/scripts/mcp-server/shared/memory/switches.ts
242
242
  function recallMode(env = process.env) {
243
- const v = (env.ADD_MEMORY_RECALL_MODE ?? "shadow").toLowerCase();
243
+ const v = (env.ADD_MEMORY_RECALL_MODE ?? "inject").toLowerCase();
244
244
  return v === "off" || v === "inject" ? v : "shadow";
245
245
  }
246
246
  function evidenceEnabled(env = process.env) {
@@ -449,7 +449,7 @@ function writeHookEvent(hook, decision, cmd, reason, plan = "unknown", status =
449
449
 
450
450
  // templates/core/scripts/mcp-server/shared/memory/switches.ts
451
451
  function recallMode(env = process.env) {
452
- const v = (env.ADD_MEMORY_RECALL_MODE ?? "shadow").toLowerCase();
452
+ const v = (env.ADD_MEMORY_RECALL_MODE ?? "inject").toLowerCase();
453
453
  return v === "off" || v === "inject" ? v : "shadow";
454
454
  }
455
455
  var L1_SNAPSHOT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -484,7 +484,7 @@ var PreloadTemplates = class _PreloadTemplates {
484
484
 
485
485
  // templates/core/scripts/mcp-server/shared/memory/switches.ts
486
486
  function recallMode(env = process.env) {
487
- const v = (env.ADD_MEMORY_RECALL_MODE ?? "shadow").toLowerCase();
487
+ const v = (env.ADD_MEMORY_RECALL_MODE ?? "inject").toLowerCase();
488
488
  return v === "off" || v === "inject" ? v : "shadow";
489
489
  }
490
490
  function memoryMaxTokens(env = process.env) {
@@ -580,19 +580,39 @@ var SessionStartGuard = class {
580
580
  * shadow → 仅提示快照存在(召回可执行并落审计,但不注入上下文)
581
581
  * inject → 读预计算快照注入(新鲜度 ≤7 天,token 预算截断,带来源边界标签)
582
582
  * 同步 Hook ≤200ms 约束:只读文件,任何异常 fail-open 静默跳过。
583
+ *
584
+ * [2026-09-21 接线修复] 三态必须**可区分**,且"未接线/已过期"不再静默:
585
+ * 原实现 `if (!existsSync(file)) return` 会让"没跑 job"与"没有已治理记忆"、
586
+ * 以及"档位没开"三种情况在会话里完全无痕,用户与模型都无从判断记忆是否接上。
583
587
  */
584
588
  emitMemoryL1() {
585
589
  try {
586
590
  const mode = recallMode();
587
591
  if (mode === "off" || !this.magicDir) return;
588
592
  const file = join3(this.projectDir, this.magicDir, MEMORY_DIR_NAME, L1_SNAPSHOT_FILE);
589
- if (!existsSync3(file)) return;
593
+ if (!existsSync3(file)) {
594
+ process.stdout.write(
595
+ `[Memory] \u26A0\uFE0F L1 \u5FEB\u7167\u672A\u63A5\u7EBF\uFF08${file} \u4E0D\u5B58\u5728\uFF09\u2014\u2014\u5F53\u524D\u6863\u4F4D ${mode}\u3002\u751F\u6210\u5FEB\u7167\uFF1A\u6267\u884C \`npx add-coder sync\`\u3001\u8C03\u7528 MCP \u5DE5\u5177 refresh_memory_snapshots\u3001\u6216\u8FD0\u884C \`${this.magicDir}/scripts/memory/memory-jobs.ts refresh-l1\`\uFF1B\u6CE8\u5165\u5F00\u5173\uFF1AADD_MEMORY_RECALL_MODE=inject\uFF08\u9ED8\u8BA4\uFF09\u3002\u8BE6\u89C1 ${this.magicDir}/docs/ADD-governance-codex.md
596
+ `
597
+ );
598
+ return;
599
+ }
590
600
  if (mode === "shadow") {
591
- process.stdout.write(`[Memory] L1 \u5FEB\u7167\u5DF2\u751F\u6210\uFF08shadow \u6A21\u5F0F\u672A\u6CE8\u5165\uFF09\u3002\u9700\u8981\u65F6\u8C03\u7528 recall_memory \u663E\u5F0F\u53EC\u56DE: ${file}
592
- `);
601
+ process.stdout.write(
602
+ `[Memory] \u5F53\u524D\u6863\u4F4D shadow\uFF1A\u53EC\u56DE\u53EF\u6267\u884C\u5E76\u843D\u5BA1\u8BA1\uFF0C\u4F46**\u4E0D\u6CE8\u5165**\u4E0A\u4E0B\u6587\uFF08\u5FEB\u7167\u5DF2\u751F\u6210: ${file}\uFF09\u3002\u9700\u8981\u6CE8\u5165\u65F6\u53BB\u6389 ADD_MEMORY_RECALL_MODE \u6216\u8BBE\u4E3A inject\uFF08\u9ED8\u8BA4\uFF09\uFF1B\u4E5F\u53EF\u76F4\u63A5\u8C03\u7528 recall_memory \u663E\u5F0F\u53EC\u56DE\u3002
603
+ `
604
+ );
605
+ return;
606
+ }
607
+ const ageMs = Date.now() - statSync(file).mtimeMs;
608
+ if (ageMs > L1_SNAPSHOT_TTL_MS) {
609
+ const days = Math.floor(ageMs / 864e5);
610
+ process.stdout.write(
611
+ `[Memory] \u26A0\uFE0F L1 \u5FEB\u7167\u5DF2\u8FC7\u671F\uFF08${days} \u5929 > TTL 7 \u5929\uFF0C\u672A\u6CE8\u5165\uFF09\uFF1A${file}\u2014\u2014\u5237\u65B0\uFF1A\u8C03\u7528 refresh_memory_snapshots \u6216\u8FD0\u884C \`${this.magicDir}/scripts/memory/memory-jobs.ts refresh-l1\`
612
+ `
613
+ );
593
614
  return;
594
615
  }
595
- if (Date.now() - statSync(file).mtimeMs > L1_SNAPSHOT_TTL_MS) return;
596
616
  const text = readFileSync3(file, "utf-8");
597
617
  const budget = memoryMaxTokens();
598
618
  if (estimateTokens(text) <= budget) {
@@ -36,7 +36,7 @@ var context = {
36
36
  {
37
37
  "id": "has_add_dev_unclosed",
38
38
  "consumed": true,
39
- "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 handoff \u540C\u76EE\u5F55 devlog-{plan}-v{n}.md\n \u683C\u5F0F: # Devlog: {plan}\\n \u65E5\u671F / Plan / \u8F6E\u6B21 / \u672C\u8F6E\u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 \u67E5\u540C\u76EE\u5F55 devlog-*.md \u2192 query_audit_logs\n"
39
+ "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 \u8C03\u7528 `record_dev_operation`\uFF08MCP\uFF09\u843D\u5E93\u5BA1\u8BA1\uFF1B\u5E76\u628A\u672C\u8F6E\u9A8C\u6536\u8BB0\u5F55\uFF08\u65E5\u671F / Plan / \u8F6E\u6B21 / \u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\uFF09\u5199\u8FDB handoff \u7684\u9A8C\u6536\u6BB5\u843D\u2014\u2014**\u4E0D\u65B0\u5EFA devlog \u6587\u4EF6**\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\uFF08\u771F\u5B9E cuid\uFF09\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 query_audit_logs({ planKeyword })\uFF08devlog \u5373 DevOperation \u8BB0\u5F55\uFF0C\u65E0\u72EC\u7ACB\u6587\u4EF6\uFF09\n"
40
40
  },
41
41
  {
42
42
  "id": "has_add_dev_closed",
@@ -48,7 +48,7 @@ var context = {
48
48
  {
49
49
  "adapter": "qoder",
50
50
  "id": "has_add_dev_unclosed",
51
- "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 handoff \u540C\u76EE\u5F55 devlog-{plan}-v{n}.md\n \u683C\u5F0F: # Devlog: {plan}\\n \u65E5\u671F / Plan / \u8F6E\u6B21 / \u672C\u8F6E\u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 \u67E5\u540C\u76EE\u5F55 devlog-*.md \u2192 query_audit_logs\n"
51
+ "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 \u8C03\u7528 `record_dev_operation`\uFF08MCP\uFF09\u843D\u5E93\u5BA1\u8BA1\uFF1B\u5E76\u628A\u672C\u8F6E\u9A8C\u6536\u8BB0\u5F55\uFF08\u65E5\u671F / Plan / \u8F6E\u6B21 / \u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\uFF09\u5199\u8FDB handoff \u7684\u9A8C\u6536\u6BB5\u843D\u2014\u2014**\u4E0D\u65B0\u5EFA devlog \u6587\u4EF6**\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\uFF08\u771F\u5B9E cuid\uFF09\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 query_audit_logs({ planKeyword })\uFF08devlog \u5373 DevOperation \u8BB0\u5F55\uFF0C\u65E0\u72EC\u7ACB\u6587\u4EF6\uFF09\n"
52
52
  }
53
53
  ],
54
54
  "templates": {
@@ -240,7 +240,7 @@ var AuditBridge = class {
240
240
 
241
241
  // templates/core/scripts/mcp-server/shared/memory/switches.ts
242
242
  function recallMode(env = process.env) {
243
- const v = (env.ADD_MEMORY_RECALL_MODE ?? "shadow").toLowerCase();
243
+ const v = (env.ADD_MEMORY_RECALL_MODE ?? "inject").toLowerCase();
244
244
  return v === "off" || v === "inject" ? v : "shadow";
245
245
  }
246
246
  function evidenceEnabled(env = process.env) {
@@ -637,7 +637,7 @@ function writeHookEvent(hook, decision, cmd, reason, plan = "unknown", status =
637
637
 
638
638
  // templates/core/scripts/mcp-server/shared/memory/switches.ts
639
639
  function recallMode(env = process.env) {
640
- const v = (env.ADD_MEMORY_RECALL_MODE ?? "shadow").toLowerCase();
640
+ const v = (env.ADD_MEMORY_RECALL_MODE ?? "inject").toLowerCase();
641
641
  return v === "off" || v === "inject" ? v : "shadow";
642
642
  }
643
643
  var L1_SNAPSHOT_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
@@ -484,7 +484,7 @@ var PreloadTemplates = class _PreloadTemplates {
484
484
 
485
485
  // templates/core/scripts/mcp-server/shared/memory/switches.ts
486
486
  function recallMode(env = process.env) {
487
- const v = (env.ADD_MEMORY_RECALL_MODE ?? "shadow").toLowerCase();
487
+ const v = (env.ADD_MEMORY_RECALL_MODE ?? "inject").toLowerCase();
488
488
  return v === "off" || v === "inject" ? v : "shadow";
489
489
  }
490
490
  function memoryMaxTokens(env = process.env) {
@@ -580,19 +580,39 @@ var SessionStartGuard = class {
580
580
  * shadow → 仅提示快照存在(召回可执行并落审计,但不注入上下文)
581
581
  * inject → 读预计算快照注入(新鲜度 ≤7 天,token 预算截断,带来源边界标签)
582
582
  * 同步 Hook ≤200ms 约束:只读文件,任何异常 fail-open 静默跳过。
583
+ *
584
+ * [2026-09-21 接线修复] 三态必须**可区分**,且"未接线/已过期"不再静默:
585
+ * 原实现 `if (!existsSync(file)) return` 会让"没跑 job"与"没有已治理记忆"、
586
+ * 以及"档位没开"三种情况在会话里完全无痕,用户与模型都无从判断记忆是否接上。
583
587
  */
584
588
  emitMemoryL1() {
585
589
  try {
586
590
  const mode = recallMode();
587
591
  if (mode === "off" || !this.magicDir) return;
588
592
  const file = join3(this.projectDir, this.magicDir, MEMORY_DIR_NAME, L1_SNAPSHOT_FILE);
589
- if (!existsSync3(file)) return;
593
+ if (!existsSync3(file)) {
594
+ process.stdout.write(
595
+ `[Memory] \u26A0\uFE0F L1 \u5FEB\u7167\u672A\u63A5\u7EBF\uFF08${file} \u4E0D\u5B58\u5728\uFF09\u2014\u2014\u5F53\u524D\u6863\u4F4D ${mode}\u3002\u751F\u6210\u5FEB\u7167\uFF1A\u6267\u884C \`npx add-coder sync\`\u3001\u8C03\u7528 MCP \u5DE5\u5177 refresh_memory_snapshots\u3001\u6216\u8FD0\u884C \`${this.magicDir}/scripts/memory/memory-jobs.ts refresh-l1\`\uFF1B\u6CE8\u5165\u5F00\u5173\uFF1AADD_MEMORY_RECALL_MODE=inject\uFF08\u9ED8\u8BA4\uFF09\u3002\u8BE6\u89C1 ${this.magicDir}/docs/ADD-governance-codex.md
596
+ `
597
+ );
598
+ return;
599
+ }
590
600
  if (mode === "shadow") {
591
- process.stdout.write(`[Memory] L1 \u5FEB\u7167\u5DF2\u751F\u6210\uFF08shadow \u6A21\u5F0F\u672A\u6CE8\u5165\uFF09\u3002\u9700\u8981\u65F6\u8C03\u7528 recall_memory \u663E\u5F0F\u53EC\u56DE: ${file}
592
- `);
601
+ process.stdout.write(
602
+ `[Memory] \u5F53\u524D\u6863\u4F4D shadow\uFF1A\u53EC\u56DE\u53EF\u6267\u884C\u5E76\u843D\u5BA1\u8BA1\uFF0C\u4F46**\u4E0D\u6CE8\u5165**\u4E0A\u4E0B\u6587\uFF08\u5FEB\u7167\u5DF2\u751F\u6210: ${file}\uFF09\u3002\u9700\u8981\u6CE8\u5165\u65F6\u53BB\u6389 ADD_MEMORY_RECALL_MODE \u6216\u8BBE\u4E3A inject\uFF08\u9ED8\u8BA4\uFF09\uFF1B\u4E5F\u53EF\u76F4\u63A5\u8C03\u7528 recall_memory \u663E\u5F0F\u53EC\u56DE\u3002
603
+ `
604
+ );
605
+ return;
606
+ }
607
+ const ageMs = Date.now() - statSync(file).mtimeMs;
608
+ if (ageMs > L1_SNAPSHOT_TTL_MS) {
609
+ const days = Math.floor(ageMs / 864e5);
610
+ process.stdout.write(
611
+ `[Memory] \u26A0\uFE0F L1 \u5FEB\u7167\u5DF2\u8FC7\u671F\uFF08${days} \u5929 > TTL 7 \u5929\uFF0C\u672A\u6CE8\u5165\uFF09\uFF1A${file}\u2014\u2014\u5237\u65B0\uFF1A\u8C03\u7528 refresh_memory_snapshots \u6216\u8FD0\u884C \`${this.magicDir}/scripts/memory/memory-jobs.ts refresh-l1\`
612
+ `
613
+ );
593
614
  return;
594
615
  }
595
- if (Date.now() - statSync(file).mtimeMs > L1_SNAPSHOT_TTL_MS) return;
596
616
  const text = readFileSync3(file, "utf-8");
597
617
  const budget = memoryMaxTokens();
598
618
  if (estimateTokens(text) <= budget) {
@@ -36,7 +36,7 @@ var context = {
36
36
  {
37
37
  "id": "has_add_dev_unclosed",
38
38
  "consumed": true,
39
- "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 handoff \u540C\u76EE\u5F55 devlog-{plan}-v{n}.md\n \u683C\u5F0F: # Devlog: {plan}\\n \u65E5\u671F / Plan / \u8F6E\u6B21 / \u672C\u8F6E\u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 \u67E5\u540C\u76EE\u5F55 devlog-*.md \u2192 query_audit_logs\n"
39
+ "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 \u8C03\u7528 `record_dev_operation`\uFF08MCP\uFF09\u843D\u5E93\u5BA1\u8BA1\uFF1B\u5E76\u628A\u672C\u8F6E\u9A8C\u6536\u8BB0\u5F55\uFF08\u65E5\u671F / Plan / \u8F6E\u6B21 / \u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\uFF09\u5199\u8FDB handoff \u7684\u9A8C\u6536\u6BB5\u843D\u2014\u2014**\u4E0D\u65B0\u5EFA devlog \u6587\u4EF6**\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\uFF08\u771F\u5B9E cuid\uFF09\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 query_audit_logs({ planKeyword })\uFF08devlog \u5373 DevOperation \u8BB0\u5F55\uFF0C\u65E0\u72EC\u7ACB\u6587\u4EF6\uFF09\n"
40
40
  },
41
41
  {
42
42
  "id": "has_add_dev_closed",
@@ -48,7 +48,7 @@ var context = {
48
48
  {
49
49
  "adapter": "qoder",
50
50
  "id": "has_add_dev_unclosed",
51
- "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 handoff \u540C\u76EE\u5F55 devlog-{plan}-v{n}.md\n \u683C\u5F0F: # Devlog: {plan}\\n \u65E5\u671F / Plan / \u8F6E\u6B21 / \u672C\u8F6E\u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 \u67E5\u540C\u76EE\u5F55 devlog-*.md \u2192 query_audit_logs\n"
51
+ "text": "[ADD Stop] \u26A0\uFE0F \u4EE3\u7801\u5DF2\u5B8C\u6210\u4F46\u9A8C\u6536\u672A\u95ED\u73AF:\n{{info}}\n\n\u8BF7\u4F9D\u6B21\u6267\u884C\uFF08\u4E0D\u8981\u7B49\u4E0B\u6B21\u4F1A\u8BDD\uFF09:\n \u2460 Write devlog \u2192 \u8C03\u7528 `record_dev_operation`\uFF08MCP\uFF09\u843D\u5E93\u5BA1\u8BA1\uFF1B\u5E76\u628A\u672C\u8F6E\u9A8C\u6536\u8BB0\u5F55\uFF08\u65E5\u671F / Plan / \u8F6E\u6B21 / \u6539\u4E86\u4EC0\u4E48 / \u9A8C\u6536\u7ED3\u679C / \u9057\u7559\u9879 / \u67B6\u6784\u56DE\u770B\uFF09\u5199\u8FDB handoff \u7684\u9A8C\u6536\u6BB5\u843D\u2014\u2014**\u4E0D\u65B0\u5EFA devlog \u6587\u4EF6**\n \u2461 Edit handoff \u2192 \u66F4\u65B0 \xA7\u9A8C\u8BC1\u6807\u51C6 \u5168\u90E8 [x] + \u8865\u5145\u5BA1\u8BA1 ID\uFF08\u771F\u5B9E cuid\uFF09\n \u2605 \u540C\u6B65: checklist \u6709\u65B0 cuid \u2192 handoff ADD-7 \u8868\u5FC5\u987B\u5BF9\u5E94\u65B0\u589E\u884C\n \u2605 Step 0 \u51C6\u5165: handoff + add-route + Specs \u4E09\u5143\u7EC4\u7F3A\u4E00\u4E0D\u53EF\uFF0C\u7F3A\u5219\u56DE\u9000 Step 0.5\n \u2462 Read docs/ \u2192 \u56DE\u770B\u67B6\u6784\u6587\u6863\u786E\u8BA4\u4E00\u81F4\u6027\n \u2463 Edit add-route \u2192 \u52FE\u9009\u5BF9\u5E94 Step [x]\n\n\u4EE5\u4E0A\u5168\u90E8\u5B8C\u6210\u540E Agent \u624D\u80FD\u505C\u6B62\u3002\n\n\u4E0B\u6B21\u6062\u590D: \u8BFB handoff \u2192 query_audit_logs({ planKeyword })\uFF08devlog \u5373 DevOperation \u8BB0\u5F55\uFF0C\u65E0\u72EC\u7ACB\u6587\u4EF6\uFF09\n"
52
52
  }
53
53
  ],
54
54
  "templates": {
@@ -0,0 +1,46 @@
1
+ <!-- BEGIN:add-workflow-entry -->
2
+ # ADD 工作流入口
3
+
4
+ > 本文件由 add-coder 模板为项目 `{{projectName}}` 渲染生成(真源 `templates/core/AGENTS.md`),占位符按项目的 `{{magicDir}}` 展开。
5
+ > **改动请改真源**,再执行 `npx add-coder sync`;不要只改本文件(下次同步会覆盖)。
6
+
7
+ ## 空白对话开局
8
+
9
+ 每次新对话开始时,必须先调用 skills: `session-init` + mcp tools: `get_project_context({ scope: "add-state" })` 获取 ADD 工作流状态快照,然后根据返回的「待执行 ADD 操作」清单确定下一步。
10
+
11
+ ## 开发执行
12
+
13
+ 所有功能开发、Bug 修复、系统修改必须走 ADD 范式 10 阶段(Step 0-9)。使用 `add-paradigm` SKILL 进入工作流。DO NOT skip sub-steps.
14
+
15
+ ### Step 3 执行风格(`executionMode`)
16
+
17
+ 在 Plan 元信息声明,二选一(缺省 `stepwise`):
18
+
19
+ | 取值 | 行为 | HITL 触点 |
20
+ |------|------|-----------|
21
+ | `stepwise`(默认) | 逐轮逐 Task 单步:每个 Task 完成后停下等人确认 | 每个 Task 后 |
22
+ | `delegated`(托管) | 连续实施该轮全部 Task,**不逐步同步进度** | 仅停止条件触发时 |
23
+
24
+ **托管不降低 ADD 合规度**:每个 Task 的 `record_dev_operation` 落库、`tasks.md` 逐子项勾选、`[T]` 验证、闸门取证、Step 3.5 / Step 0.6.5 / Step 8 一律照做;且**不得自动关闭 Plan**(关闭走 `plan_update` 人类确认)。
25
+
26
+ **两个停止条件**(可判定,用既有工具取证):①文档与代码不对齐:`check_spec_sync` 出现未归因漂移,或实现偏离 Plan/Spec 的 WHEN-THEN;②代码基线低于预期:`npx tsc --noEmit` / `pnpm test` / `npx tsx scripts/validate-docs.ts` 任一失败,或 `check_dps < 80` / `check_rahs < 90`。
27
+
28
+ ## 关键 MCP 工具
29
+
30
+ | 工具 | 用途 | 触发时机 |
31
+ |------|------|---------|
32
+ | `get_project_context({ scope: "add-state" })` | ADD 工作流状态 + 待执行清单 | 空白对话开局 |
33
+ | `check_dps` | DPS 门禁(四维各 25%) | Step 0 末尾 |
34
+ | `check_rahs` | RAHS 门禁(收敛前核定) | Step 4.6 / Step 8 |
35
+ | `check_add_route_status` | add-route 存在性校验 | Step 3 前 |
36
+ | `plan_update` | Plan 生命周期关闭/重开(唯一入口) | 收敛后关闭 / PUL 重开 |
37
+ | `refresh_memory_snapshots` | 刷新 L1/L2 记忆快照 | 会话注入缺数据时 |
38
+
39
+ ## 文档回流
40
+
41
+ Plan Review 的 P0/P1 问题必须在进入 Step 1 前回流至 Plan 体(0.6.5 卡位)。未回流 = Review 白做。
42
+
43
+ ## 端口约定
44
+
45
+ 如本项目与其他项目共享宿主端口,**先查事实源登记表**再占用;项目内端口约定统一记录在 `{{magicDir}}/rules/` 或项目 `docs/ports.md`,禁止在本文件里维护第二份端口清单。
46
+ <!-- END:add-workflow-entry -->
@@ -119,3 +119,55 @@ Claude Code 的注入通道为 **stdout → additionalContext**——hook 脚本
119
119
  | PermissionDenied | 记录拒绝原因 + 建议替代方案 |
120
120
  | StopFailure | 异常退出前紧急 dump State |
121
121
  | ConfigChange | settings.json 热重载 + 变更审计 |
122
+
123
+ ---
124
+
125
+ ## HITL 面板与工具预算(宿主能力适配)
126
+
127
+ > **时效声明**:以下为 **2026-09-21** 的实测与官方文档口径。宿主行为会变——升级 Claude Code 后请按来源链接重新核对。
128
+
129
+ ### 面板能力:不渲染 MCP Apps
130
+
131
+ | 项 | 结论 |
132
+ |---|---|
133
+ | MCP Apps(SEP-1865 `ui://` 资源) | **不渲染**:工具用 `_meta.ui.resourceUri` 绑定 UI 时,仍只返回文本结果,UI 被丢弃 |
134
+ | 来源 | 官方 issue [anthropics/claude-code#95149](https://github.com/anthropics/claude-code/issues/95149)(2026-09-17,label `area:mcp`);[MCP Apps 客户端矩阵](https://modelcontextprotocol.io/extensions/client-matrix) 未列 Claude Code |
135
+
136
+ **降级链(审批走这条)**:
137
+
138
+ ```
139
+ render_hitl_approval({ planName, type })
140
+ ├─ ui.resourceUri → 本端丢弃(不渲染)
141
+ └─ fallback
142
+ ├─ markdownPath → {magicDir}/plans/**/*.hitl.md ← 打开逐维确认
143
+ └─ htmlPath → {magicDir}/hitl/*-round<N>.html ← 浏览器/文件面板打开
144
+ ▼ 人工裁决
145
+ update_hitl({ planName, type, status: "TONGYI|BOHUI" }) ← 写哨兵 + 落库
146
+ ```
147
+
148
+ > 面板缺失**不等于**审批链断裂:`update_hitl` 是工具调用,与 widget 是否渲染无关。
149
+
150
+ ### 工具预算:延迟加载,不是禁用
151
+
152
+ | 项 | 口径 |
153
+ |---|---|
154
+ | 机制 | **MCP tool search 默认开启**:工具定义按需加载,会话启动只加载工具名与 server instructions |
155
+ | 固定上限 | **无固定 per-server 工具上限**;实际约束是上下文预算 |
156
+ | 配置 | `ENABLE_TOOL_SEARCH`:未设(全部延迟)/ `true` / `auto`(定义总量 < 10% 上下文时前载)/ `auto:N` / `false`(全部前载) |
157
+ | 治理工具常驻 | `.mcp.json` 中该 server 设 `"alwaysLoad": true`;或按工具粒度在 `_meta` 设 `"anthropic/alwaysLoad": true` |
158
+
159
+ **硬限制(直接影响治理工具可用性)**:
160
+
161
+ | 限制 | 值 | 后果 |
162
+ |---|---|---|
163
+ | 工具描述截断 | 2KB | 描述过长时关键 WHEN 被截掉,工具更难被检索到 |
164
+ | server instructions 截断 | 2KB | 同上,关键信息必须前置 |
165
+ | MCP 输出上限 | 默认 25k token | 超出落盘为文件,模型按需读取(审计大结果会走这条) |
166
+
167
+ ### 排错
168
+
169
+ | 现象 | 根因 | 处置 |
170
+ |---|---|---|
171
+ | 治理工具"找不到" | 工具定义被延迟加载 | 让模型用 ToolSearch 按名检索;高频工具改 `alwaysLoad` |
172
+ | 审批只看得到文本 | 本端不渲染 MCP Apps | 打开 `fallback.markdownPath` 确认后 `update_hitl` |
173
+ | 工具描述/约束被吞 | 2KB 截断 | 把 WHEN 前移或精简描述 |
@@ -73,3 +73,65 @@ npx add-coder init --adapter=codex
73
73
  | SessionEnd | ❌ | ✅ |
74
74
  | PreCompact | ❌ | ✅ |
75
75
  | 权限系统 | PreToolUse 可阻断 | PermissionRequest/Denied |
76
+
77
+ ---
78
+
79
+ ## HITL 审批面板:三前提与降级路径
80
+
81
+ > **定位**:ADD 的 HITL 审批在 Codex 上走 **MCP Apps core widget**(`render_hitl_approval` 打开审批面板,面板回调 `update_hitl(_use_widget=true)` 落库)。面板打不开是本端最高频的卡点,原因**只有三种**——按顺序排查即可。
82
+ > **触发来源**:`render_hitl_approval` 的返回中已固化这三条排查项(返回文本 + `structuredContent.ui.requiresHostFlag` + `structuredContent.stale`)。
83
+
84
+ ### 前提 ①:宿主实验开关 `enable_mcp_apps`
85
+
86
+ Codex 对 MCP Apps 的渲染能力挂在实验开关后面。**未开启时面板必然报 `This app couldn't be loaded`**,且与是哪个 MCP server 无关(任何 MCP Apps widget 都打不开)。
87
+
88
+ - 开关路径:Codex 的 `/experimental` → `enable_mcp_apps`;
89
+ - 也可写在 `~/.codex/config.toml` 的 `[features]` 段:`enable_mcp_apps = true`;
90
+ - 开启后**需重启 Codex 客户端**才生效;
91
+ - 工具侧标识:`render_hitl_approval` 返回 `ui.requiresHostFlag: "experimental.enable_mcp_apps"`。
92
+
93
+ > **不为该开关增加看门狗(反向约束)**:它是宿主的**实验旗标**,同族还有 `mcp_2026_07_28` / `codex_apps_mcp_2026_07_28` 等处于演进期的名字,随时可能改名或摘除;且开关可由 GUI 切换、持久化位置不稳定,做文件检测极易在"用户其实已开启"时误报。**运行时如实上报(`requiresHostFlag`)+ 本节文档**已覆盖同等职责——不要把它做成常驻检查项。
94
+
95
+ ### 前提 ②:改过工具元数据 / 资源 URI 后必须重连 MCP server
96
+
97
+ MCP Apps 的组件绑定发生在 `tools/list` 解析工具定义 `_meta.ui.resourceUri` 时。因此**只要改过工具元数据或 widget 资源 URI,就必须重连(重启)MCP server**,否则宿主仍按旧元数据取件。
98
+
99
+ - 工具侧标识:`render_hitl_approval` 返回 `stale.stale: true` 时,说明**当前 server 进程启动早于产物更新**,返回文本会明确提示"需重启";
100
+ - 典型场景:改过 `hitl-approval-widget.html`、改过 `_meta.ui.resourceUri` 的生成逻辑、或升级 add-coder 模板后未重启 server;
101
+ - 处置:重启 Codex(或让 Codex 重连该 MCP server),然后重新调用 `render_hitl_approval`。
102
+
103
+ > 资源 URI 本身是**内容哈希**(基名 `ui://add-coder/hitl-approval` + widget 内容 sha256 前 8 位,见 `shared/hitl-ui.ts::getHitlApprovalWidgetUri()`):改 HTML/JS/CSS 会自动换 URI,避免宿主命中旧组件缓存。若 URI 已变而面板仍打不开,回到前提 ①。
104
+
105
+ ### 前提 ③:降级路径(面板不可用时的唯一正解)
106
+
107
+ 面板打不开**不代表 HITL 流程阻断**——提案与裁决都有非 UI 通道:
108
+
109
+ | 环节 | 降级通道 |
110
+ |---|---|
111
+ | 提案审阅 | `render_hitl_approval` 返回的 `fallback.markdownPath`(`{magicDir}/plans/{YYYY-MM}/{DD}/*.hitl.md` 提案文件)与 `fallback.htmlPath`(`{magicDir}/hitl/*.html` 静态审批页) |
112
+ | 人类拍板 | 人类在对话中给出裁决后,以 `update_hitl({ planName, type, status: "TONGYI"|"BOHUI", reason?, _fallback: true })` 落库(写哨兵 + 更新 HitlRecord);`_fallback` 跳过弹框,**裁决语义仍由人类决定**,不得由 AI 自行替代 |
113
+ | 状态查询 | `status_hitl({ planName, type })` 返回最新 round 状态,与 `.hitl-tongyi-*` 哨兵构成双通道校验 |
114
+
115
+ > **红线**:降级不等于"绕过审批"。仍然必须由人类给出同意/驳回,AI 只负责把该裁决如实落库;`BOHUI` 后需 `create_hitl` 新建 round 重新发起,不得复用上一轮结论。
116
+
117
+ ### 前提 ④:面板报 "This app couldn't be loaded" 时的三步定位(2026-09-21 实测结论)
118
+
119
+ 该报错只说明**宿主没把 app 渲染出来**,原因可能在宿主、在 CSP、也可能在业务 widget。用下面三步把责任边界钉死(每步都有可复现命令/工具):
120
+
121
+ **第 1 步 · 服务端自证(MCP 协议直连,不经过宿主)**:确认资源真的能被取到。
122
+
123
+ 用**任意 MCP 客户端**(或自建 stdio 探针)依次调用:`initialize` → `tools/list` → `resources/list` → `resources/read`(URI 取工具 `_meta.ui.resourceUri` 的值)。
124
+
125
+ 期望输出(2026-09-21 实测):`capabilities` 含 `extensions["io.modelcontextprotocol/ui"]`、`instructions` 非空、`render_hitl_approval._meta.ui.resourceUri` 有值、`resources/list` 含 `ui://add-coder/hitl-approval-<hash> [text/html;profile=mcp-app]`、`resources/read` 返回 HTML 正文。**若这一步通过,就排除"资源缺失 / MIME 错 / 注册失败"三类原因。**
126
+
127
+ **第 2 步 · 最小 app 隔离(`probe_widget_render` 工具)**:调用后宿主会渲染一个**零依赖**探针 widget(`ui://add-coder/widget-probe-minimal`:无外部引用、10 行内联脚本、全程 try/catch、不查业务 DOM id)。
128
+
129
+ | 探针面板显示 | 结论 | 处置 |
130
+ |---|---|---|
131
+ | 「ADD widget probe: 内联脚本已执行」 | 宿主能渲染 app ⇒ 问题在**业务 widget**(握手 / DOM / 数据注入) | 改 widget |
132
+ | 「ADD widget probe: HTML 已加载(内联脚本未执行)」 | 宿主 **CSP 拦截 inline script** | 在资源注册的 `_meta.ui.csp` 里显式放行 inline script/style |
133
+ | 仍报 "This app couldn't be loaded" | **宿主渲染器故障**(连最小 app 都加载不了) | 走第 3 步;本地一律走「前提 ③」降级通道 |
134
+
135
+ **第 3 步 · 上游反馈**:把第 1、2 步的输出作为最小复现提交给宿主方,本轮实测结论见下。
136
+
137
+ > **本轮(2026-09-21)实测结论**:`~/.codex/config.toml` 的 `[features] enable_mcp_apps = true` 已开启、服务端第 1 步全部通过,但**最小探针 widget 仍报 "This app couldn't be loaded"** ⇒ **判定为 Codex 桌面端渲染器问题,非 add-coder 缺陷**。宿主日志(`~/.codex/logs_2.sqlite`)中未见对应错误条目,需宿主侧提供更细诊断入口。**在此期间 HITL 一律走「前提 ③」的 markdown/HTML 降级通道**(本轮已用该通道完成 Plan 审批与实现审查审批各一次,均正常落库)。
@@ -97,3 +97,33 @@ UserPromptSubmit hook additional context: ADD workflow active. ...
97
97
  1. **`PROJECT_DIR` 注入链最先执行**:env 优先(QODER/QODERCN_PROJECT_DIR)→ `tryResolveMagicDir` 物理推导——入口薄壳首行,因为治理库可能依赖 `${PROJECT_DIR}` 定位文件
98
98
  2. **additionalContext 放在入口最前**:在任何 `exit` 分支之前,确保所有代码路径都能注入上下文
99
99
  3. **Stop/PreToolUse 不用 JSON**:中断/阻断类输出走 stderr 纯文本,不走 stdout JSON
100
+
101
+ ---
102
+
103
+ ## 工具选择与 genui 审批(宿主能力适配)
104
+
105
+ > **时效声明**:以下为 **2026-09-21** 官方文档口径。来源:[MCP 用户指南](https://docs.qoder.com/user-guide/chat/model-context-protocol) · [MCP 常见问题](https://docs.qoder.com/troubleshooting/mcp-common-issue)。
106
+
107
+ ### 工具选择:按 prompt + 名称/描述自动挑选
108
+
109
+ Qoder IDE 依据**用户输入 prompt** 与**工具的名称与描述**自动选择 MCP 工具(调用前弹确认框);官方排障页另建议:**避免 MCP 服务器与工具命名过于相似**。
110
+
111
+ ⇒ 治理工具能否被选中,取决于其**名称与描述是否可被检索命中**——不存在确定性白名单开关。
112
+
113
+ **命名/描述规范(本项目工具族)**:
114
+
115
+ | 规则 | 反例 | 正例 |
116
+ |---|---|---|
117
+ | 描述以「动词 + 治理对象 + 触发时机」开头 | "HITL 相关操作" | "提交一次性人类审批裁决(Plan 启动 / 评审前必须调用)" |
118
+ | 同族工具描述不得雷同 | `plan_track` 与 `plan_sync` 都写"更新 Plan 数据" | `plan_track`="扫描文档回填进度";`plan_sync`="把进度写回 Plan 文档" |
119
+ | 描述带 ADD 语境 | "记录操作" | "记录开发操作审计(ADD-7,改完文件必须调用)" |
120
+
121
+ ### 前提:Agent 模式 + 已打开项目目录
122
+
123
+ 未打开项目目录时,Qoder IDE 默认 **Ask 模式**,**不调用 MCP 工具**;审批与审计链要求切到 **Agent 模式**。
124
+
125
+ ### genui 审批路径
126
+
127
+ Qoder 端走 **genui**:`hitl-interaction-rules` 按环境裁决 → 先 `genui show_widget` 渲染逐项决策表单 → 用户拍板后以 `_use_genui=true` 调 `update_hitl` 落库。
128
+
129
+ genui 不可用时的降级与 Claude Code 一致:打开 `fallback.markdownPath` 人工确认 → `update_hitl` 落库。
@@ -61,3 +61,45 @@ Trae 的注入通道为 **stdout**(与 Claude Code 兼容)。Trae 支持导
61
61
  | SessionEnd | ❌ | ✅ |
62
62
  | PreCompact | ❌ | ✅ |
63
63
  | 权限系统 | 无 hookpoint | PermissionRequest/Denied |
64
+
65
+ ---
66
+
67
+ ## 工具预算与 HITL 降级(宿主能力适配)
68
+
69
+ > **时效声明**:以下为 **2026-09-21** 官方文档口径。来源:[Troubleshoot general issues](https://docs.trae.ai/ide/troubleshoot-general-issues)。
70
+
71
+ ### 工具预算:输入长度包含「全部 MCP 工具」
72
+
73
+ 官方排障文档「Chat functionality exceptions caused by excessively long input」明确:输入长度包含下列全部内容——
74
+
75
+ 1. 输入框内容
76
+ 2. 自定义 agent 的 prompt
77
+ 3. **该 agent 所用 MCP server 的全部工具定义**
78
+ 4. 用户规则 + 项目规则
79
+
80
+ | 现象 | 后果 |
81
+ |---|---|
82
+ | 总长度超限 | **聊天功能异常(问题发不出去)**、问答质量下降 |
83
+ | 官方建议 | 精简提问 / agent prompt / MCP 工具 / 规则;或切换模型 |
84
+
85
+ > **口径提醒**:add-coder 工具族 40+ 个,但**没有**"工具数 > N 即中断"的公开阈值——约束对象是**总长度**,不要写成固定工具数。
86
+
87
+ **裁剪指引(按需选)**:
88
+
89
+ | 手段 | 做法 | 代价 |
90
+ |---|---|---|
91
+ | 按 server 取舍 | 业务任务只挂业务 server,治理任务只挂 add-coder | 单会话内无法混用两类工具 |
92
+ | 精简描述 | 工具描述只留「做什么 + 何时用」,长示例移到文档 | 需要维护描述预算 |
93
+ | 拆分挂载 | 建两个 agent(治理 / 业务),各自挂载所需工具 | 需要在 agent 间切换 |
94
+ | 缩短规则 | 临时折叠与本次任务无关的项目规则段落 | 规则可发现性下降 |
95
+
96
+ ### HITL 降级
97
+
98
+ Trae 不渲染 MCP Apps widget ⇒ 与 Claude Code 同一条降级链:
99
+
100
+ ```
101
+ render_hitl_approval → fallback.markdownPath({magicDir}/plans/**/*.hitl.md)
102
+ → 人工逐维确认 → update_hitl(写哨兵 + 落库)
103
+ ```
104
+
105
+ > 审批链依赖**工具调用**,不依赖 Hook:Trae 原生 6 事件里缺的卡位(SessionEnd / PostToolUseFailure 等)不影响 HITL 落库与 ADD-7 审计。