Skip to content

test(docs): give the translated view rosters a coverage rule too - #1561

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-1557-roster-coverage-translated-faces
Sep 3, 2026
Merged

test(docs): give the translated view rosters a coverage rule too#1561
os-sales merged 1 commit into
mainfrom
claude/issue-1557-roster-coverage-translated-faces

Conversation

@os-sales

@os-sales os-sales commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes #1557

test/docs-view-rosters.test.ts runs two complementary directions on the English face — coverage (every view the app ships is named in its page's roster) and name exactness (the name column names only views the app ships) — and the file's own header explains why neither subsumes the other. #1551 gave the translated faces name exactness plus a structural rule pinning each face to the English page's roster row count. It did not give them coverage.

Those three rules leave one shape open: a .zh-Hans.mdx or .zh-Hant.mdx roster that keeps the row count, names only lawful view names, but names one view twice and omits another.

The premise, measured first

Before writing anything, on the tree #1556 landed (f4068c4d): content/docs/revenue/products.zh-Hans.mdx with both rows renamed to **全部产品** — dropping **产品目录**, two rows in and two rows out — ran

 Test Files  1 passed (1)
      Tests  9 passed (9)

Green over the defect, on the file whose whole purpose is that defect's class. Mutation confirmed on disk (blob f30cd07add6646, removed anchor 1 → 0, injected 1 → 2); restored and proved by an empty git diff HEAD with the blob back at f30cd07.

The rules read the NAME COLUMN, not the section body

Pointing the English coverage rule at the translated faces does not work, and the reason is worth recording rather than rediscovering. That rule asks body.includes(label) — does the name appear anywhere in the section — which is a pure existence question over prose as well as table. So it is blind to multiplicity by construction and cannot see a duplicate at all, and it is blind to the omission whenever the section's prose happens to mention the dropped name.

That is not a corner: measured, 9 of the 55 names on each of the three faces are echoed in the section body outside their own table row. On one of those nine — service/cases.zh-Hans with its **已升级工单** row renamed to **全部工单**, on a page whose bullets below the table still say 已升级工单 — a body-shaped coverage rule stays green (1 passed), as does the suite (9 passed).

So the new rules reuse the nameColumns parse the name rules already share, and read the first column.

Coverage, deliberately, and not set equality

Asserting the name column's set equals the source set closes this in one line, and it was rejected: it implies name exactness, which would make #1551's two rules dead weight, and this file is deliberate that each rule earns its place. Coverage is the missing direction and only that, so each face now carries the same complementary pair the English face has carried since #1326.

They are two parallel rules sharing the parse, not one parameterised rule, because what a failure means differs: on zh-Hans a page and a live pack disagree; on zh-Hant a page and a hand-written table do.

The row-count rule is not made redundant either

Measured, not argued. Coverage and exactness together pin the set of names on a face and say nothing about multiplicity. An extra row repeating a lawful name — | **全部产品** | 重复行 | inserted on products.zh-Hans — fails only the count rule:

     × every translated face carries the same roster, entry for entry
      Tests  1 failed | 10 passed (11)

This card's shape is the other way round: with the count pinned, a duplicate forces an omission, and the omission is what coverage sees. (That ablation is an insertion, so its proof is the injected anchor 0 → 1 and the blob hash — the removed-count assertion is deliberately not read, per the trap #1551's harness voided a run over.)

Reverse verification

Every mutation confirmed on disk by blob hash and anchored counts on the removed and injected text; every restore proved by state — blob hash back at its HEAD value and an empty git diff HEAD.

ablation result
products.zh-Hans **产品目录****全部产品** (the card's shape) 1 failed | 10 passed — never names "产品目录", the zh-CN spelling of a view crm_product ships
cases.zh-Hans **已升级工单****全部工单** (prose still names it) 1 failed | 10 passed — never names "已升级工单", the zh-CN spelling of a view crm_case ships
contracts.zh-Hant **合約時間線****全部合約** 1 failed | 10 passed — never names "合約時間線", the pinned Traditional name of a view crm_contract ships
roster heading renamed on products.zh-Hans / .zh-Hant 3 failed | 8 passed — the new rule fails on its vacuity guard, roster sections this rule read no name column out of: …, not on coverage

The vacuity guard matters most for a coverage rule: an empty cell list is exactly what would let it pass by checking nothing. expectNameColumnIsReadable is called first thing in both new rules, so a lost roster section is reported as a lost roster section.

Neither new rule carries a source-side floor of its own, deliberately: the zh-CN producer rule already fails below 40 resolved pack spellings and the pin audit already fails below 40 pinned names, exactly as vacuity guard #2 carries that floor for the English coverage rule.

revenue/approvals and #1552

Still outside these rules structurally, inherited rather than restated: they read nameColumns, which walks PAGE_OBJECT and rosterOf, and that page carries no roster heading on any of its three faces — re-measured here, rosterOf is null for all three (its headings are ## Where to find pending approvals / ## 在哪里找到待处理的审批 / ## 在哪裡找到待處理的審批). No exemption list grew, and #1552 is left as open as it was.

Scope

Nothing under content/docs/** or src/ changes, and nothing needed to: all 55 name cells on each translated face are distinct and each face names exactly the 55 views its objects ship. This is a hole in the guard, not a defect in the docs — the same reason #1326 was worth landing while its pages were already correct. The pages were mutated only inside ablations, each restored and verified by state.

Verification

pnpm verifyvalidate && typecheck && lint && lint:i18n-gate && hygiene && hygiene:tokens && build && test — green end to end on 0ec6dd09:

✓ i18n lint gate: 0 `i18n/missing-*` issues
✓ source hygiene clean
✓ source token ratchet clean
  ✓ Build complete (1834ms)
 Test Files  159 passed (159)
      Tests  3365 passed | 1 skipped (3366)

Changeset uses the sanctioned empty-frontmatter form — this releases nothing to HotCRM users.


One finding out of scope, filed separately rather than folded in: the English face carries this same shape, because its coverage rule reads the body. Measured with these rules in place — cases.mdx naming **All Cases** twice and dropping **Escalated Cases** runs 11 passed. Closing it is a third rule, which this card did not scope.

🤖 Generated with Claude Code

https://claude.ai/code/session_019hUuCQStzXGMFSX4dzww5t


Generated by Claude Code

`test/docs-view-rosters.test.ts` runs two complementary directions on the
English face — coverage (every view the app ships is named in its page's
roster) and name exactness (the name column names only views the app ships).
#1551 gave the translated faces name exactness plus a structural rule pinning
each face to the English page's roster row count, and no coverage. Those three
leave one shape open: a roster that keeps the row count, names only lawful
names, but names one view twice and omits another.

Measured before writing anything, on the tree #1556 landed: `revenue/
products.zh-Hans` with both rows renamed to 全部产品 — dropping 产品目录, two
rows in and two rows out — ran 9 passed. Green over the defect.

The rules read the NAME COLUMN, not the section body. The English coverage rule
asks `body.includes(label)`, a pure existence question over prose as well as
table, so it cannot see a duplicate at all and misses the omission whenever the
prose mentions the dropped name — measured, 9 of the 55 names on each of the
three faces are echoed in the body outside their own row, and on one of them a
body-shaped rule stays green over this exact defect.

Coverage and not set equality, deliberately: set equality implies name
exactness and would make #1551's two rules dead weight. The count rule keeps
its place too — coverage and exactness pin the SET of names and say nothing
about multiplicity, so an extra row repeating a lawful name fails only the
count rule.

`revenue/approvals` stays out structurally, inherited rather than restated: the
rules walk PAGE_OBJECT and `rosterOf`, and that page has no roster heading on
any face. No exemption list grew and #1552 is untouched.

Nothing under `content/docs/**` or `src/` changes, and nothing needed to.

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

vercel Bot commented Sep 3, 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 3, 2026 12:03pm UTC

Request Review

@github-actions github-actions Bot added the ci/cd CI plumbing and the verification pipeline label Sep 3, 2026
@os-sales
os-sales marked this pull request as ready for review September 3, 2026 12:07
@os-sales
os-sales added this pull request to the merge queue Sep 3, 2026
Merged via the queue into main with commit 7db2145 Sep 3, 2026
10 checks passed
os-sales pushed a commit that referenced this pull request Sep 3, 2026
The English coverage rule asks `body.includes(label)` — does the name appear
anywhere in the roster section, prose and bullets included. That is a pure
existence question, so it is blind to multiplicity by construction and blind to
an omission whenever the section's prose still names the dropped view.

Measured on the tree #1561 landed: `service/cases.mdx`'s **Escalated Cases**
row renamed to **All Cases** leaves the name column naming *All Cases* twice
and never naming *Escalated Cases*, row count unchanged, and the file ran
`Tests 11 passed (11)`. Nine of the fifty-five English name cells echo their
view name in the section body outside their own table row, so those nine rows
had no rule protecting them.

This adds the name-column coverage rule the translated faces got in #1557,
reusing `nameColumns('')` and `expectNameColumnIsReadable` unchanged so it
inherits the `revenue/approvals` exclusion and the vacuity floor structurally.

The body-shaped rule is kept, and is now labelled for the job it does. Measured,
it is not the wider net it was taken for: name-column coverage strictly implies
body coverage, so every label the body rule reports missing is one the new rule
reports missing too. It stays as a diagnostic — both firing means the view is
named nowhere in the section, only the new rule firing means it is named in
prose but has no row — and the header says that rather than claiming coverage
it does not add.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019hUuCQStzXGMFSX4dzww5t
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

Development

Successfully merging this pull request may close these issues.

The view-roster COVERAGE rule is still English-only, so a translated face can name one view twice and drop another and stay green

1 participant