The problem
A window and door dealer runs their business on paper and spreadsheets. Stock counts drift, invoices get lost, and nobody trusts the numbers. Off-the-shelf inventory SaaS was rejected: none of it matched how the shop actually works.
Stockroom started local-first - one SQLite file and a localhost server on the dealer’s own PC, set up with a double-click. Once it became the daily system of record, it graduated to a VPS: the same single-file engine, now served over the web behind nginx, with a token-gated remote MCP connector so the LLM assistant can reach it from anywhere. The architecture didn’t change to make that move - that portability was the point of keeping the core pure.
Architecture: correctness by construction
The core design decision is an append-only, event-sourced ledger. Stock is never a mutable number in a row - it is the fold of an immutable event history. That gives you an audit trail for free and makes drift structurally impossible.
The engine follows a strict hexagonal layout:
src/stockroom/
engine/ PURE core - no I/O, no sqlite/MCP/HTTP imports
persistence/ SQLite implementation of the engine's ports
adapters/ thin entry points: po_ingest, mcp, web, pdf, watcher
tests/ mirrors the engine - one test per invariant
The engine/ package has no I/O imports at all: data in, data out. Everything effectful
lives behind ports implemented in persistence/ and adapters/. That boundary is what
makes the invariants testable in isolation.
The eight invariants
The spec defines eight non-negotiable invariants, each backed by its own test. The two that shape everything else:
- Stock rises only when a purchase order is received - never when paperwork is created.
- Stock falls only when a customer invoice is paid - never when it is drafted.
Paperwork and physical reality are different event streams, and the ledger refuses to confuse them.
The LLM is a translator, not a calculator
Stockroom is exposed to an LLM assistant through an MCP server, but the model’s role is deliberately tiny: it turns natural language into validated tool calls. It never computes stock, never maps SKUs, never touches the ledger directly. Every tool call is validated by the pure engine before any event is appended.
This is the pattern I keep coming back to for AI in business software: let the model handle language, and keep every number deterministic, replayable, and auditable.
About the demo above
The walkthrough is recorded against Stockroom’s sandbox - an isolated copy with seeded demo data and a test login that never touches the real book. It tours the dashboard (KPIs and the needs-mapping queue), stock levels, purchase orders, invoices, SKU mapping, and the reports page with inventory value and stock health.