aether-colony 5.1.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 +122 -42
- package/.aether/commands/colonize.yaml +4 -0
- package/.aether/commands/council.yaml +205 -0
- package/.aether/commands/init.yaml +46 -13
- package/.aether/commands/insert-phase.yaml +4 -0
- package/.aether/commands/plan.yaml +53 -2
- package/.aether/commands/quick.yaml +104 -0
- package/.aether/commands/resume-colony.yaml +6 -4
- package/.aether/commands/resume.yaml +9 -0
- package/.aether/commands/run.yaml +37 -1
- package/.aether/commands/seal.yaml +9 -0
- package/.aether/commands/status.yaml +45 -1
- package/.aether/docs/command-playbooks/build-full.md +2 -1
- package/.aether/docs/command-playbooks/build-prep.md +2 -1
- package/.aether/docs/command-playbooks/continue-full.md +1 -0
- package/.aether/docs/command-playbooks/continue-verify.md +1 -0
- package/.aether/utils/council.sh +425 -0
- package/.aether/utils/error-handler.sh +3 -3
- package/.aether/utils/flag.sh +23 -12
- package/.aether/utils/hive.sh +2 -2
- package/.aether/utils/immune.sh +508 -0
- package/.aether/utils/learning.sh +2 -2
- package/.aether/utils/midden.sh +178 -0
- package/.aether/utils/queen.sh +29 -17
- package/.aether/utils/session.sh +264 -0
- package/.aether/utils/spawn-tree.sh +7 -7
- package/.aether/utils/spawn.sh +2 -2
- package/.aether/utils/state-api.sh +191 -1
- package/.claude/commands/ant/colonize.md +2 -0
- package/.claude/commands/ant/council.md +205 -0
- package/.claude/commands/ant/init.md +46 -13
- package/.claude/commands/ant/insert-phase.md +4 -0
- package/.claude/commands/ant/plan.md +27 -1
- package/.claude/commands/ant/quick.md +100 -0
- package/.claude/commands/ant/resume-colony.md +3 -2
- package/.claude/commands/ant/resume.md +9 -0
- package/.claude/commands/ant/run.md +37 -1
- package/.claude/commands/ant/seal.md +9 -0
- package/.claude/commands/ant/status.md +45 -1
- package/.opencode/commands/ant/colonize.md +2 -0
- package/.opencode/commands/ant/council.md +143 -0
- package/.opencode/commands/ant/init.md +46 -13
- package/.opencode/commands/ant/insert-phase.md +4 -0
- package/.opencode/commands/ant/plan.md +26 -1
- package/.opencode/commands/ant/quick.md +91 -0
- package/.opencode/commands/ant/resume-colony.md +3 -2
- package/.opencode/commands/ant/resume.md +9 -0
- package/.opencode/commands/ant/run.md +37 -1
- package/.opencode/commands/ant/status.md +2 -0
- package/CHANGELOG.md +90 -0
- package/README.md +23 -0
- package/package.json +10 -2
|
@@ -341,6 +341,15 @@ body_claude: |
|
|
|
341
341
|
bash .aether/aether-utils.sh activity-log "MODIFIED" "Queen" "Colony sealed - wisdom review completed"
|
|
342
342
|
```
|
|
343
343
|
|
|
344
|
+
### Step 4.4: Checkpoint State
|
|
345
|
+
|
|
346
|
+
Before modifying colony state, create a rolling backup:
|
|
347
|
+
|
|
348
|
+
Run using the Bash tool with description "Checkpointing colony state before seal...":
|
|
349
|
+
```bash
|
|
350
|
+
bash .aether/aether-utils.sh state-checkpoint "pre-seal" 2>/dev/null || echo "Warning: State checkpoint failed -- continuing without backup" >&2
|
|
351
|
+
```
|
|
352
|
+
|
|
344
353
|
### Step 4.5: Increment Colony Version
|
|
345
354
|
|
|
346
355
|
Before writing the Crowned Anthill milestone, increment `colony_version` in COLONY_STATE.json.
|
|
@@ -300,9 +300,53 @@ body: |
|
|
|
300
300
|
```
|
|
301
301
|
|
|
302
302
|
{{#claude}}
|
|
303
|
-
**
|
|
303
|
+
**Colony Vital Signs:**
|
|
304
304
|
After the Memory Health table, run:
|
|
305
305
|
```bash
|
|
306
|
+
bash .aether/aether-utils.sh colony-vital-signs
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
Extract from JSON result:
|
|
310
|
+
- build_velocity.phases_per_day and build_velocity.trend
|
|
311
|
+
- error_rate.errors_per_day and error_rate.status
|
|
312
|
+
- signal_health.active_count and signal_health.status
|
|
313
|
+
- memory_pressure.instinct_count and memory_pressure.status
|
|
314
|
+
- colony_age_hours
|
|
315
|
+
- overall_health (0-100 score)
|
|
316
|
+
|
|
317
|
+
Map overall_health score to a label:
|
|
318
|
+
- 80-100: "Thriving"
|
|
319
|
+
- 60-79: "Healthy"
|
|
320
|
+
- 40-59: "Stable"
|
|
321
|
+
- 20-39: "Struggling"
|
|
322
|
+
- 0-19: "Critical"
|
|
323
|
+
|
|
324
|
+
Display:
|
|
325
|
+
```
|
|
326
|
+
💓 Colony Vital Signs
|
|
327
|
+
┌─────────────────┬────────────┬─────────────────────────────┐
|
|
328
|
+
│ Vital Sign │ Value │ Status │
|
|
329
|
+
├─────────────────┼────────────┼─────────────────────────────┤
|
|
330
|
+
│ Build Velocity │ {phases_per_day}/d │ {trend} │
|
|
331
|
+
│ Error Rate │ {errors_per_day}/d │ {error_status} │
|
|
332
|
+
│ Signal Health │ {active_count} │ {signal_status} │
|
|
333
|
+
│ Memory Pressure │ {instinct_count} │ {memory_status} │
|
|
334
|
+
│ Colony Age │ {colony_age_hours}h │ │
|
|
335
|
+
├─────────────────┼────────────┼─────────────────────────────┤
|
|
336
|
+
│ Overall Health │ {overall_health}% │ {health_label} │
|
|
337
|
+
└─────────────────┴────────────┴─────────────────────────────┘
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
If the command fails or returns no data, display:
|
|
341
|
+
```
|
|
342
|
+
💓 Colony Vital Signs: No data available
|
|
343
|
+
```
|
|
344
|
+
{{/claude}}
|
|
345
|
+
|
|
346
|
+
{{#claude}}
|
|
347
|
+
**Data Safety:**
|
|
348
|
+
After the Colony Vital Signs panel, run:
|
|
349
|
+
```bash
|
|
306
350
|
bash .aether/aether-utils.sh data-safety-stats
|
|
307
351
|
```
|
|
308
352
|
|
|
@@ -98,7 +98,8 @@ If the command fails (non-zero exit or JSON has ok: false):
|
|
|
98
98
|
If successful:
|
|
99
99
|
1. Parse the state JSON from result field
|
|
100
100
|
2. Check if goal is null - if so: "No colony initialized. Run /ant:init first." and stop
|
|
101
|
-
3.
|
|
101
|
+
3. Check if `milestone` == `"Crowned Anthill"` - if so: "This colony has been sealed. Start a new colony with `/ant:init \"new goal\"`." and stop
|
|
102
|
+
4. Extract current_phase and phase name from plan.phases[current_phase - 1].name
|
|
102
103
|
4. Display brief resumption context:
|
|
103
104
|
```
|
|
104
105
|
🔄 Resuming: Phase X - Name
|
|
@@ -98,7 +98,8 @@ If the command fails (non-zero exit or JSON has ok: false):
|
|
|
98
98
|
If successful:
|
|
99
99
|
1. Parse the state JSON from result field
|
|
100
100
|
2. Check if goal is null - if so: "No colony initialized. Run /ant:init first." and stop
|
|
101
|
-
3.
|
|
101
|
+
3. Check if `milestone` == `"Crowned Anthill"` - if so: "This colony has been sealed. Start a new colony with `/ant:init \"new goal\"`." and stop
|
|
102
|
+
4. Extract current_phase and phase name from plan.phases[current_phase - 1].name
|
|
102
103
|
4. Display brief resumption context:
|
|
103
104
|
```
|
|
104
105
|
🔄 Resuming: Phase X - Name
|
|
@@ -26,6 +26,7 @@ Extract: `goal`, `state`, `current_phase`, `plan.phases`, `errors`, `memory`, `e
|
|
|
26
26
|
|
|
27
27
|
**Validation:**
|
|
28
28
|
- If `goal: null` -> output "No colony initialized. Run /ant:init first." and stop.
|
|
29
|
+
- If `milestone` == `"Crowned Anthill"` -> output "This colony has been sealed. Start a new colony with `/ant:init \"new goal\"`." and stop.
|
|
29
30
|
- If `plan.phases` is empty -> output "No project plan. Run /ant:plan first." and stop.
|
|
30
31
|
|
|
31
32
|
### Step 1.5: Load State and Show Resumption Context
|
|
@@ -26,6 +26,7 @@ Extract: `goal`, `state`, `current_phase`, `plan.phases`, `errors`, `memory`, `e
|
|
|
26
26
|
|
|
27
27
|
**Validation:**
|
|
28
28
|
- If `goal: null` -> output "No colony initialized. Run /ant:init first." and stop.
|
|
29
|
+
- If `milestone` == `"Crowned Anthill"` -> output "This colony has been sealed. Start a new colony with `/ant:init \"new goal\"`." and stop.
|
|
29
30
|
- If `plan.phases` is empty -> output "No project plan. Run /ant:plan first." and stop.
|
|
30
31
|
|
|
31
32
|
### Step 1.5: Load State and Show Resumption Context
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Council deliberation module — Advocate/Challenger/Sage model with spawn budget guards
|
|
3
|
+
# Provides: _council_deliberate, _council_advocate, _council_challenger, _council_sage,
|
|
4
|
+
# _council_history, _council_budget_check
|
|
5
|
+
#
|
|
6
|
+
# These functions are sourced by aether-utils.sh at startup.
|
|
7
|
+
# All shared infrastructure (json_ok, json_err, atomic_write, acquire_lock,
|
|
8
|
+
# release_lock, LOCK_DIR, COLONY_DATA_DIR, error constants) is available.
|
|
9
|
+
# _spawn_can_spawn is available from spawn.sh (sourced before this module).
|
|
10
|
+
|
|
11
|
+
# ---------------------------------------------------------------------------
|
|
12
|
+
# Internal helpers
|
|
13
|
+
# ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
_council_data_dir() {
|
|
16
|
+
echo "$COLONY_DATA_DIR/council"
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
_council_deliberations_file() {
|
|
20
|
+
echo "$COLONY_DATA_DIR/council/deliberations.json"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
_council_ensure_file() {
|
|
24
|
+
local cf
|
|
25
|
+
cf="$(_council_deliberations_file)"
|
|
26
|
+
mkdir -p "$(_council_data_dir)"
|
|
27
|
+
if [[ ! -f "$cf" ]]; then
|
|
28
|
+
printf '%s\n' '{"version":"1.0","deliberations":[]}' > "$cf"
|
|
29
|
+
fi
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# _council_deliberate
|
|
34
|
+
# Usage: council-deliberate --proposal <text> [--budget N] [--depth light|standard|deep]
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
_council_deliberate() {
|
|
37
|
+
local cd_proposal=""
|
|
38
|
+
local cd_budget="3"
|
|
39
|
+
local cd_depth="standard"
|
|
40
|
+
|
|
41
|
+
while [[ $# -gt 0 ]]; do
|
|
42
|
+
case "$1" in
|
|
43
|
+
--proposal) cd_proposal="${2:-}"; shift 2 ;;
|
|
44
|
+
--budget) cd_budget="${2:-3}"; shift 2 ;;
|
|
45
|
+
--depth) cd_depth="${2:-standard}"; shift 2 ;;
|
|
46
|
+
*) shift ;;
|
|
47
|
+
esac
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
if [[ -z "$cd_proposal" ]]; then
|
|
51
|
+
json_err "$E_VALIDATION_FAILED" "council-deliberate requires --proposal"
|
|
52
|
+
return
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
if ! [[ "$cd_budget" =~ ^[0-9]+$ ]]; then
|
|
56
|
+
json_err "$E_VALIDATION_FAILED" "council-deliberate --budget must be a positive integer"
|
|
57
|
+
return
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
local cd_ts
|
|
61
|
+
cd_ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
62
|
+
local cd_unix
|
|
63
|
+
cd_unix=$(date -u +%s)
|
|
64
|
+
local cd_id="delib_${cd_unix}"
|
|
65
|
+
|
|
66
|
+
_council_ensure_file
|
|
67
|
+
|
|
68
|
+
local cd_file
|
|
69
|
+
cd_file="$(_council_deliberations_file)"
|
|
70
|
+
|
|
71
|
+
acquire_lock "$cd_file" 2>/dev/null || true
|
|
72
|
+
# shellcheck disable=SC2064
|
|
73
|
+
trap "release_lock '$cd_file' 2>/dev/null || true" EXIT
|
|
74
|
+
|
|
75
|
+
local cd_updated
|
|
76
|
+
cd_updated=$(jq \
|
|
77
|
+
--arg id "$cd_id" \
|
|
78
|
+
--arg proposal "$cd_proposal" \
|
|
79
|
+
--arg ts "$cd_ts" \
|
|
80
|
+
--argjson budget "$cd_budget" \
|
|
81
|
+
--arg depth "$cd_depth" \
|
|
82
|
+
'.deliberations += [{
|
|
83
|
+
"id": $id,
|
|
84
|
+
"proposal": $proposal,
|
|
85
|
+
"advocate": null,
|
|
86
|
+
"challenger": null,
|
|
87
|
+
"sage": null,
|
|
88
|
+
"budget": $budget,
|
|
89
|
+
"depth": $depth,
|
|
90
|
+
"created_at": $ts,
|
|
91
|
+
"status": "pending"
|
|
92
|
+
}]' "$cd_file") || {
|
|
93
|
+
release_lock "$cd_file" 2>/dev/null || true
|
|
94
|
+
trap - EXIT
|
|
95
|
+
json_err "$E_UNKNOWN" "Failed to write deliberation"
|
|
96
|
+
return
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
atomic_write "$cd_file" "$cd_updated" || {
|
|
100
|
+
release_lock "$cd_file" 2>/dev/null || true
|
|
101
|
+
trap - EXIT
|
|
102
|
+
json_err "$E_UNKNOWN" "Failed to persist deliberation"
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
release_lock "$cd_file" 2>/dev/null || true
|
|
107
|
+
trap - EXIT
|
|
108
|
+
|
|
109
|
+
json_ok "$(jq -n \
|
|
110
|
+
--arg id "$cd_id" \
|
|
111
|
+
--arg proposal "$cd_proposal" \
|
|
112
|
+
--argjson budget "$cd_budget" \
|
|
113
|
+
'{"id":$id,"proposal":$proposal,"status":"pending","budget":$budget}')"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# ---------------------------------------------------------------------------
|
|
117
|
+
# _council_advocate
|
|
118
|
+
# Usage: council-advocate --deliberation-id <id> --argument <text>
|
|
119
|
+
# ---------------------------------------------------------------------------
|
|
120
|
+
_council_advocate() {
|
|
121
|
+
local ca_id=""
|
|
122
|
+
local ca_argument=""
|
|
123
|
+
|
|
124
|
+
while [[ $# -gt 0 ]]; do
|
|
125
|
+
case "$1" in
|
|
126
|
+
--deliberation-id) ca_id="${2:-}"; shift 2 ;;
|
|
127
|
+
--argument) ca_argument="${2:-}"; shift 2 ;;
|
|
128
|
+
*) shift ;;
|
|
129
|
+
esac
|
|
130
|
+
done
|
|
131
|
+
|
|
132
|
+
if [[ -z "$ca_id" ]]; then
|
|
133
|
+
json_err "$E_VALIDATION_FAILED" "council-advocate requires --deliberation-id"
|
|
134
|
+
return
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
if [[ -z "$ca_argument" ]]; then
|
|
138
|
+
json_err "$E_VALIDATION_FAILED" "council-advocate requires --argument"
|
|
139
|
+
return
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
local ca_file
|
|
143
|
+
ca_file="$(_council_deliberations_file)"
|
|
144
|
+
|
|
145
|
+
if [[ ! -f "$ca_file" ]]; then
|
|
146
|
+
json_err "$E_VALIDATION_FAILED" "No deliberations found; run council-deliberate first"
|
|
147
|
+
return
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
local ca_exists
|
|
151
|
+
ca_exists=$(jq -r --arg id "$ca_id" '.deliberations[] | select(.id == $id) | .id' "$ca_file" 2>/dev/null || echo "")
|
|
152
|
+
if [[ -z "$ca_exists" ]]; then
|
|
153
|
+
json_err "$E_VALIDATION_FAILED" "Deliberation not found: $ca_id"
|
|
154
|
+
return
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
acquire_lock "$ca_file" 2>/dev/null || true
|
|
158
|
+
# shellcheck disable=SC2064
|
|
159
|
+
trap "release_lock '$ca_file' 2>/dev/null || true" EXIT
|
|
160
|
+
|
|
161
|
+
local ca_updated
|
|
162
|
+
ca_updated=$(jq \
|
|
163
|
+
--arg id "$ca_id" \
|
|
164
|
+
--arg arg "$ca_argument" \
|
|
165
|
+
'(.deliberations[] | select(.id == $id)).advocate = $arg
|
|
166
|
+
| (.deliberations[] | select(.id == $id)).status = "in_progress"' \
|
|
167
|
+
"$ca_file") || {
|
|
168
|
+
release_lock "$ca_file" 2>/dev/null || true
|
|
169
|
+
trap - EXIT
|
|
170
|
+
json_err "$E_UNKNOWN" "Failed to record advocate argument"
|
|
171
|
+
return
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
atomic_write "$ca_file" "$ca_updated" || {
|
|
175
|
+
release_lock "$ca_file" 2>/dev/null || true
|
|
176
|
+
trap - EXIT
|
|
177
|
+
json_err "$E_UNKNOWN" "Failed to persist advocate argument"
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
release_lock "$ca_file" 2>/dev/null || true
|
|
182
|
+
trap - EXIT
|
|
183
|
+
|
|
184
|
+
json_ok '{"role":"advocate","recorded":true}'
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# ---------------------------------------------------------------------------
|
|
188
|
+
# _council_challenger
|
|
189
|
+
# Usage: council-challenger --deliberation-id <id> --argument <text>
|
|
190
|
+
# ---------------------------------------------------------------------------
|
|
191
|
+
_council_challenger() {
|
|
192
|
+
local cc_id=""
|
|
193
|
+
local cc_argument=""
|
|
194
|
+
|
|
195
|
+
while [[ $# -gt 0 ]]; do
|
|
196
|
+
case "$1" in
|
|
197
|
+
--deliberation-id) cc_id="${2:-}"; shift 2 ;;
|
|
198
|
+
--argument) cc_argument="${2:-}"; shift 2 ;;
|
|
199
|
+
*) shift ;;
|
|
200
|
+
esac
|
|
201
|
+
done
|
|
202
|
+
|
|
203
|
+
if [[ -z "$cc_id" ]]; then
|
|
204
|
+
json_err "$E_VALIDATION_FAILED" "council-challenger requires --deliberation-id"
|
|
205
|
+
return
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
if [[ -z "$cc_argument" ]]; then
|
|
209
|
+
json_err "$E_VALIDATION_FAILED" "council-challenger requires --argument"
|
|
210
|
+
return
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
local cc_file
|
|
214
|
+
cc_file="$(_council_deliberations_file)"
|
|
215
|
+
|
|
216
|
+
if [[ ! -f "$cc_file" ]]; then
|
|
217
|
+
json_err "$E_VALIDATION_FAILED" "No deliberations found; run council-deliberate first"
|
|
218
|
+
return
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
local cc_exists
|
|
222
|
+
cc_exists=$(jq -r --arg id "$cc_id" '.deliberations[] | select(.id == $id) | .id' "$cc_file" 2>/dev/null || echo "")
|
|
223
|
+
if [[ -z "$cc_exists" ]]; then
|
|
224
|
+
json_err "$E_VALIDATION_FAILED" "Deliberation not found: $cc_id"
|
|
225
|
+
return
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
acquire_lock "$cc_file" 2>/dev/null || true
|
|
229
|
+
# shellcheck disable=SC2064
|
|
230
|
+
trap "release_lock '$cc_file' 2>/dev/null || true" EXIT
|
|
231
|
+
|
|
232
|
+
local cc_updated
|
|
233
|
+
cc_updated=$(jq \
|
|
234
|
+
--arg id "$cc_id" \
|
|
235
|
+
--arg arg "$cc_argument" \
|
|
236
|
+
'(.deliberations[] | select(.id == $id)).challenger = $arg
|
|
237
|
+
| (.deliberations[] | select(.id == $id)).status = "in_progress"' \
|
|
238
|
+
"$cc_file") || {
|
|
239
|
+
release_lock "$cc_file" 2>/dev/null || true
|
|
240
|
+
trap - EXIT
|
|
241
|
+
json_err "$E_UNKNOWN" "Failed to record challenger argument"
|
|
242
|
+
return
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
atomic_write "$cc_file" "$cc_updated" || {
|
|
246
|
+
release_lock "$cc_file" 2>/dev/null || true
|
|
247
|
+
trap - EXIT
|
|
248
|
+
json_err "$E_UNKNOWN" "Failed to persist challenger argument"
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
release_lock "$cc_file" 2>/dev/null || true
|
|
253
|
+
trap - EXIT
|
|
254
|
+
|
|
255
|
+
json_ok '{"role":"challenger","recorded":true}'
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
# ---------------------------------------------------------------------------
|
|
259
|
+
# _council_sage
|
|
260
|
+
# Usage: council-sage --deliberation-id <id> --synthesis <text> --recommendation <text>
|
|
261
|
+
# ---------------------------------------------------------------------------
|
|
262
|
+
_council_sage() {
|
|
263
|
+
local cs_id=""
|
|
264
|
+
local cs_synthesis=""
|
|
265
|
+
local cs_recommendation=""
|
|
266
|
+
|
|
267
|
+
while [[ $# -gt 0 ]]; do
|
|
268
|
+
case "$1" in
|
|
269
|
+
--deliberation-id) cs_id="${2:-}"; shift 2 ;;
|
|
270
|
+
--synthesis) cs_synthesis="${2:-}"; shift 2 ;;
|
|
271
|
+
--recommendation) cs_recommendation="${2:-}"; shift 2 ;;
|
|
272
|
+
*) shift ;;
|
|
273
|
+
esac
|
|
274
|
+
done
|
|
275
|
+
|
|
276
|
+
if [[ -z "$cs_id" ]]; then
|
|
277
|
+
json_err "$E_VALIDATION_FAILED" "council-sage requires --deliberation-id"
|
|
278
|
+
return
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
if [[ -z "$cs_synthesis" ]]; then
|
|
282
|
+
json_err "$E_VALIDATION_FAILED" "council-sage requires --synthesis"
|
|
283
|
+
return
|
|
284
|
+
fi
|
|
285
|
+
|
|
286
|
+
if [[ -z "$cs_recommendation" ]]; then
|
|
287
|
+
json_err "$E_VALIDATION_FAILED" "council-sage requires --recommendation"
|
|
288
|
+
return
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
local cs_file
|
|
292
|
+
cs_file="$(_council_deliberations_file)"
|
|
293
|
+
|
|
294
|
+
if [[ ! -f "$cs_file" ]]; then
|
|
295
|
+
json_err "$E_VALIDATION_FAILED" "No deliberations found; run council-deliberate first"
|
|
296
|
+
return
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
local cs_exists
|
|
300
|
+
cs_exists=$(jq -r --arg id "$cs_id" '.deliberations[] | select(.id == $id) | .id' "$cs_file" 2>/dev/null || echo "")
|
|
301
|
+
if [[ -z "$cs_exists" ]]; then
|
|
302
|
+
json_err "$E_VALIDATION_FAILED" "Deliberation not found: $cs_id"
|
|
303
|
+
return
|
|
304
|
+
fi
|
|
305
|
+
|
|
306
|
+
acquire_lock "$cs_file" 2>/dev/null || true
|
|
307
|
+
# shellcheck disable=SC2064
|
|
308
|
+
trap "release_lock '$cs_file' 2>/dev/null || true" EXIT
|
|
309
|
+
|
|
310
|
+
local cs_updated
|
|
311
|
+
cs_updated=$(jq \
|
|
312
|
+
--arg id "$cs_id" \
|
|
313
|
+
--arg synthesis "$cs_synthesis" \
|
|
314
|
+
--arg rec "$cs_recommendation" \
|
|
315
|
+
'(.deliberations[] | select(.id == $id)).sage = {"synthesis": $synthesis, "recommendation": $rec}
|
|
316
|
+
| (.deliberations[] | select(.id == $id)).status = "complete"' \
|
|
317
|
+
"$cs_file") || {
|
|
318
|
+
release_lock "$cs_file" 2>/dev/null || true
|
|
319
|
+
trap - EXIT
|
|
320
|
+
json_err "$E_UNKNOWN" "Failed to record sage synthesis"
|
|
321
|
+
return
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
atomic_write "$cs_file" "$cs_updated" || {
|
|
325
|
+
release_lock "$cs_file" 2>/dev/null || true
|
|
326
|
+
trap - EXIT
|
|
327
|
+
json_err "$E_UNKNOWN" "Failed to persist sage synthesis"
|
|
328
|
+
return
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
release_lock "$cs_file" 2>/dev/null || true
|
|
332
|
+
trap - EXIT
|
|
333
|
+
|
|
334
|
+
json_ok "$(jq -n \
|
|
335
|
+
--arg rec "$cs_recommendation" \
|
|
336
|
+
'{"role":"sage","recommendation":$rec,"deliberation_complete":true}')"
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
# ---------------------------------------------------------------------------
|
|
340
|
+
# _council_history
|
|
341
|
+
# Usage: council-history [--limit N]
|
|
342
|
+
# ---------------------------------------------------------------------------
|
|
343
|
+
_council_history() {
|
|
344
|
+
local ch_limit=""
|
|
345
|
+
|
|
346
|
+
while [[ $# -gt 0 ]]; do
|
|
347
|
+
case "$1" in
|
|
348
|
+
--limit) ch_limit="${2:-}"; shift 2 ;;
|
|
349
|
+
*) shift ;;
|
|
350
|
+
esac
|
|
351
|
+
done
|
|
352
|
+
|
|
353
|
+
local ch_file
|
|
354
|
+
ch_file="$(_council_deliberations_file)"
|
|
355
|
+
|
|
356
|
+
if [[ ! -f "$ch_file" ]]; then
|
|
357
|
+
json_ok '{"total":0,"deliberations":[]}'
|
|
358
|
+
return
|
|
359
|
+
fi
|
|
360
|
+
|
|
361
|
+
local ch_total
|
|
362
|
+
ch_total=$(jq '.deliberations | length' "$ch_file" 2>/dev/null || echo 0)
|
|
363
|
+
|
|
364
|
+
if [[ -n "$ch_limit" ]] && [[ "$ch_limit" =~ ^[0-9]+$ ]]; then
|
|
365
|
+
local ch_result
|
|
366
|
+
ch_result=$(jq \
|
|
367
|
+
--argjson limit "$ch_limit" \
|
|
368
|
+
--argjson total "$ch_total" \
|
|
369
|
+
'{"total":$total,"deliberations":(.deliberations | .[-($limit):])}' \
|
|
370
|
+
"$ch_file" 2>/dev/null) || ch_result='{"total":0,"deliberations":[]}'
|
|
371
|
+
json_ok "$ch_result"
|
|
372
|
+
else
|
|
373
|
+
local ch_result
|
|
374
|
+
ch_result=$(jq \
|
|
375
|
+
--argjson total "$ch_total" \
|
|
376
|
+
'{"total":$total,"deliberations":.deliberations}' \
|
|
377
|
+
"$ch_file" 2>/dev/null) || ch_result='{"total":0,"deliberations":[]}'
|
|
378
|
+
json_ok "$ch_result"
|
|
379
|
+
fi
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
# ---------------------------------------------------------------------------
|
|
383
|
+
# _council_budget_check
|
|
384
|
+
# Usage: council-budget-check [--budget N]
|
|
385
|
+
# ---------------------------------------------------------------------------
|
|
386
|
+
_council_budget_check() {
|
|
387
|
+
local cb_budget="3"
|
|
388
|
+
|
|
389
|
+
while [[ $# -gt 0 ]]; do
|
|
390
|
+
case "$1" in
|
|
391
|
+
--budget) cb_budget="${2:-3}"; shift 2 ;;
|
|
392
|
+
*) shift ;;
|
|
393
|
+
esac
|
|
394
|
+
done
|
|
395
|
+
|
|
396
|
+
if ! [[ "$cb_budget" =~ ^[0-9]+$ ]]; then
|
|
397
|
+
json_err "$E_VALIDATION_FAILED" "council-budget-check --budget must be a positive integer"
|
|
398
|
+
return
|
|
399
|
+
fi
|
|
400
|
+
|
|
401
|
+
# Delegate to spawn-can-spawn at depth 1
|
|
402
|
+
local cb_spawn_result
|
|
403
|
+
cb_spawn_result=$(_spawn_can_spawn 1 2>/dev/null || echo '{"can_spawn":false,"current_total":0,"global_cap":10}')
|
|
404
|
+
|
|
405
|
+
local cb_can
|
|
406
|
+
cb_can=$(echo "$cb_spawn_result" | jq -r '.result.can_spawn // .can_spawn // false' 2>/dev/null || echo "false")
|
|
407
|
+
local cb_current
|
|
408
|
+
cb_current=$(echo "$cb_spawn_result" | jq -r '.result.current_total // .current_total // 0' 2>/dev/null || echo 0)
|
|
409
|
+
local cb_cap
|
|
410
|
+
cb_cap=$(echo "$cb_spawn_result" | jq -r '.result.global_cap // .global_cap // 10' 2>/dev/null || echo 10)
|
|
411
|
+
local cb_remaining=$(( cb_cap - cb_current ))
|
|
412
|
+
[[ $cb_remaining -lt 0 ]] && cb_remaining=0
|
|
413
|
+
|
|
414
|
+
# allowed is true only if spawn is allowed AND remaining >= requested budget
|
|
415
|
+
local cb_allowed="false"
|
|
416
|
+
if [[ "$cb_can" == "true" ]] && [[ $cb_remaining -ge $cb_budget ]]; then
|
|
417
|
+
cb_allowed="true"
|
|
418
|
+
fi
|
|
419
|
+
|
|
420
|
+
json_ok "$(jq -n \
|
|
421
|
+
--argjson allowed "$cb_allowed" \
|
|
422
|
+
--argjson remaining "$cb_remaining" \
|
|
423
|
+
--argjson budget "$cb_budget" \
|
|
424
|
+
'{"allowed":$allowed,"remaining":$remaining,"budget":$budget}')"
|
|
425
|
+
}
|
|
@@ -87,7 +87,7 @@ json_err() {
|
|
|
87
87
|
"$code" "$escaped_message" "$details_json" "$recovery" "$timestamp" >&2
|
|
88
88
|
|
|
89
89
|
# Log to activity.log (best effort)
|
|
90
|
-
if [[ -n "${COLONY_DATA_DIR:-}" ]]; then
|
|
90
|
+
if [[ -n "${COLONY_DATA_DIR:-}" ]] && [[ "${AETHER_TESTING:-}" != "1" ]]; then
|
|
91
91
|
echo "[$timestamp] ERROR $code: $escaped_message" >> "$COLONY_DATA_DIR/activity.log" 2>/dev/null || true
|
|
92
92
|
fi
|
|
93
93
|
|
|
@@ -111,7 +111,7 @@ json_warn() {
|
|
|
111
111
|
"$code" "$escaped_message" "$timestamp"
|
|
112
112
|
|
|
113
113
|
# Log to activity.log (best effort)
|
|
114
|
-
if [[ -n "${COLONY_DATA_DIR:-}" ]]; then
|
|
114
|
+
if [[ -n "${COLONY_DATA_DIR:-}" ]] && [[ "${AETHER_TESTING:-}" != "1" ]]; then
|
|
115
115
|
echo "[$timestamp] WARN $code: $escaped_message" >> "$COLONY_DATA_DIR/activity.log" 2>/dev/null || true
|
|
116
116
|
fi
|
|
117
117
|
}
|
|
@@ -153,7 +153,7 @@ error_handler() {
|
|
|
153
153
|
"$E_BASH_ERROR" "$details" "$(_recovery_default)" "$timestamp" >&2
|
|
154
154
|
|
|
155
155
|
# Log to activity.log (best effort)
|
|
156
|
-
if [[ -n "${COLONY_DATA_DIR:-}" ]]; then
|
|
156
|
+
if [[ -n "${COLONY_DATA_DIR:-}" ]] && [[ "${AETHER_TESTING:-}" != "1" ]]; then
|
|
157
157
|
echo "[$timestamp] ERROR $E_BASH_ERROR: Command failed at line $line_num (exit $exit_code)" >> "$COLONY_DATA_DIR/activity.log" 2>/dev/null || true
|
|
158
158
|
fi
|
|
159
159
|
|
package/.aether/utils/flag.sh
CHANGED
|
@@ -204,22 +204,33 @@ _flag_list() {
|
|
|
204
204
|
exit 0
|
|
205
205
|
fi
|
|
206
206
|
|
|
207
|
-
#
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
fi
|
|
213
|
-
|
|
214
|
-
if [[ -n "$filter_type" ]]; then
|
|
215
|
-
jq_filter+=" | [.[] | select(.type == \"$filter_type\")]"
|
|
207
|
+
# Validate filter_phase as numeric (safe for --argjson)
|
|
208
|
+
if [[ -n "$filter_phase" && "$filter_phase" =~ ^[0-9]+$ ]]; then
|
|
209
|
+
phase_num="$filter_phase"
|
|
210
|
+
else
|
|
211
|
+
phase_num=""
|
|
216
212
|
fi
|
|
217
213
|
|
|
218
|
-
|
|
219
|
-
|
|
214
|
+
# Build jq command with --arg to prevent filter injection
|
|
215
|
+
# filter_type is passed via --arg (safe string interpolation in jq)
|
|
216
|
+
# phase_num is passed via --argjson (numeric-only, pre-validated)
|
|
217
|
+
if [[ -n "$phase_num" ]]; then
|
|
218
|
+
result=$(jq --arg ft "$filter_type" --argjson ph "$phase_num" --argjson all "$show_all" '
|
|
219
|
+
.flags
|
|
220
|
+
| (if $all | not then [.[] | select(.resolved_at == null)] else . end)
|
|
221
|
+
| (if $ft != "" then [.[] | select(.type == $ft)] else . end)
|
|
222
|
+
| (if $ph != null then [.[] | select(.phase == $ph or .phase == null)] else . end)
|
|
223
|
+
| {flags: ., count: length}
|
|
224
|
+
' "$flags_file")
|
|
225
|
+
else
|
|
226
|
+
result=$(jq --arg ft "$filter_type" --argjson all "$show_all" '
|
|
227
|
+
.flags
|
|
228
|
+
| (if $all | not then [.[] | select(.resolved_at == null)] else . end)
|
|
229
|
+
| (if $ft != "" then [.[] | select(.type == $ft)] else . end)
|
|
230
|
+
| {flags: ., count: length}
|
|
231
|
+
' "$flags_file")
|
|
220
232
|
fi
|
|
221
233
|
|
|
222
|
-
result=$(jq "{flags: ($jq_filter), count: ($jq_filter | length)}" "$flags_file")
|
|
223
234
|
json_ok "$result"
|
|
224
235
|
}
|
|
225
236
|
|
package/.aether/utils/hive.sh
CHANGED
|
@@ -306,14 +306,14 @@ _hive_read() {
|
|
|
306
306
|
--argjson limit "$hr_limit" '
|
|
307
307
|
.entries
|
|
308
308
|
| map(
|
|
309
|
-
select((.confidence | tonumber) >= $min_conf)
|
|
309
|
+
select(((.confidence // 0) | tonumber) >= $min_conf)
|
|
310
310
|
| if ($domain_filter | length) > 0 then
|
|
311
311
|
select(
|
|
312
312
|
[.domain_tags[] as $dt | $domain_filter[] | select(. == $dt)] | length > 0
|
|
313
313
|
)
|
|
314
314
|
else . end
|
|
315
315
|
)
|
|
316
|
-
| sort_by(-(.confidence | tonumber), -.validated_count)
|
|
316
|
+
| sort_by(-((.confidence // 0) | tonumber), -.validated_count)
|
|
317
317
|
| { total_matched: length, entries: .[:$limit], returned_ids: [.[:$limit][].id] }
|
|
318
318
|
' "$hr_wisdom_file" 2>/dev/null) || {
|
|
319
319
|
json_ok '{"entries":[],"total_matched":0,"fallback":"filter_error"}'
|