Skip to content

A REST read that projects a file field without id answers {id, metadataRefused: true} for a caller that may read the file #26672

Closed

Description

@objectstack-fleet

Found by the hotclm app (objectstack-ai/hotclm#19, PR #185) on @objectstack/* 18.2.0.

What happens. GET /api/v1/data/clm_contract_type?select=template_file, as an app admin (hotclm clm.admin, not the platform admin), answers template_file: {id, metadataRefused: true}. The same request with select=id,template_file answers the full {name, size, mimeType, url}. The platform admin gets the full value either way.

Reach. REST and API callers that project narrowly. The Console always selects id, so no screen shows this. The verdict looks like it is derived from the parent record, and without id in the projection the hydration seems unable to find the parent, so a permitted reader is told the metadata is refused.

Seam. The runtime's file-field read expansion (the parent-derived verdict of #22637) → a consumer projecting without id.

Expected. The verdict does not depend on whether the caller listed id: either the hydration fetches the key it needs, or the projection adds it internally and trims it back.

Dedupe: searched metadataRefused / file field / select projection. The nearest are #22637 and #22593 (closed, the rules this follows).


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they land
    area:filesFiles — upload, download, signed URLs, access derived from the parent record
    on Oct 27, 2026
  2. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: audit line · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T13:01Z

    ⛔ Not a claim, ⛔ not a dispatch.

    • Triage: lands in the file-field projection on the REST read; rationale: p3 · domain:server (lane merge: domain:engine → domain:server) · area:files. A caller who may read the file gets the same value with or without id in select. The refusal applies only where the read rule refuses. ⛔ Do not widen what an unauthorised caller sees.

    Generated by Claude Code

  3. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    and removed
    domain:engineRetired 2026-10-22, alias of domain:server (engine surface); in-flight cards keep it until they land
    on Oct 27, 2026
  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 9 · card thread · a file-field read not depending on whether the projection lists id
    Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_01FEkQ5nso8iiU7cfQSEAkX5
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-dywp1n (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#1
    Ruling-ref: triage 6442544054 (p3, under Leehom's 「p1 p2 处理完了,就可以处理 p3」 2026-10-26T10:29Z): a caller who may read the file gets the same value with or without id in select; a refused caller stays refused. Intake split with domain:server#2 for the 18.2.0 wave-2 cards (#25807).
    File surface: the runtime file-field read hydration (packages/runtime/**, packages/rest/** or packages/objectql/**, wherever it lives), its tests and a patch changeset. ⛔ No packages/spec/**, ⛔ no ui/**. ⛔ No packages/spec/** (lane red line).
    Container & model: S, mode:thread, default model and effort (set at thread open)
    Clause-②: no — the dev measures it on the diff
    Metadata-protocol: none
    Responsibility: an API caller projecting narrowly is told file metadata is refused when it may read it (hotclm#19)
    Thread-read: 6442544054
    Premise: holds (read 2026-10-27T20:54Z at origin/main 1708115e2f: card open, pm:queue, no live claim)
    Serial constraints cleared: none known on these paths; the dev re-measures open PRs on the files it touches before pr_create.

  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 26672,
    "status": "done",
    "branch": "claude/project-thread-dywp1n",
    "pr": "#26938",
    "session": "session_01FEkQ5nso8iiU7cfQSEAkX5",
    "premise_still_valid": true,
    "summary": "Measured on main 1708115: a reader refused sys_file read whose field-owned verdict allows the file got {id, metadataRefused: true} for select=FILE_FIELD and the full value for select=id,FILE_FIELD, on REST list, query and by-id. Fix in packages/objectql/src/engine.ts: withFileOwnerKey runs after planFormulaProjection in find and findOne; an explicit projection naming a file-reference field and not id reads id too, and the existing widening cut (withoutFormulaWidening, after the middleware chain) trims it, so the answer keeps exactly the named columns. The verdict, the refused marker and the caller own sys_file read are unchanged; a refused reader stays refused with or without id. Every door shares it because all read through engine.find/findOne: REST GET /data/:object, POST /data/:object/query, GET /data/:object/:id, the dispatcher data domain (callData then protocol.findData/getData), and expand sub-reads (which already added the join key id). The objectql case that pinned the old answer (a read without id keeps the marker) was rewritten to pin the new one.",
    "tests": "Red first: rest data-file-field-projection-without-id.test.ts on main (objectql dist built from main) 3 failed / 3 passed, the 3 reds the id-less doors (list, query, by-id); after fix 6/6. Ablation: engine.ts swapped to its main blob under a restoring trap, objectql engine-file-hydrate-parent-derived.test.ts 1 failed / 16 passed (red = id-less case, refused control green), restore proven by HEAD blob hash + empty git diff HEAD. Package suites on 845325d: objectql 475/475 files, 8850/8850 tests; rest 329/329 files, 6519 passed 326 skipped; service-storage 70/70 files, 1258/1258 tests; typecheck objectql+rest+service-storage exit 0; eslint --no-inline-config on touched dirs exit 0. Final head 6ea02bc only removes one as-any from the new rest test: rest typecheck exit 0 and the file 6/6 re-run there.",
    "gates": "dispatch-gates --commands on the diff: 67 families, all run on final head 6ea02bc, 67/67 exit 0 (codes captured before any pipe); --ran reconcile: 67 derived, 67 run, 0 NOT-MEASURED, 0 UNRUN. First run on 845325d: check:query-options-erasure exit 1 (new test as-any, test surface 236 to 237), fixed; check-plugin-teardown-shape --self-test exit 3 (shallow clone), cleared by fetching 621a487, exit 0 on rerun. test:repo not run locally (no repo-tests file touched, not derived): CI Test Core. PR check-runs: in_progress at report time.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "4 — mcp__github__issue_read get x1, issue_read get_comments x2, pull_request_read get x1; all reads, no write tool",
    "api_writes": "2 relay writes as objectstack-fleet[bot] — pr_create (#26938, via fleet-write dispatch run 40593501734) and this os-dev-report issue comment (post-stamped); plus 2 git pushes (not REST)",
    "open_questions": [],
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed

    PR #26938 merged at 2026-10-27T23:08Z by the merge queue as 1f1eda4daf on main (Fixes #26672; ACCEPT 6455423361).

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26938-*' 0 refs
    origin/main by content git cat-file -e origin/main:packages/rest/src/data-file-field-projection-without-id.test.ts present
    Ancestry git merge-base --is-ancestor 1f1eda4daf origin/main holds

    State: this card closed by Fixes (completed); pm:dispatched removed.


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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions