What we learned
We exercised the generated cloud setup against a real Claude Code web environment while preparing a Rails application for both Claude Cloud and Codex Cloud. The tunnel itself works, but the experiment exposed a few lifecycle assumptions that the generator should stop making.
Claude clones the selected branch before it runs the environment setup script, but that script does not run from the repository and CLAUDE_PROJECT_DIR is not available in that phase. The current copy/paste launcher in docs/providers/claude-code.md can therefore fail before cloud-vm-setup.sh starts. We also found that an environment setup can successfully install the project's pinned Node and Yarn while the later interactive session still resolves Claude's preinstalled Node 22 and global Yarn 1.
The larger issue is that two different jobs are currently combined:
- Machine provisioning, such as installing exact Ruby and Node toolchains. Claude caches this filesystem work between sessions.
- Repository/session preparation, such as starting PostgreSQL, reconciling dependencies, preparing databases, and ensuring the app and tunnel processes are alive. Services are not restored from the environment cache, and this work needs to run again when a session starts or resumes.
Proposed direction
- Make the Claude environment launcher find the checked-out
cloud-vm-setup.sh safely instead of assuming either the current directory or CLAUDE_PROJECT_DIR. If the selected branch does not contain the script, fail with an explicit instruction to choose the correct branch.
- Keep
cloud-vm-setup.sh responsible for exact machine-level toolchains and make their activation survive into the agent's later shell commands. In particular, honor .node-version/packageManager; a project pinned to Node 24 and Yarn 4 must not quietly run under Node 22 and Yarn 1.
- Generate an idempotent project bootstrap command for per-session work: start the detected database service, run dependency reconciliation only when needed, prepare the database, and run
bin/agent-vm-tunnel ensure.
- Invoke that bootstrap from Claude's
SessionStart hook for startup|resume. Use CLAUDE_ENV_FILE where needed to export the selected toolchain into subsequent Bash calls.
- Remove the unconditional
UserPromptSubmit tunnel reconciliation unless evidence shows it is necessary. Running the full maintenance command before every prompt adds latency and hides the actual lifecycle boundary.
- Keep the same generated project bootstrap usable from Codex Cloud and generic Linux VMs, even though those providers may invoke it through a different setup mechanism.
- Explain Anthropic's environment cache behavior and how to force a fresh provisioning run after the setup script or required toolchain changes.
Concrete gaps in the current generator
cloud-vm-setup.sh comes from lib/generators/agent_vm_tunnel/install/templates/cloud-vm-setup.sh.tt.
.claude/settings.json is assembled in install_claude_hooks / add_preview_hooks in lib/generators/agent_vm_tunnel/install/install_generator.rb.
- The Codex guidance block is assembled inline by
install_codex_guidance in the same generator.
- The Claude environment launcher exists only as a fenced code block in
docs/providers/claude-code.md; the generator does not emit it and no test syntax-checks it.
test/test_install_generator.rb covers generated shell syntax and hook merging, but does not cover the provider setup launcher or exact toolchain persistence into a later session.
Acceptance checks
Use a generated Rails app that pins exact current Ruby, Node, and Yarn versions.
- In a fresh Claude Code web environment, select a non-default branch containing the generated files and run the documented environment launcher successfully.
- In the resulting agent session, bare
ruby, node, and yarn resolve to the project's pinned versions.
- PostgreSQL is ready, dependencies are reconciled,
db:prepare succeeds, and bin/agent-vm-tunnel status reports a healthy preview.
- Start a second session from the cached environment. Machine provisioning should be reused, while services and project state are prepared again.
- Resume an idle session and confirm the same recovery path.
- Run the equivalent generated setup in Codex Cloud so the Claude-specific fix does not regress the other provider.
- Add generator tests for hook matchers/idempotence and an executable or extracted test for the documented Claude launcher.
This issue is intentionally about generated behavior and provider lifecycle. Application-specific Foundation decisions should stay in the generated application rather than accumulating inside the tunnel gem.
What we learned
We exercised the generated cloud setup against a real Claude Code web environment while preparing a Rails application for both Claude Cloud and Codex Cloud. The tunnel itself works, but the experiment exposed a few lifecycle assumptions that the generator should stop making.
Claude clones the selected branch before it runs the environment setup script, but that script does not run from the repository and
CLAUDE_PROJECT_DIRis not available in that phase. The current copy/paste launcher indocs/providers/claude-code.mdcan therefore fail beforecloud-vm-setup.shstarts. We also found that an environment setup can successfully install the project's pinned Node and Yarn while the later interactive session still resolves Claude's preinstalled Node 22 and global Yarn 1.The larger issue is that two different jobs are currently combined:
Proposed direction
cloud-vm-setup.shsafely instead of assuming either the current directory orCLAUDE_PROJECT_DIR. If the selected branch does not contain the script, fail with an explicit instruction to choose the correct branch.cloud-vm-setup.shresponsible for exact machine-level toolchains and make their activation survive into the agent's later shell commands. In particular, honor.node-version/packageManager; a project pinned to Node 24 and Yarn 4 must not quietly run under Node 22 and Yarn 1.bin/agent-vm-tunnel ensure.SessionStarthook forstartup|resume. UseCLAUDE_ENV_FILEwhere needed to export the selected toolchain into subsequent Bash calls.UserPromptSubmittunnel reconciliation unless evidence shows it is necessary. Running the full maintenance command before every prompt adds latency and hides the actual lifecycle boundary.Concrete gaps in the current generator
cloud-vm-setup.shcomes fromlib/generators/agent_vm_tunnel/install/templates/cloud-vm-setup.sh.tt..claude/settings.jsonis assembled ininstall_claude_hooks/add_preview_hooksinlib/generators/agent_vm_tunnel/install/install_generator.rb.install_codex_guidancein the same generator.docs/providers/claude-code.md; the generator does not emit it and no test syntax-checks it.test/test_install_generator.rbcovers generated shell syntax and hook merging, but does not cover the provider setup launcher or exact toolchain persistence into a later session.Acceptance checks
Use a generated Rails app that pins exact current Ruby, Node, and Yarn versions.
ruby,node, andyarnresolve to the project's pinned versions.db:preparesucceeds, andbin/agent-vm-tunnel statusreports a healthy preview.This issue is intentionally about generated behavior and provider lifecycle. Application-specific Foundation decisions should stay in the generated application rather than accumulating inside the tunnel gem.