kenaz 0.3.9 → 0.3.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 (117) hide show
  1. package/core/.claude/commands/Ansuz.md +12 -16
  2. package/core/.claude/hooks/memory-guard.js +13 -0
  3. package/core/.claude/skills/code_reviewer/SKILL.md +0 -1
  4. package/core/.claude/skills/code_reviewer/patterns.yaml +0 -8
  5. package/core/.claude/skills/dev_agent/SKILL.md +3 -6
  6. package/core/.claude/skills/heimdall/SKILL.md +5 -6
  7. package/core/.claude/skills/huginn/SKILL.md +11 -38
  8. package/core/.collaboration/core/task-api.js +2 -218
  9. package/core/.collaboration/core/task-api.test.js +1 -138
  10. package/core/.formation/bifrost/launch.js +2 -2
  11. package/core/.formation/bifrost/launch.test.js +2 -2
  12. package/core/.formation/bifrost/shared/ansuz-boot-policy.js +3 -3
  13. package/core/.formation/bifrost/shared/codex-injection.js +3 -17
  14. package/core/.formation/bifrost/shared/consolidate.js +1 -51
  15. package/core/.formation/bifrost/sync.js +4 -5
  16. package/core/.formation/tools/__tests__/subagent-bifrost-guard.test.js +1 -1
  17. package/core/.formation/tools/dispatch.js +2 -2
  18. package/core/.formation/tools/kenaz-stats.js +259 -0
  19. package/core/.formation/tools/kenaz-stats.test.js +180 -0
  20. package/core/.formation/tools/mcp-allowlist.test.js +5 -3
  21. package/core/.formation/tools/post-complete/bounded-http.js +1 -40
  22. package/core/.formation/tools/post-complete/bounded-http.test.js +2 -89
  23. package/core/.formation/tools/post-complete/cognitive.js +2 -119
  24. package/core/.formation/tools/post-complete/testing.js +3 -21
  25. package/core/.formation/tools/post-complete.js +2 -428
  26. package/core/.formation/tools/post-complete.test.js +0 -135
  27. package/core/.formation/tools/prompt.js +14 -0
  28. package/core/.formation/tools/route-audit.test.js +8 -20
  29. package/core/.rule/INDEX.yaml +3 -3
  30. package/core/.rule/general/ddx-synapse-stack.yaml +11 -11
  31. package/core/.rule/general/specialist-identity-contract.yaml +5 -5
  32. package/core/.rule/general/synapse-code-intelligence.yaml +25 -111
  33. package/core/.rule/general/worktree-isolation.yaml +2 -2
  34. package/core/.rule/pipeline/task-execution.yaml +6 -9
  35. package/core/.specialist/Amelia/README.md +5 -5
  36. package/core/.specialist/Amelia/capability.yaml +10 -18
  37. package/core/.specialist/Amelia/knowledge.yaml +15 -66
  38. package/core/.specialist/Amelia/workflow.yaml +12 -21
  39. package/core/.specialist/Ansuz/RULES.md +6 -0
  40. package/core/.specialist/Ansuz/capability.yaml +12 -87
  41. package/core/.specialist/Ansuz/concepts/autonomous_agenda.yaml +1 -1
  42. package/core/.specialist/Ansuz/concepts/autonomy_contract.yaml +2 -3
  43. package/core/.specialist/Ansuz/concepts/capability_pointers.yaml +1 -3
  44. package/core/.specialist/Ansuz/concepts/cognitive_fluency_contract.yaml +8 -21
  45. package/core/.specialist/Ansuz/concepts/cognitive_systems_overview.yaml +0 -4
  46. package/core/.specialist/Ansuz/concepts/cognitive_systems_turn_context.yaml +1 -1
  47. package/core/.specialist/Ansuz/concepts/completion_tracking.yaml +0 -1
  48. package/core/.specialist/Ansuz/concepts/deep_knowledge_pointers.yaml +0 -3
  49. package/core/.specialist/Ansuz/concepts/evolvable_guardrails.yaml +3 -3
  50. package/core/.specialist/Ansuz/concepts/fatigue_handoff.yaml +1 -7
  51. package/core/.specialist/Ansuz/concepts/guardrails_immutable.yaml +3 -4
  52. package/core/.specialist/Ansuz/concepts/identity.yaml +1 -2
  53. package/core/.specialist/Ansuz/concepts/phase9_verification.yaml +0 -1
  54. package/core/.specialist/Ansuz/concepts/phase_protocol.yaml +3 -4
  55. package/core/.specialist/Ansuz/core.yaml +3 -6
  56. package/core/.specialist/Ansuz/elixir.yaml +6 -6
  57. package/core/.specialist/Ansuz/exo_cortex_tools.yaml +5 -9
  58. package/core/.specialist/Ansuz/guards/blind_spot_guards.yaml +2 -2
  59. package/core/.specialist/Ansuz/guards/lesson_guards.yaml +6 -19
  60. package/core/.specialist/Ansuz/identity_contract.yaml +4 -4
  61. package/core/.specialist/Ansuz/knowledge.yaml +13 -40
  62. package/core/.specialist/Ansuz/modules/ddx.yaml +2 -2
  63. package/core/.specialist/Ansuz/modules/prophecy.yaml +19 -19
  64. package/core/.specialist/Ansuz/modules/pulse_empty.yaml +6 -17
  65. package/core/.specialist/Ansuz/tools/ansuz-handoff-snapshot.js +3 -102
  66. package/core/.specialist/Ansuz/tools/ansuz-sdk-runner.js +2 -44
  67. package/core/.specialist/Ansuz/workflow.yaml +32 -76
  68. package/core/.specialist/Forseti/core.yaml +2 -7
  69. package/core/.specialist/Forseti/rule.yaml +1 -1
  70. package/core/.specialist/Heimdall/SKILL.md +2 -3
  71. package/core/.specialist/Heimdall/capability.yaml +22 -45
  72. package/core/.specialist/Heimdall/core.yaml +24 -126
  73. package/core/.specialist/Heimdall/principles.yaml +8 -11
  74. package/core/.specialist/Heimdall/rule.yaml +2 -2
  75. package/core/.specialist/Huginn/capability.yaml +31 -52
  76. package/core/.specialist/Huginn/core.yaml +43 -123
  77. package/core/.specialist/Huginn/principles.yaml +5 -5
  78. package/core/.specialist/Huginn/rule.yaml +3 -9
  79. package/core/.specialist/REFLECTION_TO_CONTROL.md +3 -4
  80. package/core/.specialist/shared/guard_schema.json +1 -1
  81. package/core/.specialist/shared/principles.yaml +6 -6
  82. package/core/.specialist/tools/__tests__/task-2409-lessons.test.js +95 -0
  83. package/core/.specialist/tools/ansuz-boot-fast.js +1 -0
  84. package/core/.specialist/tools/lessons.js +234 -0
  85. package/core/.specialist/tools/phase-gate.js +10 -230
  86. package/core/.specialist/tools/phase-gate.test.js +2 -2
  87. package/core/.specialist/tools/prompt-files.test.js +1 -1
  88. package/core/.specialist/tools/session.js +2 -89
  89. package/core/.system/memory/README.md +3 -0
  90. package/core/CLAUDE.md +6 -7
  91. package/core/KENAZ_CORE_VERSION +1 -1
  92. package/core/dev/plans/CLI_FIRST_INSTALL_FLOW.md +26 -0
  93. package/core/plugins/kenaz/INVENTORY.md +1 -1
  94. package/core/plugins/kenaz/commands/Ansuz.md +12 -16
  95. package/core/plugins/kenaz/scripts/kenaz.test.js +2 -1
  96. package/core/plugins/kenaz/scripts/ygg.js +2 -0
  97. package/core/plugins/kenaz/skills/code_reviewer/SKILL.md +0 -1
  98. package/core/plugins/kenaz/skills/code_reviewer/patterns.yaml +0 -8
  99. package/core/plugins/kenaz/skills/dev_agent/SKILL.md +3 -6
  100. package/lib/cli.js +13 -3
  101. package/package.json +6 -6
  102. package/core/.claude/hooks/handoff-snapshot-on-decision.js +0 -188
  103. package/core/.claude/skills/synapse_maintain/SKILL.md +0 -84
  104. package/core/.formation/tools/alaya-gate.js +0 -198
  105. package/core/.formation/tools/post-complete/health.js +0 -464
  106. package/core/.formation/tools/post-complete/task2038.test.js +0 -246
  107. package/core/.formation/tools/post-complete/task2341.test.js +0 -74
  108. package/core/.rule/general/cortex-tools.yaml +0 -181
  109. package/core/.rule/general/memory-language.yaml +0 -67
  110. package/core/.rule/general/memory-source-priority.yaml +0 -207
  111. package/core/.rule/general/synapse-cognitive-fusion.yaml +0 -301
  112. package/core/.specialist/Ansuz/concepts/cognitive_systems_hippocampus.yaml +0 -14
  113. package/core/.specialist/Ansuz/concepts/write_verification.yaml +0 -26
  114. package/core/.specialist/Ansuz/modules/recall.yaml +0 -45
  115. package/core/.specialist/tools/MEMORY_RECALL_TRIGGERS.md +0 -156
  116. package/core/.specialist/tools/context-injection.js +0 -395
  117. package/core/dev/scripts/probe/hippocampus-concurrency.js +0 -247
@@ -289,103 +289,6 @@ function atomicWriteJson(filePath, obj) {
289
289
  }
290
290
  }
291
291
 
292
- // ─────────────────────────────────────────
293
- // Dashboard HTTP helpers (best-effort — skip gracefully if server not running)
294
- // ─────────────────────────────────────────
295
-
296
- async function callMcpTool(toolName, toolArgs) {
297
- const http = require('http');
298
- const body = JSON.stringify({
299
- jsonrpc: '2.0',
300
- id: 1,
301
- method: 'tools/call',
302
- params: { name: toolName, arguments: toolArgs },
303
- });
304
-
305
- return new Promise((resolve, reject) => {
306
- const req = http.request(
307
- {
308
- hostname: '127.0.0.1',
309
- port: 18080,
310
- path: '/api/mcp',
311
- method: 'POST',
312
- headers: {
313
- 'Content-Type': 'application/json',
314
- 'Content-Length': Buffer.byteLength(body),
315
- },
316
- timeout: 4000,
317
- },
318
- (res) => {
319
- let data = '';
320
- res.on('data', (chunk) => { data += chunk; });
321
- res.on('end', () => {
322
- try {
323
- const parsed = JSON.parse(data);
324
- if (parsed.error) { reject(new Error(parsed.error.message)); return; }
325
- const text = parsed.result?.content?.[0]?.text;
326
- resolve(text ? JSON.parse(text) : parsed.result);
327
- } catch (e) { reject(e); }
328
- });
329
- }
330
- );
331
- req.on('error', reject);
332
- req.on('timeout', () => { req.destroy(); reject(new Error('mcp timeout')); });
333
- req.write(body);
334
- req.end();
335
- });
336
- }
337
-
338
- async function getKeyDecisionsFromSynapse(projectPath, n = 5) {
339
- try {
340
- // Use synapse_recall (project-scoped, honours projectPath) — NOT cortex_recall,
341
- // which is cross-project global and would bleed core/other-project decisions
342
- // into a sub-project's handoff (e.g. KenazAI HOOK_MARKER/TASK_574 into jiayao).
343
- const result = await callMcpTool('synapse_recall', {
344
- query: 'decision risk ash',
345
- type: 'decision',
346
- projectPath,
347
- limit: n,
348
- });
349
- const runes = result?.runes || [];
350
- if (!Array.isArray(runes) || runes.length === 0) return null;
351
- return runes.slice(0, n).map((r) => {
352
- const type = r.type || r.rune_type || 'rune';
353
- const text = r.content || r.body || r.summary || '';
354
- return `[${type}] ${String(text).substring(0, 100)}`;
355
- }).filter(Boolean);
356
- } catch {
357
- return null;
358
- }
359
- }
360
-
361
- async function archiveHandoffToHippocampus(handoff, projectPath) {
362
- try {
363
- const startResult = await callMcpTool('hippocampus_episode_start', {
364
- scope: `handoff:${handoff.session_id}`,
365
- projectPath,
366
- });
367
- const episodeId = startResult?.episode_id;
368
- if (!episodeId) return false;
369
-
370
- await callMcpTool('hippocampus_episode_append', {
371
- episode_id: episodeId,
372
- phase: 'archive',
373
- content: JSON.stringify(handoff, null, 2),
374
- projectPath,
375
- });
376
-
377
- await callMcpTool('hippocampus_episode_close', {
378
- episode_id: episodeId,
379
- summary: `Archived handoff snapshot session=${handoff.session_id} trigger=${handoff.triggered_by}`,
380
- outcome: 'archived',
381
- projectPath,
382
- });
383
- return true;
384
- } catch {
385
- return false;
386
- }
387
- }
388
-
389
292
  // ─────────────────────────────────────────
