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
@@ -12,11 +12,6 @@ const {
12
12
  checkAndUpdatePostCompleteGuard,
13
13
  writeInboxEntryIdempotent,
14
14
  } = require('./post-complete');
15
- const {
16
- saveTaskHealthSnapshot,
17
- readPreTaskHealthSnapshot,
18
- resolveMcpProjectPath,
19
- } = require('./post-complete/health');
20
15
  const {
21
16
  isAutoWritePath,
22
17
  filterOutcomeFilesForPrediction,
@@ -770,136 +765,6 @@ test('checkAndUpdatePostCompleteGuard: worktree→null still fires (TASK_1430)',
770
765
  }
771
766
  });
772
767
 
773
- // ── Health snapshot enrichment tests (TASK_1431) ─────────────────────────────
774
-
775
- test('saveTaskHealthSnapshot: delta_from_previous computed against most recent prior _after snapshot', () => {
776
- const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-health-delta-'));
777
- try {
778
- const snapshotsDir = path.join(tmpRoot, '.kenaz', 'health_snapshots');
779
- fs.mkdirSync(snapshotsDir, { recursive: true });
780
-
781
- // Write a prior _after snapshot with a slightly older timestamp
782
- fs.writeFileSync(
783
- path.join(snapshotsDir, 'TASK_100_after.json'),
784
- JSON.stringify({
785
- score: 45.0, grade: 'F (Critical)', score_version: 'memory-v1',
786
- timestamp: new Date(Date.now() - 10000).toISOString(), sub_metrics: {},
787
- }, null, 2)
788
- );
789
-
790
- saveTaskHealthSnapshot('TASK_200', 'after', { score: 47.0, grade: 'F (Critical)', sub_metrics: { degree_entropy: { score: 60 } } }, tmpRoot);
791
-
792
- const saved = JSON.parse(fs.readFileSync(path.join(snapshotsDir, 'TASK_200_after.json'), 'utf8'));
793
- assert.equal(saved.score, 47.0);
794
- assert.equal(saved.delta_from_previous, 2.0, 'delta should be 47 - 45 = 2.0');
795
- assert.equal(saved.trend_5tasks, null, 'trend_5tasks is null when <5 priors');
796
- assert.ok(saved.sub_metrics && typeof saved.sub_metrics === 'object', 'sub_metrics should be present');
797
- assert.ok(saved.timestamp, 'timestamp must be present');
798
- } finally {
799
- fs.rmSync(tmpRoot, { recursive: true, force: true });
800
- }
801
- });
802
-
803
- test('saveTaskHealthSnapshot: trend_5tasks=null when fewer than 5 prior _after snapshots', () => {
804
- const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-health-trend4-'));
805
- try {
806
- const snapshotsDir = path.join(tmpRoot, '.kenaz', 'health_snapshots');
807
- fs.mkdirSync(snapshotsDir, { recursive: true });
808
-
809
- // Write exactly 4 prior _after snapshots (one short of 5)
810
- for (let i = 0; i < 4; i++) {
811
- fs.writeFileSync(
812
- path.join(snapshotsDir, `TASK_${100 + i}_after.json`),
813
- JSON.stringify({
814
- score: 45.0 + i, grade: 'F (Critical)', score_version: 'memory-v1',
815
- timestamp: new Date(Date.now() - (4 - i) * 10000).toISOString(), sub_metrics: {},
816
- }, null, 2)
817
- );
818
- }
819
-
820
- saveTaskHealthSnapshot('TASK_200', 'after', { score: 50.0, grade: 'D (Poor)', sub_metrics: {} }, tmpRoot);
821
-
822
- const saved = JSON.parse(fs.readFileSync(path.join(snapshotsDir, 'TASK_200_after.json'), 'utf8'));
823
- assert.equal(saved.trend_5tasks, null, 'trend_5tasks must be null when only 4 priors exist');
824
- assert.ok(typeof saved.delta_from_previous === 'number', 'delta_from_previous should be a number when 1+ prior exists');
825
- } finally {
826
- fs.rmSync(tmpRoot, { recursive: true, force: true });
827
- }
828
- });
829
-
830
- test('saveTaskHealthSnapshot: trend_5tasks computed when exactly 5 prior snapshots exist', () => {
831
- const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-health-trend5-'));
832
- try {
833
- const snapshotsDir = path.join(tmpRoot, '.kenaz', 'health_snapshots');
834
- fs.mkdirSync(snapshotsDir, { recursive: true });
835
-
836
- // 5 prior snapshots: scores 40,41,42,43,44 (newest=44 at i=4, oldest=40 at i=0)
837
- for (let i = 0; i < 5; i++) {
838
- fs.writeFileSync(
839
- path.join(snapshotsDir, `TASK_${100 + i}_after.json`),
840
- JSON.stringify({
841
- score: 40.0 + i, grade: 'F (Critical)', score_version: 'memory-v1',
842
- timestamp: new Date(Date.now() - (5 - i) * 10000).toISOString(), sub_metrics: {},
843
- }, null, 2)
844
- );
845
- }
846
-
847
- // current score=50; priors newest-first=[44,43,42,41,40]; 5th prior = 40
848
- // trend_5tasks = 50 - 40 = 10
849
- saveTaskHealthSnapshot('TASK_200', 'after', { score: 50.0, grade: 'D (Poor)', sub_metrics: {} }, tmpRoot);
850
-
851
- const saved = JSON.parse(fs.readFileSync(path.join(snapshotsDir, 'TASK_200_after.json'), 'utf8'));
852
- assert.equal(saved.trend_5tasks, 10.0, 'trend_5tasks = currentScore - score5TasksAgo = 50 - 40');
853
- assert.equal(saved.delta_from_previous, 6.0, 'delta_from_previous = 50 - 44 (most recent prior)');
854
- } finally {
855
- fs.rmSync(tmpRoot, { recursive: true, force: true });
856
- }
857
- });
858
-
859
- test('saveTaskHealthSnapshot: sub_metrics object passes through from health response', () => {
860
- const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-health-subm-'));
861
- try {
862
- const snapshotsDir = path.join(tmpRoot, '.kenaz', 'health_snapshots');
863
- fs.mkdirSync(snapshotsDir, { recursive: true });
864
-
865
- const subMetrics = {
866
- degree_entropy: { score: 60.5, weight: 0.25, excluded_count: 3 },
867
- community_balance: { score: 45.0, weight: 0.20 },
868
- };
869
- saveTaskHealthSnapshot('TASK_300', 'after', { score: 52.0, grade: 'D (Poor)', sub_metrics: subMetrics }, tmpRoot);
870
-
871
- const saved = JSON.parse(fs.readFileSync(path.join(snapshotsDir, 'TASK_300_after.json'), 'utf8'));
872
- assert.deepEqual(saved.sub_metrics, subMetrics, 'sub_metrics should be stored unchanged');
873
- } finally {
874
- fs.rmSync(tmpRoot, { recursive: true, force: true });
875
- }
876
- });
877
-
878
- test('readPreTaskHealthSnapshot: old snapshot without enrichment fields loads without error', () => {
879
- const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'pc-health-compat-'));
880
- try {
881
- const snapshotsDir = path.join(tmpRoot, '.kenaz', 'health_snapshots');
882
- fs.mkdirSync(snapshotsDir, { recursive: true });
883
-
884
- // Old-format snapshot: only score, grade, timestamp (no sub_metrics/delta/trend)
885
- fs.writeFileSync(
886
- path.join(snapshotsDir, 'TASK_OLD_before.json'),
887
- JSON.stringify({ score: 48.0, grade: 'F (Critical)', timestamp: new Date().toISOString() }, null, 2)
888
- );
889
-
890
- const sn = readPreTaskHealthSnapshot('TASK_OLD', tmpRoot);
891
- assert.ok(sn, 'old snapshot should load without error');
892
- assert.equal(sn.score, 48.0, 'score should be readable');
893
- // New fields are absent in old snapshots — undefined is fine, not an error
894
- assert.equal(sn.delta_from_previous, undefined, 'missing delta_from_previous is undefined not error');
895
- assert.equal(sn.sub_metrics, undefined, 'missing sub_metrics is undefined not error');
896
- } finally {
897
- fs.rmSync(tmpRoot, { recursive: true, force: true });
898
- }
899
- });
900
-
901
- // ---- TASK_1674: untracked-path collision tests --------------------------------
902
-
903
768
  test('_neutralizeUntrackedCollisions: TASK_1674 -- untracked file collides with task-branch-added path, merge proceeds and WIP preserved', () => {
904
769
  const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "pc-t1674-"));
