pub struct FactState {Show 18 fields
pub fact_id: FactId,
pub subject: Subject,
pub predicate: Predicate,
pub value: FactValue,
pub authority: Authority,
pub ttl: Ttl,
pub freshness_anchor: TimestampMillis,
pub valid_from: Option<TimestampMillis>,
pub valid_to: Option<TimestampMillis>,
pub lifecycle: FactLifecycle,
pub created_at: TimestampMillis,
pub updated_at: TimestampMillis,
pub last_event_id: FactEventId,
pub superseded_by: Option<FactId>,
pub contradicted_by: Vec<FactId>,
pub evidence_count: usize,
pub corroborating_sources: BTreeMap<SourceId, AuthorityLevel>,
pub survived_challenges: BTreeMap<SourceId, AuthorityLevel>,
}Expand description
The current projected state of a fact — the fold of its event stream. Serializable so a backend may materialize it (cache it as a derived row) or transmit it; it remains a pure projection of the log, never an independent source of truth.
Fields§
§fact_id: FactId§subject: Subject§predicate: Predicate§value: FactValueEntrenchment of the incumbent fact, captured at assertion. Drives
authority-weighted supersession arbitration; see docs/belief-revision.md.
ttl: TtlTTL captured at assertion, used by FactState::is_expired_at for
read-time freshness evaluation.
freshness_anchor: TimestampMillisValid-time anchor for TTL freshness: valid_from, else observed_at, else
the assertion’s recorded_at.
valid_from: Option<TimestampMillis>The asserted valid-time lower bound (ADR 0016), kept distinct from
freshness_anchor (which falls back to observed_at/recorded_at): a fact whose
valid_from is in the future is not yet valid, so read-time freshness treats it
as not-fresh until then. #[serde(default)] for projections materialized before it.
valid_to: Option<TimestampMillis>Valid-time upper bound captured at assertion (ADR 0016): the instant the fact is
asserted to stop holding. Read-time freshness treats an elapsed valid_to exactly
like an elapsed TTL; lifecycle is untouched. #[serde(default)] so projections
materialized before the field existed still deserialize.
lifecycle: FactLifecycle§created_at: TimestampMillis§updated_at: TimestampMillis§last_event_id: FactEventId§superseded_by: Option<FactId>§contradicted_by: Vec<FactId>§evidence_count: usize§corroborating_sources: BTreeMap<SourceId, AuthorityLevel>The distinct provenance sources that have asserted or reinforced this exact
value, each mapped to the highest authority it backed at. This measures
same-value corroboration only — surviving a challenge is the separate
entrenchment signal tracked in FactState::survived_challenges (ADR 0015).
The asserter is the first entry.
survived_challenges: BTreeMap<SourceId, AuthorityLevel>The distinct sources whose challenge against this fact the firewall rejected,
each mapped to the highest effective authority it challenged at (ADR 0015) —
the “attacked and stood” half of earned entrenchment. #[serde(default)] so
projections materialized before the field existed still deserialize.
Implementations§
Source§impl FactState
impl FactState
Sourcepub fn corroboration(&self) -> usize
pub fn corroboration(&self) -> usize
Raw earned-entrenchment degree: the number of distinct sources backing this
value. Sybil-inflatable on its own (an attacker minting many sources raises
it), so security decisions should use FactState::corroboration_at_or_above
to count only sufficiently-authoritative backers.
Sourcepub fn corroboration_at_or_above(&self, min: AuthorityLevel) -> usize
pub fn corroboration_at_or_above(&self, min: AuthorityLevel) -> usize
Authority-weighted corroboration: the number of distinct backing sources whose
authority is at least min. This is the Sybil-resistant entrenchment signal —
minting low-authority sources cannot raise the count measured at a higher floor.
Sourcepub fn survived_challenge_count(&self) -> usize
pub fn survived_challenge_count(&self) -> usize
Raw survived-challenge degree: distinct sources whose challenge this fact
survived. Sybil-inflatable like FactState::corroboration; security
decisions should use FactState::survived_challenges_at_or_above.
Sourcepub fn survived_challenges_at_or_above(&self, min: AuthorityLevel) -> usize
pub fn survived_challenges_at_or_above(&self, min: AuthorityLevel) -> usize
Authority-weighted survived challenges: distinct challengers whose effective
authority was at least min when they challenged and lost. Minting low-authority
challengers cannot simulate surviving strong attacks.
Sourcepub fn earned_entrenchment_at_or_above(&self, min: AuthorityLevel) -> usize
pub fn earned_entrenchment_at_or_above(&self, min: AuthorityLevel) -> usize
Earned entrenchment at min (ADR 0017): the sum of both Sybil-resistant halves —
independent backing (Self::corroboration_at_or_above) plus
having-been-attacked-and-held (Self::survived_challenges_at_or_above), each counted
only at authority ≥ min. This is the measure the opt-in supersession gate and the
subject-level unearned-supersession audit compare, so a fact that survived challenges
resists an equal-authority replacement as much as one with extra backing does.
Sourcepub fn expires_at(&self) -> Option<TimestampMillis>
pub fn expires_at(&self) -> Option<TimestampMillis>
When this fact stops being fresh, if ever: the earliest of its TTL bound
(relative to the freshness anchor) and its asserted valid_to (ADR 0016).
Sourcepub fn is_not_yet_valid_at(&self, now: TimestampMillis) -> bool
pub fn is_not_yet_valid_at(&self, now: TimestampMillis) -> bool
Whether the fact is not yet valid at now (ADR 0016): its asserted valid_from
is in the future. Distinct from expiry — a not-yet-valid fact reads as not-fresh
because it has not started holding, not because it has stopped.
Sourcepub fn is_fresh_at(&self, now: TimestampMillis) -> bool
pub fn is_fresh_at(&self, now: TimestampMillis) -> bool
Whether the fact is fresh at now: within its validity window — at or after
valid_from (if set) and not past its TTL / valid_to upper bound. This is the
read-time freshness predicate the receipt’s fresh flag reports; it does not consult
lifecycle (a superseded fact can still be within its window).
Sourcepub fn is_expired_at(&self, now: TimestampMillis) -> bool
pub fn is_expired_at(&self, now: TimestampMillis) -> bool
Whether the fact’s freshness has elapsed at now — its TTL ran out or its
asserted validity ended. A fact with Ttl::Never and no valid_to is never
expired. Purely the upper bound; the lower bound is Self::is_not_yet_valid_at.
It does not consult lifecycle (a superseded fact can still be “unexpired” by TTL).