polyc-agent 2026.9.0

The agent turn loop: provider + tool-call routing, shared by the control plane and harness.
//! Golden-vector schema for the compaction recall eval (#1134, #1298).
//!
//! Two eval tiers deserialize the same portable vector files owned by
//! `polyc-conformance-vectors` (`crates/conformance-vectors/vectors/compaction/*.json`):
//!
//! - the agent-side rendering-mechanics tier, `crates/agent/tests/compaction_recall.rs`;
//! - the control-plane pipeline tier, `crates/control-plane/src/grpc/recall_eval.rs`.
//!
//! Before #1298 each tier carried its own hand-copied deserialize structs,
//! and they had already drifted (mismatched `#[allow(dead_code)]` attributes
//! between the two copies). This module is the one definition both tiers
//! import, so a schema change can no longer land in only one place.
//!
//! Test-only: gated behind the `test-fixtures` feature (never a default
//! feature) so this never reaches a normal build's public surface, and so
//! `missing_docs`/`doc-strict` never see it. Each consuming crate's test
//! build turns the feature on via a `[dev-dependencies]` edge — see
//! `crates/agent/Cargo.toml` (a self-edge, for this crate's own integration
//! tests) and `crates/control-plane/Cargo.toml`.

#![allow(missing_docs, clippy::pedantic, clippy::nursery)]

use serde::Deserialize;

/// One golden vector: a family of seeded rounds plus the identifiers a
/// conforming pipeline must retain (or, for `clip_shadowed`, must NOT
/// deliver past the render clip — see the standing-miss note in
/// `compaction_recall.rs`).
#[derive(Deserialize)]
pub struct Vector {
    pub family: String,
    pub description: String,
    pub rounds: Vec<Round>,
    pub required: Vec<Required>,
    #[serde(default)]
    pub superseded: Vec<String>,
    #[serde(default)]
    pub clip_shadowed: Vec<ClipShadowed>,
}

/// One compaction round: every turn folded together before the next
/// compaction trigger fires.
#[derive(Deserialize)]
pub struct Round {
    pub turns: Vec<Turn>,
}

/// One turn's messages, in wire order.
#[derive(Deserialize)]
pub struct Turn {
    pub messages: Vec<VectorMessage>,
}

/// A single seeded message, tagged by its transcript role.
#[derive(Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum VectorMessage {
    User {
        text: String,
    },
    Assistant {
        text: String,
    },
    ToolCall {
        id: String,
        name: String,
        args_json: String,
    },
    ToolResult {
        id: String,
        result_json: String,
    },
}

/// An identifier the eval expects to survive: its class, its verbatim text,
/// and the round it was seeded in.
#[derive(Deserialize)]
pub struct Required {
    pub class: String,
    pub text: String,
    pub round: usize,
}

/// An identifier deliberately seeded beyond the summarizer's render clip —
/// the documented standing miss (#1297).
#[derive(Deserialize)]
pub struct ClipShadowed {
    pub text: String,
    #[serde(rename = "where")]
    pub location: String,
}