aether-colony 5.0.0 → 5.2.1
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 +3226 -3345
- 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 +442 -0
- package/.aether/commands/continue.yaml +1484 -0
- package/.aether/commands/council.yaml +509 -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 +502 -0
- package/.aether/commands/insert-phase.yaml +102 -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 +1364 -0
- package/.aether/commands/preferences.yaml +63 -0
- package/.aether/commands/quick.yaml +104 -0
- package/.aether/commands/redirect.yaml +123 -0
- package/.aether/commands/resume-colony.yaml +375 -0
- package/.aether/commands/resume.yaml +407 -0
- package/.aether/commands/run.yaml +229 -0
- package/.aether/commands/seal.yaml +1214 -0
- package/.aether/commands/skill-create.yaml +337 -0
- package/.aether/commands/status.yaml +408 -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 +1683 -0
- package/.aether/docs/command-playbooks/build-prep.md +284 -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 +1725 -0
- package/.aether/docs/command-playbooks/continue-gates.md +686 -0
- package/.aether/docs/command-playbooks/continue-verify.md +407 -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/council.sh +425 -0
- 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 +278 -0
- package/.aether/utils/hive.sh +572 -0
- package/.aether/utils/immune.sh +508 -0
- package/.aether/utils/learning.sh +1928 -0
- package/.aether/utils/midden.sh +520 -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 +1710 -0
- package/.aether/utils/scan.sh +860 -0
- package/.aether/utils/semantic-cli.sh +10 -8
- package/.aether/utils/session.sh +816 -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 +389 -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 +3 -14
- package/.claude/commands/ant/continue.md +40 -1026
- package/.claude/commands/ant/council.md +213 -15
- 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 +349 -191
- package/.claude/commands/ant/insert-phase.md +105 -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 +199 -50
- package/.claude/commands/ant/preferences.md +65 -0
- package/.claude/commands/ant/quick.md +100 -0
- package/.claude/commands/ant/redirect.md +18 -0
- package/.claude/commands/ant/resume-colony.md +37 -22
- package/.claude/commands/ant/resume.md +60 -7
- package/.claude/commands/ant/run.md +231 -0
- package/.claude/commands/ant/seal.md +506 -78
- package/.claude/commands/ant/skill-create.md +286 -0
- package/.claude/commands/ant/status.md +171 -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 +10 -17
- package/.opencode/commands/ant/continue.md +595 -66
- package/.opencode/commands/ant/council.md +150 -18
- 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 +396 -154
- package/.opencode/commands/ant/insert-phase.md +111 -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 +210 -57
- package/.opencode/commands/ant/preferences.md +71 -0
- package/.opencode/commands/ant/quick.md +91 -0
- package/.opencode/commands/ant/redirect.md +22 -5
- package/.opencode/commands/ant/resume-colony.md +41 -29
- package/.opencode/commands/ant/resume.md +80 -20
- package/.opencode/commands/ant/run.md +237 -0
- package/.opencode/commands/ant/seal.md +230 -25
- package/.opencode/commands/ant/skill-create.md +63 -0
- package/.opencode/commands/ant/status.md +125 -30
- 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 +368 -1
- package/README.md +195 -324
- 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 +16 -4
- 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,520 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Midden (failure tracking) utility functions — extracted from aether-utils.sh
|
|
3
|
+
# Provides: _midden_write, _midden_recent_failures, _midden_review, _midden_acknowledge
|
|
4
|
+
#
|
|
5
|
+
# These functions are sourced by aether-utils.sh at startup.
|
|
6
|
+
# All shared infrastructure (json_ok, json_err, atomic_write, acquire_lock,
|
|
7
|
+
# release_lock, LOCK_DIR, DATA_DIR, SCRIPT_DIR, error constants) is available.
|
|
8
|
+
|
|
9
|
+
_midden_try_write() {
|
|
10
|
+
# Helper: write updated JSON to midden file with retry
|
|
11
|
+
# Usage: _midden_try_write <updated_json> <midden_file>
|
|
12
|
+
# Returns: 0 on success, 1 on failure
|
|
13
|
+
local mtw_json="$1"
|
|
14
|
+
local mtw_file="$2"
|
|
15
|
+
local mtw_tmp="${mtw_file}.tmp.$$"
|
|
16
|
+
|
|
17
|
+
if ! { printf '%s\n' "$mtw_json" > "$mtw_tmp" && mv "$mtw_tmp" "$mtw_file"; }; then
|
|
18
|
+
# Silent retry (once)
|
|
19
|
+
if ! { printf '%s\n' "$mtw_json" > "$mtw_tmp" && mv "$mtw_tmp" "$mtw_file"; }; then
|
|
20
|
+
echo "Warning: Midden write failed after retry -- entry may not have been saved." >&2
|
|
21
|
+
return 1
|
|
22
|
+
fi
|
|
23
|
+
fi
|
|
24
|
+
return 0
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
_midden_write() {
|
|
28
|
+
# Write a warning/observation to the midden for later review
|
|
29
|
+
# Usage: midden-write <category> <message> <source>
|
|
30
|
+
# Example: midden-write "security" "High CVEs found: 3" "gatekeeper"
|
|
31
|
+
# Returns: JSON with success status and entry details
|
|
32
|
+
|
|
33
|
+
mw_category="${1:-general}"
|
|
34
|
+
mw_message="${2:-}"
|
|
35
|
+
mw_source="${3:-unknown}"
|
|
36
|
+
|
|
37
|
+
# Graceful degradation: if no message, return success but note it
|
|
38
|
+
if [[ -z "$mw_message" ]]; then
|
|
39
|
+
json_ok "{\"success\":true,\"warning\":\"no_message_provided\",\"entry_id\":null}"
|
|
40
|
+
return 0
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
mw_midden_dir="$COLONY_DATA_DIR/midden"
|
|
44
|
+
mw_midden_file="$mw_midden_dir/midden.json"
|
|
45
|
+
mw_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
46
|
+
mw_entry_id="midden_$(date +%s)_$$"
|
|
47
|
+
|
|
48
|
+
# Create midden directory if it doesn't exist
|
|
49
|
+
mkdir -p "$mw_midden_dir"
|
|
50
|
+
|
|
51
|
+
# Initialize midden.json if it doesn't exist
|
|
52
|
+
if [[ ! -f "$mw_midden_file" ]]; then
|
|
53
|
+
printf '%s\n' '{"version":"1.0.0","entries":[]}' > "$mw_midden_file"
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
# Create the new entry using jq for safe JSON construction
|
|
57
|
+
mw_new_entry=$(jq -n \
|
|
58
|
+
--arg id "$mw_entry_id" \
|
|
59
|
+
--arg ts "$mw_timestamp" \
|
|
60
|
+
--arg cat "$mw_category" \
|
|
61
|
+
--arg src "$mw_source" \
|
|
62
|
+
--arg msg "$mw_message" \
|
|
63
|
+
'{id: $id, timestamp: $ts, category: $cat, source: $src, message: $msg, reviewed: false}')
|
|
64
|
+
|
|
65
|
+
# Append to midden.json using jq with locking
|
|
66
|
+
if acquire_lock "$mw_midden_file" 2>/dev/null; then
|
|
67
|
+
mw_updated_midden=$(jq --argjson entry "$mw_new_entry" '
|
|
68
|
+
.entries += [$entry] |
|
|
69
|
+
.entry_count = (.entries | length)
|
|
70
|
+
' "$mw_midden_file" 2>/dev/null)
|
|
71
|
+
|
|
72
|
+
if [[ -n "$mw_updated_midden" ]]; then
|
|
73
|
+
_midden_try_write "$mw_updated_midden" "$mw_midden_file"
|
|
74
|
+
release_lock 2>/dev/null || true
|
|
75
|
+
mw_total=$(jq '.entries | length' "$mw_midden_file" 2>/dev/null || echo 0)
|
|
76
|
+
json_ok "$(jq -n --arg entry_id "$mw_entry_id" --arg category "$mw_category" --argjson midden_total "$mw_total" \
|
|
77
|
+
'{success: true, entry_id: $entry_id, category: $category, midden_total: $midden_total}')"
|
|
78
|
+
else
|
|
79
|
+
release_lock 2>/dev/null || true
|
|
80
|
+
json_ok "{\"success\":true,\"warning\":\"jq_processing_failed\",\"entry_id\":null}"
|
|
81
|
+
fi
|
|
82
|
+
else
|
|
83
|
+
# Lock failed — graceful degradation, try without lock
|
|
84
|
+
echo "Warning: Midden write completed without lock -- if another write happened at the same time, one entry may be missing." >&2
|
|
85
|
+
mw_updated_midden=$(jq --argjson entry "$mw_new_entry" '
|
|
86
|
+
.entries += [$entry] |
|
|
87
|
+
.entry_count = (.entries | length)
|
|
88
|
+
' "$mw_midden_file" 2>/dev/null)
|
|
89
|
+
|
|
90
|
+
if [[ -n "$mw_updated_midden" ]]; then
|
|
91
|
+
_midden_try_write "$mw_updated_midden" "$mw_midden_file"
|
|
92
|
+
json_ok "$(jq -n --arg entry_id "$mw_entry_id" --arg category "$mw_category" \
|
|
93
|
+
'{success: true, entry_id: $entry_id, category: $category, warning: "lock_unavailable"}')"
|
|
94
|
+
else
|
|
95
|
+
json_ok "{\"success\":true,\"warning\":\"jq_processing_failed\",\"entry_id\":null}"
|
|
96
|
+
fi
|
|
97
|
+
fi
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
_midden_recent_failures() {
|
|
101
|
+
# Extract recent failure entries from midden.json
|
|
102
|
+
# Usage: midden-recent-failures [limit]
|
|
103
|
+
# Returns: JSON with count and failures array
|
|
104
|
+
|
|
105
|
+
limit="${1:-5}"
|
|
106
|
+
midden_file="$COLONY_DATA_DIR/midden/midden.json"
|
|
107
|
+
|
|
108
|
+
if [[ ! -f "$midden_file" ]]; then
|
|
109
|
+
echo '{"count":0,"failures":[]}'
|
|
110
|
+
return 0
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# Extract failures from .entries[], sort by timestamp descending, limit results
|
|
114
|
+
result=$(jq --argjson limit "$limit" '{
|
|
115
|
+
"count": ([.entries[]?] | length),
|
|
116
|
+
"failures": ([.entries[]?] | sort_by(.timestamp) | reverse | .[:$limit] | [.[] | {timestamp, category, source, message}])
|
|
117
|
+
}' "$midden_file" 2>/dev/null)
|
|
118
|
+
|
|
119
|
+
if [[ -z "$result" ]]; then
|
|
120
|
+
echo '{"count":0,"failures":[]}'
|
|
121
|
+
else
|
|
122
|
+
echo "$result"
|
|
123
|
+
fi
|
|
124
|
+
return 0
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
_midden_review() {
|
|
128
|
+
# Review unacknowledged midden entries grouped by category
|
|
129
|
+
# Usage: midden-review [--category <cat>] [--limit N] [--include-acknowledged]
|
|
130
|
+
# Returns: JSON with unacknowledged_count, categories summary, and entries array
|
|
131
|
+
|
|
132
|
+
mr_category=""
|
|
133
|
+
mr_limit=20
|
|
134
|
+
mr_include_ack=false
|
|
135
|
+
|
|
136
|
+
while [[ $# -gt 0 ]]; do
|
|
137
|
+
case "$1" in
|
|
138
|
+
--category) mr_category="${2:-}"; shift 2 ;;
|
|
139
|
+
--limit) mr_limit="${2:-20}"; shift 2 ;;
|
|
140
|
+
--include-acknowledged) mr_include_ack=true; shift ;;
|
|
141
|
+
*) shift ;;
|
|
142
|
+
esac
|
|
143
|
+
done
|
|
144
|
+
|
|
145
|
+
mr_midden_file="$COLONY_DATA_DIR/midden/midden.json"
|
|
146
|
+
|
|
147
|
+
if [[ ! -f "$mr_midden_file" ]]; then
|
|
148
|
+
json_ok '{"unacknowledged_count":0,"categories":{},"entries":[]}'
|
|
149
|
+
return 0
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
# Build jq filter based on options
|
|
153
|
+
mr_result=$(jq \
|
|
154
|
+
--arg category "$mr_category" \
|
|
155
|
+
--argjson limit "$mr_limit" \
|
|
156
|
+
--argjson include_ack "$mr_include_ack" \
|
|
157
|
+
'
|
|
158
|
+
# Start with all entries
|
|
159
|
+
[.entries // [] | .[] |
|
|
160
|
+
# Filter acknowledged unless --include-acknowledged
|
|
161
|
+
if $include_ack then . else select(.acknowledged != true) end |
|
|
162
|
+
# Filter by category if specified
|
|
163
|
+
if ($category | length) > 0 then select(.category == $category) else . end
|
|
164
|
+
] |
|
|
165
|
+
# Sort by timestamp descending
|
|
166
|
+
sort_by(.timestamp) | reverse |
|
|
167
|
+
# Compute categories before limiting
|
|
168
|
+
. as $all |
|
|
169
|
+
# Apply limit
|
|
170
|
+
($all | .[:$limit]) as $limited |
|
|
171
|
+
# Group $all by category for counts
|
|
172
|
+
($all | group_by(.category) | map({key: .[0].category, value: length}) | from_entries) as $cats |
|
|
173
|
+
{
|
|
174
|
+
unacknowledged_count: ($all | length),
|
|
175
|
+
categories: $cats,
|
|
176
|
+
entries: $limited
|
|
177
|
+
}
|
|
178
|
+
' "$mr_midden_file" 2>/dev/null)
|
|
179
|
+
|
|
180
|
+
if [[ -z "$mr_result" ]]; then
|
|
181
|
+
json_ok '{"unacknowledged_count":0,"categories":{},"entries":[]}'
|
|
182
|
+
else
|
|
183
|
+
json_ok "$mr_result"
|
|
184
|
+
fi
|
|
185
|
+
return 0
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
_midden_ingest_errors() {
|
|
189
|
+
# Ingest entries from errors.log into midden
|
|
190
|
+
# Usage: midden-ingest-errors [--dry-run]
|
|
191
|
+
# Returns: JSON with count of ingested entries
|
|
192
|
+
# After ingestion, moves errors.log to errors.log.ingested
|
|
193
|
+
|
|
194
|
+
mie_dry_run=false
|
|
195
|
+
while [[ $# -gt 0 ]]; do
|
|
196
|
+
case "$1" in
|
|
197
|
+
--dry-run) mie_dry_run=true; shift ;;
|
|
198
|
+
*) shift ;;
|
|
199
|
+
esac
|
|
200
|
+
done
|
|
201
|
+
|
|
202
|
+
mie_errors_file="$COLONY_DATA_DIR/errors.log"
|
|
203
|
+
|
|
204
|
+
# No errors.log → nothing to ingest
|
|
205
|
+
if [[ ! -f "$mie_errors_file" ]]; then
|
|
206
|
+
json_ok '{"ingested":0}'
|
|
207
|
+
return 0
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
# Empty file → nothing to ingest
|
|
211
|
+
if [[ ! -s "$mie_errors_file" ]]; then
|
|
212
|
+
json_ok '{"ingested":0}'
|
|
213
|
+
return 0
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
mie_count=0
|
|
217
|
+
|
|
218
|
+
# Read line by line (avoid pipe-to-while subshell)
|
|
219
|
+
while IFS= read -r mie_line; do
|
|
220
|
+
# Skip blank lines
|
|
221
|
+
[[ -z "$mie_line" ]] && continue
|
|
222
|
+
|
|
223
|
+
# Parse timestamp from [YYYY-...Z] prefix
|
|
224
|
+
mie_timestamp=""
|
|
225
|
+
mie_message="$mie_line"
|
|
226
|
+
if [[ "$mie_line" =~ ^\[([^\]]+)\]\ (.*) ]]; then
|
|
227
|
+
mie_timestamp="${BASH_REMATCH[1]}"
|
|
228
|
+
mie_message="${BASH_REMATCH[2]}"
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
mie_count=$((mie_count + 1))
|
|
232
|
+
|
|
233
|
+
if [[ "$mie_dry_run" == "false" ]]; then
|
|
234
|
+
_midden_write "error_log" "$mie_message" "error-handler" >/dev/null 2>&1 || true
|
|
235
|
+
fi
|
|
236
|
+
done < "$mie_errors_file"
|
|
237
|
+
|
|
238
|
+
# Move the file (not dry-run only)
|
|
239
|
+
if [[ "$mie_dry_run" == "false" && "$mie_count" -gt 0 ]]; then
|
|
240
|
+
mv "$mie_errors_file" "${mie_errors_file}.ingested"
|
|
241
|
+
fi
|
|
242
|
+
|
|
243
|
+
json_ok "{\"ingested\":$mie_count}"
|
|
244
|
+
return 0
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
_midden_search() {
|
|
248
|
+
# Search midden entries by keyword match in message field
|
|
249
|
+
# Usage: midden-search <query> [--category <cat>] [--source <src>] [--limit N] [--include-acknowledged]
|
|
250
|
+
# Returns: JSON with query, match_count, and entries array
|
|
251
|
+
|
|
252
|
+
ms_query=""
|
|
253
|
+
ms_category=""
|
|
254
|
+
ms_source=""
|
|
255
|
+
ms_limit=10
|
|
256
|
+
ms_include_ack=false
|
|
257
|
+
|
|
258
|
+
# First positional arg is the query
|
|
259
|
+
if [[ $# -gt 0 && "$1" != --* ]]; then
|
|
260
|
+
ms_query="$1"
|
|
261
|
+
shift
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
while [[ $# -gt 0 ]]; do
|
|
265
|
+
case "$1" in
|
|
266
|
+
--category) ms_category="${2:-}"; shift 2 ;;
|
|
267
|
+
--source) ms_source="${2:-}"; shift 2 ;;
|
|
268
|
+
--limit) ms_limit="${2:-10}"; shift 2 ;;
|
|
269
|
+
--include-acknowledged) ms_include_ack=true; shift ;;
|
|
270
|
+
*) shift ;;
|
|
271
|
+
esac
|
|
272
|
+
done
|
|
273
|
+
|
|
274
|
+
ms_midden_file="$COLONY_DATA_DIR/midden/midden.json"
|
|
275
|
+
|
|
276
|
+
if [[ ! -f "$ms_midden_file" ]]; then
|
|
277
|
+
json_ok "{\"query\":$(printf '%s' "$ms_query" | jq -Rs .),\"match_count\":0,\"entries\":[]}"
|
|
278
|
+
return 0
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
ms_result=$(jq \
|
|
282
|
+
--arg query "$ms_query" \
|
|
283
|
+
--arg category "$ms_category" \
|
|
284
|
+
--arg source "$ms_source" \
|
|
285
|
+
--argjson limit "$ms_limit" \
|
|
286
|
+
--argjson include_ack "$ms_include_ack" \
|
|
287
|
+
'
|
|
288
|
+
[.entries // [] | .[] |
|
|
289
|
+
# Filter acknowledged unless --include-acknowledged
|
|
290
|
+
if $include_ack then . else select(.acknowledged != true) end |
|
|
291
|
+
# Filter by category if specified
|
|
292
|
+
if ($category | length) > 0 then select(.category == $category) else . end |
|
|
293
|
+
# Filter by source if specified
|
|
294
|
+
if ($source | length) > 0 then select(.source == $source) else . end |
|
|
295
|
+
# Filter by keyword match in message (case-insensitive)
|
|
296
|
+
if ($query | length) > 0 then
|
|
297
|
+
select(.message | ascii_downcase | contains($query | ascii_downcase))
|
|
298
|
+
else
|
|
299
|
+
.
|
|
300
|
+
end
|
|
301
|
+
] |
|
|
302
|
+
sort_by(.timestamp) | reverse |
|
|
303
|
+
. as $all |
|
|
304
|
+
{
|
|
305
|
+
query: $query,
|
|
306
|
+
match_count: ($all | length),
|
|
307
|
+
entries: ($all | .[:$limit])
|
|
308
|
+
}
|
|
309
|
+
' "$ms_midden_file" 2>/dev/null)
|
|
310
|
+
|
|
311
|
+
if [[ -z "$ms_result" ]]; then
|
|
312
|
+
json_ok "{\"query\":$(printf '%s' "$ms_query" | jq -Rs .),\"match_count\":0,\"entries\":[]}"
|
|
313
|
+
else
|
|
314
|
+
json_ok "$ms_result"
|
|
315
|
+
fi
|
|
316
|
+
return 0
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
_midden_tag() {
|
|
320
|
+
# Add or remove a tag from a midden entry's tags array
|
|
321
|
+
# Usage: midden-tag --id <entry_id> --tag <tag_name>
|
|
322
|
+
# OR: midden-tag --id <entry_id> --untag <tag_name>
|
|
323
|
+
# Returns: JSON with entry_id, tags array, and action
|
|
324
|
+
|
|
325
|
+
mt_id=""
|
|
326
|
+
mt_tag=""
|
|
327
|
+
mt_untag=""
|
|
328
|
+
|
|
329
|
+
while [[ $# -gt 0 ]]; do
|
|
330
|
+
case "$1" in
|
|
331
|
+
--id) mt_id="${2:-}"; shift 2 ;;
|
|
332
|
+
--tag) mt_tag="${2:-}"; shift 2 ;;
|
|
333
|
+
--untag) mt_untag="${2:-}"; shift 2 ;;
|
|
334
|
+
*) shift ;;
|
|
335
|
+
esac
|
|
336
|
+
done
|
|
337
|
+
|
|
338
|
+
# Validate: need --id
|
|
339
|
+
if [[ -z "$mt_id" ]]; then
|
|
340
|
+
json_err "$E_VALIDATION_FAILED" "midden-tag requires --id"
|
|
341
|
+
fi
|
|
342
|
+
|
|
343
|
+
# Validate: need --tag or --untag (but not both)
|
|
344
|
+
if [[ -z "$mt_tag" && -z "$mt_untag" ]]; then
|
|
345
|
+
json_err "$E_VALIDATION_FAILED" "midden-tag requires --tag or --untag"
|
|
346
|
+
fi
|
|
347
|
+
|
|
348
|
+
if [[ -n "$mt_tag" && -n "$mt_untag" ]]; then
|
|
349
|
+
json_err "$E_VALIDATION_FAILED" "midden-tag requires --tag or --untag, not both"
|
|
350
|
+
fi
|
|
351
|
+
|
|
352
|
+
mt_midden_file="$COLONY_DATA_DIR/midden/midden.json"
|
|
353
|
+
|
|
354
|
+
if [[ ! -f "$mt_midden_file" ]]; then
|
|
355
|
+
json_err "$E_FILE_NOT_FOUND" "midden.json not found"
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
# Check entry exists
|
|
359
|
+
mt_exists=$(jq --arg id "$mt_id" '[.entries[]? | select(.id == $id)] | length > 0' "$mt_midden_file" 2>/dev/null || echo "false")
|
|
360
|
+
if [[ "$mt_exists" != "true" ]]; then
|
|
361
|
+
json_err "$E_RESOURCE_NOT_FOUND" "Midden entry '$mt_id' not found"
|
|
362
|
+
fi
|
|
363
|
+
|
|
364
|
+
# Acquire lock with trap-based cleanup
|
|
365
|
+
acquire_lock "$mt_midden_file" || {
|
|
366
|
+
json_err "$E_LOCK_FAILED" "Failed to acquire lock on midden.json"
|
|
367
|
+
}
|
|
368
|
+
trap 'release_lock 2>/dev/null || true' EXIT
|
|
369
|
+
|
|
370
|
+
if [[ -n "$mt_tag" ]]; then
|
|
371
|
+
# Add tag — create tags array if absent, append if tag not already present
|
|
372
|
+
mt_updated=$(jq \
|
|
373
|
+
--arg id "$mt_id" \
|
|
374
|
+
--arg tag "$mt_tag" \
|
|
375
|
+
'
|
|
376
|
+
.entries = [.entries[] |
|
|
377
|
+
if .id == $id then
|
|
378
|
+
. + {tags: ((.tags // []) | if contains([$tag]) then . else . + [$tag] end)}
|
|
379
|
+
else
|
|
380
|
+
.
|
|
381
|
+
end
|
|
382
|
+
]
|
|
383
|
+
' "$mt_midden_file" 2>/dev/null)
|
|
384
|
+
mt_action="added"
|
|
385
|
+
else
|
|
386
|
+
# Remove tag — remove from tags array if present
|
|
387
|
+
mt_updated=$(jq \
|
|
388
|
+
--arg id "$mt_id" \
|
|
389
|
+
--arg tag "$mt_untag" \
|
|
390
|
+
'
|
|
391
|
+
.entries = [.entries[] |
|
|
392
|
+
if .id == $id then
|
|
393
|
+
. + {tags: ((.tags // []) | map(select(. != $tag)))}
|
|
394
|
+
else
|
|
395
|
+
.
|
|
396
|
+
end
|
|
397
|
+
]
|
|
398
|
+
' "$mt_midden_file" 2>/dev/null)
|
|
399
|
+
mt_action="removed"
|
|
400
|
+
mt_tag="$mt_untag"
|
|
401
|
+
fi
|
|
402
|
+
|
|
403
|
+
if [[ -z "$mt_updated" ]]; then
|
|
404
|
+
trap - EXIT
|
|
405
|
+
release_lock 2>/dev/null || true
|
|
406
|
+
json_err "$E_INTERNAL" "Failed to update midden.json"
|
|
407
|
+
fi
|
|
408
|
+
|
|
409
|
+
atomic_write "$mt_midden_file" "$mt_updated"
|
|
410
|
+
|
|
411
|
+
trap - EXIT
|
|
412
|
+
release_lock 2>/dev/null || true
|
|
413
|
+
|
|
414
|
+
# Read back the updated tags for the entry
|
|
415
|
+
mt_tags=$(jq --arg id "$mt_id" '[.entries[]? | select(.id == $id) | .tags // []] | .[0] // []' "$mt_midden_file" 2>/dev/null || echo "[]")
|
|
416
|
+
|
|
417
|
+
json_ok "$(jq -n \
|
|
418
|
+
--arg entry_id "$mt_id" \
|
|
419
|
+
--argjson tags "$mt_tags" \
|
|
420
|
+
--arg action "$mt_action" \
|
|
421
|
+
'{entry_id: $entry_id, tags: $tags, action: $action}')"
|
|
422
|
+
return 0
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
_midden_acknowledge() {
|
|
426
|
+
# Acknowledge midden entries by id or by category
|
|
427
|
+
# Usage: midden-acknowledge --id <entry_id> [--reason <reason>]
|
|
428
|
+
# OR: midden-acknowledge --category <cat> --reason <reason>
|
|
429
|
+
# Returns: JSON with acknowledged=true, count, and reason
|
|
430
|
+
|
|
431
|
+
ma_id=""
|
|
432
|
+
ma_category=""
|
|
433
|
+
ma_reason=""
|
|
434
|
+
|
|
435
|
+
while [[ $# -gt 0 ]]; do
|
|
436
|
+
case "$1" in
|
|
437
|
+
--id) ma_id="${2:-}"; shift 2 ;;
|
|
438
|
+
--category) ma_category="${2:-}"; shift 2 ;;
|
|
439
|
+
--reason) ma_reason="${2:-}"; shift 2 ;;
|
|
440
|
+
*) shift ;;
|
|
441
|
+
esac
|
|
442
|
+
done
|
|
443
|
+
|
|
444
|
+
# Validate: need either --id or --category
|
|
445
|
+
if [[ -z "$ma_id" && -z "$ma_category" ]]; then
|
|
446
|
+
json_err "$E_VALIDATION_FAILED" "midden-acknowledge requires --id or --category"
|
|
447
|
+
fi
|
|
448
|
+
|
|
449
|
+
ma_midden_file="$COLONY_DATA_DIR/midden/midden.json"
|
|
450
|
+
|
|
451
|
+
if [[ ! -f "$ma_midden_file" ]]; then
|
|
452
|
+
json_err "$E_FILE_NOT_FOUND" "midden.json not found"
|
|
453
|
+
fi
|
|
454
|
+
|
|
455
|
+
ma_now=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
456
|
+
|
|
457
|
+
# Acquire lock with trap-based cleanup
|
|
458
|
+
acquire_lock "$ma_midden_file" || {
|
|
459
|
+
json_err "$E_LOCK_FAILED" "Failed to acquire lock on midden.json"
|
|
460
|
+
}
|
|
461
|
+
trap 'release_lock 2>/dev/null || true' EXIT
|
|
462
|
+
|
|
463
|
+
if [[ -n "$ma_id" ]]; then
|
|
464
|
+
# Acknowledge single entry by id
|
|
465
|
+
ma_exists=$(jq --arg id "$ma_id" '[.entries[]? | select(.id == $id)] | length > 0' "$ma_midden_file" 2>/dev/null || echo "false")
|
|
466
|
+
if [[ "$ma_exists" != "true" ]]; then
|
|
467
|
+
trap - EXIT
|
|
468
|
+
release_lock 2>/dev/null || true
|
|
469
|
+
json_err "$E_RESOURCE_NOT_FOUND" "Midden entry '$ma_id' not found"
|
|
470
|
+
fi
|
|
471
|
+
|
|
472
|
+
ma_updated=$(jq \
|
|
473
|
+
--arg id "$ma_id" \
|
|
474
|
+
--arg now "$ma_now" \
|
|
475
|
+
--arg reason "$ma_reason" \
|
|
476
|
+
'
|
|
477
|
+
.entries = [.entries[] |
|
|
478
|
+
if .id == $id then
|
|
479
|
+
. + {acknowledged: true, acknowledged_at: $now, acknowledge_reason: $reason}
|
|
480
|
+
else
|
|
481
|
+
.
|
|
482
|
+
end
|
|
483
|
+
]
|
|
484
|
+
' "$ma_midden_file" 2>/dev/null)
|
|
485
|
+
|
|
486
|
+
ma_count=1
|
|
487
|
+
else
|
|
488
|
+
# Acknowledge all entries matching category
|
|
489
|
+
ma_count=$(jq --arg cat "$ma_category" '[.entries[]? | select(.category == $cat and .acknowledged != true)] | length' "$ma_midden_file" 2>/dev/null || echo "0")
|
|
490
|
+
|
|
491
|
+
ma_updated=$(jq \
|
|
492
|
+
--arg cat "$ma_category" \
|
|
493
|
+
--arg now "$ma_now" \
|
|
494
|
+
--arg reason "$ma_reason" \
|
|
495
|
+
'
|
|
496
|
+
.entries = [.entries[] |
|
|
497
|
+
if .category == $cat and .acknowledged != true then
|
|
498
|
+
. + {acknowledged: true, acknowledged_at: $now, acknowledge_reason: $reason}
|
|
499
|
+
else
|
|
500
|
+
.
|
|
501
|
+
end
|
|
502
|
+
]
|
|
503
|
+
' "$ma_midden_file" 2>/dev/null)
|
|
504
|
+
fi
|
|
505
|
+
|
|
506
|
+
if [[ -z "$ma_updated" ]]; then
|
|
507
|
+
trap - EXIT
|
|
508
|
+
release_lock 2>/dev/null || true
|
|
509
|
+
json_err "$E_INTERNAL" "Failed to update midden.json"
|
|
510
|
+
fi
|
|
511
|
+
|
|
512
|
+
atomic_write "$ma_midden_file" "$ma_updated"
|
|
513
|
+
|
|
514
|
+
trap - EXIT
|
|
515
|
+
release_lock 2>/dev/null || true
|
|
516
|
+
|
|
517
|
+
json_ok "$(jq -n --argjson count "$ma_count" --arg reason "$ma_reason" \
|
|
518
|
+
'{acknowledged: true, count: $count, reason: $reason}')"
|
|
519
|
+
return 0
|
|
520
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
You are an **Oracle Ant** -- a deep research agent in the Aether Colony.
|
|
2
|
+
|
|
3
|
+
## Mission
|
|
4
|
+
|
|
5
|
+
Research a topic thoroughly. Each iteration targets knowledge gaps and deepens
|
|
6
|
+
understanding. You are working through a structured research plan with tracked
|
|
7
|
+
sub-questions, confidence scores, and accumulated findings.
|
|
8
|
+
|
|
9
|
+
## Phase Directive
|
|
10
|
+
|
|
11
|
+
Your current research phase and specific instructions were provided above this
|
|
12
|
+
prompt. Follow them. The phase determines your strategy for this iteration.
|
|
13
|
+
|
|
14
|
+
## Steering Signals
|
|
15
|
+
|
|
16
|
+
If steering signals appear above this prompt, they were emitted by the user:
|
|
17
|
+
|
|
18
|
+
- **REDIRECT** signals are HARD CONSTRAINTS. You MUST follow them. If a REDIRECT
|
|
19
|
+
conflicts with your planned approach, change your approach.
|
|
20
|
+
- **FOCUS** signals indicate priority areas. When choosing your target question,
|
|
21
|
+
prefer questions related to focus areas. If no questions match, fall back to
|
|
22
|
+
default targeting (lowest-confidence).
|
|
23
|
+
- **FEEDBACK** signals are gentle adjustments. Incorporate them where appropriate
|
|
24
|
+
into your research approach and output style.
|
|
25
|
+
|
|
26
|
+
If no steering signals appear, follow default targeting as described in Instructions.
|
|
27
|
+
|
|
28
|
+
## Instructions
|
|
29
|
+
|
|
30
|
+
### Step 1: Read State Files
|
|
31
|
+
|
|
32
|
+
Read these files to understand the current research state:
|
|
33
|
+
|
|
34
|
+
- `.aether/oracle/state.json` -- Session metadata (topic, scope, iteration, phase, confidence)
|
|
35
|
+
- `.aether/oracle/plan.json` -- Sub-questions with status, confidence, and iteration history
|
|
36
|
+
- `.aether/oracle/gaps.md` -- Current knowledge gaps and contradictions
|
|
37
|
+
- `.aether/oracle/synthesis.md` -- Accumulated findings organized by question
|
|
38
|
+
|
|
39
|
+
Note the current `iteration` and `phase` from state.json. Your phase directive
|
|
40
|
+
above tells you how to behave this iteration.
|
|
41
|
+
|
|
42
|
+
### Step 2: Identify Target
|
|
43
|
+
|
|
44
|
+
Select your target question based on the current phase:
|
|
45
|
+
|
|
46
|
+
- **Survey phase:** Target questions with empty `iterations_touched` arrays first.
|
|
47
|
+
If all questions have been touched, target the lowest-confidence non-answered question.
|
|
48
|
+
- **Investigate / Synthesize / Verify phases:** Target the lowest-confidence
|
|
49
|
+
non-answered question.
|
|
50
|
+
|
|
51
|
+
If all questions are "answered", proceed to Step 5.
|
|
52
|
+
|
|
53
|
+
### Step 3: Research
|
|
54
|
+
|
|
55
|
+
**Before writing ANY finding:** READ existing findings for your target question
|
|
56
|
+
in synthesis.md. Know what has already been discovered.
|
|
57
|
+
|
|
58
|
+
**Your new findings MUST contain information NOT already in synthesis.md.** If you
|
|
59
|
+
cannot find new information beyond what exists, write "No new findings beyond
|
|
60
|
+
existing research" and target the next-lowest-confidence question instead.
|
|
61
|
+
|
|
62
|
+
Acceptable new information includes:
|
|
63
|
+
- Specific details not yet captured (numbers, dates, names)
|
|
64
|
+
- Concrete examples or case studies
|
|
65
|
+
- Source citations (URLs, documentation references, code paths)
|
|
66
|
+
- Edge cases and limitations
|
|
67
|
+
- Contradictions with existing findings
|
|
68
|
+
|
|
69
|
+
Use available tools:
|
|
70
|
+
- **Codebase:** Glob, Grep, Read for local files and source code
|
|
71
|
+
- **Web:** WebSearch, WebFetch for external sources and documentation
|
|
72
|
+
|
|
73
|
+
**Source Tracking (MANDATORY):**
|
|
74
|
+
For every new finding, you MUST record:
|
|
75
|
+
- The source URL (or file path for codebase research)
|
|
76
|
+
- The source title/description
|
|
77
|
+
- The date you accessed it
|
|
78
|
+
|
|
79
|
+
Register sources in plan.json under the `sources` object using sequential IDs
|
|
80
|
+
(S1, S2, S3...). Reuse existing source IDs if citing the same URL again.
|
|
81
|
+
|
|
82
|
+
Source types: "documentation", "blog", "github", "academic", "codebase", "forum", "official"
|
|
83
|
+
|
|
84
|
+
For codebase research, use file paths as URLs with type "codebase":
|
|
85
|
+
```json
|
|
86
|
+
"url": "src/components/Button.tsx",
|
|
87
|
+
"title": "Button component source",
|
|
88
|
+
"type": "codebase"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Step 4: Update State Files
|
|
92
|
+
|
|
93
|
+
After researching, update these files:
|
|
94
|
+
|
|
95
|
+
**plan.json:** Update the target question:
|
|
96
|
+
- Set `status` to "partial" (useful info but gaps remain) or "answered" (thoroughly addressed)
|
|
97
|
+
- Update `confidence` (0-100) based on evidence quality -- see Confidence Scoring Rubric below
|
|
98
|
+
- Add findings as OBJECTS (not strings): `{"text": "finding text", "source_ids": ["S1", "S2"], "iteration": <current>}`
|
|
99
|
+
- Every finding MUST have at least one source_id
|
|
100
|
+
- Add new sources to the top-level `sources` registry
|
|
101
|
+
- Reuse existing source IDs for the same URL
|
|
102
|
+
- Add current iteration number to `iterations_touched` array
|
|
103
|
+
- If a question is IRRELEVANT to the topic, REMOVE it from the questions array entirely
|
|
104
|
+
- Do NOT add new questions -- work through the original plan
|
|
105
|
+
- Write the COMPLETE updated plan.json (not a partial update)
|
|
106
|
+
|
|
107
|
+
**gaps.md:** Rewrite with current state:
|
|
108
|
+
- List remaining open questions with confidence levels under "## Open Questions"
|
|
109
|
+
- Note any contradictions discovered under "## Contradictions"
|
|
110
|
+
- Update "## Last Updated" with current iteration number and timestamp
|
|
111
|
+
|
|
112
|
+
**synthesis.md:** Update findings for the question you worked on:
|
|
113
|
+
- Keep the "## Findings by Question" structure
|
|
114
|
+
- Add new findings under the relevant question heading
|
|
115
|
+
- Include question status and confidence in the heading
|
|
116
|
+
- Do NOT duplicate existing findings -- add only new information
|
|
117
|
+
- Do not remove findings from other questions
|
|
118
|
+
|
|
119
|
+
**state.json:** Update:
|
|
120
|
+
- `last_updated` to current ISO-8601 UTC timestamp
|
|
121
|
+
- `overall_confidence` to the average of all remaining questions' confidence values
|
|
122
|
+
- Do NOT change `iteration` or `phase` (oracle.sh manages these)
|
|
123
|
+
|
|
124
|
+
### Step 5: Assess and Complete
|
|
125
|
+
|
|
126
|
+
State your assessment: "Confidence: X% -- {brief reason}"
|
|
127
|
+
|
|
128
|
+
If `overall_confidence` >= `target_confidence` (from state.json) OR all remaining
|
|
129
|
+
questions are "answered": output `<oracle>COMPLETE</oracle>`
|
|
130
|
+
|
|
131
|
+
Otherwise, end normally for another iteration.
|
|
132
|
+
|
|
133
|
+
## Confidence Scoring Rubric
|
|
134
|
+
|
|
135
|
+
Use this rubric when scoring question confidence. Anchor scores to evidence quality.
|
|
136
|
+
|
|
137
|
+
| Score | Level | Criteria |
|
|
138
|
+
|-------|-------|----------|
|
|
139
|
+
| 0-20% | Unexplored | No research conducted on this question |
|
|
140
|
+
| 20-40% | Surface level | General information only, no specific details or sources |
|
|
141
|
+
| 40-60% | Partial understanding | Specific details from 1-2 sources, some gaps remain |
|
|
142
|
+
| 60-80% | Good understanding | Multiple sources agree, edge cases identified and documented |
|
|
143
|
+
| 80-95% | Thorough | Primary sources verified, contradictions resolved, limitations known |
|
|
144
|
+
| 95-100% | Exhaustive | All reasonable angles explored, high-quality sources confirmed |
|
|
145
|
+
|
|
146
|
+
**Do NOT inflate confidence.** One blog post = 30%, not 70%. A single source
|
|
147
|
+
without corroboration caps at 50% regardless of detail level.
|
|
148
|
+
|
|
149
|
+
**Do NOT deflate confidence to keep research going.** Score honestly based on
|
|
150
|
+
the evidence you have. If the question is well-answered, say so.
|
|
151
|
+
|
|
152
|
+
**Source-backed confidence rules:**
|
|
153
|
+
- 0 sources: Finding is UNSUPPORTED -- do not record it
|
|
154
|
+
- 1 source: Single-source claim, capped at 50% contribution to question confidence
|
|
155
|
+
- 2+ sources: Multi-source claim, full confidence contribution
|
|
156
|
+
- The overall question confidence should reflect the source backing of its findings
|
|
157
|
+
|
|
158
|
+
## Important Rules
|
|
159
|
+
|
|
160
|
+
- Target ONE question per iteration
|
|
161
|
+
- Write COMPLETE JSON files, not partial updates (prevents corruption)
|
|
162
|
+
- Do NOT add new sub-questions -- work through the original plan
|
|
163
|
+
- Remove irrelevant questions entirely -- do not mark them as "skipped"
|
|
164
|
+
- Reference existing findings BEFORE writing new ones -- no restatements
|
|
165
|
+
- Do NOT modify any code files or colony state
|
|
166
|
+
- Only write to `.aether/oracle/` directory
|
|
167
|
+
- If this iteration is labeled "SYNTHESIS PASS" in the directive above, follow those instructions instead of the normal research flow -- consolidate and organize existing findings rather than researching new information
|
|
168
|
+
- In synthesis passes, use inline citations [S1], [S2] linking findings to sources. Include a ## Sources section listing all sources with IDs, URLs, titles, and access dates.
|