# v0.4.2 T5 — Replayable Audit Graph (Full Event-Sourced Reconstruction)

> Layer 4 Lifecycle Semantics 의 EVENT track 마지막 piece (T5). v0.4.0
> partial (`reconstruct_view_at`) → v0.4.2 full event-sourced reconstruction
> 완성. cycle γ 4 외부 벤치 ABAC + replay evidence prerequisite + v0.5
> 진입 D1 Auditability / D2 Reasoning Quality 게이트 강화.

## 0. TL;DR

- **Scope**: graph-wide `reconstruct_graph_at(t)` audit-only primitive +
  audit_log event taxonomy + cross-chain integration invariant
- **Strategy**: T6 v0.4.1 cycle pattern 미러 — 5-PR sequence (T5.A → B
  → C → D)
- **Cost**: ~20-30h, 1-2 일
- **Cycle γ 연결**: ABAC 3-stage + reasoning trace replay 외부 demo
  evidence 직접 제공

## 1. T5 정확한 scope — partial (v0.4.0) vs full (v0.4.2)

### Current — v0.4.0 partial

| Component | 상태 | 코드 위치 |
|---|---|---|
| `reconstruct_view_at(head, lookup, t)` | ✅ | `core/lifecycle/supersede_chain.py:264` |
| `walk_supersede_chain` | ✅ | `supersede_chain.py` |
| Validity window 기반 edge 매칭 | ✅ | `_validity_contains` |
| Reasoning trace replay (`audit_log` only) | ✅ | `tests/test_replay_trace.py` (§5.7.2) |

**Limit**: single supersede chain 한정. graph-wide replay 미완.

### Full T5 — v0.4.2 scope

| Component | 작업 | PR |
|---|---|---|
| `reconstruct_graph_at(t)` — graph-wide primitive | NEW | T5.B |
| audit_log event-type taxonomy (mutation events) | NEW | T5.A |
| Cross-chain replay 일관성 invariant | NEW | T5.C |
| Reply primitive 의 **audit-only invariant** | NEW | T5.D |
| Migration backfill | NEW | T5.A |

## 2. Audit-only invariant (T5 의 핵심)

### Definition

```
∀ t. reconstruct_graph_at(t) = (replay of audit_log events where
                                event.timestamp ≤ t)
```

**의도**:
- Graph state 의 임의 시점 reconstruction = audit_log events 만으로 가능
- DB scan (wiki fixture file 직접 read) 없이 audit_log 만 의존
- 모든 mutation 이 audit event 1개 emit 의무 (mutation → event correspondence)

### Implications

- audit_log 가 **append-only event store** 역할 (CQRS-style write side)
- Graph state = read-side projection (event log fold)
- 외부 evidence: audit_log JSON 만 share → 임의 시점 graph state reproducible

### Why critical

- PR #712 §6 corpus retrieval analysis 의 "replay invariant" claim 의
  외부 검증 evidence path
- ABAC 3-stage (Vector → Graph → Output) 의 임의 시점 state demonstration
- v0.5 도메인 pilot 진입 시 운영자가 "이 답이 그 시점 어떤 graph state
  위에서 만들어졌나" 직접 trace 가능

## 3. audit_log event taxonomy (v0.4.2 확장)

### Current audit_log schema (reasoning-focused, v0.4.0)

```sql
CREATE TABLE audit_log (
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp    TEXT    NOT NULL,
    user_role    TEXT    NOT NULL,
    endpoint     TEXT    NOT NULL,
    query        TEXT,
    answer       TEXT,
    graph_paths  TEXT,
    blocked      INTEGER DEFAULT 0,
    security_event TEXT,
    elapsed_sec  REAL,
    ip_address   TEXT
)
```

→ Reasoning round (query → answer) 만 capture. **Graph mutation event
명시적 capture 없음**.

### T5.A 확장 — `event_type` + `event_payload` columns

```sql
ALTER TABLE audit_log ADD COLUMN event_type     TEXT;
ALTER TABLE audit_log ADD COLUMN event_payload  TEXT;
```

**Event types** (lifecycle mutation):

| event_type | 의미 | event_payload (JSON) |
|---|---|---|
| `reasoning.synth.rag` (existing) | reasoning round | (existing fields) |
| **`lifecycle.supersede.edge_created`** | T7 새 edge | `{head_id, new_edge_id, supersede_ts, validity}` |
| **`lifecycle.supersede.chain_extended`** | T7 chain link | `{chain_head, new_link, validity}` |
| **`lifecycle.cascade.invalidate`** | T6 invalidate | `{base_fact_id, invalidated_edges, mutation_type}` |
| **`lifecycle.t1.expiration_cascade`** | T1 expiration | `{edge_id, validity.to}` |
| **`lifecycle.t2.dispatch_contradiction`** | T2 contradiction | `{a_edge, b_edge, mutation_type}` |
| **`lifecycle.t2d.ingest_dispatch`** | T2.D pre-merge | `{candidate, existing, pattern}` |

### Migration backfill

- 기존 wiki fixture 의 mutation history (없거나 partial)
- Backfill 전략: 현 state snapshot 으로부터 reverse-derive (last-known-good)
- `mutation_type` 이 이미 박혀있는 row 는 그대로 활용
- 없는 row 는 `event_type=lifecycle.backfill.snapshot` 으로 표시

## 4. API design

### `reconstruct_graph_at(t)` primitive

```python
# core/lifecycle/replay_graph.py (NEW)

def reconstruct_graph_at(
    t: datetime,
    *,
    audit_log_path: Optional[str] = None,
    include_event_types: Optional[tuple[str, ...]] = None,
) -> GraphSnapshot:
    """Replay every lifecycle mutation event with timestamp ≤ t and
    return the resulting graph snapshot.

    Args:
        t: UTC-aware datetime. Replay cutoff.
        audit_log_path: optional path override (default: production
            audit_log SQLite).
        include_event_types: optional filter (default: every
            lifecycle.* event_type).

    Returns:
        GraphSnapshot — a deterministic per-edge-id projection of the
        replayed event stream. Identical across calls with the same
        (t, audit_log_path) pair.

    Invariants (T5.D release-gating):
        I1 (audit-only): no DB scan or file read; the snapshot depends
            only on the audit_log row stream up to t.
        I2 (supersede-preserved): the resulting snapshot's supersede
            chains match what `walk_supersede_chain` would produce on
            the live graph at t.
        I3 (cascade-respected): edges with `mutation_type=invalidated`
            via T6 are not in the snapshot when their invalidation
            event lies before t.
        I4 (replay-equality): for any t = now, the snapshot equals the
            live graph state (modulo non-lifecycle metadata).
    """
```

### `GraphSnapshot` shape

```python
@dataclass
class GraphSnapshot:
    edges:           dict[str, dict]   # edge_id → edge dict
    supersede_chains: dict[str, list[str]]  # head → chain ids
    invalidated_ids: frozenset[str]
    replayed_at:     datetime
    event_count:     int               # how many events were replayed
```

## 5. Cross-chain integration (T5.C)

### Consistency invariant

`reconstruct_view_at` (single chain, v0.4.0) vs `reconstruct_graph_at`
(graph-wide, v0.4.2) 결과 일관성:

```
∀ head, t. reconstruct_view_at(head, lookup, t) ∈ reconstruct_graph_at(t).edges
```

(where lookup is the live id-to-edge resolver — i.e. `reconstruct_view_at`
의 결과 edge 는 항상 `reconstruct_graph_at(t)` 의 edges dict 안에 존재)

### Migration of `reconstruct_view_at`

v0.4.2 부터 `reconstruct_view_at` 는 `reconstruct_graph_at(t)` 의
single-chain projection 으로 재구현 (선택). 기존 호출 site byte-
identical.

## 6. Phase plan (5-PR sequence, T6 v0.4.1 pattern 미러)

### Phase A — Design memo (이 doc) + ARCHITECTURE.md draft (~2-4h)

- 본 design memo
- ARCHITECTURE.md §5.7.2 + §5.7.13 audit_only invariant draft

### Phase B — PR-T5.A: Schema + event taxonomy + migration (~4-6h)

**Files**:
- `core/lifecycle/replay_audit.py` (NEW) — event types + emit helpers
- `scripts/migrate_v042_replay_audit.py` (NEW) — `--dry-run` /
  `--apply` / `--verify` / pre-write snapshot
- `tests/test_t5_event_taxonomy.py` (NEW) — 20+ contract tests
- audit_log schema migration (ALTER TABLE)
- Mutation site 들 (T1/T2/T2.D/T6/T7) 에 `emit_lifecycle_event` 추가

### Phase C — PR-T5.B: `reconstruct_graph_at(t)` primitive (~6-8h)

**Files**:
- `core/lifecycle/replay_graph.py` (NEW) — `reconstruct_graph_at` 구현
- `tests/test_t5_reconstruct_graph_at.py` (NEW) — 15+ contract tests
- Invariants I1 (audit-only) / I2 (supersede) / I3 (cascade) / I4
  (replay equality) 각 contract test

### Phase D — PR-T5.C: Cross-chain integration + ARCHITECTURE 확장 (~4-6h)

**Files**:
- `core/lifecycle/supersede_chain.py` — `reconstruct_view_at` 가
  `reconstruct_graph_at` 의 single-chain projection 으로 재구현 (byte-
  identical 외부 contract)
- `tests/test_t5_cross_chain_consistency.py` (NEW) — `view_at` ⊆
  `graph_at(t).edges` invariant
- `docs/ARCHITECTURE.md` §5.7.2 확장 (trace replay + graph replay
  통합) + §5.7.14 (T5 full event-sourced reconstruction) 신규

### Phase E — PR-T5.D: Release-gating invariants + closure (~4-6h)

**Files**:
- `tests/test_t5_release_gating_invariants.py` (NEW) — 5 release-gating
  tests:
  1. `test_graph_replay_at_t_matches_event_log` — replay = event log
     fold
  2. `test_replay_audit_only_no_db_scan` — DB read = 0 during replay
  3. `test_replay_preserves_supersede_chain` — T7 chain 정합 유지
  4. `test_replay_respects_cascade_invalidate` — T6 invalidate after-
     state 정합
  5. `test_reasoning_trace_replay_invariant` — ARCHITECTURE §5.7.2
     trace 의 graph-replay consistency
- `docs/release_notes_v0.4.2.md`
- `CHANGELOG.md` `[0.4.2]`
- `.zenodo.json` v0.4.2
- DOI bump (v0.4.1 `10.5281/zenodo.20426719` → v0.4.2 TBD)

## 7. Decision-LOCKs (T6 pattern, 의도적 미리 박음)

| # | Decision | LOCK |
|---|---|---|
| 1 | audit_log event_payload format = JSON string column | LOCK (vs separate event_payload table) |
| 2 | Mutation site → event emit = synchronous in-transaction | LOCK (vs async queue) |
| 3 | Migration backfill = snapshot-based for un-evented rows | LOCK (vs strict reject) |
| 4 | `reconstruct_graph_at` API = pure function (no side effect) | LOCK |
| 5 | I1 (audit-only) test = monkeypatch DB read & assert read_count=0 | LOCK |

## 8. Invariants (extending v0.4.1's 39)

T5 new invariants: 5 release-gating (Phase E) + ~30 contract (Phase B-D)
= ~35 cumulative new. Total Layer 4 Lifecycle: **74+ invariants**.

## 9. Cross-cutting impact

