aether-colony 5.3.2 → 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 +22 -9
  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,99 @@
1
+ #!/bin/bash
2
+ # Librarian curation ant — inventory statistics across all memory stores
3
+ # Provides: _curation_librarian
4
+ #
5
+ # These functions are sourced by aether-utils.sh at startup.
6
+ # All shared infrastructure (json_ok, json_err, COLONY_DATA_DIR, error constants)
7
+ # is available.
8
+ #
9
+ # Subcommand: curation-librarian
10
+ # Generates inventory statistics for all colony memory stores.
11
+
12
+ # ============================================================================
13
+ # _curation_librarian
14
+ # Generate inventory statistics across all memory stores.
15
+ #
16
+ # Usage: curation-librarian
17
+ #
18
+ # Output:
19
+ # {
20
+ # observations: N,
21
+ # instincts: {total: N, active: N, archived: N},
22
+ # graph_edges: N,
23
+ # events: N,
24
+ # signals: {active: N, total: N},
25
+ # midden: N,
26
+ # generated_at: "ISO8601"
27
+ # }
28
+ # ============================================================================
29
+ _curation_librarian() {
30
+ local obs_file="$COLONY_DATA_DIR/learning-observations.json"
31
+ local inst_file="$COLONY_DATA_DIR/instincts.json"
32
+ local graph_file="$COLONY_DATA_DIR/instinct-graph.json"
33
+ local event_file="$COLONY_DATA_DIR/event-bus.jsonl"
34
+ local pheromone_file="$COLONY_DATA_DIR/pheromones.json"
35
+ local midden_file="$COLONY_DATA_DIR/midden/midden.json"
36
+
37
+ # Observations count
38
+ local obs_count=0
39
+ if [[ -f "$obs_file" ]]; then
40
+ obs_count=$(jq '[.observations[]] | length' "$obs_file" 2>/dev/null || echo 0)
41
+ fi
42
+
43
+ # Instinct counts: total, active, archived
44
+ local inst_total=0 inst_active=0 inst_archived=0
45
+ if [[ -f "$inst_file" ]]; then
46
+ inst_total=$(jq '[.instincts[]] | length' "$inst_file" 2>/dev/null || echo 0)
47
+ inst_active=$(jq '[.instincts[] | select(.archived == false)] | length' "$inst_file" 2>/dev/null || echo 0)
48
+ inst_archived=$(jq '[.instincts[] | select(.archived == true)] | length' "$inst_file" 2>/dev/null || echo 0)
49
+ fi
50
+
51
+ # Graph edge count
52
+ local edge_count=0
53
+ if [[ -f "$graph_file" ]]; then
54
+ edge_count=$(jq '[.edges[]] | length' "$graph_file" 2>/dev/null || echo 0)
55
+ fi
56
+
57
+ # Event count (JSONL — count non-empty lines)
58
+ local event_count=0
59
+ if [[ -f "$event_file" ]]; then
60
+ event_count=$(grep -c '.' "$event_file" 2>/dev/null || echo 0)
61
+ fi
62
+
63
+ # Signal counts: active, total
64
+ local sig_active=0 sig_total=0
65
+ if [[ -f "$pheromone_file" ]]; then
66
+ sig_total=$(jq '[.signals[]] | length' "$pheromone_file" 2>/dev/null || echo 0)
67
+ sig_active=$(jq '[.signals[] | select(.active == true)] | length' "$pheromone_file" 2>/dev/null || echo 0)
68
+ fi
69
+
70
+ # Midden entry count
71
+ local midden_count=0
72
+ if [[ -f "$midden_file" ]]; then
73
+ midden_count=$(jq '[.entries[]] | length' "$midden_file" 2>/dev/null || echo 0)
74
+ fi
75
+
76
+ local generated_at
77
+ generated_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
78
+
79
+ json_ok "$(jq -n \
80
+ --argjson obs "$obs_count" \
81
+ --argjson inst_total "$inst_total" \
82
+ --argjson inst_active "$inst_active" \
83
+ --argjson inst_archived "$inst_archived" \
84
+ --argjson edges "$edge_count" \
85
+ --argjson events "$event_count" \
86
+ --argjson sig_active "$sig_active" \
87
+ --argjson sig_total "$sig_total" \
88
+ --argjson midden "$midden_count" \
89
+ --arg generated_at "$generated_at" \
90
+ '{
91
+ observations: $obs,
92
+ instincts: {total: $inst_total, active: $inst_active, archived: $inst_archived},
93
+ graph_edges: $edges,
94
+ events: $events,
95
+ signals: {active: $sig_active, total: $sig_total},
96
+ midden: $midden,
97
+ generated_at: $generated_at
98
+ }')"
99
+ }
@@ -0,0 +1,153 @@
1
+ #!/bin/bash
2
+ # Nurse curation ant — trust recalculation for observations and instincts
3
+ # Provides: _curation_nurse
4
+ #
5
+ # These functions are sourced by aether-utils.sh at startup.
6
+ # All shared infrastructure (json_ok, json_err, atomic_write,
7
+ # COLONY_DATA_DIR, SCRIPT_DIR, error constants) is available.
8
+ #
9
+ # Subcommand: curation-nurse [--dry-run]
10
+ # Recalculates trust scores for all observations (that have source_type/evidence_type)
11
+ # and applies trust-decay to instincts based on days since created_at.
12
+
13
+ # ============================================================================
14
+ # _curation_nurse
15
+ # Recalculate trust scores across observations and instincts.
16
+ #
17
+ # Usage: curation-nurse [--dry-run]
18
+ #
19
+ # Output: {observations_updated: N, instincts_updated: N, dry_run: bool}
20
+ # ============================================================================
21
+ _curation_nurse() {
22
+ local dry_run="false"
23
+
24
+ while [[ $# -gt 0 ]]; do
25
+ case "$1" in
26
+ --dry-run) dry_run="true"; shift ;;
27
+ *) shift ;;
28
+ esac
29
+ done
30
+
31
+ local obs_updated=0
32
+ local inst_updated=0
33
+ local obs_file="$COLONY_DATA_DIR/learning-observations.json"
34
+ local inst_file="$COLONY_DATA_DIR/instincts.json"
35
+ local now_epoch
36
+ now_epoch=$(date -u +%s)
37
+
38
+ # ── Recalculate observation trust scores ──────────────────────────────────
39
+ if [[ -f "$obs_file" ]]; then
40
+ local obs_count
41
+ obs_count=$(jq '[.observations[] | select(.source_type != null and .evidence_type != null)] | length' "$obs_file" 2>/dev/null || echo 0)
42
+
43
+ if [[ "$obs_count" -gt 0 ]]; then
44
+ local updated_obs
45
+ updated_obs=$(jq -c '[.observations[]]' "$obs_file" 2>/dev/null || echo "[]")
46
+
47
+ local new_obs_array="[]"
48
+ while IFS= read -r obs_json; do
49
+ local source_type evidence_type last_seen
50
+ source_type=$(echo "$obs_json" | jq -r '.source_type // empty')
51
+ evidence_type=$(echo "$obs_json" | jq -r '.evidence_type // empty')
52
+ last_seen=$(echo "$obs_json" | jq -r '.last_seen // empty')
53
+
54
+ if [[ -z "$source_type" || -z "$evidence_type" ]]; then
55
+ new_obs_array=$(echo "$new_obs_array" | jq --argjson entry "$obs_json" '. += [$entry]')
56
+ continue
57
+ fi
58
+
59
+ local days_since=0
60
+ if [[ -n "$last_seen" ]]; then
61
+ local last_epoch
62
+ last_epoch=$(date -u -d "$last_seen" +%s 2>/dev/null || date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$last_seen" +%s 2>/dev/null || echo "$now_epoch")
63
+ days_since=$(( (now_epoch - last_epoch) / 86400 ))
64
+ [[ "$days_since" -lt 0 ]] && days_since=0
65
+ fi
66
+
67
+ local trust_result trust_score trust_tier
68
+ trust_result=$(_trust_calculate --source "$source_type" --evidence "$evidence_type" --days-since "$days_since" 2>/dev/null) || true
69
+ if echo "$trust_result" | jq -e '.ok == true' >/dev/null 2>&1; then
70
+ trust_score=$(echo "$trust_result" | jq -r '.result.score')
71
+ trust_tier=$(echo "$trust_result" | jq -r '.result.tier')
72
+ local updated_entry
73
+ updated_entry=$(echo "$obs_json" | jq \
74
+ --argjson score "$trust_score" \
75
+ --arg tier "$trust_tier" \
76
+ '. + {trust_score: $score, trust_tier: $tier}')
77
+ new_obs_array=$(echo "$new_obs_array" | jq --argjson entry "$updated_entry" '. += [$entry]')
78
+ obs_updated=$((obs_updated + 1))
79
+ else
80
+ new_obs_array=$(echo "$new_obs_array" | jq --argjson entry "$obs_json" '. += [$entry]')
81
+ fi
82
+ done < <(echo "$updated_obs" | jq -c '.[]')
83
+
84
+ if [[ "$dry_run" != "true" ]]; then
85
+ local final_obs
86
+ final_obs=$(jq --argjson obs "$new_obs_array" '.observations = $obs' "$obs_file" 2>/dev/null) || true
87
+ [[ -n "$final_obs" ]] && atomic_write "$obs_file" "$final_obs"
88
+ fi
89
+ fi
90
+ fi
91
+
92
+ # ── Apply trust-decay to instincts ────────────────────────────────────────
93
+ if [[ -f "$inst_file" ]]; then
94
+ local active_count
95
+ active_count=$(jq '[.instincts[] | select(.archived == false)] | length' "$inst_file" 2>/dev/null || echo 0)
96
+
97
+ if [[ "$active_count" -gt 0 ]]; then
98
+ local updated_inst_array="[]"
99
+ local all_inst
100
+ all_inst=$(jq -c '[.instincts[]]' "$inst_file" 2>/dev/null || echo "[]")
101
+
102
+ while IFS= read -r inst_json; do
103
+ local archived
104
+ archived=$(echo "$inst_json" | jq -r '.archived // false')
105
+
106
+ if [[ "$archived" == "true" ]]; then
107
+ updated_inst_array=$(echo "$updated_inst_array" | jq --argjson entry "$inst_json" '. += [$entry]')
108
+ continue
109
+ fi
110
+
111
+ local created_at days_inst=0
112
+ created_at=$(echo "$inst_json" | jq -r '.provenance.created_at // empty')
113
+ if [[ -n "$created_at" ]]; then
114
+ local created_epoch
115
+ created_epoch=$(date -u -d "$created_at" +%s 2>/dev/null || date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$created_at" +%s 2>/dev/null || echo "$now_epoch")
116
+ days_inst=$(( (now_epoch - created_epoch) / 86400 ))
117
+ [[ "$days_inst" -lt 0 ]] && days_inst=0
118
+ fi
119
+
120
+ local current_score
121
+ current_score=$(echo "$inst_json" | jq -r '.trust_score // 0.5')
122
+ local decay_result decayed_score new_tier
123
+ decay_result=$(_trust_decay --score "$current_score" --days "$days_inst" 2>/dev/null) || true
124
+
125
+ if echo "$decay_result" | jq -e '.ok == true' >/dev/null 2>&1; then
126
+ decayed_score=$(echo "$decay_result" | jq -r '.result.decayed')
127
+ new_tier=$(_trust_score_to_tier "$decayed_score" 2>/dev/null || echo "dormant")
128
+ local updated_entry
129
+ updated_entry=$(echo "$inst_json" | jq \
130
+ --argjson score "$decayed_score" \
131
+ --arg tier "$new_tier" \
132
+ '.trust_score = $score | .trust_tier = $tier')
133
+ updated_inst_array=$(echo "$updated_inst_array" | jq --argjson entry "$updated_entry" '. += [$entry]')
134
+ inst_updated=$((inst_updated + 1))
135
+ else
136
+ updated_inst_array=$(echo "$updated_inst_array" | jq --argjson entry "$inst_json" '. += [$entry]')
137
+ fi
138
+ done < <(echo "$all_inst" | jq -c '.[]')
139
+
140
+ if [[ "$dry_run" != "true" ]]; then
141
+ local final_inst
142
+ final_inst=$(jq --argjson insts "$updated_inst_array" '.instincts = $insts' "$inst_file" 2>/dev/null) || true
143
+ [[ -n "$final_inst" ]] && atomic_write "$inst_file" "$final_inst"
144
+ fi
145
+ fi
146
+ fi
147
+
148
+ json_ok "$(jq -n \
149
+ --argjson obs "$obs_updated" \
150
+ --argjson inst "$inst_updated" \
151
+ --argjson dry "$([ "$dry_run" == "true" ] && echo true || echo false)" \
152
+ '{observations_updated: $obs, instincts_updated: $inst, dry_run: $dry}')"
153
+ }
@@ -0,0 +1,181 @@
1
+ #!/bin/bash
2
+ # Curation Orchestrator — runs all 8 curation ants in sequence
3
+ # Provides: _curation_run
4
+ #
5
+ # These functions are sourced by aether-utils.sh at startup.
6
+ # All shared infrastructure (json_ok, json_err, COLONY_DATA_DIR, DATA_DIR,
7
+ # error constants) is available when sourced.
8
+
9
+ # ============================================================================
10
+ # _curation_run
11
+ # Run all 8 curation ants in the correct order.
12
+ #
13
+ # Usage: curation-run [--dry-run] [--verbose]
14
+ #
15
+ # Execution order:
16
+ # 1. sentinel — health check (abort if corrupt)
17
+ # 2. nurse — recalculate trust scores
18
+ # 3. critic — detect contradictions
19
+ # 4. herald — promote high-trust to QUEEN.md
20
+ # 5. janitor — clean expired events/archives
21
+ # 6. archivist — archive low-trust instincts
22
+ # 7. librarian — inventory stats
23
+ # 8. scribe — generate report
24
+ #
25
+ # Output: json_ok with {steps, total_steps, succeeded, failed, dry_run,
26
+ # report_path, duration_ms}
27
+ # ============================================================================
28
+ _curation_run() {
29
+ local dry_run="false"
30
+
31
+ while [[ $# -gt 0 ]]; do
32
+ case "$1" in
33
+ --dry-run) dry_run="true"; shift ;;
34
+ --verbose) shift ;;
35
+ *) shift ;;
36
+ esac
37
+ done
38
+
39
+ # Portable millisecond timer: try python3, fall back to seconds*1000
40
+ local start_ms
41
+ start_ms=$(python3 -c "import time; print(int(time.time()*1000))" 2>/dev/null \
42
+ || echo $(( $(date +%s) * 1000 )))
43
+
44
+ local steps_json="[]"
45
+ local succeeded=0
46
+ local failed=0
47
+ local report_path="null"
48
+ # Shared variable: holds the raw JSON result of the last step executed
49
+ local _CR_LAST_RESULT=""
50
+
51
+ # _cr_step <name> [extra_args...]
52
+ # Runs curation-<name> (with --dry-run if set), updates steps_json,
53
+ # succeeded/failed counters, and sets _CR_LAST_RESULT.
54
+ # Must be called WITHOUT command substitution so variable mutations persist.
55
+ _cr_step() {
56
+ local step_name="$1"
57
+ shift
58
+
59
+ local cmd="curation-${step_name}"
60
+
61
+ # Build the args list (avoid empty-array nounset issues)
62
+ local step_result step_status step_summary
63
+ if [[ "$dry_run" == "true" && $# -gt 0 ]]; then
64
+ step_result=$(bash "$0" "$cmd" "--dry-run" "$@" 2>/dev/null) || true
65
+ elif [[ "$dry_run" == "true" ]]; then
66
+ step_result=$(bash "$0" "$cmd" "--dry-run" 2>/dev/null) || true
67
+ elif [[ $# -gt 0 ]]; then
68
+ step_result=$(bash "$0" "$cmd" "$@" 2>/dev/null) || true
69
+ else
70
+ step_result=$(bash "$0" "$cmd" 2>/dev/null) || true
71
+ fi
72
+
73
+ _CR_LAST_RESULT="$step_result"
74
+
75
+ if echo "$step_result" | jq -e '.ok == true' >/dev/null 2>&1; then
76
+ step_status="ok"
77
+ step_summary=$(echo "$step_result" | jq -r '
78
+ .result |
79
+ if type == "object" then
80
+ [ to_entries[] | "\(.key) \(.value)" ] | join(", ")
81
+ else
82
+ tostring
83
+ end
84
+ ' 2>/dev/null | head -c 120 || echo "ok")
85
+ [[ -z "$step_summary" ]] && step_summary="ok"
86
+ succeeded=$(( succeeded + 1 ))
87
+ else
88
+ step_status="failed"
89
+ local err_msg
90
+ err_msg=$(echo "$step_result" | jq -r '.error // "unknown error"' 2>/dev/null || echo "unknown error")
91
+ step_summary="$err_msg"
92
+ failed=$(( failed + 1 ))
93
+ fi
94
+
95
+ steps_json=$(echo "$steps_json" | jq \
96
+ --arg name "$step_name" \
97
+ --arg status "$step_status" \
98
+ --arg summary "$step_summary" \
99
+ '. += [{"name": $name, "status": $status, "summary": $summary}]')
100
+ }
101
+
102
+ # ── Step 1: Sentinel — health check ─────────────────────────────────────
103
+ _cr_step "sentinel"
104
+
105
+ # Abort remaining steps if sentinel found critical corruption
106
+ local sentinel_status
107
+ sentinel_status=$(echo "$steps_json" | jq -r '.[-1].status')
108
+ if [[ "$sentinel_status" == "ok" ]]; then
109
+ local corrupt_count
110
+ corrupt_count=$(echo "$_CR_LAST_RESULT" | jq '[.result.checks[]? | select(.status == "corrupt")] | length' 2>/dev/null || echo 0)
111
+ if [[ "$corrupt_count" -gt 0 ]]; then
112
+ for skipped_step in nurse critic herald janitor archivist librarian scribe; do
113
+ steps_json=$(echo "$steps_json" | jq \
114
+ --arg name "$skipped_step" \
115
+ '. += [{"name": $name, "status": "skipped", "summary": "skipped: sentinel detected corrupt stores"}]')
116
+ done
117
+
118
+ local end_ms
119
+ end_ms=$(python3 -c "import time; print(int(time.time()*1000))" 2>/dev/null \
120
+ || echo $(( $(date +%s) * 1000 )))
121
+ local duration_ms=$(( end_ms - start_ms ))
122
+
123
+ json_ok "$(jq -n \
124
+ --argjson steps "$steps_json" \
125
+ --argjson total 8 \
126
+ --argjson succeeded "$succeeded" \
127
+ --argjson failed "$failed" \
128
+ --argjson dry "$([ "$dry_run" == "true" ] && echo true || echo false)" \
129
+ --argjson dur "$duration_ms" \
130
+ '{steps:$steps, total_steps:$total, succeeded:$succeeded, failed:$failed,
131
+ dry_run:$dry, report_path:null, duration_ms:$dur}')"
132
+ return
133
+ fi
134
+ fi
135
+
136
+ # ── Step 2: Nurse — recalculate trust scores ─────────────────────────────
137
+ _cr_step "nurse"
138
+
139
+ # ── Step 3: Critic — detect contradictions ───────────────────────────────
140
+ _cr_step "critic"
141
+
142
+ # ── Step 4: Herald — promote high-trust to QUEEN.md ─────────────────────
143
+ _cr_step "herald"
144
+
145
+ # ── Step 5: Janitor — clean expired events and archives ──────────────────
146
+ _cr_step "janitor"
147
+
148
+ # ── Step 6: Archivist — archive low-trust instincts ─────────────────────
149
+ _cr_step "archivist"
150
+
151
+ # ── Step 7: Librarian — inventory stats ──────────────────────────────────
152
+ _cr_step "librarian"
153
+
154
+ # ── Step 8: Scribe — generate report ─────────────────────────────────────
155
+ _cr_step "scribe"
156
+
157
+ # Extract report_path from scribe result
158
+ if echo "$_CR_LAST_RESULT" | jq -e '.ok == true' >/dev/null 2>&1; then
159
+ local raw_path
160
+ raw_path=$(echo "$_CR_LAST_RESULT" | jq -r '.result.report_path // empty' 2>/dev/null || echo "")
161
+ if [[ -n "$raw_path" ]]; then
162
+ report_path=$(printf '%s' "$raw_path" | jq -Rs '.')
163
+ fi
164
+ fi
165
+
166
+ local end_ms
167
+ end_ms=$(python3 -c "import time; print(int(time.time()*1000))" 2>/dev/null \
168
+ || echo $(( $(date +%s) * 1000 )))
169
+ local duration_ms=$(( end_ms - start_ms ))
170
+
171
+ json_ok "$(jq -n \
172
+ --argjson steps "$steps_json" \
173
+ --argjson total 8 \
174
+ --argjson succeeded "$succeeded" \
175
+ --argjson failed "$failed" \
176
+ --argjson dry "$([ "$dry_run" == "true" ] && echo true || echo false)" \
177
+ --argjson report_path "$report_path" \
178
+ --argjson dur "$duration_ms" \
179
+ '{steps:$steps, total_steps:$total, succeeded:$succeeded, failed:$failed,
180
+ dry_run:$dry, report_path:$report_path, duration_ms:$dur}')"
181
+ }
@@ -0,0 +1,164 @@
1
+ #!/bin/bash
2
+ # Curation Scribe — Memory Consolidation Report Generation
3
+ # Generates a markdown report of the memory consolidation state.
4
+ #
5
+ # Functions:
6
+ # _curation_scribe
7
+ #
8
+ # These functions are sourced by aether-utils.sh at startup.
9
+ # All shared infrastructure (json_ok, json_err, COLONY_DATA_DIR, DATA_DIR,
10
+ # error constants) is available when sourced.
11
+
12
+ # ============================================================================
13
+ # _curation_scribe
14
+ # Generate a markdown report of memory consolidation state.
15
+ # Usage: curation-scribe [--output <path>]
16
+ #
17
+ # Default output: $COLONY_DATA_DIR/curation-report.md
18
+ # Output: json_ok with {report_path:string, sections:N, generated_at:"ISO"}
19
+ # ============================================================================
20
+ _curation_scribe() {
21
+ local csc_output=""
22
+
23
+ while [[ $# -gt 0 ]]; do
24
+ case "$1" in
25
+ --output)
26
+ csc_output="${2:-}"
27
+ shift 2
28
+ ;;
29
+ *)
30
+ shift
31
+ ;;
32
+ esac
33
+ done
34
+
35
+ local csc_data_dir="${COLONY_DATA_DIR:-${DATA_DIR:-}}"
36
+ if [[ -z "$csc_data_dir" ]]; then
37
+ json_err "$E_VALIDATION_FAILED" "curation-scribe: COLONY_DATA_DIR is not set"
38
+ fi
39
+
40
+ [[ -z "$csc_output" ]] && csc_output="$csc_data_dir/curation-report.md"
41
+
42
+ local csc_ts
43
+ csc_ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
44
+ local csc_ts_human
45
+ csc_ts_human=$(date -u +"%Y-%m-%d %H:%M UTC")
46
+
47
+ # Step 1: Gather librarian stats (via curation-librarian subcommand if available)
48
+ local csc_total_instincts=0
49
+ local csc_active_instincts=0
50
+ local csc_archived_instincts=0
51
+ local csc_total_observations=0
52
+ local csc_total_events=0
53
+
54
+ local csc_lib_result
55
+ if csc_lib_result=$(COLONY_DATA_DIR="$csc_data_dir" DATA_DIR="$csc_data_dir" \
56
+ bash "$0" curation-librarian 2>/dev/null); then
57
+ if echo "$csc_lib_result" | jq -e '.ok == true' >/dev/null 2>&1; then
58
+ csc_total_instincts=$(echo "$csc_lib_result" | jq '.result.total_instincts // 0')
59
+ csc_active_instincts=$(echo "$csc_lib_result" | jq '.result.active_instincts // 0')
60
+ csc_archived_instincts=$(echo "$csc_lib_result"| jq '.result.archived_instincts // 0')
61
+ csc_total_observations=$(echo "$csc_lib_result"| jq '.result.total_observations // 0')
62
+ csc_total_events=$(echo "$csc_lib_result" | jq '.result.total_events // 0')
63
+ fi
64
+ else
65
+ # Gather stats directly if librarian is not yet available
66
+ local csc_instincts_file="$csc_data_dir/instincts.json"
67
+ if [[ -f "$csc_instincts_file" ]] && jq empty "$csc_instincts_file" 2>/dev/null; then
68
+ csc_total_instincts=$(jq '.instincts | length' "$csc_instincts_file" 2>/dev/null || echo 0)
69
+ csc_active_instincts=$(jq '[.instincts[] | select(.archived != true)] | length' "$csc_instincts_file" 2>/dev/null || echo 0)
70
+ csc_archived_instincts=$(jq '[.instincts[] | select(.archived == true)] | length' "$csc_instincts_file" 2>/dev/null || echo 0)
71
+ fi
72
+
73
+ local csc_obs_file="$csc_data_dir/learning-observations.json"
74
+ if [[ -f "$csc_obs_file" ]] && jq empty "$csc_obs_file" 2>/dev/null; then
75
+ csc_total_observations=$(jq '.observations | length' "$csc_obs_file" 2>/dev/null || echo 0)
76
+ fi
77
+
78
+ local csc_eb_file="$csc_data_dir/event-bus.jsonl"
79
+ if [[ -f "$csc_eb_file" ]]; then
80
+ csc_total_events=$(wc -l < "$csc_eb_file" | tr -d ' ')
81
+ fi
82
+ fi
83
+
84
+ # Step 2: Gather top 5 trusted instincts
85
+ local csc_top_instincts=""
86
+ local csc_instincts_file="$csc_data_dir/instincts.json"
87
+ if [[ -f "$csc_instincts_file" ]] && jq empty "$csc_instincts_file" 2>/dev/null; then
88
+ csc_top_instincts=$(jq -r \
89
+ '[.instincts[] | select(.archived != true)] | sort_by(-.trust_score // -.confidence // 0) | .[0:5][] |
90
+ "- **[\(.trust_score // .confidence // 0 | . * 100 | floor)%]** \(.trigger // "unknown trigger")"' \
91
+ "$csc_instincts_file" 2>/dev/null || echo "")
92
+ fi
93
+ [[ -z "$csc_top_instincts" ]] && csc_top_instincts="_No instincts found._"
94
+
95
+ # Step 3: Gather recent events (last 10)
96
+ local csc_recent_events=""
97
+ local csc_eb_file="$csc_data_dir/event-bus.jsonl"
98
+ if [[ -f "$csc_eb_file" ]] && [[ -s "$csc_eb_file" ]]; then
99
+ csc_recent_events=$(tail -10 "$csc_eb_file" | \
100
+ jq -r '"- [\(.timestamp // "?")] **\(.topic // "unknown")**: \(.source // "system")"' \
101
+ 2>/dev/null || echo "")
102
+ fi
103
+ [[ -z "$csc_recent_events" ]] && csc_recent_events="_No recent events._"
104
+
105
+ # Step 4: Generate recommendations
106
+ local csc_recommendations=""
107
+ if [[ "$csc_active_instincts" -gt 40 ]]; then
108
+ csc_recommendations+="- Run \`curation-archivist\` to archive low-trust instincts (capacity at ${csc_active_instincts}/50)."$'\n'
109
+ fi
110
+ if [[ "$csc_total_events" -gt 100 ]]; then
111
+ csc_recommendations+="- Run \`curation-janitor\` to clean up expired events (${csc_total_events} events on bus)."$'\n'
112
+ fi
113
+ if [[ "$csc_total_observations" -gt 200 ]]; then
114
+ csc_recommendations+="- Consider pruning stale learning observations (${csc_total_observations} total)."$'\n'
115
+ fi
116
+ [[ -z "$csc_recommendations" ]] && csc_recommendations="_Memory stores are healthy. No immediate action needed._"
117
+
118
+ # Step 5: Write report
119
+ mkdir -p "$(dirname "$csc_output")"
120
+
121
+ cat > "$csc_output" <<REPORT
122
+ # Memory Consolidation Report
123
+
124
+ _Generated: ${csc_ts_human}_
125
+
126
+ ---
127
+
128
+ ## Memory Health Summary
129
+
130
+ | Store | Count |
131
+ |-------|-------|
132
+ | Total instincts | ${csc_total_instincts} |
133
+ | Active instincts | ${csc_active_instincts} |
134
+ | Archived instincts | ${csc_archived_instincts} |
135
+ | Learning observations | ${csc_total_observations} |
136
+ | Events on bus | ${csc_total_events} |
137
+
138
+ ---
139
+
140
+ ## Top Trusted Instincts
141
+
142
+ ${csc_top_instincts}
143
+
144
+ ---
145
+
146
+ ## Recent Events
147
+
148
+ ${csc_recent_events}
149
+
150
+ ---
151
+
152
+ ## Recommendations
153
+
154
+ ${csc_recommendations}
155
+ REPORT
156
+
157
+ local csc_sections=4
158
+
159
+ json_ok "$(jq -nc \
160
+ --arg report_path "$csc_output" \
161
+ --argjson sections "$csc_sections" \
162
+ --arg generated_at "$csc_ts" \
163
+ '{report_path:$report_path, sections:$sections, generated_at:$generated_at}')"
164
+ }