---
name: scanner-correctness-polish
classification: infra-only
success_metric: |
  Mark-Owned is idempotent (in-flight lock per queue row); bulk toolbar no
  longer uses setTimeout(..., 100); pages/scanner.js imports lib/use-auth
  not legacy auth-context; POST /api/collections/[identifier]/cards calls
  logCollectionActivity('card_added', …).
skip:
  - ia
  - ux
  - visual
  - a11y
  - design
status: shipped
created: 2026-05-27
---

# Convoy: scanner-correctness-polish

**As-shipped:** PR #41 (2026-05-27). Idempotent Mark-Owned, bulk toolbar race fix, `lib/use-auth` import, activity logging.

Fix scanner-page correctness bugs without changing UX or visual design.

## Why

The scanner audit surfaced non-security bugs that cause duplicate adds,
racey bulk actions, a stale auth import, and missing activity logs. These
are small fixes with high reliability impact during bulk scanning sessions.

## Scope

### In scope

- **`pages/scanner.js`**
  - Line 8: change import from legacy `lib/auth-context` to `lib/use-auth`
    (should have been swept by `single-auth-provider`; scanner was missed or
    regressed).
  - **Mark-Owned idempotency** — in-flight lock per queue row so double-tap
    / double-click cannot duplicate POSTs.
  - **Bulk toolbar** — remove `setTimeout(..., 100)` race; await or use
    proper batch completion signal.
- **`pages/api/collections/[identifier]/cards.js`** — POST handler calls
  `logCollectionActivity(collectionId, userId, 'card_added', details)` per
  AGENTS.md convention.

### Out of scope

- **Scanner UX redesign** — convoy `redesign-scanner-flow` (#4).
- **Copy / vocabulary** — convoy `rename-collections-vocabulary` (#5).
- **OCR / identify pipeline** — convoys #2–#3.
- **Visual or a11y changes** — none intended; diff should be behavior-only.

## Roles invoked

1. `role-architect` — single brief (lightweight; may be parent-owned given
   infra-only classification).
2. `role-implementer` — one brief.
3. `role-reviewer` — post-PR only (design + a11y skipped).

## Todos

- [ ] Architect: brief-1 with exact line targets
- [ ] Fix use-auth import on scanner page
- [ ] Add per-row in-flight lock for Mark-Owned
- [ ] Replace setTimeout bulk-toolbar pattern
- [ ] Wire logCollectionActivity on collection card POST

## Operator action required

**None.**

## Multitask dispatch

Single brief — no implementer fan-out.

**Cross-convoy parallelism:** after #1 merges, run alongside **#2 Brief 1**
and **#5 Brief 1** — disjoint files (`pages/scanner.js` vs migrations vs
copy sweep). Coordinate if both #6 and #5 touch `pages/scanner.js` (IA
should sequence copy sweep after correctness or split files in briefs).

Post-PR audit:

```
/multitask role-reviewer
```

Group id: `audit-scanner-correctness-polish-<pr>`.

## CI impact

| Workflow / job | Behavior |
| --- | --- |
| `ci.yml` | Standard lint + vitest; no new gates. |
| `preview-smoke.yml` | Fires. |
| `visual-diff.yml` | **May fire** if `pages/scanner.js` changes — behavior-only diff should not move pixels; baseline swallow if triggered. |

## Known constraints

- **`logCollectionActivity` import** — use existing helper from
  permission-middleware or documented activity module; match sibling handlers.
- **No UX copy changes** — button labels stay as-is until #5 (or avoid
  overlapping scanner.js edits between #5 and #6).

## Acceptance criteria

1. `pages/scanner.js` imports from `lib/use-auth` only.
2. Double-click Mark-Owned produces one POST.
3. Bulk toolbar actions complete without setTimeout race.
4. Collection card POST emits activity log row.
5. Vitest 21/21; lint baseline preserved.
