kenaz 0.3.10 → 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 (108) hide show
  1. package/core/.claude/skills/code_reviewer/SKILL.md +0 -1
  2. package/core/.claude/skills/code_reviewer/patterns.yaml +0 -8
  3. package/core/.claude/skills/dev_agent/SKILL.md +3 -6
  4. package/core/.claude/skills/heimdall/SKILL.md +5 -6
  5. package/core/.claude/skills/huginn/SKILL.md +11 -38
  6. package/core/.collaboration/core/task-api.js +2 -218
  7. package/core/.collaboration/core/task-api.test.js +1 -138
  8. package/core/.formation/bifrost/launch.js +2 -2
  9. package/core/.formation/bifrost/launch.test.js +2 -2
  10. package/core/.formation/bifrost/shared/ansuz-boot-policy.js +3 -3
  11. package/core/.formation/bifrost/shared/codex-injection.js +3 -17
  12. package/core/.formation/bifrost/shared/consolidate.js +1 -51
  13. package/core/.formation/bifrost/sync.js +4 -5
  14. package/core/.formation/tools/__tests__/subagent-bifrost-guard.test.js +1 -1
  15. package/core/.formation/tools/dispatch.js +2 -2
  16. package/core/.formation/tools/kenaz-stats.js +1 -1
  17. package/core/.formation/tools/mcp-allowlist.test.js +5 -3
  18. package/core/.formation/tools/post-complete/bounded-http.js +1 -40
  19. package/core/.formation/tools/post-complete/bounded-http.test.js +2 -89
  20. package/core/.formation/tools/post-complete/cognitive.js +2 -119
  21. package/core/.formation/tools/post-complete/testing.js +3 -21
  22. package/core/.formation/tools/post-complete.js +2 -428
  23. package/core/.formation/tools/post-complete.test.js +0 -135
  24. package/core/.formation/tools/route-audit.test.js +8 -20
  25. package/core/.rule/INDEX.yaml +3 -3
  26. package/core/.rule/general/ddx-synapse-stack.yaml +11 -11
  27. package/core/.rule/general/specialist-identity-contract.yaml +5 -5
  28. package/core/.rule/general/synapse-code-intelligence.yaml +25 -111
  29. package/core/.rule/general/worktree-isolation.yaml +2 -2
  30. package/core/.rule/pipeline/task-execution.yaml +6 -9
  31. package/core/.specialist/Amelia/README.md +5 -5
  32. package/core/.specialist/Amelia/capability.yaml +10 -18
  33. package/core/.specialist/Amelia/knowledge.yaml +15 -66
  34. package/core/.specialist/Amelia/workflow.yaml +12 -21
  35. package/core/.specialist/Ansuz/capability.yaml +12 -87
  36. package/core/.specialist/Ansuz/concepts/autonomous_agenda.yaml +1 -1
  37. package/core/.specialist/Ansuz/concepts/autonomy_contract.yaml +2 -3
  38. package/core/.specialist/Ansuz/concepts/capability_pointers.yaml +1 -3
  39. package/core/.specialist/Ansuz/concepts/cognitive_fluency_contract.yaml +8 -21
  40. package/core/.specialist/Ansuz/concepts/cognitive_systems_overview.yaml +0 -4
  41. package/core/.specialist/Ansuz/concepts/cognitive_systems_turn_context.yaml +1 -1
  42. package/core/.specialist/Ansuz/concepts/completion_tracking.yaml +0 -1
  43. package/core/.specialist/Ansuz/concepts/deep_knowledge_pointers.yaml +0 -3
  44. package/core/.specialist/Ansuz/concepts/evolvable_guardrails.yaml +3 -3
  45. package/core/.specialist/Ansuz/concepts/fatigue_handoff.yaml +1 -7
  46. package/core/.specialist/Ansuz/concepts/guardrails_immutable.yaml +3 -4
  47. package/core/.specialist/Ansuz/concepts/identity.yaml +1 -2
  48. package/core/.specialist/Ansuz/concepts/phase9_verification.yaml +0 -1
  49. package/core/.specialist/Ansuz/concepts/phase_protocol.yaml +3 -4
  50. package/core/.specialist/Ansuz/core.yaml +3 -6
  51. package/core/.specialist/Ansuz/elixir.yaml +6 -6
  52. package/core/.specialist/Ansuz/exo_cortex_tools.yaml +5 -9
  53. package/core/.specialist/Ansuz/guards/blind_spot_guards.yaml +2 -2
  54. package/core/.specialist/Ansuz/guards/lesson_guards.yaml +6 -19
  55. package/core/.specialist/Ansuz/identity_contract.yaml +4 -4
  56. package/core/.specialist/Ansuz/knowledge.yaml +13 -40
  57. package/core/.specialist/Ansuz/modules/ddx.yaml +2 -2
  58. package/core/.specialist/Ansuz/modules/prophecy.yaml +19 -19
  59. package/core/.specialist/Ansuz/modules/pulse_empty.yaml +6 -17
  60. package/core/.specialist/Ansuz/tools/ansuz-handoff-snapshot.js +3 -102
  61. package/core/.specialist/Ansuz/tools/ansuz-sdk-runner.js +2 -44
  62. package/core/.specialist/Ansuz/workflow.yaml +32 -76
  63. package/core/.specialist/Forseti/core.yaml +2 -7
  64. package/core/.specialist/Forseti/rule.yaml +1 -1
  65. package/core/.specialist/Heimdall/SKILL.md +2 -3
  66. package/core/.specialist/Heimdall/capability.yaml +22 -45
  67. package/core/.specialist/Heimdall/core.yaml +24 -126
  68. package/core/.specialist/Heimdall/principles.yaml +8 -11
  69. package/core/.specialist/Heimdall/rule.yaml +2 -2
  70. package/core/.specialist/Huginn/capability.yaml +31 -52
  71. package/core/.specialist/Huginn/core.yaml +43 -123
  72. package/core/.specialist/Huginn/principles.yaml +5 -5
  73. package/core/.specialist/Huginn/rule.yaml +3 -9
  74. package/core/.specialist/REFLECTION_TO_CONTROL.md +3 -4
  75. package/core/.specialist/shared/guard_schema.json +1 -1
  76. package/core/.specialist/shared/principles.yaml +6 -6
  77. package/core/.specialist/tools/phase-gate.js +10 -230
  78. package/core/.specialist/tools/phase-gate.test.js +2 -2
  79. package/core/.specialist/tools/prompt-files.test.js +1 -1
  80. package/core/.specialist/tools/session.js +2 -89
  81. package/core/.system/memory/README.md +3 -0
  82. package/core/CLAUDE.md +6 -7
  83. package/core/KENAZ_CORE_VERSION +1 -1
  84. package/core/dev/plans/CLI_FIRST_INSTALL_FLOW.md +26 -0
  85. package/core/plugins/kenaz/INVENTORY.md +1 -1
  86. package/core/plugins/kenaz/scripts/kenaz.test.js +2 -1
  87. package/core/plugins/kenaz/scripts/ygg.js +2 -0
  88. package/core/plugins/kenaz/skills/code_reviewer/SKILL.md +0 -1
  89. package/core/plugins/kenaz/skills/code_reviewer/patterns.yaml +0 -8
  90. package/core/plugins/kenaz/skills/dev_agent/SKILL.md +3 -6
  91. package/lib/cli.js +0 -2
  92. package/package.json +6 -6
  93. package/core/.claude/hooks/handoff-snapshot-on-decision.js +0 -188
  94. package/core/.claude/skills/synapse_maintain/SKILL.md +0 -84
  95. package/core/.formation/tools/alaya-gate.js +0 -198
  96. package/core/.formation/tools/post-complete/health.js +0 -464
  97. package/core/.formation/tools/post-complete/task2038.test.js +0 -246
  98. package/core/.formation/tools/post-complete/task2341.test.js +0 -74
  99. package/core/.rule/general/cortex-tools.yaml +0 -181
  100. package/core/.rule/general/memory-language.yaml +0 -67
  101. package/core/.rule/general/memory-source-priority.yaml +0 -207
  102. package/core/.rule/general/synapse-cognitive-fusion.yaml +0 -301
  103. package/core/.specialist/Ansuz/concepts/cognitive_systems_hippocampus.yaml +0 -14
  104. package/core/.specialist/Ansuz/concepts/write_verification.yaml +0 -26
  105. package/core/.specialist/Ansuz/modules/recall.yaml +0 -45
  106. package/core/.specialist/tools/MEMORY_RECALL_TRIGGERS.md +0 -156
  107. package/core/.specialist/tools/context-injection.js +0 -395
  108. package/core/dev/scripts/probe/hippocampus-concurrency.js +0 -247
@@ -43,24 +43,18 @@
43
43
  * |-------|------------------------------------------|-------------------------------------------|----------------------------|-------------------------------------|
44
44
  * | GC | gcStaleWorktrees() | always | try/catch (non-fatal) | orphan worktrees accumulate |
45
45
  * | Guard | Duplicate-suppress guard | recent worktree pass for same taskId (<2m) | try/catch (non-fatal) | double execution |
46
- * | 0 | Hippocampus episode start | taskId && !dryRun | try/catch (non-fatal) | episodes not started |
47
46
  * | 1 | archiveCheck() | !skipArchive | inline logging | none — optional |
48
47
  * | 2 | gitBranchAndMerge() or _gitMergeFromWorktree() | !skipGit && taskId | throws (caught at CLI) | task stuck in active |
49
48
  * | 2.5a | Yggdrasil commit-sync (fire-and-forget) | gitResult.committed && !dryRun | catch (silent) | sync missed silently |
50
- * | 2.6a | Passive rune extraction | gitResult.committed && !dryRun | catch (silent) | runes not extracted |
51
49
  * | 2.7 | Outcome recording + surprise detection | !dryRun | catch (silent) | no outcome metrics recorded |
52
50
  * | 2.8 | Skill matrix update | !dryRun | catch (log warning) | metrics not updated |
53
51
  * | 2.9 | Cognitive attribution | !dryRun && mergeSucceeded | catch (silent) | attribution missed |
54
52
  * | 2.5b | Complete/block task via task-api | merge success or failure | spawnSync; safety net move | task stuck in active |
55
53
  * | 2.5c | Record AC failures (ACCEPTANCE_FAILED) | acFailures.length > 0 | catch (log) | AC failures invisible |
56
54
  * | 2.5d | Safety net: force-move to done | merge succeeded but complete() failed | spawnSync | task stays in active |
57
- * | 2.5e | Passive rune extraction (completed) | result.taskCompleted && !dryRun | catch (silent) | completion runes not tagged |
58
55
  * | 2.6b | Ansuz inbox signal | result.taskCompleted && !dryRun | catch (silent) | Ansuz not notified |
59
56
  * | 2.6c | Relay task_completed event (TASK_1409) | result.taskCompleted && !dryRun && relay | catch (silent) | PM inbox not notified cross-node |
60
57
  * | 3 | Memory agent onTaskCompleted/onError | memoryAgent && taskId | catch (log warning) | memory event not queued |
61
- * | 3b | Provenance runes (synapse_inscribe) | !dryRun && git.merged | catch (silent) | provenance not tagged |
62
- * | 4 | Guardian health check + KEEP/REVERT | taskId (always) | catch (log) | no feedback signal for loop |
63
- * | 6 | Hippocampus episode close | hippocampusEpisodeId set | catch (log warning) | episode not closed properly |
64
58
  *
65
59
  * A.2 gitBranchAndMerge() — serial mode (L2344)
66
60
  *
