Files
git.stella-ops.org/docs/qa/audit-reconciliation-20260422-deprecate003-final.md
master 2e78085115 feat(audit): drop deprecated per-service audit tables + reconciliation (DEPRECATE-003)
Closes DEPRECATE-003 in SPRINT_20260408_005. Pre-release status means
the 30/90-day compat windows in the original Decision #5 are moot — no
external consumers. Decision #5 amended twice during session.

Drop migrations (embedded resources, auto-applied on startup per §2.7):
- authority.audit / authority.airgap_audit / authority.offline_kit_audit
  (002_drop_deprecated_audit_tables.sql)
- policy.audit (013; policy.gate_bypass_audit PRESERVED as domain evidence)
- notify.audit (008)
- scheduler.audit + partitions via CASCADE (009)
- proofchain.audit_log (004)

Kept by design:
- release_orchestrator.audit_entries + audit_sequences (hash chain, Decision #2)
- policy.gate_bypass_audit (domain evidence, unique query patterns)
- authority.login_attempts (auth protocol state, not audit)

Repository neutering — local DB write removed, Timeline emission preserved:
- PolicyAuditRepository.CreateAsync → Timeline-only; readers [Obsolete]
- NotifyAuditRepository.CreateAsync → Timeline-only; readers [Obsolete]
- PostgresSchedulerAuditService → removed INSERT, Timeline-only
- PostgresAttestorAuditSink.WriteAsync → no-op (endpoint-level .Audited()
  filter carries the audit signal)

Attestor cleanup:
- Deleted AuditLogEntity.cs
- Removed DbSet<AuditLogEntity> from ProofChainDbContext
- Removed LogAuditAsync / GetAuditLogAsync from IProofChainRepository
- Removed "audit_log" from SchemaIsolationService

Reconciliation tool substitutes for the 30-day wall-clock window:
- scripts/audit-reconciliation.ps1 joins each per-service audit table to
  timeline.unified_audit_events via the dual-write discriminator
  (details_jsonb.localAuditId / localEntryId) for deterministic pairs,
  tuple-matches Authority. Test-Table/to_regclass guards handle post-drop
  vacuous-pass. Overall PASS across pre/post/final runs.
- 4 reports under docs/qa/.

Sprint archivals:
- SPRINT_20260408_004 (Timeline unified audit sink) — all 7 tasks DONE
- SPRINT_20260408_005 (audit endpoint filter deprecation) — all 12 tasks DONE

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 16:03:02 +03:00

2.4 KiB

Audit Dual-Write Reconciliation Report

  • Generated: 2026-04-22T14:33:45.9276840+03:00

  • Overall status: PASS

  • Container: stellaops-postgres

  • Database : stellaops_platform

  • Purpose : substitute for the 30-day wall-clock observation window described in SPRINT_20260408_004 (AUDIT-005) and SPRINT_20260408_005 (DEPRECATE-001). Each per-service audit table is reconciled against timeline.unified_audit_events using the discriminator the repository-level dual-write mapper puts into details_jsonb (localAuditId / localEntryId).

Headline counts

Table Rows
authority.audit dropped (DEPRECATE-003)
authority.login_attempts 0
policy.audit dropped (DEPRECATE-003)
notify.audit dropped (DEPRECATE-003)
scheduler.audit dropped (DEPRECATE-003)
release_orchestrator.audit_entries 0
timeline.unified_audit_events 610

policy.audit <-> timeline(module=policy, localAuditId)

Local table policy.audit is dropped (SPRINT_20260408_005 / DEPRECATE-003). Timeline is the sole audit store; reconciliation is vacuous.

Metric Value
Status PASS (dropped)

notify.audit <-> timeline(module=notify, localAuditId)

Local table notify.audit is dropped (SPRINT_20260408_005 / DEPRECATE-003). Timeline is the sole audit store; reconciliation is vacuous.

Metric Value
Status PASS (dropped)

scheduler.audit <-> timeline(module=scheduler, localAuditId)

Local table scheduler.audit is dropped (SPRINT_20260408_005 / DEPRECATE-003). Timeline is the sole audit store; reconciliation is vacuous.

Metric Value
Status PASS (dropped)

release_orchestrator.audit_entries <-> timeline(module IN (release,jobengine), localEntryId)

Metric Value
Local rows 0
Timeline dual-write rows (details_jsonb ? 'localEntryId') 0
Missing in Timeline (data loss) 0
Orphan in Timeline (local cleared post-emission) 0
Status PASS

authority.login_attempts <-> timeline(module=authority) [tuple-match]

AuthorityAuditSink assigns a fresh GUID for the Timeline id, so reconciliation falls back to tuple matching on (action=event_type, timestamp +/- 5s).

Metric Value
authority.login_attempts rows 0
Timeline authority-* rows 4
Local rows with no Timeline twin 0
Status PASS