Skip to content

Fix starship render regressions and document autonomous PTY debugging#224

Closed
RtlZeroMemory wants to merge 22 commits intomainfrom
fix/starship-render-regression-followup
Closed

Fix starship render regressions and document autonomous PTY debugging#224
RtlZeroMemory wants to merge 22 commits intomainfrom
fix/starship-render-regression-followup

Conversation

@RtlZeroMemory
Copy link
Owner

@RtlZeroMemory RtlZeroMemory commented Feb 27, 2026

Summary

  • fixes remaining Starship rendering regressions introduced during the refactor cycle:
    • Crew route now renders usable manifest/detail content instead of collapsing/empty regions
    • Engineering route reactor/deck panes now stretch correctly and stay visible across viewport bands
    • route screen roots now consistently use full-height containers to avoid accidental compression
  • fixes pagination composition so controls render horizontally inside a focus zone (prevents vertical pager from stealing table height)
  • adds additional frame-audit telemetry for table layout decisions (table.layout) to speed future regressions
  • keeps worker frame-audit + native debug bridge instrumentation in sync for cross-layer tracing
  • adds a dedicated runbook for autonomous PTY-driven UI verification and references it from AGENTS.md, CLAUDE.md, and dev docs nav

Validation

  • npx tsc -b packages/core packages/create-rezi packages/node
  • npx tsx --test packages/core/src/widgets/__tests__/pagination.test.ts
  • npx tsx --test packages/create-rezi/templates/starship/src/__tests__/render.test.ts packages/create-rezi/templates/starship/src/__tests__/reducer.test.ts packages/create-rezi/templates/starship/src/__tests__/keybindings.test.ts
  • manual worker-mode PTY run with frame audit and route switching (1..6, t, q), with log analysis via:
    • node scripts/frame-audit-report.mjs /tmp/rezi-frame-audit.ndjson --latest-pid

Notes

  • Left tmp/ untracked in the worktree (not part of this PR).

Summary by CodeRabbit

  • New Features

    • Added Regression Dashboard example demonstrating service fleet monitoring and real-time telemetry collection.
    • Introduced optional frame audit logging for debugging rendering and performance issues.
    • Added comprehensive Live PTY debugging and testing runbook for validating UI behavior in terminal environments.
  • Documentation

    • Expanded developer guides with native vendor setup and live PTY testing procedures.
    • Added protocol documentation for style link references and hyperlink support.
  • Chores

    • Updated native engine dependencies and integrity checks.

RtlZeroMemory and others added 22 commits February 26, 2026 11:27
- Add concurrency group to ci.yml and codeql.yml to cancel in-progress
  runs on re-push (biggest win for runner contention)
- Extract lint/typecheck/codegen/portability/unicode into a dedicated
  `checks` job that gates the matrix — lint failures caught in ~2 min
  instead of after the full 15-min pipeline
- Dynamic matrix: 5 runners on PRs (Linux × Node 18/20/22, macOS × 22,
  Windows × 22), full 3×3 on push to main
- Remove redundant lint/typecheck/codegen/biome-install steps from each
  matrix cell and the bun job
- Remove duplicate docs job (already handled by docs.yml)

Net effect on PRs: 13 jobs → 9, ~44% fewer runners, fast-fail on
static checks, stale runs cancelled automatically.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ized-renderer

# Conflicts:
#	packages/core/src/abi.ts
#	packages/core/src/app/__tests__/partialDrawlistEmission.test.ts
#	packages/core/src/app/createApp.ts
#	packages/core/src/app/rawRenderer.ts
#	packages/core/src/app/widgetRenderer.ts
#	packages/core/src/drawlist/__tests__/builder.build-into.test.ts
#	packages/core/src/drawlist/__tests__/builder.cursor.test.ts
#	packages/core/src/drawlist/__tests__/builder.round-trip.test.ts
#	packages/core/src/drawlist/__tests__/writers.gen.test.ts
#	packages/core/src/drawlist/builder.ts
#	packages/core/src/drawlist/builder_v1.ts
#	packages/core/src/drawlist/builder_v2.ts
#	packages/core/src/drawlist/index.ts
#	packages/core/src/drawlist/types.ts
#	packages/core/src/index.ts
#	packages/core/src/renderer/renderToDrawlist/widgets/renderCanvasWidgets.ts
#	packages/node/src/__tests__/config_guards.test.ts
#	packages/node/src/backend/nodeBackend.ts
#	packages/node/src/backend/nodeBackendInline.ts
…ized-renderer

# Conflicts:
#	packages/core/src/__tests__/integration/integration.dashboard.test.ts
#	packages/core/src/__tests__/integration/integration.file-manager.test.ts
#	packages/core/src/__tests__/integration/integration.form-editor.test.ts
#	packages/core/src/__tests__/integration/integration.reflow.test.ts
#	packages/core/src/__tests__/integration/integration.resize.test.ts
#	packages/core/src/__tests__/stress/stress.large-trees.test.ts
#	packages/core/src/abi.ts
#	packages/core/src/app/__tests__/partialDrawlistEmission.test.ts
#	packages/core/src/app/__tests__/rawRender.test.ts
#	packages/core/src/app/__tests__/widgetRenderer.integration.test.ts
#	packages/core/src/app/createApp.ts
#	packages/core/src/app/rawRenderer.ts
#	packages/core/src/app/widgetRenderer.ts
#	packages/core/src/drawlist/__tests__/builder.alignment.test.ts
#	packages/core/src/drawlist/__tests__/builder.golden.test.ts
#	packages/core/src/drawlist/__tests__/builder.graphics.test.ts
#	packages/core/src/drawlist/__tests__/builder.limits.test.ts
#	packages/core/src/drawlist/__tests__/builder.reset.test.ts
#	packages/core/src/drawlist/__tests__/builder.round-trip.test.ts
#	packages/core/src/drawlist/__tests__/builder.string-cache.test.ts
#	packages/core/src/drawlist/__tests__/builder.string-intern.test.ts
#	packages/core/src/drawlist/__tests__/builder.style-encoding.test.ts
#	packages/core/src/drawlist/__tests__/builder.text-run.test.ts
#	packages/core/src/drawlist/__tests__/builder.validate-caps.test.ts
#	packages/core/src/drawlist/__tests__/builder_style_attrs.test.ts
#	packages/core/src/drawlist/__tests__/builder_v6_resources.test.ts
#	packages/core/src/drawlist/builder.ts
#	packages/core/src/drawlist/builderBase.ts
#	packages/core/src/drawlist/index.ts
#	packages/core/src/drawlist/types.ts
#	packages/core/src/index.ts
#	packages/core/src/renderer/__tests__/focusIndicators.test.ts
#	packages/core/src/renderer/__tests__/overlay.edge.test.ts
#	packages/core/src/renderer/__tests__/persistentBlobKeys.test.ts
#	packages/core/src/renderer/__tests__/recipeRendering.test-utils.ts
#	packages/core/src/renderer/__tests__/render.golden.test.ts
#	packages/core/src/renderer/__tests__/renderer.border.test.ts
#	packages/core/src/renderer/__tests__/renderer.clip.test.ts
#	packages/core/src/renderer/__tests__/renderer.damage.test.ts
#	packages/core/src/renderer/__tests__/renderer.partial.perf.test.ts
#	packages/core/src/renderer/__tests__/renderer.scrollbar.test.ts
#	packages/core/src/renderer/__tests__/renderer.text.test.ts
#	packages/core/src/renderer/renderToDrawlist/renderTree.ts
#	packages/core/src/renderer/renderToDrawlist/widgets/renderCanvasWidgets.ts
#	packages/core/src/testing/renderer.ts
#	packages/core/src/theme/__tests__/theme.extend.test.ts
#	packages/core/src/theme/__tests__/theme.switch.test.ts
#	packages/core/src/ui/__tests__/themed.test.ts
#	packages/core/src/widgets/__tests__/basicWidgets.render.test.ts
#	packages/core/src/widgets/__tests__/graphics.golden.test.ts
#	packages/core/src/widgets/__tests__/graphicsWidgets.test.ts
#	packages/core/src/widgets/__tests__/renderer.regressions.test.ts
#	packages/core/src/widgets/__tests__/style.utils.test.ts
#	packages/core/src/widgets/__tests__/styleUtils.test.ts
#	packages/core/src/widgets/__tests__/widgetRenderSmoke.test.ts
#	packages/ink-compat/src/runtime/createInkRenderer.ts
#	packages/native/vendor/VENDOR_COMMIT.txt
#	packages/native/vendor/zireael/include/zr/zr_drawlist.h
#	packages/native/vendor/zireael/src/core/zr_drawlist.c
#	packages/native/vendor/zireael/src/core/zr_engine.c
#	packages/native/vendor/zireael/src/core/zr_engine_present.inc
#	packages/node/src/backend/nodeBackend.ts
#	packages/testkit/fixtures/zrdl-v1/golden/draw_text_interned.bin
@coderabbitai
Copy link

coderabbitai bot commented Feb 27, 2026

📝 Walkthrough

Walkthrough

This pull request introduces comprehensive framework enhancements including a frame auditing system for diagnostics, native vendor integrity validation, drawlist format upgrades with string/blob definitions and link references, new regression dashboard example, native Zireael vendor updates, and extensive renderer/layout refinements. Changes span documentation, CI automation, core rendering, native FFI, and template updates.

Changes

