Skip to content

Fix workspace watchers with Parcel watcher#1769

Open
zerob13 wants to merge 1 commit into
devfrom
codex/parcel-watcher-issue-1764
Open

Fix workspace watchers with Parcel watcher#1769
zerob13 wants to merge 1 commit into
devfrom
codex/parcel-watcher-issue-1764

Conversation

@zerob13

@zerob13 zerob13 commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #1764.

  • Replace the chokidar-backed workspace and skill hot-reload watchers with a shared @parcel/watcher service.
  • Add Electron utility-process watcher hosts for content and git metadata, with a main-process watcher pool that handles deduplication, ref-counting, filtering, and event fan-out.
  • Add debounced batching, overflow protection, snapshot polling fallback, host restart replay, and workspace watcher health status events.
  • Keep git metadata watching isolated in a separate host and preserve raw git event ordering for transient lockfile updates.
  • Add a compact WorkspacePanel degraded/failed watcher status banner and localized strings.
  • Update ASAR/native package handling so Parcel watcher prebuilds are copied into packaged apps.

Technical Design

  • WatcherService is the feature-facing facade. Presenters subscribe through WatchRequest objects and do not import native watcher code.
  • WatcherHostClient owns utility-process lifecycle, RPC correlation, restart status, and request replay.
  • WatcherPool deduplicates equivalent subscriptions, normalizes macOS /private/var paths, applies include/exclude filters, and fans batches out to subscribers.
  • watcherHost runs inside the utility process, owns @parcel/watcher subscriptions, coalesces regular content events, chunks large batches, and switches to snapshot polling when native watching fails or overflows.
  • The git host uses its own utility-process channel and keeps lockfile and index events observable so workspace git metadata invalidation remains responsive.

Validation

  • pnpm run format
  • pnpm run i18n
  • pnpm run lint
  • pnpm run typecheck
  • pnpm test (388 files passed, 3222 tests passed; existing skipped suites unchanged)
  • pnpm run build
  • pnpm exec playwright test -c test/e2e/playwright.config.ts test/e2e/specs/30-workspace-watcher-events.smoke.spec.ts

Summary by CodeRabbit

Release Notes

  • New Features

    • Replaced file watching system with improved native watcher for better performance and stability.
    • Added watch status indicators showing when file watching is degraded or unavailable.
    • Implemented automatic fallback to polling when native watching fails.
  • Bug Fixes

    • Resolved file descriptor exhaustion issues on macOS.
  • Documentation

    • Added implementation plans and specifications for the updated file watcher architecture.
  • Tests

    • Added comprehensive watcher functionality tests and end-to-end verification.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Replaces chokidar-based file watching with @parcel/watcher in a utility-process architecture. A new FileWatcherHost runs inside an Electron utilityProcess, fronted by WatcherHostClient, WatcherPool, and FileWatcherService. Both WorkspacePresenter and SkillPresenter are migrated to the new async IFileWatcherService API. A typed workspace.watch.status.changed event surfaces degraded/failed watch health to the renderer's WorkspacePanel, with i18n strings across 20 locales and packaging support for native binaries.

Changes

@parcel/watcher Utility-Process Watcher Migration

