feat(config): flip .backend = .null to the extracted package by default (#386 step 4) - #431
Conversation
…lt (#386 step 4) builtinProvider(.null) → labelle-null 0.1.0 — null is the third default-external backend (after bgfx #419, wgpu #428). Pure-Zig, zero deps, empty link fragment. Notable: generateTestsTarget (#83) forces .backend=.null for every project's `zig build test`, so that universal test target now fetches labelle-null (cached, pure-Zig — the no-system-libs property still holds). The null headless + plugin-controllers examples + the is_tests_target codegen now resolve external null. Test rework (same class as the bgfx/wgpu flips): backend_registry enum-shorthand (null joins bgfx/wgpu external), and the 4 build_zig `.backend=.null` tests (null-wires-modules, no-override, the two is_tests_target tests) now point at a local null pkg + project_dir so the manifest splice resolves. Inherited the cache fetch-path + externalUsesEnumPath from the earlier flips.
There was a problem hiding this comment.
Code Review
This pull request extracts the null backend into an external package (github.com/labelle-toolkit/labelle-null), moving it out of the bundled backends list. It updates the backend registry and configuration to treat the null backend as external, and updates corresponding tests in test/build_zig_tests.zig to resolve the null backend via local paths. 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.
📝 WalkthroughWalkthrough
ChangesReclassify .null backend as external
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 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.
🧹 Nitpick comments (1)
test/build_zig_tests.zig (1)
202-211: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep one regression that exercises the implicit
.nullprovider path.These updates all inject
backend_packageexplicitly, so they only cover the override branch. IfProjectConfig.builtinProvider(.null)drifts,generateBuildZig(... .backend = .null ...)would still pass here. A tiny assertion oneffectiveBackendPackage()for.nullwould pin the default-external contract without requiring a network fetch.Also applies to: 290-304, 392-399, 517-523
🤖 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 202 - 211, The current tests only cover the explicit backend_package override path, so add one regression that exercises the implicit .null provider path through ProjectConfig.builtinProvider(.null). Introduce a small assertion around effectiveBackendPackage() for .null to verify the default external package contract without relying on a network fetch, and keep the existing generateBuildZig(...) cases focused on the override branch.
🤖 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.
Nitpick comments:
In `@test/build_zig_tests.zig`:
- Around line 202-211: The current tests only cover the explicit backend_package
override path, so add one regression that exercises the implicit .null provider
path through ProjectConfig.builtinProvider(.null). Introduce a small assertion
around effectiveBackendPackage() for .null to verify the default external
package contract without relying on a network fetch, and keep the existing
generateBuildZig(...) cases focused on the override branch.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: d250280e-49e3-4ecf-bba8-cce48760505c
📒 Files selected for processing (3)
src/backend_registry.zigsrc/config.zigtest/build_zig_tests.zig
The flip surfaced this (CI: a raylib project's tests-target generate failed ExternalBackendNeedsManifest): generateTestsTarget (#83) forces .backend=.null for EVERY project's `zig build test`, but validateCache/ensureCache only considered the project's OWN backend — so external labelle-null was never reported missing or fetched for a non-null project. Account for the tests-target null in both (dedup when the project already IS null). No-op while null is bundled.
…nal tests The two external-backend validateCache tests now also see the tests-target's external null in the missing set (every project validates null post-flip). Assert the project backend specifically (local stub) + expect both fakebackend AND null for the remote case.
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/cache/resolve.zig (1)
334-336: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winCompare the full backend package identity here, not just
name.This dedupe suppresses the tests-target
labelle-nullfetch for any project backend whose effective package is also named"null"but points at a different repo/version/local path. In that casevalidateCache()can return no missing entries,ensureCache()exits early, andzig build testnever gets the real extracted null backend. Matchingrepo+version(or the wholePluginDep) would keepvalidateCachealigned withensureCache.Suggested fix
if (tests_target_cfg.effectiveBackendPackage()) |null_bp| { - const already = cfg.effectiveBackendPackage() != null and - std.mem.eql(u8, cfg.effectiveBackendPackage().?.name, null_bp.name); + const already = if (cfg.effectiveBackendPackage()) |bp| + std.mem.eql(u8, bp.name, null_bp.name) and + std.mem.eql(u8, bp.repo, null_bp.repo) and + std.mem.eql(u8, bp.version, null_bp.version) + else + false; if (!already and !try isPluginCached(allocator, null_bp)) { try missing.append(allocator, try std.fmt.allocPrint(allocator, "backend {s} {s}", .{ null_bp.name, null_bp.version })); } }🤖 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 `@src/cache/resolve.zig` around lines 334 - 336, The dedupe in the cache resolution logic is only comparing the effective backend package’s name against `null_bp.name`, which can incorrectly suppress the `labelle-null` fetch for different packages that happen to share the same name. Update the `resolve.zig` check around `cfg.effectiveBackendPackage()` and `isPluginCached()` to compare the full backend package identity instead of just `name`—for example by matching `repo` and `version` (or the complete `PluginDep` fields) so `validateCache()` and `ensureCache()` stay consistent and the real null backend is fetched when needed.
🤖 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.
Outside diff comments:
In `@src/cache/resolve.zig`:
- Around line 334-336: The dedupe in the cache resolution logic is only
comparing the effective backend package’s name against `null_bp.name`, which can
incorrectly suppress the `labelle-null` fetch for different packages that happen
to share the same name. Update the `resolve.zig` check around
`cfg.effectiveBackendPackage()` and `isPluginCached()` to compare the full
backend package identity instead of just `name`—for example by matching `repo`
and `version` (or the complete `PluginDep` fields) so `validateCache()` and
`ensureCache()` stay consistent and the real null backend is fetched when
needed.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 7a5027ad-17cf-4290-a56e-58489f0f3273
📒 Files selected for processing (1)
src/cache/resolve.zig
…hase 6c) (#432) null resolves out-of-tree now (`.backend = .null` → the labelle-null provider, flipped in #431), so the in-tree backends/null/ slot is dead weight. Delete it. THREE backends now fully out of the assembler bundle (bgfx + wgpu + null). The assembler ships no null backend, yet `.backend = .null` still works everywhere — incl. the universal `zig build test` tests target (#83), which fetches labelle-null (pure-Zig, cached; the no-system-libs property holds). - Remove backends/null/ (src/templates/manifest/fragments). - Tests: drop the 2 null-specific build_zig unit tests (no in-tree package to resolve — coverage moved to labelle-null's CI + the examples-integration null headless + plugin-controllers RUN steps, which build+run a project on the fetched external null) and the null case from the in-tree-template preview list. The two is_tests_target mechanism tests switch to the bundled raylib backend (the trim/lib-chaining behavior is backend-agnostic). - Docs: point the null references at the labelle-null package. examples/null + examples/plugin-controllers stay — they're headless/plugin feature tests that now exercise the external null end-to-end in CI.
null extraction step 4 — the flip.
builtinProvider(.null)→ labelle-null 0.1.0 (third default-external backend after bgfx/wgpu).Per the design call we made: null is the assembler's internal test/headless backend, and
generateTestsTarget(#83) forces it for every project'szig build test— so that universal test target now fetcheslabelle-null(cached + pure-Zig, so the no system libs / cross-compile toolchain property still holds). The null headless + plugin-controllers examples + the is_tests_target codegen now resolve external null; CI exercises those (build + run, asserting frame sequences).Test rework (same class as bgfx/wgpu): backend_registry enum-shorthand + the 4 build_zig
.backend=.nulltests (local null pkg + project_dir so the manifest splice resolves). labelle-null published + CI green (ubuntu+macOS build-test).zig build testgreen locally.Next: slim bundled
backends/null+ move its examples/CI to labelle-null.Summary by CodeRabbit
.nullbackend is treated consistently as an extracted external backend..nullsetups through the external-backend path (with corresponding null-backend wiring expectations)..nullbackend plugins are present..nullbehavior and override/trim behavior.