Skip to content

aggregator: complexity-weighted project render — blocked weight + depth graph (#2400, part 2 of #1122) - #2831

Merged
dezren39 merged 2 commits into
mainfrom
queue/3573-2400-weighted-render
Oct 30, 2026
Merged

dezren39 merged 2 commits into
mainfrom
queue/3573-2400-weighted-render

Conversation

@dezren39

@dezren39 dezren39 commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Implements #2400 — part 2 of 2 of the #1122 split, consuming the part-1 edge contract (#2399, PR #2744, merged). With this merged, parent #1122's acceptance is fully met.

Closes #2400
Closes #1122

What

The weighted rollup and hands queue already rendered on /program/ (src/weights.ts, #1108). This PR adds the remaining two elements plus the honest-absence states, in a new pure module src/graph.ts:

  • Blocked weight + longest blocked chain — open items waiting on an unresolved blocker (a blocker is unresolved when the pool holds it open, or does not hold it at all — an out-of-pool blocker is never assumed done). Sized weight sums effort weights; unsized blocked items are counted separately, never zero-weighted. The longest chain follows blocked-by edges cycle-safely and renders as linked refs.
  • Depth graph — server-rendered SVG (no client JS) laid out from parentRef chains: depth = sub-issue nesting, squares are epics, amber = open / grey = closed, border encodes mode (thick solid = physical, dashed = hybrid, thin = online/none). DFS order, capped at 120 nodes with an explicit truncation note; parent cycles degrade to roots instead of looping.
  • Graceful absence — a pool with no effort labels and no edges renders an explicit "Not yet sized/graphed" banner ("not a 0% weighted score"); a sized-but-edgeless pool says "Not yet graphed" only, and never prints a blocked-zero it didn't measure.

Tests

Queue task 3573 · repo-worker-code Lane A.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@dezren39
dezren39 requested a review from a team as a code owner October 30, 2026 21:14
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-30T21:42:54.409722Z 788e41e New commits
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Oct 30, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 11 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 1b348a69-01e3-4ca7-8cb8-7cbba40f8928
Commits

Reviewing files that changed from the base of the PR and between dc9b117 and 788e41e.

Files selected for processing: 3
  • pm-stack/aggregator/src/graph.ts
  • pm-stack/aggregator/src/render.ts
  • pm-stack/aggregator/test/graph.test.ts
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 09ec60d616

ℹ️ 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".

Comment thread pm-stack/aggregator/src/graph.ts Outdated
// acyclic; a cycle degrades to a path, never a hang).
const memo = new Map<RefKey, RefKey[]>();
const chainFrom = (ref: RefKey, visiting: Set<RefKey>): RefKey[] => {
if (memo.has(ref) && visiting.size === 0) return memo.get(ref)!;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Memoize shared blocker tails during recursive traversal

When open items share blocker chains, the visiting.size === 0 restriction prevents recursive calls from using the memo, while results are only cached after each top-level traversal. This enumerates exponentially many paths even for an acyclic graph: a local Bun reproduction with 32 open items, each blocked by the next two items, took approximately seven seconds. Because src/server.ts:705 invokes this renderer synchronously for each project request, such a dependency graph blocks the server's event loop. Cache reusable acyclic suffixes while retaining cycle protection instead of recomputing them for every path.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 788e41e8 — thank you. chainFrom now caches a node's chain as soon as its subtree completes, and recursive calls reuse memo entries for any node not on the current path, so shared blocker tails are computed once. Cycle safety is preserved with a guard-hit counter: a frame caches only when no cycle-guard hit occurred anywhere in its subtree, so path-truncated (path-dependent) results — and all their ancestors — are never cached, and a cached chain can never contain a node on the caller's path (that would close a cycle through the cached node and would have hit the guard during its computation). Regression test added (shared blocker tails are memoized, not re-enumerated per path): the 32-item fan-out DAG from your repro now resolves with the full 32-longest chain; the whole graph test file runs in ~15ms and the full aggregator suite shows only the 7 pre-existing #2580 manifest failures.

…2400)

Codex P1 on PR #2831: the visiting.size===0 restriction kept recursive calls from using the memo, enumerating exponentially many paths on shared blocker chains (~7s for a 32-item fan-out DAG) inside a synchronous per-request render. A frame now caches its chain whenever its subtree completed without hitting the cycle guard (tracked with a hit counter, so path-truncated results and their ancestors stay uncached), and recursive calls reuse memo entries for nodes not on the current path. Regression test: the 32-item fan-out DAG resolves instantly with the full 32-longest chain.
@dezren39
dezren39 merged commit 010378b into main Oct 30, 2026
1 check passed
@dezren39
dezren39 deleted the queue/3573-2400-weighted-render branch October 30, 2026 21:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant