Recoverable staged generations and compare-and-publish
Point publication now splits into stage_point (extraction + inference,
no writer guard) and publish_staged (guard held only for orphan audit,
target comparison and one logical commit). The stage binds database,
namespace, ontology revision, policy, mapping, evidence and valid point in
its generation digest plus a base_fingerprint of the visible target
state. Publication compares the fingerprint under the guard and raises
GenerationStaleError on any movement — readers only ever observe
old-or-new generations.
Stages retain authenticated source read-view provenance. The default
publish_staged(..., freshness="latest") also requires the current commit
horizon to match that source horizon. This first implementation conservatively
rejects unrelated intervening commits too; re-stage explicitly to retry. It
does not silently publish conclusions whose source assertions were retracted.
Historical publication requires explicit freshness="historical". It verifies
the original pinned source prefix rather than pretending it is current. Both
modes retain target compare-and-publish checks and record the source horizon,
provenance token and freshness policy in published claim provenance. Legacy
manually constructed stages without source provenance must be re-staged.
staged = stage_point(graph, compiled, valid_time=t, source_world="base",
target_world="derived", publication_key="k",
selection=selection, mapping=mapping, limits=limits)
try:
result = publish_staged(graph, staged)
except GenerationStaleError:
staged = stage_point(graph, compiled, valid_time=t, source_world="base",
target_world="derived", publication_key="k",
selection=selection, mapping=mapping, limits=limits)
result = publish_staged(graph, staged)
Retries are explicit: the caller re-stages and republishes. Concurrent
writers serialize on the writer guard; the loser observes a stale base and
retries without overwriting. Durability follows the existing temporal commit
path; commit_versions fsync behavior is unchanged. Reader generation
retention follows pinned read views: open readers keep their commit prefix
while the new generation becomes visible to fresh readers.
Recovery is consent-gated: audit_publication_orphans only lists,
reclaim_publication_orphans(..., consent=True) deletes validated
unpublished artifacts through an explicit horizon. Without consent=True
it fails closed without touching storage. Fault injection (simulated
incomplete commits) must be reclaimed explicitly; there is no silent index
repair.