Skip to content

fix(ssh): give each managed launch a fresh remote server log - #8930

Open
marmar9615-cloud wants to merge 2 commits into
pingdotgg:mainfrom
marmar9615-cloud:fix/ssh-launch-log-truncate
Open

fix(ssh): give each managed launch a fresh remote server log#8930
marmar9615-cloud wants to merge 2 commits into
pingdotgg:mainfrom
marmar9615-cloud:fix/ssh-launch-log-truncate

Conversation

@marmar9615-cloud

@marmar9615-cloud marmar9615-cloud commented Aug 31, 2026

Copy link
Copy Markdown

What changed

One line in the generated remote launch script, packages/ssh/src/tunnel.ts:

rm -f "$LOG_FILE"
nohup env T3CODE_NO_BROWSER=1 "$RUNNER_FILE" serve ... >>"$LOG_FILE" 2>&1 < /dev/null &

Why it should exist

The readiness failure branch tells two cases apart by file size:

if [ -s "$LOG_FILE" ]; then
  tail -n 80 "$LOG_FILE" >&2
else
  printf 'It wrote nothing to %s, so it exited before producing any output.\n' "$LOG_FILE" >&2
fi

That empty-log arm came from #5132, to name the case where the remote server exits without
logging anything. The launch opens the log with >> and nothing clears it, so [ -s ] is true
from the first run that logs onward and the arm is unreachable after that. A server that dies
silently is reported with the previous run's error, which sends you after the wrong problem. The
file also grows without bound across managed restarts.

Why unlink and not truncate

wait_for_pid_exit gives up after 20 x 0.1s (tunnel.ts:495-502), so a server that ignores the
kill is still holding its descriptor when the next launch runs. Measured separately from the test,
with a survivor holding the append-mode descriptor the launch gave it:

launch does resulting log [ -s ] takes
> truncate 5 bytes the tail branch
rm -f then >> 0 bytes the wrote-nothing branch

Truncating hands the new file to the old writer. Unlinking leaves it with the old one. The test
below passes under either form, so treat the table as the reason for the choice, not as something
the test proves.

Test

One executed case in packages/ssh/src/tunnel.test.ts. It seeds server.log with a marker,
installs a fake node that picks a port, fails readiness at once, and lets the runner exit
without writing a byte, then runs the real buildRemoteLaunchScript() output through a shell. It
asserts exit 1, stderr containing "It wrote nothing to", no marker in stderr, and a 0-byte log.

Against the append-only script it fails at the "It wrote nothing to" assertion. It follows the
executed-shell pattern already in apps/desktop/src/wsl/DesktopWslEnvironment.test.ts, including
the shell probe that skips where the tools are missing.

vp test run src/tunnel.test.ts: 1 file, 14 tests, all pass. tsgo --noEmit clean.

Nothing else reads this path. The only other reference is the on-demand tail script at
tunnel.ts:649, which opens by path per SSH invocation, so unlinking strands no descriptor.


Note

Low Risk
Small change to generated remote launch shell behavior and failure diagnostics only; no auth or data-path changes.

Overview
Each managed remote SSH launch now deletes the existing server.log before starting the server, so readiness failures can tell a silent new run from one that actually logged output.

Previously the log was only opened in append mode, so [ -s "$LOG_FILE" ] stayed true after the first run and a server that exited without writing anything could surface stale tail output instead of the "It wrote nothing to …" message.

Adds an executed test in tunnel.test.ts that runs the real buildRemoteLaunchScript() output under bash/WSL with a seeded stale log and a fake node; the suite skips when required POSIX tools are missing.

Reviewed by Cursor Bugbot for commit 9c84d1d. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Fix stale log tailing by deleting $LOG_FILE before nohup in SSH launch script

  • The generated remote launch script in tunnel.ts now runs rm -f "$LOG_FILE" immediately before starting the managed server, so readiness and output checks see only the current run's log.
  • Adds an end-to-end test suite in tunnel.test.ts that executes the generated script in a real POSIX shell, using a fake node binary and a seeded stale server.log to verify a silent launch is reported instead of old log content.
  • Tests conditionally skip when no suitable shell with nohup, mktemp, cmp, and tail is available.

Macroscope summarized 9c84d1d.

The managed remote launch appends to server.log, and the readiness
failure branch treats whatever is already in that file as this run's
output:

  if [ -s "$LOG_FILE" ]; then
    tail -n 80 "$LOG_FILE" >&2
  else
    printf 'It wrote nothing to %s, so it exited before producing any output.\n'
  fi

The empty-log arm arrived in pingdotgg#5132 to name the case where the remote
server exits without logging anything. The log is opened in append mode
and never cleared, so [ -s ] is true from the first run that logs
onward. After that the empty-log arm is unreachable, a server that dies
silently is reported with the previous run's error, and the user is
pointed at the wrong remedy. The file also grows without bound across
managed restarts.

Unlink rather than truncate. wait_for_pid_exit gives up after two
seconds, so a previous server that ignores the kill is still holding its
descriptor when the next launch runs. Unlinking leaves it writing into
the old file; truncating leaves it writing into the new one, which puts
the size back above zero and sends the diagnostic down the tail branch
again.

The new test runs the real generated script through a shell against a
seeded stale log, with a fake node that picks a port, fails readiness at
once, and lets the runner exit without writing a byte. It fails on the
append-only script at the "It wrote nothing to" assertion.
@coderabbitai

coderabbitai Bot commented Aug 31, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 851f68bd-5f5b-4721-b0aa-dc3f09d540c2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Warning

Your free Security trial is over. An organization admin can activate Security or dismiss this notice.


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

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T18:18:22.742111Z be1157e PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Aug 31, 2026
@macroscopeapp

macroscopeapp Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Not approved

Macroscope's review found this PR not approvable — The production change is a small, isolated SSH launch fix that clears stale managed-server logs before starting a new process, with an end-to-end regression test. The test additionally introduces a line-scoped nodeBuiltinImport:off static-analysis suppression, which warrants human review.

Notes:

  • No code objects were reviewed. Approvability was decided on eligibility alone.

You can add or adjust custom eligibility rules. Learn more.

The executed suite needs node:child_process, which effect(nodeBuiltinImport)
rejects. The first version turned the rule off for the whole file, which also
covered the thirteen tests that were already there and had never needed it.

Use the next-line form instead, so the exemption reaches only the one import
that requires it. Verified against a patched tsgo: removing the directive
reports TS377057 at src/tunnel.test.ts(3,35), and the next-line form silences
exactly that.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant