refactor(engine): extract worktree isolation flow into WorktreeFlow (#244, PR 1/2)#265
Conversation
…244) Engine's worktree-isolation/integration cluster (the `_isolated` .. `_reopen_unit` band) is an independent state machine. Carve it into a `WorktreeFlow` collaborator built from narrow deps (paths, policy, run state, journal, plugin registry) plus engine callbacks — never the whole Engine: - `emit` is late-bound (a lambda over `self._emit`) so a monkeypatched `Engine._emit` still wins; `adapters` and the `open_unit_workspace` module global are read live for the same reason (tests rebind both); - `escalation_pause` raises the engine's `RunPaused`, injected so worktree_flow need not import `engine` (which would reintroduce a runtime<->engine cycle); - `workspace_get/set` read and swap the engine's live workspace. Engine keeps same-name private delegators (`_run_isolated`, `_gc_run_worktrees`, …) so test_engine*.py and the SweepEngine/StoriesEngine subclasses stay byte-untouched and green. F-9a: rehome `provision_worktree` (and `_setup_mcp_agent_id`) out of the installer so the runtime control loop no longer imports `install`; `install.py` re-exports `provision_worktree` lazily (PEP 562 `__getattr__`) to avoid an install<->worktree_flow import cycle, and engine re-exports `_setup_mcp_agent_id` for its plugin tests. Behavior-preserving; rollback/preserve cluster (PR 2) untouched. Adds tests/test_worktree_flow.py exercising the collaborator in isolation.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
WalkthroughWorktree provisioning and per-unit isolation are extracted into ChangesWorktree flow extraction
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…bmad-code-org#244) Findings F-3 of the brownfield refactor assessment, PR 2/2 (PR 1 was the worktree cluster, bmad-code-org#265). Engine's rollback/preserve cluster is an independent recovery state machine; carve it into a `RecoveryFlow` collaborator built from narrow deps (repo paths, policy, run state, journal, run dir) plus a getter for the engine's swappable active workspace and a few engine callbacks (emit a plugin hook, save state, escalate a task, escalation-pause). The collaborator never receives the whole Engine. Same pattern as WorktreeFlow: Engine keeps same-name delegating private methods (`_rollback_or_pause`, `_safe_reset`, `_restore_patch`, `_prune_preserve_refs`, `_preserve_attempt_*`, `_pause_for_manual_recovery`, `_protected_relpaths`) so test_engine*.py stays byte-untouched and the SweepEngine/StoriesEngine subclasses (which override nothing in this cluster, and call `_rollback_or_pause` /`_safe_reset` through the inherited delegators) are unchanged. `emit` is late-bound so a monkeypatched `Engine._emit` still wins; `RunPaused` is raised via the injected `escalation_pause`/`escalate` callbacks so recovery_flow need not import engine (avoids a runtime<->engine cycle). Behavior-preserving only. New unit tests exercise the collaborator in isolation in tests/test_recovery_flow.py. engine.py shrinks by ~333 lines. The hook-bus cluster is left in place (explicitly out of scope). Closes bmad-code-org#244.
PR bmad-code-org#265 moved provision_worktree out of install.py into worktree_flow.py after this branch was cut, so the seeding-loop half of the change conflicted. Resolution: install.py keeps only the _copy_traversable changes (the helper still lives there and worktree_flow imports it); the provision_worktree docstring + dir-entry seeding branch are re-applied verbatim in worktree_flow.py. Tests and docs merged clean.
PR 1 of 2 for #244 (findings F-3 + F-9a). Worktree cluster only — the rollback/preserve cluster and the plugin hook bus stay in
engine.py(PR 2 / out of scope).What
engine.py's worktree isolation/integration band (_isolated…_reopen_unit) is an independent state machine. It moves into a newsrc/bmad_loop/worktree_flow.pyas aWorktreeFlowcollaborator, built from narrow deps (paths, policy, run state, journal, plugin registry) + a handful of engine callbacks — never the wholeEngine.Enginekeeps same-name private delegators (_run_isolated,_integrate_unit,_gc_run_worktrees,_ensure_target_branch, …) that forward to the collaborator. This is the acceptance criterion:tests/test_engine*.pyare byte-untouched, andSweepEngine/StoriesEngine(which override nothing in this cluster) see an unchanged surface.Callback seams (why they exist)
emitis late-bound (a lambda overself._emit) so a test's monkeypatchedEngine._emitstill fires from the worktree paths.adapters(getter) and theopen_unit_workspacemodule global are read live — two existing tests rebind them after construction (engine.adapters = …,eng.open_unit_workspace = …) and expect the flow to observe it.escalation_pauseraises the engine'sRunPaused, injected soworktree_flowneed not importengine(that would re-introduce a runtime↔engine import cycle).workspace_get/setread and swap the engine's liveself.workspace.F-9a — runtime no longer imports the installer
provision_worktree(and the_setup_mcp_agent_idhelper) move out soengine.pydropsfrom .install import provision_worktree.install.pyre-exportsprovision_worktreelazily via PEP 562__getattr__(a module-level re-export would form aninstall↔worktree_flowcycle, since the moved function still uses installer helpers).enginere-exports_setup_mcp_agent_idfortest_engine_plugin.py.Behavior
Behavior-preserving. No CHANGELOG (internal refactor).
Tests
test_module_skills_sync::…bmad-loop-setupfailures are pre-existing local skill-tree drift — theyskipon a clean checkout and touch nothing in this diff.)trunk check --no-fixclean.tests/test_worktree_flow.pyexercises the collaborator in isolation (noEngine): target-branch resolution incl. detached-HEAD pause, deferral on worktree-open failure, escalate/notify/pause, profile+agent-id dedup with live-adapter rebind, and theprovision_worktreere-export identity.Summary by CodeRabbit
New Features
Bug Fixes
Tests