kenaz 0.3.3 → 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 (451) hide show
  1. package/core/.agents/skills/typesafe-ai/LICENSE +21 -21
  2. package/core/.agents/skills/typesafe-ai/SKILL.md +149 -149
  3. package/core/.claude/commands/Ansuz.md +15 -2
  4. package/core/.claude/hooks/__tests__/task-2395-project-registration.test.js +84 -0
  5. package/core/.claude/hooks/handoff-staleness.js +2 -2
  6. package/core/.claude/hooks/session-init.ps1 +49 -49
  7. package/core/.claude/hooks/turn-context.js +50 -2
  8. package/core/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -26
  9. package/core/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -97
  10. package/core/.claude/skills/ui-ux-pro-max/data/icons.csv +101 -101
  11. package/core/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -31
  12. package/core/.claude/skills/ui-ux-pro-max/data/products.csv +96 -96
  13. package/core/.claude/skills/ui-ux-pro-max/data/react-performance.csv +45 -45
  14. package/core/.claude/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -54
  15. package/core/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -53
  16. package/core/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -56
  17. package/core/.claude/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -53
  18. package/core/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -53
  19. package/core/.claude/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -51
  20. package/core/.claude/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -59
  21. package/core/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -52
  22. package/core/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -54
  23. package/core/.claude/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -61
  24. package/core/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -54
  25. package/core/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -51
  26. package/core/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -50
  27. package/core/.claude/skills/ui-ux-pro-max/data/styles.csv +68 -68
  28. package/core/.claude/skills/ui-ux-pro-max/data/typography.csv +57 -57
  29. package/core/.claude/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -101
  30. package/core/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +99 -99
  31. package/core/.claude/skills/ui-ux-pro-max/data/web-interface.csv +31 -31
  32. package/core/.claude/skills/ui-ux-pro-max/scripts/core.py +253 -253
  33. package/core/.claude/skills/ui-ux-pro-max/scripts/design_system.py +1067 -1067
  34. package/core/.claude/skills/ui-ux-pro-max/scripts/search.py +106 -106
  35. package/core/.collaboration/core/task-api.js +1 -4
  36. package/core/.formation/README.md +21 -146
  37. package/core/.formation/bifrost/codex-doctor.js +37 -2
  38. package/core/.formation/bifrost/codex-doctor.test.js +18 -0
  39. package/core/.formation/bifrost/launch.js +1 -1
  40. package/core/.formation/bifrost/openai/codex-hook-shim.js +6 -0
  41. package/core/.formation/bifrost/openai/codex-hook-shim.test.js +11 -1
  42. package/core/.formation/bifrost/setup-codex.js +3 -0
  43. package/core/.formation/native/claude/README.md +1 -1
  44. package/core/.formation/role-map.yaml +3 -3
  45. package/core/.formation/spec/task_operations.parity.test.js +0 -1
  46. package/core/.formation/spec/task_operations.yaml +3 -3
  47. package/core/.formation/tests/hello.txt +2 -2
  48. package/core/.formation/tools/__tests__/subagent-bifrost-guard.test.js +4 -4
  49. package/core/.formation/tools/dashboard-models-chain.test.js +0 -6
  50. package/core/.formation/tools/dispatch-detail/cross-project-worktree.md +1 -9
  51. package/core/.formation/tools/dispatch-detail/worktree-subagent-mode.md +2 -2
  52. package/core/.formation/tools/dispatch.js +33 -554
  53. package/core/.formation/tools/mcp-allowlist.test.js +0 -1
  54. package/core/.formation/tools/post-complete/task2345.test.js +0 -5
  55. package/core/.formation/tools/pre-dispatch-snapshot.test.js +0 -100
  56. package/core/.formation/tools/prompt.js +33 -1623
  57. package/core/.formation/tools/prompt.test.js +26 -594
  58. package/core/.formation/unit/do.yaml +2 -4
  59. package/core/.kenaz/knowledge/ladybug_2c51da1e_shadow_file_frame_group.patch +163 -163
  60. package/core/.kenaz/knowledge/ladybug_30cf85690e_shadow_page_read_during_checkpoint.patch +393 -393
  61. package/core/.kenaz/knowledge/ladybug_7a17858d3a_node_group_delete_lock.patch +102 -102
  62. package/core/.kenaz/knowledge/ladybug_926a0ba1_column_checkpoint.patch +412 -412
  63. package/core/.kenaz/knowledge/ladybug_a346c4c4b6_pk_index_header_pages.patch +144 -144
  64. package/core/.kenaz/knowledge/ladybug_ba5f38815b_csr_node_group.patch +86 -86
  65. package/core/.kenaz/muninn_episode_write.ps1 +17 -17
  66. package/core/.kenaz/specialist/field_report_watermark.json +1 -1
  67. package/core/.kenaz/specialist/memory_guard_fired.json +1 -1
  68. package/core/.kenaz/specialist/memory_guard_trigger_cache.json +1 -1
  69. package/core/.kenaz/specialist/t1777_probe.txt +1 -1
  70. package/core/.matrix/index.matrix +5 -5
  71. package/core/.rule/general/agent-workflow.yaml +4 -4
  72. package/core/.rule/general/file-size-threshold.yaml +16 -4
  73. package/core/.rule/general/rust-verify-target.yaml +0 -1
  74. package/core/.rule/general/system-invariants.yaml +8 -25
  75. package/core/.specialist/Ansuz/SYSTEM_DOC.md +17 -42
  76. package/core/.specialist/Ansuz/capability.yaml +7 -14
  77. package/core/.specialist/Ansuz/concepts/formation_quick_ref.yaml +4 -11
  78. package/core/.specialist/Ansuz/elixir.yaml +3 -4
  79. package/core/.specialist/Ansuz/identity_contract.yaml +1 -1
  80. package/core/.specialist/Ansuz/knowledge.yaml +7 -10
  81. package/core/.specialist/Ansuz/rule.yaml +2 -2
  82. package/core/.specialist/Ansuz/tools/verify-and-land.js +22 -0
  83. package/core/.specialist/Ansuz/tools/verify-and-land.test.js +59 -5
  84. package/core/.specialist/Ansuz/workflow.yaml +7 -11
  85. package/core/.specialist/Forseti/core.yaml +1 -1
  86. package/core/.specialist/Heimdall/SKILL.md +1 -1
  87. package/core/.specialist/Heimdall/core.yaml +2 -2
  88. package/core/.specialist/Huginn/rule.yaml +1 -1
  89. package/core/.specialist/tools/README.md +2 -2
  90. package/core/.specialist/tools/__tests__/freya-cli-contract.test.js +0 -1
  91. package/core/.system/collaboration/README.md +1 -1
  92. package/core/.system/formation/LEGACY_SKILLS.md +2 -0
  93. package/core/.system/formation/POST_COMPLETE.md +2 -0
  94. package/core/.system/formation/README.md +2 -0
  95. package/core/KENAZ_CORE_VERSION +1 -1
  96. package/core/dev/ANSUZ_STARTUP_INJECTION_REVIEW.md +355 -355
  97. package/core/dev/JEV_INTEGRATION_PLAN.md +189 -189
  98. package/core/dev/design/matrix_format_EXAMPLE_memory.matrix +13 -13
  99. package/core/dev/design/matrix_format_EXAMPLE_project_status.matrix +9 -9
  100. package/core/dev/design/matrix_format_EXAMPLE_tasks.matrix +14 -14
  101. package/core/dev/design/matrix_v2_EXAMPLE.matrix +67 -67
  102. package/core/dev/jev-eval/ACTIVE_CORRECTION.md +52 -52
  103. package/core/dev/jev-eval/CONTEXT_REVIEW.md +31 -31
  104. package/core/dev/jev-eval/EVALUATION_STATUS.md +39 -39
  105. package/core/dev/jev-eval/HISTORICAL_CORPUS.md +33 -33
  106. package/core/dev/jev-eval/README.md +99 -99
  107. package/core/dev/jev-eval/THALAMUS_RANKING.md +58 -58
  108. package/core/dev/jev-eval/reports/annotation-pilot-2026-09-20.json +32 -32
  109. package/core/dev/jev-eval/reports/context-review-2026-09-20.json +48 -48
  110. package/core/dev/jev-eval/reports/history-corpus-2026-09-20.json +52 -52
  111. package/core/dev/pipeline/SELF_ENHANCEMENT_STATUS.json.bak +47 -47
  112. package/core/dev/pipeline/tasks/.gitkeep +1 -1
  113. package/core/dev/pipeline/test_results/TASK_TEST_PARALLEL_002_result.txt +27 -27
  114. package/core/dev/scripts/check-file-size.js +44 -17
  115. package/core/dev/scripts/init_git.sh +0 -0
  116. package/core/dev/scripts/init_project.sh +0 -0
  117. package/core/dev/scripts/legacy/dev_agent_scheduler.ps1.bak +536 -536
  118. package/core/dev/scripts/legacy/multi_agent_scheduler.ps1.bak +1978 -1978
  119. package/core/dev/scripts/matrix/src/cli/commands/list-tags.ts.wip +140 -140
  120. package/core/dev/scripts/matrix/src/cli/commands/manage-tags.ts.wip +173 -173
  121. package/core/dev/scripts/matrix/src/cli/commands/stats.ts.wip +168 -168
  122. package/core/dev/scripts/matrix/src/cli/commands/tokens.ts.wip +126 -126
  123. package/core/dev/scripts/multi_agent_scheduler.sh +0 -0
  124. package/core/dev/scripts/run_once.sh +0 -0
  125. package/core/dev/scripts/setup_mcp.sh +0 -0
  126. package/core/dev/scripts/setup_rag.sh +0 -0
  127. package/core/dev/scripts/start_dev_agent.sh +0 -0
  128. package/core/dev/scripts/start_multi_agent.sh +0 -0
  129. package/core/dev/scripts/sync_task_queue.sh +0 -0
  130. package/core/k-cli/README.md +1 -94
  131. package/core/k-cli/test/TEST_SPEC.md +5 -22
  132. package/core/k-cli/test/run.sh +1 -67
  133. package/core/plugins/kenaz/commands/Amelia.md +1 -1
  134. package/core/plugins/kenaz/commands/Ansuz.md +16 -3
  135. package/core/plugins/kenaz/scripts/kenaz-mode.js +11 -1
  136. package/core/plugins/kenaz/scripts/sync.js +6 -2
  137. package/core/plugins/kenaz/scripts/ygg.js +13 -1
  138. package/core/plugins/kenaz/skills/product_manager/SKILL.md +12 -12
  139. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/charts.csv +26 -26
  140. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/colors.csv +97 -97
  141. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/icons.csv +101 -101
  142. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/landing.csv +31 -31
  143. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/products.csv +96 -96
  144. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/react-performance.csv +45 -45
  145. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -54
  146. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -53
  147. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -56
  148. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -53
  149. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -53
  150. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -51
  151. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -59
  152. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -52
  153. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/react.csv +54 -54
  154. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -61
  155. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -54
  156. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -51
  157. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -50
  158. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/styles.csv +68 -68
  159. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/typography.csv +57 -57
  160. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -101
  161. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/ux-guidelines.csv +99 -99
  162. package/core/plugins/kenaz/skills/ui-ux-pro-max/data/web-interface.csv +31 -31
  163. package/core/plugins/kenaz/skills/ui-ux-pro-max/scripts/core.py +253 -253
  164. package/core/plugins/kenaz/skills/ui-ux-pro-max/scripts/design_system.py +1067 -1067
  165. package/core/plugins/kenaz/skills/ui-ux-pro-max/scripts/search.py +106 -106
  166. package/lib/cli.js +11 -4
  167. package/package.json +11 -7
  168. package/vendor/install-kenaz-cli.js +18 -7
  169. package/core/.formation/legion/forge.yaml +0 -299
  170. package/core/.formation/legion/hive.yaml +0 -238
  171. package/core/.formation/squad/blitz.yaml +0 -192
  172. package/core/.formation/squad/strategy-tribunal.yaml +0 -354
  173. package/core/.formation/squad/strategy.yaml +0 -349
  174. package/core/.formation/squad/sweep.yaml +0 -208
  175. package/core/.formation/tools/broadcast.js +0 -103
  176. package/core/.formation/tools/dispatch-dryrun.test.js +0 -54
  177. package/core/.formation/tools/dispatch-notifier.js +0 -84
  178. package/core/.formation/tools/formations.js +0 -1203
  179. package/core/.formation/tools/headless-branch-recovery.test.js +0 -64
  180. package/core/.formation/tools/headless-dryrun.test.js +0 -79
  181. package/core/.formation/tools/headless-v2.js +0 -659
  182. package/core/.formation/tools/headless.js +0 -1071
  183. package/core/.formation/tools/legacy-scheduler.js +0 -1994
  184. package/core/.formation/tools/logger.js +0 -273
  185. package/core/.formation/tools/memory-loader.js +0 -422
  186. package/core/.formation/tools/process-state.js +0 -33
  187. package/core/.formation/tools/remote-conflict.js +0 -98
  188. package/core/.formation/tools/runner.js +0 -1332
  189. package/core/.formation/tools/task-utils.js +0 -344
  190. package/core/.formation/tools/task2028-headless-worktree.test.js +0 -112
  191. package/core/.formation/tools/tests/headless-mcp-args-plumbing.test.js +0 -123
  192. package/core/.formation/tools/worktree-leak.test.js +0 -162
  193. package/core/k-cli/dist/alaya/run.js +0 -91
  194. package/core/k-cli/dist/core/output.js +0 -24
  195. package/core/k-cli/dist/core/process.js +0 -38
  196. package/core/k-cli/dist/core/project.js +0 -80
  197. package/core/k-cli/dist/core/spawn.js +0 -268
  198. package/core/k-cli/dist/core/types.js +0 -2
  199. package/core/k-cli/dist/dashboard/update.js +0 -53
  200. package/core/k-cli/dist/dashboard/version.js +0 -20
  201. package/core/k-cli/dist/dispatch/run.js +0 -241
  202. package/core/k-cli/dist/dispatch/status.js +0 -103
  203. package/core/k-cli/dist/index.js +0 -283
  204. package/core/k-cli/dist/project/initialize.js +0 -301
  205. package/core/k-cli/dist/project/install.js +0 -89
  206. package/core/k-cli/dist/project/status.js +0 -138
  207. package/core/k-cli/dist/specialist/list.js +0 -100
  208. package/core/k-cli/dist/specialist/run.js +0 -93
  209. package/core/k-cli/dist/task/create.js +0 -194
  210. package/core/k-cli/dist/task/delete.js +0 -98
  211. package/core/k-cli/dist/task/get.js +0 -55
  212. package/core/k-cli/dist/task/list.js +0 -58
  213. package/core/k-cli/dist/task/submit.js +0 -156
  214. package/core/k-cli/dist/ygg/run.js +0 -51
  215. package/core/k-cli/node_modules/.bin/yaml +0 -16
  216. package/core/k-cli/node_modules/.bin/yaml.cmd +0 -17
  217. package/core/k-cli/node_modules/.bin/yaml.ps1 +0 -28
  218. package/core/k-cli/node_modules/.package-lock.json +0 -23
  219. package/core/k-cli/node_modules/yaml/LICENSE +0 -13
  220. package/core/k-cli/node_modules/yaml/README.md +0 -172
  221. package/core/k-cli/node_modules/yaml/bin.mjs +0 -11
  222. package/core/k-cli/node_modules/yaml/browser/dist/compose/compose-collection.js +0 -88
  223. package/core/k-cli/node_modules/yaml/browser/dist/compose/compose-doc.js +0 -43
  224. package/core/k-cli/node_modules/yaml/browser/dist/compose/compose-node.js +0 -109
  225. package/core/k-cli/node_modules/yaml/browser/dist/compose/compose-scalar.js +0 -86
  226. package/core/k-cli/node_modules/yaml/browser/dist/compose/composer.js +0 -217
  227. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-block-map.js +0 -115
  228. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-block-scalar.js +0 -198
  229. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-block-seq.js +0 -49
  230. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-end.js +0 -37
  231. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-flow-collection.js +0 -207
  232. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +0 -223
  233. package/core/k-cli/node_modules/yaml/browser/dist/compose/resolve-props.js +0 -146
  234. package/core/k-cli/node_modules/yaml/browser/dist/compose/util-contains-newline.js +0 -34
  235. package/core/k-cli/node_modules/yaml/browser/dist/compose/util-empty-scalar-position.js +0 -26
  236. package/core/k-cli/node_modules/yaml/browser/dist/compose/util-flow-indent-check.js +0 -15
  237. package/core/k-cli/node_modules/yaml/browser/dist/compose/util-map-includes.js +0 -13
  238. package/core/k-cli/node_modules/yaml/browser/dist/doc/Document.js +0 -335
  239. package/core/k-cli/node_modules/yaml/browser/dist/doc/anchors.js +0 -71
  240. package/core/k-cli/node_modules/yaml/browser/dist/doc/applyReviver.js +0 -55
  241. package/core/k-cli/node_modules/yaml/browser/dist/doc/createNode.js +0 -88
  242. package/core/k-cli/node_modules/yaml/browser/dist/doc/directives.js +0 -176
  243. package/core/k-cli/node_modules/yaml/browser/dist/errors.js +0 -57
  244. package/core/k-cli/node_modules/yaml/browser/dist/index.js +0 -17
  245. package/core/k-cli/node_modules/yaml/browser/dist/log.js +0 -11
  246. package/core/k-cli/node_modules/yaml/browser/dist/nodes/Alias.js +0 -114
  247. package/core/k-cli/node_modules/yaml/browser/dist/nodes/Collection.js +0 -147
  248. package/core/k-cli/node_modules/yaml/browser/dist/nodes/Node.js +0 -38
  249. package/core/k-cli/node_modules/yaml/browser/dist/nodes/Pair.js +0 -36
  250. package/core/k-cli/node_modules/yaml/browser/dist/nodes/Scalar.js +0 -24
  251. package/core/k-cli/node_modules/yaml/browser/dist/nodes/YAMLMap.js +0 -144
  252. package/core/k-cli/node_modules/yaml/browser/dist/nodes/YAMLSeq.js +0 -113
  253. package/core/k-cli/node_modules/yaml/browser/dist/nodes/addPairToJSMap.js +0 -63
  254. package/core/k-cli/node_modules/yaml/browser/dist/nodes/identity.js +0 -36
  255. package/core/k-cli/node_modules/yaml/browser/dist/nodes/toJS.js +0 -37
  256. package/core/k-cli/node_modules/yaml/browser/dist/parse/cst-scalar.js +0 -214
  257. package/core/k-cli/node_modules/yaml/browser/dist/parse/cst-stringify.js +0 -61
  258. package/core/k-cli/node_modules/yaml/browser/dist/parse/cst-visit.js +0 -97
  259. package/core/k-cli/node_modules/yaml/browser/dist/parse/cst.js +0 -98
  260. package/core/k-cli/node_modules/yaml/browser/dist/parse/lexer.js +0 -717
  261. package/core/k-cli/node_modules/yaml/browser/dist/parse/line-counter.js +0 -39
  262. package/core/k-cli/node_modules/yaml/browser/dist/parse/parser.js +0 -967
  263. package/core/k-cli/node_modules/yaml/browser/dist/public-api.js +0 -102
  264. package/core/k-cli/node_modules/yaml/browser/dist/schema/Schema.js +0 -37
  265. package/core/k-cli/node_modules/yaml/browser/dist/schema/common/map.js +0 -17
  266. package/core/k-cli/node_modules/yaml/browser/dist/schema/common/null.js +0 -15
  267. package/core/k-cli/node_modules/yaml/browser/dist/schema/common/seq.js +0 -17
  268. package/core/k-cli/node_modules/yaml/browser/dist/schema/common/string.js +0 -14
  269. package/core/k-cli/node_modules/yaml/browser/dist/schema/core/bool.js +0 -19
  270. package/core/k-cli/node_modules/yaml/browser/dist/schema/core/float.js +0 -43
  271. package/core/k-cli/node_modules/yaml/browser/dist/schema/core/int.js +0 -38
  272. package/core/k-cli/node_modules/yaml/browser/dist/schema/core/schema.js +0 -23
  273. package/core/k-cli/node_modules/yaml/browser/dist/schema/json/schema.js +0 -62
  274. package/core/k-cli/node_modules/yaml/browser/dist/schema/tags.js +0 -96
  275. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/binary.js +0 -58
  276. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/bool.js +0 -26
  277. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/float.js +0 -46
  278. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/int.js +0 -71
  279. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/merge.js +0 -64
  280. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/omap.js +0 -74
  281. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/pairs.js +0 -78
  282. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/schema.js +0 -39
  283. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/set.js +0 -93
  284. package/core/k-cli/node_modules/yaml/browser/dist/schema/yaml-1.1/timestamp.js +0 -101
  285. package/core/k-cli/node_modules/yaml/browser/dist/stringify/foldFlowLines.js +0 -146
  286. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringify.js +0 -129
  287. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyCollection.js +0 -153
  288. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyComment.js +0 -20
  289. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyDocument.js +0 -85
  290. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyNumber.js +0 -24
  291. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyPair.js +0 -150
  292. package/core/k-cli/node_modules/yaml/browser/dist/stringify/stringifyString.js +0 -336
  293. package/core/k-cli/node_modules/yaml/browser/dist/util.js +0 -11
  294. package/core/k-cli/node_modules/yaml/browser/dist/visit.js +0 -233
  295. package/core/k-cli/node_modules/yaml/browser/index.js +0 -5
  296. package/core/k-cli/node_modules/yaml/browser/package.json +0 -3
  297. package/core/k-cli/node_modules/yaml/dist/cli.d.ts +0 -8
  298. package/core/k-cli/node_modules/yaml/dist/cli.mjs +0 -201
  299. package/core/k-cli/node_modules/yaml/dist/compose/compose-collection.d.ts +0 -11
  300. package/core/k-cli/node_modules/yaml/dist/compose/compose-collection.js +0 -90
  301. package/core/k-cli/node_modules/yaml/dist/compose/compose-doc.d.ts +0 -7
  302. package/core/k-cli/node_modules/yaml/dist/compose/compose-doc.js +0 -45
  303. package/core/k-cli/node_modules/yaml/dist/compose/compose-node.d.ts +0 -29
  304. package/core/k-cli/node_modules/yaml/dist/compose/compose-node.js +0 -112
  305. package/core/k-cli/node_modules/yaml/dist/compose/compose-scalar.d.ts +0 -5
  306. package/core/k-cli/node_modules/yaml/dist/compose/compose-scalar.js +0 -88
  307. package/core/k-cli/node_modules/yaml/dist/compose/composer.d.ts +0 -63
  308. package/core/k-cli/node_modules/yaml/dist/compose/composer.js +0 -222
  309. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-map.d.ts +0 -6
  310. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-map.js +0 -117
  311. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-scalar.d.ts +0 -11
  312. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-scalar.js +0 -200
  313. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-seq.d.ts +0 -6
  314. package/core/k-cli/node_modules/yaml/dist/compose/resolve-block-seq.js +0 -51
  315. package/core/k-cli/node_modules/yaml/dist/compose/resolve-end.d.ts +0 -6
  316. package/core/k-cli/node_modules/yaml/dist/compose/resolve-end.js +0 -39
  317. package/core/k-cli/node_modules/yaml/dist/compose/resolve-flow-collection.d.ts +0 -7
  318. package/core/k-cli/node_modules/yaml/dist/compose/resolve-flow-collection.js +0 -209
  319. package/core/k-cli/node_modules/yaml/dist/compose/resolve-flow-scalar.d.ts +0 -10
  320. package/core/k-cli/node_modules/yaml/dist/compose/resolve-flow-scalar.js +0 -225
  321. package/core/k-cli/node_modules/yaml/dist/compose/resolve-props.d.ts +0 -23
  322. package/core/k-cli/node_modules/yaml/dist/compose/resolve-props.js +0 -148
  323. package/core/k-cli/node_modules/yaml/dist/compose/util-contains-newline.d.ts +0 -2
  324. package/core/k-cli/node_modules/yaml/dist/compose/util-contains-newline.js +0 -36
  325. package/core/k-cli/node_modules/yaml/dist/compose/util-empty-scalar-position.d.ts +0 -2
  326. package/core/k-cli/node_modules/yaml/dist/compose/util-empty-scalar-position.js +0 -28
  327. package/core/k-cli/node_modules/yaml/dist/compose/util-flow-indent-check.d.ts +0 -3
  328. package/core/k-cli/node_modules/yaml/dist/compose/util-flow-indent-check.js +0 -17
  329. package/core/k-cli/node_modules/yaml/dist/compose/util-map-includes.d.ts +0 -4
  330. package/core/k-cli/node_modules/yaml/dist/compose/util-map-includes.js +0 -15
  331. package/core/k-cli/node_modules/yaml/dist/doc/Document.d.ts +0 -141
  332. package/core/k-cli/node_modules/yaml/dist/doc/Document.js +0 -337
  333. package/core/k-cli/node_modules/yaml/dist/doc/anchors.d.ts +0 -24
  334. package/core/k-cli/node_modules/yaml/dist/doc/anchors.js +0 -76
  335. package/core/k-cli/node_modules/yaml/dist/doc/applyReviver.d.ts +0 -9
  336. package/core/k-cli/node_modules/yaml/dist/doc/applyReviver.js +0 -57
  337. package/core/k-cli/node_modules/yaml/dist/doc/createNode.d.ts +0 -17
  338. package/core/k-cli/node_modules/yaml/dist/doc/createNode.js +0 -90
  339. package/core/k-cli/node_modules/yaml/dist/doc/directives.d.ts +0 -49
  340. package/core/k-cli/node_modules/yaml/dist/doc/directives.js +0 -178
  341. package/core/k-cli/node_modules/yaml/dist/errors.d.ts +0 -21
  342. package/core/k-cli/node_modules/yaml/dist/errors.js +0 -62
  343. package/core/k-cli/node_modules/yaml/dist/index.d.ts +0 -25
  344. package/core/k-cli/node_modules/yaml/dist/index.js +0 -50
  345. package/core/k-cli/node_modules/yaml/dist/log.d.ts +0 -3
  346. package/core/k-cli/node_modules/yaml/dist/log.js +0 -19
  347. package/core/k-cli/node_modules/yaml/dist/nodes/Alias.d.ts +0 -29
  348. package/core/k-cli/node_modules/yaml/dist/nodes/Alias.js +0 -116
  349. package/core/k-cli/node_modules/yaml/dist/nodes/Collection.d.ts +0 -73
  350. package/core/k-cli/node_modules/yaml/dist/nodes/Collection.js +0 -151
  351. package/core/k-cli/node_modules/yaml/dist/nodes/Node.d.ts +0 -53
  352. package/core/k-cli/node_modules/yaml/dist/nodes/Node.js +0 -40
  353. package/core/k-cli/node_modules/yaml/dist/nodes/Pair.d.ts +0 -22
  354. package/core/k-cli/node_modules/yaml/dist/nodes/Pair.js +0 -39
  355. package/core/k-cli/node_modules/yaml/dist/nodes/Scalar.d.ts +0 -43
  356. package/core/k-cli/node_modules/yaml/dist/nodes/Scalar.js +0 -27
  357. package/core/k-cli/node_modules/yaml/dist/nodes/YAMLMap.d.ts +0 -53
  358. package/core/k-cli/node_modules/yaml/dist/nodes/YAMLMap.js +0 -147
  359. package/core/k-cli/node_modules/yaml/dist/nodes/YAMLSeq.d.ts +0 -60
  360. package/core/k-cli/node_modules/yaml/dist/nodes/YAMLSeq.js +0 -115
  361. package/core/k-cli/node_modules/yaml/dist/nodes/addPairToJSMap.d.ts +0 -4
  362. package/core/k-cli/node_modules/yaml/dist/nodes/addPairToJSMap.js +0 -65
  363. package/core/k-cli/node_modules/yaml/dist/nodes/identity.d.ts +0 -23
  364. package/core/k-cli/node_modules/yaml/dist/nodes/identity.js +0 -53
  365. package/core/k-cli/node_modules/yaml/dist/nodes/toJS.d.ts +0 -29
  366. package/core/k-cli/node_modules/yaml/dist/nodes/toJS.js +0 -39
  367. package/core/k-cli/node_modules/yaml/dist/options.d.ts +0 -350
  368. package/core/k-cli/node_modules/yaml/dist/parse/cst-scalar.d.ts +0 -64
  369. package/core/k-cli/node_modules/yaml/dist/parse/cst-scalar.js +0 -218
  370. package/core/k-cli/node_modules/yaml/dist/parse/cst-stringify.d.ts +0 -8
  371. package/core/k-cli/node_modules/yaml/dist/parse/cst-stringify.js +0 -63
  372. package/core/k-cli/node_modules/yaml/dist/parse/cst-visit.d.ts +0 -39
  373. package/core/k-cli/node_modules/yaml/dist/parse/cst-visit.js +0 -99
  374. package/core/k-cli/node_modules/yaml/dist/parse/cst.d.ts +0 -109
  375. package/core/k-cli/node_modules/yaml/dist/parse/cst.js +0 -112
  376. package/core/k-cli/node_modules/yaml/dist/parse/lexer.d.ts +0 -87
  377. package/core/k-cli/node_modules/yaml/dist/parse/lexer.js +0 -719
  378. package/core/k-cli/node_modules/yaml/dist/parse/line-counter.d.ts +0 -22
  379. package/core/k-cli/node_modules/yaml/dist/parse/line-counter.js +0 -41
  380. package/core/k-cli/node_modules/yaml/dist/parse/parser.d.ts +0 -84
  381. package/core/k-cli/node_modules/yaml/dist/parse/parser.js +0 -972
  382. package/core/k-cli/node_modules/yaml/dist/public-api.d.ts +0 -44
  383. package/core/k-cli/node_modules/yaml/dist/public-api.js +0 -107
  384. package/core/k-cli/node_modules/yaml/dist/schema/Schema.d.ts +0 -17
  385. package/core/k-cli/node_modules/yaml/dist/schema/Schema.js +0 -39
  386. package/core/k-cli/node_modules/yaml/dist/schema/common/map.d.ts +0 -2
  387. package/core/k-cli/node_modules/yaml/dist/schema/common/map.js +0 -19
  388. package/core/k-cli/node_modules/yaml/dist/schema/common/null.d.ts +0 -4
  389. package/core/k-cli/node_modules/yaml/dist/schema/common/null.js +0 -17
  390. package/core/k-cli/node_modules/yaml/dist/schema/common/seq.d.ts +0 -2
  391. package/core/k-cli/node_modules/yaml/dist/schema/common/seq.js +0 -19
  392. package/core/k-cli/node_modules/yaml/dist/schema/common/string.d.ts +0 -2
  393. package/core/k-cli/node_modules/yaml/dist/schema/common/string.js +0 -16
  394. package/core/k-cli/node_modules/yaml/dist/schema/core/bool.d.ts +0 -4
  395. package/core/k-cli/node_modules/yaml/dist/schema/core/bool.js +0 -21
  396. package/core/k-cli/node_modules/yaml/dist/schema/core/float.d.ts +0 -4
  397. package/core/k-cli/node_modules/yaml/dist/schema/core/float.js +0 -47
  398. package/core/k-cli/node_modules/yaml/dist/schema/core/int.d.ts +0 -4
  399. package/core/k-cli/node_modules/yaml/dist/schema/core/int.js +0 -42
  400. package/core/k-cli/node_modules/yaml/dist/schema/core/schema.d.ts +0 -1
  401. package/core/k-cli/node_modules/yaml/dist/schema/core/schema.js +0 -25
  402. package/core/k-cli/node_modules/yaml/dist/schema/json/schema.d.ts +0 -2
  403. package/core/k-cli/node_modules/yaml/dist/schema/json/schema.js +0 -64
  404. package/core/k-cli/node_modules/yaml/dist/schema/json-schema.d.ts +0 -69
  405. package/core/k-cli/node_modules/yaml/dist/schema/tags.d.ts +0 -48
  406. package/core/k-cli/node_modules/yaml/dist/schema/tags.js +0 -99
  407. package/core/k-cli/node_modules/yaml/dist/schema/types.d.ts +0 -92
  408. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/binary.d.ts +0 -2
  409. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/binary.js +0 -70
  410. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/bool.d.ts +0 -7
  411. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/bool.js +0 -29
  412. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/float.d.ts +0 -4
  413. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/float.js +0 -50
  414. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/int.d.ts +0 -5
  415. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/int.js +0 -76
  416. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/merge.d.ts +0 -9
  417. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/merge.js +0 -68
  418. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/omap.d.ts +0 -22
  419. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/omap.js +0 -77
  420. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/pairs.d.ts +0 -10
  421. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/pairs.js +0 -82
  422. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/schema.d.ts +0 -1
  423. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/schema.js +0 -41
  424. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/set.d.ts +0 -28
  425. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/set.js +0 -96
  426. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/timestamp.d.ts +0 -6
  427. package/core/k-cli/node_modules/yaml/dist/schema/yaml-1.1/timestamp.js +0 -105
  428. package/core/k-cli/node_modules/yaml/dist/stringify/foldFlowLines.d.ts +0 -34
  429. package/core/k-cli/node_modules/yaml/dist/stringify/foldFlowLines.js +0 -151
  430. package/core/k-cli/node_modules/yaml/dist/stringify/stringify.d.ts +0 -21
  431. package/core/k-cli/node_modules/yaml/dist/stringify/stringify.js +0 -132
  432. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyCollection.d.ts +0 -17
  433. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyCollection.js +0 -155
  434. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyComment.d.ts +0 -10
  435. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyComment.js +0 -24
  436. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyDocument.d.ts +0 -4
  437. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyDocument.js +0 -87
  438. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyNumber.d.ts +0 -2
  439. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyNumber.js +0 -26
  440. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyPair.d.ts +0 -3
  441. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyPair.js +0 -152
  442. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyString.d.ts +0 -9
  443. package/core/k-cli/node_modules/yaml/dist/stringify/stringifyString.js +0 -338
  444. package/core/k-cli/node_modules/yaml/dist/test-events.d.ts +0 -4
  445. package/core/k-cli/node_modules/yaml/dist/test-events.js +0 -134
  446. package/core/k-cli/node_modules/yaml/dist/util.d.ts +0 -16
  447. package/core/k-cli/node_modules/yaml/dist/util.js +0 -28
  448. package/core/k-cli/node_modules/yaml/dist/visit.d.ts +0 -102
  449. package/core/k-cli/node_modules/yaml/dist/visit.js +0 -236
  450. package/core/k-cli/node_modules/yaml/package.json +0 -97
  451. package/core/k-cli/node_modules/yaml/util.js +0 -2
@@ -1,42 +1,14 @@
1
1
  /**
2
- * Formation Prompt Builder
3
- * 根據陣型和技能定義組裝 prompt
4
- *
5
- * 每個陣型有不同的 prompt 結構:
6
- * - unit: 角色 + 行為 + 任務 + 工作流程 + 輸出
7
- * - solo: 角色 + 視角列表 + 行為 + 任務 + 工作流程 + 輸出
8
- * - squad: 為每個成員分別組裝 prompt(含分工 scope)
9
- * - legion: 為 team lead + 每個成員分別組裝 prompt(含溝通規則)
2
+ * Subagent Prompt Builder
3
+ * Assembles the prompt for the one live dispatch path:
4
+ * node .formation/tools/dispatch.js --skill=do --task=X --dispatch-mode=subagent
5
+ * Task card + commit command + verify/report, behind a transport prefix
6
+ * (SUBAGENT_PREFIX / SUBAGENT_PREFIX_DIRECT / buildSubagentWorktreePrefix).
10
7
  */
11
8
 
12
- // ─────────────────────────────────────────
13
- // Memory Loader(記憶系統整合)
14
- // ─────────────────────────────────────────
15
- let memoryLoader = null;
16
- try {
17
- memoryLoader = require('./memory-loader');
18
- } catch { /* memory-loader not available — graceful degradation */ }
19
-
20
- // ─────────────────────────────────────────
21
- // Shared Principles Loader(共享原則注入)
22
- // ─────────────────────────────────────────
23
9
  const fs = require('fs');
24
10
  const path = require('path');
25
11
  const { buildWorktreeGitAddCommand } = require('./utils');
26
- // TASK_2104: Alaya relevance gate (cosine floor + stack match) extracted to
27
- // a dependency-free module so task-api.js's echoAlayaLessons can share it
28
- // without pulling in Formation-only deps. Re-exported below unchanged so
29
- // prompt.test.js's imports keep working.
30
- const {
31
- buildTaskContextQuery,
32
- deriveTaskStack,
33
- deriveBijaStack,
34
- passesAlayaRelevanceGate,
35
- dedupBijas,
36
- ALAYA_MIN_COSINE_DEFAULT,
37
- ALAYA_STACK_OVERRIDE_COSINE,
38
- ALAYA_CANDIDATE_POOL,
39
- } = require('./alaya-gate');
40
12
 
41
13
  // Shared core-path resolver (validates existsSync+isDirectory, falls back to KENAZ_SOURCE_DIR)
42
14
  let _resolveCoreDir = null;
@@ -44,53 +16,6 @@ try {
44
16
  _resolveCoreDir = require(path.join(__dirname, '..', '..', '.claude', 'hooks', 'core-resolve')).resolveCoreDir;
45
17
  } catch { /* graceful degradation — older installs without the export */ }
46
18
 
47
- let _sharedPrinciplesCache = null;
48
-
49
- // ─────────────────────────────────────────
50
- // Shared Module Tag Rules(模組自動注入規則)
51
- // ─────────────────────────────────────────
52
-
53
- // TASK_2371: opt-in only — a task must carry the explicit review tag. No keyword
54
- // match on generic tags (auth/test/...) or chart warnings; a 100-line OWASP
55
- // block on an unrelated auth card was pure noise.
56
- const MODULE_TAG_RULES = [
57
- { tags: ['security-review'], module: 'security.yaml' },
58
- { tags: ['testing-review'], module: 'testing.yaml' },
59
- { tags: ['code-review'], module: 'code_review.yaml' },
60
- ];
61
-
62
- /**
63
- * Resolve which shared modules to inject based on task tags and optional chart context.
64
- * @param {string|object} taskData - task JSON string or parsed object
65
- * @param {string|null} [chartPath] - optional path to chart.json for richer context
66
- * @param {string|null} [corePath] - KenazAI root path (defaults to two levels up from __dirname)
67
- * @returns {string[]} list of absolute module file paths to inject
68
- */
69
- function resolveModules(taskData, chartPath, corePath) {
70
- const modules = new Set();
71
- let taskTags = [];
72
-
73
- try {
74
- const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
75
- if (parsed) {
76
- taskTags = (parsed.tags || []).map(t => String(t).toLowerCase());
77
- }
78
- } catch { /* not JSON */ }
79
-
80
- // Tag-based matching
81
- for (const rule of MODULE_TAG_RULES) {
82
- if (rule.tags.some(t => taskTags.includes(t))) {
83
- modules.add(rule.module);
84
- }
85
- }
86
-
87
- if (modules.size === 0) return [];
88
-
89
- const root = corePath || path.resolve(__dirname, '..', '..');
90
- const modulesDir = path.join(root, '.formation', 'roles', '_shared', 'modules');
91
- return [...modules].map(m => path.join(modulesDir, m));
92
- }
93
-
94
19
  /**
95
20
  * Resolve the KenazAI core path from a sub-project's project.yaml.
96
21
  * Returns null when projectPath is absent or IS the core project.
@@ -122,35 +47,6 @@ function resolveCorePath(projectPath) {
122
47
  } catch { return null; }
123
48
  }
124
49
 
125
- function loadSharedPrinciples(corePath) {
126
- if (_sharedPrinciplesCache) return _sharedPrinciplesCache;
127
-
128
- const principlesPath = path.join(
129
- corePath || path.resolve(__dirname, '..', '..'),
130
- '.specialist', 'shared', 'principles.yaml'
131
- );
132
-
133
- try {
134
- if (!fs.existsSync(principlesPath)) return null;
135
- const content = fs.readFileSync(principlesPath, 'utf-8');
136
-
137
- // 提取 differential_over_intuition 區塊
138
- const ddxMatch = content.match(
139
- /differential_over_intuition:[\s\S]*?(?=\n# ═|$)/
140
- );
141
- if (!ddxMatch) return null;
142
-
143
- _sharedPrinciplesCache = ddxMatch[0].trim();
144
- return _sharedPrinciplesCache;
145
- } catch {
146
- return null;
147
- }
148
- }
149
-
150
- // ─────────────────────────────────────────
151
- // TASK_1998 R2 -> two-axis prompt policy (2026-09-22 user decision)
152
- // ─────────────────────────────────────────
153
-
154
50
  /**
155
51
  * Extract the card's `rigor` field from taskData (JSON string or object).
156
52
  * Returns null when taskData is absent, unparseable, or has no rigor field
@@ -183,78 +79,17 @@ function extractComplexity(taskData) {
183
79
  }
184
80
 
185
81
  /**
186
- * TASK_1998 two-axis prompt policy (2026-09-22 user decision, replaces the
187
- * single rigor-gated R2 diet switch):
188
- *
189
- * CONTEXT volume — driven by `complexity` (how much the task touches):
190
- * simple -> no Related Files Preview, no Known Failures, no Project Memory.
191
- * medium -> Known Failures on; no Preview; no Project Memory.
192
- * complex -> Related Files Preview on; Known Failures + Project Memory on.
193
- * (TASK_2371: Cross-Project Context (Cortex) and Episodic Memory are gone
194
- * for every complexity — stale / unverified outcomes.)
195
- *
196
- * PROCESS strictness — driven by `rigor`:
197
- * low -> the 8-line "## Working contract" only; rune inscription optional.
198
- * medium/high -> Working contract + "## Progress Note (brief)" + the
199
- * rune-inscribe line (TASK_2371: high no longer has a
200
- * mandatory recall/inscribe/Cognitive Checkpoint ritual).
201
- *
202
- * This is the ONLY place either axis branches into its behaviour; every call
203
- * site below reads flags off the returned profile instead of re-testing
204
- * rigor/complexity itself.
205
- *
206
- * Backwards compatible: a bare string (or null/undefined) is treated as the
207
- * legacy single-axis call — `rigor` — and `complexity` defaults to 'medium'.
208
- * @param {string|{rigor?: string|null, complexity?: string|null}|null|undefined} rigorOrOptions
82
+ * Context volume for the dispatch prompt, driven by the card's `complexity`:
83
+ * only a complex card gets the Related Files Preview (dispatch.js reads
84
+ * skipRelatedFilesPreview). Accepts {complexity} or a bare complexity string.
85
+ * @param {string|{complexity?: string|null}|null|undefined} complexityOrOptions
209
86
  */
