Skip to content

i18n: metadata label lookup falls through to the en bundle on a zh-CN workspace — localeChain defaults fallbackChain to ['en'] and ignores i18n.fallbackLocale, so an authored Chinese label loses to a courtesy English bundle #14882

Description

@baozhoutao

Summary

For an app whose metadata labels are authored in the workspace default locale (zh-CN, inline label: '填报单') and which ships only an en translation bundle as a courtesy for English users, the metadata API serves the English bundle labels to a zh-CN request. The authored Chinese label never wins because the label lookup's fallback chain is hard-wired to ['en'], independent of the stack's i18n.fallbackLocale.

The effect on screen: object title Entry Sheet, columns Sheet / Plan / Subject / Status / Final Score, app name KPI Assessment — sitting next to fields that have no en entry and therefore correctly show 主体类型 / 填报说明, and next to platform chrome that is fully Chinese. A zh-CN deployment cannot ship an en bundle without breaking its own default language.

Minimal reproduction

Platform 17.2.0, objectstack dev, SQLite, single tenancy, fresh DB.

objectstack.config.ts:

i18n: { defaultLocale: 'zh-CN', supportedLocales: ['zh-CN', 'en'], fallbackLocale: 'zh-CN' },
translations: [defineTranslationBundle({
  en: { objects: { kpi_entry_sheet: { label: 'Entry Sheet', pluralLabel: 'Entry Sheets',
        fields: { name: { label: 'Sheet' }, status: { label: 'Status' }, total_score: { label: 'Final Score' } } } } },
})],

kpi_entry_sheet object: label: '填报单', fields name: Field.text({ label: '填报单名称' }), status: Field.select({ label: '状态' }), total_score: Field.number({ label: '最终得分' }), subject_type: Field.select({ label: '主体类型' }) (no en entry for subject_type).

GET /api/v1/i18n/locales
→ {"locales":[{"code":"zh-CN","isDefault":true},{"code":"en","isDefault":false}]}

GET /api/v1/meta/object/kpi_entry_sheet   (Accept-Language: zh-CN, signed in as admin)
→ item.label        = "Entry Sheet"     ← expected 填报单
  item.pluralLabel  = "Entry Sheets"
  fields.name       = "Sheet"           ← expected 填报单名称
  fields.status     = "Status"          ← expected 状态
  fields.total_score= "Final Score"     ← expected 最终得分
  fields.subject_type = "主体类型"       (no en entry → authored label survives)
  fields.created_at = "创建时间"         (platform zh-CN pack → correct)

GET /api/v1/meta/object/kpi_entry_sheet   (Accept-Language: en)
→ identical for the app fields; created_at = "Created At"

GET /api/v1/meta/app/kpi_app               (Accept-Language: zh-CN)
→ label = "KPI Assessment"                ← authored label is 'KPI 考核管理'

Same request with no Accept-Language header: same English result (the workspace default is zh-CN).

Control: remove the en bundle → every label is Chinese. So the presence of a bundle for a non-requested locale changes what the requested locale sees.

Where

@objectstack/spec system — the label resolvers (lookupObjectField, lookupObjectFieldAttr, lookupAppAttr, resolveViewLabel, lookupTabLabel, …) all iterate localeChain(opts):

function localeChain(opts) {
  const locale = opts?.locale ?? "en";
  const fallbacks = opts?.fallbackChain ?? ["en"];   // ← hard-wired
  ...
  for (const code of [locale, ...fallbacks]) ...
}

and return the first bundle hit before the caller falls back to the authored label. The server-side caller does not pass a fallbackChain, so i18n.fallbackLocale: 'zh-CN' has no effect on the chain: zh-CN → en → authored. For any object the zh-CN bundle does not mention (i.e. every object whose labels are authored in Chinese), the en bundle is consulted before the authored label.

Expected

One of (in order of preference):

  1. The chain honours the stack's i18n.fallbackLocale / defaultLocale instead of a literal ['en'], so a zh-CN workspace resolves zh-CN → (fallbackLocale) → authored label, and en is only consulted when en is requested.
  2. When the requested locale is the workspace default locale, the authored label wins over any other locale's bundle (the authored label is the default-locale text — that is what defaultLocale declares).
  3. Failing both, the metadata API should at least not report locale: zh-CN while serving en strings; the i18n coverage tooling should flag "label served from a locale other than the requested/default one".

Side effects seen in the app (for triage, same root cause)

  • Related lists show what looks like a duplicate column pair 指标 / Indicator: indicator (lookup, no en entry → authored 指标) next to indicator_name (has en entry → Indicator). With the fix both read Chinese (指标 / 指标名称).
  • Record page title suffix (客户满意度 Indicator), breadcrumb (Indicator, Assessment Plan), related-list section headings (Entry Line 0, Indicator 0) all flip to English on a zh-CN page.

Environment

@objectstack/* 17.2.0 (runtime, spec, cli, console 17.2.0) · Node 22 · better-sqlite3 · single tenancy · objectstack dev. App: objectstack-ai/kpi (private) — the config above is a faithful excerpt.

Activity

  1. baozhoutao commented on Sep 3, 2026

    @baozhoutao
    ContributorAuthor

    Additional data point — the platform's own coverage gate and the runtime disagree about what "default locale" means:

    $ os i18n check
      1252 warning(s) across 2 locale(s)
      ⚠ Non-default locales have gaps but the default locale is fully covered.
    

    os i18n check treats the authored (Chinese) labels as full coverage of the default locale zh-CN — no zh-CN bundle is required, zero errors. The runtime label lookup does not: with no zh-CN bundle entry it walks localeChain to en and serves the English bundle to a zh-CN request. So an app that passes the coverage gate 100 % for its default locale still renders that locale in English wherever a secondary-locale bundle exists.

    Whichever way this is ruled, the two should agree:

    • if authored labels are the default-locale source (what i18n check assumes), the lookup must stop at the authored label for the default locale instead of trying other bundles first;
    • if every supported locale must ship a bundle (the os i18n extract per-locale workflow), then i18n check should report the missing default-locale bundle as the error it already claims to treat as one, and the docs should say the authored label is not consulted when any other bundle exists.

    App-side we can work around it by generating the zh-CN bundle from schema labels (os i18n extract --locales=zh-CN), which is the documented per-locale layout — noting it here so the ruling can say whether that is the intended contract or a workaround.

  2. os-zhuang commented on Sep 4, 2026

    @os-zhuang
    Contributor

    分诊路由(本评论来自分诊座位)· R+150 — domain:spec · pm:queue · 保留 bug + i18n + priority:p2。

    落点实测(origin/main,fetch 后 git grep):

    packages/spec/src/system/i18n-resolver.ts:283   function localeChain(opts?: ResolveOptions): string[] {
    packages/spec/src/system/i18n-resolver.ts:285     const fallbacks = opts?.fallbackChain ?? ['en'];
    

    ⇒ 报告里逐字引的那两行就在 packages/spec,['en'] 硬编码今天仍在树上 ⇒ 前提成立,落点 domain:spec(单一属主车道)。

    bug 而非 feature,判据要紧:栈声明了 i18n.fallbackLocale,而解析链不读它 ⇒ 这是「声明 ≠ 执行」,按机械边界判据属 Bug/整理侧,⛔ 不上人工地板。⚠️ 但派发时必须把范围钉在这一半:让链尊重已声明的 fallbackLocale / defaultLocale。

    ⭐ 卡上 2026-09-03T07:43Z 那条补充评论(5522341222)里藏着一个不同性质的问题,⛔ 别顺手一起裁掉:os i18n check 报「默认语言覆盖完整」(把作者写的标签当作默认语言的来源),而运行期解析不这么认为 —— 门禁与运行期对「默认语言」的定义不一致。「每个 supported locale 是否都必须出 bundle」是契约问题,不是本卡的 bug 半边。⇒ 若实现时发现不回答它就改不动,停下来拆卡送决策箱,⛔ 不要在一个 bug 修复里顺手定契约。

    p2 维持(⛔ 本席未提级,理由写下):这条确实是用户可见、可复现、影响一个 zh-CN 部署的默认语言(17.2.0 实测),但 ① 无数据损坏、② 只在部署额外发了第二个 locale bundle 时才发作、③ 报告人自己已给出可用规避(os i18n extract --locales=zh-CN 生成 zh-CN bundle,且那是文档化的 per-locale 布局)。⇒ 不到 p1。⚠️ 若该规避被裁为不是预期契约,严重度应重估。


    Generated by Claude Code

  3. os-project-manager commented on Sep 5, 2026

    @os-project-manager
    Collaborator

    Claim: PM loop round R1
    Session: session_01M59rPZZFzqhfMUPFqqZTkf
    Branch: claude/issue-14882-i18n-fallback-chain-honours-config
    Worktree: objectstack-issue-14882
    Domain: domain:spec
    File surface: packages/spec/src/system/i18n-resolver.ts (localeChain at :283–:285 on 1be26b0, ResolveOptions.fallbackChain at :199, the lookup-order docblock at :34) + its tests · the server-side producer of the ResolveOptions passed to the label resolvers (to be located by the dev on origin/main — nothing outside packages/spec passes fallbackChain today, measured 01:48Z; the caller is where i18n.fallbackLocale / defaultLocale must be threaded from, and if it lives in packages/rest / plugin-hono-server / packages/metadata the dev reports the location and fixes on the producer side, not with a consumer-side patch) · generated followers · changeset. Stop on breach; explain in the report.
    Container & model: M, mode:subagent, model: fable (claude-fable-5-1) — node scripts/pm/dispatch-gates.mjs --tier packages/spec/src/system/i18n-resolver.ts run 2026-09-05T01:48Z against the tree at 1be26b0: "no path-derived mandate", "Clause ② SUSPECT surface" hint; tier set from card CONTENT — the default resolution behaviour of a published @objectstack/spec/system resolver changes (which bundle wins for a default-locale request), and the claim-time rule is "not sure ⇒ yes" ⇒ CONTRACT_REVIEW_TIER (claude-fable-5-1, re-derived from origin/main:scripts/pm/dispatch-gates.mjs at 01:44Z).
    Clause-②: yes
    Serial constraints cleared: packages/spec/src/system/i18n-resolver.ts has no in-flight claim and is not on this seat's hot-file queue at 01:49Z; system/tenant.zod.ts and the generated i18n bundles are HELD by draft PR #15626 (#14478) — this card does not touch them, and if the dev's diff regenerates an i18n bundle it stops and reports before enqueuing; queued sibling #14972 (injected system-column labels, objectql/spec) is not in flight and is ⛔ not folded here (different defect, different fix). Same-round dispatches #14079 and #14954 are source-disjoint; generated corpus lands serially via scripts/pm/os-regen-merge.sh. Predecessor's linger PRs #15438 / #15639 / #15645 / #15626 touch none of this surface.


    Generated by Claude Code

  4. claude commented on Sep 5, 2026

    @claude
    Contributor

    Claim (os-dev): dispatched dev for this card, PM loop round R1
    Session: session_01M59rPZZFzqhfMUPFqqZTkf
    Branch: claude/issue-14882-i18n-fallback-chain-honours-config (pushed empty at claim time)
    Worktree: objectstack-issue-14882 on origin/main 7087f99c
    Scope: the config-honouring half only — the resolution chain reads the stack's declared i18n.fallbackLocale / defaultLocale instead of a literal en list; the gate-vs-runtime "what is the default locale" contract question from comment 5522341222 is not decided here.
    File surface: packages/spec/src/system/i18n-resolver.ts + tests, plus the server-side producer of the resolver options (location to be reported).


    Generated by Claude Code

  5. claude commented on Sep 5, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 14882,
      "status": "done",
      "branch": "claude/issue-14882-i18n-fallback-chain-honours-config",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15707",
      "premise_still_valid": true,
      "summary": "Premise verified on origin/main 7087f99c: localeChain still defaults fallbackChain to a literal en list and nothing outside packages/spec passed a chain. Producer located: packages/rest/src/rest-server.ts — translateMetaItem, translateMetaItems, translateMetaTypesResponse and the public-form schema path all built { locale, packagedBase } with no chain, and they could not thread the declaration because II18nService exposed no fallback accessor (only getDefaultLocale). Fix is producer-side in three pieces: optional II18nService.getFallbackLocale?() on the spec contract; FileI18nAdapter.getFallbackLocale() in service-i18n (I18nServicePlugin already receives fallbackLocale || defaultLocale || 'en' from the stack config on both boot paths); RestServer.translateOptionsFor(i18n, locale) threading fallbackChain: [fallback] at all four seams, feature-detected so a provider without the accessor (older host, core in-memory fallback) keeps today's behaviour. The card's reproduction (defaultLocale zh-CN, fallbackLocale zh-CN, en-only bundle, zh-CN request) now resolves zh-CN -> zh-CN -> authored label; en requests still get the en bundle; a shipped zh-CN bundle still wins over the authored label. H2: the resolver's own en default is unchanged and singled out in the PR body for the contract reviewer (only reachable after this PR by a service with no declared fallback). H3 did not fire as a stop: the config-honouring half was deliverable without deciding the default-locale contract question; that case (defaultLocale zh-CN, fallbackLocale en, no zh-CN bundle) is pinned unchanged (en bundle text) at the resolver and the REST seam and is in open_questions. H5: i18n-resolver.ts changed only in docblocks; check:generated reports all 15 artifacts up to date (no api-surface or docs delta from the optional contract member). One mechanical follower: check-system-context-census reported pure line rot on five manage_metadata anchors in content/docs/permissions/system-context.mdx after the helper was inserted above them — repaired with its own --fix (one line), re-check green. Deviations: the assignee field was already the PM's at claim time (not touched); dedup search went REST 403 -> MCP (2 calls, the first 422'd on punctuation); the finding's title lost a backticked token to shell substitution on POST and was repaired by PATCH with read-back. needs:contract-review label: applied via additive REST POST at PR creation, read back present alongside the size-labeler's documentation/size/l/tests/tooling/protocol:system; PR body read-back is content-identical to what was sent, the platform only moved the rule-line + session footer block to the very end (known behaviour, not repaired).",
      "tests": "Union re-run on ebf647748 (git rev-parse --short HEAD, clean tree): spec src/system/i18n-resolver.test.ts 'Test Files 1 passed (1) / Tests 239 passed (239)'; service-i18n full 'Test Files 5 passed (5) / Tests 74 passed (74)'; rest meta-i18n-declared-fallback-chain + meta-object-i18n-explicit-override + meta-plural-i18n 'Test Files 3 passed (3) / Tests 34 passed (34)'; os-verify-lock 'VERDICT command-exit 0 · held the lock 21s'; check-system-context-census 'OK — 106 elevation read sites in 20 packages across 45 files, all anchored; 140 anchors resolve'; check-nul-bytes 'OK (scanned 7580 text file(s))'. Full consumer suites on the source tree of 0fe0153fb (ebf647748 differs only by the one census docs line outside every package), each under the lock with VERDICT command-exit 0: spec 'Test Files 472 passed (472) / Tests 12692 passed (12692)' (6m22s); core '49 passed / 1190 passed'; lint '96 passed / 3011 passed'; client '33 passed / 437 passed'; rest '181 passed / 3088 passed'; runtime '226 passed / 3243 passed'; cli --project unit '174 passed / 2324 passed | 6 expected fail (2330)' (integration tier declared to CI per the 2026-09-01 ruling). Typecheck exit 0 for spec (tsc + scripts + test-typecheck OK), rest, service-i18n, core, lint, client, runtime, cli. Builds: turbo closure 71 workspace packages, all exit 0. Ablation at the REST seam, fix committed first (0fe0153fb): mutation = translateOptionsFor returns { locale } only, confirmed on disk by grep -c marker=1 and removed-text=0 (subject is a same-package relative import, so no dist rebuild leg applies): 'Tests 6 failed | 7 passed (13)' — the five card-shape cases plus the zh-CN-bundle field case red; en-request, no-bundle control, feature-detection and H3 pins green; restore via git checkout HEAD -- path, git hash-object equal to the HEAD blob 01ba6324, git diff HEAD empty, git status --porcelain empty. Gates: pnpm --filter @objectstack/spec check:generated 'All 15 generated artifacts are up to date'; dispatch-gates derived (no paths, from ebf647748's merge base 7087f99c): every runnable spec/lint/repo gate exit 0 with its own verdict line — api-surface 'public API surface + factory signatures unchanged', authorable-surface, browser-reachable-entries, docs '230 generated files in sync', dual-source-exports, empty-state, entry-nameability, error-code-provenance, export-origins, exported-any, liveness, llms-txt, meta-url-spelling, react-blocks, skill-refs, spec-changes, strictness-ledger, variant-docs, yaml-examples, doc-formula-expressions; repo: nul-bytes, changeset-gate-self-tests, cross-package-test-inputs, doc-authoring, logger-receiver-detach, authz-resolver, dispatcher-error-vocabulary, auth-mount-ledger, console-injection, docs-image-tag, driver-memory-census, engine-double-contract, error-code-casing, error-status-conformance, filter-alias-parity, i18n-stale-fill, merge-driver, objectql-double-limit, objectui-changeset, org-identifier, overlay-whitelist-table, page-declaration-shape, partof-closing-keyword (also on the PR body: 'no Part-of/closing-keyword contradiction'), pm-governed-prose, pm-half-states, pm-label-desc-cap, pm-skill-ratchet, query-options-erasure, refd-timer-probe, route-envelope, single-claim-paths self-test, slot-lookup, spec-parsed-alias, stack-collection-maps, tenant-chokepoint, type-source-resolution, watch-hint-literal, where-matcher, widget-option-census, test-source-alias, type-check-coverage, published-files, published-readme-exports (after the full build: '60 published document(s) ... 198/198'), dual-build-cjs-loads ('103 published require entry point(s) ... load'), type-check-debt ('--re-measure: OK — 12 ledger entr(ies) re-measured'), plus 25 node-script gates from the derived list (adr-0087-registration 'adds no declared-breaking changeset (3 non-breaking changeset(s) seen)', changeset-no-major, empty-changeset, ci-filter-parity, comment-mask, keyed-text-bounds, platform-object-tenancy-census, plugin-teardown-shape, registry-log-declared, tenant-audit-census, undeclared-dep-imports, system-context-census after --fix, docs-audit, release-rehearsal-clone, pr-labels). Added beyond the lead and run: the whole derived list above. Not measured by design: check:react-declaration-parity (needs objectui's manifest) and CI-context-only invocations (RUNNER_TEMP, matrix shards, PR_NUMBER/claim paths). No repo-wide eslint run: not owed, CI's.",
      "mcp_calls": "2 — both mcp__github__search_issues for the out-of-scope dedup after REST /search returned 403 (the first 422'd on punctuation; the second returned 5 results including #14882 as the in-set control, no duplicate). Everything else (issue read, comments, claim, finding, PR, labels, report comment) went through repo-scoped REST, probed 200 first.",
      "open_questions": [
        {
          "question": "Gate-vs-runtime definition of 'default locale' (card comment 5522341222): is the authored label the default-locale source, so that a request for the DEFAULT locale stops at the authored label before any other locale's bundle — or must every supported locale ship a bundle? The config-honouring half did not need this answered, but one declared shape still exposes it: defaultLocale 'zh-CN' with fallbackLocale 'en' and no zh-CN bundle. Before and after this PR a zh-CN request there gets the en bundle text (chain zh-CN -> en -> authored), pinned unchanged at the resolver (i18n-resolver.test.ts 'a chain that DECLARES en still consults en before the authored label') and at the REST seam (meta-i18n-declared-fallback-chain.test.ts §5). Callers answering it today: os i18n check (treats authored labels as full default-locale coverage, zero errors) vs the REST metadata reads (walk the declared chain before the authored label).",
          "options": [
            "A — authored label IS the default-locale source: when the requested locale resolves to the workspace defaultLocale, the resolvers stop at the authored label before consulting any other bundle (spec-side rule; i18n check's current claim becomes true at runtime; the reporter's zh-CN extract workaround becomes optional).",
            "B — every supported locale must ship a bundle: keep the runtime as it is after this PR and make os i18n check report a missing default-locale bundle as the error it already claims to treat as one; document that the authored label is not consulted while another declared bundle exists.",
            "C — leave both as they are (declared fallbackLocale is honoured literally; the gate keeps its claim) and document the divergence."
          ],
          "recommendation": "A, because it removes a declaration the runtime does not honour (defaultLocale declares that the authored text is that locale's text; the gate already reads it that way) and makes an AI-authored app structurally harder to get wrong (no second bundle needed to keep the default language intact), at the cost of a contract change to the resolvers' order for the default locale only; B is the alternative if the maintainer wants bundles to be the single source. Not decided here; the PR keeps the case unchanged."
        },
        {
          "question": "Should localeChain's literal ['en'] default (a caller that passes no fallbackChain at all) become [] — 'requested locale, then the authored label' — now that the only production caller declares a chain? Kept unchanged in this PR and singled out for the contract reviewer.",
          "options": [
            "A — keep ['en'] (this PR): a provider that declares no fallback (older host, core in-memory fallback) keeps today's answer.",
            "B — change to []: an undeclared chain means no fallback, so 'declared but not executed' cannot recur; blast radius is any caller passing only { locale } — after this PR none in production in this repo, objectui does not call these resolvers; pin 'a caller that declares NO chain keeps the resolver's literal en default' flips."
          ],
          "recommendation": "B in a follow-up once the first question is ruled, because it closes the class; not in this PR because it changes a published default's behaviour and the dispatch reserved that judgment for contract review."
        }
      ],
      "out_of_scope_findings": [
        "filed as #15694: the core in-memory i18n fallback (createMemoryI18n) never receives the declared i18n.fallbackLocale — AppPlugin injects defaultLocale and supportedLocales but not fallbackLocale, and its t() falls back to defaultLocale only, so one declaration answers differently under the memory fallback and under service-i18n (labels: finding, i18n; dedup: MCP search returned no existing card; #14382 is the unrelated boot-diagnostic duplicate-line card)."
      ]
    }

    Generated by Claude Code

  6. claude commented on Sep 5, 2026

    @claude
    Contributor

    Clause-② determination (domain:spec seat, session_01M59rPZZFzqhfMUPFqqZTkf, 2026-09-05T03:52Z) — yes, both limbs: content limb — PR #15707 adds a new optional contract member II18nService.getFallbackLocale?(): string | undefined on @objectstack/spec/contracts (a new key on a published face is the mechanical floor) and rewrites the ResolveOptions.fallbackChain contract text; path limb — packages/spec/src/contracts/i18n-service.ts, packages/spec/src/system/i18n-resolver.ts.

    needs:contract-review is now hung on BOTH carriers: the dev hung it on PR #15707 at creation (head ebf647748), this seat hung it on the card in this stroke (labels read back: bug · domain:spec · i18n · needs:contract-review · pm:dispatched · priority:p2). The in-seat contract-tier review follows in this round; both carriers are cleared in one stroke on PASS.


    Generated by Claude Code

  7. 5 remaining items

  8. claude commented on Sep 5, 2026

    @claude
    Contributor

    os-dev-report-addendum

    Patch lap for PR #15707 (merge only; review already done on the card).

    • New head: 1bb7857de (pushed, plain push). Merge commit: 9b230fadb (origin/main 1c1421401 merged into the branch via bash scripts/pm/os-regen-merge.sh; no rebase, no force-push, no stash). Regeneration commit: 1bb7857de.
    • What the regeneration changed: content/docs/permissions/system-context.mdx only, one table row, line numbers only — the manage_metadata row's rest-server.ts anchors, main's :5016, :6442, :6690, :7121, :7314 re-anchored to :5052, :6478, :6726, :7157, :7350 by pnpm gen:system-context-census on the merged tree (main had moved those sites by +12 lines; this branch's helper adds +36 above them). Staged diff inspected before the commit; the pre-commit hook printed content/docs/permissions/system-context.mdx — current and os-regen: all deferred artifacts are current — marker cleared.
    • Union re-run on 1bb7857de (clean tree), each under scripts/pm/os-verify-lock.sh: spec src/system/i18n-resolver.test.ts Test Files 1 passed (1) / Tests 239 passed (239); service-i18n full Test Files 5 passed (5) / Tests 74 passed (74); rest meta-i18n-declared-fallback-chain + meta-object-i18n-explicit-override + meta-plural-i18n Test Files 3 passed (3) / Tests 34 passed (34); lock line VERDICT command-exit 0 · held the lock 20s · waited 311s (5m11s); node scripts/check-system-context-census.mjs OK — 106 elevation read sites in 20 packages across 45 files, all anchored; 140 anchors resolve, 27 declared non-read; pnpm --filter @objectstack/spec check:generated All 15 generated artifacts are up to date. Builds on the merged tree under the lock: spec (VERDICT command-exit 0 · held the lock 130s), rest + service-i18n closures (25 successful, 25 total, VERDICT command-exit 0 · held the lock 137s).
    • PR body untouched: check:partof-closing-keyword on the live body reads no Part-of/closing-keyword contradiction. needs:contract-review not re-hung (the reviewing seat re-hangs and re-clears). PR reads mergeable_state: blocked (draft, checks pending) — no longer dirty.
    • Worktree removed again after confirming remote == local head.

    Generated by Claude Code

  9. claude commented on Sep 5, 2026

    @claude
    Contributor

    Review addendum — regen lap 2 on PR #15707 (domain:spec seat, session_01M59rPZZFzqhfMUPFqqZTkf, 2026-09-05T06:48Z). Head moved 1bb7857de → b881599cc: a merge of origin/main 8e500f23e plus one regeneration commit touching only content/docs/permissions/system-context.mdx (line-number re-anchoring of the manage_metadata row, produced by pnpm gen:system-context-census on the merged tree). Re-read: origin/main is an ancestor of the new head; the PR's file set against origin/main is still the same 13 files; no source hunk changed. ACCEPT stands; Clause-② PASS stands (5549189393). Because the head moved after the carriers were cleared, both carriers were re-hung and re-cleared in this stroke so the pair check reads the fixed spelling. Landing: flip ready + auto-merge (squash) as soon as every check on b881599cc concludes green and mergeable_state is not dirty — main moves every few minutes, so the flip is done immediately on green rather than at a fixed time.


    Generated by Claude Code

  10. claude commented on Sep 5, 2026

    @claude
    Contributor

    os-dev-report-addendum

    Second patch lap for PR #15707 (merge only; review already done on the card).

    • New head: b881599cc (pushed, plain push, remote == local). Merge commit: 352a9aba1 (origin/main 8e500f23e merged into the branch via bash scripts/pm/os-regen-merge.sh; no rebase, no force-push, no stash). Regeneration commit: b881599cc.
    • What the regeneration changed: content/docs/permissions/system-context.mdx only, one table row, line numbers only — the manage_metadata row's rest-server.ts anchors, main's :5048, :6474, :6722, :7153, :7346 re-anchored to :5084, :6510, :6758, :7189, :7382 by pnpm gen:system-context-census on the merged tree (main moved those sites again by +32 since the last lap; this branch's helper adds +36 above them). Staged diff inspected before the commit; the pre-commit hook printed content/docs/permissions/system-context.mdx — current and os-regen: all deferred artifacts are current — marker cleared.
    • Union re-run on b881599cc (clean tree) under scripts/pm/os-verify-lock.sh (first attempt returned 99 — queue wait exhausted; the parked slot resumed and the retry ran): spec src/system/i18n-resolver.test.ts Test Files 1 passed (1) / Tests 239 passed (239); service-i18n full Test Files 5 passed (5) / Tests 74 passed (74); rest meta-i18n-declared-fallback-chain + meta-object-i18n-explicit-override + meta-plural-i18n Test Files 3 passed (3) / Tests 34 passed (34); lock line VERDICT command-exit 0 · held the lock 28s · waited 0s; node scripts/check-system-context-census.mjs OK — 106 elevation read sites in 20 packages across 45 files, all anchored; 140 anchors resolve, 27 declared non-read; pnpm --filter @objectstack/spec check:generated All 15 generated artifacts are up to date. Build on the merged tree under the lock: spec + rest + service-i18n closures 25 successful, 25 total, VERDICT command-exit 0 · held the lock 339s (5m39s) · waited 132s.
    • PR body untouched; needs:contract-review not re-hung (the reviewing seat re-hangs and re-clears). Worktree removed again after confirming remote == local head.

    Generated by Claude Code

  11. claude commented on Sep 5, 2026

    @claude
    Contributor

    Landing provenance (domain:spec seat, session_01M59rPZZFzqhfMUPFqqZTkf, 2026-09-05T07:18Z) — PR #15707 at head b881599cc flipped ready and auto-merge (squash) armed at 2026-09-05T07:16Z; enqueue read from the timeline in the same minute (added_to_merge_queue events seen: 1).

    Pre-landing checks per the 2026-08-31 ruling (in-seat contract PASS ⇒ clear both carriers ⇒ land): ① in-seat contract-tier PASS on this card (5549189393), re-affirmed on both regen laps (5549338305 lap 1 via the dev, seat addendum 5550076938 lap 2); ② needs:contract-review re-hung and re-cleared on both carriers after each head move, pair check node scripts/pm/check-clause2-carriers.mjs --pair 15707 exit 0 at 2026-09-05T07:15Z; ③ every check run on b881599cc concluded — 40 runs, 0 not green at 07:15Z; governed-surface predicate on the 13-path file list: NOT governed (2026-09-05T03:51Z, file list unchanged since). Closing-keyword two-read: first line Fixes #14882; #14972 and #15694 are verb-less references.

    On MERGED this seat probes getFallbackLocale in packages/spec/src/contracts/i18n-service.ts and translateOptionsFor in packages/rest/src/rest-server.ts on origin/main against the pre-merge tip, strips pm:dispatched, clears the assignee, and posts the landing note. The reserved contract question lives in #15711 (decision inbox); the dev-filed #15694 stays bare for triage.


    Generated by Claude Code

  12. claude commented on Sep 5, 2026

    @claude
    Contributor

    Landing note (domain:spec seat, session_01M59rPZZFzqhfMUPFqqZTkf, 2026-09-05T08:06Z) — PR #15707 merged via the merge queue as a84e1ced6 at 2026-09-05T08:05:43Z, after two regen laps (generated system-context.mdx only) and a usage-wall gap.

    Landing probe on origin/main a84e1ced6 at 2026-09-05T08:06Z: getFallbackLocale present in packages/spec/src/contracts/i18n-service.ts and translateOptionsFor present in packages/rest/src/rest-server.ts; control — the pre-merge tip c99449ab5 has 0 hits for both. Card closed by Fixes #14882; pm:dispatched stripped and the assignee cleared in one write from a fresh read, read back. Lane inventory re-pulled: only this card closed on this merge. The reserved contract question is #15711 (decision inbox); the dev-filed #15694 stays bare for triage. PR #15786 (#14972, same file, disjoint region) is checked for dirty in the same stroke.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions