From 4caa0e612dd6bf98017e22a0190806fec566d387 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 27 Jul 2026 03:57:13 +0000 Subject: [PATCH] docs(spec): correct field.readonly describe re: import readonly-strip exemption (#3493) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `field.readonly` describe said "system-context writes (import, seed replay, migration) are exempt" — but a normal data import is NON-system (its writeCtx is session-derived, isSystem:false) and still strips readonly fields. The strip is bypassed only by `isSystem` writes (seed replay, migration) and, since #3497, by an opt-in "historical" import (`preserveAudit`) that admits a whitelist (the audit/timestamp family + author-declared business readonly fields). Fix the describe accordingly and regenerate references/data/field.mdx. Describe-string + regenerated reference doc only; no schema shape / behavior / api-surface change. Empty changeset (releases nothing). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01B5rdfBKjkbcoEif4KUV6xE --- .changeset/field-readonly-doc-preserveaudit.md | 4 ++++ content/docs/references/data/field.mdx | 2 +- packages/spec/src/data/field.zod.ts | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) create mode 100644 .changeset/field-readonly-doc-preserveaudit.md diff --git a/.changeset/field-readonly-doc-preserveaudit.md b/.changeset/field-readonly-doc-preserveaudit.md new file mode 100644 index 0000000000..402bb35a6b --- /dev/null +++ b/.changeset/field-readonly-doc-preserveaudit.md @@ -0,0 +1,4 @@ +--- +--- + +docs(spec): correct the `field.readonly` describe re: import exemption (#3493). It listed "import" as a system-context exemption, but a normal data import is non-system and still strips; the strip is bypassed only by `isSystem` writes (seed replay, migration) and by an opt-in "historical" import (`preserveAudit`), which admits a whitelist. Describe-string only — regenerates `references/data/field.mdx`, releases nothing. diff --git a/content/docs/references/data/field.mdx b/content/docs/references/data/field.mdx index 0b55359f8c..fe828626a9 100644 --- a/content/docs/references/data/field.mdx +++ b/content/docs/references/data/field.mdx @@ -145,7 +145,7 @@ const result = Address.parse(data); | **conditionalRequired** | `string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }` | optional | Predicate (CEL) — field is required when TRUE. Alias of `requiredWhen`. | | **widget** | `string` | optional | Form widget override — names a registered field component (resolved as `field:`) to render this field instead of the `type` default. Degrades to the `type` renderer when unregistered. e.g. "object-ref", "filter-condition", "recipient-picker". | | **hidden** | `boolean` | optional | Hidden from default UI | -| **readonly** | `boolean` | optional | Read-only — never editable in forms, AND server-enforced on BOTH write paths: a non-system write to this field is silently dropped from the payload on UPDATE (#2948/#3003) and on INSERT (#3043; a create can no longer directly seed e.g. `approval_status: "approved"`), symmetric with `readonlyWhen`. A stripped INSERT field still falls back to its `defaultValue`; system-context writes (import, seed replay, migration) are exempt. | +| **readonly** | `boolean` | optional | Read-only — never editable in forms, AND server-enforced on BOTH write paths: a non-system write to this field is silently dropped from the payload on UPDATE (#2948/#3003) and on INSERT (#3043; a create can no longer directly seed e.g. `approval_status: "approved"`), symmetric with `readonlyWhen`. A stripped INSERT field still falls back to its `defaultValue`. Exempt from the strip: `isSystem` writes (seed replay, migration), and an opt-in "historical" import (`preserveAudit`, #3493) — which admits a whitelist (the audit/timestamp family plus author-declared business `readonly` fields). A normal (non-system) import is NOT system-context and still strips. | | **requiredPermissions** | `string[]` | optional | [ADR-0066 D3] Capabilities required to read/edit this field (mask on read, deny on write; AND-gate). | | **ackPlaintextMasking** | `boolean` | optional | [ADR-0100] Affirm a generic `password` field's plaintext-at-rest / masked-on-read contract is intended, silencing the author-time warning (#3420). No effect on non-password fields. | | **system** | `boolean` | optional | Auto-injected system/audit field (e.g. created_at, updated_by, organization_id). Tools that surface system fields separately from author-declared business fields should branch on this flag. | diff --git a/packages/spec/src/data/field.zod.ts b/packages/spec/src/data/field.zod.ts index 339689fca2..6d228ca74f 100644 --- a/packages/spec/src/data/field.zod.ts +++ b/packages/spec/src/data/field.zod.ts @@ -502,7 +502,7 @@ export const FieldSchema = lazySchema(() => z.object({ /** Security & Visibility */ hidden: z.boolean().default(false).describe('Hidden from default UI'), - readonly: z.boolean().default(false).describe('Read-only — never editable in forms, AND server-enforced on BOTH write paths: a non-system write to this field is silently dropped from the payload on UPDATE (#2948/#3003) and on INSERT (#3043; a create can no longer directly seed e.g. `approval_status: "approved"`), symmetric with `readonlyWhen`. A stripped INSERT field still falls back to its `defaultValue`; system-context writes (import, seed replay, migration) are exempt.'), + readonly: z.boolean().default(false).describe('Read-only — never editable in forms, AND server-enforced on BOTH write paths: a non-system write to this field is silently dropped from the payload on UPDATE (#2948/#3003) and on INSERT (#3043; a create can no longer directly seed e.g. `approval_status: "approved"`), symmetric with `readonlyWhen`. A stripped INSERT field still falls back to its `defaultValue`. Exempt from the strip: `isSystem` writes (seed replay, migration), and an opt-in "historical" import (`preserveAudit`, #3493) — which admits a whitelist (the audit/timestamp family plus author-declared business `readonly` fields). A normal (non-system) import is NOT system-context and still strips.'), /** * [ADR-0066 D3] Capabilities required to READ/EDIT this field. A field