Skip to content

Commit 0f372a1

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-25131-mysql-fixtures-pre18-tenant-column
2 parents d714469 + 459afea commit 0f372a1

8 files changed

Lines changed: 40 additions & 19 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/lint': patch
3+
---
4+
5+
fix(lint): the record-title diagnostics no longer steer a composite title that must be queried into a formula field
6+
7+
Clause-②: no (wording only: no verdict, severity, code, key or export moves)
8+
Metadata-protocol: none
9+
10+
The `title-format-retired` and `title-unresolvable` messages and fix lines, the `object/missing-name-field` fix line, and the `os explain title-format-retired` text now say the same thing: a composite record title that an import, a sort or a search must match is a stored computed field, a non-formula field (such as a `text` field) that declares an `expression`, which the engine computes and stores on every write, designated as `nameField`. A `formula` field with `returnType: 'text'` stays a legal `nameField` for a title shown for display only: its 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.
11+
12+
No finding fires on a different object than before, and no severity changes. Only the text an author reads changes.

‎content/docs/data-modeling/formulas.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ import { ObjectSchema, Field } from '@objectstack/spec/data';
9696
export const Invoice = ObjectSchema.create({
9797
name: 'invoice',
9898
sharingModel: 'private',
99-
nameField: 'display_title', // ADR-0079 — the record title is a designated field; composite titles migrate off the deprecated `titleFormat` to a text formula
99+
nameField: 'display_title', // ADR-0079 — the record title is a designated field; a composite title migrates off the deprecated `titleFormat` to a text formula for display only, or to a stored computed field (a non-formula field with an `expression`, written on write) when import, sort or search must match it
100100
fields: {
101101
// Composite record title as a text formula, surfaced via `nameField` above.
102102
display_title: Field.formula({

‎docs/adr/0079-record-display-name.md‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,8 +138,12 @@ the diagnostic is advisory, not an error:
138138
> consumed by the release)
139139
140140
**Migration is stated, not left to the author**: a single-field title becomes
141-
`nameField: '<field>'`; a **composite** title becomes a `formula` field with
142-
`returnType: 'text'`, designated as the `nameField`
141+
`nameField: '<field>'`; a **composite** title that must be queried (matched by
142+
an import, sorted, searched) becomes a stored computed field (a non-formula
143+
field with an `expression`, written on write), designated as the `nameField`,
144+
the remedy addendum item 9 states for sort and search and ruling record
145+
6308445871 extends to import; a `formula` field with `returnType: 'text'`
146+
designated as the `nameField` stays legal for a title shown for display only
143147
(`packages/lint/src/validate-record-title.ts`; the worked example is
144148
`content/docs/data-modeling/formulas.mdx`).
145149

‎packages/lint/src/data-model-rules.fix-lines.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ describe('[#22161] data-model-rules.ts — one remedy line per fix', () => {
150150
it('object/missing-name-field: the pointer, and where the titleFormat reasoning lives', () => {
151151
const fix = fixOf(record(lintDataModel([{ name: LONG_OBJECT, fields: { amount: { type: 'number' } } }])), MISSING_NAME_FIELD);
152152
expect(fix).toBe(
153-
"Set `nameField: '<field>'` (ADR-0079's title pointer) to a stored text/autonumber field or a " +
154-
"`returnType: 'text'` formula; `titleFormat` does not count (`os explain title-format-retired`).",
153+
"Set `nameField` (ADR-0079), never `titleFormat` (`os explain title-format-retired`); a composite title " +
154+
"to import/sort/search by is a text field with an `expression` (a text formula is display-only).",
155155
);
156156
// The command the line names answers, and says why `titleFormat` does not count.
157157
expect(explainRule('title-format-retired')!.paragraphs.join('\n')).toContain('render-only');

‎packages/lint/src/data-model-rules.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -738,8 +738,8 @@ export function lintDataModel(objects: any[]): LintIssue[] {
738738
// [#22161] One remedy line; why `titleFormat` does not count is
739739
// `title-format-retired`'s `os explain` text.
740740
fix:
741-
"Set `nameField: '<field>'` (ADR-0079's title pointer) to a stored text/autonumber field or a " +
742-
"`returnType: 'text'` formula; `titleFormat` does not count (`os explain title-format-retired`).",
741+
"Set `nameField` (ADR-0079), never `titleFormat` (`os explain title-format-retired`); a composite title " +
742+
"to import/sort/search by is a text field with an `expression` (a text formula is display-only).",
743743
});
744744
}
745745

‎packages/lint/src/rule-explanations.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2644,9 +2644,13 @@ const TITLE_FORMAT_RETIRED_EXPLANATION: RuleExplanation = {
26442644
'alias), or one derived from the object\'s fields. `titleFormat` is a render-only template: the ' +
26452645
'server can neither return the title it draws nor query by it, the shared title predicate ' +
26462646
'(`objectTitleCompleteness`) never reads it, and an explicit `nameField` takes precedence over it.',
2647-
'For a single-field title, set `nameField` to that field. For a composite title, add a formula ' +
2648-
'field with `returnType: \'text\'` and point `nameField` at it: a formula whose result is text is ' +
2649-
'title-eligible. Then drop `titleFormat`, which this warning reports for as long as it is declared.',
2647+
'For a single-field title, set `nameField` to that field. A composite title that must be queried ' +
2648+
'(matched by an import, sorted, searched) is a stored computed field: a non-formula field, such as ' +
2649+
'a `text` field, that declares an `expression`, which the engine computes and stores on every ' +
2650+
'write; point `nameField` at it. A formula with `returnType: \'text\'` is title-eligible too and ' +
2651+
'stays legal for a title shown for display only: its value is computed on read and stored nowhere, ' +
2652+
'so a sort by it is refused, search never reaches it, and an import cannot match a lookup cell ' +
2653+
'against it. Then drop `titleFormat`, which this warning reports for as long as it is declared.',
26502654
'A warning, not an error: the schema still parses the key, so existing metadata keeps loading.',
26512655
],
26522656
};

‎packages/lint/src/validate-record-title.test.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,12 @@ describe('[#22161] one-line fixes — title-format-retired and title-unresolvabl
131131
const findings = validateRecordTitle(objStack({ titleFormat: '{{record.amount}}', fields: { amount: { type: 'currency' } } }));
132132
const byRule = new Map(findings.map((f) => [f.rule, f.hint]));
133133
expect(byRule.get(TITLE_FORMAT_RETIRED)).toBe(
134-
"Set nameField: '<field>' for a single-field title, or add a formula field (returnType: 'text') and " +
135-
'designate it via nameField; then drop titleFormat',
134+
"Set nameField: '<field>' for one field; a composite title to import, sort or search by is a text " +
135+
'field with an expression, stored on write (a text formula is display-only); drop titleFormat',
136136
);
137137
expect(byRule.get(TITLE_UNRESOLVABLE)).toBe(
138-
"Set nameField to a text/email field (or a formula field with returnType: 'text'), or add a text " +
139-
'field named "name"/"title"',
138+
'Set nameField to a text/email field (a composite title to import, sort or search by: a text field ' +
139+
'with an expression; a text formula is display-only), or add a text field named "name"/"title"',
140140
);
141141
});
142142

‎packages/lint/src/validate-record-title.ts‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,12 +79,13 @@ export function validateRecordTitle(stack: AnyRec): RecordTitleFinding[] {
7979
path,
8080
message:
8181
`${objName}: titleFormat is retired (ADR-0079) — migrate to nameField ` +
82-
`(single field) or a formula field designated nameField`,
82+
`(single field); a composite title that import, sort or search must match is a stored ` +
83+
`field with an expression designated nameField (a formula title is for display only)`,
8384
// [#22161] One remedy line; why the template is retired (render-only,
8485
// neither returned nor queryable) is `os explain title-format-retired`.
8586
hint:
86-
`Set nameField: '<field>' for a single-field title, or add a formula field (returnType: 'text') ` +
87-
`and designate it via nameField; then drop titleFormat`,
87+
`Set nameField: '<field>' for one field; a composite title to import, sort or search by is a ` +
88+
`text field with an expression, stored on write (a text formula is display-only); drop titleFormat`,
8889
});
8990
}
9091

@@ -104,8 +105,8 @@ export function validateRecordTitle(stack: AnyRec): RecordTitleFinding[] {
104105
// [#22161] One remedy line; the runtime's fallback title, and why this
105106
// is only a warning, are `os explain title-unresolvable`.
106107
hint:
107-
`Set nameField to a text/email field (or a formula field with returnType: 'text'), or add a ` +
108-
`text field named "name"/"title"`,
108+
`Set nameField to a text/email field (a composite title to import, sort or search by: a text ` +
109+
`field with an expression; a text formula is display-only), or add a text field named "name"/"title"`,
109110
});
110111
}
111112
}

0 commit comments

Comments
 (0)