905
770
  try {
@@ -167,6 +167,10 @@ function buildSubagentPrompt(skillDef, taskInput, taskData, options = {}) {
167
167
  lines.push('');
168
168
  appendRustCheckReminder(lines, taskData, options, isIsolated);
169
169
 
170
+ // TASK_2409: lessons the user's Ansuz learned for this project (bounded; nothing is added when there are none).
171
+ const learned = projectLessons(taskData, options);
172
+ if (learned.facts.length) lines.push('## Project lessons', ...learned.facts, '');
173
+
170
174
  lines.push('## Verify and report');
171
175
  // Ponytail A/B 2026-10-10: the arm without this line broke an existing fixture test it never ran.
172
176
  lines.push('Before editing, grep the callers, tests and fixtures your change reaches; fix what it breaks. Least code that completes the card: reuse existing helpers, no unrequested abstraction, option or duplicate implementation.');
@@ -179,12 +183,22 @@ function buildSubagentPrompt(skillDef, taskInput, taskData, options = {}) {
179
183
  if (onBlocked && onBlocked.run) {
180
184
  lines.push(`Blocked: \`${buildTaskApiRewriter(options)(onBlocked.run).replace(/\{task_id\}/g, taskId)}\` and explain.`);
181
185
  }
186
+ for (const c of learned.checks) lines.push(`Project check: ${c.slice(2)}`);
182
187
  lines.push('Final report <= 20 lines: files touched, tests run, commit sha, unverified items.');
183
188
  lines.push('');
184
189
  appendDispatchOverrides(lines, taskData);
185
190
  return lines.join('\n');
186
191
  }
187
192
 
193
+ function projectLessons(taskData, options) {
194
+ const none = { facts: [], checks: [] };
195
+ if (!options.projectPath) return none;
196
+ try {
197
+ const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
198
+ return require('../../.specialist/tools/lessons').dispatchLessons(options.projectPath, (parsed && parsed.scope_paths) || []);
199
+ } catch { return none; }
200
+ }
201
+
188
202
  function extractTaskId(taskData) {
189
203
  try {
190
204
  const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
@@ -1,21 +1,15 @@
1
1
  'use strict';
2
- // TASK_2341: every /api/synapse/* and /api/cortex/* path called from node code (.formation, .claude/hooks,
3
- // .specialist, .collaboration) must exist in the Rust router on this branch. Source of truth: synapse.rs router().
2
+ // TASK_2411: the memory system (synapse/hippocampus/cortex recall) and its /api/synapse router are gone, so no node
3
+ // code (.formation, .claude/hooks, .specialist, .collaboration) may call /api/synapse/* or /api/cortex/*.
4
4
  const { test } = require('node:test');
5
5
  const assert = require('node:assert/strict');
6
6
  const fs = require('fs');
7
7
  const path = require('path');
8
8
 
9
9
  const ROOT = path.resolve(__dirname, '..', '..');
10
- const SYNAPSE_RS = path.join(ROOT, 'dashboard', 'src-tauri', 'src', 'infrastructure', 'http_server', 'routes', 'synapse.rs');
11
10
  const SCAN_DIRS = ['.formation', '.claude/hooks', '.specialist', '.collaboration'];
12
11
  const SKIP = /node_modules|[\/](logs|sessions|reports|designs|research|investigations|decisions)[\/]|\.test\.js$/;
13
12
 
14
- function routerPaths() {
15
- const src = fs.readFileSync(SYNAPSE_RS, 'utf8');
16
- const body = src.slice(src.indexOf('pub fn router()'), src.indexOf('#[cfg(test)]'));
17
- return new Set([...body.matchAll(/\.route\(\s*"([^"]+)"/g)].map(m => '/api/synapse' + m[1]));
18
- }
19
13
  function walk(dir, out) {
20
14
  if (!fs.existsSync(dir)) return out;
21
15
  for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
@@ -38,18 +32,12 @@ function calledPaths() {
38
32
  return found;
39
33
  }
40
34
 
41
- test('router paths are discovered', () => {
42
- const r = routerPaths();
43
- for (const p of ['/api/synapse/recall', '/api/synapse/runes/mark-acted-on-by-task']) assert.ok(r.has(p), p);
44
- assert.ok(!r.has('/api/synapse/link-cognitive') && !r.has('/api/synapse/health'));
45
- // TASK_2348: the spike bus and the reflex routes are gone
46
- for (const p of ['/api/synapse/spike-bus/send', '/api/synapse/spike-bus/poll', '/api/synapse/reflex/feedback', '/api/synapse/reflex/dispatch/query', '/api/synapse/reflex/dopamine', '/api/synapse/reflex/consequence_feedback']) assert.ok(!r.has(p), p);
35
+ test('no node code calls the removed /api/synapse or /api/cortex routes', () => {
36
+ const calls = calledPaths();
37
+ assert.deepEqual(calls, [], 'callers of removed routes: ' + JSON.stringify(calls));
47
38
  });
48
39
 
49
- test('every /api/synapse|cortex path called from node code exists on this branch', () => {
50
- const r = routerPaths();
51
- const calls = calledPaths();
52
- assert.ok(calls.length >= 1, 'expected to find callers'); // turn-context.js stopped calling /turn-context (Kuzu removal)
53
- const missing = calls.filter(c => !r.has(c.p));
54
- assert.deepEqual(missing, [], 'callers of removed routes: ' + JSON.stringify(missing));
40
+ test('control: the scanner does find a /api/synapse path in a line of code', () => {
41
+ const line = "fetch('http://127.0.0.1:18080/api/synapse/recall')";
42
+ assert.ok([...line.matchAll(/\/api\/(synapse|cortex)\/[A-Za-z0-9_\-/]+/g)].length === 1);
55
43
  });
@@ -27,10 +27,10 @@ load_order:
27
27
  - path: "general/sunny-access-control.yaml"
28
28
  description: "SEC-010: Sunny 系統存取控制 - 禁止其他 Agent 存取 .system/sunny/"
29
29
  - path: "general/synapse-code-intelligence.yaml"
30
- description: "SYN-001~007: 代碼導航 — kenaz_lsp_* 代碼導航、Grep/Glob 文字與概念搜尋。其中 synapse_recall/inscribe/forge 條款自 2026-10-10 暫停(記憶系統凍結)"
31
- # 2026-10-10: synapse-cognitive-fusion.yaml (SYN-008~012) and cortex-tools.yaml (COR-001~004) removed from the index - memory system frozen (recall ~4.7% acted on, mostly mandated). Files kept on disk.
30
+ description: "SYN-001~007: 代碼導航 — kenaz_lsp_* 代碼導航、Grep/Glob 文字與概念搜尋。記憶層 synapse_* 工具已於 2026-10-11 移除(TASK_2411)"
31
+ # 2026-10-11 (TASK_2411): synapse-cognitive-fusion.yaml, cortex-tools.yaml, memory-source-priority.yaml, memory-language.yaml deleted - memory system removed.
32
32
  - path: "general/ddx-synapse-stack.yaml"
33
- description: "COG-DDX-001~003: DDx 反射 — 非 trivial 問題強制先列 3+ 假說,對抗 tunneling(synapse_recall 條款自 2026-10-10 暫停,改用 Grep/讀碼/記憶檔)"
33
+ description: "COG-DDX-001~003: DDx 反射 — 非 trivial 問題強制先列 3+ 假說,對抗 tunneling(recall 步驟改用 Grep/讀碼/記憶檔;synapse_* 已於 2026-10-11 移除)"
34
34
  - path: "general/specialist-identity-contract.yaml"
35
35
  description: "IDENT-001~007: Specialist 身份延續合約 — 不可變核心、可進化規則、可觀測標記、漂移指標、verify-identity.js 驗證流程"
36
36
  - path: "general/worktree-isolation.yaml"
@@ -1,5 +1,6 @@
1
1
  # .rule/general/ddx-synapse-stack.yaml
2
- # COG-DDX-001: DDx + Synapse reflex stack for any non-trivial problem
2
+ # COG-DDX-001: DDx reflex stack
3
+ # NOTE 2026-10-11 (TASK_2411): synapse_* tools were removed; recall steps use Grep + native memory. for any non-trivial problem
3
4
  # Priority: overrides CLAUDE.md and .claude/skills/
4
5
  # Origin: 2026-04-05 Ansuz session — user identified that DDx + Synapse + Thalamus
5
6
  # is the strongest operating mode but is never activated without user prompt.
@@ -13,11 +14,10 @@ _meta:
13
14
  last_updated: "2026-04-05"
14
15
  related:
15
16
  - "synapse-code-intelligence.yaml"
16
- - "synapse-cognitive-fusion.yaml"
17
17
 
18
18
  rules:
19
19
  - id: "COG-DDX-001"
20
- title: "DDx + Synapse reflex stack — mandatory on first contact with any non-trivial problem"
20
+ title: "DDx reflex stack — mandatory on first contact with any non-trivial problem"
21
21
  severity: "must"
22
22
  applies_to: ["ai_agent"]
23
23
 
@@ -35,16 +35,16 @@ rules:
35
35
  BEFORE the first domain tool call that touches the subject, the agent must:
36
36
  1. List 3+ competing hypotheses with one-line evidence each
37
37
  (differential diagnosis, not first-instinct single path)
38
- 2. Call synapse_recall on domain keywords (and Grep for exact strings)
39
- (historical runes/sigils may already contain the answer)
40
- 3. When modifying any code symbol: synapse_recall it, then check kenaz_lsp_references / kenaz_lsp_callers for the blast radius
38
+ 2. Grep/Glob domain keywords and check native memory files / `kenaz learn list`
39
+ (prior lessons may already contain the answer)
40
+ 3. When modifying any code symbol: check kenaz_lsp_references / kenaz_lsp_callers for the blast radius
41
41
 
42
42
  These three steps are a STACK — skipping any one of them is a violation.
43
43
 
44
44
  why: |
45
45
  Observed failure mode (2026-04-05 TR87 session): agent tunneled 15
46
46
  iterations on a dead hypothesis, attempted brute-force BFS search,
47
- called synapse_recall only reactively. User forced explicit DDx; puzzle
47
+ searched prior knowledge only reactively. User forced explicit DDx; puzzle
48
48
  then cracked in 3 steps.
49
49
  Lesson: principles-as-text do not trigger reflexively. The stack must be
50
50
  a structural precommitment, not an optional heuristic.
@@ -55,12 +55,12 @@ rules:
55
55
  - You are "trying variants" of a single idea after disconfirmation
56
56
  - You are reaching for brute-force enumeration or search
57
57
  - You cannot name 2 hypotheses besides the one you are pursuing
58
- - You have not called synapse_recall this session on the current topic
58
+ - You have not searched code/native memory this session on the current topic
59
59
 
60
60
  enforcement: |
61
61
  - Phase 1 (needs analysis) exit requires hypotheses[] with length >= 3
62
62
  OR an explicit note that problem is trivial (scope exception)
63
- - Phase 2 (background research) exit requires synapse_recall_performed=true
63
+ - Phase 2 (background research) exit requires evidence of Grep/read-code research
64
64
  - Kernel guardrails enforce at dispatch time
65
65
 
66
66
  examples:
@@ -69,14 +69,14 @@ rules:
69
69
  H1: auth token expired/invalid (check expiry logic)
70
70
  H2: session store disconnected (check Redis/DB)
71
71
  H3: CORS/cookie path changed (check recent config diffs)
72
- synapse_recall(query="login auth token session"), kenaz_lsp_callers on
72
+ Grep "login auth token session", kenaz_lsp_callers on
73
73
  the entrypoint — THEN read code.
74
74
  design_decision: |
75
75
  User asks "should we use Redis or Postgres pub/sub?" BEFORE recommending:
76
76
  H1: Redis if latency < 10ms matters and durability is optional
77
77
  H2: Postgres if ACID + existing infra reuse wins
78
78
  H3: Neither — use in-process channels if single-node
79
- synapse_recall(query="pub sub messaging event bus") to find prior decisions.
79
+ Grep/native memory for "pub sub messaging event bus" to find prior decisions.
80
80
 
81
81
  - id: "COG-DDX-002"
82
82
  title: "Hypothesis death triggers immediate DDx re-enumeration"
@@ -35,8 +35,8 @@ rules:
35
35
  The identity_contract.yaml is referenced (not duplicated) — the
36
36
  self_model_seed.json NARRATIVE section encodes the contract's intent.
37
37
  enforcement: |
38
- Phase 0 actions item "[Tier 3b — 身份認識]" triggers cortex_recall for
39
- Ansuz identity awareness. A session that skips Phase 0 has no identity.
38
+ Phase 0 actions item "[Tier 3b — 身份認識]" loads the self-model
39
+ files for Ansuz identity awareness (cortex_recall removed 2026-10-11). A session that skips Phase 0 has no identity.
40
40
  violation: "DI-001 — no phase structure visible"
41
41
  source: "dev/archive/ansuz/legacy/phases/phase_0.yaml → actions[Tier 3b]"
42
42
 
@@ -44,12 +44,12 @@ rules:
44
44
  title: "Observable markers are evaluated at Phase 9 cognitive checkpoint"
45
45
  rule: |
46
46
  During Phase 9 cognitive checkpoint, the session MUST evaluate:
47
- 1. cognitive_checkpoint_compliance (OM-001) — inscribe or explicit skip
47
+ 1. cognitive_checkpoint_compliance (OM-001) — lesson recorded (`kenaz learn add` / native memory) or explicit skip
48
48
  2. acceptance_criteria_evaluation (OM-002) — all criteria have status
49
49
  These two markers carry 0.45 of the total identity score. Skipping both
50
50
  guarantees DRIFT_WARNING even if all other markers pass.
51
51
  enforcement: |
52
- Phase 9 checklist item: "cognitive_checkpoint 已判斷(inscribe 或明確跳過)"
52
+ Phase 9 checklist item: "cognitive_checkpoint 已判斷(記錄教訓或明確跳過)"
53
53
  and acceptance criteria evaluation in progress note.
54
54
  violation: "DI-005 — Phase 9 cognitive checkpoint absent"
55
55
  source: "dev/archive/ansuz/legacy/phases/phase_9.yaml → cognitive_checkpoint"
@@ -88,7 +88,7 @@ rules:
88
88
  When evaluating identity across model substrates (e.g., Opus 4.6 vs Sonnet 4.6),
89
89
  the following differences are acceptable and MUST NOT be flagged as drift:
90
90
  - Response verbosity differences
91
- - Different Synapse tool choices for equivalent recall
91
+ - Different tool choices for equivalent lookup
92
92
  - Analysis depth differences
93
93
  - Phrasing variation in principles
94
94
  Identity is evaluated on STRUCTURE (phase discipline, task system, memory usage,
@@ -1,10 +1,10 @@
1
1
  # .rule/general/synapse-code-intelligence.yaml
2
- # 代碼導航與記憶規則 — Agent 何時、如何使用 kenaz_lsp_* / Grep / Glob / Synapse 記憶工具
2
+ # 代碼導航規則 — Agent 何時、如何使用 kenaz_lsp_* / Grep / Glob
3
3
  # 優先級:此規則凌駕於 CLAUDE.md 和 .claude/skills/ 中的定義
4
4
  #
5
5
  # TASK_2335(Kuzu replacement W5a):Synapse 的代碼圖譜(search / context / impact / pulse / map /
6
6
  # cypher / trace_route / detect_changes 等)已移除。代碼導航改由 kenaz_lsp_* 工具提供;
7
- # 文字與概念搜尋用 Grep / Glob;Synapse 只保留記憶層(recall / inscribe / forge / sigils)。
7
+ # 文字與概念搜尋用 Grep / Glob。記憶層(synapse_*)已於 2026-10-11 移除(TASK_2411)。
8
8
  # 規則編號 SYN-001~007 保持不變(其他規則與失敗記錄以編號引用)。
9
9
 
10
10
  _meta:
@@ -23,21 +23,20 @@ rules:
23
23
  title: "工具可用性偵測"
24
24
  description: |
25
25
  每個 session 開始時,偵測 Kenaz MCP 工具是否可用。
26
- kenaz_lsp_* 與 synapse_* 都依賴 Dashboard(Rust HTTP server on port 18080),
26
+ kenaz_lsp_* 依賴 Dashboard(Rust HTTP server on port 18080);
27
27
  Dashboard 沒啟動時,MCP 工具不會出現在工具列表中。
28
28
  severity: "must"
29
29
 
30
30
  detection_method: |
31
- 使用 ToolSearch 搜尋 "synapse"(記憶工具)與 "kenaz_lsp"(代碼導航工具):
32
- - 找到 synapse_status / synapse_recall → 記憶層可用
31
+ 使用 ToolSearch 搜尋 "kenaz_lsp"(代碼導航工具):
33
32
  - 找到 kenaz_lsp_callers / kenaz_lsp_references → LSP 導航可用
34
- - 沒找到 → 對應能力不可用,降級為 Grep/Glob
33
+ - 沒找到 → 不可用,降級為 Grep/Glob
35
34
 
36
35
  on_unavailable: |
37
36
  該 session 全程使用 Grep/Glob,不再嘗試已確認不可用的工具。
38
37
 
39
38
  project_path_mandatory: |
40
- ⚠️ 所有 kenaz_lsp_* 與 synapse_* MCP 工具呼叫必須明確帶 projectPath 參數。
39
+ ⚠️ 所有 kenaz_lsp_* MCP 工具呼叫必須明確帶 projectPath 參數。
41
40
  Dashboard 的 active project 可能指向其他專案,default project 不可靠。
42
41
  - Core 模式:projectPath = 當前工作目錄的絕對路徑
43
42
  - 子專案模式:projectPath = 子專案的絕對路徑(從 .kenaz/project.yaml 讀取)
@@ -50,7 +49,7 @@ rules:
50
49
  title: "搜尋工具選擇決策樹"
51
50
  description: |
52
51
  Agent 遇到搜尋 / 導航需求時,按此決策樹選擇工具。
53
- 原則:文字用 Grep/Glob,符號關係用 LSP,歷史經驗用 synapse_recall。
52
+ 原則:文字用 Grep/Glob,符號關係用 LSP。
54
53
  severity: "should"
55
54
 
56
55
  decision_tree: |
@@ -68,10 +67,6 @@ rules:
68
67
  ├── 「這個函數/類在哪裡定義?」 → kenaz_lsp_definition(projectPath, file, line | symbol)
69
68
  ├── 「這個檔案有哪些 symbol?」 → kenaz_lsp_symbols(projectPath, file)
70
69
  │
71
- ├── [歷史經驗 — 需要 Synapse 記憶層可用]
72
- │
73
- ├── 「這個 symbol / 模組有已知風險或決策嗎?」 → synapse_recall
74
- │
75
70
  ├── [Bug 調查兩階段]
76
71
  │
77
72
  ├── Step 1: 定位入口(error log 有精確路徑/URL)→ Grep 找到檔案
@@ -127,27 +122,13 @@ rules:
127
122
  use_instead: "Grep (count mode)"
128
123
 
129
124
  # =============================================================================
130
- # SYN-003.5: 記憶查詢語言規則
125
+ # SYN-003.5: 記憶查詢語言規則(已移除)
126
+ # synapse_recall/inscribe 已於 2026-10-11 移除(TASK_2411),英文限制不再適用。
131
127
  # =============================================================================
132
128
  - id: "SYN-003.5"
133
- title: "記憶查詢語言規則"
134
- description: |
135
- Synapse 記憶層的 FTS5 和 embedding 都基於英文。
136
- 非英文查詢(中文、日文等)必須先翻譯成英文再召回。
137
- severity: "must"
138
-
139
- rule: |
140
- 召回和寫入都必須使用英文:
141
-
142
- 召回(synapse_recall):
143
- - 用戶用非英文描述需求時,翻譯成英文再查詢
144
- - 例:「怎麼處理終端機的大小調整?」→ query="terminal resize fit columns rows"
145
-
146
- 寫入(synapse_inscribe / synapse_forge):
147
- - Rune content 必須用英文撰寫
148
- - Sigil name 和 abstract 必須用英文
149
- - 原因:recall 用英文搜尋,中文 content 永遠搜不到
150
- - 例:「加入 WAL 修復鎖定問題」→ content="Added WAL + busy_timeout to fix exclusive lock blocking reads"
129
+ title: "記憶查詢語言規則(已移除)"
130
+ description: "Synapse 記憶工具已移除;此條不再適用。"
131
+ severity: "may"
151
132
 
152
133
  # =============================================================================
153
134
  # SYN-004: kenaz_lsp_* 參數指南
@@ -222,24 +203,20 @@ rules:
222
203
 
223
204
  refactoring: |
224
205
  重構前評估:
225
- kenaz_lsp_references → kenaz_lsp_callers → synapse_recall
206
+ kenaz_lsp_references → kenaz_lsp_callers
226
207
  1. kenaz_lsp_references 看改動的 blast radius
227
208
  2. kenaz_lsp_callers 看跨模組呼叫者
228
- 3. synapse_recall 查已知風險
229
209
 
230
210
  pre_task: |
231
211
  開發任務前的標準動作:
232
- synapse_status → synapse_recall → Grep/Glob → kenaz_lsp_*
233
- 1. synapse_status 檢查記憶層狀態
234
- 2. synapse_recall 查詢歷史 rune 知識(有沒有已知的 risk/decision 需要注意)
235
- 3. Grep/Glob 找相關實現和型別定義
236
- 4. 修改既有 symbol 前用 kenaz_lsp_references / kenaz_lsp_callers 看影響範圍
212
+ Grep/Glob → kenaz_lsp_*
213
+ 1. Grep/Glob 找相關實現和型別定義
214
+ 2. 修改既有 symbol 前用 kenaz_lsp_references / kenaz_lsp_callers 看影響範圍
237
215
 
238
216
  pre_code_analysis: |
239
217
  寫代碼前的分析(修改既有 symbol 時):
240
- 1. synapse_recall(symbol 或模組關鍵字) → 已知 risk / decision / fix
241
- 2. kenaz_lsp_references(symbol) → 爆炸半徑
242
- 3. kenaz_lsp_callers(symbol, depth=2) → 跨模組呼叫者
218
+ 1. kenaz_lsp_references(symbol) → 爆炸半徑
219
+ 2. kenaz_lsp_callers(symbol, depth=2) → 跨模組呼叫者
243
220
  禁止盲寫 — 修改非 trivial symbol 前不看影響範圍是被禁止的行為(FAIL-009)。
244
221
 
245
222
  red_flags: |
@@ -261,88 +238,29 @@ rules:
261
238
  reality: "爆炸半徑由引用關係決定,不由你的直覺決定。一行改動可以跨三個模組邊界。"
262
239
 
263
240
  post_code: |
264
- 寫代碼後的驗證和認知更新:
241
+ 寫代碼後的驗證:
265
242
  1. git diff 確認實際改了哪些 symbol
266
243
  2. 對每個改動的公開 symbol → kenaz_lsp_references 檢查是否有漏改的呼叫端
267
- 3. 如有 non-obvious 發現 → synapse_inscribe 留給下一個 agent
244
+ 3. 如有 non-obvious 發現 → `kenaz learn add` 或原生記憶檔留給下一個 agent
268
245
 
269
- post_task: |
270
- 任務完成後的認知層更新(選擇性):
271
- synapse_inscribe / synapse_forge
272
- 判斷標準:「這個發現,未來 agent 在同 symbol 附近工作時需要知道嗎?」
273
- YES → synapse_inscribe(單 symbol 標註)或 synapse_forge(跨 symbol 概念)
274
- NO → 跳過,不要為了寫而寫
275
246
 
276
247
  tool_summary:
277
248
  kenaz_lsp_callers: "誰呼叫這個 symbol(支援 depth 遞迴)"
278
249
  kenaz_lsp_references: "所有引用位置 — 修改影響範圍"
279
250
  kenaz_lsp_definition: "定義位置"
280
251
  kenaz_lsp_symbols: "檔案內 symbol 列表"
281
- synapse_status: "檢查記憶層狀態"
282
- synapse_recall: "FTS5 召回 runes + sigils,按 confidence × recency 排序(認知層歷史知識)"
283
- synapse_inscribe: "對 symbol 寫入 Rune 知識標註(insight/decision/fix/pattern/risk/summary)。supersedes 參數可取代舊 rune"
284
- synapse_forge: "建立/更新 Sigil 跨 symbol 概念節點(永久,同名 upsert)"
285
- synapse_sigils: "瀏覽所有 Sigil 概念圖,expand=true 附 symbol 摘要"
286
252
 
287
253
  # =============================================================================
288
- # SYN-007: 認知層工具使用規則
254
+ # SYN-007: 認知層工具(已移除)
289
255
  # =============================================================================
290
256
  - id: "SYN-007"
291
- title: "認知層工具使用規則"
257
+ title: "認知層工具已移除"
292
258
  description: |
293
- 記憶層工具(synapse_recall / synapse_inscribe / synapse_forge / synapse_sigils)
294
- 的使用時機和紀律。這些工具操作 memory.db(獨立 SQLite)。
259
+ synapse_recall / inscribe / forge / sigils 等記憶層 MCP 工具已於 2026-10-11 移除(TASK_2411),
260
+ 呼叫會得到 Unknown tool。教訓寫入 `kenaz learn add` 或 Claude 原生記憶檔(含 triggers: frontmatter);
261
+ 歷史知識用 Grep / 讀碼 / `kenaz learn list` 查。
295
262
  severity: "should"
296
263
 
297
- recall_triggers:
298
- description: "以下情況應呼叫 synapse_recall 查詢歷史知識"
299
- must_recall:
300
- - trigger: "任務開始,scope_paths 涉及已有 symbol"
301
- query_hint: "用 scope_paths 中的關鍵詞搜尋"
302
- purpose: "確認有無已知 risk、design decision、已修復的 bug pattern"
303
- - trigger: "遇到陌生或複雜的函數/模組"
304
- query_hint: "用 symbol 名稱或功能描述搜尋"
305
- purpose: "Agent 之前可能已留下 insight"
306
- - trigger: "debug 過程中懷疑有類似歷史問題"
307
- query_hint: "用錯誤特徵或受影響模組搜尋"
308
- purpose: "避免重複排查已知問題"
309
- skip_recall_when:
310
- - "memory.db 尚無任何資料(全新系統,尚未有 agent inscribe)"
311
- - "任務是全新功能,與現有 symbol 無交集"
312
- - "明確知道沒有相關 rune(之前剛查過)"
313
-
314
- inscribe_triggers:
315
- description: "任務完成後,判斷是否值得留下 Rune"
316
- write_rune_when:
317
- - "發現 non-obvious 的 bug pattern 或 side-effect"
318
- - "做出影響後續開發的架構決策"
319
- - "修復了容易再次出現的問題(type=fix)"
320
- - "發現模組有使用限制或已知風險(type=risk)"
321
- do_not_write_when:
322
- - "只是執行了標準流程,沒有新發現"
323
- - "重複 docstring 或 code 中已說明的事"
324
- - "confidence < 0.6(系統會拒絕)"
325
- - "一般任務執行步驟(用 task log 記錄)"
326
- symbol_id_format: "file_path:name:kind(例:src/synapse/memory.rs:inscribe_rune:function)"
327
- related_rune_ids: |
328
- SYN-012(強制):inscribe 時回溯本 session 先前 recall 過的 rune,將相關 rune_id 傳入
329
- related_rune_ids 參數,閉合 recall → acted_on → confidence_boost 學習迴路。
330
-
331
- forge_triggers:
332
- description: "識別出跨 symbol 的語義抽象時呼叫 synapse_forge"
333
- write_sigil_when:
334
- - "多個 symbol 共同構成一個有名字的概念(如 payment_flow、auth_pipeline)"
335
- - "發現一個重要的 anti_pattern 跨越多個模組"
336
- note: "Sigil 是稀缺的,比 Rune 更需要謹慎。一個 Sigil 代表整個系統的一個認知單元"
337
-
338
- conflict_resolution:
339
- description: "當新發現與已有 Rune 衝突時的處理流程"
340
- supersede_flow:
341
- - "recall 撈出舊 rune → 發現內容過時或錯誤"
342
- - "synapse_inscribe({ ..., supersedes: '<舊rune_id>' }) → 舊 rune 被標記 superseded_by,recall 自動排除"
343
- - "新 rune 取代舊 rune 成為 active 知識"
344
- rule: "不要留下矛盾的 rune 並存 — 用 supersedes 取代;檢查矛盾用 synapse_rune_conflicts,過時檢查用 synapse_scan_stale_runes"
345
-
346
264
  # =============================================================================
347
265
  # 快速參考
348
266
  # =============================================================================
@@ -361,10 +279,6 @@ quick_reference:
361
279
  kenaz_lsp_symbols:
362
280
  best_for: "不讀完整檔案就了解其結構"
363
281
 
364
- synapse_recall:
365
- strength: "歷史 rune / sigil 召回,按 confidence × recency 排序"
366
- best_for: "修改前確認有無已知 risk / decision / fix"
367
-
368
282
  grep:
369
283
  strength: "精確匹配、正則、速度快、無依賴"
370
284
  weakness: "不理解語意、結果無排序"
@@ -43,8 +43,8 @@ rules:
43
43
  description: |
44
44
  ALL Edit/Read/Write operations MUST use the worktree path as prefix.
45
45
  Example: if pwd returns /c/.../KenazAI/.claude/worktrees/agent-abc/,
46
- then reading dashboard/src/app/page.tsx uses:
47
- /c/.../KenazAI/.claude/worktrees/agent-abc/dashboard/src/app/page.tsx
46
+ then reading packages/kenaz/lib/cli.js uses:
47
+ /c/.../KenazAI/.claude/worktrees/agent-abc/packages/kenaz/lib/cli.js
48
48
  severity: "must"
49
49
 
50
50
  - id: "WT-001-C"
@@ -248,15 +248,12 @@ rules:
248
248
 
249
249
  === Agent 傳遞測試指令 ===
250
250
  post-complete.js --task=TASK_XXX \
251
- --test-cmd "cd dashboard && npm test -- --passWithNoTests" \
252
- --test-cmd "cd dashboard/pty-server && cargo test"
251
+ --test-cmd "node dev/scripts/rust-verify-worktree.js run <branch> <filter>"
253
252
 
254
253
  如果 Agent 未傳 --test-cmd,腳本 fallback 到自動偵測所有可用測試套件。
255
254
 
256
255
  === Agent 測試判斷指引 ===
257
- - 修改 dashboard/src/ (React/TS) → --test-cmd "cd dashboard && npm test -- --passWithNoTests"
258
- - 修改 dashboard/src-tauri/ (Rust) → --test-cmd "cd dashboard/src-tauri && cargo test"
259
- - 修改 dashboard/pty-server/ (Rust) → --test-cmd "cd dashboard/pty-server && cargo test"
256
+ - 修改 dashboard/src-tauri/ (Rust, kenaz-server) → --test-cmd "node dev/scripts/rust-verify-worktree.js run <branch> <filter>"(Dashboard 前端與 pty-server 已於 2026-10-11 移除)
260
257
  - 修改 kenaz-cortex/ (Python) → --test-cmd "cd kenaz-cortex && pytest"
261
258
  - 只修改 .yaml/.md/設定檔 → --skip-tests(無需測試)
262
259
  - 修改多個子系統 → 多個 --test-cmd,分別指定
@@ -278,12 +275,12 @@ rules:
278
275
  Agent 忘記傳參時,auto-detect fallback 保底。
279
276
  防止 Agent 完成 TASK_B 時意外破壞 TASK_A 的功能。
280
277
  examples:
281
- - scenario: "修改了 React 組件和 Rust PTY server"
278
+ - scenario: "修改了 kenaz-server 的 Rust 與 plugin 的 JS"
282
279
  action: |
283
- Agent 判斷:改了 dashboard/src/ 和 dashboard/pty-server/
280
+ Agent 判斷:改了 dashboard/src-tauri/ 和 plugins/kenaz/scripts/
284
281
  → post-complete.js --task=TASK_XXX \
285
- --test-cmd "cd dashboard && npm test -- --passWithNoTests" \
286
- --test-cmd "cd dashboard/pty-server && cargo test"
282
+ --test-cmd "node dev/scripts/rust-verify-worktree.js run <branch> peer" \
283
+ --test-cmd "node --test plugins/kenaz/scripts/kenaz.test.js"
287
284
  → 腳本依序跑 2 組測試
288
285
  → 全過 → 繼續 PPL-109 commit
289
286
  - scenario: "只修改了 Python 後端 API"