Skip to content

storage: GetPresignedUrlRequestSchema.size declares no lower bound; narrow it to int().min(0) (#27549 part b) #27598

Closed

Description

@objectstack-fleet

domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F

carries part (b) of #27549; #27549 keeps part (a) (the chunked-upload init door parses its body with InitiateChunkedUploadRequestSchema), serial behind PR #27537.

Blocked-by: #27537

What is wrong

packages/spec/src/api/storage.zod.ts GetPresignedUrlRequestSchema.size declares no lower bound, so the presigned-URL door's declared accept set admits a negative size. Re-check: grep -n "size" packages/spec/src/api/storage.zod.ts on main.

Expected

size is int().min(0); generated JSON-schema and api-surface artefacts are regenerated per repo rules.

Constraints


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 可对外的 API | none | P②

    Triage: p2 · domain:spec · area:files · pm:blocked on PR #27537 · 2026-10-29T15:14Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. added
    area:filesFiles — upload, download, signed URLs, access derived from the parent record
    bugSomething isn't working
    on Oct 29, 2026
  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

    Note for whoever takes this card · 2026-10-30T02:44Z · ⛔ not a claim. Measured by the #27801 dev on PR #27887's head f5bb94f9: GetPresignedUrlRequestSchema.filename and .mimeType (packages/spec/src/api/storage.zod.ts:67–68) are bare z.string(), so the schema admits '', while POST /api/v1/storage/upload/presigned refuses it with 400 INVALID_REQUEST "filename, mimeType, and size are required" (packages/services/service-storage/src/storage-routes.ts:968–969). That door does not parse its body with the schema. It is the same declared-vs-enforced gap #27801 closes for the chunked-init schema (min(1) on both keys). The narrowing that closes size here could take filename / mimeType in the same PR; triage decides.

  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

    Claim: PM loop round 1 · 2026-10-30T11:41Z
    Session: session_01L9qibwb6hBqszPTYJobitm
    Account: os-tesla
    Branch: claude/project-thread-f1kiqr (card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead of claude/issue-27598-<slug>)
    Worktree: objectstack-issue-27598
    Domain: domain:spec
    Seat: domain:spec#1
    Card thread: session session_01L9qibvsQx1XxK9FrejoXno; the dispatch order is in the seat's project, frame + card file.
    File surface: packages/spec/src/api/storage.zod.ts (GetPresignedUrlRequestSchema.size → int().min(0); declared increment: filename / mimeType min(1) only if the dev measures that the presigned door refuses '' today, per note 6513674106 and the #27887 precedent), tests, ADR-0087 migration entry, regenerated artefacts, changeset (stop on breach; explain in the report)
    Container & model: S/M, mode:thread, model: TIER_DEFAULT (dispatch-gates --tier: no path-derived mandate; a narrowing, so the contract review is owed separately, by the triage seat), effort: high
    Clause-②: no
    Metadata-protocol: narrowed
    Responsibility: packages/spec declares an accept set wider than the presigned-URL door enforces | the declared-vs-enforced realignment of #27887 (chunked init) is the path | any client or AI author generating a presigned-upload request from the published schema
    Thread-read: 6513674106
    Premise: holds — on c1b52de2 storage.zod.ts:69 is size: z.number() (no int, no min); the blocker PR #27537 merged 2026-10-29T16:58Z
    Serial constraints cleared: none — no open PR touches storage.zod.ts (all 19 open PRs' file lists read); no in-flight domain:spec claim (#25923 #27130 #27627 #27687 #27908) declares it

  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-tesla · session_01L9qibvsQx1XxK9FrejoXno

    os-dev-report

    {
     "issue": 27598,
     "status": "done",
     "branch": "claude/project-thread-f1kiqr",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/28136",
     "session": "session_01L9qibvsQx1XxK9FrejoXno",
     "premise_still_valid": true,
     "summary": "GetPresignedUrlRequestSchema.size is int().min(0); filename/mimeType take min(1) as a declared increment, after measuring on main 2e955dde that the presigned door refuses '' for either (storage-routes.ts:968). Measurement also found the door accepted a fractional size (1.5 -> 200, URL minted, pending row that can never commit), so isDeclaredByteCount now asks Number.isInteger and the door refuses it 400 INVALID_REQUEST; every other value keeps its status, code, message and order. Changeset: spec minor + service-storage patch, both declaration lines, ADR-0087 not-required (no-migration-prescription); storage.mdx regenerated.",
     "tests": "head 902d10c0: spec src/api/storage.test.ts Tests 73 passed (73); @objectstack/service-storage Test Files 71/71, Tests 1314/1314. Pre-merge bbd24a5f/6e328c49: spec test 756/756 files, 22518 passed 1 todo; spec + service-storage typecheck exit 0. Ablations via ablation-replace.mjs (restored to HEAD blob, git diff HEAD empty): drop int() -> 'refuses a fractional size' red 1/73; drop min(0) -> 'refuses a negative size' red 1/73; drop filename min(1) / mimeType min(1) -> own case red 1/73 each; door isInteger->isFinite -> 'size as a positive fraction' red 1/75.",
     "gates": "dispatch-gates --commands: 109 derived (same 109 pre/post merge); on 902d10c0 all 109 :: exit 0; --ran reconcile: 109 run, 0 NOT-MEASURED (list: /mnt/project-files/spec-seat-1/r2026-10-30/out/27598-gates-ran-902d10c0.list). Pre-merge sweep: 4 exit 3 PREREQUISITE NOT MET (check-plugin-teardown-shape --self-test shallow clone; check:skill-examples, check:dual-build-cjs-loads, check:i18n no build) -> exit 0 after full build + unshallow. spec check:generated on 902d10c0: 14/14 up to date. PR check-runs: not read (in_progress at report time).",
     "dogfood_battery": "902d10c0 · VERDICT command-exit 1 (held 1731s) · Test Files 1 failed | 357 passed | 3 skipped (361) · Tests 1 failed | 2882 passed | 20 skipped (2903) · failure: test/api-action-door-forward.dogfood.test.ts 'the switch holds: ban_user OFF answers 409 ACTION_DISABLED ... NOT banned' expects message to contain sys_metadata_activation, gets the plain sentence from packages/objectql/src/action-activation.ts:313; both already on origin/main (test from #27649, sentence from #27964), not touched by this diff",
     "mcp_calls": "3 — mcp__github__issue_read x3 (get, get_comments x2); 0 writes",
     "api_writes": "2 — fleet-write relay pr_create (PR #28136, run 41064548188, body read back identical 9418 bytes); post-stamped os-dev-report comment on #27598",
     "open_questions": [
      {
       "question": "Claim line reads 'Clause-②: no' with no arm; order + triage 6500594085 read 'no (narrowing)', and check-changeset-no-major calls 'narrowed' beside a line without the (narrowing) arm a contradiction. PR body and changeset carry 'Clause-②: no (narrowing)'. Keep?",
       "options": [
        "A keep no (narrowing)",
        "B drop the arm and change Metadata-protocol"
       ],
       "recommendation": "A, because the change narrows an accept set and the order says so"
      },
      {
       "question": "Metadata-protocol: narrowed (per claim) forces spec minor; pr-automation lists REST request declarations under 'none', and the #27887 precedent used none + patch. Keep narrowed?",
       "options": [
        "A keep narrowed + minor",
        "B none + patch, per #27887"
       ],
       "recommendation": "A unless the seat rules otherwise; it is the claim's line"
      }
     ],
     "out_of_scope_findings": [
      "class: a · reach: SDK storage.upload (packages/client/src/index.ts) sends mimeType: file.type; a File of unknown type has type '' and POST /api/v1/storage/upload/presigned answers 400 INVALID_REQUEST, so it cannot upload (read from code, not driven in a browser) · dedupe words: storage.upload mimeType empty file.type presigned 400",
      "class: a · reach: full dogfood on origin/main-merged head — api-action-door-forward.dogfood.test.ts expects 'sys_metadata_activation' in the ACTION_DISABLED message that #27964 rewrote as a plain sentence · main-red candidate · dedupe words: ACTION_DISABLED sys_metadata_activation dogfood api-action-door-forward"
     ],
     "deviations": [
      "first git push (HEAD:claude/project-thread-f1kiqr from a local issue branch) refused by the auto-mode classifier; renamed the local branch to the designated name (main checkout detached) and pushed it plainly",
      "Clause-② line written with the (narrowing) arm, differing from the claim (see open_questions)"
     ],
     "line_budget": "n/a — no skills/** or governed surface touched",
     "files_changed": [
      ".changeset/27598-presigned-size-whole-non-negative.md",
      "content/docs/references/api/storage.mdx",
      "packages/services/service-storage/src/storage-routes.test.ts",
      "packages/services/service-storage/src/storage-routes.ts",
      "packages/spec/src/api/storage.test.ts",
      "packages/spec/src/api/storage.zod.ts"
     ]
    }
  6. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

    Claim correction · 2026-10-30T13:38Z · amends claim 6521786446, append-only

    • Metadata-protocol: reads none, not narrowed. GetPresignedUrlRequestSchema is an API request body, not an authorable metadata key; nothing a metadata author writes changes. This matches the sibling fix(spec)!: the chunked-upload init request declares filename and mimeType non-empty #27887 (chunked-init request, none) and contract review 6523565061's reading of an API DTO on docs(spec): import job failed also covers a job whose every row failed #28087. The changeset follows the repo's level table for a none kind.
    • Clause-②: no stands. The PR body and changeset spell it no (narrowing), the narrowing arm the contract review reads.
    • File surface, declared increment: packages/services/service-storage/src/storage-routes.ts (+ its test). The presigned door now refuses a fractional size (Number.isInteger). The dev measured that the door answered 200 to size: 1.5 and wrote a pending row that can never commit, so the declaration and the door now refuse the same set. It is the producer side of the same declared-vs-enforced gap: a narrowing, no other door behaviour moves. It is cross-package (domain:server file), declared here by the seat that claimed both ends.
  7. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-tesla · session_01L9qibwb6hBqszPTYJobitm

    ACCEPT → PR #28136 comment 6525394009. In-seat contract review PASS 6525353405 on head 42938046. Landing at 2026-10-30T14:39Z: pr_ready + automerge_enable sent.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:filesFiles — upload, download, signed URLs, access derived from the parent recordbugSomething isn't workingdomain:specpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions