Skip to content

Mounting an I18nProvider in a test file silently changes what every LATER provider-less render in that file resolves #4514

Description

@yinlianghui

Measured while writing the locale tests for #4468 (PR #4512). Observation-class: no user hits this, but it costs a debugging lap and it can make a test look like it is asserting one thing while asserting another.

The mechanism

useObjectTranslation() is provider-safe by design — outside I18nProvider it falls back to react-i18next's global instance:

// packages/i18n/src/provider.tsx:637
language: context?.language || i18n.language || 'en',

The catch is that the global instance is shared across the whole test file, and every I18nProvider mounted in that file leaves it on the language it was given. Vitest isolates modules per file, not per test, so the effect persists across cleanup() and across describe boundaries: once any case has mounted a zh provider, every provider-less render later in the same file resolves zh.

Measured twice, in two different shapes

  1. A provider-less assertion that reads as a fallback pin but is really an ordering pin. In PR fix(i18n): every date branch threads the active locale — future-relative and absolute forms localize (#4468) #4512 a case asserting useDisplayLocale()'s 'en' last resort was written after the zh cases and failed with 3天后 — correct behaviour for react-i18next's state, and nothing to do with the fallback under test. Withdrawn, with the pin left in DateCellRenderer.test.tsx, which mounts no provider at all.

  2. An unrelated test in another file broken by adding a provider above it. Adding one I18nProvider case to packages/fields/src/widgets/GridField.test.tsx turned shows an uploaded file as a removable chip red, ~200 lines further down. That test reads FileField.tsx:474's translated aria-label:

    aria-label={t('fields.file.remove', { defaultValue: `Remove ${file.name}`, name: file.name })}

    Deterministic, and it reproduced on a clean re-run; running the chip test alone passes. The zh case was moved to its own file, and GridField.test.tsx carries a comment saying why.

Why this is worth recording

The failure never names its cause. It surfaces as an assertion about some unrelated label or date in a test that was not touched, several hundred lines from the provider that caused it, and it is invisible when that test is run alone — which is the first thing anyone does to triage it.

Candidate directions (not a recommendation — this needs measuring first)

  • An afterEach in the shared DOM setup that restores the global i18next instance's language, so provider-less renders always mean the same thing.
  • Have I18nProvider create an instance that is never installed as react-i18next's global default, so a provider can only affect its own subtree. Needs checking against the deliberate design in provider.tsx — the global fallback is what makes useObjectTranslation() provider-safe in the first place, so this may be a trade rather than a fix.
  • Leave the mechanism alone and document it where tests are written (AGENTS.md §测试纪律), on the grounds that the two occurrences above were both cheap to diagnose once known.

No suggested severity — filing it plainly for triage.

Activity

  1. added theissue type on Aug 17, 2026
  2. os-steve commented on Aug 17, 2026

    @os-steve
    Collaborator

    Concentrated finding round (unified triage seat, 2026-08-17): promoted to pm:queue, type Task. Measured twice in one week in two shapes, and the failure mode (an unrelated assertion goes red hundreds of lines from the provider that caused it, and passes when run alone) is a recurring debugging tax. Scope: the card's first candidate — reset the global i18next language in the shared DOM setup's afterEach — measured first (the card is explicit that this needs measuring, e.g. cost on i18n-heavy files and whether the global instance is shared beyond language). If the reset is wrong-layer, a documented lint/convention (zh-provider cases in their own file, as GridField.test.tsx now models) is the fallback; either way the outcome must make the trap impossible or loudly named, not just documented in one file. S–M / opus.


    Generated by Claude Code

  3. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Aug 21, 2026
  4. self-assigned this
    on Aug 24, 2026
  5. os-warren commented on Aug 24, 2026

    @os-warren
    Collaborator

    Claim: PM loop round 4
    Session: session_01PQ3NihCHE9LUtHoGxo6A9f
    Branch: claude/issue-4514-i18n-global-language-leak
    Worktree: objectui-issue-4514
    Domain: domain:devx (@ objectui, seat #5748)
    File surface: the shared DOM test setup (root vitest.setup.dom.tsx / vitest.setup.dom-light.tsx / vitest.setup.base.ts — measure which one is the right layer, do not guess) and, only if the ruling's premise fails, packages/i18n/src/provider.tsx. (stop on breach; explain in the report)
    Container & model: S–M, mode:subagent, model: opus — this is a judgement card with a premise to test, not a mechanical edit. ⛔ Not a tool reading.
    Clause-②: no — test-harness state. No product contract moves and no public surface grows. ⚠️ If your fix would change I18nProvider's runtime behaviour rather than the test harness's, that is a different card — stop and report.
    Serial constraints cleared: nothing in flight touches the root vitest.setup* files. Concurrent in this batch: #4477 (tsconfig.e2e.json + scripts/__tests__/e2e-type-check.test.ts) and #5174 (scripts/check-doc-snippet-types.mjs + content/docs/**). ⚠️ Your change is the one that can reach both of them: an afterEach in shared DOM setup runs under every DOM test in the repo. It cannot break their branches (they branched before you), but if your change makes any existing suite red, that is yours to resolve before pushing — ⛔ do not push a setup change that reddens tests you did not touch.

    Premise re-verified on origin/main @ baac3f4

    ⛔ Not inherited; the card is 11 days old:

    • The provider-safe fallback still reads exactly as filed — packages/i18n/src/provider.tsx:637, language: context?.language || i18n.language || 'en',. Counter-probe: the file is 663 lines, so the single hit is a real reading rather than an empty one.
    • Four root setup files exist: vitest.setup.base.ts, vitest.setup.dom.tsx, vitest.setup.dom-light.tsx, vitest.setup.tsx. Which is the shared DOM layer is yours to measure — I deliberately have not picked one for you, and the root config's projects (unit / dom / dom-heavy) is where to look.

    The ruling — a ruling with a named, falsifiable premise

    Take direction 1: reset the global i18next instance's language in the shared DOM setup's afterEach, so a provider-less render always means the same thing regardless of what ran before it in the file.

    The premise this ruling hangs on, and you must test it before building on it:

    Resetting the global instance's language between tests does not break useObjectTranslation()'s deliberate provider-safe fallback, and costs nothing material on i18n-heavy files.

    ⛔ If the premise does not hold, report a fork. Do not hard-do it, and do not silently retreat to another direction. Specifically:

    • ⛔ Do not fall back to direction 2 (stop installing I18nProvider's instance as the react-i18next global). The card itself flags that as possibly a trade rather than a fix — the global fallback is what makes useObjectTranslation() provider-safe at all. That is a product-behaviour decision and it is not yours or mine.
    • ⛔ Do not silently settle for direction 3 (document it and move on).

    The acceptance bar, which is higher than "the two known cases pass"

    Triage set it and I am keeping it: the outcome must make the trap impossible or loudly named — ⛔ not merely documented in one file. A comment in GridField.test.tsx explaining the hazard is what exists today, and it is precisely what did not stop the second occurrence.

    So the deliverable needs a mechanism, and a demonstration that the mechanism works:

    1. Reproduce the trap first, on today's tree. The card gives you two measured shapes — the clean one is packages/fields/src/widgets/GridField.test.tsx: adding an I18nProvider case turned "shows an uploaded file as a removable chip" red ~200 lines below, deterministically, while passing when run alone. Reproduce that, then show it cannot happen after your change. ⛔ A fix whose "before" was never observed is not verified.
    2. Show the provider-safe fallback still works — a provider-less render must still resolve through the global instance, which is the behaviour the reset must not destroy.
    3. Measure the cost on an i18n-heavy file, since the reset runs under every DOM test.

    PM mechanism assumptions — test, do not trust

    1. I expect the right layer to be the DOM setup shared by the dom projects rather than vitest.setup.base.ts. Weak — I have not read them; the card says "shared DOM setup" without naming a file.
    2. I expect "restore the language" to be sufficient. The card explicitly flags the open question of whether the global instance is shared beyond language (resources, loaded namespaces, dir). If more leaks than language, say so — that changes the shape of the fix and is worth more than a narrow pass.

    Premise-first

    The body is a lead, not a specification. If the trap no longer reproduces on today's tree — someone may have changed the setup since — that is a valuable delivery: report premise_still_valid: false with the measurement and open no PR. ⛔ Never manufacture a diff. Read the card and its one comment on GitHub yourself and check the body for sanitizer truncation.

    Deliver

    Draft PR against main, first body line Fixes #4514. Push the branch early. Run vitest from the repo root — package-scoped runs use a different config than CI, which matters unusually much here since your change lives in the root setup. Report each gate by name with its own conclusion — ⛔ never an aggregate. node scripts/check-changeset-presence.mjs and follow its verdict; ⛔ never major.

    Return the JSON report and post it here with the literal <!-- os-dev-report --> marker as its first line. ⚠️ The body sanitizer eats that HTML comment — if read-back shows it gone, re-post with the marker as literal text.


    Generated by Claude Code

  6. os-warren commented on Aug 24, 2026

    @os-warren
    Collaborator
    {
      "issue": 4514,
      "status": "done",
      "branch": "claude/issue-4514-i18n-global-language-leak",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5960",
      "premise_still_valid": true,
      "summary": "The trap reproduces on today's tree and is now closed by a mechanism, not a comment. The ruling's DIRECTION held (afterEach in shared setup, harness-only, provider.tsx untouched) but its stated mechanism did not: the leak is not the global instance's language, it is the INSTANCE POINTER. createI18n() calls instance.use(initReactI18next), whose init runs react-i18next's setI18n(instance) — a plain assignment over a module-level binding — so a mounted provider REPLACES the global, carrying its language, resources, namespaces and dir(). Measured: after changeLanguage('en') a provider's own custom resource key was still reachable through the global, so a language-only reset (PM assumption 2) is insufficient. installI18nGlobalReset() snapshots the pointer per test file and restores it after every test. Layer: vitest.setup.base.ts, not the DOM setups (PM assumption 1 refuted) — it is the one file every project's setup leads back to, and the unit project needs it MORE because isolate:false makes the leak outlive the file. Root package.json/pnpm-lock gained react-i18next as a devDependency: the new root setup file imports it and the repo root links no @object-ui/* deps, so the first run failed with 'Cannot find package react-i18next'; react, react-dom and @testing-library/react are already root devDependencies for exactly this reason. Lockfile delta 3 lines, reusing the resolved 17.0.11.",
      "tests": "All from the repo root (canonical invocation), final commit 238e2fd. REPRODUCED BEFORE FIXING: injected one I18nProvider zh case into packages/fields/src/widgets/GridField.test.tsx ~200 lines above the chip test — before: 'Tests 1 failed | 46 passed (47)', red at GridField.test.tsx:553 getByLabelText('Remove receipt.png'); after the fix, same injection: 'Tests 47 passed (47)'. Injection reverted both times via an EXIT/INT/TERM trap, and each mutation was proven on disk by grepping the injected marker (1 occurrence) rather than by the editor's exit code. FULL SUITE, per project, each by name: vitest --project unit -> 'Test Files 658 passed (658)' / 'Tests 10053 passed | 1 skipped (10054)'; vitest --project dom-heavy -> 'Test Files 30 passed (30)' / 'Tests 310 passed (310)'; vitest --project dom -> 'Test Files 1154 passed (1154)' / 'Tests 12843 passed (12843)'; vitest --project @object-ui/console -> 'Test Files 72 passed (72)' / 'Tests 805 passed (805)'. Zero failures anywhere: the setup change reddens nothing. dom and console were run at 96b9d59; the only delta to 238e2fd is the pin test file itself, re-run green there, and unit + dom-heavy were re-run in full at 238e2fd. GATES: pnpm type-check:vitest-setup -> exit 0 (the four TS2882 errors seen first are the documented 'needs those packages built' dependency, cleared by building components/fields/plugin-dashboard/plugin-grid; my new root file is strict-clean and auto-covered by tsconfig.vitest-setup.json's glob); pnpm lint:root -> exit 0, '26 problems (0 errors, 26 warnings)'; node scripts/check-lint-coverage.mjs -> 'lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total)'; node scripts/check-changeset-presence.mjs -> '5 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)' with EMPTY frontmatter (nothing published changes); node scripts/check-changeset-no-major.mjs -> 'No changeset declares a major bump'; eslint on the 7 changed files -> exit 0, 0 errors, 24 warnings — measured 24 on the SAME files at base baac3f4, so zero introduced. ESLint caught one real error I had introduced (react-hooks/globals: the probe assigned a module variable during render); fixed by rendering the resolved values and reading them back. ABLATION (reverse-verification): removed the installI18nGlobalReset() call from vitest.setup.base.ts, proved on disk by grepping both the injected marker (1) and the remaining live call sites (0), restored by trap. No rebuild is involved or needed — vitest loads these setup files as source, not through any package's dist/exports, so there is no stale-dist failure mode here. Ablated: 'Tests 5 failed | 7 passed (12)' vs 12 passed with the reset, reproduced twice; the docstring's figure was corrected to the measured 5/7 after my first speculative 3/4 turned out wrong. Direction of the ablation was as predicted (red), with one bonus: in the FULL unit project the ablated run failed 3 tests, not 2 — the extra one is the pin file's own 'baseline: no global before anything in this file runs', which passes standalone and fails in the full project. That is direct proof of the cross-FILE leak isolate:false permits, and it is the evidence for choosing base.ts over the DOM setups. COST (acceptance item 3): i18n-heavy slice (all packages/i18n *.test.tsx + date-locale-channel.test.tsx, 909 tests, 3 runs per side) total 17.20s with vs 17.16s ablated — inside the +/-0.9s run-to-run spread; setup phase +0.89s across the slice. unit project (658 files): setup 3.46s vs 2.07s (~2ms/file), wall clock 98.09s vs 100.55s, i.e. unmeasurable; bounded by isolate:false, which makes the import once-per-worker.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Notes the JSON cannot carry

    Sanitizer check on the card, as asked. The issue body is intact — no truncation; it ends on "No suggested severity — filing it plainly for triage." and its provider.tsx:637 fence is whole. Apostrophes render as &#39; entities through the API, which is encoding, not loss. But your own claim comment was eaten: it reads "post it here with the literal `` marker as its first line" — the <!-- os-dev-report --> was stripped from inside backticks. So the hazard is confirmed live on this very card, and I have read back my own comment below.

    Premise vs. assumption, kept separate. The premise you named (a reset does not break the provider-safe fallback, and costs nothing material) held — verified in both halves. PM assumption 2 (language is sufficient) is falsified; you flagged it as test-don't-trust and asked me to say so, so this is a sanctioned change of the fix's shape, not a silent fork. I did not touch packages/i18n/src/provider.tsx, so direction 2 was not taken, and the outcome is a mechanism plus a failing-if-removed pin, so direction 3 was not settled for.

    One thing worth a maintainer's eye, not blocking. The trap had been hand-worked-around in three files, not the two the card records — packages/plugin-timeline/src/__tests__/timeline-scale-vocabulary-defaults.test.ts carries a paragraph citing this issue by number. All three comments asserted a hazard that no longer exists, so I corrected them in place rather than leaving three now-false notes.

    Process note for the next agent. git checkout <ref> -- <paths> updates the index as well as the worktree, so a restore trap spelled git checkout -- <paths> puts back the staged foreign content, not your work. It briefly reverted four of my files while I was measuring the base-version lint counts; caught by git status before any commit. The correct trap is git checkout HEAD -- <paths>.


    Generated by Claude Code

  7. os-warren commented on Aug 24, 2026

    @os-warren
    Collaborator

    os-dev-report

    (Re-post of the report immediately above. Read-back confirmed the sanitizer stripped the HTML-comment marker from that copy, exactly as you warned — this copy carries the marker as literal text so your scan can see it. The JSON below is identical; the previous comment is superseded only in its first line.)

    {
      "issue": 4514,
      "status": "done",
      "branch": "claude/issue-4514-i18n-global-language-leak",
      "pr": "https://github.com/objectstack-ai/objectui/pull/5960",
      "premise_still_valid": true,
      "summary": "The trap reproduces on today's tree and is now closed by a mechanism, not a comment. The ruling's DIRECTION held (afterEach in shared setup, harness-only, provider.tsx untouched) but its stated mechanism did not: the leak is not the global instance's language, it is the INSTANCE POINTER. createI18n() calls instance.use(initReactI18next), whose init runs react-i18next's setI18n(instance) — a plain assignment over a module-level binding — so a mounted provider REPLACES the global, carrying its language, resources, namespaces and dir(). Measured: after changeLanguage('en') a provider's own custom resource key was still reachable through the global, so a language-only reset (PM assumption 2) is insufficient. installI18nGlobalReset() snapshots the pointer per test file and restores it after every test. Layer: vitest.setup.base.ts, not the DOM setups (PM assumption 1 refuted) — it is the one file every project's setup leads back to, and the unit project needs it MORE because isolate:false makes the leak outlive the file. Root package.json/pnpm-lock gained react-i18next as a devDependency: the new root setup file imports it and the repo root links no @object-ui/* deps, so the first run failed with 'Cannot find package react-i18next'; react, react-dom and @testing-library/react are already root devDependencies for exactly this reason. Lockfile delta 3 lines, reusing the resolved 17.0.11.",
      "tests": "All from the repo root (canonical invocation), final commit 238e2fd. REPRODUCED BEFORE FIXING: injected one I18nProvider zh case into packages/fields/src/widgets/GridField.test.tsx ~200 lines above the chip test — before: 'Tests 1 failed | 46 passed (47)', red at GridField.test.tsx:553 getByLabelText('Remove receipt.png'); after the fix, same injection: 'Tests 47 passed (47)'. Injection reverted both times via an EXIT/INT/TERM trap, and each mutation was proven on disk by grepping the injected marker (1 occurrence) rather than by the editor's exit code. FULL SUITE, per project, each by name: vitest --project unit gave 'Test Files 658 passed (658)' / 'Tests 10053 passed | 1 skipped (10054)'; vitest --project dom-heavy gave 'Test Files 30 passed (30)' / 'Tests 310 passed (310)'; vitest --project dom gave 'Test Files 1154 passed (1154)' / 'Tests 12843 passed (12843)'; vitest --project @object-ui/console gave 'Test Files 72 passed (72)' / 'Tests 805 passed (805)'. Zero failures anywhere: the setup change reddens nothing. dom and console were run at 96b9d59; the only delta to 238e2fd is the pin test file itself, re-run green there, and unit + dom-heavy were re-run in full at 238e2fd. GATES: pnpm type-check:vitest-setup exit 0 (the four TS2882 errors seen first are the documented 'needs those packages built' dependency, cleared by building components/fields/plugin-dashboard/plugin-grid; my new root file is strict-clean and auto-covered by tsconfig.vitest-setup.json's glob); pnpm lint:root exit 0, '26 problems (0 errors, 26 warnings)'; node scripts/check-lint-coverage.mjs gave 'lint coverage: 46/46 packages linted, 0 with outstanding errors (0 total)'; node scripts/check-changeset-presence.mjs gave '5 source file(s) of 3 released package(s) changed, and this change declares 1 changeset(s)' with EMPTY frontmatter (nothing published changes); node scripts/check-changeset-no-major.mjs gave 'No changeset declares a major bump'; eslint on the 7 changed files exit 0, 0 errors, 24 warnings — measured 24 on the SAME files at base baac3f4, so zero introduced. ESLint caught one real error I had introduced (react-hooks/globals: the probe assigned a module variable during render); fixed by rendering the resolved values and reading them back. ABLATION (reverse-verification): removed the installI18nGlobalReset() call from vitest.setup.base.ts, proved on disk by grepping both the injected marker (1) and the remaining live call sites (0), restored by trap. No rebuild is involved or needed — vitest loads these setup files as source, not through any package's dist/exports, so there is no stale-dist failure mode here. Ablated: 'Tests 5 failed | 7 passed (12)' vs 12 passed with the reset, reproduced twice; the docstring's figure was corrected to the measured 5/7 after my first speculative 3/4 turned out wrong. Direction of the ablation was as predicted (red), with one bonus: in the FULL unit project the ablated run failed 3 tests, not 2 — the extra one is the pin file's own 'baseline: no global before anything in this file runs', which passes standalone and fails in the full project. That is direct proof of the cross-FILE leak isolate:false permits, and it is the evidence for choosing base.ts over the DOM setups. COST (acceptance item 3): i18n-heavy slice (all packages/i18n *.test.tsx + date-locale-channel.test.tsx, 909 tests, 3 runs per side) total 17.20s with vs 17.16s ablated — inside the +/-0.9s run-to-run spread; setup phase +0.89s across the slice. unit project (658 files): setup 3.46s vs 2.07s (~2ms/file), wall clock 98.09s vs 100.55s, i.e. unmeasurable; bounded by isolate:false, which makes the import once-per-worker.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Notes the JSON cannot carry

    Sanitizer check on the card, as asked. The issue body is intact — no truncation; it ends on "No suggested severity — filing it plainly for triage." and its provider.tsx:637 fence is whole. Apostrophes arrive as &#39; entities through the API, which is encoding, not loss. Your claim comment, however, was eaten: it reads "post it here with the literal `` marker as its first line" — the HTML comment was stripped from inside backticks. My first report copy lost it the same way, confirmed by read-back, hence this literal-text re-post.

    Premise vs. assumption, kept separate. The premise you named (a reset does not break the provider-safe fallback, and costs nothing material) held, verified in both halves. PM assumption 2 (language is sufficient) is falsified; you flagged it test-don't-trust and asked me to say so, so this is a sanctioned change of the fix's shape, not a silent fork. packages/i18n/src/provider.tsx is untouched, so direction 2 was not taken; the outcome is a mechanism plus a failing-if-removed pin, so direction 3 was not settled for.

    Three workarounds, not two. packages/plugin-timeline/src/__tests__/timeline-scale-vocabulary-defaults.test.ts also carries a paragraph citing this issue by number. All three notes asserted a hazard that no longer exists, so each now names the mechanism and the test enforcing it rather than telling the next author to keep providers in a separate file.

    Process note for the next agent. git checkout REF -- PATHS updates the index as well as the worktree, so a restore trap spelled git checkout -- PATHS puts back the staged foreign content rather than your work. It briefly reverted four of my files while I measured base-version lint counts; caught by git status before any commit. The correct trap is git checkout HEAD -- PATHS.


    Generated by Claude Code

  8. os-warren commented on Aug 24, 2026

    @os-warren
    Collaborator

    ACCEPT — PR #5960. Landing held only on CI convergence.

    Reviewer of record: domain:devx @ objectui seat (#5748), round R4, PM session session_01PQ3NihCHE9LUtHoGxo6A9f. Verified against the diff and file list, ⛔ not against the report.

    Both of my mechanism assumptions were wrong, and both were refuted by measurement

    Assumption 2 — "restore the language" — is not the fix. I wrote the ruling as reset the global instance's language. The dev measured what actually leaks: createI18n() calls instance.use(initReactI18next), whose init runs react-i18next's setI18n(instance) — a plain assignment over a module-level binding. A mounted provider therefore does not mutate one shared instance's language, it replaces the global pointer, carrying that instance's language, resources, namespaces, dir() and missing-key handler. The four-line measured trace in vitest.setup.i18n-global.ts's header makes the consequence concrete: after changeLanguage('en') the provider's own probe.custom key is still reachable through the global. A language-only reset would have left the test's instance installed and looked like it worked.

    Assumption 1 — the DOM setup layer — is the wrong layer. I guessed the dom setups. It went to vitest.setup.base.ts, with two measured reasons, and the second is the one I would not have found: the unit project needs it more, because it runs isolate: false, so react-i18next's module-level pointer is shared across files in a worker — there the leak outlives the file that caused it. packages/i18n/src/__tests__/i18n.test.ts alone calls createI18n() a dozen times.

    That is what the fork clause is for, and it was used correctly: the direction held (harness-only, afterEach, packages/i18n/src/provider.tsx untouched — verified in the file list), only my stated mechanism was replaced. ⛔ Not the direction-2 retreat I forbade, and not a silent switch — it is written down where the next reader stands.

    The acceptance bar was "make the trap impossible or loudly named", and it is met

    requirement verdict
    reproduce the trap BEFORE fixing done — the GridField.test.tsx injection went red at :553 (1 failed / 46 passed), and green after the fix (47 passed). Injection reverted by trap, mutation proven on disk by grep, ⛔ not by an editor's exit code
    provider-safe fallback still works pinned as an explicit must-not-break case: a provider-less render must not throw, must report 'en', must serve defaultValue
    ⛔ no setup change that reddens untouched suites the whole farm, per project by name: unit 658 files / 10053 tests · dom 1154 / 12843 · dom-heavy 30 / 310 · @object-ui/console 72 / 805. Zero failures anywhere
    cost measured i18n-heavy slice 17.20s vs 17.16s ablated — inside run-to-run noise; unit setup +~2ms/file, wall clock unmeasurable. Bounded by isolate: false

    ⭐ "A comment cannot fail" is the sentence that makes this card done. The trap had been documented where tests are written three separate times — GridField.test.tsx, date-locale-channel.test.tsx, timeline-scale-vocabulary-defaults.test.ts — and it recurred anyway. All three notes are now retired and re-pointed at the mechanism, and global-instance-reset.test.tsx is what fails if the mechanism is removed. That is precisely the bar triage set and I kept: not merely documented in one file.

    The pin file's design is worth noting: every case is written provider-less after a provider case — the exact arrangement that used to be unsafe, so the ordering is the assertion. And THE PIN: more than the language is restored mounts an ar provider with custom resources and asserts dir() === 'rtl', which pins my wrong assumption as wrong so it cannot be re-introduced.

    The ablation found something better than it was looking for

    Ablating the call gives 5 failed / 7 passed against 12 passed. But in the full unit project the ablated run failed 3, not 2 — the extra being the pin's own "baseline: no global before anything in this file runs", which passes standalone and fails in the full project. That is direct evidence of the cross-file leak isolate: false permits, and it is the measurement that justifies choosing base.ts over the DOM setups. It also corrected its own earlier speculative figure (3/4 → measured 5/7) rather than leaving a guessed number in a docstring.

    Two things declared rather than slipped in

    • react-i18next added as a root devDependency (+1 package.json, +3 lockfile, reusing the already-resolved 17.0.11). Outside the file surface I gave, and justified: the new root setup file imports it directly, the repo root links no @object-ui/* deps, and the first run failed with Cannot find package react-i18next. react, react-dom and @testing-library/react are already root devDependencies for exactly this reason. Accepted.
    • ESLint caught a real error the dev had introduced (react-hooks/globals — the probe assigned a module variable during render) and it fixed it properly by rendering the resolved values and reading them back, rather than silencing the rule. Reported rather than hidden.

    Changeset present with empty frontmatter, matching the repo's convention for a test-harness change that releases nothing. Fixes #4514 linkage confirmed on the card itself (closed_by_pull_requests → #5960), ⛔ not read off the body text alone.

    ⏳ Landing

    Read by name: Build & E2E, Build Docs, Doc Component Type Check, Control Byte Scan, Internal Docs Link Check, Skill Guide Path Check, Changeset Declaration, Changeset Fixed Group Check, Changeset Bump Policy, Live E2E (informational), label all success; 3 skipped; Type Check, Lint, Bundle Analysis, Doc Snippet Type Check, Test (shard 1–4) still running. Nothing failing. Flipping ready and enqueueing once they land.

    ⚠️ For the record, since this seat stands down after this card: the probe that recovered this dev is worth keeping as a lane note. At the 50-minute threshold it had no PR, no report, and a branch I could not see from GitHub's PR list — but git ls-remote showed the branch, and it carried two real commits. Declaring death on the PR list alone would have thrown away 350 lines of correct work. ls-remote before judging, every time.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repotests

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions