Skip to content

Commit c61ae6d

Browse files
committed
docs(i18n): restate the orphan-key table from the emitter, sibling packages included
The "Must name" table under Orphan Keys and Option Keys still stated the pre-fix rule: `objects.{object}` resolved only against "an object this stack defines", which is false on the per-package pass, where a sibling package of the same artifact is resolution context. Rewrite the table as one unit from `validate-translation-references.ts`: define "declared" once (this stack or a sibling package), add the rungs the emitter judges and the table omitted (`_tabs`, `_validations`, `flows.*`, contributed-into apps, objectExtensions targets), and extend the per-package sentence in validating-metadata.mdx to the collections that resolve the same way. Claude-Session: https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv Co-authored-by: Claude <noreply@anthropic.com>
1 parent 1cacfe4 commit c61ae6d

2 files changed

Lines changed: 36 additions & 13 deletions

File tree

‎content/docs/deployment/validating-metadata.mdx‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,10 @@ matrix answers the first question only.
623623
2. **The per-package walk.** The same table again, once per entry of `packages[]`:
624624
each package's body re-read as the stack it was assembled from, handed the
625625
artifact's own `packages[]` as **resolution context** so a sibling package's
626-
object still resolves, while the collections being judged stay that package's
626+
object still resolves — and so does a translation key naming a sibling's view,
627+
page, action, app, dashboard or flow (the
628+
[orphan-key table](/docs/protocol/kernel/i18n-standard#orphan-keys-and-option-keys)
629+
lists every rung) — while the collections being judged stay that package's
627630
alone. It is de-duplicated against the union run, so what it reports is the
628631
set of per-package findings no union finding already carried under the same
629632
rule, `where`, message and non-top-level position — the leading

‎content/docs/protocol/kernel/i18n-standard.mdx‎

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,20 +1017,40 @@ write for it: no bundle key exists for a map.
10171017
nothing supplies text for, whether that text would come from a bundle or from
10181018
an inline locale map. The reverse direction (keys a bundle carries that no
10191019
metadata claims) is checked by `os validate`, `os lint`, and `os compile`, which walk
1020-
every bundle against the stack it ships with:
1021-
1022-
| Key | Must name |
1020+
every bundle against the stack it ships with.
1021+
1022+
"Declared" below has one meaning in every row: declared by the stack being
1023+
checked **or by a sibling package of the same artifact**. On a single
1024+
`defineStack` project, and on the union pass over an artifact, those are the same
1025+
thing. On the per-package pass over an artifact's `packages[]` (see
1026+
[Validating metadata](/docs/deployment/validating-metadata)) each package is
1027+
checked alone, but with the artifact's `packages[]` as resolution context — so a
1028+
package may translate an object, view, page, action, app, dashboard or flow a
1029+
sibling package declares. The keys beneath it are then judged against the
1030+
sibling's own declaration, not waved through: a typo under a sibling's object is
1031+
still an error. Where the package being checked and a sibling declare the same
1032+
name, the package's own record is the one a field's options, an action's params,
1033+
a dashboard's widgets and header actions, or a flow's screens are judged against;
1034+
every other name the two declare adds up.
1035+
1036+
| Key | Resolves when it names |
10231037
|:---|:---|
1024-
| `objects.{object}` | an object this stack defines, or a platform object |
1025-
| `objects.{object}.fields.{field}` | a field that object declares |
1038+
| `objects.{object}` | an object declared by this stack **or a sibling package of the same artifact** — its keys are then judged row by row below; **or** a platform object, or an object an `objectExtensions[]` entry extends that no package of the artifact declares — both skipped whole, because the owner's fields are not visible from here |
1039+
| `objects.{object}.fields.{field}` | a field that object declares, a field an `objectExtensions[]` entry merges into it, or a system column the registry injects (`id`, `owner_id`, the audit and tenant columns; `_id`, `name` and `space` are accepted too) |
10261040
| `objects.{object}.fields.{field}.options.{key}` | an option's stored **`value`** |
1027-
| `objects.{object}._views.{view}` | a view's `name` |
1028-
| `objects.{object}._actions.{action}` | an action bound to that object |
1029-
| `objects.{object}._actions.{action}.params.{param}` | a param's `name` |
1030-
| `objects.{object}._sections.{section}` | a `fieldGroups[].key`, or a section with a `name` |
1031-
| `apps.{app}` / `apps.{app}.navigation.{id}` | an app's `name` / a navigation item's `id` |
1032-
| `dashboards.{dash}` / `.widgets.{id}` / `.actions.{actionUrl}` | the dashboard's `name` / a widget `id` / a header `actionUrl` |
1033-
| `globalActions.{action}` | an action with **no** `objectName` |
1041+
| `objects.{object}._views.{view}` | the key the view registry assigns a view over that object: a `listViews` / `formViews` map key, or the default `list`'s `name` — `default` when it has none (see [ObjectUI Integration](#objectui-integration)) |
1042+
| `objects.{object}._sections.{section}` | a `fieldGroups[].key`, a named section of a form view over that object (a `formViews` entry or the default `form`), or a named section of a `record:details` component on a page bound to it |
1043+
| `objects.{object}._tabs.{tab}` | a filter-preset tab's `name` in `interfaceConfig.userFilters.tabs` on a page over that object (`interfaceConfig.source`, else the page's `object`) — a list view's own `tabs` has no renderer and is not resolvable |
1044+
| `objects.{object}._validations.{rule}` | a validation rule's `name` in that object's `validations[]` or in an `objectExtensions[]` entry aimed at it — a `conditional` rule's `then` / `otherwise` branch included |
1045+
| `objects.{object}._actions.{action}` | an action bound to that object: one in its `actions`, or a stack-level action whose `objectName` is that object |
1046+
| `objects.{object}._actions.{action}.params.{param}` | a param's `name` on that action |
1047+
| `globalActions.{action}` | an action with **no** `objectName` — a key naming a bound action is reported with the `objects.{owner}._actions` key the resolver reads instead |
1048+
| `apps.{app}` | an app declared, or one a `navigationContributions` entry anywhere in the artifact contributes into — including an app owned outside the artifact |
1049+
| `apps.{app}.navigation.{id}` | a navigation item `id` the app declares (nested `children` and `areas[]` included) or one contributed into it; under an app that is only contributed into, the contributed ids alone |
1050+
| `dashboards.{dash}` / `.widgets.{id}` / `.actions.{actionUrl}` | a dashboard declared / a widget `id` on it / a header action's `actionUrl` on it |
1051+
| `flows.{flow}` | a flow's machine `name`, declared |
1052+
| `flows.{flow}.screens.{node}` | the `id` of a `type: 'screen'` node in that flow, nodes nested in loop, parallel and try/catch regions included |
1053+
| `flows.{flow}.screens.{node}.fields.{field}` | a `config.fields[].name` on that screen — an object-form screen (`config.objectName`) declares none, and its input labels live under `objects.{objectName}.fields` |
10341054

10351055
An unresolvable key is an **error**, not a warning: `os validate`, `os lint` and
10361056
`os compile` each report it as `translation-target-unknown` and exit non-zero, so

0 commit comments

Comments
 (0)