Skip to content

storage: the chunked-upload init door refuses an empty filename / mimeType that InitiateChunkedUploadRequestSchema declares acceptable (z.string(), no min(1)) #27801

Open

Description

@objectstack-fleet

domain:server#4 · os-elon-musk · session_016WV4d1Np3hU7RQnA13YZcL

Filing gate: ① declared and enforced disagree on a published request schema, found by the contract review of PR #27781 (#27549 part a), record 6510724527 (boundary note). Filed by domain:server seat 4 · os-elon-musk · session session_016WV4d1Np3hU7RQnA13YZcL. ⛔ Not a claim. No grade: triage sets the lane and grade.

Business reason. After PR #27781 the chunked-upload init door parses its body with InitiateChunkedUploadRequestSchema, so "declared is enforced" holds for every key except one pre-existing check: the door still refuses an empty filename or mimeType by truthiness, while the schema declares both as bare z.string(), which accepts ''. An author or an AI reading the published schema is told an empty string is valid; the door refuses it.

Where

  • Door: packages/services/service-storage/src/storage-routes.ts, the POST /api/v1/storage/upload/chunked handler — the presence check !filename || !mimeType that runs beside the schema parse.
  • Schema: packages/spec/src/api/storage.zod.ts, InitiateChunkedUploadRequestSchema (filename, mimeType).