Cohort / File(s) Summary
CI and Native Vendor Integration
.github/workflows/ci.yml, package.json, scripts/check-native-vendor-integrity.mjs, scripts/__tests__/check-native-vendor-integrity.test.mjs, packages/native/vendor/VENDOR_COMMIT.txt
Added npm run check:native-vendor CI step and supporting validation script to enforce vendor pin consistency between VENDOR_COMMIT.txt and repo gitlink; updated vendor commit pin.
Documentation: Native and Build
docs/backend/native.md, docs/dev/build.md, docs/dev/repo-layout.md, packages/native/README.md
Clarified native vendor handling, submodule requirements for syncing/auditing, and added vendor integrity check documentation.
Documentation: Live PTY Debugging
docs/dev/live-pty-debugging.md, docs/dev/testing.md, mkdocs.yml, AGENTS.md, CLAUDE.md
Added comprehensive live PTY UI testing runbook with frame-audit instrumentation, deterministic viewport setup, and cross-layer telemetry analysis guidance.
Documentation: Protocol Specifications
docs/protocol/versioning.md, docs/protocol/zrdl.md
Documented stable style link reference fields and new link-reference wire contract in ZRDL v1/v2.
Regression Dashboard Example
examples/regression-dashboard/*
Complete new interactive dashboard example with state management, theming, keybindings, telemetry, and multi-screen rendering; includes TypeScript types, helpers, tests, and package configuration.
Frame Audit Infrastructure
packages/core/src/perf/frameAudit.ts, packages/node/src/frameAudit.ts, packages/node/src/backend/nodeBackendInline.ts, packages/node/src/backend/nodeBackend.ts, packages/node/src/worker/engineWorker.ts
New frame auditing system providing optional NDJSON logging, binary dumps, fingerprinting (hash32, opcode histograms), and multi-layer frame-lifecycle tracking; integrates into backend and worker paths.
Drawlist Parsing and Testing
packages/core/src/__tests__/drawlistDecode.ts, packages/core/src/__tests__/integration/*.test.ts, packages/core/src/drawlist/__tests__/*.test.ts
New drawlist decoder module with full command/resource parsing; refactored tests to use centralized parsing utilities and new string/blob definition format; removed legacy header-based string/blob offset calculations.
Drawlist Builder Refactoring
packages/core/src/drawlist/builder.ts, packages/core/src/drawlist/builderBase.ts, packages/core/src/drawlist/writers.gen.ts
Major builder refactor: replaced text-arena with UTF-8 encoder-based string interning; introduced resource-definition serialization (DEF_STRING/DEF_BLOB); implemented plan/write separation; added link validation and 1-based string/blob referencing.
Drawlist ABI Updates
packages/core/src/abi.ts, packages/core/src/index.ts, packages/core/src/app/createApp.ts
Removed ZR_DRAWLIST_VERSION_V2 export; narrowed readBackendDrawlistVersionMarker return type to 1 | null.
Core Rendering and Audit
packages/core/src/app/rawRenderer.ts, packages/core/src/app/widgetRenderer.ts, packages/core/src/app/widgetRenderer/damageTracking.ts
Integrated frame auditing with fingerprinting; enhanced layout shape-mismatch validation and relayout logic; refactored damage tracking to remove markSelfDirty parameter and improve dirty propagation for identity-equal nodes; wired beginFrame marker-based submission flow.
Rendering: Overflow Culling and Containers
packages/core/src/renderer/renderToDrawlist/overflowCulling.ts, packages/core/src/renderer/renderToDrawlist/widgets/containers.ts, packages/core/src/renderer/renderToDrawlist/widgets/collections.ts
New overflow-culling module to detect subtree boundary violations; refactored container rendering to use conditional clip pushes via pushChildClipIfNeeded; integrated audit events into table/collection rendering.
Text and Style Rendering
packages/core/src/renderer/__tests__/renderer.text.test.ts, packages/core/src/widgets/__tests__/style.*.test.ts, packages/core/src/widgets/__tests__/basicWidgets.render.test.ts
Updated text rendering tests to use new string-resource decoding; refactored style validation and theme tests to use packed Rgb24 integers instead of object-based RGB; added attrs bitfield to text style validation.
Widget and Theme Updates
packages/core/src/widgets/ui.ts, packages/core/src/theme/validate.ts, packages/core/src/theme/resolve.ts, packages/core/src/widgets/pagination.ts, packages/core/src/layout/kinds/overlays.ts
Updated documentation examples to use rgb(...) helper; refactored RGB validation from per-channel objects to packed 24-bit integers; wrapped pagination controls in row layout; simplified border validation.
Canvas/Blob Rendering
packages/core/src/renderer/__tests__/persistentBlobKeys.test.ts, packages/core/src/renderer/renderToDrawlist/widgets/renderCanvasWidgets.ts
Removed 4-byte blob alignment padding; exported addBlobAligned for test usage; simplified blob handling.
Renderer Test Cleanup
packages/core/src/renderer/__tests__/renderer.partial.test.ts, packages/core/src/renderer/__tests__/renderer.scrollBlit.benchmark.test.ts, packages/ink-compat/src/runtime/createInkRenderer.ts
Removed blitRect operation support; eliminated scroll-blit benchmark harness; removed sequence scenario and logs-related test infrastructure.
Commit and Runtime
packages/core/src/runtime/commit.ts, packages/core/src/runtime/__tests__/commit.fastReuse.regression.test.ts
Enhanced fast-reuse container path to rewrite committed vnode children in-place when all children are reused; updated test expectations to verify in-place updates with dirty flags.
Native FFI and Zireael Vendor
packages/native/src/lib.rs, packages/native/vendor/zireael/**/*.h, packages/native/vendor/zireael/**/*.c
Expanded public FFI structs with link support (zr_fb_link_t, link fields in zr_fb_t, zr_style_t), new link intern/clone/lookup APIs, terminal caps extensions, underline RGB; added C++ linkage guards to all vendor headers; added damage rect _link field, framebuffer sync state for rollback, and link-aware diff logic; added link compact/intern helpers and wide-glyph safe blitting.
Starship Template: Debug and Layout
packages/create-rezi/templates/starship/src/main.ts, packages/create-rezi/templates/starship/src/screens/*.ts, packages/create-rezi/templates/starship/src/theme.ts
Introduced debug tracing subsystem, execution-mode selection, frame/drawlist snapshot helpers, viewport clamping, and audit context wiring; refactored screen layouts (crew, engineering, bridge, cargo, settings) with explicit height/width constraints and responsive deck sizing; migrated theme colors from Rgb object to Rgb24 packed integers.
Ink Compatibility
packages/ink-compat/src/runtime/createInkRenderer.ts
Disabled blitRect operation support in ink renderer; made method a no-op while maintaining interface compatibility.
Scripts and Analysis
scripts/frame-audit-report.mjs, scripts/generate-drawlist-writers.ts
New audit report analyzer for NDJSON logs with multi-pid support, relationship validation, and opcode summaries; updated writer generator to handle explicit zero-padding in trailing-bytes paths.

Sequence Diagram(s)

sequenceDiagram
    participant App as App/WidgetRenderer
    participant Audit as Frame Audit
    participant Builder as DrawlistBuilder
    participant Backend as NodeBackend
    participant Worker as EngineWorker
    
    App->>Builder: planResourceStream()
    Builder-->>App: plan (strings/blobs/cmds)
    
    App->>Builder: writeBuiltStream(buffer, plan)
    Builder-->>App: drawlist bytes
    
    App->>Audit: drawlistFingerprint(bytes)
    Audit-->>App: hash32, opcode histogram
    
    App->>Audit: emitFrameAudit("drawlist.built", {...fingerprint})
    Audit-->>Audit: log to NDJSON file
    
    App->>Backend: beginFrame() or submitFrame(bytes)
    Backend->>Worker: publish frame via SAB
    Worker->>Audit: emitFrameAudit("frame.received", {...})
    Audit-->>Audit: log per-frame metadata
    
    Worker->>Audit: drawlistFingerprint(received bytes)
    Audit-->>Worker: fingerprint
    
    Worker-->>Backend: frame.accepted ACK
    Backend-->>App: inFlight promise resolves
    
    Worker->>Audit: emitFrameAudit("frame.completed", {...})
    Audit-->>Audit: finalize frame audit
Loading
sequenceDiagram
    participant CI as CI Workflow
    participant Script as check-native-vendor-integrity
    participant FS as Filesystem
    participant Git as Git
    
    CI->>Script: npm run check:native-vendor
    Script->>FS: read packages/native/vendor/VENDOR_COMMIT.txt
    FS-->>Script: pinned commit hash
    
    Script->>Git: git rev-parse HEAD:vendor/zireael
    Git-->>Script: gitlink commit hash
    
    Script->>FS: read packages/native/build.rs
    FS-->>Script: build.rs content
    
    alt pinned == gitlink
        Script->>Script: validate build.rs paths
        Script-->>CI: ✓ OK + commit short hash
    else pinned != gitlink
        Script->>Script: collect mismatch details
        Script-->>CI: ✗ FAIL + error message
        CI->>CI: exit code 1
    end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • #213: Adds and wires BACKEND_BEGIN_FRAME_MARKER/BackendBeginFrame zero-copy API into core/node backends and updates renderer submission flow.
  • #223: Modifies the same native vendor integrity tooling, CI steps, and vendor pin updates.
  • #218: Contains overlapping drawlist builder/base changes, frame text-arena refactor, and getTextPerfCounters API additions.

Poem

🐰 Frame by Frame, We Hop Along

With audit logs and vendor pins held tight,
Our drawlist strings now dance in the light,
From core to node, the frames take flight,
A dashboard blooms—regression's in sight! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the main changes: fixing Starship rendering regressions and adding autonomous PTY debugging documentation. It is specific enough to understand the primary focus.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/starship-render-regression-followup

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

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

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: d55b8a4e1b

ℹ️ 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 on lines 757 to +758
await app.start();
await new Promise<void>(() => {});
await stopPromise;

Choose a reason for hiding this comment

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

P1 Badge Skip app startup when shutdown was already requested

A SIGINT/SIGTERM can call stopApp() before startup finishes, which resolves stopPromise and sets stopping=true, but this bootstrap path still unconditionally executes await app.start() afterward. In that sequence, the app can be started after shutdown was requested, and the stopping guard prevents a second shutdown path, leaving backend resources running and the process hanging instead of exiting cleanly. Add a guard/race so startup is skipped once stopping is set.

Useful? React with 👍 / 👎.

Comment on lines 745 to 747
DrawlistBuilder,
DrawlistTextPerfCounters,
DrawlistImageFit,
DrawlistImageFormat,

Choose a reason for hiding this comment

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

P2 Badge Re-export DrawlistTextPerfCounters in the public API

DrawlistTextPerfCounters is still defined and exported from packages/core/src/drawlist/index.ts, but it was removed from the top-level @rezi-ui/core type export list here. That is a source-compatible API regression: downstream code importing this type from @rezi-ui/core will now fail TypeScript builds after this commit.

Useful? React with 👍 / 👎.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 19

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/create-rezi/templates/starship/src/main.ts (1)

734-739: ⚠️ Potential issue | 🟠 Major

Prevent start-after-stop race during bootstrap.

A signal can call stopApp() before startup completes, but bootstrap still proceeds into app.start(). That can re-enter runtime after shutdown was already requested.

🛠️ Proposed fix
 await setupDebugTrace();
-await app.start();
-await stopPromise;
+if (!stopping) {
+  await app.start();
+  await stopPromise;
+}
 if (stopCode !== 0) {
   process.exitCode = stopCode;
 }

Based on learnings: all changes must follow docs/dev/code-standards.md, including async cancellation guards.

Also applies to: 756-761

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/create-rezi/templates/starship/src/main.ts` around lines 734 - 739,
The onSignal handler can call stopApp() while bootstrap is still running,
allowing bootstrap to continue into app.start() after shutdown was requested;
modify bootstrap logic so startup checks a cancellation flag or promise set by
onSignal/stopApp before and immediately after awaiting any async initialization,
and refuse to call app.start() (or abort startup) if shutdownRequested is true;
update onSignal/stopApp to set this shared shutdownRequested flag (or resolve a
cancellation promise) and ensure app.start() is only invoked when the flag is
false (apply same guard to the other startup lines referenced by symbols
onSignal, stopApp, and app.start).
packages/node/src/worker/engineWorker.ts (1)

1573-1583: ⚠️ Potential issue | 🟡 Minor

Avoid clearing audit-enabled state when disable fails.

At Line [1582], nativeFrameAuditEnabled is set to false even if engineDebugDisable returns an error code. That can desync worker-side drain behavior from native state.

🔧 Proposed fix
-      nativeFrameAuditEnabled = false;
+      if (rc >= 0) {
+        nativeFrameAuditEnabled = false;
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/node/src/worker/engineWorker.ts` around lines 1573 - 1583, The code
unconditionally clears nativeFrameAuditEnabled after calling
native.engineDebugDisable even when that call failed (rc != 0), which can desync
worker vs native state; in the "debug:disable" case (around engineDebugDisable,
rc, engineId, nativeFrameAuditEnabled, frameAudit.enabled) only set
nativeFrameAuditEnabled = false when engineDebugDisable returned success (e.g.,
rc === 0), otherwise leave the flag untouched (and preserve existing error
handling/return path).
packages/core/src/renderer/renderToDrawlist/widgets/collections.ts (1)

1-31: ⚠️ Potential issue | 🟡 Minor

Fix import ordering to unblock CI.

Current import ordering in this block trips the pipeline import-sort check; please reorder this new perf import to match the project’s expected sort/grouping.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/renderer/renderToDrawlist/widgets/collections.ts` around
lines 1 - 31, The import of emitFrameAudit and FRAME_AUDIT_ENABLED is misordered
and fails the import-sort check; move the "import { emitFrameAudit,
FRAME_AUDIT_ENABLED } from '../../../perf/frameAudit.js';" line into the main
block of local/project imports so it is grouped with the other "../../../..."
imports and alphabetically ordered with them (place it among the other non-type
imports such as distributeColumnWidths, tableRecipe, treeRecipe, and the
widgets/virtualList imports) to satisfy the project's import ordering rules.
🧹 Nitpick comments (14)
packages/core/src/runtime/commit.ts (1)

1337-1341: Gate parent VNode rewrites on child dirtiness to preserve the no-churn fast path.

Good fix for stale child wiring. On Line 1340 and Line 1341, the map(...) + rewrite now runs every fast-reuse cycle, even when no child changed.

♻️ Proposed tweak
-        const fastReuseCommittedChildren = prev.children.map((child) => child.vnode);
-        (prev as { vnode: VNode }).vnode = rewriteCommittedVNode(vnode, fastReuseCommittedChildren);
+        const childDirty = hasDirtyChild(prev.children);
+        if (childDirty) {
+          const fastReuseCommittedChildren = prev.children.map((child) => child.vnode);
+          (prev as { vnode: VNode }).vnode = rewriteCommittedVNode(vnode, fastReuseCommittedChildren);
+        }
@@
-        prev.dirty = hasDirtyChild(prev.children);
+        prev.dirty = childDirty;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/runtime/commit.ts` around lines 1337 - 1341, The rewrite of
the parent VNode’s committed children runs every fast-reuse cycle and should be
skipped when none of the child wiring changed; guard the work by checking child
dirtiness or reference differences before mapping and calling
rewriteCommittedVNode. Concretely: in the fast-reuse path around
prev.children.map(...) and rewriteCommittedVNode(vnode,...), first test whether
any child is dirty or whether prev.children’ committed vnode references differ
from the current vnode.children (e.g. prev.children.some(c => c.dirty) ||
!prev.children.every((c,i) => c.vnode === vnode.children[i])); only if that
predicate is true compute fastReuseCommittedChildren and call
rewriteCommittedVNode. Ensure you reference the existing symbols prev,
prev.children, vnode, fastReuseCommittedChildren, and rewriteCommittedVNode when
making the change.
packages/core/src/renderer/renderToDrawlist/overflowCulling.ts (1)

90-109: Add focused regression tests for wrapper traversal/culling edge cases

This traversal is in renderer culling logic and uses early exits plus wrapper-gated descent. Please add tests for: pass-through layer frame styles, focusZone/focusTrap/themed/layers wrapper chains, and shadow object defaults ({} vs explicit zeros).

Based on learnings: Changes to these danger zones require extra care due to risk of subtle regressions affecting all rendering: packages/core/src/renderer/.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/renderer/renderToDrawlist/overflowCulling.ts` around lines
90 - 109, Add focused regression tests exercising subtreeCanOverflowBounds
traversal: create scenarios that include pass-through "layer" frame styles,
wrapper chains (focusZone, focusTrap, themed, layers) to verify
isTransparentOverflowWrapper correctly gates descent, and cases where boxShadow
is {} versus explicit zero values to ensure hasBoxShadowOverflow treats defaults
and zeros equivalently; confirm that hasVisibleOverflow and hasBoxShadowOverflow
return true for visible/shadowing children and that traversal still
short-circuits on those checks while continuing descent only through transparent
wrappers.
packages/core/src/widgets/__tests__/style.inheritance.test.ts (1)

35-47: Consider replacing bit-shift literals with named attr constants.

The helper is correct, but named constants would make the bit mapping easier to audit and safer to maintain.

Refactor sketch
 function computeExpectedAttrs(style: Readonly<TextStyle>): number {
+  const ATTR_BOLD = 1 << 0;
+  const ATTR_ITALIC = 1 << 1;
+  const ATTR_UNDERLINE = 1 << 2;
+  const ATTR_INVERSE = 1 << 3;
+  const ATTR_DIM = 1 << 4;
+  const ATTR_STRIKETHROUGH = 1 << 5;
+  const ATTR_OVERLINE = 1 << 6;
+  const ATTR_BLINK = 1 << 7;
   let attrs = 0;
-  if (style.bold) attrs |= 1 << 0;
-  if (style.italic) attrs |= 1 << 1;
+  if (style.bold) attrs |= ATTR_BOLD;
+  if (style.italic) attrs |= ATTR_ITALIC;
   if (style.underline || (style.underlineStyle !== undefined && style.underlineStyle !== "none")) {
-    attrs |= 1 << 2;
+    attrs |= ATTR_UNDERLINE;
   }
-  if (style.inverse) attrs |= 1 << 3;
-  if (style.dim) attrs |= 1 << 4;
-  if (style.strikethrough) attrs |= 1 << 5;
-  if (style.overline) attrs |= 1 << 6;
-  if (style.blink) attrs |= 1 << 7;
+  if (style.inverse) attrs |= ATTR_INVERSE;
+  if (style.dim) attrs |= ATTR_DIM;
+  if (style.strikethrough) attrs |= ATTR_STRIKETHROUGH;
+  if (style.overline) attrs |= ATTR_OVERLINE;
+  if (style.blink) attrs |= ATTR_BLINK;
   return attrs >>> 0;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/widgets/__tests__/style.inheritance.test.ts` around lines
35 - 47, Replace the magic bit-shift literals in computeExpectedAttrs with named
constants (e.g., ATTR_BOLD = 1<<0, ATTR_ITALIC = 1<<1, ATTR_UNDERLINE = 1<<2,
etc.) declared near the top of the test file, then use those constants inside
computeExpectedAttrs instead of the raw 1<<n values so the mapping
(bold/italic/underline/inverse/dim/strikethrough/overline/blink) is explicit and
easier to audit/maintain; ensure the final return still uses unsigned shift
(attrs >>> 0) unchanged.
packages/native/src/lib.rs (3)

1709-1731: Inconsistent indentation in test helper.

Similar to the struct definitions, this initialization block uses tab characters. Consider normalizing to spaces for consistency with the rest of the file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/native/src/lib.rs` around lines 1709 - 1731, The test helper uses
tab characters in the struct initialization blocks (e.g., the caps assignment
for ffi::plat_caps_t, the limits retrieval via ffi::zr_engine_config_default(),
and the initial_term_state for ffi::zr_term_state_t); update those lines to use
spaces (matching the file's existing indentation style) so the caps, limits, and
initial_term_state initializers are consistently space-indented with the
surrounding code.

116-155: Inconsistent indentation: tabs mixed with spaces.

These struct definitions use tab characters for indentation while the rest of the file uses spaces. This inconsistency affects readability and may cause issues with automated formatting tools.

🔧 Suggested fix: normalize to spaces
-	  #[repr(C)]
-	  #[derive(Copy, Clone)]
-	  pub struct zr_terminal_caps_t {
+  #[repr(C)]
+  #[derive(Copy, Clone)]
+  pub struct zr_terminal_caps_t {
     pub color_mode: u8,
     ...
-	  }
-
-	  #[repr(C)]
-	  #[derive(Copy, Clone)]
-	  pub struct plat_caps_t {
-	    pub color_mode: u8,
+  }
+
+  #[repr(C)]
+  #[derive(Copy, Clone)]
+  pub struct plat_caps_t {
+    pub color_mode: u8,
     ...
-	  }
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/native/src/lib.rs` around lines 116 - 155, The indentation in the
struct definitions zr_terminal_caps_t and plat_caps_t uses tab characters mixed
with spaces; replace all leading tab characters inside these struct blocks with
the project's standard spaces indentation to match the rest of the file (align
field names and types consistently), and reformat the file (e.g., run rustfmt)
to ensure consistent spacing throughout.

1855-1858: Hardcoded constant may drift from vendored headers.

LINK_ENTRY_MAX_BYTES appears to derive from ZR_FB_LINK_URI_MAX_BYTES + ZR_FB_LINK_ID_MAX_BYTES (2083 + 2083). If the vendored header values change, this test's assertions could become misleading without any compile-time warning.

Consider adding a comment documenting the derivation, or if feasible, importing the constants via FFI to keep them synchronized.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/native/src/lib.rs` around lines 1855 - 1858, The test
fb_link_intern_compacts_stale_refs_and_bounds_growth uses a hardcoded
LINK_ENTRY_MAX_BYTES (2083 + 2083) that mirrors ZR_FB_LINK_URI_MAX_BYTES +
ZR_FB_LINK_ID_MAX_BYTES and may drift from vendored headers; update the test by
either (a) adding a clear comment on LINK_ENTRY_MAX_BYTES documenting its
derivation from ZR_FB_LINK_URI_MAX_BYTES and ZR_FB_LINK_ID_MAX_BYTES and why
those specific values are used, or (b) preferably import the two upstream
constants via FFI and compute LINK_ENTRY_MAX_BYTES from them inside the test
(referencing the test function name and the LINK_ENTRY_MAX_BYTES symbol) so the
values stay synchronized with the vendored headers.
packages/core/src/renderer/renderToDrawlist/widgets/renderCanvasWidgets.ts (1)

225-227: Function name addBlobAligned creates minor semantic confusion about when alignment occurs.

While the function does result in 4-byte aligned blobs (via writeDefBlob's align4() call during serialization), the name suggests alignment happens at the addition stage. Alignment actually occurs during drawlist finalization. This is correct behavior, but renaming to addBlob for clarity would be reasonable since the API contract is maintained at the serialization layer, or document that "Aligned" refers to the final serialized form.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/renderer/renderToDrawlist/widgets/renderCanvasWidgets.ts`
around lines 225 - 227, Rename the confusing addBlobAligned API to addBlob (and
update any imports/uses) because alignment actually happens during serialization
via writeDefBlob().align4(), not at add time; update the export and all call
sites to use addBlob, and optionally add a brief comment in
renderCanvasWidgets.ts near the new addBlob function clarifying that 4-byte
alignment is applied during drawlist finalization/serialization
(writeDefBlob.align4()) rather than at addition.
packages/create-rezi/templates/starship/src/screens/engineering.ts (1)

394-397: Extract min-height offsets into named constants.

The arithmetic works, but the inline 12/10/2 values are hard to tune later.

♻️ Proposed refactor
+  const CONTROL_ROWS_WITH_SUMMARY = 12;
+  const CONTROL_ROWS_BASE = 10;
+  const SINGLE_PANE_OFFSET = 2;
+
   const responsiveDeckMinHeight = Math.max(
     16,
-    contentRows - (showControlsSummary ? 12 : 10) - (showSecondaryPanels ? 0 : 2),
+    contentRows -
+      (showControlsSummary ? CONTROL_ROWS_WITH_SUMMARY : CONTROL_ROWS_BASE) -
+      (showSecondaryPanels ? 0 : SINGLE_PANE_OFFSET),
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/create-rezi/templates/starship/src/screens/engineering.ts` around
lines 394 - 397, The inline magic numbers in the responsiveDeckMinHeight
calculation (the 12, 10 and 2) should be extracted into named constants for
clarity and easier tuning: define e.g. CONTROLS_SUMMARY_OFFSET (12),
CONTROLS_OFFSET (10) and SECONDARY_PANELS_OFFSET (2) near the top of the module,
then update the responsiveDeckMinHeight expression to use these constants with
contentRows, showControlsSummary and showSecondaryPanels so the formula reads
self-documenting and the offsets can be tuned without changing the arithmetic.
packages/core/src/renderer/__tests__/persistentBlobKeys.test.ts (1)

89-94: Consider adding the null allocation branch test for addBlobAligned.

Current coverage validates the success path well; a failure-path assertion would lock expected behavior when addBlob(...) returns null.

Optional follow-up test
@@
   test("addBlobAligned forwards exact payload length", () => {
     const builder = new CountingBuilder();
     const blobId = addBlobAligned(builder, new Uint8Array([1, 2, 3]));
     assert.equal(blobId, 0);
     assert.deepEqual(builder.blobByteLens, [3]);
   });
+
+  test("addBlobAligned propagates null when blob allocation fails", () => {
+    class NullBlobBuilder extends CountingBuilder {
+      addBlob(_bytes: Uint8Array): number | null {
+        return null;
+      }
+    }
+    const builder = new NullBlobBuilder();
+    const blobId = addBlobAligned(builder, new Uint8Array([1, 2, 3]));
+    assert.equal(blobId, null);
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/renderer/__tests__/persistentBlobKeys.test.ts` around lines
89 - 94, Add a test that covers the "null allocation" branch of addBlobAligned
by creating or mocking a CountingBuilder whose addBlob method returns null, then
call addBlobAligned(builder, new Uint8Array([1,2,3])) and assert the function
returns null (or the documented failure value) and that builder.blobByteLens was
not changed; reference addBlobAligned, addBlob, and CountingBuilder in the test
to locate and implement the behavior check.
packages/core/src/drawlist/__tests__/builder_v6_resources.test.ts (1)

4-6: Prefer reusing shared u32 test utility to avoid drift.

You already have u32 in packages/core/src/__tests__/drawlistDecode.ts; importing it here would reduce duplicate parsing helpers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/drawlist/__tests__/builder_v6_resources.test.ts` around
lines 4 - 6, Replace the duplicated u32 implementation in
builder_v6_resources.test.ts by importing and reusing the shared u32 test helper
from the drawlistDecode test module; remove the local function u32 (the one that
constructs a DataView and calls getUint32) and add an import for the exported
u32 helper from the drawlistDecode test module so all tests use the same parsing
utility.
packages/core/src/app/createApp.ts (1)

596-603: Remove redundant drawlist-version check after marker parsing.

readBackendDrawlistVersionMarker() already throws for non-1 values and only returns 1 | null, so this branch is now unreachable.

♻️ Proposed cleanup
   const backendDrawlistVersion = readBackendDrawlistVersionMarker(backend);
-  if (backendDrawlistVersion !== null && backendDrawlistVersion !== 1) {
-    invalidProps(
-      `backend drawlistVersion=${String(
-        backendDrawlistVersion,
-      )} is invalid. Fix: set backend drawlist version marker to 1.`,
-    );
-  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/app/createApp.ts` around lines 596 - 603, The branch
checking backendDrawlistVersion after calling readBackendDrawlistVersionMarker
is redundant because readBackendDrawlistVersionMarker already throws for non-1
values and only returns 1 | null; remove the entire if block that references
backendDrawlistVersion and invalidProps to eliminate unreachable code (i.e.,
delete the const check starting with "if (backendDrawlistVersion !== null &&
backendDrawlistVersion !== 1) { ... }"). Ensure no other logic depends on that
removed block and keep the const backendDrawlistVersion assignment if it's still
used elsewhere.
examples/regression-dashboard/src/__tests__/keybindings.test.ts (1)

5-12: LGTM! Good keybinding resolution coverage.

The test validates canonical mappings and includes a negative case for unmapped keys.

Consider adding a case-insensitivity assertion since resolveDashboardCommand uses key.toLowerCase() internally:

💡 Optional: Add case-insensitivity test
   assert.equal(resolveDashboardCommand("t"), "cycle-theme");
   assert.equal(resolveDashboardCommand("x"), undefined);
+  // Case-insensitivity
+  assert.equal(resolveDashboardCommand("Q"), "quit");
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/regression-dashboard/src/__tests__/keybindings.test.ts` around lines
5 - 12, Add a case-insensitivity assertion to the existing keybinding test: call
resolveDashboardCommand with an uppercase key (e.g., "Q" or "J") and assert it
resolves to the same canonical command as the lowercase variant (e.g., "quit" or
"move-down") to verify the internal key.toLowerCase() behavior in
resolveDashboardCommand.
docs/dev/build.md (1)

38-41: Clarify optionality against the earlier setup step.

Line 38 says submodules are only required for sync/audit workflows, but the earlier setup still reads as unconditional. Add a short “TS-only path can skip submodule init” note to remove ambiguity.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/dev/build.md` around lines 38 - 41, The paragraph about the
vendor/zireael submodule is ambiguous about whether submodule init is required
for all developers; update docs/dev/build.md to add a short clarifying note
(adjacent to the earlier setup step and in the paragraph mentioning
"vendor/zireael" and "Native addon builds") that explicitly states the TS-only
development path can skip submodule initialization (i.e., submodule checkout is
only required for native addon builds, syncing, or auditing vendored engine
updates), and phrase it as a brief optional step so readers know they can skip
submodule init for TypeScript-only work.
examples/regression-dashboard/src/helpers/formatters.ts (1)

38-46: Consider single-pass iteration for efficiency.

fleetCounts iterates the services array three times. For a dashboard example this is fine, but a single-pass approach would be more efficient if the service list grows large.

♻️ Optional single-pass implementation
 export function fleetCounts(
   services: readonly Service[],
 ): Readonly<{ healthy: number; warning: number; down: number }> {
-  return Object.freeze({
-    healthy: services.filter((service) => service.status === "healthy").length,
-    warning: services.filter((service) => service.status === "warning").length,
-    down: services.filter((service) => service.status === "down").length,
-  });
+  const counts = { healthy: 0, warning: 0, down: 0 };
+  for (const service of services) {
+    counts[service.status]++;
+  }
+  return Object.freeze(counts);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/regression-dashboard/src/helpers/formatters.ts` around lines 38 -
46, The function fleetCounts currently iterates services three times causing
unnecessary work; change fleetCounts to compute healthy/warning/down in a single
pass by iterating once (e.g., use a for loop or Array.prototype.reduce over
services) and increment the appropriate counters, then return Object.freeze with
those counts; update the implementation of fleetCounts to reference the same
return shape ({ healthy, warning, down }) and preserve Readonly typing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/dev/live-pty-debugging.md`:
- Around line 24-25: Replace the hardcoded absolute path "/home/k3nig/Rezi" used
in the two doc examples (lines showing the cd command) with a portable
instruction to run the commands from the repository root (e.g., "change into the
repo root" or "from your project root run the build commands"); update both
occurrences (the cd line at ~24 and the similar lines at ~37-38) so contributors
are instructed to be in the repo root rather than using the specific local path.

In `@packages/core/src/__tests__/drawlistDecode.ts`:
- Around line 273-283: The DEF_/FREE_ handlers (e.g., case OP_DEF_STRING and
OP_FREE_STRING) read fields at offsets like cmd.offset + 8/12 and slice payloads
without validating the command length, so add bounds checks before any u32 reads
or bytes.subarray calls: verify that cmd.size (or computed remaining =
bytes.length - cmd.offset) is at least the minimum expected bytes for the
specific opcode (header + field offsets + byteLen) and that dataStart/dataEnd
live within bytes.length; if validation fails, skip the command (or return an
error) instead of calling u32(...) or strings.set/delete. Apply the same
validation pattern to the other DEF_/FREE handlers referenced around lines
326-336 to prevent out-of-bounds reads.

In `@packages/core/src/__tests__/integration/integration.reflow.test.ts`:
- Around line 11-19: The test imports internal helpers (OP_CLEAR, OP_DRAW_TEXT,
OP_FILL_RECT, OP_PUSH_CLIP, parseCommandHeaders, parseDrawTextCommands,
parseInternedStrings) via a relative path; replace that relative import in
integration.reflow.test.ts with an import from the package entrypoint export
(e.g., import these symbols from the package named export such as `@rezi-ui/core`)
and ensure those helpers are re-exported from the package entry module so the
test only imports from the public package API rather than ../drawlistDecode.js.

In `@packages/core/src/app/widgetRenderer.ts`:
- Around line 3963-3982: The begin-frame path must always release the
frameWriter and convert synchronous commit exceptions into a backend error
return: wrap the call to frameWriter.commit(submittedBytes.byteLength) in a
try/catch (and/or finally) so that if commit throws you call
frameWriter.abort(), call perfMarkEnd("drawlist_build", buildToken) and return a
backend failure (e.g. { ok: false, code: "ZRUI_BACKEND_ERROR", detail:
String(err) }); ensure the normal success path still assigns inFlight from the
commit result and that frameWriter.abort() is invoked for all failure paths so
the writer is always released; refer to BACKEND_BEGIN_FRAME_MARKER, beginFrame,
frameWriter, builtInto, and frameWriter.commit in your changes.

In `@packages/core/src/drawlist/__tests__/builder.text-arena.equivalence.test.ts`:
- Around line 101-108: Add explicit upper-bound checks before performing binary
reads to avoid RangeError: in the command loop (where h.cmdOffset, h.cmdBytes,
cmdEnd, off, u16 and u32 are used) validate that off + 2 and off + 8 are <=
cmdEnd (and also <= bytes.length) before calling u16(bytes, off) and u32(bytes,
off + 4), and assert with a clear message if the bounds fail; similarly, in
collectResourceTables (and the blob/header parsing that reads segCount) add
guards that the buffer has at least the expected header length (e.g., bytes
required to read segCount and subsequent fields) before decoding, and assert/die
deterministically instead of allowing a RangeError.

In `@packages/core/src/renderer/__tests__/renderer.text.test.ts`:
- Around line 95-103: The decoder currently silently defaults to a 28-byte
stride when the blob size doesn't match the 40-byte layout; update
decodeTextRunBlob to validate the blob layout and fail fast: compute segCount
and remaining as now, then if remaining === segCount * 40 use stride=40, else if
remaining === segCount * 28 use stride=28, otherwise throw an error (or assert)
describing "unsupported text-run blob layout" including segCount and remaining;
adjust use of stringFieldOffset/byteOffFieldOffset/byteLenFieldOffset as before
based on the chosen stride and apply the same validation for the other checks in
the 106–123 region.
- Around line 2-13: The test imports internal helpers (OP_DEF_BLOB,
OP_DEF_STRING, OP_DRAW_TEXT, OP_DRAW_TEXT_RUN, OP_FREE_BLOB, OP_FREE_STRING,
OP_POP_CLIP, OP_PUSH_CLIP, parseCommandHeaders, parseInternedStrings) from an
internal test path; change the import to come from the package's public test
export (the package export entry that exposes drawlistDecode utilities, e.g.,
the testing surface in the core package) so tests use package-exported symbols
instead of relative internal paths; update the import statement in
renderer.text.test.ts to import those named symbols from the package export
(e.g., `@rezi-ui/core` or the designated test export) and remove the
../../__tests__/drawlistDecode.js relative path.
- Around line 165-168: The OP_FREE_BLOB handling reads a u32 at off + 8 without
validating the command payload length; before calling u32(bytes, off + 8) check
that cmd.size is at least 12 (or otherwise large enough to include 4 bytes at
offset 8) and bail out (skip/throw/log) if it's truncated so you don't read past
the buffer. Update the block handling OP_FREE_BLOB (the condition using
OP_FREE_BLOB, the u32 call, and textRunBlobsByIndex assignment) to perform this
guard using cmd.size and only compute blobId and mutate textRunBlobsByIndex when
the size check passes.

In `@packages/core/src/renderer/renderToDrawlist/overflowCulling.ts`:
- Line 1: The import in overflowCulling.ts pulls RuntimeInstance from an
internal relative module ("../../runtime/commit.js"); change it to use the
package-export surface instead (import the RuntimeInstance type from the public
package export, e.g. the core package like `@rezi-ui/core`) so it follows the repo
rule of package-export imports; update the import statement that references
RuntimeInstance to use the package's public export rather than the internal
path.

In `@packages/core/src/widgets/__tests__/basicWidgets.render.test.ts`:
- Around line 58-76: The loop that maps decoded text (using
parseDecodedDrawTextCommands()) drifts because drawTextIndex is only incremented
for records with size >= 48; change the logic so drawTextIndex advances for
every DRAW_TEXT opcode (opcode === 3) regardless of size so the decoded[]
entries stay aligned, and when reading decodedCmd (decoded[drawTextIndex])
handle the case it may be undefined (e.g., default text "") before pushing the
DrawTextCommand; update the code around the while loop that reads opcode/size
and uses drawTextIndex accordingly.

In `@packages/core/src/widgets/ui.ts`:
- Line 358: The example uses a hardcoded rgb(...) color literal in the ui.icon
call; replace those raw RGB literals with the design-system/semantic tokens
(e.g., use variant/dsVariant/dsTone or a named token like "success" or
"green.500" depending on your token API) so the icon styling follows the theme;
update each example of ui.icon("arrow.right", { style: { fg: rgb(0, 255, 0) } })
(and the other occurrences noted) to pass the appropriate token prop instead of
rgb(...) — for example set the token via the dsTone/dsVariant/dsSize or style.fg
= getToken("...") per your project's token accessor.

In `@packages/create-rezi/templates/starship/src/main.ts`:
- Around line 126-135: The registered callback
frameAuditGlobal.__reziFrameAuditContext currently calls currentRouteId() too
early and can crash if app isn't initialized; update currentRouteId() to guard
against pre-app access by using optional chaining when dereferencing the
router/current route (e.g., use app?.router?.currentRoute()?.id or similar) so
it returns undefined or a safe fallback when app or router is not available,
then keep the frameAuditGlobal.__reziFrameAuditContext registration as-is to
rely on the safe currentRouteId() behavior.

In `@packages/ink-compat/src/runtime/createInkRenderer.ts`:
- Around line 184-192: The current no-op implementation of blitRect in
createInkRenderer should be changed to fail-fast: replace the empty body of
blitRect(_srcX, _srcY, _w, _h, _dstX, _dstY) with a thrown Error (e.g., throw
new Error("blitRect is unimplemented in ink-compat renderer")) so any future
invocation surfaces immediately; reference the blitRect method in
createInkRenderer and include a clear, descriptive message to aid debugging.

In `@packages/native/src/lib.rs`:
- Around line 1939-1969: The test ffi_layout_matches_vendored_headers is missing
an assertion for the new FFI struct zr_fb_link_t (should be 16 bytes); add size
and alignment checks inside that test so the Rust layout matches the vendored
headers — e.g., assert_eq!(size_of::<ffi::zr_fb_link_t>(), 16) and a
corresponding align_of assertion (likely 4) — to prevent memory-corrupting
mismatches for code using zr_fb_link_t (see usages zr_fb_link_intern and
zr_fb_link_lookup).

In `@packages/node/src/backend/nodeBackend.ts`:
- Line 42: Add BackendBeginFrame to the core package root exports and update the
node backend import to use the package root: modify the core export list in
packages core index (add BackendBeginFrame alongside the other backend types) so
it is exported from "@rezi-ui/core", then change the import in nodeBackend
(where it currently imports BackendBeginFrame from "@rezi-ui/core/backend") to
import BackendBeginFrame from "@rezi-ui/core" instead to satisfy the
packages/node import boundary rule.

In `@packages/node/src/backend/nodeBackendInline.ts`:
- Around line 824-892: The catch block currently emits
frameAudit.emit("frame.throw", { detail: safeDetail(err) }) without the
frameSeq, so update that emit to include frameSeq (e.g.
frameAudit.emit("frame.throw", { frameSeq, detail: safeDetail(err) })) so throw
events can be correlated with the submitted frame; use the existing frameSeq
variable declared at the top of the try and keep the same safeDetail(err)
payload.

In `@packages/node/src/frameAudit.ts`:
- Around line 17-19: The audit writer currently calls
appendFileSync/writeFileSync on the path derived from REZI_FRAME_AUDIT_LOG and
may fail silently if nested directories don't exist; before any appendFileSync
or writeFileSync call (the occurrences around REZI_FRAME_AUDIT_LOG and in the
functions that perform frame audit writes), ensure the directory for the audit
file exists by calling mkdirSync(path.dirname(auditPath), { recursive: true })
(or equivalent) and only then perform append/write; also propagate or log errors
instead of swallowing them so failures are visible.

In `@packages/node/src/worker/engineWorker.ts`:
- Around line 604-611: The DataView construction/emit blocks are misformatted;
run the Biome formatter (biome check --fix) or reformat the DataView-related
blocks so they follow project style, specifically the block that creates
dvPayload (new DataView(payload.buffer, payload.byteOffset,
DEBUG_FRAME_RECORD_BYTES)) and the subsequent frameAudit.emit call that
references reason, recordId, u64FromView(dvPayload, 0).toString(), and code;
also fix the similar DataView blocks at the other occurrences (around the
u64FromView usages at the noted ranges) so indentation, spacing, and punctuation
match the rest of the file and pass CI.

In `@scripts/frame-audit-report.mjs`:
- Around line 288-297: The pid summary is built from the unfiltered recordsByPid
so pid_sessions shows unrelated sessions; rebuild or filter recordsByPid from
the already-filtered records (the same filteredRecords/records used at the
earlier filter) before computing sessionRows, or apply the same predicate when
iterating recordsByPid; update the code that produces sessionRows/for-loop
(variables: recordsByPid, sessionRows, pid_sessions, records) to use the
filtered set so pids and pid_sessions reflect the active PID filter.

---

Outside diff comments:
In `@packages/core/src/renderer/renderToDrawlist/widgets/collections.ts`:
- Around line 1-31: The import of emitFrameAudit and FRAME_AUDIT_ENABLED is
misordered and fails the import-sort check; move the "import { emitFrameAudit,
FRAME_AUDIT_ENABLED } from '../../../perf/frameAudit.js';" line into the main
block of local/project imports so it is grouped with the other "../../../..."
imports and alphabetically ordered with them (place it among the other non-type
imports such as distributeColumnWidths, tableRecipe, treeRecipe, and the
widgets/virtualList imports) to satisfy the project's import ordering rules.

In `@packages/create-rezi/templates/starship/src/main.ts`:
- Around line 734-739: The onSignal handler can call stopApp() while bootstrap
is still running, allowing bootstrap to continue into app.start() after shutdown
was requested; modify bootstrap logic so startup checks a cancellation flag or
promise set by onSignal/stopApp before and immediately after awaiting any async
initialization, and refuse to call app.start() (or abort startup) if
shutdownRequested is true; update onSignal/stopApp to set this shared
shutdownRequested flag (or resolve a cancellation promise) and ensure
app.start() is only invoked when the flag is false (apply same guard to the
other startup lines referenced by symbols onSignal, stopApp, and app.start).

In `@packages/node/src/worker/engineWorker.ts`:
- Around line 1573-1583: The code unconditionally clears nativeFrameAuditEnabled
after calling native.engineDebugDisable even when that call failed (rc != 0),
which can desync worker vs native state; in the "debug:disable" case (around
engineDebugDisable, rc, engineId, nativeFrameAuditEnabled, frameAudit.enabled)
only set nativeFrameAuditEnabled = false when engineDebugDisable returned
success (e.g., rc === 0), otherwise leave the flag untouched (and preserve
existing error handling/return path).

---

Nitpick comments:
In `@docs/dev/build.md`:
- Around line 38-41: The paragraph about the vendor/zireael submodule is
ambiguous about whether submodule init is required for all developers; update
docs/dev/build.md to add a short clarifying note (adjacent to the earlier setup
step and in the paragraph mentioning "vendor/zireael" and "Native addon builds")
that explicitly states the TS-only development path can skip submodule
initialization (i.e., submodule checkout is only required for native addon
builds, syncing, or auditing vendored engine updates), and phrase it as a brief
optional step so readers know they can skip submodule init for TypeScript-only
work.

In `@examples/regression-dashboard/src/__tests__/keybindings.test.ts`:
- Around line 5-12: Add a case-insensitivity assertion to the existing
keybinding test: call resolveDashboardCommand with an uppercase key (e.g., "Q"
or "J") and assert it resolves to the same canonical command as the lowercase
variant (e.g., "quit" or "move-down") to verify the internal key.toLowerCase()
behavior in resolveDashboardCommand.

In `@examples/regression-dashboard/src/helpers/formatters.ts`:
- Around line 38-46: The function fleetCounts currently iterates services three
times causing unnecessary work; change fleetCounts to compute
healthy/warning/down in a single pass by iterating once (e.g., use a for loop or
Array.prototype.reduce over services) and increment the appropriate counters,
then return Object.freeze with those counts; update the implementation of
fleetCounts to reference the same return shape ({ healthy, warning, down }) and
preserve Readonly typing.

In `@packages/core/src/app/createApp.ts`:
- Around line 596-603: The branch checking backendDrawlistVersion after calling
readBackendDrawlistVersionMarker is redundant because
readBackendDrawlistVersionMarker already throws for non-1 values and only
returns 1 | null; remove the entire if block that references
backendDrawlistVersion and invalidProps to eliminate unreachable code (i.e.,
delete the const check starting with "if (backendDrawlistVersion !== null &&
backendDrawlistVersion !== 1) { ... }"). Ensure no other logic depends on that
removed block and keep the const backendDrawlistVersion assignment if it's still
used elsewhere.

In `@packages/core/src/drawlist/__tests__/builder_v6_resources.test.ts`:
- Around line 4-6: Replace the duplicated u32 implementation in
builder_v6_resources.test.ts by importing and reusing the shared u32 test helper
from the drawlistDecode test module; remove the local function u32 (the one that
constructs a DataView and calls getUint32) and add an import for the exported
u32 helper from the drawlistDecode test module so all tests use the same parsing
utility.

In `@packages/core/src/renderer/__tests__/persistentBlobKeys.test.ts`:
- Around line 89-94: Add a test that covers the "null allocation" branch of
addBlobAligned by creating or mocking a CountingBuilder whose addBlob method
returns null, then call addBlobAligned(builder, new Uint8Array([1,2,3])) and
assert the function returns null (or the documented failure value) and that
builder.blobByteLens was not changed; reference addBlobAligned, addBlob, and
CountingBuilder in the test to locate and implement the behavior check.

In `@packages/core/src/renderer/renderToDrawlist/overflowCulling.ts`:
- Around line 90-109: Add focused regression tests exercising
subtreeCanOverflowBounds traversal: create scenarios that include pass-through
"layer" frame styles, wrapper chains (focusZone, focusTrap, themed, layers) to
verify isTransparentOverflowWrapper correctly gates descent, and cases where
boxShadow is {} versus explicit zero values to ensure hasBoxShadowOverflow
treats defaults and zeros equivalently; confirm that hasVisibleOverflow and
hasBoxShadowOverflow return true for visible/shadowing children and that
traversal still short-circuits on those checks while continuing descent only
through transparent wrappers.

In `@packages/core/src/renderer/renderToDrawlist/widgets/renderCanvasWidgets.ts`:
- Around line 225-227: Rename the confusing addBlobAligned API to addBlob (and
update any imports/uses) because alignment actually happens during serialization
via writeDefBlob().align4(), not at add time; update the export and all call
sites to use addBlob, and optionally add a brief comment in
renderCanvasWidgets.ts near the new addBlob function clarifying that 4-byte
alignment is applied during drawlist finalization/serialization
(writeDefBlob.align4()) rather than at addition.

In `@packages/core/src/runtime/commit.ts`:
- Around line 1337-1341: The rewrite of the parent VNode’s committed children
runs every fast-reuse cycle and should be skipped when none of the child wiring
changed; guard the work by checking child dirtiness or reference differences
before mapping and calling rewriteCommittedVNode. Concretely: in the fast-reuse
path around prev.children.map(...) and rewriteCommittedVNode(vnode,...), first
test whether any child is dirty or whether prev.children’ committed vnode
references differ from the current vnode.children (e.g. prev.children.some(c =>
c.dirty) || !prev.children.every((c,i) => c.vnode === vnode.children[i])); only
if that predicate is true compute fastReuseCommittedChildren and call
rewriteCommittedVNode. Ensure you reference the existing symbols prev,
prev.children, vnode, fastReuseCommittedChildren, and rewriteCommittedVNode when
making the change.

In `@packages/core/src/widgets/__tests__/style.inheritance.test.ts`:
- Around line 35-47: Replace the magic bit-shift literals in
computeExpectedAttrs with named constants (e.g., ATTR_BOLD = 1<<0, ATTR_ITALIC =
1<<1, ATTR_UNDERLINE = 1<<2, etc.) declared near the top of the test file, then
use those constants inside computeExpectedAttrs instead of the raw 1<<n values
so the mapping (bold/italic/underline/inverse/dim/strikethrough/overline/blink)
is explicit and easier to audit/maintain; ensure the final return still uses
unsigned shift (attrs >>> 0) unchanged.

In `@packages/create-rezi/templates/starship/src/screens/engineering.ts`:
- Around line 394-397: The inline magic numbers in the responsiveDeckMinHeight
calculation (the 12, 10 and 2) should be extracted into named constants for
clarity and easier tuning: define e.g. CONTROLS_SUMMARY_OFFSET (12),
CONTROLS_OFFSET (10) and SECONDARY_PANELS_OFFSET (2) near the top of the module,
then update the responsiveDeckMinHeight expression to use these constants with
contentRows, showControlsSummary and showSecondaryPanels so the formula reads
self-documenting and the offsets can be tuned without changing the arithmetic.

In `@packages/native/src/lib.rs`:
- Around line 1709-1731: The test helper uses tab characters in the struct
initialization blocks (e.g., the caps assignment for ffi::plat_caps_t, the
limits retrieval via ffi::zr_engine_config_default(), and the initial_term_state
for ffi::zr_term_state_t); update those lines to use spaces (matching the file's
existing indentation style) so the caps, limits, and initial_term_state
initializers are consistently space-indented with the surrounding code.
- Around line 116-155: The indentation in the struct definitions
zr_terminal_caps_t and plat_caps_t uses tab characters mixed with spaces;
replace all leading tab characters inside these struct blocks with the project's
standard spaces indentation to match the rest of the file (align field names and
types consistently), and reformat the file (e.g., run rustfmt) to ensure
consistent spacing throughout.
- Around line 1855-1858: The test
fb_link_intern_compacts_stale_refs_and_bounds_growth uses a hardcoded
LINK_ENTRY_MAX_BYTES (2083 + 2083) that mirrors ZR_FB_LINK_URI_MAX_BYTES +
ZR_FB_LINK_ID_MAX_BYTES and may drift from vendored headers; update the test by
either (a) adding a clear comment on LINK_ENTRY_MAX_BYTES documenting its
derivation from ZR_FB_LINK_URI_MAX_BYTES and ZR_FB_LINK_ID_MAX_BYTES and why
those specific values are used, or (b) preferably import the two upstream
constants via FFI and compute LINK_ENTRY_MAX_BYTES from them inside the test
(referencing the test function name and the LINK_ENTRY_MAX_BYTES symbol) so the
values stay synchronized with the vendored headers.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 115410b and d55b8a4.

⛔ Files ignored due to path filters (23)
  • examples/regression-dashboard/package-lock.json is excluded by !**/package-lock.json
  • package-lock.json is excluded by !**/package-lock.json
  • packages/testkit/fixtures/zrdl-v1-graphics/widgets/barchart_highres.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1-graphics/widgets/canvas_primitives.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1-graphics/widgets/heatmap_plasma.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1-graphics/widgets/image_png_contain.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1-graphics/widgets/image_rgba_sixel_cover.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1-graphics/widgets/line_chart.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1-graphics/widgets/link_docs.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1-graphics/widgets/richtext_underline_ext.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1-graphics/widgets/scatter_plot.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1-graphics/widgets/sparkline_highres.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/golden/draw_text_interned.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/widgets/button_focus_states.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/widgets/divider_horizontal.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/widgets/divider_with_label.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/widgets/input_basic.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/widgets/input_disabled.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/widgets/input_focused_inverse.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/widgets/layer_backdrop_opaque.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/widgets/modal_backdrop_dim.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/widgets/spinner_tick_0.bin is excluded by !**/*.bin
  • packages/testkit/fixtures/zrdl-v1/widgets/spinner_tick_1.bin is excluded by !**/*.bin
