Skip to content

Commit cadb075

Browse files
os-billclaude
andcommitted
docs(spec, skills): a queried composite record title is a stored computed field, not a formula
Re-words the `titleFormat` describe, the reconciliation note that quotes it, the objectstack-data skill's `nameField` / `titleFormat` rows and its "Computed column" bullet to the ruling's shape: a composite title that import, sort or search must match is a stored computed field (a non-formula field with an `expression`, computed and stored on every write); a text formula stays legal for display only; a stored field's `expression` is computed by the engine, never a client hint. Claude-Session: https://claude.ai/code/session_01T1Y1g92DfSEz93iuCisju4 Co-authored-by: Claude <noreply@anthropic.com>
1 parent ab488d9 commit cadb075

5 files changed

Lines changed: 21 additions & 6 deletions

File tree

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
---
2+
'@objectstack/spec': patch
3+
'@objectstack/skills': patch
4+
---
5+
6+
docs(spec, skills): the `titleFormat` description and the objectstack-data skill no longer steer a composite record title that must be queried into a formula field
7+
8+
Clause-②: no
9+
Metadata-protocol: none
10+
11+
The `titleFormat` description (and its generated copies on the object, metadata and migration reference pages) and the `objectstack-data` skill's `nameField` and `titleFormat` rows told an author to migrate a composite record title to a `formula` field with `returnType: 'text'` designated as `nameField`. A formula value is computed on read and stored nowhere, so a sort by it is refused, search never reaches it, and an import cannot match a lookup cell against it. Both now say what the record-title lint and ADR-0079 D3 already say: a composite title that import, sort or search must match is a stored computed field, a non-`formula` field that declares an `expression`, which the engine computes and stores on every write, designated as `nameField`; a text formula designated as `nameField` stays legal for a title shown for display only.
12+
13+
The same skill's "Computed column" bullet (`rules/relationships.md`) said a stored field's `expression` is a client compute hint and the sent value is stored verbatim. The engine computes the expression from the row the write stores and stores the result, so a client-sent value never stands; the bullet now says so.
14+
15+
No key, type, default or accepted value changes; only the text an author reads changes.

