Stockroom Phase 6 eBay Connection and Alerts Plan

Part of the Simple Agility stack. Canonical copy: ~/source/stockroom/docs/superpowers/plans/2026-10-08-stockroom-phase-6-ebay-connection-and-alerts.md (8 test-first tasks with complete code). This page mirrors the decisions for retrieval. It implements the phase 6 parts of StockroomChannelsAndSalesDesign and builds on StockroomPhase5ListingsAndSalesPlan. Status: built and reviewed 2026-10-09, not yet deployed or connected.

Goal

Without the eBay refresh token, eBay works by hand exactly as in phase 5. Without the Telegram variables nothing is sent; the pages still show everything.

Architecture

Key decisions

TopicDecision
ConnectedeBay is configured when the client id, secret and refresh token are set, and connected when configured and not refused. A refusal (invalid_grant, eBay rejecting the token, or a 403 with error 1100) is held in memory until restart. It sends one message and shows Re-connect eBay.
Orders pollThe poll reads from a stored bookmark minus 10 minutes; the first run reads the last 14 days. Each line is applied in its own transaction, oldest first. The bookmark moves only after every line is read and applied, so each line is recorded and messaged once. An unreadable line holds the bookmark but never blocks the readable ones.
SKU matchingMatching is trimmed and case-insensitive, with or without the I- label prefix. Lot members, FBA items and dissolved lots go to Unmatched eBay with the reason.
OversoldA paid line for an item that already sold elsewhere is saved as an oversold sale with no effects, and a message is sent. Cancelling the active sale promotes the newest oversold one.
AdoptingA sale recorded by hand on eBay gets the order and line numbers when the poll finds them.
CurrencyA paid line not in US dollars lands in Unmatched eBay without amounts and cannot be linked. The operator records it by hand and dismisses the row.
EndingOne ebay_end_listing job runs per post, queued after the sale commits. Already ended counts as success, and an unavailable eBay retries. A refusal leaves the post in To delist with one message. While an end job holds a post, it stays off To delist, its tile and the reminders; the pending-delists gauge still counts it.
Vanished listingsA post ends only when two complete active lists in a row both miss it: ended_on_ebay, or sold_elsewhere when it was pending. A list whose count differs from eBay's stated total is treated as incomplete and changes nothing.
Unmatched eBayEach order line or listing gets one row, however many polls see it, and new rows are announced in one summary message per sync. A later poll never resolves a row; the operator links, dismisses or dismisses all.
LockingA polled line locks its sale first when it has one, otherwise the item. Adopting locks the item then the sale, the reverse of a hand cancel. A simultaneous clash makes PostgreSQL abort one side, which retries or answers try again. This is accepted for one operator.
TelemetryNew counters cover sync outcomes, notifications and oversold sales, and delists gain api and sync. The gauge stockroom_ebay_sync_age_seconds lets jakemon alert when the orders sync has not succeeded for 15 minutes.

Tasks

  1. Module, ports and plumbing: stockroom-channels, V026 (ebay_unmatched), the eBay and notify ports, user tokens, Notifications, the environment
  2. Adapter: Fulfillment getOrders
  3. Adapters: Trading listings and ends, Telegram, ChannelsConfig
  4. Orders sync: SKU matching, the line mapper, oversold and promotion
  5. Listings sync and ending, the scheduler, delist reminders, the sync-age gauge
  6. API and end to end: eBay status, Link and Dismiss, To delist without posts an end job holds, EbaySyncIT
  7. Frontend: the eBay status line, the re-connect banner and Unmatched eBay
  8. Hand-over: the eBay sign-in runbook, Telegram setup, a read-only live smoke test, coverage, operator acceptance

Out of scope


Cluster Hub: Stockroom Hub (stockroom)