Skip to content

feat(node,native): inline screen mode (Zireael v1.4.0)#416

Merged
RtlZeroMemory merged 2 commits into
mainfrom
feat/inline-screen-mode
Jun 11, 2026
Merged

feat(node,native): inline screen mode (Zireael v1.4.0)#416
RtlZeroMemory merged 2 commits into
mainfrom
feat/inline-screen-mode

Conversation

@RtlZeroMemory

Copy link
Copy Markdown
Owner

Summary

Adds inline screen mode to Rezi as a first-class app option: render a bounded region on the primary screen with terminal scrollback preserved above it and the final frame left in scrollback on exit — the presentation style of agent CLIs, progress UIs, and REPLs.

const app = createNodeApp({
  initialState: {},
  config: { screen: { mode: "inline", inlineRows: 9 } },
});

What changed

Vendor

  • Zireael bumped to v1.4.0 (8745d50, engine ABI 1.3.0) in the submodule, the packages/native/vendor/zireael build copy, and VENDOR_COMMIT.txt (integrity check green).

Native binding (@rezi-ui/native)

  • FFI struct layouts updated: plat_config_t.screen_mode (former pad byte), inline_rows on create/runtime configs, and zr_term_state_t (new engine fields) — the layout-verification test now pins the new sizes/offsets.
  • Strict config validation accepts plat.screenMode + inlineRows (camel/snake) on create and runtime paths.

Core

  • Requested engine ABI pin bumped to 1.3.0 (exact-match negotiation).

Node backend (@rezi-ui/node)

  • New NodeBackendConfig.screen option ({ mode: "alt" | "inline", inlineRows }), validated with ZrUiError and folded into the native config on both worker and in-process execution paths; the high-level option wins over raw nativeConfig passthrough keys while preserving unrelated plat keys. Omitting screen leaves config byte-for-byte unchanged.
  • Exported NodeBackendScreenConfig; createNodeApp forwards config.screen.
  • Naming note: distinct from executionMode: "inline" (engine-on-main-thread); documented in all relevant places.

Example + docs

  • examples/inline-status: runnable inline app (spinner, progress bar, quit keys).
  • New centralized guide docs/guide/screen-modes.md (+ mkdocs nav), sections in docs/backend/node.md, ABI pin + note in docs/protocol/abi.md, README feature visibility, CLAUDE.md updates.

Test evidence

  • Full suite: 5079/5079 pass (9 new: merge/validation matrix + engineCreate forwarding via a config-expectation shim on both execution paths); scripts + e2e suites green; lint/typecheck/docs build green.
  • Rust: 15/15 binding tests incl. updated FFI layout assertions; clippy clean (one pre-existing warning untouched).
  • Direct native smoke (pipe mode): inline create OK with capability suppression visible from JS (supportsScrollRegion: false), ABI 1.2.0 request rejected (-4), invalid combos rejected (-1), alt mode unchanged, strict unknown-key rejection.
  • Real terminal (tmux): scrollback preserved above the live Rezi panel, spinner/progress animate through the full widget→drawlist→worker→engine pipeline, mid-run resize (90→70 cols) reflows cleanly, exit code 0 with the final frame in scrollback and the prompt restored below; terminal fully usable after exit.

Compatibility

  • Default (no screen option) behavior is unchanged; all pre-existing tests pass unmodified.
  • The vendored engine's exact-match ABI negotiation means this Rezi version requires Zireael ABI 1.3.0 (vendored in this PR); mismatched requests fail create with a clear error.
  • Known limitation (engine-side, documented): protocol images fall back to sub-cell blitter rendering in inline mode.

🤖 Generated with Claude Code

RtlZeroMemory and others added 2 commits June 11, 2026 12:33
- vendor: bump Zireael to 8745d50 (v1.4.0, engine ABI 1.3.0) in submodule,
  build copy, and VENDOR_COMMIT.txt
- native: marshal plat.screenMode + inlineRows through strict config
  validation; update FFI struct layouts (plat_config_t, engine configs,
  zr_term_state_t) and the layout-verification test
- core: bump requested engine ABI pin to 1.3.0
- node: new NodeBackendConfig.screen option ({ mode: "alt" | "inline",
  inlineRows }) folded into the native engine config on both worker and
  in-process execution paths; validation via ZrUiError; screen option wins
  over raw nativeConfig passthrough keys
- tests: merge/validation matrix + engineCreate forwarding tests on both
  execution paths via a config-expectation shim

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- new centralized guide: docs/guide/screen-modes.md (usage, sizing,
  behavior, capability fallback, naming distinction vs executionMode)
- mkdocs nav, backend/node.md screen section, protocol/abi.md 1.3.0 pin,
  README feature visibility, CLAUDE.md examples/map updates
- examples/inline-status: runnable inline-mode app (spinner + progress +
  quit keys), verified end-to-end in a real terminal (tmux): scrollback
  preserved, live repaints, mid-run resize reflow, final frame left in
  scrollback with prompt restored below

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

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added inline screen mode support, enabling terminal widgets to render in a bounded region while preserving terminal scrollback history (complementing the existing full-screen mode).
  • Documentation

    • New Screen Modes guide documenting alt-screen and inline presentation options, including configuration, behavior differences, and viewport handling.
    • New inline-status example demonstrating inline mode configuration and usage.

Walkthrough

This PR introduces inline screen mode to Rezi, allowing TUI applications to render in a bounded viewport within the terminal's primary screen while preserving scrollback history. The implementation spans documentation, a Zireael native engine upgrade (v1.4.0), Rust FFI bindings, Node.js backend integration, and comprehensive tests and examples.

Changes

Inline Screen Mode Implementation

Layer / File(s) Summary
Documentation and Concept Introduction
docs/guide/screen-modes.md, README.md, CLAUDE.md, docs/backend/node.md
Comprehensive guides explaining inline vs. alt screen modes, configuration requirements (screen.mode: "inline", screen.inlineRows), viewport behavior (clamping to terminal height), capability fallbacks, and the distinction from executionMode: "inline".
ABI and Protocol Version Updates
packages/core/src/abi.ts, docs/protocol/abi.md, packages/native/vendor/zireael/include/zr/zr_version.h
Engine ABI bumped to 1.3.0, Zireael library updated to v1.4.0, documenting inline screen mode via plat.screenMode and inlineRows configuration.
Zireael Native Engine Implementation
packages/native/vendor/zireael/include/zr/zr_config.h, zr_platform_types.h, src/core/zr_config.c, zr_diff.h, zr_diff.c, zr_engine.c, zr_engine_present.inc, src/platform/posix/zr_plat_posix.c, src/platform/win32/zr_plat_win32.c, packages/native/vendor/VENDOR_COMMIT.txt
Core engine support: configuration validation (mode/inlineRows pairing), viewport clamping to terminal height, inline-mode-aware cursor positioning using relative motion instead of absolute, baseline establishment preserving scrollback, capability suppression for unsupported features, platform-specific enter/leave sequences gating alt-screen on inline mode, and farewell cursor positioning on exit.
Rust FFI Bindings and Config Parsing
packages/native/src/ffi.rs, packages/native/src/config.rs, packages/native/src/tests.rs
FFI constants (ZR_SCREEN_MODE_ALT, ZR_SCREEN_MODE_INLINE), struct layout updates to plat_config_t, zr_engine_config_t, zr_engine_runtime_config_t, and zr_term_state_t, plus Rust-side config parsing for screenMode and inlineRows with ABI layout assertions.
Node.js Backend Screen Configuration
packages/node/src/backend/nodeBackend/shared.ts, packages/node/src/backend/backendSharedConfig.ts, packages/node/src/backend/nodeBackend.ts, packages/node/src/backend/nodeBackendInline.ts
New NodeBackendScreenConfig type with optional mode and inlineRows, mergeScreenIntoNativeConfig() validation/conversion helper, and backend initialization patched to incorporate screen config into native engine initialization.
Node.js Public API and App Integration
packages/node/src/index.ts
Export of NodeBackendScreenConfig type and integration of screen config through app initialization pipeline.
Tests, Examples, and Build Configuration
packages/node/src/__tests__/screen_mode.test.ts, packages/node/src/__tests__/worker/testShims/screenExpectNative.ts, examples/inline-status/package.json, examples/inline-status/src/index.ts, examples/inline-status/tsconfig.json, tsconfig.json, mkdocs.yml
Full test suite covering screen mode config merging, validation, boundary cases, and backend integration; test shim module (screenExpectNative.ts) validating native engine calls; runnable inline-status example demonstrating inline mode with a progress widget and scrollback preservation; workspace references and documentation navigation updates.

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly Related PRs

  • RtlZeroMemory/Rezi#255: Introduces the strict native config parsing and validation layer (validate_known_keys, apply_plat, apply_runtime_cfg) that this PR extends to support screenMode and inlineRows configuration.

Poem

🐰 Inline we go, scrollback safe above,
Where bounded viewports render with love,
No alt-screen needed, just relative motion,
The terminal thanks this elegant notion! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 43.66% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main feature being added: inline screen mode support with the vendor upgrade to Zireael v1.4.0.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the feature, implementation details, test evidence, and compatibility notes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/inline-screen-mode

Warning

Billing warning: we have not been able to collect payment for this subscription for more than 72 hours. Please update the payment method or pay any pending invoices in Billing to avoid service interruption.


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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
packages/node/src/backend/nodeBackend/shared.ts (1)

3-25: ⚡ Quick win

Consolidated: object-shape declarations should use interface in these TS additions.

Both new screen-config contracts are object-shape type aliases; align both with the repository convention by switching to interface and readonly members.

As per coding guidelines, "Prefer interface for defining object shapes in TypeScript files".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/node/src/backend/nodeBackend/shared.ts` around lines 3 - 25, Replace
the object-shape type alias NodeBackendScreenConfig with an interface following
repo convention: change the exported declaration from "export type
NodeBackendScreenConfig = Readonly<...>" to "export interface
NodeBackendScreenConfig" and make its members readonly (e.g., readonly mode?:
"alt" | "inline"; readonly inlineRows?: number;), preserving the existing JSDoc
comments and the same optional semantics and validation notes for mode and
inlineRows.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/inline-status/src/index.ts`:
- Line 52: The root view currently returns SyncPanel directly in the app.view
handler which violates the root-view layout invariant; modify the app.view
registration (the app.view call) to wrap the returned SyncPanel component with a
root container such as ui.page({ p: 1 }, ...) or ui.appShell(...), so that
SyncPanel is passed as the child to ui.page/ui.appShell (keep SyncPanel(...) as
the inner component) and ensure p: 1 spacing is provided.

In `@packages/native/vendor/zireael/src/core/zr_diff.c`:
- Around line 754-766: Before creating inline blank baseline rows you must also
reset any open OSC 8 hyperlink state in addition to restoring SGR; update the
block that currently calls zr_emit_sgr_absolute(sb, ts,
zr_diff_baseline_style(), caps) and then writes CR/LF so that it also closes the
hyperlink (invoke the hyperlink transition/reset helper used elsewhere, e.g.
zr_diff_emit_link_transition(...) or the OSC8-reset routine) so the emitted
blank rows have link_ref==0 and no live terminal hyperlink; apply the same
change in the other analogous spot that currently only restores SGR.

In `@packages/native/vendor/zireael/src/core/zr_engine.c`:
- Around line 2081-2086: You sync the terminal keyboard with the prospective
profile via zr_engine_sync_kitty_keyboard(e, &prospective_profile) before
calling zr_engine_set_config_apply_viewport; if that apply fails you currently
goto cleanup leaving the terminal in the new keyboard state while
e->cfg_runtime/e->term_profile were not committed. Change the flow so that when
zr_engine_set_config_apply_viewport returns non-ZR_OK you immediately rollback
the kitty keyboard to the original profile (e.g., call
zr_engine_sync_kitty_keyboard(e, &original_profile) or a dedicated revert
helper) before jumping to cleanup; apply the same rollback logic to the other
occurrence around lines 2097-2099 to ensure terminal keyboard state stays in
sync with e->cfg_runtime/e->term_profile on failures.

In `@packages/node/src/__tests__/screen_mode.test.ts`:
- Around line 92-124: The tests only assert config forwarding via shims but do
not exercise the PTY contract (scrollback/final-frame semantics); update or add
tests to use the PTY harness instead of only `@xterm/headless` snapshots with
scrollback: 0 so you can assert scrollback preservation and that the final frame
is present in the scrollback for both execution modes. Specifically, modify the
existing tests named "backend: worker path forwards screen option to
engineCreate" and "backend: inline execution path forwards screen option to
engineCreate" (and/or add new tests) to start createNodeBackendInternal with the
same screen config but drive the actual PTY harness used by the backend (not
just screenExpectNative.js shim), configure a nonzero scrollback in the headless
terminal, capture the full buffer (including scrollback) after backend runs, and
assert that the expected final frame lines appear in the scrollback; keep
references to createNodeBackendInternal, screenExpectNative.js, and the PTY
harness so reviewers can locate the changes.

