You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
test(frontend): two flaky tests are producing red checks unrelated to the change under test #14613
Two frontend tests failed today for reasons unrelated to the code under change
Both surfaced while landing unrelated PRs. Neither is an assertion failure, and both passed on re-run — which is exactly what makes them expensive: a red check that means nothing trains everyone to re-run rather than read.
1. ChatInterface.test.ts > Rendering > renders the main chat interface
Seen on PR #14592, whose only change was to WorkflowCanvas.vue.
Error: Test timed out in 10000ms.
❯ src/components/__tests__/ChatInterface.test.ts:236:5
A timeout, not a failed expectation. The test renders ChatInterface with pinia + router and waits; something in that mount occasionally exceeds 10s on the runner.
2. OrgChart.people.test.ts > A partial source failure stays partial (#13938) > says the people could not be loaded, not that the company has none
Seen locally while working on #13956. Failed once in a combined run of src/composables/llc src/components/llc src/views/llc (367 tests), then passed
alone and passed on a re-run of the identical command. Nothing changed between
the two runs.
That pattern — passes alone, fails in a larger run — points at cross-file
interference rather than a slow mount: shared module state, a leaked mock, or a
timer that outlives its test.
Why this matters here
The repo's rule is that red CI never merges, and root-causing a failing check is
required rather than optional. A flaky check makes that rule unenforceable in
practice: the honest response to a red run becomes "run it again", and a real
regression hiding behind a familiar flake gets the same treatment.
Ask
Determine whether Docs improvements #1 is a genuinely slow mount (raise the timeout with a
reason) or an unresolved promise (fix it).
For Docs improvements #2, identify the interfering file: run the suite with the seed/order that
reproduced it, then bisect the file set. sys.modules-style leakage has an
exact analogue in vitest module state.
Whatever the cause, the fix is not a longer timeout applied blindly — a test
that only passes because it was given more time has not been diagnosed.
Acceptance
Root cause identified for each, stated in the issue
Each fixed at the cause, not by relaxing the assertion or the timeout alone
A reproduction recorded for each (command + ordering), so the fix is verifiable
Third instance, and it narrows the hypothesis considerably.
OrgChart.people.test.ts > Team grouping is honest about the data that exists (#13938) > groups the user under the team that claims them failed in a combined run of src/components/workflow src/components/llc src/views/llc src/composables/llc (46 files / 466 tests), passed alone (29/29), and passed on an immediate re-run of the identical combined command.
That is now two different tests in the same file — the earlier instance was says the people could not be loaded, not that the company has none — both failing only in a larger run and both passing in isolation.
Same file, different tests, order-dependent: this points at state shared across files that OrgChart.people.test.ts reads, not at a slow mount or a bad assertion in either test. Likely candidates, in the order I would check them:
A module-level ref or cache in an llc composable that another test file populates first (useCompanyPeople, useLlcCompanyContext, and the org-chart composables all hold module-scope state).
A vi.mock factory whose returned object is shared rather than recreated per file.
A timer or pending promise from a neighbouring file resolving into this one's mount.
Reproduction for whoever picks this up: run the four paths above together; it does not reproduce every time, so run it several times or use --sequence.shuffle to force ordering variation.
The ChatInterface timeout in the original report may well be a separate cause; these two share a file and a shape, that one does not.
Fourth instance, and a workaround that names the cause
OrgChart.roleLens.test.ts > never fetches or posts as a result of a lens selection failed in a combined run of src/components/workflow src/views/llc src/composables/llc src/components/llc (53 files / 534 tests), passed alone (9/9), and passed on an immediate re-run of the identical combined command (534/534).
That is now four different tests across three files — ChatInterface, OrgChart.people (twice), and now OrgChart.roleLens — all failing only under parallel file execution and all passing in isolation.
The diagnostic that matters:--no-file-parallelism makes the whole set stable. An independent run of the same suites hit the same intermittency and needed the same flag, naming CostDashboard.contract.test.ts, OrgChart.people.test.ts and ProjectBrowserView.findings.test.ts as its casualties.
Different files, different tests, same trigger. This is not four separate bugs — it is one piece of state shared across worker boundaries, and file parallelism decides who loses. Candidates, narrowing:
Module-scope state in an llc composable that survives between files in the same worker (useCompanyPeople, useLlcCompanyContext, and the org-chart composables all hold module-level refs).
A vi.mock factory returning a shared object rather than a per-file one.
A timer or unresolved promise from one file settling inside another's mount.
The lens test is a useful probe: it asserts "no fetch happens", so it fails when someone else's pending fetch resolves into its window. That points at (3) more strongly than (1).
Reproduction: run those four paths together repeatedly; it reproduces roughly one run in three. --no-file-parallelism suppresses it.
Do not "fix" this by adding --no-file-parallelism to the CI command. That hides a real leak and slows every run; the leak will still be there for anyone running a subset locally.
1. ChatInterface.test.ts > renders the main chat interface
A genuinely slow mount, not an unresolved promise. The file proves it
itself: every wait in it goes through waitFor(), which carries its own 1s
budget and fails with "unable to find element", not with a timeout. So a
10000ms failure has always meant the render was still running when the budget
expired — there is no promise left hanging to fix.
All 23 tests in the file render the full chat tree (sidebar, session list,
message list, composer, settings) with a Pinia store and a router. Two costs
were removed and one budget was set:
2. OrgChart.people.test.ts — passes alone, fails in the larger run
It is not another file interfering. It cannot be. Vitest resolves pool: "forks" with isolate: true (both defaults, neither overridden in vitest.config.ts), and an isolated task never shares a runner — the pool
throws "Isolated tasks should not share runners" on that path. Every test file
gets its own worker process and its own module registry, so a leaked mock or
shared module state from a sibling file has no route in. Bisecting the file set
would have found nothing.
The accumulation is inside the file, and it is real:
OrgChart.people.test.ts mounts the full OrgChart tree 31 times and
unmounts none of them.
The global beforeEach in the vitest setup file resets document.body.innerHTML. That detaches each tree's DOM but never unmounts
the app — component instances, watchers and effects stay live for the rest of
the file.
So the last tests in the file run with ~30 live trees still reacting to every
shared ref they touch. The file's own beforeEach sets i18n.global.locale.value = 'en' on a shared instance, which every one of
those live trees re-renders against.
The cost therefore grows as the file progresses, and whether a late test crosses
10s depends on how contended the runner is — which is exactly "passes alone,
fails in a 367-test run, passes again on a re-run".
This was already diagnosed once, for a single file: OrgChart.canvasFilters.test.ts carries a private teardown whose comment
measures the same accumulation at ~3.4s quiet vs. a timeout under load, and
notes that "no global enableAutoUnmount is configured". It was the only file
that had the teardown; every heavy file had the problem.
Fix:enableAutoUnmount(afterEach) in the global setup, so every mounted
tree is destroyed after the test that made it. The private copy in OrgChart.canvasFilters.test.ts is removed in favour of it.
Reproductions
cd autobot-frontend
npx vitest run src/components/__tests__/ChatInterface.test.ts
npx vitest run src/composables/llc src/components/llc src/views/llc
Stated honestly: the second is a contention reproduction, not a
deterministic one — it did not fail on every run when it was reported either.
The session that produced #14874 was not permitted to execute the suite, so the
verification behind that PR is CI on the branch, not a local before/after.
CI: green on the exact head sha 603a17b95 — 53 check-runs, 42 success / 11 skipped / 0 failure. Verified against the sha directly rather than the rollup, because auto-update-pr-branches pushed a base merge mid-run and GITHUB_TOKEN pushes are known to leave a refreshed head with no checks (ci: auto-update-pr-branches leaves refreshed PRs with ZERO checks — GITHUB_TOKEN pushes do not trigger workflows #12823). The trap did not bite here: the checks are genuinely on the merged head.
Review: independent code-reviewer pass. No blocking issues. It traced all 37 files for i18n mutation APIs, confirmed pool: "forks" + isolate: true are unmodified defaults, and verified the removed private teardown was an exact functional subset of the new global one.
Two real causes, neither of which was the timeout:
32 test files rebuilt a fresh createI18n carrying the ~400KB en bundle on every mount. 20 hoisted to module scope, 11 memoized per locale, 1 deliberately left per-call because two RTL cases mutate locale.value.
Nothing was ever unmounted — the global beforeEach reset document.body.innerHTML, which detaches the DOM but never unmounts the app, so watchers and effects stayed live for the rest of each file. Now enableAutoUnmount(afterEach) suite-wide.
Only one file-scope timeout was raised (ChatInterface.test.ts, 20s). vitest.config.ts stays at 10000.
Evidence: four consecutive runs of Unit & Integration Tests, all 259 files green, two of them the same commit re-run. On the slowest run the slowest unbudgeted test was 5500ms against the 10s ceiling; rtl.spec.ts went from a case timing out at 10043ms to an 18-test file in 3.2-4.8s.
Honest caveat: no speedup is claimed from run totals. Files touched moved 1.67x/0.78x vs base and files untouched moved 1.70x/0.76x — identical, so the contended singleton runner dominates and these runs cannot separate the change from load. What they show is no regression; the removed work is provable by reading.
Activity
mrveiss commentedon Aug 18, 2026
Third instance, and it narrows the hypothesis considerably.
OrgChart.people.test.ts > Team grouping is honest about the data that exists (#13938) > groups the user under the team that claims themfailed in a combined run ofsrc/components/workflow src/components/llc src/views/llc src/composables/llc(46 files / 466 tests), passed alone (29/29), and passed on an immediate re-run of the identical combined command.That is now two different tests in the same file — the earlier instance was
says the people could not be loaded, not that the company has none— both failing only in a larger run and both passing in isolation.Same file, different tests, order-dependent: this points at state shared across files that
OrgChart.people.test.tsreads, not at a slow mount or a bad assertion in either test. Likely candidates, in the order I would check them:llccomposable that another test file populates first (useCompanyPeople,useLlcCompanyContext, and the org-chart composables all hold module-scope state).vi.mockfactory whose returned object is shared rather than recreated per file.Reproduction for whoever picks this up: run the four paths above together; it does not reproduce every time, so run it several times or use
--sequence.shuffleto force ordering variation.The
ChatInterfacetimeout in the original report may well be a separate cause; these two share a file and a shape, that one does not.mrveiss commentedon Aug 18, 2026
Fourth instance, and a workaround that names the cause
OrgChart.roleLens.test.ts > never fetches or posts as a result of a lens selectionfailed in a combined run ofsrc/components/workflow src/views/llc src/composables/llc src/components/llc(53 files / 534 tests), passed alone (9/9), and passed on an immediate re-run of the identical combined command (534/534).That is now four different tests across three files —
ChatInterface,OrgChart.people(twice), and nowOrgChart.roleLens— all failing only under parallel file execution and all passing in isolation.The diagnostic that matters:
--no-file-parallelismmakes the whole set stable. An independent run of the same suites hit the same intermittency and needed the same flag, namingCostDashboard.contract.test.ts,OrgChart.people.test.tsandProjectBrowserView.findings.test.tsas its casualties.Different files, different tests, same trigger. This is not four separate bugs — it is one piece of state shared across worker boundaries, and file parallelism decides who loses. Candidates, narrowing:
llccomposable that survives between files in the same worker (useCompanyPeople,useLlcCompanyContext, and the org-chart composables all hold module-level refs).vi.mockfactory returning a shared object rather than a per-file one.The lens test is a useful probe: it asserts "no fetch happens", so it fails when someone else's pending fetch resolves into its window. That points at (3) more strongly than (1).
Reproduction: run those four paths together repeatedly; it reproduces roughly one run in three.
--no-file-parallelismsuppresses it.Do not "fix" this by adding
--no-file-parallelismto the CI command. That hides a real leak and slows every run; the leak will still be there for anyone running a subset locally.mrveiss commentedon Aug 23, 2026
Root cause, both halves — PR #14874
1.
ChatInterface.test.ts > renders the main chat interfaceA genuinely slow mount, not an unresolved promise. The file proves it
itself: every wait in it goes through
waitFor(), which carries its own 1sbudget and fails with "unable to find element", not with a timeout. So a
10000ms failure has always meant the render was still running when the budget
expired — there is no promise left hanging to fix.
All 23 tests in the file render the full chat tree (sidebar, session list,
message list, composer, settings) with a Pinia store and a router. Two costs
were removed and one budget was set:
renderComponent()in the shared test helper built a freshcreateI18ninstance on every render. Now one per module (test(frontend): mount helpers rebuild a ~400KB i18n instance on every mount #14860 is the same defect,
suite-wide).
vi.setConfig({ testTimeout: 20_000 })— per file, becausefrontend Unit & Integration Tests times out on a different test each run — 10s ceiling against 3x environment overhead #14842 recorded a different test failing each run, so naming one would be
pretending we know which. The global
testTimeoutstays at 10000 so agenuinely hung test elsewhere still fails fast.
2.
OrgChart.people.test.ts— passes alone, fails in the larger runIt is not another file interfering. It cannot be. Vitest resolves
pool: "forks"withisolate: true(both defaults, neither overridden invitest.config.ts), and an isolated task never shares a runner — the poolthrows "Isolated tasks should not share runners" on that path. Every test file
gets its own worker process and its own module registry, so a leaked mock or
shared module state from a sibling file has no route in. Bisecting the file set
would have found nothing.
The accumulation is inside the file, and it is real:
OrgChart.people.test.tsmounts the full OrgChart tree 31 times andunmounts none of them.
beforeEachin the vitest setup file resetsdocument.body.innerHTML. That detaches each tree's DOM but never unmountsthe app — component instances, watchers and effects stay live for the rest of
the file.
shared ref they touch. The file's own
beforeEachsetsi18n.global.locale.value = 'en'on a shared instance, which every one ofthose live trees re-renders against.
The cost therefore grows as the file progresses, and whether a late test crosses
10s depends on how contended the runner is — which is exactly "passes alone,
fails in a 367-test run, passes again on a re-run".
This was already diagnosed once, for a single file:
OrgChart.canvasFilters.test.tscarries a private teardown whose commentmeasures the same accumulation at ~3.4s quiet vs. a timeout under load, and
notes that "no global
enableAutoUnmountis configured". It was the only filethat had the teardown; every heavy file had the problem.
Fix:
enableAutoUnmount(afterEach)in the global setup, so every mountedtree is destroyed after the test that made it. The private copy in
OrgChart.canvasFilters.test.tsis removed in favour of it.Reproductions
Stated honestly: the second is a contention reproduction, not a
deterministic one — it did not fail on every run when it was reported either.
The session that produced #14874 was not permitted to execute the suite, so the
verification behind that PR is CI on the branch, not a local before/after.
fix(frontend): stop rebuilding i18n and leaking mounted trees in the …
mrveiss commentedon Aug 23, 2026
Delivered and merged.
d70a1f99e603a17b95— 53 check-runs, 42 success / 11 skipped / 0 failure. Verified against the sha directly rather than the rollup, becauseauto-update-pr-branchespushed a base merge mid-run andGITHUB_TOKENpushes are known to leave a refreshed head with no checks (ci: auto-update-pr-branches leaves refreshed PRs with ZERO checks — GITHUB_TOKEN pushes do not trigger workflows #12823). The trap did not bite here: the checks are genuinely on the merged head.code-reviewerpass. No blocking issues. It traced all 37 files for i18n mutation APIs, confirmedpool: "forks"+isolate: trueare unmodified defaults, and verified the removed private teardown was an exact functional subset of the new global one.Verified in base:
Two real causes, neither of which was the timeout:
createI18ncarrying the ~400KBenbundle on every mount. 20 hoisted to module scope, 11 memoized per locale, 1 deliberately left per-call because two RTL cases mutatelocale.value.beforeEachresetdocument.body.innerHTML, which detaches the DOM but never unmounts the app, so watchers and effects stayed live for the rest of each file. NowenableAutoUnmount(afterEach)suite-wide.Only one file-scope timeout was raised (
ChatInterface.test.ts, 20s).vitest.config.tsstays at 10000.Evidence: four consecutive runs of
Unit & Integration Tests, all 259 files green, two of them the same commit re-run. On the slowest run the slowest unbudgeted test was 5500ms against the 10s ceiling;rtl.spec.tswent from a case timing out at 10043ms to an 18-test file in 3.2-4.8s.Honest caveat: no speedup is claimed from run totals. Files touched moved 1.67x/0.78x vs base and files untouched moved 1.70x/0.76x — identical, so the contended singleton runner dominates and these runs cannot separate the change from load. What they show is no regression; the removed work is provable by reading.