Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- **Primary deploy target:** Static SPA on GitHub Pages (`/WorldScript-Studio/` base path)
- **Secondary targets:** Vercel (root base) and Cloudflare Pages via edge builds (`pnpm run build:edge`)
- **Desktop:** Tauri 2 bundles for Linux (AppImage), macOS (DMG), and Windows (MSI); auto-updater enabled via `latest.json`
- **Version:** `1.27.0`
- **Version:** `1.27.1`
- **License:** MIT

The app supports a multi-provider AI stack (Gemini, OpenAI, Claude, Grok, OpenRouter, Ollama, WebLLM, ONNX Runtime Web, Transformers.js), four AI execution modes (Hybrid / Cloud / Local / Eco), real-time collaboration with E2E encryption, a Plot Board v2 with swimlane/canvas/timeline modes, character/world management, manuscript export, voice dictation, and a 19-locale i18n layer.
Expand Down
6 changes: 4 additions & 2 deletions AUDIT.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# 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)**
**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.27.0** — Phase 4 closes out the encryption lifecycle feature (issue #338: production disable/rotate wiring, now user-facing in Settings › Privacy), alongside three independently-shipped reliability/UX fixes for #332, #333, and #341. **2919 keys × 19 locales**. Historical release sections below remain historical.
**Current version:** **v1.27.1** — Desktop persistence/security stabilization (#363: atomic writes, fail-closed key routing, factory-reset consolidation, packaged-build capability fix, Rust/E2E/VRT promoted to required CI gates) plus the nanoid security-advisory patch (#362). **2919 keys × 19 locales**. Historical release sections below remain historical.

**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.1` → `main` 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.

Expand Down
63 changes: 63 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,69 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [1.27.1] — 2026-08-14

> Desktop persistence/security stabilization (#363) — atomic writes and fail-closed key routing
> across every Tauri filesystem-backed store, a factory-reset UI/logic consolidation, and three
> new required CI gates (Rust compile/lint/test, E2E, Visual Regression) that previously let
> native-code and UI-regression risk merge unchecked — plus a nanoid security-advisory patch.

### Fixed

- **Atomic writes across all Tauri filesystem-backed stores** (`services/fs/{assetFsStore,
codexFsStore,fsCore,projectFsStore,settingsFsStore,snapshotFsStore}.ts`) — writes are now
serialized, and an orphaned temp file left behind by a failed write is retried on cleanup (with a
warning logged, not silently dropped) instead of risking corruption of concurrently-written
project data.
- **Unified, fail-closed desktop API-key routing.** Desktop key storage now routes unconditionally
through the same encrypted IndexedDB path used on the web, with recovery-reset hardened to fail
closed rather than leaving a partially-wiped state; `docs/SECURITY-THREAT-MODEL.md` corrected to
describe the unconditional routing and the best-effort (not guaranteed) nature of legacy-key-file
cleanup.
- **Factory reset / danger-zone UI consolidated.** New `hooks/useFactoryReset.ts` +
`components/settings/FactoryResetDangerZone.tsx` replace three near-duplicate confirm/wipe/error/
busy implementations across `ApiKeySection`, `IdbUnlockModal`, and `EncryptionRecoveryModal`; also
fixes a bug where the stuck "recovery-required" branch had no error paragraph to render a failed
reset's error state.
- **Packaged-build factory reset was broken.** The Tauri fs capability glob for `fs:allow-exists`
and `fs:allow-read-dir` only covered `$APPDATA/**`, rejecting `exists()`/`readDir()` on the
`$APPDATA` root itself; the capability now grants the exact root path too
(`src-tauri/capabilities/default.json`).
- **Corrupt binder-asset detection fixed.** A binary/metadata byte-size mismatch is now treated as
corruption instead of silently pairing two independent atomic writes from different save
generations as if they were one transaction.
- **Legacy API-key file cleanup no longer provider-list-bound.** Scans the config directory by the
`*_key.enc.json` naming convention instead of a hardcoded 5-provider list, so a future or
unlisted provider's legacy key file is no longer left behind after migration.
- **Rust: `Builder::on_menu_event` gated `#[cfg(desktop)]`.** The call was unconditional despite
the crate itself restricting that method to desktop, which would have failed to compile for the
mobile targets `src-tauri/src/lib.rs` already anticipates (`#[cfg_attr(mobile, ...)]`).
- **Re-wired the dead Rust menu-action event bridge.** `registerTauriMenuHandler` is back as a
pre-paint-window fallback, self-unregistering once the JS-owned menu (`installDesktopMenu`) takes
over — the native fallback menu's items were previously silently inert, and the two menus can no
longer double-dispatch a single click.

### Security

- **nanoid security advisory patched** (PR #362) — `nanoid@3.3.17` → `3.3.18`, added to
`minimumReleaseAgeExclude` in `pnpm-workspace.yaml` so the fix isn't held back by the
10,080-minute (7-day) `minimumReleaseAge` policy.

### Changed

- **CI: Rust compile/lint/test, E2E, and Visual Regression Testing are now required merge gates**
(`rust-tauri`, `e2e`, `vrt` join the `ci-success` aggregator's `needs`). Previously native Rust
code and UI regressions could merge into `main` with no automated check beyond an advisory OSV
vulnerability scan.

### Tests

- Added coverage for the new `clearTauriAppData` branch and the factory-reset danger-zone UI
(`tests/unit/factoryResetService.test.ts`, `tests/unit/settings/{EncryptionRecoveryModal,
IdbUnlockModal}.test.tsx`, `tests/unit/services/fs/{fsCore,fsStores}.test.ts`,
`tests/unit/tauriServices.test.ts`, `tests/unit/ApiKeySection.test.tsx`) — `codecov/patch` had
dropped to 52.75% before this pass.

## [1.27.0] — 2026-08-13

### Added
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<img src="https://img.shields.io/badge/TypeScript-7.x_(tsgo)-3178C6?logo=typescript&logoColor=white" alt="TypeScript 7 (tsgo)">
<img src="https://img.shields.io/badge/AI-Gemini_%7C_OpenAI_%7C_OpenRouter_%7C_Ollama_%7C_WebLLM-4285F4?logo=google" alt="Gemini · OpenAI · OpenRouter · Ollama · WebLLM">
<img src="https://img.shields.io/badge/Local_AI-WebGPU_%7C_ONNX_%7C_Transformers.js-8B5CF6" alt="WebGPU · ONNX · Transformers.js">
<img src="https://img.shields.io/badge/Version-v1.27.0-6366F1" alt="v1.27.0">
<img src="https://img.shields.io/badge/Version-v1.27.1-6366F1" alt="v1.27.1">
<img src="https://img.shields.io/badge/Storage-IndexedDB_v8-F59E0B" alt="IndexedDB v8">
<img src="https://img.shields.io/badge/PWA-v3.0-5BB974?logo=pwa" alt="PWA v3.0">
<img src="https://img.shields.io/badge/i18n-19_locales-2919_keys-0EA5E9" alt="i18n 19 locales — 2919 keys">
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "worldscript-studio",
"private": true,
"version": "1.27.0",
"version": "1.27.1",
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
"description": "WorldScript Studio — offline-first, AI-powered creative writing application.",
"author": "QNBS",
"keywords": [
Expand Down
2 changes: 1 addition & 1 deletion public/sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// ============================================================

// QNBS-v3 (CodeRabbit): version bump auto-synced from package.json by scripts/sync-sw-version.mjs — every CACHE_STATIC/CACHE_DYNAMIC name changes too, so this alone invalidates all prior caches on next activate.
const APP_VERSION = '1.27.0';
const APP_VERSION = '1.27.1';
const CACHE_STATIC = `worldscript-static-v${APP_VERSION}`;
const CACHE_DYNAMIC = `worldscript-dynamic-v${APP_VERSION}`;
const CACHE_IMAGES = `worldscript-images-v${APP_VERSION}`;
Expand Down
43 changes: 25 additions & 18 deletions scripts/sync-tauri-version.mjs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#!/usr/bin/env node
/**
* Sync Tauri version from package.json → src-tauri/Cargo.toml + src-tauri/tauri.conf.json
* QNBS-v3: Prevents version drift between web (package.json) and desktop builds.
* Sync version from package.json → src-tauri/Cargo.toml, src-tauri/tauri.conf.json,
* src-tauri/Cargo.lock (the workspace package's own locked entry), and AGENTS.md.
* QNBS-v3: Prevents version drift between web (package.json) and desktop builds/docs — a stale
* Cargo.lock entry fails `cargo check --locked` in CI's rust-check gate, and a stale AGENTS.md
* `Version:` field is agent-facing documentation drift (found by review-loop bots on PR #364).
* Run via predev / prebuild hooks or manually:
* node scripts/sync-tauri-version.mjs
*/
Expand All @@ -15,6 +18,8 @@ const root = path.join(__dirname, '..');
const pkgPath = path.join(root, 'package.json');
const cargoPath = path.join(root, 'src-tauri', 'Cargo.toml');
const tauriConfPath = path.join(root, 'src-tauri', 'tauri.conf.json');
const cargoLockPath = path.join(root, 'src-tauri', 'Cargo.lock');
const agentsPath = path.join(root, 'AGENTS.md');

const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
const version = pkg.version;
Expand All @@ -25,25 +30,27 @@ if (!version || typeof version !== 'string') {

let changed = false;

// --- Cargo.toml ---
const cargo = fs.readFileSync(cargoPath, 'utf8');
const cargoRe = /^version = "[^"]+"/m;
const cargoNew = cargo.replace(cargoRe, `version = "${version}"`);
if (cargoNew !== cargo) {
fs.writeFileSync(cargoPath, cargoNew);
console.log(`[sync-tauri-version] ${cargoPath} → ${version}`);
changed = true;
function syncFile(filePath, pattern, replacement) {
const content = fs.readFileSync(filePath, 'utf8');
const next = content.replace(pattern, replacement);
if (next !== content) {
fs.writeFileSync(filePath, next);
console.log(`[sync-tauri-version] ${filePath} → ${version}`);
changed = true;
}
}

// --- Cargo.toml ---
syncFile(cargoPath, /^version = "[^"]+"/m, `version = "${version}"`);

// --- tauri.conf.json ---
const tauriConf = fs.readFileSync(tauriConfPath, 'utf8');
const tauriRe = /"version":\s*"[^"]+"/;
const tauriNew = tauriConf.replace(tauriRe, `"version": "${version}"`);
if (tauriNew !== tauriConf) {
fs.writeFileSync(tauriConfPath, tauriNew);
console.log(`[sync-tauri-version] ${tauriConfPath} → ${version}`);
changed = true;
}
syncFile(tauriConfPath, /"version":\s*"[^"]+"/, `"version": "${version}"`);

// --- Cargo.lock (the workspace package's own locked entry, not registry deps) ---
syncFile(cargoLockPath, /(name = "worldscript-studio"\nversion = )"[^"]+"/, `$1"${version}"`);

// --- AGENTS.md ---
syncFile(agentsPath, /(\*\*Version:\*\* `)[^`]+(`)/, `$1${version}$2`);

if (!changed) {
console.log('[sync-tauri-version] Already in sync.');
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "worldscript-studio"
version = "1.27.0"
version = "1.27.1"
description = "AI-powered creative writing and world-building application"
authors = ["QNBS"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"productName": "WorldScript Studio",
"version": "1.27.0",
"version": "1.27.1",
"identifier": "com.worldscript.studio",
"build": {
"frontendDist": "../dist",
Expand Down
Loading