Uh oh!
There was an error while loading. Please reload this page.
fix(cli): os i18n check --help names the whole surface set it reports on, derived from the taxonomy - #15062
Conversation
The description named five source kinds — object/field/option/view/action — against the fifteen-member `CoverageIssue['source']` taxonomy the report actually carries. A five-of-fifteen sample reads as a scope statement rather than an illustration: a reader who wanted app navigation or dashboard widgets checked concluded the command did not cover them. Derive the phrase from a `Record<CoverageIssue['source'], string>` map so the help text and the taxonomy are one fact with one source: a new union member is a compile error until it is named, and is published in `--help` in the same edit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
…ived surface set oclif renders a command in two places with different budgets: the COMMANDS list shows `summary` (falling back to the description's first line), and flips the whole list to a multi-line layout once an entry wraps past five lines. So the short line goes in `summary` with the parenthetical dropped outright, and the full derived surface set goes in `description`, where a reader asking what the command covers is actually looking. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
…ing it oclif prints `summary` and then `description` in the DESCRIPTION section, so a description that repeats the summary sentence renders it twice. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
…tement The A/B run at COLUMNS=80 shows the description-only shape wraps to five lines in the COMMANDS column — at oclif's `lines.length > 4` multiline threshold, not past it. Say what was measured. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
…8n-check-help-source-count
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 22 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c7c5f8ff1dd58d2741d0e961db8004b190d3ba3f && git checkout c7c5f8ff1dd58d2741d0e961db8004b190d3ba3f
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 101ad2cc13fafd8a3879ffc4a6ce6133c02ed832 ff207ebab3b80e92544cea0b4160025f5935f902 && git checkout -B drift-repro 101ad2cc13fafd8a3879ffc4a6ce6133c02ed832 && git merge --no-ff ff207ebab3b80e92544cea0b4160025f5935f902
node scripts/docs-audit/affected-docs.mjs --json 101ad2cc13fafd8a3879ffc4a6ce6133c02ed832
|
Uh oh!
There was an error while loading. Please reload this page.
…arity Brings in PR #15062 (`os i18n check --help`), which lands in packages/cli while this branch is open — the dispatch named it as the one to merge and re-verify against. Also #15065 (rest), #15063/#15067 (docs) and #15055 (spec). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
Fixes#13837
os i18n check --helpdescribed the command as detecting "missing translation keys (object/field/option/view/action labels)". That parenthetical named 5 source kinds. The report carries 15. A five-of-fifteen sample does not read as an illustration — it reads as a scope statement, so a reader who wanted app navigation or dashboard widgets checked was told this command does objects and fields, and either skipped the gate or went looking for a second tool that does not exist. This is the CLI-side twin of the sentence PR #13833 corrected in the publishedobjectstack-i18nskill; fixing only the skill leaves the CLI teaching it at the moment a user runs the command.What I measured on today's
main(nothing inherited)Triage measured 5-named against a 14-member union, and one small fixture reaching 12 kinds / 783 issues (
metadataForm769). Re-derived fromorigin/mainat99b4deba49, both numbers have moved and are re-taken here.1 — the taxonomy is 15 members, not 14.
datasethas been added to theCoverageIssue.sourceunion since triage.2 — the retired parenthetical named 5 of those 15:
object,field,option,view,action. Ten unnamed, not nine.3 — an ordinary stack reaches 14 of the 15 kinds in one run. Driving the real walker (
collectExpectedEntries) and the real detector (computeI18nCoverage) over one object, one global action, one app with nested navigation, one dashboard with a widget, one dataset, and one page carrying a filter-preset tab — the fixture now committed as this PR's pin:The one kind that did not appear is
flow, and the reason is mechanical rather than incidental:authorWarnedTranslationGroups()returns["flows"](the shipped liveness ledger has that group atstatus: planned/authorWarn: true), andcollectExpectedEntriesfilters every entry whose group is warned. See the rationale block inpackages/cli/src/utils/i18n-extract.ts— that suppression is deliberate and self-lifting the day the screen-flow runner lands. The description still names flow screens, matching both the taxonomy and the landed skill twin; see "One thing I did not decide" below.The fix
Wording. The parenthetical is gone from the line a user meets first, and the full surface set is stated where they go to read it.
Mechanism (the optional half — taken). The set is not retyped beside the taxonomy; it is derived from it.
packages/cli/src/utils/i18n-coverage.tsgainsSOURCE_SURFACE, aRecordkeyed byCoverageIssue.sourcewhose value is the plural surface noun for each kind, and exportsCOVERAGE_SOURCE_KINDSandCOVERAGE_SURFACE_PHRASEbuilt from it. TheRecordkeyed by the union is what makes it exhaustive by construction.Declared file face. The card expected
check.tsplus its pin. Deriving the phrase needs a second source file:packages/cli/src/utils/i18n-coverage.ts, where the taxonomy lives. Declaring that here, as the claim comment asks. Nothing else is touched;packages/specis untouched, the skill text is untouched,content/docs/releases/is untouched.Why an oclif
summaryas well — no sibling command sets one, so this is the shape change worth naming. oclif renders a command in two places with different budgets. The COMMANDS list printssummary, falling back to the description's first line when a command sets none; the command's own--helpprints both. Measured at COLUMNS=80, the derived set in the COMMANDS column wraps to five lines, atrenderList'slines.length > 4multiline threshold. So the short line goes insummarywith the parenthetical dropped outright — triage's own second option, never a sample — and the derived set goes indescription.Rendered from the built CLI:
The A/B that produced the five-line reading was run on the build artifact (
dist/commands/i18n/check.js), not on source:summarydeleted and the one-line description restored,os i18n --helprendered, then the artifact restored and proved byte-identical by sha256 (76e899fa...before and after).Red-first, and which arm fired
Three arms, each mutating on disk under
traponEXIT INT TERMwith absolute paths, each mutation proved by blob hash and marker counts, each restore proved by blob-hash equality against the HEAD blob. Finalgit status --porcelainempty.Arm A — the defect's own red. Retired description restored on
check.ts(static override summaryoccurrences 0, retired-sample occurrences 1; HEAD blobeba0697d, on diskfca8c60c). The pin goes RED, 4 failed / 2 passed:Restored: on disk
eba0697d== HEAD blobeba0697d.Arm B1 — the derivation is enforced, not decorative. A sixteenth member
'sandbox'added toCoverageIssue.sourceand deliberately not named.tsc --noEmitrefuses, exit 1:Arm B2 — the derivation is proved to move. The same member added and named (
sandbox: 'sandboxes'), with zero edits tocheck.ts:That is the arm the card asked for: the derived phrase follows a union change on its own, and a member that would go unnamed cannot compile.
Ablation validity. No rebuild is required for these arms and none is claimed: the pin imports
../src/commands/i18n/check.jsand../src/utils/i18n-coverage.js— same-package relative specifiers vitest and tsx resolve to source, never through a packageexportsmap todist. The dependency closure (pnpm --filter '@objectstack/cli^...' build) was built before any reading was taken, and no dependency was mutated.Verification
Heavy runs through
scripts/pm/os-verify-lock.sh; every exit code captured before any pipe; verdicts quoted from each gate's own verdict line.Re-run in full after the final commit (
origin/mainmerged in), atgit rev-parse --short HEAD=ff207ebab3— 55/55 build tasks (FULL TURBO), 5 test files / 69 tests passed, typecheck clean:pnpm exec turbo run build --filter='@objectstack/cli^...'— VERDICT command-exit 0pnpm --filter @objectstack/cli exec vitest run --maxWorkers=2 test/i18n-check-help-surface-parity.test.ts test/i18n-coverage.test.ts test/commands.test.ts test/i18n-declared-surface-gate.test.ts test/i18n-dataset-coverage.test.ts— 5 files, 69 tests passedpnpm --filter @objectstack/cli typecheck— clean, including the test layer (tsc --noEmit && check:test-typecheck; the ledger holds at 3 files / 28 errors / 6 pinned signatures, unchanged). Worth stating becausepackages/cli/tsconfig.jsonisinclude: ['src'], sotsc --noEmitalone says nothing aboutpackages/cli/test/**— thecheck:test-typecheckleg againsttsconfig.test.jsonis what covers the new pin.pnpm --filter @objectstack/cli build— VERDICT command-exit 0 (for the rendered--helpabove; taken at315e17753c, the last commit before the merge, and the strings it renders are unchanged since)Gate union re-derived at the final HEAD after merging
origin/main:node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsgives 42 families. Run: 37 green. Reported honestly as NOT MEASURED, not as passes and not as reds — five, each self-declaring it:check-test-completeness— exit 3; needs a savedturbo run testlog, which only CI produces. The script names this branch as the family-list invocation and says to record it as NOT MEASURED.check-half-states/check:pm-half-states— exit 3; needs repo-scoped REST, which this session does not have (a repo-scoped probe returned 403GitHub access is not enabled for this session).check:dual-build-cjs-loads— exit 3 PREREQUISITE NOT MET; 12 packages have nodist/.check:i18n-coverage— exit 3 PREREQUISITE NOT MET;examples/app-showcasecould not lint because@objectstack/connector-mcphas no build output in this worktree. Nothing was compared, so the baseline says nothing either way. Declared narrowing: the build it names was queued for the shared verify lock and did not get a turn. This change adds two exported constants and one map; it alters no walker, no detector and no key path, so the coverage baseline has no route to move — but that is an argument, not a measurement, and CI runs the family.check:type-check-debt— its--re-measureleg needs the whole workspace closure built; not attempted for the same reason. Its structural half,check:type-check-coverage, ran green, andpackages/cli's owncheck:test-typecheckledger is unchanged.Repo-wide ESLint (
pnpm lint) is CI's run and is not claimed here.One thing I did not decide, and did not bury
The description names flow screens, and today no flow gap can be reported: the ledger suppression measured above filters the whole
flowsgroup out of the walk. I followed the taxonomy and the landed skill twin (#13833 names "flow screens" in the same sentence) rather than diverging the two halves of a deliberately paired fix, and the suppression lifts itself with no edit here. I did not open a new card for it: #11617 is open and already carries the "flows become visible once the bucket lands" state, andi18n-extract.tsdocuments the collision that produced the suppression. Flagged here so it is triaged rather than settled by a wording PR.Changeset
.changeset/tricky-pans-repeat.md, patch on@objectstack/cli. Measured rather than assumed:packages/cliis notprivate, and itsfilesfield is["dist","README.md","CHANGELOG.md"]—dist/commands/i18n/check.jscarries the string oclif prints, so this help text ships to users from a published package.🤖 Generated with Claude Code
https://claude.ai/code/session_016yfqQh2dBgPAymYd7xipza
Generated by Claude Code