Skip to content

chore(#520): flows-smoke + asset-streaming-smoke → null backend (agnostic fixtures) - #523

Merged
apotema merged 4 commits into
mainfrom
chore/520-null-smoke-fixtures
Jul 2, 2026
Merged

chore(#520): flows-smoke + asset-streaming-smoke → null backend (agnostic fixtures)#523
apotema merged 4 commits into
mainfrom
chore/520-null-smoke-fixtures

Conversation

@apotema

@apotema apotema commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Makes the two remaining assembler smoke-test fixtures backend-agnostic by switching them from raylib to the headless null backend (refs #520 remainder). Both test assembler codegen, not rendering, so .null (no window / GPU / GL context) is the correct target.

What changed

examples/asset-streaming-smoke — converted cleanly (build and run)

  • project.labelle .backend = .raylib.null (+ labelle.lock backend name).
  • Asset-streaming codegen is identical on null: lazy-inference still flips both atlases to registerAtlasFromMemory (defer PNG decode) and emits SceneAssetManifests — grep-asserted on the generated main.zig.
  • Generated streaming main loop + ImageBackendAdapter also run headless (frame-capped, clean rc=0).
  • CI (Examples integration test) repointed to .labelle/null_desktop, invokes the assembler binary directly (labelle-cli is pinned pre-.null), asserts the streaming codegen, builds, and runs frame-capped. Real GPU upload / on-screen render is not asserted (null has no GPU) — never was; stays a local .raylib check.

examples/flows-smoke — converted for build; CI stays build-only

  • project.labelle .backend = .raylib.null.
  • All flow-codegen assertions hold on null (tick/custom .zig, PluginFlowNodes shim, script__logger named-module promotion, overrideImport wiring) and it builds clean; the CustomNode Game-script CustomNodes don't compile: shim missing PluginFlowNodes + named-module promotion needed (follows #238) #240 path even executes (logger.log_i32: 42).
  • CI repointed to .labelle/null_desktop, build-only (its prior depth). A headless run trips a pre-existing, backend-INDEPENDENT crash: the generated tick flow-handler's runtime game.getComponent on the entity_created payload faults in labelle-core ecs (alive.contains). The fixture was raylib build-only and never ran, so it never surfaced this — unrelated to flow codegen/backend, out of scope. Reverting to raylib keeps the GPU dep for no benefit (raylib can't run headless in CI either). Flagged in README + CI comment.

Verification

  • Assembler zig build + zig build test --summary all1233 pass / 8 skip, rc=0.
  • asset-streaming-smoke: clean-room generate → build → run on null_desktop, rc=0.
  • flows-smoke: clean-room generate (all codegen assertions pass) → build on null_desktop, rc=0.

Neither needs to migrate to a backend package — the null conversion still fully tests each feature's codegen.

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

Summary by CodeRabbit

  • New Features
    • Switched the asset-streaming and flows smoke examples to a headless .null backend for display-free, faster codegen validation.
    • Updated CI to generate, build, and run the asset-streaming smoke flow in headless mode, validating key generated artifacts along the way.
  • Documentation
    • Refreshed the asset-streaming and flows smoke READMEs to explain the .null backend workflow, local commands, and revised CI expectations.

…stic assembler fixtures)

Both fixtures test assembler CODEGEN, not rendering, so the headless
`.null` backend is the correct target — no display / GPU / GL context
needed to generate or build them (labelle-assembler#520 remainder).

- asset-streaming-smoke: `.backend = .null`. The streaming codegen
  (lazy-inference → `registerAtlasFromMemory`, `SceneAssetManifests`)
  is emitted identically; the generated main loop + ImageBackendAdapter
  also RUN headless (frame-capped, clean rc=0). CI repointed to
  `.labelle/null_desktop`, invokes the assembler binary directly (cli is
  pinned pre-`.null`), asserts the streaming codegen, builds, and runs.

- flows-smoke: `.backend = .null`. All flow-codegen assertions hold and
  it builds clean on null; the CustomNode (#240) path actually executes.
  CI repointed to `.labelle/null_desktop`, stays build-only: a headless
  run trips a pre-existing, backend-INDEPENDENT crash in the generated
  `tick` flow-handler's runtime `game.getComponent` (faults in
  labelle-core ecs `alive.contains`) — the fixture never ran before
  (raylib build-only), so this latent runtime gap is out of scope here.

Assembler `zig build` + `zig build test` still green (1233 pass / 8 skip).

Claude-Session: https://claude.ai/code/session_01P7B7UzgrWEbBYLT3YBrAog
https://claude.ai/code/session_01P7B7UzgrWEbBYLT3YBrAog
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: db013fff-d442-4e4f-9083-fcfa9c97f74e

📥 Commits

Reviewing files that changed from the base of the PR and between 8727d67 and a142ccf.

📒 Files selected for processing (1)
  • examples/flows-smoke/README.md
✅ Files skipped from review due to trivial changes (1)
  • examples/flows-smoke/README.md

📝 Walkthrough

Walkthrough

CI and example fixtures switch from .raylib to a headless .null backend. The asset-streaming smoke path now uses assembler-driven generation, codegen assertions, fingerprint patching, and headless execution. The flows smoke path now validates generated null-desktop outputs and related wiring.

Changes

Headless .null backend migration

Layer / File(s) Summary
Asset-streaming-smoke fixture and CI switch to .null
examples/asset-streaming-smoke/project.labelle, examples/asset-streaming-smoke/README.md, .github/workflows/ci.yml
Backend flips to .null; README documents the headless workflow; CI generates via the assembler binary, asserts registerAtlasFromMemory/SceneAssetManifests, patches build.zig.zon fingerprint, builds, and runs the binary headlessly with LABELLE_NULL_FRAMES=10.
Flows-smoke fixture and CI switch to .null
examples/flows-smoke/project.labelle, examples/flows-smoke/README.md, .github/workflows/ci.yml
Backend flips to .null; README documents codegen-only validation; CI validates .labelle/null_desktop outputs including tick.zig, CustomNode lowering, named-module wiring, and build-module creation/override import checks.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related issues

Possibly related PRs

Poem

A bunny hopped from raylib light,
To headless .null by day and night.
Codegen hummed, the frames went by,
And fingerprint dust got patched just right.
🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: both smoke-test fixtures move to the null backend.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/520-null-smoke-fixtures

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 switches the backend for the asset-streaming-smoke and flows-smoke examples from .raylib to the headless .null backend. This change allows testing the assembler's codegen (asset-streaming and flow codegen) in headless environments, such as CI, without requiring a display or GPU. The corresponding README files, project configurations, and lockfiles have been updated to reflect this transition. There are no review comments, 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.

@apotema

apotema commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c8f405e682

ℹ️ 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".

Comment thread .github/workflows/ci.yml Outdated
# `.null` Backend variant, so `labelle generate` would fail at
# the project.labelle parse step. Same recipe as the null /
# flows-smoke / plugin-controllers steps below.
$GITHUB_WORKSPACE/labelle-assembler/zig-out/bin/labelle-assembler generate --project-root .

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Run install before the first null-backend generate

In the examples-integration job on a fresh runner, this is now the first direct .null generation, but nothing before it populates the labelle package cache with the extracted labelle-null backend; the only labelle-assembler install --project-root . that fetches the null backend is in the later external-null step. Since the assembler binary's generate path only reads the local cache (the CLI/install path is what fetches dependencies), this step will fail before reaching the later install. Add an install for this project before generate, or move this step after a cache-populating install.

Useful? React with 👍 / 👎.

The lock's `.backend = .{ .name = "null" }` made the assembler resolve null as an EXTERNAL packaged backend (ExternalBackendNeedsManifest) at generate. The working null examples (examples/null, plugin-controllers) carry no lock — match that.

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

216-229: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fourth copy of the fixFingerprint dance — consider extracting a shared script.

This ~12-line "capture invalid/use-this-value fingerprint from a first zig build, sed-patch build.zig.zon, rebuild" block is now duplicated four times in this file (here, the null example at Lines 322-330, flows-smoke at Lines 413-422, plugin-controllers at Lines 462-471). A shared shell script (e.g. scripts/ci/fix-fingerprint-and-build.sh) invoked from each step would keep the four call sites in sync if the diagnostic format or patch logic ever needs to change.

♻️ Example extraction
# scripts/ci/fix-fingerprint-and-build.sh
#!/bin/bash
set -e
set +e
ERR=$(zig build 2>&1)
set -e
BAD=$(printf '%s\n' "$ERR" | grep -oE 'invalid fingerprint: 0x[0-9a-f]+' | head -1 | awk '{print $3}')
GOOD=$(printf '%s\n' "$ERR" | grep -oE 'use this value: 0x[0-9a-f]+' | head -1 | awk '{print $4}')
if [ -n "$BAD" ] && [ -n "$GOOD" ]; then
  echo "labelle-cli-equivalent fixFingerprint: ${BAD} -> ${GOOD}"
  sed -i "s/${BAD}/${GOOD}/" build.zig.zon
fi
zig build
-          set +e
-          ERR=$(zig build 2>&1)
-          set -e
-          BAD=$(printf '%s\n' "$ERR" | grep -oE 'invalid fingerprint: 0x[0-9a-f]+' | head -1 | awk '{print $3}')
-          GOOD=$(printf '%s\n' "$ERR" | grep -oE 'use this value: 0x[0-9a-f]+' | head -1 | awk '{print $4}')
-          if [ -n "$BAD" ] && [ -n "$GOOD" ]; then
-            echo "labelle-cli-equivalent fixFingerprint: ${BAD} -> ${GOOD}"
-            sed -i "s/${BAD}/${GOOD}/" build.zig.zon
-          fi
-          zig build
+          $GITHUB_WORKSPACE/labelle-assembler/scripts/ci/fix-fingerprint-and-build.sh
🤖 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 @.github/workflows/ci.yml around lines 216 - 229, The
fingerprint-capture-and-patch block is duplicated across several CI steps, so
extract it into a shared script and call it from each site instead of keeping
four copies in .github/workflows/ci.yml. Move the logic that runs zig build,
parses BAD and GOOD, patches build.zig.zon, and rebuilds into a reusable script
such as scripts/ci/fix-fingerprint-and-build.sh, then update the null_desktop,
flows-smoke, and plugin-controllers steps to invoke that script so any future
diagnostic or sed changes stay in sync.
🤖 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 `@examples/flows-smoke/README.md`:
- Around line 21-22: The README text is wrapped in a way that leaves “#520)” at
the start of a line, which markdownlint treats as an invalid heading. Reflow the
sentence in the affected fixture description so the `labelle-assembler `#520``
reference stays on the same line or is otherwise not line-initial, preserving
the meaning while avoiding MD018.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 216-229: The fingerprint-capture-and-patch block is duplicated
across several CI steps, so extract it into a shared script and call it from
each site instead of keeping four copies in .github/workflows/ci.yml. Move the
logic that runs zig build, parses BAD and GOOD, patches build.zig.zon, and
rebuilds into a reusable script such as scripts/ci/fix-fingerprint-and-build.sh,
then update the null_desktop, flows-smoke, and plugin-controllers steps to
invoke that script so any future diagnostic or sed changes stay in sync.
🪄 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: 564a52c7-848e-4757-9b2d-3bff011fb86f

📥 Commits

Reviewing files that changed from the base of the PR and between 829dcce and c6bbd27.

⛔ Files ignored due to path filters (2)
  • examples/asset-streaming-smoke/labelle.lock is excluded by !**/*.lock
  • examples/flows-smoke/labelle.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • .github/workflows/ci.yml
  • examples/asset-streaming-smoke/README.md
  • examples/asset-streaming-smoke/project.labelle
  • examples/flows-smoke/README.md
  • examples/flows-smoke/project.labelle

Comment thread examples/flows-smoke/README.md Outdated
apotema added 2 commits July 2, 2026 16:42
…n CI

`.null` is an EXTRACTED external backend (#386 Phase 6c) — it resolves to
the `labelle-null` package, not a bundled slot, so its
`backend.manifest.zon` must be in the package cache before `generate` can
splice the codegen (else: ExternalBackendNeedsManifest).

The raw-assembler `generate` does NOT populate the cache; only
`ensureCache` (via labelle-cli `generate` / `install`) does. The existing
raw-assembler null example steps only pass because an earlier
`labelle generate` step incidentally seeds null (ensureCache always
fetches null for the tests target). Switching asset-streaming-smoke to
the raw assembler removed that seed AND moved it to the first example
step, so null was uncached when it ran → CI failed at generate.

Fix: run `labelle-assembler install --project-root .` before `generate`
in BOTH converted steps (mirrors the external-backend step), making each
fixture self-sufficient and order-independent. Verified end-to-end on a
FRESH package cache (remote labelle-null fetch, no sibling checkout —
the CI condition): install → generate → build → run all green for both.

Claude-Session: https://claude.ai/code/session_01P7B7UzgrWEbBYLT3YBrAog
https://claude.ai/code/session_01P7B7UzgrWEbBYLT3YBrAog
@apotema
apotema merged commit 4448890 into main Jul 2, 2026
4 checks passed
apotema added a commit that referenced this pull request Jul 2, 2026
…es (#525)

These backend-coupled examples now live in their backend packages — the
assembler is backend-agnostic and should not carry them:
  - gamepad          → labelle-raylib#8
  - gamepad-android  → labelle-sokol#6
  - sokol_imgui      → labelle-sokol#6

Remove the three example dirs and the raylib-only "Generate + build the
gamepad example" CI step. The gamepad-sim harness stays (it tests the shared
backends/sdl_gamepad source, not the example); its comment is repointed to the
gamepad example's new home in labelle-raylib.

The remaining examples are all backend-agnostic: null / plugin-controllers
(headless .null), asset-streaming-smoke + flows-smoke (.null, #523), and
external-null (out-of-tree backend_package fixture). Completes the examples
side of the agnosticism remainder.

Verified: `zig build` + `zig build test` green.

Claude-Session: https://claude.ai/code/session_01P7B7UzgrWEbBYLT3YBrAog
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