Skip to content

fix: Fix critical path to exclude done tasks and weight by priority#88

Open
ulascanzorer wants to merge 5 commits into
mainfrom
fix/mymr-208-fix-critical-path-to-exclude-done-tasks
Open

fix: Fix critical path to exclude done tasks and weight by priority#88
ulascanzorer wants to merge 5 commits into
mainfrom
fix/mymr-208-fix-critical-path-to-exclude-done-tasks

Conversation

@ulascanzorer
Copy link
Copy Markdown
Collaborator

@ulascanzorer ulascanzorer commented May 20, 2026

Summary

This PR bundles three changes that landed on the same branch. Each commit is self-contained and can be reviewed or reverted independently.

1. [MYMR-208] getCriticalPath: exclude done tasks, weight by priority

getCriticalPath (lib/data/traversal.ts) had two bugs that degraded the "what should I work on" recommendation derived from ready ∩ critical_path:

  1. Done tasks counted in the chain. buildDepAdjacency only excluded cancelled, so done tasks landed in graph.activeTasks and the DP counted them as full chain nodes. Result: the bottleneck skewed toward historical rather than remaining work.
  2. Uniform +1 weight ignored priority. A 3-chain of three backlog tasks (score 3) outranked a 2-chain of two urgent tasks (score 2), even though the latter is the actual prioritization bottleneck.

The fix is surgical and scoped to getCriticalPath:

  • Done-transparency is local to getCriticalPath: the DP builds a dpNodes map that drops done from graph.activeTasks, then rebuilds dpDeps / dpDependents from graph.effectiveDeps. buildDepAdjacency's !== "cancelled" filter and getBlockedTasks' manual done-skip stay (HOTL decision: surgical placement, not graph-wide).
  • DP weights each node by priorityWeight(info.priority) with a doubling ladder PRIORITY_WEIGHTS = { urgent: 8, core: 4, normal: 2, backlog: 1 }. Null or unrecognized priority falls back to the normal weight (2) so the DP never sees undefined/NaN.
  • listTasksForGraph (lib/data/task.ts) now selects tasks.priority; ActiveTaskInfo (lib/graph/effective-deps.ts) carries Priority | null. One substrate fetch shared by every graph analyzer, no extra DB round-trip in getCriticalPath.

The returned CriticalPathTask shape is unchanged (id, taskRef, title, status), so formatCriticalPath keeps rendering [status] per task without modification.

2. mymir_context bundle memory regression (follow-up to #85)

loadBundleDeps in lib/graph/effective-deps.ts was loading every task and every depends_on edge for the project on each call (via buildDepAdjacency), then walking the substrate in JS via walkEffectiveDepsBounded. On a dogfooded mymir project this allocated near 1.8 GB on a single mymir_context request at depths agent / planning / review, captured by ad-hoc process.memoryUsage() probes around the MCP route handler.

Fix:

  • Push the cancelled-transparent walk into Postgres as two recursive CTEs, fetchEffectiveDepChain and fetchEffectiveDownstream, under lib/db/raw/. Each CTE carries an effective_depth counter that increments only on active walls; cancelled middles pass through without consuming a depth slot. Postgres's CYCLE clause terminates recursion on cancelled-loop cycles. The query returns the depth-bounded effective subgraph, not the whole project graph.
  • loadBundleDeps now orchestrates the two SQL calls in parallel and maps rows to the existing { id } shape. Its signature is unchanged, so agent.ts / planning.ts / review.ts were not touched.
  • walkEffectiveDepsBounded is deleted. buildDepAdjacency, buildEffectiveDepGraph, and walkEffectiveDeps are kept; they back the analyzer tools that load the whole-project graph by design.

The three "cancelled middle is transparent: C surfaces, B does not" integration tests from #85 still pass against the new SQL implementation.

3. Pin bun run dev to webpack

Turbopack (Next.js 16's default dev bundler) has documented dev-mode memory growth (see vercel/next.js#93451, vercel/next.js#66326). Independent of fix #2, this was filling RAM under unauthenticated Claude Code retry traffic in development. Bisected by A/B: node --run dev (Bun out, Turbopack in) still leaked; bun next dev --webpack (Bun in, Turbopack out) was flat. Pinning dev to --webpack matches the existing build: next build --webpack script.

Side benefit: the webpack(...) hook in next.config.ts (driver / broker NormalModuleReplacementPlugin) now actually runs in dev. Turbopack was silently ignoring those aliases; the indirection re-exports from _driver.node / _broker.node shims made it work by coincidence.

This is an environmental workaround pending upstream Turbopack fixes; revert the line when upstream stabilizes.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Documentation

Testing

  • Linting passes (bun run lint)
  • Typecheck passes (bun run typecheck)

tests/data/traversal.test.ts covers MYMR-208's four ACs plus regression and fragmentation guards (7 tests):

  1. AC 1, A(done) → B(planned) → C(draft) reports only B → C.
  2. AC 2, 2-chain of two urgent (16) outranks 3-chain of three backlog (3).
  3. Single urgent (8) outranks 3-chain of three normal (6).
  4. AC 4, null priority defaults to normal weight (2); a parallel single urgent task still wins.
  5. AC 4, unrecognized priority string ("weird", persisted via service-role insert; column is plain nullable text) defaults to normal.
  6. Regression, cancelled-transparency still works inside the new DP (A → M(cancelled) → C reports A → C).
  7. Done-middle fragmentation, A → B(done) → C does NOT connect A and C. Locks the FILTER semantic vs the WALK semantic so a future "make done transparent like cancelled" refactor flags itself loudly.

Context-bundle integration tests (tests/context/agent.test.ts, tests/context/planning.test.ts, tests/context/review.test.ts) still pass against the new SQL CTE implementation.

bun test tests/data/ tests/graph/ tests/context/ is green.

Notes for reviewer

  • HOTL-resolved scoping decision (MYMR-208). Done-transparency is implemented locally in getCriticalPath only, not in buildDepAdjacency. The walkEffectiveDeps transparency rule (cancelled-only) and getBlockedTasks' explicit done-skip both stay. Changing buildDepAdjacency to drop done would ripple to every analyzer that consumes the shared substrate, out of scope for this fix.
  • Doubling ladder, not Fibonacci. Weights {8, 4, 2, 1} were picked so a single urgent task (8) dominates a chain of three normals (6). This is what makes the recommendation surface prefer working on the most important blocker over the longest dependency tail.
  • Bundled scope. Originally this PR was MYMR-208 only. The loadBundleDeps rewrite (section 2) and the dev script pin (section 3) were folded in because both were diagnosed while validating MYMR-208 on the same branch; splitting would have churned three reviewers' worth of context. Each commit is self-contained.
  • MYMR-210 follow-up. MYMR-208 is the prerequisite for MYMR-210 per the research brief; MYMR-210 will rebase onto this once merged. No edge mutations needed here.
  • No new instrumentation or auth surface. priority is already returned on every task-detail read path; adding it to listTasksForGraph's SELECT does not cross a trust boundary.

@ulascanzorer ulascanzorer requested review from FrkAk and ZeyNor as code owners May 20, 2026 19:23
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