Skip to content

fix(aside): run the probe deadline through a function so zsh can detect Aside - #2848

Closed
welchsteven wants to merge 1 commit into
garrytan:mainfrom
welchsteven:fix/aside-probe-zsh-deadline-function
Closed

welchsteven wants to merge 1 commit into
garrytan:mainfrom
welchsteven:fix/aside-probe-zsh-deadline-function

Conversation

@welchsteven

Copy link
Copy Markdown

Why (in your own words)

On macOS the default shell is zsh, and agent harnesses usually run their shell tool as zsh too. Under zsh, the BROWSER SETUP probe reports ASIDE_NOT_RUNNING on every run, even with Aside installed, running and signed in, so every browse-family skill silently falls back to $B and the user loses their real logged-in sessions. The probe stores its deadline as a string (_T="gtimeout 30") and runs $_T aside repl …, and zsh doesn't word-split that, so it searches for a single command named gtimeout 30. This PR makes the deadline a small _gs_bounded() function instead. A function passing "$@" needs no word splitting, so it behaves the same in zsh, bash and sh. It also makes the failure branch print why it failed, so a signed-out or windowless Aside is no longer reported as "not running".

Relationship to #2824

#2824 (@AntonioVitalic) diagnosed the same bug first and fixes the common case, zsh with Homebrew gtimeout. I'm opening this as an alternative because its eval form regresses stock macOS. The perl fallback string perl -e alarm(shift);exec(@ARGV) 30 only works when it is word-split and never re-parsed, and eval re-parses it, so the probe hits syntax error near unexpected token '(' in every shell. That includes bash users on a stock Mac who detect Aside correctly today (matrix below). Happy for this to be folded into #2824, or closed in its favour if it adopts the function form.

Fixes #2842.

Live evidence

macOS 26.6.2, zsh 5.9, Aside CLI 1.26.906.1630, Aside signed in.

The bug, reproduced:

$ zsh <probe from origin/main>
ASIDE_NOT_RUNNING

$ zsh -c '_T="gtimeout 30"; $_T aside --version'
zsh:1: command not found: gtimeout 30

The fix:

$ zsh  <patched probe>   ->  READY: aside 1.26.906.1630
$ bash <patched probe>   ->  READY: aside 1.26.906.1630
$ sh   <patched probe>   ->  READY: aside 1.26.906.1630

All three probes on a stock-macOS PATH (/usr/bin:/bin:/usr/sbin:/sbin plus ~/.local/bin: no gtimeout, no timeout, only /usr/bin/perl), plus the Homebrew case. #2824 was rebuilt by hand from its diff, not run from its branch:

Probe zsh + Homebrew zsh, stock bash, stock
origin/main ASIDE_NOT_RUNNING ASIDE_NOT_RUNNING READY
#2824 (eval) READY ASIDE_NOT_RUNNING ASIDE_NOT_RUNNING
this PR READY READY READY (sh too)

What grep -q hides on #2824's stock-macOS path:

$ env PATH=<stock> bash -c '_T="perl -e alarm(shift);exec(@ARGV) 30"; eval "${_T:+$_T }aside --version"'
bash: eval: line 0: syntax error near unexpected token `('
bash: eval: line 0: `perl -e alarm(shift);exec(@ARGV) 30 aside --version'

The perl deadline inside the function really fires:

$ perl -e 'alarm(shift); exec(@ARGV)' 2 sleep 10
exit=142 after 2s

The new diagnostic, captured live while the Aside profile was signed out. grep -q used to throw this away, although rule 2 already told agents to quote it:

ASIDE_NOT_RUNNING
PROBE_OUTPUT: No browser window is open for account u0 ("Profile 0"). Open a window in that Aside profile and retry, or pick another account with --account.

Tests:

$ bun test test/aside-driver.test.ts test/third-party-actions.test.ts
 55 pass, 0 fail
$ bun test test/parity-baseline-integrity.test.ts test/parity-sectioned.test.ts test/catalog-budget.test.ts test/context-budget-ratchet.test.ts
 26 pass, 0 fail

Scope

  • Changed: scripts/resolvers/aside.ts (the probe and the ASIDE_NOT_RUNNING guidance); test/aside-driver.test.ts (it asserted the buggy strings verbatim, which is how the bug survived; it now asserts the function form and guards against the string form coming back); 20 SKILL.md files regenerated with bun run gen:skill-docs; the three ship goldens, updated only in the probe block because no script regenerates them.
  • Verified live by: running the rendered probe from browse/SKILL.md under zsh, bash and sh against a real, signed-in Aside, on both the Homebrew PATH and a stock-macOS PATH, then driving a real page through aside repl.
  • Did NOT test: the Linux timeout branch (no Linux machine; it has the same shape as the gtimeout branch), Windows, or the paid evals. The full free suite shows 17 failures in 8 files. None of those files reference the resolver or the generated docs, and two of them, re-run on a clean origin/main tree, failed the same way.

Liveness proof (required)

⚠️ Pending. This stays a draft until @welchsteven attaches the live GSTACK PR screenshot.

Checklist

🤖 Generated with Claude Code

…ct Aside

The BROWSER SETUP probe stored its deadline wrapper as a string and ran it as
a bare word:

    _T="gtimeout 30"   # or "timeout 30", or "perl -e alarm(shift);exec(@argv) 30"
    $_T aside repl ...

zsh does not word-split unquoted parameter expansions, so it looks for one
command literally named "gtimeout 30", fails, and prints ASIDE_NOT_RUNNING
against a healthy Aside. Every browse-family skill then falls back to $B, and
zsh users (the macOS default shell) lose their real signed-in sessions.

The deadline is now a _gs_bounded() function passing "$@", which needs no word
splitting and behaves the same in zsh, bash and sh. The two string-preserving
fixes both regress someone: ${=_T} is zsh-only and breaks POSIX sh, and eval
breaks the stock-macOS path. The perl fallback string only works when it is
word-split and never re-parsed, so eval turns it into "syntax error near
unexpected token '('" in every shell, including bash users on a stock Mac who
detect Aside correctly today. Inside the function the perl program is quoted,
and the deadline is verified to fire (alarm 2 vs sleep 10: exit 142 after 2s).

The failure branch now captures the probe output and prints PROBE_OUTPUT,
so "No browser window is open for account ..." (Aside running, profile signed
out or windowless) is distinguishable from the app being down. Rule 2 already
asked agents to quote the probe output verbatim; the old grep -q discarded it.

test/aside-driver.test.ts asserted the buggy strings verbatim, which is how the
bug survived. It now asserts the function form and guards against the string
form returning. Regenerated with gen:skill-docs; the three ship goldens get the
same block by surgical replacement.

Refs garrytan#2842, garrytan#2824.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@trunk-io

trunk-io Bot commented Sep 11, 2026

Copy link
Copy Markdown

Merging to main in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Aside probe always reports ASIDE_NOT_RUNNING under zsh: unquoted $_T timeout wrapper is never word-split

1 participant