ReviewOS

also looking at this

stacks/uplink

fix(app): report a crash instead of vanishing, and cap the log

#55
Merged glennmichael123 wants to merge fix/log-a-crash-and-cap-the-log into main
4 files +252 -5
9 days ago

Closes #34.

~/Library/Logs/Uplink.log is the whole support story for the downloadable app: no crash reporting, no diagnostics bundle, and a stdout nobody is attached to. It had two problems, and the first is the reason the second matters.

The failure the log could not describe

There were no uncaughtException or unhandledRejection handlers, and the LaunchAgent sets no StandardErrorPath. So an uncaught throw went to a stderr nothing captures, and the process disappeared with the log's last line being whatever succeeded before it. The one failure most worth explaining was the one that left no trace at all.

Both are handled now: one entry with the name, message and stack, written whatever the TTY says, then a shutdown with a non-zero code.

Stopping is deliberate. A handler that swallows the error leaves the agent half alive, the watcher gone and the loopback server still answering, so the menubar reports a healthy app that answers no texts. A crash before the agent exists exits directly rather than calling shutdown, which closes over things that do not exist yet; the next start reaps the lock.

Nothing from the failing run is written beyond name, message and stack. A stack can carry a frame argument in some runtimes, and a text somebody sent is not ours to put in a file that outlives it.

The cap

5 MB, with one rotation kept as Uplink.log.1. Kept rather than truncated, for the same reason as above: the lines leading up to a crash are the ones worth reading, and a rotation triggered by that crash's own output would throw them away.

The size is checked on a counter rather than per line, because the watcher appends on a two second poll and a stat per write would be most of the work. The directory is made on every write, though — a ~/Library/Logs that goes away mid-run should not cost 200 lines before anyone notices. That one was a bug in my first draft and a test caught it.

Why there's a new module

The writer moved to app/Uplink/log.ts so the console redirect and the crash handlers share one path, which is what issue step 1 asked for. It also makes the behaviour testable, which matters more: app/Desktop/launcher.ts is a top-level script that spawns Craft, so nothing in it could be covered before.

The crash tests spawn a real process, throw inside it, and read the file back. Not a reimplementation of the wiring — they import and call the same installCrashHandlers the launcher does, so the thing under test is the thing that ships.

Verification

  • 150 unit tests pass, 14 new here, covering both handlers, the no-cascade rule, rotation in both directions, the replaced-generation case, and a write that cannot succeed
  • ./buddy lint and ./buddy typecheck clean
  • the desktop entry still bundles and --compile still produces a binary

Not in scope, as the issue says: the popover's Open log and Copy diagnostics buttons. This is the file's own reliability, so those have something worth opening.

🤖 Generated with Claude Code

4 changed files on the files tab, with 0 review threads.

Sign in to comment on this pull request.