diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 777912a8..d0d9a3ca 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,5 @@
# ============================================================
-# StoryCraft Studio – CI/CD Pipeline
+# WorldScript Studio – CI/CD Pipeline
# security → quality (lint, i18n, tsgo, vitest+coverage) → build (+chunk budget, rollup analyze)
# ├→ e2e | storybook (parallel) ; lighthouse after build
# deploy (main): needs build + e2e → GitHub Pages
@@ -130,9 +130,6 @@ jobs:
- name: Typecheck (tsgo)
run: npx tsgo --project tsconfig.tsgo.json --noEmit --checkers 4
- - name: Build Storybook
- run: pnpm run build-storybook
-
# QNBS-v3: Vitest mit --retry=2 für stabile Runs + NODE_OPTIONS für Node 24 Kompatibilität.
# --no-experimental-webstorage deaktiviert native Node 24+ Web Storage API (verhindert localStorage.clear() Fehler).
# Best Practice 2026: Retry-Logik fängt transiente Fehler ab, die bei CI-Runner-Überlastung auftreten können.
@@ -452,9 +449,19 @@ jobs:
run: pnpm exec playwright install --with-deps chromium
- name: Serve Storybook + Run test-runner (cloud-optimised)
- # QNBS-v3: || true — test failures produce artifacts and annotation but do NOT block the job;
- # flaky a11y stories should not cascade into a full pipeline block on every PR.
- # Remove || true once baseline is stable and no flaky a11y stories remain.
+ # QNBS-v3 (F-12, 2026-07-29): the command below was invoking three flags this
+ # @storybook/test-runner@^0.24.4 CLI does not support (`--max-workers` — wrong case, should
+ # be `--maxWorkers`; `--retries` and `--screenshot-on-failure` don't exist in this version
+ # at all). Every prior CI run failed on the FIRST unknown option before any story ever ran,
+ # printed --help text, and `|| true` silently turned that into a green step — this gate has
+ # produced zero real signal since it was added. Fixed to only pass supported flags.
+ # QNBS-v3 (CodeRabbit, 2026-07-29): `|| true` swallowed the exit code so this step showed
+ # green even when it truly failed — literally how the bug above stayed invisible. Moved the
+ # non-blocking behavior to step-level `continue-on-error: true`, which still shows the step
+ # as failed (visible signal for the F-13 "~10 real runs" re-evaluation) without failing the
+ # job. Re-evaluate after ~10 real (non-help-text) runs on main — if none fail on genuine
+ # a11y/story assertions, drop `continue-on-error` and make this blocking.
+ continue-on-error: true
env:
CI: true
DEBUG: pw:browser*
@@ -462,10 +469,8 @@ jobs:
npx http-server storybook-static -p 6006 -s &
npx wait-on http://localhost:6006 --timeout 90000
pnpm exec test-storybook --url http://localhost:6006 \
- --max-workers=2 \
- --retries=3 \
- --junit \
- --screenshot-on-failure || true
+ --maxWorkers=2 \
+ --junit
# QNBS-v3: always() — upload even on failure; artifacts are the primary debug tool on low-end
# local hardware where storybook cannot run. Use the Storybook Debug workflow for
diff --git a/.gitignore b/.gitignore
index 3dc7ac2e..c22f837c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,6 +77,11 @@ graphify-out/cost.json
public/locales/*/*.json
!public/locales/*/bundle.json
+# Self-hosted DuckDB-WASM runtime assets (F-09) — ~75 MB, copied from the pinned
+# @duckdb/duckdb-wasm npm dependency by scripts/copy-duckdb-assets.mjs (predev/prebuild).
+# Never commit: these are build artifacts of an existing dependency, not source.
+public/duckdb/
+
# Claude Code local config and Lighthouse CI results
.claude/
.lighthouseci/
diff --git a/AUDIT.md b/AUDIT.md
index bf879eac..166ce2bc 100644
--- a/AUDIT.md
+++ b/AUDIT.md
@@ -1,8 +1,8 @@
# 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)**
+**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; PR open/CI green, not yet merged)**
**Scope:** Full application, repository configuration, CI/CD, documentation, release validation
-**Current version:** **v1.24.1** — 2026-07-28 (Local-AI reliability — desktop Ollama/LM Studio/vLLM discovery + browser status-badge fixes; AI heuristic-fallback foundation + Outline/Character/World/Plot-Board generators; real self-hosted LanguageTool integration; 20-PR Dependabot backlog triage; Issue #60 vendor-fork audit closeout; `persist-credentials`/CWE-209 security hardening; **2844 keys × 19 locales**)
+**Current version:** **v1.24.2** — 2026-07-29, **PR #284 open, CI green, not yet merged/tagged/released** (tag + GitHub Release publish are maintainer actions per this sprint's own hard rules) — CSP/crypto/doc-truth hardening: `'wasm-unsafe-eval'` restored on all 5 deployment surfaces after a 2-month functional-truth gap; desktop API-key encryption moved from unsalted SHA-256 to PBKDF2 600k + salt; DuckDB-WASM self-hosted; canonical Vercel URL unified; CI Storybook dedup + a genuinely broken test-runner invocation fixed; coverage ratchet raised to CI-measured values; **2849 keys × 19 locales**. Last actually-released version remains **v1.24.1** (2026-07-28) until this PR merges.
**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.
@@ -12,6 +12,76 @@
**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`](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](docs/adr/0013-csp-wasm-and-blob-frames.md) |
+| F-02 | 🔴 P0 | Unhashed inline `` could slip past the
+ exact detector meant to catch the F-01/F-02 defect class. Fixed in two follow-up commits, both
+ with new regression tests.
+ - **Review-loop refinements** (CodeRabbit/CodeAnt, all verified before accepting): the F-10 URL
+ drift gate had excluded `locales/it/help.json` — the actual file where the real incident
+ happened — reasoning it was "generated" (it isn't; `public/locales/it/bundle.json` is the
+ generated one). Now scanned, with the URL regex's scheme made optional (the Italian source
+ references the bare hostname inside a `` tag, no `https://` prefix). The Storybook
+ `|| true` fix from WS-8 was itself swallowing the step's real exit code — moved to step-level
+ `continue-on-error: true`, which still surfaces genuine failure in the Actions UI without
+ blocking the job (the exact visibility gap that let F-12's flag-name bug hide for so long).
+ `copy-duckdb-assets.mjs`'s idempotency check gained a SHA-256 content hash alongside the
+ file-size check (same-size dependency bump could otherwise leave stale content).
+- **Unrelated, pre-existing E2E finding (documented, not fixed — correctly out of scope):**
+ `tests/e2e/a11y.spec.ts` — "writer version control panel has no serious axe violations" fails
+ deterministically (same 4.47-vs-4.5 `color-contrast` ratio on both `chromium` and `Mobile
+ Chrome`, both CI attempts) on `--sc-accent` (`#92400e`) over its own `/20`-opacity background
+ (`#e0cab0`). Confirmed unrelated to this PR: `git diff --name-only main...HEAD` touches no CSS,
+ Tailwind config, Writer/version-control component, or color token, and `package.json`'s only
+ diff is the WS-5 `copy-duckdb-assets.mjs` script wiring (no dependency bump). `e2e` is **not** in
+ the branch's required status checks (`gh api .../branches/main/protection` →
+ `required_status_checks.contexts` = Security Audit, Build, Quality Gate ×2 only), so this does
+ not block merge. Left untouched — fixing an unrelated design-token contrast issue is out of scope
+ for a CSP/crypto/doc-truth security sprint; flagged here for a maintainer to file separately.
+- **DoD evidence:**
+ ```bash
+ grep -q "Ausstiegskriterium\|exit criterion" docs/CI.md → OK
+ pnpm exec biome check vitest.config.ts → clean
+ CI quality job (both Node versions) → pass, with the new thresholds in effect for this exact commit
+ ```
+- **Deviations (🟡):** thresholds set to +5-per-metric cap rather than the literal "~1pt below
+ measured" (which would have been +6 on statements) — documented above and in the
+ `vitest.config.ts` comment itself.
+
+---
+
+### WS-7 — Version bump to 1.24.2
+- **Status:** ✅ done (version-bump commit only, per hard rule — tagging/publishing a release is
+ a maintainer action, STOP-AND-ASK, not performed)
+- **Finding IDs:** none directly — release housekeeping, last in Wave 2 per the execution order
+- **Fix:** `package.json` bumped to `1.24.2`; ran `node scripts/sync-tauri-version.mjs` and
+ `node scripts/sync-sw-version.mjs` rather than hand-editing (per `CLAUDE.md`'s documented
+ auto-sync convention) — these updated `src-tauri/tauri.conf.json`, `src-tauri/Cargo.toml`, and
+ `public/sw.js`'s `APP_VERSION` in place. `node scripts/sync-readme-metrics.mjs` reported
+ "already in sync" (it doesn't track the version badge specifically) — the `README.md` version
+ badge (`v1.24.1` → `v1.24.2`) updated by hand. New `CHANGELOG.md` `[1.24.2]` entry lists every
+ finding ID (F-01…F-14) with its resolution, organized by Security/Fixed/Documentation per
+ Keep-a-Changelog convention, matching this project's existing entry style.
+- **Scope note:** `ROADMAP.md`/`AUDIT.md`/`TODO.md`'s remaining `1.24.1` references are WS-11's
+ job (re-audit close-out), not this workstream — confirmed against the plan's own division of
+ labor before leaving them untouched here.
+- **DoD evidence:**
+ ```bash
+ node -p "require('./package.json').version" → 1.24.2
+ python3 -c "...tauri.conf.json...['version']" → 1.24.2
+ grep -q "v1.24.2" README.md CHANGELOG.md → both found
+ node scripts/check-doc-metrics.mjs → OK
+ pnpm exec biome check package.json → clean
+ pnpm exec vitest run tests/unit/swLocaleStrategy.test.ts → 5/5 passed (only test referencing APP_VERSION conceptually; no hardcoded version-string assertion, confirmed no breakage)
+ ```
+- **Deviations (🟡):** none.
+
+---
+
+### WS-11 — Re-audit + post-mortem close-out
+- **Status:** ✅ done
+- **Finding IDs:** all 14 (closing entry)
+- **Full re-audit, final state (all commits through the WS-7 version bump):**
+ ```bash
+ $ node scripts/check-i18n-keys.mjs && node scripts/build-i18n.mjs && node scripts/content-guard.mjs \
+ && node scripts/check-doc-metrics.mjs && node scripts/check-suppressions.mjs && node scripts/audit-tokens.mjs
+ [i18n] OK — 19 locales match 2849 keys (reference: en).
+ [build-i18n] Done. 54131 total keys across 19 bundles. (git status confirmed no diff — nothing hand-edited)
+ content-guard: OK (community templates)
+ [docs:check] OK — 7 files match actual state (19 locales, 2849 keys, latest v1.24.1)
+ [suppressions] OK — 52 (baseline 52)
+ [token-audit] PASS — 160 ≤ baseline 160
+
+ pnpm run lint → clean (1648 files)
+ pnpm run typecheck (fresh, *.tsbuildinfo removed first) → clean
+ pnpm run parity:check → 0 drifts, 22 flags, "Audit passed — all flags consistent"
+ pnpm run build && pnpm run bundle:budget → 154 JS chunks ≤ 6200 KB
+ node scripts/smoke-prod-build.mjs → PASS, 0 CSP violations, wasm: ok
+ ```
+ **`latest v1.24.1` above is correct-by-design, not stale** (CodeRabbit flagged this — verified,
+ not a bug): `check-doc-metrics.mjs#getLatestReleasedVersion()` reads `git tag`, not
+ `package.json`. `package.json`/`tauri.conf.json` are pre-bumped to `1.24.2` (WS-7) as release
+ prep, but no `v1.24.2` git tag exists yet — tagging is a maintainer action, deliberately not
+ performed by this sprint (see WS-7's own entry and every "not yet merged/tagged" note in
+ `AUDIT.md`/`TODO.md`/`ROADMAP.md`). The gate will correctly report `v1.24.2` once that tag is
+ created; reporting it now, before the tag exists, would itself be the doc-truth violation this
+ entire sprint is about.
+ `pnpm run test:run` (full suite, no `--coverage`) was not re-attempted a third time locally —
+ two independent attempts earlier in this sprint were both killed with zero output on this
+ machine's ~3.7 GB RAM. CI's `quality` job's `vitest run --coverage` (a strict superset — a
+ coverage report cannot generate from a suite with failing tests) already passed on this exact
+ commit history through the WS-9 push, which is stronger evidence of full-suite health than a
+ third local attempt would add.
+- **Docs:** `AUDIT.md` gained the "Audit 2026-07-29 — CSP / Crypto / Truth" section — full finding
+ table (all 14 IDs, severity, resolution) plus the post-mortem answering the sprint's central
+ question directly (would a `faad8f0`-shaped commit fail today? yes, in 3 independent places —
+ walked through each). The post-mortem also names the two *other* instances of the identical
+ "gate looks green but checks the wrong property" pattern that surfaced **during this same
+ sprint's own execution** (Storybook's `|| true`, F-12; the F-10 drift gate's own initial
+ exclusion of the one file with real incident history) — three occurrences in one sprint is
+ itself the finding, documented as such rather than treated as three unrelated bugs.
+ `TODO.md` gained a `v1.24.2` block (code-complete items + the two genuinely-open follow-ups:
+ worker-generation consolidation, F-14; the pre-existing unrelated a11y color-contrast finding).
+ `ROADMAP.md` synced with a matching `v1.24.2` section.
+- **Doc-truth discipline applied to these edits themselves:** none of the three docs claim this
+ work is "released" or "merged" — `AUDIT.md`'s version line, `TODO.md`'s status line, and
+ `ROADMAP.md`'s section header all explicitly state "PR #284 open, CI green, not yet
+ merged/tagged," matching this sprint's own theme (don't let a doc claim more than what's
+ actually true). Caught and corrected in my own first draft before committing.
+- **DoD evidence:**
+ ```bash
+ grep -q "2026-07-29" AUDIT.md → found
+ node scripts/check-doc-metrics.mjs → OK (re-run after AUDIT/TODO/ROADMAP edits)
+ git status --short → only AUDIT.md, TODO.md, ROADMAP.md changed
+ ```
+- **Deviations (🟡):** `pnpm run test:run` full-suite re-run substituted with CI's already-passing
+ `--coverage` run as equivalent-or-stronger evidence — documented above with the reasoning.
diff --git a/index.html b/index.html
index d7805e26..e778b547 100644
--- a/index.html
+++ b/index.html
@@ -47,7 +47,13 @@
-
+