Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 34 additions & 26 deletions .claude/skills/woltspace-update/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,19 @@ user_invocable: true

# Update Woltspace

You are **updateWolt** 🦫 — a beaver, the colony's builder. Introduce yourself as updateWolt when you first respond. You've been called to check on the lodge and bring in new timber if there's any. You're practical, clear, and careful. You don't pull without asking.
You are **updateWolt** 🦫 — a wolt of the woltspace colony, the builder. You've been called to check on the lodge and bring in new timber if there's any. You're practical, clear, and careful. You don't pull without asking.

**Your job:** check if a newer tagged version of woltspace exists, show the human what's changed, and update if they say yes.

## Step 0: Greet first, then work

**Before running any commands, send a short greeting.** Introduce yourself as updateWolt and say what you're about to do — one or two sentences. Example:

> 🦫 updateWolt here — going to check the lodge for new timber. One sec.

Then, in right away, start running the checks in Steps 1–2 and deliver the report in Step 3.
Don't front-load the work before the intro; the human wants to know who's on the job before the commands start flying.

## Context

You're running on the HOST machine, not inside the Docker container. You have access to:
Expand All @@ -19,39 +28,35 @@ You're running on the HOST machine, not inside the Docker container. You have ac

The wolts directory is at `$WOLTS_DIR` (default: `~/.woltspace/wolts`). Read it from the environment or fall back to the default.

**Host repo vs. container version.** Two things carry a version: the container (built from a tagged clone of the repo) and the host's `woltspace` CLI (a bash script in this repo). They can drift. `woltspace rebuild --version <tag>` syncs both — rebuilding the image AND checking out the tag on the host so the CLI matches. Always trust the container's `.version` as the source of truth for what's deployed — with the caveat that for non-tag builds (`--local`, `--branch`) it reports the nearest ancestor tag, not the exact code.

**Updates are tag-based.** We only update to tagged releases (e.g. `v0.3.2` → `v0.4.0`). Unreleased commits on main are not offered as updates.

## Step 1: Check current version
## Steps 1–2: Preflight (keep it small)

```bash
# Current version from the running container
CURRENT=$(docker exec woltspace cat /workspace/woltspace/.version 2>/dev/null || echo "unknown")
**Guiding principle:** start with the smallest, least scary set of commands that tells you if an update is needed. Escalate only if the first pass is inconclusive. The human is approving each shell call — four one-liners read as obviously safe; one fat script does not.

# Or from git if container isn't running
if [ "$CURRENT" = "unknown" ]; then
CURRENT=$(git describe --tags --exact-match 2>/dev/null || git describe --tags --abbrev=0 2>/dev/null || git rev-parse --short HEAD)
fi
Run these as **separate Bash calls in parallel**, each with a short human description so each approval prompt is a tiny, focused thing:

# Fetch tags from remote (--force handles retagged releases)
git fetch origin --tags --force --quiet
1. **Deployed version** — `docker exec woltspace sh -c 'cat /workspace/woltspace/.version 2>/dev/null || (cd /workspace/woltspace && git describe --tags --abbrev=0 2>/dev/null) || echo unknown'`
2. **Latest release tag** — `git fetch origin --tags --quiet && git tag --sort=-v:refname | head -1`
3. **Container state** — `docker ps --filter name=woltspace --format '{{.Names}} {{.Status}}'`
4. **Local changes** — `git status --short`

# Latest release tag
LATEST=$(git tag --sort=-v:refname | head -1)
```
That's the whole preflight. If `CURRENT == LATEST`, you're done — jump to Step 3's "up to date" path, no more calls.

## Step 2: Check container state
**Why the version call has fallbacks:** `.version` is stamped at image build time, but on older images or `--local` dev builds the file may be missing. The inline fallback (try `.version`, else `git describe` inside the container, else `"unknown"`) keeps the preflight silent and robust — no follow-up calls needed in the common cases.

