@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
package/docs/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Aesop Documentation
|
|
2
2
|
|
|
3
|
-
Aesop is
|
|
3
|
+
Aesop is an autonomous developer system that crawls into any repository and orchestrates intelligent work. It ranks tasks, dispatches parallel Haiku agents, verifies merges, audits the work, and feeds the next iteration. **State persists across multiple instances via a durable SQLite event log**, so your whole team uses one coordinated system.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -12,7 +12,7 @@ If you're new to Aesop, follow this 4-stage path:
|
|
|
12
12
|
**[INSTALL.md](INSTALL.md)** — Prerequisites, `npx` scaffold, what gets created
|
|
13
13
|
|
|
14
14
|
- System requirements (Claude Code CLI, Git, Bash, Node.js, Python)
|
|
15
|
-
- Quick-start: `npx @matt82198/aesop
|
|
15
|
+
- Quick-start: `npx @matt82198/aesop my-fleet`
|
|
16
16
|
- Manual setup for development (git clone)
|
|
17
17
|
- Pre-push hook installation
|
|
18
18
|
|
|
@@ -56,6 +56,33 @@ If you're new to Aesop, follow this 4-stage path:
|
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
59
|
+
## Core Concepts: /power and /buildsystem
|
|
60
|
+
|
|
61
|
+
### /power — System Initialization
|
|
62
|
+
The `/power` skill initializes Aesop into your repository. It:
|
|
63
|
+
- Loads your orchestrator brain (cardinal rules, domain map, team memory, system state)
|
|
64
|
+
- Verifies the system is healthy (filesystem, git, API keys, watchdog)
|
|
65
|
+
- Outputs a health brief and next-step recommendations
|
|
66
|
+
|
|
67
|
+
Run `/power` at the start of each Claude Code session. It's idempotent—safe to run multiple times. See [skills/power/SKILL.md](../skills/power/SKILL.md) for details.
|
|
68
|
+
|
|
69
|
+
### /buildsystem — One Complete Wave Cycle
|
|
70
|
+
The `/buildsystem` skill runs **one complete iteration of the autonomous delivery loop**. It:
|
|
71
|
+
1. **Ranks** the backlog (priority, dependencies, team affinity)
|
|
72
|
+
2. **Dispatches** parallel Haiku agents on file-disjoint domains (tests, build, docs, UI, review, etc.)
|
|
73
|
+
3. **Verifies** each merge (CI, security scans, audit spot-checks)
|
|
74
|
+
4. **Checkpoints** (STATE.md + BUILDLOG.md committed to git, survive wipes)
|
|
75
|
+
5. **Audits** fleet health and feeds next backlog (monitor signals, signal collectors, findings)
|
|
76
|
+
|
|
77
|
+
This is the repeatable loop that runs your delivery cycle indefinitely, with each wave learning from the prior audit. You can run `/buildsystem` once per wave (typically 30 min–2 hours depending on backlog size). See [HOW-THE-LOOP-WORKS.md](HOW-THE-LOOP-WORKS.md) for a concrete walkthrough.
|
|
78
|
+
|
|
79
|
+
### Team State & Multi-Instance Design
|
|
80
|
+
**Current Status (0.1.0)**: Single-instance proven. A team uses Aesop by designating one operator who runs the wave loop. State is durably checkpointed in git (STATE.md, BUILDLOG.md, tracker.json exports).
|
|
81
|
+
|
|
82
|
+
**In Design**: Multi-instance coordination via the state_store substrate. The event-sourced SQLite layer is production-ready but currently opt-in. A future release will enable multiple Aesop instances (e.g., per-team subgroups or geographic regions) to coordinate around a single source of truth—a Postgres-backed event log, with git as a diffable export. See [TEAM-STATE.md](TEAM-STATE.md) (design in progress) for the vision and current architecture decisions.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
59
86
|
## Deep-Dive Reference Docs
|
|
60
87
|
|
|
61
88
|
Once you've completed the adopter journey, use these for operational reference:
|
|
@@ -108,12 +135,15 @@ Once you've completed the adopter journey, use these for operational reference:
|
|
|
108
135
|
**I want to understand the cost model**
|
|
109
136
|
→ [DISPATCH-MODEL.md](DISPATCH-MODEL.md) or [HOW-THE-LOOP-WORKS.md](HOW-THE-LOOP-WORKS.md#why-its-fast--cheap)
|
|
110
137
|
|
|
111
|
-
**I want to know what's actually proven vs. claimed (the
|
|
138
|
+
**I want to know what's actually proven vs. claimed (the 0.1.0 milestone)**
|
|
112
139
|
→ [autonomous-swe.md](autonomous-swe.md)
|
|
113
140
|
|
|
114
141
|
**I need to understand how state survives a crash**
|
|
115
142
|
→ [CHECKPOINTING.md](CHECKPOINTING.md)
|
|
116
143
|
|
|
144
|
+
**I want to understand multi-instance coordination**
|
|
145
|
+
→ [TEAM-STATE.md](TEAM-STATE.md)
|
|
146
|
+
|
|
117
147
|
**I'm reviewing a PR that changes orchestration behavior**
|
|
118
148
|
→ [BEHAVIORAL-PR-REVIEW.md](BEHAVIORAL-PR-REVIEW.md)
|
|
119
149
|
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# Team-Shared State: Current & Future Architecture
|
|
2
|
+
|
|
3
|
+
**Status (0.1.0)**: Single-instance with durable git checkpointing. Event-sourced SQLite module (`state_store/`) is production-ready but not yet integrated into orchestrator reader/writer paths.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Current State Model (Git-as-Checkpoint)
|
|
8
|
+
|
|
9
|
+
Aesop currently stores orchestration state in **two git-tracked files**:
|
|
10
|
+
|
|
11
|
+
- **STATE.md** — Orchestrator intent, decisions, phase, and NEXT STEPS (single-writer)
|
|
12
|
+
- **BUILDLOG.md** — Append-only progress snapshots (agents append one line per work unit)
|
|
13
|
+
|
|
14
|
+
**Why it works**: Single-instance is proven. State survives machine wipes, resync on session restart, human-diffable for review and forensics.
|
|
15
|
+
|
|
16
|
+
**Limitation**: Does not scale to teams—no concurrent writers, no transactions, high latency (requires `git push`).
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Future: SQLite Event Log + Git Export
|
|
21
|
+
|
|
22
|
+
**Vision**: Event-sourced SQLite WAL becomes the system-of-record. Git exports are rendered from current projections for durability and review.
|
|
23
|
+
|
|
24
|
+
**In the repo now**:
|
|
25
|
+
- `state_store/store.py` — EventStore with atomic `append()`, concurrency-safe writes
|
|
26
|
+
- `state_store/projections.py` — Fold events into tracker state
|
|
27
|
+
- `state_store/api.py` — Facade for backend swaps (SQLite today, Postgres later)
|
|
28
|
+
- `tests/test_state_store.py` — Concurrent-write proofs, projection tests
|
|
29
|
+
|
|
30
|
+
**Next steps**: Wire the orchestrator and agent writers to use `StateAPI` instead of git direct writes. This unblocks team-scale coordination without breaking single-instance durability (git exports remain).
|
|
31
|
+
|
|
32
|
+
For implementation details, see `state_store/` source code and inline documentation.
|
|
33
|
+
status["phase"] = ev["payload"]["phase"]
|
|
34
|
+
elif ev["type"] == "decision_locked":
|
|
35
|
+
key = ev["payload"]["key"]
|
|
36
|
+
status["decisions"][key] = ev["payload"]["value"]
|
|
37
|
+
elif ev["type"] == "next_steps_updated":
|
|
38
|
+
status["next_steps"] = ev["payload"]["steps"]
|
|
39
|
+
elif ev["type"] == "audit_findings_recorded":
|
|
40
|
+
status["latest_audit"] = ev["payload"]
|
|
41
|
+
return status
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Readers** (same-turn):
|
|
45
|
+
- Dashboard queries `api.project("orchestrator_status")` → sees latest phase, decisions, next steps live (no git fetch).
|
|
46
|
+
- Monitor daemon queries `api.project("orchestrator_status")` → sees what the orchestrator decided last turn; compares against local observations.
|
|
47
|
+
- Agents query `api.project("orchestrator_status")` → see their assignment (brief payload in next_steps).
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Migration Path: Which Readers/Writers Move First
|
|
52
|
+
|
|
53
|
+
### Phase 1: Add orchestrator_status Stream (EARLY CUTOVER)
|
|
54
|
+
|
|
55
|
+
**What moves**: Orchestrator WRITES phase + next steps to `orchestrator_status` events instead of `STATE.md`. Readers stay on git (BUILDLOG.md + STATE.md fetches).
|
|
56
|
+
|
|
57
|
+
**Why first**: Orchestrator is single-writer; no merge conflicts to worry about. Proof that the event store works under real orchestrator load.
|
|
58
|
+
|
|
59
|
+
**Changes**:
|
|
60
|
+
1. Add `orchestrator_status` projector to `state_store/projections.py` (new file or extend existing).
|
|
61
|
+
2. Orchestrator calls `api.append("orchestrator_status", "phase_changed", {...}, actor="orchestrator")` instead of git commits.
|
|
62
|
+
3. Export job (`export_orchestrator_status()`) renders to a new `STATE.md` periodically (e.g., per-phase boundary) for git durability.
|
|
63
|
+
4. Readers still read git; no behavior change yet.
|
|
64
|
+
|
|
65
|
+
**Concurrency model**: Single orchestrator writer, many readers (dashboards, agents). No conflicts.
|
|
66
|
+
|
|
67
|
+
**Git-authoritative?**: NO. `STATE.md` becomes a rendered snapshot, not source-of-truth. On recovery, orchestrator reads latest `STATE.md` but reconciles with `orchestrator_status` events to fill any gaps.
|
|
68
|
+
|
|
69
|
+
### Phase 2: Dual-Read Tracker (MIDDLE CUTOVER)
|
|
70
|
+
|
|
71
|
+
**What moves**: Tracker CRUD (item creation, updates, archives) writes to the event log. Readers read from either git or the live event log (both running simultaneously).
|
|
72
|
+
|
|
73
|
+
**Why here**: Tracker is the most-read state; dual-read lets us test the event store under high read load while keeping git as a safety net.
|
|
74
|
+
|
|
75
|
+
**Changes**:
|
|
76
|
+
1. Add dual-read logic to tracker consumers: try `api.project("tracker")` first; fall back to git if the event store is unavailable.
|
|
77
|
+
2. Tracker CRUD emitters (`create_item()`, `update_item()`, `archive_item()`) append events instead of mutating git.
|
|
78
|
+
3. Keep the export job running: `export_tracker(api, state/tracker.json)` renders live projections back to git.
|
|
79
|
+
4. Tests compare git and event-store projections; must match exactly (see tests line 165–179).
|
|
80
|
+
|
|
81
|
+
**Concurrency model**: Multiple writers (agents creating/updating items), many readers. SQLite WAL + `BEGIN IMMEDIATE` handles concurrency safely.
|
|
82
|
+
|
|
83
|
+
**Git-authoritative?**: NO. Event store is source-of-truth. Git is an export. Reconciliation required on recovery.
|
|
84
|
+
|
|
85
|
+
### Phase 3: Flip Readers (CUTOVER COMPLETE)
|
|
86
|
+
|
|
87
|
+
**What moves**: All readers flip to the event store; git becomes purely an audit trail.
|
|
88
|
+
|
|
89
|
+
**Changes**:
|
|
90
|
+
1. Remove dual-read fallback; all tracker reads go through `api.project("tracker")`.
|
|
91
|
+
2. Remove git-read paths from dashboards, forensics, monitoring.
|
|
92
|
+
3. Keep export jobs running for durability + review.
|
|
93
|
+
|
|
94
|
+
**Concurrency model**: No change; SQLite WAL already supports N readers + 1 writer per connection.
|
|
95
|
+
|
|
96
|
+
**Git-authoritative?**: NO. Event store is source-of-truth.
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Locking & Concurrency Model
|
|
101
|
+
|
|
102
|
+
### Write path (multiple writers allowed)
|
|
103
|
+
|
|
104
|
+
1. Caller: `api.append("tracker", "item_created", {"id": "x", ...}, actor="agent-1")`
|
|
105
|
+
2. `StateAPI` delegates to `EventStore.append()`.
|
|
106
|
+
3. `EventStore.append()` opens a new connection, sets `busy_timeout=5000` (wait up to 5 seconds if locked).
|
|
107
|
+
4. Executes `BEGIN IMMEDIATE` (acquires write lock up front).
|
|
108
|
+
5. Reads max version for the stream (e.g., currently 42).
|
|
109
|
+
6. Inserts the new event with version 43.
|
|
110
|
+
7. Commits (releases lock).
|
|
111
|
+
8. Returns version 43.
|
|
112
|
+
|
|
113
|
+
**Guarantee**: No two writers see the same max version. Two concurrent writers always get consecutive versions (43, 44) with no dupes or gaps.
|
|
114
|
+
|
|
115
|
+
**Tested**: `tests/test_state_store.py::EventStoreTest::test_concurrent_appends_have_no_dupes_or_gaps` — two threads, 50 appends each, verified versions 1–100 are consecutive.
|
|
116
|
+
|
|
117
|
+
### Read path (many readers in parallel)
|
|
118
|
+
|
|
119
|
+
1. Caller: `api.project("tracker")`
|
|
120
|
+
2. `StateAPI` gets all events: `store.read("tracker")`
|
|
121
|
+
3. `EventStore.read()` opens a connection, sets `busy_timeout=5000`.
|
|
122
|
+
4. Queries all events for the stream (ascending by version). In WAL mode, readers see a consistent snapshot (MVCC).
|
|
123
|
+
5. Returns list of dicts.
|
|
124
|
+
6. `project_tracker()` folds events into a projection (in-process, no DB access).
|
|
125
|
+
|
|
126
|
+
**Guarantee**: Readers never block writers (WAL mode). Readers see a consistent snapshot at the moment they start the read. Dirty reads are impossible; serialization is guaranteed by SQLite's MVCC.
|
|
127
|
+
|
|
128
|
+
### Snapshot coordination
|
|
129
|
+
|
|
130
|
+
1. `project_tracker_with_snapshot()` reads the latest snapshot (one extra query).
|
|
131
|
+
2. Folds only events after the snapshot version.
|
|
132
|
+
3. If the snapshot is corrupt, falls back to full replay (graceful degradation).
|
|
133
|
+
|
|
134
|
+
**Snapshot updates are asynchronous**: A background job can call `api._store.save_snapshot(stream="tracker", event_version=100, projection=...)` without blocking readers. The snapshot is just an optimization.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## What Stays Git-Authoritative (and Why)
|
|
139
|
+
|
|
140
|
+
### 1. Durable Checkpoint History
|
|
141
|
+
|
|
142
|
+
Git history (commit log, diffs, tags) remains **definitive for auditing and recovery**:
|
|
143
|
+
|
|
144
|
+
- Each time an export job runs, it writes a new commit to git.
|
|
145
|
+
- The commit hash anchors a specific version of all state streams.
|
|
146
|
+
- On session restart, `git log --oneline` shows when state changed, who changed it, and why.
|
|
147
|
+
- `git show <commit>` lets you see exactly what was decided at that moment.
|
|
148
|
+
|
|
149
|
+
**Why not move to SQLite?** SQLite has no built-in diff/review/approval workflow. Code review, pull requests, and merge discipline are git patterns. For team scale, retaining git as the audit log preserves these workflows.
|
|
150
|
+
|
|
151
|
+
### 2. Durability & Offline Safety
|
|
152
|
+
|
|
153
|
+
SQLite stores state **in the working tree**, which may get wiped, corrupted, or lost.
|
|
154
|
+
|
|
155
|
+
Git stores state **on remote servers** (GitHub, GitLab, etc.), which have redundancy, backups, and audit logs built in.
|
|
156
|
+
|
|
157
|
+
**Recommendation**: Always run `export_tracker()` and `export_orchestrator_status()` jobs after each major state change. Push the exports to git. If the SQLite store is corrupted, `ingest_tracker_json(git_version)` reconstitutes it.
|
|
158
|
+
|
|
159
|
+
### 3. Team Sync & Cross-Machine Continuity
|
|
160
|
+
|
|
161
|
+
When a developer or orchestrator switches machines or sessions:
|
|
162
|
+
|
|
163
|
+
1. Clone/pull the repo (gets the latest git state).
|
|
164
|
+
2. Reconstitute the event store: `ingest_tracker_json(api, state/tracker.json)` (tests line 165–179).
|
|
165
|
+
3. Continue from there.
|
|
166
|
+
|
|
167
|
+
**Why this works**: The ingest path is idempotent (see `state_store/ingest.py` line 14–41). Running it twice on the same events is a no-op (second run has no items to ingest, since they're already in the event log).
|
|
168
|
+
|
|
169
|
+
### 4. Configuration & Secrets
|
|
170
|
+
|
|
171
|
+
Aesop configuration (aesop.config.json, credentials, API keys) **does NOT go in state streams**. These remain git-ignored and managed separately.
|
|
172
|
+
|
|
173
|
+
Event payloads must not include secrets (enforced at the append call site, not in the store).
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Multi-Writer Concurrency: Measured (2026-07-18)
|
|
178
|
+
|
|
179
|
+
**Multi-writer safety is now verified under production load.** A concurrent-writer stress test (4 writer processes, 5s duration, WAL mode + `busy_timeout=5000` + `BEGIN IMMEDIATE`) appended 800 events across the tracker stream with zero dupes, zero gaps, zero "database is locked" failures. Measured throughput: ~704 events/sec. All projections converged (consistent state). This moves multi-writer support from "design" to "validated."
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Not Yet Built
|
|
184
|
+
|
|
185
|
+
### 1. **Orchestrator Reader/Writer Integration**
|
|
186
|
+
|
|
187
|
+
Currently, the orchestrator only writes to git (STATE.md). To move to the event store:
|
|
188
|
+
|
|
189
|
+
- Orchestrator `phase_changed` → write to stream `"orchestrator_status"` instead of STATE.md
|
|
190
|
+
- Orchestrator `next_steps_updated` → append event instead of git commit
|
|
191
|
+
- Orchestrator recovery: read `api.project("orchestrator_status")` to bootstrap phase + next steps
|
|
192
|
+
|
|
193
|
+
**Effort**: ~1 sprint. Touch: `skills/power/*, monitor/` event loop, orchestrator dispatch template.
|
|
194
|
+
|
|
195
|
+
### 2. **Dashboard Real-Time Subscription**
|
|
196
|
+
|
|
197
|
+
Currently, dashboards query the git export (STATE.md, tracker.json). For real-time updates:
|
|
198
|
+
|
|
199
|
+
- Add `subscribe(stream: str, callback)` to StateAPI
|
|
200
|
+
- Implement with SQLite's FTS/updates or a simple file-watch on the .db-wal file
|
|
201
|
+
- Dashboard websocket sends live updates to browsers
|
|
202
|
+
|
|
203
|
+
**Effort**: ~1 sprint. Touch: `ui/`, `state_store/api.py`.
|
|
204
|
+
|
|
205
|
+
### 3. **Team-Shared Database Setup**
|
|
206
|
+
|
|
207
|
+
The current design assumes a **local SQLite file** (filesystem-shared at best). For a true team:
|
|
208
|
+
|
|
209
|
+
- Backend swap: `StateAPI` points to Postgres instead of SQLite (seamless; only `api.py` changes).
|
|
210
|
+
- Setup: Postgres server, connection pooling, schema migration job.
|
|
211
|
+
- Tuning: Query optimization for high read/write load.
|
|
212
|
+
|
|
213
|
+
**Effort**: ~2 sprints. Mostly infrastructure/testing; application code is abstracted.
|
|
214
|
+
|
|
215
|
+
### 4. **Reconciliation & Conflict Resolution**
|
|
216
|
+
|
|
217
|
+
If two teams merge their state stores (e.g., two subagent fleets run independently, then reconcile):
|
|
218
|
+
|
|
219
|
+
- **Event deduplication**: No duplicate event ids; version numbers are per-stream (don't collide between stores).
|
|
220
|
+
- **Metadata tagging**: Each event carries an `actor` and `ts` (timestamp); reconciliation can detect/log conflicts.
|
|
221
|
+
- **Projection idempotence**: Refolding events is deterministic; the projection will converge to the same state.
|
|
222
|
+
|
|
223
|
+
**Current support**: `actor` field in every event. Reconciliation logic is **not yet implemented** (would live in `state_store/reconcile.py` or a separate tool).
|
|
224
|
+
|
|
225
|
+
**Effort**: ~1–2 sprints, depending on conflict resolution strategy (append-only, last-write-wins, domain-specific merge).
|
|
226
|
+
|
|
227
|
+
### 5. **Model-Dispatch Correlation**
|
|
228
|
+
|
|
229
|
+
The orchestrator needs to correlate which Haiku agent was given which backlog item and track per-item cost/success.
|
|
230
|
+
|
|
231
|
+
**Current event schema**: `orchestrator_status` events can carry item assignments, but **no current mechanism to append per-item traces** (which agent, which LLM, cost, tokens, result).
|
|
232
|
+
|
|
233
|
+
**Needed**:
|
|
234
|
+
- New stream: `"agent_traces"` with events like `agent_dispatched`, `agent_completed`, `agent_failed`.
|
|
235
|
+
- Agent brief template includes event-append credentials (stream + actor).
|
|
236
|
+
- Aggregation job correlates agent traces + cost logs + PR outcomes.
|
|
237
|
+
|
|
238
|
+
**Effort**: ~1–2 sprints. Touch: `dispatch template`, `monitor/`, cost logger.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Guarantees & Semantics
|
|
243
|
+
|
|
244
|
+
### Atomicity
|
|
245
|
+
|
|
246
|
+
Each `append()` call is atomic: either the event is inserted (and you get a version back) or it fails (exception). No partial writes.
|
|
247
|
+
|
|
248
|
+
Example:
|
|
249
|
+
```python
|
|
250
|
+
version = api.append("tracker", "item_created", {"id": "x", "title": "Y"}, actor="agent-1")
|
|
251
|
+
# version is now 43 (for stream "tracker")
|
|
252
|
+
# event is durable; will survive process restart
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Consistency
|
|
256
|
+
|
|
257
|
+
The event log is append-only; once an event is inserted, it never changes. The projection is deterministic: folding the same events always yields the same result.
|
|
258
|
+
|
|
259
|
+
Example:
|
|
260
|
+
```python
|
|
261
|
+
events_1 = api.get("tracker") # [v1, v2, v3]
|
|
262
|
+
proj_1 = project_tracker(events_1)
|
|
263
|
+
|
|
264
|
+
# Later:
|
|
265
|
+
events_2 = api.get("tracker") # [v1, v2, v3, v4, v5]
|
|
266
|
+
proj_2 = project_tracker(events_2)
|
|
267
|
+
|
|
268
|
+
# proj_2 differs from proj_1 only by v4+v5's changes (if any)
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
### Isolation
|
|
272
|
+
|
|
273
|
+
Readers see a consistent snapshot (SQLite MVCC). No dirty reads. Concurrent writes are serialized by `BEGIN IMMEDIATE`.
|
|
274
|
+
|
|
275
|
+
Example:
|
|
276
|
+
```python
|
|
277
|
+
# Thread 1: reader
|
|
278
|
+
snapshot_1 = api.get("tracker") # sees [v1..v50]
|
|
279
|
+
|
|
280
|
+
# Thread 2: writer (concurrent)
|
|
281
|
+
v51 = api.append(...) # inserts v51
|
|
282
|
+
|
|
283
|
+
# Thread 1: reader (still sees [v1..v50], consistent snapshot)
|
|
284
|
+
snapshot_1 # unchanged; snapshot_1 is immutable
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Durability
|
|
288
|
+
|
|
289
|
+
Events are flushed to disk (SQLite journal + WAL file). On process restart, all events from the last committed transaction are recovered.
|
|
290
|
+
|
|
291
|
+
Example:
|
|
292
|
+
```python
|
|
293
|
+
version = api.append("tracker", "item_created", {...}) # returns 43, durable
|
|
294
|
+
# If process crashes here, event v43 is still there on next start
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Idempotence (Export Fidelity)
|
|
298
|
+
|
|
299
|
+
The `ingest_tracker_json()` + `project_tracker()` + `export_tracker()` round-trip preserves item fidelity: ingesting a tracker.json and exporting the projection reproduces the same items.
|
|
300
|
+
|
|
301
|
+
Tested in `tests/test_state_store.py::ApiAndExportTest::test_ingest_project_export_round_trips_real_tracker`:
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
original = json.loads("state/tracker.json")
|
|
305
|
+
api.ingest_tracker_json("state/tracker.json")
|
|
306
|
+
projected = api.project("tracker")
|
|
307
|
+
assert projected["items"] == original["items"] # exact match
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## File Organization
|
|
313
|
+
|
|
314
|
+
**Event store layer** (new):
|
|
315
|
+
- `state_store/store.py` — SQLite WAL append-only event log.
|
|
316
|
+
- `state_store/projections.py` — Fold events into tracker state.
|
|
317
|
+
- `state_store/api.py` — StateAPI facade (append/get/project).
|
|
318
|
+
- `state_store/export.py` — Render projections to git-tracked JSON.
|
|
319
|
+
- `state_store/ingest.py` — Backfill events from existing JSON.
|
|
320
|
+
- `state_store/__init__.py` — Public exports.
|
|
321
|
+
|
|
322
|
+
**Tests** (new):
|
|
323
|
+
- `tests/test_state_store.py` — EventStore + projection + round-trip tests.
|
|
324
|
+
- `tests/test_state_store_snapshots.py` — Snapshot save/load fidelity.
|
|
325
|
+
- `tests/test_state_store_hardening.py` — Concurrency, corruption recovery.
|
|
326
|
+
- `tests/test_api_state.py` — StateAPI facade tests.
|
|
327
|
+
|
|
328
|
+
**Git exports** (existing, to be extended):
|
|
329
|
+
- `state/tracker.json` — Current tracker state (now an export, not source-of-truth).
|
|
330
|
+
- `STATE.md` — (Will become an export; currently single-writer orchestrator file.)
|
|
331
|
+
- `BUILDLOG.md` — (Will be merged into event streams; currently append-only progress log.)
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Deployment Checklist (for future waves)
|
|
336
|
+
|
|
337
|
+
- [ ] **Wave N+1**: Add `orchestrator_status` stream to `state_store/projections.py`.
|
|
338
|
+
- [ ] Orchestrator transition: phase/next-steps writes to event store (no git commits).
|
|
339
|
+
- [ ] Export job: periodically render `orchestrator_status` → `STATE.md` for git durability.
|
|
340
|
+
- [ ] Dashboard: read `api.project("orchestrator_status")` + git for fallback.
|
|
341
|
+
- [ ] Tests: verify orchestrator recovery from event stream (not git) works.
|
|
342
|
+
|
|
343
|
+
- [ ] **Wave N+2**: Tracker dual-read.
|
|
344
|
+
- [ ] Tracker CRUD: write events instead of git.
|
|
345
|
+
- [ ] Add dual-read logic: try event store, fall back to git.
|
|
346
|
+
- [ ] Export job: `export_tracker()` keeps `state/tracker.json` in sync.
|
|
347
|
+
- [ ] Tests: compare git + event-store projections; they must match.
|
|
348
|
+
|
|
349
|
+
- [ ] **Wave N+3**: Flip readers.
|
|
350
|
+
- [ ] Remove dual-read fallback.
|
|
351
|
+
- [ ] All reads go through `api.project()`.
|
|
352
|
+
- [ ] Git becomes audit trail only.
|
|
353
|
+
|
|
354
|
+
- [ ] **Wave N+4**: Postgres backend (optional, depends on team scale).
|
|
355
|
+
- [ ] Postgres schema + migration job.
|
|
356
|
+
- [ ] Backend swap in `StateAPI.__init__()`.
|
|
357
|
+
- [ ] Load testing + performance tuning.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## References & Code Citations
|
|
362
|
+
|
|
363
|
+
All design claims are grounded in actual implementation:
|
|
364
|
+
|
|
365
|
+
- **Concurrency safety**: `state_store/store.py` lines 30–94 (BEGIN IMMEDIATE + busy_timeout).
|
|
366
|
+
- **Snapshot replay**: `state_store/projections.py` lines 84–114 (project_tracker_with_snapshot).
|
|
367
|
+
- **Tracker projection**: `state_store/projections.py` lines 25–81 (event folding logic).
|
|
368
|
+
- **Round-trip fidelity**: `tests/test_state_store.py` lines 165–179 (ingest + project + export test).
|
|
369
|
+
- **Concurrent append test**: `tests/test_state_store.py` lines 90–108 (two threads, no dupes).
|
|
370
|
+
- **API facade**: `state_store/api.py` lines 15–35 (StateAPI.project seam for backend swaps).
|
|
371
|
+
|
|
372
|
+
See `state_store/CLAUDE.md` for domain-specific setup and next steps.
|
package/docs/reproduce.md
CHANGED
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
# Reproducing Aesop
|
|
2
2
|
|
|
3
|
-
This document explains how to reproduce the project's claims from a clean checkout.
|
|
3
|
+
This document explains how to reproduce the project's claims from a clean checkout or installed package.
|
|
4
|
+
|
|
5
|
+
## Quick Start: One-Command Verification
|
|
6
|
+
|
|
7
|
+
Run the verification suite with automatic context detection:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
aesop reproduce
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This command automatically detects your context (repository checkout or installed package) and runs the appropriate test suite:
|
|
14
|
+
|
|
15
|
+
- **Repository checkout**: Runs the full test suite (Node syntax, shell syntax, Node tests, Python tests, benchmark, browser proofs)
|
|
16
|
+
- **Installed package**: Runs shipped self-checks (preflight checks, health score, secret-scan selftest, packaging assertions)
|
|
17
|
+
|
|
18
|
+
Output includes per-step timing and a clear pass/fail summary. Exit code 0 = all checks passed.
|
|
4
19
|
|
|
5
20
|
## Overview
|
|
6
21
|
|
|
@@ -55,20 +70,35 @@ Both can be reproduced entirely offline, without API keys or external dependenci
|
|
|
55
70
|
python tools/bench_runner.py --runner mock
|
|
56
71
|
```
|
|
57
72
|
|
|
58
|
-
##
|
|
73
|
+
## CI Equivalence: aesop reproduce vs. GitHub Actions
|
|
59
74
|
|
|
60
|
-
The `reproduce`
|
|
75
|
+
The `aesop reproduce` command mirrors `.github/workflows/reproduce.yml` for local execution.
|
|
76
|
+
|
|
77
|
+
### GitHub Actions Workflow (`reproduce.yml`)
|
|
78
|
+
|
|
79
|
+
The `reproduce` workflow in GitHub Actions automates testing:
|
|
61
80
|
|
|
62
81
|
1. **Fresh checkout**: Uses a clean clone (no state reuse from other CI jobs)
|
|
63
82
|
2. **Syntax checks**: Validates all Node.js (.mjs) and shell scripts (.sh)
|
|
64
83
|
3. **Node.js tests**: Runs the full test suite with `npm run test:node`
|
|
65
84
|
4. **Python tests**: Runs the full test suite with `python -m unittest discover`
|
|
66
85
|
5. **Benchmark scorer**: Proves the offline mock benchmark reproduces (no external API calls)
|
|
86
|
+
6. **Browser proofs**: Validates UI components with Playwright
|
|
67
87
|
|
|
68
88
|
The workflow is triggered:
|
|
69
89
|
- **Manually**: Via GitHub Actions "Run workflow" button (workflow_dispatch)
|
|
70
90
|
- **Weekly**: Every Sunday at 2:00 AM UTC (schedule)
|
|
71
91
|
|
|
92
|
+
### Local Reproduction
|
|
93
|
+
|
|
94
|
+
To run the same tests locally from a clean clone:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
aesop reproduce
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This produces the same verification steps and provides immediate feedback before pushing.
|
|
101
|
+
|
|
72
102
|
## What Is and Isn't Reproduced
|
|
73
103
|
|
|
74
104
|
### Reproduced (offline, no API keys needed)
|
package/driver/CLAUDE.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# driver/ — AgentDriver backend-portability seam
|
|
2
|
+
|
|
3
|
+
**What**: the domain contract letting aesop's wave loop run on non-Claude backends
|
|
4
|
+
(Codex, open models); the loop dispatches only through the `AgentDriver` interface.
|
|
5
|
+
**Phases 1-3 shipped** (interface + reference adapter; Codex Chat Completions; wave bridge).
|
|
6
|
+
|
|
7
|
+
## Files
|
|
8
|
+
|
|
9
|
+
- **agent_driver.py** — the `AgentDriver` ABC + capability/request/result
|
|
10
|
+
dataclasses. The contract. stdlib-only, no provider SDKs.
|
|
11
|
+
- **claude_code_driver.py** — reference adapter (Claude Code parity). Thin +
|
|
12
|
+
documented: two ops are concrete Python, three are serviced by the harness.
|
|
13
|
+
- **codex_driver.py** — Phase 2 IMPLEMENTATION: OpenAI Chat Completions HTTP
|
|
14
|
+
backend. Fully wired: dispatch_worker (file injection, JSON validation with
|
|
15
|
+
retry, full-file replacement), run_command (subprocess), worker_status
|
|
16
|
+
(in-memory registry), get_tokens_spent (aggregate usage). Transport injectable
|
|
17
|
+
for offline testing.
|
|
18
|
+
- **openai_transport.py** — stdlib urllib transport for OpenAI Chat Completions
|
|
19
|
+
endpoint. Injectable seam so tests feed canned responses (FakeTransport) with
|
|
20
|
+
no API key or network.
|
|
21
|
+
- **openai_compatible_driver.py** — OpenAI-compatible backend (Ollama, OpenRouter, etc.).
|
|
22
|
+
- **verification_policy.py** — Maps verification tier -> orchestrator tuning (validate_all_json,
|
|
23
|
+
spot_check_frac, repair_cap, require_adversarial_review).
|
|
24
|
+
- **wave_loop.py** — the wave ENGINE: preflight ownership guard, parallel build,
|
|
25
|
+
bounded repair, adversarial review, per-repo batched git ship, recovery journal.
|
|
26
|
+
- **wave_bridge.py** — Phase 3: bridges AgentDriver backends to wave manifest items.
|
|
27
|
+
build_manifest_item() enriches with verificationTier + model; dispatch_item() routes
|
|
28
|
+
by capability and decides green ONLY from test exit code (not model's say-so).
|
|
29
|
+
- **backend_config.py** — Per-deployment model resolution (role → model id, API key/base URL).
|
|
30
|
+
- **README.md** — the abstraction, the phased roadmap, the verification thesis.
|
|
31
|
+
- **../tests/test_agent_driver.py** — the contract's test suite.
|
|
32
|
+
- **../tests/test_codex_driver_e2e.py** — Phase 2 end-to-end offline tests
|
|
33
|
+
(FakeTransport, red-to-green verification, retry logic, ownership enforcement)
|
|
34
|
+
+ gated live test (AESOP_CODEX_LIVE env var).
|
|
35
|
+
- **../tests/test_wave_bridge.py** — Phase 3 offline e2e (honest green: exit 0 only).
|
|
36
|
+
no network). Tests: manifest building, routing, fail-safe, ownership enforcement,
|
|
37
|
+
headline test (red stub + FakeTransport fix + test pass -> ok=True).
|
|
38
|
+
|
|
39
|
+
## The five operations (what the wave loop needs from ANY backend)
|
|
40
|
+
|
|
41
|
+
1. `probe_capabilities() -> DriverCapabilities` — honest self-report (parallel?
|
|
42
|
+
filesystem? shell? structured output? worktree? cost tracking? accuracy? →
|
|
43
|
+
recommended verification tier). Read once; everything keys off it.
|
|
44
|
+
2. `dispatch_worker(request) -> WorkerResult` — spawn ONE isolated worker over a
|
|
45
|
+
prompt + owned_files + workdir; the worker may read/write files, run a shell
|
|
46
|
+
command, and return a **structured** result (extent reported by the probe).
|
|
47
|
+
3. `worker_status(worker_id) -> WorkerStatus` — liveness / stall detection for
|
|
48
|
+
the watchdog.
|
|
49
|
+
4. `run_command(command, cwd, shell) -> CommandResult` — ORCHESTRATOR-side
|
|
50
|
+
command execution (tests, git, verification). Distinct from a worker shell.
|
|
51
|
+
5. `resolve_model(role) -> str` — map an abstract role (`worker`/`setup`/
|
|
52
|
+
`verify`) to a concrete backend model id.
|
|
53
|
+
|
|
54
|
+
Optional (non-abstract): `get_tokens_spent()`.
|
|
55
|
+
|
|
56
|
+
## Invariants
|
|
57
|
+
|
|
58
|
+
- The wave loop calls **only** `AgentDriver` methods — never `agent()`,
|
|
59
|
+
`parallel()`, Read/Write/Bash tools, or `budget.spent()` directly. That is
|
|
60
|
+
the seam.
|
|
61
|
+
- `probe_capabilities()` must be **honest**. Defaults are conservative (no
|
|
62
|
+
native abilities, accuracy 0.0, tier 4) — optimism is opt-in, never default.
|
|
63
|
+
- **Weaker workers → higher verification tier.** Lower `tool_use_accuracy`
|
|
64
|
+
raises `recommended_verification_tier`. Cheaper/weaker backends RAISE the
|
|
65
|
+
orchestrator's burden; they do not lower it.
|
|
66
|
+
- Unknown roles in `resolve_model()` fall back to the worker model — a mis-typed
|
|
67
|
+
role can never silently escalate cost.
|
|
68
|
+
- stdlib-only (`abc`, `dataclasses`, `typing`, `subprocess`), ASCII-only,
|
|
69
|
+
Windows + Linux safe. Concrete adapters own any provider SDK, not this layer.
|
|
70
|
+
|
|
71
|
+
## Per-backend capability matrix (as encoded)
|
|
72
|
+
|
|
73
|
+
| Capability | claude-code | codex (Phase 2) |
|
|
74
|
+
|-----------------------|:------------:|:---------------------:|
|
|
75
|
+
| parallel_dispatch | yes | no (ext loop) |
|
|
76
|
+
| worker filesystem | yes | no (orch injects) |
|
|
77
|
+
| worker shell | yes | no (orch runs) |
|
|
78
|
+
| structured_output | yes (~perfect)| yes (JSON schema) |
|
|
79
|
+
| worktree_isolation | yes | no (temp-dir) |
|
|
80
|
+
| native_cost_tracking | yes | yes (usage metadata) |
|
|
81
|
+
| tool_use_accuracy | ~0.99 | ~0.92 |
|
|
82
|
+
| verification_tier | 1 | 2 |
|
|
83
|
+
|
|
84
|
+
## Phase 2 Codex Implementation Details
|
|
85
|
+
|
|
86
|
+
Codex driver (Tier 2): injects file contents into prompt, calls OpenAI Chat Completions via injectable transport, validates JSON with bounded retry, enforces ownership. CRITICAL: Green = exit 0 only. Verification policy: tier 2 -> {validate_all_json:True, spot_check_frac:0.50, repair_cap:2, require_adversarial_review:True}. **P1 Security**: Default model map uses gpt-4o-mini (worker, supports json_schema); init-time guard rejects models lacking json_schema support unless `allow_unverified_models=True` (P1 gate: prevent gpt-3.5-turbo silent failures).
|
|
87
|
+
|
|
88
|
+
## Phase 3 Bridge Implementation Details
|
|
89
|
+
|
|
90
|
+
Connects AgentDriver backends to wave-flat-dispatch manifest items; verification
|
|
91
|
+
tier is driven by backend capability, not config.
|
|
92
|
+
|
|
93
|
+
**build_manifest_item(driver, item) -> dict**: enriches a backlog item with model
|
|
94
|
+
(driver.resolve_model), verificationTier (probe), and the four policy knobs from
|
|
95
|
+
verification_policy(caps) — RESOLVED ONCE, carried as literal manifest fields so the
|
|
96
|
+
template cannot recompute/drift; tier-1/Claude path stays byte-identical (repairCap=1,
|
|
97
|
+
requireAdversarialReview=false, spotCheckFrac=0.10, validateAllJson=false).
|
|
98
|
+
|
|
99
|
+
**dispatch_item(driver, item) -> dict**:
|
|
100
|
+
- Routes by worker_filesystem_access: True→{route:'harness'}; False→orchestrator-managed
|
|
101
|
+
(dispatch_worker + run_command test). Returns {route, ok, testExit, filesWritten, verified, ...}.
|
|
102
|
+
- HONESTY: Green (ok=True) ONLY on test exit 0; never from model's done:true. No testCmd
|
|
103
|
+
→ unverified (ok=False, verified=False, reason='no_test_command'): "no test" ≠ "verified."
|
|
104
|
+
- Fail-safe: exception → ok=False, verified=False. Ownership at driver level.
|
|
105
|
+
|
|
106
|
+
**Tests**: prove a non-Claude backend (Codex + FakeTransport) takes a RED unittest
|
|
107
|
+
stub, applies a fix, runs the test, and returns ok=True ONLY because the test passed
|
|
108
|
+
(exit 0). All offline, no API key, no network.
|
|
109
|
+
|
|
110
|
+
## Wave Scheduler (WS3a Pilot) + GATE-1 Handoff Kit
|
|
111
|
+
|
|
112
|
+
**wave_scheduler.py** — single-cycle backlog orchestration: intake up to N file-disjoint todo items from tracker.json (empty/missing ownsFiles REJECTED; paths normalized posix+casefold-on-Windows before overlap checks; required fields pre-validated) -> manifest via build_manifest_item (model + verificationTier from driver.probe) -> HALT + cost-ceiling gates (fail-CLOSED: module import failure or check exception = abort with honest Report, phase=gate_unavailable) -> run_wave (recovery journal + git ship) -> STOP before merge; Report JSON with per-item observability (GATE-1). After ship, selected items atomically marked in_progress in tracker (temp+os.replace; dry-run never mutates) so a second run cannot double-dispatch.
|
|
113
|
+
|
|
114
|
+
**CLI** (GATE-1): `python driver/wave_scheduler.py --tracker <path> --max-items N --dry-run|--execute --driver claude|codex` (default: claude). For codex+execute, requires OPENAI_API_KEY env var; dry-run works without it.
|
|
115
|
+
|
|
116
|
+
**Tests** (35+): disjoint/normalization/rejection, gate fail-closed, dry-run, GATE-1 per-item/driver/ceiling/codex tests; module-tmpdir hygiene; all TestCase.
|
|
117
|
+
|
|
118
|
+
**Invariants**: stdlib-only, ASCII, Windows+Linux safe (list-form subprocess); manifest items carry resolved policy knobs from verification_policy (no recompute drift); merge stays manual in the pilot.
|
|
119
|
+
|
|
120
|
+
### REPORT-CONTRACT (GATE-1 Orchestrator Handoff)
|
|
121
|
+
|
|
122
|
+
The scheduler emits a Report JSON consumed by the orchestrator to decide merge eligibility:
|
|
123
|
+
```
|
|
124
|
+
{
|
|
125
|
+
"phase": "dispatch"|"intake"|"halt"|"ceiling"|"gate_unavailable"|"manifest",
|
|
126
|
+
"wave_id": "<uuid>",
|
|
127
|
+
"items_selected": ["id1", "id2"], // IDs selected for this wave
|
|
128
|
+
"items_shipped": [
|
|
129
|
+
{ "slug": "feat/x", "backend": "claude-code|codex", "tier": 1|2|3|4, "verified": true|false (false = NOT PROVEN, not necessarily failed; tier null = no build record), "testExit": 0|1|null },
|
|
130
|
+
...
|
|
131
|
+
],
|
|
132
|
+
"merged": false, "tracker_update_attempted": true|absent, "tracker_unmapped_slugs": [..]|absent (LOUD -> success false), // pilot always false (manual merge)
|
|
133
|
+
"success": true|false,
|
|
134
|
+
"timestamp": "<ISO8601>",
|
|
135
|
+
"branch": "<branch_name>", // optional; set when ship succeeds
|
|
136
|
+
"sha": "<git_sha>", // optional; set when ship succeeds
|
|
137
|
+
"halt_reason": "...", // optional; set if halted
|
|
138
|
+
"ceiling_reason": "...", // optional; ceiling exceeded before dispatch (abort-before-dispatch only)
|
|
139
|
+
"error": "..." // optional; unexpected error
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
Ceiling semantics: checked BEFORE run_wave dispatch (phase=ceiling, ceiling_reason populated). Mid-wave ceiling trips are run_wave's responsibility. items_shipped[] carries per-item observability: slug, backend (driver name), tier (verificationTier), verified (from test exit 0), testExit (test command exit code or null if not run).
|
|
143
|
+
|
|
144
|
+
## Status
|
|
145
|
+
|
|
146
|
+
- **Phase 1**: shipped. Interface + Claude reference adapter + contract tests.
|
|
147
|
+
- **Phase 2**: shipped. Codex OpenAI Chat Completions implementation. Offline tests GREEN.
|
|
148
|
+
- **Phase 3**: shipped. Wave bridge: driver → manifest, orchestrator-side dispatch.
|
|
149
|
+
Proves non-Claude backends drive items end-to-end with honest green (test exit 0 only).
|
|
150
|
+
- **Wave Scheduler (WS3a) + GATE-1**: shipped. Single-cycle orchestration: intake → manifest → dispatch → report (manual merge). Per-item observability, driver injection (--driver claude|codex), ceiling semantics documented.
|