Layer / File(s) Summary
Spec, plan, and task docs
docs/issues/parcel-watcher-issue-1764/*
Adds spec, implementation plan, and task checklist covering architecture, requirements, acceptance criteria, packaging risks, and test scope for the migration.
Shared types, domain schemas, and event contracts
src/main/lib/fileWatcher/watcherTypes.ts, src/shared/contracts/domainSchemas.ts, src/shared/contracts/events/workspace.events.ts, src/shared/contracts/events.ts, src/shared/types/presenters/workspace.d.ts, src/shared/types/presenters/index.d.ts, src/shared/types/skill.ts
Defines all watcher type primitives (WatcherHostKind, WatchMode, WatchHealth, WatchRequest, WatcherEventBatch, WatchHandle, IFileWatcherService, RPC protocol types), Zod schemas for watch health/mode/reason, the workspaceWatchStatusChangedEvent contract registered in DEEPCHAT_EVENT_CATALOG, and updated ISkillPresenter async signatures.
Build config and native binary packaging
package.json, electron.vite.config.ts, electron-builder.yml, scripts/afterPack.js
Adds @parcel/watcher@^2.5.6 dependency, registers fileWatcherUtilityHost as a Rollup entry, adds asarUnpack glob patterns, and adds copyParcelWatcherNativePackages to copy platform-specific native binaries into unpacked app resources.
FileWatcherHost and IPC utility-process entry
src/main/lib/fileWatcher/watcherHost.ts, src/main/lib/fileWatcher/fileWatcherUtilityHost.ts, src/main/fileWatcherUtilityHostEntry.ts
FileWatcherHost subscribes to @parcel/watcher, buffers/coalesces events, flushes chunked batches, handles native errors by switching to snapshot-polling fallback, and emits typed health/status messages. fileWatcherUtilityHost.ts is the IPC bridge that dispatches RPC requests to the host. The entry module guards against starting outside a utility process.
Event coalescing
src/main/lib/fileWatcher/eventCoalescer.ts
Implements coalesceWatcherEvents with platform-aware path key normalization, per-path event merging (delete→create→update; create→delete cancels), and descendant delete deduplication.
WatcherHostClient, WatcherPool, and FileWatcherService
src/main/lib/fileWatcher/watcherHostClient.ts, src/main/lib/fileWatcher/watcherPool.ts, src/main/lib/fileWatcher/watcherService.ts, src/main/lib/fileWatcher/index.ts
WatcherHostClient spawns the utility process, manages RPC, and implements restart-with-replay on unexpected exit. WatcherPool deduplicates requests by key, filters batches by include/exclude rules, and fans out events to multiple listeners over two clients (content and git). FileWatcherService provides a singleton facade with createWatcherRequestId.
WorkspacePresenter migration
src/main/presenter/workspacePresenter/index.ts, src/main/presenter/index.ts
Replaces chokidar FSWatcher instances with WatchHandle from watcherService.watch; rewrites content-batch handling for full/fs/git invalidations; adds git lock-path watching; adds emitWatchStatus; converts destroy() to async; injects watcherService via constructor.
SkillPresenter migration
src/main/presenter/skillPresenter/index.ts
Replaces chokidar with async watchSkillFiles()/stopWatching() backed by watcherService.watch; adds batch handling for create/update/delete/overflow; implements metadataCache/contentCache handlers with duplicate-name conflict resolution; converts destroy() to async.
Renderer watch-status surface
src/renderer/api/WorkspaceClient.ts, src/renderer/src/components/sidepanel/composables/useWorkspaceSync.ts, src/renderer/src/components/sidepanel/WorkspacePanel.vue, src/renderer/src/i18n/*/chat.json
Adds onWatchStatusChanged to WorkspaceClient; adds watchStatus reactive state and lifecycle wiring in useWorkspaceSync; adds a conditional watchStatusBanner computed and UI element in WorkspacePanel.vue; adds degraded/failed translation strings across 20 locales.
Tests
test/main/lib/fileWatcher/*.test.ts, test/main/presenter/*.test.ts, test/main/routes/contracts.test.ts, test/main/scripts/afterPack.test.ts, test/renderer/components/WorkspacePanel.test.ts, test/e2e/specs/30-workspace-watcher-events.smoke.spec.ts
Adds tests for event coalescing, WatcherPool deduplication/filtering/status routing, Parcel binary copy in afterPack, updated event catalog, refactored presenter tests via FakeWatcherService replacing chokidar mocks, updated WorkspacePanel renderer tests for watch-status UI, and a new e2e smoke test for watcher healthy/native status and invalidation events.

Sequence Diagram(s)

sequenceDiagram
  participant WorkspacePresenter
  participant FileWatcherService
  participant WatcherPool
  participant WatcherHostClient
  participant UtilityProcess as FileWatcherHost (utility process)
  participant Renderer as WorkspacePanel (renderer)

  WorkspacePresenter->>FileWatcherService: watch(request, onBatch, onStatus)
  FileWatcherService->>WatcherPool: watch(request, onBatch, onStatus)
  WatcherPool->>WatcherHostClient: watch(request) via IPC RPC
  WatcherHostClient->>UtilityProcess: fork + file-watcher:request watch
  UtilityProcess-->>WatcherHostClient: file-watcher:status healthy/native
  WatcherHostClient-->>WorkspacePresenter: onStatus(healthy)
  WorkspacePresenter->>Renderer: sendToAllWindows workspace.watch.status.changed

  UtilityProcess-->>WatcherHostClient: file-watcher:event-batch
  WatcherHostClient-->>WatcherPool: onBatch(batch)
  WatcherPool-->>WorkspacePresenter: filtered onBatch(batch)
  WorkspacePresenter->>WorkspacePresenter: schedule invalidation (fs/git/full)
  WorkspacePresenter->>Renderer: sendToAllWindows workspace.invalidated

  UtilityProcess--xWatcherHostClient: unexpected exit
  WatcherHostClient->>WatcherHostClient: handleHostExit → emitDegraded + scheduleRestart
  WatcherHostClient-->>WorkspacePresenter: onStatus(degraded/utility-exit)
  WorkspacePresenter->>Renderer: sendToAllWindows workspace.watch.status.changed (degraded)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • ThinkInAIXYZ/deepchat#1352: Previously modified WorkspacePresenter and workspace invalidation/event wiring, which this PR directly rewrites as part of the chokidar→watcherService migration.
  • ThinkInAIXYZ/deepchat#1334: Modified the WorkspacePresenter constructor signature, which this PR further extends to inject the new watcherService dependency.

Poem

🐇 No more EMFILE storms at dawn,
A thousand fds — thankfully gone!
@parcel/watcher takes the stage,
Utility process, its own cage.
Coalesced events flow smooth and bright,
This bunny watches files just right! 🌟

✨ 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 codex/parcel-watcher-issue-1764

@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: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/renderer/src/components/sidepanel/composables/useWorkspaceSync.ts (1)

421-427: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Register watch-status listeners before immediate watcher startup.

Line [397] runs ensureWatcherState(...) immediately, but the new watch-status listener is attached at Line [425]. Early workspace.watch.status.changed events can be missed, so initial degraded/failed state may never surface in the panel.

Suggested fix
-import { computed, onBeforeUnmount, onMounted, ref, watch, type ComputedRef, type Ref } from 'vue'
+import { computed, onBeforeUnmount, ref, watch, type ComputedRef, type Ref } from 'vue'

-  onMounted(() => {
-    stopWorkspaceInvalidatedListener = options.workspaceClient.onInvalidated(
-      handleWorkspaceInvalidated
-    )
-    stopWorkspaceWatchStatusListener = options.workspaceClient.onWatchStatusChanged(
-      handleWorkspaceWatchStatusChanged
-    )
-  })
+  stopWorkspaceInvalidatedListener = options.workspaceClient.onInvalidated(
+    handleWorkspaceInvalidated
+  )
+  stopWorkspaceWatchStatusListener = options.workspaceClient.onWatchStatusChanged(
+    handleWorkspaceWatchStatusChanged
+  )
🤖 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/renderer/src/components/sidepanel/composables/useWorkspaceSync.ts` around
lines 421 - 427, The watch-status listener registration is happening too late in
the initialization sequence. In the onMounted hook in useWorkspaceSync.ts, the
stopWorkspaceWatchStatusListener assignment (which registers the handler for
workspace watch-status changes) needs to be moved to occur before the
ensureWatcherState function is called, as ensureWatcherState triggers the
watcher immediately and could emit status-change events before the listener is
attached, causing initial degraded or failed states to be missed by the panel.
src/renderer/src/i18n/es-ES/chat.json (1)

1-1: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Translate watchStatus strings into native languages.

All five non-English locale files contain English placeholder text for the new workspace.files.watchStatus keys instead of native-language translations:

  • src/renderer/src/i18n/es-ES/chat.json#L264-267: translate the degraded and failed strings into Spanish.
  • src/renderer/src/i18n/fa-IR/chat.json#L214-217: translate the degraded and failed strings into Farsi/Persian.
  • src/renderer/src/i18n/fr-FR/chat.json#L214-217: translate the degraded and failed strings into French.
  • src/renderer/src/i18n/he-IL/chat.json#L214-217: translate the degraded and failed strings into Hebrew.
  • src/renderer/src/i18n/id-ID/chat.json#L264-267: translate the degraded and failed strings into Indonesian.
🤖 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/renderer/src/i18n/es-ES/chat.json` at line 1, Replace the English
placeholder text for the workspace.files.watchStatus keys in the five
non-English locale files with native-language translations. In
src/renderer/src/i18n/es-ES/chat.json (lines 264-267), translate the degraded
and failed strings into Spanish. In src/renderer/src/i18n/fa-IR/chat.json (lines
214-217), translate the degraded and failed strings into Farsi/Persian. In
src/renderer/src/i18n/fr-FR/chat.json (lines 214-217), translate the degraded
and failed strings into French. In src/renderer/src/i18n/he-IL/chat.json (lines
214-217), translate the degraded and failed strings into Hebrew. In
src/renderer/src/i18n/id-ID/chat.json (lines 264-267), translate the degraded
and failed strings into Indonesian.

Source: Coding guidelines

src/shared/types/presenters/workspace.d.ts (1)

97-102: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Keep WorkspaceInvalidationEvent.version required to match the contract.

Line 101 makes version optional, but workspace.invalidated defines version as required. This weakens type safety across the shared event boundary.

Suggested fix
 export type WorkspaceInvalidationEvent = {
   workspacePath: string
   kind: WorkspaceInvalidationKind
   source: WorkspaceInvalidationSource
-  version?: number
+  version: 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/shared/types/presenters/workspace.d.ts` around lines 97 - 102, The
WorkspaceInvalidationEvent type marks the version field as optional with the ?
modifier, but the workspace.invalidated contract requires version to be a
non-optional required field. Remove the ? from the version property declaration
in the WorkspaceInvalidationEvent type definition to make it a required field
matching the actual contract requirement and restore type safety at the shared
event boundary.
🤖 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 `@docs/issues/parcel-watcher-issue-1764/plan.md`:
- Around line 210-227: The fallback modes section documents
`git-metadata-polling` as a polling strategy, but this mode is missing from the
earlier `WatcherMode` union type definition and the status-event example that
shows the `mode` field values as `native | snapshot-polling | lifecycle`. To fix
this inconsistency, either add `git-metadata-polling` to the `WatcherMode` union
and update the status-event example to include `git-metadata-polling` as a valid
mode value, or remove `git-metadata-polling` from the Fallback modes section if
it is intended to be internal only. Whichever approach you choose, ensure that
the `WatcherMode` contract, the status-event example, and the documented
fallback modes all describe the same set of possible modes.

In `@docs/issues/parcel-watcher-issue-1764/spec.md`:
- Line 138: Remove the phrase "currently the latest npm release" from line 138
in the spec.md file. Keep only the part about the recommended dependency version
with the semantic versioning constraint "`@parcel/watcher`@^2.5.6". This
eliminates time-bound language that will become outdated when newer versions are
released, while maintaining the useful version specification information.

In `@docs/issues/parcel-watcher-issue-1764/tasks.md`:
- Around line 17-23: The verification checklist includes `pnpm run typecheck`
but the acceptance gate specification requires `pnpm run typecheck:node`. Update
the task list to use the correct command `pnpm run typecheck:node` that matches
the spec, or if both commands are required, add a note clarifying that both
should be executed by contributors.

In `@src/main/lib/fileWatcher/eventCoalescer.ts`:
- Around line 57-69: The code is using path.normalize on lines 59 and 66 to
normalize paths in the deletedParents array and when checking descendants, but
this is weaker than the normalizeEventKey normalization used elsewhere. Replace
both occurrences of path.normalize(event.path) in the deletedParents mapping and
the normalized variable assignment in the filter function with
normalizeEventKey(event.path) to ensure consistent and stronger path
normalization that prevents missing equivalent paths and leaking redundant
delete events.

In `@src/main/lib/fileWatcher/watcherHost.ts`:
- Around line 166-179: After detecting and reporting the terminal `root-deleted`
state in the root path existence check (where fs.existsSync fails for
activeWatch.request.rootPath), prevent any future polling from being scheduled
for this activeWatch. Identify the polling scheduling mechanism (likely a
timeout or interval setup) and ensure it is cancelled or prevented from being
re-scheduled once the `root-deleted` condition is detected and the status is set
to failed. This issue appears at two locations: the primary check at lines
166-179 and a secondary occurrence at lines 215-218; both sites need the same
fix to prevent polling from continuing after the terminal failure state is
reached.

In `@src/main/lib/fileWatcher/watcherHostClient.ts`:
- Around line 91-115: The request method does not have a timeout mechanism,
which means if the utility host becomes unresponsive and never replies, the
promise will hang indefinitely and leave entries in the pendingRequests Map
unresolved. Add a timeout to the promise created in the request method that
rejects after a reasonable duration (typically a few seconds). When the timeout
fires, delete the corresponding entry from pendingRequests using the id as the
key and reject the promise with an appropriate timeout error. This ensures that
hung RPC calls are cleaned up and do not block watch, unwatch, or shutdown
operations.

In `@src/main/lib/fileWatcher/watcherPool.ts`:
- Around line 108-130: When the watch promise in `entry.ready` rejects, the
entry remains in both the `entriesByKey` and `entriesByWatchId` maps, causing
future callers with the same key to inherit the failed promise and keep failing.
Add error handling around the `await entry.ready` statement to catch rejection
and clean up by removing the poisoned entry from both `entriesByKey` and
`entriesByWatchId` maps before re-throwing the error, allowing subsequent
requests with the same key to create a fresh entry and retry.

In `@src/main/lib/fileWatcher/watcherService.ts`:
- Around line 34-35: In the resetFileWatcherServiceForTests function, before
setting sharedWatcherService to null, you need to properly clean up the existing
watcher service instance to prevent watcher clients and processes from remaining
alive across tests. Add logic to check if sharedWatcherService exists and call
its cleanup method (such as destroy() or close()) before assigning null to
sharedWatcherService.

In `@src/main/presenter/skillPresenter/index.ts`:
- Around line 1955-1961: The code in this section calls discoverSkills() which
already publishes the skills.catalog.changed event internally, and then
immediately publishes the same event again at lines 1956-1960 with duplicate
data. Remove the redundant publishDeepchatEvent call for skills.catalog.changed
since the discoverSkills() method already handles this publication. Keep the
const skills assignment and the return statement.

In `@src/main/presenter/workspacePresenter/index.ts`:
- Around line 230-233: The fire-and-forget calls to
`this.refreshGitWatcher(runtime)` at lines 232 and 242 lack rejection handlers,
which can cause unhandled promise rejections. Add a `.catch()` handler to both
invocations of `refreshGitWatcher(runtime)` to properly handle any promise
rejections, ensuring errors are either logged or handled gracefully rather than
becoming unhandled rejections that could destabilize the runtime.
- Around line 168-176: The runtime is being added to the watchRuntimes map at
the beginning of the async setup sequence, before createContentWatcher and
refreshGitWatcher complete. If either of these async operations throws an error,
the runtime remains in the map but with incomplete or failed initialization,
causing subsequent watchWorkspace calls to find it in the map and only increment
refCount instead of retrying setup. Move the this.watchRuntimes.set(normalized,
runtime) call to execute only after both createContentWatcher and
refreshGitWatcher have completed successfully, ensuring the insertion is atomic
with the full initialization. This way, if setup fails at any point, the runtime
is not added to the map and can be properly retried on the next watchWorkspace
call.

In `@src/renderer/src/i18n/da-DK/chat.json`:
- Around line 214-217: The watchStatus object containing "degraded" and "failed"
messages remain in English across multiple non-English locale files. Replace the
English strings with proper translations in each file: in
src/renderer/src/i18n/da-DK/chat.json at lines 214-217 replace with Danish
translations, in src/renderer/src/i18n/de-DE/chat.json at lines 264-267 replace
with German translations, in src/renderer/src/i18n/pt-BR/chat.json at lines
214-217 replace with Brazilian Portuguese translations, in
src/renderer/src/i18n/ru-RU/chat.json at lines 214-217 replace with Russian
translations, and in src/renderer/src/i18n/tr-TR/chat.json at lines 264-267
replace with Turkish translations. For each file, translate both the
watchStatus.degraded and watchStatus.failed string values into the corresponding
target language.

In `@src/renderer/src/i18n/it-IT/chat.json`:
- Around line 264-267: The workspace watch-status strings contain English
placeholder text in five locale files instead of proper translations. Translate
both the degraded and failed keys under workspace.files.watchStatus to the
appropriate language at each location: in src/renderer/src/i18n/it-IT/chat.json
(lines 264-267) translate to Italian, in src/renderer/src/i18n/ja-JP/chat.json
(lines 214-217) translate to Japanese, in src/renderer/src/i18n/ko-KR/chat.json
(lines 214-217) translate to Korean, in src/renderer/src/i18n/ms-MY/chat.json
(lines 264-267) translate to Malay, and in src/renderer/src/i18n/pl-PL/chat.json
(lines 264-267) translate to Polish. For each locale, replace the English text
for both degraded ("Watching in fallback mode. Changes may refresh slower.") and
failed ("File watching is unavailable. Refresh or reselect the workspace.") with
their native language equivalents.

In `@src/renderer/src/i18n/vi-VN/chat.json`:
- Around line 264-266: The watchStatus strings (degraded and failed) in the
locale files are using placeholder text that was not properly localized for each
target language. In src/renderer/src/i18n/vi-VN/chat.json at lines 264-266,
replace the English watchStatus.degraded and watchStatus.failed messages with
Vietnamese-localized translations. In src/renderer/src/i18n/zh-HK/chat.json at
lines 222-224, replace the Simplified Chinese watchStatus text with proper
Traditional Chinese (Hong Kong) phrasing. In
src/renderer/src/i18n/zh-TW/chat.json at lines 222-224, replace the Simplified
Chinese watchStatus text with proper Traditional Chinese (Taiwan) phrasing.
Ensure each locale has culturally appropriate and grammatically correct
translations for both the degraded and failed status messages.

---

Outside diff comments:
In `@src/renderer/src/components/sidepanel/composables/useWorkspaceSync.ts`:
- Around line 421-427: The watch-status listener registration is happening too
late in the initialization sequence. In the onMounted hook in
useWorkspaceSync.ts, the stopWorkspaceWatchStatusListener assignment (which
registers the handler for workspace watch-status changes) needs to be moved to
occur before the ensureWatcherState function is called, as ensureWatcherState
triggers the watcher immediately and could emit status-change events before the
listener is attached, causing initial degraded or failed states to be missed by
the panel.

In `@src/renderer/src/i18n/es-ES/chat.json`:
- Line 1: Replace the English placeholder text for the
workspace.files.watchStatus keys in the five non-English locale files with
native-language translations. In src/renderer/src/i18n/es-ES/chat.json (lines
264-267), translate the degraded and failed strings into Spanish. In
src/renderer/src/i18n/fa-IR/chat.json (lines 214-217), translate the degraded
and failed strings into Farsi/Persian. In src/renderer/src/i18n/fr-FR/chat.json
(lines 214-217), translate the degraded and failed strings into French. In
src/renderer/src/i18n/he-IL/chat.json (lines 214-217), translate the degraded
and failed strings into Hebrew. In src/renderer/src/i18n/id-ID/chat.json (lines
264-267), translate the degraded and failed strings into Indonesian.

In `@src/shared/types/presenters/workspace.d.ts`:
- Around line 97-102: The WorkspaceInvalidationEvent type marks the version
field as optional with the ? modifier, but the workspace.invalidated contract
requires version to be a non-optional required field. Remove the ? from the
version property declaration in the WorkspaceInvalidationEvent type definition
to make it a required field matching the actual contract requirement and restore
type safety at the shared event boundary.
🪄 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

Run ID: dbc3bff8-cecf-4d26-b6b2-d1d43a7873f2

📥 Commits

Reviewing files that changed from the base of the PR and between 32a3538 and af6209c.

📒 Files selected for processing (56)
  • docs/issues/parcel-watcher-issue-1764/plan.md
  • docs/issues/parcel-watcher-issue-1764/spec.md
  • docs/issues/parcel-watcher-issue-1764/tasks.md
  • electron-builder.yml
  • electron.vite.config.ts
  • package.json
  • scripts/afterPack.js
  • src/main/fileWatcherUtilityHostEntry.ts
  • src/main/lib/fileWatcher/eventCoalescer.ts
  • src/main/lib/fileWatcher/fileWatcherUtilityHost.ts
  • src/main/lib/fileWatcher/index.ts
  • src/main/lib/fileWatcher/watcherHost.ts
  • src/main/lib/fileWatcher/watcherHostClient.ts
  • src/main/lib/fileWatcher/watcherPool.ts
  • src/main/lib/fileWatcher/watcherService.ts
  • src/main/lib/fileWatcher/watcherTypes.ts
  • src/main/presenter/index.ts
  • src/main/presenter/skillPresenter/index.ts
  • src/main/presenter/workspacePresenter/index.ts
  • src/renderer/api/WorkspaceClient.ts
  • src/renderer/src/components/sidepanel/WorkspacePanel.vue
  • src/renderer/src/components/sidepanel/composables/useWorkspaceSync.ts
  • src/renderer/src/i18n/da-DK/chat.json
  • src/renderer/src/i18n/de-DE/chat.json
  • src/renderer/src/i18n/en-US/chat.json
  • src/renderer/src/i18n/es-ES/chat.json
  • src/renderer/src/i18n/fa-IR/chat.json
  • src/renderer/src/i18n/fr-FR/chat.json
  • src/renderer/src/i18n/he-IL/chat.json
  • src/renderer/src/i18n/id-ID/chat.json
  • src/renderer/src/i18n/it-IT/chat.json
  • src/renderer/src/i18n/ja-JP/chat.json
  • src/renderer/src/i18n/ko-KR/chat.json
  • src/renderer/src/i18n/ms-MY/chat.json
  • src/renderer/src/i18n/pl-PL/chat.json
  • src/renderer/src/i18n/pt-BR/chat.json
  • src/renderer/src/i18n/ru-RU/chat.json
  • src/renderer/src/i18n/tr-TR/chat.json
  • src/renderer/src/i18n/vi-VN/chat.json
  • src/renderer/src/i18n/zh-CN/chat.json
  • src/renderer/src/i18n/zh-HK/chat.json
  • src/renderer/src/i18n/zh-TW/chat.json
  • src/shared/contracts/domainSchemas.ts
  • src/shared/contracts/events.ts
  • src/shared/contracts/events/workspace.events.ts
  • src/shared/types/presenters/index.d.ts
  • src/shared/types/presenters/workspace.d.ts
  • src/shared/types/skill.ts
  • test/e2e/specs/30-workspace-watcher-events.smoke.spec.ts
  • test/main/lib/fileWatcher/eventCoalescer.test.ts
  • test/main/lib/fileWatcher/watcherPool.test.ts
  • test/main/presenter/skillPresenter/skillPresenter.test.ts
  • test/main/presenter/workspacePresenter.test.ts
  • test/main/routes/contracts.test.ts
  • test/main/scripts/afterPack.test.ts
  • test/renderer/components/WorkspacePanel.test.ts

Comment on lines +210 to +227
Fallback modes:

- `snapshot-polling`: use `@parcel/watcher.writeSnapshot()` and `getEventsSince()` from the
watcher host on a 5000 ms interval for workspace content.
- `git-metadata-polling`: stat `HEAD`, `index`, `packed-refs`, and scan `refs` mtimes from the git
watcher host on a 1000 ms interval.
- `lifecycle`: emit a full fallback invalidation when the workspace panel activates or the
workspace path changes.

Degraded mode emits a typed status event:

```text
workspace.watch.status.changed
workspacePath
mode: native | snapshot-polling | lifecycle
health: healthy | degraded | failed
reason
```

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Keep the fallback mode contract consistent.

git-metadata-polling appears here, but the earlier WatcherMode union and status-event example only cover native, snapshot-polling, and lifecycle. Please either add git-metadata-polling to the shared contract or keep it internal; otherwise the renderer state model won’t match the documented fallback behavior.

🤖 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 `@docs/issues/parcel-watcher-issue-1764/plan.md` around lines 210 - 227, The
fallback modes section documents `git-metadata-polling` as a polling strategy,
but this mode is missing from the earlier `WatcherMode` union type definition
and the status-event example that shows the `mode` field values as `native |
snapshot-polling | lifecycle`. To fix this inconsistency, either add
`git-metadata-polling` to the `WatcherMode` union and update the status-event
example to include `git-metadata-polling` as a valid mode value, or remove
`git-metadata-polling` from the Fallback modes section if it is intended to be
internal only. Whichever approach you choose, ensure that the `WatcherMode`
contract, the status-event example, and the documented fallback modes all
describe the same set of possible modes.


## Review Decisions

- Recommended dependency version: `@parcel/watcher@^2.5.6`, currently the latest npm release.

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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n docs/issues/parcel-watcher-issue-1764/spec.md

Repository: ThinkInAIXYZ/deepchat

Length of output: 8662


🌐 Web query:

@parcel/watcher npm latest version release history

💡 Result:

As of June 15, 2026, the latest version of @parcel/watcher on npm is 2.5.6, which was published on January 23, 2026 [1][2]. Release history summary: - The package was first published on December 18, 2018 [1]. - It has seen consistent updates throughout its lifecycle, with the 2.x series beginning around October 2021 [1]. - Recent releases leading up to the current version include: - v2.5.6 (January 23, 2026): Added stack-protector-strong flag and fixed a Windows ia32 build issue [2]. - v2.5.4 (January 11, 2026): Included various engineering updates and test improvements [3]. The package maintains a history of 39 versions as of this date [1][4].

Citations:


Remove "currently the latest npm release" to avoid time-bound language.

Line 138 uses language that will become stale as soon as a new version is released. Since the spec uses semantic versioning (^2.5.6), rephrase to reference only the version chosen for this implementation without claiming it's currently latest, for example: "Recommended dependency version: @parcel/watcher@^2.5.6."

🤖 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 `@docs/issues/parcel-watcher-issue-1764/spec.md` at line 138, Remove the phrase
"currently the latest npm release" from line 138 in the spec.md file. Keep only
the part about the recommended dependency version with the semantic versioning
constraint "`@parcel/watcher`@^2.5.6". This eliminates time-bound language that
will become outdated when newer versions are released, while maintaining the
useful version specification information.

Comment on lines +17 to +23
- `pnpm run format`
- `pnpm run i18n`
- `pnpm run lint`
- `pnpm run typecheck`
- `pnpm test`
- `pnpm run build`
- `pnpm exec playwright test -c test/e2e/playwright.config.ts test/e2e/specs/30-workspace-watcher-events.smoke.spec.ts`

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align the verification checklist with the acceptance gate.

The spec calls for pnpm run typecheck:node, but this checklist records pnpm run typecheck. Please make the task list match the command you expect contributors to run, or note that both are required.

🤖 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 `@docs/issues/parcel-watcher-issue-1764/tasks.md` around lines 17 - 23, The
verification checklist includes `pnpm run typecheck` but the acceptance gate
specification requires `pnpm run typecheck:node`. Update the task list to use
the correct command `pnpm run typecheck:node` that matches the spec, or if both
commands are required, add a note clarifying that both should be executed by
contributors.

Comment on lines +57 to +69
const deletedParents = mergedEvents
.filter((event) => event.type === 'delete')
.map((event) => path.normalize(event.path))

return mergedEvents.filter((event) => {
if (event.type !== 'delete') {
return true
}

const normalized = path.normalize(event.path)
return !deletedParents.some(
(deletedParent) => deletedParent !== normalized && isDescendantOf(normalized, deletedParent)
)

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use normalizeEventKey for descendant-delete filtering too.

Line 59 and Line 66 use path.normalize, which is weaker than the merge-key normalization. That can miss dedupe for equivalent paths and leak redundant delete events.

Suggested fix
   const mergedEvents = Array.from(byPath.values())
   const deletedParents = mergedEvents
     .filter((event) => event.type === 'delete')
-    .map((event) => path.normalize(event.path))
+    .map((event) => normalizeEventKey(event.path))
 
   return mergedEvents.filter((event) => {
     if (event.type !== 'delete') {
       return true
     }
 
-    const normalized = path.normalize(event.path)
+    const normalized = normalizeEventKey(event.path)
     return !deletedParents.some(
       (deletedParent) => deletedParent !== normalized && isDescendantOf(normalized, deletedParent)
     )
   })
🤖 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/main/lib/fileWatcher/eventCoalescer.ts` around lines 57 - 69, The code is
using path.normalize on lines 59 and 66 to normalize paths in the deletedParents
array and when checking descendants, but this is weaker than the
normalizeEventKey normalization used elsewhere. Replace both occurrences of
path.normalize(event.path) in the deletedParents mapping and the normalized
variable assignment in the filter function with normalizeEventKey(event.path) to
ensure consistent and stronger path normalization that prevents missing
equivalent paths and leaking redundant delete events.

Comment on lines +166 to +179
if (!fs.existsSync(activeWatch.request.rootPath)) {
this.enqueueEvents(activeWatch, [
{
path: activeWatch.request.rootPath,
type: 'root-deleted'
}
])
this.sendStatus(activeWatch, {
health: 'failed',
mode: activeWatch.mode,
reason: 'root-deleted'
})
return
}

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Stop polling after terminal root-deleted state to avoid repeated failure churn.

When rootPath is missing, the host emits root-deleted/failed but keeps scheduling polling, which can repeatedly fire the same failure path every interval and retrigger downstream invalidation work.

Suggested fix
@@
-    void poll()
-    activeWatch.pollTimer = setInterval(() => {
+    activeWatch.pollTimer = setInterval(() => {
       void poll()
     }, SNAPSHOT_POLL_INTERVAL_MS)
+    void poll()
@@
         if (!fs.existsSync(activeWatch.request.rootPath)) {
           this.enqueueEvents(activeWatch, [
             {
               path: activeWatch.request.rootPath,
               type: 'root-deleted'
             }
           ])
           this.sendStatus(activeWatch, {
             health: 'failed',
             mode: activeWatch.mode,
             reason: 'root-deleted'
           })
+          if (activeWatch.pollTimer) {
+            clearInterval(activeWatch.pollTimer)
+            activeWatch.pollTimer = null
+          }
           return
         }

Also applies to: 215-218

🤖 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/main/lib/fileWatcher/watcherHost.ts` around lines 166 - 179, After
detecting and reporting the terminal `root-deleted` state in the root path
existence check (where fs.existsSync fails for activeWatch.request.rootPath),
prevent any future polling from being scheduled for this activeWatch. Identify
the polling scheduling mechanism (likely a timeout or interval setup) and ensure
it is cancelled or prevented from being re-scheduled once the `root-deleted`
condition is detected and the status is set to failed. This issue appears at two
locations: the primary check at lines 166-179 and a secondary occurrence at
lines 215-218; both sites need the same fix to prevent polling from continuing
after the terminal failure state is reached.

Comment on lines 168 to 176
this.watchRuntimes.set(normalized, runtime)
runtime.contentWatcher = await this.createContentWatcher(normalized)
if (runtime.disposed || this.watchRuntimes.get(normalized) !== runtime) {
await runtime.contentWatcher.close()
runtime.contentWatcher = null
return
}
await this.refreshGitWatcher(runtime)
}

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Make watcher startup atomic to avoid wedged runtimes.

At Line 168, runtime is inserted into watchRuntimes before async setup. If createContentWatcher (Line 169) or refreshGitWatcher (Line 175) throws, the map retains a runtime with no active watchers; later watchWorkspace calls hit Line 150-154 and only increment refCount, so watching may never recover.

Suggested fix
-    this.watchRuntimes.set(normalized, runtime)
-    runtime.contentWatcher = await this.createContentWatcher(normalized)
-    if (runtime.disposed || this.watchRuntimes.get(normalized) !== runtime) {
-      await runtime.contentWatcher.close()
-      runtime.contentWatcher = null
-      return
-    }
-    await this.refreshGitWatcher(runtime)
+    this.watchRuntimes.set(normalized, runtime)
+    try {
+      runtime.contentWatcher = await this.createContentWatcher(normalized)
+      if (runtime.disposed || this.watchRuntimes.get(normalized) !== runtime) {
+        await runtime.contentWatcher.close()
+        runtime.contentWatcher = null
+        return
+      }
+      await this.refreshGitWatcher(runtime)
+    } catch (error) {
+      this.watchRuntimes.delete(normalized)
+      await this.disposeRuntime(runtime)
+      throw error
+    }
🤖 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/main/presenter/workspacePresenter/index.ts` around lines 168 - 176, The
runtime is being added to the watchRuntimes map at the beginning of the async
setup sequence, before createContentWatcher and refreshGitWatcher complete. If
either of these async operations throws an error, the runtime remains in the map
but with incomplete or failed initialization, causing subsequent watchWorkspace
calls to find it in the map and only increment refCount instead of retrying
setup. Move the this.watchRuntimes.set(normalized, runtime) call to execute only
after both createContentWatcher and refreshGitWatcher have completed
successfully, ensuring the insertion is atomic with the full initialization.
This way, if setup fails at any point, the runtime is not added to the map and
can be properly retried on the next watchWorkspace call.

Comment on lines +230 to +233
for (const event of batch.events) {
if (event.type === 'overflow' || event.type === 'root-deleted') {
void this.refreshGitWatcher(runtime)
this.scheduleInvalidation(runtime, 'full', source)

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Catch refreshGitWatcher failures in fire-and-forget paths.

Line 232 and Line 242 call void this.refreshGitWatcher(runtime) without a rejection handler. If that promise rejects, it can become an unhandled rejection and destabilize shutdown/runtime behavior.

Suggested fix
+  private refreshGitWatcherSafely(runtime: WorkspaceWatchRuntime): void {
+    void this.refreshGitWatcher(runtime).catch((error) => {
+      console.warn('[Workspace] Failed to refresh git watcher', {
+        workspacePath: runtime.workspacePath,
+        error
+      })
+    })
+  }
...
-        void this.refreshGitWatcher(runtime)
+        this.refreshGitWatcherSafely(runtime)
         this.scheduleInvalidation(runtime, 'full', source)
         return
...
-        void this.refreshGitWatcher(runtime)
+        this.refreshGitWatcherSafely(runtime)
         this.scheduleInvalidation(runtime, 'full', source)
         return

Also applies to: 241-243

🤖 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/main/presenter/workspacePresenter/index.ts` around lines 230 - 233, The
fire-and-forget calls to `this.refreshGitWatcher(runtime)` at lines 232 and 242
lack rejection handlers, which can cause unhandled promise rejections. Add a
`.catch()` handler to both invocations of `refreshGitWatcher(runtime)` to
properly handle any promise rejections, ensuring errors are either logged or
handled gracefully rather than becoming unhandled rejections that could
destabilize the runtime.

Comment on lines +214 to 217
"watchStatus": {
"degraded": "Watching in fallback mode. Changes may refresh slower.",
"failed": "File watching is unavailable. Refresh or reselect the workspace."
}

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Local watch-status strings are still English in non-English locale bundles.

The same English fallback/failed messages were inserted in multiple locale files, so localized UIs will display mixed-language status text.

  • src/renderer/src/i18n/da-DK/chat.json#L214-L217: replace both watchStatus values with Danish translations.
  • src/renderer/src/i18n/de-DE/chat.json#L264-L267: replace both watchStatus values with German translations.
  • src/renderer/src/i18n/pt-BR/chat.json#L214-L217: replace both watchStatus values with Brazilian Portuguese translations.
  • src/renderer/src/i18n/ru-RU/chat.json#L214-L217: replace both watchStatus values with Russian translations.
  • src/renderer/src/i18n/tr-TR/chat.json#L264-L267: replace both watchStatus values with Turkish translations.
📍 Affects 5 files
  • src/renderer/src/i18n/da-DK/chat.json#L214-L217 (this comment)
  • src/renderer/src/i18n/de-DE/chat.json#L264-L267
  • src/renderer/src/i18n/pt-BR/chat.json#L214-L217
  • src/renderer/src/i18n/ru-RU/chat.json#L214-L217
  • src/renderer/src/i18n/tr-TR/chat.json#L264-L267
🤖 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/renderer/src/i18n/da-DK/chat.json` around lines 214 - 217, The
watchStatus object containing "degraded" and "failed" messages remain in English
across multiple non-English locale files. Replace the English strings with
proper translations in each file: in src/renderer/src/i18n/da-DK/chat.json at
lines 214-217 replace with Danish translations, in
src/renderer/src/i18n/de-DE/chat.json at lines 264-267 replace with German
translations, in src/renderer/src/i18n/pt-BR/chat.json at lines 214-217 replace
with Brazilian Portuguese translations, in src/renderer/src/i18n/ru-RU/chat.json
at lines 214-217 replace with Russian translations, and in
src/renderer/src/i18n/tr-TR/chat.json at lines 264-267 replace with Turkish
translations. For each file, translate both the watchStatus.degraded and
watchStatus.failed string values into the corresponding target language.

Comment on lines +264 to 267
"watchStatus": {
"degraded": "Watching in fallback mode. Changes may refresh slower.",
"failed": "File watching is unavailable. Refresh or reselect the workspace."
}

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing translations for workspace watch-status strings across five locales. All five non-English locale files contain English placeholder text for workspace.files.watchStatus.degraded and workspace.files.watchStatus.failed. Users who have selected these locales will see English text in the workspace watch-status banner instead of their expected language.

  • src/renderer/src/i18n/it-IT/chat.json#L264-L267: translate both keys to Italian
  • src/renderer/src/i18n/ja-JP/chat.json#L214-L217: translate both keys to Japanese
  • src/renderer/src/i18n/ko-KR/chat.json#L214-L217: translate both keys to Korean
  • src/renderer/src/i18n/ms-MY/chat.json#L264-L267: translate both keys to Malay
  • src/renderer/src/i18n/pl-PL/chat.json#L264-L267: translate both keys to Polish
📍 Affects 5 files
  • src/renderer/src/i18n/it-IT/chat.json#L264-L267 (this comment)
  • src/renderer/src/i18n/ja-JP/chat.json#L214-L217
  • src/renderer/src/i18n/ko-KR/chat.json#L214-L217
  • src/renderer/src/i18n/ms-MY/chat.json#L264-L267
  • src/renderer/src/i18n/pl-PL/chat.json#L264-L267
🤖 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/renderer/src/i18n/it-IT/chat.json` around lines 264 - 267, The workspace
watch-status strings contain English placeholder text in five locale files
instead of proper translations. Translate both the degraded and failed keys
under workspace.files.watchStatus to the appropriate language at each location:
in src/renderer/src/i18n/it-IT/chat.json (lines 264-267) translate to Italian,
in src/renderer/src/i18n/ja-JP/chat.json (lines 214-217) translate to Japanese,
in src/renderer/src/i18n/ko-KR/chat.json (lines 214-217) translate to Korean, in
src/renderer/src/i18n/ms-MY/chat.json (lines 264-267) translate to Malay, and in
src/renderer/src/i18n/pl-PL/chat.json (lines 264-267) translate to Polish. For
each locale, replace the English text for both degraded ("Watching in fallback
mode. Changes may refresh slower.") and failed ("File watching is unavailable.
Refresh or reselect the workspace.") with their native language equivalents.

Comment on lines +264 to +266
"watchStatus": {
"degraded": "Watching in fallback mode. Changes may refresh slower.",
"failed": "File watching is unavailable. Refresh or reselect the workspace."

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Localize new watchStatus strings per target locale. The same root issue appears in multiple locale packs: copied fallback text was not translated/adapted for each locale.

  • src/renderer/src/i18n/vi-VN/chat.json#L264-L266: replace English watchStatus.degraded/failed messages with Vietnamese-localized text.
  • src/renderer/src/i18n/zh-HK/chat.json#L222-L224: replace Simplified Chinese watchStatus text with Traditional Chinese (Hong Kong) phrasing.
  • src/renderer/src/i18n/zh-TW/chat.json#L222-L224: replace Simplified Chinese watchStatus text with Traditional Chinese (Taiwan) phrasing.
📍 Affects 3 files
  • src/renderer/src/i18n/vi-VN/chat.json#L264-L266 (this comment)
  • src/renderer/src/i18n/zh-HK/chat.json#L222-L224
  • src/renderer/src/i18n/zh-TW/chat.json#L222-L224
🤖 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/renderer/src/i18n/vi-VN/chat.json` around lines 264 - 266, The
watchStatus strings (degraded and failed) in the locale files are using
placeholder text that was not properly localized for each target language. In
src/renderer/src/i18n/vi-VN/chat.json at lines 264-266, replace the English
watchStatus.degraded and watchStatus.failed messages with Vietnamese-localized
translations. In src/renderer/src/i18n/zh-HK/chat.json at lines 222-224, replace
the Simplified Chinese watchStatus text with proper Traditional Chinese (Hong
Kong) phrasing. In src/renderer/src/i18n/zh-TW/chat.json at lines 222-224,
replace the Simplified Chinese watchStatus text with proper Traditional Chinese
(Taiwan) phrasing. Ensure each locale has culturally appropriate and
grammatically correct translations for both the degraded and failed status
messages.

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

Labels

None yet

Projects

None yet

1 participant