Six decisions ratified — five architect-self-ratifiable, one
(D1, scope expansion to gate the three import routes with auth +
admin-role + import rate-limit, plus the matching Bearer-token fix in
pages/admin/card-import.js) routed back for operator approval before
implementer dispatch.
Architecture: single brief, ~180 LOC across 10 files. Lib refactor
preserves Brief 4's checkAuthRateLimit(req) contract; adds four named
exports (search/upload/generate/import) with a Map<className, Ratelimit>
cache and distinct Redis prefix per class. Per-class limits tuned
against real client behavior — search bumped from 30 to 60/min after
finding ShareModal handleSearch has no debounce; generate bumped from 3
to 5/hour after confirming the generator uses DiceBear (free) not a
paid AI service.
Brief includes verbatim new lib/rate-limit.js shape so the implementer
has zero design discretion, plus per-route verbatim post-edit shapes
for all seven gated handlers and the admin-UI fetch fix.
Decision 1 investigation finding: pages/admin/card-import.js currently
fetches without an Authorization header. Without the matching fix in
this brief, adding getUserFromRequest to the import APIs immediately
breaks the admin UI on first run — the brief is internally consistent
only with both edits shipping together.
Decision 6 correction: the convoy file's claim that vitest transitively
loads lib/rate-limit.js is stale (rg verified no test imports it). The
lib refactor is strictly safer than the convoy file implied.
Co-authored-by: Cursor <cursoragent@cursor.com>
Scaffolds the add-rate-limiting convoy. fix-auth-bypass Brief 4
shipped lib/rate-limit.js with a single 5/15min auth-only limiter
wired into login + register; this convoy extends the surface to
search, upload, and (pending Decision 1) import routes.
Parent's pre-architect audit surfaced a critical secondary
finding beyond "missing rate limit": pages/api/cards/import-*.js
(3 files) have ZERO auth checks. They are publicly callable,
hit external APIs (Scryfall / Pokémon / Lorcana) with no caller
throttling, and perform unbounded DB writes. Rate-limit alone
won't close P0 #6 cleanly.
Architect Decision 1 routes the scope choice:
Option A — in-scope: add auth gates + rate limit to import
routes in this convoy. ~30 LOC across 3 files. Parent
recommends — precedent from drop-public-setup Brief 2 (mid-
convoy CJS/ESM expansion).
Option B — spin out: stay narrow on the 4 listed routes; queue
gate-import-routes follow-up. Mark P0 #6 RESOLVED-with-caveat.
Option C — rate-limit-only on imports: worst option (leaves
abusive anonymous endpoint live).
Six decisions queued (scope expansion, named-limiter shape,
per-class limits, identifier extraction, 429 response shape,
test coverage). No operator action required — Upstash env vars
already auto-provisioned by Brief 4.
If Option A wins, this convoy closes the last open P0 ship-
blocker (8/8 RESOLVED) and the launch checklist becomes empty.
Co-authored-by: Cursor <cursoragent@cursor.com>