Skip to content

fix: unblock main CI + audit follow-ups (RT-safety, silent-drop FFI, docs)#124

Open
proggeramlug wants to merge 1 commit into
mainfrom
fix/audit-followups-ci-rt-safety
Open

fix: unblock main CI + audit follow-ups (RT-safety, silent-drop FFI, docs)#124
proggeramlug wants to merge 1 commit into
mainfrom
fix/audit-followups-ci-rt-safety

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Follow-ups from the 2026-07-19 engine audit. This PR is the verified-safe batch — everything here compiles and passes tests on macOS; it's ready to merge. A second PR will carry the native platform work that needs on-device testing.

Main CI was red

ffi-parity was failing on main, and fixing it exposed a second failure that had been hidden behind it (the file-line gate — the EN-063 "gate nobody reads" pattern, recurring within 48h).

  • bloom_is_key_repeated (added in isKeyRepeated + real Windows clipboard/file dialogs/window title + unloadModel wrapper #122) was never exported on web or watchOS. Added to input_ffi.rs; regenerated watchOS ffi_stubs.rs via gen_stubs.js. → validate-ffi green.
  • renderer/mod.rs had grown to 13,156 (past its 13,058 baseline) during the PT work while the gate sat behind the broken step. Split to 12,556 by moving ~30 GPU-uniform POD structs + build_card_ortho_v2 into renderer/types.rs (its documented home). Struct/field order is byte-identical → layout-neutral. Baseline ratcheted down. (EN-052)

Silent-failure FFI bugs

  • loadMaterial called bloom_set_material_params, which isn't in the manifest → Perry silently no-op'd it, so every material's params array was dropped. Rerouted through the working _scratch path.
  • createTextureArray/createTextureArrayEx were exported but uncallable from Perry (number[]→i64 pointer). Rerouted through the scratch path so they work; non-breaking.

Audio RT-safety (audio/render.rs)

  • The 65th concurrent voice reallocated the voices Vec on the audio thread (a glitch / malloc-lock priority inversion). Added voice-stealing (drop the quietest) so the render thread never grows past its preallocated cap; same for music.
  • Added 2 regression tests to a file that had zero.

Docs

  • README: Windows XAudio2WASAPI, Linux PulseAudioALSA, dropped the Wayland claim. CLAUDE.md: same ALSA fix.
  • lumen-roadmap platform matrix: HW ray-query is opt-in, not auto.
  • pt-roadmap table: added the shipped PT-3b/6/7/8 rows.

Verification

  • validate-ffi: 0 failures. check-file-lines: 0 failures.
  • cargo test --lib (default features): 128 passed, incl. the 2 new audio tests.
  • Golden-image suite: 7/9 pass. The 2 failures (pt_progressive, pt_realtime_motion) are pre-existing, hardware-dependent (Monte-Carlo + HW ray-query, baked on different hardware — all raster goldens pass on this GPU). The moved-struct layout is provably byte-identical, so this PR cannot affect PT output. Goldens intentionally not regenerated.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added keyboard-repeat detection for WebAssembly input.
    • Added compatibility support for keyboard-repeat queries on watchOS.
    • Improved material and texture uploads across supported platforms.
  • Bug Fixes

    • Limited simultaneous audio voices and music tracks, with graceful replacement when limits are reached.
    • Prevented unnecessary runtime memory growth during audio playback.
  • Documentation

    • Updated platform audio and graphics architecture details.
    • Clarified opt-in hardware ray-query support and expanded path-tracing roadmap items.

…docs)

Main CI was red on ffi-parity, and fixing that exposed a second failure
(the file-line gate) that had been hidden behind it — the EN-063 "gate
nobody reads" pattern, recurring.

CI:
- Export bloom_is_key_repeated on web (input_ffi.rs) and watchOS
  (regenerated ffi_stubs.rs via gen_stubs.js). validate-ffi green.
- Split renderer/mod.rs 13156 -> 12556 by moving ~30 GPU-uniform POD
  structs + build_card_ortho_v2 into types.rs (their documented home).
  Struct/field order is byte-identical, so this is layout-neutral; the
  golden-image suite (raster) and 128 unit tests pass. Baseline ratcheted
  to 12556. (EN-052)

Silent-failure FFI bugs (TS API):
- loadMaterial called bloom_set_material_params, which is NOT in the
  manifest, so Perry silently no-op'd it and every material's params
  array was dropped. Reroute through the working _scratch path.
- createTextureArray/createTextureArrayEx were exported but uncallable
  from Perry (number[] into an i64 pointer). Reroute through the same
  scratch path so they work; non-breaking (no Perry caller could reach
  them before).

Audio RT-safety (audio/render.rs):
- The 65th concurrent voice reallocated the voices Vec ON the audio
  thread. Add voice-stealing (drop the quietest) so the render thread
  never grows past its preallocated cap; same for music. Add 2
  regression tests to a file that had none.

Docs:
- README: Windows audio XAudio2 -> WASAPI, Linux PulseAudio -> ALSA,
  drop the Wayland claim. CLAUDE.md: same ALSA fix.
