polyc-query 2026.9.0

Read layer over the event log: a DataFusion engine for SQL over replayed partitions, and a per-conversation Parquet projection for participation-scoped search.
[package]
name = "polyc-query"
version.workspace = true
description = "Read layer over the event log: a DataFusion engine for SQL over replayed partitions, and a per-conversation Parquet projection for participation-scoped search."
edition.workspace = true
license.workspace = true
authors.workspace = true
rust-version.workspace = true
repository.workspace = true

[package.metadata.polychrome]
# Software-system layer — see docs/architecture/. A Component composed
# into the polyc-control-plane Container; depends inward only (never on a
# Container). Every `polyc-*` dependency below is itself a foundation, but
# this crate also owns the DataFusion `SessionContext`/`TableProvider`
# integration work, so — matching `polyc-eventlog-host`'s own
# classification — it is a plain (non-foundation) Component rather than a
# leaf.
layer = "component"
# Logical plane role in the separated state, projection, and query topology
# (#1565, chunk A1) — a different question than `layer` above. The canonical
# policy is arch-capabilities.toml; scripts/check_plane_capabilities.py checks it.
plane_role = "query"
provides = ["projection-writer", "query-engine", "query-internal"]
# One id per named construct this package provides — see arch-capabilities.toml.
# Removing one authority removes its id here, so its exception goes stale on its
# own even while the package keeps providing the capability.
capability_sites = ["PRV-13", "PRV-14", "PRV-15", "PRV-27"]

[lib]
path = "src/lib.rs"

[dependencies]
polyc-proto = { version = "=2026.9.0", path = "../proto" }
polyc-eventlog = { version = "=2026.9.0", path = "../eventlog" }
# The sealed funnel's own replay handle (`authority::QueryAuthority`): Admin
# scope enumerates partitions via `EventLogHost::list_partitions`, both scopes
# replay through `EventLogHost::replay_with_positions` — this crate owns that
# step; the control plane must never keep a second copy of it (see
# `authority`'s module doc).
polyc-eventlog-host = { version = "=2026.9.0", path = "../eventlog-host" }
# The State plane's kernel vocabulary (#1565, chunk B6): the maintained
# projections here read the durable commit feed instead of an in-process
# observer, so this crate names `polyc_state::feed::FeedRecord` and the
# journal records one commit brackets. A foundation Component that owns
# semantic contracts only — no transport, no storage — so a projection can
# speak the feed's vocabulary without depending on how it arrives.
polyc-state = { version = "=2026.9.0", path = "../state" }
# Query uses the closed projection-family registry. Query derives schema-only plans from
# this declaration. Query validates the exact schema and fact versions that
# State publishes. Query does not declare a second descriptor model.
polyc-projection = { version = "=2026.9.0", path = "../projection" }
# Exact signed-manifest admission and realm-typed immutable artifact reads.
# Query receives only its object-safe read facades; writer and raw backend
# capabilities remain outside this crate.
polyc-projection-artifact = { version = "=2026.9.0", path = "../projection-artifact" }
# The query protocol's own vocabulary. This crate is the one inward layer
# that may see both it and the state plane's audit types, so it owns the
# field-complete conversion between them.
polyc-query-model = { version = "=2026.9.0", path = "../query-model" }
# Query owns the closed local/remote execution-permit sum. This transport
# adapter exposes the remote permit without exposing generated wire types.
polyc-state-connect = { version = "=2026.9.0", path = "../state-connect" }
# The production projection reader holds only `GcsReadClient`: an exact-
# generation surface with a fixed read-only OAuth scope and no create, mutable
# read, list, or delete method. The broader storage client never enters Query.
polyc-storage-gcs = { version = "=2026.9.0", path = "../storage-gcs" }
# `authority::QueryAuthority`'s admin-session path: `polyc_crypto::session::
# verify_session`/`RevokedTokens`, and the ed25519 primitives the moved-in
# conversation-grant mint/verify pair signs/checks with. A foundation
# Component — layer-legal for this (non-foundation) Component to depend on.
polyc-crypto = { version = "=2026.9.0", path = "../crypto" }
# The legacy reference adapter and fact-model value types. Production persona
# and participation authorization arrives through the narrow State-backed
# `PersonaSource`; F0 removes this physical-host compatibility dependency.
polyc-persona = { version = "=2026.9.0", path = "../persona" }
# Transport-free current-session authorization port. The State/Connect adapter
# is composed outside this crate; Query receives only the narrow verifier.
polyc-session-family = { version = "=2026.9.0", path = "../session-family" }
# The shared fold library: `decode::payments` decodes through its
# `verified_receipts` fold and `decode::message_content` through its
# `fold_message_content` fold — the fact model's fold-coupled typed tables
# (`payments`, `messages`/`tool_calls`) decode through these SHARED folds,
# never a second, independently-written signature check or match over
# `content::Type`. A foundation
# Component — layer-legal for this (non-foundation) Component to depend on,
# same as `polyc-crypto` above.
polyc-facts = { version = "=2026.9.0", path = "../facts" }
# The canonical settlement-amount readers (#1739): `dashboard`'s spend rollup
# turns a receipt's stored `amount` STRING into a number, and the two
# settlement directions store different units in that one field. Reading them
# through `polyc_payments::amount`'s two direction-specific readers — the same
# module the control plane's committed-spend floor and wallet history read
# through — is what keeps this rollup from re-inventing a third parse. A
# foundation Component, layer-legal for this (non-foundation) Component to
# depend on, same as `polyc-crypto`/`polyc-facts` above.
polyc-payments = { version = "=2026.9.0", path = "../payments" }
# Lock-free handle retained for the legacy test constructor. Production uses a
# State-backed `PersonaSource`; F0 removes this compatibility cell.
arc-swap = { workspace = true }
# Pinned exactly, not floated — this crate's own isolation boundary for the
# DataFusion embedding. `arrow` MUST track datafusion's own internal
# arrow version exactly; re-verify both pins on every DataFusion bump.
datafusion = "=54.1.0"
arrow = "=58.3.0"
# The search projection writes and reads Parquet directly (Bloom-filter and
# statistics pruning, plus footer key-value metadata for the coverage
# watermark). Same version DataFusion already resolves, so no new tree.
parquet = { version = "=58.3.0", features = ["arrow"] }
bytes = { workspace = true }
futures = { workspace = true }
# The community/contrib `json_get`/`->`/`->>` scalar UDFs `crate::decode`'s
# module docs describe (#1313's follow-up) — pinned exactly, tracking the
# `datafusion = "=54.1.0"` pin above: this crate declares no `arrow` of its
# own, so it inherits datafusion 54's arrow rather than risking a second
# arrow version in the lockfile (re-verify on every DataFusion bump, same as
# `arrow` itself). Not an official ASF release; this crate accepts that
# dependency-policy tradeoff for the scalar `json_get`/`->`/`->>` UDFs it
# needs and no upstream crate provides.
datafusion-functions-json = "=0.54.2"
thiserror = { workspace = true }
tracing = { workspace = true }
# `authority::canonical_search_scope`'s scope hash — the same pin
# `polyc-wallet-delegation`/`polyc-control-plane` already use for their own
# domain-separated hashing, never a second hash crate for this one call site.
blake3 = { workspace = true }
# `QueryEngine::execute`'s hard wall-clock timeout (`tokio::time::timeout`)
# over the collected query future. Only the `time` driver is needed at this
# layer — the control plane supplies the ambient multi-threaded runtime.
# `rt` for `spawn_blocking`: the search projection's filesystem and Parquet
# work is blocking, and its consumers run on the control plane's runtime.
tokio = { workspace = true, features = ["rt", "sync", "time"] }
# `search_index::worker::SearchIndexWorker::run`'s shutdown signal — the SAME
# `CancellationToken` type `EventLogHost::spawn` already takes, so the Container
# hands one token to the host and to this worker rather than inventing a second
# stop mechanism for a task it supervises alongside it.
tokio-util = { workspace = true }
# `QueryAuditRecord::into_event_payload`'s `Message::encode_to_vec` —
# already workspace-pinned by `polyc-proto`'s own dependency on it.
buffa = { workspace = true }
# `output::output_to_json`'s shared row-serialization helper (the HTTP handler,
# the fleet MCP tool, and the conversation-query built-ins in
# `polyc-control-plane` all call it — never three hand-rolled versions): builds the wire JSON envelope from the engine's
# `RecordBatch`es via `arrow`'s own JSON writer (default `json` feature).
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
# `authority::grant`'s moved-in conversation-grant token codec
# (`base64url(claims_json).base64url(signature)`) — byte-identical to the
# encoding the control plane used before the move.
base64 = { workspace = true }
# `metrics`'s own query-sizing series (QRY-3 hardening review item D) —
# registered into the process default registry, the same pattern
# `polyc-eventlog`/`polyc-llm`/`polyc-slack` (among other Components/edges)
# already use for their own crate-level metrics; no separate scrape endpoint
# or registry plumbing.
prometheus = { workspace = true }
# `routine_catalog::RoutineCatalog`'s `dyn`-compatible async trait — this
# crate never depends on `kube`/`polyc-controller` itself (both far heavier
# than this crate's own isolation boundary should absorb; see
# `routine_catalog`'s module doc), so the control plane's kube-backed
# implementation is the only place `async fn` in a `dyn` trait needs this
# crate — a native `async fn` in a trait is not `dyn`-safe without it.
async-trait = { workspace = true }
# Names the generic State Connect transport bound for the private production
# metadata adapter. Generated wire types remain sealed in `polyc-state-connect`.
connectrpc = { workspace = true }
# `dashboard`'s per-turn accumulator keys on the turn id `polyc_proto::kinds::
# parse` recovers from a `base:{turn_uuid}` kind suffix — a real `uuid::Uuid`,
# not a `String`, so a malformed suffix can never silently key two distinct
# turns under the same accidental string. Every other consumer of that same
# `Option<uuid::Uuid>` return type in this crate is test-only, hence `uuid`
# living only in `[dev-dependencies]` before this module needed it in
# production code too.
uuid = { workspace = true }

[dev-dependencies]
# The real State Connect server, so the Remote settlement arm is proved
# against the actual transport rather than a double that repeats its logic.
polyc-state-service = { path = "../state-service" }
axum.workspace = true
# `#[tokio::test]` plus the `execute()` timeout path under test.
tokio = { workspace = true, features = ["macros", "net", "rt", "rt-multi-thread", "time"] }
polyc-state = { path = "../state", features = ["test-util"] }
sha2 = { workspace = true }
# The real-journal fixture (`engine::tests::real_journal_round_trip_*`):
# `commonware_runtime::deterministic` drives an actual
# `polyc_eventlog::EventLog` write+replay round trip, matching
# `polyc-eventlog`'s own test style.
commonware-runtime = { workspace = true }
# `ApprovalSigner::from_seed` (test-only deterministic signer) for
# `authority`'s own admin-session/grant-verification tests.
polyc-crypto = { path = "../crypto", features = ["test-util"] }
# `decode::message_content`'s own test fixtures build wire `Content` blocks
# directly (`FunctionCallContent`/`FunctionResultContent`'s `arguments`/
# `response` fields are `google.protobuf.Struct`) — not part of this crate's
# production surface, matching `polyc-facts`'s own dev-dependency on it for
# the identical fixture shape.
buffa-types = { workspace = true }
# QRY-6's A2-seal compile-fail guard (`tests/compile_fail.rs`): proves an
# external crate cannot name `engine`/`decode`/`provider`/`session`/
# `statement_gate`/`views` — every item this crate's `authority` module doc
# claims is sealed `pub(crate)` — rather than relying on convention plus
# manual review alone, which a PR widening one of those to `pub` would
# otherwise pass silently (every other test and `just arch` stay green).
trybuild = "1.0"
# Captures the dashboard rollup's unreadable-amount log line, to prove that
# call site passes its OWN `AmountReadSite` rather than a copy-pasted one (the
# counter is labeled by direction and reason only, so it cannot tell). Not a
# hand-rolled `tracing::subscriber::set_default`: tracing's per-callsite
# `Interest` cache is process-global, so a callsite first hit by another
# concurrently-running test can race a thread-scoped override into missing
# events. Same rationale as `polyc-payments`' and `polyc-control-plane`'s own
# dev-dependency on it.
#
# `no-env-filter` because the line under assertion is emitted by
# `polyc_payments::amount::record_unreadable_amount`, not by this crate, and
# the default filter is `<this crate>=trace` — without it the assertion is
# vacuous rather than failing loudly. Cargo unifies features, so the three
# crates dev-depending on `tracing-test` declare it identically.
tracing-test = { version = "0.2", features = ["no-env-filter"] }

[features]
# Retired v2 bearer/denylist constructor for compatibility fixtures in this
# crate and the control-plane's HTTP tests. Production callers can construct
# only the State-backed authority.
test-util = []

[lints]
workspace = true