← Commit history

Publish 0.1.12

Kyle Bergstedt ·199bccd967 · ·parent 8358f0f
51 files changed +2755−74
README.md+29−29SKILL.md+6−6install.sh+8−8package.json+16−16page.json+15−15skills/hydrogen-api-key-mac/._SKILL.mdskills/hydrogen-api-key-mac/SKILL.md+103skills/hydrogen-api-mac/._SKILL.mdskills/hydrogen-api-mac/SKILL.md+63skills/hydrogen-browser-picker-mac/._SKILL.mdskills/hydrogen-browser-picker-mac/SKILL.md+74skills/hydrogen-captions-mac/._SKILL.mdskills/hydrogen-captions-mac/SKILL.md+57skills/hydrogen-container-stats/._SKILL.mdskills/hydrogen-container-stats/SKILL.md+154skills/hydrogen-container/._SKILL.mdskills/hydrogen-container/SKILL.md+131skills/hydrogen-eda-install/._SKILL.mdskills/hydrogen-eda-install/SKILL.md+70skills/hydrogen-networking/._SKILL.mdskills/hydrogen-networking/SKILL.md+182skills/hydrogen-notifications-mac/._SKILL.mdskills/hydrogen-notifications-mac/SKILL.md+48skills/hydrogen-open-url-mac/._SKILL.mdskills/hydrogen-open-url-mac/SKILL.md+67skills/hydrogen-permissions-mac/._SKILL.mdskills/hydrogen-permissions-mac/SKILL.md+77skills/hydrogen-port-watcher/._SKILL.mdskills/hydrogen-port-watcher/SKILL.md+281skills/hydrogen-ports/._SKILL.mdskills/hydrogen-ports/SKILL.md+224skills/hydrogen-recording-mac/._SKILL.mdskills/hydrogen-recording-mac/SKILL.md+73skills/hydrogen-screen-lock-mac/._SKILL.mdskills/hydrogen-screen-lock-mac/SKILL.md+61skills/hydrogen-self-screenshot-mac/._SKILL.mdskills/hydrogen-self-screenshot-mac/SKILL.md+93skills/hydrogen-setup-steps/._SKILL.mdskills/hydrogen-setup-steps/SKILL.md+94skills/hydrogen-setup/._SKILL.mdskills/hydrogen-setup/SKILL.md+199skills/hydrogen-topology/._SKILL.mdskills/hydrogen-topology/SKILL.md+134skills/hydrogen-ui-mac/._SKILL.mdskills/hydrogen-ui-mac/SKILL.md+60skills/hydrogen-volume/._SKILL.mdskills/hydrogen-volume/SKILL.md+110skills/hydrogen-workspace-lifecycle/._SKILL.mdskills/hydrogen-workspace-lifecycle/SKILL.md+173skills/hydrogen-workspace-monitoring/._SKILL.mdskills/hydrogen-workspace-monitoring/SKILL.md+153
README.md+29−29
@@ -1,24 +1,24 @@-# adom/hd-mac-bootstrap+# adom/hydrogen-mac-bootstrap -The **macOS** platform layer for Hydrogen Desktop. On a Mac, HD's workspace runs+The **macOS** platform layer for Adom Hydrogen. On a Mac, Hydrogen's workspace runs inside a **Lima Ubuntu VM** (`hd-builder`) that hosts the **`Adom-Workspace` systemd-nspawn machine**; this package adds everything specific to that runtime on top of the platform-generic `adom/hd-bootstrap`. -<img src="screenshots/workspace-macos.png" alt="Hydrogen Desktop running natively on macOS — VS Code + Claude on the left, an Adom webview on the right, workspace in the local Lima/nspawn machine" width="760">+<img src="screenshots/workspace-macos.png" alt="Adom Hydrogen running natively on macOS — VS Code + Claude on the left, an Adom webview on the right, workspace in the local Lima/nspawn machine" width="760">  ```-adom-wiki pkg install adom/hd-mac-bootstrap --allow-sudo   # the golden Lima image bakes this;-                                                           # HD's setup re-converges it every install+adom-wiki pkg install adom/hydrogen-mac-bootstrap --allow-sudo   # the golden Lima image bakes this;+                                                           # Hydrogen's setup re-converges it every install ```  ## The platform-layered bootstrap stack  ``` adom/core                         ← Adom ecosystem (generic; shared with web Hydrogen)-  └─ adom/hd-bootstrap            ← Hydrogen Desktop, platform-GENERIC+  └─ adom/hd-bootstrap            ← Adom Hydrogen, platform-GENERIC        ├─ adom/hd-windows-bootstrap   ← WSL2 runtime-       ├─ adom/hd-mac-bootstrap       ← THIS — Lima/nspawn runtime+       ├─ adom/hydrogen-mac-bootstrap       ← THIS — Lima/nspawn runtime        └─ adom/hd-ubuntu-bootstrap    ← native-Ubuntu runtime ``` @@ -27,21 +27,21 @@ so `core` stays generic across web + desktop Hydrogen and every host OS.  ## What this layer adds -- **Dependencies:** `adom/hd-bootstrap` (generic HD) + `adom/adom-workspace-updater`+- **Dependencies:** `adom/hd-bootstrap` (generic Hydrogen) + `adom/adom-workspace-updater`   (the in-machine systemd updater daemon).-- **Bundled skills:** the macOS-runtime `hd-*` skills — `hd-container`,-  `hd-container-stats`, `hd-networking`, `hd-ports`, `hd-port-watcher`, `hd-setup`,-  `hd-setup-steps`, `hd-topology`, `hd-volume`, `hd-workspace-lifecycle`,-  `hd-workspace-monitoring` (the Lima/nspawn analogs of the Windows layer's WSL2 set,-  sourced from `hydrogen-desktop/skills/public-facing/machine/`).+- **Bundled skills:** the macOS-runtime `hd-*` skills — `hydrogen-container`,+  `hydrogen-container-stats`, `hydrogen-networking`, `hydrogen-ports`, `hydrogen-port-watcher`, `hydrogen-setup`,+  `hydrogen-setup-steps`, `hydrogen-topology`, `hydrogen-volume`, `hydrogen-workspace-lifecycle`,+  `hydrogen-workspace-monitoring` (the Lima/nspawn analogs of the Windows layer's WSL2 set,+  sourced from `adom-hydrogen/skills/public-facing/machine/`). - **install script:** deploys those skills into `~/.claude/skills/` on top of the   generic layer, and installs the **`shot`** helper (`~/.local/bin/shot`) — one-call,   token-efficient workspace screenshots that print only a PNG path (see-  `hd-self-screenshot-mac`) — plus the **Adom-branded Claude Code statusline**+  `hydrogen-self-screenshot-mac`) — plus the **Adom-branded Claude Code statusline**   (`~/.claude/statusline.sh`: model + context bar + 5h usage; only set when the   user hasn't configured their own). Note `adom-wiki pkg install` does **not** re-run install-  scripts for already-baked packages — HD's setup cascade (0.1.135+) runs the chain's-  deploy scripts explicitly (`core → hd-bootstrap → hd-mac-bootstrap`) so a fresh+  scripts for already-baked packages — Hydrogen's setup cascade (0.1.135+) runs the chain's+  deploy scripts explicitly (`core → hd-bootstrap → hydrogen-mac-bootstrap`) so a fresh   skills release actually reaches an already-baked machine.  ## The runtime difference vs. the Windows layer@@ -65,17 +65,17 @@ stays in `hd-bootstrap`, the Windows half drops into `hd-windows-bootstrap`. Thi layer carries the parallel **macOS halves** in `skills/<name>-mac/`.  **Companions authored (11):**-- `hd-api-mac` — `adom-host` gateway, `~/.adom/hd-control-url` discovery, vz-no-loopback, `/workspace/*` (nspawn) endpoints (mirror of `hd-api-windows`' `/wsl/*` tables).-- `hd-api-key-mac` — diagnose/repair the injected `/var/run/adom/api-key` session token (adom-cli 401s, tmpfs wipe on machine reboot, pre-0.1.148 post-auth gap): pull via `GET /auth-token`, prompt the user to reauthorize, or trigger the `inject-api-key` setup step.-- `hd-notifications-mac` — Notification Center toasts + emergency Dock-bounce + the Notifications grant (mirror of `hd-notifications-windows`' WinRT/AUMID).-- `hd-recording-mac` — the native recorder is `#[cfg(windows)]`, so on macOS recording is in-app getDisplayMedia / pup / AD relay; ScreenCaptureKit native recorder **not yet built** (honest gap). Screen Recording TCC.-- `hd-screen-lock-mac` — IOKit `IOPMAssertionCreateWithName`/`PreventUserIdleDisplaySleep`, `pmset -g assertions`, `caffeinate` (vs `SetThreadExecutionState`).-- `hd-self-screenshot-mac` — `screencapture`/CoreGraphics + CDP `Page.captureScreenshot`; per-panel crop resolves the rect via the `dom.measure` RPC (no CDP on WKWebView) + `image`-crate crop (vs Win32 `PrintWindow`).-- `hd-browser-picker-mac` — WKWebView-over-`localhost` interception + LaunchServices `open -a` native browsers (vs WebView2/COM 6-layer interception).-- `hd-permissions-mac` — the macOS TCC model (Screen Recording, Accessibility, Automation, Camera/Mic) + preflight/relaunch (vs UAC).-- `hd-open-url-mac` — `open`/`open -a … --new-window` LaunchServices launch + control-URL discovery (vs `chrome.exe`/`msedge.exe`).-- `hd-ui-mac` — driving the UI via the `hd-ui-command`→`/ui/_result` command bus + `invoke_ui_action` (WKWebView has no CDP; raw CDP clicking is Windows-only).-- `hd-captions-mac` — workspace caption is platform-neutral; the desktop overlay (`desktop_caption`) is a **Windows-only** stub on macOS (honest gap).+- `hydrogen-api-mac` — `adom-host` gateway, `~/.adom/hd-control-url` discovery, vz-no-loopback, `/workspace/*` (nspawn) endpoints (mirror of `hd-api-windows`' `/wsl/*` tables).+- `hydrogen-api-key-mac` — diagnose/repair the injected `/var/run/adom/api-key` session token (adom-cli 401s, tmpfs wipe on machine reboot, pre-0.1.148 post-auth gap): pull via `GET /auth-token`, prompt the user to reauthorize, or trigger the `inject-api-key` setup step.+- `hydrogen-notifications-mac` — Notification Center toasts + emergency Dock-bounce + the Notifications grant (mirror of `hd-notifications-windows`' WinRT/AUMID).+- `hydrogen-recording-mac` — the native recorder is `#[cfg(windows)]`, so on macOS recording is in-app getDisplayMedia / pup / AD relay; ScreenCaptureKit native recorder **not yet built** (honest gap). Screen Recording TCC.+- `hydrogen-screen-lock-mac` — IOKit `IOPMAssertionCreateWithName`/`PreventUserIdleDisplaySleep`, `pmset -g assertions`, `caffeinate` (vs `SetThreadExecutionState`).+- `hydrogen-self-screenshot-mac` — `screencapture`/CoreGraphics + CDP `Page.captureScreenshot`; per-panel crop resolves the rect via the `dom.measure` RPC (no CDP on WKWebView) + `image`-crate crop (vs Win32 `PrintWindow`).+- `hydrogen-browser-picker-mac` — WKWebView-over-`localhost` interception + LaunchServices `open -a` native browsers (vs WebView2/COM 6-layer interception).+- `hydrogen-permissions-mac` — the macOS TCC model (Screen Recording, Accessibility, Automation, Camera/Mic) + preflight/relaunch (vs UAC).+- `hydrogen-open-url-mac` — `open`/`open -a … --new-window` LaunchServices launch + control-URL discovery (vs `chrome.exe`/`msedge.exe`).+- `hydrogen-ui-mac` — driving the UI via the `hd-ui-command`→`/ui/_result` command bus + `invoke_ui_action` (WKWebView has no CDP; raw CDP clicking is Windows-only).+- `hydrogen-captions-mac` — workspace caption is platform-neutral; the desktop overlay (`desktop_caption`) is a **Windows-only** stub on macOS (honest gap).  > Reconcile the exact 13-skill set against `adom/hd-windows-bootstrap`'s companions > (its source is `--hide-source` sealed; read it from a workspace with org access).@@ -83,11 +83,11 @@ layer carries the parallel **macOS halves** in `skills/<name>-mac/`. > skills, not its mixed companions).  > Note: the macOS-runtime skills in `skills/` that are **whole** (the `machine/` bucket-> — `hd-setup`, `hd-container`, …) correspond to the Windows layer's *clean* skills, not+> — `hydrogen-setup`, `hydrogen-container`, …) correspond to the Windows layer's *clean* skills, not > its mixed companions, so they ship complete rather than as `-mac` halves. The exact > 13-skill set should be reconciled against `adom/hd-windows-bootstrap`'s companions > (its source is `--hide-source` sealed; read it from a workspace with org access). -> NOTE — naming: the canonical sibling slug is **`hd-mac-bootstrap`** (as referenced+> NOTE — naming: the canonical sibling slug is **`hydrogen-mac-bootstrap`** (as referenced > by `adom/hd-windows-bootstrap`'s own description and stack diagram), not > `hd-macos-bootstrap`.
SKILL.md+6−6
@@ -1,14 +1,14 @@ ----name: hd-mac-bootstrap-description: The macOS platform layer (adompkg bootstrap) for Hydrogen Desktop — converges a Lima/nspawn Linux workspace to "ready to run inside HD on a Mac". Depends on adom/hd-bootstrap; bundles the macOS-runtime hd-* skills. Installed by the golden Lima image (hd-lima-image). Not an interactive skill — this is a bootstrap package.+name: hydrogen-mac-bootstrap+description: The macOS platform layer (adompkg bootstrap) for Adom Hydrogen — converges a Lima/nspawn Linux workspace to "ready to run inside Hydrogen on a Mac". Depends on adom/hd-bootstrap; bundles the macOS-runtime hd-* skills. Installed by the golden Lima image (hd-lima-image). Not an interactive skill — this is a bootstrap package. --- -# adom/hd-mac-bootstrap+# adom/hydrogen-mac-bootstrap  This is a **bootstrap package**, not an interactive skill. Installing it-(`ADOMPKG_ALLOW_SUDO=1 adompkg install adom/hd-mac-bootstrap`) converges a Lima-Ubuntu / systemd-nspawn workspace to the macOS Hydrogen Desktop runtime: it pulls-`adom/hd-bootstrap` (generic HD) + `adom/adom-workspace-updater`, and deploys the+(`ADOMPKG_ALLOW_SUDO=1 adompkg install adom/hydrogen-mac-bootstrap`) converges a Lima+Ubuntu / systemd-nspawn workspace to the macOS Adom Hydrogen runtime: it pulls+`adom/hd-bootstrap` (generic Hydrogen) + `adom/adom-workspace-updater`, and deploys the macOS-runtime `hd-*` skills.  See README.md for the layered stack and the runtime differences vs. the Windows layer.
install.sh+8−8
@@ -1,5 +1,5 @@ #!/usr/bin/env bash-# install (scripts.install) for adom/hd-mac-bootstrap — the macOS (Lima/nspawn) platform layer.+# install (scripts.install) for adom/hydrogen-mac-bootstrap — the macOS (Lima/nspawn) platform layer. # # Runs as the workspace user after the dependency tree (adom/hd-bootstrap → adom/core, # adom/adom-workspace-updater) has installed. The generic adom/hd-bootstrap layer already@@ -14,7 +14,7 @@ set -euo pipefail HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" SKILLS="$HOME/.claude/skills" -log() { printf '[hd-mac-bootstrap] %s\n' "$*"; }+log() { printf '[hydrogen-mac-bootstrap] %s\n' "$*"; }  # ── deploy the bundled macOS-runtime hd-* skills (flat into ~/.claude/skills/<name>/) ──────── mkdir -p "$SKILLS"@@ -27,7 +27,7 @@ for d in "$HERE"/skills/hd-*/; do   n=$((n + 1)) done log "deployed $n macOS-runtime hd-* skills -> $SKILLS"-[ "$n" -gt 0 ] || { echo "[hd-mac-bootstrap] ERROR: no skills deployed" >&2; exit 1; }+[ "$n" -gt 0 ] || { echo "[hydrogen-mac-bootstrap] ERROR: no skills deployed" >&2; exit 1; }  # ── code-server workbench seed (trusted domains '*' + activity-bar unpin), macOS path ──────── # The generic layer seeds the workbench; re-assert here so a mac-only re-install converges it.@@ -42,7 +42,7 @@ fi # ── the `shot` helper — token-efficient screenshots for the workspace AI ───────────────────── # `adom-cli hydrogen screenshot` prints a ~300 KB base64 dataUrl to stdout; raw in an AI # conversation that's ~85k wasted tokens per capture. `shot` decodes inline and prints only-# the PNG path (the AI then Reads it). See hd-self-screenshot-mac for the doctrine.+# the PNG path (the AI then Reads it). See hydrogen-self-screenshot-mac for the doctrine. # The canonical script lives at bin/shot in the PAGE repo, but the pkg tarball only ships # a fixed file set (no bin/) — so the same script is EMBEDDED here. Keep the two in sync. mkdir -p "$HOME/.local/bin"@@ -51,8 +51,8 @@ if [ -f "$HERE/bin/shot" ]; then else   cat > "$HOME/.local/bin/shot" <<'SHOT_EOF' #!/usr/bin/env bash-# shot — one-call HD screenshot that prints ONLY a PNG path (keeps base64 out of the-# AI's context). Installed by adom/hd-mac-bootstrap (canonical source: bin/shot there).+# shot — one-call Hydrogen screenshot that prints ONLY a PNG path (keeps base64 out of the+# AI's context). Installed by adom/hydrogen-mac-bootstrap (canonical source: bin/shot there). # #   shot                       # whole workspace (all panels) #   shot screen                # entire screen (desktop, other apps)@@ -158,9 +158,9 @@ let s = {}; try { s = JSON.parse(fs.readFileSync(p, "utf8")); } catch (e) {} if (!s.statusLine) {   s.statusLine = { type: "command", command: "~/.claude/statusline.sh" };   fs.writeFileSync(p, JSON.stringify(s, null, 2));-  console.log("[hd-mac-bootstrap] statusLine set in settings.json");+  console.log("[hydrogen-mac-bootstrap] statusLine set in settings.json"); } else {-  console.log("[hd-mac-bootstrap] statusLine already configured - left as-is");+  console.log("[hydrogen-mac-bootstrap] statusLine already configured - left as-is"); }' 2>/dev/null || log "WARN: node unavailable - statusLine not merged into settings.json" log "installed the Claude Code statusline -> ~/.claude/statusline.sh" 
package.json+16−16
@@ -1,16 +1,16 @@ {   "schema_version": 1,-  "slug": "hd-mac-bootstrap",+  "slug": "hydrogen-mac-bootstrap",   "type": "bootstrap",-  "title": "Hydrogen Desktop (macOS / Lima)",-  "brief": "The macOS platform layer for Hydrogen Desktop.",-  "version": "0.1.11",-  "description": "The macOS platform layer for Hydrogen Desktop, where the workspace runs in a Lima Ubuntu VM hosting the Adom-Workspace systemd-nspawn machine. Depends on adom/hd-bootstrap (the platform-generic HD layer) and adds everything specific to the Lima/nspawn runtime: the container/networking/ports/setup/topology/volume/workspace-lifecycle hd-* skills (the macOS-runtime bucket), the in-machine workspace-updater daemon, and the macOS code-server workbench seed. Sibling layers adom/hd-windows-bootstrap (WSL2) and adom/hd-ubuntu-bootstrap depend on the same adom/hd-bootstrap. This is the package the golden Lima image installs.",+  "title": "Adom Hydrogen (macOS / Lima)",+  "brief": "The macOS platform layer for Adom Hydrogen.",+  "version": "0.1.12",+  "description": "The macOS platform layer for Adom Hydrogen, where the workspace runs in a Lima Ubuntu VM hosting the Adom-Workspace systemd-nspawn machine. Depends on adom/hd-bootstrap (the platform-generic Hydrogen layer) and adds everything specific to the Lima/nspawn runtime: the container/networking/ports/setup/topology/volume/workspace-lifecycle hd-* skills (the macOS-runtime bucket), the in-machine workspace-updater daemon, and the macOS code-server workbench seed. Sibling layers adom/hd-windows-bootstrap (WSL2) and adom/hd-ubuntu-bootstrap depend on the same adom/hd-bootstrap. This is the package the golden Lima image installs.",   "org": "adom",   "tags": [     "bootstrap",-    "hydrogen-desktop",-    "hd",+    "adom-hydrogen",+    "hydrogen",     "macos",     "mac",     "lima",@@ -36,24 +36,24 @@     "type": "image",     "path": "screenshots/hero-v3.png"   },-  "discovery_pitch": "The macOS platform layer for Hydrogen Desktop — the Lima/nspawn workspace runtime plus every mac-specific hd-* skill.",+  "discovery_pitch": "The macOS platform layer for Adom Hydrogen — the Lima/nspawn workspace runtime plus every mac-specific hd-* skill.",   "discovery_triggers": [-    "hydrogen desktop mac",-    "hd on macos",-    "hd mac bootstrap",+    "Adom Hydrogen mac",+    "hydrogen on macos",+    "hydrogen mac bootstrap",     "lima workspace",     "nspawn machine",     "hd-builder vm",-    "mac hd skills",-    "screenshot hd mac",+    "mac hydrogen skills",+    "screenshot hydrogen mac",     "macos permissions tcc",-    "hd mac setup",+    "hydrogen mac setup",     "adom-workspace machine"   ],   "sample_prompts": [     {       "label": "Converge skills",-      "prompt": "update my HD mac skills to the latest published layer"+      "prompt": "update my Hydrogen mac skills to the latest published layer"     },     {       "label": "Screenshot",@@ -61,7 +61,7 @@     },     {       "label": "Fix permissions",-      "prompt": "why is my HD screenshot black on macOS"+      "prompt": "why is my Hydrogen screenshot black on macOS"     },     {       "label": "Workspace status",
page.json+15−15
@@ -2,9 +2,9 @@   "schema_version": 1,   "type": "bootstrap",   "slug": "hydrogen-mac-bootstrap",-  "title": "Hydrogen Desktop (macOS / Lima)",-  "brief": "The macOS platform layer for Hydrogen Desktop.",-  "version": "0.1.11",+  "title": "Adom Hydrogen (macOS / Lima)",+  "brief": "The macOS platform layer for Adom Hydrogen.",+  "version": "0.1.12",   "license": "proprietary",   "visibility": "public",   "metadata": {@@ -16,24 +16,24 @@     "type": "image",     "path": "screenshots/hero-v3.png"   },-  "discovery_pitch": "The macOS platform layer for Hydrogen Desktop — the Lima/nspawn workspace runtime plus every mac-specific hd-* skill.",+  "discovery_pitch": "The macOS platform layer for Adom Hydrogen — the Lima/nspawn workspace runtime plus every mac-specific hd-* skill.",   "discovery_triggers": [-    "hydrogen desktop mac",-    "hd on macos",-    "hd mac bootstrap",+    "Adom Hydrogen mac",+    "hydrogen on macos",+    "hydrogen mac bootstrap",     "lima workspace",     "nspawn machine",     "hd-builder vm",-    "mac hd skills",-    "screenshot hd mac",+    "mac hydrogen skills",+    "screenshot hydrogen mac",     "macos permissions tcc",-    "hd mac setup",+    "hydrogen mac setup",     "adom-workspace machine"   ],   "sample_prompts": [     {       "label": "Converge skills",-      "prompt": "update my HD mac skills to the latest published layer"+      "prompt": "update my Hydrogen mac skills to the latest published layer"     },     {       "label": "Screenshot",@@ -41,19 +41,19 @@     },     {       "label": "Fix permissions",-      "prompt": "why is my HD screenshot black on macOS"+      "prompt": "why is my Hydrogen screenshot black on macOS"     },     {       "label": "Workspace status",       "prompt": "is my Lima workspace machine healthy"     }   ],-  "description": "The macOS platform layer for Hydrogen Desktop, where the workspace runs in a Lima Ubuntu VM hosting the Adom-Workspace systemd-nspawn machine. Depends on adom/hd-bootstrap (the platform-generic HD layer) and adds everything specific to the Lima/nspawn runtime: the container/networking/ports/setup/topology/volume/workspace-lifecycle hd-* skills (the macOS-runtime bucket), the in-machine workspace-updater daemon, and the macOS code-server workbench seed. Sibling layers adom/hd-windows-bootstrap (WSL2) and adom/hd-ubuntu-bootstrap depend on the same adom/hd-bootstrap. This is the package the golden Lima image installs.",+  "description": "The macOS platform layer for Adom Hydrogen, where the workspace runs in a Lima Ubuntu VM hosting the Adom-Workspace systemd-nspawn machine. Depends on adom/hd-bootstrap (the platform-generic Hydrogen layer) and adds everything specific to the Lima/nspawn runtime: the container/networking/ports/setup/topology/volume/workspace-lifecycle hd-* skills (the macOS-runtime bucket), the in-machine workspace-updater daemon, and the macOS code-server workbench seed. Sibling layers adom/hd-windows-bootstrap (WSL2) and adom/hd-ubuntu-bootstrap depend on the same adom/hd-bootstrap. This is the package the golden Lima image installs.",   "org": "adom",   "tags": [     "bootstrap",-    "hydrogen-desktop",-    "hd",+    "adom-hydrogen",+    "hydrogen",     "macos",     "mac",     "lima",
skills/hydrogen-api-key-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-api-key-mac/SKILL.mdadded+103
@@ -0,0 +1,103 @@+---+name: hydrogen-api-key-mac+description: >+  Diagnose and repair the workspace's Adom API key (`/var/run/adom/api-key`) on macOS+  Adom Hydrogen — the injected session token that adom-cli and every Adom tool+  authenticate with. Use when adom-cli returns 401/unauthorized, `/var/run/adom/api-key`+  is missing or empty, "adom-cli can't reach Adom", or the user asks to reauthorize+  Adom / re-pull the API key. Covers: pulling the current token from Hydrogen via+  `GET /auth-token`, re-writing the key (passwordless sudo), prompting the user to+  sign in to Hydrogen when the host has no valid session, and triggering Hydrogen's+  inject-api-key setup step. Trigger words — api key, api-key, /var/run/adom,+  401 unauthorized, adom-cli auth, reauthorize adom, re-auth adom, pull api key,+  session token missing, fix adom auth, carbon 401.+---++# hydrogen-api-key-mac — recover the workspace Adom API key++`/var/run/adom/api-key` (mode 644) holds the user's Adom **session token**, injected+into the `Adom-Workspace` machine by Hydrogen. `adom-cli` and the other Adom CLIs read it to+authenticate against carbon (through Hydrogen's proxy over `adom-host`). When it's missing+or stale, every Adom platform call fails — usually surfacing as `401`/`unauthorized`+from `adom-cli`.++## Why it goes missing (know before fixing)++- `/var/run` is **tmpfs** — a machine or Lima-VM reboot silently wipes the key even+  though the user's session on the Mac is still valid.+- Hydrogen writes it at three moments: the `inject-api-key` setup step, **every Adom auth+  completion**, and **on every Hydrogen launch** (the last two are Hydrogen ≥ 0.1.148; older HDs+  only wrote it during setup — if the user signed in *after* setup ran, the key never+  landed. That gap is exactly what this skill recovers).+- The setup step self-skips with "sign in later" when the user wasn't signed in yet.++## Diagnose (two commands)++```bash+wc -c /var/run/adom/api-key 2>/dev/null   # missing/0 bytes → key absent+timeout 15 adom-cli carbon user get        # 401/unauthorized → key stale or absent+```++| Symptom | Meaning | Fix |+|---|---|---|+| file missing/empty, host signed in | tmpfs wipe or pre-0.1.148 auth gap | Pull the token (below) |+| file present, `401` | host session rotated/expired since injection | Pull the token (below) |+| pull says no session on host | user isn't signed in to Hydrogen | Prompt reauthorization (below) |++## Fix — pull the current token from Hydrogen++Hydrogen's control API serves the raw session token at `GET /auth-token` (raw text when+signed in; a JSON `{"error":"no session token"}` object when not). The `adom` user+has passwordless sudo:++```bash+CTRL="$(cat ~/.adom/hd-control-url)"          # e.g. http://adom-host:<control> — see hydrogen-api-mac+TOKEN="$(curl -sf "$CTRL/auth-token")"+case "$TOKEN" in+  ""|\{*) echo "NO SESSION on host — user must sign in to Hydrogen (see below)" ;;+  *) sudo mkdir -p /var/run/adom \+       && printf %s "$TOKEN" | sudo tee /var/run/adom/api-key >/dev/null \+       && sudo chmod 644 /var/run/adom/api-key \+       && echo "key written ($(wc -c < /var/run/adom/api-key) bytes)" ;;+esac+timeout 15 adom-cli carbon user get            # VERIFY — must return user JSON, not 401+```++Always verify with `adom-cli carbon user get` — a written key proves nothing if the+host session itself expired (then the pull "succeeds" but carbon still 401s →+reauthorization needed).++## Prompt the user to reauthorize++When the host has no session (or the pulled token still 401s), the fix is a human+step — **tell the user in chat**, plainly:++> Your Adom session has expired (or you're not signed in). Please bring up the+> Adom Hydrogen window and sign in to Adom — I'll pick it up automatically and+> finish fixing the workspace auth.++Optionally also raise a native toast so they see it outside the editor (see+`hydrogen-notifications-mac`). Then poll — on Hydrogen ≥ 0.1.148 auth completion re-injects the+key automatically:++```bash+for i in $(seq 1 60); do [ -s /var/run/adom/api-key ] && break; sleep 5; done+timeout 15 adom-cli carbon user get+```++If the key never appears (older Hydrogen), re-run the pull block above once the user+confirms they signed in.++## Alternative: trigger Hydrogen's setup step++`POST "$CTRL/setup/step/inject-api-key"` re-runs the injection through Hydrogen's Setup+panel (the panel is force-shown; the user sees live progress; the step also+validates against carbon). Use this when you want the user-visible, HD-audited path+instead of writing the file yourself. Do **NOT** use `POST /setup/run-step` — that+endpoint is deprecated and hard-refused.++## Related++- `hydrogen-api-mac` — `~/.adom/hd-control-url` discovery + the `adom-host` gateway (why+  `127.0.0.1` doesn't reach Hydrogen from the machine).+- `hydrogen-setup-steps` — where `inject-api-key` sits in the install cascade.
skills/hydrogen-api-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-api-mac/SKILL.mdadded+63
@@ -0,0 +1,63 @@+---+name: hydrogen-api-mac+description: >+  macOS platform companion to hydrogen-api — the Apple/Lima-specific half of reaching Hydrogen's+  control API. The full endpoint catalog lives in hydrogen-api (adom/hd-bootstrap); THIS+  skill carries only what differs on macOS: the adom-host gateway (vz doesn't mirror+  loopback), the ~/.adom/hd-control-url discovery file, Hydrogen binding 0.0.0.0, and the+  nspawn machine-runtime endpoints (the /workspace/* routes, /wsl/* names kept for+  wire-compat). Mirror of hd-api-windows (the /wsl/* WSL networking tables). Trigger+  words — hydrogen control api macos, adom-host, hd-control-url, reach Hydrogen from the workspace,+  machine runtime endpoints, vz loopback, workspace health macos.+---++# hydrogen-api (macOS) — reaching the control API across the Lima boundary++This is the **macOS half** of `hydrogen-api`. The endpoint catalog, `_manifest`, auth, and+every route table live in the generic **hydrogen-api** skill — read that first. This skill+only covers how the **Lima/nspawn boundary** changes *reachability* and *discovery* on+a Mac.++## Base URL + the adom-host gateway++- On the **macOS host**, Hydrogen's control API is `http://127.0.0.1:<dynamic>`.+- From **inside the workspace machine**, the Apple `vz` VM does **NOT** mirror host+  loopback, so `127.0.0.1` won't reach Hydrogen. Hydrogen binds its host services to `0.0.0.0` and+  the workspace reaches them through the **`adom-host` gateway**:+  `http://adom-host:<dynamic>`.++## ⭐ Finding the control port — `~/.adom/hd-control-url`++The port is dynamic. **From the workspace, read the discovery file** Hydrogen writes every+launch (already pointed at the `adom-host` gateway):++```bash+CTRL="$(cat ~/.adom/hd-control-url)"     # e.g. http://adom-host:<control>+curl -sf "$CTRL/health"+```++Never hard-code the port or assume `127.0.0.1` from inside the machine — that's the #1+macOS-specific failure when porting a Windows playbook that used a `\\wsl$`/loopback+path.++## Machine-runtime endpoints++The default macOS runtime is the **nspawn machine** (not Docker). Hydrogen exposes the+workspace-runtime routes under `/workspace/*` — and **keeps the `/wsl/*` route names as+wire-compat aliases**, so a caller written against the Windows tables still resolves:++| Method | Path | Notes |+|---|---|---|+| GET | `/workspace/status` | machine registered / running / code-server reachable |+| GET | `/workspace/health` | deep health: machine + code-server + host reachability |+| GET | `/container-status` | runtime available, image/machine present, workspace running |++`/test/probe-dialogs` classifies **macOS permission prompts** (Screen Recording /+Accessibility / installer pkg) as blockers — the macOS analog of the Windows+UAC/installer dialogs.++## NOT here (lives in the Windows companion)++The `\\wsl$` share paths, WSL2 port-proxy / `netsh` forwarding, and the Windows+loopback-mirroring assumptions are in `hd-api-windows`. On macOS reachability is the+`adom-host` gateway + `~/.adom/hd-control-url`, full stop.
skills/hydrogen-browser-picker-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-browser-picker-mac/SKILL.mdadded+74
@@ -0,0 +1,74 @@+---+name: hydrogen-browser-picker-mac+description: >+  macOS platform companion to hydrogen-browser-picker — the Apple-specific half of how+  Adom Hydrogen intercepts EVERY URL and routes it through the Browser Picker.+  The picker dialog, the 3 branded variants, the 5-second auto-countdown, the+  fresh-window toggle, and the prefs API all live in hydrogen-browser-picker+  (adom/hd-bootstrap); THIS skill carries only what's wired differently on macOS:+  the top frame is a WKWebView served over http://localhost (not WebView2), and the+  native-browser targets are launched via LaunchServices (`open -a`). Mirror of+  hd-browser-picker-windows (the WebView2 / ICoreWebView2 COM / 6-layer machinery).+  Trigger words — browser picker macos, WKWebView interception, open -a, native+  browser macos, Safari Chrome Arc Edge, localhost top frame, shell.open macos.+---++# hydrogen-browser-picker (macOS) — WKWebView interception + `open -a`++This is the **macOS half** of `hydrogen-browser-picker`. The user-facing behavior — every+URL routes through the Browser Picker (native browser / Hydrogen tab / Hydrogen window / Pup),+the 3 branded variants, the 5-second auto-timer, the fresh-window toggle, the prefs API+— is **identical on every host** and lives in the generic **hydrogen-browser-picker** skill.+Read that first; this skill only documents what's Apple-specific.++## The top frame is a WKWebView over `http://localhost`++Hydrogen's webview host on macOS is **WKWebView** (via wry), and the Hydrogen frontend is+served over `http://localhost` rather than the Windows `tauri.localhost` / WebView2+origin. The picker's same-origin / external-domain decision (allow localhost·tauri·+data·about, route everything else) is the same contract, but it sits on the WKWebView+navigation surface.++## ⚠️ The interception is wired differently from Windows — not documented here++The generic skill's **6 interception layers** (`on_new_window` / `on_navigation` /+`add_FrameNavigationStarting` / `add_NewWindowRequested` /+`AddScriptToExecuteOnDocumentCreated` / `add_ContextMenuRequested`) are the **WebView2+COM machinery** — that is the *Windows* lineage. On macOS the equivalent interception+runs against WKWebView and is wired differently. **Do NOT assume a macOS COM/CDP+equivalent of those specific APIs** — there is no `ICoreWebView2_11`, no+`AddScriptToExecuteOnDocumentCreated`, no `add_ContextMenuRequested` here. The+Tauri-level wrappers (`on_new_window` / `on_navigation`) are cross-platform; the raw+per-frame COM layers are not. When you need the low-level catch-point, say plainly that+the macOS mechanism is WKWebView-based and wired differently, rather than citing a+WebView2 API that doesn't exist on this host.++## Native browsers launch via LaunchServices (`open -a`)++When the picker routes to a **native desktop browser**, macOS opens it through+LaunchServices, not by resolving an `.exe` path:++- The default/shell-open path uses `open <url>` (`screenshot.rs`, `open_in_browser_profile`).+- A named browser uses `open -a "<App Name>" <url>` — `screenshot.rs` maps+  `chrome→"Google Chrome"`, `edge→"Microsoft Edge"`, `firefox→"Firefox"`,+  `brave→"Brave Browser"`; Safari and Arc are likewise app-name targets. This handles+  user-relocated installs uniformly (no hard-coded path).+- **Fresh window** (`fresh:true`) resolves a concrete browser binary and passes+  `--new-window` (Chromium) / `-new-window` (Firefox) before the URL, instead of plain+  `open` — `open` alone can't force a new window. See `lib.rs` around the+  `open_in_browser_profile` fresh-window branch. The auto-foreground behavior of a+  freshly-launched window holds on macOS just as on Windows.++## `shell:default` still applies++The "open in native browser" path still ultimately calls the Tauri shell plugin, so+both `shell:allow-open` AND `shell:default` must be in both capability files — that+requirement is platform-agnostic and is covered in the generic skill.++## NOT here (lives in the Windows companion)++The WebView2 / `ICoreWebView2` COM detail — the 6 named interception layers, the+`AddScriptToExecuteOnDocumentCreated` cross-origin-iframe script, the+`ICoreWebView2_11` context-menu wiring, the webview2-com 0.38 `.cast()` patterns, and+launching `chrome.exe`/`msedge.exe` by resolved path — is the Windows lineage; see+`hd-browser-picker-windows`. On macOS it's WKWebView + `open -a`, full stop.
skills/hydrogen-captions-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-captions-mac/SKILL.mdadded+57
@@ -0,0 +1,57 @@+---+name: hydrogen-captions-mac+description: >+  macOS platform companion to hydrogen-captions — the Apple-specific note on Hydrogen's on-screen+  captions. The two caption surfaces, the SECONDS-vs-MILLISECONDS unit trap, ids,+  positions, and the adom-cli / HTTP commands all live in hydrogen-captions+  (adom/hd-bootstrap) and are platform-neutral; THIS skill carries only the one macOS+  difference: the full-DESKTOP overlay (adom-desktop desktop_caption) is a Win32+  click-through window and is NOT implemented on macOS yet (the non-Windows build is a+  stub). The workspace caption (adom-cli hydrogen caption) works the same on macOS.+  Mirror of hd-captions-windows. Trigger words — caption macos, desktop_caption macos,+  desktop overlay mac, screen overlay mac, caption not showing mac, click-through caption mac.+---++# hydrogen-captions (macOS) — only the desktop overlay differs++This is the **macOS half** of `hydrogen-captions`, and it is deliberately short: **almost+nothing about captions is macOS-specific.** The two surfaces, the units (workspace =+SECONDS, desktop overlay = MILLISECONDS), positions/ids/sizes, the `adom-cli hydrogen+caption` command and the raw-HTTP `.../current/caption` fallback all live in the generic+**hydrogen-captions** skill and behave identically here — read that first. There is exactly+**one** macOS-distinct fact, below.++## (1) Workspace caption — fully platform-neutral on macOS++`adom-cli hydrogen caption show/hide` renders **inside the Hydrogen workspace webview** via the+workspace-events server → SSE → the frontend `captionStore`. That path has no host-native+code, so it works on macOS exactly as documented in hydrogen-captions (same SECONDS unit, 30s+default / 300s cap, newest-wins, the HTTP fallback). **This is your default on a Mac.**++## (2) ⚠️ Desktop overlay — NOT implemented on macOS++`adom-desktop desktop_caption` (the always-on-top, click-through, whole-screen overlay)+is **Windows-only**. The implementation in `src-tauri/crates/hd-app/src/caption.rs` is a+Win32 layered-window stack (`WS_EX_LAYERED | WS_EX_TOPMOST | WS_EX_TRANSPARENT` popups,+`EnumWindows`-keyed by id) gated entirely behind `#[cfg(windows)]`. The non-Windows+`show_caption_window` is a **stub** that logs *"Caption overlay is only supported on+Windows"* and returns `created=false`. So on macOS:++- `adom-desktop desktop_caption` will fail to actually paint a desktop overlay (the+  command returns, but `visible:false` / no window appears). **Do NOT rely on it for a+  full-screen recording on macOS** — there is no native overlay layer yet.+- For a whole-desktop / other-app (KiCad, Fusion, browser) recording on macOS, there is+  currently **no on-screen caption** equivalent. Caption the **Hydrogen workspace** instead+  (surface 1), or narrate via TTS — don't claim a desktop overlay you can't render.++This is an honest gap, not a different mechanism: Hydrogen has **no macOS-native desktop+caption overlay** at this time. (The `caption` capability that `adom-desktop status`+advertises reflects the Windows host overlay; treat it as Windows-only until a macOS+overlay ships.)++## NOT here (lives in the Windows companion)++The Win32 always-on-top click-through layered-window implementation — `caption.rs`'s+`#[cfg(windows)]` `show_caption_window` / `destroy_captions`, the `AdomCaptionOverlay`+window class, and the id-as-window-title routing that backs `adom-desktop+desktop_caption` — is the **Windows** delivery; see `hd-captions-windows`.
skills/hydrogen-container-stats/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-container-stats/SKILL.mdadded+154
@@ -0,0 +1,154 @@+---+name: hydrogen-container-stats+description: Hydrogen's workspace stats indicator in the top-right of the title bar — two thin progress bars (CPU + RAM) labelled "Container" plus an info tooltip on hover that shows the workspace name, CPU vs total cores, RAM usage vs limit, disk usage, image/machine, ID, and creation date. Polls once per second when running, every 5 seconds when stopped. Status badges show stopped / starting / restarting / restart-needed states. Use this skill when the user asks about CPU/RAM usage, the resource bars in Hydrogen's title bar, workspace disk usage, the "restart needed" badge, why bars are red/yellow, or what's in the tooltip. Trigger words — container stats, workspace stats, container cpu, container ram, container disk, resource bars, cpu bar, ram bar, container tooltip, container indicator, top bar stats, restart needed badge, container stopped badge, container starting, workspace_stats, machine stats, machine runtime stats.+---++# Hydrogen Workspace Stats Indicator++> This is the machine-runtime version (default). The legacy Docker equivalent+> (`HD_RUNTIME=docker`) is in the docker/ bucket.++The top-right of Hydrogen's title bar (next to the audio/video icons and your profile avatar) shows a tiny `Container` block with two horizontal progress bars — CPU% and RAM% — that animate in real-time. Hovering brings up a tooltip with the full workspace details. Under the machine runtime these numbers now reflect the **`Adom-Workspace` machine**, not a Docker container — but the UI is unchanged, so the header still reads "Container".++## How often it polls++There's an active polling loop in Hydrogen's title bar that asks the runtime for fresh stats on a timer:++- **Every 1 second** when the workspace is `running` — for live, smooth bar animation+- **Every 5 seconds** when it's `stopped`, `starting`, `restarting`, or `restart needed` — saves cycles when nothing's changing++Each poll calls Tauri `invoke('get_container_stats')`. This command no longer reaches into Docker directly — it dispatches through the runtime trait: `WorkspaceRuntime::get_stats` → `MacosVmRuntime::get_stats` → `workspace_stats()`, a machine health/stat probe of the `Adom-Workspace` machine. (Under `HD_RUNTIME=docker` the same command routes to the Docker impl instead.) The probe runs with short timeouts so a hung Lima VM or machine can't freeze the UI.++If you ever want to lighten the load (e.g. on a battery-constrained laptop), the relevant code is in `EditorNav.svelte`'s `pollStats()` closure — adjust the `pollInterval` ternary.++## What the bars show++| Bar | What it measures | Source |+|-----|------------------|--------|+| CPU | % of the machine's available CPU being consumed (1.0 = 1 full vCPU). Capped at 100% display | `workspace_stats()` machine probe |+| RAM | % of the machine's memory in use vs its limit | `workspace_stats()` machine probe |++Both bars share the same color-threshold rules:+- **Teal / green** — ≤ 50% usage (normal)+- **Yellow** (`warn` class) — 51-80% usage+- **Red** (`danger` class) — > 80% usage++## What the tooltip shows++When you hover over the bars, a tooltip appears with:++| Line | Example |+|------|---------|+| Container | `Adom-Workspace` (the machine name) |+| CPU + RAM totals | `CPU: 1.7% of 16 cores | RAM: 0.7 GB / 16.0 GB` |+| Disk | `Disk: 1.3 GB` (machine disk usage) |+| Image | `Image: Ubuntu 24.04 (Adom-Workspace)` (the imported machine image) |+| ID | `Adom-Workspace` (the registered machine identifier) |+| Created | `5/24/2026, 3:18:09 PM` (locale-formatted) |++If the workspace is in a non-healthy state, the tooltip shows a single red line instead:+- `Workspace is stopped — start from the menu` (when stopped)+- `Workspace runtime is broken — restart from the menu` (when the machine/code-server is unreachable, e.g. after sleep/hibernate)++## Status badge in the header++The `Container` text in the section header gets an inline status badge depending on state:++| Badge | Meaning | Color |+|-------|---------|-------|+| (no badge) | Running normally | normal |+| `stopped` | Machine registered but not running | red |+| `starting` | Hydrogen is starting the machine / code-server | yellow |+| `restarting` | Hydrogen is restarting the machine | yellow |+| `restart needed` | The machine or code-server is unreachable (usually a Lima/machine hiccup after Mac wake) | red |++When `restart needed` shows, open the Adom menu → Restart Workspace (see the `hydrogen-ui` skill for the Adom menu). A "Restart" here is `machinectl terminate Adom-Workspace` + re-ensure code-server — NOT `docker restart`.++## How it works under the hood++- **Polling frequency**: see "How often it polls" above — 1s when running, 5s when not.+- **Backend command**: Tauri `invoke('get_container_stats')` → `WorkspaceRuntime::get_stats`. In machine mode that is `MacosVmRuntime::get_stats` → `workspace_stats()`. NO `docker exec`, NO cgroup file reads, NO bollard `stats()` — those were the Docker-runtime mechanism and do not run on the machine path.+- **Two health levels** (`workspace_stats()` checks both): (1) is the Lima VM (`hd-builder`) up + the `Adom-Workspace` machine booted; (2) is `Adom-Workspace` registered + Running with code-server reachable on host port 7380. If either fails, the probe returns a needs-restart signal instead of stats and the UI shows the red "restart needed" badge.+- **Rate-limited error logging**: the "needs restart" log line is printed at most once every 30 seconds, even though the frontend polls every 1s, so the log stays readable.++### The benign `[docker] unhealthy` log line++You will sometimes see this in Hydrogen's log even on the machine path:++```+[docker] Container runtime unhealthy — exec failed, needs restart+```++**On the machine path this is BENIGN NOISE.** It's a leftover hd-docker heartbeat with no container bound to it — there is no Docker container in machine mode, so "exec failed" is expected and meaningless. It does NOT mean your workspace is broken.++The real machine health signal to watch for is:++```+[macos] Workspace unhealthy …+```++That line (not the `[docker]` one) reflects the actual `Adom-Workspace` machine state. And the authoritative "which runtime am I on" line is `[app] Active workspace runtime: MacosMachine`.++## API access++If you want the same stats data your AI can hit directly via the control API:++```bash+# From inside the machine — query the control API on the host+curl -s "$(cat ~/.adom/hd-control-url)/container-stats"+```++(Reach the control API at the `adom-host` gateway — vz does NOT mirror loopback, so the machine's `/etc/hosts` maps `adom-host` to the gateway where Hydrogen's host listener lives. The port is **dynamic per launch**; read the live URL from `~/.adom/hd-control-url` — already pointing at the gateway host — rather than hardcoding it.)++Returns JSON like:+```json+{+  "ok": true,+  "container_name": "Adom-Workspace",+  "container_id": "Adom-Workspace",+  "container_created": "2026-05-24T20:18:09Z",+  "image": "Ubuntu 24.04 (Adom-Workspace)",+  "cpu_percent": 1.7,+  "cpu_cores": 16,+  "mem_usage_mb": 716,+  "mem_limit_mb": 16384,+  "mem_percent": 4.4,+  "disk_usage_mb": 1300+}+```++Or if the workspace is in a broken state:+```json+{ "ok": false, "needs_restart": true, "error": "Workspace needs restart" }+```++## CSS selectors++| Element | Selector |+|---------|----------|+| The whole indicator block | `.resource-bars` |+| Header with title + status badge | `.resource-bars-header` |+| Title text ("Container") | `.resource-bars-title` |+| One bar row (CPU or RAM) | `.resource-bar-item` |+| Bar fill | `.resource-bar-fill` (modifier classes: `.warn`, `.danger`) |+| Bar percentage label | `.resource-bar-value` |+| Hover tooltip container | `.resource-bars-tooltip` |+| One tooltip line | `.resource-bars-tooltip-row` |++## Common questions++**"Why is CPU showing 200% / 1600%?"** It's not — the displayed % is capped at 100 for the bar fill, but the tooltip shows the real number. CPU of `100% of 16 cores` means 1 full vCPU pegged.++**"Why does RAM stay constant at ~4% even when I'm doing nothing?"** Code-server, the relay, the adom-vscode HTTP API, etc. all run in the machine all the time. ~700 MB baseline is normal.++**"The 'restart needed' badge appeared — what happened?"** Almost always a Lima/machine hiccup after Mac wake/sleep, leaving the machine or code-server unreachable. Click Restart Workspace in the Adom menu (top-left). Hydrogen will `machinectl terminate Adom-Workspace`, re-ensure code-server, and the badge clears.++**"The log says `[docker] … unhealthy` — is my workspace broken?"** No. On the machine path that line is a benign leftover heartbeat with no container bound. Look at `[macos] Workspace unhealthy …` for the real signal. See "The benign `[docker] unhealthy` log line" above.++**"Disk is 1.3 GB — does that include my project files?"** Yes — on the machine path there is no separate Docker volume; `/home/adom/project` lives inside the machine, so the `Disk:` figure is the machine's on-disk size including your project.++## Related skills++- [hydrogen-workspace-monitoring](../hydrogen-workspace-monitoring/SKILL.md) — the 15s state poll + floaty states behind these badges+- [hydrogen-workspace-lifecycle](../hydrogen-workspace-lifecycle/SKILL.md) — start/stop/restart/remove of the machine+- [hydrogen-networking](../hydrogen-networking/SKILL.md) — port 7380 code-server, host reachability, control port 47084
skills/hydrogen-container/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-container/SKILL.mdadded+131
@@ -0,0 +1,131 @@+---+name: hydrogen-container+description: Context for Claude Code running inside a Adom Hydrogen workspace machine. Documents the exact OS (Ubuntu 24.04, code-server 4.112.0, the Adom-Workspace machine), explains how setup differs from Adom cloud containers (setup-steps), what bridges are available, and how to use the relay. Trigger on startup, adom-cli errors, bridge commands, screenshot requests, container-platform questions, code-server / VS Code extension issues, or when the AI needs concrete facts about its environment instead of guessing.+---++# Adom Hydrogen Workspace (Machine)++Machine-runtime version (default). Legacy Docker container runtime (`HD_RUNTIME=docker`) is in the docker/ bucket.++You are running inside the **`Adom-Workspace` machine** managed by Adom Hydrogen (Hydrogen). Hydrogen is the flagship Adom desktop app — it manages your workspace machine, bridges to desktop apps, and provides VS Code + Claude Code.++## The workspace — exact facts (don't guess)++| Field | Value |+|-------|-------|+| Machine name | `Adom-Workspace` (fixed — there is no random container suffix) |+| Imported from | golden image `adom-golden.tar.gz` (full pre-baked image) via `machinectl import-tar` |+| Base | Ubuntu 24.04 |+| Architecture | `aarch64` (arm64) — the macOS golden image is a **Rosetta-hybrid**: native arm64 code-server / node / claude, with the x86-64 Adom CLIs run **translated under Rosetta** inside the Lima VM. So `uname -m` returns `aarch64` and `node`'s `process.arch` is `arm64`, but `adom-*` x86-64 binaries still run (via Rosetta) |+| code-server | 4.112.0 (pinned via setup step) |+| code-server binary | `/usr/lib/code-server/bin/code-server` |+| Extensions dir | `~/.local/share/code-server/extensions/` |+| Settings | `~/.local/share/code-server/User/settings.json` |+| Machine default user | `adom` (UID 1001; under systemd-nspawn there's no wsl.conf — the default user comes from the machine config / nsenter `-S 1001 -G 1001`) — in-machine exec via `limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'`; root variant drops `-S 1001 -G 1001` |+| Work user | `adom` (UID 1001, passwordless sudo, home `/home/adom`) |+| Workspace | `/home/adom/project` (lives directly in the machine's ext4 filesystem) |+| Pre-installed | Node 18 + npm, Python 3.12, git, gh, curl, wget, jq, build-essential, cmake, pkg-config, libssl-dev |+| Code-server runs as | `code-server --bind-addr 0.0.0.0:7380 --auth none --disable-telemetry /home/adom/project` (binds 0.0.0.0:7380; Lima exposes it to the Mac at `localhost:7380`) |+| Runtime source | `adom-hydrogen/src-tauri/crates/hd-app/src/runtime/macos.rs` (selector `runtime/mod.rs::select_runtime()`) |++If you ever need to confirm: `cat /etc/os-release`, `uname -m` (expect `aarch64` on Apple Silicon), `/usr/lib/code-server/bin/code-server --version`. Don't speculate about alpine — it's Ubuntu 24.04, same distro as the cloud container (the cloud is amd64; this macOS port is arm64 + Rosetta for the x86-64 Adom CLIs).++## Setup is via Hydrogen's setup-steps++Hydrogen provisions this machine through its own **setup-steps** flow (Rust code in `adom-hydrogen/src-tauri/crates/hd-app/src/setup_steps_macos.rs`, with the runtime mechanics in `adom-hydrogen/src-tauri/crates/hd-app/src/runtime/macos.rs`), shown as the "Install Tools" panel in the Hydrogen UI. It is a **different code path** than cloud containers, which provision via the adompkg/wiki-package bootstrap (`adom/hd-bootstrap`).++The machine cascade is **18 steps** (state file `setup-steps-macos.json`) vs the legacy Docker flow's 24. Most tooling is NOT installed by a step — it's BAKED into `adom-golden.tar.gz` at image-build time (the 8 Adom CLIs, the claude CLI + Code extension, code-server, VS Code settings, and all `hd-*` skills). The Docker `pull-image` / `create-container` / `start-container` steps are collapsed into a single step 1, `ensure-workspace`, which runs `machinectl import-tar` of `adom-golden.tar.gz` if the machine isn't already present. The relay is started by the `start-relay` step. See `hydrogen-golden-image`.++When debugging install issues here, the source of truth is `setup_steps_macos.rs` (and `runtime/macos.rs`) — its step IDs (`ensure-workspace`, `install-adom-vscode`, `set-env-vars`, `inject-api-key`, `start-relay`, `claude-auth`, etc.) map 1:1 to what ran on this machine. There is NO `install-claude-ext` / `install-claude-cli` / `write-vscode-settings` step anymore — those are baked. (For the legacy Docker `setup_steps.rs` flow see the docker/ bucket.)++## VS Code extension caveats++- The Claude Code extension and `code-server` settings (including `extensions.autoUpdate: true` / `extensions.autoCheckUpdates: true`) are BAKED into the golden image — there is no `write-vscode-settings` or `install-claude-ext` setup step. Auto-update **must** stay on so every installed extension auto-updates from the marketplace; the image bakes it on. Do not regress this.+- Bare `code-server --install-extension <id>` (no version pin) historically resolves to the **universal** target-platform build. For some extensions (notably `anthropic.claude-code` after 2.1.89), Anthropic stopped shipping universal builds — so bare install permanently sticks at 2.1.89 even when newer `linux-x64` builds exist. The golden-image bake installs the latest `linux-x64` build pinned, so the workspace ships current. It also heals orphan `extensions.json` entries (entries pointing to deleted dirs cause a permanent "Invalid extensions detected" banner).+- Marketplace: code-server uses **Open VSX** (open-vsx.org), not Microsoft Marketplace. No `EXTENSIONS_GALLERY` env override is set; `product.json` has no gallery URL — it falls through to Open VSX defaults.++## adom-cli is authenticated++Hydrogen auto-injects the user's session token on every launch. To verify:+```bash+adom-cli carbon user get+```+If this returns user data, all adom-cli commands work (containers, repos, orgs, wiki, etc.). If it fails, the user needs to log in via Hydrogen's login page.++## Relay server is running++The adom-desktop relay runs on ports 8765 (WebSocket) / 8766 (HTTP) inside this machine. The `start-relay` setup step launches it.++Check health: `curl -sf http://127.0.0.1:8766/health`+Check desktop connection: `adom-desktop ping`++The macOS host control API is reachable from inside the machine via the `adom-host` gateway alias — vz does NOT mirror loopback, so 127.0.0.1 inside the machine is NOT the Mac. The port is dynamic per launch, so read the live URL from `~/.adom/hd-control-url`: `BASE="$(cat ~/.adom/hd-control-url)"; curl "$BASE/health"`.++## Available bridge commands (when desktop is connected)++### Screenshots (zero dialogs, instant)+```bash+adom-desktop desktop_screenshot_screen                    # full screen capture+adom-desktop desktop_screenshot_screen '{"maxWidth":1500}' # resized for Claude vision+adom-desktop desktop_screenshot_window '{"hwnd": N}'       # capture specific window+adom-desktop desktop_list_windows                          # list all windows with HWNDs+```+Default to `maxWidth: 1500, format: "png"` for UI screenshots. Use `"jpeg"` for natural photos, `"webp"` for smaller files.++### Browser automation (Puppeteer)+```bash+adom-desktop browser_open_window '{"url": "https://..."}'+adom-desktop browser_screenshot '{"sessionId": "default"}'+adom-desktop browser_eval '{"js": "document.title"}'+adom-desktop browser_navigate '{"url": "..."}'+```++### KiCad bridge+```bash+adom-desktop kicad_open_board '{"path": "/Users/<you>/path/to/board.kicad_pcb"}'+adom-desktop kicad_screenshot_all+adom-desktop kicad_run_drc '{"path": "..."}'+adom-desktop kicad_window_info+```++### Fusion 360 bridge+```bash+adom-desktop fusion_start+adom-desktop fusion_import_step '{"path": "..."}'+adom-desktop fusion_export_step '{"path": "..."}'+```++### File transfer+```bash+adom-desktop send_files '{"files": [{"path": "/home/adom/file.txt"}]}'+adom-desktop pull_file '{"remotePath": "/Users/<you>/file.txt", "localPath": "/tmp/file.txt"}'+```++### Desktop interaction+```bash+adom-desktop notify_user '{"message": "Build complete", "duration_ms": 3000}'+adom-desktop desktop_open_url '{"url": "https://..."}'+adom-desktop desktop_open_folder '{"path": "/Users/<you>/project"}'+```++## What's different from Adom cloud containers++| Feature | Adom Cloud | Hydrogen Local (machine) |+|---------|-----------|-----------------|+| Workspace provisioning | Adom platform API | `machinectl import-tar` of `adom-golden.tar.gz` inside the Lima VM on the user's Mac |+| Relay connection | wss:// through Cloudflare | ws://localhost:8765 (direct, no TLS) |+| Bridge latency | ~50-100ms (internet round-trip) | <1ms (localhost) |+| VSCODE_PROXY_URI | Coder proxy URL | http://localhost:7380/proxy/{{port}}/ |+| Socket.IO (collab) | Connected to iron.adom.inc | Disabled (single-user) |+| Service containers | Adjacent containers | Remote via Adom cloud |++## Environment variables++- `ADOM_DESKTOP_MODE=local` — indicates Hydrogen local mode+- `HD_RUNTIME=macos` — indicates the machine runtime (default); `docker` selects the legacy container runtime+- `VSCODE_PROXY_URI=http://localhost:7380/proxy/{{port}}/` — code-server's proxy++## Related skills++- `hydrogen-topology` — the three-tier topology; tier 3 is this workspace machine+- `hydrogen-volume` — where your files live, what persists, and how to back up / copy out the machine
skills/hydrogen-eda-install/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-eda-install/SKILL.mdadded+70
@@ -0,0 +1,70 @@+---+name: hydrogen-eda-install+description: >+  What to do when KiCad or Fusion 360 is NOT installed on this machine and the user's+  request needs it. NEVER just report "not installed" and skip — offer to install and+  do it. KiCad installs unattended via one command; Fusion 360 needs the user to run+  Autodesk's installer but you can fetch and open it for them. READ THIS whenever a+  kicad/fusion bridge command fails with not-installed, a tour/demo step involves KiCad+  or Fusion, or the user asks for schematics/PCBs/CAD and the tool is missing.+  Trigger words — kicad not installed, fusion not installed, fusion 360 missing,+  install kicad, install fusion, no EDA tools, check my EDA tools, eda not found,+  kicad_not_installed, fusion_not_installed, bridge app missing.+---++# EDA tool missing? Install it — don't skip++The #1 failure mode on a fresh machine: a demo/tour thread checks for KiCad or+Fusion 360, sees neither, says "not installed" and gives up. That's wrong. The+whole point of Adom is that the AI has hands: **offer the install, then run it.**++## The decision tree++1. The user asked for something that needs KiCad or Fusion (open a board, show a+   symbol library, export gerbers, a tour step, …) and the tool is missing.+2. Say exactly what you'll do: "KiCad isn't installed — want me to install it now?+   It takes a few minutes." For a tour/demo thread where the user already asked+   for the tool, you may proceed directly — the request WAS the consent.+3. Run the install (below), watch it finish, then continue the original request.++## KiCad — unattended install (you can do this fully yourself)++```bash+adom-desktop desktop_install_kicad '{}'+```++- Downloads the latest official KiCad release and installs it (macOS: DMG →+  /Applications/KiCad; Windows: winget). Takes ~2-5 minutes on a normal+  connection — poll with the bridge status or just retry the kicad bridge+  command when it returns.+- After install, kicad bridge commands work immediately (the bridge launches+  KiCad on demand). Retry whatever the user originally asked for.+- If the verb errors, fall back to telling the user the direct link+  (https://www.kicad.org/download/) — but try the verb FIRST.++## Fusion 360 — assisted install (Autodesk requires their installer + a login)++Fusion cannot be silently installed (Autodesk's installer needs the user's+Autodesk account). But don't just paste a link either:++1. Tell the user: "Fusion 360 needs Autodesk's own installer and your Autodesk+   sign-in — I'll download it and open it for you."+2. Open the official download for them:+   `adom-desktop desktop_open_url '{"url":"https://www.autodesk.com/products/fusion-360/appstream"}'`+   (or drive the download in a browser surface you control and open the+   downloaded installer).+3. While they install, keep the thread alive: tell them what you'll show once+   it's up (the fusion bridge auto-detects the install; `fusion_start` launches+   it). When they say it's done, retry the original request.++## Altium / OrCAD / other EDA++No bridge exists yet. Say so honestly, and pitch co-building a bridge (see+hydrogen-eda-discovery — the KiCad/Fusion bridges are the reference implementations).++## Related skills++- `hydrogen-bridges` — full bridge ecosystem reference (status, CLI, Bridge Manager)+- `hydrogen-eda-discovery` — the onboarding playbook for discovering the user's EDA tools+- `kicad-tour`, `fusion-onboarding` — the bridges' own deep guidance (installed+  alongside the bridges)
skills/hydrogen-networking/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-networking/SKILL.mdadded+182
@@ -0,0 +1,182 @@+---+name: hydrogen-networking+description: >+  Port architecture, hostnames, and networking rules for Adom Hydrogen.+  MUST READ before adding ports, exposing a service to your macOS host,+  referencing host URLs from inside the workspace, or wiring any service+  communication.+  Trigger words — Hydrogen port, Hydrogen network, port mapping, proxy, 127.0.0.1, loopback,+  adom-host gateway, hd-control-url,+  VSCODE_PROXY_URI, relay URL, code-server proxy, container networking,+  ADOM_CARBON_URL, ADOM_HYDROGEN_URL, direct connect, 8770, 7380.+---++# Adom Hydrogen — Networking & Port Architecture++> This is the machine-runtime version (default). For the legacy Docker container+> runtime (`HD_RUNTIME=docker`) see the docker/ bucket.++## Core Principle: expose `0.0.0.0` listeners to the Mac++The Hydrogen workspace is the `Adom-Workspace` machine (a systemd-nspawn machine booted+inside the `hd-builder` Lima VM), not a Docker container. macOS does **not** do+WSL2-style magic loopback mirroring — vz does NOT mirror loopback. Instead, Lima+exposes the machine's listeners to the Mac: **any service that binds+`0.0.0.0:<port>` inside the workspace machine is reachable at+`127.0.0.1:<sameport>` on the Mac** — no Docker `-p` mapping, no proxy hop+required. The port number is the same on both sides.++The two corollaries that drive everything below:+- **Bind `0.0.0.0`, not `127.0.0.1`.** A `127.0.0.1`-only listener inside the+  machine is NOT exposed to the Mac — the Mac can't see it. A workspace service+  that binds loopback-only is invisible to the host until you route it explicitly+  (see `hydrogen-port-watcher`).+- **There are NO Docker port mappings.** code-server binds `0.0.0.0:7380`+  inside the machine and is therefore reachable at `localhost:7380` on the Mac+  automatically. Same for a dev server on `0.0.0.0:5173` → `localhost:5173`.++## In-machine services (no port mapping needed)++| Machine Port | Service | Reachable on the Mac at |+|--------------|---------|-------------------------|+| 7380 | code-server (VS Code) | `localhost:7380` (exposed by Lima) |+| 8765 | relay WS | bind `0.0.0.0` → `localhost:8765`, or `/proxy/8765/` |+| 8766 | relay HTTP | bind `0.0.0.0` → `localhost:8766`, or `/proxy/8766/` |+| 5173 | your dev server | bind `0.0.0.0` → `localhost:5173` |++All of these are also reachable through code-server's `/proxy/<port>/` URL+(see below), which remains the preferred path for browser-facing HTTP/WS.++## Host-Side Ports (Tauri app, native macOS listeners)++These ports run on the macOS host inside the Hydrogen Tauri process. They are+native TCP listeners on the Mac — not inside the workspace machine.++| Port | Service | Notes |+|------|---------|-------|+| 1420 | Dev file server | Dev mode only, serves `build/` |+| 8770 | adom-desktop direct connect | Owned by Adom Desktop, NOT Hydrogen |+| 47080 | Hydrogen discovery server | |+| 47083 | Hydrogen reverse proxy | Proxies to code-server |+| 47084 | Hydrogen control API | Health, setup, eval, CDP, `/workspace/status`, `/port-forward` |+| 47085 | WebView2 CDP | Chrome DevTools Protocol |++These are configurable via `~/Library/Application Support/adom-hydrogen/ports.json`.++**Reaching a host service FROM the machine:** use the `adom-host` gateway, NOT+`127.0.0.1`. macOS/vz does NOT mirror loopback, so a host listener on+`127.0.0.1:<port>` is not visible at the machine's own loopback. Instead the host+binds `0.0.0.0`, and the machine reaches it via the `adom-host` gateway hostname:+`adom-host:<port>`. The machine's `/etc/hosts` maps `adom-host` to the gateway IP —+no extra firewall rule needed. (`host.docker.internal` is a Docker-ism Hydrogen+does NOT use — inside the machine it resolves to a *different* interface, not where+Hydrogen listens, so curls to it fail; use `adom-host` instead.)++## Hostnames — reach the host at `adom-host`++### From inside the machine → host machine+```+adom-host+```+macOS/vz does NOT mirror loopback, so use the `adom-host` gateway alias to reach+host listeners from inside the workspace machine. The machine's `/etc/hosts` maps+`adom-host` to the gateway IP, so `adom-host:<port>` reaches the Mac's host+listeners directly (the host binds `0.0.0.0`). Used for:+- `ADOM_CARBON_URL=http://adom-host:{proxy_port}`+- `ADOM_HYDROGEN_URL=http://adom-host:{proxy_port}`+- Direct connect probe: `http://adom-host:8770/health`+- Hydrogen control API: the live URL in `~/.adom/hd-control-url` (`http://adom-host:<dynamic>`)++The control port is **dynamic per launch** — read it from `~/.adom/hd-control-url`+(your non-interactive Bash shells don't source `.bashrc`/`profile.d`, so the env var+alone is unreliable): `BASE="$(cat ~/.adom/hd-control-url)"`.++### From the host → machine services (via proxy)+```+http://127.0.0.1:{code_server_port}/proxy/{machine_port}/+ws://127.0.0.1:{code_server_port}/proxy/{machine_port}/+```+Example: relay health → `http://127.0.0.1:7380/proxy/8766/health`+Example: relay WS → `ws://127.0.0.1:7380/proxy/8765/`++This path always works for `0.0.0.0` AND `127.0.0.1`-bound machine services,+because code-server itself is inside the machine and proxies to them locally.+It is the preferred path for browser-facing HTTP/WS even though direct+exposure also works for `0.0.0.0` listeners.++### From the host → host-side services+```+http://127.0.0.1:{port}/+```+Control API → `http://127.0.0.1:47084/health`+Direct connect → `http://127.0.0.1:8770/health`++## Relay Architecture++The relay (`adom-desktop serve`) runs inside the machine on ports 8765+(WebSocket) and 8766 (HTTP). It binds `0.0.0.0`, so it is reachable both via+Lima's port exposure (`localhost:8765` / `localhost:8766` on the Mac) and via the+code-server proxy. The proxy path is preferred for connection stability:++1. **adom-desktop on the Mac** connects as a WS client via the+   code-server proxy: `ws://127.0.0.1:7380/proxy/8765/`+2. **Registration** happens via `server_add` on the direct connect+   API (port 8770):+   ```json+   {+     "name": "Hydrogen Local Machine",+     "url": "ws://127.0.0.1:7380/proxy/8765/",+     "autoConnect": true+   }+   ```+3. **Health checks** use the proxy path:+   `http://127.0.0.1:7380/proxy/8766/health`++This matches cloud containers where adom-desktop connects via+`wss://<slug>.adom.cloud/proxy/8765/`.++## VSCODE_PROXY_URI++Inside the machine, `VSCODE_PROXY_URI` is set by code-server:+```+https://<slug>.adom.cloud/proxy/{{port}}/     (cloud)+http://localhost:7380/proxy/{{port}}/          (Hydrogen local, approximate)+```++Tools that build proxy URLs should use `VSCODE_PROXY_URI` when+available, falling back to `http://127.0.0.1:{code_server}/proxy/{port}/`.++## In-machine command form++The workspace is the `Adom-Workspace` machine inside the `hd-builder` Lima VM, so+you reach it by entering its namespaces via `nsenter`, NEVER `docker exec`+(`<leader>` is the machine's leader PID):+```bash+limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'+# or the simpler form for a single command:+limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- <cmd>+```++## NEVER Do These++1. **NEVER bind `127.0.0.1`-only** for a service that must be reachable from+   the Mac. macOS won't expose it (vz does NOT mirror loopback). Bind `0.0.0.0`,+   or register it with the port-forward registry (see `hydrogen-port-watcher`).+2. **NEVER use `host.docker.internal` for machine→host** communication — it's a+   Docker-ism Hydrogen doesn't use; inside the machine it resolves to the wrong interface.+   Reach the host via the `adom-host` gateway (vz does NOT mirror loopback, so+   `127.0.0.1` does NOT reach the host); read Hydrogen's live control URL from+   `~/.adom/hd-control-url`.+3. **NEVER assume the host's `127.0.0.1` is reachable from inside the machine.**+   macOS/vz does NOT mirror loopback — use the `adom-host` gateway alias+   (`adom-host:<port>`); the host binds `0.0.0.0` and the machine's `/etc/hosts`+   maps `adom-host` to the gateway. No firewall allow needed.+4. **NEVER `docker exec` / `docker cp` against the workspace.** It's the+   `Adom-Workspace` machine; enter it via+   `limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 --`.+5. **NEVER hardcode port numbers** in machine code. Read from env vars+   (`ADOM_CARBON_URL`, `ADOM_HYDROGEN_URL`) or discover via the direct+   connect API.+6. **NEVER assume adom-desktop direct connect (8770) is owned by Hydrogen.**+   It's owned by Adom Desktop. Hydrogen's direct-api thread binds to 8770+   as a fallback but Adom Desktop takes priority when both are running.
skills/hydrogen-notifications-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-notifications-mac/SKILL.mdadded+48
@@ -0,0 +1,48 @@+---+name: hydrogen-notifications-mac+description: >+  macOS platform companion to hydrogen-notifications — the Apple-specific half of how+  Adom Hydrogen reaches the user outside the window. The generic notify handler,+  payload shape, levels, and /ui/toast live in hydrogen-notifications (adom/hd-bootstrap);+  THIS skill carries only the macOS delivery mechanics: Notification Center toasts,+  the emergency Dock-icon attention bounce, and the System Settings → Notifications+  grant. Mirror of hd-notifications-windows (WinRT/AUMID). Trigger words — macos+  notification, notification center, dock bounce, request attention macos, allow+  notifications macos, toast on mac.+---++# hydrogen-notifications (macOS) — Notification Center + Dock attention++This is the **macOS half** of `hydrogen-notifications`. The cross-platform contract — the+`notify` message, the `NotifyPayload` (`title`/`body`/`level`/`actions`), the levels+`info|warning|error|emergency`, and the in-app `POST /ui/toast` — is identical on+every host and lives in the generic **hydrogen-notifications** skill. Read that first; this+skill only documents what's Apple-specific.++## Delivery++- **Toasts → macOS Notification Center.** A `notify` surfaces as a system+  notification (short for `info`, long-duration for `warning`/`error`/`emergency`),+  rendered by `notifications.rs` → `show_toast`.+- **`emergency` → persistent Dock-icon bounce.** On top of the toast, Hydrogen calls+  `request_user_attention(Critical)` on the main window and shows/unminimizes it. On+  macOS this **bounces Hydrogen's Dock icon persistently** and keeps demanding attention+  until the user clicks through themselves — a programmatic focus does NOT clear it.+- **Identity.** Toasts surface under the **"Adom"** title (Hydrogen prefixes `title` with+  `"Adom: "` unless it already starts with "Adom"), and the notification is attributed+  to Hydrogen's **app bundle** — not via a Start-Menu-shortcut AUMID (that's the Windows+  path; see hd-notifications-windows).++## macOS gotcha — the Notifications grant++macOS gates notifications per-app: the user must allow **Adom Hydrogen** under+**System Settings → Notifications**. If toasts never appear (but the in-app+`/ui/toast` works), that grant is the first thing to check — there's no Windows-style+"always works" toast. Unlike Screen Recording / Accessibility, Hydrogen does not block setup+on it; it's best-effort.++## NOT here (lives in the Windows companion)++WinRT toast templates, the Start-Menu-shortcut **AUMID** wiring, and taskbar flash are+the Windows delivery — see `hd-notifications-windows`. Do not apply that machinery on+macOS.
skills/hydrogen-open-url-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-open-url-mac/SKILL.mdadded+67
@@ -0,0 +1,67 @@+---+name: hydrogen-open-url-mac+description: >+  macOS platform companion to hydrogen-open-url — the Apple-specific half of "open a URL+  in a native browser". The six ways to open a URL (Hydrogen webview tab, Hydrogen window,+  Pup, native browser, the Browser Picker, headless curl), the decide-for-the-user+  rule, the /open-url + /open-in-profile + prefs API all live in hydrogen-open-url+  (adom/hd-bootstrap); THIS skill carries only the macOS native-browser mechanics:+  launching via LaunchServices (`open` / `open -a "<App Name>"` / `--new-window`+  fresh windows), macOS browser-profile dirs, and the control-URL discovery file+  (~/.adom/hd-control-url). Mirror of hd-open-url-windows (chrome.exe/msedge.exe by+  path). Trigger words — open url macos, open -a, native browser mac, Safari Chrome+  Arc Edge, fresh window mac, browser profiles mac, hd-control-url.+---++# hydrogen-open-url (macOS) — opening URLs in native macOS browsers++This is the **macOS half** of `hydrogen-open-url`. The decision framework — the six ways to+open a URL, "decide for the user — DON'T hand them the picker", the+`/open-url` · `/open-in-profile` · `/browser-picker/prefs` API, the fresh-window+rationale for auth flows — is host-agnostic and lives in the generic **hydrogen-open-url**+skill. Read that first; this skill only covers how the **native-browser path (#4)**+launches on a Mac.++## Control-URL discovery — `~/.adom/hd-control-url`++Same as the generic skill: read the live base URL from `~/.adom/hd-control-url`+(`BASE="$(cat ~/.adom/hd-control-url)"`) — never hard-code the port. On macOS that file+resolves to the **`adom-host` gateway** from inside the workspace machine, because the+Apple `vz` VM does NOT mirror host loopback (`127.0.0.1` inside the machine is NOT the+Mac, and Hydrogen's host services bind `0.0.0.0`). See `hydrogen-api-mac` for the gateway detail.++## Native browsers launch via LaunchServices (`open` / `open -a`)++The `/open-in-profile` and direct native-browser paths drive macOS browsers through+LaunchServices, not by resolving an `.exe`:++- **Default / shell-open:** `open <url>` (LaunchServices picks the user's default+  browser) — `open_in_browser_profile` / `screenshot.rs`.+- **Named browser:** `open -a "<App Name>" <url>`. `screenshot.rs` maps+  `chrome→"Google Chrome"`, `edge→"Microsoft Edge"`, `firefox→"Firefox"`,+  `brave→"Brave Browser"`; **Safari** and **Arc** are likewise app-name targets. Using+  the app *name* (not a path) handles user-relocated installs uniformly.+- **Fresh window (`fresh:true`):** to force a brand-new window/process Hydrogen resolves a+  concrete browser binary and passes `--new-window` (Chromium) / `-new-window` (Firefox)+  before the URL — `open -a … <url>` alone opens a tab, not a window (`lib.rs`+  fresh-window branch). A freshly-launched window auto-foregrounds, so this is the path+  for **Claude/Adom auth pages the user must complete** (the generic skill defaults the+  fresh toggle ON for those).++## macOS browser-profile dirs++`GET /browser-profiles` enumerates the user's installed browsers + profiles so+`/open-in-profile {browser, profileDir}` can target the **work** profile (where saved+logins live). On macOS those profiles live under the app's Application Support dir+(e.g. `~/Library/Application Support/Google/Chrome/<Profile>`,+`…/Microsoft Edge/<Profile>`), as opposed to the Windows `%LOCALAPPDATA%\…\User Data`+layout. Discover them via the endpoint rather than assuming a path or browser — don't+hard-code `chrome`/`edge`.++## NOT here (lives in the Windows companion)++Launching `chrome.exe` / `msedge.exe` by resolved path (`find_browser_exe`), the+`%LOCALAPPDATA%\…\User Data\<Profile>` profile-dir layout, and the Windows+`--new-window` / `--profile-directory` exe invocation are the Windows native-browser+path — see `hd-open-url-windows`. On macOS it's LaunchServices (`open` / `open -a`) with+app-name targets.
skills/hydrogen-permissions-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-permissions-mac/SKILL.mdadded+77
@@ -0,0 +1,77 @@+---+name: hydrogen-permissions-mac+description: >+  macOS platform companion to hydrogen-permissions — the Apple-specific half of the+  permission story. The generic skill (adom/hd-bootstrap) covers Hydrogen's webview+  auto-grant: inside Hydrogen a webview app NEVER sees a "site wants to use your+  microphone/camera/clipboard/location" prompt. THIS skill carries only what's+  macOS-specific: the OS-level TCC privacy model (Screen Recording, Accessibility,+  Automation, Camera, Microphone) granted under System Settings → Privacy &+  Security, how Hydrogen preflights/requests them (CGPreflightScreenCaptureAccess,+  AXIsProcessTrustedWithOptions), and that some need a relaunch / a stable signing+  identity to stick. Mirror of hd-permissions-windows (UAC, no per-feature gate).+  Trigger words — macos permissions, TCC, screen recording permission, accessibility+  permission, automation permission, privacy and security, AXIsProcessTrusted,+  CGPreflightScreenCaptureAccess, grant accessibility, relaunch to take effect.+---++# hydrogen-permissions (macOS) — the TCC privacy gates++This is the **macOS half** of `hydrogen-permissions`. The cross-platform contract — Hydrogen's+**webview auto-grant** (mic/camera/clipboard/geolocation/notifications/sensors all+ALLOW, so no in-page "site wants to use…" prompt) — is identical on every host and+lives in the generic **hydrogen-permissions** skill. Read that first; this skill only covers+the macOS **OS-level** gates that sit *separately* underneath that auto-grant.++## Two different layers — don't conflate them++- **In-webview permissions** (mic/camera/clipboard/etc.): on macOS Hydrogen's webview is+  **WKWebView**, which grants in-page media capture with no per-site prompt — same+  user-facing result as the Windows WebView2 `PermissionRequested` auto-grant. This is+  the generic skill's territory.+- **OS-level TCC permissions:** macOS still gates Hydrogen's *process* with the system-wide+  TCC (Transparency, Consent & Control) model. These are real system prompts and live+  under **System Settings → Privacy & Security**. The webview auto-grant does NOT bypass+  them — if Hydrogen's process is denied Camera/Mic/Screen Recording at the OS, the in-page+  capture still fails (look here, not at a "denied prompt", per the generic skill).++## The TCC gates Hydrogen touches (and how it preflights them)++Hydrogen requests/preflights these natively (`mac_input.rs`, exposed as Tauri commands in+`lib.rs`):++| TCC permission | What Hydrogen uses it for | How Hydrogen checks/requests |+|---|---|---|+| **Accessibility** | synthesize keystrokes / `AXPress` into another app (press Return on Safari's claude.ai Authorize page) to auto-approve OAuth | `AXIsProcessTrustedWithOptions` via `accessibility_trusted(prompt)` → `macos_accessibility_status` |+| **Screen Recording** | screenshot the consent window for the OCR-approve flow | `CGPreflightScreenCaptureAccess` (non-prompting) / `CGRequestScreenCaptureAccess` via `screen_capture_trusted(prompt)` → `macos_screen_recording_status` |+| **Automation** ("control System Events") | the AppleScript/System-Events button-press that approves OAuth | `prime_automation` / `prime_browser_automation` → `macos_prime_automation`, `macos_prime_browser_automation` |+| **Camera / Microphone** | the first time Hydrogen's process actually uses the device | one-time system TCC prompt; toggles must be ON for **Adom Hydrogen** under Privacy & Security |++Preflight (`CGPreflight…`, `AXIsProcessTrusted…` with `prompt=false`) is+**non-prompting** — it reports current state. Passing `prompt=true` shows the system+dialog the first time and adds Hydrogen to the relevant Privacy & Security list. Hydrogen frontloads+Accessibility (and the Automation consent) in the **pre-login gate** rather than+mid-cascade (`setup_steps_macos.rs`), and can deep-link the user to the exact pane via+`open x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility`+(and the Screen Recording / Automation equivalents).++## Gotchas++- **Some grants need a relaunch.** Screen Recording in particular: after the user toggles+  it on, macOS may not honor it for the already-running process — Hydrogen's gate has a+  "stale" path that **relaunches Adom Hydrogen** (`lib.rs` relaunch command) so the+  new grant takes effect.+- **The grant is bound to the app's code signature.** An adhoc / un-notarized rebuild is+  seen as a "new app" and the Accessibility grant is dropped (`mac_input.rs` signing+  note). A stable Developer ID + notarization is needed for the grant to persist across+  builds — relevant when a dev build keeps re-prompting.+- **Graceful degradation:** if Accessibility / Screen Recording are off, Hydrogen still opens+  the sign-in page and starts the flow — the user just approves the browser page+  themselves (no auto-press). It does not hard-block on these (`setup_steps_macos.rs`).++## NOT here (lives in the Windows companion)++Windows has no per-feature OS gate equivalent to TCC — the analog is **UAC** (elevation+for the installer / WSL2 setup), and the WebView2 `PermissionRequested` auto-grant+handler. Screen Recording / Accessibility / Automation consent has no Windows+counterpart. See `hd-permissions-windows`.
skills/hydrogen-port-watcher/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-port-watcher/SKILL.mdadded+281
@@ -0,0 +1,281 @@+---+name: hydrogen-port-watcher+description: >+  Dynamic port forwarding for Adom Hydrogen. A daemon inside the+  Adom-Workspace machine watches for new TCP listeners (OAuth callbacks from VS+  Code extensions like Codex, Copilot, GitLens) and notifies Hydrogen to create+  host-side TCP proxies. Covers the cases macOS/Lima port exposure can't:+  `127.0.0.1`-only listeners and exposure gaps. Makes localhost:{port}+  on the Mac reach the machine. Extensions just work — no manual setup.+  Trigger words: port forward, dynamic port, OAuth callback, Codex auth,+  extension auth, port mapping, localhost port, port watcher, port proxy.+---++# Hydrogen Port Watcher — Dynamic Port Forwarding++> This is the machine-runtime version (default). For the legacy Docker container+> runtime (`HD_RUNTIME=docker`) see the docker/ bucket.++## Why the daemon is still here on the machine++When a service inside the Adom-Workspace machine binds `0.0.0.0:<port>`, Lima's+vz networking makes it reachable from the Mac at `localhost:<sameport>` — so you+might think a port watcher is unnecessary. It isn't. The daemon is **RETAINED**+because that `0.0.0.0` exposure has two gaps the watcher closes:++1. **`127.0.0.1`-only listeners.** vz does NOT mirror loopback, so a+   loopback-only listener inside the machine is never exposed on the Mac. VS Code+   extensions (Codex, Copilot, GitLens) start OAuth callback servers that+   frequently bind `127.0.0.1` — the browser redirects to+   `localhost:{port}/auth/callback?code=...` but the Mac never had that port,+   so it doesn't exist.+2. **Exposure lag / wedge.** macOS/Lima port exposure can lag or break+   even for `0.0.0.0` listeners (a real HD-logged failure mode:+   "code-server alive in the machine but the Mac can't reach 127.0.0.1:7380"). The+   watcher gives Hydrogen a reliable host-side proxy that doesn't depend on Lima's+   automatic exposure.++## The Solution++A lightweight daemon inside the machine monitors `/proc/net/tcp` for new+listening ports. When one appears (that wasn't there at boot and isn't a known+service), it notifies Hydrogen's control API. Hydrogen creates a host-side TCP proxy on+that same port, forwarding through code-server's `/proxy/{port}` to reach the+machine. The browser redirect just works — even for loopback-only callback+servers Lima wouldn't have exposed.++## Architecture++```+Adom-Workspace machine:             macOS Host:+┌─────────────────────┐            ┌──────────────────────┐+│ Codex creates server │           │ Hydrogen Control API       │+│ on 127.0.0.1:1455    │           │ (:47084)             │+│ (loopback, no expose)│           │                      │+│ port-watcher.sh      │──POST──→ │ /port-forward        │+│ detects port 1455    │  open     │ creates TCP proxy    │+│                      │           │ on localhost:1455     │+│ code-server          │           │                      │+│ /proxy/1455/ ←───────┼───────── │ proxy forwards here  │+└─────────────────────┘            └──────────────────────┘+                                          ↑+                                   Browser redirects to+                                   localhost:1455/callback+```++## What Gets Forwarded (and what doesn't)++**FORWARDED (temporary, appeared after boot):**+- OAuth callback ports from extensions (Codex, Copilot, etc.) — especially+  `127.0.0.1`-only ones Lima cannot expose+- Any new TCP listener that wasn't running when the machine started+- Auto-expires after 5 minutes (OAuth callbacks are fast)++**NOT FORWARDED (known services, stay behind /proxy/{port} or 0.0.0.0 exposure):**+- code-server (8080 inside the machine; exposed as 7380 mapping/`0.0.0.0`)+- relay WS/HTTP (8765, 8766)+- adom-vscode (8821)+- shotlog (8820)+- Any port in the KNOWN_PORTS list+- Any port that was listening at machine boot time+- Any port ≤ 1023++This keeps the local workspace machine behaving like cloud Hydrogen — known+services use `/proxy/{port}` (or macOS/Lima exposure for `0.0.0.0` binds), and+only short-lived / loopback-only OAuth ports get a dedicated host proxy.++## API Endpoints++### POST /port-forward+Create or remove a host-side TCP proxy. Called by the machine's watcher.++```json+{"port": 1455, "action": "open"}   // Create proxy+{"port": 1455, "action": "close"}  // Remove proxy+```++### GET /port-forward+List currently forwarded ports.++```json+{"ok": true, "forwarded_ports": [1455], "count": 1}+```++## The Watcher Daemon++`scripts/port-watcher.sh` runs inside the machine:++1. Polls `/proc/net/tcp` every 2 seconds+2. Diffs against previous scan+3. New port (not in KNOWN_PORTS whitelist) → POST `/port-forward` open+4. Port gone → POST `/port-forward` close+5. Port open > 5 minutes → auto-expire (likely a service, not OAuth)++No boot snapshot — the whitelist is the sole source of truth. Services+like shotlog can start at any time (days/weeks later) and will never+get a host proxy as long as their port is in the whitelist.++The watcher reaches Hydrogen at the `adom-host` gateway plus the control port+(`<adom-host>:<control>/port-forward`). vz does NOT mirror loopback, so the+machine cannot reach Hydrogen at its own `127.0.0.1`; instead the machine's+`/etc/hosts` maps `adom-host` to the gateway, and that's where Hydrogen's host+listener lives. The control port is dynamic per launch; the live control URL+(already pointing at the gateway host) is in `~/.adom/hd-control-url`.++## Installation++The watcher is a bash script (`port-watcher.sh`) deployed into the machine+during setup steps. Installation uses `limactl shell hd-builder` + `nsenter`+into the machine, NOT `docker cp`/`docker exec`:++```bash+# Stage the script into the Lima VM, then install it into the machine via nsenter.+# (No \\wsl$ share / wsl cat exists on macOS — copy into the VM, then nsenter.)+limactl copy port-watcher.sh hd-builder:/tmp/port-watcher.sh+limactl shell hd-builder -- sudo nsenter -t <leader> -a -- bash -lc \+  'cp /tmp/port-watcher.sh /usr/local/bin/port-watcher.sh && chmod +x /usr/local/bin/port-watcher.sh'+# Launch detached inside the machine (as the adom user)+limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc \+  'nohup bash /usr/local/bin/port-watcher.sh >/tmp/port-watcher.log 2>&1 &'+```++The script parses `/proc/net/tcp` directly in `awk` — it filters listener rows+(state `0A`), splits the hex `local_address`, and converts the port with+`strtonum("0x" …)`. No Python is involved.++## UI: Toast + Port Mappings Dialog++**Toast notification:** When a port is forwarded, Hydrogen shows an info toast:+"Port 1455 forwarded to workspace". When removed: "Port 1455 forwarding stopped".+Powered by the `port-forward-changed` Tauri event.++**Port Mappings dialog:** Accessible from the Adom menu → Admin → "Port Mappings".+Shows exposed `0.0.0.0` ports AND dynamic watcher-created proxies. Dynamic+ports have a "Stop" button. Matches the API Explorer visual style.++## Adding New Known Services++When a new service is added to the machine that runs a persistent TCP server,+add its port to the `KNOWN_PORTS` whitelist string in `port-watcher.sh`+(`scripts/port-watcher.sh:16`):++```bash+KNOWN_PORTS="8080 8765 8766 8821 8820 36035 47084 47083 47085 1420 7380 8770 9000"+```++This prevents it from getting a dedicated host proxy. Services should bind+`0.0.0.0` (exposed by Lima) or use `/proxy/{port}` instead, consistent with web+Hydrogen.++## The forwarding layers++There are distinct forwarding mechanisms. The `/proc/net/tcp` watcher above+is **Layer 2** — and on macOS it is the **primary** path. Layer 1 (the+navigation-interception localhost proxy) is **Windows-only** and does NOT run on+macOS — see the note below.++### Layer 1 — the navigation-interception proxy (Windows-only; NOT active on macOS)++On Windows, Hydrogen intercepts top-level WebView2 navigations via+`setup_frame_navigation_interception()` (`hd-app/src/lib.rs`, `#[cfg(windows)]`).+On every navigation it runs `extract_oauth_callback_port()`, which+**triple-URL-decodes** the target URL (`urlencoding::decode` x3) and scans each+decode layer — plus the raw and the percent-encoded+`redirect_uri=http%3A%2F%2Flocalhost%3A…` form — for+`redirect_uri=http://localhost:<port>`. When it finds one it calls+`start_oauth_callback_proxy(port)`, which **pre-binds** `127.0.0.1:<port>` on the+host and waits up to ~5 min for one inbound request, then forwards the full path+into the machine via code-server (`http://localhost:<code_server_port>/proxy/<port>/<path>`)+and streams the response back to the browser.++**This interceptor does NOT exist on macOS.** It depends on WebView2's+`ICoreWebView2` navigation events; Hydrogen's macOS webview is **WKWebView**, which+exposes no equivalent navigation-interception (the same CDP gap that makes+`/eval` fire-and-forget on macOS). So on macOS the *automatic* zero-latency+pre-bind doesn't happen — the **port watcher (Layer 2) is the primary path**,+with the manual `POST /start-oauth-proxy` endpoint (Layer 3) as the explicit+fallback. The `/start-oauth-proxy` + `/oauth/callback` endpoints themselves are+cross-platform and work the same on macOS.++**Control / callback endpoints** (Hydrogen control API):+- `POST /start-oauth-proxy` — manually pre-bind a callback port (on macOS this is+  the explicit way to get the Layer-1 effect, since the navigation interceptor+  doesn't run).+- `GET /oauth/callback` — Hydrogen's own built-in callback receiver (used by the+  Claude Code PKCE flow); do not call directly.++**Failure mode the AI may see:** if a proxy binds but code-server can't be+reached, Hydrogen returns a styled **502 Bad Gateway** page (dark `#0d1117`+background, teal `#00b8b0` "OAuth callback proxy" heading) reading roughly *"Hydrogen+tried to forward this OAuth callback to the workspace via code-server proxy+(port …/proxy/…), but it failed. The extension that started this OAuth flow may+need its callback port (…) mapped to the host."* Seeing that page in a browser+means the proxy bound but the machine-side proxy hop failed — check the Hydrogen log+for `[oauth-proxy]` lines and that code-server is alive.++### Layer 2 — the port watcher (`/proc/net/tcp` daemon — the primary path on macOS)++The bash daemon documented above. On macOS it is the workhorse (Layer 1's+navigation interceptor is Windows-only): it surfaces new callback ports —+including `127.0.0.1`-only callback servers Lima silently won't expose — and asks+Hydrogen to proxy them. Note: when an app has already registered its port via the+adom-port-hints protocol, the watcher skips it (it only owns the fallback for+third-party extensions that don't know Hydrogen exists).++### Layer 3 — manual++`POST /start-oauth-proxy` or `POST /port-forward` (or+`adom-cli port-forward register`) for cases the watcher misses (on macOS, where+the Windows-only navigation interceptor doesn't run, this is the explicit+pre-bind path).++## Port Conflicts++If the host port is already taken by another Mac app, the forwarding+fails silently. The OAuth callback will break. Mitigation: extensions+typically pick a different random port on retry. Hydrogen logs the conflict.++## How OAuth Callbacks Work End-to-End++1. Extension (Codex) starts HTTP server on `127.0.0.1:1455` inside the machine+2. port-watcher.sh detects port 1455 (not in boot snapshot, not known) —+   note Lima would NOT have exposed this loopback-only listener+3. Watcher POSTs `{"port":1455,"action":"open"}` to Hydrogen+4. Hydrogen creates TCP proxy: `localhost:1455` → code-server `/proxy/1455/`+5. Extension opens browser with `redirect_uri=http://localhost:1455/callback`+6. User authenticates, browser redirects to `localhost:1455/callback?code=...`+7. Hydrogen's proxy receives the request, forwards into the machine via code-server+8. Extension receives callback, completes auth+9. Extension closes its server, port 1455 disappears+10. Watcher POSTs `{"port":1455,"action":"close"}`, Hydrogen removes proxy++## Debugging++All debugging runs inside the machine via `limactl shell hd-builder -- sudo+nsenter -t <leader> -a -S 1001 -G 1001 -- ...`, NEVER `docker exec`:++```bash+# Check what ports the watcher sees+limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc "cat /proc/net/tcp | awk '\$4 == \"0A\" { split(\$2, a, \":\"); printf \"%d\\n\", strtonum(\"0x\" a[2]) }' | sort -n"++# Check the watcher's own log+limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- cat /tmp/port-watcher.log++# Check what Hydrogen has forwarded (from the Mac host)+curl -sf http://127.0.0.1:47084/port-forward++# Manually forward a port (for testing, from the Mac host)+curl -sf -X POST http://127.0.0.1:47084/port-forward \+  -H Content-Type:application/json \+  -d '{"port":1455,"action":"open"}'++# Check Hydrogen log for port-fwd events+# Look for [port-fwd] entries+```++## Related skills++- `hydrogen-networking` — `0.0.0.0` exposure, reaching the host at the `adom-host`+  gateway (vz does NOT mirror loopback), NEVER do these+- `hydrogen-ports` — bind `0.0.0.0` first; fall back to the port-forward registry for+  `127.0.0.1`-only services or unreliable exposure
skills/hydrogen-ports/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-ports/SKILL.mdadded+224
@@ -0,0 +1,224 @@+---+name: hydrogen-ports+description: How Hydrogen does port forwarding between your workspace machine and your Mac — Lima's `0.0.0.0` port exposure (no Docker `-p` map), the code-server `/proxy/<port>/` URL pattern that exposes any internal port, the port-forward registry for `127.0.0.1`-only services that macOS won't expose, and the PortMappingsDialog UI. Use when the user asks "why isn't my server reachable", "how do I expose port X", "what's localhost:7380", "register a port", "expose a port", or "the dynamic port dialog". Trigger words — port forwarding, ports, hydrogen ports, container port, proxy port, code-server proxy, /proxy/, port mappings dialog, port hints, expose port, register port, host port, dynamic port, ports.json, PortConfig, port resolver, localhost port not working.+---++# Hydrogen Ports — forwarding workspace services to your Mac++> This is the machine-runtime version (default). For the legacy Docker container+> runtime (`HD_RUNTIME=docker`) see the docker/ bucket.++The Hydrogen workspace is the `Adom-Workspace` machine (a systemd-nspawn machine booted+inside the `hd-builder` Lima VM), not a Docker container. There is **no Docker+`-p` port mapping** — Lima exposes the machine's listeners to the Mac for you.+Any service that binds `0.0.0.0:<port>` inside the machine is reachable at+`localhost:<sameport>` on the Mac automatically. code-server itself binds+`0.0.0.0:7380`, so `localhost:7380` on the Mac just works. For browser-facing+HTTP/WS, code-server's `/proxy/<port>/` URL pattern is still preferred and still+exposes any internal port. For the cases Lima's exposure can't cover+(`127.0.0.1`-only listeners, or when exposure is flaky), Hydrogen's port-forward+registry creates a real host-side proxy on demand.++## The first rule: bind `0.0.0.0`++| Bind address inside machine | Reachable on the Mac? |+|----------------------------|------------------------|+| `0.0.0.0:N` (all interfaces) | YES — `localhost:N` automatically |+| `127.0.0.1:N` (loopback only) | NO — Lima won't expose it |++So before you do anything else: **make your service bind `0.0.0.0`.** For most+dev servers that's a flag (`--host 0.0.0.0`, `vite --host`, `HOST=0.0.0.0`,+`server.address("0.0.0.0")`). Once it binds `0.0.0.0`, Lima exposes it to the+Mac — no registration needed.++## The code-server proxy URL pattern++Any service listening on port N inside the machine is reachable at:++```+http://localhost:7380/proxy/N/+```++This works for HTTP services. WebSockets work too (`ws://localhost:7380/proxy/N/`).+No port mapping needed, and it works for `0.0.0.0` AND `127.0.0.1`-bound+services (code-server is inside the machine, so it reaches loopback listeners+that the Mac can't). This is the preferred path for anything you open in a+browser.++Examples:+- Your dev server on port 5173 → `http://localhost:7380/proxy/5173/`+- The relay HTTP server on port 8766 → `http://localhost:7380/proxy/8766/`+- adom-vscode's API on port 8821 → `http://localhost:7380/proxy/8821/`++**This is how everything-inside-the-machine reaches everything-outside.** Inside+the machine, services that need to talk to "the host" use the `adom-host` gateway+(`adom-host:<port>`) — macOS/vz does NOT mirror loopback, so `127.0.0.1` does NOT+reach the Mac; the machine's `/etc/hosts` maps `adom-host` to the gateway — for+Hydrogen's own services (Hydrogen's live control URL is in `~/.adom/hd-control-url`), and the+proxy URL for everything else.++## The port-forward registry (for what Lima's exposure can't cover)++Lima's port exposure handles the common case. You still need the registry — Hydrogen's+port-forward registry, serviced by the port-watcher daemon (see+`hydrogen-port-watcher`) — when:++- The service binds **`127.0.0.1` only** and you can't change it (some OAuth+  callback servers, some native tools). Lima won't expose loopback-only+  listeners; the registry creates a real host proxy that reaches them through+  code-server.+- Lima's port exposure is **lagging or broken** for a port (a real Hydrogen+  failure mode: "code-server alive in the machine but the Mac can't reach+  127.0.0.1:7380"). The registry gives a reliable host-side proxy that doesn't+  depend on Lima's port exposure.++Decision order:+1. **Bind `0.0.0.0`** → exposed by Lima, done.+2. Can't bind `0.0.0.0` (loopback-only), or exposure is unreliable →+   **register the port** so Hydrogen proxies it.++### Register a port (from inside your workspace)++```bash+# Tell Hydrogen: "please bind localhost:5555 on the Mac and forward into the machine"+adom-cli port-forward register 5555 --name "oauth-cb" --reason "127.0.0.1-only oauth callback" --visibility external+```++Visibility options:+- `external` — bind `localhost:5555` on the Mac via an HD-managed proxy AND+  keep the `/proxy/` URL working. Best for OAuth callbacks, native clients,+  loopback-only servers.+- `internal` — proxy URL only, no host binding. Best for webview panels that+  don't need a separate host port.+- `either` — let Hydrogen decide based on heuristics (defaults to external).++### Unregister+```bash+adom-cli port-forward unregister 5555+```++### List+```bash+adom-cli port-forward list+```++## The Port Mappings dialog++Open via: Hydrogen Adom menu → "Port Mappings", or `POST http://127.0.0.1:47084/setup/panel/show`+then navigate to the Ports tab. Shows:++- **Exposed ports** — `0.0.0.0` listeners Lima is exposing to the Mac+- **Registered ports** — anything you've registered via `port-forward register`+  (HD-managed host proxies, for loopback-only / unreliable cases)++Each row shows the direct URL (`http://localhost:<port>`) AND the proxy URL+(`http://localhost:7380/proxy/<port>/`). Click either to open in your browser+via the Browser Picker.++## Hydrogen's host-side port registry++Hydrogen reads/writes its host-side port assignments at+`~/Library/Application Support/adom-hydrogen/ports.json`. The `PortConfig` struct+(`hd-control/src/ports.rs`) holds exactly four service ports —+`code_server`, `proxy`, `control`, `cdp` — plus the bind `hostname`. There are+no relay-WS / relay-HTTP host ports in `PortConfig`: in the machine the relay binds+**8765 (WS) / 8766 (HTTP) inside the workspace machine**, reached at the same port on+the Mac (Lima port exposure) or via `/proxy/8766/`, and discovered from+`~/.adom/cli-relay-ports.json` inside the machine. There is no host 47081/47082+listener.++Defaults (you can override via ports.json):++| Service | Default host port |+|---------|-------------------|+| code-server | 7380 |+| Discovery server | 47080 |+| Reverse proxy (`proxy`) | 47083 |+| Hydrogen control API (`control`) | 47084 |+| WebView2 CDP (`cdp`) | 47085 |+| KiCad bridge | **dynamic** (adom-desktop picks at bridge launch) |+| Fusion bridge | **dynamic** (adom-desktop picks at bridge launch) |+| Puppeteer bridge | **dynamic** (adom-desktop picks at bridge launch) |++**Conflict resolution is NOT "next sequential port."** On startup+`PortConfig::resolve_available()` checks `proxy`, `control`, and `cdp`; if a+preferred fixed port is taken, Hydrogen binds an **ephemeral `127.0.0.1:0`** port (the+OS hands back a guaranteed-free one) and persists it+to `ports.json`. It does **not** walk to 47086, 47087, …. `code_server` is+**exempt** and never resolved (it's tied to the+machine's exposed port). So a busy box does NOT predictably land on+47083/47085 — the AI must discover the live ports.++**Never hardcode `127.0.0.1:9001` in machine code** — that was the old default+and has since moved. To find the real runtime ports, hit **`GET /ports`** on the+control API (default `127.0.0.1:47084`) — it returns+`{code_server, proxy, control, cdp, hostname}` straight from the resolved+`PortConfig` (`hd-app/src/lib.rs` `get_ports`). `ports.json` is the single source+of truth every consumer reads.++## Inside vs outside — which URL to use++| You're calling from | To reach a service on port N | Use |+|---------------------|------------------------------|-----|+| Machine shell / app | Another machine service | `http://localhost:N` |+| Machine app | The macOS host | `http://adom-host:N` (vz does NOT mirror loopback — reach the host via the `adom-host` gateway alias; the host binds `0.0.0.0`, no firewall rule) |+| Mac browser | A `0.0.0.0`-bound machine service | `http://localhost:N` (exposed by Lima) |+| Mac browser | Any machine service (HTTP/WS) | `http://localhost:7380/proxy/N/` (proxy URL, preferred) |+| Mac browser | A `127.0.0.1`-only machine service | `http://localhost:N` (only after `port-forward register ... external`) |+| Machine | Hydrogen's own control API (on macOS host) | `$(cat ~/.adom/hd-control-url)` → `http://adom-host:<dynamic>` (port is dynamic per launch) |++## Common bugs++**"My dev server at port 5173 doesn't load in Chrome"**+- Most likely it's bound `127.0.0.1` only. Restart it bound to `0.0.0.0`+  (e.g. `vite --host`) and Lima exposes it at `localhost:5173`.+- OR use the proxy URL: `http://localhost:7380/proxy/5173/`.+- If it IS on `0.0.0.0` but still unreachable, Lima's port exposure may be lagging —+  register it: `adom-cli port-forward register 5173 --visibility external`.++**"OAuth callback fails with refused connection"**+- The provider is hitting `http://localhost:5555/callback` but the callback+  server is loopback-only inside the machine, so Lima doesn't expose it.+- Register first: `adom-cli port-forward register 5555 --visibility external --reason "oauth callback"`.+- Hydrogen auto-detects callback URLs and starts a proxy for them automatically in+  many cases (see Hydrogen log for `[oauth] Callback port X detected`), but+  registering explicitly is the reliable path.++**"`localhost:7380` shows nothing / code-server unreachable from the Mac"**+- code-server is alive in the machine but Lima's port exposure wedged (a+  real HD-logged failure mode). The port-forward registry / port-watcher+  daemon installs a host-side proxy to recover this — see `hydrogen-port-watcher`.+  Or restart Hydrogen to re-establish the forward.++## Related skills++- `hydrogen-networking` — the broader picture: `0.0.0.0` Lima exposure, hostnames,+  proxy architecture, relay vs direct connect, the `adom-host` gateway, NEVER do these+- `hydrogen-port-watcher` — the retained daemon that registers `127.0.0.1`-only+  listeners and covers Lima port-exposure gaps+- `hydrogen-container` — what's inside the workspace reachable on which port+- `hydrogen-desktop-sse` — the workspace API which runs on these ports+- `hydrogen-browser-picker` — what happens when you click a port-mappings URL++## Bridge ports are dynamic++KiCad, Fusion 360, Puppeteer, and any 3rd-party bridges no longer have known+fixed ports. When a bridge launches, **adom-desktop picks an available port at+runtime** and registers it. To find a running bridge's current port, query+adom-desktop:++```bash+adom-desktop status+# Look at desktop.apps.<bridge>.bridgePort+```++Or use the bridge directly via adom-desktop CLI — you don't need the port at all:++```bash+adom-desktop kicad_open_board '{"path":"..."}'  # adom-desktop routes to the right port+adom-desktop browser_eval '{"js":"..."}'+```++This means **old code or skill examples that hardcoded fixed bridge ports+(8771 / 8773 / 8851) are stale.** Use the adom-desktop CLI or `status` lookup+instead.
skills/hydrogen-recording-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-recording-mac/SKILL.mdadded+73
@@ -0,0 +1,73 @@+---+name: hydrogen-recording-mac+description: >+  macOS platform companion to hydrogen-recording — the Apple-specific half of recording+  screen + window video from Adom Hydrogen. The recorder ladder, codec choice,+  the max-duration cap, audio-only narration, and the "● Recording" indicator live+  in hydrogen-recording (adom/hd-bootstrap); THIS skill carries only what's true on macOS:+  Hydrogen's native recorder is NOT available here (it's #![cfg(windows)]), so recording+  goes through the in-app getDisplayMedia recorder, pup per-tab, or the AD desktop+  relay — and the Screen Recording TCC grant gates the desktop paths. Mirror of+  hd-recording-windows (the native WGC/DXGI recorder). Trigger words — record macos,+  screen recording mac, record kicad mac, native recording windows-only,+  screencapturekit, avfoundation, getdisplaymedia mac, screen recording permission,+  tcc grant recording, desktop_record_start macos.+---++# hydrogen-recording (macOS) — no native recorder; use getDisplayMedia / pup / AD++This is the **macOS half** of `hydrogen-recording`. The recorder priority ladder, codec+tradeoffs (h264 vs h265), the `maxSeconds` cap + `reason`, audio-only narration+(`adom-cli hydrogen audio`), and the "● Recording" badge are cross-platform and live in+the generic **hydrogen-recording** skill — read that first. This skill only documents what's+Apple-specific.++## ⭐ There is no native macOS recorder (be honest about this)++Hydrogen's native recorder — `crates/hd-control/src/recorder.rs` — is **`#![cfg(windows)]`**.+On macOS it is **not compiled in**:++- `POST /recording/native/start` returns `{ok:false, error:"native recording is+  Windows-only"}`, and `GET /recording/native/sources` has nothing to enumerate.+- `adom-cli hydrogen recording start --share …` routes to that endpoint, so it **fails+  on macOS** — it does not produce a file.+- **Per-window recording ("record kicad" scoped to one window) does not exist on+  macOS** — that capability is HD-native-only, hence Windows-only.++A ScreenCaptureKit / AVFoundation native recorder is the obvious macOS analog of the+Windows WGC/DXGI path, but **it is not yet built.** Do not claim Hydrogen has a+ScreenCaptureKit recorder.++## The macOS recording paths (what actually works here)++Per the source ladder, on macOS skip the native rungs and use:++1. **In-app `getDisplayMedia` recorder** — the Recording dropdown in the editor toolbar+   (`EditorNav.svelte` + `recordingStore.ts`/`audioCaptureStore.ts`). Records the Hydrogen+   workspace (and/or mic) → a single `.webm` uploaded to the workspace `recordings/`.+   This is the in-HD path. **It DOES assert a display wake-lock** while running (see+   [hydrogen-screen-lock-mac]) — release it by stopping the recording.+2. **pup per-tab — `adom-desktop browser_record_start {sessionId}`** — a clean CDP+   screencast of one Chrome tab, no wake-lock, coexists with everything.+3. **AD host desktop — `adom-desktop desktop_record_start {confirmDesktopNotTabRecording:true}`**+   — the whole-desktop fallback on macOS. Whole-desktop ONLY (no per-window), Chrome+   `getDisplayMedia` → holds the wake-lock, writes a `.webm` into AD's bridges-cache+   (NOT `~/project/recordings/`). **Verify the cap first:** `adom-desktop status` must+   list the `record` capability.++## macOS gotcha — the Screen Recording TCC grant++The desktop-scoped paths (AD `desktop_record_start`, and any `getDisplayMedia` capture+of the screen) are gated by macOS **TCC: System Settings → Privacy & Security → Screen+Recording** must allow the recording app. Without that grant the capture is black /+empty even though the call "succeeds." If a desktop recording comes back blank, check+the Screen Recording grant first.++## NOT here (lives in the Windows companion)++The native WGC/DXGI recorder — `Windows.Graphics.Capture` per-window + DXGI Desktop+Duplication full-screen → Media Foundation hardware H.264/H.265 → mp4 in+`~/project/recordings/`, the `/recording/native/*` endpoints, `adom-cli hydrogen+recording start --share`, and per-window `{source:"window:X"}` scoping (no picker, no+"you're sharing" banner, no wake-lock) — are all Windows-only; see+`hd-recording-windows`. None of that machinery exists on macOS.
skills/hydrogen-screen-lock-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-screen-lock-mac/SKILL.mdadded+61
@@ -0,0 +1,61 @@+---+name: hydrogen-screen-lock-mac+description: >+  macOS platform companion to hydrogen-screen-lock — the Apple-specific half of the display+  wake-lock that a getDisplayMedia capture asserts. The cross-platform story (Hydrogen is+  native → CDP screenshots hold no lock; the recording cap + "● Recording" indicator;+  the contention guard) lives in hydrogen-screen-lock (adom/hd-bootstrap); THIS skill carries+  only the macOS power-assertion mechanics: the IOKit IOPMAssertion+  (PreventUserIdleDisplaySleep), diagnosing the holder with `pmset -g assertions`, and+  `caffeinate`. Mirror of hd-screen-lock-windows (SetThreadExecutionState /+  ES_DISPLAY_REQUIRED, `powercfg /requests`). Trigger words — macos display wake-lock,+  iopmassertion, preventuseridledisplaysleep, pmset assertions, caffeinate, display+  won't sleep mac, screensaver blocked mac, screen capture assertion safari.+---++# hydrogen-screen-lock (macOS) — IOKit power assertions & the display wake-lock++This is the **macOS half** of `hydrogen-screen-lock`. The core point — Hydrogen is native, so its+**CDP screenshots hold no wake-lock** (no `MediaStream`, one-shot grab) and the web+`getDisplayMedia` burn-in leak **cannot happen from Hydrogen's own capture** — plus the+recording cap, the "● Recording" indicator, and the multi-thread contention guard, are+cross-platform and live in the generic **hydrogen-screen-lock** skill. Read that first; this+skill only covers the Apple power-state mechanics.++## What asserts the lock on macOS++A `getDisplayMedia` screen capture asserts an **IOKit power assertion** of type+`kIOPMAssertionTypePreventUserIdleDisplaySleep` (via `IOPMAssertionCreateWithName`).+While it's held the OS **won't run the screensaver or sleep the display** → burn-in.+This is the macOS analog of Windows' `SetThreadExecutionState(ES_DISPLAY_REQUIRED)`.++On macOS Hydrogen itself does **not** assert this for screenshots (CDP is one-shot,+assertion-free). It legitimately appears only when a **`getDisplayMedia` recording**+runs — the in-app recorder, an AD desktop recording, or a pup tab capture (see+[hydrogen-recording-mac]). Release it by **stopping that recording**; a programmatic focus or+the screensaver itself cannot clear another process's assertion.++## Diagnose the holder — `pmset -g assertions`++If you find the display pinned awake:++```bash+pmset -g assertions      # look for a PreventUserIdleDisplaySleep holder+```++- A **browser** holder (a Safari/Chrome "screen capture" assertion) = a `getDisplayMedia`+  hold from web Hydrogen, the in-app recorder, or a pup tab — stop that capture (close+  the tab / `browser_record_stop` / stop the in-app recording), not an HD-shell problem.+- The **Hydrogen app process** holding `PreventUserIdleDisplaySleep` from a CDP **screenshot**+  is unexpected (CDP capture holds no assertion) — capture it and file a bug.++`caffeinate` is the macOS CLI that holds the same assertion deliberately (e.g.+`caffeinate -d`); if you see it in `pmset -g assertions`, something invoked it on+purpose — it is not Hydrogen.++## NOT here (lives in the Windows companion)++The Windows wake-lock mechanics — `SetThreadExecutionState(ES_DISPLAY_REQUIRED)` as the+lock primitive, and diagnosing the holder with `powercfg /requests` (the elevated+`DISPLAY:` section) — are in `hd-screen-lock-windows`. On macOS it's the IOKit+`IOPMAssertion` + `pmset -g assertions`, full stop.
skills/hydrogen-self-screenshot-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-self-screenshot-mac/SKILL.mdadded+93
@@ -0,0 +1,93 @@+---+name: hydrogen-self-screenshot-mac+description: >+  macOS platform companion to hydrogen-self-screenshot — the Apple-specific half of Hydrogen+  screenshotting its own window, PLUS the token-efficient capture flow (`shot` — one+  call, prints a PNG path, never floods context with base64). The tool ladder, the+  `POST /screenshot` CDP endpoint, selector region-clips, the sharing gate, and the+  during-a-recording contention matrix live in hydrogen-self-screenshot (adom/hd-bootstrap);+  THIS skill carries the `shot` helper doctrine and the macOS capture internals: the+  `screencapture` CLI / CoreGraphics window path, the CDP `Page.captureScreenshot`+  content grab, how the per-panel selector crop resolves the rect via `dom.measure`+  (WKWebView has no CDP) and crops with the image crate, and the Screen Recording TCC+  grant. Mirror of hd-self-screenshot-windows (Win32 PrintWindow/BitBlt). Trigger+  words — screenshot hydrogen mac, shot helper, token efficient screenshot, screencapture+  cli, coregraphics screenshot, wkwebview no cdp, dom.measure crop,+  page.capturescreenshot mac, screen recording permission screenshot, tcc screenshot+  grant.+---++# hydrogen-self-screenshot (macOS) — screencapture / CoreGraphics + the CDP content path++This is the **macOS half** of `hydrogen-self-screenshot`. The default flow — `adom-cli+hydrogen screenshot panel|workspace|screen` saving a PNG into the container's+`screenshots/` to `Read`, the `POST /screenshot {target,selector,b64,silent}` CDP+power-path, the sharing gate, and the multi-thread `capture_busy` / coexistence matrix+— is cross-platform and lives in the generic **hydrogen-self-screenshot** skill. Read that+first; this skill documents the token-efficient capture flow and what's Apple-specific+about how the bytes are grabbed.++## ⭐ ALWAYS capture with `shot` — 2 tool calls, ~20 bytes of output++**Never run `adom-cli hydrogen screenshot ...` bare.** It prints the capture as a+~300 KB base64 `dataUrl` JSON on stdout — that's ~85k tokens of noise in your context+per capture, plus a second decode step you'd have to improvise. This bootstrap installs+a wrapper that does the whole thing in one call and prints ONLY the PNG path:++```bash+shot                        # whole workspace (all panels)  → /tmp/shot-workspace-<ts>.png+shot screen                 # entire screen (desktop, other apps)+shot panel --name "KiCad"   # one panel's content+shot workspace --reason "verify the flow viewer rendered"   # flags pass through+```++Then **Read** the printed path — the Read tool attaches images efficiently. Total flow:+one Bash call + one Read. That's the floor; don't add steps.++- If no image comes back, `shot` exits 1 and prints the CLI's own JSON to stderr —+  usually the **sharing gate** (capture needs the user's sharing approval; pass+  `--reason "…"` so the approval dialog says why). Read the error, don't blind-retry.+- `SHOT_OUT=/path/out.png shot ...` overrides the output path if you need a stable name.+- If `shot` is missing (pre-0.1.3 workspace), re-converge: `adom-wiki pkg install+  adom/hydrogen-mac-bootstrap` — or inline the same pipe:+  `adom-cli hydrogen screenshot workspace 2>/dev/null | python3 -c "import sys,json,base64;d=json.load(sys.stdin);open('/tmp/ws.png','wb').write(base64.b64decode(d['dataUrl'].split(',',1)[1]));print('/tmp/ws.png')"`++## How Hydrogen grabs its own window on macOS++- **Whole-window / desktop:** Hydrogen uses the macOS **`screencapture` CLI** (and+  CoreGraphics window capture) to grab the Hydrogen window or the full screen — the macOS+  analog of the Windows `PrintWindow`/`BitBlt` path.+- **Webview / panel content:** the canonical content grab is **CDP+  `Page.captureScreenshot`** (the `/capture/viewport` · `/shot` path) — a one-shot,+  wake-lock-free capture of the rendered surface. This is the same `POST /screenshot`+  endpoint as everywhere; on macOS it backs the `target`/panel content capture.++## ⭐ The per-panel selector crop — `dom.measure`, not CDP++On Windows the `selector` region-clip can lean on CDP against the webview. On macOS the+Hydrogen shell uses **WKWebView, which exposes no CDP** to drive the page directly. So a+per-panel / per-selector crop resolves the rect a different way:++1. Resolve the element's rectangle via the **`dom.measure` ui-action RPC** (the+   UI-command bus measures the selector in the live DOM and returns its bounds).+2. Capture the content area (CDP `Page.captureScreenshot` / `screencapture`).+3. **Crop the captured image to that rect with the Rust `image` crate.**++So `{selector:".resource-bars"}` still clips to just that element — but the rect comes+from `dom.measure` and the crop is server-side in Rust, because WKWebView can't be+CDP-shot to a selector the way a Chromium webview can.++## macOS gotcha — the Screen Recording TCC grant++The OS-level capture (`screencapture` / CoreGraphics) is gated by macOS **TCC: System+Settings → Privacy & Security → Screen Recording** must allow Adom Hydrogen. Without+that grant a window/screen capture comes back **black or empty** even though the call+"succeeds." If screenshots are blank, check the Screen Recording grant first — it's the+same grant the desktop recording paths need ([hydrogen-recording-mac]).++## NOT here (lives in the Windows companion)++The Win32 capture primitives — **`PrintWindow`** (per-window, works for backgrounded+windows) and **`BitBlt`** — are the Windows self-screenshot mechanics; see+`hd-self-screenshot-windows`. On macOS it's `screencapture`/CoreGraphics + CDP+`Page.captureScreenshot`, with `dom.measure`-driven cropping for selectors.
skills/hydrogen-setup-steps/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-setup-steps/SKILL.mdadded+94
@@ -0,0 +1,94 @@+---+name: hydrogen-setup-steps+description: What Hydrogen's setup steps did to prepare your workspace — the 18-step install sequence that imports the Adom-Workspace golden image, injects your Adom session, wires up the relay, and walks the Claude auth gate. Use this skill when the user asks "what did setup do", "why is X installed", "re-run a setup step", "what's a virgin reset", or "why did step N fail". Trigger words — setup steps, install steps, setup panel, virgin reset, re-run step, Run All, what did setup do, why is X installed, setup failed, install-tools, hydrogen setup, claude code extension install, hydrogen workspace ready.+---++# Hydrogen Setup Steps — what was done to prepare your workspace++Machine-runtime version (default). The legacy 24-step Docker cascade+(`HD_RUNTIME=docker`) is in the docker/ bucket.++When you first launched Adom Hydrogen (or after a virgin reset), Hydrogen ran a 18-step install sequence to turn a blank workspace into a fully-tooled Adom workspace — the `Adom-Workspace` machine. The heavy tooling (code-server, the 8 Adom CLIs, the claude CLI + Code extension, VS Code settings, and all `hd-*` skills) is already BAKED into the golden image; setup just imports that image, injects your Adom session, wires up the relay, and walks the Claude auth gate. This skill explains what each step did so you can help the user understand their environment, re-run failed steps, or wipe and rebuild. See `hydrogen-golden-image` for the baked-image model.++The cascade is executed by `setup_steps_macos.rs` (`step_defs_machine()` / `run_all_machine()`). `run_all_machine` is halt-on-failure, resume-not-restart, with per-step 3× auto-retry. In-machine work runs via `limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'` (the machine's DEFAULT user is `adom`, UID 1001, home `/home/adom`; under systemd-nspawn there's no wsl.conf — the default user comes from the machine config / nsenter `-S 1001 -G 1001`; root variant drops `-S 1001 -G 1001`). There is NO `docker exec` and NO Docker image anywhere in this flow.++## The 18 steps (in order)++| # | Step | Purpose |+|---|------|---------|+| 1 | ensure-workspace | Ensure the Lima VM `hd-builder` is up (provision it if missing — no host reboot needed on macOS), `machinectl import-tar Adom-Workspace` from the golden image `adom-golden.tar.gz` (full pre-baked image from the `adom-inc/hd-lima-image` GitHub release), create the `adom` user, start code-server, verify code-server reachable from the Mac on 7380. SUBSUMES the old Docker pull-image / create-container / start-container steps. |+| 2 | wait-codeserver | Host TCP probe of the code-server port (default 7380), 120s |+| 3 | install-adom-vscode | "Activate editor extensions" — installs NOTHING (binary + extension are baked); waits for Adom sign-in, reloads the editor iframe, and proves the :8821 editor-control API answers |+| 4 | set-env-vars | Set `ADOM_CARBON_URL`, `ADOM_HYDROGEN_URL`, `ADOM_HD_CONTROL_URL`, `VSCODE_PROXY_URI`, `ADOM_DESKTOP_MODE` |+| 5 | inject-api-key | Write the Adom session token into the machine at `/var/run/adom/api-key` (mode 644) so adom-cli works |+| 6 | configure-vscode | settings.json / trusted-domains / activity-bar are BAKED; re-asserts theme + workbench backstops idempotently, and applies the per-session layout via :8821 |+| 7 | ensure-adom-desktop | Verify the Adom Desktop companion app is running on the Mac (the relay's desktop bridge) |+| 8 | start-relay | Start the adom-desktop relay inside the workspace (ports 8765 WS / 8766 HTTP) |+| 9 | test-direct-connect | Prove the fast container→desktop command path |+| 10 | test-relay | Register the relay with Adom Desktop for file streaming (file transfer + shell exec) |+| 11 | test-adom-cli | GATE: carbon path + hydrogen-proxy reachability via adom-cli (6 retries) |+| 12 | claude-auth | The single human gate — restore Claude creds or drive the in-editor Claude.ai sign-in via the native browser Browser Picker; runs LAST before the payoff steps |+| 13 | ensure-sse | GATE: confirm the editor browser SSE session is connected so Welcome's webview-open doesn't 409 |+| 14 | verify-workspace | GATE: battery confirming the proxy holds a real layout |+| 15 | welcome | Open Claude Code, authenticate if needed, send the first prompt |+| 16 | open-welcome | GATE: open welcome.html in Hydrogen's right pane; hard-fails if the Welcome tab doesn't appear |+| 17 | eda-discovery | Opt-in payoff: opens a SECOND fresh Claude conversation and human-types a prompt to run `hydrogen-eda-discovery` — quietly detects your EDA tools (KiCad, Fusion 360, Altium, OrCAD/Cadence) and reports what it found. Never blocks setup. |+| 18 | instapcb-quote | Opt-in payoff: opens a THIRD fresh Claude conversation and human-types a prompt to run `hydrogen-instapcb` — finds your uploaded InstaPCB quotes + the quoting engine, then offers to wire the live KiCad→quote loop. Never blocks setup. |++### Steps that NO LONGER EXIST (baked into the golden image)++These are NOT setup steps anymore — they are baked at image-build time: `install-hd-skills`, `verify-adom-desktop`, `install-claude-cli`, `install-claude-ext`, `write-vscode-settings`, `set-trusted-domains`, `clean-layout`. If a user asks "what installed the Claude extension / my skills / VS Code settings", the answer is: **baked into the `adom-golden.tar.gz` golden image at build time, not a setup step.** See `hydrogen-golden-image`.++## Common user questions++**"Why is X installed in my workspace?"** → If X is an Adom CLI, the claude CLI, the Claude Code extension, VS Code settings, or an `hd-*` skill, it was **baked into the golden image** — not run as a setup step. The setup steps only import the image, inject your session, wire the relay, and walk Claude auth. The setup panel (Hydrogen UI → bottom) lists every step with its current status.++**"Setup failed at step N"** → The setup panel shows the error. Most common failures: step 1 (Lima VM `hd-builder` not available / failed to provision, or the `adom-golden.tar.gz` download), step 11 (test-adom-cli — carbon/hydrogen-proxy not reachable yet), step 12 (Claude credentials expired). Each step has a retry button (and the cascade already retried it 3×).++**"Re-run a single step"** → In the setup panel, click the step row and use the Re-run action. Or via API: `POST http://127.0.0.1:47084/setup/step/<id>` (optional body `{"continue_after":true}` to keep going after it).++**"Re-run all steps"** → Click "Run All" in the setup panel; it resumes from the first not-done step. (The old headless `POST /setup/run-all` is now REFUSED.)++## Virgin reset++Virgin reset wipes parts of your workspace so setup can rebuild them cleanly. The ONLY programmatic trigger is `POST /setup/panel/run-virgin-reset`. Toggle which artifacts to delete (option keys in parentheses):++| Toggle (key) | What gets wiped | When to use |+|--------|----------------|-------------|+| Install step state (`install_state`) | Marks all steps as pending (`setup-steps-macos.json`) so they re-run | Always (default on) |+| Workspace machine (`container`) | PRISTINE `machinectl remove Adom-Workspace` + reimport — deletes the machine AND everything in it, including `/home/adom/project` | Always (default on); your work in `/home/adom/project` is GONE |+| Machine tarball (`tarball`, legacy `image`) | Deletes the cached `adom-golden.tar.gz` (full-image re-download) | Rarely — only if the tarball is corrupted |+| Webview storage (`webview_storage`) | Queued wipe, flushed at next Hydrogen launch (restart_required) | Rarely |+| VS Code state (`vscode_state`) | Queued wipe, flushed at next Hydrogen launch (restart_required) | Rarely |+| Adom session token (`adom_token`) | Deletes `hydrogen-session.txt`; forces re-login | Rarely |+| Claude credentials (`claude_token`) | Deletes the host-side Claude creds backup; forces Claude re-auth | When Claude auth is stuck |++`confirmed_destructive` must be set. After toggling, hit "Wipe Selected", then "Run All" to rebuild. The machine reset backs up Claude creds first UNLESS `claude_token` is checked; the `webview_storage` / `vscode_state` wipes are queued and flushed at the next Hydrogen launch (restart_required), not immediate.++CRITICAL: the machine reset ONLY ever runs `machinectl remove Adom-Workspace` (+ reimport). It never runs a global teardown — stopping/deleting the `hd-builder` Lima VM itself or running global `machinectl` teardown (those would kill the user's other Lima VMs / `machinectl` machines, including Docker-for-Mac's own VM), and it performs NO Docker actions and NO reboot for the wipe.++## Setup panel APIs (useful for AI automation)++| Endpoint | What it does |+|----------|--------------|+| `GET /setup/state` | Current state — `never-run`, `in-progress`, `complete`, or `error` |+| `POST /setup/step/<id>` | Re-run a single named step (optional `{"continue_after":true}`) |+| `POST /setup/panel/run-virgin-reset` | Trigger virgin reset with the supplied toggle options |+| `POST /setup/panel/show` | Open the setup panel from anywhere |++All on Hydrogen's control API at `http://127.0.0.1:47084`.++**DEPRECATED — now REFUSED, do not use:** `POST /workspace/unregister`, `POST /setup/virgin-reset`, `POST /setup/run-all`, `POST /setup/run-step`. Use `POST /setup/panel/run-virgin-reset` and `POST /setup/step/<id>` instead.++## Setup state file++Setup writes its state to `~/Library/Application Support/adom-hydrogen/setup-steps-macos.json` (one entry per step with `status: pending|running|done|failed`, output text, percent complete). Hydrogen reads this on launch to decide whether the setup panel auto-opens. (The legacy Docker runtime uses `setup-steps.json`.)++## Related skills++- `hydrogen-golden-image` — the baked-image model: what's pre-installed in `adom-golden.tar.gz` vs. what setup does+- `hydrogen-container` — the workspace machine and runtime that setup configures+- `hydrogen-networking` — how the machine reaches the host (via the `adom-host` gateway alias; live control URL in `~/.adom/hd-control-url`) and how ports forward+- `hydrogen-browser-picker` — used by step 12 (claude-auth) for the Claude auth OAuth flow+- `hydrogen-adom-auth` — how the Adom session token (step 5) gets injected+- `hydrogen-volume` — where your files live in the machine and what survives a virgin reset (or doesn't)
skills/hydrogen-setup/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-setup/SKILL.mdadded+199
@@ -0,0 +1,199 @@+---+name: hydrogen-setup+description: >+  Hydrogen setup panel: 16 install steps, Run All, Rollback All, Virgin Reset with+  toggles, and automated testing patterns. MUST READ before running setup,+  testing steps, or doing virgin resets. Covers the step list, the virgin+  reset toggle panel, how to keep auth during resets, and how Run All handles+  failures. Trigger words — setup panel, install steps, run all, virgin reset,+  rollback, step failed, 18 steps, wipe, reset workspace, keep auth, test setup.+---++# Adom Hydrogen — Setup Panel & Virgin Reset++Machine-runtime version (default). The legacy 24-step Docker cascade+(`HD_RUNTIME=docker`) is in the docker/ bucket.++## Zero-Click Goal++The Hydrogen setup process targets ZERO manual clicks for the user. The heavy+lifting is already done at image-build time: the `Adom-Workspace` golden+image ships with code-server, the 8 Adom CLIs, the claude CLI + Code+extension, VS Code settings, and all `hd-*` skills BAKED IN. Setup just+imports that image, injects the user's session, wires up the relay, and walks+the single human auth gate. If something is missing, Hydrogen downloads it — never+tell the user to install something manually. See the `hydrogen-golden-image` skill+for the baked-image model.++## The 16 Install Steps++The machine cascade is executed by `setup_steps_macos.rs` (`step_defs_machine()` /+`run_all_machine()`). `run_all_machine` is halt-on-failure, resume-not-restart, with+per-step 3× auto-retry. State is tracked in `setup-steps-macos.json`+(the Docker flow uses `setup-steps.json`). The Docker-only `pull-image` /+`create-container` / `start-container` steps are SUBSUMED into step 1,+`ensure-workspace`, which imports the golden image into the workspace machine via+`machinectl import-tar Adom-Workspace`.++| # | ID | Label | Rollback? |+|---|---|---|---|+| 1 | ensure-workspace | Ensure the Lima VM `hd-builder` is up (provision it if missing — no host reboot needed on macOS), import golden image, create `adom` user, start code-server, verify code-server reachable from the Mac on 7380 | ✓ |+| 2 | wait-codeserver | Host TCP probe of code-server port (default 7380), 120s | ✗ |+| 3 | install-adom-vscode | "Activate editor extensions" — installs NOTHING (binary+extension baked); waits for Adom sign-in, reloads editor iframe, proves the :8821 editor-control API answers | ✓ |+| 4 | set-env-vars | ADOM_CARBON_URL / ADOM_HYDROGEN_URL / ADOM_HD_CONTROL_URL / VSCODE_PROXY_URI / ADOM_DESKTOP_MODE | ✓ |+| 5 | inject-api-key | Write the Adom session token to `/var/run/adom/api-key` (mode 644) | ✓ |+| 6 | configure-vscode | settings.json / trusted-domains / activity-bar are BAKED; re-assert theme + workbench backstops idempotently; apply per-session layout via :8821 | ✓ |+| 7 | ensure-adom-desktop | Verify the Adom Desktop companion app is running on the Mac | ✗ |+| 8 | start-relay | Start the adom-desktop relay inside the workspace (8765/8766) | ✓ |+| 9 | test-direct-connect | Prove the fast container→desktop command path | ✗ |+| 10 | test-relay | Register the relay with Adom Desktop for file streaming | ✗ |+| 11 | test-adom-cli | GATE: carbon path + hydrogen-proxy reachability via adom-cli (6 retries) | ✗ |+| 12 | claude-auth | The single human gate; restore creds or drive in-editor Claude.ai sign-in; runs LAST before payoff | ✓ |+| 13 | ensure-sse | GATE: editor browser SSE session connected so Welcome's webview-open doesn't 409 | ✗ |+| 14 | verify-workspace | GATE: battery confirming the proxy holds a real layout | ✗ |+| 15 | welcome | Opens Claude Code, authenticates if needed, sends the first prompt | ✗ |+| 16 | open-welcome | GATE (final): opens welcome.html in Hydrogen's right pane; hard-fails if the Welcome tab doesn't appear | ✗ |++### Steps that NO LONGER EXIST (baked into the golden image)++These are NOT setup steps anymore — they are baked at image-build time and+must not be listed or "run": `install-hd-skills`,+`verify-adom-desktop`, `install-claude-cli`, `install-claude-ext`,+`write-vscode-settings`, `set-trusted-domains`, `clean-layout`. The 8+Adom CLIs, the claude CLI + Code extension, code-server, settings, and all+`hd-*` skills are baked into the `adom-golden.tar.gz` image. See+`hydrogen-golden-image`.++## Panel Buttons++- **Run All** — executes all pending steps sequentially. STOPS on first failure.+- **Rollback All** — rolls back all completed steps in reverse order.+- **Virgin Reset ▾** — opens the toggle panel (see below).+- **Run** (per-step) — runs a single step.+- **Rollback** (per-step) — rolls back a single step.++## CRITICAL: Run All Stops on Failure++If any step fails, Run All ABORTS (halt-on-failure in `run_all_machine()`, after+the per-step 3× auto-retry is exhausted). Remaining steps are NOT executed.+`run_all_machine` resumes — it does NOT restart from step 1 — so to recover after+a failure: fix the issue, then either:+- Click "Run All" again (it resumes from the first not-done step)+- Click "Run" on the failed step to retry just that one++## Virgin Reset Toggle Panel++Click "Virgin Reset ▾" to expand the wipe options. Each toggle has a+live status badge showing the current state. The ONLY programmatic trigger is+`POST /setup/panel/run-virgin-reset` (see below).++| Toggle (option key) | What it wipes | Status badges |+|---|---|---|+| Install step state (`install_state`) | Resets all 18 steps to pending (`setup-steps-macos.json`) | CLEAN / EXISTS |+| Workspace machine (`container`) | PRISTINE `machinectl remove Adom-Workspace` + reimport (ONLY that machine) | GONE / RUNNING / EXISTS |+| Machine tarball (`tarball`, legacy `image`) | Deletes cached `adom-golden.tar.gz` (full image re-download) | CACHED / GONE |+| Webview storage (`webview_storage`) | Queued wipe, flushed at next Hydrogen launch (restart_required) | CLEAN / EXISTS |+| VS Code state (`vscode_state`) | Queued wipe, flushed at next Hydrogen launch (restart_required) | CLEAN / EXISTS |+| Adom session token (`adom_token`) | Deletes `hydrogen-session.txt` | CLEAN / EXISTS |+| Claude credentials (`claude_token`) | Deletes the host-side Claude creds backup | CLEAN / EXISTS |++`confirmed_destructive` must be set for the reset to proceed. **"Wipe+Selected"** at the bottom executes the reset for all checked toggles.++The machine reset is a PRISTINE `machinectl remove` + reimport — it backs up+Claude creds first UNLESS `claude_token` is checked. The `webview_storage` /+`vscode_state` wipes are queued and flushed at the next Hydrogen launch+(restart_required), NOT immediate; there is no reboot for the wipe.++CRITICAL — the machine virgin reset only ever touches `Adom-Workspace`. It runs+`machinectl remove Adom-Workspace` — NEVER a global teardown (stopping/deleting the+`hd-builder` Lima VM itself or running global `machinectl` teardown), which would+knock out the user's other Lima VMs / `machinectl` machines (e.g. Docker-for-Mac's+own VM). There is NO Docker actions.++## Automated Testing Pattern++For AI-driven ralph loop testing, keep auth tokens so steps don't fail:++```+Checked:   install_state, container+Unchecked: tarball (cached, saves the full-image re-download),+           adom_token, claude_token+```++This gives a freshly-imported machine while keeping auth working — step 5+(inject-api-key) and step 12 (claude-auth) won't fail.++### Programmatic triggers (the ONLY supported headless paths)++```bash+# Virgin reset — the ONE allowed programmatic trigger+curl -X POST http://127.0.0.1:47084/setup/panel/run-virgin-reset \+  -H Content-Type:application/json \+  -d '{"confirmed_destructive":true,"install_state":true,"container":true,+       "tarball":false,"webview_storage":false,"vscode_state":false,+       "adom_token":false,"claude_token":false}'++# Re-run a single step (optional continue_after to keep going)+curl -X POST http://127.0.0.1:47084/setup/step/test-direct-connect \+  -H Content-Type:application/json -d '{"continue_after":true}'+```++DEPRECATED — do NOT use these; they are now REFUSED (deprecated headless+triggers): `POST /workspace/unregister` (destructive `machinectl remove Adom-Workspace`),+`POST /setup/virgin-reset`, `POST /setup/run-all`, `POST /setup/run-step`.++## Key Steps Explained++### Step 1 — ensure-workspace+The big one. Ensures the Lima VM `hd-builder` is up (provisions it if missing —+no host reboot needed on macOS), then `machinectl import-tar Adom-Workspace`+from the full pre-baked golden image `adom-golden.tar.gz` (pulled from the+`adom-inc/hd-lima-image` GitHub release — NOT a thin ~30 MB tarball, NOT a 390 MB+Docker image), creates the `adom` user, starts code-server, and verifies+code-server reachable from the Mac on port 7380. This single step replaces the+old Docker pull-image / create-container / start-container trio. The machine's+DEFAULT user is `adom` (UID 1001; under systemd-nspawn there's no wsl.conf — the+default user comes from the machine config / nsenter `-S 1001 -G 1001`), NOT root;+in-machine exec = `limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'`+(root variant drops `-S 1001 -G 1001`) — NEVER `docker exec`.++### Step 5 — inject-api-key+Writes the Adom session token into the machine at `/var/run/adom/api-key`+(mode 644) so adom-cli works. Requires the user to be signed in. Keep the+`adom_token` toggle UNCHECKED during automated test resets.++### Step 9 — test-direct-connect+Proves the fast container→desktop command path. The CLI auto-probes the host+control API (wired to the `adom-host` gateway by `/etc/init-host-internal.sh`) and+routes through Adom Desktop's direct connect API.++### Step 10 — test-relay (register relay)+Registers the relay (started by step 8, `start-relay`) with Adom Desktop for+file streaming via the code-server proxy path. This enables `pull_file`,+`send_files`, and `shell_execute`.++### Step 12 — claude-auth+The single human gate. Restores / validates Claude creds in the machine+(backed up host-side) and, when needed, drives the in-editor Claude.ai sign-in+via the native browser Browser Picker. Runs LAST before the payoff steps.++### Steps 13-16 — the final gates+`ensure-sse` confirms the editor browser SSE session is connected (so the+Welcome webview-open doesn't 409); `verify-workspace` confirms the proxy holds+a real layout; `welcome` opens Claude Code and sends the first prompt; the+final `open-welcome` gate opens welcome.html in Hydrogen's right pane and HARD-FAILS+if the Welcome tab doesn't appear.++> Note: the Adom CLIs, the claude CLI + Code extension, VS Code+> settings, and `hd-*` skills are NOT setup steps — they are baked into+> `adom-golden.tar.gz`. See `hydrogen-golden-image`.++## Machine Recreation++When the machine is removed and re-imported (step 1), the new machine is a+fully pre-baked Ubuntu from `adom-golden.tar.gz`. code-server binds+`0.0.0.0:7380` inside the machine and Lima exposes it to the Mac at+`localhost:7380` — there is no Docker port-mapping layer, so the port-conflict /+stale-mapping class of bug that affected the Docker runtime does not apply here.+Other services tunnel through the code-server `/proxy/{port}/` path.
skills/hydrogen-topology/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-topology/SKILL.mdadded+134
@@ -0,0 +1,134 @@+---+name: hydrogen-topology+description: >+  MUST READ before ANY Hydrogen work. Explains the three-layer topology: cloud+  Docker (where Claude runs), the user's Mac (where Hydrogen + Adom Desktop run),+  and the Hydrogen local workspace (the Adom-Workspace machine). Every command+  you run goes through the relay to the Mac. You CANNOT directly access the+  workspace machine — you must shell through the Mac. Trigger words — Hydrogen topology,+  cloud vs local, machine exec, nsenter, where am I, which workspace, adom-desktop+  relay, test from container, three tiers, architecture, mac machine.+---++# Adom Hydrogen — Topology (READ FIRST)++Machine-runtime version (default). Legacy Docker container runtime (`HD_RUNTIME=docker`) is in the docker/ bucket.++## You are NOT on the user's machine++You (Claude) run inside an **Adom cloud Docker container**. The user's+Mac is a remote target you reach through the `adom-desktop`+CLI relay. There are THREE separate environments:++### 1. Cloud Docker (where you run)+- Your shell, your filesystem, your git repos+- `adom-desktop` CLI connects to Adom Desktop on the Mac via relay+- This container has NOTHING to do with Hydrogen's local workspace+- Restarting the workspace machine on the Mac does NOT affect you+- You can freely `git push`, edit code, run builds here++### 2. The user's Mac (user's desktop)+- **Adom Desktop** — relay client app, listens on port 8770 (direct connect)+- **Adom Hydrogen** — the Tauri app you're building+- **Lima VM (`hd-builder`)** — hosts the `Adom-Workspace` machine+- You reach this via `adom-desktop shell_execute '{"command":"..."}'`+- Commands run in a shell on the Mac (zsh/bash)++### 3. Hydrogen Local Workspace (machine) (the `Adom-Workspace` machine)+- Machine name: `Adom-Workspace` (fixed — no random suffix, no container name)+- Runs code-server, adom-desktop CLI, relay server+- You reach this via: `adom-desktop shell_execute '{"command":"limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- <cmd>"}'`+- This is TWO hops: cloud → relay → Mac → nsenter into the machine++## Command Patterns++### Run something on the Mac+```bash+adom-desktop shell_execute '{"command":"ls ~/adom"}'+```++### Run something in the Hydrogen local workspace machine+```bash+adom-desktop shell_execute '{"command":"limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- ls /home/adom"}'+```++### Test adom-desktop CLI inside the workspace machine+```bash+adom-desktop shell_execute '{"command":"limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- adom-desktop ping"}'+```+This tests the FULL chain: machine → local relay → Adom Desktop on the Mac.++### Screenshot Hydrogen window+```bash+adom-desktop desktop_screenshot_window '{"hwnd":HWND}'+```++### Pull file from the Mac to cloud+```bash+adom-desktop pull_file '{"filePaths":["/Users/.../file"],"saveTo":"/tmp"}'+```++## The workspace machine ≠ Your Container++- If the `Adom-Workspace` machine on the Mac goes down, YOUR cloud container is fine+- You can still talk to Adom Desktop, screenshot windows, run shell commands+- You just can't `nsenter` into the workspace machine until it's back+- Starting/stopping the machine on the Mac is done via shell_execute. Only ever+  touch `Adom-Workspace` (`machinectl terminate Adom-Workspace`) — NEVER tear down+  the shared Lima VM `hd-builder` wholesale or run a global `machinectl` teardown,+  which would knock out any other Lima VM / machine the user has.++## The Relay Chain++```+Cloud adom-desktop CLI+  → WS relay (cloud :8765)+    → internet+      → Adom Desktop on the Mac (relay client)+        → executes command locally+          → returns result+            → back through relay to your CLI+```++For the Hydrogen local workspace machine, there's a SECOND relay:+```+Workspace's adom-desktop CLI+  → local relay (machine :8765)+    → code-server proxy (:7380/proxy/8765/)+      → Adom Desktop on the Mac (connected as WS client)+        → executes command+          → returns through same chain+```++## Critical Setup Steps for the Hydrogen Local Workspace (machine)++The Hydrogen install flow MUST verify this chain works:++1. **Verify Adom Desktop running on the Mac** — probe port 8770+   If not running: tell user to install from wiki and launch it+2. **Install adom-desktop CLI into the workspace** — download binary+3. **Start relay inside the workspace** — `adom-desktop serve`+4. **Register relay with Adom Desktop** — `server_add` via direct connect+   URL: `ws://127.0.0.1:{code_server}/proxy/8765/`+5. **Test round-trip** — `limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- adom-desktop ping`+   Must return `pong` — proves the full chain works++## NEVER Do These++1. **NEVER confuse your cloud Docker with the Hydrogen local workspace machine.**+   `curl http://127.0.0.1:8766/health` hits YOUR relay, not the workspace's.+2. **NEVER try to enter the machine from your cloud shell.**+   The machine lives in the Lima VM on the Mac, not here.+3. **NEVER assume the workspace's status affects your container.**+   They're completely independent.+4. **NEVER tear down the shared Lima VM `hd-builder` wholesale or run a global+   `machinectl` teardown of anything other than `Adom-Workspace`.** Scope to the+   `Adom-Workspace` machine only (`machinectl terminate Adom-Workspace`) so the+   user's other Lima VMs / machines stay safe.+5. **NEVER skip the adom-desktop round-trip test.**+   The relay chain has many links — test end-to-end, not individual parts.++## Related skills++- `hydrogen-container` — facts about the `Adom-Workspace` machine you run inside+- `hydrogen-volume` — where your files live and how to back up / copy out the machine
skills/hydrogen-ui-mac/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-ui-mac/SKILL.mdadded+60
@@ -0,0 +1,60 @@+---+name: hydrogen-ui-mac+description: >+  macOS platform companion to hydrogen-ui — the Apple-specific half of DRIVING Hydrogen's UI.+  Every menu/dialog/panel id, the GET /ui/actions → POST /ui/invoke contract, and+  registerUiAction live in hydrogen-ui (adom/hd-bootstrap); THIS skill carries only what's+  macOS-specific: Hydrogen's webview is WKWebView with NO Chrome DevTools Protocol, so the+  legacy CDP .click() path is dead — you drive the UI via the platform-agnostic+  command bus (hd-ui-command → /ui/_result) and the backend invoke_ui_action RPC+  (e.g. dom.measure resolves a selector's rect WITHOUT CDP). Mirror of hd-ui-windows+  (raw CDP clicking, hd-cdp.js, WebView2 CDP). Trigger words — drive Hydrogen UI macos,+  WKWebView, no CDP, dom.measure, hd-ui-command, invoke_ui_action, ui/invoke macos,+  click Hydrogen menu mac, open dialog mac.+---++# hydrogen-ui (macOS) — driving the UI without CDP++This is the **macOS half** of `hydrogen-ui`. The window layout, the menu/dialog catalog, the+list of action ids, and the whole `GET /ui/actions` → `POST /ui/invoke` workflow are+host-agnostic and live in the generic **hydrogen-ui** skill — read that first. This skill only+covers what changes on a Mac: **how clicks reach the webview when there is no CDP.**++## The macOS reality — WKWebView has no CDP++On macOS Hydrogen's webview is **WKWebView**, which exposes **no Chrome DevTools Protocol+port**. The Windows lineage drove the UI by CDP `.click()` on CSS selectors (`hd-cdp.js`+→ WebView2's CDP). **None of that works here** — there's no port to attach to (the same+limitation that makes `/eval` effectively fire-and-forget on macOS). So on macOS the+command bus isn't merely *preferred*, it's the **only** way to operate Hydrogen's UI. For an+element that isn't on the bus yet, **register it** (a 2-line `registerUiAction(...)` —+see hydrogen-ui) rather than reaching for a selector click; there is nothing to click *through*.++## How the bus crosses to WKWebView — `hd-ui-command` → `/ui/_result`++`POST /ui/invoke {id}` is platform-agnostic precisely because it does **not** depend on+CDP. The backend (`src-tauri/crates/hd-control/src/lib.rs`, `invoke_ui_action`) emits a+Tauri event **`hd-ui-command` `{id, args, request_id}`**; the frontend dispatcher runs+the registered handler and reports back via **`POST /ui/_result` `{request_id, ok,+value}`**. The call blocks until that result lands (or the timeout fires). This is the+identical round-trip on every host — but on macOS it's load-bearing, since it's the+substitute for the CDP path Windows still has.++## `dom.measure` — resolve a selector's rect without CDP++The one place the Windows path leaned on CDP that macOS had to re-plumb is **geometry**.+Where Windows cropped a region screenshot server-side via CDP `Page.captureScreenshot`+with a clip, macOS can't — so the capture path calls **`invoke_ui_action(app,+"dom.measure", {selector}, 3000)`** to ask the *frontend* for the element's rect, then+`screencapture`s the webview content area and crops the PNG to that rect+(`hd-control/src/lib.rs` ~5193). `dom.measure` returns `{left, top, width, height,+viewport:{width,height}}` in CSS px. It's a normal bus action — the macOS analog of CDP's+`Runtime.evaluate` for "where is this element," reusable by any caller that needs a+selector's geometry without CDP.++## NOT here (lives in the Windows companion)++Raw CDP `.click()` driving, the `node C:\Users\…\hd-cdp.js` snippets, the WebView2 CDP+port, and the CSS-selector-click playbook are the **Windows** UI path — see+`hd-ui-windows`. On macOS that machinery does not exist; drive everything through the+command bus (`/ui/actions` → `/ui/invoke`) and register anything that's missing.
skills/hydrogen-volume/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-volume/SKILL.mdadded+110
@@ -0,0 +1,110 @@+---+name: hydrogen-volume+description: How Hydrogen's machine workspace storage is laid out — what's persistent, what's ephemeral, where your work lives, what survives a workspace restart vs a virgin reset, and how to access your workspace files from your Mac host. Use when the user asks "where are my files", "did I lose my work", "how do I copy a file out of the workspace", or "what happens to my code if I virgin reset". Trigger words — machine filesystem, hydrogen volume, where are my files, workspace files, persistent storage, lost my work, /home/adom/project, machine filesystem, machine export, copy file out of workspace, where is my code, workspace backup, machine remove.+---++# Hydrogen Workspace Storage — what persists, what doesn't++Machine-runtime version (default). Legacy Docker container runtime (`HD_RUNTIME=docker`) is in the docker/ bucket.++Your work lives on the `Adom-Workspace` machine's filesystem at `/home/adom/project` — a systemd-nspawn machine rootfs under the `hd-builder` Lima VM (e.g. `/var/lib/machines/Adom-Workspace`). There is **no Docker named volume** — `/home/adom/project` sits directly in the machine's own ext4 filesystem. ALL your work belongs under that path. It persists for as long as the machine stays registered.++## The filesystem++| Property | Value |+|----------|-------|+| Machine name | `Adom-Workspace` (fixed) |+| Your work path | `/home/adom/project` (plain directory in the machine's ext4 fs) |+| Backing store | The machine's rootfs directory inside the Lima VM (`hd-builder`), e.g. `/var/lib/machines/Adom-Workspace/` on the VM disk |+| On-disk location (Mac) | N/A on macOS — it lives inside the `hd-builder` Lima VM's disk image, not directly on the Mac. Reach it via the relay or `limactl shell hd-builder` + nsenter (see below) |+| Persistence | Survives workspace restart, Hydrogen restart, and Mac reboot. Persists as long as the machine stays **registered** (`machinectl`). |++There is **no named volume to delete separately** — the project files and the OS image are one and the same ext4 filesystem. The only thing that wipes `/home/adom/project` is removing the machine.++## What lives WHERE++| Path inside the machine | Persists across a tarball re-import? | Notes |+|------------------------|---------------------------------------|-------|+| `/home/adom/project/` | **Yes** — Hydrogen exports `/home/adom` before re-importing the golden image on a version bump, then restores it | This is your code. Put everything important here. |+| `~/.local/share/code-server/extensions/` | **Yes** within the machine — BAKED into the golden image, restored on tarball re-import | Not a setup step; baked at image-build time |+| `~/.local/share/code-server/User/settings.json` | **Yes** within the machine — BAKED into the golden image | Not a setup step; baked at image-build time |+| `~/.claude/` (skills, credentials) | Skills are BAKED into the golden image (restored on re-import); credentials are restored by the `claude-auth` setup step from a host-side backup | Hydrogen skills are baked FLAT into `~/.claude/skills/<name>/` |+| `/tmp/` | **No** | Ephemeral |+| `/etc/`, `/usr/`, system stuff | Lives in the same ext4 fs, but is overwritten on a tarball version bump | Treat as part of the workspace image |++**Rule of thumb:** if you want it to survive a workspace rebuild, put it under `/home/adom/project/`.++## What survives each virgin-reset choice++When you open Virgin Reset (Hydrogen setup panel), the checkbox toggles control which artifacts get deleted:++| Toggle ON means: | Result |+|------------------|--------|+| Install step state | `setup-steps-macos.json` deleted; next setup re-runs everything. Your project files unaffected. |+| Workspace machine | **`machinectl remove Adom-Workspace`. This wipes the ENTIRE machine including `/home/adom/project/`. This is the destructive option.** No reboot needed; the wipe is immediate. |+| Workspace image | Deletes the cached `adom-golden.tar.gz`, so setup re-downloads it on next run. Your machine (and your code, if still registered) is unaffected. |+| Adom session token | Re-login required next time. Project files unaffected. |+| Claude credentials | Claude Code requires re-auth. Project files unaffected. |++**The only toggle that destroys your work is "Workspace machine" (the `machinectl remove`).** All others are safe for your code. (Note: the "image" reset just deletes the cached `adom-golden.tar.gz` — NOT a 390MB Docker image like the legacy runtime.)++⚠️ The reset only ever targets `Adom-Workspace`. It NEVER touches the Lima VM itself or any other machine registered on the host.++## Accessing workspace files from the Mac++There is **no Finder share** into the machine (no macOS equivalent of the old `\\wsl$` Explorer share). Reach your files via the relay or by shelling into the Lima VM.++### 1. `limactl shell hd-builder` + nsenter + cp+```bash+# Drop into the Lima VM, enter the machine namespace as the adom user, and operate on files+limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc \+  'ls -la /home/adom/project'+```+Use `limactl copy` to move files between the Mac and the VM disk, then `nsenter` + `cp` to land them in the machine's `/home/adom/project`.++### 2. From inside the machine, there is no `/mnt/c` drive mount+N/A on macOS — there are no Windows drive mounts. Use the relay instead (see below).++### 3. `adom-desktop send_files` / `pull_file` (from inside the workspace, via the relay)+```bash+# Send a workspace file to your Mac desktop's Downloads folder+adom-desktop send_files '{"filePaths":["/home/adom/project/myfile.txt"],"targetApp":"general","destinationFolder":"."}'++# Pull a Mac file into the workspace's /tmp+adom-desktop pull_file '{"filePaths":["/Users/<you>/Downloads/myfile.txt"],"saveTo":"/tmp"}'+```++### 4. Open the workspace's `/home/adom/project/` in VS Code's File Explorer+Just use the built-in code-server tree — the project folder is what's open, so anything you create/edit there hits the machine fs directly.++## Backing up your workspace++Persistence and backup for the machine is `machinectl export-tar` / `machinectl import-tar` (run inside the `hd-builder` Lima VM) — NOT a Docker volume tar. Before a destructive operation (the workspace-machine virgin-reset / `machinectl remove`):++```bash+# Export the whole machine to a tarball inside the Lima VM+limactl shell hd-builder -- sudo machinectl export-tar Adom-Workspace /tmp/adom-workspace-backup.tar+# (then limactl copy /tmp/adom-workspace-backup.tar back to the Mac if you want it off-VM)++# Restore it later (re-register the machine from the backup)+limactl shell hd-builder -- sudo machinectl import-tar /tmp/adom-workspace-backup.tar Adom-Workspace+```++This is exactly the mechanism Hydrogen's own tarball-migration uses: on a tarball version bump it `machinectl export-tar`s `/home/adom` first, then re-imports the new base and restores your work.++Or, for just a few files, copy them out first via the relay or `limactl shell hd-builder` + nsenter + cp.++## Why a machine filesystem (no named volume, no bind mount)?++Hydrogen keeps your work directly inside the `Adom-Workspace` ext4 filesystem rather than a Docker named volume or a bind mount of a Mac path. This:+- Gives native Linux ext4 I/O — much faster than crossing the macOS ↔ VM filesystem boundary+- Avoids line-ending and permission issues that bind mounts of host paths often have+- Lets Hydrogen wipe + re-import the base image (on a tarball bump) without losing your code — it `machinectl export-tar`s `/home/adom` first++There is no Finder share into the machine on macOS — use the relay (`adom-desktop send_files`/`pull_file`) or `limactl shell hd-builder` + nsenter + cp to move files in and out.++## Related skills++- `hydrogen-container` — the `Adom-Workspace` machine whose filesystem holds your work+- `hydrogen-topology` — the three-tier topology; tier 3 is this Adom-Workspace machine+- `hydrogen-golden-image` — what's baked into `adom-golden.tar.gz` (extensions, settings, skills) vs. what setup does
skills/hydrogen-workspace-lifecycle/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-workspace-lifecycle/SKILL.mdadded+173
@@ -0,0 +1,173 @@+---+name: hydrogen-workspace-lifecycle+description: Workspace machine (`Adom-Workspace`) lifecycle management for Adom Hydrogen on macOS. Import/export/remove/terminate, code-server-as-HD-child, the resume reality-check, recovery after sleep/hibernate, and every hard-won rule about what NOT to do — above all, NEVER run a global teardown; touch ONLY Adom-Workspace so the user's other Lima VMs / machines stay safe. MUST READ before any workspace operation. Trigger words — machine, workspace, Adom-Workspace, restart workspace, terminate machine, remove, machinectl import-tar, machinectl remove, code-server, workspace broken, workspace unhealthy, machine hung, fix workspace, /workspace/status.+---++# Adom Hydrogen — Workspace Machine Lifecycle++> This is the machine-runtime version (default). The legacy Docker equivalent+> (`HD_RUNTIME=docker`) is in the docker/ bucket.++Under the machine runtime the workspace is the **`Adom-Workspace` machine** (Ubuntu, a+systemd-nspawn machine imported with `machinectl import-tar`), running locally inside+Hydrogen's `hd-builder` Lima VM on the user's Mac. `/home/adom/project` lives inside the+machine — there is no Docker volume.++## ABSOLUTE RULE — NEVER do a global teardown++**Operate ONLY on `Adom-Workspace`. Never run any global teardown that hits the shared+Lima VM or other machines.**++Forbidden, no exceptions:+- ❌ stopping or deleting the `hd-builder` Lima VM itself (kills every machine inside it)+- ❌ `machinectl terminate` on any machine other than `Adom-Workspace`+- ❌ blanket killing of `machinectl` / nsenter processes+- ❌ restarting / re-provisioning the `hd-builder` Lima VM+- ❌ `machinectl remove` on anything but `Adom-Workspace`+- ❌ global `machinectl` teardown++**Why this matters:** the user may run other Lima VMs or `machinectl` machines, and+Docker-for-Mac keeps its own VM. Any global teardown — stopping/deleting the shared+`hd-builder` VM or wiping other machines — would knock those out. It's harmless to Hydrogen's+own `Adom-Workspace`, but it looks to the user like Hydrogen broke their other VMs / Docker —+terrible first-run UX. So every Hydrogen operation must be scoped to `Adom-Workspace` and+nothing else.++Allowed, scoped operations:+- ✅ `machinectl terminate Adom-Workspace` (this is what "Restart" does)+- ✅ `machinectl remove Adom-Workspace` (destructive wipe, this machine only)+- ✅ `machinectl import-tar Adom-Workspace <tarball>` (fresh import)+- ✅ `limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'` (exec inside the machine)++## In-machine exec — NEVER `docker exec`++To run a command inside the workspace:+```bash+limactl shell hd-builder -- sudo nsenter -t <leader> -a -S 1001 -G 1001 -- bash -lc '<cmd>'+# or, root variant (drop -S/-G):+limactl shell hd-builder -- sudo nsenter -t <leader> -a -- bash -lc 'su - adom -c "..."'+```+`<leader>` is the machine's leader PID. There is no `docker exec` under this runtime,+and no Docker container to exec into.++## Workspace Lifecycle++### Import (fresh setup)+`setup_and_start` → `MacosVmRuntime::setup_and_start` → `fresh_import`:+download `adom-golden.tar.gz` (the full pre-baked golden image, downloaded+anonymously from the `adom-inc/hd-lima-image` GitHub release, SHA256-verified)+then `machinectl import-tar Adom-Workspace adom-golden.tar.gz`, then start code-server.+Log proof: `[golden-tarball] downloaded … SHA256 OK` → `[setup] machine setup_and_start completed`+and `machinectl list` shows `Adom-Workspace` running.++### code-server is an HD-OWNED CHILD process+code-server (`CODE_SERVER_CHILD` in `runtime/macos.rs`) runs as a child of Hydrogen, deliberately+tied to Hydrogen's lifetime — **NOT a detached `nohup`** (a detached process dies when the+machine tears down the session anyway). Consequence: every Hydrogen restart (rebuild / crash /+relaunch) orphans the old code-server, so a fresh Hydrogen can come up with the machine+running but the editor DEAD ("Starting AI Environment" forever) even when setup is+otherwise complete.++Fix (already in Hydrogen): Hydrogen **re-ensures code-server on every launch**+(`[macos-startup] re-ensured code-server on launch`). And the resume reality-check in+`run_all_machine` requires code-server reachable on the host port **7380** — not just+"machine present" — before it trusts the workspace as ready.++### Restart+"Restart Workspace" = `machinectl terminate Adom-Workspace` + re-ensure code-server.+NOT `docker restart`. This is the fix for a workspace that's gone unreachable after+laptop sleep/hibernate.++### Stop+`machinectl terminate Adom-Workspace`. The machine stays present (and your project files+persist); it just isn't running.++### Export / backup+`machinectl export-tar Adom-Workspace <file>.tar` snapshots the machine to a tarball.+Re-import with `machinectl import-tar Adom-Workspace <file>.tar`. (Scoped to this machine,+so safe.)++### Remove (DESTRUCTIVE wipe)+`machinectl remove Adom-Workspace` — deletes the machine AND everything in it, including+`/home/adom/project`. This is the only destructive wipe, and it names ONLY+`Adom-Workspace`. Never remove any other machine.++## Control API++The Hydrogen control API is on loopback **`http://127.0.0.1:<dynamic>`** (NOT 9001). The+port is dynamic per launch — read the live URL from `~/.adom/hd-control-url`. From+inside the machine, reach the host via the `adom-host` gateway alias (vz does NOT+mirror loopback, so 127.0.0.1 inside the machine is NOT the Mac).++| Endpoint | What it does |+|---|---|+| `GET /workspace/status` | Reports the machine state — present? running? code-server reachable on :7380? |+| `POST /workspace/unregister` | Destructive: `machinectl remove Adom-Workspace` (this machine only) |+| `GET /container-stats` | Live CPU/RAM/disk from the `workspace_stats()` probe (see hydrogen-container-stats) |++```bash+BASE="$(cat ~/.adom/hd-control-url)"   # http://127.0.0.1:<dynamic>++# Workspace status (from inside the machine, via the adom-host gateway)+curl -s "$BASE/workspace/status"++# Destructive wipe of ONLY Adom-Workspace+curl -s -X POST "$BASE/workspace/unregister"+```++## Diagnosing Workspace Problems++### "Workspace unhealthy" / editor stuck on "Starting AI Environment"+- **Cause**: code-server child was orphaned by an Hydrogen restart, or the machine went+  unreachable after sleep.+- **Check**: `[macos] Workspace unhealthy …` in the Hydrogen log (the real signal). NOTE: a+  `[docker] Container runtime unhealthy …` line under the machine runtime is **benign+  noise** — a leftover heartbeat with no container bound. Ignore it; look at the+  `[machine]` line.+- **Fix**: Restart Workspace (Adom menu) = `machinectl terminate Adom-Workspace` + re-ensure+  code-server. Verify code-server answers on :7380.++### Machine running but editor dead+- **Cause**: code-server (Hydrogen's child) isn't up even though `machinectl list` shows running.+- **Fix**: relaunch Hydrogen (it re-ensures code-server on launch), or Restart Workspace.+  Confirm via the resume reality-check — code-server reachable on :7380, not just+  "machine present."++### Lima VM not available at all+- **Cause**: the `hd-builder` Lima VM isn't up / hasn't been provisioned.+- **Fix**: the "Lima VM not available" floaty → "Run Setup Steps" walks the user through+  provisioning it. See [hydrogen-workspace-monitoring](../hydrogen-workspace-monitoring/SKILL.md).++## Recovery Checklist++When the workspace is broken, follow this order:++1. **Check the Lima VM itself**: `limactl list` / `limactl shell hd-builder -- systemctl status`+   (NOT stopping or deleting `hd-builder`).+2. **Check the machine**: `machinectl list` — is `Adom-Workspace` listed and running?+3. **If running but editor dead**: code-server child orphaned → Restart Workspace+   (`machinectl terminate Adom-Workspace` + re-ensure code-server), or relaunch Hydrogen.+4. **Verify code-server**: it must answer on host :7380 (the resume reality-check).+5. **Last resort (destructive)**: `machinectl remove Adom-Workspace` then re-import via+   `setup_and_start` — wipes the machine and re-imports `adom-golden.tar.gz`.+6. **NEVER** reach for a global teardown to "fix" things — stopping/deleting the+   `hd-builder` VM or running global `machinectl` teardown breaks the user's other+   Lima VMs / machines, doesn't help your machine, and angers the user.++## What NOT To Do (learned the hard way)++- Do NOT stop or delete the `hd-builder` Lima VM — global, kills every machine in it,+  breaks the user's other Lima VMs / Docker-for-Mac's VM.+- Do NOT `machinectl terminate` or `machinectl remove` any machine except `Adom-Workspace`.+- Do NOT blanket-kill `machinectl` / nsenter processes — same blast radius as tearing+  down the VM.+- Do NOT use `docker exec` / `docker restart` — there is no container in this runtime.+- Do NOT trust "machine present" as healthy — require code-server reachable on :7380.+- Do NOT treat `[docker] … unhealthy` in the log as a real failure under the machine+  runtime — it's benign; the real signal is `[macos] Workspace unhealthy …`.++## Related skills++- [hydrogen-workspace-monitoring](../hydrogen-workspace-monitoring/SKILL.md) — the 15s poll, floaty states, lifecycle dialog, auto-start+- [hydrogen-container-stats](../hydrogen-container-stats/SKILL.md) — title-bar CPU/RAM bars + `workspace_stats()` probe+- [hydrogen-networking](../hydrogen-networking/SKILL.md) — port 7380 code-server, control port 47084, host reachability via the `adom-host` gateway
skills/hydrogen-workspace-monitoring/._SKILL.mdadded
⋯ 1 unchanged line ⋯
skills/hydrogen-workspace-monitoring/SKILL.mdadded+153
@@ -0,0 +1,153 @@+---+name: hydrogen-workspace-monitoring+description: >+  How Adom Hydrogen monitors and manages the local workspace machine+  (`Adom-Workspace`) and code-server. Covers the 15s workspace state poll, the+  two health levels (Lima VM up, machine running + code-server reachable), the+  floaty states (Lima VM not available, machine stopped), auto-start via setup_and_start,+  auto-reload of the VS Code iframe, and the lifecycle dialog system. Read BEFORE+  touching PanelVisualStudioCode.svelte, ContainerLifecycleDialog.svelte, or any+  workspace state handling code. Trigger words: workspace stopped, lima vm not available,+  workspace monitoring, machine poll, workspace floaty, start workspace, code-server+  reachable, lifecycle dialog, workspace state, Adom-Workspace running.+---++# Hydrogen Workspace Monitoring++> This is the machine-runtime version (default). The legacy Docker equivalent+> (`HD_RUNTIME=docker`) is in the docker/ bucket.++## Architecture++Under the machine runtime the workspace is the **`Adom-Workspace` machine** (a+systemd-nspawn machine imported via `machinectl import-tar`), not a Docker container.+Hydrogen monitors workspace health at two levels:++1. **Lima VM up** — is the `hd-builder` Lima VM itself up and responding on this Mac?+2. **Workspace healthy** — is `Adom-Workspace` present AND running AND is+   code-server reachable on host port **7380**?++The second level is a real reachability check, not just "is the machine present."+A resume reality-check (after sleep, or after an Hydrogen rebuild/relaunch) requires+code-server to actually answer before Hydrogen trusts the workspace as healthy — a machine+can be running while code-server is dead.++The VS Code panel (`PanelVisualStudioCode.svelte`) polls workspace state every 15s+and shows appropriate UI for each state. These frontend components survive unchanged+from the Docker era; they now front the machine runtime via the runtime trait.++## Workspace State Poll++`PanelVisualStudioCode.svelte` calls `get_container_status` (Tauri command) every 15s.+The command dispatches through the runtime trait to the machine impl. Returns a status+struct shaped like:+```rust+pub struct ContainerStatus {+    pub docker_available: bool,    // under the machine runtime: is the Lima VM hd-builder up?+    pub image_pulled: bool,        // is the Adom-Workspace tarball/machine present?+    pub container_exists: bool,    // is the machine present (running or stopped)?+    pub container_running: bool,   // is it running AND code-server reachable on :7380?+    pub container_id: Option<String>,+    pub error: Option<String>,+}+```+The field names are inherited from the Docker era; under the machine runtime they map+onto the two health levels above (Lima VM availability, then machine-present →+machine-running + code-server-reachable).++## Floaty States++All floaties appear INSIDE the VS Code panel only — not as full-app modals.+They are draggable (grab header to move). The rest of the app (wiki tab, menu, etc.)+remains fully interactive. The old Docker floaties ("Docker Desktop not installed/+running") become Lima-VM / machine states under this runtime.++### 1. Lima VM Not Available (red dot)+- **When**: the `hd-builder` Lima VM itself is not up / not responding (level-1 health fails)+- **Message**: "The Hydrogen utility VM (hd-builder) is required but not available."+- **Action**: "Run Setup Steps" button → dispatches `hd-show-setup` event (which walks+  the user through provisioning the Lima VM)+- **Color**: Red dot in header++### 2. Workspace Stopped / Not Ready (red dot)+- **When**: the Lima VM is up but `Adom-Workspace` isn't running, or it's running but+  code-server isn't reachable on :7380 (`container_running == false`)+- **Message**: "The Adom workspace is not running."+- **Action**: "Start Workspace" button → calls `setup_and_start` Tauri command+- **Lifecycle dialog**: Shows the draggable lifecycle dialog with live setup output+- **Auto-reload**: After the machine + code-server come up, waits for code-server+  health, then reloads the VS Code iframe++There is no separate "daemon not running" middle state like Docker had — the machine+runtime has no always-on daemon to launch. Either the Lima VM is up or it isn't+(state 1), and the workspace is either healthy or it isn't (state 2).++## Auto-Start via `setup_and_start`++The "Start Workspace" path calls Tauri `setup_and_start`, which (in machine mode)+delegates to `MacosVmRuntime::setup_and_start`:+- If the machine isn't present yet, `fresh_import` = download the golden+  `adom-golden.tar.gz` (from the `adom-inc/hd-lima-image` release) + `machinectl+  import-tar Adom-Workspace` + start code-server.+- If the machine is present but code-server is down, it just (re-)starts code-server.++Importantly, code-server runs as an **HD-owned child process** (not a detached+`nohup`) — it dies with Hydrogen. So Hydrogen also **re-ensures code-server on every launch**+(look for `[macos-startup] re-ensured code-server on launch` in the log). Without that,+a freshly relaunched Hydrogen would show the machine running but the editor stuck on+"Starting AI Environment" forever, because the previous Hydrogen's code-server child was+orphaned. See [hydrogen-workspace-lifecycle](../hydrogen-workspace-lifecycle/SKILL.md) for the+full lifecycle detail.++## Lifecycle Dialog++`ContainerLifecycleDialog.svelte` — a draggable floating card (no background blur)+that shows live command output for start/stop/restart operations on the machine.++### Events+Listens for `hd-container-lifecycle` CustomEvents on `window`:+- `action: 'restart' | 'stop' | 'start'` — opens dialog, starts spinner+- `action: 'step', step: string` — appends a log line+- `action: 'done'` — stops spinner, shows Close button, auto-reloads VS Code+- `action: 'stopped'` — stops spinner (for stop-only operations)+- `action: 'error', message: string` — shows error state++### Title+Dynamic based on `actionType`:+- Restarting: "Workspace Restarting..." → "Restart Complete"+- Stopping: "Stopping Workspace..." → "Workspace Stopped"+- Starting: "Starting Workspace..." → "Workspace Started"+- On error: "Restart/Stop/Start Failed"++### VS Code Auto-Reload+After the `done` event, the dialog polls code-server health (`/proxy/8080/`) every 5s+for up to 2 minutes. When healthy, it reloads all iframes whose src points at+`localhost:7380` (the host-side code-server port), then shows "VS Code iframe reloaded"+and stops.++## Tauri Commands++| Command | What it does (machine mode) |+|---|---|+| `get_container_status` | Returns workspace status — Lima VM up, machine present, running + code-server reachable |+| `setup_and_start` | `MacosVmRuntime::setup_and_start` — `fresh_import` (import machine) + start code-server, up to 10 min timeout |+| `stop_container` | Stops the workspace (`machinectl terminate Adom-Workspace`) |+| `toggle_console` | Show/hide the debug console window |+| `is_console_visible` | Returns true if console window exists |++There is no `launch_docker_desktop` equivalent under the machine runtime — there's no daemon to start.++## Menu Items (Adom Menu → Workspace Section)+- **Restart Workspace** — `machinectl terminate Adom-Workspace` + re-ensure code-server; emits lifecycle events, shows dialog+- **Stop Workspace** — terminates the machine; emits lifecycle events, shows dialog, triggers stopped floaty+- **Start Workspace** — available when stopped (from floaty button or menu)++## Settings (Desktop section in Settings dialog)+- `desktop.show_console_on_startup` — boolean, default false+- `desktop.launch_on_boot` — boolean, default true (auto-start Hydrogen when the Mac logs in)++## Related skills++- [hydrogen-workspace-lifecycle](../hydrogen-workspace-lifecycle/SKILL.md) — import/export/remove/terminate, the never-do-a-global-teardown rule, control endpoints+- [hydrogen-container-stats](../hydrogen-container-stats/SKILL.md) — the title-bar CPU/RAM bars and `workspace_stats()` probe+- [hydrogen-networking](../hydrogen-networking/SKILL.md) — port 7380 code-server, control port 47084