@@ -333,7 +327,6 @@ try {
333
327
 
334
328
  const CONFIG = {
335
329
  DEFAULT_KEEP: 10,
336
- GUARDIAN_INTERVAL: 10, // Run synapse_health snapshot every N completed tasks
337
330
  };
338
331
 
339
332
  // ?????????????????????????????????????????????????????????????????????????????
@@ -1199,20 +1192,15 @@ function gcStaleWorktrees(currentTaskId, projectPath) {
1199
1192
 
1200
1193
  // ============================================================================
1201
1194
  // Cognitive tool usage tracking (TASK_1105)
1202
- // Records which cognitive tools (synapse_recall, kenaz_lsp_*, ddx, etc.) were
1195
+ // Records which cognitive tools (kenaz_lsp_*, ddx, etc.) were
1203
1196
  // invoked during the task, aggregates into .kenaz/specialist/ansuz_self_model.json
1204
1197
  // under cognitive_competence, and flags negative-trend tools as mirror blind spots.
1205
1198
  // ============================================================================
1206
1199
 
1207
1200
  const COGNITIVE_TOOLS = [
1208
- 'synapse_recall',
1209
1201
  'kenaz_lsp_callers',
1210
1202
  'kenaz_lsp_references',
1211
- 'synapse_inscribe',
1212
- 'synapse_forge',
1213
1203
  'ddx',
1214
- 'hippocampus_episode_start',
1215
- 'cortex_recall',
1216
1204
  ];
1217
1205
 
1218
1206
  function sleepSync(ms) {
@@ -2039,23 +2027,6 @@ setStagingConfig(CONFIG);
2039
2027
  // ─────────────────────────────────────────
2040
2028
 
2041
2029
  const TASK_STATUS_DIRS = ['backlog', 'todo', 'active', 'done', 'blocked'];
2042
- // ?????????????????????????????????????????????????????????????????????????????
2043
- // SECTION: HEALTH_AND_GUARDIAN (extracted to ./post-complete/health.js)
2044
- // ?????????????????????????????????????????????????????????????????????????????
2045
- const {
2046
- resolveMcpProjectPath,
2047
- readLoopConfig,
2048
- measureHealthScoreSync,
2049
- readPreTaskHealthSnapshot,
2050
- saveTaskHealthSnapshot,
2051
- computeHealthVerdict,
2052
- writeVerdictToTask,
2053
- readGuardianCounter,
2054
- writeGuardianCounter,
2055
- triggerGuardianHealthCheck,
2056
- HEALTH_SNAPSHOTS_DIR_NAME,
2057
- } = require('./post-complete/health');
2058
-
2059
2030
  // TASK_1885: deliberately NOT the same knob as loopCfg.revert_threshold (DEFAULT_REVERT_THRESHOLD
2060
2031
  // in health.js). This is a fixed calibration point for the Design Reasoning Attribution valence
2061
2032
  // classifier (feeds skill matrix / EFE learning signals) -- it answers "was this outcome bad
@@ -2076,8 +2047,6 @@ const {
2076
2047
  parseCognitiveCheckpoint,
2077
2048
  detectCognitiveCheckpoint,
2078
2049
  collectThinkingTags,
2079
- triggerPassiveRuneExtraction,
2080
- callHippocampusMcpSync,
2081
2050
  } = require('./post-complete/cognitive');
2082
2051
  initCognitive({ findLogForTask, getPostCompleteChangedFiles, rankTouchedFiles, stripCJK });
2083
2052
 
@@ -2094,7 +2063,7 @@ const {
2094
2063
  updateSkillMatrix,
2095
2064
  accumulateValence,
2096
2065
  } = require('./post-complete/outcome');
2097
- initOutcome({ resolveProjectRoot, resolveTasksBase, loadTaskScopePaths, getPostCompleteChangedFiles, callHippocampusMcpSync });
2066
+ initOutcome({ resolveProjectRoot, resolveTasksBase, loadTaskScopePaths, getPostCompleteChangedFiles });
2098
2067
 
2099
2068
  // ?????????????????????????????????????????????????????????????????????????????
2100
2069
  // SECTION: INTEGRATION_GAPS (extracted to ./post-complete/integration-gaps.js)
@@ -2182,56 +2151,6 @@ function postComplete(opts = {}) {
2182
2151
 
2183
2152
  const result = { archive: null, git: null };
2184
2153
 
2185
- // Step 0: Hippocampus Episode Start (episodic memory — TASK_716)
2186
- // TASK_963: Resolve touched file_paths so Rust can create TOUCHES edges to Symbols
2187
- let hippocampusEpisodeId = null;
2188
- if (taskId && !dryRun) {
2189
- try {
2190
- // Collect file_paths from worktree git diff or task scope_paths
2191
- let filePaths = [];
2192
- const diffRoot = worktreePath || resolveProjectRoot(projectPath);
2193
- if (diffRoot) {
2194
- try {
2195
- const diffCmd = worktreePath
2196
- ? 'git diff --name-only main...HEAD'
2197
- : 'git diff --name-only HEAD~1 HEAD';
2198
- const diffOut = spawnSync('git', diffCmd.split(' ').slice(1), {
2199
- cwd: diffRoot, encoding: 'utf8', timeout: 8000, windowsHide: true,
2200
- });
2201
- if (diffOut.status === 0 && diffOut.stdout) {
2202
- filePaths = diffOut.stdout.trim().split('\n').filter(Boolean);
2203
- }
2204
- } catch { /* git diff failed — fall through to scope_paths */ }
2205
- }
2206
- if (filePaths.length === 0) {
2207
- filePaths = loadTaskScopePaths(taskId, projectPath);
2208
- }
2209
-
2210
- const epArgs = {
2211
- task_id: taskId,
2212
- scope: `post-complete:${taskId}`,
2213
- agent_role: formation || 'unit',
2214
- agent_skill: skill || 'do',
2215
- ...(filePaths.length > 0 ? { file_paths: filePaths } : {}),
2216
- // TASK_2025: hippocampus_episode_start is now a write tool that requires
2217
- // an explicit projectPath (mcp.rs resolve_project_for_write) -- it no
2218
- // longer silently falls back to the Dashboard's currently-selected
2219
- // project. projectPath is only set here when --project-path was passed
2220
- // (sub-project dispatch); core-project dispatches never pass it, so
2221
- // fall back to ROOT (this script's own core project root) rather than
2222
- // omitting the param and hard-failing every core-project run.
2223
- projectPath: projectPath || ROOT,
2224
- };
2225
- const epResult = callHippocampusMcpSync('hippocampus_episode_start', epArgs);
2226
- if (epResult && epResult.episode_id) {
2227
- hippocampusEpisodeId = epResult.episode_id;
2228
- console.log(` 🧠 Hippocampus: episode ${hippocampusEpisodeId} started (${filePaths.length} file_paths)`);
2229
- }
2230
- } catch (e) {
2231
- console.log(` ⚠️ Hippocampus episode start skipped: ${e.message}`);
2232
- }
2233
- }
2234
-
2235
2154
  // Step 1: Archive
2236
2155
  if (!skipArchive) {
2237
2156
  console.log('── Archive Check ──');
@@ -2356,17 +2275,6 @@ function postComplete(opts = {}) {
2356
2275
  } catch { /* commit-sync not available — graceful degradation */ }
2357
2276
  }
2358
2277
 
2359
- // Step 2.6: Passive Rune Extraction(fire-and-forget)
2360
- if (gitResult.committed && !dryRun) {
2361
- try {
2362
- const commitMsg = message || `[${taskId}] task completed`;
2363
- const projectRoot = resolveProjectRoot(projectPath);
2364
- if (projectRoot) {
2365
- triggerPassiveRuneExtraction(commitMsg, projectRoot, projectPath);
2366
- }
2367
- } catch { /* rune extraction failed — never block post-complete */ }
2368
- }
2369
-
2370
2278
  // Step 2.7: Outcome Recording + Surprise Detection(fire-and-forget)
2371
2279
  if (!dryRun) {
2372
2280
  try {
@@ -2694,74 +2602,6 @@ function postComplete(opts = {}) {
2694
2602
  }
2695
2603
  }
2696
2604
 
2697
- // Step 2.5: Passive Rune Extraction — task completion signal (TASK_747)
2698
- if (result.taskCompleted && taskId && !dryRun) {
2699
- try {
2700
- const runeScope = loadTaskScopePaths(taskId, projectPath);
2701
- if (runeScope.length > 0) {
2702
- // Read task title and requirements via task-api get
2703
- let runeContent = `Task ${taskId}: completed.`;
2704
- try {
2705
- const getArgs = projectPath ? ['--project-path', projectPath] : [];
2706
- getArgs.push('get', taskId);
2707
- const getResult = spawnSync('node', [COLLABORATION_API, ...getArgs], {
2708
- encoding: 'utf8', timeout: 10000, cwd: ROOT, windowsHide: true,
2709
- });
2710
- if (getResult.status === 0 && getResult.stdout) {
2711
- const taskData = JSON.parse(getResult.stdout.trim());
2712
- const rawTitle = taskData.title || '';
2713
- const reqs = Array.isArray(taskData.requirements) ? taskData.requirements : [];
2714
- // MEM-LANG-001: strip CJK so FTS5 unicode61 tokenizer can index the rune
2715
- const title = stripCJK(rawTitle);
2716
- const reqSummary = stripCJK(reqs.slice(0, 3).join('; '));
2717
- // Fallback: if English portion of title is too short, use task ID as title
2718
- const effectiveTitle = title.length >= 10 ? title : `${taskId} task`;
2719
- runeContent = `Task ${taskId}: ${effectiveTitle}. Requirements: ${reqSummary}`;
2720
- if (runeContent.length > 300) runeContent = runeContent.slice(0, 297) + '...';
2721
- }
2722
- } catch { /* skip — use fallback content */ }
2723
-
2724
- // Fire-and-forget synapse_inscribe for each scope path
2725
- const http = require('http');
2726
- let fired = 0;
2727
- for (const scopePath of runeScope) {
2728
- try {
2729
- const inscribeArgs = {
2730
- symbol_id: scopePath,
2731
- type: 'summary',
2732
- content: runeContent,
2733
- confidence: 0.65,
2734
- linked_task: taskId,
2735
- // TASK_2025: write tool -- must not omit projectPath (see epArgs comment above).
2736
- projectPath: projectPath || ROOT,
2737
- };
2738
- const payload = JSON.stringify({
2739
- jsonrpc: '2.0',
2740
- id: `task-rune-${taskId}-${fired}`,
2741
- method: 'tools/call',
2742
- params: { name: 'synapse_inscribe', arguments: inscribeArgs },
2743
- });
2744
- const req = http.request({
2745
- hostname: '127.0.0.1', port: 18080, path: '/api/mcp',
2746
- method: 'POST',
2747
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
2748
- timeout: 10000,
2749
- }, (res) => {
2750
- res.on('data', () => {});
2751
- res.on('end', () => {});
2752
- });
2753
- req.on('error', (err) => { console.warn(` ⚠️ synapse_inscribe HTTP error: ${err.message}`); });
2754
- req.on('timeout', () => { req.destroy(); });
2755
- req.write(payload);
2756
- req.end();
2757
- fired++;
2758
- } catch { /* skip this scope path */ }
2759
- }
2760
- console.log(` 🧠 Rune extraction: ${fired} scope symbols for ${taskId}`);
2761
- }
2762
- } catch { /* skip — never blocks post-complete */ }
2763
- }
2764
-
2765
2605
  // Step 2.55 (cognitive tool usage -> cognitive_competence) was removed in TASK_2348: its only
2766
2606
  // data source, turn_feedback.jsonl, was written by the retired reflex hook.
2767
2607
 
@@ -3077,138 +2917,6 @@ function postComplete(opts = {}) {
3077
2917
  }
3078
2918
  }
3079
2919
 
3080
- // Step 3b: Provenance rune — record which task + role modified scope paths (Cognee-inspired)
3081
- if (taskId && !dryRun && result.git && result.git.merged) {
3082
- try {
3083
- const scopePaths = loadTaskScopePaths(taskId, projectPath);
3084
- const role = result.taskRole || 'unknown';
3085
- if (scopePaths.length > 0 && scopePaths.length <= 5) {
3086
- const http = require('http');
3087
- for (const scopePath of scopePaths) {
3088
- try {
3089
- const payload = JSON.stringify({
3090
- jsonrpc: '2.0', id: `provenance-${taskId}`,
3091
- method: 'tools/call',
3092
- params: { name: 'synapse_inscribe', arguments: {
3093
- symbol_id: scopePath,
3094
- type: 'summary',
3095
- content: `Modified by ${taskId} (${role}). Scope: ${scopePaths.join(', ')}`,
3096
- confidence: 0.6,
3097
- author: `agent:${role}`,
3098
- linked_task: taskId,
3099
- // TASK_2025: write tool -- must not omit projectPath (see epArgs comment above).
3100
- projectPath: projectPath || ROOT,
3101
- }},
3102
- });
3103
- const req = http.request({
3104
- hostname: '127.0.0.1', port: 18080, path: '/api/mcp',
3105
- method: 'POST',
3106
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
3107
- timeout: 5000,
3108
- }, (res) => { res.on('data', () => {}); res.on('end', () => {}); });
3109
- req.on('error', () => {});
3110
- req.on('timeout', () => { req.destroy(); });
3111
- req.write(payload);
3112
- req.end();
3113
- } catch { /* skip */ }
3114
- }
3115
- console.log(` 📝 Provenance runes: ${scopePaths.length} scope paths tagged with ${taskId}/${role}`);
3116
- }
3117
- } catch { /* never blocks */ }
3118
- }
3119
-
3120
- // Step 4: Guardian Health Check + KEEP/REVERT delta (TASK_706)
3121
- if (taskId) {
3122
- const counter = readGuardianCounter();
3123
- counter.completed_since_last_health = (counter.completed_since_last_health || 0) + 1;
3124
-
3125
- const loopCfg = readLoopConfig(projectPath);
3126
- const regressionSkipVerdictReason = result.git?.regression?.skipVerdictReason || '';
3127
- const shouldSkipHealthVerdict = !!result.git?.regression?.skipVerdict;
3128
-
3129
- // TASK_2341: synapse_health now scores MEMORY (rune/episode/conflict backlog), not code,
3130
- // so it can never revert, block or penalise a task. Both modes only RECORD the trend.
3131
- if (loopCfg.mode === 'auto' && shouldSkipHealthVerdict) {
3132
- console.log('── Guardian Memory-Health Trend (loop auto mode) ──');
3133
- console.log(` ⚠️ Guardian: health snapshot skipped — ${regressionSkipVerdictReason}`);
3134
- result.healthVerdictSkipped = { reason: regressionSkipVerdictReason };
3135
- counter.completed_since_last_health = 0;
3136
- counter.last_health_check = new Date().toISOString();
3137
- writeGuardianCounter(counter);
3138
- } else {
3139
- const preSn = readPreTaskHealthSnapshot(taskId, projectPath);
3140
- if (preSn && typeof preSn.score === 'number') {
3141
- console.log(`── Guardian Memory-Health Trend (${loopCfg.mode} mode — record only, never reverts) ──`);
3142
- const postHealth = measureHealthScoreSync(projectPath);
3143
- if (postHealth) {
3144
- const afterPayload = saveTaskHealthSnapshot(taskId, 'after', postHealth, projectPath);
3145
- const vr = computeHealthVerdict(preSn, postHealth, loopCfg.revert_threshold, {
3146
- trendDeltaFromPrevious: afterPayload ? afterPayload.delta_from_previous : null,
3147
- });
3148
- console.log(` 🛡️ Memory health: ${postHealth.score}/100 (${vr.reason})`);
3149
- if (typeof vr.trendDeltaFromPrevious === 'number') {
3150
- console.log(` 📈 Trend (delta_from_previous): ${vr.trendDeltaFromPrevious >= 0 ? '+' : ''}${vr.trendDeltaFromPrevious}`);
3151
- }
3152
- writeVerdictToTask(taskId, vr.verdict, vr.delta, vr.reason, projectPath);
3153
- result.healthVerdict = { verdict: vr.verdict, delta: vr.delta, reason: vr.reason };
3154
- // Auto-acted_on: mark runes recalled during this task as acted_on (closes the
3155
- // recall->acted_on->confidence_boost loop). Route still exists (synapse.rs).
3156
- if (loopCfg.mode === 'auto') {
3157
- try {
3158
- const actedOnPayload = JSON.stringify({ taskId });
3159
- const actReq = require('http').request({
3160
- hostname: '127.0.0.1',
3161
- port: 18080,
3162
- path: '/api/synapse/runes/mark-acted-on-by-task',
3163
- method: 'POST',
3164
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(actedOnPayload) },
3165
- timeout: 3000,
3166
- }, (res) => {
3167
- let body = '';
3168
- res.on('data', c => body += c);
3169
- res.on('end', () => {
3170
- try {
3171
- const r = JSON.parse(body);
3172
- if (r.marked > 0) console.log(` 🔗 Acted-on: ${r.marked} runes marked for ${taskId}`);
3173
- } catch { /* parse error — silent */ }
3174
- });
3175
- });
3176
- actReq.on('error', () => { /* Dashboard offline — silent skip */ });
3177
- actReq.write(actedOnPayload);
3178
- actReq.end();
3179
- } catch { /* never blocks post-complete */ }
3180
- }
3181
- counter.completed_since_last_health = 0;
3182
- counter.last_health_check = new Date().toISOString();
3183
- } else {
3184
- console.log(` 🛡️ Guardian: Dashboard not running — after-snapshot skipped for ${taskId}`);
3185
- }
3186
- } else if (loopCfg.mode === 'auto') {
3187
- const postHealth = measureHealthScoreSync(projectPath);
3188
- if (postHealth) {
3189
- saveTaskHealthSnapshot(taskId, 'before', postHealth, projectPath);
3190
- console.log(` 🛡️ Guardian: memory health=${postHealth.score}/100 (${postHealth.grade}) — saved as baseline (no pre-task snapshot found)`);
3191
- } else {
3192
- console.log(' 🛡️ Guardian: Dashboard not running — health snapshot skipped');
3193
- }
3194
- counter.completed_since_last_health = 0;
3195
- counter.last_health_check = new Date().toISOString();
3196
- } else if (counter.completed_since_last_health >= CONFIG.GUARDIAN_INTERVAL) {
3197
- console.log(`── Guardian Health Check (every ${CONFIG.GUARDIAN_INTERVAL} tasks) ──`);
3198
- triggerGuardianHealthCheck(projectPath);
3199
- counter.completed_since_last_health = 0;
3200
- counter.last_health_check = new Date().toISOString();
3201
- } else {
3202
- const remaining = CONFIG.GUARDIAN_INTERVAL - counter.completed_since_last_health;
3203
- console.log(` 🛡️ Guardian: ${remaining} tasks until next health snapshot (no pre-dispatch snapshot for ${taskId})`);
3204
- }
3205
- }
3206
-
3207
- if (!shouldSkipHealthVerdict || loopCfg.mode !== 'auto') {
3208
- writeGuardianCounter(counter);
3209
- }
3210
- }
3211
-
3212
2920
  // Step 4.6: Guard Failure Detector (TASK_1653)
3213
2921
  // Runs shadow-mode failure detectors for active lesson guards that carry failure_signature.
3214
2922
  // Reads guard snapshots directly from lesson_guards.yaml (not the task chart) because
@@ -3376,14 +3084,6 @@ function postComplete(opts = {}) {
3376
3084
  console.log(` 👉 On branch: ${result.git.userBranch} — merge to main when ready`);
3377
3085
  }
3378
3086
  }
3379
- // Health verdict summary (TASK_706)
3380
- if (result.healthVerdict) {
3381
- const hv = result.healthVerdict;
3382
- console.log(` Memory Health: ${hv.verdict} (trend=${typeof hv.delta === 'number' ? hv.delta.toFixed(1) : 'n/a'}) — ${hv.reason}`);
3383
- } else if (result.healthVerdictSkipped) {
3384
- console.log(` Health Verdict: SKIPPED ⚠️ — ${result.healthVerdictSkipped.reason}`);
3385
- }
3386
-
3387
3087
  // Cognitive checkpoint summary
3388
3088
  if (result.cognitive) {
3389
3089
  const cog = result.cognitive;
@@ -3398,84 +3098,6 @@ function postComplete(opts = {}) {
3398
3098
  }
3399
3099
  console.log('');
3400
3100
 
3401
- // Step 8a: Hippocampus Episode Append — record task outcome (TASK_969)
3402
- if (hippocampusEpisodeId && !dryRun) {
3403
- try {
3404
- const contentParts = [`Task ${taskId} outcome:`];
3405
-
3406
- // Changed files
3407
- const changedFileList = result.git?.diffStat || [];
3408
- if (changedFileList.length > 0) {
3409
- contentParts.push(`Changed files: ${changedFileList.join(', ')}`);
3410
- } else if (result.git?.committed) {
3411
- contentParts.push('Changes committed (file list unavailable).');
3412
- } else if (result.git?.noChanges) {
3413
- contentParts.push('No file changes detected.');
3414
- }
3415
-
3416
- // Health delta (regression test results)
3417
- if (result.git?.regression?.skipVerdict) {
3418
- contentParts.push(`Health verdict skipped: ${result.git.regression.skipVerdictReason}.`);
3419
- } else if (result.git?.regression && !result.git.regression.skipped) {
3420
- contentParts.push(result.git.regression.passed
3421
- ? 'Health: tests passed.'
3422
- : `Health: tests FAILED.${result.git.regressionFixTask ? ` Fix task: ${result.git.regressionFixTask}` : ''}`);
3423
- }
3424
-
3425
- // Fix loop status
3426
- if (result.git?.acceptanceFixTasks && result.git.acceptanceFixTasks.length > 0) {
3427
- contentParts.push(`Fix loop triggered: ${result.git.acceptanceFixTasks.join(', ')}`);
3428
- }
3429
-
3430
- // Git merge status
3431
- if (isVerifiedMergeSuccess(result.git)) {
3432
- contentParts.push(`Merged to ${result.git.userBranch}.`);
3433
- }
3434
-
3435
- const appendArgs = {
3436
- episode_id: hippocampusEpisodeId,
3437
- phase: 'outcome',
3438
- content: contentParts.join(' '),
3439
- confidence: 0.8,
3440
- outcome: isVerifiedMergeSuccess(result.git) ? 'success' : 'partial',
3441
- // TASK_2025: write tool -- must not omit projectPath (see epArgs comment above).
3442
- projectPath: projectPath || ROOT,
3443
- };
3444
- callHippocampusMcpSync('hippocampus_episode_append', appendArgs);
3445
- console.log(` 🧠 Hippocampus: episode ${hippocampusEpisodeId} appended outcome entry`);
3446
- } catch (e) {
3447
- console.log(` ⚠️ Hippocampus episode append skipped: ${e.message}`);
3448
- }
3449
- }
3450
-
3451
- // Step 8b: Hippocampus Episode Close (episodic memory — TASK_716)
3452
- if (hippocampusEpisodeId && !dryRun) {
3453
- try {
3454
- const outcome = isVerifiedMergeSuccess(result.git)
3455
- ? 'success'
3456
- : (result.git?.noChanges || result.git?.memoryOnly ? 'success' : 'partial');
3457
- const summaryParts = [`Task ${taskId} post-complete finished.`];
3458
- if (isVerifiedMergeSuccess(result.git)) summaryParts.push(`Git merged to ${result.git.userBranch}.`);
3459
- if (result.taskCompleted) summaryParts.push('Task marked done.');
3460
- if (result.git?.regression?.skipVerdict) {
3461
- summaryParts.push('Health verdict skipped due to cargo lock contention.');
3462
- } else if (result.git?.regression && !result.git.regression.skipped) {
3463
- summaryParts.push(result.git.regression.passed ? 'Tests passed.' : 'Tests failed — fix task created.');
3464
- }
3465
- const closeArgs = {
3466
- episode_id: hippocampusEpisodeId,
3467
- outcome,
3468
- summary: summaryParts.join(' '),
3469
- // TASK_2025: write tool -- must not omit projectPath (see epArgs comment above).
3470
- projectPath: projectPath || ROOT,
3471
- };
3472
- callHippocampusMcpSync('hippocampus_episode_close', closeArgs);
3473
- console.log(` 🧠 Hippocampus: episode ${hippocampusEpisodeId} closed (${outcome})`);
3474
- } catch (e) {
3475
- console.log(` ⚠️ Hippocampus episode close skipped: ${e.message}`);
3476
- }
3477
- }
3478
-
3479
3101
  // TASK_1593: Record outcome in dedup guard so a failed pass does not
3480
3102
  // suppress the next legitimate retry (Guard 1 worktree->null check).
3481
3103
  if (taskId && !dryRun) {
@@ -3593,58 +3215,11 @@ Examples:
3593
3215
  // ?????????????????????????????????????????????????????????????????????????????
3594
3216
  // SECTION: EXPORTS
3595
3217
  // ?????????????????????????????????????????????????????????????????????????????
3596
- // ─────────────────────────────────────────
3597
- // Sub-agent post-complete (lightweight equivalent for Agent tool dispatch)
3598
- // ─────────────────────────────────────────
3599
-
3600
- /**
3601
- * Lightweight post-complete for tasks dispatched via Agent tool (isolation=worktree).
3602
- * The Agent tool handles worktree git isolation automatically, so git merge is skipped.
3603
- * Health scoring is deferred to the next Heimdall cycle (dashboard must be running
3604
- * for measureHealthScoreSync to work; safe to call from Ansuz interactive session).
3605
- *
3606
- * Call this after the Agent tool sub-agent returns to record the health delta.
3607
- *
3608
- * @param {string} taskId - Task ID (TASK_XXX)
3609
- * @param {string|null} projectPath - Sub-project path (null = core)
3610
- * @returns {Promise<{ healthDelta: number|null, grade: string|null }>}
3611
- */
3612
- async function subagentPostComplete(taskId, projectPath) {
3613
- const result = { healthDelta: null, grade: null };
3614
-
3615
- try {
3616
- // Record post-task health snapshot (for audit trail + Heimdall comparison)
3617
- const postHealth = measureHealthScoreSync(projectPath);
3618
- if (postHealth && taskId) {
3619
- saveTaskHealthSnapshot(taskId, 'after', postHealth, projectPath);
3620
- const preSn = readPreTaskHealthSnapshot(taskId, projectPath);
3621
- if (preSn?.score != null) {
3622
- // TASK_2341: null when the baseline is graph-era (different score_version)
3623
- result.healthDelta = computeHealthVerdict(preSn, postHealth).delta;
3624
- result.grade = postHealth.grade;
3625
- console.log(
3626
- ` 🛡️ Sub-agent Guardian: health=${postHealth.score}/100 (${postHealth.grade})` +
3627
- ` trend=${typeof result.healthDelta === 'number' ? (result.healthDelta >= 0 ? '+' : '') + result.healthDelta : 'n/a'}`
3628
- );
3629
- } else {
3630
- // Save as baseline for future comparison
3631
- saveTaskHealthSnapshot(taskId, 'before', postHealth, projectPath);
3632
- result.grade = postHealth.grade;
3633
- console.log(` 🛡️ Sub-agent Guardian: health=${postHealth.score}/100 (${postHealth.grade}) — saved as baseline`);
3634
- }
3635
- }
3636
- } catch { /* never block Ansuz on health check failure */ }
3637
-
3638
- return result;
3639
- }
3640
-
3641
3218
  module.exports = {
3642
3219
  classifyMergeFailureReason,
3643
3220
  postComplete,
3644
3221
  archiveCheck,
3645
3222
  gitBranchAndMerge,
3646
- measureHealthScoreSync,
3647
- saveTaskHealthSnapshot,
3648
3223
  classifySkillKey,
3649
3224
  isDaemonManagedPath,
3650
3225
  listDaemonManagedPaths,
@@ -3653,7 +3228,6 @@ module.exports = {
3653
3228
  checkAndUpdatePostCompleteGuard,
3654
3229
  updatePostCompleteGuardOutcome,
3655
3230
  writeInboxEntryIdempotent,
3656
- subagentPostComplete,
3657
3231
  readLatestHandoff, // TASK_1828: handoffs/-first resolver for relay summaries
3658
3232
  reclaimWorktreeDir, // TASK_2028: unlock + git-level reclaim for stale/interrupted worktrees
3659
3233
  appendPhase9OutcomeLog, // TASK_2078: exported for agent_result/transcript precedence unit testing