---

Nitpick comments:
In `@packages/node/src/backend/nodeBackend/shared.ts`:
- Around line 3-25: Replace the object-shape type alias NodeBackendScreenConfig
with an interface following repo convention: change the exported declaration
from "export type NodeBackendScreenConfig = Readonly<...>" to "export interface
NodeBackendScreenConfig" and make its members readonly (e.g., readonly mode?:
"alt" | "inline"; readonly inlineRows?: number;), preserving the existing JSDoc
comments and the same optional semantics and validation notes for mode and
inlineRows.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 494c85db-7265-4a79-abae-f690109b6b02

📥 Commits

Reviewing files that changed from the base of the PR and between 8be5f04 and d4840c6.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (33)
  • CLAUDE.md
  • README.md
  • docs/backend/node.md
  • docs/guide/screen-modes.md
  • docs/protocol/abi.md
  • examples/inline-status/package.json
  • examples/inline-status/src/index.ts
  • examples/inline-status/tsconfig.json
  • mkdocs.yml
  • packages/core/src/abi.ts
  • packages/native/src/config.rs
  • packages/native/src/ffi.rs
  • packages/native/src/tests.rs
  • packages/native/vendor/VENDOR_COMMIT.txt
  • packages/native/vendor/zireael/include/zr/zr_config.h
  • packages/native/vendor/zireael/include/zr/zr_platform_types.h
  • packages/native/vendor/zireael/include/zr/zr_version.h
  • packages/native/vendor/zireael/src/core/zr_config.c
  • packages/native/vendor/zireael/src/core/zr_diff.c
  • packages/native/vendor/zireael/src/core/zr_diff.h
  • packages/native/vendor/zireael/src/core/zr_engine.c
  • packages/native/vendor/zireael/src/core/zr_engine_present.inc
  • packages/native/vendor/zireael/src/platform/posix/zr_plat_posix.c
  • packages/native/vendor/zireael/src/platform/win32/zr_plat_win32.c
  • packages/node/src/__tests__/screen_mode.test.ts
  • packages/node/src/__tests__/worker/testShims/screenExpectNative.ts
  • packages/node/src/backend/backendSharedConfig.ts
  • packages/node/src/backend/nodeBackend.ts
  • packages/node/src/backend/nodeBackend/shared.ts
  • packages/node/src/backend/nodeBackendInline.ts
  • packages/node/src/index.ts
  • tsconfig.json
  • vendor/zireael

]);
});

app.view(() => SyncPanel({}));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wrap the root view with ui.page({ p: 1 }, ...) (or ui.appShell()).

Line 52 returns the panel directly, which violates the root-view layout invariant.

Suggested fix
-app.view(() => SyncPanel({}));
+app.view(() => ui.page({ p: 1 }, [SyncPanel({})]));

As per coding guidelines, Root view must use ui.page() or ui.appShell() with at least p: 1 spacing....

📝 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
app.view(() => SyncPanel({}));
app.view(() => ui.page({ p: 1 }, [SyncPanel({})]));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/inline-status/src/index.ts` at line 52, The root view currently
returns SyncPanel directly in the app.view handler which violates the root-view
layout invariant; modify the app.view registration (the app.view call) to wrap
the returned SyncPanel component with a root container such as ui.page({ p: 1 },
...) or ui.appShell(...), so that SyncPanel is passed as the child to
ui.page/ui.appShell (keep SyncPanel(...) as the inner component) and ensure p: 1
spacing is provided.

Source: Coding guidelines

Comment on lines +754 to +766
if (!zr_emit_sgr_absolute(sb, ts, zr_diff_baseline_style(), caps)) {
return false;
}
/* CR before LF claims: LF preserves the column in raw mode. */
if (!zr_sb_write_u8(sb, ZR_ASCII_CR)) {
return false;
}
ts->cursor_x = 0u;
for (uint32_t claim_y = claimed; claim_y <= target_y; claim_y++) {
if (!zr_sb_write_u8(sb, ZR_ASCII_LF)) {
return false;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reset OSC 8 along with SGR before creating inline blank baseline rows.

Line 754 and Line 1678 only restore the SGR baseline. If the previous cell left a hyperlink open, the subsequent LF/ED-generated blank area is modeled as baseline (link_ref == 0) while the terminal hyperlink state is still live until a later zr_diff_emit_link_transition(). That can leak link state across claimed rows or an inline baseline clear.

Possible fix
+static bool zr_emit_link_baseline(zr_sb_t* sb, zr_term_state_t* ts, const plat_caps_t* caps) {
+  if (!sb || !ts) {
+    return false;
+  }
+  if (caps && caps->supports_hyperlinks != 0u && !zr_emit_osc8_close(sb)) {
+    return false;
+  }
+  ts->style.link_ref = 0u;
+  return true;
+}
+
 ...
-      if (!zr_emit_sgr_absolute(sb, ts, zr_diff_baseline_style(), caps)) {
+      if (!zr_emit_link_baseline(sb, ts, caps) ||
+          !zr_emit_sgr_absolute(sb, ts, zr_diff_baseline_style(), caps)) {
         return false;
       }
 ...
-  if (!zr_emit_sgr_absolute(&ctx->sb, ts, baseline, ctx->caps)) {
+  if (!zr_emit_link_baseline(&ctx->sb, ts, ctx->caps) ||
+      !zr_emit_sgr_absolute(&ctx->sb, ts, baseline, ctx->caps)) {
     return ZR_ERR_LIMIT;
   }

Also applies to: 1678-1684

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/native/vendor/zireael/src/core/zr_diff.c` around lines 754 - 766,
Before creating inline blank baseline rows you must also reset any open OSC 8
hyperlink state in addition to restoring SGR; update the block that currently
calls zr_emit_sgr_absolute(sb, ts, zr_diff_baseline_style(), caps) and then
writes CR/LF so that it also closes the hyperlink (invoke the hyperlink
transition/reset helper used elsewhere, e.g. zr_diff_emit_link_transition(...)
or the OSC8-reset routine) so the emitted blank rows have link_ref==0 and no
live terminal hyperlink; apply the same change in the other analogous spot that
currently only restores SGR.

Comment on lines +2081 to 2086
rc = zr_engine_sync_kitty_keyboard(e, &prospective_profile);
if (rc != ZR_OK) {
goto cleanup;
}
rc = zr_engine_set_config_prepare_arenas(e, cfg, &arena_frame_new, &arena_persistent_new, &want_arena_reinit);
if (rc != ZR_OK) {
goto cleanup;
}

rc = zr_engine_sync_kitty_keyboard(e, &prospective_profile);
rc = zr_engine_set_config_apply_viewport(e, viewport_changed, viewport_rows_new);
if (rc != ZR_OK) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Rollback kitty keyboard state if config apply fails after sync.

At Line 2081 you sync kitty keyboard to the prospective profile before the final fallible step at Line 2085. If viewport resize fails, cleanup returns without commit, leaving terminal keyboard mode potentially out of sync with e->cfg_runtime / e->term_profile.

💡 Proposed fix
+  bool kitty_synced = false;
   rc = zr_engine_sync_kitty_keyboard(e, &prospective_profile);
   if (rc != ZR_OK) {
     goto cleanup;
   }
+  kitty_synced = true;
   rc = zr_engine_set_config_apply_viewport(e, viewport_changed, viewport_rows_new);
   if (rc != ZR_OK) {
     goto cleanup;
   }
@@
 cleanup:
+  if (rc != ZR_OK && kitty_synced) {
+    (void)zr_engine_sync_kitty_keyboard(e, &e->term_profile);
+  }
   zr_engine_set_config_release_resources(&res);
   return rc;

