Skip to content

[codex] Store uploaded manifest file sizes#2265

Open
riderx wants to merge 1 commit into
mainfrom
codex/manifest-file-size-upload
Open

[codex] Store uploaded manifest file sizes#2265
riderx wants to merge 1 commit into
mainfrom
codex/manifest-file-size-upload

Conversation

@riderx
Copy link
Copy Markdown
Member

@riderx riderx commented May 13, 2026

Summary (AI generated)

  • Add a service-role-only uploaded_file_sizes table populated by backend upload/storage observation, not CLI manifest payloads.
  • Hydrate manifest.file_size from backend-observed upload records and ignore any client-provided manifest size values.
  • Keep the legacy R2 HEAD fallback for old or missing upload records, then cache the observed fallback size for future manifests.
  • Gate the manifest insert trigger so only rows matching a backend-observed size skip on_manifest_create; forged or missing sizes still queue verification.
  • Isolate queue-consumer per-message failures so one thrown message becomes one failed result instead of failing the whole batch.

Motivation (AI generated)

on_manifest_create was doing one follow-up R2 metadata lookup per manifest row. Large bundles can contain thousands of files, which creates queue/archive growth and risks worker limits. The fix moves size capture into backend-controlled upload handling while preserving the R2 fallback for legacy rows.

The trust boundary matters here: the CLI can send malformed or malicious manifest data, but manifest.file_size is now written from sizes observed by the backend upload/storage layer only.

The queue consumer should also keep draining successful messages when one message handler throws before returning an HTTP response. That failure now stays attached to the one message, while successful messages can still be deleted.

Business Impact (AI generated)

This reduces new on_manifest_create queue pressure for modern uploads, makes large delta bundles more reliable, and avoids turning manifest size into a client-trusted billing/storage signal. It also reduces queue blast radius when one message handler fails unexpectedly.

Test Plan (AI generated)

  • bun lint:backend
  • bun typecheck
  • bun run --cwd cli lint
  • bun run supabase:with-env -- bunx vitest run tests/queue-consumer-message-shape.unit.test.ts tests/manifest-uploaded-size.unit.test.ts tests/files-r2-error.test.ts
  • bun run supabase:db:reset
  • bash scripts/check-supabase-migration-order.sh
  • git diff --check
  • GitHub CI after force-push

Generated with AI

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 13, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR implements end-to-end manifest file-size tracking and improves queue resilience. Changes add a new uploaded_file_sizes table to persist backend-observed upload sizes, record completed TUS uploads across multiple handlers, hydrate manifest entries from observed sizes, type the CLI upload manifest, isolate queue message errors, and update tests for cache-first read behavior.

Changes

Manifest file_size tracking and queue resilience

