Skip to content

Epic: build-graph manifest v2 + build hook (#453 item 3) #461

Description

@apotema

The capstone of the backend-agnostic assembler (#386) / ecosystem-hardening (#453 item 3). Supersede the two current codegen routes (enum-path build_zig.txt sections + the thin v1 build_fragments/*.txt splice) with a declarative v2 backend.manifest.zon (modules/artifacts/system_libs/frameworks/platforms) + a constrained, versioned build hook for the genuinely-imperative residuals (NDK sysroot+libc.txt, iOS xcrun SDK, emcc).

Design: docs/design/manifest-v2-build-graph.md (merged #456, hardened through review in #459 — incorporates the comptime dep-options correction, dynamic target selection, backend.hook.zig as a separate file, capabilities retained in v2, required Android SDK, hook-aware golden gate, etc.).

PR plan (from the design's §8)

  1. v2 types + header-first version parse (no wiring; unit tests) — Low/S
  2. Generic core+gfx-diamond walk helper + unit tests (highest regression risk) — High/M
  3. Differential harness + wire behind version gate + sokol desktop v2 (byte anchor) — High/M
  4. Shared platform-packager (apk/web) — lands before Android/wasm — Med/M
  5. sokol android (NDK/libc.txt post_wire + resolve_target + packager) — Med/M
  6. sokol ios (xcrun resolve_target) — Med/S
  7. sokol wasm (emcc + emsdk root dep) — Med/M
  8. null + wgpu (pure declarative) — Low/S
  9. raylib + sdl — Med/M
  10. bgfx all platforms (per-platform loop_style, android_app root_alias, NDK ordering) — High/L
  11. Open config to name+package 3rd-party backends + capability validation before wiring — Med/M
  12. Delete enum-path sections + v1 splice; anchor→golden snapshot — Low/M

Migration is flag-day-safe: v1/enum path stays alive, v2 gated on manifest_version >= 2, one backend + one platform at a time (sokol desktop first), enum sections deleted only after all 6 are v2. Regression gate = one anchored byte-diff then golden snapshots (hook-bearing cells snapshot the hook source / run it against a fixture Build).

Tracking each PR here. Ref #453, #386, RFC #378.

https://claude.ai/code/session_017pW3ifKf9wgxNg4viy6okw

Activity

  1. added 7 commits that reference this issue on Jul 1, 2026
  2. apotema commented on Jul 1, 2026

    @apotema
    ContributorAuthor

    Milestone: PRs 1–11 merged — the v2 mechanism is complete (gated-dark)

    PR PR
    #462 v2 types + parse #467 sokol ios
    #463 core-diamond walk #468 sokol wasm
    #464 wire + sokol byte anchor (0 diff) #469 null + wgpu (generic desktop path)
    #465 shared packager #470 raylib + sdl
    #466 sokol android (first hook) #471 bgfx all platforms
    #472 open config (3rd-party) + capability gate

    All 6 backends are v2-modeled (byte-anchor for sokol-desktop; golden cells for every other platform, with the hooks compiled + run in the gate). A third-party backend (acme.foo) generates a valid build.zig with no enum tag. All of this is opt-in / gated-dark — production generate still uses the v1/enum path, byte-unchanged.

    Remaining = the production cutover (distinct from the above)

    PR 12 ("delete enum-path + v1 splice") can't happen until production actually runs on v2, which needs:

    1. generate cutover — production auto-detects/uses backend.manifest.v2.zon instead of preflighting the legacy v1 name (the feat(#461): manifest-v2 PR11 — open config for 3rd-party backends + capability gate #472 P2 finding).
    2. External-repo v2 adoption — the 6 labelle-<backend> repos ship their backend.manifest.v2.zon (today those live only as in-tree test fixtures). A per-repo sweep like the Backend ecosystem-hardening: enable 3rd-party backend authoring (follow-up to #386) #453 metadata adoption.
    3. Then delete the enum sections + v1 splice + convert the sokol-desktop anchor to a golden.

    This phase changes production output and spans all 6 backend repos, so it's a deliberate rollout rather than a mechanical delete.

  3. added 4 commits that reference this issue on Jul 1, 2026
  4. apotema commented on Jul 1, 2026

    @apotema
    ContributorAuthor

    Milestone: production cutover DONE — all 6 backends flipped to v2

    Following the mechanism (PRs 1–11) + the design-doc hardening, the production cutover landed:

    Real bugs the incremental flip surfaced (all fixed)

    Validation: desktop + wasm fully CI-covered (raylib gamepad, null headless, emsdk); bgfx-android + sokol cross-build in CI.

    Remaining = Phase C (PR 12): delete the enum-path sections + v1 splice

    Gated on on-device android/ios render validation for sokol + bgfx on v2 — CI covers the android/ios cross-build (compile/link), but not device render, and deleting the enum path removes the fallback. Per the extraction discipline (bgfx-android was render-validated on-device on a Tab A7), the enum deletion should follow an on-device v2 render check. The byte-anchor test also converts to a golden snapshot at that point (design §6 step 5). This step needs a device, so it's the deliberate final gate.

  5. added 4 commits that reference this issue on Jul 1, 2026
  6. apotema commented on Jul 3, 2026

    @apotema
    ContributorAuthor

    Follow-on: lifecycle SHAPE off the Backend enum (#461 literal-agnosticism)

    Migrating the last per-backend codegen predicates that keyed off cfg.backend — starting with the callback-lifecycle shape (the biggest one, lifecycle/render.zig). Cascade so far:

    Remaining (the deletion):

    1. Delete the render.zig sokol / bgfx-android shape fallback branches + repoint the existing sokol/bgfx main.zig unit tests to set lifecycle_override (they currently rely on the enum branch).
    2. Migrate the other is_bgfx_android / cfg.backend == .sokol sites: callback_dispatch_handled, the platform/entry comments, and build_files.zig's bgfx backend_app import (→ a manifest extra_modules signal).
    3. (Separate, device-gated) the enum-PATH build_zig.txt sections + v1 splice — still gated on on-device sokol/bgfx render validation.

    Step 1–2 removes a safety-net fallback + rewrites several tests, so it's the highest-care step; doing it deliberately rather than at the tail of a long session.

  7. added 2 commits that reference this issue on Jul 3, 2026
  8. apotema commented on Jul 3, 2026

    @apotema
    ContributorAuthor

    Lifecycle-enum reduction landed (#531) — full removal is coupled to v1-drop

    Attempted to delete the Backend-enum branches from the lifecycle codegen (render.zig shape + use_callback_lifecycle + callback_dispatch_handled, and build_files.zig's bgfx backend_app import). Review (codex + gemini) surfaced that the enum is load-bearing for the v1/legacy callback path: resolveLifecycleOverride is v2-only and manifestPathEnabled skips non-desktop v1, so a tag-matched sokol/bgfx resolving only a v1 (or no) manifest reaches the callback lifecycle with both overrides null — deleting the enum rendered it as a loop with unfilled callback holes / wrong shape / ExternalCallbackBackendUnsupported.

    So the shape-enum can't be deleted independently of dropping v1-manifest support — which is the device-gated enum-path deletion. #531 therefore landed the safely-separable subset:

    • v2 (production) lifecycle is fully manifest-driven — dispatch reordered so lifecycle_ovr != null short-circuits before the enum; production never consults it.
    • bgfx backend_app import is manifest-driven — reads the platform entry's extra_modules effective alias (backend_app), so a name-only third-party backend works too; fixed an alias bug (was matching mod.name, now the emitted backend_<name> alias).
    • Comments derive from shape.android == .bgfx_shell; tests exercise the v2 path.
    • The enum remains only as a documented v1/legacy fallback (structurally unreachable for v2), alongside androidNeedsAppImport's v1 fallback and the untouched android_link_bgfx link site.

    Remaining for full literal name-free: the device-gated enum-PATH deletion (§6 step 5) now also removes these v1 lifecycle-enum residuals in one pass, once the on-device sokol/bgfx v2 render check is done. That's the single remaining step; it needs a device.

  9. added a commit that references this issue on Jul 3, 2026
  10. apotema commented on Jul 3, 2026

    @apotema
    ContributorAuthor

    ✅ v1/legacy codegen path FULLY REMOVED (#532) — the assembler is now literally manifest-v2-only

    The final deletions PR (§6 step 5) landed. The Backend enum no longer appears anywhere in the codegen path — grep -E 'switch (cfg.backend)|cfg.backend == .|is_bgfx_android' src/codegen src/build_files.zig → zero.

    Removed (~1100 lines net):

    • build_zig.txt: the 30 enum-path sections (.backend_*/.link_*/.header_{ios,android,wasm}/.ios_deps/.android_deps/.android_package/.wasm_*/.*_link*). Kept only the scaffolding the v2 splice also emits.
    • manifest_splice.zig: the v1 struct + renderers (BackendManifest, loadManifest, renderBackendDepSection/renderLinkSection, loopStyle, …). Relocated the v2-shared survivors (ProviderManifest/identity slice, LEGACY_MANIFEST_NAME, platformEntry). parseManifest now returns BackendManifestV2 directly and rejects manifest_version < 2 with a readable error.
    • build_files.zig: the three switch (cfg.backend) sites + usesEnumBackendPath; v2 is now mandatory (v2_manifest orelse return error.ExternalBackendNeedsManifest).
    • render.zig: the lifecycle enum residual (the feat(#461): delete the dead lifecycle enum branches (manifest-driven lifecycle) #531 v1 fallback).
    • Fixtures: backends/sdl, backends/sokol_v1only, backends/sokol_v1inv2 + genSokolBuildZig.

    Guard: the sokol-desktop byte-anchor converted to 4 committed goldens (test/goldens/sokol_desktop_v2*.build.zig, captured while still proven byte-identical to the enum baseline). Every other golden unchanged. zig build test green.

    Cross-repo fallout fixed: the external-null example's nullfixture backend was v1-only → generation failed under v2-mandatory. Added a v2 manifest to labelle-nullfixture-backend v0.3.0 (mirrors labelle-null) and bumped the example's pin; Examples-integration is green end-to-end.

    ⚠️ On-device caveat (for the human): this removed the enum/v1 build-graph fallback the design gated on on-device sokol+bgfx v2 render validation. CI covers the Android/iOS cross-build (compile/link) and the text goldens are blind to render behavior — please run the on-device v2 render check to confirm.

    The only residual switch (cfg.backend) left anywhere is in deps_linker.zig (shared sub-package staging, defensive/dead — not the build-graph codegen). #461 is complete modulo that on-device confirmation.

  11. apotema commented on Jul 3, 2026

    @apotema
    ContributorAuthor

    ✅ ON-DEVICE RENDER VALIDATION PASSED — the device gate is cleared

    Ran both backends on a Galaxy Tab A7 (SM-T505, Android 12) with the v2-only assembler (main @ #532, LABELLE_ASSEMBLER override confirmed in the generate log — using assembler: …/labelle-assembler-282/…), labelle android run --release:

    • bgfx-android (.backend = .bgfx, core 1.22 / engine 1.65 / gfx 1.19) — generate → NDK ReleaseFast build → APK → install → launch → renders cleanly: the shape demo (magenta rect + teal square w/ orange diamond + teal circle on cream). Crash-free, foregrounded NativeActivity.
    • sokol-android (gamepad-android, imgui GUI, core 1.24 / engine 1.66 / gfx 1.20) — same pipeline → renders cleanly: the imgui "Gamepad Demo" HUD (title bar + all text). Crash-free, foregrounded NativeActivity.

    Both generated entirely through the v2 manifest path (the enum/v1 fallback is gone) and render correctly on-device — so removing the enum/v1 build-graph fallback caused zero on-device regression. This was the last thing gating #461; the epic is now fully validated end-to-end (unit goldens byte-identical + Examples-integration CI + on-device render on both real backends).

  12. apotema commented on Jul 5, 2026

    @apotema
    ContributorAuthor

    Status narrowing: the manifest-v2 migration is effectively COMPLETE — #529–#532 removed the v1/enum codegen path entirely (v2 mandatory, all 6 backends external, ~0 live cfg.backend== branches). What remains open here is ONLY the gated on-device render validation (sokol + bgfx on v2, which CI can't run — it covers cross-build compile/link + goldens, not device render). Everything else in the §8 plan (PRs 1–12) landed. Suggest this stays open solely as the on-device-render tracking item, or closes once that human check is done.

  13. apotema commented on Jul 8, 2026

    @apotema
    ContributorAuthor

    Closing — epic complete on origin/main. All 12 manifest-v2 + build-hook items landed (subs #462–#472); capstone #532 (ff330a5) deleted the v1/enum codegen path; build_hook/HOOK_ABI_VERSION present in manifest_v2.zig.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions