Skip to content

feat: wire the fusion rung into the board seam (ADR 0064 P3)#64

Merged
mabry1985 merged 1 commit into
mainfrom
feat/coder-seam-fusion-rung
Jul 3, 2026
Merged

feat: wire the fusion rung into the board seam (ADR 0064 P3)#64
mabry1985 merged 1 commit into
mainfrom
feat/coder-seam-fusion-rung

Conversation

@mabry1985

Copy link
Copy Markdown
Contributor

What

coder.solve()'s ladder always supported fusion (rung 4: a richer generator, oracle-selected) but the board seam (P2, #61) never wired it — solve() was called with no fusion_generate at all, so no board dispatch could ever reach rung 4, no matter how many cheaper rungs failed. This is ADR 0064's explicit P3.

The design constraint: fusion can't tool-call

Per the ADR, fusion (e.g. protolabs/fusion) is a strong generator but a plain chat completion — unlike the acp coder (a real edit/verify session in the worktree), it can't read files or run anything. So its candidate generation is a genuinely different shape from the ACP rungs:

  • _fusion_prompt hands it the task + the current content of the feature's declared files_to_modify (read from the base repo — fusion has no tool access to look these up itself), and asks for the complete, final content of every file it creates or changes (wholesale replacement, not a diff — an LLM reliably reproduces a full file; a hand-rolled patch with drifted context lines is a git apply failure mode this avoids).
  • _parse_fusion_files extracts {path: content} from the reply.
  • generate_fusion writes those files into a fresh worktree (same throwaway-per-candidate discipline as the ACP rungs, same candidates bookkeeping) and hands the path to the same verify() — real acceptance tests, no separate judge.
  • Guards against a completion naming an absolute path or a ../ climb escaping the worktree (a model completion is untrusted input).

Config

project_board:
  coder_solve_fusion_delegate: "fusion"  # an openai-type delegate name; blank = no rung 4
  coder_solve_fusion_k: 2

Resolved in _drive the same way coder/reviewer already are (_resolve_delegate(name, "openai")). Blank/unresolved ⇒ solve() gets fusion_generate=Noneidentical to today's behavior, honest degrade unchanged.

Tests

255 passed (was 244; +11 new — file-format parsing, prompt construction, generate_fusion's write/traversal-guard/empty-reply behavior, dispatch() reaching fusion end-to-end after simulated cheaper-rung failures, and the no-delegate honest-degrade path; +9 existing _fake_solve fixtures updated for the new fusion_generate/fusion_k kwargs solve() now always receives).

Gate: ruff check . && ruff format --check . && pytest -q — all green.

Version bumped 0.27.0 → 0.28.0 (new capability).

🤖 Generated with Claude Code

coder.solve()'s ladder always supported fusion (rung 4: a richer generator,
oracle-selected) but the board seam (P2) never wired it -- solve() was called
with no fusion_generate at all, so no board dispatch could ever reach it,
regardless of how many rungs failed.

Fusion can't tool-call (a plain chat completion, e.g. protolabs/fusion -- not
an ACP session), so its candidate generation is a different shape from the
ACP rungs: _fusion_prompt hands it the task + the CURRENT content of the
feature's declared files_to_modify (read from the base repo -- fusion has no
tool access to look these up itself) and asks for the complete, final content
of every file it creates or changes. _parse_fusion_files extracts {path:
content} from the reply; generate_fusion writes those files into a fresh
worktree (same throwaway-per-candidate discipline as the ACP rungs, same
promote/reap bookkeeping) and hands the path to the SAME verify() -- real
acceptance tests, no separate judge. Guards against a completion naming an
absolute path or a ../ climb escaping the worktree.

New config: coder_solve_fusion_delegate (an openai-type delegate name,
resolved by _drive the same way coder/reviewer already are), coder_solve_fusion_k.
Blank delegate (default) -> solve() gets fusion_generate=None, identical to
before this rung existed -- honest degrade, unchanged.

Tests: file-format parsing, prompt construction (existing-file content,
missing-file note, feedback), generate_fusion (writes correctly, rejects path
traversal, handles an empty/unparseable reply without crashing), dispatch()
end-to-end reaching fusion after cheaper rungs fail, and the no-delegate
honest-degrade path. 255 passed (was 244; +11 new, +9 existing _fake_solve
signatures updated for the new fusion_generate/fusion_k kwargs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@protoquinn

protoquinn Bot commented Jul 3, 2026

Copy link
Copy Markdown

👀 Quinn is reviewing — verdict (PASS / WARN / FAIL) + findings to follow.

@protoquinn protoquinn Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA Audit — PR #64 | feat: wire the fusion rung into the board seam (ADR 0064 P3)

VERDICT: WARN (CI queued — non-blocking comment; formal PASS/FAIL will follow once test is terminal)


CI Status

  • test: queued

Diff Review

  • coder_seam.py: Adds fusion rung (P3) to the solve ladder — _fusion_prompt(), _parse_fusion_files(), new generate_fusion (in truncated portion), _new_candidate_worktree() extracted for reuse. New __init__ params (fusion_delegate, files_to_modify, _fusion_dispatch seam) are cleanly defaulted for honest degrade.
  • README.md: Documents the two new config keys and the fusion rung in the ladder description. Clear opt-in semantics.

Observations

  • Gap — truncated diff: Only 200/698 lines visible. The generate_fusion method, path-traversal guards (promised in the PR description: "Guards against a completion naming an absolute path or a ../ climb"), dispatch wiring, and 11 new tests are all in the unseen portion. Cannot verify these from the diff alone.
  • Gap — clawpatch unavailable: HTTP 502 on checkout — structural cross-file review skipped. The diff is large enough (698 lines, multiple files) that a structural pass would have caught any cross-file contract mismatches.
  • LOW: _FUSION_FILE_RE uses .*? between backtick fences — file content containing triple backticks (e.g. a Markdown file) would cause misparse. This is documented as acceptable: misparse → empty candidate → fails verify(), same as any other empty candidate. No silent corruption path.
  • LOW: _fusion_prompt() reads files from the base repo via Path(repo) / rel — this is correct for P3's design constraint (fusion can't tool-call, needs to be handed file contents), and the _FUSION_READ_MAX_CHARS = 20_000 cap prevents unbounded prompt growth.

— Quinn, QA Engineer

@mabry1985 mabry1985 merged commit d479f53 into main Jul 3, 2026
1 check passed
@protoquinn

protoquinn Bot commented Jul 3, 2026

Copy link
Copy Markdown

Submitted COMMENT review on #64.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant