deckhearth/.convoys/scanner-desktop-layout/brief-5-batch-identify-helper.md
varutasu 938c161a26
feat(scanner): add desktop workstation layout (#165)
Give /scanner a md+ camera, live match inspector, and history strip
(with device picker, batch scan, and tips) without regressing the
mobile immersive checkout.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 17:21:23 -05:00

92 lines
3 KiB
Markdown

---
convoy: scanner-desktop-layout
brief_number: 5
depends_on: []
recommended_model: composer-2.5-fast
model_tier: fast
files:
- lib/scanner-batch-identify.js
- test/lib/scanner-batch-identify.test.js
cross_brief_commitments:
- brief: 6
description: |
Brief 6 calls `runSequentialGalleryIdentify(files, identifyFn, options)`
from the Batch Scan multi-file picker. On per-file failure Brief 6 enqueues
a queue row with `identifyFailed: true` and `identifyError` message; cancel
via `cancelRef.current = true` keeps completed rows (IA-locked).
---
# Brief 5: Sequential batch identify helper
## Goal (1 sentence)
Add a small library helper that runs `identifyFromGalleryFile` sequentially over multiple files with progress, cancel, and per-file failure continuation.
## Files in scope (do not edit anything else)
- `lib/scanner-batch-identify.js`
- `test/lib/scanner-batch-identify.test.js`
## Conventions to follow
- **No new API routes** — caller passes `identifyFn` (Brief 6 binds
`identification.identifyFromGalleryFile`).
- **Sequential only** — one `await identifyFn(file)` at a time; no `Promise.all`.
- Respect identify rate limits implicitly (sequential pacing).
- Pure JS module — no React.
## Implementation shape
```js
/**
* @typedef {{ current: number, total: number, file: File }} BatchProgress
*/
export async function runSequentialGalleryIdentify(files, identifyFn, options = {}) {
const {
onProgress,
onFileSuccess,
onFileError,
cancelRef = { current: false },
} = options;
const list = Array.from(files || []);
const total = list.length;
const results = [];
for (let i = 0; i < list.length; i++) {
if (cancelRef.current) break;
const file = list[i];
onProgress?.({ current: i + 1, total, file });
try {
await identifyFn(file);
results.push({ file, ok: true });
onFileSuccess?.({ file, index: i });
} catch (error) {
results.push({ file, ok: false, error });
onFileError?.({ file, index: i, error });
// continue — IA forbids stopping the batch
}
}
return { results, cancelled: cancelRef.current };
}
```
`identifyFn` may not throw today — helper still catches for forward-compat.
If `identifyFromGalleryFile` only reports via console, Brief 6 may wrap it to
throw on hard failures.
## Acceptance criteria
- [ ] Processes files one-at-a-time in order
- [ ] `onProgress` fires before each file with `{ current, total, file }`
- [ ] `cancelRef.current = true` stops remaining files but returns partial `results`
- [ ] Per-file errors invoke `onFileError` and continue loop
- [ ] tests cover success path, mid-batch cancel, and error continuation
- [ ] no scope expansion (do not edit files outside `files:` above)
## Rationale (≤3 sentences)
Batch Scan (C1) needs orchestration without touching `use-scanner-identification.js` identify logic. A 60-line pure helper is testable and keeps the page brief focused on UI wiring. Sequential execution avoids Gemini rate-limit storms explicitly forbidden in scope.