RS3B-06 — Durable Failure-Audit Sink Selection — 2026-06-21
RS3B-06 — Durable Failure-Audit Sink Selection — 2026-06-21
Macro: RS3B-REGISTRAR-HARDENING-DESIGN (read-only / KB-design)
Deliverable: 06 of 10 · Durable failure-audit sink (Mục tiêu F)
Date: 2026-06-21 · 0 mutations · NO_CODEX_LIVE_READ
Deliverable status: SINK_CANDIDATE_SELECTED_FAIL_CLOSED_UNTIL_IMMUTABILITY_RETENTION_PROVEN — event_outbox is the lead reuse candidate for the durable failure-audit sink, but no candidate proves enforced immutability/retention, so the sink stays fail-closed. No new ledger.
1. Why a durable failure-audit sink
RS3B-05 requires that a registration failure (esp. rolled-back Phase-1) leave a durable audit record outside the rolled-back transaction (RS2-PATCH1 Codex R3). The sink must be reused from existing surfaces (no new ledger until reuse is exhausted — must-not-do #13).
2. Candidate evidence (live, 2026-06-21; immutability via pg_trigger, not the privilege-blind information_schema.triggers)
| Candidate | Rows | Schema fit | Trigger facts (pg_catalog) | Immutability | Idempotency | Post-rollback writable | Payload safety |
|---|---|---|---|---|---|---|---|
event_outbox |
215,644 | rich: event_domain/type/stream/lane/severity, subject_table/ref, canonical_address, actor_ref NN, source_system, correlation_id (nullable, non-unique), safe_payload jsonb, payload_classification, occurred_at, created_at | trg_event_outbox_type_validate BEFORE INSERT (type validation) + safe_payload forbidden-key CHECK |
none (no UPDATE/DELETE block) | none (only PK on id; no idempotency key) | yes (normal heap table) | yes (safe_payload guard, payload_classification) |
registry_changelog |
87,746 | audit-log: entity_type/code/name, action, collection_name, changed_by, alert_level, resolved/resolved_by/resolved_at | birth_trigger_registry_changelog AFTER INSERT, trg_label_assign_… AFTER INSERT, trg_count_… (DISABLED); none block UPDATE/DELETE |
none — explicitly mutable by design (resolve workflow) | none | yes | weak (no classification guard) |
governance_audit_log |
1 | narrow: relation_id, checked_at (nullable), checked_by NN, result, detail json | no triggers | none | none | yes | weak |
iu_route_attempt |
68 | retry ledger; wrong domain (IU routing) | no triggers | n/a | UNIQUE(idempotency_key,attempt_no) (not single-use) |
yes | n/a |
3. Score table (0 = absent, 1 = partial, 2 = proven)
| Criterion | event_outbox | registry_changelog | governance_audit_log | iu_route_attempt |
|---|---|---|---|---|
| schema fit (failure event) | 2 | 1 | 1 | 0 |
| writer authority (enumerable, restricted) | 0 | 0 | 0 | 0 |
| append-only / immutability (enforced) | 0 | 0 | 0 | 0 |
| retention (proven) | 0 | 0 | 0 | 0 |
| post-rollback write capability | 2 | 2 | 2 | 2 |
| replay/idempotency dedup | 0 | 0 | 0 | 1 |
| payload safety | 2 | 1 | 1 | 0 |
| consumer readback | 1 | 1 | 1 | 1 |
| no-new-ledger (already exists) | 2 | 2 | 2 | 2 |
| total | 9 | 7 | 7 | 6 |
4. Selection
- Selected lead candidate:
event_outbox— highest schema fit, asafe_payloadjsonb with a forbidden-key guard andpayload_classification(payload-safety proven), normal heap table so it is writable after a Phase-1 rollback, and it already exists (no new ledger). RS3-PATCH2 classifies it consistently as "candidate failure-audit sink only." - Rejected as authority/replay surface: all four — none is an authority carrier;
iu_route_attemptis wrong-domain and not single-use. - Why fail-closed despite selection:
event_outbox(and every candidate) fails the immutability and retention criteria — there is no UPDATE/DELETE-blocking trigger or constraint on any of them (confirmed viapg_trigger), append-only is convention only, the writer role is not enumerable from the read-only role, and there is no idempotency dedup for failure records.registry_changelogis explicitly mutable (resolve columns). Therefore the sink is selected but fail-closed.
5. Required future surface (defined, not built)
To promote event_outbox to a proven durable failure-audit sink: (1) an enforced append-only mechanism (UPDATE/DELETE-blocking trigger or revoked grants) — none exists today; (2) an enumerable, restricted writer grant for the registrar; (3) a retention policy covering the replay horizon; (4) an idempotency/dedup key for failure records so retries do not duplicate audit rows; (5) consumer readback proven. These are reuse hardening of the existing table — no new ledger is authorized until these are evaluated and shown insufficient.
6. Status block
- Deliverable status:
SINK_CANDIDATE_SELECTED_FAIL_CLOSED_UNTIL_IMMUTABILITY_RETENTION_PROVEN - Selected lead:
event_outbox(sink-only, not authority); no new ledger - Feeds RS3B-05 (durable failure audit outside rolled-back txn) and RS3B-09
- Registration gate:
REGISTRATION_HOLD·REGISTRATION_CAN_PROCEED = NO· 0 mutations