| Layer | Risk |
|---|---|
| `core/lifecycle` | High — event emit wiring + reconstruct primitive |
| `core/audit_bridge` | Medium — schema migration + event_type write |
| `core/graph` | Medium — replay output consumed for view |
| `core/security_layer` (ABAC) | Medium — replay state used for "ABAC state at t" demo |
| `core/reasoning` | Low — replay output read-only, reasoning unchanged |
| `core/retrieval` | None — replay invisible to retrieval path |

## 10. Cycle γ 정합 — 외부 evidence path

| cycle γ claim | T5 partial (v0.4.0) | T5 full (v0.4.2) |
|---|---|---|
| ABAC 3-stage audit 추적 | △ single chain | ✅ graph-wide |
| ABAC state at t (외부 demo) | ❌ | ✅ `reconstruct_graph_at(t)` |
| Reasoning trace replay | ✅ §5.7.2 | ✅ + integration |
| "Replay invariant" claim 외부 evidence | △ weak | ✅ strong |

cycle γ Phase B (smoke) / Phase D (보고서) 의 ABAC + replay demo
artifact:
- `audit_log.jsonl` export (events only)
- `reconstruct_graph_at(t)` 의 임의 시점 state snapshot
- ABAC vector/graph/output stage 의 audit row 추출

## 11. Risks + mitigations

| Risk | Mitigation |
|---|---|
| audit_log schema migration ALTER TABLE risk | dry-run + verify mode + pre-write snapshot |
| Mutation event emit 누락 (audit-only invariant 깨짐) | T5.A 의 mutation site grep + contract test "every mutation → event row" |
| Backfill 추정 의도치 않은 state ALTER | `event_type=lifecycle.backfill.snapshot` 명시 + replay 시 backfill row 차이 표시 |
| PR cost overflow (T6 가 21 PR) | 5-PR sequence + 각 PR scope 정밀 박음 + early closure 옵션 |
| Cross-cutting regression | T5.D release-gating invariants + 4 axis × 2 모델 step7 bench (CLAUDE.md rule 2) |

## 12. Mother-platform 6 원칙 매핑

| 원칙 | 평가 |
|---|---|
| 원칙 1 (옵션형 거둬내기 + 다이얼 조정) | replay = 운영자 시점 다이얼. Event-sourced 가 진짜 mechanism |
| 원칙 2 (NATURAL 차단 극단 다이얼 X) | replay 는 read-only, NATURAL 영향 0 |
| 원칙 3 (단답 specific 옵션 layer) | 무관 (lifecycle 차원) |
| 원칙 4 (옵션형 코드 → 답변 방식 옵션) | 무관 |
| 원칙 5 (NATURAL 지장 없는 개선) | ✅ byte-identical, NATURAL byte-identical 유지 |
| 원칙 6 (auto-selection layer) | 향후 auto-debugging / audit UI 의 layer 마련 |

## 13. Done when — v0.4.2 closure 조건

- ✅ T5.A schema + migration land + 20+ tests green
- ✅ T5.B `reconstruct_graph_at` land + 15+ tests + 4 invariants
- ✅ T5.C cross-chain consistency land + ARCHITECTURE 확장
- ✅ T5.D 5 release-gating invariants green against real wiki fixture
- ✅ Migration script idempotent + byte-stable
- ✅ Pre-T5 cascade/supersede tests still green (no regression)
- ✅ Closure docs + DOI bump + .zenodo.json v0.4.2

## 14. Out of scope (deferred to v0.4.3+)

- T3 Evidence Aging (별도 cycle)
- T4 Reviewer Authority Hierarchy (별도 cycle)
- audit_log UI layer (replay primitive 위에서 별도)
- production audit_log 의 historic backfill (운영자 결정)
- ABAC state visualizer (replay primitive 위에서 별도)

## 15. v0.4.2 closure 후 path

```
v0.4.2 T5 closure ✅
  ↓
Cycle γ 진입 (4 외부 벤치 통합 측정) — ~80h CPU + 작업 시간
  ↓
v0.5 진입 게이트 평가
```
