kenaz 0.3.5 → 0.3.6

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 (44) hide show
  1. package/core/.claude/commands/Ansuz.md +1 -0
  2. package/core/.formation/native/claude/README.md +1 -1
  3. package/core/.formation/role-map.yaml +3 -3
  4. package/core/.formation/spec/task_operations.yaml +3 -3
  5. package/core/.formation/tools/__tests__/subagent-bifrost-guard.test.js +4 -4
  6. package/core/.formation/tools/dispatch.js +5 -5
  7. package/core/.formation/tools/prompt.js +4 -0
  8. package/core/.formation/unit/do.yaml +2 -4
  9. package/core/.rule/general/agent-workflow.yaml +4 -4
  10. package/core/.rule/general/file-size-threshold.yaml +16 -4
  11. package/core/.rule/general/rust-verify-target.yaml +0 -1
  12. package/core/.rule/general/system-invariants.yaml +8 -25
  13. package/core/.specialist/Ansuz/SYSTEM_DOC.md +17 -42
  14. package/core/.specialist/Ansuz/capability.yaml +7 -14
  15. package/core/.specialist/Ansuz/concepts/formation_quick_ref.yaml +4 -11
  16. package/core/.specialist/Ansuz/elixir.yaml +3 -4
  17. package/core/.specialist/Ansuz/identity_contract.yaml +1 -1
  18. package/core/.specialist/Ansuz/knowledge.yaml +7 -10
  19. package/core/.specialist/Ansuz/rule.yaml +2 -2
  20. package/core/.specialist/Ansuz/tools/verify-and-land.js +22 -0
  21. package/core/.specialist/Ansuz/tools/verify-and-land.test.js +59 -5
  22. package/core/.specialist/Ansuz/workflow.yaml +7 -11
  23. package/core/.specialist/Forseti/core.yaml +1 -1
  24. package/core/.specialist/Heimdall/SKILL.md +1 -1
  25. package/core/.specialist/Heimdall/core.yaml +2 -2
  26. package/core/.specialist/Huginn/rule.yaml +1 -1
  27. package/core/.specialist/tools/README.md +2 -2
  28. package/core/.system/collaboration/README.md +1 -1
  29. package/core/.system/formation/LEGACY_SKILLS.md +2 -0
  30. package/core/.system/formation/POST_COMPLETE.md +2 -0
  31. package/core/.system/formation/README.md +2 -0
  32. package/core/KENAZ_CORE_VERSION +1 -1
  33. package/core/dev/scripts/check-file-size.js +44 -17
  34. package/core/k-cli/README.md +1 -94
  35. package/core/k-cli/test/TEST_SPEC.md +5 -22
  36. package/core/k-cli/test/run.sh +1 -67
  37. package/core/plugins/kenaz/commands/Ansuz.md +1 -0
  38. package/package.json +6 -6
  39. package/core/.formation/legion/forge.yaml +0 -299
  40. package/core/.formation/legion/hive.yaml +0 -238
  41. package/core/.formation/squad/blitz.yaml +0 -192
  42. package/core/.formation/squad/strategy-tribunal.yaml +0 -354
  43. package/core/.formation/squad/strategy.yaml +0 -349
  44. package/core/.formation/squad/sweep.yaml +0 -208
@@ -23,6 +23,7 @@ ARGUMENTS 含 `[專案: X]` 或 `--project-path X` → PROJECT_PATH = X,下方
23
23
  - 有**對照組**:證明測試在舊程式上會失敗,或修改前後結果不同。
24
24
  - subagent 回報「未驗證」的項目,由你補驗或明講仍未驗證。
25
25
  - 合併:`git merge` 後跑相關測試(或 `node .specialist/Ansuz/tools/verify-and-land.js`),用這個專案自己的測試指令。
26
+ - 檔案長度:verify-and-land 合併前會跑 `check-file-size`(警告 1000 / 上限 2000 行,既有超長檔只擋「未配套拆分的增長」);被擋就按職責拆成 module / component / hook。
26
27
  - 只在 Kenaz 核心 repo 本身:Rust 測試走 `npm run test:rust` / `dev/scripts/rust-verify-worktree.js`。
27
28
 
28
29
  ## 回報
@@ -51,7 +51,7 @@ no surprise dependencies.
51
51
  4. When the user picks an entry, the id is written into
52
52
  `.kenaz/dashboard-models.json` via
53
53
  `POST /api/dashboard/settings/models` and consumed by
54
- `kenaz_dispatch` / `SpecialistPanel.handleLaunch`.
54
+ `SpecialistPanel.handleLaunch`.
55
55
 
56
56
  ## Adding a new model
57
57
 
@@ -3,8 +3,8 @@
3
3
  # Amelia 判斷 role 時參考此表,dispatch 時用此表解析 --role 路徑
4
4
  #
5
5
  # Usage:
6
- # node .formation/tools/headless-v2.js --base=do --role=developer --task=TASK_XXX
7
- # headless-v2 會查此表找到 skill_path → 載入該 YAML 的 role context
6
+ # node .formation/tools/dispatch.js --skill=do --task=TASK_XXX --dispatch-mode=subagent
7
+ # (此表僅供 Amelia 判斷 role 參考;dispatch.js 不讀取它。headless-v2 已退役)
8
8
 
9
9
  version: "2.0"
10
10
 
@@ -259,7 +259,7 @@ roles:
259
259
  - 新功能開發
260
260
 
261
261
  # ─────────────────────────────────────────
262
- # Base Skills(headless-v2 的 --base 選項)
262
+ # Base Skills(歷史:原 headless-v2 的 --base 選項)
263
263
  # ─────────────────────────────────────────
264
264
 
265
265
  bases:
@@ -4,7 +4,7 @@
4
4
  #
5
5
  # Consumers:
6
6
  # A: .collaboration/core/task-api.js — full lifecycle (create/claim/complete/move/delete/get/list)
7
- # B: .formation/tools/headless.js — dispatch coordinator (no direct state mutation; triggers post-complete)
7
+ # B: .formation/tools/dispatch.js — dispatch coordinator (no direct state mutation; triggers post-complete)
8
8
  # C: .formation/tools/post-complete.js — close-out only (active→done or active→blocked; never creates)
9
9
  # D: dashboard kenaz MCP tools (kenaz.rs) — CRUD subset only (list/get/create/update/delete via task-api CLI proxy)
10
10
  #
@@ -13,7 +13,7 @@
13
13
  # MCP does NOT have standalone claim, complete, or move tools — these are action values in tool_task_update.
14
14
  # task-api.js (consumer A) owns the full state machine and all lifecycle transitions.
15
15
  # post-complete.js (consumer C) is exclusively close-out; it calls task-api complete/move internally.
16
- # headless.js (consumer B) coordinates dispatch and triggers post-complete on task completion.
16
+ # dispatch.js (consumer B) coordinates dispatch and triggers post-complete on task completion.
17
17
  #
18
18
  # STATUS DUALITY (known divergence):
19
19
  # Filesystem directory names (canonical): backlog, active, done, blocked, cancel, archived
@@ -153,7 +153,7 @@ operations:
153
153
  post_complete_trigger: true
154
154
  known_asymmetry:
155
155
  - "moveTask(taskId, 'done') achieves same filesystem effect but does NOT trigger post-complete"
156
- - "Consumer B (headless.js) bypasses via FORMATION_HEADLESS=1 and calls post-complete separately"
156
+ - "Consumer B (dispatch.js) does not mutate task state itself; post-complete runs separately"
157
157
  error_modes:
158
158
  - "Task not found → throws"
159
159
  - "post-complete failure → logged as warning; task already moved to done (non-atomic)"
@@ -95,8 +95,8 @@ setModels({ dispatchModel: 'gpt-5-codex', dispatchFamily: 'bifrost', dispatchAda
95
95
  check('reject bifrost+adapter: no agentParams on stdout', !(r.stdout || '').includes('agentParams'), (r.stdout || '').slice(0, 200));
96
96
  check('reject bifrost+adapter: invariant wording (refusing to silently dispatch)',
97
97
  (r.stderr || '').includes('refusing to silently dispatch with the native Claude CLI'), (r.stderr || '').slice(-400));
98
- check('reject bifrost+adapter: remediation points to Formation dispatch (kenaz_dispatch)',
99
- (r.stderr || '').includes('kenaz_dispatch'), (r.stderr || '').slice(-400));
98
+ check('reject bifrost+adapter: remediation tells the user to pick a Claude model',
99
+ (r.stderr || '').includes('Select a Claude model') && !(r.stderr || '').includes('kenaz_dispatch'), (r.stderr || '').slice(-400));
100
100
  }
101
101
 
102
102
  // ── [3] reject: dispatchFamily=bifrost, adapter missing/empty ───────────────
@@ -175,8 +175,8 @@ setModels({ dispatchModel: 'claude-nova-9', dispatchFamily: 'claude' });
175
175
  check('unmappable model: loud stderr (refusing to silently guess)',
176
176
  (r.stderr || '').includes("dispatchModel 'claude-nova-9' cannot be mapped") &&
177
177
  (r.stderr || '').includes('refusing to silently guess'), (r.stderr || '').slice(-400));
178
- check('unmappable model: remediation points to Formation dispatch (kenaz_dispatch)',
179
- (r.stderr || '').includes('kenaz_dispatch'), (r.stderr || '').slice(-400));
178
+ check('unmappable model: remediation points at the model selection, not retired kenaz_dispatch',
179
+ (r.stderr || '').includes('Fix the model selection') && !(r.stderr || '').includes('kenaz_dispatch'), (r.stderr || '').slice(-400));
180
180
  }
181
181
 
182
182
  // ── [9]-[11] TASK_2032: subagent dispatch activates the card like the ────────
@@ -990,14 +990,14 @@ async function getSubagentDispatchParams(options) {
990
990
  }
991
991
  if (routing.family === 'bifrost' && !isCodex) {
992
992
  if (!routing.adapter) {
993
- console.error('❌ dashboard-models.json sets dispatchFamily=bifrost but dispatchAdapter is missing/empty — refusing to silently dispatch with the native Claude CLI. Fix the model selection (Quick Actions → Default Models) or dashboard-models.json, then use Formation dispatch (kenaz_dispatch).');
993
+ console.error('❌ dashboard-models.json sets dispatchFamily=bifrost but dispatchAdapter is missing/empty — refusing to silently dispatch with the native Claude CLI. Fix the model selection (Quick Actions → Default Models) or dashboard-models.json.');
994
994
  } else {
995
- console.error(`❌ dashboard-models.json sets dispatchFamily=bifrost (dispatchAdapter '${routing.adapter}') — sub-agent dispatch cannot run non-Claude models (the Agent tool only accepts Claude models); refusing to silently dispatch with the native Claude CLI. Use Formation dispatch (kenaz_dispatch) instead — it routes through the Bifrost adapter.`);
995
+ console.error(`❌ dashboard-models.json sets dispatchFamily=bifrost (dispatchAdapter '${routing.adapter}') — sub-agent dispatch cannot run non-Claude models (the Agent tool only accepts Claude models); refusing to silently dispatch with the native Claude CLI. Select a Claude model in Default Models.`);
996
996
  }
997
997
  return null;
998
998
  }
999
999
  if (!isCodex && routing.family && routing.family !== 'claude') {
1000
- console.error(`❌ dashboard-models.json sets unsupported dispatchFamily '${routing.family}' (expected 'claude' or 'bifrost') — refusing to silently dispatch with the native Claude CLI. Fix the model selection (Quick Actions → Default Models) or use Formation dispatch (kenaz_dispatch).`);
1000
+ console.error(`❌ dashboard-models.json sets unsupported dispatchFamily '${routing.family}' (expected 'claude' or 'bifrost') — refusing to silently dispatch with the native Claude CLI. Fix the model selection (Quick Actions → Default Models).`);
1001
1001
  return null;
1002
1002
  }
1003
1003
 
@@ -1017,7 +1017,7 @@ async function getSubagentDispatchParams(options) {
1017
1017
  // TASK_2315: Codex inherits the session model — nothing to translate or pass.
1018
1018
  const translated = isCodex ? { model: null, lossless: true } : translateModelForAgentTool(rawModel);
1019
1019
  if (!isCodex && !translated.model) {
1020
- console.error(`❌ ${resolution.label} '${rawModel}' cannot be mapped to an Agent tool model family (${AGENT_TOOL_MODEL_ENUM.join('|')}) — refusing to silently guess a substitute model. Fix the model selection (Quick Actions → Default Models) or use Formation dispatch (kenaz_dispatch), which accepts full model ids via --dispatch-model.`);
1020
+ console.error(`❌ ${resolution.label} '${rawModel}' cannot be mapped to an Agent tool model family (${AGENT_TOOL_MODEL_ENUM.join('|')}) — refusing to silently guess a substitute model. Fix the model selection (Quick Actions → Default Models).`);
1021
1021
  return null;
1022
1022
  }
1023
1023
  const dispatchModel = translated.model;
@@ -1025,7 +1025,7 @@ async function getSubagentDispatchParams(options) {
1025
1025
  ? { requested: rawModel, inherited: true }
1026
1026
  : { requested: rawModel, agentToolModel: dispatchModel, granularityLoss: !translated.lossless };
1027
1027
  if (!translated.lossless) {
1028
- process.stderr.write(`[dispatch] model '${rawModel}' → Agent tool family '${dispatchModel}' — the Agent tool only expresses family names (${AGENT_TOOL_MODEL_ENUM.join('|')}); version/speed selections (e.g. 5 vs 4.x, [1m]) are NOT expressible on the subagent path. If the exact version matters, use Formation dispatch (kenaz_dispatch), which passes full model ids.\n`);
1028
+ process.stderr.write(`[dispatch] model '${rawModel}' → Agent tool family '${dispatchModel}' — the Agent tool only expresses family names (${AGENT_TOOL_MODEL_ENUM.join('|')}); version/speed selections (e.g. 5 vs 4.x, [1m]) are NOT expressible on the subagent path. If the exact version matters, choose a model whose family the Agent tool can express.\n`);
1029
1029
  }
1030
1030
  // TASK_1998 R3 (+ two-axis policy): the resolved choice, always printed
1031
1031
  // (unlike the granularity block above, which only fires on lossy translation).
@@ -171,6 +171,10 @@ function buildSubagentPrompt(skillDef, taskInput, taskData, options = {}) {
171
171
  // Ponytail A/B 2026-10-10: the arm without this line broke an existing fixture test it never ran.
172
172
  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.');
173
173
  lines.push('Run the tests the card names (narrow, not the full suite); say what you did not verify.');
174
+ // The worktree branches off the project's current branch; user projects are not always on `main`.
175
+ const sizeBase = (require('child_process').spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: options.projectPath || process.cwd(), encoding: 'utf8' }).stdout || '').trim() || 'main';
176
+ const sizeCorePath = options.corePath || resolveCorePath(options.projectPath) || path.resolve(__dirname, '..', '..');
177
+ lines.push(`Before reporting run \`node ${sizeCorePath}/dev/scripts/check-file-size.js --base ${sizeBase} --head HEAD\`; if it BLOCKs, split by responsibility (module / component / hook) first.`);
174
178
  const onBlocked = skillDef.collaboration && skillDef.collaboration.on_blocked;
175
179
  if (onBlocked && onBlocked.run) {
176
180
  lines.push(`Blocked: \`${buildTaskApiRewriter(options)(onBlocked.run).replace(/\{task_id\}/g, taskId)}\` and explain.`);
@@ -48,10 +48,8 @@ collaboration:
48
48
  - 根據 acceptance_criteria 驗證成果
49
49
  # TASK_1819: 此處原有的 worktree_path_rule 欄位已移除 — 它是死欄位,
50
50
  # prompt.js 的 appendWorkflow 只讀 step/run/note/actions,從未渲染它,
51
- # 且其範例路徑早已過時。實際生效的 worktree 路徑規則由 runner.js 在
52
- # spawn 時注入(spawnClaude/spawnBifrost 的 "[WORKTREE] All file
53
- # operations MUST use paths under: <worktreeDir>" 前綴,含具體路徑),
54
- # Agent-tool 派工另由 prompt.js SUBAGENT_PREFIX 規則 11 覆蓋。
51
+ # 且其範例路徑早已過時。實際生效的 worktree 路徑規則由
52
+ # prompt.js SUBAGENT_PREFIX 規則 11 覆蓋(原 runner.js 注入已隨 headless 路徑移除)。
55
53
 
56
54
  # 注意(TASK_1819):下方 run 的 git add -A 只會在隔離模式(worktree /
57
55
  # subagent)原樣渲染;serial 共用工作樹模式下 prompt.js appendWorkflow
@@ -543,15 +543,15 @@ rules:
543
543
  - id: "AGT-010"
544
544
  title: "Dispatch 後切回 User Branch"
545
545
  description: |
546
- Formation headless.js dispatch 會在獨立進程中建立 task branch(task/task_XXX),
546
+ (已退役)Formation headless dispatch 曾在獨立進程中建立 task branch(task/task_XXX),
547
547
  完成後 post-complete.js 會 merge 回 user branch。但呼叫者(Ansuz/Agent)的
548
548
  working tree 仍停留在 task branch 上。
549
549
 
550
550
  如果 dispatch 後繼續在同一 session 做其他改動,會意外 commit 到 task branch,
551
551
  而不是 user branch。用戶在桌面端會看到 branch 不一致。
552
552
  severity: "must"
553
- rationale: "headless.js 是獨立進程,不會切換呼叫者的 git working tree"
554
- trigger: "Formation dispatch(headless.js)完成後,Ansuz 或 Agent 要做下一個改動之前"
553
+ rationale: "headless dispatch 是獨立進程(已退役,僅供歷史參照),不會切換呼叫者的 git working tree"
554
+ trigger: "legacy Formation dispatch 完成後,Ansuz 或 Agent 要做下一個改動之前"
555
555
  workflow:
556
556
  - step: 1
557
557
  action: "確認 dispatch 完成(task status = completed)"
@@ -568,5 +568,5 @@ rules:
568
568
  ❌ 多個 task 共用同一個 task branch(每個 task 應有自己的 branch)
569
569
  applies_to:
570
570
  - "Ansuz(PM dispatch 後繼續做其他工作)"
571
- - "任何使用 headless.js 的 Agent"
571
+ - "任何仍經 legacy Formation dispatch 的 Agent"
572
572
  - "用戶手動 dispatch 後"
@@ -9,8 +9,8 @@ rules:
9
9
  Single-file growth is the primary vector for SRP violations in this codebase.
10
10
  Enforce line-count thresholds to force extraction before files become unmaintainable.
11
11
  thresholds:
12
- warn: 2000
13
- hard_limit: 4000
12
+ warn: 1000
13
+ hard_limit: 2000
14
14
  scope:
15
15
  include:
16
16
  - "**/*.rs"
@@ -22,20 +22,32 @@ rules:
22
22
  - "**/target/**"
23
23
  - "**/node_modules/**"
24
24
  - "**/*.test.*"
25
+ - "**/*_test.*"
26
+ - "**/tests/**"
27
+ - "**/__tests__/**"
28
+ - "**/*.generated.*"
25
29
  - "**/*.spec.*"
26
30
  - "**/*.d.ts"
27
31
  - "**/generated/**"
28
32
  enforcement:
29
33
  on_task_design: |
30
- When designing a task that adds >50 LOC to a file already above warn threshold (2000 lines):
34
+ When designing a task that adds >50 LOC to a file already above warn threshold (1000 lines):
31
35
  1. Check current file size (wc -l or kenaz_lsp_symbols)
32
36
  2. If file > warn: acceptance criteria MUST include "extract [new responsibility] into separate module"
33
37
  3. If file > hard_limit: task MUST be split — one task for extraction, one for the new feature
34
38
  Ansuz must enforce this at task design time, not defer to the implementing agent.
35
39
  on_code_review: |
36
- Any PR that increases a file past hard_limit (4000 lines) without a paired extraction
40
+ Any PR that increases a file past hard_limit (2000 lines) without a paired extraction
37
41
  commit is a Blocker-level review finding.
38
42
  existing_debt: |
43
+ Decision 2026-10-10 (user, via Ansuz): warn 1000 / hard_limit 2000 lines for source
44
+ files; tests and generated files are excluded. Existing over-limit files are
45
+ GRANDFATHERED: dev/scripts/check-file-size.js only blocks growth of such a file (>50 LOC)
46
+ without a paired extraction (a new file in the same change), or a file newly crossing
47
+ hard_limit. Split by responsibility (module / component / hook) when you touch one.
48
+ Enforced in verify-and-land.js (file-size step, before merge) and the subagent
49
+ "Verify and report" prompt line. Without this yaml (user project) the script uses the
50
+ same defaults built in.
39
51
  Files currently above hard_limit are tracked as architecture debt.
40
52
  Extraction tasks should be created incrementally — do not attempt big-bang refactors.
41
53
  Follow the three-stage pattern: build-map → plan-split → minimal-patch.
@@ -33,7 +33,6 @@ rules:
33
33
  file: "dev/scripts/verify-target-dir.js"
34
34
  consumers:
35
35
  - "dev/scripts/run-rust-tests.js (defaults CARGO_TARGET_DIR when unset)"
36
- - ".formation/tools/runner.js (worktree dispatch env, 2 call sites)"
37
36
  - ".formation/tools/integration.js (in-lock re-verify env)"
38
37
  shell_usage: "CARGO_TARGET_DIR=$(node dev/scripts/verify-target-dir.js) cargo check --lib -p kenaz-dashboard"
39
38
 
@@ -90,19 +90,6 @@ invariants:
90
90
  # Parallel execution caps, locks, isolation boundaries
91
91
  # ============================================================================
92
92
 
93
- - name: max_parallel_dispatches
94
- category: concurrency
95
- formula: "concurrent_worktree_dispatches ≤ MAX_PARALLEL"
96
- bound: "MAX_PARALLEL = 5"
97
- enforcement: |
98
- Semaphore in formations.js dispatch loop — when count reaches
99
- MAX_PARALLEL, new dispatches wait. Logs "Max parallel dispatches
100
- reached (N/5), waiting...".
101
- violation_detection: |
102
- Count live worktree dispatches (.formation/worktrees/task_*/ with
103
- active child processes); if > 5, semaphore leaked.
104
- source: ".formation/tools/formations.js#L773-L810 (MAX_PARALLEL semaphore)"
105
-
106
93
  - name: worktree_isolation_boundary
107
94
  category: concurrency
108
95
  formula: |
@@ -111,17 +98,13 @@ invariants:
111
98
  repo paths)
112
99
  bound: "one worktree per active dispatch; no cross-worktree writes"
113
100
  enforcement: |
114
- runner.js spawn-time prompt injection — spawnClaude() and spawnBifrost()
115
- prepend "[WORKTREE] All file operations MUST use paths under:
116
- <worktreeDir>" (the CONCRETE path) to the final prompt whenever
117
- worktreeDir is set. Agent-tool dispatch additionally carries
118
- SUBAGENT_PREFIX rule 11 (WORKTREE FILE PATH RULE) in prompt.js.
119
- (TASK_1819: the former do.yaml worktree_path_rule field was a dead
120
- field — never rendered by prompt.js — and has been removed.)
101
+ Agent-tool dispatch carries SUBAGENT_PREFIX rule 11 (WORKTREE FILE
102
+ PATH RULE) in prompt.js. (The former runner.js spawn-time "[WORKTREE]"
103
+ injection was removed with the headless path, TASK_2401.)
121
104
  violation_detection: |
122
105
  Agent Edits a path under main repo root while cwd is a worktree →
123
106
  `git add -A` in worktree finds no changes. See FAIL-013.
124
- source: ".formation/tools/runner.js#L958,L1069 ([WORKTREE] prompt injection) + .formation/tools/prompt.js SUBAGENT_PREFIX rule 11"
107
+ source: ".formation/tools/prompt.js SUBAGENT_PREFIX rule 11"
125
108
  failure_witness: ".failure/agent-execution/FAIL-013-search-cwd-drift-and-case-mismatch.yaml"
126
109
 
127
110
  - name: per_project_dispatch_lock
@@ -129,13 +112,13 @@ invariants:
129
112
  formula: "per-project dispatch-lock serializes non-worktree dispatches"
130
113
  bound: "1 in-flight serial dispatch per project at a time"
131
114
  enforcement: |
132
- Dispatch-lock fallback in formations.js — when worktree parallel
133
- slot unavailable or task unsuitable for worktree, fall back to
134
- per-project serial lock.
115
+ Dispatch-lock fallback in dispatch.js (tryAcquireDispatchLock) — when a
116
+ worktree is unavailable or the task is unsuitable for one, fall back
117
+ to the per-project serial lock.
135
118
  violation_detection: |
136
119
  Two concurrent non-worktree dispatches writing the same project
137
120
  state (e.g., task-api moves). Lock contention visible in logs.
138
- source: ".formation/tools/formations.js#L773 (parallel semaphore + serial dispatch-lock fallback)"
121
+ source: ".formation/tools/dispatch.js (tryAcquireDispatchLock) + .formation/tools/utils.js (dispatchLockPath)"
139
122
 
140
123
  # ============================================================================
141
124
  # CATEGORY: cognitive
@@ -36,7 +36,7 @@
36
36
  身份: "Ansuz = 產品全生命週期引擎(PM + Lead 合一)"
37
37
  行為規則:
38
38
  ❌ 不 invoke 其他 Skill — 不切換身份
39
- ✅ 需要執行時 → Formation 系統 dispatch(Task tool + headless.js)
39
+ ✅ 需要執行時 → Agent tool subagent dispatch(dispatch.js --dispatch-mode=subagent)
40
40
  ✅ 本體負責:需求理解、任務設計、陣型判斷、調度、追蹤、回報
41
41
  ❌ 不親自寫程式碼 — 只做決策和調度
42
42
  ```
@@ -44,7 +44,7 @@
44
44
  **工作模式**:
45
45
  - **Ansuz = 指揮官 + 調度者**,不是執行者
46
46
  - 所有實作交給 Formation 系統的 agent(developer, backend, designer...)
47
- - 通過 `.formation/tools/headless.js` 啟動獨立 Claude CLI 進程執行任務
47
+ - 通過 `.formation/tools/dispatch.js` 組裝 prompt,再由 Agent tool subagent 執行任務
48
48
 
49
49
  ### 2. 事件驅動生命週期(7 Phases)
50
50
 
@@ -146,7 +146,7 @@ fi
146
146
 
147
147
  # 子專案範例
148
148
  node {CORE}/.collaboration/core/task-api.js --project-path {PROJECT_PATH} list
149
- node {CORE}/.formation/tools/headless.js --project-path {PROJECT_PATH} --skill=do --task=TASK_XXX
149
+ node {CORE}/.formation/tools/dispatch.js --project-path {PROJECT_PATH} --skill=do --task=TASK_XXX --dispatch-mode=subagent
150
150
  node {CORE}/.formation/tools/post-complete.js --task=TASK_XXX --project-path={PROJECT_PATH}
151
151
  ```
152
152
 
@@ -154,48 +154,26 @@ node {CORE}/.formation/tools/post-complete.js --task=TASK_XXX --project-path={PR
154
154
 
155
155
  ### 2. Dispatch 執行機制
156
156
 
157
- **工具**: `.formation/tools/headless.js`
158
- **方法**: Bash tool → `node headless.js` → 啟動獨立 Claude CLI 進程
157
+ **工具**: `.formation/tools/dispatch.js`
158
+ **方法**: Bash tool → `node dispatch.js --dispatch-mode=subagent` → 取回 `promptText` + `agentParams` → Agent tool 開 subagent
159
+ (headless CLI 進程、squad/legion 陣型已退役;只剩 unit/solo skill。)
159
160
 
160
161
  **CLI 命令格式**:
161
162
 
162
163
  ```bash
163
164
  # Unit 陣型
164
- node .formation/tools/headless.js --skill=do --task=TASK_XXX
165
+ node .formation/tools/dispatch.js --skill=do --task=TASK_XXX --dispatch-mode=subagent
165
166
 
166
167
  # Solo 陣型
167
- node .formation/tools/headless.js --formation=solo --skill=strategy --task=TASK_XXX
168
-
169
- # Squad 陣型
170
- node .formation/tools/headless.js --formation=squad --skill=blitz --task=TASK_XXX
171
-
172
- # Legion 陣型
173
- node .formation/tools/headless.js --formation=legion --skill=forge --task=TASK_XXX
174
- ```
175
-
176
- **並行模式**:
177
-
178
- ```javascript
179
- // 單任務(單一 Bash)
180
- Bash(run_in_background: true): node headless.js --task=TASK_148
181
-
182
- // 並行多任務(多個 Bash 同時啟動)
183
- Bash(run_in_background: true): node headless.js --task=TASK_148
184
- Bash(run_in_background: true): node headless.js --task=TASK_149
185
- Bash(run_in_background: true): node headless.js --task=TASK_150
186
-
187
- // 有依賴(順序執行)
188
- Bash: node headless.js --task=TASK_147
189
- // 等待完成後
190
- Bash: node headless.js --task=TASK_148
168
+ node .formation/tools/dispatch.js --formation=solo --skill=strategy --task=TASK_XXX --dispatch-mode=subagent
191
169
  ```
192
170
 
193
171
  **關鍵規則**:
194
172
  - ✅ 所有調度必須經過 `.collaboration` 任務系統
195
- - ✅ 流程:create → claim → dispatch(headless.js) → complete
196
- - ✅ 用 Bash tool 呼叫 headless.js,`run_in_background=true` 實現並行
173
+ - ✅ 流程:create → dispatch.js(subagent) → Agent tool → complete
174
+ - ✅ 有依賴 → 前一個完成再 dispatch 下一個;同一 project 序列化
197
175
  - ❌ 不跳過任務系統直接 dispatch
198
- - ❌ 不用 Task tool 開 subagent 繞過 Formation(Formation 有 prompt 組裝、log、principles 注入)
176
+ - ❌ 不手寫 subagent prompt — 用 dispatch.js 組裝(prompt 組裝、principles 注入)
199
177
 
200
178
  ### 3. Scope 安全保證
201
179
 
@@ -254,7 +232,7 @@ rigor: "medium" # low | medium | high
254
232
  dependencies: ["TASK_147"] # 真依賴(B 的輸入 = A 的輸出)
255
233
  scope_paths: # 修改的檔案/目錄(用於衝突檢測)
256
234
  - "CLAUDE.md"
257
- - ".formation/tools/runner.js"
235
+ - ".formation/tools/dispatch.js"
258
236
  requirements:
259
237
  - "新增 Lead Agent 開發流程章節"
260
238
  - "引用 .rule/pipeline/task-execution.yaml"
@@ -402,9 +380,8 @@ Step 2: 展示 Banner
402
380
  └── Ansuz.md # 斜線指令觸發點
403
381
 
404
382
  .formation/tools/
405
- ├── headless.js # Formation 調度核心
383
+ ├── dispatch.js # Formation 調度核心(subagent)
406
384
  ├── prompt.js # Prompt 組裝 + principles 注入
407
- ├── runner.js # 執行引擎
408
385
  ├── post-complete.js # 任務完成後自動化
409
386
  └── logger.js # 日誌系統
410
387
 
@@ -439,11 +416,9 @@ Phase 3: 建立任務
439
416
  - node task-api.js create ... (x3 任務)
440
417
 
441
418
  Phase 4: 陣型判斷 + Dispatch
442
- - 判斷:3 個獨立任務 → squad/blitz
419
+ - 判斷:3 個任務 → 各自 unit/do,序列化 dispatch
443
420
  - Dispatch:
444
- Bash(bg): node headless.js --formation=squad --skill=blitz --task=TASK_151
445
- Bash(bg): node headless.js --formation=squad --skill=blitz --task=TASK_152
446
- Bash(bg): node headless.js --formation=squad --skill=blitz --task=TASK_153
421
+ node dispatch.js --skill=do --task=TASK_151 --dispatch-mode=subagent(完成後再下一個)
447
422
 
448
423
  Phase 5: 結果處理
449
424
  - 驗證:檔案變更 + Git commit + 驗收標準
@@ -531,7 +506,7 @@ Phase 7: 回報
531
506
  ```yaml
532
507
  無衝突(可並行):
533
508
  TASK_A: scope_paths: ["CLAUDE.md"]
534
- TASK_B: scope_paths: [".formation/tools/runner.js"]
509
+ TASK_B: scope_paths: [".formation/tools/dispatch.js"]
535
510
 
536
511
  有衝突(必須順序執行):
537
512
  TASK_A: scope_paths: [".rule/pipeline/"]
@@ -601,7 +576,7 @@ Phase 7: 回報
601
576
  | 能力定義 | `.specialist/Ansuz/capability.yaml` | 需求分析、任務設計、陣型判斷等能力 |
602
577
  | 執行模式 | `.specialist/Ansuz/workflow.yaml` | 事件驅動 7 phases 工作流程 |
603
578
  | 共享詞彙 | `.specialist/Ansuz/knowledge.yaml` | Formation、任務系統、Agent 類型 |
604
- | Formation 系統 | `.formation/tools/` | headless.js, prompt.js, runner.js... |
579
+ | Formation 系統 | `.formation/tools/` | dispatch.js, prompt.js, post-complete.js... |
605
580
  | 任務系統 | `.collaboration/core/task-api.js` | 任務 API |
606
581
  | CLAUDE.md | `CLAUDE.md` | 專案級指令與工作流程 |
607
582
 
@@ -91,24 +91,17 @@ capabilities:
91
91
  # ─── 調度執行 ───
92
92
  # §dispatch
93
93
  dispatch:
94
- description: "使用 Formation 系統調度 agent 執行任務"
95
- tool: ".formation/tools/headless.js"
96
- method: "Bash tool → node headless.js → 啟動獨立 claude CLI 進程"
94
+ description: "使用 Formation prompt 組裝 + Agent tool subagent 執行任務(headless CLI 進程已退役)"
95
+ tool: ".formation/tools/dispatch.js"
96
+ method: "Bash tool → node dispatch.js --dispatch-mode=subagent → 取回 assembled prompt → Agent tool 開 subagent"
97
97
  rules:
98
98
  - "✅ 所有調度必須經過 .collaboration 任務系統"
99
- - "✅ create → claim → dispatch(headless.js) → complete"
100
- - "✅ 用 Bash tool 呼叫 headless.js,run_in_background=true 實現並行"
101
- - "⚠️ 同一 project 的 dispatch 必須序列化 — 一個 task 完成後再 dispatch 下一個"
102
- - "⚠️ 原因:多個 agent 共用同一 git working directory,並行會 git 衝突 + agent stall"
103
- - "⚠️ 有依賴鏈時:按依賴順序逐一 dispatch,等 TaskOutput/poll 確認前一個完成"
99
+ - "✅ create → dispatch.js(subagent) → Agent tool → complete"
100
+ - "⚠️ 同一 project 的 dispatch 必須序列化 — 多個 agent 共用同一 git working directory,並行會衝突"
101
+ - "⚠️ 有依賴鏈時:按依賴順序逐一 dispatch,確認前一個完成"
104
102
  - "❌ 不跳過任務系統直接 dispatch"
105
- - "❌ 不用 Task tool 開 subagent 繞過 Formation — Formation 有 prompt 組裝、log、principles 注入"
106
- - "❌ 禁止同時 dispatch 多個 task 到同一 project(即使它們沒有依賴關係)"
107
103
  commands:
108
- unit: "node .formation/tools/headless.js --skill=do --task=TASK_XXX"
109
- solo: "node .formation/tools/headless.js --formation=solo --skill=strategy --task=TASK_XXX"
110
- squad: "node .formation/tools/headless.js --formation=squad --skill=blitz --task=TASK_XXX"
111
- legion: "node .formation/tools/headless.js --formation=legion --skill=forge --task=TASK_XXX"
104
+ unit: "node .formation/tools/dispatch.js --skill=do --task=TASK_XXX --dispatch-mode=subagent"
112
105
 
113
106
  # ─── 結果追蹤 ───
114
107
  # §result_tracking
@@ -9,20 +9,13 @@ size_kb: 0.98
9
9
  evolution_status: reference
10
10
  ---
11
11
  formation_quick_ref:
12
- principle: "Lightest viable formation: unit > solo > squad > legion"
12
+ principle: "Only unit/solo skills are dispatchable (squad/legion retired); dispatch = Agent-tool subagent"
13
13
  unit:
14
14
  when: "Clear goal, single scope, direct execution"
15
- command: "node .formation/tools/headless.js --skill=do --task=TASK_XXX"
15
+ command: "node .formation/tools/dispatch.js --skill=do --task=TASK_XXX --dispatch-mode=subagent"
16
16
  solo:
17
17
  when: "Multi-perspective analysis needed"
18
- command: "node .formation/tools/headless.js --formation=solo --skill=strategy --task=TASK_XXX"
19
- squad:
20
- when: "Independent parallel subtasks possible"
21
- command: "node .formation/tools/headless.js --formation=squad --skill=blitz --task=TASK_XXX"
22
- legion:
23
- when: "Sequential dependencies or real-time coordination required"
24
- command: "node .formation/tools/headless.js --formation=legion --skill=forge --task=TASK_XXX"
18
+ command: "node .formation/tools/dispatch.js --formation=solo --skill=strategy --task=TASK_XXX --dispatch-mode=subagent"
25
19
  dispatch_rules:
26
- - "✅ Always background=true for parallel dispatch (Bash run_in_background=true)"
27
20
  - "⚠️ Same project: serialize dispatches — wait for each to complete (git conflicts)"
28
- - "✅ create → claim → dispatch(headless.js) → complete lifecycle"
21
+ - "✅ create → dispatch.js(subagent) → Agent tool → complete lifecycle"
@@ -54,13 +54,12 @@ mandates:
54
54
  - "Project pinning: when boot banner shows a `[pin:...]` line or session was started with --project-path, EVERY MCP call to synapse_*/hippocampus_*/cortex_* that accepts a projectPath param MUST pass projectPath=<pin>; every specialist tool CLI call (task-api, phase-gate, dispatch) MUST pass --project-path <pin>. Missing projectPath leaks memory into the wrong project graph."
55
55
  - "Verification policy (PARTIAL/UNVERIFIED/FIX-keeps-original/verified_uncommitted vs committed_unmerged vs merged): .specialist/Ansuz/VERIFY.md, loaded on demand at Phase 9."
56
56
  - "Landing mechanics for a verified card (branch check -> Rust/node suites -> optional negative control -> merge -> task-api complete -> mark-landed -> optional backend-restart wait): run .specialist/Ansuz/tools/verify-and-land.js instead of doing the same 10-20 tool calls by hand; it only executes, judgement (what to verify, scope) stays with you."
57
- - "WISHING WELL (every incarnation, TASK_1997/TASK_2040): file a wish with kenaz_wish_create — BEFORE working around the problem — whenever the Kenaz app itself blocks or degrades the work: a Kenaz tool/route fails or returns wrong data, a capability you needed is missing, a UX confusion cost you a turn, or a task ends failed/blocked because of the app. wish_type=bug|feature|question, one-line repro + what you expected, task_id when there is one. SCOPE (required on every wish): scope 'kenaz' — the Kenaz app/tools/dispatch itself blocked or degraded the work; scope 'project' — an idea or problem specific to the project being worked on (still sent; the mother may adopt it globally when it benefits Kenaz overall). FEATURE PROPOSALS: when the work reveals a capability Kenaz itself lacks that would help beyond this one task (not only blockers), you may file kenaz_wish_create with wish_type=feature, scope=kenaz. Content, in this order, within ~3000 chars: need (what you were trying to do), why current Kenaz falls short (evidence), proposed shape, who else benefits. Propose only - do not build Kenaz-core changes from a branch instance; the mother decides whether and how. MOTHER-SIDE TRIAGE: on the mother machine, field-report cards tagged needs-triage (blockers, project-scope wishes, and feature proposals alike) are judged at boot — adopt (rewrite as a global card stating the Kenaz-wide benefit, cancel the original with a pointer) or decline (cancel with a one-line reason); a feature proposal may be reshaped rather than built as proposed; never implement a project-specific ask verbatim in core. EXAMPLES — blocker: scope=kenaz, wish_type=bug, 'kenaz_dispatch timed out on a cross-project worktree with no partial log, TASK_2011'. feature proposal: scope=kenaz, wish_type=feature, 'need: retry one failed sub-task without rerunning the whole formation; current Kenaz only retries the full formation; shape: kenaz_dispatch --retry-task <id>; benefits every multi-task formation'. project-specific: scope=project, wish_type=feature, 'this project needs a CSV importer for onboarding — project-specific, flagging in case it generalizes'. At session close, anything unfinished BECAUSE of the app also becomes a wish. This is the channel that feeds the mother from every branch (TASK_1994); a session that hit an app problem and filed no wish has skipped a mandate, not saved time. Boot banner shows the open-wish count (and needs-triage count when >0) so you can see the channel is live."
57
+ - "WISHING WELL (every incarnation, TASK_1997/TASK_2040): file a wish with kenaz_wish_create — BEFORE working around the problem — whenever the Kenaz app itself blocks or degrades the work: a Kenaz tool/route fails or returns wrong data, a capability you needed is missing, a UX confusion cost you a turn, or a task ends failed/blocked because of the app. wish_type=bug|feature|question, one-line repro + what you expected, task_id when there is one. SCOPE (required on every wish): scope 'kenaz' — the Kenaz app/tools/dispatch itself blocked or degraded the work; scope 'project' — an idea or problem specific to the project being worked on (still sent; the mother may adopt it globally when it benefits Kenaz overall). FEATURE PROPOSALS: when the work reveals a capability Kenaz itself lacks that would help beyond this one task (not only blockers), you may file kenaz_wish_create with wish_type=feature, scope=kenaz. Content, in this order, within ~3000 chars: need (what you were trying to do), why current Kenaz falls short (evidence), proposed shape, who else benefits. Propose only - do not build Kenaz-core changes from a branch instance; the mother decides whether and how. MOTHER-SIDE TRIAGE: on the mother machine, field-report cards tagged needs-triage (blockers, project-scope wishes, and feature proposals alike) are judged at boot — adopt (rewrite as a global card stating the Kenaz-wide benefit, cancel the original with a pointer) or decline (cancel with a one-line reason); a feature proposal may be reshaped rather than built as proposed; never implement a project-specific ask verbatim in core. EXAMPLES — blocker: scope=kenaz, wish_type=bug, 'dispatch.js timed out on a cross-project worktree with no partial log, TASK_2011'. feature proposal: scope=kenaz, wish_type=feature, 'need: retry one failed sub-task without rerunning the whole formation; current Kenaz only retries the full formation; shape: dispatch.js --retry-task <id>; benefits every multi-task formation'. project-specific: scope=project, wish_type=feature, 'this project needs a CSV importer for onboarding — project-specific, flagging in case it generalizes'. At session close, anything unfinished BECAUSE of the app also becomes a wish. This is the channel that feeds the mother from every branch (TASK_1994); a session that hit an app problem and filed no wish has skipped a mandate, not saved time. Boot banner shows the open-wish count (and needs-triage count when >0) so you can see the channel is live."
58
58
  dispatch_routing:
59
59
  paths:
60
60
  subagent: "Agent tool within interactive session — stays on subscription billing, results return directly, no log polling. Use: short/medium tasks (rigor:low/medium, same-project or trivial scope)."
61
- formation: "kenaz_dispatch / claude -p — independent process, own log, subscription billing (the June 15 credit-pool split was rescinded, user 2026-10-02). Use: long tasks (rigor:high, multi-hour), cross-project (different cwd needed), or when no interactive session."
62
- how: "Run `node .formation/tools/dispatch.js --skill=do --task=TASK_XXX --dispatch-mode=subagent` (include the project pin when present). Read the assembled prompt from top-level promptText and pass it as the Agent tool prompt alongside agentParams. Top-level isolation is orchestration metadata, NOT an Agent tool argument: cross-project worktreePath specifies the target cwd; serialLock specifies required serialization and release. agentParams.isolation is present only for the native same-project worktree route. agentParams.model is ALREADY translated to the Agent tool enum (sonnet|opus|haiku|fable, TASK_1815) — pass it verbatim to a compatible Agent tool, do NOT re-translate or improvise. LIMIT: the Agent tool expresses family names ONLY, so dashboard version/speed picks (claude-opus-5[1m] vs claude-opus-4-8[1m], [1m] modifiers) all degrade to the family name; the payload's modelTranslation {requested, agentToolModel, granularityLoss} shows exactly what was lost — if the exact version matters, use the formation path (kenaz_dispatch → headless.js --dispatch-model accepts full model ids). Unmappable dispatchModel values make dispatch.js exit 1 loudly; never substitute a model silently. For formation path, use kenaz_dispatch MCP as before."
63
- mandate: "ALWAYS evaluate both paths before dispatching. Check .kenaz/dashboard-models.json dispatchMode for the user override (set from the Dashboard Model Selection panel > Dispatch Routing, persisted by dashboard_settings.rs since TASK_1814): 'formation' or 'subagent' = hard override, route ALL dispatches through that path; 'auto' = no forced route, apply the heuristic (subagent when within interactive session unless task requires cross-project cwd or is rigor:high, else formation); key missing = dispatch.js falls back to 'formation' (TASK_1813 contract) — treat missing the same as no override and apply the heuristic at the Ansuz level."
61
+ how: "Run `node .formation/tools/dispatch.js --skill=do --task=TASK_XXX --dispatch-mode=subagent` (include the project pin when present). Read the assembled prompt from top-level promptText and pass it as the Agent tool prompt alongside agentParams. Top-level isolation is orchestration metadata, NOT an Agent tool argument: cross-project worktreePath specifies the target cwd; serialLock specifies required serialization and release. agentParams.isolation is present only for the native same-project worktree route. agentParams.model is ALREADY translated to the Agent tool enum (sonnet|opus|haiku|fable, TASK_1815) — pass it verbatim to a compatible Agent tool, do NOT re-translate or improvise. LIMIT: the Agent tool expresses family names ONLY, so dashboard version/speed picks (claude-opus-5[1m] vs claude-opus-4-8[1m], [1m] modifiers) all degrade to the family name; the payload's modelTranslation {requested, agentToolModel, granularityLoss} shows exactly what was lost. Unmappable dispatchModel values make dispatch.js exit 1 loudly; never substitute a model silently."
62
+ mandate: "Subagent is the only dispatch path (Formation headless/kenaz_dispatch retired, TASK_2359/2401/2402). dispatchMode in .kenaz/dashboard-models.json no longer selects a route."
64
63
  exo_cortex:
65
64
  opening_move: synapse_recall + kenaz_lsp_callers
66
65
  anti_pattern: "Bash grep/rg/find for source archaeology — prefer Grep/Glob (and kenaz_lsp_symbols for symbols); the hook nudge is downstream of you knowing the tool exists"
@@ -339,7 +339,7 @@ cross_model:
339
339
  - "ME-001 governance (immutable rules cannot be deleted) must hold"
340
340
  - "AskUserQuestion tool must be available and used for all user-response questions"
341
341
  - "Phase sequence 0-11 must be respected; phase-gate.js must be callable"
342
- - "Formation dispatch must work (headless.js must be callable)"
342
+ - "Subagent dispatch must work (.formation/tools/dispatch.js --dispatch-mode=subagent must be callable)"
343
343
  - "Task-api.js must be callable for task lifecycle management"
344
344
 
345
345
  acceptable_substrate_drift:
@@ -34,9 +34,8 @@ formation:
34
34
  - "Squad ≠ 開會。Squad = 多個獨立 subagent 並行,零溝通"
35
35
  - "Legion ≠ 大號 Squad。Legion = 持久化團隊,成員間即時通訊"
36
36
  key_files:
37
- headless: ".formation/tools/headless.js"
37
+ dispatch: ".formation/tools/dispatch.js"
38
38
  prompt: ".formation/tools/prompt.js"
39
- runner: ".formation/tools/runner.js"
40
39
  logs: ".formation/logs/{formation}/"
41
40
  post_complete: ".formation/tools/post-complete.js"
42
41
  research_notes:
@@ -49,12 +48,10 @@ formation:
49
48
  - "Better mapping: represent agent-role reliability and coordination cost as factorised hidden states, then evaluate a whole formation against task context."
50
49
  - "Keep a collective dispatch objective separate from per-agent or per-symbol EFE because ensemble-level EFE is not guaranteed to be aggregate-optimal."
51
50
  dispatch_mechanism:
52
- how: "Bash tool → node headless.js → runner.js → spawn claude CLI 獨立進程"
53
- why: "headless.js 統一處理 prompt 組裝(prompt.js)、principles 注入、log 管理"
54
- anti_pattern: "❌ 禁止用 Task tool 直接開 subagent — 繞過 Formation 會失去 prompt 組裝和 log"
55
- parallel: "多個 Bash(run_in_background=true) 同時啟動 → 真正的進程級並行"
56
- max_parallel: 5 # 同時最多 5 個 headless 進程,超過排隊等空位
57
- monitoring: "讀 .formation/logs/{formation}/ 下的 log 確認結果"
51
+ how: "Bash tool → node dispatch.js --dispatch-mode=subagent → Agent tool subagent"
52
+ why: "dispatch.js 統一處理 prompt 組裝(prompt.js)、principles 注入"
53
+ anti_pattern: "❌ 不手寫 subagent prompt — 用 dispatch.js 組裝"
54
+ monitoring: "Agent tool 結果直接返回"
58
55
 
59
56
  # ═══════════════════════════════════════
60
57
  # 任務系統
@@ -89,10 +86,10 @@ task_system:
89
86
  project_yaml: ".kenaz/project.yaml"
90
87
  rule: |
91
88
  子專案的任務存在 .kenaz/tasks/,不是 .collaboration/tasks/。
92
- 所有 task-api.js、headless.js、post-complete.js 呼叫都必須加 --project-path。
89
+ 所有 task-api.js、dispatch.js、post-complete.js 呼叫都必須加 --project-path。
93
90
  cli_pattern:
94
91
  task_api: "node {CORE}/.collaboration/core/task-api.js --project-path {PROJECT_PATH} <command>"
95
- headless: "node {CORE}/.formation/tools/headless.js --project-path {PROJECT_PATH} --skill=do --task=TASK_XXX"
92
+ dispatch: "node {CORE}/.formation/tools/dispatch.js --project-path {PROJECT_PATH} --skill=do --task=TASK_XXX --dispatch-mode=subagent"
96
93
  post_complete: "node {CORE}/.formation/tools/post-complete.js --task=TASK_XXX --project-path={PROJECT_PATH}"
97
94
 
98
95
  # ═══════════════════════════════════════
@@ -35,7 +35,7 @@ allow:
35
35
  - "讀取專案內所有原始碼"
36
36
  - "讀寫 specialist/Ansuz/ 下所有檔案"
37
37
  - "使用 .collaboration/core/task-api.js 管理任務"
38
- - "使用 .formation/tools/headless.js 調度執行"
38
+ - "使用 .formation/tools/dispatch.js --dispatch-mode=subagent 調度執行"
39
39
  - "使用 .formation/tools/post-complete.js 完成後自動化"
40
40
  - "執行 git 操作(建立分支、commit、merge 到用戶分支)"
41
41
 
@@ -51,7 +51,7 @@ identity:
51
51
  symbol: "ᚨ — 神諭符文:理解意圖,傳遞給執行層"
52
52
  rules:
53
53
  - "❌ 不 invoke 其他 Skill — 不切換身份"
54
- - "✅ 需要執行時 → 用 Formation 系統 dispatch(Task tool + headless.js)"
54
+ - "✅ 需要執行時 → 用 Agent tool subagent dispatch(dispatch.js --dispatch-mode=subagent)"
55
55
  - "✅ Ansuz 本體負責:需求理解、任務設計、陣型判斷、調度、追蹤、回報"
56
56
  - "❌ 不親自寫程式碼 — 只做決策和調度"
57
57
  - "✅ Task-First — 預設先建立任務卡再 dispatch,除非是極簡單的單檔修改(< 20 行)"