Skip to content

docs(permissions): attachments-access says Field.file stores a file URL in the column — since ADR-0104 D3 it stores a sys_file id #17406

Description

@claude

content/docs/permissions/attachments-access.mdx:19-21 tells a reader that Field.file / Field.image store a file URL in the record's own column:

Field.file / Field.image are a separate path — those store a file URL
in the record's own column and never create a sys_attachment row, so nothing
on this page applies to them.

The second half is correct and is the sentence's actual job (no sys_attachment row, so the page does not apply). The stored form in it is stale: since ADR-0104 D3 the stored value is an opaque sys_file id, not a URL.

The probe

packages/spec/src/data/field-value.zod.ts is the declaring surface, and it says the opposite in two places:

  • valueSchemaFor gives every member of FILE_REFERENCE_TYPES the FileReferenceIdValueSchema in its stored form (the expanded form is the union with the metadata object).
  • isFileIdToken's own docblock, verbatim: "A minted id is uuid/nanoid-shaped: word characters and -, nothing else. A URL — https://…, /api/…, data:…, blob:… — always carries a :, / or . and so can never match."

So on any deployment past os migrate files-to-references --apply, the value in that column is an id and cannot be a URL. A URL survives only as a legacy value on a deployment that has not run the migration — which content/docs/protocol/objectql/types.mdx states correctly, and conditionally, in its own callout:

Deployments predating the file-as-reference migration may still hold the inline metadata object (or a bare URL) as the stored value. The engine warns rather than rejects until os migrate files-to-references --apply has run.

⇒ The two pages state the same fact with opposite defaults: types.mdx states the post-migration form and scopes the legacy one; attachments-access.mdx states the legacy form unconditionally.

Why it is worth a card rather than a note

It is an error rather than an omission, and it is load-bearing on this page specifically: the page's whole subject is who may read what, and a reader building or auditing an access rule on "that column holds a URL" gets it wrong on every migrated deployment — including every store created since 17.0, which is attested at birth.

Scope

One clause on one line. ⛔ Deliberately not folded into #15989's PR (#17403): touching content/docs pulls the Build Docs job into that PR's check set, which is a verification surface its diff otherwise does not have, and the driver card is fenced to driver-sql + cli.

Found while spot-checking the doc pages adjacent to #15989; that card's own PR needed no doc edit, and the measurement behind that decision is recorded in its body.


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions