kenaz 0.3.17 → 0.3.19

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 (316) hide show
  1. package/core/.claude/commands/agent-worker.md +1 -1
  2. package/core/.claude/commands/architect.md +1 -2
  3. package/core/.claude/commands/spec.md +1 -1
  4. package/core/.claude/commands//347/224/242/345/223/201.md +1 -1
  5. package/core/.claude/hooks/__tests__/task-1977-dedupe-guard.test.js +8 -8
  6. package/core/.claude/hooks/synapse-first-investigation.js +15 -0
  7. package/core/.claude/skills/architect_agent/SKILL.md +11 -92
  8. package/core/.claude/skills/assessment_agent/SKILL.md +0 -1
  9. package/core/.claude/skills/backend_engineer/SKILL.md +3 -6
  10. package/core/.claude/skills/code_reviewer/SKILL.md +1 -1
  11. package/core/.claude/skills/code_reviewer/patterns.yaml +1 -1
  12. package/core/.claude/skills/designer/SKILL.md +3 -6
  13. package/core/.claude/skills/dev_agent/SKILL.md +30 -491
  14. package/core/.claude/skills/developer/SKILL.md +3 -6
  15. package/core/.claude/skills/feature_agent/SKILL.md +3 -17
  16. package/core/.claude/skills/product_manager/SKILL.md +29 -1064
  17. package/core/.claude/skills/qa_engineer/SKILL.md +12 -17
  18. package/core/.claude/skills/spec_optimizer/SKILL.md +25 -275
  19. package/core/.claude/skills/sre_agent/SKILL.md +3 -6
  20. package/core/.collaboration/README.md +2 -3
  21. package/core/.collaboration/core/task-api.js +5 -138
  22. package/core/.collaboration/docs/CLI.md +0 -2
  23. package/core/.failure/INDEX.yaml +6 -40
  24. package/core/.failure/agent-execution/FAIL-013-search-cwd-drift-and-case-mismatch.yaml +1 -1
  25. package/core/.failure/tools/README.md +9 -227
  26. package/core/.formation/bifrost/exec.js +29 -4
  27. package/core/.formation/bifrost/launch.js +1 -6
  28. package/core/.formation/bifrost/launch.test.js +1 -1
  29. package/core/.formation/bifrost/provider-launch.js +18 -11
  30. package/core/.formation/bifrost/provider-launch.test.js +26 -2
  31. package/core/.formation/bifrost/shared/ansuz-boot-policy.js +3 -3
  32. package/core/.formation/bifrost/shared/ansuz-boot-policy.test.js +1 -2
  33. package/core/.formation/tools/__tests__/dispatch-cwd-project.test.js +42 -0
  34. package/core/.formation/tools/__tests__/m1-hmap-convid.test.js +1 -124
  35. package/core/.formation/tools/__tests__/subagent-executor.test.js +3 -2
  36. package/core/.formation/tools/dispatch.js +15 -2
  37. package/core/.formation/tools/loader.js +17 -140
  38. package/core/.formation/tools/post-complete.js +0 -31
  39. package/core/.rule/INDEX.yaml +0 -18
  40. package/core/.rule/general/agent-workflow.yaml +14 -164
  41. package/core/.rule/general/project-self-containment.yaml +8 -19
  42. package/core/.rule/general/rust-verify-target.yaml +6 -5
  43. package/core/.rule/general/synapse-code-intelligence.yaml +3 -3
  44. package/core/.specialist/Amelia/capability.yaml +12 -12
  45. package/core/.specialist/Amelia/workflow.yaml +2 -2
  46. package/core/.specialist/Ansuz/SYSTEM_DOC.md +20 -578
  47. package/core/.specialist/Ansuz/cognitive_principles.yaml +2 -3
  48. package/core/.specialist/Ansuz/concepts/boot_rules_summary.yaml +0 -1
  49. package/core/.specialist/Ansuz/concepts/deep_knowledge_pointers.yaml +1 -2
  50. package/core/.specialist/Ansuz/core.yaml +6 -108
  51. package/core/.specialist/Ansuz/guards/lesson_guards.yaml +0 -3
  52. package/core/.specialist/Ansuz/identity_contract.yaml +0 -1
  53. package/core/.specialist/Ansuz/knowledge.yaml +0 -4
  54. package/core/.specialist/Ansuz/rule.yaml +0 -1
  55. package/core/.specialist/Ansuz/tools/handoff-session-identity.test.js +6 -150
  56. package/core/.specialist/tests/runtime-discovery/README.md +1 -1
  57. package/core/.specialist/tools/__tests__/task-2374-usage-counter.test.js +1 -12
  58. package/core/.specialist/tools/prompt-files.test.js +1 -0
  59. package/core/.specialist/tools/task-bridge.js +1 -66
  60. package/core/.system/README.md +11 -552
  61. package/core/.system/collaboration/README.md +10 -174
  62. package/core/CLAUDE.md +1 -6
  63. package/core/KENAZ_CORE_VERSION +1 -1
  64. package/core/dev/scripts/js-yaml-resolution.test.js +1 -1
  65. package/core/dev/scripts/run-rust-tests.js +30 -46
  66. package/core/dev/scripts/run-rust-tests.test.js +17 -0
  67. package/core/dev/scripts/rust-verify-worktree.js +4 -4
  68. package/core/dev/scripts/verify-target-dir.js +6 -6
  69. package/core/plugins/kenaz/INVENTORY.md +8 -8
  70. package/core/plugins/kenaz/commands/architect.md +1 -2
  71. package/core/plugins/kenaz/commands/spec.md +1 -1
  72. package/core/plugins/kenaz/commands//347/224/242/345/223/201.md +1 -1
  73. package/core/plugins/kenaz/guidance.md +1 -1
  74. package/core/plugins/kenaz/scripts/sync.js +1 -1
  75. package/core/plugins/kenaz/scripts/ygg.js +6 -0
  76. package/core/plugins/kenaz/skills/architect_agent/SKILL.md +11 -92
  77. package/core/plugins/kenaz/skills/assessment_agent/SKILL.md +0 -1
  78. package/core/plugins/kenaz/skills/backend_engineer/SKILL.md +3 -6
  79. package/core/plugins/kenaz/skills/code_reviewer/SKILL.md +1 -1
  80. package/core/plugins/kenaz/skills/code_reviewer/patterns.yaml +1 -1
  81. package/core/plugins/kenaz/skills/designer/SKILL.md +3 -6
  82. package/core/plugins/kenaz/skills/dev_agent/SKILL.md +30 -491
  83. package/core/plugins/kenaz/skills/developer/SKILL.md +3 -6
  84. package/core/plugins/kenaz/skills/feature_agent/SKILL.md +3 -17
  85. package/core/plugins/kenaz/skills/product_manager/SKILL.md +29 -1064
  86. package/core/plugins/kenaz/skills/qa_engineer/SKILL.md +12 -17
  87. package/core/plugins/kenaz/skills/spec_optimizer/SKILL.md +25 -275
  88. package/core/plugins/kenaz/skills/sre_agent/SKILL.md +3 -6
  89. package/package.json +6 -6
  90. package/core/.claude/commands/archive.md +0 -24
  91. package/core/.claude/commands/init.md +0 -112
  92. package/core/.claude/commands/legacy_dev.md +0 -18
  93. package/core/.claude/commands/product-review.md +0 -131
  94. package/core/.claude/commands/strategy.md +0 -141
  95. package/core/.claude/commands/sunny.md +0 -1
  96. package/core/.claude/commands//347/224/242/345/223/201/346/252/242/350/246/226.md +0 -129
  97. package/core/.claude/commands//351/200/262/345/272/246.md +0 -15
  98. package/core/.claude/hooks/__tests__/substrate.test.js +0 -122
  99. package/core/.claude/hooks/_substrate-paths.js +0 -95
  100. package/core/.claude/hooks/context-check.js +0 -51
  101. package/core/.claude/hooks/fatigue-check.js +0 -52
  102. package/core/.claude/hooks/handoff-snapshot-on-commit.js +0 -154
  103. package/core/.claude/hooks/handoff-snapshot-on-question.js +0 -158
  104. package/core/.claude/hooks/handoff-staleness.js +0 -197
  105. package/core/.claude/hooks/jev-thalamus-context.js +0 -17
  106. package/core/.claude/hooks/jev-thalamus-context.test.js +0 -41
  107. package/core/.claude/hooks/lease-release.js +0 -119
  108. package/core/.claude/hooks/lease-touch.js +0 -96
  109. package/core/.claude/hooks/reflex-feedback.js +0 -5
  110. package/core/.claude/hooks/session-init.ps1 +0 -49
  111. package/core/.claude/hooks/sound-event.js +0 -70
  112. package/core/.claude/hooks/sound-event.sh +0 -37
  113. package/core/.claude/hooks/stream-delta.js +0 -169
  114. package/core/.claude/no-hooks.settings.json +0 -3
  115. package/core/.claude/skills/legacy_dev/SKILL.md +0 -202
  116. package/core/.claude/skills/sunny/SKILL.md +0 -74
  117. package/core/.collaboration/cli/commands/assign.js +0 -39
  118. package/core/.collaboration/cli/commands/claim.js +0 -33
  119. package/core/.collaboration/cli/commands/complete.js +0 -44
  120. package/core/.collaboration/cli/commands/create.js +0 -110
  121. package/core/.collaboration/cli/commands/get.js +0 -83
  122. package/core/.collaboration/cli/commands/index.js +0 -52
  123. package/core/.collaboration/cli/commands/list.js +0 -51
  124. package/core/.collaboration/cli/commands/lock.js +0 -45
  125. package/core/.collaboration/cli/commands/rebuild.js +0 -39
  126. package/core/.collaboration/cli/commands/search.js +0 -61
  127. package/core/.collaboration/cli/commands/stats.js +0 -61
  128. package/core/.collaboration/cli/commands/transfer.js +0 -41
  129. package/core/.collaboration/cli/commands/unlock.js +0 -48
  130. package/core/.collaboration/cli/formatters/help-formatter.js +0 -70
  131. package/core/.collaboration/cli/formatters/index.js +0 -7
  132. package/core/.collaboration/cli/task-cli.js +0 -46
  133. package/core/.collaboration/cli/utils/arg-parser.js +0 -74
  134. package/core/.collaboration/cli/utils/git-user.js +0 -25
  135. package/core/.collaboration/cli/utils/index.js +0 -9
  136. package/core/.collaboration/cli/utils/output.js +0 -10
  137. package/core/.collaboration/config/default.yaml +0 -189
  138. package/core/.collaboration/core/conflict-resolver.js +0 -461
  139. package/core/.collaboration/core/conflict-resolver.test.js +0 -389
  140. package/core/.collaboration/core/migrate-legacy.js +0 -260
  141. package/core/.collaboration/docs/API.md +0 -787
  142. package/core/.collaboration/docs/ARCHITECTURE.md +0 -596
  143. package/core/.collaboration/docs/FAQ.md +0 -695
  144. package/core/.collaboration/docs/MIGRATION.md +0 -804
  145. package/core/.collaboration/docs/examples/README.md +0 -320
  146. package/core/.collaboration/docs/examples/advanced-queries.js +0 -261
  147. package/core/.collaboration/docs/examples/basic-usage.js +0 -184
  148. package/core/.collaboration/docs/examples/team-workflow.js +0 -352
  149. package/core/.failure/agent-execution/FAIL-001-agent-no-output.yaml +0 -391
  150. package/core/.failure/agent-execution/FAIL-007-pm-lead-wrong-team-invocation.yaml +0 -0
  151. package/core/.failure/agent-execution/FAIL-010-phase0-poem-format-skipped.yaml +0 -28
  152. package/core/.failure/agent-execution/FAIL-010-skipping-cognitive-checkpoint-after-verification.yaml +0 -59
  153. package/core/.failure/agent-execution/FAIL-012-grep-shortcut-during-investigation.yaml +0 -85
  154. package/core/.failure/architecture/FAIL-006-python-version-compatibility.yaml +0 -445
  155. package/core/.failure/pipeline/FAIL-003-queue-pollution.yaml +0 -335
  156. package/core/.failure/pipeline/README.md +0 -95
  157. package/core/.failure/retired/FAIL-009-skipping-synapse-pulse-before-code-change.yaml +0 -49
  158. package/core/.failure/tools/USAGE_GUIDE.md +0 -323
  159. package/core/.failure/tools/check_failures.ps1 +0 -387
  160. package/core/.failure/tools/log_failure.ps1 +0 -474
  161. package/core/.formation/.episode-input.json +0 -10
  162. package/core/.formation/.merge-lock +0 -1
  163. package/core/.formation/lead.yaml +0 -118
  164. package/core/.formation/legacy.bak/designer.yaml +0 -57
  165. package/core/.formation/legacy.bak/pm.yaml +0 -57
  166. package/core/.formation/legacy.bak/qa.yaml +0 -57
  167. package/core/.formation/legacy.bak/review.yaml +0 -62
  168. package/core/.formation/legacy.bak/scheduler.yaml +0 -129
  169. package/core/.formation/legacy.bak/sre.yaml +0 -57
  170. package/core/.formation/role-map.yaml +0 -274
  171. package/core/.formation/roles/_shared/modules/code_review.yaml +0 -98
  172. package/core/.formation/roles/_shared/modules/security.yaml +0 -103
  173. package/core/.formation/roles/_shared/modules/testing.yaml +0 -14
  174. package/core/.formation/roles/backend/role.yaml +0 -93
  175. package/core/.formation/roles/code_reviewer/role.yaml +0 -102
  176. package/core/.formation/roles/database_optimizer/role.yaml +0 -107
  177. package/core/.formation/roles/designer/role.yaml +0 -66
  178. package/core/.formation/roles/developer/role.yaml +0 -75
  179. package/core/.formation/roles/frontend/role.yaml +0 -134
  180. package/core/.formation/roles/incident_response/role.yaml +0 -89
  181. package/core/.formation/roles/qa/role.yaml +0 -89
  182. package/core/.formation/roles/security/role.yaml +0 -87
  183. package/core/.formation/roles/sre/role.yaml +0 -66
  184. package/core/.formation/skills/coordinator.yaml +0 -52
  185. package/core/.formation/solo/diagnosis.yaml +0 -78
  186. package/core/.formation/solo/strategy.yaml +0 -121
  187. package/core/.formation/tests/hello.txt +0 -2
  188. package/core/.formation/tools/coordinator.js +0 -350
  189. package/core/.formation/tools/jev-correction.js +0 -214
  190. package/core/.formation/tools/jev-correction.test.js +0 -178
  191. package/core/.formation/tools/log-reader.js +0 -232
  192. package/core/.formation/tools/revert-daemon-state.js +0 -162
  193. package/core/.formation/tools/test-drift.js +0 -148
  194. package/core/.formation/unit/heimdall.yaml +0 -96
  195. package/core/.formation/unit/plan.yaml +0 -67
  196. package/core/.formation/unit/think.yaml +0 -58
  197. package/core/.kenaz/MUNINN_SNAPSHOT9_EPISODE.json +0 -32
  198. package/core/.kenaz/MUNINN_SNAPSHOT9_RUNE.json +0 -23
  199. package/core/.kenaz/MUNINN_SNAPSHOT9_SUMMARY.md +0 -89
  200. package/core/.kenaz/ash_TASK_1428-20260507.json +0 -14
  201. package/core/.kenaz/muninn_episode_write.ps1 +0 -17
  202. package/core/.kenaz/muninn_snapshot11_rune_candidates.md +0 -63
  203. package/core/.kenaz/muninn_snapshot1_rune_candidates.md +0 -49
  204. package/core/.kenaz/runes_TASK_1428.json +0 -57
  205. package/core/.kenaz/tmp_heimdall_proposal_8d3e392.json +0 -13
  206. package/core/.kenaz/tmp_props.json +0 -1
  207. package/core/.kenaz/tmp_sounddrop_t1_prompt.txt +0 -371
  208. package/core/.rule/MECHANIZED.md +0 -61
  209. package/core/.rule/general/cortex-memory-recall.yaml +0 -446
  210. package/core/.rule/general/pipeline.yaml +0 -246
  211. package/core/.rule/general/sunny-access-control.yaml +0 -60
  212. package/core/.rule/general/system-invariants.yaml +0 -231
  213. package/core/.rule/pipeline/PPL-002-completed-task-archival.yaml +0 -528
  214. package/core/.rule/pipeline/auto-update.yaml +0 -282
  215. package/core/.rule/pipeline/task-execution.yaml +0 -365
  216. package/core/.specialist/Ansuz/concepts/phase_protocol.yaml +0 -65
  217. package/core/.specialist/Ansuz/experiments/boot_ablation_replay.js +0 -197
  218. package/core/.specialist/Ansuz/tools/bootstrap-anchors.js +0 -289
  219. package/core/.specialist/Ansuz/workflow.yaml +0 -1225
  220. package/core/.specialist/experiments/rg_benchmark.js +0 -523
  221. package/core/.specialist/experiments/rg_benchmark_results.json +0 -27
  222. package/core/.specialist/tools/jev-optional-context.js +0 -107
  223. package/core/.specialist/tools/jev-optional-context.test.js +0 -152
  224. package/core/.specialist/tools/memory-action-log.js +0 -839
  225. package/core/.specialist/tools/phase-gate.js +0 -3534
  226. package/core/.specialist/tools/phase-gate.test.js +0 -85
  227. package/core/.specialist/tools/precompute-runner.js +0 -444
  228. package/core/.specialist/tools/rule-engine.js +0 -621
  229. package/core/.specialist/tools/task-bridge-jev.test.js +0 -170
  230. package/core/.synapse/memory-shared.json.sync +0 -1
  231. package/core/.system/agent-skills/README.md +0 -724
  232. package/core/.system/cli-tools/README.md +0 -724
  233. package/core/.system/cortex/MODULES.md +0 -1046
  234. package/core/.system/dashboard/FRONTEND.md +0 -812
  235. package/core/.system/dashboard/README.md +0 -737
  236. package/core/.system/failure/README.md +0 -723
  237. package/core/.system/formation/LEGACY_SKILLS.md +0 -974
  238. package/core/.system/formation/POST_COMPLETE.md +0 -602
  239. package/core/.system/formation/README.md +0 -1094
  240. package/core/.system/formation/TOOLS_REFERENCE.md +0 -1145
  241. package/core/.system/kenaz/README.md +0 -787
  242. package/core/.system/memory/README.md +0 -719
  243. package/core/.system/pipeline/README.md +0 -712
  244. package/core/.system/rule/README.md +0 -794
  245. package/core/.system/sunny/README.md +0 -476
  246. package/core/.system/sunny/RULES.json +0 -89
  247. package/core/.system/sunny/knowledge/INDEX.md +0 -37
  248. package/core/.system/sunny/knowledge/agent-teams-api.md +0 -0
  249. package/core/.system/sunny/knowledge/ansuz-specialist.md +0 -500
  250. package/core/.system/sunny/knowledge/claude-cli-spawn.md +0 -177
  251. package/core/.system/sunny/knowledge/collaboration-api.md +0 -925
  252. package/core/.system/sunny/teammates/README.md +0 -107
  253. package/core/.system/sunny/teammates/how_to_use.yaml +0 -101
  254. package/core/.system/sunny/teammates/tools/headless/launcher.js +0 -318
  255. package/core/.system/sunny/teammates/tools/powershell/launcher.js +0 -241
  256. package/core/.system/sunny/teammates/tools/powershell/start.bat +0 -50
  257. package/core/.system/sunny/teammates/tools/utils/env.js +0 -96
  258. package/core/.system/sunny/teammates/tools/utils/format-output.js +0 -329
  259. package/core/.system/sunny/tools.json +0 -101
  260. package/core/dev/scripts/cleanup-queue.js +0 -184
  261. package/core/dev/scripts/cleanup_queue.js +0 -86
  262. package/core/dev/scripts/legacy/README.md +0 -39
  263. package/core/dev/scripts/legacy/dev_agent_scheduler.ps1.bak +0 -536
  264. package/core/dev/scripts/legacy/multi_agent_scheduler.ps1.bak +0 -1978
  265. package/core/dev/scripts/matrix/CLI_README.md +0 -214
  266. package/core/dev/scripts/matrix/README.md +0 -310
  267. package/core/dev/scripts/matrix/package-lock.json +0 -4266
  268. package/core/dev/scripts/matrix/package.json +0 -55
  269. package/core/dev/scripts/matrix/src/abbreviations.ts +0 -295
  270. package/core/dev/scripts/matrix/src/cli/commands/convert.ts +0 -211
  271. package/core/dev/scripts/matrix/src/cli/commands/create.ts +0 -95
  272. package/core/dev/scripts/matrix/src/cli/commands/delete.ts +0 -102
  273. package/core/dev/scripts/matrix/src/cli/commands/list-tags.ts.wip +0 -140
  274. package/core/dev/scripts/matrix/src/cli/commands/manage-tags.ts.wip +0 -173
  275. package/core/dev/scripts/matrix/src/cli/commands/query.ts +0 -261
  276. package/core/dev/scripts/matrix/src/cli/commands/read.ts +0 -77
  277. package/core/dev/scripts/matrix/src/cli/commands/stats.ts.wip +0 -168
  278. package/core/dev/scripts/matrix/src/cli/commands/tokens.ts.wip +0 -126
  279. package/core/dev/scripts/matrix/src/cli/commands/update.ts +0 -147
  280. package/core/dev/scripts/matrix/src/cli/commands/validate.ts +0 -275
  281. package/core/dev/scripts/matrix/src/cli/index.ts +0 -350
  282. package/core/dev/scripts/matrix/src/cli/utils/file-handler.ts +0 -72
  283. package/core/dev/scripts/matrix/src/cli/utils/logger.ts +0 -65
  284. package/core/dev/scripts/matrix/src/index.ts +0 -62
  285. package/core/dev/scripts/matrix/src/indexer/tag-indexer.ts +0 -165
  286. package/core/dev/scripts/matrix/src/parser.ts +0 -281
  287. package/core/dev/scripts/matrix/src/serializer.ts +0 -151
  288. package/core/dev/scripts/matrix/src/validator.ts +0 -301
  289. package/core/dev/scripts/matrix/tests/abbreviations.test.ts +0 -122
  290. package/core/dev/scripts/matrix/tests/cli.test.ts +0 -177
  291. package/core/dev/scripts/matrix/tests/convert.test.ts +0 -143
  292. package/core/dev/scripts/matrix/tests/parser.test.ts +0 -203
  293. package/core/dev/scripts/matrix/tests/query.test.ts +0 -85
  294. package/core/dev/scripts/matrix/tests/serializer.test.ts +0 -197
  295. package/core/dev/scripts/matrix/tests/validate.test.ts +0 -141
  296. package/core/dev/scripts/matrix/tests/validator.test.ts +0 -167
  297. package/core/dev/scripts/matrix/tsconfig.json +0 -20
  298. package/core/dev/scripts/matrix/types/index.d.ts +0 -111
  299. package/core/dev/scripts/matrix/types/index.d.ts.map +0 -1
  300. package/core/dev/scripts/matrix/types/index.js +0 -8
  301. package/core/dev/scripts/matrix/types/index.js.map +0 -1
  302. package/core/dev/scripts/matrix/types/index.ts +0 -124
  303. package/core/dev/scripts/memory-usage-report.js +0 -152
  304. package/core/dev/scripts/probe/ansuz-scaffold-ablation.js +0 -579
  305. package/core/dev/scripts/probe/ansuz-scaffold-ablation.test.js +0 -244
  306. package/core/dev/scripts/probe/hook-latency-probe.js +0 -315
  307. package/core/dev/scripts/probe/hook-latency-probe.test.js +0 -105
  308. package/core/dev/scripts/probe/prediction-signal-auc.js +0 -483
  309. package/core/dev/scripts/probe/prediction-signal-auc.test.js +0 -200
  310. package/core/dev/scripts/test-pty-ws-tunnel.js +0 -229
  311. package/core/dev/scripts/validate_pipeline.js +0 -197
  312. package/core/dev/spikes/task2145-evidence-20261004/backend-log-excerpt.txt +0 -56
  313. package/core/dev/spikes/task2145-evidence-20261004/graph.shadow +0 -0
  314. package/core/dev/spikes/task2145-evidence-20261004/graph.wal +0 -0
  315. package/core/dev/spikes/task2145-evidence-20261004/graph_corrupt_suspect.marker +0 -1
  316. package/core/plugins/kenaz/commands/strategy.md +0 -141
