aether-colony 5.3.1 → 5.3.3
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/build.yaml +35 -0
- package/.aether/commands/entomb.yaml +1 -1
- package/.aether/commands/init.yaml +29 -12
- package/.aether/commands/oracle.yaml +70 -0
- package/.aether/commands/patrol.yaml +2 -2
- package/.aether/commands/run.yaml +3 -3
- package/.aether/commands/swarm.yaml +1 -1
- package/.aether/docs/command-playbooks/build-complete.md +41 -8
- package/.aether/docs/command-playbooks/build-full.md +7 -7
- package/.aether/docs/command-playbooks/build-prep.md +1 -1
- package/.aether/docs/command-playbooks/continue-advance.md +33 -0
- package/.aether/docs/command-playbooks/continue-finalize.md +15 -1
- package/.aether/docs/command-playbooks/continue-full.md +15 -1
- package/.aether/docs/source-of-truth-map.md +10 -10
- package/.aether/docs/structural-learning-stack.md +283 -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/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/entomb.md +1 -1
- package/.claude/commands/ant/init.md +29 -12
- package/.claude/commands/ant/oracle.md +35 -0
- package/.claude/commands/ant/patrol.md +2 -2
- package/.claude/commands/ant/run.md +3 -3
- package/.claude/commands/ant/swarm.md +1 -1
- package/.opencode/commands/ant/build.md +35 -0
- package/.opencode/commands/ant/init.md +29 -12
- package/.opencode/commands/ant/oracle.md +35 -0
- package/.opencode/commands/ant/patrol.md +2 -2
- package/.opencode/commands/ant/run.md +3 -3
- package/CHANGELOG.md +83 -0
- package/README.md +34 -37
- package/bin/lib/update-transaction.js +8 -3
- 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,347 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Trust Scoring utility functions — Aether Structural Learning Stack
|
|
3
|
+
# Provides: _trust_calculate, _trust_decay, _trust_tier
|
|
4
|
+
#
|
|
5
|
+
# These functions are sourced by aether-utils.sh at startup.
|
|
6
|
+
# All shared infrastructure (json_ok, json_err, json_warn, atomic_write,
|
|
7
|
+
# DATA_DIR, COLONY_DATA_DIR, SCRIPT_DIR, error constants) is available.
|
|
8
|
+
#
|
|
9
|
+
# This is a pure calculation module — no state is read or written.
|
|
10
|
+
# All functions accept --flag <value> arguments and return JSON via json_ok.
|
|
11
|
+
|
|
12
|
+
# ============================================================================
|
|
13
|
+
# _trust_calculate
|
|
14
|
+
# Calculate a weighted trust score from source, evidence, and activity inputs.
|
|
15
|
+
#
|
|
16
|
+
# Weights: source 40%, evidence 35%, activity 25%
|
|
17
|
+
# Activity uses a 60-day half-life decay from days_since_last_use.
|
|
18
|
+
# Floor: score is never below 0.2.
|
|
19
|
+
#
|
|
20
|
+
# Usage: trust-calculate --source <type> --evidence <type> --days-since <N>
|
|
21
|
+
#
|
|
22
|
+
# Source types and weights:
|
|
23
|
+
# user_feedback 1.0
|
|
24
|
+
# error_resolution 0.9
|
|
25
|
+
# success_pattern 0.8
|
|
26
|
+
# observation 0.6
|
|
27
|
+
# heuristic 0.4
|
|
28
|
+
#
|
|
29
|
+
# Evidence types and weights:
|
|
30
|
+
# test_verified 1.0
|
|
31
|
+
# multi_phase 0.9
|
|
32
|
+
# single_phase 0.7
|
|
33
|
+
# anecdotal 0.4
|
|
34
|
+
#
|
|
35
|
+
# Output: {score, source_score, evidence_score, activity_score, tier}
|
|
36
|
+
# ============================================================================
|
|
37
|
+
_trust_calculate() {
|
|
38
|
+
local source_type=""
|
|
39
|
+
local evidence_type=""
|
|
40
|
+
local days_since=""
|
|
41
|
+
|
|
42
|
+
# Parse --flag value arguments
|
|
43
|
+
while [[ $# -gt 0 ]]; do
|
|
44
|
+
case "$1" in
|
|
45
|
+
--source)
|
|
46
|
+
source_type="${2:-}"
|
|
47
|
+
shift 2
|
|
48
|
+
;;
|
|
49
|
+
--evidence)
|
|
50
|
+
evidence_type="${2:-}"
|
|
51
|
+
shift 2
|
|
52
|
+
;;
|
|
53
|
+
--days-since)
|
|
54
|
+
days_since="${2:-}"
|
|
55
|
+
shift 2
|
|
56
|
+
;;
|
|
57
|
+
*)
|
|
58
|
+
json_err "$E_VALIDATION_FAILED" "Usage: trust-calculate --source <type> --evidence <type> --days-since <N>"
|
|
59
|
+
return
|
|
60
|
+
;;
|
|
61
|
+
esac
|
|
62
|
+
done
|
|
63
|
+
|
|
64
|
+
[[ -z "$source_type" || -z "$evidence_type" || -z "$days_since" ]] && \
|
|
65
|
+
json_err "$E_VALIDATION_FAILED" "Usage: trust-calculate --source <type> --evidence <type> --days-since <N>"
|
|
66
|
+
|
|
67
|
+
# Map source type to score
|
|
68
|
+
local source_score
|
|
69
|
+
case "$source_type" in
|
|
70
|
+
user_feedback) source_score="1.0" ;;
|
|
71
|
+
error_resolution) source_score="0.9" ;;
|
|
72
|
+
success_pattern) source_score="0.8" ;;
|
|
73
|
+
observation) source_score="0.6" ;;
|
|
74
|
+
heuristic) source_score="0.4" ;;
|
|
75
|
+
*)
|
|
76
|
+
json_err "$E_VALIDATION_FAILED" "Unknown source type: $source_type. Valid: user_feedback, error_resolution, success_pattern, observation, heuristic"
|
|
77
|
+
return
|
|
78
|
+
;;
|
|
79
|
+
esac
|
|
80
|
+
|
|
81
|
+
# Map evidence type to score
|
|
82
|
+
local evidence_score
|
|
83
|
+
case "$evidence_type" in
|
|
84
|
+
test_verified) evidence_score="1.0" ;;
|
|
85
|
+
multi_phase) evidence_score="0.9" ;;
|
|
86
|
+
single_phase) evidence_score="0.7" ;;
|
|
87
|
+
anecdotal) evidence_score="0.4" ;;
|
|
88
|
+
*)
|
|
89
|
+
json_err "$E_VALIDATION_FAILED" "Unknown evidence type: $evidence_type. Valid: test_verified, multi_phase, single_phase, anecdotal"
|
|
90
|
+
return
|
|
91
|
+
;;
|
|
92
|
+
esac
|
|
93
|
+
|
|
94
|
+
# Validate days_since is a non-negative integer
|
|
95
|
+
if ! [[ "$days_since" =~ ^[0-9]+$ ]]; then
|
|
96
|
+
json_err "$E_VALIDATION_FAILED" "--days-since must be a non-negative integer, got: $days_since"
|
|
97
|
+
return
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# Calculate activity score using 60-day half-life decay
|
|
101
|
+
# activity = 0.5 ^ (days / 60)
|
|
102
|
+
local activity_score
|
|
103
|
+
activity_score=$(_trust_halflife_decay "1.0" "$days_since" "60")
|
|
104
|
+
|
|
105
|
+
# Weighted formula: 0.4 * source + 0.35 * evidence + 0.25 * activity
|
|
106
|
+
local raw_score
|
|
107
|
+
if command -v bc &>/dev/null; then
|
|
108
|
+
raw_score=$(echo "scale=6; 0.4 * $source_score + 0.35 * $evidence_score + 0.25 * $activity_score" | bc)
|
|
109
|
+
else
|
|
110
|
+
raw_score=$(awk "BEGIN{printf \"%.6f\", 0.4 * $source_score + 0.35 * $evidence_score + 0.25 * $activity_score}")
|
|
111
|
+
fi
|
|
112
|
+
|
|
113
|
+
# Apply floor of 0.2
|
|
114
|
+
local score
|
|
115
|
+
score=$(_trust_apply_floor "$raw_score" "0.2")
|
|
116
|
+
|
|
117
|
+
# Derive tier from final score
|
|
118
|
+
local tier
|
|
119
|
+
tier=$(_trust_score_to_tier "$score")
|
|
120
|
+
|
|
121
|
+
json_ok "$(jq -n \
|
|
122
|
+
--argjson score "$score" \
|
|
123
|
+
--argjson source_score "$source_score" \
|
|
124
|
+
--argjson evidence_score "$evidence_score" \
|
|
125
|
+
--argjson activity_score "$activity_score" \
|
|
126
|
+
--arg tier "$tier" \
|
|
127
|
+
'{
|
|
128
|
+
score: $score,
|
|
129
|
+
source_score: $source_score,
|
|
130
|
+
evidence_score: $evidence_score,
|
|
131
|
+
activity_score: $activity_score,
|
|
132
|
+
tier: $tier
|
|
133
|
+
}')"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# ============================================================================
|
|
137
|
+
# _trust_decay
|
|
138
|
+
# Apply time-based half-life decay to an existing trust score.
|
|
139
|
+
# The score never drops below the floor of 0.2.
|
|
140
|
+
#
|
|
141
|
+
# Formula: decayed = max(0.2, score * (0.5 ^ (days / 60)))
|
|
142
|
+
#
|
|
143
|
+
# Usage: trust-decay --score <float> --days <N>
|
|
144
|
+
# Output: {original, decayed, days, half_life: 60}
|
|
145
|
+
# ============================================================================
|
|
146
|
+
_trust_decay() {
|
|
147
|
+
local score=""
|
|
148
|
+
local days=""
|
|
149
|
+
|
|
150
|
+
while [[ $# -gt 0 ]]; do
|
|
151
|
+
case "$1" in
|
|
152
|
+
--score)
|
|
153
|
+
score="${2:-}"
|
|
154
|
+
shift 2
|
|
155
|
+
;;
|
|
156
|
+
--days)
|
|
157
|
+
days="${2:-}"
|
|
158
|
+
shift 2
|
|
159
|
+
;;
|
|
160
|
+
*)
|
|
161
|
+
json_err "$E_VALIDATION_FAILED" "Usage: trust-decay --score <float> --days <N>"
|
|
162
|
+
return
|
|
163
|
+
;;
|
|
164
|
+
esac
|
|
165
|
+
done
|
|
166
|
+
|
|
167
|
+
[[ -z "$score" || -z "$days" ]] && \
|
|
168
|
+
json_err "$E_VALIDATION_FAILED" "Usage: trust-decay --score <float> --days <N>"
|
|
169
|
+
|
|
170
|
+
# Validate score is a positive number
|
|
171
|
+
if ! [[ "$score" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
|
|
172
|
+
json_err "$E_VALIDATION_FAILED" "--score must be a non-negative number, got: $score"
|
|
173
|
+
return
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
# Validate days is a non-negative integer
|
|
177
|
+
if ! [[ "$days" =~ ^[0-9]+$ ]]; then
|
|
178
|
+
json_err "$E_VALIDATION_FAILED" "--days must be a non-negative integer, got: $days"
|
|
179
|
+
return
|
|
180
|
+
fi
|
|
181
|
+
|
|
182
|
+
local decayed
|
|
183
|
+
decayed=$(_trust_halflife_decay "$score" "$days" "60")
|
|
184
|
+
local final
|
|
185
|
+
final=$(_trust_apply_floor "$decayed" "0.2")
|
|
186
|
+
|
|
187
|
+
json_ok "$(jq -n \
|
|
188
|
+
--argjson original "$score" \
|
|
189
|
+
--argjson decayed "$final" \
|
|
190
|
+
--argjson days "$days" \
|
|
191
|
+
--argjson half_life 60 \
|
|
192
|
+
'{
|
|
193
|
+
original: $original,
|
|
194
|
+
decayed: $decayed,
|
|
195
|
+
days: $days,
|
|
196
|
+
half_life: $half_life
|
|
197
|
+
}')"
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
# ============================================================================
|
|
201
|
+
# _trust_tier
|
|
202
|
+
# Map a trust score to a named tier.
|
|
203
|
+
#
|
|
204
|
+
# Tier table:
|
|
205
|
+
# 0.90–1.00 canonical (index 0) — proven across multiple contexts
|
|
206
|
+
# 0.80–0.89 trusted (index 1) — reliable with strong evidence
|
|
207
|
+
# 0.70–0.79 established (index 2) — consistent but limited evidence
|
|
208
|
+
# 0.60–0.69 emerging (index 3) — promising, needs more validation
|
|
209
|
+
# 0.45–0.59 provisional (index 4) — early stage, minimal evidence
|
|
210
|
+
# 0.30–0.44 suspect (index 5) — weak evidence or conflicting signals
|
|
211
|
+
# 0.20–0.29 dormant (index 6) — inactive, near floor
|
|
212
|
+
#
|
|
213
|
+
# Usage: trust-tier --score <float>
|
|
214
|
+
# Output: {score, tier, tier_index}
|
|
215
|
+
# ============================================================================
|
|
216
|
+
_trust_tier() {
|
|
217
|
+
local score=""
|
|
218
|
+
|
|
219
|
+
while [[ $# -gt 0 ]]; do
|
|
220
|
+
case "$1" in
|
|
221
|
+
--score)
|
|
222
|
+
score="${2:-}"
|
|
223
|
+
shift 2
|
|
224
|
+
;;
|
|
225
|
+
*)
|
|
226
|
+
json_err "$E_VALIDATION_FAILED" "Usage: trust-tier --score <float>"
|
|
227
|
+
return
|
|
228
|
+
;;
|
|
229
|
+
esac
|
|
230
|
+
done
|
|
231
|
+
|
|
232
|
+
[[ -z "$score" ]] && \
|
|
233
|
+
json_err "$E_VALIDATION_FAILED" "Usage: trust-tier --score <float>"
|
|
234
|
+
|
|
235
|
+
# Validate score is a non-negative number
|
|
236
|
+
if ! [[ "$score" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
|
|
237
|
+
json_err "$E_VALIDATION_FAILED" "--score must be a non-negative number, got: $score"
|
|
238
|
+
return
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
local tier
|
|
242
|
+
local tier_index
|
|
243
|
+
tier=$(_trust_score_to_tier "$score")
|
|
244
|
+
tier_index=$(_trust_tier_to_index "$tier")
|
|
245
|
+
|
|
246
|
+
json_ok "$(jq -n \
|
|
247
|
+
--argjson score "$score" \
|
|
248
|
+
--arg tier "$tier" \
|
|
249
|
+
--argjson tier_index "$tier_index" \
|
|
250
|
+
'{
|
|
251
|
+
score: $score,
|
|
252
|
+
tier: $tier,
|
|
253
|
+
tier_index: $tier_index
|
|
254
|
+
}')"
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
# ============================================================================
|
|
258
|
+
# Internal helpers — not exposed as subcommands
|
|
259
|
+
# ============================================================================
|
|
260
|
+
|
|
261
|
+
# _trust_halflife_decay: compute score * (0.5 ^ (days / half_life))
|
|
262
|
+
# Usage: _trust_halflife_decay <score> <days> <half_life>
|
|
263
|
+
# Outputs the decayed value as a float string
|
|
264
|
+
_trust_halflife_decay() {
|
|
265
|
+
local score="$1"
|
|
266
|
+
local days="$2"
|
|
267
|
+
local half_life="$3"
|
|
268
|
+
|
|
269
|
+
if command -v bc &>/dev/null; then
|
|
270
|
+
echo "scale=6; $score * e(l(0.5) * ($days / $half_life))" | bc -l
|
|
271
|
+
else
|
|
272
|
+
awk "BEGIN{printf \"%.6f\", $score * (0.5 ^ ($days / $half_life))}"
|
|
273
|
+
fi
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
# _trust_apply_floor: return max(floor, value)
|
|
277
|
+
# Usage: _trust_apply_floor <value> <floor>
|
|
278
|
+
# Outputs the floored value as a float string
|
|
279
|
+
_trust_apply_floor() {
|
|
280
|
+
local value="$1"
|
|
281
|
+
local floor="$2"
|
|
282
|
+
|
|
283
|
+
if command -v bc &>/dev/null; then
|
|
284
|
+
local cmp
|
|
285
|
+
cmp=$(echo "$value < $floor" | bc -l)
|
|
286
|
+
if [[ "$cmp" == "1" ]]; then
|
|
287
|
+
echo "$floor"
|
|
288
|
+
else
|
|
289
|
+
echo "$value"
|
|
290
|
+
fi
|
|
291
|
+
else
|
|
292
|
+
awk "BEGIN{v=$value; f=$floor; printf \"%.6f\", (v < f ? f : v)}"
|
|
293
|
+
fi
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
# _trust_score_to_tier: map a numeric score to a tier name
|
|
297
|
+
# Usage: _trust_score_to_tier <score>
|
|
298
|
+
# Outputs the tier name string
|
|
299
|
+
_trust_score_to_tier() {
|
|
300
|
+
local score="$1"
|
|
301
|
+
|
|
302
|
+
if command -v bc &>/dev/null; then
|
|
303
|
+
if [[ $(echo "$score >= 0.90" | bc -l) == "1" ]]; then
|
|
304
|
+
echo "canonical"
|
|
305
|
+
elif [[ $(echo "$score >= 0.80" | bc -l) == "1" ]]; then
|
|
306
|
+
echo "trusted"
|
|
307
|
+
elif [[ $(echo "$score >= 0.70" | bc -l) == "1" ]]; then
|
|
308
|
+
echo "established"
|
|
309
|
+
elif [[ $(echo "$score >= 0.60" | bc -l) == "1" ]]; then
|
|
310
|
+
echo "emerging"
|
|
311
|
+
elif [[ $(echo "$score >= 0.45" | bc -l) == "1" ]]; then
|
|
312
|
+
echo "provisional"
|
|
313
|
+
elif [[ $(echo "$score >= 0.30" | bc -l) == "1" ]]; then
|
|
314
|
+
echo "suspect"
|
|
315
|
+
else
|
|
316
|
+
echo "dormant"
|
|
317
|
+
fi
|
|
318
|
+
else
|
|
319
|
+
awk "BEGIN{
|
|
320
|
+
s = $score
|
|
321
|
+
if (s >= 0.90) print \"canonical\"
|
|
322
|
+
else if (s >= 0.80) print \"trusted\"
|
|
323
|
+
else if (s >= 0.70) print \"established\"
|
|
324
|
+
else if (s >= 0.60) print \"emerging\"
|
|
325
|
+
else if (s >= 0.45) print \"provisional\"
|
|
326
|
+
else if (s >= 0.30) print \"suspect\"
|
|
327
|
+
else print \"dormant\"
|
|
328
|
+
}"
|
|
329
|
+
fi
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
# _trust_tier_to_index: map tier name to integer index
|
|
333
|
+
# Usage: _trust_tier_to_index <tier_name>
|
|
334
|
+
# Outputs the index as an integer string
|
|
335
|
+
_trust_tier_to_index() {
|
|
336
|
+
local tier="$1"
|
|
337
|
+
case "$tier" in
|
|
338
|
+
canonical) echo "0" ;;
|
|
339
|
+
trusted) echo "1" ;;
|
|
340
|
+
established) echo "2" ;;
|
|
341
|
+
emerging) echo "3" ;;
|
|
342
|
+
provisional) echo "4" ;;
|
|
343
|
+
suspect) echo "5" ;;
|
|
344
|
+
dormant) echo "6" ;;
|
|
345
|
+
*) echo "-1" ;;
|
|
346
|
+
esac
|
|
347
|
+
}
|
|
@@ -187,3 +187,100 @@ _worktree_cleanup() {
|
|
|
187
187
|
|
|
188
188
|
json_ok "$result"
|
|
189
189
|
}
|
|
190
|
+
|
|
191
|
+
# _worktree_merge
|
|
192
|
+
# Merges a worktree branch back to the target branch with safety checks.
|
|
193
|
+
#
|
|
194
|
+
# Usage: _worktree_merge --branch <branch-name> [--target <target-branch>] [--force]
|
|
195
|
+
# Returns JSON: {ok:true, result:{merged, branch, target, sha, commits_merged}}
|
|
196
|
+
_worktree_merge() {
|
|
197
|
+
local branch=""
|
|
198
|
+
local target=""
|
|
199
|
+
local force=false
|
|
200
|
+
|
|
201
|
+
while [[ $# -gt 0 ]]; do
|
|
202
|
+
case "$1" in
|
|
203
|
+
--branch) branch="${2:-}"; shift 2 ;;
|
|
204
|
+
--target) target="${2:-}"; shift 2 ;;
|
|
205
|
+
--force) force=true; shift ;;
|
|
206
|
+
*) shift ;;
|
|
207
|
+
esac
|
|
208
|
+
done
|
|
209
|
+
|
|
210
|
+
if [[ -z "$branch" ]]; then
|
|
211
|
+
json_err "$E_VALIDATION_FAILED" "Usage: worktree-merge --branch <branch-name> [--target <target-branch>]"
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
# Sanitize branch name
|
|
215
|
+
if [[ "$branch" == *..* ]] || [[ "$branch" == */* ]] || [[ "$branch" == *\\* ]]; then
|
|
216
|
+
json_err "$E_VALIDATION_FAILED" "Branch name must not contain '..', '/', or backslashes"
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
local worktree_dir="$WORKTREE_BASE_DIR/$branch"
|
|
220
|
+
|
|
221
|
+
# Default target to current branch
|
|
222
|
+
if [[ -z "$target" ]]; then
|
|
223
|
+
target=$(git -C "$AETHER_ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")
|
|
224
|
+
fi
|
|
225
|
+
|
|
226
|
+
# Safety check 1: worktree must exist
|
|
227
|
+
if [[ ! -d "$worktree_dir" ]]; then
|
|
228
|
+
json_err "$E_RESOURCE_NOT_FOUND" "No worktree found for branch '$branch'"
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
# Safety check 2: worktree must have committed changes (unless --force)
|
|
232
|
+
if [[ "$force" == "false" ]]; then
|
|
233
|
+
local dirty_count
|
|
234
|
+
dirty_count=$(git -C "$worktree_dir" status --porcelain 2>/dev/null \
|
|
235
|
+
| grep -v '\.aether/' \
|
|
236
|
+
| wc -l | tr -d ' ') || dirty_count=0
|
|
237
|
+
if [[ "$dirty_count" -gt 0 ]]; then
|
|
238
|
+
json_err "$E_VALIDATION_FAILED" "Worktree '$branch' has $dirty_count uncommitted changes. Commit or stash before merging."
|
|
239
|
+
fi
|
|
240
|
+
fi
|
|
241
|
+
|
|
242
|
+
# Safety check 3: worktree must have commits ahead of target
|
|
243
|
+
local ahead_count
|
|
244
|
+
ahead_count=$(git -C "$AETHER_ROOT" rev-list --count "${target}..${branch}" 2>/dev/null || echo "0")
|
|
245
|
+
if [[ "$ahead_count" -eq 0 ]]; then
|
|
246
|
+
# Nothing to merge — just clean up
|
|
247
|
+
_worktree_cleanup --branch "$branch" --force
|
|
248
|
+
return $?
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
# Safety check 4: check for merge conflicts (dry run)
|
|
252
|
+
local conflict_check
|
|
253
|
+
conflict_check=$(git -C "$AETHER_ROOT" merge-tree $(git -C "$AETHER_ROOT" merge-base "$target" "$branch") "$target" "$branch" 2>/dev/null | grep -c "changed in both" || echo "0")
|
|
254
|
+
if [[ "$conflict_check" -gt 0 ]] && [[ "$force" == "false" ]]; then
|
|
255
|
+
json_err "$E_VALIDATION_FAILED" "Merge would produce $conflict_check conflict(s). Resolve manually or use --force."
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
# Perform the merge
|
|
259
|
+
local merge_sha
|
|
260
|
+
if ! git -C "$AETHER_ROOT" merge "$branch" --no-edit --no-ff -m "merge: worktree branch $branch into $target" >/dev/null 2>&1; then
|
|
261
|
+
# Merge failed — abort
|
|
262
|
+
git -C "$AETHER_ROOT" merge --abort 2>/dev/null || true
|
|
263
|
+
json_err "$E_GIT_ERROR" "Merge of '$branch' into '$target' failed. Aborted."
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
merge_sha=$(git -C "$AETHER_ROOT" rev-parse HEAD 2>/dev/null || echo "unknown")
|
|
267
|
+
|
|
268
|
+
# Export pheromones from the worktree branch before cleanup
|
|
269
|
+
if [[ -f "$worktree_dir/.aether/data/pheromones.json" ]]; then
|
|
270
|
+
mkdir -p "$AETHER_ROOT/.aether/exchange"
|
|
271
|
+
cp "$worktree_dir/.aether/data/pheromones.json" \
|
|
272
|
+
"$AETHER_ROOT/.aether/exchange/pheromone-branch-export.json" 2>/dev/null || true
|
|
273
|
+
fi
|
|
274
|
+
|
|
275
|
+
# Cleanup the worktree
|
|
276
|
+
_worktree_cleanup --branch "$branch" --force 2>/dev/null || true
|
|
277
|
+
|
|
278
|
+
local result
|
|
279
|
+
result=$(jq -n \
|
|
280
|
+
--arg branch "$branch" \
|
|
281
|
+
--arg target "$target" \
|
|
282
|
+
--arg sha "$merge_sha" \
|
|
283
|
+
--argjson ahead "$ahead_count" \
|
|
284
|
+
'{merged: true, branch: $branch, target: $target, sha: $sha, commits_merged: $ahead}')
|
|
285
|
+
json_ok "$result"
|
|
286
|
+
}
|
|
@@ -444,7 +444,7 @@ Write the result to .aether/HANDOFF.md using the Write tool.
|
|
|
444
444
|
Display:
|
|
445
445
|
```
|
|
446
446
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
447
|
-
|
|
447
|
+
⚰️ C O L O N Y E N T O M B E D
|
|
448
448
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
449
449
|
|
|
450
450
|
Entombed v{colony_version}
|
|
@@ -41,7 +41,6 @@ Do not touch during init:
|
|
|
41
41
|
- .aether/dreams/ (user notes)
|
|
42
42
|
- .aether/chambers/ (archived colonies)
|
|
43
43
|
- .env* files
|
|
44
|
-
- .claude/settings.json
|
|
45
44
|
- .github/workflows/
|
|
46
45
|
</read_only>
|
|
47
46
|
|
|
@@ -156,7 +155,7 @@ Strip `(Colony: ...)` suffixes using sed. If grep finds nothing, variables remai
|
|
|
156
155
|
Display a brief header:
|
|
157
156
|
```
|
|
158
157
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
159
|
-
|
|
158
|
+
🥚 A E T H E R C O L O N Y I N I T
|
|
160
159
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
161
160
|
```
|
|
162
161
|
|
|
@@ -470,30 +469,48 @@ Display "Import skipped. Starting fresh colony." and proceed to Step 8.
|
|
|
470
469
|
**If xml_import_available is false (no chambers, no XML, or no xmllint):**
|
|
471
470
|
|
|
472
471
|
Skip silently -- proceed directly to Step 8 without any mention of import (per D-11).
|
|
472
|
+
### Step 7.5: Install Clash Detection Hook
|
|
473
|
+
|
|
474
|
+
If `.aether/utils/clash-detect.sh` exists, run:
|
|
475
|
+
|
|
476
|
+
```bash
|
|
477
|
+
bash .aether/aether-utils.sh clash-setup --install 2>/dev/null || true
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
This installs the PreToolUse hook that prevents conflicting edits across worktrees.
|
|
481
|
+
Non-blocking — if it fails, init continues normally.
|
|
482
|
+
|
|
483
|
+
Also configure the merge driver for package-lock.json:
|
|
484
|
+
|
|
485
|
+
```bash
|
|
486
|
+
git config merge.lockfile.driver "bash .aether/utils/merge-driver-lockfile.sh %O %A %B" 2>/dev/null || true
|
|
487
|
+
git config merge.lockfile.name "npm lockfile auto-merge" 2>/dev/null || true
|
|
488
|
+
```
|
|
489
|
+
|
|
473
490
|
### Step 8: Display Result
|
|
474
491
|
|
|
475
492
|
Display the success header and result block:
|
|
476
493
|
|
|
477
494
|
```
|
|
478
495
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
479
|
-
|
|
496
|
+
🥚 A E T H E R C O L O N Y
|
|
480
497
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
481
498
|
|
|
482
|
-
Queen has set the colony's intention
|
|
499
|
+
👑 Queen has set the colony's intention
|
|
483
500
|
|
|
484
501
|
"{approved_intent}"
|
|
485
502
|
|
|
486
|
-
Colony Status: READY
|
|
503
|
+
🟢 Colony Status: READY
|
|
487
504
|
|
|
488
|
-
{If re-init: " Mode: Re-init (charter updated, state preserved)"}
|
|
489
|
-
{If fresh and seeded_count > 0: " Hive wisdom: {seeded_count} cross-colony pattern(s) seeded into QUEEN.md"}
|
|
505
|
+
{If re-init: " 🔄 Mode: Re-init (charter updated, state preserved)"}
|
|
506
|
+
{If fresh and seeded_count > 0: " 🧠 Hive wisdom: {seeded_count} cross-colony pattern(s) seeded into QUEEN.md"}
|
|
490
507
|
|
|
491
|
-
State persisted -- safe to /clear, then run /ant:plan
|
|
508
|
+
💾 State persisted -- safe to /clear, then run /ant:plan
|
|
492
509
|
|
|
493
510
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
494
|
-
|
|
511
|
+
🐜 Next Up
|
|
495
512
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
496
|
-
/ant:plan Generate execution plan
|
|
497
|
-
/ant:status Check colony state
|
|
498
|
-
/ant:focus Set initial focus
|
|
513
|
+
/ant:plan 📊 Generate execution plan
|
|
514
|
+
/ant:status 📋 Check colony state
|
|
515
|
+
/ant:focus 🎯 Set initial focus
|
|
499
516
|
```
|
|
@@ -278,6 +278,41 @@ Describe the research topic in detail. The more specific, the better the Oracle'
|
|
|
278
278
|
|
|
279
279
|
(The user will type their topic via the "Other" free-text option.)
|
|
280
280
|
|
|
281
|
+
**Question 1.5: Research Brief — Formulate and Approve**
|
|
282
|
+
|
|
283
|
+
Take the user's raw topic (from `$ARGUMENTS` or Question 1) and reformulate it into a structured research brief. The user may have typed casual natural language — your job is to sharpen it into a clear, well-scoped research prompt that will produce better results.
|
|
284
|
+
|
|
285
|
+
Display the brief for approval:
|
|
286
|
+
|
|
287
|
+
```
|
|
288
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
289
|
+
🔮 R E S E A R C H B R I E F
|
|
290
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
291
|
+
|
|
292
|
+
**Topic:** {reformulated topic — clear, specific, actionable}
|
|
293
|
+
**Core Question:** {the single most important question this research should answer}
|
|
294
|
+
**Context:** {what we already know from the codebase or user input}
|
|
295
|
+
**Success Criteria:** {what a good answer looks like — what would make this research useful}
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
Guidelines for formulation:
|
|
299
|
+
- Turn vague topics into specific ones ("auth stuff" → "Authentication architecture: session-based vs token-based for this Node.js API")
|
|
300
|
+
- Add codebase context if relevant (tech stack, existing patterns)
|
|
301
|
+
- Make the core question answerable — not open-ended philosophy
|
|
302
|
+
- Keep success criteria concrete ("A recommendation with trade-offs" not "understand everything")
|
|
303
|
+
|
|
304
|
+
Then ask the user to approve:
|
|
305
|
+
|
|
306
|
+
```
|
|
307
|
+
Does this capture what you're looking for? (approve / edit)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
- If the user approves: use the reformulated topic as the research topic going forward
|
|
311
|
+
- If the user edits: incorporate their changes, display the updated brief, and ask again
|
|
312
|
+
- Max 2 revision rounds (same as init). After 2, ask for final approval or cancel.
|
|
313
|
+
|
|
314
|
+
The approved **Topic** from the brief becomes the topic used in all subsequent steps (state.json, plan.json, research-plan.md).
|
|
315
|
+
|
|
281
316
|
**Question 2: Research Template**
|
|
282
317
|
|
|
283
318
|
```
|
|
@@ -79,7 +79,7 @@ Extract from COLONY_STATE.json:
|
|
|
79
79
|
Display audit header:
|
|
80
80
|
```
|
|
81
81
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
82
|
-
|
|
82
|
+
🔍 C O L O N Y A U D I T
|
|
83
83
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
84
84
|
|
|
85
85
|
Goal: {goal}
|
|
@@ -501,7 +501,7 @@ Display the formatted audit summary:
|
|
|
501
501
|
|
|
502
502
|
```
|
|
503
503
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
504
|
-
|
|
504
|
+
📋 A U D I T R E S U L T S
|
|
505
505
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
506
506
|
|
|
507
507
|
Goal: {goal}
|
|
@@ -49,7 +49,7 @@ If `--dry-run`: read COLONY_STATE.json, list remaining incomplete phases
|
|
|
49
49
|
(applying `--max-phases` cap), display the plan, then stop without executing.
|
|
50
50
|
|
|
51
51
|
```
|
|
52
|
-
━━━ A U T O P I L O T P R E V I E W ━━━
|
|
52
|
+
━━━ 🤖 A U T O P I L O T P R E V I E W ━━━
|
|
53
53
|
Goal: {goal} | Current: Phase {N} | Remaining: {count} | Max: {max or "all"}
|
|
54
54
|
|
|
55
55
|
Phase {id}: {name} ({task_count} tasks) -> build -> continue -> advance
|
|
@@ -187,7 +187,7 @@ If `--continue` flag was passed: skip this check entirely (user dismissed replan
|
|
|
187
187
|
If `result.should_replan == true`: **PAUSE** with replan suggestion banner:
|
|
188
188
|
|
|
189
189
|
```
|
|
190
|
-
━━━ R E P L A N S U G G E S T E D ━━━
|
|
190
|
+
━━━ 🔄 R E P L A N S U G G E S T E D ━━━
|
|
191
191
|
Phases auto-completed: {N} | Learnings accumulated: {learnings_since_last}
|
|
192
192
|
|
|
193
193
|
The colony has completed {N} phases since the last plan review.
|
|
@@ -205,7 +205,7 @@ If `result.should_replan == false`: proceed normally (no pause).
|
|
|
205
205
|
### Step 6: Final Summary
|
|
206
206
|
|
|
207
207
|
```
|
|
208
|
-
━━━ A U T O P I L O T C O M P L E T E ━━━
|
|
208
|
+
━━━ ✅ A U T O P I L O T C O M P L E T E ━━━
|
|
209
209
|
Phases completed: {N} | Elapsed: {Xm Ys} | Now at: Phase {current}
|
|
210
210
|
|
|
211
211
|
{all complete} -> Colony goal achieved! Run /ant:seal
|
|
@@ -238,7 +238,7 @@ Cross-compare all findings:
|
|
|
238
238
|
Rank fix options:
|
|
239
239
|
```
|
|
240
240
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
241
|
-
|
|
241
|
+
🏆 S O L U T I O N R A N K I N G
|
|
242
242
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
243
243
|
|
|
244
244
|
#1 [0.85 confidence] {best solution}
|
|
@@ -879,6 +879,41 @@ bash .aether/aether-utils.sh activity-log "FLAG" "Watcher" "Created blocker: {is
|
|
|
879
879
|
|
|
880
880
|
This ensures verification failures are persisted as blockers that survive context resets. Chaos Ant findings are flagged in Step 5.7.
|
|
881
881
|
|
|
882
|
+
### Stage Audit Gate (Pre-Synthesis Check)
|
|
883
|
+
|
|
884
|
+
**This gate runs before Step 5.9. All build stages must have completed before synthesizing results.**
|
|
885
|
+
|
|
886
|
+
Verify that each of the following stages has a recorded completion status:
|
|
887
|
+
|
|
888
|
+
| Stage | Steps | Required |
|
|
889
|
+
|-------|-------|----------|
|
|
890
|
+
| Builder waves | Steps 5.1–5.3 | At least 1 worker completed (status "completed") |
|
|
891
|
+
| Watcher | Steps 5.4–5.5 | Watcher returned a result (any status) |
|
|
892
|
+
| Chaos | Steps 5.6–5.7 | Chaos ant returned a result (any status) |
|
|
893
|
+
|
|
894
|
+
Check by reviewing the in-memory worker results accumulated during Steps 5.1–5.7:
|
|
895
|
+
- `builder_results` — results from all builder wave tasks
|
|
896
|
+
- `watcher_result` — result from Step 5.5
|
|
897
|
+
- `chaos_result` — result from Step 5.7
|
|
898
|
+
|
|
899
|
+
**If any stage result is absent (stage did not run or returned no result):**
|
|
900
|
+
- HALT — do not proceed to Step 5.9
|
|
901
|
+
- Display:
|
|
902
|
+
```
|
|
903
|
+
Stage Audit FAILED — cannot synthesize results.
|
|
904
|
+
|
|
905
|
+
Missing or incomplete stages:
|
|
906
|
+
{list each missing stage: "Builders — no results recorded" / "Watcher — did not complete" / "Chaos — did not complete"}
|
|
907
|
+
|
|
908
|
+
Recovery options:
|
|
909
|
+
1. Re-run /ant:build to restart this phase
|
|
910
|
+
2. Run /ant:flags to review blockers
|
|
911
|
+
3. Run /ant:swarm to auto-repair failed tasks
|
|
912
|
+
```
|
|
913
|
+
- Return `{"status": "failed", "summary": "Stage audit failed — stages did not complete"}` and stop.
|
|
914
|
+
|
|
915
|
+
**If all stages have results (even if some workers failed):** proceed to Step 5.9.
|
|
916
|
+
|
|
882
917
|
### Step 5.9: Synthesize Results
|
|
883
918
|
|
|
884
919
|
**This step runs after all worker tasks have completed (Builders, Watcher, Chaos).**
|