Add multi-epic orchestration support#391
Conversation
- New --epic (repeatable) and --epics (comma-separated) CLI flags to run one session across multiple epics - MultiScopeTrackerPlugin combines tasks from multiple epics, annotating each with its ExecutionScope - ScopeFilterBar TUI component lets users cycle/reset scope filter with g/G keybindings - Worktree branch names include session and scope slugs for multi-epic parallel runs - Scope-level task summaries added to parallel run output - epicIds and executionScopes persisted in session state and resumed correctly
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThis PR adds multi-epic parallel execution: execution-scope types, a MultiScopeTrackerPlugin to aggregate/deduplicate tasks across scopes, CLI support for repeated ChangesMulti-epic execution framework
Estimated code review effort 🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
✨ Finishing Touches📝 Generate docstrings
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #391 +/- ##
==========================================
+ Coverage 48.37% 52.77% +4.39%
==========================================
Files 117 116 -1
Lines 38879 35057 -3822
==========================================
- Hits 18807 18500 -307
+ Misses 20072 16557 -3515
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/commands/run.tsx (1)
1617-1654:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winRemove the SIGINT handler on epic-picker teardown.
Line 1644 adds a process-level listener, but cleanup never unregisters it. Reopening this flow can stack stale handlers.
Suggested fix
async function showEpicSelectionTui( tracker: TrackerPlugin ): Promise<TrackerTask[] | undefined> { return new Promise(async (resolve) => { @@ - const cleanup = () => { + const cleanup = () => { + process.removeListener('SIGINT', handleSigint); renderer.destroy(); }; @@ - process.on('SIGINT', handleSigint); + process.on('SIGINT', handleSigint);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/commands/run.tsx` around lines 1617 - 1654, The SIGINT handler registered by process.on('SIGINT', handleSigint) in the Promise should be removed during teardown to avoid accumulating handlers; update the cleanup function in the same block (the closure that currently calls renderer.destroy()) to also call process.off('SIGINT', handleSigint) (or process.removeListener('SIGINT', handleSigint)) so the handler is unregistered when EpicSelectionApp is torn down, ensuring handleSigint is the same function reference used when registering.
🧹 Nitpick comments (3)
src/commands/resume.tsx (2)
199-203: ⚡ Quick win
formatSessionEntrydoesn't display multi-epic sessions.This unchanged code path only inspects
entry.epicId. For sessions registered withepicIds(and noepicIdpopulated),trackerInfowill silently fall through toprdPath/trackerPlugin, hiding epic context from--listoutput. Worth handling here since the newepicIdsfield onSessionRegistryEntrywas added in this PR.✏️ Suggested fix
- const trackerInfo = entry.epicId ? `epic:${entry.epicId}` : - entry.prdPath ? `prd:${entry.prdPath}` : entry.trackerPlugin; + const epicLabel = + entry.epicIds && entry.epicIds.length > 0 + ? `epics:${entry.epicIds.join(',')}` + : entry.epicId + ? `epic:${entry.epicId}` + : undefined; + const trackerInfo = epicLabel ?? (entry.prdPath ? `prd:${entry.prdPath}` : entry.trackerPlugin);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/commands/resume.tsx` around lines 199 - 203, The function formatSessionEntry currently sets trackerInfo by checking entry.epicId first and ignores entry.epicIds, causing multi-epic sessions to be displayed incorrectly; update the trackerInfo logic in formatSessionEntry to prefer entry.epicIds when present (e.g., join the epic IDs into a readable string like "epic:id1,id2" or similar) and fall back to entry.epicId, then to entry.prdPath and entry.trackerPlugin, ensuring the epicIds representation is included in the returned string (refer to trackerInfo, entry.epicIds, entry.epicId, and formatSessionEntry to locate and change the code).
727-740: ⚡ Quick winMismatch warning omits multi-epic context.
When a multi-epic resume yields no tasks, this branch never surfaces the configured epic IDs (only
epicIdandprdPathare checked), so users debugging a stale multi-epic session get a generic message. Consider also reportingresumedState.trackerState.epicIdswhen present.✏️ Suggested fix
if (resumedState.trackerState.epicId) { console.warn(` - The epic ID "${resumedState.trackerState.epicId}" no longer exists`); } + if (resumedState.trackerState.epicIds && resumedState.trackerState.epicIds.length > 0) { + console.warn(` - One or more epic IDs no longer exist: ${resumedState.trackerState.epicIds.join(', ')}`); + } if (resumedState.trackerState.prdPath) { console.warn(` - The PRD file "${resumedState.trackerState.prdPath}" is missing or empty`); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/commands/resume.tsx` around lines 727 - 740, The warning branch that runs when shouldWarnAboutTrackerMismatch(engineState.totalTasks, sessionTotalTasks) is triggered only checks resumedState.trackerState.epicId and prdPath and omits resumedState.trackerState.epicIds; update the message to include epicIds when present so multi-epic resumes show the configured epic list. Locate the block that logs the warnings (the if using shouldWarnAboutTrackerMismatch) and add a conditional that checks resumedState.trackerState.epicIds (and formats them as a comma-separated list or similar) and logs a user-friendly line like “Configured epic IDs: …” alongside the existing epicId/prdPath lines. Ensure you reference resumedState.trackerState.epicIds and keep the new log only when epicIds is non-empty to avoid noise.src/plugins/trackers/multi-scope.ts (1)
96-118: ⚡ Quick winFetch scoped task lists concurrently to reduce multi-scope latency.
This loop serializes one tracker request per scope. Parallelizing keeps scope order while avoiding N round-trips in sequence.
Proposed refactor
- for (const scope of this.scopes) { - const scopedTasks = await this.delegate.getTasks(mergeFilterForScope(filter, scope)); + const scopedResults = await Promise.all( + this.scopes.map(async (scope) => ({ + scope, + scopedTasks: await this.delegate.getTasks(mergeFilterForScope(filter, scope)), + })) + ); + + for (const { scope, scopedTasks } of scopedResults) { for (const task of scopedTasks) { if (seenTaskIds.has(task.id)) { if (!this.warnedDuplicateIds.has(task.id)) { this.warnedDuplicateIds.add(task.id); console.warn( `Warning: task ${task.id} appears in multiple selected epics; using first occurrence.` ); } continue; } seenTaskIds.add(task.id); combined.push({ ...task, executionScope: scope, metadata: { ...task.metadata, executionScope: scope, }, }); } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/trackers/multi-scope.ts` around lines 96 - 118, The current loop over this.scopes calls this.delegate.getTasks sequentially causing N round-trips; change it to fetch all scoped task lists concurrently by mapping this.scopes to an array of promises using this.delegate.getTasks(mergeFilterForScope(filter, scope)), awaiting Promise.all to get results in the same scope order, then iterate the resolved arrays in original scope order to perform the existing dedupe/warn logic using seenTaskIds and this.warnedDuplicateIds and push into combined (preserving executionScope and metadata). Ensure error handling/propagation remains consistent when awaiting the parallel requests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/commands/run.tsx`:
- Around line 523-533: Scope failure totals only account for merge-phase entries
from executorState.mergeQueue, so pre-merge worker-phase failures are missed;
update the logic that builds scopes/completed/failed (the loop referencing
getTaskExecutionScope and operation.workerResult.task) to also iterate the
worker-phase results (e.g., executorState.workerQueue or
executorState.workerResults) and increment failed for the corresponding scope.id
when a workerResult/status indicates failure, using the same scope lookup
(getTaskExecutionScope) and the same failed map update code; apply the same fix
to the analogous block around lines 535-542.
In `@src/plugins/trackers/multi-scope.ts`:
- Around line 84-86: The initialize method currently only checks readiness via
this.delegate.isReady() and never forwards the provided _config to the wrapped
tracker; update initialize to await and propagate the config to the delegate by
calling await this.delegate.initialize(_config) before (or in combination with)
checking/setting this.ready (using this.delegate.isReady() or the delegate's
return) so the underlying tracker is properly initialized when the wrapper is
used.
---
Outside diff comments:
In `@src/commands/run.tsx`:
- Around line 1617-1654: The SIGINT handler registered by process.on('SIGINT',
handleSigint) in the Promise should be removed during teardown to avoid
accumulating handlers; update the cleanup function in the same block (the
closure that currently calls renderer.destroy()) to also call
process.off('SIGINT', handleSigint) (or process.removeListener('SIGINT',
handleSigint)) so the handler is unregistered when EpicSelectionApp is torn
down, ensuring handleSigint is the same function reference used when
registering.
---
Nitpick comments:
In `@src/commands/resume.tsx`:
- Around line 199-203: The function formatSessionEntry currently sets
trackerInfo by checking entry.epicId first and ignores entry.epicIds, causing
multi-epic sessions to be displayed incorrectly; update the trackerInfo logic in
formatSessionEntry to prefer entry.epicIds when present (e.g., join the epic IDs
into a readable string like "epic:id1,id2" or similar) and fall back to
entry.epicId, then to entry.prdPath and entry.trackerPlugin, ensuring the
epicIds representation is included in the returned string (refer to trackerInfo,
entry.epicIds, entry.epicId, and formatSessionEntry to locate and change the
code).
- Around line 727-740: The warning branch that runs when
shouldWarnAboutTrackerMismatch(engineState.totalTasks, sessionTotalTasks) is
triggered only checks resumedState.trackerState.epicId and prdPath and omits
resumedState.trackerState.epicIds; update the message to include epicIds when
present so multi-epic resumes show the configured epic list. Locate the block
that logs the warnings (the if using shouldWarnAboutTrackerMismatch) and add a
conditional that checks resumedState.trackerState.epicIds (and formats them as a
comma-separated list or similar) and logs a user-friendly line like “Configured
epic IDs: …” alongside the existing epicId/prdPath lines. Ensure you reference
resumedState.trackerState.epicIds and keep the new log only when epicIds is
non-empty to avoid noise.
In `@src/plugins/trackers/multi-scope.ts`:
- Around line 96-118: The current loop over this.scopes calls
this.delegate.getTasks sequentially causing N round-trips; change it to fetch
all scoped task lists concurrently by mapping this.scopes to an array of
promises using this.delegate.getTasks(mergeFilterForScope(filter, scope)),
awaiting Promise.all to get results in the same scope order, then iterate the
resolved arrays in original scope order to perform the existing dedupe/warn
logic using seenTaskIds and this.warnedDuplicateIds and push into combined
(preserving executionScope and metadata). Ensure error handling/propagation
remains consistent when awaiting the parallel requests.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 18559a3e-05ef-47b1-ae8c-162f6d113047
📒 Files selected for processing (35)
README.mdsrc/commands/resume.tsxsrc/commands/run.test.tssrc/commands/run.tsxsrc/config/index.tssrc/config/types.tssrc/engine/index.tssrc/parallel/events.tssrc/parallel/index.tssrc/parallel/types.tssrc/parallel/worktree-manager.test.tssrc/parallel/worktree-manager.tssrc/plugins/trackers/index.tssrc/plugins/trackers/multi-scope.test.tssrc/plugins/trackers/multi-scope.tssrc/plugins/trackers/types.tssrc/remote/client.tssrc/remote/server.tssrc/remote/types.tssrc/session/index.tssrc/session/persistence.tssrc/session/registry.tssrc/session/types.tssrc/tui/components/EpicSelectionApp.tsxsrc/tui/components/EpicSelectionView.tsxsrc/tui/components/LeftPanel.tsxsrc/tui/components/MergeProgressView.tsxsrc/tui/components/ParallelProgressView.tsxsrc/tui/components/RunApp.tsxsrc/tui/components/ScopeFilterBar.tsxsrc/tui/components/WorkerDetailView.tsxsrc/tui/components/index.tssrc/tui/theme.tssrc/tui/types.tstests/remote/remote.test.ts
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
website/content/docs/getting-started/quick-start.mdx (1)
146-157: ⚡ Quick winConsider documenting the scope filter keys for multi-epic runs.
Now that multi-epic parallel sessions are prominently featured in the quick start, users would benefit from knowing about the scope filter bar. The PR introduces keys
g/Gto narrow the TUI view per scope, but these aren't currently documented in the keyboard shortcuts section.📋 Suggested addition to View Controls section
| `o` | Cycle right panel views (details → output → prompt) | | `O` | Jump directly to prompt preview | | `d` | Toggle progress dashboard | | `a` | Open agent/model picker | +| `g` / `G` | Cycle scope filter (for multi-epic sessions) | | `h` | Toggle show/hide closed tasks |🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/content/docs/getting-started/quick-start.mdx` around lines 146 - 157, The View Controls keyboard shortcuts table is missing the new scope filter keys for multi-epic runs; add entries for `g` and `G` to the table (e.g., `g` — cycle/narrow to next scope filter, `G` — cycle/narrow to previous scope or show all scopes) and include a one-line note in the View Controls section explaining that these keys filter the TUI by epic/scope for multi-epic parallel sessions (reference the existing "View Controls" table and the keys `g`/`G` when making the change).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/session/persistence.test.ts`:
- Around line 66-71: The test uses loaded! when calling getSessionSummary, which
hides null failures; after calling loadPersistedSession(cwd) assert that the
result is non-null (e.g., expect(loaded).toBeDefined() or similar) before using
getSessionSummary so failures are explicit—add the assertion immediately after
const loaded = await loadPersistedSession(cwd) and then call
getSessionSummary(loaded) without the non-null assertion.
In `@website/content/docs/parallel/configuration.mdx`:
- Line 66: The table row for the `--epic <id>` flag has an incomplete sentence;
update the description text for `--epic <id>` to include an explicit subject
(for example: "Select an epic. This option can be repeated for multi-epic
hierarchy tracker runs.") so the second sentence is grammatically complete and
clear.
---
Nitpick comments:
In `@website/content/docs/getting-started/quick-start.mdx`:
- Around line 146-157: The View Controls keyboard shortcuts table is missing the
new scope filter keys for multi-epic runs; add entries for `g` and `G` to the
table (e.g., `g` — cycle/narrow to next scope filter, `G` — cycle/narrow to
previous scope or show all scopes) and include a one-line note in the View
Controls section explaining that these keys filter the TUI by epic/scope for
multi-epic parallel sessions (reference the existing "View Controls" table and
the keys `g`/`G` when making the change).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 997af4de-4aab-43b1-bc7d-8592cda0a96d
📒 Files selected for processing (30)
README.mdsrc/config/index.test.tssrc/engine/index.test.tssrc/parallel/parallel-executor.test.tssrc/plugins/trackers/builtin/beads-rust/README.mdsrc/plugins/trackers/multi-scope.test.tssrc/remote/server.test.tssrc/remote/server.tssrc/session/persistence.test.tssrc/session/registry.test.tswebsite/content/docs/cli/overview.mdxwebsite/content/docs/cli/remote.mdxwebsite/content/docs/cli/resume.mdxwebsite/content/docs/cli/run.mdxwebsite/content/docs/configuration/config-file.mdxwebsite/content/docs/configuration/options.mdxwebsite/content/docs/configuration/overview.mdxwebsite/content/docs/getting-started/quick-start.mdxwebsite/content/docs/parallel/configuration.mdxwebsite/content/docs/parallel/git-worktrees.mdxwebsite/content/docs/parallel/how-it-works.mdxwebsite/content/docs/parallel/merge-and-conflicts.mdxwebsite/content/docs/parallel/overview.mdxwebsite/content/docs/parallel/task-dependencies.mdxwebsite/content/docs/parallel/troubleshooting.mdxwebsite/content/docs/parallel/tui-guide.mdxwebsite/content/docs/plugins/overview.mdxwebsite/content/docs/plugins/trackers/beads-bv.mdxwebsite/content/docs/plugins/trackers/beads-rust.mdxwebsite/content/docs/plugins/trackers/beads.mdx
✅ Files skipped from review due to trivial changes (6)
- website/content/docs/configuration/overview.mdx
- website/content/docs/configuration/config-file.mdx
- website/content/docs/parallel/task-dependencies.mdx
- website/content/docs/plugins/trackers/beads.mdx
- website/content/docs/parallel/git-worktrees.mdx
- website/content/docs/plugins/trackers/beads-bv.mdx
🚧 Files skipped from review as they are similar to previous changes (2)
- README.md
- src/plugins/trackers/multi-scope.test.ts
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/commands/run.tsx (2)
1640-1663: 💤 Low value
handleSigintreassignment pattern is fragile.
handleSigintis initialised to a no-op at Line 1640, reassigned at Line 1658, then registered withprocess.onat Line 1663 and removed insidecleanupvia the captured binding. This currently works becauseprocess.oncaptures the (post-reassignment) reference andcleanupreads the same binding, but the indirection is easy to break in future edits (e.g., movingprocess.on(...)above the reassignment would silently register the no-op). Consider declaring the handler as aconstdirectly:♻️ Suggested simplification
- let handleSigint = () => {}; - - const cleanup = () => { - process.off('SIGINT', handleSigint); - renderer.destroy(); - }; - - const handleEpicSelected = (epics: TrackerTask[]) => { - cleanup(); - resolve(epics); - }; - - const handleQuit = () => { - cleanup(); - resolve(undefined); - }; - - // Handle Ctrl+C during epic selection - handleSigint = () => { - cleanup(); - resolve(undefined); - }; - - process.on('SIGINT', handleSigint); + const handleSigint = () => { + cleanup(); + resolve(undefined); + }; + + const cleanup = () => { + process.off('SIGINT', handleSigint); + renderer.destroy(); + }; + + const handleEpicSelected = (epics: TrackerTask[]) => { + cleanup(); + resolve(epics); + }; + + const handleQuit = () => { + cleanup(); + resolve(undefined); + }; + + process.on('SIGINT', handleSigint);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/commands/run.tsx` around lines 1640 - 1663, The SIGINT handler pattern is fragile because handleSigint is assigned then reassigned before being registered; instead define the handler as a single constant function (e.g., const handleSigint = () => { cleanup(); resolve(undefined); }) and use that same reference when calling process.on('SIGINT', handleSigint) and process.off('SIGINT', handleSigint) inside cleanup; ensure cleanup (which calls renderer.destroy()) remains unchanged and that handleEpicSelected and handleQuit still call cleanup() then resolve to keep current behavior.
3704-3708: 💤 Low valueDuplicate execution-scope resolution.
resolveExecutionScopesis invoked in two adjacent code paths with the sameepicIds/executionScopesguard. The first call (around Line 3704) happens before session resume, and the second (around Line 3819) re-runs after the session block. Consider consolidating: resolve once after the resume branch has had a chance to setconfig.epicIds/executionScopes, and reusetrackerForRunfor the engine. This removes the redundantgetEpicsround-trip and keeps the scope-resolution logic in one place.Also applies to: 3819-3821
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/commands/run.tsx` around lines 3704 - 3708, Duplicate calls to resolveExecutionScopes should be consolidated: remove the early call (the block that checks config.epicIds && config.epicIds.length > 0 && executionScopes.length === 0 around where trackerForRun is obtained via getTrackerRegistry()) and instead perform a single resolveExecutionScopes call after the session/resume branch (the area that may mutate config.epicIds/executionScopes), reusing the existing trackerForRun instance for both resolution and engine setup; ensure trackerForRun is initialized once (trackerForRun = trackerForRun ?? await trackerRegistry.getInstance(config.tracker)) before the single resolveExecutionScopes call so you avoid the duplicate getEpics round-trip and centralize scope-resolution logic.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/commands/run.tsx`:
- Around line 1683-1688: The catch around the tracker.getEpics() call silently
swallows failures and causes fallback synthetic scopes; update the catch to
surface the error via the existing structured logger (or stderr) before falling
back: catch the error from tracker.getEpics(), log a clear message including the
error object and context (e.g. "failed to resolve epics from tracker" plus the
error) so failures are debuggable, then continue assigning epics fallback to the
epics variable as before; locate the tracker.getEpics() call and the epics
variable in run.tsx to add the logging.
---
Nitpick comments:
In `@src/commands/run.tsx`:
- Around line 1640-1663: The SIGINT handler pattern is fragile because
handleSigint is assigned then reassigned before being registered; instead define
the handler as a single constant function (e.g., const handleSigint = () => {
cleanup(); resolve(undefined); }) and use that same reference when calling
process.on('SIGINT', handleSigint) and process.off('SIGINT', handleSigint)
inside cleanup; ensure cleanup (which calls renderer.destroy()) remains
unchanged and that handleEpicSelected and handleQuit still call cleanup() then
resolve to keep current behavior.
- Around line 3704-3708: Duplicate calls to resolveExecutionScopes should be
consolidated: remove the early call (the block that checks config.epicIds &&
config.epicIds.length > 0 && executionScopes.length === 0 around where
trackerForRun is obtained via getTrackerRegistry()) and instead perform a single
resolveExecutionScopes call after the session/resume branch (the area that may
mutate config.epicIds/executionScopes), reusing the existing trackerForRun
instance for both resolution and engine setup; ensure trackerForRun is
initialized once (trackerForRun = trackerForRun ?? await
trackerRegistry.getInstance(config.tracker)) before the single
resolveExecutionScopes call so you avoid the duplicate getEpics round-trip and
centralize scope-resolution logic.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 7be0ae3a-2c6a-40d0-80cb-be506af1f647
📒 Files selected for processing (10)
src/commands/resume.tsxsrc/commands/run.test.tssrc/commands/run.tsxsrc/parallel/index.tssrc/parallel/parallel-executor.test.tssrc/parallel/types.tssrc/plugins/trackers/multi-scope.test.tssrc/plugins/trackers/multi-scope.tssrc/remote/server.test.tssrc/remote/server.ts
🚧 Files skipped from review as they are similar to previous changes (7)
- src/parallel/types.ts
- src/remote/server.test.ts
- src/parallel/index.ts
- src/remote/server.ts
- src/plugins/trackers/multi-scope.test.ts
- src/plugins/trackers/multi-scope.ts
- src/commands/resume.tsx
Summary
--epicand comma-separated--epicssupport so one Ralph session can run across multiple selected epics while preserving single-epic compatibility.MultiScopeTrackerPluginto combine hierarchy tracker tasks, annotate tasks with their scope, dedupe duplicate task IDs, preserve cross-epic dependencies, and exclude tasks blocked by unresolved external dependencies.g/G, per-scope labels/counts, and scope-aware parallel/merge/worker displays.Testing
bun run typecheck && bun run buildbun test src/commands/run.test.tsbun test tests/tuibun test src/config/index.test.ts src/engine/ src/parallel/ src/session/ src/remote/server.test.ts src/plugins/trackers/multi-scope.test.ts tests/remote/remote.test.ts --coverage --coverage-reporter=lcovbun test src/plugins/trackers/builtin/beads-rust/index.test.tsbun test src/plugins/trackers/builtin/beads-bv/index.test.tsgit diff --checkNote:
bun test src/plugins/trackersas a single process still hits the existing Bunmock.module()isolation issue between beads-rust and beads-bv tests. The CI workflow already runs those files in isolated batches, and the isolated runs above pass.Summary by CodeRabbit
New Features
Documentation
Tests