Skip to content

test(i18n): point the #802 term guard's pack rule at the real zh-CN tree - #1778

Merged
huangyiirene merged 2 commits into
mainfrom
claude/issue-1755-pack-rule-real-tree
Sep 8, 2026
Merged

huangyiirene merged 2 commits into
mainfrom
claude/issue-1755-pack-rule-real-tree

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #1755

What broke, and which half of it

test/docs-object-term-consistency.test.ts reaches the Chinese language pack by
two different mechanisms, and only one of them was broken.

rule site how it reaches the pack state
%s is one Chinese word everywhere (#837) label derivation :297 packFor('zh-CN') resolves through the stack to the merged pack ✅ working — untouched by this PR
"no retired spelling survives in the language pack" :252/:406 offenders([PACK_FILE], …) read one literal path ⛔ vacuous since the #1311 split

PACK_FILE named src/translations/zh-CN.ts, which was the whole zh-CN pack
when the rule was written. The #1311 locale split moved every string out of it
into src/translations/zh-CN/, leaving a 77-line barrel of import/re-export
rows. The rule kept scanning the barrel — a file that cannot contain a label —
so it could not fire.

Re-measured on this branch's base 2e0f4dd1, inheriting no number from the card:

reading value
src/translations/zh-CN.ts line count 77
canonical crm_case term in the barrel 0
canonical crm_quote term in the barrel 0
CONTROL — same two terms across src/translations/zh-CN/** 56 lines / 65 occurrences and 12 / 12

The ablation — a green run is not the evidence

The card's whole value is that the guard re-passed #1529's defect. So the
proof is two legs against the same mutation at the same commit, not a green
run. #1529's defect was re-injected verbatim into the live pack
(src/translations/zh-CN/objects.commerce.ts, crm_quote.crm_contact.help),
proved on disk before any result was read (retired-spelling count 0 → 1; blob
5e4e25b4… → cc96697a…), and the surface swapped underneath it:

LEG A  pre-fix surface (base 2e0f4dd, PACK_FILE)   VERDICT legA-prefix-exit=0
       Test Files 1 passed (1)   Tests 33 passed (33)      ← the defect is MISSED

LEG B  repaired surface (this PR, PACK_FILES)      VERDICT legB-repaired-exit=1
       Test Files 1 failed (1)   Tests 1 failed | 32 passed (33)
       the zh-CN pack still spells something with a retired word …
         src/translations/zh-CN/objects.commerce.ts:53: RETIRED_SPELLING

Restored under a trap … EXIT INT TERM and proved by bytes, not by an exit
code: post-restore blob 5e4e25b4d8da439c0c8fb1c99436814ed2c83cc3, identical to
HEAD; git diff HEAD --name-only empty.

Why a directory walk, and not the barrel's imports

Both shapes were on the table; this one was chosen on a measurement. The barrel
imports seven modules — and src/translations/zh-CN/_shared.ts is not one
of them. It holds real pack strings and is reached only through three family
files, so resolving the barrel's direct imports would have left a genuine
string carrier outside the surface, and a transitive resolver can go quiet the
same way the literal did when a specifier stops resolving.

The walk is instead the shape used by the three surfaces that survived the
#1311 split untouched — HANS_PAGES, HANT_PAGES, TEST_FILES all walk a root
and filter by suffix. Re-derived against the tree as the card asks: those three
are healthy (67 / 67 / 164 files), and packFor('zh-CN') resolves the merged
pack correctly. PACK_FILE was the only hardcoded literal path in that block,
and the only one that drifted.

Self-extension is proved, not asserted — a new module dropped under zh-CN/
with no edit to the guard:

LEG C  new src/translations/zh-CN/objects.zzz-probe.ts   VERDICT legC-newmodule-exit=1
       objects.zzz-probe.ts:3: RETIRED_SPELLING

The one line beyond the path swap

The existing the ledger and the scan surface are real check already proves the
other three surfaces are non-empty. The pack surface is now proved there too —
in the same block, in the same shape, no new it, no new gate (the suite is
still 33 tests). A file count would not have caught this bug, because the
barrel is still one real file, so the check is a cross-reference between the two
mechanisms instead: the files being scanned must carry the label the working
rule resolves through the stack.

LEG D  surface narrowed back to the barrel alone       VERDICT legD-barrel-only-exit=1
       × has pages and ledgers to scan  (crm_case, crm_quote)
       no file in the zh-CN pack surface carries crm_case's label …

That is the reading that was missing for the whole life of the bug: on the old
surface it is 0.

Gates — pnpm verify, exit code captured before any pipe

Every step run in the foreground, redirected to a file, $? read before any
pipe. All eight on final HEAD d9e6e5a:

step exit
validate · typecheck · lint · lint:i18n-gate 0 · 0 · 0 · 0
hygiene · hygiene:tokens · build 0 · 0 · 0
test — Test Files 164 passed (164), Tests 3438 passed | 1 skipped 0

hygiene, hygiene:tokens and test were re-run on d9e6e5a after the
changeset commit, because .changeset/ is inside their scan surfaces.

line_budget: NOT APPLICABLE, on the gate's own printed line rather than an
assertion:

Source token ratchet — authored surface: src/**/*.ts minus src/translations, src/data

test/** is not in src/**, so it is outside the ratchet entirely — the
reading is unchanged at authored total ~136,794 tokens.

Scope

Exactly one file of behaviour: test/docs-object-term-consistency.test.ts, plus
a changeset. No translation file edited, no gate added, content/docs/releases/
untouched, and the :297 packFor path is not modified. The repaired rule is
green on main's content — no retired spelling is live in the pack today.

No closing keyword is placed near #1530, #802, #1529, #1311 or #1598; none of
them closes here. #1530 remains open and is pm:blocked on this card's closure.

⚠️ Two PM measurement assumptions, re-measured — one falsified

  1. FALSIFIED. The dispatch says "the card says 工单 65, it measures 56 today
    … the figure moved under the card". It did not move. Both numbers are the
    same tree read two ways: 56 matching lines, 65 occurrences. Verified
    identical at a02d4c14 (the PM's own measurement point) and at 2e0f4dd, so
    the two readings never disagreed about anything. The discipline the
    assumption prescribes — inherit no number, re-derive — is what surfaced this;
    only its stated cause was wrong.
  2. HOLDS. That count is a control leg, not a violation count. Pointing the
    rule at the real tree does not red it: the repaired rule is green.

维护者速读(草稿)

改了什么 —— 只改一个测试文件里一条规则的取包路径。#802 术语守卫里有两条
规则,一条经 stack 解析、一直在正常拦截缺陷(⛔ 本 PR 不碰);另一条直接读
src/translations/zh-CN.ts 这一个路径。#1311 拆分把文案全搬进
src/translations/zh-CN/ 之后,那个路径只剩 77 行的 re-export barrel,规则从此
扫一个不可能含标签的文件。现在改成走目录(与该文件里三个没坏的扫描面同一形态)。

为什么改 —— 它是静默失效,不是报错失效:套件照样报 33 passed,而
#1529 的缺陷原样注回真实语言包后仍然全绿。空转的守卫读起来就是覆盖率,这是最坏
的失败方向。本 PR 用同一处变异、同一个 commit、只换扫描面跑了两腿:旧面 exit 0
放行,新面 exit 1 并点名到文件与行号。

风险与代价(含回滚) —— 风险低:改动只在 test/**,不发布任何东西给用户,
src/ 一个字节没动。扫描面变大意味着以后真有退役词进语言包会红 —— 那正是它该做
的事。今天不会红:main 的语言包里没有活的退役词。回滚 = revert 这两个 commit,
守卫回到空转状态,无其它连带影响。

席位意见 —— (留空)

你要做的 —— 确认一处越界:除了换路径,本 PR 在已存在的
the ledger and the scan surface are real 检查里加了一条断言(不是新 it、不是
新门禁,测试数仍是 33),要求被扫的文件必须含有另一条规则经 stack 解析出的标签。
理由是:光换路径不会阻止下一次目录搬迁重新武装同一个陷阱,而文件数量判据抓
不到本次缺陷(barrel 仍是一个真实文件)。若认为这超出本卡范围,删掉它即可,换路
径本身完整独立。


🤖 Generated with Claude Code

https://claude.ai/code/session_01PpRjGNnwyo2J1rrmekxB1W


Generated by Claude Code

`PACK_FILE` named `src/translations/zh-CN.ts`, which was the whole zh-CN pack
when the rule was written. The #1311 locale split moved every string out of it
into `src/translations/zh-CN/`, leaving a 77-line barrel of re-exports, and the
rule kept scanning the barrel — a file that cannot contain a label. It could no
longer fire, and it did not: #1529's defect was re-injected verbatim into the
live pack and the suite still reported 33 passed.

The surface is now derived the way the three that survived that split are —
walk a root, filter by suffix — so `_shared.ts` (real pack strings, reached
only through the family files) is covered and the next module added under
`zh-CN/` needs no edit here.

The non-vacuity assertion in `the ledger and the scan surface are real` is
extended to the pack: the files scanned must carry the label the other rules
resolve through the stack. A file count could not have caught this, because
the barrel is still one real file; that cross-check reads 0 on the old surface.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PpRjGNnwyo2J1rrmekxB1W
Test-only change; empty frontmatter is the sanctioned "releases nothing"
declaration that .github/workflows/changeset-check.yml documents.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PpRjGNnwyo2J1rrmekxB1W
@vercel

vercel Bot commented Sep 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated
hotcrm Ignored Ignored Sep 8, 2026 10:35am UTC

Request Review

@github-actions github-actions Bot added the ci/cd CI plumbing and the verification pipeline label Sep 8, 2026
@huangyiirene
huangyiirene marked this pull request as ready for review September 8, 2026 10:40
@huangyiirene
huangyiirene added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 0534111 Sep 8, 2026
10 checks passed
os-steve pushed a commit that referenced this pull request Sep 9, 2026
`test/hook-input-shape.test.ts` rules A and B walk every `.ts` under `test/`
and key on one token — a hook handler being INVOKED, `handler(`. The file
already guards its detector with planted probes, but nothing in it claimed the
scanned tree still holds a call site for that detector to read.

Measured on this branch's base 965933b: 175 real `.ts` files in the surface, of
which 31 call a handler directly and 13 already reach a hook through
`runHookBody(...)` instead — a form neither rule matches. The class these rules
discriminate within is migrating out of the surface while the surface stays 175
real files, so a file count could not report it. That is #1755's shape one tree
over: a real member that cannot carry the thing keeps the rule green.

Strengthened in place, inside the existing self-test block — no new `it`, no
new gate, the file stays at 16 tests (#1778's shape). The invocation pattern is
single-sourced as `CALLS_HANDLER` so rule B and the surface check cannot come
to discriminate different classes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuzfS5chho38Yx1jxx9DEj
os-steve added a commit that referenced this pull request Sep 9, 2026
Each of these four guards walks a root and filters by suffix, while a second
mechanism in the same file names a specific carrier by literal path. Nothing
tied the two together: a walk that stopped reaching the carrier leaves the
count healthy and the literal read succeeding, so the rule reports clean over
a real, non-empty tree that no longer holds what it judges. #1778 crossed
exactly this pair for PACK_FILES.

- docs-object-term-consistency: TEST_FILES must contain the ROW_LABEL ledger
  the neighbouring blocks read by literal path (literal single-sourced).
- hook-query-predicate: the surface must still hold a `where:` predicate —
  the class the rule judges — not merely a filename. Equality against a
  second population is not authorable here: the runtime half queries a
  synthetic engine and enumerates no call sites.
- collaboration-capabilities: the *.object.ts sweep asserted nothing about
  its surface; the class is any member, so a count is the content proof.
- docs-drift / docs-conversion-rate-spelling: the carrier lists proved by
  existsSync / readFileSync must also be reached by the walk.

All five assertions go into existing self-test blocks: no new `it`, no new
gate, no new file, no widened surface.

Part of #1839

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuzfS5chho38Yx1jxx9DEj
os-steve added a commit that referenced this pull request Sep 9, 2026
Empty frontmatter — the sanctioned "this PR releases nothing" declaration
documented in .github/workflows/changeset-check.yml, and the form #1778 used
for the same kind of test-only work.

Part of #1839

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuzfS5chho38Yx1jxx9DEj
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd CI plumbing and the verification pipeline

Projects

None yet

2 participants