Skip to content

Latest commit

 

History

History
1876 lines (1325 loc) · 188 KB

File metadata and controls

1876 lines (1325 loc) · 188 KB

WorldScript Studio — Codebase Audit Report

Date: 2026-04-17 (baseline); follow-up chain: … → 2026-05-28 (v1.19.0 — Security/Voice/RTL/Logger B-1..B-8) → 2026-05-30 (B-1 passphrase UX + CI unblock)2026-05-31 (i18n audit + settings features + CI stabilization)2026-05-31 (Edge-AI Perfection Cycle — Phases 0-7 complete)2026-06-01 (Post-crash session: CI stabilisation + 14 CodeAnt AI fixes + E2E hardening)2026-06-02 (Perf Phase 2.3 — pipeline-LRU unification + PR #69 CodeAnt fixes)2026-06-03 (WorkerBus v2 Phase 3 — Rust TaskSupervisor + Tauri-build unblock)2026-06-06 (Phase 3 i18n Expansion — ja/zh/pt/el + Intl APIs)2026-06-09 (v1.21 Deep Audit Correction — Whisper WASM download UI + 3 CodeAnt fixes + CloudSync LWW)2026-06-09 (feat/deep-audit-v1.21 — CSP hardening, zh locale ≤5% EN, coverage Batches A/B/C, VoiceActivityCoordinator B-2 bridge)2026-06-11 (Ultimate Copilot v2 Phase 2+3 — markdown, sidebar, Apply-to-chapter, InlineAnnotation, ProForge chip; PR #110+#111)2026-06-11 (v1.22.0 release — OpenRouter Cloud 5 provider, AI Execution Modes hybrid/cloud/local/eco, AiModeIndicator, SW cache-invalidation fix)2026-06-13/14 (v1.23 perfection batch — OpenRouter + AI-Execution-Mode settings sections localized/modernized, i18n single-brace interpolation bug-class fix + i18nPlaceholders regression guard, bundle split + budget tightening PR #130)2026-06-16 (v1.23.0 release — rebrand StoryCraft → WorldScript Studio, local-first data foundation ADR-0008, Tauri blank-screen + asset-URL fixes, AI error taxonomy + retry hardening, command-palette & local-AI settings localization, WorldScript W monogram icons)2026-06-16 (post-release documentation perfection pass — corpus sync, metric reconciliation, history archival, dependabot hardening)2026-06-17 (Language expansion — +6 locales fi/sv/hu/is/eu/fa (RTL); PR #174 merged; LanguageSelector exonym localization via portal.language.names.*; portal chrome 100 % for the 6 new langs; README/AUDIT/CHANGELOG docs sync)2026-07-28 (v1.24.1 — Local-AI reliability fixes #266, Dependabot backlog triage, Issue #60 audit closeout, security/build hardening)2026-07-29 (v1.24.2, PR #284 — CSP functional-truth, desktop crypto, and doc-truth hardening; the local-inference stack had been silently non-functional in production for two months; merged and tagged)2026-07-30 (v1.24.3, WorkerBus v2 worker-generation consolidation — 5 stacked PRs #286–288/#290–291 + independent #289 recovered after an interrupted merge queue; ADR-0015; merged and tagged)2026-08-12 (encryption lifecycle + desktop reliability + recovery journal — stacked PRs #335/#336/#337 merged into main; fail-closed IDB encryption lifecycle, desktop AI/Python provider hardening for #332/#333, durable resumable migration journal with secondary-store adapters; PR #339 closes the write-vs-migration TOCTOU race CodeRabbit and Qodo independently found on #337; PR #310 closed as superseded; Phase-4 production wiring for disable/rotate tracked as issue #338)2026-08-13 (v1.27.0 — Phase 4 encryption disable/rotate production wiring + i18n (#342/#343 close issue #338), Tauri desktop boot-hydration + flush-on-quit + Settings re-render perf fixes (#332, PR #345), AI Writing Studio unreadable-text fix (#341, PR #344), download-progress bytes/speed for voice + WebLLM models (#333 item 1, PR #346); all 5 PRs' correction loops ran to full quiescence — codebase-wide QNBS-v3/i18n fixes, a real data-integrity bug found and fixed in the rekey-recovery try/catch scoping, and sequential main-reconciliation merges across the whole 5-PR stack)2026-08-14 (v1.27.1 — desktop persistence/security stabilization; #363: atomic writes across every Tauri fs-backed store, unified fail-closed desktop API-key routing, factory-reset UI/logic consolidated into useFactoryReset + FactoryResetDangerZone, packaged-build factory-reset capability-scope fix, #[cfg(desktop)]-gated menu-event handler + re-wired native menu-action bridge, rust-tauri/e2e/vrt promoted to required CI gates; plus the nanoid security-advisory patch, PR #362) Scope: Full application, repository configuration, CI/CD, documentation, release validation Current version: v1.28.6 released — tag v1.28.6 and the GitHub Release were published on 2026-09-09 from the validated main commit b5d0eedf, a recovery release fixing v1.28.5's tag-triggered Tauri desktop build, which failed on every platform with a Rust/npm plugin version mismatch (tauri-plugin-http, tauri-plugin-notification drifted ahead of their npm counterparts after #661's Rust-only Dependabot bump). PR #678 aligned the npm versions and added check-tauri-plugin-versions.mjs, a permanent CI guard against this class of mismatch; PR #679 fixed a CHANGELOG completeness-gate PR-number reference gap that resulting-main surfaced right after #678 merged; PR #680 completed the version bump and release-truth reconciliation, converged through 3 correction batches for reviewer/accuracy findings. 7643+ Vitest source-declared tests across 604 test files · 2942 keys × 19 locales. The v1.28.5 tag itself is untouched and permanently bound to its original commit as the historical failed/incomplete desktop-release cut (its separate Docker/GHCR publish did succeed — see the release-gate entry below). Historical release sections below remain historical.

Release gate (2026-09-09 — v1.28.6): Main CI/CD run 34408072174 (the push-triggered run for the release-prep merge commit) passed all applicable required and advisory jobs (Quality Gate Node 22+24, Tauri Rust Gate, Security Audit, Verified Signatures, Build, E2E Tests, E2E Deep Coverage, Storybook, Browser Quality (VRT + Lighthouse), CI Success); Core Rust Gate legitimately skipped (the release-prep commit touches src-tauri/, not crates/). Main-push CodeQL SAST run 34408072137 passed; 0 open code-scanning alerts. Before tagging, a dedicated exact-SHA workflow_dispatch Tauri qualification (run 34410474256) on this exact commit passed Windows/Ubuntu/macOS — this recovery's own release discipline, on top of the standard tag-triggered qualification below. The tag-triggered Tauri desktop build workflow 34411937761 passed all 5 jobs (Verify release tag, Bundle for windows-latest/ubuntu-22.04/macos-latest, GitHub Release); the tag-triggered CI / CD run 34411937709 independently passed all applicable jobs including E2E Tests (Playwright). A parallel Docker workflow (34411937646) built and pushed ghcr.io/qnbs/worldscript-studio:1.28.6 (digest sha256:8fbaed94388c2bcce7b0caf182497f711748e490fdc467b0941c8cadcc77f312). The release publishes the same asset shape as v1.28.4/v1.28.3: Linux x86_64 (rpm/deb/AppImage) and Windows x86_64 (msi/exe) artifacts with corresponding Minisign .sig files; macOS arm64 ships an unsigned .dmg installer plus a signed .app.tar.gz updater bundle (.sig) — no separate .dmg.sig; latest.json present; all 14 release assets uploaded successfully. Platform code-signing and notarization remain separate claims. No macOS x86_64 artifact, matching prior releases — Intel-macOS remains a gated qualification lane (ADR-0021). Landed via PR #678 (Tauri plugin Rust/npm version-parity fix + permanent CI guard), PR #679 (CHANGELOG completeness-gate PR-number reference fix), and PR #680 (version bump + CHANGELOG/TODO/README release truth).

Release gate (2026-09-09 — v1.28.5, desktop release failed): Tag v1.28.5 was created on commit 590481189c0f9980f71b4081f4a6f8fccef7388d via PR #676's release-prep merge; the tag itself is permanently kept as-is (never deleted, moved, or re-tagged) as the historical record of this failure. Main-push CI/CD run 34353928731 and CodeQL SAST run 34353928691 both passed on the release-prep commit. The tag-triggered Tauri desktop build workflow 34357232967 ran Verify release tag successfully, then failed on every platform's Bundle job individually (windows-latest, ubuntu-22.04, macos-latest each failed): tauri-plugin-http resolved to Rust 2.6.0 vs npm 2.5.9 and tauri-plugin-notification resolved to Rust 2.4.0 vs npm 2.3.3, both drifted after #661's Rust-only Dependabot bump, and tauri build hard-rejects a Rust/npm major.minor mismatch; the GitHub Release job was correctly skipped since no bundle job produced assets — no GitHub Release, installer, or updater artifact was ever published for v1.28.5. The tag-triggered CI / CD run 34357232902 passed independently (it does not depend on the Tauri bundle jobs). A parallel Docker workflow (34357233022) succeeded and pushed ghcr.io/qnbs/worldscript-studio:1.28.5 (digest sha256:3e5ac677979de21fb45ecb2899fb824302047c114829e54265d090928862f940) — the desktop-build failure did not affect the separately-triggered container publish. Root-caused and fixed in v1.28.6 above via PR #678.

Release gate (2026-09-05 — v1.28.4): Main CI/CD run 33973305518 (the push-triggered run for the release-prep merge commit) passed all applicable required and advisory jobs (Quality Gate Node 22+24, Tauri Rust Gate, Security Audit, Verified Signatures, Build, E2E Tests, E2E Deep Coverage, Storybook, Browser Quality (VRT + Lighthouse), CI Success, Deploy to GitHub Pages — the last verified with a real github-pages Deployments API record for this exact commit, deployment id 6282574436, state: success); Core Rust Gate legitimately skipped (the release-prep commit touches src-tauri/, not crates/). Main-push CodeQL SAST run 33973305597 passed; 0 open code-scanning alerts (.github/workflows/codeql.yml triggers only on push: branches: [main] — it has no tag trigger, so this main-push run is the only applicable CodeQL evidence for this release; it does not re-run on the tag). The tag-triggered Tauri desktop build workflow 33974750122 passed; the tag-triggered CI / CD run 33974750136 independently passed all applicable jobs including E2E Tests (Playwright). A parallel Docker workflow (33974750117) built and pushed ghcr.io/qnbs/worldscript-studio (digest sha256:f605598fd0dda9156d65768dd98a141b833e0fbd35cfd053ebf7e1882ab49e15). The release publishes the same asset shape as v1.28.3: Linux x86_64 (rpm/deb/AppImage) and Windows x86_64 (msi/exe) artifacts with corresponding Minisign .sig files; macOS arm64 ships an unsigned .dmg installer plus a signed .app.tar.gz updater bundle (.sig) — no separate .dmg.sig; latest.json present; all 14 release assets uploaded successfully. Platform code-signing and notarization remain separate claims. No macOS x86_64 artifact, matching prior releases — Intel-macOS remains a gated qualification lane (ADR-0021). Landed via PR #613 (#585 fix — the full first-install classification state machine, audited across the complete state-transition matrix), PR #612 (#514 fix — positive cache-ownership scoping), and PR #615 (version bump + CHANGELOG/TODO/README release truth, itself corrected twice during review: R-15 gate language brought into agreement with the binding roadmap/ledger, and PR #596's Factory Reset data-integrity fix moved from a generic Tests bullet to its own Fixed entry).

Release gate (2026-08-27 — v1.28.3): Main CI/CD run 33113772841 (the push-triggered run for the release-prep merge commit) passed all applicable required and advisory jobs (Quality Gate Node 22+24, Tauri Rust Gate, Build, E2E, E2E Deep Coverage, Storybook, Lighthouse, Visual Regression, CI Success, Deploy to GitHub Pages — the last verified with a real github-pages Deployments API record for this exact commit, state: success); Core Rust Gate legitimately skipped (the release-prep commit touches src-tauri/, not crates/). CodeQL SAST run 33113772797 passed; 0 open code-scanning alerts. The tag-triggered Tauri workflow 33115805027 passed all 5 jobs (Verify release tag, Bundle for windows-latest/ubuntu-22.04/macos-latest, GitHub Release); the tag-triggered CI / CD run 33115805002 independently passed all applicable jobs including E2E Tests (Playwright) (PR Size Governance and Deploy to GitHub Pages legitimately skipped — restricted to pull_request and refs/heads/main respectively, neither of which a tag push satisfies) — no repeat of the non-deterministic tag-run E2E flake seen on v1.28.2's release. A parallel Docker workflow (33115804967) built and pushed ghcr.io/qnbs/worldscript-studio tagged 1.28.3/1.28/latest (digest sha256:b6e7d71b…). The release publishes the same asset shape as v1.28.2: Linux x86_64 (rpm/deb/AppImage) and Windows x86_64 (msi/exe) artifacts with corresponding Minisign .sig files; macOS arm64 ships an unsigned .dmg installer plus a signed .app.tar.gz updater bundle (.sig) — no separate .dmg.sig; latest.json present; all 14 release assets uploaded successfully. Platform code-signing and notarization remain separate claims. No macOS x86_64 artifact, matching prior releases — Intel-macOS remains a gated qualification lane (ADR-0021). Landed via PR #530 (#527 fix — synchronous isPortalActive init + isInitialLoad guard on the bootstrap effect), PR #533 (E2E harness/precondition fix associated with #532 — ensureWelcomePortalEntry() deterministic startup-state precondition, including its own internal-reload-vs-autosave race and locale-independent WelcomePortal detection via a stable data-testid; #532's root cause stays open), and PR #534 (version bump + CHANGELOG/README release truth).

Dual-graph tooling revalidated (2026-08-28): Graphify and CodeGraph were audited end-to-end and both upgraded under a controlled-upgrade policy — graphifyy 0.8.26 → 0.9.51, @colbymchenry/codegraph (last recorded here at 0.9.3, see the 2026-05-24 historical entry below) → 1.6.0. Both tools' tested versions are now tracked in one place, config/graph-tools-versions.json — this file references it rather than recording a version number that would drift independently. The old 0.9.3 value is historical provenance, while 1.6.0 is the current tested upgrade target. Fixed: a committed CODEGRAPH_REPORT.md that had been generated inside an unrelated project with no live index behind it; a dual-graph-update.mjs that swallowed every failure and always printed a false success epilogue (replaced by scripts/graphs-cli.mjs's explicit doctor/status/update/report/refresh/bootstrap interface); an orphaned, never-invoked pre-commit script; and commit-SHA-based freshness (unsafe under squash merge) in favor of a worktree-aware content fingerprint (scripts/graphSourceFingerprint.mjs). CodeGraph source/index processing remains local, but its default anonymous telemetry and separate update check are network-capable; use codegraph telemetry off, CODEGRAPH_TELEMETRY=0, DO_NOT_TRACK=1, and CODEGRAPH_NO_UPDATE_CHECK=1 as local controls.

Release gate (2026-08-27 — v1.28.2): Main CI/CD run 33064552219 passed all required and advisory jobs (Quality Gate Node 22+24, Tauri Rust Gate, Build, E2E, E2E Deep Coverage, Storybook, Lighthouse, Visual Regression, CI Success, Deploy to GitHub Pages — the last verified with real post-merge evidence, not just a green check: real Set up job/Deploy to GitHub Pages/Complete job steps and a genuine github-pages Deployments API record for this commit). Codecov project coverage 74.22% across 540 files. The tag-triggered Tauri workflow 33066539094 passed Ubuntu, Windows, and macOS bundle jobs plus GitHub Release generation and latest.json; a parallel Docker workflow built and pushed a container image to GHCR. The release publishes Linux x86_64 (rpm/deb/AppImage) and Windows x86_64 (msi/exe) artifacts with corresponding Minisign .sig files; macOS arm64 ships an unsigned .dmg installer plus a signed .app.tar.gz updater bundle (.sig) — no separate .dmg.sig is published; latest.json present. Platform code-signing and notarization remain separate claims. No macOS x86_64 artifact was available and it is omitted from the updater manifest, matching v1.28.1's pattern — Intel-macOS remains a gated qualification lane (ADR-0021), not yet in the production release matrix. Landed via PRs #519 (DA-06 docs truth), #520 (DA-05 DOCX export), #523 (#522 GitHub Pages deploy fix), and #524 (version bump + DA-03 gap fixes + the full onboarding-install-command doc-truth sweep). The separately-triggered tag CI/CD run (distinct from the main-push run above) hit a non-deterministic tests/e2e/export.spec.ts failure ([Mobile Chrome], once also [chromium]) on the identical commit that passed cleanly on the main-push run; investigation ruled out cross-test/cross-run state persistence (ephemeral ubuntu-latest runners, fresh dev server and browser context per job/test) and traced it to a plausible pre-existing effect-ordering race between hooks/useApp.ts's portal-activation effect and App.tsx's auto-seed-blank-project effect, timing-sensitive rather than deterministic — filed as issue #527. Pre-existing and not introduced by v1.28.2. The published artifacts remain valid; the issue affects a timing-sensitive first-run/onboarding path present in the released code and is being addressed in a bounded follow-up intended for v1.28.3, not by amending v1.28.2. Fixed in v1.28.3 above via PR #530.

Release gate (2026-08-23 — v1.28.1): Main CI/CD run 32616003387 passed all required and advisory jobs. The tag-triggered Tauri workflow 32616003394 passed Ubuntu, Windows, and macOS bundle jobs plus GitHub Release generation and latest.json. The release publishes Linux x86_64 (rpm/deb/AppImage), Windows x86_64 (msi/exe), and macOS arm64 (dmg/app.tar.gz) artifacts; platform code-signing and notarization remain separate claims. No macOS x86_64 artifact was available and it is omitted from the updater manifest, matching v1.28.0's pattern.

Release gate (2026-08-21 — v1.28.0): Main CI/CD run 32533780768 passed all required and advisory jobs, including Node 22/24 quality, Rust gates, build/budget/provenance, E2E, Storybook, Lighthouse, VRT, CodeQL, and Pages deployment. The tag-triggered Tauri workflow 32535622282 passed Ubuntu, Windows, and macOS bundle jobs plus GitHub Release generation and latest.json. The release intentionally publishes signed Linux x86_64, Windows x86_64, and macOS arm64 artifacts; no signed macOS x86_64 artifact was available and it is omitted from the updater manifest.

Quality gate (2026-08-14 — v1.27.1): lint ✅ · typecheck ✅ (tsgo) · i18n:check ✅ (2919 keys × 19 locales) — verified locally per the CI-cloud-first workflow; full CI suite (Quality Gate Node 22+24, rust-tauri, Build, E2E, E2E Deep Coverage, Storybook, Lighthouse, Visual Regression) tracked on the release/v1.27.1main PR before merge/tag.

Quality gate (2026-08-13 — v1.27.0, 5-PR merge): lint ✅ · typecheck ✅ (tsgo) · i18n:check ✅ (2919 keys × 19 locales) · targeted unit tests ✅ across all 5 PRs' correction loops (storageEncryptionService 75 tests, useSettingsView 38, VoiceModelDownloadModal/voiceCommandService/downloadProgressFormat, desktopTray/desktopMenu, ContextPanel/Textarea, and the full post-merge reconciliation suite) · CI Quality Gate (Node 22 + 24), E2E, E2E Deep Coverage, Build, Storybook, Lighthouse, and Visual Regression all green on every one of #342/#343/#344/#345/#346's final commits. #342 (disable/rotate production wiring), #343 (its stacked i18n), #344 (#341 fix), #345 (#332 fix), #346 (#333 item 1) all merged into main. Phase-4 (issue #338) is now fully closed — encryption disable and passphrase rotation are live in the Settings UI.

Quality gate (2026-08-12 — encryption lifecycle + desktop reliability + recovery journal): lint ✅ · typecheck ✅ (tsgo) · i18n:check ✅ (2904 keys × 19 locales) · targeted unit tests ✅ (271 across the full affected storage suite post-merge: protectedStoreMigration, encryptionMigrationJournal, secondaryPayloadStoreAdapter(s), protectedWriteAdmission, idbStoreEncryption, dbService*, sceneRevisionService, aiInferenceCacheService) · CI Quality Gate (Node 22 + 24) green on #337/#339 · codecov/patch ✅ (73.46% → target after adding secondary-adapter payload-shape coverage). #335 (fail-closed lifecycle), #336 (desktop AI/Python hardening for #332/#333), #337 (durable migration journal + secondary-store adapters), #339 (cross-tab write-admission fixing the migration TOCTOU race) all merged into main. PR #310 closed as superseded (docs/PR-310-RECONCILIATION.md). Production disable/passphrase-rotation wiring remains open Phase-4 work — issue #338.

  • extract-zip@2.0.1 OSV ignore (accepted risk, 2026-08-12): GHSA-jmr9-qjv8-65gv / CVE-2026-56876 (CVSS 8.6, unvalidated symlink path traversal when extracting an attacker-controlled zip) flags a transitive devDependency of @puppeteer/browsers (Playwright's browser-binary downloader). No fixed version exists (extract-zip@2.0.1 is the final release), so pnpm.overrides cannot remediate it; documented as an IgnoredVulns entry in src-tauri/osv-scanner.toml, matching the file's existing pattern for unfixable transitive findings. Not exploitable here: only ever extracts Playwright/Chromium's own CDN-hosted zip releases, never a user- or attacker-supplied archive, and ships in no production bundle.

Quality gate (2026-06-17 — language expansion +6 locales): lint ✅ · typecheck ✅ · i18n:check ✅ (2716 keys × 17 locales — fi/sv/hu/is/eu + fa RTL) · placeholder guard ✅ (17 bundles) · targeted unit tests ✅ (LanguageSelector 9 · I18nContext 59 · i18nPlaceholders 33). LanguageSelector exonym labels localized via portal.language.names.* (native endonym stays hardcoded by design). Bulk translation completed for all 10 Beta locales (glossary v2.0, ~44 anchor terms/locale; placeholder-masked, checkpointed): post-run coverage fi 91 % · sv 90 % · hu 91 % · is 92 % · eu 92 % · fa 93 % · ja 99 % · zh 100 % · pt 98 % · el 97 % (Beta MT; human native review tracked). Two bulk-script bugs fixed: (1) glossaryTranslate partial-match left ~1,300 strings partially English → now exact-match only; (2) --all mangled help.json rich HTML → help.json excluded from --all (ALL_SKIP) and kept English fallback for the 6 new langs (tag-dense markup isn't MT-safe; human-review task). New docs/TRANSLATION-GUIDE.md + I18N-GLOSSARY.md v2.0.

Quality gate (2026-06-16 — v1.23.0): lint ✅ · typecheck ✅ · i18n:check ✅ (2709 keys × 11 locales) · placeholder guard ✅ · unit tests ✅ (5807+ / 485 files) · coverage thresholds L74/B60/F67/S72 ✅. Toolchain: Node 22/24, pnpm 11, Vite 8, TypeScript 7 (tsgo).

Quality gate (2026-06-21 — v1.24.0 Critical & Immediate hardening sequence): lint ✅ · typecheck ✅ (tsgo) · i18n:check ✅ (2786 keys × 17 locales) · suppressions ratchet ✅ (52, no new) · targeted unit tests ✅. Stacked PRs A–F: privacy analytics gating (SEC-6), reusable Badge + experimental labeling, coverage (collab-transport/ProForge/copilot, +101 tests), voice consent clarity, device-aware Ollama pull, hygiene/docs. Coverage/E2E/Lighthouse/Stryker remain CI-gate jobs.

Quality gate (2026-06-21 — feature-flag catalog + grouped Settings + ProForge opt-in): lint ✅ · typecheck ✅ (tsgo) · i18n:check ✅ (2793 keys × 17 locales) · parity:check ✅ (0 drifts) · suppressions ratchet ✅ (52, no new) · targeted unit tests ✅ (127: slice 89, catalog 7, flagDependencies 5, FeatureFlagsSection 15, FeatureFlagsAndOverview 11). enableProForge default flipped to opt-in (now 17 on / 6 off); featureCatalog.ts reconciled to all 23 flags with defaultOn derived from the slice (drift now structurally impossible — guarded by tests/unit/featureCatalog.test.ts). Shipped as a standalone PR off main.

Audit 2026-07-29 — CSP / Crypto / Truth (v1.24.2)

Quality gate (2026-07-29): lint ✅ · typecheck ✅ (tsgo) · i18n:check ✅ (2849 keys × 19 locales) · suppressions ratchet ✅ (52, no new) · token-audit ✅ (160, no new) · parity:check ✅ (0 drifts) · pnpm run build + bundle:budget ✅ (154 JS chunks ≤ 6200 KB) · scripts/smoke-prod-build.mjs ✅ (0 real CSP violations, 1 documented known-benign exclusion, wasm: ok) · CI quality job (Node 22 + 24) ✅ — coverage L80.86/F73.78/B66.74/S79.02, ratchet raised to L79/F72/B65/S77. Full execution record: docs/audit/WS-RUN-LOG-2026-07-29.md. Branch fix/csp-truth-hardening-v1.24.2, PR #284.

Source audit (PROMPT-WSS-v1.24.x) empirically re-verified against current disk state before any change landed — all 14 findings CONFIRMED, no baseline drift, base SHA f5f9c1ba.

Finding table

ID Severity Finding Resolution
F-01 🔴 P0 script-src 'self' missing 'wasm-unsafe-eval' on all 5 CSP surfaces — WebAssembly.instantiate blocked in every deployed Chromium browser since 2026-05-27 (faad8f0); the entire advertised local-inference stack never functioned in production Fixed on all 5 surfaces; ADR-0013
F-02 🔴 P0 Unhashed inline <script> in index.html, added in the same commit as the broken script-src, contradicting that commit's own CSP-strategy comment Moved into index.tsx as a same-origin module
F-03 🔴 P0 No frame-src/child-src anywhere → blob: iframes (Binder PDF preview, ManuscriptResearchSplit) fall back to default-src 'self' and are blocked frame-src 'self' blob: added to all 5 surfaces
F-04 🔴 P0 No gate could have caught F-01/F-02 — existing CSP tests check cross-surface consistency only; smoke-prod-build.mjs only listened for pageerror, which CSP violations never fire New 3-layer test architecture: Layer A (consistency, existing) / Layer B (tests/unit/cspCorrectness.test.ts, new) / Layer C (hardened smoke-prod-build.mjs, real violation + WASM probes). docs/CI.md gate-governance table
F-05/F-06 🟠 P1 — resolved 2026-08-14 Desktop API-key encryption previously derived its key from publicly reconstructible material — obfuscation, not encryption PR #363 moved desktop API-key persistence onto the same random, non-extractable generated-key store used by the browser path and closed the Gemini split-persistence bug tracked by #358. The old filesystem-derived helper remains only as unused legacy code in services/fs/fsCore.ts; it is not an active API-key storage path. Full desktop project-file encryption is a separate open R-15 requirement tracked in issue #445.
F-07 🟠 P1 README/CLAUDE.md made a blanket "encrypted at rest" claim; a fabricated tauri-plugin-stronghold OS-keychain claim had zero trace in the codebase; SECURITY-THREAT-MODEL.md never mentioned fsCore.ts/settingsFsStore.ts Doc truth-up: differentiated 4-mechanism table in README, fabricated claim removed, threat-model gained a mitigations row + attack tree
F-08 🟠 P1 Tauri connect-src missing LanguageTool's port and the Hugging Face hosts WebLLM/Transformers.js resolve models from Fixed; scope widened during verification — the LanguageTool port was missing on all 5 surfaces, not just Tauri; the real weight-file CDN (us.aws.cdn.hf.co) traced empirically via curl, not guessed
F-09 🟠 P1 DuckDB-WASM loaded from an unversioned, floating-latest third-party CDN — already unreachable under the (correctly-scoped) worker-src CSP, so this was dead code, not just a supply-chain risk Self-hosted from the pinned npm dependency via scripts/copy-duckdb-assets.mjs (gitignored, ~72 MB, never committed)
F-10 🟡 P2 Two conflicting production URLs; the in-app link and the Italian locale pointed at a dead domain Empirically resolved (live 200 vs. 404 check) — no maintainer input needed. Unified into a PRODUCTION_URL constant + new drift gate
F-11 🟡 P2 Release/tag drift — v1.24.1 tag 5 commits behind HEAD Version bumped to 1.24.2 in this sprint (WS-7); v1.24.2 tagged and published 2026-07-29
F-12 🟡 P2 Storybook built 3× per CI run; ci.yml header still said "StoryCraft Studio" Deduped to 1×, header fixed. Bonus: the Storybook test-runner's flags were invalid for the installed CLI version, so it had never actually executed a single story on any prior CI run — fixed, and its || true (which was itself hiding the failure) replaced with continue-on-error: true
F-13 🟡 P2 Coverage ratchet stale since 2026-06-06; 4 non-blocking CI gates with no stated exit criterion Ratchet raised to CI-measured values (see quality-gate line above); docs/CI.md gained an exit-criteria table for all 4
F-14 🟡 P2 Two live worker generations (v1 + WorkerBus v2) for DuckDB/inference, confirmed via real call-site tracing Resolved. Consolidated as the dedicated migration sprint ADR-0014 called for — ADR-0015, PRs #286–288/#290. Both v1 files deleted; correction loops on the migration PRs also caught and fixed 4 real bugs the untested v2 files had drifted on (dropped SQL params, pool-termination hangs, respawn connection loss, a missing export that broke production builds)

Post-mortem: why no gate caught F-01 for two months

The question this sprint had to answer: would a commit today, shaped exactly like faad8f0 (script-src 'self' with no 'wasm-unsafe-eval', plus an unhashed inline <script>, in an app that ships WASM-based features), fail before reaching production?

Before this sprint: no. The only CSP gate that existed — tests/unit/csp.test.ts/deploymentHeaders.test.ts — asserts that the 5 deployment surfaces agree with each other and that no header is looser than the meta tag. Four (then five) identically broken CSPs satisfy that property perfectly. It is a consistency gate, not a correctness gate, and nothing in the test suite or CI pipeline ever claimed otherwise — the gap wasn't a disabled check or a skipped test, it was a class of defect nobody had written a test for. scripts/smoke-prod-build.mjs loads the actual production build in real headless Chromium — the one place that could have caught this — but listened only for the pageerror DOM event, which CSP violations do not fire (they surface as console warnings and securitypolicyviolation events instead).

After this sprint: yes, in three independent places. Layer B (tests/unit/cspCorrectness.test.ts) directly asserts 'wasm-unsafe-eval' is present, forbidden tokens are absent, and — the single assertion that would have caught the 2026-05-27 defect on day one — that every inline <script> without a src has a matching content hash in script-src. Layer C (the hardened smoke-prod-build.mjs) now captures securitypolicyviolation events and CSP-related console messages, and runs a live WebAssembly.instantiate probe in real Chromium. Losing either layer independently would still leave one standing.

The gate-design lesson, generalized: a passing test suite proves the properties it actually encodes, nothing more. "CSP tests are green" was true and irrelevant — the tests encoded consistency, and the defect was in correctness. The durable fix isn't more tests of the same kind; it's asking, for any gate, "what specific property does this check, and what class of defect would pass it anyway?" — and this sprint's own execution surfaced two more instances of exactly that pattern worth naming: the Storybook test-runner's \|\| true (F-12) turned a hard CLI argument-parsing failure into a green step for as long as it existed — the identical mechanism, one workstream over. And this sprint's own new F-10 drift gate initially excluded locales/it/help.json, the one file with real incident history, on the mistaken belief it was generated content — caught by a reviewer, not by first-pass design. Three separate instances of the same root cause in one sprint is itself the finding: the failure mode is not rare, and "we added a test" is not sufficient evidence that the right property is being tested.

v1.24.1 Local-AI reliability + Dependabot batch (2026-07-28)

Quality gate (2026-07-28): lint ✅ · typecheck ✅ · i18n:check ✅ (2844 keys × 19 localesru/ko added since the v1.24.0 count above; settings.ai.providerStatusUnavailableBrowser + settings.ai.testError.* new keys) · targeted unit tests ✅ (127 across the 7 merged PRs, incl. badge-reconciliation, scan-endpoint-order, CWE-209 stale-request-guard, and binder-pin-reconciliation coverage) · 5807+ tests / 525 files · CI Quality Gate (Node 22 + 24) green on every merged PR.

  • #266 (Ollama/LM Studio/vLLM), two remaining halves fixed, both root-caused with a real build+runtime repro rather than guessed: (1) desktop discovery was still broken after #269 because vite.config.ts externalized @tauri-apps/* even for the Tauri build itself, leaving services/localServerHttp.ts's plugin-http import unresolvable in the packaged app — fixed via a shared isTauriBuild() check (PR #272); (2) the browser status badge and the desktop-only banner were driven by two unreconciled state signals, so the badge always read "Ready" next to a banner saying otherwise — fixed with a distinct "Not available in browser" label (PR #273). Issue closed with a full root-cause explanation.
  • Dependabot backlog triaged — 20 PRs resolved (16 merged, 4 closed as superseded): 14 were straightforward bumps (Actions, small JS/dev-tooling patches, Tauri/Rust crates, the tauri-deps group, a stray log crate bump); the other 6 each had a real breaking-change blocker, root-caused and fixed with real code rather than force-merged or suppressed — biome 2.5.x and dev-tooling/Babel 8 were fixed directly on their own Dependabot branch and merged, while the 4-PR AI-SDK v4 family (@ai-sdk/google/@ai-sdk/openai/@ai-sdk/react/ai) was combined into one coordinated PR #275 and the 4 originals closed as superseded. See TODO.md § v1.24.1 for the full breakdown.
  • Issue #60 (vendor-fork audit): confirmed done (fork 10.3.0-sc2, verify:vendor CI guard); status comment posted, issue stays open per its own standing-reminder policy.
  • CLAUDE.md doc-drift correction: locale roster count (17→19) and per-locale module count (20→21) were stale relative to i18n/locales.ts (the actual SSOT).
  • pnpm run build warnings eliminated: an invalid [dir:ltr]/[dir:rtl] Tailwind arbitrary variant (compiled to the invalid CSS pseudo-class :is(dir:ltr)) replaced with Tailwind's built-in ltr:/rtl: direction variants; 3 dynamic import() calls Rolldown flagged as ineffective (already statically imported elsewhere) converted to static imports.
  • CodeRabbit review-body findings that failed to post inline (GitHub API error on a rate-limited re-review of PR #274) verified against current code and fixed: AiProviderCard a11y (role="status" aria-live="polite") and a genuine CWE-209 stale-request race in handleTest (monotonic request-id guard); ollamaService.ts's plugin_unavailable branch no longer forwards LocalServerError.message directly.

v1.24.0 Dependency Hygiene + Onboarding + Docs Truth-up (2026-06-21, PR F)

  • joi override (accepted risk, KEPT): pnpm-workspace.yaml overrides.joi: ^18.2.1 pins the patched joi pulled transitively via wait-on (Storybook/test-runner wait helper), mitigating GHSA-q7cg-457f-vx79 (unpublished jsdom exposure in @hapi/statehood). Still required — wait-on@9.x still depends on joi. pnpm audit --audit-level=high clean with the override in place; rationale documented inline in pnpm-workspace.yaml and here.
  • extract-zip@2.0.1 OSV ignore (accepted risk, 2026-08-12): GHSA-jmr9-qjv8-65gv / CVE-2026-56876 (CVSS 8.6, unvalidated symlink path traversal when extracting an attacker-controlled zip) was published/GitHub-reviewed 2026-08-12, freshly flagging a transitive devDependency of @puppeteer/browsers (Playwright's browser-binary downloader). No fixed version exists (extract-zip@2.0.1 is the final release — pnpm.overrides cannot remediate an unpatched advisory), so pnpm.overrides doesn't apply here; documented as an IgnoredVulns entry in src-tauri/osv-scanner.toml instead, matching the file's existing pattern for unfixable transitive findings. Not exploitable in this project: only ever extracts Playwright/Chromium's own CDN-hosted zip releases, never a user- or attacker-supplied archive, and ships in no production bundle.
  • SBOM — deferred (decision): evaluated a @cyclonedx/cyclonedx-npm generate-on-tag step; not adopted in 1.24.0 to keep the release scope tight. Socket Security (PR + project report) already runs every CI run and provides dependency-risk + an SBOM dashboard, so the marginal value is low. Revisit when a formal SBOM artifact is required by a downstream consumer.
  • README metric drift fixed: scripts/sync-readme-metrics.mjs had its locale count hard-coded to 11, so its regexes stopped matching after the 11→17 expansion and silently froze the key count at a stale value. Locale count is now dynamic (counts locales/ dirs) and the regexes match any digit count; re-run → README reads 2786 keys × 17 locales with the drift guard green.
  • Docs truth-up: corrected the stale public/sw.js "must hand-sync APP_VERSION" note in CLAUDE.md (it is auto-synced by scripts/sync-sw-version.mjs + sync-tauri-version.mjs via predev/prebuild); refreshed the stale 5-locale / 2 594 keys × 11 locales strings in CONTRIBUTING.md + .github/copilot-instructions.md to 17 locales.
  • Onboarding: added a prominent "Do NOT run heavy suites locally" callout + a Minimal Change Checklist to CONTRIBUTING.md, and mirrored the heavy-suite warning into .github/copilot-instructions.md (its old preflight wrongly told contributors to run test:run + build on every commit — now aligned with the low-end CI-first policy already in AGENTS.md).

v1.23.0 Post-Release Documentation Perfection Pass (2026-06-16)

Scope: Bring the entire doc corpus into lockstep with the shipped v1.23.0 code, curate/restructure historical material, clear the Dependabot queue, and complete the GitHub release/package rebrand.

  • Metric reconciliation: scripts/sync-readme-metrics.mjs re-run → README now reads 2709 keys / 485 test files (was 2706 / 481). AUDIT header (version, quality-gate, follow-up chain) advanced from the stale v1.22.0 label to v1.23.0 with current numbers.
  • Feature-flag truth-up: docs claimed 21 flags; the slice has 23. CLAUDE.md rewritten — the default model had inverted (new installs get the full set; only 5 default off: enableRtlLayout, enableVoiceSupport, enableVoiceWasm, enableGlobalCopilot, enableLocalFirstSync). Retired/promoted flags (enableCodexAutoTracking, enableCrossProjectSearch, enablePlotBoardV2, enableCloudSync) removed from the live list. Three slice JSDoc (default: true) annotations that contradicted the runtime defaults corrected to (default: false).
  • Bundle budget: current help/docs aligned to 6200 / 2500 (AGENTS.md); docs/CODE_QUALITY.md confirmed not to hardcode the figure.
  • Roadmap/TODO: v1.23 section flipped from "ACTIVE / target 2026-06-20" to RELEASED 2026-06-16 with the real deliverables; forward P1/P2 work consolidated into a single Upcoming — v1.24 / v2.0 Foundation block.
  • Corpus restructure: archived finished plans into docs/history/ via git mvEDGE_AI_PERFECT_PLAN.md, EDGE_AI_ZWISCHENSTAND.md, CHECKPOINT-2026-05-24.md, CHECKPOINT-2026-06-06.md, WIEDERAUFNAHME.md; inbound links repaired (0 broken relative links).
  • Rebrand residue: ADR-0008 present-tense "StoryCraft is offline-first" → "WorldScript Studio". Remaining storycraft* hits are intentional historical records (CHANGELOG version entries, archived sprints) or technical identifiers that were genuine at their version — left intact.
  • GitHub releases: 6 historical release titles renamed StoryCraft → WorldScript (v1.3.0, v1.5.0, v1.7.0, v1.17.0, v1.20.0, v1.21.0); v1.23.0 release/tag verified correct (latest, full changelog, tag → fbaa33c3).
  • GitHub Packages: orphaned storycraft-studio GHCR container image to be deleted so only worldscript-studio remains (requires delete:packages token scope — pending maintainer auth refresh).
  • Dependabot hardening: added cooldown: default-days: 7 to all three .github/dependabot.yml ecosystems so newly released versions age 7 days before a PR is opened — matched to the pnpm-workspace.yaml minimumReleaseAge: 10080 (7-day) supply-chain quarantine enforced at pnpm install-time, so a version is never PR'd before pnpm install --frozen-lockfile would accept it (.github/dependabot.yml is the source of truth for the cooldown value). Open queue handled per the CodeAnt Correction Loop: #150 candle-nn merged; #151 candle-core rebased + re-running; #152 dev-tooling fixed at root (dual playwright-core deduped to 1.61.0 via pnpm-workspace.yaml override); #154/#155 are blocked solely by the minimumReleaseAge quarantine (packages too fresh — working as designed) and clear once aged.

v1.23 i18n Interpolation Bug-Class Fix + Regression Guard (2026-06-14)

Scope: Two user-reported runtime i18n bugs traced to a whole bug class that i18n:check (parity-only) structurally cannot catch.

  • Bug 1 — single-brace placeholders never interpolate. contexts/I18nContext.tsx only substitutes {{token}}; the entire copilot.json module plus 2 keys each in objects.json/settings.json were authored with single-brace {token}, so they rendered literally (user saw Du bist hier: {view}). Converted 292 occurrences → {{…}} across all 11 locales. copilot.contextLabel already passes a localized view name (t(viewNavigationLabelKey(currentView))), so the brace fix alone makes it render the real page name.
  • Bug 1b — translated placeholder NAMES. es/pt had localized the param names ({count}{contar}, {seconds}{segundos}, and pt roster.resultCount {{count}}{{contagem}}), which never match the names the code passes. Reverted to canonical English tokens.
  • Bug 2 — missing common.abort key. ProForgeDashboard.tsx calls t('common.abort') but the key existed in no locale, so the raw key rendered as the pipeline abort button. Added to all 11 (de "Abbrechen", es "Interrumpir", fr "Interrompre", it "Interrompi", pt "Interromper", ja/zh "中止", el "Διακοπή"; ar/he EN fallback per RTL-stub policy).
  • Systemic guard. New tests/unit/i18nPlaceholders.test.ts scans every shipped bundle and fails CI on (a) any single-brace placeholder and (b) any locale placeholder name absent from the English source — closing the gap that let all of the above ship green. The token-consistency check caught the third bug (pt contagem) that manual greps missed.

Quality gate (2026-06-14): lint ✅ (1329 files) · typecheck ✅ · i18n:check ✅ (2646 keys × 11) · placeholder guard ✅ (21) · copilot + proForge suites green.

v1.23 Bundle Split & AI-Settings i18n Fix (2026-06-14)

Scope: Reduce precache weight and tighten the bundle gate (PR #130); fix AI-settings localization regression (PR #129).

  • Bundle budget tightened (PR #130, 4bc237d): ceilings lowered --max-kb 6500 --max-entry-kb 4000--max-kb 6200 --max-entry-kb 2500 in both package.json bundle:budget and scripts/check-bundle-budget.mjs defaults (still single-source-of-truth, just new values). This supersedes every 6500/4000 figure in earlier (dated) AUDIT/TODO/ROADMAP entries.
  • ai-core runtime chunks split (PR #130): new vendor-webllm / vendor-transformers / vendor-onnx proxy modules give Vite stable lazy-chunk names (replacing generic lib-*). Heavy-runtime manualChunks branches now match before the vendor-ai-core catch-all (a CodeAnt-flagged ordering bug had swept WebLLM/ONNX/Transformers back into the precached vendor-ai-core chunk). Verified sizes: vendor-webllm ≈ 6.0 MB, vendor-onnx ≈ 401 KB — both excluded from SW precache; the small vendor-ai-core orchestration layer stays precached. The largest chunk (vendor-webllm) is the binding constraint against the 6200 KB per-chunk ceiling (tight margin). bundle:report script added for CI trend tracking. smoke:prod mounts clean.
  • AI-settings localization (PR #129, c3fcf12): settings.openRouter.* + settings.aiMode.* keys were shipping as English placeholders in all non-EN locales (i18n:check verifies key parity, not translation quality, so the gate stayed green). Translated across all 11 locales; bundles rebuilt. Regression guard added to the smoke-test protocol (step 1.9).

v1.23 AI Execution Mode Section — Localization, Modern UI & A11y Hardening (2026-06-14)

Scope: Lift the AI Execution Mode picker (components/settings/AiExecutionModeSection.tsx, Settings → AI & Models) to the modern design system: finish localization (the only hardcoded strings lived in the related components/copilot/AiModeIndicator.tsx chip), adopt the Card shell + a native radiogroup, add dynamic per-mode capability hints, and add the missing component tests. Branch: feat/ai-execution-mode-perfection

Quality gate (2026-06-14): lint ✅ · typecheck ✅ · i18n:check ✅ (2639 keys × 11 locales, +7 settings.aiMode.*) · suppression-ratchet ✅ (no new biome-ignore) · targeted unit tests ✅ (AiExecutionModeSection 10, aiModeService 25).

Changes

  • Localization: AiModeIndicator no longer hardcodes OpenRouter / OpenRouter Free / OR ⚠ / /20 RPM — replaced with i18n keys settings.aiMode.indicator.openRouter[Free], …orShort, …rpm ({{count}} locale-formatted). Added across all 11 locales.
  • Modern UI: section wrapped in Card / CardHeader / CardContent (consistent with OpenRouterSection); kept --sc-* tokens, no dark: prefixes, no new colors.
  • A11y: mode cards are now a true ARIA radio group — native <input type="radio"> (visually-hidden, shared name) inside styled <label>s, giving free arrow-key/Home/End navigation + roving tabindex + focus-visible ring; container role="radiogroup" + aria-labelledby; mode change announced via useAnnounce() (reuses settings.aiMode.activeLabel).
  • Edge-case hardening (dynamic hints): synchronous detectWebGpuSupport() drives a WebGPU-absent warning for Local/Eco; online/offline event listeners drive a Cloud-offline warning and a Hybrid-offline info note — role="status" + aria-live="polite", --sc-warning/info tokens. New keys settings.aiMode.hint.{webgpuMissing,cloudOffline,hybridOffline}.
  • Tests: new tests/unit/settings/AiExecutionModeSection.test.tsx (radiogroup + checked state, click dispatch + announce, no-op on same mode, native grouping, all 3 capability hints). Service layer (aiModeService, listenerMiddleware) audited — routing/sync correct, unchanged.

v1.23 OpenRouter Settings Section — Localization, Modern UI & Hardening (2026-06-13)

Scope: Harden the OpenRouter provider settings panel (components/settings/OpenRouterSection.tsx) with full i18n coverage, the design-system searchable Select, API-key validation, model-catalog fetching/caching, AI-mode awareness, and error-boundary wrapping. Branch: feat/openrouter-section-perfection

Quality gate (2026-06-13): lint ✅ · typecheck ✅ · i18n:check ✅ (2632 keys × 11 locales) · targeted unit tests ✅ (Select 7, OpenRouterSection 10, openrouterModels 12, openrouterProvider 19).

CodeAnt AI review (11/11 addressed)

# File Comment Fix
1 components/settings/OpenRouterSection.tsx Hardcoded free-model labels Replaced with i18n keys settings.openRouter.freeModel.* resolved via t(...)
2 components/settings/OpenRouterSection.tsx Model-catalog fetch ungated Added assertCloudAiAllowed('openrouter') pre-flight; UI shows settings.openRouter.policyBlocked
3 components/settings/OpenRouterSection.tsx Test-connection ungated Same policy gate; returns policy-blocked message if disallowed
4 components/ui/Select.tsx Search input stops all keydown propagation Removed the blanket onKeyDown stop so Escape and all other keys bubble normally
5 services/ai/openrouterModels.ts /models fetch ungated assertCloudAiAllowed('openrouter') before any outbound request
6 services/ai/openrouterModels.ts Key-validation probe ungated Same policy gate before validateOpenRouterKey network call
7 services/ai/openrouterModels.ts Malformed localStorage cache cast Added isValidCacheEntry() runtime shape check (fetchedAt number + models array)
8 components/settings/OpenRouterSection.tsx Model catalog fetched without API key Component keeps storedKey, passes it to fetchOpenRouterModels(storedKey ?? undefined)
9 components/settings/OpenRouterSection.tsx Select hidden when catalog fetch fails Select now always renders with static free-tier + custom options; error shown as non-blocking alert
10 components/settings/OpenRouterSection.tsx No re-fetch after key save/clear storedKey state is a dependency of the catalog effect; cache clear triggers refresh with new credentials
11 components/ui/Select.tsx Search input blanket onKeyDown stop (duplicate path) Same fix as #4 — propagation stop removed

Delivered

# Area What shipped
i18n locales/en/settings.json + locales/en/common.json 32 new keys for OpenRouter UI and generic search/no-results labels; propagated to all 10 non-EN locales and rebuilt bundles.
UI primitive components/ui/Select.tsx Optional searchable prop with case-insensitive filtering across options and groups; keyboard closure and ARIA listbox behavior retained.
Model catalog services/ai/openrouterModels.ts New service: fetches OpenRouter /models, caches in localStorage (1 h TTL), validates API keys, normalizes pricing/context-length fields.
Provider hardening services/ai/providers/openrouterProvider.ts Shared request builder; timestamp-array trimming on every RPM record; callbacks.onError invoked before stream throws; optional circuit-breaker localStorage persistence.
Settings panel components/settings/OpenRouterSection.tsx Rewritten with searchable Select, key save/remove/test-connection flows, model fetch loading/error states, AI-mode warnings (local/offline), inline status badges, and screen-reader alerts.
Error boundary components/SettingsView.tsx OpenRouterSection wrapped in ViewErrorBoundary with translated viewLabel.
Tests tests/unit/settings/OpenRouterSection.test.tsx 7 new tests covering toggle, key save/remove, model selection, custom model commit, and circuit reset.

Files changed (non-locale)

File Change
components/ui/Select.tsx Added searchable prop and search input filtering
tests/unit/Select.test.tsx +3 tests for searchable filtering and empty state
services/ai/openrouterModels.ts New model catalog + key validation service
services/ai/providers/openrouterProvider.ts Shared builder, RPM trimming, error callback, CB persistence
tests/unit/ai/openrouterProvider.test.ts +4 tests for models, validation, CB persistence, RPM trimming
components/settings/OpenRouterSection.tsx Full rewrite with modern Select and validation flows
components/SettingsView.tsx ViewErrorBoundary wrapper for case 'openrouter'
tests/unit/settings/OpenRouterSection.test.tsx New 7-test suite

Quality gate (2026-06-03 — RTL/i18n Beta, C-6): lint ✅ (1097 files, 0 warnings) · typecheck ✅ · i18n:check ✅ (2259 keys × 7 localesar/he now in the parity gate, no longer English stubs) · build + smoke:prod (font/index.css change — verified). ar/he UI fully translated across all 18 modules (help.json English fallback for Beta); Noto Sans Arabic/Hebrew + Naskh fonts wired (index.tsx, --font-ui-rtl/--font-editor-rtl tokens); RTL layout: [dir="rtl"] CSS net (text-align/float flips, .rtl-auto-mirror, .rtl-keep-ltr), shell logical-property conversion (Sidebar/Modal/CommandPalette/Toast), canvas LTR islands (PlotCanvas/CharacterGraphView keep coordinate math LTR), WelcomePortal ar/he selectors. "(Beta)" labels retained in language pickers. Glossary: docs/I18N-GLOSSARY-RTL.md. Remaining (community): native-speaker review + help-prose translation.

v1.21 Deep Audit Correction — 2026-06-09 (PR #89)

Scope: Whisper WASM model download UI (P1-2), 3 CodeAnt PR findings, CloudSync LWW, locale quality sweep Branch: feat/deep-audit-correction-v1.21 | Commit: b59e0ec

Quality gate (2026-06-09): lint ✅ · typecheck ✅ · i18n:check ✅ (2348 keys × 11 locales) · unit tests ✅ (22/22 cloudSyncBackend + pandocTauri)

Delivered

# Area What shipped
P1-2 Voice WASM download UI VoiceModelDownloadModal — progress bar, cancel, retry, per-model (STT/TTS); triggered from VoiceSettingsSection via separate Whisper + Kokoro buttons. VoiceCommandService.downloadVoiceModels(type, signal?) drives the download pipeline.
CodeAnt 1 (HIGH) locales/ja/writer.json Restored canonical {{title}} / {{selection}} placeholders — they had been localised to {{タイトル}} / {{選択内容}} causing them to render as literal strings at runtime.
CodeAnt 2 VoiceSettingsSection.tsx Added dedicated "Download TTS Model" button; previously hardcoded to stt made the Kokoro TTS download path unreachable.
CodeAnt 3 VoiceModelDownloadModal.tsx Wired AbortController via abortRef — cancel button and modal onClose now abort any in-flight fetch; all async checkpoints guard signal.aborted.
P2-1 CloudSyncBackend Last-Write-Wins conflict-resolution metadata: every save* call now wraps payload in { data, meta: { lastModified, deviceId, version } }; load* unwraps transparently. +8 unit tests (19 total).
i18n Locale quality sweep Translation corrections in pt/el/ja/zh/de/fr/es/it/ar/he; all 11 bundles rebuilt (2348 keys).

Files changed (non-locale)

File Change
components/voice/VoiceModelDownloadModal.tsx New component — WASM model download UI with AbortController
components/settings/VoiceSettingsSection.tsx Separate STT + TTS download buttons
services/voice/voiceCommandService.ts downloadVoiceModels(type, signal?) export
services/cloudSync/cloudSyncBackend.ts LWW saveWithMetadata / loadWithMetadata helpers
tests/unit/cloudSyncBackend.test.ts +8 LWW assertions
tests/unit/pandocTauri.test.ts +3 edge-case assertions

Deep Correction Plan — 2026-06-06

Scope: Tauri Release-Unblock, Coverage C-7, AI Resilience, i18n Finalization, v2.0 Foundation Status: Plan approved, execution started Key Risks: Tauri Windows-Runner, Coverage Gap (~90 Tests needed), Whisper WASM Integration Success Metrics:

  • Tauri: 3 OS bundles + signed updater manifest
  • Coverage: L85/B75/F80/S82
  • Bundle: Entry ≤ 4000 KB, Total ≤ 6500 KB
  • i18n: 11 Locales, ≤ 5% Beta placeholders

Quality gate (2026-06-06 — Phase 3 i18n Expansion): lint ✅ · typecheck ✅ · i18n:check ✅ (2339 keys × 11 locales — ja/zh/pt/el added) · build ✅ · tests ✅ (53 I18nContext tests including Intl APIs). ja/zh/pt/el Beta languages added with English placeholder text; Noto Sans JP fonts via Google Fonts CDN; Intl APIs integrated (PluralRules, NumberFormat, RelativeTimeFormat, Collator, ListFormat, DisplayNames) with caching; SUPPORTED_LOCALES metadata added; Documentation: docs/I18N-PLURALS.md, docs/I18N-NUMBERS.md, docs/I18N-LOCALE.md, docs/I18N-RELATIVETIME.md, docs/I18N-COLLATION.md, docs/I18N-LISTFORMAT.md, docs/I18N-DISPLAYNAMES.md, docs/I18N-GLOSSARY.md.

Help & Settings content augmentation (2026-06-03): new Help category "Advanced & Power Features" (helpCatalog.ts) — 8 articles (Languages/RTL, LoRA fine-tuning, ProForge, Voice, At-rest encryption, Cloud Sync, Plugins, Adaptive AI/GPU/Eco) translated in en/de/fr/es/it (ar/he English fallback). Offline help-RAG (helpDocRetrieval.ts) grown 13 → 16 chunks (languages-rtl, privacy-local-ai, advanced-editing). In-app Settings Guide (SettingsGuideSection.tsx) completed — the previously-undocumented live categories Fine-Tuning (LoRA), Community, and Plugins now appear with title/desc + search hints in all 7 locales. +23 keys (help) +6 keys (settings) → 2259 keys × 7 locales. Tests green (helpCatalogIntegrity, helpDocRetrieval, helpSearchIndex, SettingsGuideSection).

SW i18n-staleness fix (2026-06-03): returning users kept stale translations after a content-only i18n update (symptom: switching to ar/he flipped layout to RTL but text stayed English until a hard reload / cache clear). Root cause: public/sw.js cached /locales/**/bundle.json with stale-while-revalidate against worldscript-dynamic-v${APP_VERSION}; APP_VERSION is synced from package.json and doesn't bump on i18n-only changes, so the old (stub) bundle was served first and only revalidated in the background. Fix: locale handler switched to network-first with cache fallback — fresh strings whenever online, offline still served from cache. Regression guard: tests/unit/swLocaleStrategy.test.ts (asserts network-first ordering, no stale-first return). Changing sw.js also triggers a new SW install → controllerchange auto-reload (register-sw.ts) for existing clients.

Quality gate (2026-06-02): lint ✅ · i18n:check ✅ (2236 keys × 5 locales; lora + common.next/back un-orphaned) · typecheck ✅ · tests ✅ · feature-parity 0 criticals · LoRA view routed (Phase 2.2) + Phase 3 coverage tests (33) · Stryker now manual-only · Coverage/E2E: CI-only

Production hotfix (2026-06-02): Live blank screen (init_locales is not defined) root-caused to rolldown production DCE dropping zod's lazy __esm init wrappers — zod declares "sideEffects": false, so its side-effect-only modules (locales, from-json-schema) were stripped while their init calls survived. Fixed via patches/zod@4.4.3.patch (sideEffects: true); rollupOptions.treeshake is ignored by rolldown-vite. Systemic gap closed: the E2E suite runs vite dev, so the production rolldown bundle was never exercised — added pnpm run smoke:prod (headless-browser mount check on the built dist/) to the CI build job, plus an unhandledrejection startup-error handler in index.tsx. Toolchain: Node 22/24, pnpm 11, Vite 8, TypeScript 7 (tsgo), Biome 2, Vitest 4.1, Playwright 1.60, Tailwind CSS 4

TypeScript 7 Migration — 2026-06-04

Scope: Migration from TypeScript 6.0.3 to TypeScript 7.0 (Go-based tsgo) for improved type-checking performance.

Changes

File Change
package.json Added @typescript/native-preview@beta and @typescript/typescript6 alias
tsconfig.tsgo.json New tsgo-specific config (excludes vite/client types)
.github/workflows/ci.yml Updated typecheck step to use tsgo
pnpm-workspace.yaml Disabled strictPeerDependencies for tsgo compatibility
pnpm-workspace.yaml Added strictPeerDependencies: false
docs/TS7-MIGRATION.md Migration guide created

Verification

  • npx tsgo --versionVersion 7.0.0-dev.20260421.2
  • pnpm run typecheck → ✅ No type errors
  • pnpm run build → ✅ Build successful (1m 27s)
  • pnpm run lint → ✅ 1111 files, 0 warnings

WorkerBus v2 Phase 3 + Tauri-Build Unblock — 2026-06-03

Scope: Complete the WorkerBus v2 Rust half (TODO.md line 26) and verify it natively. Branch feat/workerbus-v2-phase3-rust (PR #70).

Phase 3 delivered: src-tauri/src/commands/task_supervisor.rs + commands/mod.rsworldscript_task_supervisor_ping (version) + worldscript_task_supervisor_submit (taskType dispatcher; unknown/bad-payload → {success:false,error}, never a hard Err, matching the RustTaskResultEvent honest-failure contract). First native task text.analyze (word/char/sentence/syllable + Flesch Reading Ease, pure Rust, 8 #[cfg(test)] tests). TS front-end services/rustTaskSupervisor.ts analyzeTextViaRust() probes isRustComputeAvailable() before routing (Rust-only task never hits the web pool; null → JS fallback), 5 unit tests. Verified locally: biome + tsc + 5 TS tests ✅.

Verification method — Rust has no PR-CI gate. tauri-build.yml runs only on workflow_dispatch / v* tags, and the crate cannot be compiled on the dev host. Verified by dispatching tauri-build.yml on the branch: the crate now compiles clean (Finished release in ~4m18s) and bundles .deb / .rpm / .AppImage on ubuntu.

Root-caused 3 pre-existing build blockers (tauri-build red since 2026-05-30, never diagnosed):

# File Issue Fix
1 src-tauri/Cargo.toml specta = "2" / tauri-specta = "2" unused (no .rs ref) and unresolvable ("2"=^2 stable, only 2.0.0-rc.* exist) → resolution fails before any compile Removed both dead deps
2 src-tauri/src/lora.rs LoraEnvReport deserialized via serde_json::from_str (lora.rs:209) but derived only Serialize → E0277 broke whole-crate compile Added Deserialize
3 task_supervisor.rs json import test-only; RustTaskRequest wire-contract fields not read by dispatcher json → test mod; #[allow(dead_code)] with note

Remaining (not code): tauri-build still exits non-zero at the very end on the updater signing step (incorrect updater private key password: Missing comment in secret key) — a malformed TAURI_SIGNING_PRIVATE_KEY repo secret; the app + all 3 bundles build fine. The Windows runner separately fails in the ./.github/actions/setup composite (self-installer 3221226505) — env/infra, not Rust. Both are maintainer secrets/infra tasks, tracked for follow-up.

Perf Hardening — 2026-06-02 (Phase 2.3 — Pipeline-cache unification + self-review)

Scope: Local-AI inference pipeline caching; meta-review of the same day's Claude-co-authored commits.

Findings & fixes

# File Issue Fix
1 workers/inference.worker.ts:34-98 + workers/v2/inference.worker.ts:21-56 Byte-identical pipeline-LRU logic duplicated across both workers; neither disposed the evicted pipeline → VRAM/RAM leak (same bug-class as WebLLM eviction, 2026-06-01 #1) Extracted services/ai/pipelineLruCache.ts (PipelineLruCache<T>): dispose-on-evict, in-flight load dedup, injectable clock for deterministic tests. Both workers now consume it; duplication removed.
2 services/ai/aiRetry.ts (self-review) Sound (exp-backoff + full jitter + Retry-After precedence + hostile-value clamp + injectable RNG). Gap: no property-based invariant tests ✅ Added invariant tests for computeRetryDelayMs (exponential, non-decreasing, cap, jitter∈[0,capped)) + parseRetryAfterMs (ms/seconds/string/header/clamp) + a Retry-After-beats-backoff integration test. 19 tests total.
3 hooks/useLoraView.ts:70-72 (self-review) projectId ? selectDatasetForProject(projectId) : () => [] recreates the memoized selector each render useMemo-wrapped selector keyed on projectId; module-level stable empty selector. 12 existing tests still green (behavior-preserving).

Non-finding: latency telemetry is already recorded at the facade (localAiFacade.tslocalWorkerBus.recordResult(elapsedMs, …)), so no new worker→main telemetry hop was needed.

Verification: lint ✅ (1095 files, 0 warnings) · typecheck ✅ · pipelineLruCache.test.ts (13) + inferenceWorker.test.ts (7) ✅ — existing worker tests unchanged ⇒ behavior-preserving. Coverage/E2E/smoke:prod: CI.

CodeAnt PR #69 review (3/3 resolved at root): (1) set() now disposes the previous value when a live key is replaced; (2/3) PipelineLruCache.safeDispose() centrally swallows sync throws + async rejections so a failing dispose() can't surface as an unhandled rejection in either worker. +4 tests.

Phase 2.4 (coverage) follow-on: correction — sileroVadEngine.ts (5 tests) + kokoroTtsEngine.ts already had tests since 2026-05-31 (TODO "0 tests" was stale). Real gap filled: Kokoro cancel()/pause()/resume()/dispose() + no-WebAssembly branch (+4 → 10 tests). Inference-worker LRU covered via pipelineLruCache.test.ts. CI-measured coverage 75.15 L / 61.23 B / 67.84 F / 73.14 S (PR #69, both Node 22/24) → vitest.config.ts thresholds ratcheted L72→74 / F64→66 / B58→60 / S70→72 (~1 pt margin under measured). C-7 target stays L85/B75/F80.

Phase 4 (ADRs + onboarding): docs/adr/0001-state-management-boundaries.md (Redux vs Zustand — demotes the recurring "P0 dual-state" audit flag to a settled decision, not a consolidation) + docs/adr/0002-local-ai-stack-layering.md (fallback chain + shared infra + honest-degradation contract); README ⚡ Quick Start (60 seconds). Docs-only.

Post-crash Session — 2026-06-01 (CI Hardening + CodeAnt + E2E Stabilisation)

Scope: CI pipeline correctness, AI core quality, E2E test reliability, documentation.

Issues Resolved

# File Issue Fix
1 webllmOptimizer.ts:78-80 No dispose() on engine cache eviction → GPU memory leak Added void entry.engine.dispose?.() before engineCache.delete()
2 webllmOptimizer.ts:129-131 releaseWebLlm only deleted one power-preference variant Now deletes both high-performance + low-power variants when preference unspecified
3 listenerMiddleware.ts:398 releaseAllOnnxSessions() called without await (async fn) Added await — ensures GPU session cleanup completes before re-enabling
4 computeShaderFactory.ts:80-106 getComputeDevice race condition — concurrent callers each create a GPU device Promise-mutex (deviceInitPromise) serialises concurrent calls
5 listenerMiddleware.ts:368 Adaptive engine window gate not set on cold-start (flag already true from localStorage) initAdaptiveAiOnStartup() called from App.tsx on mount
6 localAiDeviceProfiler.ts:284-286 recommendBackend() returned transformers-webgpu even when WebGPU unavailable Changed to onnx-wasm for the no-GPU / high-memory path
7 adaptiveAiEngine.ts:220-225 WarmedModelEntry missing task field Added task: AiTaskType to interface and set on prewarmModel
8 telemetryService.ts Telemetry recorded even when enableDuckDbAnalytics is off setTelemetryEnabled() gate; App.tsx syncs flag on every change
9 listenerMiddleware.ts:372 Direct window access without SSR/worker guard Added typeof window !== 'undefined' guard
10 AiSections.tsx:57 useAdaptiveAi hook mounted even when AI feature disabled Parent-level conditional mount ({adaptiveAiEnabled && <AdaptiveAiHardwarePanel />})
11-14 AdaptiveAiHardwarePanel.tsx 7 hardcoded strings (capability names, available/unavailable, compute shaders label) Replaced all with t() calls; 7 new i18n keys × 5 locales

E2E Stabilisation

Test Category Previous State Fix
Welcome/command-palette axe contrast 2.03–2.91:1 (fails WCAG AA) WelcomePortal design tokens; waitForSpaReady waits for theme class
World-view / plot-board navigation strict mode violation (multiple locator matches) /World Building/i, /Scene Board/i exact labels
LoRA wizard 13 failures (view not routed in App.tsx) test.skip(true, ...) — Phase 2.2 pending
Export flow "Apply Outline" never appeared seedGeminiApiKey uses role="switch" + disables localStorageOnly
Scene Board ARIA role="tablist" with button children (critical) role="toolbar" on mode selector group
Act Swimlane ARIA <ul> with [role=button] div children (serious) SceneCard wrapped in <li>
VRT Missing baselines (all tests failed) 4 Chromium 1280×720 baseline PNGs committed

CI Pipeline Improvements

  • pnpm-lock.yaml: regenerated after @xenova/transformers@huggingface/transformers migration
  • aiCoreFallbackPaths.test.ts: Layer-3 now uses Xenova/distilgpt2 (distinct from Layer-2's SmolLM2)
  • prune-deployments.yml: fixed to prune all environments (Production, Preview, github-pages); 156 records deleted; actions/github-script v7 → v9 (node24)
  • actions/cache: storybook job upgraded v4.2.3 (node20) → v5.0.5 (node24) before June 16 2026 deadline
  • All 18 GitHub Actions are on node24 — no node20 deprecation warnings remain
  • graphifyy==0.8.26: pip install pinned by SHA256 hash (Scorecard Pinned-Dependencies #72)

Edge-AI Perfection Cycle — 2026-05-31 (Phases 0–7 Complete)

Scope: Edge-AI inference stack hardening, integration, benchmarks, telemetry.

Phase-by-Phase Summary

Phase Description Status
0 Diagnostics & baseline
1 ONNX/Transformers.js real inference, feature flags, gateway
2 Device profiler + adaptive AI engine
3 WebLLM, ONNX, WebNN optimizers (cached, prewarmed)
4 WGSL compute shaders (textProcessing, attention, feedForward, kvCache)
5 Domain integration: RAG GPU cosine, useAdaptiveAi hook, hardware panel, listener, voice eco-mode, i18n
6 benchmarkService + telemetryService (local DuckDB, no cloud)
7 Final validation: lint, i18n parity, test suites, tsconfig fix, AUDIT/AGENTS update

Known-debt items resolved

  • @domain/ai-core missing from tsconfig.json paths → fixed (paths entry added)
  • WGSL shader fetch path broken in production → fixed (Vite ?raw imports)
  • attentionForward() parallel kernel abandoned → removed (serial kernel retained)
  • ONNX Layer-2 no tab-leader guard → fixed
  • MLC→ONNX model ID mismatch → fixed (size-aware mapping table)
  • RAG CPU-only cosine → fixed (GPU batch path via computeShaderFactory)
  • Voice eco-mode not coupled to battery → fixed (ecoModeService subscriber)

New files (Edge-AI Cycle)

  • services/ai/localAiDeviceProfiler.ts — hardware detection, 30s TTL cache
  • services/ai/adaptiveAiEngine.ts — backend/model selection with LRU warmup
  • services/ai/computeShaderFactory.ts — WGSL pipeline factory (?raw)
  • services/ai/benchmarkService.ts — micro-benchmarks per task/backend
  • services/ai/telemetryService.ts — local DuckDB + localStorage telemetry
  • packages/ai-core/src/webllmOptimizer.ts — WebLLM engine cache
  • packages/ai-core/src/onnxRuntimeEngine.ts — ONNX session cache
  • packages/ai-core/src/webnnBridge.ts — WebNN detection + DirectML heuristic
  • hooks/useAdaptiveAi.ts — React hook for adaptive AI engine state
  • components/settings/AdaptiveAiHardwarePanel.tsx — device capability panel

WGSL shaders (all ?raw bundled)

  • textProcessing.wgsl — batchCosineSimilarity, vectorAdd, vectorScale
  • attention.wgsl — attentionForwardSerial (parallel kernel deferred: needs f32 atomic reduce)
  • feedForward.wgsl — mlpForward with GELU (max 4096 intermediate units, clamped by factory)
  • kvCache.wgsl — appendKvCache, applyRopeToCache

Follow-up Audit — 2026-05-31 (Phase 0 — Edge-AI Performance Audit)

Scope: AI stack (packages/ai-core, services/ai/, services/voice/, workers/), GPU/WebNN/compute infrastructure, Stryker coverage gaps, feature-flag readiness for v1.20+.

Environment Fix

  • @xenova/transformers and @mlc-ai/web-llm were missing from packages/ai-core/node_modules despite being in pnpm-lock.yaml. Cause: optionalDependencies in workspace package were not installed on the low-end dev machine (likely due to a previous --no-optional install or prune). Fix: pnpm install --prefer-offline restored both packages. Typecheck now passes cleanly.

Quality Gate Status (Phase 0 baseline)

Gate Status Detail
lint 1022 files, 0 errors, 21s
typecheck 0 errors (after env fix)
i18n:check 2129 keys × 5 locales (+ ar/he)
localAiFacade tests 6/6 passed
aiProviderService tests 46/46 passed
fallbackChain tests 21/21 passed

Critical Findings (P0 — must fix before v1.20)

ID File Finding Proposed Fix
P0-F1 packages/ai-core/src/index.ts ONNX Runtime Web is a stub: runLocalTextGeneration() detects ort.InferenceSession.create but returns a diagnostic string instead of running a model. Implement real ONNX inference with execution provider selection (webgpuwasmwebnn). Load model from ONNX_SUPPORTED_MODELS.
P0-F2 packages/ai-core/src/index.ts Transformers.js main-thread path is a stub: detects pipeline() but returns a diagnostic string. Implement real pipeline('text-generation', …) call with device auto-selection.
P0-F3 services/ai/inferenceGateway.ts modelList() returns []; healthCheck() returns {status:'ok', provider:'unknown'} with no latency probe. Implement model enumeration (cloud + local) and latency probing.
P0-F4 services/voice/sileroVadEngine.ts Completely inactive: isAvailable() returns false; initialize() throws; processChunk() returns null. Refactor VadEngine interface to support async processChunk(), then wire Silero ONNX model.
P0-F5 services/ai/webGpuDetectorService.ts Missing requestAdapter options: no powerPreference (low-power/high-performance), no forceFallbackAdapter, no feature inspection (timestamp-query, maxComputeWorkgroupSize). Add adapter option probing and feature flag detection.
P0-F6 services/ai/deviceHealthService.ts No WebNN detection (navigator.ml), no NPU detection, no compute shader capability check. Add localAiDeviceProfiler (Phase 2) to supersede static heuristics.
P0-F7 services/ai/modelRecommendations.ts onnxModel for vramTier === 'medium' is a WebLLM model ID (Qwen2.5-0.5B-Instruct-q4f16_1-MLC) instead of an ONNX model. Fix to a valid ONNX model ID (e.g., Xenova/Qwen2.5-0.5B-Instruct or HuggingFaceTB/SmolLM2-135M-Instruct).
P0-F8 services/ai/aiPolicy.ts assertCloudAiAllowed only exempts ollama and webllm as local providers. onnx and transformers are treated as cloud (will throw in localStorageOnly mode). Add onnx and transformers to the local-provider exemption set.
P0-F9 services/ai/hybridFallback.ts Fallback chain does not include onnx or transformers as fallback targets. Extend chain to support all local inference providers.

Important Findings (P1 — should fix in v1.20)

ID File Finding Proposed Fix
P1-F1 stryker.conf.json Missing mutation targets for AI/GPU/voice files: webGpuDetectorService.ts, gpuResourceManager.ts, localAiFacade.ts, deviceHealthService.ts, localEmbeddingService.ts, inference.worker.ts, voiceCommandService.ts, vadEngine.ts, sttEngine.ts. Add targets to mutate array.
P1-F2 features/featureFlags/featureFlagsSlice.ts Missing flags for upcoming v1.20 features: WebNN inference, compute shaders, adaptive AI engine. Add enableWebnnInference, enableComputeShaders, enableAdaptiveAiEngine.
P1-F3 services/ai/ecoModeService.ts Only battery-based eco detection; no thermal throttling, no CPU load detection, no memory-pressure eco mode. Integrate with deviceHealthService memory pressure and gpuResourceManager queue depth.
P1-F4 workers/inference.worker.ts No AbortSignal propagation into the actual pipeline() call; worker cancel only removes from abortMap but does not stop the running inference. Pass AbortSignal into Xenova pipeline options if supported; else document limitation.
P1-F5 services/ai/aiRetry.ts Linear backoff only; no exponential backoff, no jitter, no retry-after header parsing. Keep linear for simplicity (local AI), add jitter for cloud paths.
P1-F6 services/ai/fetchAdapter.ts No request timeout, no retry, no circuit-breaker for Tauri fetch failures. Add AbortSignal.timeout() and fallback chain.

Status Reconciliation — verified in code 2026-06-02

The P0/P1 tables above were authored against the pre-Phase-2.1 tree. A line-by-line re-verification against the current code shows most are already resolved; only aiRetry and fetchAdapter remain open.

ID Status Evidence (current code)
P0-F1 ✅ FIXED Real ONNX text-generation pipelines (Phase 2.1).
P0-F2 ✅ FIXED Real pipeline('text-generation', …) via @huggingface/transformers@3.8.1 (Phase 2.1).
P0-F3 ✅ FIXED services/ai/inferenceGateway.ts:91-134 — real modelList() enumerates cloud + WebLLM + ONNX catalogs; healthCheck() runs a latency probe via embedText.
P0-F4 ✅ FIXED Silero VAD v4 ONNX implementation (Phase 1.2).
P0-F5 ✅ FIXED services/ai/webGpuDetectorService.ts:28-102powerPreference + forceFallbackAdapter options, timestamp-query + maxComputeWorkgroupSize feature inspection, requestAdapterInfo.
P0-F6 ✅ FIXED Superseded by localAiDeviceProfiler (WebGPU/WebNN/DirectML + memory/battery detection).
P0-F7 ✅ FIXED services/ai/modelRecommendations.ts:77-82 — ONNX tiers use valid Xenova/Qwen2.5-1.5B/0.5B-Instruct + SmolLM2-135M IDs.
P0-F8 ✅ FIXED services/ai/aiPolicy.ts:5LOCAL_INFERENCE_PROVIDERS includes onnx + transformers.
P0-F9 ✅ FIXED services/ai/hybridFallback.ts:28 — local-provider set includes onnx + transformers; cloud fallback wired.
P1-F1 ✅ FIXED Stryker mutate expanded 34→40 (B-8).
P1-F2 ✅ FIXED enableAdaptiveAiEngine + compute/WebNN gates added (Phase 1.3).
P1-F3 ✅ FIXED RAM-pressure eco-mode added (Phase 1.3).
P1-F4 ✅ FIXED AbortSignal propagated end-to-end into the worker (Phase 2.1).
P1-F5 ✅ FIXED (v1.20) services/ai/aiRetry.ts — capped exponential backoff + full jitter; honors server Retry-After (seconds / HTTP-date / retryAfterMs) over the computed delay, clamped to 30s. Pure computeRetryDelayMs/parseRetryAfterMs helpers + injectable rng; 13 unit tests.
P1-F6 ✅ FIXED (v1.20) services/ai/fetchAdapter.ts — opt-in timeoutMs (DEFAULT OFF, streaming-safe) composing AbortSignal.timeout with the caller signal via AbortSignal.any. Existing no-arg callers unchanged; 5 unit tests.

Performance / Benchmark Gaps

  • No benchmark infrastructure at all — no Vitest bench(), no Playwright perf specs, no token/sec tracking.
  • No GPU tracing — no timestamp-query usage, no CDP trace collection.
  • No model pre-warming — every local inference cold-starts.
  • No IO-Binding / Graph Capture for ONNX — repeated inference pays full overhead.

Next Steps

  1. Phase 1: Fix P0-F1..P0-F9 (ONNX real inference, gateway stubs, Silero VAD, policy fixes, model recommendation bug).
  2. Phase 2: Build localAiDeviceProfiler + adaptiveAiEngine with WebNN/NPU detection.
  3. Phase 3: Production-ready WebLLM/ONNX/WebNN/DirectML with pre-warming and IO-Binding.
  4. Phase 4: Compute shader factory + WGSL shaders for RAG, plot-board, voice, manuscript.
  5. Phase 5: Domain-specific perfection (ProForge caching, plot-board GPU sim, voice pipeline, live manuscript features).
  6. Phase 6: Benchmarks, telemetry, DX scripts, documentation.
  7. Phase 7: Final validation, perfection score, ship-readiness.

Follow-up Audit — 2026-05-31 (i18n Audit + Settings + CI Stabilization)

i18n: Community Templates Localized

  • All 14 community templates now have locale-specific JSON files for DE, FR, ES, IT
  • communityTemplateService.ts is locale-aware — tries index.<lang>.json, falls back to EN
  • useFocusTrap.ts bug fixed: selector now excludes tabIndex=-1 inputs (mobile keyboard no longer pops on palette open)
  • 27 DE voice settings keys translated; total 2129 keys (up from 2117)

Settings: New Danger Zone features

  • Factory Reset (services/factoryResetService.ts): wipes all IDB databases, localStorage, SW caches
  • Repeat Onboarding: dispatches worldscript:openPortal event; useApp.ts listener re-opens WelcomePortal

CI: Stabilization

  • deploy-cloudflare-pages.yml paused (manual trigger only) — eliminates phantom 0-job failures on branch pushes
  • Fixed 3 TS errors (invalid Modal size prop + null-check for test mock) that caused cascade-skip of E2E/Build/Storybook

Follow-up Audit — 2026-05-28 (Phase 3 C-1 — collab-transport Security Peer Review)

C-1: collab-transport Crypto Security Review

File: packages/collab-transport/src/crypto.js (vendored y-webrtc 10.3.0 crypto module)

Three security findings identified and fixed in the same session:

ID Severity Finding Fix
C1-F1 High PBKDF2 iterations = 100,000 — below OWASP 2024 minimum (600k for SHA-256); WorldScript's own code uses 310k Raised to 600,000 across all 5 KDF sites (collab-transport/crypto.js, collaborationService.ts, storageEncryptionService.ts, cloudSyncEncryption.ts, libraryBackupService.ts)
C1-F2 High extractable: true on the derived CryptoKey — violates SEC-RULE-5; allows key export via crypto.subtle.exportKey() Changed to extractable: false
C1-F3 Medium promise.reject(...) in decrypt() not returned — error is swallowed, decrypt continues with garbage IV/ciphertext Added return before the rejection

Verification: pnpm exec vitest run tests/unit/collaborationService.test.ts — 38/38 passing after fixes.

Residual risk (accepted): The vendored crypto.js uses the y-webrtc PBKDF2 key derivation path (password → room key). The collaborationService.ts implements a separate PBKDF2 path (PBKDF2 600k → awareness payload encryption). Both paths now use 600k iterations and extractable: false. The room key derives salt from roomName (public, deterministic) — not a secret. This is intentional per y-webrtc design; collab session confidentiality depends on the password strength.

No known vulnerabilities in packages/collab-transport after C1-F1..C1-F3 applied.


Follow-up Audit — 2026-05-28 (v1.19.0 — Phase 2 Security/Voice/RTL/Logger Sprint)

Sprint: Phase 2 B-series (B-1..B-8) — 2026-05-28

B-1: services/storage/storageEncryptionService.ts — AES-256-GCM IDB at-rest encryption, PBKDF2-SHA-256 (600k iterations, OWASP 2024 minimum), non-extractable key, sentinel-prefixed blobs. Passphrase UX complete (2026-05-30): IdbUnlockModal (startup unlock when flag enabled), PassphraseModal (set/change/disable in Settings › Privacy), PrivacySection encryption card. 33 tests (18 service + 15 UI).

B-2: services/voice/wasmSttEngine.ts + sileroVadEngine.ts — Whisper tiny.en Q8 + Silero VAD scaffold via @xenova/transformers. Feature-flagged (enableVoiceWasm).

B-3: packages/collab-transport workspace package — vendor fork of y-webrtc 10.3.0 with WorldScript RTCDataChannel E2E encryption patch baked in. Removes patchedDependencies re-apply burden. Biome alias + tsconfig paths wired.

B-4: tests/e2e/a11y-axe.spec.ts — Playwright axe-core gate across 8 views. WCAG 2.2 AA.

B-5: RTL beta — ar/he locale stubs, enableRtlLayout flag, dir="rtl" wiring, RTL foundation tests. i18n-gate limitation: pnpm run i18n:check verifies key parity only (all keys present), not translation quality. ar and he are excluded from the LANGS parity check — both locale trees are ~99.8% English copies. Full translation content is a Phase 3 / v2.0 community task.

B-6: services/logger.ts — StructuredLogger: IDB sink (1000-entry LRU), Tauri JSONL (daily rotation), DEV console sink. createLogger(module) factory, withContext() chaining, GDPR key sanitization. 16 tests.

B-7: Coverage thresholds raised: lines 68→71%, functions 60→63%, branches 55→57%, stmts 67→69%. Vendored y-webrtc.js excluded from V8 coverage. i18nBootstrap tests: 3→15 (resolveTranslation + ar/he RTL stubs).

B-8: Stryker: break 70→75, high 80→85, low 65→70. Mutate targets: 34→40 files (added storageEncryptionService, logger, collaborationService, i18nBootstrap, featureFlagsSlice, crossProjectIndexService).

Also: All 5 release tags (v1.17.1..v1.19.0) and GitHub Releases retroactively created. Sequential shell execution rule written into all 4 instruction files. TypeScript paths alias for @xenova/transformers.

Quality gate (2026-05-28 — v1.19.0): lint ✅ · typecheck ✅ (0 errors) · tests ✅ (logger 16, encryption 24, collab 38, i18nBootstrap 15 — all green) · Stryker break 75 · coverage thresholds L71/F63/B57/S69


Follow-up Audit — 2026-05-27 (v1.18.1 — TypeScript strict-mode compliance sweep)

Sprint: TypeScript Strict-Mode Compliance Sweep (2026-05-27)

Goal: Eliminate all pre-existing TypeScript typecheck errors introduced by strict: true + exactOptionalPropertyTypes: true + noUncheckedIndexedAccess: true across source files and test files. Result: zero errors on pnpm exec tsc --noEmit.

Root causes addressed:

Category Files Fix
exactOptionalPropertyTypes in ProForge pipeline 7 agent files + slice + orchestrator Conditional spread ...(val !== undefined && { key: val }) pattern
AIRequestOptions requires model+provider 7 pipeline agents + baseAgent.ts Added buildAiOpts() protected helper to BaseAgent
Wrong module paths in toolRegistry.ts 1 '../../app/store''../../../app/store'
Missing author in EpubExportOptions productionAgent.ts Added author: project.author ?? 'Unknown'
AiModel/AIProvider type imports missing baseAgent.ts Added imports
Voice component wrong imports VoicePrivacyConsentModal.tsx, VoicePrivacyStatus.tsx Fixed useTranslation import path; Modal named import; setVoiceSettings action
noUncheckedIndexedAccess in test fixtures 35+ test files [i]! non-null assertions, ?. optional chaining
StorySection shape mismatch (no type/order) 5 test files Removed non-existent fixture fields
StorySection.act literal type 1|2|3 SceneRevisionPanel.test.tsx act: 1 as const
AiModel, Theme, MindMapNodeType, StoryObjectType union literals 4 test files Replaced with valid enum members
PrivacySettings required fields (6 fields) aiPolicyAndUtils.test.ts Added basePrivacy() helper with all required fields
DeviceHealthReport shape mismatch modelRecommendations.test.ts Removed non-existent fields, added missing ones
FlatHelpArticle shape mismatch HelpSearchPanel.test.tsx bodyKey+tagscontentKey
FeatureFlagsState.enableProForge missing 3 command test files Added enableProForge: false to mock objects
Unused variables (_result, _moveEvent) 2 test files Removed or voided
versionControlActions.restoreSnapshot missing versionControlSlice.ts Added stub reducer (typed signal only)
useTransientUiStore selector type mismatch CompileWizardModal.test.tsx any cast with biome-ignore per occurrence
useAppSelector selector type mismatch useProForgeOrchestrator.test.ts any cast with biome-ignore on mockImplementation
getByRole('combobox', { name: undefined }) ToolsPanel.test.tsx getAllByRole('combobox')[0]
Generic t<T> function type cast useTranslation.test.tsx Double-cast as unknown as <T>(k,opts?)=>T
AsyncThunk.fulfilled.match property BackupQuickActionsCard.test.tsx Object.assign + as unknown as cast

Quality gate (2026-05-27 — v1.18.1): lint ✅ (Biome — 0 errors) · typecheck ✅ (0 errors — tsgo --noEmit) · i18n:check ✅ (2062 keys × 5 locales) · tests ✅


Follow-up Audit — 2026-05-27 (v1.18.0 — ProForge Humanization & Refinement Sprint)

Sprint: ProForge Humanization & Refinement — Phases H/A/P/X (2026-05-27)

Goal: Transform the ProForge pipeline from a functional scaffold into a polished, author-facing editorial system. Four phases: UX vocabulary (H), architecture cleanup (A), quality supervision (P), and Settings/empty-state polish (X).

Changes shipped:

Phase Area What changed
H Vocabulary Stage labels, loading messages, RAG "passages" rename, non-technical feature flag descriptions
H Tests Behavioral tests replacing implementation-detail assertions across 8 agent test files
A BaseAgent Abstract base class in pipelineAgents/baseAgent.ts — ~200 LOC removed from 8 agents
A aiConstants.ts New consolidation module for CREATIVITY_TO_TEMPERATURE, LOCAL_BACKEND_PRESET_DEFAULT_URL, ORCHESTRATION_READY_PROVIDERS — re-export shims keep existing imports valid
A listenerMiddleware.ts addDebouncedListener factory; getOriginalState() synchronous capture fix (RTK constraint)
P-1 supervisorAgent.ts New heuristic quality gate (no AI calls) — detects fallback sentinels, evaluates pacing and grammar ratios
P-2 Orchestrator executeStageWithSupervision retry loop; hard gate: intake qualityScore < 30 → fail
P-3 Self-evaluation BaseAgent.selfReflect() — re-runs DiagnosticAgent/StructuralAgent on INCOHERENT flag; reflectionNotes in types
P-4 Honest fallbacks All createFallback* use 0 scores + isFallback: true; SupervisorAgent detects and retries
P-5 PipelineReviewPanel Critical Actions card, severity-grouped view, Quick Accept High-Confidence button (confidence ≥ 0.85)
X-1 SettingsView NAV_GROUPS + NavGroupHeader — semantic sidebar grouping
X-2 Flow Mode transientUiStore flowMode/setFlowMode; WriterViewUI Escape key exits
X-3 Empty states <EmptyState> for Characters, World, SceneBoard, ProForge views
i18n All 5 locales proforge.pipeline.title, proforge.pipeline.noneActive, loading messages, stage labels — 2055 keys × 5 locales

New files: services/ai/aiConstants.ts, services/proForge/pipelineAgents/baseAgent.ts, services/proForge/pipelineAgents/supervisorAgent.ts

Test fixes (6 files, 84 tests recovered):

File Root cause Fix
listenerMiddleware.test.ts getOriginalState() after await Synchronous capture before first await
writing/WriterViewUI.test.tsx useWriterViewContext missing mock Added vi.mock('../../../contexts/WriterViewContext')
proForge/components/ProForgeDashboard.test.tsx i18n key assertion Changed to screen.getByText('proforge.pipeline.noneActive')
thunks/writingAndCharacterThunks.test.ts assertCloudAiAllowedSync throws in localStorageOnly mode Added vi.mock('../../../services/ai/aiPolicy')
thunks/outlineAndWorldThunks.test.ts same same
thunks/plotBoardAiThunks.test.ts same same

Quality gate (2026-05-27 — v1.18.0): lint ✅ (Biome — 0 errors) · typecheck ✅ · i18n:check ✅ (2055 keys × 5 locales) · tests ✅ (84 previously-failing tests green; no regressions)


Follow-up Audit — 2026-05-26 (v1.17.2 — Local Inference Robustness Sprint)

Sprint: Local Inference Robustness (2026-05-26)

Goal: Harden the local AI stack (WebLLM / Transformers.js / RAG) against multi-tab GPU contention, stale workers, redundant re-embedding, and missing cloud-AI policy enforcement.

Changes shipped:

Area File What changed
Tab leader packages/ai-core/src/tabLeaderElection.ts localStorage heartbeat (5s refresh, 12s stale) for fast-path leader detection across reloads; surrenderLeadership() export; default election timeout 280→800ms
Tab leader packages/ai-core/src/index.ts Re-exports surrenderLeadership
Embedding cache services/ai/localEmbeddingService.ts LRU in-memory cache (1 000 entries, ~400ms hit savings per RAG query); worker health-check ping/pong (30s interval, 5s timeout → auto-restart)
GPU mutex services/localAiFacade.ts Acquires gpuResourceManager GPU slot before WebLLM/ONNX-WebGPU init; always releases + calls surrenderLeadership() in finally
Worker workers/inference.worker.ts WORKER_PINGWORKER_PONG handler for health-check protocol
AI policy features/project/aiThunkUtils.ts assertCloudAiAllowedSync called at thunk entry — one enforcement point instead of per-caller
RAG stability services/localRagService.ts indexedAt changed from now - offset to stable (i+1)*1000 — consistent across re-indexing runs
Turbo turbo.json Added mutation pipeline task (no cache)
Docs CLAUDE.md, .github/copilot-instructions.md, .cursor/rules/ Updated architecture docs to reflect ProForge, Voice, Feature Flags, RTCDataChannel patch, checkStorageHealth, Tauri CSP

Tests added (32 new tests across 4 files):

File New tests
tests/unit/tabLeaderElection.test.ts +6 (heartbeat fast-path, stale detection, surrenderLeadership cleanup)
tests/unit/inferenceWorker.test.ts +1 (WORKER_PING → WORKER_PONG)
tests/unit/localAiFacade.test.ts +3 (GPU acquire/release, no-GPU skip, error path)
tests/unit/aiThunkUtils.test.ts +3 (policy call args, policy rejection, payload creator not called on block)

Quality gate (2026-05-26 — v1.17.2): lint ✅ (Biome — 0 errors, 895 files) · typecheck ✅ · tests ✅ (32 new, all suites green)


Follow-up Audit — 2026-05-26 (Coverage Sprint)

Sprint: Test Coverage Expansion (2026-05-26)

Goal: Maximize Vitest unit-test coverage toward targets Lines ≥85% / Branches ≥75% / Functions ≥80% / Statements ≥85% / Stryker ≥80%.

New test files added (122+ new test files, ~600+ new tests):

Area Files Tests
settings/ components 17 ~140
writing/ components 3 ~45
manuscript/ components 2 ~31
mind-map/ components 4 ~45
ui/ atoms 2 ~20
services/ (ai, voice, misc) 24 ~180
hooks/ 17 ~150
features/ slices & types 3 ~33
Root-level components 34 ~330
proForge/ pipeline 10 ~120

Key modules newly covered:

  • components/writing/AiScratchpad.tsx — 15 tests (TTS, history nav, accept)
  • components/writing/ContextPanel.tsx — 8 tests (section display, notes)
  • components/writing/ToolInputs.tsx — 17 tests (all tool input cases)
  • components/manuscript/InspectorPanel.tsx — 18 tests (word count, metadata)
  • components/manuscript/NavigatorPanel.tsx — 13 tests (virtual scroll, drag)
  • components/mind-map/MindMapNodeEditor.tsx — 16 tests (shape, color, save)
  • components/mind-map/MindMapNodeShape.tsx — 10 tests (SVG rendering, truncation)
  • components/mind-map/MindMapNodeEditor.tsx — 16 tests
  • services/ai/ecoModeService.ts — 15 tests (battery, listeners, adaptive)
  • services/ai/creativityTemperature.ts — 4 tests (pure map)
  • hooks/useCharacterInterviewsView.ts — 13 tests
  • components/settings/GpuMetricsPanel.tsx — 14 tests
  • components/settings/FeatureFlagsSection.tsx — 7 tests
  • components/settings/PrivacySection.tsx — 8 tests
  • components/settings/SettingsOverviewCard.tsx — 10 tests
  • components/settings/SettingsModals.tsx — 14 tests
    • 70 more components and services fully covered for the first time

Maintenance pass (2026-05-26 — v1.17.1):

  • 30+ TypeScript errors fixed in ProForge pipeline test suite (15 files)
  • 5 test failures fixed in Coverage Sprint tests (NotificationsSection, Progress, ManuscriptEditor, AnalyticsBootstrap, ragPromptAssembly)
  • 16 dependencies updated: patch (ai-sdk, dompurify, tanstack/virtual, vite 8.0.14, vitest 4.1.7, storybook 10.4.1, @types/*) + minor (@google/genai 2.6.0, docx 9.7.0, vite-plugin-pwa 1.3.0, wrangler 4.94.0)
  • pnpm audit: 0 known vulnerabilities

Quality gate (2026-05-26): lint ✅ (Biome — 0 errors, 895 files) · typecheck ✅ · i18n:check ✅ (2025 keys × 5 locales; ar/he stubs excluded from parity check) · build ✅ · tests ✅ (4 044 / 386 files) · coverage ✅ Stmts 71.29% / Branches 58.79% / Funcs 65.18% / Lines 73.06% (thresholds: S≥67/B≥55/F≥60/L≥68)

Quality gate (2026-05-28, C-7 sprint): lint ✅ (Biome — 0 errors, 1006 files) · typecheck ✅ · i18n:check ✅ (2077 keys × 5 locales) · tests ✅ (4 174 / 391 files — +130 tests: supervisorAgent, baseAgent, geminiService streaming, helpCatalog, idbCore, loraThunks) · coverage thresholds raised L73/F65/B58/S71 (CI will confirm actual numbers)

Quality gate (2026-05-29, Feature Parity Audit): lint ✅ (Biome — 0 errors, 1008 files) · typecheck ✅ · i18n:check ✅ (2078 keys × 5 locales; enableIdbAtRestEncryption key added) · tests ✅ (4 192 / 392 files — +18 tests: pluginRegistry flag-gate ×2, cloudSyncBackend flag-gate ×2, 8 parity-audit drift fixes) · 8 runtime-gate drifts corrected; docs/FEATURE-PARITY.md + features/featureCatalog.ts added

Quality gate (2026-05-30, B-1 Passphrase UX): lint ✅ (Biome — 0 errors, 1010 files) · typecheck ✅ · i18n:check ✅ (2099 keys × 5 locales; +22 encryption UX keys) · tests ✅ (CI — +33 tests: 18 storageEncryptionService service-layer, 15 PrivacySection UI) · IdbUnlockModal + PassphraseModal + PrivacySection encryption card wired; transientUiStore isIdbUnlockOpen gate; flag label updated


Follow-up Audit — 2026-05-24 (v1.17 — Voice Full Support Foundation)

Sprint: v1.17 Voice Full Support Foundation (2026-05-24)

VOICE-1 complete: Voice Full Support Foundation — opt-in voice control system with abstract engine interfaces, Web Speech API fallbacks, hybrid intent engine, and full app integration.

Architecture:

  • services/voice/voiceTypes.ts — Core interfaces: SttEngine, TtsEngine, VadEngine, WakeWordEngine, IntentEngine, FeedbackService, AudioNavigator
  • services/voice/voiceCommandService.ts — Singleton orchestrator bridging all engines with Redux; state machine (idle → listening → processing → speaking → idle + dictating)
  • services/voice/intentEngine.tsHybridIntentEngine: exact template matching → Jaccard fuzzy scoring → slot extraction; view-context filtering
  • services/voice/commandVoiceMappings.ts — 25 static VoiceCommandDefinitions covering navigation, editor actions, AI features, voice-specific commands, settings shortcuts
  • services/voice/sttEngine.tsWebSpeechSttEngine with auto-restart on unexpected end; createSttEngine() factory
  • services/voice/ttsEngine.tsWebSpeechTtsEngine with voice selection, rate/volume/pitch control; createTtsEngine() factory
  • services/voice/vadEngine.tsWebRtcVadEngine (energy-based, pure JS, always available)
  • services/voice/wakeWordEngine.tsEnergyThresholdWakeWordEngine with configurable phrase, rolling transcript history
  • services/voice/feedbackService.ts — 3 verbosity levels (minimal/standard/verbose), TTS queue, event listeners for visual feedback
  • services/voice/audioNavigator.tsaudioNavigator singleton: ARIA landmark scanning, focus management, aria-live region creation/updates

Redux State:

  • features/voice/voiceSlice.tsVoiceState with mode, transcript, processing, dictationActive, sttStatus, ttsStatus, microphonePermission, onboardingCompleted, lastConfidence, lastActivityAt, activeSttEngine, activeTtsEngine
  • features/settings/settingsSlice.tsVoiceSettings added (enabled, activationMode, feedbackLevel, ttsMuted, speechRate, speechVolume, autoPunctuation, cloudFallback, listeningTimeout, sttEngine, ttsEngine)
  • features/featureFlags/featureFlagsSlice.tsenableVoiceSupport: boolean (default: false)

React Integration:

  • hooks/useVoice.ts — Primary hook: bridges Redux state with VoiceCommandService; syncs settings to service; injects dispatch/getState
  • hooks/usePushToTalk.ts — Global Ctrl+Shift+V keyboard shortcut when voice enabled
  • hooks/useVoiceDictation.ts — Editor dictation: inserts transcripts at cursor position
  • hooks/useVoiceAccessibility.ts — ARIA live region management, focus restoration
  • components/voice/VoiceIndicator.tsx — Floating status indicator (listening/processing/error)
  • components/voice/VoiceControlPanel.tsx — Expandable control panel with transcript display and quick actions
  • components/voice/VoiceSettingsSection.tsx — Settings tab with onboarding notice, engine selection, feedback level, PTT configuration
  • App.tsx — Conditional rendering of VoiceIndicator/VoiceControlPanel; document.body.dataset['view'] for intent engine context; PTT hook mount
  • Header.tsxuseVoice integration for voice status display
  • ManuscriptEditor.tsx — Dictation support via useVoiceDictation

i18n: 2025 keys × 5 locales (en/de/es/fr/it); voice settings keys added to all locales.

Tests: 83 unit tests / 9 test files:

  • voiceSlice.test.ts — 10 tests (state transitions, transcript, dictation, error, reset)
  • intentEngine.test.ts — 7 tests (exact match, fuzzy match, slot extraction, view filtering)
  • feedbackService.test.ts — 4 tests (muted events, TTS queue, level filtering, cancel)
  • sttEngine.test.ts — 9 tests (availability, start/stop, result routing, error ignore, auto-restart)
  • ttsEngine.test.ts — 10 tests (availability, speak, error, cancel, pause/resume, dispose)
  • vadEngine.test.ts — 7 tests (availability, speech detection, silence detection, ongoing speech)
  • wakeWordEngine.test.ts — 11 tests (default phrase, custom phrase, fuzzy match, history, processChunk)
  • audioNavigator.test.ts — 13 tests (landmark scan, cycle, focus, label, announce, live region)
  • commandVoiceMappings.test.ts — 12 tests (command coverage, uniqueness, dictation views, map building)

Bug fixes during implementation:

  • ttsEngine.ts: Fixed window.speechSynthesis undefined check in speak(), cancel(), pause(), resume() — was using 'speechSynthesis' in window which returns true even when undefined
  • appStoreRef: Object pattern { current: null } avoids import reassignment issues for singleton service access outside React
  • SpeechRecognition global type: Removed custom Window interface extensions; uses direct window.SpeechRecognition access with type assertions

Known limitations / v1.2 planned:

  • WASM engines (Whisper.cpp, Kokoro, Piper, Silero VAD, Sherpa-ONNX) are prepared via abstract interfaces but not yet bundled
  • currentView uses document.body.dataset['view'] as best-effort fallback (not in Redux)
  • No integration tests for VoiceCommandService or useVoice hook yet
  • No E2E tests for voice flows yet
  • No semantic intent matching (MiniLM embeddings) yet
  • No local LLM fallback for complex commands yet

Quality gate: lint ✅ · i18n:check ✅ (2025 keys × 5 locales) · typecheck ✅ · 83/83 voice tests ✅


Follow-up Audit — 2026-05-24 (DevEx — Dual-Graph Integration: Graphify + CodeGraph)

DevEx Sprint: Dual-Graph Codebase Intelligence (2026-05-24)

DUALGRAPH-1 complete: CodeGraph semantic code intelligence integrated alongside the existing Graphify knowledge graph. Both tools now run side-by-side with complementary roles — Graphify for multi-modal architecture breadth, CodeGraph for symbol-level agent navigation via MCP.

CodeGraph Setup:

  • Global install: @colbymchenry/codegraph@0.9.3 (bundled Node runtime, self-contained)
  • Project init: codegraph init -i in repo root → .codegraph/codegraph.db (SQLite + FTS5, WAL mode)
  • Index stats: 260 files · 2.754 nodes · 2.443 edges · 4.81 MB
  • Auto-sync: native OS file watcher (FSEvents/inotify/ReadDirectoryChangesW), 2s debounce
  • Respects .gitignore — no extra config needed

Solo-repo policy (mirrors Graphify):

  • .codegraph/* gitignored; only .codegraph/CODEGRAPH_REPORT.md committed
  • graphify-out/* gitignored; only graphify-out/GRAPH_REPORT.md committed

pnpm scripts added:

  • codegraph:status — index statistics
  • codegraph:update — force full re-index
  • codegraph:sync — incremental sync
  • codegraph:report — regenerate CODEGRAPH_REPORT.md
  • codegraph:affected — smart test selection from uncommitted changes
  • graphs:update — unified Graphify + CodeGraph update

Automation scripts:

  • scripts/codegraph-report.mjs — generates CODEGRAPH_REPORT.md from codegraph status + codegraph files --json
  • scripts/dual-graph-update.mjs — sequential Graphify AST update + CodeGraph force index + report generation
  • scripts/pre-commit-codegraph.mjs — optional informational hook showing affected tests (exit 0, non-blocking)

VS Code: Tasks (.vscode/tasks.json):

  • CodeGraph: status / update index / generate report
  • Dual-Graph: update both

Agent Instructions updated:

  • CLAUDE.md — CodeGraph MCP rules + dual-graph workflow
  • .github/copilot-instructions.md — CodeGraph context + tool selection guidance
  • docs/codegraph.md — full setup guide, MCP config (Kimi Code CLI + Cursor), troubleshooting
  • docs/dual-graph-setup.md — master guide: philosophy, quick start, daily workflow, prompt templates, monorepo structure

Documentation Hub updated (README.md):

  • docs/codegraph.md and docs/dual-graph-setup.md linked in Documentation Hub table
  • CONTRIBUTING.md — CodeGraph install section added under Development Setup
  • CHANGELOG.md [Unreleased] — CodeGraph entry
  • TODO.md — Dual-Graph Integration marked complete
  • .github/CI-AUDIT.md — post-feature policy updated from graphify:update to graphs:update

Configuration:

  • biome.json!!**/.codegraph added to files.includes (excluded from lint/format)
  • package.json lint-staged.codegraph/** bypass added (mirrors graphify-out/**)

Privacy & Security:

  • 100% offline — no data leaves the machine
  • No API keys required
  • SQLite-only storage
  • Safe for proprietary code

Quality gate: lint ✅ · Biome ignores .codegraph/ ✅ · codegraph status reports healthy index ✅


Follow-up Audit — 2026-05-23 (v2.0 — Phase 2: LORA-1/PLUGIN-1/PERF-1/COM-1)

Sprint: v2.0 Phase 2 Completion (2026-05-23)

LORA-1 complete: LoRA adapter inference foundation — services/loraAdapterService.ts (IDB: worldscript-lora-db, stores lora-meta + lora-blobs); components/settings/LoraAdapterSection.tsx (file upload, adapter list, delete); services/localAiFacade.ts extended with loraAdapterId parameter; enableLoraAdapters feature flag. Full QNBS-v3 comment coverage.

PLUGIN-1 complete: Plugin system v0.1 — PluginSandboxedApi interface + PluginPermission typed union (storage.read/write, ai.invoke, project.read/write, scene.read/write); pluginRegistry.execute() builds permission-checking proxy before calling plugin callback; components/settings/PluginsSection.tsx (type badges, permission chips, uninstall); enablePluginSystem feature flag. Tests: tests/unit/pluginRegistry.test.ts extended with 8 execute() tests (deny/allow, error, log).

PERF-1 complete: Large manuscript performance — useDeferredValue(activeSection?.content) in ManuscriptEditor.tsx defers expensive highlight-overlay computation; isHighlightPending dims overlay during render lag. NavigatorPanel.tsx: overscan reduced 5→3; dismissible notice at ≥500 scenes informing users virtual scrolling is active. i18n: 2 new manuscript.* keys × 5 locales.

COM-1 complete: Community section — components/settings/CommunitySection.tsx with GitHub Discussions/Issues quick-links (accessible cards with hero icons); curated model list showing all WebLLM models (WebGPU badge + WEBLLM_USE_CASES i18n labels) and ONNX models (WASM badge). settingsSearchHints.ts extended. i18n: 17 new settings.community.* keys × 5 locales.

Quality gate: lint ✅ · i18n:check ✅ (1992 keys × 5 locales) · typecheck ✅


Follow-up Audit — 2026-05-22 (v1.16 — Design System Completion: DS-1/DS-2/SB-1/HK-4)

Sprint: v1.16 Design System Completion (2026-05-22)

DS-2 complete: Zero dark: Tailwind prefix violations remain in any className string across the entire codebase. Eliminated across 18+ files using --sc-* semantic tokens and alpha-bg patterns (bg-X-500/15) for categorical colors.

DS-1 sweep: All undefined bridge CSS variables fixed — --background-hover, --background-elevated, --background-selected, --foreground-on-interactive, --foreground-tertiary replaced with sc-* equivalents in 12 files. App.tsx root loader and main div updated to sc-* tokens.

SB-1 complete: 5 missing Storybook stories added: DebouncedInput, DebouncedTextarea, Textarea, PWAComponents, SectionIcon. All UI atom components now have Storybook coverage.

HK-4: displayName added to ErrorBoundary and ViewErrorBoundary.

DS-5 readiness: Bridge block in index.css can be removed after one production cycle. Only intentional vars remain (--border-interactive, --nav-*, --glass-*, gradient overlay vars).

Quality gate: lint ✅ · i18n:check ✅ (1952 keys × 5 locales) · typecheck ✅


Follow-up Audit — 2026-05-22 (v1.11.0 — Stabilization: Deploy Fix, StorageBackend Resilience, Help Center)

Released: v1.11.0 (2026-05-22)

Deploy: resolve-deploy-base.mjs Cloudflare P0 variable-name bug fixed; sync-deploy-base.mjs error propagation + const lint fix.

StorageBackend: services/dbInitialization.ts extracted (initializeStorage(), resetAllDatabases()); retryDb() applied to saveProject + saveSettings; index.tsx mounts StorageErrorScreen on init failure; settings auto-save catch dispatches error toast.

Help Center: 13 stub articles (< 300 chars) replaced with full 700–1000 char HTML content across all 5 locales. German typographic closing quotes (11 ASCII→U+201C) fixed. 1931 keys × 5 locales at parity.

Tests: 15 new tests — dbInitialization.test.ts (8) + dbServiceRetry.test.ts (7). Both use // @vitest-environment node, vi.hoisted(), bracket notation for index-signature properties.

Quality gate: lint ✅ · i18n:check ✅ (1931 keys × 5) · typecheck ✅ · 15/15 new tests ✅


Follow-up Audit — 2026-05-21 (v1.9.0 — Lazy Loading, Help/Settings Hub, Tauri Desktop UX)

Released: v1.9.0 (2026-05-21)

Cold start / bundles: Dynamic DuckDB/RAG/codex in listenerMiddleware via duckdbListenerLoader; aiApi defers loadAiProvider(); lazy Plot Board chunks, ForceGraph, CollaborationPanel; Vite manualChunks + bundle budget gate.

Help: helpCatalog.ts (structure), 50+ articles, search UI, Documentation + Settings Guide categories, expanded helpDocRetrieval chunks; es/fr/it full article translations (scripts/help-locales-es-fr-it.json).

Settings: SettingsGuideSection, FeatureFlagsSection (12 flags), SettingsOverviewCard, Dashboard BackupQuickActionsCard.

Tauri: Native menu (File/Help) → menu-action; tauri-plugin-window-state; useTauriUpdater + About banner; openTauriDataDirectory.

Resilience: ViewErrorBoundary + withTransientRetry on AI provider calls.

i18n: 1923 keys × 5 locales. Docs: docs/SPRINT-V1.9.md, CHANGELOG, README, TAURI guides.


Follow-up Audit — 2026-05-20 (v1.7.0 — DuckDB Analytics + Hybrid RAG End-to-End + AI Extensions)

Released: v1.7.0 (2026-05-20)

DuckDB-WASM Analytics Layer (P0–P3 complete): workers/duckdbWorker.ts (OPFS + in-memory fallback, messageId protocol), services/duckdb/duckdbClient.ts (singleton proxy, init retry 3×, AbortSignal, OPFS fallback handler), services/duckdb/duckdbSchema.ts (10 tables + 5 analytics views including rag_chunks FLOAT[], cross_project_index, codex_*), services/duckdb/duckdbAnalytics.ts (typed query helpers, withDuckDbRetry, queryRagSimilarity via list_dot_product()), services/duckdb/duckdbMigration.ts (idempotent IDB→DuckDB seed). hooks/useDuckDb.ts + hooks/useAnalytics.ts integrate the layer into React with feature flag enableDuckDbAnalytics.

Hybrid RAG wired end-to-end:

  • types.ts / features/settings/settingsSlice.ts: ragMode: 'lexical' | 'hybrid' added to AdvancedAiSettings (default 'hybrid').
  • components/settings/AiSections.tsx: settings button fixed (rebuildHybridRagIndex replaces rebuildLocalRagIndex); DuckDB dual-write enabled when enableDuckDbAnalytics flag is on; RAG mode selector dropdown added.
  • hooks/useConsistencyCheckerView.ts: calls retrieveContext() before AI call; passes top-8 RAG chunks as ragChunks to geminiService.ts, replacing the full 50 000-char manuscript block. Graceful degradation when index empty or embedding model not loaded.
  • components/manuscript/ReferencePanelView.tsx: "Re-Index for AI" footer button for on-demand rebuild.
  • services/dbService.ts: migration defaults include ragMode: 'hybrid' for IDB state upgrade.
  • i18n: +35 keys (3 RAG mode + 5 re-index × 5 locales + locale bundle rebuild) → 1 625 keys × 5 locales.

AI Provider Extensions: ONNX + Transformers.js as selectable primary providers. Service-level dedup in aiThunkUtils.ts. Per-project AI preset (hash-based deep links). WorkerBus backpressure guard (MAX_QUEUE_SIZE 32; critical bypass; telemetry extended).

Collaboration: Y-WebRTC E2E AES-256-GCM encryption (collaborationService.ts); PBKDF2 600 000 iterations; CollaborationPanel E2E status badge.

Performance: PlotCanvas.tsx pointer-move throttled via rAF; eliminates 60 Hz Redux dispatch storm.

Quality gate at v1.7.0: lint ✅ typecheck ✅ i18n 1 625 keys × 5 locales2 024+ tests / 178 files — 0 failures ✅ coverage (CI pending) ✅ build ✅ bundle ≤ 7000 KB ✅



Follow-up Audit — 2026-05-19 (v1.5 Release + v1.6 Plot-Board v2 & Writer Experience)

Released: v1.5.0 (2026-05-19, commit 7950039)

Delivered: WorkerBus v2 (priority preemption, backpressure, transferables), GPU Resource Manager (gpuResourceManager.ts), Device Health Service, Eco Mode, Inference Progress Emitter (WCAG 2.2 role="progressbar" download modal), Model Recommendations v2, ONNX Runtime Web Layer-2, Transformers.js Layer-3 (workers/inference.worker.ts), Inference LRU Cache (IDB + in-memory), Local Embedding Service (MiniLM-L6-v2 384-dim), Local NLP Service (sentiment, classification, summarization), Hybrid RAG (60% semantic + 30% keyword + 10% recency), Telemetry Service, UsageAnalytics, PluginRegistry, StyleTransfer, PlotHoleFix, ChapterAutoGen, PromptLibrary, GpuMetricsPanel, BottomSheet, swipe gestures, useLongPress, useHaptics. Coverage at release: 66.1% lines · 50.98% branches · 56.07% functions. 1 851 tests / 166 files.

Released: v1.6.0 (2026-05-19, commit 61c453e)

Plot-Board v2 (features/plotBoard/plotBoardSlice.ts, services/plotBoardService.ts, components/scene-board/): Free-form canvas mode alongside existing Swimlane and Timeline. SVG connections (5 types: cause-effect, parallel, subplot, temporal, character-arc). Subplot system with color filtering. Tension curve panel with draggable overrides. Beat-sheet overlays (3-Act, Save-the-Cat, Hero's Journey). Snap-to-grid, mini-map, mobile pinch/pan gestures. Mode tab bar (Swimlane | Canvas | Timeline).

Real-Time Book Preview (components/BookPreviewView.tsx): Live Scrivener-style rendering, scrollable IntersectionObserver TOC, fullscreen, per-section font/size controls, word-count annotations.

Reference Panel (components/manuscript/ReferencePanelView.tsx): 6-tab sidebar in the manuscript editor (Characters, World, Notes, Binder, Comments, Revisions). BottomSheet on mobile.

Per-Scene Revision History (services/sceneRevisionService.ts, components/manuscript/SceneRevisionPanel.tsx): IDB scene-revisions store, word-level diff, named labels, two-step restore, auto-save via listenerMiddleware (30 s debounce, max 50 per scene).

Threaded Comments (features/sceneComments/sceneCommentsSlice.ts, components/manuscript/CommentsPanel.tsx): resolve/unresolve, nested replies, unresolved badge. IDB-persisted via listenerMiddleware.

Progress Tracker Dashboard (features/progressTracker/progressTrackerSlice.ts, components/ProgressTrackerView.tsx): Circular SVG progress ring, live session timer (Ctrl+Shift+S), 30-day velocity area chart, 12-week GitHub-style heatmap, streak system (computeStreak(history)), daily/weekly goal editing.

Mobile Polish: useFoldableLayout (Device Posture API, env(fold-top/left)), deepLinkService (URL hash routing: #/board, #/preview, #/progress, #/project/{id}/scene/{id}), named HAPTIC_PATTERNS library in useHaptics.ts, iOS safe-area insets.

Build fix: vite.config.ts gains @xenova/transformers alias (same as vitest.config.ts) so Rolldown resolves the workspace-nested package during production build.

Documentation added: docs/PLOT-BOARD.md, docs/PROGRESS-TRACKER.md. Markdown corpus now 22 files.

Markdown corpus (24 files): README.md, CONTRIBUTING.md, CHANGELOG.md, AUDIT.md, ROADMAP.md, TODO.md, CLAUDE.md, docs/BEST-PRACTICES.md, docs/Design-System.md, docs/DEPLOYMENT.md, docs/CI.md, docs/ACCESSIBILITY.md, docs/PLOT-BOARD.md, docs/PROGRESS-TRACKER.md, docs/SPRINT-V1.5.md, docs/SPRINT-V1.6.md, docs/TAURI-CI.md, docs/TAURI-UPDATER.md, docs/graphify.md, docs/codegraph.md, docs/dual-graph-setup.md, docs/history/completed-v1.1.md, .github/SECURITY.md, .github/copilot-instructions.md.

Quality gate at v1.6.0: lint ✅ typecheck ✅ i18n 1590 keys × 5 locales ✅ 1 966 tests / 174 files — 0 failures ✅ coverage 63.88% lines / 48.87% branches / 54.35% functions ✅ build ✅ bundle ≤ 7000 KB ✅

Coverage thresholds recalibrated (vitest.config.ts): lines 63 / branches 48 / functions 54 / statements 62. The 1 pt drop from v1.5 thresholds reflects 25+ new UI components (canvas, SVG interactions) that are harder to cover in unit tests. Target: branches ≥ 55% in v2.0 (tracked in TODO.md).


Follow-up Audit — 2026-05-20 (v1.6.1 + v1.6.2 — AI Models, Docker, plotBoardSlice Refactor, Locale-Aware Readability)

Released: v1.6.1 (2026-05-19, commit aa9f21c)

AI model catalogue (Gemini 3.x): Default model gemini-2.5-flashgemini-3.5-flash. Added Gemini 3.1 Pro Preview, 3.1 Flash, 3.1 Flash-Lite. Removed legacy gemini-2.0-flash. All fallback IDs updated across geminiService.ts, worldScriptCompletionFetch.ts, dbService.ts migration, AiSections.tsx, settingsSlice.ts.

Docker: Multi-stage Dockerfile (builder → nginx:1.27-alpine). .dockerignore. docker.yml GitHub Actions workflow (GHCR push on v* tags and workflow_dispatch).

Tauri v1.6: tauri.conf.json and Cargo.toml version 1.4.0 → 1.6.0. Auto-updater active: true. TAURI-CI.md example tag updated.

Security: ws override tightened to >=8.20.1; brace-expansion >=5.0.6 override added.


Released: v1.6.2 (2026-05-20, current)

Plot-Board state architecture refactoring: Connections, subplots, and tension overrides moved from plotBoardSlice into projectSlice (wrapped by redux-undo) so plot decisions are undo-able via Ctrl+Z. plotBoardSlice now holds only ephemeral viewport/UI state. New ProjectData fields: plotConnections, plotSubplots, plotTensionOverrides. Selectors: selectPlotConnections, selectPlotSubplots, selectPlotTensionOverrides in projectSelectors.ts. All 5 scene-board components updated. handleDeleteSection now also clears connections for the deleted scene.

Locale-aware readability: services/readabilityFlesch.ts supports 5 language-specific formulas — EN: Flesch, DE: Amstad, FR: Kandel-Moles, ES: Fernández Huerta, IT: Gulpease. Dashboard label updated in all non-English locale files.

CodeQL fix: docker.yml top-level permissions reduced to contents: read; packages: write scoped to job level.

biome.json: Schema version 2.4.12 → 2.4.15.

Quality gate at v1.6.2: lint ✅ typecheck ✅ i18n 1590 keys × 5 locales ✅ 2 024 tests / 178 files — 0 failures ✅ coverage 65.91% lines / 50.59% branches / 56.74% functions ✅ build ✅ E2E ✅ Lighthouse ✅ bundle ≤ 7000 KB ✅


Follow-up Audit — 2026-05-18 Session 2 (i18n Comprehensive Sweep + TypeScript Hardening)

i18n — All Hardcoded Strings Eliminated

  • 1 440 total i18n keys across de/en/es/fr/it — all 5 locales fully in sync (pnpm run i18n:check gate passes).
  • Root causes found and fixed: help.tryTour missing in all locales (command palette was rendering raw key "try.Help"); "Chapter 1" in projectSlice.ts resetProject action (extended payload with optional chapter1Title) and AdvancedImportExport.tsx; German string "Linkes Panel anpassen" hardcoded in ManuscriptView.tsx for a resizer aria-label; "Google Docs / Notion" hardcoded in AdvancedImportExport.tsx; "Meine Templates" and "🌐 Community" hardcoded tabs in TemplateView.tsx; placeholder paragraph in OutlineGeneratorView.tsx.
  • New locale keys added (all 5 languages): help.tryTour, manuscript.spellcheck.didYouMean/applyFix, manuscript.grammar.checkButton, manuscript.zenMode.enter/exit/label, manuscript.resizer.left/right, writer.stopGenerating, writer.tools.selectLabel, writer.versionControl.tooltip, writer.studio.controls.custom/customTonePlaceholder, characters.uploadImage/editorTabsAriaLabel, worlds.uploadImage/editorTabsAriaLabel, settings.ai.temperature.precise/balanced/creative, export.pasteSection.heading, outline.result.body, templates.tabs.myTemplates/community, error.boundary.title/description/reset/reload/report.

ErrorBoundary Refactored

  • components/ui/ErrorBoundary.tsx: inner ErrorFallback functional component accesses useTranslation() (from hooks/useTranslation) to render all strings in the active locale. Import path corrected (contexts/I18nContexthooks/useTranslation). onReset prop passed conditionally to satisfy exactOptionalPropertyTypes (TS2375).

TypeScript 6 Strict Fixes

  • types.ts: 'grok-3' and 'grok-3-mini' added to AiModel union (TS2322 in test).
  • Double-cast pattern (x as unknown as Record<string, unknown>)['key'] for CollaborationService private member access (TS2352).
  • Bracket notation ['gpu'] / ['__TAURI__'] throughout test files — required by TypeScript 6 index-signature enforcement (TS4111).
  • Uint8Array<ArrayBuffer> generics in collaborationService.ts for Web Crypto API strict typing.

Test Quality

  • tests/unit/ErrorBoundary.test.tsx: vi.mock('../../hooks/useTranslation', …) with EN string map — 7/7 pass.
  • tests/unit/AdvancedImportExport.test.tsx: heading assertion updated to use translation key 'export.pasteSection.heading' (consistent with t: (k) => k mock pattern). 5/5 pass.
  • Coverage (2026-05-18, clean single-run): 62.86 % statements · 49.06 % branches · 54.10 % functions · 64.68 % lines — 1 641 tests / 150 test files. All Vitest thresholds pass (53/37/50/55).

Documentation

  • CHANGELOG.md [Unreleased]: i18n sweep, ErrorBoundary refactor, TypeScript strict fixes, test mock fixes, updated coverage numbers. AUDIT.md, TODO.md, ROADMAP.md all updated.

Follow-up Audit — 2026-05-18 (Master-Perfection-Plan v1.5 — Tasks 1–5)

AI Local Inference Stack

  • WebGPU detector service: services/ai/webGpuDetectorService.tsdetectWebGpuDetails() returns {status, adapterDescription, architecture, vramTier} using navigator.gpu.requestAdapter() + adapter.limits.maxBufferSize heuristic. AiProviderCard.tsx (WebLLM tab) shows live GPU status badge (green/yellow/red), WebLLM model dropdown (4 MLC checkpoints), and ONNX model dropdown (DistilGPT-2, GPT-2).
  • ONNX Runtime Web Layer-2: packages/ai-core/src/index.ts adds an ONNX WASM fallback between WebLLM and Transformers.js. LocalAiLayer type extended with 'onnx'. ONNX_SUPPORTED_MODELS exported. vite.config.ts gains vendor-ai-onnx chunk (prevents onnxruntime-web + @xenova/transformers exceeding Workbox's 8 MiB SW cache limit).
  • Orchestration cleanup: orchestrationProviders.ts gains LOCAL_INFERENCE_PROVIDERS, LocalInferenceProvider, and isLocalInferenceProvider() — WebLLM/ONNX/Transformers.js confirmed out of the Vercel AI SDK chain.
  • i18n: 12 new settings.ai.webllm.* and settings.ai.onnx.* keys in all 5 locales (1408 → 1414 total after all Phase 5 additions).

Collaboration Encryption

  • AES-256-GCM foundation: collaborationService.ts gains encryptUpdate(), decryptUpdate(), deriveEncryptionKey() (PBKDF2 600 000 iterations, SHA-256, AES-256-GCM), and getEncryptionStatus() returning 'encrypted' | 'psk-only' | 'plaintext'. Key derivation uses a deterministic SHA-256 salt from projectId. Full in-flight P2P encryption requires y-webrtc RTCDataChannel patching (deferred to v2.0). Implemented: encrypted persistence foundation + key derivation.
  • CollaborationPanel badge: Green E2E Key Derived (AES-256-GCM) badge post-connect with password; amber Room isolation only without. role="status", aria-live="polite".

Tauri Release Pipeline

  • Auto-updater latest.json generation: tauri-build.yml release job now runs a Generate latest.json step after artifact upload — collects signed .sig files for Linux (AppImage), Windows (msi/exe), macOS (dmg) and builds a Tauri v2 update manifest using jq. Uploaded to GitHub Release via gh release upload --clobber.
  • Docs extended: TAURI-UPDATER.md gains full GitHub Secrets table (signing + Apple + Windows Authenticode). TAURI-CI.md gains a 7-step first-release checklist.

Cross-Project-Search v2

  • DB_VERSION 7→8: New projects-index-store with lastIndexed index. crossProjectIndexService.tsindexProject(), listIndexedProjects(), removeProjectIndex() (privacy-preserving: title, logline, characterNames, wordCount; no manuscript plaintext). searchAcrossProjectIndex() added to crossProjectSearchService.ts. CrossProjectSearchPanel.tsx runs two-phase search (index + current project, merged/de-duped). Footer shows live index count or "no projects indexed" hint.

Testing

  • New test files: tests/unit/aiCoreFallbackPaths.test.ts (12 tests), tests/unit/settings/WebLlmPanel.test.tsx (8 tests), tests/unit/crossProjectIndexService.test.ts (7 tests). Extended: collaborationService.test.ts (+6 encryption tests, 27 total), crossProjectSearchService.test.ts (+8 index search tests, 21 total).
  • Key test pattern: onnxruntime-web mock path = ../../packages/ai-core/node_modules/onnxruntime-web/dist/ort.node.min.mjs (Node ESM export condition used by Vitest); vi.spyOn(crypto.subtle, 'deriveKey') to bypass PBKDF2 overhead; vi.hoisted(() => vi.fn()) for detectWebGpuDetails; fake-indexeddb without deleteDatabase (use per-test removeProjectIndex cleanup instead to avoid blocking on open connections).

Documentation

  • CHANGELOG.md [Unreleased]: 5 new entries for all Phase 5 tasks. TODO.md + AUDIT.md updated. locales/*/common.json: 1414 keys (up from 1408 at session start).

Follow-up Audit — 2026-05-17 (E2E mobile selectors + CI hardening)

E2E / Testing

  • Mobile-aware E2E selectors (Phase 1 — CI gate-breaker): WriterView was split into sub-components (WriterViewUI, ContextPanel, ToolsPanel, AiScratchpad). The mobile Chrome (Pixel 5, 393×851) CI project exposed three hard selector breaks: (a) #sidebar is hidden md:flex — invisible on mobile; (b) ContextPanel only rendered when activeMobileTab === 'context' (default: tools); (c) VC button inside hidden md:flex. Fixes: clickNavItem() helper in tests/e2e/helpers.ts (tries desktop sidebar → mobile tab bar → "More" sheet); ARIA tablist/tab/tabpanel pattern added to mobile segmented control in WriterViewUI.tsx; data-testid="writer-version-control-btn" on both desktop and new md:hidden mobile VC button; data-testid="snapshot-label-input" in VersionControlPanel.tsx; data-testid="export-preview" in ExportView.tsx. All four spec files updated (writer, snapshots, a11y, export) to use 2026 Golden Hierarchy selectors (getByRole > getByTestId; never CSS classes or XPath).
  • Unit-test coverage (measured 2026-05-17): 63.32 % Lines · 61.5 % Statements · 47.1 % Branches · 53.2 % Functions — all Vitest thresholds met (55/53/37/50). Exit code 0.

CI / CD Hardening

  • Stryker gate enforced: thresholds.break raised from null30; timeoutMS lowered from 180 000 → 120 000 ms (fail faster per mutant). CI mutation job: continue-on-error: truefalse, timeout-minutes: 2030. Stryker now gates CI when mutation score drops below 30 %.
  • Lighthouse performance promoted to error: categories:performance raised from warn:0.5error:0.4; categories:seo added as warn:0.8; FCP tightened 6 000 → 5 000 ms; LCP tightened 8 000 → 7 000 ms. Accessibility gate (error:0.95) unchanged.
  • OSV scanner wired into CI: osv-scanner.toml existed but was never executed in the security job. Added google/osv-scanner-action@v2 step after pnpm audit — advisories now caught on every push.
  • Concurrency fix: cancel-in-progress: true${{ github.event_name == 'pull_request' }} — prevents cancelling a running deploy on main-branch pushes.
  • Artifact retention aligned: dist reduced to 3 days (only needed by lighthouse + deploy in same run); lighthouse-report and storybook reduced from 14 → 7 days (consistent with other report artifacts).
  • JUnit E2E upload: Playwright JUnit reporter output (tests/e2e/results/junit.xml) now uploaded as e2e-junit artifact — enables per-test annotations in GitHub PR checks.

Documentation

  • Markdown corpus: AUDIT.md header chain + version updated; TODO.md coverage line updated to measured numbers (63 %/47 %/53 %); E2E mobile-fix and CI-hardening items marked complete.

Follow-up Audit — 2026-05-16 (Quality lift: WebLLM selector, cross-project search, test coverage)

Product / Architecture

  • WebLLM model selector: packages/ai-core exports WEBLLM_SUPPORTED_MODELS (4 MLC-packaged checkpoints), WebLlmModelId, WebLlmProgressReport; runLocalTextGeneration now accepts optional modelId and onProgress params. services/localAiFacade.ts forwards both. types.ts expands AiModel union with 4 specific MLC model IDs. Settings → AI (Advanced) shows a dynamic model dropdown + pre-download button + WCAG 2.2 role="progressbar" progress bar + useRef mounted guard (prevents setState-on-unmount during async download). All 5 locales gain settings.ai.webllm.{model,downloadProgress,downloading}.
  • Cross-project search (v1 scope): services/crossProjectSearchService.tssearchAcrossProjects(query, projectData) fuzzy-searches title/logline/manuscript/characters using normalizeSearch() from fuzzyScore.ts; results carry projectId, projectTitle, matchType, excerpt (≤ 120 chars, trailing ), score. Searches Redux state only (v1); multi-project requires DB_VERSION bump and IDB migration, deferred. app/transientUiStore.ts gains isCrossProjectSearchOpen + setCrossProjectSearchOpen. labs-cross-project-search command now opens the panel instead of a stub toast. All 5 locales gain 7 crossSearch.* keys.
  • Collaboration security warning: CollaborationPanel.tsx pre-connection banner (role="alert", aria-live="polite", keyboard-accessible self-hosting link, WCAG 2.2 AA). Hidden after connect. All 5 locales gain collab.securityWarning, collab.securityWarningDetail, collab.selfHostLinkLabel.

Quality / Testing

  • Unit-test coverage (Phase 1 met): 17 new test files, 733 tests total. Vitest thresholds bumped from 25/21/17/24 to 35/30/22/33 (lines/functions/branches/statements). Measured: 36.47 % lines · 35.53 % statements · 24.96 % branches · 30.22 % functions — all Phase 1 targets exceeded. New coverage: commands system (fuzzyScore, palettePreferences, commandSystem), writing/character/binder/management thunks, hooks (useDashboard, useManuscriptView, useGlobalKeyboardShortcuts, useCharacterView, useOutlineGenerator), aiProviderService fallback chain, dbService snapshots + binder assets, crossProjectSearchService.
  • Stryker targets expanded: stryker.conf.json now mutates fuzzyScore.ts, palettePreferences.ts, and commandBuilder.ts alongside the existing codexService.ts and dbMigration.ts.
  • E2E additions: tests/e2e/commands.spec.ts (palette Ctrl+K, text search, fuzzy match, Enter-navigate) and tests/e2e/collaboration.spec.ts (security warning banner visible pre-connection) — CI-only specs.

Documentation

  • Markdown corpus (19 files — unchanged count): README.md Redux Toolkit badge corrected from 6.x to 2.x; CHANGELOG.md [Unreleased] filled with all Phase 3A/3B/3C/4 entries; TODO.md updated to reflect Phase 1+2 completion and ~36 % coverage baseline; ROADMAP.md gains a v1.4.x Quality-lift section (Phases 3A/3B/3C/4 all marked complete); docs/CI.md corrects checkout@v5checkout@v6 reference and adds commands.spec.ts / collaboration.spec.ts E2E entries; .github/SECURITY.md supported-version table updated (1.4.x current, 1.3.x best-effort); AUDIT.md header toolchain line updated.

Follow-up Audit — 2026-05-21 (v1.8 RAG assembly + PWA)

  • RAG prompt assembly: services/ragPromptAssembly.ts; Writer + Plot Board wired; DuckDB embedding 384-dim + ragVectorMigration.ts.
  • PWA: docs/PWA-AUDIT.md documents manifest/SW/share baseline.
  • Local CI: infra/low-end-ci/ (act-first + Eco-Forgejo) for low-RAM laptops.
  • Docs: docs/SPRINT-V1.8.md, README Documentation Hub rows.

Follow-up Audit — 2026-05-10 (Global Best-Practices & content program)

  • App content: Community templates unified to English master (community-templates/index.jsonpublic/); content-guard + Zod validation in fetchCommunityTemplates; help articles with tryActionId for palette/nav jumps; demo/import feedback via toasts instead of alert (WelcomePortal, useSettingsView).
  • UX/diagnostics: Experimental App Health panel (enableAppHealthPanel) under Settings → Info; About shows package.json version instead of placeholder.
  • Docs: docs/BEST-PRACTICES.md (Engineering + Content + CI); README privacy bullet clarified (local vs. cloud AI).
  • Tests: Vitest coverage thresholds moderately raised; community template tests incl. Zod fallback path.

Follow-up Audit — 2026-05-10 (Accessibility & WCAG-oriented QA)

Follow-up Audit — 2026-05-10 (Hybrid-AI, i18n runtime bundles, deployment docs)

  • Hybrid AI / OpenAI-compatible cloud: advancedAi presets (Ollama/LM Studio/vLLM), openAiCompatibleBaseUrl + optional OpenRouter headers, configurable fallback chain in aiProviderService / thunks; Writer orchestration unchanged as primary provider; Tauri CSP connect-src extended.
  • i18n: locales/*/*.json is the source; public/locales/<lang>/bundle.json must be kept in sync with source via node scripts/build-i18n.mjs (e.g. predev / after i18n:check) — otherwise raw key strings appear in the UI.
  • Deployment: docs/DEPLOYMENT.md (GitHub Pages + Vercel, equivalent), root vercel.json for SPA rewrites + build/output.
  • Docs hub / README: Vercel section + link; services/ai/index.ts architecture comment for hybrid.

Follow-up Audit — 2026-05-10 (Gold-Standard pipeline + strict lint/typecheck)

Product / architecture

  • Binder blobs & research: StorageBackend binder asset API; Binder panel import/preview; split-screen research (ManuscriptResearchSplit, transient UI store).
  • Compiler stage 1: Norm-page export, CompileProfile matter fields, EPUB improvements; optional Tauri Pandoc command (pandoc_markdown_to_epub) with JS EPUB fallback.
  • Version control UX: Side-by-side snapshot compare with word-level highlights on changed lines (bounded line count for weak hardware).
  • Scene timeline: Optional StorySection time fields; Scene Board timeline tab with rule engine caps; dashboard mirrors capped hints.
  • Offline style / privacy: Dashboard readability sample (bounded character budget); optional LanguageTool against a user-configured URL with local-only privacy gating (integrations.languageTool*).
  • Local AI hardening: Ollama/Tauri messaging; BroadcastChannel tab leader for WebLLM (electSingleHeavyInferenceTab); local RAG index rebuild → existing saveRagVectors storage with chunked yields.

Tooling / DX

  • Biome: lint uses --error-on-warnings — warnings fail CI locally and in GitHub Actions.
  • TypeScript: exactOptionalPropertyTypes fixes in wordDiff / localRagIndex where applicable.
  • i18n: New keys (e.g. vc.compareTruncated, timeline/dashboard strings) mirrored across de, en, fr, es, it.

Documentation

  • README.md: CI/local validation subsection extended for low-resource workflows and E2E deferral to cloud CI.

Follow-up Audit — 2026-05-10 (Command Center & Helper UX)

Product / architecture

  • Command registry & palette: services/commands/ (definitions, fuzzy scoring, recent/pinned persistence, AI suggestions); components/CommandPalette.tsx — single consumer for ⌘/Ctrl+K flow; execution via runCommandById / CommandExecutorProvider (contexts/CommandExecutorContext.tsx).
  • Transient UI: app/transientUiStore.ts owns isCommandPaletteOpen — palette must not duplicate unrelated React-local open flags.
  • Keyboard: hooks/useGlobalKeyboardShortcuts.ts, services/keyboard/ (matching, conflict hints); defaults features/settings/keyboardShortcutsDefaults.ts; UI components/settings/ShortcutsSection.tsx.
  • Settings hub: Search metadata services/settingsSearchHints.ts; JSON subset import/export services/settingsExchange.ts (Data section).
  • Help: Static chunk retrieval services/help/helpDocRetrieval.ts → doc context in streamAiHelpResponse; locale tryActionId on articles; services/spotlightTour.ts tourId for multiple guided flows.
  • UI primitives: components/ui/Tooltip.tsx, EmptyState.tsx; features/status/statusSlice.ts toast fields commandId / actionLabel; ErrorBoundary GitHub issue link.
  • Section icon SSOT: constants/sections.tsxAPP_SECTIONS: Record<View, SectionConfig> maps every view to icon, colorClass, textColor, accentColor. components/ui/SectionIcon.tsx renders the colored badge (sizes xs/sm/md/lg/xl, aria-hidden). All 14+ view headers, card headers, and nav items consume this SSOT. Tested in tests/unit/SectionIcon.test.tsx.
  • Feature flags: features/featureFlags/featureFlagsSlice.ts ships 23 flags (18 default-on, 5 opt-in default-off — see docs/FEATURE-PARITY.md); e.g. enableProjectHealthScore (dashboard health card). Cross-project search is now permanent core (no flag).

Documentation

  • README, CLAUDE, CONTRIBUTING, copilot-instructions, .cursor/index.mdc, docs/Design-System.md, CHANGELOG [Unreleased]: aligned with the stack above.

Follow-up Audit — 2026-05-06 (documentation inventory)

Markdown corpus (maintainer-curated)

Inventory (19 files): README.md, CONTRIBUTING.md, CHANGELOG.md, AUDIT.md, ROADMAP.md, TODO.md, CLAUDE.md, docs/BEST-PRACTICES.md, docs/Design-System.md, docs/DEPLOYMENT.md, docs/CI.md, docs/ACCESSIBILITY.md, docs/TAURI-CI.md, docs/TAURI-UPDATER.md, docs/graphify.md, docs/history/completed-v1.1.md, .github/SECURITY.md, .github/copilot-instructions.md, .github/ACTIONS-OPTIMIZATIONS.md.

Aligned with the current toolchain and UX: README Documentation Hub lists every entry above plus tests/e2e/helpers.ts and .cursorrules; CONTRIBUTING documents Playwright helpers and Version Control backdrop behavior; docs/CI.md holds E2E authoring notes; agent files (CLAUDE, copilot-instructions) reference dual IndexedDB + tests/e2e/helpers.ts; SECURITY supported-version table matches 1.3.x; CHANGELOG [Unreleased] carries doc-maintenance notes; ACTIONS-OPTIMIZATIONS remains explicitly historical vs docs/CI.md.

Excluded by design: generated Playwright HTML exports (tests/e2e/html-report/**), Stryker sandboxes (.stryker-tmp/**), and IDE-only plans under .cursor/plans/ — not treated as product documentation.

Cross-links to recent engineering fixes

  • E2E: tests/e2e/helpers.ts documents SPA-ready waits and Welcome Portal bootstrap; avoids networkidle under Vite.
  • Version Control UI: Escape closes the panel when no nested modal is open (components/VersionControlPanel.tsx); prevents backdrop from blocking sidebar clicks in tests and manual use.
  • Redux: selectCurrentBranchSnapshots memoized with createSelector to stop unstable array references tripping useSelector warnings.

Follow-up Audit — 2026-05-02

Documentation & DX alignment

  • CI reference docs/CI.md rewritten to match the live workflow (securityqualitybuild / e2e / storybooklighthouse; deploy needs build + e2e). Removed stale references to non-existent jobs (lint, typecheck, test as separate ids; build-node; default tauri job).
  • Lighthouse config path standardized to .lighthouserc.cjs across docs (replacing .js/.json mentions where incorrect).
  • CONTRIBUTING.md updated: Node ≥ 22, Biome (not ESLint), simple-git-hooks + lint-staged, Vite 8, Tailwind via Vite plugin, Act examples with real job names, E2E CI=true note, i18n selector reality (de/en).
  • README.md CI table + Act examples aligned; new Documentation Hub section linking all first-class .md guides and .cursorrules (QNBS v3).
  • .github/ACTIONS-OPTIMIZATIONS.md prefixed with an explicit “historical vs current” disclaimer pointing at docs/CI.md.

Code fix (AI provider)

  • services/aiProviderService.ts: withMergedAbortSignal() merges a standalone AbortSignal argument into AIRequestOptions for streamProvider and generateText, so OpenAI and Ollama honor cancellation the same way as Gemini streaming when callers pass thunkAPI.signal (or equivalent) as the optional parameter. Unit tests extended in tests/unit/aiProviderService.test.ts.

Outstanding

  • Local validation in this environment requires pnpm install; CI remains the canonical full gate (quality matrix, E2E, Lighthouse).

Follow-up — 2026-05-02 (storage + welcome)

  • StorageBackend: Interface extracted to services/storageBackend.ts to remove the storageServicedbService circular type dependency; StorageManager.saveProject is strictly StoryProject.
  • Welcome portal: hasSavedData uses storageService (correct backend on Tauri). Localized demo project import (outline + chapter) for first-time onboarding.
  • i18n gate: scripts/check-i18n-keys.mjs + pnpm run i18n:check in CI (key parity vs en; UI selector: de/en/fr/es/it). Tauri: .github/workflows/tauri-build.yml + docs/TAURI-CI.md — desktop artifacts + GitHub Release attachments on v* tags.

Follow-up Audit — 2026-05-08 (v1.3.0 release engineering)

Stability fixes (post-RC)

  • listenerMiddleware: Auto-save listeners for project and settings now call listenerApi.getOriginalState() before await listenerApi.delay(...), satisfying Redux Toolkit’s synchronous contract and removing console/runtime errors during debounced saves.
  • dbService.saveStoryCodex: Aligns with CODEX_STORE schema (keyPath: 'projectId'): single-argument put for inline-key records; compressed LZ payloads stored as { projectId, compressedUtf16 } with matching getStoryCodex decode path.
  • Playwright: Chromium-only projects under CI=true (matches CI browser install); snapshotPathTemplate omits {platform} so committed PNG baselines align across Linux runners and Windows dev boxes; visual regression uses bounded screenshot timeout.
  • Stryker: thresholds.break set to null until mutation testing kills enough mutants on codexService / dbMigration; CI mutation job remains informational (continue-on-error: true).
  • Documentation sweep: README, docs/CI.md, CONTRIBUTING, CHANGELOG, AUDIT, SECURITY supported-version table, CLAUDE.md commands — aligned with 1.3.0 and current workflows.

Verification (release gate)

  • pnpm run typecheck · pnpm run lint · pnpm run i18n:check · pnpm run test:run · pnpm run mutation (report) · CI=true pnpm run test:e2e — executed during release prep on maintainer hardware.

Follow-up Audit — 2026-05-06 (architecture stack)

Architecture and platform

  • Migrated from single-package layout to pnpm workspace + Turborepo baseline:
    • pnpm-workspace.yaml now includes packages/*
    • turbo.json defines orchestrated build, dev, lint, typecheck, test tasks
    • Created @domain/ai-core and @domain/ui workspace packages
  • Added tri-layer state model:
    • Persistent: existing Redux + listener middleware
    • Cached: RTK Query slice (app/aiApi.ts)
    • Transient: Zustand store (app/transientUiStore.ts)

Storage and integrity

  • Refactored IndexedDB backend into dual DB topology:
    • worldscript-state-db for app/snapshot state
    • worldscript-data-db for images, codex, rag vectors
  • Added visibilitychange persistence flush in index.tsx to reduce hidden-tab data loss windows.

AI, sync, and security

  • Added local AI facade integration (services/localAiFacade.ts) on top of @domain/ai-core WorkerBus abstractions.
  • Added BYOK provider hardening in services/aiProviderService.ts:
    • Grok provider integration
    • Zod response shape validation
    • local-only mode cloud blocking
    • EU residency guardrail for restricted providers
  • Added collaboration exponential backoff path in services/collaborationService.ts.
  • Added EXIF stripping utility (services/imageSanitizer.ts) for media hygiene.

CI and verification

  • Added mutation testing stage scaffold (Stryker) in .github/workflows/ci.yml.
  • Local validation executed:
    • pnpm run typecheck
    • pnpm run lint
    • pnpm run graphify:update

Residual risks / next audit targets

  • Socket.dev false positive — json-schema@0.4.0 (2026-06-10): Socket flagged this package as "90% likely obfuscated". Manual inspection confirms this is a false positive: the package ships two fully readable, well-commented files (lib/validate.js 271 lines, lib/links.js 64 lines) under AFL-2.1/BSD-3-Clause. It is a legitimate JSON Schema validator published in 2012 by Kris Zyp (Dojo Foundation), weekly downloads in the millions. 0.4.0 is the latest and only stable version (published 2021-11-09); @ai-sdk/provider@3.0.10 (also latest) is the sole depender. No upgrade path exists. Accepted as false positive. To suppress on future PRs, use the Socket dashboard to set triage state to "acceptable risk" for npm/json-schema@0.4.0.
  • Local AI layers currently include placeholder fallback behavior; full WebLLM + Transformers runtime path should be completed in a dedicated performance validation cycle.
  • Dual-DB migration from legacy worldscript-db Resolved (2026-05-08): idempotent migration migrateLegacyWorldscriptDbIfNeeded in services/dbMigration.ts runs from services/dbService.ts initDB(); Vitest fixtures in tests/unit/dbMigration.test.ts (fake-indexeddb) copy legacy stores (app-data-store, snapshots-store, images-store, rag-vectors-store, codex-store) into worldscript-state-db / worldscript-data-db when the legacy DB exists and dual DBs are empty.
  • CI mutation stage runs stryker.conf.json against focused targets (services/codexService.ts, services/dbMigration.ts); tune thresholds as coverage grows.
  • Automated accessibility: Playwright + @axe-core/playwright smoke test (tests/e2e/a11y.spec.ts) gates serious/critical axe violations on load (color-contrast disabled in CI for theme-variable variance); manual WCAG/sr verification remains recommended for releases.

Self-Audit Summary

  • pnpm outdated identified outdated dependencies that should be reviewed in a follow-up dependency refresh cycle.
  • pnpm audit baseline in this cycle reported 10 vulnerabilities (4 low, 1 moderate, 4 high, 1 critical).
  • pnpm run lint:fix completed successfully; 45 existing warnings remain from legacy any usage and React hook dependency concerns.
  • pnpm run typecheck passed without type errors.
  • pnpm run build completed successfully with production artifact generation.
  • pnpm run test:coverage passed with 110 tests, 96.1% statements, 81.81% branches, and 97.87% lines.

Dependency Hardening Update (2026-04-16)

  • Implemented conservative dependency remediation in package.json and pnpm-lock.yaml:
    • Upgraded jspdf from ^2.5.1 to ^4.2.1.
    • Added npm overrides for @lhci/cli to force modern transitive packages:
      • chrome-launcher -> ^1.2.1
      • tmp -> ^0.2.5
  • Removed deprecated transitive chain elements from the active install graph:
    • inflight@1.0.6 no longer present.
    • rimraf@2.x/3.x no longer present.
    • old glob@7 deprecation path no longer present.
  • Remaining deprecation warning is currently limited to node-domexception@1.0.0, pulled transitively via:
    • @google/genai -> google-auth-library -> gaxios -> node-fetch -> fetch-blob.
    • This is currently an upstream dependency-chain constraint.
  • Validation after remediation:
    • pnpm run lint -- --max-warnings=0 passed.
    • pnpm run typecheck passed.
    • pnpm run test:run passed (113/113 tests).
    • pnpm run build passed.
  • pnpm audit now reports 4 high vulnerabilities (down from 10 total, including 1 critical):
    • all remaining findings are in vite-plugin-pwa / workbox-build via @rollup/plugin-terser -> serialize-javascript.
    • npm suggests vite-plugin-pwa@0.19.8 as a fix path, which is a major backward downgrade from the current line and not applied in this conservative cycle.

Current Status

  • pnpm audit reports 0 vulnerabilities (0 low, 0 moderate, 0 high, 0 critical) as of 2026-04-17.
  • protobufjs critical vulnerability resolved via pnpm audit fix (upgraded to ≥7.5.5).
  • serialize-javascript high vulnerabilities resolved via npm overrides (vite-plugin-pwaworkbox-build@rollup/plugin-terserserialize-javascript@^7.0.5).
  • All localStorage/sessionStorage accesses are now guarded with try/catch for SSR/test safety.
  • CI pipeline extended with Security Audit, Lighthouse CI, and Storybook jobs.
  • Tauri capabilities updated: added fs:allow-read-dir and fs:allow-remove permissions.
  • AI service utilities deduplicated into shared services/aiUtils.ts.
  • Bundle analyzer (rollup-plugin-visualizer) added as opt-in devDep (pnpm run analyze).
  • fileSystemService.ts type-unsafe references to non-existent StoryProject.author/.description removed.
  • One deprecation (node-domexception) remains as an upstream transitive dependency from the Gemini SDK stack — accepted risk, no local fix.
  • The repository is stable: build, lint, typecheck, and coverage all pass.

Light Mode Theming (Resolved 2026-04-17)

  • Fixed: Tailwind CDN dark: prefix was using media strategy (OS preference) instead of selector strategy (.dark-theme body class), causing all dark: classes to ignore the in-app theme toggle.
  • Fixed: ~65 hardcoded dark-mode-only styling patterns (bg-white/5, border-white/5, via-white/15, bg-black/40, ring-white/10, via-black/40, text-white on non-interactive backgrounds) replaced with theme-aware CSS custom properties.
  • Added: 6 new CSS custom properties (--overlay-backdrop, --glass-bg, --glass-bg-hover, --glass-border, --glass-highlight, --card-gradient-overlay) with appropriate values for both dark and light themes.
  • Fixed: Aurora blob opacity reduced from 0.25 to 0.08 in light mode.

Tauri Feature Parity (historical baseline vs current — 2026-05)

The bullet list below described pre-v1.2 gaps between fileSystemService.ts and dbService.ts. As of v1.2.0, parity work is completed (see ROADMAP.md, TODO.md, archive): LZ-String compression, auto-snapshots with numeric IDs aligned with IDB, retry around filesystem ops, deleteImage() / hasSavedData(), Story Codex + RAG vectors under per-project paths, and routing via the shared StorageBackend contract (services/storageBackend.ts).

Remaining desktop release items are not feature-parity gaps but v1.2.1 release engineering: Tauri v2 auto-update (tauri-plugin-updater), code-signing, and CI/release docs (docs/TAURI-CI.md, .github/workflows/tauri-build.yml).

Executive Summary

WorldScript Studio is a well-architected React 19 + Redux Toolkit PWA with strong TypeScript enforcement, excellent i18n, and sophisticated offline-first data management. The codebase demonstrates mature React patterns and thoughtful accessibility support. Main improvement areas are test coverage, AI request lifecycle management, and desktop (Tauri) security hardening.

Scorecard

Aspect Rating Notes
Type Safety ★★★★★ Strict mode, exactOptionalPropertyTypes
Architecture ★★★★☆ Clean feature-sliced design, clear patterns
Accessibility ★★★★☆ Strong ARIA, focus management, color-blind modes
i18n ★★★★★ Modular, 5 languages, persistent selection
PWA / Offline ★★★★★ Workbox, versioned caches, smart strategies
State Management ★★★★☆ Redux-Undo well integrated, auto-save validated
Security ★★★★☆ CSP hardened, non-extractable CryptoKey, PSK collab, import validation
Test Coverage ★★★★☆ 1 641 Vitest-Tests (Unit) / 150 Dateien; 62.86 % Statements, 64.68 % Lines, 49.06 % Branches; Playwright-E2E, glob-basierte Coverage-Floors
Documentation ★★★★★ README, CONTRIBUTING, ROADMAP, TODO, CHANGELOG, AUDIT
Performance ★★★★☆ Code-splitting with 10+ manual chunks, Lighthouse CI
CI/CD ★★★★★ security→quality→build (inkl. i18n-Gate, bundle:budget, analyze-Artifact), e2e, Lighthouse, Storybook, Pages-Deploy

Critical Findings (🔴)

1. Hardcoded Language in AI Hooks ✅ FIXED

Files: hooks/useConsistencyCheckerView.ts, hooks/useCriticView.ts Issue: Both hooks passed hardcoded 'en' to AI service functions instead of reading from user settings. Impact: Non-English users received AI prompts and responses in the wrong language. Resolution: Fixed — now reads language from useTranslation() and aiCreativity from Redux settings selector.

2. Tauri CSP is null — No Content Security Policy ✅ FIXED

File: src-tauri/tauri.conf.json Issue: "security": { "csp": null } — the desktop app has no Content Security Policy. Resolution: Set comprehensive CSP string including connect-src for Gemini API + WebRTC signaling. Identifier fixed to com.worldscript.studio, version synced to 1.0.0. Capabilities narrowed to granular permissions.

3. No Request Cancellation for AI Thunks ✅ FIXED

Files: features/project/projectSlice.ts, hooks/useWriterView.ts Issue: AI generation thunks did not accept or use AbortController / AbortSignal. Resolution: Added thunkAPI.signal to all 14 AI-calling thunks. Added AbortController + cleanup to useConsistencyCheckerView and useCriticView hooks. Activated the unused retry() function in geminiService.

4. Auto-Save Memory Exhaustion Risk ✅ FIXED

File: app/listenerMiddleware.ts Issue: No validation that state.project.present was valid before saving. Resolution: Added null-check for presentData, 5MB size warning, and generationHistory capped at 50 entries FIFO.


High Priority Findings (🟡)

5. Minimal Test Coverage ✅ IMPROVED

Previous: 4 unit test files. Current: 11 unit test files, 80 tests passing. Coverage thresholds (50%) set. Remaining: E2E tests, view hook tests, additional component tests.

6. any Type Casts in Multiple Hooks ✅ PARTIALLY FIXED

Files: app/hooks.ts, app/store.ts Resolution: Removed shallowEqual as any and preloadedState as any. Remaining as any casts in view hooks (useSceneBoardView, useSettingsView) tracked for future fix.

7. Logger Middleware Performance in Dev ✅ FIXED

File: app/store.ts Resolution: Logger now opt-in via localStorage.getItem('debugRedux') === 'true'.

8. No Per-View Error Boundaries ✅ FIXED

File: App.tsx, components/ui/ErrorBoundary.tsx Resolution: Added key={currentView} for auto-reset on view switch. ErrorBoundary now has onReset prop with "Reset View" button.

9. P2P Collaboration Without Encryption ✅ IMPROVED

File: services/collaborationService.ts, components/CollaborationPanel.tsx Resolution: Added PSK-based room isolation via SHA-256 room ID derivation. Room password input in CollaborationPanel. Full E2E encryption deferred to v2.0.

10. Device-Scoped Encryption Key ✅ FIXED

File: services/dbService.ts, components/ApiKeySection.tsx Resolution: getGeminiApiKey() and getApiKey() now return 'DECRYPT_FAILED' on decrypt errors. hasGeminiApiKey() filters this value. ApiKeySection shows red warning banner with re-entry prompt.


Medium Priority Findings (🟠)

11. No DevContainer Configuration ✅ FIXED

Resolution: .devcontainer/devcontainer.json added with Node.js LTS image, corepack enable && pnpm install --frozen-lockfile as postCreateCommand, recommended extensions (ESLint, Prettier, Tailwind CSS IntelliSense), and port forwarding for dev (3000) and Storybook (6006).

12. Redundant deploy.yml Workflow ✅ FIXED

Resolution: No separate deploy.yml exists. ci.yml handles the full pipeline including deployment to GitHub Pages via actions/deploy-pages@v4.

13. Version Mismatch: Tauri vs npm ✅ FIXED

Files: src-tauri/tauri.conf.json, package.json Issue: Tauri had version 1.0.0, package.json 1.1.1. frontendDist pointed to ../build instead of ../dist (Vite default output). Window title was lowercase worldscript-studio. Resolution: Aligned version to 1.1.1, fixed frontendDist to ../dist, set proper product name and window title to WorldScript Studio, improved window defaults (1280×800, centered, min size constraints). Narrowed CSP connect-src by removing overly broad https://*.googleapis.com wildcard. Effort: Low | Priority: Low

15. No Performance Budgets ✅ FIXED

Resolution: Lighthouse CI job added to CI pipeline (.github/workflows/ci.yml). Performance budgets defined in .lighthouserc.cjs with assertions for Performance ≥ 0.9, FCP ≤ 1800ms, LCP ≤ 2500ms, TBT ≤ 150ms, CLS ≤ 0.1. Bundle analyzer available via pnpm run analyze.

16. Potential Memory Leaks in ManuscriptView Resize ✅ FIXED

File: components/ManuscriptView.tsx Issue: Resize event listeners were added in useCallback without guaranteed cleanup on unmount. Resolution: Refactored to useEffect with AbortController + { signal } option and throttled handlers. Cleanup runs on unmount via controller.abort().


New Findings (2026-04-18 Audit)

17. Feature-Flag-System ✅ FIXED

Files: features/featureFlags/featureFlagsSlice.ts, contexts/FeatureFlagsContext.tsx, components/SettingsView.tsx Resolution: Fully implemented with 3 flags (enableOllama, enablePerformanceBudgets, enableVisualRegression), localStorage persistence via featureFlagsPersistenceMiddleware, UI toggle in SettingsView, and useFeatureFlags() hook.

18. Infinite Loop in codexService.extractStoryCodex ✅ FIXED (v1.1.2)

File: services/codexService.ts (line 118-127) Issue: while loop with exec() and three continue statements skipped the match = regex.exec(text) re-assignment, causing an infinite loop when any matched proper noun was a stopword (e.g. "The"), shorter than 3 chars, or already a known entity. Triggered on virtually every English manuscript. Impact: Browser tab freeze after 1.2s debounced codex extraction on every manuscript edit. Resolution: Replaced while + manual exec() with for (const match of text.matchAll(...)) pattern.

19. Modal Focus-Trap Cleanup Fragility ✅ FIXED (v1.1.2)

File: components/ui/Modal.tsx Issue: useEffect had two conditional return paths for cleanup. While React's cleanup semantics prevent actual leaks, the pattern was fragile and hard to reason about. Resolution: Consolidated into single cleanup function with early return for !isOpen. Added test for body overflow restoration.

20. FOUC Theme Initialization ✅ FIXED (v1.1.2)

File: features/settings/settingsSlice.ts, index.html Issue: applyInitialTheme() read localStorage.getItem('worldscript-state') — a key never written in production (only in tests). JSON.parse had no try/catch. Result: flash of wrong theme on every page load. Resolution: Added inline <script> in <head> reading worldscript-theme from localStorage. Theme mirrored to localStorage on save. Wrapped JSON.parse in try/catch. Removed dead worldscript-state read.

21. Untranslated FR/ES/IT Locales ✅ FIXED (v1.1.2)

Issue: French, Spanish, Italian locale files contained 96% English strings verbatim. Language selector offered all 5 languages, giving users untranslated UI. Resolution: Removed FR/ES/IT from language selector. Locale files retained for future translation work.

22. CryptoKey Derived From Public Inputs ✅ FIXED (v1.2.0)

File: services/dbService.ts Issue: AES-256-GCM encryption key was derived from location.origin + hardcoded string + navigator.userAgent — all publicly reconstructible. Anyone with IndexedDB access could decrypt API keys. Resolution: Replaced with crypto.subtle.generateKey() producing a non-extractable CryptoKey stored directly in IndexedDB via structured clone. Migration path re-encrypts existing keys automatically.

23. CSP img-src Too Permissive ✅ FIXED (v1.2.0)

File: index.html Issue: img-src 'self' data: blob: https: allowed any HTTPS host, enabling image-beacon exfiltration via XSS. Resolution: Tightened to img-src 'self' data: blob:. Added frame-ancestors 'none' and upgrade-insecure-requests.

24. Import JSON Without Schema Validation ✅ FIXED (v1.2.0)

File: features/project/projectSlice.ts Issue: JSON.parse(text) as ImportedProjectData — compile-time-only assertion with zero runtime validation. Malformed imports could corrupt state or enable XSS via injected content. Resolution: Added Valibot schema validation before dispatch. Invalid imports show user-facing error toast.

25. Dead Code in aiThunkUtils and store ✅ FIXED (v1.1.2)

Files: features/project/aiThunkUtils.ts, app/store.ts Issue: buildDeduplicationKey was never imported (dead code). 'persist/PERSIST' in ignoredActions referenced non-existent redux-persist. Resolution: Removed both.

26. Coverage Config Vanity Metric ✅ FIXED (v1.2.0)

File: vitest.config.ts Issue: Coverage included only 24 specific files (those with tests), not the full project. Thresholds measured a curated island, not real coverage. Resolution: Replaced with glob patterns covering all source directories. Thresholds lowered to honest all-up baseline.

27. _tempStore Type Derivation — Kept (v1.2.0)

File: app/store.ts Issue: A second configureStore() call at module-import just to derive RootState/AppDispatch types. Runs serializable check middleware, doubles side effects. Resolution: Investigated — deriving types from setupStore return type causes RootState to resolve to unknown because storeOptions is typed as Parameters<typeof configureStore>[0] which widens the inferred state. The _tempStore approach is the recommended RTK pattern for factory-based store setup. Added clarifying comment. Removed dead 'persist/PERSIST' from ignoredActions.

28. Settings Change Triggers Full Project Save ✅ FIXED (v1.2.0)

File: app/listenerMiddleware.ts Issue: Auto-save listener fired on both project and settings changes, always saving both. Toggling a theme slider triggered full multi-MB project serialization. Resolution: Split into separate listeners: project changes → saveProject, settings changes → saveSettings.

29. testAIConnection('gemini') Returns Fake Success ✅ FIXED (v1.2.0)

File: services/aiProviderService.ts Issue: case 'gemini': return { ok: true } — no actual API call. Users got "connected" confirmation with invalid API keys. Resolution: Added real lightweight API validation call with timeout.

30. Silent Model Downgrade in OpenAI Provider ✅ FIXED (v1.2.0)

File: services/aiProviderService.ts Issue: Non-gpt-prefixed models (e.g. o1-preview, claude-sonnet-4-5) silently replaced with gpt-4o-mini. No warning to user. Resolution: Throws descriptive error instead of silent fallback.

31. OpenAI Stream Loop Missing Abort Check ✅ FIXED (v1.2.0)

File: services/aiProviderService.ts Issue: while(true) { reader.read() } loop never checked signal.aborted. Cancel action continued streaming until server closed connection. Resolution: Added signal.aborted check at loop start.

32. communityTemplateService Misleading Error Messages ✅ FIXED (v1.2.0)

File: services/communityTemplateService.ts Issue: Error messages and comments referenced "GitHub API" but the service fetches local static assets. Resolution: Updated all references to reflect bundled static asset source.

33. Collaboration Awareness State Without Validation ✅ FIXED (v1.2.0)

File: services/collaborationService.ts Issue: Remote peer awareness state cast directly to CollaborationUser without validation. Malicious peers could inject arbitrary data. Resolution: Added validation for user id (string, max length), name (string, max 100 chars), and color (hex format).

34. SettingsView.tsx 2116 LOC Monolith ✅ FIXED (v1.2.0)

File: components/SettingsView.tsx Issue: Single 2116-line component — untestable, unreviewable. Resolution: Decomposed into section sub-components (Appearance, AI, Accessibility, Data, Collaboration, FeatureFlags).


Low Priority Findings (🟢)

17. No Feature Flags

No mechanism to selectively enable/disable features for rollout or testing. Recommendation: Consider a simple localStorage-based feature flag system for experimental features.

18. Console Logging Instead of Logging Framework

Multiple console.log, console.warn, and console.error calls throughout the codebase. Recommendation: Create a minimal logging utility that can be configured per-environment and optionally integrated with error tracking (e.g., Sentry).

19. Storybook Has Only 3 Stories ✅ FIXED

Directory: stories/ Previous: Button, Card, Input stories. Current: 10 stories — Button, Card, Input, Modal, Toast, Spinner, Drawer, ErrorBoundary, ManuscriptView, plus storybookProviders utility. Resolution: Stories for Modal, Toast, Spinner, Drawer, and ErrorBoundary added with a11y addon assertions.

20. No Request Deduplication for AI Calls

Issue: Identical AI requests can be sent simultaneously (e.g., double-clicking a generation button). Recommendation: Implement request deduplication in geminiService.ts using a pending-request map keyed by a hash of the prompt parameters.

21. Render-Blocking Google Fonts @import ✅ FIXED

File: index.css (lines 3–5) Issue: Three @import url("https://fonts.googleapis.com/...") statements were render-blocking and required external network requests, breaking offline font loading and widening the CSP surface. Resolution: Replaced with self-hosted @fontsource/inter, @fontsource/jetbrains-mono, @fontsource/merriweather. Removed fonts.googleapis.com from CSP style-src/connect-src and fonts.gstatic.com from font-src/connect-src. Removed preconnect links and Google Fonts SW cache handler.


Environment & Configuration Findings

CI/CD Pipeline

  • ✅ Full pipeline: security → lint → typecheck → test → build → lighthouse → storybook → deploy
  • ✅ Security audit job with pnpm audit --audit-level=high and dependency-review-action
  • ✅ Lighthouse CI job with performance budgets from .lighthouserc.cjs
  • ✅ Storybook build + artifact upload
  • ✅ ESLint and typecheck now run in hard-fail mode (was soft-fail)
  • ✅ Coverage thresholds (50%) configured in vitest.config.ts

Git Configuration

  • .gitignore properly configured (fixed: now includes src-tauri/target/)
  • ✅ Pre-commit: simple-git-hooks + lint-staged (Biome)
  • ✅ Conventional Commits recommended in CONTRIBUTING.md

Biome (lint + format)

  • Biome is authoritative (biome.json); pnpm run lint / lint:fix / Prettier-era duplicates removed from contributor docs
  • Pre-commit: simple-git-hooks + lint-stagedbiome check --write on staged files

Package.json

  • "type": "module" for ES modules
  • "private": true prevents accidental npm publishing
  • ⚠️ Watch pnpm.peerDependencyRules / overrides when upgrading Vite or vite-plugin-pwa (documented in package.json)

Recommended Next Steps (Prioritized)

# Action Effort Impact Priority
1 Add Tauri CSP Low High ✅ Done
2 Add AbortController to AI thunks Medium High ✅ Done
3 Validate undo-envelope reconstruction Low High ✅ Done
4 Add per-view error boundaries Low Medium ✅ Done
5 Make Redux logger opt-in Low Medium ✅ Done
6 Fix noExplicitAny — 2 literal + 137 biome-ignore suppressions (133 in test mocks [legitimate per CLAUDE.md pattern], 4 in production files) Medium Medium 🟡 4 production fixed; 133 test mocks accepted
7 Raise unit/integration coverage toward 50–70 % (Vitest vitest.config.ts: breites coverage.include, Schwellen = aktueller Gesamt‑%; CI gate) High High 🟡 Ongoing
8 Add DevContainer configuration Low Medium 🟠 Backlog
9 Fix ManuscriptView resize memory leak Low Medium 🟠 Backlog
10 Add performance budgets Medium Medium ✅ Done
11 Encrypt P2P collaboration Medium Medium ✅ Done (PSK)
12 Align Tauri/npm versions Low Low ✅ Done
14 Add logging framework Medium Low 🟢 Backlog

Files Changed in This Audit

File Change
hooks/useConsistencyCheckerView.ts Fixed hardcoded 'en' → dynamic language from settings
hooks/useCriticView.ts Fixed hardcoded 'en' → dynamic language from settings
.gitignore Added src-tauri/target/
.prettierrc Removed (empty duplicate; .prettierrc.json is authoritative)
README.md Fixed 50+ Markdown lint errors (MD022, MD031, MD032, MD040, MD060)
CHANGELOG.md Created — Keep a Changelog format
.github/copilot-instructions.md Created — Project coding guidelines for Copilot
AUDIT.md Created — This document

Historical Baseline Audit (2026-04-15)

The following section preserves the original repository audit performed on 2026-04-15. All critical and high-priority findings from this baseline have since been addressed in the main audit above.

Click to expand the 2026-04-15 baseline audit

Executive Summary (Baseline)

WorldScript Studio was assessed as a strong, modern React/TypeScript application with good architectural design, strict TypeScript configuration, and a comprehensive CI/CD pipeline. The largest risks at that time were in the desktop backend implementation, the persistence layer, incomplete test coverage, and inconsistent dev/prod logging.

Critical Findings (Baseline)

  1. Desktop-Backend stored Provider API keys unencryptedservices/fileSystemService.ts saved keys as plaintext via saveApiKey(). → Resolved: AES-GCM encryption applied.
  2. Type incompatibility between StorageBackend and dbService — Addressed: contract in services/storageBackend.ts; IndexedDB + Tauri FS both implement it; proxy uses StoryProject for saveProject.
  3. AUTO_SNAPSHOT_INTERVAL mismatchservices/dbService.ts used 30s but comment said 30 minutes. → Clarified and documented.
  4. No production logging controlconsole.* calls scattered across services without environment filtering. → Logger service introduced (services/logger.ts).
  5. DECRYPT_FAILED as API key sentineldbService.ts returned the string 'DECRYPT_FAILED' on decrypt errors instead of null. → Resolved: explicit recovery flow with UI warning added.

High-Priority Findings (Baseline)

  • Incomplete E2E test coverage → Ongoing improvement.
  • Storage backend dynamic initialization could cause runtime errors → Guards added.
  • Scattered console.* statements in service files → Centralized via logger.
  • Auto-save persistence validation at risk from redux-undo format changes → serializeProjectForSave() extraction recommended.

Medium-Priority Findings (Baseline)

  • No performance budget / bundle limits → Lighthouse CI added.
  • No per-view Error Boundaries → Added with recovery button.
  • Feature flag system incomplete → FeatureFlags slice exists, runtime toggle deferred.
  • No dedicated logging service → services/logger.ts created.
  • Unsafe type casts in fileSystemService.tsPartially addressed.

Low-Priority Findings (Baseline)

  • Storybook expansion needed → 10 stories now available.
  • Changelog standardization → Keep a Changelog format adopted.
  • Outdated dependencies → Conservative remediation applied.

Remediation Steps Applied Post-Baseline

  • All console.log/console.warn/console.error calls in app code replaced by central services/logger.ts system.
  • public/sw.js switched to internal swLogger for consistent Service Worker logging.
  • features/featureFlags/featureFlagsSlice.ts corrected (empty object type ESLint error).
  • tests/unit/featureFlagsSlice.test.ts switched to import type for type-only imports.
  • ESLint and TypeScript checks pass after all changes.

P0/P1 Implementation — 2026-06-12

Plugin System Hardening (P0)

Status Item
Replaced unsafe Worker global eval with function-scope sandbox in workers/plugin.worker.ts
Added runtime guards for Function.prototype.constructor, eval, WebAssembly, async/generator constructors
Normalized ESM export function run syntax for new Function execution
Converted denied async APIs to synchronous throwers so misuse always propagates
Made worker handler throw on plugin failures instead of returning { success: false }
Added read-only project snapshot + side-effect bridge in services/pluginRegistry.ts
Added plugin.sandbox capability to WorkerBus v2 schema
Added telemetry logging to pluginRegistry.ts (loadPlugin method)
Created/extended tests/unit/workers/plugin.worker.test.ts (18 tests)
Created/extended tests/unit/plugins/pluginRegistryLoad.test.ts (8 tests)
Updated docs/PLUGINS-BETA.md with accurate sandbox/limitations description
Full timeout/abort coupling for dynamic import() and sync loops (P0-2)

Supply-Chain Hardening (P1)

Status Item
Added strictDepBuilds: true to pnpm-workspace.yaml
Added blockExoticSubdeps: true to pnpm-workspace.yaml
Added minimumReleaseAge: 10080 (7 days) to pnpm-workspace.yaml
Added security justification comments to pnpm-workspace.yaml overrides

OpenRouter Provider Hardening (P1)

Status Item
Expanded tests/unit/ai/openrouterProvider.test.ts to 19 tests covering streaming, 429 retry, Retry-After, circuit breaker, non-OK errors, AbortSignal, malformed SSE, RPM tracking
Added test-only delay provider hook to keep retry tests fast and deterministic

Copilot Apply-Flow Hardening (P1)

Status Item
Extended tests/unit/copilot/useGlobalCopilot.test.ts with 7 applyLastSuggestion tests (70% gate, empty section, missing section, status lifecycle, dispatch verification)

Mutation Testing Expansion (P1)

Status Item
Added services/ai/aiRetry.ts to mutation targets
Added services/ai/fetchAdapter.ts to mutation targets
Added services/ai/routingLogger.ts to mutation targets
Added services/copilot/heuristicEngine.ts, insightGenerator.ts, actionApplier.ts, copilotContextService.ts to mutation targets
Added services/ai/aiModeService.ts, services/ai/providers/openrouterProvider.ts, services/pluginRegistry.ts to mutation targets

Voice Pipeline Status

Status Item
🔄 test.fixme tests in whisper-stt.spec.ts remain (headless CI limitation)
VoiceActivityCoordinator has comprehensive unit tests (11 tests)
Voice nightly workflow (voice-nightly.yml) configured correctly

Tauri Build Pipeline

Status Item
tauri-build.yml has proper workflow_dispatch handling
Signing key logic correctly disables updater artifacts when no key present
Full verification pending maintainer secrets

Error Handling & Observability

Status Item
Plugin execution errors logged via structured logger
Voice error paths set setVoiceError in Redux
Storage encryption errors throw descriptive messages

Tauri Build Pipeline (updated)

Status Item
tauri-build.yml references secrets.TAURI_SIGNING_PRIVATE_KEY and password correctly
Workflow unsets signing keys and disables updater artifacts for workflow_dispatch test builds
🔄 Full end-to-end signing verification pending next tag or manual workflow dispatch with secrets

Known Overrides Table (Supply-Chain Hardening)

Source of truth for the override floors is pnpm-workspace.yaml (overrides:). Advisory IDs below were re-verified against the GitHub Advisory Database on 2026-06-13, except the @xmldom/xmldom, fast-uri, and qs rows, re-verified 2026-09-02 (Dependabot alert #79 and two pnpm audit sweeps) after all three floors were found to no longer exclude the then-currently-resolved vulnerable version, and the fflate row, added 2026-09-03 the same day its advisory was elevated to GitHub-reviewed status. Floors are intentionally conservative (set at or above the patched version) as preventive supply-chain pins; several apply only to dev/test transitive deps and are never shipped to users.

Package Override Advisory Justification
esbuild >=0.28.1 GHSA-67mh-4wv8-2f99 Dev-server CORS let any website send requests to the esbuild dev server and read the response (≤0.24.2; fixed 0.25.0). Build-tool only, never shipped. Pinned in the 2026-06 security merge.
serialize-javascript >=7.0.3 GHSA-76p7-773f-r4q5 / CVE-2024-11831 Regex XSS in serialized output (<6.0.2).
tmp ^0.2.6 GHSA-52f5-9888-hmc6 / CVE-2025-54798 Arbitrary temp file/dir write via symlink dir parameter (≤0.2.3; fixed 0.2.4).
@xmldom/xmldom >=0.9.12 GHSA-6gmq-8vp8-gcm6 / CVE-2026-83610 (supersedes GHSA-5fg8-2547-mr8q / CVE-2022-39353) XML fragment injection via invalid EntityReference.nodeName during requireWellFormed serialization (≥0.9.0 ≤0.9.11; fixed 0.9.12). The prior >=0.8.13 floor no longer excluded this — Dependabot alert #79, 2026-09-02. Ships via mammoth (.docx export), a production dependency.
protobufjs >=7.5.6 GHSA-h755-8qp9-cq85 / CVE-2023-36665 Prototype pollution (6.10.0–7.2.3; fixed 7.2.4).
axios >=1.15.2 GHSA-jr5f-v2jv-69x6 / CVE-2025-27152 SSRF + credential leak via absolute URL.
basic-ftp >=5.3.1 GHSA-5rq4-664w-9x2c / CVE-2026-27699 Path traversal in downloadToDir() (<5.2.0). Dev/test transitive.
fast-uri >=3.1.6 GHSA-5jgf-p345-68v8 / GHSA-f65p-4m7j-42xc / GHSA-fph4-wmhf-6fwf / GHSA-jqff-g426-hqxp (supersedes GHSA-q3j6-qgpj-74h6 / CVE-2026-6321) Host confusion / SSRF via percent-encoding and IPv6 normalization (resolved 3.1.5 vulnerable; fixed at or above 3.1.6). The prior >=3.1.5 floor no longer excluded this — found via a routine pnpm audit sweep, 2026-09-02. Dev-only transitive (@stryker-mutator/core, workbox-build), never shipped.
ws >=8.20.1 GHSA-3h5v-q93c-6h6q / CVE-2024-37890 DoS when handling a request with many HTTP headers (fixed 8.17.1).
brace-expansion >=5.0.6 GHSA-v6h2-p8h4-qcjw / CVE-2025-5889 ReDoS in expand() (fixed 1.1.12 / 2.0.2 / 3.0.1 / 4.0.1).
qs >=6.16.0 GHSA-4mjr-xmp4-gh2g / GHSA-x5fp-wj9c-mxmx (supersedes GHSA-hrpp-h998-j3pp / CVE-2022-24999) Array-limit bypass via bracket-key comma parsing, and a DoS via attacker-controlled isBuffer (resolved 6.15.2 vulnerable; fixed 6.16.0). Dev-only transitive (@lhci/cli, @stryker-mutator/core, http-server), never shipped. 6.16.0 was published 2026-08-29, inside this repo's 7-day minimumReleaseAge quarantine at the time of this fix — admitted via a version-scoped minimumReleaseAgeExclude: qs@6.16.0 entry (same mechanism/precedent as nanoid@3.3.18, PR #362), not a reduction of the 10080-minute quarantine itself. Compensating verification before admission: registry identity/integrity confirmed via npm view; no new install/preinstall/postinstall/prepare lifecycle scripts versus 6.15.2 (identical script set); the one dependency delta (es-define-property@^1.0.1 added, side-channel bumped to ^1.1.1) is maintained by the same author (ljharb) as qs itself and was already present elsewhere in the resolved tree. Remove the exclusion once 6.16.0 naturally ages past 2026-09-05T23:50:15Z UTC and a frozen install still resolves it — future qs releases remain governed by the normal quarantine.
chrome-launcher ^1.2.1 preventive pin — no direct advisory Lighthouse-CI dev transitive; conservative floor, dev-only.
ip-address >=10.1.1 preventive pin — no direct advisory Dev/test transitive hardening; no advisory matches this floor.
uuid >=11.1.1 preventive pin — no direct advisory Conservative version floor; no security advisory applies (the prior "collision" note was inaccurate).
joi (transitive via wait-on@storybook/test-runnerjest-process-manager) ^18.2.1 transitive hardening (dev-only) wait-on@7.2.0 originally pulled an older joi; lockfile pins joi@18.2.1 for both wait-on@7.2.0 and wait-on@9.0.10. Dev/test toolchain only, never shipped.
fflate >=0.8.3 GHSA-px8p-9vwx-vf98 / CVE-2026-45820 unzipSync() infinite loop on a crafted ZIP64 archive missing its required extra field (≤0.8.2; fixed at or above 0.8.3). Transitive via jspdf@4.2.1 (PDF export), whose own ^0.8.1 range already permits the patched version. Advisory elevated to GitHub-reviewed status 2026-09-03, discovered via a routine Security Audit CI failure the same day.

Dependency hygiene status (2026-06-13):

  • pnpm audit --audit-level=high → 0 vulnerabilities.
  • pnpm audit --audit-level=moderate → 0 vulnerabilities.
  • pnpm-workspace.yaml hardening active: strictDepBuilds: true, blockExoticSubdeps: true, minimumReleaseAge: 10080.
  • pnpm outdated (re-run 2026-06-13): only non-critical patch/minor drift — @ai-sdk/google|openai|react, ai, @storybook/* + storybook (10.4.2→10.4.4), @types/node, docx, dompurify, lint-staged, turbo, yjs, zustand, wrangler. No major versions. @duckdb/duckdb-wasm (1.32.0) and @typescript/native-preview are dev/pre-release tracks and intentionally pinned.

Plugin sandbox post-fix validation (2026-06-13): The v1.22 plugin-isolation hardening (workers/plugin.worker.ts, services/pluginRegistry.ts) is covered by adversarial tests — WebAssembly denial, Function/AsyncFunction/GeneratorFunction/AsyncGeneratorFunction constructor-escape blocks, guard restoration on success + error paths (tests/unit/workers/plugin.worker.test.ts), and storage-key validation + 2 MiB value cap (tests/unit/pluginRegistry.test.ts). Open follow-up FU-1 (Function.prototype.constructor restore asymmetry, low impact) tracked in docs/AUDIT-PERFECTION-PLAN-v1.23.md.