Repository navigation
Description
Source
Asked by HotCRM's maintainer, who wants Chinese customers to get Chinese demo data when they install HotCRM. Ruled option A on 2026-11-05: the platform's install paths pick datasets by locale, and HotCRM ships zh-CN datasets. The maintainer set every card of this work to priority p1.
Problem (measured on 18.3.0 and objectstack main 797f925)
Seed.locale (#16595) lets an app ship one demo dataset per language market. The only place it is read is AppPlugin.resolveSeedLocale() (packages/runtime/src/app-plugin.ts). That method reads the bundle's static i18n.defaultLocale, and three call sites use it: the boot seed, the per-org seed-replayer (captured once at boot), and dev hot-reload.
These paths pass no locale:
- marketplace install,
reseed-sample-dataand the rehydrate healer (runInlineSeedinpackages/cloud-connection/src/marketplace-install-local-plugin.ts); - package apply and draft publish.
When a locale-scoped dataset reaches one of them, every dataset loads and the loader logs a warning. The spec records this as a decision (packages/spec/src/data/seed.zod.ts, locale doc: "Publishing and installing are locale-neutral acts", ruled on #17011, 2026-09-10).
As a result, an app cannot give a Chinese tenant Chinese sample data from one package. Two things happen instead:
- if the app adds
locale: ['zh-CN']datasets, every marketplace tenant gets both the English and the Chinese rows; - one server's per-org replay gives every organization the same boot locale.
The tenant setting localization.locale (service-settings, scope: 'tenant') already exists. The seed path never reads it.
Ask (maintainer ruling 2026-11-05, Irene, option A; reverses #17011's install-path bound)
- Marketplace install, reseed sample data and the rehydrate healer pass a locale to the seed load request: the installing organization's effective
localization.locale(cascade platform → global → tenant), or else the installing user'ssys_user.locale, or else the app'si18n.defaultLocale. - The per-org
seed-replayerresolves the locale per organization, not once at boot. - Decide what happens at new-org founding, when the replay fires on
sys_organizationinsert, possibly before the org has alocalization.locale. Options:- (a) use the founding user's
sys_user.locale; - (b) defer the replay until the locale is set;
- (c) fall back to
i18n.defaultLocaleand let the tenant reseed later.
- (a) use the founding user's
- Keep the existing behaviour for datasets without
locale: they stay unrestricted.
Consumer
hotcrm: zh-CN product catalogue and standard price book (SaasTenantSeedData), as locale: ['zh-CN'] datasets beside the English ones. The hotcrm change merges only after a pinned release carries this card. Until then, marketplace tenants would seed both languages.
Generated by Claude Code
Activity
objectstack-fleet commentedon Nov 5, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: first-touch audit · p1 kept ·
domain:serverkept ·pm:queue· 2026-11-05T04:54Z⛔ Not a claim.
AppPlugin.resolveSeedLocale()readsSeed.locale, so every locale's dataset loads. HotCRM is waiting to ship zh-CN demo data, so p1 kept.Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
Correction from the consumer ·
repo:hotcrmseat 1 ·huangyiirene· sessionsession_01J8VVAnPkgK6CWrJa6t7Fk5· 2026-11-05T05:04ZThe Problem section says that a marketplace tenant gets both the English and the Chinese rows. For HotCRM that is not what happens. Measured on 18.3.0 by the hotcrm#2411 thread (branch
claude/project-thread-ypnm82, commit69b4694), with a seed load that carries no locale, the way the install paths load today:So a Chinese tenant ends up with the English catalogue only, plus a page of refused-insert warnings. The ask stands as written. Only the observed symptom differs: it is not duplicated data. It is the wrong language plus warning noise. Datasets without shared unique keys would still load twice.
Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF
Claim: PM loop round 1
Session:
session_01Utp6CZMkHvuDeVYgTD73qF(seat) · dev card threadsession_01Utp6CafJkuNKWHNo7WLtiPAccount:
os-billBranch:
claude/project-thread-cydyoy(the card thread's designated push branch, the recorded platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-30281Domain:
domain:serverSeat:
domain:server#4File surface:
packages/runtime/src/app-plugin.ts(resolveSeedLocaleand its callers) and the per-orgseed-replayerit wires,packages/cloud-connection/src/marketplace-install-local-plugin.ts(runInlineSeedand its three call sites only), the locale read from service-settings, their tests, one changeset; amended on the dev's measurement:runInlineSeed's call sites are the rehydrate healer (:1391), cloud-connection's own fallback per-org replayer (:1648) andapplySideEffectsstep 3 (:5011, reached by install and reseed), andpackages/plugins/organizations/src/organizations-plugin.tsgets one line so the founding replay receives the creating user (item 3 lands on (a) with (c) as the fallback, by construction). ⛔packages/spec/**(theSeed.localedocblock is a spec-lane follow-up),docs/adr/**,.claude/**,AGENTS.md,ui/**,skills/**(stop on breach; explain in the report)Container & model:
M,mode:thread,model: default judgment tier (opus) — dispatch-gates --tier: no path-derived mandate,effort: xhighClause-②: yes (widening) —
@objectstack/runtimeexports the shared seed-locale resolver (precedent:mergeSeedDatasets, which cloud-connection imports the same way); nothing is removed or narrowed (amended fromnoon the dev's measurement)Metadata-protocol: semantic
Responsibility:
n/a — not a defect cardThread-read: 6650196621
Premise: holds
Serial constraints cleared: seats 2, 3 and 5 confirmed (2026-11-05T05:21Z). Seat 3's #30217 edits the same
marketplace-install-local-plugin.tsin its constructor and rehydrate section only; whoever merges second mergesorigin/mainfirst.Dispatch order:
/mnt/project-files/server-seat4/dispatch-30281.md(project shared folder). Ruling section: maintainer ruling option A (2026-11-05, quoted in the card body) and triage audit 6650042287. Item 3 (new-org founding: a / b / c) is measured first; if item 1's cascade does not settle it, the dev postsneeds_decisionand carries on with items 1, 2 and 4. If an exported option is added, the dev declaresyes (widening)and the seat amends this line.Posted 2026-11-05T05:24Z.
objectstack-fleet commentedon Nov 5, 2026
domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF
os-dev-report
Status: done. Draft PR #30346 carries
Fixes #30281, head6d3a814a4.What it does, under ruling A (2026-11-05):
resolveSeedLocaleForLoad(@objectstack/runtime, newseed-locale.ts): the organization'slocalization.localewhen someone set it (asource: 'default'answer does not count, asapplyLocalizationLocaleandorganization-default-currency.tsalready read it), else the person'ssys_user.locale, else the app'si18n.defaultLocale. A package with nolocale-scoped dataset reads neither.runInlineSeedand its three call sites inmarketplace-install-local-plugin.ts) ask it with the active organization, the admitted user and the package's default; the heal asks with no organization and no user.AppPluginand the cloud-connection replayer). Theseed-replayercallable takes(organizationId, { userId }); Middleware B passes the insert's attributed user as the founder.Deviations, all agreed with the seat before or during the Claim: the call-site mapping follows the measured code, not the dispatch's line numbers.
organizations-plugin.tsjoined the file surface, so Clause-② isyes (widening). Six sibling cloud-connection suites gained three producer stubs in their runtime mock. The hand-written docs pagecontent/docs/data-modeling/seed-data.mdxis corrected in the PR. The spec docblock is follow-up owed.{ "issue": 30281, "status": "done", "branch": "claude/project-thread-cydyoy", "pr": "https://github.com/objectstack-ai/objectstack/pull/30346", "session": "session_01Utp6CafJkuNKWHNo7WLtiP", "premise_still_valid": true, "summary": "Seed.locale now selects datasets on the marketplace install, the sample-data reseed, the restart heal and the per-organization replay. A new runtime producer resolveSeedLocaleForLoad answers the organization's localization.locale when set (source not 'default'), else the person's sys_user.locale, else the app's i18n.defaultLocale; cloud-connection runInlineSeed and AppPlugin's replayer call it per load, and organizations Middleware B passes the founder. Item 3 settles on (a) with (c) as fallback by construction; datasets without locale stay unrestricted; boot seed, hot-reload, package apply and draft publish are unchanged.", "tests": "At head 6d3a814a4 under the verify lock: runtime app-plugin.seed-locale-producer.test.ts 22 passed (15 new), cloud-connection marketplace-install-local-seed-replayer.test.ts 16 passed (7 new), organizations organizations-plugin.test.ts 20 passed (2 new). Full suites on c6795fe0a (same package files as head): runtime 541 files 8635 passed 19 skipped; cloud-connection 70 files 853 passed; organizations 18 files 250 passed; typecheck of the three exit 0. Eleven ablations through scripts/ablation-replace.mjs, one per resolution point, each red then restored (settings rung, default-not-a-choice, user rung, app-default rung, no-scoped-no-read, AppPlugin per-org, Middleware B founder, install/reseed user, heal body, cc replayer founder, locale reaching the loader).", "gates": "dispatch-gates --commands derived 95 families; each run at head 6d3a814a4 after a full build (119/119) with its exit recorded before any pipe: 95 of 95 exit 0. --ran: 95 derived, 95 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. pnpm lint exit 0. pnpm install --frozen-lockfile exit 0. CI check-runs on #30346: not yet reported.", "dogfood_battery": "n/a — no ! in the PR title and Clause-② has no (narrowing) arm", "line_budget": "source +264 -27 (5 files), tests +498 (9 files), docs and changeset +57 -9 (2 files); 16 files", "mcp_calls": "3: mcp__github__issue_read x3 (get, get_comments x2); no GitHub write tool. Besides GitHub MCP: one subscribe_pr_activity on #30346 (session event subscription, writes nothing to GitHub)", "api_writes": "2 through the relay: pr_create #30346 (draft, no assignee, no labels, read-back identical) and this comment. Plus 5 git pushes to claude/project-thread-cydyoy (not REST)", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: seat · follow-up owed — packages/spec/src/data/seed.zod.ts locale docblock and .describe() still say the install paths do not filter by locale (install paths now pick by the organization's locale, ruling A 2026-11-05); the describe regenerates content/docs/references/data/seed.mdx · Tier H path, not edited", "carrier: none · noted, not filed — a sys_user.locale stored as a bare tag (zh) does not select a zh-CN dataset because the loader matches tags exactly; the Localization setting offers only region-qualified tags · in the PR's Acceptance notes" ] }Report taken 2026-11-05T06:53Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 5, 2026
domain:server#4 · os-bill · session_01Utp6CZMkHvuDeVYgTD73qF
Landed · PR #30346
8589756ac;Fixes #30281closed this card. Seat ACCEPT 6652170771 on the landed head6d3a814a.mainsourcepackages/runtime/src/seed-locale.ts:123export async function resolveSeedLocaleForLoad(Follow-up filed: #30347 (spec lane), the
Seed.localedocblock and.describe()inpackages/spec/src/data/seed.zod.ts, which still say installing is locale-neutral; its serial constraint on this PR is now met.State change:
pm:dispatchedremoved, assignee cleared. Ruling option A is carried out on all four paths; nothing is left on this card.