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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions apps/desktop-cef/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,13 @@ 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()
15 changes: 15 additions & 0 deletions apps/desktop-cef/resources/crash_reporter.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 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
6 changes: 6 additions & 0 deletions apps/desktop-cef/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#include <string>

#include "include/cef_app.h"
#include "include/cef_crash_util.h"

#include "shutdown_signal.h"
#include "worldscript_app.h"
Expand Down Expand Up @@ -45,6 +46,11 @@ int main(int argc, char* argv[]) {
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);

CefRunMessageLoop();
CefShutdown();

Expand Down
26 changes: 26 additions & 0 deletions apps/desktop-cef/src/worldscript_handler.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "worldscript_handler.h"

#include <cstdio>
#include <unordered_map>

#include "include/cef_app.h"
#include "include/cef_task.h"
Expand All @@ -15,6 +16,20 @@ 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<cef_termination_status_t, const char*> 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:
Expand Down Expand Up @@ -63,6 +78,17 @@ void WorldScriptHandler::PollShutdownFlag() {
}
}

void WorldScriptHandler::OnRenderProcessTerminated(CefRefPtr<CefBrowser> 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);
Comment thread
qnbs marked this conversation as resolved.
fflush(stdout);
}

bool WorldScriptHandler::DoClose(CefRefPtr<CefBrowser> 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.
Expand Down
12 changes: 10 additions & 2 deletions apps/desktop-cef/src/worldscript_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,30 @@

#include "include/cef_client.h"

// QNBS-v3: browser-process lifecycle/display callbacks only (ADR-0020 scorecard) — renderer-process-specific handlers (CefRenderProcessHandler) are explicitly out of scope for this proof.
// 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 CefDisplayHandler,
public CefRequestHandler {
public:
WorldScriptHandler();

CefRefPtr<CefLifeSpanHandler> GetLifeSpanHandler() override { return this; }
CefRefPtr<CefDisplayHandler> GetDisplayHandler() override { return this; }
CefRefPtr<CefRequestHandler> GetRequestHandler() override { return this; }

void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) override;

void OnAfterCreated(CefRefPtr<CefBrowser> browser) override;
bool DoClose(CefRefPtr<CefBrowser> browser) override;
void OnBeforeClose(CefRefPtr<CefBrowser> 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<CefBrowser> 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();

Expand Down
3 changes: 2 additions & 1 deletion docs/architecture/native-readiness.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,10 @@ Wave 2's first deliverable — the CEF binding/C++ decision — is now backed by
| 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 | Not yet attempted (real blocker found) | cef-runtime, Wave 2 | PR #391 attempted `CefAccessibilityHandler` — does not compile against CEF 151.3.18 (`CefClient::GetAccessibilityHandler()` doesn't exist in this version). Reverted rather than left half-working, after a fallback attempt (enable-only, no observability) regressed the previously-reliable FFI/rendering proofs. Real CEF-151 API research needed before the next attempt — 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. |
| Crash reporting / renderer-crash resilience | **PASS** — crash-reporting half only | 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. Symbolization (decoding the dump into a stack trace via `dump_syms`/`minidump_stackwalk`) needs a full Chromium source checkout and was not attempted — 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. Still narrow: `dpkg` package-presence only (not `ldd` against the actual shipped `.so` files), one distro/runner image. |
| 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**: 5 PASS, 2 explicit DEBT-in-progress rows (each with a concrete exit condition, not open-ended), 2 not-yet-attempted rows correctly left blank rather than assumed. No row is marked PASS without the evidence cited above.
**Overall for this snapshot**: 6 PASS (one — crash reporting — explicitly PASS for its reporting half only, not symbolization), 2 explicit DEBT-in-progress rows (each with a concrete exit condition, not open-ended), 2 not-yet-attempted rows correctly left blank rather than assumed. No row is marked PASS without the evidence cited above.
18 changes: 9 additions & 9 deletions docs/cef/CEF-RUST-COMPETENCY-MATRIX.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 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), 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.
**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), 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.

Expand All @@ -12,10 +12,10 @@ cef_competency:
binding_model_documented: true # docs/adr/0020-cef-binding-choice-thin-cpp-host.md
lifetime_model_reviewed: false
repeated_shutdown_ci: true # scripts/cef/run-launch-cycle-proof.mjs, cef-learning-harness CI job (PR #388)
renderer_crash_ci: false
renderer_crash_ci: true # chrome://crash + OnRenderProcessTerminated + browser-process survival, cef-learning-harness CI job (PR #392)
sandbox_smoke: false
accessibility_smoke: false
crash_symbolization_smoke: false
crash_symbolization_smoke: false # crash REPORTING is proven (PR #392) — this field is specifically about decoding a dump (dump_syms/minidump_stackwalk), which needs a full Chromium source checkout and was not attempted
```

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`.
Expand All @@ -28,7 +28,7 @@ CI validation of this block ("fail CI when a required item for the active progra
| 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`). No dedicated review doc yet (`docs/cef/knowledge/threading-and-lifetimes.md` still skeleton); IO thread and async-cancellation patterns 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. No upgrade procedure written yet (`docs/cef/knowledge/binding-upgrade-playbook.md` still skeleton); 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 — high-DPI and IME/a11y 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` (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, symbol handling, version-update automation, and sandbox verification all remain not started. |
| 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` (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 (decoding a dump into a stack trace) needs `dump_syms`/`minidump_stackwalk` built from a full Chromium source checkout — out of reach of this project's minimal-CEF-SDK CI setup, not attempted. Version-update automation and sandbox verification remain not started. |

## Appendix A.1 checklist (live)

Expand All @@ -41,9 +41,9 @@ CI validation of this block ("fail CI when a required item for the active progra
[ ] 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
[ ] Renderer crash observation green
[x] Renderer crash observation green — PR #392, chrome://crash + OnRenderProcessTerminated (TS_PROCESS_CRASHED), browser process survived, cef-learning-harness CI job
[ ] Accessibility smoke green (attempted, real blocker — see cef-architecture-primer.md's "Accessibility API" section)
[ ] Crash-reporting/symbolization smoke green
[ ] Crash-reporting/symbolization smoke green (crash-reporting half proven — PR #392, real Crashpad dump produced in CI; symbolization/decoding the dump not attempted, needs a full Chromium source checkout — see cef-architecture-primer.md)
[ ] Linux dependency inventory complete (inventoried, not yet proven sufficient — see native-readiness.md)
[ ] X11/Wayland initial smoke complete (X11 only; Wayland zero evidence)
[ ] Upgrade playbook written
Expand All @@ -59,15 +59,15 @@ CI validation of this block ("fail CI when a required item for the active progra
[x] unsafe/FFI boundary identified — apps/desktop-cef/rust-core/, proven in CI (PR #388)
[ ] threading/lifetime map reviewed (no dedicated doc yet — see domains table)
[x] clean repeated startup/shutdown proven — PR #388, 3/3 cycles, cef-learning-harness CI job
[ ] renderer termination observed and handled (not proven — PR #388 exercised normal shutdown only; no test deliberately terminates/crashes a renderer, per Qodo review finding on PR #389)
[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
[ ] sandbox development plan validated
[ ] Linux runtime dependencies inventoried (inventoried but not yet proven sufficient — see native-readiness.md)
[ ] at least one accessibility smoke test performed (attempted, real blocker — see cef-architecture-primer.md's "Accessibility API" section)
[ ] at least one crash-reporting/symbolization path proven
[x] at least one crash-reporting/symbolization path proven — PR #392: crash-reporting path proven end-to-end (real Crashpad dump produced in CI); full symbolization (decoding the dump) is a separate, unattempted step needing a full Chromium source checkout
[ ] upgrade playbook exists
```

This gate is **not** satisfied yet — 4 of 12 items checked, several with explicit caveats above. `WS-CEF-IPC` (Wave 4) remains blocked.
This gate is **not** satisfied yet — 6 of 12 items checked, several with explicit caveats above. `WS-CEF-IPC` (Wave 4) remains blocked.

## What this snapshot (Wave 2, 2026-08-18/19) does NOT claim

Expand Down
Loading
Loading