kenaz 0.3.22 → 0.3.23

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 (465) 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 +6 -6
  4. package/core/.claude/hooks/worktree-remove.js +4 -4
  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/kenaz-stats.js +1 -1
  10. package/core/.formation/tools/post-complete/git-merge.js +1 -1
  11. package/core/.formation/tools/prompt.js +1 -1
  12. package/core/.kenaz/specialist/ansuz_rules.yaml +7 -51
  13. package/core/.kenaz/specialist/ansuz_session_handoff.schema.json +2 -6
  14. package/core/.rule/general/project-self-containment.yaml +2 -2
  15. package/core/.specialist/Amelia/README.md +1 -1
  16. package/core/.specialist/Amelia/rule.yaml +1 -1
  17. package/core/.specialist/Ansuz/RULES.md +1 -1
  18. package/core/.specialist/Ansuz/concepts/commitment_layer.yaml +1 -1
  19. package/core/.specialist/Ansuz/concepts/evolvable_guardrails.yaml +1 -1
  20. package/core/.specialist/Ansuz/knowledge.yaml +1 -3
  21. package/core/.specialist/tools/__tests__/task-2409-lessons.test.js +10 -0
  22. package/core/.specialist/tools/__tests__/task-2427-harvest.test.js +25 -0
  23. package/core/.specialist/tools/ansuz-boot-fast.js +1 -1
  24. package/core/.specialist/tools/failure-detector.js +4 -4
  25. package/core/.specialist/tools/handoff-doc.js +1 -1
  26. package/core/.specialist/tools/lessons.js +2 -2
  27. package/core/CLAUDE.md +5 -5
  28. package/core/KENAZ_CORE_VERSION +1 -1
  29. package/core/dashboard/scripts/install-kenaz-cli.js +21 -6
  30. package/core/dev/scripts/check-file-size.js +1 -1
  31. package/core/dev/scripts/ci-feedback.js +1 -1
  32. package/core/dev/scripts/gc-agent-worktrees.js +11 -11
  33. package/core/dev/scripts/kenaz-session.js +3 -3
  34. package/core/dev/scripts/peer-identity-e2e.js +3 -3
  35. package/core/dev/scripts/run-rust-tests.js +2 -2
  36. package/core/dev/scripts/skill-routing-replay.js +3 -3
  37. package/core/dev/scripts/task1961/selftest.js +2 -2
  38. package/core/dev/scripts/windows-hide-guard.test.js +97 -0
  39. package/core/plugins/kenaz/guidance.md +1 -0
  40. package/core/plugins/kenaz/hooks/core-resolve.js +1 -1
  41. package/core/plugins/kenaz/hooks/kenaz-hook.js +52 -4
  42. package/core/plugins/kenaz/scripts/kenaz.test.js +72 -5
  43. package/lib/cli.js +9 -5
  44. package/package.json +6 -6
  45. package/vendor/install-kenaz-cli.js +21 -6
  46. package/core/.kenaz/designs/METAPLASTICITY_CONFIDENCE_DESIGN.md +0 -537
  47. package/core/.kenaz/designs/REFLEX_INITIATOR_DESIGN.md +0 -501
  48. package/core/.kenaz/designs/TASK_1385_handoff_mcp_design.md +0 -426
  49. package/core/.kenaz/designs/TASK_932_hippocampus_consolidation.md +0 -103
  50. package/core/.kenaz/designs/TASK_934_efe_signal_engine.md +0 -97
  51. package/core/.kenaz/designs/TASK_935_self_evolution_framework.md +0 -154
  52. package/core/.kenaz/designs/VISION_cognitive_architecture_roadmap.md +0 -159
  53. package/core/.kenaz/designs/metaplasticity_simulation_results.json +0 -696
  54. package/core/.kenaz/main-memory/README.md +0 -230
  55. package/core/.kenaz/main-memory/kenaz/cortex/001_cortex_overview.md +0 -168
  56. package/core/.kenaz/main-memory/kenaz/cortex/001_rag_engine_overview.md +0 -30
  57. package/core/.kenaz/main-memory/kenaz/memory-system/001_three_tier_architecture.md +0 -361
  58. package/core/.kenaz/main-memory/kenaz/pipeline/001_task_execution_flow.md +0 -117
  59. package/core/.kenaz/main-memory/kenaz/pipeline/001_task_granularity.md +0 -154
  60. package/core/.kenaz/main-memory/kenaz/pipeline/002_team_priority.md +0 -51
  61. package/core/.kenaz/main-memory/kenaz/pipeline/003_feature_id_system.md +0 -46
  62. package/core/.kenaz/main-memory/kenaz/rules/001_gen_007_self_containment.md +0 -108
  63. package/core/.kenaz/main-memory/kenaz/rules/001_rule_priority_system.md +0 -121
  64. package/core/.kenaz/main-memory/kenaz/rules/002_agt_006_pre_check.md +0 -0
  65. package/core/.kenaz/main-memory/kenaz/rules/003_ppl_002_task_archival.md +0 -48
  66. package/core/.kenaz/main-memory/kenaz/system/001_dual_track_task_system.md +0 -197
  67. package/core/.kenaz/main-memory/knowledge/patterns/001_json_bom_issue.md +0 -78
  68. package/core/.kenaz/main-memory/knowledge/patterns/001_leader_worker_pattern.md +0 -0
  69. package/core/.kenaz/main-memory/knowledge/patterns/002_scope_path_conflict.md +0 -115
  70. package/core/.kenaz/main-memory/knowledge/patterns/002_task_dependency_resolution.md +0 -223
  71. package/core/.kenaz/main-memory/knowledge/patterns/003_question_protocol.md +0 -242
  72. package/core/.kenaz/main-memory/knowledge/patterns/004_user_insight_value.md +0 -111
  73. package/core/.kenaz/main-memory/knowledge/patterns/005_file_lock_pattern.md +0 -274
  74. package/core/.kenaz/main-memory/knowledge/references/001_ppl_rules.md +0 -28
  75. package/core/.kenaz/main-memory/knowledge/solutions/001_json_bom_issue.md +0 -137
  76. package/core/.kenaz/main-memory/knowledge/solutions/001_team_invocation.md +0 -0
  77. package/core/.kenaz/main-memory/knowledge/solutions/002_passive_monitoring.md +0 -0
  78. package/core/.kenaz/main-memory/knowledge/solutions/003_windows_file_race_condition.md +0 -239
  79. package/core/.kenaz/main-memory/registry.json +0 -326
  80. package/core/.kenaz/main-memory/version.yaml +0 -58
  81. package/core/.kenaz/specialist/memory_guard_fired.json +0 -1
  82. package/core/.kenaz/specialist/memory_guard_trigger_cache.json +0 -1
  83. package/core/.kenaz/specialist/memory_source_priority.yaml +0 -260
  84. package/core/.kenaz/specialist/prompt_1775.txt +0 -317
  85. package/core/.kenaz/specialist/prompt_1776.txt +0 -326
  86. package/core/.kenaz/specialist/prompt_1791.txt +0 -320
  87. package/core/.kenaz/specialist/prompt_1792.txt +0 -320
  88. package/core/.kenaz/specialist/prompt_1795.txt +0 -281
  89. package/core/.kenaz/specialist/prompt_1813.txt +0 -332
  90. package/core/.kenaz/specialist/prompt_1814.txt +0 -401
  91. package/core/.kenaz/specialist/prompt_1815.txt +0 -394
  92. package/core/.kenaz/specialist/prompt_1816.txt +0 -437
  93. package/core/.kenaz/specialist/prompt_1818.txt +0 -361
  94. package/core/.kenaz/specialist/prompt_1819.txt +0 -421
  95. package/core/.kenaz/specialist/prompt_1821.txt +0 -345
  96. package/core/.kenaz/specialist/prompt_1822.txt +0 -313
  97. package/core/.kenaz/specialist/prompt_1824.txt +0 -348
  98. package/core/.kenaz/specialist/prompt_1826.txt +0 -388
  99. package/core/.kenaz/specialist/rune_efe_salience_gap.md +0 -21
  100. package/core/.kenaz/specialist/task1825_update.json +0 -43
  101. package/core/.kenaz/specialist/taskgen/consolidation_i.json +0 -38
  102. package/core/.kenaz/specialist/taskgen/csharp_a.json +0 -44
  103. package/core/.kenaz/specialist/taskgen/csharp_b.json +0 -40
  104. package/core/.kenaz/specialist/taskgen/embedding_e.json +0 -34
  105. package/core/.kenaz/specialist/taskgen/hygiene_fg.json +0 -39
  106. package/core/.kenaz/specialist/taskgen/pathology_h.json +0 -36
  107. package/core/.kenaz/specialist/taskgen/unity_noise_d.json +0 -38
  108. package/core/.kenaz/specialist/taskgen/visibility_c.json +0 -38
  109. package/core/.specialist/Ansuz/decisions/ADR-requirement-graph-layer.md +0 -390
  110. package/core/.specialist/Ansuz/self_model_seed.backup.json +0 -100
  111. package/core/.specialist/ansuz_session_handoff.json +0 -96
  112. package/core/dev/ALAYA_FIELD_FEEDBACK_DESIGN.md +0 -126
  113. package/core/dev/ANSUZ_SCAFFOLD_DIET_2026-09-20.md +0 -231
  114. package/core/dev/ANSUZ_STARTUP_INJECTION_REVIEW.md +0 -355
  115. package/core/dev/ANSUZ_WORKFLOW_MODERNIZATION.md +0 -143
  116. package/core/dev/CORTEX_META_GRAPH_ASSESSMENT.md +0 -99
  117. package/core/dev/FREYA_DOWNSTREAM_BUGS_2026-09-19.md +0 -44
  118. package/core/dev/GLOBAL_MEMORY_IDENTITY_CONSOLIDATION.md +0 -263
  119. package/core/dev/GUARD_EFFECTIVENESS_DESIGN.md +0 -180
  120. package/core/dev/HEALTH_GATE_VARIANCE_ASSESSMENT.md +0 -271
  121. package/core/dev/HOOK_DIET_2026-09-21.md +0 -126
  122. package/core/dev/JEV_INTEGRATION_PLAN.md +0 -189
  123. package/core/dev/KENAZ_AGENT_CONNECT_v1_SPEC.md +0 -139
  124. package/core/dev/MOBILE_DESIGN_SPEC.md +0 -569
  125. package/core/dev/MULTI_ANSUZ_SPEC.md +0 -136
  126. package/core/dev/MUNINN_RETIREMENT.md +0 -115
  127. package/core/dev/PHASE3_ATTRIBUTION_POPULATION_DIAGNOSIS.md +0 -211
  128. package/core/dev/PHASE3_P3-2B_TASKLEVEL_PROBE.md +0 -83
  129. package/core/dev/PHASE3_P3-2_POPULATION_DISJOINTNESS.md +0 -83
  130. package/core/dev/PHASE3_P3-2_REMEASURE_REPORT.md +0 -231
  131. package/core/dev/PREDICTION_GROUNDING_DESIGN.md +0 -376
  132. package/core/dev/PRODUCT_SPEC.md +0 -317
  133. package/core/dev/PROJECT_REGISTRY_DUPLICATE_ASSESSMENT.md +0 -86
  134. package/core/dev/STARTUP_TIMELINE_TASK_1995.md +0 -181
  135. package/core/dev/SYNAPSE_WORKER_PER_PROJECT.md +0 -881
  136. package/core/dev/TASK_1680_P3-3_DISPATCH_SPEC.md +0 -96
  137. package/core/dev/TASK_1684_CALIBRATION_IMPACT_ANALYSIS.md +0 -170
  138. package/core/dev/TASK_1684_FORK_A_GOODHART_DESIGN.md +0 -171
  139. package/core/dev/TASK_1684_P3_2_REMEASURE_VALIDATION.md +0 -262
  140. package/core/dev/TASK_1903_SIGNAL_AUC_MEASUREMENT.md +0 -207
  141. package/core/dev/TASK_1904_BRANCH_INTEGRITY_AUDIT.md +0 -146
  142. package/core/dev/ansuz-eval/README.md +0 -38
  143. package/core/dev/ansuz-eval/build-fixtures.js +0 -87
  144. package/core/dev/ansuz-eval/grade.js +0 -96
  145. package/core/dev/ansuz-eval/grader.test.js +0 -117
  146. package/core/dev/ansuz-eval/tasks.md +0 -32
  147. package/core/dev/archive/ansuz/BOOT_FULL.md +0 -141
  148. package/core/dev/archive/ansuz/agents/ansuz-boot.md +0 -176
  149. package/core/dev/archive/ansuz/designs/alaya-passive-surfacing.md +0 -84
  150. package/core/dev/archive/ansuz/designs/cc_memory_indexing.md +0 -307
  151. package/core/dev/archive/ansuz/designs/check_guard.md +0 -322
  152. package/core/dev/archive/ansuz/designs/consequence_loop.md +0 -227
  153. package/core/dev/archive/ansuz/designs/mcp_kuzudb_lock_architecture.md +0 -312
  154. package/core/dev/archive/ansuz/designs/phase0_git_enrichment.md +0 -278
  155. package/core/dev/archive/ansuz/designs/refactor_mcp_tools_synapse.md +0 -490
  156. package/core/dev/archive/ansuz/designs/semantic_markdown_extraction.md +0 -167
  157. package/core/dev/archive/ansuz/designs/task_1372_prompt_injects_into.md +0 -193
  158. package/core/dev/archive/ansuz/designs/thought_stack.md +0 -286
  159. package/core/dev/archive/ansuz/investigations/reachability_unified_api_2026-04-11.md +0 -330
  160. package/core/dev/archive/ansuz/investigations/synapse_orphans_indexing_bug_2026-04-11.md +0 -352
  161. package/core/dev/archive/ansuz/investigations/synapse_orphans_reindex_verification_2026-04-11.md +0 -48
  162. package/core/dev/archive/ansuz/investigations/synapse_orphans_verification_2026-04-11.md +0 -170
  163. package/core/dev/archive/ansuz/legacy/kernel.yaml +0 -399
  164. package/core/dev/archive/ansuz/legacy/phases/phase_0.yaml +0 -418
  165. package/core/dev/archive/ansuz/legacy/phases/phase_1.yaml +0 -376
  166. package/core/dev/archive/ansuz/legacy/phases/phase_10.yaml +0 -35
  167. package/core/dev/archive/ansuz/legacy/phases/phase_11.yaml +0 -582
  168. package/core/dev/archive/ansuz/legacy/phases/phase_2.yaml +0 -94
  169. package/core/dev/archive/ansuz/legacy/phases/phase_3.yaml +0 -274
  170. package/core/dev/archive/ansuz/legacy/phases/phase_4.yaml +0 -67
  171. package/core/dev/archive/ansuz/legacy/phases/phase_5.yaml +0 -32
  172. package/core/dev/archive/ansuz/legacy/phases/phase_6.yaml +0 -91
  173. package/core/dev/archive/ansuz/legacy/phases/phase_7.yaml +0 -58
  174. package/core/dev/archive/ansuz/legacy/phases/phase_8.yaml +0 -146
  175. package/core/dev/archive/ansuz/legacy/phases/phase_9.yaml +0 -424
  176. package/core/dev/archive/ansuz/specs/INDEX_bifrost_hook_parity.yaml +0 -92
  177. package/core/dev/archive/ansuz/specs/active-inference-formation.yaml +0 -453
  178. package/core/dev/archive/ansuz/specs/brief_TASK_1228_userpromptsubmit.yaml +0 -191
  179. package/core/dev/archive/ansuz/specs/brief_TASK_1229_pretooluse_posttooluse.yaml +0 -250
  180. package/core/dev/archive/ansuz/specs/brief_TASK_1230_sessionstart_stop.yaml +0 -301
  181. package/core/dev/archive/ansuz/specs/brief_TASK_1231_verification.yaml +0 -272
  182. package/core/dev/archive/skills/feature_agent/SKILL.md +0 -289
  183. package/core/dev/design/PHASE4_DESIGN.md +0 -559
  184. package/core/dev/design/dashboard_docs_integration_PLAN.md +0 -388
  185. package/core/dev/design/matrix_format_ABBREVIATIONS.md +0 -655
  186. package/core/dev/design/matrix_format_DESIGN_SPEC.md +0 -601
  187. package/core/dev/design/matrix_format_DISCUSSION_SUMMARY.md +0 -484
  188. package/core/dev/design/matrix_format_EXAMPLE_memory.matrix +0 -13
  189. package/core/dev/design/matrix_format_EXAMPLE_project_status.matrix +0 -9
  190. package/core/dev/design/matrix_format_EXAMPLE_tasks.matrix +0 -14
  191. package/core/dev/design/matrix_format_SYNTAX_BNF.md +0 -754
  192. package/core/dev/design/matrix_format_TOKEN_COMPARISON.md +0 -484
  193. package/core/dev/design/matrix_v2_COGNITIVE_DESIGN.md +0 -503
  194. package/core/dev/design/matrix_v2_DISCUSSION_SUMMARY.md +0 -414
  195. package/core/dev/design/matrix_v2_EXAMPLE.matrix +0 -67
  196. package/core/dev/designs/EXPANDABLE_CARD_DESIGN_SPEC.md +0 -509
  197. package/core/dev/jev-eval/ACTIVE_CORRECTION.md +0 -52
  198. package/core/dev/jev-eval/ANNOTATION_GUIDE.md +0 -68
  199. package/core/dev/jev-eval/BASELINE_COMPARISON.md +0 -45
  200. package/core/dev/jev-eval/CONTEXT_REVIEW.md +0 -31
  201. package/core/dev/jev-eval/EVALUATION_STATUS.md +0 -39
  202. package/core/dev/jev-eval/HISTORICAL_CORPUS.md +0 -33
  203. package/core/dev/jev-eval/PACKAGING_VERIFICATION.md +0 -42
  204. package/core/dev/jev-eval/README.md +0 -101
  205. package/core/dev/jev-eval/THALAMUS_RANKING.md +0 -58
  206. package/core/dev/jev-eval/VALIDATION_PROGRESS.md +0 -54
  207. package/core/dev/jev-eval/cli.mjs +0 -50
  208. package/core/dev/jev-eval/context.mjs +0 -183
  209. package/core/dev/jev-eval/corpus.mjs +0 -186
  210. package/core/dev/jev-eval/fixtures/conversation-review.seed.jsonl +0 -6
  211. package/core/dev/jev-eval/fixtures/synthetic.jsonl +0 -11
  212. package/core/dev/jev-eval/metrics.mjs +0 -67
  213. package/core/dev/jev-eval/muninn-baseline.mjs +0 -119
  214. package/core/dev/jev-eval/providers.mjs +0 -95
  215. package/core/dev/jev-eval/reports/annotation-pilot-2026-09-20.json +0 -32
  216. package/core/dev/jev-eval/reports/context-review-2026-09-20.json +0 -48
  217. package/core/dev/jev-eval/reports/conversation-review.pending.json +0 -151
  218. package/core/dev/jev-eval/reports/history-corpus-2026-09-20.json +0 -52
  219. package/core/dev/jev-eval/reports/length-probe-2026-09-20.json +0 -133
  220. package/core/dev/jev-eval/reports/live-smoke-2026-09-20.json +0 -616
  221. package/core/dev/jev-eval/reports/muninn-baseline-2026-09-20.json +0 -52
  222. package/core/dev/jev-eval/reports/resilience-2026-09-20.json +0 -520
  223. package/core/dev/jev-eval/reports/rules-baseline.json +0 -197
  224. package/core/dev/jev-eval/reports/synthetic-review.pending.json +0 -357
  225. package/core/dev/jev-eval/resilience.mjs +0 -71
  226. package/core/dev/jev-eval/review.mjs +0 -78
  227. package/core/dev/jev-eval/schema.mjs +0 -87
  228. package/core/dev/jev-eval/test/context.test.mjs +0 -95
  229. package/core/dev/jev-eval/test/corpus.test.mjs +0 -102
  230. package/core/dev/jev-eval/test/eval.test.mjs +0 -146
  231. package/core/dev/jev-eval/test/muninn-baseline.test.mjs +0 -71
  232. package/core/dev/jev-eval/test/resilience.test.mjs +0 -72
  233. package/core/dev/jev-eval/test/review.test.mjs +0 -61
  234. package/core/dev/pipeline/PHASE3_PLANNING_SUMMARY.md +0 -338
  235. package/core/dev/pipeline/PROGRESS.md +0 -113
  236. package/core/dev/pipeline/PROGRESS_template.md +0 -860
  237. package/core/dev/pipeline/QA_REPORT_F008.md +0 -431
  238. package/core/dev/pipeline/QA_REPORT_LOCAL_CLAUDE_DIAGNOSIS.md +0 -583
  239. package/core/dev/pipeline/README.md +0 -210
  240. package/core/dev/pipeline/WORKFLOW_GUIDE.md +0 -824
  241. package/core/dev/pipeline/archived/TASK_015_tauri_init.yaml +0 -53
  242. package/core/dev/pipeline/archived/TASK_016_tauri_commands.yaml +0 -54
  243. package/core/dev/pipeline/archived/TASK_017_rust_pty.yaml +0 -54
  244. package/core/dev/pipeline/archived/TASK_018_tauri_bundle.yaml +0 -53
  245. package/core/dev/pipeline/archived/TASK_019_release_workflow.yaml +0 -56
  246. package/core/dev/pipeline/archived/TASK_020_update_ui.yaml +0 -54
  247. package/core/dev/pipeline/archived/TASK_021_update_notification_ui.yaml +0 -55
  248. package/core/dev/pipeline/archived/TASK_022_remove_node_pty.yaml +0 -65
  249. package/core/dev/pipeline/archived/TASK_023_api_routes_migration.yaml +0 -86
  250. package/core/dev/pipeline/archived/TASK_024_feature_id_system.yaml +0 -51
  251. package/core/dev/pipeline/archived/TASK_025_executable_acceptance.yaml +0 -66
  252. package/core/dev/pipeline/archived/TASK_026_spec_versioning.yaml +0 -56
  253. package/core/dev/pipeline/archived/TASK_027_drift_detection.yaml +0 -60
  254. package/core/dev/pipeline/archived/TASK_028_tauri_pty_manager.yaml +0 -60
  255. package/core/dev/pipeline/archived/TASK_029_tauri_pty_commands.yaml +0 -62
  256. package/core/dev/pipeline/archived/TASK_030_frontend_session_adapter.yaml +0 -54
  257. package/core/dev/pipeline/archived/TASK_031_terminal_tauri_events.yaml +0 -56
  258. package/core/dev/pipeline/archived/TASK_041_scheduler_data_models.yaml +0 -54
  259. package/core/dev/pipeline/archived/TASK_042_path_lock_manager.yaml +0 -53
  260. package/core/dev/pipeline/archived/TASK_043_process_manager.yaml +0 -57
  261. package/core/dev/pipeline/archived/TASK_044_git_integration.yaml +0 -85
  262. package/core/dev/pipeline/archived/TASK_045_task_queue_manager.yaml +0 -54
  263. package/core/dev/pipeline/archived/TASK_046_scheduler_dispatcher.yaml +0 -60
  264. package/core/dev/pipeline/archived/TASK_047_tauri_scheduler_commands.yaml +0 -55
  265. package/core/dev/pipeline/archived/TASK_048_realtime_status_push.yaml +0 -92
  266. package/core/dev/pipeline/archived/TASK_049_frontend_scheduler_integration.yaml +0 -58
  267. package/core/dev/pipeline/archived/TASK_050_error_recovery.yaml +0 -56
  268. package/core/dev/pipeline/archived/TASK_051_optimization_proposals_backend.yaml +0 -53
  269. package/core/dev/pipeline/archived/TASK_052_optimization_proposals_ui.yaml +0 -58
  270. package/core/dev/pipeline/archived/TASK_053_optimization_proposals_diff_view.yaml +0 -54
  271. package/core/dev/pipeline/archived/TASK_054_multi_project_scheduler_state.yaml +0 -76
  272. package/core/dev/pipeline/archived/TASK_055_scheduler_api_project_scope.yaml +0 -64
  273. package/core/dev/pipeline/archived/TASK_056_frontend_scheduler_project_scope.yaml +0 -62
  274. package/core/dev/pipeline/archived/TASK_057_scheduler_module_structure.yaml +0 -54
  275. package/core/dev/pipeline/archived/TASK_058_scheduler_models_extract.yaml +0 -51
  276. package/core/dev/pipeline/archived/TASK_059_scheduler_core_extract.yaml +0 -51
  277. package/core/dev/pipeline/archived/TASK_060_scheduler_commands_extract.yaml +0 -51
  278. package/core/dev/pipeline/archived/TASK_061_scheduler_specialized_commands.yaml +0 -53
  279. package/core/dev/pipeline/archived/TASK_062_process_manager_module_structure.yaml +0 -56
  280. package/core/dev/pipeline/archived/TASK_063_process_manager_core_extract.yaml +0 -56
  281. package/core/dev/pipeline/archived/TASK_064_recovery_module_structure.yaml +0 -57
  282. package/core/dev/pipeline/archived/TASK_065_recovery_core_extract.yaml +0 -56
  283. package/core/dev/pipeline/archived/TASK_066_self_enhancement_scheduler_models.yaml +0 -60
  284. package/core/dev/pipeline/archived/TASK_067_agents_section_quick_actions.yaml +0 -53
  285. package/core/dev/pipeline/archived/TASK_067_self_enhancement_scheduler_core.yaml +0 -65
  286. package/core/dev/pipeline/archived/TASK_068_agent_workflow_rule_enhancement.yaml +0 -46
  287. package/core/dev/pipeline/archived/TASK_068_self_enhancement_tauri_commands.yaml +0 -61
  288. package/core/dev/pipeline/archived/TASK_069_agent_wizard_responsive.yaml +0 -46
  289. package/core/dev/pipeline/archived/TASK_069_self_enhancement_ui.yaml +0 -62
  290. package/core/dev/pipeline/archived/TASK_070_preview_panel_redesign.yaml +0 -62
  291. package/core/dev/pipeline/archived/TASK_070_self_enhancement_integration.yaml +0 -57
  292. package/core/dev/pipeline/archived/TASK_071_preview_panel_qa.yaml +0 -98
  293. package/core/dev/pipeline/archived/TASK_071_single_agent_config.yaml +0 -57
  294. package/core/dev/pipeline/archived/TASK_072_preview_modal_esc_fix.yaml +0 -71
  295. package/core/dev/pipeline/archived/TASK_072_product_review_module.yaml +0 -57
  296. package/core/dev/pipeline/archived/TASK_073_agents_page_component_refactor.yaml +0 -68
  297. package/core/dev/pipeline/archived/TASK_073_rename_single_agent.yaml +0 -64
  298. package/core/dev/pipeline/archived/TASK_074_preview_mode_ui_diagnosis.yaml +0 -71
  299. package/core/dev/pipeline/archived/TASK_074_single_agent_ui_toggle.yaml +0 -56
  300. package/core/dev/pipeline/archived/TASK_075_agents_page_component_refactor.yaml +0 -69
  301. package/core/dev/pipeline/archived/TASK_075_single_agent_integration_test.yaml +0 -75
  302. package/core/dev/pipeline/archived/TASK_076_preview_mode_ui_diagnosis.yaml +0 -70
  303. package/core/dev/pipeline/archived/TASK_076_single_agent_continuous_mode.yaml +0 -65
  304. package/core/dev/pipeline/archived/TASK_077_failure_directory_structure.yaml +0 -51
  305. package/core/dev/pipeline/archived/TASK_077_single_agent_typescript_types.yaml +0 -59
  306. package/core/dev/pipeline/archived/TASK_078_fail_001_agent_no_output.yaml +0 -53
  307. package/core/dev/pipeline/archived/TASK_078_single_agent_ui_simplify.yaml +0 -74
  308. package/core/dev/pipeline/archived/TASK_079_dashboard_rwd_responsive.yaml +0 -63
  309. package/core/dev/pipeline/archived/TASK_079_fail_002_json_bom.yaml +0 -56
  310. package/core/dev/pipeline/archived/TASK_080_fail_003_queue_pollution.yaml +0 -56
  311. package/core/dev/pipeline/archived/TASK_080_http_server_module.yaml +0 -55
  312. package/core/dev/pipeline/archived/TASK_081_failure_logging_tools.yaml +0 -57
  313. package/core/dev/pipeline/archived/TASK_081_http_api_routes.yaml +0 -62
  314. package/core/dev/pipeline/archived/TASK_082_frontend_api_adapter.yaml +0 -51
  315. package/core/dev/pipeline/archived/TASK_082_integrate_failure_to_workflow.yaml +0 -60
  316. package/core/dev/pipeline/archived/TASK_083_cleanup_nextjs_api.yaml +0 -48
  317. package/core/dev/pipeline/archived/TASK_083_task_queue_cleanup.yaml +0 -67
  318. package/core/dev/pipeline/archived/TASK_084_rust_dead_code_cleanup.yaml +0 -66
  319. package/core/dev/pipeline/archived/TASK_084_scheduler_sync_logic_fix.yaml +0 -68
  320. package/core/dev/pipeline/archived/TASK_085_http_api_web_mode_fixes.yaml +0 -46
  321. package/core/dev/pipeline/archived/TASK_085_memory_consolidation_0204_0205.yaml +0 -98
  322. package/core/dev/pipeline/archived/TASK_086_collapsible_preview_panel.yaml +0 -161
  323. package/core/dev/pipeline/archived/TASK_086_web_mode_feature_parity.yaml +0 -62
  324. package/core/dev/pipeline/archived/TASK_087_manual_memory_archival.yaml +0 -163
  325. package/core/dev/pipeline/archived/TASK_087_mobile_http_control.yaml +0 -68
  326. package/core/dev/pipeline/archived/TASK_088_fail_003_to_ppl_002_rule.yaml +0 -63
  327. package/core/dev/pipeline/archived/TASK_088_mobile_http_qa.yaml +0 -121
  328. package/core/dev/pipeline/archived/TASK_089_agent_collaboration_topic.yaml +0 -60
  329. package/core/dev/pipeline/archived/TASK_089_web_mode_monitor_fix.yaml +0 -58
  330. package/core/dev/pipeline/archived/TASK_090_cli_detection_fix.yaml +0 -61
  331. package/core/dev/pipeline/archived/TASK_090_ui_ux_lessons_topic.yaml +0 -63
  332. package/core/dev/pipeline/archived/TASK_091_expandable_card_design.yaml +0 -56
  333. package/core/dev/pipeline/archived/TASK_091_move_agent_section_components.yaml +0 -65
  334. package/core/dev/pipeline/archived/TASK_092_expandable_card_component.yaml +0 -60
  335. package/core/dev/pipeline/archived/TASK_092_refactor_agent_create_wizard.yaml +0 -84
  336. package/core/dev/pipeline/archived/TASK_093_taskcard_detail_integration.yaml +0 -57
  337. package/core/dev/pipeline/archived/TASK_093_update_barrel_exports_test.yaml +0 -84
  338. package/core/dev/pipeline/archived/TASK_094_ai_agent_generation_enhancement.yaml +0 -115
  339. package/core/dev/pipeline/archived/TASK_094_proposalcard_detail_integration.yaml +0 -55
  340. package/core/dev/pipeline/archived/TASK_095_preview_fixes.yaml +0 -129
  341. package/core/dev/pipeline/archived/TASK_095_single_agent_task_selector.yaml +0 -60
  342. package/core/dev/pipeline/archived/TASK_096_agents_page_ux_improvements.yaml +0 -163
  343. package/core/dev/pipeline/archived/TASK_096_single_agent_status_sync.yaml +0 -60
  344. package/core/dev/pipeline/archived/TASK_097_local_claude_error_logging.yaml +0 -59
  345. package/core/dev/pipeline/archived/TASK_097_single_agent_monitor_events.yaml +0 -59
  346. package/core/dev/pipeline/archived/TASK_098_product_manager_team_phase2.yaml +0 -60
  347. package/core/dev/pipeline/archived/TASK_098_single_agent_refactor_test.yaml +0 -58
  348. package/core/dev/pipeline/archived/TASK_099_backend_team_creation.yaml +0 -258
  349. package/core/dev/pipeline/archived/TASK_100_frontend_team_creation.yaml +0 -61
  350. package/core/dev/pipeline/archived/TASK_101_orchestrator_system.yaml +0 -403
  351. package/core/dev/pipeline/archived/TASK_102_designer_sre_teams.yaml +0 -90
  352. package/core/dev/pipeline/archived/TASK_103_integration_testing.yaml +0 -86
  353. package/core/dev/pipeline/archived/TASK_128_integration_testing.yaml +0 -83
  354. package/core/dev/pipeline/archived/TASK_134_cortex_v2_phase3_file_watcher.yaml +0 -65
  355. package/core/dev/pipeline/archived/TASK_134_team_http_api.yaml +0 -96
  356. package/core/dev/pipeline/archived/TASK_135_cortex_v2_phase3_git_monitor.yaml +0 -63
  357. package/core/dev/pipeline/archived/TASK_136_cortex_v2_phase3_change_analyzer.yaml +0 -69
  358. package/core/dev/pipeline/archived/TASK_137_cortex_v2_phase3_cache_invalidator.yaml +0 -67
  359. package/core/dev/pipeline/archived/TASK_137_team_orchestrator_integration.yaml +0 -93
  360. package/core/dev/pipeline/archived/TASK_138_cortex_v2_phase3_distributed_cache.yaml +0 -68
  361. package/core/dev/pipeline/archived/TASK_138_matrix_folder_structure.yaml +0 -61
  362. package/core/dev/pipeline/archived/TASK_139_cortex_v2_phase3_cache_prewarming.yaml +0 -65
  363. package/core/dev/pipeline/archived/TASK_146_unified_documentation.yaml +0 -141
  364. package/core/dev/pipeline/archived/TASK_147_pipeline_execution_rules.yaml +0 -76
  365. package/core/dev/pipeline/archived/TASK_148_update_claude_md.yaml +0 -77
  366. package/core/dev/pipeline/archived/TASK_149_fix_nextjs_mdx_serialization.yaml +0 -63
  367. package/core/dev/pipeline/archived/TASK_149_update_team_docs.yaml +0 -77
  368. package/core/dev/pipeline/archived/TASK_150_pipeline_workflow_guide.yaml +0 -82
  369. package/core/dev/pipeline/archived/TASK_164_fix_smart_context_switch.yaml +0 -49
  370. package/core/dev/pipeline/archived/TASK_165_ctags_symbol_index.yaml +0 -53
  371. package/core/dev/pipeline/archived/TASK_166_symbol_search_mcp_tool.yaml +0 -48
  372. package/core/dev/pipeline/archived/TASK_167_llm_module_summary.yaml +0 -55
  373. package/core/dev/pipeline/archived/TASK_168_map_reduce_context.yaml +0 -51
  374. package/core/dev/pipeline/archived/TASK_169_v2_integration_testing.yaml +0 -52
  375. package/core/dev/pipeline/backups/archive_2026-02-04T22-07-22/TASK_QUEUE_backup.json +0 -95
  376. package/core/dev/pipeline/docs/multi-agent-coordination.md +0 -446
  377. package/core/dev/pipeline/docs/overview.md +0 -406
  378. package/core/dev/pipeline/docs/task-management.md +0 -412
  379. package/core/dev/pipeline/docs/workflows.md +0 -32
  380. package/core/dev/pipeline/orchestrator/schemas/task_queue_schema.json +0 -58
  381. package/core/dev/pipeline/orchestrator/schemas/task_schema.json +0 -139
  382. package/core/dev/pipeline/plans/PLAN_task_cli_refactor.md +0 -281
  383. package/core/dev/pipeline/test_results/TASK_TEST_PARALLEL_002_result.txt +0 -27
  384. package/core/dev/plans/ANSUZ_SLIM_PLAN.md +0 -68
  385. package/core/dev/plans/CLI_FIRST_INSTALL_FLOW.md +0 -120
  386. package/core/dev/plans/FORMATION_RETIREMENT_PLAN.md +0 -313
  387. package/core/dev/plans/KUZU_REPLACEMENT_PLAN.md +0 -126
  388. package/core/dev/scripts/CRASH_DIAGNOSIS.md +0 -47
  389. package/core/dev/scripts/README.md +0 -142
  390. package/core/dev/scripts/RUN_ONCE.bat +0 -40
  391. package/core/dev/scripts/START_DEV_AGENT.bat +0 -63
  392. package/core/dev/scripts/START_MULTI_AGENT.bat +0 -80
  393. package/core/dev/scripts/dev_agent_scheduler.ps1 +0 -536
  394. package/core/dev/scripts/init_git.ps1 +0 -396
  395. package/core/dev/scripts/init_git.sh +0 -337
  396. package/core/dev/scripts/init_project.ps1 +0 -851
  397. package/core/dev/scripts/init_project.sh +0 -477
  398. package/core/dev/scripts/multi_agent_scheduler.ps1 +0 -2021
  399. package/core/dev/scripts/multi_agent_scheduler.sh +0 -1344
  400. package/core/dev/scripts/run_once.sh +0 -47
  401. package/core/dev/scripts/setup_rag.ps1 +0 -304
  402. package/core/dev/scripts/setup_rag.sh +0 -230
  403. package/core/dev/scripts/start_dev_agent.sh +0 -137
  404. package/core/dev/scripts/start_multi_agent.sh +0 -41
  405. package/core/dev/scripts/sync_task_queue.ps1 +0 -505
  406. package/core/dev/scripts/sync_task_queue.sh +0 -392
  407. package/core/dev/specs/agent-connect/PROTOCOL.md +0 -177
  408. package/core/dev/specs/agent-connect/SDK_GUIDE.md +0 -915
  409. package/core/dev/specs/agent-connect/examples/minimal-site/README.md +0 -137
  410. package/core/dev/specs/agent-connect/examples/minimal-site/package.json +0 -12
  411. package/core/dev/specs/agent-connect/examples/minimal-site/public/index.html +0 -116
  412. package/core/dev/specs/agent-connect/examples/minimal-site/public/install-redirect.js +0 -73
  413. package/core/dev/specs/agent-connect/examples/minimal-site/server.js +0 -516
  414. package/core/dev/specs/agent-connect/examples/minimal-site/test/smoke.test.mjs +0 -263
  415. 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
  416. package/core/dev/specs/agent-connect/history/DEFECT_REPORT_kenaz-agent-hub_2026-09-09_v0.4-prompt-mojibake.md +0 -90
  417. package/core/dev/specs/agent-connect/history/FEATURE_REQUEST_kenaz-agent-hub_2026-09-07.md +0 -61
  418. package/core/dev/specs/agent-connect/history/FEATURE_REQUEST_kenaz-agent-hub_2026-09-08_relay-v1-conformance.md +0 -76
  419. package/core/dev/specs/agent-connect/history/FEATURE_REQUEST_kenaz-agent-hub_2026-09-09_v0.4-session-channel.md +0 -87
  420. package/core/dev/specs/agent-connect/history/FLOWPIX_REPLY_agent-hub_2026-09-08_relay-v1-answers.md +0 -68
  421. package/core/dev/specs/agent-connect/history/FLOWPIX_bridge_CONTRACT.md +0 -642
  422. package/core/dev/specs/agent-connect/history/FLOWPIX_spikes_relay_spike_relay.mjs +0 -244
  423. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-07.md +0 -77
  424. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-08_direction-change-v0.3.md +0 -44
  425. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-08_relay-v1-answers-ack.md +0 -37
  426. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-08_relay-v1-conformance.md +0 -56
  427. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_annotations-install-coldstart.md +0 -44
  428. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_empty-prompt-first-turn.md +0 -56
  429. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_prompt-mojibake-fixed.md +0 -48
  430. package/core/dev/specs/agent-connect/history/KENAZ_RESPONSE_agent-hub_2026-09-09_session-channel.md +0 -57
  431. package/core/dev/specs/agent-connect/history/PROTOCOL_v0.3_snapshot.md +0 -379
  432. package/core/dev/specs/agent-connect/history/README.md +0 -47
  433. package/core/dev/specs/peer/CODEX_INTERACTIVE_PUSH.md +0 -65
  434. package/core/dev/specs/peer/CODEX_MANAGED_DAEMON.md +0 -54
  435. package/core/dev/specs/peer/CODEX_PEER_PULL_SPIKE.md +0 -54
  436. package/core/dev/specs/peer/MODS_SPIKE.md +0 -133
  437. package/core/dev/specs/peer/PER_PROJECT_ADDRESSING.md +0 -143
  438. package/core/dev/specs/peer/SWARM_PROTOCOLS.md +0 -256
  439. package/core/dev/specs/peer/codex-hook-shim-peer.test.js +0 -113
  440. package/core/dev/specs/peer/codex-push-launcher.test.js +0 -142
  441. package/core/dev/specs/peer/setup-codex-hooks.test.js +0 -60
  442. package/core/dev/specs/peer-ansuz/DELIVERY_SPIKE.md +0 -397
  443. package/core/dev/specs/peer-ansuz/DESIGN.md +0 -109
  444. package/core/dev/specs/peer-ansuz/spike/claude_poke.js +0 -129
  445. package/core/dev/specs/peer-ansuz/spike/claude_session_start_hook.js +0 -37
  446. package/core/dev/specs/peer-ansuz/spike/claude_settings_hook.json +0 -14
  447. package/core/dev/specs/peer-ansuz/spike/codex_appserver_client.js +0 -105
  448. package/core/dev/specs/peer-ansuz/spike/codex_appserver_query.js +0 -29
  449. package/core/dev/specs/peer-ansuz/spike/extract_timeline.js +0 -26
  450. package/core/dev/specs/synapse/SYMBOL_IDENTITY.md +0 -305
  451. package/core/dev/spikes/A4-cypher-varlen.md +0 -176
  452. package/core/dev/spikes/bloat-rounds.sh +0 -25
  453. package/core/dev/spikes/mods/clean-claude.js +0 -9
  454. package/core/dev/spikes/mods/drive-probe.js +0 -29
  455. package/core/dev/spikes/mods/e2e-peer.js +0 -71
  456. package/core/dev/spikes/mods/evidence/e2e-peer-control.json +0 -17
  457. package/core/dev/spikes/mods/evidence/e2e-peer-mod.json +0 -17
  458. package/core/dev/spikes/mods/evidence/probe-006a6e65-d807-4bc1-b648-fd7d1edfe0b4.jsonl +0 -4
  459. package/core/dev/spikes/mods/evidence/probe-cebdf1f7-f9c4-47a8-83cd-7c0786177404.jsonl +0 -12
  460. package/core/dev/spikes/mods/evidence/probe-eb21147d-10ce-4c32-93ed-37ca37d88d5a.jsonl +0 -11
  461. package/core/dev/spikes/mods/evidence/stream-cebdf1f7.jsonl +0 -18
  462. package/core/dev/spikes/mods/kenaz-mod-probe/.claude-plugin/plugin.json +0 -1
  463. package/core/dev/spikes/mods/kenaz-mod-probe/hooks/hooks.json +0 -1
  464. package/core/dev/spikes/mods/kenaz-mod-probe/hooks/register.ts +0 -65
  465. package/core/dev/spikes/mods/kenaz-mod-probe/tsconfig.json +0 -3
