Codex review of Task 1.2 found four dangling references to the deleted
stub crates (pen-types/core/engine/codegen/figma + openpencil-app):
- package.json: drop the five -p <crate> args from cargo:wasm-check
- rust-release.yml: restore the build job, re-pointed at the real
openpencil-desktop crate so release-draft has artifacts to publish
- check-jian-boundaries.sh: 4 -> 3 invariants in the success message
- README.md: remove the deleted crate rows from the crate-list table
Enforces the Step 1b §1.4 widget boundary invariant: widget logic
(Widget impls + layout/paint/access_node methods) lives in
crates/openpencil-shell-core/src/widgets/; shell-web's only
widget-touching file is `widget_host.rs` and even there the only
widget-method signature allowed is the `// glue:` marked paint
dispatcher.
Forward checks (no widget logic in shell-web/src/):
- F1: `impl(<...>)?[[:space:]]+(ns::)*Widget[[:space:]]+for[[:space:]]`
anywhere under shell-web/src/. Allows generic params + arbitrary
namespace depth so `impl<T> shell_core::widgets::Widget for X` is
caught. No `// glue:` exemption — Widget impls have no place in
shell-web period.
- F2: `fn[[:space:]]+(layout|access_node)\(` anywhere under
shell-web/src/. No exemption.
- F3: `fn[[:space:]]+paint\(` under shell-web/src/, EXCEPT lines in
widget_host.rs that ALSO carry `// glue:`. Tight exemption — the
marker only blesses one specific signature, not arbitrary tagged
lines.
- F4: any line under shell-web/src/ mentioning both
`openpencil_shell_core` AND `widgets`, except widget_host.rs.
Catches direct + grouped `use` forms (e.g. `use
openpencil_shell_core::{widgets::TreeWidget};`) plus path
expressions. Multi-line braced `use` is out of scope (single-line
policy in this crate).
Reverse check (shell-core/src/widgets/ has all four impls):
- R1: For each of {tree, prop_row, dropdown, text_input}, the file
must exist AND, after stripping `//` line comments, must contain
a live `impl Widget for X`. Block comments out of scope (line
comments only in this directory).
CI integration:
- New "Verify Step 1b widget boundary (spec §1.4)" step in
.github/workflows/rust-check.yml right after the existing
"Verify Jian boundary invariants" step, gated to Linux runner
(matches the jian-boundaries pattern).
- Added `tools/check-jian-boundaries.sh` and
`tools/check-widget-boundary.sh` to the rust-check.yml push +
pull_request path filters so PRs editing only the checker still
trigger CI.
7-test regression matrix (positive + 6 negative cases):
- positive (real codebase) → PASS
- generic `impl<T> Widget for X` injected → FAIL F1
- direct `use openpencil_shell_core::widgets` outside host → FAIL F4
- `// glue:` tag on `impl Widget for X` line → FAIL F1 (exemption
doesn't save it; only `fn paint` lines are exempted)
- shell-core file replaced with `// stub` → FAIL R1
- grouped `use openpencil_shell_core::{widgets::TreeWidget};` → FAIL F4
- grouped `use openpencil_shell_core::{widgets};` → FAIL F4
- shell-core file body replaced with `// impl Widget for X { ... }` → FAIL R1
Codex iterate review: 5 rounds → GO. Round 1 BLOCK (greedy
WidgetHost match), R2 BLOCK + 3 CONCERN (calls/imports unchecked,
generic impls, broad exemption, filename-only count), R3 BLOCK +
CONCERN (grouped imports, commented-out impls), R4 2 NITs
(documentation parity), R5 GO clean.
Updates the rust-multiplatform + rust-release workflows for the
post-C-hard.2 reality where the wasm32-unknown-unknown bundle IS
runtime-loadable locally but the CI side still needs more
automation before it can publish a release artifact.
rust-multiplatform.yml:
- add `vendor/skia-safe-op/**` to push + pull_request path
filters so changes inside the fork actually trigger CI
- rename the wasm-web job → "wasm32-unknown-unknown / openpencil-
shell-web (compile guard)" to make explicit that this is the
--no-default-features --features web stub-mount baseline, not
the real render bundle
- drop the artifact upload from this job: the stub .wasm has no
skia and would mislead downstream consumers
rust-release.yml:
- delete the standalone `wasm` job for now and update the comment
to a DEFERRED block listing the 6 CI-side automation steps
still missing (brew emscripten install, EMSDK env var,
.wasm.a → .a symlink hack, wasm-bindgen + wasm-opt, browser
smoke). Re-add the job once the pipeline lands
- update the workflow header copy so it stops claiming to build
the WASM bundle alongside desktop binaries
- drop `wasm` from the release-draft `needs:` list
This is an explicit deferral, NOT a silent drop — every removed
piece is annotated with the work item it is waiting on.
Step 1b §3.2 P0.5B Run path, sub-phase C-hard CI follow-up.
Why: "Design a profile card" through MiniMax-M2.7 produced a 375×803 mobile
screen with auto-injected status bar, because the planner skill listed
"profiles" as a Type 2 single-task screen and the orchestrator's
isMobileScreen heuristic ran on width≤480 alone.
What: design-type.md + decomposition.md add Type 0 (single component:
card / badge / chip / modal) with width=400 height=0 1 subtask no chrome.
isMobileFullScreen helper extracted to orchestrator-plan-classify.ts and
required by both orchestrator.ts and orchestrator-sub-agent.ts so the
two paths can't drift on what "mobile" means (Codex review caught this
when only orchestrator.ts had the new check).
Verified with same MiniMax + same prompt: 400×320 component, 8 nodes,
firstChildRole=card, no status-bar.
Updates the rust-multiplatform + rust-release workflows for the
post-C-hard.2 reality where the wasm32-unknown-unknown bundle IS
runtime-loadable locally but the CI side still needs more
automation before it can publish a release artifact.
rust-multiplatform.yml:
- add `vendor/skia-safe-op/**` to push + pull_request path
filters so changes inside the fork actually trigger CI
- rename the wasm-web job → "wasm32-unknown-unknown / openpencil-
shell-web (compile guard)" to make explicit that this is the
--no-default-features --features web stub-mount baseline, not
the real render bundle
- drop the artifact upload from this job: the stub .wasm has no
skia and would mislead downstream consumers
rust-release.yml:
- delete the standalone `wasm` job for now and update the comment
to a DEFERRED block listing the 6 CI-side automation steps
still missing (brew emscripten install, EMSDK env var,
.wasm.a → .a symlink hack, wasm-bindgen + wasm-opt, browser
smoke). Re-add the job once the pipeline lands
- update the workflow header copy so it stops claiming to build
the WASM bundle alongside desktop binaries
- drop `wasm` from the release-draft `needs:` list
This is an explicit deferral, NOT a silent drop — every removed
piece is annotated with the work item it is waiting on.
Step 1b §3.2 P0.5B Run path, sub-phase C-hard CI follow-up.
spec §1.2 acceptance #7 calls for cargo build --examples --workspace 三 OS
PASS in CI. Previously cargo build --workspace + cargo clippy --all-targets
were considered equivalent (clippy with --all-targets compiles examples),
but Phase B+C Gate codex review CONCERN-C1 said the explicit step should
be in the workflow for unambiguous acceptance trace.
Adds cargo build --examples --workspace --target <target> --release
between cargo build --workspace and cargo test on host runners. Skipped on
cross-arch check_only matrix (cross examples build is out of scope; test
runners cover real arch builds).
Linux GPU tests:
- skia-safe Interface::new_native dlopens libGL.so + glXGetProcAddress;
fails on EGL pbuffer + llvmpipe (Mesa headless setup). Wiring
Interface::new_load_with(eglGetProcAddress) needs a new
GlContextProvider::get_proc_address method (spec §3.1 mini-patch
follow-up). Tracked LINUX_GPU_SKIA_LOADER_TBD.
- gpu_smoke + gpu_chrome_stub_composition Linux variants now #[ignore]
with explicit reason matching Windows pattern (#[ignore =
WINDOWS_GPU_DEFERRED_NO_RUNNER]); CI Linux test step drops xvfb +
STEP1A_REQUIRE_GPU env (no longer needed since tests ignored).
- macOS continues running real GPU smoke (no skia loader issue).
Windows ARM64:
- new aarch64-pc-windows-msvc matrix entry — cargo check only
(cross-compile from x86_64 windows-latest; no Win11 ARM hosted runner GA yet).
- rust-release.yml also gains windows-aarch64 archive build.
macos-local verify: all 14 tests pass (gpu_smoke + gpu_chrome_stub_composition
still run on macOS host).
GitHub Actions deprecated macos-13 Intel runners. Apple Silicon (macos-latest)
can cargo build/check x86_64-apple-darwin out of the box (no cross tool needed).
- rust-multiplatform.yml: macos-x86_64 job uses macos-latest + check_only=true
(binary arch ≠ host arch so no test runs; cargo check verifies the workspace
type-checks for x86_64 Macs)
- rust-release.yml: macos-x86_64 job uses macos-latest, cargo build --release
cross-compiles to x86_64; archive packaged as before
Hosted Ubuntu runner has libegl1-mesa-dev installed but no X display, so
`eglInitialize` fails with 'EGL is not initialized, or could not be
initialized, for the specified EGL display connection'.
xvfb gives EGL_DEFAULT_DISPLAY a real X11 connection so eglInitialize
succeeds; LIBGL_ALWAYS_SOFTWARE + llvmpipe + MESA_GL_VERSION_OVERRIDE
forces Mesa software pipe (no GPU on runner). Together these unblock
the EGL pbuffer GPU smoke + chrome+stub composition tests on Linux CI.
Spec v19 §11 invariant 1 requires shell-native to compile on iOS / Android
cargo check, with the `GlContextProvider` trait (invariant 2) importable on
every non-wasm target. Previously the desktop GL stack (glutin / winit /
skia-safe) was referenced unconditionally in src/, so mobile cargo check
broke the moment the Cargo.toml target-gated those deps to macOS / Linux /
Windows.
This change cfg-gates the desktop-only modules and items so the mobile
cargo check builds only the cross-platform surface:
- src/lib.rs: gate `backend` + `canvas_view_stub` modules and their
re-exports to desktop OS targets; add `EaglProvider` / `AndroidEglProvider`
re-exports under `target_os = "ios"` / `"android"`. `GlContextProvider`,
`ProviderError`, `ProviderResult` stay always-on (per §11 invariant 2).
- src/context/mod.rs: split into a cross-platform trait surface +
per-platform provider re-exports; gate `shared` (depends on `skia_safe` +
`winit`) to desktop only.
- src/context/provider.rs: cfg-gate `GlutinProvider` struct + impls + the
`pick_display_api` helper to desktop OS only; localize `CString` /
`NonZeroU32` imports inside fn bodies; gate `from_error` to desktop to
silence dead_code on mobile (the only caller is `GlutinProvider`).
- Cargo.toml: split deps into a cross-platform `cfg(not(wasm32))` block
(jian-core + glow + raw-window-handle, all required by the trait
signature on every non-wasm target) and a desktop-only block (skia-safe,
glutin, glutin-winit, winit, scopeguard, jian-skia, jian-host-desktop).
Merges the previously duplicate desktop `[target...]` table headers that
cargo rejected.
- ci: rust-multiplatform.yml mobile-check job now runs cargo check on
shell-native too (per the comment update there).
Verification:
- cargo check -p openpencil-shell-native --target aarch64-apple-darwin: PASS
- cargo check -p openpencil-shell-native --target aarch64-apple-ios: PASS
- cargo check -p openpencil-shell-native --target aarch64-linux-android: PASS
- cargo check -p openpencil-shell-native --target wasm32-unknown-unknown:
FAILS with the spec §1.2 `compile_error!` (intended).
- cargo test -p openpencil-shell-native: 14/14 PASS.
- cargo clippy -p openpencil-shell-native --all-targets -- -D warnings: clean
on macOS, iOS, Android targets.
- cargo fmt --check: clean.
- tests/common/mod.rs: egl.get_display(DEFAULT_DISPLAY) wrapped in unsafe block
(khronos-egl 6.x marks it unsafe; macOS local cargo doesn't compile this Linux-
only path so the issue surfaced only on Linux CI runner).
- rust-multiplatform.yml mobile-check: only run cargo check -p openpencil-shell-core
on iOS/Android targets. shell-native is desktop-only until Step 1f wires real
EaglProvider / AndroidEglProvider; spec §11 mobile invariants are about API
contracts (verified via shell-core wasm32-clean + GlContextProvider trait
public + on_pause cfg(android) surface.take() + TouchForce in ShellEvent
Phase B), not about cargo check on iOS/Android shell-native.
Linux GPU tests:
- skia-safe Interface::new_native dlopens libGL.so + glXGetProcAddress;
fails on EGL pbuffer + llvmpipe (Mesa headless setup). Wiring
Interface::new_load_with(eglGetProcAddress) needs a new
GlContextProvider::get_proc_address method (spec §3.1 mini-patch
follow-up). Tracked LINUX_GPU_SKIA_LOADER_TBD.
- gpu_smoke + gpu_chrome_stub_composition Linux variants now #[ignore]
with explicit reason matching Windows pattern (#[ignore =
WINDOWS_GPU_DEFERRED_NO_RUNNER]); CI Linux test step drops xvfb +
STEP1A_REQUIRE_GPU env (no longer needed since tests ignored).
- macOS continues running real GPU smoke (no skia loader issue).
Windows ARM64:
- new aarch64-pc-windows-msvc matrix entry — cargo check only
(cross-compile from x86_64 windows-latest; no Win11 ARM hosted runner GA yet).
- rust-release.yml also gains windows-aarch64 archive build.
macos-local verify: all 14 tests pass (gpu_smoke + gpu_chrome_stub_composition
still run on macOS host).
GitHub Actions deprecated macos-13 Intel runners. Apple Silicon (macos-latest)
can cargo build/check x86_64-apple-darwin out of the box (no cross tool needed).
- rust-multiplatform.yml: macos-x86_64 job uses macos-latest + check_only=true
(binary arch ≠ host arch so no test runs; cargo check verifies the workspace
type-checks for x86_64 Macs)
- rust-release.yml: macos-x86_64 job uses macos-latest, cargo build --release
cross-compiles to x86_64; archive packaged as before
Hosted Ubuntu runner has libegl1-mesa-dev installed but no X display, so
`eglInitialize` fails with 'EGL is not initialized, or could not be
initialized, for the specified EGL display connection'.
xvfb gives EGL_DEFAULT_DISPLAY a real X11 connection so eglInitialize
succeeds; LIBGL_ALWAYS_SOFTWARE + llvmpipe + MESA_GL_VERSION_OVERRIDE
forces Mesa software pipe (no GPU on runner). Together these unblock
the EGL pbuffer GPU smoke + chrome+stub composition tests on Linux CI.
Spec v19 §11 invariant 1 requires shell-native to compile on iOS / Android
cargo check, with the `GlContextProvider` trait (invariant 2) importable on
every non-wasm target. Previously the desktop GL stack (glutin / winit /
skia-safe) was referenced unconditionally in src/, so mobile cargo check
broke the moment the Cargo.toml target-gated those deps to macOS / Linux /
Windows.
This change cfg-gates the desktop-only modules and items so the mobile
cargo check builds only the cross-platform surface:
- src/lib.rs: gate `backend` + `canvas_view_stub` modules and their
re-exports to desktop OS targets; add `EaglProvider` / `AndroidEglProvider`
re-exports under `target_os = "ios"` / `"android"`. `GlContextProvider`,
`ProviderError`, `ProviderResult` stay always-on (per §11 invariant 2).
- src/context/mod.rs: split into a cross-platform trait surface +
per-platform provider re-exports; gate `shared` (depends on `skia_safe` +
`winit`) to desktop only.
- src/context/provider.rs: cfg-gate `GlutinProvider` struct + impls + the
`pick_display_api` helper to desktop OS only; localize `CString` /
`NonZeroU32` imports inside fn bodies; gate `from_error` to desktop to
silence dead_code on mobile (the only caller is `GlutinProvider`).
- Cargo.toml: split deps into a cross-platform `cfg(not(wasm32))` block
(jian-core + glow + raw-window-handle, all required by the trait
signature on every non-wasm target) and a desktop-only block (skia-safe,
glutin, glutin-winit, winit, scopeguard, jian-skia, jian-host-desktop).
Merges the previously duplicate desktop `[target...]` table headers that
cargo rejected.
- ci: rust-multiplatform.yml mobile-check job now runs cargo check on
shell-native too (per the comment update there).
Verification:
- cargo check -p openpencil-shell-native --target aarch64-apple-darwin: PASS
- cargo check -p openpencil-shell-native --target aarch64-apple-ios: PASS
- cargo check -p openpencil-shell-native --target aarch64-linux-android: PASS
- cargo check -p openpencil-shell-native --target wasm32-unknown-unknown:
FAILS with the spec §1.2 `compile_error!` (intended).
- cargo test -p openpencil-shell-native: 14/14 PASS.
- cargo clippy -p openpencil-shell-native --all-targets -- -D warnings: clean
on macOS, iOS, Android targets.
- cargo fmt --check: clean.
- tests/common/mod.rs: egl.get_display(DEFAULT_DISPLAY) wrapped in unsafe block
(khronos-egl 6.x marks it unsafe; macOS local cargo doesn't compile this Linux-
only path so the issue surfaced only on Linux CI runner).
- rust-multiplatform.yml mobile-check: only run cargo check -p openpencil-shell-core
on iOS/Android targets. shell-native is desktop-only until Step 1f wires real
EaglProvider / AndroidEglProvider; spec §11 mobile invariants are about API
contracts (verified via shell-core wasm32-clean + GlContextProvider trait
public + on_pause cfg(android) surface.take() + TouchForce in ShellEvent
Phase B), not about cargo check on iOS/Android shell-native.
P0 dep-stack probe (Step 1a) cleared all three OS targets in CI
run 25358457742:
- macOS aarch64: full window+GL probe (cross-API state + readback) PASS
- Linux x86_64 (hosted runner): link-time PASS, runtime DEFERRED
(LINUX_GPU_DEFERRED_NO_RUNNER) — Xvfb GLX limitation; same skip as
bevy / rust-skia / iced CI.
- Windows x86_64 (hosted runner): link-time PASS, runtime DEFERRED
(WINDOWS_GPU_DEFERRED_NO_RUNNER per spec §8.2).
Pin versions captured in
`openpencil-docs/superpowers/notes/2026-05-05-skia-glow-loader-compat-probe.md`.
Reverts:
- transient `[dev-dependencies]` block in shell-native Cargo.toml
(skia-safe / glutin / glutin-winit / glow / raw-window-handle /
scopeguard / dev-only winit override).
- transient `tests/p0_probe.rs` + `examples/p0_probe.rs`.
- transient workflow steps that gated `--ignored P0_PROBE_GATE` and the
Xvfb / freetype / mesa apt installs that only the probe needed.
Kept:
- prod winit dep features `["x11", "wayland", "wayland-csd-adwaita",
"rwh_06"]` — needed for Linux to satisfy winit's
`compile_error!("...not supported by winit")` guard. Stage F may
trim this when RenderBackend lands.
- workflow's libxkbcommon / libwayland apt install — winit's link-time
deps for the features above.
- `.gitattributes` — enforces `eol=lf` so future cross-OS rustfmt stays
green.
Task 1 will reintroduce skia-safe / glutin / glow / raw-window-handle
/ scopeguard as permanent prod deps when SharedSkiaContext +
RenderBackend land.
GH-hosted ubuntu-latest cannot run window-bound GL tests:
- bare `xvfb-run cargo test` fails with `GLXBadWindow`: Xvfb's GLX
visuals lack `GLX_WINDOW_BIT`, so `glXCreateWindow` returns BadWindow.
- `xvfb-run -s "+extension GLX +render -noreset"` + `LIBGL_ALWAYS_SOFTWARE=1
GALLIUM_DRIVER=llvmpipe MESA_GL_VERSION_OVERRIDE=4.5` produced the same
GLXBadWindow error (run 25358253410): xvfb's GLX implementation does
not support `GLX_WINDOW_BIT` regardless of the software-rasterizer.
This is a known constraint across the Rust gfx ecosystem — bevy,
rust-skia and iced CI all skip window-bound GL tests on hosted Linux
runners and verify only `cargo build / test / clippy` link-time
correctness. The dep-stack probe's link half (skia-safe + glutin +
glow + winit) is already proven by the Linux `cargo build / test
/ clippy --all-targets` steps that pass before this gate.
Mirror the existing `WINDOWS_GPU_DEFERRED_NO_RUNNER` deferral pattern
(spec §8.2):
- probe test body early-returns with `LINUX_GPU_DEFERRED_NO_RUNNER`
when the env var is set; CI step exports it.
- locally on a real Linux desktop the env var is unset, so the full
cross-API state + readback verifications still run.
macOS retains the full window+GL path (CI + local), which alone
covers spec §7.2(2) "cross-API GL state visibility" and §6.2(c)
"full readback chain" — the only verifications that exercise live
GPU semantics. Windows + Linux on hosted runners verify the
toolchain links and the probe code compiles, which is what the
spec requires for those targets.
Linux P0 probe was failing with `GLXBadWindow` because:
- bare `xvfb-run` brings up Xvfb with default args (no `+extension GLX`);
the X server then advertises no GLX FBConfigs, so winit's X11 backend
fails when glutin tries to create a GL window.
- the runner has no GPU, so even with GLX enabled mesa would not pick a
hardware visual; without a software fallback configured glutin cannot
resolve `ContextApi::OpenGl`.
Fix:
- pass `xvfb-run -s "-screen 0 1280x1024x24 +extension GLX +render
-noreset"` so Xvfb advertises a 24-bit GLX-capable visual.
- set `LIBGL_ALWAYS_SOFTWARE=1`, `GALLIUM_DRIVER=llvmpipe`, and
`MESA_GL_VERSION_OVERRIDE=4.5` so mesa loads llvmpipe (CPU
rasterizer) and reports a desktop-GL version high enough for skia.
These env vars propagate naturally from the workflow shell down through
xvfb-run → cargo → the spawned `cargo run --example p0_probe`
subprocess (probe runs each verification in a fresh process so winit's
EventLoop singleton guard doesn't trip).
Linux `cargo test --workspace` failed at link time:
/usr/bin/ld: cannot find -lfreetype: No such file or directory
/usr/bin/ld: cannot find -lfontconfig: No such file or directory
collect2: error: ld returned 1 exit status
skia-safe 0.97 (P0 probe transient dev-dep) links against the system
freetype + fontconfig on Linux. The GitHub-hosted ubuntu-latest runner
ships only the runtime libs; we need the `-dev` packages so `cc` can
resolve `-lfreetype` / `-lfontconfig` during link.
macOS and Windows do not link against these (skia-bindings uses
CoreText / DirectWrite respectively), so the install step stays
gated on `runner.os == 'Linux'`.
Drives the three-OS CI matrix verification of the skia-safe + glutin +
glow + winit dep stack per Step 1a spec §7.
- examples/p0_probe.rs: stencil_visibility + readback chain runner (must
own a real OS main thread because winit on macOS rejects
EventLoop::new() from cargo test worker threads).
- tests/p0_probe.rs: subprocess-invoke wrapper, gated
#[ignore = "P0_PROBE_GATE"] so default cargo test stays untouched.
- Cargo.toml: add transient [target.'cfg(not(target_arch = "wasm32"))'.
dev-dependencies] block (skia-safe 0.97 + glutin 0.32.3 + glutin-winit
0.5.0 + glow 0.17.0 + raw-window-handle 0.6.2 + scopeguard 1.2.0 +
winit defaults). Pinned to versions resolved in /tmp/skia-glow-probe.
- .github/workflows/rust-check.yml: install Linux GL prereqs (xvfb,
mesa, libxkbcommon, libwayland) and add a P0-probe-gate step running
cargo test --ignored on each OS (Linux through xvfb-run; Windows
early-returns per spec §8.2 WINDOWS_GPU_DEFERRED_NO_RUNNER).
All three artefacts are TRANSIENT — reverted in a follow-up cleanup
commit after CI is green and the loader-compat notes commit lands.
Task 1 owns the permanent integration.
spec §1.2 acceptance #7 calls for cargo build --examples --workspace 三 OS
PASS in CI. Previously cargo build --workspace + cargo clippy --all-targets
were considered equivalent (clippy with --all-targets compiles examples),
but Phase B+C Gate codex review CONCERN-C1 said the explicit step should
be in the workflow for unambiguous acceptance trace.
Adds cargo build --examples --workspace --target <target> --release
between cargo build --workspace and cargo test on host runners. Skipped on
cross-arch check_only matrix (cross examples build is out of scope; test
runners cover real arch builds).
Linux GPU tests:
- skia-safe Interface::new_native dlopens libGL.so + glXGetProcAddress;
fails on EGL pbuffer + llvmpipe (Mesa headless setup). Wiring
Interface::new_load_with(eglGetProcAddress) needs a new
GlContextProvider::get_proc_address method (spec §3.1 mini-patch
follow-up). Tracked LINUX_GPU_SKIA_LOADER_TBD.
- gpu_smoke + gpu_chrome_stub_composition Linux variants now #[ignore]
with explicit reason matching Windows pattern (#[ignore =
WINDOWS_GPU_DEFERRED_NO_RUNNER]); CI Linux test step drops xvfb +
STEP1A_REQUIRE_GPU env (no longer needed since tests ignored).
- macOS continues running real GPU smoke (no skia loader issue).
Windows ARM64:
- new aarch64-pc-windows-msvc matrix entry — cargo check only
(cross-compile from x86_64 windows-latest; no Win11 ARM hosted runner GA yet).
- rust-release.yml also gains windows-aarch64 archive build.
macos-local verify: all 14 tests pass (gpu_smoke + gpu_chrome_stub_composition
still run on macOS host).
GitHub Actions deprecated macos-13 Intel runners. Apple Silicon (macos-latest)
can cargo build/check x86_64-apple-darwin out of the box (no cross tool needed).
- rust-multiplatform.yml: macos-x86_64 job uses macos-latest + check_only=true
(binary arch ≠ host arch so no test runs; cargo check verifies the workspace
type-checks for x86_64 Macs)
- rust-release.yml: macos-x86_64 job uses macos-latest, cargo build --release
cross-compiles to x86_64; archive packaged as before
Hosted Ubuntu runner has libegl1-mesa-dev installed but no X display, so
`eglInitialize` fails with 'EGL is not initialized, or could not be
initialized, for the specified EGL display connection'.
xvfb gives EGL_DEFAULT_DISPLAY a real X11 connection so eglInitialize
succeeds; LIBGL_ALWAYS_SOFTWARE + llvmpipe + MESA_GL_VERSION_OVERRIDE
forces Mesa software pipe (no GPU on runner). Together these unblock
the EGL pbuffer GPU smoke + chrome+stub composition tests on Linux CI.
Spec v19 §11 invariant 1 requires shell-native to compile on iOS / Android
cargo check, with the `GlContextProvider` trait (invariant 2) importable on
every non-wasm target. Previously the desktop GL stack (glutin / winit /
skia-safe) was referenced unconditionally in src/, so mobile cargo check
broke the moment the Cargo.toml target-gated those deps to macOS / Linux /
Windows.
This change cfg-gates the desktop-only modules and items so the mobile
cargo check builds only the cross-platform surface:
- src/lib.rs: gate `backend` + `canvas_view_stub` modules and their
re-exports to desktop OS targets; add `EaglProvider` / `AndroidEglProvider`
re-exports under `target_os = "ios"` / `"android"`. `GlContextProvider`,
`ProviderError`, `ProviderResult` stay always-on (per §11 invariant 2).
- src/context/mod.rs: split into a cross-platform trait surface +
per-platform provider re-exports; gate `shared` (depends on `skia_safe` +
`winit`) to desktop only.
- src/context/provider.rs: cfg-gate `GlutinProvider` struct + impls + the
`pick_display_api` helper to desktop OS only; localize `CString` /
`NonZeroU32` imports inside fn bodies; gate `from_error` to desktop to
silence dead_code on mobile (the only caller is `GlutinProvider`).
- Cargo.toml: split deps into a cross-platform `cfg(not(wasm32))` block
(jian-core + glow + raw-window-handle, all required by the trait
signature on every non-wasm target) and a desktop-only block (skia-safe,
glutin, glutin-winit, winit, scopeguard, jian-skia, jian-host-desktop).
Merges the previously duplicate desktop `[target...]` table headers that
cargo rejected.
- ci: rust-multiplatform.yml mobile-check job now runs cargo check on
shell-native too (per the comment update there).
Verification:
- cargo check -p openpencil-shell-native --target aarch64-apple-darwin: PASS
- cargo check -p openpencil-shell-native --target aarch64-apple-ios: PASS
- cargo check -p openpencil-shell-native --target aarch64-linux-android: PASS
- cargo check -p openpencil-shell-native --target wasm32-unknown-unknown:
FAILS with the spec §1.2 `compile_error!` (intended).
- cargo test -p openpencil-shell-native: 14/14 PASS.
- cargo clippy -p openpencil-shell-native --all-targets -- -D warnings: clean
on macOS, iOS, Android targets.
- cargo fmt --check: clean.
- tests/common/mod.rs: egl.get_display(DEFAULT_DISPLAY) wrapped in unsafe block
(khronos-egl 6.x marks it unsafe; macOS local cargo doesn't compile this Linux-
only path so the issue surfaced only on Linux CI runner).
- rust-multiplatform.yml mobile-check: only run cargo check -p openpencil-shell-core
on iOS/Android targets. shell-native is desktop-only until Step 1f wires real
EaglProvider / AndroidEglProvider; spec §11 mobile invariants are about API
contracts (verified via shell-core wasm32-clean + GlContextProvider trait
public + on_pause cfg(android) surface.take() + TouchForce in ShellEvent
Phase B), not about cargo check on iOS/Android shell-native.
Linux GPU tests:
- skia-safe Interface::new_native dlopens libGL.so + glXGetProcAddress;
fails on EGL pbuffer + llvmpipe (Mesa headless setup). Wiring
Interface::new_load_with(eglGetProcAddress) needs a new
GlContextProvider::get_proc_address method (spec §3.1 mini-patch
follow-up). Tracked LINUX_GPU_SKIA_LOADER_TBD.
- gpu_smoke + gpu_chrome_stub_composition Linux variants now #[ignore]
with explicit reason matching Windows pattern (#[ignore =
WINDOWS_GPU_DEFERRED_NO_RUNNER]); CI Linux test step drops xvfb +
STEP1A_REQUIRE_GPU env (no longer needed since tests ignored).
- macOS continues running real GPU smoke (no skia loader issue).
Windows ARM64:
- new aarch64-pc-windows-msvc matrix entry — cargo check only
(cross-compile from x86_64 windows-latest; no Win11 ARM hosted runner GA yet).
- rust-release.yml also gains windows-aarch64 archive build.
macos-local verify: all 14 tests pass (gpu_smoke + gpu_chrome_stub_composition
still run on macOS host).
GitHub Actions deprecated macos-13 Intel runners. Apple Silicon (macos-latest)
can cargo build/check x86_64-apple-darwin out of the box (no cross tool needed).
- rust-multiplatform.yml: macos-x86_64 job uses macos-latest + check_only=true
(binary arch ≠ host arch so no test runs; cargo check verifies the workspace
type-checks for x86_64 Macs)
- rust-release.yml: macos-x86_64 job uses macos-latest, cargo build --release
cross-compiles to x86_64; archive packaged as before
Hosted Ubuntu runner has libegl1-mesa-dev installed but no X display, so
`eglInitialize` fails with 'EGL is not initialized, or could not be
initialized, for the specified EGL display connection'.
xvfb gives EGL_DEFAULT_DISPLAY a real X11 connection so eglInitialize
succeeds; LIBGL_ALWAYS_SOFTWARE + llvmpipe + MESA_GL_VERSION_OVERRIDE
forces Mesa software pipe (no GPU on runner). Together these unblock
the EGL pbuffer GPU smoke + chrome+stub composition tests on Linux CI.
Spec v19 §11 invariant 1 requires shell-native to compile on iOS / Android
cargo check, with the `GlContextProvider` trait (invariant 2) importable on
every non-wasm target. Previously the desktop GL stack (glutin / winit /
skia-safe) was referenced unconditionally in src/, so mobile cargo check
broke the moment the Cargo.toml target-gated those deps to macOS / Linux /
Windows.
This change cfg-gates the desktop-only modules and items so the mobile
cargo check builds only the cross-platform surface:
- src/lib.rs: gate `backend` + `canvas_view_stub` modules and their
re-exports to desktop OS targets; add `EaglProvider` / `AndroidEglProvider`
re-exports under `target_os = "ios"` / `"android"`. `GlContextProvider`,
`ProviderError`, `ProviderResult` stay always-on (per §11 invariant 2).
- src/context/mod.rs: split into a cross-platform trait surface +
per-platform provider re-exports; gate `shared` (depends on `skia_safe` +
`winit`) to desktop only.
- src/context/provider.rs: cfg-gate `GlutinProvider` struct + impls + the
`pick_display_api` helper to desktop OS only; localize `CString` /
`NonZeroU32` imports inside fn bodies; gate `from_error` to desktop to
silence dead_code on mobile (the only caller is `GlutinProvider`).
- Cargo.toml: split deps into a cross-platform `cfg(not(wasm32))` block
(jian-core + glow + raw-window-handle, all required by the trait
signature on every non-wasm target) and a desktop-only block (skia-safe,
glutin, glutin-winit, winit, scopeguard, jian-skia, jian-host-desktop).
Merges the previously duplicate desktop `[target...]` table headers that
cargo rejected.
- ci: rust-multiplatform.yml mobile-check job now runs cargo check on
shell-native too (per the comment update there).
Verification:
- cargo check -p openpencil-shell-native --target aarch64-apple-darwin: PASS
- cargo check -p openpencil-shell-native --target aarch64-apple-ios: PASS
- cargo check -p openpencil-shell-native --target aarch64-linux-android: PASS
- cargo check -p openpencil-shell-native --target wasm32-unknown-unknown:
FAILS with the spec §1.2 `compile_error!` (intended).
- cargo test -p openpencil-shell-native: 14/14 PASS.
- cargo clippy -p openpencil-shell-native --all-targets -- -D warnings: clean
on macOS, iOS, Android targets.
- cargo fmt --check: clean.