Skip to content

refactor(core): split app runtime orchestration by concern#281

Merged
RtlZeroMemory merged 1 commit intomainfrom
codex/split-create-app
Mar 17, 2026
Merged

refactor(core): split app runtime orchestration by concern#281
RtlZeroMemory merged 1 commit intomainfrom
codex/split-create-app

Conversation

@RtlZeroMemory
Copy link
Copy Markdown
Owner

@RtlZeroMemory RtlZeroMemory commented Mar 17, 2026

Summary

  • Split packages/core/src/app/createApp.ts into internal helper modules.
  • Kept the public API unchanged.
  • No intended behavior change.

Why

  • Reduce the app-runtime orchestration monolith before tackling adjacent runtime files.

Validation

  • npm run lint
  • npm run typecheck
  • npm run build
  • node scripts/run-tests.mjs --filter "packages/core/dist/app/__tests__/"
  • node scripts/run-tests.mjs --filter "packages/core/dist/__tests__/integration/"
  • node scripts/run-tests.mjs

PTY / frame-audit evidence

  • Built native backend first with npm run build:native so the PTY app could start in this environment.
  • Deterministic PTY viewport started at 68x300, then resized to 40x120 during the run.
  • Exercised start/stop lifecycle, routed screen transitions, keyboard input, resize handling, and a rapid interactive burst (t23) while rendering was active.
  • Frame audit command: node scripts/frame-audit-report.mjs /tmp/rezi-frame-audit.ndjson --latest-pid
  • Audit summary for pid 22585: records=4969, backend_submitted=236, worker_payload=236, worker_accepted=236, worker_completed=236, hash_mismatch_backend_vs_worker=0.
  • Route summary: bridge submitted=196/completed=196, engineering 26/26, crew 11/11, settings 3/3.
  • Starship debug log captured route transitions, resize to 120x40, theme cycle, and quit handling in order.

Summary by CodeRabbit

Release Notes

  • Refactor
    • Reorganized application initialization architecture for improved code structure and maintainability.
    • Introduced new public resolveAppConfig utility for explicit application configuration handling.
    • Enhanced configuration validation with stricter bounds checking for runtime parameters.
    • Added new terminal profile loading capabilities.

@chatgpt-codex-connector
Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 17, 2026

📝 Walkthrough

Walkthrough

This PR refactors the app creation system by extracting configuration resolution, event loop, render loop, guard logic, keybinding utilities, and breadcrumb helpers into separate modules. The main createApp.ts file now exposes a public resolveAppConfig function while delegating core implementation details to these new dedicated modules, maintaining public API compatibility while reorganizing internal architecture.

Changes

Cohort / File(s) Summary
Core app creation refactoring
packages/core/src/app/createApp.ts
Refactored to expose resolveAppConfig as a public API; configuration validation and defaulting logic delegated to separate config module; internal constructs for event/render loops, guards, and keybindings extracted to dedicated modules while preserving public signatures.
Configuration utilities
packages/core/src/app/createApp/config.ts
New module introducing resolveAppConfig validation, ResolvedAppConfig type, backend marker readers, terminal profile loading via loadTerminalProfile, and runtime constants (DEFAULT_CONFIG, monotonicNowMs timer fallback).
Event and render lifecycle
packages/core/src/app/createApp/eventLoop.ts, packages/core/src/app/createApp/renderLoop.ts
Event loop module processes backend batches, user commits, and orchestrates work item processing with error handling; render loop module implements configurable rendering in raw/widget modes, manages theme transitions, emits metrics, and handles frame submission lifecycle.
Runtime guards and safety
packages/core/src/app/createApp/guards.ts
New module encapsulating app lifecycle and render/mutation safety checks via createAppGuards factory; provides assertion utilities (assertOperational, assertNotReentrant, etc.) and centralized error handling for reentrancy and lifecycle violations.
Keybinding and breadcrumb helpers
packages/core/src/app/createApp/keybindings.ts, packages/core/src/app/createApp/breadcrumbs.ts
Keybinding module manages routing-specific bindings, key code mapping, and validation; breadcrumb module provides runtime event tracking and snapshot building via createRuntimeBreadcrumbHelpers factory with state management for render/layout callbacks.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • PR #256: Extracts dirty-plan, focus-dispatcher, run-signals, and top-level error helpers from createApp into separate modules, paralleling this PR's refactoring of configuration, event/render, guard, and keybinding subsystems.
  • PR #265: Modifies createApp runtime control paths and related APIs for event/render lifecycle, directly overlapping with changes to event loop and render orchestration.
  • PR #223: Updates renderer/drawlist plumbing (RawRenderer/WidgetRenderer construction and drawlist version handling), affecting render loop implementation in this refactor.

🐰 Extracting logic left and right,
Config modules, guards shining bright,
Event loops dance, breadcrumbs trace,
Keybindings find their proper place,
Refactored clean, the code takes flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 1.47% 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 accurately describes the main refactoring objective: splitting the monolithic app runtime orchestration into separate modules organized by concern, with no public API changes intended.

✏️ 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
  • Commit unit tests in branch codex/split-create-app
📝 Coding Plan
  • Generate coding plan for human review comments

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

Copy link
Copy Markdown

@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: 4

🧹 Nitpick comments (1)
packages/core/src/app/createApp.ts (1)

375-394: Keep ResolvedAppConfig as the only source of truth for drawlistValidateParams.

The new config module already resolves this flag, but Lines 378-392 still re-read opts.config. Passing config.drawlistValidateParams through here keeps the renderer wiring aligned with resolveAppConfig() and avoids silent drift if a renderer default changes later.

♻️ Proposed cleanup
   const rawRenderer = new RawRenderer({
     backend,
     maxDrawlistBytes: config.maxDrawlistBytes,
-    ...(opts.config?.drawlistValidateParams === undefined
-      ? {}
-      : { drawlistValidateParams: opts.config.drawlistValidateParams }),
+    drawlistValidateParams: config.drawlistValidateParams,
     drawlistReuseOutputBuffer: config.drawlistReuseOutputBuffer,
     drawlistEncodedStringCacheCap: config.drawlistEncodedStringCacheCap,
   });
   const widgetRenderer = new WidgetRenderer<S>({
     backend,
@@
     rootPadding: config.rootPadding,
     breakpointThresholds: config.breakpointThresholds,
     terminalProfile,
-    ...(opts.config?.drawlistValidateParams === undefined
-      ? {}
-      : { drawlistValidateParams: opts.config.drawlistValidateParams }),
+    drawlistValidateParams: config.drawlistValidateParams,
     drawlistReuseOutputBuffer: config.drawlistReuseOutputBuffer,
     drawlistEncodedStringCacheCap: config.drawlistEncodedStringCacheCap,
🤖 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 375 - 394, The renderer
constructors are reading drawlistValidateParams from opts.config instead of the
resolved config, causing divergence from ResolvedAppConfig; update the
RawRenderer and WidgetRenderer initializers (the new RawRenderer(...) and
WidgetRenderer<S>(...) calls) to pass drawlistValidateParams:
config.drawlistValidateParams rather than using the conditional
...(opts.config?.drawlistValidateParams === undefined ? {} : {
drawlistValidateParams: opts.config.drawlistValidateParams }), so the renderers
always receive the resolved value from resolveAppConfig().
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/app/createApp/config.ts`:
- Around line 82-85: The config currently accepts any positive integer for
maxDrawlistBytes; update the resolution so after calling requirePositiveInt (or
using DEFAULT_CONFIG.maxDrawlistBytes) you clamp the result to the drawlist
layer's supported max (2147483647) to avoid later render-time failures—i.e., in
resolveAppConfig/update in createApp/config.ts compute value =
config.maxDrawlistBytes === undefined ? DEFAULT_CONFIG.maxDrawlistBytes :
requirePositiveInt("maxDrawlistBytes", config.maxDrawlistBytes) and then set
maxDrawlistBytes = Math.min(value, 2147483647).

In `@packages/core/src/app/createApp/eventLoop.ts`:
- Around line 97-125: The catch in commitUpdates currently enqueues a fatal but
lets execution continue; change commitUpdates (the catch block for state
updaters) to immediately abort the turn after calling options.fatalNowOrEnqueue
by rethrowing the caught error (or throwing a new sentinel) so callers like
eventBatch/userCommit and processTurn observe the failure and stop further
rendering; ensure the finally block still runs (so keep
options.setInCommit(false) and perfMarkEnd in finally) and only add the throw
inside the catch in commitUpdates to propagate the failure outwards.

In `@packages/core/src/app/createApp/renderLoop.ts`:
- Around line 327-341: The post-submit callbacks (focus/onRender/onLayout) may
throw or return early leaving res.inFlight unobserved; move the call to
scheduleFrameSettlement(res.inFlight, submitStartMs, buildEndMs) so it runs
immediately after updating frames-in-flight and interactive budget (i.e., right
after options.setFramesInFlight(...) and options.setInteractiveBudget(...)) and
before invoking emitFocusChangeIfNeeded(), emitInternalRenderMetrics(), and
emitInternalLayoutSnapshot() in the submitFrame handling path (and apply the
same reordering in the widget branch handled around the other submitFrame branch
noted in the review).
- Around line 325-333: The render timing uses perfNow() (which is 0 when
PERF_ENABLED is off) to compute renderTime, so update the render time
measurement to use monotonicNowMs() for public metrics while keeping
perfNow()/perfRecord()/perfMarkStart()/perfMarkEnd() around submit_frame for
optional profiling; specifically, replace the renderStart = perfNow() used
before calling options.rawRenderer.submitFrame(...) with renderStart =
monotonicNowMs(), and pass the monotonic delta (monotonicNowMs() - renderStart)
into emitInternalRenderMetrics so renderTime/renderTimeMs are always populated,
leaving perfMarkStart("submit_frame")/perfMarkEnd("submit_frame") and any
perfRecord usage intact for profiling; apply the same change in the other block
referenced (around emitInternalRenderMetrics later in the file).

---

Nitpick comments:
In `@packages/core/src/app/createApp.ts`:
- Around line 375-394: The renderer constructors are reading
drawlistValidateParams from opts.config instead of the resolved config, causing
divergence from ResolvedAppConfig; update the RawRenderer and WidgetRenderer
initializers (the new RawRenderer(...) and WidgetRenderer<S>(...) calls) to pass
drawlistValidateParams: config.drawlistValidateParams rather than using the
conditional ...(opts.config?.drawlistValidateParams === undefined ? {} : {
drawlistValidateParams: opts.config.drawlistValidateParams }), so the renderers
always receive the resolved value from resolveAppConfig().

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7a8b0bd5-7017-43f5-81d3-cb8c5e573ff4

📥 Commits

Reviewing files that changed from the base of the PR and between d4b23a1 and b932cc1.

📒 Files selected for processing (7)
  • packages/core/src/app/createApp.ts
  • packages/core/src/app/createApp/breadcrumbs.ts
  • packages/core/src/app/createApp/config.ts
  • packages/core/src/app/createApp/eventLoop.ts
  • packages/core/src/app/createApp/guards.ts
  • packages/core/src/app/createApp/keybindings.ts
  • packages/core/src/app/createApp/renderLoop.ts

@RtlZeroMemory RtlZeroMemory merged commit d494dda into main Mar 17, 2026
32 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