Stockroom Photo Identification Design

Part of the Simple Agility stack. Canonical copy: ~/source/stockroom/docs/superpowers/specs/2026-10-09-stockroom-photo-identification-design.md. This page mirrors it for retrieval. Status: approved by the operator 2026-10-09; the phase 7 plan follows. Earlier designs: StockroomIntakeInventoryPricingDesign (which planned this as its phase 4), StockroomChannelsAndSalesDesign.

Context

Stockroom phases 1–6 take items in by barcode, price and route them, prepare listings, track them physically, and record posts and sales with eBay connected. An item without a usable barcode lands in Needs identification, where the operator types what it is; a non-book item without a GTIN then waits in Needs a price for a quote typed by hand. Phase 7 adds an ItemIdentifier: a model looks at the item's photos and proposes what it is.

What the operator decided (2026-10-09)

Decisions

DecisionChoice
Item statusUnchanged — the item stays needs_identification until confirmed
ConfirmingThrough the existing identification path and its knock-on effects (weight, drafts, re-pricing, sibling copies)
Provenanceai_identification when confirmed unchanged, manual when edited; the proposal records the outcome and the fields changed (feedback loop)
Modelclaude-haiku-5-5, effort high; about 0.3 cents an item (estimate)
Price without a GTINNew source ebay_keyword: eBay Browse by brand and model number
Trusting a keyword quoteTriage sends the item to Needs a decision with the matching listings shown
Job laneThe drafter's lane, renamed GENAI; per-provider rate limit shared

The proposal

Schema-checked JSON: kind (electronics, tool, collectible, other), title (≤ 80), brand, model (the model number as printed — null unless legible, never inferred), product_type, era, up to 5 details, confidence and observations (what it read and where). Confidence: high = brand and model read from the item; medium = brand read, model inferred from the design; low = a guess from appearance. A UPC that found nothing is passed as a hint.

Stored in a new identification_proposal table: the photos sent, provider, model, effort, prompt version, the proposal, and a status of pending → proposed / failed → confirmed / corrected / rejected / superseded. One open run per item.

Flows

  1. Identify — on the item page, Identify from photos (needs a photo; off for books). The card polls while open and offers Confirm, Edit (the Identify form, prefilled) and Not it - try again. Low confidence is shown prominently.
  2. Queues — new Check identification (proposals waiting); Needs identification keeps items with none.
  3. Confirm or correct — through IdentificationService.identify. The non-book form gains product type, era and details. Identifying a not-yet-listed item clears its old route until pricing decides again (a phase 2 carry-over).
  4. Price — ebay_keyword searches active fixed-price listings in the grade's eBay conditions: brand plus model number, or the title when there is none (always low confidence). Listings must contain the model number in the title; "box only", "manual only", "charger only", "for parts" and similar are dropped. Median total price; up to 10 matches stored as evidence; cached per day by the search words.
  5. Decide — a route resting on a keyword quote goes to Needs a decision ("eBay price from a keyword search (7 matches) - check them"), with the matches as links. Triage stays pure.

Adapters

stockroom.genai.identify.provider/model/effort/max-photos (default claude, claude-haiku-5-5, high, 4). Claude: structured output, no sampling parameters, no server-side fallback for Haiku, refusals become a failed run with the category. Ollama: think: false, photos as images. Up to 4 photos, label and detail first.

Failure handling

The item always stays in Needs identification with a reason and the button again: identification off, model down (retries, then failed), refusal, an unreadable answer (re-asked once), no readable photo, or the item changed meanwhile (superseded). The eBay code now honours Retry-After and catches every exception (phase 2 carry-over).

Telemetry

stockroom_identifications_total{provider,model,outcome}, stockroom_identify_seconds{provider}, the quote counter with source ebay_keyword, and identification_* events. No image data in logs. The proposal table is the stored prediction: the confirmed share per model is a query.

Testing and rollout

Unit tests for the schema, both adapters (fake HTTP), the job, the proposal states, the keyword filters and the triage rule; PhotoIdentificationIT end to end with fakes; frontend card and queue tests. The golden set gains ~10 of the operator's real items, run on Haiku and Opus for comparison (operator-run, it costs money). A live smoke test sends one real photo through Haiku 5.5 and runs one real keyword search. No automated test calls Anthropic or eBay.

Phase 7 is one plan of about 9 tasks. No new credentials. Production deploy stays operator-gated.

Risks


Cluster Hub: Stockroom Hub (stockroom)