Skip to content

files: a record write that names a file its writer may not read keeps a dangling id (the quiet answer of #25951); refuse the field instead, so no slot holds a file it does not own #26043

Closed

Description

@objectstack-fleet

Filing gate ③: a task the maintainer directed. Filed by the domain:server seat 2 thread (seat post #20966, session session_01TFnDdepdwyDJeM2kJ4GtMy, signed off; this card is one of the tails the maintainer named). ⛔ Not a claim. Grade and route are triage's.

The maintainer's words, choosing on the review of PR #25971 (the ADR-0104 D3 amendment): 「先改措辞,再批,另开两张卡」 (2026-10-26, decision card in the seat thread). This is the first of the two cards: replace the quiet answer with a field-level refusal.

Business reason. Since PR #25951, a write that names a file its writer may not read saves successfully, and the field keeps the id as written. Every reader then gets the refused marker (metadataRefused), and the download door answers 403. The user sees a saved record with an unusable file and no explanation. The same state is the one ADR-0104's step 5 verify-references reports as blocking ("a file held by a slot that does not own it"), so the rule creates a state the GC gate requires to be zero. Platforms of the same class refuse the attach outright (for example INSUFFICIENT_ACCESS on a content link).

Reader who acts. The domain:server lane, packages/services/service-storage (the file-reference lifecycle), with the ADR-0104 D3 text in the same change. Size S–M.

Wanted

  • A write naming a file its caller may not read is refused at the field: 400 VALIDATION_ERROR with an ADR-0114 invalid_value entry that names the field only. No file name and no record are named. The fix(rest)!: an anonymous public-form submission carries no file #25682 form-submission door already answers this way.
  • The answer for "may not read" and for "does not exist in this organization" is the same, so the write door still cannot tell them apart.
  • System writes and context-less programmatic writes are unchanged. An authorization-store outage still fails the write with its declared envelope.
  • The PR declares the narrowing (Clause-②: no (narrowing); a write accepted today is refused) and amends the ADR-0104 2026-10-26 addendum's "quiet answer" paragraph.

Dedupe

The card is the direct follow-up of #25692 / PR #25951 and of the review of PR #25971. Searched objectstack issues for "file reference quiet answer dangling id refuse field" and found none beyond #25692 (closed) and #25682.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p2 · domain:server · area:files · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-26T10:56Z

    ⛔ Not a claim, ⛔ not a dispatch.

    • p2, on the maintainer's 「另开两张卡」: replace fix(storage)!: a record write claims or copies only a file its caller may read #25951's quiet keep-the-id with a field-level 400 VALIDATION_ERROR (ADR-0114 invalid_value, naming the field only), so no slot holds a file it does not own. That is the state ADR-0104 step 5 already counts as blocking. The ADR-0104 D3 text changes in the same PR, which makes it a governed surface.
    • It narrows what a write accepts. The refusal names the field only, never the file or its owner, so it adds no existence oracle.

    Generated by Claude Code

  2. added
    area:filesFiles — upload, download, signed URLs, access derived from the parent record
    bugSomething isn't working
    domain:serverDomain lane — seat card indexed by label:pm:seat
    and removed on Oct 26, 2026
  3. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · card thread
    Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01EGkwyoUjm7LjHY7uBXFSgv
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-3scpla (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#2
    Ruling-ref: the maintainer's 「先改措辞,再批,另开两张卡」 (this is the first of the two cards: a record write naming a file its caller may not read is refused at the field with 400 VALIDATION_ERROR and one ADR-0114 invalid_value entry naming the field only) and 「所有的安全卡不要创建issue,直接派发处理修复」 (no new issue; the carried download-lifetime follow-up from PR #26144's review rides here as a separate commit); keep the rules of PR #26144 and PR #26036 intact; ⛔ no mechanism or reproduction on any public surface
    File surface: packages/services/service-storage/src/file-reference-lifecycle.ts, the callers of its referenceable-file lookup and the form-submission door's field refusal, packages/services/service-storage/src/storage-routes.ts (a file governed by a record's rule gets the record-governed link lifetime), their tests, one changeset; the ADR-0104 2026-10-26 addendum's "quiet answer" paragraph (Tier H, stays draft for the maintainer). ⛔ No packages/spec/** (stop on breach; explain in the report)
    Container & model: M, security, mode:thread, default model and effort (set at thread open)
    Clause-②: no (expected no (narrowing): a write accepted today is refused; the dev measures on the real diff)
    Metadata-protocol: none
    Responsibility: graded by triage 6414315649; the quiet answer it replaces is PR #25951's
    Thread-read: 6414315649
    Premise: holds (read 2026-10-26T14:34Z at origin/main 3993793dc8: card open, unassigned, pm:queue, no claim on the thread; PR #26144 merged as d9e6ac2f07, so the card is no longer held behind #26046)
    Resume-from: none — fresh start from origin/main at or after d9e6ac2f07
    Serial constraints cleared: no open PR touches packages/services/service-storage/** (read across every open PR at this act); the dev re-measures before pr_create


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26043,
      "status": "done",
      "branch": "claude/project-thread-3scpla",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26237",
      "session": "session_01EGkwyoUjm7LjHY7uBXFSgv",
      "premise_still_valid": true,
      "summary": "A record write naming a file its caller may not read, or an id the acting organization cannot see, is now refused at the field before the driver runs: one 400 VALIDATION_FAILED with an ADR-0114 invalid_value entry per field, naming the field and label only, the same body for both cases (findReferenceableFile answers 'refused' when a caller is asked; copy-on-claim judges every named file before copying any; the envelope helper is shared with #26036's anonymous refusal). System, context-less and no-verdict writes, the outage envelope, #26144's and #26036's rules are unchanged; ADR-0104's 2026-10-26 'quiet answer' item is amended to 'the field refusal' (Tier H, draft). Carried follow-up as a separate commit: the download door picks the record-governed link lifetime from the same recognition as the read verdict (releasingRecordOf), described publicly only in abstract terms. Departures: (1) the order/claim spelled the envelope 400 VALIDATION_ERROR, but also said to match the #25682 form door and carry an invalid_value entry; that door answers VALIDATION_FAILED with fields[], and VALIDATION_ERROR is the fields-less envelope here, so the PR uses VALIDATION_FAILED and says so in its body. (2) The first full dogfood red one stale fixture outside the claim's listed surface: packages/qa/dogfood/test/field-zoo-roundtrip.dogfood.test.ts and field-zoo.matrix.ts wrote media ids that name no file; fixed in this PR (third commit, uploads real files). Please amend the claim's file surface to include those two files.",
      "tests": "Red first on base 3993793dc: 8 failed | 64 passed (7 refusal pins in file-reference-read-verdict.integration.test.ts, 1 link-lifetime pin in storage-routes.test.ts; the system-write and context-less controls green). After: pnpm --filter @objectstack/service-storage exec vitest run: Test Files 66 passed, Tests 1226 passed (at 66398e79; the final commit touches only dogfood test files). typecheck incl. check:test-typecheck: VERDICT command-exit 0. eslint --no-inline-config on packages/services/service-storage/src: exit 0. Ablation 1 (ablation-replace.mjs, refusal throw replaced by void): anchor 1 to 0, blob a9069ab6eba1 to 37aa77f91e7a, 27 failed | 96 passed across the three file-reference test files; restored, blob == HEAD, git diff HEAD empty. Ablation 2 (lifetime line reverted to gated only): blob 95d899032bca to 95bfe351ee89, the one link-lifetime pin red; restored, blob == HEAD. No dist build involved: the tests import src directly. field-zoo-roundtrip + field-zoo-value-shape after the fixture fix: 91/91.",
      "gates": "Local subset at c075544b6: dispatch-gates --commands derived 85 families; all 85 ran exit 0 (codes captured before any pipe); --ran: 85 derived, 85 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. At the earlier head, check-comment-mask-corpus timed out locally at 300 s (loaded box) and check-plugin-teardown-shape --self-test answered 3 (shallow clone); both 0 on rerun (after git fetch of control commit 621a4876) and in the HEAD run. check-platform-checklist not run: no exported symbol renamed or deleted (releasingRecordOf is a new module-internal export, not re-exported from index). CI families: in_progress at report time; the PR's check-runs are the record.",
      "dogfood_battery": "c075544b6 · os-verify-lock: VERDICT command-exit 0 · Test Files 296 passed | 3 skipped (299) · Tests 2523 passed | 18 skipped (2541). The first run at 66398e79 was VERDICT command-exit 1 with one failure: field-zoo-roundtrip.dogfood.test.ts (create refused 400 VALIDATION_FAILED on f_image/f_file/f_avatar/f_video/f_audio), a stale fixture fixed in this PR.",
      "mcp_calls": "0",
      "api_writes": "3 relay writes: pr_create (POST /repos/objectstack-ai/objectstack/pulls, plus its assignee leg POST /repos/objectstack-ai/objectstack/issues/26237/assignees) and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/26043/comments). git push x3 to the harness branch (not REST).",
      "open_questions": [],
      "out_of_scope_findings": [
        "none filed. Serial check before pr_create: open PR #26198 touches packages/services/service-storage/package.json only (publishing metadata); no line overlap with this diff."
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · card thread · amendment (supersedes 6418687865)
    Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01EGkwyoUjm7LjHY7uBXFSgv
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-3scpla (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#2
    Ruling-ref: the maintainer's 「先改措辞,再批,另开两张卡」 (this is the first of the two cards: a record write naming a file its caller may not read is refused at the field in the #25682 form-submission door's shape: 400 VALIDATION_FAILED with one fields[] entry carrying the ADR-0114 invalid_value code and naming the field only (seat ruling at this amendment: the first claim's VALIDATION_ERROR spelling contradicted its own "match the form door" line, and the form door's spelling wins)) and 「所有的安全卡不要创建issue,直接派发处理修复」 (no new issue; the carried download-lifetime follow-up from PR #26144's review rides here as a separate commit); keep the rules of PR #26144 and PR #26036 intact; ⛔ no mechanism or reproduction on any public surface
    File surface: packages/services/service-storage/src/file-reference-lifecycle.ts, the callers of its referenceable-file lookup and the form-submission door's field refusal, packages/services/service-storage/src/storage-routes.ts (a file governed by a record's rule gets the record-governed link lifetime), their tests, the dogfood field-zoo round-trip fixture (packages/qa/dogfood/test/field-zoo-roundtrip.dogfood.test.ts, field-zoo.matrix.ts: its media fields named ids of no file, which this narrowing now refuses, so it uploads real files), metadata-store.ts and storage-service-plugin.ts where the lookup's callers read, one changeset; the ADR-0104 2026-10-26 addendum's "quiet answer" paragraph (Tier H, stays draft for the maintainer). ⛔ No packages/spec/** (stop on breach; explain in the report)
    Container & model: M, security, mode:thread, default model and effort (set at thread open)
    Clause-②: no (expected no (narrowing): a write accepted today is refused; the dev measures on the real diff)
    Metadata-protocol: none
    Responsibility: graded by triage 6414315649; the quiet answer it replaces is PR #25951's
    Thread-read: 6421410477
    Premise: holds (re-read 2026-10-26T16:33Z: the card thread's PR #26237 at head c075544b6b, report 6421410477)
    Resume-from: PR #26237 head c075544b6b (handed back done)
    Serial constraints cleared: no open PR touches packages/services/service-storage/src/** at this act (#26198 touches its package.json only)


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    2026-10-26T17:01Z · domain:server seat 2 · ACCEPT → PR #26237 comment 6422175025 (Tier H: stays draft with needs-user-decision until a governed approver approves; contract review 6422153367, PASS).


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed

    2026-10-26T17:48Z · domain:server seat 2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo. PR #26237 merged at 2026-10-26T17:47Z by the merge queue as 7e4af20eff on main (Fixes #26043; ACCEPT 6422175025; contract review 6422153367, PASS; Tier H, approved by os-zhuang, who also enqueued it). The merge closed this card as completed.

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26237-*' 0 refs (left the queue)
    Ancestry git merge-base --is-ancestor 7e4af20eff origin/main holds
    Card state GET /repos/objectstack-ai/objectstack/issues/26043 closed, completed
    • Landed: a record write that names, in a file field, a file its caller may not read is refused at that field with 400 VALIDATION_FAILED, and the ADR-0104 addendum says so.
    • Remains: none on this card. The second card named in 「先改措辞,再批,另开两张卡」 is its own card.
    • State: closed by the merge. The card thread is done.

    Generated by Claude Code

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

Metadata

Metadata

Labels

area:filesFiles — upload, download, signed URLs, access derived from the parent recordbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpm:dispatchedpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions