You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(spec)!: media accept/maxSize declared + enforced, stored form is a reference — ADR-0104 D3 wave 2 (PR-5a)
accept and maxSize are now declared on FieldSchema, and enforced on the server.
Both were already READ by the upload widgets — field.accept, field.maxSize —
while the spec did not declare them, so an author who wrote them had the keys
silently stripped at parse and the constraint simply never existed. That is
exactly the ADR-0104 failure class this ADR exists to remove: a declaration
accepted in source, dropped from the contract, with no feedback anywhere.
Now that the platform owns the file, sys_file carries the authoritative MIME
type and byte size, so a record write is re-checked where the constraint
actually binds rather than only in the browser — a client-side check is a
convenience, not a control, since any caller talking to the API directly
bypasses it. Violations raise FileConstraintError and fail the write. The check
rides the pass that already loads each referenced sys_file row, so it costs no
extra read, and an entry is only judged against metadata the file actually
reports: no recorded MIME type cannot fail an accept test, no recorded size
cannot fail maxSize. "We don't know" must not become "not permitted" — a test
caught that distinction being got wrong.
The stored form of a media field narrows to an opaque sys_file id.
valueSchemaFor(field, 'stored') now yields an id for the whole media family;
the inline {url, name, size, …} blob becomes the 'expanded' read form, which
also still admits an unresolved id (storage service absent, file not committed)
exactly as an unexpanded lookup id stays valid. Two legacy forms stop
conforming, both deliberately: the inline blob, which is no longer stored but
derived; and an external URL, which was never a managed file — R7 retires those
toward an explicit `url` field, and under AI authoring that IS the point, since
it stops "managed file" and "external link" being the same declaration.
Not a breaking change today. Value-shape checking is warn-first (R1/R2): a
not-yet-backfilled row still writes and the author gets a warning naming the
field. Hard rejection arrives only under OS_DATA_VALUE_SHAPE_STRICT_ENABLED,
which a deployment opts into after running the backfill and confirming
reconciliation. The `!` marks the contract change for the v17 window, not a
runtime break on upgrade.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SHpGw3GBA9aFpfwVArRWfd
Copy file name to clipboardExpand all lines: content/docs/references/data/field.mdx
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -113,6 +113,8 @@ const result = Address.parse(data);
113
113
|**scale**|`number`| optional | Decimal places |
114
114
|**min**|`number`| optional | Minimum value |
115
115
|**max**|`number`| optional | Maximum value |
116
+
|**accept**|`string[]`| optional | Permitted upload types for media fields, as MIME types or extensions (e.g. ["image/*", ".pdf"]). Offered to the file picker AND enforced on write. |
117
+
|**maxSize**|`integer`| optional | Maximum permitted file size in BYTES for media fields. Enforced on write against the stored file size, not just checked in the browser. |
|**reference**|`string`| optional | Target object name (snake_case) for lookup/master_detail fields. Required for relationship types. Used by $expand to resolve foreign key IDs into full objects. |
118
120
|**deleteBehavior**|`Enum<'set_null' \| 'cascade' \| 'restrict'>`| optional | What happens if referenced record is deleted |
0 commit comments