ADR-014 Status: Proposed v0.1
Requiemos

Case Audit
Architecture — four layers

One case generates four different kinds of record. Each has its own purpose, its own consumer, its own retention policy. They never merge.

Each layer answers a different question.

The temptation is to build one "audit log" for everything. We don't, because legal discovery, forensic debugging, the human case narrative, and service reconciliation are four problems with four different shapes. One table cannot serve all of them well.

01
Chain of Custody
custody_events

Regulatory proof of physical possession of remains and assets. Subject to legal discovery.

Answers "Who had the body, when?"
Written by CoC scan modules only
Consumed by Regulators, courts, coroners
Retention Permanent
Immutability Append-only, supersession pattern
Volume / case 3–8 rows
02
Audited Model Trail
audits

Field-level "who changed what when" on every tracked Rails model. High volume, machine-oriented, forensic.

Answers "Which field changed, by whom?"
Written by audited gem (automatic)
Consumed by Developers, support, forensic investigation
Retention 7 years (provincial reg)
Immutability Read-only by application
Volume / case 200–500 rows
03
Case Activity Events
case_activity_events

Curated, human-readable narrative of every meaningful interaction with a case. The story of what happened.

Answers "What happened on this case?"
Written by Module engine + UI instrumentation
Consumed by Case Timeline, shift handoffs, owners
Retention 2–3 years (TBD)
Immutability Append-only by convention
Volume / case 30–80 rows
04
Services Rendered
case_services_rendered

Typed, structured record of what was actually delivered. Backs contract reconciliation and operational analytics.

Answers "What did we actually do for them?"
Written by Service modules + manual director entries
Consumed by Reconciliation, contract amendments, finance
Retention Tied to case
Immutability Structured, queryable
Volume / case 5–15 rows

One event, different layers touched.

No single event writes to all four layers. The shape of the event determines where it lands. These three examples show the pattern.

Event A — Regulatory
Director scans pickup at Vancouver General
David Chen, 08:30, QR code scan on the transfer cot
01 Authoritative custody record written custody_events
02 Field changes captured (case status, assigned_to) audits
03 Mirror narrative entry for the Timeline UI case_activity_events
Event B — Service Delivery
Visitation completed — 2 hours in Chapel B
Service-delivery module marks completion
02 Module-instance field changes captured audits
03 Narrative entry: "Visitation completed — 2 hrs, Chapel B" case_activity_events
04 Typed service row for reconciliation case_services_rendered
Event C — UI Interaction
Sophie adds a note about family preferences
Free-text note attached to case, no module involved
02 Note model insert captured automatically audits
03 UI instrumentation writes "Note added by Sophie" case_activity_events

The lines that do not get crossed.

Without explicit rules, four layers drift back into one over time. These are the load-bearing constraints that keep the architecture honest.

I.
Chain of custody never merges with anything else. Custody scans write only to custody_events. A mirror narrative entry is added to Layer 3 — but it carries only a summary and a reference, never the full custody payload. Regulators read Layer 1; everyone else reads the mirror.
II.
The audited gem never feeds Layer 3. The Case Timeline does not render translated audits rows. The translation cost is too high, the noise ratio is too poor. Developers query Layer 2 directly; end users never see it.
III.
Layer 3 captures events humans care about — not field changes. One meaningful event yields one Layer 3 row, regardless of how many underlying fields moved. The field-level detail lives in Layer 2.
IV.
Layer 4 is typed. Layer 3 is narrative. A service rendered produces two records of the same event: a structured row in Layer 4 for reconciliation queries, and a narrative row in Layer 3 for the Timeline. Same event, two shapes, two consumers.
V.
No layer references later layers as foreign keys. Layer 1, 2, and 4 never point at Layer 3. The reverse — Layer 3 referencing others via its JSONB payload — is fine. This keeps Layer 3 a derivable narrative, not a structurally load-bearing reference.

Where does this event go?

For developers and reviewers: the routing answer in one read.

If the event is…

A QR scan of remains or assets (pickup, receiving, disposition)
→ Layer 1 + 3 mirror
A change to any tracked Rails model field
→ Layer 2 (auto)
A module instance completion or skip
→ Layer 3 (auto)
A meaningful UI action (note, view, document open)
→ Layer 3 (manual)
A service was actually delivered to the family
→ Layer 4 + 3
A bug investigation or "who changed this?"
→ Read Layer 2
Regulator subpoena, coroner inquiry
→ Read Layer 1 only
"Show me everything that happened on this case"
→ Read Layer 3