← Commit history

hydrogen-dock skill: lead with the two rules that break every first attempt

John Lauer ·cb52021547 ·2mo ago ·parent 1a0a3c5
Rewritten around third-party adoption. The skill ships into every Hydrogen workspace, so it is what a user's AI reads when someone asks 'make my app dock-ready' or 'why does my dock card not work'. Adds, prominently, the two failure modes that cost real time and that both bundled bridges shipped: - a serve verb must SELF-BACKGROUND, because Hydrogen waits for the step to exit before reading stdout; a foreground server is killed by the exec timeout and the URL it printed is dead by the time Hydrogen polls it - steps must set PATH, because wiki CLIs live in ~/.local/bin which is not on the exec shell's PATH, so a bare command name works for the author and fails for every user Also documents behavior that was undocumented here: the ai block is its own lifecycle branch rather than an alternative to on_ready, thread: existing is a per-app NAMED thread injected in the background, re-click reveals the existing tab (making webview.title a stable handle), and a URL that never answers now fails loudly instead of navigating to a dead page. Adds a troubleshooting table keyed by symptom, and points at adom/hydrogen-dock-sdk as the full publisher contract rather than duplicating it.
1 file changed +116−122
skills/hydrogen-dock/SKILL.md+116−122
@@ -1,163 +1,157 @@ --- name: hydrogen-dock-description: The Hydrogen dock (the app launcher rail on the right edge of the workspace) and how apps get into it. Covers the two-layer list (the adom-curated global manifest plus user-added wiki apps), the dock.json manifest an app declares at the root of its wiki page repo so the dock knows how to launch it (webview, container_command steps, ai_thread), the container_command step contract (--print-url, skip_if, await url, timeouts), the ai block (pre-canned prompt, staged vs auto-submit, new vs existing thread), the free orbital loading screen, testing a draft manifest on your own Hydrogen, and how a user or an AI adds any wiki app to the dock locally. Read this when a user says: add an app to the dock, add my app to the dock, put this wiki page in the dock, dock settings, make my app dock-ready, dock.json, dock block, dock manifest, launch steps, AI thread on launch, auto submit prompt, staged prompt, draft manifest, how do dock apps launch, app launcher, featured apps, pin an app, hover zoom, alt-pin.+description: The Hydrogen dock bar (the app launcher rail on the right edge of the workspace) and how ANY wiki app, including a third party's, becomes a card in it. Covers the dock.json/dockbar.json manifest, the two rules that break every first attempt (a serve verb must self-background, and steps must set PATH for ~/.local/bin), the container_command step contract (--print-url, skip_if, await url, timeouts), the ai block (per-app named threads, staged vs auto), re-click behavior, the free orbital loading screen, testing a draft manifest, and troubleshooting by symptom. Read this when a user says - add an app to the dock, add my app to the dock, put this wiki page in the dock, make my app dock-ready, dock settings, dock.json, dockbar.json, dock manifest, launch steps, AI thread on launch, auto submit prompt, staged prompt, draft manifest, how do dock apps launch, why does my dock card not work, command not found in my dock launch, my dock app never answers, app launcher, featured apps, pin an app, hover zoom, alt-pin. ---  # hydrogen-dock: the Hydrogen app launcher -The dock is the launcher rail on the right edge of the Hydrogen workspace. Every card-in it is a wiki app page. The dock resolves what to SHOW (name, brief, hero)-from the page itself and how to LAUNCH from a `dock.json` manifest the app-declares at the root of its wiki page repo. Display is never duplicated into-the dock; the page is the single source of truth.+The dock is the launcher rail on the right edge of the Hydrogen workspace. Every+card in it is a wiki app page. **Any public wiki app can be a dock card**, and+users can add one themselves without anybody's permission. -## The list has two layers+The dock resolves what to SHOW (name, brief, hero) from the page itself, and how+to LAUNCH from a `dock.json` manifest at the root of the app's wiki page repo.+Display is never duplicated into the manifest; the page is the single source of+truth. -1. **The global manifest (adom-curated).** Hydrogen fetches a public manifest and-   renders its apps. Only the adom team edits it; `featured` and `order` exist-   only here. It is world-readable at:+**THE FULL PUBLISHER CONTRACT IS THE SDK PAGE:**+https://wiki.adom.inc/adom/hydrogen-dock-sdk -   `https://wiki.adom.inc/blob/bootstrap/hydrogen-bootstrap/dock/apps.json`+This skill is the working summary plus the traps. Send anyone writing a manifest+to the SDK page. -   Hydrogen refreshes it on launch and then on the poll cadence the manifest itself-   declares (`poll_seconds`, currently 6 hours), so curation changes never wait-   for an Hydrogen release. Want your app on the global list? Make it dock-ready-   (below), then open an issue on the adom/hydrogen-desktop wiki page asking-   for listing; the adom team vets and merges.+## Adding an app to a user's dock (the common request) -2. **User-added apps (self-serve, local).** Any user adds any public wiki app-   from Dock settings (the gear at the top of the rail) with `owner/slug`, or-   by asking their AI ("add adom/orbital-lab to my dock"). These render in All-   apps, are pinnable, and persist locally. They never affect other users.+1. Confirm the page exists and is public (`adom-wiki discover search -q <term>`).+2. Dock settings (the gear at the top of the rail) → Add app → `owner/slug`.+3. If the page has no manifest, the card still works: it opens the wiki page.+   Tell the user that launch steps need a `dock.json` and point the publisher at+   the SDK page. -## `dock.json` (what an app declares)--A file named exactly **`dock.json`** at the root of the app's wiki page repo:+## The manifest, in one screen  ```json {-  "dock_manifest": "1.0.0",-  "webview": { "title": "Adom TSCi", "favicon": "icons/tsci.svg" },+  "dock_manifest": "1.1.0",+  "webview": { "title": "My App", "favicon": "icons/app.svg" },   "launch": {     "mode": "container_command",+    "console": "show",     "steps": [-      { "label": "Installing Adom TSCi",-        "run": "adom-wiki pkg install adom/adom-tsci",-        "skip_if": "command -v adom-tsci",+      { "label": "Installing My App",+        "run": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; adom-wiki pkg install me/my-app'",+        "skip_if": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; command -v my-app'",         "timeout_seconds": 180 },-      { "label": "Starting the board viewer",-        "run": "adom-tsci serve --print-url",+      { "label": "Starting My App",+        "run": "sh -c 'PATH=\"$HOME/.local/bin:$PATH\"; exec my-app serve --print-url'",         "await": "url",         "timeout_seconds": 60 }     ],-    "on_ready": "navigate_placeholder"+    "on_ready": "navigate_placeholder",+    "ai": {+      "prompt": "The user just opened My App from the dock.",+      "thread": "existing",+      "thread_fallback": "new",+      "submit": "staged"+    }   } } ``` -Legacy fallback: when `dock.json` is absent, Hydrogen honors a `dock` block (same-`webview`/`launch` shape) in the page's `package.json`, and a legacy-`launch.ai_prompt` string (equivalent to `ai.prompt` with `submit: "auto"`,-`thread: "new"`). New apps should always ship `dock.json`.+Discovery order, first match wins: root `dock.json`, root `dockbar.json`, then+either name in a sub-folder (depth 2 max, never `node_modules` or dot-folders). -### webview+`mode` is `container_command` (run steps, then show a URL), `webview` (load+`webview.url` directly, no steps), or `ai_thread` (no webview at all, the click+opens a thread). -| Field | Required | Meaning |-|---|---|---|-| `title` | yes | Webview tab title while the app is loaded |-| `favicon` | no | Tab icon, a path in the app's page repo |-| `url` | for `mode: "webview"` | Page to load directly (hosted app, or the wiki page itself) |+## ⛔ The two rules that break every first attempt -### launch+**1. A serve verb MUST self-background.** Hydrogen runs the step synchronously+and waits for it to EXIT before reading stdout. A server that stays in the+foreground blocks the exec for the whole `timeout_seconds`, then the exec times+out and KILLS it, so the URL it printed is dead by the time Hydrogen polls it.+The launch fails with "started, but <url> never answered". -- `mode: "webview"`: nothing to install; the dock loads `webview.url` directly.-- `mode: "container_command"`: run `steps` in order in the user's workspace-  container, then `on_ready: "navigate_placeholder"` navigates the pre-loaded-  webview to the URL a step produced.-- `mode: "ai_thread"`: no webview at all — the click goes straight to an AI-  thread per the `ai` block.+The tell: the server is healthy when run by hand and dead when the dock runs it. -| Step field | Required | Meaning |-|---|---|---|-| `label` | yes | Shown in the launch toast while the step runs |-| `run` | yes | The command |-| `skip_if` | no | Probe command; exit 0 skips the step (idempotent installs) |-| `await` | no | `url`: scrape the first URL the command prints, hold for on_ready |-| `url_pattern` | no | Scrape regex, default `https?://\S+` |-| `timeout_seconds` | no | Per-step budget; timing out fails the launch with this label |+**2. Steps must set PATH.** Wiki-installed CLIs land in `~/.local/bin`, which is+NOT on the PATH of the shell the workspace exec uses. A step calling the app's+own CLI by bare name fails with `command not found` (exit 127) on every machine+except the developer's, where it usually happens to be on PATH already. That+also makes `skip_if` report "installed" for the author and "missing" for users. -**The `--print-url` convention** is the whole integration contract: a dock-ready-server verb prints its own reachable URL to stdout and keeps serving.+Both bundled bridges shipped both bugs. Check these two first when a card fails. -### The `ai` block (launch.ai)+## What a click does -Attach an AI thread to the launch — either as `mode: "ai_thread"` (the whole-launch) or via `on_ready: "hand_to_ai"` after container steps (the steps opened-the native app; the AI takes the placeholder webview over):--```json-"ai": {-  "prompt": "The user just launched Adom TSCi from the dock. Take over the webview tab named \"Adom TSCi\" and load the board they were last viewing.",-  "submit": "auto",-  "thread": "new",-  "typed": false-} ```+click ─▶ already open? ─▶ YES: bring that tab forward. Never a second webview.+          │ NO+          placeholder webview opens instantly (theme bg + orbital animation, free)+          ├─ steps run in order, transcript streaming into the placeholder console+          ├─ await:"url" scrapes the app URL from stdout+          ├─ Hydrogen polls it until it answers (budget = that step's timeout_seconds)+          │    never answers ─▶ the launch FAILS loudly; it does not navigate to a+          │                     dead URL and claim success+          ├─ on_ready: navigate_placeholder (show the URL) | hand_to_ai+          └─ ai block, if present: opens or reuses this app's own named thread+```++The `ai` block is its OWN branch, not an alternative to `on_ready`: an app can+show its dashboard AND open a thread about it.++**Re-click reveals the existing tab.** The dock finds it by `webview.title`+against the live workspace layout, so it holds even after a Hydrogen reload.+Publishers must keep `webview.title` stable or users get duplicate tabs.++## The ai block -| Field | Required | Meaning |+| Field | Default | Meaning | |---|---|---|-| `prompt` | yes for ai launches | The pre-canned prompt. Omitted, Hydrogen uses a sensible default naming the app |-| `submit` | no (default `auto`) | `auto`: paste + submit instantly. `staged`: the prompt sits in the chat input, ready for the user to review and send |-| `thread` | no (default `new`) | `new`: open a fresh conversation. `existing`: foreground the Claude panel and inject into the conversation the user currently has open |-| `typed` | no (default `false`) | `true`: human-jitter typing animation instead of instant paste. Best-effort: staged and existing-thread paths always type |+| `prompt` | app-naming default | The pre-canned prompt |+| `thread` | `"new"` | `"existing"` reuses THIS app's own thread, found by the name the dock gave it |+| `thread_fallback` | `"new"` | When `existing` finds none: `new`, or `current` (whatever is open) |+| `submit` | `"staged"` | `staged` waits in the input for the user; `auto` sends immediately | -`thread: "existing"` targets the conversation currently open in the panel —-true per-app conversation lookup (find *this app's* thread by title) needs-extension support and is a documented future addition, not part of 1.0.0.+Threads are opened, named and injected through the AI Threads API, so the thread+is named after the app (visible in the AI Threads panel, durable across+restarts), and injection is BACKGROUND: relaunching an app never steals focus+from whatever the user is typing. `ai.typed` is accepted and ignored.  ## The loading screen is free -The instant a launch involves a webview, Hydrogen paints the panel with the workspace-theme background token and runs the Adom orbital loading animation (transparent,-so it reflects any theme) until the app URL has truly loaded, then fades it out.-Install steps stay behind the mark with their labels ticking in the launch toast-and the full command transcript streaming into the placeholder's console. Apps-bundle nothing and opt into nothing. Details and the embeddable single-file-animation: the adom/orbital-lab wiki page.--## Testing a draft (your own Hydrogen only)--Dock settings (gear) → **Manifest URL (draft)**: point the dock at any-`apps.json` URL — a draft branch blob of `adom/hydrogen-bootstrap`, or your own file —-and this Hydrogen renders that list instead of the production one. A **DRAFT** badge-stays on the rail while active. Clear the field to return to the production-list. This is how curators validate a change before it reaches the fleet, and-how publishers preview how their entry will look once listed.--Per-app, no draft needed: Dock settings → Add app → `owner/slug` reads your-page's live `dock.json` immediately.--## Publisher checklist (make your app dock-ready)--1. Public wiki page with a hero and a billboard-quality one-line brief; the-   dock shows exactly these.-2. Install step is `adom-wiki pkg install <owner>/<slug>` with a `skip_if`-   probe so relaunches skip it.-3. Server verb takes `--print-url`; that step gets `await: "url"`.-4. Sane `timeout_seconds` (180 install / 60 serve are good defaults).-5. Add `dock.json` to your page repo root and push the page.-6. Preview it in the workbench on the adom/hydrogen-desktop-dock wiki page —-   card, launch transcript, and AI-thread behavior, all simulated.-7. Test from your own dock: Dock settings, Add app, `owner/slug`, click it,-   watch the steps run behind the loading mark.--## Adding an app for a user (AI recipe)--When the user asks you to add a wiki app to their dock:--1. Confirm the page exists and is public (`adom-wiki discover search`).-2. Open Dock settings (gear at the top of the rail), Add app, enter-   `owner/slug`; or drive the equivalent Hydrogen API if you are automating.-3. If the page has no `dock.json`, it still loads as a plain webview of its-   wiki page; tell the user launch steps need the manifest and point the-   publisher at this skill.+The instant a launch involves a webview, Hydrogen paints the panel with the theme+background and runs the Adom orbital animation until the app URL truly loads.+Apps bundle nothing and opt into nothing. `launch.console: "hide"` mutes the+command transcript (never the failures) when the output would be noise.++## Testing a draft++- **Per-app, instant:** Dock settings → Add app → `owner/slug` reads the live+  manifest immediately. Fastest loop.+- **Run the steps by hand in the container first**, exactly as written, and check+  the serve step RETURNS instead of blocking.+- **Whole-list draft:** Dock settings → Manifest URL (draft) points the dock at+  any `apps.json`. A DRAFT badge stays on the rail while active.++## Troubleshooting by symptom++| Symptom | Cause |+|---|---|+| `command not found`, exit 127 | PATH: `~/.local/bin` is not on the exec shell's PATH |+| "started, but <url> never answered" | The serve command did not self-background |+| "no URL within Ns" | Nothing matching `https?://\S+` on stdout (printed to stderr?) |+| Install step runs every launch | `skip_if` missing or probing the wrong name |+| A duplicate tab appears | `webview.title` changed between versions |+| The `ai` prompt never fires | `mode: "webview"` has no steps and no ai branch |+| Works for the author, fails for users | Tested where the CLI was already installed |++## The curated list vs user-added++The global list (`https://wiki.adom.inc/blob/bootstrap/hydrogen-bootstrap/dock/apps.json`)+is adom-curated and is ONLY a list: which apps appear and in what order, plus+`featured` and `order`. It deliberately carries no launch blocks, because an+inline launch there overrides and freezes the manifest the app maintains. Being+on it is not required to be dock-ready. To request listing, open an issue on the+adom/hydrogen-desktop wiki page.