diff --git a/.changeset/translation-liveness-walk-boundary.md b/.changeset/translation-liveness-walk-boundary.md new file mode 100644 index 0000000000..6d6d283657 --- /dev/null +++ b/.changeset/translation-liveness-walk-boundary.md @@ -0,0 +1,28 @@ +--- +'@objectstack/spec': patch +--- + +liveness ledger: `translation`'s `_note` WALK BOUNDARY sentence excepts `settingsCommon` and states that group's own boundary + +The header said "every group is a z.record keyed by target names — the drill sees each +record's VALUE shape one level". Measured on this commit straight off +`translationDataShape()`, ten of the eleven groups are `z.record`s and `settingsCommon` is a +plain strictObject: a fixed shape whose one member `sourceLabels` is itself a fixed +strictObject keyed by the ADR-0010 resolution layers (`env`, `global`, `tenant`, `user`, +`default`) — a closed set, with the retired spellings (`org`, `workspace`, `system`, +`fallback`, `environment`) rejected and pointed at the layer each meant. Nothing about it is +keyed by target names, so the universal was false for one group, and false in the direction +that matters: the sentence is the file's declaration of how far down its rows reach. + +The sentence now excepts `settingsCommon` and states what the boundary is for it: the +walk's one level lands on the named member `sourceLabels` (exactly what +`check:liveness --undrilled` prints for `translation/settingsCommon`), the layer keys +beneath it sit below the boundary and are read as one unit by `resolveSettingsSourceLabel` +and objectui's `useSettingsLabel`, and the blanket verdict over them is the declared kind — +`translation/settingsCommon` is already a row of `undrilled-containers.baseline.json`, so +no new pinned artifact is added. The `datasets` row's "WALK BOUNDARY as for every other +group here" inherited the same universal by reference and now cites the record groups only. + +Published data, prose only: `liveness/` is in this package's `files` array, so these +ledgers ship in the npm tarball. No `status` value moves, no schema changes and no gate +verdict changes. The header's summary-count sentence (#15775) is untouched. diff --git a/packages/spec/liveness/translation.json b/packages/spec/liveness/translation.json index ceeeb57568..46c0a67ad7 100644 --- a/packages/spec/liveness/translation.json +++ b/packages/spec/liveness/translation.json @@ -1,6 +1,6 @@ { "type": "translation", - "_note": "TranslationItemSchema (#3778 — one locale's translations, the SAME groups the file-authored bundles use). NO LONGER A PIPE: the schema was a z.preprocess wrapping the retired object-first-dialect guard, which the gate's walker could not see through until #4488 fixed unwrap() to take the OUT side of a transform-input pipe — `translation` was literally unwalkable before this ledger. #4001 closed the shape with `.strict()` and folded the guard's ten prescriptions into the unknown-key `guidance`, so the preprocess is gone and the registered schema is a plain strict object. Consumer chain: runtime-authored items sync into the i18n adapter's authored layer (packages/core/src/fallbacks/authored-translation-sync.ts — at kernel:ready, on metadata:reloaded, and on translation mutations; #2591 closed the publish dead-end), file bundles load via service-i18n; both merge into ONE tree read by the spec resolvers (packages/spec/src/system/i18n-resolver.ts), the REST localization layer (translateMetaItem/translateMetaTypes), objectui's client resolvers (useObjectLabel/useSettingsLabel), and plugin-audit's summary localizer. WALK BOUNDARY: every group is a z.record keyed by target names — the drill sees each record's VALUE shape one level; the deeper per-key conventions (objects..fields..label, settings..keys..options., …) are governed by the resolvers cited per row, not by ledger rows. Note also the sync merges the RAW stored payload (authored-translation-sync.ts:155, not a schema re-parse), so the declared groups below are the CONTRACT while undeclared keys technically flow through on rows already stored — the resolvers read only the declared conventions. Since #4001 no NEW row can acquire one: the metadata door rejects an undeclared key instead of stripping it, so that residue is a finite set that only shrinks. Every group but `flows` is live — `flows` is the one that is `planned`, and `datasets` was seeded LIVE and DRILLED by #14253 with its reader (`translateDataset`) in the same change. A BOUNDARY and not a total, deliberately: the per-prop rows below carry the verdicts, the generated `state-counts.md` carries this type's totals (#7377), and `props` holds the groups PLUS `locale` and the item-identity keys `name`/`label` — so no total taken over `props` is a total of groups, which is how both totals this sentence has carried came to be wrong. ⚠️ It first read \"10 of 11 groups live; the one dead group (`validationMessages`) is pointed at by #3778's own legacy-key migration table, making it a shipped false signpost\" — describing a group REMOVED in 17.0.0 (#4667), i.e. prose outliving its subject in the header of the very file whose rows warn about that; corrected 2026-09-02 (#14253) to \"11 of 12 groups live; the twelfth, `datasets`, …\", which matched no reading of `props` at all — `datasets` is one of the groups, never a twelfth. Corrected again 2026-09-06 (#15775) by deleting the integers rather than re-deriving them, on #7377's precedent for this ledger family's other hand-maintained counts. Seeded 2026-08-01 (#4488). 2026-08-28 (#13003): all nine `path:NNN` citations in this file were re-anchored to their consuming symbols; EIGHT of the nine were wrong and every one of those was IN RANGE (the exception is `locale`, whose range still lands inside its reader). This ledger carried the batch's heaviest load of the OTHER silent class as well — nine further positions written as bare `:NNN` suffixes with no path in front of them, which `PATH_RE` never matches, so they degraded to prose that no check has ever resolved, bounded or key-checked.", + "_note": "TranslationItemSchema (#3778 — one locale's translations, the SAME groups the file-authored bundles use). NO LONGER A PIPE: the schema was a z.preprocess wrapping the retired object-first-dialect guard, which the gate's walker could not see through until #4488 fixed unwrap() to take the OUT side of a transform-input pipe — `translation` was literally unwalkable before this ledger. #4001 closed the shape with `.strict()` and folded the guard's ten prescriptions into the unknown-key `guidance`, so the preprocess is gone and the registered schema is a plain strict object. Consumer chain: runtime-authored items sync into the i18n adapter's authored layer (packages/core/src/fallbacks/authored-translation-sync.ts — at kernel:ready, on metadata:reloaded, and on translation mutations; #2591 closed the publish dead-end), file bundles load via service-i18n; both merge into ONE tree read by the spec resolvers (packages/spec/src/system/i18n-resolver.ts), the REST localization layer (translateMetaItem/translateMetaTypes), objectui's client resolvers (useObjectLabel/useSettingsLabel), and plugin-audit's summary localizer. WALK BOUNDARY: every group but `settingsCommon` is a z.record keyed by target names — the drill sees each record's VALUE shape one level; the deeper per-key conventions (objects..fields..label, settings..keys..options., …) are governed by the resolvers cited per row, not by ledger rows. `settingsCommon` is a fixed strictObject with no target names, so the drill's one level lands on its own named member `sourceLabels` — itself a fixed strictObject whose keys are the ADR-0010 resolution layers (`env`, `global`, `tenant`, `user`, `default`), a closed set the schema holds closed: the retired spellings (`org`/`workspace`, `system`, `fallback`, `environment`) are rejected with a pointer to the layer each meant, never accepted as a layer. Those layer keys sit below the boundary: they are read as one unit by `resolveSettingsSourceLabel` (packages/spec/src/system/i18n-resolver.ts) and objectui's `useSettingsLabel().sourceLabel`, and the blanket verdict `settingsCommon` carries over them is the DECLARED kind — `translation/settingsCommon` is a row of scripts/liveness/undrilled-containers.baseline.json, the same standing as the record groups whose value shapes are not drilled. Note also the sync merges the RAW stored payload (authored-translation-sync.ts:155, not a schema re-parse), so the declared groups below are the CONTRACT while undeclared keys technically flow through on rows already stored — the resolvers read only the declared conventions. Since #4001 no NEW row can acquire one: the metadata door rejects an undeclared key instead of stripping it, so that residue is a finite set that only shrinks. Every group but `flows` is live — `flows` is the one that is `planned`, and `datasets` was seeded LIVE and DRILLED by #14253 with its reader (`translateDataset`) in the same change. A BOUNDARY and not a total, deliberately: the per-prop rows below carry the verdicts, the generated `state-counts.md` carries this type's totals (#7377), and `props` holds the groups PLUS `locale` and the item-identity keys `name`/`label` — so no total taken over `props` is a total of groups, which is how both totals this sentence has carried came to be wrong. ⚠️ It first read \"10 of 11 groups live; the one dead group (`validationMessages`) is pointed at by #3778's own legacy-key migration table, making it a shipped false signpost\" — describing a group REMOVED in 17.0.0 (#4667), i.e. prose outliving its subject in the header of the very file whose rows warn about that; corrected 2026-09-02 (#14253) to \"11 of 12 groups live; the twelfth, `datasets`, …\", which matched no reading of `props` at all — `datasets` is one of the groups, never a twelfth. Corrected again 2026-09-06 (#15775) by deleting the integers rather than re-deriving them, on #7377's precedent for this ledger family's other hand-maintained counts. Seeded 2026-08-01 (#4488). 2026-08-28 (#13003): all nine `path:NNN` citations in this file were re-anchored to their consuming symbols; EIGHT of the nine were wrong and every one of those was IN RANGE (the exception is `locale`, whose range still lands inside its reader). This ledger carried the batch's heaviest load of the OTHER silent class as well — nine further positions written as bare `:NNN` suffixes with no path in front of them, which `PATH_RE` never matches, so they degraded to prose that no check has ever resolved, bounded or key-checked.", "props": { "name": { "status": "live", @@ -58,7 +58,7 @@ "status": "live", "verifiedAt": "2026-09-02", "evidence": "packages/spec/src/system/i18n-resolver.ts#lookupDatasetAttr (`datasets..label` / `.description`); packages/spec/src/system/i18n-resolver.ts#lookupDatasetMemberLabel (`datasets..dimensions..label` and `.measures..label`); packages/spec/src/system/i18n-resolver.ts#translateDataset (registered in METADATA_DOCUMENT_TRANSLATORS, which is what TRANSLATABLE_METADATA_TYPES is derived from, so packages/rest/src/rest-server.ts#translateMetaItem localizes a served dataset with nothing else to remember, #3786)", - "note": "Seeded 2026-09-02 (#14253) LIVE, with its reader in the same change — not declared first and wired later. A dataset reads like a back-office definition, but a measure label is drawn ON THE DASHBOARD, under every metric tile and on every chart axis; before this `dataset` was neither in TRANSLATABLE_METADATA_TYPES nor addressed by any group, so a translated dashboard rendered Chinese tile titles with `Untouched > 14 days` directly beneath them. Top-level rather than nested under `dashboards` because a dataset is the ONE definition every presentation binds to by reference (ADR-0021 D1): the same measure is drawn by N widgets across M dashboards, and a dataset no dashboard references would otherwise be unaddressable. WALK BOUNDARY as for every other group here — the drill sees this record's VALUE shape one level; the per-key conventions below it (dimensions..label, measures..label) are governed by the resolvers cited above. Key face measured against DatasetSchema: a dimension and a measure each declare `label` and nothing else display-shaped, which `dataset.zod.ts` states at the authoring site too ('its author-facing text is `label`. `description` is declared on the DATASET itself'), so `description` lives on the dataset and nowhere below it.", + "note": "Seeded 2026-09-02 (#14253) LIVE, with its reader in the same change — not declared first and wired later. A dataset reads like a back-office definition, but a measure label is drawn ON THE DASHBOARD, under every metric tile and on every chart axis; before this `dataset` was neither in TRANSLATABLE_METADATA_TYPES nor addressed by any group, so a translated dashboard rendered Chinese tile titles with `Untouched > 14 days` directly beneath them. Top-level rather than nested under `dashboards` because a dataset is the ONE definition every presentation binds to by reference (ADR-0021 D1): the same measure is drawn by N widgets across M dashboards, and a dataset no dashboard references would otherwise be unaddressable. WALK BOUNDARY as for the other RECORD groups here (the header names the one group that is not a record): the drill sees this record's VALUE shape one level; the per-key conventions below it (dimensions..label, measures..label) are governed by the resolvers cited above. Key face measured against DatasetSchema: a dimension and a measure each declare `label` and nothing else display-shaped, which `dataset.zod.ts` states at the authoring site too ('its author-facing text is `label`. `description` is declared on the DATASET itself'), so `description` lives on the dataset and nowhere below it.", "children": { "label": { "status": "live",