Skip to content

Commit 324968e

Browse files
os-billclaude
andauthored
docs(spec): name the object-scoped bundle key in the validationMessages migration text (#17604)
* docs(spec): name the object-scoped bundle key in the validationMessages migration text The ADR-0087 conversion `translation-validation-messages-removed` told an author whose retired `validationMessages` key was rejected to author the message on the rule and stopped there. Since 17.3.0 (#14381, #14253) there is a translation route for that message — `objects.<object_name>._validations.<rule_name>.message`, resolved on the write path — and the sibling prescription in the same package (`TRANSLATION_KEY_GUIDANCE.validationMessages`) already names it. Everything the old text said is true; the defect is silence. It is read by exactly the population that authored the retired key — the authors who wanted translated rule messages — and it steered them to a plain authored literal without telling them the bundle key now exists. Both texts in the file carry the narrow prescription, so both are completed: the conversion `summary` and the docblock above it, which asserted "not translated through a group" directly above the corrected summary. The literal advice is kept in both — it is still correct. `docs/protocol-upgrade-guide.md` is regenerated with `gen:upgrade-guide`, never hand-edited. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH * chore(spec): regenerate spec-changes.json and declare the changeset `check:generated` caught `spec-changes.json` stale: the ADR-0087 conversion registry feeds two generated artifacts, not one, and the completed summary renders into both. Regenerated with `gen:spec-changes`, never hand-edited. The changeset is measured, not assumed. `registry.ts` is not a `.zod.ts` so it does not ship as source, but two paths in `packages/spec`'s `files[]` move: the new sentence is emitted into six files under `dist`, and `spec-changes.json` is itself a `files[]` entry. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 5d39d5c commit 324968e

4 files changed

Lines changed: 65 additions & 4 deletions

File tree

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
The `translation-validation-messages-removed` migration text names the object-scoped bundle key, not just the authored literal
6+
7+
`validationMessages` was retired in 17.0.0 (#4667). The ADR-0087 conversion that
8+
migrates it told an author to author the message on the rule
9+
(`object.validations[].message`) and stopped there. Since 17.3.0 (#14381,
10+
#14253) that message has a translation route —
11+
`objects.<object_name>._validations.<rule_name>.message`, resolved on the write
12+
path — and the sibling prescription ten metres away in the same package
13+
(`TRANSLATION_KEY_GUIDANCE.validationMessages`, the text the strict door
14+
returns) already names it.
15+
16+
⛔ Nothing the old text said was false, and none of it is deleted. The defect is
17+
**silence**: this is the *migration* text, read by exactly the population that
18+
authored the retired key — the authors who wanted their rule messages
19+
translated — and it steered them to a plain authored literal without mentioning
20+
that the bundle key now exists. The literal advice stays; the route is added
21+
after it.
22+
23+
**Two texts in the file carried the narrow prescription, not one.** The
24+
conversion's `summary` is the one the card named; the docblock above it asserted
25+
that rule messages are *"not translated through a group"*, which would have sat
26+
directly above the corrected summary. Both are completed. The docblock keeps its
27+
17.0.0 sentence — still true of the retired key — and says what 17.3.0 changed,
28+
including why the object-scoped group is not `validationMessages` returning (the
29+
retired one was keyed by rule name at the top level, could not tell two objects'
30+
rules apart, and had no reader).
31+
32+
**This is shipped, which is why it carries a changeset rather than
33+
`skip-changeset`.** `packages/spec/src/conversions/registry.ts` is not a
34+
`.zod.ts`, so it is not shipped as source — but two published paths move,
35+
measured on the built tree rather than reasoned about:
36+
37+
- `dist` is in `files[]`, and the new sentence is emitted into six built files
38+
(`dist/index.js` / `.mjs`, `dist/shared/index.js` / `.mjs`,
39+
`dist/browser/index.js` / `.mjs`); a negative control string scored 0 on the
40+
same tree. An author running `os migrate meta --from 16` reads the changed
41+
notice out of that runtime string.
42+
- `spec-changes.json` is itself listed in `files[]`, and it carries the summary
43+
twice. It is generated (`gen:spec-changes`), and `check:generated` caught it
44+
stale — the conversion registry feeds two generated artifacts, not one.
45+
46+
`docs/protocol-upgrade-guide.md` is the third, regenerated with
47+
`gen:upgrade-guide` and verified by `check:upgrade-guide`; all three are
48+
regenerated, never hand-edited.
49+
50+
⛔ No behaviour changes. The conversion id, its `apply`, its accept set and its
51+
fixture are untouched; no authorable key is added or removed.

docs/protocol-upgrade-guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ Finally it removes the 'pdf' member of `view.exportOptions` formats (#8010, main
280280
| `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 |
281281
| `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 |
282282
| `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 |
283-
| `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 |
283+
| `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 |
284284
| `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 |
285285
| `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 |
286286
| `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 |

packages/spec/spec-changes.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@
274274
},
275275
{
276276
"surface": "translation.validationMessages",
277-
"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`)",
277+
"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)",
278278
"conversionId": "translation-validation-messages-removed",
279279
"toMajor": 17
280280
},
@@ -1423,7 +1423,7 @@
14231423
},
14241424
{
14251425
"surface": "translation.validationMessages",
1426-
"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`)",
1426+
"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)",
14271427
"conversionId": "translation-validation-messages-removed",
14281428
"toMajor": 17
14291429
},

packages/spec/src/conversions/registry.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3120,6 +3120,16 @@ const jobIdRemoved: MetadataConversion = {
31203120
* rewritten in the same change to say rule messages are authored on the rule
31213121
* (`object.validations[].message`), not translated through a group.
31223122
*
3123+
* Since 17.3.0 (#14381, #14253) there is a group again and that same guidance
3124+
* entry names it: `objects.<object_name>._validations.<rule_name>.message`,
3125+
* which the write path resolves. It is not `validationMessages` returning —
3126+
* that one was keyed by rule name at the TOP level, so it could not tell two
3127+
* objects' rules apart, and nothing read it; this one is object-scoped and the
3128+
* rule evaluator reads it through the existing `i18nService` channel. The
3129+
* sentence above is what 17.0.0 said and it stays true of the retired key; an
3130+
* author arriving at this conversion needs both halves, so the summary below
3131+
* carries the route too.
3132+
*
31233133
* Removed from the shared `translationDataShape()`, so it retires at BOTH doors
31243134
* at once — the bundle entry and the registered item. #3778's original guard
31253135
* ran on the item door only, which is exactly how the key survived this long in
@@ -3130,7 +3140,7 @@ const translationValidationMessagesRemoved: MetadataConversion = {
31303140
toMajor: 17,
31313141
retiredFromLoadPath: true,
31323142
surface: 'translation.validationMessages',
3133-
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`)",
3143+
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)",
31343144
apply(stack, emit) {
31353145
return mapCollection(stack, 'translations', (t, path) =>
31363146
stripKeys(t, ['validationMessages'], emit, path));

0 commit comments

Comments
 (0)