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 .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)

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 asserts chrome.js + the change are inside the zip before upload. node_modules is excluded because AD npm 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.jsonversion, 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 and src/bridge.json so refresh_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/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

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)

# 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 1curl -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 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.