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,127 @@
1
+ #!/bin/bash
2
+ # Consolidation utility — lightweight end-of-phase consolidation
3
+ # Provides: _consolidation_phase_end
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: consolidation-phase-end [--dry-run]
10
+ # Runs nurse + herald + janitor curation ants in sequence,
11
+ # then publishes a consolidation.phase_end event to the event bus.
12
+ # Each step is non-blocking: failures are logged and execution continues.
13
+
14
+ # ============================================================================
15
+ # _consolidation_phase_end
16
+ # Run lightweight end-of-phase consolidation.
17
+ #
18
+ # Usage: consolidation-phase-end [--dry-run]
19
+ #
20
+ # Output: json_ok with:
21
+ # {type, steps:[{name, status, summary}], event_published, dry_run}
22
+ # ============================================================================
23
+ _consolidation_phase_end() {
24
+ local dry_run="false"
25
+
26
+ while [[ $# -gt 0 ]]; do
27
+ case "$1" in
28
+ --dry-run) dry_run="true"; shift ;;
29
+ *) shift ;;
30
+ esac
31
+ done
32
+
33
+ local dry_run_flag=""
34
+ [[ "$dry_run" == "true" ]] && dry_run_flag="--dry-run"
35
+
36
+ local steps_json="[]"
37
+
38
+ # ── Step 1: curation-nurse (trust recalculation) ──────────────────────────
39
+ local nurse_status="ok"
40
+ local nurse_summary=""
41
+ local nurse_result
42
+ # shellcheck disable=SC2086
43
+ nurse_result=$(bash "$0" curation-nurse $dry_run_flag 2>/dev/null) || nurse_status="failed"
44
+
45
+ if [[ "$nurse_status" == "ok" ]]; then
46
+ nurse_summary=$(echo "$nurse_result" | jq -r \
47
+ '"observations_updated=\(.result.observations_updated // 0) instincts_updated=\(.result.instincts_updated // 0)"' \
48
+ 2>/dev/null || echo "completed")
49
+ else
50
+ nurse_summary="nurse step failed; continuing"
51
+ fi
52
+
53
+ steps_json=$(echo "$steps_json" | jq -c \
54
+ --arg name "nurse" \
55
+ --arg status "$nurse_status" \
56
+ --arg summary "$nurse_summary" \
57
+ '. + [{name:$name,status:$status,summary:$summary}]')
58
+
59
+ # ── Step 2: curation-herald (promote high-trust to QUEEN.md) ─────────────
60
+ local herald_status="ok"
61
+ local herald_summary=""
62
+ local herald_result
63
+ # shellcheck disable=SC2086
64
+ herald_result=$(bash "$0" curation-herald $dry_run_flag 2>/dev/null) || herald_status="failed"
65
+
66
+ if [[ "$herald_status" == "ok" ]]; then
67
+ herald_summary=$(echo "$herald_result" | jq -r \
68
+ '"eligible=\(.result.eligible // 0) promoted=\(.result.promoted // 0)"' \
69
+ 2>/dev/null || echo "completed")
70
+ else
71
+ herald_summary="herald step failed; continuing"
72
+ fi
73
+
74
+ steps_json=$(echo "$steps_json" | jq -c \
75
+ --arg name "herald" \
76
+ --arg status "$herald_status" \
77
+ --arg summary "$herald_summary" \
78
+ '. + [{name:$name,status:$status,summary:$summary}]')
79
+
80
+ # ── Step 3: curation-janitor (clean expired) ─────────────────────────────
81
+ local janitor_status="ok"
82
+ local janitor_summary=""
83
+ local janitor_result
84
+ # shellcheck disable=SC2086
85
+ janitor_result=$(bash "$0" curation-janitor $dry_run_flag 2>/dev/null) || janitor_status="failed"
86
+
87
+ if [[ "$janitor_status" == "ok" ]]; then
88
+ janitor_summary=$(echo "$janitor_result" | jq -r \
89
+ '"events_removed=\(.result.events_removed // 0) instincts_pruned=\(.result.instincts_pruned // 0)"' \
90
+ 2>/dev/null || echo "completed")
91
+ else
92
+ janitor_summary="janitor step failed; continuing"
93
+ fi
94
+
95
+ steps_json=$(echo "$steps_json" | jq -c \
96
+ --arg name "janitor" \
97
+ --arg status "$janitor_status" \
98
+ --arg summary "$janitor_summary" \
99
+ '. + [{name:$name,status:$status,summary:$summary}]')
100
+
101
+ # ── Step 4: publish event to event bus ───────────────────────────────────
102
+ local event_published="false"
103
+
104
+ local current_phase
105
+ current_phase=$(jq -r '.current_phase // 0' "$COLONY_DATA_DIR/COLONY_STATE.json" 2>/dev/null || echo "0")
106
+
107
+ local payload
108
+ payload=$(jq -nc \
109
+ --argjson phase "$current_phase" \
110
+ --argjson dry_run "$dry_run" \
111
+ --argjson steps "$steps_json" \
112
+ '{phase:$phase,dry_run:$dry_run,steps:$steps}')
113
+
114
+ if bash "$0" event-publish \
115
+ --topic "consolidation.phase_end" \
116
+ --payload "$payload" \
117
+ --source "consolidation" \
118
+ > /dev/null 2>&1; then
119
+ event_published="true"
120
+ fi
121
+
122
+ json_ok "$(jq -nc \
123
+ --argjson steps "$steps_json" \
124
+ --argjson event_published "$event_published" \
125
+ --argjson dry_run "$dry_run" \
126
+ '{type:"phase_end",steps:$steps,event_published:$event_published,dry_run:$dry_run}')"
127
+ }
@@ -0,0 +1,97 @@
1
+ #!/bin/bash
2
+ # Curation Archivist — Low-Trust Instinct Archival
3
+ # Archives instincts that have decayed below a trust threshold.
4
+ #
5
+ # Functions:
6
+ # _curation_archivist
7
+ #
8
+ # These functions are sourced by aether-utils.sh at startup.
9
+ # All shared infrastructure (json_ok, json_err, atomic_write, COLONY_DATA_DIR,
10
+ # DATA_DIR, error constants) is available when sourced.
11
+
12
+ # ============================================================================
13
+ # _curation_archivist
14
+ # Archive active instincts with trust_score below threshold.
15
+ # Usage: curation-archivist [--threshold <float>] [--dry-run]
16
+ #
17
+ # Default threshold: 0.25
18
+ # Output: json_ok with {archived:N, below_threshold:N, dry_run:bool}
19
+ # ============================================================================
20
+ _curation_archivist() {
21
+ local ca_threshold="0.25"
22
+ local ca_dry_run="false"
23
+
24
+ while [[ $# -gt 0 ]]; do
25
+ case "$1" in
26
+ --threshold)
27
+ ca_threshold="${2:-0.25}"
28
+ shift 2
29
+ ;;
30
+ --dry-run)
31
+ ca_dry_run="true"
32
+ shift
33
+ ;;
34
+ *)
35
+ shift
36
+ ;;
37
+ esac
38
+ done
39
+
40
+ local ca_data_dir="${COLONY_DATA_DIR:-${DATA_DIR:-}}"
41
+ if [[ -z "$ca_data_dir" ]]; then
42
+ json_err "$E_VALIDATION_FAILED" "curation-archivist: COLONY_DATA_DIR is not set"
43
+ fi
44
+
45
+ local ca_instincts_file="$ca_data_dir/instincts.json"
46
+
47
+ # No instincts file — nothing to archive
48
+ if [[ ! -f "$ca_instincts_file" ]]; then
49
+ json_ok "$(jq -nc \
50
+ --argjson archived 0 \
51
+ --argjson below_threshold 0 \
52
+ --argjson dry_run "$ca_dry_run" \
53
+ '{archived:$archived, below_threshold:$below_threshold, dry_run:$dry_run}')"
54
+ return 0
55
+ fi
56
+
57
+ if ! jq empty "$ca_instincts_file" 2>/dev/null; then
58
+ json_err "$E_JSON_INVALID" "curation-archivist: instincts.json is not valid JSON"
59
+ fi
60
+
61
+ # Count active instincts below threshold
62
+ local ca_below_threshold
63
+ ca_below_threshold=$(jq --argjson thresh "$ca_threshold" \
64
+ '[.instincts[] | select(.archived != true and (.trust_score // .confidence // 0) < $thresh)] | length' \
65
+ "$ca_instincts_file" 2>/dev/null || echo 0)
66
+
67
+ local ca_archived=0
68
+
69
+ if [[ "$ca_dry_run" == "false" && "$ca_below_threshold" -gt 0 ]]; then
70
+ local ca_ts
71
+ ca_ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
72
+
73
+ local ca_updated
74
+ ca_updated=$(jq \
75
+ --argjson thresh "$ca_threshold" \
76
+ --arg ts "$ca_ts" \
77
+ '.instincts |= [.[] | if (.archived != true and (.trust_score // .confidence // 0) < $thresh)
78
+ then . + {archived: true, updated_at: $ts}
79
+ else .
80
+ end]' \
81
+ "$ca_instincts_file" 2>/dev/null)
82
+
83
+ if [[ -n "$ca_updated" ]]; then
84
+ atomic_write "$ca_instincts_file" "$ca_updated" 2>/dev/null \
85
+ || json_err "$E_UNKNOWN" "curation-archivist: failed to write instincts.json"
86
+ ca_archived="$ca_below_threshold"
87
+ fi
88
+ elif [[ "$ca_dry_run" == "true" ]]; then
89
+ ca_archived="$ca_below_threshold"
90
+ fi
91
+
92
+ json_ok "$(jq -nc \
93
+ --argjson archived "$ca_archived" \
94
+ --argjson below_threshold "$ca_below_threshold" \
95
+ --argjson dry_run "$ca_dry_run" \
96
+ '{archived:$archived, below_threshold:$below_threshold, dry_run:$dry_run}')"
97
+ }
@@ -0,0 +1,214 @@
1
+ #!/bin/bash
2
+ # Critic curation ant — contradiction detection between instincts
3
+ # Provides: _curation_critic
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, error constants) is available.
8
+ #
9
+ # Subcommand: curation-critic [--auto-resolve]
10
+ # Finds contradicting instincts using text heuristics and graph edges.
11
+
12
+ # ============================================================================
13
+ # _curation_critic_opposing_words
14
+ # Internal helper: check if two action strings oppose each other.
15
+ # Detects "always"/"never" and "do"/"don't" opposites in the same domain.
16
+ # Outputs "true" if opposing, "false" otherwise.
17
+ # ============================================================================
18
+ _curation_critic_opposing_words() {
19
+ local action_a="$1"
20
+ local action_b="$2"
21
+
22
+ local low_a low_b
23
+ low_a=$(echo "$action_a" | tr '[:upper:]' '[:lower:]')
24
+ low_b=$(echo "$action_b" | tr '[:upper:]' '[:lower:]')
25
+
26
+ # "always" in one and "never" in the other
27
+ if [[ "$low_a" == *"always"* && "$low_b" == *"never"* ]]; then
28
+ echo "true"; return
29
+ fi
30
+ if [[ "$low_a" == *"never"* && "$low_b" == *"always"* ]]; then
31
+ echo "true"; return
32
+ fi
33
+
34
+ # "don't" or "do not" in one and plain action (no negation) in the other
35
+ if [[ "$low_a" == *"don't"* || "$low_a" == *"do not"* ]]; then
36
+ # Strip negation from a: "don't add X" -> "add X" — check overlap with b
37
+ local stripped_a
38
+ stripped_a=$(echo "$low_a" | sed "s/don't //g; s/do not //g")
39
+ if [[ "$low_b" == *"$stripped_a"* || "$stripped_a" == *"$low_b"* ]]; then
40
+ # Only flag if they share substantial content (>= 4 chars of overlap)
41
+ local overlap_len=${#stripped_a}
42
+ [[ "$overlap_len" -ge 4 ]] && echo "true" && return
43
+ fi
44
+ fi
45
+ if [[ "$low_b" == *"don't"* || "$low_b" == *"do not"* ]]; then
46
+ local stripped_b
47
+ stripped_b=$(echo "$low_b" | sed "s/don't //g; s/do not //g")
48
+ if [[ "$low_a" == *"$stripped_b"* || "$stripped_b" == *"$low_a"* ]]; then
49
+ local overlap_len=${#stripped_b}
50
+ [[ "$overlap_len" -ge 4 ]] && echo "true" && return
51
+ fi
52
+ fi
53
+
54
+ echo "false"
55
+ }
56
+
57
+ # ============================================================================
58
+ # _curation_critic
59
+ # Find contradicting instincts and optionally auto-resolve them.
60
+ #
61
+ # Usage: curation-critic [--auto-resolve]
62
+ #
63
+ # Contradiction criteria:
64
+ # - Same domain
65
+ # - Opposing triggers (same trigger text) with opposing action keywords
66
+ # - OR existing "contradicts" edge in the graph
67
+ #
68
+ # Auto-resolve: archive the lower-trust instinct, create "contradicts" graph edge.
69
+ #
70
+ # Output: {contradictions: [{instinct_a, instinct_b, reason, resolved}], count: N}
71
+ # ============================================================================
72
+ _curation_critic() {
73
+ local auto_resolve="false"
74
+
75
+ while [[ $# -gt 0 ]]; do
76
+ case "$1" in
77
+ --auto-resolve) auto_resolve="true"; shift ;;
78
+ *) shift ;;
79
+ esac
80
+ done
81
+
82
+ local inst_file="$COLONY_DATA_DIR/instincts.json"
83
+
84
+ if [[ ! -f "$inst_file" ]]; then
85
+ json_ok '{"contradictions":[],"count":0}'
86
+ return
87
+ fi
88
+
89
+ # Load all active instincts into a bash-friendly form
90
+ local active_instincts
91
+ active_instincts=$(jq -c '[.instincts[] | select(.archived == false)]' "$inst_file" 2>/dev/null || echo "[]")
92
+
93
+ local total_active
94
+ total_active=$(echo "$active_instincts" | jq 'length')
95
+
96
+ local contradictions_json="[]"
97
+ local resolved_ids=()
98
+
99
+ if [[ "$total_active" -ge 2 ]]; then
100
+ # Compare all pairs using indexed access
101
+ local i=0
102
+ while [[ $i -lt $((total_active - 1)) ]]; do
103
+ local inst_a
104
+ inst_a=$(echo "$active_instincts" | jq -c ".[$i]")
105
+ local id_a domain_a trigger_a action_a score_a
106
+ id_a=$(echo "$inst_a" | jq -r '.id')
107
+ domain_a=$(echo "$inst_a" | jq -r '.domain // ""')
108
+ trigger_a=$(echo "$inst_a" | jq -r '.trigger // ""')
109
+ action_a=$(echo "$inst_a" | jq -r '.action // ""')
110
+ score_a=$(echo "$inst_a" | jq -r '.trust_score // 0')
111
+
112
+ local j=$((i + 1))
113
+ while [[ $j -lt $total_active ]]; do
114
+ local inst_b
115
+ inst_b=$(echo "$active_instincts" | jq -c ".[$j]")
116
+ local id_b domain_b trigger_b action_b score_b
117
+ id_b=$(echo "$inst_b" | jq -r '.id')
118
+ domain_b=$(echo "$inst_b" | jq -r '.domain // ""')
119
+ trigger_b=$(echo "$inst_b" | jq -r '.trigger // ""')
120
+ action_b=$(echo "$inst_b" | jq -r '.action // ""')
121
+ score_b=$(echo "$inst_b" | jq -r '.trust_score // 0')
122
+
123
+ local contradiction_reason=""
124
+
125
+ # Heuristic 1: Same domain + similar trigger + opposing actions
126
+ if [[ -n "$domain_a" && "$domain_a" == "$domain_b" ]]; then
127
+ # Check if triggers are similar (first 40 chars match or substantial overlap)
128
+ local trig_prefix_a trig_prefix_b
129
+ trig_prefix_a=$(echo "$trigger_a" | cut -c1-40 | tr '[:upper:]' '[:lower:]')
130
+ trig_prefix_b=$(echo "$trigger_b" | cut -c1-40 | tr '[:upper:]' '[:lower:]')
131
+
132
+ if [[ "$trig_prefix_a" == "$trig_prefix_b" && -n "$trig_prefix_a" ]]; then
133
+ local opposing
134
+ opposing=$(_curation_critic_opposing_words "$action_a" "$action_b")
135
+ if [[ "$opposing" == "true" ]]; then
136
+ contradiction_reason="same domain and trigger with opposing actions"
137
+ fi
138
+ fi
139
+ fi
140
+
141
+ # Heuristic 2: Check existing "contradicts" graph edge
142
+ if [[ -z "$contradiction_reason" ]]; then
143
+ local graph_file="$COLONY_DATA_DIR/instinct-graph.json"
144
+ if [[ -f "$graph_file" ]]; then
145
+ local has_edge
146
+ has_edge=$(jq -r \
147
+ --arg a "$id_a" --arg b "$id_b" \
148
+ '[.edges[] | select(
149
+ .relationship == "contradicts" and (
150
+ (.source == $a and .target == $b) or
151
+ (.source == $b and .target == $a)
152
+ )
153
+ )] | length' "$graph_file" 2>/dev/null || echo 0)
154
+ if [[ "$has_edge" -gt 0 ]]; then
155
+ contradiction_reason="existing contradicts graph edge"
156
+ fi
157
+ fi
158
+ fi
159
+
160
+ if [[ -n "$contradiction_reason" ]]; then
161
+ local resolved="false"
162
+
163
+ if [[ "$auto_resolve" == "true" ]]; then
164
+ # Archive the lower-trust instinct
165
+ local lower_id higher_id
166
+ local cmp
167
+ cmp=$(awk "BEGIN{print ($score_a >= $score_b)}" 2>/dev/null || echo "1")
168
+ if [[ "$cmp" == "1" ]]; then
169
+ lower_id="$id_b"
170
+ higher_id="$id_a"
171
+ else
172
+ lower_id="$id_a"
173
+ higher_id="$id_b"
174
+ fi
175
+
176
+ # Only archive if not already scheduled (avoid double-archive)
177
+ local already_resolving="false"
178
+ for rid in "${resolved_ids[@]:-}"; do
179
+ [[ "$rid" == "$lower_id" ]] && already_resolving="true" && break
180
+ done
181
+
182
+ if [[ "$already_resolving" != "true" ]]; then
183
+ _instinct_archive --id "$lower_id" >/dev/null 2>&1 || true
184
+ resolved_ids+=("$lower_id")
185
+ resolved="true"
186
+
187
+ # Create "contradicts" graph edge (best effort)
188
+ _graph_link --source "$higher_id" --target "$lower_id" \
189
+ --relationship contradicts >/dev/null 2>&1 || true
190
+ fi
191
+ fi
192
+
193
+ contradictions_json=$(echo "$contradictions_json" | jq \
194
+ --arg a "$id_a" \
195
+ --arg b "$id_b" \
196
+ --arg reason "$contradiction_reason" \
197
+ --argjson resolved "$([ "$resolved" == "true" ] && echo true || echo false)" \
198
+ '. += [{instinct_a: $a, instinct_b: $b, reason: $reason, resolved: $resolved}]')
199
+ fi
200
+
201
+ j=$((j + 1))
202
+ done
203
+ i=$((i + 1))
204
+ done
205
+ fi
206
+
207
+ local count
208
+ count=$(echo "$contradictions_json" | jq 'length')
209
+
210
+ json_ok "$(jq -n \
211
+ --argjson contradictions "$contradictions_json" \
212
+ --argjson count "$count" \
213
+ '{contradictions: $contradictions, count: $count}')"
214
+ }
@@ -0,0 +1,102 @@
1
+ #!/bin/bash
2
+ # Herald curation ant — QUEEN.md promotion of high-trust instincts
3
+ # Provides: _curation_herald
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, AETHER_ROOT, SCRIPT_DIR, error constants) is available.
8
+ #
9
+ # Subcommand: curation-herald [--min-trust <float>] [--dry-run]
10
+ # Promotes instincts with trust_score >= min-trust to QUEEN.md wisdom.
11
+
12
+ # ============================================================================
13
+ # _curation_herald
14
+ # Promote high-trust instincts to QUEEN.md.
15
+ #
16
+ # Usage: curation-herald [--min-trust <float>] [--dry-run]
17
+ # Default min-trust: 0.75
18
+ #
19
+ # Output: {eligible: N, promoted: N, already_in_queen: N, dry_run: bool}
20
+ # ============================================================================
21
+ _curation_herald() {
22
+ local min_trust="0.75"
23
+ local dry_run="false"
24
+
25
+ while [[ $# -gt 0 ]]; do
26
+ case "$1" in
27
+ --min-trust) min_trust="${2:-0.75}"; shift 2 ;;
28
+ --dry-run) dry_run="true"; shift ;;
29
+ *) shift ;;
30
+ esac
31
+ done
32
+
33
+ local inst_file="$COLONY_DATA_DIR/instincts.json"
34
+
35
+ if [[ ! -f "$inst_file" ]]; then
36
+ json_ok "$(jq -n --argjson dry "$([ "$dry_run" == "true" ] && echo true || echo false)" \
37
+ '{eligible: 0, promoted: 0, already_in_queen: 0, dry_run: $dry}')"
38
+ return
39
+ fi
40
+
41
+ # Collect eligible instincts: active, trust_score >= min_trust
42
+ local eligible_instincts
43
+ eligible_instincts=$(jq -c \
44
+ --argjson min "$min_trust" \
45
+ '[.instincts[] | select(.archived == false and .trust_score >= $min)]' \
46
+ "$inst_file" 2>/dev/null || echo "[]")
47
+
48
+ local eligible_count
49
+ eligible_count=$(echo "$eligible_instincts" | jq 'length')
50
+
51
+ local promoted=0
52
+ local already_in_queen=0
53
+
54
+ if [[ "$eligible_count" -gt 0 && "$dry_run" != "true" ]]; then
55
+ while IFS= read -r inst_json; do
56
+ local trigger action confidence domain
57
+ trigger=$(echo "$inst_json" | jq -r '.trigger // ""')
58
+ action=$(echo "$inst_json" | jq -r '.action // ""')
59
+ confidence=$(echo "$inst_json" | jq -r '.confidence // 0.75')
60
+ domain=$(echo "$inst_json" | jq -r '.domain // "workflow"')
61
+
62
+ [[ -z "$trigger" || -z "$action" ]] && continue
63
+
64
+ local promote_result
65
+ promote_result=$(_queen_promote_instinct "$trigger" "$action" "$confidence" "$domain" 2>/dev/null) || true
66
+
67
+ if echo "$promote_result" | jq -e '.ok == true' >/dev/null 2>&1; then
68
+ local was_promoted
69
+ was_promoted=$(echo "$promote_result" | jq -r '.result.promoted // false')
70
+ local reason
71
+ reason=$(echo "$promote_result" | jq -r '.result.reason // ""')
72
+
73
+ if [[ "$was_promoted" == "true" ]]; then
74
+ promoted=$((promoted + 1))
75
+ elif [[ "$reason" == "duplicate" ]]; then
76
+ already_in_queen=$((already_in_queen + 1))
77
+ fi
78
+ fi
79
+ done < <(echo "$eligible_instincts" | jq -c '.[]')
80
+ elif [[ "$eligible_count" -gt 0 && "$dry_run" == "true" ]]; then
81
+ # Dry run: count what would be promoted vs already present
82
+ local queen_file="${AETHER_ROOT:-}/.aether/QUEEN.md"
83
+ while IFS= read -r inst_json; do
84
+ local action
85
+ action=$(echo "$inst_json" | jq -r '.action // ""')
86
+ [[ -z "$action" ]] && continue
87
+
88
+ if [[ -f "$queen_file" ]] && grep -Fq -- "$action" "$queen_file" 2>/dev/null; then
89
+ already_in_queen=$((already_in_queen + 1))
90
+ else
91
+ promoted=$((promoted + 1))
92
+ fi
93
+ done < <(echo "$eligible_instincts" | jq -c '.[]')
94
+ fi
95
+
96
+ json_ok "$(jq -n \
97
+ --argjson eligible "$eligible_count" \
98
+ --argjson promoted "$promoted" \
99
+ --argjson already "$already_in_queen" \
100
+ --argjson dry "$([ "$dry_run" == "true" ] && echo true || echo false)" \
101
+ '{eligible: $eligible, promoted: $promoted, already_in_queen: $already, dry_run: $dry}')"
102
+ }
@@ -0,0 +1,121 @@
1
+ #!/bin/bash
2
+ # Curation Janitor — Expired Event/Archive Pruning
3
+ # Cleans up expired events and old archived instincts.
4
+ #
5
+ # Functions:
6
+ # _curation_janitor
7
+ #
8
+ # These functions are sourced by aether-utils.sh at startup.
9
+ # All shared infrastructure (json_ok, json_err, atomic_write, COLONY_DATA_DIR,
10
+ # DATA_DIR, error constants) is available when sourced.
11
+
12
+ # ============================================================================
13
+ # _curation_janitor
14
+ # Remove expired events and prune stale data.
15
+ # Usage: curation-janitor [--dry-run] [--max-age-days <N>]
16
+ #
17
+ # Default max-age-days: 90
18
+ # Output: json_ok with {events_removed:N, instincts_pruned:N,
19
+ # observations_pruned:N, dry_run:bool}
20
+ # ============================================================================
21
+ _curation_janitor() {
22
+ local cj_dry_run="false"
23
+ local cj_max_age_days=90
24
+
25
+ while [[ $# -gt 0 ]]; do
26
+ case "$1" in
27
+ --dry-run)
28
+ cj_dry_run="true"
29
+ shift
30
+ ;;
31
+ --max-age-days)
32
+ cj_max_age_days="${2:-90}"
33
+ shift 2
34
+ ;;
35
+ *)
36
+ shift
37
+ ;;
38
+ esac
39
+ done
40
+
41
+ local cj_data_dir="${COLONY_DATA_DIR:-${DATA_DIR:-}}"
42
+ if [[ -z "$cj_data_dir" ]]; then
43
+ json_err "$E_VALIDATION_FAILED" "curation-janitor: COLONY_DATA_DIR is not set"
44
+ fi
45
+
46
+ # Step 1: Clean expired events via event-cleanup subcommand
47
+ local cj_events_removed=0
48
+ local cj_cleanup_dry_run_flag=""
49
+ [[ "$cj_dry_run" == "true" ]] && cj_cleanup_dry_run_flag="--dry-run"
50
+
51
+ local cj_cleanup_result
52
+ if [[ -n "$cj_cleanup_dry_run_flag" ]]; then
53
+ cj_cleanup_result=$(COLONY_DATA_DIR="$cj_data_dir" DATA_DIR="$cj_data_dir" \
54
+ bash "$0" event-cleanup "$cj_cleanup_dry_run_flag" 2>/dev/null) || true
55
+ else
56
+ cj_cleanup_result=$(COLONY_DATA_DIR="$cj_data_dir" DATA_DIR="$cj_data_dir" \
57
+ bash "$0" event-cleanup 2>/dev/null) || true
58
+ fi
59
+ if echo "$cj_cleanup_result" | jq -e '.ok == true' >/dev/null 2>&1; then
60
+ cj_events_removed=$(echo "$cj_cleanup_result" | jq '.result.removed // 0' 2>/dev/null || echo 0)
61
+ fi
62
+
63
+ # Step 2: Prune archived instincts older than max-age-days
64
+ local cj_instincts_pruned=0
65
+ local cj_instincts_file="$cj_data_dir/instincts.json"
66
+
67
+ if [[ -f "$cj_instincts_file" ]] && jq empty "$cj_instincts_file" 2>/dev/null; then
68
+ local cj_cutoff
69
+ cj_cutoff=$(date -u -v"-${cj_max_age_days}d" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
70
+ || date -u -d "-${cj_max_age_days} days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
71
+ || echo "1970-01-01T00:00:00Z")
72
+
73
+ cj_instincts_pruned=$(jq --arg cutoff "$cj_cutoff" \
74
+ '[.instincts[] | select(.archived == true and (.updated_at // .created_at) < $cutoff)] | length' \
75
+ "$cj_instincts_file" 2>/dev/null || echo 0)
76
+
77
+ if [[ "$cj_dry_run" == "false" && "$cj_instincts_pruned" -gt 0 ]]; then
78
+ local cj_updated_instincts
79
+ cj_updated_instincts=$(jq --arg cutoff "$cj_cutoff" \
80
+ '.instincts |= [.[] | select(not (.archived == true and (.updated_at // .created_at) < $cutoff))]' \
81
+ "$cj_instincts_file" 2>/dev/null)
82
+ if [[ -n "$cj_updated_instincts" ]]; then
83
+ atomic_write "$cj_instincts_file" "$cj_updated_instincts" 2>/dev/null || true
84
+ fi
85
+ fi
86
+ fi
87
+
88
+ # Step 3: Prune stale learning-observations (observation_count=1, older than 90 days)
89
+ local cj_observations_pruned=0
90
+ local cj_obs_file="$cj_data_dir/learning-observations.json"
91
+ local cj_obs_cutoff_days=90
92
+
93
+ if [[ -f "$cj_obs_file" ]] && jq empty "$cj_obs_file" 2>/dev/null; then
94
+ local cj_obs_cutoff
95
+ cj_obs_cutoff=$(date -u -v"-${cj_obs_cutoff_days}d" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
96
+ || date -u -d "-${cj_obs_cutoff_days} days" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
97
+ || echo "1970-01-01T00:00:00Z")
98
+
99
+ cj_observations_pruned=$(jq --arg cutoff "$cj_obs_cutoff" \
100
+ '[.observations[] | select((.observation_count // 0) == 1 and (.last_observed // .observed_at // "") < $cutoff)] | length' \
101
+ "$cj_obs_file" 2>/dev/null || echo 0)
102
+
103
+ if [[ "$cj_dry_run" == "false" && "$cj_observations_pruned" -gt 0 ]]; then
104
+ local cj_updated_obs
105
+ cj_updated_obs=$(jq --arg cutoff "$cj_obs_cutoff" \
106
+ '.observations |= [.[] | select(not ((.observation_count // 0) == 1 and (.last_observed // .observed_at // "") < $cutoff))]' \
107
+ "$cj_obs_file" 2>/dev/null)
108
+ if [[ -n "$cj_updated_obs" ]]; then
109
+ atomic_write "$cj_obs_file" "$cj_updated_obs" 2>/dev/null || true
110
+ fi
111
+ fi
112
+ fi
113
+
114
+ json_ok "$(jq -nc \
115
+ --argjson events_removed "$cj_events_removed" \
116
+ --argjson instincts_pruned "$cj_instincts_pruned" \
117
+ --argjson observations_pruned "$cj_observations_pruned" \
118
+ --argjson dry_run "$cj_dry_run" \
119
+ '{events_removed:$events_removed, instincts_pruned:$instincts_pruned,
120
+ observations_pruned:$observations_pruned, dry_run:$dry_run}')"
121
+ }