feat: Phase 4 parallel file-disjoint workers (#37)#57
Merged
Conversation
Completes the final phase of the loop-engineering roadmap: run multiple specs concurrently, safely, by scheduling on file-disjointness rather than lock coordination — and only now that Phase 1 stall detection exists to bound the cost of missing progress. File-disjoint scheduling (spec_queue.py) — specs declare a `## Files` scope; get_parallel_batch() greedily selects a set of ready specs whose scopes don't overlap (glob + directory-prefix aware), up to N workers. A spec with no declared scope runs alone — correctness over parallelism. Ready specs are already dependency-independent, so scheduling only reasons about file scope. Shared verification gate (verification.py) — the deterministic gate (acceptance criteria + backpressure via subprocess, plus the tamper-guard hash) is extracted into one module that both the sequential engine and the parallel orchestrator call. There is exactly one definition of "verified"; the engine now delegates to it (no behavior change). Parallel orchestrator (parallel.py) + owloop run --workers N — each worker runs one target spec in its own git worktree on its own branch: implement -> shared gate -> commit. The orchestrator merges each passed branch into the base branch; because the batch is file-disjoint those merges never conflict. A round with zero progress counts toward a stall (StopReason.STALLED) so a parallel run can't spin forever. Fresh adapter per worker (adapters hold per-run streaming state). Default --workers 1 keeps the existing sequential engine untouched. Spec template + generator now teach the `## Files` scope and disjoint decomposition; CLAUDE.md documents the file-disjoint principle and the shared gate. Tests: test_parallel.py (real worktrees + merges, stall, preflight) and file-disjoint scheduling tests in test_spec_queue.py. Full suite 307 passed; ruff + mypy clean.
# Conflicts: # CLAUDE.md # src/owloop/engine.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The final phase of the #37 roadmap: run multiple specs concurrently, safely, by scheduling on file-disjointness rather than lock coordination — and only now that Phase 1 stall detection exists to bound the cost of missing progress. Builds on merged Phases 1–3.
File-disjoint scheduling (
spec_queue.py)Specs declare a
## Filesscope.get_parallel_batch(specs_dir, N)greedily selects a set of ready specs whose scopes don't overlap (glob- and directory-prefix aware), up to N workers. A spec with no declared scope runs alone — correctness over parallelism. Ready specs are already dependency-independent, so scheduling only has to reason about file scope.Shared verification gate (
verification.py)The deterministic gate — acceptance criteria + backpressure via
subprocess, plus the tamper-guard hash — is extracted into one module that both the sequential engine and the parallel orchestrator call. There is exactly one definition of "verified"; the engine now delegates to it (no behavior change, existing tests green).Parallel orchestrator (
parallel.py) +owloop run --workers NEach worker runs one target spec in its own
git worktreeon its own branch: implement → shared gate → commit. The orchestrator then merges each passed branch into the base branch — because the batch is file-disjoint, those merges never conflict. A round with zero progress counts toward a stall (StopReason.STALLED) so a parallel run can't spin forever. A fresh adapter is created per worker (adapters hold per-run streaming state).--workers 1(default) leaves the sequential engine untouched.Docs & specs
Spec template + generator now teach the
## Filesscope and disjoint decomposition;CLAUDE.mddocuments the file-disjoint principle and the shared gate.Tests & checks
tests/test_parallel.py— real worktrees + merges, overlap→sequential, stall, preflight.tests/test_spec_queue.py.ruff+mypyclean.This closes out the last phase of #37. Parallelism deliberately relies on decomposition (the
## Filescontract), never lock coordination.