chore: unify toolchain — fnm Node 24, uv 0.11, probe detects version managers

- verify_facts_probe.py: detect fnm/nvm Node managers
- TOOLCHAIN.md: resolve Node divergence (debby fnm→24, OSA node24)
- uv bumped 0.9.7→0.11.21 on debby
- fnm 1.39.0 installed, Node 24.16.0 default
This commit is contained in:
Hermes (debby) 2026-06-17 16:34:01 +02:00
parent b6bdc829e4
commit c952ae954e
2 changed files with 48 additions and 20 deletions

View file

@ -10,15 +10,15 @@ Probe before trusting this table — facts come from `scripts/verify_facts_probe
## Baseline versions ## Baseline versions
| Tool | Standard | Linux (Debian/Ubuntu) | FreeBSD 15 (pkg) | Manager / source | | Tool | Standard | Linux (Debian/Ubuntu) | FreeBSD 15 (pkg) | Manager / source |
| ---------- | ------------------- | --------------------- | ----------------------- | -------------------------------- | | ----------- | ------------------ | ---------------------------- | ----------------------- | --------------------------------- |
| **Python** | **3.12** (floor) | `python3.12` | `python312` | system pkg + **uv** for venvs | | **Python** | **3.12** (floor) | `python3.13` (debby) — ≥ floor | `python312` + `py312-*` | system pkg + **uv** for venvs |
| **uv** | ≥ 0.11 | `uv` (astral) | `uv` (pkg) — confirm | standalone binary | | **uv** | ≥ 0.11 | `uv` 0.11.21 | `uv` (pkg) — confirm | standalone binary |
| **Node** | **24 LTS** (target) | `node` 24 | `node24` + `npm-node24` | LTS only; never a non-LTS major | | **Node** | **24 LTS** (target)| `fnm` → v24.16.0 (debby) | `node24` + `npm-node24` | **fnm** (Rust, cross-platform) |
| **Rust** | stable (pinned) | rustup `stable` | `rust` (pkg) | `rust-toolchain.toml` per repo | | **Rust** | stable (pinned) | rustup 1.95.0 | `rust` (pkg) | `rust-toolchain.toml` per repo |
| **Go** | latest stable | `golang` | `go` (pkg) | only where a Go component exists | | **Go** | latest stable | `golang` | `go` (pkg) | only where a Go component exists |
| **Zig** | 0.15.2 | `~/.local/bin/zig` | manual | pinned (herdr build dep) | | **Zig** | 0.15.2 | `~/.local/bin/zig` | manual | pinned (herdr build dep) |
| **tmux** | latest stable | `tmux` | `tmux` | system pkg | | **tmux** | latest stable | `tmux` | `tmux` | system pkg |
## Conventions ## Conventions
@ -44,10 +44,9 @@ Probe before trusting this table — facts come from `scripts/verify_facts_probe
### Node — LTS only, one major across the matrix ### Node — LTS only, one major across the matrix
- Standard target: **Node 24 LTS** (FreeBSD already ships `node24`; npm 11). - Standard target: **Node 24 LTS** (FreeBSD already ships `node24`; npm 11).
- **Open divergence to resolve:** debby's Hermes Docker image deliberately pins **Node 22 - **Resolved 2026-06-17:** debby switched to Node 24 via `fnm` (system Node 20 remains
LTS** (Debian trixie's bundled 20.x is EOL). FreeBSD is on **24**. These must converge. for OS-level tools, but all Clawdie/agent workloads use 24). FreeBSD already on 24.
Proposed resolution: bump the Hermes `node_source` stage `22 → 24` and retest the gateway One step remains: bump the Hermes Dockerfile `node_source:22``24` to match.
UI build. **Pending Hermes confirmation before flipping.**
- `package.json` engines floor stays generous (`>=20`) but installed runtime tracks the - `package.json` engines floor stays generous (`>=20`) but installed runtime tracks the
agreed LTS. Never run a non-LTS Node major in production. agreed LTS. Never run a non-LTS Node major in production.

View file

@ -369,15 +369,44 @@ def probe_git() -> dict:
def probe_build_tools() -> dict: def probe_build_tools() -> dict:
# Node: check fnm/nvm first (version managers override system node)
node_info = {}
system_node = run_raw(["node", "--version"]) if shutil.which("node") else None
system_npm = run_raw(["npm", "--version"]) if shutil.which("npm") else None
# fnm (Fast Node Manager) — Rust, cross-platform, like uv for Node
fnm_bin = shutil.which("fnm")
if fnm_bin:
node_info["manager"] = "fnm"
node_info["fnm_version"] = run_raw([fnm_bin, "--version"])
# List installed node versions via fnm
versions_dir = Path.home() / ".local" / "share" / "fnm" / "node-versions"
if versions_dir.exists():
installed = [d.name for d in versions_dir.iterdir() if d.is_dir()]
node_info["installed"] = installed
# Check for default alias
default_link = Path.home() / ".local" / "share" / "fnm" / "aliases" / "default" / "current"
if default_link.exists():
node_info["default"] = default_link.resolve().name
# nvm fallback
nvm_bin = shutil.which("nvm")
if nvm_bin:
node_info["manager"] = node_info.get("manager", "nvm")
if node_info:
node_info["system_node"] = system_node
node_info["system_npm"] = system_npm
else:
node_info = {"version": system_node, "npm": system_npm, "manager": "system"}
return { return {
"rust": run(["rustc", "--version"]) if shutil.which("rustc") else None, "rust": run_raw(["rustc", "--version"]) if shutil.which("rustc") else None,
"go": run(["go", "version"]) if shutil.which("go") else None, "go": run_raw(["go", "version"]) if shutil.which("go") else None,
"zig": run(["zig", "version"]) if shutil.which("zig") else None, "zig": run_raw(["zig", "version"]) if shutil.which("zig") else None,
"python": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}", "python": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
"node": run(["node", "--version"]) if shutil.which("node") else None, "node": node_info,
"npm": run(["npm", "--version"]) if shutil.which("npm") else None, "gmake": run_raw(["gmake", "--version"]) if shutil.which("gmake") else None,
"gmake": run(["gmake", "--version"]) if shutil.which("gmake") else None, "uv": run_raw(["uv", "--version"]) if shutil.which("uv") else None,
"uv": run(["uv", "--version"]) if shutil.which("uv") else None,
} }