name: publishing-the-bridge description: "PUBLISH skill for maintainers of the adom-browser-extension (native-browser) bridge - NOT for driving it. The exact, proven recipe to cut a release: bump versions in lockstep, build the zip, publish it as a Release ASSET (release create + upload --platform any), point the manifest url at the /download/ path (never a /files blob), push source, and land it with refresh_bridges. Covers the two-artifact split (Release zip vs skills pkg), the pkg's package.json declaring dependencies:{adom/adom-desktop:^1.9.x} (so pkg install pulls the AD CLI + core skills), the pkg shipping ONLY user skills while dev/publish skills live in the source repo (not the pkg, not files[], not user-invocable:false), the bridge.json contracts we declare (timeouts) and one we deliberately don't (detect), and the standing show-in-pup rule. Everyday users never need this."

Parent skill: adom-browser-extension. Maintainer-only skill (not user-invocable). Bridge SDK: https://wiki.adom.inc/adom/adom-desktop-bridges

Publishing the native-browser bridge

What "the bridge" actually is (3 artifacts, 3 places)

  1. Runtimebridge/ (server.js + bridge.json) zipped and published as a Release asset (release create + release upload --platform any), with a manifest on /files whose url points at the Release /download/ path. AD streams it to bridges-cache/native-browser/ and spawns it. (Node runtime is source-only; AD provisions Node — see the SDK "Runtime contract".)
  2. Skills pkg — the skills/<name>/SKILL.mds, published via adom-wiki pkg publish --org adom, installed into the container's ~/.claude/skills + ~/.codex/skills.
  3. Discoverydiscovery_triggers + discovery_pitch on page.json.

