@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.
Files changed (114) hide show
  1. package/CHANGELOG.md +101 -2
  2. package/README.md +66 -15
  3. package/bin/cli.js +164 -41
  4. package/daemons/run-watchdog.sh +16 -4
  5. package/daemons/selfheal.sh +231 -0
  6. package/docs/ANY-REPO.md +427 -0
  7. package/docs/CONTRIBUTING.md +72 -0
  8. package/docs/DEMO.md +334 -0
  9. package/docs/INSTALL.md +70 -1
  10. package/docs/QUICKSTART.md +80 -0
  11. package/docs/README.md +33 -3
  12. package/docs/TEAM-STATE.md +540 -0
  13. package/driver/CLAUDE.md +148 -0
  14. package/driver/README.md +383 -0
  15. package/driver/aesop.config.example.json +80 -0
  16. package/driver/agent_driver.py +355 -0
  17. package/driver/backend_config.py +253 -0
  18. package/driver/claude_code_driver.py +198 -0
  19. package/driver/codex_driver.py +626 -0
  20. package/driver/openai_compatible_driver.py +249 -0
  21. package/driver/openai_transport.py +146 -0
  22. package/driver/verification_policy.py +75 -0
  23. package/driver/wave_bridge.py +246 -0
  24. package/driver/wave_loop.py +1041 -0
  25. package/hooks/pre-push-policy.sh +109 -28
  26. package/mcp/server.mjs +320 -4
  27. package/package.json +23 -15
  28. package/skills/CLAUDE.md +132 -2
  29. package/skills/buildsystem/SKILL.md +330 -0
  30. package/skills/buildsystem/wave-flat-dispatch.template.mjs +658 -0
  31. package/skills/fleet/SKILL.md +113 -0
  32. package/skills/power/SKILL.md +246 -131
  33. package/state_store/__init__.py +2 -1
  34. package/state_store/api.py +19 -4
  35. package/state_store/coordination.py +209 -0
  36. package/state_store/identity.py +51 -0
  37. package/state_store/store.py +185 -73
  38. package/templates/wave-presets/data.json +65 -0
  39. package/templates/wave-presets/library.json +65 -0
  40. package/templates/wave-presets/saas.json +64 -0
  41. package/tools/audit_report.py +388 -0
  42. package/tools/bench_runner.py +100 -3
  43. package/tools/ci_merge_wait.py +256 -35
  44. package/tools/ci_workflow_lint.py +430 -0
  45. package/tools/claudemd_drift.py +394 -0
  46. package/tools/claudemd_lint.py +359 -0
  47. package/tools/common.py +39 -3
  48. package/tools/cost_ceiling.py +63 -31
  49. package/tools/cost_econ.py +480 -0
  50. package/tools/defect_escape.py +252 -0
  51. package/tools/doctor.js +1 -1
  52. package/tools/eod_sweep.py +58 -0
  53. package/tools/fleet.js +260 -0
  54. package/tools/fleet_ledger.py +209 -7
  55. package/tools/git_identity_check.py +315 -0
  56. package/tools/health-score.js +40 -0
  57. package/tools/health_score.py +361 -0
  58. package/tools/metrics_gate.py +13 -4
  59. package/tools/mutation_test.py +401 -0
  60. package/tools/portability_check.py +206 -0
  61. package/tools/reconcile.py +7 -4
  62. package/tools/secret_scan.py +137 -50
  63. package/tools/self_stats.py +20 -0
  64. package/tools/transcript_digest.py +380 -0
  65. package/tools/verify_activity_filter.py +437 -0
  66. package/tools/verify_agent_inspector.py +2 -0
  67. package/tools/verify_dash.py +2 -0
  68. package/tools/verify_dispatch_panel.py +301 -0
  69. package/tools/verify_failure_drilldown.py +188 -0
  70. package/tools/verify_prboard.py +2 -0
  71. package/tools/verify_scorecards.py +281 -0
  72. package/tools/verify_submit_encoding.py +2 -0
  73. package/tools/verify_ui_trio.py +409 -0
  74. package/tools/verify_wave_telemetry.py +268 -0
  75. package/tools/wave_backlog_analyzer.py +490 -0
  76. package/tools/wave_ledger_hook.py +150 -0
  77. package/tools/wave_preflight.py +512 -0
  78. package/tools/wave_resume.py +215 -0
  79. package/tools/wave_templates.py +215 -0
  80. package/ui/agents.py +68 -14
  81. package/ui/collectors.py +0 -55
  82. package/ui/config.py +7 -2
  83. package/ui/cost.py +231 -12
  84. package/ui/handler.py +183 -0
  85. package/ui/quality_scorecard.py +232 -0
  86. package/ui/wave_audit_tail.py +213 -0
  87. package/ui/wave_dispatch.py +280 -0
  88. package/ui/wave_failure.py +288 -0
  89. package/ui/wave_gantt.py +152 -0
  90. package/ui/wave_reasoning_tail.py +176 -0
  91. package/ui/wave_telemetry.py +377 -0
  92. package/ui/web/dist/assets/index-BGbgw2Nh.js +9 -0
  93. package/ui/web/dist/assets/index-DqZLgwNg.css +1 -0
  94. package/ui/web/dist/index.html +2 -2
  95. package/bin/CLAUDE.md +0 -76
  96. package/daemons/CLAUDE.md +0 -36
  97. package/dash/CLAUDE.md +0 -32
  98. package/docs/archive/README.md +0 -3
  99. package/docs/archive/spikes/tiered-cognition/ACTIVATION.md +0 -125
  100. package/docs/archive/spikes/tiered-cognition/DESIGN.md +0 -287
  101. package/docs/archive/spikes/tiered-cognition/FINDINGS.md +0 -113
  102. package/docs/archive/spikes/tiered-cognition/README.md +0 -27
  103. package/docs/archive/spikes/tiered-cognition/aesop-cognition.example.md +0 -32
  104. package/docs/archive/spikes/tiered-cognition/force-model-policy.merged.mjs +0 -673
  105. package/docs/archive/spikes/tiered-cognition/strip-tools-hook.mjs +0 -434
  106. package/hooks/CLAUDE.md +0 -89
  107. package/mcp/CLAUDE.md +0 -213
  108. package/monitor/CLAUDE.md +0 -40
  109. package/scan/CLAUDE.md +0 -30
  110. package/state_store/CLAUDE.md +0 -39
  111. package/tools/CLAUDE.md +0 -79
  112. package/ui/CLAUDE.md +0 -127
  113. package/ui/web/dist/assets/index-0qQYnvMC.js +0 -9
  114. package/ui/web/dist/assets/index-BdIlFieV.css +0 -1
@@ -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 lock_mtime=$(cat "$lock_dir/timestamp" 2>/dev/null || echo 0)
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 lock_mtime=$(cat "$lock_dir/timestamp" 2>/dev/null || echo 0)
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