📒 Files selected for processing (150)
  • .github/workflows/ci.yml
  • AGENTS.md
  • CLAUDE.md
  • docs/backend/native.md
  • docs/dev/build.md
  • docs/dev/live-pty-debugging.md
  • docs/dev/repo-layout.md
  • docs/dev/testing.md
  • docs/protocol/versioning.md
  • docs/protocol/zrdl.md
  • examples/regression-dashboard/README.md
  • examples/regression-dashboard/package.json
  • examples/regression-dashboard/src/__tests__/keybindings.test.ts
  • examples/regression-dashboard/src/__tests__/reducer.test.ts
  • examples/regression-dashboard/src/__tests__/render.test.ts
  • examples/regression-dashboard/src/__tests__/telemetry.test.ts
  • examples/regression-dashboard/src/helpers/formatters.ts
  • examples/regression-dashboard/src/helpers/keybindings.ts
  • examples/regression-dashboard/src/helpers/state.ts
  • examples/regression-dashboard/src/helpers/telemetry.ts
  • examples/regression-dashboard/src/main.ts
  • examples/regression-dashboard/src/screens/overview.ts
  • examples/regression-dashboard/src/theme.ts
  • examples/regression-dashboard/src/types.ts
  • examples/regression-dashboard/tsconfig.json
  • mkdocs.yml
  • package.json
  • packages/core/src/__tests__/drawlistDecode.ts
  • packages/core/src/__tests__/integration/integration.dashboard.test.ts
  • packages/core/src/__tests__/integration/integration.file-manager.test.ts
  • packages/core/src/__tests__/integration/integration.form-editor.test.ts
  • packages/core/src/__tests__/integration/integration.reflow.test.ts
  • packages/core/src/__tests__/integration/integration.resize.test.ts
  • packages/core/src/abi.ts
  • packages/core/src/app/__tests__/hotStateReload.test.ts
  • packages/core/src/app/__tests__/inspectorOverlayHelper.test.ts
  • packages/core/src/app/__tests__/resilience.test.ts
  • packages/core/src/app/createApp.ts
  • packages/core/src/app/rawRenderer.ts
  • packages/core/src/app/widgetRenderer.ts
  • packages/core/src/app/widgetRenderer/damageTracking.ts
  • packages/core/src/drawlist/__tests__/builder.alignment.test.ts
  • packages/core/src/drawlist/__tests__/builder.golden.test.ts
  • packages/core/src/drawlist/__tests__/builder.graphics.test.ts
  • packages/core/src/drawlist/__tests__/builder.limits.test.ts
  • packages/core/src/drawlist/__tests__/builder.reset.test.ts
  • packages/core/src/drawlist/__tests__/builder.round-trip.test.ts
  • packages/core/src/drawlist/__tests__/builder.string-cache.test.ts
  • packages/core/src/drawlist/__tests__/builder.string-intern.test.ts
  • packages/core/src/drawlist/__tests__/builder.style-encoding.test.ts
  • packages/core/src/drawlist/__tests__/builder.text-arena.equivalence.test.ts
  • packages/core/src/drawlist/__tests__/builder.text-run.test.ts
  • packages/core/src/drawlist/__tests__/builder.validate-caps.test.ts
  • packages/core/src/drawlist/__tests__/builder_style_attrs.test.ts
  • packages/core/src/drawlist/__tests__/builder_v6_resources.test.ts
  • packages/core/src/drawlist/__tests__/writers.gen.test.ts
  • packages/core/src/drawlist/__tests__/writers.gen.v6.test.ts
  • packages/core/src/drawlist/builder.ts
  • packages/core/src/drawlist/builderBase.ts
  • packages/core/src/drawlist/writers.gen.ts
  • packages/core/src/index.ts
  • packages/core/src/layout/kinds/overlays.ts
  • packages/core/src/perf/frameAudit.ts
  • packages/core/src/renderer/__tests__/overlay.edge.test.ts
  • packages/core/src/renderer/__tests__/persistentBlobKeys.test.ts
  • packages/core/src/renderer/__tests__/render.golden.test.ts
  • packages/core/src/renderer/__tests__/renderer.damage.test.ts
  • packages/core/src/renderer/__tests__/renderer.partial.test.ts
  • packages/core/src/renderer/__tests__/renderer.scrollBlit.benchmark.test.ts
  • packages/core/src/renderer/__tests__/renderer.text.test.ts
  • packages/core/src/renderer/renderToDrawlist/overflowCulling.ts
  • packages/core/src/renderer/renderToDrawlist/widgets/collections.ts
  • packages/core/src/renderer/renderToDrawlist/widgets/containers.ts
  • packages/core/src/renderer/renderToDrawlist/widgets/renderCanvasWidgets.ts
  • packages/core/src/runtime/__tests__/commit.fastReuse.regression.test.ts
  • packages/core/src/runtime/commit.ts
  • packages/core/src/theme/__tests__/theme.extend.test.ts
  • packages/core/src/theme/__tests__/theme.validation.test.ts
  • packages/core/src/theme/resolve.ts
  • packages/core/src/theme/validate.ts
  • packages/core/src/widgets/__tests__/basicWidgets.render.test.ts
  • packages/core/src/widgets/__tests__/graphics.golden.test.ts
  • packages/core/src/widgets/__tests__/graphicsWidgets.test.ts
  • packages/core/src/widgets/__tests__/inspectorOverlay.render.test.ts
  • packages/core/src/widgets/__tests__/pagination.test.ts
  • packages/core/src/widgets/__tests__/renderer.regressions.test.ts
  • packages/core/src/widgets/__tests__/style.inheritance.test.ts
  • packages/core/src/widgets/__tests__/style.merge.test.ts
  • packages/core/src/widgets/__tests__/style.utils.test.ts
  • packages/core/src/widgets/__tests__/styleUtils.test.ts
  • packages/core/src/widgets/__tests__/widgetRenderSmoke.test.ts
  • packages/core/src/widgets/pagination.ts
  • packages/core/src/widgets/ui.ts
  • packages/create-rezi/templates/starship/src/main.ts
  • packages/create-rezi/templates/starship/src/screens/bridge.ts
  • packages/create-rezi/templates/starship/src/screens/cargo.ts
  • packages/create-rezi/templates/starship/src/screens/comms.ts
  • packages/create-rezi/templates/starship/src/screens/crew.ts
  • packages/create-rezi/templates/starship/src/screens/engineering.ts
  • packages/create-rezi/templates/starship/src/screens/settings.ts
  • packages/create-rezi/templates/starship/src/theme.ts
  • packages/ink-compat/src/runtime/createInkRenderer.ts
  • packages/native/README.md
  • packages/native/src/lib.rs
  • packages/native/vendor/VENDOR_COMMIT.txt
  • packages/native/vendor/zireael/include/zr/zr_caps.h
  • packages/native/vendor/zireael/include/zr/zr_config.h
  • packages/native/vendor/zireael/include/zr/zr_debug.h
  • packages/native/vendor/zireael/include/zr/zr_drawlist.h
  • packages/native/vendor/zireael/include/zr/zr_engine.h
  • packages/native/vendor/zireael/include/zr/zr_event.h
  • packages/native/vendor/zireael/include/zr/zr_metrics.h
  • packages/native/vendor/zireael/include/zr/zr_platform_types.h
  • packages/native/vendor/zireael/include/zr/zr_result.h
  • packages/native/vendor/zireael/include/zr/zr_terminal_caps.h
  • packages/native/vendor/zireael/include/zr/zr_version.h
  • packages/native/vendor/zireael/src/core/zr_cursor.h
  • packages/native/vendor/zireael/src/core/zr_damage.c
  • packages/native/vendor/zireael/src/core/zr_damage.h
  • packages/native/vendor/zireael/src/core/zr_diff.c
  • packages/native/vendor/zireael/src/core/zr_drawlist.c
  • packages/native/vendor/zireael/src/core/zr_engine.c
  • packages/native/vendor/zireael/src/core/zr_engine_present.inc
  • packages/native/vendor/zireael/src/core/zr_event_pack.c
  • packages/native/vendor/zireael/src/core/zr_event_pack.h
  • packages/native/vendor/zireael/src/core/zr_framebuffer.c
  • packages/native/vendor/zireael/src/core/zr_metrics.c
  • packages/native/vendor/zireael/src/core/zr_placeholder.c
  • packages/native/vendor/zireael/src/platform/win32/zr_win32_conpty_test.c
  • packages/native/vendor/zireael/src/platform/win32/zr_win32_conpty_test.h
  • packages/native/vendor/zireael/src/unicode/zr_grapheme.h
  • packages/native/vendor/zireael/src/unicode/zr_unicode_data.h
  • packages/native/vendor/zireael/src/unicode/zr_unicode_pins.h
  • packages/native/vendor/zireael/src/unicode/zr_utf8.h
  • packages/native/vendor/zireael/src/unicode/zr_width.h
  • packages/native/vendor/zireael/src/unicode/zr_wrap.h
  • packages/native/vendor/zireael/src/util/zr_arena.h
  • packages/native/vendor/zireael/src/util/zr_ring.h
  • packages/native/vendor/zireael/src/util/zr_string_builder.h
  • packages/native/vendor/zireael/src/util/zr_string_view.h
  • packages/native/vendor/zireael/src/util/zr_vec.h
  • packages/node/src/backend/nodeBackend.ts
  • packages/node/src/backend/nodeBackendInline.ts
  • packages/node/src/frameAudit.ts
  • packages/node/src/worker/engineWorker.ts
  • scripts/__tests__/check-native-vendor-integrity.test.mjs
  • scripts/check-native-vendor-integrity.mjs
  • scripts/frame-audit-report.mjs
  • scripts/generate-drawlist-writers.ts
  • vendor/zireael