Also applies to: 2097-2099

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/native/vendor/zireael/src/core/zr_engine.c` around lines 2081 -
2086, You sync the terminal keyboard with the prospective profile via
zr_engine_sync_kitty_keyboard(e, &prospective_profile) before calling
zr_engine_set_config_apply_viewport; if that apply fails you currently goto
cleanup leaving the terminal in the new keyboard state while
e->cfg_runtime/e->term_profile were not committed. Change the flow so that when
zr_engine_set_config_apply_viewport returns non-ZR_OK you immediately rollback
the kitty keyboard to the original profile (e.g., call
zr_engine_sync_kitty_keyboard(e, &original_profile) or a dedicated revert
helper) before jumping to cleanup; apply the same rollback logic to the other
occurrence around lines 2097-2099 to ensure terminal keyboard state stays in
sync with e->cfg_runtime/e->term_profile on failures.

Comment on lines +92 to +124
test("backend: worker path forwards screen option to engineCreate", async () => {
const shim = new URL("./worker/testShims/screenExpectNative.js", import.meta.url).href;
const backend = createNodeBackendInternal({
config: {
executionMode: "worker",
fpsCap: 60,
maxEventBytes: 1024,
screen: { mode: "inline", inlineRows: 6 },
},
nativeShimModule: shim,
});

await backend.start();
await backend.stop();
backend.dispose();
});

test("backend: inline execution path forwards screen option to engineCreate", async () => {
const shim = new URL("./worker/testShims/screenExpectNative.js", import.meta.url).href;
const backend = createNodeBackendInternal({
config: {
executionMode: "inline",
fpsCap: 60,
maxEventBytes: 1024,
screen: { mode: "inline", inlineRows: 6 },
},
nativeShimModule: shim,
});

await backend.start();
await backend.stop();
backend.dispose();
});

Copy link
Copy Markdown
Contributor

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
# Verify whether PTY-oriented evidence/tests exist for inline screen semantics.
# Expected: at least one PTY-backed test or evidence artifact covering scrollback/final-frame behavior.
fd -i 'inline|screen-mode|screen_mode|pty|tmux' packages/node/src/__tests__ docs -t f
rg -n --iglob '**/*.test.ts' 'pty|tmux|scrollback|final frame|inline screen|screen mode' packages/node/src/__tests__

Repository: RtlZeroMemory/Rezi

Length of output: 1554


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Identify which PTY tests exist
fd -i 'pty|tmux|screen_mode|screen-mode|inline' packages/node/src/__tests__ -t f

# Search for evidence of scrollback / final frame semantics inside PTY and screen-related tests
rg -n --iglob '**/*.test.ts' \
  'scrollback|final frame|finalFrame|in scrollback|last frame|frame.*after burst|end frame|terminate.*scroll|render.*scroll|inline.*screen|screen mode' \
  packages/node/src/__tests__

# Also look for PTY harness usage to understand backend-path realism
rg -n --iglob '**/*.test.ts' \
  'runPtyScenario|ptyScenario|spawnPty|ptyTarget|tmux|node-pty|ptyEchoTarget|expect.*native|engineCreate|inlineRows|screen:\s*\{.*mode' \
  packages/node/src/__tests__/ptyScenario.test.ts \
  packages/node/src/__tests__/ptyTargetConfig.test.ts \
  packages/node/src/__tests__/worker_integration.test.ts \
  packages/node/src/__tests__/screen_mode.test.ts

Repository: RtlZeroMemory/Rezi

Length of output: 2826


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect relevant sections with line numbers (small excerpts only)
echo "=== screen_mode.test.ts (full file size check) ==="
wc -l packages/node/src/__tests__/screen_mode.test.ts
echo "=== ptyScenario.test.ts (first 220 lines) ==="
wc -l packages/node/src/__tests__/ptyScenario.test.ts
sed -n '1,220p' packages/node/src/__tests__/ptyScenario.test.ts

echo "=== ptyTargetConfig.test.ts (first 240 lines) ==="
wc -l packages/node/src/__tests__/ptyTargetConfig.test.ts
sed -n '1,240p' packages/node/src/__tests__/ptyTargetConfig.test.ts

echo "=== worker_integration.test.ts (around line ~780-860) ==="
wc -l packages/node/src/__tests__/worker_integration.test.ts
sed -n '760,860p' packages/node/src/__tests__/worker_integration.test.ts

echo "=== testing harness entrypoints for PTY scenarios ==="
rg -n "runPtyScenario|ptyScenario|spawnPty|ptyEchoTarget|ptyTarget" packages/node/src/__tests__/testingHarness.test.ts packages/node/src/__tests__/ptyScenario.test.ts packages/node/src/__tests__/worker_integration.test.ts -S

Repository: RtlZeroMemory/Rezi

Length of output: 10256


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for scrollback/final-frame semantics across node tests
rg -n --iglob '**/*.test.ts' \
  'scrollback|in scrollback|final frame|finalFrame|last frame|end frame|final.*frame|frame.*scroll|scroll.*frame' \
  packages/node/src/__tests__

# Search for inline screen mode semantics in PTY scenarios/tests
rg -n --iglob '**/*.test.ts' \
  'inlineRows|screen:\s*\{[^}]*mode:\s*"inline"|mode:\s*"inline"|screen-mode' \
  packages/node/src/__tests__

# Inspect PTY scenario harness implementation
fd -i 'runPtyScenario' packages/node/src/testing packages/node/src/__tests__ -t f
fd -i 'ptyScenario' packages/node/src/testing -t f

# Print small relevant sections
for f in $(fd -i 'runPtyScenario' packages/node/src -t f); do
  echo "=== $f (context) ==="
  wc -l "$f"
  rg -n 'function runPtyScenario|runPtyScenario\s*\(' "$f"
done

Repository: RtlZeroMemory/Rezi

Length of output: 2191


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show size
wc -l packages/node/src/testing/ptyScenario.ts

# Read key parts: imports + runPtyScenario definition + any assertions about terminal output/scrollback.
sed -n '1,240p' packages/node/src/testing/ptyScenario.ts
sed -n '240,520p' packages/node/src/testing/ptyScenario.ts

Repository: RtlZeroMemory/Rezi

Length of output: 14757


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for scrollback/final-frame/in scrollback mentions in the PTY harness implementation
rg -n 'scrollback|in scrollback|final frame|finalFrame|last frame|end frame|page.*frame|frame.*after|after burst' packages/node/src/testing/ptyScenario.ts packages/node/src/testing/ptyHarness.* packages/node/src/testing -S || true

# Find where the harness evaluates results and compares to scenario expectations
rg -n 'evaluateScenarioResult|runResult|mismatches|validateScenarioDefinition|ScenarioRunResult|cursor|latestCursor|renderSeq|visible' packages/node/src/testing/ptyScenario.ts

# Show the tail end where results are computed
tail -n 120 packages/node/src/testing/ptyScenario.ts

# Inspect PTY harness transport: startPtyHarness
fd -i 'ptyHarness' packages/node/src/testing -t f
sed -n '1,220p' $(fd -i 'ptyHarness' packages/node/src/testing -t f | head -n 1)

Repository: RtlZeroMemory/Rezi

Length of output: 9047


🏁 Script executed:

#!/bin/bash
set -euo pipefail

wc -l packages/node/src/testing/screen.ts
sed -n '1,220p' packages/node/src/testing/screen.ts
sed -n '220,520p' packages/node/src/testing/screen.ts

rg -n 'scrollback|final|snapshot\(|last|history|buffer' packages/node/src/testing/screen.ts

Repository: RtlZeroMemory/Rezi

Length of output: 3775


Add PTY contract coverage for inline screen scrollback/final-frame semantics

screen_mode.test.ts only verifies config forwarding of screen: { mode: "inline" } via shims. The PTY harness snapshots the terminal with @xterm/headless using scrollback: 0 and reads only term.buffer.active lines, so it can’t provide evidence for scrollback preservation or “final frame in scrollback” behavior on the real backend path—matching the guideline requirement for PTY evidence when terminal-visible behavior depends on the backend.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/node/src/__tests__/screen_mode.test.ts` around lines 92 - 124, The
tests only assert config forwarding via shims but do not exercise the PTY
contract (scrollback/final-frame semantics); update or add tests to use the PTY
harness instead of only `@xterm/headless` snapshots with scrollback: 0 so you can
assert scrollback preservation and that the final frame is present in the
scrollback for both execution modes. Specifically, modify the existing tests
named "backend: worker path forwards screen option to engineCreate" and
"backend: inline execution path forwards screen option to engineCreate" (and/or
add new tests) to start createNodeBackendInternal with the same screen config
but drive the actual PTY harness used by the backend (not just
screenExpectNative.js shim), configure a nonzero scrollback in the headless
terminal, capture the full buffer (including scrollback) after backend runs, and
assert that the expected final frame lines appear in the scrollback; keep
references to createNodeBackendInternal, screenExpectNative.js, and the PTY
harness so reviewers can locate the changes.

Sources: Coding guidelines, Learnings

@RtlZeroMemory
RtlZeroMemory merged commit 963f6f0 into main Jun 11, 2026
34 checks passed
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