[10/N] test(zh): add objective Chinese regression guards / 新增客观中文回归 CI 守卫 - #841
[10/N] test(zh): add objective Chinese regression guards / 新增客观中文回归 CI 守卫#841edwingao28 wants to merge 8 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Claude finished @edwingao28's task in 4m 9s —— View job Re-review:
|
| Test | Reported violation | Looks like |
|---|---|---|
i18n.test.ts |
route-sibling missing zh for /model, /model/[slug] |
Genuine — new routes with no /zh sibling (or need explicit exemption) |
blog-content.test.ts (jalapeño) |
protected-token missingFromZh=["MW"×8] missingFromEn=["tok/s/user"×3] |
False positive — bare unit MW in prose ("throughput per MW") is legitimately rendered into Chinese; the identical-multiset check over-fires |
blog-content.test.ts (agentx) |
inline-code + protected-token --enable-prefill-delayer |
EN writes the flag as bare prose, ZH wraps it in ` backticks → real EN/ZH inconsistency the guard can't reconcile |
zh-copy.test.ts |
chip-untranslated ×2 in jalapeño zh |
False positive — fires on the proper noun "Hot Chips" / English URLs / "chiplet", not an untranslated common-noun "chip" |
Why it matters: the PR cannot merge with a red required check, and the failures show the guard currently flags legitimately-translated content. Because these tests scan all repo content rather than the PR diff, this guard will also fail CI on unrelated future PRs whenever a post uses a bare unit word in prose or a "chip" proper noun.
Fix: rebase onto master, then for each case either (a) tighten the rule so bare units in prose and proper-noun "chip"/"Hot Chips" don't count, (b) add a documented entry to zh-objective-guard-exceptions.json, or (c) bring the content into compliance (add the /model zh pages or exempt them; make EN/ZH backtick the flag consistently). The MW/tok/s/user and "Hot Chips" cases specifically read as guard over-reach — the kind of context-dependent terminology the PR description says stays out of deterministic CI.
🟡 WARNING — ReDoS in the link-reference regex (zh-objective-guard.ts:563)
CodeQL alert #25 is a true positive. (?:\\.|[^\]\n])+ lets a backslash match two ways, giving exponential backtracking (measured 12.8s at 30 chars). Trusted build-time input so not exploitable, but a malformed line could hang CI. One-char fix suggested inline ([^\]\n] → [^\]\n\\]), verified equivalent and blow-up-free.
🟢 CodeQL alert #26 (:766) — false positive
Tag-stripping there feeds a Han-character lint scan, never HTML output; no XSS surface. Noted inline; optional to dismiss.
The guard machinery itself is careful, well-tested in isolation, and the design intent (objective-only invariants) is sound. The blocker is purely that it's currently red against live master content. Once rebased and the false positives are tightened/excepted, this should be good.
· branch feat/zh-objective-ci-guard
| const target = markdownDestination(raw, index + 2); | ||
| if (target !== null) targets.push(target); | ||
| } | ||
| for (const match of raw.matchAll(/^ {0,3}\[(?:\\.|[^\]\n])+\]:[ \t]*(?<destination>.*)$/gmu)) { |
There was a problem hiding this comment.
🟡 WARNING: Catastrophic backtracking (ReDoS) in the link-reference-definition regex — the CodeQL alert here is a true positive.
Why it matters: In (?:\\.|[^\]\n])+, a backslash can be consumed either by \\. or by [^\]\n] (the negated class does not exclude \). That overlap makes the group ambiguous, so an input like [ + \a×N + newline (a [ with no closing ]:) forces the engine to try exponentially many partitions. Confirmed locally against this exact pattern:
n=26 -> 801 ms
n=28 -> 3206 ms
n=30 -> 12821 ms (~4x per +2 chars)
This runs at build time over trusted MDX so it is not remotely exploitable, but a single malformed/large blog line could hang the guard (and CI) for seconds-to-minutes.
Fix: exclude \ from the negated class so each backslash has exactly one parse. Verified equivalent on valid reference definitions and no blowup at N=2000.
| for (const match of raw.matchAll(/^ {0,3}\[(?:\\.|[^\]\n])+\]:[ \t]*(?<destination>.*)$/gmu)) { | |
| for (const match of raw.matchAll(/^ {0,3}\[(?:\\.|[^\]\n\\])+\]:[ \t]*(?<destination>.*)$/gmu)) { |
| const literals = (source.match(STRING_LITERAL) ?? []).filter((text) => HAN.test(text)); | ||
| const visible = source | ||
| .replaceAll(STRING_LITERAL, 'X') | ||
| .replaceAll(/<[^>]*>/gsu, '') |
There was a problem hiding this comment.
🟢 On the CodeQL "incomplete multi-character sanitization" alert here: this is a false positive in context and not a security issue. mdxChineseSegments strips tags only to extract visible text for a Han-character lint scan — the result is never rendered as HTML or written to any output/DOM, so there is no XSS surface. No change required for correctness/security; if you want to clear the CodeQL alert you could dismiss it as "won't fix / not exploitable" or add a short // codeql[js/incomplete-multi-character-sanitization] justification, but that's optional.
| - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| persist-credentials: false | ||
| fetch-depth: 0 |
There was a problem hiding this comment.
Unconditional full git fetch penalizes all PR runs
Low Severity
The fetch-depth: 0 checkout applies unconditionally to every workflow run, fetching the entire git history even though only the conditional "Preserve English bytes" step (gated by chinese-copy-only label) needs deep history. The vast majority of PR runs—triggered by opened, synchronize, or ready_for_review—only run typecheck and unit tests, which need only a shallow clone. Combined with the new labeled/unlabeled activity types causing extra workflow triggers for any label change on matching PRs, this increases CI time and cost across all runs.
Reviewed by Cursor Bugbot for commit 3cc309d. Configure here.
Add mutation-tested route, Blog, dictionary, protected-token, structured-data, and explicit Chinese-only English-byte guards. Document exact temporary exceptions and keep editorial judgment out of deterministic CI. 中文:新增基于 mutation 的路由、Blog、字典、受保护 token、结构化数据及显式中文专属英文逐字节检查。记录精确的临时例外,并确保确定性 CI 不介入编辑性判断。
Make the Chinese-only CLI path- and rename-aware, pair locale dictionaries per object, and extend Blog protection to real MDX links, CommonMark delimiters, and bidirectional protected-token multisets. Scan visible JSX/MDX prose independently and document exact temporary baseline exceptions. 中文:强化客观中文本地化检查。修复仅中文模式的路径与重命名处理,按对象核对中英文字典,并覆盖真实 MDX 链接、CommonMark 分隔符和双向受保护 token 多重集;同时独立扫描 JSX/MDX 可见文案,并记录精确、可删除的临时基线例外。
Anchor protected-token waivers to complete Blog pairs, parse complete Markdown and whitespace-tolerant JSON-LD structures, preserve JSON paths, and cover static template props, shorthand dictionary keys, real unit forms, and Git type changes. 中文:补齐客观中文检查的解析边界。将受保护 token 豁免绑定到完整中英文文章对,完整解析 Markdown 链接与允许空白的 JSON-LD 结构并保留 JSON 路径,同时覆盖静态模板属性、字典 shorthand key、真实单位形式和 Git 类型变更。
Recognize slash and hyphen GPU/chip-hour forms, keep static MDX link props intact across JSX comparison expressions, and prevent overlapping protected blocks from leaking into prose scans. Add consumptive, pair-pinned exceptions for the six existing JSON-LD translations.\n\n中文:保护 GPU-hour 文案不变量。兼容 GPU/chip-hour 的斜杠与连字符写法,避免 JSX 比较表达式截断 MDX 静态链接属性,并防止重叠保护区间泄漏到正文扫描;同时为现有六处 JSON-LD 译文加入按次消费、绑定文章对摘要的临时例外。
Protect slash and hyphen GPU/chip-hour forms inside JSON-LD, normalize GPU-hour cost rates before casing, and keep MDX tags intact across braces in JavaScript comments and regex literals. Refresh only the exact pair-pinned baseline exceptions made necessary by the expanded matcher. 中文:补全受保护单位解析。JSON-LD 中的 GPU/chip-hour 斜杠与连字符写法现均受保护;成本单位会先归一化 GPU-hour 再处理大小写;MDX 标签解析会正确跳过 JavaScript 注释和正则表达式中的大括号。基线例外仅按扩展 matcher 后的实际差异更新,并继续绑定完整文章对摘要。
Replace the heuristic MDX expression lexer with Acorn tokenization so division, regex literals after spread, comments, and nested template interpolation cannot truncate static link props. Add direct mutation coverage for each lexical boundary. 中文:使用 Acorn token 解析 MDX 属性表达式,替换原有启发式 lexer,避免除法、spread 后的正则表达式、注释和嵌套 template interpolation 截断静态链接属性;并为每类词法边界补充直接 mutation 覆盖。
Extend the objective guard tokenizer with JSX support so nested elements, fragments, arrow-returned JSX, and nested JSX expressions cannot hide or falsely expose static link props. Add accepted and bad-link mutations for every compiler-valid form. 中文:为客观检查的 tokenizer 增加 JSX 支持,避免嵌套元素、fragment、箭头函数返回的 JSX 及 JSX 内部表达式隐藏或误暴露静态链接属性;并为每种可编译写法补充通过与错误链接 mutation。
3cc309d to
4393f7a
Compare
|
Claude finished @edwingao28's task in 2m 43s —— View job Re-review:
|
| Test | Violation | Assessment |
|---|---|---|
i18n.test.ts |
route-sibling × 2 — /model, /model/[slug] missing zh |
Genuine — new routes on master, need /zh siblings or an explicit exemption |
blog-content.test.ts (jalapeño) |
protected-token missingFromZh=["MW"×8] missingFromEn=["tok/s/user"×3] |
Guard over-reach — bare units in prose; the multiset check over-fires |
blog-content.test.ts (agentx) |
inline-code + protected-token --enable-prefill-delayer |
EN writes the flag as bare prose, ZH backticks it → real EN/ZH inconsistency |
zh-copy.test.ts |
dictionary-key-parity — CollectiveXDisplay.tsx:67, ~70 keys missingFromEn |
Fires on a component that landed on master; en/zh STRINGS dicts diverged (or guard over-reach) |
zh-copy.test.ts |
recorded exception fingerprint baf66a66… no longer matches |
Stale exception — the CollectiveX component changed on master, so the pinned SHA-256 drifted |
zh-copy.test.ts |
chip-untranslated × 2 in jalapeño zh |
Guard over-reach — fires on "Hot Chips" (conference proper noun) and "chiplet", not an untranslated common-noun "chip" |
Why it matters: the PR cannot merge with a red required check, and several failures show the guard flagging legitimately-translated content — the bare-unit MW/tok/s/user and the "Hot Chips"/"chiplet" cases are exactly the context-dependent terminology the PR description says stays out of deterministic CI. Because these tests scan all repo content (not the PR diff), the guard will also break CI on unrelated future PRs whenever a post uses a bare unit word in prose or a "chip" proper noun, and every change to master can invalidate a pinned exception fingerprint (as just happened to CollectiveX).
Fix: rebase onto master, then per case either (a) tighten the rule so bare prose units and proper-noun "chip"/"Hot Chips"/"chiplet" don't count, (b) add a documented entry to zh-objective-guard-exceptions.json, or (c) bring the content into compliance (/zh/model pages or exempt them; backtick the flag consistently in EN/ZH; reconcile the CollectiveX en/zh dict + refresh its exception fingerprint). See inline comments on i18n.test.ts:42 and zh-objective-guard.ts:620.
🟡 WARNING — ReDoS at zh-objective-guard.ts:563 still unfixed
CodeQL alert #25 is a true positive and the one-char fix from the previous review was not applied in any of the 7 commits. (?:\\.|[^\]\n])+ lets a backslash parse two ways → exponential backtracking (~12.8s at 30 chars). Build-time/trusted input so not exploitable, but a malformed line hangs CI. Suggestion posted inline ([^\]\n] → [^\]\n\\]).
🟢 CodeQL alert #26 (:766) — false positive
Tag-stripping there feeds a Han-character lint scan, never HTML output — no XSS surface. Optional to dismiss. (Unchanged from prior review.)
The guard machinery is careful and well-tested in isolation, and the objective-only design intent is sound. But the blocker from the last review persists and has grown: the guard is red against live master, and part of that redness is the guard enforcing the very editorial judgments (bare units, proper-noun "chip", per-post flag formatting) the PR says it excludes. No browser surface, so no runtime verification needed here — the blocker is CI, not behavior.
· branch feat/zh-objective-ci-guard
| const target = markdownDestination(raw, index + 2); | ||
| if (target !== null) targets.push(target); | ||
| } | ||
| for (const match of raw.matchAll(/^ {0,3}\[(?:\\.|[^\]\n])+\]:[ \t]*(?<destination>.*)$/gmu)) { |
There was a problem hiding this comment.
🟡 WARNING: ReDoS in the link-reference-definition regex — still unfixed after 7 commits.
In (?:\\.|[^\]\n])+, a backslash can be consumed either by \\. or by [^\]\n] (the negated class does not exclude \). That overlap makes the group ambiguous, so a [ with no closing ]: followed by many \a pairs forces exponentially many partitions (CodeQL alert #25; ~12.8s at 30 chars). Runs at build time over trusted MDX so it's not remotely exploitable, but a single malformed/large blog line can hang the guard (and CI). This was flagged in the prior review and the one-char fix was not applied.
Fix: exclude \ from the negated class so each backslash has exactly one parse.
| for (const match of raw.matchAll(/^ {0,3}\[(?:\\.|[^\]\n])+\]:[ \t]*(?<destination>.*)$/gmu)) { | |
| for (const match of raw.matchAll(/^ {0,3}\[(?:\\.|[^\]\n\\])+\]:[ \t]*(?<destination>.*)$/gmu)) { |
| const withoutBlocks = withoutProtectedBlocks(raw); | ||
| const prose = withoutSpans(withoutBlocks, inlineCodeSpans(withoutBlocks)); | ||
| const pattern = | ||
| /--[a-z0-9](?:[\w.-]*[a-z0-9])?(?:=[^\s,,。;;))`]+)?|\b[A-Z][A-Z0-9]*(?:_[A-Z0-9]+)+\b|\b(?:tokens?|tok)\/(?:s|sec)(?:\/(?:user|gpu|GPU|chip))?\b|\b[KMGTPE]?FLOP\/s\b|\b[KMGT]?bit\/s\b|\b(?:kW|MW)\/(?:gpu|GPU)\b|\b(?:GPU|chip)[/-](?:hr|hour)\b|\b(?:GB\/s|TB\/s|GB|TB|ms|µs|ns|kW|MW)\b|\$\d+(?:\.\d+)?\/M\b|\$\/(?:M\s+(?:tok|tokens?)|(?:GPU|chip)[/-](?:hr|hour))/gu; |
There was a problem hiding this comment.
🔴 BLOCKING: This protected-token pattern over-fires on bare units in prose, and it is currently red on Tests (Unit).
\b(?:GB\/s|TB\/s|GB|TB|ms|µs|ns|kW|MW)\b and the tok/s/user alternative match bare unit words wherever they appear in prose, then compareBlogPair requires the EN and ZH multisets to be identical. But a Chinese sentence legitimately rephrases "throughput per MW" or "N tok/s/user" — it does not have to repeat the bare unit the same number of times. On live master this fires on openai-jalapeno-better-than-nvidia.mdx:
missingFromZh=["MW"×8] missingFromEn=["tok/s/user"×3]
This is exactly the context-dependent terminology the PR description says stays out of deterministic CI. Because the guard asserts toEqual([]) over the entire repo's real content, it also fails CI on unrelated future PRs whenever a post uses a bare unit word in prose.
Fix: restrict the protected multiset to units that appear attached to a number (e.g. \d+\s*MW) or inside inline code/JSON-LD, or move bare prose units to zh-objective-guard-exceptions.json per pair. Only compound tokens tied to identity ($/M tok, GPU-hr, --flag, SCREAMING_CASE) belong in a byte-equal invariant.
| }); | ||
|
|
||
| it('pairs every English page with a Chinese page in both directions', () => { | ||
| expect(findRoutePairViolations(pages)).toEqual([]); |
There was a problem hiding this comment.
🔴 BLOCKING: This assertion is currently red on Tests (Unit).
findRoutePairViolations(pages) returns two route-sibling violations against live master:
{ route: "/model", detail: "missing Simplified Chinese page" }
{ route: "/model/[slug]", detail: "missing Simplified Chinese page" }
The /model and /model/[slug] routes landed on master after this branch was cut and have no /zh sibling. Because the test scans the real App Router tree with no exemption set, the PR can't merge until either the /zh/model pages exist or /model is added to the exemptRoutes set passed here.
Fix: rebase onto master, then add the /zh siblings or pass an explicit exemptRoutes set (documented) for the noindex model routes.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
There are 3 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4393f7a. Configure here.
| ts.forEachChild(node, visit); | ||
| }; | ||
| visit(sourceFile); | ||
| return segments.length > 0 ? segments : [source]; |
There was a problem hiding this comment.
Han comments scan entire files
Medium Severity
When a non-MDX file contains Han but none of it sits in a string, template, or JSX text node, chineseSegments falls back to the entire source. A Chinese comment then makes English identifiers, docs, and Chip mentions look like copy regressions.
Reviewed by Cursor Bugbot for commit 4393f7a. Configure here.
…en scanner - Exempt intentionally English-only /model and /model/[slug] routes in the route-sibling check (their metadata declares canonical without hreflang). - Refresh the CollectiveXDisplay dictionary-parity fingerprint after the master-side refactor (74 -> 73 Chinese-only keys). - Add exact blog exceptions for the ported CUDA-moat and Jalapeño articles (inline-code formatting drift and MW/tok-s-user protected-token drift owned by the content branches). - Stop scanning markdown link destinations for untranslated terms; URL slugs like .../inference-chip/ are not translatable copy. - Fix CodeQL findings: remove regex alternation overlap in the reference-link pattern (exponential backtracking) and strip MDX tags to a fixed point (incomplete multi-character sanitization).
|
Claude finished @edwingao28's task in 3m 6s —— View job Re-review:
|
| Prior finding | Status | How |
|---|---|---|
ReDoS in link-reference regex (zh-objective-guard.ts:563, CodeQL #25) |
✅ Fixed | Backslash excluded from the negated class — [^\]\n] → [^\\\]\n], so each \ has exactly one parse. |
chip-untranslated false-positive on URL slugs / "Hot Chips" / "chiplet" |
✅ Fixed | New withoutMarkdownLinkDestinations strips ](url) before the Han scan; fixed-point tag-strip loop added. Covered by a new test. |
route-sibling — /model, /model/[slug] missing zh |
✅ Fixed | Documented EN_ONLY_ROUTES exemption. /model is intentionally English-only (alternates.canonical without hreflang, model/page.tsx:20). The test guards the guard: it asserts hasZhSibling(route) === false, so the exemption auto-breaks if a /zh/model sibling is ever registered. |
protected-token MW / tok/s/user (jalapeño); --enable-prefill-delayer (agentx) |
✅ Handled | Documented per-pair exceptions, gated by a tamper-evident pairSha256 pin so any edit to the article pair re-arms the guard. |
dictionary-key-parity + stale exception fingerprint (CollectiveX) |
✅ Fixed | Fingerprint refreshed to the current 73-key mismatch (8bcba14…). |
The ReDoS and CodeQL-#26 (Han-lint tag-strip, confirmed a false positive) alerts are both addressed/benign. New code (withoutMarkdownLinkDestinations, the guard-of-the-guard) ships with tests.
🟡 Non-blocking — latent over-reach to keep an eye on
Not firing today (whole-repo scan is green), so not blocking, and already noted by Cursor Bugbot on an earlier commit — flagging only so it isn't lost:
chineseSegmentswhole-file fallback (zh-objective-guard.ts~L805): for a non-MDX file whose only Han sits in a code comment (no string/template/JSX node), the AST walk finds no segments and falls back to[source], scanning English identifiers too. A future.ts/.tsxwith a Chinese comment plus aChipidentifier could then false-positivechip-untranslated. Consider stripping comments (or returning[]) instead of falling back to the raw source.
Note
Branch is 1 commit behind master (#836, which also touches tests-unit.yml / package.json / docs/chinese-copy.md). CI already validated the PR-merged-with-master result as green, so the guard passes against current master content — a final rebase before merge is advisable only to keep the diff clean.
🟢 Re-review verdict: LGTM — all previously-blocking failures are fixed, CI is green, and the new scanner logic is tested. The one remaining item is a pre-existing latent edge case, not a regression this branch introduces.
· branch feat/zh-objective-ci-guard


Part of #823 / 属于 #823 路线图的一部分
What changed
en/zhdictionary structure, and Chinese-only English-byte preservation./zhlinks.Validation
Review notes
中文说明
en/zh字典结构,以及中文专属改动中的英文字节保护。/zh链接。验证
审核说明
Note
Low Risk
Test and CI-only changes with no runtime user-facing behavior; main risk is false-positive guard failures or mis-labeled PRs blocking merges on i18n/content work.
Overview
Introduces a shared
zh-objective-guardlibrary and wires it into unit tests so CI can enforce objective en/zh invariants without judging fluency or tone.New checks include bidirectional App Router
/zhsibling pairing, blog filename pairing plus structural parity (code fences, math, figures, links, protected units/identifiers, JSON-LD), staticen/zhdictionary key-shape parity, and high-confidence mechanical copy rules (e.g. untranslatedChip, redundantwarmup 预热). Existingzh-copy,blog-content, andi18nsuites delegate to these helpers;zh-objective-guard.test.tsadds mutation coverage.For PRs labeled
chinese-copy-only, unit workflow now runsguard:zh-copy:chinese-only(merge-base git diff viafetch-depth: 0) to block accidental English MDX or rawensubtree edits. Baseline drift is recorded inzh-objective-guard-exceptions.jsonwith digest-pinned, consumable waivers.docs/chinese-copy.mdandAGENTS.mddocument the contract;acorn/acorn-jsxsupport JSX-aware MDX parsing.Reviewed by Cursor Bugbot for commit cdacfdc. Bugbot is set up for automated code reviews on this repo. Configure here.