@matt82198/aesop 0.1.0 → 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 +89 -2
- package/README.md +63 -12
- 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
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Wave bridge: connects AgentDriver backends to wave-flat-dispatch manifest items.
|
|
3
|
+
|
|
4
|
+
Phase 3: the driver->wave-manifest seam that makes the tier-2 wiring from PR #215
|
|
5
|
+
actually driven by a backend's honest self-report.
|
|
6
|
+
|
|
7
|
+
ARCHITECTURE
|
|
8
|
+
------------
|
|
9
|
+
Two core functions bridge the gap between AgentDriver backends and the wave's
|
|
10
|
+
item manifest:
|
|
11
|
+
|
|
12
|
+
1. build_manifest_item(driver, item) -> dict
|
|
13
|
+
Given an AgentDriver + a backlog item {slug, ownsFiles, prompt, testCmd, workDir},
|
|
14
|
+
produce the manifest-item dict the wave expects, with verificationTier and model
|
|
15
|
+
baked in from the driver's probe. This makes tier-driven verification tier driven
|
|
16
|
+
by backend capability, not config knobs.
|
|
17
|
+
|
|
18
|
+
2. dispatch_item(driver, item) -> dict
|
|
19
|
+
Route execution by the driver's capabilities:
|
|
20
|
+
- If worker_filesystem_access is True (Claude/tier-1 harness path): return a
|
|
21
|
+
descriptor indicating the item should go through the normal harness worker
|
|
22
|
+
path (do NOT try to fake a Claude agent here — return {route:'harness', ...}).
|
|
23
|
+
- If False (codex/tier-2 non-agentic): DRIVE it via orchestrator-side contract:
|
|
24
|
+
call driver.dispatch_worker(WorkerRequest(...)), then run the item's test via
|
|
25
|
+
driver.run_command() and decide pass ONLY from exit code 0 (NEVER from the
|
|
26
|
+
model's say-so). Return {route:'driver', ok, testExit, filesWritten}.
|
|
27
|
+
|
|
28
|
+
HONESTY & FAIL-SAFE
|
|
29
|
+
-------------------
|
|
30
|
+
Any exception -> a failed result, never a false green. Green only from run_command
|
|
31
|
+
exit 0. Ownership is enforced at the driver level (dispatch_worker rejects
|
|
32
|
+
out-of-scope paths). The bridge trusts the driver's probe truthfulness — if it
|
|
33
|
+
lies about worker_filesystem_access, the routing will be wrong, but the failure
|
|
34
|
+
will be loud and caught in CI.
|
|
35
|
+
|
|
36
|
+
stdlib-only, ASCII-only, Windows + Linux safe.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
import json
|
|
40
|
+
import sys
|
|
41
|
+
from pathlib import Path
|
|
42
|
+
from typing import Dict, Optional, Any
|
|
43
|
+
|
|
44
|
+
# Add driver/ to path for imports.
|
|
45
|
+
REPO = Path(__file__).resolve().parent.parent
|
|
46
|
+
DRIVER_DIR = REPO / "driver"
|
|
47
|
+
if str(DRIVER_DIR) not in sys.path:
|
|
48
|
+
sys.path.insert(0, str(DRIVER_DIR))
|
|
49
|
+
|
|
50
|
+
from agent_driver import (
|
|
51
|
+
AgentDriver,
|
|
52
|
+
WorkerRequest,
|
|
53
|
+
ROLE_WORKER,
|
|
54
|
+
)
|
|
55
|
+
from verification_policy import verification_policy
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def build_manifest_item(driver: AgentDriver, item: Dict[str, Any]) -> Dict[str, Any]:
|
|
59
|
+
"""Build a manifest item from a backlog item and an AgentDriver.
|
|
60
|
+
|
|
61
|
+
Takes a backlog item {slug, ownsFiles, prompt, testCmd, workDir, ...} and an
|
|
62
|
+
AgentDriver, and produces a manifest-item dict that the wave expects, with
|
|
63
|
+
verificationTier and model baked in from the driver's probed capabilities,
|
|
64
|
+
PLUS all four resolved policy knobs.
|
|
65
|
+
|
|
66
|
+
This is the seam that makes tier-driven verification actually driven by the
|
|
67
|
+
backend's honest self-report: a weak backend raises the orchestrator's burden
|
|
68
|
+
via recommended_verification_tier, not config.
|
|
69
|
+
|
|
70
|
+
The policy is RESOLVED ONCE here in Python and carried as literal manifest
|
|
71
|
+
fields, so JS cannot recompute/drift. The template consumes these resolved
|
|
72
|
+
values; tier-1/Claude path with no verificationTier must behave exactly as
|
|
73
|
+
before (tier-1 defaults: repairCap=1, requireAdversarialReview=false,
|
|
74
|
+
spotCheckFrac=0.10, validateAllJson=false).
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
driver: AgentDriver instance (provides probe_capabilities, resolve_model).
|
|
78
|
+
item: dict with at least {slug, ownsFiles, prompt}. May also have
|
|
79
|
+
testCmd, workDir, selfCheckCmd, label, phase, effort.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
dict with all item fields PLUS:
|
|
83
|
+
- model: concrete model id from driver.resolve_model('worker')
|
|
84
|
+
- verificationTier: recommended tier from driver.probe_capabilities()
|
|
85
|
+
- repairCap: resolved repair budget for this tier
|
|
86
|
+
- requireAdversarialReview: resolved adversarial review requirement
|
|
87
|
+
- spotCheckFrac: resolved spot-check fraction for this tier
|
|
88
|
+
- validateAllJson: resolved JSON validation strictness for this tier
|
|
89
|
+
The dict is suitable for passing to wave-flat-dispatch.template.mjs as an
|
|
90
|
+
item in the items[] array. All four policy fields are consumed directly
|
|
91
|
+
by the template; it does NOT recompute them.
|
|
92
|
+
"""
|
|
93
|
+
caps = driver.probe_capabilities()
|
|
94
|
+
model = driver.resolve_model(ROLE_WORKER)
|
|
95
|
+
|
|
96
|
+
# Resolve all four policy knobs from the backend's verification tier.
|
|
97
|
+
policy = verification_policy(caps)
|
|
98
|
+
|
|
99
|
+
# Copy the input item and enrich with model, tier, and all four policy knobs.
|
|
100
|
+
result = dict(item)
|
|
101
|
+
result["model"] = model
|
|
102
|
+
result["verificationTier"] = caps.recommended_verification_tier
|
|
103
|
+
result["repairCap"] = policy["repair_cap"]
|
|
104
|
+
result["requireAdversarialReview"] = policy["require_adversarial_review"]
|
|
105
|
+
result["spotCheckFrac"] = policy["spot_check_frac"]
|
|
106
|
+
result["validateAllJson"] = policy["validate_all_json"]
|
|
107
|
+
|
|
108
|
+
return result
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def dispatch_item(
|
|
112
|
+
driver: AgentDriver, item: Dict[str, Any], workdir: Optional[str] = None
|
|
113
|
+
) -> Dict[str, Any]:
|
|
114
|
+
"""Route execution of a backlog item through an AgentDriver backend.
|
|
115
|
+
|
|
116
|
+
Dispatches a single item through the driver, honoring its capabilities:
|
|
117
|
+
|
|
118
|
+
- If driver.probe_capabilities().worker_filesystem_access is True (Claude Code
|
|
119
|
+
tier-1 harness path): return {route:'harness', ...} to indicate the item
|
|
120
|
+
should go through the normal harness worker path. We do NOT attempt to fake
|
|
121
|
+
a Claude agent here — the harness will handle it.
|
|
122
|
+
|
|
123
|
+
- If False (Codex/tier-2, non-agentic): DRIVE it via orchestrator-side contract:
|
|
124
|
+
1. Call driver.dispatch_worker(WorkerRequest(...)) with the item's prompt,
|
|
125
|
+
owned files, and workdir. The driver injects file context, returns produced
|
|
126
|
+
files.
|
|
127
|
+
2. Run the item's test via driver.run_command(...) and decide pass ONLY from
|
|
128
|
+
exit code 0 (NEVER from the model's say-so). This is the center verification
|
|
129
|
+
rule: the orchestrator is the ground truth, not the model.
|
|
130
|
+
3. Return a structured result {route:'driver', ok, testExit, filesWritten}.
|
|
131
|
+
|
|
132
|
+
FAIL-SAFE: any exception -> a failed result (ok=False), never a false green.
|
|
133
|
+
Ownership is enforced at the driver level (dispatch_worker rejects out-of-scope
|
|
134
|
+
paths wholesale). Green only from run_command exit code 0.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
driver: AgentDriver instance.
|
|
138
|
+
item: dict with at least {slug, ownsFiles, prompt, testCmd}. May also have
|
|
139
|
+
workDir, selfCheckCmd, label, phase, effort.
|
|
140
|
+
workdir: optional override for the working directory (defaults to item.workDir
|
|
141
|
+
or '.').
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
dict with the following keys:
|
|
145
|
+
- route: 'harness' or 'driver'
|
|
146
|
+
- ok: bool (only True if driver route AND test passed)
|
|
147
|
+
- testExit: int (exit code from run_command, if route=='driver'; None otherwise)
|
|
148
|
+
- filesWritten: list of str (files written by the driver, if route=='driver'; None if 'harness')
|
|
149
|
+
- error: str (error message, if ok=False; None otherwise)
|
|
150
|
+
- workerId: str (worker id from dispatch_worker, if route=='driver'; None otherwise)
|
|
151
|
+
- verified: bool (True only if test was run and passed; False if not tested or test failed)
|
|
152
|
+
- reason: str (optional; present when item could not be verified, e.g. 'no_test_command')
|
|
153
|
+
"""
|
|
154
|
+
try:
|
|
155
|
+
caps = driver.probe_capabilities()
|
|
156
|
+
slug = item.get("slug", "unknown")
|
|
157
|
+
|
|
158
|
+
# Determine working directory.
|
|
159
|
+
exec_workdir = workdir or item.get("workdir") or item.get("workDir") or "."
|
|
160
|
+
|
|
161
|
+
# Route by driver capability.
|
|
162
|
+
if caps.worker_filesystem_access:
|
|
163
|
+
# Claude Code tier-1 path: harness will handle it.
|
|
164
|
+
return {
|
|
165
|
+
"route": "harness",
|
|
166
|
+
"ok": False, # Unknown status; harness will set actual result
|
|
167
|
+
"testExit": None,
|
|
168
|
+
"filesWritten": None,
|
|
169
|
+
"error": None,
|
|
170
|
+
"workerId": None,
|
|
171
|
+
"verified": False, # Not verified by orchestrator (harness will verify)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
# Codex/tier-2 non-agentic path: orchestrator-managed dispatch.
|
|
175
|
+
# Extract fields from item.
|
|
176
|
+
prompt = item.get("prompt", "")
|
|
177
|
+
owned_files = tuple(item.get("ownsFiles", []))
|
|
178
|
+
test_cmd = item.get("testCmd", "")
|
|
179
|
+
model = item.get("model")
|
|
180
|
+
|
|
181
|
+
# Dispatch the worker.
|
|
182
|
+
request = WorkerRequest(
|
|
183
|
+
prompt=prompt,
|
|
184
|
+
owned_files=owned_files,
|
|
185
|
+
workdir=exec_workdir,
|
|
186
|
+
model=model,
|
|
187
|
+
role=ROLE_WORKER,
|
|
188
|
+
label=f"dispatch:{slug}",
|
|
189
|
+
)
|
|
190
|
+
result = driver.dispatch_worker(request)
|
|
191
|
+
|
|
192
|
+
if not result.ok:
|
|
193
|
+
# Worker dispatch failed (malformed JSON, ownership violation, etc.).
|
|
194
|
+
return {
|
|
195
|
+
"route": "driver",
|
|
196
|
+
"ok": False,
|
|
197
|
+
"testExit": None,
|
|
198
|
+
"filesWritten": None,
|
|
199
|
+
"error": f"dispatch_worker failed: {result.error}",
|
|
200
|
+
"workerId": result.worker_id,
|
|
201
|
+
"verified": False, # Not verified (dispatch failed)
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# Worker succeeded; now run the test command.
|
|
205
|
+
# Green is decided ONLY by exit code 0, never by the model's done:true.
|
|
206
|
+
if not test_cmd:
|
|
207
|
+
# No test command: MUST fail-closed. An unverified item must never
|
|
208
|
+
# report success — that violates aesop's core honesty guarantee:
|
|
209
|
+
# "no test to fail" is NOT the same as "verified correct."
|
|
210
|
+
return {
|
|
211
|
+
"route": "driver",
|
|
212
|
+
"ok": False,
|
|
213
|
+
"testExit": None,
|
|
214
|
+
"filesWritten": list(result.files_written or []),
|
|
215
|
+
"error": "no testCmd: cannot verify, refusing to report success",
|
|
216
|
+
"workerId": result.worker_id,
|
|
217
|
+
"verified": False, # Not verified (no test to run)
|
|
218
|
+
"reason": "no_test_command",
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
test_result = driver.run_command(test_cmd, cwd=exec_workdir)
|
|
222
|
+
|
|
223
|
+
# Decision: ok=True ONLY if test exit code is 0.
|
|
224
|
+
ok = test_result.exit_code == 0
|
|
225
|
+
|
|
226
|
+
return {
|
|
227
|
+
"route": "driver",
|
|
228
|
+
"ok": ok,
|
|
229
|
+
"testExit": test_result.exit_code,
|
|
230
|
+
"filesWritten": list(result.files_written or []),
|
|
231
|
+
"error": None if ok else f"test failed with exit {test_result.exit_code}",
|
|
232
|
+
"workerId": result.worker_id,
|
|
233
|
+
"verified": ok, # Verified if and only if test passed (exit 0)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
except Exception as exc:
|
|
237
|
+
# Catch-all: any unexpected error -> failed result, never a false green.
|
|
238
|
+
return {
|
|
239
|
+
"route": "driver",
|
|
240
|
+
"ok": False,
|
|
241
|
+
"testExit": None,
|
|
242
|
+
"filesWritten": None,
|
|
243
|
+
"error": f"dispatch_item internal error: {exc}",
|
|
244
|
+
"workerId": None,
|
|
245
|
+
"verified": False, # Not verified (exception during execution)
|
|
246
|
+
}
|