Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .changeset/15437-validation-messages-migration-route.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
'@objectstack/spec': patch
---

The `translation-validation-messages-removed` migration text names the object-scoped bundle key, not just the authored literal

`validationMessages` was retired in 17.0.0 (#4667). The ADR-0087 conversion that
migrates it told an author to author the message on the rule
(`object.validations[].message`) and stopped there. Since 17.3.0 (#14381,
#14253) that message has a translation route —
`objects.<object_name>._validations.<rule_name>.message`, resolved on the write
path — and the sibling prescription ten metres away in the same package
(`TRANSLATION_KEY_GUIDANCE.validationMessages`, the text the strict door
returns) already names it.

⛔ Nothing the old text said was false, and none of it is deleted. The defect is
**silence**: this is the *migration* text, read by exactly the population that
authored the retired key — the authors who wanted their rule messages
translated — and it steered them to a plain authored literal without mentioning
that the bundle key now exists. The literal advice stays; the route is added
after it.

**Two texts in the file carried the narrow prescription, not one.** The
conversion's `summary` is the one the card named; the docblock above it asserted
that rule messages are *"not translated through a group"*, which would have sat
directly above the corrected summary. Both are completed. The docblock keeps its
17.0.0 sentence — still true of the retired key — and says what 17.3.0 changed,
including why the object-scoped group is not `validationMessages` returning (the
retired one was keyed by rule name at the top level, could not tell two objects'
rules apart, and had no reader).

**This is shipped, which is why it carries a changeset rather than
`skip-changeset`.** `packages/spec/src/conversions/registry.ts` is not a
`.zod.ts`, so it is not shipped as source — but two published paths move,
measured on the built tree rather than reasoned about:

- `dist` is in `files[]`, and the new sentence is emitted into six built files
(`dist/index.js` / `.mjs`, `dist/shared/index.js` / `.mjs`,
`dist/browser/index.js` / `.mjs`); a negative control string scored 0 on the
same tree. An author running `os migrate meta --from 16` reads the changed
notice out of that runtime string.
- `spec-changes.json` is itself listed in `files[]`, and it carries the summary
twice. It is generated (`gen:spec-changes`), and `check:generated` caught it
stale — the conversion registry feeds two generated artifacts, not one.

`docs/protocol-upgrade-guide.md` is the third, regenerated with
`gen:upgrade-guide` and verified by `check:upgrade-guide`; all three are
regenerated, never hand-edited.

⛔ No behaviour changes. The conversion id, its `apply`, its accept set and its
fixture are untouched; no authorable key is added or removed.
2 changes: 1 addition & 1 deletion docs/protocol-upgrade-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ Finally it removes the 'pdf' member of `view.exportOptions` formats (#8010, main
| `mapping-inert-keys-removed` | `mapping.extractQuery / mapping.errorPolicy / mapping.batchSize` | mapping keys 'extractQuery'/'errorPolicy'/'batchSize' removed (#4509 — no exporter reads a mapping, error handling belongs to the import request, and the write path sizes its own batches) | retired — `migrate meta` only |
| `book-translations-removed` | `book.translations / book.groups.translations` | book keys 'translations' (book-level and group-level) removed (#4667 — no resolver read them; the tree endpoint and portal render labels verbatim, so a localized book served its authoring locale to everyone). Localize the docs instead: `doc.translations` is live | retired — `migrate meta` only |
| `job-id-removed` | `job.id` | job key 'id' removed (#4667 — nothing read it; `name` is the job's identity everywhere, so two jobs differing only in `id` were the same job, and the key's own description advertised an override that did not exist) | retired — `migrate meta` only |
| `translation-validation-messages-removed` | `translation.validationMessages` | translation key 'validationMessages' removed (#4667 — no resolver read it, so a translated rule message was stored and never shown; #3778's migration table had been steering retired `errors:` authors into it). Author the message on the rule itself (`object.validations[].message`) | retired — `migrate meta` only |
| `translation-validation-messages-removed` | `translation.validationMessages` | translation key 'validationMessages' removed (#4667 — no resolver read it, so a translated rule message was stored and never shown; #3778's migration table had been steering retired `errors:` authors into it). Author the message on the rule itself (`object.validations[].message`), and translate it under the object-scoped group `objects.<object_name>._validations.<rule_name>.message`, which the write path resolves (17.3.0, #14381) | retired — `migrate meta` only |
| `datasource-config-driver-key-aliases` | `datasource.config` | datasource config keys → canonical per driver: sqlite 'file'/'database' → 'filename', postgres/mysql 'connectionString' → 'url' and 'user' → 'username', mongo 'uri' → 'url' and 'user' → 'username' (#4456 — driver-factory `??` fallback graduation) | retired — `migrate meta` only |
| `datasource-driver-mongo-to-mongodb` | `datasource.driver` | datasource driver id 'mongo' → 'mongodb' — the canonical id both boot hosts, the driver package and the published DRIVER_CATALOG already used (#6345) | live — protocol 17 loader accepts the old shape |
| `flow-node-script-branch-keys-removed` | `flow.node.script.config.actionType / flow.node.script.config.template / flow.node.script.config.recipients / flow.node.script.config.variables / flow.node.script.config.script` | script flow-node config keys 'actionType' (→ 'function' when it was shorthand for one; otherwise removed — 'email'/'slack' were logger-backed stubs that delivered nothing), plus 'template' / 'recipients' / 'variables' (fed those stubs) and 'script' (inline JS the runtime never executed) (#4343) | retired — `migrate meta` only |
Expand Down
4 changes: 2 additions & 2 deletions packages/spec/spec-changes.json
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@
},
{
"surface": "translation.validationMessages",
"to": "translation key 'validationMessages' removed (#4667 — no resolver read it, so a translated rule message was stored and never shown; #3778's migration table had been steering retired `errors:` authors into it). Author the message on the rule itself (`object.validations[].message`)",
"to": "translation key 'validationMessages' removed (#4667 — no resolver read it, so a translated rule message was stored and never shown; #3778's migration table had been steering retired `errors:` authors into it). Author the message on the rule itself (`object.validations[].message`), and translate it under the object-scoped group `objects.<object_name>._validations.<rule_name>.message`, which the write path resolves (17.3.0, #14381)",
"conversionId": "translation-validation-messages-removed",
"toMajor": 17
},
Expand Down Expand Up @@ -1423,7 +1423,7 @@
},
{
"surface": "translation.validationMessages",
"to": "translation key 'validationMessages' removed (#4667 — no resolver read it, so a translated rule message was stored and never shown; #3778's migration table had been steering retired `errors:` authors into it). Author the message on the rule itself (`object.validations[].message`)",
"to": "translation key 'validationMessages' removed (#4667 — no resolver read it, so a translated rule message was stored and never shown; #3778's migration table had been steering retired `errors:` authors into it). Author the message on the rule itself (`object.validations[].message`), and translate it under the object-scoped group `objects.<object_name>._validations.<rule_name>.message`, which the write path resolves (17.3.0, #14381)",
"conversionId": "translation-validation-messages-removed",
"toMajor": 17
},
Expand Down
12 changes: 11 additions & 1 deletion packages/spec/src/conversions/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3120,6 +3120,16 @@ const jobIdRemoved: MetadataConversion = {
* rewritten in the same change to say rule messages are authored on the rule
* (`object.validations[].message`), not translated through a group.
*
* Since 17.3.0 (#14381, #14253) there is a group again and that same guidance
* entry names it: `objects.<object_name>._validations.<rule_name>.message`,
* which the write path resolves. It is not `validationMessages` returning —
* that one was keyed by rule name at the TOP level, so it could not tell two
* objects' rules apart, and nothing read it; this one is object-scoped and the
* rule evaluator reads it through the existing `i18nService` channel. The
* sentence above is what 17.0.0 said and it stays true of the retired key; an
* author arriving at this conversion needs both halves, so the summary below
* carries the route too.
*
* Removed from the shared `translationDataShape()`, so it retires at BOTH doors
* at once — the bundle entry and the registered item. #3778's original guard
* ran on the item door only, which is exactly how the key survived this long in
Expand All @@ -3130,7 +3140,7 @@ const translationValidationMessagesRemoved: MetadataConversion = {
toMajor: 17,
retiredFromLoadPath: true,
surface: 'translation.validationMessages',
summary: "translation key 'validationMessages' removed (#4667 — no resolver read it, so a translated rule message was stored and never shown; #3778's migration table had been steering retired `errors:` authors into it). Author the message on the rule itself (`object.validations[].message`)",
summary: "translation key 'validationMessages' removed (#4667 — no resolver read it, so a translated rule message was stored and never shown; #3778's migration table had been steering retired `errors:` authors into it). Author the message on the rule itself (`object.validations[].message`), and translate it under the object-scoped group `objects.<object_name>._validations.<rule_name>.message`, which the write path resolves (17.3.0, #14381)",
apply(stack, emit) {
return mapCollection(stack, 'translations', (t, path) =>
stripKeys(t, ['validationMessages'], emit, path));
Expand Down
Loading