Part of the Simple Agility stack. Canonical copy:
~/source/stockroom/docs/superpowers/plans/2026-10-03-stockroom-phase-1-foundation.md, with 20 test-first tasks and complete code for every step. This page mirrors the decisions and structure for retrieval. It implements rollout phase 1 of StockroomIntakeInventoryPricingDesign. Status: written 2026-10-03, awaiting the operator's review.
Phase 1 delivers a deployed Stockroom that gives a real inventory of what is owned and where. It covers the repository scaffold, Simple Agility telemetry, a single operator login, acquisitions, locations, scan-by-ISBN intake with background catalog lookup, the inventory record and the work queues.
The backend is a Maven multi-module Spring Boot 4.1 application on Java 25: persistence → core → catalog/api → app.
It runs on PostgreSQL 18 with Flyway. A React 19 + Vite single-page app is served by the same container. A scan
creates the item immediately, and a durable database-backed job queue (FOR UPDATE SKIP LOCKED) identifies it in
the background. The lookup checks the cached catalog_record first, then Open Library, then Google Books, each
behind a CatalogSource port with its own rate limit.
| Topic | Decision |
|---|---|
| Auth | One operator account from env (bcrypt hash). JSON login creates a server-side session; CSRF uses the double-submit cookie; failed logins are throttled to 5 per 15 minutes |
| SKU | Crockford base32 of a sequence starting at 32⁴, giving five characters (10000). Lookup tolerates O/I/L misreadings |
| Cost split | Exact cents. Overrides keep their cost and leftover cents go to the earliest items. Recomputed under the acquisition row lock |
| Kind at scan | ISBN gives book; UPC/EAN give other until identified by hand |
| Statuses | intake, needs_identification and identified only. status has no CHECK, so later phases can extend it |
| Catalog sources | Open Library first (keyless, 1 request/s). Google Books second, disabled without a key: its keyless quota is 0, observed 2026-10-03. The key goes in a header, never in a URL |
| Completeness | Only a record with a title and at least one author identifies a book. Incomplete records are never cached |
| Jobs | One worker thread, 6 attempts with exponential backoff, and a 5-minute lease after which a stuck job is reclaimed. Jobs carry the request's correlation_id |
| Undo | A mistaken scan can be deleted; costs re-split and its pending lookup no-ops |
| Packaging | One container serves both the API and the SPA. Host port 8090 on docker1 by default (confirmed during bring-up) |
| Backups | A nightly pg_dump sidecar with a stockroom_backup_* freshness gauge, a jakemon alert, and its own NAS pull. Wikantik's pull hard-codes its metric names, so reusing it would overwrite Wikantik's offsite gauge |
These are the five failure modes the spec implies but does not spell out. Each is pinned by a named test.
x, add-ons joined by a space, hyphens, and a 0-prefixed
EAN-13 that is really a UPC-A.\$ interpolation: startup fails naming the variable and the
single-quote fix.| # | Task | Model |
|---|---|---|
| 1 | Backend scaffold with /healthz and /metrics | sonnet |
| 2 | Structured JSON logs and correlation IDs | sonnet |
| 3 | Operator login | sonnet |
| 4 | Locations | sonnet |
| 5 | Acquisitions | sonnet |
| 6 | Identifier normalisation (pure) | haiku |
| 7 | SKU codec and cost allocator (pure) | haiku |
| 8 | Durable job queue and runner | sonnet |
| 9 | Items and scan intake | sonnet |
| 10 | Catalog source adapters (Open Library, Google Books) | sonnet |
| 11 | Background catalog lookup | sonnet |
| 12 | Inventory reads and work queues | sonnet |
| 13 | Inventory edits, moves, manual identification, undo | sonnet |
| 14 | Frontend scaffold, API client, login, queues home | sonnet |
| 15 | Locations and acquisitions screens | sonnet |
| 16 | Scan screen | sonnet |
| 17 | Item list and item screens | sonnet |
| 18 | Docker image, Compose stack, conformance check | sonnet |
| 19 | Coverage floors, verification, hand-over | sonnet + opus review |
| 20 | Production bring-up (operator-gated) | sonnet, with the operator |
Tasks 6 and 7 can run in parallel with Tasks 2–5, and Task 10 in parallel with Tasks 8–9. Everything else is sequential.
Pricing, triage, quotes, photos, listing content, GenAI, posting, crosslisting, delisting, sales, FBA labels and shipments, label printing, lots, any WMS beyond locations and moves, and anything for the rental business.
/metrics and /healthz this phase implements