@@ -1,1344 +0,0 @@
1
- #!/usr/bin/env bash
2
- # ============================================================
3
- # Multi-Agent Parallel Scheduler (macOS/Linux version)
4
- # ============================================================
5
- # Equivalent of multi_agent_scheduler.ps1
6
- # Compatible with bash 3.2+ (macOS default)
7
- #
8
- # Launches multiple Claude Agents in parallel for task execution
9
- # - Auto-assigns non-conflicting tasks to multiple agents
10
- # - Uses path locking to prevent conflicts
11
- # - Each agent works on an independent branch
12
- # ============================================================
13
-
14
- set -uo pipefail
15
-
16
- # --- Default Parameters ---
17
- MAX_AGENTS=3
18
- POLL_INTERVAL=30
19
- TASK_TIMEOUT=60
20
- DRY_RUN=false
21
- SINGLE_RUN=false
22
- WORKING_DIRECTORY=""
23
- STANDALONE=false
24
-
25
- # --- Parse Arguments ---
26
- while [ $# -gt 0 ]; do
27
- case "$1" in
28
- --max-agents) MAX_AGENTS="$2"; shift 2 ;;
29
- --poll-interval) POLL_INTERVAL="$2"; shift 2 ;;
30
- --task-timeout) TASK_TIMEOUT="$2"; shift 2 ;;
31
- --dry-run) DRY_RUN=true; shift ;;
32
- --single-run) SINGLE_RUN=true; shift ;;
33
- --working-directory|--wd) WORKING_DIRECTORY="$2"; shift 2 ;;
34
- --standalone|-s) STANDALONE=true; shift ;;
35
- --nopause) shift ;;
36
- *) shift ;;
37
- esac
38
- done
39
-
40
- # Use KENAZ_PROJECT_PATH env var if WorkingDirectory not specified
41
- if [ -z "$WORKING_DIRECTORY" ]; then
42
- if [ -n "${KENAZ_PROJECT_PATH:-}" ]; then
43
- WORKING_DIRECTORY="$KENAZ_PROJECT_PATH"
44
- else
45
- WORKING_DIRECTORY="$(pwd)"
46
- fi
47
- fi
48
-
49
- # --- File Paths ---
50
- LOG_DIR="$WORKING_DIRECTORY/dev/pipeline/logs"
51
- LOG_FILE="$LOG_DIR/multi_agent_$(date +%Y-%m-%d).log"
52
- TASK_QUEUE_FILE="$WORKING_DIRECTORY/dev/pipeline/TASK_QUEUE.json"
53
- LOCK_STATE_FILE="$WORKING_DIRECTORY/dev/pipeline/LOCK_STATE.json"
54
- AGENT_STATE_FILE="$WORKING_DIRECTORY/dev/pipeline/AGENT_STATE.json"
55
- HEARTBEAT_FILE="$WORKING_DIRECTORY/dev/pipeline/DASHBOARD_HEARTBEAT.json"
56
- STARTUP_LOG="$WORKING_DIRECTORY/multi_agent_startup.log"
57
- SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
58
-
59
- # Ensure log directory exists
60
- mkdir -p "$LOG_DIR"
61
-
62
- # --- Agent tracking via temp directory (bash 3.2 compatible) ---
63
- # Each agent gets a directory: $TRACKER_DIR/<agent_id>/
64
- # With files: pid, task_id, branch, start_time, log_file, agent_type
65
- TRACKER_DIR=$(mktemp -d /tmp/kenaz_agents.XXXXXX)
66
-
67
- get_active_agent_ids() {
68
- # List all tracked agent IDs
69
- if [ -d "$TRACKER_DIR" ]; then
70
- ls "$TRACKER_DIR" 2>/dev/null || true
71
- fi
72
- }
73
-
74
- get_active_count() {
75
- local count=0
76
- for d in "$TRACKER_DIR"/*/; do
77
- [ -d "$d" ] && count=$((count + 1))
78
- done
79
- echo "$count"
80
- }
81
-
82
- track_agent() {
83
- local agent_id="$1" pid="$2" task_id="$3" branch="$4" agent_type="$5" log_file="$6" task_title="${7:-}"
84
- local dir="$TRACKER_DIR/$agent_id"
85
- mkdir -p "$dir"
86
- echo "$pid" > "$dir/pid"
87
- echo "$task_id" > "$dir/task_id"
88
- echo "$branch" > "$dir/branch"
89
- echo "$(date +%s)" > "$dir/start_time"
90
- echo "$log_file" > "$dir/log_file"
91
- echo "$agent_type" > "$dir/agent_type"
92
- echo "$task_title" > "$dir/task_title"
93
- }
94
-
95
- untrack_agent() {
96
- local agent_id="$1"
97
- rm -rf "$TRACKER_DIR/$agent_id"
98
- }
99
-
100
- agent_get() {
101
- local agent_id="$1" field="$2"
102
- cat "$TRACKER_DIR/$agent_id/$field" 2>/dev/null || echo ""
103
- }
104
-
105
- is_task_running() {
106
- local task_id="$1"
107
- for d in "$TRACKER_DIR"/*/; do
108
- [ -d "$d" ] || continue
109
- local running_task
110
- running_task=$(cat "$d/task_id" 2>/dev/null || echo "")
111
- if [ "$running_task" = "$task_id" ]; then
112
- return 0
113
- fi
114
- done
115
- return 1
116
- }
117
-
118
- # ============================================================
119
- # Utility Functions
120
- # ============================================================
121
-
122
- log_msg() {
123
- local msg="$1"
124
- local level="${2:-INFO}"
125
- local timestamp
126
- timestamp=$(date "+%Y-%m-%d %H:%M:%S")
127
- local log_line="[$timestamp] [$level] $msg"
128
-
129
- case "$level" in
130
- ERROR) printf '\033[31m%s\033[0m\n' "$log_line" ;;
131
- WARN) printf '\033[33m%s\033[0m\n' "$log_line" ;;
132
- SUCCESS) printf '\033[32m%s\033[0m\n' "$log_line" ;;
133
- *) echo "$log_line" ;;
134
- esac
135
-
136
- echo "$log_line" >> "$LOG_FILE" 2>/dev/null || true
137
- }
138
-
139
- debug_log() {
140
- local timestamp
141
- timestamp=$(date "+%Y-%m-%d %H:%M:%S")
142
- echo "[$timestamp] [${2:-INFO}] $1" >> "$STARTUP_LOG" 2>/dev/null || true
143
- }
144
-
145
- # ============================================================
146
- # Agent Type Detection
147
- # ============================================================
148
-
149
- get_agent_type() {
150
- local task_title="$1"
151
- local explicit_type="${2:-}"
152
-
153
- if [ -n "$explicit_type" ]; then
154
- echo "$explicit_type"
155
- return
156
- fi
157
-
158
- local title_lower
159
- title_lower=$(echo "$task_title" | tr '[:upper:]' '[:lower:]')
160
-
161
- # Keyword matching
162
- for kw in ui ux layout style design css theme color visual icon; do
163
- case "$title_lower" in *"$kw"*) echo "designer"; return ;; esac
164
- done
165
- for kw in api database backend endpoint server sql migration supabase postgres; do
166
- case "$title_lower" in *"$kw"*) echo "backend"; return ;; esac
167
- done
168
- for kw in test qa quality e2e spec coverage; do
169
- case "$title_lower" in *"$kw"*) echo "qa"; return ;; esac
170
- done
171
- for kw in deploy ci/cd monitoring infrastructure docker kubernetes devops; do
172
- case "$title_lower" in *"$kw"*) echo "sre"; return ;; esac
173
- done
174
- for kw in component react frontend tsx jsx hook state page dashboard; do
175
- case "$title_lower" in *"$kw"*) echo "developer"; return ;; esac
176
- done
177
-
178
- echo "dev"
179
- }
180
-
181
- get_display_name() {
182
- case "$1" in
183
- developer) echo "Frontend Developer" ;;
184
- backend) echo "Backend Engineer" ;;
185
- designer) echo "UI/UX Designer" ;;
186
- qa) echo "QA Engineer" ;;
187
- sre) echo "SRE Agent" ;;
188
- frontend) echo "Frontend Agent" ;;
189
- architect) echo "Architect" ;;
190
- *) echo "Dev Agent" ;;
191
- esac
192
- }
193
-
194
- get_dashboard_type() {
195
- case "$1" in
196
- developer) echo "developer" ;;
197
- backend) echo "backend_engineer" ;;
198
- designer) echo "designer" ;;
199
- qa) echo "qa_engineer" ;;
200
- sre) echo "sre_agent" ;;
201
- frontend) echo "frontend_agent" ;;
202
- architect) echo "architect_agent" ;;
203
- *) echo "dev_agent" ;;
204
- esac
205
- }
206
-
207
- # ============================================================
208
- # State Management (via Python for reliable JSON ops)
209
- # ============================================================
210
-
211
- save_lock_state() {
212
- local content="$1"
213
- python3 -c "
214
- import json
215
- from datetime import datetime
216
- data = json.loads(r'''$content''')
217
- data['last_updated'] = datetime.now().isoformat()
218
- with open('$LOCK_STATE_FILE', 'w', encoding='utf-8') as f:
219
- json.dump(data, f, indent=2, ensure_ascii=False)
220
- " 2>/dev/null || true
221
- }
222
-
223
- save_agent_state() {
224
- local content="$1"
225
- python3 -c "
226
- import json
227
- from datetime import datetime
228
- data = json.loads(r'''$content''')
229
- data['last_updated'] = datetime.now().isoformat()
230
- with open('$AGENT_STATE_FILE', 'w', encoding='utf-8') as f:
231
- json.dump(data, f, indent=2, ensure_ascii=False)
232
- " 2>/dev/null || true
233
- }
234
-
235
- add_agent_to_state() {
236
- local agent_id="$1"
237
- local task_id="$2"
238
- local task_title="$3"
239
- local branch="$4"
240
- local agent_type="$5"
241
- local dashboard_type
242
- dashboard_type=$(get_dashboard_type "$agent_type")
243
-
244
- python3 << PYEOF
245
- import json
246
- from datetime import datetime
247
-
248
- state_file = '$AGENT_STATE_FILE'
249
- try:
250
- with open(state_file, 'r', encoding='utf-8') as f:
251
- state = json.load(f)
252
- except (FileNotFoundError, json.JSONDecodeError):
253
- state = {'agents': []}
254
-
255
- state['agents'] = [a for a in state.get('agents', []) if a.get('id') != '$agent_id']
256
- state['agents'].append({
257
- 'id': '$agent_id',
258
- 'type': '$dashboard_type',
259
- 'status': 'working',
260
- 'current_task': '$task_id',
261
- 'current_operation': $(python3 -c "import json; print(json.dumps('$task_title'))"),
262
- 'branch': '$branch',
263
- 'started_at': datetime.now().isoformat(),
264
- 'task_id': '$task_id',
265
- 'task_title': $(python3 -c "import json; print(json.dumps('$task_title'))"),
266
- })
267
- state['last_updated'] = datetime.now().isoformat()
268
-
269
- with open(state_file, 'w', encoding='utf-8') as f:
270
- json.dump(state, f, indent=2, ensure_ascii=False)
271
- PYEOF
272
-
273
- local display_name
274
- display_name=$(get_display_name "$agent_type")
275
- log_msg "Added agent $agent_id ($display_name) to AGENT_STATE.json (task: $task_id)"
276
- }
277
-
278
- remove_agent_from_state() {
279
- local agent_id="$1"
280
- python3 -c "
281
- import json
282
- from datetime import datetime
283
- try:
284
- with open('$AGENT_STATE_FILE', 'r', encoding='utf-8') as f:
285
- state = json.load(f)
286
- state['agents'] = [a for a in state.get('agents', []) if a.get('id') != '$agent_id']
287
- state['last_updated'] = datetime.now().isoformat()
288
- with open('$AGENT_STATE_FILE', 'w', encoding='utf-8') as f:
289
- json.dump(state, f, indent=2, ensure_ascii=False)
290
- except Exception:
291
- pass
292
- " 2>/dev/null || true
293
- log_msg "Removed agent $agent_id from AGENT_STATE.json"
294
- }
295
-
296
- # ============================================================
297
- # Lock Management
298
- # ============================================================
299
-
300
- add_path_locks() {
301
- local agent_id="$1"
302
- local task_id="$2"
303
- shift 2
304
- # Remaining args are paths, build a JSON array
305
- local paths_json
306
- paths_json=$(python3 -c "import json,sys; print(json.dumps(sys.argv[1:]))" "$@")
307
-
308
- python3 << PYEOF
309
- import json
310
- from datetime import datetime
311
-
312
- lock_file = '$LOCK_STATE_FILE'
313
- try:
314
- with open(lock_file, 'r', encoding='utf-8') as f:
315
- state = json.load(f)
316
- except (FileNotFoundError, json.JSONDecodeError):
317
- state = {'locks': []}
318
-
319
- paths = json.loads('$paths_json')
320
- for path in paths:
321
- state['locks'].append({
322
- 'path': path,
323
- 'agent_id': '$agent_id',
324
- 'task_id': '$task_id',
325
- 'lock_type': 'directory' if path.endswith('/') else 'file',
326
- 'acquired_at': datetime.now().isoformat(),
327
- })
328
-
329
- state['last_updated'] = datetime.now().isoformat()
330
- with open(lock_file, 'w', encoding='utf-8') as f:
331
- json.dump(state, f, indent=2, ensure_ascii=False)
332
- PYEOF
333
- log_msg "Acquired locks for $agent_id on $# paths"
334
- }
335
-
336
- remove_agent_locks() {
337
- local agent_id="$1"
338
- python3 -c "
339
- import json
340
- from datetime import datetime
341
- try:
342
- with open('$LOCK_STATE_FILE', 'r', encoding='utf-8') as f:
343
- state = json.load(f)
344
- state['locks'] = [l for l in state.get('locks', []) if l.get('agent_id') != '$agent_id']
345
- state['last_updated'] = datetime.now().isoformat()
346
- with open('$LOCK_STATE_FILE', 'w', encoding='utf-8') as f:
347
- json.dump(state, f, indent=2, ensure_ascii=False)
348
- except Exception:
349
- pass
350
- " 2>/dev/null || true
351
- log_msg "Released locks for $agent_id"
352
- }
353
-
354
- # ============================================================
355
- # Task Queue Operations
356
- # ============================================================
357
-
358
- update_task_status() {
359
- local task_id="$1"
360
- local status="$2"
361
- local agent_id="${3:-}"
362
- local branch="${4:-}"
363
-
364
- python3 << PYEOF
365
- import json
366
- from datetime import datetime
367
-
368
- try:
369
- with open('$TASK_QUEUE_FILE', 'r', encoding='utf-8') as f:
370
- queue = json.load(f)
371
- except (FileNotFoundError, json.JSONDecodeError):
372
- exit(0)
373
-
374
- for task in queue.get('tasks', []):
375
- if task['id'] == '$task_id':
376
- task['status'] = '$status'
377
- if '$status' == 'in_progress':
378
- task['started_at'] = datetime.now().isoformat()
379
- if '$agent_id':
380
- task['assigned_agent'] = '$agent_id'
381
- if '$branch':
382
- task['branch'] = '$branch'
383
- task.pop('resume_branch', None)
384
- elif '$status' == 'completed':
385
- task['completed_at'] = datetime.now().isoformat()
386
- break
387
-
388
- queue['last_updated'] = datetime.now().isoformat()
389
- with open('$TASK_QUEUE_FILE', 'w', encoding='utf-8') as f:
390
- json.dump(queue, f, indent=2, ensure_ascii=False)
391
- PYEOF
392
- }
393
-
394
- move_task_to_completed() {
395
- local task_id="$1"
396
- local tasks_dir="$WORKING_DIRECTORY/dev/pipeline/tasks"
397
- local completed_dir="$WORKING_DIRECTORY/dev/pipeline/completed"
398
-
399
- mkdir -p "$completed_dir"
400
-
401
- # Move task file
402
- for f in "$tasks_dir"/${task_id}_*.yaml "$tasks_dir"/${task_id}_*.yml "$tasks_dir"/${task_id}_*.md; do
403
- if [ -f "$f" ]; then
404
- mv "$f" "$completed_dir/"
405
- log_msg "Moved task file to completed: $(basename "$f")"
406
- break
407
- fi
408
- done
409
-
410
- # Remove from TASK_QUEUE.json
411
- python3 << PYEOF
412
- import json
413
- from datetime import datetime
414
-
415
- try:
416
- with open('$TASK_QUEUE_FILE', 'r', encoding='utf-8') as f:
417
- queue = json.load(f)
418
-
419
- before = len(queue.get('tasks', []))
420
- queue['tasks'] = [t for t in queue['tasks'] if t['id'] != '$task_id']
421
- after = len(queue['tasks'])
422
-
423
- if before != after:
424
- stats = queue.get('statistics', {})
425
- stats['total'] = len(queue['tasks'])
426
- stats['pending'] = sum(1 for t in queue['tasks'] if t.get('status') == 'pending')
427
- stats['in_progress'] = sum(1 for t in queue['tasks'] if t.get('status') == 'in_progress')
428
- stats['blocked'] = sum(1 for t in queue['tasks'] if t.get('status') == 'blocked')
429
- stats['completed'] = 0
430
- stats['cancelled'] = sum(1 for t in queue['tasks'] if t.get('status') == 'cancelled')
431
- queue['statistics'] = stats
432
- queue['last_updated'] = datetime.now().isoformat()
433
-
434
- with open('$TASK_QUEUE_FILE', 'w', encoding='utf-8') as f:
435
- json.dump(queue, f, indent=2, ensure_ascii=False)
436
- except Exception:
437
- pass
438
- PYEOF
439
- log_msg "Removed $task_id from TASK_QUEUE.json"
440
- }
441
-
442
- # ============================================================
443
- # Progress File Update (ported from PowerShell Update-ProgressFile)
444
- # ============================================================
445
-
446
- update_progress_file() {
447
- local task_id="$1"
448
- local task_title="$2"
449
- local action="$3"
450
- local progress_file="$WORKING_DIRECTORY/dev/pipeline/PROGRESS.md"
451
-
452
- [ -f "$progress_file" ] || return 0
453
-
454
- TASK_QUEUE_FILE="$TASK_QUEUE_FILE" python3 << PYEOF "$progress_file" "$task_id" "$task_title" "$action"
455
- import re, os, json, sys
456
- from datetime import datetime
457
-
458
- progress_file = sys.argv[1]
459
- task_id = sys.argv[2]
460
- task_title = sys.argv[3]
461
- action = sys.argv[4]
462
-
463
- try:
464
- with open(progress_file, 'r', encoding='utf-8') as f:
465
- content = f.read()
466
- except Exception:
467
- sys.exit(0)
468
-
469
- timestamp = datetime.now().strftime('%Y-%m-%d %H:%M')
470
-
471
- # Update Last Update section
472
- content = re.sub(r'(## Last Update\n-.*?Time.*?:)[^\n]*', rf'\1 {timestamp}', content)
473
- content = re.sub(r'(## Last Update\n.*?Agent.*?:)[^\n]*', r'\1 Multi-Agent Scheduler', content)
474
- content = re.sub(r'(## Last Update\n.*?Session.*?:)[^\n]*', rf'\1 {task_id} {action}', content)
475
-
476
- # Update statistics from current queue
477
- queue_file = os.environ.get('TASK_QUEUE_FILE', '')
478
- try:
479
- with open(queue_file, 'r', encoding='utf-8') as f:
480
- queue = json.load(f)
481
- tasks = queue.get('tasks', [])
482
- total = len(tasks)
483
- completed = sum(1 for t in tasks if t.get('status') == 'completed')
484
- in_progress = sum(1 for t in tasks if t.get('status') == 'in_progress')
485
- pending = sum(1 for t in tasks if t.get('status') == 'pending')
486
- blocked = sum(1 for t in tasks if t.get('status') == 'blocked')
487
-
488
- # Also count completed/ directory
489
- completed_dir = os.path.join(os.path.dirname(queue_file), 'completed')
490
- if os.path.isdir(completed_dir):
491
- completed += sum(1 for f in os.listdir(completed_dir) if re.match(r'TASK_\d+', f))
492
- total += sum(1 for f in os.listdir(completed_dir) if re.match(r'TASK_\d+', f))
493
-
494
- c_pct = round(completed / total * 100) if total > 0 else 0
495
- i_pct = round(in_progress / total * 100) if total > 0 else 0
496
- p_pct = round(pending / total * 100) if total > 0 else 0
497
- b_pct = round(blocked / total * 100) if total > 0 else 0
498
-
499
- stats_block = f"""Total Tasks: {total}
500
- Completed: {completed} ({c_pct}%)
501
- In Progress: {in_progress} ({i_pct}%)
502
- Pending: {pending} ({p_pct}%)
503
- Blocked: {blocked} ({b_pct}%)"""
504
-
505
- content = re.sub(
506
- r'(### Overall Progress\n```\n).*?(```)',
507
- rf'\1{stats_block}\n\2',
508
- content,
509
- flags=re.DOTALL
510
- )
511
- except Exception:
512
- pass
513
-
514
- # Add to change history
515
- date_str = datetime.now().strftime('%Y-%m-%d')
516
- history_entry = f"| {date_str} | {action} | {task_id} - {task_title} |"
517
- # Insert after the header row of the history table
518
- content = re.sub(
519
- r'(\| Date \| Action \| Details \|\n\|[-| ]+\|)\n',
520
- rf'\1\n{history_entry}\n',
521
- content
522
- )
523
-
524
- with open(progress_file, 'w', encoding='utf-8') as f:
525
- f.write(content)
526
- PYEOF
527
-
528
- log_msg "Updated PROGRESS.md ($task_id: $action)"
529
- }
530
-
531
- # Get ready tasks (pending, deps met, sorted by priority) — outputs JSON lines
532
- get_ready_tasks() {
533
- TASK_QUEUE_FILE="$TASK_QUEUE_FILE" WORKING_DIRECTORY="$WORKING_DIRECTORY" python3 << 'PYEOF'
534
- import json, os, re, sys
535
-
536
- queue_file = os.environ.get('TASK_QUEUE_FILE', '')
537
- working_dir = os.environ.get('WORKING_DIRECTORY', '')
538
-
539
- try:
540
- with open(queue_file, 'r', encoding='utf-8') as f:
541
- queue = json.load(f)
542
- except (FileNotFoundError, json.JSONDecodeError):
543
- sys.exit(0)
544
-
545
- completed_ids = set()
546
- for task in queue.get('tasks', []):
547
- if task.get('status') == 'completed':
548
- completed_ids.add(task['id'])
549
-
550
- completed_dir = os.path.join(working_dir, 'dev/pipeline/completed')
551
- if os.path.isdir(completed_dir):
552
- for f in os.listdir(completed_dir):
553
- m = re.match(r'^(TASK_\d+)', f)
554
- if m:
555
- completed_ids.add(m.group(1))
556
-
557
- ready = []
558
- for task in queue.get('tasks', []):
559
- if task.get('status') != 'pending':
560
- continue
561
- deps = task.get('dependencies', [])
562
- if all(d in completed_ids for d in deps):
563
- ready.append(task)
564
-
565
- priority_order = {'P0': 0, 'P1': 1, 'P2': 2, 'P3': 3}
566
- ready.sort(key=lambda t: priority_order.get(t.get('priority', 'P2'), 2))
567
-
568
- for task in ready:
569
- print(json.dumps(task))
570
- PYEOF
571
- }
572
-
573
- # Check path conflict against current locks
574
- check_path_conflict() {
575
- local scope_paths_json="$1"
576
- python3 -c "
577
- import json, sys
578
-
579
- try:
580
- with open('$LOCK_STATE_FILE', 'r', encoding='utf-8') as f:
581
- state = json.load(f)
582
- except (FileNotFoundError, json.JSONDecodeError):
583
- print('NO_CONFLICT')
584
- sys.exit(0)
585
-
586
- requested = json.loads('''$scope_paths_json''')
587
- locks = state.get('locks', [])
588
-
589
- for req_path in requested:
590
- for lock in locks:
591
- lock_path = lock.get('path', '')
592
- if req_path.startswith(lock_path) or lock_path.startswith(req_path) or req_path == lock_path:
593
- print(f\"CONFLICT:{lock.get('agent_id', '')}:{lock_path}:{lock.get('task_id', '')}\")
594
- sys.exit(0)
595
-
596
- print('NO_CONFLICT')
597
- " 2>/dev/null || echo "NO_CONFLICT"
598
- }
599
-
600
- # ============================================================
601
- # Heartbeat Check
602
- # ============================================================
603
-
604
- check_heartbeat() {
605
- if [ "$STANDALONE" = "true" ]; then
606
- echo "ALIVE"
607
- return
608
- fi
609
-
610
- if [ ! -f "$HEARTBEAT_FILE" ]; then
611
- echo "ALIVE"
612
- return
613
- fi
614
-
615
- python3 -c "
616
- import json, sys
617
- from datetime import datetime
618
-
619
- try:
620
- with open('$HEARTBEAT_FILE', 'r', encoding='utf-8') as f:
621
- hb = json.load(f)
622
-
623
- if hb.get('status') == 'closing':
624
- print('CLOSING')
625
- sys.exit(0)
626
-
627
- if hb.get('timestamp'):
628
- ts = hb['timestamp']
629
- # Handle various ISO format variants
630
- for fmt in ['%Y-%m-%dT%H:%M:%S.%f', '%Y-%m-%dT%H:%M:%S', '%Y-%m-%dT%H:%M:%S%z']:
631
- try:
632
- last = datetime.strptime(ts.split('+')[0].split('Z')[0], fmt.replace('%z',''))
633
- break
634
- except ValueError:
635
- continue
636
- else:
637
- print('ALIVE')
638
- sys.exit(0)
639
- elapsed = (datetime.now() - last).total_seconds()
640
- if elapsed > 120:
641
- print(f'TIMEOUT:{int(elapsed)}')
642
- sys.exit(0)
643
-
644
- print('ALIVE')
645
- except Exception:
646
- print('ALIVE')
647
- " 2>/dev/null || echo "ALIVE"
648
- }
649
-
650
- # ============================================================
651
- # Agent Success Detection
652
- # ============================================================
653
-
654
- test_agent_success() {
655
- local exit_code="$1"
656
- local log_file="$2"
657
- local branch="$3"
658
- local task_id="$4"
659
-
660
- # Exit code 0 = always success
661
- if [ "$exit_code" = "0" ]; then
662
- echo "SUCCESS:Exit code 0"
663
- return
664
- fi
665
-
666
- # Check for commits in branch
667
- if [ -n "$branch" ]; then
668
- local commits_ahead="0"
669
- if cd "$WORKING_DIRECTORY" 2>/dev/null; then
670
- if git rev-parse --verify "$branch" >/dev/null 2>&1; then
671
- local main_branch="main"
672
- git rev-parse --verify main >/dev/null 2>&1 || main_branch="master"
673
- commits_ahead=$(git rev-list --count "${main_branch}..${branch}" 2>/dev/null || echo "0")
674
- if [ "$commits_ahead" -gt 0 ] 2>/dev/null; then
675
- local commit_msgs
676
- commit_msgs=$(git log --oneline "${main_branch}..${branch}" 2>/dev/null || echo "")
677
- if echo "$commit_msgs" | grep -q "$task_id"; then
678
- echo "SUCCESS:Branch has $commits_ahead commit(s) with $task_id"
679
- return
680
- fi
681
- fi
682
- fi
683
- fi
684
- fi
685
-
686
- # Check log file for success patterns
687
- if [ -n "$log_file" ] && [ -f "$log_file" ]; then
688
- if grep -qE "Status:\s*SUCCESS|completed\s+successfully|\[SUCCESS\].*completed" "$log_file" 2>/dev/null; then
689
- echo "SUCCESS:Log matches success pattern"
690
- return
691
- fi
692
- if grep -qE "FATAL\s+ERROR|Build\s+failed|Compilation\s+error" "$log_file" 2>/dev/null; then
693
- echo "FAIL:Log matches failure pattern"
694
- return
695
- fi
696
- fi
697
-
698
- echo "FAIL:Exit code $exit_code"
699
- }
700
-
701
- # ============================================================
702
- # Process Management
703
- # ============================================================
704
-
705
- kill_process_tree() {
706
- local pid="$1"
707
- # Try to kill the process group first
708
- kill -TERM -- -"$pid" 2>/dev/null || kill -TERM "$pid" 2>/dev/null || true
709
- sleep 1
710
- kill -9 -- -"$pid" 2>/dev/null || kill -9 "$pid" 2>/dev/null || true
711
- log_msg "Terminated process tree for PID $pid"
712
- }
713
-
714
- generate_agent_id() {
715
- echo "agent_$(date +%H%M%S)_$(head -c 4 /dev/urandom | od -An -tx1 | tr -d ' \n' | head -c 4)"
716
- }
717
-
718
- # ============================================================
719
- # Start Agent Worker
720
- # ============================================================
721
-
722
- start_agent_worker() {
723
- local agent_id="$1"
724
- local task_id="$2"
725
- local task_title="$3"
726
- local agent_type="$4"
727
- shift 4
728
- local scope_paths_args=("$@")
729
-
730
- local display_name
731
- display_name=$(get_display_name "$agent_type")
732
- log_msg "Starting $display_name ($agent_id) for task $task_id" "SUCCESS"
733
-
734
- if [ "$DRY_RUN" = "true" ]; then
735
- log_msg "[DRY RUN] Would start: claude -p '/agent-worker --task $task_id --agent $agent_id'" "WARN"
736
- return 1
737
- fi
738
-
739
- # Check for resumable branch
740
- local resume_branch=""
741
- resume_branch=$(python3 -c "
742
- import json
743
- try:
744
- with open('$TASK_QUEUE_FILE', 'r') as f:
745
- q = json.load(f)
746
- for t in q.get('tasks', []):
747
- if t['id'] == '$task_id' and t.get('resume_branch'):
748
- print(t['resume_branch'])
749
- break
750
- except Exception:
751
- pass
752
- " 2>/dev/null)
753
-
754
- # Determine branch name
755
- local branch_name
756
- if [ -n "$resume_branch" ]; then
757
- branch_name="$resume_branch"
758
- log_msg "Resuming task from existing branch: $resume_branch"
759
- else
760
- branch_name="task-$(echo "$task_id" | tr '[:upper:]' '[:lower:]')-$agent_id"
761
- fi
762
-
763
- # Setup git branch
764
- (
765
- cd "$WORKING_DIRECTORY" || exit 1
766
- if [ -n "$resume_branch" ]; then
767
- git checkout "$resume_branch" 2>/dev/null || {
768
- log_msg "Failed to checkout resume branch, creating new one" "WARN"
769
- branch_name="task-$(echo "$task_id" | tr '[:upper:]' '[:lower:]')-$agent_id"
770
- git checkout main 2>/dev/null || git checkout master 2>/dev/null
771
- git checkout -b "$branch_name" 2>/dev/null
772
- }
773
- else
774
- local current_branch
775
- current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
776
- if [ "$current_branch" != "main" ] && [ "$current_branch" != "master" ]; then
777
- git checkout main 2>/dev/null || git checkout master 2>/dev/null
778
- fi
779
- git checkout -b "$branch_name" 2>/dev/null
780
- fi
781
- )
782
-
783
- # Create agent log file
784
- local agent_log="$LOG_DIR/agent_${agent_id}.log"
785
- local agent_err="$LOG_DIR/agent_${agent_id}.err"
786
- local prompt="/agent-worker --task $task_id --agent $agent_id --branch $branch_name"
787
-
788
- # Launch claude agent in background (must run from project root so .claude/commands/ is found)
789
- (cd "$WORKING_DIRECTORY" && exec claude --dangerously-skip-permissions -p "$prompt") \
790
- > "$agent_log" 2> "$agent_err" &
791
- local pid=$!
792
-
793
- log_msg "Agent $agent_id started (PID: $pid)"
794
- log_msg "Agent log: $agent_log"
795
-
796
- # Store tracking info
797
- track_agent "$agent_id" "$pid" "$task_id" "$branch_name" "$agent_type" "$agent_log" "$task_title"
798
-
799
- return 0
800
- }
801
-
802
- # ============================================================
803
- # Backup Task Files
804
- # ============================================================
805
-
806
- backup_task_files() {
807
- local tasks_dir="$WORKING_DIRECTORY/dev/pipeline/tasks"
808
- local backup_dir="$WORKING_DIRECTORY/dev/pipeline/backups"
809
-
810
- if [ ! -d "$tasks_dir" ]; then
811
- log_msg "Tasks directory not found, skipping backup" "WARN"
812
- return
813
- fi
814
-
815
- local file_count
816
- file_count=$(find "$tasks_dir" -maxdepth 1 -type f 2>/dev/null | wc -l | tr -d ' ')
817
- if [ "$file_count" -eq 0 ]; then
818
- log_msg "No task files to backup"
819
- return
820
- fi
821
-
822
- local timestamp
823
- timestamp=$(date +%Y%m%d_%H%M%S)
824
- local backup_path="$backup_dir/tasks_$timestamp"
825
-
826
- mkdir -p "$backup_dir"
827
- cp -r "$tasks_dir" "$backup_path"
828
- log_msg "Backed up $file_count task file(s) to: $backup_path" "SUCCESS"
829
-
830
- # Clean up old backups (keep last 5)
831
- ls -dt "$backup_dir"/tasks_* 2>/dev/null | tail -n +6 | while read -r old; do
832
- rm -rf "$old"
833
- log_msg "Removed old backup: $(basename "$old")"
834
- done
835
- }
836
-
837
- # ============================================================
838
- # Clear Stale State
839
- # ============================================================
840
-
841
- clear_stale_state() {
842
- log_msg "Cleaning stale state from previous sessions..."
843
-
844
- # Clear locks
845
- if [ -f "$LOCK_STATE_FILE" ]; then
846
- local lock_count
847
- lock_count=$(python3 -c "
848
- import json
849
- try:
850
- with open('$LOCK_STATE_FILE', 'r') as f:
851
- data = json.load(f)
852
- print(len(data.get('locks', [])))
853
- except Exception:
854
- print(0)
855
- " 2>/dev/null)
856
- if [ "${lock_count:-0}" -gt 0 ] 2>/dev/null; then
857
- log_msg "Found $lock_count stale lock(s) from previous session" "WARN"
858
- fi
859
- fi
860
- save_lock_state '{"locks": []}'
861
-
862
- # Clear agents
863
- if [ -f "$AGENT_STATE_FILE" ]; then
864
- local agent_count
865
- agent_count=$(python3 -c "
866
- import json
867
- try:
868
- with open('$AGENT_STATE_FILE', 'r') as f:
869
- data = json.load(f)
870
- print(len(data.get('agents', [])))
871
- except Exception:
872
- print(0)
873
- " 2>/dev/null)
874
- if [ "${agent_count:-0}" -gt 0 ] 2>/dev/null; then
875
- log_msg "Found $agent_count stale agent(s) from previous session" "WARN"
876
- fi
877
- fi
878
- save_agent_state '{"agents": []}'
879
-
880
- # Reset in_progress tasks to pending
881
- if [ -f "$TASK_QUEUE_FILE" ]; then
882
- WORKING_DIRECTORY="$WORKING_DIRECTORY" python3 << 'PYEOF'
883
- import json, os, subprocess
884
- from datetime import datetime
885
-
886
- queue_file = os.environ.get('TASK_QUEUE_FILE', '')
887
- working_dir = os.environ.get('WORKING_DIRECTORY', '')
888
-
889
- try:
890
- with open(queue_file, 'r', encoding='utf-8') as f:
891
- queue = json.load(f)
892
- except (FileNotFoundError, json.JSONDecodeError):
893
- exit(0)
894
-
895
- changed = False
896
- for task in queue.get('tasks', []):
897
- if task.get('status') == 'in_progress':
898
- branch = task.get('branch', '')
899
- has_progress = False
900
-
901
- if branch:
902
- try:
903
- result = subprocess.run(
904
- ['git', 'rev-list', '--count', f'main..{branch}'],
905
- capture_output=True, text=True, cwd=working_dir
906
- )
907
- if result.returncode == 0 and int(result.stdout.strip()) > 0:
908
- has_progress = True
909
- except Exception:
910
- pass
911
-
912
- task['status'] = 'pending'
913
- task.pop('assigned_agent', None)
914
- task.pop('started_at', None)
915
-
916
- if has_progress:
917
- task['resume_branch'] = branch
918
- task.pop('branch', None)
919
- else:
920
- task.pop('branch', None)
921
- task.pop('resume_branch', None)
922
-
923
- changed = True
924
-
925
- if changed:
926
- queue['last_updated'] = datetime.now().isoformat()
927
- with open(queue_file, 'w', encoding='utf-8') as f:
928
- json.dump(queue, f, indent=2, ensure_ascii=False)
929
- PYEOF
930
- fi
931
-
932
- log_msg "Stale state cleanup completed" "SUCCESS"
933
- }
934
-
935
- # ============================================================
936
- # Cleanup on Exit
937
- # ============================================================
938
-
939
- cleanup() {
940
- log_msg "Shutting down scheduler..."
941
-
942
- # Terminate all active agents
943
- for agent_id in $(get_active_agent_ids); do
944
- local pid
945
- pid=$(agent_get "$agent_id" "pid")
946
- if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
947
- log_msg "Terminating agent $agent_id (PID: $pid)"
948
- kill_process_tree "$pid"
949
- fi
950
- remove_agent_locks "$agent_id"
951
- remove_agent_from_state "$agent_id"
952
- done
953
-
954
- # Update AGENT_STATE.json
955
- save_agent_state '{"agents": [], "scheduler": {"status": "stopped", "stopped_at": "'"$(date -u +%Y-%m-%dT%H:%M:%S)"'"}}'
956
- log_msg "Scheduler stopped"
957
-
958
- # Cleanup tracker directory
959
- rm -rf "$TRACKER_DIR"
960
- }
961
-
962
- trap cleanup EXIT INT TERM
963
-
964
- # ============================================================
965
- # Main
966
- # ============================================================
967
-
968
- clear
969
- echo ""
970
- echo "============================================================"
971
- echo " Multi-Agent Parallel Scheduler"
972
- echo "============================================================"
973
- echo " Max Parallel Agents: $MAX_AGENTS"
974
- echo " Poll Interval: ${POLL_INTERVAL}s"
975
- echo " Task Timeout: ${TASK_TIMEOUT}min"
976
- echo " Working Dir: $WORKING_DIRECTORY"
977
- [ "$DRY_RUN" = "true" ] && echo " Mode: DRY RUN"
978
- [ "$STANDALONE" = "true" ] && echo " Mode: STANDALONE (heartbeat ignored)"
979
- echo "============================================================"
980
- echo ""
981
-
982
- # Write startup debug log
983
- cat > "$STARTUP_LOG" << EOF
984
- === Multi-Agent Scheduler Startup ===
985
- Time: $(date "+%Y-%m-%d %H:%M:%S")
986
- WorkingDirectory: $WORKING_DIRECTORY
987
- Script Directory: $SCRIPT_DIR
988
- Shell: ${SHELL:-bash}
989
- OS: $(uname -s) $(uname -r)
990
- Standalone: $STANDALONE
991
- MaxAgents: $MAX_AGENTS
992
- PollIntervalSeconds: $POLL_INTERVAL
993
- TaskTimeoutMinutes: $TASK_TIMEOUT
994
- === Execution Log ===
995
- EOF
996
-
997
- # --- Prerequisite Checks ---
998
- echo "[CHECK] Running prerequisite checks..."
999
-
1000
- if ! command -v claude >/dev/null 2>&1; then
1001
- echo ""
1002
- echo "============================================================"
1003
- echo " ERROR: Claude CLI not found!"
1004
- echo "============================================================"
1005
- echo ""
1006
- echo " To install:"
1007
- echo " npm install -g @anthropic-ai/claude-code"
1008
- echo ""
1009
- exit 1
1010
- fi
1011
- echo " [OK] Claude CLI found"
1012
-
1013
- if ! command -v python3 >/dev/null 2>&1; then
1014
- echo " [ERROR] python3 not found (required for JSON operations)"
1015
- exit 1
1016
- fi
1017
- echo " [OK] python3 found"
1018
-
1019
- if [ ! -d "$WORKING_DIRECTORY" ]; then
1020
- echo " [ERROR] Working directory not found: $WORKING_DIRECTORY"
1021
- exit 1
1022
- fi
1023
- echo " [OK] Working directory exists"
1024
-
1025
- if [ ! -d "$WORKING_DIRECTORY/dev/pipeline" ]; then
1026
- echo " [ERROR] Pipeline directory not found. Run Initialize Project first."
1027
- exit 1
1028
- fi
1029
- echo " [OK] Pipeline directory exists"
1030
-
1031
- if [ ! -f "$TASK_QUEUE_FILE" ]; then
1032
- echo " [WARN] Task queue not found, will be created by sync"
1033
- fi
1034
-
1035
- echo "[CHECK] Prerequisites passed"
1036
- echo ""
1037
-
1038
- # --- Standalone: remove old heartbeat ---
1039
- if [ "$STANDALONE" = "true" ] && [ -f "$HEARTBEAT_FILE" ]; then
1040
- rm -f "$HEARTBEAT_FILE"
1041
- echo "[STANDALONE] Removed old heartbeat file"
1042
- fi
1043
-
1044
- # --- Auto-sync task queue ---
1045
- echo "[SYNC] Syncing task queue from task files..."
1046
- SYNC_SCRIPT="$SCRIPT_DIR/sync_task_queue.sh"
1047
- if [ -x "$SYNC_SCRIPT" ]; then
1048
- "$SYNC_SCRIPT" --wd "$WORKING_DIRECTORY" --quiet || true
1049
- echo "[SYNC] Task queue synced successfully"
1050
- else
1051
- echo "[SYNC] sync_task_queue.sh not found or not executable, skipping"
1052
- fi
1053
- echo ""
1054
-
1055
- log_msg "Multi-Agent Scheduler started (MaxAgents=$MAX_AGENTS)"
1056
-
1057
- # Backup task files
1058
- backup_task_files
1059
-
1060
- # Clean stale state
1061
- export TASK_QUEUE_FILE WORKING_DIRECTORY
1062
- clear_stale_state
1063
-
1064
- # Check for pending tasks
1065
- PENDING_INFO=$(python3 -c "
1066
- import json
1067
- try:
1068
- with open('$TASK_QUEUE_FILE', 'r') as f:
1069
- q = json.load(f)
1070
- pending = [t for t in q.get('tasks', []) if t.get('status') == 'pending']
1071
- total = len(q.get('tasks', []))
1072
- print(f'{len(pending)}:{total}')
1073
- except Exception:
1074
- print('0:0')
1075
- " 2>/dev/null)
1076
-
1077
- PENDING="${PENDING_INFO%%:*}"
1078
- TOTAL="${PENDING_INFO##*:}"
1079
-
1080
- if [ "$PENDING" -eq 0 ] 2>/dev/null; then
1081
- echo ""
1082
- echo "============================================================"
1083
- echo " No pending tasks found!"
1084
- echo "============================================================"
1085
- echo " Total: $TOTAL Pending: 0"
1086
- echo ""
1087
- if [ "$SINGLE_RUN" = "true" ]; then
1088
- log_msg "No pending tasks (SingleRun mode)"
1089
- exit 0
1090
- fi
1091
- log_msg "No pending tasks. Scheduler will wait for new tasks..."
1092
- else
1093
- log_msg "Found $PENDING pending task(s) out of $TOTAL total" "SUCCESS"
1094
- fi
1095
-
1096
- # Set scheduler status to running
1097
- save_agent_state '{"agents": [], "scheduler": {"status": "running", "max_agents": '"$MAX_AGENTS"', "started_at": "'"$(date -u +%Y-%m-%dT%H:%M:%S)"'"}}'
1098
- log_msg "Scheduler status set to 'running' in AGENT_STATE.json"
1099
-
1100
- # Sync counter
1101
- SYNC_COUNTER=0
1102
- SYNC_INTERVAL=5
1103
-
1104
- # ============================================================
1105
- # Main Loop
1106
- # ============================================================
1107
-
1108
- while true; do
1109
- # Check if scheduler was stopped via API
1110
- local_status=$(python3 -c "
1111
- import json
1112
- try:
1113
- with open('$AGENT_STATE_FILE', 'r') as f:
1114
- s = json.load(f)
1115
- print(s.get('scheduler', {}).get('status', 'running'))
1116
- except Exception:
1117
- print('running')
1118
- " 2>/dev/null)
1119
-
1120
- if [ "$local_status" = "stopped" ]; then
1121
- log_msg "Scheduler stop requested via API - shutting down..." "WARN"
1122
- exit 0
1123
- fi
1124
-
1125
- # Check heartbeat
1126
- hb_status=$(check_heartbeat)
1127
- if [ "$hb_status" = "CLOSING" ]; then
1128
- log_msg "Dashboard explicitly closed. Exiting." "WARN"
1129
- exit 0
1130
- fi
1131
- case "$hb_status" in
1132
- TIMEOUT:*)
1133
- log_msg "Dashboard heartbeat timeout. Exiting." "WARN"
1134
- exit 0
1135
- ;;
1136
- esac
1137
-
1138
- # Periodic sync
1139
- SYNC_COUNTER=$((SYNC_COUNTER + 1))
1140
- active_count=$(get_active_count)
1141
- if [ $SYNC_COUNTER -ge $SYNC_INTERVAL ] || [ "$active_count" -eq 0 ]; then
1142
- SYNC_COUNTER=0
1143
- if [ -x "$SYNC_SCRIPT" ]; then
1144
- "$SYNC_SCRIPT" --wd "$WORKING_DIRECTORY" --quiet 2>/dev/null || true
1145
- fi
1146
- fi
1147
-
1148
- # Check completed agents
1149
- for agent_id in $(get_active_agent_ids); do
1150
- pid=$(agent_get "$agent_id" "pid")
1151
- task_id=$(agent_get "$agent_id" "task_id")
1152
- branch=$(agent_get "$agent_id" "branch")
1153
- start_time=$(agent_get "$agent_id" "start_time")
1154
- agent_log=$(agent_get "$agent_id" "log_file")
1155
-
1156
- if [ -z "$pid" ]; then
1157
- untrack_agent "$agent_id"
1158
- continue
1159
- fi
1160
-
1161
- if ! kill -0 "$pid" 2>/dev/null; then
1162
- # Process has exited
1163
- wait "$pid" 2>/dev/null
1164
- exit_code=$?
1165
- now=$(date +%s)
1166
- duration_min=$(( (now - start_time) / 60 ))
1167
-
1168
- result=$(test_agent_success "$exit_code" "$agent_log" "$branch" "$task_id")
1169
-
1170
- case "$result" in
1171
- SUCCESS:*)
1172
- reason="${result#SUCCESS:}"
1173
- log_msg "Agent $agent_id completed task $task_id successfully (${duration_min}min) - $reason" "SUCCESS"
1174
-
1175
- # Get task title before moving (from tracker)
1176
- task_title=$(agent_get "$agent_id" "task_title")
1177
-
1178
- update_task_status "$task_id" "completed"
1179
- move_task_to_completed "$task_id"
1180
-
1181
- # Update PROGRESS.md
1182
- update_progress_file "$task_id" "$task_title" "Completed"
1183
-
1184
- # Merge branch to main
1185
- (
1186
- cd "$WORKING_DIRECTORY" || exit 1
1187
- git checkout main 2>/dev/null || git checkout master 2>/dev/null
1188
- git merge "$branch" -m "Merge $task_id: Completed by $agent_id" 2>/dev/null || true
1189
- git branch -d "$branch" 2>/dev/null || true
1190
- ) && log_msg "Merged branch $branch to main" "SUCCESS"
1191
- ;;
1192
- *)
1193
- reason="${result#FAIL:}"
1194
- log_msg "Agent $agent_id failed task $task_id after ${duration_min}min - $reason" "ERROR"
1195
-
1196
- # Check for partial work
1197
- has_partial=false
1198
- if [ -n "$branch" ]; then
1199
- partial_check=$(
1200
- cd "$WORKING_DIRECTORY" 2>/dev/null || exit 0
1201
- if git rev-parse --verify "$branch" >/dev/null 2>&1; then
1202
- main_branch="main"
1203
- git rev-parse --verify main >/dev/null 2>&1 || main_branch="master"
1204
- ahead=$(git rev-list --count "${main_branch}..${branch}" 2>/dev/null || echo "0")
1205
- [ "$ahead" -gt 0 ] 2>/dev/null && echo "PARTIAL"
1206
- fi
1207
- )
1208
- [ "$partial_check" = "PARTIAL" ] && has_partial=true
1209
- fi
1210
-
1211
- if [ "$has_partial" = "true" ]; then
1212
- log_msg "Branch $branch has commits - marking for resume"
1213
- python3 -c "
1214
- import json
1215
- from datetime import datetime
1216
- try:
1217
- with open('$TASK_QUEUE_FILE', 'r') as f:
1218
- q = json.load(f)
1219
- for t in q.get('tasks', []):
1220
- if t['id'] == '$task_id':
1221
- t['status'] = 'pending'
1222
- t['resume_branch'] = '$branch'
1223
- t.pop('assigned_agent', None)
1224
- break
1225
- q['last_updated'] = datetime.now().isoformat()
1226
- with open('$TASK_QUEUE_FILE', 'w') as f:
1227
- json.dump(q, f, indent=2, ensure_ascii=False)
1228
- except Exception:
1229
- pass
1230
- " 2>/dev/null
1231
- else
1232
- update_task_status "$task_id" "pending"
1233
- fi
1234
- ;;
1235
- esac
1236
-
1237
- remove_agent_locks "$agent_id"
1238
- remove_agent_from_state "$agent_id"
1239
- untrack_agent "$agent_id"
1240
- else
1241
- # Check timeout
1242
- now=$(date +%s)
1243
- elapsed_min=$(( (now - start_time) / 60 ))
1244
- if [ $elapsed_min -gt $TASK_TIMEOUT ] 2>/dev/null; then
1245
- log_msg "Agent $agent_id timed out on task $task_id (PID: $pid)" "WARN"
1246
- kill_process_tree "$pid"
1247
- remove_agent_locks "$agent_id"
1248
- remove_agent_from_state "$agent_id"
1249
- update_task_status "$task_id" "pending"
1250
- untrack_agent "$agent_id"
1251
- fi
1252
- fi
1253
- done
1254
-
1255
- # Start new agents if slots available
1256
- active_count=$(get_active_count)
1257
- available_slots=$(( MAX_AGENTS - active_count ))
1258
-
1259
- if [ $available_slots -gt 0 ]; then
1260
- get_ready_tasks | while IFS= read -r task_json; do
1261
- [ -z "$task_json" ] && continue
1262
-
1263
- # Re-check available slots (may have changed in this loop iteration)
1264
- current_active=$(get_active_count)
1265
- current_available=$(( MAX_AGENTS - current_active ))
1266
- [ $current_available -le 0 ] && break
1267
-
1268
- task_id=$(echo "$task_json" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
1269
- task_title=$(echo "$task_json" | python3 -c "import sys,json; print(json.load(sys.stdin)['title'])")
1270
- scope_paths_json=$(echo "$task_json" | python3 -c "import sys,json; t=json.load(sys.stdin); print(json.dumps(t.get('scope_paths', t.get('scope', {}).get('paths', ['src/']))))")
1271
- agent_type_explicit=$(echo "$task_json" | python3 -c "import sys,json; print(json.load(sys.stdin).get('agent_type', ''))")
1272
-
1273
- # Skip already running tasks
1274
- if is_task_running "$task_id"; then
1275
- continue
1276
- fi
1277
-
1278
- # Check path conflict
1279
- conflict_result=$(check_path_conflict "$scope_paths_json")
1280
- if [ "$conflict_result" != "NO_CONFLICT" ]; then
1281
- conflict_info="${conflict_result#CONFLICT:}"
1282
- log_msg "Task $task_id conflicts with $conflict_info" "WARN"
1283
- continue
1284
- fi
1285
-
1286
- # Read scope paths into args
1287
- scope_paths_args=()
1288
- while IFS= read -r p; do
1289
- scope_paths_args+=("$p")
1290
- done < <(echo "$scope_paths_json" | python3 -c "import sys,json; [print(p) for p in json.load(sys.stdin)]")
1291
-
1292
- # Determine agent type
1293
- agent_type=$(get_agent_type "$task_title" "$agent_type_explicit")
1294
-
1295
- # Generate agent ID
1296
- agent_id=$(generate_agent_id)
1297
-
1298
- # Start agent
1299
- if start_agent_worker "$agent_id" "$task_id" "$task_title" "$agent_type" "${scope_paths_args[@]}"; then
1300
- branch=$(agent_get "$agent_id" "branch")
1301
-
1302
- # Add locks
1303
- add_path_locks "$agent_id" "$task_id" "${scope_paths_args[@]}"
1304
-
1305
- # Update task status
1306
- update_task_status "$task_id" "in_progress" "$agent_id" "$branch"
1307
-
1308
- # Update AGENT_STATE.json
1309
- add_agent_to_state "$agent_id" "$task_id" "$task_title" "$branch" "$agent_type"
1310
-
1311
- display_name=$(get_display_name "$agent_type")
1312
- log_msg "Launched $display_name ($agent_id) for $task_id: $task_title"
1313
- fi
1314
- done
1315
- fi
1316
-
1317
- # Display status line
1318
- timestamp=$(date "+%H:%M:%S")
1319
- active_count=$(get_active_count)
1320
- status_line="[$timestamp] Agents: ${active_count}/$MAX_AGENTS"
1321
-
1322
- if [ "$active_count" -gt 0 ]; then
1323
- agent_info=""
1324
- for agent_id in $(get_active_agent_ids); do
1325
- start_time=$(agent_get "$agent_id" "start_time")
1326
- task_id=$(agent_get "$agent_id" "task_id")
1327
- now=$(date +%s)
1328
- elapsed_min=$(( (now - start_time) / 60 ))
1329
- agent_info="$agent_info ${task_id}(${elapsed_min}min)"
1330
- done
1331
- status_line="$status_line |$agent_info"
1332
- else
1333
- status_line="$status_line | Waiting for tasks..."
1334
- fi
1335
- printf '\033[36m%s\033[0m\n' "$status_line"
1336
-
1337
- # Single run check
1338
- if [ "$SINGLE_RUN" = "true" ] && [ "$active_count" -eq 0 ]; then
1339
- log_msg "Single run completed, no active agents"
1340
- break
1341
- fi
1342
-
1343
- sleep "$POLL_INTERVAL"
1344
- done