Audience: developers adding planner behavior. Read the architecture and the contracts first. Each recipe lists the files to change and the tests to add; keep each change inside the stage that owns the decision.
A new sketch that Pass 1 can offer for an intent.
- Type. Add the variant to
SketchAlgorithmand its parameters toSketchParams(crates/types/src/ir/schema/sketch.rs). - Capability. Add its row to
FamilyCapability::of(crates/types/src/ir/schema/family_capability.rs): which inputs it can merge (MergeInputRule) and which statistics it answers (StatisticKind, with the merged error). Pass 2's sharing, pane and rollup rules read only this table. - Offer and size. List it for its intents in
realization::summary_candidates(crates/logical-asap-optimization), most preferred first, and size it inrealization::default_size_params. - Accuracy. Add its error model under
crates/logical-asap-optimization/src/accuracy/estimators/and return it fromDefaultAccuracyModel'slocal_guarantee, by inverting the same sizing formulas. Without a guarantee, every candidate that uses it is rejected. - Cost. Give it a state size and per-operation CPU in
crates/plan-selection/src/lib.rs(and accept its parameters incrates/plan-selection/src/measured_operation_costs.rsif deployments measure it). - Executor. Wrap the
asap_sketchlibimplementation incrates/executor/src/summary_kernels/and register it infactory.rsandcapability.rs. Gaps in the library are fixed upstream, not with a local copy. - Labels. Name it in
asap_devtools::candidate_labels.
Tests: a Pass 1 test in crates/logical-asap-optimization/tests/per_computation_candidates.rs
that the intent gets the new candidate (and that a deployment without it
prunes it with UnsupportedByDeployment); an estimator test beside the
estimator; an execution test in crates/integration-tests/tests/ such as
frequency_execution.rs or kll_pane_execution.rs.
A new way for computations of one Pass 1 candidate to share, split or reuse work.
- In
crates/logical-asap-optimization/src/asap_aware_cse.rs, add the part it can choose as a*Candidatetype with a finder (for examplerollup_candidates(roots)), and the function that applies a set of them and returns*Pass2Candidates (for examplerollup_pass2_candidates). Read summary families only throughFamilyCapability. - In
crates/logical-asap-optimization/src/workload_candidates.rs, call it from Stage 1 enumeration after the existing rules and record what it applied in a newPass2Candidatefield. - Rebuild its label from that field in
asap_devtools::candidate_labels. - Add its row to the Pass 2 rule table of the design.
A rule must keep every Pass 1 candidate, must not add a family or a readout, and must keep the composed DAG's meaning. Stage 3 decides whether it pays off.
Tests: unit tests beside the rule in asap_aware_cse.rs for when it applies
and when it must not; an end-to-end test like
crates/integration-tests/tests/reuse_rules.rs that the planned workload
contains the candidate and that Stage 3 prices it.
- Add the
Materializationvariant (crates/types/src/physical/materialization.rs) and its timing incrates/types/src/ir/properties/timing.rs, with its data-state edge rules. - Offer it in
crates/physical-asap-optimization/src/lib.rs(theMaterializationCandidateoptions), after the existing options so default candidate ids stay the same, and gate it on the deployment input that allows it. - Price it in
crates/plan-selection/src/lib.rs: what it keeps in memory or on disk, and what each evaluation rebuilds.
Tests: Stage 2 unit tests for when the option is offered;
crates/integration-tests/tests/materialization_e2e.rs and the Example 4
tests in planner_layering_example4.rs.
- A new validity check goes in
select_planand rejects a candidate with aRejection { cause, reason }whosecauseis a new failed-checkRejectionCausevariant (listed inRejectionCause::failed_a_check); it never fails the whole selection unless the input itself is bad (a newSelectionErrorvariant, documented as bad input). - A new pricing input goes in
PlanSelectionSettings(validated in itsvalidate); a new statement about the executor goes inDeploymentCapabilities. Both reach every stage throughPlanningModels. - Expose it in
stage_pipelineand in the Stage Viewer guide.
Tests: a Stage 3 unit test that the check rejects exactly the candidates it
should, and a crates/devtools/tests/stage_pipeline.rs test for the flag.
Lower the construct in its frontend (crates/frontend-promql/src/promql.rs,
crates/frontend-sql/src/sql/, crates/frontend-metricsql/src/) into
existing NonASAPOp nodes and AggIntents. If it needs a new intent, add
it in asap-types and offer its candidates in Pass 1 (recipe 1). A construct
that cannot be represented is rejected with the frontend's
unsupported-feature error, never lowered approximately.
Tests: frontend tests for the lowering and its rejection cases; the corpus
tests (analyze_corpora, variant_coverage) show the coverage change.
- The decision is made in the stage that owns it (contracts §6).
- Every new public type, field and variant has a doc comment.
- Unit tests beside the code and one end-to-end test through the stages.
- The design document's examples still plan the same, or the change is
explained: run the
planner_layering_example*tests andstage_pipeline --example planner-layering-1(and-2,-3a,-3b). - The design, developer and user docs that name what changed are updated.