Files
git.stella-ops.org/docs/qa/audit-reconciliation-20260422-115951.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

73 lines
2.4 KiB
Markdown

# Audit Dual-Write Reconciliation Report
- Generated: 2026-04-22T11:59:51.1886399+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` | 0 |
| `authority.login_attempts` | 0 |
| `notify.audit` | 0 |
| `policy.audit` | 0 |
| `release_orchestrator.audit_entries` | 0 |
| `scheduler.audit` | 0 |
| `timeline.unified_audit_events` | 567 |
## policy.audit <-> timeline(module=policy, localAuditId)
| Metric | Value |
| --- | ---: |
| Local rows | 0 |
| Timeline dual-write rows (`details_jsonb ? 'localAuditId'`) | 0 |
| **Missing in Timeline (data loss)** | 0 |
| Orphan in Timeline (local cleared post-emission) | 0 |
| Status | **PASS** |
## notify.audit <-> timeline(module=notify, localAuditId)
| Metric | Value |
| --- | ---: |
| Local rows | 0 |
| Timeline dual-write rows (`details_jsonb ? 'localAuditId'`) | 18 |
| **Missing in Timeline (data loss)** | 0 |
| Orphan in Timeline (local cleared post-emission) | 18 |
| Status | **PASS** |
## scheduler.audit <-> timeline(module=scheduler, localAuditId)
| Metric | Value |
| --- | ---: |
| Local rows | 0 |
| Timeline dual-write rows (`details_jsonb ? 'localAuditId'`) | 0 |
| **Missing in Timeline (data loss)** | 0 |
| Orphan in Timeline (local cleared post-emission) | 0 |
| Status | **PASS** |
## 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 | 0 |
| **Local rows with no Timeline twin** | 0 |
| Status | **PASS** |