app
DEPRECATED: Bridge SDK (moved into Adom Bridge)
Public Made by Adomby adom
Bridge SDK guide: bridge.json schema, kind:python/node/exe, hello-python + hello-rust reference templates, packaging + lifecycle commands.
← Commit history
SDK: line of demarcation (ab is an OS, your bridge is an app), the bridge keyword, and a PITFALLS section of eight real failures
3 files changed
+471
README.md+157@@ -24,6 +24,163 @@ A **bridge** is a small local program (Python, Node, Rust, anything) that ab spa --- +## ⛔ STOP — the LINE OF DEMARCATION: ab is an operating system, your bridge is an app++**If you remember one thing from this SDK: a bridge NEVER modifies ab. Every line of logic specific+to your app lives in YOUR bridge. The correct amount of ab-core change for a new bridge or a bridge+feature is ZERO.** This is not style — it is the architecture, and violating it breaks the thing that+makes bridges work.++**ab is an operating system for the desktop.** Like a kernel it does two jobs: it provides **generic,+app-agnostic primitives** (windows, screenshots, input, files, shell, WSL, taskbar/window identity,+jump lists, notifications, tunnels) that know nothing about any specific app — `desktop_set_window_identity`+does not know what a browser is, it applies whatever icon/name/AUMID *you* hand it — and it **loads,+unloads, reloads, and updates bridges on the fly** (`bridge_install`, the wiki auto-sync, hot respawn).+ab stays deliberately stable and generic so it almost never has to change for a new bridge.++**Your bridge is an app running on that OS** — a fluid, hot-loadable asset with its own process, its+own skills, its own wiki page, its own release cadence entirely independent of ab. It owns **100% of+the logic specific to its host app or tool**, and when it needs the desktop to *do* something it+**calls one of ab's existing generic verbs** over the loopback direct API (see `adom-bridge-sdk-callback`).+The payoff: your bridge ships and updates from your own page with **no ab release**. The moment your+code lives in ab you have chained your feature to ab's release train and made the kernel carry your+app's weight — the opposite of what a dynamic, loadable bridge is for.++### The three AI failure modes (all seen live, 2026-08-28)++A capable coding AI slides into all three. Guard against them explicitly:++1. **Reproducing capability ab already ships.** Before writing ANY desktop capability — and absolutely+ before proposing an ab change — search ab's existing verbs (`/verbs`, the `adom-bridge-verbs` skill,+ or just ask the ab thread). Real case: a bridge shipped a **2,668-line ab-core module** re-implementing+ window identity, taskbar overlay, and jump lists that ab **already provided generically** and another+ bridge was **already using**. The check is one grep. Do it.+2. **Baking your app's name into ab.** The same case hardcoded a forced `Adom.<App>.` AUMID namespace,+ app-named jump-list labels, and an app-named router verb into ab-core. **If ab would end up knowing+ your app's name, the line has been crossed.** Your app-specifics stay in your bridge, which passes+ its own appId / icon / labels *into* ab's generic verbs. **If you catch your AI editing `src-tauri/`, stop.**+3. **Pulling app-AWARE correctness logic UP into the host.** The subtlest one. Strict window binding —+ "bind this operation to an exact HWND + owner PID + process-start so I never act on the wrong window" —+ *feels* like it belongs in the kernel because it is about windows. It does not: doing it requires+ knowing **how your app creates its windows** (process/window/creation-time relationships, correlating+ a spawned browser to a session). That is narrow app knowledge and **ab must never have it.** Do the+ identify-and-verify work in YOUR bridge, then hand the resolved HWND + your identity to ab's dumb,+ generic paint verbs. **The bridge decides *which* window and proves it; ab never learns why.**++### The rare, legitimate exception — and the process++Sometimes a bridge genuinely needs a **generic ab primitive that does not exist yet.** That is real and+welcome. **What is gated is the CONTENT, not the channel:**++1. **Confirm it doesn't already exist.** Search the verb surface first. Most "I need a new verb" is+ "there's already a generic one."+2. **Apply the two-part test:** *could a completely unrelated app use this verb unchanged?* AND *can ab+ implement it knowing nothing app-specific?* **If either answer is no, it belongs in your bridge** — no+ channel makes it acceptable. When an existing generic verb is close but not strict enough for you, ask+ for a **generic hardening of that verb**; never fork an app-specific copy of it, and never ask ab to+ absorb app-aware logic (failure mode 3).+3. **Then pick the channel by who you are:**+ - **Adom team** — **just send a PR.** It is the efficient path and it is welcome. The PR must still+ pass the two-part test and touch only generic surface: a reviewer's first question is *"does ab now+ know anything about your app?"*+ - **Third-party author** — **file a discussion on the `adom-bridge` page** proposing the verb as a+ generic contract, and the ab thread adds it (or invites a PR once the contract is agreed).++> **Being on the team does not widen the line — it only shortens the path to proposing something that was+> already generic.** A team PR that adds an app-named namespace, app-named labels, or app-aware binding+> logic to ab-core gets the same answer as anyone else's: build it in the bridge.++### Your bridge's KEYWORD — the short word a user says++**Declare ONE short, unambiguous keyword and use it everywhere.** A user says *"open my app in **pup**"*+and the AI must resolve that single word, through ab's layers, to your skills and verbs. That only works+if the word is declared and consistent. Requirements:++- **Short and speakable** (`pup`, `kicad`, `pw`) — this is what a human types in chat, not your product name.+- **Unambiguous** — avoid a word that collides with a common term or another bridge's niche.+- **Consistent everywhere**: your `bridge.json` `name`, your **verb prefix** (`<keyword>_…`), your+ `discovery_triggers` phrasings, your README's opening line, and your card. If the keyword is `pw`, the+ verbs are `pw_*` and the README says so in the first paragraph.+- **State it explicitly** in your README's first section: *"Say **pw** to the AI to reach this bridge."*+ A keyword agreed in conversation but written nowhere does not exist.++---++## ⚠ PITFALLS — real failures from real bridges, and how to avoid each++Every entry here happened. They are listed in the order they tend to bite, and each is written as+**symptom → why it happens → fix**, because the ones that hurt are the ones that look fine while you+are making them.++### 1. Building your feature into ab instead of into your bridge++**Symptom:** you have a PR against `adom-bridge` for something your bridge needs. **Why:** the capability+sounds like a desktop primitive ("I need the taskbar to show my app"), so extending the host feels+natural — and if you never enumerated ab's existing verbs, it also feels *necessary*. **Fix:** the+capability is almost certainly already generic in ab (window identity, taskbar overlay, jump lists,+screenshots, input, files, notifications). Call it. See the LINE OF DEMARCATION above. *Measured cost of+getting this wrong: a 2,668-line ab-core module duplicating verbs another bridge was already using.*++### 2. Your app's name ending up in ab++**Symptom:** ab-core contains your app's name — a forced `Adom.<YourApp>.` namespace, app-named labels,+an app-named router verb. **Why:** you needed *some* identity string, and hardcoding it in the host was+the shortest path. **Fix:** every app-specific value is an ARGUMENT your bridge passes into a generic+verb. `grep -ri "<yourapp>" <ab>/src-tauri/src/` must return nothing.++### 3. Pulling app-AWARE correctness logic up into the host++**Symptom:** you want ab to do strict binding — "act only on the exact HWND + owner PID + process-start,+never the wrong window." **Why:** it is about windows, so it feels like kernel work. **Fix:** it is not —+doing it well requires knowing *how your app creates its windows* (process/window timing, correlating a+spawned process to a session), which is precisely the knowledge ab must not carry. Resolve and verify in+YOUR bridge; hand ab a resolved HWND for its dumb generic paint verbs. **The bridge decides *which*+window and proves it; ab never learns why.** This is the subtlest one on the list — it passes the+"is it generic?" sniff test and still fails the two-part test.++### 4. Shipping something nobody can RUN++**Symptom:** your work sits unreviewed and untried. **Why:** it is only runnable if someone builds and+runs a *modified core ab* — nobody will do that to try out a feature — or your releases are all+dev/updater-ineligible candidates, so `bridge_install` has nothing to resolve. **Fix:** a bridge's whole+value proposition is that **anyone can install it in one command and use it in a minute.** Ship at least+one qualified, updater-eligible release with its runtime `.zip`, and keep 100% of the feature inside the+bridge so trying it never requires touching ab. *A change people must review is worth far less than a+thing people can just use.*++### 5. Shipping something nobody can SEE++**Symptom:** your README is all prose; a reader cannot tell what your bridge does or whether it works.+**Why:** you can see it working on your own machine, so the gap is invisible to you. **Fix:** screenshots+of the real outcome, and for anything VISUAL (taskbar tiles, jump lists, overlays, rendered output)+**before/after** shots — prose cannot convey it and nobody can verify it otherwise. pup's README carries+13 images, kicad's 8, plus demo videos; a new bridge shipped **0** for an entirely visual feature and its+reviewer had no way to evaluate it at all. `desktop_screenshot_window` makes capturing this scriptable.+**This matters most for a bridge aimed at outside users: an internal bridge can lean on the author being+in the room, but for everyone else the page IS the product.**++### 6. A keyword that exists only in conversation++**Symptom:** you and a colleague agreed the bridge is "pw", but an AI never resolves it. **Why:** the word+was never written into `bridge.json`, the verb prefix, the discovery triggers, or the README. **Fix:**+declare it once and use it in all four places — see "Your bridge's KEYWORD" above.++### 7. Stale naming and a missing page kit++**Symptom:** your page reads as abandoned next to the other bridges. **Why:** slugs and copy written+before the rename (**Adom Bridge**, not "Adom Desktop"), no root `SKILL.md` (only a `skill-package/`+directory), no 1.6-aspect hero (so ab cover-crops the OG-card fallback and clips it), and none of the+standard kit (`install.sh`/`uninstall.sh`, `CHANGELOG.md`, `PUBLISHING.md`, billboard art). **Fix:** the+page-identity block of the self-audit checklist (`adom-bridge-sdk-audit`) is a literal punch list for this.++### 8. Assuming ab will forward what it never received++**Symptom:** you add an arg to a verb and it silently does nothing. **Why:** something in the chain+rebuilt the payload from a fixed allowlist instead of forwarding verbatim. **Fix:** round-trip test it —+send an unknown arg and have your bridge echo its received keys. Every arg you send must arrive.++---+ ## Where every file lives: the two artifacts, their deps, and which skills go where **Two DISTINCT artifacts, two DIFFERENT homes, two DIFFERENT consumers. Crossing them is the #1 inconsistency across bridge repos, so pin it here before anything else.** (Live audit 2026-07-06: fusion + pup do this right; kicad had **61** loose `adom-bridge-kicad-v*.zip` on `/files` + a `mesa-llvmpipe-x64.zip`; blender dumped 2 loose zips too.)
SKILL.md+157@@ -24,6 +24,163 @@ A **bridge** is a small local program (Python, Node, Rust, anything) that ab spa --- +## ⛔ STOP — the LINE OF DEMARCATION: ab is an operating system, your bridge is an app++**If you remember one thing from this SDK: a bridge NEVER modifies ab. Every line of logic specific+to your app lives in YOUR bridge. The correct amount of ab-core change for a new bridge or a bridge+feature is ZERO.** This is not style — it is the architecture, and violating it breaks the thing that+makes bridges work.++**ab is an operating system for the desktop.** Like a kernel it does two jobs: it provides **generic,+app-agnostic primitives** (windows, screenshots, input, files, shell, WSL, taskbar/window identity,+jump lists, notifications, tunnels) that know nothing about any specific app — `desktop_set_window_identity`+does not know what a browser is, it applies whatever icon/name/AUMID *you* hand it — and it **loads,+unloads, reloads, and updates bridges on the fly** (`bridge_install`, the wiki auto-sync, hot respawn).+ab stays deliberately stable and generic so it almost never has to change for a new bridge.++**Your bridge is an app running on that OS** — a fluid, hot-loadable asset with its own process, its+own skills, its own wiki page, its own release cadence entirely independent of ab. It owns **100% of+the logic specific to its host app or tool**, and when it needs the desktop to *do* something it+**calls one of ab's existing generic verbs** over the loopback direct API (see `adom-bridge-sdk-callback`).+The payoff: your bridge ships and updates from your own page with **no ab release**. The moment your+code lives in ab you have chained your feature to ab's release train and made the kernel carry your+app's weight — the opposite of what a dynamic, loadable bridge is for.++### The three AI failure modes (all seen live, 2026-08-28)++A capable coding AI slides into all three. Guard against them explicitly:++1. **Reproducing capability ab already ships.** Before writing ANY desktop capability — and absolutely+ before proposing an ab change — search ab's existing verbs (`/verbs`, the `adom-bridge-verbs` skill,+ or just ask the ab thread). Real case: a bridge shipped a **2,668-line ab-core module** re-implementing+ window identity, taskbar overlay, and jump lists that ab **already provided generically** and another+ bridge was **already using**. The check is one grep. Do it.+2. **Baking your app's name into ab.** The same case hardcoded a forced `Adom.<App>.` AUMID namespace,+ app-named jump-list labels, and an app-named router verb into ab-core. **If ab would end up knowing+ your app's name, the line has been crossed.** Your app-specifics stay in your bridge, which passes+ its own appId / icon / labels *into* ab's generic verbs. **If you catch your AI editing `src-tauri/`, stop.**+3. **Pulling app-AWARE correctness logic UP into the host.** The subtlest one. Strict window binding —+ "bind this operation to an exact HWND + owner PID + process-start so I never act on the wrong window" —+ *feels* like it belongs in the kernel because it is about windows. It does not: doing it requires+ knowing **how your app creates its windows** (process/window/creation-time relationships, correlating+ a spawned browser to a session). That is narrow app knowledge and **ab must never have it.** Do the+ identify-and-verify work in YOUR bridge, then hand the resolved HWND + your identity to ab's dumb,+ generic paint verbs. **The bridge decides *which* window and proves it; ab never learns why.**++### The rare, legitimate exception — and the process++Sometimes a bridge genuinely needs a **generic ab primitive that does not exist yet.** That is real and+welcome. **What is gated is the CONTENT, not the channel:**++1. **Confirm it doesn't already exist.** Search the verb surface first. Most "I need a new verb" is+ "there's already a generic one."+2. **Apply the two-part test:** *could a completely unrelated app use this verb unchanged?* AND *can ab+ implement it knowing nothing app-specific?* **If either answer is no, it belongs in your bridge** — no+ channel makes it acceptable. When an existing generic verb is close but not strict enough for you, ask+ for a **generic hardening of that verb**; never fork an app-specific copy of it, and never ask ab to+ absorb app-aware logic (failure mode 3).+3. **Then pick the channel by who you are:**+ - **Adom team** — **just send a PR.** It is the efficient path and it is welcome. The PR must still+ pass the two-part test and touch only generic surface: a reviewer's first question is *"does ab now+ know anything about your app?"*+ - **Third-party author** — **file a discussion on the `adom-bridge` page** proposing the verb as a+ generic contract, and the ab thread adds it (or invites a PR once the contract is agreed).++> **Being on the team does not widen the line — it only shortens the path to proposing something that was+> already generic.** A team PR that adds an app-named namespace, app-named labels, or app-aware binding+> logic to ab-core gets the same answer as anyone else's: build it in the bridge.++### Your bridge's KEYWORD — the short word a user says++**Declare ONE short, unambiguous keyword and use it everywhere.** A user says *"open my app in **pup**"*+and the AI must resolve that single word, through ab's layers, to your skills and verbs. That only works+if the word is declared and consistent. Requirements:++- **Short and speakable** (`pup`, `kicad`, `pw`) — this is what a human types in chat, not your product name.+- **Unambiguous** — avoid a word that collides with a common term or another bridge's niche.+- **Consistent everywhere**: your `bridge.json` `name`, your **verb prefix** (`<keyword>_…`), your+ `discovery_triggers` phrasings, your README's opening line, and your card. If the keyword is `pw`, the+ verbs are `pw_*` and the README says so in the first paragraph.+- **State it explicitly** in your README's first section: *"Say **pw** to the AI to reach this bridge."*+ A keyword agreed in conversation but written nowhere does not exist.++---++## ⚠ PITFALLS — real failures from real bridges, and how to avoid each++Every entry here happened. They are listed in the order they tend to bite, and each is written as+**symptom → why it happens → fix**, because the ones that hurt are the ones that look fine while you+are making them.++### 1. Building your feature into ab instead of into your bridge++**Symptom:** you have a PR against `adom-bridge` for something your bridge needs. **Why:** the capability+sounds like a desktop primitive ("I need the taskbar to show my app"), so extending the host feels+natural — and if you never enumerated ab's existing verbs, it also feels *necessary*. **Fix:** the+capability is almost certainly already generic in ab (window identity, taskbar overlay, jump lists,+screenshots, input, files, notifications). Call it. See the LINE OF DEMARCATION above. *Measured cost of+getting this wrong: a 2,668-line ab-core module duplicating verbs another bridge was already using.*++### 2. Your app's name ending up in ab++**Symptom:** ab-core contains your app's name — a forced `Adom.<YourApp>.` namespace, app-named labels,+an app-named router verb. **Why:** you needed *some* identity string, and hardcoding it in the host was+the shortest path. **Fix:** every app-specific value is an ARGUMENT your bridge passes into a generic+verb. `grep -ri "<yourapp>" <ab>/src-tauri/src/` must return nothing.++### 3. Pulling app-AWARE correctness logic up into the host++**Symptom:** you want ab to do strict binding — "act only on the exact HWND + owner PID + process-start,+never the wrong window." **Why:** it is about windows, so it feels like kernel work. **Fix:** it is not —+doing it well requires knowing *how your app creates its windows* (process/window timing, correlating a+spawned process to a session), which is precisely the knowledge ab must not carry. Resolve and verify in+YOUR bridge; hand ab a resolved HWND for its dumb generic paint verbs. **The bridge decides *which*+window and proves it; ab never learns why.** This is the subtlest one on the list — it passes the+"is it generic?" sniff test and still fails the two-part test.++### 4. Shipping something nobody can RUN++**Symptom:** your work sits unreviewed and untried. **Why:** it is only runnable if someone builds and+runs a *modified core ab* — nobody will do that to try out a feature — or your releases are all+dev/updater-ineligible candidates, so `bridge_install` has nothing to resolve. **Fix:** a bridge's whole+value proposition is that **anyone can install it in one command and use it in a minute.** Ship at least+one qualified, updater-eligible release with its runtime `.zip`, and keep 100% of the feature inside the+bridge so trying it never requires touching ab. *A change people must review is worth far less than a+thing people can just use.*++### 5. Shipping something nobody can SEE++**Symptom:** your README is all prose; a reader cannot tell what your bridge does or whether it works.+**Why:** you can see it working on your own machine, so the gap is invisible to you. **Fix:** screenshots+of the real outcome, and for anything VISUAL (taskbar tiles, jump lists, overlays, rendered output)+**before/after** shots — prose cannot convey it and nobody can verify it otherwise. pup's README carries+13 images, kicad's 8, plus demo videos; a new bridge shipped **0** for an entirely visual feature and its+reviewer had no way to evaluate it at all. `desktop_screenshot_window` makes capturing this scriptable.+**This matters most for a bridge aimed at outside users: an internal bridge can lean on the author being+in the room, but for everyone else the page IS the product.**++### 6. A keyword that exists only in conversation++**Symptom:** you and a colleague agreed the bridge is "pw", but an AI never resolves it. **Why:** the word+was never written into `bridge.json`, the verb prefix, the discovery triggers, or the README. **Fix:**+declare it once and use it in all four places — see "Your bridge's KEYWORD" above.++### 7. Stale naming and a missing page kit++**Symptom:** your page reads as abandoned next to the other bridges. **Why:** slugs and copy written+before the rename (**Adom Bridge**, not "Adom Desktop"), no root `SKILL.md` (only a `skill-package/`+directory), no 1.6-aspect hero (so ab cover-crops the OG-card fallback and clips it), and none of the+standard kit (`install.sh`/`uninstall.sh`, `CHANGELOG.md`, `PUBLISHING.md`, billboard art). **Fix:** the+page-identity block of the self-audit checklist (`adom-bridge-sdk-audit`) is a literal punch list for this.++### 8. Assuming ab will forward what it never received++**Symptom:** you add an arg to a verb and it silently does nothing. **Why:** something in the chain+rebuilt the payload from a fixed allowlist instead of forwarding verbatim. **Fix:** round-trip test it —+send an unknown arg and have your bridge echo its received keys. Every arg you send must arrive.++---+ ## Where every file lives: the two artifacts, their deps, and which skills go where **Two DISTINCT artifacts, two DIFFERENT homes, two DIFFERENT consumers. Crossing them is the #1 inconsistency across bridge repos, so pin it here before anything else.** (Live audit 2026-07-06: fusion + pup do this right; kicad had **61** loose `adom-bridge-kicad-v*.zip` on `/files` + a `mesa-llvmpipe-x64.zip`; blender dumped 2 loose zips too.)
bridges-SDK.md+157@@ -24,6 +24,163 @@ A **bridge** is a small local program (Python, Node, Rust, anything) that ab spa --- +## ⛔ STOP — the LINE OF DEMARCATION: ab is an operating system, your bridge is an app++**If you remember one thing from this SDK: a bridge NEVER modifies ab. Every line of logic specific+to your app lives in YOUR bridge. The correct amount of ab-core change for a new bridge or a bridge+feature is ZERO.** This is not style — it is the architecture, and violating it breaks the thing that+makes bridges work.++**ab is an operating system for the desktop.** Like a kernel it does two jobs: it provides **generic,+app-agnostic primitives** (windows, screenshots, input, files, shell, WSL, taskbar/window identity,+jump lists, notifications, tunnels) that know nothing about any specific app — `desktop_set_window_identity`+does not know what a browser is, it applies whatever icon/name/AUMID *you* hand it — and it **loads,+unloads, reloads, and updates bridges on the fly** (`bridge_install`, the wiki auto-sync, hot respawn).+ab stays deliberately stable and generic so it almost never has to change for a new bridge.++**Your bridge is an app running on that OS** — a fluid, hot-loadable asset with its own process, its+own skills, its own wiki page, its own release cadence entirely independent of ab. It owns **100% of+the logic specific to its host app or tool**, and when it needs the desktop to *do* something it+**calls one of ab's existing generic verbs** over the loopback direct API (see `adom-bridge-sdk-callback`).+The payoff: your bridge ships and updates from your own page with **no ab release**. The moment your+code lives in ab you have chained your feature to ab's release train and made the kernel carry your+app's weight — the opposite of what a dynamic, loadable bridge is for.++### The three AI failure modes (all seen live, 2026-08-28)++A capable coding AI slides into all three. Guard against them explicitly:++1. **Reproducing capability ab already ships.** Before writing ANY desktop capability — and absolutely+ before proposing an ab change — search ab's existing verbs (`/verbs`, the `adom-bridge-verbs` skill,+ or just ask the ab thread). Real case: a bridge shipped a **2,668-line ab-core module** re-implementing+ window identity, taskbar overlay, and jump lists that ab **already provided generically** and another+ bridge was **already using**. The check is one grep. Do it.+2. **Baking your app's name into ab.** The same case hardcoded a forced `Adom.<App>.` AUMID namespace,+ app-named jump-list labels, and an app-named router verb into ab-core. **If ab would end up knowing+ your app's name, the line has been crossed.** Your app-specifics stay in your bridge, which passes+ its own appId / icon / labels *into* ab's generic verbs. **If you catch your AI editing `src-tauri/`, stop.**+3. **Pulling app-AWARE correctness logic UP into the host.** The subtlest one. Strict window binding —+ "bind this operation to an exact HWND + owner PID + process-start so I never act on the wrong window" —+ *feels* like it belongs in the kernel because it is about windows. It does not: doing it requires+ knowing **how your app creates its windows** (process/window/creation-time relationships, correlating+ a spawned browser to a session). That is narrow app knowledge and **ab must never have it.** Do the+ identify-and-verify work in YOUR bridge, then hand the resolved HWND + your identity to ab's dumb,+ generic paint verbs. **The bridge decides *which* window and proves it; ab never learns why.**++### The rare, legitimate exception — and the process++Sometimes a bridge genuinely needs a **generic ab primitive that does not exist yet.** That is real and+welcome. **What is gated is the CONTENT, not the channel:**++1. **Confirm it doesn't already exist.** Search the verb surface first. Most "I need a new verb" is+ "there's already a generic one."+2. **Apply the two-part test:** *could a completely unrelated app use this verb unchanged?* AND *can ab+ implement it knowing nothing app-specific?* **If either answer is no, it belongs in your bridge** — no+ channel makes it acceptable. When an existing generic verb is close but not strict enough for you, ask+ for a **generic hardening of that verb**; never fork an app-specific copy of it, and never ask ab to+ absorb app-aware logic (failure mode 3).+3. **Then pick the channel by who you are:**+ - **Adom team** — **just send a PR.** It is the efficient path and it is welcome. The PR must still+ pass the two-part test and touch only generic surface: a reviewer's first question is *"does ab now+ know anything about your app?"*+ - **Third-party author** — **file a discussion on the `adom-bridge` page** proposing the verb as a+ generic contract, and the ab thread adds it (or invites a PR once the contract is agreed).++> **Being on the team does not widen the line — it only shortens the path to proposing something that was+> already generic.** A team PR that adds an app-named namespace, app-named labels, or app-aware binding+> logic to ab-core gets the same answer as anyone else's: build it in the bridge.++### Your bridge's KEYWORD — the short word a user says++**Declare ONE short, unambiguous keyword and use it everywhere.** A user says *"open my app in **pup**"*+and the AI must resolve that single word, through ab's layers, to your skills and verbs. That only works+if the word is declared and consistent. Requirements:++- **Short and speakable** (`pup`, `kicad`, `pw`) — this is what a human types in chat, not your product name.+- **Unambiguous** — avoid a word that collides with a common term or another bridge's niche.+- **Consistent everywhere**: your `bridge.json` `name`, your **verb prefix** (`<keyword>_…`), your+ `discovery_triggers` phrasings, your README's opening line, and your card. If the keyword is `pw`, the+ verbs are `pw_*` and the README says so in the first paragraph.+- **State it explicitly** in your README's first section: *"Say **pw** to the AI to reach this bridge."*+ A keyword agreed in conversation but written nowhere does not exist.++---++## ⚠ PITFALLS — real failures from real bridges, and how to avoid each++Every entry here happened. They are listed in the order they tend to bite, and each is written as+**symptom → why it happens → fix**, because the ones that hurt are the ones that look fine while you+are making them.++### 1. Building your feature into ab instead of into your bridge++**Symptom:** you have a PR against `adom-bridge` for something your bridge needs. **Why:** the capability+sounds like a desktop primitive ("I need the taskbar to show my app"), so extending the host feels+natural — and if you never enumerated ab's existing verbs, it also feels *necessary*. **Fix:** the+capability is almost certainly already generic in ab (window identity, taskbar overlay, jump lists,+screenshots, input, files, notifications). Call it. See the LINE OF DEMARCATION above. *Measured cost of+getting this wrong: a 2,668-line ab-core module duplicating verbs another bridge was already using.*++### 2. Your app's name ending up in ab++**Symptom:** ab-core contains your app's name — a forced `Adom.<YourApp>.` namespace, app-named labels,+an app-named router verb. **Why:** you needed *some* identity string, and hardcoding it in the host was+the shortest path. **Fix:** every app-specific value is an ARGUMENT your bridge passes into a generic+verb. `grep -ri "<yourapp>" <ab>/src-tauri/src/` must return nothing.++### 3. Pulling app-AWARE correctness logic up into the host++**Symptom:** you want ab to do strict binding — "act only on the exact HWND + owner PID + process-start,+never the wrong window." **Why:** it is about windows, so it feels like kernel work. **Fix:** it is not —+doing it well requires knowing *how your app creates its windows* (process/window timing, correlating a+spawned process to a session), which is precisely the knowledge ab must not carry. Resolve and verify in+YOUR bridge; hand ab a resolved HWND for its dumb generic paint verbs. **The bridge decides *which*+window and proves it; ab never learns why.** This is the subtlest one on the list — it passes the+"is it generic?" sniff test and still fails the two-part test.++### 4. Shipping something nobody can RUN++**Symptom:** your work sits unreviewed and untried. **Why:** it is only runnable if someone builds and+runs a *modified core ab* — nobody will do that to try out a feature — or your releases are all+dev/updater-ineligible candidates, so `bridge_install` has nothing to resolve. **Fix:** a bridge's whole+value proposition is that **anyone can install it in one command and use it in a minute.** Ship at least+one qualified, updater-eligible release with its runtime `.zip`, and keep 100% of the feature inside the+bridge so trying it never requires touching ab. *A change people must review is worth far less than a+thing people can just use.*++### 5. Shipping something nobody can SEE++**Symptom:** your README is all prose; a reader cannot tell what your bridge does or whether it works.+**Why:** you can see it working on your own machine, so the gap is invisible to you. **Fix:** screenshots+of the real outcome, and for anything VISUAL (taskbar tiles, jump lists, overlays, rendered output)+**before/after** shots — prose cannot convey it and nobody can verify it otherwise. pup's README carries+13 images, kicad's 8, plus demo videos; a new bridge shipped **0** for an entirely visual feature and its+reviewer had no way to evaluate it at all. `desktop_screenshot_window` makes capturing this scriptable.+**This matters most for a bridge aimed at outside users: an internal bridge can lean on the author being+in the room, but for everyone else the page IS the product.**++### 6. A keyword that exists only in conversation++**Symptom:** you and a colleague agreed the bridge is "pw", but an AI never resolves it. **Why:** the word+was never written into `bridge.json`, the verb prefix, the discovery triggers, or the README. **Fix:**+declare it once and use it in all four places — see "Your bridge's KEYWORD" above.++### 7. Stale naming and a missing page kit++**Symptom:** your page reads as abandoned next to the other bridges. **Why:** slugs and copy written+before the rename (**Adom Bridge**, not "Adom Desktop"), no root `SKILL.md` (only a `skill-package/`+directory), no 1.6-aspect hero (so ab cover-crops the OG-card fallback and clips it), and none of the+standard kit (`install.sh`/`uninstall.sh`, `CHANGELOG.md`, `PUBLISHING.md`, billboard art). **Fix:** the+page-identity block of the self-audit checklist (`adom-bridge-sdk-audit`) is a literal punch list for this.++### 8. Assuming ab will forward what it never received++**Symptom:** you add an arg to a verb and it silently does nothing. **Why:** something in the chain+rebuilt the payload from a fixed allowlist instead of forwarding verbatim. **Fix:** round-trip test it —+send an unknown arg and have your bridge echo its received keys. Every arg you send must arrive.++---+ ## Where every file lives: the two artifacts, their deps, and which skills go where **Two DISTINCT artifacts, two DIFFERENT homes, two DIFFERENT consumers. Crossing them is the #1 inconsistency across bridge repos, so pin it here before anything else.** (Live audit 2026-07-06: fusion + pup do this right; kicad had **61** loose `adom-bridge-kicad-v*.zip` on `/files` + a `mesa-llvmpipe-x64.zip`; blender dumped 2 loose zips too.)