@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
@@ -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
+ }