Stockroom Phase 1 Foundation Plan

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.

Goal

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.

Architecture

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.

Decisions the spec left open

TopicDecision
AuthOne 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
SKUCrockford base32 of a sequence starting at 32⁴, giving five characters (10000). Lookup tolerates O/I/L misreadings
Cost splitExact cents. Overrides keep their cost and leftover cents go to the earliest items. Recomputed under the acquisition row lock
Kind at scanISBN gives book; UPC/EAN give other until identified by hand
Statusesintake, needs_identification and identified only. status has no CHECK, so later phases can extend it
Catalog sourcesOpen 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
CompletenessOnly a record with a title and at least one author identifies a book. Incomplete records are never cached
JobsOne 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
UndoA mistaken scan can be deleted; costs re-split and its pending lookup no-ops
PackagingOne container serves both the API and the SPA. Host port 8090 on docker1 by default (confirmed during bring-up)
BackupsA 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

Review focus

These are the five failure modes the spec implies but does not spell out. Each is pinned by a named test.

  1. Rapid-fire scanning into one acquisition: one item per scan, unique SKUs, and costs always summing to the total.
  2. Scanner input quirks: trailing CR/LF, a lowercase x, add-ons joined by a space, hyphens, and a 0-prefixed EAN-13 that is really a UPC-A.
  3. Misbehaving catalog sources: keyless Google Books 429, an Open Library record without authors, a Google Books volume for a different ISBN, garbage bodies and timeouts.
  4. Restart during a lookup: the stuck job is reclaimed and the item still gets identified.
  5. A mangled password hash from Compose \$ interpolation: startup fails naming the variable and the single-quote fix.

Tasks

#TaskModel
1Backend scaffold with /healthz and /metricssonnet
2Structured JSON logs and correlation IDssonnet
3Operator loginsonnet
4Locationssonnet
5Acquisitionssonnet
6Identifier normalisation (pure)haiku
7SKU codec and cost allocator (pure)haiku
8Durable job queue and runnersonnet
9Items and scan intakesonnet
10Catalog source adapters (Open Library, Google Books)sonnet
11Background catalog lookupsonnet
12Inventory reads and work queuessonnet
13Inventory edits, moves, manual identification, undosonnet
14Frontend scaffold, API client, login, queues homesonnet
15Locations and acquisitions screenssonnet
16Scan screensonnet
17Item list and item screenssonnet
18Docker image, Compose stack, conformance checksonnet
19Coverage floors, verification, hand-oversonnet + opus review
20Production 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.

Out of scope for phase 1

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.

See also