work-state · git:20261015.ca7368f · 2026-10-15 · sha256 30774ed29a334ecc
work-state git:20261015.ca7368fA
Immutable. This exact content never changes and is served at /api/v1/blob/30774ed29a334ecc.
---
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
On exit 0 and 1 the last line of stdout is a JSON object, also when the report itself failed (then it holds `line` and `error`). Exits 2, 64 and 70 print only a reason on stderr and no JSON: show that reason and stop. 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 kan ryddes, 2 må avgjøres)`), 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 only permitted addition is the `--quit-path <bundle recorded in verify-and-land Phase 5>` that the user's Phase 6 yes covered, appended to the `install` entry.
- Each entry is an argv list (a JSON array of separate arguments). Execute it directly as separate arguments (for example `subprocess.run(argv)`), never joined into a shell string and never re-quoted: paths contain spaces, and the Trash entry carries an AppleScript fragment. The `install` entry starts with the absolute path of a Python 3.11+ interpreter, so it needs no substitution.
- 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 wait for the session's notification that it finished before the second `report`; a `report` while it runs would show the old state.
- 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. Make one question per item, in plain words, with concrete options; with more than four items, put them in one numbered list in the question text and ask the user to answer by number. 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 each `report` puts the question in `ask` again until a run succeeds (the same stop may therefore repeat; ask it once per message): 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, or `--repo` is not a git repository | 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 current version and install it in /Applications", 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`. The only permitted addition is the `--quit-path <bundle recorded in verify-and-land Phase 5>` that the user's Phase 6 yes covered, appended to the `install` entry.
- Never stash, reset, or remove a worktree the session stands in or one that `report` marked as in use.