# Frozen availability and hold interface

Frozen 2026-10-25 by root with reservation author review, before implementation or derivatives. Extends Stage 0 without changing any Stage 1 command.

## Learning outcome and boundary

Given a trusted, complete snapshot of one credit-normal customer deposit liability at an explicit as-of time, calculate posted, reserved, available, spendable, and shortfall amounts without counting captured value twice. This is Cedar's `cedar-no-overdraft-v1` teaching product: credit limit zero; pending incoming transfers informational only; no deposit-availability schedule, card-network rules, overdraft facility, or real-bank policy replication. It is a read calculation, not authorization or a concurrency control.

The exact request keys are `schema_version` (`d30-availability-v1`), `policy_version` (`cedar-no-overdraft-v1`), `ledger_id`, `entity_id`, `account_id`, `account_type` (`liability`), `normal_side` (`credit`), `currency` (`USD` or `EUR`), `as_of`, `snapshot_at`, `posted_balance_minor`, `holds`, `pending_incoming`. All are required. Stage 0 identifiers and real Gregorian millisecond UTC timestamps apply. `snapshot_at` must equal `as_of`; stale or future snapshots reject. The snapshot exporter is trusted to supply complete posted and hold state from one consistent boundary. This function cannot establish that provenance from caller-supplied JSON. Never reconstruct a past snapshot by filtering a later terminal hold row.

Posted balance is the normal-side liability amount, a canonical signed decimal string: zero `0`, positive `[1-9][0-9]*`, negative `-[1-9][0-9]*`. This envelope accepts at most 120 magnitude digits as a resource limit; the underlying ledger uses arbitrary-precision sums. No floating-point conversion or rounding. USD and EUR exponent 2; no currency aggregation. Arrays contain at most 1000 rows each, no duplicate IDs.

## Shared hold snapshot v1

Exact keys: `ledger_id`, `entity_id`, `account_id`, `currency`, `hold_id`, `original_minor`, `captured_minor`, `released_minor`, `remaining_minor`, `created_at`, `updated_at`, `expires_at`, `version`, `status`. Scope must equal the request. Original is a positive canonical decimal string up to 30 digits; other amounts are nonnegative canonical strings up to 30 digits. Version is a positive canonical integer string up to 30 digits. Original = captured + released + remaining. `created_at <= updated_at <= as_of`, `created_at < expires_at`.

Stored status is one of `active`, `captured`, `released`, `expired`. Active requires remaining > 0 and released = 0; prior partial captures may be positive. Captured requires remaining = 0 and captured > 0; a final partial capture may release the rest. Released/expired require remaining = 0 and released > 0; prior captures may be positive. Expired requires updated_at >= expires_at. Captured/released terminal updates must precede expiry under this lifecycle. An active row may remain stored after expiry because a worker has not persisted the terminal event.

Read effectiveness is half-open: an active hold counts its remaining amount only when `as_of < expires_at`. At exact equality or later it counts zero and reports `effective_expired`, without mutating its stored status. Terminal rows count zero. New captures use trusted processing time strictly before expiry; clients cannot backdate a capture. Partial capture reduces both posted liability and remaining hold atomically; final capture releases the remainder. Reservation topic owns commands, durable idempotency, races and atomic posting. New trusted liability `card_settlement_payable_usd` is an explicit Stage 2 chart extension, not an alteration of Stage 1 fixtures.

Pending incoming exact keys: `pending_id`, `amount_minor`, `source_available_at`. IDs unique, amount positive up to 30 digits, genuine timestamp <= as_of. Rows are synthetic informational evidence, scoped by enclosing snapshot and currency. Future evidence rejects rather than enters the diagnostic totals.

## Calculation and independent oracle

Let L be posted normal balance and H the sum of effective remaining holds. A = L - H; spendable = max(0,A); shortfall = max(0,-A). Pending incoming and credit facility contributions are both zero. Preserve negative A; do not hide it by clamping the displayed balance. Output every hold's stored/effective status and counted amount in input order. Reject malformed input with a stable reason and null result; never return a partial sum.

Independently calculated Cedar sequence in USD cents:

| State | L | Effective H | Incoming info | A | Spendable | Shortfall |
|---|---:|---:|---:|---:|---:|---:|
| Corrected funding/transfer snapshot | 97600 | 0 | 0 | 97600 | 97600 | 0 |
| Reserve 30000 | 97600 | 30000 | 0 | 67600 | 67600 | 0 |
| Capture 12000, retain 18000 | 85600 | 18000 | 0 | 67600 | 67600 | 0 |
| Add pending incoming 5000 | 85600 | 18000 | 5000 | 67600 | 67600 | 0 |
| Final capture 10000, release 8000 | 75600 | 0 | 5000 | 75600 | 75600 | 0 |
| Reserve exactly 75600 | 75600 | 75600 | 5000 | 0 | 0 | 0 |
| Expiry equality, worker delayed | 75600 | 0 | 5000 | 75600 | 75600 | 0 |
| External posted debit creates deficit | -400 | 0 | 5000 | -400 | 0 | 400 |

These are read snapshots, not a claim that the product authorizes the final external debit. The no-overdraft reservation guard cannot make all external ledger posting paths obey a policy they did not invoke. A consistency exporter must bind captures and hold changes to one snapshot; otherwise arithmetic alone cannot detect every stale capture/hold mismatch.

## Acceptance

Strict exact-key/type validation; no coercion; whole-result Python/TypeScript parity; independent expected arithmetic; expiry equality and one millisecond before; partial/final capture; duplicate hold; mixed scope/currency; malformed decomposition/status; future/stale evidence; unsupported product/account perspective; exact large integers. Guided lab shows at least these eight meaningful states plus a failure scenario, all calculations from the reference implementation. Render and inspect Mermaid/SVG, browser-test controls and 390/768/1440 layouts. Visitor checks are an aggregate release gate.