@@ -41,11 +41,6 @@ categories:
41
41
  description: "架構決策失敗(設計缺陷、技術選型錯誤)"
42
42
  icon: "🏗️"
43
43
 
44
- pipeline:
45
- path: "pipeline/"
46
- description: "Pipeline 系統失敗(任務管理、佇列、統計)"
47
- icon: "⚙️"
48
-
49
44
  data_integrity:
50
45
  path: "data-integrity/"
51
46
  description: "數據完整性問題(JSON 損壞、編碼錯誤)"
@@ -83,7 +78,7 @@ failure_template: |
83
78
 
84
79
  _meta:
85
80
  id: "FAIL-XXX"
86
- category: "agent_execution | code_quality | architecture | pipeline | data_integrity"
81
+ category: "agent_execution | code_quality | architecture | data_integrity"
87
82
  severity: "critical | high | medium | low"
88
83
  date: "YYYY-MM-DD"
89
84
  related_tasks: ["TASK_XXX"]
@@ -118,7 +113,7 @@ failure_template: |
118
113
  - [ ] 檢查項目 1
119
114
  - [ ] 檢查項目 2
120
115
 
121
- ### 三級預防(由 Ansuz Phase 9 prevention_analysis 自動填入)
116
+ ### 三級預防
122
117
  prevention:
123
118
  primary: "防止再發 — 架構加固、測試覆蓋、代碼重構"
124
119
  secondary: "早期偵測 — 監控指標、日誌告警、健康檢查"
@@ -137,18 +132,10 @@ failure_template: |
137
132
  # 載入順序(按嚴重程度)
138
133
  # ============================================================
139
134
  load_order:
140
- - path: "agent-execution/FAIL-001-agent-no-output.yaml"
141
- title: "Agent 執行無輸出問題"
142
- severity: "critical"
143
-
144
135
  - path: "data-integrity/FAIL-002-json-bom.yaml"
145
136
  title: "JSON BOM 編碼問題"
146
137
  severity: "high"
147
138
 
148
- - path: "pipeline/FAIL-003-queue-pollution.yaml"
149
- title: "TASK_QUEUE 污染問題"
150
- severity: "high"
151
-
152
139
  - path: "agent-execution/FAIL-004_agent_platform_specific_commands.md"
153
140
  title: "Agent 使用平台特定命令導致失敗"
154
141
  severity: "high"
@@ -157,18 +144,10 @@ load_order:
157
144
  title: "背景 Agent 無寫入權限"
158
145
  severity: "critical"
159
146
 
160
- - path: "architecture/FAIL-006-python-version-compatibility.yaml"
161
- title: "Python 3.14 依賴兼容性問題"
162
- severity: "high"
163
-
164
147
  - path: "agent-execution/FAIL-008-bypass-api-direct-yaml-write.yaml"
165
148
  title: "Agent 繞過 API 直接寫 YAML 任務檔案"
166
149
  severity: "high"
167
150
 
168
- - path: "agent-execution/FAIL-010-phase0-poem-format-skipped.yaml"
169
- title: "Phase 0 記憶之烏鴉歸巢段落持續缺失"
170
- severity: "medium"
171
-
172
151
  - path: "agent-execution/FAIL-014-shared-worktree-premature-death-verdict.yaml"
173
152
  title: "並發 Agent worktree 共用路徑被提前清理 + 沉默誤判死亡 → 重複派工"
174
153
  severity: "high"
@@ -178,19 +157,11 @@ load_order:
178
157
  # 統計數據
179
158
  # ============================================================
180
159
  statistics:
181
- total_failures: 10
160
+ total_failures: 5
182
161
  by_category:
183
- agent_execution: 6
162
+ agent_execution: 4
184
163
  data_integrity: 1
185
- pipeline: 1
186
- architecture: 1
187
- by_severity:
188
- critical: 2
189
- high: 5
190
- medium: 1
191
- low: 0
192
- resolved: 7
193
- converted_to_rules: 2 # 已轉化為 .rule/ 規則
164
+ resolved: 5
194
165
 
195
166
  # ============================================================
196
167
  # Agent 使用指南
@@ -220,12 +191,7 @@ system_integration:
220
191
  priority_order: |
221
192
  1. .rule/ (最高優先級 - 必須遵守的規則)
222
193
  2. .failure/ (失敗經驗 - 應避免的模式)
223
- 3. .memory/ (經驗記錄 - 一般性筆記)
224
- 4. CLAUDE.md (專案指導)
194
+ 3. CLAUDE.md (專案指導)
225
195
 
226
196
  workflow: |
227
197
  失敗 → 記錄到 .failure/ → 分析根因 → 如重複發生 → 提升為 .rule/
228
-
229
- example:
230
- failure: "FAIL-003: TASK_QUEUE 被 completed 任務污染"
231
- rule: "PPL-002: 已完成任務必須封存到 .memory/"
@@ -72,7 +72,7 @@ prevention:
72
72
  → Grep -i pattern path=ABSOLUTE_PATH (找內容)
73
73
 
74
74
  「我想理解一個概念的拓撲」
75
- → synapse_search / synapse_pulse (理解關係)
75
+ → kenaz_lsp_callers / kenaz_lsp_references (理解關係)
76
76
 
77
77
  「我搜了但什麼都沒找到」
78
78
  → 檢查 cwd(Bash pwd)
@@ -1,230 +1,12 @@
1
- # 🔧 Failure Learning Tools
1
+ # .failure/tools
2
2
 
3
- ## 目錄說明
3
+ Small Node helpers for the JSON/BOM failure pattern (`data-integrity/FAIL-002-json-bom.yaml`). All take a file or directory path.
4
4
 
5
- 此目錄包含自動化工具腳本,幫助 Agent 和開發者快速記錄和檢查失敗經驗。
5
+ | Tool | Use |
6
+ |---|---|
7
+ | `check_bom.js <file\|dir> [--recursive]` | Report files that start with a UTF-8 BOM |
8
+ | `remove_bom.js <file>` | Strip the BOM |
9
+ | `fix_json.js <file>` | Strip the BOM, validate, and reformat a JSON file |
10
+ | `validate_json.js <file>` | Exit non-zero if the file is not valid JSON |
6
11
 
