Skip to content

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,
}
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.

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.

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.

Terminal window
cargo run -p brandforge -- build \
--source fixtures/brandforge/site.md \
--profile fixtures/brandforge/business-profile-unsupported.json \
--output target/brandforge-bad

Inspect:

Terminal window
cat target/brandforge-bad/claim-audit.json
test ! -f target/brandforge-bad/ad-preview.svg

The audit should mark 98% client satisfaction unsupported, and the second command should succeed because no preview exists.

Try these mutations:

  1. evidence quote is empty;
  2. quote differs only in repeated whitespace;
  3. source says 5 years, claim says 15 years but cites the shorter passage;
  4. quote exists in a customer complaint rather than a business assertion;
  5. 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.

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.

Next: Day 5 — Artifacts and Tests →.