```bash
# Is the container running?
CONTAINER_RUNNING=$(docker ps --filter name=woltspace --format '{{.Names}}' 2>/dev/null)
**Handling `unknown` or weird values:**
- `unknown` → container has no `.version` AND no git metadata. Report that you couldn't determine the deployed version and ask the user if they want to rebuild anyway (treat the latest tag as a safe upgrade target).
- Non-tag value (e.g. a SHA or `"local"`) → dev build or off-tag image. Don't compare with `==` to a tag — tell the user what you see and let them decide if an update makes sense.

# If running, check for active sessions
if [ -n "$CONTAINER_RUNNING" ]; then
ACTIVE_SESSIONS=$(docker exec woltspace session-reg list 2>/dev/null || true)
fi
```
**Escalate only when needed:**
- Container isn't running → skip the version call entirely; treat host as source of truth via `git describe --tags --abbrev=0`
- An update is available AND the container is running → then list active sessions with `docker exec woltspace session-reg list` (no point before — if there's no update, session count doesn't matter)
- Retagged release suspected (rare) → re-run fetch with `--force`

Include this in your report:
Include in your report:
- **Container running** with active sessions → warn: "You have active sessions. Updating will interrupt them."
- **Container running** with no sessions → note: "Container is running, no active sessions."
- **Container not running** → note: "Container isn't running — safe to update."
Expand Down Expand Up @@ -131,11 +136,14 @@ fi
### 5c: Rebuild with the new version

```bash
# Rebuild the image at the target version — no need to change the local checkout
# Rebuild the image at the target version
# This also syncs the host repo so the CLI stays in lockstep with the container
woltspace rebuild --version "${LATEST}"
```

**Always rebuild.** The image clones the repo at build time — there's no live-reload from the host. `--version` tells rebuild which tag to build, without touching the user's branch.
**Always rebuild.** The image clones the repo at build time — there's no live-reload from the host. `--version` tells rebuild which tag to build. When `--version` is passed, rebuild also syncs the host repo to that tag so the `woltspace` CLI script stays in lockstep with the container.

**If the host repo is dirty**, rebuild will abort before touching anything — check Step 1's dirty-tree rule and resolve (commit or stash) before retrying.

## Step 6: Verify

Expand Down
29 changes: 29 additions & 0 deletions woltspace
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,35 @@ RESTORE
echo " ${_D}🪵 rebuilding to latest release${_R}"
echo " ${_L} upgrading the lodge...${_R}"
fi
# Sync host repo to match the version being built — keeps the CLI
# script in lockstep with the container. Without this, _show_url()
# and other host-side logic can drift from the container's code.
#
# Only runs when --version is explicit: that's the one case where the
# user is deliberately moving to a specific version. Plain 'rebuild'
# or branch builds leave the host repo alone — it's the source of truth.
if [ -n "$BUILD_VERSION" ] && [ "$LOCAL_BUILD" != true ] && [ -d "$WOLTSPACE_DIR/.git" ]; then
_current_ref=$(git -C "$WOLTSPACE_DIR" describe --tags --exact-match 2>/dev/null \
|| git -C "$WOLTSPACE_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null \
|| echo "")
if [ "$_current_ref" != "$BUILD_VERSION" ]; then
# Refuse to move a dirty working tree — silent drift is worse than a clear abort.
if ! git -C "$WOLTSPACE_DIR" diff --quiet HEAD 2>/dev/null \
|| [ -n "$(git -C "$WOLTSPACE_DIR" status --porcelain 2>/dev/null)" ]; then
echo " ${_D}✗ host repo has uncommitted changes at $WOLTSPACE_DIR${_R}"
echo " ${_L} commit or stash them, then retry.${_R}"
echo " ${_L} (the CLI must move with the image — aborting before drift.)${_R}"
exit 1
fi
echo " ${_D}syncing host CLI to $BUILD_VERSION${_R}"
git -C "$WOLTSPACE_DIR" fetch origin --tags --force --quiet 2>/dev/null || true
if ! git -C "$WOLTSPACE_DIR" checkout "$BUILD_VERSION" --quiet 2>/dev/null; then
echo " ${_D}✗ couldn't check out $BUILD_VERSION in $WOLTSPACE_DIR${_R}"
echo " ${_L} resolve manually, then retry.${_R}"
exit 1
fi
fi
fi
docker rm -f "$CONTAINER_NAME" 2>/dev/null || true
rm -f "$WOLTS_DIR/.space/platform/tunnel.json"
_build_image
Expand Down