# Proposal: AD-managed shared runtimes (portable Node + Python) for all bridges

**Status:** proposal to the adom-desktop (AD) build thread. Authored from the pup bridge after a full
cold-start debugging pass on three fresh Windows boxes (AdomLapper, AdomTestWin11 vbox, winvm Azure).
**Owner of the fix:** AD core (bridge runtime provisioning + spawn). Bridges only *declare* their need.

---

## The problem we hit (concrete)

pup is a Node process AD spawns as `node server.js`. On a fresh box that costs a cascade of prompts and
dead-ends, none of which are pup's to fix:

1. **UAC.** `desktop_install_node` uses Node's machine-wide **MSI**, manifested `requireAdministrator` →
   a Windows UAC dialog. A cloud AI can't click it; if the user denies it, cold-start dead-ends.
2. **Stale PATH.** Even after a *successful* install, AD's already-running process keeps its old `PATH`,
   so the next `browser_*` STILL returns `node_not_found` / `could not run npm`. Reinstalling doesn't help;
   AD must be relaunched.
3. **Firewall.** A hostless `listen()` binds all interfaces → "allow Node through the firewall?" (pup
   fixed its own side in v1.6.0 by binding loopback, but the *runtime* provisioning is still AD's).
4. **Duplication risk.** If every node bridge solves this itself, you multiply UAC + downloads + PATH pain
   by the number of bridges, and ship N copies of Node.

We proved a **no-UAC** path by hand on winvm: download the portable Node **zip** into `%LOCALAPPDATA%`,
add it to the **user** PATH (HKCU, no admin), reopen AD → pup drove Edge and opened amazon.com. Zero UAC.
But that's a per-bridge hack in pup's skill. It should be a platform capability.

## Goal

One managed runtime story so that, on any fresh box, **any** node/python bridge spawns with **no UAC, no
firewall prompt, no PATH dependency, and no per-bridge reinvention** — while still honoring a bridge that
needs a specific runtime version.

## Non-goals

- Not proposing AD sandbox/containerize bridges. Just: provide the interpreter + spawn correctly.
- Not removing per-bridge dependency isolation (node_modules / venv stay per-bridge).

---

## Design

### 1. A shared, content-addressed runtime cache owned by AD

```
%LOCALAPPDATA%\Adom Desktop\adom-runtimes\
    node-22.11.0\node.exe ...
    node-18.20.4\node.exe ...        # only if some bridge pins it
    python-3.12.7\python.exe ...
```

- Populated by **portable archives** (Node `.zip`, Python `embeddable`/standalone build) extracted into a
  **user-writable** dir. **No installer, no admin, no UAC.**
- Content-addressed by exact version, so two bridges needing `node >=20` **share one** `node-22.11.0`
  copy; a bridge that pins `node-18` gets its own without disturbing the rest. No duplication for the
  common case; isolation when actually needed.
- Best case: AD **bundles** one current Node (and optionally Python) in its installer, so a fresh box has
  the default runtime with **zero download**.

### 2. Bridges DECLARE their runtime in `bridge.json` (they don't install it)

```jsonc
"runtime": {
  "kind": "node",          // "node" | "python"
  "version": ">=20",       // semver range the bridge supports
  "preferred": "22"        // optional: pick this major when provisioning fresh
}
```

Declaring ≠ installing. This is the whole point: **centralize the mechanism, decentralize the version
declaration.** (pup already carries `dependencies.node = ">=20"`; the dedicated `runtime` block makes it
unambiguous for AD's resolver. pup ships it as of the version alongside this proposal.)

### 3. AD resolves + spawns by ABSOLUTE PATH

On bridge spawn, AD:
1. Reads `runtime` from the manifest.
2. If the shared cache has an interpreter satisfying the range → use it.
3. Else fetch the pinned/preferred version (portable zip → `adom-runtimes\…`, no UAC) → use it.
4. **Spawn with the interpreter's absolute path**, e.g.
   `…\adom-runtimes\node-22.11.0\node.exe server.js` — **never** rely on the machine `PATH`.

Spawning by absolute path is the linchpin: it eliminates **both** the UAC (portable, user-dir) **and** the
stale-PATH dead-end (AD already knows exactly where the interpreter is), in one move.

### 4. Python specifics

- Same shared interpreter cache (`python-3.12.7\`).
- Python deps are messier than Node's `node_modules`: give each python bridge its **own venv** layered on
  the shared interpreter (`bridges-cache\<bridge>\.venv`), created with `python -m venv` at spawn — shared
  interpreter, isolated packages. Mirrors how node bridges already get per-bridge `node_modules`.
- ABI sensitivity (C extensions) is why the *version declaration* matters more for Python — but the shared
  cache + per-version resolution already handles it.

---

## Why not the alternatives

- **Each bridge installs its own runtime (status quo).** Multiplies UAC/download/PATH pain per bridge and
  ships N copies of Node. This is exactly the pain that triggered this proposal.
- **One hard-pinned global runtime, no per-version support.** Breaks any bridge that needs a different
  major, and one bad shared update breaks everything at once. The content-addressed cache avoids both:
  shared when compatible, isolated when pinned.

Industry precedent for the hybrid: VS Code (bundles Node for the extension host + `engines` constraints),
nvm/uv (shared cache + `.nvmrc`/pin), Python venvs (shared interpreter + per-project envs).

---

## What AD needs to build

1. `adom-runtimes\` content-addressed cache + a portable-archive fetcher (Node zip today; Python next).
2. Read `bridge.json.runtime` (fall back to `dependencies.node` / `dependencies.python`).
3. Resolver: satisfy-from-cache-else-fetch, keyed by version range.
4. **Spawn bridges by absolute interpreter path** (drop the bare-`node` PATH lookup).
5. Optional but ideal: **bundle** one Node (and Python) in the AD installer for a zero-download fresh box.
6. Deprecate the UAC MSI path in `desktop_install_node` in favor of the portable install (or keep it as a
   last-resort fallback with the user warned — see the pup SKILL cold-start playbook).

## What bridges do

- Declare `runtime` in `bridge.json` (pup already does).
- Keep their own dependency layer (`node_modules` / `.venv`) — unchanged.
- Stop shipping/instructing their own interpreter bootstrap once AD provides this.

## Migration / back-compat

- `runtime` is additive; older AD ignores it and keeps today's behavior.
- Until AD ships this, pup's SKILL carries a PROVEN zero-prompt interim recipe (typed verbs: send_files +
  launch_process/tar + registry_set; only an AD reopen remains). desktop_install_node (UAC) / shell_execute
  (approval) are fallbacks.
- Once AD spawns by absolute path from the shared cache, the interim recipe and the UAC/firewall/PATH
  warnings become dead code we can delete from the skill.