390
293
  // ISBAR builder
391
294
  // ─────────────────────────────────────────
@@ -560,17 +463,15 @@ async function main() {
560
463
  if (fs.existsSync(handoffPath)) {
561
464
  try {
562
465
  const existing = JSON.parse(fs.readFileSync(handoffPath, 'utf8'));
563
- if (existing.session_id && existing.session_id !== sessionId) {
564
- await archiveHandoffToHippocampus(existing, projectPath);
565
- } else if (existing.session_id === sessionId) {
466
+ if (existing.session_id === sessionId) {
566
467
  priorHistory = Array.isArray(existing.history) ? existing.history.slice(-9) : [];
567
468
  if (existing.generated_at) priorHistory.push(existing.generated_at);
568
469
  }
569
470
  } catch { /* ignore corrupt/missing */ }
570
471
  }
571
472
 
572
- // Pull recent decision/risk runes from this project's Synapse (best-effort; skip if dashboard not running)
573
- const keyDecisions = await getKeyDecisionsFromSynapse(projectPath, 5);
473
+ // Key decisions came from Synapse runes; the memory system was removed in TASK_2411.
474
+ const keyDecisions = null;
574
475
 
575
476
  // Build handoff object
576
477
  const generatedAt = new Date().toISOString();
@@ -838,37 +838,6 @@ async function runBackgroundCognitiveLoop(lastActivityMs = Date.now()) {
838
838
  try {
839
839
  const signalsToAppend = [];
840
840
 
841
- // Step 4c: hippocampus_due_episodes as signal source (TASK_965)
842
- try {
843
- const dueResp = await callMcpTool('hippocampus_due_episodes', { projectPath: ROOT }, MCP_FAST_TIMEOUT_MS);
844
- const dueContent = dueResp?.result?.content || dueResp?.content || [];
845
- const dueText = (Array.isArray(dueContent) ? dueContent : [dueContent])
846
- .map(c => (typeof c === 'string' ? c : c?.text || '')).join('\n');
847
- let dueCount = 0;
848
- try {
849
- const parsed = JSON.parse(dueText);
850
- dueCount = parsed?.due_count ?? (Array.isArray(parsed?.episodes) ? parsed.episodes.length : 0);
851
- } catch { /* non-JSON */ }
852
- if (dueCount > 0) {
853
- signalsToAppend.push({
854
- detected_at: new Date().toISOString(),
855
- source: 'hippocampus',
856
- signal_type: 'due_episodes',
857
- summary: `${dueCount} hippocampus episode(s) due for review`,
858
- symbols: ['hippocampus_review'],
859
- meta: { due_count: dueCount },
860
- });
861
- log(`[BG] Signal: due_episodes — ${dueCount} episode(s) due`);
862
- } else {
863
- // TASK_1095 (I1): source cleared — drop stale ambient entries AND
864
- // evict hippocampus_review from attentionBuffer so Grade 4 routing
865
- // stops re-appending on subsequent cycles.
866
- reconcileSignalSource('due_episodes', false, ['hippocampus_review']);
867
- }
868
- } catch (e) {
869
- log(`[BG] Warning: hippocampus_due_episodes failed (5s timeout): ${e.message}`);
870
- }
871
-
872
841
  // TASK_1095 (Amelia Blocker 4): TTL-based defense in depth for signal types
873
842
  // with no MCP source-check (curiosity_spike, hot_symbol_unaddressed_risk,
874
843
  // efe_weight_shift, heat). 30min > 5min tick interval but < 50min trim horizon.
@@ -953,9 +922,8 @@ function buildThalamusPrompt(msg) {
953
922
  `Suggested action: ${ctx.suggested_action || 'update stale runes'}`,
954
923
  ``,
955
924
  `## Allowed Actions (Level 1 only)`,
956
- `- Update stale runes for fired symbols (synapse_recall → synapse_inscribe if outdated)`,
925
+ `- Review the fired symbols with kenaz_lsp_references / kenaz_lsp_callers`,
957
926
  `- Fix lint issues in fired symbol files`,
958
- `- Inscribe new risk/fix runes for discovered issues`,
959
927
  ``,
960
928
  `## Forbidden Actions`,
961
929
  `- Do NOT create new tasks`,
@@ -964,7 +932,7 @@ function buildThalamusPrompt(msg) {
964
932
  `- Do NOT create pull requests`,
965
933
  `- Do NOT modify files outside the fired symbols' scope`,
966
934
  ``,
967
- `Review fired symbols, update any stale runes, and report findings. Keep it brief.`,
935
+ `Review the fired symbols and report findings. Keep it brief.`,
968
936
  ].join('\n');
969
937
  }
970
938
 
@@ -999,25 +967,15 @@ async function runQuery(prompt, sessionId) {
999
967
  'Bash', 'Edit', 'Glob', 'Grep', 'Read', 'Write',
1000
968
  'ToolSearch', 'TodoWrite', 'WebFetch', 'WebSearch',
1001
969
  'AskUserQuestion',
1002
- 'mcp__kenaz-dashboard__synapse_recall',
1003
970
  'mcp__kenaz-dashboard__kenaz_lsp_callers',
1004
971
  'mcp__kenaz-dashboard__kenaz_lsp_references',
1005
972
  'mcp__kenaz-dashboard__kenaz_lsp_definition',
1006
973
  'mcp__kenaz-dashboard__kenaz_lsp_symbols',
1007
- 'mcp__kenaz-dashboard__synapse_inscribe',
1008
- 'mcp__kenaz-dashboard__synapse_forge',
1009
- 'mcp__kenaz-dashboard__synapse_sigils',
1010
- 'mcp__kenaz-dashboard__hippocampus_episode_start',
1011
- 'mcp__kenaz-dashboard__hippocampus_episode_append',
1012
- 'mcp__kenaz-dashboard__hippocampus_episode_close',
1013
- 'mcp__kenaz-dashboard__hippocampus_episode_recall',
1014
- 'mcp__kenaz-dashboard__hippocampus_episode_link',
1015
974
  'mcp__kenaz-dashboard__kenaz_task_create',
1016
975
  'mcp__kenaz-dashboard__kenaz_task_update',
1017
976
  'mcp__kenaz-dashboard__kenaz_task_list',
1018
977
  'mcp__kenaz-dashboard__kenaz_task_get',
1019
978
  'mcp__kenaz-dashboard__kenaz_task_delete',
1020
- 'mcp__kenaz-dashboard__cortex_recall',
1021
979
  ],
1022
980
  mcpServers: {
1023
981
  'kenaz-dashboard': {
@@ -136,8 +136,6 @@ phase_0_initialization:
136
136
  - "node {CORE}/.collaboration/core/task-api.js [--project-path {PROJECT_PATH}] list — 統計任務狀態"
137
137
  - "node {CORE}/.collaboration/core/task-api.js [--project-path {PROJECT_PATH}] stats — 取得統計數據"
138
138
  - "讀 .failure/INDEX.yaml — 檢查已知失敗模式(從 CORE 讀取)"
139
- - "synapse_status — 檢查索引狀態。注意 rune_count/sigil_count:這些是其他 agent 留下的認知記憶,裡面有 bug 根因、設計決策、隱藏約束等你看不到的知識。數字越大,認知層越豐富 — 後續工作中遇到相關 symbol 時用 synapse_recall 查詢"
140
- - "cortex_status — 檢查跨專案數據可用性(失敗則跳過 Cortex,不阻塞)"
141
139
  present:
142
140
  banner: "ᚨ Ansuz 已就位"
143
141
  show:
@@ -192,12 +190,7 @@ phase_0_initialization:
192
190
  phase_1_requirement_analysis:
193
191
  goal: "快速理解用戶需求,澄清模糊點"
194
192
  cognitive_checkpoint:
195
- recall: "synapse_recall + cortex_recall 並行查詢需求相關的歷史 rune(risk/fix),帶著認知做分析"
196
- rule: "Phase 1 開始時必須執行 recall(本地 + 跨專案),不可跳過"
197
- parallel: |
198
- 同時呼叫(無依賴,必須並行):
199
- - synapse_recall({ query: '<需求關鍵詞>' }) — 本地認知
200
- - cortex_recall({ query: '<需求關鍵詞>' }) — 跨專案認知(不可用時靜默跳過)
193
+ note: "歷史教訓在 native memory 檔 / `kenaz learn list`;代碼現況用 Grep + kenaz_lsp_references 查"
201
194
  ddx_trigger:
202
195
  when: "需求類型為 Bug/Fix — 根因不明確時"
203
196
  how: "套用 differential_over_intuition protocol — 列出 3+ 可能根因,用客觀數據(日誌、代碼路徑)逐一排除"
@@ -237,93 +230,59 @@ phase_1_requirement_analysis:
237
230
  # Phase 2: 背景調研
238
231
  # ═══════════════════════════════════════
239
232
  phase_2_research:
240
- goal: "用 recall + LSP callers + Grep 建立完整 context — 先查證,再設計任務"
233
+ goal: "用 LSP callers + Grep 建立完整 context — 先查證,再設計任務"
241
234
  actions:
242
235
  - "搜尋相關 Feature ID(Grep dev/PRODUCT_SPEC.md)"
243
- - "synapse_recall(目標 symbol / 主題) — 已知 risk/decision/fix rune"
244
236
  - "kenaz_lsp_callers / kenaz_lsp_references(目標 symbol) — 誰呼叫、誰引用(爆炸半徑)"
245
237
  - "Grep/Glob — 探索相關代碼與檔案結構"
246
- - "cortex_recall — 查跨專案歷史知識(已知 risk/decision/fix,不止當前專案)"
247
- - "cortex_search — 找其他專案的相似實現(避免重複造輪子)"
248
238
  - "Grep — 僅用於精確字串匹配(如 error code、常量值、枚舉所有 X)"
249
239
  - "查閱類似已完成任務(task-api.js search)"
250
240
  - "檢查 .failure/ 相關失敗模式 — 避免重複踩坑"
251
241
  caller_driven_research:
252
- description: "對核心 symbol 查 recall + LSP callers,驅動 Phase 3 任務設計"
242
+ description: "對核心 symbol 查 LSP callers,驅動 Phase 3 任務設計"
253
243
  note: "核心 symbol 來源:需求描述或 scope_paths 推斷"
254
244
  steps:
255
245
  - "從需求描述或 scope_paths 推斷核心 symbol"
256
- - "對每個核心 symbol → synapse_recall(symbol) + kenaz_lsp_callers(symbol)"
246
+ - "對每個核心 symbol → kenaz_lsp_callers(symbol) + kenaz_lsp_references(symbol)"
257
247
  - "從結果提取:"
258
248
  - " scope_paths ← callers/references 所在文件"
259
249
  - " dependencies ← callers/callees 跨文件的真依賴"
260
250
  - " complexity ← caller 數量 + 跨模組數量決定"
261
- - " known_risks ← recall 的歷史 rune 注入 technical_notes"
262
- rule: "Phase 3 的 scope_paths 和 dependencies 必須有 LSP / recall 結果支撐,不靠推斷"
263
- cross_project_thinking:
264
- description: "跨專案調研 — PM 層面主動觸發跨專案知識查詢"
265
- rule: "Phase 2 調研時,除了當前專案內的搜尋,必須主動思考跨專案維度"
266
- # ─── 強制觸發(不再是建議清單) ───
267
- mandatory_recall:
268
- rule: |
269
- Phase 2 開始時,與 synapse_recall 同步執行 cortex_recall(可並行):
270
- - cortex_recall({ query: '<需求核心關鍵詞>' })
271
- - 有結果 → 注入 Phase 3 的 technical_notes(標記 [Cortex] 前綴)
272
- - 無結果 → 靜默跳過,不阻塞
273
- - cortex 不可用 → 靜默跳過,不阻塞
274
- why: "跨專案的 risk/fix/decision rune 是免費的經驗值 — 不查就浪費"
275
- parallel_with: "synapse_recall — 兩者無依賴,必須並行呼叫"
276
- questions:
277
- - "這個需求涉及的概念,在其他專案中有沒有類似的實現或處理方式?→ cortex_search"
278
- - "其他專案在相同領域有沒有踩過坑(已知 risk/fix)?這個功能是否可以複用其他專案已有的模組或 pattern?如果是跨專案共用的概念(如 auth、i18n、error handling),是否需要統一方案?→ cortex_recall"
279
- when_to_skip:
280
- - "cortex_status 顯示無跨專案數據(Phase 0 已偵測)"
281
- - "Cortex MCP 不可用(Phase 0 已偵測,graceful degradation)"
282
- when_NOT_to_skip:
283
- - "❌ 「需求局限於當前專案內部」不是跳過理由 — 其他專案可能踩過同樣的坑"
284
- - "❌ 「看起來很簡單」不是跳過理由 — recall 成本極低(一次 FTS5 查詢)"
285
- # ─── 空結果協議(recall / callers 回空時的替代策略) ───
251
+ - " known_risks ← .failure/ 與 native memory 相關條目注入 technical_notes"
252
+ rule: "Phase 3 的 scope_paths 和 dependencies 必須有 LSP 結果支撐,不靠推斷"
253
+ # ─── 空結果協議(callers 回空時的替代策略) ───
286
254
  empty_result_protocol:
287
- when: "synapse_recall 與 kenaz_lsp_callers 都回空(新功能、新專案、未知領域)"
288
- principle: "本地無資料 → 跨專案查詢(Cortex)→ 手動探索 → 保守設計"
255
+ when: "kenaz_lsp_callers 回空(新功能、新專案、未知領域)"
256
+ principle: "本地無資料 → 手動探索 → 保守設計"
289
257
  phase_2_actions:
290
- - "cortex_search({ query: '<功能描述>' }) — 跨專案語義搜尋:其他專案有沒有類似功能的實現?"
291
- - "cortex_recall({ query: '<領域關鍵詞>' }) — 跨專案認知回憶:其他專案在同領域踩過什麼坑?有什麼架構決策?這個概念在哪些專案出現過?"
292
258
  - "Grep/Glob/Read — 手動探索相關目錄結構和現有 pattern"
293
259
  - "查 .failure/ — 類似功能有沒有已知失敗模式"
294
260
  phase_3_actions:
295
- - "scope_paths 靠目錄結構 + 現有 pattern + Cortex 建議推斷(標記為 '未經 LSP 驗證')"
261
+ - "scope_paths 靠目錄結構 + 現有 pattern 推斷(標記為 '未經 LSP 驗證')"
296
262
  - "complexity 預設 medium(無數據,保守估計)"
297
- - "technical_notes 必須注入:'此任務為新功能/未知領域,無本地 recall/callers 數據。Cortex 跨專案參考:{cortex 結果摘要}。Agent 需自行探索 codebase 並遵循現有 pattern。'"
298
- - "如果 Cortex 找到其他專案的類似實現 → 在 technical_notes 中附上參考路徑和 pattern"
299
- agent_post_code_emphasis:
300
- - "synapse_inscribe — 為新功能留下認知種子(特別重要!這是該領域的第一顆種子)"
301
- - "synapse_forge — 如果新功能涉及 3+ symbols → 建立 Sigil 概念節點"
302
- - "這些種子是未來 agent 在這個領域的起點 — 寧可多寫不可少寫"
263
+ - "technical_notes 必須注入:'此任務為新功能/未知領域,無本地 callers 數據。Agent 需自行探索 codebase 並遵循現有 pattern。'"
303
264
  # ─── 預言比較(多方案脈衝對比) ───
304
265
  prophetic_comparison:
305
266
  description: "偵測到多方案時,自動觸發脈衝比較流程"
306
267
  trigger: |
307
268
  Phase 2 調研過程中偵測到以下任一信號:
308
269
  - 需求描述包含選擇(「A 或 B」「可以改 X 也可以改 Y」)
309
- - recall/callers 結果顯示多個可行切入點(如 callers 分佈在不同模組)
270
+ - callers 結果顯示多個可行切入點(如 callers 分佈在不同模組)
310
271
  - DDx 產出 2+ 競爭方案
311
272
  - Ansuz 自身分析出 2+ 路徑
312
273
  actions:
313
- - "對每個方案的目標 symbol 各跑 kenaz_lsp_callers + synapse_recall(可並行)"
314
- - "收集 caller 數 / 跨模組數 / known_risks"
274
+ - "對每個方案的目標 symbol 各跑 kenaz_lsp_callers + kenaz_lsp_references(可並行)"
275
+ - "收集 caller 數 / 跨模組數 / .failure 風險"
315
276
  - "填入 🔮 預言比較 cli_format 表格"
316
277
  - "比較結果直接注入 Phase 3 的 scope_paths 和 complexity 決策"
317
278
  output: "預言比較表格 — 併入 Phase 3 cli_format 輸出給用戶"
318
279
  example: |
319
280
  需求:「修復 data flow 斷裂,可以改 source 端或改 sink 端」
320
- → kenaz_lsp_callers + synapse_recall(source_symbol) → 8 callers, 跨 1 模組, 0 risks
321
- → kenaz_lsp_callers + synapse_recall(sink_symbol) → 22 callers, 跨 3 模組, 2 risks
281
+ → kenaz_lsp_callers(source_symbol) → 8 callers, 跨 1 模組, 0 risks
282
+ → kenaz_lsp_callers(sink_symbol) → 22 callers, 跨 3 模組, 2 risks
322
283
  → 結論: 改 source 端 — 爆炸半徑小 3 倍,零歷史風險
323
- synapse_fallback:
284
+ lsp_fallback:
324
285
  rule: "kenaz_lsp_* 首次呼叫失敗 → 該 session 降級用 Grep/Glob,不重試"
325
- cortex_fallback:
326
- rule: "cortex 工具不可用 → 跳過跨專案查詢,不阻塞流程"
327
286
  cli_format: |
328
287
  🔍 背景調研 + 影響分析
329
288
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -331,9 +290,9 @@ phase_2_research:
331
290
 
332
291
  ⚡ 影響分析: {symbol_name}
333
292
  爆炸半徑: {caller_count} callers, {module_crossings} 跨模組(kenaz_lsp_callers)
334
- 歷史風險: {known_risks 摘要}(synapse_recall)
293
+ 已知風險: {.failure / native memory 摘要}
335
294
 
336
- 相關代碼: {從 callers/recall 提取的檔案路徑}
295
+ 相關代碼: {從 callers 提取的檔案路徑}
337
296
  類似任務: {TASK_XXX, TASK_YYY}
338
297
  已知風險: {.failure 中的相關模式}
339
298
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -343,7 +302,7 @@ phase_2_research:
343
302
  # Phase 3: 任務設計
344
303
  # ═══════════════════════════════════════
345
304
  phase_3_task_design:
346
- goal: "用 recall + callers 結果拆解需求為獨立、可執行的任務 — 證據驅動,不靠推斷"
305
+ goal: "用 callers 結果拆解需求為獨立、可執行的任務 — 證據驅動,不靠推斷"
347
306
  ddx_trigger:
348
307
  when: "需求有多種實作方案、或涉及架構決策時"
349
308
  how: "套用 differential_over_intuition protocol — 列出 2+ 方案,用客觀證據排序後選最優"
@@ -353,31 +312,31 @@ phase_3_task_design:
353
312
  rule: |
354
313
  DDx 識別出 2+ 方案後,不可直接靠推斷選方案。
355
314
  必須檢查 Phase 2 是否已有預言比較結果:
356
- - 有 → 直接引用 callers/recall 數據做選擇
357
- - 無 → 此刻補跑:對每個方案目標 symbol 各跑 kenaz_lsp_callers + synapse_recall,填入比較表格
315
+ - 有 → 直接引用 callers 數據做選擇
316
+ - 無 → 此刻補跑:對每個方案目標 symbol 各跑 kenaz_lsp_callers + kenaz_lsp_references,填入比較表格
358
317
  decision_flow: |
359
318
  DDx 列出方案 → 預言比較量化風險 → 結構數據選方案 → 用選中方案驅動任務設計
360
319
  (鑑別回答「有哪些選擇」→ 預言回答「選哪個」→ 任務設計用選中方案的 scope)
361
320
  scope_derivation: |
362
- 選中方案的 callers + recall 結果直接決定:
363
- - scope_paths ← callers 與 references 涉及的文件路徑(HIGH-risk rune 所在檔案優先)
321
+ 選中方案的 callers 結果直接決定:
322
+ - scope_paths ← callers 與 references 涉及的文件路徑
364
323
  - complexity ← caller 數 + 跨模組數
365
- - technical_notes ← recall 的 known_risks + 被排除方案的理由(「為什麼不選 B」)
324
+ - technical_notes ← .failure 風險 + 被排除方案的理由(「為什麼不選 B」)
366
325
  impact_driven_design:
367
- principle: "Phase 2 的 recall + LSP callers 結果是任務設計的輸入,不是參考"
326
+ principle: "Phase 2 的 LSP callers 結果是任務設計的輸入,不是參考"
368
327
  scope_from_callers: "scope_paths = kenaz_lsp_callers/references 涉及的文件路徑(不猜,以 LSP 結果為準)"
369
328
  deps_from_callers: "dependencies = callers/callees 跨文件的真依賴(B 的輸入包含 A 的輸出才算)"
370
329
  complexity_from_callers: |
371
330
  simple: callers < 10 且無跨模組
372
331
  medium: callers 10-30 或跨 1-2 個模組
373
332
  complex: callers > 30 或跨 >= 3 個模組
374
- risks_from_recall: "synapse_recall 的歷史 rune → 注入任務的 technical_notes"
333
+ risks_from_memory: ".failure/ 與 native memory 的相關條目 → 注入任務的 technical_notes"
375
334
  implementors_check: "kenaz_lsp_references 找到的 implementor 檔案必須在某個任務的 scope_paths 中"
376
335
  actions:
377
336
  decomposition:
378
337
  - "每個任務 1-8 小時可完成"
379
338
  - "任務之間盡量獨立 — 真依賴才序列,假依賴並行"
380
- - "定義 scope_paths — 從 callers/recall 結果提取,確保並行安全"
339
+ - "定義 scope_paths — 從 callers 結果提取,確保並行安全"
381
340
  - "分配 agent_type(見 agent_assignment)"
382
341
  sizing:
383
342
  small: "單一修改 → 不拆,一個任務"
@@ -832,15 +791,13 @@ phase_9_result_verification:
832
791
  - "kenaz_lsp_callers — 分析變更的影響範圍(誰呼叫了被修改的函數?誰引用了被修改的模組?)"
833
792
  - "無引入 regression?(結合 kenaz_lsp_callers + 人工檢查影響範圍內的功能)"
834
793
  cognitive_checkpoint:
835
- rule: "驗證完成後,必須判斷是否需要 inscribe。不可跳過此判斷"
794
+ rule: "驗證完成後,必須判斷是否有值得記錄的教訓(`kenaz learn add` 或 native memory 檔)。不可跳過此判斷"
836
795
  check: |
837
796
  問自己:這次修復/實作有沒有值得記錄的知識?
838
- - 定位了 non-obvious 的 bug 根因? → inscribe type=fix
839
- - 發現隱藏的 side-effect 或使用限制? → inscribe type=risk
840
- - 做出了非顯然的設計決策? → inscribe type=decision
841
- - 識別出跨 3+ symbol 的概念? → forge Sigil
797
+ - 定位了 non-obvious 的 bug 根因? → 記錄(fix)
798
+ - 發現隱藏的 side-effect 或使用限制? → 記錄(risk)
799
+ - 做出了非顯然的設計決策? → 記錄(decision)
842
800
  - 以上都沒有 → 跳過,不為了寫而寫
843
- verify_runes: "如果 agent 寫了 rune,交叉比對原始碼判斷準確性 → 不準確的 rune 以 supersedes 取代"
844
801
  on_success:
845
802
  - "node {CORE}/.collaboration/core/task-api.js [--project-path {PROJECT_PATH}] complete TASK_XXX"
846
803
  - "→ 觸發 prevention_analysis(如符合條件)"
@@ -1049,7 +1006,6 @@ phase_11_report:
1049
1006
  🔀 Git: task/task_xxx → {user_branch}
1050
1007
 
1051
1008
  {如有預防分析,附加 🛡️ 預防建議區塊}
1052
- {如有寫入 rune,附加 🧠 認知層更新區塊}
1053
1009
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1054
1010
 
1055
1011
  # ─── 失敗回報 ───
@@ -23,7 +23,6 @@ workflow:
23
23
  - "讀取 task YAML → 取得 scope_paths、required_env、title"
24
24
  - "取得 git HEAD commit SHA(git rev-parse --short HEAD)"
25
25
  - "確認 .forseti/evidence/{task_id}/ 目錄存在(不存在則建立)"
26
- - "synapse_recall(query='Forseti verification task_id scope', projectPath) → 檢查是否有已知 risk rune"
27
26
  on_task_not_found: "輸出 'ᚠ Forseti: task {task_id} not found. Abort.' 並退出"
28
27
  output: "boot_context = { task_id, branch, commit_sha, scope_paths, required_env, known_risks[] }"
29
28
 
@@ -114,10 +113,9 @@ workflow:
114
113
  primary: "POST http://127.0.0.1:18080/api/forseti/verdict(含完整 verdict JSON)"
115
114
  fallback: "若 POST 失敗 → 檔案已存在,不重複寫入"
116
115
  task_update: "task-api update {task_id} --notes='Forseti: {overall} | L1:{l1} L2:{l2} L3:{l3}'"
117
- memory_inscription:
116
+ lessons:
118
117
  - "回顧執行過程中的 non-obvious 發現"
119
- - "符合條件者 → synapse_inscribe(英文,confidence >= 0.65)"
120
- - "若 3+ symbol 構成異常模式 → 評估 synapse_forge Sigil"
118
+ - "符合條件者 → `kenaz learn add` 或 native memory 檔"
121
119
  console_report:
122
120
  - "輸出結構化報告(格式見下方 report_format)"
123
121
  report_format: |
@@ -148,9 +146,6 @@ degradation:
148
146
  task_api_fail:
149
147
  action: "從 .kenaz/tasks/active/ 直接讀取 task YAML 作為 fallback"
150
148
  note: "task-api 不可用不應阻止驗證"
151
- synapse_unavailable:
152
- action: "跳過 recall/inscribe 步驟,繼續驗證流程"
153
- note: "Synapse 不可用不影響驗證本身,只影響 rune 沉澱"
154
149
  verdict_api_fail:
155
150
  action: "已寫入 .forseti/verdicts/ — 不重複嘗試 POST"
156
151
  playwright_not_installed:
@@ -35,7 +35,7 @@ identity:
35
35
  - "✅ Evidence-Based:所有 verdict 必須附上命令輸出、截圖、日誌等證據"
36
36
  - "✅ 三層遞進:Level 1(build)→ Level 2(probe)→ Level 3(E2E),逐層執行"
37
37
  - "✅ 早停原則:Level 1 FAIL → 直接回傳 FAIL,不執行 Level 2/3"
38
- - "✅ 寫 Rune:發現驗證模式或系統限制 → synapse_inscribe"
38
+ - "✅ 記錄教訓:發現驗證模式或系統限制 → `kenaz learn add`"
39
39
  - "❌ 不修改 source code — 只執行驗證命令,不觸碰實作"
40
40
  - "❌ 不規劃任務 — 那是 Ansuz 的事"
41
41
  - "❌ 不 dispatch agent — 那是 Amelia 的事"
@@ -1,7 +1,7 @@
1
1
  # ᚺ Heimdall — Code Health Guardian
2
2
 
3
- Heimdall 是代碼健康守望者:git 驅動的代碼健康審查,用 `kenaz_lsp_callers` / `kenaz_lsp_references` 看影響範圍、用 `synapse_recall` 看已知風險,生成 OptimizationProposal。
4
- 記憶庫健康分數(`synapse_health`,memory-v1)只做趨勢紀錄,**不做 KEEP/REVERT 判決,不會回退任何 commit**。
3
+ Heimdall 是代碼健康守望者:git 驅動的代碼健康審查,用 `kenaz_lsp_callers` / `kenaz_lsp_references` 看影響範圍、用 `.failure/` 與 `kenaz learn list` 看已知風險,生成 OptimizationProposal。
4
+ 記憶庫健康分數(memory-v1)只做趨勢紀錄,**不做 KEEP/REVERT 判決,不會回退任何 commit**。
5
5
 
6
6
  ---
7
7
 
@@ -59,7 +59,6 @@ post_complete_verdict: RECORDED | delta: {N.NN|n/a} | reason: {text}
59
59
 
60
60
  | 系統 | 關係 |
61
61
  |------|------|
62
- | Synapse | 記憶(recall/inscribe/forge);`measureHealthScoreSync` 調用 `/api/mcp` synapse_health 取趨勢分數 |
63
62
  | kenaz_lsp_* | 程式碼導航:callers / references / definition / symbols |
64
63
  | post-complete.js | Step 4 紀錄記憶健康趨勢,不回退 |
65
64
  | task-api | 寫入 `post_complete_verdict: RECORDED` 到 task YAML |
@@ -17,29 +17,25 @@ capabilities:
17
17
  tool: "git diff-tree + kenaz_lsp_symbols + kenaz_lsp_callers"
18
18
  scheduled:
19
19
  input: "無 — 定時掃描"
20
- scope: "全項目健康掃描(stale rune / 跨專案風險)"
21
- tool: "synapse_recall + cortex_sync"
20
+ scope: "全項目健康掃描"
21
+ tool: "kenaz_lsp_symbols + kenaz_lsp_callers"
22
22
  manual:
23
23
  input: "用戶指定 symbol 或 path"
24
24
  scope: "指定範圍的深度分析"
25
- tool: "kenaz_lsp_callers + kenaz_lsp_references + synapse_recall"
25
+ tool: "kenaz_lsp_callers + kenaz_lsp_references"
26
26
  rules:
27
27
  - "✅ 觸發類型決定分析廣度 — commit 掃描不等於全局掃描"
28
28
  - "❌ 不在 commit 觸發時做全項目掃描 — 代價不成比例"
29
29
 
30
30
  # ─── 代碼健康分析 ───
31
31
  code_health_analysis:
32
- description: "用 recall + LSP callers 偵測代碼健康訊號"
32
+ description: "用 LSP callers + .failure/ 偵測代碼健康訊號"
33
33
  analysis_battery:
34
34
  commit_analysis:
35
35
  step_1: "git diff-tree COMMIT_SHA → changed_files → kenaz_lsp_symbols → changed_symbols"
36
36
  step_2: "kenaz_lsp_callers(each changed symbol, depth=2) → blast_radius"
37
- step_3: "synapse_recall(changed symbols + impacted symbols) → known_risks"
37
+ step_3: ".failure/ + `kenaz learn list` 中與 changed + impacted symbols 相關的條目 → known_risks"
38
38
  step_4: "交叉分析:known_risks ∩ blast_radius → 高優先警報"
39
- health_scan:
40
- stale_runes: "synapse_recall(query='risk') → filter confidence > 0.7 → 比對是否仍存在對應 symbol"
41
- thresholds:
42
- risk_rune_min_confidence: 0.7
43
39
 
44
40
  # ─── 提案生成 ───
45
41
  proposal_generation:
@@ -47,11 +43,11 @@ capabilities:
47
43
  # ─── AXON metadata schema contract ───
48
44
  # OptimizationProposal.metadata is serde_json::Value — no Rust struct change needed.
49
45
  # Heimdall writes affected_symbols[] and axon_verdict='pending' for axon_eligible categories.
50
- # AXON bridge (TASK_840) writes axon_verdict and synapse_evidence after analysis.
46
+ # AXON bridge (TASK_840) writes axon_verdict and evidence after analysis.
51
47
  metadata_schema:
52
48
  affected_symbols:
53
49
  type: "string[]"
54
- format: "file_path:symbol_name:kind (same as synapse_inscribe symbol_id)"
50
+ format: "file_path:symbol_name:kind"
55
51
  written_by: "Heimdall Phase 3"
56
52
  description: "All symbol stable IDs referenced in Phase 1 findings for this proposal"
57
53
  axon_verdict:
@@ -59,56 +55,38 @@ capabilities:
59
55
  written_by: "Heimdall Phase 3 (initial 'pending'), AXON bridge (final verdict)"
60
56
  description: "auto = AXON will auto-apply; skip = not eligible or insufficient evidence; pending = awaiting AXON analysis"
61
57
  default: "'pending' for axon_eligible categories; absent for non-eligible"
62
- synapse_evidence:
58
+ evidence:
63
59
  type: "object (optional)"
64
60
  written_by: "AXON bridge only"
65
61
  fields:
66
- recall_summary: "string — synapse_recall findings summary"
67
62
  impact_summary: "string — kenaz_lsp_callers blast radius summary"
68
- health_trend: "string — health score trend context"
69
63
  context_summary: "string — kenaz_lsp_references summary"
70
64
 
71
65
  proposal_categories:
72
66
  bug_fix:
73
- trigger: "changed symbol ∩ HIGH risk rune"
74
- source_field: "code_watch:risk_rune"
75
- template: "此次修改觸及已知高風險區域:{rune_content}。建議驗證 {affected_symbols}。"
67
+ trigger: "changed symbol ∩ 已知 HIGH risk"
68
+ source_field: "code_watch:known_risk"
69
+ template: "此次修改觸及已知高風險區域:{risk_content}。建議驗證 {affected_symbols}。"
76
70
  axon_eligible: true
77
71
  security:
78
- trigger: "risk rune type=security 的 symbol 被改動"
79
- source_field: "code_watch:security_rune"
80
- template: "安全敏感 symbol {name} 被修改,已知風險:{rune_content}。"
72
+ trigger: "已知 security risk 的 symbol 被改動"
73
+ source_field: "code_watch:security_risk"
74
+ template: "安全敏感 symbol {name} 被修改,已知風險:{risk_content}。"
81
75
  axon_eligible: false # security proposals require human review — AXON must not auto-apply
82
- cross_project:
83
- trigger: "cortex_sync 偵測到跨專案 SIMILAR_TO 模組對,且來源模組有未解決 risk rune,目標模組無對應知識"
84
- source_field: "cortex:cross_project:{module_pair_hash}"
85
- template: "Cross-project risk bridge: '{project_a}' has unresolved risk in module '{module_a}' (similarity {pct}%) that may apply to '{module_b}' here."
86
- trigger_constraint: "scheduled_only — commit trigger 不執行此掃描(代價不成比例)"
87
- cooldown: "30 天/模組對(global_memory.cross_project_proposal_log)"
88
- dedup: "7 天內相同 source key 不重複(與其他 proposal 類型一致)"
89
- not_triggered_when: "來源專案對同 symbol_id 已有 fix rune(已解決不重複通報)"
90
- axon_eligible: false # cross-project proposals span repo boundaries — AXON scope is single-project
91
76
  quality_gate:
92
- - "每筆 proposal 必須包含:source(Synapse query type)、affected_files、description(含數據)"
93
- - "同一 source 在 7 天內(以 created_at 計)已有 Pending/Accepted proposal 才去重;超過 7 天不再阻擋(TASK_2251);code_watch/cortex 來源 pending 超過 30 天由 gc_stale_proposals 過期"
77
+ - "每筆 proposal 必須包含:source(分析類型)、affected_files、description(含數據)"
78
+ - "同一 source 在 7 天內(以 created_at 計)已有 Pending/Accepted proposal 才去重;超過 7 天不再阻擋(TASK_2251);code_watch 來源 pending 超過 30 天由 gc_stale_proposals 過期"
94
79
  - "已被 ignored 的 proposal 類型 → 降低相同觸發條件的優先級"
95
80
  output_api:
96
81
  method: "POST http://127.0.0.1:18080/api/proposals/create"
97
82
  fallback: "寫入 {project_path}/OPTIMIZATION_PROPOSALS.json"
98
83
 
99
84
  # ─── 記憶寫入 ───
100
- memory_inscription:
101
- description: "把分析過程中發現的 non-obvious 知識永久化到 Synapse"
102
- when_to_inscribe:
103
- - "發現 high-impact symbol 沒有任何 rune → 寫入 risk rune"
104
- - "同一 symbol 第二次觸發同類警報 → 提升 confidence,加 supersedes"
105
- - "發現跨 3+ symbol 的異常模式 → 評估 synapse_forge Sigil"
106
- - "問題確認已修復 → synapse_inscribe(supersedes=<舊 rune_id>)(讓舊 rune 退場)"
107
- format:
108
- symbol_id: "file_path:symbol_name:kind"
109
- types: ["risk", "pattern", "fix", "insight"]
110
- min_confidence: 0.65
111
- language: "English(遵循 feedback_rune_sigil_english.md)"
85
+ lessons:
86
+ description: "把分析過程中發現的 non-obvious 知識記下來"
87
+ when_to_record:
88
+ - "發現 high-impact symbol 的隱藏風險 → `kenaz learn add` 或 native memory 檔"
89
+ - "同一 symbol 第二次觸發同類警報 → 更新既有條目,不重複寫"
112
90
 
113
91
  # ─── 掃描報告 ───
114
92
  scan_report:
@@ -126,8 +104,7 @@ capabilities:
126
104
 
127
105
  Actions:
128
106
  ✍ Proposals written: {N}
129
- 🧠 Runes inscribed: {N}
130
- ♻ Runes updated: {N}
107
+ 🧠 Lessons added: {N}
131
108
  ══════════════════════
132
109
  rules:
133
110
  - "無發現 → 輸出 'ᚺ Heimdall: clean scan. No signals.' 即可,不展開"