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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .github/workflows/cef-learning-harness.yml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ jobs:
- 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: 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
Expand Down Expand Up @@ -227,5 +234,9 @@ jobs:
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"
6 changes: 3 additions & 3 deletions docs/architecture/native-readiness.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ Wave 2's first deliverable — the CEF binding/C++ decision — is now backed by
| Wayland display-server smoke | **PASS** — single-runner smoke only | cef-runtime, Wave 2 | PR #393: `worldscript_host` (the exact binary already proven under X11) also renders the real production bundle under a headless Weston Wayland compositor (`--ozone-platform=wayland`), same FFI-boundary + exact-title checks as the X11 harness, CI-run and non-blocking (roadmap §44.2). Grounded in real evidence before attempting: Chromium's own upstream GN default compiles Wayland Ozone support into every standard Linux build, and CEF's `tools/gn_args.py` has no override disabling it. Does **not** satisfy roadmap §44.2/§44.5's real-hardware/compositor matrix (NVIDIA/AMD/Intel × KDE/GNOME) — one virtual CI runner, one compositor implementation (Weston headless), no real GPU. |
| CEF lifecycle assumptions documented | **PASS** | cef-runtime | `docs/cef/knowledge/subprocess-and-shutdown.md`'s core Wave 2 claim (SIGTERM → graceful `TryCloseBrowser`/`OnBeforeClose`/`CefQuitMessageLoop`/`CefShutdown`, repeated clean start/close cycles) now has a real linked chain: test (`scripts/cef/run-launch-cycle-proof.mjs`) → CI job (`🧪 CEF Learning Harness`) → doc, exactly what §61.1.4 requires. Save-coordinator/window-state persistence remain explicitly Wave 5+ scope (not a Wave 2 gap); Windows/macOS and a real packaged layout remain open, tracked in the doc's own "Outline" section. |
| Early Accessibility Gate | **PASS** — state enablement only, tree observability open | cef-runtime, Wave 2 | PR #391 found the real root cause: `GetAccessibilityHandler()` is declared on `CefRenderHandler` (OSR-only, "when window rendering is disabled"), not `CefClient` — never reachable from this windowed host regardless of what was inherited. PR #397: `CefBrowserHost::SetAccessibilityState(STATE_ENABLED)` alone is windowed-mode-correct per its own doc comment; CI-proven in every one of 3 repeated cycles with zero regression to the FFI/rendering/crash-reporting/Wayland proofs. Does **not** yet prove the platform accessibility tree is observable — that needs OS-level AT-SPI introspection on Linux, separate unattempted follow-up — see `docs/cef/knowledge/cef-architecture-primer.md`. |
| Sandbox posture | Not yet attempted | desktop-security, Wave 2/3 (roadmap §12) | Every run so far used `no_sandbox=true`; zero evidence either way on this row. |
| Sandbox posture | Not yet attempted | desktop-security, Wave 2/3 (roadmap §12) | Every run so far used `no_sandbox=true`; zero evidence either way on this row. PR #402: diagnostic-only feasibility inventory confirmed `unshare --user --pid --fork` succeeds on the CI runner (functional test, not just a sysctl read) — unprivileged user-namespace sandboxing appears reachable, but no sandbox behavior has been attempted yet. **Acceptance bar for the follow-up enable attempt** (not this row's current status): not just "CEF starts without `no_sandbox=true`" — must show browser/renderer/utility-GPU processes actually running under the sandbox (Chromium's own recommendation: check real per-process sandbox status, e.g. `chrome://sandbox` or an equivalent CLI-observable signal — Linux combines namespace isolation *and* seccomp-BPF, and both matter), zero regression to the existing lifecycle/crash/accessibility/Wayland proofs, and a reproducible sandbox-status proof in CI. Explicitly disallowed: "fixing" a launch failure by silently trading `no_sandbox=true` for a narrower blanket disable like `--disable-setuid-sandbox` while still claiming the row as proven — that would misrepresent what's actually protected. **CI-sandbox-proof and production-packaging-sandbox-proof are two separate gates** — a GitHub Actions runner can prove "our CEF config can run sandboxed," not "our eventual .deb/AppImage/installer distribution correctly installs the helper/permissions/runtime layout on every target distro." The latter is real packaging scope, not to be pulled into Wave 2. |
| Crash reporting / renderer-crash resilience / symbolization | **PASS** — Chromium-internal frames excepted | cef-runtime | PR #392: `crash_reporter.cfg` + `CefCrashReportingEnabled()` verified true, `chrome://crash` deliberately crashes the renderer, `CefRequestHandler::OnRenderProcessTerminated` fires (`TS_PROCESS_CRASHED`), the browser process/message loop survive, and a real Crashpad `.dmp` file — the harness's actual assertion, alongside Crashpad's own `.meta`/`settings.dat` housekeeping files (observed, not independently asserted) — is produced under an overridden `BREAKPAD_DUMP_LOCATION`; all CI-run, not a doc claim. PR #400: symbolization also proven — the initial "needs a full Chromium checkout" assumption was wrong for our own code's frames; `dump_syms`/`minidump-stackwalk` (standalone Rust tools, no Chromium checkout) resolved a self-induced browser-process crash (`--debug-crash-self`) end-to-end back to the crashing function's name. Chromium/CEF-internal frames remain genuinely unsymbolized — no distribution type ships debug symbols, verified against CEF's own build index — see `docs/cef/knowledge/cef-architecture-primer.md`. |
| CEF SDK fetch/verify + version diagnostics automated | **PASS** | cef-runtime | `🧪 CEF Learning Harness` CI job (`.github/workflows/cef-learning-harness.yml`) fetches the pinned CEF SDK, verifies its checksum, and parses real version macros out of the extracted `include/cef_version.h` — a genuine CI-run check, not a doc claim. |
| Linux dependency inventory — clean-machine data point | DEBT — partial | cef-runtime | Same CI job runs the package-presence check against a stock `ubuntu-latest` runner before any `apt-get`, adding a real second data point beyond the spike's one already-configured dev machine. PR #395 added the specific check this row previously flagged as missing: `scripts/cef/check-linux-runtime-linkage.mjs` runs `ldd` against the real, already-built `worldscript_host` and `libcef.so` — both fully resolved on the runner, zero unresolved dependencies. Still narrow: one distro/runner image only, no packaged-installer dependency declaration. |
| Linux dependency inventory (Wave 2 scope) — clean-machine data point | **PASS** — single distro/runner only, packaged-installer declaration excepted | cef-runtime | Same CI job runs the package-presence check against a stock `ubuntu-latest` runner before any `apt-get`, adding a real second data point beyond the spike's one already-configured dev machine. PR #395 added the specific check this row previously flagged as missing: `scripts/cef/check-linux-runtime-linkage.mjs` runs `ldd` against the real, already-built `worldscript_host` and `libcef.so` — both fully resolved on the runner, zero unresolved dependencies. Split 2026-08-19 (`CEF-RUST-COMPETENCY-MATRIX.md`'s own gate item split the same way): this row previously stayed DEBT pending "a packaged-installer dependency declaration," which is a different, later packaging-wave goal (matching this table's own existing convention for Wayland below — proven on what Wave 2 actually needs, one CI runner, not blocked pending a broader matrix). One distro/runner image only; a real multi-distro packaged-installer compatibility proof is separate, later scope, tracked as its own open item, not this row. |
| CEF host build + repeated launch/close cycle proof, in CI | **PASS** | cef-runtime | PR #388: `apps/desktop-cef/`'s `worldscript_host` (real, repo-committed C++/Rust source, not spike code) builds against the fetched CEF SDK and runs 3 independently-verified clean start/close cycles under Xvfb in CI — the roadmap's literal "isolated learning harness" / "safe repeated startup/shutdown" deliverables (§3142), not just the fetch/diagnostics increment. |
| Rust FFI boundary proven inside the real host | **PASS** | cef-runtime, rust-core | `worldscript_rust_ping()` (rust-core, linked via Corrosion) is called from `OnAfterCreated` on every cycle and its exact sentinel value observed in CI output — stronger than the ADR-0020 spike's decoupled isolation test, since this proves the boundary works inside the actual multi-process CEF host, not a standalone C++ program. |

**Overall for this snapshot**: 8 PASS (crash reporting/symbolization explicitly PASS except for Chromium-internal frames, which no CEF distribution ships debug symbols for; Wayland explicitly PASS for a single-runner smoke only, not the real-hardware/compositor matrix; Early Accessibility Gate explicitly PASS for state enablement only, not tree observability), 2 explicit `DEBT — partial` rows (each with a concrete exit condition, not open-ended), 1 row marked `Not yet attempted` (Sandbox posture) rather than assumed. No row is marked PASS without the evidence cited above.
**Overall for this snapshot**: 9 PASS (crash reporting/symbolization explicitly PASS except for Chromium-internal frames, which no CEF distribution ships debug symbols for; Wayland explicitly PASS for a single-runner smoke only, not the real-hardware/compositor matrix; Early Accessibility Gate explicitly PASS for state enablement only, not tree observability; Linux dependency inventory explicitly PASS for Wave 2 scope only, split 2026-08-19 from the separate packaged-installer-declaration goal — see that row), 1 explicit `DEBT — partial` row (with a concrete exit condition, not open-ended), 1 row marked `Not yet attempted` (Sandbox posture, though PR #402 validated real feasibility — see that row) rather than assumed. No row is marked PASS without the evidence cited above.
6 changes: 4 additions & 2 deletions docs/cef/CEF-RISK-REGISTER.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ Every P0/P1 risk below must have an owner, a test, and an exit condition before
| 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 | OPEN | *unassigned* | Minimal wrapper surface, competency harness, dual-review (§4.11.4) | Learning harness (§61.1) green in CI across repeated start/stop cycles |
| 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 | OPEN | *unassigned* | Early integration spike (§23.1) + deep certification wave (Wave 16) | Wave 16 exit criteria pass |
| 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 |
Expand All @@ -34,3 +34,5 @@ R-15–R-18 were derived directly from the Wave 0 PR reconciliation (roadmap §6
## 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.
Loading
Loading