Skip to content

Improve create-react-on-rails-app fresh app onboarding#2849

Merged
justin808 merged 30 commits intomainfrom
fix/create-app-onboarding
Mar 29, 2026
Merged

Improve create-react-on-rails-app fresh app onboarding#2849
justin808 merged 30 commits intomainfrom
fix/create-app-onboarding

Conversation

@justin808
Copy link
Copy Markdown
Member

@justin808 justin808 commented Mar 25, 2026

Summary

  • give fresh apps a real root landing page with OSS/Pro/RSC links and auto-open it from bin/dev
  • teach create-react-on-rails-app to validate git and create educational commits for each scaffold step
  • extend the local smoke scaffold, docs, and changelog to cover the richer onboarding flow

Testing

  • pnpm --filter create-react-on-rails-app test -- --runInBand
  • pnpm --filter create-react-on-rails-app type-check
  • pnpm eslint packages/create-react-on-rails-app/src/create-app.ts packages/create-react-on-rails-app/src/index.ts packages/create-react-on-rails-app/src/utils.ts packages/create-react-on-rails-app/src/validators.ts packages/create-react-on-rails-app/tests/create-app.test.ts packages/create-react-on-rails-app/tests/validators.test.ts --no-warn-ignored
  • bundle exec rubocop react_on_rails/lib/generators/react_on_rails/base_generator.rb react_on_rails/lib/generators/react_on_rails/generator_messages.rb react_on_rails/lib/generators/react_on_rails/install_generator.rb react_on_rails/lib/generators/react_on_rails/templates/base/base/app/controllers/home_controller.rb react_on_rails/lib/react_on_rails/dev/server_manager.rb react_on_rails/spec/react_on_rails/dev/server_manager_spec.rb react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
  • NPM_CONFIG_CACHE=$(mktemp -d) bundle exec rspec react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb react_on_rails/spec/react_on_rails/dev/server_manager_spec.rb react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb:73 react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb:1511
  • KEEP_WORKDIR_ON_FAILURE=1 packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh

Note

Medium Risk
Changes the app scaffolding workflow across the Node CLI and Ruby generators (new root route/landing page, git automation, and new bin/dev browser-opening behavior), which could affect fresh-app creation flows and developer startup scripts across platforms.

Overview
Freshly scaffolded apps now start at a generated root landing page (/) with links to the SSR/RSC demo routes, key files, and curated OSS vs Pro/RSC resources. This is enabled via a new hidden --new-app generator mode that injects root to: "home#index" (when safe), adds HomeController + home/index template, and updates demo views to include richer onboarding content and cross-links.

bin/dev can now auto-open the browser once and supports new flags (--open-browser, --open-browser-once, --no-open-browser), implemented in ReactOnRails::Dev::ServerManager with a one-time marker file and route/URL normalization. The generator configures fresh apps to default to / and enable one-time open.

create-react-on-rails-app now requires git and records educational commits for each scaffold phase (Rails app creation, gem adds, generator run, pnpm normalization), using rails new --skip-git plus restored Rails .gitignore/.gitattributes templates for clean commits and disabling GPG signing for automation. Smoke tests, docs, and changelog are updated to cover --pro, the landing page, browser auto-open, and the new commit history.

Written by Cursor Bugbot for commit 81e287e. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Generated apps now land on a root home page linking to example pages, local docs, OSS/Pro guidance, a Pro quick‑start, and an RSC marketplace demo.
    • First run of bin/dev will auto‑open that home page once.
    • --rsc scaffolds the same landing experience with server‑example links.
    • App setup records step‑by‑step educational git commits.
  • Documentation

    • Quick‑start updated to reflect root home page, one‑time browser open, git prerequisite, and adjusted RSC/Pro guidance.

@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
Contributor

coderabbitai Bot commented Mar 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds fresh-app onboarding: generated apps include a root landing page at /, bin/dev can auto-open the browser once on first run, the CLI records step-by-step educational git commits during scaffold phases, validators now require git, and generator/Dev tooling and docs were updated accordingly.

Changes

Cohort / File(s) Summary
Changelog & Docs
CHANGELOG.md, docs/oss/getting-started/create-react-on-rails-app.md
Document fresh-app onboarding, home landing page at /, bin/dev first-run auto-open, educational git commits, added git prerequisite, updated OSS/Pro/RSC guidance and quick-start instructions.
CLI generator core & helpers
packages/create-react-on-rails-app/src/create-app.ts, packages/create-react-on-rails-app/src/index.ts, packages/create-react-on-rails-app/src/utils.ts, packages/create-react-on-rails-app/src/validators.ts
Always pass --new-app; add git automation (init, read user config, detect changes, create authored educational commits); add execCaptureArgs and childEnv; add validateGit() and include git in prerequisite checks; update help/success messaging to reference root URL and git-history.
Generator tests & smoke scripts
packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh, packages/create-react-on-rails-app/tests/...
Smoke and unit tests assert root route, landing-page contents, bin/dev constants/flags (DEFAULT_ROUTE="/", AUTO_OPEN_BROWSER_ONCE, --open-browser-once), verify expected educational git commit sequences, and extend mocks for new exec helpers.
Rails generators & messages
react_on_rails/lib/generators/react_on_rails/base_generator.rb, react_on_rails/lib/generators/react_on_rails/install_generator.rb, react_on_rails/lib/generators/react_on_rails/generator_messages.rb
Added hidden --new_app option; conditional injection of root to: "home#index" and generation of HomeController/view when appropriate; compute DEFAULT_ROUTE and AUTO_OPEN_BROWSER_ONCE; threaded landing_page into post-install message (affects printed visit URL).
Landing page template & controller
react_on_rails/lib/generators/.../app/controllers/home_controller.rb, .../app/views/home/index.html.erb.tt
Added HomeController template and a comprehensive ERB landing-page template (hero, examples, file hints, OSS/Pro/RSC/marketplace links, learning resources).
bin/dev template
react_on_rails/lib/generators/.../bin/dev
Added AUTO_OPEN_BROWSER_ONCE constant and logic to append --open-browser-once to dev server args when appropriate.
ServerManager & dev tooling
react_on_rails/lib/react_on_rails/dev/server_manager.rb, react_on_rails/spec/react_on_rails/dev/server_manager_spec.rb
Refactored CLI parsing; added --open-browser, --open-browser-once, --no-open-browser flags; threaded flags into start/run methods; implemented port-polling async browser opener with one-time marker file; normalized printed URLs.
Generator & message tests
react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb, react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb
Added tests for landing-page messaging and --new-app / --new-app --rsc contexts verifying root route, landing page content, and bin/dev constants/flags.
Utility tests
packages/create-react-on-rails-app/tests/utils.test.ts
Added tests for execLiveArgs/execCaptureArgs, verifying spawn options, environment merging, and stdout capture.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as CLI (create-app)
    participant Git as Git
    participant RailsGen as Rails generator
    participant Server as ServerManager

    CLI->>Git: ensure repository exists / read user.name & user.email
    CLI->>RailsGen: run `rails new` + generator args (--new-app, --rsc/--pro...)
    RailsGen-->>CLI: scaffold (may inject root route, HomeController, landing page)
    CLI->>Git: createEducationalCommit("rails new")
    CLI->>CLI: bundle add react_on_rails (and optionally react_on_rails_pro)
    CLI->>Git: createEducationalCommit("add react_on_rails / pro")
    CLI->>RailsGen: run react_on_rails generator (--new-app)
    CLI->>Git: createEducationalCommit("run generators / scaffold")
    CLI->>Server: print success message (visit http://localhost:3000) + git-history hint
    Note right of Server: on first bin/dev run, ServerManager may poll port and open browser once
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hop through commits, one tidy trace,
A cozy home page waits at the root space,
The dev server peeks, opens once to say hi,
Links to demos, docs, and guides waving nigh,
Little commits teach — come build and try!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.05% 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
Title check ✅ Passed The title accurately summarizes the main change: improving fresh app onboarding in create-react-on-rails-app by scaffolding a root landing page, updating bin/dev with browser auto-open, and adding educational git commits.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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 fix/create-app-onboarding

Warning

Review ran into problems

🔥 Problems

Timed out fetching pipeline failures after 30000ms


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

❤️ Share

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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 25, 2026

size-limit report 📦

Path Size
react-on-rails/client bundled (gzip) 62.63 KB (+0.01% 🔺)
react-on-rails/client bundled (gzip) (time) 62.63 KB (0%)
react-on-rails/client bundled (brotli) 53.7 KB (0%)
react-on-rails/client bundled (brotli) (time) 53.7 KB (0%)
react-on-rails-pro/client bundled (gzip) 63.65 KB (0%)
react-on-rails-pro/client bundled (gzip) (time) 63.65 KB (0%)
react-on-rails-pro/client bundled (brotli) 54.67 KB (0%)
react-on-rails-pro/client bundled (brotli) (time) 54.67 KB (0%)
registerServerComponent/client bundled (gzip) 127.46 KB (0%)
registerServerComponent/client bundled (gzip) (time) 127.46 KB (0%)
registerServerComponent/client bundled (brotli) 61.65 KB (0%)
registerServerComponent/client bundled (brotli) (time) 61.64 KB (-0.02% 🔽)
wrapServerComponentRenderer/client bundled (gzip) 122.02 KB (0%)
wrapServerComponentRenderer/client bundled (gzip) (time) 122.02 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) 56.77 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) (time) 56.77 KB (0%)

Comment thread react_on_rails/lib/react_on_rails/dev/server_manager.rb
Comment thread react_on_rails/lib/react_on_rails/dev/server_manager.rb
Comment thread react_on_rails/lib/react_on_rails/dev/server_manager.rb Outdated
@coderabbitai coderabbitai Bot added full-ci codex PRs created from codex-named branches labels Mar 25, 2026
Comment thread packages/create-react-on-rails-app/src/create-app.ts Outdated
Comment thread packages/create-react-on-rails-app/src/create-app.ts
Comment thread packages/create-react-on-rails-app/tests/create-app.test.ts Outdated
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 25, 2026

Greptile Summary

This PR significantly improves the fresh-app onboarding experience: create-react-on-rails-app now validates git as a prerequisite, creates one educational git commit per scaffold step, and the generated bin/dev auto-opens the app's new landing page on first boot. The landing page (app/views/home/index.html.erb) is a data-driven ERB template rendered from a Ruby config hash in base_generator.rb, linking to example routes, key files, and Pro/RSC resources. The browser-open feature in ServerManager is implemented as a background thread with CI/TTY guards and a marker file for "open once" semantics; because run_with_process_manager uses system() (not exec), the thread correctly survives the foreman/overmind call.

Key observations:

  • createEducationalCommit is nested inside each step's try/catch block — a git failure here would fire the step's error message (e.g. "Failed to create Rails application") and trigger cleanupAppDirectory, even though the step itself succeeded. This is the main concrete issue to fix.
  • The smoke-test assertion grep -q -- '--open-browser-once' is trivially true because the string always appears in the template; grep -q 'AUTO_OPEN_BROWSER_ONCE = true' is the meaningful check.
  • The --new-app flag is correctly declared as a hidden option in both base_generator.rb and install_generator.rb and forwarded through invoke.
  • The preexisting_root_route? guard correctly memoises the file read so no double root route is injected on existing apps.
  • educationalGitEnv gracefully falls back to DEFAULT_GIT_AUTHOR_NAME/DEFAULT_GIT_AUTHOR_EMAIL when git config user.* is not set, covering fresh CI environments and first-time developers.

Confidence Score: 4/5

  • Safe to merge after addressing the git-commit error-handling conflation in create-app.ts.
  • The PR is well-structured with comprehensive tests, correct CI/TTY guards on browser opening, proper memoisation in the route guard, and all existing tests updated. The one concrete bug — createEducationalCommit nested inside step try/catch blocks producing misleading errors and spurious cleanup — is straightforward to fix but is a real failure path (any git failure during scaffolding would delete the successfully-created app). The smoke-test assertion weakness is a minor follow-up. No security, data-loss, or production-runtime risk.
  • packages/create-react-on-rails-app/src/create-app.ts (git commit error handling), packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh (assertion strength)

Important Files Changed

Filename Overview
packages/create-react-on-rails-app/src/create-app.ts Adds educational git commits after each scaffold step and passes --new-app to the generator. Git commit calls are nested inside each step's existing try/catch, which can produce misleading error messages if the commit itself fails.
react_on_rails/lib/react_on_rails/dev/server_manager.rb Adds browser-open capability with CI/TTY guards, marker-file "once" semantics, and a clean extracted parse_cli_options. Uses system() (not exec) so the polling thread survives the process-manager call. All edge cases look well-handled.
react_on_rails/lib/generators/react_on_rails/base_generator.rb Adds the --new-app flag and generates the landing page (home controller, view, root route) conditioned on generate_new_app_home_page?. The preexisting_root_route? guard correctly memoises the file read and skips generation for existing apps.
react_on_rails/lib/generators/react_on_rails/install_generator.rb Forwards new_app to the base generator, introduces default_bin_dev_route, and always excludes dev from the directory copy before handling it separately with copy_file + two gsub_file patches. Logic is correct.
react_on_rails/lib/generators/react_on_rails/templates/base/base/app/views/home/index.html.erb.tt New 421-line ERB template driven entirely by the config hash. No inline logic beyond iteration — all data is prepared in Ruby. Includes responsive CSS, stack badges, example links, file hints, and learning resources.
packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh Adds expect_git_history helper and verifies home-page files, root routes, and bin/dev flags for all app variants. The --open-browser-once grep assertion is always true (string exists in template regardless of the flag value); the meaningful assertion would check AUTO_OPEN_BROWSER_ONCE = true.

Sequence Diagram

sequenceDiagram
    participant CLI as create-react-on-rails-app
    participant Git as Git
    participant Rails as rails new
    participant Bundle as Bundler
    participant Gen as react_on_rails:install --new-app
    participant Dev as bin/dev (thread)
    participant Browser as Browser

    CLI->>CLI: validateAll() — includes validateGit()
    CLI->>Rails: rails new --database=postgresql --skip-javascript
    Rails-->>CLI: app directory created + git init
    CLI->>Git: createEducationalCommit("Create Rails app with PostgreSQL")

    CLI->>Bundle: bundle add react_on_rails
    CLI->>Git: createEducationalCommit("Add react_on_rails gem")

    opt --rsc or --pro
        CLI->>Bundle: bundle add react_on_rails_pro
        CLI->>Git: createEducationalCommit("Add react_on_rails_pro gem")
    end

    CLI->>Gen: rails generate react_on_rails:install --new-app [flags]
    Gen-->>CLI: home_controller.rb + index.html.erb.tt + root route + bin/dev (AUTO_OPEN_BROWSER_ONCE=true)
    CLI->>Git: createEducationalCommit("Install React on Rails with …")

    opt packageManager == pnpm
        CLI->>CLI: normalizeGeneratedPackageManager (pnpm import + install)
        CLI->>Git: createEducationalCommit("Normalize the generated app for pnpm")
    end

    Note over CLI: User runs bin/dev
    Dev->>Dev: inject --open-browser-once into argv (AUTO_OPEN_BROWSER_ONCE=true)
    Dev->>Dev: schedule_browser_open thread spawned
    Dev->>Dev: ProcessManager.run_with_process_manager (system() — thread survives)
    Dev-->>Browser: wait_for_server_on_port → open_browser(url)
    Dev->>Dev: mark_browser_opened_once (writes tmp/react_on_rails/browser_opened_once)
Loading

Comments Outside Diff (1)

  1. packages/create-react-on-rails-app/src/create-app.ts, line 359-366 (link)

    P1 Git commit failure triggers misleading error and spurious cleanup

    createEducationalCommit is called inside the same try/catch block as the step command it follows. If the git commit fails (e.g. git add or git commit throws), the catch block fires with the step's error message and then calls cleanupAppDirectory — even though the actual step (e.g. rails new) completed successfully.

    For the Rails step the user would see:

    Failed to create Rails application. Check the output above for details.
    

    …and have their freshly generated app directory deleted, despite Rails completing normally.

    The same pattern repeats for the bundle add react_on_rails step (around the gem-add try/catch) and the bundle add react_on_rails_pro step.

    Consider wrapping each createEducationalCommit call in its own try/catch that logs a non-fatal warning instead of aborting:

    try {
      execLiveArgs('rails', ['new', appName, '--database=postgresql', '--skip-javascript']);
      logStepDone('Rails application created');
    } catch (error) {
      logError('Failed to create Rails application. Check the output above for details.');
      cleanupAppDirectory(appPath, appName);
      process.exit(1);
    }
    try {
      const { subject, body } = railsBaselineCommitMessage();
      createEducationalCommit(appPath, subject, body);
    } catch (error) {
      logInfo('Educational git commit skipped (git error). The app was still created successfully.');
    }

Reviews (1): Last reviewed commit: "Add changelog entry for fresh app onboar..." | Re-trigger Greptile

Comment thread packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Baseline educational commit silently skipped when git configured
    • Added --skip-git to rails new so the CLI always owns repo initialization and reliably creates the baseline educational commit.
  • ✅ Fixed: Git commit failure deletes app and shows wrong error
    • Wrapped educational commit creation in its own try/catch with a warning log so git commit failures no longer trigger scaffold-step cleanup and misleading fatal errors.

Create PR

Or push these changes by commenting:

@cursor push f6ad2a0abd
Preview (f6ad2a0abd)
diff --git a/packages/create-react-on-rails-app/src/create-app.ts b/packages/create-react-on-rails-app/src/create-app.ts
--- a/packages/create-react-on-rails-app/src/create-app.ts
+++ b/packages/create-react-on-rails-app/src/create-app.ts
@@ -211,14 +211,19 @@
 }
 
 function createEducationalCommit(appPath: string, subject: string, body: string): void {
-  ensureGitRepository(appPath);
+  try {
+    ensureGitRepository(appPath);
 
-  if (!gitHasPendingChanges(appPath)) {
-    return;
+    if (!gitHasPendingChanges(appPath)) {
+      return;
+    }
+
+    execLiveArgs('git', ['add', '-A'], appPath);
+    execLiveArgs('git', ['commit', '-m', subject, '-m', body], appPath, educationalGitEnv(appPath));
+  } catch (error) {
+    const message = error instanceof Error ? error.message : 'unknown git error';
+    logInfo(`Skipping educational commit "${subject}" due to git error: ${message}`);
   }
-
-  execLiveArgs('git', ['add', '-A'], appPath);
-  execLiveArgs('git', ['commit', '-m', subject, '-m', body], appPath, educationalGitEnv(appPath));
 }
 
 function railsBaselineCommitMessage(): { subject: string; body: string } {
@@ -351,7 +356,7 @@
   // so it's always a simple directory name safe to use with rails new.
   logStep(currentStep, totalSteps, 'Creating Rails application...');
   try {
-    execLiveArgs('rails', ['new', appName, '--database=postgresql', '--skip-javascript']);
+    execLiveArgs('rails', ['new', appName, '--database=postgresql', '--skip-javascript', '--skip-git']);
     const { subject, body } = railsBaselineCommitMessage();
     createEducationalCommit(appPath, subject, body);
     logStepDone('Rails application created');

diff --git a/packages/create-react-on-rails-app/tests/create-app.test.ts b/packages/create-react-on-rails-app/tests/create-app.test.ts
--- a/packages/create-react-on-rails-app/tests/create-app.test.ts
+++ b/packages/create-react-on-rails-app/tests/create-app.test.ts
@@ -270,6 +270,7 @@
       'my-app',
       '--database=postgresql',
       '--skip-javascript',
+      '--skip-git',
     ]);
     expect(mockedExecLiveArgs).toHaveBeenNthCalledWith(
       4,
@@ -309,6 +310,7 @@
       'my-app',
       '--database=postgresql',
       '--skip-javascript',
+      '--skip-git',
     ]);
     expect(mockedExecLiveArgs).toHaveBeenNthCalledWith(
       4,
@@ -365,6 +367,7 @@
       'my-app',
       '--database=postgresql',
       '--skip-javascript',
+      '--skip-git',
     ]);
     expect(mockedExecLiveArgs).toHaveBeenNthCalledWith(
       4,
@@ -391,6 +394,23 @@
     expect(processExitSpy).not.toHaveBeenCalled();
   });
 
+  it('continues app creation when an educational commit fails', () => {
+    const appPath = path.resolve(process.cwd(), 'my-app');
+    mockedExecLiveArgs.mockImplementation((command, args) => {
+      if (command === 'git' && args[0] === 'commit' && args[2] === 'Create Rails app with PostgreSQL') {
+        throw new Error('hook rejected commit');
+      }
+    });
+
+    createApp('my-app', baseOptions);
+
+    expect(processExitSpy).not.toHaveBeenCalled();
+    expect(mockedFs.rmSync).not.toHaveBeenCalledWith(appPath, { recursive: true, force: true });
+    expect(mockedLogInfo).toHaveBeenCalledWith(
+      expect.stringContaining('Skipping educational commit "Create Rails app with PostgreSQL" due to git error'),
+    );
+  });
+
   it('converts pnpm scaffolds away from npm artifacts', () => {
     const appPath = path.resolve(process.cwd(), 'my-app');
     const packageJsonPath = path.join(appPath, 'package.json');

Comment thread packages/create-react-on-rails-app/src/create-app.ts Outdated
Comment thread packages/create-react-on-rails-app/src/create-app.ts
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 25, 2026

AGENTS.md

Instructions for AI coding agents working on the React on Rails codebase.

React on Rails is a Ruby gem + npm package that integrates React with Ruby on Rails, providing server-side rendering (SSR) via Node.js or ExecJS. This is a monorepo: the open-source gem lives at react_on_rails/, the npm package at packages/react-on-rails/, and the Pro package at react_on_rails_pro/.

Reusable Workflows

  • AGENTS.md: canonical entry point for agent instructions and workflow discovery
  • .claude/commands/: Claude Code slash commands
  • .agents/workflows/: shared prompt templates and reusable workflows for Codex, GPT, and other non-Claude tools
  • When the user asks to address PR review comments outside Claude slash commands, follow .agents/workflows/address-review.md

Canonical Agent Policy

AGENTS.md is the canonical source for repository-wide agent rules:

  • Commands and test/lint workflow
  • Code style and formatting expectations
  • Git/PR boundaries and safety rules
  • Directory and documentation boundaries

Other agent-facing docs (for example CLAUDE.md) should contain only tool-specific workflow notes and link back here.
If there is a conflict, AGENTS.md wins.

Commands

# Install dependencies
bundle && pnpm install

# Build TypeScript → JavaScript
pnpm run build

# Lint (MANDATORY before every commit)
bundle exec rubocop                  # Ruby — must pass with zero offenses
pnpm run lint                        # JS/TS via ESLint
pnpm start format.listDifferent      # Check Prettier formatting
rake lint                            # All linting (Ruby + JS + formatting)

# Auto-fix formatting
rake autofix                         # Preferred for all formatting

# Run tests
rake run_rspec:gem                   # Ruby unit tests (gem code)
rake run_rspec:dummy                 # Ruby integration tests (dummy Rails app)
pnpm run test                        # JavaScript/TypeScript tests
rake                                 # Full suite (lint + all tests except examples)

# Type checking
pnpm run type-check                  # TypeScript
bundle exec rake rbs:validate        # RBS signatures

# Additional test subsets
rake run_rspec                       # All Ruby tests
rake all_but_examples                # All tests except generated examples
rake run_rspec:shakapacker_examples_basic  # Single example test

# Full initial setup
bundle && pnpm install && rake shakapacker_examples:gen_all && rake node_package && rake

# CI/workflow linting
actionlint                           # GitHub Actions lint
yamllint .github/                    # YAML lint (do NOT run RuboCop on .yml files)

# Dependency version updates
rake shakapacker:update_version[9.6.1]  # Update shakapacker across the monorepo

Updating Shakapacker

Use rake shakapacker:update_version[VERSION] to update shakapacker across the entire monorepo. This single command updates all Gemfiles, package.json files, Gemfile.lock files, and pnpm-lock.yaml. Do not manually edit individual version references — always use the rake task to keep everything in sync.

The task handles Ruby version switching for apps that require a different Ruby version (set RUBY_VERSION_MANAGER to rvm, rbenv, asdf, or mise if needed; defaults to rvm). It continues gracefully if a single lock file update fails (e.g., due to a missing Ruby version).

Testing

  • Prefer local testing over CI iteration — don't push "hopeful" fixes. Apply the 15-minute rule: if 15 more minutes of local testing would catch the issue before CI does, spend the 15 minutes.
  • Never claim a test is "fixed" without running it locally first. Use "This SHOULD fix..." or "Proposed fix (UNTESTED)" for unverified changes.
  • Ruby: RSpec. Unit tests in react_on_rails/spec/react_on_rails/, integration tests via a dummy Rails app in react_on_rails/spec/dummy/.
  • JavaScript/TypeScript: Jest. Tests in packages/react-on-rails/tests/.
  • E2E: Playwright. Tests in react_on_rails/spec/dummy/e2e/playwright/e2e/. Run with cd react_on_rails/spec/dummy && pnpm test:e2e.
  • The dummy app (react_on_rails/spec/dummy/) is a full Rails application used for integration testing. Many tests require it.

Run specific test files:

bundle exec rspec react_on_rails/spec/react_on_rails/path/to/spec.rb
cd react_on_rails/spec/dummy && bundle exec rspec spec/path/to/spec.rb

Project Structure

Directory Purpose
react_on_rails/lib/react_on_rails/ Ruby gem source — helpers, configuration, SSR pool, engine
react_on_rails/lib/generators/ Rails generators for react_on_rails:install
react_on_rails/spec/ RSpec tests (unit + integration via dummy app)
react_on_rails/spec/dummy/ Full Rails app for integration testing and E2E
packages/react-on-rails/src/ TypeScript source — client-side React integration
packages/react-on-rails/tests/ Jest tests for the npm package
react_on_rails_pro/ Pro package (separate gem + npm, own lint config)
rakelib/ Rake task definitions
docs/oss/ OSS documentation — published to the ShakaCode website
docs/pro/ Pro documentation — installation, configuration, RSC, node renderer, caching
internal/contributor-info/ Internal contributor docs (not published to the website)
internal/planning/ Internal planning docs, drafts, and historical analysis
internal/react_on_rails_pro/contributors-info/ Internal Pro contributor docs (not published to the website)
analysis/ Investigation and analysis documents (kebab-case .md files)

Code Style

Ruby (RuboCop)

Line length max 120 characters. Run bundle exec rubocop [file] to check.

Line length — break long chains:

# Bad
content = pack_content.gsub(/import.*from.*['"];/, "").gsub(/ReactOnRails\.register.*/, "")

# Good
content = pack_content.gsub(/import.*from.*['"];/, "")
                      .gsub(/ReactOnRails\.register.*/, "")

Named subjects in RSpec:

# Bad
subject { instance.method_name(arg) }

# Good
subject(:method_result) { instance.method_name(arg) }

Security violations — scope disable comments tightly:

# rubocop:disable Security/Eval
expect { evaluate(sanitized_content) }.not_to raise_error
# rubocop:enable Security/Eval

JavaScript/TypeScript

Prettier handles all formatting. Never manually format — run rake autofix instead.

Git Workflow

Branch naming: type/descriptive-name (e.g., fix/ssr-hydration-mismatch)

Commit messages: Explain why, not what. One logical change per commit.

PR creation: Use gh pr create with a clear title, summary, and test plan.

Review Workflow

For small, focused PRs (roughly 5 files changed or fewer and one clear purpose):

  • Use at most one AI reviewer that leaves inline comments. Additional AI tools should be summary-only or used manually.
  • Wait for the first full review pass to finish before pushing follow-up commits.
  • Batch review fixes into one follow-up push when practical. Do not create a new commit for each minor comment.
  • Treat as blocking only: correctness bugs, failing tests, regressions, and clear inconsistencies with adjacent code. Nits and style suggestions are optional unless a maintainer asks for them.
  • Verify language, runtime, and library claims locally before changing code in response to AI review comments.
  • Deduplicate repeated bot comments before acting on them. Fix the underlying issue once, then resolve the duplicates.
  • Rebase or merge main once, near the end of the review cycle. For CHANGELOG.md conflicts, prefer resolving them as the final step before merge.
  • When asking an agent to address review comments, instruct it to classify comments into blocking, optional, and noise, then apply only the blocking items plus any explicitly selected optional items.

Boundaries

Always

  • Run bundle exec rubocop before committing — CI will reject violations
  • Use pnpm for all JS operations — never npm or yarn
  • Use bundle exec for Ruby commands
  • Ensure all files end with a newline
  • Let Prettier and RuboCop handle formatting — never format manually

Ask First

  • Destructive git operations (force push, reset --hard, branch deletion)
  • Changes to CI workflows (.github/workflows/)
  • Changes to build configuration (package.json scripts, webpack config)
  • Modifying the Pro package (react_on_rails_pro/)

Never

  • Skip pre-commit hooks (--no-verify)
  • Commit secrets, credentials, or .env files
  • Commit package-lock.json, yarn.lock, or other non-pnpm lock files
  • Add files to the docs/ root — OSS docs go in docs/oss/ subdirectories (getting-started/, core-concepts/, building-features/, configuration/, api-reference/, deployment/, migrating/, upgrading/, misc/); Pro docs go in docs/pro/
  • Force push to main or master

Key Concept: File Suffixes vs. RSC Directive

React on Rails has two independent systems that both use "client" and "server" terminology. Do not confuse them.

1. Bundle Placement (.client. / .server. file suffixes)

A React on Rails auto-bundling feature that controls which webpack bundle imports a file. This exists independently of React Server Components and is used with or without RSC:

  • Component.client.jsx → imported only in the client bundle (browser)
  • Component.server.jsx → imported only in the server bundle (and RSC bundle when RSC enabled)
  • Component.jsx (no suffix) → imported in both bundles

This controls where the source file is loaded, nothing more. A .server.jsx file is NOT a React Server Component — it is simply a file that webpack includes in the server bundle (and the RSC bundle when RSC is enabled). These suffixes only make sense for client components, as server components exist only in the RSC bundle.

2. RSC Classification ('use client' directive)

The 'use client' directive is part of the React Server Components architecture. It marks a component as a React Client Component. Components without it are treated as React Server Components.

When auto-bundling is enabled with RSC support (Pro feature), React on Rails uses this directive to control:

  • Registration: 'use client'ReactOnRails.register(), no 'use client'registerServerComponent()
  • RSC bundling: The RSC webpack loader uses this directive to decide whether a component is included in the RSC bundle or replaced with a client reference in that bundle

The client_entrypoint? method in packs_generator.rb checks for this directive.

They Are Orthogonal

A .client.jsx file can be a React Server Component (if it lacks 'use client'), and a .server.jsx file can be a React Client Component (if it has 'use client'). In practice, paired .client./.server. files should have consistent 'use client' status because the client and server must agree on the component's RSC role for hydration to work.

Changelog

Update /CHANGELOG.md for user-visible changes only (features, bug fixes, breaking changes, deprecations, performance improvements). Do not add entries for linting, formatting, refactoring, tests, or doc fixes.

  • Format: [PR 1818](https://github.com/shakacode/react_on_rails/pull/1818) by [username](https://github.com/username) (no hash before PR number)
  • Pro-only changes use an inline **[Pro]** tag prefix within the standard category sections (e.g., - **[Pro]** **Feature name**: Description...); do NOT create separate #### Pro subsections

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 25, 2026

PR Review: Improve create-react-on-rails-app fresh app onboarding

Overall this is a well-structured feature. The generated landing page, educational git history, and browser auto-open are all valuable developer UX improvements. Good test coverage across the new code paths. A few issues worth addressing before merging:

Bug: explicit --open-browser silently no-ops in non-TTY contexts

browser_auto_open_allowed? checks stdin.tty? and stdout.tty?, which makes sense for the auto/generated AUTO_OPEN_BROWSER_ONCE behavior. But schedule_browser_open applies this same guard for both the implicit case (generated constant) and the explicit case (user typed --open-browser). In a Docker container, an SSH session without a pseudo-TTY, or when output is piped, the flag silently does nothing with no warning or error.

The TTY guard should only apply to the auto-open path, not to explicit user opt-in. Consider threading an explicit: keyword through schedule_browser_open that bypasses the TTY check.

Bug: OPEN_BROWSER_ONCE_MARKER is a relative path

This constant resolves against Dir.pwd at constant-evaluation time (when the file is first required). If Dir.pwd is not the Rails app root at that moment the marker lands in the wrong directory and the once-open guard silently misfires. Prefer resolving it lazily at call time, e.g. relative to Rails.root.

Smoke test gap: JS-only template has no git-history assertion

expect_git_history is called for the TypeScript and RSC-JS variants but not for the plain-JS, Rspack-only, RSC-TS, or RSC-Rspack variants. A commit-message string change on those paths would go undetected. Consider adding expect_git_history for at least the JS-only variant to cover the non-TypeScript branch.

Nit: unreachable npm branch in packageManagerCommitMessage

The function constructs a body for the non-pnpm case, but the call site is guarded by a pnpm check - the npm branch is dead code. Either remove it from the function or add a comment at the call site explaining why npm does not get a normalization commit.

Nit: Windows start empty-string idiom deserves a comment

browser_command returns cmd /c start with an empty string on Windows. The empty string is the required window-title placeholder that keeps start from misinterpreting the URL as the title. This is correct but non-obvious - a brief inline comment would save the next reader from a grep detour.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 25, 2026

AGENTS.md

Instructions for AI coding agents working on the React on Rails codebase.

React on Rails is a Ruby gem + npm package that integrates React with Ruby on Rails, providing server-side rendering (SSR) via Node.js or ExecJS. This is a monorepo: the open-source gem lives at react_on_rails/, the npm package at packages/react-on-rails/, and the Pro package at react_on_rails_pro/.

Reusable Workflows

  • AGENTS.md: canonical entry point for agent instructions and workflow discovery
  • .claude/commands/: Claude Code slash commands
  • .agents/workflows/: shared prompt templates and reusable workflows for Codex, GPT, and other non-Claude tools
  • When the user asks to address PR review comments outside Claude slash commands, follow .agents/workflows/address-review.md

Canonical Agent Policy

AGENTS.md is the canonical source for repository-wide agent rules:

  • Commands and test/lint workflow
  • Code style and formatting expectations
  • Git/PR boundaries and safety rules
  • Directory and documentation boundaries

Other agent-facing docs (for example CLAUDE.md) should contain only tool-specific workflow notes and link back here.
If there is a conflict, AGENTS.md wins.

Commands

# Install dependencies
bundle && pnpm install

# Build TypeScript → JavaScript
pnpm run build

# Lint (MANDATORY before every commit)
bundle exec rubocop                  # Ruby — must pass with zero offenses
pnpm run lint                        # JS/TS via ESLint
pnpm start format.listDifferent      # Check Prettier formatting
rake lint                            # All linting (Ruby + JS + formatting)

# Auto-fix formatting
rake autofix                         # Preferred for all formatting

# Run tests
rake run_rspec:gem                   # Ruby unit tests (gem code)
rake run_rspec:dummy                 # Ruby integration tests (dummy Rails app)
pnpm run test                        # JavaScript/TypeScript tests
rake                                 # Full suite (lint + all tests except examples)

# Type checking
pnpm run type-check                  # TypeScript
bundle exec rake rbs:validate        # RBS signatures

# Additional test subsets
rake run_rspec                       # All Ruby tests
rake all_but_examples                # All tests except generated examples
rake run_rspec:shakapacker_examples_basic  # Single example test

# Full initial setup
bundle && pnpm install && rake shakapacker_examples:gen_all && rake node_package && rake

# CI/workflow linting
actionlint                           # GitHub Actions lint
yamllint .github/                    # YAML lint (do NOT run RuboCop on .yml files)

# Dependency version updates
rake shakapacker:update_version[9.6.1]  # Update shakapacker across the monorepo

Updating Shakapacker

Use rake shakapacker:update_version[VERSION] to update shakapacker across the entire monorepo. This single command updates all Gemfiles, package.json files, Gemfile.lock files, and pnpm-lock.yaml. Do not manually edit individual version references — always use the rake task to keep everything in sync.

The task handles Ruby version switching for apps that require a different Ruby version (set RUBY_VERSION_MANAGER to rvm, rbenv, asdf, or mise if needed; defaults to rvm). It continues gracefully if a single lock file update fails (e.g., due to a missing Ruby version).

Testing

  • Prefer local testing over CI iteration — don't push "hopeful" fixes. Apply the 15-minute rule: if 15 more minutes of local testing would catch the issue before CI does, spend the 15 minutes.
  • Never claim a test is "fixed" without running it locally first. Use "This SHOULD fix..." or "Proposed fix (UNTESTED)" for unverified changes.
  • Ruby: RSpec. Unit tests in react_on_rails/spec/react_on_rails/, integration tests via a dummy Rails app in react_on_rails/spec/dummy/.
  • JavaScript/TypeScript: Jest. Tests in packages/react-on-rails/tests/.
  • E2E: Playwright. Tests in react_on_rails/spec/dummy/e2e/playwright/e2e/. Run with cd react_on_rails/spec/dummy && pnpm test:e2e.
  • The dummy app (react_on_rails/spec/dummy/) is a full Rails application used for integration testing. Many tests require it.

Run specific test files:

bundle exec rspec react_on_rails/spec/react_on_rails/path/to/spec.rb
cd react_on_rails/spec/dummy && bundle exec rspec spec/path/to/spec.rb

Project Structure

Directory Purpose
react_on_rails/lib/react_on_rails/ Ruby gem source — helpers, configuration, SSR pool, engine
react_on_rails/lib/generators/ Rails generators for react_on_rails:install
react_on_rails/spec/ RSpec tests (unit + integration via dummy app)
react_on_rails/spec/dummy/ Full Rails app for integration testing and E2E
packages/react-on-rails/src/ TypeScript source — client-side React integration
packages/react-on-rails/tests/ Jest tests for the npm package
react_on_rails_pro/ Pro package (separate gem + npm, own lint config)
rakelib/ Rake task definitions
docs/oss/ OSS documentation — published to the ShakaCode website
docs/pro/ Pro documentation — installation, configuration, RSC, node renderer, caching
internal/contributor-info/ Internal contributor docs (not published to the website)
internal/planning/ Internal planning docs, drafts, and historical analysis
internal/react_on_rails_pro/contributors-info/ Internal Pro contributor docs (not published to the website)
analysis/ Investigation and analysis documents (kebab-case .md files)

Code Style

Ruby (RuboCop)

Line length max 120 characters. Run bundle exec rubocop [file] to check.

Line length — break long chains:

# Bad
content = pack_content.gsub(/import.*from.*['"];/, "").gsub(/ReactOnRails\.register.*/, "")

# Good
content = pack_content.gsub(/import.*from.*['"];/, "")
                      .gsub(/ReactOnRails\.register.*/, "")

Named subjects in RSpec:

# Bad
subject { instance.method_name(arg) }

# Good
subject(:method_result) { instance.method_name(arg) }

Security violations — scope disable comments tightly:

# rubocop:disable Security/Eval
expect { evaluate(sanitized_content) }.not_to raise_error
# rubocop:enable Security/Eval

JavaScript/TypeScript

Prettier handles all formatting. Never manually format — run rake autofix instead.

Git Workflow

Branch naming: type/descriptive-name (e.g., fix/ssr-hydration-mismatch)

Commit messages: Explain why, not what. One logical change per commit.

PR creation: Use gh pr create with a clear title, summary, and test plan.

Review Workflow

For small, focused PRs (roughly 5 files changed or fewer and one clear purpose):

  • Use at most one AI reviewer that leaves inline comments. Additional AI tools should be summary-only or used manually.
  • Wait for the first full review pass to finish before pushing follow-up commits.
  • Batch review fixes into one follow-up push when practical. Do not create a new commit for each minor comment.
  • Treat as blocking only: correctness bugs, failing tests, regressions, and clear inconsistencies with adjacent code. Nits and style suggestions are optional unless a maintainer asks for them.
  • Verify language, runtime, and library claims locally before changing code in response to AI review comments.
  • Deduplicate repeated bot comments before acting on them. Fix the underlying issue once, then resolve the duplicates.
  • Rebase or merge main once, near the end of the review cycle. For CHANGELOG.md conflicts, prefer resolving them as the final step before merge.
  • When asking an agent to address review comments, instruct it to classify comments into blocking, optional, and noise, then apply only the blocking items plus any explicitly selected optional items.

Boundaries

Always

  • Run bundle exec rubocop before committing — CI will reject violations
  • Use pnpm for all JS operations — never npm or yarn
  • Use bundle exec for Ruby commands
  • Ensure all files end with a newline
  • Let Prettier and RuboCop handle formatting — never format manually

Ask First

  • Destructive git operations (force push, reset --hard, branch deletion)
  • Changes to CI workflows (.github/workflows/)
  • Changes to build configuration (package.json scripts, webpack config)
  • Modifying the Pro package (react_on_rails_pro/)

Never

  • Skip pre-commit hooks (--no-verify)
  • Commit secrets, credentials, or .env files
  • Commit package-lock.json, yarn.lock, or other non-pnpm lock files
  • Add files to the docs/ root — OSS docs go in docs/oss/ subdirectories (getting-started/, core-concepts/, building-features/, configuration/, api-reference/, deployment/, migrating/, upgrading/, misc/); Pro docs go in docs/pro/
  • Force push to main or master

Key Concept: File Suffixes vs. RSC Directive

React on Rails has two independent systems that both use "client" and "server" terminology. Do not confuse them.

1. Bundle Placement (.client. / .server. file suffixes)

A React on Rails auto-bundling feature that controls which webpack bundle imports a file. This exists independently of React Server Components and is used with or without RSC:

  • Component.client.jsx → imported only in the client bundle (browser)
  • Component.server.jsx → imported only in the server bundle (and RSC bundle when RSC enabled)
  • Component.jsx (no suffix) → imported in both bundles

This controls where the source file is loaded, nothing more. A .server.jsx file is NOT a React Server Component — it is simply a file that webpack includes in the server bundle (and the RSC bundle when RSC is enabled). These suffixes only make sense for client components, as server components exist only in the RSC bundle.

2. RSC Classification ('use client' directive)

The 'use client' directive is part of the React Server Components architecture. It marks a component as a React Client Component. Components without it are treated as React Server Components.

When auto-bundling is enabled with RSC support (Pro feature), React on Rails uses this directive to control:

  • Registration: 'use client'ReactOnRails.register(), no 'use client'registerServerComponent()
  • RSC bundling: The RSC webpack loader uses this directive to decide whether a component is included in the RSC bundle or replaced with a client reference in that bundle

The client_entrypoint? method in packs_generator.rb checks for this directive.

They Are Orthogonal

A .client.jsx file can be a React Server Component (if it lacks 'use client'), and a .server.jsx file can be a React Client Component (if it has 'use client'). In practice, paired .client./.server. files should have consistent 'use client' status because the client and server must agree on the component's RSC role for hydration to work.

Changelog

Update /CHANGELOG.md for user-visible changes only (features, bug fixes, breaking changes, deprecations, performance improvements). Do not add entries for linting, formatting, refactoring, tests, or doc fixes.

  • Format: [PR 1818](https://github.com/shakacode/react_on_rails/pull/1818) by [username](https://github.com/username) (no hash before PR number)
  • Pro-only changes use an inline **[Pro]** tag prefix within the standard category sections (e.g., - **[Pro]** **Feature name**: Description...); do NOT create separate #### Pro subsections

Comment thread react_on_rails/lib/react_on_rails/dev/server_manager.rb
Comment thread react_on_rails/lib/react_on_rails/dev/server_manager.rb
Comment thread packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh
Comment thread react_on_rails/lib/react_on_rails/dev/server_manager.rb
Comment thread packages/create-react-on-rails-app/src/create-app.ts Outdated
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 25, 2026

PR Review: Improve create-react-on-rails-app fresh app onboarding

Overall this is a well-structured, thoughtful onboarding improvement. The layered design — git-validation → educational commits → landing page → first-run browser open — is clean, and the guard logic (CI detection, TTY check, --no-open-browser override, marker file) is solid. A few issues worth addressing before merge:


Blocker: GPG commit signing hangs scaffolding

Developers with commit.gpgsign = true in their global git config will see the scaffolding block waiting for a GPG passphrase (or fail outright in non-TTY CI environments). Since educational commits are machine-generated, signing adds no value. The git commit call in createEducationalCommit should pass -c commit.gpgsign=false to disable signing for these automated commits. See inline comment on create-app.ts:221.


Medium: WSL users get no browser open and no feedback

browser_command correctly handles macOS, Linux (via xdg-open), and native Windows, but silently returns nil on WSL where xdg-open is absent. WSL is a common Rails dev environment on Windows. At minimum, a "Auto-open not supported on this platform — visit http://localhost:3000" message when browser_command returns nil would prevent confusion. See inline comment on server_manager.rb:923.


Medium: Thread.new + report_on_exception = false + next

The combination of suppressed exceptions and the non-idiomatic next in a non-iterating block will make debugging any future issue in schedule_browser_open very difficult. next works in MRI today, but this is fragile. Restructuring with a rescue clause that at least logs to $stderr in verbose mode would be safer and more conventional. See inline comment on server_manager.rb:875.


Medium: Brittle positional mock chain in tests

The mockImplementationOnce stacks in create-app.test.ts now require adding 2-6 new blank stubs every time a git operation is added to the flow. This PR already had to update nthCalledWith(2) to nthCalledWith(4) and nthCalledWith(3) to nthCalledWith(7) because of this. A mockImplementation keyed on (command, args) would make these tests resilient to future additions. See inline comment on create-app.test.ts:477.


Minor: Memoize educationalGitEnv

readGitConfig spawns a subprocess each time, and educationalGitEnv calls it twice per commit across 4-5 commits per scaffold run (8-10 subprocesses). The name and email don't change mid-run — caching after the first read is trivial. See inline comment on create-app.ts:196.


Minor: Soft race condition in --open-browser-once

The marker-file existence check happens before the thread is spawned; the write happens inside the thread. Two near-simultaneous bin/dev restarts could both pass the check and both open a browser tab. Very unlikely in practice — just documenting it for if it ever gets reported as a bug. See inline comment on server_manager.rb:872.


Observations (no action needed)

  • OPEN_BROWSER_ONCE_MARKER is a relative path (tmp/react_on_rails/browser_opened_once). This is fine — Rails convention is to always run bin/dev from the app root, and tmp/ is gitignored. Worth a note in the constant comment for future maintainers.
  • ensureGitRepository is called on every createEducationalCommit invocation but is idempotent and cheap since rails new already initialises git. No change needed.
  • The open_browser method correctly short-circuits on non-zero exit, so the marker is only written when the browser command actually succeeded. Good.

Copy link
Copy Markdown
Contributor

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

🧹 Nitpick comments (4)
react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb (1)

73-96: Add the preexisting-root-route regression case.

These contexts only cover the happy path. Please add one case where config/routes.rb already has a non-commented root and assert we do not inject root to: "home#index" or scaffold HomeController/app/views/home, since that guard is what keeps --new-app from clobbering existing apps.

Also applies to: 1511-1532

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

In `@react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb`
around lines 73 - 96, Add a regression spec that simulates a preexisting
non-commented root route and asserts the generator does not inject or scaffold
over it: reuse the existing context "with --new-app" and
run_generator_test_with_args(%w[--new-app], package_json: true) but with
config/routes.rb prepopulated with an existing root (e.g. 'root to:
"existing#home"'); then assert_file "config/routes.rb" does NOT include 'root
to: "home#index"' and assert_no_file for "app/controllers/home_controller.rb"
and "app/views/home/index.html.erb" (or assert those files are unchanged) to
confirm HomeController and view are not created when a root already exists.
packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh (1)

126-176: Smoke-test the other commit-history variants too.

expect_git_history is only used for APP_TS_DIR and APP_RSC_JS_DIR. The CLI builds different commit subjects for JavaScript/Rspack/RSC+Rspack flows in packages/create-react-on-rails-app/src/create-app.ts, so regressions in those variants can slip through this smoke test today.

Also applies to: 178-204

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

In `@packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh` around
lines 126 - 176, The test only asserts commit history for APP_TS_DIR and
APP_RSC_JS_DIR, so add expect_git_history checks for the other generated-app
variants (APP_JS_DIR and APP_RSPACK_DIR) to catch commit-message regressions;
update the smoke-test script to call expect_git_history for APP_JS_DIR and
APP_RSPACK_DIR with the expected sequence of commit subjects used by
create-app.ts (matching the patterns used for APP_TS_DIR and APP_RSC_JS_DIR),
and mirror the same addition in the later block referenced around lines 178-204
so all four app variants (APP_JS_DIR, APP_TS_DIR, APP_RSPACK_DIR,
APP_RSC_JS_DIR) have expect_git_history assertions.
packages/create-react-on-rails-app/src/create-app.ts (1)

179-222: Consider exception handling in gitHasPendingChanges.

Unlike readGitConfig (which wraps execCaptureArgs in try/catch), gitHasPendingChanges propagates exceptions. If git status --porcelain fails (e.g., corrupted git repo), the exception bubbles up through createEducationalCommit to the caller's try/catch, failing the entire step.

This is likely intentional (fail fast on git issues), but if you want educational commits to be best-effort without blocking app creation, consider wrapping with try/catch:

Optional: Make git status check resilient
 function gitHasPendingChanges(appPath: string): boolean {
+  try {
     return execCaptureArgs('git', ['status', '--porcelain'], appPath) !== '';
+  } catch {
+    return false;
+  }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/create-react-on-rails-app/src/create-app.ts` around lines 179 - 222,
gitHasPendingChanges currently calls execCaptureArgs('git', ['status',
'--porcelain'], appPath) and lets any exception bubble up; make it resilient by
wrapping the execCaptureArgs call in a try/catch and returning false on error so
educational commits remain best-effort (similar to readGitConfig's pattern).
Update the gitHasPendingChanges function to catch exceptions from
execCaptureArgs and return false when git status fails, leaving
createEducationalCommit and ensureGitRepository unchanged.
react_on_rails/lib/react_on_rails/dev/server_manager.rb (1)

939-944: Consider the marker file cleanup strategy.

The marker file at tmp/react_on_rails/browser_opened_once persists across server restarts. This is intentional (to avoid re-opening after the first boot), but developers may want to re-trigger the auto-open without manually deleting the file.

Consider documenting the cleanup in the help text or providing a flag like --reset-browser-marker.

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

In `@react_on_rails/lib/react_on_rails/dev/server_manager.rb` around lines 939 -
944, The marker file OPEN_BROWSER_ONCE_MARKER created by
mark_browser_opened_once persists and has no opt-in reset; add a new CLI flag
(e.g., --reset-browser-marker) parsed where server start options are handled
(the server manager/CLI option parsing code that constructs ServerManager),
document the new flag in the help text, and when the flag is present call
FileUtils.rm_f(OPEN_BROWSER_ONCE_MARKER) (or File.delete with existence check)
before starting the server so the auto-open behavior can re-run; ensure any file
operations are wrapped in a rescue StandardError to avoid crashing and reference
OPEN_BROWSER_ONCE_MARKER and mark_browser_opened_once in the changes.
🤖 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/create-react-on-rails-app/scripts/smoke-test-local-gems.sh`:
- Around line 99-105: Replace the terminal "exit 1" with "return 1" so the
git-history mismatch uses the function return path (allowing the ERR trap and
cleanup_on_error to run and respect KEEP_WORKDIR_ON_FAILURE); specifically
change the branch that checks if "$actual" != "$expected" (which prints
Expected/Actual for $app_dir) to return 1 instead of exiting the shell.

In `@react_on_rails/lib/generators/react_on_rails/base_generator.rb`:
- Around line 622-624: The message returned by home_page_pro_note_for_oss_app is
misleading because it suggests React on Rails Pro can be evaluated "without a
license" while the app is expected to raise on boot if Pro is installed but
unlicensed; update the home_page_pro_note_for_oss_app method to remove the
"without a license" language and instead clearly instruct users to obtain a Pro
license or uninstall Pro (or otherwise state that Pro requires a valid license
and will raise on boot if missing), so the displayed note aligns with the
intended licensed/raise-on-boot behavior.

---

Nitpick comments:
In `@packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh`:
- Around line 126-176: The test only asserts commit history for APP_TS_DIR and
APP_RSC_JS_DIR, so add expect_git_history checks for the other generated-app
variants (APP_JS_DIR and APP_RSPACK_DIR) to catch commit-message regressions;
update the smoke-test script to call expect_git_history for APP_JS_DIR and
APP_RSPACK_DIR with the expected sequence of commit subjects used by
create-app.ts (matching the patterns used for APP_TS_DIR and APP_RSC_JS_DIR),
and mirror the same addition in the later block referenced around lines 178-204
so all four app variants (APP_JS_DIR, APP_TS_DIR, APP_RSPACK_DIR,
APP_RSC_JS_DIR) have expect_git_history assertions.

In `@packages/create-react-on-rails-app/src/create-app.ts`:
- Around line 179-222: gitHasPendingChanges currently calls
execCaptureArgs('git', ['status', '--porcelain'], appPath) and lets any
exception bubble up; make it resilient by wrapping the execCaptureArgs call in a
try/catch and returning false on error so educational commits remain best-effort
(similar to readGitConfig's pattern). Update the gitHasPendingChanges function
to catch exceptions from execCaptureArgs and return false when git status fails,
leaving createEducationalCommit and ensureGitRepository unchanged.

In `@react_on_rails/lib/react_on_rails/dev/server_manager.rb`:
- Around line 939-944: The marker file OPEN_BROWSER_ONCE_MARKER created by
mark_browser_opened_once persists and has no opt-in reset; add a new CLI flag
(e.g., --reset-browser-marker) parsed where server start options are handled
(the server manager/CLI option parsing code that constructs ServerManager),
document the new flag in the help text, and when the flag is present call
FileUtils.rm_f(OPEN_BROWSER_ONCE_MARKER) (or File.delete with existence check)
before starting the server so the auto-open behavior can re-run; ensure any file
operations are wrapped in a rescue StandardError to avoid crashing and reference
OPEN_BROWSER_ONCE_MARKER and mark_browser_opened_once in the changes.

In `@react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb`:
- Around line 73-96: Add a regression spec that simulates a preexisting
non-commented root route and asserts the generator does not inject or scaffold
over it: reuse the existing context "with --new-app" and
run_generator_test_with_args(%w[--new-app], package_json: true) but with
config/routes.rb prepopulated with an existing root (e.g. 'root to:
"existing#home"'); then assert_file "config/routes.rb" does NOT include 'root
to: "home#index"' and assert_no_file for "app/controllers/home_controller.rb"
and "app/views/home/index.html.erb" (or assert those files are unchanged) to
confirm HomeController and view are not created when a root already exists.
🪄 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

Run ID: 48e024bc-9080-4c35-8945-d51c1bd68071

📥 Commits

Reviewing files that changed from the base of the PR and between 9606a5f and f5911c2.

📒 Files selected for processing (19)
  • CHANGELOG.md
  • docs/oss/getting-started/create-react-on-rails-app.md
  • packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh
  • packages/create-react-on-rails-app/src/create-app.ts
  • packages/create-react-on-rails-app/src/index.ts
  • packages/create-react-on-rails-app/src/utils.ts
  • packages/create-react-on-rails-app/src/validators.ts
  • packages/create-react-on-rails-app/tests/create-app.test.ts
  • packages/create-react-on-rails-app/tests/validators.test.ts
  • react_on_rails/lib/generators/react_on_rails/base_generator.rb
  • react_on_rails/lib/generators/react_on_rails/generator_messages.rb
  • react_on_rails/lib/generators/react_on_rails/install_generator.rb
  • react_on_rails/lib/generators/react_on_rails/templates/base/base/app/controllers/home_controller.rb
  • react_on_rails/lib/generators/react_on_rails/templates/base/base/app/views/home/index.html.erb.tt
  • react_on_rails/lib/generators/react_on_rails/templates/base/base/bin/dev
  • react_on_rails/lib/react_on_rails/dev/server_manager.rb
  • react_on_rails/spec/react_on_rails/dev/server_manager_spec.rb
  • react_on_rails/spec/react_on_rails/generators/generator_messages_spec.rb
  • react_on_rails/spec/react_on_rails/generators/install_generator_spec.rb

Comment thread packages/create-react-on-rails-app/scripts/smoke-test-local-gems.sh Outdated
Comment thread react_on_rails/lib/generators/react_on_rails/base_generator.rb
@justin808 justin808 force-pushed the fix/create-app-onboarding branch from f5911c2 to 3fad485 Compare March 25, 2026 23:16
Comment thread packages/create-react-on-rails-app/src/create-app.ts
Comment thread packages/create-react-on-rails-app/src/create-app.ts
Comment thread react_on_rails/lib/react_on_rails/dev/server_manager.rb
Comment thread react_on_rails/lib/react_on_rails/dev/server_manager.rb
radial-gradient(circle at bottom right, rgba(13, 59, 102, 0.16), transparent 28%),
var(--page-bg);
color: var(--ink);
font-family: "Avenir Next", Avenir, "Segoe UI", sans-serif;
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.

The body { margin: 0; ... } rule applies globally and will conflict with any styles the developer adds once they start building beyond the scaffold. Since the entire page content is already wrapped in <div class="ror-demo-page">, consider moving these reset styles onto .ror-demo-page instead:

Suggested change
font-family: "Avenir Next", Avenir, "Segoe UI", sans-serif;
.ror-demo-page {
margin: 0;
background:
radial-gradient(circle at top left, rgba(239, 131, 84, 0.22), transparent 34%),
radial-gradient(circle at bottom right, rgba(13, 59, 102, 0.16), transparent 28%),
var(--page-bg);
color: var(--ink);
font-family: "Avenir Next", Avenir, "Segoe UI", sans-serif;
}

Same concern applies to the a and code global rules below — scope them under .ror-demo-page a and .ror-demo-page code.

class ServerManager
HELP_FLAGS = ["-h", "--help"].freeze
TEST_WATCH_MODES = %w[auto full client-only].freeze
OPEN_BROWSER_WAIT_TIMEOUT = 60
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.

When the server fails to start (DB down, port conflict), this background thread will poll localhost for the full 60 s before giving up — ~120 TCP connect attempts. Since the thread is fire-and-forget this doesn't block the process, but it emits a confusing warn message long after the user has seen the visible error.

Consider a shorter timeout (e.g. 30 s) and/or a comment explaining why 60 s was chosen, so future maintainers know whether it's safe to reduce.

end

def browser_auto_open_allowed?
!ENV.key?("CI") && $stdin.tty? && $stdout.tty?
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.

Under Overmind (and some tmux-mux setups), $stdin and $stdout are not attached to a pseudo-TTY even when the developer is running interactively, so this guard will silently skip browser opening. The CI env-var check is the right safety net; the TTY check may be too aggressive in practice.

If the intent is just to avoid running in CI/automation, !ENV.key?("CI") alone is likely sufficient. If keeping the TTY guard, a comment explaining the Overmind/tmux caveat would help users who report "browser never opens".


private

def new_app_landing_page_available?
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.

This duplicates the same File.foreach + /^\s*root\b/ pattern already in base_generator.rb#preexisting_root_route? and install_generator.rb#new_app_root_route_available?. Three copies means any future fix (e.g. ignoring commented-out root routes inside heredocs, or handling the root "home#index" shorthand) needs to be applied in three places.

Consider extracting a shared helper into GeneratorHelper:

def root_route_declared?
  routes_file = File.join(destination_root, "config/routes.rb")
  return false unless File.file?(routes_file)

  File.foreach(routes_file).any? do |line|
    !line.match?(/^\s*#/) && line.match?(/^\s*root\b/)
  end
end

Then all three callers become a one-liner.

const DOCS_URL = 'https://reactonrails.com/docs/';
const DEFAULT_GIT_AUTHOR_NAME = 'React on Rails Generator';
const DEFAULT_GIT_AUTHOR_EMAIL = '[email protected]';
const RAILS_GIT_TEMPLATE_RENDERER = `
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.

The ReactOnRailsGitTemplateContext mock reimplements a slice of Rails' internal template API. If a future railties version adds a new method that the .gitignore.tt or .gitattributes.tt templates call, the ERB render will raise a NoMethodError — which is caught and falls back to the hardcoded strings, so it fails silently.

A single comment here listing the Rails methods being mocked and the Rails version they were validated against would make it easier for maintainers to know when this needs updating:

Suggested change
const RAILS_GIT_TEMPLATE_RENDERER = `
// Inline Ruby script that renders railties' .gitignore.tt / .gitattributes.tt templates.
// Mocks the subset of Rails' template binding used by these two files as of Rails 7.2.
// If a future Rails version adds new template methods, this will fall back to the
// RAILS_GITIGNORE_FALLBACK / RAILS_GITATTRIBUTES_FALLBACK constants below.
const RAILS_GIT_TEMPLATE_RENDERER = `

@justin808
Copy link
Copy Markdown
Member Author

Following up on #2849 (comment):

I moved the three maintainability-focused discuss items into follow-up issue #2890 so they can be handled outside this merge path:
#2890

Skipping the remaining items for this PR:

  • The detached browser-open thread is intentional best-effort behavior; it does not change correctness for the onboarding flow.
  • The xdg-open wording suggestion is a nice improvement, but it is warning-text polish rather than a blocking issue.
  • The mutual exclusion between --open-browser and --open-browser-once is already intentional and covered by tests; extra commentary can happen later if we want it.
  • The explicit protect_from_forgery with: :exception in the generated HomeController is redundant, but harmless and not a merge blocker for this onboarding work.

No code changes needed for this comment in PR #2849.

@justin808 justin808 merged commit b19b5cd into main Mar 29, 2026
70 checks passed
@justin808 justin808 deleted the fix/create-app-onboarding branch March 29, 2026 09:46
justin808 added a commit that referenced this pull request Mar 29, 2026
…e-repo

* origin/main:
  Improve create-react-on-rails-app fresh app onboarding (#2849)
  Add Docker deployment guide for Kamal, Kubernetes, and Control Plane (#2837)
  Reframe RSC migration docs around React on Rails patterns (#2661)
  Enforce secure renderer password defaults for production-like envs (#2829)
ihabadham added a commit that referenced this pull request Apr 2, 2026
…create-app tests

The create-app tests used toHaveBeenNthCalledWith and counted
mockImplementationOnce chains that coupled to the exact interleaving of
educational git commits. Adding any intermediate execLiveArgs call broke
13 of 78 tests even when behavior was identical.

Replace positional ordinals with toHaveBeenCalledWith (argument matching
without position), replace mockImplementationOnce chains with
command-matching mockImplementation, and replace toHaveBeenCalledTimes
with a filtered stepCalls() helper that counts non-git commands only.

Addresses item 1 of #2888 (deferred from #2849 review).

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
ihabadham added a commit that referenced this pull request Apr 3, 2026
## Summary
- Replace `toHaveBeenNthCalledWith` ordinals with `toHaveBeenCalledWith`
(semantic argument matching)
- Replace `mockImplementationOnce` chains with command-matching
`mockImplementation`
- Replace `toHaveBeenCalledTimes` with `stepCallSummaries()` — a
filtered sequence assertion on non-git commands that verifies ordering,
count, and argument correctness in one check
- Add `stepCallSummaries()` helper (mirrors the existing
`gitCommitSubjects()` pattern for commit ordering)

## Context

PR #2849 added educational git commits to the
`create-react-on-rails-app` scaffold flow, interleaving `git add` + `git
commit` calls between each real step. The existing tests were updated to
use new ordinal positions (1→4→7→10) and counts (9, 12), but a reviewer
flagged this as brittle and it was deferred as item 1 of #2888.

The brittleness was verified: adding a single `execLiveArgs` call to
`create-app.ts` broke 13 of 78 tests — 7 from shifted ordinals, 5 from
`mockImplementationOnce` chain off-by-one, and 1 from a count mismatch.

## Approach

The test file already had `gitCommitSubjects().toEqual([...])` which
verifies commit ordering through `mock.calls` filtering — the [standard
Jest community pattern](jestjs/jest#4402) for
asserting call order on a subset of calls. This PR extends that pattern
to step commands via `stepCallSummaries()`, which filters out
educational git operations and asserts on the exact sequence of real
steps:

```typescript
expect(stepCallSummaries()).toEqual([
  'rails new',
  'bundle add react_on_rails',
  'bundle add react_on_rails_pro',
  'bundle generate',
]);
```

This provides ordering verification (via `toEqual` on an ordered array),
count verification (exact array length), and resilience to
educational-commit interleaving — without the positional coupling of the
original ordinal assertions.

Partially addresses #2888.

## Test plan
- [x] All 78 existing tests pass
- [x] Adding an extra `git` command to `create-app.ts` → 0 test failures
(correctly tolerant of git interleaving)
- [x] Adding an extra non-git command to `create-app.ts` →
`stepCallSummaries()` catches the sequence mismatch
- [x] Inserting a command at the wrong position → `stepCallSummaries()`
catches the ordering violation
- [x] Pre-commit hooks pass (prettier, eslint, trailing-newlines)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Improved test reliability and maintainability for the app-creation
flow by adding helpers to summarize and verify ordered setup steps,
shifting assertions from strict call-count/order to presence and
expected step sequences, and consolidating failure-path mocks for
clearer, more robust error simulations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
justin808 added a commit that referenced this pull request Apr 9, 2026
Server manager browser-open improvements:
- Refactor Thread.new block: remove non-idiomatic `next`, remove
  `report_on_exception = false` that suppressed genuine crashes
- Add WSL browser support: detect WSL_DISTRO_NAME/WSLENV and try
  wslview/wsl-open before falling back to xdg-open
- Warn user when no browser launcher is found (with WSL-specific hint)
- Let explicit --open-browser bypass TTY/CI guard since the developer
  deliberately requested it; auto-opens still respect the guard
- Resolve marker-file path lazily at call time instead of at
  constant-definition time to avoid wrong path when required before
  chdir into Rails root
- Narrow rescued exceptions in http_get_localhost from broad
  StandardError to specific socket/connection error classes

Generator fix:
- Update misleading Pro note that said Pro works "without a license"
  (Pro raises on boot without one)

Closes #2880

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
justin808 added a commit that referenced this pull request Apr 12, 2026
Server manager browser-open improvements:
- Refactor Thread.new block: remove non-idiomatic `next`, remove
  `report_on_exception = false` that suppressed genuine crashes
- Add WSL browser support: detect WSL_DISTRO_NAME/WSLENV and try
  wslview/wsl-open before falling back to xdg-open
- Warn user when no browser launcher is found (with WSL-specific hint)
- Let explicit --open-browser bypass TTY/CI guard since the developer
  deliberately requested it; auto-opens still respect the guard
- Resolve marker-file path lazily at call time instead of at
  constant-definition time to avoid wrong path when required before
  chdir into Rails root
- Narrow rescued exceptions in http_get_localhost from broad
  StandardError to specific socket/connection error classes

Generator fix:
- Update misleading Pro note that said Pro works "without a license"
  (Pro raises on boot without one)

Closes #2880

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
justin808 pushed a commit that referenced this pull request Apr 12, 2026
## Summary
- Replace `toHaveBeenNthCalledWith` ordinals with `toHaveBeenCalledWith`
(semantic argument matching)
- Replace `mockImplementationOnce` chains with command-matching
`mockImplementation`
- Replace `toHaveBeenCalledTimes` with `stepCallSummaries()` — a
filtered sequence assertion on non-git commands that verifies ordering,
count, and argument correctness in one check
- Add `stepCallSummaries()` helper (mirrors the existing
`gitCommitSubjects()` pattern for commit ordering)

## Context

PR #2849 added educational git commits to the
`create-react-on-rails-app` scaffold flow, interleaving `git add` + `git
commit` calls between each real step. The existing tests were updated to
use new ordinal positions (1→4→7→10) and counts (9, 12), but a reviewer
flagged this as brittle and it was deferred as item 1 of #2888.

The brittleness was verified: adding a single `execLiveArgs` call to
`create-app.ts` broke 13 of 78 tests — 7 from shifted ordinals, 5 from
`mockImplementationOnce` chain off-by-one, and 1 from a count mismatch.

## Approach

The test file already had `gitCommitSubjects().toEqual([...])` which
verifies commit ordering through `mock.calls` filtering — the [standard
Jest community pattern](jestjs/jest#4402) for
asserting call order on a subset of calls. This PR extends that pattern
to step commands via `stepCallSummaries()`, which filters out
educational git operations and asserts on the exact sequence of real
steps:

```typescript
expect(stepCallSummaries()).toEqual([
  'rails new',
  'bundle add react_on_rails',
  'bundle add react_on_rails_pro',
  'bundle generate',
]);
```

This provides ordering verification (via `toEqual` on an ordered array),
count verification (exact array length), and resilience to
educational-commit interleaving — without the positional coupling of the
original ordinal assertions.

Partially addresses #2888.

## Test plan
- [x] All 78 existing tests pass
- [x] Adding an extra `git` command to `create-app.ts` → 0 test failures
(correctly tolerant of git interleaving)
- [x] Adding an extra non-git command to `create-app.ts` →
`stepCallSummaries()` catches the sequence mismatch
- [x] Inserting a command at the wrong position → `stepCallSummaries()`
catches the ordering violation
- [x] Pre-commit hooks pass (prettier, eslint, trailing-newlines)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Improved test reliability and maintainability for the app-creation
flow by adding helpers to summarize and verify ordered setup steps,
shifting assertions from strict call-count/order to presence and
expected step sequences, and consolidating failure-path mocks for
clearer, more robust error simulations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
justin808 pushed a commit that referenced this pull request Apr 12, 2026
## Summary
- Replace `toHaveBeenNthCalledWith` ordinals with `toHaveBeenCalledWith`
(semantic argument matching)
- Replace `mockImplementationOnce` chains with command-matching
`mockImplementation`
- Replace `toHaveBeenCalledTimes` with `stepCallSummaries()` — a
filtered sequence assertion on non-git commands that verifies ordering,
count, and argument correctness in one check
- Add `stepCallSummaries()` helper (mirrors the existing
`gitCommitSubjects()` pattern for commit ordering)

## Context

PR #2849 added educational git commits to the
`create-react-on-rails-app` scaffold flow, interleaving `git add` + `git
commit` calls between each real step. The existing tests were updated to
use new ordinal positions (1→4→7→10) and counts (9, 12), but a reviewer
flagged this as brittle and it was deferred as item 1 of #2888.

The brittleness was verified: adding a single `execLiveArgs` call to
`create-app.ts` broke 13 of 78 tests — 7 from shifted ordinals, 5 from
`mockImplementationOnce` chain off-by-one, and 1 from a count mismatch.

## Approach

The test file already had `gitCommitSubjects().toEqual([...])` which
verifies commit ordering through `mock.calls` filtering — the [standard
Jest community pattern](jestjs/jest#4402) for
asserting call order on a subset of calls. This PR extends that pattern
to step commands via `stepCallSummaries()`, which filters out
educational git operations and asserts on the exact sequence of real
steps:

```typescript
expect(stepCallSummaries()).toEqual([
  'rails new',
  'bundle add react_on_rails',
  'bundle add react_on_rails_pro',
  'bundle generate',
]);
```

This provides ordering verification (via `toEqual` on an ordered array),
count verification (exact array length), and resilience to
educational-commit interleaving — without the positional coupling of the
original ordinal assertions.

Partially addresses #2888.

## Test plan
- [x] All 78 existing tests pass
- [x] Adding an extra `git` command to `create-app.ts` → 0 test failures
(correctly tolerant of git interleaving)
- [x] Adding an extra non-git command to `create-app.ts` →
`stepCallSummaries()` catches the sequence mismatch
- [x] Inserting a command at the wrong position → `stepCallSummaries()`
catches the ordering violation
- [x] Pre-commit hooks pass (prettier, eslint, trailing-newlines)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Tests**
* Improved test reliability and maintainability for the app-creation
flow by adding helpers to summarize and verify ordered setup steps,
shifting assertions from strict call-count/order to presence and
expected step sequences, and consolidating failure-path mocks for
clearer, more robust error simulations.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
justin808 added a commit that referenced this pull request Apr 13, 2026
Server manager browser-open improvements:
- Refactor Thread.new block: remove non-idiomatic `next`, remove
  `report_on_exception = false` that suppressed genuine crashes
- Add WSL browser support: detect WSL_DISTRO_NAME/WSLENV and try
  wslview/wsl-open before falling back to xdg-open
- Warn user when no browser launcher is found (with WSL-specific hint)
- Let explicit --open-browser bypass TTY/CI guard since the developer
  deliberately requested it; auto-opens still respect the guard
- Resolve marker-file path lazily at call time instead of at
  constant-definition time to avoid wrong path when required before
  chdir into Rails root
- Narrow rescued exceptions in http_get_localhost from broad
  StandardError to specific socket/connection error classes

Generator fix:
- Update misleading Pro note that said Pro works "without a license"
  (Pro raises on boot without one)

Closes #2880

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
justin808 added a commit that referenced this pull request Apr 13, 2026
## Summary
- Refactor server manager browser-open: remove non-idiomatic `next` in
Thread.new, remove `report_on_exception = false` that suppressed genuine
crashes, add WSL browser support (wslview/wsl-open), let explicit
`--open-browser` bypass TTY guard, resolve marker path lazily, narrow
rescued socket exceptions
- Fix misleading Pro note that said Pro works "without a license"

## Details

### Server manager (`server_manager.rb`)
- **Thread.new refactor**: Replaced `next` (non-idiomatic in Thread.new)
with standard conditionals; removed `report_on_exception = false` so
genuine crashes surface in development
- **WSL support**: Detects WSL via `WSL_DISTRO_NAME`/`WSLENV` env vars
and tries `wslview`/`wsl-open` before `xdg-open`
- **Platform diagnostic**: Warns user when no browser launcher is found
(with WSL-specific install hint)
- **Explicit vs auto-open TTY gating**: `--open-browser` (deliberate
user request) now bypasses TTY/CI guard; `--open-browser-once`
(auto-open) still respects it
- **Lazy marker path**: Replaced `OPEN_BROWSER_ONCE_MARKER` constant
(resolved at class-load time) with `open_browser_once_marker` method
(resolved at call time) to avoid wrong path when file is required before
chdir into Rails root
- **Narrowed exception handling**: `http_get_localhost` now rescues
specific socket/connection errors instead of broad `StandardError`

### Generator (`base_generator.rb`)
- Updated `home_page_pro_note_for_oss_app` to not claim Pro works
"without a license" (Pro raises on boot when unlicensed)

Closes #2880

## Test plan
- [x] `bundle exec rspec spec/react_on_rails/dev/server_manager_spec.rb`
— 93 examples, 0 failures
- [x] `bundle exec rspec
spec/react_on_rails/generators/install_generator_spec.rb:73` — passes
- [x] `bundle exec rubocop` on all changed files — no offenses
- [ ] Verify WSL browser detection in WSL environment
- [ ] Verify `--open-browser` works in non-TTY context (e.g., Docker)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it changes development process management and SSR
webpack configuration, which can affect local workflows and
server-bundle debugging/stack traces across platforms.
> 
> **Overview**
> **Stabilizes development ergonomics across environments.**
`ServerManager` browser auto-open was refactored to distinguish explicit
`--open-browser` from guarded auto-open (`--open-browser-once`), add WSL
launcher support (`wslview`/`wsl-open`), improve failure warnings,
lazily resolve the once-marker path, and narrow localhost polling
exception handling.
> 
> **Avoids a webpack 5.106+ SSR regression.** Server webpack configs
switch away from `devtool = 'eval'` to `cheap-module-source-map` in
development (and `false` in production) to prevent ESM default-export
issues and improve SSR stack trace mapping.
> 
> Also updates generator messaging to remove the claim that Pro can be
evaluated without a license, fixes generator `chmod` to use
`destination_root`, adds `.playwright-cli/` to `.gitignore`, and adds a
Jest assertion ensuring `scriptSanitizedVal` keeps a stable function
name for webpack compatibility.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
bd84af3. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Fixed server-side rendering (SSR) error stack traces to accurately map
line numbers by updating source map configuration.
* Improved browser auto-open functionality on WSL systems with better
launcher detection and fallback handling.

* **Documentation**
* Updated Pro licensing guidance to clarify users must review upgrade
documentation and obtain proper licenses before enabling Pro features.

* **Tests**
* Added test coverage for function naming stability and browser
auto-open behavior across different environments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
justin808 added a commit that referenced this pull request Apr 18, 2026
…ages

* origin/main:
  Fix initial page startup race for late-loading client bundles (#3151)
  chore: apply prettier formatting to tracked docs files (#3153)
  docs: comprehensive RSC API documentation and registration consolidation (#3140)
  Split rspec-package-tests into parallel generator/unit shards (#3134)
  fix: add concurrency groups to long-running CI workflows (#3133)
  refactor: add RenderRequest, JsCodeBuilder, and RenderingStrategy abstractions (#3094)
  fix: address deferred review items from PR #2849 (#3093)
  Add complimentary OSS license policy for React on Rails Pro (#3123)
  fix: centralize CI docs-only detection and add CLI flag validation (#3091)
  refactor: replace stub-throw + Object.assign with capability-based composition (#3096)
  Enhance address-review with parallel fixes, self-review, and Greptile verification (#3121)
  fix: Doctor no longer fails custom projects for missing bin/dev (#3117)
  fix: cap webpack <5.106.0 to prevent ExecJS SSR breakage (#3095)
  Add Rspack + RSC compatibility tests and documentation (#1828) (#3120)
  Add error scenarios hub and test pages (#2497)
  docs: document polyfill requirements for web-targeted server bundles (#3092)
  docs: RSC integration pitfalls from tutorial app (#3087)
  docs: fix render function/helper API documentation (#3088)
  Doctor: accept TS/TSX server bundle suffixes (#3111)
  feat: add CI guard requiring sidebar updates when adding docs (#3089)
justin808 added a commit that referenced this pull request Apr 18, 2026
## Summary
- Refactor server manager browser-open: remove non-idiomatic `next` in
Thread.new, remove `report_on_exception = false` that suppressed genuine
crashes, add WSL browser support (wslview/wsl-open), let explicit
`--open-browser` bypass TTY guard, resolve marker path lazily, narrow
rescued socket exceptions
- Fix misleading Pro note that said Pro works "without a license"

## Details

### Server manager (`server_manager.rb`)
- **Thread.new refactor**: Replaced `next` (non-idiomatic in Thread.new)
with standard conditionals; removed `report_on_exception = false` so
genuine crashes surface in development
- **WSL support**: Detects WSL via `WSL_DISTRO_NAME`/`WSLENV` env vars
and tries `wslview`/`wsl-open` before `xdg-open`
- **Platform diagnostic**: Warns user when no browser launcher is found
(with WSL-specific install hint)
- **Explicit vs auto-open TTY gating**: `--open-browser` (deliberate
user request) now bypasses TTY/CI guard; `--open-browser-once`
(auto-open) still respects it
- **Lazy marker path**: Replaced `OPEN_BROWSER_ONCE_MARKER` constant
(resolved at class-load time) with `open_browser_once_marker` method
(resolved at call time) to avoid wrong path when file is required before
chdir into Rails root
- **Narrowed exception handling**: `http_get_localhost` now rescues
specific socket/connection errors instead of broad `StandardError`

### Generator (`base_generator.rb`)
- Updated `home_page_pro_note_for_oss_app` to not claim Pro works
"without a license" (Pro raises on boot when unlicensed)

Closes #2880

## Test plan
- [x] `bundle exec rspec spec/react_on_rails/dev/server_manager_spec.rb`
— 93 examples, 0 failures
- [x] `bundle exec rspec
spec/react_on_rails/generators/install_generator_spec.rb:73` — passes
- [x] `bundle exec rubocop` on all changed files — no offenses
- [ ] Verify WSL browser detection in WSL environment
- [ ] Verify `--open-browser` works in non-TTY context (e.g., Docker)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Medium risk because it changes development process management and SSR
webpack configuration, which can affect local workflows and
server-bundle debugging/stack traces across platforms.
> 
> **Overview**
> **Stabilizes development ergonomics across environments.**
`ServerManager` browser auto-open was refactored to distinguish explicit
`--open-browser` from guarded auto-open (`--open-browser-once`), add WSL
launcher support (`wslview`/`wsl-open`), improve failure warnings,
lazily resolve the once-marker path, and narrow localhost polling
exception handling.
> 
> **Avoids a webpack 5.106+ SSR regression.** Server webpack configs
switch away from `devtool = 'eval'` to `cheap-module-source-map` in
development (and `false` in production) to prevent ESM default-export
issues and improve SSR stack trace mapping.
> 
> Also updates generator messaging to remove the claim that Pro can be
evaluated without a license, fixes generator `chmod` to use
`destination_root`, adds `.playwright-cli/` to `.gitignore`, and adds a
Jest assertion ensuring `scriptSanitizedVal` keeps a stable function
name for webpack compatibility.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
bd84af3. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Fixed server-side rendering (SSR) error stack traces to accurately map
line numbers by updating source map configuration.
* Improved browser auto-open functionality on WSL systems with better
launcher detection and fallback handling.

* **Documentation**
* Updated Pro licensing guidance to clarify users must review upgrade
documentation and obtain proper licenses before enabling Pro features.

* **Tests**
* Added test coverage for function naming stability and browser
auto-open behavior across different environments.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex PRs created from codex-named branches full-ci

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants