Skip to content

feat(config): enum-as-shorthand backend resolution (#386 Phase 5) - #412

Merged
apotema merged 1 commit into
mainfrom
feat/386-enum-as-shorthand
Jun 29, 2026
Merged

feat(config): enum-as-shorthand backend resolution (#386 Phase 5)#412
apotema merged 1 commit into
mainfrom
feat/386-enum-as-shorthand

Conversation

@apotema

@apotema apotema commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Phase 5 — enum-as-shorthand backend resolution

Completes the Phase-5 resolver seam so a built-in .backend enum tag is a shorthand for a provider, not a hardwired bundled slot. This is the backward-compatibility hinge that lets a backend be extracted out-of-tree (Phase 6c) without breaking existing .backend = .bgfx projects (Flying Platform).

What it adds

  • config.builtinProvider(Backend) ?PluginDep — the extraction table. Every built-in maps to null (bundled) today.
  • config.effectiveBackendPackage() = backend_package orelse builtinProvider(backend) — the single place that composes an explicit external package with the enum-shorthand. backendName(), isExternal(), and backend_registry.resolveBackendPackage all route through it, so they can't disagree about whether a backend is external or which package it is.

Effect today: none (by design)

builtinProvider returns null for all six built-ins, so .backend = .<tag> resolves to the bundled backends/<tag> slot exactly as before — generated output is byte-identical (golden suites green).

What it unblocks

Extracting a backend (Phase 6c) becomes a one-line entry here:

.bgfx => .{ .name = "bgfx", .repo = "github.com/labelle-toolkit/labelle-bgfx", .version = "X.Y.Z" },

After that, .backend = .bgfx transparently fetches the provider package — isExternal() ⇒ true, so the manifest path + the #386 Phase-6b contract guard + self-contained staging all activate — with zero change to existing project configs.

Deferred to the actual extraction PR

The bgfx repo move itself, and handling the shared ../sdl_gamepad / ../android_gamepad sub-packages (bgfx/raylib/sokol share them in-tree; an external bgfx can't reference ../). That sub-package decision is the open item gating Phase 6c — intentionally not in this PR.

Tests

  • Every built-in tag is still bundled (!isExternal(), no effective package, named by enum tag) — a behavior-preservation guard that flips visibly the moment a tag is extracted.
  • An explicit backend_package still wins over the enum at the seam.
  • Full zig build test green.

Summary by CodeRabbit

  • New Features
    • Improved backend selection so an explicit backend package now takes priority when both a built-in backend and a package are configured.
    • Built-in backends continue to be treated as local by default, with backend naming aligned to the selected configuration.

Completes the Phase-5 resolver seam: a built-in `.backend` enum tag is now a
*shorthand* for a provider, not a hardwired bundled slot. Adds:

- `config.builtinProvider(Backend) ?PluginDep` — the extraction table. Every
  built-in maps to `null` (bundled) today, so behavior is unchanged.
- `config.effectiveBackendPackage()` — `backend_package orelse
  builtinProvider(backend)`: the ONE place that composes an explicit external
  package with the enum-shorthand. `backendName()`, `isExternal()`, and
  `backend_registry.resolveBackendPackage` all route through it, so they agree
  on whether a backend is external and which package it is.

Effect today: none — `builtinProvider` returns null for all six built-ins, so
`.backend = .<tag>` resolves to the bundled `backends/<tag>` slot exactly as
before (golden suites byte-identical). The point is what it UNBLOCKS: extracting
a backend out-of-tree (Phase 6c) becomes a one-line entry here, after which
`.backend = .bgfx` transparently fetches the `labelle-bgfx` provider package
(isExternal ⇒ true; manifest path + 6b contract guard + self-contained staging
all activate) with zero change to existing project configs.

Deferred to the actual extraction PR: the bgfx repo move itself + handling the
shared `../sdl_gamepad` / `../android_gamepad` sub-packages (bgfx/raylib/sokol
share them in-tree — an external bgfx can't reference `../`), which is the open
decision gating Phase 6c.

Tests: every built-in tag is still bundled (behavior-preservation guard that
flips visibly when a tag is extracted); an explicit backend_package still wins
over the enum at the seam.
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 86f6a0fe-ac15-4563-b70c-8399d33dbc14

📥 Commits

Reviewing files that changed from the base of the PR and between 023bc16 and b26dbdf.

📒 Files selected for processing (2)
  • src/backend_registry.zig
  • src/config.zig
 ___________________________________________________________________________
< Your code and I are going to be best friends. Very critical best friends. >
 ---------------------------------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/386-enum-as-shorthand

Comment @coderabbitai help to get the list of available commands.

@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 the "enum-as-shorthand" seam (Phase 5 of epic #386) to allow built-in backend enum tags to eventually resolve to external provider packages. It introduces builtinProvider and effectiveBackendPackage methods on ProjectConfig and updates backendName, isExternal, and resolveBackendPackage to use them. A critical issue was identified in src/config.zig where calling builtinProvider directly without qualifying it with the struct name ProjectConfig will cause a compile-time error in Zig.

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.

Comment thread src/config.zig
Comment on lines +629 to +631
pub fn effectiveBackendPackage(self: ProjectConfig) ?PluginDep {
return self.backend_package orelse builtinProvider(self.backend);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

critical

In Zig, struct-level namespace declarations (associated functions/static methods) are not automatically in scope as bare identifiers within the struct's methods. Calling builtinProvider(self.backend) directly will result in a compile-time error (use of undeclared identifier 'builtinProvider').\n\nTo fix this, qualify the call with the struct name: ProjectConfig.builtinProvider(self.backend).

    pub fn effectiveBackendPackage(self: ProjectConfig) ?PluginDep {\n        return self.backend_package orelse ProjectConfig.builtinProvider(self.backend);\n    }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a false positive. In Zig, declarations within a container (struct) are in scope as bare identifiers from that container's other declarations — builtinProvider is a decl of ProjectConfig, so a sibling method (effectiveBackendPackage) calls it by bare name without a ProjectConfig. qualifier. Evidence: this exact code compiles — zig build + zig build test are green locally and all five CI build/test jobs (build-and-test ubuntu+macos, bgfx macOS + Android, Examples integration) passed on this commit. A genuine use of undeclared identifier would have failed every one. Leaving as-is.

@apotema
apotema merged commit dcdd356 into main Jun 29, 2026
6 checks passed
@apotema
apotema deleted the feat/386-enum-as-shorthand branch June 29, 2026 19:50
apotema added a commit that referenced this pull request Jun 30, 2026
… (#418)

Pluggable-backends Phases 5+6 (epic #386): the assembler can now FETCH, verify,
and build out-of-tree backend packages, and bgfx is extracted to its own repo.

- #409 6a: remote .backend_package cache-fetch (fetched like a plugin)
- #410 6b: contract-verify an external backend (assertBackend/Window/Input)
- #411: conform raylib + null windows to the canonical window contract
- #412 Phase 5: enum-as-shorthand resolution (a built-in tag can resolve to a package)
- #413 6c: bgfx extracted -> github.com/labelle-toolkit/labelle-bgfx; opt-in CI-verified
- #414: select the GUI bridge by backend name, not the enum (external backends)
- #415: labelle-bgfx v0.2.0 — gamepad sources extracted to their own packages
- #416/#417: the two flip-blockers (callback-external guard; android enum-fallthrough)

Opt-in today via .backend_package; built-in .backend = .bgfx still ships bundled
(the default-flip is a follow-up gated on this release). Built-in backends are
byte-identical. External bgfx validated on-device (Galaxy Tab A7): builds, runs
crash-free, behaves identically to bundled.
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.

1 participant