Record audit-redesign-scanner-flow-44 rollup and queue follow-up convoys from reviewer, design-system, and a11y fan-out on PRs #42–#44. Co-authored-by: Cursor <cursoragent@cursor.com>
5.7 KiB
| name | classification | success_metric | skip | status | created | depends_on | |
|---|---|---|---|---|---|---|---|
| redesign-scanner-flow | feature | Scanner page uses a stack-destination model (one target at top, every scan pours into it); per-card condition + foil + quantity captured and propagate to destinations; "you already own N" ownership badge via /api/cards/[id]/ownership; captured frame persisted to Vercel Blob and attached to user_cards. | closed | 2026-05-27 |
|
Convoy: redesign-scanner-flow
Redesign the scanner session around a single destination stack, rich card metadata, ownership feedback, and persisted scan images.
Why
The current scanner treats each card as an isolated add with no session context: users re-select destination every scan, cannot set condition/foil/ quantity in bulk, get no feedback when they already own a card, and lose the captured frame after confirm. The audit flagged these as core UX gaps that block efficient bulk scanning at the table.
Scope
In scope
-
Brief 1 — stack-destination UX + game pre-select
pages/scanner.js— session model: one chosen target at top; every confirmed scan routes there.components/ScannerDestinationPicker.js(new) — collection / deck / default collection picker with game filter.
-
Brief 2 — condition / foil / quantity + ownership badge
pages/scanner.js,components/ScannedCardItem.js(extract from scanner page).pages/api/user-cards.js,pages/api/decks/[id]/cards.js,pages/api/collections/[identifier]/cards.js— acceptcondition,is_foil,quantitybody params on POST.- Ownership badge via existing
pages/api/cards/[id]/ownership.js.
-
Brief 3 — captured image persistence
pages/api/scan/upload-image.js(new) — auth + Blob upload.components/CameraScanner.js— upload frame on confirm.pages/api/user-cards.js— acceptscan_image_url.
Out of scope
- OCR / identify pipeline changes — convoys #2 and #3.
- Vocabulary rename ("My Collection" / "Lists") — convoy
rename-collections-vocabulary(#5). - Schema cleanup — global
cards.quantityremoval isschema-cleanup-from-scanner-audit, not here.
Roles invoked
role-ia-architect— destination stack model, nav labels.role-ux-reviewer— bulk-scan flow, ownership badge placement.role-architect— API param contract, 3 briefs.role-implementer— 3 briefs (Briefs 2+3 parallel after Brief 1).role-reviewer+role-design-system-auditor+role-a11y-auditor.
Todos
- IA: stack-destination information architecture
- UX: condition/foil/quantity controls + ownership badge
- Architect: brief decomposition + API body-param contract
- Brief 1 — destination picker + session state (#42)
- Brief 2 — metadata + ownership (#43)
- Brief 3 — Blob persistence (#44)
- Post-PR audit —
audit-redesign-scanner-flow-44(see.convoys/redesign-scanner-flow/audit-redesign-scanner-flow-44.md)
Operator action required
None. Assumes BLOB_READ_WRITE_TOKEN is already provisioned (avatar
upload path uses Blob today).
Multitask dispatch
Slice dependencies
slice_dependencies:
- brief: 1
depends_on: []
files:
- pages/scanner.js
- components/ScannerDestinationPicker.js
- brief: 2
depends_on: [1]
files:
- pages/scanner.js
- components/ScannedCardItem.js
- pages/api/user-cards.js
- pages/api/decks/[id]/cards.js
- pages/api/collections/[identifier]/cards.js
- brief: 3
depends_on: [1]
files:
- pages/api/scan/upload-image.js
- components/CameraScanner.js
- pages/api/user-cards.js
After Brief 1 merges: /multitask role-implementer briefs 2, 3
(disjoint file sets except shared pages/scanner.js / user-cards.js —
architect must resolve: likely Brief 2 owns scanner.js queue UI, Brief 3
owns CameraScanner + upload route only; adjust files: if conflict).
Post-PR audit:
/multitask role-reviewer + role-design-system-auditor + role-a11y-auditor
Group id: audit-redesign-scanner-flow-<pr>.
Cross-convoy: can run parallel with #3 after #2 Brief 1+2 merge (disjoint primary surfaces).
CI impact
| Workflow / job | Behavior |
|---|---|
preview-smoke.yml |
Fires. |
visual-diff.yml |
Fires — pages/scanner.js, components/** match paths; !pages/api/** still allows page changes through. |
| New grep gates | None. |
Decisions to ratify (architect)
- Default destination — last-used vs explicit pick-required on session start.
- Condition enum — align with existing
user_cards.conditionVARCHAR values. - Blob path convention — e.g.
scans/{userId}/{uuid}.jpg. - Brief 2 vs 3 file overlap — split
pages/scanner.jsownership to avoid multitask conflict.
Known constraints
- Theme tokens — no hardcoded hex; use
var(--*)per ui-and-theming rule. - Rate limits — upload-image may need
checkUploadRateLimitif architect classifies scan images as upload class (Decision pending). - Mobile scanner — destination picker must work on narrow viewports.
Acceptance criteria
- User selects one destination; all scans in session target it until changed.
- Condition, foil, quantity propagate on add-to-collection/deck/user-cards.
- Ownership badge shows when user already holds the card.
- Confirmed scan image URL stored on
user_cardsrow. - Lint + vitest baseline preserved.
- A11y: destination picker keyboard-operable; badge has accessible text.
Out of scope follow-ups
god-component-split— further splitpages/scanner.jsif still > 500 lines.harden-multipart-parser— if upload-image uses multipart.