Adom Desktop - Bridge SDK
Public Made by Adomby adom
Bridge SDK guide: bridge.json schema, kind:python/node/exe, hello-python + hello-rust reference templates, packaging + lifecycle commands.
name: bridge-publish-template
user-invocable: false
description: "TEMPLATE — the canonical, battle-tested recipe for publishing a new version of ANY Adom Desktop bridge to wiki.adom.inc, plus every gotcha hit live. Copy into your own bridge's dev-skills/, replace the / placeholders, and follow it. NOT for general users. Read before cutting a release: which artifact goes where (pkg=skills, release=bridge zip, repo=manifest+source), version-lockstep (kept above the AD bundled seed), the files allowlist + skills/** glob trap, keeping the tarball lean (NO zip / NO src / NO heroes), keeping dev/publish skills SOURCE-ONLY (not SKILL.md), deploying skills to .claude AND .codex, scrubbing retired-wiki host URLs, user-first discovery triggers, sha matching, and verify steps. Trigger words: publish bridge, ship bridge, release bridge, pkg publish, repo push, adom-wiki release, bridge manifest, bridge version bump, no zip in tarball, dev skill source only, scrub old wiki url, codex skills, discovery triggers, adom-desktop bridge publish template."
bridge-publish-template — shipping a new <bridge> bridge version
Template — copy into your bridge's own
dev-skills/, replace the<bridge>/<prefix>placeholders, follow the steps.
This is the step-by-step ship recipe for a cloud-owned Adom Desktop bridge living at
wiki.adom.inc/adom/adom-desktop-<bridge>-bridge, plus the traps that cost real time. Companion docs in
your repo: a *-dev.md dev skill (architecture / ownership), a terse PUBLISHING.md, and CLAUDE.md
(ownership boundary).
Placeholder legend — replace consistently throughout before you ship:
| Placeholder | Meaning | Example (Puppeteer bridge) |
|---|---|---|
<bridge> |
lowercase bridge id | puppeteer |
<Bridge Name> |
display name | Puppeteer |
<prefix>_ |
verb prefix | browser_ |
adom-desktop-<bridge>-bridge |
wiki slug | adom-desktop-puppeteer-bridge |
adom-bridge-<bridge>-manifest.json |
streaming manifest filename | adom-bridge-puppeteer-manifest.json |
adom-bridge-<bridge>-v<version>.zip |
release zip filename | adom-bridge-puppeteer-v1.1.1.zip |
<your bridge's files> |
the runtime source files | server.js, chrome.js, bridge.json |
<your runtime> |
the language/runtime AD reconstructs | node + node_modules |
What goes WHERE — three artifacts, three homes (memorize this)
| Artifact | Home | Command | Carries |
|---|---|---|---|
bridge runtime .zip |
a Release asset | adom-wiki release upload |
src/ at zip-root (<your bridge's files> — the thing AD runs). |
| streaming manifest JSON | the git repo | adom-wiki repo push |
points at the release zip URL + its sha256/size |
| container skills tarball | a Package | adom-wiki pkg publish |
SKILL.md + skills/* (the Claude/Codex skills). NO runtime. |
The
.zipis a RELEASE, never a git file:*.zipis gitignored andrepo push --files X.zipreportsokbut silently skips it (the URL then 404s). Binaries →release upload.
Auth (once per container)
export ADOM_WIKI_TOKEN=$(cat /var/run/adom/api-key 2>/dev/null) # container default; or session.json's login session_token
adom-wiki whoami # MUST show a real user (e.g. [email protected]). Strict-auth rejects the shared "Developer" token.
ADOM_WIKI_TOKEN is the user's login session_token — the container default lives at
/var/run/adom/api-key; otherwise pull it from ~/.adom/session.json. Every mutation needs a real
--changelog / -m (≥10 chars, ≥2 words) or the server 400s.
The recipe
1. Bump version IN LOCKSTEP, and keep it > the AD bundled seed
printf '1.1.1' > src/BRIDGE_VERSION
sed -i 's/"version": "1.1.0"/"version": "1.1.1"/' src/bridge.json # bridge runtime
sed -i 's/"version": "1.1.0"/"version": "1.1.1"/' package.json # adompkg descriptor (the pkg)
src/BRIDGE_VERSION and src/bridge.json MUST match (AD uses BRIDGE_VERSION to detect a stale running
process). Keep the number above the seed in adom-desktop/plugins/<bridge> so cache-over-bundled wins.
2. Build the bridge zip (source-only, src/ at zip ROOT — no top-level dir)
zip may be absent → Python. Your <your runtime> deps (e.g. node_modules) are NEVER included (AD
reconstructs them on spawn; the seed ships them; a fat zip would blow wiki limits):
import zipfile, os, hashlib
root, out = "src", "adom-bridge-<bridge>-v1.1.1.zip"
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:
for dp, dirs, files in os.walk(root):
for skip in ("node_modules","screenshots","profiles"): # your runtime's heavy/generated dirs
if skip in dirs: dirs.remove(skip)
for f in files: z.write(os.path.join(dp,f), os.path.relpath(os.path.join(dp,f), root))
print("sha256:", hashlib.sha256(open(out,"rb").read()).hexdigest(), "size:", os.path.getsize(out))
# SANITY: assert your changed file(s) are inside before shipping.
Concrete pup example (so the commands aren't abstract): the Puppeteer bridge skips
node_modules/screenshots/profiles, and its sanity check assertschrome.js+ the change are inside the zip before upload.node_modulesis excluded because ADnpm installs it on spawn (an 88 MB zip would exceed wiki limits).
3. Publish the zip as a Release (releases PRESERVE your sha)
adom-wiki release create adom/adom-desktop-<bridge>-bridge 1.1.1 --title "..." --changelog "<≥2 words>"
adom-wiki release upload adom/adom-desktop-<bridge>-bridge 1.1.1 adom-bridge-<bridge>-v1.1.1.zip --json
# the response's assets[].sha256/size are what the manifest MUST carry. Then VERIFY the PUBLIC url:
curl -sL https://wiki.adom.inc/download/adom/adom-desktop-<bridge>-bridge/1.1.1/adom-bridge-<bridge>-v1.1.1.zip | sha256sum
4. Point the manifest at the release + push it (git repo)
Edit adom-bridge-<bridge>-manifest.json → version, url (the release download URL), sha256,
size (from step 3 — must match the served asset or bridge_install rejects it):
{ "manifest_version": 1, "name": "<bridge>", "version": "1.1.1",
"url": "https://wiki.adom.inc/download/adom/adom-desktop-<bridge>-bridge/1.1.1/adom-bridge-<bridge>-v1.1.1.zip",
"sha256": "<the release asset's sha256>", "size": <bytes>,
"verbPrefixes": ["<prefix>_"],
"healthEndpoint": "/status", "statusVerb": "<prefix>_readiness",
"updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-desktop-<bridge>-bridge/files/adom-bridge-<bridge>-manifest.json",
"released_at": "<UTC ISO8601>" }
Declare
updateManifestUrl(the full URL of THIS manifest on your own page) in both the manifest andsrc/bridge.jsonsorefresh_bridges/ the auto-poll reach YOUR page instead of the legacy AD-owned convention. Then push:
adom-wiki repo push adom/adom-desktop-<bridge>-bridge \
--files adom-bridge-<bridge>-manifest.json src/bridge.json src/BRIDGE_VERSION <your bridge's files> SKILL.md \
-m "v1.1.1: <what changed>"
sha256/sizeMUST match the served asset orbridge_installrejects it. Re-download to confirm:curl -sL <download_url> | sha256sum.
5. Publish the container skills as a pkg — keep it LEAN, and MIND THE TRAPS
adom-wiki pkg pack # ALWAYS pack + inspect BEFORE publish
tar tzf *.tgz # confirm: SKILL.md + skills/*/SKILL.md + install.sh, and NOTHING heavy
adom-wiki pkg publish --org adom --version 1.1.1
adom-wiki pkg notes adom/adom-desktop-<bridge>[email protected] -m "Container skills: <what changed>"
The pkg description lives in the root package.json description — keep it explicit that this is
container-side skills only; the bridge runtime is the Release zip + bundled in Adom Desktop.
Traps hit live (don't relearn these)
pkg packincludes EVERYTHING by default → a fat tarball (all the hero PNGs +src/runtime). Fix: the heroes are gitignored (*.png) since they're already page assets, ANDpackage.jsonhas afilesallowlist. With both, the tarball drops to ~12 KB. (Pup's was 16 MB before the guards.)filesdirectory entries need a GLOB. Listing"skills"(bare dir) silently includes ZERO skill files. Use"skills/**"(and/or the explicit"skills/<name>/SKILL.md"). Alwaystar tzfto confirm your skills actually made it in — a missing skill = an empty Skills tab.pkg packhonors.gitignore, not a separate npmignore. So you can't gitignore something you need in git (e.g.src/) to drop it from the pkg — use thefilesallowlist for that.- A RELEASE shows up in
adom-wiki pkg infoas a row withtype:null(wiki UI conflation). The real pkg rows aretype:"app". Useadom-wiki release listfor true releases; don't think your pkg shipped just because a version appears inpkg info. - sha/size mismatch →
bridge_installrejects the manifest. Releases preserve the uploaded sha, so use therelease uploadresponse values verbatim and re-download to confirm. - The Skills tab +
skill_count+adom-wiki skills installcome from the GIT REPO, not the pkg. The wiki indexes everySKILL.mdin the page repo (root = main skill;<dir>/SKILL.md= sub-skills) —page.skillsis null; it's a repo scan. So a file namedSKILL.mdANYWHERE in the repo becomes an indexed, tab-listed,skills install-able skill. (This is separate frompkg install, which installs the pkg tarball governed by thefilesallowlist.) Corollary → see "Keep dev/publish skills source-only".
Verify (every ship)
# manifest serves at the updateManifestUrl path:
curl -s https://wiki.adom.inc/api/v1/pages/adom-desktop-<bridge>-bridge/files/adom-bridge-<bridge>-manifest.json | jq .sha256
# anon-fetch the PUBLIC release zip URL and confirm the sha matches the manifest (curl 200 is NOT enough):
curl -sL https://wiki.adom.inc/download/adom/adom-desktop-<bridge>-bridge/1.1.1/adom-bridge-<bridge>-v1.1.1.zip | sha256sum
# live install + new verbs on a real desktop (multi-desktop → --target; `adom-desktop targets` lists them):
adom-desktop --target <name> bridge_install '{"manifestUrl":".../files/adom-bridge-<bridge>-manifest.json","force":true}'
adom-desktop --target <name> <prefix>_readiness '{}'
# bridge_list shows the new version + skillPkg; bridge_check_updates reaches your page; sync_skills pulls the user skill:
adom-desktop --target <name> bridge_list '{}' # confirm version + skillPkg (adom/adom-desktop-<bridge>-bridge)
adom-desktop --target <name> bridge_check_updates '{}' # wikiHosted:true, latest = your new version
adom-desktop --target <name> sync_skills '{}' # installs the user skill pkg into the container
# render the page in pup + READ the screenshot. Confirm version + Skills tab.
Server-only change → AD reaps + respawns the bridge from the new cache on the next
<prefix>_*call.
Keep dev/publish skills SOURCE-ONLY (not on the Skills tab, not installed)
Everyday users should get ONLY the <bridge> user skill — not the dev/publish skills. But the wiki indexes
every SKILL.md in the repo (above), so a dev skill named SKILL.md would show on the Skills tab AND
install via skills install. The fix: keep dev/publish skills as source-only docs that are NOT named
SKILL.md — they live in dev-skills/ as <bridge>-bridge-dev.md / <bridge>-bridge-publish.md. Then
they are:
- not indexed (
skill_countstays 1), not on the Skills tab, notskills install-able; - still in git → available via
adom-wiki repo clone(and the Files tab); - excluded from the pkg too (the
filesallowlist lists onlySKILL.md+skills/**).
dev-skills/README.md tells a developer how to load one as a live skill (copy it to
~/.claude/skills/<name>/SKILL.md). Verify after publish: skill_count should be 1 —
curl -s https://wiki.adom.inc/api/pages/adom/adom-desktop-<bridge>-bridge | jq '.page.skill_count'.
Keep the pkg tarball LEAN — NO zip, NO src/, NO heroes
The pkg tarball must be the <bridge> user skill ONLY — never the bridge runtime zip, src/, or hero PNGs.
Guards: the package.json files allowlist (SKILL.md, skills/**, install.sh, uninstall.sh,
README.md, package.json) plus .gitignore (*.zip, *.png, *.bundle, *.tgz, the old
*-source.tar.gz). ALWAYS adom-wiki pkg pack then tar tzf *.tgz and confirm there is no
*.zip, no src/, no *.png. A lean tarball is ~10 KB; if it's megabytes, something heavy leaked in
(it was 16 MB before the guards). rm -f *.tgz after inspecting.
Skills install to BOTH .claude AND .codex
install.sh deploys the user skill into both ~/.claude/skills/ and ~/.codex/skills/ (Codex only
if ~/.codex exists); uninstall.sh removes from both. Codex is a real second agent skills home now —
don't ship an install.sh that only does .claude.
Scrub retired-wiki URLs (the #1 fork/extract gotcha)
The only wiki host is wiki.adom.inc. Any wiki-*.adom.cloud host is RETIRED — as are the
/static/apps/... and /wiki/apps/... path formats. When you fork/extract a page, the inherited
README.md and the page.json readme body carry OLD URLs (this bit pup live — a dead static/apps
zip link). Before every publish:
grep -rnI 'adom\.cloud\|/static/apps\|/wiki/apps' . --exclude-dir=node_modules
Fix every hit (README + the page.json readme body are the usual culprits), then VERIFY the live
entity (not just local):
curl -s https://wiki.adom.inc/api/pages/adom/adom-desktop-<bridge>-bridge | grep -c '\.adom\.cloud' # want 0
Also repo rm any stale source-mirror artifacts left on the page (old *-source.tar.gz / *.bundle).
(https://adom.inc/fonts/... is fine — that's the brand font CDN, not a wiki host.)
Canonical wiki.adom.inc URL formats
- Page:
https://wiki.adom.inc/adom/<slug> - File (served, anon):
https://wiki.adom.inc/api/v1/pages/<slug>/files/<name> - Release download:
https://wiki.adom.inc/download/adom/<slug>/<ver>/<file> - Sibling pages:
/adom/adom-desktop-bridges,/adom/adom-desktop-kicad-bridge,/adom/adom-desktop-fusion-bridge,/adom/adom-desktop
Discovery triggers — lead with everyday USER phrases
Lead discovery_triggers with everyday phrases, not dev jargon — the single most important is the bare
bridge name users actually say. The wiki auto-generates the discover snippet from discovery_triggers
discovery_pitch(there is no separate "upload a snippet" — those two fields ARE the snippet). Set them viaadom-wiki discover triggers <ref> --add ...ORpage.jsondiscovery_triggers/discovery_pitch- push. Verify:
adom-wiki discover preview "<a natural user phrase for your bridge>"should rank this page #1.
Concrete pup example: the most important trigger is the bare word
pup, plusopen in pup,open my app in pup,screenshot the page,record a browser window.adom-wiki discover preview "open my app in pup"must rank the Puppeteer page #1.
---
name: bridge-publish-template
user-invocable: false
description: "TEMPLATE — the canonical, battle-tested recipe for publishing a new version of ANY Adom Desktop bridge to wiki.adom.inc, plus every gotcha hit live. Copy into your own bridge's dev-skills/, replace the <bridge>/<prefix> placeholders, and follow it. NOT for general users. Read before cutting a release: which artifact goes where (pkg=skills, release=bridge zip, repo=manifest+source), version-lockstep (kept above the AD bundled seed), the `files` allowlist + `skills/**` glob trap, keeping the tarball lean (NO zip / NO src / NO heroes), keeping dev/publish skills SOURCE-ONLY (not SKILL.md), deploying skills to .claude AND .codex, scrubbing retired-wiki host URLs, user-first discovery triggers, sha matching, and verify steps. Trigger words: publish bridge, ship bridge, release bridge, pkg publish, repo push, adom-wiki release, bridge manifest, bridge version bump, no zip in tarball, dev skill source only, scrub old wiki url, codex skills, discovery triggers, adom-desktop bridge publish template."
---
# bridge-publish-template — shipping a new `<bridge>` bridge version
> **Template — copy into your bridge's own `dev-skills/`, replace the `<bridge>`/`<prefix>` placeholders, follow the steps.**
This is the step-by-step ship recipe for a cloud-owned Adom Desktop bridge living at
`wiki.adom.inc/adom/adom-desktop-<bridge>-bridge`, plus the traps that cost real time. Companion docs in
your repo: a `*-dev.md` dev skill (architecture / ownership), a terse `PUBLISHING.md`, and `CLAUDE.md`
(ownership boundary).
Placeholder legend — replace consistently throughout before you ship:
| Placeholder | Meaning | Example (Puppeteer bridge) |
|---|---|---|
| `<bridge>` | lowercase bridge id | `puppeteer` |
| `<Bridge Name>` | display name | `Puppeteer` |
| `<prefix>_` | verb prefix | `browser_` |
| `adom-desktop-<bridge>-bridge` | wiki slug | `adom-desktop-puppeteer-bridge` |
| `adom-bridge-<bridge>-manifest.json` | streaming manifest filename | `adom-bridge-puppeteer-manifest.json` |
| `adom-bridge-<bridge>-v<version>.zip` | release zip filename | `adom-bridge-puppeteer-v1.1.1.zip` |
| `<your bridge's files>` | the runtime source files | `server.js`, `chrome.js`, `bridge.json` |
| `<your runtime>` | the language/runtime AD reconstructs | `node` + `node_modules` |
## What goes WHERE — three artifacts, three homes (memorize this)
| Artifact | Home | Command | Carries |
|---|---|---|---|
| **bridge runtime `.zip`** | a **Release** asset | `adom-wiki release upload` | `src/` at zip-root (`<your bridge's files>` — the thing AD runs). |
| **streaming manifest JSON** | the **git repo** | `adom-wiki repo push` | points at the release zip URL + its sha256/size |
| **container skills tarball** | a **Package** | `adom-wiki pkg publish` | `SKILL.md` + `skills/*` (the Claude/Codex skills). NO runtime. |
> The `.zip` is a RELEASE, never a git file: `*.zip` is gitignored and `repo push --files X.zip`
> reports `ok` but silently skips it (the URL then 404s). Binaries → `release upload`.
## Auth (once per container)
```bash
export ADOM_WIKI_TOKEN=$(cat /var/run/adom/api-key 2>/dev/null) # container default; or session.json's login session_token
adom-wiki whoami # MUST show a real user (e.g. [email protected]). Strict-auth rejects the shared "Developer" token.
```
`ADOM_WIKI_TOKEN` is the user's **login `session_token`** — the container default lives at
`/var/run/adom/api-key`; otherwise pull it from `~/.adom/session.json`. Every mutation needs a real
`--changelog` / `-m` (≥10 chars, ≥2 words) or the server 400s.
## The recipe
### 1. Bump version IN LOCKSTEP, and keep it > the AD bundled seed
```bash
printf '1.1.1' > src/BRIDGE_VERSION
sed -i 's/"version": "1.1.0"/"version": "1.1.1"/' src/bridge.json # bridge runtime
sed -i 's/"version": "1.1.0"/"version": "1.1.1"/' package.json # adompkg descriptor (the pkg)
```
`src/BRIDGE_VERSION` and `src/bridge.json` MUST match (AD uses `BRIDGE_VERSION` to detect a stale running
process). Keep the number **above** the seed in `adom-desktop/plugins/<bridge>` so cache-over-bundled wins.
### 2. Build the bridge zip (source-only, `src/` at zip ROOT — no top-level dir)
`zip` may be absent → Python. Your `<your runtime>` deps (e.g. `node_modules`) are NEVER included (AD
reconstructs them on spawn; the seed ships them; a fat zip would blow wiki limits):
```python
import zipfile, os, hashlib
root, out = "src", "adom-bridge-<bridge>-v1.1.1.zip"
with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as z:
for dp, dirs, files in os.walk(root):
for skip in ("node_modules","screenshots","profiles"): # your runtime's heavy/generated dirs
if skip in dirs: dirs.remove(skip)
for f in files: z.write(os.path.join(dp,f), os.path.relpath(os.path.join(dp,f), root))
print("sha256:", hashlib.sha256(open(out,"rb").read()).hexdigest(), "size:", os.path.getsize(out))
# SANITY: assert your changed file(s) are inside before shipping.
```
> **Concrete pup example (so the commands aren't abstract):** the Puppeteer bridge skips
> `node_modules`/`screenshots`/`profiles`, and its sanity check asserts `chrome.js` + the change are
> inside the zip before upload. `node_modules` is excluded because AD `npm install`s it on spawn (an
> 88 MB zip would exceed wiki limits).
### 3. Publish the zip as a Release (releases PRESERVE your sha)
```bash
adom-wiki release create adom/adom-desktop-<bridge>-bridge 1.1.1 --title "..." --changelog "<≥2 words>"
adom-wiki release upload adom/adom-desktop-<bridge>-bridge 1.1.1 adom-bridge-<bridge>-v1.1.1.zip --json
# the response's assets[].sha256/size are what the manifest MUST carry. Then VERIFY the PUBLIC url:
curl -sL https://wiki.adom.inc/download/adom/adom-desktop-<bridge>-bridge/1.1.1/adom-bridge-<bridge>-v1.1.1.zip | sha256sum
```
### 4. Point the manifest at the release + push it (git repo)
Edit `adom-bridge-<bridge>-manifest.json` → `version`, `url` (the release download URL), `sha256`,
`size` (from step 3 — **must match the served asset or `bridge_install` rejects it**):
```json
{ "manifest_version": 1, "name": "<bridge>", "version": "1.1.1",
"url": "https://wiki.adom.inc/download/adom/adom-desktop-<bridge>-bridge/1.1.1/adom-bridge-<bridge>-v1.1.1.zip",
"sha256": "<the release asset's sha256>", "size": <bytes>,
"verbPrefixes": ["<prefix>_"],
"healthEndpoint": "/status", "statusVerb": "<prefix>_readiness",
"updateManifestUrl": "https://wiki.adom.inc/api/v1/pages/adom-desktop-<bridge>-bridge/files/adom-bridge-<bridge>-manifest.json",
"released_at": "<UTC ISO8601>" }
```
> Declare `updateManifestUrl` (the full URL of THIS manifest on your own page) in both the manifest and
> `src/bridge.json` so `refresh_bridges` / the auto-poll reach YOUR page instead of the legacy
> AD-owned convention. Then push:
```bash
adom-wiki repo push adom/adom-desktop-<bridge>-bridge \
--files adom-bridge-<bridge>-manifest.json src/bridge.json src/BRIDGE_VERSION <your bridge's files> SKILL.md \
-m "v1.1.1: <what changed>"
```
> `sha256`/`size` MUST match the served asset or `bridge_install` rejects it. Re-download to confirm:
> `curl -sL <download_url> | sha256sum`.
### 5. Publish the container skills as a pkg — keep it LEAN, and MIND THE TRAPS
```bash
adom-wiki pkg pack # ALWAYS pack + inspect BEFORE publish
tar tzf *.tgz # confirm: SKILL.md + skills/*/SKILL.md + install.sh, and NOTHING heavy
adom-wiki pkg publish --org adom --version 1.1.1
adom-wiki pkg notes adom/adom-desktop-<bridge>[email protected] -m "Container skills: <what changed>"
```
The pkg description lives in the root **`package.json`** `description` — keep it explicit that this is
**container-side skills only; the bridge runtime is the Release zip + bundled in Adom Desktop.**
## Traps hit live (don't relearn these)
- **`pkg pack` includes EVERYTHING by default** → a fat tarball (all the hero PNGs + `src/` runtime).
Fix: the heroes are gitignored (`*.png`) since they're already page assets, AND `package.json` has a
**`files` allowlist**. With both, the tarball drops to ~12 KB. (Pup's was 16 MB before the guards.)
- **`files` directory entries need a GLOB.** Listing `"skills"` (bare dir) silently includes ZERO skill
files. Use **`"skills/**"`** (and/or the explicit `"skills/<name>/SKILL.md"`). Always `tar tzf` to
confirm your skills actually made it in — a missing skill = an empty Skills tab.
- **`pkg pack` honors `.gitignore`, not a separate npmignore.** So you can't gitignore something you
need in git (e.g. `src/`) to drop it from the pkg — use the `files` allowlist for that.
- **A RELEASE shows up in `adom-wiki pkg info` as a row with `type:null`** (wiki UI conflation). The real
pkg rows are `type:"app"`. Use `adom-wiki release list` for true releases; don't think your pkg shipped
just because a version appears in `pkg info`.
- **sha/size mismatch → `bridge_install` rejects the manifest.** Releases preserve the uploaded sha, so
use the `release upload` response values verbatim and re-download to confirm.
- **The Skills tab + `skill_count` + `adom-wiki skills install` come from the GIT REPO, not the pkg.**
The wiki indexes **every `SKILL.md`** in the page repo (root = main skill; `<dir>/SKILL.md` = sub-skills)
— `page.skills` is null; it's a repo scan. So a file named `SKILL.md` ANYWHERE in the repo becomes an
indexed, tab-listed, `skills install`-able skill. (This is *separate* from `pkg install`, which installs
the pkg tarball governed by the `files` allowlist.) Corollary → see "Keep dev/publish skills source-only".
## Verify (every ship)
```bash
# manifest serves at the updateManifestUrl path:
curl -s https://wiki.adom.inc/api/v1/pages/adom-desktop-<bridge>-bridge/files/adom-bridge-<bridge>-manifest.json | jq .sha256
# anon-fetch the PUBLIC release zip URL and confirm the sha matches the manifest (curl 200 is NOT enough):
curl -sL https://wiki.adom.inc/download/adom/adom-desktop-<bridge>-bridge/1.1.1/adom-bridge-<bridge>-v1.1.1.zip | sha256sum
# live install + new verbs on a real desktop (multi-desktop → --target; `adom-desktop targets` lists them):
adom-desktop --target <name> bridge_install '{"manifestUrl":".../files/adom-bridge-<bridge>-manifest.json","force":true}'
adom-desktop --target <name> <prefix>_readiness '{}'
# bridge_list shows the new version + skillPkg; bridge_check_updates reaches your page; sync_skills pulls the user skill:
adom-desktop --target <name> bridge_list '{}' # confirm version + skillPkg (adom/adom-desktop-<bridge>-bridge)
adom-desktop --target <name> bridge_check_updates '{}' # wikiHosted:true, latest = your new version
adom-desktop --target <name> sync_skills '{}' # installs the user skill pkg into the container
# render the page in pup + READ the screenshot. Confirm version + Skills tab.
```
> Server-only change → AD reaps + respawns the bridge from the new cache on the next `<prefix>_*` call.
## Keep dev/publish skills SOURCE-ONLY (not on the Skills tab, not installed)
Everyday users should get ONLY the `<bridge>` user skill — not the dev/publish skills. But the wiki indexes
every `SKILL.md` in the repo (above), so a dev skill named `SKILL.md` would show on the Skills tab AND
install via `skills install`. The fix: keep dev/publish skills as **source-only docs that are NOT named
`SKILL.md`** — they live in `dev-skills/` as `<bridge>-bridge-dev.md` / `<bridge>-bridge-publish.md`. Then
they are:
- **not indexed** (`skill_count` stays 1), **not on the Skills tab**, **not `skills install`-able**;
- still in git → available via `adom-wiki repo clone` (and the Files tab);
- excluded from the pkg too (the `files` allowlist lists only `SKILL.md` + `skills/**`).
`dev-skills/README.md` tells a developer how to load one as a live skill (copy it to
`~/.claude/skills/<name>/SKILL.md`). **Verify after publish:** `skill_count` should be **1** —
`curl -s https://wiki.adom.inc/api/pages/adom/adom-desktop-<bridge>-bridge | jq '.page.skill_count'`.
## Keep the pkg tarball LEAN — NO zip, NO src/, NO heroes
The pkg tarball must be the `<bridge>` user skill ONLY — never the bridge runtime zip, `src/`, or hero PNGs.
Guards: the `package.json` `files` allowlist (`SKILL.md`, `skills/**`, `install.sh`, `uninstall.sh`,
`README.md`, `package.json`) **plus** `.gitignore` (`*.zip`, `*.png`, `*.bundle`, `*.tgz`, the old
`*-source.tar.gz`). **ALWAYS** `adom-wiki pkg pack` then `tar tzf *.tgz` and confirm there is **no
`*.zip`, no `src/`, no `*.png`**. A lean tarball is ~10 KB; if it's megabytes, something heavy leaked in
(it was 16 MB before the guards). `rm -f *.tgz` after inspecting.
## Skills install to BOTH .claude AND .codex
`install.sh` deploys the user skill into **both `~/.claude/skills/` and `~/.codex/skills/`** (Codex only
if `~/.codex` exists); `uninstall.sh` removes from both. Codex is a real second agent skills home now —
don't ship an install.sh that only does `.claude`.
## Scrub retired-wiki URLs (the #1 fork/extract gotcha)
**The only wiki host is `wiki.adom.inc`.** Any `wiki-*.adom.cloud` host is RETIRED — as are the
`/static/apps/...` and `/wiki/apps/...` path formats. When you fork/extract a page, the inherited
`README.md` and the `page.json` `readme` body carry OLD URLs (this bit pup live — a dead `static/apps`
zip link). Before every publish:
```bash
grep -rnI 'adom\.cloud\|/static/apps\|/wiki/apps' . --exclude-dir=node_modules
```
Fix every hit (README + the `page.json` `readme` body are the usual culprits), then VERIFY the **live**
entity (not just local):
```bash
curl -s https://wiki.adom.inc/api/pages/adom/adom-desktop-<bridge>-bridge | grep -c '\.adom\.cloud' # want 0
```
Also `repo rm` any stale source-mirror artifacts left on the page (old `*-source.tar.gz` / `*.bundle`).
(`https://adom.inc/fonts/...` is fine — that's the brand font CDN, not a wiki host.)
## Canonical wiki.adom.inc URL formats
- **Page:** `https://wiki.adom.inc/adom/<slug>`
- **File (served, anon):** `https://wiki.adom.inc/api/v1/pages/<slug>/files/<name>`
- **Release download:** `https://wiki.adom.inc/download/adom/<slug>/<ver>/<file>`
- **Sibling pages:** `/adom/adom-desktop-bridges`, `/adom/adom-desktop-kicad-bridge`,
`/adom/adom-desktop-fusion-bridge`, `/adom/adom-desktop`
## Discovery triggers — lead with everyday USER phrases
Lead `discovery_triggers` with everyday phrases, not dev jargon — the single most important is the bare
bridge name users actually say. The wiki **auto-generates** the discover snippet from `discovery_triggers`
+ `discovery_pitch` (there is no separate "upload a snippet" — those two fields ARE the snippet). Set them
via `adom-wiki discover triggers <ref> --add ...` OR `page.json` `discovery_triggers`/`discovery_pitch`
+ push. **Verify:** `adom-wiki discover preview "<a natural user phrase for your bridge>"` should rank this
page #1.
> **Concrete pup example:** the most important trigger is the bare word **`pup`**, plus `open in pup`,
> `open my app in pup`, `screenshot the page`, `record a browser window`. `adom-wiki discover preview
> "open my app in pup"` must rank the Puppeteer page #1.