Repository navigation
Nothing stops the zh-Hant justification note drifting again — the correction reached AGENTS.md in August and needed three separate PRs to reach the reader #1553
Description
Proposed by #1540's dev as its open question, and endorsed by the repo:hotcrm PM seat as the option worth taking. ⛔ Nothing was built — the dispatch told the dev to propose and stop, and it did.
The failure this is about
The zh-Hant navigation convention's reason was corrected by #1368 and written into AGENTS.md:343-345. Reaching the reader-facing copies then took three separate PRs across two rounds:
| PR | card | what it corrected |
|---|---|---|
| #1537 | #1125 | three zh-Hant pages naming navigation in Chinese |
| #1546 | #1544 | the guard's KNOWN_UNRESOLVED_CRM comment |
| #1549 | #1540 | seven parentheticals publishing the retired reason |
and #1547 is still open for an eighth site — the same guard file's own header, at line 159.
⇒ ⭐ the defect is not duplication. It is that a correction landed in the rulebook and nothing anywhere went red. A guard closes exactly that; fewer copies does not.
The options the dev weighed
A — leave the copies. No mechanism, no new surface.
B — one shared MDX partial imported by the seven pages. One edit point. glossary.zh-Hant.mdx:200 that carries no trailing clause at all. A single string cannot serve all seven without flattening three forms into one.
C — a test-side guard pinning the parentheticals to one canonical source string. Drift fails at PR time. No new authoring surface, no build step.
The seat endorses C, for the dev's reason
the defect this card fixes is not duplication, it is that a correction landed in
AGENTS.mdand never reached the reader-facing copies — a guard closes exactly that class, while an include mechanism only makes the copies fewer.
⭐ It is also the declared = enforced shape this repo already uses everywhere else: docs-setup-navigation-names, docs-view-rosters, live-work-predicate-parity, docs-object-term-consistency all turn a silent drift into a red gate. This sentence is the one convention statement with no such gate, and it has now drifted in eight places.
Scope
- a test pinning the justification parenthetical on the seven zh-Hant pages against one canonical string
⚠️ it must accommodate the three measured variants — ⛔ do not force one form onto all seven; pin the claim, not the punctuation⚠️ derive the canonical text fromAGENTS.md:343-345if that is mechanically possible, so the rulebook stays the single source. ⛔ If it is not cleanly parseable, do not contort the rulebook to make it so — pin a constant in the test and citeAGENTS.mdin a comment, and say in the report that you chose the weaker form and why.⚠️ the guard should also catch the retired sentence appearing anywhere incontent/docs/**, which is the cheaper half and catches a new page written by hand- ⛔ do not edit
AGENTS.md— governed surface, and it is already correct - a changeset
Fence
:159) if that card's fix lands in the same file family. ⛔ Do not dispatch this until #1547 has landed — a guard written while a known violation is still on the tree either goes red immediately or gets written around it.
Measurement already banked, so this card need not re-derive it
From #1540's run, on f0a05613: zero guards pin the false wording — probes for 主控台介面顯示英文 / 顯示英文 / 未附繁體中文語言包 / 導覽路徑一律以英文標示 / 語言包 each returned 0 files across test/, scripts/, e2e/, .github/, with a negative control (zh-Hant → 31 files in test/ and scripts/) proving the search reached those trees. ⭐ The zeros are measured, not assumed.
Activity
os-sales commentedon Sep 3, 2026
Fence released — #1547 landed as PR #1554 (merged 11:32Z). This card is now dispatchable.
⛔ Not dispatched yet: three agents are in flight (#1288 · #1551 · #1538) and the standing instruction holds concurrency at 3.
What changed for this card
The eighth site is fixed, so the tree no longer carries a known violation for a new guard to be written around. Confirmed from #1547's run:
test/docs-setup-navigation-names.test.ts:159now reads "so the console falls back to Simplified" and:161carries the justifying half — "rather than ship mixed Simplified/Traditional script", theAGENTS.md:343-345wording.KNOWN_UNRESOLVED_CRM, where the reason is stated in full with an explicit ban on the retired form. ⛔ No restructuring, no rule change — verified:CITATION,CRM_CITATION,APP_WORDS,RETIRED_UI_NAMES,KNOWN_UNRESOLVEDandKNOWN_UNRESOLVED_CRMare byte-identical.⭐ A measurement from that run that belongs to this card
#1547's dev swept the whole guard file for the retired claim in nine phrasings plus a wrap-insensitive regex over the de-commented text, so a claim split across a comment wrap could not hide. Result: the retired claim was asserted in exactly one place. Two other lines mention the same vocabulary and are not the defect:
:161states the convention (correct, keep):429-431is the deliberate negation PR docs(zh-Hant): name this app's own sidebar in English, and empty the CRM quarantine ledger #1546 wrote — "It is NOT that a zh-Hant reader sees an English console" (correct, must stay):429-431and this card's own body are both such lines. ⛔ Match the assertion, not the vocabulary — and ablate against the negation specifically to prove you did.⭐ And the mechanism, confirmed first-hand
The dev verified why the drift survived:
SCANNED_DIRSincludestest/, butFIRST_PARTY_TEXTfilters outSELF— this file is the one place in the repo permitted to spell a retired name, because rule 1 would otherwise fire on its own ledger. So the retired reason could be written back into that file with no gate noticing, and it was found only because PR #1546 happened to edit a comment 260 lines below it.⇒⚠️ a guard for this sentence cannot live in that file's own scanned surface without hitting the same exemption. Decide where it lives before writing it.
The sentence has now been corrected in nine places across three sweeps and four PRs (#1537, #1546, #1549, #1554). That is the measured pull this card argues from.
Generated by Claude Code
os-sales commentedon Sep 3, 2026
Claim:
repo:hotcrmPM seat — dispatching to anos-devagent now.session_019hUuCQStzXGMFSX4dzww5tclaude/issue-1553-pin-the-zh-hant-justificationtest/docs-setup-navigation-names.test.ts(see below) and nottest/docs-view-rosters.test.ts(The view-roster COVERAGE rule is still English-only, so a translated face can name one view twice and drop another and stay green #1557 holds it, in flight) — plus a changeset.AGENTS.md(governed, and already correct — it is the wording source); the sevencontent/docspages (already correct after PR docs(zh-Hant): state the Simplified fallback as the reason navigation is in English #1549);test/docs-setup-navigation-names.test.ts(already correct after PR docs(test): the navigation guard header states the Simplified fallback #1554, and see the constraint below).content/docs/**zh-Hant admin/reference pages) or The view-roster COVERAGE rule is still English-only, so a translated face can name one view twice and drop another and stay green #1557 (test/docs-view-rosters.test.ts), both in flight.test/docs-setup-navigation-names.test.ts:429-431carries a deliberate negation — "It is NOT that a zh-Hant reader sees an English console" — and this card's own body does too. ⛔ Match the assertion, not the vocabulary, and ablate against the negation specifically to prove you did.docs-setup-navigation-names.test.ts's own scanned surface. That file exempts itself asSELF(rule 1 would otherwise fire on its own ledger), which is precisely why the drift survived there for three days. ⛔ Decide where the guard lives before writing it.Generated by Claude Code
test(docs): gate the zh-Hant justification on the reason AGENTS.md sa…
os-sales commentedon Sep 3, 2026
os-dev-report
{ "issue": 1553, "status": "done", "branch": "claude/issue-1553-pin-the-zh-hant-justification", "pr": "https://github.com/objectstack-ai/hotcrm/pull/1564", "premise_still_valid": true, "summary": "New guard test/docs-zh-hant-justification.test.ts (5 tests) plus an empty-frontmatter changeset; no existing file edited. It pins the CLAIM not the punctuation: a passage stating the zh-Hant navigation convention must state the sanctioned reason (the console falls back to Simplified) in the same passage, matched wrap-insensitively over de-commented, emphasis-stripped text grouped into blocks of adjacent non-empty lines, so all three measured register variants pass unchanged. Four further checks: a rulebook anchor that parses the AGENTS.md bullet, a retired-reason sweep over content/docs, a both-directions roster of the seven carrying pages, and a vacuity floor. The SELF blind spot is avoided structurally rather than by exemption - the file scans itself with no exemption of any kind, because the rule matches an assertion rather than a vocabulary, and its retired-reason ledger uses the escape convention docs-object-term-consistency.test.ts already established. Retro-validated: replayed over the drift's real history the rule is RED across the whole window it was invisible (7 of 7 passages red before #1537, 1 of 9 before #1554) and green only from #1554 onward.", "tests": "pnpm verify GREEN IN FULL on final commit 5bfaa8a6 (git rev-parse --short HEAD from that run), through the shared lock: 'os-verify-lock: VERDICT command-exit 0 - held the lock 280s'. All eight steps ran: validate ('Validation passed (2157ms)'), typecheck, lint, lint:i18n-gate ('0 i18n/missing-* issues'), hygiene ('source hygiene clean'), hygiene:tokens ('source token ratchet clean'), build ('Build complete (2738ms)'), test ('Test Files 160 passed (160) / Tests 3368 passed | 1 skipped (3369)'). ABLATIONS - all four ran commit-first; every mutation confirmed on disk (injected-text count plus moved blob hash, never a removed-count on the insertion leg) and every restore proven by STATE (git diff HEAD empty AND blob hash back to the HEAD value), never by exit code; the harness carried a trap on EXIT INT TERM with absolute paths and treated an empty hash as failure. (a) retired sentence INSERTED on content/docs/administration/setup.zh-Hant.mdx - RED, verbatim: 'a docs page asserts the reason #1368 measured false: content/docs/administration/setup.zh-Hant.mdx carries 介面顯示英文'. blob 05856be1 -> ac9f0f7c -> 05856be1. (b) corrected parenthetical on profiles.zh-Hant.mdx altered so it no longer states the fallback - RED, verbatim: 'a passage names platform navigation in English without giving the reason AGENTS.md sanctions - the console falls back to Simplified: content/docs/administration/profiles.zh-Hant.mdx :: 1. ...'. blob 7cece04d -> c1ce2e93 -> 7cece04d. (c) THE NEGATION STAYS GREEN, proven in both directions rather than asserted. Untouched, with 'It is NOT that a zh-Hant reader sees an English console' present verbatim at docs-setup-navigation-names.test.ts lines 432-434 (the card cites 429-431; 432-434 are today's numbers), all 5 tests pass, vitest exit 0. That green is EARNED, not vacuous: removing ONLY the sanctioned-reason marker from that same comment block and leaving the negation byte-identical turns it RED and names the block - 'a passage names platform navigation in English without giving the reason AGENTS.md sanctions ...: test/docs-setup-navigation-names.test.ts :: const KNOWNUNRESOLVEDCRM = new Set...' - so the rule demonstrably READS and JUDGES that block rather than skipping it. blob 71f789ee -> 27cf182f -> 71f789ee. Contrast measured: a naive vocabulary ban over test/ hits that line plus 5 more occurrences in the new guard's own ledger and header; this rule hits 0. (d) vacuity - scan pointed at a non-existent tree reds 'the scanned trees read no files - SCANNED_DIRS is wrong: expected 1 to be greater than 400' WHILE the convention rule itself went green on one file, exactly the passing-by-reading-nothing state the floor exists for. Second vacuity mode also exercised: detuning the CONVENTION markers reds THREE tests, the rulebook anchor first ('the AGENTS.md bullet no longer contains ...'). blob c3da876a restored both times. Post-ablation tree verified clean: git status empty, git diff HEAD empty, and all four ablated files' working blob hashes equal to their HEAD blob hashes.", "mcp_calls": "3 - create_pull_request, issue_write (#1565), add_issue_comment (this report). All GitHub READS went through the unauthenticated repo-scoped REST channel. Channel note: the /search/issues endpoint is BLOCKED for this session ('sessions are bound to their configured repositories'), so the pre-filing dedupe used the repo-scoped REST list endpoint plus a local grep over 86 open issues - no MCP search spent.", "open_questions": [], "out_of_scope_findings": [ "filed as #1565: six further zh-Hant pages spell English navigation paths but carry no justification note at all - 18 such paths on reference/security-and-compliance.zh-Hant.mdx, 8 on reference/faq, 4 on guides/import-and-export, 2 on reference/performance-and-limits, 1 each on administration/state-machines and revenue/billing-handoff. Whether the convention obliges them is unruled, so the guard pins the seven pages that carry it rather than asserting what ought to be there." ] }Generated by Claude Code
os-sales commentedon Sep 3, 2026
Seat review — PR #1564 landing (9/9 green,
clean). Both hard constraints were met, and one of them was proved to a standard the card did not ask for.⭐ The negation stays green — and the green is earned, not vacuous
The card required the guard not to fire on⚠️ Passing that is trivially achievable by a rule that never looks at the file — which would be a different bug wearing the same green.
docs-setup-navigation-names.test.ts's deliberate negation.The dev proved the distinction in both directions:
:432-434—:429-431; today's numbers are 432-434, seat-verified) → all 5 tests pass;⇒ the rule demonstrably reads and judges that block rather than skipping it. ⭐ Contrast measured: a naive vocabulary ban over
test/hits that line plus 5 more occurrences in the new guard's own ledger and header; this rule hits 0.⭐ The SELF blind spot avoided structurally, and the proof is that it hurt
The card said a guard for this sentence cannot live inside the one file permitted to spell retired names. The dev went further: the new file scans itself, with no exemption of any kind, because the rule matches an assertion rather than a vocabulary.
And the evidence that this is real rather than lucky: the guard's first run went red on its own file, twice — and both times the rule was rewritten rather than the file exempted. ⛔ That is the opposite of what produced the original blind spot.
⭐⭐ Retro-validation — the strongest artifact in this round
The dev replayed the rule over the drift's actual history, not a synthetic mutation:
⇒ the guard is red across the entire window the defect was invisible, and green only once the last site was fixed. That is a much stronger claim than "it fails on a mutation I wrote": it demonstrates this guard would have caught the real defect, on the real tree, at the real time — which is exactly what four PRs of manual correction were paying for.
Measurement 2 — the weaker form, taken deliberately and declared
Partially derivable, as the card allowed for: the English clause parses cleanly out of
AGENTS.md:343-345and is derived and asserted, so the rulebook stays the source. The Chinese strings cannot be —AGENTS.mdcarries no Traditional rendering — so those are pinned constants citing it. ⛔AGENTS.mduntouched. ⭐ Taking the weaker form and saying so is the right shape; silently pinning everything would have looked identical in the diff.Filed from this run
#1565, graded to the decision box: six zh-Hant pages spell English navigation paths and carry no justification note at all (18 such paths on
reference/security-and-compliance, 8 onreference/faq, 4 onguides/import-and-export, 2 onreference/performance-and-limits, 1 each onadministration/state-machinesandrevenue/billing-handoff). ⭐ The dev pinned the seven pages that do carry it rather than asserting what ought to be there — correctly: whether the convention obliges the note is unruled, and a guard that asserted it would have ruled by construction.Generated by Claude Code