bridge.json contracts we declare (and one we deliberately don't)

  • timeouts (AD v1.9.79+): AD hard-caps every bridge verb at 60s unless you declare per-verb HTTP budgets. Ours match the bridge's own timeoutFor() (screenshot|record|fetch_url|input_dispatch → 120s) plus wait (blocks up to its 120s cap): verbs {wait:125, fetch_url:120, input_dispatch:120}
    • prefixes [{screenshot:120},{record:120}], default:60, and statusVerb:"nbrowser_status" (AD returns {stillRunning:true,statusVerb} on its own timeout → caller polls). Keep the describe timeoutSeconds in server.js aligned. Note: the adom-desktop CLI has its own ~30s client timeout, separate from AD's bridge budget — you can't observe a >30s verb via a bare CLI call.
  • detect — intentionally OMITTED. detect is for a HOST APP AD detects/gates. Native-browser drives the user's REAL browser via the extension, so the authoritative "is it usable" signal is extension-connected (nbrowser_readiness), not a browser binary on disk — and our read-only verbs are always forwarded regardless. A single hostApp:"Google Chrome" would MISREPORT the common Edge-only machine (verified real: ADOMBASELINE runs fine on Edge with no Chrome), and Edge is universal on Windows so "browser installed" is near-zero signal. So we do NOT declare detect.

Release the RUNTIME — the exact, proven sequence

Deps: none (core http/net), so the payload is source-only (no node_modules).

  1. Bump the version in THREE places, together (they drift if you don't):
    • bridge/bridge.json version (what AD reads + reports),
    • bridge/package.json version,
    • bridge/BRIDGE_VERSION. server.js reads VERSION from bridge.json (authoritative) so it can't drift — keep it that way. AD reads that same version off your /status JSON as liveVersion, so keep /status accurate.
  2. Build the zip with files at the ROOT (there is no zip binary — use Python zipfile): members = bridge.json, package.json, server.js, BRIDGE_VERSION, adom-favicon.png. Name it native-browser-bridge-v<ver>.zip (as of 0.1.29 — it always WAS a zip; the old .bin naming was a scar from the retired /files base64 API which rejected .zip. Release assets are extension-agnostic and AD only follows the manifest url, so no AD-side change was needed for the rename; old .bin releases stay valid at their existing URLs).
  3. The runtime is a RELEASE ASSET, not a /files blob (SDK "two-artifact" rule — fusion + pup do exactly this). Cut a Release for the bridge version + upload the zip:
    • adom-wiki release create adom/adom-browser-extension <ver> --title "native-browser bridge runtime v<ver>"
    • adom-wiki release upload adom/adom-browser-extension <ver> native-browser-bridge-v<ver>.zip --platform any The download then serves anon at https://wiki.adom.inc/download/adom/adom-browser-extension/<ver>/native-browser-bridge-v<ver>.zip (verify: curl -sI → 200). (The pkg's own release stays 0.3.x; the bridge release is 0.0.x — separate version streams on the same page, that's expected.)
  4. sha256 + byte size → write native-browser-bridge-manifest.json with the url pointing at that /download/ path (NOT a /files blob): {manifest_version:1, version, url:"https://wiki.adom.inc/download/adom/adom-browser-extension/<ver>/native-browser-bridge-v<ver>.zip", sha256, size, released_at}.
  5. Push manifest + source as TEXT with adom-wiki repo push adom-browser-extension --files native-browser-bridge-manifest.json bridge/bridge.json bridge/package.json bridge/server.js bridge/BRIDGE_VERSION ... (the manifest lives on /files; only the binary is off it).
  6. Land it: refresh_bridges {name:"native-browser"} → verify nbrowser_readiness routes and bridge_check_updates/bridge_list show liveVersion==<ver> (no staleProcess). (bridge_check_updates intermittently omits native-browser after an AD self-update — trust readiness + bridge_list.)
  7. NEVER repo rm a runtime zip while the manifest still points at it. The old /files .bin uploads are the dead anti-pattern (they piled up to 22 stale versions once) — do NOT re-add them. If any linger, migrate as a SEQUENCE: release-upload → repoint manifest → verify /download/ serves → THEN delete the /files copies. The pkg's package.json (the skills tarball, not the zip) declares dependencies:{"adom/adom-desktop":"^1.9.x"} (so pkg install pulls the AD CLI + core skills the container needs) — NOT the server's npm deps (the bridge has none anyway). The zip's bridge/package.json holds the server's runtime deps (none for us — core http/net).

Reaping is fixed (AD v1.9.76) — the old orphan trap is gone

Earlier, refresh_bridges could leave a stale PID serving old code (a node bridge doesn't lock the cache dir, so a clean swap didn't reap). AD v1.9.76 fixed this: refresh_bridges now force-restarts a running bridge after an update, restart_bridge {name} is a real verb (kill tracked+port-orphan + respawn on the stable port), and bridge_check_updates surfaces liveVersion + staleProcess directly. So: after a release, just refresh_bridges (or restart_bridge for an explicit restart) and confirm liveVersion matches — no more digging in host.json or manual taskkill. (bridge_pause/bridge_resume are a ROUTING gate only; they never kill the process.)

Publish the SKILLS pkg — USER skills ONLY (dev/publish live in the repo)

adom-wiki pkg publish --org adom. The pkg ships ONLY your USER skills — the ones a general Adom user's agent needs to DRIVE the bridge. That's the pkg's whole job: a container pkg installs it to USE you. Each is a real skills/<name>/SKILL.md with a Parent skill: first line; list every user-skill path EXPLICITLY (no globs) in package.json files[] AND install.sh/uninstall.sh.

  • DEV + PUBLISH skills do NOT go in the pkg — they live in your source REPO (this skill, and developing-the-bridge). A developer who clones the source to EDIT the bridge gets them as repo files; the normal user never needs them. So: not in files[], not in install.sh, and do NOT scope them with user-invocable:false — that convention still SHIPS them and bloats every container install. Keep them under dev-skills/ + publish-skills/ in the repo (the wiki Skills page reads those folders for its Dev/Publish tabs). Open-vs-closed source is irrelevant — it only sets whether the repo is publicly readable; either way dev/publish stay in the repo, never the pkg. (adom-desktop core does exactly this: its RELEASE/WIKI_PUBLISH/SIGNING skills live only in source, never in the published pkg.)
  • The pkg's package.json declares dependencies:{"adom/adom-desktop":"^1.9.x"}pkg install resolves it npm-style, so installing our skill pkg also pulls the AD CLI + core skills the container needs to drive us. (Do NOT put the SERVER's npm deps here — those ride in the zip's bridge/package.json.)

Discovery

Edit page.json discovery_triggers (30+ user-task phrasings, login/forms niche — pup can't do logins) + discovery_pitch (an INSTRUCTION). Push page.json LAST (a pkg publish clobbers it and drops the hero). Verify: adom-wiki discover preview --json "<7 real user queries>" → land top-3.

After ANY wiki change: show John in pup (standing rule)

Open the live page in a maximized pup window + shotlog + hand him the phone URL. Never just report "published."

After ANY release: prove the USER-VISIBLE result, unprompted (standing rule)

Verifying the pipeline is not verifying the feature. "The verb returned ok / the file exists / a test window rendered" is engineer-proof; the user cares what THEY will see. So after landing a release, automatically demonstrate the actual user-visible outcome on EVERY surface the change touches (e.g. a taskbar-badge change = show every browser+profile it will decorate, not one throwaway Notepad button) and put it in shotlog with the phone URL - before the user has to ask. If a live demo would touch something sensitive (a banking window, a signed-in session), don't skip the demo - build the same proof a safe way (generate from the real inputs off to the side) and say why. The user should never have to say "prove it."