Skip to content

feat(upload): reject over-limit files in the browser before transferring (FV-04) - #1064

Merged
BigSimmo merged 3 commits into
mainfrom
claude/upload-size-precheck-123366
Jul 22, 2026
Merged

feat(upload): reject over-limit files in the browser before transferring (FV-04)#1064
BigSimmo merged 3 commits into
mainfrom
claude/upload-size-precheck-123366

Conversation

@BigSimmo

@BigSimmoBigSimmo commented Jul 22, 2026

Copy link
Copy Markdown
Owner

Summary

Rejects an over-limit upload in the browser instead of transferring it in full and waiting for the server's 413 (finding FV-04), and states the limit on the file field for the first time.

Previously a 300 MB PDF was uploaded byte-for-byte before assertAllowedFile answered 413 payload_too_large — on a clinic connection that is a long wait for a guaranteed rejection, and nothing in the UI said what the limit was.

Why the pre-check can't reject something the server would have accepted. The server's effective limit is env.MAX_UPLOAD_MB, whose schema now derives both its .max() and .default() from the new MAX_UPLOAD_MB_CEILING. An operator can configure a lower limit, never a higher one, so a client check at the ceiling is a strict superset of the server's — anything under it is still sent and the server remains the authority.

  • New src/lib/upload-limits.ts — dependency-free so a client component can import it. src/lib/http.ts pulls in next/server and src/lib/env.ts is server-only, so neither can cross into the bundle; this is the one thing both sides may share.
  • UploadPanel skips an over-ceiling file using the server's own wording (shared message builder, so the two phrasings can't drift) and still uploads the rest of the batch, matching the server's per-file outcome semantics rather than failing the whole selection.
  • Surfaced limit — "PDF only, up to 150 MB per file", wired via aria-describedby so it is announced to assistive tech, not just visible next to the input.
  • src/lib/http.ts keeps the authoritative check unchanged; only the message string moved to the shared builder.

Verification

  • tests/upload-size-precheck.dom.test.tsx4/4 pass (new). Mutation-checked: with exceedsUploadSizeCeiling stubbed to false, the two behavioural tests go red and the two structural ones correctly stay green — they are not vacuous.
  • Full jsdom project — 34 files / 136 tests pass
  • upload-size-contract, upload-ingress-limits, upload-outcome, api-validation-contract, bounded-json-body, private-access-routes, document-admin-rate-limit, clinical-dashboard-helpers167 tests pass
  • npm run typecheck, eslint (changed files), prettier --write
  • npm run check:design-system-contract (9 / 1 / 28 — unchanged), npm run check:type-scale --strict
  • UI verification not run: the only rendered change is one static hint paragraph in the admin upload panel; the interaction is covered by the jsdom tests above and the full unit + jsdom suite runs in CI.

Pin updated, not weakened.tests/upload-ingress-limits.test.ts asserted the literal …max(150).default(150) source text to keep the 151mb proxy envelope tied to the upload cap. It now asserts MAX_UPLOAD_MB_CEILING === 150and that env.ts derives from the constant — strictly stronger, since a second literal can no longer appear.

Risk and rollout

  • Risk: low. The pre-check is a superset guard that cannot produce a false rejection (argued above); the server-side check is byte-for-byte unchanged.
  • Rollback: revert this commit — the server limit is untouched, so nothing depends on the client behaviour.
  • Provider or production effects: none. Verification was fully offline; no provider calls.

Clinical Governance Preflight

(Touches the admin upload panel and one production env bound; no clinical output, retrieval, or document-access behaviour changes.)

  • Source-backed claims still require linked source verification before clinical use
  • No patient-identifiable document workflow was introduced or expanded without explicit governance approval
  • Supabase target remains Clinical KB Database (sjrfecxgysukkwxsowpy)
  • Service-role keys and private document access remain server-only — the new shared module is a numeric constant plus two pure helpers, no config or secret crosses into the client
  • Demo/synthetic content remains clearly separated from real clinical sources — the demo read-only branch still short-circuits before the pre-check
  • Source metadata, review status, and outdated/unknown-source behavior remain conservative
  • Deployment classification/TGA SaMD impact was checked — upload ergonomics only, no clinical decision-support behaviour change

RAG impact: no retrieval behaviour change — upload-size pre-check and one env bound; no retrieval, ranking, or answer-generation surface is touched.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a consistent per-file upload size limit across browser and API uploads.
    • Upload guidance now clearly displays the maximum allowed file size.
    • Oversized files are rejected before transfer, while valid files in the same batch continue uploading.
    • Error responses now use consistent messaging for files exceeding the limit.
  • Bug Fixes

    • Prevented oversized files from triggering unnecessary network requests.
  • Tests

    • Added coverage for size-limit messaging, boundary values, mixed batches, and pre-upload rejection.

…ing (FV-04)
An over-limit guideline PDF was transferred in full before the server
answered 413 — on a large file over a clinic connection that is a long
wait for a guaranteed rejection, with the limit itself never stated
anywhere in the UI.
- New dependency-free `src/lib/upload-limits.ts` holds the ceiling so a
client component can import it; `src/lib/http.ts` (next/server) and
`src/lib/env.ts` (server-only secrets) both can't cross into the bundle.
- `env.MAX_UPLOAD_MB` now derives its cap and default from that constant,
so the configured limit can only ever be *lower* than what the client
rejects up front — the pre-check can never refuse a file the server
would have accepted, and the server stays the authority.
- `UploadPanel` skips an over-ceiling file with the server's own 413
wording (shared message builder, so the two can't diverge) and still
uploads the rest of the batch, matching per-file outcome semantics.
- The file field states "PDF only, up to 150 MB per file", wired via
aria-describedby so it is announced, not just visible.
The ingress pin in tests/upload-ingress-limits.test.ts now asserts the
constant's value plus env's derivation from it, keeping the 151mb proxy
envelope tied to the same number.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@supabase

supabaseBot commented Jul 22, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project sjrfecxgysukkwxsowpy because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@coderabbitai

coderabbitaiBot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: daf0476c-579e-4bf9-aad8-1125c0b33f9d

📥 Commits

Reviewing files that changed from the base of the PR and between 9273fbe and e64dbc0.

📒 Files selected for processing (6)
  • src/components/clinical-dashboard/DocumentManagerPanel.tsx
  • src/lib/env.ts
  • src/lib/http.ts
  • src/lib/upload-limits.ts
  • tests/upload-ingress-limits.test.ts
  • tests/upload-size-precheck.dom.test.tsx

📝 Walkthrough

Walkthrough

The change centralizes the 150 MB upload ceiling, aligns environment and API validation with it, and adds client-side pre-checks that reject oversized files before transfer while preserving mixed-batch uploads. The file input now exposes the limit through accessible helper text.

Changes

Upload size limit alignment

Layer / File(s)Summary
Shared upload limit utilities
src/lib/upload-limits.ts, tests/upload-ingress-limits.test.ts
Defines the shared ceiling, byte-size check, and standardized error message, with tests verifying the ceiling-backed environment contract.
Server limit configuration and errors
src/lib/env.ts, src/lib/http.ts
Uses the shared ceiling for MAX_UPLOAD_MB validation and the shared message for HTTP 413 upload errors.
Client upload pre-check and accessibility
src/components/clinical-dashboard/DocumentManagerPanel.tsx, tests/upload-size-precheck.dom.test.tsx
Links the file input to a size hint, rejects oversized files before requests, and tests boundary and mixed-batch behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers:claude

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change: client-side rejection of over-limit uploads before transfer.
Description check✅ PassedThe PR description includes the required Summary, Verification, Risk and rollout, and Clinical Governance Preflight sections with concrete details.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/upload-size-precheck-123366

Comment @coderabbitai help to get the list of available commands.

@BigSimmo
BigSimmo enabled auto-merge (squash) July 22, 2026 03:42

@chatgpt-codex-connectorchatgpt-codex-connectorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit:e64dbc08d5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment threadsrc/components/clinical-dashboard/DocumentManagerPanel.tsx
@BigSimmo

Copy link
Copy Markdown
OwnerAuthor

@codex resolve actionable Codex review findings for this pull request and current head using the repository instructions. This is the pull request's single automatic repair pass: do not perform a fresh review, create new standalone findings, or request another review. Work only the existing unresolved Codex threads on the current head. Always fix P0 and P1 findings. For P2 and lower findings, fix only clear, scoped, low-risk issues; otherwise disposition them with a concise reason. After fixing or dispositioning a thread, reply in that thread with as the first line, followed by a concise summary; that marker authorizes the workflow to close that exact thread. If human input or new authorization is required, do not use the marker and leave the thread open with the blocker. Finish only after every actionable thread is fixed or dispositioned and closed, or explicitly left open for a human decision. Do not update the branch from main, address unrelated reviews, broaden scope, or create more than one scoped fix commit. Do not use external APIs, paid services, credentials, dependency changes, or broad refactors unless explicitly authorized. Add targeted tests where behavior changes and run the narrowest relevant validation.

@chatgpt-codex-connector

Copy link
Copy Markdown

Summary

Testing

  • npm run workflow:flightplan -- --write-evidence --files src/components/clinical-dashboard/DocumentManagerPanel.tsx,src/lib/upload-limits.ts,src/lib/http.ts,src/lib/env.ts,tests/upload-size-precheck.dom.test.tsx,tests/upload-ingress-limits.test.ts
  • git diff --check
  • ⚠️npm run test -- --run tests/upload-size-precheck.dom.test.tsx tests/setup-status-route.test.ts — blocked because repository dependencies were not installed (node_modules/vitest/vitest.mjs missing).
  • ⚠️npm run typecheck — blocked because repository dependencies were not installed (node_modules/typescript/bin/tsc missing).
  • ⚠️npm ci — blocked by the local runtime mismatch: repo requires Node 24.x / npm 11.x, but the environment has Node v20.20.2 / npm 11.4.2.

View task →

@BigSimmo
BigSimmo merged commit ee0c018 into mainJul 22, 2026
17 checks passed
@BigSimmo
BigSimmo deleted the claude/upload-size-precheck-123366 branch July 22, 2026 05:56
BigSimmo added a commit that referenced this pull request Jul 22, 2026
#1064 was squash-merged, so this branch's own FV-04 commits are not ancestors
of main even though their content is. That made every FV-04 file an add/add or
content conflict against main's squashed copy.
Verified before resolving that main's version of all six FV-04 files is
byte-identical to the state merged as #1064 (git diff d51e2fb origin/main
was empty for each), so main contributed no new content to them and the branch
side is the correct resolution. The three resolved files are byte-identical to
the pre-merge branch tip c023b92, leaving only the NEXT_PUBLIC_MAX_UPLOAD_MB
work as this PR's delta.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@BigSimmo