💤 Files with no reviewable changes (6)
  • packages/core/src/abi.ts
  • packages/native/vendor/zireael/src/util/zr_string_view.h
  • packages/core/src/index.ts
  • packages/native/vendor/zireael/src/unicode/zr_unicode_pins.h
  • packages/core/src/renderer/tests/renderer.scrollBlit.benchmark.test.ts
  • packages/native/vendor/zireael/src/unicode/zr_utf8.h

Comment on lines +24 to +25
cd /home/k3nig/Rezi
npx tsc -b packages/core packages/node packages/create-rezi
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Replace hardcoded local path with portable repo-root instructions.

Line 24 and Line 37 use /home/k3nig/Rezi, which won’t work for most contributors.

🛠️ Proposed doc fix
-cd /home/k3nig/Rezi
+cd <path-to-your-Rezi-clone>

Apply this at both occurrences.

Also applies to: 37-38

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/dev/live-pty-debugging.md` around lines 24 - 25, Replace the hardcoded
absolute path "/home/k3nig/Rezi" used in the two doc examples (lines showing the
cd command) with a portable instruction to run the commands from the repository
root (e.g., "change into the repo root" or "from your project root run the build
commands"); update both occurrences (the cd line at ~24 and the similar lines at
~37-38) so contributors are instructed to be in the repo root rather than using
the specific local path.

Comment on lines +273 to +283
case OP_DEF_STRING: {
const id = u32(bytes, cmd.offset + 8);
const byteLen = u32(bytes, cmd.offset + 12);
const dataStart = cmd.offset + 16;
const dataEnd = dataStart + byteLen;
strings.set(id, cloneBytes(bytes.subarray(dataStart, dataEnd)));
break;
}
case OP_FREE_STRING: {
const id = u32(bytes, cmd.offset + 8);
strings.delete(id);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Validate DEF_*/FREE_* command sizes before reading fields.

Both parsers read +8/+12 fields and payload slices without command-specific bounds checks, so malformed streams can consume bytes from adjacent commands and decode incorrect resources.

🐛 Proposed hardening patch
       case OP_DEF_STRING: {
+        if (cmd.size < 16) break;
         const id = u32(bytes, cmd.offset + 8);
         const byteLen = u32(bytes, cmd.offset + 12);
         const dataStart = cmd.offset + 16;
         const dataEnd = dataStart + byteLen;
+        if (dataEnd > cmd.offset + cmd.size) break;
         strings.set(id, cloneBytes(bytes.subarray(dataStart, dataEnd)));
         break;
       }
       case OP_FREE_STRING: {
+        if (cmd.size !== 12) break;
         const id = u32(bytes, cmd.offset + 8);
         strings.delete(id);
         break;
       }
@@
       case OP_DEF_BLOB: {
+        if (cmd.size < 16) break;
         const id = u32(bytes, cmd.offset + 8);
         const byteLen = u32(bytes, cmd.offset + 12);
         const dataStart = cmd.offset + 16;
         const dataEnd = dataStart + byteLen;
+        if (dataEnd > cmd.offset + cmd.size) break;
         blobs.set(id, cloneBytes(bytes.subarray(dataStart, dataEnd)));
         break;
       }
       case OP_FREE_BLOB: {
+        if (cmd.size !== 12) break;
         blobs.delete(u32(bytes, cmd.offset + 8));
         break;
       }

Also applies to: 326-336

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/__tests__/drawlistDecode.ts` around lines 273 - 283, The
DEF_/FREE_ handlers (e.g., case OP_DEF_STRING and OP_FREE_STRING) read fields at
offsets like cmd.offset + 8/12 and slice payloads without validating the command
length, so add bounds checks before any u32 reads or bytes.subarray calls:
verify that cmd.size (or computed remaining = bytes.length - cmd.offset) is at
least the minimum expected bytes for the specific opcode (header + field offsets
+ byteLen) and that dataStart/dataEnd live within bytes.length; if validation
fails, skip the command (or return an error) instead of calling u32(...) or
strings.set/delete. Apply the same validation pattern to the other DEF_/FREE
handlers referenced around lines 326-336 to prevent out-of-bounds reads.

Comment on lines +11 to +19
import {
OP_CLEAR,
OP_DRAW_TEXT,
OP_FILL_RECT,
OP_PUSH_CLIP,
parseCommandHeaders,
parseDrawTextCommands,
parseInternedStrings,
} from "../drawlistDecode.js";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Use a package export instead of an internal relative import path.

This new import from ../drawlistDecode.js creates internal-path coupling in a TypeScript file. Please re-export these helpers from a package entrypoint and import from that export.

As per coding guidelines, "Import from package exports only (@rezi-ui/core, @rezi-ui/node), never from internal paths in TypeScript files."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/__tests__/integration/integration.reflow.test.ts` around
lines 11 - 19, The test imports internal helpers (OP_CLEAR, OP_DRAW_TEXT,
OP_FILL_RECT, OP_PUSH_CLIP, parseCommandHeaders, parseDrawTextCommands,
parseInternedStrings) via a relative path; replace that relative import in
integration.reflow.test.ts with an import from the package entrypoint export
(e.g., import these symbols from the package named export such as `@rezi-ui/core`)
and ensure those helpers are re-exported from the package entry module so the
test only imports from the public package API rather than ../drawlistDecode.js.

Comment on lines +3963 to +3982
const beginFrame = (
this.backend as RuntimeBackend &
Partial<Record<typeof BACKEND_BEGIN_FRAME_MARKER, BackendBeginFrame>>
)[BACKEND_BEGIN_FRAME_MARKER];
if (typeof beginFrame === "function") {
const frameWriter = beginFrame();
if (frameWriter) {
const builtInto = this.builder.buildInto(frameWriter.buf);
if (!builtInto.ok) {
frameWriter.abort();
perfMarkEnd("drawlist_build", buildToken);
return {
ok: false,
code: "ZRUI_DRAWLIST_BUILD_ERROR",
detail: `${builtInto.error.code}: ${builtInto.error.detail}`,
};
}
submittedBytes = builtInto.bytes;
inFlight = frameWriter.commit(submittedBytes.byteLength);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle begin-frame commit failures as backend errors and always release the frame writer.

If frameWriter.commit(...) throws synchronously, this path currently skips explicit writer cleanup and can escape as a generic outer error. Treat it as backend failure and abort the writer before returning.

🛠️ Proposed fix
       if (typeof beginFrame === "function") {
         const frameWriter = beginFrame();
         if (frameWriter) {
           const builtInto = this.builder.buildInto(frameWriter.buf);
           if (!builtInto.ok) {
             frameWriter.abort();
             perfMarkEnd("drawlist_build", buildToken);
             return {
               ok: false,
               code: "ZRUI_DRAWLIST_BUILD_ERROR",
               detail: `${builtInto.error.code}: ${builtInto.error.detail}`,
             };
           }
           submittedBytes = builtInto.bytes;
-          inFlight = frameWriter.commit(submittedBytes.byteLength);
+          try {
+            inFlight = frameWriter.commit(submittedBytes.byteLength);
+          } catch (e: unknown) {
+            frameWriter.abort();
+            perfMarkEnd("drawlist_build", buildToken);
+            return {
+              ok: false,
+              code: "ZRUI_BACKEND_ERROR",
+              detail: describeThrown(e),
+            };
+          }
         }
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/app/widgetRenderer.ts` around lines 3963 - 3982, The
begin-frame path must always release the frameWriter and convert synchronous
commit exceptions into a backend error return: wrap the call to
frameWriter.commit(submittedBytes.byteLength) in a try/catch (and/or finally) so
that if commit throws you call frameWriter.abort(), call
perfMarkEnd("drawlist_build", buildToken) and return a backend failure (e.g. {
ok: false, code: "ZRUI_BACKEND_ERROR", detail: String(err) }); ensure the normal
success path still assigns inFlight from the commit result and that
frameWriter.abort() is invoked for all failure paths so the writer is always
released; refer to BACKEND_BEGIN_FRAME_MARKER, beginFrame, frameWriter,
builtInto, and frameWriter.commit in your changes.

