Skip to content

Commit 59e9b7c

Browse files
fix(lint): searchable-fields hints prescribe a stored field, not formula (#6673) (#6921)
Both authoring-time hints for a bad `searchableFields` entry told the author to mirror a related record's value onto a formula field — a fix that can never work: a `formula` field is virtual (no driver materializes a column for it), so a `$contains` predicate against one has nothing to scan, and a CEL formula only reads the record's own fields, so it cannot fetch the related title in the first place. - validate-searchable-fields.ts:347 (dotted-path entry, SEARCHABLE_FIELD_UNKNOWN): "copy the value onto a formula field here" -> "...a stored text field here." - validate-searchable-fields.ts:414 (lookup/master_detail outside the allowed set, SEARCHABLE_FIELD_UNSEARCHABLE): "mirror it onto a text/formula field" -> "mirror it onto a stored text field". Matches the prescription already shipped in content/docs/data-modeling/ schema-design.mdx and the objectstack-data / objectstack-ui skills (PR #6670, #6898) — the tool's own hint text now agrees with the corpus that quotes it. Pinned tests strengthened to assert the new wording and reject "formula" (reverse-verified: reverting either string to the old wording turns both tests red). Co-authored-by: Claude <noreply@anthropic.com>
1 parent b5404f4 commit 59e9b7c

3 files changed

Lines changed: 43 additions & 3 deletions

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
"@objectstack/lint": patch
3+
---
4+
5+
fix(lint): `searchable-field-unknown` / `searchable-field-unsearchable` prescribe a **stored** mirror, not a formula (#6673)
6+
7+
Both authoring-time hints for a bad `searchableFields` entry told the author to
8+
mirror a related record's value onto a formula field — a fix that can never
9+
work:
10+
11+
- FROM (dotted-path entry, e.g. `project_id.name`): "…expand the relation and
12+
search the related object, or copy the value onto **a formula field** here."
13+
- TO: "…or copy the value onto **a stored text field** here."
14+
15+
- FROM (a `lookup`/`master_detail` column outside the allowed set): "…mirror
16+
it onto **a text/formula** field here and declare that instead."
17+
- TO: "…mirror it onto **a stored text** field here and declare that instead."
18+
19+
A `formula` field is virtual — no driver materializes a column for it
20+
(`packages/objectql/src/engine.ts`, `driver-sql/src/schema-drift.ts`,
21+
`driver-turso/src/remote-transport.ts`), so a `$contains` predicate against one
22+
has nothing to scan. A CEL formula also only reads the record's own fields
23+
(`record.<field>`), so it cannot fetch the related title in the first place.
24+
Only the **stored** half of the old prescription ever worked; an author who
25+
followed it verbatim got metadata that passed both lint and the `#4254`
26+
runtime gate and then just never matched.
27+
28+
The corpus already prescribes the stored-field mirror everywhere else
29+
(`content/docs/data-modeling/schema-design.mdx`, the `objectstack-data` and
30+
`objectstack-ui` skills, PR #6670 / #6898) — this brings the tool's own hint
31+
text into agreement with it.
32+
33+
Message text only — no schema, rule id, severity, or runtime behaviour change.

packages/lint/src/validate-searchable-fields.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ describe('validateSearchableFields — dotted paths', () => {
213213
expect(findings).toHaveLength(1);
214214
expect(findings[0].path).toBe('objects[0].searchableFields[1]');
215215
expect(findings[0].hint).toContain("scans this object's own columns");
216+
// The prescription must be a STORED field — a `formula` field is virtual
217+
// (no driver materializes a column for it), so it can never be scanned.
218+
expect(findings[0].hint).toContain('copy the value onto a stored text field');
219+
expect(findings[0].hint).not.toContain('formula');
216220
});
217221
});
218222

@@ -315,8 +319,11 @@ describe('validateSearchableFields — list views that narrow the set', () => {
315319
expect(findings[0].message).toContain("type 'lookup'");
316320
expect(findings[0].message).toContain('400 INVALID_FIELD');
317321
// The lookup-specific prescription: search cannot cross objects, so the
318-
// related record's title must be mirrored onto a local text/formula field.
322+
// related record's title must be mirrored onto a local STORED text field —
323+
// never a `formula` field, which is virtual and materializes no column.
319324
expect(findings[0].hint).toContain('mirror');
325+
expect(findings[0].hint).toContain('mirror it onto a stored text field');
326+
expect(findings[0].hint).not.toContain('formula');
320327
});
321328

322329
it('flags a real field outside the object\'s declared searchableFields', () => {

packages/lint/src/validate-searchable-fields.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ export function checkSearchableFieldList(
344344
(dotted
345345
? `'search' scans this object's own columns, so a related record's ` +
346346
`column cannot be a search target — expand the relation and search ` +
347-
`the related object, or copy the value onto a formula field here. `
347+
`the related object, or copy the value onto a stored text field here. `
348348
: `Fix the name, or add "${name}" to ${objectName}.fields. `) +
349349
`Clients echo this declaration verbatim as the '$searchFields' ` +
350350
`override, so a stale entry becomes a 400 INVALID_FIELD on list ` +
@@ -411,7 +411,7 @@ export function checkSearchableFieldList(
411411
(isReference
412412
? `A ${meta?.type} column stores only the referenced record's id, so it ` +
413413
`cannot be a keyword target — drop "${name}" from this view and, to ` +
414-
`search by the related record's title, mirror it onto a text/formula ` +
414+
`search by the related record's title, mirror it onto a stored text ` +
415415
`field here and declare that instead. `
416416
: `Drop "${name}" from this view, or target a text-like field instead. `) +
417417
`Declaring 'searchableFields' on object "${objectName}" chooses the ` +

0 commit comments

Comments
 (0)