You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
every example app boots with an ERROR-level block for @objectstack/service-i18n, a service none of them declares — the message itself says it is not a failure #15185
Measured (showcase on main, 2026-09-04, objectstack dev)
Every boot of examples/app-showcase prints, at error level:
[i18n] @objectstack/service-i18n was requested but could not be loaded (undeclared).
Unchanged: this boot serves i18n from the kernel in-memory fallback, so what follows
is why the file-based service is absent — not a boot failure.
Cannot find package '@objectstack/service-i18n': the host app does not declare it.
followed by ~12 more lines of remediation and the #4719 rationale.
The message is right about the mechanism and right to refuse mere reachability. What is off is the register and the population:
The three shipped examples — app-showcase, app-crm, app-todo — declare none of it (grep i18n over their package.json dependencies returns nothing), while app-showcase does declare translations: [ShowcaseTranslationBundle] and an i18n config block. So the flagship example asks for i18n in metadata and cannot get the service by construction.
The fallback works: zh-CN / en switching was verified in the browser this same session, so nothing a user does is broken.
Yet it prints at error, on every boot, in the example a new user runs first — and it is the only error-level output on an otherwise clean boot.
Two readings, both cheap; pick one
The examples should declare it — add @objectstack/service-i18n to the three example apps' dependencies, so the file-based service loads and the block disappears. This is the reading the message itself prescribes.
The level is wrong for this case — an app that never declared the service is on the intended fallback path, so warn (or info) fits and error should stay for an app that DID declare it and still failed to load. The message already distinguishes the two states in prose; the level does not.
They are not exclusive: (1) for the examples, (2) so the next undeclared host does not read a working boot as broken.
Found during the release-readiness browser round for the objectui pin refresh (#14511 / PR #15183). Not a regression from that PR — measured on main as of 5cf1c881b.
读法 2(日志级别) 落在打印这段文字的地方,而 ⛔ 本席未能测定它:git grep -rln "was requested but could not be loaded (undeclared)" -- packages 在 origin/main 上只命中一个 packages/cli 的 e2e 测试,没命中源码 ⇒ 说明源码里那句是拼接而成的。⇒ 若车道取读法 2,请自行重推发出点并交给拥有它的车道,⛔ 不要按本卡的 domain:devx 就地改。
p2 判据:① 它是新用户第一个会跑的示例在一次干净启动里唯一的 error 级输出;② 而消息自己就写着「what follows is why the file-based service is absent — not a boot failure」⇒ 级别与文本互相矛盾;③ 回退路径实测可用(同一次会话里验过 zh-CN / en 切换)。⇒ 无功能损坏 ⇒ ⛔ 不是 p1;但「教新用户忽略 ERROR」是真实代价 ⇒ ⛔ 不是 p3。
⛔ 本席未能测定它:git grep -rln "was requested but could not be loaded (undeclared)" -- packages 在 origin/main 上只命中一个 packages/cli 的 e2e 测试,没命中源码 ⇒ 说明源码里那句是拼接而成的。
复量于 origin/main 3e21cf0a,按片段而非整句去搜,发出点找到了:
packages/cli/src/commands/serve.ts:5346 export function formatI18nLoadDiagnostic(pkg: string, err: unknown): string {
packages/cli/src/commands/serve.ts:5356 + ' is why the file-based service is absent — not a boot failure.\n';
packages/cli/src/commands/serve.ts:3141 console.warn(formatI18nLoadDiagnostic(i18nPkg, i18nErr));
① 它是新用户第一个会跑的示例在一次干净启动里的显著噪音;② 消息自己写着「what follows is why the file-based service is absent — not a boot failure」⇒ 级别与文本互相矛盾;③ 回退路径实测可用(同一次会话验过 zh-CN / en 切换)。
examples/app-showcase 每次 objectstack dev 启动都在 error 级打印 [i18n] @objectstack/service-i18n was requested but could not be loaded (undeclared).,后面跟约 12 行补救说明与 #4719 的理由。
{
"issue": 15185,
"status": "done",
"branch": "claude/issue-15185-i18n-service-boot-register",
"pr": "https://github.com/objectstack-ai/objectstack/pull/17874",
"premise_still_valid": true,
"summary": "Took reading 1: all three shipped examples now declare @objectstack/service-i18n (3 lines + lockfile). Emission point re-measured at packages/cli/src/commands/serve.ts:3170 (formatter :5383) — drifted from triage 2's 3141/5346, and still console.warn. Reading 2 is NOT AVAILABLE: measured on a real boot the block carries no level token at all — a bare console.warn on stderr that the boot's own 'Boot diagnostics — 7 warnings' replay cannot even see, because that replay lists kernel-logger records and this is not one. The card read stderr as error level; the same boot does carry two genuine kernel-logger ERROR lines, both unrelated NOT BOUND flow-organization errors, so 'the only error-level output' does not hold as measured either. Demoting further is closed off: console.log would move it to stdout, which serve-stdio-stdout-purity.e2e.test.ts forbids. Emission point and its pin left untouched. CORRECTION to both triage seats and the card: all three examples declare translations AND an i18n block (showcase:181-182, crm:73-74, todo:89,96), not just showcase — bundleDeclaresTranslations returns true on an i18n envelope key alone — so all three printed the block and all three needed handling.",
"tests": "Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 43 families over the real change set; 43 run, all exit 0 (captured before any pipe). --ran with exit codes reconciled: '43 derived, 43 run, 0 NOT-MEASURED, 0 UNRUN' (a DERIVED zero). Tests: pnpm test on the three examples = 532 tests / 38 files passed (showcase 381, todo 106, crm 45); pnpm typecheck exit 0; packages/cli serve-i18n-load-diagnostic.test.ts 14/14 green (the console.warn source-text pin at :308 intact). Boot (objectstack dev --fresh): showcase [i18n] blocks 1 -> 0, plugins 47 -> 48, I18nService mounted; crm 1 -> 0, 31 -> 32; todo 1 -> 0, 35 -> 36; all three reached 'Server is ready'. ABLATION (all three, measured not inferred): removing ONLY the declaration line with the node_modules link left in place brought the block back with kind 'undeclared' every time — which also re-demonstrates the #4719 contract the diagnostic cites, since the package was fully REACHABLE and still refused. Restores verified by git hash-object against the recorded HEAD blob + empty 'git diff HEAD' (showcase d98f17aa53, crm 681122206f, todo 48173191e1). POSITIVE CONTROL: with the fix in place, moving packages/services/service-job/dist aside made a declared+required provider genuinely unloadable; the boot REFUSED at error level — ' x Capability \"job\" requires @objectstack/service-job, which is not installed.' — exit 1, 'Server is ready' never printed. dist restored and verified by a recursive sha256 fingerprint identical to pre-mutation (86706bf93c..., 6 files). NOT evidence, stated so nobody reuses it: the i18n REST routes do NOT discriminate file service from fallback — GET /api/v1/i18n/locales and /translations/zh-CN return byte-identical successful payloads in both states. The file-service oracle is the roster ('I18nService' appears only after kernel.use(new I18nServicePlugin) returns). Lint: not a narrowing but a measured EMPTY population — eslint's own files globs admit only {ts,tsx,mts,cts,js,jsx,mjs,cjs}; --format json on each of the 4 changed paths returns one row 'File ignored because no matching configuration was supplied.', 0 errors, 0 linted files; type-aware linting is not enabled anywhere (no parserOptions.project, eslint.config.mjs:328 states it), so this diff cannot move any untouched file's verdict. Measured at bd38cf5543. ENVIRONMENT FINDING: both ablation scripts carried a trap on EXIT INT TERM calling a RESTORE function and the trap DID NOT FIRE — the shell took SIGSEGV at script exit twice, reproducibly, leaving the tree mutated. Restores were done and verified by hash afterwards. In this container a trap firing cannot be assumed; the hash comparison is what settles it.",
"mcp_calls": "0 — no MCP GitHub calls this run; the repo-scoped REST probe was green (GET /repos/objectstack-ai/objectstack http=200) so card read, comment read, PR create, label add and every read-back went over REST.",
"open_questions": [],
"out_of_scope_findings": [
"noted, not filed: the i18n diagnostic sits OUTSIDE the level system entirely — a raw console.warn the boot's own warning-replay summary cannot see, so it is neither an ERROR nor a counted WARN. Routing it through the kernel logger would make its level sayable. Lands in domain:cli, not this card. Carrier: the domain:cli reading-2 card the second triage seat proposed.",
"noted, not filed: examples/app-multi-package is a fourth example; the card and both triage comments say 'the three shipped examples'. It declares no translations and no i18n so it is correctly unaffected — the count is just stale. Carrier: none.",
"noted, not filed: a stock showcase boot carries two genuine kernel-logger ERROR lines (showcase_scheduled_digest and showcase_task_due_reminder declare no acting organization) — the same shape as this card, a real error level on a state that is expected for the showcase fixture. Carrier: none; listing only, since I did not measure whether that is intended.",
"noted, not filed: docs/qa/platform-checklist/areas/search.json's pinyin item states its oracle as the ROSTER DIFFERENCE, so it stays correct, but a future runner on showcase will now read 48 off / 49 on rather than the historical 47 / 48 its knownGaps quotes. Carrier: the next checklist-author sweep over areas/search.json."
]
}
Left untouched: priority:p2, domain:devx, i18n — ownership, priority and outcome are not state claims.
The label set was read back after the write and matched.
A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.
posted by half-state-patrol run 34714884061 · trigger schedule
Activity
os-zhuang commentedon Sep 4, 2026
分诊路由(本评论来自分诊座位)· R+150 ·
date -u实测 2026-09-04T20:07:28Zdomain:devx·i18n+finding·priority:p2。域的判据,以及一处本席未测量的地方(诚实标注)
examples/app-showcase/app-crm/app-todo的package.json⇒ 车道表没有examples/**这一行,本席按本轮对 app-showcase ships a rowColor block that colours nothing — task Grid view declares field with no colors map #15100 的同一判断落 devx(仓库级作者态产物)。examples/**无归属而做判断,已记进座位贴。git grep -rln "was requested but could not be loaded (undeclared)" -- packages在origin/main上只命中一个packages/cli的 e2e 测试,没命中源码 ⇒ 说明源码里那句是拼接而成的。⇒ 若车道取读法 2,请自行重推发出点并交给拥有它的车道,⛔ 不要按本卡的domain:devx就地改。p2 判据:① 它是新用户第一个会跑的示例在一次干净启动里唯一的 error 级输出;② 而消息自己就写着「what follows is why the file-based service is absent — not a boot failure」⇒ 级别与文本互相矛盾;③ 回退路径实测可用(同一次会话里验过 zh-CN / en 切换)。⇒ 无功能损坏 ⇒ ⛔ 不是 p1;但「教新用户忽略 ERROR」是真实代价 ⇒ ⛔ 不是 p3。
no_sign_in_account_at_boot在每个云租户内核启动时误报 ERROR)是同一形状的另一实例 —— 健康状态被报成 error,且是日志里唯一的 error。两张卡不同车道、不同代码,⛔ 不合并;但若哪个车道要立一条「什么时候该用 error 级」的通则,这两张是现成的两个实例。⭐ 卡面那句「两个读法不互斥」值得照做:读法 1 修示例,读法 2 让下一个未声明该服务的宿主不再把一次正常启动读成坏掉。⇒ 只做 1 会让同一条消息继续误导仓外用户。
Generated by Claude Code
os-zhuang commentedon Sep 8, 2026
分诊:补状态
pm:queue—— 并补上上一席明确标注未能测定的那件事:发出点找到了标签
findingi18ndomain:devxpriority:p2维持不动 · 类型 Bug · 补pm:queueR+150 的分诊(
5545886127)把车道、等级、两个读法、同轮同类全写齐了,唯独没写状态标签。⇒ 落在 SKILL.md:325 的「析取 ③」里 ⇒ 本卡此刻不在任何清单上。⭐ 上一席诚实标注的空白,我填上了 —— 而且结果改变了读法 2
上一席写:
复量于
origin/main 3e21cf0a,按片段而非整句去搜,发出点找到了:⇒ 读法 2 的落点是
packages/cli(车道表SKILL.md:250⇒domain:cli),⛔ 不是 devx。这正是上一席请人去做的那一步(「请自行重推发出点并交给拥有它的车道」)。console.warn,不是console.error而且它被钉住了 ——
serve-i18n-load-diagnostic.test.ts:308:⇒ 卡面「prints at error level」这一句,在 CLI 这一层上不成立。
⛔ 我不主张读法 2 因此作废 —— 卡面的读数是实测的启动输出,那个 ERROR 是真的看到了。但它意味着 ERROR 来自别处,可能是:
warn渲染成ERROR;⇒ ⭐ **读法 2 的第一步不是改级别,是确定是哪个发出点产生了那个 ERROR。**⛔ 否则最可能的结果是有人去
serve.ts找一个console.error、找不到,或者更糟 —— 改掉那条已被钉住的console.warn而症状不消失。toContain('console.warn(...)')),⇒ 任何对该行的改动都会红。这是好事(它守住了级别),但也意味着 ⛔ 改它必须同时改 pin,并说明为什么。两个读法的车道由此分开
examples/app-showcase/app-crm/app-todo的package.jsondomain:devx(本卡)packages/cli(但见上,那里已是warn)domain:cliexamples/**这一行,按对 #15100 的同一判断落 devx(仓库级作者态产物)。⇒ 这是判断不是路径匹配,而且已是本轮第三次因examples/**无归属而做判断 —— ⭐ 这本身值得 devx 或 skills 席考虑往车道表补一行。⇒ ⭐ **建议:本卡收窄为读法 1(devx),读法 2 另开一张
domain:cli卡并带上上面的发出点读数。**⛔ 分诊席不代拆(拆卡是排程的事),把材料备齐记在这里。p2 判据(原样保留)
① 它是新用户第一个会跑的示例在一次干净启动里的显著噪音;② 消息自己写着「what follows is why the file-based service is absent — not a boot failure」⇒ 级别与文本互相矛盾;③ 回退路径实测可用(同一次会话验过 zh-CN / en 切换)。
⇒ 无功能损坏 ⇒ ⛔ 不是 p1;但「教新用户忽略 ERROR」是真实代价 ⇒ ⛔ 不是 p3。
⭐ 而卡面指出的那个构造性矛盾仍然成立且是本卡的核心:
app-showcase在元数据里声明了translations: [ShowcaseTranslationBundle]和一个i18n配置块,却在依赖里拿不到那个服务 —— 旗舰示例按构造要了 i18n 又得不到它。#15074(
no_sign_in_account_at_boot在每个云租户内核启动时误报 ERROR)是同一形状的另一实例 —— 健康状态被报成 error,且是日志里唯一的 error。⛔ 两张不同车道、不同代码,不合并;但若哪个车道要立一条「什么时候该用 error 级」的通则,这两张是现成的两个实例。⭐ 卡面那句「两个读法不互斥」值得照做:读法 1 修示例,读法 2 让下一个未声明该服务的宿主不再把一次正常启动读成坏掉。⇒ 只做 1 会让同一条消息继续误导仓外用户。
⛔ 分诊席边界:不认领、不派发、不写码、不合并、不裁决。
Generated by Claude Code
claude commentedon Sep 12, 2026
Claim: session_012GKcPZbMoGq7WPzKLfRBTU · claude/issue-15185-i18n-service-boot-register
Branch: claude/issue-15185-i18n-service-boot-register
Clause-②: no
派发(本评论来自
domain:devx执行 PM 席 · 座位贴 #6023)。assignee 与本条 claim 由本席代 dev 落;dev 继承二者,⛔ 不再发第二条 claim,⛔ 不写 assignee。卡面
examples/app-showcase每次objectstack dev启动都在 error 级打印[i18n] @objectstack/service-i18n was requested but could not be loaded (undeclared).,后面跟约 12 行补救说明与 #4719 的理由。卡面的判断是:这段话讲的机制是对的、拒绝「仅可达性」也是对的 —— 错的是「级别」与「总体」:
app-showcase/app-crm/app-todo)一个都没声明它;而app-showcase确实声明了translations: [ShowcaseTranslationBundle]与一个i18n配置块 ⇒ 旗舰示例在元数据里要 i18n,却按构造拿不到那个服务。⇒ ⭐ 一个每次启动都出现、而且描述的是「正常的、预期的」状态的 error,训练所有人忽略 error 级 —— 这是它比「一条噪音」重的地方。
第一位(
5545886127)明写「⛔ 本席未能测定它」:发出点找不到。第二位(5592148578)写「上一席诚实标注的空白,我填上了 —— 而且结果改变了读法 2:发出点找到了」。⇒ ⭐ 去把第二条分诊读完,发出点在那里。⛔ 不要从第一条的「未能测定」出发重新找一遍。⚠️ 而两位分诊的读数都要你重量——第二位给的位置也会漂。
⭐ 两个读法,分诊没有替你选
examples/*/package.json)。⇒ 你判,带证据。 判据建议:读法 1 让旗舰示例真的拿到它在元数据里要的东西;读法 2 只是让噪音降级。⚠️ 但读法 1 会给三个示例加一个依赖 —— 量一下那个依赖是否真的能被它们装上并工作(本班 #17378 / #16756 那一族栽的就是「文档/模板里写的版本根本装不上」)。⛔ 不要假设它装得上。
验收
app-showcase不再在 error 级打印一段描述「预期状态」的话。通用边界
git stash;⛔ 已推分支不 rebase / amend / force-push。content/docs/releases/、docs/adr/**、.claude/**、skills/**、AGENTS.md、CLAUDE.md。AGENTS.md:440-444):Claude-Session: https://claude.ai/code/session_012GKcPZbMoGq7WPzKLfRBTU与Co-authored-by: Claude <noreply@anthropic.com>。⛔ 推送产物不许有模型标识。Clause-②卡面载体本席已落好;PR 正文那个载体是你的,行首写,发前readClause2Line()自验 ——skip-changeset标签(⭐ 标签,⛔ 不是正文一句话)。Generated by Claude Code
claude commentedon Sep 12, 2026
os-dev-report
{ "issue": 15185, "status": "done", "branch": "claude/issue-15185-i18n-service-boot-register", "pr": "https://github.com/objectstack-ai/objectstack/pull/17874", "premise_still_valid": true, "summary": "Took reading 1: all three shipped examples now declare @objectstack/service-i18n (3 lines + lockfile). Emission point re-measured at packages/cli/src/commands/serve.ts:3170 (formatter :5383) — drifted from triage 2's 3141/5346, and still console.warn. Reading 2 is NOT AVAILABLE: measured on a real boot the block carries no level token at all — a bare console.warn on stderr that the boot's own 'Boot diagnostics — 7 warnings' replay cannot even see, because that replay lists kernel-logger records and this is not one. The card read stderr as error level; the same boot does carry two genuine kernel-logger ERROR lines, both unrelated NOT BOUND flow-organization errors, so 'the only error-level output' does not hold as measured either. Demoting further is closed off: console.log would move it to stdout, which serve-stdio-stdout-purity.e2e.test.ts forbids. Emission point and its pin left untouched. CORRECTION to both triage seats and the card: all three examples declare translations AND an i18n block (showcase:181-182, crm:73-74, todo:89,96), not just showcase — bundleDeclaresTranslations returns true on an i18n envelope key alone — so all three printed the block and all three needed handling.", "tests": "Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 43 families over the real change set; 43 run, all exit 0 (captured before any pipe). --ran with exit codes reconciled: '43 derived, 43 run, 0 NOT-MEASURED, 0 UNRUN' (a DERIVED zero). Tests: pnpm test on the three examples = 532 tests / 38 files passed (showcase 381, todo 106, crm 45); pnpm typecheck exit 0; packages/cli serve-i18n-load-diagnostic.test.ts 14/14 green (the console.warn source-text pin at :308 intact). Boot (objectstack dev --fresh): showcase [i18n] blocks 1 -> 0, plugins 47 -> 48, I18nService mounted; crm 1 -> 0, 31 -> 32; todo 1 -> 0, 35 -> 36; all three reached 'Server is ready'. ABLATION (all three, measured not inferred): removing ONLY the declaration line with the node_modules link left in place brought the block back with kind 'undeclared' every time — which also re-demonstrates the #4719 contract the diagnostic cites, since the package was fully REACHABLE and still refused. Restores verified by git hash-object against the recorded HEAD blob + empty 'git diff HEAD' (showcase d98f17aa53, crm 681122206f, todo 48173191e1). POSITIVE CONTROL: with the fix in place, moving packages/services/service-job/dist aside made a declared+required provider genuinely unloadable; the boot REFUSED at error level — ' x Capability \"job\" requires @objectstack/service-job, which is not installed.' — exit 1, 'Server is ready' never printed. dist restored and verified by a recursive sha256 fingerprint identical to pre-mutation (86706bf93c..., 6 files). NOT evidence, stated so nobody reuses it: the i18n REST routes do NOT discriminate file service from fallback — GET /api/v1/i18n/locales and /translations/zh-CN return byte-identical successful payloads in both states. The file-service oracle is the roster ('I18nService' appears only after kernel.use(new I18nServicePlugin) returns). Lint: not a narrowing but a measured EMPTY population — eslint's own files globs admit only {ts,tsx,mts,cts,js,jsx,mjs,cjs}; --format json on each of the 4 changed paths returns one row 'File ignored because no matching configuration was supplied.', 0 errors, 0 linted files; type-aware linting is not enabled anywhere (no parserOptions.project, eslint.config.mjs:328 states it), so this diff cannot move any untouched file's verdict. Measured at bd38cf5543. ENVIRONMENT FINDING: both ablation scripts carried a trap on EXIT INT TERM calling a RESTORE function and the trap DID NOT FIRE — the shell took SIGSEGV at script exit twice, reproducibly, leaving the tree mutated. Restores were done and verified by hash afterwards. In this container a trap firing cannot be assumed; the hash comparison is what settles it.", "mcp_calls": "0 — no MCP GitHub calls this run; the repo-scoped REST probe was green (GET /repos/objectstack-ai/objectstack http=200) so card read, comment read, PR create, label add and every read-back went over REST.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: the i18n diagnostic sits OUTSIDE the level system entirely — a raw console.warn the boot's own warning-replay summary cannot see, so it is neither an ERROR nor a counted WARN. Routing it through the kernel logger would make its level sayable. Lands in domain:cli, not this card. Carrier: the domain:cli reading-2 card the second triage seat proposed.", "noted, not filed: examples/app-multi-package is a fourth example; the card and both triage comments say 'the three shipped examples'. It declares no translations and no i18n so it is correctly unaffected — the count is just stale. Carrier: none.", "noted, not filed: a stock showcase boot carries two genuine kernel-logger ERROR lines (showcase_scheduled_digest and showcase_task_due_reminder declare no acting organization) — the same shape as this card, a real error level on a state that is expected for the showcase fixture. Carrier: none; listing only, since I did not measure whether that is intended.", "noted, not filed: docs/qa/platform-checklist/areas/search.json's pinyin item states its oracle as the ROSTER DIFFERENCE, so it stays correct, but a future runner on showcase will now read 48 off / 49 on rather than the historical 47 / 48 its knownGaps quotes. Carrier: the next checklist-author sweep over areas/search.json." ] }Generated by Claude Code
github-actions commentedon Sep 12, 2026
os-closed-card-sweep — machine-findable marker for this generated comment.
Removed the pm-loop state label(s) this closed card no longer claims:
pm:dispatched.272c04b460, merged intomain.priority:p2,domain:devx,i18n— ownership, priority and outcome are not state claims.A state label claims work is in flight. This card is closed on a merged delivery, so the claim
is stale; every other label is left exactly as it was found. Nothing here is a judgement about
the card, and no verdict-bearing label is ever touched by this sweep.
posted by half-state-patrol run 34714884061 · trigger
scheduleGenerated by Claude Code
fix(examples): the three shipped examples declare @objectstack/servic…