Commit 1b323fc
feat(loop): wire ADR 0064 P2 board seam — execution-grounded coder.solve() (#61)
* feat(loop): wire ADR 0064 P2 board seam — execution-grounded coder.solve()
On a fresh build, when the `coder` plugin is importable, the feature has
acceptance criteria, and a runnable acceptance-test command is configured,
the loop now dispatches through coder.solve()'s execution-grounded ladder
(greedy → best-of-k → tree-search) instead of a single delegate_to(acp)
shot — gated on the feature's acceptance tests actually passing in a real
candidate worktree, never an LLM judge.
- coder_seam.py: a thin adapter composing plugins.coder.solve (imported
lazily/best-effort — no hard dependency on the coder plugin). generate()
creates a fresh throwaway worktree per candidate and dispatches the ACP
coder into it; verify() runs the configured acceptance-test command in
that worktree and reports real pass/fail. The winner is promoted to the
feature's canonical worktree/branch; losers are reaped. A spent budget
with no passing candidate raises SolveExhausted (a WorktreeError
subclass), which the existing capability-failure handling treats exactly
like NoChangesError/CoderTimeout — so the coders-map tier ladder still
climbs when search itself stalls.
- Honest degrade: should_use_solve requires all three gates (coder
importable + acceptance_criteria + a test command); missing any falls
back to today's single shot, so no existing deployment can regress.
- store.py: record_gens_spent accumulates coder.solve()'s generation cost
onto a `gens:<total>` label; `_project` surfaces it as `gens_spent` so
portfolio_rollup can read cost without raw reads.
- Deferred: compiling EARS acceptance criteria into generated tests. The
coder is already prompted to write tests satisfying the acceptance
criteria as part of its definition of done; verify() runs whatever tests
exist via the configured command rather than fabricating them.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(coder_seam): reap candidate worktrees when solve() raises mid-ladder
coder.solve() (plugins/coder/solve.py) has no try/except around its injected
generate()/verify() callables — it assumes a candidate attempt only ever fails
its tests, never errors. But a real ACP dispatch can raise for real (CoderTimeout
on one best-of-k candidate, a worktree op erroring), and that propagated straight
out of dispatch() uncaught, leaking every worktree already created for that run:
untracked in the loop's `_inflight` map until dispatch() returns, and invisible
to the health sweep (a `.gN` candidate id isn't a real board feature, so the
sweep's own get_feature() lookup raises and it skips reaping). This was a much
more common trigger than the "crash mid-ladder" case the PR already called out
as a known limitation — a single slow candidate in best-of-k hitting
coder_timeout_s would hit it on any normal run.
Wrap the solve() call so any exception reaps every candidate seen so far,
surfaces the attempted generation count as spent cost (a failed dispatch still
spent the gen), and re-raises the original exception unchanged so the loop's
existing capability-failure classification (retry/escalate/block) still applies.
Also drop the `typing.Optional` usage in this file for the `X | None` style
every other module here already uses under `from __future__ import annotations`,
and hoist the (always-available, stdlib) `importlib` import to the top of the
file with the rest of the module's imports.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* fix(coder_seam,loop): guard record_gens fire-and-forget; fix Max-Mode/coder_solve precedence
Two adversarial-review blockers on the ADR 0064 P2 board seam:
- coder_seam.dispatch()'s two record_gens(...) calls (mid-ladder-raise and
post-solve()) were unguarded, even though store.record_gens_spent's own
docstring promises fire-and-forget semantics. A transient BoardError from
a `br` hiccup would propagate past _drive's capability-failure handling
into the outer `except BoardError`, which never reaps a worktree — leaking
an already-verified (or already-reaped) candidate and blocking the feature
over a bookkeeping label write. Wrapped both call sites in a shared
_record_gens_best_effort() helper that logs and swallows.
- _use_coder_solve() let coder.solve() preempt Max-Mode purely because the
separate `coder` plugin became importable, even on a board already
configured with max_mode_n>1 + local_gate_cmd (the README's own documented
execution-grounded Max-Mode recipe). Since coder_solve defaults on and its
test-cmd falls back to local_gate_cmd, that board flips from "always ships
a best-effort PR" to "blocks outright on an exhausted search" with zero
config change of its own. coder.solve now only preempts Max-Mode when
max_mode_n<=1; a board wanting the ladder over Max-Mode must opt in
explicitly.
Added tests pinning both fixes (record_gens raising on the promoted-winner,
exhausted, and mid-ladder-raise paths; Max-Mode-wins-precedence).
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>1 parent 1e40213 commit 1b323fc
9 files changed
Lines changed: 1414 additions & 6 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
46 | 46 | | |
47 | 47 | | |
48 | 48 | | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
49 | 59 | | |
50 | 60 | | |
51 | 61 | | |
| |||
99 | 109 | | |
100 | 110 | | |
101 | 111 | | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
102 | 117 | | |
103 | 118 | | |
104 | 119 | | |
| |||
124 | 139 | | |
125 | 140 | | |
126 | 141 | | |
| 142 | + | |
127 | 143 | | |
128 | 144 | | |
129 | 145 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
0 commit comments