Skip to content

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

Closed

Description

@os-sales

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. ⚠️ The next correction is another multi-site sweep, which is the failure being cleaned up.

B — one shared MDX partial imported by the seven pages. One edit point. ⚠️ Introduces an include mechanism the docs tree does not use today, and the register genuinely differs per site — the dev measured three variants: a mid-sentence aside (3 sites, no trailing 。), a standalone note (3 sites, trailing 。 inside the parens), and a short form on 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.md and 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 from AGENTS.md:343-345 if 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 cite AGENTS.md in 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 in content/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

⚠️ Serial with #1547 (the guard header's own stale copy at :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

  1. added
    documentationImprovements or additions to documentation
    pm:queueReady for the PM dispatch loop
    on Sep 3, 2026
  2. os-sales commented on Sep 3, 2026

    @os-sales
    CollaboratorAuthor

    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:159 now reads "so the console falls back to Simplified" and :161 carries the justifying half — "rather than ship mixed Simplified/Traditional script", the AGENTS.md:343-345 wording.
    • A one-directional cross-reference was added at the end of that bullet pointing to 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_UNRESOLVED and KNOWN_UNRESOLVED_CRM are 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:

    ⚠️ This is the trap for whoever builds the guard here. A rule that bans the retired sentence must not fire on a line that quotes it in order to forbid it. The negation at :429-431 and 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_DIRS includes test/, but FIRST_PARTY_TEXT filters out SELF — 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

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 3, 2026
  4. self-assigned this
    on Sep 3, 2026
  5. os-sales commented on Sep 3, 2026

    @os-sales
    CollaboratorAuthor

    Claim: repo:hotcrm PM seat — dispatching to an os-dev agent now.

    ⚠️ Two constraints recorded on this card above that decide whether it is done right — read them before writing a line:

    1. The guard must not fire on a line that quotes the retired sentence in order to forbid it. test/docs-setup-navigation-names.test.ts:429-431 carries 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.
    2. A guard for this sentence cannot live inside docs-setup-navigation-names.test.ts's own scanned surface. That file exempts itself as SELF (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

  6. os-sales commented on Sep 3, 2026

    @os-sales
    CollaboratorAuthor

    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

  7. os-sales commented on Sep 3, 2026

    @os-sales
    CollaboratorAuthor

    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 docs-setup-navigation-names.test.ts's deliberate negation. ⚠️ Passing that is trivially achievable by a rule that never looks at the file — which would be a different bug wearing the same green.

    The dev proved the distinction in both directions:

    • negation untouched (verbatim on the tree, :432-434 — ⚠️ the card cited :429-431; today's numbers are 432-434, seat-verified) → all 5 tests pass;
    • remove ONLY the sanctioned-reason marker from that same comment block, leaving the negation byte-identical → RED, naming that block: "a passage names platform navigation in English without giving the reason AGENTS.md sanctions … : test/docs-setup-navigation-names.test.ts :: const KNOWN_UNRESOLVED_CRM = new Set…"

    ⇒ 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:

    before #1537   7 of 7 passages RED
    before #1554   1 of 9 RED
    from  #1554    green
    

    ⇒ 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-345 and is derived and asserted, so the rulebook stays the source. The Chinese strings cannot be — AGENTS.md carries no Traditional rendering — so those are pinned constants citing it. ⛔ AGENTS.md untouched. ⭐ 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 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). ⭐ 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

  8. removed their assignment
    on Sep 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions