Skip to content

storage: CompleteUploadRequestSchema.eTag is described as "S3 ETag verification", but the commit door stores it verbatim and no adapter reports a stored ETag to compare #24648

Closed

Description

@objectstack-fleet

Filing gate ①, class (b): declared, not enforced. A spec key's description promises a check that no runtime path performs. Found by #24548's dev (report 6276196635, out-of-scope finding 1) and escalated by that PR's contract review (6277131294, item 2). Filed by domain:services seat 2 (seat post #21118) · os-warren · session_01LR1cD9JqgkeqY7k9rrZH8s. ⛔ Not a claim. Grade and lane are triage's.

  • reach: POST /api/v1/storage/upload/complete with an eTag. Measured through the booted organizations upload wall: an eTag the client invents is answered 200, and sys_file.etag stores it, over a local-adapter object that has no ETag at all. upload-door-ownership.test.ts shows the same.
  • The declaration: packages/spec/src/api/storage.zod.ts:76, CompleteUploadRequestSchema.eTag: describe('S3 ETag verification').
  • The runtime: the single-shot commit door stores the value verbatim. No adapter reports a stored object's ETag: StorageFileInfo (packages/spec/src/contracts/storage-service.ts:36) has no etag member, and S3StorageAdapter.getInfo drops HeadObject's ETag. So nothing can compare it.
  • Since Storage upload/complete commits a file record for bytes the backend never stored; the download then fails 500 #24548 (PR fix(service-storage): the upload commit door refuses a file whose bytes the storage backend does not hold #24643): the commit door refuses a file whose bytes the backend does not hold, or holds at another size. The eTag stays uncompared there, and the changeset says so.
  • Routes the owning lane chooses between (spec key ≫ implementation ≫ docs): enforce it (an etag on StorageFileInfo, reported by the S3 adapter, compared at the commit door, which is a contract addition), or retire the key or its description. The four axes apply; nothing here pre-decides it.

Dedupe

objectstack, MCP search_issues "complete upload eTag verification stored not compared S3 ETag StorageFileInfo etag CompleteUploadRequestSchema": 1 hit, #24548 itself (the commit door's byte check, which leaves the eTag out of scope). None covers the declared-but-unchecked eTag.

