bootstrap
Adom Hydrogen (Windows / WSL2)
Public Made by Adomby adom
The Windows (WSL2) platform layer for Hydrogen.
← Commit history
Publish 0.3.0
15 files changed
+1862−23
README.md+12−12install.sh+4−4package.json+5−4page.json+4−3skills/hydrogen-container-stats/SKILL.md+154skills/hydrogen-container/SKILL.md+132skills/hydrogen-networking/SKILL.md+175skills/hydrogen-port-watcher/SKILL.md+284skills/hydrogen-ports/SKILL.md+223skills/hydrogen-setup-steps/SKILL.md+106skills/hydrogen-setup/SKILL.md+199skills/hydrogen-topology/SKILL.md+132skills/hydrogen-volume/SKILL.md+115skills/hydrogen-workspace-lifecycle/SKILL.md+165skills/hydrogen-workspace-monitoring/SKILL.md+152README.md+12−12@@ -1,30 +1,30 @@-# adom/hd-windows-bootstrap+# adom/hydrogen-windows-bootstrap The **Windows (WSL2)** platform layer for Hydrogen Desktop. On Windows, HD's workspace runs inside a WSL2 Ubuntu distro; this package adds everything-specific to that runtime on top of the platform-generic `adom/hd-bootstrap`.+specific to that runtime on top of the platform-generic `adom/hydrogen-bootstrap`. ```-adompkg install adom/hd-windows-bootstrap # the golden WSL2 image installs this+adompkg install adom/hydrogen-windows-bootstrap # the golden WSL2 image installs this ``` ## The platform-layered bootstrap stack ``` adom/core ← Adom ecosystem (generic; shared with web Hydrogen)- └─ adom/hd-bootstrap ← Hydrogen Desktop, platform-GENERIC- ├─ adom/hd-windows-bootstrap ← THIS — WSL2 runtime+ └─ adom/hydrogen-bootstrap ← Hydrogen Desktop, platform-GENERIC+ ├─ adom/hydrogen-windows-bootstrap ← THIS — WSL2 runtime ├─ adom/hd-mac-bootstrap ← (Kyle) └─ adom/hd-ubuntu-bootstrap ← (Barrett) ``` -Each platform layer depends on `adom/hd-bootstrap`, which depends on+Each platform layer depends on `adom/hydrogen-bootstrap`, which depends on `adom/core`. Colby's `hydrogen-web` bootstrap depends on `adom/core` too, so core stays generic across web + desktop Hydrogen. ## What this layer adds -- **Dependencies:** `adom/hd-bootstrap` (generic HD) + `~~adom/adom-workspace-updater~~ (RETIRED 2026-07-16, dep removed in 0.2.3)`+- **Dependencies:** `adom/hydrogen-bootstrap` (generic HD) + `~~adom/adom-workspace-updater~~ (RETIRED 2026-07-16, dep removed in 0.2.3)` (the in-distro systemd updater daemon — WSL2/systemd-specific). - **Bundled skills:** the WSL2-runtime `hd-*` skills (container, networking, ports, port-watcher, setup, setup-steps, topology, volume, workspace-lifecycle,@@ -38,14 +38,14 @@ core stays generic across web + desktop Hydrogen. All 46 `hd-*` skills were read and classified by what's platform-generic vs Windows-specific (not the old `shared`/`wsl2` buckets): -- **Generic skills** (31) live in `adom/hd-bootstrap`.+- **Generic skills** (31) live in `adom/hydrogen-bootstrap`. - **Clean Windows skills** (15) live here — e.g. `hd-browser-picker` (WebView2),- `hd-recording` (WGC/DXGI), `hd-runtime-mode`, `hd-networking`, `hd-ports`,- `hd-workspace-lifecycle`, the golden-image/WSL2 skills.+ `hd-recording` (WGC/DXGI), `hd-runtime-mode`, `hydrogen-networking`, `hydrogen-ports`,+ `hydrogen-workspace-lifecycle`, the golden-image/WSL2 skills. - **Mixed skills** were split: the generic core stays in `hd-bootstrap`, and the Windows specifics become a `<name>-windows` companion here (13 of them — e.g.- `hd-api-windows` carries the `/wsl/*` endpoint tables, `hd-notifications-windows`- the WinRT/AUMID toast mechanics, `hd-setup-windows` the WSL2 install cascade).+ `hydrogen-api-windows` carries the `/wsl/*` endpoint tables, `hydrogen-notifications-windows`+ the WinRT/AUMID toast mechanics, `hydrogen-setup-windows` the WSL2 install cascade). Distinct names mean both layers coexist with no deploy collision. Still a **collaborative pass** with Kyle (Mac) and Barrett (Ubuntu): anything in
install.sh+4−4@@ -1,17 +1,17 @@ #!/usr/bin/env bash-# postinstall.sh — adom/hd-windows-bootstrap+# postinstall.sh — adom/hydrogen-windows-bootstrap #-# The Windows (WSL2) platform layer. Runs after adom/hd-bootstrap (generic) and+# The Windows (WSL2) platform layer. Runs after adom/hydrogen-bootstrap (generic) and # all deps install. Applies ONLY what is specific to the WSL2 runtime: # - deploy the WSL2/networking/lifecycle hd-* skills (this package's skills/) # - seed the WSL2 code-server workbench.html (trusted domains + activity bar) # The generic editor config (Claude CLI, extensions, settings.json) is in-# adom/hd-bootstrap. (The workspace-updater daemon dep was RETIRED 2026-07-16 —+# adom/hydrogen-bootstrap. (The workspace-updater daemon dep was RETIRED 2026-07-16 — # updates are registry-native: adom-wiki pkg update via adom/hook.) Mac/Ubuntu siblings provide their own platform layer instead. # # Idempotent + live-installable; the golden WSL2 image runs the same install. set -euo pipefail-log() { echo "[hd-windows-bootstrap] $*"; }+log() { echo "[hydrogen-windows-bootstrap] $*"; } HERE="$(cd "$(dirname "$0")" && pwd)" # 0. Deploy the bundled WSL2-specific hd-* skills into ~/.claude/skills/.
package.json+5−4@@ -1,12 +1,13 @@ { "schema_version": 1,- "slug": "hd-windows-bootstrap",+ "slug": "hydrogen-windows-bootstrap", "type": "bootstrap",- "title": "Hydrogen Desktop Bootstrap for Windows (apps + skills)",+ "title": "Adom Hydrogen (Windows / WSL2)", "brief": "The Windows (WSL2) platform layer for Hydrogen Desktop.",- "version": "0.2.9",+ "version": "0.3.0", "description": "The Windows platform layer for Hydrogen Desktop, where the workspace runs in a WSL2 Ubuntu distro. Depends on adom/hd-bootstrap (the platform-generic HD layer, which bundles the generic hd-* skills) and adds everything specific to the WSL2 runtime: the WSL2/networking/lifecycle hd-* skills (BUNDLED here by design, John+Kyle 2026-07-16) and the WSL2 code-server workbench seed. Sibling adom/hd-mac-bootstrap carries the parallel mac skills. This is the package the golden WSL2 image installs.", "org": "adom",+ "platform": "linux", "tags": [ "bootstrap", "hydrogen-desktop",@@ -19,7 +20,7 @@ "install": "./install.sh" }, "dependencies": {- "adom/hd-bootstrap": "^0.2.0",+ "adom/hydrogen-bootstrap": "^0.3.0", "adom/adom-shotlog": "^0.24.0" }, "files": [
page.json+4−3@@ -2,9 +2,9 @@ "schema_version": 1, "type": "bootstrap", "slug": "hydrogen-windows-bootstrap",- "title": "Hydrogen Desktop Bootstrap for Windows (apps + skills)",+ "title": "Adom Hydrogen (Windows / WSL2)", "brief": "The Windows (WSL2) platform layer for Hydrogen Desktop.",- "version": "0.2.9",+ "version": "0.3.0", "author": { "name": "John Lauer", "email": "[email protected]"@@ -28,7 +28,8 @@ "install": "./install.sh" }, "dependencies": {- "adom/hd-bootstrap": "^0.2.0"+ "adom/hydrogen-bootstrap": "^0.3.0",+ "adom/adom-shotlog": "^0.24.0" }, "files": [ "package.json",
skills/hydrogen-container-stats/SKILL.mdadded+154@@ -0,0 +1,154 @@+---+name: hydrogen-container-stats+description: HD'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/distro, 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 HD'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, distro stats, wsl stats.+---++# HD Workspace Stats Indicator++> This is the WSL2-runtime version (default). The legacy Docker equivalent+> (`HD_RUNTIME=docker`) is in the docker/ bucket.++The top-right of HD'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 WSL2 runtime these numbers now reflect the **`Adom-Workspace` distro**, 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 HD'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` → `WslDistroRuntime::get_stats` → `workspace_stats()`, a WSL health/stat probe of the `Adom-Workspace` distro. (Under `HD_RUNTIME=docker` the same command routes to the Docker impl instead.) The probe runs with short timeouts so a hung WSL subsystem 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 distro's available CPU being consumed (1.0 = 1 full vCPU). Capped at 100% display | `workspace_stats()` WSL probe |+| RAM | % of the distro's memory in use vs its limit | `workspace_stats()` WSL 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 distro name) |+| CPU + RAM totals | `CPU: 1.7% of 16 cores | RAM: 0.7 GB / 16.0 GB` |+| Disk | `Disk: 1.3 GB` (distro disk usage) |+| Image | `Image: Ubuntu 24.04 (Adom-Workspace)` (the imported distro) |+| ID | `Adom-Workspace` (the registered distro 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 distro/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` | Distro registered but not running | red |+| `starting` | HD is starting the distro / code-server | yellow |+| `restarting` | HD is restarting the distro | yellow |+| `restart needed` | The distro or code-server is unreachable (usually a WSL2 hiccup after Windows 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 `wsl --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 WSL mode that is `WslDistroRuntime::get_stats` → `workspace_stats()`. NO `docker exec`, NO cgroup file reads, NO bollard `stats()` — those were the Docker-runtime mechanism and do not run under WSL.+- **Two health levels** (`workspace_stats()` checks both): (1) is WSL2 itself available; (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 HD's log even on the WSL2 path:++```+[docker] Container runtime unhealthy — exec failed, needs restart+```++**Under WSL this is BENIGN NOISE.** It's a leftover hd-docker heartbeat with no container bound to it — there is no Docker container in WSL mode, so "exec failed" is expected and meaningless. It does NOT mean your workspace is broken.++The real WSL health signal to watch for is:++```+[wsl] Workspace unhealthy …+```++That line (not the `[docker]` one) reflects the actual `Adom-Workspace` distro state. And the authoritative "which runtime am I on" line is `[app] Active workspace runtime: WslDistro`.++## API access++If you want the same stats data your AI can hit directly via the control API:++```bash+# From inside the distro — query the control API on the host+curl -s "$(cat ~/.adom/hd-control-url)/container-stats"+```++(Reach the control API at `127.0.0.1` — WSL2 mirrored networking shares loopback with the Windows host. The port is **dynamic per launch**; read the live URL from `~/.adom/hd-control-url` 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, gallia, the adom-vscode HTTP API, etc. all run in the distro all the time. ~700 MB baseline is normal.++**"The 'restart needed' badge appeared — what happened?"** Almost always a WSL2 hiccup after Windows wake/sleep, leaving the distro or code-server unreachable. Click Restart Workspace in the Adom menu (top-left). HD will `wsl --terminate Adom-Workspace`, re-ensure code-server, and the badge clears.++**"The log says `[docker] … unhealthy` — is my workspace broken?"** No. Under WSL that line is a benign leftover heartbeat with no container bound. Look at `[wsl] 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 — under WSL there is no separate Docker volume; `/home/adom/project` lives inside the distro, so the `Disk:` figure is the distro'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/unregister of the distro+- [hydrogen-networking](../hydrogen-networking/SKILL.md) — port 7380 code-server, host reachability, control port 47084
skills/hydrogen-container/SKILL.mdadded+132@@ -0,0 +1,132 @@+---+name: hydrogen-container+description: Context for Claude Code running inside a Hydrogen Desktop WSL2 workspace distro. Documents the exact OS (Ubuntu 24.04, code-server 4.112.0, the Adom-Workspace distro), explains how setup differs from Adom cloud containers (setup-steps NOT bootstrap.sh), 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.+---++# Hydrogen Desktop Workspace (WSL2)++WSL2-runtime version (default). Legacy Docker container runtime (`HD_RUNTIME=docker`) is in the docker/ bucket.++You are running inside the **`Adom-Workspace` WSL2 distro** managed by Hydrogen Desktop (HD). HD is the flagship Adom desktop app — it manages your WSL2 workspace, bridges to desktop apps, and provides VS Code + Claude Code.++## The workspace — exact facts (don't guess)++| Field | Value |+|-------|-------|+| Distro name | `Adom-Workspace` (fixed — there is no random container suffix) |+| Imported from | golden image `adom-golden.tar.gz` (full pre-baked image) via `wsl --import` |+| Base | Ubuntu 24.04 |+| Architecture | matches host (`dpkg --print-architecture`) — almost always `linux-x64` on Windows hosts, can be `linux-arm64` on Apple Silicon hosts |+| 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` |+| Distro default user | `adom` (`wsl.conf [user] default=adom`) — in-distro exec via `wsl -d Adom-Workspace -u adom --`; root via `-u root` |+| Work user | `adom` (UID 1001, passwordless sudo, home `/home/adom`) |+| Workspace | `/home/adom/project` (lives directly in the distro'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` (WSL2 auto-forwards 7380 to Windows `localhost:7380`) |+| Runtime source | `hydrogen-desktop/src-tauri/crates/hd-app/src/runtime/wsl.rs` (selector `runtime/mod.rs::select_runtime()`) |++If you ever need to confirm: `cat /etc/os-release`, `uname -m`, `/usr/lib/code-server/bin/code-server --version`. Don't speculate about alpine/arm64 — Ubuntu 24.04 matches the cloud container.++## Setup is via HD's setup-steps, NOT gallia bootstrap.sh++HD provisions this distro through its own **setup-steps** flow (Rust code in `hydrogen-desktop/src-tauri/crates/hd-app/src/setup_steps_wsl.rs`, with the runtime mechanics in `hydrogen-desktop/src-tauri/crates/hd-app/src/runtime/wsl.rs`), shown as the "Install Tools" panel in the HD UI. It is a **different code path** than cloud containers, which run `gallia/scripts/bootstrap.sh`.++The WSL cascade is **28 steps** (state file `setup-steps-wsl.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 (gallia, the 8 Adom CLIs, the Claude Code extension, code-server, VS Code settings, and all `hd-*` skills). The one exception is the `claude` CLI, which is installed at RUNTIME by step 13 `install-claude-cli` (its self-setup needs a live session). The Docker `pull-image` / `create-container` / `start-container` steps are collapsed into a single step 1, `ensure-workspace`, which runs `wsl --import` of `adom-golden.tar.gz` if the distro isn't already registered. The relay is started by the `start-relay` step. See `hd-golden-image`.++When debugging install issues here, the source of truth is `setup_steps_wsl.rs` (and `runtime/wsl.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 distro. There is NO `install-claude-ext` / `install-claude-cli` / `install-gallia` / `write-vscode-settings` step anymore — those are baked. Do NOT chase bugs into `bootstrap.sh` first — that script lives in the baked image but HD itself never invokes it. (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++HD 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 HD's login page.++## Relay server is running++The adom-desktop relay runs on ports 8765 (WebSocket) / 8766 (HTTP) inside this distro. 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 Windows host control API is reachable from inside the distro at `127.0.0.1` — WSL2 mirrored networking shares loopback with the Windows host. The port is dynamic per launch, so read the live URL from `~/.adom/hd-control-url` (`http://127.0.0.1:<dynamic>`): `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": "C:\\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": "C:\\Users\\john\\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": "C:\\Users\\john\\project"}'+```++## What's different from Adom cloud containers++| Feature | Adom Cloud | HD Local (WSL2) |+|---------|-----------|-----------------|+| Workspace provisioning | Adom platform API | `wsl --import` of `adom-golden.tar.gz` on the user's machine |+| 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 HD local mode+- `HD_RUNTIME=wsl2` — indicates the WSL2 runtime (default); `docker` selects the legacy container runtime+- `GALLIA_SERVICE=local` — tells gallia this is a local workspace+- `VSCODE_PROXY_URI=http://localhost:7380/proxy/{{port}}/` — code-server's proxy++## Related skills++- `hydrogen-topology` — the three-tier topology; tier 3 is this WSL2 distro+- `hydrogen-volume` — where your files live, what persists, and how to back up / copy out the distro
skills/hydrogen-networking/SKILL.mdadded+175@@ -0,0 +1,175 @@+---+name: hydrogen-networking+description: >+ Port architecture, hostnames, and networking rules for Hydrogen Desktop.+ MUST READ before adding ports, exposing a service to your Windows host,+ referencing host URLs from inside the workspace, or wiring any service+ communication.+ Trigger words — HD port, HD network, port mapping, proxy, 127.0.0.1, loopback,+ mirrored networking, hd-control-url,+ VSCODE_PROXY_URI, relay URL, code-server proxy, container networking,+ ADOM_CARBON_URL, ADOM_HYDROGEN_URL, direct connect, 8770, 7380.+---++# Hydrogen Desktop — Networking & Port Architecture++> This is the WSL2-runtime version (default). For the legacy Docker container+> runtime (`HD_RUNTIME=docker`) see the docker/ bucket.++## Core Principle: WSL2 auto-forwards `0.0.0.0` listeners++The HD workspace is a WSL2 distro (`Adom-Workspace`), not a Docker container.+WSL2 has a built-in localhost-forwarding feature: **any service that binds+`0.0.0.0:<port>` inside the distro is automatically reachable at+`127.0.0.1:<sameport>` on Windows** — no Docker `-p` mapping, no proxy hop+required. There is no port-mapping table to maintain; 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`.** WSL2 does NOT auto-forward a+ `127.0.0.1`-only listener — Windows can't see it. A distro 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 distro and is therefore reachable at `localhost:7380` on Windows+ automatically. Same for a dev server on `0.0.0.0:5173` → `localhost:5173`.++## In-distro services (no port mapping needed)++| Distro Port | Service | Reachable on Windows at |+|-------------|---------|-------------------------|+| 7380 | code-server (VS Code) | `localhost:7380` (auto-forwarded) |+| 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 Windows listeners)++These ports run on the Windows host inside the HD Tauri process. They are+native TCP listeners on Windows — not inside the distro.++| Port | Service | Notes |+|------|---------|-------|+| 1420 | Dev file server | Dev mode only, serves `build/` |+| 8770 | adom-desktop direct connect | Owned by Adom Desktop, NOT HD |+| 47080 | HD discovery server | |+| 47083 | HD reverse proxy | Proxies to code-server |+| 47084 | HD control API | Health, setup, eval, CDP, `/wsl/status`, `/port-forward` |+| 47085 | WebView2 CDP | Chrome DevTools Protocol |++These are configurable via `%APPDATA%\hydrogen-desktop\ports.json`.++**Reaching a host service FROM the distro:** just use `127.0.0.1:<port>`. WSL2+**mirrored networking** shares the loopback interface between the distro and the+Windows host, so a Windows listener on `127.0.0.1:<port>` is reachable at the same+`127.0.0.1:<port>` from inside the distro — no gateway IP, no firewall rule, no+`0.0.0.0` requirement on the host side. (`host.docker.internal` is a Docker-ism HD+does NOT use under WSL2 — inside the distro it resolves to a *different* interface,+not where HD listens, so curls to it fail.)++## Hostnames — reach the host at `127.0.0.1`++### From inside the distro → host machine+```+127.0.0.1+```+WSL2 mirrored networking shares loopback with the Windows host, so `127.0.0.1`+inside the distro reaches Windows host listeners directly. The distro's+`/etc/hosts` also has `127.0.0.1 adom-host` (the non-Docker alias → loopback), but+you generally don't need it — use `127.0.0.1`. Used for:+- `ADOM_CARBON_URL=http://127.0.0.1:{proxy_port}`+- `ADOM_HYDROGEN_URL=http://127.0.0.1:{proxy_port}`+- Direct connect probe: `http://127.0.0.1:8770/health`+- HD control API: the live URL in `~/.adom/hd-control-url` (`http://127.0.0.1:<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 → distro services (via proxy)+```+http://127.0.0.1:{code_server_port}/proxy/{distro_port}/+ws://127.0.0.1:{code_server_port}/proxy/{distro_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 distro services,+because code-server itself is inside the distro and proxies to them locally.+It is the preferred path for browser-facing HTTP/WS even though direct+auto-forward 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 distro on ports 8765+(WebSocket) and 8766 (HTTP). It binds `0.0.0.0`, so it is reachable both via+WSL2 auto-forward (`localhost:8765` / `localhost:8766` on Windows) and via the+code-server proxy. The proxy path is preferred for connection stability:++1. **adom-desktop on Windows** 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": "HD Local WSL",+ "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 distro, `VSCODE_PROXY_URI` is set by code-server:+```+https://<slug>.adom.cloud/proxy/{{port}}/ (cloud)+http://localhost:7380/proxy/{{port}}/ (HD 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-distro command form++The workspace is a WSL2 distro, so you reach it with `wsl`, NEVER `docker exec`:+```bash+wsl -d Adom-Workspace -u adom -- <cmd>+# or, since the distro boots as root:+wsl -d Adom-Workspace -- su - adom -c '<cmd>'+```++## NEVER Do These++1. **NEVER bind `127.0.0.1`-only** for a service that must be reachable from+ Windows. WSL2 won't auto-forward it. Bind `0.0.0.0`, or register it with the+ port-forward registry (see `hydrogen-port-watcher`).+2. **NEVER use `host.docker.internal` for distro→host** communication — it's a+ Docker-ism HD doesn't use; inside the distro it resolves to the wrong interface.+ Reach the host at `127.0.0.1` (WSL2 mirrored networking shares loopback); read+ HD's live control URL from `~/.adom/hd-control-url`.+3. **NEVER assume you need a firewall rule or gateway IP to reach the host.**+ Under WSL2 mirrored networking the host's `127.0.0.1` listeners are shared with+ the distro's loopback — `127.0.0.1:<port>` just works, no firewall allow needed.+4. **NEVER `docker exec` / `docker cp` against the workspace.** It's a WSL2+ distro; use `wsl -d Adom-Workspace`.+5. **NEVER hardcode port numbers** in distro 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 HD.**+ It's owned by Adom Desktop. HD's direct-api thread binds to 8770+ as a fallback but Adom Desktop takes priority when both are running.
skills/hydrogen-port-watcher/SKILL.mdadded+284@@ -0,0 +1,284 @@+---+name: hydrogen-port-watcher+description: >+ Port reachability in Hydrogen Desktop — and why you do NOT need to set up+ port forwarding. Under WSL2 networkingMode=mirrored (HD's default, which the+ setup cascade hard-targets), the distro SHARES the Windows loopback, so every+ port a service binds in the workspace — including `127.0.0.1`-only OAuth+ callback servers from VS Code extensions (Codex, Copilot, GitLens) — is+ reachable at the same `localhost:<port>` on Windows automatically. No daemon,+ no proxies, no manual mapping. The old port-watcher daemon was a Docker/NAT-era+ mechanism and is not used under mirrored networking. Trigger words: port+ forward, dynamic port, OAuth callback, Codex auth, extension auth, port mapping,+ localhost port, port watcher, port proxy, does port forwarding work, why can't+ I reach my port.+---++# HD Port Watcher — you don't port-watch anymore (mirrored networking does it)++> **TL;DR for the AI: do NOT try to set up port forwarding in HD.** Under the+> default WSL2 `networkingMode=mirrored`, ports just work. If a `localhost:<port>`+> from the workspace isn't reachable on Windows, the fix is almost never "forward+> the port" — it's "confirm mirrored is on" (see below), or the service didn't+> actually bind.++## Why there's nothing to do (mirrored networking)++HD's setup cascade hard-targets `networkingMode=mirrored` in `.wslconfig`. Under+mirrored, the WSL2 distro shares the Windows loopback interface, so **any** listener+in the distro — `0.0.0.0:<port>` AND `127.0.0.1:<port>` — is reachable at the same+`localhost:<port>` on Windows with zero setup. This is why HD's own OAuth host-proxy+is explicitly **skipped** under mirrored (`lib.rs`: *"under WSL2 mirrored it's+redundant… host proxy not needed"*).++## The legacy daemon (Docker / non-mirrored only — historical)++The `port-watcher` daemon below existed for the pre-mirroring world: the Docker/NAT+runtime, where the distro could NOT reach Windows loopback, so a daemon watched+`/proc/net/tcp` for new listeners and HD spun up host-side TCP proxies. It closed+two gaps that DO NOT EXIST under mirrored:++1. **`127.0.0.1`-only listeners.** NAT-mode WSL2 did NOT forward loopback-only+ listeners. VS Code extensions (Codex, Copilot, GitLens) start OAuth callback+ servers that frequently bind `127.0.0.1`; without mirroring the browser redirect+ to `localhost:{port}/auth/callback?code=...` hit a port that didn't exist on Windows.+2. **Auto-forward lag / wedge.** WSL2 localhost-forwarding can lag or break+ even for `0.0.0.0` listeners (a real HD-logged failure mode:+ "code-server alive in distro but Windows can't reach 127.0.0.1:7380"). The+ watcher gives HD a reliable host-side proxy that doesn't depend on WSL2's+ forwarder.++## The Solution++A lightweight daemon inside the distro 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 HD's control API. HD creates a host-side TCP proxy on+that same port, forwarding through code-server's `/proxy/{port}` to reach the+distro. The browser redirect just works — even for loopback-only callback+servers WSL2 wouldn't have forwarded.++## Architecture++```+WSL2 distro (Adom-Workspace): Windows Host:+┌─────────────────────┐ ┌──────────────────────┐+│ Codex creates server │ │ HD Control API │+│ on 127.0.0.1:1455 │ │ (:47084) │+│ (WSL2 won't forward) │ │ │+│ 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 WSL2 cannot auto-forward+- Any new TCP listener that wasn't running when the distro started+- Auto-expires after 5 minutes (OAuth callbacks are fast)++**NOT FORWARDED (known services, stay behind /proxy/{port} or auto-forward):**+- code-server (8080 inside distro; auto-forwarded 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 distro boot time+- Any port ≤ 1023++This keeps the local WSL workspace behaving like cloud Hydrogen — known+services use `/proxy/{port}` (or WSL2 auto-forward 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 distro'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 distro:++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 HD at `127.0.0.1:<control>/port-forward` — WSL2 mirrored+networking shares loopback with the Windows host, so `127.0.0.1` from inside the+distro reaches HD's host listener. The control port is dynamic per launch; the+live control URL is in `~/.adom/hd-control-url`.++## Installation++The watcher is a bash script (`port-watcher.sh`) deployed into the distro+during setup steps. Installation uses `wsl -d Adom-Workspace`, NOT+`docker cp`/`docker exec`:++```bash+# Copy the script into the distro (from Windows host, via the \\wsl$ share or wsl cat)+wsl -d Adom-Workspace -u root -- bash -c 'cat > /usr/local/bin/port-watcher.sh' < port-watcher.sh+wsl -d Adom-Workspace -u root -- chmod +x /usr/local/bin/port-watcher.sh+# Launch detached inside the distro+wsl -d Adom-Workspace -u adom -- bash -c '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, HD 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 auto-forwarded `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 distro 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` (auto-forwarded) or use `/proxy/{port}` instead, consistent with web+Hydrogen.++## Why Both Layers++There are TWO distinct forwarding mechanisms. The `/proc/net/tcp` watcher above+is **Layer 2**. Layer 1 is a completely separate path — the OAuth-callback+**localhost proxy** baked into HD itself — and it's why VS Code extension logins+"just work" without any watcher round-trip.++### Layer 1 — the OAuth-callback localhost proxy (zero-latency, the primary path)++HD intercepts top-level WebView2 navigations via+`setup_frame_navigation_interception()` (`hd-app/src/lib.rs:75`). On every+navigation it runs `extract_oauth_callback_port()` (`lib.rs:384`), 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)` (`lib.rs:411`), which **pre-binds**+`127.0.0.1:<port>` on the Windows host (via `hd_control::reuse_bind_tcp`) and+waits up to ~5 min for one inbound request. When the browser redirects to+`localhost:<port>/callback?code=…`, HD's pre-bound listener accepts it and+forwards the full path into the distro via code-server:+`http://localhost:<code_server_port>/proxy/<port>/<path>` (`lib.rs:454`), then+streams code-server's response back to the browser.++So the callback port is bound the *instant* the OAuth URL is seen leaving the+WebView — before the provider ever redirects — which is why Codex / Copilot+OAuth completes with zero manual setup and zero watcher latency.++**Control / callback endpoints** (HD control API, port 47084):+- `POST /start-oauth-proxy` — manually pre-bind a callback port (same effect as+ the navigation interceptor; used by the manual fallback).+- `GET /oauth/callback` — HD's own built-in callback receiver (used by the+ Claude Code PKCE flow); do not call directly.++**Failure mode the AI may see:** if the proxy binds but code-server can't be+reached, HD returns a styled **502 Bad Gateway** page (dark `#0d1117`+background, teal `#00b8b0` "OAuth callback proxy" heading) reading roughly *"HD+tried to forward this OAuth callback to the container 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 Layer 1 fired but the distro-side proxy hop failed — check the HD log for+`[oauth-proxy]` lines and that code-server is alive.++### Layer 2 — the port watcher (`/proc/net/tcp` daemon, the safety net)++The bash daemon documented above. Safety net for extensions that open OAuth+URLs through mechanisms that bypass top-level WebView navigations, AND the only+thing that surfaces `127.0.0.1`-only callback servers WSL2 silently won't+forward. 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 HD exists).++### Layer 3 — manual++`POST /start-oauth-proxy` or `POST /port-forward` (or+`adom-cli port-forward register`) for cases where both automatic layers miss.++## Port Conflicts++If the host port is already taken by another Windows app, the forwarding+fails silently. The OAuth callback will break. Mitigation: extensions+typically pick a different random port on retry. HD logs the conflict.++## How OAuth Callbacks Work End-to-End++1. Extension (Codex) starts HTTP server on `127.0.0.1:1455` inside the distro+2. port-watcher.sh detects port 1455 (not in boot snapshot, not known) —+ note WSL2 would NOT have auto-forwarded this loopback-only listener+3. Watcher POSTs `{"port":1455,"action":"open"}` to HD+4. HD 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. HD's proxy receives the request, forwards into the distro via code-server+8. Extension receives callback, completes auth+9. Extension closes its server, port 1455 disappears+10. Watcher POSTs `{"port":1455,"action":"close"}`, HD removes proxy++## Debugging++All debugging runs inside the distro via `wsl -d Adom-Workspace -u adom -- ...`,+NEVER `docker exec`:++```bash+# Check what ports the watcher sees+wsl -d Adom-Workspace -u adom -- bash -c "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+wsl -d Adom-Workspace -u adom -- cat /tmp/port-watcher.log++# Check what HD has forwarded (from Windows host)+curl -sf http://127.0.0.1:47084/port-forward++# Manually forward a port (for testing, from Windows host)+curl -sf -X POST http://127.0.0.1:47084/port-forward \+ -H Content-Type:application/json \+ -d '{"port":1455,"action":"open"}'++# Check HD log for port-fwd events+# Look for [port-fwd] entries+```++## Related skills++- `hydrogen-networking` — `0.0.0.0` auto-forward, reaching the host at `127.0.0.1`+ (mirrored networking / 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 auto-forward
skills/hydrogen-ports/SKILL.mdadded+223@@ -0,0 +1,223 @@+---+name: hydrogen-ports+description: How HD does port forwarding between your WSL2 workspace and your Windows machine — WSL2's automatic `0.0.0.0` localhost-forwarding (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 WSL2 won't auto-forward, 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", "auto-forward a port", or "the dynamic port dialog". Trigger words — port forwarding, ports, hd ports, container port, proxy port, code-server proxy, /proxy/, port mappings dialog, port hints, auto forward, expose port, register port, host port, dynamic port, ports.json, PortConfig, port resolver, localhost port not working.+---++# HD Ports — forwarding workspace services to your Windows host++> This is the WSL2-runtime version (default). For the legacy Docker container+> runtime (`HD_RUNTIME=docker`) see the docker/ bucket.++The HD workspace is a WSL2 distro (`Adom-Workspace`), not a Docker container.+There is **no Docker `-p` port mapping** — WSL2 auto-forwards listeners for you.+Any service that binds `0.0.0.0:<port>` inside the distro is reachable at+`localhost:<sameport>` on Windows automatically. code-server itself binds+`0.0.0.0:7380`, so `localhost:7380` on Windows 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 WSL2 auto-forward can't cover+(`127.0.0.1`-only listeners, or when auto-forward is flaky), HD's port-forward+registry creates a real host-side proxy on demand.++## The first rule: bind `0.0.0.0`++| Bind address inside distro | Reachable on Windows? |+|----------------------------|------------------------|+| `0.0.0.0:N` (all interfaces) | YES — `localhost:N` automatically |+| `127.0.0.1:N` (loopback only) | NO — WSL2 won't forward 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`, it auto-forwards — no+registration needed.++## The code-server proxy URL pattern++Any service listening on port N inside the distro 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 distro, so it reaches loopback listeners+that Windows 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-distro reaches everything-outside.** Inside+the distro, services that need to talk to "the host" use `127.0.0.1:<port>` —+WSL2 mirrored networking shares loopback with the Windows host — for HD's own+services (HD's live control URL is in `~/.adom/hd-control-url`), and the proxy+URL for everything else.++## The port-forward registry (for what auto-forward can't cover)++Auto-forward handles the common case. You still need the registry — HD'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). WSL2 won't auto-forward loopback-only+ listeners; the registry creates a real host proxy that reaches them through+ code-server.+- WSL2 localhost-forwarding is **lagging or broken** for a port (a real HD+ failure mode: "code-server alive in distro but Windows can't reach+ 127.0.0.1:7380"). The registry gives a reliable host-side proxy that doesn't+ depend on WSL2's forwarder.++Decision order:+1. **Bind `0.0.0.0`** → auto-forwarded, done.+2. Can't bind `0.0.0.0` (loopback-only), or auto-forward is unreliable →+ **register the port** so HD proxies it.++### Register a port (from inside your workspace)++```bash+# Tell HD: "please bind localhost:5555 on Windows and forward into the distro"+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 Windows 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 HD 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: HD Adom menu → "Port Mappings", or `POST http://127.0.0.1:47084/setup/panel/show`+then navigate to the Ports tab. Shows:++- **Auto-forwarded ports** — `0.0.0.0` listeners WSL2 is mirroring to Windows+- **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.++## HD's host-side port registry++HD reads/writes its host-side port assignments at+`%APPDATA%\hydrogen-desktop\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`: under WSL2 the relay binds+**8765 (WS) / 8766 (HTTP) inside the distro**, reached at the same port on+Windows (mirrored loopback) or via `/proxy/8766/`, and discovered from+`~/.adom/cli-relay-ports.json` inside the distro. 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 |+| HD 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, HD binds an **ephemeral `127.0.0.1:0`** port (the+OS hands back a guaranteed-free one outside any HNS reservation) and persists it+to `ports.json`. It does **not** walk to 47086, 47087, … — HNS reserves+contiguous chunks invisible to netstat, so a walk would march into the next+reserved block. `code_server` is **exempt** and never resolved (it's tied to the+distro's mirrored 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 distro 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 |+|---------------------|------------------------------|-----|+| Distro shell / app | Another distro service | `http://localhost:N` |+| Distro app | The Windows host | `http://127.0.0.1:N` (WSL2 mirrored networking shares loopback with the host — no gateway IP, no firewall rule) |+| Windows browser | A `0.0.0.0`-bound distro service | `http://localhost:N` (auto-forwarded) |+| Windows browser | Any distro service (HTTP/WS) | `http://localhost:7380/proxy/N/` (proxy URL, preferred) |+| Windows browser | A `127.0.0.1`-only distro service | `http://localhost:N` (only after `port-forward register ... external`) |+| Distro | HD's own control API (on Windows host) | `$(cat ~/.adom/hd-control-url)` → `http://127.0.0.1:<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 it auto-forwards to `localhost:5173`.+- OR use the proxy URL: `http://localhost:7380/proxy/5173/`.+- If it IS on `0.0.0.0` but still unreachable, WSL2's forwarder 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 distro, so WSL2 doesn't forward it.+- Register first: `adom-cli port-forward register 5555 --visibility external --reason "oauth callback"`.+- HD auto-detects callback URLs and starts a proxy for them automatically in+ many cases (see HD log for `[oauth] Callback port X detected`), but+ registering explicitly is the reliable path.++**"`localhost:7380` shows nothing / code-server unreachable from Windows"**+- code-server is alive in the distro but WSL2 localhost-forwarding 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 HD to re-establish the forward.++## Related skills++- `hydrogen-networking` — the broader picture: `0.0.0.0` auto-forward, hostnames,+ proxy architecture, relay vs direct connect, firewall rules, NEVER do these+- `hydrogen-port-watcher` — the retained daemon that registers `127.0.0.1`-only+ listeners and covers WSL2 auto-forward gaps+- `hydrogen-container` — what's inside the workspace reachable on which port+- `hydrogen-desktop-sse` — the workspace API which runs on these ports+- `hd-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-setup-steps/SKILL.mdadded+106@@ -0,0 +1,106 @@+---+name: hydrogen-setup-steps+description: What HD's setup steps did to prepare your workspace — the 28-step cascade that imports the Adom-Workspace golden image, injects your Adom session, wires up the relay, walks the Claude auth gate, and then opens 9 named AI-thread conversations. 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, hd setup, claude code extension install, gallia install, hd workspace ready.+---++# HD Setup Steps — what was done to prepare your workspace++WSL2-runtime version (default). The legacy 24-step Docker cascade+(`HD_RUNTIME=docker`) is in the docker/ bucket.++When you first launched Hydrogen Desktop (or after a virgin reset), HD ran a 28-step cascade to turn a blank machine into a fully-tooled Adom workspace — the `Adom-Workspace` WSL2 distro. The heavy tooling (code-server, gallia, the Adom CLIs, the Claude Code VS Code extension, VS Code settings, and all `hd-*` skills) is already BAKED into the golden image; setup imports that image, injects your Adom session, wires up the relay, installs the `claude` CLI at runtime (the one thing NOT baked — its self-setup needs a live session), walks the Claude auth gate, then opens the first conversation + 9 named AI-thread conversations. 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 `hd-golden-image` for the baked-image model.++The cascade is executed by `setup_steps_wsl.rs` (`step_defs_wsl()` / `run_all_wsl()`). `run_all_wsl` is halt-on-failure, resume-not-restart, with per-step 3× auto-retry. In-distro work runs via `wsl -d Adom-Workspace -u adom -- <cmd>` (the distro's DEFAULT user is `adom` per `wsl.conf default=adom`, home `/home/adom`; root via `-u root`). There is NO `docker exec` and NO Docker image anywhere in this flow.++## The 28 steps (in order)++| # | Step | Purpose |+|---|------|---------|+| 1 | ensure-workspace | Install WSL2 if needed (one auto-reboot), `wsl --import Adom-Workspace` from the golden image tarball (full pre-baked image from the `adom-inc/hd-wsl2-image` GitHub release), create the `adom` user, start code-server, verify host loopback 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 | update-packages | GATE: `adom-wiki pkg update` converges the container to the LATEST published packages (skills included) — the golden image is baked once but installed for months. Gates on the ARTIFACT (hd-bootstrap == registry latest + skills deployed), not the raw exit code; re-runs bootstrap postinstalls when an update lands. |+| 4 | 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 |+| 5 | set-env-vars | Set `ADOM_CARBON_URL`, `ADOM_HYDROGEN_URL`, `ADOM_HD_CONTROL_URL`, `VSCODE_PROXY_URI`, `ADOM_DESKTOP_MODE` |+| 6 | inject-api-key | Write the Adom session token into the distro at `/var/run/adom/api-key` (mode 644) so adom-cli works |+| 7 | configure-vscode | settings.json / trusted-domains / activity-bar are BAKED; re-asserts theme + workbench backstops idempotently, and applies the per-session layout via :8821 |+| 8 | ensure-adom-desktop | Verify the Adom Desktop companion app is running on Windows (the relay's desktop bridge) |+| 9 | start-relay | Start the adom-desktop relay inside the workspace (ports 8765 WS / 8766 HTTP) |+| 10 | test-direct-connect | Prove the fast workspace→desktop command path |+| 11 | test-relay | Register the relay with Adom Desktop for file streaming (file transfer + shell exec) |+| 12 | test-adom-cli | GATE: carbon path + hydrogen-proxy reachability via adom-cli (6 retries) |+| 13 | install-claude-cli | Install the Claude Code CLI (`claude`) into the distro via the official installer. NOT baked (its self-setup needs a live session) — HD installs it here at runtime, before sign-in, so the editor's Claude Code + the Welcome/thread conversations have a working `claude`. Idempotent (skips if present). |+| 14 | 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 just before the payoff steps |+| 15 | ensure-sse | GATE: confirm the editor browser SSE session is connected so Welcome's webview-open doesn't 409 |+| 16 | verify-workspace | GATE: battery confirming the proxy holds a real layout |+| 17 | welcome | Open Claude Code, authenticate if needed, and human-type the first prompt ("who am i and what can i do here") |+| 18 | thread-parts-search | Fresh Claude conversation: Adom parts-search app in a webview, find a popular STM32 for a BLDC motor |+| 19 | thread-kicad | Fresh conversation: is KiCad installed? open the symbol library, else offer to install + show a sample board |+| 20 | thread-fusion360 | Fresh conversation: is Fusion 360 installed? open a sample project (sch/brd/3d), else offer to install |+| 21 | thread-altium | Fresh conversation: is Altium installed? open it, else report whether it can be installed |+| 22 | thread-pup | Fresh conversation: intro pup, show the Adom wiki (adom-lbr page) in a pup window |+| 23 | thread-native-browser | Fresh conversation: what the AI can do to drive the user's real Chrome/Edge (logged-in sites) + any extension needed |+| 24 | thread-google | Fresh conversation: install adom-google (its own guided auth), then latest email subject, next cal entry, a gchat DM, a sample PCB BOM sheet |+| 25 | thread-chip-fetcher | Fresh conversation: install chip-fetcher, show it in a webview, search VL53L8CH |+| 26 | thread-tsci | Fresh conversation: adom-tsci builds an RP2040 + USB-C dev board in one prompt, shown in pup |+| 27 | verify-setup | GATE (catch-all): independently re-verifies the whole workspace end-to-end; HALTS naming any artifact that isn't actually there |+| 28 | open-welcome | GATE (final): open welcome.html in HD's right pane; hard-fails if the Welcome tab doesn't appear |++Steps 18–26 (the AI threads) each open a FRESH Claude conversation and human-type a prompt, strictly one at a time. They replaced the old single `eda-discovery` + `instapcb-quote` steps.++### Steps that NO LONGER EXIST (baked into the golden image)++These are NOT setup steps anymore — they are baked at image-build time: `install-gallia`, `install-hd-skills`, `verify-adom-desktop`, `install-claude-ext`, `write-vscode-settings`, `set-trusted-domains`, `clean-layout`. If a user asks "what installed gallia / the Claude extension / my skills / VS Code settings", the answer is: **baked into the golden image at build time, not a setup step.** (Note: `install-claude-cli` is NOT in this list — the `claude` CLI IS a live setup step, since its self-setup needs a live session.) See `hd-golden-image`.++## Common user questions++**"Why is X installed in my workspace?"** → If X is gallia, an Adom 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 `claude` CLI is the exception: it's installed at RUNTIME by step 13 `install-claude-cli`.) The setup steps only import the image, inject your session, wire the relay, and walk Claude auth. The setup panel (HD 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 (WSL2 unavailable / Virtual Machine Platform feature off, or the `adom-golden.tar.gz` download), step 12 (test-adom-cli — carbon/hydrogen-proxy not reachable yet), step 13/14 (claude CLI install / 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-wsl.json`) so they re-run | Always (default on) |+| WSL2 distro (`container`) | PRISTINE `wsl --unregister Adom-Workspace` + reimport — deletes the distro AND everything in it, including `/home/adom/project` | Always (default on); your work in `/home/adom/project` is GONE |+| Distro 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 HD launch (restart_required) | Rarely |+| VS Code state (`vscode_state`) | Queued wipe, flushed at next HD 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 distro reset backs up Claude creds first UNLESS `claude_token` is checked; the `webview_storage` / `vscode_state` wipes are queued and flushed at the next HD launch (restart_required), not immediate.++CRITICAL: the WSL reset ONLY ever runs `wsl --unregister Adom-Workspace` (+ reimport). It never runs global `wsl --shutdown` or `wsl --uninstall` (those would kill the user's other distros, including any Docker Desktop WSL integration), 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 HD's control API at `http://127.0.0.1:47084`.++**DEPRECATED — now REFUSED, do not use:** `POST /wsl/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 `%APPDATA%\hydrogen-desktop\setup-steps-wsl.json` (one entry per step with `status: pending|running|done|failed`, output text, percent complete). HD reads this on launch to decide whether the setup panel auto-opens. (The legacy Docker runtime uses `setup-steps.json`.)++## Related skills++- `hd-golden-image` — the baked-image model: what's pre-installed in `adom-golden.tar.gz` vs. what setup does+- `hydrogen-container` — the WSL2 distro and runtime that setup configures+- `hydrogen-networking` — how the distro reaches the host (`127.0.0.1` via WSL2 mirrored networking; live control URL in `~/.adom/hd-control-url`) and how ports forward+- `hd-browser-picker` — used by step 14 (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 distro and what survives a virgin reset (or doesn't)
skills/hydrogen-setup/SKILL.mdadded+199@@ -0,0 +1,199 @@+---+name: hydrogen-setup+description: >+ HD setup panel: 27 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, 28 steps, wipe, reset workspace, keep auth, test setup.+---++# Hydrogen Desktop — Setup Panel & Virgin Reset++WSL2-runtime version (default). The legacy 24-step Docker cascade+(`HD_RUNTIME=docker`) is in the docker/ bucket.++## Zero-Click Goal++The HD 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, gallia, 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, HD downloads it — never+tell the user to install something manually. See the `hd-golden-image` skill+for the baked-image model.++## The 27 Install Steps++The WSL cascade is executed by `setup_steps_wsl.rs` (`step_defs_wsl()` /+`run_all_wsl()`). `run_all_wsl` is halt-on-failure, resume-not-restart, with+per-step 3× auto-retry. State is tracked in `setup-steps-wsl.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 distro via `wsl --import+Adom-Workspace`.++| # | ID | Label | Rollback? |+|---|---|---|---|+| 1 | ensure-workspace | Install WSL2 if needed (one auto-reboot), import golden distro, create `adom` user, start code-server, verify host loopback (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 Windows | ✗ |+| 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 | install-claude-cli | Install the `claude` CLI into the distro at RUNTIME (NOT baked — its self-setup needs a live session). Idempotent | ✗ |+| 13 | claude-auth | The single human gate; restore creds or drive in-editor Claude.ai sign-in; runs just before payoff | ✓ |+| 14 | ensure-sse | GATE: editor browser SSE session connected so Welcome's webview-open doesn't 409 | ✗ |+| 15 | verify-workspace | GATE: battery confirming the proxy holds a real layout | ✗ |+| 16 | welcome | Opens Claude Code, authenticates if needed, human-types the first prompt | ✗ |+| 17-25 | thread-* (9) | Nine FRESH Claude conversations, one at a time: parts-search, kicad, fusion360, altium, pup, native-browser, google, chip-fetcher, adom-tsci. Replaced the old eda-discovery + instapcb-quote steps | ✗ |+| 26 | verify-setup | GATE (catch-all): independently re-verifies the whole workspace end-to-end; HALTS naming any missing artifact | ✗ |+| 27 | open-welcome | GATE (final): opens welcome.html in HD'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-gallia`, `install-hd-skills`,+`verify-adom-desktop`, `install-claude-ext`,+`write-vscode-settings`, `set-trusted-domains`, `clean-layout`. gallia, the 8+Adom CLIs, the Claude Code VS Code extension (the claude CLI is runtime step 12, NOT baked), code-server, settings, and all+`hd-*` skills are baked into the golden image. See+`hd-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_wsl()`, after+the per-step 3× auto-retry is exhausted). Remaining steps are NOT executed.+`run_all_wsl` 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 28 steps to pending (`setup-steps-wsl.json`) | CLEAN / EXISTS |+| WSL2 distro (`container`) | PRISTINE `wsl --unregister Adom-Workspace` + reimport (ONLY that distro) | GONE / RUNNING / EXISTS |+| Distro 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 HD launch (restart_required) | CLEAN / EXISTS |+| VS Code state (`vscode_state`) | Queued wipe, flushed at next HD 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 distro reset is a PRISTINE `wsl --unregister` + 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 HD launch+(restart_required), NOT immediate; there is no reboot for the wipe.++CRITICAL — the WSL virgin reset only ever touches `Adom-Workspace`. It runs+`wsl --unregister Adom-Workspace` — NEVER global `wsl --shutdown` or+`wsl --uninstall`, which would knock out the user's other distros (e.g. a+pre-existing Docker Desktop integration). 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 distro 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 /wsl/unregister`, `POST /setup/virgin-reset`,+`POST /setup/run-all`, `POST /setup/run-step`.++## Key Steps Explained++### Step 1 — ensure-workspace+The big one. Installs WSL2 if needed (one auto-reboot), then `wsl --import+Adom-Workspace` from the full pre-baked golden image `adom-golden.tar.gz`+(pulled from the `adom-inc/hd-wsl2-image` GitHub release — NOT a thin ~30 MB+tarball, NOT a 390 MB Docker image), creates the `adom` user, starts+code-server, and verifies host loopback on port 7380. This single step+replaces the old Docker pull-image / create-container / start-container trio.+The distro's DEFAULT user is `adom` (`wsl.conf default=adom`), NOT root;+in-distro exec = `wsl -d Adom-Workspace -u adom --` (root via `-u root`) —+NEVER `docker exec`.++### Step 5 — inject-api-key+Writes the Adom session token into the distro 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 WSL gateway IP 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 distro+(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 HD's right pane and HARD-FAILS+if the Welcome tab doesn't appear.++> Note: gallia, 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 `hd-golden-image`.++## Distro Recreation++When the distro is unregistered and re-imported (step 1), the new distro is a+fully pre-baked Ubuntu from `adom-golden.tar.gz`. WSL2 auto-forwards+code-server's port 7380 to Windows `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+132@@ -0,0 +1,132 @@+---+name: hydrogen-topology+description: >+ MUST READ before ANY HD work. Explains the three-layer topology: cloud+ Docker (where Claude runs), Windows machine (where HD + Adom Desktop run),+ and the HD local WSL2 workspace (the Adom-Workspace distro). Every command+ you run goes through the relay to Windows. You CANNOT directly access the+ WSL2 distro — you must shell through Windows. Trigger words — HD topology,+ cloud vs local, wsl exec, where am I, which workspace, adom-desktop+ relay, test from container, three tiers, architecture, windows machine.+---++# Hydrogen Desktop — Topology (READ FIRST)++WSL2-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+Windows machine 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 Windows via relay+- This container has NOTHING to do with HD's local WSL2 workspace+- Restarting the WSL2 distro on Windows does NOT affect you+- You can freely `git push`, edit code, run builds here++### 2. Windows Machine (user's desktop)+- **Adom Desktop** — relay client app, listens on port 8770 (direct connect)+- **Hydrogen Desktop** — the Tauri app you're building+- **WSL2** — hosts the `Adom-Workspace` distro+- You reach this via `adom-desktop shell_execute '{"command":"..."}'`+- Commands run in `cmd.exe` on Windows++### 3. HD Local WSL2 Workspace (the `Adom-Workspace` distro)+- Distro 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":"wsl -d Adom-Workspace -u adom -- <cmd>"}'`+- This is TWO hops: cloud → relay → Windows → wsl exec → distro++## Command Patterns++### Run something on Windows+```bash+adom-desktop shell_execute '{"command":"dir C:\\Github"}'+```++### Run something in the HD local WSL2 workspace+```bash+adom-desktop shell_execute '{"command":"wsl -d Adom-Workspace -u adom -- ls /home/adom"}'+```++### Test adom-desktop CLI inside the WSL2 workspace+```bash+adom-desktop shell_execute '{"command":"wsl -d Adom-Workspace -u adom -- adom-desktop ping"}'+```+This tests the FULL chain: distro → local relay → Adom Desktop on Windows.++### Screenshot HD window+```bash+adom-desktop desktop_screenshot_window '{"hwnd":HWND}'+```++### Pull file from Windows to cloud+```bash+adom-desktop pull_file '{"filePaths":["C:\\path\\file"],"saveTo":"/tmp"}'+```++## The WSL2 distro ≠ Your Container++- If the `Adom-Workspace` distro on Windows goes down, YOUR cloud container is fine+- You can still talk to Adom Desktop, screenshot windows, run shell commands+- You just can't `wsl -d Adom-Workspace ...` into the workspace until it's back+- Starting/stopping the distro on Windows is done via shell_execute. Only ever+ touch `Adom-Workspace` (`wsl --terminate Adom-Workspace`) — NEVER a global+ `wsl --shutdown`, which would knock out any other WSL distro the user has+ (e.g. a Docker Desktop integration).++## The Relay Chain++```+Cloud adom-desktop CLI+ → WS relay (cloud :8765)+ → internet+ → Adom Desktop on Windows (relay client)+ → executes command locally+ → returns result+ → back through relay to your CLI+```++For the HD local WSL2 workspace, there's a SECOND relay:+```+Workspace's adom-desktop CLI+ → local relay (distro :8765)+ → code-server proxy (:7380/proxy/8765/)+ → Adom Desktop on Windows (connected as WS client)+ → executes command+ → returns through same chain+```++## Critical Setup Steps for the HD Local WSL2 Workspace++The HD install flow MUST verify this chain works:++1. **Verify Adom Desktop running on Windows** — 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** — `wsl -d Adom-Workspace -u adom -- adom-desktop ping`+ Must return `pong` — proves the full chain works++## NEVER Do These++1. **NEVER confuse your cloud Docker with the HD local WSL2 workspace.**+ `curl http://127.0.0.1:8766/health` hits YOUR relay, not the workspace's.+2. **NEVER try to `wsl -d Adom-Workspace` from your shell.**+ The distro lives on Windows WSL2, not here.+3. **NEVER assume the workspace's status affects your container.**+ They're completely independent.+4. **NEVER run a global `wsl --shutdown` or `wsl --unregister` of anything+ other than `Adom-Workspace`.** Those hit every distro on the user's machine.+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` distro you run inside+- `hydrogen-volume` — where your files live and how to back up / copy out the distro
skills/hydrogen-volume/SKILL.mdadded+115@@ -0,0 +1,115 @@+---+name: hydrogen-volume+description: How HD's WSL2 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 Windows 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 — wsl filesystem, hd volume, where are my files, workspace files, persistent storage, lost my work, /home/adom/project, distro filesystem, wsl export, copy file out of workspace, where is my code, workspace backup, wsl unregister.+---++# HD Workspace Storage — what persists, what doesn't++WSL2-runtime version (default). Legacy Docker container runtime (`HD_RUNTIME=docker`) is in the docker/ bucket.++Your work lives in the `Adom-Workspace` WSL2 distro at `/home/adom/project`. There is **no Docker named volume** — `/home/adom/project` sits directly in the distro's own ext4 filesystem. ALL your work belongs under that path. It persists for as long as the distro stays registered with WSL.++## The filesystem++| Property | Value |+|----------|-------|+| Distro name | `Adom-Workspace` (fixed) |+| Your work path | `/home/adom/project` (plain directory in the distro's ext4 fs) |+| Backing store | The distro's virtual disk (`ext4.vhdx`) inside the WSL2 LxssManager store on Windows |+| On-disk location (Windows) | Under `%LOCALAPPDATA%\...\Adom-Workspace\` — but you don't poke at the vhdx directly; use `\\wsl$\Adom-Workspace\...` (see below) |+| Persistence | Survives workspace restart, HD restart, and Windows reboot. Persists as long as the distro is **registered** with WSL. |++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 unregistering the distro.++## What lives WHERE++| Path inside the distro | Persists across a tarball re-import? | Notes |+|------------------------|---------------------------------------|-------|+| `/home/adom/project/` | **Yes** — HD 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 distro — 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 distro — 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 | HD 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 (HD setup panel), the checkbox toggles control which artifacts get deleted:++| Toggle ON means: | Result |+|------------------|--------|+| Install step state | `setup-steps-wsl.json` deleted; next setup re-runs everything. Your project files unaffected. |+| WSL distro | **`wsl --unregister Adom-Workspace`. This wipes the ENTIRE distro 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 distro (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 "WSL distro" (the unregister).** 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 runs a global `wsl --shutdown` or `wsl --uninstall`, which would hit every other WSL distro on the machine.++## Accessing workspace files from Windows++Under WSL2 you **can** browse the distro's filesystem directly from Windows Explorer — a real advantage over the legacy Docker named-volume runtime. There is no `docker cp`.++### 1. Windows Explorer via the `\\wsl$` share+Open Explorer and go to:+```+\\wsl$\Adom-Workspace\home\adom\project\+```+(or the newer form `\\wsl.localhost\Adom-Workspace\home\adom\project\`).+You can drag files in and out, double-click to open in Windows apps, etc. The distro must be running (it auto-starts when you access the share).++### 2. From inside the distro, reach Windows drives at `/mnt/c/...`+```bash+# Copy a workspace file out to the Windows Downloads folder+cp /home/adom/project/myfile.txt /mnt/c/Users/john/Downloads/++# Copy a Windows file into the workspace+cp /mnt/c/Users/john/Downloads/myfile.txt /home/adom/project/+```++### 3. `adom-desktop send_files` / `pull_file` (from inside the workspace, via the relay)+```bash+# Send a workspace file to your Windows desktop's Downloads folder+adom-desktop send_files '{"filePaths":["/home/adom/project/myfile.txt"],"targetApp":"general","destinationFolder":"."}'++# Pull a Windows file into the workspace's /tmp+adom-desktop pull_file '{"filePaths":["C:\\Users\\john\\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 distro fs directly.++## Backing up your workspace++Persistence and backup for WSL2 is `wsl --export` / `wsl --import` — NOT a Docker volume tar. Before a destructive operation (the WSL-distro virgin-reset/unregister):++```cmd+:: Export the whole distro to a tarball on your Windows host+wsl --export Adom-Workspace C:\Users\john\Downloads\adom-workspace-backup.tar++:: Restore it later (re-register the distro from the backup)+wsl --import Adom-Workspace C:\WSL\Adom-Workspace C:\Users\john\Downloads\adom-workspace-backup.tar+```++This is exactly the mechanism HD's own tarball-migration uses: on a tarball version bump it `wsl --export`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 `\\wsl$\Adom-Workspace\...` or `/mnt/c/...`.++## Why a distro filesystem (no named volume, no bind mount)?++HD keeps your work directly inside the `Adom-Workspace` ext4 filesystem rather than a Docker named volume or a bind mount of a Windows path. This:+- Gives native Linux ext4 I/O — much faster than crossing the Windows ↔ WSL2 filesystem boundary+- Avoids line-ending and permission issues that bind mounts of Windows paths often have+- Lets HD wipe + re-import the base image (on a tarball bump) without losing your code — it `wsl --export`s `/home/adom` first++The bonus over the legacy Docker runtime: you **can** open the files directly in Windows Explorer at `\\wsl$\Adom-Workspace\...`.++## Related skills++- `hydrogen-container` — the `Adom-Workspace` distro whose filesystem holds your work+- `hydrogen-topology` — the three-tier topology; tier 3 is this WSL2 distro+- `hd-golden-image` — what's baked into `adom-golden.tar.gz` (extensions, settings, skills) vs. what setup does
skills/hydrogen-workspace-lifecycle/SKILL.mdadded+165@@ -0,0 +1,165 @@+---+name: hydrogen-workspace-lifecycle+description: WSL2 distro (`Adom-Workspace`) lifecycle management for Hydrogen Desktop on Windows. Import/export/unregister/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 global WSL operations; touch ONLY Adom-Workspace so a co-installed Docker Desktop WSL integration stays safe. MUST READ before any workspace operation. Trigger words — wsl, workspace, distro, Adom-Workspace, restart workspace, terminate distro, unregister, wsl --import, wsl --unregister, code-server, workspace broken, workspace unhealthy, wsl hung, fix workspace, /wsl/status.+---++# Hydrogen Desktop — WSL2 Workspace Lifecycle++> This is the WSL2-runtime version (default). The legacy Docker equivalent+> (`HD_RUNTIME=docker`) is in the docker/ bucket.++Under the WSL2 runtime the workspace is the **`Adom-Workspace` distro** (Ubuntu,+imported with `wsl --import`), running locally on the user's Windows machine.+`/home/adom/project` lives inside the distro — there is no Docker volume.++## ABSOLUTE RULE — NEVER touch global WSL state++**Operate ONLY on `Adom-Workspace`. Never run any global / cross-distro WSL command.**++Forbidden, no exceptions:+- ❌ `wsl --shutdown` (kills the entire WSL utility VM and every distro)+- ❌ `wsl --terminate` on any distro other than `Adom-Workspace`+- ❌ blanket killing of `wsl.exe` processes+- ❌ restarting the WSL service / utility VM+- ❌ `wsl --unregister` on anything but `Adom-Workspace`++**Why this matters:** the user may already run Docker Desktop, which cross-mounts a+proxy into every distro via the shared `/mnt/wsl` namespace+(`/mnt/wsl/docker-desktop/docker-desktop-user-distro`). Any global WSL teardown makes+that path vanish and pops a "Docker Desktop - Ubuntu: WSL integration unexpectedly+stopped" dialog. It's harmless to HD's own `Adom-Workspace`, but it looks to the user+like HD broke their Docker — terrible first-run UX. So every HD operation must be+scoped to `Adom-Workspace` and nothing else.++Allowed, scoped operations:+- ✅ `wsl --terminate Adom-Workspace` (this is what "Restart" does)+- ✅ `wsl --unregister Adom-Workspace` (destructive wipe, this distro only)+- ✅ `wsl --import Adom-Workspace <dir> <tarball>` (fresh import)+- ✅ `wsl -d Adom-Workspace -u adom -- <cmd>` (exec inside the distro)++## In-distro exec — NEVER `docker exec`++To run a command inside the workspace:+```bash+wsl -d Adom-Workspace -u adom -- <cmd>+# or+wsl -d Adom-Workspace -u adom -- su - adom -c '...'+```+There is no `docker exec` under this runtime, and no Docker container to exec into.++## Workspace Lifecycle++### Import (fresh setup)+`setup_and_start` → `WslDistroRuntime::setup_and_start` → `fresh_import`:+download `adom-golden.tar.gz` (the full pre-baked golden image, downloaded+anonymously from the `adom-inc/hd-wsl2-image` GitHub release, SHA256-verified)+then `wsl --import Adom-Workspace <dir> adom-golden.tar.gz`, then start code-server.+Log proof: `[wsl-tarball] downloaded … SHA256 OK` → `[setup] WSL setup_and_start completed`+and `wsl -l -v` shows `Adom-Workspace Running 2`.++### code-server is an HD-OWNED CHILD process+code-server (`CODE_SERVER_CHILD` in `runtime/wsl.rs`) runs as a child of HD, deliberately+tied to HD's lifetime — **NOT a detached `nohup`** (a detached process dies when WSL+tears down the session anyway). Consequence: every HD restart (rebuild / crash /+relaunch) orphans the old code-server, so a fresh HD can come up with the distro+`Running` but the editor DEAD ("Starting AI Environment" forever) even when setup is+otherwise complete.++Fix (already in HD): HD **re-ensures code-server on every launch**+(`[wsl-startup] re-ensured code-server on launch`). And the resume reality-check in+`run_all_wsl` requires code-server reachable on the host port **7380** — not just+"distro registered" — before it trusts the workspace as ready.++### Restart+"Restart Workspace" = `wsl --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+`wsl --terminate Adom-Workspace`. The distro stays registered (and your project files+persist); it just isn't Running.++### Export / backup+`wsl --export Adom-Workspace <file>.tar` snapshots the distro to a tarball. Re-import+with `wsl --import Adom-Workspace <dir> <file>.tar`. (Scoped to this distro, so safe.)++### Unregister (DESTRUCTIVE wipe)+`wsl --unregister Adom-Workspace` — deletes the distro AND everything in it, including+`/home/adom/project`. This is the only destructive wipe, and it names ONLY+`Adom-Workspace`. Never unregister any other distro.++## Control API++The HD 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 distro, reach the host at the same `127.0.0.1` URL: WSL2 mirrored+networking shares loopback with the Windows host.++| Endpoint | What it does |+|---|---|+| `GET /wsl/status` | Reports `Adom-Workspace` state — registered? Running? code-server reachable on :7380? |+| `POST /wsl/unregister` | Destructive: `wsl --unregister Adom-Workspace` (this distro 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 distro)+curl -s "$BASE/wsl/status"++# Destructive wipe of ONLY Adom-Workspace+curl -s -X POST "$BASE/wsl/unregister"+```++## Diagnosing Workspace Problems++### "Workspace unhealthy" / editor stuck on "Starting AI Environment"+- **Cause**: code-server child was orphaned by an HD restart, or the distro went+ unreachable after sleep.+- **Check**: `[wsl] Workspace unhealthy …` in the HD log (the real signal). NOTE: a+ `[docker] Container runtime unhealthy …` line under WSL is **benign noise** — a+ leftover heartbeat with no container bound. Ignore it; look at the `[wsl]` line.+- **Fix**: Restart Workspace (Adom menu) = `wsl --terminate Adom-Workspace` + re-ensure+ code-server. Verify code-server answers on :7380.++### Distro `Running` but editor dead+- **Cause**: code-server (HD's child) isn't up even though `wsl -l -v` shows Running.+- **Fix**: relaunch HD (it re-ensures code-server on launch), or Restart Workspace.+ Confirm via the resume reality-check — code-server reachable on :7380, not just+ "distro registered."++### WSL2 not available at all+- **Cause**: WSL2 isn't installed/enabled on the machine.+- **Fix**: the "WSL2 Not Available" floaty → "Run Setup Steps" walks the user through+ enabling it. See [hydrogen-workspace-monitoring](../hydrogen-workspace-monitoring/SKILL.md).++## Recovery Checklist++When the workspace is broken, follow this order:++1. **Check WSL2 itself**: `wsl --status` (NOT `wsl --shutdown`).+2. **Check the distro**: `wsl -l -v` — is `Adom-Workspace` listed and Running?+3. **If Running but editor dead**: code-server child orphaned → Restart Workspace+ (`wsl --terminate Adom-Workspace` + re-ensure code-server), or relaunch HD.+4. **Verify code-server**: it must answer on host :7380 (the resume reality-check).+5. **Last resort (destructive)**: `wsl --unregister Adom-Workspace` then re-import via+ `setup_and_start` — wipes the distro and re-imports `adom-golden.tar.gz`.+6. **NEVER** reach for global WSL ops to "fix" things — that breaks co-installed Docker+ Desktop, doesn't help your distro, and angers the user.++## What NOT To Do (learned the hard way)++- Do NOT run `wsl --shutdown` — global, kills every distro, breaks Docker Desktop's+ WSL integration.+- Do NOT `wsl --terminate` or `wsl --unregister` any distro except `Adom-Workspace`.+- Do NOT blanket-kill `wsl.exe` — same blast radius as `--shutdown`.+- Do NOT use `docker exec` / `docker restart` — there is no container in this runtime.+- Do NOT trust "distro registered" as healthy — require code-server reachable on :7380.+- Do NOT treat `[docker] … unhealthy` in the log as a real failure under WSL — it's+ benign; the real signal is `[wsl] 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
skills/hydrogen-workspace-monitoring/SKILL.mdadded+152@@ -0,0 +1,152 @@+---+name: hydrogen-workspace-monitoring+description: >+ How Hydrogen Desktop monitors and manages the local WSL2 workspace distro+ (`Adom-Workspace`) and code-server. Covers the 15s workspace state poll, the+ two health levels (WSL2 available, distro Running + code-server reachable), the+ floaty states (WSL not available, distro 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, wsl not available,+ workspace monitoring, distro poll, workspace floaty, start workspace, code-server+ reachable, lifecycle dialog, workspace state, Adom-Workspace running.+---++# HD Workspace Monitoring++> This is the WSL2-runtime version (default). The legacy Docker equivalent+> (`HD_RUNTIME=docker`) is in the docker/ bucket.++## Architecture++Under the WSL2 runtime the workspace is the **`Adom-Workspace` distro** (imported via+`wsl --import`), not a Docker container. HD monitors workspace health at two levels:++1. **WSL2 available** — is WSL2 itself installed and responding on this machine?+2. **Workspace healthy** — is `Adom-Workspace` registered AND Running AND is+ code-server reachable on host port **7380**?++The second level is a real reachability check, not just "is the distro registered."+A resume reality-check (after sleep, or after an HD rebuild/relaunch) requires+code-server to actually answer before HD trusts the workspace as healthy — a distro+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 WSL 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 WSL impl. Returns a status+struct shaped like:+```rust+pub struct ContainerStatus {+ pub docker_available: bool, // under WSL: is WSL2 available?+ pub image_pulled: bool, // is the Adom-Workspace tarball/distro present?+ pub container_exists: bool, // is the distro registered (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 WSL they map onto the two+health levels above (WSL availability, then distro-registered → distro-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 WSL/distro states under this runtime.++### 1. WSL2 Not Available (red dot)+- **When**: WSL2 itself is not installed / not responding (level-1 health fails)+- **Message**: "WSL2 is required but not available."+- **Action**: "Run Setup Steps" button → dispatches `hd-show-setup` event (which walks+ the user through enabling WSL2)+- **Color**: Red dot in header++### 2. Workspace Stopped / Not Ready (red dot)+- **When**: WSL2 is available 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 distro + 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 — WSL has no+always-on daemon to launch. Either WSL2 is available 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 WSL mode)+delegates to `WslDistroRuntime::setup_and_start`:+- If the distro isn't present yet, `fresh_import` = download the golden+ `adom-golden.tar.gz` (from the `adom-inc/hd-wsl2-image` release) + `wsl --import+ Adom-Workspace` + start code-server.+- If the distro 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 HD. So HD also **re-ensures code-server on every launch**+(look for `[wsl-startup] re-ensured code-server on launch` in the log). Without that,+a freshly relaunched HD would show the distro Running but the editor stuck on+"Starting AI Environment" forever, because the previous HD'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 distro.++### Events+Listens for `hydrogen-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 (WSL mode) |+|---|---|+| `get_container_status` | Returns workspace status — WSL available, distro present/registered, Running + code-server reachable |+| `setup_and_start` | `WslDistroRuntime::setup_and_start` — `fresh_import` (import distro) + start code-server, up to 10 min timeout |+| `stop_container` | Stops the workspace (`wsl --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 WSL — there's no daemon to start.++## Menu Items (Adom Menu → Workspace Section)+- **Restart Workspace** — `wsl --terminate Adom-Workspace` + re-ensure code-server; emits lifecycle events, shows dialog+- **Stop Workspace** — terminates the distro; 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 registry key)++## Related skills++- [hydrogen-workspace-lifecycle](../hydrogen-workspace-lifecycle/SKILL.md) — import/export/unregister/terminate, the never-touch-global-WSL 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