Skip to content

fix(telemetry): default telemetry to opt-out with explicit consent UI - #1069

Draft
edelauna wants to merge 4 commits into
mainfrom
issue/830-1
Draft

fix(telemetry): default telemetry to opt-out with explicit consent UI#1069
edelauna wants to merge 4 commits into
mainfrom
issue/830-1

Conversation

@edelauna

@edelauna edelauna commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Related GitHub Issue

Refs: #830 (consent/privacy half only — Part A, event volume, is separate work in #835)

Description

Splits the consent and privacy fixes out of #835 into their own PR, so they can ship independent of the telemetry event-volume work in that PR.

Consent default. The banner used to send telemetrySetting: "enabled" when a user dismissed it with the close button, with no way to decline. This PR changes the semantics to a disclosed opt-out default: "unset" (no choice made) and "enabled" both keep telemetry on; only an explicit "disabled" turns it off. The banner's close button now sends no message at all, so dismissing it leaves the default in effect instead of recording an affirmative choice. isTelemetryOptedIn() in packages/types/src/telemetry.ts is the single source of truth for this logic — used by the banner, the About settings checkbox, and both the extension-host and webview PostHog clients.

Explicit consent UI. TelemetryBanner.tsx gets separate Accept and Decline actions instead of only a close button.

Live VS Code telemetry gating. extension.ts now listens to vscode.env.onDidChangeTelemetryEnabled and re-evaluates telemetry state immediately when the user flips VS Code's global telemetry toggle, ANDing it with the stored telemetrySetting. webviewMessageHandler.ts does the same for the webview's own PostHog client, and serializes concurrent telemetrySetting messages through a queue so a fast toggle can't have an earlier, slower write clobber a later one.

Docs and strings. PRIVACY.md now discloses the opt-out default and that the "does not collect code or prompts" claim applies to the PostHog path specifically. Banner and settings copy updated in all locales.

Visual regression coverage. Added a Playwright CT snapshot for TelemetryBanner (first visual test to exercise VSCodeButton) and fixed the CT harness's button mock, which rendered as a bare unstyled <button> with no VS Code styling — screenshots would have shown that instead of the real look. Styled button[appearance] in the CT-only theme CSS using the existing --vscode-button-* tokens; this fixes appearance for any future visual test using VSCodeButton, not just this one.

