Stockroom Phase 2 Pricing and Triage Plan

Part of the Simple Agility stack. Canonical copy: ~/source/stockroom/docs/superpowers/plans/2026-10-03-stockroom-phase-2-pricing-triage.md. It has 17 test-first tasks with complete code for every step. This page mirrors the decisions and structure for retrieval. It implements rollout phase 2 of StockroomIntakeInventoryPricingDesign and follows StockroomPhase1FoundationPlan. Status: written 2026-10-03; execution started the same day.

Goal

Price every identified item for each sales route (Amazon FBA, eBay, local, buyback, lot). Then triage it to the best route and explain why. The pieces:

The Amazon SP-API price source is not in this plan. It waits for SP-API approval and gets its own small plan.

Architecture

New core packages:

A new adapter module, stockroom-pricing, holds the eBay Browse adapter.

An item that becomes identified enqueues a price_item job. The job:

  1. asks each enabled price source for an observation, outside any database transaction, through a shared non-blocking per-source rate limiter (a rate-limited job is deferred without spending a retry);
  2. stores quotes, with the net worked out from the active rules;
  3. derives the local value;
  4. runs the pure triage;
  5. records the decision and moves the item to its route's status.

Manual quotes and overrides apply at once, with no job and no network.

Decisions the spec left open

TopicDecision
eBay accessThe Browse adapter is built but disabled until EBAY_CLIENT_ID/EBAY_CLIENT_SECRET are set. eBay documents Buy-API production access as partner-only, so manual quotes are first-class for every route
FBA without SP-APIA manual FBA quote takes the list price, sales rank and a restricted flag from the Amazon Seller app; the fees are worked out from the rules
eBay comparablesActive fixed-price listings by GTIN, in the item's condition and its neighbours. List price = median of price + fixed shipping; USD only. Confidence comes from the comparables count
eBay netWe offer free shipping: list − 15.3% final value fee − $0.30/$0.40 per-order fee − our Media Mail (books) or Ground Advantage postage by weight
FBA netlist − 15% referral − $1.80 closing fee − fulfillment by shipping weight (+3.5% surcharge) − inbound per unit
WeightBooks: base + per-page × pages, by binding; a plausible Open Library weight wins. It is stored as catalog_estimate unless the operator entered a weight
Effective quotePer route, among usable quotes (not older than quote_max_age_days): the newest manual, else the newest automatic, else the newest derived
Triage order1. Look-closer or high value → needs a decision (high value also flags look-closer). 2. No usable quotes → needs price. 3. A lot item takes the best of eBay/local. 4. FBA if net ≥ min, rank ≤ cap and not restricted. 5. The best of eBay/local if net ≥ the individual minimum (ties go to local). 6. Buyback ≥ min. 7. Otherwise lot
Decision recordRule fired, reason, expected net (the prediction), quotes used, suppressed routes with measured and required values, rules version, and the item's cost at decision time. An override keeps the recommendation
Idempotent jobsPricing de-duplicates on item_id + requested_at; an identical decision is not re-inserted. A source cache per (identifier, condition, day) means copies cost one call
LotsA lot is an item of kind lot with no acquisition. Its cost is the sum of its members' costs. Members move to ready. Dissolving returns them to needs_prep. Value = Σ member local value × lot_factor
RulesVersioned in triage_rules; version 1 is seeded at start-up from bundled defaults marked uncalibrated (2026-10 research). Edited on the Rules page

Review focus

Five failure modes, each pinned by named tests:

  1. A pricing job that runs twice (crash, reclaim or deferral) writes one quote and one decision, and a deferral never spends a retry.
  2. Noisy eBay data. $0.99/$999 outliers, non-USD listings, missing shipping, unknown conditions, zero results, an expired token and rate limiting are all handled without blocking the worker.
  3. Odd or partial quotes. These include a missing sales rank, negative nets, stale quotes, look-closer with no quotes, and high value that also qualifies for FBA. Triage never routes without a usable quote and never auto-routes a look-closer or high-value item.
  4. eBay unavailable for a whole session. No item sticks in identified, and a manual quote re-triages immediately.
  5. Lot integrity while items change. A conflict returns 409 with nothing half-done. An item is never in two lots, and lots and their members cannot be deleted.

Tasks

#Task
1Non-blocking source rate limits and job deferral
2Catalog record kind and lock-before-load
3Versioned triage rules
4Binding, the book weight estimate, and Open Library binding and weight
5Pricing schema, routes, statuses and net maths
6The pure triage function
7TriageService and PricingService
8Pricing HTTP and the read side
9The automatic pricing job
10The eBay Browse adapter (new module)
11Lots (backend)
12Frontend: wire types, API modules, labels, queues and the item list
13Frontend: the item page pricing card
14Frontend: the Decide page
15Frontend: the Lots page
16Frontend: the Rules page
17Hand-over: configuration, docs, coverage floors and verification

Out of scope for phase 2

See also