Skip to content

[STG-2501] docs(cli): make unique named sessions the default in bundled browse skill#2329

Draft
shrey150 wants to merge 2 commits into
mainfrom
shrey/browse-skill-named-session-default
Draft

[STG-2501] docs(cli): make unique named sessions the default in bundled browse skill#2329
shrey150 wants to merge 2 commits into
mainfrom
shrey/browse-skill-named-session-default

Conversation

@shrey150

@shrey150 shrey150 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Two agents running the browse CLI in parallel clobber each other when they share a browser session — reported on browse 0.9.4: two Claude Code sessions mixed up each other's pages until one agent noticed the shared default session "got hijacked" and recovered by improvising a named session.

Reproduced with parallel Claude subagents against the shipped skill text. Two distinct failure modes, one root cause — session identity is not unique per agent:

  1. Bare default sharing. Quick one-off tasks run session-less commands (browse open <url> --local) because the skill's guidance is conditional ("named sessions for any non-trivial work… when multiple agents or parallel tasks may run at once" — a condition an agent cannot evaluate, since sibling sessions are invisible to it) and the first seven example commands in the file carry no --session. Repro: two parallel subagents given trivial tasks both landed on "session": "default" and both ran bare browse stop at the end — each stop tears down the other's browser.
  2. Colliding names. Even when agents follow the "use a named session" guidance, nothing says the name must be unique. Repro: two parallel subagents with similar prompts independently chose --session example-task; one agent's click showed up in the other's snapshots (the victim interpreted it as "the site silently redirected"), and the first finisher's browse stop --session example-task killed the sibling's browser mid-task, forcing a doctor → stop → re-open recovery cycle.

Amplifier: after a sibling's stop tears down the shared daemon, the victim's next command auto-starts a fresh daemon — which defaults to remote mode when BROWSERBASE_API_KEY is set (existing documented behavior), silently spinning up a paid cloud session for what the agent thinks is local work.

Fix (docs-only, bundled skill text)

Make unique task-derived session names the modeled default in packages/cli/skills/browse/SKILL.md:

  • --session research-a7f added to the anchor examples in Browser Target Selection (the block agents copy verbatim)
  • the conditional named-session paragraph is now unconditional, requires a task-derived name plus a unique suffix, and explains what sharing actually does (active page replaced, refs invalidated, sibling stop kills your browser)
  • Parallel Browser Work: uniqueness matters as much as naming; when spawning subagents, assign each one its session name (or set BROWSE_SESSION per subagent) instead of letting them choose
  • Best Practices rule 6 strengthened to match
  • new Troubleshooting bullet: how to recognize a hijacked session (page you never navigated to, refs stop matching, session dies mid-task) and recover by moving to a fresh unique name

