Files
pokemon-inventory/docs/IMPLEMENTATION-STATUS.md
T
2026-09-09 11:25:07 -04:00

14 KiB

Implementation Status

Last updated: 2026-09-08

Resume Here

Before changing code, read this document, AGENTS.md, and the linked source-of-truth documentation. Then confirm the current baseline with:

npm test

For persistence or Redis-backed changes, also run the isolated integration suite after setting TEST_DATABASE_URL to the *_test database and TEST_REDIS_URL to the test Redis instance:

TEST_DATABASE_URL='postgresql://...' TEST_REDIS_URL='redis://...' npm run test:integration

The current local container runtime is OrbStack. If Docker commands point at a stale host, run:

unset DOCKER_HOST
docker context use orbstack

Completed

Workspace And Database

  • Npm workspace with the API and shared contracts/validation packages.
  • PostgreSQL Compose service on local port 5433; API port 3000 is registered in .local/project-ports.json.
  • Redis Compose service on local port 6380 supports distributed login throttling.
  • Drizzle schema and migrations:
    • 0000_core_inventory: users, sellers, categories, condition profiles, conditions, products, and physical inventory items.
    • 0001_auth_sessions: hashed server-side sessions, expiry, revocation, IP address, and user agent.
    • 0002_session_csrf_tokens: session-bound hashed CSRF tokens.
    • 0003_security_audit_events: private append-only authentication security events.
    • 0004_password_reset_tokens: hashed, one-time password recovery tokens.
    • 0005_mfa_methods: protected TOTP method records and lifecycle state.
    • 0006_listings: listing records and lifecycle enum.
    • 0007_listing_image_count: persisted image prerequisite for publication.
    • 0008_inventory_images: item-owned image metadata and processing state.
    • 0009_remove_listing_image_count: publication no longer stores a listing-level image snapshot.
  • Isolated integration tests require a database name ending in _test and truncate only that database's tables.
    • Local-only npm run provision:admin creates the first Admin User and linked Seller atomically. It requires INITIAL_ADMIN_EMAIL, INITIAL_ADMIN_PASSWORD, and INITIAL_SELLER_NAME, hashes the password with scrypt, and refuses once any User exists.

Domain And Persistence

  • Product identity requires name, category, and seller.
  • Products receive seller-scoped, normalized slugs; duplicate names receive a numeric suffix.
  • Each Inventory Item is a separate physical copy.
  • Inventory condition must belong to the Product category.
  • Inventory lifecycle supports ACTIVE, SOLD, and ARCHIVED; sold and archived items are locked against ordinary edits.
  • Repositories for products, inventory items, conditions, users, and sessions use Drizzle/PostgreSQL.
  • Listing persistence supports draft creation, seller-scoped lookup, status updates, and inventory-item history.

