aether-colony 5.3.2 → 5.4.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 (281) hide show
  1. package/.aether/aether-utils.sh +181 -5
  2. package/.aether/commands/archaeology.yaml +3 -3
  3. package/.aether/commands/build.yaml +80 -45
  4. package/.aether/commands/chaos.yaml +7 -7
  5. package/.aether/commands/colonize.yaml +17 -17
  6. package/.aether/commands/continue.yaml +40 -40
  7. package/.aether/commands/council.yaml +6 -6
  8. package/.aether/commands/data-clean.yaml +3 -3
  9. package/.aether/commands/dream.yaml +2 -2
  10. package/.aether/commands/entomb.yaml +12 -12
  11. package/.aether/commands/export-signals.yaml +2 -2
  12. package/.aether/commands/feedback.yaml +6 -6
  13. package/.aether/commands/flag.yaml +2 -2
  14. package/.aether/commands/flags.yaml +4 -4
  15. package/.aether/commands/focus.yaml +6 -6
  16. package/.aether/commands/help.yaml +1 -1
  17. package/.aether/commands/history.yaml +1 -1
  18. package/.aether/commands/import-signals.yaml +2 -2
  19. package/.aether/commands/init.yaml +44 -27
  20. package/.aether/commands/insert-phase.yaml +1 -1
  21. package/.aether/commands/interpret.yaml +2 -2
  22. package/.aether/commands/lay-eggs.yaml +3 -3
  23. package/.aether/commands/maturity.yaml +2 -2
  24. package/.aether/commands/memory-details.yaml +1 -1
  25. package/.aether/commands/migrate-state.yaml +1 -1
  26. package/.aether/commands/oracle.yaml +147 -82
  27. package/.aether/commands/organize.yaml +5 -5
  28. package/.aether/commands/patrol.yaml +8 -8
  29. package/.aether/commands/pause-colony.yaml +7 -7
  30. package/.aether/commands/phase.yaml +1 -1
  31. package/.aether/commands/pheromones.yaml +1 -1
  32. package/.aether/commands/plan.yaml +14 -14
  33. package/.aether/commands/quick.yaml +4 -4
  34. package/.aether/commands/redirect.yaml +6 -6
  35. package/.aether/commands/resume-colony.yaml +9 -9
  36. package/.aether/commands/resume.yaml +5 -38
  37. package/.aether/commands/run.yaml +10 -10
  38. package/.aether/commands/seal.yaml +33 -33
  39. package/.aether/commands/skill-create.yaml +4 -4
  40. package/.aether/commands/status.yaml +14 -14
  41. package/.aether/commands/swarm.yaml +14 -14
  42. package/.aether/commands/tunnels.yaml +7 -7
  43. package/.aether/commands/update.yaml +1 -1
  44. package/.aether/commands/verify-castes.yaml +3 -3
  45. package/.aether/commands/watch.yaml +15 -15
  46. package/.aether/docs/command-playbooks/build-complete.md +48 -15
  47. package/.aether/docs/command-playbooks/build-context.md +11 -11
  48. package/.aether/docs/command-playbooks/build-full.md +76 -76
  49. package/.aether/docs/command-playbooks/build-prep.md +10 -10
  50. package/.aether/docs/command-playbooks/build-verify.md +27 -27
  51. package/.aether/docs/command-playbooks/build-wave.md +38 -38
  52. package/.aether/docs/command-playbooks/continue-advance.md +60 -27
  53. package/.aether/docs/command-playbooks/continue-finalize.md +25 -11
  54. package/.aether/docs/command-playbooks/continue-full.md +60 -46
  55. package/.aether/docs/command-playbooks/continue-gates.md +18 -18
  56. package/.aether/docs/command-playbooks/continue-verify.md +10 -10
  57. package/.aether/docs/source-of-truth-map.md +10 -10
  58. package/.aether/docs/structural-learning-stack.md +283 -0
  59. package/.aether/templates/colony-state-template.json +1 -0
  60. package/.aether/utils/consolidation-seal.sh +196 -0
  61. package/.aether/utils/consolidation.sh +127 -0
  62. package/.aether/utils/curation-ants/archivist.sh +97 -0
  63. package/.aether/utils/curation-ants/critic.sh +214 -0
  64. package/.aether/utils/curation-ants/herald.sh +102 -0
  65. package/.aether/utils/curation-ants/janitor.sh +121 -0
  66. package/.aether/utils/curation-ants/librarian.sh +99 -0
  67. package/.aether/utils/curation-ants/nurse.sh +153 -0
  68. package/.aether/utils/curation-ants/orchestrator.sh +181 -0
  69. package/.aether/utils/curation-ants/scribe.sh +164 -0
  70. package/.aether/utils/curation-ants/sentinel.sh +119 -0
  71. package/.aether/utils/event-bus.sh +301 -0
  72. package/.aether/utils/graph.sh +559 -0
  73. package/.aether/utils/instinct-store.sh +401 -0
  74. package/.aether/utils/learning.sh +79 -7
  75. package/.aether/utils/oracle/oracle-stop-hook.sh +896 -0
  76. package/.aether/utils/session.sh +13 -0
  77. package/.aether/utils/state-api.sh +1 -1
  78. package/.aether/utils/trust-scoring.sh +347 -0
  79. package/.aether/utils/worktree.sh +97 -0
  80. package/.claude/commands/ant/archaeology.md +2 -2
  81. package/.claude/commands/ant/chaos.md +4 -4
  82. package/.claude/commands/ant/colonize.md +9 -9
  83. package/.claude/commands/ant/council.md +6 -6
  84. package/.claude/commands/ant/data-clean.md +3 -3
  85. package/.claude/commands/ant/dream.md +2 -2
  86. package/.claude/commands/ant/entomb.md +9 -9
  87. package/.claude/commands/ant/export-signals.md +2 -2
  88. package/.claude/commands/ant/feedback.md +4 -4
  89. package/.claude/commands/ant/flag.md +2 -2
  90. package/.claude/commands/ant/flags.md +4 -4
  91. package/.claude/commands/ant/focus.md +4 -4
  92. package/.claude/commands/ant/help.md +1 -1
  93. package/.claude/commands/ant/history.md +1 -1
  94. package/.claude/commands/ant/import-signals.md +2 -2
  95. package/.claude/commands/ant/init.md +44 -27
  96. package/.claude/commands/ant/insert-phase.md +1 -1
  97. package/.claude/commands/ant/interpret.md +2 -2
  98. package/.claude/commands/ant/lay-eggs.md +2 -2
  99. package/.claude/commands/ant/maturity.md +2 -2
  100. package/.claude/commands/ant/memory-details.md +1 -1
  101. package/.claude/commands/ant/migrate-state.md +1 -1
  102. package/.claude/commands/ant/oracle.md +78 -42
  103. package/.claude/commands/ant/organize.md +3 -3
  104. package/.claude/commands/ant/patrol.md +8 -8
  105. package/.claude/commands/ant/pause-colony.md +5 -5
  106. package/.claude/commands/ant/phase.md +1 -1
  107. package/.claude/commands/ant/pheromones.md +1 -1
  108. package/.claude/commands/ant/plan.md +8 -8
  109. package/.claude/commands/ant/quick.md +4 -4
  110. package/.claude/commands/ant/redirect.md +4 -4
  111. package/.claude/commands/ant/resume-colony.md +5 -5
  112. package/.claude/commands/ant/resume.md +17 -29
  113. package/.claude/commands/ant/run.md +10 -10
  114. package/.claude/commands/ant/seal.md +25 -25
  115. package/.claude/commands/ant/skill-create.md +2 -2
  116. package/.claude/commands/ant/status.md +14 -14
  117. package/.claude/commands/ant/swarm.md +14 -14
  118. package/.claude/commands/ant/tunnels.md +4 -4
  119. package/.claude/commands/ant/update.md +1 -1
  120. package/.claude/commands/ant/verify-castes.md +2 -2
  121. package/.claude/commands/ant/watch.md +8 -8
  122. package/.opencode/commands/ant/archaeology.md +1 -1
  123. package/.opencode/commands/ant/build.md +80 -45
  124. package/.opencode/commands/ant/chaos.md +3 -3
  125. package/.opencode/commands/ant/colonize.md +8 -8
  126. package/.opencode/commands/ant/continue.md +40 -40
  127. package/.opencode/commands/ant/council.md +5 -5
  128. package/.opencode/commands/ant/data-clean.md +2 -2
  129. package/.opencode/commands/ant/dream.md +1 -1
  130. package/.opencode/commands/ant/entomb.md +3 -3
  131. package/.opencode/commands/ant/export-signals.md +1 -1
  132. package/.opencode/commands/ant/feedback.md +2 -2
  133. package/.opencode/commands/ant/flag.md +1 -1
  134. package/.opencode/commands/ant/flags.md +3 -3
  135. package/.opencode/commands/ant/focus.md +2 -2
  136. package/.opencode/commands/ant/import-signals.md +1 -1
  137. package/.opencode/commands/ant/init.md +44 -27
  138. package/.opencode/commands/ant/insert-phase.md +1 -1
  139. package/.opencode/commands/ant/interpret.md +1 -1
  140. package/.opencode/commands/ant/lay-eggs.md +2 -2
  141. package/.opencode/commands/ant/maturity.md +1 -1
  142. package/.opencode/commands/ant/memory-details.md +1 -1
  143. package/.opencode/commands/ant/oracle.md +69 -40
  144. package/.opencode/commands/ant/organize.md +2 -2
  145. package/.opencode/commands/ant/patrol.md +8 -8
  146. package/.opencode/commands/ant/pause-colony.md +2 -2
  147. package/.opencode/commands/ant/pheromones.md +1 -1
  148. package/.opencode/commands/ant/plan.md +6 -6
  149. package/.opencode/commands/ant/quick.md +4 -4
  150. package/.opencode/commands/ant/redirect.md +2 -2
  151. package/.opencode/commands/ant/resume-colony.md +4 -4
  152. package/.opencode/commands/ant/resume.md +5 -17
  153. package/.opencode/commands/ant/run.md +10 -10
  154. package/.opencode/commands/ant/seal.md +8 -8
  155. package/.opencode/commands/ant/skill-create.md +2 -2
  156. package/.opencode/commands/ant/status.md +10 -10
  157. package/.opencode/commands/ant/tunnels.md +3 -3
  158. package/.opencode/commands/ant/verify-castes.md +1 -1
  159. package/.opencode/commands/ant/watch.md +7 -7
  160. package/CHANGELOG.md +83 -0
  161. package/README.md +22 -9
  162. package/bin/cli.js +118 -3
  163. package/bin/lib/binary-downloader.js +267 -0
  164. package/bin/lib/update-transaction.js +27 -3
  165. package/bin/lib/version-gate.js +179 -0
  166. package/bin/npx-entry.js +0 -0
  167. package/package.json +1 -1
  168. package/.aether/agents/aether-ambassador.md +0 -140
  169. package/.aether/agents/aether-archaeologist.md +0 -108
  170. package/.aether/agents/aether-architect.md +0 -133
  171. package/.aether/agents/aether-auditor.md +0 -144
  172. package/.aether/agents/aether-builder.md +0 -184
  173. package/.aether/agents/aether-chaos.md +0 -115
  174. package/.aether/agents/aether-chronicler.md +0 -122
  175. package/.aether/agents/aether-gatekeeper.md +0 -116
  176. package/.aether/agents/aether-includer.md +0 -117
  177. package/.aether/agents/aether-keeper.md +0 -177
  178. package/.aether/agents/aether-measurer.md +0 -128
  179. package/.aether/agents/aether-oracle.md +0 -137
  180. package/.aether/agents/aether-probe.md +0 -133
  181. package/.aether/agents/aether-queen.md +0 -286
  182. package/.aether/agents/aether-route-setter.md +0 -130
  183. package/.aether/agents/aether-sage.md +0 -106
  184. package/.aether/agents/aether-scout.md +0 -101
  185. package/.aether/agents/aether-surveyor-disciplines.md +0 -391
  186. package/.aether/agents/aether-surveyor-nest.md +0 -329
  187. package/.aether/agents/aether-surveyor-pathogens.md +0 -264
  188. package/.aether/agents/aether-surveyor-provisions.md +0 -334
  189. package/.aether/agents/aether-tracker.md +0 -137
  190. package/.aether/agents/aether-watcher.md +0 -174
  191. package/.aether/agents/aether-weaver.md +0 -130
  192. package/.aether/commands/claude/archaeology.md +0 -334
  193. package/.aether/commands/claude/build.md +0 -65
  194. package/.aether/commands/claude/chaos.md +0 -336
  195. package/.aether/commands/claude/colonize.md +0 -259
  196. package/.aether/commands/claude/continue.md +0 -60
  197. package/.aether/commands/claude/council.md +0 -507
  198. package/.aether/commands/claude/data-clean.md +0 -81
  199. package/.aether/commands/claude/dream.md +0 -268
  200. package/.aether/commands/claude/entomb.md +0 -498
  201. package/.aether/commands/claude/export-signals.md +0 -57
  202. package/.aether/commands/claude/feedback.md +0 -96
  203. package/.aether/commands/claude/flag.md +0 -151
  204. package/.aether/commands/claude/flags.md +0 -169
  205. package/.aether/commands/claude/focus.md +0 -76
  206. package/.aether/commands/claude/help.md +0 -154
  207. package/.aether/commands/claude/history.md +0 -140
  208. package/.aether/commands/claude/import-signals.md +0 -71
  209. package/.aether/commands/claude/init.md +0 -505
  210. package/.aether/commands/claude/insert-phase.md +0 -105
  211. package/.aether/commands/claude/interpret.md +0 -278
  212. package/.aether/commands/claude/lay-eggs.md +0 -210
  213. package/.aether/commands/claude/maturity.md +0 -113
  214. package/.aether/commands/claude/memory-details.md +0 -77
  215. package/.aether/commands/claude/migrate-state.md +0 -171
  216. package/.aether/commands/claude/oracle.md +0 -642
  217. package/.aether/commands/claude/organize.md +0 -232
  218. package/.aether/commands/claude/patrol.md +0 -620
  219. package/.aether/commands/claude/pause-colony.md +0 -233
  220. package/.aether/commands/claude/phase.md +0 -115
  221. package/.aether/commands/claude/pheromones.md +0 -156
  222. package/.aether/commands/claude/plan.md +0 -693
  223. package/.aether/commands/claude/preferences.md +0 -65
  224. package/.aether/commands/claude/quick.md +0 -100
  225. package/.aether/commands/claude/redirect.md +0 -76
  226. package/.aether/commands/claude/resume-colony.md +0 -197
  227. package/.aether/commands/claude/resume.md +0 -388
  228. package/.aether/commands/claude/run.md +0 -231
  229. package/.aether/commands/claude/seal.md +0 -774
  230. package/.aether/commands/claude/skill-create.md +0 -286
  231. package/.aether/commands/claude/status.md +0 -410
  232. package/.aether/commands/claude/swarm.md +0 -349
  233. package/.aether/commands/claude/tunnels.md +0 -426
  234. package/.aether/commands/claude/update.md +0 -132
  235. package/.aether/commands/claude/verify-castes.md +0 -143
  236. package/.aether/commands/claude/watch.md +0 -239
  237. package/.aether/commands/opencode/archaeology.md +0 -331
  238. package/.aether/commands/opencode/build.md +0 -1168
  239. package/.aether/commands/opencode/chaos.md +0 -329
  240. package/.aether/commands/opencode/colonize.md +0 -195
  241. package/.aether/commands/opencode/continue.md +0 -1436
  242. package/.aether/commands/opencode/council.md +0 -437
  243. package/.aether/commands/opencode/data-clean.md +0 -77
  244. package/.aether/commands/opencode/dream.md +0 -260
  245. package/.aether/commands/opencode/entomb.md +0 -377
  246. package/.aether/commands/opencode/export-signals.md +0 -54
  247. package/.aether/commands/opencode/feedback.md +0 -99
  248. package/.aether/commands/opencode/flag.md +0 -149
  249. package/.aether/commands/opencode/flags.md +0 -167
  250. package/.aether/commands/opencode/focus.md +0 -73
  251. package/.aether/commands/opencode/help.md +0 -157
  252. package/.aether/commands/opencode/history.md +0 -136
  253. package/.aether/commands/opencode/import-signals.md +0 -68
  254. package/.aether/commands/opencode/init.md +0 -518
  255. package/.aether/commands/opencode/insert-phase.md +0 -111
  256. package/.aether/commands/opencode/interpret.md +0 -272
  257. package/.aether/commands/opencode/lay-eggs.md +0 -213
  258. package/.aether/commands/opencode/maturity.md +0 -108
  259. package/.aether/commands/opencode/memory-details.md +0 -83
  260. package/.aether/commands/opencode/migrate-state.md +0 -165
  261. package/.aether/commands/opencode/oracle.md +0 -593
  262. package/.aether/commands/opencode/organize.md +0 -226
  263. package/.aether/commands/opencode/patrol.md +0 -626
  264. package/.aether/commands/opencode/pause-colony.md +0 -203
  265. package/.aether/commands/opencode/phase.md +0 -113
  266. package/.aether/commands/opencode/pheromones.md +0 -162
  267. package/.aether/commands/opencode/plan.md +0 -684
  268. package/.aether/commands/opencode/preferences.md +0 -71
  269. package/.aether/commands/opencode/quick.md +0 -91
  270. package/.aether/commands/opencode/redirect.md +0 -84
  271. package/.aether/commands/opencode/resume-colony.md +0 -190
  272. package/.aether/commands/opencode/resume.md +0 -394
  273. package/.aether/commands/opencode/run.md +0 -237
  274. package/.aether/commands/opencode/seal.md +0 -452
  275. package/.aether/commands/opencode/skill-create.md +0 -63
  276. package/.aether/commands/opencode/status.md +0 -307
  277. package/.aether/commands/opencode/swarm.md +0 -15
  278. package/.aether/commands/opencode/tunnels.md +0 -400
  279. package/.aether/commands/opencode/update.md +0 -127
  280. package/.aether/commands/opencode/verify-castes.md +0 -139
  281. package/.aether/commands/opencode/watch.md +0 -227
@@ -0,0 +1,401 @@
1
+ #!/bin/bash
2
+ # Instinct Store utility functions — standalone instinct storage with trust scoring
3
+ # Provides: _instinct_store, _instinct_read_trusted, _instinct_decay_all, _instinct_archive
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, COLONY_DATA_DIR, SCRIPT_DIR, error constants) is available.
8
+ # Depends on trust-scoring.sh for trust score computation.
9
+ #
10
+ # State file: $COLONY_DATA_DIR/instincts.json
11
+ # Schema version: 1.0
12
+ # Cap: 50 instincts (lowest trust archived on overflow)
13
+
14
+ # ============================================================================
15
+ # _instinct_store
16
+ # Store a new instinct or reinforce an existing one (dedup by trigger prefix).
17
+ #
18
+ # Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f>
19
+ # --source <s> --evidence <e> [--source-type <type>]
20
+ #
21
+ # Deduplication: first 50 chars of trigger matched against existing entries.
22
+ # On match: boost confidence to max of existing/new, recompute trust score.
23
+ # Cap: 50 instincts. When exceeded, archive the entry with lowest trust_score.
24
+ # ============================================================================
25
+ _instinct_store() {
26
+ local trigger=""
27
+ local action=""
28
+ local domain=""
29
+ local confidence=""
30
+ local source=""
31
+ local evidence=""
32
+ local source_type="observation"
33
+
34
+ while [[ $# -gt 0 ]]; do
35
+ case "$1" in
36
+ --trigger) trigger="${2:-}"; shift 2 ;;
37
+ --action) action="${2:-}"; shift 2 ;;
38
+ --domain) domain="${2:-}"; shift 2 ;;
39
+ --confidence) confidence="${2:-}"; shift 2 ;;
40
+ --source) source="${2:-}"; shift 2 ;;
41
+ --evidence) evidence="${2:-}"; shift 2 ;;
42
+ --source-type) source_type="${2:-}"; shift 2 ;;
43
+ *) shift ;;
44
+ esac
45
+ done
46
+
47
+ [[ -z "$trigger" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
48
+ [[ -z "$action" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
49
+ [[ -z "$domain" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
50
+ [[ -z "$confidence" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
51
+ [[ -z "$source" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
52
+ [[ -z "$evidence" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-store --trigger <t> --action <a> --domain <d> --confidence <f> --source <s> --evidence <e>"
53
+
54
+ # Validate confidence is a number
55
+ if ! [[ "$confidence" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
56
+ json_err "$E_VALIDATION_FAILED" "--confidence must be a number between 0 and 1, got: $confidence"
57
+ fi
58
+
59
+ mkdir -p "$COLONY_DATA_DIR"
60
+ local instincts_file="$COLONY_DATA_DIR/instincts.json"
61
+
62
+ # Initialize file if missing
63
+ if [[ ! -f "$instincts_file" ]]; then
64
+ atomic_write "$instincts_file" '{"version":"1.0","instincts":[]}'
65
+ fi
66
+
67
+ local ts
68
+ ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
69
+
70
+ # Compute trust score via trust-calculate
71
+ local trust_result trust_score trust_tier
72
+ trust_result=$(_trust_calculate --source "$source_type" --evidence "$evidence" --days-since 0 2>/dev/null) || true
73
+ if echo "$trust_result" | jq -e '.ok == true' > /dev/null 2>&1; then
74
+ trust_score=$(echo "$trust_result" | jq -r '.result.score')
75
+ trust_tier=$(echo "$trust_result" | jq -r '.result.tier')
76
+ else
77
+ # Fallback: use confidence as trust score, derive tier
78
+ trust_score="$confidence"
79
+ trust_tier=$(_trust_score_to_tier "$confidence" 2>/dev/null || echo "provisional")
80
+ fi
81
+
82
+ # Acquire lock for atomic update
83
+ acquire_lock "$instincts_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on instincts.json"
84
+ trap 'release_lock 2>/dev/null || true' EXIT # SUPPRESS:OK -- cleanup: lock may not be held
85
+
86
+ # Dedup: check if a matching trigger prefix (first 50 chars) already exists
87
+ local trigger_prefix
88
+ trigger_prefix=$(echo "$trigger" | cut -c1-50)
89
+
90
+ local existing_id
91
+ existing_id=$(jq -r --arg prefix "$trigger_prefix" '
92
+ .instincts[]
93
+ | select(.archived == false)
94
+ | select((.trigger | .[0:50]) == $prefix)
95
+ | .id
96
+ | select(. != null)
97
+ ' "$instincts_file" 2>/dev/null | head -1)
98
+
99
+ local updated
100
+ if [[ -n "$existing_id" ]]; then
101
+ # Reinforce: boost confidence to max, recompute trust score
102
+ local new_confidence
103
+ new_confidence=$(jq -r --arg id "$existing_id" \
104
+ '.instincts[] | select(.id == $id) | .confidence' "$instincts_file")
105
+ local boosted_confidence
106
+ boosted_confidence=$(awk "BEGIN{
107
+ a=$new_confidence; b=$confidence;
108
+ printf \"%.4f\", (a > b ? a : b)
109
+ }")
110
+
111
+ # Recompute trust with boosted confidence
112
+ local boost_trust_result
113
+ boost_trust_result=$(_trust_calculate --source "$source_type" --evidence "$evidence" --days-since 0 2>/dev/null) || true
114
+ if echo "$boost_trust_result" | jq -e '.ok == true' > /dev/null 2>&1; then
115
+ trust_score=$(echo "$boost_trust_result" | jq -r '.result.score')
116
+ trust_tier=$(echo "$boost_trust_result" | jq -r '.result.tier')
117
+ fi
118
+
119
+ updated=$(jq \
120
+ --arg id "$existing_id" \
121
+ --argjson conf "$boosted_confidence" \
122
+ --argjson ts_score "$trust_score" \
123
+ --arg ts_tier "$trust_tier" \
124
+ --arg ts "$ts" '
125
+ .instincts = [.instincts[] | if .id == $id then
126
+ .confidence = $conf |
127
+ .trust_score = $ts_score |
128
+ .trust_tier = $ts_tier |
129
+ .provenance.last_applied = $ts |
130
+ .provenance.application_count += 1
131
+ else . end]
132
+ ' "$instincts_file") || json_err "$E_JSON_INVALID" "Failed to reinforce instinct"
133
+ atomic_write "$instincts_file" "$updated"
134
+ trap - EXIT
135
+ release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
136
+ json_ok "$(jq -n --arg id "$existing_id" --arg action "reinforced" \
137
+ '{id: $id, action: $action}')"
138
+ return
139
+ fi
140
+
141
+ # New instinct: generate id and build entry
142
+ local id
143
+ id="inst_$(date -u +%s)_$(head -c 3 /dev/urandom | od -An -tx1 | tr -d ' \n' | cut -c1-6)"
144
+
145
+ local new_entry
146
+ new_entry=$(jq -n \
147
+ --arg id "$id" \
148
+ --arg trigger "$trigger" \
149
+ --arg action "$action" \
150
+ --arg domain "$domain" \
151
+ --argjson trust_score "$trust_score" \
152
+ --arg trust_tier "$trust_tier" \
153
+ --argjson confidence "$confidence" \
154
+ --arg source "$source" \
155
+ --arg source_type "$source_type" \
156
+ --arg evidence "$evidence" \
157
+ --arg ts "$ts" \
158
+ '{
159
+ id: $id,
160
+ trigger: $trigger,
161
+ action: $action,
162
+ domain: $domain,
163
+ trust_score: $trust_score,
164
+ trust_tier: $trust_tier,
165
+ confidence: $confidence,
166
+ provenance: {
167
+ source: $source,
168
+ source_type: $source_type,
169
+ evidence: $evidence,
170
+ created_at: $ts,
171
+ last_applied: null,
172
+ application_count: 0
173
+ },
174
+ application_history: [],
175
+ related_instincts: [],
176
+ archived: false
177
+ }')
178
+
179
+ # Append entry
180
+ updated=$(jq --argjson entry "$new_entry" '.instincts += [$entry]' "$instincts_file") \
181
+ || json_err "$E_JSON_INVALID" "Failed to append instinct"
182
+
183
+ # Enforce 50-entry cap: archive lowest-trust non-archived instinct if over limit
184
+ local active_count
185
+ active_count=$(echo "$updated" | jq '[.instincts[] | select(.archived == false)] | length')
186
+ if [[ "$active_count" -gt 50 ]]; then
187
+ updated=$(echo "$updated" | jq '
188
+ # Find the id of the lowest-trust active instinct
189
+ (
190
+ [.instincts[] | select(.archived == false)]
191
+ | sort_by(.trust_score)
192
+ | .[0].id
193
+ ) as $lowest_id
194
+ |
195
+ .instincts = [.instincts[] | if .id == $lowest_id then .archived = true else . end]
196
+ ') || json_err "$E_JSON_INVALID" "Failed to enforce instinct cap"
197
+ fi
198
+
199
+ atomic_write "$instincts_file" "$updated"
200
+ trap - EXIT
201
+ release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
202
+ json_ok "$(jq -n --arg id "$id" --arg action "stored" '{id: $id, action: $action}')"
203
+ }
204
+
205
+ # ============================================================================
206
+ # _instinct_read_trusted
207
+ # Read trusted instincts, sorted by trust_score descending.
208
+ #
209
+ # Usage: instinct-read-trusted [--min-score <f>] [--domain <d>] [--limit <N>]
210
+ #
211
+ # Defaults: min-score=0.5, limit=20. Excludes archived entries.
212
+ # ============================================================================
213
+ _instinct_read_trusted() {
214
+ local min_score="0.5"
215
+ local domain=""
216
+ local limit="20"
217
+
218
+ while [[ $# -gt 0 ]]; do
219
+ case "$1" in
220
+ --min-score) min_score="${2:-0.5}"; shift 2 ;;
221
+ --domain) domain="${2:-}"; shift 2 ;;
222
+ --limit) limit="${2:-20}"; shift 2 ;;
223
+ *) shift ;;
224
+ esac
225
+ done
226
+
227
+ local instincts_file="$COLONY_DATA_DIR/instincts.json"
228
+ if [[ ! -f "$instincts_file" ]]; then
229
+ json_ok '{"instincts":[],"count":0}'
230
+ return
231
+ fi
232
+
233
+ local result
234
+ result=$(jq -n \
235
+ --argjson min_score "$min_score" \
236
+ --arg domain "$domain" \
237
+ --argjson limit "$limit" \
238
+ --slurpfile data "$instincts_file" '
239
+ $data[0].instincts
240
+ | [.[] | select(.archived == false)]
241
+ | [.[] | select(.trust_score >= $min_score)]
242
+ | (if $domain != "" then [.[] | select(.domain == $domain)] else . end)
243
+ | sort_by(-.trust_score)
244
+ | .[0:$limit]
245
+ | {instincts: ., count: length}
246
+ ') || json_err "$E_JSON_INVALID" "Failed to read trusted instincts"
247
+
248
+ json_ok "$result"
249
+ }
250
+
251
+ # ============================================================================
252
+ # _instinct_decay_all
253
+ # Apply trust-based time decay to all non-archived instincts.
254
+ #
255
+ # Usage: instinct-decay-all [--days <N>] [--dry-run]
256
+ #
257
+ # Archives instincts whose decayed score falls below 0.25.
258
+ # Updates trust_tier for all processed entries.
259
+ # ============================================================================
260
+ _instinct_decay_all() {
261
+ local days="30"
262
+ local dry_run="false"
263
+
264
+ while [[ $# -gt 0 ]]; do
265
+ case "$1" in
266
+ --days) days="${2:-30}"; shift 2 ;;
267
+ --dry-run) dry_run="true"; shift ;;
268
+ *) shift ;;
269
+ esac
270
+ done
271
+
272
+ local instincts_file="$COLONY_DATA_DIR/instincts.json"
273
+ if [[ ! -f "$instincts_file" ]]; then
274
+ json_ok '{"processed":0,"archived":0,"dry_run":false}'
275
+ return
276
+ fi
277
+
278
+ # Read all active instincts and apply decay
279
+ local current_data
280
+ current_data=$(cat "$instincts_file")
281
+
282
+ local active_count
283
+ active_count=$(echo "$current_data" | jq '[.instincts[] | select(.archived == false)] | length')
284
+
285
+ if [[ "$active_count" -eq 0 ]]; then
286
+ json_ok "$(jq -n --argjson days "$days" '{"processed":0,"archived":0,"dry_run":false}')"
287
+ return
288
+ fi
289
+
290
+ # Build updated instincts array: apply decay to each active entry
291
+ local updated_instincts="[]"
292
+ local archived_count=0
293
+
294
+ while IFS= read -r instinct_json; do
295
+ local current_score
296
+ current_score=$(echo "$instinct_json" | jq -r '.trust_score')
297
+
298
+ local decay_result decayed_score new_tier
299
+ decay_result=$(_trust_decay --score "$current_score" --days "$days" 2>/dev/null) || true
300
+
301
+ if echo "$decay_result" | jq -e '.ok == true' > /dev/null 2>&1; then
302
+ decayed_score=$(echo "$decay_result" | jq -r '.result.decayed')
303
+ else
304
+ decayed_score="$current_score"
305
+ fi
306
+
307
+ new_tier=$(_trust_score_to_tier "$decayed_score" 2>/dev/null || echo "dormant")
308
+
309
+ local should_archive="false"
310
+ local below_threshold
311
+ below_threshold=$(awk "BEGIN{print ($decayed_score < 0.25)}" 2>/dev/null || echo "0")
312
+ if [[ "$below_threshold" == "1" ]]; then
313
+ should_archive="true"
314
+ archived_count=$((archived_count + 1))
315
+ fi
316
+
317
+ local updated_entry
318
+ updated_entry=$(echo "$instinct_json" | jq \
319
+ --argjson score "$decayed_score" \
320
+ --arg tier "$new_tier" \
321
+ --argjson archive "$should_archive" \
322
+ '.trust_score = $score | .trust_tier = $tier | .archived = $archive')
323
+
324
+ updated_instincts=$(echo "$updated_instincts" | jq \
325
+ --argjson entry "$updated_entry" \
326
+ '. += [$entry]')
327
+ done < <(echo "$current_data" | jq -c '.instincts[] | select(.archived == false)')
328
+
329
+ # Merge: keep archived entries as-is, replace active entries with updated versions
330
+ local merged
331
+ merged=$(jq -n \
332
+ --argjson updated "$updated_instincts" \
333
+ --argjson original "$(echo "$current_data" | jq '.instincts')" \
334
+ '
335
+ # Build a lookup of updated entries by id
336
+ ($updated | map({(.id): .}) | add // {}) as $lookup
337
+ |
338
+ [
339
+ $original[] | if .archived == true then
340
+ .
341
+ elif ($lookup[.id] != null) then
342
+ $lookup[.id]
343
+ else
344
+ .
345
+ end
346
+ ]
347
+ ')
348
+
349
+ local final_data
350
+ final_data=$(echo "$current_data" | jq --argjson instincts "$merged" '.instincts = $instincts')
351
+
352
+ if [[ "$dry_run" != "true" ]]; then
353
+ acquire_lock "$instincts_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on instincts.json"
354
+ trap 'release_lock 2>/dev/null || true' EXIT # SUPPRESS:OK -- cleanup: lock may not be held
355
+ atomic_write "$instincts_file" "$final_data"
356
+ trap - EXIT
357
+ release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
358
+ fi
359
+
360
+ json_ok "$(jq -n \
361
+ --argjson processed "$active_count" \
362
+ --argjson archived "$archived_count" \
363
+ --argjson days "$days" \
364
+ --argjson dry_run "$([ "$dry_run" == "true" ] && echo true || echo false)" \
365
+ '{processed: $processed, archived: $archived, days: $days, dry_run: $dry_run}')"
366
+ }
367
+
368
+ # ============================================================================
369
+ # _instinct_archive
370
+ # Soft-delete an instinct by id (sets archived: true).
371
+ #
372
+ # Usage: instinct-archive --id <id>
373
+ # ============================================================================
374
+ _instinct_archive() {
375
+ local id=""
376
+
377
+ while [[ $# -gt 0 ]]; do
378
+ case "$1" in
379
+ --id) id="${2:-}"; shift 2 ;;
380
+ *) shift ;;
381
+ esac
382
+ done
383
+
384
+ [[ -z "$id" ]] && json_err "$E_VALIDATION_FAILED" "Usage: instinct-archive --id <id>"
385
+
386
+ local instincts_file="$COLONY_DATA_DIR/instincts.json"
387
+ [[ ! -f "$instincts_file" ]] && json_err "$E_FILE_NOT_FOUND" "No instincts.json found"
388
+
389
+ acquire_lock "$instincts_file" || json_err "$E_LOCK_FAILED" "Failed to acquire lock on instincts.json"
390
+ trap 'release_lock 2>/dev/null || true' EXIT # SUPPRESS:OK -- cleanup: lock may not be held
391
+
392
+ local updated
393
+ updated=$(jq --arg id "$id" '
394
+ .instincts = [.instincts[] | if .id == $id then .archived = true else . end]
395
+ ' "$instincts_file") || json_err "$E_JSON_INVALID" "Failed to archive instinct"
396
+
397
+ atomic_write "$instincts_file" "$updated"
398
+ trap - EXIT
399
+ release_lock 2>/dev/null || true # SUPPRESS:OK -- cleanup: lock may not be held
400
+ json_ok "$(jq -n --arg id "$id" '{archived: $id}')"
401
+ }
@@ -91,16 +91,18 @@ _learning_inject() {
91
91
  # ============================================================================
92
92
  # _learning_observe
93
93
  # Record observation of a learning across colonies
94
- # Usage: learning-observe <content> <wisdom_type> [colony_name]
95
- # Returns: JSON with observation_count, threshold status, and colonies list
94
+ # Usage: learning-observe <content> <wisdom_type> [colony_name] [source_type] [evidence_type]
95
+ # Returns: JSON with observation_count, threshold status, colonies list, and trust_score
96
96
  # ============================================================================
97
97
  _learning_observe() {
98
98
  # Record observation of a learning across colonies
99
- # Usage: learning-observe <content> <wisdom_type> [colony_name]
100
- # Returns: JSON with observation_count, threshold status, and colonies list
99
+ # Usage: learning-observe <content> <wisdom_type> [colony_name] [source_type] [evidence_type]
100
+ # Returns: JSON with observation_count, threshold status, colonies list, and trust_score
101
101
  content="${1:-}"
102
102
  wisdom_type="${2:-}"
103
103
  colony_name="${3:-unknown}"
104
+ local source_type="${4:-observation}"
105
+ local evidence_type="${5:-anecdotal}"
104
106
 
105
107
  # Validate required arguments
106
108
  [[ -z "$content" ]] && json_err "$E_VALIDATION_FAILED" "Usage: learning-observe <content> <wisdom_type> [colony_name]" '{"missing":"content"}'
@@ -178,11 +180,64 @@ _learning_observe() {
178
180
  # Get current timestamp
179
181
  ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
180
182
 
183
+ # Initialize trust_score (set for new observations; left as default for existing)
184
+ local trust_score="0.490000"
185
+
186
+ # --- Legacy migration: backfill missing fields on any entries that lack trust_score ---
187
+ local has_legacy
188
+ has_legacy=$(jq '[.observations[] | select(.trust_score == null)] | length' "$observations_file")
189
+ if [[ "$has_legacy" -gt 0 ]]; then
190
+ jq '.observations |= map(
191
+ if .trust_score == null then
192
+ .trust_score = 0.49 |
193
+ .source_type = "legacy" |
194
+ .evidence_type = "indirect" |
195
+ .compression_level = 0
196
+ else . end
197
+ )' "$observations_file" > "${observations_file}.migrate.$$" && \
198
+ mv "${observations_file}.migrate.$$" "$observations_file" 2>/dev/null || {
199
+ rm -f "${observations_file}.migrate.$$"
200
+ _aether_log_error "Could not backfill legacy observation fields"
201
+ }
202
+ fi
203
+
181
204
  # Check if observation with same hash already exists
182
205
  existing_index=$(jq -r --arg hash "$content_hash" '.observations | to_entries[] | select(.value.content_hash == $hash) | .key' "$observations_file" | head -1)
183
206
 
184
207
  if [[ -n "$existing_index" ]]; then
185
208
  # Existing observation: increment count, update last_seen, add colony if new
209
+ # Recalculate trust_score based on existing source/evidence types and days since first_seen
210
+ local existing_source existing_evidence existing_first_seen days_since
211
+ existing_source=$(jq -r --arg hash "$content_hash" '.observations[] | select(.content_hash == $hash) | .source_type // "observation"' "$observations_file")
212
+ existing_evidence=$(jq -r --arg hash "$content_hash" '.observations[] | select(.content_hash == $hash) | .evidence_type // "anecdotal"' "$observations_file")
213
+
214
+ # Map legacy/invalid types to valid trust-calculate types
215
+ case "$existing_source" in
216
+ user_feedback|error_resolution|success_pattern|observation|heuristic) ;;
217
+ *) existing_source="observation" ;;
218
+ esac
219
+ case "$existing_evidence" in
220
+ test_verified|multi_phase|single_phase|anecdotal) ;;
221
+ *) existing_evidence="anecdotal" ;;
222
+ esac
223
+ existing_first_seen=$(jq -r --arg hash "$content_hash" '.observations[] | select(.content_hash == $hash) | .first_seen' "$observations_file")
224
+
225
+ # Compute days since first_seen
226
+ if [[ -n "$existing_first_seen" ]]; then
227
+ local epoch_first epoch_now
228
+ epoch_first=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" "$existing_first_seen" "+%s" 2>/dev/null || echo "0")
229
+ epoch_now=$(date -u +%s)
230
+ days_since=$(( (epoch_now - epoch_first) / 86400 ))
231
+ [[ "$days_since" -lt 0 ]] && days_since=0
232
+ else
233
+ days_since=0
234
+ fi
235
+
236
+ # Calculate trust score for the re-observation
237
+ local trust_result
238
+ trust_result=$(bash "$0" trust-calculate --source "$existing_source" --evidence "$existing_evidence" --days-since "$days_since" 2>/dev/null)
239
+ trust_score=$(echo "$trust_result" | jq -r '.result.score // "0.490000"' 2>/dev/null)
240
+
186
241
  # Rotate backups before write (uses .bak.N naming)
187
242
  if [[ -f "$observations_file" ]]; then
188
243
  cp -f "${observations_file}.bak.2" "${observations_file}.bak.3" 2>/dev/null || _aether_log_error "Could not rotate observations backup .bak.2 to .bak.3"
@@ -194,12 +249,14 @@ _learning_observe() {
194
249
  jq --arg hash "$content_hash" \
195
250
  --arg colony "$colony_name" \
196
251
  --arg ts "$ts" \
252
+ --argjson trust_score "$trust_score" \
197
253
  '
198
254
  .observations |= map(
199
255
  if .content_hash == $hash then
200
256
  .observation_count += 1 |
201
257
  .last_seen = $ts |
202
- .colonies = ((.colonies + [$colony]) | unique)
258
+ .colonies = ((.colonies + [$colony]) | unique) |
259
+ .trust_score = ($trust_score | tonumber)
203
260
  else
204
261
  .
205
262
  end
@@ -235,11 +292,20 @@ _learning_observe() {
235
292
  fi
236
293
  tmp_file="${observations_file}.tmp.$$"
237
294
 
295
+ # Compute trust score for new observation
296
+ local trust_result
297
+ trust_result=$(bash "$0" trust-calculate --source "$source_type" --evidence "$evidence_type" --days-since 0 2>/dev/null)
298
+ local trust_score
299
+ trust_score=$(echo "$trust_result" | jq -r '.result.score // "0.490000"' 2>/dev/null)
300
+
238
301
  jq --arg hash "$content_hash" \
239
302
  --arg content "$content" \
240
303
  --arg type "$wisdom_type" \
241
304
  --arg colony "$colony_name" \
242
305
  --arg ts "$ts" \
306
+ --arg trust_score "$trust_score" \
307
+ --arg source_type "$source_type" \
308
+ --arg evidence_type "$evidence_type" \
243
309
  '.observations += [{
244
310
  "content_hash": $hash,
245
311
  "content": $content,
@@ -247,7 +313,11 @@ _learning_observe() {
247
313
  "observation_count": 1,
248
314
  "first_seen": $ts,
249
315
  "last_seen": $ts,
250
- "colonies": [$colony]
316
+ "colonies": [$colony],
317
+ "trust_score": ($trust_score | tonumber),
318
+ "source_type": $source_type,
319
+ "evidence_type": $evidence_type,
320
+ "compression_level": 0
251
321
  }]' "$observations_file" > "$tmp_file" || {
252
322
  _aether_log_error "Could not create new observation entry"
253
323
  rm -f "$tmp_file"
@@ -294,6 +364,7 @@ _learning_observe() {
294
364
  --argjson threshold_met "$threshold_met" \
295
365
  --argjson colonies "$colonies" \
296
366
  --argjson is_new "$is_new" \
367
+ --arg trust_score "$trust_score" \
297
368
  '{
298
369
  content_hash: $hash,
299
370
  content: $content,
@@ -302,7 +373,8 @@ _learning_observe() {
302
373
  threshold: $threshold,
303
374
  threshold_met: $threshold_met,
304
375
  colonies: $colonies,
305
- is_new: $is_new
376
+ is_new: $is_new,
377
+ trust_score: ($trust_score | tonumber)
306
378
  }')
307
379
 
308
380
  json_ok "$result"