vole-document 0.1.0-alpha.18

Persistent procedural document runtime: byte-exact reconstruction plus a content-addressed procedural seed DAG, queryable observations with provenance, and selective late materialization.
Documentation
//! EXPLAIN and EXPLAIN ANALYZE for field observations (Phase 11.5, ADR-0026).
//!
//! [`explain`] is a pure function of validated metadata: it never materializes a
//! node and never writes to the store. [`explain_analyze`] executes the
//! observation and returns both the intended plan and the measured actual work,
//! which is the central evidence surface for Phase-11 claims (ADR-0027).
//!
//! The JSON key sets are frozen and exact:
//!
//! * plan   — `selector`, `representation`, `format`, `adapter`, `capability`,
//!   `index_route`, `shape`, `index_reads`, `required_nodes`,
//!   `will_materialize`, `will_not_materialize`.
//! * actual — `format`, `adapter`, `index_nodes_read`, `seed_nodes_fetched`,
//!   `seed_nodes_materialized`, `member_decodes`, `xml_parses`,
//!   `nodes_id_shared`, `shared_resource_ids`, `inverse_work_units`,
//!   `descriptor_bytes_read`, `descriptor_read_mode`, `manifest_bytes_read`,
//!   `index_bytes_read`, `seed_bytes_read`, `bytes_read`, `bytes_returned`,
//!   `deepened`, `whole_source_materialized`, `wall_micros`, `basis`, `exact`.
//!
//! `format`/`adapter` are the detected format and its adapter; `capability` is the
//! resolved path (`native`, or `common:<selector> -> <adapter>`); `index_route` is
//! `hier-index`/`none`. `member_decodes`/`xml_parses` are observation-boundary
//! materialization requests (see [`ObserveStats`]); `whole_source_materialized` is
//! yes exactly when the answer verified whole-source integrity.
//!
//! `bytes_read` is the **sum** of the four `*_bytes_read` classes: total physical
//! bytes this observation made the OS fetch, including the descriptor blob. It is
//! deliberately not the seed-store-only number it was before Phase 11.9's
//! review (ADR-0027 accounting).

use crate::error::Result;
use crate::field::document_format::DocumentFormat;
use crate::field::manifest::FieldRoot;
use crate::field::observe::{ObserveRequest, ObserveStats, OpenedField, observe_opened};
use crate::field::plan::{ObservePlan, plan};
use crate::field::provenance::{Basis, IntegrityScope, json_escape};
use crate::field::{FieldId, FieldStore};
use crate::limits::Limits;

/// The intended plan plus its canonical JSON rendering.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExplainPlan {
    /// The structured plan.
    pub plan: ObservePlan,
    /// The plan as a flat JSON object with exactly the documented keys.
    pub json: String,
}

/// The measured cost of an executed observation.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExplainActual {
    /// The observation's stats.
    pub stats: ObserveStats,
    /// The basis of the answer that was produced.
    pub answer_basis: Basis,
    /// Whether the answer was exact.
    pub exact: bool,
    /// The detected document format (`"unknown"` when the manifest predates it).
    pub format: String,
    /// The adapter that served the answer.
    pub adapter: String,
    /// Whether the whole source was materialized (whole-source integrity checked).
    pub whole_source_materialized: bool,
}

impl ExplainActual {
    /// The actual work as a flat JSON object with exactly the documented keys.
    pub fn to_json(&self) -> String {
        let s = &self.stats;
        format!(
            concat!(
                "{{",
                "\"format\":\"{}\",",
                "\"adapter\":\"{}\",",
                "\"index_nodes_read\":{},",
                "\"seed_nodes_fetched\":{},",
                "\"seed_nodes_materialized\":{},",
                "\"member_decodes\":{},",
                "\"xml_parses\":{},",
                "\"nodes_id_shared\":{},",
                "\"shared_resource_ids\":{},",
                "\"inverse_work_units\":{},",
                "\"descriptor_bytes_read\":{},",
                "\"descriptor_read_mode\":\"{}\",",
                "\"manifest_bytes_read\":{},",
                "\"index_bytes_read\":{},",
                "\"seed_bytes_read\":{},",
                "\"bytes_read\":{},",
                "\"bytes_returned\":{},",
                "\"deepened\":{},",
                "\"whole_source_materialized\":{},",
                "\"wall_micros\":{},",
                "\"basis\":\"{}\",",
                "\"exact\":{}",
                "}}"
            ),
            json_escape(&self.format),
            json_escape(&self.adapter),
            s.index_nodes_read,
            s.seed_nodes_fetched,
            s.seed_nodes_materialized,
            s.member_decodes,
            s.xml_parses,
            s.nodes_id_shared,
            s.shared_resource_ids,
            s.seed_nodes_executed.saturating_add(s.bytes_read),
            s.descriptor_bytes_read,
            s.descriptor_read_mode.name(),
            s.manifest_bytes_read,
            s.index_bytes_read,
            s.seed_bytes_read,
            s.bytes_read,
            s.bytes_returned,
            s.deepened,
            self.whole_source_materialized,
            s.wall_micros,
            self.answer_basis.name(),
            self.exact,
        )
    }
}

/// Explain an observation without executing it. Pure.
pub fn explain(
    manifest: &FieldRoot,
    store: &FieldStore,
    req: &ObserveRequest,
) -> Result<ExplainPlan> {
    let plan = plan(manifest, store, req)?;
    let json = plan_json(req, &plan, manifest);
    Ok(ExplainPlan { plan, json })
}

/// Execute an observation and report both the intended plan and actual work.
///
/// The field is opened **once** here and reused for both the plan and the
/// evaluation (`observe_with_field`), so a cold `explain --analyze` reads the
/// descriptor blob exactly once (review fix #2). The reported `wall_micros`
/// covers the whole analysis: open + plan + evaluation.
pub fn explain_analyze(
    store: &mut FieldStore,
    id: &FieldId,
    req: &ObserveRequest,
    limits: Limits,
) -> Result<(ExplainPlan, ExplainActual)> {
    let started = std::time::Instant::now();
    let opened = OpenedField::open(store, id, req, limits)?;
    let planned = plan(opened.manifest(), store, req)?;
    let json = plan_json(req, &planned, opened.manifest());
    let (answer, mut stats, _) = observe_opened(store, &opened, req, limits)?;
    stats.wall_micros = started.elapsed().as_micros().min(u128::from(u64::MAX)) as u64;
    let fmt = DocumentFormat::from_provenance(&opened.manifest().provenance);
    let actual = ExplainActual {
        stats,
        answer_basis: answer.basis,
        exact: answer.exact,
        format: fmt.map_or("unknown", |f| f.name()).to_string(),
        adapter: fmt.map_or("unknown", |f| f.adapter()).to_string(),
        whole_source_materialized: answer.integrity_scope == IntegrityScope::WholeSource,
    };
    Ok((
        ExplainPlan {
            plan: planned,
            json,
        },
        actual,
    ))
}

fn plan_json(req: &ObserveRequest, plan: &ObservePlan, manifest: &FieldRoot) -> String {
    let fmt = DocumentFormat::from_provenance(&manifest.provenance);
    let format_name = fmt.map_or("unknown", |f| f.name());
    let adapter = fmt.map_or("unknown", |f| f.adapter());
    let capability = match crate::field::capabilities::common_selector_name(&req.selector) {
        Some(name) => format!("common:{name} -> {adapter}"),
        None => "native".to_string(),
    };
    let index_route = if manifest.has_index() {
        "hier-index"
    } else {
        "none"
    };
    format!(
        concat!(
            "{{",
            "\"selector\":\"{}\",",
            "\"representation\":\"{}\",",
            "\"format\":\"{}\",",
            "\"adapter\":\"{}\",",
            "\"capability\":\"{}\",",
            "\"index_route\":\"{}\",",
            "\"shape\":\"{}\",",
            "\"index_reads\":{},",
            "\"required_nodes\":{},",
            "\"will_materialize\":{},",
            "\"will_not_materialize\":{}",
            "}}"
        ),
        json_escape(&req.selector.canonical()),
        json_escape(req.representation.name()),
        json_escape(format_name),
        json_escape(adapter),
        json_escape(&capability),
        index_route,
        plan.shape.name(),
        plan.index_reads,
        plan.required_nodes,
        string_array(&plan.will_materialize),
        string_array(&plan.will_not_materialize),
    )
}

fn string_array(items: &[String]) -> String {
    let body = items
        .iter()
        .map(|s| format!("\"{}\"", json_escape(s)))
        .collect::<Vec<_>>()
        .join(",");
    format!("[{body}]")
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn actual_json_has_exactly_the_documented_keys() {
        let actual = ExplainActual {
            stats: ObserveStats::default(),
            answer_basis: Basis::DirectlyObserved,
            exact: true,
            format: "unknown".to_string(),
            adapter: "unknown".to_string(),
            whole_source_materialized: false,
        };
        assert_eq!(
            actual.to_json(),
            "{\"format\":\"unknown\",\"adapter\":\"unknown\",\"index_nodes_read\":0,\"seed_nodes_fetched\":0,\"seed_nodes_materialized\":0,\"member_decodes\":0,\"xml_parses\":0,\"nodes_id_shared\":0,\"shared_resource_ids\":0,\"inverse_work_units\":0,\"descriptor_bytes_read\":0,\"descriptor_read_mode\":\"full\",\"manifest_bytes_read\":0,\"index_bytes_read\":0,\"seed_bytes_read\":0,\"bytes_read\":0,\"bytes_returned\":0,\"deepened\":false,\"whole_source_materialized\":false,\"wall_micros\":0,\"basis\":\"directly-observed\",\"exact\":true}"
        );
    }

    #[test]
    fn string_array_escapes() {
        assert_eq!(
            string_array(&["a".into(), "b\"c".into()]),
            "[\"a\",\"b\\\"c\"]"
        );
        assert_eq!(string_array(&[]), "[]");
    }
}