docs/superpowers/plans/2026-05-25-ws1-sys-6.0-parity.md
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Make raylib-sys a faithful, verified, cross-platform-green FFI surface for raylib 6.0 — new symbols bound and guarded, feature flags reconciled with 6.0's config.h, hand-defined type layouts proven correct, and the crate building on ubuntu + macOS + windows in CI on the fork.
Architecture: raylib-sys already compiles against raylib 6.0 (WS0 bumped the submodule; bindgen has no allowlist so it regenerated every symbol automatically). WS1 therefore is verification + reconciliation + cross-platform CI, not a rewrite. The safe raylib crate stays red until WS3 — do not touch it here. Work continues on the long-lived 6.0-rc branch.
Tech Stack: Rust 1.85, bindgen + cmake (sys build), GitHub Actions on the fork remote (Dacode45/ms-raylib-rs), gh CLI.
Reference: spec docs/superpowers/specs/2026-05-25-raylib-rs-6.0-roadmap-design.md; WS1 worklist docs/superpowers/notes/ws1-breakage-baseline.md; backlog docs/superpowers/inventory.md.
Owner decision recorded (2026-05-25): push 6.0-rc to the fork now to activate CI, and pull a slice of WS6 forward — expand CI to a 3-OS raylib-sys build. This refines the timing of D4/D6/D7; it does not change D5 (canonical merge still only at WS8). Web/wasm verification stays in WS6 (the emscripten spike found local Windows emsdk needs Python; run web CI on Linux).
Pre-flight:
git rev-parse --abbrev-ref HEAD → 6.0-rc.git remote get-url fork → https://github.com/Dacode45/ms-raylib-rs.git.git -C raylib-sys/raylib describe --tags → starts with 6.0.cargo build -p raylib-sys → succeeds (Windows baseline).| Path | Responsibility | Task |
|---|---|---|
raylib-sys/tests/symbol_presence.rs | Compile/link-time guard that key new 6.0 symbols are bound (create) | 1 |
raylib-sys/tests/layout_compat.rs | Static assertions that the 7 hand-defined types match raylib 6.0 layout (create) | 2 |
raylib-sys/build.rs | features_from_env: reconcile SUPPORT_* defines with 6.0 config.h (modify) | 3 |
raylib-sys/Cargo.toml | Feature list reconciled with 6.0 (modify) | 3 |
raylib/Cargo.toml | Mirror the reconciled feature list (modify) | 3 |
docs/superpowers/notes/ws1-config-reconcile.md | Record of added/removed/renamed flags + rationale (create) | 3 |
raylib-sys/build.rs (+ maybe Cargo.toml) | Incorporate PR #279 (links/include path export) with attribution (modify) | 4 |
.github/workflows/baseline.yml | Expand build-sys to ubuntu+macOS+windows matrix (modify) | 5 |
Files: Create raylib-sys/tests/symbol_presence.rs
Rationale: bindgen has no allowlist, so 6.0's new symbols (+40 file, +30 text fns, the redesigned model types) are generated automatically. The risk is a future bindgen-config change silently dropping them. A test that merely references each symbol fails to compile/link if the symbol disappears — a cheap regression guard. We reference, never call (no window needed).
raylib-sys/tests/symbol_presence.rs://! Compile/link-time guard: these raylib 6.0 symbols must remain bound.
//! Referencing (not calling) each symbol fails the build if bindgen ever drops it.
#![allow(unused, clippy::no_effect)]
use raylib_sys::*;
#[test]
fn new_6_0_functions_are_bound() {
// Filesystem additions (6.0 folded ~40 fns into rcore)
let _ = GetFileLength as *const ();
let _ = MakeDirectory as *const ();
let _ = LoadDirectoryFiles as *const ();
// Model animation redesign
let _ = UpdateModelAnimation as *const ();
let _ = UnloadModelAnimations as *const ();
}
#[test]
fn new_6_0_types_exist() {
// Skeletal-animation redesign types
let _: Option<ModelSkeleton> = None;
// ModelAnimPose is `typedef Transform *ModelAnimPose` in 6.0
let _: Option<ModelAnimPose> = None;
}
Run: cargo test -p raylib-sys --test symbol_presence -- --list
Expected: lists new_6_0_functions_are_bound and new_6_0_types_exist (compiling + linking the test binary proves the symbols resolve). If any symbol name doesn't resolve, it's either renamed in 6.0 or genuinely absent — fix the reference to the real 6.0 name (check raylib-sys/raylib/src/raylib.h) rather than deleting the guard.
Run: cargo test -p raylib-sys --test symbol_presence
Expected: 2 passed (the bodies do nothing at runtime; this confirms the link).
git add raylib-sys/tests/symbol_presence.rs
git commit -m "$(printf 'test(ws1): guard new raylib 6.0 symbols are bound\n\nCompile/link-time references to new file, text, and model-animation\nsymbols so a future bindgen-config change cannot silently drop them.\n\nCo-Authored-By: Claude Opus 4.7 <[email protected]>')"
Files: Create raylib-sys/tests/layout_compat.rs
Rationale: build.rs blocklists 7 types from bindgen (Vector2/3/4, Matrix, Quaternion, Rectangle, Color) because they're hand-defined in raylib-sys/src/math.rs and color.rs. If 6.0 changed any of their layouts, our hand definitions would be silently wrong (UB across FFI). Static size/align assertions catch that. (These values are the documented raylib layout; they did not change in 6.0, but the test makes that a guarantee, not an assumption.)
raylib-sys/tests/layout_compat.rs://! Static guarantee that hand-defined (bindgen-blocklisted) types keep the exact
//! C layout raylib 6.0 expects. A mismatch here is undefined behavior across FFI.
use raylib_sys::{Color, Matrix, Quaternion, Rectangle, Vector2, Vector3, Vector4};
use std::mem::{align_of, size_of};
const _: () = assert!(size_of::<Vector2>() == 8 && align_of::<Vector2>() == 4);
const _: () = assert!(size_of::<Vector3>() == 12 && align_of::<Vector3>() == 4);
const _: () = assert!(size_of::<Vector4>() == 16 && align_of::<Vector4>() == 4);
const _: () = assert!(size_of::<Quaternion>() == 16 && align_of::<Quaternion>() == 4);
const _: () = assert!(size_of::<Matrix>() == 64 && align_of::<Matrix>() == 4);
const _: () = assert!(size_of::<Rectangle>() == 16 && align_of::<Rectangle>() == 4);
const _: () = assert!(size_of::<Color>() == 4 && align_of::<Color>() == 1);
#[test]
fn layouts_match_raylib_6_0() {
// The const _ assertions above run at compile time; this test documents intent
// and gives a named target. If raylib 6.0 ever changes a layout, the build fails above.
}
Run: cargo test -p raylib-sys --test layout_compat
Expected: PASS. If a const _ assertion fails, the COMPILE fails with the failing assert — that means raylib 6.0 changed a layout and raylib-sys/src/math.rs/color.rs must be updated to match (do that, then re-run). Cross-check the field set against raylib-sys/raylib/src/raylib.h.
git add raylib-sys/tests/layout_compat.rs
git commit -m "$(printf 'test(ws1): assert hand-defined FFI types match raylib 6.0 layout\n\nStatic size/align checks for the 7 bindgen-blocklisted types (Vector*/\nMatrix/Quaternion/Rectangle/Color). A layout drift is FFI UB; this makes\nit a build error.\n\nCo-Authored-By: Claude Opus 4.7 <[email protected]>')"
config.hFiles: Modify raylib-sys/build.rs (features_from_env), raylib-sys/Cargo.toml, raylib/Cargo.toml; create docs/superpowers/notes/ws1-config-reconcile.md
Rationale (spec: "config.h flag changes reflected in build.rs"): raylib 6.0 redesigned config.h (now 55 SUPPORT_* flags). Our features_from_env in build.rs maps each Cargo SUPPORT_* feature to a cmake define, and both raylib-sys/Cargo.toml and raylib/Cargo.toml list these features. Any flag 6.0 added/removed/renamed must be reflected, or we silently pass dead defines or miss real ones.
Run:
grep -oE "SUPPORT_[A-Z0-9_]+" raylib-sys/raylib/src/config.h | sort -u > /tmp/raylib60-flags.txt
wc -l /tmp/raylib60-flags.txt # ~55
(On Windows write to a writable temp path instead of /tmp.) Also skim raylib-sys/raylib/src/config.h for any NEW flags grouped by module, and check the CMake side: raylib-sys/raylib/cmake/ / CMakeLists.txt for options exposed under CUSTOMIZE_BUILD.
Run:
grep -oE 'SUPPORT_[A-Z0-9_]+' raylib-sys/build.rs | sort -u > /tmp/ours-buildrs.txt
grep -oE 'SUPPORT_[A-Z0-9_]+' raylib-sys/Cargo.toml | sort -u > /tmp/ours-sys.txt
diff /tmp/raylib60-flags.txt /tmp/ours-buildrs.txt
The diff shows: lines only in raylib 6.0 (we're MISSING — candidate to add) and lines only in ours (raylib 6.0 DROPPED or renamed — candidate to remove/deprecate).
Step 3: Apply reconciliation rules. For each difference:
raylib-sys/Cargo.toml [features], a mirrored entry in raylib/Cargo.toml (forwarding raylib-sys/<FLAG>), and a cmake.define("<FLAG>", bstr(cfg!(feature = "<FLAG>"))); line in features_from_env. Match the existing surrounding style exactly.cmake.define line and the [features] entries from BOTH Cargo.tomls. (These are build-time toggles, not public API, so removal is safe; note it.)raylib-sys/Cargo.toml and raylib/Cargo.toml feature lists in sync (raylib's forward to raylib-sys/*), as they are today. Step 4: Record the reconciliation in docs/superpowers/notes/ws1-config-reconcile.md:
# WS1 — config.h feature-flag reconciliation (raylib 5.6-dev → 6.0)
## Added
- <FLAG> — <module/what it gates>
## Removed (gone in 6.0)
- <FLAG> — <why removed>
## Renamed
- <OLD> -> <NEW>
## Unchanged
- (the rest)
Run: cargo build -p raylib-sys
Expected: success. Then cargo build -p raylib-sys --no-default-features — expected: success (or a documented, intentional failure if a non-optional flag is required; note it).
Run:
diff <(grep -oE 'SUPPORT_[A-Z0-9_]+' raylib-sys/Cargo.toml | sort -u) \
<(grep -oE 'SUPPORT_[A-Z0-9_]+' raylib/Cargo.toml | sort -u)
Expected: no differences (every sys SUPPORT flag is mirrored in raylib).
git add raylib-sys/build.rs raylib-sys/Cargo.toml raylib/Cargo.toml docs/superpowers/notes/ws1-config-reconcile.md
git commit -m "$(printf 'feat(ws1)!: reconcile SUPPORT_* feature flags with raylib 6.0 config.h\n\nAdd new 6.0 flags, drop flags removed in 6.0, fix renames; keep raylib and\nraylib-sys manifests in sync. Reconciliation recorded in ws1-config-reconcile.md.\n\nCo-Authored-By: Claude Opus 4.7 <[email protected]>')"
Files: Modify raylib-sys/build.rs (and possibly raylib-sys/Cargo.toml)
Rationale (inventory.md, tagged merge → WS1): PR #279 (themkat, branch feature/links-include-var) adds a links setting to raylib-sys and exports the include path for downstream sys-crate composition. Small and correct; incorporate it now with author credit.
Run: gh pr view 279 --repo raylib-rs/raylib-rs and gh pr diff 279 --repo raylib-rs/raylib-rs
Read what it changes (expected: build.rs emits cargo:include=<path> / link metadata, possibly an env-driven extra-links setting).
Step 2: Apply the change, adapting to the current 6.0 build.rs structure. Keep it minimal and matching surrounding style. (Re-author rather than raw-cherry-pick if the branch is based on an old tree; preserve intent.)
Step 3: Verify the build still succeeds and the metadata is emitted
Run: cargo build -p raylib-sys -vv 2>&1 | grep -iE "cargo:include|cargo:rustc-link" | head
Expected: the new cargo:include=... (and/or link) line appears.
git add raylib-sys/build.rs raylib-sys/Cargo.toml
git commit -m "$(printf 'feat(ws1): export raylib-sys include path + links setting\n\nIncorporates PR #279 for downstream sys-crate composition.\n\nCo-authored-by: themkat <[email protected]>\nCo-Authored-By: Claude Opus 4.7 <[email protected]>')"
(Confirm themkat's exact git identity from gh pr view 279 --json author and use it in the Co-authored-by line so the credit is real.)
raylib-sys build matrixFiles: Modify .github/workflows/baseline.yml
Rationale (owner decision): pull a slice of WS6 forward so the fork's CI builds raylib-sys on all three desktop OSes, giving continuous cross-platform feedback during WS1+. Web stays in WS6.
build-sys job as an OS matrix. Replace the single build-sys job in .github/workflows/baseline.yml with: build-sys:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Linux build deps
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install --no-install-recommends -y cmake libglfw3-dev libasound2-dev libudev-dev
- name: Install macOS build deps
if: runner.os == 'macOS'
run: brew install cmake
# Windows runners ship cmake + MSVC; no extra deps needed.
- name: Build raylib-sys
run: cargo build -p raylib-sys
(Leave the fmt job unchanged.)
Step 2: Validate the YAML (Node or any available parser, as in WS0): confirm it parses and has fmt + build-sys jobs with a 3-entry matrix.os.
Step 2b: Smoke-test the Linux leg locally with act (if Docker is available). act (run via gh act if the gh extension is installed, else the act binary) executes the workflow in local Docker containers — this catches dependency/step mistakes in the ubuntu-latest legs without a push. It only emulates Linux, so it covers the fmt job and the build-sys (ubuntu-latest) leg; macOS/windows still need real runners (Task 6).
gh act -W .github/workflows/baseline.yml -j build-sys --matrix os:ubuntu-latest (or act -j build-sys). First run prompts for a runner image — pick the medium/catthehacker/ubuntu image.build-sys leg runs and builds raylib-sys (or surfaces a missing-dep error to fix in the workflow now).act is not installed, skip this step and rely on Task 6's real CI run — note that you skipped it.Step 3: Commit
git add .github/workflows/baseline.yml
git commit -m "$(printf 'ci(ws1): build raylib-sys on ubuntu+macos+windows matrix\n\nPulls a WS6 slice forward for continuous cross-platform feedback on the\nfork. Web/wasm stays in WS6.\n\nCo-Authored-By: Claude Opus 4.7 <[email protected]>')"
Files: none (CI orchestration); may loop back to build.rs/workflow if a platform fails.
Rationale: this is where "compiles clean on all desktop platforms" is actually proven. It is iterative (push → observe → fix → repeat) and controller-orchestrated rather than a single TDD subagent — CI feedback loops don't fit the implementer/review cadence. (Pre-validate the Linux leg with act per Task 5 Step 2b to cut cycles; only macOS/windows truly need this push.)
6.0-rc to the forkRun: git push -u fork 6.0-rc
Expected: branch created on Dacode45/ms-raylib-rs; push succeeds. (This is the first push — an outward action, already authorized by the owner on 2026-05-25.)
Run: gh run list --repo Dacode45/ms-raylib-rs --branch 6.0-rc --limit 1 then gh run watch <run-id> --repo Dacode45/ms-raylib-rs (or poll gh run view <run-id>).
Expected: fmt green; build-sys (ubuntu/macos/windows) either green or with specific per-OS errors.
Step 3: Fix per-OS failures until green. Likely culprits and fixes:
MAX_MATERIAL_MAPS = 12 override in raylib-sys/src/lib.rs:13 is macOS-only — confirm it still matches 6.0 (grep MAX_MATERIAL_MAPS raylib-sys/raylib/src/*.h). Missing frameworks/link flags surface here.build.rs platform arm), commit with a ci(ws1):/fix(ws1): message + co-author trailer, push, re-watch. Repeat until all three matrix legs are green.Step 4: Confirm green end-state
Run: gh run list --repo Dacode45/ms-raylib-rs --branch 6.0-rc --limit 1
Expected: latest run completed / success with all build-sys matrix legs + fmt passing.
symbol_presence.rs and layout_compat.rs exist, compile, and pass.SUPPORT_* flags reconciled with 6.0 config.h; raylib-sys and raylib manifests in sync; reconciliation documented; default + --no-default-features build states known/green.baseline.yml builds raylib-sys on ubuntu+macOS+windows.6.0-rc pushed to the fork; the 3-OS matrix + fmt are green on Actions.raylib crate was NOT modified (still WS3's job); commits grouped under (ws1).raylib/src/ — the 37 safe-crate errors are WS3, after the WS2 math decouple. Stated in every task.config.h diff and CI output (legitimately not knowable until run).GetFileLength, MakeDirectory, LoadDirectoryFiles, UpdateModelAnimation, UnloadModelAnimations, ModelSkeleton, ModelAnimPose) and documented layouts.