Skip to content

refactor(lint): move the i18n translatable-key walk from the CLI into @objectstack/lint - #30375

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-eme43k
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-eme43k

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Requested by Yibo Wang · project thread

Part of #30285

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:把「哪些翻译键存在、各自原文是什么」的遍历(collectExpectedEntries,2,520 行)原样从 CLI 挪进 @objectstack/lint,CLI 改为再导出;不改任何键、骨架或覆盖率结论。
  • 为什么改:Studio translation editor: a source-to-translation table with visible gaps, replacing the raw JSON boxes #30285 要在 Studio 里做「原文 / 译文」对照表,浏览器必须跑同一套遍历;CLI 是 Node 包,浏览器用不了,lint 已被控制台打包使用。
  • 风险与回滚:纯搬移。PR 计 +879 / −682(1561 行,低于 3000 行的人工合并线,check-governed-merges --pr 30375 读数)。回滚即 revert 本 PR。
  • 先看:packages/lint/src/i18n-walk.ts(重命名自 packages/cli/src/utils/i18n-extract.ts,看头部说明与删掉的尾部)、packages/cli/src/utils/i18n-bundle-extract.ts(保留的打包/序列化与再导出)、packages/cli/test/i18n-bundle-extract-exports.pin.test.ts(导出不变的钉测)。
  • 两分钟验证:pnpm --filter @objectstack/cli exec vitest run test/i18n-bundle-extract-exports.pin.test.ts test/i18n-walk-output-parity.test.ts,预期全部通过。
  • 需要你做的:无。不是受管面,也未过行数线,席位直接落地。

What changes

collectExpectedEntries, the one definition of which translation keys a stack can carry and the source text each one translates, moves from packages/cli/src/utils/i18n-extract.ts to packages/lint/src/i18n-walk.ts. It is exported from the @objectstack/lint root together with authorWarnedTranslationGroups, humanizeFieldPath and the ExpectedEntry / ExpectedEntryOptions types. The point is that a browser can run the same walk the CLI runs. The follow-up PR for #30285 builds Studio's source-to-translation table from it.

  • Layout. The walk keeps git's rename: i18n-extract.ts → lint/src/i18n-walk.ts, 17 lines added and 618 removed in place. The removed lines are the extract types and the tail: bundle assembly, extractTranslations and serialization. That tail stays in the CLI as a new file, packages/cli/src/utils/i18n-bundle-extract.ts (631 lines). It imports the walk from @objectstack/lint and re-exports it, so the CLI module offers exactly the names the old one did.
  • Importers. 44 CLI importers (src and test) are repointed from i18n-extract to i18n-bundle-extract, with no other edit. normalized-call-sites.test.ts renames its key to the new path.
  • Ledgers and scripts. The action and view liveness ledgers cite the walk at its new path (4 cites). check-i18n-walk-parity.mjs reads the walk source at its new path and adds it to its watch hints. dispatch-gates.self-test.mjs uses the new CLI file as its specimen. Comment-only path repoints are in i18n-bundle-surface.mjs, check-i18n-bundles.mjs and three lint files.
  • Browser default. In a browser the walk cannot read the liveness ledger from disk (resolveLivenessDir answers null), so its default leaves no translation group out. That is the same set the CLI leaves out today, because the ledger warns on no translation group. A lint test pins it, so the day a group is warned, the test fails and a browser caller has to pass warnedGroups.
  • Changeset. @objectstack/lint, @objectstack/cli and @objectstack/spec patch (spec for the liveness cites).

No key, skeleton or coverage verdict changes. The walk's code moved as is.

Tests

  • packages/lint/src/i18n-walk.test.ts (new): a stack with an object, a select field with options and an app with navigation yields the expected keys, each with its source text and source. A warned group is left out. The shipped ledger warns on no translation group (the browser-parity pin).
  • packages/cli/test/i18n-bundle-extract-exports.pin.test.ts (new): the CLI module's runtime exports equal the old module's 12 names exactly. The walk functions are lint's own (toBe), not a copy. The six type exports are still offered (held by type-check).
  • The CLI's existing i18n suites (about 45 files) reach the walk through the re-export unchanged.

