Repository navigation
feat(dag-viewer): list the enumerated candidate DAGs and draw any of them - #1019
Merged
Merged
Conversation
…them An Enumerated DAGs panel reads memo.enumeration: per-step counts, one row per listed choice with the groups whose candidate differs from the selected choice, the budget-share marker, cost and validity when priced (cheaper than selected in red). Clicking a row draws the choice's composed DAG in the Stage 1 lane. Documents without memo are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… root groups Review fixes: a group a choice does not name takes candidate 0; a selected choice over a later step is flagged and only the row's groups are compared; an enumerated DAG with fewer root groups than queries labels its roots as root groups; the cheaper chip has its own style; the enumerated row is released by selectLogical/selectPhysical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
compose writes roots in increasing root-group id, not query order: name each root by its group's roots_of from the enumerated step's memo, and fall back to root group #n without it. No row is marked selected when the enumerated step is earlier than the last. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A root group answering several queries is each one's root, so highlighting any of them keeps it bright; the lane note no longer guesses a reason when roots cannot be matched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1003 may write "dag": null with "error" for a choice that does not compose: accept it, show the error, and leave the row unclickable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… example Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zzylol
force-pushed
the
feat/1006-viewer-enumeration
branch
from
October 28, 2026 20:39
4a817b8 to
24db35e
Compare
Require a non-empty error for a choice without a DAG, check the cross-step header on the real example, and use its counts in the docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #1006. Part of #1008.
Why
For the ratio-of-quantiles workload in #1006, the Pass 1 memo stands for many complete candidate DAGs (49 = 7 × 7 in production, where the two equal quantiles share a group; #1006's 343 was measured without series identity; see #1024). The viewer's Stage 3 list shows only the selected plan and the rejections Stage 3 recorded, so nobody can see how many DAGs the memo stands for, how each differs from the selected plan, or whether any is cheaper than it.
What
A new Enumerated DAGs panel in
tools/dag-viewer, between the Stage 0 and Stage 1 lanes. It readsmemo.enumeration(written by #1003, priced by #1004) and is hidden for documents without it.Stage 0: 1 · Pass 1: 49 · Pass 2: ≥ 1000 · concrete states: ≥ 1000;≥and the limit when counting stopped), how many of the enumerated step's DAGs the document lists, and, when priced, how many are cheaper than the selected plan.selected_choice(both choices are sparse: a group a choice leaves out takes candidate 0; a selected group the DAG does not read shows asG9 not read) (G7 → #2 CountSketch+heap (pass1): group, candidate index, and the candidate's label and origin frommemo.stepswhen that step lists it, else just the index);budget share < 1whenbudget_share_1is false;✓ selected; and, when priced, the cost and validity (✗ invalidwith the rejection cause). A row withcheaper_than_selectedis red.dagin the Stage 1 lane with the same renderer as a Stage 1 candidate (render(1, dag, false, null)). The page composes nothing. The Stage 2 lane is cleared, since the document has no physical plan for an enumerated DAG.composewrites one root per root group in increasing group id; the panel maps them to queries through the step'sroots_of, so the lane has one root per query (a group answering two queries is both their root), or labels themroot group #nwhen the step's memo is absent.selected_choiceis over the last), the header says so, only each row's own groups are compared, and no row is marked selected."dag": nullwitherror) shows its error and can't be clicked.memo.enumerationis malformed, as it does for other sections.How
All new logic is in
enumeration.js: the checks and the row view are DOM-free;renderEnumerationfills the panel and handles clicks with one delegated listener.app.jsgets three hooks: it addsvalidateMemoEnumerationto the document check, callsrenderEnumeration(doc, showEnumerated)after loading a document, and callspressEnumeratedRow(null)inselectLogical/selectPhysical, so that picking a Stage 1/2 candidate releases the enumerated row.showEnumeratedis a new 10-line function in app.js that draws the DAG in lane 1.Key code interfaces added / modified
tools/dag-viewer/enumeration.js(new)tools/dag-viewer/app.jsThe document format is unchanged by this PR: it reads
memo.enumerationas #1003/#1004 define it (step,per_step,limit,choices[]withid,choice,dag,budget_share_1,selectedand optionalvalid,rejection,cost_per_second,cheaper_than_selected;priced), plusmemo.selected_choiceandmemo.steps[].groups[].candidates[]for origins and labels.Usage on #1003's committed example: open the
Ratio of quantiles (memo after each step)tab (python3 tools/dag-viewer/server.py, thenhttp://localhost:8000/#memo-ratio-of-quantiles). The header readsStage 0: 1 · Pass 1: 49 · Pass 2: ≥ 1000 · concrete states: ≥ 1000 · listed: all 49 Pass 1 DAGs · each row: groups whose candidate differs from the selected plan’s choice, which is over the concrete states memo: …. Row E2 readsG4 → #1 Project ← SummaryEstimate Quantile(q=0.9) ← SummaryAgg Kll(failure_probability=0.001 k=345) (pass1) · G6 → #0 …. Clicking it draws its DAG in the Stage 1 lane, with its root as q1's root.The same with the priced hand-written fixture:
The panel header reads
Stage 0: 1 · Pass 1: 4 · listed: all 4 Pass 1 DAGs · 4 priced · 1 cheaper than the selected plan (2) · each row: groups whose candidate differs from the selected plan’s choice. Row E3 readsG7 → #2 CountSketch+heap (pass1), costs 1.5, and is red. Clicking it sets the Stage 1 lane header toenumerated DAG E3 · G7 → #2 CountSketch+heap (pass1) · 11 nodes, draws that DAG, and sets the Stage 2 lane tono physical plan for an enumerated DAG.Major algorithm changes
None.
Before this PR
The viewer showed one plan's three DAGs and the Stage 3 rejections. It could not show how many candidate DAGs the memo stands for, list them, or draw any of them.
After this PR
A document with
memo.enumerationshows the counts per step and the listed DAGs, each described by how it differs from the selected choice, with cost and validity when priced. Any listed DAG can be drawn in the Stage 1 lane. Documents withoutmemolook and behave as before.Evidence
rgb(251, 229, 227),--bad-soft), the lane 1 and lane 2 headers after the click, the row being released after a Stage 3 row click, and the hidden panel for plain Example 1 all matched the text above, with no page errors.Verification
node --test tools/dag-viewer/*.test.mjs, Node 22):enumeration.test.mjs, 12 tests:memo(or withoutenumeration) have no enumeration problems and no panel;≥and the limit when counting stopped;memo.steps, and names every group when there is no selected choice;dag: nullanderroris accepted, shows the error and is disabled.python3 -m unittest test_viewer, stub DOM in V8): 81 tests, 8 skipped (they need a builtstage_pipeline). New ones:memokeeps the panel hidden;roots_ofin root-group order (swapped order is caught), a group answering q1 and q2 is both their root and stays bright when q2 is highlighted, and without the step's memo roots areroot group #n;examples/memo-ratio-of-quantiles.json, the panel saysPass 1: 49and lists all 49 DAGs, and clicking a row redraws the Stage 1 lane with that DAG (root of q1) and clears Stage 2;enumeration.test.mjs.Architectural decisions
enumeration.js), so it doesn't conflict with the memo view (DAG viewer: draw the memo after each step (groups with their candidates) #1005,memo.steps).validateStagePipeline, not inside it, sostages.jsstays unchanged.Limitations and follow-up
selected_choiceis over the last memo step. When the enumerated step is an earlier one, the comparison crosses steps (flagged in the header).root group #nlabels.Human review — do not complete with an agent
🤖 Generated with Claude Code