PROBLEM
python-sdk has an approved fluent-query design (#212) but no engine. This issue delivers milestone 1 of the Rust query engine: a deferred, explainable, backend-neutral query core incubated in-repo as a Python-independent crate, surfaced through a fluent Python facade.
SCOPE BOUNDARY
No schema-v2 change; no analyzer work. Consumes schema 2.0.0 as-is. Cross-service vocabulary (services, gRPC/proto boundaries) is Epic E — no service()/rpc()/across_services() verbs here. Dataflow slicing stays on the existing cldk.graph engine for M1 (replacement is the follow-on child). Packaging/release-matrix work is the finishing child, not this one (local builds via maturin develop are enough here).
GOALS (the contract, as a checklist)
- Algebra ADR locked first: LogicalPlan vocabulary designed fresh from the 07-09 primitives + RFC sketch; recorded before core code lands.
- Root-level
crates/ Cargo workspace (workspace Cargo.toml at repo root; polars/ruff idiom): cldk-query-core (no PyO3 anywhere in it) + cldk-python (bindings compiled to the private submodule cldk._native; maturin driven from the root pyproject via tool.maturin.manifest-path).
- Core reads schema-2.0.0
analysis.json via serde CPG models; identity is can:// ids end to end.
- Fluent chains build plans without executing; strict terminator boundary (
.collect(), .uris(), .count(), .explain()); iterating a Query raises with a hint.
.explain() renders initial + optimized plans, backend lowering, and any Opaque split points.
- Optimizer performs at least: filter pushdown, adjacent-traversal collapse, dead-branch elimination.
- Backends: in-memory (serde substrate) and Neo4j (core lowers to Cypher, executes over Bolt via neo4rs); identical results on both.
- Opaque(fn) plan-split semantics: Rust prefix → URIs → Python lambda → re-enter Rust.
PlanEnvelope wire format (semver string): serialized plans round-trip and reconstruct without Python fluent objects.
- M1 gate: the two Odoo PoE audit queries (
public_unauth_sudo, inapp_sudo_no_gate) run end-to-end on both backends and .explain() reproduces the manual audit evidence.
CAVEATS AND KNOWN RISKS
- Algebra redesign is on the critical path — nothing in the core builds until the ADR is locked; timebox it.
- neo4rs in-core widens the pre-extraction API surface; Bolt auth/session semantics must not leak into plan types.
- The plan-split re-entry (goal 8) is the hardest boundary piece — a lambda between two Rust stages must not force full materialization of intermediate objects, only URIs.
- Python-version support: PyO3 abi3 target must cover the SDK's supported range (3.11+).
DEFINITION OF DONE
- M1 gate exact: both PoE queries return the hand-verified entity sets from the audit, not merely non-empty results; in-memory and Neo4j results byte-identical after URI sort.
cargo test green in cldk-query-core with no Python interpreter available.
- Plan serialization round-trip test: chain → envelope → JSON → envelope → identical plan hash.
- Facade conformance: every fluent verb maps to a registry entry; illegal verbs raise the registry diagnostic.
Part of #279
PROBLEM
python-sdk has an approved fluent-query design (#212) but no engine. This issue delivers milestone 1 of the Rust query engine: a deferred, explainable, backend-neutral query core incubated in-repo as a Python-independent crate, surfaced through a fluent Python facade.
SCOPE BOUNDARY
No schema-v2 change; no analyzer work. Consumes schema 2.0.0 as-is. Cross-service vocabulary (services, gRPC/proto boundaries) is Epic E — no
service()/rpc()/across_services()verbs here. Dataflow slicing stays on the existingcldk.graphengine for M1 (replacement is the follow-on child). Packaging/release-matrix work is the finishing child, not this one (local builds via maturin develop are enough here).GOALS (the contract, as a checklist)
crates/Cargo workspace (workspace Cargo.toml at repo root; polars/ruff idiom):cldk-query-core(no PyO3 anywhere in it) +cldk-python(bindings compiled to the private submodulecldk._native; maturin driven from the root pyproject viatool.maturin.manifest-path).analysis.jsonvia serde CPG models; identity iscan://ids end to end..collect(),.uris(),.count(),.explain()); iterating a Query raises with a hint..explain()renders initial + optimized plans, backend lowering, and any Opaque split points.PlanEnvelopewire format (semver string): serialized plans round-trip and reconstruct without Python fluent objects.public_unauth_sudo,inapp_sudo_no_gate) run end-to-end on both backends and.explain()reproduces the manual audit evidence.CAVEATS AND KNOWN RISKS
DEFINITION OF DONE
cargo testgreen incldk-query-corewith no Python interpreter available.Part of #279