Repository navigation
#1368 is decided, so the navigation guard's five quarantined zh-Hant sidebar pairs are now a live worklist — and the prose and the ledger must land in one commit #1544
Description
Found during #1125's R32 run (PR #1537) and verified by the repo:hotcrm PM seat against the tree at f0a05613.
The guard says so itself
test/docs-setup-navigation-names.test.ts, at KNOWN_UNRESOLVED_CRM, verbatim:
⚠️ They are quarantined rather than rewritten ON PURPOSE: which locale a zh-Hant reader should be sent to is the open question on #1368, which carriesneeds-user-decisionand argues the console falls back to Simplified rather than to English — the opposite of the convention #1113 applied. Rewriting five pages here would pre-empt that ruling. When #1368 is decided, these five lines are the worklist, and the staleness checks below make sure they cannot outlive it quietly.
#1368 was decided on 2026-08-31. The condition the quarantine was waiting on has been met for three days and nothing moved, because the guard is green and a green guard announces nothing. ⭐ This is the intended behaviour of that comment working exactly as designed — it left a tripwire in prose because it could not leave one in code.
needs-user-decision". It does not any more. Correct that sentence in the same PR — a quarantine note that misstates its own unblock condition is how the next reader concludes the block is still live.
The five entries and where they are cited
crmPairKey('我的工作', '我的行事曆') guides/email-and-calendar.zh-Hant.mdx:82
crmPairKey('我的工作', '我的任務') sales/activities.zh-Hant.mdx:94, :109
crmPairKey('我的工作', '我的線索') sales/leads.zh-Hant.mdx:144
crmPairKey('我的工作', '我的日曆') sales/meetings-and-calls.zh-Hant.mdx:145
crmPairKey('活動', '活動') sales/meetings-and-calls.zh-Hant.mdx:134
Six citation sites, five ledger entries, four pages. Under #1368's ruling these become their English pairs (My Work → My Calendar, My Work → My Tasks, My Work → My Leads, Activity → Activities — ⛔ resolve each against src/apps/crm.app.ts plus src/translations/*, do not translate by eye).
⛔ The trap: this cannot be a rider on another card
test/docs-setup-navigation-names.test.ts:700 runs 'holds no quarantined pair the docs no longer cite':
const cited = new Set(crmCitations().map((c) => crmPairKey(c.group, c.child)));
const dead = [...KNOWN_UNRESOLVED_CRM].filter((key) => !cited.has(key));
expect(dead, `KNOWN_UNRESOLVED_CRM entries no longer cited by any page: …`).toEqual([]);
⇒ the moment the prose is fixed, the ledger lines go red. Fixing four pages and forgetting five lines turns pnpm test red; deleting five lines without fixing the pages turns the other check red. They are one atomic change and must land in one commit.
⭐ This is also the one case where editing that guard file is correct, which is why #1125's dev deliberately did not touch it: deleting a satisfied quarantine entry is the ledger doing its job, and is a different act from widening a rule to get green. The guard's own message says it — "Someone fixed the prose — delete these lines so the ledger keeps measuring the real remainder."
Scope
- the six citation sites across the four pages
- the five
KNOWN_UNRESOLVED_CRMlines, deleted - the stale
needs-user-decisionclause in that block's comment, corrected - ⛔ no rule change. Do not touch
CITATION,CRM_CITATION,APP_WORDS,RETIRED_UI_NAMESorKNOWN_UNRESOLVED(the platform ledger, a different set fromKNOWN_UNRESOLVED_CRM) - a changeset
Fence
test/docs-setup-navigation-names.test.ts for the duration. #1538 declares that file explicitly out of its own surface, so the two do not collide on it — but ⛔ do not dispatch anything else that edits the guard alongside this.
✅ No page overlap with #1538 (sandbox-and-releases, performance-and-limits, security-and-compliance, setup) or #1540 (automation, profiles, sandbox-and-releases, setup, sharing-and-security, integrations, glossary). All three are separately dispatchable on the docs axis.
Activity
os-sales commentedon Sep 3, 2026
Claim:
repo:hotcrmPM seat — dispatching to anos-devagent now (round R32, second wave).session_019hUuCQStzXGMFSX4dzww5tclaude/issue-1544-crm-nav-quarantine-worklistcontent/docs/guides/email-and-calendar.zh-Hant.mdxcontent/docs/sales/activities.zh-Hant.mdxcontent/docs/sales/leads.zh-Hant.mdxcontent/docs/sales/meetings-and-calls.zh-Hant.mdxtest/docs-setup-navigation-names.test.ts— theKNOWN_UNRESOLVED_CRMblock only.changeset/设置is checked against nothing, and the shipped zh-CN label is系统设置#1403 also targets that file and is fenced for the duration. Four more zh-Hant sites name the Setup app in Chinese with no arrow — a shape all three rules of the navigation guard are structurally unable to see #1538 declares it out of surface, so those two do not collide.CITATION,CRM_CITATION,APP_WORDS,RETIRED_UI_NAMESandKNOWN_UNRESOLVED(the platform ledger — a different set fromKNOWN_UNRESOLVED_CRM) are all out of surface.test/docs-setup-navigation-names.test.ts:700(holds no quarantined pair the docs no longer cite) goes red on prose-without-ledger, and the citation check goes red on ledger-without-prose.No page overlap with #1328 (in flight,
content/docs/service/cases.*) or #1540 (in flight,administration/*+reference/glossary+guides/integrations).Generated by Claude Code
docs(zh-Hant): name this app's own sidebar in English, and empty the …
os-sales commentedon Sep 3, 2026
os-dev-report
{ "issue": 1544, "status": "done", "branch": "claude/issue-1544-crm-nav-quarantine-worklist", "pr": "https://github.com/objectstack-ai/hotcrm/pull/1546", "premise_still_valid": true, "summary": "Executed the worklist KNOWN_UNRESOLVED_CRM left for #1368. Six bold citation sites across four zh-Hant pages now name this app's own sidebar in English, the five ledger entries are deleted, and the block's comment is rewritten to record #1368's decision (2026-08-31), its measured reason (no Traditional bundle => console falls back to Simplified => a Traditional page names navigation in English; NOT 'the reader sees an English UI', which is retired) and the opposite-direction failure pair. All of it in ONE commit, d979ece7. ZONE 2 assumption 2 is FALSIFIED in one place: the card's worked example 'Activity -> Activities' is in no locale of CRM_PAIRS; the pair resolves to 'Activity -> Events' (group_activity -> nav_event). One PM-side half-state noted: the issue arrived with assignees ['os-sales'] already set, as expected for this seat.", "measurements": { "M1_citation_sites": "201 DOC_PAGES scanned, 30 CRM citations parsed; the five quarantined pairs occur at EXACTLY the six declared sites and nowhere else. No seventh site. 我的工作 → 我的行事曆 = 1 site (guides/email-and-calendar.zh-Hant.mdx:82); 我的工作 → 我的任務 = 2 (sales/activities.zh-Hant.mdx:94, :109); 我的工作 → 我的線索 = 1 (sales/leads.zh-Hant.mdx:144); 我的工作 → 我的日曆 = 1 (sales/meetings-and-calls.zh-Hant.mdx:145); 活動 → 活動 = 1 (sales/meetings-and-calls.zh-Hant.mdx:134). Citations resolving to nothing and NOT quarantined: 0.", "M2_pair_resolution": "CRM_PAIRS.size = 104, CRM_GROUP_LABELS.size = 23, CRM_LOCALES = ['en','zh-CN','ja-JP','es-ES']. inCRM_PAIRS readings: 'My Work → My Calendar' true (en: group_work -> nav_my_calendar); 'My Work → My Tasks' true (en: group_work -> nav_my_tasks); 'My Work → My Leads' true (en: group_work -> nav_my_leads); 'Activity → Events' true (en: group_activity -> nav_event); 'Activity → Activities' FALSE (origin null) — the card's guess ships nowhere; 'Activity → Sales Activity' true (nav_activity_dashboard, the other child, rejected on context).", "M3_guard_green_on_main": "origin/main at 05f867e1, before any edit: 'Test Files 1 passed (1) / Tests 18 passed (18)', os-verify-lock VERDICT command-exit 0. Not pre-red.", "M4_atomicity": "Real, three legs, each with on-disk proof and state-verified restore. See 'tests'." }, "pairs_chosen": [ "guides/email-and-calendar.zh-Hant.mdx:82 → **My Work → My Calendar** — resolved against src/apps/crm.app.ts group_work/nav_my_calendar (en label 'My Calendar'); English twin email-and-calendar.mdx:82 uses the identical string", "sales/activities.zh-Hant.mdx:94 and :109 → **My Work › My Tasks** — group_work/nav_my_tasks; English twin activities.mdx:94 and :109 identical", "sales/leads.zh-Hant.mdx:144 → **My Work › My Leads** — group_work/nav_my_leads; English twin leads.mdx:153 identical", "sales/meetings-and-calls.zh-Hant.mdx:145 → **My Work › My Calendar** — group_work/nav_my_calendar; English twin meetings-and-calls.mdx:145 identical", "sales/meetings-and-calls.zh-Hant.mdx:134 → **Activity › Events** — group_activity/nav_event. THE AWKWARD ONE: group and child were the same word because zh-CN labels BOTH group_activity and nav_event as 活动 (src/translations/zh-CN.ts:1285 and :1286); the group half resolved at all only because ja-JP labels group_activity 活動 (ja-JP.ts:1321). It is NOT 'Activity → Activities' (ships nowhere); the group's only two children are Events and Sales Activity, and the sentence is about the Events list. English twin meetings-and-calls.mdx:134 confirms: '**Activity › Events** is the one sidebar entry'." ], "tests": "All runs through the shared container lock (scripts/pm/os-verify-lock.sh, slot dev-1544); verdicts read from its VERDICT line, exit codes captured before any pipe.\n\nABLATION (three legs, guard file only, base origin/main 05f867e1). No rebuild leg exists and none was skipped: this guard imports ../src/apps and ../src/translations as TypeScript SOURCE through vitest and reads content/docs/** off disk — nothing on its resolution path goes through a package `exports` field or a dist/ directory, so there is no stale-artifact surface for a mutation to hide behind. Mutation proof on disk was therefore the whole of it, and it was done every leg: blob hash before/after PLUS anchored counts on removed AND injected text (removed-text count 0, injected-text count N, per file), a zero-hit anchor being a hard exit 3. Restore: `git checkout HEAD -- ABSOLUTE_PATH` (HEAD named explicitly) from a `trap ... EXIT INT TERM` with REPO_ROOT resolved by git rev-parse --show-toplevel, then verified BY STATE — `git diff HEAD` empty and every blob hash back to its HEAD value — never by exit code.\n\n leg (a) prose only → Tests 1 failed | 17 passed (18). FAIL 'holds no quarantined pair the docs no longer cite': \"AssertionError: KNOWN_UNRESOLVED_CRM entries no longer cited by any page: 我的工作 → 我的行事曆, 我的工作 → 我的任務, 我的工作 → 我的線索, 我的工作 → 我的日曆, 活動 → 活動. Someone fixed the prose — delete these lines so the ledger keeps measuring the real remainder.\" (test file line 708)\n leg (b) ledger only → Tests 1 failed | 17 passed (18). FAIL 'name a group and a child this app really ships, in one locale': \"AssertionError: docs cite sidebar paths this app does not ship: content/docs/guides/email-and-calendar.zh-Hant.mdx: '我的工作 → 我的行事曆'; content/docs/sales/activities.zh-Hant.mdx: '我的工作 → 我的任務'; content/docs/sales/leads.zh-Hant.mdx: '我的工作 → 我的線索'; content/docs/sales/meetings-and-calls.zh-Hant.mdx: '活動 → 活動'; content/docs/sales/meetings-and-calls.zh-Hant.mdx: '我的工作 → 我的日曆'. Resolve the pair against src/apps/crm.app.ts and the locale bundles in src/translations — the child must be a child of that group, in the same locale. Do not add it to KNOWN_UNRESOLVED_CRM to get this green.\" (test file line 683)\n leg (c) both → Tests 18 passed (18), VERDICT command-exit 0.\n\nFULL GATE: `pnpm verify` (validate && typecheck && lint && lint:i18n-gate && hygiene && hygiene:tokens && build && test) — VERDICT command-exit 0, held 205s. 'Test Files 159 passed (159) / Tests 3344 passed | 1 skipped (3345)'; '✓ i18n lint gate: 0 i18n/missing-* issues'; '✓ source hygiene clean'; '✓ source token ratchet clean' (business semantics ~83,423 / ceiling ~85,000; interaction ~37,426 / ~40,000; authored total ~135,190 / ~140,000 — unmoved, the ratchet is comment-stripped and reads src/** only). It ran on exactly the bytes that were committed. RE-RUN AFTER THE FINAL COMMIT, at d979ece7 with a clean tree: hygiene + hygiene:tokens + the guard file — '✓ source hygiene clean', '✓ source token ratchet clean', 'Test Files 1 passed (1) / Tests 18 passed (18)', VERDICT command-exit 0.\n\nControl-byte scan on all five edited files plus the changeset: grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' → no matches.", "mcp_calls": "2 — create_pull_request, add_issue_comment. Every GitHub READ went through the unauthenticated REST channel as dispatched; note that https://api.github.com/search/issues is refused on it (\"This GitHub API path is not available: sessions are bound to their configured repositories\"), so the dedupe sweep used the repo-scoped list endpoint (repos/objectstack-ai/hotcrm/issues?state=open, 89 rows) plus local grep instead.", "open_questions": [], "out_of_scope_findings": [ "NOT FILED (reported for PM to file, per this dispatch's 'report them' instruction and the flagged MCP budget) — the navigation guard's OWN header publishes the reason #1368 retired. test/docs-setup-navigation-names.test.ts:159, inside the #1113 history section: 'the platform ships en / zh-CN / ja-JP / es-ES and no Traditional-Chinese pack, so a zh-Hant reader sees the English UI'. That is the causal claim #1368 struck. Dedupe: #1540 is the open card for this exact sentence, but its declared surface is seven content/docs zh-Hant pages (administration/automation, profiles, sandbox-and-releases, setup, sharing-and-security, guides/integrations, reference/glossary) — the guard file is not among them, and #1544's claim fences that file for this PR's duration, so no in-flight card covers it. I deliberately did NOT fix it: it sits outside 'the KNOWN_UNRESOLVED_CRM block only' that my claim declares, and the file is fenced against #1403. Consequence to weigh: after this PR the file states the correct reason at line ~420 and the retired one at line 159, ~260 lines apart.", "ALREADY COVERED by open decision card #1329 — no new card needed. zh-Hant pages spell SAVED VIEW names in Traditional (全部任務, 我的待辦任務, 我的日曆, 我的線索, 全部線索) while src/views/*.ts labels them in English and only en/zh-CN/ja-JP/es-ES are translated, so those exact strings render in no configuration — the same defect class as this card, one layer over, and unguarded. New datum for #1329: sales/activities.zh-Hant.mdx already ships BOTH conventions for the SAME view on adjacent lines — '**My Open Tasks**' (English, :96 and :109) and '**我的待辦任務**' (Traditional, :94 and :109). Untouched by this PR on purpose; navigation is what #1368 ruled on, view names are what #1329 has not." ] }Generated by Claude Code