Day 4 — Ground Every Claim
Today you fix the most important flaw in the original experiment: telling a model to “verify metrics” does not perform verification. A generated ad can confidently display a project count, satisfaction percentage, phone number, or service area that never appeared in the source.
Brandforge makes each claim carry its proposed evidence:
pub struct Claim { pub text: String, pub evidence_quote: String,}Model the verdict
Section titled “Model the verdict”pub enum ClaimStatus { Supported, Unsupported,}
pub struct ClaimAudit { pub claim: String, pub evidence_quote: String, pub status: ClaimStatus,}An enum is better than a Boolean because its meaning appears at every match site and can grow later:
Ambiguous, Expired, or RequiresHumanReview are product states, not magic combinations of flags.
Implement the first grounding rule
Section titled “Implement the first grounding rule”The baseline rule normalizes whitespace and case, then requires the evidence quote to occur in the frozen source:
fn normalize(value: &str) -> String { value .split_whitespace() .collect::<Vec<_>>() .join(" ") .to_lowercase()}
let status = if normalized_source.contains(&normalize(&claim.evidence_quote)) { ClaimStatus::Supported} else { ClaimStatus::Unsupported};This is intentionally strict and simple. It can miss a valid paraphrase, but it will not approve a number merely because the model says it saw one. Later projects add source coordinates, retrieval, semantic entailment, expiry, and human review.
Fail closed but preserve diagnosis
Section titled “Fail closed but preserve diagnosis”The pipeline writes claim-audit.json before checking the verdict count:
write_json(output.join("claim-audit.json"), &audits)?;
if unsupported > 0 { return Err(BrandforgeError::UnsupportedClaims { count: unsupported });}That order matters. A failure without an inspection artifact teaches the learner nothing and gives an operator no repair path. A failed run may write diagnostic evidence; it must not write a publishable ad.
Reproduce the unsafe case
Section titled “Reproduce the unsafe case”cargo run -p brandforge -- build \ --source fixtures/brandforge/site.md \ --profile fixtures/brandforge/business-profile-unsupported.json \ --output target/brandforge-badInspect:
cat target/brandforge-bad/claim-audit.jsontest ! -f target/brandforge-bad/ad-preview.svgThe audit should mark 98% client satisfaction unsupported, and the second command should succeed
because no preview exists.
Attack your rule
Section titled “Attack your rule”Try these mutations:
- evidence quote is empty;
- quote differs only in repeated whitespace;
- source says
5 years, claim says15 yearsbut cites the shorter passage; - quote exists in a customer complaint rather than a business assertion;
- quote existed when scraped but is now expired pricing.
The baseline catches the first three only partially and cannot understand the last two. Record those limitations. “Grounded” must always name the verification rule.
What you built
Section titled “What you built”You used enums, iterators, normalization, and typed errors to put a deterministic safety boundary after a probabilistic model. This is the first harness: the model proposes, evidence decides.
Deep references: Provenance, hashes, locators, and trust and Layered evidence validation.