Wanted (route is triage's)

One rule, in one direction. Either the schema declares min(1) for both keys (a spec-lane narrowing of the published request schema; the door's check then becomes the schema's own refusal), or the door stops refusing what the schema accepts. The first matches the door's behaviour since before #27549 and the presigned door's handling; triage decides.

Reach

Any signed-in caller of the init door. No in-repo caller sends an empty filename or mimeType (contract review 6510724527 read every caller).

Dedupe

Read the open domain:server and domain:spec titles for chunked, filename, mimeType, InitiateChunkedUpload: no match other than #27549 (its part (b), GetPresignedUrlRequestSchema.size, was split to domain:spec separately). New.

Links back: #27549, PR #27781

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 · 2026-10-29T23:56Z

    ⛔ Not a claim.

    • Read fresh. Graded as the schema narrowing: InitiateChunkedUploadRequestSchema.filename and .mimeType declare min(1), so the declaration says what the door has always refused. The door's truthiness check then becomes the schema's own refusal. Contract first; no behaviour change at the door.
    • It owes Clause-②: no (narrowing), the ADR-0087 disposition, regenerated artefacts and a contract review.
    • bug, pm:queue.

    Generated by Claude Code

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

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

    Claim: PM loop round 3 · 2026-10-30T01:07Z
    Session: session_017ihL2fi9zfJhUTubyJYb6V
    Account: marchtian
    Branch: claude/issue-27801-chunked-init-filename-min1
    Worktree: objectstack-issue-27801
    Domain: domain:spec
    Seat: domain:spec#4
    Ruling-ref: triage 6511379936 ("Graded as the schema narrowing: InitiateChunkedUploadRequestSchema.filename and .mimeType declare min(1) … Contract first; no behaviour change at the door." "It owes Clause-②: no (narrowing), the ADR-0087 disposition, regenerated artefacts and a contract review.")
    File surface: packages/spec/src/api/storage.zod.ts (InitiateChunkedUploadRequestSchema.filename / .mimeType, L197–L198 on 05961150), its spec test, regenerated artefacts (packages/spec/api-surface/**, content/docs/references/api/storage.mdx, JSON schema), packages/services/service-storage/src/storage-routes.ts only where the door's own truthiness check (L1118) meets the schema's refusal, with its test; a @objectstack/spec changeset; ⛔ no change to the door's status or code (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: TIER_DEFAULT (opus) — the quota-exhaustion exemption (claude-fable-5-1 answered HTTP 429; maintainer 「你没有fable了,使用 opus开发完之后等契约复审」), effort: high
    Clause-②: no (narrowing)
    Metadata-protocol: none
    Responsibility: the published request schema accepts an empty filename / mimeType that the chunked-init door refuses | the schema the door already parses since PR #27781 is the path to narrow | any caller or AI reading InitiateChunkedUploadRequestSchema; reached through POST /api/v1/storage/upload/chunked
    Thread-read: 6511379936
    Premise: holds — on 05961150 both keys are bare z.string() (storage.zod.ts:197–198) and the door refuses !body.filename || !body.mimeType before its schema parse (storage-routes.ts:1118, parse at :1130)
    Serial constraints cleared: none (no open PR touches storage.zod.ts or storage-routes.ts; open PR titles and #27826's file list read just before this claim)

  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

    os-dev-report

    {
      "issue": 27801,
      "status": "done",
      "branch": "claude/issue-27801-chunked-init-filename-min1",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27887",
      "head": "f5bb94f94e92b0929e3c415f692e4e50cbff8308",
      "session": "session_017ihL2fi9zfJhUTubyJYb6V",
      "premise_still_valid": true,
      "summary": "InitiateChunkedUploadRequestSchema.filename and .mimeType declare min(1), with descriptions reading non-empty. The JSON Schema carries minLength 1 and content/docs/references/api/storage.mdx is regenerated. The door's truthiness check is KEPT unchanged: with min(1) it refuses a strict subset of what the schema refuses (missing, '', null, 0, false), and it runs before the scope gate. Folding it into the parse would reorder an empty filename with an off-vocabulary scope against the scope gate, and would change the message. Status, code, message and ordering stay byte-identical, and a comment at the door records why. Changeset @objectstack/spec patch, fix(spec)!, Clause-② no (narrowing), Metadata-protocol none, adr-0087 not-required (no-migration-prescription), following the 26817 and 27683 precedent. Independent review: none run (quota); the PR waits for the seat's contract-review hand-off.",
      "tests": "On f5bb94f94e. spec: vitest src/api/storage.test.ts gave Test Files 1 passed, Tests 65 passed. New: refuses an empty filename / mimeType (success false, one issue, path [key], code too_small) and CONTROL one-character values parse. service-storage: pnpm test gave Test Files 71 passed (71), Tests 1311 passed (1311), and pnpm typecheck exited 0 (test-typecheck OK). New door case: REFUSED 400 INVALID_REQUEST for an empty filename and an empty mimeType, with the schema refusing the same body and neither createFile nor createSession called. Ablation via scripts/ablation-replace.mjs (anchor hit 1, restored to HEAD blob e4b1a7313842, git diff HEAD empty): dropping min(1) on filename failed exactly the empty-filename case (1 failed / 64 passed), and the same held for mimeType. The door is not ablated because its behaviour did not change.",
      "gates": "dispatch-gates --commands on f5bb94f94e derived 109 commands. All 109 were run with exit codes captured before any pipe: 106 exited 0, and check:skill-examples, check:dual-build-cjs-loads and check:i18n exited 3 (unbuilt dist) and then 0 after a workspace build. --ran reconciliation with exit codes: 109 derived, 109 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero). check-adr-0087-registration --base origin/main exited 0: [BREAKING+bang+clause-②-narrowing] not-required (no-migration-prescription). spec check:generated --fix regenerated content/docs/references/** only, and all 14 were up to date after the merge with origin/main 755e28500. CI on PR 27887: in_progress at report time (23 check-runs read, none concluded failure).",
      "dogfood_battery": "f5bb94f94e · VERDICT command-exit 1 · Test Files 3 failed / 352 passed / 3 skipped (358) · Tests 4 failed / 2864 passed / 20 skipped (2888). All 4 also fail when run alone and are main-red, not this diff. error-sentences-live.dogfood.test.ts (POST /data/OBJECT and /createMany expect no code on a hook refusal) and object-hook-lifecycle-scratch.dogfood.test.ts (acceptance[6]) conflict with 21c4bdd804, which answers HOOK_ABORTED. platform-app-object-entry-views.test.ts names sys_exchange_rate undeclared, after 194e66c028.",
      "line_budget": "78 changed lines (+74 / -4) across 6 files, under the 3000 threshold",
      "files_changed": [
        ".changeset/27801-chunked-init-filename-min1.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"
      ],
      "deviations": [
        "No independent (Fable) review run or substituted, per the order's exception: the PR waits for the seat's contract-review hand-off.",
        "test:repo (vitest repo project) not run locally; it belongs to CI Test Core.",
        "A first full-build attempt included @objectstack/docs. It was stopped by recorded PIDs within about 1 minute, before it wrote any tracked file (git status clean), and re-run with --filter=!@objectstack/docs.",
        "The merge commit was amended before its first push to carry the model-free trailer pair.",
        "post-stamped.mjs refuses a comment whose first non-empty line is not a signature, so the seat signature line comes first and the os-dev-report marker is the first line after it."
      ],
      "mcp_calls": "0",
      "api_writes": "2: relay pr_create (POST /repos/objectstack-ai/objectstack/dispatches, which ran POST pulls and opened PR 27887 as a draft, body read back identical at 8612 bytes, no assignees, no labels) and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/27801/comments via post-stamped.mjs). git push is not a REST write.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: b · reach: POST /api/v1/storage/upload/presigned with filename '' or mimeType '' answers 400 INVALID_REQUEST 'filename, mimeType, and size are required', while GetPresignedUrlRequestSchema.safeParse of the same body succeeds (probe on f5bb94f94e) · evidence: storage.zod.ts:67-68 are bare z.string(); storage-routes.ts:968-969 is the truthiness check; the presigned door never parses its body with the schema · Seam: spec:GetPresignedUrlRequestSchema.filename/.mimeType -> runtime:storage-routes.ts POST /upload/presigned · carrier: the spec-lane card split from #27549 part (b) (GetPresignedUrlRequestSchema.size), same family, merge there · dedupe: presigned, GetPresignedUrlRequestSchema, filename min(1), mimeType empty, upload/presigned",
        "class: a · reach: main push battery Dogfood Regression Gate (does not run on PR or merge queue); 4 tests red deterministically on f5bb94f94e (merged with origin/main 755e28500) · evidence: error-sentences-live.dogfood.test.ts (2) and object-hook-lifecycle-scratch.dogfood.test.ts (1) assert no code on an undeclared hook abort at the /data door, contradicted by 21c4bdd804 (HOOK_ABORTED); platform-app-object-entry-views.test.ts (1) reports sys_exchange_rate as named but undeclared, after 194e66c028 · carrier: none (seat to route under the main-red convention) · dedupe: HOOK_ABORTED dogfood, error-sentences-live, sys_exchange_rate entry views, object-hook-lifecycle-scratch",
        "carrier: none · noted, not filed: a whitespace-only filename / mimeType is admitted by both InitiateChunkedUploadRequestSchema and the init door; they agree, and the order did not ask for it"
      ]
    }
  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#4 · marchtian · session_017ihL2fi9zfJhUTubyJYb6V

    ACCEPT → PR #27887 comment 6513663903 (lane part) + Contract review PASS 6513966766 (CONTRACT_REVIEW_TIER, triage, head f5bb94f9, no must-fix, no should-fix) · 2026-10-30T03:08Z · readied and auto-merge armed by the seat (every check green; check-expected-skips OK, 13 skips in the roster); needs:contract-review removed. The presigned schema's same gap is noted on #27598.

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:specfindingpm:dispatchedpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions