# MVP-TASK-032 — Process-restart durability proof

**Status:** Implemented  
**Parent:** MVP-REL-001-S04; MVP-SALES-001-S09; MVP-HIST-001-S01, S07

## Objective and required behavior

Prove finalized records, active fulfillment state, history, and today's commercial figures survive
application process restart and are reconstructed from PostgreSQL.

## Scope, areas, and non-goals

Extend the existing packaged backend lifecycle/Docker or a focused reproducible integration harness.
No host crash simulation, high availability, backup/restore, or offline client persistence.

## Evidence and acceptance

Use all four IDs in restart evidence plus database integration. Accept when a real process restart,
not page state, proves durability and no duplicate history/sales appears, traceability is current,
and inherited DoD passes. Handoff records exact environment and remaining production gaps.

## Implementation record

Durability is now proven at both layers the task allows, with the packaged process and the storage
boundary each covering what the other cannot.

`tests/docker/backend-lifecycle.mjs` was extended from a bare clean-exit check into a data-bearing
restart proof against the running Compose stack. It provisions a synthetic operator through the
project's own guarded user tooling, then creates two real finalized orders **through the packaged
gateway HTTP API** — one confirmed commercial order and one non-commercial one — so the real request
path is what commits the facts. The non-commercial order is driven `Preparing → Ready → Delivered`,
so it leaves the active queue and only its rows can bring it back; the commercial order is left in
`Preparing` with a corrected queue number and note, so a genuinely non-default active state has to
survive. The backend process is then really replaced: Compose stop, a clean SIGTERM exit 0, start
again, and an asserted **different container PID and start time** so nothing can pass on page state.
Afterwards the run signs in again — Compose generates the local signing secret per start, so the
pre-restart session is expected to be invalid — which is precisely why the surviving records cannot
have come from anything the new process held. It then re-reads the active queue, today's commercial
figures, and every durable order/item/finalization/transition/checkout/metadata row through the same
gateway and compares them. History has no HTTP read, so the rows themselves are read with `psql`
inside the Compose PostgreSQL container; row counts of all six fact tables are compared too, because
a rendered figure can look correct while rows were appended behind it.

`order-restart-durability.integration.test.ts` carries all four IDs at the storage boundary, where a
browser journey could never reach: it closes every connection the application owns and then builds a
completely fresh repository, connection pool, and service graph, asserting the finalized snapshot,
the mid-fulfillment active state, the delivered order's full history, today's figures, and the row
counts are all identical. Reading again through the same rebuilt process must also be identical, so
a reconstruction that accumulated would drift rather than pass.

Two failure modes were ruled out by construction rather than by assertion. The restart proof is not
allowed to depend on a clean database: every precondition is scoped to the orders this run created,
because the validation database is shared and persistent and can already hold active orders and
sales from earlier steps. And this run leaves nothing at the head of the queue — its remaining
active order is delivered and its catalog rows and operator are deleted — so the browser suite that
runs afterwards meets the queue it started with.

Deliberately **not** claimed: no host crash, power-loss, high-availability failover, backup/restore,
or offline client persistence is simulated. Those remain production infrastructure concerns outside
this task, and the packaged restart proves the durable-state guarantee only.

Local validation is recorded in [current-state.md](../../../current-state.md). Hosted CI was not
run, so this is Implemented and awaiting CI rather than Done.