Explicitly out of scope (left for #835 or a follow-up): the circuit breaker for CODE_INDEX_ERROR retry storms, Task Completed aggregation of toolsUsed/messageCount, telemetry shutdown draining, and the modelCache.ts empty-response throttle. None of those files are touched here.

Test Procedure

  • pnpm check-types passes repo-wide.
  • 109 unit/integration tests pass across @roo-code/types, src, and webview-ui — the exact files touched (telemetry consent logic, banner, settings checkbox, extension activation/deactivation, webview message handling).
  • Lint clean on every touched file; src/eslint-suppressions.json only lost the now-stale __tests__/extension.spec.ts entry, no new suppressions added.
  • pnpm test:visual:docker passes deterministically for all 3 visual tests (2 pre-existing + the new TelemetryBanner baseline).
  • No changesets added, per this PR's scope guidance.

Pre-Submission Checklist

  • Issue Linked: This PR is linked to an approved GitHub Issue (see "Related GitHub Issue" above).
  • Scope: My changes are focused on the linked issue (one major feature/fix per PR).
  • Self-Review: I have performed a thorough self-review of my code.
  • Testing: New and/or updated tests have been added to cover my changes (if applicable).
  • Visual Snapshot (UI changes only): If a user would notice this change at a glance (layout, theme tokens, brand elements, empty/error states), I've added or updated a *.visual.tsx snapshot in webview-ui/. See webview-ui/AGENTS.md → "When a UI change needs a snapshot".
  • Documentation Impact: I have considered if my changes require documentation updates (see "Documentation Updates" section below).
  • Contribution Guidelines: I have read and agree to the Contributor Guidelines.

Visual Snapshots

Added webview-ui/src/components/common/__tests__/TelemetryBanner.visual.tsx with baseline __screenshots__/telemetry-banner-dark.png, showing the banner with its explicit Accept (primary) and Decline (secondary) buttons.

Documentation Updates

  • Yes, documentation updates are required. PRIVACY.md updated in this PR to disclose the opt-out default and clarify PostHog-specific scope.

Additional Notes

Split out of #835 per review — that PR mixed event-volume reduction with consent/privacy fixes across ~65 files. This PR contains only the consent/privacy hunks, branched clean from main.

Summary by CodeRabbit

  • New Features

    • Added clear Accept and Decline options for telemetry, while dismissing the notice without changing settings.
    • Telemetry now respects both the extension preference and VS Code’s global telemetry setting.
    • Settings and webview state stay synchronized when telemetry preferences change.
  • Documentation

    • Updated privacy disclosures in supported languages to identify installation-linked usage and error data and clarify that code and prompts are not collected.
  • Bug Fixes

    • Improved telemetry startup and shutdown reliability, including protection against conflicting preference updates.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d579922e-9411-4a68-a5ce-a22bd135cd79

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR centralizes telemetry opt-in logic, gates telemetry with VS Code’s global setting, serializes webview updates, adds explicit consent actions, propagates telemetry state, updates tests, and revises privacy disclosures and translations.

Changes

Telemetry control flow

Layer / File(s) Summary
Shared telemetry contract
packages/types/src/telemetry.ts, packages/types/src/vscode-extension-host.ts, webview-ui/src/utils/TelemetryClient.ts
Adds isTelemetryOptedIn, extends extension state with vscodeTelemetryEnabled, and gates telemetry initialization on both settings.
Extension synchronization
src/extension.ts, src/core/webview/ClineProvider.ts, src/__tests__/extension.spec.ts
Synchronizes stored telemetry settings with VS Code’s live telemetry toggle, updates webview state, and awaits telemetry shutdown during deactivation.
Webview update ordering
src/core/webview/webviewMessageHandler.ts, webview-ui/src/App.tsx, src/core/webview/__tests__/*, webview-ui/src/__tests__/TelemetryClient.spec.ts
Queues launch and setting updates, applies the global VS Code telemetry gate, and tests concurrent update and initialization races.
Consent UI and validation
webview-ui/src/components/common/*, webview-ui/src/components/settings/*, webview-ui/playwright/*
Adds explicit Accept and Decline actions, keeps close dismissal neutral, and adds unit and visual coverage.
Privacy and localized disclosures
PRIVACY.md, webview-ui/src/i18n/locales/*
Identifies PostHog, clarifies retention and installation-linked identifiers, and updates telemetry consent wording in supported locales.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • Zoo-Code-Org/Zoo-Code#526: Adds the Playwright visual regression infrastructure extended by the telemetry banner visual test.

Suggested labels: awaiting-review

Suggested reviewers: hannesrudolph, taltas

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main telemetry consent change and matches the disclosed opt-out behavior and explicit consent UI.
Description check ✅ Passed The description covers the issue, implementation, testing, scope, visual snapshot, and documentation updates, with only a minor checklist inconsistency.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue/830-1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (2)
src/core/webview/__tests__/webviewMessageHandler.spec.ts (1)

1889-1900: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider replacing the fixed microtask-flush count with an explicit signal.

The loop flushes exactly ten microtask turns as a fallback for the fixed code path. This count is a timing heuristic. If webviewDidLaunch later gains another await before it chains onto telemetrySettingQueue, the flush may finish too early and the test would pass or fail for the wrong reason.

The rest of this test already uses deferred-promise handshakes. An explicit signal from the queued update, or awaiting an exported queue handle, would remove the magic number.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/__tests__/webviewMessageHandler.spec.ts` around lines 1889 -
1900, Replace the fixed ten-turn microtask flush in the webviewDidLaunch test
with an explicit deferred-promise or queue-completion signal tied to the queued
telemetry update. Await that signal alongside snapshotTakenPromise so the test
synchronizes with the actual operation rather than relying on timing, preserving
the existing pre-fix snapshot behavior.
webview-ui/src/__tests__/TelemetryClient.spec.ts (1)

107-134: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consolidate TelemetryClient spec coverage into one file.

webview-ui/src/__tests__/TelemetryClient.spec.ts and webview-ui/src/utils/__tests__/TelemetryClient.spec.ts both cover TelemetryClient and updateTelemetryState. Move one set or merge the specs so this coverage does not drift between two files.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@webview-ui/src/__tests__/TelemetryClient.spec.ts` around lines 107 - 134,
Consolidate the duplicate TelemetryClient and updateTelemetryState test coverage
from webview-ui/src/__tests__/TelemetryClient.spec.ts and
webview-ui/src/utils/__tests__/TelemetryClient.spec.ts into a single spec file.
Move or merge the relevant cases, remove the redundant file or duplicated tests,
and preserve coverage for both telemetry-disabled and opted-in initialization
behavior.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@PRIVACY.md`:
- Around line 35-42: Update the Telemetry (Usage Data) section in PRIVACY.md to
state the active PostHog event-retention period or link to the authoritative
retention policy, replacing the vague “only as long as needed” wording. Verify
the stated period against the current PostHog plan or configuration before
merging.

In `@src/core/webview/webviewMessageHandler.ts`:
- Around line 659-666: Guard the queued telemetry update in the webview launch
handler with TelemetryService.hasInstance() before accessing
TelemetryService.instance. Preserve the existing telemetry-setting calculation
and update behavior when an instance exists, while skipping the update when it
does not.

In
`@webview-ui/src/components/common/__tests__/TelemetryBanner.visual.fixture.tsx`:
- Around line 19-20: Update the visual fixture’s i18next initialization so its
init promise is returned and supplied to the test setup, then await that promise
before calling mount for TelemetryBannerFixture. Ensure initialization completes
before Trans renders, without changing the existing translation configuration.

---

Nitpick comments:
In `@src/core/webview/__tests__/webviewMessageHandler.spec.ts`:
- Around line 1889-1900: Replace the fixed ten-turn microtask flush in the
webviewDidLaunch test with an explicit deferred-promise or queue-completion
signal tied to the queued telemetry update. Await that signal alongside
snapshotTakenPromise so the test synchronizes with the actual operation rather
than relying on timing, preserving the existing pre-fix snapshot behavior.

In `@webview-ui/src/__tests__/TelemetryClient.spec.ts`:
- Around line 107-134: Consolidate the duplicate TelemetryClient and
updateTelemetryState test coverage from
webview-ui/src/__tests__/TelemetryClient.spec.ts and
webview-ui/src/utils/__tests__/TelemetryClient.spec.ts into a single spec file.
Move or merge the relevant cases, remove the redundant file or duplicated tests,
and preserve coverage for both telemetry-disabled and opted-in initialization
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d0452b8c-95db-46c1-a02a-2b4d497d0cca

📥 Commits

Reviewing files that changed from the base of the PR and between cd29243 and 2375591.

⛔ Files ignored due to path filters (1)
  • webview-ui/src/components/common/__tests__/__screenshots__/telemetry-banner-dark.png is excluded by !**/*.png
📒 Files selected for processing (57)
  • PRIVACY.md
  • packages/types/src/__tests__/telemetry.isTelemetryOptedIn.test.ts
  • packages/types/src/telemetry.ts
  • packages/types/src/vscode-extension-host.ts
  • src/__tests__/extension.spec.ts
  • src/core/webview/ClineProvider.ts
  • src/core/webview/__tests__/telemetrySettingsTracking.spec.ts
  • src/core/webview/__tests__/webviewMessageHandler.spec.ts
  • src/core/webview/webviewMessageHandler.ts
  • src/eslint-suppressions.json
  • src/extension.ts
  • webview-ui/playwright/vscode-theme-dark.css
  • webview-ui/src/App.tsx
  • webview-ui/src/__tests__/TelemetryClient.spec.ts
  • webview-ui/src/components/common/TelemetryBanner.tsx
  • webview-ui/src/components/common/__tests__/TelemetryBanner.spec.tsx
  • webview-ui/src/components/common/__tests__/TelemetryBanner.visual.fixture.tsx
  • webview-ui/src/components/common/__tests__/TelemetryBanner.visual.tsx
  • webview-ui/src/components/settings/About.tsx
  • webview-ui/src/components/settings/__tests__/About.spec.tsx
  • webview-ui/src/i18n/locales/ca/settings.json
  • webview-ui/src/i18n/locales/ca/welcome.json
  • webview-ui/src/i18n/locales/de/settings.json
  • webview-ui/src/i18n/locales/de/welcome.json
  • webview-ui/src/i18n/locales/en/settings.json
  • webview-ui/src/i18n/locales/en/welcome.json
  • webview-ui/src/i18n/locales/es/settings.json
  • webview-ui/src/i18n/locales/es/welcome.json
  • webview-ui/src/i18n/locales/fr/settings.json
  • webview-ui/src/i18n/locales/fr/welcome.json
  • webview-ui/src/i18n/locales/hi/settings.json
  • webview-ui/src/i18n/locales/hi/welcome.json
  • webview-ui/src/i18n/locales/id/settings.json
  • webview-ui/src/i18n/locales/id/welcome.json
  • webview-ui/src/i18n/locales/it/settings.json
  • webview-ui/src/i18n/locales/it/welcome.json
  • webview-ui/src/i18n/locales/ja/settings.json
  • webview-ui/src/i18n/locales/ja/welcome.json
  • webview-ui/src/i18n/locales/ko/settings.json
  • webview-ui/src/i18n/locales/ko/welcome.json
  • webview-ui/src/i18n/locales/nl/settings.json
  • webview-ui/src/i18n/locales/nl/welcome.json
  • webview-ui/src/i18n/locales/pl/settings.json
  • webview-ui/src/i18n/locales/pl/welcome.json
  • webview-ui/src/i18n/locales/pt-BR/settings.json
  • webview-ui/src/i18n/locales/pt-BR/welcome.json
  • webview-ui/src/i18n/locales/ru/settings.json
  • webview-ui/src/i18n/locales/ru/welcome.json
  • webview-ui/src/i18n/locales/tr/settings.json
  • webview-ui/src/i18n/locales/tr/welcome.json
  • webview-ui/src/i18n/locales/vi/settings.json
  • webview-ui/src/i18n/locales/vi/welcome.json
  • webview-ui/src/i18n/locales/zh-CN/settings.json
  • webview-ui/src/i18n/locales/zh-CN/welcome.json
  • webview-ui/src/i18n/locales/zh-TW/settings.json
  • webview-ui/src/i18n/locales/zh-TW/welcome.json
  • webview-ui/src/utils/TelemetryClient.ts
💤 Files with no reviewable changes (1)
  • src/eslint-suppressions.json

Comment thread PRIVACY.md
Comment on lines +659 to +666
telemetrySettingQueue = telemetrySettingQueue
.catch(() => undefined)
.then(async () => {
const telemetrySetting = getGlobalState("telemetrySetting") || "unset"
TelemetryService.instance.updateTelemetryState(
isTelemetryOptedIn(telemetrySetting) && vscode.env.isTelemetryEnabled,
)
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard this queued telemetry update with TelemetryService.hasInstance().

The "telemetrySetting" case at Line 2603 checks TelemetryService.hasInstance() before it touches TelemetryService.instance. This call site does not. If no instance exists when webviewDidLaunch arrives, TelemetryService.instance throws inside the queued callback. The handler does not await this chain and attaches no terminal catch, so the throw becomes an unhandled promise rejection instead of a logged error.

A guard fixes both problems and makes the two call sites consistent.

🛡️ Proposed guard
 			telemetrySettingQueue = telemetrySettingQueue
 				.catch(() => undefined)
 				.then(async () => {
+					if (!TelemetryService.hasInstance()) {
+						return
+					}
+
 					const telemetrySetting = getGlobalState("telemetrySetting") || "unset"
 					TelemetryService.instance.updateTelemetryState(
 						isTelemetryOptedIn(telemetrySetting) && vscode.env.isTelemetryEnabled,
 					)
 				})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
telemetrySettingQueue = telemetrySettingQueue
.catch(() => undefined)
.then(async () => {
const telemetrySetting = getGlobalState("telemetrySetting") || "unset"
TelemetryService.instance.updateTelemetryState(
isTelemetryOptedIn(telemetrySetting) && vscode.env.isTelemetryEnabled,
)
})
telemetrySettingQueue = telemetrySettingQueue
.catch(() => undefined)
.then(async () => {
if (!TelemetryService.hasInstance()) {
return
}
const telemetrySetting = getGlobalState("telemetrySetting") || "unset"
TelemetryService.instance.updateTelemetryState(
isTelemetryOptedIn(telemetrySetting) && vscode.env.isTelemetryEnabled,
)
})
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/core/webview/webviewMessageHandler.ts` around lines 659 - 666, Guard the
queued telemetry update in the webview launch handler with
TelemetryService.hasInstance() before accessing TelemetryService.instance.
Preserve the existing telemetry-setting calculation and update behavior when an
instance exists, while skipping the update when it does not.

Comment on lines +19 to +20
const visualTestI18n = i18next.createInstance()
void visualTestI18n.use(initReactI18next).init({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/sh
set -eu
rg -n -C 5 'TelemetryBannerFixture|visualTestI18n|createInstance|\.init\(' \
  webview-ui/src/components/common/__tests__

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 4189


🏁 Script executed:

#!/bin/sh
set -eu

printf '\n--- relevant test files ---\n'
sed -n '1,120p' webview-ui/src/components/common/__tests__/TelemetryBanner.visual.fixture.tsx
printf '\n--- TelemetryBanner source references ---\n'
rg -n -C 8 'helpImproveMessage|settingsLink|settingsUrl|Trans|useTranslation|TranslationContext' webview-ui/src/components/common -S

printf '\n--- package deps for i18next versions ---\n'
fd -a 'package.json|pnpm-lock.yaml|package-lock.json|yarn.lock' . | sed 's#^\./##' | while read -r f; do
  if rg -q '"i18next"|"react-i18next"|"initReactI18next"' "$f" || rg -q 'i18next@|react-i18next@' "$f"; then
    echo "### $f"
    rg -n '"i18next"|"react-i18next"|i18next@|react-i18next@' "$f" | head -n 20
  fi
done

printf '\n--- imports in fixture/test ---\n'
sed -n '1,180p' webview-ui/src/components/common/__tests__/TelemetryBanner.visual.tsx

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 38219


🌐 Web query:

i18next 25 init returns Promise synchronous resources documentation

💡 Result:

In i18next, the i18next.init method consistently returns a Promise, regardless of whether you are using synchronous or asynchronous resource loading [1][2]. When using a backend that supports synchronous (blocking) loading, such as i18next-fs-backend, you can configure initialization to load resources synchronously by setting the initAsync option to false (in older versions, this option was referred to as initImmediate) [3][4]. Key technical details regarding this behavior: 1. Consistency: The init method always returns a Promise [1][2]. Even when initAsync is set to false, the return type of the function remains a Promise [3][4]. 2. Synchronous Configuration: Setting initAsync: false is intended for environments where the backend performs blocking, synchronous I/O [3]. This ensures that translation resources are available immediately after the init call returns, allowing you to use i18next.t without waiting for the Promise to resolve or using the initialization callback [3][4]. 3. TypeScript and Linting: Because init always returns a Promise, using it in a synchronous context may trigger "floating promise" warnings in ESLint configurations (e.g., @typescript-eslint/no-floating-promises) [4]. To handle this, you can explicitly mark the promise as ignored (e.g., using the void operator) or ensure your code structure respects the asynchronous nature of the initialization process [4]. In summary, while you can force the resource loading process to be synchronous via backend configuration, the i18next.init API signature itself does not change to a non-promise-returning function [3][2].

Citations:


🌐 Web query:

react-i18next 15 Trans I18nextProvider documentation initialization

💡 Result:

In react-i18next v15, initialization and usage patterns for I18nextProvider and the Trans component remain consistent with established practices, as the v15 release primarily focused on internal build optimizations, dependency updates, and modernization of syntax [1][2]. Initialization and Setup To initialize react-i18next, you must use the initReactI18next plugin to connect i18next with your React application [3][4]. import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; i18n.use(initReactI18next).init({ // configuration options }); I18nextProvider The I18nextProvider is a context provider used to pass an i18next instance down to the component tree [5]. It is generally only required if your application needs to support multiple i18next instances simultaneously (e.g., in a component library) or for specific server-side rendering (SSR) configurations [5]. import { I18nextProvider } from 'react-i18next'; Trans Component The Trans component is designed for translating JSX trees while preserving structure, such as components or formatting within a string [6][7]. It automatically retrieves the translation function from the context (provided by I18nextProvider or the global instance) [6][8]. Welcome to React If you are working with React 15 or older, you must set the defaultTransParent option globally or pass the parent prop to the Trans component to ensure correct rendering [6][8]. In modern React environments (v16+), this is handled automatically [6]. Trans also supports various props such as i18nKey, values, and components for advanced interpolation [6][8].

Citations:


🏁 Script executed:

#!/bin/sh
set -eu

printf '\n--- inspect locked i18next init source snippets from pnpm store ---\n'

lockdir=$(mktemp -d)
cd "$lockdir"

printf '%s\n' 'i18next@25.2.1' 'react-i18next@15.5.1' > import-map.txt
cat import-map.txt

printf '%s\n' '--- pnpm-store location candidates ---'
home="${HOME:-/root}"
for d in "$home/.local/share/pnpm/store/v3" "$home/.pnpm-store/v3" "$PWD/pnpm-store"; do
  if [ -d "$d" ]; then
    echo "FOUND $d"
  fi
done

printf '%s\n' '--- fetch package tarballs and inspect init/Trans source ---'
tmp=$(mktemp -d)
cd "$tmp"

for pkg in i18next/react-i18next; do
  case $pkg in
    i18next) ver=25.2.1 ;;
    react-i18next) ver=15.5.1 ;;
  esac
  tarball="$(npm view "$pkg@$ver" dist.tarball 2>/dev/null)"
  echo "### $pkg@$ver tarball: $tarball"
  if [ -n "$tarball" ]; then
    mkdir -p "pkg-$pkg"
    curl -fsSL "$tarball" -o "pkg-$pkg.tgz"
    tar -xzf "pkg-$pkg.tgz" -C "pkg-$pkg"
    echo "--- $pkg package files ---"
    find "pkg-$pkg" -maxdepth 3 -type f \( -name '*.js' -o -name '*.ts' -o -name '*.mjs' \) | sort | head -n 40
    echo "--- symbols in package ---"
    rg -n 'function init|init\\(|initAsync|initImmediate|react-i18next|I18nextContext|TransContext|i18n\\.use|i18n\\.setDefaults|i18n\\.loadNamespace|i18n\\.defaultNS' "pkg-$pkg" || true
  else
    echo "npm tarball unavailable"
  fi
done

printf '%s\n' '--- inspect likely init implementations ---'
for f in $(find "pkg-i18next" "pkg-react-i18next" -type f \( -name '*.js' -o -name '*.ts' -o -name '*.mjs' \) 2>/dev/null |  rg 'lib/index|src/index|cjs|esm|trans|react' | head -n 80); do
  if rg -q 'init\\(|initAsync|initImmediate|use.*init' "$f"; then
    echo "### $f"
    rg -n -C 8 'function init|const init|\.init|initAsync|initImmediate|init\\(' "$f" | head -n 220
  fi
done

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 401


🏁 Script executed:

#!/bin/sh
set -eu

tmp=$(mktemp -d)
cd "$tmp"

packages="i18next@25.2.1 react-i18next@15.5.1"
for pkg_name in i18next react-i18next; do
  case $pkg_name in
    i18next) ver=25.2.1 ;;
    react-i18next) ver=15.5.1 ;;
  esac
  tarball="$(npm view "$pkg_name@$ver" dist.tarball 2>/dev/null)"
  echo "### $pkg_name@$ver"
  echo "tarball: $tarball"
  mkdir -p "pkg-$pkg_name"
  curl -fsSL "$tarball" -o "pkg-$pkg_name.tgz"
  tar -xzf "pkg-$pkg_name.tgz" -C "pkg-$pkg_name"
done

printf '\n--- init-related declarations in i18next ---\n'
rg -n -C 6 'function init|const init|init\\(|initAsync|initImmediate|return.*Promise|loadNamespaces|isInitialized' pkg-i18next package package.json dist || true

printf '\n--- Trans-related declarations in react-i18next ---\n'
rg -n -C 8 'function Trans|const Trans|TransContext|useTranslation|initReactI18next|useIsomorphicLayoutEffect|useEffect' pkg-react-i18next package package.json dist || true

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 50378


🏁 Script executed:

#!/bin/sh
set -eu

tmp=$(mktemp -d)
cd "$tmp"

pkg="pkg-i18next"
tarball="$(npm view i18next@25.2.1 dist.tarball 2>/dev/null)"
mkdir -p "$pkg"
curl -fsSL "$tarball" -o "$pkg.tgz"
tar -xzf "$pkg.tgz" -C "$pkg"

printf '\n--- init implementation ---\n'
rg -n -C 12 'function init|const init|initAsync|initImmediate|isInitialized|isLanguageChangingTo|loadNamespaces|deferred|return' "$pkg/package" "$pkg/dist" "$pkg/src" 2>/dev/null | head -n 260

printf '\n--- relevant source files ---\n'
fd . "$pkg/package" "$pkg/dist" "$pkg/src" -t f | sed 's#^\./##' | head -n 120

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 18186


🏁 Script executed:

#!/bin/sh
set -eu

tmp=$(mktemp -d)
cd "$tmp"
mkdir -p i18next
npm view i18next@25.2.1 dist.tarball >/tmp/tarball.txt 2>/dev/null || true
tarball="$(sed -n '1p' /tmp/tarball.txt)"
curl -fsSL "$tarball" -o i18next.tgz
tar -xzf i18next.tgz -C i18next

files="$(find i18next/package -name '*.js' -path '*/package/*' -printf '%p\n' | sort)"
for f in $files; do
  if rg -q 'initAsync|initImmediate|function init\\(|const init|loadNamespace\\(|loadNamespaces\\(' "$f"; then
    echo "### $f"
    rg -n -C 16 'function init\\(|const init|isInitialized|isInitializing|initAsync|initImmediate|loadNamespaces|return Promise|deferred' "$f" | head -n 260
  fi
done

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 1887


🏁 Script executed:

#!/bin/sh
set -eu

tmp=$(mktemp -d)
cd "$tmp"
mkdir -p i18next
npm view i18next@25.2.1 dist.tarball >/tmp/tarball.txt 2>/dev/null || true
tarball="$(sed -n '1p' /tmp/tarball.txt)"
curl -fsSL "$tarball" -o i18next.tgz
tar -xzf i18next.tgz -C i18next

for f in $(find i18next/package -name '*.js' | sort); do
  if grep -E 'initAsync|initImmediate|init\(|isInitialized|isInitializing|loadNamespaces|loadLanguage|deferred|loadNamespace' "$f" >/dev/null; then
    echo "### $f"
    grep -E -n -C 16 'init\(|isInitialized|isInitializing|initAsync|initImmediate|loadNamespaces|loadLanguage|deferred|loadNamespace' "$f" | head -n 280
  fi
done

Repository: Zoo-Code-Org/Zoo-Code

Length of output: 50378


Await i18next initialization before mount returns.

init(...) returns a promise, and mount(<TelemetryBannerFixture />) awaits the fixture component rather than initialization. Since this bundle does not set initAsync: false, Trans can read before the welcome namespace is loaded and render fallback/missing text. Return the init promise from the fixture, pass it to the test, and await it before mount.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@webview-ui/src/components/common/__tests__/TelemetryBanner.visual.fixture.tsx`
around lines 19 - 20, Update the visual fixture’s i18next initialization so its
init promise is returned and supplied to the test setup, then await that promise
before calling mount for TelemetryBannerFixture. Ensure initialization completes
before Trans renders, without changing the existing translation configuration.

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.90909% with 3 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/core/webview/webviewMessageHandler.ts 86.95% 2 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant