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
12 changes: 12 additions & 0 deletions .changeset/30607-seed-locale-liveness-note.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@objectstack/spec': patch
---

fix(spec): the liveness ledger's `Seed.locale` row names the six seed-load request builders and where each one's locale comes from, so it no longer says the marketplace install passes none

Clause-②: no
Metadata-protocol: none

- `liveness/seed.json`'s `locale` row said three publish/install-time paths pass no locale, the marketplace install among them. The install paths now pick seed datasets by locale: the marketplace install, the sample-data reseed, the restart heal and the per-organization replay each resolve one (the organization's Localization setting, then the user's language, then the app's `i18n.defaultLocale`). The boot seed and the dev hot-reload seeder still use the app's `i18n.defaultLocale`. Package apply and draft publish still pass none.
- The earlier ruling that publish and install are locale-neutral is kept as recorded, followed by a dated line saying its bound now covers package apply and draft publish only. The note also says why the loader's no-locale warning stays.
- Nothing to do on upgrade: only the shipped `liveness/seed.json` note text and its `verifiedAt` date change. No key, schema shape or export changes.
4 changes: 2 additions & 2 deletions packages/spec/liveness/seed.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,11 @@
},
"locale": {
"status": "live",
"verifiedAt": "2026-09-09",
"verifiedAt": "2026-11-05",
"evidenceScope": "in-repo",
"evidence": "packages/metadata-protocol/src/seed-loader.ts#datasetAllowsLocale (`const declared = dataset.locale` — the one read of the key; absence is the unrestricted spelling, because locales have no enumerable universe to default to the way `env` does); packages/metadata-protocol/src/seed-loader.ts#filterByLocale (drops the datasets it excludes, and always NAMES what it dropped)",
"producer": "packages/runtime/src/app-plugin.ts#resolveSeedLocale (AppPlugin resolves the load-time locale ITSELF off the app's declared `i18n.defaultLocale` — the same ENVELOPE key, read the same way `loadTranslations` reads it for `setDefaultLocale` — and threads it into all THREE `SeedLoaderRequest`s this plugin builds: the inline boot seed, the per-org replayer registered for tenant provisioning, and the dev hot-reload seeder. A blank or undeclared `defaultLocale` sends NO `locale` key rather than an `'en'` default, because absence is the loader's unrestricted spelling and a default would have turned a wiring change into a data change)",
"note": "FLIPPED experimental → live 2026-09-09 by #16595, which supplied the missing PRODUCER. The consumer half (#16510 / PR #16592) was always closed and tested; what was missing was the second input the effect depends on, `SeedLoaderConfig.locale`, which no first-party call site passed — so `filterByLocale` returned its input on its first line and `dataset.locale` was never read at all on the default boot path. That is the same shape `Seed.env` was in before framework#4704, which is why this row was recorded as `experimental` instead of published `live` on a correct-but-insufficient consumer pointer (#4837). EVIDENCE FOR THE FLIP, and it is a NEGATIVE leg rather than a green suite: `packages/runtime/src/app-plugin.seed-locale-producer.test.ts` asserts on the rows that reach the engine, not on the request object, and an ablation that neutralises `resolveSeedLocale` (returning `undefined`) turns 5 of its 7 cases red in the inert direction — a `locale: ['zh-CN']` dataset loads on an `en` stack. All three of this plugin's call sites go red, so the pin covers each one. ⚠️ WHAT THIS `live` ASSERTS, AND WHAT IT DOES NOT — read this before citing the row. It ASSERTS the ledger's own criterion and nothing wider: authoring `locale` changes runtime behaviour, on the boot path a `defineStack()` app actually boots through, and the `producer` field names the code that makes that true. ⛔ It does NOT assert that every path which can reach `SeedLoaderService.load()` supplies a locale — three do not, they are named below, and a reader who takes `live` as \"the axis is honoured everywhere\" is reading something this row does not say. That is the distinction between this row and the #4837 specimen one entry up: `Seed.env` was `live` while ZERO of six call sites supplied it, so authoring the key changed nothing anywhere and the verdict was false in the only sense the criterion has. Here it is true, and bounded, and the bound is written down. ⚠️ THE SCOPE OF THIS `live`, stated rather than left to be discovered: this repo has SIX request builders, the same six the `env` row one entry up enumerates. #16595 wired the THREE in `packages/runtime/src/app-plugin.ts` — the default boot path, which is what makes authoring `locale` change runtime behaviour and therefore what makes this row `live`. The other three are publish/install-time paths and still pass no locale: packages/runtime/src/domains/packages.ts#applyPublishedSeeds (package apply), packages/metadata-protocol/src/protocol.ts#applySeedBodies (draft publish) and packages/cloud-connection/src/marketplace-install-local-plugin.ts#runInlineSeed (marketplace install). None of the three is handed the stack config, and none passes `env` either, so this is the SAME frontier the `live` `env` row already sits behind rather than a new one — but `env` crosses it because `resolveEnvConfig` can read an ambient `NODE_ENV` inside `load()`, and a locale has no ambient source, so it cannot be closed the same way. RULED 2026-09-10 on #17011 (director seat, one-class self-adjudication, option A 「不扩散」): publish and install are locale-neutral acts, so this bound is the CONTRACT rather than an open defect — 「无违约、非缺陷」, because `content/docs/data-modeling/seed-data.mdx` already declared that these three paths load every dataset and warn. What the ruling ordered was wording, not wiring: `SeedSchema.locale`'s describe and that docs page now state the bound, and ⛔ no behaviour changed on the three sites — making them pass a locale (option B) and turning the warning into a refusal (option C) were both explicitly ⛔ not ruled. So this line is a DECISION, not a to-do. ⚠️ THE RULING'S LOAD-BEARING PREMISE, and the one thing that reopens it: real first-party seed data using `locale`, measured repo-wide, is ZERO — re-derived 2026-09-22 against 1f53b0b685 by a structural scan of all 49 `defineSeed` call sites (24 of them real app data in `examples/`, none declaring `locale`), self-lit because the same scan does find the two `locale`-declaring call sites on the docs page. That reach is THIS repo only; cloud / hotcrm / external customer apps stay unmeasured. A measured use of `locale`-scoped seed data in any of them reopens this as option B. ⚠️ AND THEREFORE `SeedLoaderService#warnOnUnresolvedLocaleScope` STAYS (the judgement #16595 was asked to make, decided with its reason recorded here rather than deleted silently): it is not a signpost for an unwired state that has now gone away. It is the live diagnostic for the three call sites above, for any embedding host that builds its own request, and for a stack that declares no `i18n` block — all of which still reach `load()` with locale-scoped datasets and no `config.locale`. Deleting it would make exactly those paths silently inert, which is the property this row spent a release being. REPOINTED while flipping: the pre-flip text of this note named four call sites (the three in app-plugin.ts plus draft publish) and called the `protocol.ts` one unconfirmed. Re-derived against `main` at bb7d91f19f: the `protocol.ts` site is REAL — the earlier negative came from grepping `seedLoader.load`, and that call site names its local `loader` — and the enumeration was short by two (package apply, marketplace install). The full set is the six named above, found by scanning `SeedLoaderRequestSchema` rather than a variable name."
"note": "FLIPPED experimental → live 2026-09-09 by #16595, which supplied the missing PRODUCER. The consumer half (#16510 / PR #16592) was always closed and tested; what was missing was the second input the effect depends on, `SeedLoaderConfig.locale`, which no first-party call site passed — so `filterByLocale` returned its input on its first line and `dataset.locale` was never read at all on the default boot path. That is the same shape `Seed.env` was in before framework#4704, which is why this row was recorded as `experimental` instead of published `live` on a correct-but-insufficient consumer pointer (#4837). EVIDENCE FOR THE FLIP, and it is a NEGATIVE leg rather than a green suite: `packages/runtime/src/app-plugin.seed-locale-producer.test.ts` asserts on the rows that reach the engine, not on the request object, and an ablation that neutralises `resolveSeedLocale` (returning `undefined`) turns 5 of its 7 cases red in the inert direction — a `locale: ['zh-CN']` dataset loads on an `en` stack. All three of this plugin's call sites go red, so the pin covers each one. ⚠️ WHAT THIS `live` ASSERTS, AND WHAT IT DOES NOT — read this before citing the row. It ASSERTS the ledger's own criterion and nothing wider: authoring `locale` changes runtime behaviour, on the boot path a `defineStack()` app actually boots through, and the `producer` field names the code that makes that true. ⛔ It does NOT assert that every path which can reach `SeedLoaderService.load()` supplies a locale — two do not, they are named below, and a reader who takes `live` as \"the axis is honoured everywhere\" is reading something this row does not say. That is the distinction between this row and the #4837 specimen one entry up: `Seed.env` was `live` while ZERO of six call sites supplied it, so authoring the key changed nothing anywhere and the verdict was false in the only sense the criterion has. Here it is true, and bounded, and the bound is written down. ⚠️ THE SCOPE OF THIS `live`, stated rather than left to be discovered: this repo has SIX request builders, the same six the `env` row one entry up enumerates. FOUR supply a locale, each named by the function that builds its request: (1) packages/runtime/src/app-plugin.ts#start, the inline boot seed (single-tenant boot) — the app's declared `i18n.defaultLocale`, read by `resolveSeedLocale`; (2) packages/runtime/src/app-plugin.ts#registerHotReloadSeeder, the dev hot-reload seeder — the same `resolveSeedLocale`, off the boot bundle; (3) packages/runtime/src/app-plugin.ts#start, the per-organization replayer it registers as `seed-replayer` for tenant provisioning — `resolveSeedLocaleForLoad` (packages/runtime/src/seed-locale.ts), resolved per organization at replay time: the organization's `localization.locale` setting when somebody set it (a `source: 'default'` answer does not count), then the founder's `sys_user.locale`, then the app's `i18n.defaultLocale`; (4) packages/cloud-connection/src/marketplace-install-local-plugin.ts#runInlineSeed, one builder behind four paths, all through `resolveSeedLocaleForLoad` — the marketplace install and the sample-data reseed (`applySideEffects`: the setting, then the installing or reseeding user's `sys_user.locale`, then the package's declared `i18n.defaultLocale`), the restart heal (`maybeHealSampleData`: no organization and no user, so the setting as read for a load naming no organization, then the package's default) and this plugin's own per-organization replayer (`buildSeedReplayer`, registered only where no `AppPlugin` replayer already is: the setting, then the founder, and no app-default rung, because its shared list is every installed package's datasets). #16595 wired (1)–(3) in `packages/runtime/src/app-plugin.ts` — the default boot path, which is what makes authoring `locale` change runtime behaviour and therefore what makes this row `live` — and PR #30346 (`Fixes #30281`, merged 2026-11-05) moved (3) onto `resolveSeedLocaleForLoad`, keeping `resolveSeedLocale` as its last rung, and wired (4). A builder whose source answers nothing sends no `locale` key, never an `'en'` guess. TWO are publish-time paths and still pass no locale: (5) packages/runtime/src/domains/packages.ts#applyPublishedSeeds (package apply) and (6) packages/metadata-protocol/src/protocol-publish-batch.ts#applySeedBodies (draft publish — the body `packages/metadata-protocol/src/protocol.ts#applySeedBodies` delegates to; the request moved out of `protocol.ts` with it). Neither is handed the stack config, and neither passes `env` either, so this is the SAME frontier the `live` `env` row already sits behind rather than a new one — but `env` crosses it because `resolveEnvConfig` can read an ambient `NODE_ENV` inside `load()`, and a locale has no ambient source inside `load()`, so it cannot be closed the same way: the four builders that supply one resolve it at the call site. RULED 2026-09-10 on #17011 (director seat, one-class self-adjudication, option A 「不扩散」): publish and install are locale-neutral acts, so this bound is the CONTRACT rather than an open defect — 「无违约、非缺陷」, because `content/docs/data-modeling/seed-data.mdx` already declared that these three paths load every dataset and warn. What the ruling ordered was wording, not wiring: `SeedSchema.locale`'s describe and that docs page now state the bound, and ⛔ no behaviour changed on the three sites — making them pass a locale (option B) and turning the warning into a refusal (option C) were both explicitly ⛔ not ruled. So this line is a DECISION, not a to-do. ⚠️ NARROWED 2026-11-05 — the ruling above is kept as it was recorded, and its \"three paths\" and \"three sites\" are the 2026-09-10 set, marketplace install included. A maintainer ruling on #30281 (2026-11-05, option A, reversing #17011's install-path bound) ordered the install paths to pick datasets by locale, on HotCRM's request for zh-CN demo data rather than through the measurement the premise below names; PR #30346 (`Fixes #30281`, merged 2026-11-05) carried it out and moved the marketplace-install path, builder (4) above, across that bound. The ruling's bound now covers package apply and draft publish only — builders (5) and (6) — and `SeedSchema.locale`'s describe (PR #30604) and that docs page state it narrowed the same way. ⚠️ THE RULING'S LOAD-BEARING PREMISE, and the one thing that reopens it: real first-party seed data using `locale`, measured repo-wide, is ZERO — re-derived 2026-09-22 against 1f53b0b685 by a structural scan of all 49 `defineSeed` call sites (24 of them real app data in `examples/`, none declaring `locale`), self-lit because the same scan does find the two `locale`-declaring call sites on the docs page. That reach is THIS repo only; cloud / hotcrm / external customer apps stay unmeasured. A measured use of `locale`-scoped seed data in any of them reopens this as option B. ⚠️ AND THEREFORE `SeedLoaderService#warnOnUnresolvedLocaleScope` STAYS (the judgement #16595 was asked to make, decided with its reason recorded here rather than deleted silently): it is not a signpost for an unwired state that has now gone away. It is the live diagnostic for the two call sites above that pass none (package apply, draft publish), for any embedding host that builds its own request, for a stack that declares no `i18n` block (the boot seed and the dev hot-reload seeder have no other source), and, since 2026-11-05, for an install, reseed, heal or replay on which no rung of `resolveSeedLocaleForLoad` answers — all of which still reach `load()` with locale-scoped datasets and no `config.locale`. Deleting it would make exactly those paths silently inert, which is the property this row spent a release being. REPOINTED while flipping: the pre-flip text of this note named four call sites (the three in app-plugin.ts plus draft publish) and called the `protocol.ts` one unconfirmed. Re-derived against `main` at bb7d91f19f: the `protocol.ts` site is REAL — the earlier negative came from grepping `seedLoader.load`, and that call site names its local `loader` — and the enumeration was short by two (package apply, marketplace install). The full set is the six named above, found by scanning `SeedLoaderRequestSchema` rather than a variable name. RE-COUNTED 2026-11-05 against b8929f48 by the same scan of production `SeedLoaderRequestSchema` parses: still SIX builders, the six named above, with the draft-publish request now built in `protocol-publish-batch.ts`. A control scan of every `new SeedLoaderService(` finds no seventh: its three other constructions, in the marketplace plugin's sample-data scope, purge and count reads (`sampleDataInScope`, `handlePurge`, `handleSampleDataCount`), call only `buildDependencyGraph` and never `load()`."
},
"records": {
"status": "live",
Expand Down
Loading