Repository navigation
feat(#461): production generate cutover — auto-detect + use backend.manifest.v2.zon - #473
Conversation
Make the real `generate` entry auto-detect a backend's `backend.manifest.v2.zon` in the resolved backend package and drive the manifest-v2 codegen path — without the caller passing `backend_manifest_name`. Closes the #472 P2 finding. - Add `manifest_v2.V2_MANIFEST_NAME` (canonical `backend.manifest.v2.zon`). - `root.zig`: probe ONCE via `detectV2ManifestName` (resolveBackendPackage + access), thread the result through the 4 sites: `requireManifestIfExternal` (so a v2-only external isn't rejected as manifest-less), `generateBuildZigZon`, `generateBuildZig`, and `stageBackendBuildHook` (stages the hook next to build.zig when the v2 manifest declares one). The tests-target path (#83) inherits this since the probe lives inside `generate`. - Graceful degradation: any probe I/O error (resolution/access/OOM) falls back to null (v1/enum), never crashes. Production NO-OP today: no external backend repo ships a v2 manifest yet, so every real `generate` returns null and output is byte-identical. Tests (drive the REAL `generate`, not the `generateBuildZig` unit helper): - acme_foo (v2-only) → generate auto-detects v2 and emits v2 build.zig (`b.dependency("acme_foo")` + generic `unifyCoreDiamond` walk), no opt-in. - new `backends/sokol_v1only` fixture → generate stays on v1/enum (no v2 markers, no hook import). - sokol (dual manifest) → auto-detected v2 output == v1/enum baseline (production-no-op guarantee). `zig build` + `zig build test` exit 0; existing goldens/byte-anchors unchanged. Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
🚧 Files skipped from review as they are similar to previous changes (2)
📝 WalkthroughWalkthroughAdds v2 manifest detection in generation, threads the detected manifest name through validation, build output, and template selection, and adds sokol fixture/template files plus tests for v1-only and v2 cutover behavior. ChangesManifest v2 cutover
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Code Review
This pull request implements the manifest-v2 production cutover by introducing auto-detection of the backend.manifest.v2.zon file within the resolved backend package. The generate entry point now automatically probes for this v2 manifest, threading its presence through downstream codegen steps while maintaining graceful fallback to the legacy v1/enum path if absent. To support this, a v1-only test fixture was added, along with corresponding integration tests and helper functions to verify the auto-detection behavior and ensure backward compatibility. There are no review comments to address, so I have no feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/root.zig`:
- Around line 464-466: Thread the detected manifest name into provider-contract
validation so v2-only backends are validated against the correct manifest.
Update the call site in root.zig to pass backend_manifest_name into
validateProviderContracts, and adjust validateProviderContracts to load the v2
manifest when that name is present instead of always reading the legacy provider
manifest path. Make sure the id and capabilities checks run against the manifest
chosen by detectV2ManifestName.
- Around line 897-904: Thread v2 manifest detection through main-template
generation: `generate` currently stages v2 hooks but `loadBackendTemplate` still
re-checks manifests with `null` and falls back to the legacy gate, so v2-only
backends can fail later when building `main.zig`. Pass `backend_manifest_name`
through `loadBackendTemplate` and the loop-style/template selection path, and
use it to resolve the v2 `.platforms.<platform>.entry` when present instead of
relying only on the legacy manifest check.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 82a24671-31fa-44f5-a91d-d25634af4fe0
📒 Files selected for processing (7)
backends/sokol_v1only/backend.manifest.zonbackends/sokol_v1only/build_fragments/backend_dep.txtbackends/sokol_v1only/build_fragments/link.txtsrc/codegen/manifest_v2.zigsrc/root.zigtest/build_zig_tests.zigtest/helpers.zig
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 991eeba0b6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const backend_manifest_name = detectV2ManifestName(allocator, cfg, game_dir); | ||
| try manifest_splice.requireManifestIfExternal(allocator, cfg, game_dir, backend_manifest_name); |
There was a problem hiding this comment.
Thread v2 manifests into executable template generation
When generate is called for a non-test target with a v2-only external backend, this detected name is only passed to the build.zig/zon and hook paths; the later loadBackendTemplate call still probes requireManifestIfExternal(..., null) and manifestPathEnabled(..., null), so it looks for the legacy backend.manifest.zon and fails with ExternalBackendNeedsManifest before main.zig can be emitted. The added tests drive generate with is_tests_target = true, which skips that executable-only path, so the real cutover still breaks for the v2-only backend shape this change is enabling.
Useful? React with 👍 / 👎.
| // Passing the detected name to `requireManifestIfExternal` is load-bearing: a | ||
| // v2-ONLY external backend (no legacy `backend.manifest.zon`) must not be | ||
| // rejected as manifest-less (the requirement keys off THIS name). | ||
| const backend_manifest_name = detectV2ManifestName(allocator, cfg, game_dir); |
There was a problem hiding this comment.
Validate provider contracts from the selected manifest
When a backend ships both manifests during migration, this switches build generation to backend.manifest.v2.zon, but the following validateProviderContracts call still reads only the legacy backend.manifest.zon. If the v2 manifest corrects or tightens .id/.capabilities while the v1 file is retained for older assemblers, generate can reject, warn, or accept based on stale v1 metadata before the v2 validation in generateBuildZig runs; thread the detected manifest into the provider-contract check or skip the legacy check once v2 is selected.
Useful? React with 👍 / 👎.
| // manifest-v2 cutover: the auto-detected v2 manifest name (null → v1/enum, | ||
| // byte-unchanged). When a v2 manifest is present this routes the | ||
| // backend-dep + link sections to the v2 codegen (`manifest_v2_splice`). | ||
| .backend_manifest_name = backend_manifest_name, |
There was a problem hiding this comment.
Keep tests target on the null backend
When this path is reached through generateTestsTarget, is_tests_target is true but the original backend_package can still be set; passing the auto-detected v2 manifest here makes the tests build use the user's external backend instead of the documented .null backend. For a v2 third-party backend with native deps or no desktop cell, zig build test will now require that backend or fail, even though the tests target is meant to be backend-agnostic.
Useful? React with 👍 / 👎.
…seams (PR #473) PR #473 review: `generate` auto-detects a `backend.manifest.v2.zon` and threads `backend_manifest_name` through build-file emission, but two downstream sites still ignored it. A v2-ONLY backend (no legacy `backend.manifest.zon`) therefore passed build.zig emission and then either failed main.zig template loading or had its identity/capability contract silently skipped. Finding 1 (main-template loading, `loadBackendTemplate`): now takes `backend_manifest_name`; when a v2 manifest is detected it resolves the entry-point template from `.platforms[<platform>].entry` and keys `requireManifestIfExternal` off that same name (so a v2-only external is not rejected as manifest-less). The per-platform run-loop style is likewise read from `.platforms[<platform>].loop_style`. v1/enum path unchanged (name null → falls through to the existing v1 splice / enum mappings). Finding 2 (provider-contract validation, `validateProviderContracts`): now takes `backend_manifest_name`; when a v2 manifest is detected it runs the identity + capability-gate checks against the v2 `.id`/`.capabilities` instead of the (absent) legacy provider manifest. Shared body factored into `validateProviderContractsInner` so v1 and v2 run the same negotiation. Both seams load the detected v2 manifest in place (matching the existing v1 style, which loads per-site) and degrade gracefully (probe/parse failure or a file that parses as v1 → the v1/enum path). Tests: strengthened the real-`generate` cutover coverage with a capability mismatch caught through the production entry point, plus direct seam tests proving `loadBackendTemplate` + `validateProviderContracts` honor a passed `backend_manifest_name` (v2-only sokol variant for the template, acme_foo for the contract) — each with a null-name counter-test proving the detected name is load-bearing. Added `backends/sokol_v2only/templates/desktop.txt` so the v2-only fixture can be driven through template loading. Production byte-unchanged (no external repo ships v2 → probe null → legacy behavior). Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
test/build_zig_tests.zig (1)
713-736: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPlease validate the v2-only desktop
main.zigpath too.These additions cover
build.ziggeneration and template lookup, but the newbackends/sokol_v2only/templates/desktop.txtis never parsed or compile-checked.loadBackendTemplate(...).len > 0would still pass with a broken placeholder or callback signature. Add a fixture that generatesmain.zigforsokol_v2onlyand at least AST-parses it.Also applies to: 797-810
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/build_zig_tests.zig` around lines 713 - 736, Add a new test around the v2-only desktop main.zig generation path to cover the new sokol_v2only template, since the current coverage only validates build.zig and template presence. Use the existing generate/loadBackendTemplate flow with the sokol_v2only fixture to generate main.zig, then AST-parse the rendered output to ensure the desktop template in desktop.txt is actually syntactically valid. Keep the test aligned with the existing generate and generateAndReadBuildZig helpers so it exercises the same backend selection path and catches broken placeholders or callback signatures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/root.zig`:
- Around line 1051-1066: The loop-style resolution logic in the
`backend_manifest_name` handling skips legacy `.v1` fallback because the `else
if` prevents `manifest_splice.manifestPathEnabled(...)` from running after
`loadNamedManifest()` returns a v1 manifest. Update the control flow around
`loadNamedManifest`, the `.v1` switch arm, and the
`manifest_splice.manifestPathEnabled` check so legacy handling runs in a
separate pass unless a real v2 entry in `manifest_v2_splice.platformEntry`
already set `main_zig.main_template.loop_style_override`.
---
Nitpick comments:
In `@test/build_zig_tests.zig`:
- Around line 713-736: Add a new test around the v2-only desktop main.zig
generation path to cover the new sokol_v2only template, since the current
coverage only validates build.zig and template presence. Use the existing
generate/loadBackendTemplate flow with the sokol_v2only fixture to generate
main.zig, then AST-parse the rendered output to ensure the desktop template in
desktop.txt is actually syntactically valid. Keep the test aligned with the
existing generate and generateAndReadBuildZig helpers so it exercises the same
backend selection path and catches broken placeholders or callback signatures.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: e2b67e87-645a-4c4b-bcb8-5a14a0f68ebc
📒 Files selected for processing (3)
backends/sokol_v2only/templates/desktop.txtsrc/root.zigtest/build_zig_tests.zig
Major) The loop_style resolution chained the legacy `manifestPathEnabled` branch as an `else if (backend_manifest_name == null)`. When a detected named manifest (`backend.manifest.v2.zon`) parsed as v1 (`manifest_version <= 1`), the v2 arm no-oped AND the legacy `else if` was skipped because the name was non-null — silently leaving `loop_style_override` unset and dropping that backend's loop_style. Extract the resolution into `resolveLoopStyleOverride` and restructure so the legacy path is a SECOND guarded pass (`!v2_resolved`), not an else-if: it runs whenever a REAL v2 manifest (union tag .v2) did NOT handle it — name null, a named file that parsed as v1, or a swallowed v2 load error. Mirrors the .v2-returns / .v1-falls-through shape loadBackendTemplate + validateProviderContracts already use correctly (verified; neither had the bug, since their v2 arms return early so the legacy code after runs unconditionally). Production byte-unchanged (name is null in production today, so the legacy pass runs exactly as before). Tests: add three resolveLoopStyleOverride tests to MANIFEST_V2_CUTOVER_SEAMS — (1) the regression lock: a v1-by-name file (sokol_v1only) still resolves its legacy .callback loop_style instead of null; (2) a v2-only backend resolves from its per-platform matrix despite shipping no legacy manifest; (3) the same bgfx v2 manifest yields desktop=.loop, android=.callback. Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/root.zig`:
- Around line 502-505: The fallback path in root.zig drops a detected `.v1`
manifest when `backend.manifest.v2.zon` parses as v1, so preserve that parsed
manifest instead of reloading only the canonical legacy file. Update the
`v2_resolved` fallback flow around `manifest_splice.manifestPathEnabled` and the
later load path to carry the detected `name`/parsed v1 data through the legacy
gate, ensuring `loop_style` and template fields survive even when no
`backend.manifest.zon` sibling exists.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: c5c0338e-7462-4d22-bd82-5de978b33fc7
📒 Files selected for processing (2)
src/root.zigtest/build_zig_tests.zig
🚧 Files skipped from review as they are similar to previous changes (1)
- test/build_zig_tests.zig
…#473 Major) `detectV2ManifestName` keys off file EXISTENCE, so a backend shipping ONLY `backend.manifest.v2.zon` whose CONTENT is v1 (manifest_version 1/omitted) is threaded as the detected name. Both fallback seams then retried the legacy pass with the canonical (null) name, probing the ABSENT `backend.manifest.zon` — the loop_style override dropped to null and the template fell to the enum path (reading the closed `cfg.backend` for a backend with no tag). Fix both sites to resolve from the file actually found: - resolveLoopStyleOverride (~505): the `.v1` arm now resolves loop_style straight from the parsed v1 manifest and marks it handled, so the canonical-name pass is suppressed. Renamed `v2_resolved` -> `handled`. - loadBackendTemplate (~1258): the `.v1` arm now resolves `main_loop_template` from the parsed v1 manifest and reads that template, instead of freeing and falling through to the canonical-name legacy pass. Order is now (1) `.v2` per-platform matrix; (2) detected name parsed as `.v1` -> resolve from THAT file; (3) name null / load error -> canonical `backend.manifest.zon`. Production is byte-unchanged (name is null -> straight to canonical, as before). Fixture: `backends/sokol_v1inv2` ships ONLY `backend.manifest.v2.zon` with v1 content (loop_style = .loop, no canonical sibling). Two seam tests assert the loop_style + template resolve from that file (not dropped to null, not erroring on a missing canonical). Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9ea2d157b3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| defer parsed.free(allocator); | ||
| return validateProviderContractsInner(allocator, cfg, m.id, m.capabilities); | ||
| }, | ||
| .v1 => parsed.free(allocator), // not actually a v2 manifest — fall through |
There was a problem hiding this comment.
Keep provider validation on detected v1 manifests
When backend_manifest_name points at a detected file that parses as .v1, this branch discards the parsed manifest and falls back to loadProviderManifest, which always probes the canonical backend.manifest.zon. Fresh evidence in this patch is the new v1-in-v2 path (sokol_v1inv2) that supports a backend shipping only backend.manifest.v2.zon; in that scenario the canonical file is absent, so manifest_id becomes null and declared is empty, causing capability enforcement to downgrade to warnings and skipping validation of the selected manifest's id/capabilities. Validate against the parsed v1 manifest here, matching the selected-manifest handling added for templates and loop style.
Useful? React with 👍 / 👎.
| return error.TemplateNotFound; | ||
| }; | ||
| }, | ||
| .v1 => |m| { |
There was a problem hiding this comment.
Gate v1-by-name templates to desktop
When a detected backend.manifest.v2.zon parses as .v1 on a non-desktop target, this arm still reads the v1 main_loop_template. The build generator's v1 handling is desktop-only and falls back to enum wiring off desktop, so an enum-backed migration package on Android/iOS/WASM can get enum build wiring but a desktop manifest template/loop style for main.zig; for bgfx Android the top-level .loop style is also wrong. Restrict the named-v1 path to desktop, or fall through/error for non-desktop targets.
Useful? React with 👍 / 👎.
| const tmpl_path = try std.fs.path.join(allocator, &.{ backend_path, entry.entry }); | ||
| defer allocator.free(tmpl_path); | ||
| return std.Io.Dir.cwd().readFileAlloc(config.globalIo(), tmpl_path, allocator, .limited(64 * 1024)) catch |err| { |
There was a problem hiding this comment.
Keep sokol wasm on an existing template
When a v2 manifest is auto-selected for a sokol wasm target, this now reads the manifest's .platforms.wasm.entry verbatim. The checked in sokol v2 manifest points that entry at templates/wasm.txt, but the package only ships templates/desktop.txt and templates/mobile.txt; the old enum path deliberately mapped sokol wasm to desktop.txt. As soon as a sokol package with this v2 manifest is used for wasm, generate will fail with TemplateNotFound before emitting main.zig, so either keep the wasm entry on desktop.txt or add the template before cutover.
Useful? React with 👍 / 👎.
…tests target The prior fix (be826cd) skipped only the ROOT-level `validateProviderContracts` capability requirement for the tests target. But `build_files.generateBuildZig` runs its OWN v2 capability validation (added in the #472 open-config PR), and the tests target calls `generateBuildZig` directly via `generateTestsTarget`. So the forced-null (`.headless`-only null-v2) tests harness still hard-failed `UnsupportedCapability` for GUI/gamepad projects — the #474 examples-integration gamepad example's tests-target generate. Guard `generateBuildZig`'s v2 capability REQUIREMENT check with `if (!opts.is_tests_target)`, consistent with the root-level skip. The provider IDENTITY check stays ON for the tests target (cheap + still valid); only the capability requirement is skipped. The real exe target (`is_tests_target = false`) is unchanged. `is_tests_target` was already threaded from the tests-target generate call site (root.zig:1006-1007). Adds two DIRECT `generateBuildZig` tests (via `h.genNullV2BuildZig`): a raw_backend (imgui) GUI project generating build.zig against forced-null (null-v2) does NOT error with `is_tests_target = true`, and STILL errors `UnsupportedCapability` with `is_tests_target = false`. This mirrors the CI scenario the root-level `validateProviderContracts` tests could not reach. Also re-points the "#473 finding 2" test to drive the REAL exe target (`is_tests_target = false`): it was passing only because `generateBuildZig`'s second gate caught the mismatch on the tests-target path (`generateAndReadBuildZig` forces `is_tests_target = true`), which this fix now correctly skips. The finding-2 catch is a resolve-time `validateProviderContracts` concern, so the exe path errors before any engine-template work. Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
…2.0) (#474) * feat(#461): bump null pin to 0.2.0 — flip null to v2 in production labelle-null 0.2.0 ships backend.manifest.v2.zon. With the generate cutover (#473) live, bumping builtinProvider(.null) means production `generate` now fetches null 0.2.0, auto-detects its v2 manifest, and builds the null backend via the declarative v2 build graph. First real production flip of the epic. null is the safest first backend (headless, desktop-only, pure-declarative, hookless). Validated by the null headless examples-integration CI (build+run on v2). Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw * fix(#83): skip capability gate for the tests-target forced-null The tests target (#83) force-substitutes `cfg.backend = .null` as a headless test harness while keeping the rest of the project config (e.g. `resolved_gui = imgui`), so `requiredCapabilities(cfg)` still derives the REAL backend's needs (`.raw_gui_adapter`, …). Now that null ships a v2 manifest declaring only `.headless`, the opted-in capability gate hard-failed `zig build test` (`UnsupportedCapability`) for every GUI/gamepad project — surfaced by the null→v2 flip in #474 CI. Thread `is_tests_target` into `validateProviderContracts` / `validateProviderContractsInner` and skip ONLY the capability requirement check for the forced-null tests harness. Identity + id-collision checks still run (cheap + valid). The real exe target is unaffected: a GUI project whose CHOSEN backend lacks `.raw_gui_adapter` still fails. Adds both-directions test: same imgui (raw_backend) project against the null-v2 fixture passes with `is_tests_target = true` and still errors `UnsupportedCapability` with `is_tests_target = false`. Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw * fix(#83): skip the SECOND capability gate (generateBuildZig) for the tests target The prior fix (be826cd) skipped only the ROOT-level `validateProviderContracts` capability requirement for the tests target. But `build_files.generateBuildZig` runs its OWN v2 capability validation (added in the #472 open-config PR), and the tests target calls `generateBuildZig` directly via `generateTestsTarget`. So the forced-null (`.headless`-only null-v2) tests harness still hard-failed `UnsupportedCapability` for GUI/gamepad projects — the #474 examples-integration gamepad example's tests-target generate. Guard `generateBuildZig`'s v2 capability REQUIREMENT check with `if (!opts.is_tests_target)`, consistent with the root-level skip. The provider IDENTITY check stays ON for the tests target (cheap + still valid); only the capability requirement is skipped. The real exe target (`is_tests_target = false`) is unchanged. `is_tests_target` was already threaded from the tests-target generate call site (root.zig:1006-1007). Adds two DIRECT `generateBuildZig` tests (via `h.genNullV2BuildZig`): a raw_backend (imgui) GUI project generating build.zig against forced-null (null-v2) does NOT error with `is_tests_target = true`, and STILL errors `UnsupportedCapability` with `is_tests_target = false`. This mirrors the CI scenario the root-level `validateProviderContracts` tests could not reach. Also re-points the "#473 finding 2" test to drive the REAL exe target (`is_tests_target = false`): it was passing only because `generateBuildZig`'s second gate caught the mismatch on the tests-target path (`generateAndReadBuildZig` forces `is_tests_target = true`), which this fix now correctly skips. The finding-2 catch is a resolve-time `validateProviderContracts` concern, so the exe path errors before any engine-template work. Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
…releases) (#476) wgpu 0.2.0, sdl 0.2.0, bgfx 0.3.0 each ship backend.manifest.v2.zon (+ bgfx's android hook). With generate auto-detect (#473) + the tests-target-null capability fix (#474), these backends now build via the declarative v2 build graph in production. Desktop paths fully CI-validated; bgfx-android cross-build validated by the bgfx-android-build job (on-device render unchanged from v1 — same source). wgpu/sdl are desktop-only, hookless, golden-reviewed. Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
The production
generatecutover for the manifest-v2 epic (#461). Realgeneratenow auto-detects a backend'sbackend.manifest.v2.zonand drives the v2 codegen path — no caller-passedbackend_manifest_nameneeded. Closes the #472 P2 finding. Production no-op today (no external repo ships a v2 manifest yet; the in-treebackends/*_v2/are test fixtures) — the next step is shipping v2 to the external repos one at a time.Mechanism
manifest_v2.V2_MANIFEST_NAME = "backend.manifest.v2.zon".detectV2ManifestName(allocator, cfg, project_dir)inroot.zig: resolves the backend package (resolveBackendPackage),accesses<pkg>/backend.manifest.v2.zon, returns the name if present else null. Called once at the top ofgenerate.requireManifestIfExternalpreflight (so a v2-only external backend isn't rejected as manifest-less),generateBuildZigZon,generateBuildZig, andstageBackendBuildHook(only when non-null → the generated@import("backend_build_hook.zig")resolves). Tests-target path (Backend-agnostic test target: .labelle/tests/ via null backend #83) covered automatically (it callsgenerate).catch return null) falls back to v1/enum; real external misconfig still surfaced byrequireManifestIfExternal.Tests (drive the REAL
generate, not the unit helper)GenerateOptionshas nobackend_manifest_namefield, so a v2 build.zig can only come fromgenerate's own probe:b.dependency("acme_foo")+ genericunifyCoreDiamond+ valid AST (wasExternalBackendNeedsManifestbefore).backends/sokol_v1only/fixture → v1 splice output (unifyGfxSubpackageCore, nounifyCoreDiamond/hook import).Production byte-unchanged
No external repo ships v2, so the probe returns null on every real
generate; examples-integration CI (fetches the real v1 backends) unchanged. All prior unit-helper tests, the sokol-desktop byte anchor, and the android/ios/wasm/wgpu/null goldens stay green.zig build test+zig buildexit 0.Ref #461, #453.
https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
Summary by CodeRabbit