diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2cd778df9..d34d28c3d 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -337,6 +337,9 @@ jobs:
- name: Native-readiness roadmap policy gate
run: pnpm run native-readiness:check
+ - name: Tauri plugin Rust/npm version-parity gate
+ run: pnpm run tauri-plugins:check
+
- name: Feature parity audit
run: pnpm run parity:check
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0114ce434..b350f0ef7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Fixed
+
+- **Tauri plugin Rust/npm version parity restored:** `tauri-plugin-http` and
+ `tauri-plugin-notification` had drifted ahead of their npm counterparts
+ (`@tauri-apps/plugin-http`, `@tauri-apps/plugin-notification`) after #661 bumped only the Rust
+ side, failing every platform's Tauri release build. Bumped the npm packages to match; added
+ `check-tauri-plugin-versions.mjs`, a cheap CI guard catching this class of mismatch before the
+ next release tag instead of at tag-triggered release time.
+
## [1.28.5] — 2026-09-09
diff --git a/README.md b/README.md
index 97ddc9775..ac00f4ae3 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,7 @@
-
+
@@ -512,7 +512,7 @@ The Settings → AI panel shows a live GPU status badge with adapter details and
| **Document Export** | docx + jszip | Word-compatible `.docx` generation (lazy-loaded) |
| **PWA** | Service Worker + Web App Manifest v3 | Offline support, installability, Workbox chunking |
| **i18n** | Custom React Context (`I18nContext.tsx`) | 2942 keys × 19 locales (de/en/es/fr/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu/ru/ko Beta); EN fallback; `localStorage` persistence |
-| **Testing** | Vitest 4.x (7635+ tests / 603 files) + Playwright E2E | Unit/integration + cross-browser E2E; Stryker mutation (manual workflow) |
+| **Testing** | Vitest 4.x (7643+ tests / 604 files) + Playwright E2E | Unit/integration + cross-browser E2E; Stryker mutation (manual workflow) |
| **Code Quality** | Biome (lint + format) + TypeScript 7 (tsgo) strict | `--error-on-warnings` in CI; zero `any` policy |
| **Visualization** | Force-directed graph | Interactive character relationship network |
| **Desktop** | Tauri v2 | Cross-platform installer; auto-updater via `latest.json` |
@@ -550,7 +550,7 @@ WorldScript-Studio/
│ ├── sw.js # PWA Service Worker
│ └── manifest.json # PWA Web App Manifest v3
├── tests/
-│ ├── unit/ # Vitest unit tests (7635+ tests, 603 files) — count spans tests/, components/, packages/*/tests/, not just this folder
+│ ├── unit/ # Vitest unit tests (7643+ tests, 604 files) — count spans tests/, components/, packages/*/tests/, not just this folder
│ │ ├── ai/ # aiSmallModules, aiCoreFallbackPaths
│ │ └── settings/ # WebLlmPanel, AiSections
│ └── e2e/ # Playwright specs + helpers.ts
@@ -715,7 +715,7 @@ The main pipeline is [`.github/workflows/ci.yml`](.github/workflows/ci.yml). Opt
Raw bundle-budget ceilings (KB per uncompressed asset): entry **2500 KB**, vendor **6200 KB**, other JavaScript **2500 KB**, and WASM **30000 KB**.
**Current test metrics (2026-09-07, source-synchronized; CI remains authoritative for pass/fail):**
-- **7635+ unit tests** across **603 test files** — CI is authoritative for pass/fail
+- **7643+ unit tests** across **604 test files** — CI is authoritative for pass/fail
- Coverage thresholds: lines ≥ 80 · branches ≥ 66 · functions ≥ 72 · statements ≥ 78 — enforced in CI (see Codecov badge for live metrics)
- i18n: **2942 keys × 19 locales** (en/de/fr/es/it + ar/he/fa RTL Beta + ja/zh/pt/el/fi/sv/hu/is/eu/ru/ko Beta)
diff --git a/docs/CI.md b/docs/CI.md
index fd3f09d0b..b54e79a6c 100644
--- a/docs/CI.md
+++ b/docs/CI.md
@@ -375,7 +375,7 @@ the local block above; GitHub Actions owns those heavy checks on this hardware.
`pnpm run ci:prepush` always resolves a change classification from the outgoing evidence first
(`scripts/ci-prepush-classifier.mjs`), then runs docs/release-truth, CSP, desktop-import boundary,
-native-readiness, and dependency-state checks unconditionally on every invocation. It does **not**
+native-readiness, Tauri plugin version-parity, and dependency-state checks unconditionally on every invocation. It does **not**
run Biome lint — full-repository lint stays CI-owned (`quality` job); only staged files are linted
locally, by the separate pre-commit hook (`lint-staged`). Two check groups are conditional on the
change classification instead of always running:
diff --git a/docs/PR-CI-MERGE-WORKFLOW.md b/docs/PR-CI-MERGE-WORKFLOW.md
index e5d810934..9a7ea1a33 100644
--- a/docs/PR-CI-MERGE-WORKFLOW.md
+++ b/docs/PR-CI-MERGE-WORKFLOW.md
@@ -2,11 +2,11 @@
Full pre-push gate mechanics, worktree bootstrap, CI pipeline order, the PR review-comment correction loop (bot roster, three-channel comment check, quiescence loop), and known GitHub merge-gate quirks with recovery steps. The short list of non-negotiable rules distilled from this doc lives in the root `AGENTS.md`.
-**Mandatory pre-push gate:** Run `pnpm run ci:prepush` before every push and again after every local correction before re-pushing. It always resolves a change-aware classification (`scripts/ci-prepush-classifier.mjs`) from the outgoing evidence first, then runs docs/release-truth, CSP, desktop-import-boundary, native-readiness, and dependency-state checks unconditionally — it does **not** run Biome lint; that stays the pre-commit hook's job on staged files only (`lint-staged`), and full-repository lint is CI-owned. The single-checker (`--checkers 1`) local typecheck and the i18n/content-guard checks run only when the classification requires them — `DOCS_ONLY`, `WORKFLOW_ONLY`, `NON_CODE_ONLY`, `RUST_TAURI`, `TOOLING`, and non-TypeScript `TEST_ONLY` changes report typecheck as `DEFERRED_TO_REQUIRED_CI` instead of running it locally, and i18n/content-guard checks run only for changes matching their own governed paths or implementation files (see `scripts/ci-prepush-check-registry.mjs`). It is the same `tsgo --noEmit` check as CI, not literally identical to it — CI uses `--checkers 4`. Whenever outgoing path evidence is incomplete, unresolved, or the manual committed-range diff fails, the gate fails closed into full local admission (every conditional check runs) rather than deferring anything. A targeted test or changed-file lint run alone is insufficient. If pnpm reports a dependency verification failure after a branch or lockfile change, run `node scripts/dependency-state.mjs reconcile` (or `pnpm run deps:reconcile`) first, then rerun the gate. The pre-commit hook does not replace this gate. Required GitHub CI remains the unconditional authority for the complete lint, TypeScript, and i18n validation regardless of what the local gate deferred.
+**Mandatory pre-push gate:** Run `pnpm run ci:prepush` before every push and again after every local correction before re-pushing. It always resolves a change-aware classification (`scripts/ci-prepush-classifier.mjs`) from the outgoing evidence first, then runs docs/release-truth, CSP, desktop-import-boundary, native-readiness, Tauri plugin version-parity, and dependency-state checks unconditionally — it does **not** run Biome lint; that stays the pre-commit hook's job on staged files only (`lint-staged`), and full-repository lint is CI-owned. The single-checker (`--checkers 1`) local typecheck and the i18n/content-guard checks run only when the classification requires them — `DOCS_ONLY`, `WORKFLOW_ONLY`, `NON_CODE_ONLY`, `RUST_TAURI`, `TOOLING`, and non-TypeScript `TEST_ONLY` changes report typecheck as `DEFERRED_TO_REQUIRED_CI` instead of running it locally, and i18n/content-guard checks run only for changes matching their own governed paths or implementation files (see `scripts/ci-prepush-check-registry.mjs`). It is the same `tsgo --noEmit` check as CI, not literally identical to it — CI uses `--checkers 4`. Whenever outgoing path evidence is incomplete, unresolved, or the manual committed-range diff fails, the gate fails closed into full local admission (every conditional check runs) rather than deferring anything. A targeted test or changed-file lint run alone is insufficient. If pnpm reports a dependency verification failure after a branch or lockfile change, run `node scripts/dependency-state.mjs reconcile` (or `pnpm run deps:reconcile`) first, then rerun the gate. The pre-commit hook does not replace this gate. Required GitHub CI remains the unconditional authority for the complete lint, TypeScript, and i18n validation regardless of what the local gate deferred.
**New-worktree / first-time dependency bootstrap — never a bare `pnpm install`:** When materializing `node_modules` for the first time on a new git worktree (or any branch checkout that hasn't been installed yet), always run `node scripts/dependency-state.mjs reconcile` (or `pnpm run deps:reconcile`) — never a bare `pnpm install`. The bare form omits `--frozen-lockfile`, so on any manifest/lockfile drift it silently *rewrites* `pnpm-lock.yaml` instead of failing loudly, and it never writes the repo's own dependency fingerprint (`node_modules/.worldscript-deps-fingerprint`) — that fingerprint is checked only by `pnpm run deps:verify`, `ci:prepush`, and the pre-commit hook, not by arbitrary `pnpm run