Skip to content

feat: absorb backends, ecs adapters, and gui plugins from labelle-cli - #7

Merged
apotema merged 10 commits into
mainfrom
feat/absorb-backends
Apr 13, 2026
Merged

apotema merged 10 commits into
mainfrom
feat/absorb-backends

Conversation

@apotema

@apotema apotema commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the assembler side of #5 (and labelle-toolkit/labelle-cli#147). All runtime content that the assembler wires into generated build.zig files — the five backends, the three ECS adapters, and the five GUI plugins — now lives in this repo instead of labelle-cli.

Paired with labelle-toolkit/labelle-cli#??? (feat/eject-backends) which removes the same content from there.

What moved

  • backends/: raylib, sokol, bgfx, sdl, wgpu (5 backends)
  • ecs/: zig-ecs, zflecs, mr-ecs (3 adapters)
  • gui/: clay, raylib-imgui, simple-raylib, simple-sokol, sokol-imgui (5 plugins)

Cache layout change

Introduces a new cache slot ~/.labelle/packages/assembler/<version>/ alongside the existing cli/<version>/ slot. During the staged migration, resolveBundledPackage falls back from the assembler slot to the cli slot so each stage could land independently. The final stage (#8) removes the dead cli-slot machinery entirely since nothing bundled lives there anymore.

The resolver takes both cli_version and assembler_version so local: path overrides can point each slot at a different sibling repo during dev.

Commits (staged for review)

  1. feat: absorb raylib backend — Stage 1 plumbing + first backend
  2. feat: absorb sokol backend — Stage 2
  3. refactor: silence assembler-slot probe — suppresses spurious warnings during migration
  4. feat: absorb bgfx backend — Stage 3
  5. feat: absorb sdl backend — Stage 4
  6. feat: absorb wgpu backend — Stage 5 (last backend)
  7. refactor: drop dead backend fallback — Stage 6 cleanup
  8. feat: absorb ecs adapters — Stage 7
  9. feat: absorb gui plugins + drop dead cli cache slot — Stage 8 (final)

Test plan

  • zig build test passes on this branch
  • cd ecs/zig-ecs && zig build test passes (CI step added)
  • End-to-end: generate examples/{raylib,sokol,bgfx,wgpu} in labelle-cli with LABELLE_ASSEMBLER pointing at this branch's binary, verify .labelle/deps/labelle-*/src/gfx.zig inodes trace back to backends/ in this repo
  • CI green on the remote

Closes #5.

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

apotema and others added 9 commits April 13, 2026 13:27
Stage 1 of extracting backends from labelle-cli (see
labelle-toolkit/labelle-cli#147 and #5). raylib moves here; the four
remaining backends stay in labelle-cli until their own stages.

Adds a new assembler cache slot at ~/.labelle/packages/assembler/{ver}/
alongside the existing cli/ slot. resolveBundledPackage tries the
assembler slot first and falls back to the cli slot for backends not
yet migrated, so the two halves of the migration compose.

The resolver takes both cli_version and assembler_version so that
local: path overrides can point each slot at a different sibling repo
during dev (e.g. labelle-cli vs labelle-assembler).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stage 2 of #147 / #5. Moves the sokol backend (desktop + mobile + wasm
templates, Android/iOS lifecycle glue, stb_image vendored sources,
standalone example) into the assembler alongside raylib.

No resolver changes — the Stage 1 plumbing handles this automatically.
The three remaining backends (bgfx, sdl, wgpu) still live in labelle-cli
and resolveBundledPackage falls back to the cli cache slot for them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
resolveAssemblerPackage routes local: versions through resolveLocalPath,
which logs "local path '...' does not exist" on misses. During the staged
backend migration every un-migrated backend (bgfx/sdl/wgpu today) triggers
that warning before the CLI-slot fallback succeeds, polluting the output.

Replace the probe with a silent helper that computes the candidate path
inline and only returns it when the directory exists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stage 3 of #147 / #5. Pure file move — the Stage 1 resolver handles
this automatically. Two backends remain in labelle-cli: sdl and wgpu.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stage 4 of #147 / #5. Pure file move. One backend remains in
labelle-cli: wgpu.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stage 5 of #147 / #5 — the last backend. labelle-cli/backends/ is now
empty. Cleanup of the dual-path fallback resolver and the CLI cache
population of backends/ follows in a separate stage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
With Stage 5 of the backend migration complete, all five backends live
in labelle-assembler/backends/. The CLI-slot fallback in
resolveBundledPackage and the probeBundledSlot helper that silenced it
are both dead for backends. Remove them — resolveBundledPackage now
just picks the right version key (assembler_version orelse cli_version)
and calls resolveAssemblerPackage directly.

Also drops the "backends" entry from populateCliCache and fetchCliPackages
so the CLI cache slot stops trying to symlink/copy a directory that no
longer exists in labelle-cli. ECS and GUI remain in the CLI slot until
their own stages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stage 7 of #147 / #5. Moves zig-ecs, zflecs, and mr-ecs out of
labelle-cli/ecs/ into this repo. Flips the two call sites (build_files
and deps_linker) from resolveCliPackage to resolveBundledPackage so
ECS picks up from the assembler cache slot like backends.

Adds the standalone zig-ecs adapter test step (cd ecs/zig-ecs &&
zig build test) to this repo's CI — it previously lived in
labelle-cli's CI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Stage 8 of #147 / #5 — the last content move.

Brings the five GUI plugins (clay, raylib-imgui, simple-raylib,
simple-sokol, sokol-imgui) into this repo. The raylib-imgui and
sokol-imgui build.zig files reach into the sokol/raylib backend src/;
their relative paths go from ../../../labelle-assembler/backends/...
back to the natural ../../backends/... since both now live under the
same repo root.

With gui moved, the labelle-cli companion directory has no content
left. Removes the entire dead cli-slot machinery from cache.zig:
resolveCliPackage, populateCliCache, fetchCliPackages, isCliCached,
and the validateCache check for cli packages. Also drops the matching
re-exports from root.zig.

ECS and GUI in resolveBundledPackage still accept cli_version as a
fallback key — kept for the case where cfg.assembler_version is null
(production ships the two versions in lockstep).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Apr 13, 2026 •

Copy link
Copy Markdown

PR Summary

Medium Risk
Large, mostly additive drop of multiple backend implementations and build templates plus new third-party deps; risk is mainly build/link portability regressions (SDL/bgfx native linking, shader binaries, and audio mixer edge cases).

Overview
Moves backend runtime content into this repo by adding new Zig packages for bgfx, raylib, SDL2, and sokol, each exposing gfx/input/audio/window modules and re-exporting any native artifacts needed for consumers to link.

Adds substantial new backend implementations and demo/example projects (notably a full bgfx renderer with embedded shaders and a software audio mixer, plus an SDL2 backend with SDL_mixer audio, texture management, and embedded font text rendering), along with backend-specific templates/*.txt used for generated desktop/WASM entrypoints.

Updates CI to run the additional ecs/zig-ecs adapter test suite (cd ecs/zig-ecs && zig build test).

Reviewed by Cursor Bugbot for commit aa69c1b. Bugbot is set up for automated code reviews on this repo. Configure here.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant updates to the backend and ECS adapter infrastructure, including the addition of new backends (bgfx, raylib, sdl, sokol, wgpu) and ECS adapters (mr-ecs, zig-ecs). The changes also include a migration from CLI-bundled packages to an assembler-based cache system. My review identified several critical issues: hardcoded paths in the SDL build file, platform-specific window handle logic in the bgfx backend, memory leaks in the sokol audio backend, inefficient view implementation in the zig-ecs adapter, and potential integer overflow vulnerabilities in the wgpu audio WAV parser.

Comment thread backends/sdl/build.zig
Comment on lines +7 to +8
const sdl_include = std.Build.LazyPath{ .cwd_relative = "/opt/homebrew/include" };
const sdl_lib = std.Build.LazyPath{ .cwd_relative = "/opt/homebrew/lib" };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The include and library paths for SDL2 are hardcoded to a specific macOS Homebrew location (/opt/homebrew/...). This will cause build failures on Linux, Windows, Intel-based Macs (which use /usr/local), or any system where Homebrew is installed in a non-standard location. Consider using b.dependency to manage SDL2 or at least providing a way to override these paths via build options.

init.resolution.height = @intCast(height);
init.resolution.reset = 0x00000080; // BGFX_RESET_VSYNC
init.platformData.ndt = null;
init.platformData.nwh = glfw.getCocoaWindow(win);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The native window handle is hardcoded to use glfw.getCocoaWindow(win), which is specific to macOS. This will prevent the code from compiling or running correctly on Windows or Linux. You should use a conditional check based on builtin.target.os.tag to retrieve the appropriate native handle for the target platform.

    init.platformData.nwh = switch (builtin.target.os.tag) {
        .macos => glfw.getCocoaWindow(win),
        .windows => glfw.getWin32Window(win),
        .linux => glfw.getX11Window(win),
        else => null,
    };

// of sample memory happens at shutdown.
sounds[id] = null;
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This line causes a memory leak. By setting the sound slot to null without freeing the underlying samples buffer, the reference to the allocated memory is lost. The deinit function (lines 81-86) only iterates over non-null slots, so it will not be able to reclaim this memory later. Even if a race condition exists with the audio thread, you must track these allocations (e.g., in a 'pending free' list) to ensure they are eventually deallocated. The same issue exists in unloadMusic at line 437.

Comment on lines +133 to +154
pub fn view(self: *Self, comptime includes: anytype, comptime excludes: anytype) View(includes, excludes) {
var result: std.ArrayListUnmanaged(Entity) = .{};

if (includes.len == 1 and excludes.len == 0) {
const basic = self.inner.basicView(includes[0]);
var iter = basic.entityIterator();
while (iter.next()) |internal| {
result.append(self.inner.allocator, toExternal(internal)) catch @panic("OOM");
}
} else {
var multi = self.inner.view(includes, excludes);
var iter = multi.entityIterator();
while (iter.next()) |internal| {
result.append(self.inner.allocator, toExternal(internal)) catch @panic("OOM");
}
}

return .{
.entities = result.toOwnedSlice(self.inner.allocator) catch @panic("OOM"),
.allocator = self.inner.allocator,
};
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The view implementation is extremely inefficient for a game engine. It performs a full O(N) scan of all entities and allocates a new ArrayList (materialized as a slice) every time it is called. In a typical ECS usage pattern, views are created every frame, leading to significant performance degradation and memory pressure. This should be refactored to wrap the underlying zig-ecs view iterators directly, avoiding allocations and leveraging the ECS's optimized query structures.

if (fmt_found and data_found) break;

// Advance to next chunk (chunks are 2-byte aligned)
pos = chunk_data_start + chunk_size;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Potential integer overflow and out-of-bounds logic in the WAV chunk parser. If a corrupted WAV file provides a very large chunk_size, the addition chunk_data_start + chunk_size can overflow usize (especially on 32-bit platforms). This could lead to an infinite loop or incorrect memory access in subsequent iterations. Validate that the chunk fits within the bytes_read bounds before advancing pos.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 530c2c4. Configure here.

init.resolution.height = @intCast(height);
init.resolution.reset = 0x00000080; // BGFX_RESET_VSYNC
init.platformData.ndt = null;
init.platformData.nwh = glfw.getCocoaWindow(win);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bgfx window hardcodes macOS-only native window handle

High Severity

glfw.getCocoaWindow(win) is unconditionally called to obtain the native window handle, but this function only exists on macOS. On Linux or Windows, a different GLFW function is needed (e.g., getX11Window, getWin32Window). The builtin import on line 3 is present but unused, suggesting platform-conditional logic was intended but never implemented. This makes the entire bgfx backend unusable on non-macOS platforms.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 530c2c4. Configure here.

sounds[id] = snd;
next_sound_id += 1;
return id;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raylib audio never recycles freed sound/music slots

Medium Severity

Unlike the bgfx and SDL audio backends which both implement findFreeSoundSlot()/findFreeMusicSlot() to scan for previously-freed null slots, the raylib loadSound and loadMusic only ever increment next_sound_id/next_music_id. After repeated load/unload cycles, IDs will be exhausted even though freed slots exist, causing all subsequent loads to return 0.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 530c2c4. Configure here.

Comment thread backends/sdl/build.zig
const optimize = b.standardOptimizeOption(.{});

const sdl_include = std.Build.LazyPath{ .cwd_relative = "/opt/homebrew/include" };
const sdl_lib = std.Build.LazyPath{ .cwd_relative = "/opt/homebrew/lib" };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SDL build hardcodes macOS Homebrew library paths

Medium Severity

The SDL include and library paths are hardcoded to /opt/homebrew/include and /opt/homebrew/lib, which are specific to Apple Silicon macOS with Homebrew. This breaks builds on Linux, Windows, and even Intel Macs where Homebrew installs to /usr/local. These paths are baked into the module definitions and propagated to all consumers of the SDL backend.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 530c2c4. Configure here.

apotema added a commit to labelle-toolkit/labelle-cli that referenced this pull request Apr 13, 2026
The docker-build and versions-integration jobs invoke the CLI end-to-end
with init + generate. With backends/ecs/gui moved to labelle-assembler,
the default assembler_version that `labelle init` writes (v0.2.0)
points to a released binary that pre-dates this refactor and still
looks for backends in labelle-cli — so the test project fails to
generate with "template not found".

Fix both jobs by checking out labelle-assembler as a sibling, building
it in-place, pointing LABELLE_ASSEMBLER at the fresh binary, and
passing --assembler-version="local:../../labelle-assembler" to
labelle init so the generated project.labelle resolves backends
against the sibling repo.

The checkout is temporarily pinned to the paired feat/absorb-backends
branch — revert to main once labelle-toolkit/labelle-assembler#7 merges.

The three test project.labelle fixtures (plugin-manifest-test,
gui-plugin-test, nuklear-plugin-test) also gain the assembler_version
local: override so they resolve correctly in monorepo dev.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes the migration of assembler-wired runtime content (backends, ECS adapters, GUI plugins) into labelle-assembler, and updates cache resolution to use a new ~/.labelle/packages/assembler/<version>/ slot (with transitional version-key handling via assembler_version).

Changes:

  • Reworked cache APIs from CLI-bundled packages to assembler-bundled packages (resolveAssemblerPackage / resolveBundledPackage, populateAssemblerCache, fetchAssemblerPackages).
  • Updated generator/linker code paths to resolve bundled runtime content from the assembler cache slot.
  • Added the migrated backend/ECS/GUI package sources (plus examples) and expanded CI to run zig-ecs adapter tests.

Reviewed changes

Copilot reviewed 84 out of 87 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/root.zig Re-exports updated cache APIs and resolves backend templates via bundled-package resolver.
src/deps_linker.zig Links backend/ECS deps using resolveBundledPackage instead of CLI cache.
src/cache.zig Introduces assembler cache slot resolution, population, and fetching; updates cache validation.
src/build_files.zig Uses bundled-package resolver for backend/ECS path generation in fallback zon generation.
gui/sokol-imgui/src/adapter.zig Adds sokol Dear ImGui GUI adapter module.
gui/sokol-imgui/build.zig.zon Declares sokol-imgui GUI plugin package metadata/deps.
gui/sokol-imgui/build.zig Builds sokol-imgui module and re-exports sokol backend modules/artifact.
gui/simple-sokol/src/adapter.zig Adds simple immediate-mode GUI backend for sokol.
gui/simple-sokol/build.zig.zon Declares simple-sokol GUI plugin package metadata/deps.
gui/simple-sokol/build.zig Builds simple-sokol GUI module.
gui/simple-raylib/src/adapter.zig Adds simple immediate-mode GUI backend for raylib.
gui/simple-raylib/build.zig.zon Declares simple-raylib GUI plugin package metadata/deps.
gui/simple-raylib/build.zig Builds simple-raylib GUI module.
gui/raylib-imgui/src/adapter.zig Adds raylib Dear ImGui GUI adapter module.
gui/raylib-imgui/build.zig.zon Declares raylib-imgui GUI plugin package metadata/deps.
gui/raylib-imgui/build.zig Builds rlImGui bridge C++ static lib + GUI module; re-exports raylib backend modules/artifact.
gui/clay/src/adapter.zig Adds Clay GUI adapter module.
gui/clay/build.zig.zon Declares clay GUI plugin package metadata/deps.
gui/clay/build.zig Builds clay GUI module.
ecs/zig-ecs/test/adapter_test.zig Adds zig-ecs adapter tests.
ecs/zig-ecs/src/adapter.zig Adds zig-ecs adapter implementation for engine ECS contract.
ecs/zig-ecs/build.zig.zon Declares zig-ecs adapter package metadata/deps.
ecs/zig-ecs/build.zig Builds zig-ecs adapter module and test step.
ecs/zflecs/src/adapter.zig Adds zflecs adapter implementation.
ecs/zflecs/build.zig.zon Declares zflecs adapter package metadata/deps.
ecs/zflecs/build.zig Builds zflecs adapter module.
ecs/mr-ecs/src/adapter.zig Adds mr-ecs adapter implementation.
ecs/mr-ecs/build.zig.zon Declares mr-ecs adapter package metadata/deps.
ecs/mr-ecs/build.zig Builds mr-ecs adapter module.
backends/wgpu/templates/desktop.txt Adds wgpu backend lifecycle template.
backends/wgpu/src/window.zig Adds wgpu window module skeleton (GLFW-based).
backends/wgpu/src/input.zig Adds wgpu input module (GLFW-based).
backends/wgpu/src/audio.zig Adds wgpu audio module (software WAV mixer).
backends/wgpu/example/build.zig.zon Adds wgpu backend demo package metadata.
backends/wgpu/example/build.zig Adds wgpu backend demo build script.
backends/wgpu/build.zig.zon Declares wgpu backend package metadata/deps.
backends/wgpu/build.zig Builds wgpu backend modules and re-exports native artifacts.
backends/sokol/templates/mobile.txt Adds sokol mobile lifecycle template.
backends/sokol/templates/desktop.txt Adds sokol desktop lifecycle template.
backends/sokol/src/window.zig Adds sokol window module.
backends/sokol/src/stb_image_impl.c Adds stb_image implementation for sokol gfx.
backends/sokol/src/input.zig Adds sokol input module.
backends/sokol/src/audio.zig Adds sokol audio module (saudio mixer).
backends/sokol/example/build.zig.zon Adds sokol backend demo package metadata.
backends/sokol/example/build.zig Adds sokol backend demo build script.
backends/sokol/example/assets/characters_frames.zon Adds sokol demo sprite atlas frame data.
backends/sokol/example/assets/characters_animations.zon Adds sokol demo animation data.
backends/sokol/build.zig.zon Declares sokol backend package metadata/deps.
backends/sokol/build.zig Builds sokol backend modules and re-exports native artifact + emscripten helpers.
backends/sdl/templates/desktop.txt Adds SDL backend lifecycle template.
backends/sdl/src/window.zig Adds SDL window module.
backends/sdl/src/sdl.zig Adds shared SDL2 @cImport module.
backends/sdl/src/input.zig Adds SDL input module.
backends/sdl/src/audio.zig Adds SDL audio module (SDL_mixer).
backends/sdl/example/build.zig.zon Adds SDL backend demo package metadata.
backends/sdl/example/build.zig Adds SDL backend demo build script.
backends/sdl/build.zig.zon Declares SDL backend package metadata.
backends/sdl/build.zig Builds SDL backend modules and shared SDL import module.
backends/raylib/templates/wasm.txt Adds raylib WASM lifecycle template.
backends/raylib/templates/desktop.txt Adds raylib desktop lifecycle template.
backends/raylib/src/window.zig Adds raylib window module.
backends/raylib/src/input.zig Adds raylib input module.
backends/raylib/src/gfx.zig Adds raylib gfx module.
backends/raylib/src/audio.zig Adds raylib audio module.
backends/raylib/build.zig.zon Declares raylib backend package metadata/deps.
backends/raylib/build.zig Builds raylib backend modules and re-exports native artifact + emsdk helpers.
backends/bgfx/templates/desktop.txt Adds bgfx backend lifecycle template.
backends/bgfx/src/window.zig Adds bgfx window module (GLFW + bgfx).
backends/bgfx/src/input.zig Adds bgfx input module (GLFW-based).
backends/bgfx/src/audio.zig Adds bgfx audio module (software WAV mixer).
backends/bgfx/example/main.zig Adds bgfx backend demo program.
backends/bgfx/example/build.zig.zon Adds bgfx backend demo package metadata.
backends/bgfx/example/build.zig Adds bgfx backend demo build script.
backends/bgfx/build.zig.zon Declares bgfx backend package metadata/deps.
backends/bgfx/build.zig Builds bgfx backend modules and re-exports native artifacts.
.github/workflows/ci.yml Adds CI step to run zig-ecs adapter tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/cache.zig Outdated
Comment on lines 204 to 207
// Assembler-bundled packages (backends, ecs, gui)
if (!try isAssemblerCached(allocator, cfg.labelle_version)) {
try missing.append(allocator, try std.fmt.allocPrint(allocator, "assembler {s}", .{cfg.labelle_version}));
}

Copilot AI Apr 13, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateCache checks the assembler-bundled cache slot using cfg.labelle_version only. When cfg.assembler_version is set to a different version, this will report the wrong cache state (and can block generation even though resolveBundledPackage will look under assembler_version). Consider using (cfg.assembler_version orelse cfg.labelle_version) for the assembler cache check and for the missing-package message.

Copilot uses AI. Check for mistakes.
Addresses PR #7 review feedback from Cursor Bugbot (high severity) and
Copilot. validateCache was always keying the assembler-slot check on
cfg.labelle_version, but ensureCache in labelle-cli uses the
correct selection logic: cfg.assembler_version orelse cfg.labelle_version.

When a project pins assembler_version to a value different from
labelle_version, validateCache would probe the wrong cache slot and
report the wrong package as missing (and ensureCache would then fetch
the right one anyway, so runtime still works — but the reported state
lied). Match ensureCache's selection so both agree.

Also drops a stale comment in loadBackendTemplate that still described
the old "prefer assembler, fall back to CLI" dual-slot probe — the
fallback was removed in the Stage 6 cleanup (commit 83ef543).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
apotema added a commit to labelle-toolkit/labelle-cli that referenced this pull request Apr 13, 2026
0.3.0 is the first assembler release to bundle backends, ecs adapters,
and gui plugins (see labelle-toolkit/labelle-assembler#7). New projects
created via \`labelle init\` after this commit will pin to 0.3.0 and
auto-download the right binary on first generate.

Safe for CI on this branch because both docker-build and
versions-integration jobs pass \`--assembler-version="local:..."\` to
\`labelle init\`, which overrides the default.

Follow-up after labelle-assembler#7 merges:
  - Cut labelle-assembler v0.3.0 release on GitHub
  - Revert the CI assembler checkout pin from feat/absorb-backends to
    main (commit 3f5bc3c)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@apotema
apotema merged commit 4c6f5db into main Apr 13, 2026
3 checks passed
apotema added a commit to labelle-toolkit/labelle-cli that referenced this pull request Apr 13, 2026
labelle-toolkit/labelle-assembler#7 is merged and v0.4.0 is tagged
with the absorbed runtime content (backends, ecs, gui). This commit:

  1. Drops the temporary feat/absorb-backends pin on the assembler
     checkout in ci.yml — CI now resolves against the default branch
     (main), which has the merged content.

  2. Updates DEFAULT_ASSEMBLER_VERSION from 0.3.0 to 0.4.0. The 0.3.0
     bump in commit 6526383 was wrong: v0.3.0 already existed before
     this refactor (the "animations directory scanning" release) and
     does not contain the absorbed packages. v0.4.0 is the first
     release that does.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
apotema added a commit that referenced this pull request Jul 1, 2026
chatgpt-codex raised 8 findings on PR #459 not covered by the #456 round.
Verified each against the real code and folded the valid ones in:

- root_build_deps now carry required resolution (url+hash/path/builtin);
  emsdk is a pinned template section, not name-synthesizable (#2)
- bgfx-android android_app extra module requires root_alias="backend_app" (#3)
- hookless mobile uses an assembler-owned default resolve_target; the
  backend-agnostic resolver means .resolved does not force a hook (#4)
- build_hook must be a dedicated backend.hook.zig, not the provider build.zig
  (top-level @import("sokol") re-exports don't resolve in the root package) (#5)
- android_target_sdk is required for Android; post_wire panics instead of
  the silent orelse 34 fallback (#6)
- golden gate strengthened for hook-bearing cells: snapshot hook source
  and/or run the hook against a fixture *std.Build (#7)
- carried v1 .capabilities forward into the v2 schema so opting into v2
  doesn't bypass capability negotiation (.id was already present) (#8)

Finding #1 (dep-option removal) was already resolved by ea24373 (base =
universal options, per-platform = appends, no subtractive form) — recorded,
not re-edited. Added a "PR #459 corrections" section documenting each.

Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
apotema added a commit that referenced this pull request Jul 1, 2026
* docs(#453): fold PR #456 review findings into manifest-v2 design

Revise the build-graph manifest v2 design doc to address the substantive
coderabbitai/chatgpt-codex findings that PR #456 merged without incorporating,
so the #453 item-3 implementation does not inherit the design flaws.

Central corrections (all verified against the real code):
- Dependency options are declarative (DepOption name + closed ValueSource
  predicate set), NOT a runtime pre_wire hook returning []Flag — a b.dependency
  options literal needs comptime-known field names. pre_wire/DependencyOptions
  deleted.
- Target selection is a pre-dependency resolve_target phase (iOS device/sim +
  SDK, Android ABI) resolved from -Ddevice/-Demulator/-Dandroid_arch + host, not
  a static .triple; iOS SDK now computed before plugin b.dependency calls.
- Core-diamond walk carries a gfx_mod singleton so it preserves engine->gfx.
- Header-first bounded version parse (v1 stays readable, > SUPPORTED rejected).
- Preserve backend_* import aliases; per-platform loop_style/artifacts/link_libc;
  root_build_deps for the emsdk wasm hook; android_target_sdk into HookContext.
- Hook reframed as trusted build code (not mechanically sandboxable).
- Byte-identical gate -> one desktop anchor + golden snapshots; packager PR moved
  before Android/wasm conversions.

Docs-only. Adds a "Review corrections (PR #456)" summary section.

Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw

* docs(#453): specify dep_options merge semantics + fix code-span spacing

Address CodeRabbit findings on PR #459.

Major — dep_options merge is now defined precisely: a comptime, name-keyed
fold of per-platform entries over the base (override on collision, append
otherwise), with NO subtractive form (an empty per-platform list inherits the
base unchanged). Grounded in the v1 paramValue/param_names mechanism
(manifest_splice.zig): the merge is a codegen-time operation on the NAME set,
and because each platform's b.dependency literal is emitted independently, a
name absent from a platform's folded set is simply never written.

Corrected the sokol worked example to match build_zig.txt ground truth
(:94/:124/:535/:763): with_imgui is the only base option (common to all four
platforms); gamepad_* is a desktop-only append; dont_link_system_libs is an
ios/android append (android's was missing). This dissolves the empty-wasm-list
"drop" conflict — wasm forwards only with_imgui because gamepad_* was never in
the base, not by removing it. Updated the "Review corrections" section to note
the clarification.

Minor — fixed inline code spans that wrapped across lines (MD038): the version
gate spans (`< 1 or > SUPPORTED`, `2 <= v <= SUPPORTED_MANIFEST_VERSION`,
`v > SUPPORTED`) and the `switch (target.result.os.tag)` span now sit on single
lines.

Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw

* docs(#453): fold PR #459 review findings into manifest-v2 design

chatgpt-codex raised 8 findings on PR #459 not covered by the #456 round.
Verified each against the real code and folded the valid ones in:

- root_build_deps now carry required resolution (url+hash/path/builtin);
  emsdk is a pinned template section, not name-synthesizable (#2)
- bgfx-android android_app extra module requires root_alias="backend_app" (#3)
- hookless mobile uses an assembler-owned default resolve_target; the
  backend-agnostic resolver means .resolved does not force a hook (#4)
- build_hook must be a dedicated backend.hook.zig, not the provider build.zig
  (top-level @import("sokol") re-exports don't resolve in the root package) (#5)
- android_target_sdk is required for Android; post_wire panics instead of
  the silent orelse 34 fallback (#6)
- golden gate strengthened for hook-bearing cells: snapshot hook source
  and/or run the hook against a fixture *std.Build (#7)
- carried v1 .capabilities forward into the v2 schema so opting into v2
  doesn't bypass capability negotiation (.id was already present) (#8)

Finding #1 (dep-option removal) was already resolved by ea24373 (base =
universal options, per-platform = appends, no subtractive form) — recorded,
not re-edited. Added a "PR #459 corrections" section documenting each.

Claude-Session: https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor: assembler should fetch backends as standalone packages

2 participants