feat: Phase 1a multi-track resolvers — scope, portfolio, --track (#291)#300
Conversation
… --track (#291) Read-side of component-partitioned sprints; single-track text output stays byte-identical (G4). - lib.js: scopesOverlap() — the one shared overlap predicate (component: equality; scope: glob path-prefix containment) that sprint-state consumes now and sprint-init/backlog-doctor will reuse. - sprint-state.js: schema_version -> 2. No-arg emits active_sprints[] (ordered by started asc) plus the retained top-level active_sprint (sole element when N==1, null for a portfolio). --track/--component resolve one track. MULTIPLE_ACTIVE_SPRINTS throw replaced by OVERLAPPING_TRACKS, raised only on scope collision — disjoint tracks are a portfolio, not an error. - sprint-mirror.js: accept schema_version 2 (atomic with the bump; #291 B3). - next.sh / status.sh / context-hook.sh: render a portfolio for N>1 disjoint tracks and add --track selection; a single active track is unchanged (G4). - Tests: sprint-state/lib/mirror unit tests updated + multi-track cases; smoke-test multi-active section rewritten to the portfolio contract, enforced #291 fixtures added, overlap gate tightened to the active_sprint verdict. Validated: smoke-test 166/166 pass, 2 xfail (GATE_MT_DISJOINT/OVERLAP stay RED — they track the #293 doctor rewrite, not #291). All node suites green except a pre-existing sprint-init.test.js failure (#13), unrelated to this change and already red on main (CI runs only the bash smoke suite). Refs #291, #289. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 14 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (11)
✨ Finishing Touches🧪 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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9b235d7e8b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const matches = perSprints.filter((s) => matchesSelector(s, { track, component })); | ||
| if (matches.length === 0) return emptyState(); | ||
| return singleState(matches[0]); |
There was a problem hiding this comment.
Reject ambiguous component selectors
When two active sprint files share component: auth, they are an overlapping pair and the no-arg path below throws OVERLAPPING_TRACKS, but this selector path runs first. --component auth (and --track auth, because track also matches components) filters both files and returns matches[0], so the resolver silently chooses whichever sprint sorts first; this inverse resolver is meant to route track-specific consumers, so an invalid overlapping state should fail loudly instead of directing updates to an arbitrary sprint.
Useful? React with 👍 / 👎.
| if [ "$ACTIVE_STATUS" -eq 2 ]; then | ||
| # Multiple disjoint tracks: a portfolio. Overlap is the doctor's fail signal. | ||
| ACTIVE_COUNT=$(find_active_sprints "$SPRINTS_DIR" | grep -c . || true) | ||
| echo "=== $ACTIVE_COUNT active tracks (portfolio) ===" |
There was a problem hiding this comment.
Check overlap before printing portfolios
This human-output branch treats every multiple-active set as a successful portfolio without consulting the new scopesOverlap logic. With two active files that share the same component: or overlapping scope:, next.sh exits 0 and prints normal next work while next.sh --json correctly raises OVERLAPPING_TRACKS (the same bypass exists in status.sh/context-hook.sh), so users orienting through the default text path get no overlap warning before dispatching conflicting tracks.
Useful? React with 👍 / 👎.
Summary
Phase 1a of multi-track sprints (epic #289) — the read-side resolvers. Multiple disjoint-scope sprints can now be read as a portfolio, while a single active track stays byte-identical (G4). No lifecycle or doctor changes yet (those are #292/#293).
Changes
lib.js—scopesOverlap(): the one shared overlap predicate (component:equality;scope:glob normalized path-prefix containment). Consumed by sprint-state now; sprint-init (Phase 1b: sprint lifecycle track-awareness (init/close/mirror) #292) and backlog-doctor (Phase 1c: backlog-doctor active_sprint → scope-disjointness check #293) will reuse it.sprint-state.js—schema_version→ 2. No-arg emitsactive_sprints[](ordered bystarted:asc) plus the retained top-levelactive_sprint(sole element when N==1,nullfor a portfolio) so v1 consumers keep working.--track/--componentresolve one track deterministically.MULTIPLE_ACTIVE_SPRINTSthrow replaced byOVERLAPPING_TRACKS, raised only on scope collision — disjoint tracks are a portfolio, not an error.sprint-mirror.js— acceptschema_version2 (atomic with the bump — review finding B3; landing the bump alone would have broken single-track mirror).next.sh/status.sh/context-hook.sh— render a portfolio for N>1 disjoint tracks and add--trackselection; a single active track is unchanged (G4 text).Validation
The two multi-track gates stay RED on purpose — they assert
backlog-doctorbehavior, which is #293's rewrite; #291 does not touch the doctor. All Node test suites pass (sprint-state +5 multi-track cases, lib +scopesOverlap, mirror schema-2), and the smoke multi-active section was rewritten from the old single-active-invariant messages to the portfolio contract.Note (pre-existing, not this PR):
sprint-init.test.js#13 is red onmainalready (it assertsobjectives: []but sprint-init omits spec fields per #258). CI runs only the bash smoke suite, so the Node-suite rot went unnoticed. Recommend fixing in #292 (which touchessprint-init.js).Refs #291, #289.
🤖 Generated with Claude Code