v1.91.172.0 feat(capy): approvals per thread, Captain plan briefs and before/after PR images

1 hour ago

This pull request makes gstack Capy aware. When it runs under a Capy Captain, every decision now reaches the person in plain words. Plan approvals arrive as a five part brief with a one word reply. Each branch keeps a record of who approved what. And the ship step puts before and after pictures of the change at the top of the pull request.

Ask about this presentation

Chapters

  1. 0:00gstack asks a Capy Captain in plain words
  2. 0:21Why: the relay lost the plain words
  3. 0:42The plan gate, before and after
  4. 1:06The Captain brief
  5. 1:31Who gets asked
  6. 1:56Standing approvals and fresh asks
  7. 2:23The approval record
  8. 2:50What keeps the record honest
  9. 3:17Before and after images on the merge ask
  10. 3:43Evidence
  11. 4:05What to know before you rely on it
Show transcript

gstack asks a Capy Captain in plain words

gstack now asks a Capy Captain in plain words

briefplan approvals as a five-part brief, reply rec
recordwho approved what, kept per branch
imagesbefore/after pictures on merge asks
v1.91.172.0 · feat(capy)

This pull request makes gstack Capy aware. When it runs under a Capy Captain, every decision now reaches the person in plain words. Plan approvals arrive as a five part brief with a one word reply. Each branch keeps a record of who approved what. And the ship step puts before and after pictures of the change at the top of the pull request.

Why: the relay lost the plain words

“Eli5 these recommendations and please note in the future you need to explain.”
Garry · 2026-10-17
1 CaptainGStack Minder relays every ask
~30 crew threads29 thread reports as of 2026-10-20

On October 17th, Garry had to write this. He runs gstack through one Captain, GStack Minder, and about 30 crew threads, so every plan gate and every merge ask reaches him through a relay. Each thread and the Captain translated gate output by hand, so the shape depended on who wrote it. Three days later he asked for the fix.

The plan gate, before and after

briefasksrecordimages

Gate codes became a brief that says “reply rec”

Before: the autoplan gate as relayed, with gate ids, option letters and reply tokens before
After: the Captain brief, opening with Decision needed and Reply rec after

This is the plan gate a thread used to relay. A gate line with a run id, then numbered items with option letters, and a reply grammar built from those letters. The Captain rewrote it into plain language by hand, and the codes leaked through anyway. Here is the same gate now. It opens with the decision in one sentence, says reply rec to take every recommendation, and then explains the plan.

The Captain brief

briefasksrecordimages

Five sections, in a fixed order

$ gstack-owner-brief render --captain --out <run>
  1. Before → after
  2. Why it matters
  3. What caused this work
  4. Big decisions
  5. What we need from you
Reply: rec
trailing agent linegate ids, gate_rev and rec_hash, for the relaying agent only
CAPTAIN_BRIEF_INCOMPLETEa plan missing a section prints “Not ready for approval” and exits 1

The brief comes from gstack owner brief render, with the captain flag. The first four sections are read straight from the reviewed plan, through a new fence aware heading parser. The last one is built from the gate itself. It lists every choice rec would take, in words. Gate ids and the rec hash move to one trailing line for the relaying agent. And a plan missing a section prints not ready for approval, and exits with code 1.

Who gets asked

briefasksrecordimages

Under Capy, every ask goes through a Captain or the person

CAPY: on
gstack-skill-start, when the launch line sets GSTACK_CAPY=1, plus one capy-asks block
crew thread →
Its Captain
a needs-you report
no Captain →
The person
a Capy question
Printed at runtime: 0 bytes added to generated SKILL.md files. Other hosts print CAPY: off.

How does a workflow know it is under Capy? The Capy launch line sets a flag, and skill start prints Capy on, plus one block of instructions. A crew thread sends a needs you report to its Captain. A thread with no Captain asks the person with a Capy question. The agent picks the route, since Capy keeps crew membership to itself. The block is printed at runtime, so the generated skill files stay the same size.

Standing approvals and fresh asks

briefasksrecordimages

Standing approvals get cited, and a fixed list always gets asked

standing approval
“yes merge when green”
Listed in the Captain’s rules. The thread cites the rule instead of asking again.
always a fresh ask
  • a product or behaviour decision
  • spend over a stated cap
  • publishing outside the repository
  • a threshold, budget or trial-count change
  • closing someone’s contribution without landing it
  • anything destructive

