Skip to content

[STG-2502] proposal: auto-sync browse skill from stagehand to browse-plugin#2330

Draft
shrey150 wants to merge 2 commits into
mainfrom
shrey/sync-browse-plugin-skill
Draft

[STG-2502] proposal: auto-sync browse skill from stagehand to browse-plugin#2330
shrey150 wants to merge 2 commits into
mainfrom
shrey/sync-browse-plugin-skill

Conversation

@shrey150

@shrey150 shrey150 commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

⚠️ PROPOSAL — draft, not functional yet. Opening this to make the shape concrete for a team discussion, not to merge as-is.

Linear: STG-2502

What

browserbase/browse-plugin (the static, git-pinned repo external marketplaces — Claude Code, Codex, Cursor, Gemini CLI, Grok Build, Droid — SHA-pin) carries its own copy of the browse CLI skill at skills/browse/SKILL.md. That copy is currently updated manually, which is exactly the kind of drift risk that's already caused confusion elsewhere in this effort (multiple repos disagreeing about which skill copy is current).

This adds steps to the existing release.yml "Release" job — right after "Tag browse release" — that diff the canonical SKILL.md against browse-plugin's copy and open a PR there if they've drifted. Nothing auto-merges — a human reviews the sync PR like any other.

Trigger, corrected from the first version of this PR: the new steps are gated on steps.browse_cli.outputs.should_publish == 'true' && steps.browse_cli.outputs.already_published != 'true' — the exact same condition that already gates "Publish browse to npm" and the browse Docker image a few steps up. This only fires on a genuine new browse release, not on every push that happens to touch the skill file. (The first version of this PR triggered on any push to main touching the SKILL.md path, with no relationship to whether a new version actually shipped — wrong, since browse-plugin could then describe CLI behavior nobody can actually install yet. Fixed by moving the sync into the release job itself instead of a separate path-filtered workflow.)

Why this shape, and what else was considered

  • Direction (stagehand canonical → browse-plugin follows): the CLI and its own skill instructions should version together in the repo that ships the actual product. browse-plugin is a downstream distribution artifact, not source of truth.
  • Trigger (gated on actual release, not on file-change): browse-plugin should only ever describe a version of the CLI that's genuinely npm install -g-able. Syncing on every SKILL.md edit reaching main — including edits bundled with CLI code that hasn't been version-bumped/published yet — would let the marketplace-facing skill get ahead of what's actually shipped.
  • Alternatives considered for the sync mechanism itself: git submodule (rejected — GitHub's UI shows submodules as opaque commit-links, not real file content, which breaks marketplace validators that expect an actual SKILL.md in the working tree) and git subtree (same end-state as this PR, just different tooling to produce the diff — no clear advantage). Manual sync is the status quo this is meant to replace.

What's real vs. what's still needed

Real and tested (this PR):

  • scripts/sync-browse-plugin-skill.sh — the diff+copy logic, deliberately separated from the workflow so it's unit-testable without CI. Verified against the actual current files in both directions:
    • No drift (today's real state): correctly reports changed=false, touches nothing.
    • Simulated drift (edited a scratch copy of browse-plugin's SKILL.md): correctly reports changed=true and copies the canonical content over exactly.
  • The modified release.yml passes actionlint clean end-to-end (not just the new steps in isolation — the whole file, to make sure nothing existing broke).

Not yet real — needs a decision before this can run:

  • A BROWSE_PLUGIN_SYNC_TOKEN secret with write access to browserbase/browse-plugin (contents + pull-requests). The default GITHUB_TOKEN won't work — PRs authored by GITHUB_TOKEN don't trigger downstream CI, which already caused a silent-CI issue on stagehand#2241. A classic PAT won't work either — the org's SSO enforcement 403s those (hit repeatedly in past sessions). That leaves a GitHub App installation as the realistic option — there's no existing cross-repo-PR-opening pattern anywhere in this org (checked stagehand, core, browserbase-ai-sdk, integrations) to copy from, so this would be a new piece of infrastructure, not a config tweak.
  • Review ownership on the receiving end: browse-plugin's CODEOWNERS is currently just * @browserbase/gtm — worth deciding if sync PRs should route somewhere more specific.

E2E Test Matrix

Command / flow Observed output Confidence / sufficiency
diff packages/cli/skills/browse/SKILL.md <browse-plugin>/skills/browse/SKILL.md (real files, both repos as of today) exit 0, no diff Confirms today's copy is currently in sync — the automation would be a no-op right now, proving the script doesn't false-positive on the real content it'll actually run against.
STAGEHAND_DIR=<real stagehand> BROWSE_PLUGIN_DIR=<real browse-plugin> scripts/sync-browse-plugin-skill.sh changed=false Sufficient — proves the no-op path against real files, not fixtures.
Same script against a scratch copy of browse-plugin with its SKILL.md deliberately edited to simulate drift changed=true, then diff <canonical> <scratch-copy> → exit 0 (files now identical) Sufficient — proves the copy path actually resolves drift correctly, not just detects it.
actionlint .github/workflows/release.yml (full file, post-edit) exit 0, zero findings Sufficient for workflow syntax/semantics, and confirms the existing npm-publish/Docker/canary steps weren't broken by this edit — does not (and cannot, without the real secret) prove the live checkout/push/gh pr create steps work end-to-end.

Not proven: the actual cross-repo checkout/push/PR-open steps, since they need a real BROWSE_PLUGIN_SYNC_TOKEN this environment doesn't have. That's the actual open question for the team — not the diff logic or the trigger condition, both of which are verified.

🤖 Generated with Claude Code

Whenever packages/cli/skills/browse/SKILL.md changes on main, diff it
against browserbase/browse-plugin's copy and open a PR there if it
drifted. Keeps the plugin-marketplace distribution repo from silently
going stale relative to the canonical skill shipped in the CLI.

Diff+copy logic lives in scripts/sync-browse-plugin-skill.sh, kept
separate from the workflow so it's unit-testable against two local
checkouts without needing CI. Verified both branches against the real
current files: no-op when in sync, correct copy when drifted
(simulated by editing a scratch copy of browse-plugin).

NOT YET FUNCTIONAL — needs a BROWSE_PLUGIN_SYNC_TOKEN secret with
write access to browserbase/browse-plugin (contents + pull-requests).
Default GITHUB_TOKEN won't work: PRs it authors don't trigger
downstream CI (already bit us once, stagehand#2241), and classic PATs
get 403'd by the org's SSO enforcement. Recommend a GitHub App
installation — no existing cross-repo-PR pattern in this org to copy
from, so this would be a new one, not a copy of a battle-tested setup.

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

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 7432353

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

The first version triggered on every push to main touching
packages/cli/skills/browse/SKILL.md, with no relationship to whether
browse actually shipped a new version. That meant browse-plugin could
get synced to describe CLI behavior that isn't published yet.

Moved the sync into release.yml itself, as steps gated on the exact
same should_publish/already_published condition that already gates
"Publish browse to npm" and the browse Docker image — so it only
fires on a genuine new release, same trigger the rest of the browse
release pipeline already trusts.

Co-Authored-By: Claude Sonnet 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