diff --git a/app_spec.txt b/app_spec.txt
deleted file mode 100644
index 4a47408..0000000
--- a/app_spec.txt
+++ /dev/null
@@ -1,381 +0,0 @@
-# Zman TCG Fulfillment - Unified Product Requirements Document (PRD)
-
-This document serves as the comprehensive, developer-ready blueprint and product requirements document for the Zman TCG Fulfillment system. It consolidates all architectural designs, implementation plans, integration research, workflows, and database schemas into a single source of truth.
-
----
-
-## 1. Product Overview
-
-The Zman TCG Fulfillment project is a centralized inventory, pricing, and order management system for a Trading Card Game (TCG) business. It unifies fragmented data across multiple external platforms and APIs (ManaPool, TCGPlayer, TCGCSV, MTGJSON) into a single source of truth. 
-
-The system empowers effortless platform-agnostic order fulfillment, integrates physical hardware (printers and scanners) to streamline warehouse workflows, implements a highly granular "Chaos Sort" inventory mechanism, and lays the groundwork for a proprietary internal storefront.
-
-### 1.1 Core Objectives
-- **Centralized Source of Truth:** Establish a unified catalog and inventory system that avoids alphabetized sorting delays.
-- **Multi-Channel Orchestration:** Treat TCGPlayer, ManaPool, and custom storefronts as unified streams of orders and inventory.
-- **Hardware Integration:** Connect cloud-based serverless systems securely to local physical warehouse hardware.
-- **Automation at Scale:** Reduce manual data entry and repetitive fulfillment tasks through scraping, APIs, and intelligent shipping rules.
-
----
-
-## 2. Technical Architecture & Hosting Strategy
-
-The system relies on a bifurcated architecture to leverage the best parts of Serverless computing while bypassing its limitations for specific heavy tasks.
-
-### 2.1 The Core Application (Serverless)
-**Stack:** Next.js (React), Tailwind CSS, Radix UI, Node.js API Routes.
-**Database:** PostgreSQL (using a Serverless-native provider like Neon/Supabase, or a connection proxy like PgBouncer to prevent connection limits from bursting webhooks). ORM via Drizzle or Prisma.
-
-*Included in the Core:*
-- The UI Dashboard.
-- Internal APIs for storefronts, scanner lookups, and order resolutions.
-- Webhook endpoints to ingest real-time data from ManaPool.
-- Outbound API calls to fast services (EasyPost, API-driven syncs).
-
-*Why Serverless?*
-Zero-maintenance auto-scaling. Webhooks from ManaPool or rapid bursts of scanner gun inputs instantiate instantly without managing server loads. The predictable compute costs and edge network delivery make the UI incredibly fast.
-
-### 2.2 The Quarantined Services (Persistent / Local)
-By moving pain points out of the core app, system bottlenecks are eliminated:
-
-**A. The "Warehouse" Hardware Daemon:**
-- A tiny script (Node or Python) or a lightweight desktop app (Electron) running on a warehouse PC.
-- It securely polls the serverless API for pending print jobs (utilizing secure exponential backoff to minimize edge invocations).
-- Downloads PDFs or labels and routes them directly to local printers (Brother QL-1100 or network laser printers via `SumatraPDF` or `lp`). 
-- **Advantage:** Serverless never has to worry about local IP configurations or port forwarding. You pay $0.00 for idle hardware management.
-
-**B. The ETL & Scraper Worker:**
-- A persistent, low-cost container (e.g., Railway, DigitalOcean, Render) running BullMQ + Redis.
-- Manages strict 15-second+ tasks: Puppeteer headless browsers to hijack TCGPlayer cookies, downloading massive bulk CSVs from TCGCSV, and asynchronous web scraping.
-
-## Part 1: Database Schema Design (PostgreSQL)
-
-Before any code is written, the central database must be structured to handle the "Chaos Sort" coordinates, universal product mapping, and multi-channel order orchestration.
-
-```sql
--- 1. Catalog & Products (Sourced from TCGCSV & MTGJSON)
-CREATE TABLE categories (
-    id INT PRIMARY KEY, -- e.g. 1 (MTG), 3 (Pokemon)
-    name VARCHAR(255) NOT NULL
-);
-
-CREATE TABLE groups (
-    id INT PRIMARY KEY, -- e.g. 3170 (Silver Tempest)
-    category_id INT REFERENCES categories(id),
-    name VARCHAR(255) NOT NULL,
-    published_on DATE
-);
-
-CREATE TABLE products (
-    id INT PRIMARY KEY, -- TCGPlayer Product ID
-    group_id INT REFERENCES groups(id),
-    name VARCHAR(255) NOT NULL,
-    clean_name VARCHAR(255),
-    image_url TEXT,
-    -- Native TCGTracking & Affiliate IDs
-    scryfall_id UUID,
-    mtgjson_uuid UUID,
-    tcgplayer_url TEXT,
-    manapool_url TEXT,
-    card_number VARCHAR(50),
-    rarity VARCHAR(50)
-);
-
-CREATE TABLE skus (
-    id INT PRIMARY KEY, -- TCGPlayer SKU ID
-    product_id INT REFERENCES products(id),
-    condition VARCHAR(10) NOT NULL, -- NM, LP, MP, HP, DMG
-    variant VARCHAR(10) NOT NULL, -- N (Normal), F (Foil), etc.
-    language VARCHAR(10) NOT NULL, -- EN, JP, DE, etc.
-    market_price_cents INT,
-    lowest_listing_cents INT
-);
-
--- 2. Chaos Sort Locations
-CREATE TABLE physical_locations (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    coord_string VARCHAR(50) UNIQUE NOT NULL, -- Format: 01:01:01:01:01:01
-    section VARCHAR(10), aisle VARCHAR(10), rack VARCHAR(10), 
-    shelf VARCHAR(10), box VARCHAR(10), slot VARCHAR(10)
-);
-
--- 3. Inventory Master
-CREATE TABLE inventory (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    sku_id INT REFERENCES skus(id), -- Maps exactly to the physical SKU combination
-    location_id UUID REFERENCES physical_locations(id),
-    quantity INT DEFAULT 0,
-    listed_price_cents INT NOT NULL,
-    -- Sync tracking
-    last_synced_tcgplayer TIMESTAMP,
-    last_synced_manapool TIMESTAMP
-);
-
--- 4. Unified Orders
-CREATE TABLE orders (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    marketplace VARCHAR(50) NOT NULL, -- 'TCGPLAYER', 'MANAPOOL', 'INTERNAL'
-    marketplace_order_id VARCHAR(255) UNIQUE NOT NULL,
-    status VARCHAR(50) DEFAULT 'PENDING', -- PENDING, PRINTED, SHIPPED, CANCELLED
-    buyer_name VARCHAR(255),
-    shipping_address JSONB NOT NULL,
-    total_cents INT NOT NULL,
-    shipping_cents INT NOT NULL,
-    tracking_number VARCHAR(255),
-    easypost_label_url TEXT,
-    created_at TIMESTAMP DEFAULT NOW()
-);
-
-CREATE TABLE order_lines (
-    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
-    order_id UUID REFERENCES orders(id),
-    inventory_id UUID REFERENCES inventory(id), -- Maps exactly to the physical card leaving
-    quantity INT NOT NULL,
-    price_cents INT NOT NULL
-);
-```
-
----
-
-## Part 2: Configurable Hybrid Catalog ETL Worker (Phase 1)
-**Stack:** Node.js (TypeScript), BullMQ + Redis, Node `pg` Pool.
-
-**Core Philosophy:** The structural shape of the data (endpoints, schemas) can and should be hardcoded. However, execution parameters (when to run, what to fetch) must be driven by configuration.
-
-**Developer Steps:**
-1. **Configuration Driven Execution:** The cron schedules for both the TCGCSV and TCGTracking pulls must be derived from `process.env` (e.g., `CRON_TCGCSV_SYNC`, `CRON_TCGTRACKING_SYNC`). 
-2. **Dynamic Game Targeting:** The worker must read an array of `SUPPORTED_CATEGORIES` (e.g., `[1, 3, 27]`) from the configuration. The worker iterates over this array, hitting the hardcoded `https://tcgcsv.com/tcgplayer` endpoints for those specific IDs.
-3. **SKU & Pricing Enrichment (TCGTracking):** Following the base catalog sync, the worker iterates over those identical dynamic `SUPPORTED_CATEGORIES` against the Open TCG API (`/skus` endpoints) to build out the `skus` table and inject `mtgjson_uuid` where available.
-4. **Resilient Upsert Logic:** 
-    - Use `ON CONFLICT (id) DO UPDATE` to safely build the `products` table from TCGCSV.
-    - Process TCGTracking payloads concurrently in queues to build out child `skus` without locking the database or exceeding memory limits.
-
----
-
-## Part 3: Undocumented TCGPlayer API Reverse Engineering (Phase 3)
-*This is the most critical and fragile component. It runs exclusively in the Persistent Worker to avoid serverless timeouts.*
-
-**The Methodology:**
-1. **Puppeteer Stealth:** Install `puppeteer-extra` and `puppeteer-extra-plugin-stealth` to bypass TCGPlayer's Cloudflare/DataDome challenges.
-2. **Session Interception:**
-    *   Launch browser, navigate to `store.tcgplayer.com/admin/login`.
-    *   Inject credentials securely. Wait for navigation.
-    *   On the network tab, listen for XHR requests to `https://order-management-api.tcgplayer.com/*`.
-    *   Extract the `Authorization: Bearer <TOKEN>` or the specific `Cookie` string holding the session ID.
-3. **API Exploitation:** Once the Bearer token/Cookie is captured, close the heavy browser. Store the token in Redis with a TTL (Time-To-Live). Proceed to make lightweight `axios`/`fetch` calls to the `order-management-api` endpoints until a `401 Unauthorized` is hit, which triggers the Puppeteer flow to restart.
-3. **API Exploitation:** Once the Bearer token/Cookie is captured, close the heavy browser. Store the token in Redis with a TTL (Time-To-Live). Proceed to make lightweight `axios`/`fetch` calls to the `order-management-api` endpoints until a `401 Unauthorized` is hit, which triggers the Puppeteer flow to restart.
-4. **Operations to map:**
-
-    **A. POST `/orders/search`** (To find new orders)
-    *   *Payload:* Requires pagination and filter logic (e.g., `status=New`).
-    *   *Response Structure:* Provides high-level `orderNumber` and totals for iteration.
-
-    **B. GET `/orders/{order_id}`** (To pull the line items)
-    *   *Response Structure:* Maps perfectly to our unified database!
-    ```json
-    {
-      "orderNumber": "E576ED4C...",
-      "status": "Ready to Ship",
-      "shippingAddress": { "recipientName": "John Doe", "addressOne": "123 St"... },
-      "products": [
-        {
-          "quantity": 1,
-          "productId": "652840",
-          "skuId": "8925977" // Explicit target for our skus table!
-        }
-      ]
-    }
-    ```
-    
-    **C. Inventory Operations & Document Exports:**
-    *   **Updating Price/Quantity for a Condition:** `POST https://store.tcgplayer.com/admin/product/updateinventory`
-        *Payload (URL Encoded Form Data):* Requires building a specific nested array string (e.g., `productQuantityPrices[0][ConditionQuantityPrices][0][Quantity]=2&...[Price]=0.26`).
-    *   **Inventory Mass Export CSV:** `POST https://store.tcgplayer.com/admin/pricing/downloadexportcsv` (Returns a CSV containing your active Inventory mapped to their SKU IDs).
-    *   **Orders Mass Export CSV:** `POST https://order-management-api.tcgplayer.com/orders/export` (Returns a CSV of multi-order data).
-    *   **Generate Native Pull Sheets:** `POST https://order-management-api.tcgplayer.com/orders/pull-sheets/export`
-        *Payload:* `{"orderNumbers": ["E576ED4..."], "timezoneOffset": -5}` (Returns a PDF buffer).
-    *   **Generate Native Packing Slips:** `POST https://order-management-api.tcgplayer.com/orders/packing-slips/export`
-        *Payload:* `{"sortingType": "ByRelease", "format": "Default", "orderNumbers": ["E576ED4..."]}` (Returns a PDF buffer).
-
-    **D. Order Fulfillment (Placeholder)** 
-    Because marking an order as shipped could not be tested in the current network capture, this API call must be intercepted live during development.
-    *   *Action:* Find the API triggered when clicking "Add Tracking" or "Mark as Shipped" on the TCGPlayer UI.
-    *   *Expected Form:* Likely a `POST /orders/{id}/tracking` or `POST /orders/shipments` payload taking the `carrier` and `trackingNumber`.
-
----
-
-## Part 4: ManaPool OpenAPI Sync & Unification (Phase 4)
-**Stack:** Next.js API Routes (Serverless)
-
-Per the `https://manapool.com/api/docs/v1` specification, ManaPool operates on a standard REST API with predictable payloads. We must build robust webhook receivers to ingest this native shape and normalize it into our universal `orders` table.
-
-### 4.1 Ingestion (Webhook Listeners)
-**Endpoint:** `POST /api/webhooks/manapool/order-created`
-
-**Expected Native Payload Structure:**
-```json
-{
-  "event": "order.created",
-  "data": {
-    "id": "ord_12345abc",
-    "status": "unfulfilled",
-    "buyer": { "name": "John Doe" },
-    "shipping": {
-      "address1": "123 Main St",
-      "city": "Seattle",
-      "state": "WA",
-      "zip": "98101",
-      "country": "US"
-    },
-    "totals": {
-      "grand_total": 4550, // cents
-      "shipping": 500
-    },
-    "items": [
-      {
-        "id": "item_890xyz",
-        "product_id": "557921", // TCGPlayer ID Match
-        "condition": "NM",
-        "foil": true,
-        "quantity": 1,
-        "price": 2050
-      }
-    ]
-  }
-}
-```
-
-**Developer Steps:**
-1.  **Validation:** Validate the webhook signature using the configured `MANAPOOL_WEBHOOK_SECRET` header.
-2.  **Normalization:** Map the incoming Manapool native `items` array directly to our overarching `skus` table (using `product_id`, `condition`, and `foil` status dynamically) to create `order_lines`.
-3.  **Insert:** Write to the unify `orders` table.
-
-### 4.2 Fulfillment Upstream Sync
-When an order transitions to `SHIPPED` inside our UI, we must push the tracking state back to the external marketplace.
-
-**Endpoint:** `POST https://manapool.com/api/v1/orders/{order_id}/fulfill`
-**Method:** Create a background job triggered by the DB `status` change.
-
-**Payload Assembly:**
-```json
-{
-  "tracking_number": "9400123456789",
-  "carrier": "USPS"
-}
-```
-*Note: TCGPlayer fulfillment sync is fundamentally different and will require enqueuing a Selenium/Puppeteer automation job via the Phase 3 Session Manager.*
-
----
-
-## Part 5: EasyPost & Configurable Shipping Rules (Phase 5)
-**Stack:** Core Serverless App (Next.js) + `@easypost/api`
-
-The shipment generation flow must be incredibly fast (1-click) on the dashboard. To achieve this, the underlying rules engine cannot rely on hardcoded thresholds (e.g., "12 cards = Letter"). It must look up user-defined rules from a configuration file or database setting.
-
-### 5.1 The Configurable Rules Engine
-Before interacting with EasyPost, the system evaluates the unified `orders` record against settings defined in a `shipping_rules` table (or environment variables).
-
-**Example Configuration Structure:**
-```json
-{
-  "usps_letter_thresholds": {
-    "max_card_count": 14,
-    "max_order_value_cents": 1999,
-    "default_weight_oz": 2.0
-  },
-  "usps_ground_advantage_thresholds": {
-    "default_weight_oz": 4.0 // Baseline bubble mailer
-  }
-}
-```
-
-### 5.2 EasyPost Implementation Flow
-
-**Developer Steps:**
-1.  **Address Verification:** When the user clicks "Buy Postage", first call `client.Address.create({ verify: true, ... })`. If `verification.delivery.success` is false, halt and prompt the user to override.
-2.  **Evaluate Thresholds:**
-    *   *Calculate Order Stats:* Sum `order_lines.quantity` and read `orders.total_cents`.
-    *   *Decision:* If `sum(qty) <= max_card_count` AND `total_cents <= max_order_value_cents`, proceed with **USPS Letter**. Otherwise, proceed with **USPS Ground Advantage**.
-3.  **Create the Parcel & Shipment:**
-    ```javascript
-    // Example: USPS Letter Generation
-    const shipment = await client.Shipment.create({
-      to_address: verifiedBuyerAddress,
-      from_address: warehouseAddress,
-      parcel: {
-        length: 9.5, // Standard #10 envelope or PWE dimension
-        width: 4.1,
-        height: 0.1, // Crucial for PWE classification
-        weight: rules.usps_letter_thresholds.default_weight_oz 
-      }
-    });
-    ```
-4.  **Buy the Rate:** Iterate through `shipment.rates` to find the exact matching service (e.g., `service: 'First'` for USPS Letter, or `service: 'GroundAdvantage'`).
-    ```javascript
-    const purchasedShipment = await client.Shipment.buy(
-      shipment.id,
-      shipment.rates.find(r => r.service === targetService).id
-    );
-    ```
-5.  **Store & Queue:**
-    *   Save `purchasedShipment.postage_label.label_url` to the DB `orders` table.
-    *   Save `purchasedShipment.tracking_code`.
-    *   Insert a job into the `print_queue` for the Hardware Daemon to fetch and print locally.
-
----
-
-## Part 6: Hardware Interactor Daemon (Phase 6)
-**Stack:** Node.js locally running on warehouse PC, or Electron.
-
-**The Serverless Advantage:** Because the warehouse printing hardware is inactive 90% of the time, the *Serverless Core* handles the print queue perfectly. When no orders are being fulfilled, the cloud infrastructure scales to zero, meaning you pay exactly $0.00 for idle hardware management.
-
-**The Interaction Model:** 
-Since the Serverless Core (in the cloud) cannot securely "push" a PDF through your local router to your physical printer, we use a lightweight, outbound-only "pull" mechanism.
-
-**Developer Steps:**
-1. **The Print Queue (Cloud):** The serverless database maintains a simple `print_jobs` table (containing the PDF URL or ZPL string).
-2. **The Local Daemon (Warehouse):** A tiny Node.js script runs constantly in the background on your warehouse PC (a machine that is already turned on anyway).
-3. **Smart Polling Loop:** The daemon executes an HTTP `GET /api/internal/print-queue` against your serverless endpoint.
-    *   *Efficiency:* To minimize edge-function invocations when idle, the daemon should implement exponential backoff. Start polling every 5 seconds. If empty, increase to 10s, then 30s, up to a maximum of 60 seconds. As soon as a job is found (or a user clicks a "wake" button on the UI), it resets to 5 seconds.
-4. **Execution Routing:**
-    *   **ZPL/Label Queue:** If the job is a thermal label, pipe the EasyPost URL payload directly to the Brother QL-1100 native network IP (`raw socket port 9100`) or USB interface via `node-thermal-printer`.
-    *   **PDF Queue (Pull Sheets):** Download the generated PDF locally, then execute native OS print commands (e.g., `SumatraPDF -print-to <PrinterName>` on Windows, or `lp -d <PrinterName>` on Mac).
-    *   **Acknowledge:** Send a `POST /api/internal/print-queue/{id}/complete` to the serverless core to dequeue the job.
-
----
-
-## Verification Plan
-
-### Database & Types
-Ensure TypeScript types are auto-generated from the provided PostgreSQL schema using an ORM like Drizzle or Prisma, guaranteeing strict type-safety across the monorepo.
-
-### TCGPlayer Session Automation
-**Test Script:** A standalone manual script (`scripts/test-tcg-auth.ts`) must be written first. It will launch Puppeteer visibly (`headless: false`) so the developer can watch the bot navigate DataDome. The script will `console.log` the bearer token. If this token successfully curls the `/orders` endpoint, Phase 3 is validated and can be moved to headless production.
-
-### Hardware Validation
-A simulated endpoint will push a test label into the `/internal/print-queue`. The local developer machine will run the daemon, expecting a physical print from a configured local printer within 5 seconds.
-
-# Part 1.1: The Hybrid Catalog Architecture
-
-The foundation of the Zman TCG Fulfillment system relies on marrying the broad scope of **TCGCSV** with the deep, explicit data modeling of **TCGTracking**.
-
-### The Problem
-*   **TCGCSV** provides the absolute best, exhaustive coverage of TCGPlayer's catalog (Categories, Groups, Products) across *all* games (Pokemon, Lorcana, etc.). However, it does not provide SKU-level data (Condition/Finish) or prices for those specific conditions.
-*   **TCGTracking** completely maps the `Product -> SKU` relationship and native MTGJSON mapping, but its coverage outside of Magic: The Gathering is not guaranteed to be fully exhaustive.
-
-### The Hybrid Solution
-
-We will utilize both APIs in an ordered sequence to build our database.
-
-1.  **The Base Layer (TCGCSV):** 
-    We run our ETL pipeline against TCGCSV first. This ensures our `categories`, `groups`, and `products` tables are populated exhaustively for every single game on TCGPlayer. 
-    *   *Result:* We have a record for Product #`557921` (Llanowar Elves).
-
-2.  **The Enrichment Layer (TCGTracking):**
-    We immediately follow up by pinging the TCGTracking API for supported games. We use their `/skus` payload to generate the child records in our `skus` table and map the conditions (NM/LP) to the base `products` we just synced. 
-    *   *Result:* Product #`557921` is enriched with its native `mtgjson_uuid`. We generate SKU #`4639` (NM, Foil) and link it.
-
-3.  **The Fallback (Custom Webscrapers - Phase 3):**
-    If a user scans a Pokemon card, and we have the Product info (from TCGCSV) but no SKUs exist in our DB (because TCGTracking didn't cover it), we fall back to our custom persistent webscrapers. The scraper hits the TCGPlayer frontend, extracts the SKU IDs and prices for that specific Pokemon card, and writes them to the `skus` table permanently.
-
-By isolating the `products` table from the `skus` table, **our internal inventory ("Chaos Sort") only ever interacts with the explicit SKU ID.** It does not care where the SKU data originated from (TCGTracking vs. Custom Scraper), allowing for a highly extensible architecture.
diff --git a/app_spec.txt b/app_spec.txt
new file mode 120000
index 0000000..27217f0
--- /dev/null
+++ b/app_spec.txt
@@ -0,0 +1 @@
+/Users/zman/workspace/projects/zman-tcg-fulfillment/code/app_spec.txt
\ No newline at end of file
diff --git a/claude-progress.txt b/claude-progress.txt
deleted file mode 100644
index 1742c3f..0000000
--- a/claude-progress.txt
+++ /dev/null
@@ -1,235 +0,0 @@
-# Claude Progress - Zman TCG Fulfillment
-
-## Task 3: Core Database Schema - Categories and Groups Tables
-**Status:** Verified working (round 3 re-verification), awaiting tester sign-off
-**Date:** 2026-02-23
-
-### What Was Implemented
-
-1. **Enhanced `packages/shared/src/schema/catalog.ts`:**
-   - Added `InferSelectModel` and `InferInsertModel` imports from `drizzle-orm`
-   - Added typed exports: `Category`, `NewCategory`, `Group`, `NewGroup`, `Product`, `NewProduct`, `Sku`, `NewSku`
-   - These provide compile-time type safety for all insert and select operations on catalog tables
-   - The underlying table definitions (`categories`, `groups`, `products`, `skus`) with their relations and indexes were already in place from task 2
-
-2. **Updated `packages/shared/src/schema/index.ts`:**
-   - Added barrel export for all 8 new type aliases
-
-3. **Created `scripts/test-catalog-schema.ts`:**
-   - Uses **PGlite** (embedded in-process WASM PostgreSQL) — NO Docker or external DB needed
-   - 13 tests covering all 4 test strategy items:
-     - [1/4] Run migration (DDL) against test database
-     - [2/4] Insert sample category (id: 1, name: 'Magic: The Gathering') and group (id: 3170, name: 'Silver Tempest')
-     - [3/4] Verify FK constraint — invalid categoryId rejected, NULL categoryId allowed
-     - [4/4] Query with joins (inner + left) and verify TypeScript types
-
-4. **Created `scripts/_ensure-pglite.js`:**
-   - Auto-extracts PGlite from npm cache if `npm install` fails (registry 403)
-   - Runs automatically as part of `db:test:catalog` npm script
-   - Safe to run multiple times (exits early if PGlite already installed)
-
-5. **Updated `package.json`:**
-   - `db:test:catalog` script uses `node --import tsx` (not bare `tsx` CLI) to avoid IPC socket errors in sandboxed environments
-   - Script chain: ensure PGlite → run tests
-
-### Fix Applied (Round 2)
-Previous version required Docker PostgreSQL which was not available in the test environment. Fixed by:
-- Switched from `pg` + Docker to PGlite (embedded WASM PostgreSQL), matching `test-db-connection.ts` pattern
-- Added `_ensure-pglite.js` to auto-extract PGlite from npm cache when registry is blocked
-- Changed from bare `tsx` CLI to `node --import tsx` to fix EPERM socket errors
-
-### Round 3 Re-verification (2026-02-23)
-All tests confirmed passing in fresh worktree context:
-- `test-catalog-schema.ts`: **13 passed, 0 failed** (all 4 test strategy items verified)
-- `test-db-connection.ts`: **32 passed, 0 failed** (no regressions)
-- `tsc --noEmit` on shared package: **zero errors**
-- NOTE: npm registry is 403-blocked; copy node_modules from main repo or use `_ensure-pglite.js`
-
-### How to Test
-```bash
-# If npm install already worked:
-npm run db:test:catalog
-
-# If npm install failed (registry 403), run manually:
-node scripts/_ensure-pglite.js
-node --import tsx scripts/test-catalog-schema.ts
-
-# Also verify existing tests still pass:
-node scripts/_ensure-pglite.js
-node --import tsx scripts/test-db-connection.ts
-
-# TypeScript compilation:
-npx tsc --project packages/shared/tsconfig.json --noEmit
-```
-
-### What the Tester Should Pay Attention To
-- `npm run db:test:catalog` should show 13 passed, 0 failed
-- FK constraint test: insert with `categoryId: 99999` MUST be rejected
-- Join test: inner join = 2 rows, left join = 3 rows
-- TypeScript types importable from `@zman-tcg/shared`
-- `tsc --noEmit` on shared package = zero errors
-- Existing `db:test` (32 tests) still passes
-
----
-
-## Task 10: Implement Database Schema - Shipping Rules Table
-**Status:** Implementation complete, awaiting tester verification
-**Date:** 2026-02-23
-
-### What Was Implemented
-
-1. **Enhanced `packages/shared/src/schema/settings.ts`:**
-   - Added Zod validation schemas (`uspsLetterConfigSchema`, `uspsGroundAdvantageConfigSchema`)
-   - Added `shippingRuleConfigSchemas` registry mapping rule names to their Zod validators
-   - Added `validateShippingRuleConfig()` utility function for validating config JSON against expected shapes
-   - Existing schema table definition (`shippingRules`), TypeScript interfaces (`UspsLetterConfig`, `UspsGroundAdvantageConfig`), and seed data (`DEFAULT_SHIPPING_RULES`) were already in place from task 2
-
-2. **Updated `packages/shared/src/schema/index.ts`:**
-   - Exported new Zod schemas and validation utilities from the barrel
-
-3. **Created `scripts/test-shipping-rules.ts`:**
-   - Comprehensive 31-test suite covering all 4 test strategy items:
-     - [1/4] Insert default shipping rules (seed) - 7 tests
-     - [2/4] Query active rules - 4 tests
-     - [3/4] Config JSON structure validation (Zod) - 10 tests
-     - [4/4] Update threshold values + verify updatedAt timestamp changes - 10 tests
-   - Uses PGlite embedded PostgreSQL (no external DB required)
-
-4. **Updated `package.json`:**
-   - Added `test:shipping-rules` npm script
-
-### Key Decisions
-- The basic schema table definition, TypeScript interfaces, default seed data, and migration SQL already existed from task 2. This task enhanced the schema with Zod runtime validation and a comprehensive test suite.
-- `updatedAt` is managed application-side using `sql\`NOW()\`` when performing updates (not via a DB trigger), matching the Drizzle ORM pattern used elsewhere.
-- Config validation uses a registry pattern (`shippingRuleConfigSchemas`) so new rule types can be added by simply registering their Zod schema.
-
-### How to Test
-```bash
-npm run test:shipping-rules    # 31 tests, all should pass
-npm run db:test                # 32 tests, existing suite still passes
-```
-
-### What the Tester Should Pay Attention To
-- All 31 shipping rules tests should pass (insert, query active, Zod validation, update + updatedAt)
-- The existing `db:test` suite (32 tests) should still pass with no regressions
-- The Zod schemas correctly reject invalid configs (wrong types, missing fields, negative values, zero weights)
-- The `validateShippingRuleConfig()` function correctly routes rule names to their schemas
-- `updatedAt` is null on initial insert and gets set to current timestamp on updates
-
----
-
-## Task 16: Implement Puppeteer Stealth Session Manager for TCGPlayer
-**Status:** Implementation complete, awaiting tester verification
-**Date:** 2026-02-23
-
-### What Was Implemented
-
-1. **Created `packages/worker/src/services/tcgplayer-auth.ts`:**
-   - Core service that captures TCGPlayer bearer tokens via Puppeteer
-   - Dynamically loads `puppeteer-extra` + stealth plugin with fallback to plain `puppeteer`
-   - Navigates to `store.tcgplayer.com/admin/login`, fills credentials, submits form
-   - Sets up request interception to capture Authorization header from XHR requests to `order-management-api.tcgplayer.com`
-   - Stores captured token in Redis with configurable TTL (default: 1 hour)
-   - Closes browser after token capture to free resources
-   - Exports: `captureTcgplayerToken`, `getStoredToken`, `hasValidToken`, `invalidateToken`, `getOrRefreshToken`
-   - Fully typed with TypeScript interfaces: `TcgplayerAuthConfig`, `TokenCaptureResult`, `StoredTokenInfo`
-
-2. **Created `packages/worker/src/services/index.ts`:**
-   - Barrel export for all service exports
-
-3. **Created `packages/worker/src/types/puppeteer-extra.d.ts`:**
-   - Type declarations for `puppeteer-extra` and `puppeteer-extra-plugin-stealth`
-   - Prevents TS2307 errors during compilation when packages are optional
-
-4. **Updated `packages/worker/src/workers/index.ts`:**
-   - Replaced placeholder `processTcgplayerSession` with real implementation
-   - `capture-token` job type uses `getOrRefreshToken` (checks Redis cache first)
-   - `refresh-token` job type invalidates existing token then runs fresh capture
-   - Reads `TCGPLAYER_USERNAME`, `TCGPLAYER_PASSWORD`, `CHROME_PATH` from env
-
-5. **Updated `packages/worker/package.json`:**
-   - Added `puppeteer`, `puppeteer-extra`, `puppeteer-extra-plugin-stealth` to dependencies
-
-6. **Created `scripts/test-tcg-auth.ts`:**
-   - Standalone test script with 21 tests across 4 suites:
-     - [1/4] Module Exports & Constants (11 tests)
-     - [2/4] Redis Token Storage & Retrieval (7 tests)
-     - [3/4] Input Validation (3 tests)
-     - [4/4] Full Puppeteer Token Capture (4 tests, skipped without credentials)
-   - Three Redis backends: external Redis → redis-memory-server → in-memory mock
-   - Full mode (with `TCGPLAYER_USERNAME`/`TCGPLAYER_PASSWORD`): launches visible browser (headless: false), captures token, logs for manual curl testing
-   - Unit mode (no credentials): tests all service logic without browser
-
-7. **Created `scripts/_ensure-puppeteer.js`:**
-   - Extracts puppeteer, puppeteer-core, @puppeteer/browsers, chromium-bidi, ws, devtools-protocol, typed-query-selector, cosmiconfig, mitt, parse-json from npm cache
-   - Follows same pattern as `_ensure-pglite.js`
-   - Safe to re-run (skips already-installed packages)
-
-8. **Updated root `package.json`:**
-   - Added `test:tcg-auth` npm script
-
-9. **Updated `.env.example`:**
-   - Added `CHROME_PATH` and `TCGPLAYER_TOKEN_TTL` documentation
-
-### Key Decisions
-- **Dynamic imports with fallback:** `puppeteer-extra` and stealth plugin are loaded via `await import()` with try/catch. Falls back to plain `puppeteer` if not installed, logging a warning. This makes the service work in environments where the full stealth stack isn't available.
-- **Redis mock for testing:** The test script includes a minimal in-memory Redis mock so tests can run without any external Redis, redis-memory-server, or Docker. This ensures the test suite works in sandboxed CI environments.
-- **npm registry blocked:** The npm registry was 403-blocked during implementation. Dependencies are declared in package.json but need `npm install` when registry is available. The `_ensure-puppeteer.js` script extracts cached packages as a workaround.
-- **Token metadata:** Besides the token itself, metadata (capturedAt, TTL, sourceUrl, usingStealth) is stored in Redis under a separate key for debugging/monitoring.
-
-### How to Test
-```bash
-# Unit mode (no external deps needed):
-node --import tsx scripts/test-tcg-auth.ts
-
-# Or via npm script (also extracts puppeteer from cache if needed):
-npm run test:tcg-auth
-
-# Full mode with credentials (headless: false for observation):
-TCGPLAYER_USERNAME=your_user TCGPLAYER_PASSWORD=your_pass node --import tsx scripts/test-tcg-auth.ts
-
-# If npm install works (registry available):
-npm install
-npm run test:tcg-auth
-```
-
-### Re-verification (Session 2, 2026-02-23)
-All tests re-confirmed passing in a fresh worktree context:
-- `test-tcg-auth.ts`: **21 passed, 0 failed, 4 skipped** (no credentials set)
-- `test-db-connection.ts`: **32 passed, 0 failed** (no regressions)
-- `tsc --noEmit` on worker package: only pre-existing `redis-memory-server` error from task 12
-- All files properly committed in commit `b9ec244`
-
-### Re-verification (Session 3, 2026-02-23)
-Full re-verification in fresh worktree context — all tests confirmed passing:
-- `test-tcg-auth.ts`: **21 passed, 0 failed, 4 skipped** (no credentials set)
-- `test-db-connection.ts`: **32 passed, 0 failed** (no regressions)
-- `test-catalog-schema.ts`: **13 passed, 0 failed** (no regressions)
-- `tsc --noEmit` on worker package: only pre-existing `redis-memory-server` error from task 12 (zero new errors from task 16)
-- All implementation files verified: tcgplayer-auth.ts, services/index.ts, types/puppeteer-extra.d.ts, workers/index.ts, package.json, test-tcg-auth.ts, _ensure-puppeteer.js
-- Code review: service properly handles dynamic puppeteer-extra loading with plain puppeteer fallback, request interception for bearer token capture, Redis storage with TTL, input validation, and resource cleanup (browser.close)
-
-### Re-verification (Session 4, 2026-02-23)
-Fresh worktree context verification — all tests confirmed passing:
-- `test-tcg-auth.ts`: **21 passed, 0 failed, 4 skipped** (no credentials set)
-- `test-db-connection.ts`: **32 passed, 0 failed** (no regressions)
-- `test-catalog-schema.ts`: **13 passed, 0 failed** (no regressions)
-- `tsc --noEmit` on worker package: only pre-existing `redis-memory-server` error from task 12 (zero new errors from task 16)
-- All implementation files present and committed in `b9ec244`
-- No uncommitted changes (clean git status)
-
-### What the Tester Should Pay Attention To
-- `npm run test:tcg-auth` should show **21 passed, 0 failed, 4 skipped** (without credentials)
-- With credentials set, all 25 tests should pass and a browser should be visible
-- The captured token should be logged with a curl command for manual `/orders` endpoint testing
-- Redis TTL should be verified (test checks TTL is set correctly)
-- The service validates missing username/password before launching browser
-- `getOrRefreshToken` returns cached token without launching browser when Redis has one
-- TypeScript compilation: `tsc --noEmit` on worker package should have zero new errors (the only error is pre-existing `redis-memory-server` in test-queues.ts from task 12)
-- The worker's `processTcgplayerSession` handler is now wired to the real auth service
-- If npm install doesn't work, copy `node_modules` from the main repo: `cp -a ../code/node_modules .`
-
-## Previous Tasks (completed before this session)
-- Task 1: Initialized Next.js monorepo project structure
-- Task 2: Configured PostgreSQL Database with Drizzle ORM (includes base schema + migration)
-- Task 12: Set up Redis and BullMQ for Worker Package
diff --git a/claude-progress.txt b/claude-progress.txt
new file mode 120000
index 0000000..f566328
--- /dev/null
+++ b/claude-progress.txt
@@ -0,0 +1 @@
+/Users/zman/workspace/projects/zman-tcg-fulfillment/code/claude-progress.txt
\ No newline at end of file
diff --git a/scripts/test-tcg-auth.ts b/scripts/test-tcg-auth.ts
index e67f0d1..d89b6ba 100644
--- a/scripts/test-tcg-auth.ts
+++ b/scripts/test-tcg-auth.ts
@@ -6,12 +6,14 @@
  * It runs in two modes:
  *
  * 1. FULL MODE (with credentials + browser):
- *    - Launches Puppeteer with headless: false so you can observe
+ *    - Launches Puppeteer with headless: false so you can observe DataDome navigation
  *    - Logs into TCGPlayer admin
  *    - Captures the bearer token
  *    - Stores it in Redis
  *    - Verifies Redis TTL
- *    - Logs the token for manual curl testing
+ *    - Tests the token by making a real API request to /orders/search endpoint
+ *    - Reports success/failure based on API response
+ *    - Logs the token and curl command for further manual testing
  *
  * 2. UNIT MODE (no credentials / no browser):
  *    - Tests Redis token storage and retrieval
@@ -520,6 +522,10 @@ async function testFullPuppeteerFlow(): Promise<void> {
       'Redis TTL verified after capture',
       'requires credentials'
     );
+    skip(
+      'API validation: POST /orders/search with captured token',
+      'requires credentials'
+    );
     skip(
       'Manual curl command for /orders endpoint',
       'requires credentials'
@@ -566,6 +572,44 @@ async function testFullPuppeteerFlow(): Promise<void> {
     console.log(`    Redis TTL: ${ttl}s (configured: ${tokenTtl}s)`);
   });
 
+  await test('API validation: POST /orders/search with captured token', async () => {
+    assert(captureResult !== null, 'capture result must exist before API test');
+    assert(captureResult!.token.length > 0, 'captured token must not be empty');
+
+    console.log('    Testing token against /orders/search endpoint...');
+
+    const apiUrl = 'https://order-management-api.tcgplayer.com/orders/search';
+    const response = await fetch(apiUrl, {
+      method: 'POST',
+      headers: {
+        'Authorization': `Bearer ${captureResult!.token}`,
+        'Content-Type': 'application/json',
+      },
+      body: JSON.stringify({ page: 1, pageSize: 1 }),
+    });
+
+    console.log(`    API Response Status: ${response.status} ${response.statusText}`);
+
+    if (response.ok) {
+      const data = await response.json();
+      console.log(`    Response contains data: ${JSON.stringify(data).slice(0, 200)}...`);
+      console.log('\n    ╔══════════════════════════════════════════════════════════╗');
+      console.log('    ║  SUCCESS: Token is valid! /orders API responded OK.     ║');
+      console.log('    ╚══════════════════════════════════════════════════════════╝\n');
+    } else {
+      const errorText = await response.text().catch(() => '<no body>');
+      console.log(`    Response body: ${errorText.slice(0, 300)}`);
+      console.log('\n    ╔══════════════════════════════════════════════════════════╗');
+      console.log('    ║  FAILURE: Token was rejected by /orders API.            ║');
+      console.log('    ╚══════════════════════════════════════════════════════════╝\n');
+    }
+
+    assert(
+      response.ok,
+      `Expected 2xx from /orders/search, got ${response.status} ${response.statusText}`
+    );
+  });
+
   await test('token logged for manual curl testing', async () => {
     assert(captureResult !== null, 'capture result should exist');
 
