--- 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.