API And Authentication

  • Fastify app bootstrap and GET /health response.
  • Admin Product endpoints:
    • GET /api/v1/admin/products
    • POST /api/v1/admin/products
  • Admin Inventory endpoint:
    • POST /api/v1/admin/inventory
  • Authentication endpoints:
    • POST /api/v1/auth/login
    • POST /api/v1/auth/logout
    • GET /api/v1/auth/me
  • Login uses asynchronous scrypt password verification. Session tokens are random, stored only as SHA-256 hashes, and never returned in JSON.
  • Session cookie settings are HttpOnly, SameSite=Strict, path /, and Secure in production.
  • Login is limited to five attempts per client IP every 15 minutes and returns 429 AUTH_RATE_LIMITED with Retry-After once exhausted. Redis atomically coordinates the limit across API processes.
  • Login sets a separate same-site CSRF cookie. Authenticated state-changing routes require its matching x-csrf-token header, validated against the session-bound hash.
  • GET /api/v1/auth/sessions returns safe metadata for the current user's active sessions and marks the current session. DELETE /api/v1/auth/sessions/:id revokes an owned session; DELETE /api/v1/auth/sessions revokes all other sessions while retaining the current one. Revoking the current session clears both cookies.
  • Active sessions renew last_activity_at after five minutes, avoiding a database write on every authenticated request.
  • Authentication events are retained separately from inventory activity. LOGIN_SUCCESS, LOGIN_FAILURE, SESSION_CREATED, and SESSION_REVOKED include the known actor where available, plus safe request context. Passwords and session/CSRF tokens or hashes are never recorded.
  • Password recovery uses generic POST /api/v1/auth/forgot-password responses to prevent account enumeration. Opaque tokens are delivered only through the SMTP adapter, stored only as hashes, expire after one hour, are consumed once, and revoke existing sessions after a password reset.
  • API startup requires SMTP_URL, SMTP_FROM, and PASSWORD_RESET_URL; configure these local values before starting the server.
  • Existing admin routes validate the authenticated session and derive seller ownership server-side.
  • Request schemas live in packages/validation; admin response DTOs live in packages/contracts.
  • MFA enrollment, verification, disable, protected secret handling, login verification, and audit events are implemented.
  • Admin listing creation and lifecycle routes are implemented:
    • POST /api/v1/admin/listings
    • GET /api/v1/admin/listings
    • GET /api/v1/admin/listings/:id
    • POST /api/v1/admin/listings/:id/publish
    • POST /api/v1/admin/listings/:id/unpublish
    • POST /api/v1/admin/listings/:id/pending
    • POST /api/v1/admin/listings/:id/archive
  • Listing publication is seller-scoped, CSRF-protected, and gated by persisted identity, price, physical inventory, condition, and image prerequisites.
  • Listing publication rejects physical inventory in SOLD or ARCHIVED status with a conflict response.
  • Listing reads are seller-scoped and return only admin listing DTO fields.
  • Item-owned image metadata can be added through a seller-scoped, CSRF-protected admin route; publication uses the persisted image count.
  • Public search is available at GET /api/v1/public/search through a normalized SearchService boundary with pagination, price filters, text matching, and sold-item visibility controls.
  • Public search also supports active category and condition lookup options, URL-backed filters, and allowlisted recent/price sorting.
  • Public search responses include items plus pagination metadata (page, limit, total, and totalPages); pending listings are visible by default and sold listings require an explicit filter.
  • Public product pages are available at GET /api/v1/public/products/:slug; responses use a public DTO and require an active physical item with an available or pending listing.
  • Public listing collection and detail reads are available at GET /api/v1/public/listings and GET /api/v1/public/listings/:id; collection responses include pagination metadata and detail reads hide internal inventory identity.
  • Public listing detail responses include safe condition labels, latest market value, and public image URLs; unavailable image states are rendered accessibly in the gallery.
  • Public inventory collection and detail reads are available at GET /api/v1/public/inventory and GET /api/v1/public/inventory/:id; responses join only active physical items to available or pending listings and exclude private item notes.
  • The public seller directory is available at GET /api/v1/public/sellers; it returns only sellers with visible inventory and exposes display identity without user or contact fields.
  • The web workspace now has a Vue/Vite public catalog shell with Vue Router, a catalog API service, search composable, loading/error/empty states, and responsive search-first styling.
  • The web catalog now provides keyboard-accessible listing cards with stable /listings/:id links, a public listing detail page, and service-level detail loading/error handling.
  • Catalog search and pagination are synchronized to the browser URL (q and page) with accessible previous/next controls; the web port 5173 is declared in the project port registry.
  • The web catalog includes a stable /products/:slug product detail route with API-backed loading, not-found/error states, and semantic product facts.
  • The visual design foundation is centralized in web tokens with light/dark/system runtime theme support; the fixture composition has moved to Storybook at port 6006 with the accessibility addon and component stories for available, pending, and sold states.
  • ImageStorage, traversal-safe local storage, multipart upload transport, and binary signature validation are implemented; derivative processing remains.
  • Derivative orchestration now defines thumbnail, medium, and large WebP storage outputs behind an injected renderer; the concrete worker-backed renderer remains.
  • The worker owns an image derivative job runner with explicit processing, completion, and failure outcomes; queue transport and persistent job records remain.
  • Image metadata limits are enforced in the service boundary: supported MIME types, 10 MiB default file size, and 10,000-pixel maximum dimensions.
  • Acquisition, sales, dashboard, pricing observations, market valuations, audited sale voiding, and manual market-value overrides are implemented with seller-scoped API and admin UI workflows.

Verified Baseline

Suite Current coverage
Domain Product requirements and inventory lifecycle rules
Application Product slug allocation, condition compatibility, password verification/recovery, session issuance, activity renewal, management, and audit events
HTTP Product and inventory creation, validation, authentication/recovery, CSRF, rate limiting, and session management
Integration PostgreSQL catalog/session/audit/password recovery persistence and Redis-shared login throttling

Latest result: API, web, build, Storybook build, and Storybook interaction/accessibility checks pass locally. The isolated integration suite passes 20 tests, including acquisition, pricing, multi-item sales, audited sale voiding, and seller ownership persistence. The authenticated E2E workflow covers acquisition, image upload, pricing, listing publication, sale recording, dashboard loading, and audited sale voiding.

Next Milestone

Complete operational hardening around the implemented vertical slices.

  1. Add integration coverage for the latest acquisition, sales, pricing, and dashboard migrations.
  2. Document and verify CI execution of integration, E2E, and Storybook gates.
  3. Add provider adapters and asynchronous pricing refresh jobs behind the existing pricing boundary.

Acceptance criteria: public DTOs remain private-data-safe, migrations apply to clean test databases, and CI runs the same meaningful gates used for local vertical-slice validation.

Overall Roadmap

Work through one bounded vertical slice at a time. The linked documentation remains authoritative when this roadmap is less detailed.

  1. Authentication hardening.
  2. Listings and image prerequisites: keep Listing separate from Inventory Item; enforce publish requirements from REQUIREMENTS.md.
  3. Acquisition, pricing provider mappings, observations, valuations, and private/public DTO boundaries from DATA-MODEL.md and PRICING.md.
  4. Sales workflow, sold-item locking, corrections, cost basis, and audit history.
  5. SearchService, public catalog/search endpoints, filtering, pagination, and public product URLs.
  6. Image storage abstraction, upload validation, processing worker, responsive variants, and alt-text editing.
  7. Vue admin workflows and public catalog, following DESIGN.md, UX.md, and ACCESSIBILITY.md.
  8. Dashboard, bulk operations, exports, settings, workers, and provider adapters.
  9. CI, Docker deployment completion, backups, end-to-end/accessibility coverage, and operational documentation.

Known Gaps

  • No initial User/Seller is provisioned in the development database yet. Run npm run provision:admin after setting its required local .env variables.
  • Listings are partially implemented: draft creation, seller-scoped reads, publication gating, lifecycle writes, archive behavior, sold/archived inventory checks, and item-owned image metadata are complete; storage-backed processing remains.
  • Concrete provider adapters, asynchronous pricing refresh transport, richer exports/settings, full integration CI services, and deployment/backup automation remain incomplete to varying degrees.
  • The pricing refresh worker boundary now has explicit completed/failed outcomes and routes provider observations through the pricing service; persistent refresh-job storage and a configured external provider remain deployment work.
  • npm install currently reports dependency audit findings. Review and remediate them before production deployment; do not use npm audit fix --force without reviewing its changes.

Local Commands

npm install
npm run db:up
npm run db:migrate
npm run provision:admin
npm test

The Compose database requires a local .env created from .env.example. To prepare the test database once after bringing PostgreSQL up:

docker compose exec -T postgres sh -c 'createdb -U "$POSTGRES_USER" pokemon_inventory_test'

Then migrate it using its TEST_DATABASE_URL value as DATABASE_URL, and run integration tests with both TEST_DATABASE_URL and TEST_REDIS_URL set. See README.md for the environment-file prerequisites.

Suggested Resume Prompt

Read docs/IMPLEMENTATION-STATUS.md and continue the next bounded milestone. Verify the existing baseline first, follow AGENTS.md, and keep the change focused with tests.