Acceptance notes

  • Size. check-governed-merges --pr 30375 puts 0 of 61 paths on the register and reads the size from the PR object, as the queue guard does: +879 / −682 = 1561, under the 3000-line human-merge line. The --branch forecast below counts with --no-renames (+3382 / −3185 = 6567) and over-reads a rename; it is not the reading the queue guard applies. lint cannot import from cli, because cli depends on lint.
  • Stale comment cites left as they are (comments only, no behaviour): spec i18n-resolver.ts (3), i18n-resolver.object-list-views.test.ts, platform-objects source-hash.ts, two echo-decisions tests, cli tsconfig.test.json and test-typecheck-debt.json. They name the old path in prose, and editing them would widen the diff into packages this move does not otherwise touch.
  • Gate derivation gap (not fixed here). check:i18n and check:i18n-coverage derive from packages/cli/src/** only, so a later edit to packages/lint/src/i18n-walk.ts alone does not derive them locally. CI still runs them. This PR touches packages/cli/src, so both were derived and run here.

Verification

lint and cli, under os-verify-lock
lint test :: exit 0
lint typecheck :: exit 0
cli unit :: exit 0
cli typecheck :: exit 0
VERDICT command-exit 0 · held the lock 913s (shared box)
dispatch-gates: 87 derived, 87 run, all exit 0 (--ran: 0 NOT-MEASURED, 0 UNRUN)
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-adoption.mjs :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs --self-test :: exit 0
node scripts/check-declaration-mirrors.mjs :: exit 0
node scripts/check-dev-prereqs.mjs --self-test :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-self-test-wired.mjs --self-test :: exit 0
node scripts/check-self-test-wired.mjs :: exit 0
node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0
node scripts/check-self-test-workflow-commands.mjs :: exit 0
node scripts/check-whole-set-label-write.mjs --self-test :: exit 0
node scripts/check-whole-set-label-write.mjs :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm --filter @objectstack/spec run check:empty-state :: exit 0
pnpm --filter @objectstack/spec run check:liveness :: exit 0
pnpm --filter @objectstack/spec run check:strictness-ledger :: exit 0
pnpm --filter @objectstack/spec run check:variant-docs :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:bash32-floor :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:cli-command-ids :: exit 0
pnpm check:cli-test-child-env :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:docs-transcript-drift :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 0
pnpm check:engine-double-contract :: exit 0
pnpm check:entry-guard :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:i18n :: exit 0
pnpm check:i18n-coverage :: exit 0
pnpm check:i18n-walk-parity :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:merge-driver :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:parse-guard :: exit 0
pnpm check:platform-checklist :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-dispatch-gates :: exit 0
pnpm check:pnpm-filter-targets :: exit 0
pnpm check:pnpm-flag-spellings :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:ratchet-remedy-authority :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:scripts-symbol-anchors :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0

check-plugin-teardown-shape.mjs --self-test first exited 3 because this shallow clone could not read its pinned positive-control commit (621a487). After git fetch --depth=1 origin 621a4876… it exits 0 (48 cases). That is an environment condition, not this diff.

The 87 ran serially under os-verify-lock on a shared box (40m14s).

check-governed-merges --branch claude/project-thread-eme43k (head f6ecb06)
governed-surface predicate: 0 of 61 path(s) hit the register
size: +3382 / -3185 over 61 file(s) (--no-renames)
HUMAN MERGE OR AUTHORIZED APPROVAL - 6567 changed line(s) > 3000
exit 3

Generated by Claude Code

… @objectstack/lint

collectExpectedEntries and its helpers move, by rename, from
packages/cli/src/utils/i18n-extract.ts to packages/lint/src/i18n-walk.ts and
are exported from @objectstack/lint, so a browser (Studio's translation editor)
can run the same walk the CLI runs. The CLI keeps bundle assembly and
serialization in utils/i18n-bundle-extract.ts, which re-exports the walk names,
so the module surface is unchanged (pinned). Liveness evidence, the walk-parity
gate and the dispatch-gates specimen follow the move.

Claude-Session: https://claude.ai/code/session_01WGufzfqCqBH59yrHmuqXH3
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/cli, @objectstack/lint, @objectstack/spec, touching 127 documentable anchor(s). ⚠️ 6 changed file(s) yielded no anchor (packages/cli/src/utils/i18n-coverage.ts, packages/lint/src/index.ts, packages/lint/src/validate-translatable-sections.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

44 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 0e4dfbe27a2a312864cd8d14e4954b6af14e2a22.

⛔ 14 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 6 changed file(s) yielded no anchor (packages/cli/src/utils/i18n-coverage.ts, packages/lint/src/index.ts, packages/lint/src/validate-translatable-sections.ts, …) — pages documenting those are invisible to this run
  • 2 anchor(s) matched too much of the corpus to be a work list: objectName (symbol, 56 pages), objectName (literal, 56 pages)
  • 7 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 103 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 179 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0e4dfbe27a2a312864cd8d14e4954b6af14e2a22 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 140f246573e91c9feea13b8d11db7bb16514daac — the merge of head 6950aec746cb42060cbcb2d1940648b53a42c9a4 into base 0e4dfbe27a2a312864cd8d14e4954b6af14e2a22, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 140f246573e91c9feea13b8d11db7bb16514daac && git checkout 140f246573e91c9feea13b8d11db7bb16514daac
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0e4dfbe27a2a312864cd8d14e4954b6af14e2a22 6950aec746cb42060cbcb2d1940648b53a42c9a4 && git checkout -B drift-repro 0e4dfbe27a2a312864cd8d14e4954b6af14e2a22 && git merge --no-ff 6950aec746cb42060cbcb2d1940648b53a42c9a4

node scripts/docs-audit/affected-docs.mjs --json 0e4dfbe27a2a312864cd8d14e4954b6af14e2a22

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 0e4dfbe27a2a312864cd8d14e4954b6af14e2a22 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-support-ai · session_01WGufzfqCqBH59yrHmuqXH3

CI note, read at 2026-11-05T08:23Z: Spec property liveness is red on this PR for a reason outside it.


Generated by Claude Code

Brings in #30368 (the query.searchFields liveness repoint), which clears
main's red on Spec property liveness and Test Core.

Claude-Session: https://claude.ai/code/session_01WGufzfqCqBH59yrHmuqXH3
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-support-ai · session_01WGufzfE4V9fzZgJLrLDmMC

维护者速读(席位终稿)

  • 改了什么:把「一个应用有哪些可翻译的键、各自原文是什么」的遍历 collectExpectedEntries 原样从 CLI 挪进 @objectstack/lint;CLI 保留打包与写文件部分,并再导出原来的 12 个名字。不改任何键、骨架或覆盖率结论。
  • 为什么改:Studio translation editor: a source-to-translation table with visible gaps, replacing the raw JSON boxes #30285 要在 Studio 里做「原文 / 译文」对照表,浏览器必须跑同一套遍历,而 CLI 是 Node 包。
  • 风险与回滚:低。纯搬移,席位逐行对过:搬出的 618 行与新 CLI 文件只差头部注释和 import 两行。PR 计 +879 / −682 = 1561 行。回滚即 revert 本 PR。
  • 先看:packages/lint/src/i18n-walk.ts(由 packages/cli/src/utils/i18n-extract.ts 改名而来)与 packages/cli/src/utils/i18n-bundle-extract.ts。
  • 两分钟验证:pnpm --filter @objectstack/cli exec vitest run test/i18n-bundle-extract-exports.pin.test.ts test/i18n-walk-output-parity.test.ts。
  • 需要你做的:无。不是受管面,也未过 3000 行线,席位直接落地。
  • 席位意见:ACCEPT。之前说要人工合并是席位读错了行数(用了 --branch 的 --no-renames 预估),按 --pr 读数改正,正文同步更正。

席位核对 · ACCEPT · head 6950aec7 · 2026-11-05T10:53Z

  1. Shape: draft, base main, Part of #30285 (PR-B, the Studio table, follows), no other issue number in the body. 60 files, +879/−682. check-governed-merges --pr 30375: 0 of 61 paths governed; size 1561 ≤ 3000, read from the PR object as the queue guard reads it. Ordinary queue landing. The body's earlier "human merge" lines came from the --branch forecast, which counts with --no-renames (6567); corrected in the body in this stroke.
  2. Read by the seat: the rename cli/src/utils/i18n-extract.ts → lint/src/i18n-walk.ts adds 17 lines (header prose; authorWarnedProperties / walkPageComponents now imported relatively from ./lint-liveness-properties.js / ./page-walk.js) and removes 618. Those 618 lines, compared with the new cli/src/utils/i18n-bundle-extract.ts (631), differ only in header prose and the @objectstack/lint import / re-export lines: the code moved as is. lint's new imports are all @objectstack/spec/* subpaths it already depends on. The 44 importer edits are one-line path repoints; lint/src/index.ts adds the five public names. The liveness ledgers change cites only (4). packages/lint is private: false: the changeset names lint, cli and spec (spec for the ledger cites), and its prose matches the diff.
  3. Accepted deviations: stale prose cites of the old path left in eight files outside the move's packages (listed in the body); a follow-up gap noted in the body: check:i18n / check:i18n-coverage derive from packages/cli/src/** only, so a later lint-only edit to the walk does not derive them locally (CI still runs them).
  4. Evidence: check-runs on 6950aec7: 55 success, 9 skipped; check-expected-skips --pr 30375 OK (all 9 in main's roster). Dev's local battery: lint + cli unit and typecheck exit 0, dispatch-gates 87/87 exit 0 with --ran clean. No merge=os-regen path touched. mergeable_state: clean.
  5. Out of scope: the derivation gap above goes with PR-B's card thread to name or file; Skills-docs: n/a (refactor, no authoring change).
  6. Landing: ordinary queue, now: relay pr_ready + automerge_enable, follow to MERGED, then PR-B for Studio translation editor: a source-to-translation table with visible gaps, replacing the raw JSON boxes #30285 (Fixes #30285) is reviewed the same way.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 10:54
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 5, 2026 10:54
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 8293c74 Nov 5, 2026
69 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-eme43k branch November 5, 2026 11:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant