@matt82198/aesop 0.1.0 → 0.3.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/CHANGELOG.md +117 -2
- package/README.md +59 -218
- package/bin/cli.js +168 -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/HOOK-INSTALL.md +15 -56
- package/docs/INSTALL.md +74 -4
- package/docs/PORTING.md +166 -0
- package/docs/README.md +33 -3
- package/docs/TEAM-STATE.md +372 -0
- package/docs/reproduce.md +33 -3
- package/driver/CLAUDE.md +150 -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 +673 -0
- package/driver/openai_compatible_driver.py +249 -0
- package/driver/openai_transport.py +179 -0
- package/driver/verification_policy.py +75 -0
- package/driver/wave_bridge.py +254 -0
- package/driver/wave_loop.py +1408 -0
- package/driver/wave_scheduler.py +890 -0
- package/hooks/pre-push-policy.sh +131 -33
- package/mcp/server.mjs +320 -4
- package/monitor/collect-signals.mjs +69 -0
- package/package.json +22 -14
- 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 +4 -2
- package/state_store/api.py +19 -4
- package/state_store/coordination.py +209 -0
- package/state_store/identity.py +51 -0
- package/state_store/projections.py +63 -0
- package/state_store/read_api.py +156 -0
- package/state_store/store.py +185 -73
- package/state_store/write_api.py +462 -0
- 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 +166 -43
- package/tools/cost_econ.py +480 -0
- package/tools/cost_projection.py +559 -0
- package/tools/crossos_drift.py +394 -0
- package/tools/defect_escape.py +252 -0
- package/tools/doctor.js +1 -1
- package/tools/eod_sweep.py +188 -26
- 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 +523 -0
- package/tools/portability_check.py +206 -0
- package/tools/proposals.mjs +47 -2
- package/tools/reconcile.py +7 -4
- package/tools/reproduce.js +405 -0
- package/tools/secret_scan.py +207 -65
- package/tools/self_stats.py +20 -0
- package/tools/stall_check.py +247 -16
- package/tools/stateapi_lint.py +325 -0
- package/tools/test_battery.py +173 -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_cost_panel.py +345 -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 +779 -0
- package/tools/wave_resume.py +215 -0
- package/tools/wave_templates.py +340 -0
- package/ui/agents.py +68 -14
- package/ui/collectors.py +81 -55
- package/ui/config.py +7 -2
- package/ui/cost.py +231 -12
- package/ui/handler.py +383 -55
- package/ui/quality_scorecard.py +232 -0
- package/ui/sse.py +3 -3
- 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 +383 -0
- package/ui/web/dist/assets/index-CNQxaiOW.css +1 -0
- package/ui/web/dist/assets/index-CP68RIh3.js +9 -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
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""ClaudeCodeDriver -- reference AgentDriver for the Claude Code backend.
|
|
3
|
+
|
|
4
|
+
This is the PARITY reference: it maps the five AgentDriver operations onto what
|
|
5
|
+
Claude Code's Workflow harness already provides. It is intentionally a thin,
|
|
6
|
+
well-documented adapter, because in Claude Code the actual dispatch does NOT run
|
|
7
|
+
inside this Python process -- it runs inside the harness's Workflow context
|
|
8
|
+
(the `agent()`, `parallel()`, Read/Write/Bash tools, `budget.spent()`).
|
|
9
|
+
|
|
10
|
+
WHAT MAPS WHERE
|
|
11
|
+
---------------
|
|
12
|
+
Operation Claude Code mechanism Lives where
|
|
13
|
+
------------------- ------------------------------- ------------------------
|
|
14
|
+
probe_capabilities static, known-good facts this file (concrete)
|
|
15
|
+
dispatch_worker Workflow agent()/Task tool HARNESS (conceptual)
|
|
16
|
+
worker_status heartbeat + harness liveness HARNESS + state files
|
|
17
|
+
run_command Bash tool HARNESS
|
|
18
|
+
resolve_model Anthropic model-name passthrough this file (concrete)
|
|
19
|
+
|
|
20
|
+
So two of the five ops (probe_capabilities, resolve_model) are fully concrete
|
|
21
|
+
Python here -- they are pure data/mapping and need no harness. The other three
|
|
22
|
+
(dispatch_worker, worker_status, run_command) are documented adapters: in a live
|
|
23
|
+
Claude Code wave they are serviced by the harness's own tools, not by this
|
|
24
|
+
process. This class exists so the SEAM is real and testable -- the wave loop can
|
|
25
|
+
be written against AgentDriver and this reference proves the Claude Code backend
|
|
26
|
+
satisfies the contract. When the wave-flat-dispatch template is refactored onto
|
|
27
|
+
the driver (spike Phase 1), these three methods become the documented handoff
|
|
28
|
+
points to the harness.
|
|
29
|
+
|
|
30
|
+
For out-of-harness use (tests, tooling, local scripts) run_command is given a
|
|
31
|
+
real subprocess implementation so it is not a dead stub; dispatch_worker and
|
|
32
|
+
worker_status raise a clear, explained error rather than pretending to spawn a
|
|
33
|
+
Claude agent from plain Python.
|
|
34
|
+
|
|
35
|
+
stdlib-only, ASCII-only, Windows + Linux safe.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
import subprocess
|
|
39
|
+
from typing import Optional
|
|
40
|
+
|
|
41
|
+
from agent_driver import (
|
|
42
|
+
AgentDriver,
|
|
43
|
+
CommandResult,
|
|
44
|
+
DriverCapabilities,
|
|
45
|
+
ROLE_SETUP,
|
|
46
|
+
ROLE_VERIFY,
|
|
47
|
+
ROLE_WORKER,
|
|
48
|
+
WorkerRequest,
|
|
49
|
+
WorkerResult,
|
|
50
|
+
WorkerStatus,
|
|
51
|
+
WORKER_UNKNOWN,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# Default abstract-role -> Anthropic model mapping. Workers are Haiku by policy
|
|
56
|
+
# (aesop cardinal rule: subagents are always Haiku); setup/verify may lift to
|
|
57
|
+
# Sonnet. resolve_model() reads this and is overridable via the constructor.
|
|
58
|
+
_DEFAULT_MODEL_MAP = {
|
|
59
|
+
ROLE_WORKER: "haiku",
|
|
60
|
+
ROLE_SETUP: "sonnet",
|
|
61
|
+
ROLE_VERIFY: "haiku",
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Marker error text shared with tests: the ops that only a live harness can do.
|
|
65
|
+
_HARNESS_ONLY = (
|
|
66
|
+
"ClaudeCodeDriver.{op} is serviced by the Claude Code Workflow harness "
|
|
67
|
+
"(agent()/Task tool), not by this Python process. In a live wave the "
|
|
68
|
+
"orchestrator dispatches through the harness; this reference adapter marks "
|
|
69
|
+
"the seam. See driver/README.md 'What maps where'."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ClaudeCodeDriver(AgentDriver):
|
|
74
|
+
"""Reference adapter mapping AgentDriver onto Claude Code."""
|
|
75
|
+
|
|
76
|
+
name = "claude-code"
|
|
77
|
+
|
|
78
|
+
def __init__(self, model_map: Optional[dict] = None):
|
|
79
|
+
# Copy so callers cannot mutate our defaults; fall back per-role.
|
|
80
|
+
self._model_map = dict(_DEFAULT_MODEL_MAP)
|
|
81
|
+
if model_map:
|
|
82
|
+
self._model_map.update(model_map)
|
|
83
|
+
|
|
84
|
+
# -- Operation 1: capability probe (concrete) --------------------------
|
|
85
|
+
def probe_capabilities(self) -> DriverCapabilities:
|
|
86
|
+
"""Claude Code is the self-contained, high-accuracy reference backend.
|
|
87
|
+
|
|
88
|
+
Every capability is native and instant; tool-use accuracy is ~0.99, so
|
|
89
|
+
the recommended verification tier is 1 (light spot-check). These facts
|
|
90
|
+
are static and known-good -- no probing round-trip required.
|
|
91
|
+
"""
|
|
92
|
+
return DriverCapabilities(
|
|
93
|
+
name=self.name,
|
|
94
|
+
parallel_dispatch=True, # native parallel() in the harness
|
|
95
|
+
worker_filesystem_access=True, # workers use Read/Write tools
|
|
96
|
+
worker_shell_access=True, # workers use the Bash tool
|
|
97
|
+
structured_output=True, # agent(schema=...) is near-perfect
|
|
98
|
+
worktree_isolation=True, # orchestrator manages git worktrees
|
|
99
|
+
native_cost_tracking=True, # budget.spent() real-time API
|
|
100
|
+
native_stall_detection=True, # harness + heartbeat liveness
|
|
101
|
+
tool_use_accuracy=0.99,
|
|
102
|
+
recommended_verification_tier=1,
|
|
103
|
+
available_models=("haiku", "sonnet", "opus"),
|
|
104
|
+
notes=(
|
|
105
|
+
"Reference backend. Self-contained: no external harness. "
|
|
106
|
+
"dispatch_worker/worker_status/run_command are serviced by the "
|
|
107
|
+
"Claude Code Workflow harness in a live wave."
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
# -- Operation 2: dispatch (harness-serviced) --------------------------
|
|
112
|
+
def dispatch_worker(self, request: WorkerRequest) -> WorkerResult:
|
|
113
|
+
"""Documented seam: real dispatch happens in the harness.
|
|
114
|
+
|
|
115
|
+
In a live Claude Code wave the orchestrator spawns the worker via the
|
|
116
|
+
harness's agent()/Task tool with the resolved model, the owned-files
|
|
117
|
+
contract, and (optionally) request.result_schema. From plain Python
|
|
118
|
+
there is no Claude agent to spawn, so we raise a clear, explained error
|
|
119
|
+
rather than fake a result.
|
|
120
|
+
"""
|
|
121
|
+
raise NotImplementedError(_HARNESS_ONLY.format(op="dispatch_worker"))
|
|
122
|
+
|
|
123
|
+
# -- Operation 3: stall detection (harness-serviced) -------------------
|
|
124
|
+
def worker_status(self, worker_id: str) -> WorkerStatus:
|
|
125
|
+
"""Documented seam: liveness comes from the harness + heartbeat files.
|
|
126
|
+
|
|
127
|
+
A live wave reads worker liveness from the harness and the fleet's
|
|
128
|
+
heartbeat/state files. Out of harness there is nothing to observe, so
|
|
129
|
+
we report UNKNOWN honestly.
|
|
130
|
+
"""
|
|
131
|
+
return WorkerStatus(
|
|
132
|
+
worker_id=worker_id,
|
|
133
|
+
state=WORKER_UNKNOWN,
|
|
134
|
+
stalled=False,
|
|
135
|
+
age_s=0.0,
|
|
136
|
+
detail=_HARNESS_ONLY.format(op="worker_status"),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# -- Operation 4: orchestrator-side command (real) ---------------------
|
|
140
|
+
def run_command(
|
|
141
|
+
self,
|
|
142
|
+
command: str,
|
|
143
|
+
cwd: Optional[str] = None,
|
|
144
|
+
shell: Optional[str] = None,
|
|
145
|
+
) -> CommandResult:
|
|
146
|
+
"""Run a command on the orchestrator host.
|
|
147
|
+
|
|
148
|
+
In a live wave this is the Bash tool; out of harness we back it with a
|
|
149
|
+
real subprocess so tooling/tests get genuine behavior. `shell` is
|
|
150
|
+
advisory -- we always execute through the platform shell so the same
|
|
151
|
+
call works on Windows and Linux.
|
|
152
|
+
"""
|
|
153
|
+
try:
|
|
154
|
+
completed = subprocess.run(
|
|
155
|
+
command,
|
|
156
|
+
cwd=cwd,
|
|
157
|
+
shell=True,
|
|
158
|
+
capture_output=True,
|
|
159
|
+
text=True,
|
|
160
|
+
)
|
|
161
|
+
return CommandResult(
|
|
162
|
+
exit_code=completed.returncode,
|
|
163
|
+
stdout=completed.stdout or "",
|
|
164
|
+
stderr=completed.stderr or "",
|
|
165
|
+
)
|
|
166
|
+
except OSError as exc:
|
|
167
|
+
return CommandResult(exit_code=127, stdout="", stderr=str(exc))
|
|
168
|
+
|
|
169
|
+
# -- Operation 5: model selection (concrete) ---------------------------
|
|
170
|
+
def resolve_model(self, role: str) -> str:
|
|
171
|
+
"""Map an abstract role to an Anthropic model name.
|
|
172
|
+
|
|
173
|
+
Unknown roles fall back to the worker (Haiku) mapping so a mis-typed
|
|
174
|
+
role can never silently escalate cost.
|
|
175
|
+
"""
|
|
176
|
+
return self._model_map.get(role, self._model_map[ROLE_WORKER])
|
|
177
|
+
|
|
178
|
+
# -- Optional: cost tracking -------------------------------------------
|
|
179
|
+
def get_tokens_spent(self) -> Optional[int]:
|
|
180
|
+
"""Return None: per-instance spend is not observable from the harness-serviced driver.
|
|
181
|
+
|
|
182
|
+
CONTRACT:
|
|
183
|
+
The orchestrator-side driver cannot directly observe driver instance spend
|
|
184
|
+
because dispatch happens inside the harness's Workflow context (agent()/Task
|
|
185
|
+
tool), not in this Python process. In a live wave the harness-side budget.spent()
|
|
186
|
+
API is not exposed to this adapter.
|
|
187
|
+
|
|
188
|
+
DESIGN:
|
|
189
|
+
Cost enforcement is delivered by the wave loop passing None here, which causes
|
|
190
|
+
cost_ceiling.check() to perform its own windowed ledger fallback. This ensures
|
|
191
|
+
that period windowing (e.g., daily caps) is respected: cost_ceiling reads the
|
|
192
|
+
ledger directly and filters rows by the requested period. Returning a lifetime
|
|
193
|
+
sum would bypass that windowing logic and cause false trips after wave 2+.
|
|
194
|
+
|
|
195
|
+
See cost_ceiling.py:read_ledger_total_tokens() for the authoritative windowing
|
|
196
|
+
implementation.
|
|
197
|
+
"""
|
|
198
|
+
return None
|