7
- ## 工具列表
8
-
9
- ### 1. log_failure.ps1
10
- 自動化建立失敗記錄文件的 PowerShell 腳本。
11
-
12
- #### 使用方式
13
- ```powershell
14
- # 互動式建立失敗記錄
15
- .\.failure\tools\log_failure.ps1
16
-
17
- # 指定參數建立
18
- .\.failure\tools\log_failure.ps1 `
19
- -Category "pipeline" `
20
- -Title "TASK_QUEUE 污染問題" `
21
- -Severity "high" `
22
- -TaskID "TASK_088"
23
- ```
24
-
25
- #### 功能
26
- - 自動生成 FAIL-XXX ID(根據 INDEX.yaml 中的最大 ID)
27
- - 根據分類選擇正確的目錄
28
- - 使用標準模板建立 YAML 文件
29
- - 自動更新 INDEX.yaml 的 load_order 和統計數據
30
- - 支援互動式和參數化兩種模式
31
-
32
- ### 2. check_failures.ps1
33
- 檢查當前任務是否匹配已知失敗模式的腳本。
34
-
35
- #### 使用方式
36
- ```powershell
37
- # 檢查特定任務的失敗模式
38
- .\.failure\tools\check_failures.ps1 -TaskID "TASK_077"
39
-
40
- # 檢查特定類型的任務
41
- .\.failure\tools\check_failures.ps1 -AgentType "developer"
42
-
43
- # 檢查特定路徑的失敗模式
44
- .\.failure\tools\check_failures.ps1 -ScopePath "dev/pipeline/"
45
-
46
- # 使用關鍵詞搜尋
47
- .\.failure\tools\check_failures.ps1 -Keywords "JSON,BOM,encoding"
48
-
49
- # 過濾特定分類和嚴重程度
50
- .\.failure\tools\check_failures.ps1 -Category "pipeline" -Severity "critical"
51
- ```
52
-
53
- #### 功能
54
- - 根據任務 ID 自動載入 TASK_QUEUE.json 詳情
55
- - 根據 agent_type 匹配相關失敗記錄
56
- - 根據 scope_paths 匹配相關失敗記錄
57
- - 支援關鍵詞模糊搜尋
58
- - 輸出預防檢查清單和解決方案參考
59
- - 按嚴重程度排序顯示結果
60
-
61
- ### 3. validate_json.js
62
- 驗證 JSON 文件完整性的 Node.js 腳本。
63
-
64
- #### 使用方式
65
- ```bash
66
- # 驗證單個文件
67
- node .failure/tools/validate_json.js dev/pipeline/TASK_QUEUE.json
68
- ```
69
-
70
- #### 功能
71
- - 驗證 JSON 語法正確性
72
- - 檢測 BOM (Byte Order Mark)
73
- - 提供錯誤診斷提示
74
-
75
- ### 3.1 remove_bom.js
76
- 移除 UTF-8 文件開頭的 BOM (Byte Order Mark)。
77
-
78
- #### 使用方式
79
- ```bash
80
- # 移除 BOM
81
- node .failure/tools/remove_bom.js dev/pipeline/TASK_QUEUE.json
82
- ```
83
-
84
- #### 功能
85
- - 檢測並移除 BOM (0xFEFF)
86
- - 不修改其他內容
87
- - 無 BOM 時不做任何操作
88
-
89
- ### 3.2 fix_json.js
90
- 一鍵修復 JSON 文件(移除 BOM + 驗證 + 重新格式化)。
91
-
92
- #### 使用方式
93
- ```bash
94
- # 修復 JSON 文件
95
- node .failure/tools/fix_json.js dev/pipeline/TASK_QUEUE.json
96
- ```
97
-
98
- #### 功能
99
- - 自動移除 BOM
100
- - 驗證 JSON 語法
101
- - 重新格式化(統一縮排 2 空格)
102
- - 寫回無 BOM 的 UTF-8 文件
103
-
104
- ### 3.3 check_bom.js
105
- 批量檢查 JSON 文件是否有 BOM(用於 CI/CD 或 pre-commit hook)。
106
-
107
- #### 使用方式
108
- ```bash
109
- # 檢查單個文件
110
- node .failure/tools/check_bom.js dev/pipeline/TASK_QUEUE.json
111
-
112
- # 遞迴檢查目錄中所有 JSON 文件
113
- node .failure/tools/check_bom.js dev/pipeline --recursive
114
- ```
115
-
116
- #### 功能
117
- - 批量檢查 JSON 文件
118
- - 遞迴掃描目錄(使用 --recursive)
119
- - 列出所有有 BOM 的文件
120
- - 退出碼:0(無 BOM)或 1(檢測到 BOM)
121
-
122
- #### Pre-commit Hook 整合
123
- ```bash
124
- #!/bin/bash
125
- # .git/hooks/pre-commit
126
- node .failure/tools/check_bom.js dev/pipeline --recursive || exit 1
127
- ```
128
-
129
- ### 4. failure-report.ps1 (計劃中)
130
- 生成失敗經驗報告的腳本。
131
-
132
- #### 使用方式
133
- ```powershell
134
- # 生成完整報告
135
- .\.failure\tools\failure-report.ps1
136
-
137
- # 只生成特定分類的報告
138
- .\.failure\tools\failure-report.ps1 -Category "pipeline"
139
-
140
- # 只生成特定嚴重程度的報告
141
- .\.failure\tools\failure-report.ps1 -Severity "critical,high"
142
-
143
- # 輸出為 Markdown 格式
144
- .\.failure\tools\failure-report.ps1 -Format "markdown" -Output "failure-report.md"
145
- ```
146
-
147
- #### 功能(計劃中)
148
- - 統計失敗記錄數量
149
- - 按分類和嚴重程度分組
150
- - 列出已轉化為規則的失敗
151
- - 生成 Markdown 報告
152
-
153
- ### 5. failure-to-rule.ps1 (計劃中)
154
- 將重複發生的失敗轉化為規則的輔助工具。
155
-
156
- #### 使用方式
157
- ```powershell
158
- # 檢查哪些失敗應該轉化為規則
159
- .\.failure\tools\failure-to-rule.ps1 -Analyze
160
-
161
- # 將特定失敗轉化為規則
162
- .\.failure\tools\failure-to-rule.ps1 -FailureID "FAIL-003" -RuleID "PPL-002"
163
- ```
164
-
165
- #### 功能(計劃中)
166
- - 分析失敗記錄,建議哪些應該轉化為規則
167
- - 自動生成規則文件草稿
168
- - 更新失敗記錄的 `converted_to_rules` 狀態
169
- - 更新 INDEX.yaml 統計數據
170
-
171
- ## 開發新工具
172
-
173
- 如果需要開發新的失敗學習工具,請遵循以下指南:
174
-
175
- ### 1. 工具命名規範
176
- - PowerShell 腳本:`action-noun.ps1`(如 `check-failure-patterns.ps1`)
177
- - Node.js 腳本:`action-noun.js`(如 `validate-json.js`)
178
- - Python 腳本:`action_noun.py`(如 `analyze_failures.py`)
179
-
180
- ### 2. 必須包含的功能
181
- - **幫助訊息**:`-Help` 或 `--help` 參數
182
- - **錯誤處理**:優雅處理錯誤,提供清晰的錯誤訊息
183
- - **日誌輸出**:使用一致的日誌格式
184
- - **返回碼**:成功返回 0,失敗返回非 0
185
-
186
- ### 3. 文檔要求
187
- - 在此 README.md 中添加工具說明
188
- - 在腳本開頭添加註解說明用途和使用方式
189
- - 提供使用範例
190
-
191
- ### 4. 測試要求
192
- - 提供測試用例
193
- - 在 `.failure/tools/tests/` 目錄中
194
-
195
- ## 工具使用最佳實踐
196
-
197
- ### Agent 使用場景
198
-
199
- #### 任務開始前
200
- ```powershell
201
- # 檢查相關失敗模式
202
- .\.failure\tools\check_failures.ps1 -TaskID $TASK_ID
203
- ```
204
-
205
- #### 任務執行中
206
- ```bash
207
- # 驗證 JSON 文件
208
- node .failure/tools/validate_json.js dev/pipeline/TASK_QUEUE.json
209
- ```
210
-
211
- #### 任務失敗後
212
- ```powershell
213
- # 建立失敗記錄
214
- .\.failure\tools\log_failure.ps1 -Interactive
215
- ```
216
-
217
- ### 定期維護
218
- ```powershell
219
- # 每週生成失敗報告
220
- .\failure\tools\failure-report.ps1 -Output "weekly-failure-report.md"
221
-
222
- # 分析哪些失敗應該轉化為規則
223
- .\failure\tools\failure-to-rule.ps1 -Analyze
224
- ```
225
-
226
- ## 相關資源
227
-
228
- - **失敗記錄索引**:`.failure/INDEX.yaml`
229
- - **規則系統**:`.rule/INDEX.yaml`
230
- - **Agent 工作流程**:`.rule/general/agent-workflow.yaml`
12
+ Looking up known failures: read `.failure/INDEX.yaml`, then Grep `.failure/` for the task's scope or error text. Record a new failure by adding a YAML file from the template in INDEX.yaml and listing it in `load_order`; lessons that are not reproducible failure patterns go to `kenaz learn add`.
@@ -19,7 +19,7 @@ const { spawn, spawnSync } = require('child_process');
19
19
 
20
20
  // First = default. From GET https://api.deepseek.com/models on 2026-10-11 (V4 ids, 1M context); the V3 ids still
21
21
  // answer but are no longer listed. `kenaz launch` re-reads the context window from that endpoint at start.
22
- const DEEPSEEK_MODELS = ['deepseek-v4-pro', 'deepseek-flash', 'deepseek-chat', 'deepseek-reasoner'];
22
+ const DEEPSEEK_MODELS = ['deepseek-flash', 'deepseek-v4-pro', 'deepseek-chat', 'deepseek-reasoner']; // flash = default (user decision 2026-10-12)
23
23
  const DEEPSEEK_BASE_URL = 'https://api.deepseek.com/anthropic';
24
24
  const DEEPSEEK_CONTEXT_TOKENS = { 'deepseek-v4-pro': 1048576, 'deepseek-flash': 1048576, 'deepseek-chat': 128000, 'deepseek-reasoner': 128000 };
25
25
  const DEEPSEEK_FAST = 'deepseek-flash'; // Claude Code's small/fast slot (titles, summaries): the cheap model
@@ -38,12 +38,37 @@ function parseExecutorModel(raw) {
38
38
  return { adapter, id };
39
39
  }
40
40
  if (adapter === 'deepseek') {
41
- if (!DEEPSEEK_MODELS.includes(id)) throw new Error(`unknown deepseek model '${id}'; valid: ${DEEPSEEK_MODELS.map((i) => 'deepseek:' + i).join(', ')}`);
42
- return { adapter, id };
41
+ // Any id the vendor may add later is accepted (no release needed); `deepseek:` alone = the user's saved default.
42
+ const model = id || providerDefault('deepseek') || DEEPSEEK_MODELS[0];
43
+ if (!MODEL_ID.test(model)) throw new Error(`invalid deepseek model id '${id}' (letters, digits, . _ - only); known: ${DEEPSEEK_MODELS.map((i) => 'deepseek:' + i).join(', ')}`);
44
+ return { adapter, id: model };
43
45
  }
44
46
  throw new Error(`unknown executor prefix '${m[1]}:'; valid prefixes: codex:<id>, deepseek:<model>`);
45
47
  }
46
48
 
49
+ const MODEL_ID = /^[A-Za-z0-9][\w.-]{0,63}$/;
50
+ const configFile = (home) => path.join(home, '.kenaz', 'config.json');
51
+
52
+ /** The user's own default model for a provider: KENAZ_<PROVIDER>_MODEL, else ~/.kenaz/config.json
53
+ * providers.<provider>.model (written by `kenaz launch --provider p --model m --save-default`); null when unset. */
54
+ function providerDefault(provider, env = process.env, home = os.homedir()) {
55
+ const e = env['KENAZ_' + provider.toUpperCase() + '_MODEL'];
56
+ if (e && e.trim()) return e.trim();
57
+ try { const m = JSON.parse(fs.readFileSync(configFile(home), 'utf8')).providers[provider].model; return typeof m === 'string' && m.trim() ? m.trim() : null; } catch { return null; }
58
+ }
59
+
60
+ /** Save the default (other keys in config.json are kept; atomic write). */
61
+ function saveProviderDefault(provider, model, home = os.homedir()) {
62
+ let cfg = {};
63
+ try { cfg = JSON.parse(fs.readFileSync(configFile(home), 'utf8')) || {}; } catch { /* new file */ }
64
+ cfg.providers = { ...(cfg.providers || {}), [provider]: { ...((cfg.providers || {})[provider] || {}), model } };
65
+ fs.mkdirSync(path.dirname(configFile(home)), { recursive: true });
66
+ const tmp = configFile(home) + '.tmp';
67
+ fs.writeFileSync(tmp, JSON.stringify(cfg, null, 2) + '\n');
68
+ fs.renameSync(tmp, configFile(home));
69
+ return configFile(home);
70
+ }
71
+
47
72
  /** DEEPSEEK_API_KEY, else deepseek_api_key in ~/.kenaz/credentials.yaml; null when neither. */
48
73
  function deepseekKey(env = process.env, home = os.homedir()) {
49
74
  if (env.DEEPSEEK_API_KEY && env.DEEPSEEK_API_KEY.trim()) return env.DEEPSEEK_API_KEY.trim();
@@ -175,4 +200,4 @@ async function main(argv) {
175
200
  }
176
201
 
177
202
  if (require.main === module) main(process.argv.slice(2)).then((c) => { process.exitCode = c; });
178
- module.exports = { otherVendorSession, parseExecutorModel, deepseekKey, deepseekEnv, deepseekModels, DEEPSEEK_UNSET, winQuote, buildChild, run, main, DEEPSEEK_MODELS };
203
+ module.exports = { otherVendorSession, providerDefault, saveProviderDefault, MODEL_ID, parseExecutorModel, deepseekKey, deepseekEnv, deepseekModels, DEEPSEEK_UNSET, winQuote, buildChild, run, main, DEEPSEEK_MODELS };
@@ -286,12 +286,7 @@ function renderOverride(prompt, bootContext, { policy = BOOT_POLICY, projectPath
286
286
  const content = `${projectInstruction}${bootContext}${memoryStartupDoc}${codexDdxInjection}You have been activated as ${policy.specialist} via Bifröst.
287
287
 
288
288
  IMPORTANT — Shell compatibility (you are running under Codex which uses PowerShell on Windows):
289
- - When calling phase-gate.js exit with --data, do NOT pass JSON inline. PowerShell mangles JSON quotes.
290
- - Instead: write JSON to a temp file, then use --data-file=path. Example:
291
- 1. Write: Set-Content -Path _phase_data.json -Value '{"ash_loaded":true,"poem_displayed":true,"session_handoff_loaded":false}'
292
- 2. Run: node .specialist/tools/phase-gate.js exit --phase=0 --data-file=_phase_data.json
293
- 3. Cleanup: Remove-Item _phase_data.json
294
- - This applies to ALL phase-gate.js exit calls, not just Phase 0.
289
+ - Do not pass inline JSON on the command line; PowerShell mangles JSON quotes. Write it to a temp file and pass the path instead.
295
290
 
296
291
  ${prompt}
297
292
 
@@ -104,7 +104,7 @@ test('override ends with fast precedence and retains full project pin', () => {
104
104
  assert.equal((fast.match(/## Codex Bootstrap Policy/g) || []).length, 1);
105
105
  assert.ok(fast.includes(`projectPath="${projectPath}"`));
106
106
  const full = api.renderOverride('fixture', '', { projectPath, policy: resolveBootPolicy({ depth: 'kernel' }) });
107
- assert.ok(full.includes(`--project-path="${projectPath}"`));
107
+ assert.ok(full.includes(`projectPath="${projectPath}"`));
108
108
  assert.match(full, /HALT and emit recovery/);
109
109
  assert.doesNotMatch(full, /Ansuz fast boot/);
110
110
  });
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict';
3
3
  /**
4
- * `kenaz launch --provider deepseek [--model deepseek-v4-pro|deepseek-flash|...] [claude args...]`
4
+ * `kenaz launch --provider deepseek [--model deepseek-flash (default)|deepseek-v4-pro|...] [claude args...]`
5
5
  * Starts Claude Code with every model request sent to another vendor's Anthropic-compatible endpoint, so
6
6
  * Ansuz (/kenaz:Ansuz), its subagents and background requests run on that vendor and use no Claude quota.
7
7
  * The key is read from the env or ~/.kenaz/credentials.yaml and set only in the claude child's env: never in
@@ -9,7 +9,7 @@
9
9
  */
10
10
  const os = require('os');
11
11
  const { spawn } = require('child_process');
12
- const { deepseekKey, deepseekEnv, deepseekModels, DEEPSEEK_UNSET, DEEPSEEK_MODELS, winQuote } = require('./exec');
12
+ const { deepseekKey, deepseekEnv, deepseekModels, DEEPSEEK_UNSET, DEEPSEEK_MODELS, winQuote, providerDefault, saveProviderDefault, MODEL_ID } = require('./exec');
13
13
 
14
14
  const PROVIDERS = {
15
15
  deepseek: {
@@ -17,29 +17,31 @@ const PROVIDERS = {
17
17
  keyHelp: 'set DEEPSEEK_API_KEY, or add `deepseek_api_key: <key>` to ~/.kenaz/credentials.yaml',
18
18
  },
19
19
  };
20
- const USAGE = 'usage: kenaz launch --provider <' + Object.keys(PROVIDERS).join('|') + '> [--model <id>] [claude args...]';
20
+ const USAGE = 'usage: kenaz launch --provider <' + Object.keys(PROVIDERS).join('|') + '> [--model <id> [--save-default]] [claude args...]';
21
21
 
22
22
  function parse(argv) {
23
- const rest = []; let provider = null; let model = null;
23
+ const rest = []; let provider = null; let model = null; let save = false;
24
24
  for (let i = 0; i < argv.length; i++) {
25
+ if (argv[i] === '--save-default') { save = true; continue; }
25
26
  const m = /^--(provider|model)(?:=(.*))?$/.exec(argv[i]);
26
27
  if (!m) { rest.push(argv[i]); continue; }
27
28
  const v = m[2] !== undefined ? m[2] : argv[++i];
28
29
  if (m[1] === 'provider') provider = v; else model = v;
29
30
  }
30
- return { rest, provider, model };
31
+ return { rest, provider, model, save };
31
32
  }
32
33
 
33
- /** Child process plan: { bin, args, env }. live = {id: context_window} from the vendor's model list (or null):
34
- * a listed id is valid even if it is newer than the built-in list, and its window drives auto-compact.
35
- * Throws with a user-facing message. */
34
+ /** Child process plan: { bin, args, env }. Model = --model, else the user's own default (KENAZ_<P>_MODEL or
35
+ * ~/.kenaz/config.json, see providerDefault), else the built-in default. live = {id: context_window} from the
36
+ * vendor's model list (or null): when known it is the only validation (new vendor models work without a Kenaz
37
+ * release); when unreachable any well-formed id is accepted. Throws with a user-facing message. */
36
38
  function plan(argv, env = process.env, home = os.homedir(), live = null) {
37
39
  const { rest, provider, model } = parse(argv);
38
40
  const p = PROVIDERS[provider];
39
41
  if (!p) throw new Error((provider ? `unknown provider '${provider}'. ` : '') + USAGE);
40
- const id = model || p.models[0];
41
- const valid = [...new Set([...p.models, ...Object.keys(live || {})])];
42
- if (!valid.includes(id)) throw new Error(`unknown ${provider} model '${id}'; valid: ${valid.join(', ')}`);
42
+ const id = model || providerDefault(provider, env, home) || p.models[0];
43
+ if (!MODEL_ID.test(id)) throw new Error(`invalid ${provider} model id '${id}'`);
44
+ if (live && !(id in live)) throw new Error(`unknown ${provider} model '${id}'; the vendor currently lists: ${Object.keys(live).join(', ')}`);
43
45
  const key = p.key(env, home);
44
46
  if (!key) throw new Error(`${provider}: no API key (${p.keyHelp})`);
45
47
  const childEnv = { ...env, ...p.env(id, key, live && live[id]), CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
@@ -56,6 +58,11 @@ async function main(argv, out = console.log, deps = {}) {
56
58
  const live = key ? await (deps.live || p.live)(key) : null;
57
59
  let pl;
58
60
  try { pl = plan(argv, env, home, live); } catch (e) { out(e.message); return 2; }
61
+ const { model: asked, save } = parse(argv);
62
+ if (save) {
63
+ if (!asked) { out('--save-default needs --model <id>'); return 2; }
64
+ out(`kenaz launch: ${pl.provider} default model saved: ${pl.model} (${saveProviderDefault(pl.provider, pl.model, home)})`);
65
+ }
59
66
  out(`kenaz launch: Claude Code on ${pl.provider} (${pl.model}, context ${pl.context} tokens); no Claude quota is used in this session`);
60
67
  const win = (deps.platform || process.platform) === 'win32'; // claude is a .cmd shim on npm installs
61
68
  const child = (deps.spawn || spawn)(win ? [pl.bin, ...pl.args].map(winQuote).join(' ') : pl.bin, win ? [] : pl.args,
@@ -25,7 +25,7 @@ test('deepseek: every model slot points at DeepSeek, key only in the child env,
25
25
  assert.strictEqual(e.ANTHROPIC_BASE_URL, 'https://api.deepseek.com/anthropic');
26
26
  assert.strictEqual(e.ANTHROPIC_AUTH_TOKEN, KEY);
27
27
  for (const k of ['ANTHROPIC_MODEL', 'ANTHROPIC_DEFAULT_SONNET_MODEL', 'ANTHROPIC_DEFAULT_OPUS_MODEL', 'CLAUDE_CODE_SUBAGENT_MODEL']) {
28
- assert.strictEqual(e[k], 'deepseek-v4-pro', k);
28
+ assert.strictEqual(e[k], 'deepseek-flash', k);
29
29
  }
30
30
  assert.strictEqual(e.ANTHROPIC_SMALL_FAST_MODEL, 'deepseek-flash'); assert.strictEqual(e.ANTHROPIC_DEFAULT_HAIKU_MODEL, 'deepseek-flash');
31
31
  assert.strictEqual(e.CLAUDE_CODE_MAX_CONTEXT_TOKENS, '1048576', 'V4 window from the built-in table when the live list is unavailable');
@@ -54,7 +54,7 @@ test('control: no key, unknown provider or model -> exit 2 and claude is never s
54
54
  [['--provider', 'deepseek'], {}, /no API key/],
55
55
  [['--provider', 'nope'], { DEEPSEEK_API_KEY: KEY }, /unknown provider 'nope'/],
56
56
  [[], { DEEPSEEK_API_KEY: KEY }, /usage: kenaz launch --provider <deepseek>/],
57
- [['--provider', 'deepseek', '--model', 'gpt-x'], { DEEPSEEK_API_KEY: KEY }, /valid: deepseek-v4-pro, deepseek-flash, deepseek-chat, deepseek-reasoner/],
57
+ [['--provider', 'deepseek', '--model', 'bad id'], { DEEPSEEK_API_KEY: KEY }, /invalid deepseek model id/],
58
58
  ]) {
59
59
  const calls = [], lines = [];
60
60
  const rc = await main(argv, (l) => lines.push(l), { spawn: fakeSpawn(calls), live: async () => null, env, home: home(), platform: 'linux' });
@@ -74,3 +74,27 @@ test('live model list: a newer listed id is accepted and its context window driv
74
74
  assert.strictEqual(calls[0].opts.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS, '2097152');
75
75
  assert.match(lines[0], /context 2097152 tokens/);
76
76
  });
77
+
78
+ test('model not in the vendor list is refused only when the list is known; unknown list = accepted (no release needed)', async () => {
79
+ const calls = [], lines = [];
80
+ const rc = await main(['--provider', 'deepseek', '--model', 'gpt-x'], (l) => lines.push(l), { spawn: fakeSpawn(calls), live: async () => ({ 'deepseek-flash': 1048576 }), env: { DEEPSEEK_API_KEY: KEY }, home: home(), platform: 'linux' });
81
+ assert.strictEqual(rc, 2); assert.strictEqual(calls.length, 0); assert.match(lines.join(String.fromCharCode(10)), /the vendor currently lists: deepseek-flash/);
82
+ const calls2 = [];
83
+ assert.strictEqual(await main(['--provider', 'deepseek', '--model', 'deepseek-v9'], () => {}, { spawn: fakeSpawn(calls2), live: async () => null, env: { DEEPSEEK_API_KEY: KEY }, home: home(), platform: 'linux' }), 0);
84
+ assert.strictEqual(calls2[0].opts.env.ANTHROPIC_MODEL, 'deepseek-v9');
85
+ });
86
+
87
+ test('the user picks the default: --save-default writes ~/.kenaz/config.json (other keys kept), env overrides it, --model overrides both', async () => {
88
+ const h = home(); fs.mkdirSync(path.join(h, '.kenaz'));
89
+ fs.writeFileSync(path.join(h, '.kenaz', 'config.json'), JSON.stringify({ setupCompleted: true }));
90
+ const run = async (argv, env = {}) => { const calls = [], lines = []; const rc = await main(argv, (l) => lines.push(l), { spawn: fakeSpawn(calls), live: async () => null, env: { DEEPSEEK_API_KEY: KEY, ...env }, home: h, platform: 'linux' }); return { rc, model: calls[0] && calls[0].opts.env.ANTHROPIC_MODEL, lines }; };
91
+ assert.strictEqual((await run(['--provider', 'deepseek'])).model, 'deepseek-flash', 'built-in default');
92
+ const saved = await run(['--provider', 'deepseek', '--model', 'deepseek-v4-pro', '--save-default']);
93
+ assert.strictEqual(saved.model, 'deepseek-v4-pro'); assert.match(saved.lines.join(String.fromCharCode(10)), /default model saved: deepseek-v4-pro/);
94
+ const cfg = JSON.parse(fs.readFileSync(path.join(h, '.kenaz', 'config.json'), 'utf8'));
95
+ assert.strictEqual(cfg.setupCompleted, true); assert.strictEqual(cfg.providers.deepseek.model, 'deepseek-v4-pro');
96
+ assert.strictEqual((await run(['--provider', 'deepseek'])).model, 'deepseek-v4-pro', 'saved default used');
97
+ assert.strictEqual((await run(['--provider', 'deepseek'], { KENAZ_DEEPSEEK_MODEL: 'deepseek-chat' })).model, 'deepseek-chat', 'env overrides saved');
98
+ assert.strictEqual((await run(['--provider', 'deepseek', '--model', 'deepseek-flash'])).model, 'deepseek-flash', '--model overrides');
99
+ assert.strictEqual((await run(['--provider', 'deepseek', '--save-default'])).rc, 2, '--save-default without --model');
100
+ });
@@ -19,11 +19,11 @@ function renderBootstrap({ policy, projectPath, port = '18080', contextual = fal
19
19
  const fast = [
20
20
  'Ansuz fast boot (elixir) is the default. This bootstrap policy supersedes any older instruction to run Phase 0, including the Phase 0 sentence inside elixir.',
21
21
  'Check kenaz_ping with the active projectPath when available. If unavailable, report that the Kenaz server is unavailable and continue only read-only inspection and analysis using local evidence.',
22
- 'Lightweight Phase 0 uses the supplied boot context and local project evidence; do not run phase-gate Phase 0 or its boot rituals in fast mode. This also supersedes legacy handoff instructions that promise Phase 0 archival. When offline, defer state-changing execution and memory-dependent work until the required service is restored.',
22
+ 'Lightweight Phase 0 uses the supplied boot context and local project evidence; do not run Phase 0 boot rituals in fast mode. This also supersedes legacy handoff instructions that promise Phase 0 archival. When offline, defer state-changing execution and memory-dependent work until the required service is restored.',
23
23
  'Existing authorization, scope, verification, task tracking, and merge restrictions remain in force.',
24
24
  ].join('\n');
25
- const command = `node .specialist/tools/phase-gate.js enter --phase=0 --specialist=${(policy?.specialist || 'Ansuz').toLowerCase()}${projectPath ? ` --project-path="${projectPath}"` : ''}`;
26
- const full = `Before running phase-gate enter, call kenaz_ping with the active projectPath. If it errors or times out, HALT and emit recovery: confirm Kenaz Dashboard on port ${port}, re-run setup-codex, and restart the Codex session.\nExecute Phase 0 initialization now: run \`${command}\` and follow its checklist.`;
25
+ const full = `Before starting Phase 0, call kenaz_ping with the active projectPath${projectPath ? ` ("${projectPath}")` : ''}. If it errors or times out, HALT and emit recovery: confirm the Kenaz server on port ${port}, re-run setup-codex, and restart the Codex session.
26
+ Execute Phase 0 initialization now using the supplied boot context and local project evidence.`;
27
27
  if (contextual) return `## Codex Bootstrap Policy\n\nFor Ansuz without an explicit full boot or legacy depth:\n${fast}\n\nFor explicit --full / --boot-mode full or kernel depth, and for other specialists: retain the full bootstrap guard; call kenaz_ping before Phase 0 and HALT with recovery instructions if it is unavailable. Use the active specialist and project-path for Phase 0.\n`;
28
28
  return `## Codex Bootstrap Policy (${policy.mode})\n\n${policy.mode === 'fast' ? fast : full}\n`;
29
29
  }
@@ -28,8 +28,7 @@ test('renderer preserves full project pin and limits fast offline work without r
28
28
  const projectPath = 'C:\\example project\\quotation';
29
29
  const full = renderBootstrap({ policy: resolveBootPolicy({ full: true }), projectPath });
30
30
  assert.match(full, /HALT/);
31
- assert.ok(full.includes(`--project-path="${projectPath}"`));
32
- assert.match(full, /--specialist=ansuz/);
31
+ assert.ok(full.includes(`"${projectPath}"`));
33
32
  const fast = renderBootstrap({ policy: resolveBootPolicy() });
34
33
  assert.match(fast, /Lightweight Phase 0/);
35
34
  assert.match(fast, /supersedes any older instruction/);
@@ -0,0 +1,42 @@
1
+ // dispatch.js without --project-path, run from a user's project through the plugin (cwd = that project, ROOT = core):
2
+ // the card and the worktree belong to the cwd project, not the core (repro 2026-10-12: '任務不存在' and a worktree
3
+ // created inside the core repo, leaving Ansuz waiting on a subagent that had no card).
4
+ const test = require('node:test');
5
+ const assert = require('node:assert');
6
+ const fs = require('fs');
7
+ const os = require('os');
8
+ const path = require('path');
9
+ const { spawnSync } = require('child_process');
10
+
11
+ const ROOT = path.resolve(__dirname, '..', '..', '..');
12
+ const DISPATCH = path.join(ROOT, '.formation', 'tools', 'dispatch.js');
13
+ const git = (cwd, args) => spawnSync('git', ['-c', 'user.name=t', '-c', 'user.email=t@t', ...args], { cwd, encoding: 'utf8' });
14
+
15
+ function project() {
16
+ const p = fs.mkdtempSync(path.join(os.tmpdir(), 'dcp-'));
17
+ git(p, ['init', '-q']); fs.writeFileSync(path.join(p, 'README.md'), 'x'); git(p, ['add', '-A']); git(p, ['commit', '-q', '-m', 'init']);
18
+ for (const d of ['backlog', 'active', 'done', 'blocked', 'cancel']) fs.mkdirSync(path.join(p, '.kenaz', 'tasks', d), { recursive: true });
19
+ fs.writeFileSync(path.join(p, '.kenaz', 'project.yaml'), 'name: dcp\n');
20
+ fs.writeFileSync(path.join(p, '.kenaz', 'tasks', 'backlog', 'TASK_7201_x.yaml'),
21
+ 'id: TASK_7201\ntitle: "Unique card title 7201"\nstatus: backlog\npriority: low\nrigor: low\nrequirements: ["r"]\nacceptance_criteria: ["a"]\nscope_paths: [x.txt]\n');
22
+ return p;
23
+ }
24
+
25
+ const env = () => { const e = { ...process.env }; for (const k of ['CLAUDE_PROJECT_DIR', 'KENAZ_PROVIDER', 'ANTHROPIC_BASE_URL', 'CODEX_SESSION_ID', 'CODEX_THREAD_ID']) delete e[k]; return e; };
26
+
27
+ test('from a user project without --project-path: the card is found and isolation targets that project', () => {
28
+ const p = project();
29
+ const r = spawnSync(process.execPath, [DISPATCH, '--skill=do', '--task=TASK_7201', '--dispatch-mode=subagent', '--dry-run'], { cwd: p, env: env(), encoding: 'utf8' });
30
+ assert.strictEqual(r.status, 0, r.stderr);
31
+ assert.doesNotMatch(r.stderr, /任務不存在/);
32
+ const j = JSON.parse(r.stdout);
33
+ assert.match(j.promptText, /Unique card title 7201/);
34
+ assert.match(r.stderr, new RegExp('cross-project detected .*' + path.basename(p)));
35
+ });
36
+
37
+ test('control: in the core repo itself nothing changes (same-project Agent worktree)', () => {
38
+ const r = spawnSync(process.execPath, [DISPATCH, '--skill=do', '--task=TASK_2376', '--dispatch-mode=subagent', '--dry-run'], { cwd: ROOT, env: env(), encoding: 'utf8' });
39
+ assert.strictEqual(r.status, 0, r.stderr);
40
+ const j = JSON.parse(r.stdout);
41
+ assert.strictEqual(j.isolation.mode, 'agent_tool_worktree');
42
+ });