Skip to content

feat(devtools): asap_planner — one planner CLI that runs the stages up to --run-until (#1161) - #1190

Merged
milindsrivastava1997 merged 10 commits into
mainfrom
1161-one-planner-cli-run-stages-up-to-a-chosen-point-print-as-json-or-text
Nov 4, 2026
Merged

milindsrivastava1997 merged 10 commits into
mainfrom
1161-one-planner-cli-run-stages-up-to-a-chosen-point-print-as-json-or-text

Conversation

@milindsrivastava1997

@milindsrivastava1997 milindsrivastava1997 commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #1161. Lands #1159's two commits (its text printer), so #1159 can be closed.

Why

Inspecting the planner took three overlapping tools (stage_pipeline, show_logical_dag, #1159's printer), and each had its own flags, time limits and display caps. None of them could stop after a chosen stage, and none showed every tumbling-window width at once.

What

A single CLI, asap_planner, runs the stages up to the one you name and prints each stage's output as text (the default) or as an asap-stage-pipeline/v1 JSON document:

asap_planner --workload <file>... --run-until stage0|stage1_pass1|stage1_pass2|stage2|stage3
  [--format text|json] [--out <file>] [--sql-catalog-table <json>]...
  [--cost-inputs <file>] [--deployment-capabilities <file>] [--ignore-accuracy-guarantees]
  • It prints every stage up to and including --run-until. Pass 2 prints two snapshots: the memo after local window composition, and LogicalASAPMemoCandidates. In JSON, the sections of stages that were not run are null.
  • Each stage runs once, with no deadline. Pass 2 uses TumblingWindowWidthDomain::Complete, so every legal width is in one memo.
  • Accuracy guarantees are enforced by default. Every removed flag (--example, --promql, --sql, --table, --cost-model, --exhaustive, --milp-time-limit-s, --max-displayed, --no-memo, --text, …) is a usage error with exit status 2. An ad hoc query is now a one-query workload file.
  • stage_pipeline and show_logical_dag are deleted. Every caller is migrated: the corpus and sketch snapshots, memo_pricing, planner_fuzz, the Stage Viewer server, and validate_joint_workloads.py.
  • Stage Viewer:
    • The server plans through asap_planner and writes the three small examples (1, 3a, 3b) into out/.
    • The committed example documents, the Query planner box (editor.js, /api/plan) and the provisioned-capacity cost preset are removed. Stage 3 refuses cost models 2 and 3 (Memo search solver: MILP over the memo for all cost models #888), so that preset could not be used.
    • The workload files are kept.
  • Docs:
    • New user guide: docs/user_guide_docs/asap-planner.md, which replaces stage-pipeline.md.
    • run-a-query.md, stage-viewer.md and planner-snapshots.md are updated.
    • Design and developer docs get the tool rename only. Historical run records are left as they were.

How

run_stages_until in crates/devtools/src/planning_stages.rs calls Stage 0, Pass 1, Pass 2, Stage 2 and Stage 3 in run_planning's order and returns after the stage --run-until names. It does not call run_planning, because run_planning has no stop point; #1169 tracks moving the stop point there. The text printer is #1159's planning_stages_text, which now stops at the same point.

Key code interfaces added / modified

  • New CLI: asap_planner (crates/devtools/src/bin/asap_planner.rs).
  • New in asap_devtools::planning_stages:
    • RunUntil
    • run_stages_until(roots, entries, data, models, until)
    • new fields on PlanningStagesRun: run_until, stage0_memo_groups, pass2_local_window_composition
  • Text printer: asap_devtools::planning_stages_text::planning_stages_text(&PlanningStagesRun).
  • Restored accessor: Pass2RoundState::unexpanded_memo() (asap-logical-asap-optimization), the memo after local window composition. refactor: stage-boundary cleanup — Memo rename, drop MemoSteps and computation_targets, Stage 0 module (#1047, #1054, #1012) #1177 had removed it.
  • JSON document: memo.steps is now stage0, pass1, pass2_local_window_composition, pass2, and there is a new top-level run_until.

Major algorithm changes

None. The planner's stages are unchanged; only the tool that drives them is new.

Before this PR

$ cargo run -p asap-devtools --bin stage_pipeline -- --example planner-layering-1 --enforce-accuracy-guarantees --out plan.json

This planned under a 30 s budget, in tumbling-window-width rounds, and the JSON showed only the selected round's memo. Logical DAGs needed a separate show_logical_dag.

After this PR

$ asap_planner --workload tools/dag-viewer/workloads/planner-layering/example1.json --run-until stage3
==================== Stage 0 output: the frontends' roots ...
==================== Stage 0 → Stage 1: DagOfLogicalMemoGroups ...
==================== Stage 1 Pass 1 output: PerComputationLogicalMemoCandidates ...
==================== Stage 1 Pass 2, after local window composition (tumbling-window candidates) ...
==================== Stage 1 Pass 2 output = Stage 2 input: LogicalASAPMemoCandidates ...
==================== Stage 2 output = Stage 3 input: PhysicalASAPMemoCandidates ...
==================== Stage 3 output: Selection ====================
selected choice: G0:c0 G1:c0 G2:c0 G3:c0 G4:c0 G5:c0
search: Optimal, 525 MILP variables, 921 constraints, 14 ms solving
...
cost_per_second: 4.377000

--run-until stage0 replaces show_logical_dag.

Measurements on this branch:

  • All snapshots run in 3 s (release).
  • The devtools tests take 42 s in debug: 79 run and 2 are ignored. One ignored test is the RangeHeavy T8+T5 MILP test, which takes 263 s in debug; the other is the fuzz campaign.
  • Seven tests that were ignored for wall-clock budgets now run by default.
  • validate_joint_workloads.py selects a plan for all 30 workloads; the slowest takes 9.8 s.

Snapshot changes

No plan or cost changed in any snapshot.

Snapshot Change Why (agent's reading)
o11y_bench q05, q19, q25 proven_optimal false → true Stage 3 now runs with no deadline, so the MILP finishes its optimality proof.
sketch shared-quantiles-1h-mvp, quantile-single-mvp proven_optimal false → true; search_status deadline_reached → optimal Same reason. Their deadline_reached_reason entries are removed from the manifest.
o11y_bench q18, q21 new snapshots (skip_reason removed) They were skipped because of wall-clock budgets. Both now plan in under 1 s and are proven optimal.
  • A person confirms the why above.

Tests

  • Acceptance tests (tests/asap_planner_acceptance.rs):
    • --run-until prints exactly the stages up to the one named.
    • Pass 2 holds a candidate for every width that divides the grid.
    • Stage 3 selects among the candidates Stage 2 lists.
    • JSON sections of stages that were not run are null.
    • A missing --run-until, or any removed flag, is a usage error.
  • stage_pipeline.rs is ported to tests/asap_planner.rs. Tests of removed features (display cap, exhaustive enumeration, the reference pricer, --name, the planning budget) are deleted.
  • The whole workspace passes cargo test, clippy and fmt. The viewer tests pass: Node 43/43, Python 95/95.

Follow-ups: #1169 (stop point inside run_planning) and #1186 (cleanup of tools left broken or unused), #1202 (warnings in JSON before Stage 3).

🤖 Generated with Claude Code

zzylol and others added 5 commits November 2, 2026 19:58
…tput as text

Adds asap_devtools::planning_stages_text and the print_planning_stages
bin: the frontends' roots, the memo groups, Pass 1's
PerComputationLogicalMemoCandidates, the memo after Pass 2's local window
composition, LogicalASAPMemoCandidates, PhysicalASAPMemoCandidates and
Stage 3's Selection for one PromQL workload. node_label moves from
stage_pipeline to candidate_labels so both share it, and now names a
pane chain's layout (pane_width_ms, pane_count).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One binary plans the workload; --text writes the run as text instead of the
viewer document, so both outputs come from the same inputs and flags.
The text output, now in the aligned format (G1:  TimeRange 60s  <- G0), also
covers what tools/dag-viewer/print_stages.py (#1158) prints: candidates new
at each memo step, the selected memo choice, Stage 3's search outcome, cost
per plan node and accepted accuracy per query, and with --enumerated the
memo choices with their DAGs. --out - writes to standard output.
node_label prints a time range as 60s.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ints them (#1161)

One planner CLI replaces stage_pipeline and show_logical_dag:
asap_planner --workload <file>... --run-until stage0|pass1|pass2|stage2|stage3
[--format text|json]. Each stage runs once, with no deadline, over every
legal tumbling-window width; Pass 2 prints the memo after local window
composition and after the sharing rules. Accuracy guarantees are enforced
by default; removed flags are usage errors.

Tests move to tests/asap_planner.rs (plus acceptance tests); snapshots,
memo_pricing and planner_fuzz go through asap_planner. Five snapshots are
now proven optimal (plans unchanged) and q18/q21 are no longer skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and the planner box (#1161)

The server plans with asap_planner --run-until stage3 --format json and
writes the three small examples into out/. The committed example
documents, editor.js and /api/plan are removed; workload files stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1161)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@milindsrivastava1997 milindsrivastava1997 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review (Claude Code, /code-review high). Findings were not independently verified.

Comment thread crates/devtools/src/bin/asap_planner.rs Resolved
Comment thread tools/dag-viewer/server.py Resolved
Comment thread crates/devtools/src/bin/asap_planner.rs Resolved
Comment thread crates/devtools/src/planning_stages.rs Resolved
Comment thread crates/devtools/src/bin/asap_planner.rs Resolved
Comment thread crates/devtools/src/planning_stages.rs Resolved
Comment thread crates/devtools/src/planning_stages.rs Resolved
- Exit 1 without the usage line for non-argument failures; 2 stays for
  bad arguments.
- Report a Pass 1 rejection before contradictory deployment capabilities,
  as run_planning does.
- Report a Stage 2 failure under Stage 2, not Stage 1.
- Attach warnings only to a Stage 3 section that exists.
- Number accepted accuracy q1..qN in text, like the warnings.
- Build the Stage 0 memo once.
- Viewer server and validate_joint_workloads accept a null Stage 3.
- Test that the viewer's cost-input files match the presets.
- Drop stale --exhaustive/--table references.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@milindsrivastava1997 milindsrivastava1997 left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review (Claude Code). Two findings are on lines outside the diff, so they are here:

  • tools/dag-viewer/stages.js:425: the viewer rejects every partial run. Documents from --run-until stage0|pass1|pass2|stage2 are refused because the later sections are null. The user guide still says to open --format json output in the Stage Viewer. For example, asap_planner --run-until pass2 --format json --out run.json (the example in the bin header) gives "stage2_physical_asap_optimization is missing".
  • docs/develop_docs/joint-candidate-workload-evaluation.md:21: the reproduction doc is out of date. It still says to build stage_pipeline and pass it to validate_joint_workloads.py. The script now sends asap_planner flags (--run-until, --format, a --cost-inputs file), so a frozen stage_pipeline binary fails every workload with "unknown argument --run-until".

Comment thread crates/devtools/src/bin/asap_planner.rs Resolved
Comment thread crates/devtools/src/planning_stages.rs Resolved
Comment thread crates/devtools/src/bin/asap_planner.rs Resolved
Comment thread crates/devtools/src/bin/asap_planner.rs Resolved
Comment thread crates/devtools/src/planning_stages.rs Resolved
Comment thread crates/devtools/src/planner_fuzz/mod.rs Resolved
Comment thread crates/devtools/src/planning_stages.rs Resolved
Comment thread crates/devtools/src/bin/asap_planner.rs Resolved
- asap_planner exits 1 when a stage failed, in text and JSON, after
  writing its output; the viewer server reads the document whenever
  one was written.
- The viewer shows a null (not run) stage section as empty, so
  --run-until before stage3 and Pass 1 failures open again.
- sql_catalog returns the table names; catalog_table_names is gone.
- stage0_memo_groups is built only by run_stages_until.
- planner_fuzz repro commands say when the exhaustive search may not
  reproduce.
- Docs: exit status; joint-workload reproduction uses asap_planner.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@milindsrivastava1997

Copy link
Copy Markdown
Collaborator Author

Review-body findings, fixed in a3f0282:

  • stages.js partial runs: null stage sections are shown empty; test test_a_run_stopped_before_stage3_is_shown_with_empty_later_stages covers stage0, pass1, pass2 and stage2 documents.
  • joint-candidate-workload-evaluation.md: the reproduction now builds asap_planner.

milindsrivastava1997 and others added 2 commits November 3, 2026 22:21
- stage_pipeline's bin, tests and guide stay deleted; main only removed
  their deadline flags and ignores, which asap_planner never had.
- Pass2RoundState is gone on main; Pass 2's entry is split into
  pass2_local_window_composition and pass2_after_local_window_composition
  so asap_planner still prints the local window composition step.
- SelectionError::DeadlineReached is gone; SelectionError::NotRun marks
  a run that stopped before plan selection (asap_planner --run-until).
- Snapshot tests keep asap_planner with main's optimal-only checks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every --run-until value now names its stage: stage0, stage1_pass1,
stage1_pass2, stage2, stage3. The old pass1 and pass2 are usage errors.
The JSON run_until uses the same names; the memo step ids (pass1, pass2)
are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@milindsrivastava1997
milindsrivastava1997 merged commit 05ce8f1 into main Nov 4, 2026
3 of 4 checks passed
@milindsrivastava1997
milindsrivastava1997 deleted the 1161-one-planner-cli-run-stages-up-to-a-chosen-point-print-as-json-or-text branch November 4, 2026 15:00
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.

One planner CLI: run stages up to a chosen point, print as JSON or text

2 participants