Some checks failed
lakehouse/auditor 3 blocking issues: todo!() macro call in tests/real-world/scrum_master_pipeline.ts
Phase 43 PRD (docs/CONTROL_PLANE_PRD.md:161) was the one audit finding
truly unimplemented — no crate, no trait, no tests, no workspace entry.
Neither PHASES.md nor the source tree had any Phase 43 presence.
Genuine greenfield gap.
Lands the scaffold as a real crate, registered in workspace Cargo.toml:
crates/validator/
src/lib.rs — Validator trait, Artifact enum (5 variants:
FillProposal, EmailDraft, Playbook,
TerraformPlan, AnsiblePlaybook), Report,
Finding, Severity, ValidationError
src/staffing/mod.rs — staffing validators module root
src/staffing/fill.rs — FillValidator (schema-level: fills array
+ per-fill {candidate_id, name}). 4 tests.
Worker-existence + status + geo checks
are TODO v2 (need catalog query handle).
src/staffing/email.rs — EmailValidator (to/body schema + SMS ≤160
+ email subject ≤78). 4 tests. PII scan +
name-consistency TODO v2.
src/staffing/playbook.rs — PlaybookValidator (operation prefix,
endorsed_names non-empty + ≤ target×2,
fingerprint present per Phase 25). 5 tests.
src/devops.rs — TerraformValidator + AnsibleValidator
scaffolds. Return Unimplemented — keeps
dispatcher shape stable, surfaces a clear
"phase 43 not wired" signal instead of
silently passing or panicking.
Total: 15 tests, all green. Covers the happy paths, the common
failure modes (missing fields, overfull arrays, length violations),
and the dispatch-error path (wrong artifact type into wrong validator).
Still open from Phase 43 (v2 work, beyond scaffold):
- FillValidator catalog integration (worker-existence, status,
geo/role match) — needs catalog handle in constructor
- EmailValidator PII scan (shared::pii::strip_pii integration) +
name-consistency cross-check
- Execution loop wiring: generate → validate → observer correction
+ retry (bounded by max_iterations=3) — spans crates, follow-up
- Observer logging: validation results to data/_observer/ops.jsonl
and data/_kb/outcomes.jsonl
- Scenario fixture tests against tests/multi-agent/playbooks/*
Workspace warnings still at 0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
96 lines
3.6 KiB
Rust
96 lines
3.6 KiB
Rust
//! Phase 43 Validation Pipeline.
|
|
//!
|
|
//! PRD: "Staffing outputs run through schema / completeness /
|
|
//! consistency / policy gates. Plug into Layer 5 execution loop —
|
|
//! failure triggers observer-correction iteration."
|
|
//!
|
|
//! This crate provides the `Validator` trait + `Artifact` enum +
|
|
//! Report/ValidationError types. Staffing validators (fill, email,
|
|
//! playbook) and the DevOps scaffold live in submodules.
|
|
//!
|
|
//! Landed 2026-04-24 as a scaffold — the trait + types + module
|
|
//! layout match the PRD; individual validator implementations are
|
|
//! `Unimplemented` stubs that return a clear "phase 43 not wired"
|
|
//! error rather than silently passing. The execution-loop integration
|
|
//! (generate → validate → correct → retry) comes in a follow-up
|
|
//! commit once the stubs are filled.
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
use thiserror::Error;
|
|
|
|
pub mod staffing;
|
|
pub mod devops;
|
|
|
|
/// What a validator saw. One variant per artifact class we validate.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
#[serde(tag = "kind")]
|
|
pub enum Artifact {
|
|
/// A fill proposal from the staffing executor — shape is
|
|
/// `{fills: [{candidate_id, name}]}` per PRD.
|
|
FillProposal(serde_json::Value),
|
|
/// An email/SMS draft for outreach.
|
|
EmailDraft(serde_json::Value),
|
|
/// A playbook being sealed for memory.
|
|
Playbook(serde_json::Value),
|
|
/// Terraform plan output (scaffold, long-horizon).
|
|
TerraformPlan(serde_json::Value),
|
|
/// Ansible playbook (scaffold, long-horizon).
|
|
AnsiblePlaybook(serde_json::Value),
|
|
}
|
|
|
|
/// Success report. Empty `findings` means a clean pass. Populated
|
|
/// findings with `Severity::Warning` means "acceptable but notable" —
|
|
/// the artifact passes. `Severity::Error` means validation failed;
|
|
/// the validator should return `Err(...)` in that case, not `Ok`.
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Report {
|
|
pub findings: Vec<Finding>,
|
|
pub elapsed_ms: u64,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct Finding {
|
|
pub field: String,
|
|
pub severity: Severity,
|
|
pub message: String,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum Severity {
|
|
Warning,
|
|
Error,
|
|
}
|
|
|
|
/// Validation failure — what went wrong + where + why. Returned as
|
|
/// `Err` from `validate`. Execution loop catches these and feeds them
|
|
/// to the observer-correction retry loop.
|
|
#[derive(Debug, Clone, Error, Serialize, Deserialize)]
|
|
pub enum ValidationError {
|
|
/// Artifact schema doesn't match what we expected.
|
|
#[error("schema mismatch at {field}: {reason}")]
|
|
Schema { field: String, reason: String },
|
|
/// Required data missing (e.g. endorsed count != target count).
|
|
#[error("completeness: {reason}")]
|
|
Completeness { reason: String },
|
|
/// Data that's inconsistent with another source of truth
|
|
/// (e.g. worker_id doesn't exist in the workers table).
|
|
#[error("consistency: {reason}")]
|
|
Consistency { reason: String },
|
|
/// Policy violation — truth rule or access control said no.
|
|
#[error("policy: {reason}")]
|
|
Policy { reason: String },
|
|
/// Validator hasn't been implemented yet — scaffold stub.
|
|
#[error("validator not yet implemented for {artifact} — phase 43 scaffold")]
|
|
Unimplemented { artifact: &'static str },
|
|
}
|
|
|
|
/// Core validation contract. Implementations live in `staffing::*` and
|
|
/// `devops::*`. The execution loop dispatches to the right impl based
|
|
/// on the Artifact variant.
|
|
pub trait Validator: Send + Sync {
|
|
fn validate(&self, artifact: &Artifact) -> Result<Report, ValidationError>;
|
|
/// Human-readable name for logs + Langfuse traces.
|
|
fn name(&self) -> &'static str;
|
|
}
|