internal/infra/pg/factreceipt.go
internal/infra/pg · 223 lines · 8 declarations · source
Declarations
const MaxRecoveryPage
const MaxRecoveryPage = 100
var ErrInvalidRecovery
var ErrInvalidRecovery = errors.New("recovery requires a valid cursor, source and page size")
var ErrInvalidReceipt
var ErrInvalidReceipt = errors.New("formation receipt no longer matches its surviving source")
type RecoveryPage
type RecoveryPage struct {
Examined int `json:"examined"`
Restored int `json:"restored"`
Repaired int `json:"repaired"`
Next string `json:"next,omitempty"`
}
RecoveryPage is a live UUID page. Repeating a page preserves retained records, and an interrupted transaction advances nothing. A new full pass finds concurrent additions below a previous cursor.
method FactStore.RecoverFacts
func (s *FactStore) RecoverFacts(ctx context.Context, schema Schema, scope, source, after, principal string, limit int) (RecoveryPage, error)
RecoverFacts restores one bounded page without a model call or a delete of available memory. It does not claim to regenerate embeddings, summaries, or interpretations under new model rules.
func restoreFactReceiptTx
func restoreFactReceiptTx(ctx context.Context, tx pgx.Tx, schema Schema, scope, id string) (bool, error)
Receipt state is a typed PostgreSQL row, not a model response. Source byte verification prevents recovery from a changed transcript. Existing rows win; recovery never restamps current memory.
const retainFactReceiptSQL
const retainFactReceiptSQL = `INSERT INTO {schema}.fact_receipt(scope,fact_id,source_observation_id,source_ordinal,state,evidence,subject_identity,object_identity,pipeline_version,supersession_source,superseded_by)
SELECT f.scope,f.fact_id,e.source_observation_id,e.source_ordinal,to_jsonb(f),to_jsonb(e),
to_jsonb(l)-'aliases'-'normalized_aliases',to_jsonb(r)-'aliases'-'normalized_aliases',d.pipeline_version,f.supersession_source,f.superseded_by
FROM {schema}.fact f JOIN {schema}.fact_evidence e ON e.scope=f.scope AND e.fact_id=f.fact_id
JOIN {schema}.projection_dependency d ON d.scope=f.scope AND d.fact_ref=f.fact_id AND d.source_observation_id=e.source_observation_id
LEFT JOIN {schema}.entity l ON l.scope=f.scope AND l.entity_id=f.subject_entity_id
LEFT JOIN {schema}.entity r ON r.scope=f.scope AND r.entity_id=f.object_entity_id
WHERE f.scope=$1 AND f.fact_id=$2::uuid ON CONFLICT(scope,fact_id) DO NOTHING`
func receiptOwnershipSQLFor
func receiptOwnershipSQLFor(table, owned string) string
receiptOwnershipSQLFor is the ownership predicate over any subquery of owned observation ids, so an erasure by source counts receipts exactly as an erasure by subject does, and both governance readers — the export and the erasure — ask the question in exactly one way. The table names are a fixed internal set.