@matt82198/aesop 0.1.0-rc.1 → 0.2.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/CHANGELOG.md +101 -2
- package/README.md +66 -15
- package/bin/cli.js +164 -41
- package/daemons/run-watchdog.sh +16 -4
- package/daemons/selfheal.sh +231 -0
- package/docs/ANY-REPO.md +427 -0
- package/docs/CONTRIBUTING.md +72 -0
- package/docs/DEMO.md +334 -0
- package/docs/INSTALL.md +70 -1
- package/docs/QUICKSTART.md +80 -0
- package/docs/README.md +33 -3
- package/docs/TEAM-STATE.md +540 -0
- package/driver/CLAUDE.md +148 -0
- package/driver/README.md +383 -0
- package/driver/aesop.config.example.json +80 -0
- package/driver/agent_driver.py +355 -0
- package/driver/backend_config.py +253 -0
- package/driver/claude_code_driver.py +198 -0
- package/driver/codex_driver.py +626 -0
- package/driver/openai_compatible_driver.py +249 -0
- package/driver/openai_transport.py +146 -0
- package/driver/verification_policy.py +75 -0
- package/driver/wave_bridge.py +246 -0
- package/driver/wave_loop.py +1041 -0
- package/hooks/pre-push-policy.sh +109 -28
- package/mcp/server.mjs +320 -4
- package/package.json +23 -15
- package/skills/CLAUDE.md +132 -2
- package/skills/buildsystem/SKILL.md +330 -0
- package/skills/buildsystem/wave-flat-dispatch.template.mjs +658 -0
- package/skills/fleet/SKILL.md +113 -0
- package/skills/power/SKILL.md +246 -131
- package/state_store/__init__.py +2 -1
- package/state_store/api.py +19 -4
- package/state_store/coordination.py +209 -0
- package/state_store/identity.py +51 -0
- package/state_store/store.py +185 -73
- package/templates/wave-presets/data.json +65 -0
- package/templates/wave-presets/library.json +65 -0
- package/templates/wave-presets/saas.json +64 -0
- package/tools/audit_report.py +388 -0
- package/tools/bench_runner.py +100 -3
- package/tools/ci_merge_wait.py +256 -35
- package/tools/ci_workflow_lint.py +430 -0
- package/tools/claudemd_drift.py +394 -0
- package/tools/claudemd_lint.py +359 -0
- package/tools/common.py +39 -3
- package/tools/cost_ceiling.py +63 -31
- package/tools/cost_econ.py +480 -0
- package/tools/defect_escape.py +252 -0
- package/tools/doctor.js +1 -1
- package/tools/eod_sweep.py +58 -0
- package/tools/fleet.js +260 -0
- package/tools/fleet_ledger.py +209 -7
- package/tools/git_identity_check.py +315 -0
- package/tools/health-score.js +40 -0
- package/tools/health_score.py +361 -0
- package/tools/metrics_gate.py +13 -4
- package/tools/mutation_test.py +401 -0
- package/tools/portability_check.py +206 -0
- package/tools/reconcile.py +7 -4
- package/tools/secret_scan.py +137 -50
- package/tools/self_stats.py +20 -0
- package/tools/transcript_digest.py +380 -0
- package/tools/verify_activity_filter.py +437 -0
- package/tools/verify_agent_inspector.py +2 -0
- package/tools/verify_dash.py +2 -0
- package/tools/verify_dispatch_panel.py +301 -0
- package/tools/verify_failure_drilldown.py +188 -0
- package/tools/verify_prboard.py +2 -0
- package/tools/verify_scorecards.py +281 -0
- package/tools/verify_submit_encoding.py +2 -0
- package/tools/verify_ui_trio.py +409 -0
- package/tools/verify_wave_telemetry.py +268 -0
- package/tools/wave_backlog_analyzer.py +490 -0
- package/tools/wave_ledger_hook.py +150 -0
- package/tools/wave_preflight.py +512 -0
- package/tools/wave_resume.py +215 -0
- package/tools/wave_templates.py +215 -0
- package/ui/agents.py +68 -14
- package/ui/collectors.py +0 -55
- package/ui/config.py +7 -2
- package/ui/cost.py +231 -12
- package/ui/handler.py +183 -0
- package/ui/quality_scorecard.py +232 -0
- package/ui/wave_audit_tail.py +213 -0
- package/ui/wave_dispatch.py +280 -0
- package/ui/wave_failure.py +288 -0
- package/ui/wave_gantt.py +152 -0
- package/ui/wave_reasoning_tail.py +176 -0
- package/ui/wave_telemetry.py +377 -0
- package/ui/web/dist/assets/index-BGbgw2Nh.js +9 -0
- package/ui/web/dist/assets/index-DqZLgwNg.css +1 -0
- package/ui/web/dist/index.html +2 -2
- package/bin/CLAUDE.md +0 -76
- package/daemons/CLAUDE.md +0 -36
- package/dash/CLAUDE.md +0 -32
- package/docs/archive/README.md +0 -3
- package/docs/archive/spikes/tiered-cognition/ACTIVATION.md +0 -125
- package/docs/archive/spikes/tiered-cognition/DESIGN.md +0 -287
- package/docs/archive/spikes/tiered-cognition/FINDINGS.md +0 -113
- package/docs/archive/spikes/tiered-cognition/README.md +0 -27
- package/docs/archive/spikes/tiered-cognition/aesop-cognition.example.md +0 -32
- package/docs/archive/spikes/tiered-cognition/force-model-policy.merged.mjs +0 -673
- package/docs/archive/spikes/tiered-cognition/strip-tools-hook.mjs +0 -434
- package/hooks/CLAUDE.md +0 -89
- package/mcp/CLAUDE.md +0 -213
- package/monitor/CLAUDE.md +0 -40
- package/scan/CLAUDE.md +0 -30
- package/state_store/CLAUDE.md +0 -39
- package/tools/CLAUDE.md +0 -79
- package/ui/CLAUDE.md +0 -127
- package/ui/web/dist/assets/index-0qQYnvMC.js +0 -9
- package/ui/web/dist/assets/index-BdIlFieV.css +0 -1
package/daemons/run-watchdog.sh
CHANGED
|
@@ -25,8 +25,8 @@ elif command -v python >/dev/null 2>&1; then
|
|
|
25
25
|
PYTHON_EXE="python"
|
|
26
26
|
fi
|
|
27
27
|
|
|
28
|
-
# Resolve conductor3 directory (sibling of AESOP_ROOT)
|
|
29
|
-
CONDUCTOR_ROOT="$(dirname "$AESOP_ROOT")/conductor3"
|
|
28
|
+
# Resolve conductor3 directory (sibling of AESOP_ROOT); env var override for portability
|
|
29
|
+
CONDUCTOR_ROOT="${CONDUCTOR_ROOT:-$(dirname "$AESOP_ROOT")/conductor3}"
|
|
30
30
|
MONITOR_HB_FILE="$CONDUCTOR_ROOT/monitor/.monitor-heartbeat"
|
|
31
31
|
MONITOR_HB_STALE_THRESHOLD=600
|
|
32
32
|
|
|
@@ -59,7 +59,11 @@ acquire_lock() {
|
|
|
59
59
|
# Check if both required files exist
|
|
60
60
|
if [ -f "$lock_dir/timestamp" ] && [ -f "$lock_dir/pid" ] && [ -n "$lock_pid" ]; then
|
|
61
61
|
# Both files present and pid is readable - use timestamp for stale check
|
|
62
|
-
local
|
|
62
|
+
local lock_ts=$(cat "$lock_dir/timestamp" 2>/dev/null)
|
|
63
|
+
local lock_mtime=${lock_ts:-0}
|
|
64
|
+
if [ -z "$lock_ts" ]; then
|
|
65
|
+
echo "empty/corrupt lock timestamp — treating lock as stale" >&2
|
|
66
|
+
fi
|
|
63
67
|
local now=$(date +%s)
|
|
64
68
|
local lock_age=$((now - lock_mtime))
|
|
65
69
|
|
|
@@ -87,7 +91,11 @@ acquire_lock() {
|
|
|
87
91
|
|
|
88
92
|
# Case 2: no pid file, but timestamp exists and is old (definitely abandoned)
|
|
89
93
|
if [ $should_reclaim -eq 0 ] && [ -f "$lock_dir/timestamp" ]; then
|
|
90
|
-
local
|
|
94
|
+
local lock_ts=$(cat "$lock_dir/timestamp" 2>/dev/null)
|
|
95
|
+
local lock_mtime=${lock_ts:-0}
|
|
96
|
+
if [ -z "$lock_ts" ]; then
|
|
97
|
+
echo "empty/corrupt lock timestamp — treating lock as stale" >&2
|
|
98
|
+
fi
|
|
91
99
|
local now=$(date +%s)
|
|
92
100
|
local lock_age=$((now - lock_mtime))
|
|
93
101
|
if [ "$lock_age" -gt "$stale_threshold" ]; then
|
|
@@ -125,6 +133,7 @@ release_lock() {
|
|
|
125
133
|
}
|
|
126
134
|
|
|
127
135
|
# Check monitor heartbeat staleness; log to fleet-backup.log if missing or >600s old
|
|
136
|
+
# Gracefully skips if CONDUCTOR_ROOT doesn't exist (portability for non-conductor deployments)
|
|
128
137
|
check_monitor_staleness() {
|
|
129
138
|
local hb_file="$1"
|
|
130
139
|
local stale_threshold="$2"
|
|
@@ -132,6 +141,9 @@ check_monitor_staleness() {
|
|
|
132
141
|
if [ -z "$hb_file" ] || [ -z "$log_file" ]; then
|
|
133
142
|
return
|
|
134
143
|
fi
|
|
144
|
+
if [ ! -d "$(dirname "$hb_file")" ]; then
|
|
145
|
+
return
|
|
146
|
+
fi
|
|
135
147
|
if [ ! -f "$hb_file" ]; then
|
|
136
148
|
echo "[$(date '+%F %T')] SIGNAL: monitor heartbeat missing ($hb_file)" >> "$log_file"
|
|
137
149
|
return
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Self-healing fleet supervisor: detects stale sibling heartbeats and restarts daemons.
|
|
3
|
+
# Monitors run-watchdog.sh and run-monitor.sh with safe, idempotent recovery actions.
|
|
4
|
+
# Usage: selfheal.sh [--once]
|
|
5
|
+
#
|
|
6
|
+
# Configuration: export AESOP_ROOT=/path/to/aesop before running (defaults to script directory's parent).
|
|
7
|
+
# Testing: export AESOP_SELFHEAL_SKIP_RESTART=1 to skip actual daemon restarts (test-only flag).
|
|
8
|
+
|
|
9
|
+
set -u
|
|
10
|
+
|
|
11
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
12
|
+
AESOP_ROOT="${AESOP_ROOT:-$(dirname "$SCRIPT_DIR")}"
|
|
13
|
+
CONDUCTOR_ROOT="${CONDUCTOR_ROOT:-$(dirname "$AESOP_ROOT")/conductor3}"
|
|
14
|
+
MODE="${1:-daemon}"
|
|
15
|
+
|
|
16
|
+
SELFHEAL_LOCK_DIR="$AESOP_ROOT/state/.selfheal-lock"
|
|
17
|
+
SELFHEAL_LOG="$AESOP_ROOT/state/SELFHEAL.log"
|
|
18
|
+
SELFHEAL_STALE_THRESHOLD=600
|
|
19
|
+
|
|
20
|
+
WATCHDOG_HB="$AESOP_ROOT/state/.watchdog-heartbeat"
|
|
21
|
+
WATCHDOG_STALE_THRESHOLD=600
|
|
22
|
+
|
|
23
|
+
MONITOR_HB="$CONDUCTOR_ROOT/monitor/.monitor-heartbeat"
|
|
24
|
+
MONITOR_STALE_THRESHOLD=600
|
|
25
|
+
|
|
26
|
+
ts() {
|
|
27
|
+
date '+%Y-%m-%d %H:%M:%S'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
log_heal() {
|
|
31
|
+
local msg="$1"
|
|
32
|
+
echo "[$(ts)] $msg" | tee -a "$SELFHEAL_LOG"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
ensure_log_dir() {
|
|
36
|
+
mkdir -p "$(dirname "$SELFHEAL_LOG")" 2>/dev/null || true
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
acquire_lock() {
|
|
40
|
+
local lock_dir="$1"
|
|
41
|
+
local stale_threshold="$2"
|
|
42
|
+
|
|
43
|
+
mkdir -p "$(dirname "$lock_dir")" 2>/dev/null || true
|
|
44
|
+
|
|
45
|
+
if mkdir "$lock_dir" 2>/dev/null; then
|
|
46
|
+
if ! date +%s > "$lock_dir/timestamp" 2>/dev/null || ! echo $$ > "$lock_dir/pid" 2>/dev/null; then
|
|
47
|
+
rm -rf "$lock_dir" 2>/dev/null || true
|
|
48
|
+
return 1
|
|
49
|
+
fi
|
|
50
|
+
return 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
if [ -d "$lock_dir" ]; then
|
|
54
|
+
local lock_pid=""
|
|
55
|
+
if [ -f "$lock_dir/pid" ]; then
|
|
56
|
+
lock_pid=$(cat "$lock_dir/pid" 2>/dev/null)
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
if [ -f "$lock_dir/timestamp" ] && [ -f "$lock_dir/pid" ] && [ -n "$lock_pid" ]; then
|
|
60
|
+
local lock_ts=$(cat "$lock_dir/timestamp" 2>/dev/null)
|
|
61
|
+
local lock_mtime=${lock_ts:-0}
|
|
62
|
+
local now=$(date +%s)
|
|
63
|
+
local lock_age=$((now - lock_mtime))
|
|
64
|
+
|
|
65
|
+
if [ "$lock_age" -gt "$stale_threshold" ]; then
|
|
66
|
+
rm -rf "$lock_dir" 2>/dev/null || true
|
|
67
|
+
if mkdir "$lock_dir" 2>/dev/null; then
|
|
68
|
+
date +%s > "$lock_dir/timestamp" 2>/dev/null || true
|
|
69
|
+
echo $$ > "$lock_dir/pid" 2>/dev/null || true
|
|
70
|
+
return 2
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
else
|
|
74
|
+
local should_reclaim=0
|
|
75
|
+
if [ -f "$lock_dir/pid" ] && [ -n "$lock_pid" ]; then
|
|
76
|
+
if ! kill -0 "$lock_pid" 2>/dev/null; then
|
|
77
|
+
should_reclaim=1
|
|
78
|
+
fi
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
if [ $should_reclaim -eq 0 ] && [ -f "$lock_dir/timestamp" ]; then
|
|
82
|
+
local lock_ts=$(cat "$lock_dir/timestamp" 2>/dev/null)
|
|
83
|
+
local lock_mtime=${lock_ts:-0}
|
|
84
|
+
local now=$(date +%s)
|
|
85
|
+
local lock_age=$((now - lock_mtime))
|
|
86
|
+
|
|
87
|
+
if [ "$lock_age" -gt "$stale_threshold" ]; then
|
|
88
|
+
should_reclaim=1
|
|
89
|
+
fi
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
if [ $should_reclaim -eq 1 ]; then
|
|
93
|
+
rm -rf "$lock_dir" 2>/dev/null || true
|
|
94
|
+
if mkdir "$lock_dir" 2>/dev/null; then
|
|
95
|
+
date +%s > "$lock_dir/timestamp" 2>/dev/null || true
|
|
96
|
+
echo $$ > "$lock_dir/pid" 2>/dev/null || true
|
|
97
|
+
return 2
|
|
98
|
+
fi
|
|
99
|
+
fi
|
|
100
|
+
fi
|
|
101
|
+
fi
|
|
102
|
+
|
|
103
|
+
return 1
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
release_lock() {
|
|
107
|
+
local lock_dir="$1"
|
|
108
|
+
rm -rf "$lock_dir" 2>/dev/null || true
|
|
109
|
+
if [ -d "$lock_dir" ]; then
|
|
110
|
+
ensure_log_dir
|
|
111
|
+
log_heal "WARN: release_lock: failed to remove lock dir: $lock_dir (may be in-use on Windows)"
|
|
112
|
+
fi
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
is_heartbeat_stale() {
|
|
116
|
+
local hb_file="$1"
|
|
117
|
+
local stale_threshold="$2"
|
|
118
|
+
|
|
119
|
+
if [ ! -f "$hb_file" ]; then
|
|
120
|
+
return 0
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
local hb_ts=$(cat "$hb_file" 2>/dev/null)
|
|
124
|
+
if [ -z "$hb_ts" ] || ! [[ "$hb_ts" =~ ^[0-9]+$ ]]; then
|
|
125
|
+
return 0
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
local now=$(date +%s)
|
|
129
|
+
local age=$((now - hb_ts))
|
|
130
|
+
|
|
131
|
+
if [ "$age" -gt "$stale_threshold" ]; then
|
|
132
|
+
return 0
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
return 1
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
restart_watchdog() {
|
|
139
|
+
ensure_log_dir
|
|
140
|
+
|
|
141
|
+
if is_heartbeat_stale "$WATCHDOG_HB" "$WATCHDOG_STALE_THRESHOLD"; then
|
|
142
|
+
if [ -z "${AESOP_SELFHEAL_SKIP_RESTART:-}" ]; then
|
|
143
|
+
log_heal "ACTION: run-watchdog heartbeat stale, restarting..."
|
|
144
|
+
if command -v bash >/dev/null 2>&1; then
|
|
145
|
+
bash "$AESOP_ROOT/daemons/run-watchdog.sh" >/dev/null 2>&1 &
|
|
146
|
+
log_heal "RESTART watchdog: spawned (PID: $!)"
|
|
147
|
+
else
|
|
148
|
+
log_heal "WARN: Cannot restart watchdog, bash not found"
|
|
149
|
+
fi
|
|
150
|
+
else
|
|
151
|
+
log_heal "DRY-RUN: watchdog heartbeat stale (would restart)"
|
|
152
|
+
fi
|
|
153
|
+
return 0
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
return 1
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
restart_monitor() {
|
|
160
|
+
ensure_log_dir
|
|
161
|
+
|
|
162
|
+
if [ ! -d "$CONDUCTOR_ROOT" ]; then
|
|
163
|
+
return 1
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
if is_heartbeat_stale "$MONITOR_HB" "$MONITOR_STALE_THRESHOLD"; then
|
|
167
|
+
if [ -z "${AESOP_SELFHEAL_SKIP_RESTART:-}" ]; then
|
|
168
|
+
log_heal "ACTION: run-monitor heartbeat stale, restarting..."
|
|
169
|
+
if command -v bash >/dev/null 2>&1; then
|
|
170
|
+
bash "$CONDUCTOR_ROOT/monitor/run-monitor.sh" >/dev/null 2>&1 &
|
|
171
|
+
log_heal "RESTART monitor: spawned (PID: $!)"
|
|
172
|
+
else
|
|
173
|
+
log_heal "WARN: Cannot restart monitor, bash not found"
|
|
174
|
+
fi
|
|
175
|
+
else
|
|
176
|
+
log_heal "DRY-RUN: monitor heartbeat stale (would restart)"
|
|
177
|
+
fi
|
|
178
|
+
return 0
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
return 1
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
cycle_once() {
|
|
185
|
+
ensure_log_dir
|
|
186
|
+
log_heal "=== Selfheal cycle START ==="
|
|
187
|
+
|
|
188
|
+
local any_action=0
|
|
189
|
+
|
|
190
|
+
restart_watchdog
|
|
191
|
+
if [ $? -eq 0 ]; then
|
|
192
|
+
any_action=1
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
restart_monitor
|
|
196
|
+
if [ $? -eq 0 ]; then
|
|
197
|
+
any_action=1
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
if [ $any_action -eq 0 ]; then
|
|
201
|
+
log_heal "No stale heartbeats detected"
|
|
202
|
+
fi
|
|
203
|
+
|
|
204
|
+
log_heal "=== Selfheal cycle END ==="
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
main() {
|
|
208
|
+
lock_result=0
|
|
209
|
+
acquire_lock "$SELFHEAL_LOCK_DIR" "$SELFHEAL_STALE_THRESHOLD"
|
|
210
|
+
lock_result=$?
|
|
211
|
+
|
|
212
|
+
if [ $lock_result -eq 1 ]; then
|
|
213
|
+
exit 0
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
trap "release_lock \"$SELFHEAL_LOCK_DIR\"; exit 0" INT TERM EXIT
|
|
217
|
+
|
|
218
|
+
if [ "$MODE" = "--once" ]; then
|
|
219
|
+
cycle_once
|
|
220
|
+
exit 0
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
while true; do
|
|
224
|
+
cycle_once
|
|
225
|
+
sleep 60
|
|
226
|
+
done
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
230
|
+
main "$@"
|
|
231
|
+
fi
|