You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The planning stages (#509) and the unified operator DAG (#511) are settled, and #514/#544 renamed the DAG types to match. The code layout still follows the old pre-ASAP / post-ASAP split:
asap-types has pre_asap and post_asap modules. The names no longer describe their contents. pre_asap::schema is the one schema for every DAG, and it already depends on post_asap::sketch. post_asap::sketch is shared vocabulary that the logical stage uses. post_asap::execution_data_state::ExecutionTiming is physical-stage data. The new ir/node.rs imports from both modules.
asap-aware-mapping (45k lines) mixes three docs: propose workload-wide planning, summary sharing, and materialization #509 stages: Stage 1 rewriting (replacement, rewrite, rollup, …), Stage 2 materialization and lowering (summary_maintenance_lifecycle, query_physical_lowering, …), and Stage 3 cost and accuracy (cost_model, analytical_cost, erp, …). Its crate doc still describes it as "the cost-aware optimizer layer over the pre-ASAP intent algebra".
pre_asap, post_asap, asap-aware-mapping and asap-physical-operators disappear. Logical and physical candidates are the same OperatorNode DAG under #511, differing only in timing, so LogicalASAPDAG / PhysicalASAPDAG stay in types::ir. Leftover pre-#509 names are renamed in the same step: PostAsapDAG, the pre_asap_sub_dag export key, and tools/dag-viewer/post_asap_fixture.json.
Stage 1 (Pass 1) keeps every candidate "not provably unable to meet its accuracy requirement". That proof is analytical: the per-family error bounds and parameter sizing in accuracy/estimators, propagation through exact functions (composition), and error-budget allocation (allocation). Today replacement.rs is the main consumer.
Stage 1 (Pass 2): "a shared summary must meet the strictest accuracy requirement". That is accuracy/reconciliation.
Stage 3 is "the only stage that uses the deployment's cost and accuracy models… accuracy is estimated by the deployment's accuracy model, not assumed from a summary's nominal bound". That is the empirical side: erp and empirical_comparison.
Stage 2 makes no accuracy decisions. It preserves OperatorNode.guarantee and records window-summary evidence, such as an EH boundary falling inside a bucket (summary_maintenance_cost/window.rs), for Stage 3.
Stage 3 reads the analytical result from OperatorNode.guarantee, which Stage 1 establishes (#511 §2.2), so plan-selection does not need to call the analytical model.
The deployment's accuracy model plugs into Stage 3 only. Today the pluggable AccuracyModel trait is passed into Pass 1 (SketchAlgorithmStrategy::new_with_planning_inputs), so a deployment can supply composition proofs there. That conflicts with #509. After the move:
Stage 1 uses only the planner's built-in analytical rules. It does not use AccuracyModel.
Shape: crates per stage, not modules inside today's crates.
asap-types keeps its crate name; the module names carry the meaning.
Accuracy model: analytical in Stage 1, the deployment's (empirical, pluggable) in Stage 3 only. Stage 1 keeps candidates that have no analytical rule, with guarantee = None.
Executor: stays in this repo as a separate crate. No stage crate depends on it, only integration-tests and devtools. Today asap-aware-mapping already has no dependency on asap-physical-operators.
Plan
One PR per step, each a pure move plus import updates:
Update (2026-10-03): verified move manifest and decisions
A file-by-file check against call sites on #541' found that this is not a pure move. Decisions:
Placement corrections:
asap-physical-operators::{plan, physical_planner} go to executor, not physical-optimizer. They compile plans into runtime operators, and plan and runtime import each other.
query_physical_lowering and storage_io go to plan-selection. Only cost code calls them, and they import analytical_cost.
Split execution_data_state: ExecutionTiming / ExecutionDataState go to types::ir::properties, because ir/node, ir/timing and ir/physical_export use them. The rest goes to types::physical.
Delete modules with no callers: pane_sharing, erp, empirical_comparison and post_asap::query_time.
Deleted (about 11.6k lines): summary_maintenance_lifecycle, summary_maintenance_cost/, summary_maintenance_dag_export, post_asap::{summary_maintenance, summary_maintenance_lifecycle}, SummaryWindowFramework, the lifecycle e2e test, the viewer's lifecycle UI and its proposal doc.
Kept: ir/timing (LifecycleAssignment is renamed MaterializationAssignment), and the pane primitives for Pass 2 window composition.
Until Stage 2 materialization exists, every node defaults to query time.
Backward dependencies:
Deleting the lifecycle removes the Stage 2 ↔ Stage 3 cycle.
The remaining cycle is {cost_model, replacement, recurrence, exact_composition}. Phase B breaks it by moving only the CostModel trait and the recurrence types into types (types only).
Phase C runs in parallel with Phase B. Stage 2 (physical_candidates) and Stage 3 (plan_selection) start as new modules and move with the rest.
Import rewrite volume: about 686 use lines in 228 files for the types step; the other steps are small.
Update (2026-10-04): Stage 1 stops using the cost model
B0 ("move only the CostModel trait into types") is not possible. The trait signature needs Stage 1 types that carry behavior, and two default methods call Stage 1 sizing and Stage 3 recurrence logic. Decisions:
The cost-dependent parts of replacement, exact_composition and grouping move to plan-selection or are deleted. This changes planner behavior and lands as its own PR before B2.
Q37.PlanningModels, the bundled cost and accuracy models, moves to plan-selection. The facade imports it from there.
Problem
The planning stages (#509) and the unified operator DAG (#511) are settled, and #514/#544 renamed the DAG types to match. The code layout still follows the old pre-ASAP / post-ASAP split:
asap-typeshaspre_asapandpost_asapmodules. The names no longer describe their contents.pre_asap::schemais the one schema for every DAG, and it already depends onpost_asap::sketch.post_asap::sketchis shared vocabulary that the logical stage uses.post_asap::execution_data_state::ExecutionTimingis physical-stage data. The newir/node.rsimports from both modules.asap-aware-mapping(45k lines) mixes three docs: propose workload-wide planning, summary sharing, and materialization #509 stages: Stage 1 rewriting (replacement,rewrite,rollup, …), Stage 2 materialization and lowering (summary_maintenance_lifecycle,query_physical_lowering, …), and Stage 3 cost and accuracy (cost_model,analytical_cost,erp, …). Its crate doc still describes it as "the cost-aware optimizer layer over the pre-ASAP intent algebra".asap-physical-operatorsmixes Stage 2 planning (plan,physical_planner) with Stage 4 execution (runtime,operators,sources,summary_kernels).Proposed layout
One crate per #509 stage, so Cargo enforces the one-way stage flow. Shared types are arranged by #511 section.
pre_asap,post_asap,asap-aware-mappingandasap-physical-operatorsdisappear. Logical and physical candidates are the sameOperatorNodeDAG under #511, differing only intiming, soLogicalASAPDAG/PhysicalASAPDAGstay intypes::ir. Leftover pre-#509 names are renamed in the same step:PostAsapDAG, thepre_asap_sub_dagexport key, andtools/dag-viewer/post_asap_fixture.json.Accuracy placement
#509 splits accuracy by stage:
accuracy/estimators, propagation through exact functions (composition), and error-budget allocation (allocation). Todayreplacement.rsis the main consumer.accuracy/reconciliation.erpandempirical_comparison.OperatorNode.guaranteeand records window-summary evidence, such as an EH boundary falling inside a bucket (summary_maintenance_cost/window.rs), for Stage 3.Stage 3 reads the analytical result from
OperatorNode.guarantee, which Stage 1 establishes (#511 §2.2), so plan-selection does not need to call the analytical model.The deployment's accuracy model plugs into Stage 3 only. Today the pluggable
AccuracyModeltrait is passed into Pass 1 (SketchAlgorithmStrategy::new_with_planning_inputs), so a deployment can supply composition proofs there. That conflicts with #509. After the move:AccuracyModel.guarantee = None, meaning unknown, never treated as exact (docs: define unified operators and SQL/PromQL scalar boundaries #511 §2.2).plan-selection/accuracy/.Decisions
asap-typeskeeps its crate name; the module names carry the meaning.guarantee = None.integration-testsanddevtools. Todayasap-aware-mappingalready has no dependency onasap-physical-operators.Plan
One PR per step, each a pure move plus import updates:
typesmodules (ir/,workload/,physical/; removepre_asap/post_asap)logical-optimizerphysical-optimizerplan-selectionexecutor, then retireasap-aware-mapping/asap-physical-operatorsThe module mapping above comes from module docs and call sites. Each step re-checks it file by file.
Follows #543 · Stages: #509 · IR: #511 · Tracker: #528
🤖 Generated with Claude Code
Update (2026-10-03): verified move manifest and decisions
A file-by-file check against call sites on #541' found that this is not a pure move. Decisions:
asap-physical-operators::{plan, physical_planner}go to executor, not physical-optimizer. They compile plans into runtime operators, andplanandruntimeimport each other.query_physical_loweringandstorage_iogo to plan-selection. Only cost code calls them, and they importanalytical_cost.execution_data_state:ExecutionTiming/ExecutionDataStatego totypes::ir::properties, becauseir/node,ir/timingandir/physical_exportuse them. The rest goes totypes::physical.pane_sharing,erp,empirical_comparisonandpost_asap::query_time.summary_maintenance_lifecycle,summary_maintenance_cost/,summary_maintenance_dag_export,post_asap::{summary_maintenance, summary_maintenance_lifecycle},SummaryWindowFramework, the lifecycle e2e test, the viewer's lifecycle UI and its proposal doc.ir/timing(LifecycleAssignmentis renamedMaterializationAssignment), and the pane primitives for Pass 2 window composition.cost_model,replacement,recurrence,exact_composition}. Phase B breaks it by moving only theCostModeltrait and the recurrence types intotypes(types only).MajorPass.physical_candidates) and Stage 3 (plan_selection) start as new modules and move with the rest.uselines in 228 files for the types step; the other steps are small.Update (2026-10-04): Stage 1 stops using the cost model
B0 ("move only the
CostModeltrait intotypes") is not possible. The trait signature needs Stage 1 types that carry behavior, and two default methods call Stage 1 sizing and Stage 3 recurrence logic. Decisions:MajorPass(cost-ranked Pass 1) is retired, and the planner facade runs the stage pipeline: Stage 1 (feat(planner): enumerate local logical alternatives without execution timing #561/feat(planner): Stage 1 whole-workload candidates and the stage_pipeline devtool #575) → Stage 2 → Stage 3 (feat(planner): Stage 2 physical candidates and Stage 3 cost-based selection #576).replacement,exact_compositionandgroupingmove to plan-selection or are deleted. This changes planner behavior and lands as its own PR before B2.PlanningModels, the bundled cost and accuracy models, moves to plan-selection. The facade imports it from there.