v0.13.0¶
Release Date: 2026-08-10
Highlights¶
claims.Lint: evidence-integrity linting for claims reports — a verified claim must have a resolving URL and quote, sufficient independent corroboration, and a statistic that isn't staleSourceRole: distinguishes primary/secondary-relay/secondary-analysis/self-reported sources; secondary-analysis and self-reported now require a corroboratingrelatedClaimIdClaimsCriteria.MinCorroboratingSources/MaxClaimAge: opt-in, configurable corroboration and staleness thresholds, enforced by bothclaims.LintandEvaluateClaims- BREAKING:
ExternalValidation.AccessedAtandInternalValidation.ValidatedAtare now*time.Timesoomitemptyactually omits an unset value
Overview¶
v0.13.0 closes the gap that let a claim marked verified cite a figure
synthesized from secondary reporting, when a primary-source check later put
the real number substantially lower. DetermineVerdict computes a verdict
from a Validation, but a verdict can also be hand-authored directly on a
Claim — bypassing that computation entirely. This release adds a
re-checking layer, claims.Lint, that verifies a stated verified is
actually earned, plus three specific checks targeting the failure mode that
motivated it: missing quotes, insufficiently corroborated secondary sources,
and stale statistics presented as current.
New Features¶
claims.Lint and sevaluation lint <claims.json>¶
Lint gates every claim whose Verdict is VerdictVerified:
| Rule | Level | Description |
|---|---|---|
claim-missing-id / claim-duplicate-id |
Error | Every claim has a unique id |
verified-requires-validation |
Error | A verified claim has a validation object |
verified-requires-url / verified-requires-quote |
Error | A verified external claim has a source URL and a verbatim quote |
verified-value-in-quote |
Warning | The claim's statistical value appears in the quoted text (advisory — a legitimate quote can state a rule or a range instead of the literal number) |
verified-derived-needs-sources |
Error | A verified derived claim lists source claim ids |
verified-internal-needs-evidence |
Error | A verified internal claim has an evidence path or output |
verified-subjective |
Warning | A subjective estimate is published as verified |
verified-role-needs-corroboration |
Error | A secondary-analysis/self-reported source has a corroborating relatedClaimId |
verified-insufficient-corroboration |
Error | Opt-in — fewer independent sources than criteria.minCorroboratingSources requires |
verified-stale-as-of-date |
Error | Opt-in — asOfDate older than criteria.maxClaimAge |
Lint never mutates the report. It's wired into sevaluation lint
(auto-detected from the claims top-level key) and sevaluation render
--format=html/--format=json and sevaluation check also gained claims-
report support. See Evidence-Integrity Linting
for the full walkthrough.
SourceRole¶
ExternalSourceType categorizes a source's general authority (NVD,
reputable vendor, community, aggregator, ...). SourceRole is a separate
axis: how directly this citation speaks for the claim.
type SourceRole string
const (
SourceRolePrimary SourceRole = "primary"
SourceRoleSecondaryRelay SourceRole = "secondary-relay"
SourceRoleSecondaryAnalysis SourceRole = "secondary-analysis"
SourceRoleSelfReported SourceRole = "self-reported"
)
Two "reputable vendor" citations can carry very different trust: a wire
report relaying an earnings call is near-primary, but an outlet's own
synthesized estimate across other reporting is secondary-analysis —
exactly what a direct primary-source check can contradict.
SourceRole.RequiresCorroboration() is true for secondary-analysis and
self-reported; Lint then requires at least one RelatedClaimIDs entry.
Role is optional and empty by default, so existing reports are unaffected.
ClaimsCriteria.MinCorroboratingSources / MaxClaimAge¶
Two new, opt-in criteria knobs, both disabled by default:
report.Criteria = claims.ClaimsCriteria{
MinCorroboratingSources: 2, // a general threshold, independent of SourceRole
CorroborationCategories: []claims.ClaimCategory{claims.ClaimStatistical},
MaxClaimAge: 365 * 24 * time.Hour, // flags a stale Statistical.AsOfDate
}
IsSufficientlyCorroborated and IsFresh are the pure checks; both
claims.Lint and EvaluateClaims use them. In EvaluateClaims, a verified
claim failing either check is treated the same way as needs-review for the
report-level Decision — a conditional pass if AllowNeedsReview,
otherwise a fail — without changing the claim's own stored Verdict or the
Counts breakdown. The combined rationale text now lists up to three
reasons, e.g. "Passed with 1 claim needing review, 1 claim lacking
sufficient corroboration, and 1 claim with a stale statistic"; the existing
single- and two-reason wording is unchanged.
Bug Fix: AccessedAt/ValidatedAt omitempty¶
ExternalValidation.AccessedAt and InternalValidation.ValidatedAt were
plain time.Time with json:",omitempty" — omitempty never applies to a
non-pointer time.Time (its zero value isn't Go's "empty" value for
structs), so an unset field silently serialized as
"0001-01-01T00:00:00Z" instead of being omitted. Both are now
*time.Time; NewExternalValidation/NewInternalValidation still default
them to time.Now().UTC(), and new WithAccessedAt/WithValidatedAt
chainable setters (mirroring StatisticalDetail.WithAsOfDate) cover
hand-authored reports. No JSON schema change — the wire shape was already
string/format: date-time; only the Go-side representation was wrong.
Backward Compatibility¶
claims.Lint, SourceRole, MinCorroboratingSources, and MaxClaimAge
are all additive and opt-in — disabled/unset by default, verified to
produce byte-identical lint output against existing case-study ledgers that
don't set the new fields. The one breaking change is the AccessedAt/
ValidatedAt field-type fix: code that assigned these fields a time.Time
value directly (rather than via the constructors or new With* setters)
needs updating to take an address.
Commits¶
7709583feat(render/html): add HTML renderer for claims reports06b9fbafeat(cli): render and check claims reportsd53be38feat(claims): add Lint and wire sevaluation lint for claims reports5ff6086feat(claims): add SourceRole and gate secondary-analysis/self-reported on corroboration18a5a0bfeat(claims): add MinCorroboratingSources criteria + report-level enforcementc01aa66feat(claims): add MaxClaimAge criteria + staleness enforcement4ee701bfix(claims): make AccessedAt/ValidatedAt *time.Time so omitempty works3869045docs: add v0.13.0 changelog entry