- lumen-roadmap platform matrix: HW ray-query is opt-in, not auto.
- pt-roadmap ticket table: add the shipped PT-3b/6/7/8 rows.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR reorganizes renderer parameter types, bounds audio allocations, routes material and texture uploads through scratch buffers, adds keyboard-repeat FFI bindings, and updates platform and rendering documentation.

Changes

Renderer type organization

Layer / File(s) Summary
Centralize renderer uniform types
native/shared/src/renderer/types.rs, native/shared/src/renderer/mod.rs, tools/file-lines-baseline.json
Renderer pass uniforms, GI data layouts, post-processing parameters, and card projection construction move into types.rs; mod.rs imports them and its line-count baseline is updated.

Audio allocation bounds

Layer / File(s) Summary
Bound active audio tracks
native/shared/src/audio/render.rs
Voice and music collections use fixed caps, evict entries at capacity, and test capacity preservation plus quietest-voice stealing.

Keyboard repeat FFI

Layer / File(s) Summary
Expose keyboard-repeat queries
native/web/src/input_ffi.rs, native/watchos/src/ffi_stubs.rs
Web returns the engine keyboard-repeat state, while watchOS provides a zero-valued stub.

Scratch-based material and texture uploads

Layer / File(s) Summary
Route model uploads through scratch buffers
src/core/index.ts, src/models/index.ts
Material parameters and RGBA8 texture-array data are repacked through scratch buffers before native FFI calls.

Platform and roadmap documentation

Layer / File(s) Summary
Refresh platform and rendering documentation
CLAUDE.md, README.md, docs/perf/lumen-roadmap.md, docs/pt/pt-roadmap.md
Platform audio labels, Lumen hardware-path opt-in wording, and path-tracing roadmap tickets are updated.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly reflects the main CI, FFI, audio safety, and docs follow-up work in the PR.
Description check ✅ Passed The description covers the summary, verification, and reviewer notes, though it doesn't follow the exact template headings or checkbox test plan.
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 docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/audit-followups-ci-rt-safety

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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)
native/shared/src/renderer/types.rs (1)

588-590: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Update the PtParamsCpu byte count. The doc comment says 672 bytes, but the current field layout is 944 bytes, so the comment is misleading for WGSL parity checks.

🤖 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 `@native/shared/src/renderer/types.rs` around lines 588 - 590, Update the
byte-count statement in the PtParamsCpu documentation to 944 bytes, keeping the
existing WGSL mirror and alignment descriptions unchanged.
🤖 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 `@src/models/index.ts`:
- Around line 588-590: The packed texture integer passed to
bloom_mesh_scratch_push_u32 is signed when the high byte has its top bit set;
append an unsigned conversion (>>> 0) to the bitwise expression at
src/models/index.ts lines 588-590. Apply the identical fix to the matching
texture repacking logic at src/models/index.ts lines 630-632.

---

Nitpick comments:
In `@native/shared/src/renderer/types.rs`:
- Around line 588-590: Update the byte-count statement in the PtParamsCpu
documentation to 944 bytes, keeping the existing WGSL mirror and alignment
descriptions unchanged.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 35ce7988-b806-4d7c-8f26-96b8e76dbf13

📥 Commits

Reviewing files that changed from the base of the PR and between e498433 and 098b9ee.

📒 Files selected for processing (12)
  • CLAUDE.md
  • README.md
  • docs/perf/lumen-roadmap.md
  • docs/pt/pt-roadmap.md
  • native/shared/src/audio/render.rs
  • native/shared/src/renderer/mod.rs
  • native/shared/src/renderer/types.rs
  • native/watchos/src/ffi_stubs.rs
  • native/web/src/input_ffi.rs
  • src/core/index.ts
  • src/models/index.ts
  • tools/file-lines-baseline.json
💤 Files with no reviewable changes (1)
  • src/core/index.ts

Comment thread src/models/index.ts
Comment on lines +588 to +590
bloom_mesh_scratch_push_u32(
bytes[b] | (bytes[b + 1] << 8) | (bytes[b + 2] << 16) | (bytes[b + 3] << 24),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Bitwise OR produces a signed integer, causing data corruption for high alpha values.

In JavaScript, bitwise operations evaluate to a signed 32-bit integer. If the highest bit is set (bytes[b + 3] >= 128), the result becomes a negative number. When this negative number is passed as an f64 to the Rust FFI, casting it to u32 saturates to 0, completely corrupting the packed texture data for those texels. The shared root cause across these sites is the missing unsigned conversion.

  • src/models/index.ts#L588-L590: Add >>> 0 to convert the expression to an unsigned 32-bit integer: (bytes[b] | (bytes[b + 1] << 8) | (bytes[b + 2] << 16) | (bytes[b + 3] << 24)) >>> 0.
  • src/models/index.ts#L630-L632: Apply the exact same >>> 0 fix to this identical texture array repacking logic.
📍 Affects 1 file
  • src/models/index.ts#L588-L590 (this comment)
  • src/models/index.ts#L630-L632
🤖 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/models/index.ts` around lines 588 - 590, The packed texture integer
passed to bloom_mesh_scratch_push_u32 is signed when the high byte has its top
bit set; append an unsigned conversion (>>> 0) to the bitwise expression at
src/models/index.ts lines 588-590. Apply the identical fix to the matching
texture repacking logic at src/models/index.ts lines 630-632.

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