Repository navigation
fix(lint): a composite record title that must be queried is a stored computed field; a formula title is for display only - #25127
Conversation
…computed field; a formula title is for display only The title-format-retired and title-unresolvable diagnostics, the object/missing-name-field fix line, the os explain text, ADR-0079 D3's migration sentence and the formulas.mdx worked example no longer steer a composite title that import, sort or search must match into a formula field. Wording only: no verdict, severity, code, key or export moves. Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
…mposite-title-stored
…ter bound Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz Co-authored-by: Claude <noreply@anthropic.com>
…mposite-title-stored
…e wording fix Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 36a9b5f3d737aea70405a052059f175bd5d1bc53 && git checkout 36a9b5f3d737aea70405a052059f175bd5d1bc53
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin fccbd9d0d5f6637111ec9e3f17a723fe84c6903a a16a9c02bcb59793cc651d78a5c542a87f67cb34 && git checkout -B drift-repro fccbd9d0d5f6637111ec9e3f17a723fe84c6903a && git merge --no-ff a16a9c02bcb59793cc651d78a5c542a87f67cb34
node scripts/docs-audit/affected-docs.mjs --json fccbd9d0d5f6637111ec9e3f17a723fe84c6903a |
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz
Contract reviewServed-tier: ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Seat re-read of the check-runs before posting: all 42 runs on |
Closes #25071
Clause-②: no (wording only: no verdict, severity, code, key or export moves)
Metadata-protocol: none
Governance: Tier H. This diff touches
docs/adr/**(ADR-0079 D3's migration sentence), a governed surface, so it lands only on an authorized APPROVED review. It stays a draft until then.What changes
Every sentence the card lists now gives the same remedy. A composite record title that must be queried (matched by an import, sorted, searched) is a stored computed field: a non-formula field (such as a
textfield) that declares anexpression, which the engine computes and stores on every write. That field is designated asnameField. Aformulafield withreturnType: 'text'stays a legalnameFieldfor a title shown for display only.packages/lint/src/validate-record-title.tstitle-format-retiredmessage and hintpackages/lint/src/validate-record-title.tstitle-unresolvablehintpackages/lint/src/rule-explanations.tstitle-format-retiredexplanationpackages/lint/src/data-model-rules.tsobject/missing-name-fieldfix linereturnType: 'text'formula"expressionfor a queried composite; text formula is display-only. Still inside the 200-character fix-line bounddocs/adr/0079-record-display-name.mdD3 migration sentence onlycontent/docs/data-modeling/formulas.mdx~:99 commentNo finding fires on a different object, and no severity, rule code, key or export changes. Only the text an author reads changes.
The shape is real on origin/main
packages/objectql/src/engine.ts:3028-3050,planStoredExpressions: every field that is not aformulaand declares anexpression(def.type !== 'formula' && def.expression, :3046) is computed on every write and stored in its own column (18.0 round-2 sweep framework findings: a paid invoice line'samount/totalstays writable while its inputs are locked; an unevaluable hook condition answers 500 INTERNAL_ERROR;remote-tables?schema=never narrows on the SQL drivers #24619). Write paths call it at :16210 (insert) and :17559 (update).packages/rest/src/import-reference-stored-computed-title.test.ts(on main) pins the import side end to end: a lookup cell matches a target titled by a stored computed field, and the same title declared as a formula answersreference_not_found(ruling 6308445871).docs/adr/0079-record-display-name.md:400-406): a formula value is stored nowhere, a sort by it is refused (400 INVALID_SORT), and search cannot reach it.Tests
The two suites that pin the exact strings are updated:
packages/lint/src/validate-record-title.test.tsandpackages/lint/src/data-model-rules.fix-lines.test.ts. Both are needed because the shipped text is pinned verbatim there (the fix-line suite also enforces the 200-character bound).Measured at head
e9e0ec7bab, after merging origin/main03966ceae0:pnpm --filter '@objectstack/lint^...' build, under the verify lock:VERDICT command-exit 0.pnpm --filter @objectstack/lint test:Test Files 153 passed (153),Tests 7085 passed (7085).pnpm --filter @objectstack/lint typecheck(includingcheck:test-typecheck):VERDICT command-exit 0.node scripts/pm/dispatch-gates.mjs --ranwith exit codes recorded:100 derived famil(ies) accounted for — 99 run, 1 NOT-MEASURED. Exit 0 on every gate run, includingcheck:docs,check:doc-authoring,check:doc-formula-expressions,check-changeset-no-major,check-empty-changeset,check-adr-0087-registration,check:changeset-gate-self-tests,check-adr-links,check:adr-anchorsandcheck:nul-bytes.dist, because a sibling build was rebuilding at the same time. After the build they were re-run and exited 0:check:skill-examples(262 prose examples type-check),check:dts-closure(128/128 declarations present) andcheck:lean-entry-closure.check:dual-build-cjs-loads, reason: it needs every package'sdist, which means a whole-repo build, and that is left to CI. The diff changes no build output shape.scripts/pm/clause2-line.mjsreads the changeset declarations as{"value":"no","arm":null}and{"value":"none"}.Changeset
@objectstack/lintpatch. The text of a shipped diagnostic changes, but nothing a metadata author can write changes, so this isMetadata-protocol: none. Under AGENTS.md Post-Task Checklist item 3,nonetakespatch.Acceptance notes
The same "composite title is a text formula" advice exists in other places that this card did not claim, so this PR leaves them alone. They are listed in the dev report for the seat to file or route:
packages/spec/src/data/object.zod.ts:2843: thetitleFormatdescribe says "a composite to a formula field designated as nameField". The generated reference pages repeat it:content/docs/references/data/object.mdx:167,content/docs/references/api/metadata.mdx:964andcontent/docs/references/system/migration.mdx:349,643. The reconciliation notepackages/spec/src/system/metadata-form-zod-reconciliation.test.ts:404quotes it too.skills/objectstack-data/SKILL.md:90: "for a composite title, designate areturnType: 'text'formula field asnameField".Open PR #25074 also edits
packages/lint/src/rule-explanations.ts, but in a different region (around lines 3562 and 4880; this PR edits around 2644), so the two do not conflict.维护者速读(草稿)
os explain说明、ADR-0079 D3 迁移句和一处文档注释,措辞统一为:需要被导入匹配、排序或搜索的组合标题应使用"存储型计算字段"(非 formula 字段加expression,写入时计算并落库);text formula 标题仍合法,但只用于展示。docs/adr/**,属 Tier H,需要一次授权的 APPROVED 评审后才能落地。Generated by Claude Code