kenaz 0.3.22 → 0.3.24

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 (474) hide show
  1. package/core/.claude/hooks/_user-scope.js +1 -1
  2. package/core/.claude/hooks/core-resolve.js +1 -1
  3. package/core/.claude/hooks/worktree-create.js +11 -29
  4. package/core/.claude/hooks/worktree-remove.js +12 -38
  5. package/core/.collaboration/core/reconcile.js +16 -1
  6. package/core/.collaboration/core/task-2467-lifecycle.test.js +63 -0
  7. package/core/.collaboration/core/task-api.js +37 -7
  8. package/core/.formation/bifrost/launch.js +1 -1
  9. package/core/.formation/tools/dispatch-detail/worktree-subagent-mode.md +6 -1
  10. package/core/.formation/tools/kenaz-stats.js +1 -1
  11. package/core/.formation/tools/post-complete/git-merge.js +1 -1
  12. package/core/.formation/tools/prompt.js +1 -1
  13. package/core/.formation/tools/utils.js +5 -5
  14. package/core/.formation/tools/worktree-reclaim.js +73 -2
  15. package/core/.formation/tools/worktree-reclaim.test.js +113 -0
  16. package/core/.kenaz/specialist/ansuz_rules.yaml +7 -51
  17. package/core/.kenaz/specialist/ansuz_session_handoff.schema.json +2 -6
  18. package/core/.rule/general/project-self-containment.yaml +2 -2
  19. package/core/.specialist/Amelia/README.md +1 -1
  20. package/core/.specialist/Amelia/rule.yaml +1 -1
  21. package/core/.specialist/Ansuz/RULES.md +6 -2
  22. package/core/.specialist/Ansuz/concepts/commitment_layer.yaml +1 -1
  23. package/core/.specialist/Ansuz/concepts/evolvable_guardrails.yaml +1 -1
  24. package/core/.specialist/Ansuz/knowledge.yaml +1 -3
  25. package/core/.specialist/tools/__tests__/task-2409-lessons.test.js +10 -0
  26. package/core/.specialist/tools/__tests__/task-2427-harvest.test.js +25 -0
  27. package/core/.specialist/tools/ansuz-boot-fast.js +1 -1
  28. package/core/.specialist/tools/failure-detector.js +4 -4
  29. package/core/.specialist/tools/handoff-doc.js +1 -1
  30. package/core/.specialist/tools/lessons.js +2 -2
  31. package/core/CLAUDE.md +5 -5
  32. package/core/KENAZ_CORE_VERSION +1 -1
  33. package/core/dashboard/scripts/install-kenaz-cli.js +56 -30
  34. package/core/dev/scripts/check-file-size.js +1 -1
  35. package/core/dev/scripts/ci-feedback.js +1 -1
  36. package/core/dev/scripts/gc-agent-worktrees.js +13 -12
  37. package/core/dev/scripts/kenaz-session.js +3 -3
  38. package/core/dev/scripts/peer-identity-e2e.js +6 -4
  39. package/core/dev/scripts/run-rust-tests.js +2 -2
  40. package/core/dev/scripts/skill-routing-replay.js +3 -3
  41. package/core/dev/scripts/task1961/selftest.js +2 -2
  42. package/core/dev/scripts/windows-hide-guard.test.js +112 -0
  43. package/core/plugins/kenaz/guidance.md +1 -0
  44. package/core/plugins/kenaz/hooks/core-resolve.js +1 -1
  45. package/core/plugins/kenaz/hooks/hooks.json +9 -0
  46. package/core/plugins/kenaz/hooks/kenaz-hook.js +58 -4
  47. package/core/plugins/kenaz/hooks/worktree-guard.js +121 -0
  48. package/core/plugins/kenaz/hooks/worktree-guard.test.js +75 -0
  49. package/core/plugins/kenaz/scripts/kenaz.test.js +72 -5
  50. package/core/plugins/kenaz-peer/hooks/register.test.ts +14 -2
  51. package/core/plugins/kenaz-peer/hooks/register.ts +6 -1
  52. package/lib/cli.js +9 -5
  53. package/package.json +6 -6
  54. package/vendor/install-kenaz-cli.js +56 -30
  55. package/core/.kenaz/designs/METAPLASTICITY_CONFIDENCE_DESIGN.md +0 -537
  56. package/core/.kenaz/designs/REFLEX_INITIATOR_DESIGN.md +0 -501
  57. package/core/.kenaz/designs/TASK_1385_handoff_mcp_design.md +0 -426
  58. package/core/.kenaz/designs/TASK_932_hippocampus_consolidation.md +0 -103
  59. package/core/.kenaz/designs/TASK_934_efe_signal_engine.md +0 -97
  60. package/core/.kenaz/designs/TASK_935_self_evolution_framework.md +0 -154
  61. package/core/.kenaz/designs/VISION_cognitive_architecture_roadmap.md +0 -159
  62. package/core/.kenaz/designs/metaplasticity_simulation_results.json +0 -696
  63. package/core/.kenaz/main-memory/README.md +0 -230
  64. package/core/.kenaz/main-memory/kenaz/cortex/001_cortex_overview.md +0 -168
  65. package/core/.kenaz/main-memory/kenaz/cortex/001_rag_engine_overview.md +0 -30
  66. package/core/.kenaz/main-memory/kenaz/memory-system/001_three_tier_architecture.md +0 -361
  67. package/core/.kenaz/main-memory/kenaz/pipeline/001_task_execution_flow.md +0 -117
  68. package/core/.kenaz/main-memory/kenaz/pipeline/001_task_granularity.md +0 -154
  69. package/core/.kenaz/main-memory/kenaz/pipeline/002_team_priority.md +0 -51
  70. package/core/.kenaz/main-memory/kenaz/pipeline/003_feature_id_system.md +0 -46
  71. package/core/.kenaz/main-memory/kenaz/rules/001_gen_007_self_containment.md +0 -108
  72. package/core/.kenaz/main-memory/kenaz/rules/001_rule_priority_system.md +0 -121
  73. package/core/.kenaz/main-memory/kenaz/rules/002_agt_006_pre_check.md +0 -0
  74. package/core/.kenaz/main-memory/kenaz/rules/003_ppl_002_task_archival.md +0 -48
  75. package/core/.kenaz/main-memory/kenaz/system/001_dual_track_task_system.md +0 -197
  76. package/core/.kenaz/main-memory/knowledge/patterns/001_json_bom_issue.md +0 -78
  77. package/core/.kenaz/main-memory/knowledge/patterns/001_leader_worker_pattern.md +0 -0
  78. package/core/.kenaz/main-memory/knowledge/patterns/002_scope_path_conflict.md +0 -115
  79. package/core/.kenaz/main-memory/knowledge/patterns/002_task_dependency_resolution.md +0 -223
  80. package/core/.kenaz/main-memory/knowledge/patterns/003_question_protocol.md +0 -242
  81. package/core/.kenaz/main-memory/knowledge/patterns/004_user_insight_value.md +0 -111
  82. package/core/.kenaz/main-memory/knowledge/patterns/005_file_lock_pattern.md +0 -274
  83. package/core/.kenaz/main-memory/knowledge/references/001_ppl_rules.md +0 -28
  84. package/core/.kenaz/main-memory/knowledge/solutions/001_json_bom_issue.md +0 -137
  85. package/core/.kenaz/main-memory/knowledge/solutions/001_team_invocation.md +0 -0
  86. package/core/.kenaz/main-memory/knowledge/solutions/002_passive_monitoring.md +0 -0
  87. package/core/.kenaz/main-memory/knowledge/solutions/003_windows_file_race_condition.md +0 -239
  88. package/core/.kenaz/main-memory/registry.json +0 -326
  89. package/core/.kenaz/main-memory/version.yaml +0 -58
  90. package/core/.kenaz/specialist/memory_guard_fired.json +0 -1
  91. package/core/.kenaz/specialist/memory_guard_trigger_cache.json +0 -1
  92. package/core/.kenaz/specialist/memory_source_priority.yaml +0 -260
  93. package/core/.kenaz/specialist/prompt_1775.txt +0 -317
  94. package/core/.kenaz/specialist/prompt_1776.txt +0 -326
  95. package/core/.kenaz/specialist/prompt_1791.txt +0 -320
  96. package/core/.kenaz/specialist/prompt_1792.txt +0 -320
  97. package/core/.kenaz/specialist/prompt_1795.txt +0 -281
  98. package/core/.kenaz/specialist/prompt_1813.txt +0 -332
  99. package/core/.kenaz/specialist/prompt_1814.txt +0 -401
  100. package/core/.kenaz/specialist/prompt_1815.txt +0 -394
  101. package/core/.kenaz/specialist/prompt_1816.txt +0 -437
  102. package/core/.kenaz/specialist/prompt_1818.txt +0 -361
  103. package/core/.kenaz/specialist/prompt_1819.txt +0 -421
  104. package/core/.kenaz/specialist/prompt_1821.txt +0 -345
  105. package/core/.kenaz/specialist/prompt_1822.txt +0 -313
  106. package/core/.kenaz/specialist/prompt_1824.txt +0 -348
  107. package/core/.kenaz/specialist/prompt_1826.txt +0 -388
  108. package/core/.kenaz/specialist/rune_efe_salience_gap.md +0 -21
  109. package/core/.kenaz/specialist/task1825_update.json +0 -43
  110. package/core/.kenaz/specialist/taskgen/consolidation_i.json +0 -38
  111. package/core/.kenaz/specialist/taskgen/csharp_a.json +0 -44
  112. package/core/.kenaz/specialist/taskgen/csharp_b.json +0 -40
  113. package/core/.kenaz/specialist/taskgen/embedding_e.json +0 -34
  114. package/core/.kenaz/specialist/taskgen/hygiene_fg.json +0 -39
  115. package/core/.kenaz/specialist/taskgen/pathology_h.json +0 -36
  116. package/core/.kenaz/specialist/taskgen/unity_noise_d.json +0 -38
  117. package/core/.kenaz/specialist/taskgen/visibility_c.json +0 -38
  118. package/core/.specialist/Ansuz/decisions/ADR-requirement-graph-layer.md +0 -390
  119. package/core/.specialist/Ansuz/self_model_seed.backup.json +0 -100
  120. package/core/.specialist/ansuz_session_handoff.json +0 -96
  121. package/core/dev/ALAYA_FIELD_FEEDBACK_DESIGN.md +0 -126
  122. package/core/dev/ANSUZ_SCAFFOLD_DIET_2026-09-20.md +0 -231
  123. package/core/dev/ANSUZ_STARTUP_INJECTION_REVIEW.md +0 -355
  124. package/core/dev/ANSUZ_WORKFLOW_MODERNIZATION.md +0 -143
  125. package/core/dev/CORTEX_META_GRAPH_ASSESSMENT.md +0 -99
  126. package/core/dev/FREYA_DOWNSTREAM_BUGS_2026-09-19.md +0 -44
  127. package/core/dev/GLOBAL_MEMORY_IDENTITY_CONSOLIDATION.md +0 -263
  128. package/core/dev/GUARD_EFFECTIVENESS_DESIGN.md +0 -180
  129. package/core/dev/HEALTH_GATE_VARIANCE_ASSESSMENT.md +0 -271
  130. package/core/dev/HOOK_DIET_2026-09-21.md +0 -126
  131. package/core/dev/JEV_INTEGRATION_PLAN.md +0 -189
  132. package/core/dev/KENAZ_AGENT_CONNECT_v1_SPEC.md +0 -139
  133. package/core/dev/MOBILE_DESIGN_SPEC.md +0 -569
  134. package/core/dev/MULTI_ANSUZ_SPEC.md +0 -136
  135. package/core/dev/MUNINN_RETIREMENT.md +0 -115
  136. package/core/dev/PHASE3_ATTRIBUTION_POPULATION_DIAGNOSIS.md +0 -211
  137. package/core/dev/PHASE3_P3-2B_TASKLEVEL_PROBE.md +0 -83
  138. package/core/dev/PHASE3_P3-2_POPULATION_DISJOINTNESS.md +0 -83
  139. package/core/dev/PHASE3_P3-2_REMEASURE_REPORT.md +0 -231
  140. package/core/dev/PREDICTION_GROUNDING_DESIGN.md +0 -376
  141. package/core/dev/PRODUCT_SPEC.md +0 -317
  142. package/core/dev/PROJECT_REGISTRY_DUPLICATE_ASSESSMENT.md +0 -86
  143. package/core/dev/STARTUP_TIMELINE_TASK_1995.md +0 -181
  144. package/core/dev/SYNAPSE_WORKER_PER_PROJECT.md +0 -881
  145. package/core/dev/TASK_1680_P3-3_DISPATCH_SPEC.md +0 -96
  146. package/core/dev/TASK_1684_CALIBRATION_IMPACT_ANALYSIS.md +0 -170
  147. package/core/dev/TASK_1684_FORK_A_GOODHART_DESIGN.md +0 -171
  148. package/core/dev/TASK_1684_P3_2_REMEASURE_VALIDATION.md +0 -262
  149. package/core/dev/TASK_1903_SIGNAL_AUC_MEASUREMENT.md +0 -207
  150. package/core/dev/TASK_1904_BRANCH_INTEGRITY_AUDIT.md +0 -146
  151. package/core/dev/ansuz-eval/README.md +0 -38
  152. package/core/dev/ansuz-eval/build-fixtures.js +0 -87
  153. package/core/dev/ansuz-eval/grade.js +0 -96
  154. package/core/dev/ansuz-eval/grader.test.js +0 -117
  155. package/core/dev/ansuz-eval/tasks.md +0 -32
  156. package/core/dev/archive/ansuz/BOOT_FULL.md +0 -141
  157. package/core/dev/archive/ansuz/agents/ansuz-boot.md +0 -176
  158. package/core/dev/archive/ansuz/designs/alaya-passive-surfacing.md +0 -84
  159. package/core/dev/archive/ansuz/designs/cc_memory_indexing.md +0 -307
  160. package/core/dev/archive/ansuz/designs/check_guard.md +0 -322
  161. package/core/dev/archive/ansuz/designs/consequence_loop.md +0 -227
  162. package/core/dev/archive/ansuz/designs/mcp_kuzudb_lock_architecture.md +0 -312
  163. package/core/dev/archive/ansuz/designs/phase0_git_enrichment.md +0 -278
  164. package/core/dev/archive/ansuz/designs/refactor_mcp_tools_synapse.md +0 -490
  165. package/core/dev/archive/ansuz/designs/semantic_markdown_extraction.md +0 -167
  166. package/core/dev/archive/ansuz/designs/task_1372_prompt_injects_into.md +0 -193
  167. package/core/dev/archive/ansuz/designs/thought_stack.md +0 -286
  168. package/core/dev/archive/ansuz/investigations/reachability_unified_api_2026-04-11.md +0 -330
  169. package/core/dev/archive/ansuz/investigations/synapse_orphans_indexing_bug_2026-04-11.md +0 -352
  170. package/core/dev/archive/ansuz/investigations/synapse_orphans_reindex_verification_2026-04-11.md +0 -48
  171. package/core/dev/archive/ansuz/investigations/synapse_orphans_verification_2026-04-11.md +0 -170
  172. package/core/dev/archive/ansuz/legacy/kernel.yaml +0 -399
  173. package/core/dev/archive/ansuz/legacy/phases/phase_0.yaml +0 -418
  174. package/core/dev/archive/ansuz/legacy/phases/phase_1.yaml +0 -376
  175. package/core/dev/archive/ansuz/legacy/phases/phase_10.yaml +0 -35
  176. package/core/dev/archive/ansuz/legacy/phases/phase_11.yaml +0 -582
  177. package/core/dev/archive/ansuz/legacy/phases/phase_2.yaml +0 -94
  178. package/core/dev/archive/ansuz/legacy/phases/phase_3.yaml +0 -274
  179. package/core/dev/archive/ansuz/legacy/phases/phase_4.yaml +0 -67
  180. package/core/dev/archive/ansuz/legacy/phases/phase_5.yaml +0 -32
  181. package/core/dev/archive/ansuz/legacy/phases/phase_6.yaml +0 -91
  182. package/core/dev/archive/ansuz/legacy/phases/phase_7.yaml +0 -58
  183. package/core/dev/archive/ansuz/legacy/phases/phase_8.yaml +0 -146
  184. package/core/dev/archive/ansuz/legacy/phases/phase_9.yaml +0 -424
  185. package/core/dev/archive/ansuz/specs/INDEX_bifrost_hook_parity.yaml +0 -92
  186. package/core/dev/archive/ansuz/specs/active-inference-formation.yaml +0 -453
  187. package/core/dev/archive/ansuz/specs/brief_TASK_1228_userpromptsubmit.yaml +0 -191
  188. package/core/dev/archive/ansuz/specs/brief_TASK_1229_pretooluse_posttooluse.yaml +0 -250
  189. package/core/dev/archive/ansuz/specs/brief_TASK_1230_sessionstart_stop.yaml +0 -301
  190. package/core/dev/archive/ansuz/specs/brief_TASK_1231_verification.yaml +0 -272
  191. package/core/dev/archive/skills/feature_agent/SKILL.md +0 -289
  192. package/core/dev/design/PHASE4_DESIGN.md +0 -559
  193. package/core/dev/design/dashboard_docs_integration_PLAN.md +0 -388
  194. package/core/dev/design/matrix_format_ABBREVIATIONS.md +0 -655
  195. package/core/dev/design/matrix_format_DESIGN_SPEC.md +0 -601
  196. package/core/dev/design/matrix_format_DISCUSSION_SUMMARY.md +0 -484
  197. package/core/dev/design/matrix_format_EXAMPLE_memory.matrix +0 -13
  198. package/core/dev/design/matrix_format_EXAMPLE_project_status.matrix +0 -9
  199. package/core/dev/design/matrix_format_EXAMPLE_tasks.matrix +0 -14
  200. package/core/dev/design/matrix_format_SYNTAX_BNF.md +0 -754
  201. package/core/dev/design/matrix_format_TOKEN_COMPARISON.md +0 -484
  202. package/core/dev/design/matrix_v2_COGNITIVE_DESIGN.md +0 -503
  203. package/core/dev/design/matrix_v2_DISCUSSION_SUMMARY.md +0 -414
  204. package/core/dev/design/matrix_v2_EXAMPLE.matrix +0 -67
  205. package/core/dev/designs/EXPANDABLE_CARD_DESIGN_SPEC.md +0 -509
  206. package/core/dev/jev-eval/ACTIVE_CORRECTION.md +0 -52
  207. package/core/dev/jev-eval/ANNOTATION_GUIDE.md +0 -68
  208. package/core/dev/jev-eval/BASELINE_COMPARISON.md +0 -45
  209. package/core/dev/jev-eval/CONTEXT_REVIEW.md +0 -31
  210. package/core/dev/jev-eval/EVALUATION_STATUS.md +0 -39
  211. package/core/dev/jev-eval/HISTORICAL_CORPUS.md +0 -33
  212. package/core/dev/jev-eval/PACKAGING_VERIFICATION.md +0 -42
  213. package/core/dev/jev-eval/README.md +0 -101
  214. package/core/dev/jev-eval/THALAMUS_RANKING.md +0 -58
  215. package/core/dev/jev-eval/VALIDATION_PROGRESS.md +0 -54
  216. package/core/dev/jev-eval/cli.mjs +0 -50
  217. package/core/dev/jev-eval/context.mjs +0 -183
  218. package/core/dev/jev-eval/corpus.mjs +0 -186
  219. package/core/dev/jev-eval/fixtures/conversation-review.seed.jsonl +0 -6
  220. package/core/dev/jev-eval/fixtures/synthetic.jsonl +0 -11
  221. package/core/dev/jev-eval/metrics.mjs +0 -67
  222. package/core/dev/jev-eval/muninn-baseline.mjs +0 -119
  223. package/core/dev/jev-eval/providers.mjs +0 -95
  224. package/core/dev/jev-eval/reports/annotation-pilot-2026-09-20.json +0 -32
  225. package/core/dev/jev-eval/reports/context-review-2026-09-20.json +0 -48
  226. package/core/dev/jev-eval/reports/conversation-review.pending.json +0 -151
  227. package/core/dev/jev-eval/reports/history-corpus-2026-09-20.json +0 -52
  228. package/core/dev/jev-eval/reports/length-probe-2026-09-20.json +0 -133
  229. package/core/dev/jev-eval/reports/live-smoke-2026-09-20.json +0 -616
  230. package/core/dev/jev-eval/reports/muninn-baseline-2026-09-20.json +0 -52
  231. package/core/dev/jev-eval/reports/resilience-2026-09-20.json +0 -520
  232. package/core/dev/jev-eval/reports/rules-baseline.json +0 -197
  233. package/core/dev/jev-eval/reports/synthetic-review.pending.json +0 -357
  234. package/core/dev/jev-eval/resilience.mjs +0 -71
  235. package/core/dev/jev-eval/review.mjs +0 -78
  236. package/core/dev/jev-eval/schema.mjs +0 -87
  237. package/core/dev/jev-eval/test/context.test.mjs +0 -95
  238. package/core/dev/jev-eval/test/corpus.test.mjs +0 -102
  239. package/core/dev/jev-eval/test/eval.test.mjs +0 -146
  240. package/core/dev/jev-eval/test/muninn-baseline.test.mjs +0 -71
  241. package/core/dev/jev-eval/test/resilience.test.mjs +0 -72
  242. package/core/dev/jev-eval/test/review.test.mjs +0 -61
  243. package/core/dev/pipeline/PHASE3_PLANNING_SUMMARY.md +0 -338
  244. package/core/dev/pipeline/PROGRESS.md +0 -113
  245. package/core/dev/pipeline/PROGRESS_template.md +0 -860
  246. package/core/dev/pipeline/QA_REPORT_F008.md +0 -431
  247. package/core/dev/pipeline/QA_REPORT_LOCAL_CLAUDE_DIAGNOSIS.md +0 -583
  248. package/core/dev/pipeline/README.md +0 -210
  249. package/core/dev/pipeline/WORKFLOW_GUIDE.md +0 -824
  250. package/core/dev/pipeline/archived/TASK_015_tauri_init.yaml +0 -53
  251. package/core/dev/pipeline/archived/TASK_016_tauri_commands.yaml +0 -54
  252. package/core/dev/pipeline/archived/TASK_017_rust_pty.yaml +0 -54
  253. package/core/dev/pipeline/archived/TASK_018_tauri_bundle.yaml +0 -53
  254. package/core/dev/pipeline/archived/TASK_019_release_workflow.yaml +0 -56
  255. package/core/dev/pipeline/archived/TASK_020_update_ui.yaml +0 -54
  256. package/core/dev/pipeline/archived/TASK_021_update_notification_ui.yaml +0 -55
  257. package/core/dev/pipeline/archived/TASK_022_remove_node_pty.yaml +0 -65
  258. package/core/dev/pipeline/archived/TASK_023_api_routes_migration.yaml +0 -86
  259. package/core/dev/pipeline/archived/TASK_024_feature_id_system.yaml +0 -51
  260. package/core/dev/pipeline/archived/TASK_025_executable_acceptance.yaml +0 -66
  261. package/core/dev/pipeline/archived/TASK_026_spec_versioning.yaml +0 -56
  262. package/core/dev/pipeline/archived/TASK_027_drift_detection.yaml +0 -60
  263. package/core/dev/pipeline/archived/TASK_028_tauri_pty_manager.yaml +0 -60
  264. package/core/dev/pipeline/archived/TASK_029_tauri_pty_commands.yaml +0 -62
  265. package/core/dev/pipeline/archived/TASK_030_frontend_session_adapter.yaml +0 -54
  266. package/core/dev/pipeline/archived/TASK_031_terminal_tauri_events.yaml +0 -56
  267. package/core/dev/pipeline/archived/TASK_041_scheduler_data_models.yaml +0 -54
  268. package/core/dev/pipeline/archived/TASK_042_path_lock_manager.yaml +0 -53
  269. package/core/dev/pipeline/archived/TASK_043_process_manager.yaml +0 -57
  270. package/core/dev/pipeline/archived/TASK_044_git_integration.yaml +0 -85
  271. package/core/dev/pipeline/archived/TASK_045_task_queue_manager.yaml +0 -54
  272. package/core/dev/pipeline/archived/TASK_046_scheduler_dispatcher.yaml +0 -60
  273. package/core/dev/pipeline/archived/TASK_047_tauri_scheduler_commands.yaml +0 -55
  274. package/core/dev/pipeline/archived/TASK_048_realtime_status_push.yaml +0 -92
  275. package/core/dev/pipeline/archived/TASK_049_frontend_scheduler_integration.yaml +0 -58
  276. package/core/dev/pipeline/archived/TASK_050_error_recovery.yaml +0 -56
  277. package/core/dev/pipeline/archived/TASK_051_optimization_proposals_backend.yaml +0 -53
  278. package/core/dev/pipeline/archived/TASK_052_optimization_proposals_ui.yaml +0 -58
  279. package/core/dev/pipeline/archived/TASK_053_optimization_proposals_diff_view.yaml +0 -54
  280. package/core/dev/pipeline/archived/TASK_054_multi_project_scheduler_state.yaml +0 -76
  281. package/core/dev/pipeline/archived/TASK_055_scheduler_api_project_scope.yaml +0 -64
  282. package/core/dev/pipeline/archived/TASK_056_frontend_scheduler_project_scope.yaml +0 -62
  283. package/core/dev/pipeline/archived/TASK_057_scheduler_module_structure.yaml +0 -54
  284. package/core/dev/pipeline/archived/TASK_058_scheduler_models_extract.yaml +0 -51
  285. package/core/dev/pipeline/archived/TASK_059_scheduler_core_extract.yaml +0 -51
  286. package/core/dev/pipeline/archived/TASK_060_scheduler_commands_extract.yaml +0 -51
  287. package/core/dev/pipeline/archived/TASK_061_scheduler_specialized_commands.yaml +0 -53
  288. package/core/dev/pipeline/archived/TASK_062_process_manager_module_structure.yaml +0 -56
  289. package/core/dev/pipeline/archived/TASK_063_process_manager_core_extract.yaml +0 -56
  290. package/core/dev/pipeline/archived/TASK_064_recovery_module_structure.yaml +0 -57
  291. package/core/dev/pipeline/archived/TASK_065_recovery_core_extract.yaml +0 -56
  292. package/core/dev/pipeline/archived/TASK_066_self_enhancement_scheduler_models.yaml +0 -60
  293. package/core/dev/pipeline/archived/TASK_067_agents_section_quick_actions.yaml +0 -53
  294. package/core/dev/pipeline/archived/TASK_067_self_enhancement_scheduler_core.yaml +0 -65
  295. package/core/dev/pipeline/archived/TASK_068_agent_workflow_rule_enhancement.yaml +0 -46
  296. package/core/dev/pipeline/archived/TASK_068_self_enhancement_tauri_commands.yaml +0 -61
  297. package/core/dev/pipeline/archived/TASK_069_agent_wizard_responsive.yaml +0 -46
  298. package/core/dev/pipeline/archived/TASK_069_self_enhancement_ui.yaml +0 -62
  299. package/core/dev/pipeline/archived/TASK_070_preview_panel_redesign.yaml +0 -62
  300. package/core/dev/pipeline/archived/TASK_070_self_enhancement_integration.yaml +0 -57
  301. package/core/dev/pipeline/archived/TASK_071_preview_panel_qa.yaml +0 -98
  302. package/core/dev/pipeline/archived/TASK_071_single_agent_config.yaml +0 -57
  303. package/core/dev/pipeline/archived/TASK_072_preview_modal_esc_fix.yaml +0 -71
  304. package/core/dev/pipeline/archived/TASK_072_product_review_module.yaml +0 -57
  305. package/core/dev/pipeline/archived/TASK_073_agents_page_component_refactor.yaml +0 -68
  306. package/core/dev/pipeline/archived/TASK_073_rename_single_agent.yaml +0 -64
  307. package/core/dev/pipeline/archived/TASK_074_preview_mode_ui_diagnosis.yaml +0 -71
  308. package/core/dev/pipeline/archived/TASK_074_single_agent_ui_toggle.yaml +0 -56
  309. package/core/dev/pipeline/archived/TASK_075_agents_page_component_refactor.yaml +0 -69
  310. package/core/dev/pipeline/archived/TASK_075_single_agent_integration_test.yaml +0 -75
  311. package/core/dev/pipeline/archived/TASK_076_preview_mode_ui_diagnosis.yaml +0 -70
  312. package/core/dev/pipeline/archived/TASK_076_single_agent_continuous_mode.yaml +0 -65
  313. package/core/dev/pipeline/archived/TASK_077_failure_directory_structure.yaml +0 -51
  314. package/core/dev/pipeline/archived/TASK_077_single_agent_typescript_types.yaml +0 -59
  315. package/core/dev/pipeline/archived/TASK_078_fail_001_agent_no_output.yaml +0 -53
  316. package/core/dev/pipeline/archived/TASK_078_single_agent_ui_simplify.yaml +0 -74
  317. package/core/dev/pipeline/archived/TASK_079_dashboard_rwd_responsive.yaml +0 -63
  318. package/core/dev/pipeline/archived/TASK_079_fail_002_json_bom.yaml +0 -56
  319. package/core/dev/pipeline/archived/TASK_080_fail_003_queue_pollution.yaml +0 -56
  320. package/core/dev/pipeline/archived/TASK_080_http_server_module.yaml +0 -55
  321. package/core/dev/pipeline/archived/TASK_081_failure_logging_tools.yaml +0 -57
  322. package/core/dev/pipeline/archived/TASK_081_http_api_routes.yaml +0 -62
  323. package/core/dev/pipeline/archived/TASK_082_frontend_api_adapter.yaml +0 -51
  324. package/core/dev/pipeline/archived/TASK_082_integrate_failure_to_workflow.yaml +0 -60
  325. package/core/dev/pipeline/archived/TASK_083_cleanup_nextjs_api.yaml +0 -48
  326. package/core/dev/pipeline/archived/TASK_083_task_queue_cleanup.yaml +0 -67
  327. package/core/dev/pipeline/archived/TASK_084_rust_dead_code_cleanup.yaml +0 -66
  328. package/core/dev/pipeline/archived/TASK_084_scheduler_sync_logic_fix.yaml +0 -68
  329. package/core/dev/pipeline/archived/TASK_085_http_api_web_mode_fixes.yaml +0 -46
  330. package/core/dev/pipeline/archived/TASK_085_memory_consolidation_0204_0205.yaml +0 -98
  331. package/core/dev/pipeline/archived/TASK_086_collapsible_preview_panel.yaml +0 -161
  332. package/core/dev/pipeline/archived/TASK_086_web_mode_feature_parity.yaml +0 -62
  333. package/core/dev/pipeline/archived/TASK_087_manual_memory_archival.yaml +0 -163
  334. package/core/dev/pipeline/archived/TASK_087_mobile_http_control.yaml +0 -68
  335. package/core/dev/pipeline/archived/TASK_088_fail_003_to_ppl_002_rule.yaml +0 -63
  336. package/core/dev/pipeline/archived/TASK_088_mobile_http_qa.yaml +0 -121
  337. package/core/dev/pipeline/archived/TASK_089_agent_collaboration_topic.yaml +0 -60
  338. package/core/dev/pipeline/archived/TASK_089_web_mode_monitor_fix.yaml +0 -58
  339. package/core/dev/pipeline/archived/TASK_090_cli_detection_fix.yaml +0 -61
  340. package/core/dev/pipeline/archived/TASK_090_ui_ux_lessons_topic.yaml +0 -63
  341. package/core/dev/pipeline/archived/TASK_091_expandable_card_design.yaml +0 -56
  342. package/core/dev/pipeline/archived/TASK_091_move_agent_section_components.yaml +0 -65
  343. package/core/dev/pipeline/archived/TASK_092_expandable_card_component.yaml +0 -60
  344. package/core/dev/pipeline/archived/TASK_092_refactor_agent_create_wizard.yaml +0 -84
  345. package/core/dev/pipeline/archived/TASK_093_taskcard_detail_integration.yaml +0 -57
  346. package/core/dev/pipeline/archived/TASK_093_update_barrel_exports_test.yaml +0 -84
  347. package/core/dev/pipeline/archived/TASK_094_ai_agent_generation_enhancement.yaml +0 -115
  348. package/core/dev/pipeline/archived/TASK_094_proposalcard_detail_integration.yaml +0 -55
  349. package/core/dev/pipeline/archived/TASK_095_preview_fixes.yaml +0 -129
  350. package/core/dev/pipeline/archived/TASK_095_single_agent_task_selector.yaml +0 -60
  351. package/core/dev/pipeline/archived/TASK_096_agents_page_ux_improvements.yaml +0 -163
  352. package/core/dev/pipeline/archived/TASK_096_single_agent_status_sync.yaml +0 -60
  353. package/core/dev/pipeline/archived/TASK_097_local_claude_error_logging.yaml +0 -59
  354. package/core/dev/pipeline/archived/TASK_097_single_agent_monitor_events.yaml +0 -59
  355. package/core/dev/pipeline/archived/TASK_098_product_manager_team_phase2.yaml +0 -60
  356. package/core/dev/pipeline/archived/TASK_098_single_agent_refactor_test.yaml +0 -58
  357. package/core/dev/pipeline/archived/TASK_099_backend_team_creation.yaml +0 -258
  358. package/core/dev/pipeline/archived/TASK_100_frontend_team_creation.yaml +0 -61
  359. package/core/dev/pipeline/archived/TASK_101_orchestrator_system.yaml +0 -403
  360. package/core/dev/pipeline/archived/TASK_102_designer_sre_teams.yaml +0 -90
  361. package/core/dev/pipeline/archived/TASK_103_integration_testing.yaml +0 -86
  362. package/core/dev/pipeline/archived/TASK_128_integration_testing.yaml +0 -83
  363. package/core/dev/pipeline/archived/TASK_134_cortex_v2_phase3_file_watcher.yaml +0 -65
  364. package/core/dev/pipeline/archived/TASK_134_team_http_api.yaml +0 -96
  365. package/core/dev/pipeline/archived/TASK_135_cortex_v2_phase3_git_monitor.yaml +0 -63
  366. package/core/dev/pipeline/archived/TASK_136_cortex_v2_phase3_change_analyzer.yaml +0 -69
  367. package/core/dev/pipeline/archived/TASK_137_cortex_v2_phase3_cache_invalidator.yaml +0 -67
  368. package/core/dev/pipeline/archived/TASK_137_team_orchestrator_integration.yaml +0 -93
  369. package/core/dev/pipeline/archived/TASK_138_cortex_v2_phase3_distributed_cache.yaml +0 -68
  370. package/core/dev/pipeline/archived/TASK_138_matrix_folder_structure.yaml +0 -61
  371. package/core/dev/pipeline/archived/TASK_139_cortex_v2_phase3_cache_prewarming.yaml +0 -65
  372. package/core/dev/pipeline/archived/TASK_146_unified_documentation.yaml +0 -141
  373. package/core/dev/pipeline/archived/TASK_147_pipeline_execution_rules.yaml +0 -76
  374. package/core/dev/pipeline/archived/TASK_148_update_claude_md.yaml +0 -77
  375. package/core/dev/pipeline/archived/TASK_149_fix_nextjs_mdx_serialization.yaml +0 -63
  376. package/core/dev/pipeline/archived/TASK_149_update_team_docs.yaml +0 -77
  377. package/core/dev/pipeline/archived/TASK_150_pipeline_workflow_guide.yaml +0 -82
  378. package/core/dev/pipeline/archived/TASK_164_fix_smart_context_switch.yaml +0 -49
  379. package/core/dev/pipeline/archived/TASK_165_ctags_symbol_index.yaml +0 -53
  380. package/core/dev/pipeline/archived/TASK_166_symbol_search_mcp_tool.yaml +0 -48
  381. package/core/dev/pipeline/archived/TASK_167_llm_module_summary.yaml +0 -55
  382. package/core/dev/pipeline/archived/TASK_168_map_reduce_context.yaml +0 -51
  383. package/core/dev/pipeline/archived/TASK_169_v2_integration_testing.yaml +0 -52
  384. package/core/dev/pipeline/backups/archive_2026-02-04T22-07-22/TASK_QUEUE_backup.json +0 -95
  385. package/core/dev/pipeline/docs/multi-agent-coordination.md +0 -446
  386. package/core/dev/pipeline/docs/overview.md +0 -406
  387. package/core/dev/pipeline/docs/task-management.md +0 -412
  388. package/core/dev/pipeline/docs/workflows.md +0 -32
  389. package/core/dev/pipeline/orchestrator/schemas/task_queue_schema.json +0 -58
  390. package/core/dev/pipeline/orchestrator/schemas/task_schema.json +0 -139
  391. package/core/dev/pipeline/plans/PLAN_task_cli_refactor.md +0 -281
  392. package/core/dev/pipeline/test_results/TASK_TEST_PARALLEL_002_result.txt +0 -27
  393. package/core/dev/plans/ANSUZ_SLIM_PLAN.md +0 -68
  394. package/core/dev/plans/CLI_FIRST_INSTALL_FLOW.md +0 -120
  395. package/core/dev/plans/FORMATION_RETIREMENT_PLAN.md +0 -313
  396. package/core/dev/plans/KUZU_REPLACEMENT_PLAN.md +0 -126
  397. package/core/dev/scripts/CRASH_DIAGNOSIS.md +0 -47
  398. package/core/dev/scripts/README.md +0 -142
  399. package/core/dev/scripts/RUN_ONCE.bat +0 -40
  400. package/core/dev/scripts/START_DEV_AGENT.bat +0 -63
  401. package/core/dev/scripts/START_MULTI_AGENT.bat +0 -80
  402. package/core/dev/scripts/dev_agent_scheduler.ps1 +0 -536
  403. package/core/dev/scripts/init_git.ps1 +0 -396
  404. package/core/dev/scripts/init_git.sh +0 -337
  405. package/core/dev/scripts/init_project.ps1 +0 -851
  406. package/core/dev/scripts/init_project.sh +0 -477
  407. package/core/dev/scripts/multi_agent_scheduler.ps1 +0 -2021
  408. package/core/dev/scripts/multi_agent_scheduler.sh +0 -1344
  409. package/core/dev/scripts/run_once.sh +0 -47
  410. package/core/dev/scripts/setup_rag.ps1 +0 -304
  411. package/core/dev/scripts/setup_rag.sh +0 -230
  412. package/core/dev/scripts/start_dev_agent.sh +0 -137
  413. package/core/dev/scripts/start_multi_agent.sh +0 -41
  414. package/core/dev/scripts/sync_task_queue.ps1 +0 -505
  415. package/core/dev/scripts/sync_task_queue.sh +0 -392
  416. package/core/dev/specs/agent-connect/PROTOCOL.md +0 -177
  417. package/core/dev/specs/agent-connect/SDK_GUIDE.md +0 -915
  418. package/core/dev/specs/agent-connect/examples/minimal-site/README.md +0 -137
  419. package/core/dev/specs/agent-connect/examples/minimal-site/package.json +0 -12
  420. package/core/dev/specs/agent-connect/examples/minimal-site/public/index.html +0 -116
  421. package/core/dev/specs/agent-connect/examples/minimal-site/public/install-redirect.js +0 -73
  422. package/core/dev/specs/agent-connect/examples/minimal-site/server.js +0 -516
  423. package/core/dev/specs/agent-connect/examples/minimal-site/test/smoke.test.mjs +0 -263
  424. package/core/dev/specs/agent-connect/history/DEFECT_REPORT_kenaz-agent-hub_2026-09-09_v0.4-empty-prompt-no-first-turn.md +0 -77
  425. package/core/dev/specs/agent-connect/history/DEFECT_REPORT_kenaz-agent-hub_2026-09-09_v0.4-prompt-mojibake.md +0 -90
  426. package/core/dev/specs/agent-connect/history/FEATURE_REQUEST_kenaz-agent-hub_2026-09-07.md +0 -61
  427. package/core/dev/specs/agent-connect/history/FEATURE_REQUEST_kenaz-agent-hub_2026-09-08_relay-v1-conformance.md +0 -76
  428. package/core/dev/specs/agent-connect/history/FEATURE_REQUEST_kenaz-agent-hub_2026-09-09_v0.4-session-channel.md +0 -87
  429. package/core/dev/specs/agent-connect/history/FLOWPIX_REPLY_agent-hub_2026-09-08_relay-v1-answers.md +0 -68
  430. package/core/dev/specs/agent-connect/history/FLOWPIX_bridge_CONTRACT.md +0 -642
  431. package/core/dev/specs/agent-connect/history/FLOWPIX_spikes_relay_spike_relay.mjs +0 -244
  432. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-07.md +0 -77
  433. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-08_direction-change-v0.3.md +0 -44
  434. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-08_relay-v1-answers-ack.md +0 -37
  435. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-08_relay-v1-conformance.md +0 -56
  436. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_annotations-install-coldstart.md +0 -44
  437. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_empty-prompt-first-turn.md +0 -56
  438. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_prompt-mojibake-fixed.md +0 -48
  439. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_session-channel.md +0 -57
  440. package/core/dev/specs/agent-connect/history/PROTOCOL_v0.3_snapshot.md +0 -379
  441. package/core/dev/specs/agent-connect/history/README.md +0 -47
  442. package/core/dev/specs/peer/CODEX_INTERACTIVE_PUSH.md +0 -65
  443. package/core/dev/specs/peer/CODEX_MANAGED_DAEMON.md +0 -54
  444. package/core/dev/specs/peer/CODEX_PEER_PULL_SPIKE.md +0 -54
  445. package/core/dev/specs/peer/MODS_SPIKE.md +0 -133
  446. package/core/dev/specs/peer/PER_PROJECT_ADDRESSING.md +0 -143
  447. package/core/dev/specs/peer/SWARM_PROTOCOLS.md +0 -256
  448. package/core/dev/specs/peer/codex-hook-shim-peer.test.js +0 -113
  449. package/core/dev/specs/peer/codex-push-launcher.test.js +0 -142
  450. package/core/dev/specs/peer/setup-codex-hooks.test.js +0 -60
  451. package/core/dev/specs/peer-ansuz/DELIVERY_SPIKE.md +0 -397
  452. package/core/dev/specs/peer-ansuz/DESIGN.md +0 -109
  453. package/core/dev/specs/peer-ansuz/spike/claude_poke.js +0 -129
  454. package/core/dev/specs/peer-ansuz/spike/claude_session_start_hook.js +0 -37
  455. package/core/dev/specs/peer-ansuz/spike/claude_settings_hook.json +0 -14
  456. package/core/dev/specs/peer-ansuz/spike/codex_appserver_client.js +0 -105
  457. package/core/dev/specs/peer-ansuz/spike/codex_appserver_query.js +0 -29
  458. package/core/dev/specs/peer-ansuz/spike/extract_timeline.js +0 -26
  459. package/core/dev/specs/synapse/SYMBOL_IDENTITY.md +0 -305
  460. package/core/dev/spikes/A4-cypher-varlen.md +0 -176
  461. package/core/dev/spikes/bloat-rounds.sh +0 -25
  462. package/core/dev/spikes/mods/clean-claude.js +0 -9
  463. package/core/dev/spikes/mods/drive-probe.js +0 -29
  464. package/core/dev/spikes/mods/e2e-peer.js +0 -71
  465. package/core/dev/spikes/mods/evidence/e2e-peer-control.json +0 -17
  466. package/core/dev/spikes/mods/evidence/e2e-peer-mod.json +0 -17
  467. package/core/dev/spikes/mods/evidence/probe-006a6e65-d807-4bc1-b648-fd7d1edfe0b4.jsonl +0 -4
  468. package/core/dev/spikes/mods/evidence/probe-cebdf1f7-f9c4-47a8-83cd-7c0786177404.jsonl +0 -12
  469. package/core/dev/spikes/mods/evidence/probe-eb21147d-10ce-4c32-93ed-37ca37d88d5a.jsonl +0 -11
  470. package/core/dev/spikes/mods/evidence/stream-cebdf1f7.jsonl +0 -18
  471. package/core/dev/spikes/mods/kenaz-mod-probe/.claude-plugin/plugin.json +0 -1
  472. package/core/dev/spikes/mods/kenaz-mod-probe/hooks/hooks.json +0 -1
  473. package/core/dev/spikes/mods/kenaz-mod-probe/hooks/register.ts +0 -65
  474. package/core/dev/spikes/mods/kenaz-mod-probe/tsconfig.json +0 -3