210
- function promptProfile(rigorOrOptions) {
211
- let rawRigor;
212
- let rawComplexity;
213
- if (rigorOrOptions && typeof rigorOrOptions === 'object') {
214
- rawRigor = rigorOrOptions.rigor;
215
- rawComplexity = rigorOrOptions.complexity;
216
- } else {
217
- // Legacy string-rigor call — complexity defaults to 'medium'.
218
- rawRigor = rigorOrOptions;
219
- rawComplexity = 'medium';
220
- }
221
-
222
- const rigor = (rawRigor === 'low' || rawRigor === 'high') ? rawRigor : 'medium';
223
- const complexity = (rawComplexity === 'simple' || rawComplexity === 'complex') ? rawComplexity : 'medium';
224
-
225
- const CONTEXT_BY_COMPLEXITY = {
226
- simple: {
227
- skipRelatedFilesPreview: true,
228
- skipKnownFailures: true,
229
- skipProjectMemory: true,
230
- },
231
- medium: {
232
- skipRelatedFilesPreview: true,
233
- skipKnownFailures: false,
234
- skipProjectMemory: true,
235
- },
236
- complex: {
237
- skipRelatedFilesPreview: false,
238
- skipKnownFailures: false,
239
- skipProjectMemory: false,
240
- },
241
- };
242
-
243
- // ritualTier drives appendCognitiveCheckpoint:
244
- // 'low' -> Working contract only; 'medium' -> Working contract + short
245
- // progress note + mandatory inscribe line; 'high' -> full ritual (compactRitual=false).
246
- const PROCESS_BY_RIGOR = {
247
- low: { compactRitual: true, ritualTier: 'low' },
248
- medium: { compactRitual: true, ritualTier: 'medium' },
249
- high: { compactRitual: true, ritualTier: 'medium' }, // TASK_2371: high == medium ritual
250
- };
251
-
252
- return {
253
- rigor,
254
- complexity,
255
- ...CONTEXT_BY_COMPLEXITY[complexity],
256
- ...PROCESS_BY_RIGOR[rigor],
257
- };
87
+ function promptProfile(complexityOrOptions) {
88
+ const raw = complexityOrOptions && typeof complexityOrOptions === 'object'
89
+ ? complexityOrOptions.complexity
90
+ : complexityOrOptions;
91
+ const complexity = (raw === 'simple' || raw === 'complex') ? raw : 'medium';
92
+ return { complexity, skipRelatedFilesPreview: complexity !== 'complex' };
258
93
  }
259
94
 
260
95
  /**
@@ -287,17 +122,6 @@ function splitPromptSections(promptText) {
287
122
  return sections;
288
123
  }
289
124
 
290
- // ─────────────────────────────────────────
291
- // 主入口
292
- // ─────────────────────────────────────────
293
-
294
- /**
295
- * 根據陣型分發到對應的 prompt builder
296
- * @param {object} skillDef - parsed YAML
297
- * @param {string} taskInput - 任務描述或 ID
298
- * @param {string|null} taskData - 從 .collaboration 載入的任務 YAML
299
- * @returns {string|string[]} 單一 prompt(unit/solo)或 prompt 陣列(squad)
300
- */
301
125
  // TASK_2374: append-only usage counter (measure-first; cut decisions after 14 days of data).
302
126
  // Env KENAZ_USAGE_COUNTERS_FILE overrides the target (tests). Never throws.
303
127
  function recordSkillUsage(skillDef, options = {}) {
@@ -314,62 +138,13 @@ function recordSkillUsage(skillDef, options = {}) {
314
138
 
315
139
  function buildPrompt(skillDef, taskInput, taskData, options = {}) {
316
140
  const formation = skillDef.formation || 'unit';
317
- recordSkillUsage(skillDef, options);
318
-
319
- const builders = {
320
- unit: buildUnitPrompt,
321
- solo: buildSoloPrompt,
322
- squad: buildSquadPrompts,
323
- legion: buildLegionPrompts,
324
- legacy: buildLegacyPrompt,
325
- };
326
-
327
- // TASK_2391: worktree/direct subagent dispatch gets the short template, not the
328
- // full Formation prompt. squad/legion/legacy and non-subagent paths are unchanged.
329
- if (options.isSubagent && (formation === 'unit' || formation === 'solo')) {
330
- return buildSubagentPrompt(skillDef, taskInput, taskData, options);
331
- }
332
-
333
- const builder = builders[formation];
334
- if (!builder) {
335
- throw new Error(`Unknown formation: ${formation}. Expected: ${Object.keys(builders).join(', ')}`);
141
+ if (formation === 'squad' || formation === 'legion') {
142
+ throw new Error(`Formation '${formation}' (skill '${skillDef.name || 'unknown'}') is retired: only unit/solo skills can be dispatched (--dispatch-mode=subagent).`);
336
143
  }
337
-
338
- return builder(skillDef, taskInput, taskData, options);
339
- }
340
-
341
- // ─────────────────────────────────────────
342
- // Unit:單一角色,直接執行
343
- // ─────────────────────────────────────────
344
-
345
- function buildUnitPrompt(skillDef, taskInput, taskData, options = {}) {
346
- const lines = [];
347
-
348
- lines.push(`## Role`);
349
- lines.push(skillDef.role.identity);
350
- lines.push(`Priority: ${skillDef.role.priority}`);
351
- lines.push('');
352
-
353
- appendProjectContext(lines, options);
354
- appendFormationPreamble(lines, taskData, options);
355
- appendYggdrasilContext(lines, options.yggdrasilContext);
356
- appendMemoryContext(lines, taskData, options);
357
- appendAlayaLessons(lines, taskData);
358
- appendSharedModules(lines, taskData, options);
359
- appendBehavior(lines, skillDef);
360
- appendTask(lines, taskInput, taskData, options);
361
- appendCognitiveRecall(lines, taskData, options);
362
- appendWorkflow(lines, skillDef, options, taskData);
363
- appendOutput(lines, skillDef);
364
- appendCognitiveCheckpoint(lines, taskData, options);
365
-
366
- return lines.join('\n');
144
+ recordSkillUsage(skillDef, options);
145
+ return buildSubagentPrompt(skillDef, taskInput, taskData, options);
367
146
  }
368
147
 
369
- // ─────────────────────────────────────────
370
- // Subagent:短範本(TASK_2391)
371
- // ─────────────────────────────────────────
372
-
373
148
  /**
374
149
  * Short prompt for Agent-tool subagent dispatch (both harnesses): task card,
375
150
  * commit command, verification expectation, report format. The worktree hard
@@ -393,7 +168,13 @@ function buildSubagentPrompt(skillDef, taskInput, taskData, options = {}) {
393
168
  appendRustCheckReminder(lines, taskData, options, isIsolated);
394
169
 
395
170
  lines.push('## Verify and report');
171
+ // Ponytail A/B 2026-10-10: the arm without this line broke an existing fixture test it never ran.
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.');
396
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.`);
397
178
  const onBlocked = skillDef.collaboration && skillDef.collaboration.on_blocked;
398
179
  if (onBlocked && onBlocked.run) {
399
180
  lines.push(`Blocked: \`${buildTaskApiRewriter(options)(onBlocked.run).replace(/\{task_id\}/g, taskId)}\` and explain.`);
@@ -411,516 +192,20 @@ function extractTaskId(taskData) {
411
192
  } catch { return null; }
412
193
  }
413
194
 
414
- // ─────────────────────────────────────────
415
- // Solo:一個 agent 裝備多視角,自主切換
416
- // ─────────────────────────────────────────
417
-
418
- function buildSoloPrompt(skillDef, taskInput, taskData, options = {}) {
419
- const lines = [];
420
-
421
- lines.push(`## Role`);
422
- lines.push(skillDef.role.identity);
423
- lines.push(`Priority: ${skillDef.role.priority}`);
424
- lines.push('');
425
-
426
- appendProjectContext(lines, options);
427
- appendFormationPreamble(lines, taskData, options);
428
- appendYggdrasilContext(lines, options.yggdrasilContext);
429
- appendMemoryContext(lines, taskData, options);
430
- appendAlayaLessons(lines, taskData);
431
-
432
- // 分析原則(Solo strategy 專用)
433
- if (skillDef.analysis_principles && skillDef.analysis_principles.length > 0) {
434
- lines.push(`## Analysis Principles`);
435
- lines.push('');
436
- for (const p of skillDef.analysis_principles) {
437
- lines.push(`### ${p.name}`);
438
- lines.push(p.description.trim());
439
- lines.push('');
440
- }
441
- }
442
-
443
- // 可用視角
444
- if (skillDef.perspectives && skillDef.perspectives.length > 0) {
445
- lines.push(`## Available Perspectives`);
446
- lines.push('');
447
- lines.push('你隨時可以切換到以下任一視角。根據分析過程中的需要自主決定。');
448
- lines.push('');
449
-
450
- for (const p of skillDef.perspectives) {
451
- lines.push(`### ${p.name}`);
452
- lines.push(`- 關注:${p.lens}`);
453
- lines.push(`- 何時切換:${p.trigger}`);
454
- lines.push('');
455
- }
456
- }
457
-
458
- appendBehavior(lines, skillDef);
459
- appendSharedPrinciples(lines, options);
460
- appendTask(lines, taskInput, taskData, options);
461
- appendCognitiveRecall(lines, taskData, options);
462
- appendWorkflow(lines, skillDef, options, taskData);
463
- appendOutput(lines, skillDef);
464
- appendCognitiveCheckpoint(lines, taskData, options);
465
-
466
- return lines.join('\n');
467
- }
468
-
469
- // ─────────────────────────────────────────
470
- // Squad:為每個成員分別組裝 prompt
471
- // ─────────────────────────────────────────
472
-
473
- /**
474
- * Squad 回傳 prompt 陣列,每個成員一份
475
- * @returns {{ memberId: string, prompt: string, skill: string }[]}
476
- */
477
- function buildSquadPrompts(skillDef, taskInput, taskData, options = {}) {
478
- const members = skillDef.members || [];
479
- if (members.length === 0) {
480
- throw new Error('Squad YAML must define `members` array');
481
- }
482
-
483
- // 組裝共享上下文(注入到每個成員 prompt)
484
- const sharedBlock = buildSharedContextBlock(skillDef);
485
-
486
- return members.map(member => {
487
- const lines = [];
488
-
489
- // 成員角色
490
- lines.push(`## Role`);
491
- lines.push(typeof member.role === 'string' ? member.role.trim() : `你是 Squad 的一員:${member.name}`);
492
- lines.push('');
493
-
494
- // 分析模式(mode_behavior 注入)
495
- const analysisMode = options.analysisMode;
496
- if (analysisMode && member.mode_behavior && member.mode_behavior[analysisMode]) {
497
- const modeConfig = skillDef.analysis_mode?.modes?.[analysisMode];
498
- lines.push(`## Analysis Mode: ${analysisMode}`);
499
- if (modeConfig?.description) lines.push(modeConfig.description.trim());
500
- lines.push('');
501
- lines.push('**你在此模式下的立場調整**:');
502
- lines.push(member.mode_behavior[analysisMode].trim());
503
- lines.push('');
504
- }
505
-
506
- // 核心原則(principles 注入 — 所有成員共用的硬規則)
507
- if (skillDef.principles) {
508
- lines.push(`## Principles (絕對遵守)`);
509
- if (skillDef.principles.focus) {
510
- lines.push(skillDef.principles.focus.trim());
511
- lines.push('');
512
- }
513
- if (skillDef.principles.forbidden && skillDef.principles.forbidden.length > 0) {
514
- lines.push('**禁止 (違反即失敗)**:');
515
- for (const f of skillDef.principles.forbidden) lines.push(`- ❌ ${f}`);
516
- lines.push('');
517
- }
518
- if (skillDef.principles.required && skillDef.principles.required.length > 0) {
519
- lines.push('**必須遵守**:');
520
- for (const r of skillDef.principles.required) lines.push(`- ✅ ${r}`);
521
- lines.push('');
522
- }
523
- }
524
-
525
- // 成員身份
526
- lines.push(`## Identity`);
527
- lines.push(`你是 Squad 的成員 **${member.name}** (id: ${member.id || member.name})。`);
528
- lines.push(`你與其他成員並行工作,各自獨立,不能互相通訊。`);
529
- lines.push(`完成你負責的部分後回報結果,Lead 會負責整合。`);
530
- lines.push('');
531
-
532
- // 成員的 scope(負責範圍)— 支援 include/exclude 格式
533
- if (member.scope) {
534
- lines.push(`## Scope (你只能碰這些檔案)`);
535
- if (member.scope.include || member.scope.exclude) {
536
- if (member.scope.include) {
537
- lines.push(`Include:`);
538
- const includes = Array.isArray(member.scope.include) ? member.scope.include : [member.scope.include];
539
- for (const p of includes) lines.push(` - ${p}`);
540
- }
541
- if (member.scope.exclude) {
542
- lines.push(`Exclude:`);
543
- const excludes = Array.isArray(member.scope.exclude) ? member.scope.exclude : [member.scope.exclude];
544
- for (const p of excludes) lines.push(` - ${p}`);
545
- }
546
- } else if (Array.isArray(member.scope)) {
547
- for (const s of member.scope) lines.push(`- ${s}`);
548
- } else {
549
- lines.push(member.scope);
550
- }
551
- lines.push('');
552
- }
553
-
554
- // Yggdrasil network context
555
- appendYggdrasilContext(lines, options.yggdrasilContext);
556
- appendMemoryContext(lines, taskData, options);
557
- appendAlayaLessons(lines, taskData);
558
- appendFormationPreamble(lines, taskData, options);
559
-
560
- // 共享上下文
561
- if (sharedBlock) {
562
- lines.push(sharedBlock);
563
- lines.push('');
564
- }
565
-
566
- // 行為準則(squad 全局 + 成員級別)
567
- const behaviors = [
568
- ...(skillDef.behavior || []),
569
- ...(member.behavior || [])
570
- ];
571
- if (behaviors.length > 0) {
572
- lines.push(`## Behavior`);
573
- for (const b of behaviors) lines.push(`- ${b}`);
574
- lines.push('');
575
- }
576
-
577
- // 任務
578
- appendTask(lines, taskInput, taskData, options);
579
- appendCognitiveRecall(lines, taskData, options);
580
-
581
- // 成員專屬工作流程或全局工作流程
582
- if (member.workflow) {
583
- lines.push(`## Workflow`);
584
- for (const step of member.workflow) lines.push(`- ${step}`);
585
- lines.push('');
586
- appendDispatchOverrides(lines, taskData); // TASK_1820: same post-envelope channel as appendWorkflow
587
- } else {
588
- appendWorkflow(lines, skillDef, options, taskData);
589
- }
590
-
591
- // 輸出
592
- if (member.output) {
593
- lines.push(`## Expected Output`);
594
- lines.push(typeof member.output === 'string' ? member.output.trim() : JSON.stringify(member.output));
595
- } else {
596
- appendOutput(lines, skillDef);
597
- }
598
-
599
- appendCognitiveCheckpoint(lines, taskData, options);
600
-
601
- return {
602
- memberId: member.id || member.name,
603
- prompt: lines.join('\n'),
604
- skill: member.skill || 'do'
605
- };
606
- });
607
- }
608
-
609
- /**
610
- * 組裝 shared_context 區塊(Squad 成員共用的約定)
611
- */
612
- function buildSharedContextBlock(skillDef) {
613
- if (!skillDef.shared_context) return null;
614
-
615
- const lines = [];
616
- lines.push(`## Shared Context (所有成員共享的約定)`);
617
-
618
- if (skillDef.shared_context.description) {
619
- lines.push(skillDef.shared_context.description.trim());
620
- }
621
-
622
- if (skillDef.shared_context.includes) {
623
- lines.push('');
624
- const includes = skillDef.shared_context.includes;
625
- if (typeof includes === 'object' && !Array.isArray(includes)) {
626
- for (const [key, val] of Object.entries(includes)) {
627
- lines.push(`### ${key}`);
628
- lines.push(typeof val === 'string' ? val.trim() : JSON.stringify(val));
629
- lines.push('');
630
- }
631
- } else if (Array.isArray(includes)) {
632
- for (const item of includes) {
633
- if (typeof item === 'string') {
634
- lines.push(`- ${item}`);
635
- } else if (typeof item === 'object') {
636
- for (const [key, val] of Object.entries(item)) {
637
- lines.push(`### ${key}`);
638
- lines.push(typeof val === 'string' ? val.trim() : JSON.stringify(val));
639
- lines.push('');
640
- }
641
- }
642
- }
643
- }
644
- }
645
-
646
- return lines.join('\n');
647
- }
648
-
649
- // ─────────────────────────────────────────
650
- // Legion:為 team lead 和成員組裝 prompt
651
- // ─────────────────────────────────────────
652
-
653
- /**
654
- * Legion 回傳 team config + 每個成員的 prompt
655
- * @returns {{ teamName: string, teamDesc: string, lead: { prompt: string }, members: { name: string, prompt: string }[] }}
656
- */
657
- function buildLegionPrompts(skillDef, taskInput, taskData, options = {}) {
658
- const team = skillDef.team || {};
659
- // 支援 members: [...] 和 members: { roles: [...] } 兩種格式
660
- const rawMembers = skillDef.members || [];
661
- const members = Array.isArray(rawMembers) ? rawMembers : (rawMembers.roles || []);
662
-
663
- // 團隊名稱:優先用 team.name,fallback 到 skillDef.name
664
- const teamName = team.name || skillDef.name || 'Legion';
665
-
666
- // Team lead prompt
667
- const leadLines = [];
668
- leadLines.push(`## Role`);
669
- leadLines.push(`你是 ${teamName} 的 Team Lead。`);
670
- leadLines.push(skillDef.role?.identity || '');
671
- leadLines.push('');
672
- leadLines.push(`## Team Members`);
673
- for (const m of members) {
674
- leadLines.push(`- **${m.name}**: ${m.role || m.description || ''}`);
675
- }
676
- leadLines.push('');
677
-
678
- if (skillDef.communication) {
679
- leadLines.push(`## Communication Rules`);
680
- const comm = skillDef.communication;
681
- if (Array.isArray(comm)) {
682
- for (const rule of comm) leadLines.push(`- ${rule}`);
683
- } else if (typeof comm === 'object') {
684
- if (comm.principles) {
685
- leadLines.push(`### Principles`);
686
- for (const p of comm.principles) leadLines.push(`- ${p}`);
687
- leadLines.push('');
688
- }
689
- if (comm.patterns) {
690
- leadLines.push(`### Patterns`);
691
- for (const p of comm.patterns) {
692
- leadLines.push(`- **${p.name}**: ${p.who} — ${p.content || ''}`);
693
- }
694
- leadLines.push('');
695
- }
696
- if (comm.broadcast_policy) {
697
- leadLines.push(`### Broadcast Policy`);
698
- leadLines.push(comm.broadcast_policy.trim());
699
- leadLines.push('');
700
- }
701
- }
702
- leadLines.push('');
703
- }
704
-
705
- appendYggdrasilContext(leadLines, options.yggdrasilContext);
706
- appendMemoryContext(leadLines, taskData, options);
707
- appendAlayaLessons(leadLines, taskData);
708
- appendFormationPreamble(leadLines, taskData, options);
709
- appendTask(leadLines, taskInput, taskData, options);
710
- appendCognitiveRecall(leadLines, taskData);
711
-
712
- if (skillDef.lifecycle) {
713
- leadLines.push(`## Lifecycle`);
714
- // lifecycle 可能是 { phases: [...] } 或直接是陣列
715
- const phases = Array.isArray(skillDef.lifecycle)
716
- ? skillDef.lifecycle
717
- : (skillDef.lifecycle.phases || []);
718
- for (const step of phases) {
719
- if (typeof step === 'string') {
720
- leadLines.push(`- ${step}`);
721
- } else {
722
- leadLines.push(`### ${step.phase || step.step}`);
723
- if (step.description) leadLines.push(step.description.trim());
724
- if (step.lead_actions) {
725
- leadLines.push('Lead actions:');
726
- for (const a of step.lead_actions) leadLines.push(`- ${a}`);
727
- }
728
- if (step.exit_criteria) {
729
- leadLines.push('Exit criteria:');
730
- for (const c of step.exit_criteria) leadLines.push(`- ${c}`);
731
- }
732
- leadLines.push('');
733
- }
734
- }
735
- leadLines.push('');
736
- }
737
-
738
- appendOutput(leadLines, skillDef);
739
- appendCognitiveCheckpoint(leadLines, taskData, options);
740
-
741
- // Member prompts
742
- // 優先使用 YAML 的 member_prompts 模板(如 forge 的 designer/implementer/reviewer)
743
- const promptTemplates = skillDef.member_prompts || {};
744
-
745
- const memberPrompts = members.map(member => {
746
- const lines = [];
747
-
748
- // 如果有預寫好的 prompt 模板,直接用
749
- const template = promptTemplates[member.id];
750
- if (template) {
751
- lines.push(template.trim());
752
- lines.push('');
753
- } else {
754
- // 自動生成
755
- lines.push(`## Role`);
756
- lines.push(member.role || member.description?.trim() || `你是 ${teamName} 團隊的 ${member.name}。`);
757
- lines.push('');
758
-
759
- if (member.responsibilities) {
760
- lines.push(`## Responsibilities`);
761
- for (const r of member.responsibilities) lines.push(`- ${r}`);
762
- lines.push('');
763
- }
764
-
765
- if (member.skills) {
766
- lines.push(`## Skills`);
767
- for (const s of member.skills) lines.push(`- ${s}`);
768
- lines.push('');
769
- }
770
-
771
- if (member.scope) {
772
- lines.push(`## Scope`);
773
- if (Array.isArray(member.scope)) {
774
- for (const s of member.scope) lines.push(`- ${s}`);
775
- } else {
776
- lines.push(member.scope);
777
- }
778
- lines.push('');
779
- }
780
- }
781
-
782
- lines.push(`## Team Context`);
783
- lines.push(`你是 ${teamName} 團隊的成員。可以透過 SendMessage 和其他成員溝通。`);
784
- lines.push(`Team Lead 會分配任務給你,完成後回報。如果被阻塞,主動溝通。`);
785
- lines.push('');
786
-
787
- appendYggdrasilContext(lines, options.yggdrasilContext);
788
- appendFormationPreamble(lines, taskData, options);
789
- appendTask(lines, taskInput, taskData, options);
790
- appendCognitiveRecall(lines, taskData, options);
791
- appendCognitiveCheckpoint(lines, taskData, options);
792
-
793
- return {
794
- name: member.name,
795
- agentType: member.agent_type || 'general-purpose',
796
- prompt: lines.join('\n')
797
- };
798
- });
799
-
800
- return {
801
- teamName: teamName.toLowerCase().replace(/\s+/g, '-'),
802
- teamDesc: team.description || skillDef.description || '',
803
- lead: { prompt: leadLines.join('\n') },
804
- members: memberPrompts
805
- };
806
- }
807
-
808
- // ─────────────────────────────────────────
809
- // Legacy:排程器模式(不建 prompt,直接走 scheduler loop)
810
- // ─────────────────────────────────────────
811
-
812
- /**
813
- * Legacy 不需要組裝 subagent prompt。
814
- * 回傳 scheduler 配置摘要(供 dry-run 顯示用)。
815
- */
816
- function buildLegacyPrompt(skillDef, taskInput, taskData, options = {}) {
817
- const lines = [];
818
- lines.push(`## Role`);
819
- lines.push(skillDef.role?.identity || 'You are a developer assigned to complete the task below.');
820
- lines.push('');
821
-
822
- appendBehavior(lines, skillDef);
823
- appendProjectContext(lines, options);
824
- appendYggdrasilContext(lines, options.yggdrasilContext);
825
- appendMemoryContext(lines, taskData, options);
826
- appendAlayaLessons(lines, taskData);
827
- appendTask(lines, taskInput, taskData, options);
828
- appendCognitiveRecall(lines, taskData, options);
829
-
830
- if (skillDef.collaboration?.workflow) {
831
- appendWorkflow(lines, skillDef, options, taskData);
832
- }
833
-
834
- appendCognitiveCheckpoint(lines, taskData, options);
835
-
836
- return lines.join('\n');
837
- }
838
-
839
- // ─────────────────────────────────────────
840
- // 共用片段
841
- // ─────────────────────────────────────────
842
-
843
- /**
844
- * Append context-aware shared modules to prompt.
845
- * Reads .formation/roles/_shared/modules/*.yaml based on task tags / chart context.
846
- * Inserted before appendBehavior().
847
- */
848
- function appendSharedModules(lines, taskData, options = {}) {
849
- const corePath = options.corePath || resolveCorePath(options.projectPath) || undefined;
850
- const chartPath = options.chartPath || null;
851
- const modulePaths = resolveModules(taskData, chartPath, corePath);
852
- if (modulePaths.length === 0) return;
853
-
854
- for (const modPath of modulePaths) {
855
- try {
856
- if (!fs.existsSync(modPath)) continue;
857
- const raw = fs.readFileSync(modPath, 'utf-8');
858
-
859
- // Extract name and description for the section header
860
- const nameMatch = raw.match(/^name:\s*(.+)$/m);
861
- const descMatch = raw.match(/^description:\s*(.+)$/m);
862
- const moduleName = nameMatch ? nameMatch[1].trim() : path.basename(modPath, '.yaml');
863
- const moduleDesc = descMatch ? descMatch[1].trim() : '';
864
-
865
- lines.push(`## Shared Module: ${moduleName}`);
866
- if (moduleDesc) {
867
- lines.push(moduleDesc);
868
- lines.push('');
869
- }
870
-
871
- // Inject content section (raw YAML-formatted text, stripped of top-level key)
872
- const contentMatch = raw.match(/^content:\n([\s\S]*)$/m);
873
- if (contentMatch) {
874
- lines.push(contentMatch[1].trimEnd());
875
- } else {
876
- lines.push(raw.trimEnd());
877
- }
878
- lines.push('');
879
- } catch { /* graceful degradation — skip unreadable module */ }
880
- }
881
- }
882
-
883
- function appendBehavior(lines, skillDef) {
884
- if (skillDef.behavior && skillDef.behavior.length > 0) {
885
- lines.push(`## Behavior`);
886
- for (const b of skillDef.behavior) {
887
- lines.push(`- ${b}`);
888
- }
889
- lines.push('');
890
- }
891
- }
892
-
893
- /**
894
- * Append shared principles (DDx protocol) to prompt.
895
- * Loaded from .specialist/shared/principles.yaml.
896
- */
897
- function appendSharedPrinciples(lines, options = {}) {
898
- const corePath = options.corePath || undefined;
899
- const ddx = loadSharedPrinciples(corePath);
900
- if (!ddx) return;
901
-
902
- lines.push(`## Shared Principles — 鑑別診斷 (DDx)`);
903
- lines.push(`遇到 Bug 診斷、架構決策、結果驗證時,必須套用以下 protocol:`);
904
- lines.push('');
905
- lines.push(ddx);
906
- lines.push('');
907
- }
908
-
909
195
  /**
910
196
  * Append project context to prompt (multi-project support).
911
197
  * When projectPath is provided, injects context telling the agent
912
198
  * which project to operate on and where system files are.
913
199
  */
914
200
  function appendProjectContext(lines, options = {}) {
915
- const { projectPath, worktreePath } = options;
201
+ const { projectPath } = options;
916
202
 
917
203
  // Sub-project context — only when projectPath is not the core project
918
204
  if (projectPath) {
919
- const pathMod = require('path');
920
- const coreRoot = pathMod.resolve(__dirname, '..', '..');
205
+ const coreRoot = path.resolve(__dirname, '..', '..');
921
206
  const norm = p => p.replace(/[\\/]+$/, '').replace(/\\/g, '/').toLowerCase();
922
207
 
923
- if (norm(pathMod.resolve(projectPath)) !== norm(coreRoot)) {
208
+ if (norm(path.resolve(projectPath)) !== norm(coreRoot)) {
924
209
  lines.push(`## Project Context [internal — do NOT mention sub-project status to user]`);
925
210
  lines.push(`Working directory: ${projectPath}`);
926
211
  lines.push(`- Task files: ${projectPath}/.kenaz/tasks/`);
@@ -935,121 +220,13 @@ function appendProjectContext(lines, options = {}) {
935
220
  lines.push('');
936
221
  lines.push(`### KENAZ_CORE_PATH (resolved)`);
937
222
  lines.push(`\`${corePath}\``);
938
- lines.push(`All system tools use this path as root. When you need task-api.js, headless.js, or other Formation tools, reference:`);
223
+ lines.push(`All system tools use this path as root. When you need task-api.js, reference:`);
939
224
  lines.push(`- task-api: \`node "${corePath}/.collaboration/core/task-api.js" --project-path "${projectPath}"\``);
940
- lines.push(`- headless: \`node "${corePath}/.formation/tools/headless.js" --task=TASK_XXX --project-path="${projectPath}"\``);
941
- lines.push(`- post-complete: \`node "${corePath}/.formation/tools/post-complete.js" --task=TASK_XXX --project-path="${projectPath}"\``);
942
225
  }
943
226
 
944
227
  lines.push('');
945
228
  }
946
229
  }
947
-
948
- // Worktree context — when agent runs in an isolated git worktree
949
- if (worktreePath) {
950
- const originalProjectPath = projectPath || require('path').resolve(__dirname, '..', '..');
951
- lines.push(`## Worktree Context [internal]`);
952
- lines.push(`Your cwd is a git worktree at ${worktreePath}.`);
953
- lines.push(`For Synapse MCP calls (recall/inscribe/forge), use projectPath=${originalProjectPath} not your cwd.`);
954
- lines.push('');
955
- }
956
- }
957
-
958
- /**
959
- * Append standard Formation Preamble to prompt.
960
- * Injected into every dispatch agent for consistent task orientation.
961
- * Fields: Task (id + title), Branch (git, fallback: unknown),
962
- * Scope (scope_paths, omitted when empty), Principles (fixed).
963
- *
964
- * @param {string[]} lines - prompt lines array (mutated)
965
- * @param {string|object} taskData - task JSON string or parsed object
966
- * @param {object} [options] - options ({ projectPath })
967
- */
968
- function appendFormationPreamble(lines, taskData, options = {}) {
969
- let taskId = null;
970
- let taskTitle = null;
971
- let scopePaths = [];
972
-
973
- try {
974
- const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
975
- if (parsed) {
976
- taskId = parsed.id || null;
977
- taskTitle = parsed.title || null;
978
- scopePaths = parsed.scope_paths || [];
979
- }
980
- } catch { /* not JSON */ }
981
-
982
- // Resolve git branch — fallback to 'unknown' on any failure
983
- let branch = 'unknown';
984
- try {
985
- const { execSync } = require('child_process');
986
- const cwd = options.projectPath || path.resolve(__dirname, '..', '..');
987
- const out = execSync('git rev-parse --abbrev-ref HEAD', {
988
- cwd,
989
- encoding: 'utf-8',
990
- stdio: ['pipe', 'pipe', 'pipe'],
991
- timeout: 3000,
992
- windowsHide: true,
993
- });
994
- branch = (out || '').trim() || 'unknown';
995
- } catch { /* git unavailable or not a repo */ }
996
-
997
- lines.push(`## Dispatch Context`);
998
- if (taskId || taskTitle) {
999
- const label = [taskId, taskTitle].filter(Boolean).join(' — ');
1000
- lines.push(`Task: ${label}`);
1001
- }
1002
- lines.push(`Branch: ${branch}`);
1003
- if (scopePaths.length > 0) {
1004
- lines.push(`Scope: ${scopePaths.join(', ')}`);
1005
- }
1006
- lines.push(`Principles: 完美達成>一切|禁止盲寫|露出問題|結構>估計`);
1007
- lines.push('');
1008
-
1009
- appendMinimalismLadder(lines);
1010
- appendFileBudgetSRP(lines);
1011
- }
1012
-
1013
- /**
1014
- * Append the YAGNI minimalism decision-ladder (Ponytail essence) to prompt.
1015
- * Wired once into appendFormationPreamble so EVERY dispatched agent
1016
- * (unit/solo/squad/legion + sub-agent path via shared buildPrompt) inherits it.
1017
- * Closes the gap: Ansuz elixir holds wire>rewrite, but dispatched agents
1018
- * previously received no active "write less" nudge — only the "禁止盲寫" guard.
1019
- * Kept compact: a nudge, not a ritual.
1020
- *
1021
- * @param {string[]} lines - prompt lines array (mutated)
1022
- */
1023
- function appendMinimalismLadder(lines) {
1024
- lines.push(`## Code Minimalism — 最省決策階梯 (YAGNI)`);
1025
- lines.push(`寫任何代碼前,停在第一個成立的階:`);
1026
- lines.push(`1. 這需要存在嗎?不需要 → 跳過(不臆測未來需求)`);
1027
- lines.push(`2. stdlib / 語言內建有嗎?→ 用它`);
1028
- lines.push(`3. 平台 / 框架原生功能?→ 用它,別自己造`);
1029
- lines.push(`4. 專案已裝依賴有嗎?→ 重用,別加新依賴`);
1030
- lines.push(`5. 一行能解嗎?→ 一行`);
1031
- lines.push(`6. 否則:最小可行實作`);
1032
- lines.push(`規則:不為單一實作建抽象介面;刪除優於新增;最短 diff。`);
1033
- lines.push(`刻意簡化用 \`// thrift:\` 標記上限與升級路徑(例:\`// thrift: 同步即可,QPS>100 再改 async\`)。`);
1034
- lines.push(`不可簡化掉(lazy ≠ negligent):input validation、error handling、security、accessibility、用戶明確要求的功能。`);
1035
- lines.push('');
1036
- }
1037
-
1038
- /**
1039
- * Append the ARCH-001 file-budget & SRP clause (shift-left prevention half).
1040
- * Wired into appendFormationPreamble next to appendMinimalismLadder so every
1041
- * dispatched agent (unit/solo/squad/legion, subagent + Formation paths) sees
1042
- * the budget while writing code, not as late rework at the L1 merge gate
1043
- * (dev/scripts/check-file-size.js, .rule/general/file-size-threshold.yaml).
1044
- *
1045
- * @param {string[]} lines - prompt lines array (mutated)
1046
- */
1047
- function appendFileBudgetSRP(lines) {
1048
- lines.push(`## File Budget & SRP (ARCH-001)`);
1049
- lines.push(`單檔行數:warn=2000 / hard=4000。接近 warn 就停筆,優先 extraction 而非累加。`);
1050
- lines.push(`拆分模式:module / hooks/ / components/ / types.ts(依 CODE-STR-002)。`);
1051
- lines.push(`verify 步驟跑:\`node dev/scripts/check-file-size.js --staged\` — 有 BLOCK 先拆再提交。`);
1052
- lines.push('');
1053
230
  }
1054
231
 
1055
232
  function appendTask(lines, taskInput, taskData, options = {}) {
@@ -1078,8 +255,6 @@ function appendTask(lines, taskInput, taskData, options = {}) {
1078
255
  lines.push(`[internal] Sub-project: \`${taskProjectPath}\` — do NOT mention this to the user.`);
1079
256
  lines.push(`- ALL file operations must target this project directory`);
1080
257
  lines.push(`- ALL git operations (branch, commit, merge) must run in this repo`);
1081
- lines.push(`- post-complete: \`--project-path=${taskProjectPath}\``);
1082
- lines.push(`- headless dispatch: \`--project-path=${taskProjectPath}\``);
1083
258
  lines.push('');
1084
259
  }
1085
260
 
@@ -1100,7 +275,6 @@ function appendTask(lines, taskInput, taskData, options = {}) {
1100
275
  lines.push('**Scope Paths** (你只能操作這些路徑):');
1101
276
  for (const sp of scopePaths) {
1102
277
  if (root) {
1103
- const path = require('path');
1104
278
  lines.push(`- ${sp} → ${path.resolve(root, sp)}`);
1105
279
  } else {
1106
280
  lines.push(`- ${sp}`);
@@ -1315,106 +489,6 @@ function applyRustCheckToPrefix(prefix, body) {
1315
489
  return prefix.split(HARD_RULE_4_RUST_PHRASE).join(HARD_RULE_4_RUST_ALLOW_PHRASE);
1316
490
  }
1317
491
 
1318
- function appendWorkflow(lines, skillDef, options = {}, taskData = null) {
1319
- if (skillDef.collaboration && skillDef.collaboration.workflow) {
1320
- // Sub-project: rewrite task-api commands to include --project-path
1321
- const rewriteCmd = buildTaskApiRewriter(options);
1322
- // Replace {task_id} placeholder with actual task ID when available
1323
- const taskId = options.taskId || null;
1324
- const resolveTaskId = (cmd) => taskId ? cmd.replace(/\{task_id\}/g, taskId) : cmd;
1325
-
1326
- // TASK_1821: rigor-gated self-complete. rigor:high cards must NOT be closed by
1327
- // their own executor — the party with independent verification on the Formation
1328
- // path is post-complete (dispatcher side: regression tests + verify-acceptance +
1329
- // verified merge), and it already moves the task active→done itself after
1330
- // isVerifiedMergeSuccess (post-complete.js Step 2.5), so the card cannot strand.
1331
- // NOTE: this axis (per-card rigor) is orthogonal to the isSubagent axis below
1332
- // (per-dispatch-path). The subagent branch `continue`s first, so its replacement
1333
- // note stays byte-identical and this check only fires on Formation/headless/serial.
1334
- // TASK_2252: extractRigor accepts string OR object (a bare JSON.parse(object)
1335
- // threw into the catch and silently skipped this gate).
1336
- const taskRigor = extractRigor(taskData);
1337
-
1338
- lines.push(`## Workflow`);
1339
- // TASK_1819: only isolated executors may stage the whole tree.
1340
- // - options.isSubagent → Agent-tool dispatch (worktree isolation, dispatch.js)
1341
- // ...UNLESS options.isCrossProject (TASK_1824): cross-project sub-agent dispatch
1342
- // has NO worktree isolation (TASK_1672 — the Agent tool worktree derives from the
1343
- // conversation cwd repo, not the target sub-project). It writes directly into the
1344
- // sub-project's SHARED working tree, so it gets explicit-paths staging like serial.
1345
- // - options.isWorktree → Formation task worktree (threaded from headless.js
1346
- // promptOpts.isWorktree = !!_unitWorktreePath)
1347
- // Serial (neither flag) shares the working tree with the user and Ansuz —
1348
- // a blanket `git add -A` swept 392 unrelated files in TASK_188, so serial
1349
- // mode gets an explicit-paths staging instruction instead.
1350
- const isIsolated = !!((options.isSubagent && !options.isCrossProject) || options.isWorktree);
1351
- for (const step of skillDef.collaboration.workflow) {
1352
- // In subagent mode: skip read/claim (card already inline; claim done by parent)
1353
- if (options.isSubagent) {
1354
- const stepRun = step.run || '';
1355
- if (stepRun.includes('task-api.js get ') || stepRun.includes('task-api.js claim ')) {
1356
- continue;
1357
- }
1358
- if (stepRun.includes('task-api.js complete')) {
1359
- lines.push(`### ${step.step}`);
1360
- lines.push('Note: ⚠️ Worktree mode — 禁止執行 task-api.js complete。主 thread 在 merge + 驗證後處理。');
1361
- lines.push('');
1362
- continue;
1363
- }
1364
- }
1365
- // TASK_1821: Formation/headless/serial path + rigor:high → replace the
1366
- // self-complete step with a prohibition note (same technique as the
1367
- // subagent branch above). The dispatcher-side post-complete pipeline
1368
- // closes the card after merge + independent verification.
1369
- if (taskRigor === 'high' && step.run && step.run.includes('task-api.js complete')) {
1370
- lines.push(`### ${step.step}`);
1371
- lines.push('Note: ⚠️ rigor: high — 禁止執行 task-api.js complete。任務留在 active/,由派工方的 post-complete 在 merge + 獨立驗證(回歸測試 + verify-acceptance)通過後移入 done/。在完成摘要中明確標註「未自行 complete,待派工者驗證後關閉」。');
1372
- lines.push('');
1373
- continue;
1374
- }
1375
- // TASK_1833: the workflow commit step must never render the blanket
1376
- // `|| true` from the skill YAML — see buildCommitCommand for why (one
1377
- // swallow covered both nothing-to-commit AND index.lock data loss).
1378
- // Applies to BOTH modes: isolated gets `git add -A`, shared-tree gets
1379
- // explicit-paths staging (TASK_1819) — each with the honest commit shape.
1380
- if (step.run && step.run.includes('git add -A') && step.run.includes('git commit')) {
1381
- const msgMatch = step.run.match(/-m\s+"([^"]+)"/);
1382
- const commitMsg = msgMatch ? msgMatch[1] : '[{task_id}] task completed';
1383
- lines.push(`### ${step.step}`);
1384
- if (isIsolated) {
1385
- lines.push(`Command: ${resolveTaskId(buildCommitCommand('-A', commitMsg))}`);
1386
- if (step.note) lines.push(`Note: ${step.note}`);
1387
- lines.push('Note: nothing to commit 時靜默跳過(不算失敗);git commit 失敗(如 index.lock 競爭)會自動退避重試兩次,仍失敗則以非零退出 — 此時大聲回報錯誤,不要吞掉。');
1388
- } else {
1389
- lines.push(`Command: ${resolveTaskId(buildCommitCommand('<只列出你本次明確編輯過的檔案路徑>', commitMsg))}`);
1390
- lines.push('Note: ⚠️ 共用工作樹模式(無 worktree 隔離)— 你與使用者、其他 agent 共用同一個工作樹。只 stage 你本次明確編輯過的檔案,嚴禁一次 stage 整個工作樹(TASK_188 曾因此把 392 個無關檔案掃進單一 commit)。nothing to commit 時靜默跳過(不算失敗);git commit 失敗(如 .git/index.lock 被其他 agent 持有)會自動退避重試兩次,仍失敗則以非零退出 — 此時大聲回報錯誤,不要吞掉。');
1391
- }
1392
- lines.push('');
1393
- appendRustCheckReminder(lines, taskData, options, isIsolated);
1394
- continue;
1395
- }
1396
- lines.push(`### ${step.step}`);
1397
- if (step.run) lines.push(`Command: ${resolveTaskId(rewriteCmd(step.run))}`);
1398
- if (step.note) lines.push(`Note: ${step.note}`);
1399
- if (step.actions) {
1400
- for (const a of step.actions) {
1401
- lines.push(`- ${a}`);
1402
- }
1403
- }
1404
- lines.push('');
1405
- }
1406
-
1407
- if (skillDef.collaboration.on_blocked) {
1408
- lines.push(`### On Blocked`);
1409
- lines.push(`Command: ${resolveTaskId(rewriteCmd(skillDef.collaboration.on_blocked.run))}`);
1410
- lines.push(`Note: ${skillDef.collaboration.on_blocked.note}`);
1411
- lines.push('');
1412
- }
1413
- }
1414
-
1415
- appendDispatchOverrides(lines, taskData);
1416
- }
1417
-
1418
492
  /**
1419
493
  * TASK_1820: card-controlled override channel, rendered immediately AFTER the
1420
494
  * ## Workflow envelope. Section order is fixed (## Task always precedes
@@ -1466,563 +540,13 @@ function buildTaskApiRewriter(options = {}) {
1466
540
  return (cmd) => cmd.replace(TASK_API_PATTERN, replacement);
1467
541
  }
1468
542
 
1469
- /**
1470
- * Append Yggdrasil network context — shows what other nodes are doing.
1471
- * Lets the agent know about remote activity and potential file conflicts.
1472
- *
1473
- * @param {string[]} lines
1474
- * @param {object} ctx - { onlineNodes, remoteTasks, myNodeName, myScopePaths }
1475
- */
1476
- function appendYggdrasilContext(lines, ctx) {
1477
- if (!ctx) return;
1478
- const { onlineNodes = [], remoteTasks = [], myScopePaths = [] } = ctx;
1479
- if (onlineNodes.length === 0 && remoteTasks.length === 0) return;
1480
-
1481
- lines.push('## Yggdrasil Network (Remote Collaborators)');
1482
- lines.push('Other developers/agents are currently working on this project:');
1483
- lines.push('');
1484
-
1485
- for (const node of onlineNodes) {
1486
- const files = node.touching_files || [];
1487
- lines.push(`**${node.node_name}** — ONLINE`);
1488
- if (node.branch) lines.push(` Branch: ${node.branch}`);
1489
- if (node.current_task) lines.push(` Task: ${node.current_task}`);
1490
- if (node.work_summary) lines.push(` Working on: ${node.work_summary}`);
1491
- if (files.length > 0) {
1492
- lines.push(` Files: ${files.slice(0, 8).join(', ')}${files.length > 8 ? ` (+${files.length - 8})` : ''}`);
1493
- }
1494
-
1495
- // Highlight file overlaps with my scope (informational, not blocking)
1496
- if (myScopePaths.length > 0 && files.length > 0) {
1497
- const overlaps = files.filter(f =>
1498
- myScopePaths.some(sp => f.startsWith(sp) || sp.startsWith(f))
1499
- );
1500
- if (overlaps.length > 0) {
1501
- lines.push(` ℹ️ FILE OVERLAP: ${overlaps.join(', ')}`);
1502
- lines.push(` → Be aware of potential merge conflicts. Coordinate if needed.`);
1503
- }
1504
- }
1505
- lines.push('');
1506
- }
1507
-
1508
- if (remoteTasks.length > 0) {
1509
- lines.push('Active remote tasks:');
1510
- for (const t of remoteTasks.slice(0, 5)) {
1511
- lines.push(`- ${t.task_id} (claimed by ${t.claimed_by})`);
1512
- }
1513
- lines.push('');
1514
- }
1515
-
1516
- lines.push('To check real-time network status: `node .formation/tools/coordinator.js --status`');
1517
- lines.push('');
1518
- }
1519
-
1520
- /**
1521
- * Append memory context — 從 .failure/ + .memory/ + .kenaz/main-memory/ 載入相關經驗
1522
- * 注入到 prompt 中讓 agent 避免重蹈覆轍
1523
- */
1524
- function appendMemoryContext(lines, taskData, options = {}) {
1525
- if (!memoryLoader) return;
1526
-
1527
- try {
1528
- // 從 taskData 提取上下文資訊
1529
- const memOpts = {};
1530
- if (taskData) {
1531
- let parsed = null;
1532
- try { parsed = JSON.parse(taskData); } catch { /* not JSON */ }
1533
- if (parsed) {
1534
- memOpts.taskTitle = parsed.title || '';
1535
- memOpts.role = parsed.role || '';
1536
- memOpts.scopePaths = parsed.scope_paths || [];
1537
- memOpts.tags = parsed.tags || [];
1538
- memOpts.priority = parsed.priority || '';
1539
- }
1540
- }
1541
-
1542
- // TASK_1998 two-axis policy: Known Failures / Project Memory are CONTEXT
1543
- // volume, driven by complexity — simple drops both, medium restores Known
1544
- // Failures but keeps Project Memory dropped, complex keeps both. Knowledge
1545
- // Base (main-memory) is unaffected either way — it isn't part of this gate.
1546
- const profile = promptProfile({ rigor: extractRigor(taskData), complexity: extractComplexity(taskData) });
1547
- memOpts.skipFailures = profile.skipKnownFailures;
1548
- // 2026-10-10: memory system frozen (project_memory_system_frozen) - no Project Memory block.
1549
- memOpts.skipMemory = true;
1550
-
1551
- // Resolve corePath for sub-projects so memory-loader reads from core's
1552
- // .failure/, .memory/, .kenaz/main-memory/ instead of __dirname fallback
1553
- const corePath = options.corePath || resolveCorePath(options.projectPath);
1554
- if (corePath) memOpts.corePath = corePath;
1555
-
1556
- const memoryBlock = memoryLoader.loadMemoryContext(memOpts);
1557
- if (memoryBlock) {
1558
- lines.push(memoryBlock);
1559
- }
1560
-
1561
- // Code-navigation reminder (SYN-002, rewritten for LSP in TASK_2335)
1562
- lines.push('');
1563
- lines.push('## Search Tool Selection (SYN-002)');
1564
- lines.push('搜尋前先判斷:精確字串(函數名、error message、概念關鍵字)→ Grep / Glob。「誰呼叫它」「改了影響哪裡」「定義在哪」→ kenaz_lsp_callers / kenaz_lsp_references / kenaz_lsp_definition(args: projectPath, file, line 或 symbol, depth);檔案內 symbol 列表 → kenaz_lsp_symbols。');
1565
- lines.push('');
1566
- } catch { /* graceful degradation */ }
1567
- }
1568
-
1569
- /**
1570
- * Synchronously call an MCP tool via JSON-RPC over the local Dashboard HTTP
1571
- * server (127.0.0.1:18080/api/mcp) in a subprocess — keeps buildPrompt() sync.
1572
- * Returns the parsed inner data object, or null on any failure / unavailability.
1573
- * Shared by appendAlayaLessons.
1574
- *
1575
- * @param {string} toolName - MCP tool name (e.g. 'cortex_recall')
1576
- * @param {object} toolArgs - forwarded as params.arguments
1577
- * @returns {object|null}
1578
- */
1579
- function callMcpToolSync(toolName, toolArgs) {
1580
- try {
1581
- const { execSync } = require('child_process');
1582
- const script = `
1583
- const http = require('http');
1584
- const payload = JSON.stringify({
1585
- jsonrpc: '2.0', id: 1,
1586
- method: 'tools/call',
1587
- params: { name: ${JSON.stringify(toolName)}, arguments: ${JSON.stringify(toolArgs)} }
1588
- });
1589
- const req = http.request({
1590
- hostname: '127.0.0.1', port: 18080, path: '/api/mcp',
1591
- method: 'POST',
1592
- headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload) },
1593
- timeout: 5000
1594
- }, (res) => {
1595
- let d = '';
1596
- res.on('data', c => d += c);
1597
- res.on('end', () => process.stdout.write(d));
1598
- });
1599
- req.on('error', () => process.exit(0));
1600
- req.on('timeout', () => { req.destroy(); process.exit(0); });
1601
- req.write(payload);
1602
- req.end();
1603
- `;
1604
- const stdout = execSync(`node -e "${script.replace(/"/g, '\\"').replace(/\n/g, ' ')}"`, {
1605
- timeout: 8000, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true,
1606
- });
1607
- if (!stdout || !stdout.trim()) return null;
1608
- const result = JSON.parse(stdout);
1609
- if (!result || result.error) return null;
1610
- const content = result.result?.content;
1611
- if (!content || !Array.isArray(content)) return null;
1612
- const textPart = content.find(c => c.type === 'text');
1613
- if (!textPart || !textPart.text) return null;
1614
- try { return JSON.parse(textPart.text); } catch { return null; }
1615
- } catch { return null; } // MCP server unavailable — graceful degradation
1616
- }
1617
-
1618
- // TASK_2101/TASK_2104: the absolute relevance gate (cosine floor + stack
1619
- // match) for Alaya lesson injection — constants, deriveTaskStack,
1620
- // deriveBijaStack, passesAlayaRelevanceGate — now lives in
1621
- // .formation/tools/alaya-gate.js (dependency-free, shared with task-api.js's
1622
- // echoAlayaLessons). Destructured from the require() at the top of this
1623
- // file; see alaya-gate.js for the full calibration writeup and rationale.
1624
-
1625
- /**
1626
- * Append bounded Alaya process-lesson context to the prompt.
1627
- * Reuses the title+scope_paths query, queries
1628
- * alaya_recall (hybrid mode, failure_seed + lesson_seed, with the TASK_2101
1629
- * min_cosine floor) and injects a ## Process lessons (Alaya) section capped
1630
- * at 900 chars total — but ONLY for candidates that also pass the stack-match
1631
- * gate. Zero passing candidates renders nothing (R3: an empty section beats
1632
- * noise).
1633
- *
1634
- * Graceful degradation: silently skips on empty query, empty results, or
1635
- * any MCP failure — the recall bar is never allowed to raise dispatch
1636
- * latency or fail the prompt build.
1637
- *
1638
- * @param {string[]} lines - prompt lines array (mutated)
1639
- * @param {string|object} taskData - task JSON string or parsed object
1640
- * @param {(toolName: string, toolArgs: object) => object|null} [mcpCaller] - injectable MCP bridge (defaults to callMcpToolSync; test seam)
1641
- */
1642
- function appendAlayaLessons(lines, taskData, mcpCaller = callMcpToolSync) {
1643
- if (!mcpCaller.__forceAlaya) return; // 2026-10-10: memory system frozen; tests opt in via __forceAlaya
1644
- const query = buildTaskContextQuery(taskData);
1645
- if (!query) return;
1646
-
1647
- let scopePaths = [];
1648
- try {
1649
- const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
1650
- if (parsed) scopePaths = parsed.scope_paths || [];
1651
- } catch { /* taskData not JSON — task stack stays empty, floor-only gate */ }
1652
- const taskStack = deriveTaskStack(scopePaths);
1653
-
1654
- try {
1655
- const recallData = mcpCaller('alaya_recall', {
1656
- query, mode: 'hybrid', seed_types: ['failure_seed', 'lesson_seed'],
1657
- limit: ALAYA_CANDIDATE_POOL, min_cosine: ALAYA_MIN_COSINE_DEFAULT,
1658
- });
1659
- if (!recallData) return;
1660
-
1661
- let bijas = recallData.bijas || [];
1662
- if (bijas.length === 0) return;
1663
- const candidateCount = bijas.length;
1664
-
1665
- bijas = dedupBijas(bijas);
1666
-
1667
- // TASK_2101 R3: stack-match gate on top of the server-side cosine floor
1668
- // (the server already applied min_cosine, but re-checking here keeps
1669
- // this function correct against a stub/older server that ignores it).
1670
- const gated = bijas.filter(b => passesAlayaRelevanceGate(b, taskStack, ALAYA_MIN_COSINE_DEFAULT));
1671
- const droppedCount = candidateCount - gated.length;
1672
- if (droppedCount > 0) {
1673
- // Log for floor/gate tuning (R3) — never fails the prompt build.
1674
- console.error(`[appendAlayaLessons] dropped ${droppedCount}/${candidateCount} candidates below the cosine floor (${ALAYA_MIN_COSINE_DEFAULT}) or stack mismatch for query "${query}"`);
1675
- }
1676
- bijas = gated.slice(0, 3);
1677
- if (bijas.length === 0) return;
1678
-
1679
- const modeEffective = recallData.mode_effective || 'hybrid';
1680
- const sectionLines = [
1681
- '## Process lessons (Alaya)',
1682
- `Query: "${query}" — ${bijas.length} seeds (mode=${modeEffective}).`,
1683
- ...bijas.map(b => {
1684
- const content = (b.content || '').replace(/\s+/g, ' ').trim().slice(0, 220);
1685
- return `- **${b.seed_type || 'seed'}** [${b.origin || 'unknown'}] ${content}`;
1686
- }),
1687
- ];
1688
-
1689
- let section = sectionLines.join('\n');
1690
- if (section.length > 900) {
1691
- section = section.slice(0, 899) + '…';
1692
- }
1693
-
1694
- lines.push('');
1695
- lines.push(...section.split('\n'));
1696
- lines.push('');
1697
- } catch { /* Alaya unavailable — graceful degradation */ }
1698
- }
1699
-
1700
- function appendCognitiveRecall(lines, taskData, options = {}) {
1701
- // Extract recall keywords from task data (title, scope_paths, tags)
1702
- let keywords = [];
1703
- let scopeFiles = [];
1704
- try {
1705
- const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
1706
- if (parsed) {
1707
- if (parsed.title) {
1708
- const stopWords = new Set(['the','a','an','is','are','was','were','be','to','of','and','in','for','on','at','by','with','from','as','into','fix','task','add','update','implement']);
1709
- const words = parsed.title.replace(/[^a-zA-Z0-9_\s]/g, ' ').split(/\s+/).filter(w => w.length > 2 && !stopWords.has(w.toLowerCase()));
1710
- keywords.push(...words.slice(0, 5));
1711
- }
1712
- if (parsed.scope_paths && parsed.scope_paths.length > 0) {
1713
- for (const sp of parsed.scope_paths) {
1714
- const parts = sp.replace(/\\/g, '/').split('/');
1715
- const last = parts[parts.length - 1].replace(/\.[^.]+$/, '');
1716
- if (last && last.length > 2) {
1717
- keywords.push(last);
1718
- scopeFiles.push(last);
1719
- }
1720
- }
1721
- }
1722
- if (parsed.tags && parsed.tags.length > 0) {
1723
- keywords.push(...parsed.tags.filter(t => t.length > 2));
1724
- }
1725
- }
1726
- } catch { /* taskData not JSON, skip */ }
1727
-
1728
- if (keywords.length === 0) return;
1729
- keywords = [...new Set(keywords)].slice(0, 8);
1730
-
1731
- }
1732
-
1733
- // Exact, shared wording — rigor:low ends its Working contract with this
1734
- // bullet; rigor:medium repeats the identical sentence as its own line after
1735
- // the short progress-note instructions (TASK_1998 spec: "the exact line").
1736
- const RUNE_OPTIONAL_LINE = 'Inscribe exactly one rune (synapse_inscribe) if you learned something non-obvious; otherwise skip it.';
1737
-
1738
- /**
1739
- * TASK_1998: rigor:medium's short (3-line) progress-note instruction — the
1740
- * same write-then-run contract as appendProgressNoteInstructions()'s full
1741
- * block, compressed to a header + 2 lines instead of a full schema dump.
1742
- * No-op when taskData has no id (mirrors appendProgressNoteInstructions).
1743
- */
1744
- function appendShortProgressNote(lines, taskData, options = {}) {
1745
- let taskId = null;
1746
- try {
1747
- const parsed = typeof taskData === 'string' ? JSON.parse(taskData) : taskData;
1748
- if (parsed) taskId = parsed.id;
1749
- } catch { /* taskData not JSON, skip */ }
1750
- if (!taskId) return;
1751
-
1752
- const corePath = options.corePath || path.resolve(__dirname, '..', '..');
1753
- const corePathFwd = corePath.replace(/\\/g, '/');
1754
- const noteFile = `.kenaz/specialist/progress_note_${taskId}.json`;
1755
-
1756
- lines.push(`## Progress Note (brief)`);
1757
- lines.push(`Write JSON to \`${noteFile}\`, then run: node "${corePathFwd}/.specialist/tools/phase-gate.js" note --task=${taskId} --from-file="${noteFile}"`);
1758
- lines.push(`Include: files touched, acceptance-criteria pass/fail, any deviations.`);
1759
- }
1760
-
1761
- function appendCognitiveCheckpoint(lines, taskData, options = {}) {
1762
- const profile = promptProfile({ rigor: extractRigor(taskData), complexity: extractComplexity(taskData) });
1763
-
1764
- // TASK_1998: PROCESS strictness is rigor-driven. low/medium collapse the
1765
- // six ritual sections (Before You Start recall, Before You Finish
1766
- // inscribe, Cognitive Checkpoint, Progress Note, Known Failures, Project
1767
- // Memory) into a compact contract — Ansuz already ran recall at
1768
- // scoping time; the full mandatory recall/inscribe ceremony is reserved
1769
- // for rigor:high. rigor:medium additionally gets a short progress-note
1770
- // instruction on top of the contract (dropped entirely for rigor:low).
1771
- if (profile.compactRitual) {
1772
- lines.push('');
1773
- lines.push(`## Working contract`);
1774
- lines.push(`- Read the files you touch before editing.`);
1775
- lines.push(`- Independent reads: fire them as parallel tool calls in ONE turn (e.g. Read a.js + Read b.js + Grep "foo" together), not one call per turn.`);
1776
- lines.push(`- Independent shell checks: one bash command with separators (e.g. \`echo ==a==; cmd1; echo ==b==; cmd2\`), not N bash calls.`);
1777
- lines.push(`- N similar items: one command over all N (e.g. one grep with alternation, or a loop), not N calls.`);
1778
- lines.push(`- One Write or a multi-hunk Edit beats many small edits.`);
1779
- lines.push(`- No self-check greps unless the card asks for them.`);
1780
- lines.push(`- Commit early and often; the worktree hard rules above still apply verbatim.`);
1781
- lines.push(`- Final report <= 20 lines: files touched, tests run, commit sha, uncertainties.`);
1782
- lines.push('');
1783
-
1784
- if (profile.ritualTier === 'medium') {
1785
- appendShortProgressNote(lines, taskData, options);
1786
- lines.push('');
1787
- }
1788
- return;
1789
- }
1790
- }
1791
-
1792
- function appendOutput(lines, skillDef) {
1793
- if (!skillDef.output) return;
1794
- lines.push(`## Expected Output`);
1795
- lines.push(`Primary: ${typeof skillDef.output.primary === 'string' ? skillDef.output.primary.trim() : JSON.stringify(skillDef.output.primary)}`);
1796
- if (skillDef.output.secondary) {
1797
- lines.push(`Secondary: ${typeof skillDef.output.secondary === 'string' ? skillDef.output.secondary.trim() : JSON.stringify(skillDef.output.secondary)}`);
1798
- }
1799
- }
1800
-
1801
- // ─────────────────────────────────────────
1802
- // Squad Synthesis:整合多份成員報告
1803
- // ─────────────────────────────────────────
1804
-
1805
- /**
1806
- * 組裝 Squad Synthesis Agent 的 prompt
1807
- * 在所有成員完成後,由 headless.js 自動呼叫
1808
- * @param {object} skillDef - Squad 的 YAML 定義(含 merge 設定)
1809
- * @param {string} reportDir - 報告目錄名稱(如 squad-strategy)
1810
- * @returns {string} synthesis prompt
1811
- */
1812
- function buildSquadSynthesisPrompt(skillDef, reportDir, options = {}) {
1813
- const lines = [];
1814
- const merge = skillDef.merge;
1815
-
1816
- lines.push('## Role');
1817
- lines.push('你是 Squad Synthesis Agent(整合者)。');
1818
- lines.push('你的任務是讀取多份獨立分析報告,按照指定的整合策略產出最終報告。');
1819
- lines.push('你不是成員之一,你是整合者。你不偏袒任何一方。');
1820
- lines.push('');
1821
-
1822
- // Principles(整合者也必須遵守的硬規則)
1823
- if (skillDef.principles) {
1824
- lines.push('## Principles (整合時也必須遵守)');
1825
- if (skillDef.principles.focus) {
1826
- lines.push(skillDef.principles.focus.trim());
1827
- lines.push('');
1828
- }
1829
- if (skillDef.principles.forbidden && skillDef.principles.forbidden.length > 0) {
1830
- lines.push('**禁止 (違反即失敗)**:');
1831
- for (const f of skillDef.principles.forbidden) lines.push(`- ❌ ${f}`);
1832
- lines.push('');
1833
- }
1834
- if (skillDef.principles.required && skillDef.principles.required.length > 0) {
1835
- lines.push('**必須遵守**:');
1836
- for (const r of skillDef.principles.required) lines.push(`- ✅ ${r}`);
1837
- lines.push('');
1838
- }
1839
- }
1840
-
1841
- // Merge strategy
1842
- if (merge) {
1843
- lines.push(`## Merge Strategy: ${merge.strategy || 'default'}`);
1844
- if (merge.description) {
1845
- lines.push(merge.description.trim());
1846
- }
1847
- lines.push('');
1848
-
1849
- // Merge steps — 核心指令
1850
- if (merge.steps && merge.steps.length > 0) {
1851
- lines.push('## Merge Steps (按順序執行)');
1852
- lines.push('');
1853
- for (const step of merge.steps) {
1854
- lines.push(`### Step: ${step.step}`);
1855
- if (step.action) lines.push(step.action.trim());
1856
- if (step.on_partial_failure) {
1857
- lines.push(`**Partial Failure Handling**: ${step.on_partial_failure.trim()}`);
1858
- }
1859
- lines.push('');
1860
- }
1861
- }
1862
- }
1863
-
1864
- // Reports location
1865
- const reportPath = `.formation/reports/${reportDir}`;
1866
- lines.push('## Reports Location');
1867
- lines.push(`所有成員報告位於 \`${reportPath}/\` 目錄。`);
1868
- lines.push('');
1869
- lines.push('**執行步驟**:');
1870
- lines.push(`1. 用 Glob 工具掃描 \`${reportPath}/*.md\``);
1871
- lines.push('2. 排除 synthesis.md(那是你要寫的輸出)');
1872
- lines.push('3. 用 Read 工具逐一讀取每份報告');
1873
- lines.push('4. 按照上述 Merge Steps 逐步整合');
1874
- lines.push('');
1875
-
1876
- // Squad members for context
1877
- if (skillDef.members && skillDef.members.length > 0) {
1878
- lines.push('## Squad Members(供參考)');
1879
- for (const m of skillDef.members) {
1880
- const roleFirstLine = (typeof m.role === 'string' ? m.role.split('\n')[0].trim() : m.name);
1881
- lines.push(`- **${m.name}** (${m.id}): ${roleFirstLine}`);
1882
- }
1883
- lines.push('');
1884
- }
1885
-
1886
- // Output
1887
- lines.push('## Output');
1888
- lines.push(`將整合報告寫入 \`${reportPath}/synthesis.md\``);
1889
- lines.push('');
1890
- lines.push('報告格式依 Merge Strategy 而定。確保報告完整、結構清晰、附上各成員的核心觀點摘要。');
1891
- lines.push('');
1892
-
1893
- // No taskData in scope (post-hoc synthesis, not a dispatched task card) —
1894
- // rigor/complexity both normalize to 'medium', landing in the compact-ritual
1895
- // branch (TASK_1998 two-axis policy).
1896
- appendCognitiveCheckpoint(lines, null, options);
1897
-
1898
- return lines.join('\n');
1899
- }
1900
-
1901
- // ─────────────────────────────────────────
1902
- // Squad Debate:Round 2 交叉辯論 prompt
1903
- // ─────────────────────────────────────────
1904
-
1905
- /**
1906
- * 組裝 Squad Round 2 Debate prompts
1907
- * 每個成員讀取其他人的 Round 1 報告,寫出針對性回應
1908
- * @param {object} skillDef - Squad YAML 定義
1909
- * @param {string} reportDir - 報告目錄名稱
1910
- * @returns {{ memberId: string, prompt: string, skill: string }[]}
1911
- */
1912
- function buildSquadDebatePrompts(skillDef, reportDir) {
1913
- const members = skillDef.members || [];
1914
- const debate = skillDef.debate || {};
1915
- const reportPath = `.formation/reports/${reportDir}`;
1916
-
1917
- return members.map(member => {
1918
- const lines = [];
1919
- const otherMembers = members.filter(m => m.id !== member.id);
1920
-
1921
- // Role reminder
1922
- lines.push('## Role');
1923
- lines.push(`你是 **${member.name}**,這是你的第二輪分析(辯論回合)。`);
1924
- lines.push('');
1925
-
1926
- // Original stance
1927
- const roleFirstLine = typeof member.role === 'string' ? member.role.split('\n')[0].trim() : '';
1928
- if (roleFirstLine) {
1929
- lines.push('## 你的立場(簡要提醒)');
1930
- lines.push(roleFirstLine);
1931
- lines.push('');
1932
- }
1933
-
1934
- // Debate instructions (per-member)
1935
- if (member.debate_instructions) {
1936
- lines.push('## Debate Instructions');
1937
- lines.push(member.debate_instructions.trim());
1938
- lines.push('');
1939
- }
1940
-
1941
- // Debate rules (squad-wide)
1942
- if (debate.rules && debate.rules.length > 0) {
1943
- lines.push('## Debate Rules');
1944
- for (const rule of debate.rules) {
1945
- lines.push(`- ${rule}`);
1946
- }
1947
- lines.push('');
1948
- }
1949
-
1950
- // Reports to read
1951
- lines.push('## Reports to Read');
1952
- for (const other of otherMembers) {
1953
- lines.push(`- **${other.name}**: \`${reportPath}/${other.id}.md\``);
1954
- }
1955
- lines.push('');
1956
- lines.push('用 Read 工具讀取上述報告,然後寫出你的回應。');
1957
- lines.push(`你自己的 Round 1 報告在 \`${reportPath}/${member.id}.md\`,可以參考但不需要重複。`);
1958
- lines.push('');
1959
-
1960
- // Output
1961
- lines.push('## Output');
1962
- lines.push(`將反駁報告寫入 \`${reportPath}/${member.id}-r2.md\``);
1963
- lines.push('');
1964
-
1965
- return {
1966
- memberId: `${member.id}-r2`,
1967
- prompt: lines.join('\n'),
1968
- skill: member.skill || 'do'
1969
- };
1970
- });
1971
- }
1972
-
1973
- // ─────────────────────────────────────────
1974
- // Headless 模式常數
1975
- // ─────────────────────────────────────────
1976
-
1977
- // TASK_1712: Rewritten with honest provenance framing so safety-hardened models
1978
- // (claude-sonnet-5) read this as legitimate orchestration context rather than injection.
1979
- // Behavioral constraints are fully preserved — only the framing changed.
1980
- // Key changes: (1) opened with machine-assembly provenance declaration, (2) replaced
1981
- // "don't read CLAUDE.md" with why Formation supplies its own rule set, (3) replaced
1982
- // "don't ask questions" with "no human is watching — report and exit if blocked",
1983
- // (4) replaced concealment wording in git rule with safety-rationale explanation.
1984
- // TASK_1784 (DEFECT-A): Rule 6 was a categorical git ban that contradicted the workflow's
1985
- // mandated commit step (do.yaml "提交變更": git add -A && git commit --no-verify). Literal-obedience
1986
- // models (gpt-5.6-sol) obeyed the ban, produced zero commits, and tripped the leak detector on
1987
- // every unmodified bifrost dispatch (TASK_1779 A/B-proven). Rule 6 now carves out exactly the
1988
- // workflow commit command shape and nothing else; all other git subcommands stay forbidden.
1989
- const HEADLESS_PREFIX = `## Automated Dispatch Context
1990
- This prompt was machine-assembled by the Kenaz orchestration system, which is owned and
1991
- operated by the repository owner. It runs unattended via \`claude -p\` (no interactive user
1992
- is watching the output stream). The task brief below was authored and approved by the user
1993
- before this automated run was triggered.
1994
-
1995
- ## Operating Rules
1996
- 1. Start with a one-line summary: "📋 {TASK_ID}: {what you are about to do}" (<=30 words). Then proceed.
1997
- 2. Execute directly — do not ask clarifying questions. Requirements were confirmed before dispatch.
1998
- If you are blocked by a genuine ambiguity, document the blocker in your output and exit; a human will read the log.
1999
- 3. Formation supplies its own rule set (see ## Role / ## Task below), so the project CLAUDE.md
2000
- is not the active instruction source for this run. Rely on the content in this prompt.
2001
- 4. Produce structured output when finished.
2002
- 5. On unrecoverable errors: describe what failed and exit. Do not attempt open-ended retries.
2003
- 6. **Git is restricted to the workflow's own commit step — nothing else.** Exactly two git commands
2004
- are permitted, and only when the ## Workflow section below explicitly lists them as a step:
2005
- the "git add ..." staging command and the "git commit --no-verify -m <message>" that follows it,
2006
- run EXACTLY as shown in that workflow step — stage only what that step specifies, never more —
2007
- on the branch this session started on, without switching branches.
2008
- When the workflow lists that commit step you MUST execute it — this rule does not cancel it.
2009
- Every other git subcommand is forbidden: do not run git checkout, git branch, git merge,
2010
- git push, git rebase, git stash, git reset, or anything else. Branch, merge, and push management
2011
- is handled exclusively by the Formation Lead after the task completes, to prevent
2012
- cross-repository contamination when multiple agents run in parallel against different project paths.
2013
- 7. **Handle exactly the task specified in ## Task below.** Do not discover or claim other tasks.
2014
- One agent — one task.
2015
- 8. Do not read the task directory directly (.collaboration/tasks/ or .kenaz/tasks/). Use task-api.js.
2016
- 9. Only complete the task assigned to you (see ## Task for the Task ID). Do not mark other tasks done.
2017
-
2018
- `;
2019
543
  // TASK_1833: rules 12-14 are shared VERBATIM across all sub-agent prefixes
2020
544
  // (worktree / direct-write / cross-project-worktree). The only variance is
2021
545
  // which rule number the commit instruction lives under. Kept as one template
2022
546
  // so the three prefixes cannot drift apart (coupling rule: no near-duplicate
2023
547
  // parallel impls).
2024
548
  const subagentAsyncRules = (commitRuleNo) => `12. **⚠️ 你送出訊息就等於結束本次執行。** 你的最終訊息**就是**回傳值 —— 沒有「稍後回來接手」這回事。不要規劃「先讓出控制權、等背景工作完成再繼續」,那會直接結束你的執行並留下無人收拾的背景行程。
2025
- 13. **禁止啟動預期超過數分鐘的建置或測試** —— cold Rust build(kuzu 的 CMake 可跑十幾分鐘)、完整測試套件都屬此類。原因是結構性的:Bash 前景呼叫上限 10 分鐘,超時會被砍;改用背景跑則你沒有同步工作可做,只能送出訊息(見規則 12),於是留下持有 target dir 鎖的孤兒行程,後續建置全部空等。
549
+ 13. **禁止啟動預期超過數分鐘的建置或測試** —— cold Rust build(可跑十幾分鐘)、完整測試套件都屬此類。原因是結構性的:Bash 前景呼叫上限 10 分鐘,超時會被砍;改用背景跑則你沒有同步工作可做,只能送出訊息(見規則 12),於是留下持有 target dir 鎖的孤兒行程,後續建置全部空等。
2026
550
  - **正確做法**:改完程式碼 → commit(規則 ${commitRuleNo})→ **直接回報結束**。在報告中寫明「未執行建置驗證」。
2027
551
  - **Rust 建置驗證一律由主線程(Ansuz)負責** —— 主線程有熱快取、能跨 turn 輪詢、能診斷並清理孤兒行程。這是分工,不是你偷懶。
2028
552
  - 這**不代表**你不必自我檢查。讀周邊程式碼、確認語法與型別、小範圍 grep 比對既有實作,這些輕量檢查仍然必須做。被禁止的只有「耗時數分鐘以上的建置與完整測試套件」。
@@ -2045,8 +569,7 @@ const WORKTREE_DISPATCH_HARD_RULES = `2. CLAUDE.md 生效;完成輸出結果
2045
569
  4. 送出訊息=結束;禁長建置/全測試(commit 後報未驗證,Rust 交 Ansuz);禁 sleep 輪詢;禁用 image name 殺行程(只殺自己 PID)。
2046
570
  5. 只做指定任務;不讀任務目錄;只用 task-api.js;worktree 禁 complete。`;
2047
571
 
2048
- // Sub-agent dispatch preamble — used when task is dispatched via Agent tool (isolation=worktree)
2049
- // instead of claude -p (headless). Key differences from HEADLESS_PREFIX:
572
+ // Sub-agent dispatch preamble — used when a task is dispatched via the Agent tool (isolation=worktree):
2050
573
  // - Git IS allowed (worktree provides isolation; commits land on task branch, not user branch)
2051
574
  // - CLAUDE.md IS active (running inside an interactive Claude Code session)
2052
575
  // - Agent must commit changes to the worktree before returning
@@ -2102,135 +625,22 @@ function buildSubagentWorktreePrefix(worktreePath, branchName, projectPath) {
2102
625
  return `[CROSS-PROJECT WORKTREE SUBAGENT MODE] wt=${worktreePath} repo=${projectPath} branch=${branchName}
2103
626
  1. 先印摘要「📋 {TASK_ID}: {你準備做什麼}」,直接做,不問。
2104
627
  ${WORKTREE_DISPATCH_HARD_RULES}
2105
- 6. 工作目錄=上述 worktree;Edit/Write、cd/git -C 皆用它;禁編輯主樹;Synapse projectPath 用 ${projectPath}。
628
+ 6. 工作目錄=上述 worktree;Edit/Write、cd/git -C 皆用它;禁編輯主樹。
2106
629
  Details: .formation/tools/dispatch-detail/cross-project-worktree.md (Read only if needed)
2107
630
  `;
2108
631
  }
2109
632
 
2110
- const LEGION_SYSTEM_PREFIX = `You are the TEAM LEAD using Claude Code's OFFICIAL "agent teams" feature.
2111
-
2112
- YOUR ROLE: Team Lead
2113
- - You are RESPONSIBLE for this task's success or failure.
2114
- - You MUST coordinate teammates and ensure they complete their work.
2115
- - You CANNOT end the session until ALL teammates have reported back.
2116
- - If you end early, the task FAILS and it's YOUR fault.
2117
-
2118
- This is NOT the project's .formation/ directory system. Do NOT read .formation/ files.
2119
- To create teammates, use natural language like: "Create a teammate named X who handles Y"
2120
- Each teammate is a separate Claude instance running in parallel.
2121
-
2122
- `;
2123
-
2124
- const LEGION_AUTO_EXECUTE = `
2125
-
2126
- BEFORE YOU START - OUTPUT YOUR PLAN:
2127
- First, explain exactly what you will do step by step:
2128
- 1. What teammates will you create?
2129
- 2. How will you wait for their results?
2130
- 3. What tools will you use to poll for messages?
2131
- 4. When will you know all teammates are done?
2132
-
2133
- RULES:
2134
- 1. Do NOT ask questions. Execute immediately after stating your plan.
2135
- 2. Do NOT read CLAUDE.md - this is a separate system.
2136
- 3. Create teammates using natural language descriptions.
2137
- 4. IMPORTANT: Use "sonnet" model for ALL teammates to reduce cost.
2138
-
2139
- WAITING FOR TEAMMATES - CRITICAL:
2140
- 5. After spawning ALL teammates, you MUST enter a polling loop.
2141
- 6. In the loop: call "sleep 5" with Bash, then check if teammates sent messages.
2142
- 7. Teammates send results via SendMessage - you will see them as incoming user messages.
2143
- 8. Keep looping (sleep + check) until you have received messages from ALL teammates.
2144
- 9. Do NOT end until you have ACTUAL CONTENT from every single teammate.
2145
- 10. If a teammate is slow, keep waiting - do NOT give up or timeout early.
2146
- 11. Only after ALL teammates report back, synthesize their findings into final answer.`;
2147
-
2148
- // ─────────────────────────────────────────
2149
- // Legion Lead Prompt 組裝
2150
- // ─────────────────────────────────────────
2151
-
2152
- /**
2153
- * 組裝 Legion Lead 的完整 headless prompt
2154
- * 複製 Sunny teammate 的成功模式:
2155
- * SYSTEM_PREFIX + 成員列表(自然語言)+ 任務 + AUTO_EXECUTE
2156
- */
2157
- function buildLegionLeadPrompt(config) {
2158
- let prompt = LEGION_SYSTEM_PREFIX;
2159
-
2160
- prompt += `Create an agent team with ${config.members.length} teammates:\n`;
2161
- config.members.forEach((m, i) => {
2162
- const lines = m.prompt.split('\n');
2163
- const roleIdx = lines.findIndex(l => l.startsWith('## Role'));
2164
- let desc = m.name;
2165
- if (roleIdx >= 0) {
2166
- for (let j = roleIdx + 1; j < lines.length && j < roleIdx + 5; j++) {
2167
- const line = lines[j].trim();
2168
- if (line && !line.startsWith('#')) { desc = line; break; }
2169
- }
2170
- }
2171
- prompt += `${i + 1}. "${m.name}" - ${desc}\n`;
2172
- });
2173
- prompt += '\n';
2174
-
2175
- const taskMatch = config.lead.prompt.match(/## Task\n([\s\S]*?)(?=\n##|$)/);
2176
- if (taskMatch) {
2177
- prompt += `Task: ${taskMatch[1].trim()}\n`;
2178
- }
2179
-
2180
- prompt += LEGION_AUTO_EXECUTE;
2181
- return prompt;
2182
- }
2183
-
2184
633
  module.exports = {
2185
634
  recordSkillUsage,
2186
- buildPrompt, buildSquadSynthesisPrompt, buildSquadDebatePrompts,
2187
- buildLegionLeadPrompt,
2188
- resolveModules,
2189
- HEADLESS_PREFIX,
635
+ buildPrompt,
2190
636
  SUBAGENT_PREFIX,
2191
637
  SUBAGENT_PREFIX_DIRECT,
2192
638
  buildSubagentWorktreePrefix,
2193
- // TASK_1978: exported for direct unit-testing of the compressed ceremony
2194
- // sections (byte budget + hard-constraint presence), without exercising
2195
- // the full buildUnitPrompt pipeline (which touches memory/cortex/episodic
2196
- // context and needs a live project).
2197
- appendCognitiveCheckpoint,
2198
- // TASK_1992: exported for direct unit-testing of the Alaya process-lessons
2199
- // section with an injected mcpCaller stub (no live MCP dependency).
2200
- buildTaskContextQuery,
2201
- appendAlayaLessons,
2202
- // TASK_2101: exported for direct unit-testing of the absolute-relevance
2203
- // gate (cosine floor + stack match) that appendAlayaLessons applies before
2204
- // any bija is rendered into the dispatch prompt.
2205
- deriveTaskStack,
2206
- deriveBijaStack,
2207
- passesAlayaRelevanceGate,
2208
- ALAYA_MIN_COSINE_DEFAULT,
2209
- ALAYA_STACK_OVERRIDE_COSINE,
2210
- ALAYA_CANDIDATE_POOL,
2211
- // TASK_1998: exported for dispatch.js (Related Files Preview gating,
2212
- // prompt-byte-stats telemetry) and for direct unit-testing of the
2213
- // two-axis prompt policy (context volume by complexity, process
2214
- // strictness by rigor).
2215
639
  promptProfile,
2216
640
  extractRigor,
2217
641
  extractComplexity,
2218
642
  splitPromptSections,
2219
- // TASK_1998 R2 follow-up: exported for direct unit-testing of Known
2220
- // Failures/Project Memory rigor gating against the real .failure/.memory
2221
- // dirs (no live-project mocking needed).
2222
- appendMemoryContext,
2223
- // TASK_2082: exported for direct unit-testing of the Rust-check reminder
2224
- // gate (scope detection + core-crate existence + isolated-worktree gate)
2225
- // without needing a live dashboard/src-tauri checkout on the test machine.
2226
643
  hasRustScope,
2227
644
  appendRustCheckReminder,
2228
- // TASK_2082: exported so prompt.test.js can exercise the commit-step +
2229
- // Rust-check-reminder wiring directly, without paying for buildUnitPrompt's
2230
- // other live-project-dependent appends (memory/cortex/episodic context).
2231
- appendWorkflow,
2232
- // TASK_2082 defect fix: exported for dispatch.js to call at the prefix +
2233
- // body composition point (rewrites hard rule 4's wording in-place instead
2234
- // of relying on "later instructions win"), and for direct unit-testing.
2235
645
  applyRustCheckToPrefix,
2236
646
  };