# ADR-057: Reconciliation Statement Cannot Be Closed Until All Lines Are Matched

## Status

Accepted, 2026-06-22.

## Status History

```yaml
status_history:
  - date: 2026-06-22
    status: Proposed
    changed_by: hkl
    reason: Formalising hard-gate close and no-reopen policy for CC reconciliation
    changed_via: adr-kit (360lm)
  - date: 2026-06-22
    status: Accepted
    changed_by: hkl
    reason: close_statement RPC enforces 0 unmatched; closed statements are locked
    changed_via: adr-kit (360lm)
```

## Context

Credit Card Reconciliation requires matching every statement line (CC transaction) to an internal expense record. Closing a statement without all lines matched would leave unreconciled charges — these represent either: genuine expense records not yet entered, duplicate charges, or fraudulent transactions. Allowing partial close puts the finance team in a state where "closed" does not mean "fully verified." Similarly, allowing re-opening of a closed statement enables retroactive modification of reconciled periods — a standard accounting control violation.

## Decision

`close_statement(statement_id)` RPC enforces: **count of unmatched lines must be 0**. If any line has `match_status = 'unmatched'`, the RPC returns an error with the count of remaining unmatched lines. The UI surfaces this as a hard block — no "close anyway" option.

Once closed (`status = 'closed'`), a statement is **permanently locked**. There is no `reopen_statement` RPC. If corrections are needed post-close, a new statement is created or an admin notes the correction separately — the closed period record is not modified.

"Matched" includes both `matched` (linked to an internal expense) and `marked` (manually marked as reviewed/acknowledged even without a direct expense match, e.g. a bank fee with no corresponding expense). Lines that cannot be matched must be explicitly marked before close is permitted.

**Decision Maker:** hkl

## Alternatives Considered

- **Warn on close if lines are unmatched (soft gate).** Rejected: "are you sure?" prompts are clicked through; finance admin under time pressure closes with unmatched items, planning to "come back to it" — this never happens; unmatched items are buried in a "closed" statement.
- **Allow partial close with an "unmatched items" flag.** Rejected: introduces a third statement state (closed-partial) that complicates the TM/admin dashboard; "closed" should unambiguously mean "fully reconciled."
- **Allow statement reopening.** Rejected: reopening allows retroactive modification of a closed accounting period; violates basic accounting controls; creates audit trail ambiguity (what was the state when the statement was closed?).
- **No close concept (statements are always editable).** Rejected: without a closed state, there is no point of finality for a reconciliation period; finance team cannot produce a signed-off monthly reconciliation report.

## Consequences

**Positive:**
- "Closed" unambiguously means "every line reviewed and accounted for."
- No retroactive modification of closed periods — clean audit trail.
- Finance team is forced to address every line before moving on.

**Negative / Trade-offs:**
- Cannot close if even one line is unmatched — a line from a cancelled charge (chargeback pending) blocks close until manually marked.
- No reopen means genuine errors in a closed statement require a correction note outside the system.
- Close requires admin diligence — cannot be used as a "done for now" marker.

**Risks and mitigations:**
- Line cannot be matched or marked (no corresponding expense, no explanation): mitigated by the "mark" action (mark as reviewed with a freetext reason) — any line can be marked even without a match.
- Accidental close (all lines marked incorrectly to force close): audit trail shows who marked what and when; admin can review marks in the closed statement read-only view.

## Related Decisions

- ADR-056 (AI hand-off for statement extraction) — AI hand-off populates the statement lines that this gate applies to.
- ADR-033 (atomic impress deduction) — same principle: financial state transitions must be hard gates, not advisory warnings.

## References

- `memory/dbt_creditcard.md` — "close gated on 0 unmatched lines"; "Statement reopening: once closed, statement is locked"
- `creditcard/index.html` — close_statement RPC, unmatched count check