Some decisions are already settled. A standing approval, like merge when green through a merge train, lives in the Captain's rules, and the thread cites it instead of asking again. A fixed list always needs the person's own words. That covers a product or behaviour decision, spend over a stated cap, anything published outside the repository, a change to a threshold, a budget or a trial count, closing someone's contribution without landing it, and anything destructive.

The approval record

briefasksrecordimages

Each branch keeps who approved what, in their exact words

DateAskedExact wordsOutcome
2026-10-20Approve the plan “Make gstack Capy-aware …” (5 decisions)“rec”approved
2026-10-15Run the focused paid eval cases for the changed skills“it's fine run as many $93 paid gates as you want who cares”standing approval
2026-10-16Merge this PR“yes merge when green”standing approval
$ gstack-approvals add | import --autoplan <run> | render | list | sync | path

Every answer gets written down. gstack approvals add stores one row per answer, with the person's exact words, who said them, what the answer covers, and what is still pending. After an autoplan gate is answered, import pulls the reply the gate already stored, and a second run adds zero rows. Render prints this table, which comes from this pull request's own body: the plan approval, a standing approval for paid evals, and merge when green.

What keeps the record honest

briefasksrecordimages

The record refuses secrets and never poses as a citation

One JSONL file per branch
<state root>/projects/<slug>/approvals/
mirrored to the private Drive folder under Capy
Secrets refuse the write
APPROVAL_RECORD_SECRET
names the field and rule, never the text
Display only, never a citation
cell() escapes @ [ ] ! ` | and “approved by”
checked against parseCitations in the tests
“Record unavailable”
APPROVAL_RECORD_UNAVAILABLE
a fresh machine with no mirror says so, instead of “No approvals recorded”

The record is one file per repository and branch, under the state root. A Capy machine's state root gets thrown away, so under Capy every write is mirrored to the private Drive folder. Every field goes through the secret scanner first, and a hit refuses the whole row. Cells are escaped, so the table can never be read as an approval citation. And a fresh machine with no record and no mirror says the record is unavailable, rather than showing an empty table.

Before and after images on the merge ask

briefasksrecordimages

Under Capy, the /ship PR body opens with what changed, why, and approvals

Before: the /ship PR body template opened with Summary and Test Coveragebefore
After: the PR body opens with What changed, captioned before/after images, then Whyafter

Merge asks get pictures. Before, the ship step's pull request body opened with a summary and test coverage, so a merge decision meant reading the diff. Under Capy it now opens with what changed, why, and approvals. These two images come from the new PR shots command. It reads text only and runs nothing, then stacks before over after at GitHub's 880 pixel column, showing only the changed hunks.

Evidence

Tested end to end, from the gate to the mirrored record

4 new test files
including the whole Captain flow: gate, RELAY line, brief, relayed answer, record and mirror
0 failed
of 21,395 tests on the remote free tier at 3a7867f
3 paid cases
ship-local-workflow, ship-base-branch and ship-section-loading PASS; typecheck clean
approval-record.test.tscaptain-brief.test.tspr-shots.test.tscapy-captain-flow.test.ts+ Capy cases in gstack-skill-start.test.ts

Four new test files cover the record, the brief, the images, and the whole Captain flow, from the gate, to the relay line, to the brief, to a relayed answer landing in the record and its mirror. Before merge, the remote free tier ran about 21 thousand tests with zero failures. Three focused paid ship cases passed, and typecheck came back clean.

What to know before you rely on it

Three things to know before you rely on it

Two gate ceilings moved
capy/skill/gstack context budget 2,923 → 3,498 tokens
ship parity cap maxSizeRatio 1.6488 → 1.6690
Owners refresh their Drive copy by hand
cp -R ~/.local/share/gstack/capy/skill/gstack/. <drive>/skills/gstack/
Outside Capy, one new line
every host prints CAPY: off; nothing else changes
gstack asks a Capy Captain in plain words

Three things to know before you rely on it. Two gate ceilings moved: the Capy skill's context budget went from about 2,900 to 3,500 tokens, and ship's parity cap rose slightly for the new section. Eval thresholds stayed where they were. Owners copy the skill folder over their Drive copy by hand to pick up the new guidance. And outside Capy, the only change is one status line. That is gstack, asking a Capy Captain in plain words.

What happened in AI today, in your inbox

One short email each evening. Read it and you’re caught up.

Built in New York with ❤️