@@ -19,5 +19,5 @@ if (!hookFile) process.exit(0);
19
19
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
20
20
  if (fs.existsSync(path.join(projectDir, '.claude', 'hooks', hookFile))) process.exit(0);
21
21
 
22
- const res = spawnSync(process.execPath, [path.join(__dirname, hookFile), ...args], { stdio: 'inherit' });
22
+ const res = spawnSync(process.execPath, [path.join(__dirname, hookFile), ...args], { windowsHide: true, stdio: 'inherit' });
23
23
  process.exit(res.status == null ? 0 : res.status);
@@ -221,7 +221,7 @@ if (require.main === module) {
221
221
  if (!result) process.exit(0); // silent -- non-blocking hook
222
222
 
223
223
  try {
224
- execFileSync("node", [result.resolved, ...result.args], { stdio: "inherit" });
224
+ execFileSync("node", [result.resolved, ...result.args], { stdio: "inherit", windowsHide: true });
225
225
  } catch {
226
226
  // Hook errors should not block
227
227
  }
@@ -99,7 +99,7 @@ function readStdin() {
99
99
  * a no-op (GEN-009). We do NOT write to any git config file here.
100
100
  */
101
101
  function git(repo, args) {
102
- return execFileSync('git', ['-C', repo, '-c', 'core.longpaths=true', ...args], {
102
+ return execFileSync('git', ['-C', repo, '-c', 'core.longpaths=true', ...args], { windowsHide: true,
103
103
  stdio: ['ignore', 'pipe', 'pipe'],
104
104
  }).toString().trim();
105
105
  }
@@ -107,7 +107,7 @@ function git(repo, args) {
107
107
  function isGitRepo(dir) {
108
108
  if (!dir) return false;
109
109
  try {
110
- return execFileSync('git', ['-C', dir, 'rev-parse', '--is-inside-work-tree'], {
110
+ return execFileSync('git', ['-C', dir, 'rev-parse', '--is-inside-work-tree'], { windowsHide: true,
111
111
  stdio: ['ignore', 'pipe', 'ignore'],
112
112
  }).toString().trim() === 'true';
113
113
  } catch { return false; }
@@ -334,7 +334,7 @@ function isLandedProgressNote(repo, relPath) {
334
334
  function classifyWorktreeDirt(worktreeDir, noisePaths, repo) {
335
335
  const raw = execFileSync('git', ['-C', worktreeDir, '-c', 'core.longpaths=true',
336
336
  '-c', 'core.quotePath=false', 'status', '--porcelain'],
337
- { stdio: ['ignore', 'pipe', 'pipe'] }).toString();
337
+ { windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }).toString();
338
338
  const real = [];
339
339
  const noise = [];
340
340
  for (const line of raw.split('\n')) {
@@ -491,7 +491,8 @@ function gcStaleWorktrees(repo, keepPath, opts) {
491
491
  // Windows recurses through junctions on delete and would empty the MAIN
492
492
  // checkout's node_modules (measured; see unlinkNodeModulesLinks). This
493
493
  // also covers pre-TASK_1844 worktrees where agents hand-built junctions.
494
- unlinkNodeModulesLinks(p);
494
+ const unl = unlinkNodeModulesLinks(p);
495
+ if (unl.failed.length) { diag(name + ' KEPT — cannot unlink: ' + unl.failed.slice(0, 3).join(', ')); continue; }
495
496
  // Lock-resilient removal: try git remove --force, fall back to fs.rmSync
496
497
  // with retries, then prune. If still locked -> log to stderr (observable)
497
498
  // but never throw (GC must remain non-blocking).
@@ -635,18 +636,18 @@ function findNodeModulesParents(repo) {
635
636
  function ensureWorktreeIgnores(worktreePath, relPath) {
636
637
  try {
637
638
  execFileSync('git', ['-C', worktreePath, 'check-ignore', '-q', '--', relPath],
638
- { stdio: ['ignore', 'ignore', 'ignore'] });
639
+ { windowsHide: true, stdio: ['ignore', 'ignore', 'ignore'] });
639
640
  return; // exit 0 → already ignored (repo .gitignore covers it)
640
641
  } catch { /* not ignored → add per-worktree exclude */ }
641
642
  const gp = execFileSync('git', ['-C', worktreePath, 'rev-parse', '--git-path', 'info/exclude'],
642
- { stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
643
+ { windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }).toString().trim();
643
644
  const excludeFile = path.isAbsolute(gp) ? gp : path.resolve(worktreePath, gp);
644
645
  fs.mkdirSync(path.dirname(excludeFile), { recursive: true });
645
646
  fs.appendFileSync(excludeFile, '/' + relPath + '/\n');
646
647
  }
647
648
 
648
649
  /**
649
- * Unlink node_modules junctions/symlinks inside `root` (a worktree about to be
650
+ * Unlink EVERY junction/symlink (any name, any depth) inside `root` (a worktree about to be
650
651
  * removed). MUST run before any `git worktree remove`: Git for Windows RECURSES
651
652
  * THROUGH junctions when deleting and EMPTIES THE LINK TARGET — measured
652
653
  * 2026-08-13 (TASK_1844 control test): a junction into the source repo's
@@ -657,27 +658,8 @@ function ensureWorktreeIgnores(worktreePath, relPath) {
657
658
  * left for the normal removal path. Best-effort: never throws.
658
659
  */
659
660
  function unlinkNodeModulesLinks(root) {
660
- const walk = (dir, depth) => {
661
- let entries;
662
- try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
663
- for (const ent of entries) {
664
- const p = path.join(dir, ent.name);
665
- if (ent.name === 'node_modules') {
666
- try {
667
- if (fs.lstatSync(p).isSymbolicLink()) {
668
- try { fs.unlinkSync(p); } catch { fs.rmdirSync(p); }
669
- }
670
- } catch { /* best-effort */ }
671
- continue;
672
- }
673
- let isDir = false;
674
- try { const st = fs.lstatSync(p); isDir = st.isDirectory() && !st.isSymbolicLink(); }
675
- catch { continue; }
676
- if (!isDir || NODE_MODULES_SCAN_PRUNE.has(ent.name)) continue;
677
- if (depth + 1 <= NODE_MODULES_SCAN_DEPTH) walk(p, depth + 1);
678
- }
679
- };
680
- walk(root, 0);
661
+ // TASK_2485: ONE implementation (lstat walk, every link, any name/depth) lives in worktree-reclaim.js.
662
+ return require('../../.formation/tools/worktree-reclaim.js').unlinkAllLinks(root); // {removed, failed}
681
663
  }
682
664
 
683
665
  /**
@@ -797,7 +779,7 @@ function main() {
797
779
  const taken = (b) => {
798
780
  try {
799
781
  execFileSync('git', ['-C', repo, 'rev-parse', '--verify', '--quiet', 'refs/heads/' + b],
800
- { stdio: ['ignore', 'pipe', 'ignore'] });
782
+ { windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] });
801
783
  return true;
802
784
  } catch { return false; }
803
785
  };
@@ -25,7 +25,7 @@ function readStdin() {
25
25
 
26
26
  function gitTry(repo, args) {
27
27
  try {
28
- execFileSync('git', ['-C', repo, ...args], { stdio: ['ignore', 'ignore', 'ignore'] });
28
+ execFileSync('git', ['-C', repo, ...args], { windowsHide: true, stdio: ['ignore', 'ignore', 'ignore'] });
29
29
  return true;
30
30
  } catch { return false; }
31
31
  }
@@ -33,7 +33,7 @@ function gitTry(repo, args) {
33
33
  function gitTopLevel(dir) {
34
34
  try {
35
35
  return execFileSync('git', ['-C', dir, 'rev-parse', '--show-toplevel'],
36
- { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
36
+ { windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
37
37
  } catch { return null; }
38
38
  }
39
39
 
@@ -51,47 +51,21 @@ function gitTopLevel(dir) {
51
51
  // falsified by the control test above — keep this note so nobody re-litigates
52
52
  // it in either direction without re-measuring.)
53
53
  //
54
- // DUPLICATED from worktree-create.js ON PURPOSE — do not "deduplicate" via
55
- // require('./worktree-create.js'): this hook is wired as `node -e
56
- // "require(...)"`, so inside a required worktree-create.js
57
- // require.main === undefined, which its entry dispatch treats as "I am the
58
- // WorktreeCreate hook" → it would consume our stdin JSON and CREATE a spurious
59
- // worktree in the middle of a remove.
60
- const NM_SCAN_DEPTH = 3;
61
- const NM_SCAN_PRUNE = new Set([
62
- '.git', 'node_modules', '.next', 'target', 'dist', 'build', 'out',
63
- 'vendor', 'worktrees', '.kenaz-worktrees',
64
- ]);
65
-
54
+ // TASK_2485: the unlink walk is shared (worktree-reclaim.js unlinkAllLinks): every
55
+ // symlink/junction at any depth, because git recurses through ALL of them. Do not
56
+ // require('./worktree-create.js') here: this hook is wired as `node -e "require(...)"`,
57
+ // so inside a required worktree-create.js require.main === undefined, which its entry
58
+ // dispatch treats as "I am the WorktreeCreate hook" and would CREATE a worktree.
66
59
  function unlinkNodeModulesLinks(root) {
67
- const walk = (dir, depth) => {
68
- let entries;
69
- try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
70
- for (const ent of entries) {
71
- const p = path.join(dir, ent.name);
72
- if (ent.name === 'node_modules') {
73
- try {
74
- if (fs.lstatSync(p).isSymbolicLink()) {
75
- try { fs.unlinkSync(p); } catch { fs.rmdirSync(p); }
76
- }
77
- } catch { /* best-effort */ }
78
- continue;
79
- }
80
- let isDir = false;
81
- try { const st = fs.lstatSync(p); isDir = st.isDirectory() && !st.isSymbolicLink(); }
82
- catch { continue; }
83
- if (!isDir || NM_SCAN_PRUNE.has(ent.name)) continue;
84
- if (depth + 1 <= NM_SCAN_DEPTH) walk(p, depth + 1);
85
- }
86
- };
87
- try { walk(root, 0); } catch { /* best-effort */ }
60
+ try { return require('../../.formation/tools/worktree-reclaim.js').unlinkAllLinks(root); }
61
+ catch { return { removed: [], failed: ['(unlink helper unavailable)'] }; }
88
62
  }
89
63
 
90
64
  /** Find the branch checked out in `worktreePath` via `git worktree list --porcelain`. */
91
65
  function worktreeBranch(repo, worktreePath) {
92
66
  try {
93
67
  const out = execFileSync('git', ['-C', repo, 'worktree', 'list', '--porcelain'],
94
- { stdio: ['ignore', 'pipe', 'ignore'] }).toString();
68
+ { windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }).toString();
95
69
  const want = path.resolve(worktreePath).replace(/\\/g, '/').toLowerCase();
96
70
  let curPath = null;
97
71
  for (const line of out.split(/\r?\n/)) {
@@ -132,7 +106,7 @@ function main() {
132
106
  let repo = null;
133
107
  try {
134
108
  const common = execFileSync('git', ['-C', worktreePath, 'rev-parse', '--git-common-dir'],
135
- { stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
109
+ { windowsHide: true, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
136
110
  if (common) {
137
111
  const commonAbs = path.resolve(worktreePath, common); // handles relative or absolute
138
112
  if (path.basename(commonAbs) === '.git') repo = path.dirname(commonAbs);
@@ -164,7 +138,7 @@ function main() {
164
138
  // TASK_1844: MUST unlink node_modules junctions before the git removal —
165
139
  // Git for Windows traverses junctions on delete (measured; see header
166
140
  // comment above unlinkNodeModulesLinks). fs.rmSync below is safe as-is.
167
- unlinkNodeModulesLinks(worktreePath);
141
+ if (unlinkNodeModulesLinks(worktreePath).failed.length) process.exit(0); // refuse: a surviving link would be traversed
168
142
  gitTry(repo, ['worktree', 'remove', '--force', worktreePath]);
169
143
  gitTry(repo, ['worktree', 'prune']);
170
144
  if (branch) gitTry(repo, ['branch', '-D', branch]);
@@ -192,4 +192,19 @@ function externalLanding(repo, card, opts) {
192
192
  return wts.some((w) => w.branch && names.has(String(w.branch).replace(/^refs\/heads\//, ''))) ? null : a;
193
193
  }
194
194
 
195
- module.exports = { reconcile, assessCard, landedAssessment, externalLanding, countLanded };
195
+ /**
196
+ * TASK_2467: validate a hand-supplied landing sha. Returns {sha, baseRef, landed_into}; throws unless <sha>
197
+ * resolves to a commit that is an ancestor of the base ref (origin/main, else local main).
198
+ */
199
+ function verifyLandedSha(repo, sha, { remote = 'origin', base = 'main' } = {}) {
200
+ if (!/^[0-9a-fA-F]{7,64}$/.test(String(sha))) throw new Error(`--landed-sha: '${sha}' is not a commit sha`);
201
+ const baseRef = resolveBaseRef(repo, remote, base);
202
+ if (!baseRef) throw new Error(`--landed-sha: no ${remote}/${base} or ${base} ref to verify against`);
203
+ let full;
204
+ try { full = git(repo, ['rev-parse', '--verify', '--quiet', `${sha}^{commit}`]); } catch { throw new Error(`--landed-sha: commit ${sha} not found`); }
205
+ const into = baseRef.replace(/^refs\/(heads|remotes)\//, '');
206
+ if (!gitOk(repo, ['merge-base', '--is-ancestor', full, baseRef])) throw new Error(`--landed-sha: ${full.slice(0, 10)} is not an ancestor of ${into}`);
207
+ return { landing_sha: full, landed_into: into };
208
+ }
209
+
210
+ module.exports = { verifyLandedSha, reconcile, assessCard, landedAssessment, externalLanding, countLanded };
@@ -0,0 +1,63 @@
1
+ // TASK_2467 -- update --status rejected; complete --landed-sha from any open state; list shows archived.
2
+ // Real CLI in a temp git project. TASK_API=<path> runs the same checks against another task-api.js (control).
3
+ // Usage: node .collaboration/core/task-2467-lifecycle.test.js
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const os = require('os');
7
+ const cp = require('child_process');
8
+ const API = process.env.TASK_API || path.join(__dirname, 'task-api.js');
9
+ const SB = fs.mkdtempSync(path.join(os.tmpdir(), 't2467_'));
10
+ const git = (...a) => cp.execFileSync('git', a, { cwd: SB, encoding: 'utf8', windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] }).trim();
11
+ const cli = (...a) => { const r = cp.spawnSync(process.execPath, [API, ...a, '--project-path', SB], { cwd: SB, encoding: 'utf8', windowsHide: true }); return { code: r.status, out: (r.stdout || '') + (r.stderr || '') }; };
12
+ let pass = 0, fail = 0;
13
+ const check = (n, c, d) => { console.log((c ? 'PASS' : 'FAIL') + ' | ' + n + (c ? '' : ' | ' + d)); c ? pass++ : fail++; };
14
+ const T = (...p) => path.join(SB, '.kenaz', 'tasks', ...p);
15
+ const dirOf = (id) => ['backlog', 'active', 'done', 'blocked', 'archived'].find(d => fs.existsSync(T(d)) && fs.readdirSync(T(d)).some(f => f.startsWith(id + '_')));
16
+ const card = (id, dir) => { fs.mkdirSync(T(dir), { recursive: true }); fs.writeFileSync(T(dir, `${id}_t.yaml`), `id: ${id}\ntitle: t\npriority: P2\nstatus: ${dir === 'active' ? 'in_progress' : 'pending'}\n`); };
17
+
18
+ try {
19
+ git('init', '-q', '-b', 'main'); git('config', 'user.email', 't@e.com'); git('config', 'user.name', 't');
20
+ fs.writeFileSync(path.join(SB, 'a.txt'), '1'); git('add', 'a.txt'); git('commit', '-q', '-m', 'base');
21
+ fs.writeFileSync(path.join(SB, 'b.txt'), '2'); git('add', 'b.txt'); git('commit', '-q', '-m', 'landed');
22
+ const sha = git('rev-parse', 'HEAD');
23
+ git('checkout', '-q', '-b', 'side'); fs.writeFileSync(path.join(SB, 'c.txt'), '3'); git('add', 'c.txt'); git('commit', '-q', '-m', 'off-base');
24
+ const offSha = git('rev-parse', 'HEAD'); git('checkout', '-q', 'main');
25
+ for (const [id, d] of [['TASK_1', 'backlog'], ['TASK_2', 'blocked'], ['TASK_3', 'active'], ['TASK_4', 'backlog'], ['TASK_5', 'backlog']]) card(id, d);
26
+
27
+ // 1) update --status
28
+ let r = cli('update', 'TASK_5', '--status', 'done', '--priority', 'P1');
29
+ check('1 update --status (with other fields) exits non-zero', r.code !== 0, r.out);
30
+ check('1 error names move/complete', /move|complete/.test(r.out), r.out);
31
+ check('1 card unchanged', dirOf('TASK_5') === 'backlog' && !/priority: P1/.test(fs.readFileSync(T('backlog', 'TASK_5_t.yaml'), 'utf8')), r.out);
32
+ r = cli('update', 'TASK_5', '--status', 'done');
33
+ check('1 update --status alone exits non-zero naming move/complete', r.code !== 0 && /move|complete/.test(r.out), r.out);
34
+ r = cli('update', 'TASK_5', '--status=done', '--priority', 'P1');
35
+ check('1 update --status=done form is refused too', r.code !== 0 && !/priority: P1/.test(fs.readFileSync(T('backlog', 'TASK_5_t.yaml'), 'utf8')), r.out);
36
+
37
+ // 2) complete --landed-sha
38
+ r = cli('complete', 'TASK_4', '--landed-sha', offSha);
39
+ check('2 sha not on base refused, card unchanged', r.code !== 0 && dirOf('TASK_4') === 'backlog', r.out);
40
+ r = cli('complete', 'TASK_4', '--landed-sha', 'zzzz');
41
+ check('2 garbage sha refused', r.code !== 0 && dirOf('TASK_4') === 'backlog', r.out);
42
+ card('TASK_6', 'cancel');
43
+ r = cli('complete', 'TASK_6', '--landed-sha', sha);
44
+ check('2 a cancelled card is not closed by --landed-sha', r.code !== 0 && fs.existsSync(T('cancel', 'TASK_6_t.yaml')), r.out);
45
+ for (const id of ['TASK_1', 'TASK_2', 'TASK_3']) {
46
+ r = id === 'TASK_2' ? cli('complete', id, `--landed-sha=${sha}`) : cli('complete', id, '--landed-sha', sha);
47
+ check(`2 ${id} -> done in one command`, r.code === 0 && dirOf(id) === 'done', r.out);
48
+ }
49
+ const y = dirOf('TASK_1') === 'done' ? fs.readFileSync(T('done', 'TASK_1_t.yaml'), 'utf8') : '';
50
+ check('2 records external landing fields', /integration: external/.test(y) && y.includes(sha), y);
51
+
52
+ // 3) list shows archived
53
+ card('TASK_90', 'archived'); card('TASK_91', 'archived');
54
+ r = cli('list');
55
+ const tot = /\((\d+) total\)/.exec(r.out);
56
+ check('3 list summary has archived count; total includes it', /archived: 2 \(hidden; --include-archived\)/.test(r.out) && tot && tot[1] === '8', r.out);
57
+ r = cli('list', '--all');
58
+ check('3 list --all summary has archived count', /archived: 2 \(hidden; --include-archived\)/.test(r.out), r.out);
59
+ r = cli('list', '--include-archived');
60
+ check('3 --include-archived shows archived, no hidden note', /archived: 2/.test(r.out) && !/hidden/.test(r.out), r.out);
61
+ } finally { try { fs.rmSync(SB, { recursive: true, force: true }); } catch (e) { /* best effort */ } }
62
+ console.log(`\n${pass} passed, ${fail} failed`);
63
+ process.exit(fail ? 1 : 0);
@@ -2080,8 +2080,15 @@ class TaskAPI {
2080
2080
  throw await this._missingTaskError(taskId);
2081
2081
  }
2082
2082
 
2083
+ // TASK_2467: card finished outside the dispatch flow (plumbing merge, no card branch): verify the sha
2084
+ // is on the base, then close from backlog/active/blocked in one step (no post-complete).
2085
+ if (options.landedSha) {
2086
+ if (!['backlog', 'active', 'blocked'].includes(task._status_dir)) throw new Error(`--landed-sha closes a backlog/active/blocked card; ${taskId} is in ${task._status_dir}`);
2087
+ options = { ...options, external: require('./reconcile').verifyLandedSha(this._resolveProjectRoot(), options.landedSha), skipPostComplete: true };
2088
+ }
2089
+
2083
2090
  // TASK_2233: backlog → done is not an allowed transition; say what to do instead
2084
- if (task.status === 'backlog' || task.status === 'pending' || task._status_dir === 'backlog') {
2091
+ if (!options.landedSha && (task.status === 'backlog' || task.status === 'pending' || task._status_dir === 'backlog')) {
2085
2092
  throw new Error(`無法從 backlog 直接完成 ${taskId}: move it to active first (node task-api.js move ${taskId} active), then complete.`);
2086
2093
  }
2087
2094
 
@@ -2119,7 +2126,7 @@ class TaskAPI {
2119
2126
 
2120
2127
  const postCompleteRuns = !options.skipPostComplete;
2121
2128
  let postCompleteSummary = null;
2122
- const result = await this.moveTask(taskId, 'done', updatedTask);
2129
+ const result = await this.moveTask(taskId, 'done', updatedTask, options.landedSha ? { skipValidation: true } : {});
2123
2130
 
2124
2131
  try {
2125
2132
  this._autoPopThoughtsForCompletedTask(taskId, now);
@@ -2516,7 +2523,7 @@ Commands:
2516
2523
  預設為整組取代(會回顯被移除的既有項目);加 --append 改為追加到既有項目之後(與既有重複的項目會略過並回顯)
2517
2524
  node task-api.js pull TASK_ID 匯入共享 room 的唯讀 replica 為本機卡片(標記 origin_node;TASK_2421)
2518
2525
  node task-api.js claim TASK_ID
2519
- node task-api.js complete TASK_ID [--actual-hours HOURS]
2526
+ node task-api.js complete TASK_ID [--actual-hours HOURS] [--landed-sha SHA] --landed-sha: work already on main (no card branch); closes from backlog/active/blocked in one step
2520
2527
  node task-api.js reconcile [--remote origin] [--base main] [--apply] [--worktrees] 關閉分支已併入 base 的 active 任務(預設 dry-run)
2521
2528
  node task-api.js move TASK_ID STATUS [--force]
2522
2529
  node task-api.js delete TASK_ID (僅限 backlog 狀態)
@@ -2683,7 +2690,12 @@ Create 選項:
2683
2690
  }
2684
2691
  }
2685
2692
  const totalCount = Object.values(dirCounts).reduce((a, b) => a + b, 0);
2686
- if (totalCount === 0) emitEmptyListDiagnostics(api.tasksDir); // TASK_1778
2693
+ // TASK_2467: archived/ is not scanned without --include-archived; count it instead of hiding it
2694
+ let archivedHidden = 0;
2695
+ if (!includeArchived) {
2696
+ try { archivedHidden = (await fs.readdir(path.join(api.tasksDir, 'archived'))).filter(n => n.startsWith('TASK_')).length; } catch { /* absent */ }
2697
+ }
2698
+ if (totalCount + archivedHidden === 0) emitEmptyListDiagnostics(api.tasksDir); // TASK_1778
2687
2699
 
2688
2700
  // Load tasks: full _scanDir for small dirs, fast path for large dirs
2689
2701
  const grouped = {};
@@ -2747,10 +2759,11 @@ Create 選項:
2747
2759
  'done': 'done', 'cancel': 'cancel', 'archived': 'archived'
2748
2760
  };
2749
2761
 
2750
- const statusSummary = statusOrder
2762
+ let statusSummary = statusOrder
2751
2763
  .map(s => `${statusLabels[s]}: ${dirCounts[s] || 0}`)
2752
2764
  .join(' | ');
2753
- console.log(`任務統計 (${totalCount} total)`);
2765
+ if (!includeArchived) statusSummary += ` | archived: ${archivedHidden} (hidden; --include-archived)`;
2766
+ console.log(`任務統計 (${totalCount + archivedHidden} total)`);
2754
2767
  console.log(` ${statusSummary}`);
2755
2768
 
2756
2769
  const formatTask = (t) => {
@@ -2818,9 +2831,15 @@ Create 選項:
2818
2831
  const dir = CONFIG.STATUS_MAP[oldStatus] || oldStatus;
2819
2832
  byDir[dir] = (byDir[dir] || 0) + count;
2820
2833
  }
2834
+ // TASK_2467: getStatistics folds hidden archived/ files into completed; split them back out
2835
+ let archivedHidden = 0;
2836
+ if (!showArchived) {
2837
+ try { archivedHidden = (await fs.readdir(path.join(api.tasksDir, 'archived'))).filter(n => n.startsWith('TASK_')).length; } catch { /* absent */ }
2838
+ byDir.done = Math.max(0, (byDir.done || 0) - archivedHidden);
2839
+ }
2821
2840
  const statusSummary = statusOrder
2822
2841
  .map(s => `${statusLabels[s]}: ${byDir[s] || 0}`)
2823
- .join(' | ');
2842
+ .join(' | ') + (showArchived ? '' : ` | archived: ${archivedHidden} (hidden; --include-archived)`);
2824
2843
  console.log(`任務統計 (${stats.total} total)`);
2825
2844
  console.log(` ${statusSummary}`);
2826
2845
 
@@ -3076,6 +3095,12 @@ Create 選項:
3076
3095
  if (args.includes('--skip-post-complete')) {
3077
3096
  options.skipPostComplete = true;
3078
3097
  }
3098
+ const landedIdx = args.findIndex(a => a === '--landed-sha' || a.startsWith('--landed-sha='));
3099
+ if (landedIdx >= 0) {
3100
+ const v = args[landedIdx].includes('=') ? args[landedIdx].slice('--landed-sha='.length) : args[landedIdx + 1];
3101
+ if (!v || v.startsWith('--')) { console.error('❌ --landed-sha: requires a commit sha'); process.exit(1); }
3102
+ options.landedSha = v;
3103
+ }
3079
3104
  // TASK_2420: runtime/config tasks with no branch — git merge/verify reported as skipped
3080
3105
  if (args.includes('--no-source')) {
3081
3106
  options.noSource = true;
@@ -3431,6 +3456,11 @@ Create 選項:
3431
3456
  process.exit(1);
3432
3457
  }
3433
3458
  }
3459
+ // TASK_2467: status is not an update field; never accept it silently.
3460
+ if (args.some(a => a === '--status' || a.startsWith('--status='))) {
3461
+ console.error(`❌ update 不接受 --status(不會變更狀態)。請用: move ${taskId} <backlog|active|blocked|cancel|done> 或 complete ${taskId}`);
3462
+ process.exit(1);
3463
+ }
3434
3464
  const title = getArg('--title'); if (title) updates.title = title;
3435
3465
  // Clearable text fields: '' is a legitimate value, so gate on presence (!== null).
3436
3466
  const desc = getArg('--description'); if (desc !== null) updates.description = desc;
@@ -315,7 +315,7 @@ let stopPush = () => {};
315
315
 
316
316
  function killTree(child) {
317
317
  try {
318
- if (process.platform === 'win32' && child.pid) execFileSync('taskkill', ['/PID', String(child.pid), '/T', '/F'], { stdio: 'ignore' }); // own child PID only
318
+ if (process.platform === 'win32' && child.pid) execFileSync('taskkill', ['/PID', String(child.pid), '/T', '/F'], { windowsHide: true, stdio: 'ignore' }); // own child PID only
319
319
  else child.kill('SIGTERM');
320
320
  } catch { /* already gone */ }
321
321
  }
@@ -15,7 +15,12 @@ waiting for a green build. The commit command (built by
15
15
  paths (`.kenaz/sessions`, `.kenaz/specialist`, `.formation/.dispatch-branch`,
16
16
  `.formation/analytics`) — never hand-type `git add -A` to replace it, and
17
17
  never delete the worktree yourself even after committing; the main thread
18
- owns worktree lifecycle.
18
+ owns worktree lifecycle. When the main thread (or an agent told to clean up)
19
+ must remove one, the only safe command is
20
+ `node <core>/.formation/tools/worktree-reclaim.js remove <path>`: dispatch
21
+ worktrees hold junctions into the main repo (node_modules, workspace links),
22
+ and on Windows `git worktree remove --force` follows every one of them and
23
+ empties the target (TASK_2485).
19
24
 
20
25
  ## Why "sending a message ends execution" is a hard rule
21
26
 
@@ -75,7 +75,7 @@ function peerStats({ roots, now, days }) {
75
75
  }
76
76
 
77
77
  function git(cwd, ...args) {
78
- return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 64 << 20 });
78
+ return execFileSync('git', args, { cwd, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], maxBuffer: 64 << 20, windowsHide: true });
79
79
  }
80
80
  /** Commits of the repo (all refs): [{sha, parents, t, subject, firstParent, onHead}] newest first; null when not a git repo. */
81
81
  function readCommits(repo, sinceMs, untilMs) {
@@ -2592,7 +2592,7 @@ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"`, { cwd: worktreePath }
2592
2592
  if (baseRef && fs.existsSync(checkScript)) {
2593
2593
  const gateProc = spawnSync(process.execPath, [
2594
2594
  checkScript, '--base', baseRef, '--head', taskBranch, '--repo', projectRoot, '--json',
2595
- ], { encoding: 'utf8' });
2595
+ ], { windowsHide: true, encoding: 'utf8' });
2596
2596
  let gate = null;
2597
2597
  try { gate = JSON.parse((gateProc.stdout || '').trim() || '{}'); } catch { gate = null; }
2598
2598
  if (gate && Array.isArray(gate.blocks) && gate.blocks.length > 0) {
@@ -187,7 +187,7 @@ function buildSubagentPrompt(skillDef, taskInput, taskData, options = {}) {
187
187
  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.');
188
188
  lines.push('Run the tests the card names (narrow, not the full suite); say what you did not verify.');
189
189
  // The worktree branches off the project's current branch; user projects are not always on `main`.
190
- const sizeBase = (require('child_process').spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: options.projectPath || process.cwd(), encoding: 'utf8' }).stdout || '').trim() || 'main';
190
+ const sizeBase = (require('child_process').spawnSync('git', ['rev-parse', '--abbrev-ref', 'HEAD'], { cwd: options.projectPath || process.cwd(), encoding: 'utf8', windowsHide: true }).stdout || '').trim() || 'main';
191
191
  const sizeCorePath = options.corePath || resolveCorePath(options.projectPath) || path.resolve(__dirname, '..', '..');
192
192
  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.`);
193
193
  const onBlocked = skillDef.collaboration && skillDef.collaboration.on_blocked;
@@ -63,10 +63,9 @@ function linkNodeModulesBestEffort(projectRoot, worktreePath) {
63
63
  * target (the main checkout's node_modules). Never throws.
64
64
  */
65
65
  function unlinkNodeModulesBestEffort(worktreePath) {
66
- const hook = _loadWorktreeCreateHook();
67
- if (!hook || typeof hook.unlinkNodeModulesLinks !== 'function') return;
68
- try { hook.unlinkNodeModulesLinks(worktreePath); }
69
- catch (e) { console.warn(` ⚠️ node_modules unlink failed: ${(e && e.message) || 'unknown'}`); }
66
+ // TASK_2485: unlinks EVERY link (any name/depth); returns {removed, failed}. failed != [] => do not remove.
67
+ try { return require('./worktree-reclaim').unlinkAllLinks(worktreePath); }
68
+ catch (e) { console.warn(` ⚠️ link unlink failed: ${(e && e.message) || 'unknown'}`); return { removed: [], failed: ['(unlink helper unavailable)'] }; }
70
69
  }
71
70
 
72
71
  /**
@@ -848,7 +847,8 @@ function removeWorktree(projectRoot, worktreePath, maxRetries = 3) {
848
847
  // TASK_1844/TASK_2030: MUST run before any `git worktree remove` -- Git for
849
848
  // Windows recurses through a node_modules junction/symlink on removal and
850
849
  // empties the link target (the main checkout's node_modules).
851
- unlinkNodeModulesBestEffort(worktreePath);
850
+ const unl = unlinkNodeModulesBestEffort(worktreePath);
851
+ if (unl.failed.length) { console.warn(` ⚠️ not removing ${worktreePath}: link(s) survive unlink: ${unl.failed.slice(0, 3).join(', ')}`); return false; }
852
852
  for (let i = 0; i < maxRetries; i++) {
853
853
  try {
854
854
  execSync(`git worktree remove --force "${worktreePath}"`, {
@@ -73,6 +73,66 @@ function walkTree(root) {
73
73
  return { size, newest, links, managedLinks };
74
74
  }
75
75
 
76
+ /**
77
+ * TASK_2485: unlink EVERY symlink/junction under `root` (any name, any depth, also inside a real
78
+ * node_modules). lstat walk: a link is removed as a link and never entered. Measured on Windows:
79
+ * `git worktree remove --force` traverses every junction and empties its target (the main repo's
80
+ * node_modules, backend/.env, tracked files); fs.rmSync, Git Bash rm -rf and cmd rmdir /s do not.
81
+ * @returns {{removed:string[], failed:string[]}} paths relative to root
82
+ */
83
+ function unlinkAllLinks(root) {
84
+ const removed = []; const failed = [];
85
+ const stack = [root];
86
+ while (stack.length) {
87
+ const dir = stack.pop();
88
+ let names;
89
+ try { names = fs.readdirSync(dir); } catch { continue; }
90
+ for (const name of names) {
91
+ const p = path.join(dir, name);
92
+ let st;
93
+ try { st = fs.lstatSync(p); } catch { continue; }
94
+ const rel = path.relative(root, p).replace(/\\/g, '/');
95
+ if (st.isSymbolicLink()) {
96
+ try { fs.unlinkSync(p); } catch { try { fs.rmdirSync(p); } catch { failed.push(rel); continue; } }
97
+ removed.push(rel);
98
+ } else if (st.isDirectory()) stack.push(p);
99
+ }
100
+ }
101
+ return { removed, failed };
102
+ }
103
+
104
+ /**
105
+ * The one safe removal: unlink all links, re-scan, then remove (git worktree remove --force when
106
+ * registered, fs.rmSync for orphan dirs). Refuses (ok:false) when a link survives or the path is
107
+ * not a worktree of this repo / under .formation/worktrees or .claude/worktrees.
108
+ * @returns {{ok:boolean, registered:boolean, unlinked:string[], reason?:string}}
109
+ */
110
+ function safeRemoveWorktree(repo, target) {
111
+ const abs = path.resolve(target);
112
+ const { main, worktrees } = listRegisteredWorktrees(repo);
113
+ const registered = worktrees.some((w) => norm(w.path) === norm(abs));
114
+ const inside = (...dir) => { const rel = path.relative(path.join(repo, ...dir), abs); return !!rel && !rel.startsWith('..') && !path.isAbsolute(rel); };
115
+ const res = { ok: false, registered, unlinked: [] };
116
+ if (norm(abs) === norm(main.path)) return { ...res, reason: 'is the main checkout' };
117
+ if (!registered && !inside('.formation', 'worktrees') && !inside('.claude', 'worktrees')) return { ...res, reason: 'not a registered worktree and not under .formation/worktrees or .claude/worktrees' };
118
+ if (!fs.existsSync(abs) && !registered) return { ...res, ok: true, reason: 'already gone' };
119
+ const first = unlinkAllLinks(abs);
120
+ res.unlinked = first.removed;
121
+ const again = unlinkAllLinks(abs);
122
+ if (first.failed.length || again.removed.length || again.failed.length) {
123
+ return { ...res, reason: `cannot unlink: ${[...first.failed, ...again.removed, ...again.failed].slice(0, 5).join(', ')}` };
124
+ }
125
+ const rm = () => fs.rmSync(abs, { recursive: true, force: true, maxRetries: 3 });
126
+ try {
127
+ if (registered) {
128
+ try { git(main.path, ['worktree', 'remove', '--force', abs]); } catch { if (fs.existsSync(abs)) rm(); }
129
+ } else rm();
130
+ } catch (e) { return { ...res, reason: `remove failed: ${String(e.message).split('\n')[0]}` }; }
131
+ try { git(main.path, ['worktree', 'prune']); } catch { /* non-fatal */ }
132
+ const gone = !fs.existsSync(abs);
133
+ return { ...res, ok: gone, reason: gone ? undefined : 'directory still present (locked file?)' };
134
+ }
135
+
76
136
  /** Uncommitted content beyond telemetry. Compares content (CRLF-only changes do not count). */
77
137
  function dirtyContent(wt) {
78
138
  const dirty = [];
@@ -177,7 +237,7 @@ function removeWorktree(repo, w, opts = {}) {
177
237
  const { main } = listRegisteredWorktrees(repo);
178
238
  // Drop dispatch's own node_modules links first: `git worktree remove` would
179
239
  // traverse a junction and empty its target (the main checkout's node_modules).
180
- (opts.unlinkManaged || require('./utils').unlinkNodeModulesBestEffort)(w.path);
240
+ (opts.unlinkManaged || unlinkAllLinks)(w.path);
181
241
  const left = walkTree(w.path);
182
242
  if (left.links.length || left.managedLinks.length) {
183
243
  log(`KEPT ${w.path}: links still present after unlink: ${[...left.links, ...left.managedLinks].slice(0, 3).join(', ')}`);
@@ -256,4 +316,15 @@ function formatReport(evald) {
256
316
  ].join('\n')).join('\n') || '(no worktrees)';
257
317
  }
258
318
 
259
- module.exports = { evaluateWorktrees, ensureWorktreeSpace, reclaimAllEligible, listRegisteredWorktrees, formatReport, DEFAULT_MIN_IDLE_MS };
319
+ // CLI for agents: node worktree-reclaim.js remove <path> (never raw `git worktree remove` / rm -rf)
320
+ if (require.main === module) {
321
+ const target = process.argv[2] === 'remove' ? process.argv[3] : null;
322
+ if (!target) { process.stderr.write('usage: node worktree-reclaim.js remove <worktree-or-orphan-dir>\n'); process.exit(2); }
323
+ let repo;
324
+ try { repo = path.dirname(git(process.cwd(), ['rev-parse', '--path-format=absolute', '--git-common-dir']).trim()); } catch { process.stderr.write('run from inside the repo\n'); process.exit(2); }
325
+ const r = safeRemoveWorktree(repo, target);
326
+ process.stdout.write(`${r.ok ? 'REMOVED' : 'REFUSED'} ${path.resolve(target)}${r.unlinked.length ? ` (unlinked ${r.unlinked.length} link(s): ${r.unlinked.slice(0, 5).join(', ')})` : ''}${r.reason ? ` - ${r.reason}` : ''}\n`);
327
+ process.exit(r.ok ? 0 : 1);
328
+ }
329
+
330
+ module.exports = { unlinkAllLinks, safeRemoveWorktree, evaluateWorktrees, ensureWorktreeSpace, reclaimAllEligible, listRegisteredWorktrees, formatReport, DEFAULT_MIN_IDLE_MS };