← Commit history

THE RENAME: SDK moves to adom-bridge-sdk; host app is Adom Bridge; new <name>-bridge page convention

John Lauer ·b8e807e928 ·3mo ago ·parent 0fcda26
3 files changed +156−156
README.md+52−52
@@ -1,9 +1,9 @@ ----name: adom-desktop-bridge-sdk-description: Author, PUBLISH, and make-discoverable an adom-desktop bridge — a small local server that adds a namespace of verbs (kicad_, fusion_, browser_, your_prefix_) to adom-desktop. Use when building, publishing, updating, prewarming, or debugging a bridge, or deciding what skills to ship. Covers the AD-core-vs-author boundary; the #1 rule (rich hints in EVERY verb's OUTPUT, because the AI reads CLI output, never your on-disk skill); bridge.json; where every file lives (the Release-zip-vs-skills-pkg split, the TWO package.json files [server deps in the zip; the pkg declares dependencies:{adom/adom-desktop}], manifest + runtime-asset placement); the pkg carries ONLY user skills while dev + publish skills live in your source repo (not the pkg); the THREE things you publish (runtime Release zip + manifest, container skills pkg, wiki discovery_triggers); prewarm; and the auto-discover → install-check → use flow.+name: adom-bridge-bridge-sdk+description: Author, PUBLISH, and make-discoverable an adom-bridge bridge — a small local server that adds a namespace of verbs (kicad_, fusion_, browser_, your_prefix_) to adom-bridge. Use when building, publishing, updating, prewarming, or debugging a bridge, or deciding what skills to ship. Covers the AD-core-vs-author boundary; the #1 rule (rich hints in EVERY verb's OUTPUT, because the AI reads CLI output, never your on-disk skill); bridge.json; where every file lives (the Release-zip-vs-skills-pkg split, the TWO package.json files [server deps in the zip; the pkg declares dependencies:{adom/adom-bridge}], manifest + runtime-asset placement); the pkg carries ONLY user skills while dev + publish skills live in your source repo (not the pkg); the THREE things you publish (runtime Release zip + manifest, container skills pkg, wiki discovery_triggers); prewarm; and the auto-discover → install-check → use flow. --- -# Bridge SDK — build, publish, and surface a bridge for Adom Desktop+# Bridge SDK — build, publish, and surface a bridge for Adom Bridge  A **bridge** is a small local program (Python, Node, Rust, anything) that AD spawns and routes a **namespace of verbs** to over HTTP. KiCad, Fusion 360, Puppeteer (pup), native-browser, Blender are bridges. The modern flow has THREE moving parts you own — get all three right and a cloud/WSL2 container can *discover* your bridge from a plain-English request, confirm it's installed, and drive it with zero tribal knowledge: @@ -29,9 +29,9 @@ A **bridge** is a small local program (Python, Node, Rust, anything) that AD spa | **Installed by** | `bridge_install {manifestUrl}` → AD streams, unzips, spawns | `pkg install adom/<slug>` / `sync_skills` → `~/.claude/skills` + `~/.codex/skills` | | **Contains** | server code + `bridge.json` at the zip **ROOT** + assets. **Node: SOURCE ONLY, no `node_modules`.** | ONLY: ROOT `SKILL.md`, `skills/<name>/SKILL.md` (user skills), `package.json`, `install.sh`, `uninstall.sh`. **Text only — the whole tarball should be a few KB.** | | **NEVER contains** | the skills pkg | **ANY image (hero / screenshots — those are WIKI PAGE assets, see below), any binary, the Release zip, `src/`, `node_modules`, dev/publish skills, or the server's npm deps.** If `tar tzf` shows a `.png`/`.jpg`/`.zip`/`.exe`, you bundled bloat — remove it. |-| **its `package.json`** | (node) the **bridge SERVER's runtime deps** AD installs at spawn: `dependencies:{ puppeteer, … }` | the **pkg manifest**: `files[]` (user skills), `discovery_triggers`, scripts, **AND `dependencies:{"adom/adom-desktop":"^1.9.x"}`** (the container needs the AD CLI + core skills to drive you) — but NOT the server's npm deps |+| **its `package.json`** | (node) the **bridge SERVER's runtime deps** AD installs at spawn: `dependencies:{ puppeteer, … }` | the **pkg manifest**: `files[]` (user skills), `discovery_triggers`, scripts, **AND `dependencies:{"adom/adom-bridge":"^1.9.x"}`** (the container needs the AD CLI + core skills to drive you) — but NOT the server's npm deps | -**Yes, a node bridge has TWO `package.json` files and they are NOT interchangeable.** One rides inside the Release zip and lists the **bridge server's** runtime deps (puppeteer, sharp, ...) that AD installs on the *desktop* at spawn. The other is the **pkg manifest** in the skills tarball: it lists your user-skill `files[]` + `discovery_triggers` AND declares **`dependencies:{"adom/adom-desktop":"^1.9.x"}`** — `pkg install` resolves that npm-style, so installing your bridge's skill pkg also pulls the adom-desktop CLI + core skills the container needs to drive you. What the pkg manifest must NOT list is the **server's** npm deps (those are desktop-side, in the zip) — put them there and you bloat every container install, since the container never runs your server.+**Yes, a node bridge has TWO `package.json` files and they are NOT interchangeable.** One rides inside the Release zip and lists the **bridge server's** runtime deps (puppeteer, sharp, ...) that AD installs on the *desktop* at spawn. The other is the **pkg manifest** in the skills tarball: it lists your user-skill `files[]` + `discovery_triggers` AND declares **`dependencies:{"adom/adom-bridge":"^1.9.x"}`** — `pkg install` resolves that npm-style, so installing your bridge's skill pkg also pulls the adom-bridge CLI + core skills the container needs to drive you. What the pkg manifest must NOT list is the **server's** npm deps (those are desktop-side, in the zip) — put them there and you bloat every container install, since the container never runs your server.  **Where each file physically lives (this is the map that ends the inconsistency):** - **`bridge.json`** → the **root of the Release zip** (AD reads it post-unzip). Carries `version` + `updateManifestUrl`.@@ -48,7 +48,7 @@ A **bridge** is a small local program (Python, Node, Rust, anything) that AD spa | **DEV** | a maintainer editing the bridge | your **source repo** | **NO, never** | only if your repo is public | | **PUBLISH** | a maintainer releasing the bridge | your **source repo** | **NO, never** | only if your repo is public | -**The one rule:** the pkg tarball carries **USER skills only**; **DEV + PUBLISH skills live in the source repo.** Do NOT "scope" dev/publish into the pkg with `user-invocable:false` — that still ships them (pure bloat). Open-vs-closed source only sets your repo's *visibility*; it never changes what's in the pkg. adom-desktop core does exactly this: user/integration skills in the pkg; `RELEASE` / `WIKI_PUBLISH` / `SIGNING` / `AD_RELAUNCH` live only in the source repo.+**The one rule:** the pkg tarball carries **USER skills only**; **DEV + PUBLISH skills live in the source repo.** Do NOT "scope" dev/publish into the pkg with `user-invocable:false` — that still ships them (pure bloat). Open-vs-closed source only sets your repo's *visibility*; it never changes what's in the pkg. adom-bridge core does exactly this: user/integration skills in the pkg; `RELEASE` / `WIKI_PUBLISH` / `SIGNING` / `AD_RELAUNCH` live only in the source repo.  --- @@ -84,10 +84,10 @@ curl -s -X POST "$ADOM_DIRECT_API_URL/command" -H 'Content-Type: application/jso  ### Notify the user AND get their answer back (`notify_user` — two separate axes) -Your bridge can prompt the user with a native toast and read their reply — the same `notify_user` verb the AI uses. It has **two independent axes; don't conflate them** (full ref: the `adom-desktop-notify` skill / `skills/NOTIFY.md`):+Your bridge can prompt the user with a native toast and read their reply — the same `notify_user` verb the AI uses. It has **two independent axes; don't conflate them** (full ref: the `adom-bridge-notify` skill / `skills/NOTIFY.md`):  - **Axis 1 — get the answer back.** There is no server-push, so either **`wait:true`** (the `notify_user` call blocks and returns the user's choice inline — best for a one-shot prompt) or fire-and-continue then poll **`notify_events {sinceSeq}`** (drains every click) / **`notify_response {id}`**. The click is recorded durably, so it survives even on a headless box.-- **Axis 2 — reveal a window (`focus`).** A LOCAL desktop action, not a reply. `focus:{hwnd|cacheKey|titleContains|app|host}` raises a window on the user's screen when they click. **`focus:{app:"<your-exe>"}`** points the click at a window YOUR bridge drives; **`focus:{host:true}`** points it at the app AD is embedded under (Adom Hydrogen). Pass `focus` whenever the toast is *about* a window, so the click lands there instead of opening AD.+- **Axis 2 — reveal a window (`focus`).** A LOCAL desktop action, not a reply. `focus:{hwnd|cacheKey|titleContains|app|host}` raises a window on the user's screen when they click. **`focus:{app:"<your-exe>"}`** points the click at a window YOUR bridge drives; **`focus:{host:true}`** points it at the app AD is embedded under (Hydrogen Desktop). Pass `focus` whenever the toast is *about* a window, so the click lands there instead of opening AD.  ```bash # Ask, and get the answer back inline:@@ -109,7 +109,7 @@ A user runs roughly **20 AI threads against one desktop**. Until v1.9.180 AD rel - No bridge log could **attribute a verb** to the thread that issued it, so interference was impossible to reconstruct afterwards. - Any agent's `bridge_kill` **destroyed every other agent's work**, silently, with no way for anyone to see who did it. -Filed as wiki issue `adom/adom-desktop#34` by AdityaAngajala, with the pup half at `adom/adom-desktop-puppeteer-bridge#22`.+Filed as wiki issue `adom/adom-bridge#34` by AdityaAngajala, with the pup half at `adom/adom-desktop-puppeteer-bridge#22`.  ### What you now receive, for free @@ -127,7 +127,7 @@ AD stamps three headers on **every relayed bridge request** — all four request  These values are **SELF-ASSERTED**, exactly like `reason`. Use them for **attribution, logging, UX and arbitration hints**. **NEVER for authorization, and never as a security boundary.** -AD's own security model is transport-based (the `adom-desktop-permissions` skill has the full model): a caller is trusted because of *how it reached AD*, not because of what it *says about itself*. A header a caller supplies about itself cannot gate anything — if you write `if (thread === "admin-tab") allowDangerousThing()`, you have written a hole, not a check.+AD's own security model is transport-based (the `adom-bridge-permissions` skill has the full model): a caller is trusted because of *how it reached AD*, not because of what it *says about itself*. A header a caller supplies about itself cannot gate anything — if you write `if (thread === "admin-tab") allowDangerousThing()`, you have written a hole, not a check.  One asymmetry worth knowing: the container name in `X-Adom-Caller-Container` is the one the **caller claimed**. AD separately knows a **verified** container (the authenticated relay connection name) and shows the user **both** when they disagree. You only get the claimed one, so treat it as a label, not an identity. @@ -177,7 +177,7 @@ The recommended posture: - **Refuse only for destructive operations** — closing, resetting, navigating away, deleting. - **Always name the current owner in the message.** The whole point is that the second agent can now go coordinate with a thread it can identify. An error that says "session busy" without a name is barely better than no header at all. -**3. Label the UI.** If your bridge drives a visible window, put the owning thread where the **user** can see it — window title, taskbar tooltip, or jump-list name — via `desktop_set_window_identity` (and `desktop_taskbar`'s `thumbnailTooltip` for uncapped hover text). The user is the one with 20 tabs open; showing which tab owns which window is the fix for their side of the problem. Full guide: the `adom-desktop-window-identity` skill, and the "Branding + driving a window you borrowed" section immediately below.+**3. Label the UI.** If your bridge drives a visible window, put the owning thread where the **user** can see it — window title, taskbar tooltip, or jump-list name — via `desktop_set_window_identity` (and `desktop_taskbar`'s `thumbnailTooltip` for uncapped hover text). The user is the one with 20 tabs open; showing which tab owns which window is the fix for their side of the problem. Full guide: the `adom-bridge-window-identity` skill, and the "Branding + driving a window you borrowed" section immediately below.  **4. Report it back.** Include the owning thread in your **status / list verbs**, so an agent can *discover* it is not alone before it stomps on something: @@ -242,14 +242,14 @@ Useful to understand, though none of it is your responsibility:  ```bash # Per call — the CORRECT mechanism:-adom-desktop --ai-thread "chip-fetcher tab 3" <verb> '<json>'+adom-bridge-cli --ai-thread "chip-fetcher tab 3" <verb> '<json>' ``` -The `adom-desktop` CLI attaches a `caller` block to **every** verb automatically. A relay call with **no `aiThread` is REFUSED by AD** with `errorCode: caller_identity_required` before it ever reaches a bridge.+The `adom-bridge` CLI attaches a `caller` block to **every** verb automatically. A relay call with **no `aiThread` is REFUSED by AD** with `errorCode: caller_identity_required` before it ever reaches a bridge.  > **Correction, 2026-07-25 (AD 1.9.182):** an earlier version of this page told callers to `export ADOM_AI_THREAD` once per session. **That advice was wrong and has been withdrawn.** Roughly 20 AI threads share one container and one `$HOME`, so the env var scopes to the shell environment, not to the thread — every thread then reports the *same* name, which is worse than no name at all because it is confident and plausible. It also failed silently: tool shells are non-interactive, so an export appended below `~/.bashrc`'s `case $- in *i*` early-return never ran. The env var is still honored, but only fits a container running exactly one agent (CI, a single-purpose job). **If your bridge's own docs repeat the export advice, please correct them.** -**So by the time a request reaches you, identity is normally present.** The exception is **loopback callers** — Hydrogen, another bridge, or the local CLI — which are trusted by transport and not yet *required* to identify (that gate tightens once bridges adopt the forwarding contract above). Code for the absent case (`|| 'unidentified'`), never assume the header exists.+**So by the time a request reaches you, identity is normally present.** The exception is **loopback callers** — HD, another bridge, or the local CLI — which are trusted by transport and not yet *required* to identify (that gate tightens once bridges adopt the forwarding contract above). Code for the absent case (`|| 'unidentified'`), never assume the header exists.  ### If your bridge wants to REQUIRE identity — use the shared contract @@ -259,7 +259,7 @@ Some verbs are worth refusing anonymously. If you do, **use AD's exact refusal s { "success": false, "errorCode": "caller_identity_required",   "error": "'<verb>' needs to know WHO is asking — the user runs many AI threads and needs to see which one drove this.",   "supplyIdentity": {-    "bestWay": "adom-desktop --ai-thread \"<your conversation name>\" <verb> '<json>'",+    "bestWay": "adom-bridge-cli --ai-thread \"<your conversation name>\" <verb> '<json>'",     "orInArgs": { "caller": { "aiThread": "<name>", "containerName": "<container>" } }   },   "_hint": "This is attribution, not a permission check — the name is self-reported and nothing is gated on it." }@@ -283,7 +283,7 @@ Reusing `caller_identity_required` verbatim is the point: an AI that learned the  ## Branding + driving a window you borrowed (window identity + taskbar, v1.9.106–1.9.153) -If your bridge drives a window another process owns — the classic case is a **Chrome for Testing** window that otherwise reads *"Test"* with Chrome's icon — AD gives you a generic verb family to make it present as YOUR app across every Windows shell surface, and to show live status on its taskbar button. AD stays generic: it applies whatever name/icon/command you pass; it knows nothing about your app. **Full guide: the `adom-desktop-window-identity` skill** (`https://wiki.adom.inc/api/v1/pages/adom-desktop/files/docs/WINDOW_IDENTITY.md` or the repo `skills/WINDOW_IDENTITY.md`). The essentials:+If your bridge drives a window another process owns — the classic case is a **Chrome for Testing** window that otherwise reads *"Test"* with Chrome's icon — AD gives you a generic verb family to make it present as YOUR app across every Windows shell surface, and to show live status on its taskbar button. AD stays generic: it applies whatever name/icon/command you pass; it knows nothing about your app. **Full guide: the `adom-bridge-window-identity` skill** (`https://wiki.adom.inc/api/v1/pages/adom-bridge/files/docs/WINDOW_IDENTITY.md` or the repo `skills/WINDOW_IDENTITY.md`). The essentials:  - **Two halves, you need both:** `desktop_register_app_identity {appId, displayName, iconPath, shortcut}` teaches Windows what an AUMID means (HKCU only, no UAC; `shortcut:false` for a per-session id with no Start Menu spam), and `desktop_set_window_identity {hwnd, appId, iconPath, displayName, relaunchCommand}` stamps it on the live window. Stamp without register → Alt-Tab / taskbar fall back to the owning exe's icon. - **Per-session identity:** register `shortcut:false` per window (e.g. `Adom.Pup.<sessionId>`) so each keeps its own taskbar button; `desktop_unregister_app_identity {appId}` on close.@@ -317,7 +317,7 @@ If your bridge drives a window another process owns — the classic case is a ** This SDK evolves. When it does, AD posts a re-audit notice to **YOUR bridge's wiki discussion** and re-publishes this billboard. You do NOT need a human to hand you a prompt — self-serve at the start of a work session:  1. **Check your discussion for a notice.** `adom-wiki discussion list <your-slug>` (or open your page's Discussions tab) and read any "SDK update" / "re-audit" thread. A notice can be EDITED after posting (a correction), so always read the **current** body, not a cached earlier version.-2. **Re-read this billboard**, especially "Where every file lives" (above) and the "Self-audit checklist" (below): `https://wiki.adom.inc/adom/adom-desktop-bridges` (or `curl -s https://wiki.adom.inc/api/v1/pages/adom-desktop-bridges/files/README.md`).+2. **Re-read this billboard**, especially "Where every file lives" (above) and the "Self-audit checklist" (below): `https://wiki.adom.inc/adom/adom-bridge-sdk` (or `curl -s https://wiki.adom.inc/api/v1/pages/adom-bridge-sdk/files/README.md`). 3. **Run the Self-audit checklist** (next section) against your code, `bridge.json`, skills, README, and wiki page. Fix every miss. 4. **Reply on the discussion** with what you changed or any blockers. That thread is the ONLY channel AD uses to reach you, so closing the loop there is how AD knows you are current. @@ -338,7 +338,7 @@ So make the invitation part of your verb output, the same way `_hint` is. This i   "success": false,   "errorCode": "…",   "_hint": "…",-  "_reportIssues": "THIS MAY BE A <BRIDGE> BUG. PLEASE REPORT IT. Check for a duplicate with `adom-wiki issue list adom/<your-slug>`, then file: `adom-wiki issue create adom/<your-slug> --title \"<one-line symptom>\" --category bug-report --body \"<what you ran, what you expected, what happened, the VERBATIM response JSON, the bridge version from bridge_list, and the OS>\"`. Reports from real machines are the only way this bridge gets correct. (An AD-core problem, such as the relay, bridge lifecycle or caller identity, belongs on adom/adom-desktop instead.)"+  "_reportIssues": "THIS MAY BE A <BRIDGE> BUG. PLEASE REPORT IT. Check for a duplicate with `adom-wiki issue list adom/<your-slug>`, then file: `adom-wiki issue create adom/<your-slug> --title \"<one-line symptom>\" --category bug-report --body \"<what you ran, what you expected, what happened, the VERBATIM response JSON, the bridge version from bridge_list, and the OS>\"`. Reports from real machines are the only way this bridge gets correct. (An AD-core problem, such as the relay, bridge lifecycle or caller identity, belongs on adom/adom-bridge instead.)" } ``` @@ -354,7 +354,7 @@ A line on **every** response is noise that trains the caller to ignore the field  **Make it actionable, not a slogan.** "Report bugs" produces nothing. Give the AI the exact `adom-wiki issue create` command, your slug, a `--category`, and the list of evidence to include (the verbatim response JSON and your bridge version are the two that make a report reproducible). Tell it to check for a duplicate first: an `issue list` call costs nothing and keeps your page clean. -**Route the report to the repo that owns the code.** Say explicitly, in the same string, that AD-core problems (the relay and `/command` passthrough, spawn and provisioning, bridge lifecycle, caller identity) go to **`adom/adom-desktop`**, not to your page. Authors get this wrong in the other direction too, which is the next section: a misfiled issue is a note to yourself that the people who can fix it never read.+**Route the report to the repo that owns the code.** Say explicitly, in the same string, that AD-core problems (the relay and `/command` passthrough, spawn and provisioning, bridge lifecycle, caller identity) go to **`adom/adom-bridge`**, not to your page. Authors get this wrong in the other direction too, which is the next section: a misfiled issue is a note to yourself that the people who can fix it never read.  **Let the user turn it off.** Ship a settings toggle (pup uses `browser_configure {reportIssues:"off"}`) so a user who finds it noisy can silence it without losing functionality. Default it **on**: the cost is a few tokens on failures, and the benefit is the bug class you would otherwise ship to every user. @@ -369,14 +369,14 @@ You WILL find AD-core bugs and gaps while building your bridge. That is expected **The rule: an issue lives on the issue list of the repo that OWNS the code that must change.**  - A bug in YOUR bridge's verb / hint / self-heal / status chip goes to **YOUR page's issue list** (or you just fix it, since you own it).-- A missing or broken **AD-CORE** capability (a lifecycle verb, a spawn / provision / bind behavior, the relay passthrough dropping a field, a manifest field AD must honor, the status-chip contract) goes to **`adom/adom-desktop`**, NOT your own page. AD-core's maintainers and AI watch `adom/adom-desktop`; they do NOT read your bridge page's issues. **An AD ask filed on your own page is a note to yourself.**+- A missing or broken **AD-CORE** capability (a lifecycle verb, a spawn / provision / bind behavior, the relay passthrough dropping a field, a manifest field AD must honor, the status-chip contract) goes to **`adom/adom-bridge`**, NOT your own page. AD-core's maintainers and AI watch `adom/adom-bridge`; they do NOT read your bridge page's issues. **An AD ask filed on your own page is a note to yourself.** - A **wiki or CLI** bug goes to `adom/wiki`. A **sibling bridge's** bug goes to that bridge's page.  **How (the CLI):**  ```bash-adom-wiki issue list   adom/adom-desktop            # FIRST: skim for a dup, comment if one exists-adom-wiki issue create adom/adom-desktop \+adom-wiki issue list   adom/adom-bridge            # FIRST: skim for a dup, comment if one exists+adom-wiki issue create adom/adom-bridge \   --title "<verb or subsystem>: <the gap in one line>" \   --category bug-report \   --body "$(cat your-writeup.md)"@@ -386,7 +386,7 @@ adom-wiki issue create adom/adom-desktop \  **After you file:** the AD-core thread replies on that issue. Read the reply, it may change your plan (for example "fixed in AD X.Y, bump your pin"). That issue thread is the cross-repo channel; keep the loop closed there. -**Why this matters most for AD:** AD-core spawns you, provisions your runtime, relays your verbs, and installs you, so AD is the repo you will file against most often. Get the destination right the first time. The established precedent all sits on `adom/adom-desktop`: #26 and #27 (pup to AD, Chrome for Testing), #16 (pup to AD, window-verb interception), #3 / #20 / #21 (Bridge SDK asks).+**Why this matters most for AD:** AD-core spawns you, provisions your runtime, relays your verbs, and installs you, so AD is the repo you will file against most often. Get the destination right the first time. The established precedent all sits on `adom/adom-bridge`: #26 and #27 (pup to AD, Chrome for Testing), #16 (pup to AD, window-verb interception), #3 / #20 / #21 (Bridge SDK asks).  --- @@ -402,7 +402,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against  **Artifacts & deps** (the two-artifact split — the #1 inconsistency) *(→ Where every file lives)*: - [ ] Your Release `.zip` is a **Release asset** (`release upload`), NOT a loose `/files` blob — and old version zips are NOT piling up on `/files` (audit for stray `*-v*.zip`).-- [ ] The **server's** runtime deps live in the **ZIP's** `package.json` (node) / `requirements.txt` (python). The **pkg's** `package.json` declares `dependencies:{"adom/adom-desktop":"^1.9.x"}` (so `pkg install` pulls the AD CLI + core skills) and does NOT list the server's npm deps.+- [ ] The **server's** runtime deps live in the **ZIP's** `package.json` (node) / `requirements.txt` (python). The **pkg's** `package.json` declares `dependencies:{"adom/adom-bridge":"^1.9.x"}` (so `pkg install` pulls the AD CLI + core skills) and does NOT list the server's npm deps. - [ ] A big runtime asset (Mesa, a browser) is a **`prewarm.asset`** or seed-bundled — NOT a loose `/files` blob. - [ ] Your **manifest** (`adom-bridge-<name>-manifest.json`) is on `/files`; its `url` points at the **Release download**, not at a `/files` blob. - [ ] **PAYLOAD:** `unzip -l <your zip> | sort -k1 -n -r | head` shows **runtime only** — no `.mp4`/`.png`/`.jpg`/screenshots/CHANGELOG/`.github`. A runtime zip is normally well under 1 MB; if yours is bigger, justify every large entry. `tar tzf <your tgz>` shows **skills only** — zero media, zero binaries. Media belongs on the PAGE. *(→ Payload rules)*@@ -427,7 +427,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against **Skills & docs** (what the container + humans read): - [ ] Your pkg ships **ONLY your USER skill(s)** — a real `skills/<name>/SKILL.md` (core user skill + user sub-skills) with a `Parent skill:` first body line; every path listed EXPLICITLY (no globs) in `files[]` + install.sh/uninstall.sh. *(→ Artifact 2 + The SKILL SET)* - [ ] **DEV + PUBLISH skills are in your source REPO, NOT the pkg** — not in `files[]`, not scoped in via `user-invocable:false`. A developer gets them by cloning the source; the normal user never needs them. Open-vs-closed source is irrelevant (it only sets repo visibility). *(→ Where every file lives)*-- [ ] Your pkg's `package.json` declares `dependencies:{"adom/adom-desktop":"^1.9.x"}` so `pkg install` pulls the AD CLI + core skills the container needs. *(→ Where every file lives)*+- [ ] Your pkg's `package.json` declares `dependencies:{"adom/adom-bridge":"^1.9.x"}` so `pkg install` pulls the AD CLI + core skills the container needs. *(→ Where every file lives)* - [ ] Your USER skill leads with the cold-start "read before you panic" section + the 8 conventions, and points the AI at `bridge_readiness`. *(→ USER-skill conventions)* - [ ] README + wiki page + skills all AGREE with the above (no stale github links, no wrong schema, triggers vetted). @@ -444,7 +444,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against   "description": "Drive Blender headless + GUI",   "author": "you",   "license": "MIT",-  "docs": "https://wiki.adom.inc/adom/adom-desktop-blender-bridge",  // your OWN page (drives skillPkg + hero)+  "docs": "https://wiki.adom.inc/adom/adom-bridge-blender-bridge",  // your OWN page (drives skillPkg + hero)   "hero": "https://.../hero.png",           // optional; else AD uses your wiki page hero, else a monogram. Ship it at 1.6 aspect (≈2000×1250) — see "Card hero + update cost"   "spawn": {     "kind": "python",                       // python | node | exe | external-http  (NOT "process")@@ -458,7 +458,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against   "verbPrefixes": ["blender_"],             // any verb with these prefixes routes to you   "verbs": ["blender_render", "blender_status", "blender_describe"],  // every verb (routing + Verbs tab)   "statusVerb": "blender_status",           // verb a caller polls after a long op times out-  "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-desktop-blender-bridge/files/blender-manifest.json",+  "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-bridge-blender-bridge/files/blender-manifest.json",    "timeouts": {                             // v1.9.79 — per-verb HTTP budgets (seconds); see note below     "default": 60,                          //   fallback for any verb not matched (absent → AD's global 60s)@@ -481,7 +481,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against - **`spawn.healthEndpoint`** lives **inside `spawn`** (not at the top level), default `/health`. - **`updateManifestUrl`** — the full URL of YOUR manifest on YOUR page. **Declare it in `bridge.json`** so it survives a republish; AD polls it (on launch + every 4h) and auto-updates users. (AD also remembers the install URL in `~/.adom/bridge-install-urls.json` as a fallback, but the declared field is the durable, author-intended path.) - **`docs`** — point at YOUR dedicated wiki page. AD derives your **skillPkg** (`adom/<slug>`) and your card hero from this. Don't point it at a shared/structural page.-- **`homepage`** — a **`wiki.adom.inc`** URL (your own page, or `https://wiki.adom.inc/adom/adom-desktop` for the platform). **NEVER `github.com/...`** — GitHub is source-backup only; the wiki is the canonical home. (A `homepage: github.com/adom-inc/adom-desktop` is a stale default to fix.)+- **`homepage`** — a **`wiki.adom.inc`** URL (your own page, or `https://wiki.adom.inc/adom/adom-bridge` for the platform). **NEVER `github.com/...`** — GitHub is source-backup only; the wiki is the canonical home. (A `homepage: github.com/adom-inc/adom-bridge` is a stale default to fix.) - **`detect` / `prewarm`** — the v1.9.47 cold-start declarations (full rules in "Cold-start tiers" below). `detect` = your HOST APP (AD detects, never installs); `prewarm` = your heavy RUNTIME assets (AD prewarms/auto-installs). A node bridge that ships its deps still works without `prewarm`; declare `prewarm.assets` only for big *downloaded* assets like a browser. - **`timeouts`** (v1.9.79) — per-verb HTTP budgets (seconds) AD honors so a SLOW verb (a big export, a cloud open, a long render) isn't cut off before it finishes. Resolution: exact `verbs` (BARE names, no prefix) → first matching `prefixes` → `default` → AD's global 60s. A timeout is **NON-terminal**: AD returns `{stillRunning:true, statusVerb, timeoutSeconds}` and the caller polls your `statusVerb` (so ALSO declare `statusVerb`). **⚠ Before v1.9.79 a third-party bridge was hard-capped at 60s with no way to raise it** — if any of your verbs can run longer, declare `timeouts` (this is the fix). AD reads it with ZERO AD change; the two bundled EDA bridges keep a labeled built-in table only until they declare their own. @@ -493,19 +493,19 @@ This is the code AD streams onto the user's machine. Two files on your page:  1. **A versioned Release `.zip`** of your bridge dir (server + `bridge.json` at the zip ROOT + any assets). Ship it as a **Release** (the downloadable, version-pinned artifact), not a loose `/files` blob:    ```bash-   adom-wiki release create adom/adom-desktop-<bridge>-bridge <version> --title "..." --changelog "..."-   adom-wiki release upload adom/adom-desktop-<bridge>-bridge <version> your-bridge-v<version>.zip --platform any+   adom-wiki release create adom/adom-bridge-<bridge>-bridge <version> --title "..." --changelog "..."+   adom-wiki release upload adom/adom-bridge-<bridge>-bridge <version> your-bridge-v<version>.zip --platform any    ```    (Node bridges: the zip carries SOURCE only — keep `node_modules` out; AD/the seed handle deps. See Prewarm.) 2. **The manifest JSON** (e.g. `blender-manifest.json`) — identity + where the zip is — POSTed to your page `/files`:    ```jsonc    { "name":"blender", "version":"1.2.0",-     "url":"https://wiki.adom.inc/download/adom/adom-desktop-blender-bridge/1.2.0/blender-v1.2.0.zip",  // base-relative, extension-agnostic (.bin works where wiki blocks .zip)+     "url":"https://wiki.adom.inc/download/adom/adom-bridge-blender-bridge/1.2.0/blender-v1.2.0.zip",  // base-relative, extension-agnostic (.bin works where wiki blocks .zip)      "sha256":"…", "size":123456, "verbPrefixes":["blender_"], "healthEndpoint":"/health",-     "updateManifestUrl":"https://wiki.adom.inc/api/v1/pages/adom-desktop-blender-bridge/files/blender-manifest.json" }+     "updateManifestUrl":"https://wiki.adom.inc/api/v1/pages/adom-bridge-blender-bridge/files/blender-manifest.json" }    ``` -**Users install/update** with AD's `bridge_install {manifestUrl}` → AD downloads the zip, sha256-verifies, unzips into `%LOCALAPPDATA%\Adom Desktop\bridges-cache\<name>\`, rescans, and remembers the URL for auto-update.+**Users install/update** with AD's `bridge_install {manifestUrl}` → AD downloads the zip, sha256-verifies, unzips into `%LOCALAPPDATA%\Adom Bridge\bridges-cache\<name>\`, rescans, and remembers the URL for auto-update.  **To ship a new version:** bump `version` in BOTH `bridge.json` (in the zip) and the manifest, re-`release upload` the zip, re-POST the manifest. Users auto-get it on the next 4h poll, or immediately via `refresh_bridges {name}`. **A bundled SEED** (a `plugins/<name>/` folder shipped in AD's NSIS) is the offline/first-run fallback; your published cache copy with a higher `version` supersedes it (cache-over-bundled, version-aware). ⚠ **A `release upload` REPLACES a same-platform asset ONLY when the FILENAME is IDENTICAL** — a different name (a version bump `-v1.2.0` → `-v1.3.0`) ADDS a second asset, leaving a stale zip on the release. Keep ONE asset per release: overwrite the same filename, or `adom-wiki release delete-asset <owner/slug> <ver> "<old-filename>"` after a rename. (A stale extra won't break the manifest-URL install, but it clutters the release.) @@ -519,14 +519,14 @@ Your runtime runs on the user's DESKTOP; the AI driving it runs in a cloud/WSL2  - **The pkg carries ONLY your USER skill(s).** Each is a real `skills/<name>/SKILL.md`. A ROOT `SKILL.md` (required by the registry) is the pack entry point: `user-invocable: true`, a `description` full of trigger words. Large surface → a core user skill + user sub-skills, but **every skill in the pkg is a *user* skill.** - **DEV + PUBLISH skills do NOT go in the pkg — they live in your source repo** (a developer gets them by cloning to edit the bridge). Do NOT list them in `files[]`, and do NOT "scope" them in with `user-invocable:false` (that still ships them and bloats every container install).-- **Declare `dependencies:{"adom/adom-desktop":"^1.9.x"}`** in `package.json` — `pkg install` resolves it npm-style, so installing your bridge's skill pkg also pulls the AD CLI + core skills the container needs to drive you.-- **Each sub-skill's body opens with a `Parent skill:` line** (first line after the frontmatter): `Parent skill: **adom-desktop-<bridge>-bridge**`.+- **Declare `dependencies:{"adom/adom-bridge":"^1.9.x"}`** in `package.json` — `pkg install` resolves it npm-style, so installing your bridge's skill pkg also pulls the AD CLI + core skills the container needs to drive you.+- **Each sub-skill's body opens with a `Parent skill:` line** (first line after the frontmatter): `Parent skill: **adom-bridge-<bridge>-bridge**`. - **List every user-skill path EXPLICITLY — no globs — in `package.json` `files[]` AND `install.sh`/`uninstall.sh`** (one `install_skill <dest> <src>` line each, kept in sync). The publish linter REQUIRES explicit listing. (The old "use `skills/**`" advice is dead.)  ```bash # Stage dir: ROOT SKILL.md + skills/<name>/SKILL.md per USER skill (dev/publish stay in the repo),-# package.json {name:"adom-desktop-<bridge>-bridge", version, discovery_triggers,-#               dependencies:{"adom/adom-desktop":"^1.9.x"},+# package.json {name:"adom-bridge-<bridge>-bridge", version, discovery_triggers,+#               dependencies:{"adom/adom-bridge":"^1.9.x"}, #               files:[ every USER-skill path, explicitly ], scripts:{install,uninstall}}. # TWO traps that still bite — verify each with `tar tzf` before publishing: #  1. ⚠ pkg pack/publish HONORS the stage dir's .gitignore — a gitignored skills/ ships@@ -535,14 +535,14 @@ Your runtime runs on the user's DESKTOP; the AI driving it runs in a cloud/WSL2 #     before guards). Skills + docs only. adom-wiki pkg pack                       # then: tar tzf <tgz> → every skills/<name>/SKILL.md                                          #   listed in files[] present, ZERO binaries-adom-wiki pkg publish --org adom         # publishes adom/adom-desktop-<bridge>-bridge+adom-wiki pkg publish --org adom         # publishes adom/adom-bridge-<bridge>-bridge ```  **`skill_count` on your page counts the USER skills your published PKG carries** — so it should be SMALL (1 for a single-skill bridge, a handful for a core + sub-skills). If it looks inflated, you probably shipped dev/publish skills that belong in the repo instead. The metric is NOT "SKILL.md files in the repo," so a `type:skill` page can read `skill_count: 0` while its repo holds many — don't chase parity across page types (wiki-side quirk).  A container gets it **without anyone asking**: - `bridge_install` returns `skillPkg` + `installSkill` (AD derives `adom/<slug>` from your `docs`) and the `_hint` tells the AI to run it.-- **`adom-desktop sync_skills`** installs every connected desktop's bridge skillPkgs (the CLI also runs it once/day in the background), so a container that connects a week later self-heals your skill.+- **`adom-bridge-cli sync_skills`** installs every connected desktop's bridge skillPkgs (the CLI also runs it once/day in the background), so a container that connects a week later self-heals your skill. - The wiki page's **Skills tab** lists the skills your pkg ships, so the page itself advertises them.  **Install to BOTH agent homes.** A container may run Claude OR Codex — your pkg's `install.sh` must deploy the user skill to **`~/.claude/skills/`** AND **`~/.codex/skills/`** (Codex is a real second skills home now). `adom-wiki pkg update` keeps both fresh.@@ -553,7 +553,7 @@ Your page carries **two artifacts that look interchangeable and are not**, and u  **So state it, in ONE plain sentence, in both your package `description` and your README.** Keep this line, or something equivalent: -> This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Desktop loads the bridge runtime itself from the release zip.+> This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Bridge loads the bridge runtime itself from the release zip.  **Keep it to one sentence.** The package `description` renders as your page subtitle AND the text under the Install card, so a multi-paragraph architecture essay there actively hurts: it buries the one fact a visitor needs. (AD-core overshot exactly this and had to trim it back.) The long version belongs in your README body, not the card. @@ -565,7 +565,7 @@ Your page carries **two artifacts that look interchangeable and are not**, and u  This is the single easiest way to hurt every one of your users, and it is invisible from your machine. -**The real case (fusion, caught 2026-07-19):** `adom/adom-desktop-fusion-bridge`'s release zip was **16.7 MB, of which ~15.3 MB was demo MP4s, screenshots and repo docs**. Every user's machine streamed that on `bridge_install`, and again on every auto-update. Stripped to runtime-only it is **456 KB — 97% smaller — with zero functional change.**+**The real case (fusion, caught 2026-07-19):** `adom/fusion-bridge`'s release zip was **16.7 MB, of which ~15.3 MB was demo MP4s, screenshots and repo docs**. Every user's machine streamed that on `bridge_install`, and again on every auto-update. Stripped to runtime-only it is **456 KB — 97% smaller — with zero functional change.**  Nobody was careless. The zip was built from `git ls-files` (or by zipping the repo), which sweeps in exactly the things that legitimately belong on your WIKI PAGE: heroes, screenshots, demo videos, architecture diagrams, long-form docs. @@ -607,14 +607,14 @@ Media on the page is served to a browser when someone looks at it. Media in an a The wiki has an **auto-discover** index, and it is the difference between a usable bridge and an **invisible** one. Declare `discovery_triggers` (the phrases a user actually says) on your page, and a plain request matches them via `adom-wiki discover find` / the in-app discovery an AI runs.  ```bash-adom-wiki discover triggers adom/adom-desktop-<bridge>-bridge --add "open my pcb" "export gerbers"  # add triggers+adom-wiki discover triggers adom/adom-bridge-<bridge>-bridge --add "open my pcb" "export gerbers"  # add triggers adom-wiki discover preview --json "open my pcb design"          # what would surface + the SCORE + WHY adom-wiki discover find    "screenshot my website"              # the live find an AI runs ```  **How scoring actually works (measured 2026-06-30 — know this or you'll guess wrong):** - `score = 3×(distinct triggers matched) + 1×(tags) + 1×(brief/text)`. **Trigger matches dominate (3× each);** tags/title/brief barely move it.-- **NO TRIGGERS = INVISIBLE.** A bridge with a perfect title ("Adom Desktop - Blender Bridge") + a matching tag scored **ZERO** for the literal query *"blender"* — it never appeared in `discover preview`. The scorer ignores title/tags when there are no triggers. **Every shipped bridge MUST carry `discovery_triggers` or it cannot be found by the discovery an AI uses.** (`discover search` full-texts the readme, but that is NOT the path discovery takes.)+- **NO TRIGGERS = INVISIBLE.** A bridge with a perfect title ("Adom Bridge - Blender Bridge") + a matching tag scored **ZERO** for the literal query *"blender"* — it never appeared in `discover preview`. The scorer ignores title/tags when there are no triggers. **Every shipped bridge MUST carry `discovery_triggers` or it cannot be found by the discovery an AI uses.** (`discover search` full-texts the readme, but that is NOT the path discovery takes.) - **More good triggers = higher rank.** Each match is +3, so 10 user-task phrasings beat 2. The healthy bundled bridges carry **30-40** triggers; a struggling one had 14 — all dev jargon — and lost EVERY user query.  **The cardinal rule: triggers are USER-TASK phrasings, NOT dev jargon.** A real user types *"open my pcb design"* / *"export gerber files"* / *"design a circuit board"* — NOT *"reverse bridge"* / *"kicad_bridge_call"* / *"fork the kicad bridge"*. A trigger list full of architecture terms scores 0 on every real query (kicad's actual failure: 7/7 user queries missed, and the **Fusion** bridge out-ranked KiCad on PCB queries because Fusion happened to carry a couple of fuzzy "pcb"/"export" triggers). Put **10+ everyday task phrasings first**; technical aliases (verb names) are a small minority at the end.@@ -627,13 +627,13 @@ adom-wiki discover find    "screenshot my website"              # the live find **Claim YOUR niche — don't poach a sibling's territory.** Discovery routes by whose triggers match; if two bridges match a query the user lands on whichever has *more* triggers, NOT whichever is actually right. So your triggers must describe what YOU are genuinely best at — and you must LEAVE OUT cases a sibling owns. **Canonical example: logging into a site / filling a form on a signed-in page is the native-browser extension's job — it drives the user's REAL browser with their REAL logins — NOT puppeteer's.** Pup is a fresh Chrome-for-Testing profile with no saved logins; it's for *non-login* automation (scraping, screenshots, testing, public pages). So pup must NOT carry `log in` / `fill a form on a site I'm signed into` triggers, and native-browser MUST. When a task needs a capability you DON'T have, make your `discovery_pitch` *point at the bridge that does* — and tell the user to install it (e.g. "for logged-in form-filling, install the native-browser extension") — rather than claim it and do it badly.  - **`discovery_pitch`** is the one-liner the snippet quotes back to the AI — auto-generated from `discovery_triggers` + `discovery_pitch`, **no separate upload step.** Make it an INSTRUCTION, not a tagline.-- **Make your `brief` / `discovery_pitch` an instruction** — it's what the AI reads from the find result. Tell it the next moves: *"…check `adom-desktop bridge_list` to confirm the bridge is installed; if it is, drive its `<prefix>_*` verbs. If the container lacks the skill, `adom-wiki pkg install adom/adom-desktop-<bridge>-bridge` (or `adom-desktop sync_skills`)."*+- **Make your `brief` / `discovery_pitch` an instruction** — it's what the AI reads from the find result. Tell it the next moves: *"…check `adom-bridge-cli bridge_list` to confirm the bridge is installed; if it is, drive its `<prefix>_*` verbs. If the container lacks the skill, `adom-wiki pkg install adom/adom-bridge-<bridge>-bridge` (or `adom-bridge-cli sync_skills`)."*  ### The end-to-end flow your three artifacts enable  User says *"show me my app in pup"* → the AI: 1. `adom-wiki discover find "show me my app in pup"` → your bridge surfaces (Artifact 3).-2. `adom-desktop bridge_list` → is your bridge installed in AD? If not, `bridge_install {your manifestUrl}` (Artifact 1).+2. `adom-bridge-cli bridge_list` → is your bridge installed in AD? If not, `bridge_install {your manifestUrl}` (Artifact 1). 3. Is your skill pkg installed in THIS container (`~/.claude/skills/` or `~/.codex/skills/`)? If not, `adom-wiki pkg install adom/<slug>` / `sync_skills` (Artifact 2). 4. Drive your verbs — guided by the **rich hints you return** (the principle at the top). @@ -666,7 +666,7 @@ A cold first call that pays multi-minute setup (npm install, downloading a runti  ## Cold-start tiers, host-app detection & the `bridge_readiness` probe (v1.9.47) -A bridge has TWO kinds of dependency, and **AD treats them OPPOSITELY.** Get this right or a first-install probe (e.g. Hydrogen's "what tools do I have") triggers a slow install at the worst moment.+A bridge has TWO kinds of dependency, and **AD treats them OPPOSITELY.** Get this right or a first-install probe (e.g. HD's "what tools do I have") triggers a slow install at the worst moment.  **Tier 1 — your RUNTIME (Node, Python, your own browser download): AD prewarms + auto-installs it.** This is your plumbing. On first boot AD **provisions** Node/Python in the background — a **portable, no-UAC** copy under `~/.adom/adom-runtimes/` (or a system install if one's on PATH), **NOT winget/MSI** (see **Runtime contract** above) — and prewarms heavy assets you declare. You may also download your own runtime asset (pup downloads Chrome-for-Testing) — fine, it's yours. **But do it in the BACKGROUND: a `*_status` / `*_readiness` / ANY probe verb must NEVER block on a heavy download.** Spawn your server fast (it usually doesn't need the asset to *start*), kick the download off detached, and report `state:"warming"` until ready. A synchronous-download-on-first-verb is the exact footgun this release fixed (pup's 150 MB Chrome fetch used to block the probe). @@ -678,7 +678,7 @@ A bridge has TWO kinds of dependency, and **AD treats them OPPOSITELY.** Get thi  | Level | Bridge(s) | Behavior | |---|---|---|-| **0 · Built-in / MUST** | Adom Hydrogen (`hydrogen`) | The platform itself — the editor AD lives in. Always present; not "installed" by AD. |+| **0 · Built-in / MUST** | Hydrogen Desktop (`hd`) | The platform itself — the editor AD lives in. Always present; not "installed" by AD. | | **1 · Fundamental — pre-emptive** | Puppeteer (`pup`) | Browser automation is foundational to Adom. AD pre-installs its RUNTIME (Node + Chrome-for-Testing) ASAP on first boot, no asking. | | **2 · Recommended — proactive offer** | native-browser (the browser EXTENSION half) | The user's REAL logged-in browser (login/forms). Since v1.9.142 the DESKTOP BRIDGE half ships bundled in the AD installer (0.2 MB, zero deps, `persistent:true` — its extension-free verbs like `nbrowser_open_os_window`/`nbrowser_profiles`/`nbrowser_os_windows` work day one). What remains level-2 is the in-browser EXTENSION: AD/the bridge proactively SUGGESTS it and it installs on a yes (a browser-extension step the user confirms), unlocking full in-page drive. | | **3 · On-request host app** | KiCad, Fusion, Altium, … | Detect by default; NEVER pre-install (huge app, rude unasked). The moment the user wants it, the AI installs it AUTOMATICALLY (winget / `desktop_install_*`) — NEVER tells the user to do it manually. |@@ -720,7 +720,7 @@ Don't cram everything into one file. Ship **three kinds** of skill, each with a **Start from the templates — don't write these from scratch.** Fill-in-the-blanks files for each of the three skill kinds, generalized from pup's battle-tested set. **Fetch them from THIS page** (they used to be cited as a path inside AD's own repo, which is private, so the pointer was unfollowable for exactly the cloud authors it addressed — fixed 2026-07-25):  ```bash-BASE=https://wiki.adom.inc/api/v1/pages/adom-desktop-bridges/files+BASE=https://wiki.adom.inc/api/v1/pages/adom-bridge-sdk/files curl -fsSL $BASE/bridge-dev-template.md        -o dev-skills/<bridge>-dev.md curl -fsSL $BASE/bridge-publish-template.md    -o dev-skills/<bridge>-publish.md curl -fsSL $BASE/bridge-user-skill-template.md -o skills/<bridge>/SKILL.md@@ -730,7 +730,7 @@ curl -fsSL $BASE/bridge-user-skill-template.md -o skills/<bridge>/SKILL.md - **`bridge-publish-template.md`** — the whole release/pkg/discovery dance and every trap (the `.gitignore` set-aside, the lean-tarball rule, release-and-pkg-ship-together). - **`bridge-user-skill-template.md`** — the consumer skill, already encoding all eight conventions below plus the `--ai-thread` identity line. -Copy the one you need, swap `<bridge>`/`<prefix>` for your names, delete what doesn't apply. **How your skills reach a container in the first place** (the runtime installs on the desktop, the skills install in the container, and the two travel by different routes): [`docs/bridge-skill-propagation.md`](https://wiki.adom.inc/api/v1/pages/adom-desktop-bridges/files/docs/bridge-skill-propagation.md) on this page.+Copy the one you need, swap `<bridge>`/`<prefix>` for your names, delete what doesn't apply. **How your skills reach a container in the first place** (the runtime installs on the desktop, the skills install in the container, and the two travel by different routes): [`docs/bridge-skill-propagation.md`](https://wiki.adom.inc/api/v1/pages/adom-bridge-sdk/files/docs/bridge-skill-propagation.md) on this page.  --- @@ -777,7 +777,7 @@ Every bridge writes to stdout, AD captures it, and nobody asks the volume questi  *(READING the log is the section immediately above: `bridge_log_read` plus AD's separate lifecycle audit. This section is about what you WRITE.)* -**Do NOT write your own parallel log file.** Two log files means two sources of truth and double the disk for the same bytes. pup built one (a rotated `pup.log`) and deliberately reverted it. If you think you need your own log, what you probably want is for AD to scope `bridge_log_read` to your bridge: file that against `adom/adom-desktop` (see "Filing issues against AD" above) instead of forking the log.+**Do NOT write your own parallel log file.** Two log files means two sources of truth and double the disk for the same bytes. pup built one (a rotated `pup.log`) and deliberately reverted it. If you think you need your own log, what you probably want is for AD to scope `bridge_log_read` to your bridge: file that against `adom/adom-bridge` (see "Filing issues against AD" above) instead of forking the log.  **The five patterns that keep a bridge out of firehose territory:** @@ -796,11 +796,11 @@ Every bridge writes to stdout, AD captures it, and nobody asks the volume questi  ## Publish checklist (the whole modern flow) -1. **Page** — `adom-wiki page create adom/adom-desktop-<bridge>-bridge` with a hero + an **instructional `brief`** + **`discovery_triggers`**.+1. **Page** — `adom-wiki page create adom/adom-bridge-<bridge>-bridge` with a hero + an **instructional `brief`** + **`discovery_triggers`**. 2. **Runtime** — `release create` + `release upload` the versioned `.zip`; POST the manifest to `/files`; set `updateManifestUrl` in BOTH the zip's `bridge.json` and the manifest. **Before uploading: `unzip -l <zip> | sort -k1 -n -r | head` — runtime only, no media.** *(→ Payload rules)* 3. **Skills pkg** — stage ROOT SKILL.md + `skills/<slug>/SKILL.md` (core USER skill + sub-skills) + `discovery_triggers`; `pkg pack` (**`tar tzf` it — skills only, zero media/binaries**) → `pkg publish --org adom` (mind the `.gitignore` trap). Your `description` carries the one-sentence "this installs SKILLS, not the runtime" line. *(→ Artifact 2)* 4. **Discovery** — `discover triggers` to confirm; `discover preview "<a real user phrase>"` to confirm you surface. 5. **Re-seed AD's bundled copy** (AD-core thread, not you) — extract your Release zip over `plugins/<name>/` keeping bundled deps; ships in the next NSIS as the offline fallback. 6. **Verify** — `bridge_install {manifestUrl}` on a desktop → `bridge_list` shows your `version` + `skillPkg`; `bridge_check_updates` reaches your page; a container `sync_skills` pulls your USER skill; `discover find "<phrase>"` surfaces you with an instructional brief. -**Reference bridge = pup** (`adom/adom-desktop-puppeteer-bridge`): live `discovery_triggers`, a skills pkg with a core + sub-skills, a Release zip + manifest with `updateManifestUrl`, a bundled seed with prewarmed `node_modules`, and rich per-verb hints. Copy its shape. (`scripts/sample-bridges/hello-python` + `hello-rust` are the minimal skeletons.) KiCad, Fusion, Blender, Puppeteer, and the **native-browser** bridge (in the `adom-browser-extension` repo) are cloud-owned — don't edit them from the AD repo. When an AD release changes a bridge-facing contract **— or when THIS SDK doc itself changes —** AD posts a migration notice to **each** of their wiki discussions (`adom-desktop-{kicad,fusion,blender,puppeteer}-bridge` + `adom-browser-extension`). **Author-facing takeaway: watch your bridge's own wiki DISCUSSION/issues list — that's where an "SDK changed, please re-audit against the billboard" notice lands, and where you reply with your plan or blockers.** It's the only channel AD uses to reach you; AD never edits your repo.+**Reference bridge = pup** (`adom/adom-desktop-puppeteer-bridge`): live `discovery_triggers`, a skills pkg with a core + sub-skills, a Release zip + manifest with `updateManifestUrl`, a bundled seed with prewarmed `node_modules`, and rich per-verb hints. Copy its shape. (`scripts/sample-bridges/hello-python` + `hello-rust` are the minimal skeletons.) KiCad, Fusion, Blender, Puppeteer, and the **native-browser** bridge (in the `adom-browser-extension` repo) are cloud-owned — don't edit them from the AD repo. When an AD release changes a bridge-facing contract **— or when THIS SDK doc itself changes —** AD posts a migration notice to **each** of their wiki discussions (`adom-bridge-{kicad,fusion,blender,puppeteer}-bridge` + `adom-browser-extension`). **Author-facing takeaway: watch your bridge's own wiki DISCUSSION/issues list — that's where an "SDK changed, please re-audit against the billboard" notice lands, and where you reply with your plan or blockers.** It's the only channel AD uses to reach you; AD never edits your repo.
SKILL.md+52−52
@@ -1,9 +1,9 @@ ----name: adom-desktop-bridge-sdk-description: Author, PUBLISH, and make-discoverable an adom-desktop bridge — a small local server that adds a namespace of verbs (kicad_, fusion_, browser_, your_prefix_) to adom-desktop. Use when building, publishing, updating, prewarming, or debugging a bridge, or deciding what skills to ship. Covers the AD-core-vs-author boundary; the #1 rule (rich hints in EVERY verb's OUTPUT, because the AI reads CLI output, never your on-disk skill); bridge.json; where every file lives (the Release-zip-vs-skills-pkg split, the TWO package.json files [server deps in the zip; the pkg declares dependencies:{adom/adom-desktop}], manifest + runtime-asset placement); the pkg carries ONLY user skills while dev + publish skills live in your source repo (not the pkg); the THREE things you publish (runtime Release zip + manifest, container skills pkg, wiki discovery_triggers); prewarm; and the auto-discover → install-check → use flow.+name: adom-bridge-bridge-sdk+description: Author, PUBLISH, and make-discoverable an adom-bridge bridge — a small local server that adds a namespace of verbs (kicad_, fusion_, browser_, your_prefix_) to adom-bridge. Use when building, publishing, updating, prewarming, or debugging a bridge, or deciding what skills to ship. Covers the AD-core-vs-author boundary; the #1 rule (rich hints in EVERY verb's OUTPUT, because the AI reads CLI output, never your on-disk skill); bridge.json; where every file lives (the Release-zip-vs-skills-pkg split, the TWO package.json files [server deps in the zip; the pkg declares dependencies:{adom/adom-bridge}], manifest + runtime-asset placement); the pkg carries ONLY user skills while dev + publish skills live in your source repo (not the pkg); the THREE things you publish (runtime Release zip + manifest, container skills pkg, wiki discovery_triggers); prewarm; and the auto-discover → install-check → use flow. --- -# Bridge SDK — build, publish, and surface a bridge for Adom Desktop+# Bridge SDK — build, publish, and surface a bridge for Adom Bridge  A **bridge** is a small local program (Python, Node, Rust, anything) that AD spawns and routes a **namespace of verbs** to over HTTP. KiCad, Fusion 360, Puppeteer (pup), native-browser, Blender are bridges. The modern flow has THREE moving parts you own — get all three right and a cloud/WSL2 container can *discover* your bridge from a plain-English request, confirm it's installed, and drive it with zero tribal knowledge: @@ -29,9 +29,9 @@ A **bridge** is a small local program (Python, Node, Rust, anything) that AD spa | **Installed by** | `bridge_install {manifestUrl}` → AD streams, unzips, spawns | `pkg install adom/<slug>` / `sync_skills` → `~/.claude/skills` + `~/.codex/skills` | | **Contains** | server code + `bridge.json` at the zip **ROOT** + assets. **Node: SOURCE ONLY, no `node_modules`.** | ONLY: ROOT `SKILL.md`, `skills/<name>/SKILL.md` (user skills), `package.json`, `install.sh`, `uninstall.sh`. **Text only — the whole tarball should be a few KB.** | | **NEVER contains** | the skills pkg | **ANY image (hero / screenshots — those are WIKI PAGE assets, see below), any binary, the Release zip, `src/`, `node_modules`, dev/publish skills, or the server's npm deps.** If `tar tzf` shows a `.png`/`.jpg`/`.zip`/`.exe`, you bundled bloat — remove it. |-| **its `package.json`** | (node) the **bridge SERVER's runtime deps** AD installs at spawn: `dependencies:{ puppeteer, … }` | the **pkg manifest**: `files[]` (user skills), `discovery_triggers`, scripts, **AND `dependencies:{"adom/adom-desktop":"^1.9.x"}`** (the container needs the AD CLI + core skills to drive you) — but NOT the server's npm deps |+| **its `package.json`** | (node) the **bridge SERVER's runtime deps** AD installs at spawn: `dependencies:{ puppeteer, … }` | the **pkg manifest**: `files[]` (user skills), `discovery_triggers`, scripts, **AND `dependencies:{"adom/adom-bridge":"^1.9.x"}`** (the container needs the AD CLI + core skills to drive you) — but NOT the server's npm deps | -**Yes, a node bridge has TWO `package.json` files and they are NOT interchangeable.** One rides inside the Release zip and lists the **bridge server's** runtime deps (puppeteer, sharp, ...) that AD installs on the *desktop* at spawn. The other is the **pkg manifest** in the skills tarball: it lists your user-skill `files[]` + `discovery_triggers` AND declares **`dependencies:{"adom/adom-desktop":"^1.9.x"}`** — `pkg install` resolves that npm-style, so installing your bridge's skill pkg also pulls the adom-desktop CLI + core skills the container needs to drive you. What the pkg manifest must NOT list is the **server's** npm deps (those are desktop-side, in the zip) — put them there and you bloat every container install, since the container never runs your server.+**Yes, a node bridge has TWO `package.json` files and they are NOT interchangeable.** One rides inside the Release zip and lists the **bridge server's** runtime deps (puppeteer, sharp, ...) that AD installs on the *desktop* at spawn. The other is the **pkg manifest** in the skills tarball: it lists your user-skill `files[]` + `discovery_triggers` AND declares **`dependencies:{"adom/adom-bridge":"^1.9.x"}`** — `pkg install` resolves that npm-style, so installing your bridge's skill pkg also pulls the adom-bridge CLI + core skills the container needs to drive you. What the pkg manifest must NOT list is the **server's** npm deps (those are desktop-side, in the zip) — put them there and you bloat every container install, since the container never runs your server.  **Where each file physically lives (this is the map that ends the inconsistency):** - **`bridge.json`** → the **root of the Release zip** (AD reads it post-unzip). Carries `version` + `updateManifestUrl`.@@ -48,7 +48,7 @@ A **bridge** is a small local program (Python, Node, Rust, anything) that AD spa | **DEV** | a maintainer editing the bridge | your **source repo** | **NO, never** | only if your repo is public | | **PUBLISH** | a maintainer releasing the bridge | your **source repo** | **NO, never** | only if your repo is public | -**The one rule:** the pkg tarball carries **USER skills only**; **DEV + PUBLISH skills live in the source repo.** Do NOT "scope" dev/publish into the pkg with `user-invocable:false` — that still ships them (pure bloat). Open-vs-closed source only sets your repo's *visibility*; it never changes what's in the pkg. adom-desktop core does exactly this: user/integration skills in the pkg; `RELEASE` / `WIKI_PUBLISH` / `SIGNING` / `AD_RELAUNCH` live only in the source repo.+**The one rule:** the pkg tarball carries **USER skills only**; **DEV + PUBLISH skills live in the source repo.** Do NOT "scope" dev/publish into the pkg with `user-invocable:false` — that still ships them (pure bloat). Open-vs-closed source only sets your repo's *visibility*; it never changes what's in the pkg. adom-bridge core does exactly this: user/integration skills in the pkg; `RELEASE` / `WIKI_PUBLISH` / `SIGNING` / `AD_RELAUNCH` live only in the source repo.  --- @@ -84,10 +84,10 @@ curl -s -X POST "$ADOM_DIRECT_API_URL/command" -H 'Content-Type: application/jso  ### Notify the user AND get their answer back (`notify_user` — two separate axes) -Your bridge can prompt the user with a native toast and read their reply — the same `notify_user` verb the AI uses. It has **two independent axes; don't conflate them** (full ref: the `adom-desktop-notify` skill / `skills/NOTIFY.md`):+Your bridge can prompt the user with a native toast and read their reply — the same `notify_user` verb the AI uses. It has **two independent axes; don't conflate them** (full ref: the `adom-bridge-notify` skill / `skills/NOTIFY.md`):  - **Axis 1 — get the answer back.** There is no server-push, so either **`wait:true`** (the `notify_user` call blocks and returns the user's choice inline — best for a one-shot prompt) or fire-and-continue then poll **`notify_events {sinceSeq}`** (drains every click) / **`notify_response {id}`**. The click is recorded durably, so it survives even on a headless box.-- **Axis 2 — reveal a window (`focus`).** A LOCAL desktop action, not a reply. `focus:{hwnd|cacheKey|titleContains|app|host}` raises a window on the user's screen when they click. **`focus:{app:"<your-exe>"}`** points the click at a window YOUR bridge drives; **`focus:{host:true}`** points it at the app AD is embedded under (Adom Hydrogen). Pass `focus` whenever the toast is *about* a window, so the click lands there instead of opening AD.+- **Axis 2 — reveal a window (`focus`).** A LOCAL desktop action, not a reply. `focus:{hwnd|cacheKey|titleContains|app|host}` raises a window on the user's screen when they click. **`focus:{app:"<your-exe>"}`** points the click at a window YOUR bridge drives; **`focus:{host:true}`** points it at the app AD is embedded under (Hydrogen Desktop). Pass `focus` whenever the toast is *about* a window, so the click lands there instead of opening AD.  ```bash # Ask, and get the answer back inline:@@ -109,7 +109,7 @@ A user runs roughly **20 AI threads against one desktop**. Until v1.9.180 AD rel - No bridge log could **attribute a verb** to the thread that issued it, so interference was impossible to reconstruct afterwards. - Any agent's `bridge_kill` **destroyed every other agent's work**, silently, with no way for anyone to see who did it. -Filed as wiki issue `adom/adom-desktop#34` by AdityaAngajala, with the pup half at `adom/adom-desktop-puppeteer-bridge#22`.+Filed as wiki issue `adom/adom-bridge#34` by AdityaAngajala, with the pup half at `adom/adom-desktop-puppeteer-bridge#22`.  ### What you now receive, for free @@ -127,7 +127,7 @@ AD stamps three headers on **every relayed bridge request** — all four request  These values are **SELF-ASSERTED**, exactly like `reason`. Use them for **attribution, logging, UX and arbitration hints**. **NEVER for authorization, and never as a security boundary.** -AD's own security model is transport-based (the `adom-desktop-permissions` skill has the full model): a caller is trusted because of *how it reached AD*, not because of what it *says about itself*. A header a caller supplies about itself cannot gate anything — if you write `if (thread === "admin-tab") allowDangerousThing()`, you have written a hole, not a check.+AD's own security model is transport-based (the `adom-bridge-permissions` skill has the full model): a caller is trusted because of *how it reached AD*, not because of what it *says about itself*. A header a caller supplies about itself cannot gate anything — if you write `if (thread === "admin-tab") allowDangerousThing()`, you have written a hole, not a check.  One asymmetry worth knowing: the container name in `X-Adom-Caller-Container` is the one the **caller claimed**. AD separately knows a **verified** container (the authenticated relay connection name) and shows the user **both** when they disagree. You only get the claimed one, so treat it as a label, not an identity. @@ -177,7 +177,7 @@ The recommended posture: - **Refuse only for destructive operations** — closing, resetting, navigating away, deleting. - **Always name the current owner in the message.** The whole point is that the second agent can now go coordinate with a thread it can identify. An error that says "session busy" without a name is barely better than no header at all. -**3. Label the UI.** If your bridge drives a visible window, put the owning thread where the **user** can see it — window title, taskbar tooltip, or jump-list name — via `desktop_set_window_identity` (and `desktop_taskbar`'s `thumbnailTooltip` for uncapped hover text). The user is the one with 20 tabs open; showing which tab owns which window is the fix for their side of the problem. Full guide: the `adom-desktop-window-identity` skill, and the "Branding + driving a window you borrowed" section immediately below.+**3. Label the UI.** If your bridge drives a visible window, put the owning thread where the **user** can see it — window title, taskbar tooltip, or jump-list name — via `desktop_set_window_identity` (and `desktop_taskbar`'s `thumbnailTooltip` for uncapped hover text). The user is the one with 20 tabs open; showing which tab owns which window is the fix for their side of the problem. Full guide: the `adom-bridge-window-identity` skill, and the "Branding + driving a window you borrowed" section immediately below.  **4. Report it back.** Include the owning thread in your **status / list verbs**, so an agent can *discover* it is not alone before it stomps on something: @@ -242,14 +242,14 @@ Useful to understand, though none of it is your responsibility:  ```bash # Per call — the CORRECT mechanism:-adom-desktop --ai-thread "chip-fetcher tab 3" <verb> '<json>'+adom-bridge-cli --ai-thread "chip-fetcher tab 3" <verb> '<json>' ``` -The `adom-desktop` CLI attaches a `caller` block to **every** verb automatically. A relay call with **no `aiThread` is REFUSED by AD** with `errorCode: caller_identity_required` before it ever reaches a bridge.+The `adom-bridge` CLI attaches a `caller` block to **every** verb automatically. A relay call with **no `aiThread` is REFUSED by AD** with `errorCode: caller_identity_required` before it ever reaches a bridge.  > **Correction, 2026-07-25 (AD 1.9.182):** an earlier version of this page told callers to `export ADOM_AI_THREAD` once per session. **That advice was wrong and has been withdrawn.** Roughly 20 AI threads share one container and one `$HOME`, so the env var scopes to the shell environment, not to the thread — every thread then reports the *same* name, which is worse than no name at all because it is confident and plausible. It also failed silently: tool shells are non-interactive, so an export appended below `~/.bashrc`'s `case $- in *i*` early-return never ran. The env var is still honored, but only fits a container running exactly one agent (CI, a single-purpose job). **If your bridge's own docs repeat the export advice, please correct them.** -**So by the time a request reaches you, identity is normally present.** The exception is **loopback callers** — Hydrogen, another bridge, or the local CLI — which are trusted by transport and not yet *required* to identify (that gate tightens once bridges adopt the forwarding contract above). Code for the absent case (`|| 'unidentified'`), never assume the header exists.+**So by the time a request reaches you, identity is normally present.** The exception is **loopback callers** — HD, another bridge, or the local CLI — which are trusted by transport and not yet *required* to identify (that gate tightens once bridges adopt the forwarding contract above). Code for the absent case (`|| 'unidentified'`), never assume the header exists.  ### If your bridge wants to REQUIRE identity — use the shared contract @@ -259,7 +259,7 @@ Some verbs are worth refusing anonymously. If you do, **use AD's exact refusal s { "success": false, "errorCode": "caller_identity_required",   "error": "'<verb>' needs to know WHO is asking — the user runs many AI threads and needs to see which one drove this.",   "supplyIdentity": {-    "bestWay": "adom-desktop --ai-thread \"<your conversation name>\" <verb> '<json>'",+    "bestWay": "adom-bridge-cli --ai-thread \"<your conversation name>\" <verb> '<json>'",     "orInArgs": { "caller": { "aiThread": "<name>", "containerName": "<container>" } }   },   "_hint": "This is attribution, not a permission check — the name is self-reported and nothing is gated on it." }@@ -283,7 +283,7 @@ Reusing `caller_identity_required` verbatim is the point: an AI that learned the  ## Branding + driving a window you borrowed (window identity + taskbar, v1.9.106–1.9.153) -If your bridge drives a window another process owns — the classic case is a **Chrome for Testing** window that otherwise reads *"Test"* with Chrome's icon — AD gives you a generic verb family to make it present as YOUR app across every Windows shell surface, and to show live status on its taskbar button. AD stays generic: it applies whatever name/icon/command you pass; it knows nothing about your app. **Full guide: the `adom-desktop-window-identity` skill** (`https://wiki.adom.inc/api/v1/pages/adom-desktop/files/docs/WINDOW_IDENTITY.md` or the repo `skills/WINDOW_IDENTITY.md`). The essentials:+If your bridge drives a window another process owns — the classic case is a **Chrome for Testing** window that otherwise reads *"Test"* with Chrome's icon — AD gives you a generic verb family to make it present as YOUR app across every Windows shell surface, and to show live status on its taskbar button. AD stays generic: it applies whatever name/icon/command you pass; it knows nothing about your app. **Full guide: the `adom-bridge-window-identity` skill** (`https://wiki.adom.inc/api/v1/pages/adom-bridge/files/docs/WINDOW_IDENTITY.md` or the repo `skills/WINDOW_IDENTITY.md`). The essentials:  - **Two halves, you need both:** `desktop_register_app_identity {appId, displayName, iconPath, shortcut}` teaches Windows what an AUMID means (HKCU only, no UAC; `shortcut:false` for a per-session id with no Start Menu spam), and `desktop_set_window_identity {hwnd, appId, iconPath, displayName, relaunchCommand}` stamps it on the live window. Stamp without register → Alt-Tab / taskbar fall back to the owning exe's icon. - **Per-session identity:** register `shortcut:false` per window (e.g. `Adom.Pup.<sessionId>`) so each keeps its own taskbar button; `desktop_unregister_app_identity {appId}` on close.@@ -317,7 +317,7 @@ If your bridge drives a window another process owns — the classic case is a ** This SDK evolves. When it does, AD posts a re-audit notice to **YOUR bridge's wiki discussion** and re-publishes this billboard. You do NOT need a human to hand you a prompt — self-serve at the start of a work session:  1. **Check your discussion for a notice.** `adom-wiki discussion list <your-slug>` (or open your page's Discussions tab) and read any "SDK update" / "re-audit" thread. A notice can be EDITED after posting (a correction), so always read the **current** body, not a cached earlier version.-2. **Re-read this billboard**, especially "Where every file lives" (above) and the "Self-audit checklist" (below): `https://wiki.adom.inc/adom/adom-desktop-bridges` (or `curl -s https://wiki.adom.inc/api/v1/pages/adom-desktop-bridges/files/README.md`).+2. **Re-read this billboard**, especially "Where every file lives" (above) and the "Self-audit checklist" (below): `https://wiki.adom.inc/adom/adom-bridge-sdk` (or `curl -s https://wiki.adom.inc/api/v1/pages/adom-bridge-sdk/files/README.md`). 3. **Run the Self-audit checklist** (next section) against your code, `bridge.json`, skills, README, and wiki page. Fix every miss. 4. **Reply on the discussion** with what you changed or any blockers. That thread is the ONLY channel AD uses to reach you, so closing the loop there is how AD knows you are current. @@ -338,7 +338,7 @@ So make the invitation part of your verb output, the same way `_hint` is. This i   "success": false,   "errorCode": "…",   "_hint": "…",-  "_reportIssues": "THIS MAY BE A <BRIDGE> BUG. PLEASE REPORT IT. Check for a duplicate with `adom-wiki issue list adom/<your-slug>`, then file: `adom-wiki issue create adom/<your-slug> --title \"<one-line symptom>\" --category bug-report --body \"<what you ran, what you expected, what happened, the VERBATIM response JSON, the bridge version from bridge_list, and the OS>\"`. Reports from real machines are the only way this bridge gets correct. (An AD-core problem, such as the relay, bridge lifecycle or caller identity, belongs on adom/adom-desktop instead.)"+  "_reportIssues": "THIS MAY BE A <BRIDGE> BUG. PLEASE REPORT IT. Check for a duplicate with `adom-wiki issue list adom/<your-slug>`, then file: `adom-wiki issue create adom/<your-slug> --title \"<one-line symptom>\" --category bug-report --body \"<what you ran, what you expected, what happened, the VERBATIM response JSON, the bridge version from bridge_list, and the OS>\"`. Reports from real machines are the only way this bridge gets correct. (An AD-core problem, such as the relay, bridge lifecycle or caller identity, belongs on adom/adom-bridge instead.)" } ``` @@ -354,7 +354,7 @@ A line on **every** response is noise that trains the caller to ignore the field  **Make it actionable, not a slogan.** "Report bugs" produces nothing. Give the AI the exact `adom-wiki issue create` command, your slug, a `--category`, and the list of evidence to include (the verbatim response JSON and your bridge version are the two that make a report reproducible). Tell it to check for a duplicate first: an `issue list` call costs nothing and keeps your page clean. -**Route the report to the repo that owns the code.** Say explicitly, in the same string, that AD-core problems (the relay and `/command` passthrough, spawn and provisioning, bridge lifecycle, caller identity) go to **`adom/adom-desktop`**, not to your page. Authors get this wrong in the other direction too, which is the next section: a misfiled issue is a note to yourself that the people who can fix it never read.+**Route the report to the repo that owns the code.** Say explicitly, in the same string, that AD-core problems (the relay and `/command` passthrough, spawn and provisioning, bridge lifecycle, caller identity) go to **`adom/adom-bridge`**, not to your page. Authors get this wrong in the other direction too, which is the next section: a misfiled issue is a note to yourself that the people who can fix it never read.  **Let the user turn it off.** Ship a settings toggle (pup uses `browser_configure {reportIssues:"off"}`) so a user who finds it noisy can silence it without losing functionality. Default it **on**: the cost is a few tokens on failures, and the benefit is the bug class you would otherwise ship to every user. @@ -369,14 +369,14 @@ You WILL find AD-core bugs and gaps while building your bridge. That is expected **The rule: an issue lives on the issue list of the repo that OWNS the code that must change.**  - A bug in YOUR bridge's verb / hint / self-heal / status chip goes to **YOUR page's issue list** (or you just fix it, since you own it).-- A missing or broken **AD-CORE** capability (a lifecycle verb, a spawn / provision / bind behavior, the relay passthrough dropping a field, a manifest field AD must honor, the status-chip contract) goes to **`adom/adom-desktop`**, NOT your own page. AD-core's maintainers and AI watch `adom/adom-desktop`; they do NOT read your bridge page's issues. **An AD ask filed on your own page is a note to yourself.**+- A missing or broken **AD-CORE** capability (a lifecycle verb, a spawn / provision / bind behavior, the relay passthrough dropping a field, a manifest field AD must honor, the status-chip contract) goes to **`adom/adom-bridge`**, NOT your own page. AD-core's maintainers and AI watch `adom/adom-bridge`; they do NOT read your bridge page's issues. **An AD ask filed on your own page is a note to yourself.** - A **wiki or CLI** bug goes to `adom/wiki`. A **sibling bridge's** bug goes to that bridge's page.  **How (the CLI):**  ```bash-adom-wiki issue list   adom/adom-desktop            # FIRST: skim for a dup, comment if one exists-adom-wiki issue create adom/adom-desktop \+adom-wiki issue list   adom/adom-bridge            # FIRST: skim for a dup, comment if one exists+adom-wiki issue create adom/adom-bridge \   --title "<verb or subsystem>: <the gap in one line>" \   --category bug-report \   --body "$(cat your-writeup.md)"@@ -386,7 +386,7 @@ adom-wiki issue create adom/adom-desktop \  **After you file:** the AD-core thread replies on that issue. Read the reply, it may change your plan (for example "fixed in AD X.Y, bump your pin"). That issue thread is the cross-repo channel; keep the loop closed there. -**Why this matters most for AD:** AD-core spawns you, provisions your runtime, relays your verbs, and installs you, so AD is the repo you will file against most often. Get the destination right the first time. The established precedent all sits on `adom/adom-desktop`: #26 and #27 (pup to AD, Chrome for Testing), #16 (pup to AD, window-verb interception), #3 / #20 / #21 (Bridge SDK asks).+**Why this matters most for AD:** AD-core spawns you, provisions your runtime, relays your verbs, and installs you, so AD is the repo you will file against most often. Get the destination right the first time. The established precedent all sits on `adom/adom-bridge`: #26 and #27 (pup to AD, Chrome for Testing), #16 (pup to AD, window-verb interception), #3 / #20 / #21 (Bridge SDK asks).  --- @@ -402,7 +402,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against  **Artifacts & deps** (the two-artifact split — the #1 inconsistency) *(→ Where every file lives)*: - [ ] Your Release `.zip` is a **Release asset** (`release upload`), NOT a loose `/files` blob — and old version zips are NOT piling up on `/files` (audit for stray `*-v*.zip`).-- [ ] The **server's** runtime deps live in the **ZIP's** `package.json` (node) / `requirements.txt` (python). The **pkg's** `package.json` declares `dependencies:{"adom/adom-desktop":"^1.9.x"}` (so `pkg install` pulls the AD CLI + core skills) and does NOT list the server's npm deps.+- [ ] The **server's** runtime deps live in the **ZIP's** `package.json` (node) / `requirements.txt` (python). The **pkg's** `package.json` declares `dependencies:{"adom/adom-bridge":"^1.9.x"}` (so `pkg install` pulls the AD CLI + core skills) and does NOT list the server's npm deps. - [ ] A big runtime asset (Mesa, a browser) is a **`prewarm.asset`** or seed-bundled — NOT a loose `/files` blob. - [ ] Your **manifest** (`adom-bridge-<name>-manifest.json`) is on `/files`; its `url` points at the **Release download**, not at a `/files` blob. - [ ] **PAYLOAD:** `unzip -l <your zip> | sort -k1 -n -r | head` shows **runtime only** — no `.mp4`/`.png`/`.jpg`/screenshots/CHANGELOG/`.github`. A runtime zip is normally well under 1 MB; if yours is bigger, justify every large entry. `tar tzf <your tgz>` shows **skills only** — zero media, zero binaries. Media belongs on the PAGE. *(→ Payload rules)*@@ -427,7 +427,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against **Skills & docs** (what the container + humans read): - [ ] Your pkg ships **ONLY your USER skill(s)** — a real `skills/<name>/SKILL.md` (core user skill + user sub-skills) with a `Parent skill:` first body line; every path listed EXPLICITLY (no globs) in `files[]` + install.sh/uninstall.sh. *(→ Artifact 2 + The SKILL SET)* - [ ] **DEV + PUBLISH skills are in your source REPO, NOT the pkg** — not in `files[]`, not scoped in via `user-invocable:false`. A developer gets them by cloning the source; the normal user never needs them. Open-vs-closed source is irrelevant (it only sets repo visibility). *(→ Where every file lives)*-- [ ] Your pkg's `package.json` declares `dependencies:{"adom/adom-desktop":"^1.9.x"}` so `pkg install` pulls the AD CLI + core skills the container needs. *(→ Where every file lives)*+- [ ] Your pkg's `package.json` declares `dependencies:{"adom/adom-bridge":"^1.9.x"}` so `pkg install` pulls the AD CLI + core skills the container needs. *(→ Where every file lives)* - [ ] Your USER skill leads with the cold-start "read before you panic" section + the 8 conventions, and points the AI at `bridge_readiness`. *(→ USER-skill conventions)* - [ ] README + wiki page + skills all AGREE with the above (no stale github links, no wrong schema, triggers vetted). @@ -444,7 +444,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against   "description": "Drive Blender headless + GUI",   "author": "you",   "license": "MIT",-  "docs": "https://wiki.adom.inc/adom/adom-desktop-blender-bridge",  // your OWN page (drives skillPkg + hero)+  "docs": "https://wiki.adom.inc/adom/adom-bridge-blender-bridge",  // your OWN page (drives skillPkg + hero)   "hero": "https://.../hero.png",           // optional; else AD uses your wiki page hero, else a monogram. Ship it at 1.6 aspect (≈2000×1250) — see "Card hero + update cost"   "spawn": {     "kind": "python",                       // python | node | exe | external-http  (NOT "process")@@ -458,7 +458,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against   "verbPrefixes": ["blender_"],             // any verb with these prefixes routes to you   "verbs": ["blender_render", "blender_status", "blender_describe"],  // every verb (routing + Verbs tab)   "statusVerb": "blender_status",           // verb a caller polls after a long op times out-  "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-desktop-blender-bridge/files/blender-manifest.json",+  "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-bridge-blender-bridge/files/blender-manifest.json",    "timeouts": {                             // v1.9.79 — per-verb HTTP budgets (seconds); see note below     "default": 60,                          //   fallback for any verb not matched (absent → AD's global 60s)@@ -481,7 +481,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against - **`spawn.healthEndpoint`** lives **inside `spawn`** (not at the top level), default `/health`. - **`updateManifestUrl`** — the full URL of YOUR manifest on YOUR page. **Declare it in `bridge.json`** so it survives a republish; AD polls it (on launch + every 4h) and auto-updates users. (AD also remembers the install URL in `~/.adom/bridge-install-urls.json` as a fallback, but the declared field is the durable, author-intended path.) - **`docs`** — point at YOUR dedicated wiki page. AD derives your **skillPkg** (`adom/<slug>`) and your card hero from this. Don't point it at a shared/structural page.-- **`homepage`** — a **`wiki.adom.inc`** URL (your own page, or `https://wiki.adom.inc/adom/adom-desktop` for the platform). **NEVER `github.com/...`** — GitHub is source-backup only; the wiki is the canonical home. (A `homepage: github.com/adom-inc/adom-desktop` is a stale default to fix.)+- **`homepage`** — a **`wiki.adom.inc`** URL (your own page, or `https://wiki.adom.inc/adom/adom-bridge` for the platform). **NEVER `github.com/...`** — GitHub is source-backup only; the wiki is the canonical home. (A `homepage: github.com/adom-inc/adom-bridge` is a stale default to fix.) - **`detect` / `prewarm`** — the v1.9.47 cold-start declarations (full rules in "Cold-start tiers" below). `detect` = your HOST APP (AD detects, never installs); `prewarm` = your heavy RUNTIME assets (AD prewarms/auto-installs). A node bridge that ships its deps still works without `prewarm`; declare `prewarm.assets` only for big *downloaded* assets like a browser. - **`timeouts`** (v1.9.79) — per-verb HTTP budgets (seconds) AD honors so a SLOW verb (a big export, a cloud open, a long render) isn't cut off before it finishes. Resolution: exact `verbs` (BARE names, no prefix) → first matching `prefixes` → `default` → AD's global 60s. A timeout is **NON-terminal**: AD returns `{stillRunning:true, statusVerb, timeoutSeconds}` and the caller polls your `statusVerb` (so ALSO declare `statusVerb`). **⚠ Before v1.9.79 a third-party bridge was hard-capped at 60s with no way to raise it** — if any of your verbs can run longer, declare `timeouts` (this is the fix). AD reads it with ZERO AD change; the two bundled EDA bridges keep a labeled built-in table only until they declare their own. @@ -493,19 +493,19 @@ This is the code AD streams onto the user's machine. Two files on your page:  1. **A versioned Release `.zip`** of your bridge dir (server + `bridge.json` at the zip ROOT + any assets). Ship it as a **Release** (the downloadable, version-pinned artifact), not a loose `/files` blob:    ```bash-   adom-wiki release create adom/adom-desktop-<bridge>-bridge <version> --title "..." --changelog "..."-   adom-wiki release upload adom/adom-desktop-<bridge>-bridge <version> your-bridge-v<version>.zip --platform any+   adom-wiki release create adom/adom-bridge-<bridge>-bridge <version> --title "..." --changelog "..."+   adom-wiki release upload adom/adom-bridge-<bridge>-bridge <version> your-bridge-v<version>.zip --platform any    ```    (Node bridges: the zip carries SOURCE only — keep `node_modules` out; AD/the seed handle deps. See Prewarm.) 2. **The manifest JSON** (e.g. `blender-manifest.json`) — identity + where the zip is — POSTed to your page `/files`:    ```jsonc    { "name":"blender", "version":"1.2.0",-     "url":"https://wiki.adom.inc/download/adom/adom-desktop-blender-bridge/1.2.0/blender-v1.2.0.zip",  // base-relative, extension-agnostic (.bin works where wiki blocks .zip)+     "url":"https://wiki.adom.inc/download/adom/adom-bridge-blender-bridge/1.2.0/blender-v1.2.0.zip",  // base-relative, extension-agnostic (.bin works where wiki blocks .zip)      "sha256":"…", "size":123456, "verbPrefixes":["blender_"], "healthEndpoint":"/health",-     "updateManifestUrl":"https://wiki.adom.inc/api/v1/pages/adom-desktop-blender-bridge/files/blender-manifest.json" }+     "updateManifestUrl":"https://wiki.adom.inc/api/v1/pages/adom-bridge-blender-bridge/files/blender-manifest.json" }    ``` -**Users install/update** with AD's `bridge_install {manifestUrl}` → AD downloads the zip, sha256-verifies, unzips into `%LOCALAPPDATA%\Adom Desktop\bridges-cache\<name>\`, rescans, and remembers the URL for auto-update.+**Users install/update** with AD's `bridge_install {manifestUrl}` → AD downloads the zip, sha256-verifies, unzips into `%LOCALAPPDATA%\Adom Bridge\bridges-cache\<name>\`, rescans, and remembers the URL for auto-update.  **To ship a new version:** bump `version` in BOTH `bridge.json` (in the zip) and the manifest, re-`release upload` the zip, re-POST the manifest. Users auto-get it on the next 4h poll, or immediately via `refresh_bridges {name}`. **A bundled SEED** (a `plugins/<name>/` folder shipped in AD's NSIS) is the offline/first-run fallback; your published cache copy with a higher `version` supersedes it (cache-over-bundled, version-aware). ⚠ **A `release upload` REPLACES a same-platform asset ONLY when the FILENAME is IDENTICAL** — a different name (a version bump `-v1.2.0` → `-v1.3.0`) ADDS a second asset, leaving a stale zip on the release. Keep ONE asset per release: overwrite the same filename, or `adom-wiki release delete-asset <owner/slug> <ver> "<old-filename>"` after a rename. (A stale extra won't break the manifest-URL install, but it clutters the release.) @@ -519,14 +519,14 @@ Your runtime runs on the user's DESKTOP; the AI driving it runs in a cloud/WSL2  - **The pkg carries ONLY your USER skill(s).** Each is a real `skills/<name>/SKILL.md`. A ROOT `SKILL.md` (required by the registry) is the pack entry point: `user-invocable: true`, a `description` full of trigger words. Large surface → a core user skill + user sub-skills, but **every skill in the pkg is a *user* skill.** - **DEV + PUBLISH skills do NOT go in the pkg — they live in your source repo** (a developer gets them by cloning to edit the bridge). Do NOT list them in `files[]`, and do NOT "scope" them in with `user-invocable:false` (that still ships them and bloats every container install).-- **Declare `dependencies:{"adom/adom-desktop":"^1.9.x"}`** in `package.json` — `pkg install` resolves it npm-style, so installing your bridge's skill pkg also pulls the AD CLI + core skills the container needs to drive you.-- **Each sub-skill's body opens with a `Parent skill:` line** (first line after the frontmatter): `Parent skill: **adom-desktop-<bridge>-bridge**`.+- **Declare `dependencies:{"adom/adom-bridge":"^1.9.x"}`** in `package.json` — `pkg install` resolves it npm-style, so installing your bridge's skill pkg also pulls the AD CLI + core skills the container needs to drive you.+- **Each sub-skill's body opens with a `Parent skill:` line** (first line after the frontmatter): `Parent skill: **adom-bridge-<bridge>-bridge**`. - **List every user-skill path EXPLICITLY — no globs — in `package.json` `files[]` AND `install.sh`/`uninstall.sh`** (one `install_skill <dest> <src>` line each, kept in sync). The publish linter REQUIRES explicit listing. (The old "use `skills/**`" advice is dead.)  ```bash # Stage dir: ROOT SKILL.md + skills/<name>/SKILL.md per USER skill (dev/publish stay in the repo),-# package.json {name:"adom-desktop-<bridge>-bridge", version, discovery_triggers,-#               dependencies:{"adom/adom-desktop":"^1.9.x"},+# package.json {name:"adom-bridge-<bridge>-bridge", version, discovery_triggers,+#               dependencies:{"adom/adom-bridge":"^1.9.x"}, #               files:[ every USER-skill path, explicitly ], scripts:{install,uninstall}}. # TWO traps that still bite — verify each with `tar tzf` before publishing: #  1. ⚠ pkg pack/publish HONORS the stage dir's .gitignore — a gitignored skills/ ships@@ -535,14 +535,14 @@ Your runtime runs on the user's DESKTOP; the AI driving it runs in a cloud/WSL2 #     before guards). Skills + docs only. adom-wiki pkg pack                       # then: tar tzf <tgz> → every skills/<name>/SKILL.md                                          #   listed in files[] present, ZERO binaries-adom-wiki pkg publish --org adom         # publishes adom/adom-desktop-<bridge>-bridge+adom-wiki pkg publish --org adom         # publishes adom/adom-bridge-<bridge>-bridge ```  **`skill_count` on your page counts the USER skills your published PKG carries** — so it should be SMALL (1 for a single-skill bridge, a handful for a core + sub-skills). If it looks inflated, you probably shipped dev/publish skills that belong in the repo instead. The metric is NOT "SKILL.md files in the repo," so a `type:skill` page can read `skill_count: 0` while its repo holds many — don't chase parity across page types (wiki-side quirk).  A container gets it **without anyone asking**: - `bridge_install` returns `skillPkg` + `installSkill` (AD derives `adom/<slug>` from your `docs`) and the `_hint` tells the AI to run it.-- **`adom-desktop sync_skills`** installs every connected desktop's bridge skillPkgs (the CLI also runs it once/day in the background), so a container that connects a week later self-heals your skill.+- **`adom-bridge-cli sync_skills`** installs every connected desktop's bridge skillPkgs (the CLI also runs it once/day in the background), so a container that connects a week later self-heals your skill. - The wiki page's **Skills tab** lists the skills your pkg ships, so the page itself advertises them.  **Install to BOTH agent homes.** A container may run Claude OR Codex — your pkg's `install.sh` must deploy the user skill to **`~/.claude/skills/`** AND **`~/.codex/skills/`** (Codex is a real second skills home now). `adom-wiki pkg update` keeps both fresh.@@ -553,7 +553,7 @@ Your page carries **two artifacts that look interchangeable and are not**, and u  **So state it, in ONE plain sentence, in both your package `description` and your README.** Keep this line, or something equivalent: -> This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Desktop loads the bridge runtime itself from the release zip.+> This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Bridge loads the bridge runtime itself from the release zip.  **Keep it to one sentence.** The package `description` renders as your page subtitle AND the text under the Install card, so a multi-paragraph architecture essay there actively hurts: it buries the one fact a visitor needs. (AD-core overshot exactly this and had to trim it back.) The long version belongs in your README body, not the card. @@ -565,7 +565,7 @@ Your page carries **two artifacts that look interchangeable and are not**, and u  This is the single easiest way to hurt every one of your users, and it is invisible from your machine. -**The real case (fusion, caught 2026-07-19):** `adom/adom-desktop-fusion-bridge`'s release zip was **16.7 MB, of which ~15.3 MB was demo MP4s, screenshots and repo docs**. Every user's machine streamed that on `bridge_install`, and again on every auto-update. Stripped to runtime-only it is **456 KB — 97% smaller — with zero functional change.**+**The real case (fusion, caught 2026-07-19):** `adom/fusion-bridge`'s release zip was **16.7 MB, of which ~15.3 MB was demo MP4s, screenshots and repo docs**. Every user's machine streamed that on `bridge_install`, and again on every auto-update. Stripped to runtime-only it is **456 KB — 97% smaller — with zero functional change.**  Nobody was careless. The zip was built from `git ls-files` (or by zipping the repo), which sweeps in exactly the things that legitimately belong on your WIKI PAGE: heroes, screenshots, demo videos, architecture diagrams, long-form docs. @@ -607,14 +607,14 @@ Media on the page is served to a browser when someone looks at it. Media in an a The wiki has an **auto-discover** index, and it is the difference between a usable bridge and an **invisible** one. Declare `discovery_triggers` (the phrases a user actually says) on your page, and a plain request matches them via `adom-wiki discover find` / the in-app discovery an AI runs.  ```bash-adom-wiki discover triggers adom/adom-desktop-<bridge>-bridge --add "open my pcb" "export gerbers"  # add triggers+adom-wiki discover triggers adom/adom-bridge-<bridge>-bridge --add "open my pcb" "export gerbers"  # add triggers adom-wiki discover preview --json "open my pcb design"          # what would surface + the SCORE + WHY adom-wiki discover find    "screenshot my website"              # the live find an AI runs ```  **How scoring actually works (measured 2026-06-30 — know this or you'll guess wrong):** - `score = 3×(distinct triggers matched) + 1×(tags) + 1×(brief/text)`. **Trigger matches dominate (3× each);** tags/title/brief barely move it.-- **NO TRIGGERS = INVISIBLE.** A bridge with a perfect title ("Adom Desktop - Blender Bridge") + a matching tag scored **ZERO** for the literal query *"blender"* — it never appeared in `discover preview`. The scorer ignores title/tags when there are no triggers. **Every shipped bridge MUST carry `discovery_triggers` or it cannot be found by the discovery an AI uses.** (`discover search` full-texts the readme, but that is NOT the path discovery takes.)+- **NO TRIGGERS = INVISIBLE.** A bridge with a perfect title ("Adom Bridge - Blender Bridge") + a matching tag scored **ZERO** for the literal query *"blender"* — it never appeared in `discover preview`. The scorer ignores title/tags when there are no triggers. **Every shipped bridge MUST carry `discovery_triggers` or it cannot be found by the discovery an AI uses.** (`discover search` full-texts the readme, but that is NOT the path discovery takes.) - **More good triggers = higher rank.** Each match is +3, so 10 user-task phrasings beat 2. The healthy bundled bridges carry **30-40** triggers; a struggling one had 14 — all dev jargon — and lost EVERY user query.  **The cardinal rule: triggers are USER-TASK phrasings, NOT dev jargon.** A real user types *"open my pcb design"* / *"export gerber files"* / *"design a circuit board"* — NOT *"reverse bridge"* / *"kicad_bridge_call"* / *"fork the kicad bridge"*. A trigger list full of architecture terms scores 0 on every real query (kicad's actual failure: 7/7 user queries missed, and the **Fusion** bridge out-ranked KiCad on PCB queries because Fusion happened to carry a couple of fuzzy "pcb"/"export" triggers). Put **10+ everyday task phrasings first**; technical aliases (verb names) are a small minority at the end.@@ -627,13 +627,13 @@ adom-wiki discover find    "screenshot my website"              # the live find **Claim YOUR niche — don't poach a sibling's territory.** Discovery routes by whose triggers match; if two bridges match a query the user lands on whichever has *more* triggers, NOT whichever is actually right. So your triggers must describe what YOU are genuinely best at — and you must LEAVE OUT cases a sibling owns. **Canonical example: logging into a site / filling a form on a signed-in page is the native-browser extension's job — it drives the user's REAL browser with their REAL logins — NOT puppeteer's.** Pup is a fresh Chrome-for-Testing profile with no saved logins; it's for *non-login* automation (scraping, screenshots, testing, public pages). So pup must NOT carry `log in` / `fill a form on a site I'm signed into` triggers, and native-browser MUST. When a task needs a capability you DON'T have, make your `discovery_pitch` *point at the bridge that does* — and tell the user to install it (e.g. "for logged-in form-filling, install the native-browser extension") — rather than claim it and do it badly.  - **`discovery_pitch`** is the one-liner the snippet quotes back to the AI — auto-generated from `discovery_triggers` + `discovery_pitch`, **no separate upload step.** Make it an INSTRUCTION, not a tagline.-- **Make your `brief` / `discovery_pitch` an instruction** — it's what the AI reads from the find result. Tell it the next moves: *"…check `adom-desktop bridge_list` to confirm the bridge is installed; if it is, drive its `<prefix>_*` verbs. If the container lacks the skill, `adom-wiki pkg install adom/adom-desktop-<bridge>-bridge` (or `adom-desktop sync_skills`)."*+- **Make your `brief` / `discovery_pitch` an instruction** — it's what the AI reads from the find result. Tell it the next moves: *"…check `adom-bridge-cli bridge_list` to confirm the bridge is installed; if it is, drive its `<prefix>_*` verbs. If the container lacks the skill, `adom-wiki pkg install adom/adom-bridge-<bridge>-bridge` (or `adom-bridge-cli sync_skills`)."*  ### The end-to-end flow your three artifacts enable  User says *"show me my app in pup"* → the AI: 1. `adom-wiki discover find "show me my app in pup"` → your bridge surfaces (Artifact 3).-2. `adom-desktop bridge_list` → is your bridge installed in AD? If not, `bridge_install {your manifestUrl}` (Artifact 1).+2. `adom-bridge-cli bridge_list` → is your bridge installed in AD? If not, `bridge_install {your manifestUrl}` (Artifact 1). 3. Is your skill pkg installed in THIS container (`~/.claude/skills/` or `~/.codex/skills/`)? If not, `adom-wiki pkg install adom/<slug>` / `sync_skills` (Artifact 2). 4. Drive your verbs — guided by the **rich hints you return** (the principle at the top). @@ -666,7 +666,7 @@ A cold first call that pays multi-minute setup (npm install, downloading a runti  ## Cold-start tiers, host-app detection & the `bridge_readiness` probe (v1.9.47) -A bridge has TWO kinds of dependency, and **AD treats them OPPOSITELY.** Get this right or a first-install probe (e.g. Hydrogen's "what tools do I have") triggers a slow install at the worst moment.+A bridge has TWO kinds of dependency, and **AD treats them OPPOSITELY.** Get this right or a first-install probe (e.g. HD's "what tools do I have") triggers a slow install at the worst moment.  **Tier 1 — your RUNTIME (Node, Python, your own browser download): AD prewarms + auto-installs it.** This is your plumbing. On first boot AD **provisions** Node/Python in the background — a **portable, no-UAC** copy under `~/.adom/adom-runtimes/` (or a system install if one's on PATH), **NOT winget/MSI** (see **Runtime contract** above) — and prewarms heavy assets you declare. You may also download your own runtime asset (pup downloads Chrome-for-Testing) — fine, it's yours. **But do it in the BACKGROUND: a `*_status` / `*_readiness` / ANY probe verb must NEVER block on a heavy download.** Spawn your server fast (it usually doesn't need the asset to *start*), kick the download off detached, and report `state:"warming"` until ready. A synchronous-download-on-first-verb is the exact footgun this release fixed (pup's 150 MB Chrome fetch used to block the probe). @@ -678,7 +678,7 @@ A bridge has TWO kinds of dependency, and **AD treats them OPPOSITELY.** Get thi  | Level | Bridge(s) | Behavior | |---|---|---|-| **0 · Built-in / MUST** | Adom Hydrogen (`hydrogen`) | The platform itself — the editor AD lives in. Always present; not "installed" by AD. |+| **0 · Built-in / MUST** | Hydrogen Desktop (`hd`) | The platform itself — the editor AD lives in. Always present; not "installed" by AD. | | **1 · Fundamental — pre-emptive** | Puppeteer (`pup`) | Browser automation is foundational to Adom. AD pre-installs its RUNTIME (Node + Chrome-for-Testing) ASAP on first boot, no asking. | | **2 · Recommended — proactive offer** | native-browser (the browser EXTENSION half) | The user's REAL logged-in browser (login/forms). Since v1.9.142 the DESKTOP BRIDGE half ships bundled in the AD installer (0.2 MB, zero deps, `persistent:true` — its extension-free verbs like `nbrowser_open_os_window`/`nbrowser_profiles`/`nbrowser_os_windows` work day one). What remains level-2 is the in-browser EXTENSION: AD/the bridge proactively SUGGESTS it and it installs on a yes (a browser-extension step the user confirms), unlocking full in-page drive. | | **3 · On-request host app** | KiCad, Fusion, Altium, … | Detect by default; NEVER pre-install (huge app, rude unasked). The moment the user wants it, the AI installs it AUTOMATICALLY (winget / `desktop_install_*`) — NEVER tells the user to do it manually. |@@ -720,7 +720,7 @@ Don't cram everything into one file. Ship **three kinds** of skill, each with a **Start from the templates — don't write these from scratch.** Fill-in-the-blanks files for each of the three skill kinds, generalized from pup's battle-tested set. **Fetch them from THIS page** (they used to be cited as a path inside AD's own repo, which is private, so the pointer was unfollowable for exactly the cloud authors it addressed — fixed 2026-07-25):  ```bash-BASE=https://wiki.adom.inc/api/v1/pages/adom-desktop-bridges/files+BASE=https://wiki.adom.inc/api/v1/pages/adom-bridge-sdk/files curl -fsSL $BASE/bridge-dev-template.md        -o dev-skills/<bridge>-dev.md curl -fsSL $BASE/bridge-publish-template.md    -o dev-skills/<bridge>-publish.md curl -fsSL $BASE/bridge-user-skill-template.md -o skills/<bridge>/SKILL.md@@ -730,7 +730,7 @@ curl -fsSL $BASE/bridge-user-skill-template.md -o skills/<bridge>/SKILL.md - **`bridge-publish-template.md`** — the whole release/pkg/discovery dance and every trap (the `.gitignore` set-aside, the lean-tarball rule, release-and-pkg-ship-together). - **`bridge-user-skill-template.md`** — the consumer skill, already encoding all eight conventions below plus the `--ai-thread` identity line. -Copy the one you need, swap `<bridge>`/`<prefix>` for your names, delete what doesn't apply. **How your skills reach a container in the first place** (the runtime installs on the desktop, the skills install in the container, and the two travel by different routes): [`docs/bridge-skill-propagation.md`](https://wiki.adom.inc/api/v1/pages/adom-desktop-bridges/files/docs/bridge-skill-propagation.md) on this page.+Copy the one you need, swap `<bridge>`/`<prefix>` for your names, delete what doesn't apply. **How your skills reach a container in the first place** (the runtime installs on the desktop, the skills install in the container, and the two travel by different routes): [`docs/bridge-skill-propagation.md`](https://wiki.adom.inc/api/v1/pages/adom-bridge-sdk/files/docs/bridge-skill-propagation.md) on this page.  --- @@ -777,7 +777,7 @@ Every bridge writes to stdout, AD captures it, and nobody asks the volume questi  *(READING the log is the section immediately above: `bridge_log_read` plus AD's separate lifecycle audit. This section is about what you WRITE.)* -**Do NOT write your own parallel log file.** Two log files means two sources of truth and double the disk for the same bytes. pup built one (a rotated `pup.log`) and deliberately reverted it. If you think you need your own log, what you probably want is for AD to scope `bridge_log_read` to your bridge: file that against `adom/adom-desktop` (see "Filing issues against AD" above) instead of forking the log.+**Do NOT write your own parallel log file.** Two log files means two sources of truth and double the disk for the same bytes. pup built one (a rotated `pup.log`) and deliberately reverted it. If you think you need your own log, what you probably want is for AD to scope `bridge_log_read` to your bridge: file that against `adom/adom-bridge` (see "Filing issues against AD" above) instead of forking the log.  **The five patterns that keep a bridge out of firehose territory:** @@ -796,11 +796,11 @@ Every bridge writes to stdout, AD captures it, and nobody asks the volume questi  ## Publish checklist (the whole modern flow) -1. **Page** — `adom-wiki page create adom/adom-desktop-<bridge>-bridge` with a hero + an **instructional `brief`** + **`discovery_triggers`**.+1. **Page** — `adom-wiki page create adom/adom-bridge-<bridge>-bridge` with a hero + an **instructional `brief`** + **`discovery_triggers`**. 2. **Runtime** — `release create` + `release upload` the versioned `.zip`; POST the manifest to `/files`; set `updateManifestUrl` in BOTH the zip's `bridge.json` and the manifest. **Before uploading: `unzip -l <zip> | sort -k1 -n -r | head` — runtime only, no media.** *(→ Payload rules)* 3. **Skills pkg** — stage ROOT SKILL.md + `skills/<slug>/SKILL.md` (core USER skill + sub-skills) + `discovery_triggers`; `pkg pack` (**`tar tzf` it — skills only, zero media/binaries**) → `pkg publish --org adom` (mind the `.gitignore` trap). Your `description` carries the one-sentence "this installs SKILLS, not the runtime" line. *(→ Artifact 2)* 4. **Discovery** — `discover triggers` to confirm; `discover preview "<a real user phrase>"` to confirm you surface. 5. **Re-seed AD's bundled copy** (AD-core thread, not you) — extract your Release zip over `plugins/<name>/` keeping bundled deps; ships in the next NSIS as the offline fallback. 6. **Verify** — `bridge_install {manifestUrl}` on a desktop → `bridge_list` shows your `version` + `skillPkg`; `bridge_check_updates` reaches your page; a container `sync_skills` pulls your USER skill; `discover find "<phrase>"` surfaces you with an instructional brief. -**Reference bridge = pup** (`adom/adom-desktop-puppeteer-bridge`): live `discovery_triggers`, a skills pkg with a core + sub-skills, a Release zip + manifest with `updateManifestUrl`, a bundled seed with prewarmed `node_modules`, and rich per-verb hints. Copy its shape. (`scripts/sample-bridges/hello-python` + `hello-rust` are the minimal skeletons.) KiCad, Fusion, Blender, Puppeteer, and the **native-browser** bridge (in the `adom-browser-extension` repo) are cloud-owned — don't edit them from the AD repo. When an AD release changes a bridge-facing contract **— or when THIS SDK doc itself changes —** AD posts a migration notice to **each** of their wiki discussions (`adom-desktop-{kicad,fusion,blender,puppeteer}-bridge` + `adom-browser-extension`). **Author-facing takeaway: watch your bridge's own wiki DISCUSSION/issues list — that's where an "SDK changed, please re-audit against the billboard" notice lands, and where you reply with your plan or blockers.** It's the only channel AD uses to reach you; AD never edits your repo.+**Reference bridge = pup** (`adom/adom-desktop-puppeteer-bridge`): live `discovery_triggers`, a skills pkg with a core + sub-skills, a Release zip + manifest with `updateManifestUrl`, a bundled seed with prewarmed `node_modules`, and rich per-verb hints. Copy its shape. (`scripts/sample-bridges/hello-python` + `hello-rust` are the minimal skeletons.) KiCad, Fusion, Blender, Puppeteer, and the **native-browser** bridge (in the `adom-browser-extension` repo) are cloud-owned — don't edit them from the AD repo. When an AD release changes a bridge-facing contract **— or when THIS SDK doc itself changes —** AD posts a migration notice to **each** of their wiki discussions (`adom-bridge-{kicad,fusion,blender,puppeteer}-bridge` + `adom-browser-extension`). **Author-facing takeaway: watch your bridge's own wiki DISCUSSION/issues list — that's where an "SDK changed, please re-audit against the billboard" notice lands, and where you reply with your plan or blockers.** It's the only channel AD uses to reach you; AD never edits your repo.
bridges-SDK.md+52−52
@@ -1,9 +1,9 @@ ----name: adom-desktop-bridge-sdk-description: Author, PUBLISH, and make-discoverable an adom-desktop bridge — a small local server that adds a namespace of verbs (kicad_, fusion_, browser_, your_prefix_) to adom-desktop. Use when building, publishing, updating, prewarming, or debugging a bridge, or deciding what skills to ship. Covers the AD-core-vs-author boundary; the #1 rule (rich hints in EVERY verb's OUTPUT, because the AI reads CLI output, never your on-disk skill); bridge.json; where every file lives (the Release-zip-vs-skills-pkg split, the TWO package.json files [server deps in the zip; the pkg declares dependencies:{adom/adom-desktop}], manifest + runtime-asset placement); the pkg carries ONLY user skills while dev + publish skills live in your source repo (not the pkg); the THREE things you publish (runtime Release zip + manifest, container skills pkg, wiki discovery_triggers); prewarm; and the auto-discover → install-check → use flow.+name: adom-bridge-bridge-sdk+description: Author, PUBLISH, and make-discoverable an adom-bridge bridge — a small local server that adds a namespace of verbs (kicad_, fusion_, browser_, your_prefix_) to adom-bridge. Use when building, publishing, updating, prewarming, or debugging a bridge, or deciding what skills to ship. Covers the AD-core-vs-author boundary; the #1 rule (rich hints in EVERY verb's OUTPUT, because the AI reads CLI output, never your on-disk skill); bridge.json; where every file lives (the Release-zip-vs-skills-pkg split, the TWO package.json files [server deps in the zip; the pkg declares dependencies:{adom/adom-bridge}], manifest + runtime-asset placement); the pkg carries ONLY user skills while dev + publish skills live in your source repo (not the pkg); the THREE things you publish (runtime Release zip + manifest, container skills pkg, wiki discovery_triggers); prewarm; and the auto-discover → install-check → use flow. --- -# Bridge SDK — build, publish, and surface a bridge for Adom Desktop+# Bridge SDK — build, publish, and surface a bridge for Adom Bridge  A **bridge** is a small local program (Python, Node, Rust, anything) that AD spawns and routes a **namespace of verbs** to over HTTP. KiCad, Fusion 360, Puppeteer (pup), native-browser, Blender are bridges. The modern flow has THREE moving parts you own — get all three right and a cloud/WSL2 container can *discover* your bridge from a plain-English request, confirm it's installed, and drive it with zero tribal knowledge: @@ -29,9 +29,9 @@ A **bridge** is a small local program (Python, Node, Rust, anything) that AD spa | **Installed by** | `bridge_install {manifestUrl}` → AD streams, unzips, spawns | `pkg install adom/<slug>` / `sync_skills` → `~/.claude/skills` + `~/.codex/skills` | | **Contains** | server code + `bridge.json` at the zip **ROOT** + assets. **Node: SOURCE ONLY, no `node_modules`.** | ONLY: ROOT `SKILL.md`, `skills/<name>/SKILL.md` (user skills), `package.json`, `install.sh`, `uninstall.sh`. **Text only — the whole tarball should be a few KB.** | | **NEVER contains** | the skills pkg | **ANY image (hero / screenshots — those are WIKI PAGE assets, see below), any binary, the Release zip, `src/`, `node_modules`, dev/publish skills, or the server's npm deps.** If `tar tzf` shows a `.png`/`.jpg`/`.zip`/`.exe`, you bundled bloat — remove it. |-| **its `package.json`** | (node) the **bridge SERVER's runtime deps** AD installs at spawn: `dependencies:{ puppeteer, … }` | the **pkg manifest**: `files[]` (user skills), `discovery_triggers`, scripts, **AND `dependencies:{"adom/adom-desktop":"^1.9.x"}`** (the container needs the AD CLI + core skills to drive you) — but NOT the server's npm deps |+| **its `package.json`** | (node) the **bridge SERVER's runtime deps** AD installs at spawn: `dependencies:{ puppeteer, … }` | the **pkg manifest**: `files[]` (user skills), `discovery_triggers`, scripts, **AND `dependencies:{"adom/adom-bridge":"^1.9.x"}`** (the container needs the AD CLI + core skills to drive you) — but NOT the server's npm deps | -**Yes, a node bridge has TWO `package.json` files and they are NOT interchangeable.** One rides inside the Release zip and lists the **bridge server's** runtime deps (puppeteer, sharp, ...) that AD installs on the *desktop* at spawn. The other is the **pkg manifest** in the skills tarball: it lists your user-skill `files[]` + `discovery_triggers` AND declares **`dependencies:{"adom/adom-desktop":"^1.9.x"}`** — `pkg install` resolves that npm-style, so installing your bridge's skill pkg also pulls the adom-desktop CLI + core skills the container needs to drive you. What the pkg manifest must NOT list is the **server's** npm deps (those are desktop-side, in the zip) — put them there and you bloat every container install, since the container never runs your server.+**Yes, a node bridge has TWO `package.json` files and they are NOT interchangeable.** One rides inside the Release zip and lists the **bridge server's** runtime deps (puppeteer, sharp, ...) that AD installs on the *desktop* at spawn. The other is the **pkg manifest** in the skills tarball: it lists your user-skill `files[]` + `discovery_triggers` AND declares **`dependencies:{"adom/adom-bridge":"^1.9.x"}`** — `pkg install` resolves that npm-style, so installing your bridge's skill pkg also pulls the adom-bridge CLI + core skills the container needs to drive you. What the pkg manifest must NOT list is the **server's** npm deps (those are desktop-side, in the zip) — put them there and you bloat every container install, since the container never runs your server.  **Where each file physically lives (this is the map that ends the inconsistency):** - **`bridge.json`** → the **root of the Release zip** (AD reads it post-unzip). Carries `version` + `updateManifestUrl`.@@ -48,7 +48,7 @@ A **bridge** is a small local program (Python, Node, Rust, anything) that AD spa | **DEV** | a maintainer editing the bridge | your **source repo** | **NO, never** | only if your repo is public | | **PUBLISH** | a maintainer releasing the bridge | your **source repo** | **NO, never** | only if your repo is public | -**The one rule:** the pkg tarball carries **USER skills only**; **DEV + PUBLISH skills live in the source repo.** Do NOT "scope" dev/publish into the pkg with `user-invocable:false` — that still ships them (pure bloat). Open-vs-closed source only sets your repo's *visibility*; it never changes what's in the pkg. adom-desktop core does exactly this: user/integration skills in the pkg; `RELEASE` / `WIKI_PUBLISH` / `SIGNING` / `AD_RELAUNCH` live only in the source repo.+**The one rule:** the pkg tarball carries **USER skills only**; **DEV + PUBLISH skills live in the source repo.** Do NOT "scope" dev/publish into the pkg with `user-invocable:false` — that still ships them (pure bloat). Open-vs-closed source only sets your repo's *visibility*; it never changes what's in the pkg. adom-bridge core does exactly this: user/integration skills in the pkg; `RELEASE` / `WIKI_PUBLISH` / `SIGNING` / `AD_RELAUNCH` live only in the source repo.  --- @@ -84,10 +84,10 @@ curl -s -X POST "$ADOM_DIRECT_API_URL/command" -H 'Content-Type: application/jso  ### Notify the user AND get their answer back (`notify_user` — two separate axes) -Your bridge can prompt the user with a native toast and read their reply — the same `notify_user` verb the AI uses. It has **two independent axes; don't conflate them** (full ref: the `adom-desktop-notify` skill / `skills/NOTIFY.md`):+Your bridge can prompt the user with a native toast and read their reply — the same `notify_user` verb the AI uses. It has **two independent axes; don't conflate them** (full ref: the `adom-bridge-notify` skill / `skills/NOTIFY.md`):  - **Axis 1 — get the answer back.** There is no server-push, so either **`wait:true`** (the `notify_user` call blocks and returns the user's choice inline — best for a one-shot prompt) or fire-and-continue then poll **`notify_events {sinceSeq}`** (drains every click) / **`notify_response {id}`**. The click is recorded durably, so it survives even on a headless box.-- **Axis 2 — reveal a window (`focus`).** A LOCAL desktop action, not a reply. `focus:{hwnd|cacheKey|titleContains|app|host}` raises a window on the user's screen when they click. **`focus:{app:"<your-exe>"}`** points the click at a window YOUR bridge drives; **`focus:{host:true}`** points it at the app AD is embedded under (Adom Hydrogen). Pass `focus` whenever the toast is *about* a window, so the click lands there instead of opening AD.+- **Axis 2 — reveal a window (`focus`).** A LOCAL desktop action, not a reply. `focus:{hwnd|cacheKey|titleContains|app|host}` raises a window on the user's screen when they click. **`focus:{app:"<your-exe>"}`** points the click at a window YOUR bridge drives; **`focus:{host:true}`** points it at the app AD is embedded under (Hydrogen Desktop). Pass `focus` whenever the toast is *about* a window, so the click lands there instead of opening AD.  ```bash # Ask, and get the answer back inline:@@ -109,7 +109,7 @@ A user runs roughly **20 AI threads against one desktop**. Until v1.9.180 AD rel - No bridge log could **attribute a verb** to the thread that issued it, so interference was impossible to reconstruct afterwards. - Any agent's `bridge_kill` **destroyed every other agent's work**, silently, with no way for anyone to see who did it. -Filed as wiki issue `adom/adom-desktop#34` by AdityaAngajala, with the pup half at `adom/adom-desktop-puppeteer-bridge#22`.+Filed as wiki issue `adom/adom-bridge#34` by AdityaAngajala, with the pup half at `adom/adom-desktop-puppeteer-bridge#22`.  ### What you now receive, for free @@ -127,7 +127,7 @@ AD stamps three headers on **every relayed bridge request** — all four request  These values are **SELF-ASSERTED**, exactly like `reason`. Use them for **attribution, logging, UX and arbitration hints**. **NEVER for authorization, and never as a security boundary.** -AD's own security model is transport-based (the `adom-desktop-permissions` skill has the full model): a caller is trusted because of *how it reached AD*, not because of what it *says about itself*. A header a caller supplies about itself cannot gate anything — if you write `if (thread === "admin-tab") allowDangerousThing()`, you have written a hole, not a check.+AD's own security model is transport-based (the `adom-bridge-permissions` skill has the full model): a caller is trusted because of *how it reached AD*, not because of what it *says about itself*. A header a caller supplies about itself cannot gate anything — if you write `if (thread === "admin-tab") allowDangerousThing()`, you have written a hole, not a check.  One asymmetry worth knowing: the container name in `X-Adom-Caller-Container` is the one the **caller claimed**. AD separately knows a **verified** container (the authenticated relay connection name) and shows the user **both** when they disagree. You only get the claimed one, so treat it as a label, not an identity. @@ -177,7 +177,7 @@ The recommended posture: - **Refuse only for destructive operations** — closing, resetting, navigating away, deleting. - **Always name the current owner in the message.** The whole point is that the second agent can now go coordinate with a thread it can identify. An error that says "session busy" without a name is barely better than no header at all. -**3. Label the UI.** If your bridge drives a visible window, put the owning thread where the **user** can see it — window title, taskbar tooltip, or jump-list name — via `desktop_set_window_identity` (and `desktop_taskbar`'s `thumbnailTooltip` for uncapped hover text). The user is the one with 20 tabs open; showing which tab owns which window is the fix for their side of the problem. Full guide: the `adom-desktop-window-identity` skill, and the "Branding + driving a window you borrowed" section immediately below.+**3. Label the UI.** If your bridge drives a visible window, put the owning thread where the **user** can see it — window title, taskbar tooltip, or jump-list name — via `desktop_set_window_identity` (and `desktop_taskbar`'s `thumbnailTooltip` for uncapped hover text). The user is the one with 20 tabs open; showing which tab owns which window is the fix for their side of the problem. Full guide: the `adom-bridge-window-identity` skill, and the "Branding + driving a window you borrowed" section immediately below.  **4. Report it back.** Include the owning thread in your **status / list verbs**, so an agent can *discover* it is not alone before it stomps on something: @@ -242,14 +242,14 @@ Useful to understand, though none of it is your responsibility:  ```bash # Per call — the CORRECT mechanism:-adom-desktop --ai-thread "chip-fetcher tab 3" <verb> '<json>'+adom-bridge-cli --ai-thread "chip-fetcher tab 3" <verb> '<json>' ``` -The `adom-desktop` CLI attaches a `caller` block to **every** verb automatically. A relay call with **no `aiThread` is REFUSED by AD** with `errorCode: caller_identity_required` before it ever reaches a bridge.+The `adom-bridge` CLI attaches a `caller` block to **every** verb automatically. A relay call with **no `aiThread` is REFUSED by AD** with `errorCode: caller_identity_required` before it ever reaches a bridge.  > **Correction, 2026-07-25 (AD 1.9.182):** an earlier version of this page told callers to `export ADOM_AI_THREAD` once per session. **That advice was wrong and has been withdrawn.** Roughly 20 AI threads share one container and one `$HOME`, so the env var scopes to the shell environment, not to the thread — every thread then reports the *same* name, which is worse than no name at all because it is confident and plausible. It also failed silently: tool shells are non-interactive, so an export appended below `~/.bashrc`'s `case $- in *i*` early-return never ran. The env var is still honored, but only fits a container running exactly one agent (CI, a single-purpose job). **If your bridge's own docs repeat the export advice, please correct them.** -**So by the time a request reaches you, identity is normally present.** The exception is **loopback callers** — Hydrogen, another bridge, or the local CLI — which are trusted by transport and not yet *required* to identify (that gate tightens once bridges adopt the forwarding contract above). Code for the absent case (`|| 'unidentified'`), never assume the header exists.+**So by the time a request reaches you, identity is normally present.** The exception is **loopback callers** — HD, another bridge, or the local CLI — which are trusted by transport and not yet *required* to identify (that gate tightens once bridges adopt the forwarding contract above). Code for the absent case (`|| 'unidentified'`), never assume the header exists.  ### If your bridge wants to REQUIRE identity — use the shared contract @@ -259,7 +259,7 @@ Some verbs are worth refusing anonymously. If you do, **use AD's exact refusal s { "success": false, "errorCode": "caller_identity_required",   "error": "'<verb>' needs to know WHO is asking — the user runs many AI threads and needs to see which one drove this.",   "supplyIdentity": {-    "bestWay": "adom-desktop --ai-thread \"<your conversation name>\" <verb> '<json>'",+    "bestWay": "adom-bridge-cli --ai-thread \"<your conversation name>\" <verb> '<json>'",     "orInArgs": { "caller": { "aiThread": "<name>", "containerName": "<container>" } }   },   "_hint": "This is attribution, not a permission check — the name is self-reported and nothing is gated on it." }@@ -283,7 +283,7 @@ Reusing `caller_identity_required` verbatim is the point: an AI that learned the  ## Branding + driving a window you borrowed (window identity + taskbar, v1.9.106–1.9.153) -If your bridge drives a window another process owns — the classic case is a **Chrome for Testing** window that otherwise reads *"Test"* with Chrome's icon — AD gives you a generic verb family to make it present as YOUR app across every Windows shell surface, and to show live status on its taskbar button. AD stays generic: it applies whatever name/icon/command you pass; it knows nothing about your app. **Full guide: the `adom-desktop-window-identity` skill** (`https://wiki.adom.inc/api/v1/pages/adom-desktop/files/docs/WINDOW_IDENTITY.md` or the repo `skills/WINDOW_IDENTITY.md`). The essentials:+If your bridge drives a window another process owns — the classic case is a **Chrome for Testing** window that otherwise reads *"Test"* with Chrome's icon — AD gives you a generic verb family to make it present as YOUR app across every Windows shell surface, and to show live status on its taskbar button. AD stays generic: it applies whatever name/icon/command you pass; it knows nothing about your app. **Full guide: the `adom-bridge-window-identity` skill** (`https://wiki.adom.inc/api/v1/pages/adom-bridge/files/docs/WINDOW_IDENTITY.md` or the repo `skills/WINDOW_IDENTITY.md`). The essentials:  - **Two halves, you need both:** `desktop_register_app_identity {appId, displayName, iconPath, shortcut}` teaches Windows what an AUMID means (HKCU only, no UAC; `shortcut:false` for a per-session id with no Start Menu spam), and `desktop_set_window_identity {hwnd, appId, iconPath, displayName, relaunchCommand}` stamps it on the live window. Stamp without register → Alt-Tab / taskbar fall back to the owning exe's icon. - **Per-session identity:** register `shortcut:false` per window (e.g. `Adom.Pup.<sessionId>`) so each keeps its own taskbar button; `desktop_unregister_app_identity {appId}` on close.@@ -317,7 +317,7 @@ If your bridge drives a window another process owns — the classic case is a ** This SDK evolves. When it does, AD posts a re-audit notice to **YOUR bridge's wiki discussion** and re-publishes this billboard. You do NOT need a human to hand you a prompt — self-serve at the start of a work session:  1. **Check your discussion for a notice.** `adom-wiki discussion list <your-slug>` (or open your page's Discussions tab) and read any "SDK update" / "re-audit" thread. A notice can be EDITED after posting (a correction), so always read the **current** body, not a cached earlier version.-2. **Re-read this billboard**, especially "Where every file lives" (above) and the "Self-audit checklist" (below): `https://wiki.adom.inc/adom/adom-desktop-bridges` (or `curl -s https://wiki.adom.inc/api/v1/pages/adom-desktop-bridges/files/README.md`).+2. **Re-read this billboard**, especially "Where every file lives" (above) and the "Self-audit checklist" (below): `https://wiki.adom.inc/adom/adom-bridge-sdk` (or `curl -s https://wiki.adom.inc/api/v1/pages/adom-bridge-sdk/files/README.md`). 3. **Run the Self-audit checklist** (next section) against your code, `bridge.json`, skills, README, and wiki page. Fix every miss. 4. **Reply on the discussion** with what you changed or any blockers. That thread is the ONLY channel AD uses to reach you, so closing the loop there is how AD knows you are current. @@ -338,7 +338,7 @@ So make the invitation part of your verb output, the same way `_hint` is. This i   "success": false,   "errorCode": "…",   "_hint": "…",-  "_reportIssues": "THIS MAY BE A <BRIDGE> BUG. PLEASE REPORT IT. Check for a duplicate with `adom-wiki issue list adom/<your-slug>`, then file: `adom-wiki issue create adom/<your-slug> --title \"<one-line symptom>\" --category bug-report --body \"<what you ran, what you expected, what happened, the VERBATIM response JSON, the bridge version from bridge_list, and the OS>\"`. Reports from real machines are the only way this bridge gets correct. (An AD-core problem, such as the relay, bridge lifecycle or caller identity, belongs on adom/adom-desktop instead.)"+  "_reportIssues": "THIS MAY BE A <BRIDGE> BUG. PLEASE REPORT IT. Check for a duplicate with `adom-wiki issue list adom/<your-slug>`, then file: `adom-wiki issue create adom/<your-slug> --title \"<one-line symptom>\" --category bug-report --body \"<what you ran, what you expected, what happened, the VERBATIM response JSON, the bridge version from bridge_list, and the OS>\"`. Reports from real machines are the only way this bridge gets correct. (An AD-core problem, such as the relay, bridge lifecycle or caller identity, belongs on adom/adom-bridge instead.)" } ``` @@ -354,7 +354,7 @@ A line on **every** response is noise that trains the caller to ignore the field  **Make it actionable, not a slogan.** "Report bugs" produces nothing. Give the AI the exact `adom-wiki issue create` command, your slug, a `--category`, and the list of evidence to include (the verbatim response JSON and your bridge version are the two that make a report reproducible). Tell it to check for a duplicate first: an `issue list` call costs nothing and keeps your page clean. -**Route the report to the repo that owns the code.** Say explicitly, in the same string, that AD-core problems (the relay and `/command` passthrough, spawn and provisioning, bridge lifecycle, caller identity) go to **`adom/adom-desktop`**, not to your page. Authors get this wrong in the other direction too, which is the next section: a misfiled issue is a note to yourself that the people who can fix it never read.+**Route the report to the repo that owns the code.** Say explicitly, in the same string, that AD-core problems (the relay and `/command` passthrough, spawn and provisioning, bridge lifecycle, caller identity) go to **`adom/adom-bridge`**, not to your page. Authors get this wrong in the other direction too, which is the next section: a misfiled issue is a note to yourself that the people who can fix it never read.  **Let the user turn it off.** Ship a settings toggle (pup uses `browser_configure {reportIssues:"off"}`) so a user who finds it noisy can silence it without losing functionality. Default it **on**: the cost is a few tokens on failures, and the benefit is the bug class you would otherwise ship to every user. @@ -369,14 +369,14 @@ You WILL find AD-core bugs and gaps while building your bridge. That is expected **The rule: an issue lives on the issue list of the repo that OWNS the code that must change.**  - A bug in YOUR bridge's verb / hint / self-heal / status chip goes to **YOUR page's issue list** (or you just fix it, since you own it).-- A missing or broken **AD-CORE** capability (a lifecycle verb, a spawn / provision / bind behavior, the relay passthrough dropping a field, a manifest field AD must honor, the status-chip contract) goes to **`adom/adom-desktop`**, NOT your own page. AD-core's maintainers and AI watch `adom/adom-desktop`; they do NOT read your bridge page's issues. **An AD ask filed on your own page is a note to yourself.**+- A missing or broken **AD-CORE** capability (a lifecycle verb, a spawn / provision / bind behavior, the relay passthrough dropping a field, a manifest field AD must honor, the status-chip contract) goes to **`adom/adom-bridge`**, NOT your own page. AD-core's maintainers and AI watch `adom/adom-bridge`; they do NOT read your bridge page's issues. **An AD ask filed on your own page is a note to yourself.** - A **wiki or CLI** bug goes to `adom/wiki`. A **sibling bridge's** bug goes to that bridge's page.  **How (the CLI):**  ```bash-adom-wiki issue list   adom/adom-desktop            # FIRST: skim for a dup, comment if one exists-adom-wiki issue create adom/adom-desktop \+adom-wiki issue list   adom/adom-bridge            # FIRST: skim for a dup, comment if one exists+adom-wiki issue create adom/adom-bridge \   --title "<verb or subsystem>: <the gap in one line>" \   --category bug-report \   --body "$(cat your-writeup.md)"@@ -386,7 +386,7 @@ adom-wiki issue create adom/adom-desktop \  **After you file:** the AD-core thread replies on that issue. Read the reply, it may change your plan (for example "fixed in AD X.Y, bump your pin"). That issue thread is the cross-repo channel; keep the loop closed there. -**Why this matters most for AD:** AD-core spawns you, provisions your runtime, relays your verbs, and installs you, so AD is the repo you will file against most often. Get the destination right the first time. The established precedent all sits on `adom/adom-desktop`: #26 and #27 (pup to AD, Chrome for Testing), #16 (pup to AD, window-verb interception), #3 / #20 / #21 (Bridge SDK asks).+**Why this matters most for AD:** AD-core spawns you, provisions your runtime, relays your verbs, and installs you, so AD is the repo you will file against most often. Get the destination right the first time. The established precedent all sits on `adom/adom-bridge`: #26 and #27 (pup to AD, Chrome for Testing), #16 (pup to AD, window-verb interception), #3 / #20 / #21 (Bridge SDK asks).  --- @@ -402,7 +402,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against  **Artifacts & deps** (the two-artifact split — the #1 inconsistency) *(→ Where every file lives)*: - [ ] Your Release `.zip` is a **Release asset** (`release upload`), NOT a loose `/files` blob — and old version zips are NOT piling up on `/files` (audit for stray `*-v*.zip`).-- [ ] The **server's** runtime deps live in the **ZIP's** `package.json` (node) / `requirements.txt` (python). The **pkg's** `package.json` declares `dependencies:{"adom/adom-desktop":"^1.9.x"}` (so `pkg install` pulls the AD CLI + core skills) and does NOT list the server's npm deps.+- [ ] The **server's** runtime deps live in the **ZIP's** `package.json` (node) / `requirements.txt` (python). The **pkg's** `package.json` declares `dependencies:{"adom/adom-bridge":"^1.9.x"}` (so `pkg install` pulls the AD CLI + core skills) and does NOT list the server's npm deps. - [ ] A big runtime asset (Mesa, a browser) is a **`prewarm.asset`** or seed-bundled — NOT a loose `/files` blob. - [ ] Your **manifest** (`adom-bridge-<name>-manifest.json`) is on `/files`; its `url` points at the **Release download**, not at a `/files` blob. - [ ] **PAYLOAD:** `unzip -l <your zip> | sort -k1 -n -r | head` shows **runtime only** — no `.mp4`/`.png`/`.jpg`/screenshots/CHANGELOG/`.github`. A runtime zip is normally well under 1 MB; if yours is bigger, justify every large entry. `tar tzf <your tgz>` shows **skills only** — zero media, zero binaries. Media belongs on the PAGE. *(→ Payload rules)*@@ -427,7 +427,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against **Skills & docs** (what the container + humans read): - [ ] Your pkg ships **ONLY your USER skill(s)** — a real `skills/<name>/SKILL.md` (core user skill + user sub-skills) with a `Parent skill:` first body line; every path listed EXPLICITLY (no globs) in `files[]` + install.sh/uninstall.sh. *(→ Artifact 2 + The SKILL SET)* - [ ] **DEV + PUBLISH skills are in your source REPO, NOT the pkg** — not in `files[]`, not scoped in via `user-invocable:false`. A developer gets them by cloning the source; the normal user never needs them. Open-vs-closed source is irrelevant (it only sets repo visibility). *(→ Where every file lives)*-- [ ] Your pkg's `package.json` declares `dependencies:{"adom/adom-desktop":"^1.9.x"}` so `pkg install` pulls the AD CLI + core skills the container needs. *(→ Where every file lives)*+- [ ] Your pkg's `package.json` declares `dependencies:{"adom/adom-bridge":"^1.9.x"}` so `pkg install` pulls the AD CLI + core skills the container needs. *(→ Where every file lives)* - [ ] Your USER skill leads with the cold-start "read before you panic" section + the 8 conventions, and points the AI at `bridge_readiness`. *(→ USER-skill conventions)* - [ ] README + wiki page + skills all AGREE with the above (no stale github links, no wrong schema, triggers vetted). @@ -444,7 +444,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against   "description": "Drive Blender headless + GUI",   "author": "you",   "license": "MIT",-  "docs": "https://wiki.adom.inc/adom/adom-desktop-blender-bridge",  // your OWN page (drives skillPkg + hero)+  "docs": "https://wiki.adom.inc/adom/adom-bridge-blender-bridge",  // your OWN page (drives skillPkg + hero)   "hero": "https://.../hero.png",           // optional; else AD uses your wiki page hero, else a monogram. Ship it at 1.6 aspect (≈2000×1250) — see "Card hero + update cost"   "spawn": {     "kind": "python",                       // python | node | exe | external-http  (NOT "process")@@ -458,7 +458,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against   "verbPrefixes": ["blender_"],             // any verb with these prefixes routes to you   "verbs": ["blender_render", "blender_status", "blender_describe"],  // every verb (routing + Verbs tab)   "statusVerb": "blender_status",           // verb a caller polls after a long op times out-  "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-desktop-blender-bridge/files/blender-manifest.json",+  "updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-bridge-blender-bridge/files/blender-manifest.json",    "timeouts": {                             // v1.9.79 — per-verb HTTP budgets (seconds); see note below     "default": 60,                          //   fallback for any verb not matched (absent → AD's global 60s)@@ -481,7 +481,7 @@ When a maintainer says *"audit your code, skills, README, and wiki page against - **`spawn.healthEndpoint`** lives **inside `spawn`** (not at the top level), default `/health`. - **`updateManifestUrl`** — the full URL of YOUR manifest on YOUR page. **Declare it in `bridge.json`** so it survives a republish; AD polls it (on launch + every 4h) and auto-updates users. (AD also remembers the install URL in `~/.adom/bridge-install-urls.json` as a fallback, but the declared field is the durable, author-intended path.) - **`docs`** — point at YOUR dedicated wiki page. AD derives your **skillPkg** (`adom/<slug>`) and your card hero from this. Don't point it at a shared/structural page.-- **`homepage`** — a **`wiki.adom.inc`** URL (your own page, or `https://wiki.adom.inc/adom/adom-desktop` for the platform). **NEVER `github.com/...`** — GitHub is source-backup only; the wiki is the canonical home. (A `homepage: github.com/adom-inc/adom-desktop` is a stale default to fix.)+- **`homepage`** — a **`wiki.adom.inc`** URL (your own page, or `https://wiki.adom.inc/adom/adom-bridge` for the platform). **NEVER `github.com/...`** — GitHub is source-backup only; the wiki is the canonical home. (A `homepage: github.com/adom-inc/adom-bridge` is a stale default to fix.) - **`detect` / `prewarm`** — the v1.9.47 cold-start declarations (full rules in "Cold-start tiers" below). `detect` = your HOST APP (AD detects, never installs); `prewarm` = your heavy RUNTIME assets (AD prewarms/auto-installs). A node bridge that ships its deps still works without `prewarm`; declare `prewarm.assets` only for big *downloaded* assets like a browser. - **`timeouts`** (v1.9.79) — per-verb HTTP budgets (seconds) AD honors so a SLOW verb (a big export, a cloud open, a long render) isn't cut off before it finishes. Resolution: exact `verbs` (BARE names, no prefix) → first matching `prefixes` → `default` → AD's global 60s. A timeout is **NON-terminal**: AD returns `{stillRunning:true, statusVerb, timeoutSeconds}` and the caller polls your `statusVerb` (so ALSO declare `statusVerb`). **⚠ Before v1.9.79 a third-party bridge was hard-capped at 60s with no way to raise it** — if any of your verbs can run longer, declare `timeouts` (this is the fix). AD reads it with ZERO AD change; the two bundled EDA bridges keep a labeled built-in table only until they declare their own. @@ -493,19 +493,19 @@ This is the code AD streams onto the user's machine. Two files on your page:  1. **A versioned Release `.zip`** of your bridge dir (server + `bridge.json` at the zip ROOT + any assets). Ship it as a **Release** (the downloadable, version-pinned artifact), not a loose `/files` blob:    ```bash-   adom-wiki release create adom/adom-desktop-<bridge>-bridge <version> --title "..." --changelog "..."-   adom-wiki release upload adom/adom-desktop-<bridge>-bridge <version> your-bridge-v<version>.zip --platform any+   adom-wiki release create adom/adom-bridge-<bridge>-bridge <version> --title "..." --changelog "..."+   adom-wiki release upload adom/adom-bridge-<bridge>-bridge <version> your-bridge-v<version>.zip --platform any    ```    (Node bridges: the zip carries SOURCE only — keep `node_modules` out; AD/the seed handle deps. See Prewarm.) 2. **The manifest JSON** (e.g. `blender-manifest.json`) — identity + where the zip is — POSTed to your page `/files`:    ```jsonc    { "name":"blender", "version":"1.2.0",-     "url":"https://wiki.adom.inc/download/adom/adom-desktop-blender-bridge/1.2.0/blender-v1.2.0.zip",  // base-relative, extension-agnostic (.bin works where wiki blocks .zip)+     "url":"https://wiki.adom.inc/download/adom/adom-bridge-blender-bridge/1.2.0/blender-v1.2.0.zip",  // base-relative, extension-agnostic (.bin works where wiki blocks .zip)      "sha256":"…", "size":123456, "verbPrefixes":["blender_"], "healthEndpoint":"/health",-     "updateManifestUrl":"https://wiki.adom.inc/api/v1/pages/adom-desktop-blender-bridge/files/blender-manifest.json" }+     "updateManifestUrl":"https://wiki.adom.inc/api/v1/pages/adom-bridge-blender-bridge/files/blender-manifest.json" }    ``` -**Users install/update** with AD's `bridge_install {manifestUrl}` → AD downloads the zip, sha256-verifies, unzips into `%LOCALAPPDATA%\Adom Desktop\bridges-cache\<name>\`, rescans, and remembers the URL for auto-update.+**Users install/update** with AD's `bridge_install {manifestUrl}` → AD downloads the zip, sha256-verifies, unzips into `%LOCALAPPDATA%\Adom Bridge\bridges-cache\<name>\`, rescans, and remembers the URL for auto-update.  **To ship a new version:** bump `version` in BOTH `bridge.json` (in the zip) and the manifest, re-`release upload` the zip, re-POST the manifest. Users auto-get it on the next 4h poll, or immediately via `refresh_bridges {name}`. **A bundled SEED** (a `plugins/<name>/` folder shipped in AD's NSIS) is the offline/first-run fallback; your published cache copy with a higher `version` supersedes it (cache-over-bundled, version-aware). ⚠ **A `release upload` REPLACES a same-platform asset ONLY when the FILENAME is IDENTICAL** — a different name (a version bump `-v1.2.0` → `-v1.3.0`) ADDS a second asset, leaving a stale zip on the release. Keep ONE asset per release: overwrite the same filename, or `adom-wiki release delete-asset <owner/slug> <ver> "<old-filename>"` after a rename. (A stale extra won't break the manifest-URL install, but it clutters the release.) @@ -519,14 +519,14 @@ Your runtime runs on the user's DESKTOP; the AI driving it runs in a cloud/WSL2  - **The pkg carries ONLY your USER skill(s).** Each is a real `skills/<name>/SKILL.md`. A ROOT `SKILL.md` (required by the registry) is the pack entry point: `user-invocable: true`, a `description` full of trigger words. Large surface → a core user skill + user sub-skills, but **every skill in the pkg is a *user* skill.** - **DEV + PUBLISH skills do NOT go in the pkg — they live in your source repo** (a developer gets them by cloning to edit the bridge). Do NOT list them in `files[]`, and do NOT "scope" them in with `user-invocable:false` (that still ships them and bloats every container install).-- **Declare `dependencies:{"adom/adom-desktop":"^1.9.x"}`** in `package.json` — `pkg install` resolves it npm-style, so installing your bridge's skill pkg also pulls the AD CLI + core skills the container needs to drive you.-- **Each sub-skill's body opens with a `Parent skill:` line** (first line after the frontmatter): `Parent skill: **adom-desktop-<bridge>-bridge**`.+- **Declare `dependencies:{"adom/adom-bridge":"^1.9.x"}`** in `package.json` — `pkg install` resolves it npm-style, so installing your bridge's skill pkg also pulls the AD CLI + core skills the container needs to drive you.+- **Each sub-skill's body opens with a `Parent skill:` line** (first line after the frontmatter): `Parent skill: **adom-bridge-<bridge>-bridge**`. - **List every user-skill path EXPLICITLY — no globs — in `package.json` `files[]` AND `install.sh`/`uninstall.sh`** (one `install_skill <dest> <src>` line each, kept in sync). The publish linter REQUIRES explicit listing. (The old "use `skills/**`" advice is dead.)  ```bash # Stage dir: ROOT SKILL.md + skills/<name>/SKILL.md per USER skill (dev/publish stay in the repo),-# package.json {name:"adom-desktop-<bridge>-bridge", version, discovery_triggers,-#               dependencies:{"adom/adom-desktop":"^1.9.x"},+# package.json {name:"adom-bridge-<bridge>-bridge", version, discovery_triggers,+#               dependencies:{"adom/adom-bridge":"^1.9.x"}, #               files:[ every USER-skill path, explicitly ], scripts:{install,uninstall}}. # TWO traps that still bite — verify each with `tar tzf` before publishing: #  1. ⚠ pkg pack/publish HONORS the stage dir's .gitignore — a gitignored skills/ ships@@ -535,14 +535,14 @@ Your runtime runs on the user's DESKTOP; the AI driving it runs in a cloud/WSL2 #     before guards). Skills + docs only. adom-wiki pkg pack                       # then: tar tzf <tgz> → every skills/<name>/SKILL.md                                          #   listed in files[] present, ZERO binaries-adom-wiki pkg publish --org adom         # publishes adom/adom-desktop-<bridge>-bridge+adom-wiki pkg publish --org adom         # publishes adom/adom-bridge-<bridge>-bridge ```  **`skill_count` on your page counts the USER skills your published PKG carries** — so it should be SMALL (1 for a single-skill bridge, a handful for a core + sub-skills). If it looks inflated, you probably shipped dev/publish skills that belong in the repo instead. The metric is NOT "SKILL.md files in the repo," so a `type:skill` page can read `skill_count: 0` while its repo holds many — don't chase parity across page types (wiki-side quirk).  A container gets it **without anyone asking**: - `bridge_install` returns `skillPkg` + `installSkill` (AD derives `adom/<slug>` from your `docs`) and the `_hint` tells the AI to run it.-- **`adom-desktop sync_skills`** installs every connected desktop's bridge skillPkgs (the CLI also runs it once/day in the background), so a container that connects a week later self-heals your skill.+- **`adom-bridge-cli sync_skills`** installs every connected desktop's bridge skillPkgs (the CLI also runs it once/day in the background), so a container that connects a week later self-heals your skill. - The wiki page's **Skills tab** lists the skills your pkg ships, so the page itself advertises them.  **Install to BOTH agent homes.** A container may run Claude OR Codex — your pkg's `install.sh` must deploy the user skill to **`~/.claude/skills/`** AND **`~/.codex/skills/`** (Codex is a real second skills home now). `adom-wiki pkg update` keeps both fresh.@@ -553,7 +553,7 @@ Your page carries **two artifacts that look interchangeable and are not**, and u  **So state it, in ONE plain sentence, in both your package `description` and your README.** Keep this line, or something equivalent: -> This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Desktop loads the bridge runtime itself from the release zip.+> This package installs the bridge's SKILLS into your container so your AI knows how to drive it; Adom Bridge loads the bridge runtime itself from the release zip.  **Keep it to one sentence.** The package `description` renders as your page subtitle AND the text under the Install card, so a multi-paragraph architecture essay there actively hurts: it buries the one fact a visitor needs. (AD-core overshot exactly this and had to trim it back.) The long version belongs in your README body, not the card. @@ -565,7 +565,7 @@ Your page carries **two artifacts that look interchangeable and are not**, and u  This is the single easiest way to hurt every one of your users, and it is invisible from your machine. -**The real case (fusion, caught 2026-07-19):** `adom/adom-desktop-fusion-bridge`'s release zip was **16.7 MB, of which ~15.3 MB was demo MP4s, screenshots and repo docs**. Every user's machine streamed that on `bridge_install`, and again on every auto-update. Stripped to runtime-only it is **456 KB — 97% smaller — with zero functional change.**+**The real case (fusion, caught 2026-07-19):** `adom/fusion-bridge`'s release zip was **16.7 MB, of which ~15.3 MB was demo MP4s, screenshots and repo docs**. Every user's machine streamed that on `bridge_install`, and again on every auto-update. Stripped to runtime-only it is **456 KB — 97% smaller — with zero functional change.**  Nobody was careless. The zip was built from `git ls-files` (or by zipping the repo), which sweeps in exactly the things that legitimately belong on your WIKI PAGE: heroes, screenshots, demo videos, architecture diagrams, long-form docs. @@ -607,14 +607,14 @@ Media on the page is served to a browser when someone looks at it. Media in an a The wiki has an **auto-discover** index, and it is the difference between a usable bridge and an **invisible** one. Declare `discovery_triggers` (the phrases a user actually says) on your page, and a plain request matches them via `adom-wiki discover find` / the in-app discovery an AI runs.  ```bash-adom-wiki discover triggers adom/adom-desktop-<bridge>-bridge --add "open my pcb" "export gerbers"  # add triggers+adom-wiki discover triggers adom/adom-bridge-<bridge>-bridge --add "open my pcb" "export gerbers"  # add triggers adom-wiki discover preview --json "open my pcb design"          # what would surface + the SCORE + WHY adom-wiki discover find    "screenshot my website"              # the live find an AI runs ```  **How scoring actually works (measured 2026-06-30 — know this or you'll guess wrong):** - `score = 3×(distinct triggers matched) + 1×(tags) + 1×(brief/text)`. **Trigger matches dominate (3× each);** tags/title/brief barely move it.-- **NO TRIGGERS = INVISIBLE.** A bridge with a perfect title ("Adom Desktop - Blender Bridge") + a matching tag scored **ZERO** for the literal query *"blender"* — it never appeared in `discover preview`. The scorer ignores title/tags when there are no triggers. **Every shipped bridge MUST carry `discovery_triggers` or it cannot be found by the discovery an AI uses.** (`discover search` full-texts the readme, but that is NOT the path discovery takes.)+- **NO TRIGGERS = INVISIBLE.** A bridge with a perfect title ("Adom Bridge - Blender Bridge") + a matching tag scored **ZERO** for the literal query *"blender"* — it never appeared in `discover preview`. The scorer ignores title/tags when there are no triggers. **Every shipped bridge MUST carry `discovery_triggers` or it cannot be found by the discovery an AI uses.** (`discover search` full-texts the readme, but that is NOT the path discovery takes.) - **More good triggers = higher rank.** Each match is +3, so 10 user-task phrasings beat 2. The healthy bundled bridges carry **30-40** triggers; a struggling one had 14 — all dev jargon — and lost EVERY user query.  **The cardinal rule: triggers are USER-TASK phrasings, NOT dev jargon.** A real user types *"open my pcb design"* / *"export gerber files"* / *"design a circuit board"* — NOT *"reverse bridge"* / *"kicad_bridge_call"* / *"fork the kicad bridge"*. A trigger list full of architecture terms scores 0 on every real query (kicad's actual failure: 7/7 user queries missed, and the **Fusion** bridge out-ranked KiCad on PCB queries because Fusion happened to carry a couple of fuzzy "pcb"/"export" triggers). Put **10+ everyday task phrasings first**; technical aliases (verb names) are a small minority at the end.@@ -627,13 +627,13 @@ adom-wiki discover find    "screenshot my website"              # the live find **Claim YOUR niche — don't poach a sibling's territory.** Discovery routes by whose triggers match; if two bridges match a query the user lands on whichever has *more* triggers, NOT whichever is actually right. So your triggers must describe what YOU are genuinely best at — and you must LEAVE OUT cases a sibling owns. **Canonical example: logging into a site / filling a form on a signed-in page is the native-browser extension's job — it drives the user's REAL browser with their REAL logins — NOT puppeteer's.** Pup is a fresh Chrome-for-Testing profile with no saved logins; it's for *non-login* automation (scraping, screenshots, testing, public pages). So pup must NOT carry `log in` / `fill a form on a site I'm signed into` triggers, and native-browser MUST. When a task needs a capability you DON'T have, make your `discovery_pitch` *point at the bridge that does* — and tell the user to install it (e.g. "for logged-in form-filling, install the native-browser extension") — rather than claim it and do it badly.  - **`discovery_pitch`** is the one-liner the snippet quotes back to the AI — auto-generated from `discovery_triggers` + `discovery_pitch`, **no separate upload step.** Make it an INSTRUCTION, not a tagline.-- **Make your `brief` / `discovery_pitch` an instruction** — it's what the AI reads from the find result. Tell it the next moves: *"…check `adom-desktop bridge_list` to confirm the bridge is installed; if it is, drive its `<prefix>_*` verbs. If the container lacks the skill, `adom-wiki pkg install adom/adom-desktop-<bridge>-bridge` (or `adom-desktop sync_skills`)."*+- **Make your `brief` / `discovery_pitch` an instruction** — it's what the AI reads from the find result. Tell it the next moves: *"…check `adom-bridge-cli bridge_list` to confirm the bridge is installed; if it is, drive its `<prefix>_*` verbs. If the container lacks the skill, `adom-wiki pkg install adom/adom-bridge-<bridge>-bridge` (or `adom-bridge-cli sync_skills`)."*  ### The end-to-end flow your three artifacts enable  User says *"show me my app in pup"* → the AI: 1. `adom-wiki discover find "show me my app in pup"` → your bridge surfaces (Artifact 3).-2. `adom-desktop bridge_list` → is your bridge installed in AD? If not, `bridge_install {your manifestUrl}` (Artifact 1).+2. `adom-bridge-cli bridge_list` → is your bridge installed in AD? If not, `bridge_install {your manifestUrl}` (Artifact 1). 3. Is your skill pkg installed in THIS container (`~/.claude/skills/` or `~/.codex/skills/`)? If not, `adom-wiki pkg install adom/<slug>` / `sync_skills` (Artifact 2). 4. Drive your verbs — guided by the **rich hints you return** (the principle at the top). @@ -666,7 +666,7 @@ A cold first call that pays multi-minute setup (npm install, downloading a runti  ## Cold-start tiers, host-app detection & the `bridge_readiness` probe (v1.9.47) -A bridge has TWO kinds of dependency, and **AD treats them OPPOSITELY.** Get this right or a first-install probe (e.g. Hydrogen's "what tools do I have") triggers a slow install at the worst moment.+A bridge has TWO kinds of dependency, and **AD treats them OPPOSITELY.** Get this right or a first-install probe (e.g. HD's "what tools do I have") triggers a slow install at the worst moment.  **Tier 1 — your RUNTIME (Node, Python, your own browser download): AD prewarms + auto-installs it.** This is your plumbing. On first boot AD **provisions** Node/Python in the background — a **portable, no-UAC** copy under `~/.adom/adom-runtimes/` (or a system install if one's on PATH), **NOT winget/MSI** (see **Runtime contract** above) — and prewarms heavy assets you declare. You may also download your own runtime asset (pup downloads Chrome-for-Testing) — fine, it's yours. **But do it in the BACKGROUND: a `*_status` / `*_readiness` / ANY probe verb must NEVER block on a heavy download.** Spawn your server fast (it usually doesn't need the asset to *start*), kick the download off detached, and report `state:"warming"` until ready. A synchronous-download-on-first-verb is the exact footgun this release fixed (pup's 150 MB Chrome fetch used to block the probe). @@ -678,7 +678,7 @@ A bridge has TWO kinds of dependency, and **AD treats them OPPOSITELY.** Get thi  | Level | Bridge(s) | Behavior | |---|---|---|-| **0 · Built-in / MUST** | Adom Hydrogen (`hydrogen`) | The platform itself — the editor AD lives in. Always present; not "installed" by AD. |+| **0 · Built-in / MUST** | Hydrogen Desktop (`hd`) | The platform itself — the editor AD lives in. Always present; not "installed" by AD. | | **1 · Fundamental — pre-emptive** | Puppeteer (`pup`) | Browser automation is foundational to Adom. AD pre-installs its RUNTIME (Node + Chrome-for-Testing) ASAP on first boot, no asking. | | **2 · Recommended — proactive offer** | native-browser (the browser EXTENSION half) | The user's REAL logged-in browser (login/forms). Since v1.9.142 the DESKTOP BRIDGE half ships bundled in the AD installer (0.2 MB, zero deps, `persistent:true` — its extension-free verbs like `nbrowser_open_os_window`/`nbrowser_profiles`/`nbrowser_os_windows` work day one). What remains level-2 is the in-browser EXTENSION: AD/the bridge proactively SUGGESTS it and it installs on a yes (a browser-extension step the user confirms), unlocking full in-page drive. | | **3 · On-request host app** | KiCad, Fusion, Altium, … | Detect by default; NEVER pre-install (huge app, rude unasked). The moment the user wants it, the AI installs it AUTOMATICALLY (winget / `desktop_install_*`) — NEVER tells the user to do it manually. |@@ -720,7 +720,7 @@ Don't cram everything into one file. Ship **three kinds** of skill, each with a **Start from the templates — don't write these from scratch.** Fill-in-the-blanks files for each of the three skill kinds, generalized from pup's battle-tested set. **Fetch them from THIS page** (they used to be cited as a path inside AD's own repo, which is private, so the pointer was unfollowable for exactly the cloud authors it addressed — fixed 2026-07-25):  ```bash-BASE=https://wiki.adom.inc/api/v1/pages/adom-desktop-bridges/files+BASE=https://wiki.adom.inc/api/v1/pages/adom-bridge-sdk/files curl -fsSL $BASE/bridge-dev-template.md        -o dev-skills/<bridge>-dev.md curl -fsSL $BASE/bridge-publish-template.md    -o dev-skills/<bridge>-publish.md curl -fsSL $BASE/bridge-user-skill-template.md -o skills/<bridge>/SKILL.md@@ -730,7 +730,7 @@ curl -fsSL $BASE/bridge-user-skill-template.md -o skills/<bridge>/SKILL.md - **`bridge-publish-template.md`** — the whole release/pkg/discovery dance and every trap (the `.gitignore` set-aside, the lean-tarball rule, release-and-pkg-ship-together). - **`bridge-user-skill-template.md`** — the consumer skill, already encoding all eight conventions below plus the `--ai-thread` identity line. -Copy the one you need, swap `<bridge>`/`<prefix>` for your names, delete what doesn't apply. **How your skills reach a container in the first place** (the runtime installs on the desktop, the skills install in the container, and the two travel by different routes): [`docs/bridge-skill-propagation.md`](https://wiki.adom.inc/api/v1/pages/adom-desktop-bridges/files/docs/bridge-skill-propagation.md) on this page.+Copy the one you need, swap `<bridge>`/`<prefix>` for your names, delete what doesn't apply. **How your skills reach a container in the first place** (the runtime installs on the desktop, the skills install in the container, and the two travel by different routes): [`docs/bridge-skill-propagation.md`](https://wiki.adom.inc/api/v1/pages/adom-bridge-sdk/files/docs/bridge-skill-propagation.md) on this page.  --- @@ -777,7 +777,7 @@ Every bridge writes to stdout, AD captures it, and nobody asks the volume questi  *(READING the log is the section immediately above: `bridge_log_read` plus AD's separate lifecycle audit. This section is about what you WRITE.)* -**Do NOT write your own parallel log file.** Two log files means two sources of truth and double the disk for the same bytes. pup built one (a rotated `pup.log`) and deliberately reverted it. If you think you need your own log, what you probably want is for AD to scope `bridge_log_read` to your bridge: file that against `adom/adom-desktop` (see "Filing issues against AD" above) instead of forking the log.+**Do NOT write your own parallel log file.** Two log files means two sources of truth and double the disk for the same bytes. pup built one (a rotated `pup.log`) and deliberately reverted it. If you think you need your own log, what you probably want is for AD to scope `bridge_log_read` to your bridge: file that against `adom/adom-bridge` (see "Filing issues against AD" above) instead of forking the log.  **The five patterns that keep a bridge out of firehose territory:** @@ -796,11 +796,11 @@ Every bridge writes to stdout, AD captures it, and nobody asks the volume questi  ## Publish checklist (the whole modern flow) -1. **Page** — `adom-wiki page create adom/adom-desktop-<bridge>-bridge` with a hero + an **instructional `brief`** + **`discovery_triggers`**.+1. **Page** — `adom-wiki page create adom/adom-bridge-<bridge>-bridge` with a hero + an **instructional `brief`** + **`discovery_triggers`**. 2. **Runtime** — `release create` + `release upload` the versioned `.zip`; POST the manifest to `/files`; set `updateManifestUrl` in BOTH the zip's `bridge.json` and the manifest. **Before uploading: `unzip -l <zip> | sort -k1 -n -r | head` — runtime only, no media.** *(→ Payload rules)* 3. **Skills pkg** — stage ROOT SKILL.md + `skills/<slug>/SKILL.md` (core USER skill + sub-skills) + `discovery_triggers`; `pkg pack` (**`tar tzf` it — skills only, zero media/binaries**) → `pkg publish --org adom` (mind the `.gitignore` trap). Your `description` carries the one-sentence "this installs SKILLS, not the runtime" line. *(→ Artifact 2)* 4. **Discovery** — `discover triggers` to confirm; `discover preview "<a real user phrase>"` to confirm you surface. 5. **Re-seed AD's bundled copy** (AD-core thread, not you) — extract your Release zip over `plugins/<name>/` keeping bundled deps; ships in the next NSIS as the offline fallback. 6. **Verify** — `bridge_install {manifestUrl}` on a desktop → `bridge_list` shows your `version` + `skillPkg`; `bridge_check_updates` reaches your page; a container `sync_skills` pulls your USER skill; `discover find "<phrase>"` surfaces you with an instructional brief. -**Reference bridge = pup** (`adom/adom-desktop-puppeteer-bridge`): live `discovery_triggers`, a skills pkg with a core + sub-skills, a Release zip + manifest with `updateManifestUrl`, a bundled seed with prewarmed `node_modules`, and rich per-verb hints. Copy its shape. (`scripts/sample-bridges/hello-python` + `hello-rust` are the minimal skeletons.) KiCad, Fusion, Blender, Puppeteer, and the **native-browser** bridge (in the `adom-browser-extension` repo) are cloud-owned — don't edit them from the AD repo. When an AD release changes a bridge-facing contract **— or when THIS SDK doc itself changes —** AD posts a migration notice to **each** of their wiki discussions (`adom-desktop-{kicad,fusion,blender,puppeteer}-bridge` + `adom-browser-extension`). **Author-facing takeaway: watch your bridge's own wiki DISCUSSION/issues list — that's where an "SDK changed, please re-audit against the billboard" notice lands, and where you reply with your plan or blockers.** It's the only channel AD uses to reach you; AD never edits your repo.+**Reference bridge = pup** (`adom/adom-desktop-puppeteer-bridge`): live `discovery_triggers`, a skills pkg with a core + sub-skills, a Release zip + manifest with `updateManifestUrl`, a bundled seed with prewarmed `node_modules`, and rich per-verb hints. Copy its shape. (`scripts/sample-bridges/hello-python` + `hello-rust` are the minimal skeletons.) KiCad, Fusion, Blender, Puppeteer, and the **native-browser** bridge (in the `adom-browser-extension` repo) are cloud-owned — don't edit them from the AD repo. When an AD release changes a bridge-facing contract **— or when THIS SDK doc itself changes —** AD posts a migration notice to **each** of their wiki discussions (`adom-bridge-{kicad,fusion,blender,puppeteer}-bridge` + `adom-browser-extension`). **Author-facing takeaway: watch your bridge's own wiki DISCUSSION/issues list — that's where an "SDK changed, please re-audit against the billboard" notice lands, and where you reply with your plan or blockers.** It's the only channel AD uses to reach you; AD never edits your repo.