Repository navigation
ci: build the bgfx examples from their pins, and run the shader pipeline e2e (#732, #735) - #745
Conversation
…ine e2e (#732, #735) Two coverage gaps in one workflow, grouped because they rewire the same lanes of `.github/workflows/ci.yml` and would conflict as separate PRs. ── #732: the bgfx examples were generated, never built ────────────────── `generate` proves the manifest parsed. It does not compile a line of backend source, so every defect that lives in a DEPENDENCY — an exhaustive switch, a moved contract type, a raised floor — passed. That is how core v1.32.0's `MaterialEffect.pixel_water` reached main: `material-demo` and `pack-city` both pinned a bgfx with no arm for it and CI said nothing. Only `sprite-runner` (sokol) was built, so the signal read "one backend broken" instead of "three examples broken, two untested" (#731). The same lane also FABRICATES breaks: it clones core/engine/gfx/null at `main` as siblings, and the assembler's auto-discovery builds those instead of the pins, so mid-release-train the four mains disagree and an unrelated PR goes red (#730, `backend_contract has no member named 'PixelWaterRgba'`; a re-run of the identical commit passed). * New job `bgfx examples (PINNED resolution)` — ubuntu, scratch LABELLE_HOME, NO runtime sibling checkouts — GENERATES AND BUILDS both bgfx examples against their declared `project.labelle` pins. It refuses to start if a runtime sibling exists, asserts each pinned version was fetched into the scratch home, and greps the install/generate logs for the assembler's own "using LOCAL sources" / "built from LOCAL sources" warnings — a mechanism assertion, not a value one. * The sibling lane is RENAMED "Examples integration (siblings @ main — cross-repo, may be mid-train)" and prints a banner of every sibling SHA/ref/describe before anything builds, so a mid-train red is recognised at a glance instead of debugged from scratch. Linux only (one platform catches a compile break in backend source), and on every PR rather than path-gated: the v1.32.0 break arrived from a dependency's release with no file in this repo changed, so a path filter would have skipped the job exactly when the break landed. Proof, on a mirrored checkout outside the toolkit tree with a scratch LABELLE_HOME (so pins really resolve from the registry): material-demo @ its own pins (core 1.32.0 / bgfx 0.20.0) install: fetching core 1.32.0, bgfx 0.20.0 (remote) generate: ok zig build -> exit 0, zig-out/bin/material_demo (36s) pack-city @ its own pins -> zig build exit 0 (25s) material-demo with bgfx re-pinned to the known-bad 0.13.1 (the #731 break) $ labelle-assembler generate -> exit 0 <-- old job: GREEN $ zig build -> exit 1 <-- new job: RED labelle-bgfx/src/gfx/texture.zig:469:12: error: switch must handle all possibilities labelle-core/src/backend_contract.zig:308:5: note: unhandled enumeration value: 'pixel_water' And the local-sources assertion, verified against a real sibling checkout: install: "core: using LOCAL sources from '…' — the pinned 1.32.0 will NOT be built (#685)" generate: "labelle: core is built from LOCAL sources, not the pinned 1.32.0 — source '…' (rev …)" ── #735: nothing ran the shader BUILD pipeline ────────────────────────── `test/material_pipeline_e2e.py` is the only test covering the PRODUCER side of game-owned materials (descriptor -> shaderc -> packaged variants -> a material that validates against core's contract). It was opt-in behind three `-D` flags because CI had no shaderc, so it never ran. Everything else covers CONSUMERS of already-compiled shaders, and the fixtures are pre-compiled — structurally unable to notice a pipeline regression. * New job `Material shader pipeline e2e (real shaderc)` provisions shaderc by REUSING the existing toolchain source rather than adding a second one: it reads the builtin bgfx provider version out of `src/config.zig`, reads that release's `zbgfx` archive URL out of labelle-bgfx's `build.zig.zon`, and builds `-Dwith_shaderc=true` from exactly that archive (cached on the URL; ~2.5 min cold locally). `--core` comes from the curated `core_version` default in `build.zig`, so there is no second pin to drift. * Nobody passes a flag by hand — the job derives all three. * Already independent of `examples/condenser`: the driver synthesizes its own three-material fixture under `.zig-cache/`. * `test/material_pipeline_e2e.py`: the missing-target assertion hard-coded `target requires 'spv'`, which is the LINUX answer. `material_build` picks the required variant per host (darwin -> mtl), so the test could not pass on macOS at all. It now follows the host. The driver already exercises five REJECTION paths beside the happy one, so it proves the pipeline fails closed: an include escaping the material dir, an uncompilable `.sc`, a descriptor missing the host's required variant, and two component-collision cases in the emitted comptime guard. Proof it catches a real producer-side regression that nothing else does — disabled include-tree tracking in `material_build.zig` so a changed `color.sh` no longer invalidates the shader cache: zig build test-materials -> exit 0 (448 tests, all green) zig build test-material-integration -> exit 1 AssertionError: shader fog (spv) (fog.spv.bin) success [not rebuilt] and, with the escaping-include rejection disabled: zig build test-material-integration -> exit 1 at build(False, "EscapingShaderInclude") Clean run on this branch: "PASS: 12 real shader variants, generated core descriptors, cached rebuild, include invalidation, escaping include rejection, compiler failure, missing target diagnostic". Closes #732 Closes #735 Co-authored-by: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XBGRLojiysEr5zy1U5mbP6
|
Warning Review limit reached
Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing. Next included review available in 28 minutes. View limit detailsLimit details: You’ve used all 5 included reviews currently available. Your 55 included PR review attempts over the past 7 days set your current allowance at 5 reviews per hour. Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. Review configuration: ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (2)
Comment |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Grouped, not split: both issues rewire the same lanes of
.github/workflows/ci.yml(the examples lane and the material lane), and the#735job reuses the pinned/scratch-LABELLE_HOMEscaffolding introduced for#732. As two PRs they would conflict on merge.#741is in its own PR (#743).#732 — CI generated the bgfx examples but never built them
The defect (both halves)
It hides breaks.
generateproves the manifest parsed. It compiles no backend source, so every defect that lives in a dependency — an exhaustive switch, a moved contract type, a raised floor — passes. That is how labelle-core v1.32.0'sMaterialEffect.pixel_waterreached main:material-demoandpack-cityboth pinned a bgfx with no arm for it, and CI said nothing. Onlysprite-runner(sokol) was built, so the signal read "one backend broken" instead of "three examples broken, two untested" (#731).It fabricates breaks. The same lane clones core/engine/gfx/null at
mainas siblings, and the assembler's monorepo auto-discovery builds those instead of each example's pins. Mid-release-train the four mains disagree for the minutes between merges, so an unrelated PR goes red: #730 died onbackend_contract has no member named 'PixelWaterRgba', and a re-run of the identical commit passed.The fix
New job:
bgfx examples (PINNED resolution)— ubuntu-latest, scratchLABELLE_HOME, and deliberately no runtime sibling checkouts. It generates and buildsmaterial-demoandpack-cityagainst their declaredproject.labellepins, and asserts the pins are what got used rather than trusting a green build:labelle-core/-engine/-gfx/-nullis present as a sibling (that absence is the test);The sibling lane is renamed to
Examples integration (siblings @ main — cross-repo, may be mid-train)and now prints, before anything builds, a banner of every sibling's SHA / ref /git describe, plus a note that these override the examples' pins and that the pinned job is the one that tests what the PR declares.Why Linux-only, and why every PR (not path-gated)
Path-gating on
examples/**/src/config.zig/ backend pins was the tempting option and it does not cover the defect that motivated the issue. The v1.32.0 break arrived from a dependency's release; no file in this repo changed. A path filter would have skipped the job on every PR while main stayed broken — a check that skips exactly when the break lands is the same class of defect being fixed here. One platform is enough to catch a compile break in backend source, so: Linux only, always. Cost is bounded by sharing one zig cache between the two examples — bgfx's C++ objects compile once andpack-citylinks against them.Proof I broke it and CI notices
Run on a mirrored checkout outside
prj/labelle-toolkit/with a scratchLABELLE_HOME, so pins genuinely resolve from the registry (the assembler'sfindRepoRootwalks up from the executable, so an in-tree run would have silently used siblings):material-demo@ its own pinsinstall: fetching core 1.32.0 … bgfx 0.20.0 (remote);zig build→ exit 0,zig-out/bin/material_demo(36s)pack-city@ its own pinszig build→ exit 0 (25s)Then re-pinned
material-demo's backend to the known-badbgfx 0.13.1— the exact #731 break:That is the defect, reproduced, and the new lane catching it.
#735 — provision shaderc so the material pipeline e2e actually runs
The defect
test/material_pipeline_e2e.pyis the only test covering the producer side of game-owned materials: descriptor → shaderc → packaged variants → a material that compiles and validates against core's contract. It was opt-in behind three-Dflags because CI had no shaderc, so it ran nowhere. Everything else in CI covers consumers of already-compiled shaders (goldens, the bgfx probe with checked-in.binfixtures) — and pre-compiled fixtures are structurally incapable of noticing a pipeline regression.The fix
New job:
Material shader pipeline e2e (real shaderc)(Linux). It provisions shaderc by reusing the existing toolchain source, not adding a second one — the chain is derived entirely from this repo's own pins:All three
-Dflags are derived by the job; nobody passes one by hand. The shaderc build is cached on the zbgfx URL (~2.5 min cold locally, 560s CPU; seconds on a hit). Each extraction was verified against the real files before committing.Already independent of
examples/condenser— the driver synthesizes its own three-material fixture (shared includes, an escaping include, a deliberately broken.sc) under.zig-cache/.It fails closed, not just open. The driver asserts five rejection paths beside the happy one: an include escaping the material dir (
EscapingShaderInclude), an uncompilable.sc, a descriptor missing the variant the host renderer requires, and two component-collision cases in the emitted comptime guard.Bug found and fixed while wiring it up: the missing-target assertion hard-coded
target requires 'spv', which is the Linux answer.material_build.createpicks the required variant per host (darwin -> mtl), so the test could not pass on macOS at all — it ran 12 real shader compiles and then died on the last line. It now follows the host.Proof I broke the pipeline and only this test noticed
Disabled include-tree tracking in
material_build.zig(trackTree(b, run, b.path("materials"), true)), so editing acolor.shno longer invalidates the shader cache — a producer-side regression with no consumer-side symptom:The entire in-repo suite stays green. Only the e2e catches it. That is the coverage gap, demonstrated.
Second probe — disabled the escaping-include rejection in
material_schema.zigso the pipeline stops failing closed:Clean run on this branch, with a shaderc built from the pinned zbgfx:
CI cost added (MEASURED on this PR's own run, not estimated)
bgfx examples (PINNED resolution)material-demo2m18s (cold bgfx C++ compile),pack-city30s — it links the same objects. No cache, deliberately: a cold registry-resolved build is the point.Material shader pipeline e2e (real shaderc)Both run in parallel with the existing jobs. Longest pre-existing job on this run was Windows at 9m38s, so wall-clock CI goes from ~9m38s to ~11m01s (+1m23s); ~15 runner-minutes added on a cold run, ~5 warm.
Verified green end to end on this PR (run 35376494395, all 8 jobs success):
Files touched
.github/workflows/ci.yml,test/material_pipeline_e2e.py. No overlap withsrc/material_pipeline.zigorsrc/init_cmd.zig(concurrent work in #739/#742). The two probes above touchedsrc/material_build.zigandsrc/material_schema.zigtemporarily and were reverted — neither file is modified by this PR.Closes #732
Closes #735
https://claude.ai/code/session_01XBGRLojiysEr5zy1U5mbP6
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.