Cross-Platform Guarantees: Audit + Dynamic Enforcement Plan (2026-06-15)
Cross-Platform Guarantees: Audit + Dynamic Enforcement Plan
Section titled “Cross-Platform Guarantees: Audit + Dynamic Enforcement Plan”Status: Plan / design — no code landed. Produced by a full-repo graphify graph build (62,594 AST nodes / 143,930 edges over 4,252 code files + semantic doc layer) overlaid with an 8-avenue semantic audit. All file:line citations below were hand-verified or cross-corroborated by ≥2 independent audit passes.
1. Verdict
Section titled “1. Verdict”This workspace was built primarily on Windows and must compile + build + test on
Windows (x86_64-pc-windows-msvc), Ubuntu Linux (x86_64-unknown-linux-gnu),
and macOS (aarch64-apple-darwin).
The code is, on most avenues, disciplined about portability — per-OS path
resolution, [target.'cfg(...)'] dependency tables, and cfg-gated symbol pairs are
the norm. The enforcement is the hole:
- The blocking merge gate runs on Linux self-hosted only. The required check
Check, Build, and Test (Rust)(ci.yml:935ci-summary)needs: [guards-fast, lints, compiler-gates, tests, audits]— every one of those jobs isruns-on: [self-hosted, linux, x64]. No Windows or macOS job is in the required dependency chain. The only thing proven on Windows/macOS per PR is “vox-clibuilds andvox compilehas a help screen,” and only when CLI-adjacent paths change. - There is no cross-platform detector or gate today. Portability is convention-only
(
CLAUDE.md/MEMORY.md: nocargo fmt --all,CREATE_NO_WINDOWon spawns). A purpose-built detector (scripts/coverage-graph/os_compat.py) and a script-policy gate (vox ci script-hygiene) both exist but are unwired into CI/pre-push. - Two real build/run breaks already exist on Linux/macOS (§3) — invisible because the gate never compiles those OSes with the relevant feature set.
The fix is two-pronged and matches existing machinery exactly: (A) promote a real
3-OS clippy + check + test matrix into the required gate; (B) add a dynamic
cross-platform enforcement layer built on the existing vox-arch-check forbidden-pattern
scanner and vox-code-audit detector trait — both glob-over-disk walkers driven by SSOT
config, so new files/crates are covered automatically as the codebase changes.
2. The complete cross-platform surface (“avenues”)
Section titled “2. The complete cross-platform surface (“avenues”)”The audit decomposed portability risk into 8 non-overlapping avenues. This is the surface map — the enforcement layer in §5 must cover every one.
| # | Avenue | Health | Where it lives |
|---|---|---|---|
| 1 | Conditional compilation (cfg) OS-coverage | Strong (203 sites, ~all balanced) | crates/**/*.rs, build.rs |
| 2 | Path & filesystem portability | Strong in Rust; weak in .vox scripts | vox-config/src/paths.rs, vox-identity/src/storage.rs, scripts/** |
| 3 | Process spawning & shell-outs | Mixed (~7 risky + console-window gap) | Command::new sites (~498, 186 files) |
| 4 | Platform deps & build scripts | Strong; 1 hard break | every Cargo.toml, build.rs, .cargo/config.toml |
| 5 | Line endings / encoding / goldens | Mature but coverage mismatch | .gitattributes, *.snap, *.txt, vox-cli-ci::line_endings |
| 6 | CI/CD + Docker coverage | The core gap (Linux-only gate) | .github/workflows/*.yml, Dockerfile* |
| 7 | Existing enforcement machinery | 5 layers exist; 0 portability rules | vox-arch-check, vox-code-audit, contracts/**, lefthook.yml |
| 8 | Scripts / GUI / mobile / toolchain | Spine exists; gate unwired | scripts/**, vox-gui (Tauri), mobile workflows |
3. Confirmed defects (hand-verified — fix these first)
Section titled “3. Confirmed defects (hand-verified — fix these first)”D1 — vox-ml-cli fails to build on Linux + macOS (HIGH, verified)
Section titled “D1 — vox-ml-cli fails to build on Linux + macOS (HIGH, verified)”crates/vox-ml-cli/src/commands/schola/process_priority.rs:32 calls
libc::setpriority(libc::PRIO_PROCESS, 0, 10) under #[cfg(unix)], but
crates/vox-ml-cli/Cargo.toml declares windows-sys (line 94) and no libc and
no [target.'cfg(unix)'.dependencies] block. Reached via the schola train path
gated behind feature = "gpu" (also implied by mens-qlora / mens-candle-cuda /
cloud) — the MENS training build.
- Breaks:
cargo build -p vox-ml-cli --features gpuon Linux/macOS →E0433: use of undeclared crate or module 'libc'. - Why hidden: on Windows the
#[cfg(unix)]arm is stripped, so the workspace’s primary dev OS never exercises it. This is the canonical “built on Windows, broken elsewhere” defect. - Fix: add to
crates/vox-ml-cli/Cargo.toml[target.'cfg(unix)'.dependencies]\nlibc = { workspace = true }(libcis already a root workspace dep). Mirror the canonical templatecrates/vox-cli/Cargo.toml:280-290. (Low, same crate: move its unconditionalwindows-sysinto[target.'cfg(windows)'.dependencies].)
D2 — flywheel.rs autonomous-training spawn is dead + Windows-only (HIGH, verified)
Section titled “D2 — flywheel.rs autonomous-training spawn is dead + Windows-only (HIGH, verified)”crates/vox-orchestrator/src/services/flywheel.rs:136
(trigger_autonomous_training) spawns pwsh -File scripts/mens-full-pipeline.ps1.
The script does not exist anywhere in the repo (verified) and pwsh is hardcoded
with no Unix branch.
- Breaks: always errors (missing script) on Windows; additionally non-portable.
- Fix: remove the dead function, or drive the pipeline via
vox run scripts/*.vox(project VoxScript-first policy) and resolve the shell viawhich::which("pwsh").or_else(|_| which::which("powershell")).
D3 — cargo fmt --all inside a .vox script (HIGH, policy self-violation, verified-by-agent)
Section titled “D3 — cargo fmt --all inside a .vox script (HIGH, policy self-violation, verified-by-agent)”scripts/perf/test-baseline.vox:81 calls cargo fmt --all --check. The entire
os error 206 Windows mitigation depends on never running cargo fmt --all
(CLAUDE.md, AGENTS.md §Formatting). Fix: use the sanctioned VOX_FMT_CHECK=1 vox run scripts/fmt.vox.
4. CI/CD + Docker gap analysis (avenue 6 — the core ask)
Section titled “4. CI/CD + Docker gap analysis (avenue 6 — the core ask)”4.1 What proves what, today
Section titled “4.1 What proves what, today”| Workflow : job | OS / host | Trigger | Depth | Gating |
|---|---|---|---|---|
ci.yml : ci-summary = “Check, Build, and Test (Rust)“ | self-hosted linux | PR / push / merge_group | aggregate of below | REQUIRED |
ci.yml : guards-fast / lints / compiler-gates / tests / audits | self-hosted linux | PR / push / mq | fmt, clippy (excl. vox-gui), nextest --workspace, --all-features check | feed required |
cross-platform-check.yml | windows-latest + macos-latest | weekly cron Mon + dispatch | cargo check --workspace (excl. vox-gui) + nextest of 3 crates | ❌ not required |
compile-matrix.yml (win/mac legs) | windows-latest, macos-latest | PR, path-filtered (~8 globs) | cargo build -p vox-cli + vox compile --help only | ❌ |
setup-e2e.yml | ubuntu + windows + macos | push:main / nightly / dispatch (not PR) | clean-room cargo run + cargo check --workspace | ❌ |
ci-fallback-hosted.yml : gui-windows-build-smoke | windows-latest | dispatch only | full Tauri GUI build + headless smoke | ❌ |
release-* / bundle-release / release-gui | win/mac/linux | tag / release only | build + package + installer | tag only |
docker-eval.yml + root Dockerfile | ubuntu-latest | push:main (Dockerfile paths) | buildx vox-cli image + Trivy (report-only, exit 0) | ❌ |
4.2 What each OS does NOT prove per PR
Section titled “4.2 What each OS does NOT prove per PR”- Linux (the only gated OS): full clippy +
nextest --workspace+ coverage — butvox-guiis excluded from gated clippy (ci.yml:509) and never compiled on a PR; Docker image build, GUI Playwright, browser/CDP, and the 24-crate--all-featuresmatrix arepush:main/full-ci-label only. - Windows: on a routine PR, nothing unless a
compile-matrix.ymlpath changed — and then onlyvox compile --help. Nocargo check --workspace, no clippy, no tests. Windows#[cfg(windows)]paths (path separators,CREATE_NO_WINDOW, the isolated mens-gate runner,os error 206logic, Tauri sidecar staging) are unexercised per PR. Fullcargo check --workspaceis weekly-cron-only and excludesvox-gui. - macOS: identical to Windows. Real
cargo check --workspaceis weekly-cron-only,vox-gui-excluded, targetingaarch64-apple-darwin(the stated goal triple).
Quantified: cargo check --workspace, clippy -D warnings, and nextest --workspace are proven on zero non-Linux OSes per PR.
4.3 Docker gaps
Section titled “4.3 Docker gaps”Docker only ever simulates Linux (Dockerfile.ci-runner is the sanctioned
“simulate Linux from a Windows dev box” path). Gaps: Trivy is report-only (never blocks);
image is single-arch linux/amd64 (no linux/arm64); the PR gate builds no Docker
image (smoke is push:main-only), so a Dockerfile-breaking PR merges green.
5. Enforcement design (phased — the deliverable)
Section titled “5. Enforcement design (phased — the deliverable)”Two prongs. Prong A forces proof; Prong B prevents the sources of breakage from being introduced. Both reuse existing machinery so they cover all surfaces dynamically.
Prong A — Force 3-OS proof in the required gate
Section titled “Prong A — Force 3-OS proof in the required gate”| Phase | Change | Cost note |
|---|---|---|
| A0 | Fix D1/D2/D3 so a 3-OS gate can go green at all. | trivial |
| A1 | Promote cross-platform-check.yml from weekly cron → pull_request + merge_group + push:main; upgrade depth from cargo check to clippy -D warnings + nextest run --workspace (mirror ci-fallback-hosted.yml:73-92); add an ubuntu-latest row (the workflow’s name claims cross-platform but omits Linux). sccache-GHA + CARGO_INCREMENTAL=0 already wired. | GitHub-hosted win (2×) + mac (10×) minutes |
| A2 | Make the new Win/mac/Ubuntu jobs required: either add them to ci-summary.needs, or register per-OS checks Check, Build, and Test (Windows)/(macOS) in branch protection. To bound cost, run cheap check+clippy on every PR but defer full nextest --workspace on Win/mac to merge_group only (run once per merge batch). | merge-queue already enabled |
| A3 | Close the vox-gui hole: promote ci-fallback-hosted.yml’s gui-windows-build-smoke (full pnpm→sidecar→cargo build -p vox-gui) to PR-triggered + required, and add Linux WebKitGTK + macOS WKWebView legs. Add an explicit sidecar vox-<triple>[.exe] build+rename step before tauri-action (fixes the release-gui.yml externalBin + src-tauri signing-path issues). | Linux needs libwebkit2gtk-4.1-dev |
| A4 | Replace compile-matrix.yml’s vox compile --help win/mac legs with cargo check --workspace --exclude vox-gui, or retire it (superseded by A1). Add a linux/arm64 buildx leg + make Trivy gating for the Docker path. | — |
Prong B — Dynamic cross-platform detectors (cover surfaces as code changes)
Section titled “Prong B — Dynamic cross-platform detectors (cover surfaces as code changes)”The right homes already exist and are glob-over-disk SSOT-driven, so they scan every new file/crate automatically — no hand-maintained file lists:
vox-arch-checkRule 11[[forbidden_pattern]](docs/src/architecture/layers.toml, scanned bycrates/vox-arch-check/src/forbidden_patterns.rs:63). Has per-lineallow_annotationescape hatch +exempt_files+ per-rule strictness promotion ([guards], shipwarn→ promote toerroronce clean, exactly asorphanwas). Add rules for:no-hardcoded-shell-spawn—Command::new("(cmd|powershell|pwsh|sh|bash)")outside a cfg-gated/which-resolved wrapper (avenue 3).missing-create-no-window—Command::new(incrates/vox-gui/**lackingcreation_flags/quiet_command(only 2 of ~180 spawn sites set it today). Best as a small bespoke check (multi-line) scoped to GUI.ungated-platform-use—use (winapi|nix|core_foundation|objc)::not under a matching#[cfg(...)], cross-checked against the crate’s[target.*]tables (catches the D1 class).
contracts/code-audit/rules.v1.yaml(loaded byrule_pack_detector.rs, no recompile, fixture-scored) — regex hazards across Rust+TS+Vox, beside the existingmagic-value/pathrule (rules.v1.yaml:530): hardcoded path separators /C:\//tmpstring literals,.ps1/.shliterals,/dev/nullvsNUL.vox-code-auditdetector trait (rules.rs:214, register indetectors/mod.rs:148, bumprule_count) — for anything needing AST context (thellm_provider_calldetector is the template). Candidate:\\?\canonicalize-leak detector (avenue 2).- Wire the two dark gates that already exist:
scripts/coverage-graph/os_compat.py— a complete portability scanner that no CI job references, yet its committed outputgraphify-out/OS_COMPATIBILITY.mdalready reports 168 un-gated portability findings + 31 asymmetric-cfg files across 3,271 Rust files. This is the proof the gate works and the tree is already dirty. Its current category counts (un-gated):abs-unix-path64,dynlib-ext33 (.so/.dll/.dylibsuffix assumptions — an avenue the manual sweep under-weighted),path-sep-env12,crlf-literal17,path-join-fmt11,env-home-asym9,shell-command5,home-tilde3,win-drive-path2,os-unix-api11,os-windows-api1. Wire it intovox ciatwarn, drive the 168 down, then promote toerror; extend its glob fromcrates/**/*.rsto also coverscripts/**/*.vox(where the real breakage lives).vox ci script-hygiene(run_script_hygiene,matrix.rs:97) — enforces the “.vox-only automation” policy but is never invoked; 12scripts/coverage-graph/*.pyfiles slipped past it. Add toguards-fast+ pre-push; add.bat/.cmdto the prohibited-extension set; prune the 2 dead allowlist entries.
- Line-endings backstop (avenue 5): change
.gitattributes:2to* text=auto eol=lfthengit add --renormalize .; add a config-independent CI gategit ls-files --eol | grep -E 'i/crlf|i/mixed'(whitelisting*.ps1 *.bat *.cmd); syncEXT_LF(vox-cli-ci/src/line_endings.rs:10) with the.gitattributeseol=lfset + add a parity test; strip the 46 byte-0 BOM files + add a BOM gate; normalize newlines incommand_catalog_paths_baseline.rs:51.
Why this is “dynamic”
Section titled “Why this is “dynamic””Both forbidden-pattern and the detector trait are walkers over disk driven by SSOT
config, not allowlists. A crate added next month is scanned on the next run with zero
edits; the only per-site maintenance is adding an allow_annotation when a platform call is
intentionally one-sided — which is the desired forcing function. A new
contracts/portability/surface-registry.v1.yaml was considered and rejected:
cross-platform hazards are anti-patterns to forbid, not surfaces to enumerate, so the
forbidden-pattern model fits better than the GUI-style surface registry.
6. Recommended execution order
Section titled “6. Recommended execution order”- A0 / §3 — land D1, D2, D3 fixes (without them no 3-OS gate can pass).
- B.4 — wire
script-hygiene+os_compat.py(cheap, immediate, no infra cost). - B.5 —
.gitattributes eol=lf+ CRLF/BOM gates (eliminates a whole flake class). - A1 → A2 — promote
cross-platform-check.ymlto a required 3-OS clippy+test matrix. - B.1–B.3 — land forbidden-pattern + detector rules at
warn, drive the tree clean, then promote toerror. - A3 / A4 —
vox-gui3-OS build proof + Docker arm64/Trivy hardening.
7. Done-right templates (copy these)
Section titled “7. Done-right templates (copy these)”crates/vox-cli/Cargo.toml:280-290— four[target.'cfg(...)']dep blocks (the D1 fix).crates/vox-cli/src/commands/runtime/run/sandbox.rs:16— linux/windows/not(any())code-gating.crates/vox-config/src/paths.rs+crates/vox-identity/src/storage.rs:58-70— per-OS dir +#[cfg(unix)]perms with anot(unix)fallback.crates/vox-cli/src/fs_utils.rs:83-104—strip_windows_verbatim_path(\\?\neutralizer).crates/vox-cli-core/src/fs_utils.rs:23—open_browser_synccfg-gatingcmd /C start/open/xdg-open.crates/vox-cli/build.rs:9-17—CARGO_CFG_TARGET_OS-based linker branch (cross-compile-safe).scripts/render-durable-animation.vox:19-23— multi-OS binary auto-detect candidate list.
8. Branch-protection wiring (manual, one-time)
Section titled “8. Branch-protection wiring (manual, one-time)”After cross-platform-check.yml runs green on a PR, a repo admin must add the status check
Cross-Platform (Win/macOS/Ubuntu) to the main branch-protection required-checks set
(alongside the existing Check, Build, and Test (Rust)). This is the step that makes 3-OS proof
forced rather than advisory. Until then the matrix runs on every PR but does not block merge.