Comment on lines +101 to +108
const cmdEnd = h.cmdOffset + h.cmdBytes;
let off = h.cmdOffset;
for (let i = 0; i < h.cmdCount; i++) {
assert.equal(off < cmdEnd, true, "command cursor in bounds");
const opcode = u16(bytes, off);
const size = u32(bytes, off + 4);
assert.equal(size >= 8, true, "command size includes header");

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add explicit upper-bound guards before binary reads.

Two reads can currently fail with RangeError instead of a deterministic assertion: command-size overrun in collectResourceTables, and zero/short blob headers before segCount decode.

🔧 Suggested patch
@@
   for (let i = 0; i < h.cmdCount; i++) {
     assert.equal(off < cmdEnd, true, "command cursor in bounds");
     const opcode = u16(bytes, off);
     const size = u32(bytes, off + 4);
     assert.equal(size >= 8, true, "command size includes header");
+    assert.equal(off + size <= cmdEnd, true, "command size in bounds");
@@
       const blobDv = new DataView(blobBytes.buffer, blobBytes.byteOffset, blobBytes.byteLength);
       const blobLen = blobBytes.byteLength;
+      assert.equal(blobLen >= 4, true, "blob header in bounds");
       const segCount = blobDv.getUint32(0, true);

Also applies to: 205-208

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/drawlist/__tests__/builder.text-arena.equivalence.test.ts`
around lines 101 - 108, Add explicit upper-bound checks before performing binary
reads to avoid RangeError: in the command loop (where h.cmdOffset, h.cmdBytes,
cmdEnd, off, u16 and u32 are used) validate that off + 2 and off + 8 are <=
cmdEnd (and also <= bytes.length) before calling u16(bytes, off) and u32(bytes,
off + 4), and assert with a clear message if the bounds fail; similarly, in
collectResourceTables (and the blob/header parsing that reads segCount) add
guards that the buffer has at least the expected header length (e.g., bytes
required to read segCount and subsequent fields) before decoding, and assert/die
deterministically instead of allowing a RangeError.

setTextMeasureEmojiPolicy,
severityToNum,
} from "@rezi-ui/core";
import type { BackendBeginFrame } from "@rezi-ui/core/backend";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Subpath imports under packages/node/src ==="
rg -n --type ts 'from "@rezi-ui/core/' packages/node/src || echo "No matches found"

echo
echo "=== BackendBeginFrame declarations/exports in core ==="
rg -n --type ts 'BackendBeginFrame' packages/core/src || echo "No matches found"

echo
echo "=== Core root index exports ==="
head -100 packages/core/src/index.ts | cat -n

Repository: RtlZeroMemory/Rezi

Length of output: 4468


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Checking what's exported from core root for backend ==="
grep -A 20 'from "./backend' packages/core/src/index.ts | head -25

echo
echo "=== Checking if BackendBeginFrame should be in the export list ==="
rg -n 'export type BackendBeginFrame' packages/core/src/backend.ts

echo
echo "=== Checking backend.ts to see all exports ==="
rg -n 'export (type|const)' packages/core/src/backend.ts | head -20

Repository: RtlZeroMemory/Rezi

Length of output: 1668


Add BackendBeginFrame to core root exports and import from package root.

This file uses a subpath import (@rezi-ui/core/backend), which violates the boundary rule for packages/node. Additionally, BackendBeginFrame is not currently exported from the @rezi-ui/core root, so the import requires both changes:

  1. Add the type to packages/core/src/index.ts exports (alongside other backend types)
  2. Update the import to use the root package
📝 Changes needed

In packages/core/src/index.ts, add BackendBeginFrame to the backend export block (around line 84-94):

 export {
   BACKEND_BEGIN_FRAME_MARKER,
   BACKEND_DRAWLIST_VERSION_MARKER,
   BACKEND_FPS_CAP_MARKER,
   BACKEND_MAX_EVENT_BYTES_MARKER,
   BACKEND_RAW_WRITE_MARKER,
   type BackendRawWrite,
   type BackendEventBatch,
+  type BackendBeginFrame,
   type RuntimeBackend,
 } from "./backend.js";

Then in packages/node/src/backend/nodeBackend.ts:

-import type { BackendBeginFrame } from "@rezi-ui/core/backend";
+import type { BackendBeginFrame } from "@rezi-ui/core";

Per coding guidelines: packages/node/src/**/*.{ts,tsx} must import from @rezi-ui/core root only.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/node/src/backend/nodeBackend.ts` at line 42, Add BackendBeginFrame
to the core package root exports and update the node backend import to use the
package root: modify the core export list in packages core index (add
BackendBeginFrame alongside the other backend types) so it is exported from
"@rezi-ui/core", then change the import in nodeBackend (where it currently
imports BackendBeginFrame from "@rezi-ui/core/backend") to import
BackendBeginFrame from "@rezi-ui/core" instead to satisfy the packages/node
import boundary rule.

Comment on lines 824 to +892
try {
const frameSeq = nextFrameSeq++;
const fp = frameAudit.enabled ? drawlistFingerprint(drawlist) : null;
maybeDumpDrawlistBytes("backend-inline", "requestFrame", frameSeq, drawlist);
if (fp !== null) {
frameAudit.emit("frame.submitted", {
frameSeq,
submitPath: "requestFrame",
transport: "inline-v1",
...fp,
});
frameAudit.emit("frame.submit.payload", {
frameSeq,
transport: "inline-v1",
...fp,
});
}
const submitRc = native.engineSubmitDrawlist(engineId, drawlist);
if (frameAudit.enabled) {
frameAudit.emit("frame.submit.result", {
frameSeq,
submitResult: submitRc,
});
}
if (submitRc < 0) {
if (frameAudit.enabled) {
frameAudit.emit("frame.completed", {
frameSeq,
completedResult: submitRc,
});
}
return Promise.reject(
new ZrUiError(
"ZRUI_BACKEND_ERROR",
`engine_submit_drawlist failed: code=${String(submitRc)}`,
),
);
}
if (frameAudit.enabled) {
frameAudit.emit("frame.accepted", { frameSeq });
}
const presentRc = native.enginePresent(engineId);
if (frameAudit.enabled) {
frameAudit.emit("frame.present.result", {
frameSeq,
presentResult: presentRc,
});
}
if (presentRc < 0) {
if (frameAudit.enabled) {
frameAudit.emit("frame.completed", {
frameSeq,
completedResult: presentRc,
});
}
return Promise.reject(
new ZrUiError("ZRUI_BACKEND_ERROR", `engine_present failed: code=${String(presentRc)}`),
);
}
if (frameAudit.enabled) {
frameAudit.emit("frame.completed", {
frameSeq,
completedResult: 0,
});
}
} catch (err) {
if (frameAudit.enabled) {
frameAudit.emit("frame.throw", { detail: safeDetail(err) });
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Include frameSeq on throw-path audit events.

At Line [891], frame.throw is emitted without frameSeq, so throw events cannot be reliably joined with the corresponding submitted frame.

🔧 Proposed fix
-      try {
-        const frameSeq = nextFrameSeq++;
+      const frameSeq = nextFrameSeq++;
+      try {
         const fp = frameAudit.enabled ? drawlistFingerprint(drawlist) : null;
         maybeDumpDrawlistBytes("backend-inline", "requestFrame", frameSeq, drawlist);
         if (fp !== null) {
@@
       } catch (err) {
         if (frameAudit.enabled) {
-          frameAudit.emit("frame.throw", { detail: safeDetail(err) });
+          frameAudit.emit("frame.throw", {
+            frameSeq,
+            detail: safeDetail(err),
+          });
         }
         return Promise.reject(safeErr(err));
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
const frameSeq = nextFrameSeq++;
const fp = frameAudit.enabled ? drawlistFingerprint(drawlist) : null;
maybeDumpDrawlistBytes("backend-inline", "requestFrame", frameSeq, drawlist);
if (fp !== null) {
frameAudit.emit("frame.submitted", {
frameSeq,
submitPath: "requestFrame",
transport: "inline-v1",
...fp,
});
frameAudit.emit("frame.submit.payload", {
frameSeq,
transport: "inline-v1",
...fp,
});
}
const submitRc = native.engineSubmitDrawlist(engineId, drawlist);
if (frameAudit.enabled) {
frameAudit.emit("frame.submit.result", {
frameSeq,
submitResult: submitRc,
});
}
if (submitRc < 0) {
if (frameAudit.enabled) {
frameAudit.emit("frame.completed", {
frameSeq,
completedResult: submitRc,
});
}
return Promise.reject(
new ZrUiError(
"ZRUI_BACKEND_ERROR",
`engine_submit_drawlist failed: code=${String(submitRc)}`,
),
);
}
if (frameAudit.enabled) {
frameAudit.emit("frame.accepted", { frameSeq });
}
const presentRc = native.enginePresent(engineId);
if (frameAudit.enabled) {
frameAudit.emit("frame.present.result", {
frameSeq,
presentResult: presentRc,
});
}
if (presentRc < 0) {
if (frameAudit.enabled) {
frameAudit.emit("frame.completed", {
frameSeq,
completedResult: presentRc,
});
}
return Promise.reject(
new ZrUiError("ZRUI_BACKEND_ERROR", `engine_present failed: code=${String(presentRc)}`),
);
}
if (frameAudit.enabled) {
frameAudit.emit("frame.completed", {
frameSeq,
completedResult: 0,
});
}
} catch (err) {
if (frameAudit.enabled) {
frameAudit.emit("frame.throw", { detail: safeDetail(err) });
}
const frameSeq = nextFrameSeq++;
try {
const fp = frameAudit.enabled ? drawlistFingerprint(drawlist) : null;
maybeDumpDrawlistBytes("backend-inline", "requestFrame", frameSeq, drawlist);
if (fp !== null) {
frameAudit.emit("frame.submitted", {
frameSeq,
submitPath: "requestFrame",
transport: "inline-v1",
...fp,
});
frameAudit.emit("frame.submit.payload", {
frameSeq,
transport: "inline-v1",
...fp,
});
}
const submitRc = native.engineSubmitDrawlist(engineId, drawlist);
if (frameAudit.enabled) {
frameAudit.emit("frame.submit.result", {
frameSeq,
submitResult: submitRc,
});
}
if (submitRc < 0) {
if (frameAudit.enabled) {
frameAudit.emit("frame.completed", {
frameSeq,
completedResult: submitRc,
});
}
return Promise.reject(
new ZrUiError(
"ZRUI_BACKEND_ERROR",
`engine_submit_drawlist failed: code=${String(submitRc)}`,
),
);
}
if (frameAudit.enabled) {
frameAudit.emit("frame.accepted", { frameSeq });
}
const presentRc = native.enginePresent(engineId);
if (frameAudit.enabled) {
frameAudit.emit("frame.present.result", {
frameSeq,
presentResult: presentRc,
});
}
if (presentRc < 0) {
if (frameAudit.enabled) {
frameAudit.emit("frame.completed", {
frameSeq,
completedResult: presentRc,
});
}
return Promise.reject(
new ZrUiError("ZRUI_BACKEND_ERROR", `engine_present failed: code=${String(presentRc)}`),
);
}
if (frameAudit.enabled) {
frameAudit.emit("frame.completed", {
frameSeq,
completedResult: 0,
});
}
} catch (err) {
if (frameAudit.enabled) {
frameAudit.emit("frame.throw", {
frameSeq,
detail: safeDetail(err),
});
}
return Promise.reject(safeErr(err));
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/node/src/backend/nodeBackendInline.ts` around lines 824 - 892, The
catch block currently emits frameAudit.emit("frame.throw", { detail:
safeDetail(err) }) without the frameSeq, so update that emit to include frameSeq
(e.g. frameAudit.emit("frame.throw", { frameSeq, detail: safeDetail(err) })) so
throw events can be correlated with the submitted frame; use the existing
frameSeq variable declared at the top of the try and keep the same
safeDetail(err) payload.

Comment on lines +17 to +19
import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
import { performance } from "node:perf_hooks";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Ensure custom audit log directory exists before append.

With nested REZI_FRAME_AUDIT_LOG paths, appendFileSync fails and the catch swallows it, so all audit records are silently dropped.

🛠️ Suggested fix
-import { join } from "node:path";
+import { dirname, join } from "node:path";
@@
 let frameAuditDumpCount = 0;
+let frameAuditLogPathPrepared = false;
@@
       if (FRAME_AUDIT_LOG_PATH !== null) {
+        if (!frameAuditLogPathPrepared) {
+          mkdirSync(dirname(FRAME_AUDIT_LOG_PATH), { recursive: true });
+          frameAuditLogPathPrepared = true;
+        }
         appendFileSync(FRAME_AUDIT_LOG_PATH, `${line}\n`, "utf8");
         if (!FRAME_AUDIT_STDERR_MIRROR) {
           return;
         }

Also applies to: 256-260

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/node/src/frameAudit.ts` around lines 17 - 19, The audit writer
currently calls appendFileSync/writeFileSync on the path derived from
REZI_FRAME_AUDIT_LOG and may fail silently if nested directories don't exist;
before any appendFileSync or writeFileSync call (the occurrences around
REZI_FRAME_AUDIT_LOG and in the functions that perform frame audit writes),
ensure the directory for the audit file exists by calling
mkdirSync(path.dirname(auditPath), { recursive: true }) (or equivalent) and only
then perform append/write; also propagate or log errors instead of swallowing
them so failures are visible.

Comment on lines +604 to +611
}
if (wrote >= DEBUG_FRAME_RECORD_BYTES) {
const dvPayload = new DataView(payload.buffer, payload.byteOffset, DEBUG_FRAME_RECORD_BYTES);
frameAudit.emit("native.frame.summary", {
reason,
recordId: recordId.toString(),
frameId: u64FromView(dvPayload, 0).toString(),
code,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Apply Biome formatter output for DataView blocks.

These ranges match the formatter failure reported in CI; apply biome check --fix (or equivalent) to pass.

Also applies to: 643-644, 672-673

🧰 Tools
🪛 GitHub Actions: ci

[error] 604-611: Formatter would have printed the following content: DataView construction spans multiple lines and formatting changes were suggested (DataView payload conversions and related fields). Run biome check --fix to apply formatting.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/node/src/worker/engineWorker.ts` around lines 604 - 611, The
DataView construction/emit blocks are misformatted; run the Biome formatter
(biome check --fix) or reformat the DataView-related blocks so they follow
project style, specifically the block that creates dvPayload (new
DataView(payload.buffer, payload.byteOffset, DEBUG_FRAME_RECORD_BYTES)) and the
subsequent frameAudit.emit call that references reason, recordId,
u64FromView(dvPayload, 0).toString(), and code; also fix the similar DataView
blocks at the other occurrences (around the u64FromView usages at the noted
ranges) so indentation, spacing, and punctuation match the rest of the file and
pass CI.

Comment on lines +288 to +297
if (recordsByPid.size > 0) {
process.stdout.write(`pids=${recordsByPid.size}\n`);
process.stdout.write("pid_sessions:\n");
const sessionRows = [...recordsByPid.entries()].sort((a, b) => a[0] - b[0]);
for (const [pid, s] of sessionRows) {
const modes = [...s.modes].sort().join("|");
process.stdout.write(
` pid=${pid} records=${s.count} firstTs=${s.firstTs ?? "-"} lastTs=${s.lastTs ?? "-"} modes=${modes}\n`,
);
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Filter pid_sessions output when a PID filter is active.

records is filtered at Line 96-Line 98, but pids/pid_sessions are printed from the unfiltered recordsByPid, so filtered reports can show unrelated sessions.

💡 Suggested fix
-if (recordsByPid.size > 0) {
-  process.stdout.write(`pids=${recordsByPid.size}\n`);
+const pidSessions = [...recordsByPid.entries()].filter(
+  ([pid]) => pidFilter === null || pid === pidFilter,
+);
+if (pidSessions.length > 0) {
+  process.stdout.write(`pids=${pidSessions.length}\n`);
   process.stdout.write("pid_sessions:\n");
-  const sessionRows = [...recordsByPid.entries()].sort((a, b) => a[0] - b[0]);
+  const sessionRows = pidSessions.sort((a, b) => a[0] - b[0]);
   for (const [pid, s] of sessionRows) {
     const modes = [...s.modes].sort().join("|");
     process.stdout.write(
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (recordsByPid.size > 0) {
process.stdout.write(`pids=${recordsByPid.size}\n`);
process.stdout.write("pid_sessions:\n");
const sessionRows = [...recordsByPid.entries()].sort((a, b) => a[0] - b[0]);
for (const [pid, s] of sessionRows) {
const modes = [...s.modes].sort().join("|");
process.stdout.write(
` pid=${pid} records=${s.count} firstTs=${s.firstTs ?? "-"} lastTs=${s.lastTs ?? "-"} modes=${modes}\n`,
);
}
const pidSessions = [...recordsByPid.entries()].filter(
([pid]) => pidFilter === null || pid === pidFilter,
);
if (pidSessions.length > 0) {
process.stdout.write(`pids=${pidSessions.length}\n`);
process.stdout.write("pid_sessions:\n");
const sessionRows = pidSessions.sort((a, b) => a[0] - b[0]);
for (const [pid, s] of sessionRows) {
const modes = [...s.modes].sort().join("|");
process.stdout.write(
` pid=${pid} records=${s.count} firstTs=${s.firstTs ?? "-"} lastTs=${s.lastTs ?? "-"} modes=${modes}\n`,
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/frame-audit-report.mjs` around lines 288 - 297, The pid summary is
built from the unfiltered recordsByPid so pid_sessions shows unrelated sessions;
rebuild or filter recordsByPid from the already-filtered records (the same
filteredRecords/records used at the earlier filter) before computing
sessionRows, or apply the same predicate when iterating recordsByPid; update the
code that produces sessionRows/for-loop (variables: recordsByPid, sessionRows,
pid_sessions, records) to use the filtered set so pids and pid_sessions reflect
the active PID filter.

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