aether-colony 5.3.2 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/.aether/aether-utils.sh +181 -5
  2. package/.aether/commands/archaeology.yaml +3 -3
  3. package/.aether/commands/build.yaml +80 -45
  4. package/.aether/commands/chaos.yaml +7 -7
  5. package/.aether/commands/colonize.yaml +17 -17
  6. package/.aether/commands/continue.yaml +40 -40
  7. package/.aether/commands/council.yaml +6 -6
  8. package/.aether/commands/data-clean.yaml +3 -3
  9. package/.aether/commands/dream.yaml +2 -2
  10. package/.aether/commands/entomb.yaml +12 -12
  11. package/.aether/commands/export-signals.yaml +2 -2
  12. package/.aether/commands/feedback.yaml +6 -6
  13. package/.aether/commands/flag.yaml +2 -2
  14. package/.aether/commands/flags.yaml +4 -4
  15. package/.aether/commands/focus.yaml +6 -6
  16. package/.aether/commands/help.yaml +1 -1
  17. package/.aether/commands/history.yaml +1 -1
  18. package/.aether/commands/import-signals.yaml +2 -2
  19. package/.aether/commands/init.yaml +44 -27
  20. package/.aether/commands/insert-phase.yaml +1 -1
  21. package/.aether/commands/interpret.yaml +2 -2
  22. package/.aether/commands/lay-eggs.yaml +3 -3
  23. package/.aether/commands/maturity.yaml +2 -2
  24. package/.aether/commands/memory-details.yaml +1 -1
  25. package/.aether/commands/migrate-state.yaml +1 -1
  26. package/.aether/commands/oracle.yaml +147 -82
  27. package/.aether/commands/organize.yaml +5 -5
  28. package/.aether/commands/patrol.yaml +8 -8
  29. package/.aether/commands/pause-colony.yaml +7 -7
  30. package/.aether/commands/phase.yaml +1 -1
  31. package/.aether/commands/pheromones.yaml +1 -1
  32. package/.aether/commands/plan.yaml +14 -14
  33. package/.aether/commands/quick.yaml +4 -4
  34. package/.aether/commands/redirect.yaml +6 -6
  35. package/.aether/commands/resume-colony.yaml +9 -9
  36. package/.aether/commands/resume.yaml +5 -38
  37. package/.aether/commands/run.yaml +10 -10
  38. package/.aether/commands/seal.yaml +33 -33
  39. package/.aether/commands/skill-create.yaml +4 -4
  40. package/.aether/commands/status.yaml +14 -14
  41. package/.aether/commands/swarm.yaml +14 -14
  42. package/.aether/commands/tunnels.yaml +7 -7
  43. package/.aether/commands/update.yaml +1 -1
  44. package/.aether/commands/verify-castes.yaml +3 -3
  45. package/.aether/commands/watch.yaml +15 -15
  46. package/.aether/docs/command-playbooks/build-complete.md +48 -15
  47. package/.aether/docs/command-playbooks/build-context.md +11 -11
  48. package/.aether/docs/command-playbooks/build-full.md +76 -76
  49. package/.aether/docs/command-playbooks/build-prep.md +10 -10
  50. package/.aether/docs/command-playbooks/build-verify.md +27 -27
  51. package/.aether/docs/command-playbooks/build-wave.md +38 -38
  52. package/.aether/docs/command-playbooks/continue-advance.md +60 -27
  53. package/.aether/docs/command-playbooks/continue-finalize.md +25 -11
  54. package/.aether/docs/command-playbooks/continue-full.md +60 -46
  55. package/.aether/docs/command-playbooks/continue-gates.md +18 -18
  56. package/.aether/docs/command-playbooks/continue-verify.md +10 -10
  57. package/.aether/docs/source-of-truth-map.md +10 -10
  58. package/.aether/docs/structural-learning-stack.md +283 -0
  59. package/.aether/templates/colony-state-template.json +1 -0
  60. package/.aether/utils/consolidation-seal.sh +196 -0
  61. package/.aether/utils/consolidation.sh +127 -0
  62. package/.aether/utils/curation-ants/archivist.sh +97 -0
  63. package/.aether/utils/curation-ants/critic.sh +214 -0
  64. package/.aether/utils/curation-ants/herald.sh +102 -0
  65. package/.aether/utils/curation-ants/janitor.sh +121 -0
  66. package/.aether/utils/curation-ants/librarian.sh +99 -0
  67. package/.aether/utils/curation-ants/nurse.sh +153 -0
  68. package/.aether/utils/curation-ants/orchestrator.sh +181 -0
  69. package/.aether/utils/curation-ants/scribe.sh +164 -0
  70. package/.aether/utils/curation-ants/sentinel.sh +119 -0
  71. package/.aether/utils/event-bus.sh +301 -0
  72. package/.aether/utils/graph.sh +559 -0
  73. package/.aether/utils/instinct-store.sh +401 -0
  74. package/.aether/utils/learning.sh +79 -7
  75. package/.aether/utils/oracle/oracle-stop-hook.sh +896 -0
  76. package/.aether/utils/session.sh +13 -0
  77. package/.aether/utils/state-api.sh +1 -1
  78. package/.aether/utils/trust-scoring.sh +347 -0
  79. package/.aether/utils/worktree.sh +97 -0
  80. package/.claude/commands/ant/archaeology.md +2 -2
  81. package/.claude/commands/ant/chaos.md +4 -4
  82. package/.claude/commands/ant/colonize.md +9 -9
  83. package/.claude/commands/ant/council.md +6 -6
  84. package/.claude/commands/ant/data-clean.md +3 -3
  85. package/.claude/commands/ant/dream.md +2 -2
  86. package/.claude/commands/ant/entomb.md +9 -9
  87. package/.claude/commands/ant/export-signals.md +2 -2
  88. package/.claude/commands/ant/feedback.md +4 -4
  89. package/.claude/commands/ant/flag.md +2 -2
  90. package/.claude/commands/ant/flags.md +4 -4
  91. package/.claude/commands/ant/focus.md +4 -4
  92. package/.claude/commands/ant/help.md +1 -1
  93. package/.claude/commands/ant/history.md +1 -1
  94. package/.claude/commands/ant/import-signals.md +2 -2
  95. package/.claude/commands/ant/init.md +44 -27
  96. package/.claude/commands/ant/insert-phase.md +1 -1
  97. package/.claude/commands/ant/interpret.md +2 -2
  98. package/.claude/commands/ant/lay-eggs.md +2 -2
  99. package/.claude/commands/ant/maturity.md +2 -2
  100. package/.claude/commands/ant/memory-details.md +1 -1
  101. package/.claude/commands/ant/migrate-state.md +1 -1
  102. package/.claude/commands/ant/oracle.md +78 -42
  103. package/.claude/commands/ant/organize.md +3 -3
  104. package/.claude/commands/ant/patrol.md +8 -8
  105. package/.claude/commands/ant/pause-colony.md +5 -5
  106. package/.claude/commands/ant/phase.md +1 -1
  107. package/.claude/commands/ant/pheromones.md +1 -1
  108. package/.claude/commands/ant/plan.md +8 -8
  109. package/.claude/commands/ant/quick.md +4 -4
  110. package/.claude/commands/ant/redirect.md +4 -4
  111. package/.claude/commands/ant/resume-colony.md +5 -5
  112. package/.claude/commands/ant/resume.md +17 -29
  113. package/.claude/commands/ant/run.md +10 -10
  114. package/.claude/commands/ant/seal.md +25 -25
  115. package/.claude/commands/ant/skill-create.md +2 -2
  116. package/.claude/commands/ant/status.md +14 -14
  117. package/.claude/commands/ant/swarm.md +14 -14
  118. package/.claude/commands/ant/tunnels.md +4 -4
  119. package/.claude/commands/ant/update.md +1 -1
  120. package/.claude/commands/ant/verify-castes.md +2 -2
  121. package/.claude/commands/ant/watch.md +8 -8
  122. package/.opencode/commands/ant/archaeology.md +1 -1
  123. package/.opencode/commands/ant/build.md +80 -45
  124. package/.opencode/commands/ant/chaos.md +3 -3
  125. package/.opencode/commands/ant/colonize.md +8 -8
  126. package/.opencode/commands/ant/continue.md +40 -40
  127. package/.opencode/commands/ant/council.md +5 -5
  128. package/.opencode/commands/ant/data-clean.md +2 -2
  129. package/.opencode/commands/ant/dream.md +1 -1
  130. package/.opencode/commands/ant/entomb.md +3 -3
  131. package/.opencode/commands/ant/export-signals.md +1 -1
  132. package/.opencode/commands/ant/feedback.md +2 -2
  133. package/.opencode/commands/ant/flag.md +1 -1
  134. package/.opencode/commands/ant/flags.md +3 -3
  135. package/.opencode/commands/ant/focus.md +2 -2
  136. package/.opencode/commands/ant/import-signals.md +1 -1
  137. package/.opencode/commands/ant/init.md +44 -27
  138. package/.opencode/commands/ant/insert-phase.md +1 -1
  139. package/.opencode/commands/ant/interpret.md +1 -1
  140. package/.opencode/commands/ant/lay-eggs.md +2 -2
  141. package/.opencode/commands/ant/maturity.md +1 -1
  142. package/.opencode/commands/ant/memory-details.md +1 -1
  143. package/.opencode/commands/ant/oracle.md +69 -40
  144. package/.opencode/commands/ant/organize.md +2 -2
  145. package/.opencode/commands/ant/patrol.md +8 -8
  146. package/.opencode/commands/ant/pause-colony.md +2 -2
  147. package/.opencode/commands/ant/pheromones.md +1 -1
  148. package/.opencode/commands/ant/plan.md +6 -6
  149. package/.opencode/commands/ant/quick.md +4 -4
  150. package/.opencode/commands/ant/redirect.md +2 -2
  151. package/.opencode/commands/ant/resume-colony.md +4 -4
  152. package/.opencode/commands/ant/resume.md +5 -17
  153. package/.opencode/commands/ant/run.md +10 -10
  154. package/.opencode/commands/ant/seal.md +8 -8
  155. package/.opencode/commands/ant/skill-create.md +2 -2
  156. package/.opencode/commands/ant/status.md +10 -10
  157. package/.opencode/commands/ant/tunnels.md +3 -3
  158. package/.opencode/commands/ant/verify-castes.md +1 -1
  159. package/.opencode/commands/ant/watch.md +7 -7
  160. package/CHANGELOG.md +83 -0
  161. package/README.md +22 -9
  162. package/bin/cli.js +118 -3
  163. package/bin/lib/binary-downloader.js +267 -0
  164. package/bin/lib/update-transaction.js +27 -3
  165. package/bin/lib/version-gate.js +179 -0
  166. package/bin/npx-entry.js +0 -0
  167. package/package.json +1 -1
  168. package/.aether/agents/aether-ambassador.md +0 -140
  169. package/.aether/agents/aether-archaeologist.md +0 -108
  170. package/.aether/agents/aether-architect.md +0 -133
  171. package/.aether/agents/aether-auditor.md +0 -144
  172. package/.aether/agents/aether-builder.md +0 -184
  173. package/.aether/agents/aether-chaos.md +0 -115
  174. package/.aether/agents/aether-chronicler.md +0 -122
  175. package/.aether/agents/aether-gatekeeper.md +0 -116
  176. package/.aether/agents/aether-includer.md +0 -117
  177. package/.aether/agents/aether-keeper.md +0 -177
  178. package/.aether/agents/aether-measurer.md +0 -128
  179. package/.aether/agents/aether-oracle.md +0 -137
  180. package/.aether/agents/aether-probe.md +0 -133
  181. package/.aether/agents/aether-queen.md +0 -286
  182. package/.aether/agents/aether-route-setter.md +0 -130
  183. package/.aether/agents/aether-sage.md +0 -106
  184. package/.aether/agents/aether-scout.md +0 -101
  185. package/.aether/agents/aether-surveyor-disciplines.md +0 -391
  186. package/.aether/agents/aether-surveyor-nest.md +0 -329
  187. package/.aether/agents/aether-surveyor-pathogens.md +0 -264
  188. package/.aether/agents/aether-surveyor-provisions.md +0 -334
  189. package/.aether/agents/aether-tracker.md +0 -137
  190. package/.aether/agents/aether-watcher.md +0 -174
  191. package/.aether/agents/aether-weaver.md +0 -130
  192. package/.aether/commands/claude/archaeology.md +0 -334
  193. package/.aether/commands/claude/build.md +0 -65
  194. package/.aether/commands/claude/chaos.md +0 -336
  195. package/.aether/commands/claude/colonize.md +0 -259
  196. package/.aether/commands/claude/continue.md +0 -60
  197. package/.aether/commands/claude/council.md +0 -507
  198. package/.aether/commands/claude/data-clean.md +0 -81
  199. package/.aether/commands/claude/dream.md +0 -268
  200. package/.aether/commands/claude/entomb.md +0 -498
  201. package/.aether/commands/claude/export-signals.md +0 -57
  202. package/.aether/commands/claude/feedback.md +0 -96
  203. package/.aether/commands/claude/flag.md +0 -151
  204. package/.aether/commands/claude/flags.md +0 -169
  205. package/.aether/commands/claude/focus.md +0 -76
  206. package/.aether/commands/claude/help.md +0 -154
  207. package/.aether/commands/claude/history.md +0 -140
  208. package/.aether/commands/claude/import-signals.md +0 -71
  209. package/.aether/commands/claude/init.md +0 -505
  210. package/.aether/commands/claude/insert-phase.md +0 -105
  211. package/.aether/commands/claude/interpret.md +0 -278
  212. package/.aether/commands/claude/lay-eggs.md +0 -210
  213. package/.aether/commands/claude/maturity.md +0 -113
  214. package/.aether/commands/claude/memory-details.md +0 -77
  215. package/.aether/commands/claude/migrate-state.md +0 -171
  216. package/.aether/commands/claude/oracle.md +0 -642
  217. package/.aether/commands/claude/organize.md +0 -232
  218. package/.aether/commands/claude/patrol.md +0 -620
  219. package/.aether/commands/claude/pause-colony.md +0 -233
  220. package/.aether/commands/claude/phase.md +0 -115
  221. package/.aether/commands/claude/pheromones.md +0 -156
  222. package/.aether/commands/claude/plan.md +0 -693
  223. package/.aether/commands/claude/preferences.md +0 -65
  224. package/.aether/commands/claude/quick.md +0 -100
  225. package/.aether/commands/claude/redirect.md +0 -76
  226. package/.aether/commands/claude/resume-colony.md +0 -197
  227. package/.aether/commands/claude/resume.md +0 -388
  228. package/.aether/commands/claude/run.md +0 -231
  229. package/.aether/commands/claude/seal.md +0 -774
  230. package/.aether/commands/claude/skill-create.md +0 -286
  231. package/.aether/commands/claude/status.md +0 -410
  232. package/.aether/commands/claude/swarm.md +0 -349
  233. package/.aether/commands/claude/tunnels.md +0 -426
  234. package/.aether/commands/claude/update.md +0 -132
  235. package/.aether/commands/claude/verify-castes.md +0 -143
  236. package/.aether/commands/claude/watch.md +0 -239
  237. package/.aether/commands/opencode/archaeology.md +0 -331
  238. package/.aether/commands/opencode/build.md +0 -1168
  239. package/.aether/commands/opencode/chaos.md +0 -329
  240. package/.aether/commands/opencode/colonize.md +0 -195
  241. package/.aether/commands/opencode/continue.md +0 -1436
  242. package/.aether/commands/opencode/council.md +0 -437
  243. package/.aether/commands/opencode/data-clean.md +0 -77
  244. package/.aether/commands/opencode/dream.md +0 -260
  245. package/.aether/commands/opencode/entomb.md +0 -377
  246. package/.aether/commands/opencode/export-signals.md +0 -54
  247. package/.aether/commands/opencode/feedback.md +0 -99
  248. package/.aether/commands/opencode/flag.md +0 -149
  249. package/.aether/commands/opencode/flags.md +0 -167
  250. package/.aether/commands/opencode/focus.md +0 -73
  251. package/.aether/commands/opencode/help.md +0 -157
  252. package/.aether/commands/opencode/history.md +0 -136
  253. package/.aether/commands/opencode/import-signals.md +0 -68
  254. package/.aether/commands/opencode/init.md +0 -518
  255. package/.aether/commands/opencode/insert-phase.md +0 -111
  256. package/.aether/commands/opencode/interpret.md +0 -272
  257. package/.aether/commands/opencode/lay-eggs.md +0 -213
  258. package/.aether/commands/opencode/maturity.md +0 -108
  259. package/.aether/commands/opencode/memory-details.md +0 -83
  260. package/.aether/commands/opencode/migrate-state.md +0 -165
  261. package/.aether/commands/opencode/oracle.md +0 -593
  262. package/.aether/commands/opencode/organize.md +0 -226
  263. package/.aether/commands/opencode/patrol.md +0 -626
  264. package/.aether/commands/opencode/pause-colony.md +0 -203
  265. package/.aether/commands/opencode/phase.md +0 -113
  266. package/.aether/commands/opencode/pheromones.md +0 -162
  267. package/.aether/commands/opencode/plan.md +0 -684
  268. package/.aether/commands/opencode/preferences.md +0 -71
  269. package/.aether/commands/opencode/quick.md +0 -91
  270. package/.aether/commands/opencode/redirect.md +0 -84
  271. package/.aether/commands/opencode/resume-colony.md +0 -190
  272. package/.aether/commands/opencode/resume.md +0 -394
  273. package/.aether/commands/opencode/run.md +0 -237
  274. package/.aether/commands/opencode/seal.md +0 -452
  275. package/.aether/commands/opencode/skill-create.md +0 -63
  276. package/.aether/commands/opencode/status.md +0 -307
  277. package/.aether/commands/opencode/swarm.md +0 -15
  278. package/.aether/commands/opencode/tunnels.md +0 -400
  279. package/.aether/commands/opencode/update.md +0 -127
  280. package/.aether/commands/opencode/verify-castes.md +0 -139
  281. package/.aether/commands/opencode/watch.md +0 -227
