# Append-only account mapping and snapshot correction audits

Use this reference for source-only reviews of financial systems where one generic bank/cash account must be separated into account-specific identities without opening production rows.

## Evidence discipline

1. Pin the requested Git object and inspect it with `git show <sha>:<path>` / `git grep <sha>` rather than trusting a divergent checkout.
2. Read migration, service, API schema/router, and synthetic test code only. Do not inspect production DBs, raw imports, reports, or secrets.
3. Cite exact files, symbols, and line ranges from the pinned object; distinguish existing behavior from proposals.
4. Verify the worktree remained unchanged.

## Trace the identity chain

Follow source identity through canonical accounts, household/budget projections, source-reference mappings, import file/item lineage, stored snapshot assignment, every latest/current/history reader, and API Preview→Confirm/audit code. Establish which table is canonical from FKs and writers; do not assume a `canonical_accounts` table exists. Names and masked suffixes are labels, not identity.

Look specifically for:

- a generic bootstrap account representing several real accounts;
- correction by destructive upsert;
- a required `preview_id` that confirm never validates;
- inconsistent latest-snapshot ordering across readers;
- institution/profile-wide metadata copied onto every account;
- latest-file counts mixed with batch-wide item counts;
- missing account-scoped lineage for multi-account files and duplicate/review items.

## Fail-closed append-only model

Prefer a migration that adds immutable relations rather than rewriting history:

1. Canonical cash identity metadata: institution, role, source-reference hash, masked hint.
2. Correction batch: contract version, intent/preview/baseline fingerprints, counts, confirmation note/text, one audit ID.
3. Mapping correction relation preserving the original mapping.
4. Snapshot-account correction relation preserving original account, amount, date, type, and source.
5. Account-scoped import file coverage/item bindings for future imports.
6. Effective mapping/snapshot views or one shared query builder consumed by all readers.

Keep a generic account only for genuinely unknown references. Never redistribute by display name, suffix text, amount, or heuristic. Ambiguous or unclassified rows block the whole batch.

## Preview→Confirm contract

Preview is read-only and returns a deterministic ordered action manifest using masked labels and opaque IDs. Bind:

- `intent_fingerprint = hash(canonical request)`;
- `baseline_fingerprint = hash(all relations affecting validation/writes)`;
- `preview_fingerprint = hash(contract version, intent, baseline, ordered actions)`.

Confirm resubmits the full intent and fingerprints and requires `confirm=true`, exact confirmation text, and a non-empty note. Under `BEGIN IMMEDIATE` (or equivalent): recompute baseline/actions; reject all drift or conflicts; insert accounts, links, corrections, coverage, one audit, and the immutable batch atomically; never UPDATE/DELETE original history; return the original batch on identical replay; roll back if audit or any final item fails. A random preview token is not binding, and a schema field is not proof the service validates it.

## Latest cash precedence

Centralize effective selection. Safe default:

1. filter by corrected effective account and `as_of`;
2. `balance_date DESC`;
3. same date: `reconciliation > manual > csv_anchor > calculated`;
4. `created_at DESC, snapshot_id DESC`.

Thus newer evidence beats stale higher-priority evidence; type precedence resolves same-date conflicts. If global type priority is required, document and test that explicitly. Replace every direct snapshot reader, not just the dashboard query that exposed the issue.

## Account-scoped import metadata

Persist coverage per effective account at import confirmation: period, physical/logical/imported/duplicate/review counts. Aggregate all scoped rows for that account. If historical scope cannot be inferred unambiguously without raw sources, report `unknown`; never clone institution-wide coverage onto every account.

## Counterexample tests

Prove at minimum:

- two specific accounts plus generic resolve independently, including a multi-account file;
- effective correction changes reads once while raw rows remain unchanged;
- same-date precedence is insertion-order independent;
- newer low-priority evidence beats older high-priority evidence under date-first semantics;
- multiple same-source files do not overcount account metadata;
- unknown historical scope is not copied;
- preview performs no writes;
- baseline drift, altered intent, arbitrary preview ID, wrong phrase, empty note, inactive/wrong-role target, duplicate assignment, and conflicting correction all fail with zero writes;
- ambiguous items prevent partial confirmation;
- injected audit/final-item failure rolls back everything;
- identical replay adds no batch/audit/correction;
- APIs disclose no raw references or unmasked identifiers.
