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>
104 lines
3.7 KiB
Rust
104 lines
3.7 KiB
Rust
//! Fill-proposal validator.
|
|
//!
|
|
//! PRD checks:
|
|
//! - Schema compliance (propose_done shape matches
|
|
//! `{fills: [{candidate_id, name}]}`)
|
|
//! - Completeness (endorsed count == target_count)
|
|
//! - Worker existence (every candidate_id present in workers_500k)
|
|
//! - Status check (active, not_on_client_blacklist)
|
|
//! - Geo/role match (worker city/state/role matches contract)
|
|
//!
|
|
//! Today this is a scaffold — schema check is real (it's cheap); the
|
|
//! worker-existence / status / geo checks need a catalog lookup and
|
|
//! land in a follow-up when the catalog query helper is wired into
|
|
//! this crate.
|
|
|
|
use crate::{Artifact, Report, Validator, ValidationError};
|
|
use std::time::Instant;
|
|
|
|
pub struct FillValidator;
|
|
|
|
impl Validator for FillValidator {
|
|
fn name(&self) -> &'static str { "staffing.fill" }
|
|
|
|
fn validate(&self, artifact: &Artifact) -> Result<Report, ValidationError> {
|
|
let started = Instant::now();
|
|
let value = match artifact {
|
|
Artifact::FillProposal(v) => v,
|
|
other => return Err(ValidationError::Schema {
|
|
field: "artifact".into(),
|
|
reason: format!("FillValidator expects FillProposal, got {other:?}"),
|
|
}),
|
|
};
|
|
|
|
// Schema check — the only real validation shipped in this
|
|
// scaffold. Catches the common "model emitted prose instead of
|
|
// JSON" failure mode before the consistency checks even run.
|
|
let fills = value.get("fills").and_then(|f| f.as_array()).ok_or(
|
|
ValidationError::Schema {
|
|
field: "fills".into(),
|
|
reason: "expected top-level `fills` array".into(),
|
|
},
|
|
)?;
|
|
for (i, fill) in fills.iter().enumerate() {
|
|
if fill.get("candidate_id").is_none() {
|
|
return Err(ValidationError::Schema {
|
|
field: format!("fills[{i}].candidate_id"),
|
|
reason: "missing".into(),
|
|
});
|
|
}
|
|
if fill.get("name").is_none() {
|
|
return Err(ValidationError::Schema {
|
|
field: format!("fills[{i}].name"),
|
|
reason: "missing".into(),
|
|
});
|
|
}
|
|
}
|
|
|
|
// TODO(phase-43 v2): worker-existence / status / geo checks.
|
|
// Need a catalog query handle injected into FillValidator's
|
|
// constructor — out of scope for the scaffold.
|
|
|
|
Ok(Report {
|
|
findings: vec![],
|
|
elapsed_ms: started.elapsed().as_millis() as u64,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn wrong_artifact_type_fails_schema() {
|
|
let r = FillValidator.validate(&Artifact::EmailDraft(serde_json::json!({})));
|
|
assert!(matches!(r, Err(ValidationError::Schema { .. })));
|
|
}
|
|
|
|
#[test]
|
|
fn missing_fills_array_fails_schema() {
|
|
let r = FillValidator.validate(&Artifact::FillProposal(serde_json::json!({})));
|
|
assert!(matches!(r, Err(ValidationError::Schema { field, .. }) if field == "fills"));
|
|
}
|
|
|
|
#[test]
|
|
fn fill_without_candidate_id_fails() {
|
|
let r = FillValidator.validate(&Artifact::FillProposal(serde_json::json!({
|
|
"fills": [{"name": "Jane"}]
|
|
})));
|
|
assert!(matches!(r, Err(ValidationError::Schema { field, .. }) if field.contains("candidate_id")));
|
|
}
|
|
|
|
#[test]
|
|
fn well_formed_proposal_passes_schema() {
|
|
let r = FillValidator.validate(&Artifact::FillProposal(serde_json::json!({
|
|
"fills": [
|
|
{"candidate_id": "W-123", "name": "Jane Doe"},
|
|
{"candidate_id": "W-456", "name": "John Smith"}
|
|
]
|
|
})));
|
|
assert!(r.is_ok(), "well-formed proposal should pass schema: {:?}", r);
|
|
}
|
|
}
|