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
86 changes: 86 additions & 0 deletions .github/workflows/cef-learning-harness.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# ============================================================
# 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).
#
# Scope of this first increment: fetch + verify the pinned CEF SDK, report a
# clean-machine Linux runtime-dependency inventory (before any apt-get — a genuinely
# new data point vs. the spike's already-configured dev machine), and print real
# version diagnostics parsed from the fetched SDK itself. The actual CEF host
# build/run/shutdown proof (§4.11.3's "isolated learning harness") is deliberately
# NOT part of this increment — see ADR-0020's Consequences for why that needs its
# own CMake/Cargo bootstrap PR.
#
# 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.
# ============================================================

name: 🧪 CEF Learning Harness

on:
pull_request:
branches: [main]
paths:
- 'scripts/cef/**'
- 'docs/cef/**'
- '.github/workflows/cef-learning-harness.yml'
push:
branches: [main]
paths:
- 'scripts/cef/**'
- 'docs/cef/**'
- '.github/workflows/cef-learning-harness.yml'
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:
harness:
name: 🧪 Fetch CEF SDK + dependency inventory + version diagnostics
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v6
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

- 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"
Comment thread
qodo-code-review[bot] marked this conversation as resolved.

- name: Print CEF version diagnostics
run: node scripts/cef/print-cef-version-diagnostics.mjs "$CEF_DIR"

- name: Summary
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 "- Scope: fetch/verify + clean-machine dependency inventory + version diagnostics only." >> "$GITHUB_STEP_SUMMARY"
echo "- Not yet in scope: CEF host build/run/shutdown proof (see ADR-0020 Consequences)." >> "$GITHUB_STEP_SUMMARY"
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ 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
Expand Down
6 changes: 4 additions & 2 deletions docs/architecture/native-readiness.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The Wave 1 PR A snapshot left three DEBT rows marked "not yet scheduled to a spe

Re-score "Platform APIs adapter-contained" again once `services/logger.ts`'s JSONL sink migrates (Wave 5/7, see above). Re-score the three newly-scheduled rows at each wave's exit (Wave 4 typed-IPC-v1, Wave 5 domain extraction).

## Snapshot: Wave 2 binding spike (ADR-0020, `docs/cef/CEF-BINDING-DECISION-SCORECARD.md`)
## Snapshot: Wave 2 progress (ADR-0020 spike + CI bootstrap, `docs/cef/CEF-BINDING-DECISION-SCORECARD.md`)

Wave 2's first deliverable — the CEF binding/C++ decision — is now backed by a real hands-on spike, not a desk pick. Full scorecard: `docs/cef/CEF-BINDING-DECISION-SCORECARD.md`. This snapshot only tracks what changes for *this* readiness table; the roadmap's own Wave 2 exit criteria (Early Accessibility Gate, "no production-critical lifecycle assumption remains undocumented") are broader than this single row and are **not** claimed as met here.

Expand All @@ -61,5 +61,7 @@ Wave 2's first deliverable — the CEF binding/C++ decision — is now backed by
| CEF lifecycle assumptions documented | DEBT — partial | cef-runtime, Wave 2 (in progress) | `docs/cef/knowledge/subprocess-and-shutdown.md` now has one real (non-speculative) finding — SIGTERM shutdown is not instantaneous, don't false-positive an immediate post-signal check — but it is explicitly flagged as not yet backed by a committed/CI-run test, per the doc's own evidence-link discipline. Exit condition: a real test + CI job, not just a spike observation. |
| Early Accessibility Gate | Not yet attempted | cef-runtime, Wave 2 | Out of this spike's scope entirely — no accessibility tree/API integration was touched. |
| Sandbox posture | Not yet attempted | desktop-security, Wave 2/3 (roadmap §12) | Spike explicitly ran with `no_sandbox=true`; zero evidence either way on this row. |
| 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. Does not build or run the CEF host itself yet (see ADR-0020 Consequences for that follow-up). |
| 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. |

**Overall for this snapshot**: 1 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**: 2 PASS, 3 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.
11 changes: 7 additions & 4 deletions docs/cef/OWNERSHIP.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ documents:
worldscript: "v1.27.1"
cef: "151.3.18+gbeff58d+chromium-151.0.7922.138"
review_days: 90
related_ci: []
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."

Expand Down Expand Up @@ -146,9 +147,10 @@ documents:
worldscript: "v1.27.1"
cef: "151.3.18+gbeff58d+chromium-151.0.7922.138"
review_days: 90
related_ci: []
related_ci:
- cef-learning-harness
driftCheckTool: "planned — not implemented, see Wave 1"
note: "Preliminary spike evidence only (ADR-0020) — one machine, one GPU, X11 only; not a compatibility contract yet."
note: "Preliminary spike evidence only (ADR-0020) — one machine, one GPU, X11 only; not a compatibility contract yet. The package-presence portion of the dependency inventory now also has a real CI-run data point via the cef-learning-harness job (stock ubuntu-latest, pre-apt-get)."

- path: docs/cef/knowledge/debugging-and-crash-playbook.md
tier: A
Expand Down Expand Up @@ -189,7 +191,8 @@ documents:
worldscript: "v1.27.1"
cef: "151.3.18+gbeff58d+chromium-151.0.7922.138"
review_days: 180
related_ci: []
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."

Expand Down
4 changes: 2 additions & 2 deletions docs/cef/knowledge/linux-runtime-notes.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# CEF Linux Runtime Notes

**Status:** Preliminary spike evidence only (2026-08-18, ADR-0020) — one dev machine, 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).
**Status:** Preliminary spike evidence only (2026-08-18, ADR-0020) — one dev machine, 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). The package-presence portion of the dependency inventory now also has a real, CI-run second data point (stock `ubuntu-latest`) via the `🧪 CEF Learning Harness` workflow — see the "Remaining outline" section below for exactly what that job does and does not cover.
**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).
Expand All @@ -26,4 +26,4 @@ Do not hardcode a glibc/distro minimum here until packaged builds have proven it
- `libcef.so` and resource layout for our actual packaging (not yet designed — this spike used CEF's own unpackaged build-output layout)
- 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) — this spike ran on an already-configured dev machine, not a clean install
- 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. Still open: only checks `dpkg` package presence, not `ldd` against the actual shipped `.so` files; only one distro/runner image; still not a packaged-installer dependency declaration
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
"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",
Expand Down
10 changes: 10 additions & 0 deletions scripts/cef/cef-version.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"$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"
}
23 changes: 23 additions & 0 deletions scripts/cef/cefPaths.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/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 };
}
81 changes: 81 additions & 0 deletions scripts/cef/check-linux-runtime-deps.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/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',
];
Comment thread
qnbs marked this conversation as resolved.

/** @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);
}
Loading
Loading