mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
docs: carry-forward, ELI5 section and stacked approvals import
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
8e06646775
commit
f5bcdf71a7
8 files changed
+85
-16
No files matched your search
+10
-3
@@ -27,6 +27,7 @@ line yourself; gstack never writes the plan.
|
||||
1. `next` prints one `ATTEMPT: <id> phase=<p> voice=native|outside prompt=<file> result=<file>`
|
||||
per voice. Dispatch the native prompt to a subagent on your model and the outside prompt to
|
||||
a subagent on another model family (or `gstack-outside-voice run`). Each writes `result=`.
|
||||
The first `next` also prints `REVIEW_ESTIMATE:` (most attempts and reservations the review can take).
|
||||
2. Submit each voice exactly once (`BOUND:`). Both voices in closes the phase (`PHASE_CLOSED:`).
|
||||
3. Repeat `next` until `status=gate_pending` (`GATE: gate_rev=<n>`); render it with `gstack-gate render <dir>/gate.json --all` and relay every page with the two cost lines printed under `GATE:`: `GATE_SPEND:` (spent, worst case, cap, headroom, attempts; host-subagent attempts are priced unknown and counted at their reservation, so quote their real figure from the host's cost view) and `GATE_REC_COST:` (the phases `rec` can reopen and the estimate for them, an upper bound; `gate_cost` in `--json`).
|
||||
You never approve the gate for the owner. Offer `rec` (every recommendation, plan changes included) as the default.
|
||||
@@ -38,6 +39,11 @@ line yourself; gstack never writes the plan.
|
||||
5. `REOPENED: <phases>` means loop `next` again (Eng last). `APPROVED:` means `export` (`DISPATCHES: <n> est_input_tokens=<k>`, relative).
|
||||
6. Rounds: one by default; a second only for High or Critical findings; cap 2 (`--max-rounds`). A `revise` whose decisions all resolve below
|
||||
High prints `APPLIED: <ids> without re-review (below High)`. At the cap with High open, `ESCALATE: evidence sufficient, decision needed`: relay it, never ask for another round.
|
||||
7. Carry-forward: a later gate that asks nothing new takes the owner's words from the gate before it
|
||||
(`CARRIED: gate_rev=<n> <- gate_rev=<m>`, then `APPROVED:`); `CARRY: no (<why>)` leaves it for the owner. `CARRY_CHECK:`
|
||||
means one reviewer from another model family reads the printed delta prompt, then `check --out <dir> --attempt <id>
|
||||
--result <file> --model <id>` (or `--unavailable "<reason>"`). `--no-carry-forward` on the first `next` opts out
|
||||
([docs/unattended.md#carry-forward](../docs/unattended.md#carry-forward)).
|
||||
|
||||
### On Capy
|
||||
|
||||
@@ -51,9 +57,10 @@ line yourself; gstack never writes the plan.
|
||||
with `gstack-config set autoplan_checkpoint drive`) so a run survives machine replacement: a
|
||||
generation is published at rest, after each bound voice and after each phase transition; restore
|
||||
with `resume --out <new dir> --from <dir>/<run>`, which never redispatches a bound voice.
|
||||
- Plans carry `## Before → after`, `## Why it matters`, `## What caused this work`, `## Big decisions`;
|
||||
relay the printed `RELAY: gstack-owner-brief render --captain` output to your Captain (or the person),
|
||||
then `gstack-approvals import --autoplan <dir>` after `answer` (`capy/skill/gstack/captain.md`).
|
||||
- Plans carry `## ELI5` (5-10 plain sentences), `## Before → after`, `## Why it matters`, `## What caused this work`,
|
||||
`## Big decisions`; relay the printed `RELAY: gstack-owner-brief render --captain` output to your Captain (or the person),
|
||||
then `gstack-approvals import --autoplan <dir>` after `answer`, with `--stack <b1,b2,...>` when the plan ships as a
|
||||
stack (`capy/skill/gstack/captain.md`).
|
||||
|
||||
### Commands
|
||||
|
||||
|
||||
+10
-3
@@ -24,6 +24,7 @@ line yourself; gstack never writes the plan.
|
||||
1. `next` prints one `ATTEMPT: <id> phase=<p> voice=native|outside prompt=<file> result=<file>`
|
||||
per voice. Dispatch the native prompt to a subagent on your model and the outside prompt to
|
||||
a subagent on another model family (or `gstack-outside-voice run`). Each writes `result=`.
|
||||
The first `next` also prints `REVIEW_ESTIMATE:` (most attempts and reservations the review can take).
|
||||
2. Submit each voice exactly once (`BOUND:`). Both voices in closes the phase (`PHASE_CLOSED:`).
|
||||
3. Repeat `next` until `status=gate_pending` (`GATE: gate_rev=<n>`); render it with `gstack-gate render <dir>/gate.json --all` and relay every page with the two cost lines printed under `GATE:`: `GATE_SPEND:` (spent, worst case, cap, headroom, attempts; host-subagent attempts are priced unknown and counted at their reservation, so quote their real figure from the host's cost view) and `GATE_REC_COST:` (the phases `rec` can reopen and the estimate for them, an upper bound; `gate_cost` in `--json`).
|
||||
You never approve the gate for the owner. Offer `rec` (every recommendation, plan changes included) as the default.
|
||||
@@ -35,6 +36,11 @@ line yourself; gstack never writes the plan.
|
||||
5. `REOPENED: <phases>` means loop `next` again (Eng last). `APPROVED:` means `export` (`DISPATCHES: <n> est_input_tokens=<k>`, relative).
|
||||
6. Rounds: one by default; a second only for High or Critical findings; cap 2 (`--max-rounds`). A `revise` whose decisions all resolve below
|
||||
High prints `APPLIED: <ids> without re-review (below High)`. At the cap with High open, `ESCALATE: evidence sufficient, decision needed`: relay it, never ask for another round.
|
||||
7. Carry-forward: a later gate that asks nothing new takes the owner's words from the gate before it
|
||||
(`CARRIED: gate_rev=<n> <- gate_rev=<m>`, then `APPROVED:`); `CARRY: no (<why>)` leaves it for the owner. `CARRY_CHECK:`
|
||||
means one reviewer from another model family reads the printed delta prompt, then `check --out <dir> --attempt <id>
|
||||
--result <file> --model <id>` (or `--unavailable "<reason>"`). `--no-carry-forward` on the first `next` opts out
|
||||
([docs/unattended.md#carry-forward](../docs/unattended.md#carry-forward)).
|
||||
|
||||
### On Capy
|
||||
|
||||
@@ -48,9 +54,10 @@ line yourself; gstack never writes the plan.
|
||||
with `gstack-config set autoplan_checkpoint drive`) so a run survives machine replacement: a
|
||||
generation is published at rest, after each bound voice and after each phase transition; restore
|
||||
with `resume --out <new dir> --from <dir>/<run>`, which never redispatches a bound voice.
|
||||
- Plans carry `## Before → after`, `## Why it matters`, `## What caused this work`, `## Big decisions`;
|
||||
relay the printed `RELAY: gstack-owner-brief render --captain` output to your Captain (or the person),
|
||||
then `gstack-approvals import --autoplan <dir>` after `answer` (`capy/skill/gstack/captain.md`).
|
||||
- Plans carry `## ELI5` (5-10 plain sentences), `## Before → after`, `## Why it matters`, `## What caused this work`,
|
||||
`## Big decisions`; relay the printed `RELAY: gstack-owner-brief render --captain` output to your Captain (or the person),
|
||||
then `gstack-approvals import --autoplan <dir>` after `answer`, with `--stack <b1,b2,...>` when the plan ships as a
|
||||
stack (`capy/skill/gstack/captain.md`).
|
||||
|
||||
### Commands
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ Spend cap: <focused paid runs per PR, from SPEND>
|
||||
|
||||
1. Plan (when Plan: required). After the session-start install check, install for autoplan
|
||||
and follow autoplan/DRIVER.md "On Capy" (launch file sourced, GSTACK_AUTOPLAN_CHECKPOINT_DIR
|
||||
set, run dir under ~/.capy/work). Write the plan with its ## Before → after,
|
||||
set, run dir under ~/.capy/work). Write the plan with its ## ELI5, ## Before → after,
|
||||
## Why it matters, ## What caused this work, ## Big decisions and ## Implementation plan
|
||||
sections, then run gstack-autoplan next --out <run dir> --plan <plan.md> <scope flags>
|
||||
sized to this brief (a one-file fix: --no-ui --no-dx --max-rounds 1 --estimate-usd <n>
|
||||
@@ -25,7 +25,8 @@ Spend cap: <focused paid runs per PR, from SPEND>
|
||||
(gstack-autoplan answer --out <run dir> --gate-rev <n> --reply <tokens> --rec-hash <h>
|
||||
--by agent:<your thread> --owner-reply "<their words>"), incorporate every accepted plan
|
||||
change (revise), finish every reopened phase, and start product edits only when the run
|
||||
reports APPROVED. Then gstack-approvals import --autoplan <run dir> --by "<owner>".
|
||||
reports APPROVED (a later gate that asks nothing new prints CARRIED: and needs no second
|
||||
ask). Then gstack-approvals import --autoplan <run dir> --by "<owner>" (--stack b1,b2 for a stack).
|
||||
New scope you take on part-way goes back to step 1 with its own plan.
|
||||
3. Ship. CHANGELOG entry under [Unreleased]; GUARD_CHECKS, pregate and /ship at the final
|
||||
head; one unstamped READY block (captain/templates/READY.md) to me; the PR body carries
|
||||
|
||||
@@ -7,6 +7,9 @@ shape from an autoplan run; write it by hand for any other plan.
|
||||
```text
|
||||
Decision needed on <thread>: <one-line title>. Reply **rec** to take every recommendation.
|
||||
|
||||
## ELI5
|
||||
<5-10 plain sentences: what changes, for whom, and what it costs>
|
||||
|
||||
## Before → after
|
||||
<what the user or maintainer sees today; what they will see after; a command and its
|
||||
output, a screen, a cost or time number>
|
||||
|
||||
@@ -48,6 +48,7 @@ gstack-approvals add --ask "Spend up to \$40 on a paid eval rerun for the ship c
|
||||
gstack-approvals add --outcome standing --ask "Merge #3330 when green" \
|
||||
--words "yes merge when green" --by "Garry, 2026-10-16 (Captain rule)" --scope "every crew PR, through a train"
|
||||
gstack-approvals import --autoplan <run dir> --by Garry # after gstack-autoplan answer; idempotent
|
||||
gstack-approvals import --autoplan <run dir> --by Garry --stack b1,b2 # a stacked PR set: every branch, or none
|
||||
gstack-approvals render # the ## Approvals section
|
||||
```
|
||||
|
||||
@@ -93,8 +94,8 @@ project can follow it:
|
||||
the plan names (docs, trains, cleanups) included, and carry them through to done with no further ask.
|
||||
Say in one line what it covers. For an autoplan gate, "do it" answers as `rec`; record the words.
|
||||
|
||||
For an autoplan run, write parts 1 to 4 as `## Before → after`, `## Why it matters`,
|
||||
`## What caused this work` and `## Big decisions` sections in the plan. Then, at the gate:
|
||||
For an autoplan run, open the plan with `## ELI5` (5-10 plain sentences), then write parts 1 to 4 as
|
||||
`## Before → after`, `## Why it matters`, `## What caused this work` and `## Big decisions` sections. Then, at the gate:
|
||||
|
||||
```bash
|
||||
gstack-owner-brief render --captain --out <run dir>
|
||||
@@ -112,6 +113,11 @@ gstack-autoplan answer --out <run dir> --gate-rev <n> --reply rec --rec-hash <h>
|
||||
gstack-approvals import --autoplan <run dir> --by <name>
|
||||
```
|
||||
|
||||
A later gate that asks nothing new is answered for you with the words you recorded (`CARRIED:`, then `APPROVED:`);
|
||||
the brief says "Nothing pending. Carried forward", so import and build without a second ask. `CARRY_CHECK:` means
|
||||
one reviewer from another model family reads the unreviewed delta first (`gstack-autoplan check`). Anything
|
||||
else prints `CARRY: no (<why>)` and goes to the person as today (`docs/unattended.md#carry-forward`).
|
||||
|
||||
If the person answers in words ("split it into two PRs"), map the words to tokens, answer, and confirm in one
|
||||
line what you recorded ("Recorded: split into two PRs; everything else as recommended"). A plan missing a
|
||||
section renders "Not ready for approval" with no reply invitation, and exits 1 (`CAPTAIN_BRIEF_INCOMPLETE`):
|
||||
|
||||
+8
-5
@@ -138,8 +138,9 @@ this rule shipped cites this section by name.
|
||||
**What the lane sends.** The plan and its gate in plain language a person outside the
|
||||
project can follow: what changes, why now, what the change does, what it costs, the lane's
|
||||
pick, and the recommended one-word reply. `gstack-owner-brief render --captain --out <run dir>`
|
||||
prints that from the plan's `## Before → after`, `## Why it matters`,
|
||||
`## What caused this work` and `## Big decisions` sections, with the cost lines
|
||||
prints that from the plan's `## ELI5`, `## Before → after`, `## Why it matters`,
|
||||
`## What caused this work` and `## Big decisions` sections (also saved as `eli5` in
|
||||
`gate.json`, so `gstack-gate render` opens with them), with the cost lines
|
||||
(`GATE_SPEND:`, `GATE_REC_COST:`) and a last line for the controller; a plan missing a
|
||||
section exits `CAPTAIN_BRIEF_INCOMPLETE`, so the lane adds the section and renders again
|
||||
([capy/skill/gstack/captain.md](../capy/skill/gstack/captain.md) § 3 lists them). The lane
|
||||
@@ -156,10 +157,12 @@ controller and been answered into the run
|
||||
every accepted plan change has been incorporated (`revise`), every reopened phase has
|
||||
finished, and the run reports `APPROVED` (`status=complete` after `export`). An answered
|
||||
gate that still prints `PLAN_AMEND_PENDING` or `REOPENED:` is not a completed plan, and an
|
||||
imported approval row alone is not completion.
|
||||
imported approval row alone is not completion. A later gate revision that asks nothing new
|
||||
is answered with the owner's recorded words and says `CARRIED:`; that is the owner's word,
|
||||
not a new ask ([carry-forward](unattended.md#carry-forward)).
|
||||
|
||||
**The record.** `gstack-approvals import --autoplan <run dir> --by "<owner>"` writes the
|
||||
approval row (date, the ask, the owner's exact words, the lane that answered as relayer,
|
||||
**The record.** `gstack-approvals import --autoplan <run dir> --by "<owner>"` (with
|
||||
`--stack <b1,b2,...>` for a stacked PR set) writes the approval row (date, the ask, the owner's exact words, the lane that answered as relayer,
|
||||
what it covers); `gstack-approvals render` puts it in the report and the PR body. Before
|
||||
training the PR the controller compares that row's words and `gate_rev` with the ask it
|
||||
relayed and the reply it carried back; a row that does not match is not READY (shape A).
|
||||
|
||||
@@ -1769,7 +1769,9 @@ import again. Imports are idempotent, so a retry never records an answer twice.
|
||||
### `gstack-owner-brief render --captain: ... (CAPTAIN_BRIEF_INCOMPLETE)`
|
||||
|
||||
**Meaning.** The plan is missing at least one section the brief needs: `## Before → after`,
|
||||
`## Why it matters`, `## What caused this work`, `## Big decisions`. The brief still prints,
|
||||
`## Why it matters`, `## What caused this work`, `## Big decisions`, and `## ELI5` (alias
|
||||
`## In plain words`) on a run whose `gate.json` carries an `eli5` field (gstack 1.91.305 and
|
||||
later; older runs render as before). The brief still prints,
|
||||
but it opens with "Not ready for approval" and has no reply invitation. Exit 1.
|
||||
|
||||
**Fix.** Add the named sections to the plan (`gstack-autoplan revise --plan <file>` if
|
||||
|
||||
@@ -494,6 +494,46 @@ by its runner. Result codes: `EXECUTION_UNKNOWN`, `RUN_LOCKED`,
|
||||
decision ids are renamed and journaled); one written by a newer gstack is
|
||||
refused.
|
||||
|
||||
### Carry-forward
|
||||
|
||||
A gate revision after the first (`gate_rev` 2 and later, written after a
|
||||
`revise`) is answered with the owner's recorded words from the revision before
|
||||
it when nothing new needs their decision. `gstack-autoplan` prints
|
||||
`CARRIED: gate_rev=<n> <- gate_rev=<m> words="<their words>" by=<relayer> reason=<why>`,
|
||||
then the usual `APPROVED:` lines, and journals `gate_carried`. The carry needs
|
||||
all of: the earlier gate approved (`p1a` or `p1b`) with the owner's words
|
||||
recorded; that gate was not itself carried; every accepted change incorporated
|
||||
or settled; the plan file's bytes equal those recorded at the gate or at the
|
||||
`revise` that took the changes (an ELI5-only edit counts); no urgent or
|
||||
outside-plan finding open; and every new gate item one of the cases below.
|
||||
Anything else prints `CARRY: no (<why>)` (journal `carry_refused`) and the gate
|
||||
goes to the owner as before. `--no-carry-forward` on the first `next` turns it
|
||||
off for the run.
|
||||
|
||||
- **"do it".** When the owner's words were "do it" (their words, not a
|
||||
`captain:` relay of them), a new `auto` item that changes no scope, cost or
|
||||
direction and is no round or budget grant takes its recommendation. The
|
||||
decision row gets `carried_under: do it`, the output prints
|
||||
`CARRIED_UNDER_DO_IT:` per item, and the approval record lists them as
|
||||
"taken under "do it", not seen by the owner". `rec` carries no new item.
|
||||
- **Unreviewed post-cap delta.** A "plan changed after <phase> cap" item is
|
||||
read by one reviewer before it carries. `CARRY_CHECK:` names the attempt and
|
||||
the prompt (the delta against `plan-at-gate.r<m>.md`, the accepted changes,
|
||||
the full plan); the run reserves the estimate in `spend.json` and refuses
|
||||
when it is over `--carry-check-cap <usd>` (default 5) or the run's cap.
|
||||
Dispatch it to a model family other than the plan reviewers', then
|
||||
`check --out <dir> --attempt <id> --result <file> --model <id>`
|
||||
(`--same-family "<why>"` when no other family is available). A High or
|
||||
Critical finding, or `--unavailable "<reason>"`, leaves the gate for the
|
||||
owner. The verdict is saved as `carry_check` on `gate.json`, on the
|
||||
decision rows (`carry_check`) and in `<dir>/decisions.tsv`.
|
||||
|
||||
Carried rows keep `answered_by: agent:gstack-autoplan` and name their source
|
||||
(`carried_gate_from`, `carried_gate_rev`, `carried_answered_by`);
|
||||
`gstack-approvals import --autoplan` writes the carried gate as its own row,
|
||||
and `gstack-owner-brief render --captain` opens with "Nothing pending.
|
||||
Carried forward from gate_rev <m>".
|
||||
|
||||
## Owner brief
|
||||
|
||||
`gstack-owner-brief render --out <dir>` is what the owner sees at the final
|
||||
|
||||
Reference in new issue
Block a user