‎packages/spec/src/data/object.zod.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2841,7 +2841,7 @@ const ObjectSchemaBase = strictObject(
28412841
* cross-repo back-compat). New metadata should set `nameField`.
28422842
*/
28432843
displayNameField: z.string().optional().describe('[DEPRECATED → nameField] Field to use as the record display name (e.g., "name", "title"). Accepted as an alias for nameField.'),
2844-
titleFormat: TemplateExpressionInputSchema.optional().describe('[DEPRECATED → nameField (ADR-0079)] Render-only title template; the server cannot return or query it, and an explicit nameField now takes precedence. Migrate a single-field title to nameField, a composite to a formula field designated as nameField. Placeholders may be written {{field}} or {field} — the title renderers treat the two as equivalent, normalizing {{field}} to {field} before substituting; neither spelling is judged at parse time.'),
2844+
titleFormat: TemplateExpressionInputSchema.optional().describe('[DEPRECATED → nameField (ADR-0079)] Render-only title template; the server cannot return or query it, and an explicit nameField now takes precedence. Migrate a single-field title to nameField; a composite title that import, sort or search must match to a stored computed field (a non-formula field with an expression, computed and stored on every write) designated as nameField — a text formula designated as nameField stays legal for a title shown for display only. Placeholders may be written {{field}} or {field} — the title renderers treat the two as equivalent, normalizing {{field}} to {field} before substituting; neither spelling is judged at parse time.'),
28452845
/**
28462846
* [#21182 — ruling A on objectstack-ai/hotcrm#1199] The record's PICTURE:
28472847
* names the field whose value the record page header — the record chrome

‎packages/spec/src/system/metadata-form-zod-reconciliation.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ const LEDGER: ReadonlyArray<OmitEntry | SubsetEntry> = [
401401
type: 'object',
402402
path: ROOT_PATH,
403403
key: 'titleFormat',
404-
why: "`[DEPRECATED → nameField (ADR-0079)]` render-only title template the server cannot return or query, deliberately not offered to new authors; its own describe prescribes the migration — a single-field title to `nameField`, a composite to a formula field designated as `nameField` — and both targets are authorable",
404+
why: "`[DEPRECATED → nameField (ADR-0079)]` render-only title template the server cannot return or query, deliberately not offered to new authors; its own describe prescribes the migration — a single-field title to `nameField`, a composite title that import, sort or search must match to a stored computed field (a non-formula field with an `expression`) designated as `nameField`, a text formula staying legal for display only — and every target is authorable",
405405
},
406406
{
407407
kind: 'omit',

‎skills/objectstack-data/SKILL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ the publish door rejects it before that:
8585
| `highlightFields` | derived | Ordered field keys used as a record's compact face: the columns a **related list** renders on the parent's detail page, and what a lookup picker shows. Declare it on the CHILD object (see [Relationships](./rules/relationships.md)) |
8686
| `namespace` | — | **Not a schema key** — `ObjectSchema.create()` rejects unknown keys, so authoring it is a build error. Embed the prefix directly in `name` instead (e.g. `name: 'crm_account'`) |
8787
| `datasource` | `'default'` | Target datasource ID for virtualized data |
88-
| `nameField` | derived (e.g. `'name'`/`'title'`) | **Canonical** record-title field — the stored field used as the record's display name. Use a single text/email field, or a formula field (`returnType: 'text'`) for a composite title |
88+
| `nameField` | derived (e.g. `'name'`/`'title'`) | **Canonical** record-title field — the stored field used as the record's display name. Use a single text/email field; a composite title that import, sort or search must match is a stored computed field (a non-`formula` field with an `expression`, computed and stored on every write); a `returnType: 'text'` formula is legal for display only |
8989
| `displayNameField` | — | **Deprecated** alias for `nameField` (still honored as a fallback) |
90-
| `titleFormat` | — | **Deprecated, not removed (ADR-0079)** — render-only: the server can't return or query it. Use `nameField` (it wins); for a composite title, designate a `returnType: 'text'` formula field as `nameField` |
90+
| `titleFormat` | — | **Deprecated, not removed (ADR-0079)** — render-only: the server can't return or query it. Use `nameField` (it wins); a composite title migrates to the stored computed field, or the display-only text formula, the `nameField` row describes |
9191
| `enable` | — | Capability flags (trackHistory, searchable, apiEnabled, etc.) |
9292
| `fieldGroups` | — | Ordered list of logical field groups for forms/detail pages (see [Field Groups](./references/examples-objects.md#field-groups-mvp)) |
9393
| `lifecycle` | `record` semantics (permanent) | Data retention/rotation/archival contract. **Required for append-only, high-write-rate objects** — a `telemetry`/`transient`/`event`/`audit` class must declare a bounding policy or parsing fails (see [Data Lifecycle & Retention](./rules/lifecycle.md)) |

‎skills/objectstack-data/rules/relationships.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,8 @@ InvoiceLine = {
293293
- **Computed column** — a *stored* `currency`/`number` field with an `expression`
294294
renders read-only and recomputes live in the grid, then persists. Keep it
295295
stored (NOT a `formula` field) so the parent `summary` can roll it up; the
296-
server only treats `type: 'formula'` as computed, so on a stored field the
297-
`expression` is a client compute hint and the sent value is stored verbatim.
296+
engine computes a stored field's `expression` from the row the write stores
297+
and stores the result, so a client-sent value never stands.
298298
- **Catalog auto-fill** — a `lookup` line field + sibling columns whose names
299299
match fields on the referenced record (e.g. `unit_price`, `description`) →
300300
picking a record fills those cells.

0 commit comments

Comments
 (0)