@@ -0,0 +1,896 @@
1
+ #!/bin/bash
2
+ # Oracle Ant - In-Session Stop Hook
3
+ # Intercepts session exit when Oracle research is active.
4
+ # Re-feeds phase-aware prompts to continue the research loop.
5
+ #
6
+ # Modeled on the Ralph Loop Stop hook pattern:
7
+ # - Checks .aether/oracle/.loop-active for loop state
8
+ # - Session isolation via session_id
9
+ # - Outputs {"decision":"block","reason":"..."} to re-feed prompt
10
+ # - Outputs nothing (exit 0) to allow normal stop
11
+ #
12
+ # Completion criteria (any triggers synthesis pass):
13
+ # - Max iterations reached
14
+ # - overall_confidence >= target_confidence
15
+ # - <oracle>COMPLETE</oracle> in last assistant message
16
+ # - Convergence detected (composite score >= threshold + low novelty)
17
+
18
+ set -euo pipefail
19
+
20
+ # ---------------------------------------------------------------------------
21
+ # Configuration
22
+ # ---------------------------------------------------------------------------
23
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24
+ AETHER_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
25
+ ORACLE_DIR="$AETHER_ROOT/.aether/oracle"
26
+ MARKER_FILE="$ORACLE_DIR/.loop-active"
27
+ STATE_FILE="$ORACLE_DIR/state.json"
28
+ PLAN_FILE="$ORACLE_DIR/plan.json"
29
+ STOP_FILE="$ORACLE_DIR/.stop"
30
+ ORACLE_MD="$SCRIPT_DIR/oracle.md"
31
+
32
+ # Convergence thresholds (matching oracle.sh defaults)
33
+ CONV_THRESHOLD=${ORACLE_CONVERGENCE_THRESHOLD:-85}
34
+ DR_WINDOW=${ORACLE_DR_WINDOW:-3}
35
+
36
+ # ---------------------------------------------------------------------------
37
+ # Fast exit: no active loop
38
+ # ---------------------------------------------------------------------------
39
+ if [[ ! -f "$MARKER_FILE" ]]; then
40
+ exit 0
41
+ fi
42
+
43
+ # ---------------------------------------------------------------------------
44
+ # Read hook input from stdin
45
+ # ---------------------------------------------------------------------------
46
+ HOOK_INPUT=$(cat)
47
+
48
+ INPUT_SESSION_ID=$(echo "$HOOK_INPUT" | jq -r '.session_id // ""')
49
+ TRANSCRIPT_PATH=$(echo "$HOOK_INPUT" | jq -r '.transcript_path // ""')
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Parse marker file frontmatter
53
+ # ---------------------------------------------------------------------------
54
+ FRONTMATTER=$(sed -n '/^---$/,/^---$/{ /^---$/d; p; }' "$MARKER_FILE")
55
+
56
+ ITERATION=$(echo "$FRONTMATTER" | grep '^iteration:' | sed 's/iteration: *//')
57
+ MAX_ITERATIONS=$(echo "$FRONTMATTER" | grep '^max_iterations:' | sed 's/max_iterations: *//')
58
+ MARKER_SESSION_ID=$(echo "$FRONTMATTER" | grep '^session_id:' | sed 's/session_id: *//' || true)
59
+ CURRENT_PHASE=$(echo "$FRONTMATTER" | grep '^phase:' | sed 's/phase: *//' || echo "survey")
60
+ TARGET_CONFIDENCE=$(echo "$FRONTMATTER" | grep '^target_confidence:' | sed 's/target_confidence: *//' || echo "95")
61
+ SYNTHESIS_DONE=$(echo "$FRONTMATTER" | grep '^synthesis_done:' | sed 's/synthesis_done: *//' || echo "false")
62
+
63
+ # ---------------------------------------------------------------------------
64
+ # Validate numeric fields
65
+ # ---------------------------------------------------------------------------
66
+ if [[ ! "$ITERATION" =~ ^[0-9]+$ ]]; then
67
+ echo "Oracle hook: Invalid iteration in marker file. Removing." >&2
68
+ rm -f "$MARKER_FILE"
69
+ exit 0
70
+ fi
71
+
72
+ if [[ ! "$MAX_ITERATIONS" =~ ^[0-9]+$ ]]; then
73
+ echo "Oracle hook: Invalid max_iterations in marker file. Removing." >&2
74
+ rm -f "$MARKER_FILE"
75
+ exit 0
76
+ fi
77
+
78
+ # ---------------------------------------------------------------------------
79
+ # Session isolation
80
+ # ---------------------------------------------------------------------------
81
+ if [[ -n "$MARKER_SESSION_ID" ]] && [[ "$MARKER_SESSION_ID" != "$INPUT_SESSION_ID" ]]; then
82
+ # Different session started this loop -- don't interfere
83
+ exit 0
84
+ fi
85
+
86
+ # ---------------------------------------------------------------------------
87
+ # Check for user-requested stop
88
+ # ---------------------------------------------------------------------------
89
+ if [[ -f "$STOP_FILE" ]]; then
90
+ rm -f "$STOP_FILE"
91
+ rm -f "$MARKER_FILE"
92
+ echo "Oracle hook: Stop signal received. Ending research." >&2
93
+ exit 0
94
+ fi
95
+
96
+ # ---------------------------------------------------------------------------
97
+ # Validate state files exist
98
+ # ---------------------------------------------------------------------------
99
+ if [[ ! -f "$STATE_FILE" ]]; then
100
+ echo "Oracle hook: state.json missing. Ending research." >&2
101
+ rm -f "$MARKER_FILE"
102
+ exit 0
103
+ fi
104
+
105
+ # Validate state.json is valid JSON
106
+ if ! jq -e . "$STATE_FILE" >/dev/null 2>&1; then
107
+ echo "Oracle hook: state.json is corrupt. Ending research." >&2
108
+ rm -f "$MARKER_FILE"
109
+ exit 0
110
+ fi
111
+
112
+ # ---------------------------------------------------------------------------
113
+ # Read state from state.json
114
+ # ---------------------------------------------------------------------------
115
+ OVERALL_CONFIDENCE=$(jq '.overall_confidence // 0' "$STATE_FILE" 2>/dev/null || echo "0")
116
+ STATE_TARGET=$(jq '.target_confidence // 95' "$STATE_FILE" 2>/dev/null || echo "95")
117
+ # Prefer marker's target_confidence, fall back to state.json
118
+ TARGET=${TARGET_CONFIDENCE:-$STATE_TARGET}
119
+
120
+ # ---------------------------------------------------------------------------
121
+ # Read last assistant message from transcript for COMPLETE tag
122
+ # ---------------------------------------------------------------------------
123
+ COMPLETE_TAG_FOUND=false
124
+ if [[ -n "$TRANSCRIPT_PATH" ]] && [[ -f "$TRANSCRIPT_PATH" ]]; then
125
+ # Get last 100 assistant lines (bounded for performance)
126
+ LAST_LINES=$(grep '"role":"assistant"' "$TRANSCRIPT_PATH" | tail -n 100 2>/dev/null || echo "")
127
+ if [[ -n "$LAST_LINES" ]]; then
128
+ LAST_OUTPUT=$(echo "$LAST_LINES" | jq -rs '
129
+ map(.message.content[]? | select(.type == "text") | .text) | last // ""
130
+ ' 2>/dev/null || echo "")
131
+ if echo "$LAST_OUTPUT" | grep -q "<oracle>COMPLETE</oracle>"; then
132
+ COMPLETE_TAG_FOUND=true
133
+ fi
134
+ fi
135
+ fi
136
+
137
+ # ---------------------------------------------------------------------------
138
+ # Convergence detection (ported from oracle.sh)
139
+ # ---------------------------------------------------------------------------
140
+
141
+ # Compute convergence metrics from plan.json
142
+ compute_convergence() {
143
+ local plan_file="$1"
144
+ local state_file="$2"
145
+
146
+ [[ -f "$plan_file" ]] || { echo '{"gap_resolution_pct":0,"coverage_pct":0,"novelty_delta":0,"total_findings":0}'; return 0; }
147
+
148
+ local total answered partial_high
149
+ total=$(jq '[.questions[]] | length' "$plan_file" 2>/dev/null || echo "0")
150
+ answered=$(jq '[.questions[] | select(.status == "answered")] | length' "$plan_file" 2>/dev/null || echo "0")
151
+ partial_high=$(jq '[.questions[] | select(.status == "partial" and .confidence >= 70)] | length' "$plan_file" 2>/dev/null || echo "0")
152
+
153
+ local gap_resolution=0
154
+ if [[ "$total" -gt 0 ]]; then
155
+ gap_resolution=$(( (answered + partial_high) * 100 / total ))
156
+ fi
157
+
158
+ local touched coverage=0
159
+ touched=$(jq '[.questions[] | select((.iterations_touched // []) | length > 0)] | length' "$plan_file" 2>/dev/null || echo "0")
160
+ if [[ "$total" -gt 0 ]]; then
161
+ coverage=$(( touched * 100 / total ))
162
+ fi
163
+
164
+ local current_findings prev_findings novelty_delta
165
+ current_findings=$(jq '[.questions[].key_findings | length] | add // 0' "$plan_file" 2>/dev/null || echo "0")
166
+ prev_findings=$(jq '.convergence.prev_findings_count // 0' "$state_file" 2>/dev/null || echo "0")
167
+ novelty_delta=$(( current_findings - prev_findings ))
168
+
169
+ jq -n --argjson gap "$gap_resolution" --argjson cov "$coverage" \
170
+ --argjson novelty "$novelty_delta" --argjson findings "$current_findings" \
171
+ '{gap_resolution_pct: $gap, coverage_pct: $cov, novelty_delta: $novelty, total_findings: $findings}'
172
+ }
173
+
174
+ # Check if research has converged
175
+ check_convergence() {
176
+ local state_file="$1"
177
+
178
+ local composite_score
179
+ composite_score=$(jq '.convergence.composite_score // 0' "$state_file" 2>/dev/null || echo "0")
180
+
181
+ if [[ "$composite_score" -lt "$CONV_THRESHOLD" ]]; then
182
+ return 1
183
+ fi
184
+
185
+ local history_len
186
+ history_len=$(jq '(.convergence.history // []) | length' "$state_file" 2>/dev/null || echo "0")
187
+ if [[ "$history_len" -lt 2 ]]; then
188
+ return 1
189
+ fi
190
+
191
+ local low_novelty_count
192
+ low_novelty_count=$(jq '[(.convergence.history // [])[-2:][] | select(.novelty_delta <= 1)] | length' "$state_file" 2>/dev/null || echo "0")
193
+
194
+ [[ "$low_novelty_count" -ge 2 ]]
195
+ }
196
+
197
+ # Detect diminishing returns
198
+ detect_diminishing_returns() {
199
+ local state_file="$1"
200
+
201
+ local history_len
202
+ history_len=$(jq '(.convergence.history // []) | length' "$state_file" 2>/dev/null || echo "0")
203
+
204
+ if [[ "$history_len" -lt "$DR_WINDOW" ]]; then
205
+ echo "continue"
206
+ return 0
207
+ fi
208
+
209
+ local novelty_threshold
210
+ case "$CURRENT_PHASE" in
211
+ investigate) novelty_threshold=0 ;;
212
+ *) novelty_threshold=1 ;;
213
+ esac
214
+
215
+ local low_change_count
216
+ low_change_count=$(jq --argjson window "$DR_WINDOW" --argjson threshold "$novelty_threshold" \
217
+ '[(.convergence.history // [])[-$window:][] | select(.novelty_delta <= $threshold)] | length' \
218
+ "$state_file" 2>/dev/null || echo "0")
219
+
220
+ if [[ "$low_change_count" -ge "$DR_WINDOW" ]]; then
221
+ case "$CURRENT_PHASE" in
222
+ survey|investigate) echo "strategy_change" ;;
223
+ synthesize|verify) echo "synthesize_now" ;;
224
+ *) echo "continue" ;;
225
+ esac
226
+ else
227
+ echo "continue"
228
+ fi
229
+ }
230
+
231
+ # Update convergence metrics in state.json
232
+ update_convergence_metrics() {
233
+ local state_file="$1"
234
+ local plan_file="$2"
235
+ local next_iteration="$3"
236
+ local next_phase="$4"
237
+
238
+ local metrics
239
+ metrics=$(compute_convergence "$plan_file" "$state_file")
240
+
241
+ local gap_pct coverage_pct novelty_delta total_findings
242
+ gap_pct=$(echo "$metrics" | jq '.gap_resolution_pct')
243
+ coverage_pct=$(echo "$metrics" | jq '.coverage_pct')
244
+ novelty_delta=$(echo "$metrics" | jq '.novelty_delta')
245
+ total_findings=$(echo "$metrics" | jq '.total_findings')
246
+
247
+ local current_confidence prev_confidence confidence_delta
248
+ current_confidence=$(jq '.overall_confidence // 0' "$state_file" 2>/dev/null || echo "0")
249
+ prev_confidence=$(jq '.convergence.prev_overall_confidence // 0' "$state_file" 2>/dev/null || echo "0")
250
+ confidence_delta=$(( current_confidence - prev_confidence ))
251
+
252
+ # Composite score: gap*0.4 + coverage*0.3 + (novelty<=1?100:0)*0.3
253
+ local novelty_component composite_score converged
254
+ if [[ "$novelty_delta" -le 1 ]]; then
255
+ novelty_component=100
256
+ else
257
+ novelty_component=0
258
+ fi
259
+ composite_score=$(( gap_pct * 40 / 100 + coverage_pct * 30 / 100 + novelty_component * 30 / 100 ))
260
+
261
+ if [[ "$composite_score" -ge "$CONV_THRESHOLD" ]]; then
262
+ converged="true"
263
+ else
264
+ converged="false"
265
+ fi
266
+
267
+ # Atomic write
268
+ jq --argjson prev_findings "$total_findings" \
269
+ --argjson prev_confidence "$current_confidence" \
270
+ --argjson iteration "$next_iteration" \
271
+ --argjson novelty "$novelty_delta" \
272
+ --argjson conf_delta "$confidence_delta" \
273
+ --argjson gap "$gap_pct" \
274
+ --argjson cov "$coverage_pct" \
275
+ --arg phase "$next_phase" \
276
+ --argjson composite "$composite_score" \
277
+ --argjson converged "$converged" \
278
+ '
279
+ .convergence = (.convergence // {}) |
280
+ .convergence.prev_findings_count = $prev_findings |
281
+ .convergence.prev_overall_confidence = $prev_confidence |
282
+ .convergence.history = ((.convergence.history // []) + [{
283
+ iteration: $iteration,
284
+ novelty_delta: $novelty,
285
+ confidence_delta: $conf_delta,
286
+ gap_resolution_pct: $gap,
287
+ coverage_pct: $cov,
288
+ phase: $phase
289
+ }]) |
290
+ .convergence.composite_score = $composite |
291
+ .convergence.converged = $converged
292
+ ' "$state_file" > "$state_file.tmp" && mv "$state_file.tmp" "$state_file"
293
+ }
294
+
295
+ # Compute trust scores from plan.json source tracking
296
+ compute_trust_scores() {
297
+ local plan_file="$1"
298
+
299
+ [[ -f "$plan_file" ]] || return 0
300
+
301
+ local has_structured
302
+ has_structured=$(jq '
303
+ [.questions[].key_findings[] | type] | if length == 0 then false else any(. == "object") end
304
+ ' "$plan_file" 2>/dev/null || echo "false")
305
+
306
+ if [[ "$has_structured" != "true" ]]; then
307
+ return 0
308
+ fi
309
+
310
+ local total_findings single_source multi_source
311
+ total_findings=$(jq '[.questions[].key_findings[]] | length' "$plan_file" 2>/dev/null || echo "0")
312
+ single_source=$(jq '[.questions[].key_findings[] | select(type == "object" and (.source_ids | length) == 1)] | length' "$plan_file" 2>/dev/null || echo "0")
313
+ multi_source=$(jq '[.questions[].key_findings[] | select(type == "object" and (.source_ids | length) >= 2)] | length' "$plan_file" 2>/dev/null || echo "0")
314
+ local no_source
315
+ no_source=$(jq '[.questions[].key_findings[] | select(type == "object" and ((.source_ids // []) | length) == 0)] | length' "$plan_file" 2>/dev/null || echo "0")
316
+
317
+ local trust_ratio=0
318
+ if [[ "$total_findings" -gt 0 ]]; then
319
+ trust_ratio=$(( multi_source * 100 / total_findings ))
320
+ fi
321
+
322
+ jq --argjson total "$total_findings" \
323
+ --argjson single "$single_source" \
324
+ --argjson multi "$multi_source" \
325
+ --argjson nosrc "$no_source" \
326
+ --argjson ratio "$trust_ratio" \
327
+ '.trust_summary = {
328
+ total_findings: $total,
329
+ single_source: $single,
330
+ multi_source: $multi,
331
+ no_source: $nosrc,
332
+ trust_ratio: $ratio
333
+ }' "$plan_file" > "$plan_file.tmp" && mv "$plan_file.tmp" "$plan_file"
334
+ }
335
+
336
+ # ---------------------------------------------------------------------------
337
+ # Determine next phase (ported from oracle.sh)
338
+ # ---------------------------------------------------------------------------
339
+ determine_next_phase() {
340
+ local state_file="$1"
341
+ local plan_file="$2"
342
+
343
+ [[ -f "$state_file" ]] || { echo "survey"; return 0; }
344
+ [[ -f "$plan_file" ]] || { echo "survey"; return 0; }
345
+
346
+ local total_questions touched_count avg_confidence below_50_count
347
+
348
+ total_questions=$(jq '[.questions[]] | length' "$plan_file" 2>/dev/null || echo "0")
349
+ if [[ "$total_questions" -eq 0 ]]; then
350
+ echo "survey"
351
+ return 0
352
+ fi
353
+
354
+ touched_count=$(jq '[.questions[] | select((.iterations_touched // []) | length > 0)] | length' "$plan_file" 2>/dev/null || echo "0")
355
+ avg_confidence=$(jq '[.questions[].confidence] | if length > 0 then (add / length) else 0 end | floor' "$plan_file" 2>/dev/null || echo "0")
356
+ below_50_count=$(jq '[.questions[] | select(.status != "answered" and .confidence < 50)] | length' "$plan_file" 2>/dev/null || echo "0")
357
+
358
+ if [[ "$avg_confidence" -ge 80 ]]; then
359
+ echo "verify"
360
+ return 0
361
+ fi
362
+
363
+ if [[ "$avg_confidence" -ge 60 ]] || [[ "$below_50_count" -lt 2 ]]; then
364
+ echo "synthesize"
365
+ return 0
366
+ fi
367
+
368
+ if [[ "$touched_count" -ge "$total_questions" ]] || [[ "$avg_confidence" -ge 25 ]]; then
369
+ echo "investigate"
370
+ return 0
371
+ fi
372
+
373
+ echo "survey"
374
+ }
375
+
376
+ # ---------------------------------------------------------------------------
377
+ # Phase directive generators (copied verbatim from oracle.sh)
378
+ # ---------------------------------------------------------------------------
379
+ phase_directive_survey() {
380
+ cat <<'DIRECTIVE'
381
+ ## Current Phase: SURVEY
382
+
383
+ Cast a wide net -- get initial findings for every open question. Target untouched
384
+ questions first (those with empty iterations_touched arrays). Aim for 20-40%
385
+ confidence per question. List all discovered unknowns in gaps.md.
386
+
387
+ Do NOT go deep on any single question yet. Breadth over depth in this phase.
388
+ Your goal is to ensure every question has at least some initial findings before
389
+ the research moves to the investigation phase.
390
+
391
+ Source tracking is MANDATORY -- register sources and link every finding to source_ids.
392
+
393
+ ---
394
+
395
+ DIRECTIVE
396
+ }
397
+
398
+ phase_directive_investigate() {
399
+ cat <<'DIRECTIVE'
400
+ ## Current Phase: INVESTIGATE
401
+
402
+ Target the lowest-confidence question and go DEEP. You MUST reference existing
403
+ findings in synthesis.md and ADD NEW information, not restate what is already there.
404
+ Aim to push confidence above 70% for your target question.
405
+
406
+ Update gaps.md with specific remaining unknowns. If you find contradictions with
407
+ existing findings, document them explicitly. One thoroughly investigated question
408
+ per iteration is better than shallow passes on many.
409
+
410
+ Source tracking is MANDATORY this iteration. Every new finding must have at least one source_id.
411
+
412
+ ---
413
+
414
+ DIRECTIVE
415
+ }
416
+
417
+ phase_directive_synthesize() {
418
+ cat <<'DIRECTIVE'
419
+ ## Current Phase: SYNTHESIZE
420
+
421
+ Read ALL findings in synthesis.md before doing anything. Identify connections,
422
+ patterns, and contradictions ACROSS questions. Consolidate redundant findings.
423
+ Resolve contradictions with evidence. Push overall confidence toward the target.
424
+
425
+ Your job is NOT to find new information -- it is to make sense of what has already
426
+ been found. Cross-reference answers between questions. Strengthen weak claims
427
+ with evidence from other questions. Remove speculation that lacks support.
428
+
429
+ Verify source attribution is complete. Flag any findings missing source_ids.
430
+
431
+ ---
432
+
433
+ DIRECTIVE
434
+ }
435
+
436
+ phase_directive_verify() {
437
+ cat <<'DIRECTIVE'
438
+ ## Current Phase: VERIFY
439
+
440
+ Focus on claims in gaps.md contradictions section. Cross-reference key findings
441
+ with additional sources. Confirm or correct confidence scores. Mark well-supported
442
+ questions as answered with 90%+ confidence.
443
+
444
+ Final gaps.md should contain only genuinely unresolvable unknowns. If a contradiction
445
+ cannot be resolved, document both positions with evidence quality assessment.
446
+ This is the final quality pass before research completion.
447
+
448
+ Cross-reference source coverage. Ensure all key findings have 2+ independent sources.
449
+
450
+ ---
451
+
452
+ DIRECTIVE
453
+ }
454
+
455
+ phase_directive_for() {
456
+ local phase="$1"
457
+ case "$phase" in
458
+ survey) phase_directive_survey ;;
459
+ investigate) phase_directive_investigate ;;
460
+ synthesize) phase_directive_synthesize ;;
461
+ verify) phase_directive_verify ;;
462
+ *) echo "## Current Phase: $phase"; echo ""; echo "---"; echo "" ;;
463
+ esac
464
+ }
465
+
466
+ # ---------------------------------------------------------------------------
467
+ # Strategy modifier (ported from oracle.sh)
468
+ # ---------------------------------------------------------------------------
469
+ strategy_modifier() {
470
+ local state_file="$1"
471
+ local strategy
472
+ strategy=$(jq -r '.strategy // "adaptive"' "$state_file" 2>/dev/null || echo "adaptive")
473
+
474
+ case "$strategy" in
475
+ breadth-first)
476
+ cat <<'STRATEGY'
477
+
478
+ STRATEGY NOTE: Breadth-first mode is active. Prioritize covering ALL questions
479
+ before going deep on any single one. Aim for broad coverage across the research
480
+ plan. When multiple questions are untouched, target the easiest-to-answer first
481
+ for maximum coverage.
482
+
483
+ STRATEGY
484
+ ;;
485
+ depth-first)
486
+ cat <<'STRATEGY'
487
+
488
+ STRATEGY NOTE: Depth-first mode is active. Pick the single most important open
489
+ question and investigate it exhaustively. Push confidence to 80%+ before moving
490
+ to the next question. Prefer thorough, well-sourced answers over broad coverage.
491
+
492
+ STRATEGY
493
+ ;;
494
+ *) ;; # adaptive: no modifier
495
+ esac
496
+ }
497
+
498
+ # ---------------------------------------------------------------------------
499
+ # Steering signals (ported from oracle.sh)
500
+ # ---------------------------------------------------------------------------
501
+ read_steering_signals() {
502
+ local utils="$AETHER_ROOT/.aether/aether-utils.sh"
503
+
504
+ [[ -f "$utils" ]] || return 0
505
+
506
+ local signals
507
+ signals=$(bash "$utils" pheromone-read 2>/dev/null || echo '{"signals":[]}')
508
+
509
+ local signal_array
510
+ signal_array=$(echo "$signals" | jq -c '.result.signals // .signals // []' 2>/dev/null || echo '[]')
511
+
512
+ local count
513
+ count=$(echo "$signal_array" | jq 'length' 2>/dev/null || echo "0")
514
+
515
+ [[ "$count" -gt 0 ]] || return 0
516
+
517
+ local directive=""
518
+
519
+ # REDIRECT (max 2)
520
+ local redirects
521
+ redirects=$(echo "$signal_array" | jq -r '
522
+ map(select(.type == "REDIRECT"))
523
+ | sort_by(-.effective_strength)
524
+ | .[:2]
525
+ | .[] | "- [" + ((.effective_strength * 100 | floor | tostring)) + "%] \"" + (.content.text // "") + "\""
526
+ ' 2>/dev/null || echo "")
527
+
528
+ if [[ -n "$redirects" ]]; then
529
+ directive+="**REDIRECT (Hard constraints -- MUST follow):**"$'\n'"$redirects"$'\n\n'
530
+ fi
531
+
532
+ # FOCUS (max 3)
533
+ local focuses
534
+ focuses=$(echo "$signal_array" | jq -r '
535
+ map(select(.type == "FOCUS"))
536
+ | sort_by(-.effective_strength)
537
+ | .[:3]
538
+ | .[] | "- [" + ((.effective_strength * 100 | floor | tostring)) + "%] \"" + (.content.text // "") + "\""
539
+ ' 2>/dev/null || echo "")
540
+
541
+ if [[ -n "$focuses" ]]; then
542
+ directive+="**FOCUS (Prioritize these areas):**"$'\n'"$focuses"$'\n\n'
543
+ fi
544
+
545
+ # FEEDBACK (max 2)
546
+ local feedbacks
547
+ feedbacks=$(echo "$signal_array" | jq -r '
548
+ map(select(.type == "FEEDBACK"))
549
+ | sort_by(-.effective_strength)
550
+ | .[:2]
551
+ | .[] | "- [" + ((.effective_strength * 100 | floor | tostring)) + "%] \"" + (.content.text // "") + "\""
552
+ ' 2>/dev/null || echo "")
553
+
554
+ if [[ -n "$feedbacks" ]]; then
555
+ directive+="**FEEDBACK (Adjust approach):**"$'\n'"$feedbacks"$'\n\n'
556
+ fi
557
+
558
+ if [[ -n "$directive" ]]; then
559
+ echo "## Active Steering Signals"
560
+ echo ""
561
+ echo "$directive"
562
+ echo "When selecting your target question, PRIORITIZE questions related to FOCUS areas."
563
+ echo "REDIRECT signals are hard constraints -- adjust your approach to comply."
564
+ echo "FEEDBACK signals are suggestions -- incorporate where appropriate."
565
+ echo ""
566
+ echo "---"
567
+ echo ""
568
+ fi
569
+ }
570
+
571
+ # ---------------------------------------------------------------------------
572
+ # Synthesis prompt builder (ported from oracle.sh)
573
+ # ---------------------------------------------------------------------------
574
+ build_synthesis_prompt() {
575
+ local reason="$1"
576
+ local template
577
+ template=$(jq -r '.template // "custom"' "$STATE_FILE" 2>/dev/null || echo "custom")
578
+
579
+ cat <<SYNTHESIS_DIRECTIVE
580
+ ## SYNTHESIS PASS (Final Report)
581
+
582
+ This is the final pass. The oracle loop has ended (reason: $reason).
583
+ Produce the best possible research report from the current state.
584
+
585
+ Read ALL of these files:
586
+ - .aether/oracle/state.json -- session metadata
587
+ - .aether/oracle/plan.json -- questions, findings, confidence, AND sources registry
588
+ - .aether/oracle/synthesis.md -- accumulated findings
589
+ - .aether/oracle/gaps.md -- remaining unknowns
590
+
591
+ If any state file is unreadable, skip it and work with what you have.
592
+
593
+ Then REWRITE synthesis.md as a structured final report.
594
+
595
+ SYNTHESIS_DIRECTIVE
596
+
597
+ # Template-specific sections
598
+ case "$template" in
599
+ tech-eval)
600
+ cat <<'TEMPLATE'
601
+ ### Required Sections:
602
+ 1. **Executive Summary** -- 2-3 paragraphs: what was evaluated, key conclusion, recommendation
603
+ 2. **Comparison Matrix** -- Table comparing the evaluated technology against alternatives on key dimensions
604
+ 3. **Pros and Cons** -- Bullet lists with evidence citations
605
+ 4. **Adoption Assessment** -- Community size, maintenance status, release cadence
606
+ 5. **Migration/Integration Path** -- Steps to adopt, estimated effort, risks
607
+ 6. **Recommendation** -- Clear recommendation with confidence level
608
+ 7. **Open Questions** -- Remaining gaps
609
+ 8. **Sources** -- All sources with inline citations [S1], [S2] format
610
+
611
+ TEMPLATE
612
+ ;;
613
+ architecture-review)
614
+ cat <<'TEMPLATE'
615
+ ### Required Sections:
616
+ 1. **Executive Summary** -- 2-3 paragraphs: system overview, key findings, critical risks
617
+ 2. **Component Map** -- List of major components with responsibilities
618
+ 3. **Dependency Analysis** -- How components connect, coupling assessment
619
+ 4. **Risk Assessment** -- Single points of failure, complexity hotspots
620
+ 5. **Scalability Analysis** -- Current capacity, growth limitations
621
+ 6. **Improvement Recommendations** -- Prioritized list
622
+ 7. **Open Questions** -- Remaining gaps
623
+ 8. **Sources** -- All sources with inline citations [S1], [S2] format
624
+
625
+ TEMPLATE
626
+ ;;
627
+ bug-investigation)
628
+ cat <<'TEMPLATE'
629
+ ### Required Sections:
630
+ 1. **Executive Summary** -- 1-2 paragraphs: bug description, root cause, recommended fix
631
+ 2. **Reproduction Steps** -- Exact steps to reproduce
632
+ 3. **Root Cause Analysis** -- What causes the bug, code paths involved
633
+ 4. **Impact Assessment** -- Who is affected, severity
634
+ 5. **Fix Recommendations** -- Proposed fixes ranked by safety and effort
635
+ 6. **Related Issues** -- Similar bugs, regression risk
636
+ 7. **Open Questions** -- Remaining gaps
637
+ 8. **Sources** -- All sources with inline citations [S1], [S2] format
638
+
639
+ TEMPLATE
640
+ ;;
641
+ best-practices)
642
+ cat <<'TEMPLATE'
643
+ ### Required Sections:
644
+ 1. **Executive Summary** -- 2-3 paragraphs: domain overview, key recommendations
645
+ 2. **Best Practice Benchmark** -- What industry consensus considers best practice
646
+ 3. **Current State Assessment** -- How the subject compares
647
+ 4. **Gap Analysis** -- Specific gaps prioritized by impact
648
+ 5. **Action Plan** -- Ordered steps to close gaps
649
+ 6. **Open Questions** -- Remaining gaps
650
+ 7. **Sources** -- All sources with inline citations [S1], [S2] format
651
+
652
+ TEMPLATE
653
+ ;;
654
+ *)
655
+ cat <<'TEMPLATE'
656
+ ### Required Sections:
657
+ 1. **Executive Summary** -- 2-3 paragraphs summarizing what was found
658
+ 2. **Findings by Question** -- organized by sub-question, with confidence %
659
+ 3. **Open Questions** -- remaining gaps
660
+ 4. **Methodology Notes** -- how many iterations, which phases completed
661
+ 5. **Sources** -- List ALL sources from plan.json sources registry
662
+
663
+ TEMPLATE
664
+ ;;
665
+ esac
666
+
667
+ # Common directives
668
+ cat <<'COMMON'
669
+
670
+ ### Confidence Grouping:
671
+ Within each findings section, group findings by confidence level:
672
+ - **High confidence (80%+)** -- list first with full citations
673
+ - **Medium confidence (50-79%)** -- list with caveats
674
+ - **Low confidence (<50%)** -- list as tentative/unverified
675
+
676
+ Use inline citations [S1], [S2] linking findings to their sources.
677
+ Flag single-source findings with (single source) marker.
678
+
679
+ Also update state.json: set status to "complete" if reason is "converged",
680
+ or "stopped" otherwise.
681
+
682
+ COMMON
683
+
684
+ # Append the base oracle.md for tool access and rules
685
+ cat "$ORACLE_MD"
686
+ }
687
+
688
+ # ---------------------------------------------------------------------------
689
+ # Update marker file with new values
690
+ # ---------------------------------------------------------------------------
691
+ update_marker() {
692
+ local new_iteration="$1"
693
+ local new_phase="$2"
694
+ local new_synthesis_done="$3"
695
+
696
+ local oracle_md_body
697
+ oracle_md_body=$(awk '/^---$/{i++; next} i>=2' "$MARKER_FILE")
698
+
699
+ cat > "${MARKER_FILE}.tmp.$$" <<MARKER
700
+ ---
701
+ iteration: $new_iteration
702
+ max_iterations: $MAX_ITERATIONS
703
+ session_id: $MARKER_SESSION_ID
704
+ phase: $new_phase
705
+ target_confidence: $TARGET_CONFIDENCE
706
+ synthesis_done: $new_synthesis_done
707
+ oracle_md_path: .aether/utils/oracle/oracle.md
708
+ ---
709
+ $oracle_md_body
710
+ MARKER
711
+ mv "${MARKER_FILE}.tmp.$$" "$MARKER_FILE"
712
+ }
713
+
714
+ # ---------------------------------------------------------------------------
715
+ # Update state.json iteration and phase
716
+ # ---------------------------------------------------------------------------
717
+ update_state_json() {
718
+ local new_iteration="$1"
719
+ local new_phase="$2"
720
+
721
+ local ts
722
+ ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
723
+
724
+ jq --argjson iter "$new_iteration" \
725
+ --arg phase "$new_phase" \
726
+ --arg ts "$ts" \
727
+ '.iteration = $iter | .phase = $phase | .last_updated = $ts' \
728
+ "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
729
+ }
730
+
731
+ # ---------------------------------------------------------------------------
732
+ # MAIN DECISION LOGIC
733
+ # ---------------------------------------------------------------------------
734
+
735
+ NEXT_ITERATION=$((ITERATION + 1))
736
+
737
+ # Check completion conditions
738
+ IS_COMPLETE=false
739
+ COMPLETION_REASON=""
740
+
741
+ # 1. Max iterations reached
742
+ if [[ "$MAX_ITERATIONS" -gt 0 ]] && [[ "$NEXT_ITERATION" -gt "$MAX_ITERATIONS" ]]; then
743
+ IS_COMPLETE=true
744
+ COMPLETION_REASON="max_iterations"
745
+ fi
746
+
747
+ # 2. Confidence target met
748
+ if [[ "$OVERALL_CONFIDENCE" -ge "$TARGET" ]]; then
749
+ IS_COMPLETE=true
750
+ COMPLETION_REASON="${COMPLETION_REASON:-confidence_target}"
751
+ fi
752
+
753
+ # 3. AI signaled complete
754
+ if [[ "$COMPLETE_TAG_FOUND" == "true" ]]; then
755
+ IS_COMPLETE=true
756
+ COMPLETION_REASON="${COMPLETION_REASON:-ai_signal}"
757
+ fi
758
+
759
+ # 4. Convergence detected
760
+ if [[ -f "$PLAN_FILE" ]] && check_convergence "$STATE_FILE"; then
761
+ IS_COMPLETE=true
762
+ COMPLETION_REASON="${COMPLETION_REASON:-convergence}"
763
+ fi
764
+
765
+ # Handle synthesis pass
766
+ if [[ "$IS_COMPLETE" == "true" ]]; then
767
+ if [[ "$SYNTHESIS_DONE" == "true" ]]; then
768
+ # Research complete AND synthesis done -- allow stop
769
+ rm -f "$MARKER_FILE"
770
+
771
+ # Update state.json status
772
+ local final_status
773
+ case "$COMPLETION_REASON" in
774
+ max_iterations) final_status="stopped" ;;
775
+ convergence|confidence_target|ai_signal) final_status="complete" ;;
776
+ *) final_status="stopped" ;;
777
+ esac
778
+ jq --arg status "$final_status" --arg reason "$COMPLETION_REASON" \
779
+ '.status = $status | .stop_reason = $reason' \
780
+ "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
781
+
782
+ # Allow exit
783
+ exit 0
784
+ fi
785
+
786
+ # Complete but no synthesis yet -- trigger synthesis pass
787
+ update_marker "$NEXT_ITERATION" "synthesis-pass" "true"
788
+ update_state_json "$NEXT_ITERATION" "synthesis-pass"
789
+
790
+ # Build synthesis prompt
791
+ SYNTHESIS_PROMPT=$(build_synthesis_prompt "$COMPLETION_REASON")
792
+
793
+ SYSTEM_MSG="Oracle iteration $NEXT_ITERATION (SYNTHESIS PASS) -- Final report generation"
794
+
795
+ jq -n \
796
+ --arg prompt "$SYNTHESIS_PROMPT" \
797
+ --arg msg "$SYSTEM_MSG" \
798
+ '{
799
+ "decision": "block",
800
+ "reason": $prompt,
801
+ "systemMessage": $msg
802
+ }'
803
+
804
+ exit 0
805
+ fi
806
+
807
+ # ---------------------------------------------------------------------------
808
+ # NOT COMPLETE -- continue research loop
809
+ # ---------------------------------------------------------------------------
810
+
811
+ # Determine next phase from plan.json metrics
812
+ NEXT_PHASE=$(determine_next_phase "$STATE_FILE" "$PLAN_FILE")
813
+
814
+ # Check diminishing returns -- may force phase advancement
815
+ if [[ -f "$PLAN_FILE" ]]; then
816
+ DR_RESULT=$(detect_diminishing_returns "$STATE_FILE")
817
+ case "$DR_RESULT" in
818
+ strategy_change)
819
+ # Force synthesize phase
820
+ NEXT_PHASE="synthesize"
821
+ ;;
822
+ synthesize_now)
823
+ # Force early synthesis completion
824
+ NEXT_PHASE="verify"
825
+ ;;
826
+ esac
827
+ fi
828
+
829
+ # Update convergence metrics (reads plan.json, writes state.json)
830
+ if [[ -f "$PLAN_FILE" ]]; then
831
+ update_convergence_metrics "$STATE_FILE" "$PLAN_FILE" "$NEXT_ITERATION" "$NEXT_PHASE"
832
+ compute_trust_scores "$PLAN_FILE"
833
+ fi
834
+
835
+ # Update state files
836
+ update_marker "$NEXT_ITERATION" "$NEXT_PHASE" "$SYNTHESIS_DONE"
837
+ update_state_json "$NEXT_ITERATION" "$NEXT_PHASE"
838
+
839
+ # Generate research-plan.md summary
840
+ if [[ -f "$PLAN_FILE" ]]; then
841
+ {
842
+ echo "# Research Plan"
843
+ echo ""
844
+ local_topic=$(jq -r '.topic // "unknown"' "$STATE_FILE" 2>/dev/null || echo "unknown")
845
+ local_conf=$(jq '.overall_confidence // 0' "$STATE_FILE" 2>/dev/null || echo "0")
846
+ echo "**Topic:** $local_topic"
847
+ echo "**Status:** active | **Iteration:** $NEXT_ITERATION of $MAX_ITERATIONS"
848
+ echo "**Overall Confidence:** ${local_conf}%"
849
+ echo ""
850
+ echo "## Questions"
851
+ echo "| # | Question | Status | Confidence |"
852
+ echo "|---|----------|--------|------------|"
853
+ jq -r '.questions[] | "| \(.id) | \(.text) | \(.status) | \(.confidence)% |"' "$PLAN_FILE" 2>/dev/null || true
854
+ echo ""
855
+ echo "## Next Steps"
856
+ local_next
857
+ local_next=$(jq -r '[.questions[] | select(.status != "answered")] | sort_by(.confidence) | first | .text // "All questions answered"' "$PLAN_FILE" 2>/dev/null || echo "Continue research")
858
+ echo "Next investigation: $local_next"
859
+ echo ""
860
+ echo "---"
861
+ echo "*Generated from plan.json -- do not edit directly*"
862
+ } > "$ORACLE_DIR/research-plan.md"
863
+ fi
864
+
865
+ # Build the full iteration prompt
866
+ FULL_PROMPT=""
867
+
868
+ # Phase directive
869
+ FULL_PROMPT+=$(phase_directive_for "$NEXT_PHASE")
870
+
871
+ # Strategy modifier
872
+ FULL_PROMPT+=$(strategy_modifier "$STATE_FILE")
873
+
874
+ # Steering signals
875
+ STEERING=$(read_steering_signals "$AETHER_ROOT")
876
+ if [[ -n "$STEERING" ]]; then
877
+ FULL_PROMPT+="$STEERING"
878
+ fi
879
+
880
+ # Base oracle.md prompt
881
+ if [[ -f "$ORACLE_MD" ]]; then
882
+ FULL_PROMPT+=$(cat "$ORACLE_MD")
883
+ fi
884
+
885
+ SYSTEM_MSG="Oracle iteration $NEXT_ITERATION ($NEXT_PHASE phase) | Confidence: ${OVERALL_CONFIDENCE}% / ${TARGET}% | Iterations remaining: $((MAX_ITERATIONS - NEXT_ITERATION))"
886
+
887
+ jq -n \
888
+ --arg prompt "$FULL_PROMPT" \
889
+ --arg msg "$SYSTEM_MSG" \
890
+ '{
891
+ "decision": "block",
892
+ "reason": $prompt,
893
+ "systemMessage": $msg
894
+ }'
895
+
896
+ exit 0