← Commit history

Publish 0.3.16

John Lauer ·546fd702f1 ·2mo ago ·parent dbe0894
4 files changed +159−2
migrate-retired-slugs.shadded+152
@@ -0,0 +1,152 @@+#!/usr/bin/env bash+# migrate-retired-slugs.sh — adom/hydrogen-bootstrap+#+# Heal lockfile roots left behind by slug renames (John 2026-08-13). This is a+# PREREQUISITE for deleting the old wiki pages: the rename redirects are going+# away entirely, so every container must be moved off retired slugs while they+# still resolve. Containers provisioned before the consolidations carry the OLD+# slug as a separate pinned root in adom_modules/.lock.json (live example:+# adom/adom-desktop pinned 1.9.211 next to adom/adom-bridge 2.0.30).+#+# WHY THE PURGE PHASE EXISTS (verified live 2026-08-13): `adom-wiki pkg+# uninstall` removes the entry from `packages` but leaves it in the lockfile's+# `resolved` + `order` arrays; the NEXT `pkg install` re-syncs `packages` from+# that closure and RESURRECTS the removed entry as a ghost that uninstall then+# refuses ("not installed") while `packages` still lists it. Until the CLI bug+# is fixed, this script phases all installs BEFORE all uninstalls, then purges+# retired refs from packages/resolved/order directly (lockfile-only removal:+# uninstall itself is registry-independent, so this is safe offline too).+#+# Idempotent: a clean container matches nothing and exits in <1s.+# Best-effort: NEVER fails the bootstrap install (no set -e).+set -u+mlog() { echo "[slug-migration] $*"; }++WIKI="${ADOM_WIKI_BIN:-$HOME/.local/bin/adom-wiki}"+command -v "$WIKI" >/dev/null 2>&1 || WIKI="adom-wiki"+command -v "$WIKI" >/dev/null 2>&1 || { mlog "adom-wiki not found; skipping"; exit 0; }++MOD=""+for c in "$HOME/project/adom_modules" "$HOME/adom_modules"; do+  [ -f "$c/.lock.json" ] && MOD="$c" && break+done+[ -n "$MOD" ] || { mlog "no adom_modules lockfile found; skipping"; exit 0; }+LOCK="$MOD/.lock.json"++# ── The migration table (bootstraps first: [email protected] carries the dep+# edge on adom/adom-desktop; migrating it drops that edge before we prune).+# retired-ref:successor-ref (empty successor = absorbed, hub re-delivers).+TABLE="adom/hd-bootstrap:adom/hydrogen-bootstrap+adom/hd-windows-bootstrap:adom/hydrogen-windows-bootstrap+adom/hd-mac-bootstrap:adom/hydrogen-mac-bootstrap+adom/adom-desktop:adom/adom-bridge+adom/step2glb:adom/adom-step2glb+adom/adom-theme-system:adom/adom-theme+adom/adom-workspace-control:"++has_pkg() {+  python3 - "$LOCK" "$1" <<'PY'+import json, sys+d = json.load(open(sys.argv[1]))+sys.exit(0 if sys.argv[2] in (d.get("packages") or {}) else 1)+PY+}++PRESENT=""+while IFS=: read -r old new; do+  [ -n "$old" ] && has_pkg "$old" && PRESENT="$PRESENT$old:$new+"+done <<< "$TABLE"++if [ -z "$PRESENT" ]; then+  mlog "clean: no retired slugs present"+  exit 0+fi++# ── Phase 1: install every successor (ALL installs happen before ANY removal,+# so the resurrect-on-install bug can't undo phase 2/3).+while IFS=: read -r old new; do+  [ -n "$old" ] || continue+  mlog "MIGRATING $old -> ${new:-'(absorbed into adom/adom)'}"+  if [ -n "$new" ]; then+    "$WIKI" pkg install "$new" >/dev/null 2>&1 \+      && mlog "  installed successor $new" \+      || mlog "  ⚠ could not install $new (will still remove $old; re-run heals)"+  fi+done <<< "$PRESENT"+# The absorbed-skill case: adom/adom (the hub) owns adom-workspace-control's+# skill now — re-install it so the shared skill file survives phase 2's prune.+if echo "$PRESENT" | grep -q "adom/adom-workspace-control" && has_pkg "adom/adom"; then+  "$WIKI" pkg install "adom/adom" >/dev/null 2>&1 \+    && mlog "  re-installed adom/adom (owns the absorbed workspace-control skill)"+fi++# ── Phase 2: uninstall the retired refs (registry-independent; may fail on+# ghosts — phase 3 purges whatever this leaves).+while IFS=: read -r old new; do+  [ -n "$old" ] || continue+  "$WIKI" pkg uninstall "$old" >/dev/null 2>&1 \+    && mlog "  removed $old" \+    || mlog "  (uninstall of $old refused; purging from lockfile directly)"+done <<< "$PRESENT"++# ── Phase 3: purge residue from packages/resolved/order so no later install+# can resurrect the retired refs.+python3 - "$LOCK" <<'PY'+import json, sys+RETIRED = {"adom/hd-bootstrap","adom/hd-windows-bootstrap","adom/hd-mac-bootstrap",+           "adom/adom-desktop","adom/step2glb","adom/adom-theme-system",+           "adom/adom-workspace-control"}+path = sys.argv[1]+d = json.load(open(path))+removed = []+pk = d.get("packages") or {}+for r in list(pk):+    if r in RETIRED:+        del pk[r]; removed.append(r)+def ref_of(entry):+    if not isinstance(entry, dict): return ""+    o, s = entry.get("owner"), entry.get("slug")+    return f"{o}/{s}" if o else (s or "")+d["resolved"] = [e for e in (d.get("resolved") or []) if ref_of(e) not in RETIRED]+d["order"] = [o for o in (d.get("order") or []) if o not in RETIRED]+if removed:+    json.dump(d, open(path, "w"), indent=2)+    print("[slug-migration]   purged from lockfile:", ", ".join(sorted(removed)))+else:+    print("[slug-migration]   no lockfile residue to purge")+PY++# Module-dir leftovers of purged refs (uninstall removes these when it works;+# the ghost path doesn't). Delivered ~/.claude/skills files are deliberately+# LEFT alone: deleting by name could hit a file the successor now owns — the+# successor installs above already overwrote every shared name.+while IFS=: read -r old new; do+  [ -n "$old" ] || continue+  [ -d "$MOD/$old" ] && rm -rf "$MOD/$old" 2>/dev/null && mlog "  removed module dir $old"+done <<< "$PRESENT"++# ── Phase 4: verify every successor's delivered skills exist on disk; one+# re-install heals a collateral prune (safe now: retired refs are purged, so+# installs can no longer resurrect them).+for ref in adom/hydrogen-bootstrap adom/hydrogen-windows-bootstrap adom/hydrogen-mac-bootstrap \+           adom/adom-bridge adom/adom-step2glb adom/adom-theme adom/adom; do+  pkgdir="$MOD/$ref"+  [ -d "$pkgdir/skills" ] || continue+  missing=0+  for dsk in "$pkgdir"/skills/*/; do+    [ -d "$dsk" ] || continue+    n=$(basename "$dsk")+    [ -f "$HOME/.claude/skills/$n/SKILL.md" ] || { mlog "  ⚠ $ref skill '$n' missing on disk"; missing=1; }+  done+  if [ "$missing" = "1" ]; then+    mlog "  re-installing $ref to re-deliver missing skills"+    "$WIKI" pkg install "$ref" >/dev/null 2>&1 || mlog "  ⚠ re-install of $ref failed"+  fi+done++N=$(echo "$PRESENT" | grep -c ":" || true)+mkdir -p "$HOME/.adom" 2>/dev/null+echo "{\"version\":1,\"migrated\":$N,\"at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" > "$HOME/.adom/slug-migration.json" 2>/dev/null+mlog "DONE: $N retired slug(s) migrated"+exit 0
package.json+1−1
@@ -4,7 +4,7 @@   "type": "bootstrap",   "title": "Adom Hydrogen (platform base)",   "brief": "Layer Hydrogen's workspace onto a standard Adom workstation.",-  "version": "0.3.15",+  "version": "0.3.16",   "description": "Hydrogen's PLATFORM-GENERIC workspace layer on top of adom/core. Installs adom/core for the full Adom ecosystem, then adds everything common to running inside Hydrogen on ANY platform: the runtime-neutral hydrogen-* skills and the generic editor configuration (Claude Code CLI + extension, VS Code settings, trusted domains). Platform-specific layers depend on THIS: adom/hydrogen-windows-bootstrap (WSL2), adom/hydrogen-mac-bootstrap, adom/hydrogen-ubuntu-bootstrap. The reference design other orgs copy to build their own bootstrap on adom/core.",   "org": "adom",   "platform": "any",
page.json+1−1
@@ -4,7 +4,7 @@   "type": "bootstrap",   "title": "Adom Hydrogen (platform base)",   "brief": "Layer Hydrogen's workspace onto a standard Adom workstation.",-  "version": "0.3.15",+  "version": "0.3.16",   "description": "Hydrogen's PLATFORM-GENERIC workspace layer on top of adom/core. Installs adom/core for the full Adom ecosystem, then adds everything common to running inside Hydrogen on ANY platform: the runtime-neutral hydrogen-* skills and the generic editor configuration (Claude Code CLI + extension, VS Code settings, trusted domains). Platform-specific layers depend on THIS: adom/hydrogen-windows-bootstrap (WSL2), adom/hydrogen-mac-bootstrap, adom/hydrogen-ubuntu-bootstrap. The reference design other orgs copy to build their own bootstrap on adom/core.",   "org": "adom",   "tags": [
postinstall.sh+5
@@ -175,3 +175,8 @@ C /run/adom/api-key 0644 root root - /home/adom/.adom/api-key # 7. tidy — install.mjs / tooling leave an empty ~/project/.mcp.json; remove it. rm -f "$HOME/project/.mcp.json" 2>/dev/null || true log "done"++# ── Retired-slug migration (John 2026-08-13) ─────────────────────────────────+# Heals pre-rename lockfile roots (hd-bootstrap, adom-desktop, ...) so the old+# wiki pages/redirects can be deleted. Idempotent, best-effort, never fatal.+bash "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/migrate-retired-slugs.sh" || true