From b0d255bb0c27a5261993292592f47589e8da9682 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 21:07:56 +0000 Subject: [PATCH 1/3] docs(spec): key the fieldPatches example option by a value crm_account.industry stores The documented example patched option `tech`, which crm_account.industry does not store, so a copy was refused at os validate and install. Re-key it as `technology: { label: 'Tech' }` in the schema description, TSDoc, the option-list refusal text, the object-extensions guide and the two unreleased changesets that carried it. Claude-Session: https://claude.ai/code/session_01ADHkmQfYHca8dfoXTJfeKp Co-authored-by: Claude --- .changeset/28560-field-patch-slot.md | 2 +- .changeset/28565-field-patch-applied.md | 2 +- .changeset/28803-field-patches-example.md | 10 ++++++++++ content/docs/data-modeling/object-extensions.mdx | 2 +- packages/spec/src/data/object.zod.ts | 8 ++++---- 5 files changed, 17 insertions(+), 7 deletions(-) create mode 100644 .changeset/28803-field-patches-example.md diff --git a/.changeset/28560-field-patch-slot.md b/.changeset/28560-field-patch-slot.md index 90adaccf9ec..5ff320bf6e7 100644 --- a/.changeset/28560-field-patch-slot.md +++ b/.changeset/28560-field-patch-slot.md @@ -16,7 +16,7 @@ objectExtensions: [{ industry: { label: 'Sector', inlineHelpText: 'The market the account sells into', - options: { tech: { label: 'Technology' } }, + options: { technology: { label: 'Tech' } }, }, }, }] diff --git a/.changeset/28565-field-patch-applied.md b/.changeset/28565-field-patch-applied.md index ed7d5d1e605..57255c29d72 100644 --- a/.changeset/28565-field-patch-applied.md +++ b/.changeset/28565-field-patch-applied.md @@ -17,7 +17,7 @@ ADR-0146 D10's second rule, live. The per-field display patch an object extensio objectExtensions: [{ extend: 'crm_account', fieldPatches: { - industry: { label: 'Sector', inlineHelpText: 'The market the account sells into', options: { tech: { label: 'Technology' } } }, + industry: { label: 'Sector', inlineHelpText: 'The market the account sells into', options: { technology: { label: 'Tech' } } }, }, }] ``` diff --git a/.changeset/28803-field-patches-example.md b/.changeset/28803-field-patches-example.md new file mode 100644 index 00000000000..d193135253f --- /dev/null +++ b/.changeset/28803-field-patches-example.md @@ -0,0 +1,10 @@ +--- +'@objectstack/spec': patch +--- + +docs(spec): the documented `fieldPatches` example keys its option by a value `crm_account.industry` really stores + +Clause-②: no +Metadata-protocol: none + +The `fieldPatches` description, its TSDoc example and the refusal text for an option list under `options` showed `options: { tech: { label: 'Technology' } }`. `crm_account.industry` stores `technology`, not `tech`, so a copy of that example was refused at `os validate` and at install as naming an option the field does not have. The example now reads `options: { technology: { label: 'Tech' } }`, the same spelling the published skill uses. Prose only: no schema accepts or refuses anything differently. diff --git a/content/docs/data-modeling/object-extensions.mdx b/content/docs/data-modeling/object-extensions.mdx index 63134d17f74..7324c797131 100644 --- a/content/docs/data-modeling/object-extensions.mdx +++ b/content/docs/data-modeling/object-extensions.mdx @@ -110,7 +110,7 @@ export const AccountWordingExtension = defineObjectExtension({ industry: { label: 'Sector', inlineHelpText: 'The market the account sells into.', - options: { tech: { label: 'Technology' } }, // addressed by the stored value + options: { technology: { label: 'Tech' } }, // addressed by the stored value }, }, }); diff --git a/packages/spec/src/data/object.zod.ts b/packages/spec/src/data/object.zod.ts index 10b52d10a3c..957a4415905 100644 --- a/packages/spec/src/data/object.zod.ts +++ b/packages/spec/src/data/object.zod.ts @@ -4835,7 +4835,7 @@ const FieldDisplayPatchSchema = lazySchema(() => { error: (issue) => issue.code === 'invalid_type' && Array.isArray(issue.input) ? 'A field patch\'s `options` is a map keyed by each option\'s stored value — ' + - '`options: { tech: { label: \'Technology\' } }` — not the field\'s option list: which options ' + + '`options: { technology: { label: \'Tech\' } }` — not the field\'s option list: which options ' + 'exist stays the vendor\'s (ADR-0146 D6).' : undefined, }).optional().describe( @@ -4879,7 +4879,7 @@ function refuseExtensionFieldPatchKeys(fieldPatches: unknown, ctx: z.RefinementC * fields: { sales_stage: Field.select([...]) }, * // A base field's display properties only (ADR-0146 D10), applied by the * // registry merge (`mergeObjectDefinitions`) — see `fieldPatches`. - * fieldPatches: { industry: { label: 'Sector', options: { tech: { label: 'Technology' } } } }, + * fieldPatches: { industry: { label: 'Sector', options: { technology: { label: 'Tech' } } } }, * }] * ``` */ @@ -4963,8 +4963,8 @@ export const ObjectExtensionSchema = lazySchema(() => strictObject({ 'the additive merges, on every object read: a tenant\'s own text keeps winning, and between two ' + 'extensions the higher `priority` wins. A patch naming a field or option value the target does not ' + 'have is refused at install and by `os validate`. Example: `fieldPatches: { industry: { label: ' + - '\'Sector\', inlineHelpText: \'The market the account sells into\', options: { tech: { label: ' + - '\'Technology\' } } } }`.', + '\'Sector\', inlineHelpText: \'The market the account sells into\', options: { technology: { label: ' + + '\'Tech\' } } } }`.', ), /** Override label */ From 78d84876bf3376ce371cd879b8d2de716afb2567 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 21:13:03 +0000 Subject: [PATCH 2/3] docs(spec): re-record the object reference for the fieldPatches example Claude-Session: https://claude.ai/code/session_01ADHkmQfYHca8dfoXTJfeKp Co-authored-by: Claude --- content/docs/references/data/object.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/docs/references/data/object.mdx b/content/docs/references/data/object.mdx index 0db67fe2f83..1fbc09d856a 100644 --- a/content/docs/references/data/object.mdx +++ b/content/docs/references/data/object.mdx @@ -576,7 +576,7 @@ const result = ApiMethod.parse(data); | :--- | :--- | :--- | :--- | | **extend** | `string` | ✅ | Target object name (FQN) to extend | | **fields** | `Record; description?: string; … }>` | optional | Fields to add/override | -| **fieldPatches** | `Record` | optional | Display patches of the target object's own fields, keyed by field name: change a vendor field's display properties (BASE_FIELD_DISPLAY_WHITELIST: label, description, help text, placeholder, option labels) without taking the field over, so its type, values and rules stay the vendor's and its upgrades still apply (ADR-0146 D10). Applied by the registry merge (`mergeObjectDefinitions`) after the additive merges, on every object read: a tenant's own text keeps winning, and between two extensions the higher `priority` wins. A patch naming a field or option value the target does not have is refused at install and by `os validate`. Example: `fieldPatches: { industry: { label: 'Sector', inlineHelpText: 'The market the account sells into', options: { tech: { label: 'Technology' } } } }`. | +| **fieldPatches** | `Record` | optional | Display patches of the target object's own fields, keyed by field name: change a vendor field's display properties (BASE_FIELD_DISPLAY_WHITELIST: label, description, help text, placeholder, option labels) without taking the field over, so its type, values and rules stay the vendor's and its upgrades still apply (ADR-0146 D10). Applied by the registry merge (`mergeObjectDefinitions`) after the additive merges, on every object read: a tenant's own text keeps winning, and between two extensions the higher `priority` wins. A patch naming a field or option value the target does not have is refused at install and by `os validate`. Example: `fieldPatches: { industry: { label: 'Sector', inlineHelpText: 'The market the account sells into', options: { technology: { label: 'Tech' } } } }`. | | **label** | `string` | optional | Override label for the extended object | | **pluralLabel** | `string` | optional | Override plural label for the extended object | | **description** | `string` | optional | Override description for the extended object | From 0b024ae90c2013c870e437ff844dd15af785c688 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 22:12:52 +0000 Subject: [PATCH 3/3] docs(spec): the fieldPatches changeset names no skill spelling that is not on main yet Claude-Session: https://claude.ai/code/session_01ADHkmQfYHca8dfoXTJfeKp Co-authored-by: Claude --- .changeset/28803-field-patches-example.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/28803-field-patches-example.md b/.changeset/28803-field-patches-example.md index d193135253f..97800db01eb 100644 --- a/.changeset/28803-field-patches-example.md +++ b/.changeset/28803-field-patches-example.md @@ -7,4 +7,4 @@ docs(spec): the documented `fieldPatches` example keys its option by a value `cr Clause-②: no Metadata-protocol: none -The `fieldPatches` description, its TSDoc example and the refusal text for an option list under `options` showed `options: { tech: { label: 'Technology' } }`. `crm_account.industry` stores `technology`, not `tech`, so a copy of that example was refused at `os validate` and at install as naming an option the field does not have. The example now reads `options: { technology: { label: 'Tech' } }`, the same spelling the published skill uses. Prose only: no schema accepts or refuses anything differently. +The `fieldPatches` description, its TSDoc example and the refusal text for an option list under `options` showed `options: { tech: { label: 'Technology' } }`. `crm_account.industry` stores `technology`, not `tech`, so a copy of that example was refused at `os validate` and at install as naming an option the field does not have. The example now reads `options: { technology: { label: 'Tech' } }`. Prose only: no schema accepts or refuses anything differently.