Follow-up to #300 (the QueryIR wire choke point).
fetch_filtered (src/operations.rs:2418-2824, ~400 lines) exposes the materialization plan's internal shape as two optional kwargs the caller must pair correctly:
record_cls — required iff the plan is record
hop_classes — required for instances plans and traversed-projection record plans
Four PyValueError guard arms exist only to catch caller mis-pairing, and Python re-walks the relation-spec chain (_hop_classes_for_paths, duplicating resolve_join_hops) to ship a to_table → model class map that Rust could resolve itself from MODEL_REGISTRY (table→model is unambiguous, enforced at registration — see query.rs:322).
Proposal: dispatch on plan.materialization inside Rust. The payload already names the mode; Rust resolves the record class and hop classes from the registry keyed off the plan. The FFI shrinks to (cls, query_ir_json, route), the four pairing errors become unrepresentable states, and the Python-side spec-chain re-walk disappears.
Now safe to do against a pinned contract: the golden vectors assert the builder→serde bytes from both sides, so this is a pure Rust-side dispatch change with no wire impact.
Follow-up to #300 (the QueryIR wire choke point).
fetch_filtered(src/operations.rs:2418-2824, ~400 lines) exposes the materialization plan's internal shape as two optional kwargs the caller must pair correctly:record_cls— required iff the plan isrecordhop_classes— required forinstancesplans and traversed-projectionrecordplansFour
PyValueErrorguard arms exist only to catch caller mis-pairing, and Python re-walks the relation-spec chain (_hop_classes_for_paths, duplicatingresolve_join_hops) to ship ato_table → model classmap that Rust could resolve itself fromMODEL_REGISTRY(table→model is unambiguous, enforced at registration — seequery.rs:322).Proposal: dispatch on
plan.materializationinside Rust. The payload already names the mode; Rust resolves the record class and hop classes from the registry keyed off the plan. The FFI shrinks to(cls, query_ir_json, route), the four pairing errors become unrepresentable states, and the Python-side spec-chain re-walk disappears.Now safe to do against a pinned contract: the golden vectors assert the builder→serde bytes from both sides, so this is a pure Rust-side dispatch change with no wire impact.