No changeset: skill-text-only change, no runtime/package behavior change (same treatment as #2235, #2245).

E2E Test Matrix

Command / flow Observed output Confidence / sufficiency
Baseline: 2 parallel Claude subagents, trivial one-off task framing, shipped skill text (browse 0.9.1 local install; identical skill text to 0.9.4/main) Both ran bare browse open <url> --local"session": "default" in JSON output; both ran bare browse stop on the shared session Reproduces failure mode 1: agents default to the shared default session on quick tasks
Baseline: 2 parallel Claude subagents, multi-step tasks with similar prompts Both independently picked --session example-task; arm B's snapshot showed the IANA page arm A had clicked into (urlMaphttps://www.iana.org/help/example-domains on a task that never navigated there); arm A's browse stop --session example-task killed arm B mid-task (about:blank, then "already running in remote mode" on re-open) Reproduces failure mode 2: named-but-colliding sessions clobber exactly like default sharing
Post-fix: same 2 trivial prompts, agents pointed at the updated SKILL.md --session check-ex1a and --session httpbin-a1b — unique task-derived names, correct title/heading/URL output, each stopped only its own session Fixes mode 1: trivial tasks now start unique named sessions
Post-fix: same 2 multi-step prompts, agents pointed at the updated SKILL.md --session iana-check-x7q and --session research-ex01 — distinct names, no literal copy of the research-a7f example suffix, no foreign pages in any snapshot, clean isolated stops Fixes mode 2: derived names no longer collide; the concrete example suffix is not copied verbatim
pnpm turbo run build --filter=browse then pnpm vitest run tests/skills.test.ts tests/skills-install.test.ts in packages/cli () Test Files 2 passed (2) / Tests 33 passed (33) Skill install/catalog tests unaffected by the text change
npx prettier skills/browse/SKILL.md --check All matched files use Prettier code style! Formatting gate clean

Post-fix rows point agents at the updated file explicitly (installed copies keep the old text until the next release picks this up); baseline multi-step agents read the shipped text. n=2 per arm — behavioral evidence, not a statistical guarantee.

Linear: STG-2501

🤖 Generated with Claude Code


Summary by cubic

Make unique named sessions the default in the bundled browse skill so parallel agents don’t share the default session or collide on the same name. Updates packages/cli/skills/browse/SKILL.md to add --session to anchor examples, require task‑derived names with a unique suffix (and use them in the parallel‑work example), add guidance for subagents and a troubleshooting tip, and clarify BROWSE_SESSION; no runtime changes (Linear STG‑2501).

Written for commit 3f1e607. Summary will update on new commits.

Review in cubic

…kill

Parallel agents sharing the default browse session (or independently
picking the same session name) clobber each other's active page, refs,
and daemon lifetime. Make unique task-derived session names the modeled
default: session flags in the anchor examples, unconditional guidance
with a uniqueness requirement, parallel/subagent naming rules, and a
troubleshooting entry for recognizing a hijacked session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 3f1e607

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cubic-dev-ai cubic-dev-ai Bot left a comment

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.

1 issue found across 1 file

Confidence score: 5/5

  • In packages/cli/skills/browse/SKILL.md, the example under the parallel-work rule still uses collidable names (search-a/search-b), which could confuse contributors and lead to occasional branch/worktree naming collisions if copied as-is; update the example to task-derived names with unique suffixes before merging to fully align guidance.
Architecture diagram
sequenceDiagram
    participant A as Agent A
    participant B as Agent B
    participant CLI as browse CLI
    participant DM as Daemon (Session Manager)

    Note over A,DM: CHANGED: Docs now enforce unique session names
    alt BEFORE: No --session → shared "default" session
        A->>CLI: browse open url --local
        B->>CLI: browse open url --local
        CLI->>DM: session "default" (shared)
        A->>CLI: browse stop
        CLI->>DM: kill session "default" (kills B)
    else AFTER CHANGED: Unique session names per task
        A->>CLI: browse open url --session research-a7f --local
        B->>CLI: browse open url --session data-b3c --local
        CLI->>DM: sessions "research-a7f" & "data-b3c"
        A->>CLI: browse stop --session research-a7f
        CLI->>DM: stop session research-a7f only
    end
Loading

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/cli/skills/browse/SKILL.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@shrey150 shrey150 marked this pull request as draft July 8, 2026 03:56
@shrey150

shrey150 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Parking this as draft deliberately: we prototyped and validated a CLI-side fix (auto-generated session names, branch shrey/browse-auto-session-prototype) that would supersede most of this guidance. Holding to avoid shipping skill text we immediately rewrite. Context: STG-2501.

shrey150 added a commit that referenced this pull request Jul 9, 2026
…idance (#2336)

## Summary

A 3-provider bare-loop smoke test (E2B/Modal/Vercel, real sandboxes,
local CLI build) surfaced two systematic gaps in the bundled `browse`
skill (`packages/cli/skills/browse/SKILL.md`):

1. **3/3 agents ignored the pre-wired `BROWSE_SESSION` env var** and
self-named sessions via `--session <name>` (one run orphaned a
Browserbase session as a result). The skill taught named `--session`
usage but never said "if `BROWSE_SESSION` is already set, every command
already targets that session — don't pass `--session` or invent a new
name." This defeats the sandbox-template env-steering pattern
(`BROWSERBASE_API_KEY` + `BROWSE_SESSION=<name>` → flagless remote
commands).
2. **2/3 agents tried `--local` first inside Chrome-less containers**,
hit "No Chrome or Chromium found", then recovered on retry. The skill
lacked explicit guidance that `--local` requires local Chrome/Chromium
and that containers/CI/sandboxes without Chrome should go straight to
`--remote`.

## What changed

Two minimal additions to `packages/cli/skills/browse/SKILL.md`, both in
the existing "Browser Target Selection" section (the section that
already teaches local/remote choice and named-session usage) — no
restructuring, no new sections:

```
`--local` requires Chrome or Chromium already installed on the machine. In containers, CI, and sandboxes with no browser installed, use `--remote` instead of `--local`. If `--local` fails with "No Chrome or Chromium found" and `BROWSERBASE_API_KEY` is set, switch to `--remote` — do not retry `--local`.
```

```
If `BROWSE_SESSION` is already set in the environment, every command already targets that session — do not pass `--session` or invent a new name. An explicit `--session <name>` always overrides `BROWSE_SESSION` for that command, so only pass it to deliberately target a different session.
```

Verified the precedence claim against the actual implementation before
writing the wording (not just trusting the smoke-test writeup):

- `packages/cli/src/lib/driver/flags.ts:73` — `export function
sessionName(value?: string): string { return value ??
process.env.BROWSE_SESSION ?? "default"; }`. An explicit `--session`
flag always wins over `BROWSE_SESSION`; when omitted, `BROWSE_SESSION`
becomes the effective session; with neither, it's `"default"`.
- `packages/cli/src/lib/driver/command-cli.ts:75` —
`runDriverCommandFromFlags` calls `sessionName(flags.session)` for every
driver command, so this precedence applies uniformly across `open`,
`snapshot`, `click`, `stop`, etc.
- `packages/cli/src/lib/driver/remote.ts:130` — exact no-Chrome error
string is `"No Chrome or Chromium found on this machine. Install one
(Linux: apt install chromium · macOS: brew install --cask google-chrome,
or Chromium with CHROME_PATH set), attach to a running browser with
--cdp <port>, or set BROWSERBASE_API_KEY to use a remote browser."`

## No changeset

This is a skill-text-only change (`SKILL.md`), same category as PR #2329
(skill-text-only fix, no changeset). The updated text ships with the
next `browse` release — a release is already queued by #2335's changeset
— and is consumed at runtime by the evals harness added in #2334.

## E2E Test Matrix

| Command / flow | Observed output | Confidence / sufficiency |
| --- | --- | --- |
| `pnpm install && pnpm turbo run build --filter=browse` (worktree,
local build) | `Tasks: 4 successful, 4 total` | Proves the local CLI
build succeeds with the change in place; no build regressions from a
docs-only edit. |
| Resolve `bundledCliSkillPath()` from the compiled
`dist/lib/skills/install.js` via `require.resolve` + manual path join
(no `skills show` command exists on `main` yet — it ships in PR #2335,
unmerged at time of this PR) | `resolved bundledCliSkillPath:
<worktree>/packages/cli/skills/browse` | Proves the exact file `browse
skills install` resolves to at runtime is the file this PR edits, not a
stale copy. |
| `grep -n "already set in the environment\|do not retry"
packages/cli/skills/browse/SKILL.md` | `76:If \`BROWSE_SESSION\` is
already set in the environment, every command already targets that
session...` `68:...If \`--local\` fails with "No Chrome or Chromium
found"...do not retry \`--local\`.` | Confirms the exact new lines are
present at the bundled path resolved above — the new guidance flows
through to the real command path. |
| `node bin/run.js skills install --help` | Prints usage for `browse
skills install` (no side effects triggered — did not run the real
install, which mutates the global `~/.agents/skills` pool via `npx
skills add --global`) | Confirms the command is wired and reachable;
deliberately did not execute the real install to avoid mutating local
global skill state outside this change's scope. |
| `pnpm test:cli` (vitest, worktree build) | `Test Files 23 passed (23)`
/ `Tests 344 passed (344)` | Full existing CLI test suite still green;
this change touches no runtime code. |
| `pnpm eslint .` | No output / exit 0 | Lint clean. |
| `pnpm format:check` (prettier) | `All matched files use Prettier code
style!` | Formatting clean, including the edited Markdown. |
| `tsc --noEmit -p tsconfig.json` | No output / exit 0 | Typecheck clean
(unaffected by a Markdown-only change, included for completeness). |

## Related

- Linear:
https://linear.app/browserbase/issue/STG-2513/browse-skill-document-browse-session-precedence-container-remote
- Evidence source: 3-provider (E2B/Modal/Vercel) bare-loop smoke test,
2026-07-09
- Text will be exercised by the evals harness landing in #2334
- Release vehicle: changeset already queued in #2335

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


<!-- This is an auto-generated description by cubic. -->
---
## Summary by cubic
Clarifies the `browse` skill docs to prevent session misrouting and
container failures by documenting `BROWSE_SESSION` precedence and when
to use `--remote` instead of `--local`. Addresses Linear STG-2513.

- **Bug Fixes**
- Documented that if `BROWSE_SESSION` is set, commands target that
session; only pass `--session` to override (precedence: `--session` >
`BROWSE_SESSION` > `default`).
- Stated that `--local` requires Chrome/Chromium; in
containers/CI/sandboxes use `--remote`. If you see "No Chrome or
Chromium found," switch to `--remote` instead of retrying `--local`.

<sup>Written for commit 829ff7b.
Summary will update on new commits.</sup>

<a
href="https://cubic.dev/pr/browserbase/stagehand/pull/2336?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>

<!-- End of auto-generated description by cubic. -->

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant