aether-colony 5.3.1 → 5.3.3

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 (165) hide show
  1. package/.aether/aether-utils.sh +181 -5
  2. package/.aether/commands/build.yaml +35 -0
  3. package/.aether/commands/entomb.yaml +1 -1
  4. package/.aether/commands/init.yaml +29 -12
  5. package/.aether/commands/oracle.yaml +70 -0
  6. package/.aether/commands/patrol.yaml +2 -2
  7. package/.aether/commands/run.yaml +3 -3
  8. package/.aether/commands/swarm.yaml +1 -1
  9. package/.aether/docs/command-playbooks/build-complete.md +41 -8
  10. package/.aether/docs/command-playbooks/build-full.md +7 -7
  11. package/.aether/docs/command-playbooks/build-prep.md +1 -1
  12. package/.aether/docs/command-playbooks/continue-advance.md +33 -0
  13. package/.aether/docs/command-playbooks/continue-finalize.md +15 -1
  14. package/.aether/docs/command-playbooks/continue-full.md +15 -1
  15. package/.aether/docs/source-of-truth-map.md +10 -10
  16. package/.aether/docs/structural-learning-stack.md +283 -0
  17. package/.aether/utils/consolidation-seal.sh +196 -0
  18. package/.aether/utils/consolidation.sh +127 -0
  19. package/.aether/utils/curation-ants/archivist.sh +97 -0
  20. package/.aether/utils/curation-ants/critic.sh +214 -0
  21. package/.aether/utils/curation-ants/herald.sh +102 -0
  22. package/.aether/utils/curation-ants/janitor.sh +121 -0
  23. package/.aether/utils/curation-ants/librarian.sh +99 -0
  24. package/.aether/utils/curation-ants/nurse.sh +153 -0
  25. package/.aether/utils/curation-ants/orchestrator.sh +181 -0
  26. package/.aether/utils/curation-ants/scribe.sh +164 -0
  27. package/.aether/utils/curation-ants/sentinel.sh +119 -0
  28. package/.aether/utils/event-bus.sh +301 -0
  29. package/.aether/utils/graph.sh +559 -0
  30. package/.aether/utils/instinct-store.sh +401 -0
  31. package/.aether/utils/learning.sh +79 -7
  32. package/.aether/utils/session.sh +13 -0
  33. package/.aether/utils/state-api.sh +1 -1
  34. package/.aether/utils/trust-scoring.sh +347 -0
  35. package/.aether/utils/worktree.sh +97 -0
  36. package/.claude/commands/ant/entomb.md +1 -1
  37. package/.claude/commands/ant/init.md +29 -12
  38. package/.claude/commands/ant/oracle.md +35 -0
  39. package/.claude/commands/ant/patrol.md +2 -2
  40. package/.claude/commands/ant/run.md +3 -3
  41. package/.claude/commands/ant/swarm.md +1 -1
  42. package/.opencode/commands/ant/build.md +35 -0
  43. package/.opencode/commands/ant/init.md +29 -12
  44. package/.opencode/commands/ant/oracle.md +35 -0
  45. package/.opencode/commands/ant/patrol.md +2 -2
  46. package/.opencode/commands/ant/run.md +3 -3
  47. package/CHANGELOG.md +83 -0
  48. package/README.md +34 -37
  49. package/bin/lib/update-transaction.js +8 -3
  50. package/bin/npx-entry.js +0 -0
  51. package/package.json +1 -1
  52. package/.aether/agents/aether-ambassador.md +0 -140
  53. package/.aether/agents/aether-archaeologist.md +0 -108
  54. package/.aether/agents/aether-architect.md +0 -133
  55. package/.aether/agents/aether-auditor.md +0 -144
  56. package/.aether/agents/aether-builder.md +0 -184
  57. package/.aether/agents/aether-chaos.md +0 -115
  58. package/.aether/agents/aether-chronicler.md +0 -122
  59. package/.aether/agents/aether-gatekeeper.md +0 -116
  60. package/.aether/agents/aether-includer.md +0 -117
  61. package/.aether/agents/aether-keeper.md +0 -177
  62. package/.aether/agents/aether-measurer.md +0 -128
  63. package/.aether/agents/aether-oracle.md +0 -137
  64. package/.aether/agents/aether-probe.md +0 -133
  65. package/.aether/agents/aether-queen.md +0 -286
  66. package/.aether/agents/aether-route-setter.md +0 -130
  67. package/.aether/agents/aether-sage.md +0 -106
  68. package/.aether/agents/aether-scout.md +0 -101
  69. package/.aether/agents/aether-surveyor-disciplines.md +0 -391
  70. package/.aether/agents/aether-surveyor-nest.md +0 -329
  71. package/.aether/agents/aether-surveyor-pathogens.md +0 -264
  72. package/.aether/agents/aether-surveyor-provisions.md +0 -334
  73. package/.aether/agents/aether-tracker.md +0 -137
  74. package/.aether/agents/aether-watcher.md +0 -174
  75. package/.aether/agents/aether-weaver.md +0 -130
  76. package/.aether/commands/claude/archaeology.md +0 -334
  77. package/.aether/commands/claude/build.md +0 -65
  78. package/.aether/commands/claude/chaos.md +0 -336
  79. package/.aether/commands/claude/colonize.md +0 -259
  80. package/.aether/commands/claude/continue.md +0 -60
  81. package/.aether/commands/claude/council.md +0 -507
  82. package/.aether/commands/claude/data-clean.md +0 -81
  83. package/.aether/commands/claude/dream.md +0 -268
  84. package/.aether/commands/claude/entomb.md +0 -498
  85. package/.aether/commands/claude/export-signals.md +0 -57
  86. package/.aether/commands/claude/feedback.md +0 -96
  87. package/.aether/commands/claude/flag.md +0 -151
  88. package/.aether/commands/claude/flags.md +0 -169
  89. package/.aether/commands/claude/focus.md +0 -76
  90. package/.aether/commands/claude/help.md +0 -154
  91. package/.aether/commands/claude/history.md +0 -140
  92. package/.aether/commands/claude/import-signals.md +0 -71
  93. package/.aether/commands/claude/init.md +0 -505
  94. package/.aether/commands/claude/insert-phase.md +0 -105
  95. package/.aether/commands/claude/interpret.md +0 -278
  96. package/.aether/commands/claude/lay-eggs.md +0 -210
  97. package/.aether/commands/claude/maturity.md +0 -113
  98. package/.aether/commands/claude/memory-details.md +0 -77
  99. package/.aether/commands/claude/migrate-state.md +0 -171
  100. package/.aether/commands/claude/oracle.md +0 -642
  101. package/.aether/commands/claude/organize.md +0 -232
  102. package/.aether/commands/claude/patrol.md +0 -620
  103. package/.aether/commands/claude/pause-colony.md +0 -233
  104. package/.aether/commands/claude/phase.md +0 -115
  105. package/.aether/commands/claude/pheromones.md +0 -156
  106. package/.aether/commands/claude/plan.md +0 -693
  107. package/.aether/commands/claude/preferences.md +0 -65
  108. package/.aether/commands/claude/quick.md +0 -100
  109. package/.aether/commands/claude/redirect.md +0 -76
  110. package/.aether/commands/claude/resume-colony.md +0 -197
  111. package/.aether/commands/claude/resume.md +0 -388
  112. package/.aether/commands/claude/run.md +0 -231
  113. package/.aether/commands/claude/seal.md +0 -774
  114. package/.aether/commands/claude/skill-create.md +0 -286
  115. package/.aether/commands/claude/status.md +0 -410
  116. package/.aether/commands/claude/swarm.md +0 -349
  117. package/.aether/commands/claude/tunnels.md +0 -426
  118. package/.aether/commands/claude/update.md +0 -132
  119. package/.aether/commands/claude/verify-castes.md +0 -143
  120. package/.aether/commands/claude/watch.md +0 -239
  121. package/.aether/commands/opencode/archaeology.md +0 -331
  122. package/.aether/commands/opencode/build.md +0 -1168
  123. package/.aether/commands/opencode/chaos.md +0 -329
  124. package/.aether/commands/opencode/colonize.md +0 -195
  125. package/.aether/commands/opencode/continue.md +0 -1436
  126. package/.aether/commands/opencode/council.md +0 -437
  127. package/.aether/commands/opencode/data-clean.md +0 -77
  128. package/.aether/commands/opencode/dream.md +0 -260
  129. package/.aether/commands/opencode/entomb.md +0 -377
  130. package/.aether/commands/opencode/export-signals.md +0 -54
  131. package/.aether/commands/opencode/feedback.md +0 -99
  132. package/.aether/commands/opencode/flag.md +0 -149
  133. package/.aether/commands/opencode/flags.md +0 -167
  134. package/.aether/commands/opencode/focus.md +0 -73
  135. package/.aether/commands/opencode/help.md +0 -157
  136. package/.aether/commands/opencode/history.md +0 -136
  137. package/.aether/commands/opencode/import-signals.md +0 -68
  138. package/.aether/commands/opencode/init.md +0 -518
  139. package/.aether/commands/opencode/insert-phase.md +0 -111
  140. package/.aether/commands/opencode/interpret.md +0 -272
  141. package/.aether/commands/opencode/lay-eggs.md +0 -213
  142. package/.aether/commands/opencode/maturity.md +0 -108
  143. package/.aether/commands/opencode/memory-details.md +0 -83
  144. package/.aether/commands/opencode/migrate-state.md +0 -165
  145. package/.aether/commands/opencode/oracle.md +0 -593
  146. package/.aether/commands/opencode/organize.md +0 -226
  147. package/.aether/commands/opencode/patrol.md +0 -626
  148. package/.aether/commands/opencode/pause-colony.md +0 -203
  149. package/.aether/commands/opencode/phase.md +0 -113
  150. package/.aether/commands/opencode/pheromones.md +0 -162
  151. package/.aether/commands/opencode/plan.md +0 -684
  152. package/.aether/commands/opencode/preferences.md +0 -71
  153. package/.aether/commands/opencode/quick.md +0 -91
  154. package/.aether/commands/opencode/redirect.md +0 -84
  155. package/.aether/commands/opencode/resume-colony.md +0 -190
  156. package/.aether/commands/opencode/resume.md +0 -394
  157. package/.aether/commands/opencode/run.md +0 -237
  158. package/.aether/commands/opencode/seal.md +0 -452
  159. package/.aether/commands/opencode/skill-create.md +0 -63
  160. package/.aether/commands/opencode/status.md +0 -307
  161. package/.aether/commands/opencode/swarm.md +0 -15
  162. package/.aether/commands/opencode/tunnels.md +0 -400
  163. package/.aether/commands/opencode/update.md +0 -127
  164. package/.aether/commands/opencode/verify-castes.md +0 -139
  165. 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"
@@ -275,6 +275,13 @@ _session_init() {
275
275
  summary: "Session initialized"
276
276
  }' > "$session_file.tmp"
277
277
  mv "$session_file.tmp" "$session_file"
278
+
279
+ # Mirror to legacy path so session recovery (which checks .aether/data/session.json) works
280
+ local legacy_session="$DATA_DIR/session.json"
281
+ if [[ "$session_file" != "$legacy_session" ]]; then
282
+ cp "$session_file" "$legacy_session" 2>/dev/null || true
283
+ fi
284
+
278
285
  json_ok "$(jq -n --arg sid "$session_id" --arg goal "$goal" --arg file "$session_file" \
279
286
  '{session_id: $sid, goal: $goal, file: $file}')"
280
287
  }
@@ -361,6 +368,12 @@ _session_update() {
361
368
  json_err "$E_UNKNOWN" "Failed to rename temporary session file"
362
369
  }
363
370
 
371
+ # Mirror to legacy path so session recovery (which checks .aether/data/session.json) works
372
+ local legacy_session="$DATA_DIR/session.json"
373
+ if [[ "$session_file" != "$legacy_session" ]]; then
374
+ cp "$session_file" "$legacy_session" 2>/dev/null || true
375
+ fi
376
+
364
377
  json_ok "$(jq -n --arg cmd "$cmd_run" '{updated: true, command: $cmd}')"
365
378
  }
366
379
 
@@ -229,7 +229,7 @@ _state_migrate() {
229
229
  # Gracefully degrades: missing files produce zero/default values
230
230
  # ============================================================================
231
231
  _colony_vital_signs() {
232
- local cvs_state_file="$COLONY_DATA_DIR/COLONY_STATE.json"
232
+ local cvs_state_file="$DATA_DIR/COLONY_STATE.json"
233
233
  local cvs_midden_file="$COLONY_DATA_DIR/midden/midden.json"
234
234
  local cvs_phero_file="$COLONY_DATA_DIR/pheromones.json"
235
235
  local cvs_session_file="$COLONY_DATA_DIR/session.json"