From 11ecff314760f0780508794c6be04a2f90aaec41 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Thu, 20 Aug 2026 04:20:42 +0200 Subject: [PATCH 1/6] chore(cef): retire obsolete desktop CEF target Removes apps/desktop-cef/ (the thin C++ host + Rust FFI-boundary scaffold, ADR-0020's Option B spike made real), scripts/cef/ (SDK fetch/build/proof tooling), and the advisory-only cef-learning-harness.yml CI workflow (never part of the required ci-success aggregator - zero impact on required gates). Git history is the archive - see docs/historical/cef/README.md. Per ADR-0021. Co-Authored-By: Claude Sonnet 5 --- .github/workflows/cef-learning-harness.yml | 251 ----------- apps/desktop-cef/CMakeLists.txt | 73 ---- apps/desktop-cef/resources/crash_reporter.cfg | 15 - apps/desktop-cef/rust-core/Cargo.toml | 15 - apps/desktop-cef/rust-core/src/lib.rs | 22 - apps/desktop-cef/src/main.cpp | 81 ---- apps/desktop-cef/src/shutdown_signal.cpp | 24 -- apps/desktop-cef/src/shutdown_signal.h | 11 - apps/desktop-cef/src/worldscript_app.cpp | 70 --- apps/desktop-cef/src/worldscript_app.h | 24 -- apps/desktop-cef/src/worldscript_handler.cpp | 116 ----- apps/desktop-cef/src/worldscript_handler.h | 42 -- scripts/cef/cef-version.json | 10 - scripts/cef/cefPaths.mjs | 23 - scripts/cef/check-linux-runtime-deps.mjs | 81 ---- scripts/cef/check-linux-runtime-linkage.mjs | 94 ----- scripts/cef/check-linux-sandbox-inventory.mjs | 82 ---- scripts/cef/fetch-cef-sdk.mjs | 114 ----- scripts/cef/prepare-cef-build.mjs | 60 --- scripts/cef/print-cef-version-diagnostics.mjs | 79 ---- scripts/cef/run-launch-cycle-proof.mjs | 397 ------------------ scripts/cef/run-symbolization-proof.mjs | 272 ------------ scripts/cef/run-wayland-smoke.mjs | 202 --------- 23 files changed, 2158 deletions(-) delete mode 100644 .github/workflows/cef-learning-harness.yml delete mode 100644 apps/desktop-cef/CMakeLists.txt delete mode 100644 apps/desktop-cef/resources/crash_reporter.cfg delete mode 100644 apps/desktop-cef/rust-core/Cargo.toml delete mode 100644 apps/desktop-cef/rust-core/src/lib.rs delete mode 100644 apps/desktop-cef/src/main.cpp delete mode 100644 apps/desktop-cef/src/shutdown_signal.cpp delete mode 100644 apps/desktop-cef/src/shutdown_signal.h delete mode 100644 apps/desktop-cef/src/worldscript_app.cpp delete mode 100644 apps/desktop-cef/src/worldscript_app.h delete mode 100644 apps/desktop-cef/src/worldscript_handler.cpp delete mode 100644 apps/desktop-cef/src/worldscript_handler.h delete mode 100644 scripts/cef/cef-version.json delete mode 100644 scripts/cef/cefPaths.mjs delete mode 100644 scripts/cef/check-linux-runtime-deps.mjs delete mode 100644 scripts/cef/check-linux-runtime-linkage.mjs delete mode 100644 scripts/cef/check-linux-sandbox-inventory.mjs delete mode 100644 scripts/cef/fetch-cef-sdk.mjs delete mode 100644 scripts/cef/prepare-cef-build.mjs delete mode 100644 scripts/cef/print-cef-version-diagnostics.mjs delete mode 100644 scripts/cef/run-launch-cycle-proof.mjs delete mode 100644 scripts/cef/run-symbolization-proof.mjs delete mode 100644 scripts/cef/run-wayland-smoke.mjs diff --git a/.github/workflows/cef-learning-harness.yml b/.github/workflows/cef-learning-harness.yml deleted file mode 100644 index da602759..00000000 --- a/.github/workflows/cef-learning-harness.yml +++ /dev/null @@ -1,251 +0,0 @@ -# ============================================================ -# WorldScript Studio – CEF Learning Harness (Wave 2 bootstrap) -# -# docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md §3142 (Wave 2 deliverables), ADR-0020 -# (CEF binding choice — the Wave 2 spike this harness formalizes into a real, -# repo-committed, CI-run check per §61.1.4's evidence-link discipline). -# -# Two increments live here: -# 1. Fetch + verify the pinned CEF SDK, report a clean-machine Linux runtime- -# dependency inventory (before any apt-get), and print real version diagnostics. -# 2. Build apps/desktop-cef's worldscript_host against that SDK, serve the real -# production bundle (pnpm run build's dist/), and run repeated start/close -# cycles against it under Xvfb — the roadmap's literal "isolated learning -# harness" / "safe repeated startup/shutdown" / "existing dist/" deliverables, -# plus the FFI boundary proven from inside the real host, not just in isolation. -# -# Advisory only: not part of the required `ci-success` aggregator. Wave 2 is still -# in progress; this harness informs it rather than gating unrelated PRs on it. -# -# Trigger scope is deliberately asymmetric: `pull_request` stays path-scoped to -# CEF-specific files, so this ~30-minute, ~300MB-download job doesn't tax every -# unrelated content/feature PR. `push` to main has NO path filter — it runs the -# real production bundle through worldscript_host on every merge, so a -# CEF-rendering regression introduced by an unrelated app-source PR is still -# caught promptly (informationally, non-blocking), not silently missed until -# someone next happens to touch a CEF-specific path. -# -# QNBS-v3: docs/cef/** deliberately excluded from the PR path list — a docs-only -# change (e.g. updating the competency matrix) can't regress the built host, so it -# doesn't need a ~30min rebuild; code changes under apps/desktop-cef/** or -# scripts/cef/** still trigger this regardless of whether docs are also touched. -# The push-to-main trigger stays deliberately unscoped (no `paths:`) precisely so an -# unrelated app-source change that alters dist/'s real inputs is still caught after -# merge — narrowing that trigger to apps/desktop-cef/** only would reintroduce the -# PR #388 gap this design already closed. -# -# QNBS-v3: `fast-gate` (lint+typecheck) runs before `harness` so a trivial code-style -# or type error fails in ~2-3min instead of after the full ~30min SDK+CMake+Rust+Xvfb -# cycle. `Swatinem/rust-cache` caches cargo's registry/git dirs for the rust-core -# crate Corrosion builds via CMake, cutting redundant crates.io re-fetches. -# -# QNBS-v3: CMAKE_BUILD_TYPE stays Release (RelWithDebInfo broke CEF's own chrome-sandbox -# copy step — the fetched SDK's prebuilt binaries only ship Release/ and Debug/ -# subdirectories, so CEF's build macros looked for a nonexistent RelWithDebInfo/ source -# path; a real CI failure, not a guess). worldscript_host gets its own -g flag directly -# (apps/desktop-cef/CMakeLists.txt) instead, for the crash-symbolization proof -# (scripts/cef/run-symbolization-proof.mjs)'s dump_syms/minidump-stackwalk to resolve -# function names against. Both tools are standalone Rust projects with tiny prebuilt -# Linux binaries — neither needs a Chromium checkout, confirmed by reading their -# own READMEs directly. -# ============================================================ - -name: 🧪 CEF Learning Harness - -on: - pull_request: - branches: [main] - paths: - - 'apps/desktop-cef/**' - - 'scripts/cef/**' - - '.github/workflows/cef-learning-harness.yml' - push: - branches: [main] - workflow_dispatch: - -permissions: - contents: read - -concurrency: - group: cef-learning-harness-${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }} - cancel-in-progress: ${{ github.event_name == 'pull_request' }} - -jobs: - # QNBS-v3: fast, cheap checks (lint/typecheck) gate the ~30min CEF build so a trivial - # code-style or type error doesn't burn the full SDK-fetch+CMake+Rust+Xvfb cycle before - # surfacing. Duplicates a subset of ci.yml's quality job (which runs in parallel anyway) — - # deliberate trade: a couple extra lint/typecheck minutes vs. up to 30 wasted on a fail-fast case. - fast-gate: - name: ⚡ Fast gate (lint + typecheck) - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - uses: ./.github/actions/setup - - name: Lint (Biome) - run: pnpm run lint - - name: Typecheck (tsgo) - run: npx tsgo --project tsconfig.tsgo.json --noEmit --checkers 4 - - harness: - name: 🧪 CEF host build, dependency inventory, launch-cycle proof - runs-on: ubuntu-latest - timeout-minutes: 30 - needs: [fast-gate] - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - persist-credentials: false - - - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version-file: '.nvmrc' - - # Runs on the stock runner before any apt-get — the "clean machine" data point - # docs/cef/knowledge/linux-runtime-notes.md flags as still open. - - name: Clean-machine Linux dependency inventory - run: node scripts/cef/check-linux-runtime-deps.mjs - - # QNBS-v3: diagnostic-only, no behavior change — real evidence on whether this runner can - # even support Chromium's Linux sandbox (Sandbox posture row, native-readiness.md, "Not yet - # attempted") before any attempt to actually enable it. Non-fatal by design. - - name: Linux sandbox feasibility inventory (diagnostic only) - continue-on-error: true - run: | - set -o pipefail - node scripts/cef/check-linux-sandbox-inventory.mjs | tee "$RUNNER_TEMP/cef-sandbox-inventory.txt" - - - name: Restore CEF SDK cache - id: cef-cache - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: .cef-cache - key: cef-sdk-${{ hashFiles('scripts/cef/cef-version.json') }} - - - name: Fetch + verify CEF SDK - run: | - set -o pipefail - CEF_DIR=$(node scripts/cef/fetch-cef-sdk.mjs --cache-dir .cef-cache | tail -1) - echo "CEF_DIR=$CEF_DIR" >> "$GITHUB_ENV" - - - name: Print CEF version diagnostics - run: node scripts/cef/print-cef-version-diagnostics.mjs "$CEF_DIR" - - - uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable - with: - toolchain: stable - - # QNBS-v3: rust-core (apps/desktop-cef/rust-core) is built via CMake's corrosion_import_crate, - # invoking cargo under the hood — this caches the global registry/git dirs cargo uses regardless - # of where Corrosion places its target/ output, so repeat runs skip re-fetching crates.io deps. - - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 - with: - workspaces: apps/desktop-cef/rust-core -> target - - # QNBS-v3: /var/cache/apt/archives is root-owned by default — actions/cache's restore - # (tar extraction) runs as the unprivileged runner user and fails with "Permission denied" - # on every file without this, silently degrading to a cache miss every time (real CI - # evidence: "Cache hit" followed immediately by "Cache not found" from the failed tar). - - name: Make apt archive cache dir writable by the runner user - run: sudo mkdir -p /var/cache/apt/archives/partial && sudo chown -R runner:runner /var/cache/apt/archives && sudo chmod -R 755 /var/cache/apt/archives - - - name: Cache apt packages (CEF host + Wayland build deps) - uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 - with: - path: /var/cache/apt/archives/*.deb - # QNBS-v3: bump the -v suffix if either apt-get install package list in this job changes - key: apt-cef-harness-deps-v1 - - - name: Install worldscript_host build dependencies - run: | - sudo apt-get update - sudo apt-get install -y cmake build-essential libx11-dev xvfb - - - uses: ./.github/actions/setup - - - name: Build WorldScript Studio production bundle - run: pnpm run build - env: - NODE_ENV: production - - - name: Configure + build worldscript_host - run: | - set -o pipefail - BUILD_SRC_DIR=$(node scripts/cef/prepare-cef-build.mjs "$CEF_DIR" build | tail -1) - cmake -S "$BUILD_SRC_DIR" -B build -DCMAKE_BUILD_TYPE=Release - cmake --build build --target worldscript_host --parallel "$(nproc)" - - - name: List worldscript_host output directory (diagnostic) - run: ls -la build/worldscript_host/ - - - name: Linux runtime linkage check (ldd against shipped .so files) - run: node scripts/cef/check-linux-runtime-linkage.mjs build/worldscript_host - - # QNBS-v3: pinned prebuilt release binaries (~3.6MB each), sha256-verified — not built from - # source (would need a slow cargo build for two more Rust tools) and not a Chromium - # checkout (neither tool needs one; see the header comment). - - name: Fetch dump_syms + minidump-stackwalk (crash-symbolization tools) - run: | - set -euo pipefail - mkdir -p symtools - curl -sSL --fail --retry 3 --retry-all-errors "https://github.com/mozilla/dump_syms/releases/download/v2.3.9/dump_syms-x86_64-unknown-linux-gnu.tar.xz" -o symtools/dump_syms.tar.xz - curl -sSL --fail --retry 3 --retry-all-errors "https://github.com/rust-minidump/rust-minidump/releases/download/v0.27.0/minidump-stackwalk-x86_64-unknown-linux-gnu.tar.xz" -o symtools/mds.tar.xz - echo "0fc852a86b00337407d9d423cc388a24c3b489ccaaedcf92623cad57af5ca8ad symtools/dump_syms.tar.xz" | sha256sum -c - - echo "0020324c54cc359596e927ee907204f4c8d4da6718765536edcabaa1622122ad symtools/mds.tar.xz" | sha256sum -c - - tar xf symtools/dump_syms.tar.xz -C symtools - tar xf symtools/mds.tar.xz -C symtools - DUMP_SYMS_BIN=$(find symtools -name dump_syms -type f -print -quit) - MINIDUMP_STACKWALK_BIN=$(find symtools -name minidump-stackwalk -type f -print -quit) - test -x "$DUMP_SYMS_BIN" || { echo "dump_syms binary not found in archive" >&2; exit 1; } - test -x "$MINIDUMP_STACKWALK_BIN" || { echo "minidump-stackwalk binary not found in archive" >&2; exit 1; } - echo "DUMP_SYMS_BIN=$(pwd)/$DUMP_SYMS_BIN" >> "$GITHUB_ENV" - echo "MINIDUMP_STACKWALK_BIN=$(pwd)/$MINIDUMP_STACKWALK_BIN" >> "$GITHUB_ENV" - - - name: Serve production bundle + repeated launch/close proof - run: | - python3 -m http.server 8080 --directory dist & - SERVER_PID=$! - sleep 1 - xvfb-run -a node scripts/cef/run-launch-cycle-proof.mjs \ - "$(pwd)/build/worldscript_host/worldscript_host" \ - "http://localhost:8080/" --cycles 3 - kill "$SERVER_PID" - - - name: Crash-symbolization proof (dump_syms + minidump-stackwalk) - run: | - xvfb-run -a node scripts/cef/run-symbolization-proof.mjs \ - "$(pwd)/build/worldscript_host/worldscript_host" \ - "$DUMP_SYMS_BIN" \ - "$MINIDUMP_STACKWALK_BIN" - - - name: Best-effort Wayland launch smoke (roadmap §44.2) - id: wayland-smoke - continue-on-error: true - run: | - sudo apt-get install -y weston - python3 -m http.server 8081 --directory dist & - SERVER_PID=$! - sleep 1 - node scripts/cef/run-wayland-smoke.mjs \ - "$(pwd)/build/worldscript_host/worldscript_host" \ - "http://localhost:8081/" - kill "$SERVER_PID" - - - name: Summary - if: always() - run: | - echo "## 🧪 CEF Learning Harness" >> "$GITHUB_STEP_SUMMARY" - echo "" >> "$GITHUB_STEP_SUMMARY" - echo "- Pinned SDK: \`$(node -e "console.log(require('./scripts/cef/cef-version.json').cefVersion)")\`" >> "$GITHUB_STEP_SUMMARY" - echo "- Cache hit: \`${{ steps.cef-cache.outputs.cache-hit }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- worldscript_host built and repeated launch/close cycles proven against the real production bundle (dist/), under Xvfb." >> "$GITHUB_STEP_SUMMARY" - echo "- Linux runtime linkage (ldd against the shipped worldscript_host + libcef.so, not just dpkg package presence): see the \"Linux runtime linkage check\" step above." >> "$GITHUB_STEP_SUMMARY" - echo "- Crash-symbolization proof: a self-induced browser-process crash resolved via dump_syms + minidump-stackwalk against our own DWARF debug info — Chromium/CEF-internal frames remain unsymbolized (no debug-symbols archive is published for this distribution)." >> "$GITHUB_STEP_SUMMARY" - echo "- Linux sandbox feasibility inventory (diagnostic only, no sandbox behavior attempted yet):" >> "$GITHUB_STEP_SUMMARY" - echo '```' >> "$GITHUB_STEP_SUMMARY" - cat "$RUNNER_TEMP/cef-sandbox-inventory.txt" >> "$GITHUB_STEP_SUMMARY" 2>/dev/null || echo "(inventory output unavailable)" >> "$GITHUB_STEP_SUMMARY" - echo '```' >> "$GITHUB_STEP_SUMMARY" - echo "- Wayland smoke (best-effort, roadmap §44.2): \`${{ steps.wayland-smoke.outcome }}\`" >> "$GITHUB_STEP_SUMMARY" - echo "- Not yet in scope: X11/Wayland matrix beyond this one runner, sandbox posture, accessibility-tree observability (AT-SPI — state enablement is proven, see the launch-cycle-proof step)." >> "$GITHUB_STEP_SUMMARY" diff --git a/apps/desktop-cef/CMakeLists.txt b/apps/desktop-cef/CMakeLists.txt deleted file mode 100644 index 78897c64..00000000 --- a/apps/desktop-cef/CMakeLists.txt +++ /dev/null @@ -1,73 +0,0 @@ -# QNBS-v3: subdirectory CMakeLists.txt reusing CEF's own proven CMake macros rather than reinventing its platform bootstrap (ADR-0020, Option B). -# -# This file is a SUBDIRECTORY CMakeLists.txt, added via add_subdirectory() from -# a *copy* of CEF's own root CMakeLists.txt at build time (see -# .github/workflows/cef-learning-harness.yml and scripts/cef/prepare-cef-build.mjs) -# — never invoked directly with `cmake -S apps/desktop-cef`. This mirrors the -# ADR-0020 spike's proven mechanism exactly, rather than reinventing CEF's own -# platform bootstrap (compiler/linker flags, OS_LINUX detection, PROJECT_ARCH) -# from scratch: it relies on CEF_STANDARD_LIBS, CEF_LIB_DEBUG/CEF_LIB_RELEASE, -# CEF_BINARY_FILES, CEF_RESOURCE_FILES, CEF_BINARY_DIR, CEF_RESOURCE_DIR, and the -# ADD_LOGICAL_TARGET/COPY_FILES/SET_EXECUTABLE_TARGET_PROPERTIES/FIND_LINUX_LIBRARIES -# macros already being defined by that parent scope by the time this runs — exactly -# what CEF's own bundled sample targets (cefsimple, cefclient) rely on too. - -# QNBS-v3: fetched via CMake FetchContent (small, MIT-licensed CMake module, not a binary) rather than vendored — replaces the ADR-0020 spike's hardcoded .a path with real Cargo<->CMake integration. -include(FetchContent) -FetchContent_Declare( - Corrosion - GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git - GIT_TAG v0.5.1 -) -FetchContent_MakeAvailable(Corrosion) - -corrosion_import_crate(MANIFEST_PATH "${CMAKE_CURRENT_SOURCE_DIR}/rust-core/Cargo.toml") - -set(WORLDSCRIPT_HOST_SRCS - src/main.cpp - src/shutdown_signal.h - src/shutdown_signal.cpp - src/worldscript_app.h - src/worldscript_app.cpp - src/worldscript_handler.h - src/worldscript_handler.cpp -) - -add_executable(worldscript_host ${WORLDSCRIPT_HOST_SRCS}) -add_dependencies(worldscript_host libcef_dll_wrapper) - -# QNBS-v3: -g directly on this target only, not CMAKE_BUILD_TYPE=RelWithDebInfo — that broke -# CEF's own chrome-sandbox copy step (the fetched SDK's prebuilt binaries only ship Release/ and -# Debug/ subdirectories, not RelWithDebInfo/; a real CI failure, not a guess). This keeps CEF's -# own macros on the Release path they expect while still giving our own crash-symbolization proof -# (scripts/cef/run-symbolization-proof.mjs) real DWARF debug info to resolve function names from. -target_compile_options(worldscript_host PRIVATE -g) - -ADD_LOGICAL_TARGET("libcef_lib" "${CEF_LIB_DEBUG}" "${CEF_LIB_RELEASE}") - -target_link_libraries(worldscript_host - libcef_lib - libcef_dll_wrapper - worldscript_rust_core - ${CEF_STANDARD_LIBS} -) - -set_target_properties(worldscript_host PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" -) - -SET_EXECUTABLE_TARGET_PROPERTIES(worldscript_host) - -COPY_FILES("worldscript_host" "${CEF_BINARY_FILES}" "${CEF_BINARY_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") -COPY_FILES("worldscript_host" "${CEF_RESOURCE_FILES}" "${CEF_RESOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}") - -# QNBS-v3: our own file, not a CEF SDK one, so COPY_FILES (which only knows CEF_BINARY_DIR/CEF_RESOURCE_DIR) doesn't apply — CEF requires crash_reporter.cfg next to the executable on Linux (include/cef_crash_util.h). -configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/resources/crash_reporter.cfg" - "${CMAKE_CURRENT_BINARY_DIR}/crash_reporter.cfg" - COPYONLY -) - -if(OS_LINUX) - FIND_LINUX_LIBRARIES("x11") -endif() diff --git a/apps/desktop-cef/resources/crash_reporter.cfg b/apps/desktop-cef/resources/crash_reporter.cfg deleted file mode 100644 index 18772b98..00000000 --- a/apps/desktop-cef/resources/crash_reporter.cfg +++ /dev/null @@ -1,15 +0,0 @@ -# CEF crash-reporting config (docs/cef/knowledge/cef-architecture-primer.md, -# roadmap Appendix A.1 "Accessibility/crash smoke" items). Format and every -# key here come from include/cef_crash_util.h in the pinned CEF branch -# (https://github.com/chromiumembedded/cef/blob/7922/include/cef_crash_util.h) -# — verified against source, not assumed. Copied next to worldscript_host by -# CMakeLists.txt; CEF requires this exact filename and location on Linux. -# -# No ServerURL is set: reports stay local-only, nothing is ever uploaded — -# deliberate for a CI/dev proof, safe by default for anyone building this host. -# RateLimitEnabled / MaxUploadsPerDay / MaxDatabaseSizeInMb / MaxDatabaseAgeInDays -# are intentionally omitted — the same doc states they are not supported on Linux. - -[Config] -ProductName=WorldScriptStudioDesktopHost -ProductVersion=0.0.0-cef-wave2-smoke diff --git a/apps/desktop-cef/rust-core/Cargo.toml b/apps/desktop-cef/rust-core/Cargo.toml deleted file mode 100644 index 1ff4da75..00000000 --- a/apps/desktop-cef/rust-core/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "worldscript_rust_core" -version = "0.1.0" -edition = "2021" -publish = false - -[lib] -crate-type = ["staticlib"] - -[profile.release] -panic = "abort" -# QNBS-v3: cargo's release profile strips debug info by default — without this, dump_syms would -# have nothing to extract from the Rust side of worldscript_host, and the Wave 2 crash-symbolization -# proof couldn't resolve worldscript_rust_debug_crash_self_test's function name. No runtime cost. -debug = true diff --git a/apps/desktop-cef/rust-core/src/lib.rs b/apps/desktop-cef/rust-core/src/lib.rs deleted file mode 100644 index b9842fa9..00000000 --- a/apps/desktop-cef/rust-core/src/lib.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! Minimal FFI boundary proof for the Wave 2 CEF host (ADR-0020, Option B). -//! -//! Rule from the roadmap (§10): C++ owns CEF integration only; Rust owns WorldScript's -//! actual logic. This crate is not yet real business logic — it proves the boundary a -//! future migration will build on, called from a real CEF callback (see -//! `apps/desktop-cef/src/worldscript_handler.cpp`), not just a decoupled test binary. - -#[no_mangle] -pub extern "C" fn worldscript_rust_ping() -> i32 { - // QNBS-v3: non-trivial sentinel so a stub/miscompiled stand-in can't accidentally match by coincidence (e.g. a default-zeroed return reading as success). - 424242 -} - -/// Deliberate, distinctively-named crash for the CI crash-symbolization proof -/// (docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md Wave 2 exit criterion). Only ever called -/// behind the `--debug-crash-self` CLI flag (see apps/desktop-cef/src/main.cpp) — never -/// reachable in normal operation. `panic = "abort"` (this crate's release profile) turns -/// this into a real SIGABRT Crashpad can catch, not an unwind. -#[no_mangle] -pub extern "C" fn worldscript_rust_debug_crash_self_test() { - panic!("worldscript_rust_debug_crash_self_test: deliberate self-test crash"); -} diff --git a/apps/desktop-cef/src/main.cpp b/apps/desktop-cef/src/main.cpp deleted file mode 100644 index 93c5796b..00000000 --- a/apps/desktop-cef/src/main.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include -#include - -#include "include/cef_app.h" -#include "include/cef_crash_util.h" - -#include "shutdown_signal.h" -#include "worldscript_app.h" - -// QNBS-v3: crash-symbolization proof (Wave 2 exit criterion) — only ever invoked behind -// --debug-crash-self below, never reachable in normal operation. -extern "C" void worldscript_rust_debug_crash_self_test(); - -namespace { - -// QNBS-v3: defaults to about:blank so a bare invocation (e.g. a subprocess re-exec) never depends on --url being present. -std::string ParseStartUrl(int argc, char* argv[]) { - const std::string prefix = "--url="; - for (int i = 1; i < argc; ++i) { - const std::string arg = argv[i]; - if (arg.rfind(prefix, 0) == 0) { - return arg.substr(prefix.size()); - } - } - return "about:blank"; -} - -bool HasDebugCrashSelfFlag(int argc, char* argv[]) { - for (int i = 1; i < argc; ++i) { - if (std::string(argv[i]) == "--debug-crash-self") { - return true; - } - } - return false; -} - -} // namespace - -int main(int argc, char* argv[]) { - CefMainArgs main_args(argc, argv); - - const bool debug_crash_self = HasDebugCrashSelfFlag(argc, argv); - CefRefPtr app(new WorldScriptApp(ParseStartUrl(argc, argv))); - - // QNBS-v3: every CEF host re-executes itself for renderer/GPU/utility subprocesses — must run before CefInitialize; non-negative return means this invocation *was* one of those, already run to completion. - int exit_code = CefExecuteProcess(main_args, app.get(), nullptr); - if (exit_code >= 0) { - return exit_code; - } - - // QNBS-v3: installed only in the real browser process (past the subprocess check above) — CodeRabbit review finding on PR #388: a raw, unhandled SIGTERM bypassed OnBeforeClose/CefQuitMessageLoop/CefShutdown entirely. - InstallShutdownSignalHandlers(); - - CefSettings settings; - settings.no_sandbox = true; // ADR-0020: sandbox posture deliberately deferred (roadmap §12). - - // QNBS-v3: CefInitialize's return value was previously ignored, masking init failure (missing display/resources) as a normal exit — CodeAnt review finding on PR #388. - if (!CefInitialize(main_args, settings, app.get(), nullptr)) { - fprintf(stderr, "[worldscript_host] CefInitialize failed\n"); - return 1; - } - - // QNBS-v3: CefCrashReportingEnabled() reflects whether crash_reporter.cfg (copied next to this binary by CMakeLists.txt) was found and parsed — the CI harness asserts on this line, not just on the .cfg file existing on disk. - printf("[worldscript_host] crash_reporting_enabled = %s\n", - CefCrashReportingEnabled() ? "true" : "false"); - fflush(stdout); - - // QNBS-v3: Wave 2 crash-symbolization proof only — deliberately crashes the browser - // process itself (not a renderer subprocess) so the resulting dump's stack is our own - // code, not Chromium/CEF internals we have no debug symbols for. - if (debug_crash_self) { - printf("[worldscript_host] debug_crash_self_test = triggering\n"); - fflush(stdout); - worldscript_rust_debug_crash_self_test(); - } - - CefRunMessageLoop(); - CefShutdown(); - - return 0; -} diff --git a/apps/desktop-cef/src/shutdown_signal.cpp b/apps/desktop-cef/src/shutdown_signal.cpp deleted file mode 100644 index 80726562..00000000 --- a/apps/desktop-cef/src/shutdown_signal.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "shutdown_signal.h" - -// QNBS-v3: sigaction/sigemptyset are POSIX, not standard C++ — alone doesn't guarantee them; this file is Linux-only per the app's own OS_LINUX scoping. -#include - -volatile std::sig_atomic_t g_worldscript_shutdown_requested = 0; - -namespace { - -void HandleShutdownSignal(int /*signum*/) { - g_worldscript_shutdown_requested = 1; -} - -} // namespace - -void InstallShutdownSignalHandlers() { - struct sigaction action = {}; - action.sa_handler = HandleShutdownSignal; - sigemptyset(&action.sa_mask); - action.sa_flags = 0; - // QNBS-v3: SIGINT alongside SIGTERM — a dev running the host interactively expects Ctrl+C to trigger the same graceful path, not the OS default abrupt kill. - sigaction(SIGTERM, &action, nullptr); - sigaction(SIGINT, &action, nullptr); -} diff --git a/apps/desktop-cef/src/shutdown_signal.h b/apps/desktop-cef/src/shutdown_signal.h deleted file mode 100644 index d214a7b0..00000000 --- a/apps/desktop-cef/src/shutdown_signal.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef WORLDSCRIPT_DESKTOP_CEF_SHUTDOWN_SIGNAL_H_ -#define WORLDSCRIPT_DESKTOP_CEF_SHUTDOWN_SIGNAL_H_ - -#include - -// QNBS-v3: CEF APIs are not async-signal-safe (CodeRabbit review finding on PR #388) — the signal handler only sets this flag; the actual CloseBrowser/CefQuitMessageLoop/CefShutdown sequence runs from a normal UI-thread task that polls it. -extern volatile std::sig_atomic_t g_worldscript_shutdown_requested; - -void InstallShutdownSignalHandlers(); - -#endif // WORLDSCRIPT_DESKTOP_CEF_SHUTDOWN_SIGNAL_H_ diff --git a/apps/desktop-cef/src/worldscript_app.cpp b/apps/desktop-cef/src/worldscript_app.cpp deleted file mode 100644 index 39bb62d6..00000000 --- a/apps/desktop-cef/src/worldscript_app.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "worldscript_app.h" - -#include - -#include "include/cef_browser.h" -#include "include/views/cef_browser_view.h" -#include "include/views/cef_window.h" -#include "include/wrapper/cef_helpers.h" - -#include "worldscript_handler.h" - -namespace { - -// QNBS-v3: window-close policy only (cefsimple convention) — CanClose returns TryCloseBrowser's own result, not an unconditional true. -class WorldScriptWindowDelegate : public CefWindowDelegate { - public: - explicit WorldScriptWindowDelegate(CefRefPtr browser_view) - : browser_view_(browser_view) {} - - void OnWindowCreated(CefRefPtr window) override { - window->AddChildView(browser_view_); - window->Show(); - browser_view_->RequestFocus(); - } - - void OnWindowDestroyed(CefRefPtr window) override { browser_view_ = nullptr; } - - bool CanClose(CefRefPtr window) override { - // QNBS-v3: TryCloseBrowser (not CloseBrowser(false) + unconditional true) — an unconditional true let the window close before CEF's own unload-handler sequence finished, matching cefsimple's real pattern (CodeRabbit review finding on PR #388). - CefRefPtr browser = browser_view_ ? browser_view_->GetBrowser() : nullptr; - if (browser) { - return browser->GetHost()->TryCloseBrowser(); - } - return true; - } - - CefSize GetPreferredSize(CefRefPtr view) override { return CefSize(1024, 768); } - - private: - CefRefPtr browser_view_; - - IMPLEMENT_REFCOUNTING(WorldScriptWindowDelegate); - DISALLOW_COPY_AND_ASSIGN(WorldScriptWindowDelegate); -}; - -class WorldScriptBrowserViewDelegate : public CefBrowserViewDelegate { - public: - WorldScriptBrowserViewDelegate() = default; - - private: - IMPLEMENT_REFCOUNTING(WorldScriptBrowserViewDelegate); - DISALLOW_COPY_AND_ASSIGN(WorldScriptBrowserViewDelegate); -}; - -} // namespace - -WorldScriptApp::WorldScriptApp(std::string start_url) : start_url_(std::move(start_url)) {} - -void WorldScriptApp::OnContextInitialized() { - CEF_REQUIRE_UI_THREAD(); - - CefRefPtr handler(new WorldScriptHandler()); - CefBrowserSettings browser_settings; - - CefRefPtr browser_view_delegate = new WorldScriptBrowserViewDelegate(); - CefRefPtr browser_view = CefBrowserView::CreateBrowserView( - handler, start_url_, browser_settings, nullptr, nullptr, browser_view_delegate); - - CefWindow::CreateTopLevelWindow(new WorldScriptWindowDelegate(browser_view)); -} diff --git a/apps/desktop-cef/src/worldscript_app.h b/apps/desktop-cef/src/worldscript_app.h deleted file mode 100644 index 6b7feaad..00000000 --- a/apps/desktop-cef/src/worldscript_app.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef WORLDSCRIPT_DESKTOP_CEF_WORLDSCRIPT_APP_H_ -#define WORLDSCRIPT_DESKTOP_CEF_WORLDSCRIPT_APP_H_ - -#include - -#include "include/cef_app.h" - -// QNBS-v3: bootstraps a single top-level window via CEF's Views framework (avoids raw X11/GTK code, matching cefsimple) — C++ owns CEF integration only (ADR-0020), no WorldScript logic here. -class WorldScriptApp : public CefApp, public CefBrowserProcessHandler { - public: - explicit WorldScriptApp(std::string start_url); - - CefRefPtr GetBrowserProcessHandler() override { return this; } - - void OnContextInitialized() override; - - private: - std::string start_url_; - - IMPLEMENT_REFCOUNTING(WorldScriptApp); - DISALLOW_COPY_AND_ASSIGN(WorldScriptApp); -}; - -#endif // WORLDSCRIPT_DESKTOP_CEF_WORLDSCRIPT_APP_H_ diff --git a/apps/desktop-cef/src/worldscript_handler.cpp b/apps/desktop-cef/src/worldscript_handler.cpp deleted file mode 100644 index 5b0d7e88..00000000 --- a/apps/desktop-cef/src/worldscript_handler.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "worldscript_handler.h" - -#include -#include - -#include "include/cef_app.h" -#include "include/cef_browser.h" -#include "include/cef_task.h" -#include "include/wrapper/cef_helpers.h" - -#include "shutdown_signal.h" - -// QNBS-v3: declared not defined — implemented in rust-core, linked in by Corrosion; this is the whole FFI boundary ADR-0020 proves (C++ never implements WorldScript logic itself). -extern "C" int worldscript_rust_ping(); - -namespace { - -constexpr int kShutdownPollIntervalMs = 100; - -// QNBS-v3: lookup table (repo convention) over an if/else chain — six real, verified enum values from include/internal/cef_types.h in the pinned CEF branch. -const char* TerminationStatusToString(cef_termination_status_t status) { - static const std::unordered_map kNames = { - {TS_ABNORMAL_TERMINATION, "TS_ABNORMAL_TERMINATION"}, - {TS_PROCESS_WAS_KILLED, "TS_PROCESS_WAS_KILLED"}, - {TS_PROCESS_CRASHED, "TS_PROCESS_CRASHED"}, - {TS_PROCESS_OOM, "TS_PROCESS_OOM"}, - {TS_LAUNCH_FAILED, "TS_LAUNCH_FAILED"}, - {TS_INTEGRITY_FAILURE, "TS_INTEGRITY_FAILURE"}, - }; - const auto it = kNames.find(status); - return it != kNames.end() ? it->second : "TS_UNKNOWN"; -} - -// QNBS-v3: plain CefTask subclass instead of base::BindOnce — CEF's own ref-counting scheme hit real base::Bind template/header issues (caught by CI, not locally); this is simpler and avoids that machinery entirely. -class PollShutdownTask : public CefTask { - public: - explicit PollShutdownTask(CefRefPtr handler) : handler_(handler) {} - void Execute() override { handler_->PollShutdownFlag(); } - - private: - CefRefPtr handler_; - - IMPLEMENT_REFCOUNTING(PollShutdownTask); - DISALLOW_COPY_AND_ASSIGN(PollShutdownTask); -}; - -} // namespace - -WorldScriptHandler::WorldScriptHandler() = default; - -void WorldScriptHandler::OnTitleChange(CefRefPtr browser, const CefString& title) { - CEF_REQUIRE_UI_THREAD(); - // QNBS-v3: reports the actual title text (not just "a title fired") so the harness can require the specific "WorldScript Studio" title — a CEF error page would not produce it — CodeRabbit/Qodo review finding on PR #388. - printf("[worldscript_host] title = %s\n", title.ToString().c_str()); - fflush(stdout); -} - -void WorldScriptHandler::OnAfterCreated(CefRefPtr browser) { - CEF_REQUIRE_UI_THREAD(); - browser_list_.push_back(browser); - // QNBS-v3: moved here from OnTitleChange (Qodo review finding on PR #388) — this fires deterministically once per browser regardless of page content, so the FFI-boundary proof no longer depends on the loaded page setting/changing a title. - printf("[worldscript_host] rust_core ping = %d\n", worldscript_rust_ping()); - fflush(stdout); - - // QNBS-v3: CefClient::GetAccessibilityHandler doesn't exist (PR #391's real finding) — that method is on CefRenderHandler instead, which is OSR-only ("when window rendering is disabled", include/cef_render_handler.h) and doesn't apply to this Views-based windowed host. SetAccessibilityState's own doc comment (include/cef_browser.h) confirms windowed browsers need only this one call: "all platform accessibility objects will be created and managed by Chromium's internal implementation" — no CefAccessibilityHandler required. - browser->GetHost()->SetAccessibilityState(STATE_ENABLED); - printf("[worldscript_host] accessibility_state_requested = true\n"); - fflush(stdout); - - CefPostDelayedTask(TID_UI, new PollShutdownTask(this), kShutdownPollIntervalMs); -} - -void WorldScriptHandler::PollShutdownFlag() { - CEF_REQUIRE_UI_THREAD(); - if (g_worldscript_shutdown_requested) { - // QNBS-v3: TryCloseBrowser (not CloseBrowser(false) directly) — it respects unload handlers and re-signals CanClose once ready, matching cefsimple's real close protocol (CodeRabbit review finding on PR #388). - for (const auto& browser : browser_list_) { - browser->GetHost()->TryCloseBrowser(); - } - return; // Closing now — no need to keep polling. - } - if (!browser_list_.empty()) { - CefPostDelayedTask(TID_UI, new PollShutdownTask(this), kShutdownPollIntervalMs); - } -} - -void WorldScriptHandler::OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status, - int error_code, - const CefString& error_string) { - CEF_REQUIRE_UI_THREAD(); - // QNBS-v3: proof line for the CI harness's crash cycle — the browser process reaching this line at all is itself the "renderer termination observed and handled" evidence (CEF-RUST-COMPETENCY-MATRIX.md), since only the renderer subprocess died. - printf("[worldscript_host] renderer_terminated status=%s error_code=%d\n", - TerminationStatusToString(status), error_code); - fflush(stdout); -} - -bool WorldScriptHandler::DoClose(CefRefPtr browser) { - CEF_REQUIRE_UI_THREAD(); - // QNBS-v3: no save-coordinator/state to flush yet (Wave 5+ scope, docs/cef/knowledge/subprocess-and-shutdown.md) — allow the close to proceed. - return false; -} - -void WorldScriptHandler::OnBeforeClose(CefRefPtr browser) { - CEF_REQUIRE_UI_THREAD(); - for (auto it = browser_list_.begin(); it != browser_list_.end(); ++it) { - if ((*it)->IsSame(browser)) { - browser_list_.erase(it); - break; - } - } - if (browser_list_.empty()) { - // QNBS-v3: the literal mechanism the Wave 2 spike proved (ADR-0020) — makes CefRunMessageLoop() in main() return, the signal to call CefShutdown(). - CefQuitMessageLoop(); - } -} diff --git a/apps/desktop-cef/src/worldscript_handler.h b/apps/desktop-cef/src/worldscript_handler.h deleted file mode 100644 index 794deaf9..00000000 --- a/apps/desktop-cef/src/worldscript_handler.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef WORLDSCRIPT_DESKTOP_CEF_WORLDSCRIPT_HANDLER_H_ -#define WORLDSCRIPT_DESKTOP_CEF_WORLDSCRIPT_HANDLER_H_ - -#include - -#include "include/cef_client.h" - -// QNBS-v3: browser-process lifecycle/display/request callbacks only (ADR-0020 scorecard) — renderer-process-specific handlers (CefRenderProcessHandler) are explicitly out of scope for this proof. -class WorldScriptHandler : public CefClient, - public CefLifeSpanHandler, - public CefDisplayHandler, - public CefRequestHandler { - public: - WorldScriptHandler(); - - CefRefPtr GetLifeSpanHandler() override { return this; } - CefRefPtr GetDisplayHandler() override { return this; } - CefRefPtr GetRequestHandler() override { return this; } - - void OnTitleChange(CefRefPtr browser, const CefString& title) override; - - void OnAfterCreated(CefRefPtr browser) override; - bool DoClose(CefRefPtr browser) override; - void OnBeforeClose(CefRefPtr browser) override; - - // QNBS-v3: real, verified CefRequestHandler method (unlike the reverted GetAccessibilityHandler attempt) — fires in the browser process when a renderer subprocess dies; the browser process itself and CefRunMessageLoop() keep running. - void OnRenderProcessTerminated(CefRefPtr browser, - TerminationStatus status, - int error_code, - const CefString& error_string) override; - - // QNBS-v3: public (not private) — called from PollShutdownTask::Execute(), an unrelated class in worldscript_handler.cpp's anonymous namespace; polls g_worldscript_shutdown_requested from the UI thread and requests a graceful close via TryCloseBrowser when set. - void PollShutdownFlag(); - - private: - std::list> browser_list_; - - IMPLEMENT_REFCOUNTING(WorldScriptHandler); - DISALLOW_COPY_AND_ASSIGN(WorldScriptHandler); -}; - -#endif // WORLDSCRIPT_DESKTOP_CEF_WORLDSCRIPT_HANDLER_H_ diff --git a/scripts/cef/cef-version.json b/scripts/cef/cef-version.json deleted file mode 100644 index bc3915b4..00000000 --- a/scripts/cef/cef-version.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "$comment": "Pinned CEF binary distribution for the Wave 2 learning harness (docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md §3142, ADR-0020). Bump this file — and only this file — to move to a new CEF version; scripts/cef/fetch-cef-sdk.mjs reads it, never hardcodes a version. sha1/sizeBytes come straight from https://cef-builds.spotifycdn.com/index.json — do not hand-compute them.", - "cefVersion": "151.3.18+gbeff58d+chromium-151.0.7922.138", - "platform": "linux64", - "distType": "minimal", - "filename": "cef_binary_151.3.18+gbeff58d+chromium-151.0.7922.138_linux64_minimal.tar.bz2", - "sha1": "5fc23dcccfead1044b2c3bf02e4ad27e86c29c0f", - "sizeBytes": 321422583, - "baseUrl": "https://cef-builds.spotifycdn.com" -} diff --git a/scripts/cef/cefPaths.mjs b/scripts/cef/cefPaths.mjs deleted file mode 100644 index a9ee9085..00000000 --- a/scripts/cef/cefPaths.mjs +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env node -/** - * Shared cache-path computation for the CEF Wave 2 scripts (ADR-0020) — kept in one place so - * fetch-cef-sdk.mjs and print-cef-version-diagnostics.mjs can never compute a different path for - * the same pinned SDK. - */ -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const root = path.join(__dirname, '..', '..'); - -/** - * @param {{filename: string}} pin - * @param {string} [cacheDirArg] - */ -export function resolveCefPaths(pin, cacheDirArg) { - const cacheDir = cacheDirArg ? path.resolve(cacheDirArg) : path.join(root, '.cef-cache'); - const archivePath = path.join(cacheDir, pin.filename); - const extractedDirName = pin.filename.replace(/\.tar\.bz2$/, ''); - const extractedDir = path.join(cacheDir, extractedDirName); - return { cacheDir, archivePath, extractedDir, extractedDirName }; -} diff --git a/scripts/cef/check-linux-runtime-deps.mjs b/scripts/cef/check-linux-runtime-deps.mjs deleted file mode 100644 index 1419fdb1..00000000 --- a/scripts/cef/check-linux-runtime-deps.mjs +++ /dev/null @@ -1,81 +0,0 @@ -#!/usr/bin/env node -/** - * CEF Linux runtime-dependency inventory (docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md §44.3, ADR-0020). - * - * Formalizes the Wave 2 spike's informal dev-machine findings - * (docs/cef/knowledge/linux-runtime-notes.md) into a real, CI-runnable check. Reports which of - * CEF's documented runtime shared-library dependencies are present via dpkg, on *this* machine. - * - * Deliberately non-fatal by default (exit 0 even with missing packages): this script's job is to - * produce an honest inventory data point, not to gate CI on a specific distro's package set — - * that gate belongs to a later packaging wave (§44's clean-machine test), once WorldScript ships - * an actual installer with declared dependencies. Pass --strict to fail on any missing package - * once a real compatibility floor has been proven (not yet — see the roadmap warning below). - * - * Run: node scripts/cef/check-linux-runtime-deps.mjs [--strict] - */ -import { execFileSync } from 'node:child_process'; - -// QNBS-v3: list is what CEF's own build docs call out, confirmed present on one dev machine — see docs/cef/knowledge/linux-runtime-notes.md; don't add packages from generic Chromium docs without a real WorldScript test. -const REQUIRED_PACKAGES = [ - 'libnss3', - 'libnspr4', - 'libatk1.0-0', - 'libatk-bridge2.0-0', - 'libcups2', - 'libdrm2', - 'libgbm1', - 'libxcomposite1', - 'libxdamage1', - 'libxfixes3', - 'libxrandr2', - 'libxkbcommon0', - 'libpango-1.0-0', - 'libcairo2', - 'libasound2', - 'libgtk-3-0', - 'libx11-xcb1', - 'libxcb1', -]; - -/** @param {string} pkg */ -function dpkgReportsInstalled(pkg) { - try { - // QNBS-v3: dpkg -s exits 0 even for a removed package still holding config-files state — only the Status line's "installed" word confirms it's actually present. - const out = execFileSync('dpkg', ['-s', pkg], { - stdio: ['ignore', 'pipe', 'ignore'], - }).toString(); - return /^Status:.*\binstalled\b/m.test(out); - } catch { - return false; - } -} - -// QNBS-v3: Ubuntu 24.04's 64-bit-time_t transition renamed several of these to a "t64" suffix (confirmed on the real ubuntu-latest CI runner) — check both spellings. -/** @param {string} pkg */ -function isInstalled(pkg) { - return dpkgReportsInstalled(pkg) || dpkgReportsInstalled(`${pkg}t64`); -} - -const strict = process.argv.includes('--strict'); -const results = REQUIRED_PACKAGES.map((pkg) => ({ pkg, present: isInstalled(pkg) })); -const missing = results.filter((r) => !r.present); - -console.log('[check-linux-deps] CEF Linux runtime-dependency inventory:'); -for (const { pkg, present } of results) { - console.log(` ${present ? '✓' : '✗'} ${pkg}`); -} - -if (missing.length > 0) { - console.log( - `\n[check-linux-deps] ${missing.length}/${results.length} package(s) missing on this machine: ` + - missing.map((m) => m.pkg).join(', '), - ); -} else { - console.log(`\n[check-linux-deps] All ${results.length} documented packages present.`); -} - -if (strict && missing.length > 0) { - console.error('[check-linux-deps] --strict requested and packages are missing — failing.'); - process.exit(1); -} diff --git a/scripts/cef/check-linux-runtime-linkage.mjs b/scripts/cef/check-linux-runtime-linkage.mjs deleted file mode 100644 index 8e9065d9..00000000 --- a/scripts/cef/check-linux-runtime-linkage.mjs +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env node -/** - * CEF Linux runtime *linkage* check (docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md §44.3) — - * the specific gap docs/architecture/native-readiness.md and - * docs/cef/knowledge/linux-runtime-notes.md have both flagged as open since Wave 2's - * first spike: check-linux-runtime-deps.mjs only confirms a *package* is installed via - * dpkg, never that the *actual shipped* .so files this build produced can resolve their - * real runtime dependencies. A package can be installed and still not satisfy a binary's - * exact SONAME/version requirement; dpkg presence alone doesn't prove that. - * - * Runs `ldd` against the real, already-built worldscript_host executable and the real - * libcef.so CEF shipped into the same output directory (COPY_FILES, apps/desktop-cef/ - * CMakeLists.txt) — not a hypothetical package list. Reports any `=> not found` line as - * a genuine unresolved runtime dependency. - * - * Deliberately non-fatal by default, matching check-linux-runtime-deps.mjs's own - * reasoning: an honest inventory data point, not a gate on a specific distro's package - * set, until a real compatibility floor (§44.1) has been proven. Pass --strict once it - * has. - * - * Run: node scripts/cef/check-linux-runtime-linkage.mjs [--strict] - */ -import { execFileSync } from 'node:child_process'; -import path from 'node:path'; - -const [outputDir] = process.argv.slice(2); -const strict = process.argv.includes('--strict'); - -if (!outputDir) { - console.error( - '[check-linux-linkage] Usage: node scripts/cef/check-linux-runtime-linkage.mjs [--strict]', - ); - process.exit(1); -} - -// QNBS-v3: the two artifacts whose actual runtime linkage matters most — the host executable itself, and libcef.so, CEF's own largest and most dependency-heavy shared library (apps/desktop-cef/CMakeLists.txt's CEF_BINARY_FILES COPY_FILES step puts both in the same output directory). -const TARGETS = ['worldscript_host', 'libcef.so']; - -/** @param {string} target */ -function checkLinkage(target) { - const targetPath = path.join(outputDir, target); - let out; - try { - // QNBS-v3: ldd's own exit code is 0 even when a dependency is unresolved (it prints "=> not found" and still exits cleanly) — the unresolved-dependency signal is in stdout text, not the process exit code, so it must be parsed rather than trusted from execFileSync alone. - out = execFileSync('ldd', [targetPath], { stdio: ['ignore', 'pipe', 'pipe'] }).toString(); - } catch (err) { - // A genuinely non-dynamic-executable or missing file is itself a real finding, not a script bug. - return { - target, - ok: false, - notFound: [], - error: err instanceof Error ? err.message : String(err), - }; - } - const notFound = out - .split('\n') - .map((line) => line.trim()) - .filter((line) => line.includes('=> not found') || /^\S+\s+not found/.test(line)); - return { target, ok: notFound.length === 0, notFound, error: null }; -} - -const results = TARGETS.map(checkLinkage); - -console.log( - '[check-linux-linkage] CEF Linux runtime *linkage* check (ldd against shipped .so files):', -); -for (const { target, ok, notFound, error } of results) { - if (error) { - console.log(` ✗ ${target} — could not run ldd: ${error}`); - continue; - } - console.log( - ` ${ok ? '✓' : '✗'} ${target}${ok ? '' : ` — ${notFound.length} unresolved dependency line(s):`}`, - ); - for (const line of notFound) { - console.log(` ${line}`); - } -} - -const anyFailed = results.some((r) => r.error || !r.ok); -if (anyFailed) { - console.log('\n[check-linux-linkage] One or more targets have unresolved runtime dependencies.'); -} else { - console.log( - `\n[check-linux-linkage] All ${results.length} target(s) fully resolved on this runner.`, - ); -} - -if (strict && anyFailed) { - console.error( - '[check-linux-linkage] --strict requested and unresolved dependencies found — failing.', - ); - process.exit(1); -} diff --git a/scripts/cef/check-linux-sandbox-inventory.mjs b/scripts/cef/check-linux-sandbox-inventory.mjs deleted file mode 100644 index 19e67be2..00000000 --- a/scripts/cef/check-linux-sandbox-inventory.mjs +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env node -/** - * CEF Linux sandbox feasibility inventory (Wave 2, roadmap Sandbox posture item — "Not yet - * attempted" in docs/architecture/native-readiness.md). - * - * Diagnostic-only, non-invasive — reports real evidence about whether this runner can support - * Chromium's Linux sandbox, before any attempt to actually enable it (CefSettings.no_sandbox is - * currently `true` unconditionally, apps/desktop-cef/src/main.cpp, ADR-0020). Never changes - * worldscript_host's build or runtime behavior. - * - * Background (confirmed against CEF's own docs/sandbox_setup.md and Chromium's - * docs/linux_sandboxing.md before writing this — CEF has no Linux-specific sandbox API, unlike - * cef_sandbox_win.h/cef_sandbox_mac.h; the sandbox is entirely a Chromium-internal mechanism, - * toggled only via CefSettings.no_sandbox): - * - Layer-1 (process/namespace isolation): the legacy setuid `chrome-sandbox` helper (needs - * root ownership + the setuid bit), or the modern unprivileged user-namespaces sandbox - * (preferred automatically since Chromium M-43 if the kernel/policy allows it — no setuid - * binary needed at all). - * - Layer-2 (seccomp-bpf syscall filtering): independent of layer-1, needs Linux kernel >= 3.5. - * - * This script checks real, functional evidence for layer-1 feasibility — not just documentation - * claims — since a sysctl value alone doesn't prove unprivileged namespace creation actually - * succeeds (AppArmor profiles or container restrictions can block it even when the sysctl says - * "enabled"). - * - * Run: node scripts/cef/check-linux-sandbox-inventory.mjs - */ -import { execFileSync } from 'node:child_process'; -import fs from 'node:fs'; - -function tryRun(cmd, args) { - try { - return execFileSync(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'] }) - .toString() - .trim(); - } catch { - return null; - } -} - -console.log('[check-sandbox-inventory] CEF Linux sandbox feasibility inventory:'); - -const kernelRelease = tryRun('uname', ['-r']); -console.log(` kernel release: ${kernelRelease ?? '(uname failed)'}`); - -// QNBS-v3: this sysctl is a Debian/Ubuntu-specific patch, not present on every distro/kernel — -// its absence is itself informative (means the mainline kernel default applies), not an error. -let sysctlValue = null; -try { - sysctlValue = fs.readFileSync('/proc/sys/kernel/unprivileged_userns_clone', 'utf8').trim(); - console.log(` kernel.unprivileged_userns_clone: ${sysctlValue}`); -} catch { - console.log(' kernel.unprivileged_userns_clone: (sysctl not present on this kernel/distro)'); -} - -// QNBS-v3: functional test, not just reading the sysctl — AppArmor profiles or container-level -// restrictions (e.g. missing CAP_SYS_ADMIN, seccomp policies on the runner itself) can block -// unprivileged namespace creation even when the sysctl claims it's allowed. -const unshareTest = tryRun('unshare', ['--user', '--pid', '--fork', 'true']); -const unshareWorks = unshareTest !== null; -console.log( - ` unshare --user --pid --fork (functional test): ${unshareWorks ? 'succeeded' : 'FAILED'}`, -); - -// QNBS-v3: existsSync is not a guarantee the following read succeeds (TOCTOU, permissions) — -// an unguarded readFileSync throwing here would abort this entire diagnostic script before it -// even reaches CEF SDK setup. CodeRabbit finding on PR #402. -let aaEnabled = null; -if (fs.existsSync('/sys/module/apparmor/parameters/enabled')) { - try { - aaEnabled = fs.readFileSync('/sys/module/apparmor/parameters/enabled', 'utf8').trim(); - } catch { - aaEnabled = '(read failed)'; - } -} -console.log(` AppArmor module enabled: ${aaEnabled ?? '(not present)'}`); - -console.log( - `\n[check-sandbox-inventory] Verdict: unprivileged user-namespace sandboxing appears ` + - `${unshareWorks ? 'FEASIBLE' : 'NOT FEASIBLE'} on this runner (functional test, not just a sysctl read). ` + - `This is informational only — no sandbox behavior was changed by running this script.`, -); diff --git a/scripts/cef/fetch-cef-sdk.mjs b/scripts/cef/fetch-cef-sdk.mjs deleted file mode 100644 index ddf25949..00000000 --- a/scripts/cef/fetch-cef-sdk.mjs +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env node -import { execFileSync } from 'node:child_process'; -/** - * CEF SDK fetch/verify/extract for the Wave 2 learning harness - * (docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md §3142, ADR-0020). - * - * ADR-0020's own "Consequences" section requires this to be a fetch script, never committed - * binaries — the SDK is ~300MB compressed / ~1.5GB extracted. Idempotent: a second run with a - * matching cache is a no-op, so CI can call this on every job without re-downloading each time - * (pair with actions/cache on CACHE_DIR). - * - * Version/checksum are read from cef-version.json, never hardcoded here — bumping the pinned CEF - * version is a one-file diff, not a code change. - * - * Run: node scripts/cef/fetch-cef-sdk.mjs [--cache-dir ] - */ -import { createHash } from 'node:crypto'; -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { resolveCefPaths } from './cefPaths.mjs'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -const pin = JSON.parse(fs.readFileSync(path.join(__dirname, 'cef-version.json'), 'utf8')); - -const argCacheDirIdx = process.argv.indexOf('--cache-dir'); -const cacheDirArg = argCacheDirIdx !== -1 ? process.argv[argCacheDirIdx + 1] : undefined; -if (argCacheDirIdx !== -1 && (!cacheDirArg || cacheDirArg.startsWith('--'))) { - throw new Error('[fetch-cef-sdk] --cache-dir requires a directory path'); -} -const { cacheDir, archivePath, extractedDir, extractedDirName } = resolveCefPaths(pin, cacheDirArg); -const markerPath = path.join(extractedDir, '.fetch-cef-sdk-verified'); - -/** @param {string} filePath */ -function sha1Of(filePath) { - const hash = createHash('sha1'); - hash.update(fs.readFileSync(filePath)); - return hash.digest('hex'); -} - -function alreadyVerified() { - if (!fs.existsSync(markerPath)) return false; - const marker = fs.readFileSync(markerPath, 'utf8').trim(); - return marker === pin.sha1; -} - -async function downloadArchive() { - const url = `${pin.baseUrl}/${pin.filename}`; - console.log( - `[fetch-cef-sdk] Downloading ${url} (${(pin.sizeBytes / 1024 / 1024).toFixed(0)} MB)…`, - ); - const res = await fetch(url); - if (!res.ok) { - throw new Error(`[fetch-cef-sdk] Download failed: HTTP ${res.status} ${res.statusText}`); - } - const buffer = Buffer.from(await res.arrayBuffer()); - fs.mkdirSync(cacheDir, { recursive: true }); - fs.writeFileSync(archivePath, buffer); -} - -function verifyArchive() { - const actual = sha1Of(archivePath); - if (actual !== pin.sha1) { - fs.rmSync(archivePath, { force: true }); - throw new Error( - `[fetch-cef-sdk] Checksum mismatch for ${pin.filename}: expected ${pin.sha1}, got ${actual}. ` + - 'Deleted the bad download — re-run to retry, or update cef-version.json if the pin is stale.', - ); - } -} - -function extractArchive() { - console.log(`[fetch-cef-sdk] Extracting to ${extractedDir}…`); - fs.rmSync(extractedDir, { recursive: true, force: true }); - // QNBS-v3: shells out to tar (bzip2 support) rather than a JS decompressor — runs once per cache miss, not hot code. - execFileSync('tar', ['xjf', archivePath, '-C', cacheDir], { stdio: 'inherit' }); - fs.writeFileSync(markerPath, pin.sha1); -} - -async function main() { - // QNBS-v3: enforced here, not just in the package.json wrapper — a direct `node` invocation must not bypass the CI-only disk/RAM guard either. - if (process.env.CI !== 'true') { - throw new Error( - '[fetch-cef-sdk] CI-only on this machine (disk/RAM constraints) — use: gh workflow run cef-learning-harness.yml', - ); - } - - if (alreadyVerified()) { - console.log( - `[fetch-cef-sdk] OK — ${extractedDirName} already fetched and verified (cache hit).`, - ); - console.log(extractedDir); - return; - } - - if (!fs.existsSync(archivePath) || sha1Of(archivePath) !== pin.sha1) { - await downloadArchive(); - verifyArchive(); - } else { - console.log('[fetch-cef-sdk] Archive present and checksum-verified; skipping download.'); - } - - extractArchive(); - console.log( - `[fetch-cef-sdk] OK — fetched CEF ${pin.cefVersion} (${pin.platform}/${pin.distType}).`, - ); - console.log(extractedDir); -} - -main().catch((err) => { - console.error(err.message ?? err); - process.exit(1); -}); diff --git a/scripts/cef/prepare-cef-build.mjs b/scripts/cef/prepare-cef-build.mjs deleted file mode 100644 index 8a956c09..00000000 --- a/scripts/cef/prepare-cef-build.mjs +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env node -/** - * Wires apps/desktop-cef into a fetched CEF SDK's own build, by appending an - * add_subdirectory() call to a *copy* of the SDK's root CMakeLists.txt — the exact - * mechanism ADR-0020's spike proved works, reused here instead of reinventing CEF's - * platform bootstrap (compiler/linker flags, OS_LINUX/PROJECT_ARCH detection) from a - * from-scratch top-level CMakeLists.txt in this repo. - * - * Never mutates the fetched SDK in place — copies its root CMakeLists.txt into the - * build directory first, so re-running scripts/cef/fetch-cef-sdk.mjs's cache-hit path - * always sees the SDK exactly as extracted. - * - * Run: node scripts/cef/prepare-cef-build.mjs - * Prints the patched CMakeLists.txt's directory (the -S argument for `cmake`). - */ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const repoRoot = path.join(__dirname, '..', '..'); - -const [cefDir, buildDir] = process.argv.slice(2); -if (!cefDir || !buildDir) { - console.error( - '[prepare-cef-build] Usage: node scripts/cef/prepare-cef-build.mjs ', - ); - process.exit(1); -} - -const sourceCMakeListsPath = path.join(cefDir, 'CMakeLists.txt'); -if (!fs.existsSync(sourceCMakeListsPath)) { - console.error(`[prepare-cef-build] Not found: ${sourceCMakeListsPath}`); - process.exit(1); -} - -const cmakeSourceDir = path.join(buildDir, 'cmake-src'); -fs.mkdirSync(cmakeSourceDir, { recursive: true }); - -// QNBS-v3: copies the whole extracted SDK's top level via symlinks for CEF's own subdirs, keeping only CMakeLists.txt as a real (patchable) file. -for (const entry of fs.readdirSync(cefDir, { withFileTypes: true })) { - if (entry.name === 'CMakeLists.txt') continue; - const target = path.join(cmakeSourceDir, entry.name); - // QNBS-v3: existsSync follows symlinks, so a dangling link from an earlier run (stale target) reads as absent and symlinkSync then throws EEXIST — lstatSync + remove first makes this idempotent (CodeRabbit review finding on PR #388). - try { - fs.lstatSync(target); - fs.rmSync(target, { recursive: true, force: true }); - } catch { - // No existing entry — nothing to remove. - } - fs.symlinkSync(path.join(cefDir, entry.name), target); -} - -const desktopCefAbsPath = path.join(repoRoot, 'apps', 'desktop-cef'); -const originalCMakeLists = fs.readFileSync(sourceCMakeListsPath, 'utf8'); -const patchedCMakeLists = `${originalCMakeLists}\nadd_subdirectory("${desktopCefAbsPath}" "\${CMAKE_BINARY_DIR}/worldscript_host")\n`; -fs.writeFileSync(path.join(cmakeSourceDir, 'CMakeLists.txt'), patchedCMakeLists); - -console.log(`[prepare-cef-build] Patched CMakeLists.txt ready at ${cmakeSourceDir}`); -console.log(cmakeSourceDir); diff --git a/scripts/cef/print-cef-version-diagnostics.mjs b/scripts/cef/print-cef-version-diagnostics.mjs deleted file mode 100644 index b573c20b..00000000 --- a/scripts/cef/print-cef-version-diagnostics.mjs +++ /dev/null @@ -1,79 +0,0 @@ -#!/usr/bin/env node -/** - * CEF version diagnostics (docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md §3142 "version diagnostics", - * ADR-0020). - * - * Parses the fetched SDK's include/cef_version.h for its CEF_* / CHROME_VERSION_* #define macros - * and prints them, so CI has a real, machine-readable record of exactly which CEF/Chromium build - * a given harness run exercised — not just the pin file's string, which only says what we *asked* - * for, not what actually landed in the extracted archive. - * - * Deliberately tolerant of macro-set drift across CEF versions: reports whichever of the known - * macro names it finds and does not fail on ones it doesn't, since CEF's exact macro list has - * changed across releases and this is a diagnostic aid, not a schema contract. - * - * Run: node scripts/cef/print-cef-version-diagnostics.mjs [path-to-extracted-cef-dir] - * With no argument, defaults to the standard `.cef-cache/` path that - * fetch-cef-sdk.mjs extracts to — so this is directly usable after `cef:fetch-sdk`. - */ -import fs from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; -import { resolveCefPaths } from './cefPaths.mjs'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); - -// QNBS-v3: requires BOTH a CEF and Chromium identifying macro — a partial hit doesn't establish which build this is. -const REQUIRED_IDENTIFYING_MACROS = ['CEF_VERSION_MAJOR', 'CHROME_VERSION_MAJOR']; - -const KNOWN_MACROS = [ - 'CEF_VERSION', - 'CEF_VERSION_MAJOR', - 'CEF_VERSION_MINOR', - 'CEF_VERSION_PATCH', - 'CEF_COMMIT_NUMBER', - 'CEF_COMMIT_HASH', - 'CHROME_VERSION_MAJOR', - 'CHROME_VERSION_MINOR', - 'CHROME_VERSION_BUILD', - 'CHROME_VERSION_PATCH', -]; - -let cefDir = process.argv[2]; -if (!cefDir) { - const pin = JSON.parse(fs.readFileSync(path.join(__dirname, 'cef-version.json'), 'utf8')); - cefDir = resolveCefPaths(pin).extractedDir; - console.log(`[cef-version-diagnostics] No path given — defaulting to ${cefDir}`); -} - -const versionHeaderPath = path.join(cefDir, 'include', 'cef_version.h'); -if (!fs.existsSync(versionHeaderPath)) { - console.error(`[cef-version-diagnostics] Not found: ${versionHeaderPath}`); - process.exit(1); -} - -const text = fs.readFileSync(versionHeaderPath, 'utf8'); -// QNBS-v3: [ \t] not \s — \s matches \n, so a bare `#define GUARD_H_` line would otherwise swallow the next line as its own value. -const DEFINE_RE = /^#define[ \t]+(\w+)[ \t]+(.+?)[ \t]*$/gm; - -/** @type {Record} */ -const found = {}; -for (const match of text.matchAll(DEFINE_RE)) { - const [, name, value] = match; - if (name && KNOWN_MACROS.includes(name)) { - found[name] = value.replace(/^"|"$/g, ''); - } -} - -console.log(`[cef-version-diagnostics] ${versionHeaderPath}`); -for (const macro of KNOWN_MACROS) { - console.log(` ${macro} = ${found[macro] ?? '(not found)'}`); -} - -const missingIdentifying = REQUIRED_IDENTIFYING_MACROS.filter((m) => !(m in found)); -if (missingIdentifying.length > 0) { - console.error( - `[cef-version-diagnostics] Could not identify the CEF/Chromium build — missing: ${missingIdentifying.join(', ')}. Header format may have changed.`, - ); - process.exit(1); -} diff --git a/scripts/cef/run-launch-cycle-proof.mjs b/scripts/cef/run-launch-cycle-proof.mjs deleted file mode 100644 index ae43f749..00000000 --- a/scripts/cef/run-launch-cycle-proof.mjs +++ /dev/null @@ -1,397 +0,0 @@ -#!/usr/bin/env node -/** - * Repeated start/close cycle proof for the Wave 2 CEF host (ADR-0020, roadmap - * §3142/§61.1.1 CI-enforceable harness checks). - * - * Launches the built worldscript_host N times against a real URL, sends SIGTERM - * after a startup grace period, and verifies a clean process tree after a shutdown - * grace period — per the documented finding that CEF's shutdown is not instantaneous - * (docs/cef/knowledge/subprocess-and-shutdown.md): an immediate post-signal check is - * a false positive, not evidence of a hang. - * - * Each cycle must independently show three proofs, not just "at least one cycle across - * the whole run" (a real review finding — masking a later cycle's failure behind an - * earlier success would make the proof meaningless): - * - FFI boundary: apps/desktop-cef/src/worldscript_handler.cpp's OnAfterCreated - * calls into rust-core deterministically, regardless of page content. - * - Real rendering: the page's title must be exactly "WorldScript Studio" — a CEF - * error page (bad bundle, load failure) would not produce that specific title, - * so this catches "CEF started but the app didn't actually render" failures the - * FFI proof alone cannot. - * - Accessibility state requested: SetAccessibilityState(STATE_ENABLED) is called on - * every cycle (see the Early Accessibility Gate note below). - * - * Early Accessibility Gate (roadmap §3142): apps/desktop-cef's OnAfterCreated calls - * browser->GetHost()->SetAccessibilityState(STATE_ENABLED) on every cycle — the real, - * windowed-mode-appropriate API (CefClient has no GetAccessibilityHandler(); that method - * is on CefRenderHandler, which is OSR-only and doesn't apply to this Views-based host — - * see docs/cef/knowledge/cef-architecture-primer.md). This proves accessibility state can - * be enabled intentionally (roadmap §23.1's first bullet); it does not yet prove the - * platform accessibility tree is actually observable — that needs OS-level AT-SPI - * introspection, not a CEF callback, and is separate follow-up work. - * - * After the repeated cycles, one additional crash-reporting proof runs - * (runCrashReportingProofCycle): launches with chrome://crash to deliberately crash - * the renderer subprocess, verifies CefCrashReportingEnabled() was true, verifies the - * browser process survived (CefRequestHandler::OnRenderProcessTerminated fired instead - * of the whole process dying), and verifies a real dump file was written to a - * BREAKPAD_DUMP_LOCATION-overridden directory. Full symbolization (dump_syms / - * minidump_stackwalk) needs a complete Chromium source checkout and is genuinely out of - * reach of this project's minimal-CEF-SDK-only CI setup — see the primer doc. - * - * Run: node scripts/cef/run-launch-cycle-proof.mjs [--cycles N] - */ -import { execFileSync, spawn } from 'node:child_process'; -import fs from 'node:fs'; -import os from 'node:os'; -import path from 'node:path'; - -const [binaryPath, url] = process.argv.slice(2); - -const cyclesArgIdx = process.argv.indexOf('--cycles'); -// QNBS-v3: distinguishes "flag absent" (default 3) from "flag present but no value" (e.g. trailing --cycles) — a naive undefined-check would silently default the latter too, same footgun class as fetch-cef-sdk.mjs's --cache-dir. -const cyclesArg = cyclesArgIdx !== -1 ? process.argv[cyclesArgIdx + 1] : undefined; -const cycles = cyclesArgIdx === -1 ? 3 : Number(cyclesArg); - -// QNBS-v3: raised from 4000ms after two consecutive CI runs on identical code (byte-for-byte matching main, which had passed reliably before) showed the browser process alive but never reaching OnAfterCreated within the old window — runner-speed variance, not a code regression. -// QNBS-v3: raised again from 10000ms after the same "Cycle 1: no FFI boundary proof" symptom -// recurred on a main push right after PR #400 added -g to worldscript_host — real, measured -// evidence: the binary grew from 1.34MB to 6.33MB (target_compile_options in -// apps/desktop-cef/CMakeLists.txt), a plausible contributor to slower first-launch I/O on a -// loaded runner. Cycles 2/3 in runCycle always passed at the old window — the actual observed -// failure only ever hit the cold first launch there. This constant is also read by -// runCrashReportingProofCycle's own renderer-crash-detection timeout below (line ~278) — a -// shared constant, not a runCycle-only one; that consumer's failure-detection window widens by -// the same 5s as a side effect, which is fine (strictly more lenient, same CI-runner-speed -// rationale applies), not a dedicated timeout since there's no evidence the two need to differ. -const STARTUP_GRACE_MS = 15000; -const SHUTDOWN_GRACE_MS = 6000; -// QNBS-v3: extra buffer after the main process exits — a renderer/GPU subprocess can take a moment longer to actually be reaped than its parent (docs/cef/knowledge/subprocess-and-shutdown.md's own "not instantaneous" finding applies to the whole tree, not just the browser process). -const ORPHAN_CHECK_GRACE_MS = 3000; -const FFI_PROOF_LINE = 'rust_core ping = 424242'; -const EXPECTED_TITLE_LINE = 'title = WorldScript Studio'; -// QNBS-v3: proves SetAccessibilityState(STATE_ENABLED) is requested on every cycle, not just once — same "no cycle can mask another" discipline as the FFI/title proofs above. -const ACCESSIBILITY_STATE_PROOF_LINE = 'accessibility_state_requested = true'; - -// QNBS-v3: crash-reporting/symbolization competency-gate proof (CEF-RUST-COMPETENCY-MATRIX.md) — mechanism verified against real CEF 151 source (libcef/common/crash_reporting.cc, crash_reporter_client.cc), not assumed; see docs/cef/knowledge/cef-architecture-primer.md. -const CRASH_REPORTING_ENABLED_LINE = 'crash_reporting_enabled = true'; -// QNBS-v3: requires the specific TS_PROCESS_CRASHED value, not a bare "status=" prefix — CodeAnt review finding on PR #392 (TS_LAUNCH_FAILED/TS_PROCESS_WAS_KILLED/TS_ABNORMAL_TERMINATION would otherwise also satisfy the proof). -const RENDERER_CRASHED_PROOF_LINE = 'renderer_terminated status=TS_PROCESS_CRASHED'; -const CRASH_URL = 'chrome://crash'; -// QNBS-v3: Crashpad's dump finalization is asynchronous relative to OnRenderProcessTerminated — CodeAnt review finding on PR #392; this is how long the harness waits for a real .dmp file before giving up, separate from SHUTDOWN_GRACE_MS's own meaning. Set generously (not the original 5000ms) per the same CI-runner-speed-variance lesson that forced STARTUP_GRACE_MS up from 4000ms to 10000ms in this same file — proactive, not waiting for a flaky failure to prove it (second CodeAnt finding, same PR). -const DUMP_WRITE_GRACE_MS = 8000; - -if (!binaryPath || !url) { - console.error( - '[launch-cycle-proof] Usage: node scripts/cef/run-launch-cycle-proof.mjs [--cycles N]', - ); - process.exit(1); -} - -// QNBS-v3: Number() accepts Infinity/NaN/fractional/non-positive values unvalidated — CodeAnt review finding on PR #388 (an unbounded or skipped loop from a malformed --cycles). -if (!Number.isInteger(cycles) || cycles <= 0) { - console.error(`[launch-cycle-proof] --cycles must be a positive integer, got: ${cyclesArg}`); - process.exit(1); -} - -function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -function listMatchingPids() { - try { - // QNBS-v3: anchored to the start of the command line — xvfb-run's own wrapper process also carries binaryPath as an argument it forwards, so an unanchored match false-flags it as a leaked worldscript_host. - const out = execFileSync('pgrep', ['-f', `^${binaryPath}`], { - stdio: ['ignore', 'pipe', 'ignore'], - }) - .toString() - .trim(); - return out - .split('\n') - .filter(Boolean) - .map(Number) - .filter((pid) => pid !== process.pid); - } catch { - return []; // pgrep exits 1 when nothing matches — that's the clean state. - } -} - -function processTreeAlive() { - return listMatchingPids().length > 0; -} - -// QNBS-v3: CEF re-execs the same binary for every subprocess role (renderer/GPU/crashpad-handler) — CodeAnt review finding on PR #392: SIGKILLing only the one tracked child PID can leave those descendants (including a Crashpad handler still writing to the dump directory) alive. This sweeps and kills everything matching the binary path, not just the direct child. -function killAllMatchingProcesses() { - for (const pid of listMatchingPids()) { - try { - process.kill(pid, 'SIGKILL'); - } catch { - // Already gone between the pgrep snapshot and this call — fine. - } - } -} - -function logStderr(label, stderr) { - if (stderr) console.error(`[launch-cycle-proof] ${label} stderr:\n${stderr}`); -} - -// QNBS-v3: recursive, not a flat readdir — Crashpad's on-disk database nests reports under subdirectories (e.g. pending/, completed/, attachments/) whose exact layout isn't asserted on here; callers filter the result to *.dmp specifically (settings.dat/lock/.meta files are written during normal init and are not evidence of a dump). -function findFilesRecursive(dir) { - let entries; - try { - entries = fs.readdirSync(dir, { withFileTypes: true }); - } catch { - return []; - } - return entries.flatMap((entry) => { - const full = path.join(dir, entry.name); - return entry.isDirectory() ? findFilesRecursive(full) : [full]; - }); -} - -async function runCycle(index) { - console.log(`[launch-cycle-proof] Cycle ${index + 1}/${cycles}: launching…`); - // QNBS-v3: cwd set to the binary's own directory — Chromium resolves several resource paths (icudtl.dat et al.) relative to cwd, not the executable's location; without this, "Invalid file descriptor to ICU data received" crashes it on startup even though every file is correctly present. - // QNBS-v3: verbose CEF/Chromium logging to stderr — an early crash otherwise produces zero diagnostic output, making root-causing it impossible from this harness's own log. - const child = spawn(binaryPath, [`--url=${url}`, '--enable-logging=stderr', '--v=1'], { - cwd: path.dirname(binaryPath), - stdio: ['ignore', 'pipe', 'pipe'], - }); - - let stdout = ''; - let stderr = ''; - child.stdout.on('data', (chunk) => { - stdout += chunk.toString(); - }); - child.stderr.on('data', (chunk) => { - stderr += chunk.toString(); - }); - - const exited = new Promise((resolve) => - child.once('exit', (code, signal) => resolve({ code, signal })), - ); - // QNBS-v3: 'exit' fires as soon as the process terminates but its stdio streams can still be open (buffered output not yet fully delivered); 'close' is the guarantee all of it has arrived — CodeRabbit review finding on PR #397, backed by real Node.js child_process docs and a reproduction. Only gates the stdout marker checks below, not the exit/signal checks above, which are legitimately about process termination itself. - const closed = new Promise((resolve) => child.once('close', () => resolve())); - - // QNBS-v3: races against the startup grace period so an immediate crash is caught here, distinct from a deliberate SIGTERM-driven exit later — Qodo review finding on PR #388 ("crashed cycles count clean"). - const earlyExit = await Promise.race([exited, sleep(STARTUP_GRACE_MS).then(() => null)]); - if (earlyExit) { - logStderr(`Cycle ${index + 1}`, stderr); - throw new Error( - `Cycle ${index + 1}: exited during startup (code=${earlyExit.code}, signal=${earlyExit.signal}) instead of staying up — likely a crash, not a deliberate shutdown.`, - ); - } - - child.kill('SIGTERM'); - const shutdownResult = await Promise.race([exited, sleep(SHUTDOWN_GRACE_MS).then(() => null)]); - if (!shutdownResult) { - logStderr(`Cycle ${index + 1}`, stderr); - throw new Error(`Cycle ${index + 1}: process did not exit within the shutdown grace period.`); - } - - // QNBS-v3: accepts either "died from the SIGTERM we sent" or "exited 0 on its own" as clean — anything else (e.g. SIGSEGV) is a real crash during shutdown, not evidence this proof should accept. - const cleanShutdown = shutdownResult.signal === 'SIGTERM' || shutdownResult.code === 0; - if (!cleanShutdown) { - logStderr(`Cycle ${index + 1}`, stderr); - throw new Error( - `Cycle ${index + 1}: abnormal exit during shutdown (code=${shutdownResult.code}, signal=${shutdownResult.signal}).`, - ); - } - - await sleep(ORPHAN_CHECK_GRACE_MS); - if (processTreeAlive()) { - logStderr(`Cycle ${index + 1}`, stderr); - throw new Error(`Cycle ${index + 1}: orphaned worldscript_host process(es) still running.`); - } - - // QNBS-v3: waits for 'close' (all stdio fully drained), not just 'exit' — see the QNBS-v3 comment where `closed` is declared above. Bounded rather than awaited outright, matching this file's established defensive-timeout convention even though close should already have fired well within ORPHAN_CHECK_GRACE_MS in practice. - const streamsClosed = await Promise.race([ - closed.then(() => true), - sleep(2000).then(() => false), - ]); - if (!streamsClosed) { - logStderr(`Cycle ${index + 1}`, stderr); - throw new Error( - `Cycle ${index + 1}: stdio streams did not close within 2000ms after process exit.`, - ); - } - - // QNBS-v3: required per cycle, not aggregated across the whole run — Qodo review finding on PR #388 ("FFI proof is not repeated"); one cycle's success must never mask another cycle's failure. - if (!stdout.includes(FFI_PROOF_LINE)) { - logStderr(`Cycle ${index + 1}`, stderr); - throw new Error(`Cycle ${index + 1}: no FFI boundary proof ("${FFI_PROOF_LINE}") observed.`); - } - if (!stdout.includes(EXPECTED_TITLE_LINE)) { - logStderr(`Cycle ${index + 1}`, stderr); - throw new Error( - `Cycle ${index + 1}: expected "${EXPECTED_TITLE_LINE}" not observed — the production bundle may not have rendered (a CEF error page would not produce this specific title).`, - ); - } - if (!stdout.includes(ACCESSIBILITY_STATE_PROOF_LINE)) { - logStderr(`Cycle ${index + 1}`, stderr); - throw new Error( - `Cycle ${index + 1}: expected "${ACCESSIBILITY_STATE_PROOF_LINE}" not observed — SetAccessibilityState(STATE_ENABLED) was not requested.`, - ); - } - console.log( - `[launch-cycle-proof] Cycle ${index + 1}/${cycles}: clean exit (signal=${shutdownResult.signal}), FFI + rendering + accessibility-state proofs all present.`, - ); -} - -// QNBS-v3: separate function, not a mode flag on runCycle — keeps the already-proven repeated-cycle proof completely untouched (the accessibility-attempt regression on PR #391 was caused by exactly this kind of shared-code coupling). -async function runCrashReportingProofCycle() { - console.log( - `[launch-cycle-proof] Crash-reporting proof: launching with ${CRASH_URL} to deliberately crash the renderer…`, - ); - // QNBS-v3: fresh, empty-at-start temp dir — BREAKPAD_DUMP_LOCATION (verified in libcef/common/crash_reporter_client.cc) overrides where CEF/Crashpad writes dumps on Linux/POSIX, so a *.dmp file appearing here is unambiguous evidence, no need to guess CEF's default directory layout. - const dumpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'worldscript-crash-dumps-')); - - const child = spawn(binaryPath, [`--url=${CRASH_URL}`, '--enable-logging=stderr', '--v=1'], { - cwd: path.dirname(binaryPath), - stdio: ['ignore', 'pipe', 'pipe'], - env: { ...process.env, BREAKPAD_DUMP_LOCATION: dumpDir }, - }); - - let stdout = ''; - let stderr = ''; - child.stdout.on('data', (chunk) => { - stdout += chunk.toString(); - }); - child.stderr.on('data', (chunk) => { - stderr += chunk.toString(); - }); - - const exited = new Promise((resolve) => - child.once('exit', (code, signal) => resolve({ code, signal })), - ); - - // QNBS-v3: every throw below is caught here so the child is always reaped, even on a failed proof — CodeAnt/Qodo review finding on PR #392 (a thrown assertion left the browser and its subprocesses orphaned). - try { - // QNBS-v3: `stopPolling` lets each while-loop below notice its own Promise.race already settled — CodeRabbit review finding on PR #392 (an abandoned loop kept scheduling sleep(200) forever after the race resolved via the other arm, keeping the Node process alive indefinitely on a failed/timed-out proof). - let stopPolling = false; - - // QNBS-v3: requires the specific TS_PROCESS_CRASHED value, not just any termination status — CodeAnt review finding on PR #392 (TS_LAUNCH_FAILED/TS_PROCESS_WAS_KILLED/TS_ABNORMAL_TERMINATION would otherwise also satisfy a bare prefix match). chrome://crash triggers a real SIGSEGV in the renderer, confirmed against CEF's own cefclient reference usage of this exact URL. - const rendererCrashed = await Promise.race([ - (async () => { - while (!stopPolling && !stdout.includes(RENDERER_CRASHED_PROOF_LINE)) { - await sleep(200); - } - return stdout.includes(RENDERER_CRASHED_PROOF_LINE); - })(), - exited.then(() => false), - sleep(STARTUP_GRACE_MS).then(() => false), - ]); - stopPolling = true; - - if (!rendererCrashed) { - throw new Error( - `"${RENDERER_CRASHED_PROOF_LINE}" not observed within ${STARTUP_GRACE_MS}ms (or the process exited early) — stdout so far:\n${stdout}`, - ); - } - // QNBS-v3: echoed on the success path too, not just via logStderr on failure — the raw child stdout is otherwise invisible in the CI log (Qodo/CodeAnt review context on PR #392: nothing here previously proved which status value was actually observed). - console.log( - `[launch-cycle-proof] Crash-reporting proof: observed "${RENDERER_CRASHED_PROOF_LINE}".`, - ); - - // QNBS-v3: the actual "renderer termination observed and handled" evidence (CEF-RUST-COMPETENCY-MATRIX.md) — only the renderer subprocess should have died; the browser process and its message loop must still be running. - if (!processTreeAlive()) { - throw new Error( - 'browser process is not alive after the renderer crash — process isolation did not hold.', - ); - } - - if (!stdout.includes(CRASH_REPORTING_ENABLED_LINE)) { - throw new Error( - `"${CRASH_REPORTING_ENABLED_LINE}" not observed — crash_reporter.cfg (apps/desktop-cef/resources/crash_reporter.cfg) was not found/parsed next to the binary.`, - ); - } - - // QNBS-v3: Crashpad's dump finalization is asynchronous relative to OnRenderProcessTerminated — CodeAnt review finding on PR #392 (shutting down the browser immediately raced the dump actually being written). Wait for a real *.dmp file, bounded, before sending SIGTERM. - stopPolling = false; - const dumpAppeared = await Promise.race([ - (async () => { - while ( - !stopPolling && - findFilesRecursive(dumpDir).filter((f) => f.endsWith('.dmp')).length === 0 - ) { - await sleep(200); - } - return findFilesRecursive(dumpDir).some((f) => f.endsWith('.dmp')); - })(), - sleep(DUMP_WRITE_GRACE_MS).then(() => false), - ]); - stopPolling = true; - // QNBS-v3: filtered to .dmp specifically — CodeAnt/Qodo review finding on PR #392 (Crashpad's settings.dat/lock/.meta files are written during normal init and would otherwise falsely count as "a dump produced"). - const dumpFiles = findFilesRecursive(dumpDir).filter((f) => f.endsWith('.dmp')); - if (!dumpAppeared || dumpFiles.length === 0) { - throw new Error( - `no .dmp file appeared under BREAKPAD_DUMP_LOCATION (${dumpDir}) within ${DUMP_WRITE_GRACE_MS}ms despite crash_reporting_enabled=true and an observed renderer crash.`, - ); - } - console.log( - `[launch-cycle-proof] Crash-reporting proof: dump file(s) confirmed before shutdown: ${dumpFiles.join(', ')}`, - ); - - child.kill('SIGTERM'); - const shutdownResult = await Promise.race([exited, sleep(SHUTDOWN_GRACE_MS).then(() => null)]); - if (!shutdownResult) { - throw new Error( - 'process did not exit within the shutdown grace period after the renderer crash.', - ); - } - // QNBS-v3: same clean-shutdown check as runCycle — Qodo review finding on PR #392 (the crash cycle accepted any exit, including an abnormal one, as a successful isolation proof). - const cleanShutdown = shutdownResult.signal === 'SIGTERM' || shutdownResult.code === 0; - if (!cleanShutdown) { - throw new Error( - `abnormal exit during shutdown (code=${shutdownResult.code}, signal=${shutdownResult.signal}).`, - ); - } - - await sleep(ORPHAN_CHECK_GRACE_MS); - if (processTreeAlive()) { - throw new Error('orphaned worldscript_host process(es) still running after shutdown.'); - } - - console.log( - '[launch-cycle-proof] Crash-reporting proof: OK — crash reporting enabled, renderer crash observed and handled (browser process survived), ' + - `${dumpFiles.length} dump file(s) written to the crash dump location. Full symbolization (dump_syms/minidump_stackwalk) requires a complete Chromium source checkout and is out of scope — see docs/cef/knowledge/cef-architecture-primer.md.`, - ); - } catch (err) { - logStderr('Crash-reporting proof', stderr); - // QNBS-v3: unconditional, not `if (!child.killed)` — .killed only reflects whether kill() was ever called, not whether the process actually died (e.g. SIGTERM already sent but the shutdown-grace-period/orphan checks below still failed); kill() on an already-exited process is a harmless no-op. - child.kill('SIGKILL'); - // QNBS-v3: await + verify, not fire-and-forget — CodeAnt review finding on PR #392 (the normal shutdown path waits and checks for orphans; the failure path didn't, so a failed proof could leave CEF subprocesses running after the harness exits). - await Promise.race([exited, sleep(SHUTDOWN_GRACE_MS)]); - // QNBS-v3: sweeps every process matching the binary path, not just the tracked child — CodeAnt review finding on PR #392 (a surviving renderer/GPU/crashpad-handler descendant could still be using dumpDir when the finally block below removes it). Runs before that removal, not after. - if (processTreeAlive()) { - killAllMatchingProcesses(); - await sleep(ORPHAN_CHECK_GRACE_MS); - if (processTreeAlive()) { - console.error( - '[launch-cycle-proof] Crash-reporting proof: WARNING — worldscript_host process(es) still running after failure cleanup.', - ); - } - } - throw new Error(`Crash-reporting proof: ${err instanceof Error ? err.message : String(err)}`); - } finally { - // QNBS-v3: CodeRabbit review finding on PR #392 — the dump directory (which can contain real browser memory) was never removed on success or failure. Runs after the process-tree sweep above, not before, so it never races a still-running Crashpad handler. - fs.rmSync(dumpDir, { recursive: true, force: true }); - } -} - -async function main() { - for (let i = 0; i < cycles; i++) { - await runCycle(i); - } - - console.log( - `[launch-cycle-proof] OK — ${cycles}/${cycles} repeated start/close cycles clean, FFI boundary, real rendering, and accessibility-state request all proven in every cycle.`, - ); - - await runCrashReportingProofCycle(); -} - -main().catch((err) => { - console.error(`[launch-cycle-proof] FAIL — ${err.message}`); - process.exit(1); -}); diff --git a/scripts/cef/run-symbolization-proof.mjs b/scripts/cef/run-symbolization-proof.mjs deleted file mode 100644 index 8abb42e7..00000000 --- a/scripts/cef/run-symbolization-proof.mjs +++ /dev/null @@ -1,272 +0,0 @@ -#!/usr/bin/env node -/** - * Crash-symbolization proof for the Wave 2 CEF host (roadmap §3142 exit criterion - * "initial crash-reporting/symbolization proof"). - * - * The existing crash-reporting proof (run-launch-cycle-proof.mjs's runCrashReportingProofCycle) - * deliberately crashes chrome://crash — a Chromium/CEF-internal renderer crash. That proves - * *reporting* (a real Crashpad .dmp file is written), but its own comment is explicit that - * *symbolizing* that specific dump is out of reach: the crash is entirely inside Chromium's own - * stripped code, and CEF's official Spotify-hosted builds (verified against - * https://cef-builds.spotifycdn.com/index.json for this project's pinned version) ship no - * separate debug-symbols archive for any distribution type (standard/tools/minimal/client) — so - * there is no way to symbolize Chromium-internal frames without building Chromium itself. - * - * This script proves the *other* half honestly instead: symbolizing a crash inside *our own* - * code, which we compile ourselves and fully control the debug info for. It deliberately crashes - * the browser process itself (not a renderer subprocess) via the --debug-crash-self CLI flag - * (apps/desktop-cef/src/main.cpp), which calls a distinctively-named Rust function - * (worldscript_rust_debug_crash_self_test, apps/desktop-cef/rust-core/src/lib.rs) that panics - * under panic=abort — a real SIGABRT Crashpad catches the same way it caught the renderer crash. - * - * dump_syms (github.com/mozilla/dump_syms) and minidump-stackwalk (github.com/rust-minidump/ - * rust-minidump) are both standalone Rust projects with prebuilt Linux release binaries — neither - * needs a Chromium checkout, confirmed by reading their own READMEs directly, not assumed. Their - * combination is the same Breakpad-format toolchain Mozilla uses for real Firefox crash - * symbolication, applied here to our own binary's DWARF debug info (RelWithDebInfo build + - * rust-core's `debug = true` release-profile override — see cef-learning-harness.yml). - * - * Deliberately a separate script, not a mode flag on run-launch-cycle-proof.mjs's existing - * crash-reporting proof — same "no shared-code coupling between proofs" discipline established - * after the PR #391 accessibility-attempt regression. - * - * Run: node scripts/cef/run-symbolization-proof.mjs - */ -import { execFileSync, spawn } from 'node:child_process'; -import fs from 'node:fs'; -import os from 'node:os'; -import path from 'node:path'; - -const [binaryPath, dumpSymsPath, minidumpStackwalkPath] = process.argv.slice(2); - -const STARTUP_GRACE_MS = 10000; -const SHUTDOWN_GRACE_MS = 6000; -const ORPHAN_CHECK_GRACE_MS = 3000; -const DUMP_WRITE_GRACE_MS = 8000; -const CRASH_TRIGGERED_LINE = 'debug_crash_self_test = triggering'; -// QNBS-v3: the exact Rust function name — proves minidump-stackwalk actually resolved a symbol, -// not just that it ran without error (a tool that silently produced zero symbols would still -// exit 0 and emit valid-but-useless JSON). -const CRASH_FUNCTION_NAME = 'worldscript_rust_debug_crash_self_test'; - -if (!binaryPath || !dumpSymsPath || !minidumpStackwalkPath) { - console.error( - '[symbolization-proof] Usage: node scripts/cef/run-symbolization-proof.mjs ', - ); - process.exit(1); -} - -function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -// QNBS-v3: pgrep -f treats its argument as an extended regex — an unescaped binaryPath would let -// any '.'/'+'/etc. in the runner's workspace path match arbitrary characters, risking a false -// "orphaned process" match. CodeRabbit finding on PR #400. -const binaryPathPattern = binaryPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - -function listMatchingPids() { - try { - const out = execFileSync('pgrep', ['-f', `^${binaryPathPattern}`], { - stdio: ['ignore', 'pipe', 'ignore'], - }) - .toString() - .trim(); - return out - .split('\n') - .filter(Boolean) - .map(Number) - .filter((pid) => pid !== process.pid); - } catch { - return []; - } -} - -function processTreeAlive() { - return listMatchingPids().length > 0; -} - -function killAllMatchingProcesses() { - for (const pid of listMatchingPids()) { - try { - process.kill(pid, 'SIGKILL'); - } catch { - // Already gone between the pgrep snapshot and this call — fine. - } - } -} - -function logStderr(label, stderr) { - if (stderr) console.error(`[symbolization-proof] ${label} stderr:\n${stderr}`); -} - -function findFilesRecursive(dir) { - let entries; - try { - entries = fs.readdirSync(dir, { withFileTypes: true }); - } catch { - return []; - } - return entries.flatMap((entry) => { - const full = path.join(dir, entry.name); - return entry.isDirectory() ? findFilesRecursive(full) : [full]; - }); -} - -async function main() { - console.log( - '[symbolization-proof] Launching with --debug-crash-self to deliberately crash the browser process itself…', - ); - const dumpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'worldscript-symbolization-dumps-')); - const symbolsDir = fs.mkdtempSync(path.join(os.tmpdir(), 'worldscript-symbolization-syms-')); - - const child = spawn( - binaryPath, - ['--url=about:blank', '--debug-crash-self', '--enable-logging=stderr', '--v=1'], - { - cwd: path.dirname(binaryPath), - stdio: ['ignore', 'pipe', 'pipe'], - env: { ...process.env, BREAKPAD_DUMP_LOCATION: dumpDir }, - }, - ); - - let stdout = ''; - let stderr = ''; - child.stdout.on('data', (chunk) => { - stdout += chunk.toString(); - }); - child.stderr.on('data', (chunk) => { - stderr += chunk.toString(); - }); - - const exited = new Promise((resolve) => - child.once('exit', (code, signal) => resolve({ code, signal })), - ); - - // QNBS-v3: every throw below is caught here so the child is always reaped — same discipline as - // run-launch-cycle-proof.mjs's runCrashReportingProofCycle (PR #392 finding). - try { - let stopPolling = false; - - const crashTriggered = await Promise.race([ - (async () => { - while (!stopPolling && !stdout.includes(CRASH_TRIGGERED_LINE)) { - await sleep(200); - } - return stdout.includes(CRASH_TRIGGERED_LINE); - })(), - sleep(STARTUP_GRACE_MS).then(() => false), - ]); - stopPolling = true; - if (!crashTriggered) { - throw new Error( - `"${CRASH_TRIGGERED_LINE}" not observed within ${STARTUP_GRACE_MS}ms — stdout so far:\n${stdout}`, - ); - } - - const shutdownResult = await Promise.race([exited, sleep(SHUTDOWN_GRACE_MS).then(() => null)]); - if (!shutdownResult) { - throw new Error( - 'process did not exit within the shutdown grace period after the self-crash.', - ); - } - // QNBS-v3: panic=abort raises SIGABRT — anything else (a clean exit, SIGTERM) means the - // crash trigger didn't actually crash the process, which would make the rest of this proof - // meaningless (symbolizing a dump that was never really a crash). - if (shutdownResult.signal !== 'SIGABRT') { - throw new Error( - `expected SIGABRT from the self-crash, got code=${shutdownResult.code} signal=${shutdownResult.signal}.`, - ); - } - console.log('[symbolization-proof] Browser process crashed via SIGABRT as expected.'); - - stopPolling = false; - const dumpAppeared = await Promise.race([ - (async () => { - while ( - !stopPolling && - findFilesRecursive(dumpDir).filter((f) => f.endsWith('.dmp')).length === 0 - ) { - await sleep(200); - } - return findFilesRecursive(dumpDir).some((f) => f.endsWith('.dmp')); - })(), - sleep(DUMP_WRITE_GRACE_MS).then(() => false), - ]); - stopPolling = true; - const dumpFiles = findFilesRecursive(dumpDir).filter((f) => f.endsWith('.dmp')); - if (!dumpAppeared || dumpFiles.length === 0) { - throw new Error( - `no .dmp file appeared under BREAKPAD_DUMP_LOCATION (${dumpDir}) within ${DUMP_WRITE_GRACE_MS}ms.`, - ); - } - console.log(`[symbolization-proof] Dump file confirmed: ${dumpFiles[0]}`); - - await sleep(ORPHAN_CHECK_GRACE_MS); - if (processTreeAlive()) { - throw new Error('orphaned worldscript_host process(es) still running after the self-crash.'); - } - - console.log('[symbolization-proof] Generating Breakpad symbols with dump_syms…'); - execFileSync(dumpSymsPath, ['-s', symbolsDir, binaryPath], { stdio: 'inherit' }); - const symFiles = findFilesRecursive(symbolsDir).filter((f) => f.endsWith('.sym')); - if (symFiles.length === 0) { - throw new Error(`dump_syms produced no .sym file under ${symbolsDir}.`); - } - console.log(`[symbolization-proof] Symbol file generated: ${symFiles[0]}`); - - console.log('[symbolization-proof] Running minidump-stackwalk…'); - const stackwalkOutput = execFileSync( - minidumpStackwalkPath, - ['--json', dumpFiles[0], symbolsDir], - { encoding: 'utf8' }, - ); - // QNBS-v3: parses the JSON and checks crashing_thread.frames[].function specifically, rather - // than a raw substring match over the whole document — the schema (rust-minidump/minidump- - // processor/json-schema.md) also has module names/file paths/assertion strings the name could - // coincidentally appear in outside an actual resolved frame. CodeRabbit finding on PR #400. - let report; - try { - report = JSON.parse(stackwalkOutput); - } catch (parseErr) { - throw new Error( - `minidump-stackwalk's --json output did not parse as JSON: ${parseErr instanceof Error ? parseErr.message : String(parseErr)}. Output:\n${stackwalkOutput.slice(0, 4000)}`, - ); - } - const frames = report?.crashing_thread?.frames ?? []; - const resolved = frames.some( - (frame) => typeof frame.function === 'string' && frame.function.includes(CRASH_FUNCTION_NAME), - ); - if (!resolved) { - throw new Error( - `no crashing_thread frame's "function" field contains "${CRASH_FUNCTION_NAME}" — the crash frame was not symbolized. Output:\n${stackwalkOutput.slice(0, 4000)}`, - ); - } - - console.log( - `[symbolization-proof] OK — real Crashpad dump from a self-induced crash was symbolized end-to-end, resolving "${CRASH_FUNCTION_NAME}" via dump_syms + minidump-stackwalk. Chromium/CEF-internal frames remain unsymbolized (no debug-symbols archive is published for this distribution) — this proof is scoped to our own code, honestly, not the whole stack.`, - ); - } catch (err) { - logStderr('Symbolization proof', stderr); - child.kill('SIGKILL'); - await Promise.race([exited, sleep(SHUTDOWN_GRACE_MS)]); - if (processTreeAlive()) { - killAllMatchingProcesses(); - await sleep(ORPHAN_CHECK_GRACE_MS); - if (processTreeAlive()) { - console.error( - '[symbolization-proof] WARNING — worldscript_host process(es) still running after failure cleanup.', - ); - } - } - throw new Error(`Symbolization proof: ${err instanceof Error ? err.message : String(err)}`); - } finally { - fs.rmSync(dumpDir, { recursive: true, force: true }); - fs.rmSync(symbolsDir, { recursive: true, force: true }); - } -} - -main().catch((err) => { - console.error(`[symbolization-proof] FAIL — ${err.message}`); - process.exit(1); -}); diff --git a/scripts/cef/run-wayland-smoke.mjs b/scripts/cef/run-wayland-smoke.mjs deleted file mode 100644 index 6a8bcbb8..00000000 --- a/scripts/cef/run-wayland-smoke.mjs +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env node -/** - * Best-effort Wayland launch smoke test for the Wave 2 CEF host (roadmap §44.2: - * "'CEF uses Chromium' is not accepted as proof of Wayland/X11 correctness" — the - * mandatory run-launch-cycle-proof.mjs only ever runs worldscript_host under Xvfb/X11). - * - * This is a genuine feasibility attempt, not a guess: Chromium's own upstream GN - * default (build/config/ozone.gni, `is_linux` branch) compiles BOTH the x11 and - * wayland Ozone platforms into every standard Linux build (`ozone_platform_wayland = - * true`), and CEF's own tools/gn_args.py has no override disabling it — confirmed by - * reading both files directly, not assumed. What's still genuinely unverified before - * this script runs is whether the fetched "linux64 minimal" binary distribution - * actually carries that support through, and whether a headless Weston compositor - * (the Wayland-side equivalent of Xvfb — no real display/GPU needed) is a viable - * launch target for it on a stock GitHub Actions runner. - * - * Deliberately does NOT touch worldscript_host's build or the already-proven X11 - * proofs — this launches the exact same already-built binary, just under - * --ozone-platform=wayland against a headless Weston socket instead of Xvfb. The - * calling CI step is marked continue-on-error so a real "Wayland doesn't work with - * this CEF distribution/runner" finding is informative, not a regression gate. - * - * Run: node scripts/cef/run-wayland-smoke.mjs - */ -import { execFileSync, spawn } from 'node:child_process'; -import fs from 'node:fs'; -import os from 'node:os'; -import path from 'node:path'; - -const [binaryPath, url] = process.argv.slice(2); - -if (!binaryPath || !url) { - console.error( - '[wayland-smoke] Usage: node scripts/cef/run-wayland-smoke.mjs ', - ); - process.exit(1); -} - -// QNBS-v3: same grace period rationale as run-launch-cycle-proof.mjs's STARTUP_GRACE_MS — CI-runner-speed variance, not a code concern this script has any control over. -const LAUNCH_GRACE_MS = 10000; -const COMPOSITOR_SOCKET_GRACE_MS = 5000; -// QNBS-v3: a process created in the gap between the first pgrep snapshot and its SIGKILL (or one still exiting) wouldn't be caught by a single one-shot sweep — CodeAnt review finding on PR #393. This is how long the second, verifying pass waits before re-sweeping. -const CLEANUP_RECHECK_GRACE_MS = 2000; -const FFI_PROOF_LINE = 'rust_core ping = 424242'; -const EXPECTED_TITLE_LINE = 'title = WorldScript Studio'; -const WAYLAND_SOCKET_NAME = 'wayland-smoke-0'; - -function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -// QNBS-v3: shared by the two cleanup passes below — CEF re-execs the same binary for renderer/GPU/crashpad-handler subprocesses, none of which carry WAYLAND_SOCKET_NAME in their own command line (it's passed via env var, not argv), so a name-based pkill never matched them — CodeAnt review finding on PR #393. Same pattern run-launch-cycle-proof.mjs uses for the identical problem. -function sweepMatchingProcesses() { - try { - const out = execFileSync('pgrep', ['-f', `^${binaryPath}`], { - stdio: ['ignore', 'pipe', 'ignore'], - }) - .toString() - .trim(); - const pids = out.split('\n').filter(Boolean).map(Number); - for (const pid of pids) { - if (pid === process.pid) continue; - try { - process.kill(pid, 'SIGKILL'); - } catch { - // Already gone — fine. - } - } - return pids.filter((pid) => pid !== process.pid).length > 0; - } catch { - return false; // pgrep exits 1 when nothing matches — nothing to kill. - } -} - -// QNBS-v3: trusts an already-set XDG_RUNTIME_DIR as-is (GitHub Actions runners provide a real one, e.g. /run/user/) rather than chmod'ing a directory this script doesn't own — CodeRabbit review finding on PR #393. Only falls back to creating (and owning) its own via mkdtempSync, never a fixed predictable /tmp path. -function ensureXdgRuntimeDir() { - const existing = process.env.XDG_RUNTIME_DIR; - if (existing && fs.existsSync(existing)) { - return existing; - } - const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'wayland-smoke-xdg-runtime-')); - fs.chmodSync(dir, 0o700); - return dir; -} - -async function main() { - const xdgRuntimeDir = ensureXdgRuntimeDir(); - const env = { ...process.env, XDG_RUNTIME_DIR: xdgRuntimeDir }; - - // QNBS-v3: a stale socket left by a previous crashed/killed run would otherwise satisfy the existsSync check below immediately, without this Weston instance ever actually starting — CodeAnt review finding on PR #393. Removing it first means "socket exists" can only mean "this Weston instance created it". - const socketPath = path.join(xdgRuntimeDir, WAYLAND_SOCKET_NAME); - try { - fs.unlinkSync(socketPath); - } catch { - // Nothing there — the expected common case. - } - - console.log('[wayland-smoke] Starting headless Weston compositor…'); - // QNBS-v3: Weston's headless backend needs no real display/GPU — the Wayland-side equivalent of Xvfb, same reasoning as run-launch-cycle-proof.mjs uses xvfb-run for X11. - const weston = spawn( - 'weston', - ['--backend=headless-backend.so', `--socket=${WAYLAND_SOCKET_NAME}`], - { - env, - stdio: ['ignore', 'pipe', 'pipe'], - }, - ); - let westonStderr = ''; - let westonExited = false; - weston.once('exit', () => { - westonExited = true; - }); - // QNBS-v3: spawn() reports a missing/non-executable program via an async 'error' event, not a thrown exception — without a listener Node crashes with a raw ENOENT stack trace instead of this script's own FAIL diagnostic, exactly the failure mode this probe exists to report cleanly — CodeRabbit review finding on PR #393. - weston.on('error', (err) => { - westonStderr += `spawn error: ${err.message}\n`; - }); - weston.stderr.on('data', (chunk) => { - westonStderr += chunk.toString(); - }); - - const socketDeadline = Date.now() + COMPOSITOR_SOCKET_GRACE_MS; - // QNBS-v3: also bails out early if Weston itself already exited — otherwise this loop would keep polling for a socket a dead process will never create, wasting the whole grace period on a doomed wait — CodeAnt review finding on PR #393 (the same "verify the process is actually alive" half of the finding). - while (!fs.existsSync(socketPath) && !westonExited && Date.now() < socketDeadline) { - await sleep(200); - } - if (westonExited || !fs.existsSync(socketPath)) { - console.error( - `[wayland-smoke] FAIL — Weston ${westonExited ? 'exited before creating' : 'did not create'} ${socketPath} in time.`, - ); - if (westonStderr) console.error(`[wayland-smoke] Weston stderr:\n${westonStderr}`); - weston.kill('SIGKILL'); - process.exit(1); - } - console.log(`[wayland-smoke] Weston compositor socket ready: ${socketPath}`); - - console.log(`[wayland-smoke] Launching worldscript_host with --ozone-platform=wayland…`); - const child = spawn( - binaryPath, - [`--url=${url}`, '--ozone-platform=wayland', '--enable-logging=stderr', '--v=1'], - { - cwd: path.dirname(binaryPath), - stdio: ['ignore', 'pipe', 'pipe'], - env: { ...env, WAYLAND_DISPLAY: WAYLAND_SOCKET_NAME }, - }, - ); - - let stdout = ''; - let stderr = ''; - // QNBS-v3: same rationale as weston's 'error' listener above — a missing/non-executable binaryPath must surface through this script's own FAIL diagnostic, not an unhandled ENOENT crash. - child.on('error', (err) => { - stderr += `spawn error: ${err.message}\n`; - }); - child.stdout.on('data', (chunk) => { - stdout += chunk.toString(); - }); - child.stderr.on('data', (chunk) => { - stderr += chunk.toString(); - }); - - const exited = new Promise((resolve) => - child.once('exit', (code, signal) => resolve({ code, signal })), - ); - - const rendered = await Promise.race([ - (async () => { - while (!(stdout.includes(FFI_PROOF_LINE) && stdout.includes(EXPECTED_TITLE_LINE))) { - await sleep(200); - } - return true; - })(), - exited.then(() => false), - sleep(LAUNCH_GRACE_MS).then(() => false), - ]); - - weston.kill('SIGKILL'); - sweepMatchingProcesses(); - // QNBS-v3: verify-and-resweep, not a single one-shot pass — CodeAnt review finding on PR #393 (a subprocess created between the first pgrep snapshot and its kill, or one still in the middle of exiting, would otherwise survive undetected). - await sleep(CLEANUP_RECHECK_GRACE_MS); - if (sweepMatchingProcesses()) { - console.error( - '[wayland-smoke] WARNING — worldscript_host process(es) still matched after cleanup; sent a second SIGKILL sweep.', - ); - } - - if (!rendered) { - console.error( - `[wayland-smoke] FAIL — did not observe both "${FFI_PROOF_LINE}" and "${EXPECTED_TITLE_LINE}" within ${LAUNCH_GRACE_MS}ms under --ozone-platform=wayland.`, - ); - console.error(`[wayland-smoke] stdout:\n${stdout || '(empty)'}`); - console.error(`[wayland-smoke] stderr:\n${stderr || '(empty)'}`); - process.exit(1); - } - - console.log( - '[wayland-smoke] OK — worldscript_host rendered the real production bundle under a headless Weston Wayland compositor (--ozone-platform=wayland), FFI boundary and title both proven.', - ); -} - -main().catch((err) => { - console.error(`[wayland-smoke] FAIL — unexpected error: ${err.message}`); - process.exit(1); -}); From f27ebede1ae04e26ac1506df0d83e5197b2edba1 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Thu, 20 Aug 2026 04:20:58 +0200 Subject: [PATCH 2/6] docs(cef): archive obsolete CEF governance docs, relocate reusable classification Removes 14 of 15 docs/cef/ files (roadmap, risk register, competency matrix, ownership manifest, binding scorecard, knowledge-base articles) - all CEF-program-specific, git history is the archive. Relocates docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md to docs/native/UI-DOMAIN-STATE-CLASSIFICATION.md and generalizes its framing (drops CEF-wave section citations, updates its "later waves" pointers to the new Qt/GPUI roadmap's wave numbering and cites issues #357/#359/#360/#361 as the concrete R-15 gaps) - the Redux domain/UI-state classification table itself is unchanged and remains directly useful for the future Rust Core migration-priority work. Per ADR-0021. Co-Authored-By: Claude Sonnet 5 --- docs/cef/CEF-BINDING-DECISION-SCORECARD.md | 32 - docs/cef/CEF-RISK-REGISTER.md | 38 - docs/cef/CEF-RUST-COMPETENCY-MATRIX.md | 86 - docs/cef/OWNERSHIP.yaml | 217 - docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md | 5098 ----------------- docs/cef/TAURI-COUPLING-INVENTORY.md | 92 - .../cef/knowledge/binding-upgrade-playbook.md | 40 - docs/cef/knowledge/cef-architecture-primer.md | 97 - .../knowledge/cef-rust-binding-cookbook.md | 38 - .../knowledge/debugging-and-crash-playbook.md | 19 - docs/cef/knowledge/linux-runtime-notes.md | 37 - docs/cef/knowledge/subprocess-and-shutdown.md | 24 - docs/cef/knowledge/threading-and-lifetimes.md | 41 - docs/cef/tauri-coupling-inventory.json | 240 - .../UI-DOMAIN-STATE-CLASSIFICATION.md | 20 +- 15 files changed, 10 insertions(+), 6109 deletions(-) delete mode 100644 docs/cef/CEF-BINDING-DECISION-SCORECARD.md delete mode 100644 docs/cef/CEF-RISK-REGISTER.md delete mode 100644 docs/cef/CEF-RUST-COMPETENCY-MATRIX.md delete mode 100644 docs/cef/OWNERSHIP.yaml delete mode 100644 docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md delete mode 100644 docs/cef/TAURI-COUPLING-INVENTORY.md delete mode 100644 docs/cef/knowledge/binding-upgrade-playbook.md delete mode 100644 docs/cef/knowledge/cef-architecture-primer.md delete mode 100644 docs/cef/knowledge/cef-rust-binding-cookbook.md delete mode 100644 docs/cef/knowledge/debugging-and-crash-playbook.md delete mode 100644 docs/cef/knowledge/linux-runtime-notes.md delete mode 100644 docs/cef/knowledge/subprocess-and-shutdown.md delete mode 100644 docs/cef/knowledge/threading-and-lifetimes.md delete mode 100644 docs/cef/tauri-coupling-inventory.json rename docs/{cef => native}/UI-DOMAIN-STATE-CLASSIFICATION.md (70%) diff --git a/docs/cef/CEF-BINDING-DECISION-SCORECARD.md b/docs/cef/CEF-BINDING-DECISION-SCORECARD.md deleted file mode 100644 index 7295c1bc..00000000 --- a/docs/cef/CEF-BINDING-DECISION-SCORECARD.md +++ /dev/null @@ -1,32 +0,0 @@ -# CEF Integration Decision Scorecard - -Fills in the empty template from `docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md` Appendix H, per ADR-0020. Scored **only** where the Wave 2 spike produced real evidence — a blank/"not tested" cell means exactly that, not an assumed pass. - -**Decision: Option B (thin C++ CEF host + Rust core).** See `docs/adr/0020-cef-binding-choice-thin-cpp-host.md` for the full rationale and spike description. - -| Criterion | Rust binding (A) | Thin C++ host (B) | C API (C) | -|---|---|---|---| -| Linux support | not spiked | **Partial** — built and ran on one Ubuntu 22.04/X11/Xvfb machine, CEF's own officially-tested target for this version; packaged/clean-machine runtime compatibility remains unproven (§44.3) | not spiked | -| Windows support | not spiked | not spiked (Linux-only spike) | not spiked | -| macOS support | not spiked | not spiked (Linux-only spike) | not spiked | -| Sandbox | not spiked | Not exercised — spike ran with `no_sandbox=true`; roadmap §12's real sandbox posture is separate, later scope | not spiked | -| Renderer callbacks | not spiked | Partial — browser-process handlers (`CefLifeSpanHandler`/`CefDisplayHandler`) fired correctly across 3 repeated create/close cycles; renderer-process-specific callbacks (`CefRenderProcessHandler`/`CefRenderHandler`) were not implemented or tested this spike | not spiked | -| GPU/crash callbacks | not spiked | Partial — observed real GPU-fallback warnings (Bay Trail Vulkan incomplete, VAAPI 1.14 < required 1.17) with a clean software-render fallback (SwiftShader), not a crash; no deliberate crash-callback test performed yet | not spiked | -| Packaging | not spiked | not spiked (this was a build+run spike, not a packaging spike) | not spiked | -| Upstream docs | not spiked | **Good** — CEF's own `cefsimple`-pattern conventions and shipped CMake macros were directly usable with only the expected per-target additions (`ADD_LOGICAL_TARGET`, `SET_CEF_TARGET_OUT_DIR` — not auto-invoked by the minimal distribution, but documented in the macro file itself) | not spiked | -| Memory/lifetime safety | not spiked | Noted, not yet stress-tested: CEF's own shutdown sequence takes a few real seconds after SIGTERM — confirmed clean (no permanent orphans) but not instantaneous; see `docs/cef/knowledge/subprocess-and-shutdown.md` | not spiked | -| FFI complexity | n/a (no C++/Rust FFI boundary in this option) | **Proven low** — a minimal `#[no_mangle] extern "C"` Rust `staticlib`, linked via a plain `.a` path, called correctly and returned the expected value; no binding-generation tooling needed for a function this simple | n/a for this spike's scope | -| Maintenance | not spiked | Reasonable expectation, not yet tested across a version bump — the spike used one pinned CEF version only | not spiked | -| CI complexity | not spiked | Not yet run in CI. Real, evidence-based local-dev finding: linking against `libcef.so`/`libcef_dll_wrapper.a` needed `ld.gold` + reduced optimization flags + full process detachment from this session's own tooling to complete reliably on constrained (~3.7GB RAM) hardware — a CI runner with more memory may not need any of this, but it should be verified, not assumed | not spiked | -| Rust-core fit | not spiked | **Proven** — the FFI boundary this option's whole premise depends on (C++ owns CEF only, Rust owns logic) works, verified in isolation from CEF's own process complexity | not spiked | - -## What this scorecard does *not* claim - -- Cross-platform parity (Windows/macOS) — zero evidence either way; this was a Linux-only spike matching the roadmap's own Wave 2 exit criterion ("renders reliably... on Linux development systems"). -- Sandbox viability — deliberately disabled for this spike; a real security-model evaluation is separate, later scope (roadmap §12, Wave 3+). -- Production packaging, updater, or crash-reporting pipeline integration — none of these were exercised. -- A verdict on Options A or C's actual viability — they were not spiked in parallel; see ADR-0020's "why rejected" section for the reasoning behind not spiking them given B's clean result. - -## Re-scoring triggers - -Re-score this table (not just append to it) when: Windows/macOS support is spiked, sandbox mode is enabled and tested, a second CEF version is tried (to exercise "maintenance"), or this moves into real CI (to score "CI complexity" honestly instead of narratively). diff --git a/docs/cef/CEF-RISK-REGISTER.md b/docs/cef/CEF-RISK-REGISTER.md deleted file mode 100644 index 22a35c5a..00000000 --- a/docs/cef/CEF-RISK-REGISTER.md +++ /dev/null @@ -1,38 +0,0 @@ -# CEF Desktop Migration — Risk Register - -**Companion to:** [`ROADMAP-CEF-DESKTOP-MIGRATION.md`](ROADMAP-CEF-DESKTOP-MIGRATION.md) §76 · [ADR-0019](../adr/0019-cef-desktop-runtime-strategy.md) -**Kept as a separate document** (per roadmap §76) so it can be updated per-wave without editing the large roadmap file. -**Established:** Wave 0, 2026-08-18. - -Every P0/P1 risk below must have an owner, a test, and an exit condition before the wave that would otherwise be blocked by it begins. `Status` values: `OPEN`, `MITIGATING`, `ACCEPTED_RISK`, `CLOSED`. - -| ID | Risk | Severity | Status | Owner | Mitigation | Exit condition | -|---|---|---|---|---|---|---| -| R-01 | Data migration corruption (Tauri→CEF) | Critical | OPEN | *unassigned* | Backup, journal, idempotence, failure injection | Migration rehearsal passes on all fixture classes (§54) with interrupted-migration recovery proven | -| R-02 | Updater defect (bad signature, corrupt download, failed rollback) | Critical | OPEN | *unassigned* | Signatures, staged rollout, rollback rehearsal | RC rehearsal (§63) passes all failure-injection scenarios (§53) | -| R-03 | Unrestricted/unvalidated IPC surface | Critical | OPEN | *unassigned* | Typed allowlist, schema validation, fuzzing | Bridge contract tests (§52) pass; unknown methods rejected | -| R-04 | CEF packaging complexity across 3 platforms | High | OPEN | *unassigned* | Incremental cross-platform CI | Wave 12 packaged artifacts install/launch/uninstall clean on all 3 platforms | -| R-05 | Chromium security patch cadence falls behind upstream | High | OPEN | *unassigned* | Automated monitoring + emergency update lane (§34.1) | Security-SLA dashboard green, no overdue exception (Appendix M.1) | -| R-06 | CEF/Rust/C++ lifetime defects (UB, use-after-free, shutdown races) | High | MITIGATING | cef-runtime (backup: rust-core) | Minimal wrapper surface, competency harness, dual-review (§4.11.4). Real evidence now exists: `scripts/cef/run-launch-cycle-proof.mjs` (3/3 repeated start/close cycles, `cef-learning-harness` CI job, PR #388+); a real callback-lifetime bug found and fixed (`base::Unretained` vs. a plain `CefTask`, `docs/cef/knowledge/threading-and-lifetimes.md`, PR #390). | This row's own exit condition is met for the scope it covers — reassessed 2026-08-19 (Wave 2 in progress, not deferred to a later wave per this register's own "assign before the corresponding wave begins" rule). **Not CLOSED**: IO-thread and render-process-side lifetime rules, and async-cancellation patterns, remain untouched (`CEF-RUST-COMPETENCY-MATRIX.md`'s threading domain is "Partial", not complete) — the risk is real-mitigated for the covered surface, not retired. | -| R-07 | Memory regression vs. current baseline | High | OPEN | *unassigned* | Per-process attribution (§28), soak tests, numeric budgets | Wave 15 soak passes, no unbounded growth over 2h | -| R-08 | GPU process instability (Wayland/X11/driver-specific) | High | OPEN | *unassigned* | Compatibility matrix (Appendix A.3), recovery path (§30) | Field matrix (§64) passes on NVIDIA/AMD/Intel × Wayland/X11 | -| R-09 | Low-end/resource-constrained device regression | High | OPEN | *unassigned* | L1/L2 budgets, resource-admission layer (§44.6.3) | Low-end qualification gate (§44.6.7) passes | -| R-10 | Documentation drift (stale Tier-A docs more dangerous than missing ones) | High | MITIGATING | *unassigned* | Ownership manifest (`OWNERSHIP.yaml`) established Wave 0. Reconsidered at Wave 1 per this row's own exit condition: `docs:cef-check` remains deferred — Wave 1 produces TS contracts, not CEF/native code, so there is still nothing real for a drift-check to check drift against (same rationale `OWNERSHIP.yaml` already documents). Re-deferred to Wave 2, the first wave with actual CEF host code. | `docs:cef-check` implemented and green in CI | -| R-11 | Feature-parity drift between Tauri and CEF during transition | High | OPEN | *unassigned* | Machine-readable parity ledger (§36 table + `tauri-coupling-inventory.json`) | Tauri retirement gate (§72) — full parity table PASS | -| R-12 | Two-runtime maintenance burden destabilizes both | High | OPEN | *unassigned* | Short-lived parity period, Tauri feature freeze (§69) | CEF Stable cutover (Wave 19) | -| R-13 | Accessibility regression under CEF vs. current web/Tauri baseline | High | MITIGATING | cef-runtime (backup: desktop-architecture) | Early integration spike (§23.1) + deep certification wave (Wave 16). Real evidence now exists: `CefBrowserHost::SetAccessibilityState(STATE_ENABLED)` proven in CI, 3/3 cycles, zero regression to FFI/rendering/crash-reporting/Wayland proofs (PR #397, after a real first-attempt failure correctly root-caused and reverted rather than left half-working — PR #391). | Wave 16 exit criteria pass — **not yet met**, reassessed 2026-08-19 only to record that the §23.1 spike half is real, not to claim Wave 16 certification. Platform accessibility-tree observability (AT-SPI) remains genuinely untouched — state *enablement* is proven, the tree itself is not, see `cef-architecture-primer.md`'s "Accessibility API" section. | -| R-14 | Oversized bundled-Chromium footprint hurts low-end adoption | Medium/High | OPEN | *unassigned* | Low-end benchmark, lazy startup | Bake-off (§25) shows acceptable cold-start delta vs. Tauri on L1 | -| **R-15** | **Desktop project-text-at-rest encryption gap** — Tauri filesystem-backed project stores (`services/fs/*Store.ts`) are not encrypted at rest; only the browser/PWA IndexedDB path is (ADR-0018/B-1). Formerly PR #356's scope; PR #363 (merged, v1.27.1) did not cover this — it addressed atomic writes and API-key routing only. | **High** | **OPEN** | *unassigned* | Rebuild on renderer-neutral `worldscript-crypto` (§20) with migration journal, admission lock, AAD, binary-asset coverage, recovery — same rigor as ADR-0018's IDB path. Do not patch the stale PR #356 implementation into the current architecture. | Wave 7 exit: desktop security claims truthful and tested (roadmap §71 Security gate) | -| R-16 | Desktop credential storage remains OS-filesystem-based, not platform-keychain | Medium | OPEN | *unassigned* | PR #363 already fixed the immediate secret-material flaw (fail-closed routing, legacy key discard); full Keychain/Credential-Manager/Secret-Service integration deferred | Wave 7 exit: `worldscript-crypto`/credential storage matches §21 hierarchy | -| R-17 | Rust/Tauri CI gate (ex-PR #353) content is lost if closed without extraction | Low | MITIGATING | *unassigned* | Confirmed superseded by PR #363's shipped "🦀 Tauri Rust Gate" (verified passing on `main` as of 2026-08-18); diff before close (§65) | #353 closed with cited delta-check; nothing unique left unmerged | -| R-18 | Atomic-writes correctness delta (ex-PR #354) lost if closed without extraction | Low | MITIGATING | *unassigned* | Confirmed same scope as PR #363's shipped atomic-write work; diff before close (§65) | #354 closed with cited delta-check; nothing unique left unmerged | - -## Provenance - -R-15–R-18 were derived directly from the Wave 0 PR reconciliation (roadmap §65), which is itself based on verified `gh pr view`/`gh pr list` output against `qnbs/WorldScript-Studio` on 2026-08-18 — not the original roadmap draft's guessed PR content. R-01–R-14 are carried over from the roadmap draft's §76 table, expanded with explicit owner/status/exit-condition columns per this register's format. - -## Review cadence - -This register should be reviewed at the exit of every Wave (roadmap §67) and whenever a new P0/P1-class finding surfaces. Owners are intentionally unassigned as of Wave 0 — assign before the corresponding wave begins, not before. - -**Wave 2 checkpoint (2026-08-19, via external review feedback on this session's own work):** R-06 and R-13 assigned real role-based owners (per `OWNERSHIP.yaml`'s established role taxonomy, roadmap §80.1.8 — role/subsystem ownership, not a named individual) and moved `OPEN` → `MITIGATING` with linked evidence, since their Wave-2-scoped mitigations genuinely exist now (learning harness, accessibility state enablement) — leaving them `*unassigned*`/`OPEN` had drifted behind the real implementation. The remaining rows (R-01–R-05, R-07–R-12, R-14) correctly stay `*unassigned*` per this section's own rule — their corresponding waves (5, 4, 12, 15, 16 field-matrix, etc.) have not begun. This is not a one-time fix: re-check at every future Wave exit, the same way this gap was caught. diff --git a/docs/cef/CEF-RUST-COMPETENCY-MATRIX.md b/docs/cef/CEF-RUST-COMPETENCY-MATRIX.md deleted file mode 100644 index 9a5ebaa5..00000000 --- a/docs/cef/CEF-RUST-COMPETENCY-MATRIX.md +++ /dev/null @@ -1,86 +0,0 @@ -# CEF/Rust Competency Matrix - -**Companion to:** [`ROADMAP-CEF-DESKTOP-MIGRATION.md`](ROADMAP-CEF-DESKTOP-MIGRATION.md) §4.11, §61.1, Appendix A.1 · [ADR-0019](../adr/0019-cef-desktop-runtime-strategy.md) -**Established:** Wave 0, 2026-08-18. **Baseline was: nothing done yet.** Updated in place, 2026-08-18/19 (Wave 2, ADR-0020 spike + PR #386/#387/#388/#391/#392/#393/#397/#400/#402), per this doc's own "Update discipline" below — items flip to `true` only with a linked evidence commit, in the same commit as the flip. This file exists so future waves have a live, gradeable target instead of re-deriving the checklist from the roadmap prose each time. - -This is an engineering gate (roadmap §4.11.6), not a training checklist. `WS-CEF-IPC` (Wave 4) and any production storage capability exposing privileged native operations may not proceed until the relevant items below are `true` with linked evidence. - -## Machine-readable manifest (roadmap §61.1.3 shape) - -```yaml -cef_competency: - binding_model_documented: true # docs/adr/0020-cef-binding-choice-thin-cpp-host.md - lifetime_model_reviewed: true # docs/cef/knowledge/threading-and-lifetimes.md (PR #390) — UI-thread callbacks + ref-counting/callback-lifetime; IO thread and async cancellation still untouched - repeated_shutdown_ci: true # scripts/cef/run-launch-cycle-proof.mjs, cef-learning-harness CI job (PR #388) - renderer_crash_ci: true # chrome://crash + OnRenderProcessTerminated + browser-process survival, cef-learning-harness CI job (PR #392) - sandbox_smoke: false # feasibility-only (PR #402): unshare --user --pid --fork functionally succeeds on the CI runner (kernel 6.17, unprivileged_userns_clone=1) — real evidence the sandbox is reachable, not a sandbox-enable attempt. Stays false until a follow-up PR proves browser/renderer/GPU processes actually run sandboxed (per-process status, not just "launched without complaining"), with zero regression to existing proofs — see cef-architecture-primer.md's "Sandbox configuration" section for the full acceptance bar - accessibility_smoke: false # state ENABLEMENT is proven (PR #397, SetAccessibilityState, 3/3 CI cycles) — this field is specifically about the platform accessibility tree being observable, which needs OS-level AT-SPI introspection and was not attempted - crash_symbolization_smoke: true # PR #400 — a self-induced crash inside our own code (rust-core's worldscript_rust_debug_crash_self_test, --debug-crash-self) was symbolized end-to-end via dump_syms + minidump-stackwalk (both standalone Rust tools, no Chromium checkout needed — that earlier assumption was wrong, see cef-architecture-primer.md). Chromium/CEF-internal frames remain unsymbolized — no distribution type ships a separate debug-symbols archive (verified against cef-builds.spotifycdn.com/index.json) — so this is honestly scoped to our own code, not the whole stack. -``` - -CI validation of this block ("fail CI when a required item for the active program phase is absent or false") is not yet implemented — this manifest is hand-maintained for now, matching every `driftCheckTool: "planned — not implemented"` entry in `OWNERSHIP.yaml`. - -## Required competency domains (roadmap §4.11.1) - -| Domain | Status | Evidence | -|---|---|---| -| CEF architecture (process model, browser/frame/client ownership, message loop, shutdown ordering, subprocess packaging, sandbox expectations) | Partial | Process model, message loop, and shutdown ordering all have real working code + CI proof (`apps/desktop-cef/`, PR #388), now written up in `docs/cef/knowledge/cef-architecture-primer.md` (PR #390, no longer a skeleton). Subprocess *resource layout* (unpackaged CEF build output — `COPY_FILES`) is confirmed, but real shipped/installer packaging is separate, unproven, later scope. Sandbox expectations still have zero evidence. | -| CEF threading & lifetime rules (UI-thread callbacks, IO thread, ref-counted objects, callback lifetime, async cancellation, shutdown races) | Partial | `CEF_REQUIRE_UI_THREAD()` used throughout; `IMPLEMENT_REFCOUNTING`/`CefRefPtr` applied correctly; a real callback-lifetime lesson learned and fixed (`base::Unretained` vs. a plain `CefTask` — see `apps/desktop-cef/src/worldscript_handler.cpp`), now written up in `docs/cef/knowledge/threading-and-lifetimes.md` (PR #390, no longer a skeleton). IO thread, render-process-side code, and async-cancellation patterns remain untouched. | -| Rust binding layer (crate/version, unsafe/FFI boundary, wrapper ownership, API coverage gaps, upgrade procedure) | Partial | `apps/desktop-cef/rust-core/` (`worldscript_rust_core`, Corrosion-linked) — FFI boundary proven inside the real CEF host in CI (PR #388), not just an isolated test. Upgrade procedure written proactively (PR #402, `docs/cef/knowledge/binding-upgrade-playbook.md` — a real 15-step executable procedure, not a skeleton, but not yet exercised against a real upgrade); API coverage is currently one trivial function, not representative of real surface area. | -| Cross-platform native host (Linux loader/resource layout, Windows process/installer/sandbox, macOS bundle/signing, window lifecycle, high-DPI, IME/a11y) | Partial (Linux only) | Linux loader/resource layout confirmed via a real filesystem listing in CI (`docs/cef/knowledge/linux-runtime-notes.md`); a real cwd-relative-path startup bug found and fixed. Zero Windows/macOS evidence. Window lifecycle proven for open/close only. Accessibility: state enablement proven (PR #397), tree observability (AT-SPI) and IME both still untouched. High-DPI untouched. | -| Operational CEF (crash reporting, symbol handling, version-update automation, sandbox verification, packaging deps, runtime diagnostics) | Partial | Packaging deps: `scripts/cef/check-linux-runtime-deps.mjs` (dpkg package presence) + `scripts/cef/check-linux-runtime-linkage.mjs` (PR #395 — real `ldd` against the CI-built runtime artifacts `worldscript_host` and `libcef.so`, both fully resolved on the CI runner), both CI-run. Runtime diagnostics: `scripts/cef/print-cef-version-diagnostics.mjs` + verbose CEF logging (`--enable-logging=stderr --v=1`) added mid-debugging this wave. Crash reporting: proven in CI (PR #392) — `crash_reporter.cfg` + `CefCrashReportingEnabled()` + a deliberately induced renderer crash (`chrome://crash`) produced a real Crashpad `.dmp` file (the harness's actual assertion) under an overridden `BREAKPAD_DUMP_LOCATION`, alongside Crashpad's own `.meta`/`settings.dat` housekeeping files (observed, not independently asserted); the browser process survived. Symbol handling: also proven now (PR #400) — the initial assumption that decoding a dump needs a full Chromium source checkout was wrong for *our own* code's frames; `dump_syms`/`minidump-stackwalk` (both standalone Rust projects, prebuilt Linux binaries, no Chromium checkout) resolved a self-induced browser-process crash (`--debug-crash-self`) end-to-end back to the crashing Rust function's name. Chromium/CEF-internal frames (e.g. the `chrome://crash` renderer crash above) remain genuinely unsymbolized — CEF's official builds ship no separate debug-symbols archive for any distribution type (verified against `cef-builds.spotifycdn.com/index.json`). Version-update automation and sandbox verification remain not started. | - -## Appendix A.1 checklist (live) - -```text -[x] Process architecture understood/documented — PR #390, docs/cef/knowledge/cef-architecture-primer.md's "Process model" section (real, CI-proven, PR #388 evidence) -[x] Message loop decision documented — PR #390, cef-architecture-primer.md's "Message-loop choice and why" section -[x] Thread affinity documented — PR #390, docs/cef/knowledge/threading-and-lifetimes.md's "Thread map" section (UI-thread callbacks only; IO thread and render-process-side code remain untouched) -[x] CEF reference-count/lifetime rules documented — PR #390, threading-and-lifetimes.md (IMPLEMENT_REFCOUNTING/CefRefPtr usage + a real callback-lifetime bug found and fixed) -[ ] Rust binding unsafe surface reviewed (trivial surface so far, not representative) -[ ] Binding API gaps catalogued -[x] Unpackaged CEF resource layout proven — PR #388, real filesystem listing in CI (real shipped/installer packaging remains separate, unproven, later scope) -[x] Repeated startup/shutdown harness green — PR #388, 3/3 cycles, cef-learning-harness CI job -[x] Renderer crash observation green — PR #392, chrome://crash + OnRenderProcessTerminated (TS_PROCESS_CRASHED), browser process survived, cef-learning-harness CI job -[ ] Accessibility smoke green (state enablement proven, PR #397, 3/3 CI cycles, zero regression; tree observability — AT-SPI introspection — still open, see cef-architecture-primer.md's "Accessibility API" section) -[x] Crash-reporting/symbolization smoke green — PR #392 (crash reporting: real Crashpad dump produced in CI) + PR #400 (symbolization: a self-induced crash in our own code resolved end-to-end via dump_syms + minidump-stackwalk); Chromium/CEF-internal frames remain unsymbolized — see cef-architecture-primer.md -[x] Linux dependency inventory (Wave 2 scope) complete — PR #395: package presence + real `ldd` against the CI-built runtime artifacts (`worldscript_host`, `libcef.so`), both fully resolved on the CI runner — matches this project's own established convention (see the X11/Wayland item below: proven on what Wave 2 actually needs, one CI runner, not blocked pending a broader matrix). Previously conflated with the separate item below; split out 2026-08-19 per the same "two separate gates" distinction just documented for sandbox. -[ ] Linux packaged-installer dependency declaration (multi-distro compatibility contract for an eventual real installer) — separate, later packaging-wave scope, not Wave 2; zero evidence, correctly unchecked; see native-readiness.md -[x] X11/Wayland initial smoke complete — PR #393: X11 proven since PR #388 (Xvfb); Wayland now also proven (headless Weston compositor, --ozone-platform=wayland, same FFI+title checks, cef-learning-harness CI job). Real-hardware/compositor matrix (roadmap §44.2/§44.5 — NVIDIA/AMD/Intel × KDE/GNOME, real graphics hardware) remains unproven; this is one virtual-CI runner only. -[x] Upgrade playbook written — PR #402: docs/cef/knowledge/binding-upgrade-playbook.md written proactively (a 15-step executable procedure mapping to real scripts/CI steps from Wave 2's own proof work), resolving the circular dependency where the gate required a playbook that could only be written after the first upgrade it was meant to gate. To be enriched with real lessons after the first actual upgrade — not yet exercised for real, honestly noted in the doc itself. -[ ] External-expertise escalation path documented -``` - -## CEF competency gate (roadmap §4.11.6, blocks `WS-CEF-IPC`) - -```text -[x] CEF process architecture documented — PR #390, docs/cef/knowledge/cef-architecture-primer.md (sandbox expectations still unproven — see domains table) -[x] chosen Rust/C++ integration model documented — docs/adr/0020-cef-binding-choice-thin-cpp-host.md -[x] binding version pinned — scripts/cef/cef-version.json -[x] unsafe/FFI boundary identified — apps/desktop-cef/rust-core/, proven in CI (PR #388) -[x] threading/lifetime map reviewed — PR #390, docs/cef/knowledge/threading-and-lifetimes.md (IO thread/async-cancellation still untouched — see domains table) -[x] clean repeated startup/shutdown proven — PR #388, 3/3 cycles, cef-learning-harness CI job -[x] renderer termination observed and handled — PR #392, chrome://crash deliberately crashes the renderer, OnRenderProcessTerminated fires, browser process/message loop survive, cef-learning-harness CI job -[x] sandbox development plan validated — PR #402: CEF has no Linux sandbox API (confirmed against docs/sandbox_setup.md); real functional feasibility test (unshare --user --pid --fork) succeeds on the CI runner; explicit acceptance bar documented for the follow-up enable attempt (cef-architecture-primer.md's "Sandbox configuration" section) — a validated plan, not yet the sandbox itself (sandbox_smoke stays false) -[x] Linux runtime dependencies inventoried (Wave 2 scope) — PR #395: package presence + real `ldd` against the CI-built runtime artifacts (`worldscript_host`, `libcef.so`), both fully resolved on the CI runner. Split 2026-08-19 from the packaged-installer item below — conflating Wave 2's own inventory goal with later packaging-wave scope was the same "two separate gates" issue just resolved for sandbox. -[ ] Linux packaged-installer dependency declaration (multi-distro compatibility contract for an eventual real installer — separate, later packaging-wave scope, not Wave 2; zero evidence, correctly unchecked; see native-readiness.md) -[ ] at least one accessibility smoke test performed (state enablement proven, PR #397, 3/3 CI cycles, zero regression; tree observability — AT-SPI introspection — still open, see cef-architecture-primer.md's "Accessibility API" section) -[x] at least one crash-reporting/symbolization path proven — PR #392: crash-reporting path proven end-to-end (real Crashpad dump produced in CI); PR #400: symbolization also proven — a self-induced crash in our own code resolved end-to-end via dump_syms + minidump-stackwalk (Chromium/CEF-internal frames remain unsymbolized, honestly scoped) -[x] upgrade playbook exists — PR #402: docs/cef/knowledge/binding-upgrade-playbook.md, a real 15-step executable procedure, not a skeleton — see the Appendix A.1 entry above for the full rationale -``` - -This gate is **not** satisfied yet — 11 of 13 items checked (13, not 12 — the Linux dependency item was split into a Wave-2-scoped half now checked and a separate packaged-installer half, see below), several with explicit caveats above. `WS-CEF-IPC` (Wave 4) remains blocked. - -## What this snapshot (Wave 2, 2026-08-18/19) does NOT claim - -Superseding the original "Wave 0 does not claim" list, now that some of those items are no longer true: - -- The CEF integration approach **has** been selected (Option B, ADR-0020) — this is no longer an open item. -- A learning harness **does** exist and runs in CI (`cef-learning-harness`, PR #388) — this is no longer an open item. -- Still true: no external-expertise engagement has been triggered — none of the escalation criteria (roadmap §4.11.5) have occurred. -- Still true, and still the most important caveat: this is Linux-only (X11 proven since PR #388, Wayland smoke also proven PR #393 — but one virtual CI runner, no real GPU/hardware matrix), `no_sandbox=true` throughout (though PR #402 validated real feasibility for the follow-up enable attempt — see the domains table), and no accessibility-tree observability (state *enablement* is proven, PR #397 — its own real root cause was found rather than staying blocked, but the tree itself is still unverified). Crash symbolization is now proven for our own code's frames (PR #400) but not for Chromium/CEF-internal ones. The upgrade playbook is no longer a skeleton either (PR #402, written proactively rather than waiting for a real upgrade — see the domains table). The competency gate above is explicitly **not** satisfied. -- This matrix will continue to be updated in place as each item is genuinely satisfied, with a link to the proving test/CI job/doc (roadmap §61.1.4 evidence-link pattern) — not marked done on intention alone. - -## Update discipline - -When an item flips to `true`/checked, add the evidence link in the same commit (test path, CI job name, or doc section). An unchecked-to-checked change without linked evidence should be rejected in review — this matrix exists specifically to prevent false "done" claims (roadmap §4.11.6). diff --git a/docs/cef/OWNERSHIP.yaml b/docs/cef/OWNERSHIP.yaml deleted file mode 100644 index faeaf98a..00000000 --- a/docs/cef/OWNERSHIP.yaml +++ /dev/null @@ -1,217 +0,0 @@ -# CEF documentation ownership manifest -# -# Per ROADMAP-CEF-DESKTOP-MIGRATION.md §80.1 / Appendix M.3. -# Tier A = release/security-critical (90-day review cadence). -# Tier B = architecture/reference (180-day review cadence). -# Tier C = explanatory/how-to (as-needed). -# -# `driftCheckTool` for every entry below is intentionally "planned — not implemented" -# as of Wave 0 (see roadmap §80.1.3): building a docs:cef-check stub now would add an -# npm script and CI job with nothing real to check drift against, since no CEF code -# exists yet. Revisit at Wave 1, not silently forgotten. - -documents: - - path: docs/adr/0019-cef-desktop-runtime-strategy.md - tier: A - owner_role: desktop-architecture - backup_role: cef-runtime - last_verified: - worldscript: "v1.27.1" - cef: "not applicable — pre-CEF-selection" - review_days: 90 - related_ci: [] - driftCheckTool: "planned — not implemented, see Wave 1" - - - path: docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md - tier: A - owner_role: desktop-architecture - backup_role: cef-runtime - last_verified: - worldscript: "v1.27.1" - cef: "not applicable — pre-CEF-selection" - review_days: 90 - related_ci: [] - driftCheckTool: "planned — not implemented, see Wave 1" - - - path: docs/cef/CEF-RISK-REGISTER.md - tier: A - owner_role: desktop-architecture - backup_role: desktop-security - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 90 - related_ci: [] - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Wave 2 checkpoint (2026-08-19, external review feedback): R-06 (CEF/Rust/C++ lifetime defects) and R-13 (accessibility regression) had drifted — real Wave 2 implementation/CI evidence existed for both while they stayed *unassigned*/OPEN, past the point this register's own rule allows ('assign before the corresponding wave begins, not before' — Wave 2 has begun). Both assigned real role-based owners and moved to MITIGATING with linked evidence; the remaining rows correctly stay unassigned/OPEN since their waves haven't begun." - - - path: docs/cef/CEF-RUST-COMPETENCY-MATRIX.md - tier: A - owner_role: cef-runtime - backup_role: rust-core - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 90 - related_ci: - - cef-learning-harness - # cef-competency-gate: no such CI workflow/job exists yet — planned, not implemented (CodeRabbit review finding on PR #389). Re-add once it's a real job. - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Updated in place for Wave 2 (PR #386/#387/#388/#391/#392/#393/#397/#400/#402) — 5 of 7 cef_competency items now true with linked evidence (lifetime_model_reviewed, renderer_crash_ci, crash_symbolization_smoke added); doc-sync fix flipped 2 more Appendix A.1/gate items to checked once cef-architecture-primer.md/threading-and-lifetimes.md were noticed to already have real content from PR #390 (previously left unchecked on a stale 'no dedicated doc yet' annotation); Wayland smoke checked in Appendix A.1 (PR #393); accessibility_smoke stays false (PR #397 proved state enablement only, not tree observability); crash_symbolization_smoke flipped true (PR #400 — self-induced crash in our own code resolved end-to-end via dump_syms + minidump-stackwalk, neither tool needs a Chromium checkout despite the earlier assumption; Chromium-internal frames remain genuinely unsymbolized, no distribution ships debug symbols); PR #402: sandbox development plan validated (real feasibility test succeeds on the CI runner, explicit acceptance bar documented — sandbox_smoke itself stays false); the Linux-dependency gate item split into a Wave-2-scoped half (now checked — PR #395's inventory+ldd work was already complete, just conflated with a different later goal) and a separate packaged-installer half (stays unchecked, correctly later scope); and binding-upgrade-playbook.md written proactively with a real 15-step executable procedure (resolving a circular dependency — the gate required a playbook that could previously only be written after the first upgrade it was meant to gate), so 'upgrade playbook exists'/'Upgrade playbook written' both flip to checked — competency gate still not satisfied (11/13, was 8/12 before the split; sandbox_smoke, accessibility-tree observability, Linux packaged-installer declaration remain open)." - - - path: docs/cef/TAURI-COUPLING-INVENTORY.md - tier: B - owner_role: desktop-architecture - backup_role: rust-core - last_verified: - worldscript: "v1.27.1" - cef: "not applicable" - review_days: 180 - related_ci: [] - driftCheckTool: "planned — not implemented, see Wave 1" - - - path: docs/adr/0020-cef-binding-choice-thin-cpp-host.md - tier: A - owner_role: cef-runtime - backup_role: desktop-architecture - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 90 - related_ci: [] - driftCheckTool: "planned — not implemented, see Wave 1" - - - path: docs/cef/CEF-BINDING-DECISION-SCORECARD.md - tier: A - owner_role: cef-runtime - backup_role: desktop-architecture - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 90 - related_ci: - - cef-learning-harness - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Appendix H scorecard, filled in per ADR-0020 — re-score triggers are listed in the doc itself, not just the review_days cadence." - - - path: docs/cef/knowledge/cef-architecture-primer.md - tier: A - owner_role: cef-runtime - backup_role: rust-core - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 90 - related_ci: - - cef-learning-harness - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Real evidence from PR #388 for process model, message loop, subprocess packaging; crash reporting proven in CI (PR #392); Wayland display-server smoke proven in CI (PR #393); accessibility state enablement proven in CI, zero regression (PR #397); crash symbolization proven for our own code's frames (PR #400) — Chromium/CEF-internal frames remain unsymbolized, honestly (no distribution ships debug symbols, verified against CEF's own build index). Sandbox config, a real GPU/compositor matrix, accessibility-tree observability (AT-SPI), and a directly-observed process-tree snapshot remain open." - - - path: docs/cef/knowledge/cef-rust-binding-cookbook.md - tier: A - owner_role: rust-core - backup_role: cef-runtime - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 90 - related_ci: - - cef-learning-harness - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Real evidence from apps/desktop-cef/rust-core/ (PR #388) — but the FFI surface is one trivial function, not representative of real API coverage." - - - path: docs/cef/knowledge/threading-and-lifetimes.md - tier: A - owner_role: cef-runtime - backup_role: rust-core - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 90 - related_ci: - - cef-learning-harness - # cef-competency-gate: no such CI workflow/job exists yet — planned, not implemented (CodeRabbit review finding on PR #389). Re-add once it's a real job. - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Real evidence from PR #388 for UI-thread callback discipline and CEF ref-counting/callback-lifetime patterns. IO-thread and render-process-side rules untouched; full dual-review (§4.11.4) not yet done." - - - path: docs/cef/knowledge/subprocess-and-shutdown.md - tier: A - owner_role: cef-runtime - backup_role: rust-core - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 90 - related_ci: - - cef-learning-harness - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Real, CI-run evidence (PR #388) — test (scripts/cef/run-launch-cycle-proof.mjs) -> CI job (cef-learning-harness) -> doc, per §61.1.4. Save-coordinator/window-state steps remain unimplemented Wave 5+ scope." - - - path: docs/cef/knowledge/linux-runtime-notes.md - tier: A - owner_role: cef-runtime - backup_role: desktop-architecture - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 90 - related_ci: - - cef-learning-harness - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Dev-machine spike (ADR-0020) plus real CI-run evidence (PR #388, GitHub Actions ubuntu-latest) — two machines now, one GPU config each, X11 only; not a compatibility contract yet." - - - path: docs/cef/knowledge/debugging-and-crash-playbook.md - tier: A - owner_role: desktop-security - backup_role: cef-runtime - last_verified: null - review_days: 90 - related_ci: [] - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Skeleton only — Status: Not started" - - - path: docs/cef/knowledge/binding-upgrade-playbook.md - tier: A - owner_role: rust-core - backup_role: cef-runtime - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 90 - related_ci: - - cef-learning-harness - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Written proactively (PR #402), not left blank pending the first real upgrade — resolves a circular dependency (the competency gate required this doc to exist before Wave 4, but it could previously only be written after the first upgrade it was meant to gate). 15-step executable procedure mapping to real scripts/CI steps from Wave 2's own proof work (SDK pin/fetch/verify, build, lifecycle harness, sandbox, crash, symbolization, accessibility, X11, Wayland, dependency/linkage diff, docs, rollback). Not yet exercised against a real upgrade — must be enriched with what actually broke the first time one happens, honestly noted in the doc itself." - - - path: docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md - tier: B - owner_role: desktop-architecture - backup_role: cef-runtime - last_verified: - worldscript: "v1.27.1" - cef: "not applicable — pre-CEF-selection" - review_days: 180 - related_ci: [] - driftCheckTool: "planned — not implemented, see Wave 1" - - - path: docs/architecture/native-readiness.md - tier: B - owner_role: desktop-architecture - backup_role: desktop-security - last_verified: - worldscript: "v1.27.1" - cef: "151.3.18+gbeff58d+chromium-151.0.7922.138" - review_days: 180 - related_ci: - - cef-learning-harness - driftCheckTool: "planned — not implemented, see Wave 1" - note: "Living scorecard — re-score at every architecture-changing PR and wave exit (§7.4.5), not just on a review-day cadence." - -# Owner/backup roles above are role placeholders (roadmap §80.1.8: prefer role/subsystem -# ownership over one person's name, e.g. via CODEOWNERS mapping). Assigning real -# maintainer handles to each role is Wave 1+ operational work, not a Wave 0 blocker. -roles: - desktop-architecture: "unassigned — maps to whoever owns ADR-0019 and the roadmap" - cef-runtime: "unassigned — maps to whoever implements the CEF host (Wave 2+)" - rust-core: "unassigned — maps to whoever owns worldscript-core/-storage/-crypto" - desktop-security: "unassigned — maps to whoever owns the CEF security review (Wave 14)" diff --git a/docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md b/docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md deleted file mode 100644 index b168810b..00000000 --- a/docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md +++ /dev/null @@ -1,5098 +0,0 @@ -# WorldScript Studio — CEF Desktop Roadmap & Realization Concept - -**Status:** Binding strategic architecture and execution roadmap -**Repository:** `qnbs/WorldScript-Studio` -**Baseline:** WorldScript Studio `v1.27.1` on current `main` -**Primary next-generation desktop runtime:** Chromium Embedded Framework (CEF) -**Current transitional desktop runtime:** Tauri 2 / system WebView -**Future secondary product line:** `egui` + `wgpu`, explicitly deferred -**Original roadmap draft:** 2026-08-15 -**Architecture refinement review:** 2026-08-17 -**Adopted as project documentation:** 2026-08-18, via [ADR-0019](../adr/0019-cef-desktop-runtime-strategy.md) (Wave 0) -**Revision:** 4 — §65 replaced with a `gh`-verified PR reconciliation (the original draft's PR-content guesses for #352–#356 have been checked against real GitHub state; dispositions for #353/#354/#355 held up, #352's guessed content did not and is corrected, #356 remains the one genuinely open gap) - -> **Provenance note (Wave 0):** This document was originally drafted as a standalone strategic proposal before being adopted as project documentation. Section numbering is preserved from that draft so later PRs can cite `§NN` stably. The only substantive content correction applied during adoption is §65 below; everything else reflects the reviewed draft as accepted by ADR-0019. Companion documents: `docs/cef/CEF-RISK-REGISTER.md`, `docs/cef/CEF-RUST-COMPETENCY-MATRIX.md`, `docs/cef/TAURI-COUPLING-INVENTORY.md`, `docs/cef/knowledge/`, `docs/cef/OWNERSHIP.yaml`. - ---- - -## 0. Executive decision - -WorldScript Studio will now pursue **Chromium Embedded Framework (CEF) as the primary next-generation desktop runtime**. - -This is not a throwaway proof of concept, not merely a Linux workaround, and not one of several competing desktop experiments. CEF is the planned successor desktop architecture that will be designed, implemented, validated, hardened, productionized, and eventually promoted to the preferred WorldScript Studio desktop runtime. - -The current Tauri implementation remains important during the transition, but its role changes: - -- **reference runtime** for current desktop behavior; -- **migration fallback** while CEF lacks parity; -- **regression comparator** for feature and lifecycle behavior; -- **temporary support path** for critical production fixes; -- **not** the target for large new architectural investments that would immediately need to be rebuilt under CEF. - -The future `egui` + `wgpu` architecture is **not cancelled**. It is intentionally gated. No production WorldScript Native effort starts until CEF and the wider product have reached the maturity, stability, security, performance, accessibility, storage, updater, recovery, and field-validation standards defined in this document. - -The strategic order is therefore fixed: - -```text -CURRENT -React + Vite + Tauri 2 - │ - ▼ -CEF architecture + platform abstraction - │ - ▼ -CEF Linux proof and #332 differential validation - │ - ▼ -CEF production implementation -Linux + Windows + macOS - │ - ▼ -CEF security / storage / updater / packaging / crash recovery - │ - ▼ -CEF feature parity and packaged-artifact validation - │ - ▼ -CEF Stable - │ - ▼ -Tauri retirement - │ - ▼ -WorldScript-wide perfection and hardening - │ - ▼ -Native-v2 admission review - │ - ▼ -ONLY THEN: separate egui/wgpu WorldScript Native product -``` - ---- - -# 1. Strategic vision - -The objective is larger than "replace WebKitGTK". - -WorldScript Studio should evolve from a web application wrapped by a desktop shell into a **renderer-independent product platform** with a durable native core and multiple presentation targets. - -Long-term target: - -```text - WORLD SCRIPT PLATFORM - │ - ┌──────────────┴──────────────┐ - │ │ - WorldScript Core Shared Contracts - Rust │ - │ │ - └──────────────┬──────────────┘ - │ - ┌─────────────┼─────────────┐ - │ │ │ - ▼ ▼ ▼ - Web/PWA CEF Desktop Future Native - React React/CEF egui/wgpu -``` - -The defining architectural rule is: - -> **Product logic must not be duplicated across renderers.** - -CEF is therefore both a desktop-runtime migration and a platform-maturation program. - -It should produce: - -- renderer-independent domain boundaries; -- renderer-independent persistence; -- renderer-independent encryption; -- renderer-independent migrations; -- renderer-independent native task orchestration; -- typed platform contracts; -- deterministic crash recovery; -- explicit lifecycle semantics; -- measurable performance SLOs; -- reproducible packaged-desktop validation; -- mature release engineering. - -CEF should make the eventual native Rust client easier to build, while **not developing that client yet**. - ---- - -# 2. Why CEF is the chosen next runtime - -CEF gives WorldScript Studio a particularly strong combination of: - -- preservation of the current React/Vite application; -- a controlled Chromium engine rather than Linux distribution WebKitGTK; -- mature DOM/CSS/text/IME/BiDi/accessibility behavior; -- mature browser profiling and debugging; -- cross-platform renderer consistency; -- process isolation; -- renderer crash detection; -- a clean path to a Rust-authoritative native core; -- reuse of the successful PWA/Chromium frontend assumptions. - -WorldScript is no longer a trivial desktop wrapper. Its surface includes: - -- a professional writing environment; -- large manuscript state; -- undo/version history; -- AI orchestration; -- local AI and model downloads; -- RAG; -- DuckDB/WASM; -- workers; -- images and Binder assets; -- advanced UI effects; -- a large i18n surface; -- accessibility requirements; -- desktop lifecycle; -- storage, encryption, backups and recovery. - -For that class of application, owning the desktop browser-engine version is strategically valuable. - ---- - -# 3. Current baseline and known pressure points - -At roadmap creation the authoritative baseline is `v1.27.1` on `main`. - -Important current characteristics: - -- React 19 / Vite 8 frontend; -- Tauri 2 desktop shell; -- substantial direct `@tauri-apps/*` dependency surface; -- browser and desktop storage paths; -- extensive CI, browser E2E, VRT, Storybook and security checks; -- recently strengthened Rust/Tauri CI gating; -- recent atomic-write and desktop persistence work; -- Linux desktop remains dependent on WebKitGTK; -- issue #332 remains the principal desktop performance/stability investigation; -- one diagnostic case produced a WebKit process footprint around 26.7 GB before WebKit killed it at its 16 GiB threshold; -- application-side memory-amplification risks exist around serialization, autosave, snapshots and snapshot listing; -- disabling motion/transparency produced a major usability improvement for the affected Linux reporter; -- the PWA remains a valuable Chromium performance/control reference; -- desktop project-data encryption remains an area requiring renderer-independent completion; -- historical Tauri PRs #352–#356 remain candidates for reconciliation, not blind merge (see §65 for the `gh`-verified disposition of each, established at Wave 0 / 2026-08-18). - -The CEF program must preserve recent reliability gains and must not reintroduce previously corrected storage, lifecycle or security defects. - ---- - -# 4. Binding program principles - -## 4.1 CEF is production infrastructure - -Every CEF decision must be evaluated against real production operation: - -- installers; -- signing; -- updates; -- rollback; -- sandbox; -- crash handling; -- long sessions; -- large projects; -- field support; -- migrations; -- accessibility; -- privacy; -- security patch cadence. - -A window that renders `dist/` is merely an early milestone. - -## 4.2 No big-bang rewrite - -The React application remains the production frontend during migration. - -Preferred progression: - -```text -current main -→ platform boundary -→ CEF bootstrap -→ vertical slices -→ packaged beta -→ parity -→ hardening -→ stable cutover -→ Tauri retirement -``` - -## 4.3 Current `main` is the only implementation baseline - -Do not build CEF on stale Tauri PR branches. - -Unique useful deltas from older PRs are reimplemented or extracted after diffing against current main. - -## 4.4 Do not reproduce Tauri coupling under a new name - -The migration target is not: - -```text -Tauri API -→ matching CEF API -``` - -It is: - -```text -Tauri-specific behavior -→ product capability -→ renderer-neutral platform/core implementation -``` - -## 4.5 Rust becomes increasingly authoritative - -Move critical desktop responsibilities out of the renderer where this materially improves correctness, security, performance or future portability: - -- persistence; -- large serialization; -- compression; -- snapshots; -- crypto; -- migrations; -- filesystem; -- tasks; -- diagnostics; -- updater state; -- large native transforms. - -## 4.6 IPC is a security boundary - -No arbitrary privileged command bridge. - -Every operation must be: - -- named; -- typed; -- validated; -- capability-scoped; -- size-limited; -- origin-aware; -- observable; -- cancellable where relevant. - -## 4.7 Measure performance - -CEF is not accepted because "Chromium should be faster". - -Benchmark: - -- PWA Chromium; -- Tauri/WebKitGTK; -- CEF Chromium. - -## 4.8 Data integrity outranks visual polish - -No renderer, GPU or performance optimization may weaken: - -- save correctness; -- migration safety; -- encryption; -- recovery; -- backups. - -## 4.9 Tauri retirement requires proof - -Tauri remains until CEF has replaced its relevant functional and operational guarantees. - -## 4.10 `egui/wgpu` remains locked - -Allowed before Native-v2 admission: - -- documentation; -- renderer-neutral core design needed for CEF; -- APIs that do not prevent a future native client. - -Not allowed: - -- native UI implementation; -- egui Writer; -- native packaging; -- second desktop feature-parity track. - ---- - -# 4.11 CEF/Rust competency and knowledge-building program - -CEF plus a deeper Rust core is deliberately ambitious. The program must therefore treat **knowledge acquisition as an engineering deliverable**, not as an informal prerequisite that is assumed to happen automatically while production code is being written. - -The objective is not to make every contributor a Chromium specialist. The objective is to ensure that the project has explicit competence in the areas where incorrect assumptions can create hard-to-debug crashes, security defects, lifetime errors, deadlocks, packaging failures, or renderer instability. - -## 4.11.1 Required competency domains - -Before the CEF host becomes a critical production dependency, the team/agent workflow must establish working knowledge of: - -### CEF architecture -- browser process vs renderer process vs GPU/utility processes; -- CEF browser/frame/client ownership; -- process callbacks; -- message-loop choices; -- shutdown ordering; -- subprocess launch and packaging; -- Chromium sandbox expectations. - -### CEF threading and lifetime rules -- UI-thread-only callbacks; -- IO-thread behavior; -- reference-counted CEF objects; -- callback lifetime; -- renderer/browser process boundaries; -- async cancellation and object invalidation; -- shutdown races. - -### Rust binding layer -- exact binding crate/version selected; -- unsafe and FFI boundaries; -- wrapper ownership model; -- supported CEF API coverage; -- missing/partial APIs; -- platform-specific gaps; -- binding upgrade procedure. - -### Cross-platform native host -- Linux loader/resource layout; -- Windows process/installer/sandbox behavior; -- macOS bundle/helper-process/signing structure; -- native window lifecycle; -- high-DPI behavior; -- input/IME/accessibility integration. - -### Operational CEF -- crash reporting; -- symbol handling; -- Chromium/CEF version updates; -- sandbox verification; -- packaging dependencies; -- runtime diagnostics; -- GPU/process troubleshooting. - ---- - -## 4.11.2 Knowledge-building phase - -Before the first production-capable native bridge, create a focused **CEF Enablement / Knowledge-Building phase** with concrete artifacts. - -Required outputs (skeletons created at Wave 0; real content arrives Wave 2+ as the corresponding competency is actually built): - -```text -docs/cef/knowledge/cef-architecture-primer.md -docs/cef/knowledge/cef-rust-binding-cookbook.md -docs/cef/knowledge/threading-and-lifetimes.md -docs/cef/knowledge/subprocess-and-shutdown.md -docs/cef/knowledge/linux-runtime-notes.md -docs/cef/knowledge/debugging-and-crash-playbook.md -docs/cef/knowledge/binding-upgrade-playbook.md -``` - -These documents should be written from the actual WorldScript integration, not copied generic CEF tutorials. - ---- - -## 4.11.3 Minimal executable learning harness - -Create a small, isolated CEF integration harness before the full WorldScript host becomes complex. - -The harness should prove: - -- browser creation; -- renderer subprocess launch; -- controlled internal content; -- JS↔native request/response; -- browser close; -- renderer crash detection; -- GPU-process observation where available; -- accessibility enablement/smoke; -- clean CEF shutdown; -- repeat launch/close cycles. - -This harness becomes both: - -1. a learning tool; and -2. a regression test for binding/CEF upgrades. - -This is Wave 2 scope — not built in Wave 0. Wave 0 only records the plan for it (§61.1 below) and the competency gate it must pass. - ---- - -## 4.11.4 Pairing and dual-review policy - -The first critical CEF milestones should not be implemented by a single unchecked path. - -Require **pairing or independent second-pass review** for: - -- CEF initialization; -- message-loop integration; -- subprocess setup; -- sandbox configuration; -- unsafe Rust/FFI; -- browser/client ownership; -- shutdown; -- IPC bridge; -- crash reporting; -- packaging/signing. - -"Pairing" may be: - -- two human engineers; -- human + specialist coding agent with explicit review pass; -- primary implementer + independent expert reviewer. - -The important invariant is **independent challenge of lifecycle and security assumptions**. - ---- - -## 4.11.5 External expertise escalation - -Budget an explicit option for external CEF/Chromium expertise for the earliest high-risk milestones. - -External review becomes strongly recommended if any of the following occur: - -- unclear binding lifetime semantics; -- production requires disabling sandbox behavior; -- repeated process shutdown hangs; -- unexplained GPU/Wayland/X11 failures; -- cross-platform subprocess packaging uncertainty; -- crash reporting cannot reliably distinguish process roles; -- binding API gaps force significant custom FFI; -- CEF upgrades repeatedly break the integration. - -External expertise should be used strategically for architecture validation and unblockers, not as permanent ownership of WorldScript business logic. - ---- - -## 4.11.6 CEF competency gate - -Before `WS-CEF-IPC` or production storage capabilities are allowed to expose privileged native operations, require: - -```text -[ ] CEF process architecture documented -[ ] chosen Rust/C++ integration model documented -[ ] binding version pinned -[ ] unsafe/FFI boundary identified -[ ] threading/lifetime map reviewed -[ ] clean repeated startup/shutdown proven -[ ] renderer termination observed and handled -[ ] sandbox development plan validated -[ ] Linux runtime dependencies inventoried -[ ] at least one accessibility smoke test performed -[ ] at least one crash-reporting/symbolization path proven -[ ] upgrade playbook exists -``` - -This is an engineering gate, not a training checkbox. Current status: not started — see `docs/cef/CEF-RUST-COMPETENCY-MATRIX.md` for the live checklist. - ---- - -# 4.12 Bus-factor and maintainability requirement - -CEF knowledge must not live in one person, one agent conversation, or one undocumented workaround. - -For every critical native subsystem maintain: - -- an architectural owner; -- an operational playbook; -- reproducible tests; -- failure examples; -- upgrade notes. - -Any CEF workaround that cannot be explained in repository documentation is temporary technical debt and must be tracked explicitly. - ---- - -# 5. Required architecture decision record - -**Status: done at Wave 0.** [ADR-0019](../adr/0019-cef-desktop-runtime-strategy.md) locks: - -1. CEF is the intended production successor to Tauri/system-WebView Desktop. -2. React/Vite remains the primary frontend during the migration. -3. Rust increasingly owns critical desktop concerns. -4. Tauri is a transition/reference/fallback runtime. -5. CEF development starts from current `main`. -6. Renderer-native integration occurs through typed contracts. -7. Renderer IPC is allowlisted. -8. `egui/wgpu` production development is forbidden before the Native-v2 admission gate. -9. New desktop functionality should avoid unnecessary Tauri-only coupling. -10. Storage, updater, diagnostics and crash recovery are first-class CEF deliverables. - ---- - -# 5.1 Language boundary — Rust/TypeScript primary, C++ narrowly conditional, Go default-NO - -The CEF program must retain language-focus discipline. - -The default architecture is: - -```text -React / TypeScript -→ presentation - -Rust -→ native/core/product capabilities - -C/C++ -→ only a thin CEF integration layer if the evidence-based integration decision requires it -``` - -**Go is not part of the CEF core plan.** - -Introducing Go now for storage, IPC, task supervision, updater orchestration or native business logic would create another toolchain and ownership boundary without a demonstrated need. - ---- - -## 5.1.1 When Go may be reconsidered - -A future Go component is admissible only if it is an **independently deployable process/tool** with a strong concrete reason. - -Potential examples, if future requirements justify them: - -- an optional standalone network/server component; -- a separately distributed operations/service daemon; -- a utility whose Go ecosystem provides a uniquely compelling operational advantage. - -Even in those cases, require all of: - -```text -[ ] separate-process boundary is natural -[ ] component can be versioned/deployed independently -[ ] Rust would impose a demonstrated material disadvantage -[ ] no duplicate WorldScript domain logic -[ ] protocol boundary is stable and documented -[ ] security/operations ownership exists -[ ] benchmark/operational evidence supports the choice -[ ] architecture review explicitly approves it -``` - -Default decision if these conditions are not met: - -```text -NO-GO for Go -``` - ---- - -## 5.1.2 Explicit non-use cases for Go - -Do not introduce Go merely to: - -- avoid learning CEF/Rust FFI; -- implement a second task supervisor; -- proxy CEF IPC; -- duplicate the updater; -- duplicate storage/crypto; -- replace a Rust utility because a contributor prefers Go; -- create an unnecessary local microservice around a desktop application. - -Language diversity must solve a real boundary problem, not dilute focus. - ---- - -# 6. Target repository shape - -The repository should evolve incrementally toward a structure similar to: - -```text -WorldScript-Studio/ -│ -├── apps/ -│ ├── web/ -│ ├── desktop-cef/ -│ └── desktop-tauri/ # transitional -│ -├── crates/ -│ ├── worldscript-core/ -│ ├── worldscript-domain/ -│ ├── worldscript-storage/ -│ ├── worldscript-crypto/ -│ ├── worldscript-tasks/ -│ ├── worldscript-diagnostics/ -│ └── worldscript-platform/ -│ -├── packages/ -│ ├── ui/ -│ ├── ai-core/ -│ ├── worker-bus/ -│ ├── collab-transport/ -│ ├── desktop-contracts/ -│ └── shared-types/ -│ -├── tests/ -│ ├── unit/ -│ ├── integration/ -│ ├── e2e/ -│ ├── desktop-cef/ -│ ├── packaged/ -│ ├── perf/ -│ └── fixtures/ -│ -└── docs/ - ├── architecture/ - ├── cef/ - ├── migration/ - └── release/ -``` - -This is a target direction, not a mandate for an immediate repository mega-reorganization. Note: `packages/` today already contains `ai-core`, `ui`, `worker-bus`, `collab-transport` — `desktop-contracts`/`shared-types` are the new additions this shape implies (Wave 1+). No `apps/` directory or `crates/` exist yet as of Wave 0. - ---- - -# 7. WorldScript Core strategy - -## 7.1 Core purpose - -The native core is the authority for behavior that should not depend on React, CEF, Tauri, browser storage or future egui. - -## 7.2 High-value early extraction - -Prioritize: - -### Persistence -- project save/load; -- settings persistence; -- active-project identity; -- autosave coordination; -- dirty state; -- save coalescing. - -### Snapshots -- metadata; -- payload storage; -- pruning; -- byte/count budgets; -- recovery. - -### Filesystem -- path resolution; -- project roots; -- atomic replacement; -- durability; -- temp cleanup; -- corruption checks. - -### Diagnostics -- process/runtime identity; -- storage health; -- migration state; -- encryption state; -- crash data. - -### Tasks -- priority; -- cancellation; -- deadline; -- retry; -- progress; -- concurrency; -- resource class. - -## 7.3 Later extraction - -Once CEF is operating: - -- crypto; -- migration orchestration; -- project domain; -- search; -- RAG; -- export; -- AI provider/local AI layers; -- collaboration support where appropriate. - ---- - -# 7.4 Native-readiness during the CEF phase — design now, implement later - -`egui/wgpu` remains implementation-locked, but **Native-readiness is a cross-cutting architectural quality attribute starting now**. - -The purpose is not to prematurely generalize every interface. The purpose is to prevent the CEF implementation from moving business rules deeper into React, browser lifecycle hooks, or CEF-only services in ways that would later force a second product rewrite. - -The rule is: - -> **Prepare interfaces and ownership boundaries for a future non-web renderer; do not build that renderer yet.** - -## 7.4.1 Three-state separation - -Every major feature should distinguish: - -### Domain/business state -Renderer-neutral and ideally core-owned. - -Examples: -- project entities; -- manuscript structure; -- semantic commands; -- snapshot metadata; -- task state; -- migration state. - -### Presentation/UI state -Renderer-specific and allowed to remain in React. - -Examples: -- open panel; -- modal visibility; -- hover; -- current tab; -- animation progress; -- temporary form focus. - -### Durable user preference state -Renderer-neutral semantics even if each frontend renders the setting differently. - -Examples: -- locale; -- reduced motion; -- autosave policy; -- AI provider selection; -- editor preferences. - -Do not put domain truth into transient React component state. - ---- - -## 7.4.2 Business-logic extraction rule - -New business rules should not be introduced inside: - -- React components; -- rendering hooks; -- CSS-driven state machines; -- CEF callback handlers. - -Prefer: - -```text -renderer event -→ typed command/use-case -→ core/domain behavior -→ renderer-neutral result/event -→ presentation -``` - -React may validate for immediate UX, but the authoritative rule remains outside the renderer. - ---- - -## 7.4.3 Renderer-free testability - -A capability is more Native-ready when its important behavior can be tested without: - -- DOM; -- React; -- CEF; -- Tauri; -- browser storage. - -For each major CEF wave, ask: - -```text -Can the core behavior be tested headlessly? -``` - -If not, determine whether the coupling is genuinely presentation-specific or accidental. - ---- - -## 7.4.4 Browser-only API quarantine - -Browser APIs that are useful for presentation remain allowed. - -But canonical product semantics should not become dependent on: - -- `document.visibilityState`; -- service worker lifecycle; -- browser IndexedDB as the only durable source; -- DOM events as business commands; -- CSS state as application state. - -Place unavoidable browser-specific behavior behind adapters. - ---- - -## 7.4.5 Native-Readiness scorecard - -At architecture-changing PRs and major CEF wave exits, record: - -| Check | PASS / N/A / DEBT | -|---|---| -| Domain logic renderer-neutral | | -| Critical behavior headless-testable | | -| Canonical data outside UI state | | -| Browser APIs adapter-contained | | -| Platform APIs adapter-contained | | -| Stable semantic commands/events | | -| No duplicate business rule in JS/native | | -| Durable schema UI-independent | | -| Error taxonomy UI-independent | | -| Task semantics UI-independent | | - -A `DEBT` result is allowed during migration, but must have an issue/owner. - ---- - -## 7.4.6 Native-readiness anti-patterns - -Reject or explicitly track: - -```text -React hook becomes authoritative storage coordinator -CEF IPC handler contains product business rules -CSS/DOM state determines durable behavior -same validation duplicated in React and Rust -project schema shaped around a single React component -native core returns presentation-specific HTML -core errors encoded as toast text -business commands expressed as DOM event names -``` - ---- - -## 7.4.7 Native-readiness gate for CEF Stable - -CEF Stable requires: - -- critical product logic demonstrably separated from presentation; -- canonical project/storage schemas renderer-independent; -- storage/crypto/migration/task APIs usable without React; -- no new critical Tauri/CEF-only business-rule islands; -- Native-Readiness scorecard with no unowned high-impact debt. - -This does **not** authorize egui implementation. It only prevents architecture debt that would make the later implementation unnecessarily destructive. - ---- - -# 8. DesktopPlatform contract - -React must progressively stop importing framework-specific desktop APIs. - -Conceptual contract: - -```ts -interface DesktopPlatform { - readonly runtime: RuntimeInfo; - - filesystem: DesktopFilesystem; - persistence: DesktopPersistence; - dialogs: DesktopDialogs; - window: DesktopWindow; - menu: DesktopMenu; - tray: DesktopTray; - notifications: DesktopNotifications; - updater: DesktopUpdater; - lifecycle: DesktopLifecycle; - tasks: DesktopTasks; - diagnostics: DesktopDiagnostics; - clipboard: DesktopClipboard; - deepLinks: DesktopDeepLinks; -} -``` - -The exact implementation may be split into modules. The requirement is architectural separation, not one giant object. Not implemented at Wave 0 — this is Wave 1 scope. - ---- - -# 9. Typed CEF ↔ Rust protocol - -Define a versioned desktop protocol. - -Conceptual request: - -```json -{ - "protocolVersion": 1, - "id": "uuid", - "method": "project.save", - "params": {} -} -``` - -Response: - -```json -{ - "protocolVersion": 1, - "id": "uuid", - "ok": true, - "result": {} -} -``` - -Structured failure: - -```json -{ - "protocolVersion": 1, - "id": "uuid", - "ok": false, - "error": { - "code": "PROJECT_SAVE_FAILED", - "message": "Safe user-facing message", - "retryable": true - } -} -``` - -Events: - -```text -task.progress -project.saved -storage.warning -renderer.recovered -update.available -update.progress -``` - -Protocol requirements: - -- request IDs; -- cancellation; -- timeout; -- progress; -- structured errors; -- capability negotiation; -- schema version; -- payload size limits; -- binary-transfer policy; -- backpressure; -- instrumentation. - -Large binary or huge project data must not automatically be base64/JSON copied through IPC. Wave 4 scope — not implemented at Wave 0. - ---- - -# 10. CEF integration implementation choice - -A constrained spike must choose between: - -## Option A — Rust CEF bindings - -Advantages: -- Rust-centric host; -- less C++ surface; -- potentially tight integration. - -Risks: -- binding maturity; -- unsafe surface; -- cross-platform parity; -- ecosystem/documentation depth. - -## Option B — thin C++ CEF host + Rust core - -Advantages: -- closest to upstream CEF examples and lifecycle model; -- mature CEF integration patterns. - -Risks: -- additional C++ toolchain; -- FFI boundary. - -Rule if selected: - -> C++ owns CEF integration only. It must not become the home of WorldScript business logic. - -## Option C — C API boundary - -Potentially stable but verbose. - -The selection spike must explicitly evaluate: - -- Linux/Windows/macOS support; -- sandbox integration; -- crash callbacks; -- packaging; -- browser/renderer lifecycle; -- automation; -- upstream cadence; -- FFI maintenance. - -Document the result in an ADR. Wave 2 scope — not decided at Wave 0. Scorecard template: Appendix H. - ---- - -# 11. Secure CEF application origin and resource loading - -Avoid a loosely privileged `file://` application. - -Prefer a controlled internal application origin or CEF resource scheme, conceptually: - -```text -worldscript://app/index.html -``` - -Requirements: - -- only bundled application resources; -- deterministic MIME handling; -- no arbitrary path traversal; -- explicit external navigation interception; -- explicit download policy; -- strict CSP; -- no accidental privilege inheritance by remote content. - -The custom scheme/resource handler must be tested against: - -- `../` traversal; -- URL-encoded traversal; -- mixed slash styles; -- null bytes; -- oversized paths; -- invalid MIME types; -- origin spoofing. - -Wave 3 scope. - ---- - -# 12. CEF security model - -The renderer should be treated as a **potentially compromised presentation process**. - -It may: - -- render UI; -- request approved native capabilities; -- receive approved project/application DTOs; -- perform browser-local presentation work. - -It may not: - -- arbitrarily read OS paths; -- execute arbitrary shell commands; -- access signing keys; -- retrieve native credentials; -- bypass storage policy; -- create unrestricted processes; -- disable the production sandbox. - -Production invariants: - -- Chromium sandbox enabled; -- no `--no-sandbox` workaround; -- remote debugging disabled or explicitly gated; -- no Node.js runtime injected into the renderer; -- external navigation intercepted; -- native bridge allowlisted; -- browser permissions denied by default unless a feature requires them. - ---- - -# 13. Browser capability policy - -Every browser-level permission must be classified. - -Initial policy: - -| Capability | Policy | -|---|---| -| Camera | deny unless an explicit future feature requires it | -| Microphone | explicit user-controlled grant for voice features | -| Geolocation | deny | -| Notifications | prefer native abstraction | -| Clipboard | controlled | -| WebGPU | feature-dependent, diagnosed | -| MIDI | deny | -| USB | deny | -| Serial | deny | -| Web filesystem APIs | not canonical desktop persistence | -| External navigation | intercepted | -| Downloads | controlled | - ---- - -# 14. Persistence redesign - -The renderer must stop being the durability authority. - -Target conceptual flow: - -```text -React interaction - ↓ -domain/native command - ↓ -Rust core - ↓ -authoritative state / transaction - ↓ -durable persistence - ↓ -acknowledged event -``` - -This does not require an IPC round-trip for every keystroke. It requires a design where a renderer crash cannot be equivalent to losing the only authoritative unsaved project state. - ---- - -# 15. Dirty-state and save coordinator - -Introduce explicit persistence states: - -```text -clean -dirty -saving -saved -save_failed -recovery_required -``` - -A single save coordinator should own durable writes. - -Requirements: - -- no overlapping full project serializations; -- lifecycle flush and autosave share the same coordinator; -- no unbounded queue; -- "latest state wins" semantics where safe; -- obsolete saves can be dropped before expensive work; -- current write is never corrupted by cancellation; -- renderer lifecycle does not create duplicate save work; -- shutdown awaits required durable completion. - -The existing "visibility hidden → full flush" behavior should be replaced by dirty-aware, coalesced persistence. - ---- - -# 16. Large-data serialization and compression - -Desktop-critical persistence should migrate away from repeated renderer-side: - -```text -JSON.stringify -→ JS compression -→ IPC -→ filesystem -``` - -Target: - -```text -structured change / canonical native state -→ Rust serialization -→ Rust compression -→ encryption -→ durable storage -``` - -Evaluate: - -- streaming behavior; -- zstd or other appropriate native codec; -- backward compatibility; -- CPU cost; -- memory amplification; -- migration implications. - -Do not change storage format merely for novelty. Measure and justify. - ---- - -# 17. Snapshot redesign - -The CEF program must eliminate current snapshot memory-risk patterns. - -Required invariants: - -1. Snapshot list rendering does not load full snapshot payloads. -2. Metadata is stored/indexed separately from heavy content. -3. Automatic snapshot count is bounded. -4. Total snapshot byte usage is bounded or actively monitored. -5. A project save and auto-snapshot do not perform uncontrolled duplicate full serialization. -6. Snapshot pruning is deterministic. -7. Snapshot failure cannot corrupt the active project. -8. Readers see only committed generations. -9. Snapshot payload format supports future deduplication/delta evaluation. - -Current whole-manuscript word counting should become streaming/chunked rather than whole-document `join → split → filter`. - ---- - -# 18. Durability model - -Atomic rename protects readers from partial final files but does not by itself prove power-loss durability. - -Document exact guarantees per platform. - -Where strong durability is required, evaluate a sequence equivalent to: - -```text -write temporary -→ flush/fsync temporary -→ atomic replace -→ flush/fsync parent metadata -``` - -Multi-file state should use a true commit concept: - -- generation files + atomic manifest; -- journal; -- single transactional container; -- database transaction. - -Binder binary + metadata must not rely on two independent final writes being "close enough". - ---- - -# 19. Storage architecture decision checkpoint - -CEF does not automatically imply "use SQLite". - -At the appropriate wave, evaluate: - -### File-oriented -Pros: -- portability; -- transparency; -- existing model. - -### SQLite/native DB -Pros: -- transactions; -- partial updates; -- indexing; -- bounded incremental persistence. - -### Hybrid -Potentially: -- portable project/export format; -- native transactional working store. - -Criteria: - -- recoverability; -- encryption; -- migration; -- large-project behavior; -- backup; -- user portability; -- future Native-v2 reuse. - -No storage-engine migration without explicit ADR and recovery plan. - ---- - -# 20. Encryption architecture - -Desktop crypto must become renderer-independent. - -Target `worldscript-crypto` responsibilities: - -- versioned encrypted envelopes; -- AEAD; -- AAD bound to record identity/type; -- strong KDF; -- passphrase lifecycle; -- session lock/unlock; -- platform secret integration where appropriate; -- crash-resumable migrations; -- migration admission lock; -- binary assets; -- corruption detection; -- recovery. - -No UI runtime may make security claims that exceed the actual native storage behavior. **This is the direct successor scope for PR #356's goal** (§65) — desktop project-text-at-rest encryption remains an open gap on `main` today and is tracked in the risk register as Wave-7 scope, not patched into the current Tauri filesystem architecture. - ---- - -# 21. Credential storage - -API credentials should not remain normal frontend persistence. - -Preferred hierarchy: - -1. Windows Credential Manager / platform-equivalent; -2. macOS Keychain; -3. Linux Secret Service; -4. carefully designed encrypted fallback when platform secret service is unavailable and policy allows it. - -Rules: - -- no silent protected→plaintext downgrade; -- no secrets in logs; -- no secrets in diagnostics; -- no full API key echoed back to UI after storage if not necessary. - -This is the direct successor scope for PR #355's deeper goal (§65) — merged PR #363 already unified fail-closed API-key routing and discarded legacy derived-key files, but full platform-keychain integration remains future (Wave 7) scope. - ---- - -# 22. Tauri → CEF data migration - -CEF must safely adopt existing users. - -Migration requirements: - -- discover current data; -- identify schema/runtime version; -- validate source; -- create backup; -- checksum; -- migrate idempotently; -- journal progress; -- resume after interruption; -- verify result before cleanup; -- preserve fallback during beta; -- never destroy the old store before successful commit. - -Early CEF beta should favor safety over seamlessness. - -A separate CEF profile/cache is strongly preferred initially. Canonical project-data migration can be explicit and guarded. - ---- - -# 23. Concurrent runtime protection - -Tauri and CEF must not write the same canonical project concurrently without a defined lock/transaction model. - -During transition evaluate: - -- process-level project lock; -- runtime ownership marker; -- read-only fallback if another runtime owns the project; -- explicit "open anyway" recovery only when safe. - ---- - -# 23.1 Accessibility integration must be proven early - -Accessibility cannot wait until the late polish phase. - -CEF/Chromium provides platform accessibility integration for windowed browsers, but WorldScript must prove that its **chosen host/window integration actually exposes the expected accessibility tree and interaction model**. - -The early CEF bootstrap must therefore include an Accessibility Integration Spike. - -Minimum early checks: - -- accessibility is not accidentally disabled by host settings or command-line flags; -- CEF accessibility state can be enabled/observed intentionally; -- the WorldScript root, navigation and primary controls appear in the platform accessibility tree; -- keyboard focus reaches the CEF content correctly; -- native menu ↔ browser focus transitions work; -- one screen-reader smoke path works on the first target platform; -- focus is restored after native dialogs; -- renderer restart does not leave accessibility/focus broken. - -Where useful, instrument CEF accessibility callbacks in development builds for tree/location diagnostics. - -This early spike is not full certification. It prevents discovering after months of CEF work that the selected host/window strategy has a fundamental accessibility integration defect. - ---- - -# 23.2 Accessibility regression ladder - -Accessibility validation occurs at three levels: - -### Early integration gate -During bootstrap: -- tree exists; -- keyboard/focus works; -- basic screen-reader smoke. - -### Continuous feature gate -During normal CEF development: -- changed dialogs/menus/navigation receive keyboard/a11y tests; -- ARIA/browser semantics preserved. - -### Final certification gate -Later hardening: -- representative screen readers; -- keyboard-only workflows; -- Writer; -- IME interaction; -- high contrast/scaling; -- complete cross-platform matrix. - -The late Accessibility wave is therefore **certification and deep regression**, not the first time CEF accessibility is tested. - ---- - -# 24. CEF bootstrap milestone - -The first CEF milestone should intentionally be small: - -1. initialize CEF correctly; -2. create native window; -3. load existing Vite `dist/`; -4. render WorldScript; -5. expose no dangerous native bridge; -6. support clean shutdown; -7. report CEF/Chromium/runtime identity. - -This is the first production program milestone, not the end product. Wave 2 scope. - ---- - -# 25. Immediate renderer bake-off - -As soon as bootstrap works, compare: - -```text -A — PWA / Chromium -B — Tauri / WebKitGTK -C — CEF / Chromium -``` - -Use identical frontend revision and workload. - -Measure: - -- cold start; -- warm start; -- Settings switching; -- Writer typing; -- Writer scrolling; -- motion/transparency ON/OFF; -- Alt-Tab loops; -- idle; -- autosave; -- snapshot boundary; -- large project; -- 30–60 minute session. - ---- - -# 26. #332 differential validation - -Issue #332 becomes an explicit CEF acceptance track. - -On the reporter-class environment, collect: - -- host RSS/PSS; -- CEF browser-process memory; -- CEF renderer-process memory; -- GPU-process memory where available; -- JS heap; -- frame/input latency; -- renderer crash count; -- GPU crash count; -- Alt-Tab failures; -- hard freezes. - -CEF success must be evidence-based, not subjective. - -The program should especially test: - -```text -Ubuntu -KDE/Plasma -Wayland -NVIDIA -``` - -while also expanding beyond one machine. - ---- - -# 27. Memory SLOs - -Exact numerical budgets follow baseline measurement, but the contract is immediate: - -> Repeated equivalent workloads must converge toward a stable memory plateau rather than exhibit monotonic unbounded growth. - -Track: - -- idle baseline; -- large project; -- save peak; -- snapshot peak; -- AI peak; -- 30-minute slope; -- 2-hour slope; -- GPU process; -- recovery. - -A renderer reaching catastrophic multi-gigabyte growth without recovery is a release blocker. - ---- - -# 28. CEF process diagnostics - -WorldScript Diagnostics must distinguish: - -```text -WorldScript native host -CEF browser process -CEF renderer process -CEF GPU process -JS heap -Rust core memory where measurable -``` - -This is mandatory. - -The #332 investigation exposed how costly it is to have only one undifferentiated "the app used a lot of memory" observation. - ---- - -# 29. Renderer crash recovery - -CEF should turn process isolation into product resilience. - -Target: - -```text -renderer terminates - ↓ -host records sanitized crash context - ↓ -Rust core remains alive - ↓ -pending durable state secured - ↓ -renderer recreated - ↓ -UI rehydrated - ↓ -user sees recovery notice -``` - -A renderer crash should not automatically equal application-data loss. - -Repeated crash loops should trigger Safe Mode. - ---- - -# 30. GPU failure recovery - -Handle explicitly: - -- GPU process crash; -- device/context loss; -- software fallback; -- repeated GPU crash; -- driver/backend diagnostics. - -Do not silently remain in a severely degraded software-rendering mode without observability. - ---- - -# 31. Visual-effects policy - -CEF will likely improve renderer behavior, but Chromium is not an excuse for unlimited visual cost. - -Maintain or improve: - -- Reduce Motion; -- Reduce Transparency; -- Plain Writing Surface. - -Audit: - -- `backdrop-filter`; -- large blur layers; -- permanent `will-change`; -- large fixed animated surfaces; -- unnecessary compositor promotion. - -Define internal visual performance profiles if measurement justifies them: - -```text -Full -Balanced -Reduced -``` - -Accessibility preferences always override decorative ambitions. - ---- - -# 32. Desktop Diagnostics feature - -Implement: - -```text -Settings -→ About / Diagnostics -→ Copy Desktop Diagnostics -``` - -Include safe data such as: - -- app version; -- runtime; -- CEF version; -- Chromium version; -- OS; -- kernel; -- desktop environment; -- Wayland/X11; -- GPU/driver; -- sandbox status; -- renderer backend; -- memory snapshot; -- project size class; -- snapshot count/bytes; -- storage health; -- encryption configured/locked state; -- migration state; -- update channel; -- crash/recovery counters; -- reduce-motion/transparency state. - -Explicitly exclude: - -- manuscript contents; -- API keys; -- passphrases; -- auth headers; -- signing keys; -- sensitive raw paths unless essential. - ---- - -# 33. Logging and privacy - -Use structured logs with: - -- timestamp; -- process; -- subsystem; -- event; -- correlation ID; -- severity; -- sanitized metadata. - -Never log full project objects as routine diagnostics. - -Crash dumps may contain sensitive memory and therefore require a deliberate privacy/consent policy. - -Prefer local diagnostics by default. - ---- - -# 34. CEF version governance - -Bundling CEF means WorldScript owns the desktop browser-runtime lifecycle. - -Required automation: - -```text -CEF/Chromium release -→ monitored -→ dependency update PR -→ build -→ unit/integration -→ packaged smoke -→ GPU/runtime checks -→ security evaluation -→ candidate release -``` - -Critical Chromium/CEF vulnerabilities require a defined patch-response policy. - -Do not allow a stale embedded browser runtime to persist indefinitely. - ---- - -# 34.1 CEF/Chromium security patch SLA - -WorldScript Studio must operate its embedded Chromium runtime under an explicit internal **security response SLA**. - -The purpose is not to promise that every upstream Chromium vulnerability requires an emergency WorldScript release. The purpose is to ensure that every relevant CEF/Chromium security event is triaged quickly, classified consistently, and patched within a defined risk window when the WorldScript threat model is affected. - -## 34.1.1 Severity-based response targets - -Use these internal targets unless a stricter incident-specific response is justified: - -| Upstream risk class | Internal triage target | Patch/release target when applicable | -|---|---:|---:| -| Actively exploited / known exploitation relevant to embedded Chromium | within 24 hours | emergency candidate immediately; production release target <= 72 hours | -| Critical, remotely exploitable, plausible in WorldScript threat model | <= 1 business day | target <= 5 calendar days | -| High severity, relevant but mitigated by WorldScript usage/sandbox | <= 2 business days | target <= 10 calendar days | -| Medium | <= 5 business days | next planned maintenance release unless risk changes | -| Low / not applicable | document during normal dependency review | normal cadence | - -These are **internal operational targets**, not guarantees to users. If a vulnerability is not exploitable in the WorldScript configuration, record why. - -## 34.1.2 Emergency browser-runtime update lane - -Maintain an emergency path capable of: - -```text -upstream advisory -→ relevance assessment -→ CEF candidate selection -→ build -→ security smoke -→ core packaged smoke -→ updater/signature verification -→ targeted Linux/Windows/macOS runtime validation -→ emergency release -``` - -The emergency lane may shorten only non-risk-relevant long-tail validation. It must never omit: - -- signature verification; -- sandbox smoke; -- application startup; -- project open/save; -- updater correctness; -- basic accessibility/focus; -- migration compatibility. - -## 34.1.3 Patch deferral policy - -A CEF/Chromium patch may be deferred only with a recorded decision containing: - -- advisory/CVE; -- affected CEF/Chromium versions; -- affected WorldScript version; -- exploit prerequisites; -- renderer/network exposure analysis; -- sandbox relevance; -- mitigations and compensating controls; -- re-evaluation date; -- accountable owner. - -No silent "update later" disposition is acceptable. - -## 34.1.4 Security freshness indicators - -Expose in release/governance reporting: - -```text -Current CEF version -Current Chromium version -Latest evaluated upstream security release -Days since last CEF security review -Open browser-runtime security exceptions -Oldest exception age -``` - -An overdue critical/high exception becomes a release-management escalation. - -## 34.1.5 Routine update cadence - -Separate emergency security servicing from routine browser-runtime maintenance. - -Operational policy: - -- monitor upstream automatically where practical; -- evaluate each relevant stable CEF/Chromium security release; -- uptake routine CEF updates on a predictable maintenance cadence; -- avoid unnecessary large multi-version jumps; -- maintain one tested last-known-good runtime for rollback. - -Security review must never depend on maintainers remembering to inspect upstream manually. - -## 34.1.6 SLA automation - -Repository/CI automation should enforce or at least surface: - -- version-age checks; -- stale security-exception warnings; -- dependency-update issue/PR generation; -- release-blocking alerts when critical accepted-risk deadlines expire. - -The final risk decision remains maintainer-owned, while deadline tracking is automated. - ---- - -# 35. CEF binary provenance and licensing - -Track: - -- exact CEF version; -- Chromium version; -- binary source; -- checksum; -- third-party notices; -- redistribution requirements. - -Initially prefer official/prebuilt CEF distributions. - -Do not build Chromium from source unless a demonstrated product requirement justifies the large CI and security-maintenance burden. - ---- - -# 36. Desktop capability parity inventory - -Before replacing Tauri, inventory every current desktop responsibility. **Baseline established at Wave 0** — see `docs/cef/TAURI-COUPLING-INVENTORY.md` for the verified file-level detail behind this table. - -Minimum matrix: - -| Capability | Current | CEF target | -|---|---|---| -| renderer | system WebView | bundled Chromium/CEF | -| filesystem | Tauri FS (`services/fs/fsCore.ts`) | Rust core/platform | -| dialogs | Tauri Dialog (`services/fs/fsCore.ts`, `services/lora/loraTrainingService.ts`) | native platform adapter | -| tray | Tauri (`services/desktop/desktopTray.ts`, `services/tauriTrayService.ts`) | native CEF-host/platform | -| menus | Tauri (`services/desktop/desktopMenu.ts`, `services/tauriMenuService.ts`) | native command registry | -| quit/lifecycle | Tauri process/window (`App.tsx`, `plugin-process`) | native lifecycle coordinator | -| updater | Tauri Updater (`hooks/useTauriUpdater.ts`) | signed CEF updater | -| notifications | Tauri plugin (`services/desktop/desktopNotifications.ts`) | native adapter | -| deep links | Tauri (`services/tauriDeepLink.ts`) | native host | -| file associations | Tauri bundle (`src-tauri/tauri.conf.json`) | native installer | -| native tasks | Tauri commands (`services/tauriTaskBridge.ts`, `src-tauri/src/`) | Rust task runtime | -| diagnostics | partial | first-class | -| storage | mixed (IDB browser path + Tauri fs path) | core-owned | -| encryption | mixed — IDB path complete (ADR-0018), Tauri filesystem path incomplete (§65, PR #356 gap) | core-owned | - -No capability disappears silently. - ---- - -# 37. Command registry - -Menus, shortcuts, command palette and future native frontend should converge on shared command IDs: - -```text -CommandId::Save -CommandId::OpenProject -CommandId::Search -CommandId::Settings -CommandId::CommandPalette -CommandId::Generate -CommandId::Quit -``` - -Native menu actions trigger command IDs rather than duplicate product logic. - ---- - -# 38. Lifecycle semantics - -Define distinct states: - -```text -hide window -close window -quit application -OS shutdown -renderer crash -browser-process crash -system suspend -resume -update restart -``` - -One authoritative shutdown protocol should exist: - -```text -request quit -→ stop accepting unsafe new work -→ cancel/defer tasks -→ flush save coordinator -→ persist window/application state -→ stop renderer -→ stop CEF -→ stop core -→ exit -``` - -Failure and timeout policy must be explicit. - ---- - -# 39. Tray and menu parity - -Required: - -- localized menu labels; -- Show; -- Settings; -- Command Palette; -- Quit; -- close-to-tray; -- no duplicate event handlers; -- clean quit flush. - ---- - -# 40. Deep links and file associations - -Preserve/evaluate current schemes and project extensions. - -Current project extensions: - -- `.worldscript` -- `.wsst` - -Test: - -- cold-start deep link; -- already-running instance; -- malformed input; -- newer/older schema; -- encrypted project; -- project already open; -- untrusted invite token. - ---- - -# 41. Single-instance and multi-window policy - -Decide deliberately. - -Avoid accidental multiple write owners. - -If multi-window is introduced later, prefer a model such as: - -```text -one native core -→ multiple CEF views -→ explicit project ownership -``` - -Do not add multi-project windows before locking storage ownership semantics. - ---- - -# 41.1 Crash reporting and hang diagnostics are an independent operations track - -CEF has its own multi-process crash-reporting facilities and process lifecycle. WorldScript must build an explicit **Crash Operations** subsystem rather than treating crashes as ordinary application log messages. - -The subsystem must distinguish at least: - -```text -native host crash/panic -CEF browser-process crash -CEF renderer-process crash -CEF GPU/utility-process crash -renderer termination/OOM -hang/freeze without crash -startup crash loop -``` - -A crash reporter alone is insufficient because #332-like incidents can manifest as hangs/frozen UI. Therefore Crash Operations must combine: - -- CEF crash reporting/minidumps where appropriate; -- process termination callbacks; -- application heartbeat/watchdog diagnostics where safe; -- hang detection; -- structured runtime logs; -- process-memory snapshots; -- renderer recovery counters. - -## 41.2 Symbolization and release provenance - -Every crash artifact must be attributable to: - -- WorldScript version; -- commit; -- CEF version; -- Chromium version; -- platform/architecture; -- symbol set/build ID. - -Maintain a symbol retention/symbolization strategy for released builds. - -Unsymbolized dumps without build provenance have limited operational value. - -## 41.3 Crash privacy - -Creative-writing content can be exceptionally sensitive. - -Crash/minidump policy must therefore define: - -- whether dump collection is local-only by default; -- user consent before upload; -- retention; -- filesystem permissions; -- redaction limits; -- transport encryption; -- deletion; -- who can access submitted dumps. - -Never assume that a minidump is free of manuscript or credential material. - -## 41.4 Crash-free and hang-free quality metrics - -For beta/stable readiness, track: - -- crash-free sessions; -- renderer-recovery success; -- startup crash-loop count; -- unrecovered hangs; -- forced-kill reports. - -Do not use crash-free percentage as the only metric; a frozen renderer that never crashes is still a severe defect. - -## 41.5 Crash Operations gate - -Before broad CEF beta: - -```text -[ ] native-host crash path tested -[ ] renderer termination path tested -[ ] GPU/utility crash observation tested where possible -[ ] symbols/build provenance available -[ ] privacy policy documented -[ ] local diagnostic bundle available -[ ] repeated startup crash invokes safe recovery path -[ ] at least one hang-diagnostic path exists -``` - ---- - -# 42. Updater as independent critical workstream - -WorldScript must distinguish **two different update problems**: - -1. **WorldScript application update** — the complete product, schemas, Rust core, frontend and bundled runtime. -2. **CEF/Chromium runtime servicing** — keeping the embedded browser engine on an acceptable security/stability revision. - -CEF documents installer/runtime mechanisms on some platforms, including a Windows shared-runtime installer model, but that is **not a cross-platform replacement for the WorldScript application updater**. WorldScript must own an application-level update architecture even if platform-specific CEF servicing mechanisms are later used internally. - -The CEF application updater must provide at least: - -- signed manifest; -- signed artifact; -- cryptographic verification; -- release channels; -- version policy; -- download progress; -- disk-space checks; -- interrupted download recovery; -- atomic activation; -- rollback; -- failed-new-version crash-loop handling. - -Treat updater bugs as potential P0/P1 issues. - ---- - -# 43. Release channels - -Plan for: - -```text -stable -beta -nightly -``` - -During migration: - -- Tauri can remain Stable; -- CEF initially Nightly/Internal; -- CEF then Beta; -- CEF Stable only after formal admission. - ---- - -# 44. Packaging — Linux - -Target staged support: - -- `.deb`; -- AppImage; -- `.rpm` where justified. - -Validation matrix: - -- Ubuntu/Debian family; -- Fedora family where supported; -- KDE; -- GNOME; -- Wayland; -- X11 where in support policy; -- NVIDIA; -- AMD; -- Intel. - ---- - -# 44.1 Linux runtime compatibility is its own engineering workstream - -CEF removes the production dependency on the system WebKitGTK renderer, but it does **not** make Linux dependency-free. - -CEF binary distributions ship a platform-specific `libcef` plus required resources and have Linux package/runtime dependencies that vary with the selected CEF build and compatibility floor. - -Therefore define a **Linux Runtime Compatibility Contract**. - -It must specify: - -- minimum supported distribution/runtime baseline; -- CPU architectures; -- required dynamic libraries/packages; -- `libcef.so` and resource layout; -- loader/rpath policy; -- sandbox requirements; -- X11 policy; -- Wayland policy; -- Ozone/backend policy where relevant; -- GPU/driver support expectations; -- installer dependency behavior. - -Do not hardcode a glibc/distro minimum in architecture docs until the selected CEF distribution and packaged builds have proven it. - ---- - -# 44.2 Wayland and X11 must be tested from bootstrap onward - -The historical problem involved WebKitGTK, but CEF/Chromium has its own Linux display and GPU stack. - -Therefore: - -> **"CEF uses Chromium" is not accepted as proof of Wayland/X11 correctness.** - -The first Linux milestones should run both display-server paths where supported by policy. - -Track separately: - -```text -KDE / Wayland -KDE / X11 -GNOME / Wayland -GNOME / X11 where supported -``` - -and combine them with: - -```text -NVIDIA -AMD -Intel -``` - -Do not wait until Release Candidate to discover compositor/backend-specific behavior. - ---- - -# 44.3 Clean-machine dependency tests - -For every supported Linux package type, test in clean VMs/images: - -- install dependencies; -- install WorldScript; -- launch; -- sandbox; -- GPU renderer; -- fonts; -- native dialogs; -- accessibility bridge; -- update; -- uninstall. - -A developer workstation with accumulated packages is not sufficient proof. - ---- - -# 44.4 CEF version compatibility-floor gate - -Every CEF/Chromium upgrade must answer: - -```text -Did required Linux runtime dependencies change? -Did the minimum supported distro/glibc floor move? -Did Wayland/X11 behavior change? -Did sandbox requirements change? -Did packaged resource layout change? -``` - -If yes, treat it as a product compatibility decision, not a routine dependency bump. - ---- - -# 44.5 Hardware CI versus virtual CI - -Cloud CI is valuable for compilation/install/smoke tests but cannot replace real graphics hardware for: - -- NVIDIA Wayland; -- AMD Wayland; -- Intel GPU; -- suspend/resume; -- compositor integration. - -Maintain at least a small real-hardware validation lane before Stable. - ---- - -# 44.6 Low-end and resource-constrained device qualification - -WorldScript Studio is a writing application first. It must remain useful on machines substantially weaker than a typical AI/developer workstation. - -CEF carries a higher baseline runtime footprint than a minimal system-WebView shell, while local AI can add major RAM/VRAM pressure. Therefore the CEF program requires a dedicated **Low-End Qualification Track**. - -This is not optional polish. It prevents the product from drifting toward high-end-hardware exclusivity. - -## 44.6.1 Resource classes - -Maintain at least three stable reference classes: - -### Class L1 — minimum practical desktop -Representative profile: - -- 8 GB system RAM; -- older integrated Intel/AMD GPU; -- 2–4 physical cores / modest mobile CPU; -- no dedicated GPU; -- SATA SSD or modest NVMe; -- 1080p display. - -### Class L2 — constrained modern - -- 12–16 GB RAM; -- recent integrated GPU; -- 4–6 CPU cores; -- no dedicated AI GPU. - -### Class L3 — mainstream baseline - -- 16 GB RAM; -- mainstream integrated or entry dedicated GPU; -- modern 6+ core CPU. - -Exact devices may change, but class semantics must remain stable enough for trend comparisons. - -## 44.6.2 Mandatory low-end scenarios - -Test: - -```text -cold start -small project -100k-word project -500k-word project -typing -search -Settings -autosave -snapshot -export -background/resume -update -``` - -Test local AI disabled and enabled separately. - -## 44.6.3 Local AI resource-admission policy - -Local AI must never destabilize the core writing workflow. - -Add a resource-admission layer able to: - -- inspect available RAM/VRAM; -- estimate model footprint; -- warn before model download/load; -- reject clearly unsafe combinations; -- prefer smaller models on constrained devices; -- unload models when not needed; -- avoid simultaneous model-load + snapshot/export peaks. - -The Writer and project persistence always outrank model residency. - -## 44.6.4 Graceful degradation hierarchy - -Under pressure, degrade in this order where applicable: - -```text -decorative effects -nonessential background indexing -preview caches -local AI model residency -derived analytics -``` - -before degrading: - -```text -typing -save correctness -project load -recovery -``` - -Never trade data safety for AI or visual effects. - -## 44.6.5 Low-end memory budgets - -CEF Stable must establish measured budgets for: - -- runtime baseline; -- small project; -- large project; -- local AI disabled; -- local AI active; -- autosave peak; -- snapshot peak; -- acceptable swap behavior. - -A material L1/L2 regression requires explicit GO/NO-GO review. - -## 44.6.6 Thermal and battery-aware behavior - -On laptops and constrained devices consider: - -- reduced nonessential background polling on battery; -- deferred indexing; -- lower-frequency diagnostics; -- explicit local-AI power/resource warnings; -- avoiding continuous decorative GPU work. - -Never reduce persistence correctness to save power. - -## 44.6.7 Low-end qualification gate - -Before CEF Stable: - -```text -[ ] L1 launch and core Writer workflow passes -[ ] L1 100k-word project usable -[ ] L2 500k-word project acceptable or documented limit -[ ] no OOM during routine save/snapshot -[ ] local-AI admission prevents unsafe model load -[ ] visual-reduction mode demonstrably lowers resource cost -[ ] swap/memory-pressure behavior documented -[ ] low-end regression baseline stored -``` - ---- - -# 45. Packaging — Windows - -Required: - -- signing; -- installer; -- upgrade-in-place; -- uninstall; -- AppData correctness; -- deep links; -- file associations; -- updater; -- crash/recovery. - ---- - -# 46. Packaging — macOS - -Required: - -- Apple Silicon first-class; -- signing; -- notarization; -- entitlements; -- app bundle; -- DMG or equivalent; -- deep links; -- file associations; -- updater. - -Intel support follows explicit product policy. - ---- - -# 47. Profile/data separation - -Do not mix disposable CEF browser state with durable WorldScript data. - -Separate: - -```text -canonical project data -native settings/credentials -CEF profile/cache -temporary files -logs -crash data -update staging -model files -``` - -Deleting a corrupt Chromium profile must never delete user manuscripts. - ---- - -# 48. IndexedDB policy - -Long-term desktop principle: - -- canonical project data → native core; -- browser-local caches/UI convenience → IndexedDB if useful; -- secrets → not ordinary IndexedDB; -- large native assets → native storage. - -Service-worker behavior must be explicitly reviewed so PWA update mechanics do not conflict with the native CEF updater. - ---- - -# 49. Local AI and provider parity - -CEF must preserve the useful provider matrix: - -- Gemini; -- OpenAI; -- Anthropic; -- OpenRouter; -- Groq where supported; -- Ollama; -- LM Studio; -- local browser/CEF inference where still appropriate. - -Validate: - -- download; -- progress; -- cancellation; -- storage space; -- memory pressure; -- renderer responsiveness. - -Over time prefer renderer-neutral AI orchestration for desktop. - ---- - -# 50. TaskSupervisor evolution - -CEF should evolve the current Rust supervisor from a thin bridge into a real task runtime. - -Minimum task contract: - -```text -task id -task type -priority -deadline -timeout -retry policy -cancellation -progress -resource class -result -structured error -``` - -Candidate workloads: - -- compression; -- serialization; -- indexing; -- export; -- conversion; -- large text analysis; -- local AI helper tasks. - -Move only measured bottlenecks. - - ---- - -# 51. Test architecture - -CEF needs layered validation. - -## Layer 1 — core unit tests - -No renderer, no CEF. - -Test: - -- domain; -- storage; -- crypto; -- migrations; -- tasks; -- parsers; -- project format. - -## Layer 2 — frontend/platform contract tests - -React against mock DesktopPlatform. - -## Layer 3 — native CEF integration - -Real CEF host + real IPC. - -## Layer 4 — packaged smoke - -Real built artifact. - -## Layer 5 — packaged E2E - -Real installation/runtime flows. - -## Layer 6 — performance / memory / soak - -Long-running real-runtime tests. - -## Layer 7 — field validation - -Representative end-user systems. - -A green web E2E suite is never sufficient evidence that CEF Desktop is healthy. - ---- - -# 52. Bridge contract tests - -Every privileged method requires tests for: - -- valid input; -- missing input; -- invalid type; -- unknown enum; -- oversized payload; -- cancellation; -- timeout; -- permission denial; -- renderer disposal; -- native error; -- malformed protocol version. - -Unknown methods must be rejected. - ---- - -# 53. Failure injection - -Persistence: - -- temp-write failure; -- rename failure; -- flush failure; -- disk full; -- permission denied; -- corrupted file; -- truncated file; -- orphan temp; -- interrupted migration; -- crash during snapshot. - -Updater: - -- bad signature; -- corrupt download; -- disk full; -- network disconnect; -- restart during update; -- bad new binary; -- rollback. - -Runtime: - -- renderer crash; -- renderer OOM; -- GPU-process crash; -- IPC disconnect; -- native panic/test fault. - ---- - -# 54. Deterministic project fixtures - -Create reusable fixtures: - -```text -project-small -project-medium -project-large -project-huge -project-snapshot-heavy -project-binder-heavy -project-image-heavy -project-ai-history-heavy -project-arabic -project-hebrew -project-mixed-bidi -project-japanese -project-chinese -project-korean -project-corrupt -``` - -Suggested text scale classes: - -```text -small < 25k words -medium ~100k -large ~500k -huge 1M+ -``` - -The exact fixtures should represent actual WorldScript structures, not synthetic repeated text only. - ---- - -# 55. Performance metrics - -Track at least: - -- cold start; -- warm start; -- project open; -- time to interactive; -- typing input latency; -- navigation p50/p95/p99; -- Writer scroll frame time; -- Settings switch; -- search; -- save duration; -- save main-thread block; -- snapshot duration; -- AI progress responsiveness; -- renderer memory; -- native memory; -- GPU process memory; -- CPU; -- IPC round-trip and throughput. - ---- - -# 56. Performance budget policy - -Do not invent final budgets before baseline measurement. - -The program must nevertheless establish hard categories such as: - -- routine interactions must feel immediate; -- no routine operation may block the UI thread for hundreds of milliseconds; -- typing must remain responsive under large documents; -- save/snapshot must not create multi-gigabyte transient amplification; -- background work must not cause visible UI starvation; -- memory must stabilize under soak. - -Once measured, record numerical thresholds in CI/perf documentation. - ---- - -# 57. Soak tests - -CEF Release Candidate must survive a representative soak such as: - -```text -2 hours total session -100–500 Alt-Tab cycles -large project -continuous editing -repeated autosaves -multiple snapshot cycles -project switching -Settings navigation -AI start/cancel -network loss/recovery -idle/background/resume -renderer restart -``` - -Required outcome: - -- no hard freeze; -- no manual SIGKILL; -- no unrecovered renderer death; -- no monotonic unbounded PSS/RSS; -- no project corruption; -- no lost settings; -- no stuck save state. - ---- - -# 58. Accessibility - -CEF must preserve or exceed web accessibility. - -Validate: - -- keyboard-only use; -- focus order; -- screen reader; -- menu semantics; -- dialog semantics; -- Writer selection; -- large text; -- high contrast; -- reduced motion; -- reduced transparency; -- scaling; -- 200% zoom where applicable. - -Accessibility is a release gate, not polish. - ---- - -# 59. i18n / IME / BiDi - -The existing multilingual surface must remain first-class. - -Mandatory representative test set: - -- English; -- German; -- Arabic; -- Hebrew; -- Japanese; -- Simplified Chinese; -- Korean. - -IME validation: - -- start composition; -- modify composition; -- cancel; -- commit; -- move cursor; -- selection during composition; -- candidate window behavior. - -BiDi validation: - -- RTL-only; -- mixed Latin/RTL; -- numbers; -- punctuation; -- AI suggestions and highlights. - ---- - -# 60. Visual regression and browser automation - -Retain existing browser/VRT strengths. - -Add CEF-specific automation without unnecessarily exposing production remote debugging. - -Development/test mode may enable: - -- DevTools; -- controlled remote debugging; -- browser automation. - -Production should not expose a debug port by default. - ---- - -# 61. CI target architecture - -Progressively add: - -```text -cef-core -cef-native-compile -cef-linux-build -cef-windows-build -cef-macos-build -cef-learning-harness -cef-competency-gate -cef-integration -cef-packaged-smoke -cef-security -cef-security-sla -cef-doc-drift -cef-low-end-smoke -cef-perf-smoke -cef-success -``` - -Not all jobs must become mandatory on day one. - -By Release Candidate, the aggregate CEF success gate must represent the actual product artifact. - ---- - -# 61.1 Competency and Learning Harness must be observable in CI - -The CEF learning harness and competency gates must not remain documentation-only. - -Create a dedicated executable lane, conceptually: - -```text -cef-learning-harness -``` - -and a policy validation job: - -```text -cef-competency-gate -``` - -## 61.1.1 CI-enforceable harness checks - -Where platform CI permits, automate: - -```text -CEF initializes -browser process starts -renderer subprocess starts -internal page loads -typed IPC ping/pong works -renderer termination is detected -browser closes cleanly -CEF shutdown completes -second launch after shutdown succeeds -unknown privileged IPC method is rejected -runtime version/provenance is reported -``` - -On capable runners additionally exercise: - -- accessibility-tree smoke; -- sandbox smoke; -- crash-artifact generation; -- Linux dependency presence. - -## 61.1.2 Wave dependency enforcement - -Encode prerequisite evidence into CI/repository policy. - -Examples: - -```text -WS-CEF-IPC -requires -cef-learning-harness = PASS -cef-competency-gate = PASS - -WS-CEF-STORAGE -requires -cef-ipc-contract = PASS - -WS-CEF-BETA -requires -cef-packaged-smoke = PASS -cef-crash-ops-smoke = PASS -cef-accessibility-smoke = PASS -``` - -This need not become a complex workflow engine. The invariant is that critical waves cannot silently bypass prerequisite evidence. - -## 61.1.3 Machine-readable competency manifest - -Maintain a manifest such as: - -```yaml -cef_competency: - binding_model_documented: true - lifetime_model_reviewed: true - repeated_shutdown_ci: true - renderer_crash_ci: true - sandbox_smoke: true - accessibility_smoke: true - crash_symbolization_smoke: true -``` - -Validation must fail CI when a required item for the active program phase is absent or false. Live tracking: `docs/cef/CEF-RUST-COMPETENCY-MATRIX.md` (currently all items `false`/not-started — Wave 0 baseline). - -Manifest entries should reference real tests/docs/evidence where practical rather than function as self-attestation. - -## 61.1.4 Documentation-to-test evidence links - -For critical integration assumptions maintain: - -```text -Invariant -→ test -→ CI job -→ failure playbook -``` - -Example: - -```text -CEF cleanly restarts after shutdown -→ tests/cef/lifecycle/restart -→ cef-learning-harness -→ docs/cef/knowledge/subprocess-and-shutdown.md -``` - -This converts learning into a maintained operational contract. - -## 61.1.5 Required failure artifacts - -The harness should publish compact sanitized diagnostics on failure: - -- CEF/Chromium version; -- platform; -- process start/stop sequence; -- exit codes; -- sanitized logs; -- crash-artifact metadata; -- accessibility smoke status where relevant. - -Never include user manuscripts or secrets. - -## 61.1.6 Competency regression policy - -A CEF upgrade that breaks the learning harness is automatically **not merge-eligible** until: - -- integration is fixed; or -- changed upstream behavior is understood, documented, and the harness is deliberately updated. - -This prevents dependency upgrades from silently invalidating hard-earned lifecycle knowledge. - ---- - -# 62. Nightly Desktop Candidate lane - -Nightly must build real CEF artifacts and run: - -- startup; -- project open; -- edit; -- save; -- quit; -- relaunch; -- persistence verification; -- Settings; -- AI cancellation; -- diagnostics; -- crash-recovery smoke where practical. - ---- - -# 63. Release Candidate lane - -Add: - -- fresh install; -- upgrade; -- migration; -- signed artifact; -- updater; -- rollback; -- uninstall; -- longer soak; -- security verification. - ---- - -# 64. Field validation matrix - -CEF must not be certified from one machine. - -Linux reference set: - -```text -NVIDIA + KDE + Wayland -AMD + KDE + Wayland -AMD + GNOME + Wayland -Intel integrated GPU + GNOME/KDE -``` - -Windows: - -```text -NVIDIA -AMD -Intel integrated -``` - -macOS: - -```text -Apple Silicon -Intel only if maintained by support policy -``` - -The #332 reporter environment remains an unusually valuable regression reference, but never the sole validation source. - -The field program must also include at least one **resource-constrained / low-end reference system** representative of L1 or L2. High-end GPU stability and low-end usability are separate release dimensions. - ---- - -# 65. Current open-PR reconciliation - -**Verified 2026-08-18 via `gh pr view` against `qnbs/WorldScript-Studio` — this replaces the original draft's guessed dispositions with checked GitHub state.** The CEF program starts from `main`, not from #352–#356. - -All five PRs opened 2026-08-13, branching from `main` (#352, #353, #354 directly; #355 stacked on #354; #356 stacked on #355). One day later, merged PR **#363** ("fix: stabilize critical desktop persistence and release gates," merged 2026-08-14, released as v1.27.1) shipped: atomic same-directory temp-write-plus-rename across every Tauri filesystem-backed store (project, settings, snapshots, Codex/RAG, images, binder payloads); unified fail-closed desktop API-key routing through the shared storage service with legacy derived-key files discarded; destructive confirmed recovery-reset; and blocking Rust/Tauri fmt/check/clippy/test CI plus required E2E/VRT gates. This overlaps heavily with #353's, #354's, and #355's stated goals. - -Use disposition categories: - -```text -MERGE_AFTER_FIXES -EXTRACT_UNIQUE_DELTA -REIMPLEMENT_ON_CURRENT_MAIN -SUPERSEDED_CLOSE -DEFER -``` - -## #352 — `docs(security): correct false desktop encryption claims` - -**Verified content (the original draft guessed "image MIME/storage behavior" — incorrect; actual scope is documentation truthfulness):** corrects `docs/IDB-ENCRYPTION.md`'s false claim that Tauri desktop shares the web build's full IDB encryption lifecycle; it does not — `services/fs/*Store.ts` writes project/settings/snapshot/Codex/RAG/binder-asset data outside that path. Docs-only, no code changed. 16 files, +105/−55. - -**State:** `mergeable: MERGEABLE`, `mergeStateStatus: BLOCKED` (likely pending review — `reviewDecision` empty; `codecov/patch` is the one failing check, all substantive CI green including the Tauri Rust Gate). - -**Disposition: MERGE_AFTER_FIXES.** Pure docs correction, but it predates #363, which changed the very behavior it documents (fail-closed API-key routing, atomic writes). Re-diff its claims against current `main` before merging — some corrected claims may need a further update now that #363 shipped, or may already be accurate. Do not merge unread. - -## #353 — `ci: add required Rust/Tauri compile gate on pull requests` - -**Verified content:** adds a `rust-check` CI job (`cargo fmt --check`, `cargo check`, clippy, test) — previously Rust code in `src-tauri/` could merge without ever compiling. 19 files, +6796/−197. - -**State:** `mergeable: CONFLICTING`, `mergeStateStatus: DIRTY`, CI **failing** (Security Audit, Rust Check, CI Success all FAILURE). - -**Disposition: SUPERSEDED_CLOSE.** #363 already shipped "blocking Rust/Tauri fmt/check/clippy/test CI" — visible today as the passing "🦀 Tauri Rust Gate" check on #352. Diff for any unique delta (e.g. a specific clippy rule or Linux build-dependency install step #363 didn't cover) before closing; close with a comment citing #363 and this roadmap. - -## #354 — `fix(desktop): atomic writes for all filesystem-backed stores` - -**Verified content:** every `services/fs/*Store.ts` writer previously wrote directly to its final path (`writeTextFile`/`writeFile`); a crash or power loss mid-write could truncate/corrupt with no recovery. Adds `writeTextFileAtomic` (temp-write + rename) across project, active-project marker, settings, API keys, snapshots, Codex, RAG vectors, binder assets, images. 14 files, +1318/−66. - -**State:** `mergeable: CONFLICTING`, `mergeStateStatus: DIRTY`, CI green (Quality Gate, Build, E2E, Storybook, Lighthouse, VRT all SUCCESS; only `codecov/patch` fails). - -**Disposition: SUPERSEDED_CLOSE** (verify first). #363's description — "Replace authoritative Tauri AppData writes with same-directory temp-write plus rename, including project, settings, snapshots, Codex/RAG, images, and binder payload files" — is the same scope, same mechanism. Diff #354 against what actually shipped in #363 for any correctness edge case #363 missed (e.g. specific retry/error-path handling) before closing. Remember: atomic rename alone does not prove power-loss durability (§18) — this remains true regardless of which PR's implementation is kept. - -## #355 — `security(desktop): fix API-key encryption using a real secret` - -**Verified content:** F-05/F-06 (2026-07-29) was credited as fixed for upgrading desktop API-key encryption from unsalted SHA-256 to PBKDF2 + random salt, but never addressed the actual secret material used. Stacked on #354 ("needs `writeTextFileAtomic`"). 12 files, +364/−144. - -**State:** base branch is `fix/desktop-atomic-writes` (#354), not `main`. `mergeable: MERGEABLE`, `mergeStateStatus: CLEAN`, most CI not yet run (stacked). - -**Disposition: SUPERSEDED_CLOSE, likely** (verify first) **+ DEFER the deeper fix.** #363's "unified fail-closed desktop API-key routing... legacy derived-key files discarded" covers the immediate secret-material issue this PR targets. Per §21 above, the durable fix — real platform-keychain integration (Windows Credential Manager / macOS Keychain / Linux Secret Service) — belongs in the renderer-neutral CEF/native core, not patched further into today's Tauri filesystem architecture. Do not restore or extend the old filesystem-key design; close citing #363, and track platform-keychain credential storage as Wave 7 scope. - -## #356 — `security(desktop): encrypt project data at rest (text stores, desktop)` - -**Verified content:** enabling Settings → Privacy → "Encrypt project data at rest" only ever protected the browser/PWA IndexedDB path; on Tauri desktop, `services/fs/*Store.ts` wrote project text unencrypted. Stacked on #355 ("Sibling of #355 — both branch from #354 independently"). 70 files, +2603/−116 — the largest and most architecturally significant of the five. - -**State:** base branch is `fix/desktop-api-key-encryption` (#355). `mergeable: CONFLICTING`, `mergeStateStatus: DIRTY`, most CI not yet run (stacked). - -**Disposition: EXTRACT_UNIQUE_DELTA / REIMPLEMENT_LATER — the one genuine gap.** Unlike #353/#354/#355, **nothing in #363 covers this.** #363 was atomic writes + API-key routing + CI gating, not full project-text encryption for the desktop filesystem path. Desktop Tauri builds today still lack at-rest encryption for project text stores; browser/PWA IDB encryption (ADR-0018, B-1) does not reach the Tauri filesystem path at all. The security goal remains fully valid. Per §20 above, do not merge the stale implementation as final CEF architecture — do not merge it at all in its current conflicting state — but do not let the goal quietly disappear either: it is carried forward explicitly as a named, owned risk (`docs/cef/CEF-RISK-REGISTER.md`) and as Wave 7 (`worldscript-crypto`) scope: migration journal, admission lock, AAD, binary assets, recovery, performance bounds — the same rigor already proven for the IDB path in ADR-0018. - -## Execution note - -The disposition table above is the analysis; **executing it (closing #353/#354/#355 with cited rationale, commenting on #356 to preserve intent and link the risk register, re-validating and merging #352) is a separate, explicitly user-confirmed action** — not a side effect of committing this document. See `docs/cef/CEF-RISK-REGISTER.md` for how #356's goal is tracked going forward. - ---- - -# 66. CEF workstream catalogue - -Use stable IDs: - -```text -WS-CEF-ADR -WS-CEF-STATE -WS-CEF-PLATFORM -WS-CEF-BOOT -WS-CEF-ORIGIN -WS-CEF-IPC -WS-CEF-SECURITY -WS-CEF-SECURITY-SLA -WS-CEF-STORAGE -WS-CEF-SNAPSHOT -WS-CEF-CRYPTO -WS-CEF-MIGRATION -WS-CEF-CREDENTIALS -WS-CEF-LIFECYCLE -WS-CEF-MENU -WS-CEF-TRAY -WS-CEF-DIALOG -WS-CEF-DEEPLINK -WS-CEF-FILEASSOC -WS-CEF-TASKS -WS-CEF-AI -WS-CEF-UPDATER -WS-CEF-PACKAGING -WS-CEF-DIAGNOSTICS -WS-CEF-CRASH -WS-CEF-PERF -WS-CEF-MEMORY -WS-CEF-GPU -WS-CEF-LOWEND -WS-CEF-A11Y -WS-CEF-I18N -WS-CEF-TEST -WS-CEF-CI -WS-CEF-COMPETENCY-CI -WS-CEF-DOCS -WS-CEF-FIELD -WS-CEF-CUTOVER -WS-TAURI-RETIRE -WS-PERFECTION -WS-NATIVE-ADMISSION -``` - ---- - -# 67. Program waves - -## Wave 0 — State reconciliation, architecture freeze and CEF enablement - -**Status: in progress (this commit).** - -Deliver: - -- fresh current-main audit — done, see §3, §36, `docs/cef/TAURI-COUPLING-INVENTORY.md`; -- open PR/issue reconciliation — done, §65 (verified against real GitHub state); -- ADR — done, [ADR-0019](../adr/0019-cef-desktop-runtime-strategy.md); -- runtime capability inventory — done, `docs/cef/TAURI-COUPLING-INVENTORY.md` + `docs/cef/tauri-coupling-inventory.json`; -- risk register — done, `docs/cef/CEF-RISK-REGISTER.md`; -- this roadmap committed — done, this file; -- CEF/Rust competency matrix — done (baseline, all items not-started), `docs/cef/CEF-RUST-COMPETENCY-MATRIX.md`; -- knowledge-building documentation skeleton — done, `docs/cef/knowledge/*.md`; -- isolated CEF learning harness plan — captured in §4.11.3 and §61.1 above (plan only; harness itself is Wave 2); -- external-expertise escalation criteria — §4.11.5 above; -- CI plan for the executable learning harness — §61.1 above (plan only; not implemented); -- documentation ownership manifest for Tier A CEF docs — done, `docs/cef/OWNERSHIP.yaml`. - -Exit additionally requires the **CEF Competency Gate** for the scope about to be implemented — not required yet, since no CEF implementation starts until Wave 2. - -Exit: - -- source of truth unambiguous — satisfied: `main` is the baseline, and every historical PR (#352–#356) has a verified, evidence-based disposition rather than an assumption; -- no CEF dependency on stale branches — satisfied: nothing here depends on #352–#356; the one live gap they revealed (#356's goal) is tracked forward, not resurrected. - -## Wave 1 — DesktopPlatform boundary and Native-readiness baseline - -Deliver: - -- renderer-neutral contracts; -- Tauri adapter; -- web adapter where needed; -- tests; -- initial UI-state/domain-state classification; -- first Native-Readiness scorecard; -- automated guardrail plan for direct platform imports. - -Exit: - -- direct Tauri imports confined to explicit transitional adapter surface. - -## Wave 2 — CEF integration selection, competency proof and bootstrap - -Deliver: - -- binding/C++ decision; -- binding/lifetime/threading cookbook; -- isolated learning harness; -- CEF host; -- existing `dist/`; -- safe repeated startup/shutdown; -- version diagnostics; -- Linux dependency inventory; -- initial X11/Wayland launch checks; -- early accessibility integration smoke; -- initial crash-reporting/symbolization proof; -- learning harness running in CI; -- competency manifest initialized. - -Exit: - -- WorldScript renders reliably under CEF on Linux development systems; -- no production-critical lifecycle assumption remains undocumented; -- the Early Accessibility Gate passes on the first target platform; -- clean repeated start/close cycles pass. - -## Wave 3 — secure origin, renderer validation and crash-operations foundation - -Deliver: - -- internal app origin/resource handler; -- CSP; -- navigation policy; -- PWA/Tauri/CEF benchmark harness; -- #332 comparison; -- process-role crash/termination diagnostics; -- hang-diagnostic foundation; -- Linux display-server/GPU compatibility matrix. - -Exit: - -- CEF renderer viability proven with evidence. - -## Wave 4 — typed IPC v1 - -Deliver: - -- requests; -- responses; -- events; -- errors; -- cancellation; -- capability negotiation; -- size limits; -- schema tests. - -Exit: - -- no generic privileged bridge. - -## Wave 5 — persistence core vertical slice - -Deliver: - -```text -open project -→ edit -→ native save -→ quit -→ relaunch -→ restore -``` - -plus save coordinator and dirty-state model. - -Exit: - -- renderer is no longer sole durability authority. - -## Wave 6 — snapshot / memory hardening - -Deliver: - -- metadata-only listing; -- retention/byte policy; -- efficient word counting; -- save/snapshot coordination; -- memory benchmarks. - -Exit: - -- no known snapshot-driven memory bomb. - -## Wave 7 — crypto, credentials and migration - -Deliver: - -- renderer-independent crypto; -- binary protection; -- credential storage; -- migration journal; -- recovery. - -**This is where #356's goal (desktop project-text-at-rest encryption) and #355's deeper goal (platform-keychain credentials) are actually implemented** — properly, on the renderer-neutral core, per §65's disposition. - -Exit: - -- desktop security claims truthful and tested. - -## Wave 8 — desktop integration parity - -Deliver: - -- dialogs; -- tray; -- menus; -- window; -- notifications; -- clipboard; -- deep links; -- file associations. - -Exit: - -- user-visible shell parity. - -## Wave 9 — task runtime and heavy native work - -Deliver: - -- mature TaskSupervisor; -- native compression/index/export work where measured; -- cancellation/timeouts/progress. - -Exit: - -- expensive work no longer unnecessarily burdens renderer. - -## Wave 10 — AI/local AI parity - -Deliver: - -- cloud providers; -- Ollama; -- LM Studio; -- model downloads; -- cancellation; -- memory/space checks. - -Exit: - -- no material AI regression. - -## Wave 11 — updater - -Deliver: - -- signed update system; -- channels; -- staging; -- activation; -- rollback; -- failure recovery. - -Exit: - -- production-grade update path. - -## Wave 12 — cross-platform packaging and compatibility-floor qualification - -Deliver: - -- Linux; -- Windows; -- macOS packages; -- signing/notarization as applicable; -- clean-machine Linux dependency validation; -- initial low-end L1/L2 package/runtime smoke; -- documented CEF/Linux compatibility floor. - -Exit: - -- installable real artifacts on all supported platforms; -- no unknown mandatory Linux runtime dependency; -- basic low-end Writer workflow passes. - -## Wave 13 — packaged-artifact E2E - -Deliver: - -- install; -- launch; -- edit; -- save; -- relaunch; -- migrate; -- update; -- recovery; -- uninstall. - -Exit: - -- CI tests the product, not merely the web app. - -## Wave 14 — security hardening - -Deliver: - -- CEF threat-model review; -- IPC audit; -- sandbox verification; -- updater audit; -- dependency review; -- fuzz/property tests. - -Exit: - -- no unresolved security P0/P1. - -## Wave 15 — performance / memory / GPU / low-end hardening - -Deliver: - -- numerical budgets; -- large fixtures; -- soak tests; -- GPU recovery; -- memory plateau proof; -- L1/L2 resource budgets; -- local-AI resource-admission validation; -- battery/thermal behavior review. - -Exit: - -- no known catastrophic runaway or reproducible hard freeze. - -## Wave 16 — accessibility / i18n / IME certification - -This is the **deep certification wave**, not the first accessibility test. - -Deliver: - -- cross-platform representative screen-reader validation; -- keyboard-only end-to-end workflows; -- CJK IME; -- RTL/BiDi; -- scale/theme/high-contrast tests; -- Writer-specific accessibility validation; -- regression comparison against Web/PWA. - -Exit: - -- no major accessibility/internationalization regression; -- early accessibility assumptions proven across the supported platform matrix. - -## Wave 17 — controlled field beta - -Deliver: - -- beta channel; -- diagnostics; -- representative tester matrix; -- at least one low-end/resource-constrained tester; -- crash/hang operational dashboards; -- security-SLA dashboard; -- issue triage. - -Exit: - -- stable field operation across multiple hardware stacks. - -## Wave 18 — Release Candidate - -Deliver: - -- full GO/NO-GO dossier; -- migration rehearsal; -- updater rehearsal; -- rollback rehearsal; -- known-risk ledger; -- security-patch SLA compliance report; -- documentation freshness report; -- competency/learning-harness CI status; -- low-end qualification report. - -Exit: - -- GO recommendation. - -## Wave 19 — CEF Stable cutover - -CEF becomes preferred Desktop. - -Tauri becomes explicitly transitional/deprecated. - -## Wave 20 — Tauri retirement - -After a stabilization interval: - -- remove obsolete runtime code; -- remove old dependencies; -- remove old CI; -- retain historical data-compatibility tests. - -## Wave 21 — post-cutover perfection - -WorldScript-wide: - -- defect burn-down; -- tail-performance optimization; -- memory efficiency; -- recovery polish; -- UX consistency; -- accessibility; -- documentation; -- dependency cleanup. - -## Wave 22 — Native-v2 admission review - -Only now decide whether to start the separate `egui/wgpu` product. - ---- - -# 68. PR slicing policy - -CEF must not arrive as a 30,000-line replacement PR. - -Preferred early sequence: - -```text -PR A — ADR + roadmap -PR B — runtime/capability inventory -PR C — DesktopPlatform contract -PR D — Tauri adapter -PR E — CEF bootstrap -PR F — secure resource origin -PR G — diagnostics foundation -PR H — renderer benchmark/#332 harness -PR I — typed IPC foundation -PR J — persistence vertical slice -``` - -Each PR should: - -- have one dominant concern; -- be independently testable; -- preserve buildability; -- include rollback path; -- add targeted tests; -- update architecture docs if an invariant changes. - -Avoid long stacked PR chains. Merge frequently behind inactive build targets/feature gates. - -(PR A and PR B are Wave 0's own two PRs — see the Wave 0 section above and Appendix G.) - ---- - -# 69. Tauri feature-freeze policy - -During CEF implementation: - -### Always allowed -- security fixes; -- data-integrity fixes; -- severe regressions; -- critical production reliability; -- necessary release fixes. - -### Strongly discouraged -- large Tauri-only architecture; -- major new Tauri-native capabilities that CEF would immediately replace. - -Feature work should increasingly target renderer-neutral core + React presentation. - ---- - -# 70. Product feature prioritization - -During critical CEF waves, prioritize: - -```text -stability -data integrity -security -migration -performance -packaging -diagnostics -``` - -over broad feature expansion. - -Large plugin/collaboration/workspace additions should not destabilize the runtime transition. - ---- - -# 71. CEF stable admission gate - -CEF may be called Stable only when all major categories pass. - -## Correctness -- open P0 = 0; -- open unaccepted P1 = 0; -- no known data corruption; -- migration verified. - -## Runtime -- no reproducible hard freeze; -- no unrecovered renderer crash loop; -- safe recovery. - -## Memory -- no unbounded standard-workload growth; -- bounded save/snapshot peaks; -- soak passes. - -## Security -- sandbox verified; -- IPC audited; -- updater signatures verified; -- credentials protected; -- threat model current. - -## Packaging -- Linux pass; -- Windows pass; -- macOS pass. - -## Accessibility/i18n -- early CEF accessibility integration was proven before feature expansion; -- critical flows pass; -- representative screen-reader paths pass; -- IME/RTL validated. - -## Native-readiness -- critical domain behavior renderer-neutral; -- storage/crypto/migration/task APIs usable without React; -- no unowned high-impact Native-readiness debt. - -## Operational readiness -- crash reporting/symbolization works; -- hang diagnostics works; -- CEF/Chromium servicing playbook works; -- explicit security patch SLA operational; -- no overdue critical/high browser-runtime security exception without approved escalation; -- Linux dependency compatibility floor documented; -- learning/competency harness is CI-enforced; -- Tier A documentation ownership and drift checks are green. - -## Resource constrained -- L1/L2 qualification completed; -- no routine core-workflow OOM; -- local-AI resource admission works; -- low-end regression baseline stored. - -## Field -- representative systems pass. - ---- - -# 72. Tauri retirement gate - -Tauri may only be removed after: - -```text -CEF feature parity PASS -CEF migration PASS -CEF encryption PASS -CEF credentials PASS -CEF updater PASS -CEF rollback PASS -CEF packaged Linux PASS -CEF packaged Windows PASS -CEF packaged macOS PASS -CEF memory soak PASS -CEF renderer recovery PASS -CEF GPU stability PASS -CEF accessibility PASS -CEF i18n / IME / RTL PASS -CEF field beta PASS -CEF low-end qualification PASS -CEF security-patch SLA PASS -CEF competency CI PASS -CEF Tier A doc drift PASS -CEF open P0 0 -CEF open unaccepted P1 0 -``` - ---- - -# 73. Native-v2 admission gate - -No `egui/wgpu` production implementation begins until: - -```text -CEF Stable PASS -Tauri retirement PASS or effectively complete -Storage core renderer-neutral PASS -Crypto core renderer-neutral PASS -AI core renderer-neutral PASS -Task runtime renderer-neutral PASS -Packaged E2E mature PASS -Updater mature PASS -Diagnostics mature PASS -Open P0 0 -Open unaccepted P1 0 -Large-project performance PASS -Accessibility maturity PASS -Product semantics stable PASS -UI/business-state separation mature PASS -Core behavior headless-testable PASS -No critical renderer-owned business logic PASS -Native-Readiness debt reviewed PASS -Engineering capacity confirmed PASS -``` - -The future Native effort is then a **separate product implementation**, not an emergency CEF replacement. - ---- - -# 74. Why Native v2 waits - -The eventual native frontend will already face difficult work: - -- professional Writer; -- text layout; -- IME; -- accessibility; -- BiDi; -- font fallback; -- rich presentation; -- feature parity. - -It must not simultaneously chase moving targets in: - -- storage; -- encryption; -- project format; -- AI; -- migrations; -- tasks. - -CEF is the stabilization platform that makes Native v2 rational. - ---- - -# 75. Program governance - -Maintain a roadmap ledger: - -| ID | Workstream | Goal/Finding | Status | Severity | Evidence | PR | Test | Exit | -|---|---|---|---|---|---|---|---|---| - -Statuses: - -```text -PLANNED -IN_PROGRESS -BLOCKED -VALIDATING -DONE -SUPERSEDED -DEFERRED -ACCEPTED_RISK -``` - -Severity: - -```text -P0 Critical -P1 High -P2 Medium -P3 Low -``` - -CEF Stable blockers include: - -- data loss; -- corruption; -- sandbox/IPC security failure; -- updater signature bypass; -- unbounded memory; -- hard freeze; -- renderer crash loop; -- migration dead end. - ---- - -# 76. Risk register - -**Live version: `docs/cef/CEF-RISK-REGISTER.md`** (kept as a separate document so it can be updated without editing this roadmap). - -Initial high-level risks: - -| Risk | Initial severity | Mitigation | -|---|---:|---| -| data migration corruption | Critical | backup, journal, idempotence, failure injection | -| updater defect | Critical | signatures, staging, rollback, RC rehearsal | -| unrestricted IPC | Critical | typed allowlist, schemas, fuzzing | -| CEF packaging complexity | High | incremental cross-platform CI | -| Chromium security cadence | High | automated monitoring/update lane | -| browser-runtime security patch delay | Critical/High | explicit SLA, automated freshness checks, emergency lane | -| CEF/Rust/C++ lifetime defects | High | minimal wrapper, competency harness, documented ownership | -| memory regression | High | process attribution, soak, budgets | -| GPU regression | High | matrix, recovery, diagnostics | -| low-end/resource regression | High | L1/L2 budgets, resource-admission, low-end CI/field lane | -| documentation drift | High | ownership tiers, generated facts, drift CI, freshness SLO | -| feature parity drift | High | machine-readable parity ledger | -| two-runtime transition burden | High | short-lived parity period, feature freeze | -| accessibility regression | High | early smoke + final certification | -| oversized binaries/low-end cost | Medium/High | low-end benchmark, lazy startup | -| **desktop project-text-at-rest encryption gap (ex-#356)** | **High** | **tracked forward to Wave 7; see risk register for owner/status** | - -Every P0/P1 risk must have an owner, test and exit condition. - -Operational quality evidence must therefore span more than ordinary CI: - -```text -CI -+ executable competency evidence -+ documentation freshness -+ packaged artifacts -+ runtime measurements -+ high-end GPU evidence -+ low-end resource evidence -+ security-SLA status -+ field evidence -+ recovery evidence -``` - ---- - -# 77. Safe Mode and crash-loop prevention - -CEF Stable should eventually provide Safe Mode. - -Potential Safe Mode behavior: - -- reduced effects; -- optional GPU fallback; -- plugins disabled if plugins exist; -- nonessential AI deferred; -- derived indexes delayed; -- clean CEF profile option; -- project recovery tools. - -If renderer crashes repeatedly at startup: - -```text -detect crash loop -→ preserve durable state -→ start Safe Mode -→ expose recovery/diagnostics -``` - -Profile corruption recovery must never delete canonical project data. - ---- - -# 78. Supportability - -Create a CEF-specific issue template requesting: - -- WorldScript version; -- CEF/Chromium version; -- OS/kernel; -- desktop environment; -- Wayland/X11; -- GPU/driver; -- diagnostics output; -- project size class; -- repro steps; -- motion/transparency settings. - -This turns future field reports into actionable evidence rather than generic "slow/frozen" descriptions. - ---- - -# 79. Supply-chain and security operations - -CEF increases the binary dependency surface. - -Maintain: - -- CEF/Chromium inventory; -- Rust SBOM/dependency inventory; -- Node dependency inventory; -- third-party notices; -- accepted-risk register; -- secret scanning; -- vulnerability scanning; -- release provenance. - -A critical browser-runtime CVE must have an internal response process. - ---- - -# 80. Documentation set - -Create/maintain (paths adapted to the `docs/cef/` layout adopted at Wave 0 — see `docs/cef/OWNERSHIP.yaml` for the authoritative live list): - -```text -docs/architecture/cef-runtime.md -docs/architecture/desktop-platform-contract.md -docs/architecture/worldscript-core.md -docs/cef/security.md -docs/cef/ipc.md -docs/cef/storage.md -docs/cef/migration.md -docs/cef/packaging.md -docs/cef/updater.md -docs/cef/diagnostics.md -docs/cef/testing.md -docs/cef/performance.md -docs/cef/release-gates.md -docs/cef/security-patch-sla.md -docs/cef/low-end-qualification.md -docs/cef/OWNERSHIP.yaml -docs/cef/knowledge/cef-rust-binding-cookbook.md -docs/cef/knowledge/threading-and-lifetimes.md -docs/cef/knowledge/debugging-and-crash-playbook.md -docs/cef/knowledge/linux-runtime-notes.md -docs/architecture/native-readiness.md -docs/architecture/language-boundaries.md -``` - -Documentation must describe actual current behavior, not intended future behavior as if already shipped. - ---- - -# 80.1 Documentation ownership and drift control - -The CEF program intentionally requires substantial documentation because lifecycle, FFI, security, migration, packaging and operational knowledge must not live only in code or individual memory. - -That documentation volume creates its own risk: - -> **Stale documentation can be more dangerous than missing documentation.** - -Documentation therefore requires ownership, evidence linkage and automated freshness controls. - -## 80.1.1 Documentation ownership metadata - -Critical documents should declare or be represented in a central manifest with: - -```text -Owner role -Backup owner/reviewer role -Last verified against WorldScript version/commit -Last verified against CEF version -Review cadence -Related tests/CI jobs -``` - -## 80.1.2 Document classes - -### Tier A — release/security critical -- CEF integration/lifetimes; -- sandbox/security; -- updater; -- storage/migration; -- crash recovery; -- Linux runtime compatibility. - -### Tier B — architecture/reference -- DesktopPlatform; -- Native-readiness; -- language boundaries. - -### Tier C — explanatory/how-to -Lower enforcement. - -## 80.1.3 Automated drift checks - -Create a command such as: - -```text -pnpm docs:cef-check -``` - -Validate: - -- documented app version vs package version; -- documented CEF version vs pinned runtime; -- IPC list vs generated registry; -- runtime capabilities vs manifest; -- platform/package list vs release matrix; -- overdue verification date; -- missing owner metadata; -- missing related-test reference for Tier A docs. - -Run it in CI. - -**Deferred at Wave 0** (see `docs/cef/OWNERSHIP.yaml`): building even a minimal stub now would add an npm script and CI job with nothing real to check drift against yet, since no CEF code exists. Recorded as planned/not-implemented rather than silently omitted; revisit at Wave 1. - -## 80.1.4 Generate facts, write rationale - -Generate or validate machine facts from source where practical: - -```text -CEF version -Chromium version -protocol methods -capability list -release channels -package targets -``` - -Human docs focus on rationale, invariants, trade-offs, failure semantics and recovery. - -## 80.1.5 Change-triggered documentation review - -Examples: - -```text -desktop-cef/** → CEF lifecycle/architecture docs -worldscript-storage/** → storage/migration docs -worldscript-crypto/** → crypto/security docs -updater/** → updater/runbook -CEF version bump → Linux compatibility + binding cookbook + crash/sandbox assumptions -``` - -CI may accept relevant doc changes or explicit PR metadata: `docs reviewed — no change required`. - -## 80.1.6 Documentation freshness SLO - -Suggested internal policy: - -- Tier A: every 90 days or relevant dependency/architecture change, whichever comes first; -- Tier B: every 180 days or major architecture change; -- Tier C: as needed. - -## 80.1.7 Documentation debt ledger - -Track: - -```text -DOC-DEBT -owner -document -reason -risk -due date -``` - -High-risk Tier A drift becomes a release blocker. - -## 80.1.8 Ownership must survive personnel changes - -Prefer role/subsystem ownership and CODEOWNERS mapping over one person's name. - ---- - -# 81. Developer experience - -Target commands, exact naming subject to implementation: - -```text -pnpm cef:dev -pnpm cef:build -pnpm cef:test -pnpm cef:smoke -pnpm cef:learning-harness -pnpm cef:low-end-smoke -pnpm cef:security-sla-check -pnpm docs:cef-check -pnpm cef:package -``` - -None of these exist yet as of Wave 0 — `package.json` has only `tauri`, `tauri:dev`, `tauri:build`, `dev:tauri`. - -Provide reproducible bootstrap instructions and verified CEF binary checksums. - -Cache CEF by exact version/platform/architecture. - ---- - -# 82. Static architecture guardrails - -Once DesktopPlatform exists, add automated checks preventing direct `@tauri-apps/*` imports outside approved transitional adapters. - -Similarly: - -- reject unknown IPC methods; -- validate generated contracts; -- enforce schema migration discipline; -- scan diagnostics for forbidden secret fields. - ---- - -# 83. Renderer-neutral project commands - -Long-term, evolve toward semantic operations: - -```text -UpdateSection -AddCharacter -MoveBinderNode -CreateSnapshot -RenameProject -``` - -rather than whole-state replacement for every change. - -Benefits: - -- bounded IPC; -- smaller persistence work; -- deterministic undo possibilities; -- future Native-v2 reuse. - -Do not force a full command-system rewrite before CEF needs it. - ---- - -# 84. Canonical data vs derived data - -Classify: - -### Canonical -- project/manuscript; -- user settings; -- user-defined entities; -- explicit version data. - -### Derived/rebuildable -- search index; -- analytics mirrors; -- RAG embeddings where reproducible; -- caches. - -Derived-data failure must not block critical project saves unnecessarily. - ---- - -# 85. Backup and repair - -Long-term consider a renderer-independent Rust repair CLI capable of: - -- verify project; -- inspect storage; -- migrate; -- rebuild derived indexes; -- export diagnostics; -- recover from partial states. - -This benefits CEF today and Native v2 later. - ---- - -# 86. Definition of Done for each CEF workstream - -A workstream is not complete because code compiles. - -Done means, where applicable: - -- implementation; -- unit tests; -- integration tests; -- failure-path tests; -- diagnostics; -- docs; -- migration; -- packaged validation; -- rollback/recovery consideration; -- no unresolved high-severity review finding. - ---- - -# 87. Final GO / NO-GO dossier - -Before CEF Stable, produce a formal dossier containing: - -1. current release candidate SHA/version; -2. feature-parity ledger; -3. unresolved issues; -4. accepted risks; -5. security audit results; -6. CEF/Chromium vulnerability status; -7. IPC/sandbox verification; -8. persistence/durability results; -9. migration rehearsal; -10. encryption/credential results; -11. updater rehearsal; -12. rollback rehearsal; -13. packaged platform results; -14. memory/soak results; -15. GPU results; -16. performance vs PWA/Tauri; -17. accessibility/i18n/IME results; -18. field beta evidence; -19. Tauri fallback readiness; -20. final GO/NO-GO recommendation. - ---- - -# 88. Definition of success - -The CEF program succeeds when WorldScript Desktop has: - -- a controlled Chromium runtime; -- no production dependency on Linux WebKitGTK; -- a renderer-independent critical core; -- durable storage owned outside the renderer; -- complete desktop encryption/credential design; -- mature signed updater; -- reliable rollback; -- process-attributed diagnostics; -- renderer crash recovery; -- packaged E2E; -- measurable bounded memory behavior; -- an explicit, functioning Chromium/CEF security-response SLA; -- low-end/resource-constrained qualification as a first-class release dimension; -- CI-enforced CEF competency/lifecycle evidence; -- owned, freshness-checked critical documentation; -- cross-platform field evidence; -- preserved accessibility and internationalization; -- a clear path to retire Tauri; -- a core reusable by the later separate Native implementation. - ---- - -# 89. Explicit anti-goals - -The CEF program is **not**: - -- a React rewrite; -- a simultaneous egui rewrite; -- a "bundle Chromium and hope" exercise; -- a reason to weaken sandbox/CSP; -- a reason to ignore current application-side memory amplification; -- a reason to abandon PWA quality; -- a reason to blindly merge stale Tauri PRs; -- a reason to duplicate business rules in native code and JS. - ---- - -# 90. Immediate execution order - -The immediate next actions should be: - -1. Commit the CEF ADR. **[Done — ADR-0019]** -2. Commit this perfected roadmap. **[Done — this file]** -3. Reconcile #352–#356 against current main. **[Done — §65, verified against real `gh` state; execution of the disposition is a separate confirmed step]** -4. Inventory all direct `@tauri-apps/*` imports. **[Done — `docs/cef/TAURI-COUPLING-INVENTORY.md`]** -5. Inventory all Tauri capabilities and runtime assumptions. **[Done — §36]** -6. Establish the CEF/Rust competency matrix and knowledge-building artifacts. **[Done — `docs/cef/CEF-RUST-COMPETENCY-MATRIX.md`, `docs/cef/knowledge/`]** -7. Define DesktopPlatform contracts. **[Wave 1]** -8. Produce the first Native-Readiness/UI-vs-domain-state scorecard. **[Wave 1]** -9. Route current behavior through a Tauri adapter without changing semantics. **[Wave 1]** -10. Run the CEF integration-choice spike. **[Wave 2]** -11. Build the isolated CEF learning/lifecycle harness. **[Wave 2]** -12. Bootstrap CEF and load current `dist/`. **[Wave 2]** -13. Implement secure resource origin. **[Wave 3]** -14. Prove early accessibility integration. **[Wave 3]** -15. Implement process/runtime/crash diagnostics foundation. **[Wave 3]** -16. Inventory Linux runtime dependencies and launch-test both supported display-server paths. **[Wave 3]** -17. Build PWA/Tauri/CEF #332 benchmark harness. **[Wave 3]** -18. Run the differential. **[Wave 3]** -19. Implement typed IPC v1 only after the CEF Competency Gate passes. **[Wave 4]** -20. Implement project persistence vertical slice. **[Wave 5]** -21. Establish application updater + CEF servicing strategy and explicit security-patch SLA. **[Wave 11, §34.1]** -22. Add documentation ownership metadata and CEF doc-drift validation. **[Ownership manifest done at Wave 0; drift-check tooling deferred to Wave 1, §80.1.3]** -23. Add packaged CEF CI plus `cef-learning-harness`/competency enforcement. **[Wave 2]** -24. Establish L1/L2 low-end reference devices and baseline measurements. **[Wave 12/15]** -25. Continue wave-by-wave with Native-Readiness checks at every architecture-changing gate. **[Ongoing]** - ---- - -# 91. Strategic directive - -> **WorldScript Studio will migrate its production desktop runtime from the current Tauri system-webview architecture toward a Chromium Embedded Framework based desktop runtime. CEF is the primary next-generation desktop target. The existing React/Vite frontend will be preserved and progressively decoupled from Tauri through typed platform abstractions and an increasingly authoritative Rust core. Persistence, encryption, migrations, native tasks, diagnostics, updater behavior, and other critical desktop responsibilities will be designed to be renderer-independent.** -> -> **The CEF implementation will originate from current `main`, will be delivered incrementally, and will be validated against both the Chromium PWA and the current Tauri runtime. It will be subjected to packaged-artifact, security, memory, GPU, performance, accessibility, migration, updater, crash-recovery, and field-validation gates. Tauri remains a transitional reference/fallback until CEF has demonstrated full required parity and production maturity.** -> -> **No production `egui/wgpu` implementation will begin while the CEF program is incomplete. WorldScript Native becomes eligible only after CEF is stable, the Tauri transition is complete, the wider application has been comprehensively hardened and substantially perfected, and the renderer-neutral core is mature enough to serve as the authoritative foundation for a second UI implementation.** -> -> **The objective is not merely to replace WebKitGTK. The objective is to establish a durable WorldScript platform whose desktop runtime is controlled, measurable, secure, recoverable, high-performance, and architecturally prepared for a later native presentation without duplicating product logic.** - - ---- - -# Appendix A — Master implementation checklist - -## A.1 Architecture - -```text -[x] CEF ADR committed -[x] roadmap committed -[x] current-main architecture baseline documented -[x] Tauri capability inventory complete -[x] direct Tauri import inventory complete -[ ] DesktopPlatform contract approved -[ ] renderer/core responsibility matrix approved -[ ] CEF integration approach selected -[ ] CEF lifetime/threading model documented -[x] Native-v2 gate documented -``` - -## A.2 CEF bootstrap - -```text -[ ] CEF initializes -[ ] Chromium version exposed in diagnostics -[ ] bundled React/Vite app loads -[ ] secure internal origin implemented -[ ] external navigation intercepted -[ ] sandbox works in production package -[ ] clean shutdown works -[ ] crash callbacks wired -``` - -## A.3 IPC - -```text -[ ] protocol version -[ ] typed methods -[ ] generated/shared types -[ ] request IDs -[ ] structured errors -[ ] timeout -[ ] cancellation -[ ] progress events -[ ] method allowlist -[ ] size limits -[ ] binary strategy -[ ] backpressure -[ ] unknown-method rejection -[ ] malformed-request tests -``` - -## A.4 Persistence - -```text -[ ] explicit dirty state -[ ] single save coordinator -[ ] lifecycle flush coalesced -[ ] no duplicate whole-state serialization -[ ] canonical native project storage -[ ] native settings storage -[ ] active-project persistence -[ ] durable write semantics documented -[ ] disk-full behavior -[ ] permission-failure behavior -[ ] corruption detection -[ ] recovery -``` - -## A.5 Snapshots - -```text -[ ] metadata separated -[ ] listing does not load full payloads -[ ] count budget -[ ] byte budget -[ ] pruning -[ ] streaming/efficient word count -[ ] serialization overlap eliminated -[ ] multi-file commit semantics where needed -[ ] failure injection -``` - -## A.6 Crypto - -```text -[ ] versioned envelope -[ ] AEAD -[ ] AAD -[ ] passphrase/KDF policy -[ ] session lock -[ ] project text -[ ] settings if sensitive -[ ] Codex/RAG as required -[ ] images -[ ] Binder binary -[ ] migration journal -[ ] admission lock -[ ] corruption handling -[ ] recovery UI -``` - -## A.7 Credentials - -```text -[ ] Windows credential backend -[ ] macOS Keychain backend -[ ] Linux Secret Service backend -[ ] fallback policy -[ ] locked-state behavior -[ ] no plaintext silent downgrade -[ ] no secrets in logs/diagnostics -``` - -## A.8 Desktop shell - -```text -[ ] native dialogs -[ ] menus -[ ] tray -[ ] close-to-tray -[ ] clean quit -[ ] notifications -[ ] clipboard -[ ] deep links -[ ] file associations -[ ] single-instance policy -[ ] window-state persistence -``` - -## A.9 Updater - -```text -[ ] signed manifest -[ ] signed package -[ ] verification -[ ] stable/beta/nightly channels -[ ] progress -[ ] retry -[ ] interrupted download -[ ] staging -[ ] atomic activation -[ ] rollback -[ ] bad-version crash-loop recovery -[ ] CEF/Chromium security-SLA integration -[ ] emergency browser-runtime update lane -[ ] last-known-good runtime rollback -``` - -## A.10 Packaging - -```text -[ ] Linux .deb -[ ] Linux AppImage -[ ] RPM if adopted -[ ] Windows signed installer -[ ] macOS signed app -[ ] macOS notarization -[ ] file associations per platform -[ ] deep links per platform -[ ] uninstall -[ ] upgrade-in-place -``` - -## A.11 Testing - -```text -[ ] core unit -[ ] contract tests -[ ] CEF native integration -[ ] packaged smoke -[ ] packaged E2E -[ ] migration -[ ] update -[ ] rollback -[ ] failure injection -[ ] memory -[ ] GPU -[ ] soak -[ ] a11y -[ ] IME -[ ] RTL -[ ] field beta -[ ] low-end L1/L2 -[ ] CEF learning harness -[ ] security-SLA check -[ ] documentation drift check -``` - ---- - - -# Appendix A.1 — CEF/Rust competency checklist - -Live tracking: `docs/cef/CEF-RUST-COMPETENCY-MATRIX.md`. - -```text -[ ] Process architecture understood/documented -[ ] Message loop decision documented -[ ] Thread affinity documented -[ ] CEF reference-count/lifetime rules documented -[ ] Rust binding unsafe surface reviewed -[ ] Binding API gaps catalogued -[ ] Subprocess packaging proven -[ ] Repeated startup/shutdown harness green -[ ] Renderer crash observation green -[ ] Accessibility smoke green -[ ] Crash-reporting/symbolization smoke green -[ ] Linux dependency inventory complete -[ ] X11/Wayland initial smoke complete -[ ] Upgrade playbook written -[ ] External-expertise escalation path documented -``` - ---- - -# Appendix A.2 — Native-Readiness review template - -For every major architecture PR: - -```text -Domain logic renderer-neutral: PASS / N/A / DEBT -Canonical data outside UI state: PASS / N/A / DEBT -Critical behavior headless-testable: PASS / N/A / DEBT -Browser APIs adapter-contained: PASS / N/A / DEBT -Platform APIs adapter-contained: PASS / N/A / DEBT -Business rules not duplicated: PASS / N/A / DEBT -Semantic command/event available: PASS / N/A / DEBT -Error type renderer-independent: PASS / N/A / DEBT -Durable schema renderer-independent: PASS / N/A / DEBT -Future native consumer blocked by design: NO / EXPLAIN -``` - -Every `DEBT`/`EXPLAIN` item requires an issue or explicit accepted rationale. - ---- - -# Appendix A.3 — Linux compatibility matrix template - -| DE/Display | GPU | Package | Launch | Sandbox | GPU accel | A11y | Suspend/Resume | Soak | -|---|---|---|---|---|---|---|---|---| -| KDE/Wayland | NVIDIA | .deb | | | | | | | -| KDE/Wayland | AMD | .deb | | | | | | | -| KDE/X11 | NVIDIA | .deb | | | | | | | -| GNOME/Wayland | AMD | .deb | | | | | | | -| GNOME/Wayland | Intel | .deb | | | | | | | -| supported variants | ... | AppImage/RPM | | | | | | | - -Every selected CEF upgrade re-runs the compatibility-floor subset. - ---- - -# Appendix A.4 — Go decision boundary - -Default: - -```text -GO IN CORE? NO -GO IN CEF HOST? NO -GO FOR STORAGE/CRYPTO/TASKS? NO -GO FOR UPDATER? NO -``` - -Reconsider only for an independently deployable component with a demonstrated ecosystem/operational advantage and explicit architecture approval. - ---- - -# Appendix A.5 — Crash and update operations checklist - -```text -[ ] Host crash classified -[ ] Renderer crash classified -[ ] GPU/utility crash classified -[ ] Hang detection path exists -[ ] Minidump/crash artifact privacy documented -[ ] Symbols retained per release -[ ] Build IDs/provenance linked -[ ] Crash-loop Safe Mode works -[ ] App updater signed -[ ] App updater rollback works -[ ] CEF/Chromium servicing process documented -[ ] CEF security update response policy documented -[ ] Runtime upgrade compatibility floor checked -``` - ---- - -# Appendix B — Runtime comparison benchmark - -Maintain the same test fixture and app revision across all three runtimes. - -| Metric | PWA Chromium | Tauri/WebKitGTK | CEF Chromium | Target/Decision | -|---|---:|---:|---:|---| -| Cold start | | | | | -| Warm start | | | | | -| Time to interactive | | | | | -| Idle RSS/PSS | | | | | -| Large-project RSS/PSS | | | | | -| Peak save memory | | | | | -| Peak snapshot memory | | | | | -| 30-min memory slope | | | | | -| 2-h memory slope | | | | | -| Settings switch p95 | | | | | -| Writer typing p95 | | | | | -| Writer scroll frame time | | | | | -| Search latency | | | | | -| Autosave duration | | | | | -| Snapshot duration | | | | | -| Alt-Tab 100× | | | | | -| Alt-Tab 500× | | | | | -| Effects ON | | | | | -| Effects OFF | | | | | -| Renderer crashes | | | | | -| GPU crashes | | | | | -| Hard freezes | | | | | -| Manual SIGKILL needed | | | | | -| CJK IME | | | | | -| RTL/BiDi | | | | | -| Screen reader | | | | | - ---- - -# Appendix C — #332 memory investigation acceptance matrix - -## Scenario 1 — Effects matrix - -```text -Transparency OFF / Motion OFF -Transparency ON / Motion OFF -Transparency OFF / Motion ON -Transparency ON / Motion ON -``` - -Record: - -- input responsiveness; -- Settings p95; -- frame time; -- renderer memory; -- GPU process memory; -- stability. - -## Scenario 2 — Background/resume - -```text -cold start -→ 2 min idle -→ 20 Alt-Tabs -→ 5 min idle -→ 20 Alt-Tabs -→ Settings switching -→ 10 min idle -``` - -## Scenario 3 — Snapshot boundary - -```text -launch -→ large project -→ edit -→ cross autosnapshot interval -→ save -→ inspect memory -→ repeat -``` - -## Scenario 4 — Long soak - -```text -2 hours -→ repeated editing -→ repeated saves -→ snapshots -→ AI start/cancel -→ background/resume -``` - -Required CEF result: - -- no WebKit-style catastrophic memory event; -- no equivalent CEF runaway; -- no unrecovered renderer death; -- stable memory plateau. - ---- - -# Appendix D — Process attribution schema - -Diagnostics should conceptually report: - -```json -{ - "runtime": { - "appVersion": "...", - "runtime": "cef", - "cefVersion": "...", - "chromiumVersion": "..." - }, - "processes": [ - { - "role": "native-host", - "pid": 0, - "rssMb": 0, - "pssMb": 0 - }, - { - "role": "browser", - "pid": 0, - "rssMb": 0, - "pssMb": 0 - }, - { - "role": "renderer", - "pid": 0, - "rssMb": 0, - "pssMb": 0 - }, - { - "role": "gpu", - "pid": 0, - "rssMb": 0, - "pssMb": 0 - } - ] -} -``` - -Do not treat exact schema above as final API; preserve the role separation. - ---- - -# Appendix E — Security invariants - -1. Production sandbox is enabled. -2. Renderer has no arbitrary filesystem access. -3. Renderer has no arbitrary shell execution. -4. Renderer does not receive signing material. -5. Unknown IPC methods are denied. -6. Every privileged method validates input. -7. External content never inherits native bridge privilege. -8. Native bridge origin is validated. -9. Secrets never enter routine diagnostics. -10. Update artifacts are cryptographically verified. -11. User project data is not routine telemetry. -12. Crash data is treated as potentially sensitive. -13. Security state cannot silently downgrade. -14. Project import is considered untrusted input. -15. CEF/Chromium security updates are actively governed. - ---- - -# Appendix F — Persistence invariants - -1. A renderer crash cannot be equivalent to project loss. -2. Only one coordinator owns durable project writes. -3. Autosave and shutdown save do not independently full-serialize the same state. -4. Readers never observe an uncommitted generation. -5. Snapshot listing is metadata-only. -6. Derived indexes are rebuildable. -7. Migration is idempotent or resumable. -8. A failed migration preserves a recoverable source. -9. A corrupt derived cache cannot destroy canonical project data. -10. Tauri and CEF cannot concurrently write the same project without explicit ownership. - ---- - -# Appendix G — Suggested early PR sequence - -## PR 1 — `docs(architecture): adopt CEF desktop runtime strategy` - -**Status: this PR.** - -Contents: - -- ADR; -- this roadmap (with §65 verified against real GitHub state); -- risk register, competency matrix, knowledge skeletons, ownership manifest; -- no runtime changes. - -## PR 2 — `chore(desktop): inventory runtime capabilities and Tauri coupling` - -**Status: follows this PR (Wave 0, second half).** - -Contents: - -- machine-readable capability ledger; -- direct `@tauri-apps` import report; -- no behavior change. - -## PR 3 — `refactor(desktop): introduce renderer-neutral platform contracts` - -Contents: - -- interfaces/types; -- tests; -- no CEF yet. - -## PR 4 — `refactor(desktop): route current Tauri behavior through platform adapter` - -Contents: - -- current behavior preserved; -- direct imports reduced. - -## PR 5 — `feat(cef): bootstrap Chromium Embedded Framework host` - -Contents: - -- minimal host; -- existing `dist/`; -- no broad native bridge. - -## PR 6 — `security(cef): add controlled app origin and navigation policy` - -## PR 7 — `feat(cef): add runtime/process diagnostics foundation` - -## PR 8 — `perf(cef): add PWA/Tauri/CEF desktop comparison harness` - -## PR 9 — `feat(cef): add typed desktop protocol v1` - -## PR 10 — `feat(storage): first Rust-owned project persistence vertical slice` - -After these, continue by workstream rather than creating an enormous stacked chain. - ---- - -# Appendix H — CEF integration decision scorecard - -Score each candidate 1–5. - -| Criterion | Rust binding | Thin C++ host | C API | -|---|---:|---:|---:| -| Linux support | | | | -| Windows support | | | | -| macOS support | | | | -| Sandbox | | | | -| Renderer callbacks | | | | -| GPU/crash callbacks | | | | -| Packaging | | | | -| Upstream docs | | | | -| Memory/lifetime safety | | | | -| FFI complexity | | | | -| Maintenance | | | | -| CI complexity | | | | -| Rust-core fit | | | | - -Decision must be recorded, including why rejected alternatives were rejected. - ---- - -# Appendix I — CEF Stable GO/NO-GO template - -## Candidate - -```text -Version: -Commit: -CEF: -Chromium: -Release channel: -``` - -## Correctness - -```text -Open P0: -Open P1: -Data corruption known: -Migration blockers: -``` - -## Runtime - -```text -Hard freeze reproductions: -Renderer crash loop: -GPU crash loop: -Recovery: -``` - -## Memory - -```text -Idle: -Large project: -Save peak: -Snapshot peak: -2-hour slope: -``` - -## Security - -```text -Sandbox: -IPC audit: -Credential audit: -Crypto audit: -Updater signatures: -CEF/Chromium CVEs: -Security-SLA compliance: -Open security exceptions: -``` - -## Packaging - -```text -Linux: -Windows: -macOS: -``` - -## Accessibility/i18n - -```text -Screen reader: -Keyboard: -IME: -RTL: -Scaling: -``` - -## Field - -```text -Linux NVIDIA: -Linux AMD: -Linux Intel: -Windows: -macOS: -``` - -## Decision - -```text -GO / NO-GO -Rationale: -Accepted risks: -Required follow-up: -``` - ---- - -# Appendix J — Tauri retirement checklist - -```text -[ ] CEF is Stable -[ ] CEF is default Desktop runtime -[ ] migration adoption confirmed -[ ] no Tauri-only critical feature remains -[ ] no updater dependency on Tauri remains -[ ] old project-data compatibility tests exist -[ ] docs updated -[ ] release artifacts no longer require Tauri -[ ] direct Tauri imports removed -[ ] Tauri CI removed -[ ] Tauri dependencies removed -[ ] src-tauri archived/removed as planned -[ ] support documentation updated -``` - -Retirement should be its own reviewed program milestone, not cleanup hidden inside another PR. - ---- - -# Appendix K — Native-v2 admission checklist - -This checklist remains intentionally locked during the CEF program. - -```text -[ ] CEF Stable -[ ] Tauri retired/effectively retired -[ ] storage core mature -[ ] crypto core mature -[ ] migration model mature -[ ] project schema stable -[ ] AI core mature -[ ] task runtime mature -[ ] packaged E2E mature -[ ] updater mature -[ ] diagnostics mature -[ ] no open P0 -[ ] no open unaccepted P1 -[ ] large-project performance strong -[ ] accessibility mature -[ ] i18n/IME/RTL mature -[ ] CEF can serve as behavioral reference -[ ] explicit engineering capacity exists -[ ] formal GO decision recorded -``` - -Only after this checklist passes may a separate `WorldScript Native / egui+wgpu` roadmap move from deferred concept to implementation. - ---- - -# Appendix L — Long-term architectural invariants - -1. **Canonical product state is independent of UI renderer.** -2. **Durable project state survives renderer failure.** -3. **Native privilege is explicit and least-privilege.** -4. **Storage and crypto are reusable by future runtimes.** -5. **Large workloads do not require repeated whole-state copies.** -6. **Every long-running task is cancellable or has documented non-cancellability.** -7. **Every critical migration has recovery semantics.** -8. **Every supported platform is validated as a packaged artifact.** -9. **Performance claims are based on measurements.** -10. **Security claims describe real deployed behavior.** -11. **Diagnostics are useful without exposing creative content or secrets.** -12. **Future Native v2 reuses product logic rather than reproducing it.** - ---- - -# Appendix M — Visionary end-state - -A mature WorldScript platform should be able to survive failures at layers that currently look inseparable. - -Conceptually: - -```text -Renderer crashes? -→ project remains safe. - -GPU process crashes? -→ runtime diagnoses and recovers. - -Update fails? -→ prior version boots. - -Migration is interrupted? -→ journal resumes or rolls back. - -Search index corrupts? -→ rebuild it. - -CEF profile corrupts? -→ reset profile without touching manuscripts. - -User moves from old Tauri? -→ verified migration. - -Future Native client opens same project? -→ same core/storage semantics. - -Large manuscript grows? -→ memory remains bounded and observable. -``` - -At that point the renderer ceases to be the architecture. - -It becomes one replaceable presentation surface over a mature product core. - -CEF is the immediate path to that state because it gives WorldScript Studio a controlled Chromium runtime while preserving its strongest existing UI investment. The later native Rust implementation becomes viable precisely because CEF first forces storage, security, tasks, diagnostics and product semantics into clean, reusable boundaries. - ---- - -# Closing statement - -This roadmap deliberately chooses **sequential ambition** over simultaneous architectural experimentation. - -First: - -> **Build CEF completely, rigorously, securely and measurably.** - -Then: - -> **Harden and perfect the entire application until the desktop product is robust enough to act as a reference implementation.** - -Only after that: - -> **Begin the separate WorldScript Native `egui/wgpu` product against the mature shared core.** - -This sequencing maximizes the chance that both long-term architectures become excellent rather than leaving WorldScript with two partially finished desktop systems. - - - ---- - -# Appendix M.1 — Chromium/CEF security SLA checklist - -```text -[ ] Upstream release/advisory monitoring automated -[ ] 24h actively-exploited triage path documented -[ ] Critical patch target documented -[ ] High patch target documented -[ ] Emergency release lane tested -[ ] Patch-deferral template exists -[ ] Open exceptions have owners/due dates -[ ] Security freshness visible in dashboard -[ ] CI warns on overdue exception -[ ] Last-known-good runtime retained for rollback -``` - ---- - -# Appendix M.2 — Low-end qualification checklist - -```text -[ ] L1 reference system selected -[ ] L2 reference system selected -[ ] cold start baseline -[ ] idle memory baseline -[ ] 100k Writer baseline -[ ] 500k Writer baseline -[ ] autosave peak -[ ] snapshot peak -[ ] local AI disabled baseline -[ ] local AI admission tested -[ ] visual reduction measured -[ ] battery/thermal behavior reviewed -[ ] regression thresholds stored -``` - ---- - -# Appendix M.3 — Documentation ownership manifest example - -See the real, live manifest at `docs/cef/OWNERSHIP.yaml` (Wave 0). Template: - -```yaml -documents: - - path: docs/cef/security.md - tier: A - owner_role: desktop-security - backup_role: cef-runtime - last_verified: - worldscript: "vX.Y.Z" - cef: "X.Y" - review_days: 90 - related_ci: - - cef-security - - cef-doc-drift - - - path: docs/cef/knowledge/threading-and-lifetimes.md - tier: A - owner_role: cef-runtime - backup_role: rust-core - review_days: 90 - related_ci: - - cef-learning-harness - - cef-competency-gate -``` - ---- - -# Appendix M.4 — Competency CI release invariant - -A foundational CEF change is not eligible for merge if: - -```text -cef-learning-harness != PASS -or -cef-competency-gate != PASS -or -required Tier A docs are overdue/missing -``` - -Exceptions require explicit maintainer override with written rationale and a short expiry date. - - ---- - -# Appendix N — Primary technical reference baseline - -This roadmap should be kept synchronized with the selected CEF version. The following upstream references are particularly relevant to the integration assumptions made here: - -- CEF Documentation index — architecture, API versioning, crash reporting, sandbox setup and installer documentation: - https://chromiumembedded.github.io/cef/ - -- CEF General Usage — multi-process architecture, binary distributions, platform layout and Linux dependency guidance: - https://chromiumembedded.github.io/cef/general_usage.html - -- CEF Rust bindings (`cef` crate) — exact version must be pinned by the implementation ADR: - https://docs.rs/cef/latest/cef/ - -- CEF BrowserHost accessibility API — verify against the exact selected CEF release: - https://cef-builds.spotifycdn.com/docs/ - -- CEF installer/runtime documentation — currently includes platform-specific runtime installer concepts; it must not be confused with the cross-platform WorldScript application updater: - https://chromiumembedded.github.io/cef/installer.html - -**Rule:** Upstream behavior can change. During each CEF upgrade, revalidate version-specific assumptions instead of treating this appendix as timeless truth. diff --git a/docs/cef/TAURI-COUPLING-INVENTORY.md b/docs/cef/TAURI-COUPLING-INVENTORY.md deleted file mode 100644 index b5aa1f0d..00000000 --- a/docs/cef/TAURI-COUPLING-INVENTORY.md +++ /dev/null @@ -1,92 +0,0 @@ -# Tauri Coupling Inventory - -**Companion to:** [`ROADMAP-CEF-DESKTOP-MIGRATION.md`](ROADMAP-CEF-DESKTOP-MIGRATION.md) §36 (capability parity) · [`tauri-coupling-inventory.json`](tauri-coupling-inventory.json) (machine-readable) · [ADR-0019](../adr/0019-cef-desktop-runtime-strategy.md) -**Verified:** 2026-08-18, against `main` (v1.27.1), via `rg` — not estimated or carried over from an earlier, less precise pass. -**Purpose (roadmap §90 step 4/5, Appendix G PR 2):** establish exactly how large and how centralized today's Tauri coupling is, before Wave 1 begins building the `DesktopPlatform` abstraction that will eventually replace it. - -## Methodology - -```bash -# Real @tauri-apps/* imports (static or dynamic) -rg -l "from ['\"]@tauri-apps|import\(['\"]@tauri-apps" -g '*.ts' -g '*.tsx' -g '!*.test.ts' -g '!*.test.tsx' -g '!tests/**' . - -# Files that only check Tauri presence, with no direct API import — TWO detection methods, both -# required (the first pass only covered the raw-global check and undercounted; corrected 2026-08-18): -rg -l "isTauriRuntime\(\)" -g '*.ts' -g '*.tsx' -g '!*.test.ts' -g '!*.test.tsx' -g '!tests/**' . # the helper function -rg -l "__TAURI_INTERNALS__|__TAURI__|__TAURI_METADATA__" -g '*.ts' -g '*.tsx' -g '!*.test.ts' -g '!*.test.tsx' -g '!tests/**' . # raw globals (e.g. register-sw.ts) -# ...both diffed against the first list; comment-only matches (e.g. a docstring mentioning -# isTauriRuntime()) manually excluded after inspection. -``` - -Per-file API breakdown was extracted with `rg -oE "@tauri-apps/[a-zA-Z0-9/_-]*"` on each matched file. Full structured result: [`tauri-coupling-inventory.json`](tauri-coupling-inventory.json). - -## Summary - -Tauri coupling is **real but not centralized**. `services/tauriRuntime.ts` — the module whose name suggests it's the abstraction layer — is actually a thin 64-line, 5-function facade (`isTauriRuntime`, `getDesktopOs`, `applyDesktopRuntimeFlags`, `getTauriAppVersion`, `openTauriDataDirectory`). The real `@tauri-apps/*` API surface is imported directly across **16 non-config source files**, plus one build-config file (`vite.config.ts`, externalization only — not app-level coupling). - -| Category | File count | Files | -|---|---|---| -| Direct `@tauri-apps/*` API imports | 16 | see table below | -| Build-time externalization only | 1 | `vite.config.ts` | -| Detection-only (`isTauriRuntime()`/`__TAURI__`, no direct API import) | 13 | `components/settings/AiProviderCard.tsx`, `components/settings/DataSection.tsx`, `components/settings/DesktopSection.tsx`, `components/settings/FeatureFlagsSection.tsx`, `components/settings/GeneralSections.tsx`, `hooks/useNativeNotifications.ts`, `register-sw.ts`, `services/ai/localAiDeviceProfiler.ts`, `services/aiProviderService.ts`, `services/appBootstrap.ts`, `services/factoryResetService.ts`, `services/ollamaService.ts`, `services/storageService.ts` | -| Ambient type declarations | 1 | `types/tauri-plugins.d.ts` | - -## Direct API coupling by category - -| Category | Files | `@tauri-apps/*` surface | -|---|---|---| -| **Filesystem + dialog + invoke** (largest single point) | `services/fs/fsCore.ts` | `api/core`, `api/path`, `plugin-dialog`, `plugin-fs` | -| **Invoke / native commands** | `services/tauriTaskBridge.ts`, `services/pandocTauri.ts`, `services/lora/loraTrainingService.ts` | `api/core` (+ `api/event`, `plugin-dialog` for LoRA) | -| **Events** | `services/tauriDeepLink.ts`, `services/tauriMenuService.ts` | `api/event` | -| **Filesystem (secondary)** | `services/logger.ts`, `services/tauriDeepLink.ts` | `api/path`, `plugin-fs` | -| **Window / menu / tray (native UI)** | `services/desktop/desktopMenu.ts`, `services/desktop/desktopTray.ts`, `services/tauriTrayService.ts` | `api/menu`, `api/tray`, `api/app`, `api/window` | -| **Notifications** | `services/desktop/desktopNotifications.ts` | `plugin-notification` | -| **Updater / process lifecycle** | `hooks/useTauriUpdater.ts`, `App.tsx` | `plugin-updater`, `api/app`, `plugin-process` | -| **HTTP (CORS bypass)** | `services/ai/fetchAdapter.ts`, `services/localServerHttp.ts` | `plugin-http` | -| **Runtime detection + version + shell** | `services/tauriRuntime.ts` | `api/app`, `api/path`, `plugin-shell` | - -`services/tauriRuntime.ts` itself uses `api/app` (version) and `plugin-shell` (open data directory) — it is not merely a detection stub, but it is far from a comprehensive `DesktopPlatform`-style abstraction; most callers still reach past it into `@tauri-apps/*` directly. - -## `src-tauri/` Rust scaffold - -Standard, fairly small Tauri v2 layout as of `main`: - -```text -src-tauri/ -├── build.rs -├── capabilities/default.json -├── Cargo.toml / Cargo.lock -├── Entitlements.plist -├── fuzz/Cargo.toml (filename-sanitization fuzz harness) -├── osv-scanner.toml -├── src/ -│ ├── commands/ -│ │ ├── mod.rs -│ │ └── task_supervisor.rs (defines worldscript_task_supervisor_ping/submit — registered in lib.rs; active native task-dispatch surface used by services/tauriTaskBridge.ts) -│ ├── lib.rs -│ ├── lora.rs -│ ├── main.rs -│ └── pandoc.rs -├── tauri.conf.json -└── icons/ (11 image assets, no coupling) -``` - -6 Rust source files (corrected 2026-08-18 — the `commands/` module was omitted from the first pass). This is the entire native surface being migrated — small relative to the JS/TS coupling above, but it is where `WS-CEF-*` Rust work (§66 workstream catalogue) eventually lands. - -## `package.json` - -Existing scripts: `tauri`, `tauri:dev`, `tauri:build`, `dev:tauri`. No `cef:*` scripts exist — confirmed at Wave 0, and none are added by this PR (roadmap §81, Developer Experience, is Wave 2+ scope). - -## How this maps to the roadmap - -- §36 (Desktop capability parity inventory) cites this file's findings directly, file-path-annotated. -- §90 step 4/5 ("Inventory all direct `@tauri-apps/*` imports" / "Inventory all Tauri capabilities and runtime assumptions") — this document and the JSON ledger are that inventory. -- Wave 1 (`DesktopPlatform` boundary) is where this coupling starts getting funneled through typed adapters — not touched by this Wave 0 PR. - -## Not in scope for this document - -No behavior changes, no refactoring, no new abstractions. This is a snapshot, not an implementation — re-run the methodology above before relying on these counts once Wave 1 work begins moving files. - -## Update: Wave 1 PR B re-verification - -The methodology above was re-run at the Wave 1 PR B checkpoint (`packages/desktop-contracts` + `services/desktopPlatform.ts` + consumer migration). Of the 16 direct-coupling files listed above, 13 now route through `desktopPlatform` (all except `services/ai/fetchAdapter.ts` and `services/localServerHttp.ts`, permanently out of the boundary by design — HTTP is not a `DesktopPlatform` facet — and `services/logger.ts`, scheduled debt pending a logging-port decoupling; see `docs/architecture/native-readiness.md`'s Wave 1 PR B snapshot). The counts and per-category tables above are the **Wave 0 baseline** and are intentionally left as-is for historical comparison — the current, mechanically-enforced state lives in `tauri-coupling-inventory.json`'s `directApiCoupling` (now-remaining coupling) and `resolvedWave1PrB` (what migrated) arrays, and is continuously verified going forward by `pnpm run guardrail:desktop-imports` in CI, not by re-running this document's manual `rg` methodology. diff --git a/docs/cef/knowledge/binding-upgrade-playbook.md b/docs/cef/knowledge/binding-upgrade-playbook.md deleted file mode 100644 index 22f7974a..00000000 --- a/docs/cef/knowledge/binding-upgrade-playbook.md +++ /dev/null @@ -1,40 +0,0 @@ -# CEF/Binding Upgrade Playbook - -**Status:** Written proactively (2026-08-19), before any real CEF/Chromium version upgrade has happened — not left blank waiting for one. The competency gate (`docs/cef/CEF-RUST-COMPETENCY-MATRIX.md`) requires "upgrade playbook exists" before Wave 4's privileged IPC proceeds; leaving this genuinely empty until the first upgrade would create a circular dependency (playbook can't exist until an upgrade happens, but nothing forces an upgrade to happen). Every step below maps to a real script or CI job that already exists in this repo from Wave 2's own proof work — this is an executable checklist synthesized from real evidence, not a hypothetical. **After the first real upgrade, enrich this with what actually broke and what the checklist missed** — that update is still required and this document is not "done" just because it has content now. -**Scope:** The repeatable procedure for upgrading the pinned CEF/Chromium version and/or the Rust binding crate version — what to re-validate, what evidence to collect, and how the competency-regression policy (roadmap §61.1.6) applies. -**Tier:** A (release/security-critical) — see [`../OWNERSHIP.yaml`](../OWNERSHIP.yaml). -**Roadmap context:** [`../ROADMAP-CEF-DESKTOP-MIGRATION.md`](../ROADMAP-CEF-DESKTOP-MIGRATION.md) §34 (CEF version governance), §34.1 (security patch SLA), §44.4 (compatibility-floor gate), §61.1.6 (competency regression policy). - -## Routine upgrade procedure - -Every step in this chain is a real command or CI step that already exists — this is not aspirational. - -1. **Change the pin.** Edit `scripts/cef/cef-version.json` only (`cefVersion`, `filename`, `sha1`, `sizeBytes`) — per that file's own `$comment`, values come straight from `https://cef-builds.spotifycdn.com/index.json`, never hand-computed. This is deliberately the *only version-pin file* a routine version bump touches (step 14 below still updates docs in the same PR — "only file" refers to the pin itself, not the complete procedure); `scripts/cef/fetch-cef-sdk.mjs` reads it, nothing hardcodes a version. -2. **Verify checksum integrity before trusting the new pin.** `node scripts/cef/fetch-cef-sdk.mjs --cache-dir .cef-cache` downloads and compares the archive's SHA-1 against the pinned `sha1` — a mismatch deletes the bad download and fails loudly (see `verifyArchive()`), it does not silently proceed. This is a checksum/integrity check against a value this repo itself maintains, not a cryptographic signature or provenance verification (no independent trust anchor is involved) — `verifyArchive()` does not check `sizeBytes`, only `sha1`. -3. **Fetch.** Same command as step 2 — idempotent, safe to re-run. -4. **Build.** `cmake -S -B build -DCMAKE_BUILD_TYPE=Release && cmake --build build --target worldscript_host --parallel $(nproc)` (`.github/workflows/cef-learning-harness.yml`'s "Configure + build worldscript_host" step). A build failure here is the first, cheapest signal of an API-incompatible upgrade — cheaper than discovering it at runtime. -5. **Lifecycle harness.** `xvfb-run -a node scripts/cef/run-launch-cycle-proof.mjs --cycles 3` — repeated start/close cycles, FFI boundary (`rust_core ping = 424242`), real rendering (exact title check), accessibility-state request. Per roadmap §61.1.6, **a CEF upgrade that breaks this harness is automatically not merge-eligible** until fixed or the harness is deliberately updated with documented, understood upstream-behavior changes — never silently. -6. **Sandbox.** `node scripts/cef/check-linux-sandbox-inventory.mjs` (diagnostic feasibility today; once the follow-up sandbox-enable PR lands, this step becomes a real sandbox-status proof, not just a feasibility check — see `cef-architecture-primer.md`'s "Sandbox configuration" section for that PR's own acceptance bar). Re-run regardless of upgrade type — an upstream Chromium change can alter sandbox requirements (roadmap §44.4 asks this explicitly). -7. **Crash reporting.** The launch-cycle harness's own `runCrashReportingProofCycle()` (same script, step 5) — `chrome://crash`, `CefCrashReportingEnabled()`, `OnRenderProcessTerminated` with `TS_PROCESS_CRASHED`, a real Crashpad `.dmp` file under `BREAKPAD_DUMP_LOCATION`. -8. **Symbolization.** `xvfb-run -a node scripts/cef/run-symbolization-proof.mjs ` — `--debug-crash-self`, SIGABRT, dump, `dump_syms -s`, `minidump-stackwalk --json`, `crashing_thread.frames[].function` resolves. Requires `worldscript_host` built with `-g` (`apps/desktop-cef/CMakeLists.txt`'s `target_compile_options`) — do not accidentally drop that flag while chasing an upgrade-induced build fix. -9. **Accessibility-state enablement.** Covered by step 5's `accessibility_state_requested = true` check — this asserts *state enablement* only (`SetAccessibilityState(STATE_ENABLED)` was requested), not focus behavior or platform accessibility-tree observability (AT-SPI); see the competency matrix for why the tree/focus half is separate, unattempted scope. Do not read this step as a "basic accessibility/focus" check without extending it with an explicit focus assertion first. -10. **X11.** Covered by step 5 — the harness runs under `xvfb-run` (X11) by default. -11. **Wayland.** `node scripts/cef/run-wayland-smoke.mjs ` against a headless Weston compositor (`--ozone-platform=wayland`) — same FFI+title checks as X11. Non-blocking in CI (`continue-on-error: true`) but still run and read, not skipped. -12. **Dependency/linkage diff.** `node scripts/cef/check-linux-runtime-deps.mjs` (dpkg package presence — did the required package list change?) and `node scripts/cef/check-linux-runtime-linkage.mjs build/worldscript_host` (real `ldd` against the newly-built artifacts — did a new unresolved shared-library dependency appear?). Directly answers roadmap §44.4's "did required Linux runtime dependencies change?" and "did packaged resource layout change?" with real command output, not a changelog read. -13. **API diff.** No automated tooling for this yet (real gap, not hidden) — manually diff the new CEF version's `include/` headers against the pinned one (or read CEF's own release notes at `https://cef-builds.spotifycdn.com` / the `chromiumembedded/cef` GitHub mirror's tag-to-tag diff) for signature changes to any API this repo actually calls: `CefExecuteProcess`, `CefInitialize`, `CefSettings`, `CefBrowserView`/`CefWindow` creation, `CefClient`/`CefRequestHandler`/`OnRenderProcessTerminated`, `CefBrowserHost::SetAccessibilityState`, `CefCrashReportingEnabled`, `cef_crash_util.h`. A build failure at step 4 will catch most of these mechanically; this step is for behavioral/semantic changes a successful compile wouldn't surface. -14. **Docs.** Update in the same PR, not a follow-up: `scripts/cef/cef-version.json`'s pin (step 1, already done), `docs/cef/CEF-RUST-COMPETENCY-MATRIX.md`'s `last_verified.cef` fields (`CEF-RUST-COMPETENCY-MATRIX.md`, `CEF-BINDING-DECISION-SCORECARD.md`, `cef-architecture-primer.md`, `cef-rust-binding-cookbook.md`, `threading-and-lifetimes.md`, `linux-runtime-notes.md` per `docs/cef/OWNERSHIP.yaml`'s per-doc `last_verified.cef` field), and this playbook itself with what was actually re-validated and what broke (see "This is a living procedure" below). -15. **Rollback.** Scope depends on what actually changed — reverting only `cef-version.json` does not undo a `Cargo.toml`/`CMakeLists.txt` change, and doing so would leave an incompatible partial upgrade in the branch: - - **CEF-only upgrade** (just `scripts/cef/cef-version.json` changed, per step 1): revert that one file to the last-known-good values (roadmap Appendix M.1's "last-known-good runtime retention for rollback") — genuinely a one-file revert, since step 1 deliberately keeps the pin isolated from everything else for exactly this case. - - **Rust binding or Corrosion upgrade** (`apps/desktop-cef/rust-core/Cargo.toml`'s `edition`, or `apps/desktop-cef/CMakeLists.txt`'s Corrosion `GIT_TAG`, also changed — see "Binding-crate upgrade specifics" below): make the whole upgrade one atomic commit in the first place, so rollback is `git revert `, not a partial file-by-file undo. If the upgrade was already split across multiple commits before a failure was discovered, revert all of them together, not just the pin. - -## Emergency security-patch path (roadmap §34.1.2) - -Shortened, but never omitting: checksum/integrity verification (step 2 — note: this is a SHA-1 comparison against a value this repo itself maintains, not cryptographic signature or provenance verification against an independent trust anchor; the roadmap's own §34.1.2 wording says "signature verification," and that gap between the roadmap's stated requirement and what's actually implemented is real and still open, not silently assumed closed by step 2), sandbox smoke (step 6), application startup (step 5), project open/save (not yet CEF-side — Wave 5+ scope, N/A until then), updater correctness (N/A — no CEF-side updater exists yet), accessibility-state enablement (step 9 — state only, not focus), migration compatibility (N/A until Wave 5+ persistence work lands). May skip: Wayland (step 11, already non-blocking), the full API-diff read (step 13) in favor of a targeted read of just the CVE's affected code paths, and broader distro/hardware validation (roadmap §44.5/§64, already out of this project's CI scope entirely). - -## Binding-crate (Rust/`worldscript_rust_core`) upgrade specifics - -`apps/desktop-cef/rust-core/Cargo.toml` pins `edition = "2021"` and Corrosion (`v0.5.1` in `apps/desktop-cef/CMakeLists.txt`'s `FetchContent_Declare`) — a version bump to either needs the same step-4 build-first signal, plus checking `panic = "abort"` and `debug = true` (`[profile.release]`) both survive the bump (the former is load-bearing for the crash-reporting/symbolization proofs at steps 7–8; the latter is load-bearing for step 8 specifically). Corrosion version bumps separately: re-run step 4 with a clean `build/` directory (stale Corrosion-generated CMake cache files are a plausible false-failure source, not a real incompatibility) before concluding something broke. **Make the bump one atomic commit** (unlike step 1's isolated one-file CEF pin change, a binding/Corrosion bump touches `Cargo.toml`/`CMakeLists.txt` together) — this is what makes step 15's rollback guidance for this case (`git revert` the one commit) actually work. - -## This is a living procedure - -Update this playbook every time a real upgrade happens, with what was actually re-validated and what broke — the numbered steps above are what Wave 2's own proof work makes possible *today*; a real upgrade will surface gaps this checklist doesn't yet know to ask about. Do not let this document freeze back into a hypothetical once it has been exercised for real. diff --git a/docs/cef/knowledge/cef-architecture-primer.md b/docs/cef/knowledge/cef-architecture-primer.md deleted file mode 100644 index 6d5b6824..00000000 --- a/docs/cef/knowledge/cef-architecture-primer.md +++ /dev/null @@ -1,97 +0,0 @@ -# CEF Architecture Primer - -**Status:** Real evidence from `apps/desktop-cef/` (PR #388) for process model, message loop, and subprocess packaging; crash reporting and renderer-crash resilience proven in CI (PR #392); Wayland display-server smoke also proven in CI (PR #393), alongside X11; accessibility state enablement proven in CI with zero regression (PR #397); crash symbolization proven for our own code's frames (PR #400) — Chromium/CEF-internal frames remain unsymbolized, honestly. Sandbox configuration, a real GPU/compositor matrix, accessibility-tree observability (AT-SPI), and a directly-observed full process-tree snapshot remain open. -**Scope:** How CEF's multi-process architecture (browser process, renderer process, GPU/utility processes; browser/frame/client ownership; message-loop integration; subprocess launch and packaging; sandbox model) maps onto WorldScript Studio's specific host and build, written from our actual integration — not a generic CEF tutorial. -**Tier:** A (release/security-critical) — see [`../OWNERSHIP.yaml`](../OWNERSHIP.yaml). -**Roadmap context:** [`../ROADMAP-CEF-DESKTOP-MIGRATION.md`](../ROADMAP-CEF-DESKTOP-MIGRATION.md) §4.11.1 ("CEF architecture" domain), §4.11.2, Wave 2. - -## Process model, as implemented (`apps/desktop-cef/src/main.cpp`) - -`worldscript_host` is a single executable re-executed by CEF itself for every process role — there is no separate subprocess binary. `main()` calls `CefExecuteProcess(main_args, app.get(), nullptr)` *before* anything else; a non-negative return means *this invocation* is a subprocess (renderer/GPU/utility) that has already run to completion, and `main()` returns immediately. Only when that call returns `-1` (this is the actual browser process) does the code proceed to install the shutdown-signal handler, call `CefInitialize`, and enter the message loop. - -This single-binary-multi-role design is directly why `scripts/cef/run-launch-cycle-proof.mjs`'s orphan check anchors its `pgrep` pattern to the *start* of the binary's own path (`^${binaryPath}`) — every subprocess CEF spawns re-execs that exact same path with different flags (e.g. `--type=renderer`), so they're all catchable by one pattern, and nothing else on the system should share that literal path prefix. - -**Directly observed evidence a renderer process exists and runs the real page**: the CI log for a real production-bundle load shows a `[INFO:CONSOLE:95]` line — a JavaScript console message relayed from the renderer process back to the browser process via CEF's own IPC, not something the browser process could produce itself. **GPU process**: not directly observed by name (no `ps`/`--type=gpu-process` capture was taken), but the build output includes `libvk_swiftshader.so`/`libvulkan.so.1` (Vulkan software rendering) and the ADR-0020 spike separately observed real GPU-fallback warnings (`Bay Trail Vulkan support is incomplete`) — consistent with a GPU process existing and falling back to software rendering, not confirmed as a distinct observed process in this specific proof. - -## Browser/frame/client ownership, as implemented - -- `WorldScriptApp::OnContextInitialized` creates exactly **one** `CefBrowserView` (`CefBrowserView::CreateBrowserView`) wrapped in exactly one top-level `CefWindow` (`CefWindow::CreateTopLevelWindow`) — single-window, single-browser, by design; nothing in this host creates additional windows or popups. -- `WorldScriptHandler` is the `CefClient` implementation and the sole owner of browser-lifecycle bookkeeping: a `std::list> browser_list_`, appended to in `OnAfterCreated` and pruned in `OnBeforeClose`. The list shape supports more than one browser in principle, but only one is ever created today. -- Frame-level ownership (multiple frames per browser, cross-frame navigation) has not been touched at all — the production bundle loads as a single top-level document. - -## Message-loop choice and why - -`CefRunMessageLoop()` (the blocking, OS-native-integrated variant) — not `CefDoMessageLoopWork()` in a manual polling loop — matching the standard `cefsimple` convention and avoiding a busy-poll CPU cost. `WorldScriptHandler::OnBeforeClose` calls `CefQuitMessageLoop()` only once `browser_list_` becomes empty, which is the actual mechanism that makes `CefRunMessageLoop()` in `main()` return — the real, CI-proven signal that it's safe to call `CefShutdown()`. See `docs/cef/knowledge/subprocess-and-shutdown.md` for the full shutdown sequence. - -## Subprocess launch and packaging, as implemented - -`apps/desktop-cef/CMakeLists.txt` runs two `COPY_FILES` calls (`CEF_BINARY_FILES`, `CEF_RESOURCE_FILES`) that land everything the runtime needs next to the executable — confirmed via a real `ls -la` in CI (PR #388), not just assumed from the macro's documented behavior: `libcef.so`, `icudtl.dat`, `resources.pak`, `chrome_100_percent.pak`, `chrome_200_percent.pak`, `v8_context_snapshot.bin`, `locales/`, `libEGL.so`, `libGLESv2.so`, `libvk_swiftshader.so`, `libvulkan.so.1`, and `chrome-sandbox`. **This is CEF's own unpackaged build-output layout** (`cmake --build` output, run in place) — not a real installer's layout, which is separate, later, unproven scope. - -A real launch-path bug was found and fixed here too: Chromium resolves several of these resource paths relative to the process's *working directory*, not the executable's own location — launching the binary from a different cwd produced an ICU-init crash despite every file being correctly present. See `docs/cef/knowledge/linux-runtime-notes.md` for the full finding. - -## Accessibility API — state enablement proven; tree observability open - -**First attempt (2026-08-19, PR #391)**: implemented `CefAccessibilityHandler` on `WorldScriptHandler`, returned from a `CefClient::GetAccessibilityHandler()` override, following the pattern every other handler type in this codebase uses (`GetLifeSpanHandler`, `GetDisplayHandler`). **This does not compile against CEF 151.3.18** — the actual compiler error was explicit: `'CefRefPtr WorldScriptHandler::GetAccessibilityHandler()' marked 'override', but does not override`. A same-PR fallback (`SetAccessibilityState` + `--force-renderer-accessibility`, no observability) caused an unrelated regression in the FFI/rendering proofs; both changes were fully reverted rather than left half-working. - -**Root cause, found on the second attempt (2026-08-19, PR #397)**: `GetAccessibilityHandler()` is real, but it's declared on `CefRenderHandler`, not `CefClient` (`include/cef_render_handler.h`, verified against the pinned CEF branch's actual source) — and `CefRenderHandler`'s own doc comment states it is for "handling events when window rendering is disabled" (OSR mode only). `worldscript_host` uses windowed rendering (`CefBrowserView`/`CefWindow`), so that method was never reachable here regardless of what `WorldScriptHandler` inherited from — PR #391's blocker had a real, findable, version-independent cause, not a moving target. - -**What PR #397 does instead**: `CefBrowserHost::SetAccessibilityState(STATE_ENABLED)` alone, called in `OnAfterCreated` — `SetAccessibilityState`'s own doc comment (`include/cef_browser.h`) confirms windowed browsers need only this one call: "all platform accessibility objects will be created and managed by Chromium's internal implementation," no `CefAccessibilityHandler` required. This is a small, isolated addition (one call, one proof line, no new class inheritance). - -**Directly observed evidence, PR #397, `🧪 CEF Learning Harness` CI job**: `[launch-cycle-proof] OK — 3/3 repeated start/close cycles clean, FFI boundary, real rendering, and accessibility-state request all proven in every cycle.` The crash-reporting and Wayland proofs both remained green in the same run — this addition caused no regression to the already-proven proofs (unlike PR #391's fallback attempt). One transient first-attempt CI timeout on cycle 1 (browser never reached `OnAfterCreated` within the grace period) reproduced the exact shape of the runner-speed variance already documented for `STARTUP_GRACE_MS` elsewhere in this file, and did not recur on a clean re-run — treated as CI-runner noise, not a code-shaped regression, consistent with that established precedent. - -**What this proves**: accessibility state can be enabled intentionally, on every launch, with zero regression to existing proofs (roadmap §23.1's first bullet). **What it does NOT prove**: that the platform accessibility tree is actually observable — Chromium's windowed-mode accessibility integration registers with the OS's native accessibility bus (AT-SPI on Linux) rather than exposing a CEF-level callback, so verifying the tree exists needs OS-level AT-SPI introspection in CI, not a C++ handler. That remains separate, unattempted follow-up work. `accessibility_smoke` stays `false` in the competency manifest until both halves have real evidence. - -## Crash reporting — a real, working proof, now with symbolization too - -Unlike the accessibility attempt above, every mechanism here was verified against the pinned CEF branch's actual source (`chromiumembedded/cef` branch `7922`, matching `151.0.7922.138`) before any code was written — the same discipline the "what this means for the next attempt" note above called for. - -**A real, and initially surprising, correction to CEF's own docs**: `docs/crash_reporting.md` in the CEF repo states crash reporting is "implemented using Crashpad on Windows and macOS, and Breakpad on Linux." That is stale relative to this exact branch's source. `libcef/common/crash_reporting.cc`'s `InitCrashReporter()` calls `crash_reporter::InitializeCrashpad(...)` unconditionally for every non-Mac POSIX process (Linux included) — Linux uses **Crashpad** too in this CEF version, not Breakpad. This was confirmed, not assumed, before relying on it: reading `libcef/common/crash_reporter_client.cc`'s `GetCrashDumpLocation()` showed the `BREAKPAD_DUMP_LOCATION` environment variable (a legacy name, kept for compatibility) still overrides the dump directory on POSIX, and CI evidence (below) confirmed a real Crashpad database layout (`pending/`, `.meta`, `settings.dat`), not a Breakpad one. - -**What's implemented** (`apps/desktop-cef/resources/crash_reporter.cfg`, `CMakeLists.txt`, `main.cpp`, `worldscript_handler.{h,cpp}`): `crash_reporter.cfg` (format from `include/cef_crash_util.h`) is copied next to the built executable via a `configure_file` step; `main.cpp` logs `CefCrashReportingEnabled()` after `CefInitialize`; `WorldScriptHandler` now also implements `CefRequestHandler` and overrides `OnRenderProcessTerminated` — a real method (confirmed present in `include/cef_client.h`'s `GetRequestHandler()`, unlike the accessibility handler) that fires in the browser process when a renderer subprocess dies, without the browser process itself going down. - -**Directly observed evidence, PR #392, `🧪 CEF Learning Harness` CI job**: the harness launches `worldscript_host --url=chrome://crash` (the same debug URL CEF's own `cefclient` reference app uses to test this exact path) with `BREAKPAD_DUMP_LOCATION` pointed at a fresh, empty temp directory. The CI log shows `crash_reporting_enabled = true`, then `renderer_terminated status=TS_PROCESS_CRASHED error_code=...`, then a real `pending/.dmp` file — the one artifact the harness actually asserts on (`endsWith('.dmp')`, waited for before shutdown) — alongside `pending/.meta` and `settings.dat`, Crashpad's own housekeeping files that were also observed in that directory but are not independently checked by the harness. The browser process's own clean-shutdown proof (same mechanism as the repeated start/close cycles) passed too, confirming process isolation held: only the renderer subprocess died. - -**Symbolization, first assumption (PR #392, this section's original text)**: decoding the `.dmp` file into a human-readable stack trace needs `dump_syms` and `minidump_stackwalk`, which CEF's own docs say must be built from a *complete Chromium source checkout* (`gn`/`ninja`, hours of build time, tens of GB of disk) — assumed out of reach and not attempted. - -**Second attempt, real research first (2026-08-19, PR #400)**: that assumption doesn't hold for *our own* code's frames. `dump_syms` ([mozilla/dump_syms](https://github.com/mozilla/dump_syms)) and `minidump-stackwalk` ([rust-minidump/rust-minidump](https://github.com/rust-minidump/rust-minidump)) are both standalone Rust projects with prebuilt Linux release binaries — neither needs a Chromium checkout at all, confirmed by reading their own READMEs directly, and `dump_syms -s `'s output layout was verified locally against a throwaway compiled test binary before any CI code was written. Separately, `https://cef-builds.spotifycdn.com/index.json` was checked for this project's pinned CEF version and confirmed **no distribution type** (standard/tools/minimal/client) ships a separate debug-symbols archive — so Chromium/CEF-internal frames genuinely cannot be symbolized regardless of tooling. That narrower, real limitation is why the existing `chrome://crash` renderer-crash proof above stays unsymbolized; "needs a full Chromium checkout" as a blanket statement was not correct. - -**What PR #400 does**: `apps/desktop-cef/rust-core/src/lib.rs` adds `worldscript_rust_debug_crash_self_test()`, reachable only behind a new `--debug-crash-self` CLI flag (`main.cpp`) — never in normal operation. It panics under this crate's `panic = "abort"` release profile, a real SIGABRT Crashpad catches the same way it caught the renderer crash above. `rust-core`'s `[profile.release] debug = true` and a `target_compile_options(worldscript_host PRIVATE -g)` on just this one CMake target (not `CMAKE_BUILD_TYPE=RelWithDebInfo` — that broke CEF's own `chrome-sandbox` copy step in a real, CI-observed failure, because the fetched SDK's prebuilt binaries only ship `Release/`/`Debug/` subdirectories) give the final linked binary real DWARF debug info for both its C++ and Rust code. - -**Directly observed evidence, PR #400, `🧪 CEF Learning Harness` CI job**: `scripts/cef/run-symbolization-proof.mjs` launches `worldscript_host --debug-crash-self`, confirms a SIGABRT, confirms a real `.dmp` file, runs `dump_syms -s worldscript_host`, then `minidump-stackwalk --json ` — parsing the JSON and checking `crashing_thread.frames[].function` specifically (not a raw substring match over the whole document, a CodeRabbit-flagged nitpick fixed before merge). The CI log shows: `OK — real Crashpad dump from a self-induced crash was symbolized end-to-end, resolving "worldscript_rust_debug_crash_self_test" via dump_syms + minidump-stackwalk.` - -**What this proves**: our own code's crash frames genuinely symbolize, end-to-end, in CI, with prebuilt tools that cost ~3.6MB each to fetch — a real capability, not a stub. **What it does NOT prove**: Chromium/CEF-internal frames (e.g. the `chrome://crash` renderer crash documented above) remain unsymbolized — a real, permanent limitation of this CEF distribution channel, not a "not attempted yet" gap. `crash_symbolization_smoke` in `docs/cef/CEF-RUST-COMPETENCY-MATRIX.md` is now `true`, with that scope stated explicitly in the same commit. - -## Display server — X11 proven since PR #388, Wayland now also proven - -Roadmap §44.2 is explicit: *"'CEF uses Chromium' is not accepted as proof of Wayland/X11 correctness."* Until PR #393 this host had only ever been exercised under X11 (`xvfb-run`). - -**Real evidence gathered before attempting anything**, matching the discipline the accessibility attempt's own "what this means for the next attempt" note called for: Chromium's own upstream GN default (`build/config/ozone.gni`, the `is_linux` branch) compiles **both** the `x11` and `wayland` Ozone platforms into every standard Linux build (`ozone_platform_wayland = true`), and CEF's own `tools/gn_args.py` has zero ozone/wayland overrides — confirmed by reading both files directly. `--ozone-platform=wayland` is a real, verified Chromium switch (`ui/ozone/public/ozone_switches.cc`'s `kOzonePlatform`). - -**Directly observed evidence, PR #393**: `scripts/cef/run-wayland-smoke.mjs` launches a headless Weston compositor (`weston --backend=headless-backend.so` — the Wayland-side equivalent of Xvfb, no real display/GPU needed) and the *exact same already-built* `worldscript_host` binary under `--ozone-platform=wayland`, `WAYLAND_DISPLAY` pointed at Weston's socket. The CI log shows the compositor socket created, then `worldscript_host` reaching both `rust_core ping = 424242` and `title = WorldScript Studio` within about 1.2 seconds — the same two proofs the X11 harness uses, now also true under Wayland, on a stock GitHub Actions runner, first attempt. - -**What this does NOT prove**: roadmap §44.2/§44.5's real matrix — NVIDIA/AMD/Intel GPUs × KDE/GNOME compositors × real graphics hardware. This is one virtual CI runner, one compositor implementation (Weston, headless, no GPU), non-blocking (`continue-on-error`) in CI. It answers "does the fetched CEF binary distribution and this host even support Wayland at all" (yes), not "does WorldScript Studio work correctly under every real-world Wayland desktop" (unproven). - -## Sandbox configuration, as shipped - -`chrome-sandbox` is present in the output directory (copied automatically as part of `CEF_BINARY_FILES`) but is **not used** — `main.cpp` sets `CefSettings.no_sandbox = true` unconditionally. Zero evidence exists on real sandbox posture; this is explicitly tracked as "Not yet attempted" in `docs/architecture/native-readiness.md` and `false` in the competency manifest. - -**Confirmed against CEF's own `docs/sandbox_setup.md` before writing any code (PR #402)**: unlike Windows (`cef_sandbox_win.h`, static-library linking) and macOS (`cef_sandbox_mac.h`, `dlopen`'d dylib), CEF has **no Linux-specific sandbox API at all**. The doc's entire Linux section is one line pointing at Chromium's own `docs/linux_sandboxing.md`: the sandbox is a Chromium-internal mechanism, `CefSettings.no_sandbox` the only lever. Layer-1 (process/namespace isolation) uses either the legacy setuid `chrome-sandbox` helper (root-owned, setuid bit) or — automatically preferred since Chromium M-43 if the kernel/policy allows it — unprivileged user namespaces, no setuid binary needed. Layer-2 (seccomp-bpf syscall filtering) is independent of layer-1. - -**Diagnostic-only feasibility check (PR #402)**: `scripts/cef/check-linux-sandbox-inventory.mjs` ran a *functional* test (`unshare --user --pid --fork`, not just reading the `kernel.unprivileged_userns_clone` sysctl — a container/AppArmor restriction can block namespace creation even when the sysctl claims it's allowed) on the CI runner. Real result: kernel `6.17.0-1022-azure`, sysctl `1`, AppArmor module enabled, and `unshare` **succeeded** — unprivileged user-namespace sandboxing appears reachable here. This is evidence the follow-up attempt has a real chance, not a guarantee it will work end-to-end once CEF's own zygote/GPU process spawning is actually exercised under it. - -**Acceptance bar for the follow-up enable attempt (not yet done — separate PR)**: not just "CEF starts without `no_sandbox=true`." Must show browser, renderer, and utility/GPU processes actually running under the sandbox — Chromium's own guidance is to check each process's *real* sandbox status (e.g. `chrome://sandbox`, or an equivalent CI-observable signal), since Linux combines namespace isolation *and* seccomp-BPF and both matter independently. Zero regression to the existing lifecycle/crash-reporting/symbolization/accessibility-state/Wayland proofs. A reproducible sandbox-status proof in CI, not a one-off manual check. **Explicitly disallowed**: silently trading `no_sandbox=true` for a narrower blanket disable (e.g. `--disable-setuid-sandbox`) to get past a launch failure while still claiming this row proven — that would misrepresent what's actually protected, the same honesty standard already applied to the accessibility and symbolization sections above. - -**Two separate gates, not one**: a GitHub Actions runner can prove "our CEF configuration is *capable* of running sandboxed" (a CI-sandbox-proof) — it cannot prove "our eventual `.deb`/AppImage/installer distribution correctly installs the sandbox helper, its permissions, and the runtime layout on every target Linux distribution" (a production-packaging-sandbox-proof). The latter is real, separate, later packaging-wave scope (matching the roadmap's existing treatment of installer packaging elsewhere in this doc) — not to be pulled forward into Wave 2 just because a CI proof exists. - -## Process tree — what we can honestly claim - -```text -worldscript_host (browser process, no_sandbox=true) -└── worldscript_host --type=renderer ... (confirmed indirectly: console-log IPC observed; - not directly captured by ps/process-name in this proof) -└── (likely) worldscript_host --type=gpu-process ... (consistent with SwiftShader/Vulkan - files present and GPU-fallback warnings from the ADR-0020 spike; not directly observed - by process name in PR #388's own CI run) -``` - -Not a diagram of the generic CEF process model — this is what PR #388's evidence actually supports, with each claim's confidence level stated rather than assumed. A real `ps`/process-tree capture during a live run would upgrade the two `(likely)`/"not directly observed" lines to confirmed evidence; that capture has not been taken yet. diff --git a/docs/cef/knowledge/cef-rust-binding-cookbook.md b/docs/cef/knowledge/cef-rust-binding-cookbook.md deleted file mode 100644 index 7d0dcf26..00000000 --- a/docs/cef/knowledge/cef-rust-binding-cookbook.md +++ /dev/null @@ -1,38 +0,0 @@ -# CEF Rust Binding Cookbook - -**Status:** Real evidence from `apps/desktop-cef/rust-core/` (PR #388) — but the FFI surface it documents is currently one trivial function, not representative of real API coverage. Treat this as "the pattern is proven," not "the binding is complete." -**Scope:** Practical recipes for the exact Rust↔CEF integration WorldScript adopted (Wave 2 decision, ADR-0020, Appendix H) — unsafe/FFI boundary patterns, wrapper ownership idioms, API coverage gaps we've hit, and workarounds, written against our real usage. -**Tier:** A (release/security-critical) — see [`../OWNERSHIP.yaml`](../OWNERSHIP.yaml). -**Roadmap context:** [`../ROADMAP-CEF-DESKTOP-MIGRATION.md`](../ROADMAP-CEF-DESKTOP-MIGRATION.md) §4.11.1 ("Rust binding layer" domain), §10 (integration choice), Appendix H (decision scorecard). - -## There is no "CEF binding crate" — that's the point of Option B - -ADR-0020 chose Option B (thin C++ host + Rust core) specifically *because* it means Rust never touches CEF's C++ API at all — no `cef`-style Rust crate wrapping `CefRefPtr`/CEF interfaces exists or is needed. The "binding" here is a plain C FFI boundary between the C++ host and a Rust `staticlib`, nothing CEF-specific on the Rust side. If a future wave changes this decision (Option A), this doc's premise would need a full rewrite, not an update. - -## What's actually built (PR #388) - -- **Crate**: `apps/desktop-cef/rust-core/` (`worldscript_rust_core`), `crate-type = ["staticlib"]`, `panic = "abort"` in the release profile (matches the C++ host's own lack of a Rust-panic-to-C++-exception bridge — a Rust panic across this boundary today is unrecoverable-by-design, not caught and translated). -- **The entire FFI surface right now**: one function. - ```rust - #[no_mangle] - pub extern "C" fn worldscript_rust_ping() -> i32 { 424242 } - ``` - Declared on the C++ side as a bare forward declaration (`extern "C" int worldscript_rust_ping();` in `worldscript_handler.cpp`) — **no shared/generated header exists between the two languages yet**. This is a real, acknowledged gap: today's single trivial function is hand-matched by inspection: a real API surface (multiple functions, structs crossing the boundary) would need a proper shared header or a tool like `cbindgen` to avoid silent ABI drift between the Rust and C++ sides. -- **Build integration**: [Corrosion](https://github.com/corrosion-rs/corrosion) (`corrosion_import_crate(MANIFEST_PATH ...)` in `apps/desktop-cef/CMakeLists.txt`), fetched via CMake `FetchContent` (a small MIT-licensed CMake module, not a vendored binary). Corrosion creates a CMake target named after the crate (`worldscript_rust_core`), linked via a plain `target_link_libraries(worldscript_host ... worldscript_rust_core ...)` — no manual `.a` path (the ADR-0020 spike's own documented workaround, now replaced by real integration). -- **Proven inside the real host, not just isolated**: `worldscript_rust_ping()` is called from `WorldScriptHandler::OnAfterCreated` on every launch cycle, and its exact sentinel return value is observed in CI output (`scripts/cef/run-launch-cycle-proof.mjs`) — stronger evidence than the ADR-0020 spike's decoupled standalone-binary test. - -## Unsafe/FFI boundary map - -The *entire* unsafe surface today is the `extern "C"` function signature itself — no raw pointers, no shared mutable state, no lifetime crossing the boundary (the function takes no arguments and returns a plain `i32`). This is deliberately the simplest possible boundary, proving the *mechanism* works before any real data crosses it. **Do not extrapolate this simplicity to "the FFI boundary is safe/solved"** — passing strings, structs, or any pointer-based data across this boundary is materially riskier and entirely unproven so far. - -## Wrapper ownership model - -Not applicable yet — no CEF object (or any pointer-owning type) crosses into Rust. The moment a real API needs to pass e.g. a string or buffer across this boundary, this section needs real content: who allocates, who frees, and on which side. - -## Known API coverage gaps - -None cataloged — there is no real API surface yet to have gaps in. The competency matrix (`docs/cef/CEF-RUST-COMPETENCY-MATRIX.md`) already flags this explicitly: `binding_model_documented: true` refers to the *integration model* (ADR-0020's Option B choice), not a claim that the FFI surface itself is complete or battle-tested. - -## Binding upgrade procedure - -Not written yet — see [`binding-upgrade-playbook.md`](binding-upgrade-playbook.md), still a skeleton. With only one trivial function crossing the boundary, there is nothing yet to have an upgrade procedure *for* beyond bumping the Corrosion `GIT_TAG` pin in `CMakeLists.txt`, which has not been exercised even once. diff --git a/docs/cef/knowledge/debugging-and-crash-playbook.md b/docs/cef/knowledge/debugging-and-crash-playbook.md deleted file mode 100644 index c11a377e..00000000 --- a/docs/cef/knowledge/debugging-and-crash-playbook.md +++ /dev/null @@ -1,19 +0,0 @@ -# CEF Debugging and Crash Playbook - -**Status:** Not started — this playbook's own prose (process-role crash taxonomy, symbolization procedure, hang detection) is still Wave 3+ scope. CEF integration itself is real, and crash *reporting* (the raw material for a future symbolization section) is now proven in CI — see `docs/cef/knowledge/cef-architecture-primer.md`'s "Crash reporting" section (PR #392) for what currently exists: `crash_reporter.cfg`, `CefCrashReportingEnabled()`, a deliberately induced renderer crash producing a real Crashpad `.dmp`. Decoding that dump (`dump_syms`/`minidump_stackwalk`) needs a full Chromium source checkout and remains unattempted. -**Scope:** How to diagnose CEF-specific failures in WorldScript's build — process-role crash classification (native host / browser / renderer / GPU / hang-without-crash), symbolization procedure, minidump handling, GPU/Wayland/X11 troubleshooting, and the operational Crash Ops subsystem's actual behavior. -**Tier:** A (release/security-critical) — see [`../OWNERSHIP.yaml`](../OWNERSHIP.yaml). -**Roadmap context:** [`../ROADMAP-CEF-DESKTOP-MIGRATION.md`](../ROADMAP-CEF-DESKTOP-MIGRATION.md) §41.1–§41.5 (Crash Operations), Appendix A.5 (crash/update ops checklist), Appendix D (process attribution schema). - -## Outline (to be filled in during Wave 3+) - -- Process-role crash taxonomy as implemented (native host / browser / renderer / GPU/utility / hang-without-crash / startup crash loop) -- Symbolization procedure and build-provenance linkage for released builds -- Minidump privacy policy as shipped (local-only by default? consent flow?) — cross-reference §41.3 -- Known GPU/Wayland/X11 failure signatures and their resolutions -- Crash-loop → Safe Mode trigger behavior (§77) -- Step-by-step "renderer appears frozen, not crashed" diagnostic procedure (hang detection, §41.1) - -## Why this matters beyond crashes - -Per roadmap §41.1, a crash reporter alone is insufficient — issue #332-class incidents can manifest as **hangs without a crash**. This playbook must cover both, not just the easier crash-reporting half. diff --git a/docs/cef/knowledge/linux-runtime-notes.md b/docs/cef/knowledge/linux-runtime-notes.md deleted file mode 100644 index 7556c882..00000000 --- a/docs/cef/knowledge/linux-runtime-notes.md +++ /dev/null @@ -1,37 +0,0 @@ -# CEF Linux Runtime Notes - -**Status:** Preliminary spike evidence (2026-08-18, ADR-0020, one dev machine) plus real CI-run evidence (2026-08-18/19, PR #388, GitHub Actions `ubuntu-latest`) — two machines now, still one CEF version, X11 only. Not a compatibility contract yet; do not treat any number below as a floor until packaged builds prove it (§44.1). See "Second data point" below for the CI-runner evidence, and "Remaining outline" for what neither data point covers yet. -**Scope:** The Linux Runtime Compatibility Contract for WorldScript's CEF build — minimum supported distribution/runtime baseline, CPU architectures, required dynamic libraries/packages, `libcef.so`/resource layout, loader/rpath policy, X11 vs. Wayland policy, Ozone/backend policy, GPU/driver expectations, installer dependency behavior. -**Tier:** A (release/security-critical) — see [`../OWNERSHIP.yaml`](../OWNERSHIP.yaml). -**Roadmap context:** [`../ROADMAP-CEF-DESKTOP-MIGRATION.md`](../ROADMAP-CEF-DESKTOP-MIGRATION.md) §44.1–§44.5 (Linux runtime compatibility as its own workstream), Appendix A.3 (compatibility matrix template). - -## Spike evidence (2026-08-18, one machine only — see status above) - -- **Distribution/version tested:** CEF 151.3.18 (Chromium 151.0.7922.138), linux64 **minimal** distribution, on Ubuntu 22.04 (glibc-based) — matches CEF's own officially-tested target for this CEF version per its shipped `CMakeLists.txt`. -- **Runtime shared-library dependencies:** every package CEF's build docs call out (`libnss3`, `libnspr4`, `libatk1.0-0`, `libatk-bridge2.0-0`, `libcups2`, `libdrm2`, `libgbm1`, `libxcomposite1`, `libxdamage1`, `libxfixes3`, `libxrandr2`, `libxkbcommon0`, `libpango-1.0-0`, `libcairo2`, `libasound2`, `libgtk-3-0`, `libx11-xcb1`, `libxcb1`) were **already present** on this dev machine at their latest Ubuntu 22.04 package versions — none needed fresh installation. This is one data point, not proof these are sufficient on a clean/minimal install (§44.3's clean-machine test is still open). -- **Build-time-only dependency, not a runtime one:** `libx11-dev` (for `pkg-config --exists x11`, used only by the CMake build's `FIND_LINUX_LIBRARIES` macro) — this is a compile-time header/pkg-config need, not something the *packaged* app requires on an end-user machine. -- **Display server:** X11 only, via Xvfb (virtual framebuffer, headless). **Wayland was not tested at all.** Do not extrapolate X11-working to Wayland-working — the roadmap's own explicit warning applies: "CEF uses Chromium" is not proof of Wayland correctness. -- **GPU:** integrated Intel graphics on this machine reported `Bay Trail Vulkan support is incomplete` and `Installed VAAPI version is too old (min 1.17, installed 1.14)`. Chromium fell back to software rendering (bundled SwiftShader) rather than crashing. This is a real, reproducible data point for exactly the kind of older/constrained-GPU Linux hardware the roadmap's compatibility matrix (Appendix A.3) needs to cover — not yet placed into that matrix formally since only one GPU/driver combination was observed. -- **Sandbox:** not exercised (`no_sandbox=true` for this spike). No data point here at all. - -## Second data point: real rendering on GitHub Actions `ubuntu-latest` (2026-08-18/19, PR #388) - -Distinct from the spike above — this is CI-run, repo-committed evidence via [`scripts/cef/run-launch-cycle-proof.mjs`](../../../scripts/cef/run-launch-cycle-proof.mjs) in the `🧪 CEF Learning Harness` job, not a manual spike: - -- **A second machine, X11/Xvfb again, but this time rendering the real production bundle** (`pnpm run build`'s `dist/`, served over local HTTP), not just `about:blank`. Confirmed via the page's own console log line and its exact document title ("WorldScript Studio") observed in CEF's `OnTitleChange` callback. -- **`libcef.so`/resource layout observed for the first time on a real filesystem listing** (not just assumed from `COPY_FILES` macro behavior): `icudtl.dat`, `resources.pak`, `chrome_100_percent.pak`, `chrome_200_percent.pak`, `v8_context_snapshot.bin`, `locales/`, `libEGL.so`, `libGLESv2.so`, `libvk_swiftshader.so`, `libvulkan.so.1`, and `chrome-sandbox` all land correctly next to the executable via the two `COPY_FILES` calls in `apps/desktop-cef/CMakeLists.txt` — still CEF's own unpackaged build-output layout, not a real installer, but no longer just assumed. -- **A real startup-path bug found on this runner, unrelated to the dev-machine spike**: Chromium resolves several resource paths relative to the process's *working directory*, not the executable's own location — launching the binary from a different cwd (the repo root, as the CI step's default) produced `icu_util.cc: Invalid file descriptor to ICU data received` and an immediate crash, even with every required file correctly present. This is a genuine Linux-runtime-launch finding worth carrying forward into whatever eventually launches the packaged app (a desktop entry, a system service, a supervisor process) — it must set cwd correctly, or the equivalent Chromium flag/env override must be used instead. - -## Explicit warning carried from the roadmap - -> "CEF uses Chromium" is not accepted as proof of Wayland/X11 correctness (§44.2). Do not fill in this document with assumptions extrapolated from generic Chromium behavior — every claim here must come from an actual test run against WorldScript's build. - -Do not hardcode a glibc/distro minimum here until packaged builds have proven it (§44.1). The Ubuntu 22.04 data point above is a spike observation on one machine, not a floor. - -## Remaining outline (not yet done) - -- Minimum distro/glibc floor **as proven by packaged builds**, not assumed (still open — Ubuntu 22.04 above is one dev-machine spike observation, not a proof) -- `libcef.so` and resource layout for our actual **packaging** (still not designed — PR #388 confirmed CEF's own unpackaged build-output layout copies correctly via `COPY_FILES`, on two machines now, but a real installer's layout is separate, later scope) -- X11 and Wayland smoke-test results across KDE, GNOME × NVIDIA, AMD, Intel (Appendix A.3 matrix) — this spike covers exactly one cell (X11/Xvfb, Intel integrated) of that matrix -- Sandbox requirements observed on Linux (not exercised this spike) -- Clean-machine dependency test results (§44.3) — **partially closed**: the `🧪 CEF Learning Harness` CI job (`.github/workflows/cef-learning-harness.yml`, `scripts/cef/check-linux-runtime-deps.mjs`) runs the same package-presence check against a stock `ubuntu-latest` runner, before any `apt-get`, giving a real second data point beyond this already-configured dev machine. `scripts/cef/check-linux-runtime-linkage.mjs` (PR #395) closes the specific gap this note originally called out — `ldd` against the actual shipped `.so` files, not just dpkg presence: run against the real, already-built `worldscript_host` and `libcef.so` (CI build output, `apps/desktop-cef/CMakeLists.txt`'s `COPY_FILES` step — not a packaged installer), it reported both fully resolved on the CI runner, zero `=> not found` lines. Still open: only one distro/runner image; still not a packaged-installer dependency declaration diff --git a/docs/cef/knowledge/subprocess-and-shutdown.md b/docs/cef/knowledge/subprocess-and-shutdown.md deleted file mode 100644 index fd41a636..00000000 --- a/docs/cef/knowledge/subprocess-and-shutdown.md +++ /dev/null @@ -1,24 +0,0 @@ -# CEF Subprocess Launch and Shutdown - -**Status:** Real, repo-committed, CI-run evidence (2026-08-18/19, PR #388) for the Linux subprocess-launch and SIGTERM-shutdown path — superseding the preliminary ADR-0020 spike this doc originally only had a lead from. Still not a complete implementation of this doc's full scope (see "Outline" below) — the save-coordinator/window-state steps remain unimplemented, Wave 5+ scope. -**Scope:** Subprocess launch/packaging for our build, and the authoritative shutdown protocol (request quit → stop accepting unsafe new work → cancel/defer tasks → flush save coordinator → persist window/application state → stop renderer → stop CEF → stop core → exit) as actually implemented. -**Tier:** A (release/security-critical) — see [`../OWNERSHIP.yaml`](../OWNERSHIP.yaml). -**Roadmap context:** [`../ROADMAP-CEF-DESKTOP-MIGRATION.md`](../ROADMAP-CEF-DESKTOP-MIGRATION.md) §4.11.1, §38 (lifecycle semantics), §61.1.4 (worked example: "CEF cleanly restarts after shutdown → test → CI job → this doc"). - -## Real finding, CI-run (2026-08-18/19, PR #388) - -**Test → CI job → this doc**, per §61.1.4's own worked example: [`scripts/cef/run-launch-cycle-proof.mjs`](../../../scripts/cef/run-launch-cycle-proof.mjs) → the `🧪 CEF host build, dependency inventory, launch-cycle proof` job in [`.github/workflows/cef-learning-harness.yml`](../../../.github/workflows/cef-learning-harness.yml) → this section. - -- **Graceful shutdown is implemented and proven, not just OS-forced.** [`apps/desktop-cef/src/shutdown_signal.h`/`.cpp`](../../../apps/desktop-cef/src/shutdown_signal.cpp) installs an async-signal-safe SIGTERM/SIGINT handler (a `sig_atomic_t` flag only — CEF APIs are not async-signal-safe). `WorldScriptHandler` polls that flag from the UI thread via a `CefTask` (`PollShutdownFlag`, [`worldscript_handler.cpp`](../../../apps/desktop-cef/src/worldscript_handler.cpp)) and calls `TryCloseBrowser()` on every tracked browser when set — the real `CanClose`/`DoClose`/`OnBeforeClose` → `CefQuitMessageLoop()` → `CefShutdown()` protocol actually runs. The CI log shows `signal=null` (a genuine self-initiated exit via `return 0` in `main()`), not `signal=SIGTERM` (an OS-forced kill) — direct evidence the graceful path executed, not just that the OS could terminate the process. -- **Shutdown after SIGTERM is still not instantaneous**, consistent with the original spike finding: the harness allows a 6s grace period for the main process to exit after SIGTERM, then waits an *additional*, fixed ~3s grace period before performing a single `processTreeAlive()` orphan check — added after an early version of this proof (checking immediately) reported a false orphan on the first real run that spawned a renderer subprocess (once the ICU/cwd startup bug below was fixed). The harness does not measure actual reaping duration, only whether a fixed buffer was sufficient. -- **3 repeated start → SIGTERM → close cycles, each independently verified clean** (not aggregated across the run — an earlier version of this harness could mask a later cycle's failure behind an earlier success; fixed before merge) — every cycle showed a clean process tree via an anchored `pgrep` check, the FFI boundary proof (`rust_core ping = 424242` from `OnAfterCreated`, deterministic regardless of page content), and the real rendering proof (`title = WorldScript Studio` from `OnTitleChange` — a CEF error page would not produce this exact title). -- **A real startup bug found and fixed en route, unrelated to shutdown but worth recording here**: `icu_util.cc: Invalid file descriptor to ICU data received` crashed the host within ~200ms of every launch, despite `icudtl.dat` being correctly present next to the executable — root cause was the *working directory* the process was launched from (Chromium resolves several resource paths relative to cwd, not the executable's own location), not a packaging bug. Fixed by setting `cwd` to the binary's own directory when spawning it. -- `WorldScriptHandler::OnBeforeClose` calling `CefQuitMessageLoop()` only once the last tracked browser closes (the standard `cefsimple` pattern) is what makes `CefRunMessageLoop()` in `main()` return, which is the actual signal that the process is ready for `CefShutdown()`. Everything upstream of it in the full protocol this doc's scope describes (save coordinator flush, window/app state persistence) is still Wave 5+ scope and was not part of this proof. - -## Outline (remaining — not yet done) - -- Subprocess packaging layout for Linux/Windows/macOS **as actually shipped** (this proof used CEF's own unpackaged build-output layout, not a real installer) -- Save-coordinator flush and window/application-state persistence steps — Wave 5+ scope, genuinely unimplemented, not just untested -- Failure/timeout policy for each shutdown step (this proof has grace periods for *its own test assertions*, not a documented production timeout policy) -- Windows/macOS subprocess-launch and shutdown evidence — this proof is Linux-only -- Known shutdown hangs encountered during development and their root causes — none encountered in this proof once the ICU/cwd bug above was fixed, but this is one CI runner, one CEF version, one run count diff --git a/docs/cef/knowledge/threading-and-lifetimes.md b/docs/cef/knowledge/threading-and-lifetimes.md deleted file mode 100644 index 46febad9..00000000 --- a/docs/cef/knowledge/threading-and-lifetimes.md +++ /dev/null @@ -1,41 +0,0 @@ -# CEF Threading and Lifetimes - -**Status:** Real, hands-on evidence from `apps/desktop-cef/` (PR #388) — the first genuine content this doc has ever had. Narrower than its full intended scope: UI-thread callback discipline and CEF ref-counting/callback-lifetime patterns are covered with real code + CI proof; IO-thread behavior, render-process-side code, and async cancellation are **not** touched at all yet. -**Scope:** UI-thread-only callback rules, IO-thread behavior, CEF's reference-counted object model, callback lifetime, renderer/browser process boundaries, async cancellation and object invalidation, and shutdown races — as they actually manifest in WorldScript's host implementation. -**Tier:** A (release/security-critical) — see [`../OWNERSHIP.yaml`](../OWNERSHIP.yaml). -**Roadmap context:** [`../ROADMAP-CEF-DESKTOP-MIGRATION.md`](../ROADMAP-CEF-DESKTOP-MIGRATION.md) §4.11.1 ("CEF threading and lifetime rules" domain), §4.11.4 (dual-review requirement for this exact area), Appendix A.1. - -## Thread map: what we've actually proven (PR #388) - -Every callback our code implements — `WorldScriptApp::OnContextInitialized`, `WorldScriptHandler::OnTitleChange`/`OnAfterCreated`/`DoClose`/`OnBeforeClose`/`PollShutdownFlag`, `WorldScriptWindowDelegate::OnWindowCreated`/`OnWindowDestroyed`/`CanClose` — runs on CEF's **UI thread**, enforced by `CEF_REQUIRE_UI_THREAD()` (a `DCHECK`-style assertion, not just a comment) at the top of each. This is CEF's own contract for these specific interfaces (`CefBrowserProcessHandler`, `CefLifeSpanHandler`, `CefDisplayHandler`, `CefWindowDelegate`) — we didn't have to manually dispatch anything onto the UI thread for these to be correct; CEF delivers them there itself. - -**Not touched at all**: IO-thread behavior, render-process-side code (no `CefRenderProcessHandler` implemented — see `docs/cef/CEF-BINDING-DECISION-SCORECARD.md`'s "Renderer callbacks" row, which explicitly scores this as untested), and any cross-thread posting *other than* the one pattern below. - -## Reference-counting discipline — a real gotcha found via CI, not review - -Every CEF-facing class we wrote (`WorldScriptHandler`, `WorldScriptApp`, `WorldScriptWindowDelegate`, `WorldScriptBrowserViewDelegate`, `PollShutdownTask`) uses `IMPLEMENT_REFCOUNTING(ClassName)` — CEF's own ref-counted base (`CefBaseRefCounted`-derived interfaces), not Chromium's `base::RefCounted`. - -**The gotcha**: these are two *different* ref-counting schemes. Chromium's `base::BindOnce`/`base::Bind` has automatic ref-counting detection built for its *own* `base::RefCounted` family — it does **not** recognize CEF's `IMPLEMENT_REFCOUNTING` scheme the same way. Binding a bare `this` (a CEF ref-counted object) directly into `base::BindOnce(&Method, this)` produced a real compile failure on the actual CI build (`base::BindFailedCheckPreviousErrors`, "invalid use of incomplete type `OnceCallback`") — not caught by local review (no CEF SDK available to compile against locally), only by the CI-first loop actually building it. `base::Unretained(this)` fixed the *receiver* half of that error but not the underlying incomplete-type issue. - -**What we shipped instead**: a plain `CefTask` subclass (`PollShutdownTask`, in `apps/desktop-cef/src/worldscript_handler.cpp`'s anonymous namespace) holding a `CefRefPtr handler_` member, passed to the simpler `CefPostDelayedTask(CefThreadId, CefRefPtr, int64_t)` overload — sidestepping `base::Bind`'s template machinery entirely. `CefRefPtr(this)` in the task's constructor calls `AddRef()`, genuinely extending the handler's lifetime for as long as the task holds it — a real lifetime guarantee, not just an "I promise this is safe" annotation like `base::Unretained` would have been. - -## Callback lifetime pattern: the shutdown-poll chain - -`WorldScriptHandler::OnAfterCreated` schedules the first `PollShutdownTask` via `CefPostDelayedTask(TID_UI, ..., 100ms)`. Each execution either (a) detects the shutdown flag set and calls `TryCloseBrowser()` on every tracked browser, stopping the chain, or (b) reschedules itself only `if (!browser_list_.empty())`. This means the polling chain **self-terminates** once the last browser closes (`OnBeforeClose` empties `browser_list_`) — no explicit cancellation call was needed, and no scheduled-but-orphaned tasks accumulate after the browser closes, confirmed by the clean process-tree check in `scripts/cef/run-launch-cycle-proof.mjs` across 3 repeated cycles. - -## Shutdown races — one real one avoided, one real one found and worked around - -- **Avoided by design**: CEF APIs are not async-signal-safe (confirmed via CEF's own docs during this investigation). `apps/desktop-cef/src/shutdown_signal.h`/`.cpp`'s SIGTERM/SIGINT handler does *only* an async-signal-safe `sig_atomic_t` flag write — no CEF call happens inside the signal handler itself. The actual `TryCloseBrowser()`/`OnBeforeClose()`/`CefQuitMessageLoop()` sequence runs from the polling task on the UI thread, a normal (non-signal-handler) context. -- **Found and worked around**: a renderer subprocess can still be alive for a short window after the main (browser) process itself has already exited — an immediate orphan-process check after the main process exits produced a false positive on the first real run that spawned a renderer (once an unrelated ICU/cwd startup bug was fixed — see `docs/cef/knowledge/subprocess-and-shutdown.md`). Worked around with a fixed grace period before checking, not a measured wait — see that doc for the precise, review-corrected wording. - -## Repeated-startup/shutdown test results - -Real, CI-run evidence — see `docs/cef/knowledge/subprocess-and-shutdown.md` and the `🧪 CEF Learning Harness` workflow's `scripts/cef/run-launch-cycle-proof.mjs` step (PR #388): 3/3 independently-verified clean cycles. - -## Still open (not this doc's real content yet) - -- IO-thread behavior — never touched. -- Render-process-side lifetime rules (`CefRenderProcessHandler`) — never implemented. -- Async cancellation of in-flight CEF work (e.g. a pending `CefPostDelayedTask` explicitly cancelled mid-flight, not just left to self-terminate as above). -- Object invalidation patterns beyond the one ref-counted-task pattern documented here. -- The full dual-review this doc's own header requires (roadmap §4.11.4) — this is one author's real findings, not yet an independent second review. diff --git a/docs/cef/tauri-coupling-inventory.json b/docs/cef/tauri-coupling-inventory.json deleted file mode 100644 index c279f7ba..00000000 --- a/docs/cef/tauri-coupling-inventory.json +++ /dev/null @@ -1,240 +0,0 @@ -{ - "$schema": "informal — see docs/cef/TAURI-COUPLING-INVENTORY.md for field definitions", - "verifiedAt": "2026-08-18", - "verifiedAgainst": "refactor/desktop-platform-adapter-migration (Wave 1 PR B, post packages/desktop-contracts merge)", - "method": "rg -l \"from ['\\\"]@tauri-apps|import\\\\(['\\\"]@tauri-apps\" -g '*.ts' -g '*.tsx' -g '!*.test.ts' -g '!*.test.tsx' -g '!tests/**' ., cross-checked per-file with rg -oE \"@tauri-apps/[a-zA-Z0-9/_-]*\". Superseded going forward by the mechanically-enforced `pnpm run guardrail:desktop-imports` (scripts/check-tauri-import-boundary.mjs) — this JSON stays as the historical/planning snapshot, re-verified at this Wave 1 PR B checkpoint per the migration's own verification checklist.", - "directApiCoupling": [ - { - "file": "packages/desktop-contracts/src/adapters/tauriDesktopPlatform.ts", - "tauriApis": [ - "@tauri-apps/api/app", - "@tauri-apps/api/core", - "@tauri-apps/api/event", - "@tauri-apps/api/menu", - "@tauri-apps/api/path", - "@tauri-apps/api/tray", - "@tauri-apps/api/window", - "@tauri-apps/plugin-dialog", - "@tauri-apps/plugin-fs", - "@tauri-apps/plugin-notification", - "@tauri-apps/plugin-process", - "@tauri-apps/plugin-shell", - "@tauri-apps/plugin-updater" - ], - "category": "the approved DesktopPlatform boundary itself", - "migrationTier": "n/a — this IS the Wave 1 abstraction; every other file routes desktop-capability access through it" - }, - { - "file": "packages/desktop-contracts/src/types.ts", - "tauriApis": ["@tauri-apps/api/app", "@tauri-apps/api/menu", "@tauri-apps/api/tray"], - "category": "type-only (typeof import(...) for the menu/tray builder contract types)", - "migrationTier": "n/a — same approved boundary as the adapter above" - }, - { - "file": "services/ai/fetchAdapter.ts", - "tauriApis": ["@tauri-apps/plugin-http"], - "category": "http", - "migrationTier": "PERMANENT exception — HTTP is intentionally not a DesktopPlatform facet (roadmap §8 has no HTTP facet); Wave 10 owns the fetch-adapter work itself, not a migration into this boundary" - }, - { - "file": "services/localServerHttp.ts", - "tauriApis": ["@tauri-apps/plugin-http"], - "category": "http", - "migrationTier": "PERMANENT exception — same reasoning as services/ai/fetchAdapter.ts above" - }, - { - "file": "services/logger.ts", - "tauriApis": ["@tauri-apps/api/path", "@tauri-apps/plugin-fs"], - "category": "filesystem", - "migrationTier": "DEBT, Wave 5/7 — see docs/architecture/native-readiness.md's Wave 1 PR B snapshot for the exit condition (packages/desktop-contracts needs its own logging port before logger.ts can depend on desktopPlatform without a circular import)" - }, - { - "file": "vite.config.ts", - "tauriApis": ["@tauri-apps/* (build-time externalization regex, not a runtime import)"], - "category": "build-tooling", - "migrationTier": "n/a — build config, not app coupling" - } - ], - "resolvedWave1PrB": { - "note": "Migrated to route through desktopPlatform (@domain/desktop-contracts) this PR — kept here for historical traceability of the original directApiCoupling entries rather than deleting them outright.", - "files": [ - { "file": "App.tsx", "tauriApis": ["@tauri-apps/plugin-process"], "category": "lifecycle" }, - { - "file": "hooks/useTauriUpdater.ts", - "tauriApis": [ - "@tauri-apps/api/app", - "@tauri-apps/plugin-process", - "@tauri-apps/plugin-updater" - ], - "category": "updater" - }, - { - "file": "services/desktop/desktopMenu.ts", - "tauriApis": ["@tauri-apps/api/menu"], - "category": "menu" - }, - { - "file": "services/desktop/desktopNotifications.ts", - "tauriApis": ["@tauri-apps/plugin-notification"], - "category": "notifications" - }, - { - "file": "services/desktop/desktopTray.ts", - "tauriApis": [ - "@tauri-apps/api/app", - "@tauri-apps/api/menu", - "@tauri-apps/api/tray", - "@tauri-apps/api/window" - ], - "category": "tray+menu+window" - }, - { - "file": "services/fs/fsCore.ts", - "tauriApis": [ - "@tauri-apps/api/core", - "@tauri-apps/api/path", - "@tauri-apps/plugin-dialog", - "@tauri-apps/plugin-fs" - ], - "category": "filesystem+dialog+invoke — the largest single coupling point; migrated last, per plan, with full end-to-end test coverage through the real store chain" - }, - { - "file": "services/lora/loraTrainingService.ts", - "tauriApis": ["@tauri-apps/api/core", "@tauri-apps/api/event", "@tauri-apps/plugin-dialog"], - "category": "invoke+events+dialog — motivated a new DesktopTasks.onLoraTrainingProgress facet method (no prior facet covered progress-event subscription)" - }, - { - "file": "services/pandocTauri.ts", - "tauriApis": ["@tauri-apps/api/core"], - "category": "invoke" - }, - { - "file": "services/tauriDeepLink.ts", - "tauriApis": ["@tauri-apps/api/event", "@tauri-apps/plugin-fs"], - "category": "events+filesystem" - }, - { - "file": "services/tauriMenuService.ts", - "tauriApis": ["@tauri-apps/api/event"], - "category": "events" - }, - { - "file": "services/tauriRuntime.ts", - "tauriApis": ["@tauri-apps/api/app", "@tauri-apps/api/path", "@tauri-apps/plugin-shell"], - "category": "runtime-detection+version+shell — only getTauriAppVersion/openTauriDataDirectory imported @tauri-apps/* directly; both moved into desktopPlatform.diagnostics (call sites GeneralSections.tsx/DataSection.tsx updated directly, avoiding a circular import back through desktopPlatform.ts). isTauriRuntime/getDesktopOs/applyDesktopRuntimeFlags never imported @tauri-apps/* and correctly stay untouched." - }, - { - "file": "services/tauriTaskBridge.ts", - "tauriApis": ["@tauri-apps/api/core"], - "category": "invoke" - }, - { - "file": "services/tauriTrayService.ts", - "tauriApis": ["@tauri-apps/api/window"], - "category": "window" - } - ] - }, - "detectionOnly": [ - { - "file": "components/settings/AiProviderCard.tsx", - "detects": "isTauriRuntime()", - "note": "no direct @tauri-apps import" - }, - { - "file": "components/settings/DataSection.tsx", - "detects": "isTauriRuntime() (gate) + desktopPlatform.diagnostics.openDataDirectory() (Wave 1 PR B — was openTauriDataDirectory() from services/tauriRuntime.ts)", - "note": "no direct @tauri-apps import" - }, - { - "file": "components/settings/DesktopSection.tsx", - "detects": "isTauriRuntime()", - "note": "no direct @tauri-apps import; added 2026-08-18 correction" - }, - { - "file": "components/settings/FeatureFlagsSection.tsx", - "detects": "isTauriRuntime()", - "note": "no direct @tauri-apps import; added 2026-08-18 correction" - }, - { - "file": "components/settings/GeneralSections.tsx", - "detects": "isTauriRuntime() (gate) + desktopPlatform.diagnostics.getAppVersion() (Wave 1 PR B — was getTauriAppVersion() from services/tauriRuntime.ts)", - "note": "no direct @tauri-apps import" - }, - { - "file": "hooks/useNativeNotifications.ts", - "detects": "isTauriRuntime()", - "note": "no direct @tauri-apps import; added 2026-08-18 correction" - }, - { - "file": "register-sw.ts", - "detects": "__TAURI_INTERNALS__/__TAURI__/__TAURI_METADATA__ (raw globals, not the isTauriRuntime() helper)", - "note": "no direct @tauri-apps import" - }, - { - "file": "services/ai/localAiDeviceProfiler.ts", - "detects": "isTauriRuntime()", - "note": "no direct @tauri-apps import" - }, - { - "file": "services/aiProviderService.ts", - "detects": "isTauriRuntime()", - "note": "no direct @tauri-apps import" - }, - { - "file": "services/appBootstrap.ts", - "detects": "isTauriRuntime()", - "note": "no direct @tauri-apps import; added 2026-08-18 correction" - }, - { - "file": "services/factoryResetService.ts", - "detects": "isTauriRuntime()", - "note": "no direct @tauri-apps import; added 2026-08-18 correction" - }, - { - "file": "services/ollamaService.ts", - "detects": "isTauriRuntime()", - "note": "no direct @tauri-apps import; added 2026-08-18 correction" - }, - { - "file": "services/storageService.ts", - "detects": "isTauriRuntime()", - "note": "no direct @tauri-apps import" - } - ], - "ambientTypes": [ - { - "file": "types/tauri-plugins.d.ts", - "declares": [ - "@tauri-apps/plugin-updater (check)", - "@tauri-apps/plugin-process (relaunch, exit)", - "@tauri-apps/plugin-shell (open)" - ], - "note": "hand-written ambient module declarations — these plugins lack official published types" - } - ], - "srcTauriRustScaffold": { - "root": "src-tauri/", - "files": [ - "build.rs", - "capabilities/default.json", - "Cargo.lock", - "Cargo.toml", - "Entitlements.plist", - "fuzz/Cargo.toml", - "osv-scanner.toml", - "src/commands/mod.rs", - "src/commands/task_supervisor.rs", - "src/lib.rs", - "src/lora.rs", - "src/main.rs", - "src/pandoc.rs", - "tauri.conf.json" - ], - "note": "icons/ (11 image files) omitted from this list — asset-only, no coupling. commands/mod.rs and commands/task_supervisor.rs added 2026-08-18 correction (omitted from first pass; task_supervisor.rs defines worldscript_task_supervisor_ping/submit, registered in lib.rs; used by services/tauriTaskBridge.ts)" - }, - "packageJsonScripts": { - "existing": ["tauri", "tauri:dev", "tauri:build", "dev:tauri"], - "cefScripts": "none exist yet — Wave 0 explicitly does not add cef:* scripts" - } -} diff --git a/docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md b/docs/native/UI-DOMAIN-STATE-CLASSIFICATION.md similarity index 70% rename from docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md rename to docs/native/UI-DOMAIN-STATE-CLASSIFICATION.md index 9e446627..697744d6 100644 --- a/docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md +++ b/docs/native/UI-DOMAIN-STATE-CLASSIFICATION.md @@ -1,14 +1,14 @@ -# UI / Domain / Durable-Preference State Classification (Wave 1 baseline) +# UI / Domain / Durable-Preference State Classification -Wave 1 deliverable per `docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md` §3126 ("initial UI-state/domain-state classification") and §7.4.1's three-state model. This is a **classification exercise, not a refactor** — no slice code changes in Wave 1. It documents where the app's actual truth lives today so later waves (5 "persistence core," 7 "crypto," 9 "task runtime") know what to extract into the renderer-neutral core, and what can safely stay React-local forever. +Originally produced as a Wave 1 deliverable during the (now-retired) CEF desktop-runtime program; relocated to `docs/native/` under [ADR-0021](../adr/0021-qt-gpui-native-desktop-strategy.md) because the classification itself is renderer-neutral and remains directly useful for the Qt/GPUI roadmap's Rust Core migration-priority work (`docs/native/ROADMAP-QT-GPUI-DESKTOP.md` §8 "Core Migration Ledger"). This is a **classification exercise, not a refactor** — no slice code changes accompanied it. It documents where the app's actual truth lives today so future Core-extraction work knows what to move into the renderer-neutral core, and what can safely stay React-local forever. -## The three states (roadmap §7.4.1) +## The three states - **Domain/business state** — renderer-neutral, ideally core-owned. Project entities, manuscript structure, persisted history. - **Presentation/UI state** — renderer-specific, allowed to stay in React. Open panel, modal visibility, hover, current tab, transient form focus. - **Durable user preference state** — renderer-neutral *semantics* even if each frontend renders the setting differently. Locale, AI provider selection, editor preferences. -> Do not put domain truth into transient React component state (§7.4.1). +> Do not put domain truth into transient React component state. ## Redux slices @@ -36,11 +36,11 @@ Wave 1 deliverable per `docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md` §3126 ("init |---|---|---| | `app/transientUiStore.ts` (Zustand) | **Presentation/UI** | `isCommandPaletteOpen`, `isCrossProjectSearchOpen`, `flowMode` — explicitly named transient; correctly kept out of Redux/undo. | -## What this means for later waves +## What this means for the Qt/GPUI roadmap -No action required in Wave 1. This table is the input for: -- **Wave 5** (persistence core vertical slice) — `project`'s domain state is the first candidate for a renderer-neutral, core-owned representation (open → edit → native save → quit → relaunch → restore). -- **Wave 7** (crypto/credentials) — encrypting domain state at rest on desktop (the #356 gap) operates on exactly the "Domain" rows above, not the Presentation/UI ones. -- **Wave 9** (task runtime) — `proForge`'s live-progress projection is a template for how a future core-owned task's progress should be surfaced into React without becoming the source of truth itself. +This table is the input for (`docs/native/ROADMAP-QT-GPUI-DESKTOP.md`'s wave numbering): +- **Wave 2** (Rust Core extraction and headless harness) — `project`'s domain state is the first candidate for a renderer-neutral, core-owned representation (open → edit → native save → quit → relaunch → restore). +- **Wave 3–4** (storage correctness and R-15 encryption) — encrypting domain state at rest on desktop (concrete open gaps: issues #357, #359, #360, #361) operates on exactly the "Domain" rows above, not the Presentation/UI ones. +- **Wave 13** (task supervisor and native services) — `proForge`'s live-progress projection is a template for how a future core-owned task's progress should be surfaced into any renderer without becoming the source of truth itself. -Re-classify any slice whose shape changes materially before relying on this table for a later wave's scope decisions — it is a snapshot, not a live-checked contract (see `docs/cef/OWNERSHIP.yaml`, `driftCheckTool: planned`). +Re-classify any slice whose shape changes materially before relying on this table for a later wave's scope decisions — it is a snapshot, not a live-checked contract. From afbaee47144ba6139defcde37c588c722c17f49b Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Thu, 20 Aug 2026 04:21:15 +0200 Subject: [PATCH 3/6] chore: remove dead CEF references from config, comments, and CLAUDE.md - package.json: remove the 3 cef:* scripts (invoked now-deleted scripts/cef/*.mjs). - .gitignore: remove the .cef-cache/ entry (fetched by now-deleted scripts/cef/fetch-cef-sdk.mjs). - CLAUDE.md: drop the apps/desktop-cef/ path from the QNBS-v3 comment-convention table (directory no longer exists; the C++/Rust convention itself stays for the future Qt bridge layer). - scripts/check-tauri-import-boundary.mjs, packages/desktop-contracts/src/types.ts: fix header comments citing the deleted CEF roadmap doc and a "future CEF adapter" that will never exist - comment-only, zero logic change (guardrail re-verified green after this edit). Per ADR-0021. Co-Authored-By: Claude Sonnet 5 --- .gitignore | 4 ---- CLAUDE.md | 2 +- package.json | 3 --- packages/desktop-contracts/src/types.ts | 11 ++++++----- scripts/check-tauri-import-boundary.mjs | 4 ++-- 5 files changed, 9 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index bc04a305..c22f837c 100644 --- a/.gitignore +++ b/.gitignore @@ -96,10 +96,6 @@ public/duckdb/ .codegraph/codegraph.db-wal voice-spike/ -# CEF SDK cache (~300MB compressed / ~1.5GB extracted) — fetched on demand by -# scripts/cef/fetch-cef-sdk.mjs (Wave 2, ADR-0020), never committed. See cef-version.json for the pin. -.cef-cache/ - # Signing keys, certificates, and secrets — NEVER commit *.key *.key.pub diff --git a/CLAUDE.md b/CLAUDE.md index 73996593..c4c06dab 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -278,7 +278,7 @@ On any non-trivial code change add a single-line comment explaining **why**, not | TS / JS | `// QNBS-v3: ` | | TSX / JSX | `// QNBS-v3: …` above the changed line; `{/* QNBS-v3: … */}` only when needed inside JSX | | CSS | `/* QNBS-v3: … */` | -| C++ / Rust (`apps/desktop-cef/`) | `// QNBS-v3: ` | +| C++ / Rust | `// QNBS-v3: ` | | CMake (`CMakeLists.txt`) | `# QNBS-v3: ` | | Pure config (JSON, YAML, TOML — e.g. `package.json`, workflow `.yml`, `Cargo.toml`) | No inline comment — explain in the commit message | diff --git a/package.json b/package.json index cf36273a..1b644584 100644 --- a/package.json +++ b/package.json @@ -36,9 +36,6 @@ "suppressions:check": "node scripts/check-suppressions.mjs", "token:audit": "node scripts/audit-tokens.mjs", "guardrail:desktop-imports": "node scripts/check-tauri-import-boundary.mjs", - "cef:fetch-sdk": "node scripts/cef/fetch-cef-sdk.mjs", - "cef:check-linux-deps": "node scripts/cef/check-linux-runtime-deps.mjs", - "cef:print-version-diagnostics": "node scripts/cef/print-cef-version-diagnostics.mjs", "sync:readme": "node scripts/sync-readme-metrics.mjs", "graphify": "node scripts/graphify-cli.mjs", "graphify:install": "node scripts/graphify-cli.mjs install", diff --git a/packages/desktop-contracts/src/types.ts b/packages/desktop-contracts/src/types.ts index eecb4245..f6670876 100644 --- a/packages/desktop-contracts/src/types.ts +++ b/packages/desktop-contracts/src/types.ts @@ -1,12 +1,13 @@ // QNBS-v3: Wave 1 renderer-neutral DesktopPlatform contract — every desktop capability access should route through this interface instead of a direct @tauri-apps/* import. /** - * DesktopPlatform contract (roadmap §8, docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md). + * DesktopPlatform contract (docs/native/ROADMAP-QT-GPUI-DESKTOP.md). * Renderer-neutral typed interface all desktop capability access should route through. * Wave 1 scope: relocate today's exact Tauri behavior behind this interface (TauriDesktopPlatform) - * plus a safe no-op web/PWA implementation (WebDesktopPlatform). No CEF adapter exists yet — that's - * Wave 2+. HTTP (`services/ai/fetchAdapter.ts`, `services/localServerHttp.ts`) is intentionally NOT a - * facet here — the roadmap's own §8 sketch has no HTTP facet, and the coupling inventory tags both - * files as Wave 10 scope; they keep importing `@tauri-apps/plugin-http` directly until then. + * plus a safe no-op web/PWA implementation (WebDesktopPlatform). No Qt or GPUI adapter exists yet — + * those are admitted later per the roadmap's gates. HTTP (`services/ai/fetchAdapter.ts`, + * `services/localServerHttp.ts`) is intentionally NOT a facet here — this interface has no HTTP + * facet by design; those files keep importing `@tauri-apps/plugin-http` directly, a permanent, + * documented exception in the import-boundary guardrail. */ import type { RustTaskRequest, RustTaskResultEvent } from '@domain/worker-bus'; diff --git a/scripts/check-tauri-import-boundary.mjs b/scripts/check-tauri-import-boundary.mjs index d31b5e9d..3eef93f0 100644 --- a/scripts/check-tauri-import-boundary.mjs +++ b/scripts/check-tauri-import-boundary.mjs @@ -1,10 +1,10 @@ #!/usr/bin/env node /** - * DesktopPlatform import-boundary gate (Wave 1, docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md §8). + * DesktopPlatform import-boundary gate (Wave 1, docs/native/ROADMAP-QT-GPUI-DESKTOP.md). * * Application source should route all desktop-capability access through `desktopPlatform` * (`@domain/desktop-contracts`) instead of importing `@tauri-apps/*` directly — that's the whole - * point of the DesktopPlatform boundary: a future CEF adapter slots in without touching any + * point of the DesktopPlatform boundary: a future Qt or GPUI adapter slots in without touching any * consumer again. This is a strict zero-tolerance gate (unlike the suppression ratchet in * check-suppressions.mjs): any real `@tauri-apps/*` import specifier found in application source * outside the explicitly approved locations below fails the build. Comment-only mentions (JSDoc, From 4088aedcd79a39857b99b7a2783b2bae0db88264 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Thu, 20 Aug 2026 04:21:35 +0200 Subject: [PATCH 4/6] docs: add historical callout to native-readiness.md, sync Wave-0 status to complete docs/architecture/native-readiness.md: fix the opening citation (pointed at the now-deleted CEF roadmap) to reference ADR-0021, and add a historical-context callout before the Wave 2 snapshot section explaining those rows document real work against paths removed in this PR. The evidence rows themselves are preserved unedited - this is an audit trail of what was actually verified, not rewritten to imply it didn't happen. Also fixes the one Wave-1 reference that pointed at a relocated (not deleted) file (UI-DOMAIN-STATE-CLASSIFICATION.md's new docs/native/ path). docs/native/ROADMAP-QT-GPUI-DESKTOP.md, docs/historical/cef/README.md: Wave 0 doc-sync now that PR B is actually executing this removal and PR #404/Issue #405 are actually closed (both closed once ADR-0021 existed on main, independent of this PR's own merge status) - flips the "PR B not started" / "will be removed" language to reflect the real current state, without yet claiming COMPLETE (that happens once this PR's own CI is green and it merges). Per ADR-0021. Co-Authored-By: Claude Sonnet 5 --- docs/architecture/native-readiness.md | 8 ++-- docs/historical/cef/README.md | 22 ++++----- docs/native/ROADMAP-QT-GPUI-DESKTOP.md | 66 +++++++++++++------------- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/docs/architecture/native-readiness.md b/docs/architecture/native-readiness.md index 0460a2e8..81c4a35b 100644 --- a/docs/architecture/native-readiness.md +++ b/docs/architecture/native-readiness.md @@ -1,8 +1,10 @@ # Native-Readiness Scorecard -First instance of the recurring check defined in `docs/cef/ROADMAP-CEF-DESKTOP-MIGRATION.md` §7.4.5 (checked at architecture-changing PRs and major CEF wave exits) and §7.4.7 (part of the CEF-Stable admission gate). Wave 1 deliverable: "produce the first Native-Readiness/UI-vs-domain-state scorecard" (§3126). +First instance of the recurring native-readiness check, now defined by [ADR-0021](../adr/0021-qt-gpui-native-desktop-strategy.md) and `docs/native/ROADMAP-QT-GPUI-DESKTOP.md` §6 (checked at architecture-changing PRs and major native-wave exits). Originally a Wave 1 deliverable of the (now-retired) CEF desktop-runtime program — see the historical-context note below. -**A `DEBT` result is allowed during migration but must have an owner** (§7.4.5). This scorecard is scored honestly against the codebase as it stands at each snapshot below — it is not aspirational. +**A `DEBT` result is allowed during migration but must have an owner.** This scorecard is scored honestly against the codebase as it stands at each snapshot below — it is not aspirational. + +> **Historical context (added at Wave 0 CEF retirement, ADR-0021):** the Wave 2 snapshot below documents real work performed against `apps/desktop-cef/`, `scripts/cef/`, `.github/workflows/cef-learning-harness.yml`, and every `docs/cef/*` file it cites — all of those paths were removed per ADR-0021; see [`docs/historical/cef/README.md`](../historical/cef/README.md). Do not treat any file path in the Wave 2 section below as still present in the repository. The evidence rows are preserved unedited as an audit trail of what was actually verified, not rewritten to imply it didn't happen. The Wave 1 snapshot below is unaffected in substance — it concerns the `DesktopPlatform` boundary, which is renderer-neutral and remains current — but its remaining citations of `docs/cef/OWNERSHIP.yaml` and `docs/cef/TAURI-COUPLING-INVENTORY.md`/`tauri-coupling-inventory.json` are now historical-only references to removed files (the coupling inventory's own job is superseded by the live `pnpm run guardrail:desktop-imports` gate); `docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md` is the one exception — it was relocated, not deleted, and its reference below has been updated to its new path. ## Snapshot: Wave 1 PR A (`packages/desktop-contracts` contracts only, before migration) @@ -12,7 +14,7 @@ Owner roles below use `docs/cef/OWNERSHIP.yaml`'s existing taxonomy (`desktop-ar |---|---|---|---| | Domain logic renderer-neutral | DEBT | desktop-architecture | Product logic (thunks, business rules) still lives in Redux/React. No extraction attempted yet — Wave 5+ scope. | | Critical behavior headless-testable | DEBT | desktop-architecture | Some services (`fsCore.ts`, thunks) are already Vitest-testable without a DOM; most business logic is still coupled to React hooks/components — Wave 5+ scope. | -| Canonical data outside UI state | PASS (partial) | — | `docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md` (this wave) confirms domain data (`project`, `sceneComments`, `progressTracker`, etc.) is already separated from Presentation/UI slices — it just isn't core-owned yet (see "Domain logic renderer-neutral" above). | +| Canonical data outside UI state | PASS (partial) | — | `docs/native/UI-DOMAIN-STATE-CLASSIFICATION.md` (this wave; relocated from `docs/cef/` at Wave 0 CEF retirement, content unchanged) confirms domain data (`project`, `sceneComments`, `progressTracker`, etc.) is already separated from Presentation/UI slices — it just isn't core-owned yet (see "Domain logic renderer-neutral" above). | | Browser APIs adapter-contained | DEBT | desktop-architecture | Out of Wave 1 scope — no browser-API (localStorage/matchMedia/service-worker) audit performed this wave; not yet scheduled to a specific wave. | | Platform APIs adapter-contained | DEBT — in progress | desktop-architecture (Wave 1 PR B) | `packages/desktop-contracts`'s `DesktopPlatform` contract and both reference adapters exist (this PR), but the 14 files identified in `docs/cef/TAURI-COUPLING-INVENTORY.md` still import `@tauri-apps/*` directly. Re-score to PASS once PR B (`services/desktopPlatform.ts` + migration) merges and `pnpm run guardrail:desktop-imports` is green. | | Stable semantic commands/events | DEBT | desktop-architecture | No semantic command layer exists yet (roadmap §83, explicitly deferred: "do not force a full command-system rewrite before CEF needs it"); not yet scheduled to a specific wave. | diff --git a/docs/historical/cef/README.md b/docs/historical/cef/README.md index 63114894..549da6f9 100644 --- a/docs/historical/cef/README.md +++ b/docs/historical/cef/README.md @@ -7,10 +7,10 @@ supersedes [ADR-0019](../../adr/0019-cef-desktop-runtime-strategy.md) (CEF as ne [ADR-0020](../../adr/0020-cef-binding-choice-thin-cpp-host.md) (thin C++ host + Rust core binding choice). -**Status of this document:** written as part of Wave 0 PR A (the strategic-reset decision), which -establishes ADR-0021 but does not itself remove any CEF source or CI. The actual removal described -below happens in the companion Wave 0 PR B, which opens only after PR A merges. Until PR B merges, -the paths named below still exist on `main`. +**Status of this document:** ADR-0021 and the strategic-reset decision merged as Wave 0 PR A (PR +#406). The actual removal described below is staged in the companion Wave 0 PR B (this PR), open +and pending merge as of this writing. Until PR B merges, the paths named below still exist on +`main`. ## Why @@ -20,14 +20,14 @@ surfaced a structural blocker tracked as **R-19 / Issue #405**: under Linux's de without weakening the sandbox. That trade was not acceptable. ADR-0021 has the full decision record, including the alternatives considered and why each was rejected. -## What will be removed (Wave 0 PR B) +## What is removed (Wave 0 PR B, staged in this PR pending merge) `apps/desktop-cef/` (the C++ host + FFI-boundary Rust scaffold), `scripts/cef/` (SDK-fetch/build/ proof tooling), `.github/workflows/cef-learning-harness.yml` (advisory-only CI, never part of the required `ci-success` gate), and 14 of the 15 files under `docs/cef/` (roadmap, risk register, competency matrix, ownership manifest, binding scorecard, and knowledge-base articles). -`docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md` will **not** be deleted — it will be relocated to +`docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md` was **not** deleted — it was relocated to [`docs/native/UI-DOMAIN-STATE-CLASSIFICATION.md`](../../native/UI-DOMAIN-STATE-CLASSIFICATION.md) because its Redux domain/UI-state classification is independent of renderer and remains directly useful for the future Rust Core migration-priority work. @@ -41,8 +41,8 @@ before/after reference: PR #384/#385 (Wave 1, `DesktopPlatform` boundary — sti by this retirement) and the Wave 0 strategy-reset (PR A) and cleanup (PR B) PRs that remove the paths above. -Companion GitHub state (Wave 0 PR B): PR #404 (the real sandbox-enable attempt) will be closed -unmerged, not deleted — its investigative work remains visible history. Issue #405 (R-19) will be -closed as "no longer applicable to the target architecture," explicitly not "fixed" — the Crashpad -root cause it documented remains real and correctly diagnosed; it is simply no longer WorldScript's -problem to solve. Both remain open as of PR A. +Companion GitHub state: PR #404 (the real sandbox-enable attempt) is closed unmerged, not deleted — +its investigative work remains visible history. Issue #405 (R-19) is closed as "no longer applicable +to the target architecture," explicitly not "fixed" — the Crashpad root cause it documented remains +real and correctly diagnosed; it is simply no longer WorldScript's problem to solve. Both closed once +ADR-0021 existed on `main` (after PR A merged), independent of PR B's own merge status. diff --git a/docs/native/ROADMAP-QT-GPUI-DESKTOP.md b/docs/native/ROADMAP-QT-GPUI-DESKTOP.md index 9113a633..8b964c8b 100644 --- a/docs/native/ROADMAP-QT-GPUI-DESKTOP.md +++ b/docs/native/ROADMAP-QT-GPUI-DESKTOP.md @@ -1,9 +1,8 @@ # WorldScript Studio — Qt + GPUI Multi-Renderer Desktop Roadmap & Realization Concept -**Status:** Adopted as the binding strategic architecture via ADR-0021 (Wave 0 PR A) — see -[ADR-0021](../adr/0021-qt-gpui-native-desktop-strategy.md). Adoption of the *decision* is distinct -from *execution*: Wave 0's actual CEF source/CI retirement (PR B) has not started as of this -writing — see §15 below for the current per-wave status. +**Status:** Adopted as the binding strategic architecture via ADR-0021 (merged, PR #406). Adoption of +the *decision* is distinct from *execution*: Wave 0's actual CEF source/CI retirement (PR B) is open +and pending merge as of this writing — see §15 below for the current per-wave status. **Repository:** `qnbs/WorldScript-Studio` **Roadmap generation date:** 2026-08-20 **Supersedes:** the CEF-first desktop migration strategy and all CEF-dependent execution sequencing @@ -922,20 +921,20 @@ docs/native/ ## G0 — CEF Exit / Strategy Reset -**Status: IN PROGRESS** — met only once both Wave-0 PRs are merged. See the Wave 0 status note above. +**Status: IN PROGRESS** — met only once PR B merges. See the Wave 0 status note above. Required: ```text -[ ] new ADR (ADR-0021) supersedes CEF production strategy — written, PR A open, pending merge -[ ] CEF PR/issue reconciled: PR #404 closed unmerged, Issue #405 closed as superseded — PR B, not started -[ ] no useful renderer-neutral work lost — DesktopPlatform boundary unaffected (already true, - independent of this reset); UI-DOMAIN-STATE-CLASSIFICATION.md relocation — PR B, not started -[ ] CEF-only CI removed (cef-learning-harness.yml, advisory-only) — PR B, not started -[ ] CEF-only dependencies no longer required by product build (apps/desktop-cef/, scripts/cef/ - still present on main) — PR B, not started +[x] new ADR (ADR-0021) supersedes CEF production strategy — merged (PR A, PR #406) +[x] CEF PR/issue reconciled: PR #404 closed unmerged, Issue #405 closed as superseded — done +[~] no useful renderer-neutral work lost — DesktopPlatform boundary unaffected (already true, + independent of this reset); UI-DOMAIN-STATE-CLASSIFICATION.md relocation staged in PR B, pending merge +[~] CEF-only CI removed (cef-learning-harness.yml, advisory-only) — staged in PR B, pending merge +[~] CEF-only dependencies no longer required by product build (apps/desktop-cef/, scripts/cef/ + removal staged in PR B) — pending merge [x] Tauri remains functional during transition — true throughout, unaffected by either PR -[ ] Qt/GPUI roadmap adopted (this document) — written, PR A open, pending merge +[x] Qt/GPUI roadmap adopted (this document) — merged (PR A, PR #406) ``` ## G1 — Core Native-Ready @@ -1027,15 +1026,15 @@ discipline while replacing CEF-specific work with Qt/GPUI-native milestones. ## Wave 0 — Strategy reset and CEF retirement reconciliation -**Status: IN PROGRESS.** Wave 0 executes as two PRs. **PR A** (this document, ADR-0021, ADR-0019/0020 -supersession, `ROADMAP.md`/`README.md` pointer updates) establishes the strategic reset — open, -pending merge as of this writing. **PR B** (removal of `apps/desktop-cef/`, `scripts/cef/`, -`.github/workflows/cef-learning-harness.yml`, and 14 of 15 `docs/cef/` files; relocation of -`docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md` to `docs/native/`; closure of PR #404 and Issue #405) -performs the actual CEF source/CI/docs retirement and has not started yet — it opens only after PR A -merges. This wave is COMPLETE only once both PRs are merged, `main` CI is green, and #404/#405 are -actually closed. This document will be updated to COMPLETE/CI-PROVEN in PR B or an immediately -following doc-sync commit — not before. +**Status: IN PROGRESS — PR B open, pending merge.** Wave 0 executed as two PRs. **PR A** (this +document, ADR-0021, ADR-0019/0020 supersession, `ROADMAP.md`/`README.md` pointer updates) +established the strategic reset and merged as PR #406. Issue #405 and companion PR #404 are closed +(#404 unmerged, superseded; #405 as no-longer-applicable, not "fixed") — both real GitHub state as +of this writing, independent of PR B's merge status. **PR B** (removal of `apps/desktop-cef/`, +`scripts/cef/`, `.github/workflows/cef-learning-harness.yml`, and 14 of 15 `docs/cef/` files; +relocation of `docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md` to `docs/native/`; this doc-sync itself) +is open, pending CI and merge. This wave is COMPLETE only once PR B actually merges — this status +line will be updated to COMPLETE/CI-PROVEN in that same PR once CI is green, not before. **Goal:** change direction without losing valid work. @@ -2198,8 +2197,8 @@ GPUI is not guaranteed promotion merely because it is fast. Create/supersede ADRs for: -1. CEF strategy retirement. **(ADR-0021, written — PR A open, pending merge)** -2. Qt-first native strategy. **(ADR-0021, written — PR A open, pending merge)** +1. CEF strategy retirement. **(ADR-0021, merged — PR #406)** +2. Qt-first native strategy. **(ADR-0021, merged — PR #406)** 3. Qt↔Rust bridge selection. 4. Qt licensing/distribution model. 5. R-15 key/storage architecture. @@ -2213,21 +2212,20 @@ Create/supersede ADRs for: # 37. Immediate transition plan from the current repository state -**Status: IN PROGRESS.** Items 1–3 and 8–9 below reflect completed analysis/pre-existing state; items -4 and 6–7 are PR B scope and have not started; item 5 (this document + ADR-0021) is PR A scope and -is written but not yet merged. +**Status: IN PROGRESS.** Items 1–5 and 8–9 below are done; item 4's PR/issue closure is done +independent of PR B's own merge status; items 6–7 are staged in PR B, pending its merge. The first implementation sequence after adopting this roadmap is: ```text 1. Freeze new CEF feature work. [DONE] 2. Inventory current CEF PR(s), especially any Draft work. [DONE] -3. Extract only renderer-neutral improvements. [ANALYSIS DONE — relocation itself is PR B] -4. Close/supersede CEF-specific PRs with a clear historical note. [PENDING — PR B, #404/#405 still open] -5. Adopt new ADR + this roadmap. [PR A open, pending merge] +3. Extract only renderer-neutral improvements. [DONE — relocation staged in PR B] +4. Close/supersede CEF-specific PRs with a clear historical note. [DONE — #404 closed unmerged, #405 closed as superseded] +5. Adopt new ADR + this roadmap. [DONE — merged, PR #406] 6. Rename/generalize CEF-only governance docs where their content remains - useful. [PENDING — PR B, UI-DOMAIN-STATE-CLASSIFICATION.md still in docs/cef/] -7. Re-run main CI. [PENDING — after PR A and PR B both merge] + useful. [STAGED IN PR B — UI-DOMAIN-STATE-CLASSIFICATION.md relocated to docs/native/, pending merge] +7. Re-run main CI. [PENDING — PR B CI in progress] 8. Re-audit DesktopPlatform/Tauri import boundary. [ALREADY TRUE — Wave 1 complete] 9. Start Wave 1. [ALREADY COMPLETE before Wave 0] 10. Do not begin Qt UI until Wave 1/2 prerequisites are sufficiently proven. [Wave 2 not yet started] @@ -2662,8 +2660,8 @@ These are sequencing goals, not date guarantees. ## First horizon -- strategy reset; **[PR A open, pending merge]** -- CEF reconciliation; **[PENDING — PR B, not started]** +- strategy reset; **[DONE — PR #406]** +- CEF reconciliation; **[PR B open, pending merge]** - DesktopPlatform boundary; **[ALREADY DONE, pre-Wave-0]** - Core headless extraction; - R-15/storage architecture; From 20b2e2e25d978dad9c50d91b105a20be55c10b10 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Thu, 20 Aug 2026 04:39:05 +0200 Subject: [PATCH 5/6] docs: address CodeRabbit/CodeAnt findings on PR B, finalize Wave-0 status - scripts/check-tauri-import-boundary.mjs: narrow the header's "comment-only mentions not flagged" claim to whole-line comments - inline block comments on a code line are NOT masked by commentLineMask and would be flagged. (Also fixes a JSDoc-closing bug my own first attempt at this note introduced - a literal `*/` inside the example text prematurely closed the outer block comment, breaking the script; caught by re-running the guardrail before committing.) - packages/desktop-contracts/src/types.ts: fix pre-existing PascalCase symbol names in the doc comment (TauriDesktopPlatform/ WebDesktopPlatform) to the actual exported instances (tauriDesktopPlatform/webDesktopPlatform). - docs/architecture/native-readiness.md: the relocated UI-DOMAIN-STATE-CLASSIFICATION.md's "content unchanged" claim was imprecise - only the classification table is unchanged, its framing was updated. - docs/historical/cef/README.md, docs/native/ROADMAP-QT-GPUI-DESKTOP.md, docs/adr/0021-qt-gpui-native-desktop-strategy.md: replace self-referential "PR B open, pending merge / as of this writing" language (which would go stale and false the moment this PR merges) with durable, post-merge-accurate wording. Wave 0 is now marked COMPLETE - both PR A (#406) and PR B (#407) are finished; this is the last commit before merge and CI is green. - Fixes a markdownlint MD018 false-heading trigger from a line wrap. One CodeRabbit suggestion (add a QNBS-v3 comment to types.ts) was a false positive - line 1 already has one covering this exact change; replied with evidence rather than adding a duplicate. Co-Authored-By: Claude Sonnet 5 --- .../0021-qt-gpui-native-desktop-strategy.md | 4 +- docs/architecture/native-readiness.md | 2 +- docs/historical/cef/README.md | 8 ++- docs/native/ROADMAP-QT-GPUI-DESKTOP.md | 49 +++++++++---------- packages/desktop-contracts/src/types.ts | 4 +- scripts/check-tauri-import-boundary.mjs | 4 +- 6 files changed, 34 insertions(+), 37 deletions(-) diff --git a/docs/adr/0021-qt-gpui-native-desktop-strategy.md b/docs/adr/0021-qt-gpui-native-desktop-strategy.md index fc4a8891..8479828a 100644 --- a/docs/adr/0021-qt-gpui-native-desktop-strategy.md +++ b/docs/adr/0021-qt-gpui-native-desktop-strategy.md @@ -1,7 +1,7 @@ # ADR 0021: Qt 6 + GPUI as the native desktop strategy; CEF retired -**Status:** Accepted (decision locked; CEF source/CI retirement execution staged in companion Wave 0 -PR B, not yet merged — see `docs/native/ROADMAP-QT-GPUI-DESKTOP.md` §15 Wave 0 for current status). +**Status:** Accepted and executed (Wave 0 complete — CEF source/CI/docs retirement shipped in +companion PR #407; see `docs/native/ROADMAP-QT-GPUI-DESKTOP.md` §15 Wave 0). **Supersedes:** [[0019-cef-desktop-runtime-strategy]], [[0020-cef-binding-choice-thin-cpp-host]]. This ADR does not add any Qt, GPUI, or Rust-Core code — it locks direction only, exactly as ADR-0019 and ADR-0020 did for CEF. Full strategy, waves, and gates: diff --git a/docs/architecture/native-readiness.md b/docs/architecture/native-readiness.md index 81c4a35b..3075e2d3 100644 --- a/docs/architecture/native-readiness.md +++ b/docs/architecture/native-readiness.md @@ -14,7 +14,7 @@ Owner roles below use `docs/cef/OWNERSHIP.yaml`'s existing taxonomy (`desktop-ar |---|---|---|---| | Domain logic renderer-neutral | DEBT | desktop-architecture | Product logic (thunks, business rules) still lives in Redux/React. No extraction attempted yet — Wave 5+ scope. | | Critical behavior headless-testable | DEBT | desktop-architecture | Some services (`fsCore.ts`, thunks) are already Vitest-testable without a DOM; most business logic is still coupled to React hooks/components — Wave 5+ scope. | -| Canonical data outside UI state | PASS (partial) | — | `docs/native/UI-DOMAIN-STATE-CLASSIFICATION.md` (this wave; relocated from `docs/cef/` at Wave 0 CEF retirement, content unchanged) confirms domain data (`project`, `sceneComments`, `progressTracker`, etc.) is already separated from Presentation/UI slices — it just isn't core-owned yet (see "Domain logic renderer-neutral" above). | +| Canonical data outside UI state | PASS (partial) | — | `docs/native/UI-DOMAIN-STATE-CLASSIFICATION.md` (this wave; relocated from `docs/cef/` at Wave 0 CEF retirement — the classification table itself is unchanged, its surrounding framing/wave references were updated for the Qt/GPUI roadmap) confirms domain data (`project`, `sceneComments`, `progressTracker`, etc.) is already separated from Presentation/UI slices — it just isn't core-owned yet (see "Domain logic renderer-neutral" above). | | Browser APIs adapter-contained | DEBT | desktop-architecture | Out of Wave 1 scope — no browser-API (localStorage/matchMedia/service-worker) audit performed this wave; not yet scheduled to a specific wave. | | Platform APIs adapter-contained | DEBT — in progress | desktop-architecture (Wave 1 PR B) | `packages/desktop-contracts`'s `DesktopPlatform` contract and both reference adapters exist (this PR), but the 14 files identified in `docs/cef/TAURI-COUPLING-INVENTORY.md` still import `@tauri-apps/*` directly. Re-score to PASS once PR B (`services/desktopPlatform.ts` + migration) merges and `pnpm run guardrail:desktop-imports` is green. | | Stable semantic commands/events | DEBT | desktop-architecture | No semantic command layer exists yet (roadmap §83, explicitly deferred: "do not force a full command-system rewrite before CEF needs it"); not yet scheduled to a specific wave. | diff --git a/docs/historical/cef/README.md b/docs/historical/cef/README.md index 549da6f9..26ac1e7b 100644 --- a/docs/historical/cef/README.md +++ b/docs/historical/cef/README.md @@ -7,10 +7,8 @@ supersedes [ADR-0019](../../adr/0019-cef-desktop-runtime-strategy.md) (CEF as ne [ADR-0020](../../adr/0020-cef-binding-choice-thin-cpp-host.md) (thin C++ host + Rust core binding choice). -**Status of this document:** ADR-0021 and the strategic-reset decision merged as Wave 0 PR A (PR -#406). The actual removal described below is staged in the companion Wave 0 PR B (this PR), open -and pending merge as of this writing. Until PR B merges, the paths named below still exist on -`main`. +**Status of this document:** Wave 0 is complete. ADR-0021 and the strategic-reset decision merged as +PR A (`#406`); the removal described below merged as PR B (`#407`). ## Why @@ -20,7 +18,7 @@ surfaced a structural blocker tracked as **R-19 / Issue #405**: under Linux's de without weakening the sandbox. That trade was not acceptable. ADR-0021 has the full decision record, including the alternatives considered and why each was rejected. -## What is removed (Wave 0 PR B, staged in this PR pending merge) +## What was removed (Wave 0 PR B, #407) `apps/desktop-cef/` (the C++ host + FFI-boundary Rust scaffold), `scripts/cef/` (SDK-fetch/build/ proof tooling), `.github/workflows/cef-learning-harness.yml` (advisory-only CI, never part of the diff --git a/docs/native/ROADMAP-QT-GPUI-DESKTOP.md b/docs/native/ROADMAP-QT-GPUI-DESKTOP.md index 8b964c8b..fa040d2f 100644 --- a/docs/native/ROADMAP-QT-GPUI-DESKTOP.md +++ b/docs/native/ROADMAP-QT-GPUI-DESKTOP.md @@ -1,8 +1,8 @@ # WorldScript Studio — Qt + GPUI Multi-Renderer Desktop Roadmap & Realization Concept -**Status:** Adopted as the binding strategic architecture via ADR-0021 (merged, PR #406). Adoption of -the *decision* is distinct from *execution*: Wave 0's actual CEF source/CI retirement (PR B) is open -and pending merge as of this writing — see §15 below for the current per-wave status. +**Status:** Adopted as the binding strategic architecture via ADR-0021 (merged, PR #406). Wave 0 +(both the decision and its CEF source/CI retirement execution) is complete — see §15 below for the +full per-wave status. **Repository:** `qnbs/WorldScript-Studio` **Roadmap generation date:** 2026-08-20 **Supersedes:** the CEF-first desktop migration strategy and all CEF-dependent execution sequencing @@ -921,20 +921,20 @@ docs/native/ ## G0 — CEF Exit / Strategy Reset -**Status: IN PROGRESS** — met only once PR B merges. See the Wave 0 status note above. +**Status: COMPLETE.** PR A (#406) and PR B (#407) both merged. Required: ```text -[x] new ADR (ADR-0021) supersedes CEF production strategy — merged (PR A, PR #406) +[x] new ADR (ADR-0021) supersedes CEF production strategy — merged (PR A, #406) [x] CEF PR/issue reconciled: PR #404 closed unmerged, Issue #405 closed as superseded — done -[~] no useful renderer-neutral work lost — DesktopPlatform boundary unaffected (already true, - independent of this reset); UI-DOMAIN-STATE-CLASSIFICATION.md relocation staged in PR B, pending merge -[~] CEF-only CI removed (cef-learning-harness.yml, advisory-only) — staged in PR B, pending merge -[~] CEF-only dependencies no longer required by product build (apps/desktop-cef/, scripts/cef/ - removal staged in PR B) — pending merge +[x] no useful renderer-neutral work lost — DesktopPlatform boundary unaffected (already true, + independent of this reset); UI-DOMAIN-STATE-CLASSIFICATION.md relocated (PR B, #407) +[x] CEF-only CI removed (cef-learning-harness.yml, advisory-only) — removed (PR B, #407) +[x] CEF-only dependencies no longer required by product build (apps/desktop-cef/, scripts/cef/ + removed, PR B #407) [x] Tauri remains functional during transition — true throughout, unaffected by either PR -[x] Qt/GPUI roadmap adopted (this document) — merged (PR A, PR #406) +[x] Qt/GPUI roadmap adopted (this document) — merged (PR A, #406) ``` ## G1 — Core Native-Ready @@ -1026,15 +1026,13 @@ discipline while replacing CEF-specific work with Qt/GPUI-native milestones. ## Wave 0 — Strategy reset and CEF retirement reconciliation -**Status: IN PROGRESS — PR B open, pending merge.** Wave 0 executed as two PRs. **PR A** (this -document, ADR-0021, ADR-0019/0020 supersession, `ROADMAP.md`/`README.md` pointer updates) -established the strategic reset and merged as PR #406. Issue #405 and companion PR #404 are closed -(#404 unmerged, superseded; #405 as no-longer-applicable, not "fixed") — both real GitHub state as -of this writing, independent of PR B's merge status. **PR B** (removal of `apps/desktop-cef/`, -`scripts/cef/`, `.github/workflows/cef-learning-harness.yml`, and 14 of 15 `docs/cef/` files; -relocation of `docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md` to `docs/native/`; this doc-sync itself) -is open, pending CI and merge. This wave is COMPLETE only once PR B actually merges — this status -line will be updated to COMPLETE/CI-PROVEN in that same PR once CI is green, not before. +**Status: COMPLETE — CI-PROVEN.** Wave 0 executed as two PRs. **PR A** (#406: ADR-0021, +ADR-0019/0020 supersession, `ROADMAP.md`/`README.md` pointer updates) established the strategic +reset. Issue #405 and companion PR #404 are closed (#404 unmerged, superseded; #405 as +no-longer-applicable, not "fixed"). **PR B** (#407: removal of `apps/desktop-cef/`, `scripts/cef/`, +`.github/workflows/cef-learning-harness.yml`, and 14 of 15 `docs/cef/` files; relocation of +`docs/cef/UI-DOMAIN-STATE-CLASSIFICATION.md` to `docs/native/`; this doc-sync) performed the actual +CEF source/CI/docs retirement. Both PRs merged, `main` CI green. **Goal:** change direction without losing valid work. @@ -2212,20 +2210,19 @@ Create/supersede ADRs for: # 37. Immediate transition plan from the current repository state -**Status: IN PROGRESS.** Items 1–5 and 8–9 below are done; item 4's PR/issue closure is done -independent of PR B's own merge status; items 6–7 are staged in PR B, pending its merge. +**Status: COMPLETE.** All items below are done. The first implementation sequence after adopting this roadmap is: ```text 1. Freeze new CEF feature work. [DONE] 2. Inventory current CEF PR(s), especially any Draft work. [DONE] -3. Extract only renderer-neutral improvements. [DONE — relocation staged in PR B] +3. Extract only renderer-neutral improvements. [DONE — PR B, #407] 4. Close/supersede CEF-specific PRs with a clear historical note. [DONE — #404 closed unmerged, #405 closed as superseded] 5. Adopt new ADR + this roadmap. [DONE — merged, PR #406] 6. Rename/generalize CEF-only governance docs where their content remains - useful. [STAGED IN PR B — UI-DOMAIN-STATE-CLASSIFICATION.md relocated to docs/native/, pending merge] -7. Re-run main CI. [PENDING — PR B CI in progress] + useful. [DONE — UI-DOMAIN-STATE-CLASSIFICATION.md relocated to docs/native/, PR #407] +7. Re-run main CI. [DONE — green] 8. Re-audit DesktopPlatform/Tauri import boundary. [ALREADY TRUE — Wave 1 complete] 9. Start Wave 1. [ALREADY COMPLETE before Wave 0] 10. Do not begin Qt UI until Wave 1/2 prerequisites are sufficiently proven. [Wave 2 not yet started] @@ -2661,7 +2658,7 @@ These are sequencing goals, not date guarantees. ## First horizon - strategy reset; **[DONE — PR #406]** -- CEF reconciliation; **[PR B open, pending merge]** +- CEF reconciliation; **[DONE — PR #407]** - DesktopPlatform boundary; **[ALREADY DONE, pre-Wave-0]** - Core headless extraction; - R-15/storage architecture; diff --git a/packages/desktop-contracts/src/types.ts b/packages/desktop-contracts/src/types.ts index f6670876..d8fa7de9 100644 --- a/packages/desktop-contracts/src/types.ts +++ b/packages/desktop-contracts/src/types.ts @@ -2,8 +2,8 @@ /** * DesktopPlatform contract (docs/native/ROADMAP-QT-GPUI-DESKTOP.md). * Renderer-neutral typed interface all desktop capability access should route through. - * Wave 1 scope: relocate today's exact Tauri behavior behind this interface (TauriDesktopPlatform) - * plus a safe no-op web/PWA implementation (WebDesktopPlatform). No Qt or GPUI adapter exists yet — + * Wave 1 scope: relocate today's exact Tauri behavior behind this interface (`tauriDesktopPlatform`) + * plus a safe no-op web/PWA implementation (`webDesktopPlatform`). No Qt or GPUI adapter exists yet — * those are admitted later per the roadmap's gates. HTTP (`services/ai/fetchAdapter.ts`, * `services/localServerHttp.ts`) is intentionally NOT a facet here — this interface has no HTTP * facet by design; those files keep importing `@tauri-apps/plugin-http` directly, a permanent, diff --git a/scripts/check-tauri-import-boundary.mjs b/scripts/check-tauri-import-boundary.mjs index 3eef93f0..94f4f418 100644 --- a/scripts/check-tauri-import-boundary.mjs +++ b/scripts/check-tauri-import-boundary.mjs @@ -7,8 +7,10 @@ * point of the DesktopPlatform boundary: a future Qt or GPUI adapter slots in without touching any * consumer again. This is a strict zero-tolerance gate (unlike the suppression ratchet in * check-suppressions.mjs): any real `@tauri-apps/*` import specifier found in application source - * outside the explicitly approved locations below fails the build. Comment-only mentions (JSDoc, + * outside the explicitly approved locations below fails the build. Whole-line comments (JSDoc, * `//` notes) are not flagged — only actual `import ... from`, `import(...)`, `require(...)`. + * Note: an inline C-style block comment placed mid-line on real code is NOT masked and would be + * flagged — `commentLineMask` only recognizes lines whose trimmed content starts a comment. * * Run: node scripts/check-tauri-import-boundary.mjs */ From d3465fe257c0b173725df7a2fae62f35321c2bfd Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Thu, 20 Aug 2026 04:45:57 +0200 Subject: [PATCH 6/6] docs: clarify Wave-0-complete status doesn't contradict item 10's guardrail Item 10 ("do not begin Qt UI until Wave 1/2 prerequisites are proven") is a standing constraint for future waves, not a Wave 0 task - its "[Wave 2 not yet started]" bracket was reading as contradicting the blanket "all items below are done" line above it. Scoped the status line to items 1-9 and explained item 10's role explicitly. Co-Authored-By: Claude Sonnet 5 --- docs/native/ROADMAP-QT-GPUI-DESKTOP.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/native/ROADMAP-QT-GPUI-DESKTOP.md b/docs/native/ROADMAP-QT-GPUI-DESKTOP.md index fa040d2f..a8474630 100644 --- a/docs/native/ROADMAP-QT-GPUI-DESKTOP.md +++ b/docs/native/ROADMAP-QT-GPUI-DESKTOP.md @@ -2210,7 +2210,9 @@ Create/supersede ADRs for: # 37. Immediate transition plan from the current repository state -**Status: COMPLETE.** All items below are done. +**Status: COMPLETE for the Wave 0 transition steps (items 1–9).** Item 10 is a standing guardrail +for later waves, not a Wave 0 task — it stays in effect until Wave 2 actually proves out; its +bracket note is not a sign of incomplete Wave-0 work. The first implementation sequence after adopting this roadmap is: