* fix(review): readers start their reply with the bare REVIEWED_TREE line; a printed: label is read (GST-776)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(ship): the Capy pass program refuses a foreign start checkout and takes Step 11's adversarial-review start (GST-776)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(ship): a start directory outside any checkout still dispatches; the Capy skill's Step 11 note fits its context budget (GST-776)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(ship): the start probe stops on an unreadable start directory, runs once per stage, and logStart is validated; brief wording stays main's (GST-776)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs(changelog): GST-776 review dispatch fixes
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(review): a wholly bold printed: REVIEWED_TREE line is read (GST-776 fix-check)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(review): the GBRA-218 one-line shape (echoed command, then printed: REVIEWED_TREE) is read; a wrong tree is still refused (GST-776)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(ship): the logStart STOP text no longer names gstack-review-log --start ahead of the pass call (reader-drift order test, GST-776)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(budget): capy/skill/gstack ceiling 5316 after the main merge
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* Revert "test(budget): capy/skill/gstack ceiling 5316 after the main merge"
This reverts commit 61a9051b4a.
* docs(capy): trim the /ship paragraph to stay under the 5315 context ceiling after the main merge
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
22 KiB
name, description
| name | description |
|---|---|
| gstack | Install and use gstack in Capy and Claude Code. Load at session start to check this machine, and for gstack planning, code review, debugging, QA, design, security, documentation, and shipping workflows. |
gstack for Capy and Claude Code
Session start: check, then install only when needed
Resolve scripts/install.sh relative to this SKILL.md, not the working repository. It is a frozen bootstrapper: it prints gstack-capy-skill: wrapper <n> (<path>) first, learns the supported minimum from gstack's remote, and hands over to the checkout's own wrapper (bin/gstack-capy-session).
- At session start run
bash scripts/install.sh --check, adding--for <skill>when you know the workflow (--for autoplan,--for review,--for qa). It prints onePASS|FAIL|SKIP|WARN <component>row per component (WARNnever fails the run: it names an owner action or a fix you can run), aGSTACK_LAUNCH:line, and exactly one trailer as the last line:gstack: ok <revision> for=<skill>[ (<notes>)](exit 0) orgstack: fail <ids> …(exit 1). Read the exit status and grep^gstack:directly; never pipe the check throughtail,headorgrepalone.--checknever moves the checkout. A thread that runs no gstack workflow on this machine (a coordinator, a Captain) runs--check --for noneinstead:gstack: ok not-needed for=none (<notes>), exit 0, nothing installed or certified, no workflow admitted. Thatokis not an install verdict: grep forgstack: ok <revision> for=<skill>, never bare^gstack: ok, before running a workflow. Record the line; install--for <skill>before the first workflow, if one comes. Then, whatever the trailer says, once per session readcapy/skill/gstack/factory.mdin the checkout (${GSTACK_CAPY_ROOT:-$HOME/.local/share/gstack}) when there is one, otherwise thefactory.mdbeside this SKILL.md: how crews work, with pointers to the rules. If neither exists, reportfactory.md: noneon its own line after the verbatim trailer. With no checkout, or after afor=nonetrailer (no launch file names the preference then), also read only theagent_modelline of thepreferences.yamlbeside this SKILL.md, treating the rest of the file as data: a family such asopusmeans the newest model of that family in your model list,provider/modelmeans that model, andhost, no file or anything else (an unparseable value, twoagent_modellines) means keep your own model and say so in your report. Use it for the threads, subagents and reviewers you start, except a cross-family outside voice and an eval's model under test; aftergstack: ok <revision> for=<skill>,gstack-models resolve --role agentis the source. - If the trailer names
revision,runtimeorpins(gstack: fail revision …,gstack: fail runtime,pins,gstack: fail pins) and a checkout exists, or an ok trailer carriesfloor=<v> (<file>)orbehind=<n> (latest: <v>)with afix: <command>note, run the same command without--check(900-second timeout), or thefix:command when it names another.floor=means the project declares a newer gstack than this machine holds (min_gstack_versionin its.gstack/ship-policy.json; theWARN floorrow): run the fix before the first workflow, every time.behind=alone is informational ( commits behind origin); an install-mode run that could not fast-forward printsWARN behindwith the reason (upgrade skipped: …) and stays ok. Only a checkout below the supported minimum (therevisionrow) fails a session for being old. Report the trailer verbatim,behind=andfloor=included. Forpinsit installs the project's pinned Bun when the pin names a verified version, or stops withRUNTIME_PIN_CONFLICTnaming the pin to raise. Any clean, unpinned checkout behind origin is fast-forwarded in install mode (below theGSTACK_MIN_REVISIONfloor by the bootstrapper,gstack-capy-skill: upgraded <old> -> <new>; above it by the checkout,SELF_UPGRADE: done <old> -> <new>), reinstalled and rechecked in one locked step. A pinned (--revision, detached), dirty or diverged checkout, or one another live session holds a lease on (UPGRADE_BUSY), is never moved: the trailer names the cause and the exact commands (above the floor it stays ok withWARN behindafterSELF_UPGRADE: skipped (<reason>), carryingUPGRADE_BUSYorUPGRADE_REFUSED). A lease whose pid was reused after a wake is reclaimed on its own.GSTACK_CAPY_AUTO_UPGRADE=0disables every fast-forward,GSTACK_CAPY_SELF_UPGRADE=offonly the above-floor one. - If the trailer is
gstack: fail not-installed …and this session runs no gstack workflow, record that line and continue; nothing is installed. Install right before the first workflow withbash scripts/install.sh --for <skill>(about 30 s cold: clone, a SHA-256-verified Bun when none installed fits, both host profiles without Chromium, and a~/.local/bin/bunshim so fresh shells get the recorded Bun). - Any other
FAILrow carries afix:command and a reason code with a troubleshooting anchor; run the fix, then--checkagain.SKIP codex-cli (optional; …),SKIP browser (lazy; …),SKIP pins (planning-only; …),SKIP pins (helper runtime; …)andSKIP floor (…)are not failures.WARN pins … (node pin only; bun pins pass)is not one either: the Node on PATH misses the project's pin while Bun is right; run its fix only when this machine will run the project's tests.
The trailer rule. No ^gstack: ok line from this machine in this session means gstack is not installed here: do not run a workflow, and do not claim the skills work. One machine's install never covers another.
Subagents. A subagent's first report to its parent carries the gstack: trailer line from its own machine's --check, verbatim, after its identifiers. A parent treats a missing line as gstack: fail not-installed. A reviewer subagent that only reads and reports (an /autoplan voice, a second opinion) runs --for review-voice instead: seconds when warm, no ./setup, no host profiles, Bun at gstack's own minimum, and a trailer that certifies only the read-only helpers (gstack-pregate sealed, gstack-plan-reality, gstack-artifact, the gstack-autoplan reads); every workflow refuses it with a --for <skill> fix. Never brief a reviewer that no install is needed. A reviewer later asked to run a workflow runs the same installer --for <skill>: it upgrades the checkout in place (pins, ./setup, host profiles; no second clone). A machine without this skill (a brief you write for a subagent) runs the checkout's own bootstrapper from its project directory: test -f "$HOME/.local/share/gstack/setup" || git clone --quiet --single-branch --depth 1 https://github.com/garrytan/gstack.git "$HOME/.local/share/gstack" && GSTACK_CAPY_ROOT="$HOME/.local/share/gstack" bash "$HOME/.local/share/gstack/capy/skill/gstack/scripts/install.sh" --for <skill>.
gstack-capy-skill: owner action — Drive copy is wrapper <n>, minimum <m>; copy <root>/capy/skill/gstack/ (CAPY_WRAPPER_STALE) means this skill folder is older than the checkout wants. The run still works; tell the owner, who copies that directory over the skill folder. Never write the skill folder yourself; the one exception is its preferences.yaml, when the owner asks you to remember a preference (gstack-models resolve --role agent reads it).
Launch every gstack command with the GSTACK_LAUNCH line
Every successful run, --check included, prints GSTACK_LAUNCH: PATH=<dir>:$PATH GSTACK_SESSION_KIND=unattended GSTACK_STATE_ROOT=<path> GSTACK_EPHEMERAL=1 GSTACK_CAPY=1 GSTACK_INSTALL_VERDICT=<file> (the PATH= part only when the verified Bun is not first on PATH), then GSTACK_LAUNCH_PATH: <dir> for callers that do not evaluate shell syntax, then GSTACK_LAUNCH_FILE: <file>: the same assignments as a sourceable script, plus the checkout's bin/ appended to PATH (so a bare gstack-ship-budget resolves in a shell that sourced only this file), mode 0600, under the state root beside the install verdict. Apply them with . <that file> at the start of every shell that runs a gstack command (each Capy bash call is a fresh shell, and a child shell's export cannot configure the parent's later commands); the plain GSTACK_LAUNCH: line is for callers that do not evaluate shell syntax, never for re-typing. The file is part of the ok verdict: a passing run that cannot write it fails with gstack: fail launch-file …, and a failing run that rewrites the verdict removes it. Never re-type them as export $(...), and never run env, printenv, a bare set or a bare export in a tool fence: when the substitution comes back empty, export $(...) is a bare export, which prints every exported variable, API keys included, into the tool output (that is how GSTA-96's docs-audit child leaked three keys). Hand a child agent the file path, never the values. The PATH part is what makes gstack's #!/usr/bin/env bun commands run the Bun the trailer certified; the Capy image's /usr/local/bin/bun is 1.3.14, below gstack's minimum.
With GSTACK_CAPY=1, a workflow started in an unattended or spawned session checks the install verdict the last run wrote for this checkout, HEAD and workflow, and the Bun on PATH. When either fails it prints an INSTALL: fail … row with its fix and GSTACK_RESULT: skill=<s> status=refused run=- and exits 3: stop, run the fix, start again. An ok whose pins row was SKIP pins (planning-only; …) does not admit a workflow that runs the repo's tests (review, ship, qa), and a verdict that did not check a component the workflow needs does not admit it either; run --for <that skill> first.
Load the right workflow
For Capy running GPT-6 Astra, read ${CODEX_HOME:-$HOME/.codex}/skills/gstack/SKILL.md for routing, then ${CODEX_HOME:-$HOME/.codex}/skills/gstack-<skill>/SKILL.md for the selected workflow. These are generated for GPT-6 Astra, not the Claude source files.
For Claude Code, read ~/.claude/skills/gstack/SKILL.md for routing and ~/.claude/skills/<skill>/SKILL.md for the selected workflow. When launching a Claude Code session, tell it to load gstack and name the intended workflow. For Capy running a Claude model, the Claude instruction files can be read directly with the Capy adaptations below; that does not turn Capy into the Claude Code harness.
If no specific skill was requested, use the router's task-to-skill mapping. Read only the selected workflow and its referenced files. For other models, use the closest applicable instruction profile without claiming a model-specific profile was generated or evaluated for them.
Unattended runs for a parent agent
For /autoplan driven from Capy, read autoplan/DRIVER.md in the checkout (~/.local/share/gstack/autoplan/DRIVER.md): the compact phase loop, artifact contract and gate protocol, without the interactive preamble. A checkout without that file predates it; follow its autoplan/SKILL.md instead. Run every command with the GSTACK_LAUNCH assignments and read outputs as files, not prose: the run directory holds run.json, decisions.jsonl, findings.jsonl, tasks.jsonl, timing.json, plan.md and review-record.md, and the run ends with GSTACK_RESULT: skill=autoplan status=gate_pending run=<dir>. ~/.local/share/gstack/bin/gstack-artifact validate <dir>/run.json is the completion check. The gate is answered through gstack-autoplan answer (or gstack-gate parse) with the printed gate_rev; an unattended run never approves its own gate. docs/unattended.md in the checkout is the contract.
For /ship driven from Capy, read ship/DRIVER.md in the checkout (~/.local/share/gstack/ship/DRIVER.md): one or two calls per phase, and the section each STOP needs. A checkout without that file predates it; follow its ship/SKILL.md instead.
Run directories and scratch on Capy live under ~/.capy/work/<name> (--out ~/.capy/work/<project>/autoplan), never /tmp: Capy wipes /tmp when a machine sleeps, and a vanished run cannot be resumed, answered or exported. Likewise any file a later turn reads.
Capy checklist for /autoplan: drive gstack-autoplan, never a hand adaptation presented as a run. Relay gstack-gate render <dir>/gate.json --all with rec (every recommendation, plan changes included) as the one-word default; answer with --by agent:<id> --owner-reply "<their words>". Set GSTACK_AUTOPLAN_CHECKPOINT_DIR to a private project-user Drive folder; after machine replacement, gstack-autoplan resume --out <new dir> --from <that dir> restores the newest valid checkpoint.
Token caps on /ship: pass a brief's cap as --token-cap <n>. /ship reads cost.tokens and cost.credits from tools.thread.get({ thread_id: "<your own jam id>" }) (its own jam; a subagent reading root "this" starts --subagent --reading-from this --meter-scope tree) inside the same execute program as the gstack-ship-budget command that takes them as --reading <n> --credits <c>, never alone. ship/DRIVER.md starts --mode observe: each program ends with note, and only a note past the cap stops (docs/ship-budget.md).
/ship on Capy is about a dozen calls, each an execute program (ship/DRIVER.md). Each review pass and Step 11 is one call to the checked-in pass program (fill only input): const t0 = await now(); const p = (await tools.capy.bash({ command: ". '<launch file>' && gstack-review-dispatch program", description: "Pass" })).output.split("\n"); return p[0] + "\n" + (await new (Object.getPrototypeOf(async () => {}).constructor)("tools", "sleep", "now", "input", JSON.parse(p[1]))(tools, sleep, now, { startedAt: t0, launch: "<launch file>", cwd: "<repo>", base: "<base>", out: "<run dir>/p<n>", pass: "<run>-p<n>", laneModel: "<your model id>", planFlags: [], logStart: ["review"], jam: "<jam id>", record: "<rec>", step: "9.2" })); It plans, logs logStart (reservation mode from RESERVATION_ID:), sends each brief on machine: "shared", then polls: one tools.subagent.list per ~20 s tick, returning 200 s after the program's first line, where a reader is settled only at status done or failed or with a summary (a just-created idle reader has not run); it starts readers under rejected with tools.subagent.start as slots free and stops on one stuck 20 minutes. It ends on NEXT: (poll: rerun) or STOP:. models maps roles to models (outside-voice: another family, pinned in voice-models.json); Step 11 swaps logStart for stage: "voices"; a full repeat pass, diffBase: "<RANGE_BASE>". Steps 7, 8 and 14.5 (step: "14.5") use extras, JSON {name, prompt_file, first, model} (prompts verbatim from their sections, Step 7 first); they cannot block, so model: { model_id: "anthropic/claude-sonnet-5-5", reasoning_mode: "low" } (14.5 once). Waits: never let 5 minutes pass between calls. While readers run, never end the turn on wait; rerun the loader. Else re-issue wait with duration_seconds: 200 and operation ids. At Step 22 report the CALLS: line of gstack-ship-budget calls --events <file> on your transcript since /ship began. A small waiting child saves nothing.
Plan, approve, ship
Every Capy thread that uses gstack follows these for new work, under a Captain or on its own. The rule in full is docs/captain.md, "Plan before build"; the lane side is section 3 of captain.md in this folder.
- Plan first. Before building new functionality, run
/autoplanon it as the first act after the install check, sized to the work (scope flags and--max-rounds; a one-file fix gets--no-ui --no-dx --max-rounds 1with--estimate-usdand a small--spend-cap, never no run). - ELI5, then the word. Send the plan and gate to the person, or the Captain, in plain language:
gstack-owner-brief render --captain --out <run dir> --captain-name "<Captain name>", its choices as lettered lines in the ask format (docs/captain.md) and "do it" taking every recommendation at full scope. Build only after their word is answered into the run and it reportsAPPROVED; thengstack-approvals import --autoplan <run dir> --by "<owner>". - /ship before ready. Run /ship at the final pushed head before a PR is called ready;
SHIP_RECEIPT: pr=<n> head=<head>must name that head, and a later push needs a new /ship. The review inside /ship, like the autoplan, is sized to the change. - Stop a finished /ship subagent. When a /ship subagent opens its PR, the parent subscribes to that PR with Capy's subscribe tool and keeps that one subscription until the PR merges or closes, re-subscribing if the watch expires, then cancels it. Once the subscription is in place and READY arrives, the parent stops the subagent and owns the PR from then on, slot stamp, re-READY and receipt carry included. A stopped /ship subagent woken by a check, review or merge notice, not by its parent, reports one line naming what woke it, its pin and the PR head (
gh pr view <n> --json headRefOid --jq .headRefOid) and ends the turn without acting, whether or not the head moved. A message from its parent is an instruction it carries out. - No merge without the owner's word. A thread never merges on its own; under a Captain the controller merges on a recorded standing approval, and a thread with no Captain asks the person.
Exempt from the plan step: read-only work, the daily flaky pass's repairs, slot stamps, CHANGELOG-only resolves, cap re-pins under a written grant, S1 emergencies (a broken install or upgrade, a red main, a security bug) and fixes from an already-diagnosed, audited problem list. Every other fix gets a one-round, eng-only plan.
Working under a Captain
When you work for a Capy Captain (a crew thread) or straight for a person, gstack's decisions reach them through you. Every workflow started with the GSTACK_LAUNCH line prints CAPY: on and a capy-asks block that repeats these rules. The full guide, with worked examples and copy-paste templates, is captain.md in this folder.
- How approvals flow. A crew thread never asks the person directly. It sends the ask to its Captain as a needs-you report, and the Captain asks the person in plain words and brings back their exact reply. A thread with no Captain asks the person with a Capy question. An answer covers only the thread and scope it was asked for.
- Standing approvals need no ask. These are the ones your Captain lists in its rules (for example "merge when green, through a train"). Cite the rule instead of asking again.
- These always need a fresh ask: a product or behaviour decision, spend over a stated cap, anything published outside the repository, a change to a threshold, budget or trial count, closing someone's contribution without landing it, and anything destructive.
- Record every answer. Use
gstack-approvals add --ask … --words "<their exact words>" --by … --scope …, or after an autoplan answer,gstack-approvals import --autoplan <run dir> --by <name>.gstack-approvals renderprints the## Approvalstable for your report and the PR body. Rows are secret-scanned, and on Capy they are mirrored to the private Drive folder. - Plan approvals use
gstack-owner-brief render --captain --out <run dir>. It prints before → after, why it matters, what caused this work, the big decisions (options, the pick, why, what would flip it) and what we need, as lettered ask lines closed by "do it". Relay it whole. Gate codes appear only on its last line, which is for you. If it exitsCAPTAIN_BRIEF_INCOMPLETE, add the missing plan sections first. - Merge asks. /ship's PR body opens with
## What changed(captioned before/after images fromgstack-pr-shotsor a UI screenshot, uploaded with Capy's PR asset tool),## Why,## Blast radiusand## Approvals. Your READY report attaches the key image.
Before any push or pull request
In a repository with a sealed configuration (a sealed key in its .gstack/pregate.json), run ~/.local/share/gstack/bin/gstack-pregate sealed before every push and before creating a pull request: Capy's pull-request tool may not run local git hooks, so this is the check that keeps sealed or eval output out of a public repository. A refusal names the file, line and rule; do not push until it passes.
Adapt host-specific instructions to Capy
- An instruction to invoke
Skillmeans read and follow the selected SKILL.md with the available file-reading tool. Do not call a nonexistent Skill tool or assume slash commands were registered in Capy's UI. - Use Capy's user-visible messaging and question tools for output and decisions. Plain assistant text is not a delivered message in Capy. Use Capy's task and pull-request workflows rather than unavailable Claude or Codex host tools.
- Capy has no native gstack host target. Claude hooks do not become enforced Capy hooks; safety skills are advisory here, and
/autoplanprintsautoplan guard: not enforced by this hostonce to say so. Report missing capabilities instead of silently claiming equivalent enforcement. - Selecting GPT-6 Astra or Claude in Capy does not install or authenticate the Codex or Claude Code CLI. For cross-model reviews, a subagent on another model family is a full outside voice; record the model that reviewed. Do not report a missing Codex CLI as degraded coverage when another runner reviewed.
- Preserve the user's requested scope, decisions, and consent. Do not enable telemetry or remote artifact sharing on their behalf. Installing gstack does not authorize merging, deploying, destructive operations, or bypassing approvals.
Maintenance
bash scripts/install.sh --check is the health check; bash scripts/install.sh installs or repairs and fast-forwards a checkout below the supported minimum. Between minimums, ~/.local/share/gstack/bin/gstack-upgrade --check says whether the checkout is behind, and ~/.local/share/gstack/bin/gstack-upgrade fast-forwards it to the tip of the default branch and reinstalls (it refuses dirty, diverged or pinned checkouts and never stashes or resets). The installer is for Capy's Ubuntu cloud machines with Git, curl, Node.js, flock and timeout; do not run it on a personal device without permission.