Skip to content

web(i18n): one-way ceiling on isZh branching (#5519) - #5805

Merged
Hmbown merged 1 commit into
mainfrom
fix/web-iszh-ceiling-20260901
Sep 1, 2026
Merged

web(i18n): one-way ceiling on isZh branching (#5519)#5805
Hmbown merged 1 commit into
mainfrom
fix/web-iszh-ceiling-20260901

Conversation

@Hmbown

@Hmbown Hmbown commented Sep 1, 2026

Copy link
Copy Markdown
Owner

Adds the ceiling test #5519 asked for: files branching on isZh outside web/lib/i18n must never exceed 28 (the count on main today). Migration work lowers the count; the constant ratchets down with it.

vitest run lib/i18n/iszh-ceiling.test.ts — 1 passed.


Note

Low Risk
Adds a regression test only; no runtime or product behavior changes.

Overview
Adds a Vitest guard for the isZhlib/i18n migration: it scans the web tree for .js/.ts/.jsx/.tsx files that contain isZh while excluding lib/i18n and test files, and fails if that count exceeds 28 (today’s baseline on main).

The test is documented as a one-way ratchet—migration should shrink the offender list; CEILING is only lowered after branches move into lib/i18n, so new scattered isZh branching cannot creep back in.

Reviewed by Cursor Bugbot for commit 015e177. Bugbot is set up for automated code reviews on this repo. Configure here.

The isZh migration was losing ground (12 -> 31 files over 90 days at
filing; 28 today outside web/lib/i18n). Count the files that still
branch on isZh outside the migration home and fail web CI if the number
grows. Migrating branches lowers the count; then ratchet the CEILING.

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Copilot AI lite review requested due to automatic review settings September 1, 2026 21:44
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@cursor

cursor Bot commented Sep 1, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_b1052a9b-3530-441f-9e2e-68b0d5f76d26)

@claude

claude Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 3m 2s —— View job


Code review: one-way isZh ceiling test

  • Gather context (PR diff, changed files)
  • Understand the request (code review of the ceiling test)
  • Read web/lib/i18n/iszh-ceiling.test.ts and related i18n code
  • Check correctness, reuse/simplification, test coverage
  • Post final review

Summary: Small, self-contained addition — a single vitest file that walks web/ and fails if more than CEILING (28) non-lib/i18n, non-.test. files still reference isZh. The intent (one-way ratchet, #5519) is good and the comment explains how to lower CEILING later. Two things worth a look before merge, neither blocking:

  1. CEILING may be looser than "today's count." I couldn't execute vitest in this sandbox (no node_modules, and running node/scripts here needs approval I don't have), so I reconstructed the exact filter by hand from a grep -l isZh over **/*.{ts,tsx,js,jsx} and applying the same !startsWith("lib/i18n") / !/\.test\.[jt]sx?$/ filters used in iszh-ceiling.test.ts:26-31. That reconstruction lands on 27 offending files today, not 28:
    lib/page-meta.ts, 6 files under components/, and 20 files under app/[locale]/....
    Since the assertion is toBeLessThanOrEqual(CEILING) (iszh-ceiling.test.ts:36), a CEILING of 28 still passes at 27 — it's not a failing bug — but it means the ratchet has one file of slack baked in from day one: a future PR could add one new isZh branch outside lib/i18n without tripping this test, which is slightly looser than the PR description's claim that 28 "is the count on main today." Worth double-checking with an actual vitest run and setting CEILING = 27 if that's what it reports, so the ceiling is exact rather than off-by-one loose.

  2. file.startsWith(MIGRATION_HOME) (iszh-ceiling.test.ts:28) is a string-prefix check, not a path-segment check. MIGRATION_HOME is "lib/i18n" with no trailing separator, so a hypothetical sibling directory like lib/i18n-legacy/ would also match startsWith("lib/i18n") and be silently excluded from the count. No such directory exists today (only lib/i18n), so this isn't live, but file.startsWith(MIGRATION_HOME + sep) (or path.sep) would be more robust against a future directory name collision.

Nits (not blocking): the recursive walk() scans all of web/ (including public/, brand/, data/, etc.), filtered only by extension — fine for a one-off CI test, just slightly more I/O than scoping to app/, components/, and lib/.

No correctness bugs, no provider/model-routing surface touched (this is web i18n only), and no missing test coverage — the PR is the test.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not approved: Cursor Bugbot was present but skipped (usage limit), so the required automated-review signal did not complete successfully. Human review is needed; one reviewer was assigned.

Open in Web View Automation 

Sent by Cursor Approval Agent: Pull Request Router and Approver

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The new test is currently prone to false positives and environment-dependent behavior (cwd + generated dirs), which can make the guard flaky and reduce its signal.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds a Vitest regression guard intended to prevent the isZh migration from regressing by enforcing a one-way ceiling on isZh usage outside web/lib/i18n.

Changes:

  • Introduces web/lib/i18n/iszh-ceiling.test.ts that walks the web/ tree, collects .js/.ts/.jsx/.tsx files, and fails if “offender” files exceed a fixed ceiling (28).
  • Reports offender paths in the assertion message to make regressions actionable.
File summaries
File Description
web/lib/i18n/iszh-ceiling.test.ts Adds a filesystem-scanning Vitest guard to cap isZh usage outside lib/i18n.
Review details

Suppressed comments (2)

web/lib/i18n/iszh-ceiling.test.ts:15

  • The directory walk currently doesn’t exclude .open-next, .wrangler, or .vercel (all common local build artifacts here). If they exist, this test will waste time scanning generated bundles and may become flaky if those bundles contain isZh.
  for (const entry of readdirSync(dir)) {
    if (entry === "node_modules" || entry === ".next" || entry === ".git") continue;
    const full = join(dir, entry);

web/lib/i18n/iszh-ceiling.test.ts:31

  • .includes("isZh") will count non-code mentions (e.g., components/nav.tsx has a comment that mentions isZh while explicitly having no isZh branch). Stripping comments before matching (and using a word-boundary regex) keeps this guard focused on real code usage and reduces false positives.
      .filter(
        (file) =>
          !file.startsWith(MIGRATION_HOME) &&
          !/\.test\.[jt]sx?$/.test(file) &&
          readFileSync(join(ROOT, file), "utf8").includes("isZh"),
      )
  • Files reviewed: 1/1 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +1 to +6
import { describe, expect, it } from "vitest";
import { readdirSync, readFileSync, statSync } from "node:fs";
import { join, relative } from "node:path";

const ROOT = process.cwd();
const MIGRATION_HOME = join("lib", "i18n");

@codewhale-agent codewhale-agent Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codewhale review

Adds a Vitest one-way ceiling test for isZh branching outside web/lib/i18n. The approach is simple, but path resolution and text matching can make the count unstable or incorrect depending on invocation and repository layout.

Findings

  • [WARNING] ROOT depends on process.cwd() (web/lib/i18n/iszh-ceiling.test.ts:5)
    The scan root is process.cwd(), so running the test from the repo root instead of the web package changes what is scanned. The test lives under web/lib/i18n and MIGRATION_HOME is relative to web (lib/i18n), so from repo root the intended migration home is not excluded and the count is wrong. Derive ROOT from the test file location instead.
  • [WARNING] MIGRATION_HOME prefix check is not path-segment safe (web/lib/i18n/iszh-ceiling.test.ts:28)
    file.startsWith(MIGRATION_HOME) also excludes paths such as lib/i18n-utils or lib/i18next, and it does not account for Windows path separators. Use file.startsWith(MIGRATION_HOME + sep) or compare path segments.
  • [INFO] Text search counts any mention of "isZh", not actual branching (web/lib/i18n/iszh-ceiling.test.ts:30)
    The test reads each file and checks .includes("isZh"), so comments, strings, type names, or import references can count as branching. This may produce false positives and require ceiling adjustments for non-branching mentions. Consider using an AST or a more targeted regex for actual isZh conditional expressions if precision is desired.
  • [INFO] walk() only skips node_modules, .next, and .git (web/lib/i18n/iszh-ceiling.test.ts:14)
    Other generated or vendor directories such as dist, build, .turbo, or coverage may be walked and can contain transpiled files with isZh, causing false counts or slow runs. Add project-specific ignore patterns or use Vitest config root exclusions.

Suggestions

  • web/lib/i18n/iszh-ceiling.test.ts:3 — Import the platform path separator so the migration-home prefix check is segment-safe.

    import { join, relative, sep } from "node:path";
    
  • web/lib/i18n/iszh-ceiling.test.ts:28 — Compare with the platform separator so only actual children of lib/i18n are excluded and Windows paths work correctly.

              !file.startsWith(MIGRATION_HOME + sep) &&
    
  • web/lib/i18n/iszh-ceiling.test.ts:5 — Derive ROOT from the test file location rather than process.cwd() so the test scans the web package even when vitest is invoked from the repo root. This requires importing fileURLToPath from node:url and resolving ../.. from this file.

Assessment

The test is a useful regression guard, but it should be made invocation-independent and path-segment-aware before relying on it in CI. The current text-matching and ignore-list limitations are acceptable for a coarse ceiling but should be documented.


Advisory review by Codewhale (codewhale review --pr 5805 --post, head 015e1770ef6fc91309f48d3addd8661abf95f36d). Line-specific findings are also posted as inline review comments; mechanical fixes arrive as committable suggestions you can apply from the Files tab. CODEOWNERS approval still governs merge.

import { readdirSync, readFileSync, statSync } from "node:fs";
import { join, relative } from "node:path";

const ROOT = process.cwd();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING] ROOT depends on process.cwd()

The scan root is process.cwd(), so running the test from the repo root instead of the web package changes what is scanned. The test lives under web/lib/i18n and MIGRATION_HOME is relative to web (lib/i18n), so from repo root the intended migration home is not excluded and the count is wrong. Derive ROOT from the test file location instead.

.map((file) => relative(ROOT, file))
.filter(
(file) =>
!file.startsWith(MIGRATION_HOME) &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[WARNING] MIGRATION_HOME prefix check is not path-segment safe

file.startsWith(MIGRATION_HOME) also excludes paths such as lib/i18n-utils or lib/i18next, and it does not account for Windows path separators. Use file.startsWith(MIGRATION_HOME + sep) or compare path segments.

(file) =>
!file.startsWith(MIGRATION_HOME) &&
!/\.test\.[jt]sx?$/.test(file) &&
readFileSync(join(ROOT, file), "utf8").includes("isZh"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[INFO] Text search counts any mention of "isZh", not actual branching

The test reads each file and checks .includes("isZh"), so comments, strings, type names, or import references can count as branching. This may produce false positives and require ceiling adjustments for non-branching mentions. Consider using an AST or a more targeted regex for actual isZh conditional expressions if precision is desired.


function walk(dir: string, out: string[] = []): string[] {
for (const entry of readdirSync(dir)) {
if (entry === "node_modules" || entry === ".next" || entry === ".git") continue;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[INFO] walk() only skips node_modules, .next, and .git

Other generated or vendor directories such as dist, build, .turbo, or coverage may be walked and can contain transpiled files with isZh, causing false counts or slow runs. Add project-specific ignore patterns or use Vitest config root exclusions.

@@ -0,0 +1,38 @@
import { describe, expect, it } from "vitest";
import { readdirSync, readFileSync, statSync } from "node:fs";
import { join, relative } from "node:path";

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import the platform path separator so the migration-home prefix check is segment-safe.

Suggested change
import { join, relative } from "node:path";
import { join, relative, sep } from "node:path";

.map((file) => relative(ROOT, file))
.filter(
(file) =>
!file.startsWith(MIGRATION_HOME) &&

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Compare with the platform separator so only actual children of lib/i18n are excluded and Windows paths work correctly.

Suggested change
!file.startsWith(MIGRATION_HOME) &&
!file.startsWith(MIGRATION_HOME + sep) &&

import { readdirSync, readFileSync, statSync } from "node:fs";
import { join, relative } from "node:path";

const ROOT = process.cwd();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Derive ROOT from the test file location rather than process.cwd() so the test scans the web package even when vitest is invoked from the repo root. This requires importing fileURLToPath from node:url and resolving ../.. from this file.

@Hmbown
Hmbown merged commit 8e4b682 into main Sep 1, 2026
33 of 36 checks passed
@Hmbown
Hmbown deleted the fix/web-iszh-ceiling-20260901 branch September 1, 2026 22:07
Hmbown pushed a commit that referenced this pull request Sep 1, 2026
…5797, #5796, #5795, #5805, #5800)

Signed-off-by: CodeWhale Bot <bot@codewhale.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants