The marking flow read student_submissions but there was no way to create them —
print→scan→mark could not be completed in-app. Add an "Upload scans" control to
the marking queue that posts PDFs to the existing POST /batches/{id}/scans and
refreshes the queue.
- examRepository.uploadScan(batchId, file, {studentId?, matchingMethod?}): multipart
upload (axios sets the boundary), returns the created/updated StudentSubmission.
- ExamMarkingPage: "Upload scans" button + hidden multi-file PDF input; uploads each
with matching_method='ordered' (fills the roster in order; unmatched scans become
reconcilable submissions), then reloads the queue with a success/failure summary.
No API or schema change (endpoint already existed). tsc clean; behaviour is covered
by the cc-browser-tester card (FX-V).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>