aether-colony 5.3.2 → 5.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.aether/aether-utils.sh +181 -5
- package/.aether/commands/archaeology.yaml +3 -3
- package/.aether/commands/build.yaml +80 -45
- package/.aether/commands/chaos.yaml +7 -7
- package/.aether/commands/colonize.yaml +17 -17
- package/.aether/commands/continue.yaml +40 -40
- package/.aether/commands/council.yaml +6 -6
- package/.aether/commands/data-clean.yaml +3 -3
- package/.aether/commands/dream.yaml +2 -2
- package/.aether/commands/entomb.yaml +12 -12
- package/.aether/commands/export-signals.yaml +2 -2
- package/.aether/commands/feedback.yaml +6 -6
- package/.aether/commands/flag.yaml +2 -2
- package/.aether/commands/flags.yaml +4 -4
- package/.aether/commands/focus.yaml +6 -6
- package/.aether/commands/help.yaml +1 -1
- package/.aether/commands/history.yaml +1 -1
- package/.aether/commands/import-signals.yaml +2 -2
- package/.aether/commands/init.yaml +44 -27
- package/.aether/commands/insert-phase.yaml +1 -1
- package/.aether/commands/interpret.yaml +2 -2
- package/.aether/commands/lay-eggs.yaml +3 -3
- package/.aether/commands/maturity.yaml +2 -2
- package/.aether/commands/memory-details.yaml +1 -1
- package/.aether/commands/migrate-state.yaml +1 -1
- package/.aether/commands/oracle.yaml +147 -82
- package/.aether/commands/organize.yaml +5 -5
- package/.aether/commands/patrol.yaml +8 -8
- package/.aether/commands/pause-colony.yaml +7 -7
- package/.aether/commands/phase.yaml +1 -1
- package/.aether/commands/pheromones.yaml +1 -1
- package/.aether/commands/plan.yaml +14 -14
- package/.aether/commands/quick.yaml +4 -4
- package/.aether/commands/redirect.yaml +6 -6
- package/.aether/commands/resume-colony.yaml +9 -9
- package/.aether/commands/resume.yaml +5 -38
- package/.aether/commands/run.yaml +10 -10
- package/.aether/commands/seal.yaml +33 -33
- package/.aether/commands/skill-create.yaml +4 -4
- package/.aether/commands/status.yaml +14 -14
- package/.aether/commands/swarm.yaml +14 -14
- package/.aether/commands/tunnels.yaml +7 -7
- package/.aether/commands/update.yaml +1 -1
- package/.aether/commands/verify-castes.yaml +3 -3
- package/.aether/commands/watch.yaml +15 -15
- package/.aether/docs/command-playbooks/build-complete.md +48 -15
- package/.aether/docs/command-playbooks/build-context.md +11 -11
- package/.aether/docs/command-playbooks/build-full.md +76 -76
- package/.aether/docs/command-playbooks/build-prep.md +10 -10
- package/.aether/docs/command-playbooks/build-verify.md +27 -27
- package/.aether/docs/command-playbooks/build-wave.md +38 -38
- package/.aether/docs/command-playbooks/continue-advance.md +60 -27
- package/.aether/docs/command-playbooks/continue-finalize.md +25 -11
- package/.aether/docs/command-playbooks/continue-full.md +60 -46
- package/.aether/docs/command-playbooks/continue-gates.md +18 -18
- package/.aether/docs/command-playbooks/continue-verify.md +10 -10
- package/.aether/docs/source-of-truth-map.md +10 -10
- package/.aether/docs/structural-learning-stack.md +283 -0
- package/.aether/templates/colony-state-template.json +1 -0
- package/.aether/utils/consolidation-seal.sh +196 -0
- package/.aether/utils/consolidation.sh +127 -0
- package/.aether/utils/curation-ants/archivist.sh +97 -0
- package/.aether/utils/curation-ants/critic.sh +214 -0
- package/.aether/utils/curation-ants/herald.sh +102 -0
- package/.aether/utils/curation-ants/janitor.sh +121 -0
- package/.aether/utils/curation-ants/librarian.sh +99 -0
- package/.aether/utils/curation-ants/nurse.sh +153 -0
- package/.aether/utils/curation-ants/orchestrator.sh +181 -0
- package/.aether/utils/curation-ants/scribe.sh +164 -0
- package/.aether/utils/curation-ants/sentinel.sh +119 -0
- package/.aether/utils/event-bus.sh +301 -0
- package/.aether/utils/graph.sh +559 -0
- package/.aether/utils/instinct-store.sh +401 -0
- package/.aether/utils/learning.sh +79 -7
- package/.aether/utils/oracle/oracle-stop-hook.sh +896 -0
- package/.aether/utils/session.sh +13 -0
- package/.aether/utils/state-api.sh +1 -1
- package/.aether/utils/trust-scoring.sh +347 -0
- package/.aether/utils/worktree.sh +97 -0
- package/.claude/commands/ant/archaeology.md +2 -2
- package/.claude/commands/ant/chaos.md +4 -4
- package/.claude/commands/ant/colonize.md +9 -9
- package/.claude/commands/ant/council.md +6 -6
- package/.claude/commands/ant/data-clean.md +3 -3
- package/.claude/commands/ant/dream.md +2 -2
- package/.claude/commands/ant/entomb.md +9 -9
- package/.claude/commands/ant/export-signals.md +2 -2
- package/.claude/commands/ant/feedback.md +4 -4
- package/.claude/commands/ant/flag.md +2 -2
- package/.claude/commands/ant/flags.md +4 -4
- package/.claude/commands/ant/focus.md +4 -4
- package/.claude/commands/ant/help.md +1 -1
- package/.claude/commands/ant/history.md +1 -1
- package/.claude/commands/ant/import-signals.md +2 -2
- package/.claude/commands/ant/init.md +44 -27
- package/.claude/commands/ant/insert-phase.md +1 -1
- package/.claude/commands/ant/interpret.md +2 -2
- package/.claude/commands/ant/lay-eggs.md +2 -2
- package/.claude/commands/ant/maturity.md +2 -2
- package/.claude/commands/ant/memory-details.md +1 -1
- package/.claude/commands/ant/migrate-state.md +1 -1
- package/.claude/commands/ant/oracle.md +78 -42
- package/.claude/commands/ant/organize.md +3 -3
- package/.claude/commands/ant/patrol.md +8 -8
- package/.claude/commands/ant/pause-colony.md +5 -5
- package/.claude/commands/ant/phase.md +1 -1
- package/.claude/commands/ant/pheromones.md +1 -1
- package/.claude/commands/ant/plan.md +8 -8
- package/.claude/commands/ant/quick.md +4 -4
- package/.claude/commands/ant/redirect.md +4 -4
- package/.claude/commands/ant/resume-colony.md +5 -5
- package/.claude/commands/ant/resume.md +17 -29
- package/.claude/commands/ant/run.md +10 -10
- package/.claude/commands/ant/seal.md +25 -25
- package/.claude/commands/ant/skill-create.md +2 -2
- package/.claude/commands/ant/status.md +14 -14
- package/.claude/commands/ant/swarm.md +14 -14
- package/.claude/commands/ant/tunnels.md +4 -4
- package/.claude/commands/ant/update.md +1 -1
- package/.claude/commands/ant/verify-castes.md +2 -2
- package/.claude/commands/ant/watch.md +8 -8
- package/.opencode/commands/ant/archaeology.md +1 -1
- package/.opencode/commands/ant/build.md +80 -45
- package/.opencode/commands/ant/chaos.md +3 -3
- package/.opencode/commands/ant/colonize.md +8 -8
- package/.opencode/commands/ant/continue.md +40 -40
- package/.opencode/commands/ant/council.md +5 -5
- package/.opencode/commands/ant/data-clean.md +2 -2
- package/.opencode/commands/ant/dream.md +1 -1
- package/.opencode/commands/ant/entomb.md +3 -3
- package/.opencode/commands/ant/export-signals.md +1 -1
- package/.opencode/commands/ant/feedback.md +2 -2
- package/.opencode/commands/ant/flag.md +1 -1
- package/.opencode/commands/ant/flags.md +3 -3
- package/.opencode/commands/ant/focus.md +2 -2
- package/.opencode/commands/ant/import-signals.md +1 -1
- package/.opencode/commands/ant/init.md +44 -27
- package/.opencode/commands/ant/insert-phase.md +1 -1
- package/.opencode/commands/ant/interpret.md +1 -1
- package/.opencode/commands/ant/lay-eggs.md +2 -2
- package/.opencode/commands/ant/maturity.md +1 -1
- package/.opencode/commands/ant/memory-details.md +1 -1
- package/.opencode/commands/ant/oracle.md +69 -40
- package/.opencode/commands/ant/organize.md +2 -2
- package/.opencode/commands/ant/patrol.md +8 -8
- package/.opencode/commands/ant/pause-colony.md +2 -2
- package/.opencode/commands/ant/pheromones.md +1 -1
- package/.opencode/commands/ant/plan.md +6 -6
- package/.opencode/commands/ant/quick.md +4 -4
- package/.opencode/commands/ant/redirect.md +2 -2
- package/.opencode/commands/ant/resume-colony.md +4 -4
- package/.opencode/commands/ant/resume.md +5 -17
- package/.opencode/commands/ant/run.md +10 -10
- package/.opencode/commands/ant/seal.md +8 -8
- package/.opencode/commands/ant/skill-create.md +2 -2
- package/.opencode/commands/ant/status.md +10 -10
- package/.opencode/commands/ant/tunnels.md +3 -3
- package/.opencode/commands/ant/verify-castes.md +1 -1
- package/.opencode/commands/ant/watch.md +7 -7
- package/CHANGELOG.md +83 -0
- package/README.md +22 -9
- package/bin/cli.js +118 -3
- package/bin/lib/binary-downloader.js +267 -0
- package/bin/lib/update-transaction.js +27 -3
- package/bin/lib/version-gate.js +179 -0
- package/bin/npx-entry.js +0 -0
- package/package.json +1 -1
- package/.aether/agents/aether-ambassador.md +0 -140
- package/.aether/agents/aether-archaeologist.md +0 -108
- package/.aether/agents/aether-architect.md +0 -133
- package/.aether/agents/aether-auditor.md +0 -144
- package/.aether/agents/aether-builder.md +0 -184
- package/.aether/agents/aether-chaos.md +0 -115
- package/.aether/agents/aether-chronicler.md +0 -122
- package/.aether/agents/aether-gatekeeper.md +0 -116
- package/.aether/agents/aether-includer.md +0 -117
- package/.aether/agents/aether-keeper.md +0 -177
- package/.aether/agents/aether-measurer.md +0 -128
- package/.aether/agents/aether-oracle.md +0 -137
- package/.aether/agents/aether-probe.md +0 -133
- package/.aether/agents/aether-queen.md +0 -286
- package/.aether/agents/aether-route-setter.md +0 -130
- package/.aether/agents/aether-sage.md +0 -106
- package/.aether/agents/aether-scout.md +0 -101
- package/.aether/agents/aether-surveyor-disciplines.md +0 -391
- package/.aether/agents/aether-surveyor-nest.md +0 -329
- package/.aether/agents/aether-surveyor-pathogens.md +0 -264
- package/.aether/agents/aether-surveyor-provisions.md +0 -334
- package/.aether/agents/aether-tracker.md +0 -137
- package/.aether/agents/aether-watcher.md +0 -174
- package/.aether/agents/aether-weaver.md +0 -130
- package/.aether/commands/claude/archaeology.md +0 -334
- package/.aether/commands/claude/build.md +0 -65
- package/.aether/commands/claude/chaos.md +0 -336
- package/.aether/commands/claude/colonize.md +0 -259
- package/.aether/commands/claude/continue.md +0 -60
- package/.aether/commands/claude/council.md +0 -507
- package/.aether/commands/claude/data-clean.md +0 -81
- package/.aether/commands/claude/dream.md +0 -268
- package/.aether/commands/claude/entomb.md +0 -498
- package/.aether/commands/claude/export-signals.md +0 -57
- package/.aether/commands/claude/feedback.md +0 -96
- package/.aether/commands/claude/flag.md +0 -151
- package/.aether/commands/claude/flags.md +0 -169
- package/.aether/commands/claude/focus.md +0 -76
- package/.aether/commands/claude/help.md +0 -154
- package/.aether/commands/claude/history.md +0 -140
- package/.aether/commands/claude/import-signals.md +0 -71
- package/.aether/commands/claude/init.md +0 -505
- package/.aether/commands/claude/insert-phase.md +0 -105
- package/.aether/commands/claude/interpret.md +0 -278
- package/.aether/commands/claude/lay-eggs.md +0 -210
- package/.aether/commands/claude/maturity.md +0 -113
- package/.aether/commands/claude/memory-details.md +0 -77
- package/.aether/commands/claude/migrate-state.md +0 -171
- package/.aether/commands/claude/oracle.md +0 -642
- package/.aether/commands/claude/organize.md +0 -232
- package/.aether/commands/claude/patrol.md +0 -620
- package/.aether/commands/claude/pause-colony.md +0 -233
- package/.aether/commands/claude/phase.md +0 -115
- package/.aether/commands/claude/pheromones.md +0 -156
- package/.aether/commands/claude/plan.md +0 -693
- package/.aether/commands/claude/preferences.md +0 -65
- package/.aether/commands/claude/quick.md +0 -100
- package/.aether/commands/claude/redirect.md +0 -76
- package/.aether/commands/claude/resume-colony.md +0 -197
- package/.aether/commands/claude/resume.md +0 -388
- package/.aether/commands/claude/run.md +0 -231
- package/.aether/commands/claude/seal.md +0 -774
- package/.aether/commands/claude/skill-create.md +0 -286
- package/.aether/commands/claude/status.md +0 -410
- package/.aether/commands/claude/swarm.md +0 -349
- package/.aether/commands/claude/tunnels.md +0 -426
- package/.aether/commands/claude/update.md +0 -132
- package/.aether/commands/claude/verify-castes.md +0 -143
- package/.aether/commands/claude/watch.md +0 -239
- package/.aether/commands/opencode/archaeology.md +0 -331
- package/.aether/commands/opencode/build.md +0 -1168
- package/.aether/commands/opencode/chaos.md +0 -329
- package/.aether/commands/opencode/colonize.md +0 -195
- package/.aether/commands/opencode/continue.md +0 -1436
- package/.aether/commands/opencode/council.md +0 -437
- package/.aether/commands/opencode/data-clean.md +0 -77
- package/.aether/commands/opencode/dream.md +0 -260
- package/.aether/commands/opencode/entomb.md +0 -377
- package/.aether/commands/opencode/export-signals.md +0 -54
- package/.aether/commands/opencode/feedback.md +0 -99
- package/.aether/commands/opencode/flag.md +0 -149
- package/.aether/commands/opencode/flags.md +0 -167
- package/.aether/commands/opencode/focus.md +0 -73
- package/.aether/commands/opencode/help.md +0 -157
- package/.aether/commands/opencode/history.md +0 -136
- package/.aether/commands/opencode/import-signals.md +0 -68
- package/.aether/commands/opencode/init.md +0 -518
- package/.aether/commands/opencode/insert-phase.md +0 -111
- package/.aether/commands/opencode/interpret.md +0 -272
- package/.aether/commands/opencode/lay-eggs.md +0 -213
- package/.aether/commands/opencode/maturity.md +0 -108
- package/.aether/commands/opencode/memory-details.md +0 -83
- package/.aether/commands/opencode/migrate-state.md +0 -165
- package/.aether/commands/opencode/oracle.md +0 -593
- package/.aether/commands/opencode/organize.md +0 -226
- package/.aether/commands/opencode/patrol.md +0 -626
- package/.aether/commands/opencode/pause-colony.md +0 -203
- package/.aether/commands/opencode/phase.md +0 -113
- package/.aether/commands/opencode/pheromones.md +0 -162
- package/.aether/commands/opencode/plan.md +0 -684
- package/.aether/commands/opencode/preferences.md +0 -71
- package/.aether/commands/opencode/quick.md +0 -91
- package/.aether/commands/opencode/redirect.md +0 -84
- package/.aether/commands/opencode/resume-colony.md +0 -190
- package/.aether/commands/opencode/resume.md +0 -394
- package/.aether/commands/opencode/run.md +0 -237
- package/.aether/commands/opencode/seal.md +0 -452
- package/.aether/commands/opencode/skill-create.md +0 -63
- package/.aether/commands/opencode/status.md +0 -307
- package/.aether/commands/opencode/swarm.md +0 -15
- package/.aether/commands/opencode/tunnels.md +0 -400
- package/.aether/commands/opencode/update.md +0 -127
- package/.aether/commands/opencode/verify-castes.md +0 -139
- package/.aether/commands/opencode/watch.md +0 -227
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Nurse curation ant — trust recalculation for observations and instincts
|
|
3
|
+
# Provides: _curation_nurse
|
|
4
|
+
#
|
|
5
|
+
# These functions are sourced by aether-utils.sh at startup.
|
|
6
|
+
# All shared infrastructure (json_ok, json_err, atomic_write,
|
|
7
|
+
# COLONY_DATA_DIR, SCRIPT_DIR, error constants) is available.
|
|
8
|
+
#
|
|
9
|
+
# Subcommand: curation-nurse [--dry-run]
|
|
10
|
+
# Recalculates trust scores for all observations (that have source_type/evidence_type)
|
|
11
|
+
# and applies trust-decay to instincts based on days since created_at.
|
|
12
|
+
|
|
13
|
+
# ============================================================================
|
|
14
|
+
# _curation_nurse
|
|
15
|
+
# Recalculate trust scores across observations and instincts.
|
|
16
|
+
#
|
|
17
|
+
# Usage: curation-nurse [--dry-run]
|
|
18
|
+
#
|
|
19
|
+
# Output: {observations_updated: N, instincts_updated: N, dry_run: bool}
|
|
20
|
+
# ============================================================================
|
|
21
|
+
_curation_nurse() {
|
|
22
|
+
local dry_run="false"
|
|
23
|
+
|
|
24
|
+
while [[ $# -gt 0 ]]; do
|
|
25
|
+
case "$1" in
|
|
26
|
+
--dry-run) dry_run="true"; shift ;;
|
|
27
|
+
*) shift ;;
|
|
28
|
+
esac
|
|
29
|
+
done
|
|
30
|
+
|
|
31
|
+
local obs_updated=0
|
|
32
|
+
local inst_updated=0
|
|
33
|
+
local obs_file="$COLONY_DATA_DIR/learning-observations.json"
|
|
34
|
+
local inst_file="$COLONY_DATA_DIR/instincts.json"
|
|
35
|
+
local now_epoch
|
|
36
|
+
now_epoch=$(date -u +%s)
|
|
37
|
+
|
|
38
|
+
# ── Recalculate observation trust scores ──────────────────────────────────
|
|
39
|
+
if [[ -f "$obs_file" ]]; then
|
|
40
|
+
local obs_count
|
|
41
|
+
obs_count=$(jq '[.observations[] | select(.source_type != null and .evidence_type != null)] | length' "$obs_file" 2>/dev/null || echo 0)
|
|
42
|
+
|
|
43
|
+
if [[ "$obs_count" -gt 0 ]]; then
|
|
44
|
+
local updated_obs
|
|
45
|
+
updated_obs=$(jq -c '[.observations[]]' "$obs_file" 2>/dev/null || echo "[]")
|
|
46
|
+
|
|
47
|
+
local new_obs_array="[]"
|
|
48
|
+
while IFS= read -r obs_json; do
|
|
49
|
+
local source_type evidence_type last_seen
|
|
50
|
+
source_type=$(echo "$obs_json" | jq -r '.source_type // empty')
|
|
51
|
+
evidence_type=$(echo "$obs_json" | jq -r '.evidence_type // empty')
|
|
52
|
+
last_seen=$(echo "$obs_json" | jq -r '.last_seen // empty')
|
|
53
|
+
|
|
54
|
+
if [[ -z "$source_type" || -z "$evidence_type" ]]; then
|
|
55
|
+
new_obs_array=$(echo "$new_obs_array" | jq --argjson entry "$obs_json" '. += [$entry]')
|
|
56
|
+
continue
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
local days_since=0
|
|
60
|
+
if [[ -n "$last_seen" ]]; then
|
|
61
|
+
local last_epoch
|
|
62
|
+
last_epoch=$(date -u -d "$last_seen" +%s 2>/dev/null || date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$last_seen" +%s 2>/dev/null || echo "$now_epoch")
|
|
63
|
+
days_since=$(( (now_epoch - last_epoch) / 86400 ))
|
|
64
|
+
[[ "$days_since" -lt 0 ]] && days_since=0
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
local trust_result trust_score trust_tier
|
|
68
|
+
trust_result=$(_trust_calculate --source "$source_type" --evidence "$evidence_type" --days-since "$days_since" 2>/dev/null) || true
|
|
69
|
+
if echo "$trust_result" | jq -e '.ok == true' >/dev/null 2>&1; then
|
|
70
|
+
trust_score=$(echo "$trust_result" | jq -r '.result.score')
|
|
71
|
+
trust_tier=$(echo "$trust_result" | jq -r '.result.tier')
|
|
72
|
+
local updated_entry
|
|
73
|
+
updated_entry=$(echo "$obs_json" | jq \
|
|
74
|
+
--argjson score "$trust_score" \
|
|
75
|
+
--arg tier "$trust_tier" \
|
|
76
|
+
'. + {trust_score: $score, trust_tier: $tier}')
|
|
77
|
+
new_obs_array=$(echo "$new_obs_array" | jq --argjson entry "$updated_entry" '. += [$entry]')
|
|
78
|
+
obs_updated=$((obs_updated + 1))
|
|
79
|
+
else
|
|
80
|
+
new_obs_array=$(echo "$new_obs_array" | jq --argjson entry "$obs_json" '. += [$entry]')
|
|
81
|
+
fi
|
|
82
|
+
done < <(echo "$updated_obs" | jq -c '.[]')
|
|
83
|
+
|
|
84
|
+
if [[ "$dry_run" != "true" ]]; then
|
|
85
|
+
local final_obs
|
|
86
|
+
final_obs=$(jq --argjson obs "$new_obs_array" '.observations = $obs' "$obs_file" 2>/dev/null) || true
|
|
87
|
+
[[ -n "$final_obs" ]] && atomic_write "$obs_file" "$final_obs"
|
|
88
|
+
fi
|
|
89
|
+
fi
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# ── Apply trust-decay to instincts ────────────────────────────────────────
|
|
93
|
+
if [[ -f "$inst_file" ]]; then
|
|
94
|
+
local active_count
|
|
95
|
+
active_count=$(jq '[.instincts[] | select(.archived == false)] | length' "$inst_file" 2>/dev/null || echo 0)
|
|
96
|
+
|
|
97
|
+
if [[ "$active_count" -gt 0 ]]; then
|
|
98
|
+
local updated_inst_array="[]"
|
|
99
|
+
local all_inst
|
|
100
|
+
all_inst=$(jq -c '[.instincts[]]' "$inst_file" 2>/dev/null || echo "[]")
|
|
101
|
+
|
|
102
|
+
while IFS= read -r inst_json; do
|
|
103
|
+
local archived
|
|
104
|
+
archived=$(echo "$inst_json" | jq -r '.archived // false')
|
|
105
|
+
|
|
106
|
+
if [[ "$archived" == "true" ]]; then
|
|
107
|
+
updated_inst_array=$(echo "$updated_inst_array" | jq --argjson entry "$inst_json" '. += [$entry]')
|
|
108
|
+
continue
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
local created_at days_inst=0
|
|
112
|
+
created_at=$(echo "$inst_json" | jq -r '.provenance.created_at // empty')
|
|
113
|
+
if [[ -n "$created_at" ]]; then
|
|
114
|
+
local created_epoch
|
|
115
|
+
created_epoch=$(date -u -d "$created_at" +%s 2>/dev/null || date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$created_at" +%s 2>/dev/null || echo "$now_epoch")
|
|
116
|
+
days_inst=$(( (now_epoch - created_epoch) / 86400 ))
|
|
117
|
+
[[ "$days_inst" -lt 0 ]] && days_inst=0
|
|
118
|
+
fi
|
|
119
|
+
|
|
120
|
+
local current_score
|
|
121
|
+
current_score=$(echo "$inst_json" | jq -r '.trust_score // 0.5')
|
|
122
|
+
local decay_result decayed_score new_tier
|
|
123
|
+
decay_result=$(_trust_decay --score "$current_score" --days "$days_inst" 2>/dev/null) || true
|
|
124
|
+
|
|
125
|
+
if echo "$decay_result" | jq -e '.ok == true' >/dev/null 2>&1; then
|
|
126
|
+
decayed_score=$(echo "$decay_result" | jq -r '.result.decayed')
|
|
127
|
+
new_tier=$(_trust_score_to_tier "$decayed_score" 2>/dev/null || echo "dormant")
|
|
128
|
+
local updated_entry
|
|
129
|
+
updated_entry=$(echo "$inst_json" | jq \
|
|
130
|
+
--argjson score "$decayed_score" \
|
|
131
|
+
--arg tier "$new_tier" \
|
|
132
|
+
'.trust_score = $score | .trust_tier = $tier')
|
|
133
|
+
updated_inst_array=$(echo "$updated_inst_array" | jq --argjson entry "$updated_entry" '. += [$entry]')
|
|
134
|
+
inst_updated=$((inst_updated + 1))
|
|
135
|
+
else
|
|
136
|
+
updated_inst_array=$(echo "$updated_inst_array" | jq --argjson entry "$inst_json" '. += [$entry]')
|
|
137
|
+
fi
|
|
138
|
+
done < <(echo "$all_inst" | jq -c '.[]')
|
|
139
|
+
|
|
140
|
+
if [[ "$dry_run" != "true" ]]; then
|
|
141
|
+
local final_inst
|
|
142
|
+
final_inst=$(jq --argjson insts "$updated_inst_array" '.instincts = $insts' "$inst_file" 2>/dev/null) || true
|
|
143
|
+
[[ -n "$final_inst" ]] && atomic_write "$inst_file" "$final_inst"
|
|
144
|
+
fi
|
|
145
|
+
fi
|
|
146
|
+
fi
|
|
147
|
+
|
|
148
|
+
json_ok "$(jq -n \
|
|
149
|
+
--argjson obs "$obs_updated" \
|
|
150
|
+
--argjson inst "$inst_updated" \
|
|
151
|
+
--argjson dry "$([ "$dry_run" == "true" ] && echo true || echo false)" \
|
|
152
|
+
'{observations_updated: $obs, instincts_updated: $inst, dry_run: $dry}')"
|
|
153
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Curation Orchestrator — runs all 8 curation ants in sequence
|
|
3
|
+
# Provides: _curation_run
|
|
4
|
+
#
|
|
5
|
+
# These functions are sourced by aether-utils.sh at startup.
|
|
6
|
+
# All shared infrastructure (json_ok, json_err, COLONY_DATA_DIR, DATA_DIR,
|
|
7
|
+
# error constants) is available when sourced.
|
|
8
|
+
|
|
9
|
+
# ============================================================================
|
|
10
|
+
# _curation_run
|
|
11
|
+
# Run all 8 curation ants in the correct order.
|
|
12
|
+
#
|
|
13
|
+
# Usage: curation-run [--dry-run] [--verbose]
|
|
14
|
+
#
|
|
15
|
+
# Execution order:
|
|
16
|
+
# 1. sentinel — health check (abort if corrupt)
|
|
17
|
+
# 2. nurse — recalculate trust scores
|
|
18
|
+
# 3. critic — detect contradictions
|
|
19
|
+
# 4. herald — promote high-trust to QUEEN.md
|
|
20
|
+
# 5. janitor — clean expired events/archives
|
|
21
|
+
# 6. archivist — archive low-trust instincts
|
|
22
|
+
# 7. librarian — inventory stats
|
|
23
|
+
# 8. scribe — generate report
|
|
24
|
+
#
|
|
25
|
+
# Output: json_ok with {steps, total_steps, succeeded, failed, dry_run,
|
|
26
|
+
# report_path, duration_ms}
|
|
27
|
+
# ============================================================================
|
|
28
|
+
_curation_run() {
|
|
29
|
+
local dry_run="false"
|
|
30
|
+
|
|
31
|
+
while [[ $# -gt 0 ]]; do
|
|
32
|
+
case "$1" in
|
|
33
|
+
--dry-run) dry_run="true"; shift ;;
|
|
34
|
+
--verbose) shift ;;
|
|
35
|
+
*) shift ;;
|
|
36
|
+
esac
|
|
37
|
+
done
|
|
38
|
+
|
|
39
|
+
# Portable millisecond timer: try python3, fall back to seconds*1000
|
|
40
|
+
local start_ms
|
|
41
|
+
start_ms=$(python3 -c "import time; print(int(time.time()*1000))" 2>/dev/null \
|
|
42
|
+
|| echo $(( $(date +%s) * 1000 )))
|
|
43
|
+
|
|
44
|
+
local steps_json="[]"
|
|
45
|
+
local succeeded=0
|
|
46
|
+
local failed=0
|
|
47
|
+
local report_path="null"
|
|
48
|
+
# Shared variable: holds the raw JSON result of the last step executed
|
|
49
|
+
local _CR_LAST_RESULT=""
|
|
50
|
+
|
|
51
|
+
# _cr_step <name> [extra_args...]
|
|
52
|
+
# Runs curation-<name> (with --dry-run if set), updates steps_json,
|
|
53
|
+
# succeeded/failed counters, and sets _CR_LAST_RESULT.
|
|
54
|
+
# Must be called WITHOUT command substitution so variable mutations persist.
|
|
55
|
+
_cr_step() {
|
|
56
|
+
local step_name="$1"
|
|
57
|
+
shift
|
|
58
|
+
|
|
59
|
+
local cmd="curation-${step_name}"
|
|
60
|
+
|
|
61
|
+
# Build the args list (avoid empty-array nounset issues)
|
|
62
|
+
local step_result step_status step_summary
|
|
63
|
+
if [[ "$dry_run" == "true" && $# -gt 0 ]]; then
|
|
64
|
+
step_result=$(bash "$0" "$cmd" "--dry-run" "$@" 2>/dev/null) || true
|
|
65
|
+
elif [[ "$dry_run" == "true" ]]; then
|
|
66
|
+
step_result=$(bash "$0" "$cmd" "--dry-run" 2>/dev/null) || true
|
|
67
|
+
elif [[ $# -gt 0 ]]; then
|
|
68
|
+
step_result=$(bash "$0" "$cmd" "$@" 2>/dev/null) || true
|
|
69
|
+
else
|
|
70
|
+
step_result=$(bash "$0" "$cmd" 2>/dev/null) || true
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
_CR_LAST_RESULT="$step_result"
|
|
74
|
+
|
|
75
|
+
if echo "$step_result" | jq -e '.ok == true' >/dev/null 2>&1; then
|
|
76
|
+
step_status="ok"
|
|
77
|
+
step_summary=$(echo "$step_result" | jq -r '
|
|
78
|
+
.result |
|
|
79
|
+
if type == "object" then
|
|
80
|
+
[ to_entries[] | "\(.key) \(.value)" ] | join(", ")
|
|
81
|
+
else
|
|
82
|
+
tostring
|
|
83
|
+
end
|
|
84
|
+
' 2>/dev/null | head -c 120 || echo "ok")
|
|
85
|
+
[[ -z "$step_summary" ]] && step_summary="ok"
|
|
86
|
+
succeeded=$(( succeeded + 1 ))
|
|
87
|
+
else
|
|
88
|
+
step_status="failed"
|
|
89
|
+
local err_msg
|
|
90
|
+
err_msg=$(echo "$step_result" | jq -r '.error // "unknown error"' 2>/dev/null || echo "unknown error")
|
|
91
|
+
step_summary="$err_msg"
|
|
92
|
+
failed=$(( failed + 1 ))
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
steps_json=$(echo "$steps_json" | jq \
|
|
96
|
+
--arg name "$step_name" \
|
|
97
|
+
--arg status "$step_status" \
|
|
98
|
+
--arg summary "$step_summary" \
|
|
99
|
+
'. += [{"name": $name, "status": $status, "summary": $summary}]')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
# ── Step 1: Sentinel — health check ─────────────────────────────────────
|
|
103
|
+
_cr_step "sentinel"
|
|
104
|
+
|
|
105
|
+
# Abort remaining steps if sentinel found critical corruption
|
|
106
|
+
local sentinel_status
|
|
107
|
+
sentinel_status=$(echo "$steps_json" | jq -r '.[-1].status')
|
|
108
|
+
if [[ "$sentinel_status" == "ok" ]]; then
|
|
109
|
+
local corrupt_count
|
|
110
|
+
corrupt_count=$(echo "$_CR_LAST_RESULT" | jq '[.result.checks[]? | select(.status == "corrupt")] | length' 2>/dev/null || echo 0)
|
|
111
|
+
if [[ "$corrupt_count" -gt 0 ]]; then
|
|
112
|
+
for skipped_step in nurse critic herald janitor archivist librarian scribe; do
|
|
113
|
+
steps_json=$(echo "$steps_json" | jq \
|
|
114
|
+
--arg name "$skipped_step" \
|
|
115
|
+
'. += [{"name": $name, "status": "skipped", "summary": "skipped: sentinel detected corrupt stores"}]')
|
|
116
|
+
done
|
|
117
|
+
|
|
118
|
+
local end_ms
|
|
119
|
+
end_ms=$(python3 -c "import time; print(int(time.time()*1000))" 2>/dev/null \
|
|
120
|
+
|| echo $(( $(date +%s) * 1000 )))
|
|
121
|
+
local duration_ms=$(( end_ms - start_ms ))
|
|
122
|
+
|
|
123
|
+
json_ok "$(jq -n \
|
|
124
|
+
--argjson steps "$steps_json" \
|
|
125
|
+
--argjson total 8 \
|
|
126
|
+
--argjson succeeded "$succeeded" \
|
|
127
|
+
--argjson failed "$failed" \
|
|
128
|
+
--argjson dry "$([ "$dry_run" == "true" ] && echo true || echo false)" \
|
|
129
|
+
--argjson dur "$duration_ms" \
|
|
130
|
+
'{steps:$steps, total_steps:$total, succeeded:$succeeded, failed:$failed,
|
|
131
|
+
dry_run:$dry, report_path:null, duration_ms:$dur}')"
|
|
132
|
+
return
|
|
133
|
+
fi
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# ── Step 2: Nurse — recalculate trust scores ─────────────────────────────
|
|
137
|
+
_cr_step "nurse"
|
|
138
|
+
|
|
139
|
+
# ── Step 3: Critic — detect contradictions ───────────────────────────────
|
|
140
|
+
_cr_step "critic"
|
|
141
|
+
|
|
142
|
+
# ── Step 4: Herald — promote high-trust to QUEEN.md ─────────────────────
|
|
143
|
+
_cr_step "herald"
|
|
144
|
+
|
|
145
|
+
# ── Step 5: Janitor — clean expired events and archives ──────────────────
|
|
146
|
+
_cr_step "janitor"
|
|
147
|
+
|
|
148
|
+
# ── Step 6: Archivist — archive low-trust instincts ─────────────────────
|
|
149
|
+
_cr_step "archivist"
|
|
150
|
+
|
|
151
|
+
# ── Step 7: Librarian — inventory stats ──────────────────────────────────
|
|
152
|
+
_cr_step "librarian"
|
|
153
|
+
|
|
154
|
+
# ── Step 8: Scribe — generate report ─────────────────────────────────────
|
|
155
|
+
_cr_step "scribe"
|
|
156
|
+
|
|
157
|
+
# Extract report_path from scribe result
|
|
158
|
+
if echo "$_CR_LAST_RESULT" | jq -e '.ok == true' >/dev/null 2>&1; then
|
|
159
|
+
local raw_path
|
|
160
|
+
raw_path=$(echo "$_CR_LAST_RESULT" | jq -r '.result.report_path // empty' 2>/dev/null || echo "")
|
|
161
|
+
if [[ -n "$raw_path" ]]; then
|
|
162
|
+
report_path=$(printf '%s' "$raw_path" | jq -Rs '.')
|
|
163
|
+
fi
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
local end_ms
|
|
167
|
+
end_ms=$(python3 -c "import time; print(int(time.time()*1000))" 2>/dev/null \
|
|
168
|
+
|| echo $(( $(date +%s) * 1000 )))
|
|
169
|
+
local duration_ms=$(( end_ms - start_ms ))
|
|
170
|
+
|
|
171
|
+
json_ok "$(jq -n \
|
|
172
|
+
--argjson steps "$steps_json" \
|
|
173
|
+
--argjson total 8 \
|
|
174
|
+
--argjson succeeded "$succeeded" \
|
|
175
|
+
--argjson failed "$failed" \
|
|
176
|
+
--argjson dry "$([ "$dry_run" == "true" ] && echo true || echo false)" \
|
|
177
|
+
--argjson report_path "$report_path" \
|
|
178
|
+
--argjson dur "$duration_ms" \
|
|
179
|
+
'{steps:$steps, total_steps:$total, succeeded:$succeeded, failed:$failed,
|
|
180
|
+
dry_run:$dry, report_path:$report_path, duration_ms:$dur}')"
|
|
181
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Curation Scribe — Memory Consolidation Report Generation
|
|
3
|
+
# Generates a markdown report of the memory consolidation state.
|
|
4
|
+
#
|
|
5
|
+
# Functions:
|
|
6
|
+
# _curation_scribe
|
|
7
|
+
#
|
|
8
|
+
# These functions are sourced by aether-utils.sh at startup.
|
|
9
|
+
# All shared infrastructure (json_ok, json_err, COLONY_DATA_DIR, DATA_DIR,
|
|
10
|
+
# error constants) is available when sourced.
|
|
11
|
+
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# _curation_scribe
|
|
14
|
+
# Generate a markdown report of memory consolidation state.
|
|
15
|
+
# Usage: curation-scribe [--output <path>]
|
|
16
|
+
#
|
|
17
|
+
# Default output: $COLONY_DATA_DIR/curation-report.md
|
|
18
|
+
# Output: json_ok with {report_path:string, sections:N, generated_at:"ISO"}
|
|
19
|
+
# ============================================================================
|
|
20
|
+
_curation_scribe() {
|
|
21
|
+
local csc_output=""
|
|
22
|
+
|
|
23
|
+
while [[ $# -gt 0 ]]; do
|
|
24
|
+
case "$1" in
|
|
25
|
+
--output)
|
|
26
|
+
csc_output="${2:-}"
|
|
27
|
+
shift 2
|
|
28
|
+
;;
|
|
29
|
+
*)
|
|
30
|
+
shift
|
|
31
|
+
;;
|
|
32
|
+
esac
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
local csc_data_dir="${COLONY_DATA_DIR:-${DATA_DIR:-}}"
|
|
36
|
+
if [[ -z "$csc_data_dir" ]]; then
|
|
37
|
+
json_err "$E_VALIDATION_FAILED" "curation-scribe: COLONY_DATA_DIR is not set"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
[[ -z "$csc_output" ]] && csc_output="$csc_data_dir/curation-report.md"
|
|
41
|
+
|
|
42
|
+
local csc_ts
|
|
43
|
+
csc_ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
44
|
+
local csc_ts_human
|
|
45
|
+
csc_ts_human=$(date -u +"%Y-%m-%d %H:%M UTC")
|
|
46
|
+
|
|
47
|
+
# Step 1: Gather librarian stats (via curation-librarian subcommand if available)
|
|
48
|
+
local csc_total_instincts=0
|
|
49
|
+
local csc_active_instincts=0
|
|
50
|
+
local csc_archived_instincts=0
|
|
51
|
+
local csc_total_observations=0
|
|
52
|
+
local csc_total_events=0
|
|
53
|
+
|
|
54
|
+
local csc_lib_result
|
|
55
|
+
if csc_lib_result=$(COLONY_DATA_DIR="$csc_data_dir" DATA_DIR="$csc_data_dir" \
|
|
56
|
+
bash "$0" curation-librarian 2>/dev/null); then
|
|
57
|
+
if echo "$csc_lib_result" | jq -e '.ok == true' >/dev/null 2>&1; then
|
|
58
|
+
csc_total_instincts=$(echo "$csc_lib_result" | jq '.result.total_instincts // 0')
|
|
59
|
+
csc_active_instincts=$(echo "$csc_lib_result" | jq '.result.active_instincts // 0')
|
|
60
|
+
csc_archived_instincts=$(echo "$csc_lib_result"| jq '.result.archived_instincts // 0')
|
|
61
|
+
csc_total_observations=$(echo "$csc_lib_result"| jq '.result.total_observations // 0')
|
|
62
|
+
csc_total_events=$(echo "$csc_lib_result" | jq '.result.total_events // 0')
|
|
63
|
+
fi
|
|
64
|
+
else
|
|
65
|
+
# Gather stats directly if librarian is not yet available
|
|
66
|
+
local csc_instincts_file="$csc_data_dir/instincts.json"
|
|
67
|
+
if [[ -f "$csc_instincts_file" ]] && jq empty "$csc_instincts_file" 2>/dev/null; then
|
|
68
|
+
csc_total_instincts=$(jq '.instincts | length' "$csc_instincts_file" 2>/dev/null || echo 0)
|
|
69
|
+
csc_active_instincts=$(jq '[.instincts[] | select(.archived != true)] | length' "$csc_instincts_file" 2>/dev/null || echo 0)
|
|
70
|
+
csc_archived_instincts=$(jq '[.instincts[] | select(.archived == true)] | length' "$csc_instincts_file" 2>/dev/null || echo 0)
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
local csc_obs_file="$csc_data_dir/learning-observations.json"
|
|
74
|
+
if [[ -f "$csc_obs_file" ]] && jq empty "$csc_obs_file" 2>/dev/null; then
|
|
75
|
+
csc_total_observations=$(jq '.observations | length' "$csc_obs_file" 2>/dev/null || echo 0)
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
local csc_eb_file="$csc_data_dir/event-bus.jsonl"
|
|
79
|
+
if [[ -f "$csc_eb_file" ]]; then
|
|
80
|
+
csc_total_events=$(wc -l < "$csc_eb_file" | tr -d ' ')
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
# Step 2: Gather top 5 trusted instincts
|
|
85
|
+
local csc_top_instincts=""
|
|
86
|
+
local csc_instincts_file="$csc_data_dir/instincts.json"
|
|
87
|
+
if [[ -f "$csc_instincts_file" ]] && jq empty "$csc_instincts_file" 2>/dev/null; then
|
|
88
|
+
csc_top_instincts=$(jq -r \
|
|
89
|
+
'[.instincts[] | select(.archived != true)] | sort_by(-.trust_score // -.confidence // 0) | .[0:5][] |
|
|
90
|
+
"- **[\(.trust_score // .confidence // 0 | . * 100 | floor)%]** \(.trigger // "unknown trigger")"' \
|
|
91
|
+
"$csc_instincts_file" 2>/dev/null || echo "")
|
|
92
|
+
fi
|
|
93
|
+
[[ -z "$csc_top_instincts" ]] && csc_top_instincts="_No instincts found._"
|
|
94
|
+
|
|
95
|
+
# Step 3: Gather recent events (last 10)
|
|
96
|
+
local csc_recent_events=""
|
|
97
|
+
local csc_eb_file="$csc_data_dir/event-bus.jsonl"
|
|
98
|
+
if [[ -f "$csc_eb_file" ]] && [[ -s "$csc_eb_file" ]]; then
|
|
99
|
+
csc_recent_events=$(tail -10 "$csc_eb_file" | \
|
|
100
|
+
jq -r '"- [\(.timestamp // "?")] **\(.topic // "unknown")**: \(.source // "system")"' \
|
|
101
|
+
2>/dev/null || echo "")
|
|
102
|
+
fi
|
|
103
|
+
[[ -z "$csc_recent_events" ]] && csc_recent_events="_No recent events._"
|
|
104
|
+
|
|
105
|
+
# Step 4: Generate recommendations
|
|
106
|
+
local csc_recommendations=""
|
|
107
|
+
if [[ "$csc_active_instincts" -gt 40 ]]; then
|
|
108
|
+
csc_recommendations+="- Run \`curation-archivist\` to archive low-trust instincts (capacity at ${csc_active_instincts}/50)."$'\n'
|
|
109
|
+
fi
|
|
110
|
+
if [[ "$csc_total_events" -gt 100 ]]; then
|
|
111
|
+
csc_recommendations+="- Run \`curation-janitor\` to clean up expired events (${csc_total_events} events on bus)."$'\n'
|
|
112
|
+
fi
|
|
113
|
+
if [[ "$csc_total_observations" -gt 200 ]]; then
|
|
114
|
+
csc_recommendations+="- Consider pruning stale learning observations (${csc_total_observations} total)."$'\n'
|
|
115
|
+
fi
|
|
116
|
+
[[ -z "$csc_recommendations" ]] && csc_recommendations="_Memory stores are healthy. No immediate action needed._"
|
|
117
|
+
|
|
118
|
+
# Step 5: Write report
|
|
119
|
+
mkdir -p "$(dirname "$csc_output")"
|
|
120
|
+
|
|
121
|
+
cat > "$csc_output" <<REPORT
|
|
122
|
+
# Memory Consolidation Report
|
|
123
|
+
|
|
124
|
+
_Generated: ${csc_ts_human}_
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Memory Health Summary
|
|
129
|
+
|
|
130
|
+
| Store | Count |
|
|
131
|
+
|-------|-------|
|
|
132
|
+
| Total instincts | ${csc_total_instincts} |
|
|
133
|
+
| Active instincts | ${csc_active_instincts} |
|
|
134
|
+
| Archived instincts | ${csc_archived_instincts} |
|
|
135
|
+
| Learning observations | ${csc_total_observations} |
|
|
136
|
+
| Events on bus | ${csc_total_events} |
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## Top Trusted Instincts
|
|
141
|
+
|
|
142
|
+
${csc_top_instincts}
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Recent Events
|
|
147
|
+
|
|
148
|
+
${csc_recent_events}
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Recommendations
|
|
153
|
+
|
|
154
|
+
${csc_recommendations}
|
|
155
|
+
REPORT
|
|
156
|
+
|
|
157
|
+
local csc_sections=4
|
|
158
|
+
|
|
159
|
+
json_ok "$(jq -nc \
|
|
160
|
+
--arg report_path "$csc_output" \
|
|
161
|
+
--argjson sections "$csc_sections" \
|
|
162
|
+
--arg generated_at "$csc_ts" \
|
|
163
|
+
'{report_path:$report_path, sections:$sections, generated_at:$generated_at}')"
|
|
164
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Curation Sentinel — Memory Health Monitoring
|
|
3
|
+
# Checks health of all memory stores and reports issues.
|
|
4
|
+
#
|
|
5
|
+
# Functions:
|
|
6
|
+
# _curation_sentinel
|
|
7
|
+
#
|
|
8
|
+
# These functions are sourced by aether-utils.sh at startup.
|
|
9
|
+
# All shared infrastructure (json_ok, json_err, COLONY_DATA_DIR, DATA_DIR,
|
|
10
|
+
# error constants) is available when sourced.
|
|
11
|
+
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# _curation_sentinel
|
|
14
|
+
# Check health of all memory stores.
|
|
15
|
+
# Usage: curation-sentinel
|
|
16
|
+
#
|
|
17
|
+
# Output: json_ok with {checks:[{store,status,details}], healthy:N, issues:N}
|
|
18
|
+
# ============================================================================
|
|
19
|
+
_curation_sentinel() {
|
|
20
|
+
local cs_data_dir="${COLONY_DATA_DIR:-${DATA_DIR:-}}"
|
|
21
|
+
if [[ -z "$cs_data_dir" ]]; then
|
|
22
|
+
json_err "$E_VALIDATION_FAILED" "curation-sentinel: COLONY_DATA_DIR is not set"
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
local cs_checks_json="[]"
|
|
26
|
+
local cs_healthy=0
|
|
27
|
+
local cs_issues=0
|
|
28
|
+
|
|
29
|
+
# Helper: append a check entry
|
|
30
|
+
# status "healthy" increments healthy; "optional_missing" is neutral;
|
|
31
|
+
# all other statuses (missing, corrupt, empty) increment issues.
|
|
32
|
+
_cs_add_check() {
|
|
33
|
+
local store="$1"
|
|
34
|
+
local status="$2"
|
|
35
|
+
local details="$3"
|
|
36
|
+
|
|
37
|
+
cs_checks_json=$(echo "$cs_checks_json" | jq \
|
|
38
|
+
--arg store "$store" \
|
|
39
|
+
--arg status "$status" \
|
|
40
|
+
--arg details "$details" \
|
|
41
|
+
'. += [{store:$store, status:$status, details:$details}]')
|
|
42
|
+
|
|
43
|
+
if [[ "$status" == "healthy" ]]; then
|
|
44
|
+
cs_healthy=$(( cs_healthy + 1 ))
|
|
45
|
+
elif [[ "$status" != "optional_missing" ]]; then
|
|
46
|
+
cs_issues=$(( cs_issues + 1 ))
|
|
47
|
+
fi
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Helper: check a JSON file
|
|
51
|
+
_cs_check_json_file() {
|
|
52
|
+
local store="$1"
|
|
53
|
+
local filepath="$2"
|
|
54
|
+
local required="${3:-false}"
|
|
55
|
+
|
|
56
|
+
if [[ ! -f "$filepath" ]]; then
|
|
57
|
+
if [[ "$required" == "true" ]]; then
|
|
58
|
+
_cs_add_check "$store" "missing" "Required file not found: $filepath"
|
|
59
|
+
else
|
|
60
|
+
_cs_add_check "$store" "optional_missing" "Optional file not found: $filepath"
|
|
61
|
+
fi
|
|
62
|
+
return
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
if [[ ! -s "$filepath" ]]; then
|
|
66
|
+
_cs_add_check "$store" "empty" "File exists but is empty: $filepath"
|
|
67
|
+
return
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
if ! jq empty "$filepath" 2>/dev/null; then
|
|
71
|
+
_cs_add_check "$store" "corrupt" "File contains invalid JSON: $filepath"
|
|
72
|
+
return
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
_cs_add_check "$store" "healthy" "OK"
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
# 1. learning-observations.json
|
|
79
|
+
_cs_check_json_file "learning-observations" \
|
|
80
|
+
"$cs_data_dir/learning-observations.json" "false"
|
|
81
|
+
|
|
82
|
+
# 2. instincts.json (optional)
|
|
83
|
+
_cs_check_json_file "instincts" \
|
|
84
|
+
"$cs_data_dir/instincts.json" "false"
|
|
85
|
+
|
|
86
|
+
# 3. instinct-graph.json (optional)
|
|
87
|
+
_cs_check_json_file "instinct-graph" \
|
|
88
|
+
"$cs_data_dir/instinct-graph.json" "false"
|
|
89
|
+
|
|
90
|
+
# 4. event-bus.jsonl (optional — check last line if exists)
|
|
91
|
+
local eb_file="$cs_data_dir/event-bus.jsonl"
|
|
92
|
+
if [[ ! -f "$eb_file" ]]; then
|
|
93
|
+
_cs_add_check "event-bus" "optional_missing" "Optional file not found: $eb_file"
|
|
94
|
+
elif [[ ! -s "$eb_file" ]]; then
|
|
95
|
+
_cs_add_check "event-bus" "healthy" "File is empty (no events)"
|
|
96
|
+
else
|
|
97
|
+
local last_line
|
|
98
|
+
last_line=$(tail -1 "$eb_file")
|
|
99
|
+
if echo "$last_line" | jq empty 2>/dev/null; then
|
|
100
|
+
_cs_add_check "event-bus" "healthy" "OK"
|
|
101
|
+
else
|
|
102
|
+
_cs_add_check "event-bus" "corrupt" "Last line is not valid JSON"
|
|
103
|
+
fi
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# 5. pheromones.json (required)
|
|
107
|
+
_cs_check_json_file "pheromones" \
|
|
108
|
+
"$cs_data_dir/pheromones.json" "true"
|
|
109
|
+
|
|
110
|
+
# 6. COLONY_STATE.json (required)
|
|
111
|
+
_cs_check_json_file "COLONY_STATE" \
|
|
112
|
+
"$cs_data_dir/COLONY_STATE.json" "true"
|
|
113
|
+
|
|
114
|
+
json_ok "$(jq -nc \
|
|
115
|
+
--argjson checks "$cs_checks_json" \
|
|
116
|
+
--argjson healthy "$cs_healthy" \
|
|
117
|
+
--argjson issues "$cs_issues" \
|
|
118
|
+
'{checks:$checks, healthy:$healthy, issues:$issues}')"
|
|
119
|
+
}
|