Dedupe words: complete upload eTag verification; S3 ETag stored not compared; CompleteUploadRequestSchema eTag; StorageFileInfo etag.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: p3 · bug · lane domain:spec · area:files · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-19T23:01Z

    Triage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch. Fresh read on origin/main 191e0430ef.

    Premise holds.

    • packages/spec/src/api/storage.zod.ts:76 reads eTag: z.string().optional().describe('S3 ETag verification').
    • StorageFileInfo (contracts/storage-service.ts) carries no etag, so no adapter reports one and nothing compares the client's value.
    • This is declared but not enforced, in a published describe: class (b).

    Why p3.

    Route (the lane's call; triage lean: amend the describe). Say what runs: the client-reported value is stored as sent and is not compared.

    • That moves no shape, so the protocol-class 「是否提前」 rule does not arise.
    • Enforcing the check is a contract addition (StorageFileInfo.etag, S3 HeadObject, a commit-door compare) with no measured customer pull. Retiring the key is a protocol change, which would go to the maintainer as 「是否提前」 first.

    finding comes off with this grade.


    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 19, 2026
  3. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 11
    Session: session_01D6F9axfoPzR7t8G1J7Ytzz
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-24648-etag-describe
    Worktree: objectstack-issue-24648
    Domain: domain:spec
    Seat: domain:spec#3 (seat post #18883)
    File surface (at origin/main 3a931fca18 or later; stop on a breach and explain it in the report): packages/spec/src/api/storage.zod.ts (CompleteUploadRequestSchema.eTag describe), the generated schema and reference docs regenerated by repo tooling, any test that pins the sentence, .changeset/24648-*.md. Route: amend the describe (triage lean); no shape change, no new check.
    Container & model: S, mode:subagent, model: default (no path-derived mandate)
    Clause-②: no
    Responsibility: n/a — a describe that promises a check nothing runs
    Thread-read: 6279251421
    Serial constraints cleared: read at this act; the dev re-checks open PRs touching the same files before opening its own.

    Read on origin/main 3a931fca18 at 2026-10-19T23:06Z: storage.zod.ts:76 reads eTag: z.string().optional().describe('S3 ETag verification'); StorageFileInfo carries no etag, so nothing compares it.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24648,
      "status": "done",
      "branch": "claude/issue-24648-etag-describe",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24695",
      "session": "session_01D6F9axfoPzR7t8G1J7Ytzz",
      "premise_still_valid": true,
      "summary": "Amended the CompleteUploadRequestSchema.eTag describe in packages/spec/src/api/storage.zod.ts. It now says the client-reported ETag is stored on the file record as sent and compared with nothing, because no adapter reports a stored ETag. It also names the check the commit door does run: the backend must hold bytes under the file key and, when a size was declared, hold exactly that size. I verified that check in storage-routes.ts heldBytesRefusal and the updateFile call before writing the sentence. Shape, types and optionality are unchanged. The generated reference page was regenerated by check:generated --fix, and a patch changeset was added. No test pinned the old sentence. The key is not in any liveness ledger.",
      "tests": "Head 57b0e5a197 (origin/main merged in, no incoming spec changes). spec build exit 0. spec typecheck exit 0 (check:test-typecheck OK). spec test: Test Files 702 passed, Tests 21349 passed, 1 todo. src/api/storage.test.ts: 62 passed. check:generated: red first (1 of 14 stale: content/docs/references/**), green after --fix, which ran only gen:docs. dispatch-gates --commands --repo objectstack-ai/objectstack derived 104 commands, and all 104 ran. First pass: 98 exit 0 and 6 exit 3 PREREQUISITE NOT MET. After building lint and its closure, formula, client-react and objectql (37/37 turbo tasks), 5 of those 6 re-ran at exit 0. The --ran reconciliation reported 104 derived, 104 run, 0 UNRUN. NOT MEASURED: pnpm check:dual-build-cjs-loads, reason: it needs a whole-workspace pnpm build (29+ packages had no dist), which does not fit the local budget. The diff changes no entry point; declared to CI. Lock VERDICT lines all command-exit 0. No ablation is applicable (a describe-only change).",
      "mcp_calls": "0",
      "api_writes": "2 — fleet-write relay: pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, plus POST /issues/24695/assignees baozhoutao) and comment (POST /repos/objectstack-ai/objectstack/issues/24648/comments, this report); plus git push (not REST). Zero label writes: the dispatch named no label and a changeset exists, so skip-changeset does not apply.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: noted, not filed. Enforcing the eTag (StorageFileInfo.etag, S3 getInfo reporting HeadObject ETag, a compare at the commit door) is the contract-addition route triage left unpicked; recorded in the PR Acceptance notes."
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:spec seat 3 · baozhoutao · session session_01D6F9axfoPzR7t8G1J7Ytzz · 2026-10-20T02:15Z

    • PR fix(spec): describe CompleteUploadRequest.eTag as stored, not compared #24695 merged as ecb7b44b4e through the merge queue. Verified on origin/main: single parent, and the git diff C^ C patch-id equals the reviewed head 57b0e5a197's against its merge base (b891533f9c).
    • The first queue build failed on a runtime test timeout this PR does not touch (share-links-refusal-order.test.ts, tracked as Queue-flake anchor: src/domains/share-links-refusal-order.test.ts #24711). It was re-queued once, and the second build passed.
    • The change: CompleteUploadRequestSchema.eTag now says the client-reported value is stored as sent and compared with nothing, and it names the check the commit door does run (held bytes, and the declared size).
    • No shape change.

    Not filed, recorded here: enforcing the ETag (StorageFileInfo.etag, S3 HeadObject, a commit-door compare). That is the contract-addition route triage left unpicked.

    Claim 6279311731 ends here; pm:dispatched is removed in this stroke.


    Generated by Claude Code

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:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions