Repository navigation
Commit 3557366
feat(spec)!: refuse an object that declares titleFormat at save and os validate, with the nameField prescription (#29499)
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9XPP2t6ZazH2FfFDug17NNH)_
Fixes #29007
Clause-②: no (narrowing)
Metadata-protocol: narrowed
## 维护者速读
1. 对象定义里再写 `titleFormat`,保存、发布、恢复和 `os validate` 都会直接拒绝,并提示改用
`nameField`。
2. 已存的旧对象照常读取和展示,不做任何数据转换;只有下次保存时才会被要求改掉。
3. 记录标题的取法不变:先看 `nameField`,没有就按原规则推导,`titleFormat` 一直没生效,现在只是不再假装接受。
4. 平台自带的 68 个系统对象已去掉 `titleFormat`;其中 3 个补上了本来就在用的标题字段,用户看到的标题不变。
5. 这是破坏性变更(BREAKING),升级指南已登记迁移说明,dogfood 全量回归已跑。
6. 草稿保存仍会落库但标记为无效、无法发布;是否连草稿也拒绝,留作待定问题。
## What changes
Ruling of record 6574393684, letter B: `titleFormat` is refused, not
deleted and not honoured.
- **One judge at every door.** `title-format-retired` moves out of the
advisory `validateRecordTitle` into a new gating registry rule,
`validateTitleFormatRetired`
(`packages/lint/src/validate-record-title.ts`, registered in
`authoring-rules.ts` with `surfaces: CLI_AND_RUNTIME`, `runtimeTypes:
['object']`). The registry is what `os validate`, `os build`, `os lint`
and the runtime publish gate already run, so the CLI and the metadata
save gate (`422 INVALID_METADATA` on an active save, a draft promotion
and a restore) refuse with the same finding. The message and the fix
line are byte-identical to the existing prescription; no new wording.
`validateRecordTitle` now reports `title-unresolvable` only.
- **Schema.** `object.zod.ts`: the `titleFormat` describe gains one
sentence (refused at the metadata save gate and by `os validate`; a
stored object that carries it still parses and reads). The key stays in
the schema; no conversion drops it; `provisionPrimary`, console ordering
and ADR-0079 D3/D9 are untouched.
- **ADR-0087.** New D3 semantic entry `object-title-format-refused`
(`packages/spec/src/migrations/entries/semantic/18.object-title-format-refused.ts`)
with the replacement, reason and acceptance criteria; the changeset
registers it.
- **Every code-shipped hit moves to `nameField`.** 68 system objects
across platform-objects, plugin-approvals, plugin-audit,
plugin-security, plugin-sharing, plugin-webhooks, service-automation,
service-mailbox, service-messaging, service-realtime and service-storage
drop `titleFormat`. 65 already declared the `nameField` that titles
them. `sys_attachment` (`file_name`), `sys_audience_binding_suggestion`
(`permission_set_name`) and `sys_notification_template` (`subject`) now
declare the field the registry already stamped, so every record title
reads as before. Comments that described the template were rewritten in
place. No example or template in the repo declared it.
- **Docs.** `data-modeling/objects.mdx`, `schema-design.mdx`,
`frontend/building-crud-app.mdx`,
`docs/notes/crm-development-standards.mdx`; references regenerated; four
CLI transcripts now print "author-time rules (62)"
(`deployment/cli.mdx`, `deployment/validating-metadata.mdx`,
`getting-started/build-with-claude-code.mdx`, `ui/react-pages.mdx`).
- **Liveness.** `packages/spec/liveness/sharing_rule.json`: the name
note no longer cites `titleFormat`; the label evidence now cites the
list view columns and sort keys.
### Forced test edits
These tests read `X.titleFormat` from the shipped object to prove a
formula matched the old template. The template no longer ships, so each
file now keeps the retired template text in a local
`RETIRED_TITLE_TEMPLATES` map and reads
`RETIRED_TITLE_TEMPLATES[X.name]`; the assertions are otherwise
unchanged:
-
`packages/platform-objects/src/identity/identity-display-title.test.ts`
-
`packages/plugins/plugin-approvals/src/sys-approval-delegation-display-title.test.ts`
-
`packages/plugins/plugin-approvals/src/sys-approval-display-title.test.ts`
(one comment adjusted)
-
`packages/plugins/plugin-security/src/objects/sys-security-assignment-display-title.test.ts`
-
`packages/services/service-automation/src/sys-automation-run-display-title.test.ts`
-
`packages/services/service-messaging/src/objects/http-delivery-display-title.test.ts`
-
`packages/services/service-messaging/src/objects/notification-display-title.test.ts`
-
`packages/services/service-realtime/src/objects/sys-presence-display-title.test.ts`
`packages/plugins/plugin-approvals/src/sys-approval-request-title.integration.test.ts`
asserted the shipped template; it now asserts `titleFormat` is undefined
on the shipped object (test renamed accordingly).
## Tests
- `packages/lint/src/validate-record-title.test.ts`: the refusal is an
error with the prescription, the envelope and controls; the explain text
names the save gate and `os validate`.
- `packages/lint/src/runtime-gate.object-writes.test.ts`: the rule is on
the object door roster; an object carrying `titleFormat` is refused;
re-saving a stored row that carries it is refused, and the healed row
saves.
-
`packages/metadata-protocol/src/protocol.draft-publish-verdict.test.ts`:
a `title-format-retired` case in the publish verdict table; a stored
object carrying `titleFormat` still loads, its next save is refused 422
with the prescription, and the `nameField` control saves.
- `packages/cli/test/validate-title-format-retired.test.ts` (new,
integration): `os validate --json` exits 1 with `title-format-retired`
and "migrate to nameField"; the `nameField` control exits 0.
-
`packages/platform-objects/src/managed-api-method-affordance-sweep.test.ts`:
a repo-wide pin that no code-shipped object declares `titleFormat`, with
a red fixture and a control.
## Acceptance notes
- **Draft saves.** Per the publish-gate contract (#4463 D1) a draft save
is not gated: it still stores, its `_diagnostics` reads `valid: false`
with this refusal, and its promotion is refused. The ruling names the
save gate; I read that as the gate that already exists. Refusing drafts
too would be a gate-wide change, so it is left as an open question.
- **Out of scope (governed).** `skills/objectstack-data/SKILL.md` still
calls `titleFormat` "Deprecated, not removed". It is a governed path, so
this PR leaves it for a follow-up.
- **Still-parsed readers kept.** The metadata-core FLS contract fixture
and the ui renderers still read the key; the ruling keeps it parseable,
so they stay.
- **Title text.** Dropping a template changes only the blank-pointer
fallback text on objects whose `nameField` value is empty; main-path
titles are unchanged (measured per object).
## Verification
**Round 2 (head `98601175`, main merged through `os-regen-merge.sh`).**
Contract review FAIL fixes: the declaration line is now
`Metadata-protocol: narrowed` (a refused value is narrowed), and the
four CLI transcripts print 62, the count `check:docs-transcript-drift`
derives on the merged tree (#29424 landed one more rule on main).
References were regenerated from the merged tree. On this head: lint,
spec, platform-objects, plugin-security, service-automation,
metadata-protocol and cli suites green (cli 6879 passed / 9 skipped);
typecheck green for cli, lint and metadata-protocol; `check:generated`
green; dogfood battery green (397 files passed / 3 skipped; 3277 tests
passed / 28 skipped; lock VERDICT command-exit 0); `dispatch-gates
--ran` 113 derived / 113 run / 0 NOT-MEASURED / 0 red;
`check-changeset-no-major --event` and `check-adr-0087-registration`
exit 0.
**Round 1** readings, on head `0885931864`, under `os-verify-lock` (slot
`card-29007`).
- **Package suites.** These were all green: lint 7834, spec 23451 (1
todo), platform-objects 1505, plugin-approvals 1381, plugin-audit,
plugin-security, plugin-sharing, plugin-webhooks, service-automation,
service-mailbox, service-messaging, service-realtime, service-storage,
metadata-protocol and cli (6871 passed, 9 skipped, unit and
integration).
- **Dogfood battery** (`pnpm --filter @objectstack/dogfood test`): 393
files passed and 3 skipped; 3243 tests passed and 28 skipped.
- **Typecheck.** `run typecheck` passed for all 15 touched packages,
including the test layers. The cli test layer ran out of memory at the
default node heap; with the CI heap (`--max-old-space-size=4136`) it
passed.
- **Derived gates.** `dispatch-gates --ran` reports 113 derived, 113
run, 0 NOT-MEASURED and 0 red.
- The first pass found `check:docs-transcript-drift` stale: four CLI
transcripts print "author-time rules (60)", and the new gating rule
makes the count 61. This PR fixes those transcripts.
- `check-plugin-teardown-shape --self-test` exited 3 in the shallow
clone. It went green after fetching its pinned commit.
- **Clause-② and Metadata-protocol lines.** These were checked offline
with `check-changeset-no-major.mjs --base origin/main --event` against
this body, which accepts `no (narrowing)` with `narrowed` (exit 0).
- **Ablation.** The new rule's severity was changed from `'error'` to
`'warning'` and the dist preflight was rerun. That turned lint red (5
tests), metadata-protocol red (2) and the cli `os validate` test red
(1). Restoring the severity made them green again, with a clean tree and
the preflight `--absent` check passing.
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01ADHkmQaszscubbRkbhxD9k
<!-- signed: domain:spec#1 · os-steve · session_01ADHkmQaszscubbRkbhxD9k
-->
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 0814b89 commit 3557366
101 files changed
Lines changed: 728 additions & 273 deletions
File tree
- .changeset
- content/docs
- data-modeling
- deployment
- frontend
- getting-started
- references
- api
- data
- system
- ui
- docs/notes
- packages
- cli/test
- lint/src
- metadata-protocol/src
- platform-objects/src
- audit
- identity
- system
- plugins
- plugin-approvals/src
- plugin-audit/src/objects
- plugin-security/src/objects
- plugin-sharing/src/objects
- plugin-webhooks/src
- services
- service-automation/src
- service-mailbox/src/objects
- service-messaging/src/objects
- service-realtime/src/objects
- service-storage/src/objects
- spec
- liveness
- src
- data
- migrations/entries/semantic
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
70 | | - | |
| 70 | + | |
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
59 | | - | |
| 59 | + | |
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
771 | 771 | | |
772 | 772 | | |
773 | 773 | | |
774 | | - | |
| 774 | + | |
775 | 775 | | |
776 | 776 | | |
777 | 777 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
760 | 760 | | |
761 | 761 | | |
762 | 762 | | |
763 | | - | |
| 763 | + | |
764 | 764 | | |
765 | 765 | | |
766 | 766 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
123 | 123 | | |
124 | 124 | | |
125 | 125 | | |
126 | | - | |
| 126 | + | |
127 | 127 | | |
128 | 128 | | |
129 | 129 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
310 | 310 | | |
311 | 311 | | |
312 | 312 | | |
313 | | - | |
| 313 | + | |
314 | 314 | | |
315 | 315 | | |
316 | 316 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
958 | 958 | | |
959 | 959 | | |
960 | 960 | | |
961 | | - | |
| 961 | + | |
962 | 962 | | |
963 | 963 | | |
964 | 964 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
162 | 162 | | |
163 | 163 | | |
164 | 164 | | |
165 | | - | |
| 165 | + | |
166 | 166 | | |
167 | 167 | | |
168 | 168 | | |
| |||
0 commit comments