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>
This commit is contained in:
72
docs/qa/audit-reconciliation-20260422-115951.md
Normal file
72
docs/qa/audit-reconciliation-20260422-115951.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# 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** |
|
||||
|
||||
66
docs/qa/audit-reconciliation-20260422-deprecate003-final.md
Normal file
66
docs/qa/audit-reconciliation-20260422-deprecate003-final.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# 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** |
|
||||
|
||||
66
docs/qa/audit-reconciliation-20260422-deprecate003-post.md
Normal file
66
docs/qa/audit-reconciliation-20260422-deprecate003-post.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Audit Dual-Write Reconciliation Report
|
||||
|
||||
- Generated: 2026-04-22T14:29:51.3585125+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` | 608 |
|
||||
|
||||
## 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 | 2 |
|
||||
| **Local rows with no Timeline twin** | 0 |
|
||||
| Status | **PASS** |
|
||||
|
||||
72
docs/qa/audit-reconciliation-20260422-deprecate003-pre.md
Normal file
72
docs/qa/audit-reconciliation-20260422-deprecate003-pre.md
Normal file
@@ -0,0 +1,72 @@
|
||||
# Audit Dual-Write Reconciliation Report
|
||||
|
||||
- Generated: 2026-04-22T13:39:32.1244153+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 |
|
||||
| `policy.audit` | 0 |
|
||||
| `notify.audit` | 0 |
|
||||
| `scheduler.audit` | 0 |
|
||||
| `release_orchestrator.audit_entries` | 0 |
|
||||
| `timeline.unified_audit_events` | 602 |
|
||||
|
||||
## 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** |
|
||||
|
||||
Reference in New Issue
Block a user