aether-colony 5.0.0 → 5.1.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.
- package/.aether/aether-utils.sh +3150 -3349
- package/.aether/agents-claude/aether-ambassador.md +265 -0
- package/.aether/agents-claude/aether-archaeologist.md +327 -0
- package/.aether/agents-claude/aether-architect.md +236 -0
- package/.aether/agents-claude/aether-auditor.md +271 -0
- package/.aether/agents-claude/aether-builder.md +224 -0
- package/.aether/agents-claude/aether-chaos.md +269 -0
- package/.aether/agents-claude/aether-chronicler.md +305 -0
- package/.aether/agents-claude/aether-gatekeeper.md +330 -0
- package/.aether/agents-claude/aether-includer.md +374 -0
- package/.aether/agents-claude/aether-keeper.md +272 -0
- package/.aether/agents-claude/aether-measurer.md +322 -0
- package/.aether/agents-claude/aether-oracle.md +237 -0
- package/.aether/agents-claude/aether-probe.md +211 -0
- package/.aether/agents-claude/aether-queen.md +330 -0
- package/.aether/agents-claude/aether-route-setter.md +178 -0
- package/.aether/agents-claude/aether-sage.md +418 -0
- package/.aether/agents-claude/aether-scout.md +179 -0
- package/.aether/agents-claude/aether-surveyor-disciplines.md +417 -0
- package/.aether/agents-claude/aether-surveyor-nest.md +355 -0
- package/.aether/agents-claude/aether-surveyor-pathogens.md +289 -0
- package/.aether/agents-claude/aether-surveyor-provisions.md +360 -0
- package/.aether/agents-claude/aether-tracker.md +270 -0
- package/.aether/agents-claude/aether-watcher.md +280 -0
- package/.aether/agents-claude/aether-weaver.md +248 -0
- package/.aether/commands/archaeology.yaml +653 -0
- package/.aether/commands/build.yaml +1221 -0
- package/.aether/commands/chaos.yaml +653 -0
- package/.aether/commands/colonize.yaml +438 -0
- package/.aether/commands/continue.yaml +1484 -0
- package/.aether/commands/council.yaml +304 -0
- package/.aether/commands/data-clean.yaml +80 -0
- package/.aether/commands/dream.yaml +275 -0
- package/.aether/commands/entomb.yaml +863 -0
- package/.aether/commands/export-signals.yaml +64 -0
- package/.aether/commands/feedback.yaml +158 -0
- package/.aether/commands/flag.yaml +160 -0
- package/.aether/commands/flags.yaml +177 -0
- package/.aether/commands/focus.yaml +112 -0
- package/.aether/commands/help.yaml +167 -0
- package/.aether/commands/history.yaml +137 -0
- package/.aether/commands/import-signals.yaml +79 -0
- package/.aether/commands/init.yaml +469 -0
- package/.aether/commands/insert-phase.yaml +98 -0
- package/.aether/commands/interpret.yaml +285 -0
- package/.aether/commands/lay-eggs.yaml +224 -0
- package/.aether/commands/maturity.yaml +122 -0
- package/.aether/commands/memory-details.yaml +74 -0
- package/.aether/commands/migrate-state.yaml +174 -0
- package/.aether/commands/oracle.yaml +1224 -0
- package/.aether/commands/organize.yaml +446 -0
- package/.aether/commands/patrol.yaml +621 -0
- package/.aether/commands/pause-colony.yaml +424 -0
- package/.aether/commands/phase.yaml +124 -0
- package/.aether/commands/pheromones.yaml +153 -0
- package/.aether/commands/plan.yaml +1313 -0
- package/.aether/commands/preferences.yaml +63 -0
- package/.aether/commands/redirect.yaml +123 -0
- package/.aether/commands/resume-colony.yaml +373 -0
- package/.aether/commands/resume.yaml +398 -0
- package/.aether/commands/run.yaml +193 -0
- package/.aether/commands/seal.yaml +1205 -0
- package/.aether/commands/skill-create.yaml +337 -0
- package/.aether/commands/status.yaml +364 -0
- package/.aether/commands/swarm.yaml +352 -0
- package/.aether/commands/tunnels.yaml +814 -0
- package/.aether/commands/update.yaml +131 -0
- package/.aether/commands/verify-castes.yaml +159 -0
- package/.aether/commands/watch.yaml +454 -0
- package/.aether/docs/INCIDENT_TEMPLATE.md +32 -0
- package/.aether/docs/QUEEN-SYSTEM.md +11 -11
- package/.aether/docs/README.md +32 -2
- package/.aether/docs/command-playbooks/README.md +23 -0
- package/.aether/docs/command-playbooks/build-complete.md +349 -0
- package/.aether/docs/command-playbooks/build-context.md +282 -0
- package/.aether/docs/command-playbooks/build-full.md +1682 -0
- package/.aether/docs/command-playbooks/build-prep.md +283 -0
- package/.aether/docs/command-playbooks/build-verify.md +405 -0
- package/.aether/docs/command-playbooks/build-wave.md +749 -0
- package/.aether/docs/command-playbooks/continue-advance.md +524 -0
- package/.aether/docs/command-playbooks/continue-finalize.md +447 -0
- package/.aether/docs/command-playbooks/continue-full.md +1724 -0
- package/.aether/docs/command-playbooks/continue-gates.md +686 -0
- package/.aether/docs/command-playbooks/continue-verify.md +406 -0
- package/.aether/docs/context-continuity.md +84 -0
- package/.aether/docs/disciplines/DISCIPLINES.md +9 -7
- package/.aether/docs/error-codes.md +1 -1
- package/.aether/docs/known-issues.md +34 -173
- package/.aether/docs/pheromones.md +86 -6
- package/.aether/docs/plans/pheromone-display-plan.md +257 -0
- package/.aether/docs/queen-commands.md +10 -9
- package/.aether/docs/source-of-truth-map.md +132 -0
- package/.aether/docs/xml-utilities.md +47 -0
- package/.aether/rules/aether-colony.md +23 -13
- package/.aether/scripts/incident-test-add.sh +47 -0
- package/.aether/scripts/weekly-audit.sh +79 -0
- package/.aether/skills/.index.json +649 -0
- package/.aether/skills/colony/.manifest.json +16 -0
- package/.aether/skills/colony/build-discipline/SKILL.md +78 -0
- package/.aether/skills/colony/colony-interaction/SKILL.md +56 -0
- package/.aether/skills/colony/colony-lifecycle/SKILL.md +77 -0
- package/.aether/skills/colony/colony-visuals/SKILL.md +112 -0
- package/.aether/skills/colony/context-management/SKILL.md +80 -0
- package/.aether/skills/colony/error-presentation/SKILL.md +99 -0
- package/.aether/skills/colony/pheromone-protocol/SKILL.md +79 -0
- package/.aether/skills/colony/pheromone-visibility/SKILL.md +81 -0
- package/.aether/skills/colony/state-safety/SKILL.md +84 -0
- package/.aether/skills/colony/worker-priming/SKILL.md +82 -0
- package/.aether/skills/domain/.manifest.json +24 -0
- package/.aether/skills/domain/README.md +33 -0
- package/.aether/skills/domain/django/SKILL.md +49 -0
- package/.aether/skills/domain/docker/SKILL.md +52 -0
- package/.aether/skills/domain/golang/SKILL.md +52 -0
- package/.aether/skills/domain/graphql/SKILL.md +51 -0
- package/.aether/skills/domain/html-css/SKILL.md +48 -0
- package/.aether/skills/domain/nextjs/SKILL.md +45 -0
- package/.aether/skills/domain/nodejs/SKILL.md +53 -0
- package/.aether/skills/domain/postgresql/SKILL.md +53 -0
- package/.aether/skills/domain/prisma/SKILL.md +59 -0
- package/.aether/skills/domain/python/SKILL.md +50 -0
- package/.aether/skills/domain/rails/SKILL.md +52 -0
- package/.aether/skills/domain/react/SKILL.md +45 -0
- package/.aether/skills/domain/rest-api/SKILL.md +58 -0
- package/.aether/skills/domain/svelte/SKILL.md +47 -0
- package/.aether/skills/domain/tailwind/SKILL.md +45 -0
- package/.aether/skills/domain/testing/SKILL.md +53 -0
- package/.aether/skills/domain/typescript/SKILL.md +58 -0
- package/.aether/skills/domain/vue/SKILL.md +49 -0
- package/.aether/templates/QUEEN.md.template +23 -41
- package/.aether/templates/colony-state-reset.jq.template +1 -0
- package/.aether/templates/colony-state.template.json +4 -0
- package/.aether/templates/learning-observations.template.json +6 -0
- package/.aether/templates/midden.template.json +13 -0
- package/.aether/templates/pheromones.template.json +6 -0
- package/.aether/templates/session.template.json +9 -0
- package/.aether/utils/atomic-write.sh +63 -17
- package/.aether/utils/chamber-utils.sh +145 -2
- package/.aether/utils/emoji-audit.sh +166 -0
- package/.aether/utils/error-handler.sh +21 -7
- package/.aether/utils/file-lock.sh +182 -27
- package/.aether/utils/flag.sh +267 -0
- package/.aether/utils/hive.sh +572 -0
- package/.aether/utils/learning.sh +1928 -0
- package/.aether/utils/midden.sh +342 -0
- package/.aether/utils/oracle/oracle.md +168 -0
- package/.aether/utils/oracle/oracle.sh +1023 -0
- package/.aether/utils/pheromone.sh +2029 -0
- package/.aether/utils/queen.sh +1698 -0
- package/.aether/utils/scan.sh +860 -0
- package/.aether/utils/semantic-cli.sh +10 -8
- package/.aether/utils/session.sh +552 -0
- package/.aether/utils/skills.sh +509 -0
- package/.aether/utils/spawn-tree.sh +103 -271
- package/.aether/utils/spawn.sh +260 -0
- package/.aether/utils/state-api.sh +199 -0
- package/.aether/utils/state-loader.sh +8 -6
- package/.aether/utils/suggest.sh +611 -0
- package/.aether/utils/swarm-display.sh +10 -1
- package/.aether/utils/swarm.sh +1004 -0
- package/.aether/utils/watch-spawn-tree.sh +11 -2
- package/.aether/utils/xml-compose.sh +2 -2
- package/.aether/utils/xml-convert.sh +9 -5
- package/.aether/utils/xml-core.sh +5 -9
- package/.aether/utils/xml-query.sh +4 -4
- package/.aether/workers.md +86 -67
- package/.claude/agents/ant/aether-ambassador.md +2 -1
- package/.claude/agents/ant/aether-archaeologist.md +6 -1
- package/.claude/agents/ant/aether-architect.md +236 -0
- package/.claude/agents/ant/aether-auditor.md +6 -1
- package/.claude/agents/ant/aether-builder.md +38 -1
- package/.claude/agents/ant/aether-chaos.md +2 -1
- package/.claude/agents/ant/aether-chronicler.md +1 -0
- package/.claude/agents/ant/aether-gatekeeper.md +6 -1
- package/.claude/agents/ant/aether-includer.md +1 -0
- package/.claude/agents/ant/aether-keeper.md +1 -0
- package/.claude/agents/ant/aether-measurer.md +6 -1
- package/.claude/agents/ant/aether-oracle.md +237 -0
- package/.claude/agents/ant/aether-probe.md +2 -1
- package/.claude/agents/ant/aether-queen.md +6 -1
- package/.claude/agents/ant/aether-route-setter.md +6 -1
- package/.claude/agents/ant/aether-sage.md +68 -3
- package/.claude/agents/ant/aether-scout.md +38 -1
- package/.claude/agents/ant/aether-surveyor-disciplines.md +2 -1
- package/.claude/agents/ant/aether-surveyor-nest.md +2 -1
- package/.claude/agents/ant/aether-surveyor-pathogens.md +2 -1
- package/.claude/agents/ant/aether-surveyor-provisions.md +2 -1
- package/.claude/agents/ant/aether-tracker.md +6 -1
- package/.claude/agents/ant/aether-watcher.md +37 -1
- package/.claude/agents/ant/aether-weaver.md +2 -1
- package/.claude/commands/ant/archaeology.md +1 -8
- package/.claude/commands/ant/build.md +43 -1159
- package/.claude/commands/ant/chaos.md +1 -14
- package/.claude/commands/ant/colonize.md +1 -14
- package/.claude/commands/ant/continue.md +40 -1026
- package/.claude/commands/ant/council.md +9 -16
- package/.claude/commands/ant/data-clean.md +81 -0
- package/.claude/commands/ant/dream.md +12 -9
- package/.claude/commands/ant/entomb.md +62 -87
- package/.claude/commands/ant/export-signals.md +57 -0
- package/.claude/commands/ant/feedback.md +18 -0
- package/.claude/commands/ant/flag.md +12 -0
- package/.claude/commands/ant/flags.md +22 -8
- package/.claude/commands/ant/focus.md +18 -0
- package/.claude/commands/ant/help.md +40 -8
- package/.claude/commands/ant/history.md +3 -0
- package/.claude/commands/ant/import-signals.md +71 -0
- package/.claude/commands/ant/init.md +316 -191
- package/.claude/commands/ant/insert-phase.md +101 -0
- package/.claude/commands/ant/interpret.md +11 -0
- package/.claude/commands/ant/lay-eggs.md +167 -158
- package/.claude/commands/ant/maturity.md +22 -11
- package/.claude/commands/ant/memory-details.md +77 -0
- package/.claude/commands/ant/migrate-state.md +6 -0
- package/.claude/commands/ant/oracle.md +317 -62
- package/.claude/commands/ant/organize.md +10 -5
- package/.claude/commands/ant/patrol.md +620 -0
- package/.claude/commands/ant/pause-colony.md +8 -22
- package/.claude/commands/ant/phase.md +26 -37
- package/.claude/commands/ant/pheromones.md +156 -0
- package/.claude/commands/ant/plan.md +175 -52
- package/.claude/commands/ant/preferences.md +65 -0
- package/.claude/commands/ant/redirect.md +18 -0
- package/.claude/commands/ant/resume-colony.md +34 -20
- package/.claude/commands/ant/resume.md +51 -7
- package/.claude/commands/ant/run.md +195 -0
- package/.claude/commands/ant/seal.md +497 -78
- package/.claude/commands/ant/skill-create.md +286 -0
- package/.claude/commands/ant/status.md +127 -1
- package/.claude/commands/ant/swarm.md +11 -23
- package/.claude/commands/ant/tunnels.md +1 -0
- package/.claude/commands/ant/update.md +58 -135
- package/.claude/commands/ant/verify-castes.md +90 -42
- package/.claude/commands/ant/watch.md +1 -0
- package/.opencode/agents/aether-ambassador.md +1 -1
- package/.opencode/agents/aether-architect.md +133 -0
- package/.opencode/agents/aether-builder.md +3 -3
- package/.opencode/agents/aether-oracle.md +137 -0
- package/.opencode/agents/aether-queen.md +1 -1
- package/.opencode/agents/aether-route-setter.md +1 -1
- package/.opencode/agents/aether-scout.md +1 -1
- package/.opencode/agents/aether-surveyor-disciplines.md +6 -1
- package/.opencode/agents/aether-surveyor-nest.md +6 -1
- package/.opencode/agents/aether-surveyor-pathogens.md +6 -1
- package/.opencode/agents/aether-surveyor-provisions.md +6 -1
- package/.opencode/agents/aether-tracker.md +1 -1
- package/.opencode/agents/aether-watcher.md +1 -1
- package/.opencode/agents/aether-weaver.md +1 -1
- package/.opencode/commands/ant/archaeology.md +7 -14
- package/.opencode/commands/ant/build.md +54 -88
- package/.opencode/commands/ant/chaos.md +7 -24
- package/.opencode/commands/ant/colonize.md +8 -17
- package/.opencode/commands/ant/continue.md +595 -66
- package/.opencode/commands/ant/council.md +11 -22
- package/.opencode/commands/ant/data-clean.md +77 -0
- package/.opencode/commands/ant/dream.md +15 -17
- package/.opencode/commands/ant/entomb.md +28 -18
- package/.opencode/commands/ant/export-signals.md +54 -0
- package/.opencode/commands/ant/feedback.md +24 -5
- package/.opencode/commands/ant/flag.md +16 -4
- package/.opencode/commands/ant/flags.md +24 -10
- package/.opencode/commands/ant/focus.md +22 -5
- package/.opencode/commands/ant/help.md +41 -8
- package/.opencode/commands/ant/history.md +9 -0
- package/.opencode/commands/ant/import-signals.md +68 -0
- package/.opencode/commands/ant/init.md +365 -156
- package/.opencode/commands/ant/insert-phase.md +107 -0
- package/.opencode/commands/ant/interpret.md +16 -0
- package/.opencode/commands/ant/lay-eggs.md +184 -112
- package/.opencode/commands/ant/maturity.md +18 -2
- package/.opencode/commands/ant/memory-details.md +83 -0
- package/.opencode/commands/ant/migrate-state.md +12 -0
- package/.opencode/commands/ant/oracle.md +322 -67
- package/.opencode/commands/ant/organize.md +14 -12
- package/.opencode/commands/ant/patrol.md +626 -0
- package/.opencode/commands/ant/pause-colony.md +12 -29
- package/.opencode/commands/ant/phase.md +30 -40
- package/.opencode/commands/ant/pheromones.md +162 -0
- package/.opencode/commands/ant/plan.md +184 -56
- package/.opencode/commands/ant/preferences.md +71 -0
- package/.opencode/commands/ant/redirect.md +22 -5
- package/.opencode/commands/ant/resume-colony.md +38 -27
- package/.opencode/commands/ant/resume.md +71 -20
- package/.opencode/commands/ant/run.md +201 -0
- package/.opencode/commands/ant/seal.md +230 -25
- package/.opencode/commands/ant/skill-create.md +63 -0
- package/.opencode/commands/ant/status.md +124 -31
- package/.opencode/commands/ant/swarm.md +3 -345
- package/.opencode/commands/ant/tunnels.md +3 -9
- package/.opencode/commands/ant/update.md +63 -127
- package/.opencode/commands/ant/verify-castes.md +96 -42
- package/.opencode/commands/ant/watch.md +7 -0
- package/CHANGELOG.md +278 -1
- package/README.md +188 -340
- package/bin/cli.js +236 -429
- package/bin/generate-commands.js +186 -0
- package/bin/generate-commands.sh +128 -89
- package/bin/lib/spawn-logger.js +0 -15
- package/bin/lib/update-transaction.js +285 -35
- package/bin/npx-install.js +178 -0
- package/bin/validate-package.sh +85 -3
- package/package.json +7 -3
- package/.aether/CONTEXT.md +0 -160
- package/.aether/docs/QUEEN.md +0 -84
- package/.aether/exchange/colony-registry.xml +0 -11
- package/.aether/exchange/pheromones.xml +0 -87
- package/.aether/exchange/queen-wisdom.xml +0 -14
- package/.aether/model-profiles.yaml +0 -100
- package/.aether/utils/spawn-with-model.sh +0 -56
- package/bin/lib/model-profiles.js +0 -445
- package/bin/lib/model-verify.js +0 -288
- package/bin/lib/proxy-health.js +0 -253
- package/bin/lib/telemetry.js +0 -441
|
@@ -0,0 +1,1023 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Oracle Ant - Deep research loop using RALF pattern
|
|
3
|
+
# Usage: ./oracle.sh [max_iterations_override]
|
|
4
|
+
# Based on: https://github.com/snarktank/ralph
|
|
5
|
+
#
|
|
6
|
+
# Configuration is read from state.json (written by /ant:oracle wizard).
|
|
7
|
+
# Command-line arg overrides max_iterations if provided.
|
|
8
|
+
|
|
9
|
+
set -e
|
|
10
|
+
|
|
11
|
+
# Unset CLAUDECODE to allow spawning Claude CLI from within Claude Code
|
|
12
|
+
unset CLAUDECODE 2>/dev/null || true
|
|
13
|
+
|
|
14
|
+
# Configuration
|
|
15
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
16
|
+
AETHER_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
|
17
|
+
|
|
18
|
+
# State files live in the colony's .aether/oracle/ directory (not alongside this script)
|
|
19
|
+
ORACLE_STATE_DIR="$AETHER_ROOT/.aether/oracle"
|
|
20
|
+
|
|
21
|
+
# Files
|
|
22
|
+
STATE_FILE="$ORACLE_STATE_DIR/state.json"
|
|
23
|
+
PLAN_FILE="$ORACLE_STATE_DIR/plan.json"
|
|
24
|
+
GAPS_FILE="$ORACLE_STATE_DIR/gaps.md"
|
|
25
|
+
SYNTHESIS_FILE="$ORACLE_STATE_DIR/synthesis.md"
|
|
26
|
+
RESEARCH_PLAN_FILE="$ORACLE_STATE_DIR/research-plan.md"
|
|
27
|
+
STOP_FILE="$ORACLE_STATE_DIR/.stop"
|
|
28
|
+
ARCHIVE_DIR="$ORACLE_STATE_DIR/archive"
|
|
29
|
+
DISCOVERIES_DIR="$ORACLE_STATE_DIR/discoveries"
|
|
30
|
+
|
|
31
|
+
# Generate research-plan.md from state.json and plan.json
|
|
32
|
+
generate_research_plan() {
|
|
33
|
+
local state_file="$STATE_FILE"
|
|
34
|
+
local plan_file="$PLAN_FILE"
|
|
35
|
+
local output_file="$RESEARCH_PLAN_FILE"
|
|
36
|
+
|
|
37
|
+
# Bail if source files don't exist
|
|
38
|
+
[ -f "$state_file" ] || return 0
|
|
39
|
+
[ -f "$plan_file" ] || return 0
|
|
40
|
+
|
|
41
|
+
local topic iteration max_iter confidence status
|
|
42
|
+
topic=$(jq -r '.topic' "$state_file")
|
|
43
|
+
iteration=$(jq -r '.iteration' "$state_file")
|
|
44
|
+
max_iter=$(jq -r '.max_iterations' "$state_file")
|
|
45
|
+
confidence=$(jq -r '.overall_confidence' "$state_file")
|
|
46
|
+
status=$(jq -r '.status' "$state_file")
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
echo "# Research Plan"
|
|
50
|
+
echo ""
|
|
51
|
+
echo "**Topic:** $topic"
|
|
52
|
+
echo "**Status:** $status | **Iteration:** $iteration of $max_iter"
|
|
53
|
+
echo "**Overall Confidence:** ${confidence}%"
|
|
54
|
+
echo ""
|
|
55
|
+
echo "## Questions"
|
|
56
|
+
echo "| # | Question | Status | Confidence |"
|
|
57
|
+
echo "|---|----------|--------|------------|"
|
|
58
|
+
jq -r '.questions[] | "| \(.id) | \(.text) | \(.status) | \(.confidence)% |"' "$plan_file"
|
|
59
|
+
echo ""
|
|
60
|
+
echo "## Next Steps"
|
|
61
|
+
local next
|
|
62
|
+
next=$(jq -r '[.questions[] | select(.status != "answered")] | sort_by(.confidence) | first | .text // "All questions answered"' "$plan_file")
|
|
63
|
+
echo "Next investigation: $next"
|
|
64
|
+
|
|
65
|
+
# Show trust summary if available
|
|
66
|
+
local trust_ratio
|
|
67
|
+
trust_ratio=$(jq '.trust_summary.trust_ratio // -1' "$plan_file" 2>/dev/null || echo "-1")
|
|
68
|
+
if [ "$trust_ratio" -ge 0 ]; then
|
|
69
|
+
local total single multi
|
|
70
|
+
total=$(jq '.trust_summary.total_findings // 0' "$plan_file")
|
|
71
|
+
single=$(jq '.trust_summary.single_source // 0' "$plan_file")
|
|
72
|
+
multi=$(jq '.trust_summary.multi_source // 0' "$plan_file")
|
|
73
|
+
echo ""
|
|
74
|
+
echo "## Source Trust"
|
|
75
|
+
echo "| Total Findings | Multi-Source | Single-Source | Trust Ratio |"
|
|
76
|
+
echo "|----------------|-------------|---------------|-------------|"
|
|
77
|
+
echo "| $total | $multi | $single | ${trust_ratio}% |"
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
echo ""
|
|
81
|
+
echo "---"
|
|
82
|
+
echo "*Generated from plan.json -- do not edit directly*"
|
|
83
|
+
} > "$output_file"
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Determine research phase based on structural metrics in state.json and plan.json
|
|
87
|
+
# Phases: survey -> investigate -> synthesize -> verify
|
|
88
|
+
determine_phase() {
|
|
89
|
+
local state_file="$1"
|
|
90
|
+
local plan_file="$2"
|
|
91
|
+
|
|
92
|
+
# Bail to default if files missing
|
|
93
|
+
[ -f "$state_file" ] || { echo "survey"; return 0; }
|
|
94
|
+
[ -f "$plan_file" ] || { echo "survey"; return 0; }
|
|
95
|
+
|
|
96
|
+
local total_questions touched_count avg_confidence below_50_count
|
|
97
|
+
|
|
98
|
+
total_questions=$(jq '[.questions[]] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
99
|
+
if [ "$total_questions" -eq 0 ]; then
|
|
100
|
+
echo "survey"
|
|
101
|
+
return 0
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# Count questions with non-empty iterations_touched arrays
|
|
105
|
+
touched_count=$(jq '[.questions[] | select((.iterations_touched // []) | length > 0)] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
106
|
+
|
|
107
|
+
# Average confidence across all questions
|
|
108
|
+
avg_confidence=$(jq '[.questions[].confidence] | if length > 0 then (add / length) else 0 end | floor' "$plan_file" 2>/dev/null || echo "0")
|
|
109
|
+
|
|
110
|
+
# Count questions below 50% confidence that are not answered
|
|
111
|
+
below_50_count=$(jq '[.questions[] | select(.status != "answered" and .confidence < 50)] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
112
|
+
|
|
113
|
+
# verify: avg confidence >= 80%
|
|
114
|
+
if [ "$avg_confidence" -ge 80 ]; then
|
|
115
|
+
echo "verify"
|
|
116
|
+
return 0
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
# synthesize: avg confidence >= 60% OR fewer than 2 questions below 50%
|
|
120
|
+
if [ "$avg_confidence" -ge 60 ] || [ "$below_50_count" -lt 2 ]; then
|
|
121
|
+
echo "synthesize"
|
|
122
|
+
return 0
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# investigate: all questions touched OR avg confidence >= 25%
|
|
126
|
+
if [ "$touched_count" -ge "$total_questions" ] || [ "$avg_confidence" -ge 25 ]; then
|
|
127
|
+
echo "investigate"
|
|
128
|
+
return 0
|
|
129
|
+
fi
|
|
130
|
+
|
|
131
|
+
# Default: survey
|
|
132
|
+
echo "survey"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
# Build the complete prompt by prepending a phase-specific directive to oracle.md
|
|
136
|
+
build_oracle_prompt() {
|
|
137
|
+
local state_file="$1"
|
|
138
|
+
local oracle_md="$2"
|
|
139
|
+
local steering_directive="${3:-}"
|
|
140
|
+
|
|
141
|
+
local current_phase
|
|
142
|
+
current_phase=$(jq -r '.phase // "survey"' "$state_file" 2>/dev/null || echo "survey")
|
|
143
|
+
|
|
144
|
+
# Emit phase-specific directive
|
|
145
|
+
case "$current_phase" in
|
|
146
|
+
survey)
|
|
147
|
+
cat <<'DIRECTIVE'
|
|
148
|
+
## Current Phase: SURVEY
|
|
149
|
+
|
|
150
|
+
Cast a wide net -- get initial findings for every open question. Target untouched
|
|
151
|
+
questions first (those with empty iterations_touched arrays). Aim for 20-40%
|
|
152
|
+
confidence per question. List all discovered unknowns in gaps.md.
|
|
153
|
+
|
|
154
|
+
Do NOT go deep on any single question yet. Breadth over depth in this phase.
|
|
155
|
+
Your goal is to ensure every question has at least some initial findings before
|
|
156
|
+
the research moves to the investigation phase.
|
|
157
|
+
|
|
158
|
+
Source tracking is MANDATORY -- register sources and link every finding to source_ids.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
DIRECTIVE
|
|
163
|
+
;;
|
|
164
|
+
investigate)
|
|
165
|
+
cat <<'DIRECTIVE'
|
|
166
|
+
## Current Phase: INVESTIGATE
|
|
167
|
+
|
|
168
|
+
Target the lowest-confidence question and go DEEP. You MUST reference existing
|
|
169
|
+
findings in synthesis.md and ADD NEW information, not restate what is already there.
|
|
170
|
+
Aim to push confidence above 70% for your target question.
|
|
171
|
+
|
|
172
|
+
Update gaps.md with specific remaining unknowns. If you find contradictions with
|
|
173
|
+
existing findings, document them explicitly. One thoroughly investigated question
|
|
174
|
+
per iteration is better than shallow passes on many.
|
|
175
|
+
|
|
176
|
+
Source tracking is MANDATORY this iteration. Every new finding must have at least one source_id.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
DIRECTIVE
|
|
181
|
+
;;
|
|
182
|
+
synthesize)
|
|
183
|
+
cat <<'DIRECTIVE'
|
|
184
|
+
## Current Phase: SYNTHESIZE
|
|
185
|
+
|
|
186
|
+
Read ALL findings in synthesis.md before doing anything. Identify connections,
|
|
187
|
+
patterns, and contradictions ACROSS questions. Consolidate redundant findings.
|
|
188
|
+
Resolve contradictions with evidence. Push overall confidence toward the target.
|
|
189
|
+
|
|
190
|
+
Your job is NOT to find new information -- it is to make sense of what has already
|
|
191
|
+
been found. Cross-reference answers between questions. Strengthen weak claims
|
|
192
|
+
with evidence from other questions. Remove speculation that lacks support.
|
|
193
|
+
|
|
194
|
+
Verify source attribution is complete. Flag any findings missing source_ids.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
DIRECTIVE
|
|
199
|
+
;;
|
|
200
|
+
verify)
|
|
201
|
+
cat <<'DIRECTIVE'
|
|
202
|
+
## Current Phase: VERIFY
|
|
203
|
+
|
|
204
|
+
Focus on claims in gaps.md contradictions section. Cross-reference key findings
|
|
205
|
+
with additional sources. Confirm or correct confidence scores. Mark well-supported
|
|
206
|
+
questions as answered with 90%+ confidence.
|
|
207
|
+
|
|
208
|
+
Final gaps.md should contain only genuinely unresolvable unknowns. If a contradiction
|
|
209
|
+
cannot be resolved, document both positions with evidence quality assessment.
|
|
210
|
+
This is the final quality pass before research completion.
|
|
211
|
+
|
|
212
|
+
Cross-reference source coverage. Ensure all key findings have 2+ independent sources.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
DIRECTIVE
|
|
217
|
+
;;
|
|
218
|
+
*)
|
|
219
|
+
echo "## Current Phase: $current_phase"
|
|
220
|
+
echo ""
|
|
221
|
+
echo "---"
|
|
222
|
+
echo ""
|
|
223
|
+
;;
|
|
224
|
+
esac
|
|
225
|
+
|
|
226
|
+
# Inject steering directive if provided
|
|
227
|
+
if [ -n "$steering_directive" ]; then
|
|
228
|
+
echo "$steering_directive"
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
# Read strategy and emit modifier
|
|
232
|
+
local strategy
|
|
233
|
+
strategy=$(jq -r '.strategy // "adaptive"' "$state_file" 2>/dev/null || echo "adaptive")
|
|
234
|
+
|
|
235
|
+
case "$strategy" in
|
|
236
|
+
breadth-first)
|
|
237
|
+
cat <<'STRATEGY'
|
|
238
|
+
|
|
239
|
+
STRATEGY NOTE: Breadth-first mode is active. Prioritize covering ALL questions
|
|
240
|
+
before going deep on any single one. Aim for broad coverage across the research
|
|
241
|
+
plan. When multiple questions are untouched, target the easiest-to-answer first
|
|
242
|
+
for maximum coverage.
|
|
243
|
+
|
|
244
|
+
STRATEGY
|
|
245
|
+
;;
|
|
246
|
+
depth-first)
|
|
247
|
+
cat <<'STRATEGY'
|
|
248
|
+
|
|
249
|
+
STRATEGY NOTE: Depth-first mode is active. Pick the single most important open
|
|
250
|
+
question and investigate it exhaustively. Push confidence to 80%+ before moving
|
|
251
|
+
to the next question. Prefer thorough, well-sourced answers over broad coverage.
|
|
252
|
+
|
|
253
|
+
STRATEGY
|
|
254
|
+
;;
|
|
255
|
+
*)
|
|
256
|
+
# adaptive: no strategy modifier needed
|
|
257
|
+
;;
|
|
258
|
+
esac
|
|
259
|
+
|
|
260
|
+
# Emit the base oracle.md prompt
|
|
261
|
+
cat "$oracle_md"
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
# Compute convergence metrics from plan.json structural data
|
|
265
|
+
# Returns JSON object with gap_resolution_pct, coverage_pct, novelty_delta, total_findings
|
|
266
|
+
compute_convergence() {
|
|
267
|
+
local plan_file="$1"
|
|
268
|
+
local state_file="$2"
|
|
269
|
+
|
|
270
|
+
local total answered partial_high
|
|
271
|
+
|
|
272
|
+
total=$(jq '[.questions[]] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
273
|
+
answered=$(jq '[.questions[] | select(.status == "answered")] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
274
|
+
partial_high=$(jq '[.questions[] | select(.status == "partial" and .confidence >= 70)] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
275
|
+
|
|
276
|
+
# Gap resolution: fraction of questions substantively addressed
|
|
277
|
+
local gap_resolution
|
|
278
|
+
if [ "$total" -eq 0 ]; then
|
|
279
|
+
gap_resolution=100
|
|
280
|
+
else
|
|
281
|
+
gap_resolution=$(( (answered + partial_high) * 100 / total ))
|
|
282
|
+
fi
|
|
283
|
+
|
|
284
|
+
# Coverage: fraction of questions with non-empty iterations_touched
|
|
285
|
+
local touched coverage
|
|
286
|
+
touched=$(jq '[.questions[] | select((.iterations_touched // []) | length > 0)] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
287
|
+
if [ "$total" -eq 0 ]; then
|
|
288
|
+
coverage=100
|
|
289
|
+
else
|
|
290
|
+
coverage=$(( touched * 100 / total ))
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
# Novelty: compare total findings count to previous iteration
|
|
294
|
+
local current_findings prev_findings novelty_delta
|
|
295
|
+
current_findings=$(jq '[.questions[].key_findings | length] | add // 0' "$plan_file" 2>/dev/null || echo "0")
|
|
296
|
+
prev_findings=$(jq '.convergence.prev_findings_count // 0' "$state_file" 2>/dev/null || echo "0")
|
|
297
|
+
novelty_delta=$(( current_findings - prev_findings ))
|
|
298
|
+
|
|
299
|
+
jq -n --argjson gap "$gap_resolution" --argjson cov "$coverage" \
|
|
300
|
+
--argjson novelty "$novelty_delta" --argjson findings "$current_findings" \
|
|
301
|
+
'{gap_resolution_pct: $gap, coverage_pct: $cov, novelty_delta: $novelty, total_findings: $findings}'
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
# Update convergence metrics in state.json after each iteration
|
|
305
|
+
update_convergence_metrics() {
|
|
306
|
+
local state_file="$1"
|
|
307
|
+
local plan_file="$2"
|
|
308
|
+
|
|
309
|
+
local metrics
|
|
310
|
+
metrics=$(compute_convergence "$plan_file" "$state_file")
|
|
311
|
+
|
|
312
|
+
local gap_pct coverage_pct novelty_delta total_findings
|
|
313
|
+
gap_pct=$(echo "$metrics" | jq '.gap_resolution_pct')
|
|
314
|
+
coverage_pct=$(echo "$metrics" | jq '.coverage_pct')
|
|
315
|
+
novelty_delta=$(echo "$metrics" | jq '.novelty_delta')
|
|
316
|
+
total_findings=$(echo "$metrics" | jq '.total_findings')
|
|
317
|
+
|
|
318
|
+
local current_confidence prev_confidence confidence_delta current_iteration current_phase
|
|
319
|
+
current_confidence=$(jq '.overall_confidence // 0' "$state_file" 2>/dev/null || echo "0")
|
|
320
|
+
prev_confidence=$(jq '.convergence.prev_overall_confidence // 0' "$state_file" 2>/dev/null || echo "0")
|
|
321
|
+
confidence_delta=$(( current_confidence - prev_confidence ))
|
|
322
|
+
current_iteration=$(jq '.iteration // 0' "$state_file" 2>/dev/null || echo "0")
|
|
323
|
+
current_phase=$(jq -r '.phase // "survey"' "$state_file" 2>/dev/null || echo "survey")
|
|
324
|
+
|
|
325
|
+
# Compute composite score:
|
|
326
|
+
# gap_resolution * 0.4 + coverage * 0.3 + (novelty_delta <= 1 ? 100 : 0) * 0.3
|
|
327
|
+
# Using integer arithmetic scaled by 100
|
|
328
|
+
local novelty_component composite_score converged
|
|
329
|
+
if [ "$novelty_delta" -le 1 ]; then
|
|
330
|
+
novelty_component=100
|
|
331
|
+
else
|
|
332
|
+
novelty_component=0
|
|
333
|
+
fi
|
|
334
|
+
composite_score=$(( gap_pct * 40 / 100 + coverage_pct * 30 / 100 + novelty_component * 30 / 100 ))
|
|
335
|
+
|
|
336
|
+
local conv_threshold
|
|
337
|
+
conv_threshold=${ORACLE_CONVERGENCE_THRESHOLD:-85}
|
|
338
|
+
if [ "$composite_score" -ge "$conv_threshold" ]; then
|
|
339
|
+
converged="true"
|
|
340
|
+
else
|
|
341
|
+
converged="false"
|
|
342
|
+
fi
|
|
343
|
+
|
|
344
|
+
# Update state.json with convergence data
|
|
345
|
+
jq --argjson prev_findings "$total_findings" \
|
|
346
|
+
--argjson prev_confidence "$current_confidence" \
|
|
347
|
+
--argjson iteration "$current_iteration" \
|
|
348
|
+
--argjson novelty "$novelty_delta" \
|
|
349
|
+
--argjson conf_delta "$confidence_delta" \
|
|
350
|
+
--argjson gap "$gap_pct" \
|
|
351
|
+
--argjson cov "$coverage_pct" \
|
|
352
|
+
--arg phase "$current_phase" \
|
|
353
|
+
--argjson composite "$composite_score" \
|
|
354
|
+
--argjson converged "$converged" \
|
|
355
|
+
'
|
|
356
|
+
.convergence = (.convergence // {}) |
|
|
357
|
+
.convergence.prev_findings_count = $prev_findings |
|
|
358
|
+
.convergence.prev_overall_confidence = $prev_confidence |
|
|
359
|
+
.convergence.history = ((.convergence.history // []) + [{
|
|
360
|
+
iteration: $iteration,
|
|
361
|
+
novelty_delta: $novelty,
|
|
362
|
+
confidence_delta: $conf_delta,
|
|
363
|
+
gap_resolution_pct: $gap,
|
|
364
|
+
coverage_pct: $cov,
|
|
365
|
+
phase: $phase
|
|
366
|
+
}]) |
|
|
367
|
+
.convergence.composite_score = $composite |
|
|
368
|
+
.convergence.converged = $converged
|
|
369
|
+
' "$state_file" > "$state_file.tmp" && mv "$state_file.tmp" "$state_file"
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
# Compute trust scores from plan.json source tracking data
|
|
373
|
+
# Writes trust metadata to plan.json (trust_summary field)
|
|
374
|
+
compute_trust_scores() {
|
|
375
|
+
local plan_file="$1"
|
|
376
|
+
|
|
377
|
+
# Check if plan.json uses the new structured findings format
|
|
378
|
+
local has_structured
|
|
379
|
+
has_structured=$(jq '
|
|
380
|
+
[.questions[].key_findings[] | type] | if length == 0 then false else any(. == "object") end
|
|
381
|
+
' "$plan_file" 2>/dev/null || echo "false")
|
|
382
|
+
|
|
383
|
+
if [ "$has_structured" != "true" ]; then
|
|
384
|
+
# Pre-Phase-9 plan.json with string findings -- skip trust computation
|
|
385
|
+
return 0
|
|
386
|
+
fi
|
|
387
|
+
|
|
388
|
+
local total_findings single_source multi_source no_source
|
|
389
|
+
total_findings=$(jq '[.questions[].key_findings[]] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
390
|
+
single_source=$(jq '[.questions[].key_findings[] | select(type == "object" and (.source_ids | length) == 1)] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
391
|
+
multi_source=$(jq '[.questions[].key_findings[] | select(type == "object" and (.source_ids | length) >= 2)] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
392
|
+
no_source=$(jq '[.questions[].key_findings[] | select(type == "object" and ((.source_ids // []) | length) == 0)] | length' "$plan_file" 2>/dev/null || echo "0")
|
|
393
|
+
|
|
394
|
+
local trust_ratio=0
|
|
395
|
+
if [ "$total_findings" -gt 0 ]; then
|
|
396
|
+
trust_ratio=$(( multi_source * 100 / total_findings ))
|
|
397
|
+
fi
|
|
398
|
+
|
|
399
|
+
jq --argjson total "$total_findings" \
|
|
400
|
+
--argjson single "$single_source" \
|
|
401
|
+
--argjson multi "$multi_source" \
|
|
402
|
+
--argjson nosrc "$no_source" \
|
|
403
|
+
--argjson ratio "$trust_ratio" \
|
|
404
|
+
'.trust_summary = {
|
|
405
|
+
total_findings: $total,
|
|
406
|
+
single_source: $single,
|
|
407
|
+
multi_source: $multi,
|
|
408
|
+
no_source: $nosrc,
|
|
409
|
+
trust_ratio: $ratio
|
|
410
|
+
}' "$plan_file" > "$plan_file.tmp" && mv "$plan_file.tmp" "$plan_file"
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
# Read active pheromone signals and format as a steering directive for the AI prompt
|
|
414
|
+
# Returns formatted steering text, or empty string if no signals active
|
|
415
|
+
read_steering_signals() {
|
|
416
|
+
local aether_root="$1"
|
|
417
|
+
local utils="$aether_root/.aether/aether-utils.sh"
|
|
418
|
+
|
|
419
|
+
# Gracefully handle missing pheromone system
|
|
420
|
+
if [ ! -f "$utils" ]; then
|
|
421
|
+
echo ""
|
|
422
|
+
return 0
|
|
423
|
+
fi
|
|
424
|
+
|
|
425
|
+
# Read active signals via pheromone-read (handles decay, expiry, filtering)
|
|
426
|
+
local signals
|
|
427
|
+
signals=$(bash "$utils" pheromone-read 2>/dev/null || echo '{"signals":[]}')
|
|
428
|
+
|
|
429
|
+
# Extract the signals array from the json_ok wrapper
|
|
430
|
+
local signal_array
|
|
431
|
+
signal_array=$(echo "$signals" | jq -c '.result.signals // .signals // []' 2>/dev/null || echo '[]')
|
|
432
|
+
|
|
433
|
+
local count
|
|
434
|
+
count=$(echo "$signal_array" | jq 'length' 2>/dev/null || echo "0")
|
|
435
|
+
|
|
436
|
+
if [ "$count" -eq 0 ]; then
|
|
437
|
+
echo ""
|
|
438
|
+
return 0
|
|
439
|
+
fi
|
|
440
|
+
|
|
441
|
+
# Format steering directive
|
|
442
|
+
local directive=""
|
|
443
|
+
|
|
444
|
+
# REDIRECT signals (highest priority -- hard constraints, max 2)
|
|
445
|
+
local redirects
|
|
446
|
+
redirects=$(echo "$signal_array" | jq -r '
|
|
447
|
+
map(select(.type == "REDIRECT"))
|
|
448
|
+
| sort_by(-.effective_strength)
|
|
449
|
+
| .[:2]
|
|
450
|
+
| .[] | "- [" + ((.effective_strength * 100 | floor | tostring)) + "%] \"" + (.content.text // "") + "\""
|
|
451
|
+
' 2>/dev/null || echo "")
|
|
452
|
+
|
|
453
|
+
if [ -n "$redirects" ]; then
|
|
454
|
+
directive+="**REDIRECT (Hard constraints -- MUST follow):**"$'\n'"$redirects"$'\n\n'
|
|
455
|
+
fi
|
|
456
|
+
|
|
457
|
+
# FOCUS signals (prioritization, max 3)
|
|
458
|
+
local focuses
|
|
459
|
+
focuses=$(echo "$signal_array" | jq -r '
|
|
460
|
+
map(select(.type == "FOCUS"))
|
|
461
|
+
| sort_by(-.effective_strength)
|
|
462
|
+
| .[:3]
|
|
463
|
+
| .[] | "- [" + ((.effective_strength * 100 | floor | tostring)) + "%] \"" + (.content.text // "") + "\""
|
|
464
|
+
' 2>/dev/null || echo "")
|
|
465
|
+
|
|
466
|
+
if [ -n "$focuses" ]; then
|
|
467
|
+
directive+="**FOCUS (Prioritize these areas):**"$'\n'"$focuses"$'\n\n'
|
|
468
|
+
fi
|
|
469
|
+
|
|
470
|
+
# FEEDBACK signals (behavioral adjustment, max 2)
|
|
471
|
+
local feedbacks
|
|
472
|
+
feedbacks=$(echo "$signal_array" | jq -r '
|
|
473
|
+
map(select(.type == "FEEDBACK"))
|
|
474
|
+
| sort_by(-.effective_strength)
|
|
475
|
+
| .[:2]
|
|
476
|
+
| .[] | "- [" + ((.effective_strength * 100 | floor | tostring)) + "%] \"" + (.content.text // "") + "\""
|
|
477
|
+
' 2>/dev/null || echo "")
|
|
478
|
+
|
|
479
|
+
if [ -n "$feedbacks" ]; then
|
|
480
|
+
directive+="**FEEDBACK (Adjust approach):**"$'\n'"$feedbacks"$'\n\n'
|
|
481
|
+
fi
|
|
482
|
+
|
|
483
|
+
if [ -n "$directive" ]; then
|
|
484
|
+
echo "## Active Steering Signals"
|
|
485
|
+
echo ""
|
|
486
|
+
echo "$directive"
|
|
487
|
+
echo "When selecting your target question, PRIORITIZE questions related to FOCUS areas."
|
|
488
|
+
echo "REDIRECT signals are hard constraints -- adjust your approach to comply."
|
|
489
|
+
echo "FEEDBACK signals are suggestions -- incorporate where appropriate."
|
|
490
|
+
echo ""
|
|
491
|
+
echo "---"
|
|
492
|
+
echo ""
|
|
493
|
+
fi
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
# Check if research has converged
|
|
497
|
+
# Returns 0 (true) if composite_score >= threshold AND last 2 history entries have low novelty
|
|
498
|
+
check_convergence() {
|
|
499
|
+
local state_file="$1"
|
|
500
|
+
|
|
501
|
+
local conv_threshold
|
|
502
|
+
conv_threshold=${ORACLE_CONVERGENCE_THRESHOLD:-85}
|
|
503
|
+
|
|
504
|
+
local composite_score
|
|
505
|
+
composite_score=$(jq '.convergence.composite_score // 0' "$state_file" 2>/dev/null || echo "0")
|
|
506
|
+
|
|
507
|
+
if [ "$composite_score" -lt "$conv_threshold" ]; then
|
|
508
|
+
return 1
|
|
509
|
+
fi
|
|
510
|
+
|
|
511
|
+
# Check that at least 2 history entries exist and last 2 have novelty_delta <= 1
|
|
512
|
+
local history_len
|
|
513
|
+
history_len=$(jq '(.convergence.history // []) | length' "$state_file" 2>/dev/null || echo "0")
|
|
514
|
+
if [ "$history_len" -lt 2 ]; then
|
|
515
|
+
return 1
|
|
516
|
+
fi
|
|
517
|
+
|
|
518
|
+
local low_novelty_count
|
|
519
|
+
low_novelty_count=$(jq '[(.convergence.history // [])[-2:][] | select(.novelty_delta <= 1)] | length' "$state_file" 2>/dev/null || echo "0")
|
|
520
|
+
|
|
521
|
+
if [ "$low_novelty_count" -ge 2 ]; then
|
|
522
|
+
return 0
|
|
523
|
+
fi
|
|
524
|
+
|
|
525
|
+
return 1
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
# Detect diminishing returns from convergence history
|
|
529
|
+
# Outputs: "strategy_change", "synthesize_now", or "continue"
|
|
530
|
+
detect_diminishing_returns() {
|
|
531
|
+
local state_file="$1"
|
|
532
|
+
|
|
533
|
+
local dr_window
|
|
534
|
+
dr_window=${ORACLE_DR_WINDOW:-3}
|
|
535
|
+
|
|
536
|
+
local history_len
|
|
537
|
+
history_len=$(jq '(.convergence.history // []) | length' "$state_file" 2>/dev/null || echo "0")
|
|
538
|
+
|
|
539
|
+
if [ "$history_len" -lt "$dr_window" ]; then
|
|
540
|
+
echo "continue"
|
|
541
|
+
return 0
|
|
542
|
+
fi
|
|
543
|
+
|
|
544
|
+
local current_phase
|
|
545
|
+
current_phase=$(jq -r '.phase // "survey"' "$state_file" 2>/dev/null || echo "survey")
|
|
546
|
+
|
|
547
|
+
# Phase-adjusted novelty threshold
|
|
548
|
+
local novelty_threshold
|
|
549
|
+
case "$current_phase" in
|
|
550
|
+
investigate) novelty_threshold=0 ;;
|
|
551
|
+
*) novelty_threshold=1 ;;
|
|
552
|
+
esac
|
|
553
|
+
|
|
554
|
+
# Count entries in the last dr_window with novelty at or below threshold
|
|
555
|
+
local low_change_count
|
|
556
|
+
low_change_count=$(jq --argjson window "$dr_window" --argjson threshold "$novelty_threshold" \
|
|
557
|
+
'[(.convergence.history // [])[-$window:][] | select(.novelty_delta <= $threshold)] | length' \
|
|
558
|
+
"$state_file" 2>/dev/null || echo "0")
|
|
559
|
+
|
|
560
|
+
if [ "$low_change_count" -ge "$dr_window" ]; then
|
|
561
|
+
case "$current_phase" in
|
|
562
|
+
survey|investigate)
|
|
563
|
+
echo "strategy_change"
|
|
564
|
+
;;
|
|
565
|
+
synthesize|verify)
|
|
566
|
+
echo "synthesize_now"
|
|
567
|
+
;;
|
|
568
|
+
*)
|
|
569
|
+
echo "continue"
|
|
570
|
+
;;
|
|
571
|
+
esac
|
|
572
|
+
else
|
|
573
|
+
echo "continue"
|
|
574
|
+
fi
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
# Validate a JSON file and recover from backup if invalid
|
|
578
|
+
validate_and_recover() {
|
|
579
|
+
local file="$1"
|
|
580
|
+
|
|
581
|
+
if jq -e . "$file" >/dev/null 2>&1; then
|
|
582
|
+
return 0
|
|
583
|
+
fi
|
|
584
|
+
|
|
585
|
+
echo "WARNING: $(basename "$file") is invalid JSON. Attempting recovery..." >&2
|
|
586
|
+
|
|
587
|
+
# Try pre-iteration backup
|
|
588
|
+
if [ -f "${file}.pre-iteration" ] && jq -e . "${file}.pre-iteration" >/dev/null 2>&1; then
|
|
589
|
+
cp "${file}.pre-iteration" "$file"
|
|
590
|
+
echo " Recovered $(basename "$file") from pre-iteration backup." >&2
|
|
591
|
+
return 0
|
|
592
|
+
fi
|
|
593
|
+
|
|
594
|
+
# Fall back to atomic-write backup system
|
|
595
|
+
if [ -f "$AETHER_ROOT/.aether/utils/atomic-write.sh" ]; then
|
|
596
|
+
source "$AETHER_ROOT/.aether/utils/atomic-write.sh" 2>/dev/null || true
|
|
597
|
+
if type restore_backup >/dev/null 2>&1 && restore_backup "$file" 2>/dev/null; then
|
|
598
|
+
echo " Recovered $(basename "$file") from atomic-write backup." >&2
|
|
599
|
+
return 0
|
|
600
|
+
fi
|
|
601
|
+
fi
|
|
602
|
+
|
|
603
|
+
echo " FATAL: Cannot recover $(basename "$file")." >&2
|
|
604
|
+
return 1
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
# Build the synthesis-specific prompt for the final AI pass
|
|
608
|
+
build_synthesis_prompt() {
|
|
609
|
+
local reason="$1"
|
|
610
|
+
|
|
611
|
+
# Read template type from state.json
|
|
612
|
+
local template
|
|
613
|
+
template=$(jq -r '.template // "custom"' "$STATE_FILE" 2>/dev/null || echo "custom")
|
|
614
|
+
|
|
615
|
+
cat <<SYNTHESIS_DIRECTIVE
|
|
616
|
+
## SYNTHESIS PASS (Final Report)
|
|
617
|
+
|
|
618
|
+
This is the final pass. The oracle loop has ended (reason: $reason).
|
|
619
|
+
Produce the best possible research report from the current state.
|
|
620
|
+
|
|
621
|
+
Read ALL of these files:
|
|
622
|
+
- .aether/oracle/state.json -- session metadata
|
|
623
|
+
- .aether/oracle/plan.json -- questions, findings, confidence, AND sources registry
|
|
624
|
+
- .aether/oracle/synthesis.md -- accumulated findings
|
|
625
|
+
- .aether/oracle/gaps.md -- remaining unknowns
|
|
626
|
+
|
|
627
|
+
If any state file is unreadable, skip it and work with what you have.
|
|
628
|
+
|
|
629
|
+
Then REWRITE synthesis.md as a structured final report.
|
|
630
|
+
|
|
631
|
+
SYNTHESIS_DIRECTIVE
|
|
632
|
+
|
|
633
|
+
# Emit template-specific sections
|
|
634
|
+
case "$template" in
|
|
635
|
+
tech-eval)
|
|
636
|
+
cat <<'TEMPLATE'
|
|
637
|
+
### Required Sections:
|
|
638
|
+
1. **Executive Summary** -- 2-3 paragraphs: what was evaluated, key conclusion, recommendation
|
|
639
|
+
2. **Comparison Matrix** -- Table comparing the evaluated technology against alternatives on key dimensions (performance, community, learning curve, maturity, ecosystem)
|
|
640
|
+
3. **Pros and Cons** -- Bullet lists of advantages and disadvantages with evidence citations
|
|
641
|
+
4. **Adoption Assessment** -- Community size, maintenance status, release cadence, corporate backing
|
|
642
|
+
5. **Migration/Integration Path** -- Steps to adopt, estimated effort, risks
|
|
643
|
+
6. **Recommendation** -- Clear recommendation with confidence level and conditions/caveats
|
|
644
|
+
7. **Open Questions** -- Remaining gaps
|
|
645
|
+
8. **Sources** -- All sources with inline citations [S1], [S2] format
|
|
646
|
+
|
|
647
|
+
TEMPLATE
|
|
648
|
+
;;
|
|
649
|
+
architecture-review)
|
|
650
|
+
cat <<'TEMPLATE'
|
|
651
|
+
### Required Sections:
|
|
652
|
+
1. **Executive Summary** -- 2-3 paragraphs: system overview, key findings, critical risks
|
|
653
|
+
2. **Component Map** -- List of major components with responsibilities and boundaries
|
|
654
|
+
3. **Dependency Analysis** -- How components connect, coupling assessment, external dependencies
|
|
655
|
+
4. **Risk Assessment** -- Single points of failure, complexity hotspots, scaling bottlenecks
|
|
656
|
+
5. **Scalability Analysis** -- Current capacity, growth limitations, scaling strategy
|
|
657
|
+
6. **Improvement Recommendations** -- Prioritized list of architectural improvements
|
|
658
|
+
7. **Open Questions** -- Remaining gaps
|
|
659
|
+
8. **Sources** -- All sources with inline citations [S1], [S2] format
|
|
660
|
+
|
|
661
|
+
TEMPLATE
|
|
662
|
+
;;
|
|
663
|
+
bug-investigation)
|
|
664
|
+
cat <<'TEMPLATE'
|
|
665
|
+
### Required Sections:
|
|
666
|
+
1. **Executive Summary** -- 1-2 paragraphs: bug description, root cause, recommended fix
|
|
667
|
+
2. **Reproduction Steps** -- Exact steps to reproduce, environment details, frequency
|
|
668
|
+
3. **Root Cause Analysis** -- What causes the bug, code paths involved, why it was introduced
|
|
669
|
+
4. **Impact Assessment** -- Who is affected, severity, data loss risk
|
|
670
|
+
5. **Fix Recommendations** -- Proposed fixes ranked by safety and effort, with tradeoffs
|
|
671
|
+
6. **Related Issues** -- Similar bugs, upstream/downstream effects, regression risk
|
|
672
|
+
7. **Open Questions** -- Remaining gaps
|
|
673
|
+
8. **Sources** -- All sources with inline citations [S1], [S2] format
|
|
674
|
+
|
|
675
|
+
TEMPLATE
|
|
676
|
+
;;
|
|
677
|
+
best-practices)
|
|
678
|
+
cat <<'TEMPLATE'
|
|
679
|
+
### Required Sections:
|
|
680
|
+
1. **Executive Summary** -- 2-3 paragraphs: domain overview, current state assessment, key recommendations
|
|
681
|
+
2. **Best Practice Benchmark** -- What industry/community consensus considers best practice, with evidence
|
|
682
|
+
3. **Current State Assessment** -- How the subject compares to best practice (strengths and gaps)
|
|
683
|
+
4. **Gap Analysis** -- Specific gaps between current state and best practice, prioritized by impact
|
|
684
|
+
5. **Action Plan** -- Ordered steps to close gaps, estimated effort, quick wins highlighted
|
|
685
|
+
6. **Open Questions** -- Remaining gaps
|
|
686
|
+
7. **Sources** -- All sources with inline citations [S1], [S2] format
|
|
687
|
+
|
|
688
|
+
TEMPLATE
|
|
689
|
+
;;
|
|
690
|
+
*)
|
|
691
|
+
# custom or unrecognized: use existing generic structure
|
|
692
|
+
cat <<'TEMPLATE'
|
|
693
|
+
### Required Sections:
|
|
694
|
+
1. **Executive Summary** -- 2-3 paragraphs summarizing what was found
|
|
695
|
+
2. **Findings by Question** -- organized by sub-question, with confidence %. Use inline citations [S1], [S2] linking findings to their sources. Flag single-source findings with (single source) marker.
|
|
696
|
+
3. **Open Questions** -- remaining gaps with explanation of what is unknown and why
|
|
697
|
+
4. **Methodology Notes** -- how many iterations, which phases completed
|
|
698
|
+
5. **Sources** -- List ALL sources from plan.json sources registry: Format: [S1] Title -- URL (accessed: date). Group by type (documentation, blog, codebase, etc.). Note total source count and multi-source coverage percentage.
|
|
699
|
+
|
|
700
|
+
TEMPLATE
|
|
701
|
+
;;
|
|
702
|
+
esac
|
|
703
|
+
|
|
704
|
+
# Common directives for all templates
|
|
705
|
+
cat <<'COMMON'
|
|
706
|
+
|
|
707
|
+
### Confidence Grouping:
|
|
708
|
+
Within each findings section, group findings by confidence level:
|
|
709
|
+
- **High confidence (80%+)** -- list first with full citations
|
|
710
|
+
- **Medium confidence (50-79%)** -- list with caveats noted
|
|
711
|
+
- **Low confidence (<50%)** -- list as tentative/unverified
|
|
712
|
+
|
|
713
|
+
Use inline citations [S1], [S2] linking findings to their sources.
|
|
714
|
+
Flag single-source findings with (single source) marker.
|
|
715
|
+
|
|
716
|
+
Also update state.json: set status to "complete" if reason is "converged",
|
|
717
|
+
or "stopped" otherwise.
|
|
718
|
+
|
|
719
|
+
COMMON
|
|
720
|
+
|
|
721
|
+
# Append the base oracle.md for tool access and rules
|
|
722
|
+
cat "$SCRIPT_DIR/oracle.md"
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
# Run the synthesis pass: update state, invoke AI, regenerate research plan
|
|
726
|
+
run_synthesis_pass() {
|
|
727
|
+
local reason="$1"
|
|
728
|
+
|
|
729
|
+
# Update state.json status and stop_reason before AI call
|
|
730
|
+
local new_status
|
|
731
|
+
case "$reason" in
|
|
732
|
+
converged) new_status="complete" ;;
|
|
733
|
+
*) new_status="stopped" ;;
|
|
734
|
+
esac
|
|
735
|
+
|
|
736
|
+
if [ -f "$STATE_FILE" ] && jq -e . "$STATE_FILE" >/dev/null 2>&1; then
|
|
737
|
+
jq --arg status "$new_status" --arg reason "$reason" \
|
|
738
|
+
'.status = $status | .stop_reason = $reason' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
739
|
+
fi
|
|
740
|
+
|
|
741
|
+
echo ""
|
|
742
|
+
echo "==============================================================="
|
|
743
|
+
echo " SYNTHESIS PASS ($reason)"
|
|
744
|
+
echo "==============================================================="
|
|
745
|
+
|
|
746
|
+
# Invoke AI with synthesis prompt
|
|
747
|
+
if command -v timeout >/dev/null 2>&1; then
|
|
748
|
+
build_synthesis_prompt "$reason" | timeout 180 $AI_CMD 2>&1 | tee /dev/stderr || true
|
|
749
|
+
else
|
|
750
|
+
build_synthesis_prompt "$reason" | $AI_CMD 2>&1 | tee /dev/stderr || true
|
|
751
|
+
fi
|
|
752
|
+
|
|
753
|
+
# Regenerate research-plan.md one final time
|
|
754
|
+
generate_research_plan
|
|
755
|
+
|
|
756
|
+
echo ""
|
|
757
|
+
echo "Results saved to:"
|
|
758
|
+
echo " $SYNTHESIS_FILE"
|
|
759
|
+
echo " $RESEARCH_PLAN_FILE"
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
# Promote high-confidence oracle findings to colony knowledge
|
|
763
|
+
# Reads plan.json, extracts findings from questions with confidence >= 80% and status "answered"
|
|
764
|
+
# Calls instinct-create, learning-promote, and memory-capture for each qualifying finding
|
|
765
|
+
promote_to_colony() {
|
|
766
|
+
local plan_file="$1"
|
|
767
|
+
local state_file="$2"
|
|
768
|
+
local aether_root="$3"
|
|
769
|
+
local utils="$aether_root/.aether/aether-utils.sh"
|
|
770
|
+
|
|
771
|
+
# Verify state is complete or stopped (not active)
|
|
772
|
+
local status
|
|
773
|
+
status=$(jq -r '.status // "active"' "$state_file" 2>/dev/null || echo "active")
|
|
774
|
+
if [ "$status" = "active" ]; then
|
|
775
|
+
echo "ERROR: Research is still active. Wait for completion or stop the oracle first."
|
|
776
|
+
return 1
|
|
777
|
+
fi
|
|
778
|
+
|
|
779
|
+
# Verify colony state exists (promotion requires active colony)
|
|
780
|
+
if [ ! -f "$aether_root/.aether/data/COLONY_STATE.json" ]; then
|
|
781
|
+
echo "ERROR: No active colony. Run /ant:init first."
|
|
782
|
+
return 1
|
|
783
|
+
fi
|
|
784
|
+
|
|
785
|
+
local topic
|
|
786
|
+
topic=$(jq -r '.topic // "unknown"' "$state_file" 2>/dev/null || echo "unknown")
|
|
787
|
+
|
|
788
|
+
# Extract high-confidence answered questions (>= 80%)
|
|
789
|
+
local questions
|
|
790
|
+
questions=$(jq -c '[.questions[] | select(.status == "answered" and .confidence >= 80)]' "$plan_file" 2>/dev/null || echo "[]")
|
|
791
|
+
|
|
792
|
+
local count
|
|
793
|
+
count=$(echo "$questions" | jq 'length')
|
|
794
|
+
|
|
795
|
+
if [ "$count" -eq 0 ]; then
|
|
796
|
+
echo "No findings meet promotion threshold (answered + 80%+ confidence)."
|
|
797
|
+
echo "Lower-confidence findings remain in .aether/oracle/synthesis.md for reference."
|
|
798
|
+
return 0
|
|
799
|
+
fi
|
|
800
|
+
|
|
801
|
+
echo "Promoting $count high-confidence findings to colony knowledge..."
|
|
802
|
+
|
|
803
|
+
# Use process substitution to avoid subshell variable loss with while-read
|
|
804
|
+
local promoted=0
|
|
805
|
+
local failed=0
|
|
806
|
+
|
|
807
|
+
while IFS= read -r question; do
|
|
808
|
+
local q_text q_confidence findings_text
|
|
809
|
+
q_text=$(echo "$question" | jq -r '.text')
|
|
810
|
+
q_confidence=$(echo "$question" | jq -r '.confidence')
|
|
811
|
+
|
|
812
|
+
# Handle both v1.1 structured findings {text, source_ids, iteration} and v1.0 string findings
|
|
813
|
+
findings_text=$(echo "$question" | jq -r '[.key_findings[].text // .key_findings[]] | join("; ")' 2>/dev/null | head -c 200)
|
|
814
|
+
|
|
815
|
+
# Create instinct from the research finding
|
|
816
|
+
bash "$utils" instinct-create \
|
|
817
|
+
--trigger "researching: $q_text" \
|
|
818
|
+
--action "Oracle found (${q_confidence}% confidence): $findings_text" \
|
|
819
|
+
--confidence "$(echo "scale=2; $q_confidence / 100" | bc)" \
|
|
820
|
+
--domain "research" \
|
|
821
|
+
--source "oracle:$topic" \
|
|
822
|
+
--evidence "Oracle research: $q_text" 2>/dev/null || true
|
|
823
|
+
|
|
824
|
+
# Promote as learning (extract first finding for content)
|
|
825
|
+
local first_finding
|
|
826
|
+
first_finding=$(echo "$question" | jq -r '[.key_findings[].text // .key_findings[]] | first // "No findings"' 2>/dev/null)
|
|
827
|
+
bash "$utils" learning-promote \
|
|
828
|
+
"Oracle: $q_text -- $first_finding" \
|
|
829
|
+
"oracle" \
|
|
830
|
+
"oracle-research" \
|
|
831
|
+
"oracle,research" 2>/dev/null || true
|
|
832
|
+
|
|
833
|
+
# Record via memory-capture for observation tracking
|
|
834
|
+
bash "$utils" memory-capture learning \
|
|
835
|
+
"Oracle research finding: $q_text (${q_confidence}%)" \
|
|
836
|
+
"pattern" \
|
|
837
|
+
"oracle:promote" 2>/dev/null || true
|
|
838
|
+
|
|
839
|
+
promoted=$((promoted + 1))
|
|
840
|
+
done < <(echo "$questions" | jq -c '.[]')
|
|
841
|
+
|
|
842
|
+
echo ""
|
|
843
|
+
echo "Promoted $promoted findings to colony knowledge."
|
|
844
|
+
echo " - Instincts created in COLONY_STATE.json"
|
|
845
|
+
echo " - Learnings stored in learnings.json"
|
|
846
|
+
echo " - Observations tracked for wisdom promotion"
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
# Trap handler: synthesize before exit on SIGINT/SIGTERM
|
|
850
|
+
cleanup_and_synthesize() {
|
|
851
|
+
if [ "$INTERRUPTED" = true ]; then
|
|
852
|
+
exit 130
|
|
853
|
+
fi
|
|
854
|
+
INTERRUPTED=true
|
|
855
|
+
echo ""
|
|
856
|
+
echo "Oracle interrupted. Running synthesis pass..."
|
|
857
|
+
run_synthesis_pass "interrupted"
|
|
858
|
+
exit 130
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
# Check state.json exists (wizard must create it before launching oracle.sh)
|
|
862
|
+
if [ ! -f "$STATE_FILE" ]; then
|
|
863
|
+
echo "Error: No state.json found. Run /ant:oracle to configure research first."
|
|
864
|
+
exit 1
|
|
865
|
+
fi
|
|
866
|
+
|
|
867
|
+
# Read config from state.json (wizard writes these)
|
|
868
|
+
CURRENT_TOPIC=$(jq -r '.topic // empty' "$STATE_FILE" 2>/dev/null || echo "")
|
|
869
|
+
TARGET_CONFIDENCE=$(jq -r '.target_confidence // 95' "$STATE_FILE" 2>/dev/null || echo "95")
|
|
870
|
+
JSON_MAX_ITER=$(jq -r '.max_iterations // 50' "$STATE_FILE" 2>/dev/null || echo "50")
|
|
871
|
+
|
|
872
|
+
# Command-line arg overrides state.json
|
|
873
|
+
MAX_ITERATIONS=${1:-$JSON_MAX_ITER}
|
|
874
|
+
|
|
875
|
+
# Detect AI CLI (claude or opencode)
|
|
876
|
+
if command -v claude &>/dev/null; then
|
|
877
|
+
AI_CMD="claude --dangerously-skip-permissions --print"
|
|
878
|
+
elif command -v opencode &>/dev/null; then
|
|
879
|
+
AI_CMD="opencode --dangerously-skip-permissions --print"
|
|
880
|
+
else
|
|
881
|
+
echo "Error: Neither 'claude' nor 'opencode' CLI found on PATH."
|
|
882
|
+
exit 1
|
|
883
|
+
fi
|
|
884
|
+
|
|
885
|
+
# Archive previous run if topic changed
|
|
886
|
+
if [ -f "$STATE_FILE" ]; then
|
|
887
|
+
LAST_TOPIC=$(jq -r '.topic // empty' "$STATE_FILE" 2>/dev/null || echo "")
|
|
888
|
+
# If the wizard passed a new topic via environment, compare
|
|
889
|
+
if [ -n "${ORACLE_NEW_TOPIC:-}" ] && [ -n "$LAST_TOPIC" ] && [ "$ORACLE_NEW_TOPIC" != "$LAST_TOPIC" ]; then
|
|
890
|
+
ARCHIVE_FOLDER="$ARCHIVE_DIR/$(date +%Y-%m-%d-%H%M%S)"
|
|
891
|
+
|
|
892
|
+
echo "Archiving previous research: $LAST_TOPIC"
|
|
893
|
+
mkdir -p "$ARCHIVE_FOLDER"
|
|
894
|
+
for f in state.json plan.json gaps.md synthesis.md research-plan.md; do
|
|
895
|
+
[ -f "$ORACLE_STATE_DIR/$f" ] && cp "$ORACLE_STATE_DIR/$f" "$ARCHIVE_FOLDER/"
|
|
896
|
+
done
|
|
897
|
+
echo " Archived to: $ARCHIVE_FOLDER"
|
|
898
|
+
# Do NOT create empty files -- the wizard handles initial file creation
|
|
899
|
+
fi
|
|
900
|
+
fi
|
|
901
|
+
|
|
902
|
+
# Initialize discoveries directory
|
|
903
|
+
mkdir -p "$DISCOVERIES_DIR"
|
|
904
|
+
|
|
905
|
+
echo ""
|
|
906
|
+
echo "==============================================================="
|
|
907
|
+
echo " ORACLE ANT - Deep Research Loop"
|
|
908
|
+
echo "==============================================================="
|
|
909
|
+
echo "Topic: $CURRENT_TOPIC"
|
|
910
|
+
echo "Iterations: $MAX_ITERATIONS"
|
|
911
|
+
echo "Confidence: $TARGET_CONFIDENCE%"
|
|
912
|
+
echo "CLI: $AI_CMD"
|
|
913
|
+
echo ""
|
|
914
|
+
|
|
915
|
+
# Signal handling setup
|
|
916
|
+
INTERRUPTED=false
|
|
917
|
+
trap cleanup_and_synthesize SIGINT SIGTERM
|
|
918
|
+
|
|
919
|
+
# Main loop
|
|
920
|
+
for i in $(seq 1 "$MAX_ITERATIONS"); do
|
|
921
|
+
# Check for stop signal (cooperative stop)
|
|
922
|
+
if [ -f "$STOP_FILE" ]; then
|
|
923
|
+
rm -f "$STOP_FILE"
|
|
924
|
+
echo ""
|
|
925
|
+
echo "Oracle stopped by user at iteration $i"
|
|
926
|
+
run_synthesis_pass "stopped"
|
|
927
|
+
exit 0
|
|
928
|
+
fi
|
|
929
|
+
|
|
930
|
+
# Read steering signals from pheromones
|
|
931
|
+
STEERING_DIRECTIVE=$(read_steering_signals "$AETHER_ROOT")
|
|
932
|
+
|
|
933
|
+
# Build iteration header with optional steering info
|
|
934
|
+
SIGNAL_COUNT=$(echo "$STEERING_DIRECTIVE" | grep -c '^\- \[' 2>/dev/null || echo 0)
|
|
935
|
+
STRATEGY=$(jq -r '.strategy // "adaptive"' "$STATE_FILE" 2>/dev/null || echo "adaptive")
|
|
936
|
+
|
|
937
|
+
echo ""
|
|
938
|
+
echo "---------------------------------------------------------------"
|
|
939
|
+
echo " Iteration $i of $MAX_ITERATIONS"
|
|
940
|
+
if [ -n "$STEERING_DIRECTIVE" ] || [ "$STRATEGY" != "adaptive" ]; then
|
|
941
|
+
echo " Steering: $SIGNAL_COUNT signals active"
|
|
942
|
+
echo " Strategy: $STRATEGY"
|
|
943
|
+
fi
|
|
944
|
+
echo "---------------------------------------------------------------"
|
|
945
|
+
|
|
946
|
+
# Pre-iteration backup for recovery
|
|
947
|
+
cp "$STATE_FILE" "$STATE_FILE.pre-iteration"
|
|
948
|
+
cp "$PLAN_FILE" "$PLAN_FILE.pre-iteration"
|
|
949
|
+
|
|
950
|
+
# Run AI with phase-aware prompt (directive + steering + oracle.md)
|
|
951
|
+
OUTPUT=$(build_oracle_prompt "$STATE_FILE" "$SCRIPT_DIR/oracle.md" "$STEERING_DIRECTIVE" | $AI_CMD 2>&1 | tee /dev/stderr) || true
|
|
952
|
+
|
|
953
|
+
# Validate and recover from malformed JSON
|
|
954
|
+
if ! validate_and_recover "$STATE_FILE" || ! validate_and_recover "$PLAN_FILE"; then
|
|
955
|
+
run_synthesis_pass "corruption"
|
|
956
|
+
exit 1
|
|
957
|
+
fi
|
|
958
|
+
|
|
959
|
+
# Increment iteration counter
|
|
960
|
+
ITER_TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
961
|
+
jq --arg ts "$ITER_TS" '.iteration += 1 | .last_updated = $ts' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
962
|
+
|
|
963
|
+
# Check for phase transition
|
|
964
|
+
NEW_PHASE=$(determine_phase "$STATE_FILE" "$PLAN_FILE")
|
|
965
|
+
CURRENT_PHASE=$(jq -r '.phase' "$STATE_FILE")
|
|
966
|
+
if [ "$NEW_PHASE" != "$CURRENT_PHASE" ]; then
|
|
967
|
+
echo " Phase transition: $CURRENT_PHASE -> $NEW_PHASE"
|
|
968
|
+
jq --arg phase "$NEW_PHASE" '.phase = $phase' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
969
|
+
fi
|
|
970
|
+
|
|
971
|
+
# Update convergence metrics
|
|
972
|
+
update_convergence_metrics "$STATE_FILE" "$PLAN_FILE"
|
|
973
|
+
|
|
974
|
+
# Compute trust scores from source tracking data
|
|
975
|
+
compute_trust_scores "$PLAN_FILE"
|
|
976
|
+
|
|
977
|
+
# Check for diminishing returns
|
|
978
|
+
DR_RESULT=$(detect_diminishing_returns "$STATE_FILE")
|
|
979
|
+
case "$DR_RESULT" in
|
|
980
|
+
strategy_change)
|
|
981
|
+
echo " Diminishing returns detected. Advancing to synthesize phase."
|
|
982
|
+
jq '.phase = "synthesize"' "$STATE_FILE" > "$STATE_FILE.tmp" && mv "$STATE_FILE.tmp" "$STATE_FILE"
|
|
983
|
+
;;
|
|
984
|
+
synthesize_now)
|
|
985
|
+
echo " Research plateaued. Running synthesis."
|
|
986
|
+
run_synthesis_pass "converged"
|
|
987
|
+
exit 0
|
|
988
|
+
;;
|
|
989
|
+
esac
|
|
990
|
+
|
|
991
|
+
# Check for convergence
|
|
992
|
+
if check_convergence "$STATE_FILE"; then
|
|
993
|
+
echo " Research converged."
|
|
994
|
+
run_synthesis_pass "converged"
|
|
995
|
+
exit 0
|
|
996
|
+
fi
|
|
997
|
+
|
|
998
|
+
# Regenerate research-plan.md from current state
|
|
999
|
+
generate_research_plan
|
|
1000
|
+
|
|
1001
|
+
# Check for AI completion signal
|
|
1002
|
+
if echo "$OUTPUT" | grep -q "<oracle>COMPLETE</oracle>"; then
|
|
1003
|
+
echo ""
|
|
1004
|
+
echo "==============================================================="
|
|
1005
|
+
echo " ORACLE RESEARCH COMPLETE!"
|
|
1006
|
+
echo "==============================================================="
|
|
1007
|
+
echo "Completed at iteration $i"
|
|
1008
|
+
run_synthesis_pass "converged"
|
|
1009
|
+
exit 0
|
|
1010
|
+
fi
|
|
1011
|
+
|
|
1012
|
+
echo ""
|
|
1013
|
+
echo "Iteration $i complete. Continuing..."
|
|
1014
|
+
sleep 2
|
|
1015
|
+
done
|
|
1016
|
+
|
|
1017
|
+
echo ""
|
|
1018
|
+
echo "==============================================================="
|
|
1019
|
+
echo " ORACLE REACHED MAX ITERATIONS"
|
|
1020
|
+
echo "==============================================================="
|
|
1021
|
+
echo "Max iterations ($MAX_ITERATIONS) reached."
|
|
1022
|
+
run_synthesis_pass "max_iterations"
|
|
1023
|
+
exit 0
|