Repository navigation
zh-CN demo dataset: a fully Chinese Ardenline for Chinese-locale demos (pnpm demo:zh) #117
Copy link
Copy link
Closed
Description
Activity
Claiming this issue.
- Branch:
claude/issue-117-zh-demo-seed - Session: https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p
- Worktree:
/home/user/duly-issue-117
Planned file surface:
src/data/*.ts(fixtures + seed builders +index.tslocale switch),scripts/demo.mjs,package.json,README.md,test/seed.test.ts. Item 5 (admin rename to 演示管理员) will be measured against a real boot, not assumed — if the rename breaks sign-in or the natural-key replay,Dev Adminstays and the PR says so.
Generated by Claude Code
- Branch:
- added a commit that references this issue
on Sep 2, 2026 os-dev-report
{ "issue": 117, "status": "done", "branch": "claude/issue-117-zh-demo-seed", "pr": "https://github.com/objectstack-ai/duly/pull/121", "pr_number": 121, "head_sha": "e05dcfb", "premise_still_valid": true, "gates": { "validate": "pass", "typecheck": "pass", "test": "pass", "build": "pass" }, "summary": "DULY_DEMO_LOCALE (unset=en, zh-CN) chooses the demo fixture's language, read at compile time beside DULY_DEMO_SEED and passed to both of scripts/demo.mjs's boots; pnpm demo:zh added to package.json and the README table. One fixture in two languages, not two fixtures: the arrays stay authored in English and are mapped through t() once per section, with src/data/demo-zh.ts holding every human-readable string keyed by its English original — which keeps one org chart, one history planner and one set of invariants, and makes 'is it translated?' a two-directional set comparison. Machine values (unit codes, period keys, select values, timezones, frequencies) are identical in both locales; mailboxes stay ASCII pinyin on ardenline.example while display names are Chinese. Position codes are now readable in both locales (Plant compliance officer / 厂区合规专员). Item 5 was MEASURED, not assumed, and the rename ships.", "tests": "All four gates green at head_sha e05dcfb (run under the container's shared verify lock, exit codes captured before any pipe): validate 'Validation passed (484ms)' exit 0; typecheck exit 0; test 'Test Files 35 passed (35) / Tests 868 passed (868)' exit 0 (origin/main baseline was 32 files / 833 tests); build 'Artifact: dist/objectstack.json (208.3 KB)' exit 0. New suites: test/seed-locale.test.ts boots a real kernel PER LOCALE with the declarative seeder running and reads rows back from the database (12 tests) — two independent stores, equal row counts across all 8 objects, equal status/caliber/review-state/frequency/period_key tallies, a Han character in every one of 395 zh-CN display strings and none in the English ones, ASCII mailboxes, invented-only 《…》第N条 references, and the English names/subjects pinned literally rather than via toMatchSnapshot. test/demo-locale.test.ts (18 tests) pins the switch and holds the dictionary to the fixture in both directions. test/demo-script.test.ts (5 tests) holds scripts/demo.mjs's admin name to the fixture's ADMIN. REVERSE-VERIFIED, both new guards, on the committed tree with an EXIT INT TERM restore trap and the mutation confirmed on disk by grepping the removed AND injected text (never the editor's exit code): (A) one translation replaced by its English original — removed=0/injected=1 on disk — turned BOTH guards red, seed-locale naming the real field ('duly_log_entry.subject'); (B) an English fixture line reworded — original=0/reworded=1 on disk — turned it red THREE ways (the zh compile throws by name, plus both directions of the coverage test), which is item 6's byte-for-byte pin demonstrated. No rebuild leg is needed: vitest reads TS source and both suites point artifactPath at a path that cannot exist, so a stale dist/objectstack.json cannot be what is under test. LIVE: pnpm demo:zh on a clean DB (port 4321) printed '1/2 done — admin account ready, renamed to 演示管理员 for this locale', sign-in returned 200 as 演示管理员, and GET /api/v1/data/duly_task?limit=5 returned 200 / total 183 with Chinese subjects (班前安全讲话记录—河畔厂区 | open | 2026-09; 产线安全巡查—B线 | done | 2026-05; …). pnpm demo on a clean DB is unchanged (183 tasks, 13 users, Dev Admin, English subjects). Both boot controls were RE-TAKEN after an earlier reading was contaminated by stray servers — setsid re-parents, so $! is not the process-group leader and the first teardown missed four; all were killed by process group and none survive.", "admin_rename": { "attempted": true, "worked": true, "detail": "Measured on @objectstack/* 17.2.0, live, clean DB. PATCH /api/v1/data/sys_user/{id} with {\"name\":\"演示管理员\"} as the signed-in admin returned 200 and read back 演示管理员. Signing in again with the SAME credentials returned 200 and reported the new name — both immediately and after a restart. The seed's natural-key match landed on the renamed row: 13 sys_user rows, not 14, and the demo account owns 26 tasks (6 open/in_progress), so My week is populated. A second pnpm demo:zh on the same database printed '1/2 done — admin account ready.' with no rename (idempotent) and every row count identical — the replay is a no-op. It is done in the priming boot because that is the only place it can be: plugin-auth's maybeSeedDevAdmin takes no name, and the seed's sys_user row for the admin carries its natural key and nothing else on purpose. scripts/demo.mjs FAILS LOUDLY if the rename does not land — a warning would leave a fourteenth user owning the demo account's work and four empty screens on first boot, with the seed reporting success. Dev Admin is unchanged in English." }, "files_changed": [ "src/data/demo-locale.ts (new) — the DULY_DEMO_LOCALE switch, t(), and the requested-string registry", "src/data/demo-zh.ts (new) — the zh-CN dictionary keyed by English original, plus the 12 people's names and pinyin mailboxes", "src/data/demo-org.ts — locale-aware UNITS/PEOPLE/POSITIONS/ADMIN, per-row email, personOf()", "src/data/demo-catalog.ts — locale-aware CATALOG_ITEMS and DUTIES", "src/data/demo-history.ts — the hand-placed occurrence tables keyed by translated duty name", "src/data/demo-assignments.ts — assignment subjects/briefs, owners and task notes", "src/data/log-entry.seed.ts — the 15 work-log entries", "src/data/org.seed.ts — reads the row's email instead of deriving it from the display name", "src/data/index.ts — documents and re-exports the locale beside the seed gate", "scripts/demo.mjs — locale to BOTH boots, and the measured admin rename in the priming step", "package.json — demo:zh", "README.md — the demo:zh row and the two-languages-one-fixture note", "test/seed-locale.test.ts (new), test/demo-locale.test.ts (new), test/demo-script.test.ts (new)" ], "decisions": [ "Dictionary keyed by the English string rather than parallel zh fixture files: a parallel fixture duplicates structure as well as prose and drifts invisibly (19 catalog items vs 20 is not something anybody spots on a screen). It also makes coverage a two-directional set comparison, which is what pins item 6 — rewording an English line orphans its entry and goes red.", "A missing translation THROWS in zh-CN rather than falling back to English. Half these strings are natural keys, so a fallback would not merely read wrong — it would split one obligation into two rows nothing downstream could reconcile. In English t() is the identity function, so the English fixture is untouched by the mechanism.", "An unrecognised DULY_DEMO_LOCALE (zh_CN, zh-Hans) is a hard error. A silent fallback brings the demo up in English, working perfectly, and is discovered in front of a customer.", "ADMIN is defined explicitly in demo-org.ts rather than as a dictionary row, because its Chinese spelling is only true if something actually renames a live credential-bearing account — the measurement, not the fixture, is what decides it.", "The per-locale boot lives in test/seed-locale.test.ts, not test/seed.test.ts as the card says: seed.test.ts boots one kernel from static imports, and a second locale needs vi.resetModules() plus dynamic imports. seed.test.ts is untouched and still green.", "The sync/apply action placeholders (plant_compliance_officer) were NOT changed: they are mirrored in the zh-CN translations bundle, which the card's 'not in this card' excludes. Filed as #120 together with samples/catalog-items.csv, which carries the same old spelling." ], "out_of_scope_findings": [ "filed as #120: readable position codes stopped at the demo seed — samples/catalog-items.csv, the two catalog action placeholders, the zh-CN bundle's copies of them, and possibly docs/import/walkthrough.md still say plant_compliance_officer" ], "concerns": [ "The card says '21 catalog items'; the fixture has 20 — the 21 is samples/catalog-items.csv, which carries one extra row (Daily line start-up check) that CATALOG_ITEMS does not. All 20 fixture items are translated; no code change was needed and none was made.", "Pre-existing and untouched: src/data/duty.seed.ts's doc comment says 'Thirty duties over thirteen people' where DUTIES has 31. A one-word comment drift in a file this PR does not modify — reported rather than filed, since a separate issue for it is more noise than signal, but flagging it so the decision is on the record.", "Switching DULY_DEMO_LOCALE on a database that is already seeded leaves BOTH organisations in it — the two fixtures have disjoint natural keys, so nothing matches and everything inserts. This is inherent to a compile-time locale and is documented in the README ('rm -rf .objectstack/data first'), not guarded in code." ] }
Generated by Claude Code
Why
The Chinese UI is done (nav, columns, statuses, progress phrases, form help all render in zh-CN — measured 2026-09-02 in the browser). But every record in the demo is English:
Emissions return — Northgate,Sami Okonkwo,Northgate Quality,Reading the bulletins is not the duty…. A Chinese business leader watching a demo sees a Chinese frame around English content, which reads as "not localised" however good the chrome is. This is the single largest visible gap for pre-sales in China.What "done" is
A locale switch on the demo seed.
DULY_DEMO_LOCALE(endefault ·zh-CN), read whereDULY_DEMO_SEEDis already read (src/data/index.ts, compile time — the seed is baked intodist/objectstack.json).scripts/demo.mjsmust pass it through to BOTH boots (today it only deletesDULY_DEMO_SEEDfor the priming boot; keep that). Add"demo:zh": "DULY_DEMO_LOCALE=zh-CN node scripts/demo.mjs"topackage.jsonand a row to README's "Two ways to start it" table.Every human-readable fixture string gets a zh-CN counterpart, chosen by that switch. That is: the 6 units, 12 people, 21 catalog items (name, description / what-done-means, reference), duty names (catalog name + unit suffix — the suffix pattern becomes
—北门厂区style), task subjects and notes indemo-history.ts, assignment name and brief, the 15 log entries, the return reason on the returned duty, progress notes. Machine keys (code,period_key, select values, timezones) stay as they are.Readable position codes in BOTH locales.
duly_catalog_item.position_codeis free text; the demo currently showsplant_compliance_officerin the 岗位 column. UsePlant compliance officer/厂区合规专员,Shift supervisor/班组长,Quality technician/质量技术员.POSITIONSindemo-org.tsis the one place; the sync action and any test keyed on the old codes must follow.Invented names only — the fixture's hard rule holds in Chinese. Company 「安岭集团」, sites 「北门厂区」「河畔厂区」「集团总部」, departments 「北门生产部」「北门质量部」 (or your own — but no real company, person, site or regulation). Emails stay pinyin on
ardenline.example(RFC 2606). References stay internal policy numbers: 《集团环境标准 GE-09》第3条.The admin persona. The account the evaluator logs in as is
Dev Admin(minted by plugin-auth, name not configurable — measured: onlyOS_SEED_ADMIN_EMAIL/OS_SEED_ADMIN_PASSWORDexist). Measure whetherdemo.mjscan PATCHsys_user.nameto演示管理员in the priming step (it already has a signed-in probe) withADMINindemo-org.tsfollowing the locale, so the seed's natural-key match still lands on the same row. If the rename breaks sign-in or the natural-key replay, keepDev Adminand say so in the PR — do not leave a demo that cannot log in.Tests.
test/seed.test.tsboots the stack once per locale: same row counts in both, every zh-CN display string contains at least one Han character (a fixture line someone forgot to translate fails here, not on the demo screen), and theenfixture is byte-for-byte what it was (snapshot the en subjects/names so this card cannot quietly reword the English demo).pnpm validate/typecheck/testgreen.Not in this card
@objectstack/*release after 17.2.0.