work-state · git:20261015.f372105 · 2026-10-15 · sha256 091e20102648a3ab
work-state git:20261015.f372105A
Immutable. This exact content never changes and is served at /api/v1/blob/091e20102648a3ab.
---
name: work-state
description: |
Report whether work is committed, pushed, tidy and installed, do the safe clean-up and install,
and ask the rest once. Ends the message with one honest status line.
---
# Work state
After finished work the question is not only "is it landed" but "is it committed, pushed, tidied away and installed". This skill runs `scripts/work-state.py`, does what is safe, asks about the rest once, and ends the message with the script's status line; the script measures, this skill only acts on what it printed.
Invoke with: `/superpowers-gstack:work-state`
## Run
Locate the script relative to this skill (it usually runs in the user's project, where `scripts/` does not exist). It needs Python 3.11 or newer; macOS's `/usr/bin/python3` is 3.9, so pick an interpreter first:
```bash
WS="<this skill's base directory>/../../scripts/work-state.py"
PY=""
for p in python3 python3.14 python3.13 python3.12 python3.11 /opt/homebrew/bin/python3; do
if command -v "$p" >/dev/null 2>&1 && "$p" -c 'import sys; sys.exit(sys.version_info < (3, 11))' 2>/dev/null; then PY=$p; break; fi
done
if [ -z "$PY" ]; then
echo "work-state: no Python 3.11+ found (macOS /usr/bin/python3 is 3.9) — install one, e.g. brew install python" >&2
false # stop here: non-zero, and the script is not run
else
"$PY" "$WS" report --repo "$PWD"
fi
```
`report` changes nothing except one `git fetch --quiet origin` (15 s limit). Run with an older Python anyway, the script stops with code 70.
## Read the result
The last line of stdout is a JSON object, also when the report itself failed (then it holds `line` and `error`). Its keys: `line`, `committed`, `pushed`, `tidy`, `installable` (each with `status`, `detail`, `items`), `safe_actions` and `ask`. Exit code 0 means nothing is open, unknown or stale; 1 means something is; 2 means `--repo` is not a git repository; 64 is a usage error; 70 is Python older than 3.11.
`line` is what ends your message, copied as printed. The line never says ✓ while something is left: it names what remains (`mapper ryddet ✗ (1 gren ikke landet: feat/x)`), and `?` means unmeasured, not fine. Never rewrite a ✗ or ? into a ✓ and never drop a part of the line.
## Do what is safe
`safe_actions` is a list of argument lists the script judged safe: a push of a branch that has an upstream, removal of a clean worktree whose branch is an ancestor of `origin/main`, `git branch -d` of such a branch, a leftover folder that holds only cache moved to the Trash, and the `install` call for a `macos-app` that is `stale`.
- Run `safe_actions` as printed: one entry at a time, from the main folder (the first entry of `git worktree list`), in the order given. Do not add flags, reorder, merge or "improve" an entry. The one allowed change: an entry that starts with `python3` may start with `"$PY"` from the loop above, because the script needs 3.11+.
- Stop at the first entry that fails and show its output; do not try the later ones.
- An `install` entry can take minutes (it builds). Run it in the background and write the result when the session is notified.
- Re-run `report` once at the end, and end the message with that second `line`. Never loop more than twice: report, act, report.
- `committed` has no safe action. Commit only files you changed yourself in this task; anything else goes to `ask`.
## Ask once
Collect everything in `ask`, plus any stop code from `install` that needs the user (4, 5, 6 below), into one `AskUserQuestion` in one message. Phrase each item in plain words with the choice it needs. If the user does not answer, do nothing about those items; the line stays honest and shows them as open. After `install` has stopped on a question, its answer is stored in `<git dir>/gstack-install-result.json`, so the next `report` puts the question in `ask` again: ask it only if the session is not in the middle of another task.
## Install codes
`install` prints a last stdout line `{"status", "code", "reason", "commit"}`, and every run that holds the install lock also stores it in `<git dir>/gstack-install-result.json`. A `status` of `current` means the stamp already proves `origin/main` is installed.
| Code | Meaning | The agent does |
|---|---|---|
| 0 | Installed, or already current | Re-run `report`; the line ends the message. |
| 2 | The `Installable:` line is not `macos-app`, or is missing | Say so. Nothing to install here; `/superpowers-gstack:adapt` writes the line. |
| 3 | The main folder is not on `main`, is not clean, `pull --ff-only` failed, or `main` differs from `origin/main` | Show the named files or the reason. The user decides; do not stash or reset. |
| 4 | The app is running | Ask the user. Only after their yes in this session, re-run `install` with `--quit-running` (asks the app to quit itself), or with `--quit-path APP` to quit just that bundle. The script never kills. |
| 5 | The span since the stamp may change data on disk, or cannot be measured | Show the reason and ask. Only after the user's yes in this session, re-run with `--accept-migration`. |
| 6 | A copy without a stamp is in the way | Ask whether it may be replaced. Only after the user's yes in this session, re-run with `--replace-unstamped`. |
| 7 | Another app, or one that cannot be identified, has the name | Tell the user to tidy by hand: no flag helps. Name the app the reason names. |
| 8 | The build failed; nothing was touched | Show the log path from the reason (`<git dir>/gstack-install-build.log`). |
| 9 | The check failed; the old copy is untouched (a temporary copy was removed, or the swap was rolled back) | Report the reason and stop. Nothing is stamped. |
| 10 | The applications folder is missing or not writable, or macOS refused the swap | Report and stop. Never `sudo`. |
| 11 | `main` moved during the build; nothing swapped or stamped | Report and stop. The user may run `/superpowers-gstack:work-state` again. |
| 12 | Another install holds the lock, or `land` held its lock longer than 60 s | Wait, then run `report` again. |
| 64 | Usage error (bad flags, scheme or settings not usable) | Show the state as printed. Do not guess. |
| 70 | Unexpected error, interrupted ("avbrutt"), or Python older than 3.11 | Show the printed state. Do not guess. |
`/superpowers-gstack:verify-and-land` has already opened the branch build in Phase 5; when the user says yes to "make this the live version and install it", it passes `--quit-path <the bundle from Phase 5>` to `install`, so the commonest path does not stop at code 4. Without that yes, the flag is not passed.
## Never
- never `git branch -D` (a refused `-d` is the guard working), never force push, never `sudo`, never kill a process; an app is quit with `--quit-running` or `--quit-path` only.
- Never pass `--quit-running`, `--quit-path`, `--accept-migration` or `--replace-unstamped` without the user's yes in this session. A yes from an earlier session or from a stored result does not count.
- Never edit, shorten or reword the script's output: not `line`, not an entry of `safe_actions`, not a `reason`.
- Never stash, reset, or remove a worktree the session stands in or one that `report` marked as in use.