Files
hongming-codex-laptop 47d24be523
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Waiting to run
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 3s
CI / Detect changes (pull_request) Successful in 6s
CI / Platform (Go) (pull_request) Successful in 4m9s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 9s
CI / Canvas (Next.js) (pull_request) Successful in 5m11s
CI / Python Lint & Test (pull_request) Successful in 6m37s
E2E API Smoke Test / detect-changes (pull_request) Successful in 5s
CI / all-required (pull_request) Successful in 4m50s
E2E Chat / detect-changes (pull_request) Successful in 5s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 5s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 3s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 3s
Harness Replays / detect-changes (pull_request) Successful in 4s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 3s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 7s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 5s
gate-check-v3 / gate-check (pull_request) Successful in 4s
qa-review / approved (pull_request) Successful in 3s
security-review / approved (pull_request) Failing after 3s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / all-items-acked (pull_request) Successful in 3s
sop-checklist / review-refire (pull_request) Has been skipped
sop-tier-check / tier-check (pull_request) Successful in 3s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 58s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 13s
Harness Replays / Harness Replays (pull_request) Successful in 3s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 8s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Failing after 2m11s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 2m10s
E2E Chat / E2E Chat (pull_request) Failing after 7m40s
audit-force-merge / audit (pull_request) Successful in 9s
feat(uploads): add /uploads/limits SSOT endpoint + Go-side convergence (task #320)
Eliminates the upload-cap drift class that produced mc#1588 (push-mode
bumped to 100MB) and mc#1589 (poll-mode + DB CHECK catch-up one day
later). The same five surfaces had to be hand-synced for every cap
change; this PR collapses the Go-side mirrors into a single source
(internal/uploads) and exposes that source via a public GET
/uploads/limits endpoint so the out-of-process consumers (canvas TS,
workspace Python push + poll) can converge in Phase 2 follow-ups.

Source:
  * internal/uploads/limits.go — UploadLimits struct +
    DefaultUploadLimits() (per_file_bytes=100MB, per_request_bytes=100MB,
    max_attachments_per_message=10). JSON-tagged shape is the stable
    wire contract.
  * Pinned by internal/uploads/limits_test.go — every cap change must
    update this test as part of the same PR (forces a reviewer to see
    the cap move and audit the matching DB migration + nginx config).

Endpoint:
  * GET /uploads/limits — public, no auth, mirrors /buildinfo
    rationale (platform constraint, not operational state; gating it
    would force pre-auth UX before learning the cap).
  * Cached in the binary via DefaultUploadLimits(); zero per-request
    DB round-trip.

Go consumer convergence:
  * pendinguploads.MaxFileBytes — now var derived from
    uploads.DefaultUploadLimits().PerFileBytes (int cast preserves the
    int-typed API surface so len() comparisons and make([]byte, N+1)
    sites keep working).
  * handlers.chatUploadMaxBytes — now var derived from
    uploads.DefaultUploadLimits().PerRequestBytes (int64 for
    http.MaxBytesReader).
  * chat_files.go line 631: int64(pendinguploads.MaxFileBytes)
    conversion for fh.Size (multipart.FileHeader.Size is int64).
  * chat_files_poll_test.go: matching int64 cast in the skip-guard
    that compares per-file vs body cap.

Tests:
  * internal/uploads/limits_test.go — pins values + JSON wire shape.
  * internal/router/uploads_limits_route_test.go — pins endpoint is
    public + 200 + payload matches DefaultUploadLimits + in-tree Go
    consumers agree with the SSOT.
  * Full workspace-server test suite green (go test ./... — all
    packages ok).

Boundaries (intentional non-changes):
  * Cap value stays at 100MB everywhere — no behavior change for any
    upload. The 25→100MB bump landed in mc#1588 + mc#1589; this PR is
    purely the SSOT refactor.
  * Migration's pending_uploads.size_bytes CHECK upper bound stays at
    104857600 — DB constraints can't read Go vars at runtime, so this
    constant lives in lockstep with DefaultUploadLimits and the
    migration's --comment notes the dependency. Bumping the cap is
    still a two-step coordinated dance (Go default + matching
    migration) but step 1 is now one line.
  * Canvas TS (MAX_UPLOAD_BYTES) + workspace Python
    (CHAT_UPLOAD_MAX_BYTES / MAX_FILE_BYTES) stay as their own
    pinned-100MB constants for this PR; the Phase 2 follow-up
    migrates them to fetch /uploads/limits at startup with a cache.
    The doc comments in chat_files.go point at this PR so reviewers
    of the Phase 2 PR can trace the SSOT lineage.

Why the canvas + Python migrations are NOT in this PR:
  Each consumer needs a different cache+retry shape (canvas at
  app-init in a browser, workspace Python at module-load in the
  container, python ingest similar but distinct). Bundling all of
  them into a single mega-PR fails the review-able-unit test and
  blocks CI for hours on a single conflict. The source-first
  sequencing (this PR) + per-consumer follow-up PRs ships faster
  through 2-eye review per CTO 2026-05-19 move-fast directive.
2026-05-20 03:15:10 -07:00
..