Repository navigation
Commit f108e6d
feat(spec): a field declares the key(s) it was renamed from —
Fixes #30880
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: follow-up #30885 — `skills/**` does not teach the key yet:
it is declared ahead of the upgrade step that carries the data (rename
execution, #30885), and that step owns the skills line; this diff
documents it in `content/docs/data-modeling/fields.mdx` and the
generated references
## 维护者速读
- 改了什么:字段定义新增可选属性 `renamedFrom: string[]`,包作者在新字段上写旧名(如 `renamedFrom:
['company']`),并加五条最小校验(旧名须合法、不能是自己、不能和本对象现存字段重名、不能被两个字段同时认领、列表内不重复),违者在解析时拒收并点名对象、字段和旧名。
- 为什么改:按你在 cloud#3652 的裁决(Q3「默认拒绝,需显式映射(推荐)」与「写进清单
renamedFrom」),改名映射写在清单里;这推翻了 #24443 裁决 A 第 5 条「规范里不加改名写法」。本 PR
只做协议这一层,真正搬数据的是 #30885,升级预览读它是 #30884。
- 风险与回滚:不写这个键的对象解析结果不变;`FieldSchema` 是严格模式,带 `renamedFrom`
的制品会被比本版本旧的运行时拒收(未知键)。在 #30885 落地前,这个键不会让任何升级搬数据。回滚即还原本 PR。
-
先看:`packages/spec/src/data/object.zod.ts`(`refuseDishonestRenamedFrom`,四条整表校验)、`packages/spec/src/data/field.zod.ts`(新键与
describe 文案)、`packages/spec/liveness/field.json`(`planned` 行与翻转条件)。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run
src/data/field-renamed-from.test.ts`,预期 11 个用例全绿。
- 需要你做的:无,席位落地(落地前席位先做一次契约复核)。
## What changes
- **`FieldSchema.renamedFrom: string[]`, optional**
(`packages/spec/src/data/field.zod.ts`, at the tail of the shape so the
abbreviated type summaries in the references do not move). Each entry is
an earlier key of the same field, in the field-key grammar
`^[a-z_][a-z0-9_]*$`; a non-key entry is refused by `FieldSchema` itself
with a message naming the entry. The `.describe()` says what the key
means, that it is read only at package upgrade, and that it is declared
ahead of the upgrade step that carries the data.
- **`refuseDishonestRenamedFrom`** in `ObjectSchema`'s `superRefine`,
beside `refuseStageFieldWithoutKinds`
(`packages/spec/src/data/object.zod.ts`). One located `custom` issue at
`fields.FIELD.renamedFrom.INDEX` per offending entry, naming the object,
the field and the old key, when the entry is the field's own key,
repeats an earlier entry of the same list, is a key another field of the
object still declares, or is an old key an earlier field already claims
(reported on the later claimant, naming the first). The own-key and
repeat checks sit here rather than in `FieldSchema` because
`FieldSchema.name` is optional (the map key is the field's key) and
because the card asks every refusal to name the object.
- **Liveness:** `renamedFrom` is a `planned` row in
`packages/spec/liveness/field.json`, naming #30885 (rename execution) as
the reader that flips it to `live` with its `file#symbol` pointer, and
#30884 (upgrade plan) as a second reader. No `authorWarn`, as the card
states. `liveness/state-counts/field.md` regenerated.
- **Mirrors the gates require:** the authorable-surface shard
(`packages/spec/authorable-surface/data.json`, +1 row), the generated
references (`content/docs/references/**`), the field-form omission
ledger (`metadata-form-zod-reconciliation.test.ts`: not offered in the
field form while `planned`, the `conditionalFormatting` precedent), the
two maps pinned exhaustive over `FieldSchema` (`FIELD_KEY_STORAGE_CLASS`
in driver-sql as `storage`; `FIELD_REFERENCE_POSITIONS` in metadata-core
as `keep`, since the parse refuses any entry naming a live field), and
the `/meta/types` field property count pin (82 to 83).
- **Docs:** one row under a new "Package upgrades" heading in
`content/docs/data-modeling/fields.mdx`.
- **Changeset:** `@objectstack/spec` minor, `@objectstack/driver-sql`
and `@objectstack/metadata-core` patch.
Not added, per the card: an object-level or package-level rename map,
renames of objects or other metadata types, type compatibility checking
(the upgrade plan judges it against the installed version), and any
runtime behaviour.
### Rulings this lands
- Epic objectstack-ai/cloud#3652, maintainer rulings (record on the
epic): Q3 「默认拒绝,需显式映射(推荐)」, and on where the mapping lives, 「写进清单
renamedFrom」.
- These supersede item 5 of objectstack#24443's ruling A (record
6259332195): "No rename spelling is added to the spec". Items 1 to 4 of
that record stand. The card notes that no ADR text forbids the key
(ADR-0126's dated note describes the access stages, it is not a rule),
so no ADR amendment rides here.
### Publish-time consequence, for the contract review
`FieldSchema` is strict. An artifact that carries `renamedFrom` is
refused, as an unrecognized key, by every runtime older than this
release. A package that adds the key therefore needs its installing
runtime at this release or later; a package that does not write it is
unaffected. The changeset says the same.
## Tests
- New `packages/spec/src/data/field-renamed-from.test.ts` (11 cases):
one and two earlier keys parse and are kept as written; an object
without the key parses with no `renamedFrom` materialized on any field;
four grammar refusals at `renamedFrom.0`; each of the four map-half
refusals pinned by `code` and `path` and by its sentence (object, field,
old key, reason clause).
- `os validate` acceptance, run by hand with the built CLI against two
throwaway `defineStack` fixtures (removed after): `renamedFrom:
['company']` exits 0; `renamedFrom: ['status']` beside a live `status`
field exits 1 with `objects.0.fields.account_name.renamedFrom.0: Field
account_name on object crm_account declares renamedFrom entry 'status',
but object crm_account still declares a field status: that name is still
in use, so no field was renamed away from it. ...`
## Acceptance notes
- **The key is inert until #30885.** Between this landing and the rename
execution, an upgrade that meets `renamedFrom` does what it does today
(additive sync). The describe, the docs row and the changeset each say
so; the ledger row's note records the card's plan that #30884 refuses a
declared rename by name until #30885 lands. That refusal is #30884's to
build: until it lands, nothing refuses either.
- **Extension fields get the field half only.**
`ObjectExtensionSchema.fields` parses `FieldSchema`, so an extension
field may carry `renamedFrom` with its grammar judged, but the map half
runs only in `ObjectSchema`'s refinement, where the whole field map is
visible. The card scopes the checks to the object; no extension
behaviour is added.
- **The grammar refusal names the entry, not the object or field.**
`FieldSchema` sees one field at a time and `name` is optional; the issue
path locates it. Every map-half refusal names all three.
- **Empty list:** `renamedFrom: []` parses (no rename declared). The
card's minimal list does not refuse it, so this PR does not.
- **One ablation per rule** (each through
`scripts/ablation-replace.mjs`, anchor hit x1 to x0, restored, `git diff
HEAD` empty after the run): grammar, repeat, live key and double claim
each turned their pin red on the first run. The own-key ablation stayed
green on its first run: the pin's `toContain("the field's own key")`
also matched the shared prescription sentence, so the assertion was
vacuous. The pin now asserts the reason clause, and the re-run ablation
turned it red (`refuses the field's own key` failed, 1 of 11).
- **`Metadata-protocol:` line.** The claim carried `Metadata-protocol:
none`; this body declares `added`, because AGENTS.md defines a new
authorable key as `added` and `check-changeset-no-major` refuses `none`
beside a changed `authorable-surface/` shard. Reported to the seat.
- `origin/main` moved 10 commits after the branch point; none touches
`packages/spec`, `packages/metadata-core`,
`packages/drivers/driver-sql`, `packages/metadata-protocol` or
`content/docs` (only other cards' changesets and an ADR anchor), so the
branch was not merged before opening.
## Verification
All readings at head `4ad4222ab8` unless stated.
<details>
<summary>packages/spec full suite and typecheck (verify lock)</summary>
- `pnpm exec vitest run --maxWorkers=2` in `packages/spec`: exit 0,
`Test Files 909 passed (909)`, `Tests 27388 passed | 1 todo (27389)`.
- `pnpm typecheck` in `packages/spec`: exit 0.
- An earlier full run at `e3c18ce2a8` was red on exactly one test,
`metadata-form-zod-reconciliation.test.ts` (the field form offered no
control for `renamedFrom`); fixed by the omission-ledger row, then
green.
</details>
<details>
<summary>Other packages the diff touches (verify lock)</summary>
- metadata-core `object-schema-fls-references.test.ts` +
`objects/builtin-column-storage-attributes.test.ts`: exit 0, 67 passed.
- driver-sql `builtin-column-collision.test.ts`: exit 0, 11 passed.
- metadata-protocol `protocol.meta-types-degenerate-derivation.test.ts`:
exit 0, 18 passed (red at 82 before the count pin moved to 83).
- cli `test/data-model-rules.test.ts` (unit project): exit 0, 56 passed.
lint `validate-expressions`, `validate-security-posture`,
`validate-expressions.formula-one-hop`, `validate-predicate-path-refs`:
exit 0, 737 passed (both at `4ad4222ab8`'s parent commits; the commits
after touch only test files of other packages).
- `typecheck` for metadata-core, driver-sql, metadata-protocol: exit 0
each.
</details>
<details>
<summary>Derived gates (dispatch-gates --commands at 4ad4222, 106
families)</summary>
- 105 exit 0, 1 exit 3 (NOT MEASURED): `pnpm check:dual-build-cjs-loads`
refuses without a full `pnpm build` of every package; it runs in CI.
- `dispatch-gates --ran` with exit codes recorded: `✓ 106 derived
famil(ies) accounted for — 105 run, 1 NOT-MEASURED (1 DERIVED from a
recorded exit 3)`.
- `pnpm --filter @objectstack/spec check:generated`: `✓ All 14 generated
artifacts are up to date`. `check:liveness`: `field 112 classified (live
105, planned 6, dead 1)`, every row classified, no orphan.
- Families CI runs on this PR (Lint & Repo Gates, TypeScript Type Check,
Test Core, Build Core and the rest) are not re-run here; their
check-runs are the reading.
</details>
---
_Generated by [Claude
Code](https://claude.ai/code/session_01Q4EjjRhUZjdy8VXs6Kkur2)_
---------
Co-authored-by: Claude <noreply@anthropic.com>renamedFrom on FieldSchema (#30934)1 parent 33043bd commit f108e6d
15 files changed
Lines changed: 294 additions & 2 deletions
File tree
- .changeset
- content/docs
- data-modeling
- references
- data
- system
- packages
- drivers/driver-sql/src
- metadata-core/src
- metadata-protocol/src
- spec
- authorable-surface
- liveness
- state-counts
- src
- data
- system
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
458 | 458 | | |
459 | 459 | | |
460 | 460 | | |
| 461 | + | |
| 462 | + | |
| 463 | + | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
461 | 467 | | |
462 | 468 | | |
463 | 469 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
125 | 125 | | |
126 | 126 | | |
127 | 127 | | |
| 128 | + | |
128 | 129 | | |
129 | 130 | | |
130 | 131 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
294 | 294 | | |
295 | 295 | | |
296 | 296 | | |
| 297 | + | |
297 | 298 | | |
298 | 299 | | |
299 | 300 | | |
| |||
666 | 667 | | |
667 | 668 | | |
668 | 669 | | |
| 670 | + | |
669 | 671 | | |
670 | 672 | | |
671 | 673 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
128 | 128 | | |
129 | 129 | | |
130 | 130 | | |
| 131 | + | |
131 | 132 | | |
132 | 133 | | |
133 | 134 | | |
| |||
562 | 563 | | |
563 | 564 | | |
564 | 565 | | |
| 566 | + | |
565 | 567 | | |
566 | 568 | | |
567 | 569 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
| 90 | + | |
90 | 91 | | |
91 | 92 | | |
92 | 93 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
636 | 636 | | |
637 | 637 | | |
638 | 638 | | |
| 639 | + | |
| 640 | + | |
| 641 | + | |
| 642 | + | |
| 643 | + | |
639 | 644 | | |
640 | 645 | | |
641 | 646 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
364 | 364 | | |
365 | 365 | | |
366 | 366 | | |
| 367 | + | |
| 368 | + | |
367 | 369 | | |
368 | 370 | | |
369 | 371 | | |
| |||
372 | 374 | | |
373 | 375 | | |
374 | 376 | | |
375 | | - | |
| 377 | + | |
376 | 378 | | |
377 | 379 | | |
378 | 380 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
344 | 344 | | |
345 | 345 | | |
346 | 346 | | |
| 347 | + | |
347 | 348 | | |
348 | 349 | | |
349 | 350 | | |
| |||
0 commit comments