Repository navigation
feat(uploads): keep every upload's original, rows, row errors and log, insert-only (M3b + M3c server, #107) - #138
Merged
Conversation
…y (M3b, #107) Migration 0007 adds per-tenant uploads, upload_blobs and source_rows. Each upload attempt records its filename, SHA-256, size, uploader, mode, outcome and counts, with the file's bytes and parsed rows. All three tables reject UPDATE, DELETE and TRUNCATE. Overwriting or deleting a migrated file no longer loses its original (the product owner moved retiring overwrite to M5, 28 Oct). New routes: GET /api/csv/uploads (list_files) and GET /api/csv/uploads/{id}/original (read_rows), which returns the exact bytes with an X-Content-SHA256 header. /api/csv/files now names each file's uploader. VCRM: BR-23 verified; BR-24 partial until the runtime role (M3d). CI replica 325/0/0, evals 26/26, lint and health clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YPoXzBSj89ZHgw7nfTmbB
…er (M3c backend, #107) The uploads row now stores why rows failed (capped at API_MAX_ROW_ERRORS) and the step log, inside the same insert-only row. New route GET /api/csv/uploads/{id} (list_files) returns them, so any session in the tenant can read them, not only the browser that uploaded the file. Product owner's scope addition on #107, 27 Oct. CI replica 326/0/0, evals 26/26, lint clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019YPoXzBSj89ZHgw7nfTmbB
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #107. Part M3b of ART Milestone 3, plus the server side of M3c. The frontend side of M3c follows in a separate PR. The parts and the product owner's 28 Oct decisions are on #107.
What it does
uploads: one row per upload attempt, with id, filename, SHA-256, byte size, uploader, mode, outcome and row counts. The outcome is one ofimported,partly_imported,failed,duplicateorrejected.csv_file_idnames the registry row the upload produced.upload_blobs: the file's bytes exactly as received.source_rows: the parsed rows as text, with row 0 the header.audit_login 0006. The shared guard function now names the table it stopped.uploadId.csv_file_idis a plain value, not a foreign key, so the original stays (decision 3: overwrite itself stays until M5).GET /api/csv/uploads(list_files) lists every upload attempt.GET /api/csv/uploads/{id}/original(read_rows) returns the exact bytes with anX-Content-SHA256header.GET /api/csv/filesnow includesuploaderandupload_id.uploadsalso storesrow_errors(capped atAPI_MAX_ROW_ERRORS) andlogs.GET /api/csv/uploads/{id}(list_files) returns them, so any session in the tenant can read why rows failed, not only the uploading browser.Known limitation
The loaders commit their own transaction, so the original is stored just after the load commits. That's the same window as the upload audit row. If storing it fails, the error is logged at ERROR, and the upload still reports its outcome with
uploadId: null. M6's single-transaction import (ADR-0002) closes this window.Verification
make lintmake healthtests/test_preserved_uploads.py(7 tests)/files; a failed upload keeps its original; partly-failed and duplicate outcomes; overwrite keeps the replaced original; UPDATE and DELETE rejected on all three tables; unknown id gives 404I broke the code three times to check that the tests catch it. Each time the expected test failed:
source_rowspartly_importedmapped toimportedAlso changed:
test_migrationsandtest_tenant_isolationnow expect head0007.docs/ROLES.mdlists the new routes, plus/api/audit/verifyfrom M3a, which I'd left out.Generated by Claude Code