Layer / File(s) Summary
CLI: typed partial upload manifest entries
cli/src/bundle/partial.ts
Introduce PartialUploadManifestEntry interface with file_name, s3_path, file_hash fields; type uploadPartial return as Promise<PartialUploadManifestEntry[] | null> and batch aggregation from any[] to typed array.
Type defs: uploaded_file_sizes table types
cli/src/types/supabase.types.ts, supabase/functions/_backend/utils/supabase.types.ts
Add Database["public"]["Tables"]["uploaded_file_sizes"] table type with Row, Insert, Update shapes for s3_path, file_size, app_id, owner_org, created_at, updated_at.
Files handlers: durable-object integration and response plumbing
supabase/functions/_backend/files/files.ts
Import upload-size helpers, record completed durable-object TUS responses via recordCompletedTusUpload, and return the stored durable-object response from the upload handler.
TUS proxy and recording: detect and record completed uploads
supabase/functions/_backend/files/supabaseTusProxy.ts
Parse uploadId from Supabase Location, copy Upload-Length into responses, read upstream progress (HEAD) or storage object size when needed, derive completed sizes, and safely call recordUploadedFileSize() on successful create/patch.
uploadHandler: conditional Upload-Length header
supabase/functions/_backend/files/uploadHandler.ts
Refactor TUS create/patch responses to build a Headers object and set Upload-Length only when uploadInfo.uploadLength is present.
Backend: uploadSize utilities
supabase/functions/_backend/files/uploadSize.ts
Add normalizeUploadedFileSize, recordUploadedFileSize to upsert into public.uploaded_file_sizes, and getCompletedTusUploadSize to derive completion from TUS Upload-Offset/Upload-Length headers.
Manifest trigger and version update: use observed sizes
supabase/functions/_backend/triggers/on_manifest_create.ts, supabase/functions/_backend/triggers/on_version_update.ts
on_manifest_create short-circuits when record.file_size is already positive and uses recordUploadedFileSize() to persist computed sizes; migration introduces a trigger function that queues on_manifest_create only when NEW.file_size is positive and matches public.uploaded_file_sizes. on_version_update adds getUploadedFileSizes() and hydrates inserted manifest rows' file_size from observed backend sizes (fallback 0), exporting test utilities.
Queue consumer: per-message error isolation
supabase/functions/_backend/triggers/queue_consumer.ts
Wrap per-message HTTP execution and CF-ID bulk update in try/catch to return synthetic 500 JSON responses on failures and continue cleanup instead of letting a rejection fail the whole batch.
Migration: uploaded_file_sizes table, trigger, and cleanup
supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql
Create public.uploaded_file_sizes with file_size > 0 constraint, enable RLS and restrictive policies, revoke broad privileges, grant service_role, add index on (owner_org, app_id), add trigger_verified_manifest_create() that only enqueues when NEW.file_size is positive and matches a uploaded_file_sizes row, replace the manifest trigger to call it, add cleanup_queue_messages() SECURITY DEFINER function with per-queue isolation, and harden process_all_cron_tasks() ownership/exec grants and cron schedule for cleaning.
Tests: cached reads and manifest size validation
tests/files-app-read-guard.unit.test.ts, tests/manifest-uploaded-size.unit.test.ts
Reorder a mock assertion in the cache-read test and add manifest-uploaded-size.unit.test.ts validating handleManifest hydrates manifest.file_size from public.uploaded_file_sizes (observed value or 0 fallback).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • Cap-go/capgo#2247: Both PRs modify cli/src/bundle/partial.ts's uploadPartial output/metadata generation (returned manifest entry file_name/s3_path plus related typing), so the changes are directly code-level related.

Poem

🐰 A rabbit hops through layers deep,

where upload sizes now we keep.
I count each chunk, then store it right,
Manifests shine with size in sight,
Queues breathe easy through the night.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.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 name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding storage and tracking of uploaded manifest file sizes from the backend.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed PR description includes required sections (Summary, Test plan, and Checklist items) with substantive content covering motivation, business impact, and testing approach.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/manifest-file-size-upload

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

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq Bot commented May 13, 2026

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing codex/manifest-file-size-upload (eff81cb) with main (7d53749)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Comment thread supabase/functions/_backend/triggers/queue_consumer.ts Fixed
@riderx riderx force-pushed the codex/manifest-file-size-upload branch 3 times, most recently from b0e3498 to e775849 Compare May 13, 2026 18:12
@riderx riderx marked this pull request as ready for review May 13, 2026 18:21
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@riderx riderx force-pushed the codex/manifest-file-size-upload branch from e775849 to 5555c5c Compare May 13, 2026 18:27
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (4)
tests/files-app-read-guard.unit.test.ts (1)

37-37: ⚡ Quick win

Convert these tests to it.concurrent(...).

Both test declarations should use concurrent execution per the test guideline for parallel test execution across files.

Suggested diff
-  it('serves cached app-scoped files without a database lookup', async () => {
+  it.concurrent('serves cached app-scoped files without a database lookup', async () => {
...
-  it('serves malformed cached paths without an app lookup', async () => {
+  it.concurrent('serves malformed cached paths without an app lookup', async () => {

Also applies to: 73-73

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/files-app-read-guard.unit.test.ts` at line 37, Change the two test
declarations that currently use it(...) to use it.concurrent(...); specifically
update the test with description "serves cached app-scoped files without a
database lookup" and the other test at the second occurrence (around the "Also
applies to" note) so both are declared as it.concurrent(...) instead of it(...),
leaving the test bodies and assertions untouched to enable concurrent execution.
tests/files-security.test.ts (1)

220-220: ⚡ Quick win

Use it.concurrent for this test case.

Switch this test declaration to it.concurrent(...) to align with the parallel-test requirement.

Suggested diff
-  it('serves uploaded attachments from storage after the app is deleted', async () => {
+  it.concurrent('serves uploaded attachments from storage after the app is deleted', async () => {

Per the coding guideline: tests/**/*.test.ts should use it.concurrent() instead of it() to enable parallel execution within the same file for faster CI/CD.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/files-security.test.ts` at line 220, The test declaration for "serves
uploaded attachments from storage after the app is deleted" uses it(...) but
must use parallel execution; replace the existing it('serves uploaded
attachments from storage after the app is deleted', async () => ...) with
it.concurrent('serves uploaded attachments from storage after the app is
deleted', async () => ...) so the test runs in parallel with other tests in the
file (no other code changes required).
supabase/functions/_backend/triggers/on_manifest_create.ts (1)

85-87: ⚡ Quick win

Dead code: this condition is now unreachable.

The early exit at lines 70-73 ensures that when execution reaches line 85, record.file_size is guaranteed to be null or <= 0. The check if (record.file_size && record.file_size > 0) will always be false here.

♻️ Proposed cleanup
   const { size, lastError } = await getManifestSizeWithRetry(c, record.s3_path)
   if (lastError) {
     cloudlogErr({ requestId: c.get('requestId'), message: 'getSize failed after retries', id: record.id, s3_path: record.s3_path, error: lastError })
   }
   if (size === 0) {
-    if (record.file_size && record.file_size > 0) {
-      cloudlog({ requestId: c.get('requestId'), message: 'getSize returned 0, keeping existing file_size', id: record.id, s3_path: record.s3_path, file_size: record.file_size })
-      return c.json(BRES)
-    }
     cloudlog({ requestId: c.get('requestId'), message: 'getSize returned 0 after retries, skipping update', id: record.id, s3_path: record.s3_path })
     return c.json(BRES)
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/functions/_backend/triggers/on_manifest_create.ts` around lines 85 -
87, The if-check "if (record.file_size && record.file_size > 0)" is unreachable
because earlier code guarantees record.file_size is null or <= 0 by the time
execution reaches this point; remove this dead branch (the cloudlog call and
return c.json(BRES)) from the function that handles the manifest trigger so
there’s no redundant logging/early return, and ensure any needed behavior is
preserved by the earlier exit already using c.json(BRES) and cloudlog where
appropriate (symbols: record, c, cloudlog, BRES).
supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql (1)

29-75: 💤 Low value

SonarCloud warning about newline is a false positive.

The warning flagging "illegal character with code point 10" (newline) in the SQL string literal at line 46 is a linter false positive. PostgreSQL accepts newlines in single-quoted string literals, and your dynamic SQL construction is correct:

  • Uses format() with %I for safe identifier quoting
  • Passes parameters via USING clause (not concatenated)
  • Has proper exception handling per queue
  • Limits archive deletes to 50k rows to avoid long locks

The function follows all security best practices (SECURITY DEFINER, empty search_path, fully qualified names). If you want to silence the linter, you could use dollar-quoting ($sql$...$sql$) instead of single quotes, but it's not necessary.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql` around
lines 29 - 75, SonarCloud's "illegal character code point 10" warning is a false
positive for the multi-line single-quoted SQL string inside function
cleanup_queue_messages; to silence the linter without changing behavior, switch
the EXECUTE format string to use dollar-quoting (e.g. $sql$...$sql$) instead of
single quotes so the multi-line literal contains no escaped newlines, leaving
the use of format(... %I ...) and USING archive_cutoff, archive_delete_limit
unchanged and preserving the exception handling and SECURITY DEFINER/search_path
setup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@supabase/functions/_backend/triggers/on_manifest_create.ts`:
- Around line 85-87: The if-check "if (record.file_size && record.file_size >
0)" is unreachable because earlier code guarantees record.file_size is null or
<= 0 by the time execution reaches this point; remove this dead branch (the
cloudlog call and return c.json(BRES)) from the function that handles the
manifest trigger so there’s no redundant logging/early return, and ensure any
needed behavior is preserved by the earlier exit already using c.json(BRES) and
cloudlog where appropriate (symbols: record, c, cloudlog, BRES).

In `@supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql`:
- Around line 29-75: SonarCloud's "illegal character code point 10" warning is a
false positive for the multi-line single-quoted SQL string inside function
cleanup_queue_messages; to silence the linter without changing behavior, switch
the EXECUTE format string to use dollar-quoting (e.g. $sql$...$sql$) instead of
single quotes so the multi-line literal contains no escaped newlines, leaving
the use of format(... %I ...) and USING archive_cutoff, archive_delete_limit
unchanged and preserving the exception handling and SECURITY DEFINER/search_path
setup.

In `@tests/files-app-read-guard.unit.test.ts`:
- Line 37: Change the two test declarations that currently use it(...) to use
it.concurrent(...); specifically update the test with description "serves cached
app-scoped files without a database lookup" and the other test at the second
occurrence (around the "Also applies to" note) so both are declared as
it.concurrent(...) instead of it(...), leaving the test bodies and assertions
untouched to enable concurrent execution.

In `@tests/files-security.test.ts`:
- Line 220: The test declaration for "serves uploaded attachments from storage
after the app is deleted" uses it(...) but must use parallel execution; replace
the existing it('serves uploaded attachments from storage after the app is
deleted', async () => ...) with it.concurrent('serves uploaded attachments from
storage after the app is deleted', async () => ...) so the test runs in parallel
with other tests in the file (no other code changes required).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3d13abc1-8560-4a80-a431-adae046075d5

📥 Commits

Reviewing files that changed from the base of the PR and between 1c0bc76 and e775849.

📒 Files selected for processing (11)
  • cli/src/bundle/partial.ts
  • cli/src/types/supabase.types.ts
  • supabase/functions/_backend/files/files.ts
  • supabase/functions/_backend/triggers/on_manifest_create.ts
  • supabase/functions/_backend/triggers/on_version_update.ts
  • supabase/functions/_backend/triggers/queue_consumer.ts
  • supabase/functions/_backend/utils/supabase.types.ts
  • supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql
  • tests/files-app-read-guard.unit.test.ts
  • tests/files-local-read-proxy.unit.test.ts
  • tests/files-security.test.ts

@digzrow-coder
Copy link
Copy Markdown

This looks like it turns manifest.file_size into a client-trusted value for new uploads.

The migration changes on_manifest_create to only enqueue the R2 HEAD fallback when NEW.file_size IS NULL OR NEW.file_size <= 0, and on_version_update then copies entry.file_size into the version manifest via normalizeManifestFileSize(entry.file_size). The new CLI sends the real finalBuffer.length, but that value is still supplied by the client/upload request path, not derived server-side from R2.

A modified CLI or direct API caller can set any small positive file_size for uploaded manifest rows. Because the trigger skips on_manifest_create, the server never re-HEADs the object and the bogus size is persisted into the app version manifest. Any downstream storage/bandwidth/accounting, quota, or audit code that trusts the manifest size can then be under-reported while the actual R2 object is larger.

I think the optimization needs a trust boundary: either keep the R2 HEAD verification for client-provided positive sizes, or only skip the trigger when the row is written by a trusted backend path that has independently measured the uploaded object size. At minimum, the DB trigger should not let arbitrary positive file_size bypass server-side verification.

@riderx riderx force-pushed the codex/manifest-file-size-upload branch from 5555c5c to f8ae012 Compare May 13, 2026 21:53
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@supabase/functions/_backend/files/supabaseTusProxy.ts`:
- Around line 347-349: The call to recordSupabaseCompletedTusUpload is on the
hot path and must not propagate errors; wrap the await
recordSupabaseCompletedTusUpload(c, uploadId, responseHeaders) in a try/catch
(for the occurrence around the response.status >= 200 check and the other
occurrence at lines ~384-386), catch any error and log it (including error
details and context such as uploadId and responseHeaders) instead of rethrowing
so a successful storage upload does not fail at the API boundary.
- Around line 265-267: In recordSupabaseCompletedTusUpload, don't unguardedly
cast c.get('fileId') to a string; instead read fileId into a local (e.g., const
fileId = c.get('fileId')), verify it's a non-empty string (typeof fileId ===
'string' && fileId.trim() !== '') and if not, log a clear warning/error and
return early (or throw a controlled error) so subsequent use of s3Path (formerly
s3Path = ...) is never executed with an invalid key; update any references to
use the validated local variable (fileId or s3Path) so callers bail safely.
- Around line 381-385: copyResponseHeaders is currently only copying
'Upload-Offset' and 'Upload-Expires' from the PATCH response, so
recordSupabaseCompletedTusUpload falls back to a HEAD call to determine
completion; modify the call site where copyResponseHeaders(response.headers,
responseHeaders, ['Upload-Offset', 'Upload-Expires']) is invoked to include
'Upload-Length' in the header list so responseHeaders contains Upload-Length
(i.e., use ['Upload-Offset','Upload-Expires','Upload-Length']), then ensure
readErrorBody and the subsequent check that calls
recordSupabaseCompletedTusUpload(c, uploadId, responseHeaders) will be able to
detect completion without issuing the extra HEAD request.

In `@supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql`:
- Around line 120-126: The conversion of process_all_cron_tasks() to SECURITY
DEFINER must also pin the search_path to an empty value and ensure
fully-qualified object names are used; update the function's properties to
include SET search_path = '' (or equivalent empty search_path setting) when
altering security, and audit the function body referenced by
process_all_cron_tasks to replace any unqualified table/schema references with
fully qualified names (schema.table) so definer execution cannot be hijacked by
path-based resolution.
- Around line 60-64: The trigger on_manifest_create currently skips the fallback
queue for any positive NEW.file_size, allowing forged sizes to bypass backend
revalidation; update the WHEN condition so the queue is only bypassed if
file_size is positive AND the write is explicitly marked as verified by a
trusted path (e.g., require NEW.size_verified_by_backend = TRUE or a similar
trusted flag/claim), otherwise ensure
public.trigger_http_queue_post_to_function('on_manifest_create') runs for
non-trusted writes; locate the CREATE TRIGGER on_manifest_create and modify the
WHEN clause to require both NEW.file_size > 0 and the trusted marker (or add a
boolean column like size_verified_by_backend and check it) so positive sizes
from untrusted sources still enqueue for server-side verification.

In `@tests/manifest-uploaded-size.unit.test.ts`:
- Line 42: Change the single test declaration in
tests/manifest-uploaded-size.unit.test.ts named "hydrates manifest file_size
from backend-observed upload rows only" from using it(...) to
it.concurrent(...); locate the it(...) call for that test and replace it with
it.concurrent(...) so the test runs in parallel with other tests in the same
file, preserving the existing test body and async behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d4ba0d8a-34b2-457b-8a62-50bec6d5f625

📥 Commits

Reviewing files that changed from the base of the PR and between 5555c5c and f8ae012.

📒 Files selected for processing (15)
  • cli/src/bundle/partial.ts
  • cli/src/types/supabase.types.ts
  • supabase/functions/_backend/files/files.ts
  • supabase/functions/_backend/files/supabaseTusProxy.ts
  • supabase/functions/_backend/files/uploadHandler.ts
  • supabase/functions/_backend/files/uploadSize.ts
  • supabase/functions/_backend/triggers/on_manifest_create.ts
  • supabase/functions/_backend/triggers/on_version_update.ts
  • supabase/functions/_backend/triggers/queue_consumer.ts
  • supabase/functions/_backend/utils/supabase.types.ts
  • supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql
  • tests/files-app-read-guard.unit.test.ts
  • tests/files-local-read-proxy.unit.test.ts
  • tests/files-security.test.ts
  • tests/manifest-uploaded-size.unit.test.ts
✅ Files skipped from review due to trivial changes (1)
  • supabase/functions/_backend/utils/supabase.types.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • tests/files-security.test.ts
  • supabase/functions/_backend/triggers/on_manifest_create.ts
  • supabase/functions/_backend/triggers/queue_consumer.ts
  • tests/files-local-read-proxy.unit.test.ts
  • tests/files-app-read-guard.unit.test.ts

Comment thread supabase/functions/_backend/files/supabaseTusProxy.ts
Comment thread supabase/functions/_backend/files/supabaseTusProxy.ts
Comment thread supabase/functions/_backend/files/supabaseTusProxy.ts Outdated
Comment thread supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql Outdated
Comment thread supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql Outdated
Comment thread tests/manifest-uploaded-size.unit.test.ts Outdated
@riderx riderx force-pushed the codex/manifest-file-size-upload branch 2 times, most recently from 9de40ff to e64dfcd Compare May 13, 2026 22:27
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql`:
- Around line 149-150: The DELETE against pgmq.q_||queue_name is unbounded and
should be chunked like the archive cleanup; replace the single EXECUTE
format('DELETE FROM %I.%I WHERE read_ct > 5', 'pgmq', 'q_' || queue_name) with a
looped/chunked DELETE (e.g. DELETE ... WHERE read_ct > 5 AND ctid IN (SELECT
ctid FROM %I.%I WHERE read_ct > 5 LIMIT <batch_size>)) executed via EXECUTE
format(...) until no rows are deleted, so the code repeatedly removes small
batches from the table (pgmq, 'q_' || queue_name) rather than one long
transaction.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a3dae449-905c-4b37-8fbd-0d0d2a4db672

📥 Commits

Reviewing files that changed from the base of the PR and between 9de40ff and e64dfcd.

📒 Files selected for processing (13)
  • cli/src/bundle/partial.ts
  • cli/src/types/supabase.types.ts
  • supabase/functions/_backend/files/files.ts
  • supabase/functions/_backend/files/supabaseTusProxy.ts
  • supabase/functions/_backend/files/uploadHandler.ts
  • supabase/functions/_backend/files/uploadSize.ts
  • supabase/functions/_backend/triggers/on_manifest_create.ts
  • supabase/functions/_backend/triggers/on_version_update.ts
  • supabase/functions/_backend/triggers/queue_consumer.ts
  • supabase/functions/_backend/utils/supabase.types.ts
  • supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql
  • tests/files-app-read-guard.unit.test.ts
  • tests/manifest-uploaded-size.unit.test.ts
✅ Files skipped from review due to trivial changes (1)
  • tests/files-app-read-guard.unit.test.ts
🚧 Files skipped from review as they are similar to previous changes (10)
  • cli/src/types/supabase.types.ts
  • supabase/functions/_backend/triggers/on_manifest_create.ts
  • supabase/functions/_backend/triggers/queue_consumer.ts
  • supabase/functions/_backend/files/uploadSize.ts
  • tests/manifest-uploaded-size.unit.test.ts
  • supabase/functions/_backend/utils/supabase.types.ts
  • supabase/functions/_backend/files/files.ts
  • supabase/functions/_backend/files/supabaseTusProxy.ts
  • supabase/functions/_backend/triggers/on_version_update.ts
  • cli/src/bundle/partial.ts

Comment thread supabase/migrations/20260513152858_fix_queue_archive_cleanup.sql Outdated
@riderx riderx force-pushed the codex/manifest-file-size-upload branch from e64dfcd to 5c1cee1 Compare May 13, 2026 22:41
@riderx
Copy link
Copy Markdown
Member Author

riderx commented May 13, 2026

Addressed in the latest commit. The CLI no longer sends file_size in manifest entries, and on_version_update ignores any client-provided size value. New manifest rows are hydrated only from public.uploaded_file_sizes, which is populated by backend upload/storage observation or by the legacy R2 HEAD fallback. The manifest trigger also only skips on_manifest_create when the inserted manifest.file_size matches that service-role-owned observed-size row; forged or missing values still enqueue the R2 verification path.

@nagiexplorer88
Copy link
Copy Markdown

One blocker remains in the forged/untrusted positive file_size path.

The migration now does the right first step: trigger_verified_manifest_create() only skips the queue when NEW.file_size matches public.uploaded_file_sizes; otherwise it enqueues on_manifest_create so the R2 HEAD fallback can verify it.

But the queued handler still exits immediately for any positive value:

if (record.file_size && record.file_size > 0) {
  return c.json(BRES)
}

So a manifest row inserted with file_size = 123 and no matching uploaded_file_sizes row follows this path:

  1. migration trigger enqueues it because the size is not backend-observed;
  2. on_manifest_create receives the row;
  3. updateManifestSize() returns before calling getManifestSizeWithRetry().

That means forged or stale positive sizes are still accepted instead of being revalidated, which contradicts the migration comment that “Legacy/missing or forged records still keep the fallback R2 HEAD.” The early return needs to distinguish verified backend-observed sizes from queued/unverified positive sizes, or the queued path should ignore the existing positive value and run the R2 size check before deciding whether to keep it.

@riderx riderx marked this pull request as draft May 14, 2026 00:01
@riderx riderx force-pushed the codex/manifest-file-size-upload branch from 5c1cee1 to 6489327 Compare May 14, 2026 00:02
@riderx riderx marked this pull request as ready for review May 14, 2026 00:33
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copy link
Copy Markdown

@digzrow-coder digzrow-coder left a comment

Choose a reason for hiding this comment

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

I think the trusted-size shortcut still needs to bind the observed upload row to the app/version being materialized, not only to the raw s3_path.

handleManifest() now hydrates file_size from uploaded_file_sizes with WHERE s3_path = ANY(...), and trigger_verified_manifest_create() skips the fallback queue when any observed row has the same s3_path/size. But the manifest being inserted is scoped by app_version_id while uploaded_file_sizes also records owner_org/app_id; neither path verifies that the observed row's app/owner matches the app_versions row currently being updated. Since app_versions.manifest is API-key/user writable through the existing update policies, a manifest payload can point at an already-observed path outside the current app and get a trusted non-zero size without revalidation for this version.

Please either reject manifest entries whose parsed scoped path does not match record.owner_org/record.app_id, or constrain the uploaded-size lookup/trigger check through the owning app_versions/apps scope. A regression should seed an uploaded_file_sizes row for app A, update an app B version with that s3_path, and assert the manifest insert is rejected or gets file_size = 0/queues verification instead of taking the trusted size.

@riderx riderx marked this pull request as draft May 14, 2026 09:34
@riderx riderx force-pushed the codex/manifest-file-size-upload branch from 6489327 to 6c08a6f Compare May 14, 2026 09:34
@riderx riderx force-pushed the codex/manifest-file-size-upload branch from 6c08a6f to eff81cb Compare May 14, 2026 10:26
@sonarqubecloud
Copy link
Copy Markdown

@riderx riderx marked this pull request as ready for review May 14, 2026 11:35
@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Copy link
Copy Markdown

@KCDaemon KCDaemon left a comment

Choose a reason for hiding this comment

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

Rechecked the latest head (eff81cb).

The trusted uploaded-size path still is not scoped tightly enough to the version/app being materialized. getUploadedFileSizes() loads rows only with WHERE s3_path = ANY(...), and trigger_verified_manifest_create() skips the fallback verification when any uploaded_file_sizes row has the same s3_path and file_size. Neither path checks that the observed row's owner_org / app_id matches the app_versions row receiving the manifest.

Because the manifest payload is still user/API-key writable and uploaded_file_sizes stores the owning scope separately, a version can point at an already observed path from another app/org and receive a trusted non-zero file_size without verification for the current app version. I would constrain the lookup and trigger through the owning app_versions/apps scope, or reject entries whose parsed scoped path does not match the record being updated.

The CI checks are green and the patch applies cleanly with whitespace checking, but I would keep this blocked until cross-app/org trusted-size reuse is covered by a regression test.

Sign up for free to 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.

5 participants