aether-colony 5.0.0 → 5.1.0

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 (312) hide show
  1. package/.aether/aether-utils.sh +3150 -3349
  2. package/.aether/agents-claude/aether-ambassador.md +265 -0
  3. package/.aether/agents-claude/aether-archaeologist.md +327 -0
  4. package/.aether/agents-claude/aether-architect.md +236 -0
  5. package/.aether/agents-claude/aether-auditor.md +271 -0
  6. package/.aether/agents-claude/aether-builder.md +224 -0
  7. package/.aether/agents-claude/aether-chaos.md +269 -0
  8. package/.aether/agents-claude/aether-chronicler.md +305 -0
  9. package/.aether/agents-claude/aether-gatekeeper.md +330 -0
  10. package/.aether/agents-claude/aether-includer.md +374 -0
  11. package/.aether/agents-claude/aether-keeper.md +272 -0
  12. package/.aether/agents-claude/aether-measurer.md +322 -0
  13. package/.aether/agents-claude/aether-oracle.md +237 -0
  14. package/.aether/agents-claude/aether-probe.md +211 -0
  15. package/.aether/agents-claude/aether-queen.md +330 -0
  16. package/.aether/agents-claude/aether-route-setter.md +178 -0
  17. package/.aether/agents-claude/aether-sage.md +418 -0
  18. package/.aether/agents-claude/aether-scout.md +179 -0
  19. package/.aether/agents-claude/aether-surveyor-disciplines.md +417 -0
  20. package/.aether/agents-claude/aether-surveyor-nest.md +355 -0
  21. package/.aether/agents-claude/aether-surveyor-pathogens.md +289 -0
  22. package/.aether/agents-claude/aether-surveyor-provisions.md +360 -0
  23. package/.aether/agents-claude/aether-tracker.md +270 -0
  24. package/.aether/agents-claude/aether-watcher.md +280 -0
  25. package/.aether/agents-claude/aether-weaver.md +248 -0
  26. package/.aether/commands/archaeology.yaml +653 -0
  27. package/.aether/commands/build.yaml +1221 -0
  28. package/.aether/commands/chaos.yaml +653 -0
  29. package/.aether/commands/colonize.yaml +438 -0
  30. package/.aether/commands/continue.yaml +1484 -0
  31. package/.aether/commands/council.yaml +304 -0
  32. package/.aether/commands/data-clean.yaml +80 -0
  33. package/.aether/commands/dream.yaml +275 -0
  34. package/.aether/commands/entomb.yaml +863 -0
  35. package/.aether/commands/export-signals.yaml +64 -0
  36. package/.aether/commands/feedback.yaml +158 -0
  37. package/.aether/commands/flag.yaml +160 -0
  38. package/.aether/commands/flags.yaml +177 -0
  39. package/.aether/commands/focus.yaml +112 -0
  40. package/.aether/commands/help.yaml +167 -0
  41. package/.aether/commands/history.yaml +137 -0
  42. package/.aether/commands/import-signals.yaml +79 -0
  43. package/.aether/commands/init.yaml +469 -0
  44. package/.aether/commands/insert-phase.yaml +98 -0
  45. package/.aether/commands/interpret.yaml +285 -0
  46. package/.aether/commands/lay-eggs.yaml +224 -0
  47. package/.aether/commands/maturity.yaml +122 -0
  48. package/.aether/commands/memory-details.yaml +74 -0
  49. package/.aether/commands/migrate-state.yaml +174 -0
  50. package/.aether/commands/oracle.yaml +1224 -0
  51. package/.aether/commands/organize.yaml +446 -0
  52. package/.aether/commands/patrol.yaml +621 -0
  53. package/.aether/commands/pause-colony.yaml +424 -0
  54. package/.aether/commands/phase.yaml +124 -0
  55. package/.aether/commands/pheromones.yaml +153 -0
  56. package/.aether/commands/plan.yaml +1313 -0
  57. package/.aether/commands/preferences.yaml +63 -0
  58. package/.aether/commands/redirect.yaml +123 -0
  59. package/.aether/commands/resume-colony.yaml +373 -0
  60. package/.aether/commands/resume.yaml +398 -0
  61. package/.aether/commands/run.yaml +193 -0
  62. package/.aether/commands/seal.yaml +1205 -0
  63. package/.aether/commands/skill-create.yaml +337 -0
  64. package/.aether/commands/status.yaml +364 -0
  65. package/.aether/commands/swarm.yaml +352 -0
  66. package/.aether/commands/tunnels.yaml +814 -0
  67. package/.aether/commands/update.yaml +131 -0
  68. package/.aether/commands/verify-castes.yaml +159 -0
  69. package/.aether/commands/watch.yaml +454 -0
  70. package/.aether/docs/INCIDENT_TEMPLATE.md +32 -0
  71. package/.aether/docs/QUEEN-SYSTEM.md +11 -11
  72. package/.aether/docs/README.md +32 -2
  73. package/.aether/docs/command-playbooks/README.md +23 -0
  74. package/.aether/docs/command-playbooks/build-complete.md +349 -0
  75. package/.aether/docs/command-playbooks/build-context.md +282 -0
  76. package/.aether/docs/command-playbooks/build-full.md +1682 -0
  77. package/.aether/docs/command-playbooks/build-prep.md +283 -0
  78. package/.aether/docs/command-playbooks/build-verify.md +405 -0
  79. package/.aether/docs/command-playbooks/build-wave.md +749 -0
  80. package/.aether/docs/command-playbooks/continue-advance.md +524 -0
  81. package/.aether/docs/command-playbooks/continue-finalize.md +447 -0
  82. package/.aether/docs/command-playbooks/continue-full.md +1724 -0
  83. package/.aether/docs/command-playbooks/continue-gates.md +686 -0
  84. package/.aether/docs/command-playbooks/continue-verify.md +406 -0
  85. package/.aether/docs/context-continuity.md +84 -0
  86. package/.aether/docs/disciplines/DISCIPLINES.md +9 -7
  87. package/.aether/docs/error-codes.md +1 -1
  88. package/.aether/docs/known-issues.md +34 -173
  89. package/.aether/docs/pheromones.md +86 -6
  90. package/.aether/docs/plans/pheromone-display-plan.md +257 -0
  91. package/.aether/docs/queen-commands.md +10 -9
  92. package/.aether/docs/source-of-truth-map.md +132 -0
  93. package/.aether/docs/xml-utilities.md +47 -0
  94. package/.aether/rules/aether-colony.md +23 -13
  95. package/.aether/scripts/incident-test-add.sh +47 -0
  96. package/.aether/scripts/weekly-audit.sh +79 -0
  97. package/.aether/skills/.index.json +649 -0
  98. package/.aether/skills/colony/.manifest.json +16 -0
  99. package/.aether/skills/colony/build-discipline/SKILL.md +78 -0
  100. package/.aether/skills/colony/colony-interaction/SKILL.md +56 -0
  101. package/.aether/skills/colony/colony-lifecycle/SKILL.md +77 -0
  102. package/.aether/skills/colony/colony-visuals/SKILL.md +112 -0
  103. package/.aether/skills/colony/context-management/SKILL.md +80 -0
  104. package/.aether/skills/colony/error-presentation/SKILL.md +99 -0
  105. package/.aether/skills/colony/pheromone-protocol/SKILL.md +79 -0
  106. package/.aether/skills/colony/pheromone-visibility/SKILL.md +81 -0
  107. package/.aether/skills/colony/state-safety/SKILL.md +84 -0
  108. package/.aether/skills/colony/worker-priming/SKILL.md +82 -0
  109. package/.aether/skills/domain/.manifest.json +24 -0
  110. package/.aether/skills/domain/README.md +33 -0
  111. package/.aether/skills/domain/django/SKILL.md +49 -0
  112. package/.aether/skills/domain/docker/SKILL.md +52 -0
  113. package/.aether/skills/domain/golang/SKILL.md +52 -0
  114. package/.aether/skills/domain/graphql/SKILL.md +51 -0
  115. package/.aether/skills/domain/html-css/SKILL.md +48 -0
  116. package/.aether/skills/domain/nextjs/SKILL.md +45 -0
  117. package/.aether/skills/domain/nodejs/SKILL.md +53 -0
  118. package/.aether/skills/domain/postgresql/SKILL.md +53 -0
  119. package/.aether/skills/domain/prisma/SKILL.md +59 -0
  120. package/.aether/skills/domain/python/SKILL.md +50 -0
  121. package/.aether/skills/domain/rails/SKILL.md +52 -0
  122. package/.aether/skills/domain/react/SKILL.md +45 -0
  123. package/.aether/skills/domain/rest-api/SKILL.md +58 -0
  124. package/.aether/skills/domain/svelte/SKILL.md +47 -0
  125. package/.aether/skills/domain/tailwind/SKILL.md +45 -0
  126. package/.aether/skills/domain/testing/SKILL.md +53 -0
  127. package/.aether/skills/domain/typescript/SKILL.md +58 -0
  128. package/.aether/skills/domain/vue/SKILL.md +49 -0
  129. package/.aether/templates/QUEEN.md.template +23 -41
  130. package/.aether/templates/colony-state-reset.jq.template +1 -0
  131. package/.aether/templates/colony-state.template.json +4 -0
  132. package/.aether/templates/learning-observations.template.json +6 -0
  133. package/.aether/templates/midden.template.json +13 -0
  134. package/.aether/templates/pheromones.template.json +6 -0
  135. package/.aether/templates/session.template.json +9 -0
  136. package/.aether/utils/atomic-write.sh +63 -17
  137. package/.aether/utils/chamber-utils.sh +145 -2
  138. package/.aether/utils/emoji-audit.sh +166 -0
  139. package/.aether/utils/error-handler.sh +21 -7
  140. package/.aether/utils/file-lock.sh +182 -27
  141. package/.aether/utils/flag.sh +267 -0
  142. package/.aether/utils/hive.sh +572 -0
  143. package/.aether/utils/learning.sh +1928 -0
  144. package/.aether/utils/midden.sh +342 -0
  145. package/.aether/utils/oracle/oracle.md +168 -0
  146. package/.aether/utils/oracle/oracle.sh +1023 -0
  147. package/.aether/utils/pheromone.sh +2029 -0
  148. package/.aether/utils/queen.sh +1698 -0
  149. package/.aether/utils/scan.sh +860 -0
  150. package/.aether/utils/semantic-cli.sh +10 -8
  151. package/.aether/utils/session.sh +552 -0
  152. package/.aether/utils/skills.sh +509 -0
  153. package/.aether/utils/spawn-tree.sh +103 -271
  154. package/.aether/utils/spawn.sh +260 -0
  155. package/.aether/utils/state-api.sh +199 -0
  156. package/.aether/utils/state-loader.sh +8 -6
  157. package/.aether/utils/suggest.sh +611 -0
  158. package/.aether/utils/swarm-display.sh +10 -1
  159. package/.aether/utils/swarm.sh +1004 -0
  160. package/.aether/utils/watch-spawn-tree.sh +11 -2
  161. package/.aether/utils/xml-compose.sh +2 -2
  162. package/.aether/utils/xml-convert.sh +9 -5
  163. package/.aether/utils/xml-core.sh +5 -9
  164. package/.aether/utils/xml-query.sh +4 -4
  165. package/.aether/workers.md +86 -67
  166. package/.claude/agents/ant/aether-ambassador.md +2 -1
  167. package/.claude/agents/ant/aether-archaeologist.md +6 -1
  168. package/.claude/agents/ant/aether-architect.md +236 -0
  169. package/.claude/agents/ant/aether-auditor.md +6 -1
  170. package/.claude/agents/ant/aether-builder.md +38 -1
  171. package/.claude/agents/ant/aether-chaos.md +2 -1
  172. package/.claude/agents/ant/aether-chronicler.md +1 -0
  173. package/.claude/agents/ant/aether-gatekeeper.md +6 -1
  174. package/.claude/agents/ant/aether-includer.md +1 -0
  175. package/.claude/agents/ant/aether-keeper.md +1 -0
  176. package/.claude/agents/ant/aether-measurer.md +6 -1
  177. package/.claude/agents/ant/aether-oracle.md +237 -0
  178. package/.claude/agents/ant/aether-probe.md +2 -1
  179. package/.claude/agents/ant/aether-queen.md +6 -1
  180. package/.claude/agents/ant/aether-route-setter.md +6 -1
  181. package/.claude/agents/ant/aether-sage.md +68 -3
  182. package/.claude/agents/ant/aether-scout.md +38 -1
  183. package/.claude/agents/ant/aether-surveyor-disciplines.md +2 -1
  184. package/.claude/agents/ant/aether-surveyor-nest.md +2 -1
  185. package/.claude/agents/ant/aether-surveyor-pathogens.md +2 -1
  186. package/.claude/agents/ant/aether-surveyor-provisions.md +2 -1
  187. package/.claude/agents/ant/aether-tracker.md +6 -1
  188. package/.claude/agents/ant/aether-watcher.md +37 -1
  189. package/.claude/agents/ant/aether-weaver.md +2 -1
  190. package/.claude/commands/ant/archaeology.md +1 -8
  191. package/.claude/commands/ant/build.md +43 -1159
  192. package/.claude/commands/ant/chaos.md +1 -14
  193. package/.claude/commands/ant/colonize.md +1 -14
  194. package/.claude/commands/ant/continue.md +40 -1026
  195. package/.claude/commands/ant/council.md +9 -16
  196. package/.claude/commands/ant/data-clean.md +81 -0
  197. package/.claude/commands/ant/dream.md +12 -9
  198. package/.claude/commands/ant/entomb.md +62 -87
  199. package/.claude/commands/ant/export-signals.md +57 -0
  200. package/.claude/commands/ant/feedback.md +18 -0
  201. package/.claude/commands/ant/flag.md +12 -0
  202. package/.claude/commands/ant/flags.md +22 -8
  203. package/.claude/commands/ant/focus.md +18 -0
  204. package/.claude/commands/ant/help.md +40 -8
  205. package/.claude/commands/ant/history.md +3 -0
  206. package/.claude/commands/ant/import-signals.md +71 -0
  207. package/.claude/commands/ant/init.md +316 -191
  208. package/.claude/commands/ant/insert-phase.md +101 -0
  209. package/.claude/commands/ant/interpret.md +11 -0
  210. package/.claude/commands/ant/lay-eggs.md +167 -158
  211. package/.claude/commands/ant/maturity.md +22 -11
  212. package/.claude/commands/ant/memory-details.md +77 -0
  213. package/.claude/commands/ant/migrate-state.md +6 -0
  214. package/.claude/commands/ant/oracle.md +317 -62
  215. package/.claude/commands/ant/organize.md +10 -5
  216. package/.claude/commands/ant/patrol.md +620 -0
  217. package/.claude/commands/ant/pause-colony.md +8 -22
  218. package/.claude/commands/ant/phase.md +26 -37
  219. package/.claude/commands/ant/pheromones.md +156 -0
  220. package/.claude/commands/ant/plan.md +175 -52
  221. package/.claude/commands/ant/preferences.md +65 -0
  222. package/.claude/commands/ant/redirect.md +18 -0
  223. package/.claude/commands/ant/resume-colony.md +34 -20
  224. package/.claude/commands/ant/resume.md +51 -7
  225. package/.claude/commands/ant/run.md +195 -0
  226. package/.claude/commands/ant/seal.md +497 -78
  227. package/.claude/commands/ant/skill-create.md +286 -0
  228. package/.claude/commands/ant/status.md +127 -1
  229. package/.claude/commands/ant/swarm.md +11 -23
  230. package/.claude/commands/ant/tunnels.md +1 -0
  231. package/.claude/commands/ant/update.md +58 -135
  232. package/.claude/commands/ant/verify-castes.md +90 -42
  233. package/.claude/commands/ant/watch.md +1 -0
  234. package/.opencode/agents/aether-ambassador.md +1 -1
  235. package/.opencode/agents/aether-architect.md +133 -0
  236. package/.opencode/agents/aether-builder.md +3 -3
  237. package/.opencode/agents/aether-oracle.md +137 -0
  238. package/.opencode/agents/aether-queen.md +1 -1
  239. package/.opencode/agents/aether-route-setter.md +1 -1
  240. package/.opencode/agents/aether-scout.md +1 -1
  241. package/.opencode/agents/aether-surveyor-disciplines.md +6 -1
  242. package/.opencode/agents/aether-surveyor-nest.md +6 -1
  243. package/.opencode/agents/aether-surveyor-pathogens.md +6 -1
  244. package/.opencode/agents/aether-surveyor-provisions.md +6 -1
  245. package/.opencode/agents/aether-tracker.md +1 -1
  246. package/.opencode/agents/aether-watcher.md +1 -1
  247. package/.opencode/agents/aether-weaver.md +1 -1
  248. package/.opencode/commands/ant/archaeology.md +7 -14
  249. package/.opencode/commands/ant/build.md +54 -88
  250. package/.opencode/commands/ant/chaos.md +7 -24
  251. package/.opencode/commands/ant/colonize.md +8 -17
  252. package/.opencode/commands/ant/continue.md +595 -66
  253. package/.opencode/commands/ant/council.md +11 -22
  254. package/.opencode/commands/ant/data-clean.md +77 -0
  255. package/.opencode/commands/ant/dream.md +15 -17
  256. package/.opencode/commands/ant/entomb.md +28 -18
  257. package/.opencode/commands/ant/export-signals.md +54 -0
  258. package/.opencode/commands/ant/feedback.md +24 -5
  259. package/.opencode/commands/ant/flag.md +16 -4
  260. package/.opencode/commands/ant/flags.md +24 -10
  261. package/.opencode/commands/ant/focus.md +22 -5
  262. package/.opencode/commands/ant/help.md +41 -8
  263. package/.opencode/commands/ant/history.md +9 -0
  264. package/.opencode/commands/ant/import-signals.md +68 -0
  265. package/.opencode/commands/ant/init.md +365 -156
  266. package/.opencode/commands/ant/insert-phase.md +107 -0
  267. package/.opencode/commands/ant/interpret.md +16 -0
  268. package/.opencode/commands/ant/lay-eggs.md +184 -112
  269. package/.opencode/commands/ant/maturity.md +18 -2
  270. package/.opencode/commands/ant/memory-details.md +83 -0
  271. package/.opencode/commands/ant/migrate-state.md +12 -0
  272. package/.opencode/commands/ant/oracle.md +322 -67
  273. package/.opencode/commands/ant/organize.md +14 -12
  274. package/.opencode/commands/ant/patrol.md +626 -0
  275. package/.opencode/commands/ant/pause-colony.md +12 -29
  276. package/.opencode/commands/ant/phase.md +30 -40
  277. package/.opencode/commands/ant/pheromones.md +162 -0
  278. package/.opencode/commands/ant/plan.md +184 -56
  279. package/.opencode/commands/ant/preferences.md +71 -0
  280. package/.opencode/commands/ant/redirect.md +22 -5
  281. package/.opencode/commands/ant/resume-colony.md +38 -27
  282. package/.opencode/commands/ant/resume.md +71 -20
  283. package/.opencode/commands/ant/run.md +201 -0
  284. package/.opencode/commands/ant/seal.md +230 -25
  285. package/.opencode/commands/ant/skill-create.md +63 -0
  286. package/.opencode/commands/ant/status.md +124 -31
  287. package/.opencode/commands/ant/swarm.md +3 -345
  288. package/.opencode/commands/ant/tunnels.md +3 -9
  289. package/.opencode/commands/ant/update.md +63 -127
  290. package/.opencode/commands/ant/verify-castes.md +96 -42
  291. package/.opencode/commands/ant/watch.md +7 -0
  292. package/CHANGELOG.md +278 -1
  293. package/README.md +188 -340
  294. package/bin/cli.js +236 -429
  295. package/bin/generate-commands.js +186 -0
  296. package/bin/generate-commands.sh +128 -89
  297. package/bin/lib/spawn-logger.js +0 -15
  298. package/bin/lib/update-transaction.js +285 -35
  299. package/bin/npx-install.js +178 -0
  300. package/bin/validate-package.sh +85 -3
  301. package/package.json +7 -3
  302. package/.aether/CONTEXT.md +0 -160
  303. package/.aether/docs/QUEEN.md +0 -84
  304. package/.aether/exchange/colony-registry.xml +0 -11
  305. package/.aether/exchange/pheromones.xml +0 -87
  306. package/.aether/exchange/queen-wisdom.xml +0 -14
  307. package/.aether/model-profiles.yaml +0 -100
  308. package/.aether/utils/spawn-with-model.sh +0 -56
  309. package/bin/lib/model-profiles.js +0 -445
  310. package/bin/lib/model-verify.js +0 -288
  311. package/bin/lib/proxy-health.js +0 -253
  312. package/bin/lib/telemetry.js +0 -441
@@ -0,0 +1,260 @@
1
+ #!/bin/bash
2
+ # Spawn utility functions — extracted from aether-utils.sh
3
+ # Provides: _spawn_log, _spawn_complete, _spawn_can_spawn, _spawn_get_depth, _spawn_can_spawn_swarm, _spawn_tree_load, _spawn_tree_active, _spawn_tree_depth, _spawn_efficiency
4
+ #
5
+ # These functions are sourced by aether-utils.sh at startup.
6
+ # All shared infrastructure (json_ok, json_err, json_warn, atomic_write, acquire_lock,
7
+ # release_lock, feature_enabled, LOCK_DIR, DATA_DIR, SCRIPT_DIR, error constants) is available.
8
+ # Note: get_caste_emoji is defined in the main file and available to this module at call time.
9
+
10
+ _spawn_log() {
11
+ # Usage: spawn-log <parent_id> <child_caste> <child_name> <task_summary> [model] [status]
12
+ parent_id="${1:-}"
13
+ child_caste="${2:-}"
14
+ child_name="${3:-}"
15
+ task_summary="${4:-}"
16
+ model="${5:-default}"
17
+ status="${6:-spawned}"
18
+ # Model slot resolution removed (archived: .aether/archive/model-routing/)
19
+ # Agent frontmatter model: fields handle routing natively via Claude Code.
20
+ [[ "$model" == "default" ]] && model="inherit"
21
+ [[ -z "$parent_id" || -z "$child_caste" || -z "$task_summary" ]] && json_err "$E_VALIDATION_FAILED" "Usage: spawn-log <parent_id> <child_caste> <child_name> <task_summary> [model] [status]"
22
+ mkdir -p "$COLONY_DATA_DIR"
23
+ ts=$(date -u +"%H:%M:%S")
24
+ ts_full=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
25
+ emoji=$(get_caste_emoji "$child_caste")
26
+ parent_emoji=$(get_caste_emoji "$parent_id")
27
+ # Log to activity log with spawn format, emojis, and model info
28
+ echo "[$ts] ⚡ SPAWN $parent_emoji $parent_id -> $emoji $child_name ($child_caste): $task_summary [model: $model]" >> "$COLONY_DATA_DIR/activity.log"
29
+ # Log to spawn tree file for visualization (NEW FORMAT: includes model field)
30
+ echo "$ts_full|$parent_id|$child_caste|$child_name|$task_summary|$model|$status" >> "$COLONY_DATA_DIR/spawn-tree.txt"
31
+ # Return emoji-formatted result for display (jq-safe: child_name may contain JSON-special chars)
32
+ json_ok "$(jq -n --arg msg "⚡ $emoji $child_name spawned" '$msg')"
33
+ }
34
+
35
+ _spawn_complete() {
36
+ # Migrated to state-api facade: uses _state_mutate for failed spawn event logging
37
+ # Usage: spawn-complete <ant_name> <status> [summary]
38
+ ant_name="${1:-}"
39
+ status="${2:-completed}"
40
+ summary="${3:-}"
41
+ [[ -z "$ant_name" ]] && json_err "$E_VALIDATION_FAILED" "Usage: spawn-complete <ant_name> <status> [summary]"
42
+ mkdir -p "$COLONY_DATA_DIR"
43
+ ts=$(date -u +"%H:%M:%S")
44
+ ts_full=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
45
+ emoji=$(get_caste_emoji "$ant_name")
46
+ status_icon="✅"
47
+ [[ "$status" == "failed" ]] && status_icon="❌"
48
+ [[ "$status" == "blocked" ]] && status_icon="🚫"
49
+ echo "[$ts] $status_icon $emoji $ant_name: $status${summary:+ - $summary}" >> "$COLONY_DATA_DIR/activity.log"
50
+ # Update spawn tree
51
+ echo "$ts_full|$ant_name|$status|$summary" >> "$COLONY_DATA_DIR/spawn-tree.txt"
52
+ # Log failed spawns to events array as pipe-delimited strings (matching template format)
53
+ if [[ "$status" == "failed" ]] || [[ "$status" == "error" ]]; then
54
+ if [[ -f "$DATA_DIR/COLONY_STATE.json" ]]; then
55
+ SC_EVENT="$ts_full|spawn_failed|$ant_name|${summary:-unknown}" \
56
+ _state_mutate '
57
+ .events += [env.SC_EVENT]
58
+ ' >/dev/null 2>&1 || _aether_log_error "Failed to log spawn failure to colony state"
59
+ fi
60
+ fi
61
+ # Return emoji-formatted result for display (jq-safe: ant_name/summary may contain JSON-special chars)
62
+ json_ok "$(jq -n --arg msg "$status_icon $emoji $ant_name: ${summary:-$status}" '$msg')"
63
+ }
64
+
65
+ _spawn_can_spawn() {
66
+ # Check if spawning is allowed at given depth
67
+ # Usage: spawn-can-spawn [depth] [--enforce]
68
+ # Returns: {can_spawn: bool, depth: N, max_spawns: N, current_total: N, global_cap: N}
69
+ # --enforce: fail with non-zero exit when spawning is not allowed
70
+ depth=""
71
+ enforce_mode=false
72
+ for arg in "$@"; do
73
+ case "$arg" in
74
+ --enforce) enforce_mode=true ;;
75
+ *)
76
+ if [[ -z "$depth" ]]; then
77
+ depth="$arg"
78
+ else
79
+ json_err "$E_VALIDATION_FAILED" "Usage: spawn-can-spawn [depth] [--enforce]"
80
+ fi
81
+ ;;
82
+ esac
83
+ done
84
+ [[ -z "$depth" ]] && depth=1
85
+ [[ "$depth" =~ ^[0-9]+$ ]] || json_err "$E_VALIDATION_FAILED" "Depth must be a non-negative integer" "{\"provided\":\"$depth\"}"
86
+
87
+ # Depth limits: 1→4 spawns, 2→2 spawns, 3+→0 spawns
88
+ if [[ $depth -eq 1 ]]; then
89
+ max_for_depth=4
90
+ elif [[ $depth -eq 2 ]]; then
91
+ max_for_depth=2
92
+ else
93
+ max_for_depth=0
94
+ fi
95
+
96
+ # Count current spawns in this session (from spawn-tree.txt)
97
+ current=0
98
+ if [[ -f "$COLONY_DATA_DIR/spawn-tree.txt" ]]; then
99
+ current=$(grep -c "|spawned$" "$COLONY_DATA_DIR/spawn-tree.txt" 2>/dev/null || echo 0) # SUPPRESS:OK -- read-default: count defaults to 0 if file missing
100
+ fi
101
+
102
+ # Global cap of 10 workers per phase
103
+ global_cap=10
104
+
105
+ # Can spawn if: depth < 3 AND under global cap
106
+ if [[ $depth -lt 3 && $current -lt $global_cap ]]; then
107
+ can="true"
108
+ else
109
+ can="false"
110
+ fi
111
+
112
+ if [[ "$enforce_mode" == "true" && "$can" == "false" ]]; then
113
+ json_err "$E_VALIDATION_FAILED" "Spawn cap exceeded: depth=$depth current=$current max=$global_cap"
114
+ fi
115
+
116
+ json_ok "{\"can_spawn\":$can,\"depth\":$depth,\"max_spawns\":$max_for_depth,\"current_total\":$current,\"global_cap\":$global_cap}"
117
+ }
118
+
119
+ _spawn_get_depth() {
120
+ # Return depth for a given ant name by tracing spawn tree
121
+ # Usage: spawn-get-depth <ant_name>
122
+ # Queen = depth 0, Queen's spawns = depth 1, their spawns = depth 2, etc.
123
+ ant_name="${1:-Queen}"
124
+
125
+ if [[ "$ant_name" == "Queen" ]]; then
126
+ json_ok '{"ant":"Queen","depth":0}'
127
+ exit 0
128
+ fi
129
+
130
+ # Check if spawn tree exists
131
+ if [[ ! -f "$COLONY_DATA_DIR/spawn-tree.txt" ]]; then
132
+ json_ok "$(jq -n --arg ant "$ant_name" '{ant: $ant, depth: 1, found: false}')"
133
+ exit 0
134
+ fi
135
+
136
+ # Check if ant exists in spawn tree (gracefully handle missing ants)
137
+ if ! grep -qF "|$ant_name|" "$COLONY_DATA_DIR/spawn-tree.txt" 2>/dev/null; then # SUPPRESS:OK -- existence-test: file may not exist; -F: ant_name may contain regex metacharacters
138
+ json_ok "$(jq -n --arg ant "$ant_name" '{ant: $ant, depth: 1, found: false}')"
139
+ exit 0
140
+ fi
141
+
142
+ # Find the spawn record for this ant and trace parents
143
+ depth=1
144
+ current_ant="$ant_name"
145
+
146
+ # Find who spawned this ant (look for lines with |spawned)
147
+ while true; do
148
+ # Format: timestamp|parent|caste|child_name|task|spawned
149
+ # SUPPRESS:OK -- read-default: returns fallback on failure
150
+ parent=$(grep -F "|$current_ant|" "$COLONY_DATA_DIR/spawn-tree.txt" 2>/dev/null | grep "|spawned$" | head -1 | cut -d'|' -f2 || echo "")
151
+
152
+ if [[ -z "$parent" || "$parent" == "Queen" ]]; then
153
+ break
154
+ fi
155
+
156
+ depth=$((depth + 1))
157
+ current_ant="$parent"
158
+
159
+ # Safety limit
160
+ if [[ $depth -gt 5 ]]; then
161
+ break
162
+ fi
163
+ done
164
+
165
+ json_ok "$(jq -n --arg ant "$ant_name" --argjson depth "$depth" '{ant: $ant, depth: $depth, found: true}')"
166
+ }
167
+
168
+ _spawn_can_spawn_swarm() {
169
+ # Check if swarm can spawn more scouts (separate from phase workers)
170
+ # Usage: spawn-can-spawn-swarm <swarm_id>
171
+ # Swarm has its own cap of 6 (4 scouts + 2 sub-scouts max)
172
+ swarm_id="${1:-swarm}"
173
+ swarm_cap=6
174
+
175
+ current=0
176
+ if [[ -f "$COLONY_DATA_DIR/spawn-tree.txt" ]]; then
177
+ # SUPPRESS:OK -- existence-test: grep returns 1 when no matches
178
+ # -F: swarm_id may contain regex metacharacters; anchor $ dropped (swarm_id is unique, no substring collision risk)
179
+ current=$(grep -cF "|swarm:$swarm_id" "$COLONY_DATA_DIR/spawn-tree.txt" 2>/dev/null) || current=0
180
+ fi
181
+
182
+ if [[ $current -lt $swarm_cap ]]; then
183
+ can="true"
184
+ remaining=$((swarm_cap - current))
185
+ else
186
+ can="false"
187
+ remaining=0
188
+ fi
189
+
190
+ json_ok "$(jq -n --argjson can_spawn "$can" --argjson current "$current" \
191
+ --argjson cap "$swarm_cap" --argjson remaining "$remaining" --arg swarm_id "$swarm_id" \
192
+ '{can_spawn: $can_spawn, current: $current, cap: $cap, remaining: $remaining, swarm_id: $swarm_id}')"
193
+ }
194
+
195
+ _spawn_tree_load() {
196
+ source "$SCRIPT_DIR/utils/spawn-tree.sh" 2>/dev/null || { # SUPPRESS:OK -- read-default: utility may not be installed
197
+ json_err "$E_FILE_NOT_FOUND" "spawn-tree.sh not found"
198
+ exit 1
199
+ }
200
+ tree_json=$(reconstruct_tree_json)
201
+ if echo "$tree_json" | jq -e . >/dev/null 2>&1; then
202
+ json_ok "$tree_json"
203
+ else
204
+ json_err "$E_VALIDATION_FAILED" "spawn tree reconstruction produced invalid JSON"
205
+ return 1
206
+ fi
207
+ }
208
+
209
+ _spawn_tree_active() {
210
+ source "$SCRIPT_DIR/utils/spawn-tree.sh" 2>/dev/null || { # SUPPRESS:OK -- read-default: utility may not be installed
211
+ json_err "$E_FILE_NOT_FOUND" "spawn-tree.sh not found"
212
+ exit 1
213
+ }
214
+ active=$(get_active_spawns)
215
+ if echo "$active" | jq -e . >/dev/null 2>&1; then
216
+ json_ok "$active"
217
+ else
218
+ json_err "$E_VALIDATION_FAILED" "spawn-tree active produced invalid JSON"
219
+ return 1
220
+ fi
221
+ }
222
+
223
+ _spawn_tree_depth() {
224
+ ant_name="${1:-}"
225
+ [[ -z "$ant_name" ]] && json_err "$E_VALIDATION_FAILED" "Usage: spawn-tree-depth <ant_name>"
226
+ source "$SCRIPT_DIR/utils/spawn-tree.sh" 2>/dev/null || { # SUPPRESS:OK -- read-default: utility may not be installed
227
+ json_err "$E_FILE_NOT_FOUND" "spawn-tree.sh not found"
228
+ exit 1
229
+ }
230
+ depth=$(get_spawn_depth "$ant_name")
231
+ if echo "$depth" | jq -e . >/dev/null 2>&1; then
232
+ json_ok "$depth"
233
+ else
234
+ json_err "$E_VALIDATION_FAILED" "spawn-tree depth produced invalid JSON"
235
+ return 1
236
+ fi
237
+ }
238
+
239
+ _spawn_efficiency() {
240
+ # Calculate spawn efficiency metrics from spawn-tree.txt
241
+ # Usage: spawn-efficiency
242
+ spawn_tree_file="$COLONY_DATA_DIR/spawn-tree.txt"
243
+ total=0
244
+ completed=0
245
+ failed=0
246
+
247
+ if [[ -f "$spawn_tree_file" ]]; then
248
+ total=$(grep -c "|spawned$" "$spawn_tree_file" 2>/dev/null || echo 0) # SUPPRESS:OK -- read-default: count defaults to 0 if file missing
249
+ completed=$(grep -c "|completed$" "$spawn_tree_file" 2>/dev/null || echo 0) # SUPPRESS:OK -- read-default: count defaults to 0 if file missing
250
+ failed=$(grep -c "|failed$" "$spawn_tree_file" 2>/dev/null || echo 0) # SUPPRESS:OK -- read-default: count defaults to 0 if file missing
251
+ fi
252
+
253
+ if [[ "$total" -gt 0 ]]; then
254
+ efficiency=$(( completed * 100 / total ))
255
+ else
256
+ efficiency=0
257
+ fi
258
+
259
+ json_ok "{\"total\":$total,\"completed\":$completed,\"failed\":$failed,\"efficiency_pct\":$efficiency}"
260
+ }
@@ -0,0 +1,199 @@
1
+ #!/bin/bash
2
+ # State API facade -- centralized COLONY_STATE.json access
3
+ # Provides: _state_read, _state_write, _state_read_field, _state_mutate, _state_migrate
4
+ #
5
+ # These functions are sourced by aether-utils.sh at startup.
6
+ # All shared infrastructure (json_ok, json_err, atomic_write, acquire_lock,
7
+ # release_lock, LOCK_DIR, DATA_DIR, SCRIPT_DIR, error constants) is available.
8
+
9
+ _state_read() {
10
+ # Read full COLONY_STATE.json and return via json_ok
11
+ # Usage: state-read
12
+ # No lock needed for reads (jq is atomic on single files)
13
+ # Returns: json_ok with full state, or json_err on missing/invalid file
14
+
15
+ sr_state_file="$DATA_DIR/COLONY_STATE.json"
16
+
17
+ if [[ ! -f "$sr_state_file" ]]; then
18
+ json_err "$E_FILE_NOT_FOUND" "COLONY_STATE.json not found" '{"file":"COLONY_STATE.json"}'
19
+ fi
20
+
21
+ sr_content=$(cat "$sr_state_file" 2>/dev/null) || {
22
+ json_err "$E_FILE_NOT_FOUND" "Failed to read COLONY_STATE.json"
23
+ }
24
+
25
+ if ! echo "$sr_content" | jq -e . >/dev/null 2>&1; then
26
+ json_err "$E_JSON_INVALID" "COLONY_STATE.json contains invalid JSON"
27
+ fi
28
+
29
+ json_ok "$sr_content"
30
+ }
31
+
32
+ _state_read_field() {
33
+ # Read a specific jq field path from COLONY_STATE.json
34
+ # Usage: state-read-field <jq_path>
35
+ # For internal callers: outputs raw value to stdout (no json_ok wrapper)
36
+ # For subcommand entry: case block wraps in json_ok
37
+ # Returns empty string + exit 0 for missing field (callers check emptiness)
38
+
39
+ srf_field="${1:-}"
40
+
41
+ if [[ -z "$srf_field" ]]; then
42
+ json_err "$E_VALIDATION_FAILED" "state-read-field requires a jq field path argument"
43
+ fi
44
+
45
+ srf_state_file="$DATA_DIR/COLONY_STATE.json"
46
+
47
+ if [[ ! -f "$srf_state_file" ]]; then
48
+ json_err "$E_FILE_NOT_FOUND" "COLONY_STATE.json not found" '{"file":"COLONY_STATE.json"}'
49
+ fi
50
+
51
+ # Extract the field value (raw output, no quotes around strings)
52
+ srf_value=$(jq -r "$srf_field // empty" "$srf_state_file" 2>/dev/null) || srf_value=""
53
+
54
+ echo "$srf_value"
55
+ }
56
+
57
+ _state_write() {
58
+ # Write COLONY_STATE.json through a locked, validated, atomic path
59
+ # Usage: state-write '<json>'
60
+ # or: cat state.json | state-write
61
+ # Refactored from inline state-write case block for reuse
62
+ # Validates JSON, acquires lock, creates backup, writes atomically
63
+
64
+ sw_content="${1:-}"
65
+ if [[ -z "$sw_content" ]]; then
66
+ sw_content=$(cat)
67
+ fi
68
+
69
+ # Validate JSON
70
+ if ! echo "$sw_content" | jq -e . >/dev/null 2>&1; then # SUPPRESS:OK -- validation: testing JSON validity
71
+ json_err "$E_JSON_INVALID" "state-write received invalid JSON"
72
+ fi
73
+
74
+ sw_state_file="$DATA_DIR/COLONY_STATE.json"
75
+
76
+ # Acquire lock (colony-level, not hub-level)
77
+ acquire_lock "$sw_state_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on COLONY_STATE.json"
78
+
79
+ # Create backup before writing
80
+ if [[ -f "$sw_state_file" ]]; then
81
+ if ! create_backup "$sw_state_file"; then
82
+ _aether_log_error "Could not create backup of colony state before writing"
83
+ fi
84
+ fi
85
+
86
+ # Write atomically; release lock on failure
87
+ atomic_write "$sw_state_file" "$sw_content" || {
88
+ release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
89
+ json_err "$E_UNKNOWN" "Failed to write COLONY_STATE.json"
90
+ }
91
+ release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
92
+
93
+ json_ok '{"written":true}'
94
+ }
95
+
96
+ _state_mutate() {
97
+ # Read-modify-write COLONY_STATE.json with a jq expression
98
+ # Usage: state-mutate '<jq_expression>'
99
+ # Acquires lock, creates backup, applies jq, validates, writes atomically
100
+ # Returns: json_ok with mutated:true, or json_err on failure
101
+
102
+ sm_expr="${1:-}"
103
+
104
+ if [[ -z "$sm_expr" ]]; then
105
+ json_err "$E_VALIDATION_FAILED" "state-mutate requires a jq expression argument"
106
+ fi
107
+
108
+ sm_state_file="$DATA_DIR/COLONY_STATE.json"
109
+
110
+ if [[ ! -f "$sm_state_file" ]]; then
111
+ json_err "$E_FILE_NOT_FOUND" "COLONY_STATE.json not found" '{"file":"COLONY_STATE.json"}'
112
+ fi
113
+
114
+ # Acquire lock for safe read-modify-write
115
+ acquire_lock "$sm_state_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on COLONY_STATE.json"
116
+
117
+ # Create backup before mutation
118
+ if type create_backup &>/dev/null; then
119
+ if ! create_backup "$sm_state_file"; then
120
+ _aether_log_error "Could not create backup of colony state before mutation"
121
+ fi
122
+ fi
123
+
124
+ # Apply jq expression to current state
125
+ sm_updated=$(jq "$sm_expr" "$sm_state_file" 2>/dev/null) || {
126
+ release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
127
+ json_err "$E_JSON_INVALID" "jq expression failed: $sm_expr"
128
+ }
129
+
130
+ # Validate the result is valid JSON
131
+ if [[ -z "$sm_updated" ]] || ! echo "$sm_updated" | jq -e . >/dev/null 2>&1; then
132
+ release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
133
+ json_err "$E_JSON_INVALID" "state-mutate produced invalid JSON"
134
+ fi
135
+
136
+ # Write atomically
137
+ atomic_write "$sm_state_file" "$sm_updated" || {
138
+ release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
139
+ json_err "$E_UNKNOWN" "Failed to write mutated COLONY_STATE.json"
140
+ }
141
+
142
+ release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
143
+
144
+ json_ok '{"mutated":true}'
145
+ }
146
+
147
+ _state_migrate() {
148
+ # Schema migration helper: auto-upgrades pre-3.0 state files to v3.0
149
+ # Additive only (never removes fields) -- idempotent and safe for concurrent access
150
+ # Moved from validate-state case block for reuse
151
+
152
+ sm_state_file="${1:-}"
153
+ [[ -f "$sm_state_file" ]] || return 0
154
+
155
+ # First: verify file is parseable JSON at all
156
+ if ! jq -e . "$sm_state_file" >/dev/null 2>&1; then # SUPPRESS:OK -- validation: testing JSON validity
157
+ # Corrupt state file -- backup and error
158
+ if type create_backup &>/dev/null; then
159
+ if ! create_backup "$sm_state_file"; then
160
+ _aether_log_error "Could not create backup of corrupted COLONY_STATE.json"
161
+ fi
162
+ fi
163
+ json_err "$E_JSON_INVALID" \
164
+ "COLONY_STATE.json is corrupted (invalid JSON). A backup was saved in .aether/data/backups/. Try: run /ant:init to reset colony state."
165
+ fi
166
+
167
+ sm_current_version=$(jq -r '.version // "1.0"' "$sm_state_file" 2>/dev/null) # SUPPRESS:OK -- read-default: file may not exist yet
168
+
169
+ if [[ "$sm_current_version" != "3.0" ]]; then
170
+ sm_lock_held=false
171
+ # Skip lock acquisition when caller already holds the state lock
172
+ if [[ "${AETHER_STATE_LOCKED:-false}" != "true" ]] && type acquire_lock &>/dev/null; then
173
+ acquire_lock "$sm_state_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on COLONY_STATE.json for migration"
174
+ sm_lock_held=true
175
+ fi
176
+
177
+ # Add missing v3.0 fields (additive only -- idempotent and safe for concurrent access)
178
+ sm_updated=$(jq '
179
+ .version = "3.0" |
180
+ if .signals == null then .signals = [] else . end |
181
+ if .graveyards == null then .graveyards = [] else . end |
182
+ if .events == null then .events = [] else . end
183
+ ' "$sm_state_file" 2>/dev/null) || { # SUPPRESS:OK -- read-default: file may not exist yet
184
+ [[ "$sm_lock_held" == "true" ]] && release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
185
+ json_err "$E_JSON_INVALID" "Failed to migrate COLONY_STATE.json"
186
+ }
187
+
188
+ if [[ -n "$sm_updated" ]]; then
189
+ atomic_write "$sm_state_file" "$sm_updated" || {
190
+ [[ "$sm_lock_held" == "true" ]] && release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
191
+ json_err "$E_JSON_INVALID" "Failed to write migrated COLONY_STATE.json"
192
+ }
193
+ # Notify user of migration (auto-migrate + notify pattern)
194
+ printf '{"ok":true,"warning":"W_MIGRATED","message":"Migrated colony state from v%s to v3.0"}\n' "$sm_current_version" >&2
195
+ fi
196
+
197
+ [[ "$sm_lock_held" == "true" ]] && release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
198
+ fi
199
+ }
@@ -10,11 +10,13 @@
10
10
  #
11
11
  # Provides: load_colony_state, unload_colony_state, get_handoff_summary, display_resumption_context
12
12
 
13
- # Aether root detection - use git root if available, otherwise use current directory
14
- if git rev-parse --show-toplevel >/dev/null 2>&1; then
15
- AETHER_ROOT="$(git rev-parse --show-toplevel)"
16
- else
17
- AETHER_ROOT="$(pwd)"
13
+ # Aether root detection - respect existing AETHER_ROOT, or use git root, or use current directory
14
+ if [[ -z "${AETHER_ROOT:-}" ]]; then
15
+ if git rev-parse --show-toplevel >/dev/null 2>&1; then
16
+ AETHER_ROOT="$(git rev-parse --show-toplevel)"
17
+ else
18
+ AETHER_ROOT="$(pwd)"
19
+ fi
18
20
  fi
19
21
 
20
22
  SCRIPT_DIR="${SCRIPT_DIR:-$AETHER_ROOT/.aether}"
@@ -70,7 +72,7 @@ load_colony_state() {
70
72
 
71
73
  # Validate state before loading
72
74
  local validation
73
- validation=$(bash "$SCRIPT_DIR/aether-utils.sh" validate-state colony 2>/dev/null)
75
+ validation=$(AETHER_STATE_LOCKED=true bash "$SCRIPT_DIR/aether-utils.sh" validate-state colony 2>/dev/null)
74
76
  if ! echo "$validation" | jq -e '.result.pass' >/dev/null 2>&1; then
75
77
  # Validation failed - release lock and report error
76
78
  if type release_lock &>/dev/null; then