v2.20.0
v2.20.0 — the ledger gets a guard, the error points at the field
Nine sprints, zero migrations. Two ledger holes found by audit and closed before any customer could find them; a registry that makes the third one impossible to add silently.
Nine sprints, zero migrations, every one PO-smoked before merge.
Shipped
LEDGER-GUARD-01 (#274): the last direct stock endpoint is gone. PATCH /products/:id/stock could overwrite a bucket with no movement row; it is retired, and a replay of the ledger against the snapshot is now a test that fails first.
LEDGER-GUARD-02 (#276): the opening balance is a row too. A CSV import that sets an opening quantity writes an INITIAL_SEED movement in the same transaction, with the importer as the user. No backfill of past imports; they stay named reconciliation discrepancies.
LEDGER-GUARD-03 (#280): the stock writer registry. Every call site that can change a bucket is listed with its movement type and its witness; CI refuses a new writer that is not on the list. Four sites are registered as not yet ledgered by name, each with the sprint that closes it.
UX-ERRORS-02 (#282): the error points at the field. When a lot-tracked product is received without batch or expiry, the refusal now paints the field, says what is missing under it, and focuses it. The same rule on the server names the fields it refused. The message did not change.
STATION-VERIFY-RACE-01 (#266): an empty list is not verified. The station console could paint a pack step as verified before the pack lines had loaded; unknown is now its own state with its own message, and a completed order never flashes the pick step.
PO-DRAFT-AUTOSAVE-01 (#267): purchase orders survive the tab, with the same server-side parachute sales orders got in v2.19.0, now a shared primitive.
UX-ACTIONS-01 (#255) + HF1 (#262) + HF2 (#269): one way to act on a row. One action shows as an icon, two or more as a menu, on every list. The menu survives a list refresh and measures itself before it is placed, so it never opens over the sidebar.
UI-NITS-02 (#253) + UI-NITS-03 (#278): two design-system laws written down. Text on a semantic surface inherits its token; a warning banner is a tint, not a brick, in both modes, with a quieter gold in dark.
Website
The public site grew from a skeleton to a glossary, four problem pages and a buyer center: a WMS hub with eight guides and a commercial twin. Every claim on those pages is tied to the release that shipped it.
Governance
First-client gate sealed in the architecture: five conditions, all green at once, before any tenant other than the owner’s runs in production. An Angular frontend standard: signal-first, reusable primitives, nothing converted before an audit.
Migrations: none.
Ritual: backup drill before the tag · Railway green first, Vercel confirmed · production smoke after deploy on three paths (lot-gate field paint, PO autosave row, station verify).