aether-colony 5.0.0 → 5.2.1

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 (317) hide show
  1. package/.aether/aether-utils.sh +3226 -3345
  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 +442 -0
  30. package/.aether/commands/continue.yaml +1484 -0
  31. package/.aether/commands/council.yaml +509 -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 +502 -0
  44. package/.aether/commands/insert-phase.yaml +102 -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 +1364 -0
  57. package/.aether/commands/preferences.yaml +63 -0
  58. package/.aether/commands/quick.yaml +104 -0
  59. package/.aether/commands/redirect.yaml +123 -0
  60. package/.aether/commands/resume-colony.yaml +375 -0
  61. package/.aether/commands/resume.yaml +407 -0
  62. package/.aether/commands/run.yaml +229 -0
  63. package/.aether/commands/seal.yaml +1214 -0
  64. package/.aether/commands/skill-create.yaml +337 -0
  65. package/.aether/commands/status.yaml +408 -0
  66. package/.aether/commands/swarm.yaml +352 -0
  67. package/.aether/commands/tunnels.yaml +814 -0
  68. package/.aether/commands/update.yaml +131 -0
  69. package/.aether/commands/verify-castes.yaml +159 -0
  70. package/.aether/commands/watch.yaml +454 -0
  71. package/.aether/docs/INCIDENT_TEMPLATE.md +32 -0
  72. package/.aether/docs/QUEEN-SYSTEM.md +11 -11
  73. package/.aether/docs/README.md +32 -2
  74. package/.aether/docs/command-playbooks/README.md +23 -0
  75. package/.aether/docs/command-playbooks/build-complete.md +349 -0
  76. package/.aether/docs/command-playbooks/build-context.md +282 -0
  77. package/.aether/docs/command-playbooks/build-full.md +1683 -0
  78. package/.aether/docs/command-playbooks/build-prep.md +284 -0
  79. package/.aether/docs/command-playbooks/build-verify.md +405 -0
  80. package/.aether/docs/command-playbooks/build-wave.md +749 -0
  81. package/.aether/docs/command-playbooks/continue-advance.md +524 -0
  82. package/.aether/docs/command-playbooks/continue-finalize.md +447 -0
  83. package/.aether/docs/command-playbooks/continue-full.md +1725 -0
  84. package/.aether/docs/command-playbooks/continue-gates.md +686 -0
  85. package/.aether/docs/command-playbooks/continue-verify.md +407 -0
  86. package/.aether/docs/context-continuity.md +84 -0
  87. package/.aether/docs/disciplines/DISCIPLINES.md +9 -7
  88. package/.aether/docs/error-codes.md +1 -1
  89. package/.aether/docs/known-issues.md +34 -173
  90. package/.aether/docs/pheromones.md +86 -6
  91. package/.aether/docs/plans/pheromone-display-plan.md +257 -0
  92. package/.aether/docs/queen-commands.md +10 -9
  93. package/.aether/docs/source-of-truth-map.md +132 -0
  94. package/.aether/docs/xml-utilities.md +47 -0
  95. package/.aether/rules/aether-colony.md +23 -13
  96. package/.aether/scripts/incident-test-add.sh +47 -0
  97. package/.aether/scripts/weekly-audit.sh +79 -0
  98. package/.aether/skills/.index.json +649 -0
  99. package/.aether/skills/colony/.manifest.json +16 -0
  100. package/.aether/skills/colony/build-discipline/SKILL.md +78 -0
  101. package/.aether/skills/colony/colony-interaction/SKILL.md +56 -0
  102. package/.aether/skills/colony/colony-lifecycle/SKILL.md +77 -0
  103. package/.aether/skills/colony/colony-visuals/SKILL.md +112 -0
  104. package/.aether/skills/colony/context-management/SKILL.md +80 -0
  105. package/.aether/skills/colony/error-presentation/SKILL.md +99 -0
  106. package/.aether/skills/colony/pheromone-protocol/SKILL.md +79 -0
  107. package/.aether/skills/colony/pheromone-visibility/SKILL.md +81 -0
  108. package/.aether/skills/colony/state-safety/SKILL.md +84 -0
  109. package/.aether/skills/colony/worker-priming/SKILL.md +82 -0
  110. package/.aether/skills/domain/.manifest.json +24 -0
  111. package/.aether/skills/domain/README.md +33 -0
  112. package/.aether/skills/domain/django/SKILL.md +49 -0
  113. package/.aether/skills/domain/docker/SKILL.md +52 -0
  114. package/.aether/skills/domain/golang/SKILL.md +52 -0
  115. package/.aether/skills/domain/graphql/SKILL.md +51 -0
  116. package/.aether/skills/domain/html-css/SKILL.md +48 -0
  117. package/.aether/skills/domain/nextjs/SKILL.md +45 -0
  118. package/.aether/skills/domain/nodejs/SKILL.md +53 -0
  119. package/.aether/skills/domain/postgresql/SKILL.md +53 -0
  120. package/.aether/skills/domain/prisma/SKILL.md +59 -0
  121. package/.aether/skills/domain/python/SKILL.md +50 -0
  122. package/.aether/skills/domain/rails/SKILL.md +52 -0
  123. package/.aether/skills/domain/react/SKILL.md +45 -0
  124. package/.aether/skills/domain/rest-api/SKILL.md +58 -0
  125. package/.aether/skills/domain/svelte/SKILL.md +47 -0
  126. package/.aether/skills/domain/tailwind/SKILL.md +45 -0
  127. package/.aether/skills/domain/testing/SKILL.md +53 -0
  128. package/.aether/skills/domain/typescript/SKILL.md +58 -0
  129. package/.aether/skills/domain/vue/SKILL.md +49 -0
  130. package/.aether/templates/QUEEN.md.template +23 -41
  131. package/.aether/templates/colony-state-reset.jq.template +1 -0
  132. package/.aether/templates/colony-state.template.json +4 -0
  133. package/.aether/templates/learning-observations.template.json +6 -0
  134. package/.aether/templates/midden.template.json +13 -0
  135. package/.aether/templates/pheromones.template.json +6 -0
  136. package/.aether/templates/session.template.json +9 -0
  137. package/.aether/utils/atomic-write.sh +63 -17
  138. package/.aether/utils/chamber-utils.sh +145 -2
  139. package/.aether/utils/council.sh +425 -0
  140. package/.aether/utils/emoji-audit.sh +166 -0
  141. package/.aether/utils/error-handler.sh +21 -7
  142. package/.aether/utils/file-lock.sh +182 -27
  143. package/.aether/utils/flag.sh +278 -0
  144. package/.aether/utils/hive.sh +572 -0
  145. package/.aether/utils/immune.sh +508 -0
  146. package/.aether/utils/learning.sh +1928 -0
  147. package/.aether/utils/midden.sh +520 -0
  148. package/.aether/utils/oracle/oracle.md +168 -0
  149. package/.aether/utils/oracle/oracle.sh +1023 -0
  150. package/.aether/utils/pheromone.sh +2029 -0
  151. package/.aether/utils/queen.sh +1710 -0
  152. package/.aether/utils/scan.sh +860 -0
  153. package/.aether/utils/semantic-cli.sh +10 -8
  154. package/.aether/utils/session.sh +816 -0
  155. package/.aether/utils/skills.sh +509 -0
  156. package/.aether/utils/spawn-tree.sh +103 -271
  157. package/.aether/utils/spawn.sh +260 -0
  158. package/.aether/utils/state-api.sh +389 -0
  159. package/.aether/utils/state-loader.sh +8 -6
  160. package/.aether/utils/suggest.sh +611 -0
  161. package/.aether/utils/swarm-display.sh +10 -1
  162. package/.aether/utils/swarm.sh +1004 -0
  163. package/.aether/utils/watch-spawn-tree.sh +11 -2
  164. package/.aether/utils/xml-compose.sh +2 -2
  165. package/.aether/utils/xml-convert.sh +9 -5
  166. package/.aether/utils/xml-core.sh +5 -9
  167. package/.aether/utils/xml-query.sh +4 -4
  168. package/.aether/workers.md +86 -67
  169. package/.claude/agents/ant/aether-ambassador.md +2 -1
  170. package/.claude/agents/ant/aether-archaeologist.md +6 -1
  171. package/.claude/agents/ant/aether-architect.md +236 -0
  172. package/.claude/agents/ant/aether-auditor.md +6 -1
  173. package/.claude/agents/ant/aether-builder.md +38 -1
  174. package/.claude/agents/ant/aether-chaos.md +2 -1
  175. package/.claude/agents/ant/aether-chronicler.md +1 -0
  176. package/.claude/agents/ant/aether-gatekeeper.md +6 -1
  177. package/.claude/agents/ant/aether-includer.md +1 -0
  178. package/.claude/agents/ant/aether-keeper.md +1 -0
  179. package/.claude/agents/ant/aether-measurer.md +6 -1
  180. package/.claude/agents/ant/aether-oracle.md +237 -0
  181. package/.claude/agents/ant/aether-probe.md +2 -1
  182. package/.claude/agents/ant/aether-queen.md +6 -1
  183. package/.claude/agents/ant/aether-route-setter.md +6 -1
  184. package/.claude/agents/ant/aether-sage.md +68 -3
  185. package/.claude/agents/ant/aether-scout.md +38 -1
  186. package/.claude/agents/ant/aether-surveyor-disciplines.md +2 -1
  187. package/.claude/agents/ant/aether-surveyor-nest.md +2 -1
  188. package/.claude/agents/ant/aether-surveyor-pathogens.md +2 -1
  189. package/.claude/agents/ant/aether-surveyor-provisions.md +2 -1
  190. package/.claude/agents/ant/aether-tracker.md +6 -1
  191. package/.claude/agents/ant/aether-watcher.md +37 -1
  192. package/.claude/agents/ant/aether-weaver.md +2 -1
  193. package/.claude/commands/ant/archaeology.md +1 -8
  194. package/.claude/commands/ant/build.md +43 -1159
  195. package/.claude/commands/ant/chaos.md +1 -14
  196. package/.claude/commands/ant/colonize.md +3 -14
  197. package/.claude/commands/ant/continue.md +40 -1026
  198. package/.claude/commands/ant/council.md +213 -15
  199. package/.claude/commands/ant/data-clean.md +81 -0
  200. package/.claude/commands/ant/dream.md +12 -9
  201. package/.claude/commands/ant/entomb.md +62 -87
  202. package/.claude/commands/ant/export-signals.md +57 -0
  203. package/.claude/commands/ant/feedback.md +18 -0
  204. package/.claude/commands/ant/flag.md +12 -0
  205. package/.claude/commands/ant/flags.md +22 -8
  206. package/.claude/commands/ant/focus.md +18 -0
  207. package/.claude/commands/ant/help.md +40 -8
  208. package/.claude/commands/ant/history.md +3 -0
  209. package/.claude/commands/ant/import-signals.md +71 -0
  210. package/.claude/commands/ant/init.md +349 -191
  211. package/.claude/commands/ant/insert-phase.md +105 -0
  212. package/.claude/commands/ant/interpret.md +11 -0
  213. package/.claude/commands/ant/lay-eggs.md +167 -158
  214. package/.claude/commands/ant/maturity.md +22 -11
  215. package/.claude/commands/ant/memory-details.md +77 -0
  216. package/.claude/commands/ant/migrate-state.md +6 -0
  217. package/.claude/commands/ant/oracle.md +317 -62
  218. package/.claude/commands/ant/organize.md +10 -5
  219. package/.claude/commands/ant/patrol.md +620 -0
  220. package/.claude/commands/ant/pause-colony.md +8 -22
  221. package/.claude/commands/ant/phase.md +26 -37
  222. package/.claude/commands/ant/pheromones.md +156 -0
  223. package/.claude/commands/ant/plan.md +199 -50
  224. package/.claude/commands/ant/preferences.md +65 -0
  225. package/.claude/commands/ant/quick.md +100 -0
  226. package/.claude/commands/ant/redirect.md +18 -0
  227. package/.claude/commands/ant/resume-colony.md +37 -22
  228. package/.claude/commands/ant/resume.md +60 -7
  229. package/.claude/commands/ant/run.md +231 -0
  230. package/.claude/commands/ant/seal.md +506 -78
  231. package/.claude/commands/ant/skill-create.md +286 -0
  232. package/.claude/commands/ant/status.md +171 -1
  233. package/.claude/commands/ant/swarm.md +11 -23
  234. package/.claude/commands/ant/tunnels.md +1 -0
  235. package/.claude/commands/ant/update.md +58 -135
  236. package/.claude/commands/ant/verify-castes.md +90 -42
  237. package/.claude/commands/ant/watch.md +1 -0
  238. package/.opencode/agents/aether-ambassador.md +1 -1
  239. package/.opencode/agents/aether-architect.md +133 -0
  240. package/.opencode/agents/aether-builder.md +3 -3
  241. package/.opencode/agents/aether-oracle.md +137 -0
  242. package/.opencode/agents/aether-queen.md +1 -1
  243. package/.opencode/agents/aether-route-setter.md +1 -1
  244. package/.opencode/agents/aether-scout.md +1 -1
  245. package/.opencode/agents/aether-surveyor-disciplines.md +6 -1
  246. package/.opencode/agents/aether-surveyor-nest.md +6 -1
  247. package/.opencode/agents/aether-surveyor-pathogens.md +6 -1
  248. package/.opencode/agents/aether-surveyor-provisions.md +6 -1
  249. package/.opencode/agents/aether-tracker.md +1 -1
  250. package/.opencode/agents/aether-watcher.md +1 -1
  251. package/.opencode/agents/aether-weaver.md +1 -1
  252. package/.opencode/commands/ant/archaeology.md +7 -14
  253. package/.opencode/commands/ant/build.md +54 -88
  254. package/.opencode/commands/ant/chaos.md +7 -24
  255. package/.opencode/commands/ant/colonize.md +10 -17
  256. package/.opencode/commands/ant/continue.md +595 -66
  257. package/.opencode/commands/ant/council.md +150 -18
  258. package/.opencode/commands/ant/data-clean.md +77 -0
  259. package/.opencode/commands/ant/dream.md +15 -17
  260. package/.opencode/commands/ant/entomb.md +28 -18
  261. package/.opencode/commands/ant/export-signals.md +54 -0
  262. package/.opencode/commands/ant/feedback.md +24 -5
  263. package/.opencode/commands/ant/flag.md +16 -4
  264. package/.opencode/commands/ant/flags.md +24 -10
  265. package/.opencode/commands/ant/focus.md +22 -5
  266. package/.opencode/commands/ant/help.md +41 -8
  267. package/.opencode/commands/ant/history.md +9 -0
  268. package/.opencode/commands/ant/import-signals.md +68 -0
  269. package/.opencode/commands/ant/init.md +396 -154
  270. package/.opencode/commands/ant/insert-phase.md +111 -0
  271. package/.opencode/commands/ant/interpret.md +16 -0
  272. package/.opencode/commands/ant/lay-eggs.md +184 -112
  273. package/.opencode/commands/ant/maturity.md +18 -2
  274. package/.opencode/commands/ant/memory-details.md +83 -0
  275. package/.opencode/commands/ant/migrate-state.md +12 -0
  276. package/.opencode/commands/ant/oracle.md +322 -67
  277. package/.opencode/commands/ant/organize.md +14 -12
  278. package/.opencode/commands/ant/patrol.md +626 -0
  279. package/.opencode/commands/ant/pause-colony.md +12 -29
  280. package/.opencode/commands/ant/phase.md +30 -40
  281. package/.opencode/commands/ant/pheromones.md +162 -0
  282. package/.opencode/commands/ant/plan.md +210 -57
  283. package/.opencode/commands/ant/preferences.md +71 -0
  284. package/.opencode/commands/ant/quick.md +91 -0
  285. package/.opencode/commands/ant/redirect.md +22 -5
  286. package/.opencode/commands/ant/resume-colony.md +41 -29
  287. package/.opencode/commands/ant/resume.md +80 -20
  288. package/.opencode/commands/ant/run.md +237 -0
  289. package/.opencode/commands/ant/seal.md +230 -25
  290. package/.opencode/commands/ant/skill-create.md +63 -0
  291. package/.opencode/commands/ant/status.md +125 -30
  292. package/.opencode/commands/ant/swarm.md +3 -345
  293. package/.opencode/commands/ant/tunnels.md +3 -9
  294. package/.opencode/commands/ant/update.md +63 -127
  295. package/.opencode/commands/ant/verify-castes.md +96 -42
  296. package/.opencode/commands/ant/watch.md +7 -0
  297. package/CHANGELOG.md +368 -1
  298. package/README.md +195 -324
  299. package/bin/cli.js +236 -429
  300. package/bin/generate-commands.js +186 -0
  301. package/bin/generate-commands.sh +128 -89
  302. package/bin/lib/spawn-logger.js +0 -15
  303. package/bin/lib/update-transaction.js +285 -35
  304. package/bin/npx-install.js +178 -0
  305. package/bin/validate-package.sh +85 -3
  306. package/package.json +16 -4
  307. package/.aether/CONTEXT.md +0 -160
  308. package/.aether/docs/QUEEN.md +0 -84
  309. package/.aether/exchange/colony-registry.xml +0 -11
  310. package/.aether/exchange/pheromones.xml +0 -87
  311. package/.aether/exchange/queen-wisdom.xml +0 -14
  312. package/.aether/model-profiles.yaml +0 -100
  313. package/.aether/utils/spawn-with-model.sh +0 -56
  314. package/bin/lib/model-profiles.js +0 -445
  315. package/bin/lib/model-verify.js +0 -288
  316. package/bin/lib/proxy-health.js +0 -253
  317. package/bin/lib/telemetry.js +0 -441
@@ -0,0 +1,6 @@
1
+ {
2
+ "_template": "learning-observations",
3
+ "_version": "1.0",
4
+ "_instructions": "Write to .aether/data/learning-observations.json. No substitution needed. Remove underscore-prefixed keys.",
5
+ "observations": []
6
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "_template": "midden",
3
+ "_version": "1.0",
4
+ "_instructions": "Write to .aether/data/midden/midden.json. No substitution needed. Remove underscore-prefixed keys.",
5
+ "version": "1.0.0",
6
+ "signals": [],
7
+ "spawn_metrics": {
8
+ "total_spawned": 0,
9
+ "completed": 0,
10
+ "failed": 0,
11
+ "efficiency_pct": 0
12
+ }
13
+ }
@@ -0,0 +1,6 @@
1
+ {
2
+ "_template": "pheromones",
3
+ "_version": "1.0",
4
+ "_instructions": "Write to .aether/data/pheromones.json. No substitution needed. Remove underscore-prefixed keys.",
5
+ "signals": []
6
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "_template": "session",
3
+ "_version": "1.0",
4
+ "_instructions": "Write to .aether/data/session.json. Replace __SESSION_ID__, __GOAL__, __TIMESTAMP__. Remove underscore-prefixed keys.",
5
+ "session_id": "__SESSION_ID__",
6
+ "colony_goal": "__GOAL__",
7
+ "started_at": "__TIMESTAMP__",
8
+ "last_activity": "__TIMESTAMP__"
9
+ }
@@ -25,11 +25,13 @@ if [ ! -f "$_AETHER_UTILS_DIR/file-lock.sh" ]; then
25
25
  fi
26
26
  source "$_AETHER_UTILS_DIR/file-lock.sh"
27
27
 
28
- # Aether root detection - use git root if available, otherwise use current directory
29
- if git rev-parse --show-toplevel >/dev/null 2>&1; then
30
- AETHER_ROOT="$(git rev-parse --show-toplevel)"
31
- else
32
- AETHER_ROOT="$(pwd)"
28
+ # Aether root detection - respect existing AETHER_ROOT, or use git root, or use current directory
29
+ if [[ -z "${AETHER_ROOT:-}" ]]; then
30
+ if git rev-parse --show-toplevel >/dev/null 2>&1; then
31
+ AETHER_ROOT="$(git rev-parse --show-toplevel)"
32
+ else
33
+ AETHER_ROOT="$(pwd)"
34
+ fi
33
35
  fi
34
36
 
35
37
  TEMP_DIR="$AETHER_ROOT/.aether/temp"
@@ -41,9 +43,44 @@ mkdir -p "$TEMP_DIR" "$BACKUP_DIR"
41
43
  # Number of backups to keep
42
44
  MAX_BACKUPS=3
43
45
 
46
+ # Safety stats file for tracking data safety events (best-effort, never fails operations)
47
+ SAFETY_STATS_FILE="${AETHER_ROOT}/.aether/data/safety-stats.json"
48
+
49
+ # Increment a safety stats counter (best-effort, never fails the calling operation)
50
+ # Arguments: counter_name (e.g., "stale_locks_cleaned", "json_validation_rejects")
51
+ _safety_stats_increment() {
52
+ local counter_name="$1"
53
+ local stats_file="$SAFETY_STATS_FILE"
54
+ local now_iso
55
+ now_iso=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "unknown")
56
+
57
+ # Ensure data directory exists
58
+ mkdir -p "$(dirname "$stats_file")" 2>/dev/null || return 0
59
+
60
+ # Initialize if missing
61
+ if [[ ! -f "$stats_file" ]]; then
62
+ printf '{"stale_locks_cleaned":0,"json_validation_rejects":0,"last_updated":"%s"}\n' "$now_iso" > "$stats_file" 2>/dev/null || return 0
63
+ fi
64
+
65
+ # Increment counter (best-effort, don't fail on jq errors)
66
+ local updated
67
+ updated=$(jq --arg key "$counter_name" --arg ts "$now_iso" '
68
+ .[$key] = ((.[$key] // 0) + 1) |
69
+ .last_updated = $ts
70
+ ' "$stats_file" 2>/dev/null) || return 0
71
+
72
+ if [[ -n "$updated" ]]; then
73
+ printf '%s\n' "$updated" > "$stats_file" 2>/dev/null || return 0
74
+ fi
75
+ }
76
+
44
77
  # Atomic write: write content to file via temporary file
45
78
  # Arguments: target_file, content
46
79
  # Returns: 0 on success, 1 on failure
80
+ # NOTE: atomic_write does NOT interact with file locks. Lock management
81
+ # (acquire_lock/release_lock) is the CALLER's responsibility. If you need
82
+ # exclusive access, acquire the lock before calling atomic_write, and release
83
+ # it after (including on error paths).
47
84
  atomic_write() {
48
85
  local target_file="$1"
49
86
  local content="$2"
@@ -53,10 +90,10 @@ atomic_write() {
53
90
  mkdir -p "$target_dir"
54
91
 
55
92
  # Create unique temp file
56
- local temp_file="${TEMP_DIR}/$(basename "$target_file").$$.$(date +%s%N).tmp"
93
+ local temp_file="${TEMP_DIR}/$(basename "$target_file").$$.$( date +%s )_${RANDOM}.tmp"
57
94
 
58
95
  # Write content to temp file
59
- if ! echo "$content" > "$temp_file"; then
96
+ if ! printf '%s\n' "$content" > "$temp_file"; then
60
97
  echo "Failed to write to temp file: $temp_file"
61
98
  rm -f "$temp_file"
62
99
  return 1
@@ -67,11 +104,12 @@ atomic_write() {
67
104
  create_backup "$target_file"
68
105
  fi
69
106
 
70
- # Validate JSON if it's a JSON file
107
+ # Validate JSON if it's a JSON file (lock management is caller's responsibility)
71
108
  if [[ "$target_file" == *.json ]]; then
72
- if ! python3 -c "import json; json.load(open('$temp_file'))" 2>/dev/null; then
109
+ if ! jq empty "$temp_file" 2>/dev/null; then
73
110
  echo "Invalid JSON in temp file: $temp_file"
74
111
  rm -f "$temp_file"
112
+ _safety_stats_increment "json_validation_rejects" 2>/dev/null || true
75
113
  return 1
76
114
  fi
77
115
  fi
@@ -108,7 +146,7 @@ atomic_write_from_file() {
108
146
  mkdir -p "$target_dir"
109
147
 
110
148
  # Create unique temp file
111
- local temp_file="${TEMP_DIR}/$(basename "$target_file").$$.$(date +%s%N).tmp"
149
+ local temp_file="${TEMP_DIR}/$(basename "$target_file").$$.$( date +%s )_${RANDOM}.tmp"
112
150
 
113
151
  # Copy source to temp
114
152
  if ! cp "$source_file" "$temp_file"; then
@@ -122,11 +160,12 @@ atomic_write_from_file() {
122
160
  create_backup "$target_file"
123
161
  fi
124
162
 
125
- # Validate JSON if it's a JSON file
163
+ # Validate JSON if it's a JSON file (lock management is caller's responsibility)
126
164
  if [[ "$target_file" == *.json ]]; then
127
- if ! python3 -c "import json; json.load(open('$temp_file'))" 2>/dev/null; then
165
+ if ! jq empty "$temp_file" 2>/dev/null; then
128
166
  echo "Invalid JSON in temp file: $temp_file"
129
167
  rm -f "$temp_file"
168
+ _safety_stats_increment "json_validation_rejects" 2>/dev/null || true
130
169
  return 1
131
170
  fi
132
171
  fi
@@ -166,10 +205,17 @@ create_backup() {
166
205
  # Arguments: base_name
167
206
  rotate_backups() {
168
207
  local base_name="$1"
169
- local backups=$(ls -t "${BACKUP_DIR}/${base_name}".*.backup 2>/dev/null | wc -l)
170
208
 
171
- if [ "$backups" -gt "$MAX_BACKUPS" ]; then
172
- ls -t "${BACKUP_DIR}/${base_name}".*.backup | tail -n +$((MAX_BACKUPS + 1)) | xargs rm -f
209
+ # Use find with -print0 for safe handling of paths with spaces
210
+ local backup_count
211
+ backup_count=$(find "$BACKUP_DIR" -maxdepth 1 -name "${base_name}.*.backup" -type f 2>/dev/null | wc -l | tr -d ' ')
212
+
213
+ if [ "$backup_count" -gt "$MAX_BACKUPS" ]; then
214
+ # Delete oldest backups beyond MAX_BACKUPS using find for space-safe handling
215
+ find "$BACKUP_DIR" -maxdepth 1 -name "${base_name}.*.backup" -type f -print0 2>/dev/null \
216
+ | xargs -0 ls -t 2>/dev/null \
217
+ | tail -n +$((MAX_BACKUPS + 1)) \
218
+ | while IFS= read -r file; do rm -f "$file" 2>/dev/null || true; done
173
219
  fi
174
220
  }
175
221
 
@@ -209,9 +255,9 @@ list_backups() {
209
255
 
210
256
  # Cleanup temp files older than 1 hour
211
257
  cleanup_temp_files() {
212
- find "$TEMP_DIR" -name "*.tmp" -mtime +1/24 -delete 2>/dev/null || true
258
+ find "$TEMP_DIR" -name "*.tmp" -mmin +60 -delete 2>/dev/null || true
213
259
  }
214
260
 
215
261
  # Export functions
216
262
  export -f atomic_write atomic_write_from_file create_backup rotate_backups
217
- export -f restore_backup list_backups cleanup_temp_files
263
+ export -f restore_backup list_backups cleanup_temp_files _safety_stats_increment
@@ -17,7 +17,10 @@ CURRENT_LOCK=${CURRENT_LOCK:-""}
17
17
 
18
18
  # Get script directory for sourcing (preserve parent SCRIPT_DIR if set)
19
19
  __chamber_utils_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20
- AETHER_ROOT="$(cd "$__chamber_utils_dir/../.." && pwd 2>/dev/null || echo "$__chamber_utils_dir/../..")"
20
+ # Respect existing AETHER_ROOT if already set
21
+ if [[ -z "${AETHER_ROOT:-}" ]]; then
22
+ AETHER_ROOT="$(cd "$__chamber_utils_dir/../.." && pwd 2>/dev/null || echo "$__chamber_utils_dir/../..")"
23
+ fi
21
24
 
22
25
  # Use parent SCRIPT_DIR if available, otherwise use local
23
26
  SCRIPT_DIR="${SCRIPT_DIR:-$__chamber_utils_dir}"
@@ -44,6 +47,7 @@ fi
44
47
  : "${E_FILE_NOT_FOUND:=E_FILE_NOT_FOUND}"
45
48
  : "${E_BASH_ERROR:=E_BASH_ERROR}"
46
49
  : "${E_JSON_INVALID:=E_JSON_INVALID}"
50
+ : "${E_FEATURE_UNAVAILABLE:=E_FEATURE_UNAVAILABLE}"
47
51
 
48
52
  # --- Chamber Functions ---
49
53
 
@@ -293,5 +297,144 @@ EOF
293
297
  json_ok "$sorted"
294
298
  }
295
299
 
300
+ # --- Colony Archive XML ---
301
+
302
+ # Export combined colony archive XML containing pheromones, wisdom, and registry
303
+ # Usage: _colony_archive_xml [output_file]
304
+ # Default output: .aether/exchange/colony-archive.xml
305
+ # Always filters to active-only pheromone signals
306
+ _colony_archive_xml() {
307
+ # Graceful degradation: check for xmllint
308
+ if ! command -v xmllint >/dev/null 2>&1; then
309
+ json_err "$E_FEATURE_UNAVAILABLE" "xmllint is not installed. Try: xcode-select --install on macOS."
310
+ fi
311
+
312
+ cax_output="${1:-$SCRIPT_DIR/exchange/colony-archive.xml}"
313
+ mkdir -p "$(dirname "$cax_output")"
314
+
315
+ # Step 1: Filter active-only pheromone signals to a temp file
316
+ cax_tmp_pheromones=$(mktemp)
317
+ if [[ -f "$COLONY_DATA_DIR/pheromones.json" ]]; then
318
+ jq '{
319
+ version: .version,
320
+ colony_id: .colony_id,
321
+ generated_at: .generated_at,
322
+ signals: [.signals[] | select(.active == true)]
323
+ }' "$COLONY_DATA_DIR/pheromones.json" > "$cax_tmp_pheromones" 2>/dev/null # SUPPRESS:OK -- read-default: file may not exist yet
324
+ else
325
+ printf '%s\n' '{"version":"1.0","colony_id":"unknown","generated_at":"","signals":[]}' > "$cax_tmp_pheromones"
326
+ fi
327
+
328
+ # Step 2: Export each section to temp XML files
329
+ cax_tmp_dir=$(mktemp -d)
330
+
331
+ # Pheromone section (using filtered active-only)
332
+ source "$SCRIPT_DIR/exchange/pheromone-xml.sh"
333
+ xml-pheromone-export "$cax_tmp_pheromones" "$cax_tmp_dir/pheromones.xml" 2>/dev/null || _aether_log_error "Could not export pheromones to XML"
334
+
335
+ # Wisdom section — reuse wisdom-export-xml fallback logic
336
+ source "$SCRIPT_DIR/exchange/wisdom-xml.sh"
337
+ cax_wisdom_input="$DATA_DIR/queen-wisdom.json"
338
+ if [[ ! -f "$cax_wisdom_input" ]]; then
339
+ # MIGRATE: direct COLONY_STATE.json access -- use _state_read_field instead
340
+ # Try extracting from COLONY_STATE.json memory field
341
+ if [[ -f "$DATA_DIR/COLONY_STATE.json" ]]; then
342
+ cax_wex_memory=$(jq '.memory // {}' "$DATA_DIR/COLONY_STATE.json" 2>/dev/null || echo '{}') # SUPPRESS:OK -- read-default: returns fallback if missing
343
+ if [[ "$cax_wex_memory" != "{}" && "$cax_wex_memory" != "null" ]]; then
344
+ cax_wex_created_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
345
+ cax_wisdom_input="$cax_tmp_dir/wisdom-input.json"
346
+ printf '%s\n' "{
347
+ \"version\": \"1.0.0\",
348
+ # SUPPRESS:OK -- read-default: query may return empty
349
+ \"metadata\": {\"created\": \"$cax_wex_created_at\", \"colony_id\": \"$(jq -r '.goal // \"unknown\"' "$DATA_DIR/COLONY_STATE.json" 2>/dev/null)\"},
350
+ \"philosophies\": [],
351
+ # SUPPRESS:OK -- read-default: query may return empty
352
+ \"patterns\": $(echo "$cax_wex_memory" | jq '[.instincts // [] | .[] | {"id": (. | @base64), "content": ., "confidence": 0.7, "domain": "general", "source": "colony_memory"}]' 2>/dev/null || echo '[]')
353
+ }" > "$cax_wisdom_input"
354
+ fi
355
+ fi
356
+ fi
357
+ if [[ -f "$cax_wisdom_input" ]]; then
358
+ xml-wisdom-export "$cax_wisdom_input" "$cax_tmp_dir/wisdom.xml" 2>/dev/null || _aether_log_error "Could not export wisdom to XML"
359
+ fi
360
+
361
+ # Registry section — reuse registry-export-xml on-demand generation logic
362
+ source "$SCRIPT_DIR/exchange/registry-xml.sh"
363
+ cax_registry_input="$DATA_DIR/colony-registry.json"
364
+ if [[ ! -f "$cax_registry_input" ]]; then
365
+ cax_rex_chambers_dir="$AETHER_ROOT/.aether/chambers"
366
+ cax_rex_generated_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
367
+ cax_rex_colonies="[]"
368
+ if [[ -d "$cax_rex_chambers_dir" ]]; then
369
+ cax_rex_colonies=$(
370
+ for manifest in "$cax_rex_chambers_dir"/*/manifest.json; do
371
+ [[ -f "$manifest" ]] || continue
372
+ jq -c '{
373
+ id: (.colony_id // .goal // "unknown"),
374
+ name: (.goal // "Unnamed Colony"),
375
+ created_at: (.created_at // "unknown"),
376
+ sealed_at: (.sealed_at // null),
377
+ status: (if .sealed_at then "sealed" else "active" end),
378
+ chamber: input_filename
379
+ }' "$manifest" 2>/dev/null || true # SUPPRESS:OK -- cleanup: operation is best-effort
380
+ done | jq -s '.' 2>/dev/null || echo '[]' # SUPPRESS:OK -- read-default: returns fallback if missing
381
+ )
382
+ fi
383
+ cax_registry_input="$cax_tmp_dir/registry-input.json"
384
+ printf '%s\n' "{
385
+ \"version\": \"1.0.0\",
386
+ \"generated_at\": \"$cax_rex_generated_at\",
387
+ \"colonies\": $cax_rex_colonies
388
+ }" > "$cax_registry_input"
389
+ fi
390
+ xml-registry-export "$cax_registry_input" "$cax_tmp_dir/registry.xml" 2>/dev/null || _aether_log_error "Could not export registry to XML"
391
+
392
+ # Step 3: Build combined XML
393
+ # SUPPRESS:OK -- read-default: query may return empty
394
+ cax_colony_id=$(jq -r '.goal // "unknown"' "$DATA_DIR/COLONY_STATE.json" 2>/dev/null | tr '[:upper:]' '[:lower:]' | tr -cs '[:alnum:]' '-' | sed 's/^-//;s/-$//')
395
+ [[ -z "$cax_colony_id" || "$cax_colony_id" == "unknown" ]] && cax_colony_id="unknown"
396
+ cax_sealed_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
397
+ cax_pheromone_count=$(jq '.signals | length' "$cax_tmp_pheromones" 2>/dev/null || echo 0) # SUPPRESS:OK -- read-default: file may not exist yet
398
+
399
+ {
400
+ printf '<?xml version="1.0" encoding="UTF-8"?>\n'
401
+ printf '<colony-archive\n'
402
+ printf ' xmlns="http://aether.colony/schemas/archive/1.0"\n'
403
+ printf ' colony_id="%s"\n' "$cax_colony_id"
404
+ printf ' sealed_at="%s"\n' "$cax_sealed_at"
405
+ printf ' version="1.0.0"\n'
406
+ printf ' pheromone_count="%s">\n' "$cax_pheromone_count"
407
+
408
+ # Append pheromone section (strip XML declaration)
409
+ if [[ -f "$cax_tmp_dir/pheromones.xml" ]]; then
410
+ sed '1{/^<?xml/d;}' "$cax_tmp_dir/pheromones.xml"
411
+ fi
412
+
413
+ # Append wisdom section (strip XML declaration)
414
+ if [[ -f "$cax_tmp_dir/wisdom.xml" ]]; then
415
+ sed '1{/^<?xml/d;}' "$cax_tmp_dir/wisdom.xml"
416
+ fi
417
+
418
+ # Append registry section (strip XML declaration)
419
+ if [[ -f "$cax_tmp_dir/registry.xml" ]]; then
420
+ sed '1{/^<?xml/d;}' "$cax_tmp_dir/registry.xml"
421
+ fi
422
+
423
+ printf '</colony-archive>\n'
424
+ } > "$cax_output"
425
+
426
+ # Step 4: Validate well-formedness
427
+ if xmllint --noout "$cax_output" 2>/dev/null; then # SUPPRESS:OK -- validation: testing XML validity
428
+ cax_valid=true
429
+ else
430
+ cax_valid=false
431
+ fi
432
+
433
+ # Step 5: Cleanup temp files
434
+ rm -rf "$cax_tmp_dir" "$cax_tmp_pheromones"
435
+
436
+ json_ok "$(jq -n --arg path "$cax_output" --argjson valid "$cax_valid" --arg colony_id "$cax_colony_id" --argjson pheromone_count "$cax_pheromone_count" '{path: $path, valid: $valid, colony_id: $colony_id, pheromone_count: $pheromone_count}')"
437
+ }
438
+
296
439
  # Export functions for use in other scripts
297
- export -f chamber_sanitize_goal chamber_compute_hash chamber_create chamber_verify chamber_list
440
+ export -f chamber_sanitize_goal chamber_compute_hash chamber_create chamber_verify chamber_list _colony_archive_xml