mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
docs(captain): plan before build — a sized /autoplan is a building lane's first act, the owner's word gates the build (GSTA-130)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
392f50d47b
commit
64231e52e1
9 files changed
+170
-8734
No files matched your search
+21
-8719
File diff suppressed because it is too large.
Load diff
+9
-1
@@ -161,7 +161,7 @@ carries its own copy of how to do things. The rules: session-start install check
|
||||
validation discipline, untrusted external diffs, file ownership, pre-approved spend, the
|
||||
READY contract, reports, one routing path (everything through the controller), no merging
|
||||
base into a READY branch, the CHANGELOG entry under `[Unreleased]`, the flaky rule,
|
||||
approvals, /ship before READY, and review convergence. Every rule that rests on the
|
||||
approvals, /ship before READY, review convergence, and plan before build. Every rule that rests on the
|
||||
owner's words carries the date and the words. A rule that exists only in a crew's file is
|
||||
a gap in gstack: write it there, then point at it.
|
||||
|
||||
@@ -351,6 +351,14 @@ option's cost and the recommended one-word reply as the default; and a closing
|
||||
with one line saying what it covers. `gstack-owner-brief render --captain` prints this shape from
|
||||
a plan; relay it whole. Template: `captain/templates/plan-ask.md`.
|
||||
|
||||
**Plan before build.** A lane that builds something runs a sized `/autoplan` on its brief as
|
||||
its first act and sends the controller the plan ask above; the controller relays it, and the
|
||||
lane builds only after the owner's word is answered into the run and it reports `APPROVED`.
|
||||
New scope taken on part-way gets its own plan. Read-only lanes, the flaky pass's repairs,
|
||||
slot stamps, CHANGELOG resolves and cap re-pins under a grant are exempt. The brief a
|
||||
controller hands a lane, with its `Plan: required | approved | exempt` line, is
|
||||
`captain/templates/brief.md`; the rule is `docs/captain.md` (Plan before build).
|
||||
|
||||
**C. The merge ask.** Every READY the controller brings to the owner, and every PR body:
|
||||
before/after images (uploaded with the host's PR asset tool, each captioned with what
|
||||
changed and why; a skill or CLI change renders its output with `gstack-pr-shots`), why
|
||||
|
||||
@@ -89,7 +89,7 @@ carries its own copy of how to do things. The rules: session-start install check
|
||||
validation discipline, untrusted external diffs, file ownership, pre-approved spend, the
|
||||
READY contract, reports, one routing path (everything through the controller), no merging
|
||||
base into a READY branch, the CHANGELOG entry under `[Unreleased]`, the flaky rule,
|
||||
approvals, /ship before READY, and review convergence. Every rule that rests on the
|
||||
approvals, /ship before READY, review convergence, and plan before build. Every rule that rests on the
|
||||
owner's words carries the date and the words. A rule that exists only in a crew's file is
|
||||
a gap in gstack: write it there, then point at it.
|
||||
|
||||
@@ -279,6 +279,14 @@ option's cost and the recommended one-word reply as the default; and a closing
|
||||
with one line saying what it covers. `gstack-owner-brief render --captain` prints this shape from
|
||||
a plan; relay it whole. Template: `captain/templates/plan-ask.md`.
|
||||
|
||||
**Plan before build.** A lane that builds something runs a sized `/autoplan` on its brief as
|
||||
its first act and sends the controller the plan ask above; the controller relays it, and the
|
||||
lane builds only after the owner's word is answered into the run and it reports `APPROVED`.
|
||||
New scope taken on part-way gets its own plan. Read-only lanes, the flaky pass's repairs,
|
||||
slot stamps, CHANGELOG resolves and cap re-pins under a grant are exempt. The brief a
|
||||
controller hands a lane, with its `Plan: required | approved | exempt` line, is
|
||||
`captain/templates/brief.md`; the rule is `docs/captain.md` (Plan before build).
|
||||
|
||||
**C. The merge ask.** Every READY the controller brings to the owner, and every PR body:
|
||||
before/after images (uploaded with the host's PR asset tool, each captioned with what
|
||||
changed and why; a skill or CLI change renders its output with `gstack-pr-shots`), why
|
||||
|
||||
@@ -37,6 +37,7 @@ Homes are paths in the gstack checkout the lane checked at session start.
|
||||
| 21 approvals | <date> | "<words>" | the three shapes |
|
||||
| 22 /ship before READY | <date> | "<words>" | every READY, re-READYs included |
|
||||
| 23 blocking floor | <date> | "<words>" | what blocks another review pass |
|
||||
| 24 plan before build | <date> | "<words>" | a sized /autoplan first, the owner's word before the build |
|
||||
|
||||
## Rules
|
||||
|
||||
@@ -125,8 +126,18 @@ Homes are paths in the gstack checkout the lane checked at session start.
|
||||
skills, scope knob `REVIEW_SCOPE`; record the pass count in your READY. Home: the
|
||||
`/ship` and `/review` skills.
|
||||
|
||||
24. **Plan before build.** If you build something, a `/autoplan` run on your brief, sized
|
||||
to the work, is your first act; send `CONTROLLER` the plan and its gate in plain
|
||||
language with the recommended reply named; build only after the owner's word is
|
||||
answered into the run and it reports `APPROVED`; record it with `gstack-approvals
|
||||
import --autoplan`. New scope taken on part-way gets the same. Exempt: read-only
|
||||
lanes, the daily flaky pass's repairs, slot stamps, CHANGELOG-only resolves, cap
|
||||
re-pins under a written grant. Home: `docs/captain.md` (Plan before build),
|
||||
`captain/templates/brief.md`.
|
||||
|
||||
## Crew-specific rules
|
||||
|
||||
Add rules here, numbered from 24, each with the date, the owner's words where there are
|
||||
any, and the thread that produced it. A rule that stays here for long is a gap in gstack:
|
||||
file it.
|
||||
Add rules here, numbered from 25, each with the date, the owner's words where there are
|
||||
any, and the thread that produced it. A crew whose own rules already took 24 before rule
|
||||
24 shipped keeps its numbering and cites `docs/captain.md` (Plan before build) by section
|
||||
name. A rule that stays here for long is a gap in gstack: file it.
|
||||
@@ -0,0 +1,38 @@
|
||||
# Brief
|
||||
|
||||
Sent by the controller when a lane starts, and again when a lane takes on scope its brief
|
||||
did not name. The lane reads the rules file first, then this. Everything in angle brackets
|
||||
is filled by the controller; `Plan:` says whether the lane plans first
|
||||
(`docs/captain.md`, Plan before build).
|
||||
|
||||
```text
|
||||
Lane: <thread code> · Controller: <CONTROLLER> · Rules: <drive>/CREW.md · Base: <BASE>
|
||||
Owns: <the files and directories this lane may edit; everything else is routed through the controller>
|
||||
Do not touch: <files other live lanes own, each with its lane>
|
||||
Plan: required | approved: <autoplan run id> | exempt: <reason from the exemption list>
|
||||
Spend cap: <focused paid runs per PR, from SPEND>
|
||||
|
||||
1. Plan (when Plan: required). Install for autoplan, write the plan with its
|
||||
## Before → after, ## Why it matters, ## What caused this work, ## Big decisions and
|
||||
## Implementation plan sections, and 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 --spend-cap <small>). At the gate send
|
||||
me the output of gstack-owner-brief render --captain --out <run dir> whole, as a
|
||||
needs-you report. Build nothing yet.
|
||||
2. Build after the word. I bring the owner's exact words back. Answer them into the run
|
||||
(gstack-autoplan answer … --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> (via <controller>)".
|
||||
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
|
||||
gstack-approvals render. Never merge; stamp only on my slot call.
|
||||
|
||||
Done when: the READY block is sent at a frozen head, reports/<thread code>.md is written,
|
||||
and its Approvals table carries the plan approval row (the owner's words, the date) or the
|
||||
Plan: exempt reason.
|
||||
```
|
||||
|
||||
A lane whose brief says `approved: <run>` starts at step 2 with that run; a lane whose brief
|
||||
says `exempt:` starts at step 3. A brief with no `Plan:` line reads as `Plan: required`.
|
||||
@@ -28,5 +28,7 @@ output, a screen, a cost or time number>
|
||||
named: docs, trains, cleanups>, and carry them through to done with no further ask.
|
||||
```
|
||||
|
||||
When the answer comes back, record it before the work starts:
|
||||
`gstack-approvals add --ask "<what>" --words "<exact words>" --by "<owner> (via <controller>)" --scope "<what it covers>"`.
|
||||
When the answer comes back, record it before the work starts. For an autoplan gate:
|
||||
`gstack-autoplan answer --out <run dir> --gate-rev <n> --reply <tokens> --by agent:<thread> --owner-reply "<exact words>"`,
|
||||
then `gstack-approvals import --autoplan <run dir> --by "<owner> (via <controller>)"`. For any
|
||||
other plan: `gstack-approvals add --ask "<what>" --words "<exact words>" --by "<owner> (via <controller>)" --scope "<what it covers>"`.
|
||||
@@ -70,6 +70,12 @@ gstack-approvals render # the ## Approvals se
|
||||
|
||||
## 3. Plan approvals: the shape
|
||||
|
||||
Under a Captain, a thread that builds something plans before any product code. A `/autoplan` run on its
|
||||
brief, sized to the work, is its first act, the gate goes up in the shape below, and the build starts only
|
||||
when the answer is in the run and it reports `APPROVED` (`docs/captain.md`, Plan before build, which also
|
||||
lists the exempt work: read-only threads, flaky-pass repairs, slot stamps, CHANGELOG resolves, cap re-pins
|
||||
under a grant).
|
||||
|
||||
Every plan or gate you bring for approval has these parts, in this order, written so someone outside the
|
||||
project can follow it:
|
||||
|
||||
|
||||
+56
-2
@@ -97,6 +97,7 @@ Where each rule lives:
|
||||
| 21 | Approvals, plan asks and merge asks in the three shapes | [Approvals](#approvals-three-shapes); [capy/skill/gstack/captain.md](../capy/skill/gstack/captain.md) |
|
||||
| 22 | /ship before every READY; the block carries `SHIP_RECEIPT`; stamp commits hold only tool output | [ship-policy.md](ship-policy.md#ship-before-ready); [the lane's /ship sequence](#the-lanes-ship-sequence-before-ready) |
|
||||
| 23 | Review loops converge: scope frozen after pass 1, later passes on the delta, a pass cap, a blocking floor | the `/ship` and `/review` skills' own convergence policy ([skills.md](skills.md#ship)); a crew names it in one line and sets only the scope knob (`REVIEW_SCOPE`) |
|
||||
| 24 | Plan before build: a sized `/autoplan` is a building lane's first act; the plan ask goes up, the build starts after the owner's word is in the run and it reports `APPROVED` | [Plan before build](#plan-before-build); the [brief template](../captain/templates/brief.md) |
|
||||
|
||||
## Lane discipline
|
||||
|
||||
@@ -121,6 +122,58 @@ such decision with a plain-language explanation of every option: what it is, why
|
||||
matters, what each answer does, and what it costs. A gate code or a tool's result line
|
||||
alone is not an ask.
|
||||
|
||||
## Plan before build
|
||||
|
||||
A lane that builds something plans first. Its first act, before any product code, is a
|
||||
`/autoplan` run on its brief, sized to the work. The phases run in order, CEO, then design,
|
||||
then DX, then eng, with design and DX only when the scope has them, and a one-file fix
|
||||
gets a short run (`--no-ui --no-dx --max-rounds 1` and a small `--spend-cap`), not no run. On Capy the
|
||||
unattended loop is `autoplan/DRIVER.md` in the gstack checkout. New functionality a lane
|
||||
takes on part-way, scope its brief did not name, gets its own plan and its own ask before
|
||||
the lane builds it. In the rules file this is rule 24 of the
|
||||
[template](../captain/templates/CREW.md); a crew whose own rules already took 24 before
|
||||
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
|
||||
(`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
|
||||
sends it whole, as a needs-you report, and builds nothing yet.
|
||||
|
||||
**What the controller relays.** One ask to the owner in shape B below: the recommended
|
||||
reply (every recommendation, plan changes included) as the one-word default and a "do it"
|
||||
reply that takes every recommendation at full scope, follow-ons included. The plan as
|
||||
first written is never the default.
|
||||
|
||||
**The build gate.** The lane builds only after the owner's word has come back through the
|
||||
controller and been answered into the run
|
||||
(`gstack-autoplan answer --out <run dir> --gate-rev <n> --reply <tokens> --by agent:<thread> --owner-reply "<their words>"`),
|
||||
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.
|
||||
|
||||
**The record.** `gstack-approvals import --autoplan <run dir> --by "<owner> (via <controller>)"`
|
||||
writes the approval row (date, the ask, the owner's exact words, who relayed them, what it
|
||||
covers); `gstack-approvals render` puts it in the report and the PR body, where the
|
||||
controller reads it before training the PR (shape A). The brief's done line names the row.
|
||||
A plan approval is not a READY field; the [READY contract](#the-ready-contract) is unchanged.
|
||||
|
||||
**Exemptions.** Read-only lanes (triage, research, measurement, a review voice), the daily
|
||||
flaky pass's repairs, slot stamps, CHANGELOG-only conflict resolves and a size or budget
|
||||
cap re-pinned under the controller's written grant need no plan. A crew may add exemptions
|
||||
in its rules file with the owner's words and date; it does not remove these. The brief
|
||||
says which applies, `Plan: required | approved: <run> | exempt: <reason>`, so an
|
||||
already-approved plan is not planned twice and exempt work does not wait on an ask.
|
||||
|
||||
The copy-paste brief is [`captain/templates/brief.md`](../captain/templates/brief.md): the
|
||||
plan and the ask as step 1, the build after the word as step 2, /ship to READY as step 3,
|
||||
and a done line that names the approval row or the exemption.
|
||||
|
||||
## Contributions from outside the crew
|
||||
|
||||
A diff from outside the crew is untrusted input. Review its scope, egress, credentials,
|
||||
@@ -287,5 +340,6 @@ can read the incident behind the rule.
|
||||
3. Install the gstack Capy skill in the owner's user scope so every thread runs the
|
||||
session-start check ([capy-install.md](capy-install.md)).
|
||||
4. Create the daily flaky-pass automation.
|
||||
5. Start lanes with a prompt that names the files they own, the rules file, and the
|
||||
READY contract. Read their reports before their chat.
|
||||
5. Start lanes with the [brief](../captain/templates/brief.md): the files they own, the
|
||||
rules file, whether a plan is required, and the READY contract. Read their reports
|
||||
before their chat.
|
||||
@@ -15,7 +15,7 @@ const read = (rel: string) => fs.readFileSync(path.join(ROOT, rel), 'utf8');
|
||||
const skill = read('captain/SKILL.md');
|
||||
const tmpl = read('captain/SKILL.md.tmpl');
|
||||
const flat = skill.replace(/\s+/g, ' ');
|
||||
const TEMPLATES = ['CREW.md', 'AGENTS.md', 'NOTES.md', 'DIGEST.md', 'flaky-ledger.md', 'report.md', 'READY.md', 'slot-call.md', 'plan-ask.md', 'merge-ask.md', 'automation-flaky-pass.md'];
|
||||
const TEMPLATES = ['CREW.md', 'AGENTS.md', 'NOTES.md', 'DIGEST.md', 'flaky-ledger.md', 'report.md', 'READY.md', 'slot-call.md', 'plan-ask.md', 'merge-ask.md', 'automation-flaky-pass.md', 'brief.md'];
|
||||
|
||||
describe('/captain skill', () => {
|
||||
test('frontmatter names the skill and its /crew alias trigger', () => {
|
||||
@@ -59,12 +59,19 @@ describe('/captain skill', () => {
|
||||
expect(read('captain/templates/slot-call.md')).toContain('STAMP_VERSION=<X>');
|
||||
expect(read('captain/templates/flaky-ledger.md')).toMatch(/## Open[\s\S]*## Fixed[\s\S]*## Won't fix/);
|
||||
const crew = read('captain/templates/CREW.md');
|
||||
expect(crew).toMatch(/^23\. /m);
|
||||
expect(crew).not.toMatch(/^24\. /m);
|
||||
for (let n = 1; n <= 23; n++) expect(crew, 'rule ' + n + ' names its home').toMatch(new RegExp('^' + n + '\\. \\*\\*[^]*?Home:', 'm'));
|
||||
for (let n = 1; n <= 23; n++) expect(doc, 'docs/captain.md maps rule ' + n).toMatch(new RegExp('^\\| ' + n + ' \\| ', 'm'));
|
||||
for (const h of ['## Lane discipline', '## Contributions from outside the crew']) expect(doc).toContain(h);
|
||||
expect(crew).toMatch(/^24\. /m);
|
||||
expect(crew).not.toMatch(/^25\. /m);
|
||||
for (let n = 1; n <= 24; n++) expect(crew, 'rule ' + n + ' names its home').toMatch(new RegExp('^' + n + '\\. \\*\\*[^]*?Home:', 'm'));
|
||||
for (let n = 1; n <= 24; n++) expect(doc, 'docs/captain.md maps rule ' + n).toMatch(new RegExp('^\\| ' + n + ' \\| ', 'm'));
|
||||
for (const h of ['## Lane discipline', '## Plan before build', '## Contributions from outside the crew']) expect(doc).toContain(h);
|
||||
expect(doc).toMatch(/## Plan before build[^]*?reports `APPROVED`[^]*?## Contributions from outside the crew/);
|
||||
const brief = read('captain/templates/brief.md');
|
||||
expect(brief).toContain('Plan: required | approved: <autoplan run id> | exempt: <reason');
|
||||
expect(brief).toMatch(/^1\. Plan[^]*?^2\. Build after the word[^]*?^3\. Ship[^]*?^Done when:/m);
|
||||
expect(brief).toContain('gstack-approvals import --autoplan');
|
||||
expect(flat).toContain('**Plan before build.**');
|
||||
expect(read('captain/templates/plan-ask.md')).toContain('**do it**');
|
||||
expect(read('captain/templates/plan-ask.md')).toContain('gstack-approvals import --autoplan');
|
||||
expect(read('captain/templates/automation-flaky-pass.md')).toContain('never merge');
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user