mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-13 03:48:06 +01:00
Merge main 90a170c94 into frontier-models-under-test (rule 19 grant)
Conflicts: bin/gstack-eval-plan keeps both --tier-question and --seat-metered; context-budget eval-plan ceiling set to the measured 5107 (5106.9 tokens, no headroom). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
commit
7388cb3259
119 files changed
+2849
-468
No files matched your search
@@ -37,8 +37,8 @@ unknown is reserved for existing behavior you could not inspect. In a live audit
|
||||
you never triggered are unknown, not extrapolated.
|
||||
|
||||
**Caps.** Name the path and item behind every cap and show the corrected error line.
|
||||
- **4/10 (Critical finding, title `CAP4:<path>:<item>`):** a path leaks a secret, signals failure as success, gives an unsafe fix, or blocks waiting for a terminal (hangs, or fails for lack of a terminal without naming a non-interactive alternative such as `--yes`).
|
||||
- **6/10 (High finding, title `CAP6:<path>:<item>`):** a path lacks an applicable fix, recovery, context or trace item.
|
||||
- **4/10 (Critical finding, title `CAP4:<path>:<item> <what fails>`):** a path leaks a secret, signals failure as success, gives an unsafe fix, or blocks waiting for a terminal (hangs, or fails for lack of a terminal without naming a non-interactive alternative such as `--yes`).
|
||||
- **6/10 (High finding, title `CAP6:<path>:<item> <what fails>`):** a path lacks an applicable fix, recovery, context or trace item.
|
||||
|
||||
Every mode, DX TRIAGE included, reports every CAP4 finding.
|
||||
|
||||
|
||||
@@ -257,6 +257,13 @@ the same three paths. An errored init is skipped, so a later successful init
|
||||
still binds. This denial applies when no init ever succeeded, or when an
|
||||
errored init follows a good one.
|
||||
|
||||
An init whose output is not its JSON result (extra text, or no single text
|
||||
block) also counts as failed, and the message adds "without a leading cd" to
|
||||
the re-run hint. The one exception is the `Shell cwd was reset to <dir>` line
|
||||
Claude Code adds as the last line of a Bash result when the command's `cd`
|
||||
left the session's folder: the guard removes exactly that line before reading
|
||||
an init or checkpoint result (#3651).
|
||||
|
||||
<a id="init-unbindable"></a>
|
||||
### `init_unbindable`
|
||||
Init ran through a shell variable, substitution, chaining, a pipe or a
|
||||
|
||||
+1
-1
@@ -172,7 +172,7 @@ not a new ask ([carry-forward](unattended.md#carry-forward)).
|
||||
|
||||
**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
|
||||
what it covers; a gate answered with `--words-by agent:<id>` is listed under that agent as its own decision, not the owner's words); `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).
|
||||
The brief's done line names the row. A plan approval is not a READY field; the
|
||||
|
||||
@@ -38,8 +38,8 @@ cause, the held files and the fix.
|
||||
|
||||
| State | Trigger | Attention line (after `ARTIFACTS_SYNC: attention: `) | Fix |
|
||||
|---|---|---|---|
|
||||
| `held` | The secret scan held back flagged files and synced the rest | `status=held held=<N>. The secret scan is holding back <N> file(s); everything else still syncs. See which files and how to fix them: <bin> --status` | Edit the file so the scan no longer matches, or `<bin> --skip-file <path>` (permanent; undo with `--unskip-file <path>`) |
|
||||
| `blocked` | A re-scan still flagged after the hold, so nothing was committed | `status=blocked. The secret scan flagged a file it could not hold back on its own, so nothing is syncing. See the fix: <bin> --status` | Edit or skip the files `--status` names, or clear the batch with `<bin> --drop-queue --yes` |
|
||||
| `held` | The secret scan or the JSON check held back flagged files and synced the rest | `status=held held=<N>. Sync is holding back <N> file(s) that the secret scan flagged or that are not valid JSON; everything else still syncs. See which files and how to fix them: <bin> --status` | Edit the file so the scan no longer matches, or `<bin> --skip-file <path>` (permanent; undo with `--unskip-file <path>`), or repair a file that is not valid JSON |
|
||||
| `blocked` | A re-scan still flagged after the hold, or a file would be held during an unfinished git merge, so nothing was committed | `status=blocked. Sync committed nothing: a flagged file could not be held back on its own, or held files are part of an unfinished git merge. See the fix: <bin> --status` | Edit or skip the files `--status` names, or clear the batch with `<bin> --drop-queue --yes`. For an unfinished merge, repair the held files or finish the merge; skipping files or dropping the queue does not clear it |
|
||||
| `push_failed` | A commit is saved locally but the push failed | `status=push_failed. Synced files are committed locally but the push failed; it retries at skill start. See the cause: <bin> --status` | The fix `--status` names (usually `gh auth status`) |
|
||||
| `error` | The drain stopped before committing and kept the queue | `status=error. The last sync stopped before committing; the queue is kept and retried. See the cause: <bin> --status` | The cause `--status` names: a git index lock, a commit hook, a `git add` failure |
|
||||
| `unknown` | The status file holds a status code outside this table | `status=unknown. The sync status file is unreadable. See it: <bin> --status` | Run `<bin> --once` to rewrite it |
|
||||
@@ -76,6 +76,11 @@ both fixes:
|
||||
~/.claude/skills/gstack/bin/gstack-brain-sync --drop-queue --yes
|
||||
```
|
||||
|
||||
A synced `.json` file that does not parse (often git conflict markers from a
|
||||
merge) is held the same way under rule `invalid-json`. Its only fix is to
|
||||
repair the file; `--skip-file` is not offered, because it would stop that
|
||||
file syncing for good. The next sync re-checks it.
|
||||
|
||||
**Git index lock.** A git process killed mid-write leaves
|
||||
`~/.gstack/.git/index.lock`. The sync removes it once it is older than 10
|
||||
minutes, because every gstack writer to `~/.gstack` takes the same sync lock
|
||||
|
||||
+9
-3
@@ -91,8 +91,8 @@ gstack-brain-sync --status
|
||||
```
|
||||
|
||||
Shows: the status (`ok`, `idle`, `held`, `collision`, `blocked`,
|
||||
`push_failed`, `error`), files held back by the secret scan with their
|
||||
fixes, files held because their path differs only by letter case from
|
||||
`push_failed`, `error`), files held back by the secret scan or because
|
||||
they are not valid JSON, with their fixes, files held because their path differs only by letter case from
|
||||
another path (`case_held`, `case_names`; see
|
||||
[troubleshooting](troubleshooting.md#artifact-case-collision)), the
|
||||
`drainable` count, the last sync and push times, pending queue depth, and
|
||||
@@ -156,7 +156,7 @@ to it); everything else syncs. The held file stays queued and is re-scanned
|
||||
at every sync, and skill start prints:
|
||||
|
||||
```
|
||||
ARTIFACTS_SYNC: attention: status=held held=1. The secret scan is holding back 1 file(s); everything else still syncs. See which files and how to fix them: <bin> --status
|
||||
ARTIFACTS_SYNC: attention: status=held held=1. Sync is holding back 1 file(s) that the secret scan flagged or that are not valid JSON; everything else still syncs. See which files and how to fix them: <bin> --status
|
||||
```
|
||||
|
||||
To remediate:
|
||||
@@ -200,6 +200,12 @@ is kept in a git stash; `gstack-brain-sync --status` names the stash and the
|
||||
`git -C ~/.gstack --literal-pathspecs restore --source=<stash> -- <path>`
|
||||
command that brings it back.
|
||||
|
||||
JSON files such as `developer-profile.json` have no merge driver, so a
|
||||
merge can leave conflict markers in them. The sync parses every staged
|
||||
`.json` file and holds back one that does not parse (status `held`, rule
|
||||
`invalid-json`) until you repair it, and `gstack-doctor` warns when a synced profile or
|
||||
decision snapshot holds conflict markers.
|
||||
|
||||
## Cross-machine pull cadence
|
||||
|
||||
The preamble runs `git fetch` + `git merge --ff-only` once per 24 hours
|
||||
|
||||
+43
-16
@@ -93,11 +93,14 @@ incomplete coverage, which blocks until the reviewer is rerun.
|
||||
for every reviewer (the gate scan and QA probes keep the whole candidate) and
|
||||
classifies with `--range-base`. Only a blocking finding in that range or in
|
||||
the diff is `fix`; every below-floor finding is a `note`.
|
||||
3. **Two full passes is the cap**, plus at most one fix-check; a scoped pass
|
||||
([below](#scoped-pass)) is a full pass. A blocking
|
||||
finding still open after the last allowed step stops the run and names the
|
||||
finding (path, severity, problem) for the owner; a third full pass is the
|
||||
owner's call, never the loop's. The cap approves nothing.
|
||||
3. **Two rounds is the cap** (R51): a full pass, a scoped pass
|
||||
([below](#scoped-pass)) or a fix-check is a round. After round 2 the run
|
||||
finishes with its open notes listed in the PR body and receipt, or stops for
|
||||
the owner when a blocking finding is open, or was fixed in round 2 and is
|
||||
unverified, or a round-2 fix edited a gate-class path, naming it (path,
|
||||
severity, problem). A third round is the owner's
|
||||
grant of one scoped fix-check (recorded in `review_loop_exception`), never the
|
||||
loop's. The cap approves nothing.
|
||||
|
||||
<a id="transitions"></a>
|
||||
### Transitions
|
||||
@@ -112,10 +115,12 @@ incomplete coverage, which blocks until the reviewer is rerun.
|
||||
| a full pass that fixed a blocking finding, below the cap, on a diff below full depth with a fix range of at most 150 runtime lines | scoped pass on the range |
|
||||
| a full pass that fixed a blocking finding, below the cap (full depth, a larger range, or a range or depth it could not read) | full pass on the range |
|
||||
| a full pass whose fix touched a gate-class path, below the cap | full pass on the range |
|
||||
| a full pass that fixed only below-floor findings | one fix-check |
|
||||
| a full pass at the cap that fixed a blocking finding | one fix-check |
|
||||
| a full pass that fixed only below-floor findings | one fix-check (round 2) |
|
||||
| a light pass 1 whose below-floor fixes change no runtime lines | done |
|
||||
| round 2 (a full pass or a fix-check) that fixed a blocking finding or edited a gate-class path | stop: name it for the owner, who may grant one scoped fix-check |
|
||||
| round 2 (a full pass or a fix-check) that fixed only below-floor findings | done: notes in the PR body |
|
||||
| a full pass that leaves a blocking finding open (fixed or not) | stop: name it for the owner |
|
||||
| a fix-check that found nothing blocking (below-floor fixes before it included) | done |
|
||||
| a fix-check that found nothing blocking | done |
|
||||
| a fix-check that found a blocking finding | stop: name it for the owner |
|
||||
|
||||
A **fix-check** is one reviewer reading the touched hunks, the fix list, the
|
||||
@@ -186,10 +191,27 @@ in a data file still schedules the outside voice.
|
||||
|
||||
| Depth | When | Pass 1 dispatches |
|
||||
|---|---|---|
|
||||
| **light**, runtime 0 | docs and tests only, or only registered outputs | the core checklist only; the outside voice when the diff publishes numbers |
|
||||
| **light**, runtime 1–50 | `SMALL_DIFF_LINES` or fewer runtime lines | core, one **batched** specialist (the batched checklists below plus the Red Team prompt in one context), the native adversarial pass (kept: backtest inconclusive, GSTA-147 D5) and the outside voice on numbers |
|
||||
| **standard** | more than 50 runtime lines | one batched specialist at `medium` and the Red Team at 200 runtime lines (or on a CRITICAL); the batched reader leaves docs and registered outputs out of its diff; adversarial and outside voice read everything |
|
||||
| **full** | gate class, or any classifier error | one batched specialist on the whole diff, the Red Team always, no hit-rate gating |
|
||||
| **light**, runtime 0 | docs, tests, eval write-ups, or only registered outputs | one reader: the core checklist; the outside voice instead reads a diff that publishes numbers |
|
||||
| **light**, runtime above 0 | code with no risk class, at any size (R51: size never raises the depth) | core, one **batched** specialist (the batched checklists below plus the Red Team prompt in one context), the native adversarial pass (kept: backtest inconclusive, GSTA-147 D5) and the outside voice on numbers |
|
||||
| **standard** | `--depth-floor standard`, or a light diff raised by a fixed blocker or a CRITICAL | one batched specialist at `medium` and the Red Team at 200 runtime lines (or on a CRITICAL or a raise); the batched reader leaves docs and registered outputs out of its diff; adversarial and outside voice read everything |
|
||||
| **full** | gate class, a [risk class](#risk-classes), or any classifier error | one batched specialist on the whole diff, the Red Team always, no hit-rate gating |
|
||||
|
||||
<a id="risk-classes"></a>
|
||||
**Risk classes (R51).** `lib/review-risk.ts` reads a code diff's paths and its
|
||||
added and deleted lines; any class makes the depth full (`risk:<class>` in
|
||||
`reasons=`, `review_depth.risk` in the receipt). money, auth and secret are the
|
||||
[targeted pass](#targeted-pass-on-money-gate-auth-and-secret-code)'s matchers.
|
||||
**security** is a trust-boundary path (trust, redact, sanitize, neutralize,
|
||||
consent, escape) or line (`shell: true`, `execSync(`, `eval`, `new Function(`,
|
||||
raw HTML sinks, a shell run through argv, recursive deletes). **data** is user data: a path naming pii, gdpr, ccpa, privacy,
|
||||
personal, account, customer, cookie, persistence, storage or database, or a line
|
||||
that drops a table or column, deletes rows or bulk-deletes records. Auth scope,
|
||||
migrations and deleted code paths were already gate class. `RISK_RULES` hashes
|
||||
the patterns and `DEPTH_RULES` includes it.
|
||||
|
||||
**UI text and SVGs** stay code (light unless a risk class applies). A docs path
|
||||
for UI copy, locale values and chart SVGs waits for an allowlist classifier: three
|
||||
rounds of R51 review found bypasses in line-pattern versions of both.
|
||||
|
||||
**Batched checklists (the lean ensemble, GSTA-167 part 2).** One specialist
|
||||
subagent reads Testing and Security on every code or gate diff, plus
|
||||
@@ -230,7 +252,7 @@ and lockfiles stay runtime. To adopt it, commit an entry like this on the base b
|
||||
|
||||
**Gate paths for the machinery itself.** An edit to `lib/review-depth.ts`,
|
||||
`lib/review-collect.ts`, `bin/gstack-review-plan`, `lib/review-scope.ts`,
|
||||
`lib/review-targeted.ts`, `review/specialists/targeted.md`, `lib/ship-queue*.ts`, `lib/regen.ts`, `.gstack/generated.json`,
|
||||
`lib/review-targeted.ts`, `lib/review-risk.ts`, `review/specialists/targeted.md`, `lib/ship-queue*.ts`, `lib/regen.ts`, `.gstack/generated.json`,
|
||||
`ship/sections/review-army.md.tmpl`, `ship/sections/adversarial.md.tmpl` or
|
||||
`scripts/resolvers/review-army.ts` is gate class: changing how much review runs gets
|
||||
the most review. `rules=` (`DEPTH_RULES`) hashes the deciding constants and patterns,
|
||||
@@ -282,6 +304,10 @@ the specialists, audits and watchers at the host default too
|
||||
prompt. At runtime 0 light runs no adversarial pass either.
|
||||
- Standard reads thresholds on runtime lines, not the whole diff, so a test-heavy
|
||||
diff with few runtime lines dispatches fewer specialists.
|
||||
- R51: size never raises the depth; non-risky code is light at any size, and a risk
|
||||
class makes it full. Two rounds is the cap, a fix-check
|
||||
included: a blocker fixed in round 2 stops for the owner instead of earning a
|
||||
fix-check, and a light pass 1 whose below-floor fixes change no runtime lines is done.
|
||||
- A JSON, CSV, TSV or raster data file no code names is docs, not runtime (GSTA-167
|
||||
part 4a). A binary image no code names is no longer a `binary_code_path` error
|
||||
that forces full depth; one that code names still is.
|
||||
@@ -354,6 +380,7 @@ puts in the receipt:
|
||||
| `review_loop.fixed` | findings fixed in this PR |
|
||||
| `review_loop.scope` | the `review_scope` the loop ran under |
|
||||
| `review_loop.fix_checks` | fix-checks this ship ran (never passes) |
|
||||
| `review_loop.rounds` | passes plus fix-checks (R51 caps it at 2; the queue's `REVIEW_DEPTH:` line prints it) |
|
||||
| `review_loop.dispatches` | reviewer dispatches by role: `specialist`, `red-team`, `adversarial-native`, `outside-voice`, `fix-check`, `targeted` |
|
||||
| `review_loop.est_input_tokens` | prompt plus diff bytes the dispatches read, divided by 4: a relative estimate for comparing runs, not a bill |
|
||||
| `review_targeted` | the [targeted pass](#targeted-pass-on-money-gate-auth-and-secret-code): the `classes` it covered, `rules`, `brief_sha`, the receipt's own `product_patch_id`, `status` (`done` only with its output collected and the committed diff it was rendered on still the one at verify, else `failed`; absent when write got no record, which the queue reports as `legacy`), `findings` and `raised_by`; a carry keeps it while the product patch is unchanged |
|
||||
@@ -366,7 +393,7 @@ specialist stats they record). The summary line appends `fix_checks=`,
|
||||
loop prints the old line.
|
||||
|
||||
Beside the counters, `write` records `review_depth` (the depth, runtime lines,
|
||||
rules hash, base and registry commits, and the gate scan bound to the head) and
|
||||
rules hash, base and registry commits, the gate scan bound to the head and the risk classes) and
|
||||
the queue refuses a receipt below the depth its head needs
|
||||
([ship-policy.md#ship-receipt](ship-policy.md#ship-receipt)). A stop the owner
|
||||
accepted after a fix-check edit goes in `review_loop_exception`, never in prose
|
||||
@@ -385,8 +412,8 @@ difference passed a scoped review.
|
||||
|
||||
| Value | Pass 1 | Blocking finding in unchanged code | Pass 2+ | Cap |
|
||||
|---|---|---|---|---|
|
||||
| `frozen` (default) | the diff | filed as an issue, listed as deferred | the range since the previous pass, blocking only | 2 full passes + 1 fix-check |
|
||||
| `diff-plus-blocking` | the diff | fixed in this PR | the range since the previous pass, blocking only | 2 full passes + 1 fix-check |
|
||||
| `frozen` (default) | the diff | filed as an issue, listed as deferred | the range since the previous pass, blocking only | 2 rounds (a fix-check is one) |
|
||||
| `diff-plus-blocking` | the diff | fixed in this PR | the range since the previous pass, blocking only | 2 rounds (a fix-check is one) |
|
||||
| `all` | everything | fixed in this PR | the whole diff, every finding | 3 fix cycles |
|
||||
|
||||
`diff-plus-blocking` is the fallback for a repository where deferred issues pile
|
||||
|
||||
+4
-4
@@ -1221,13 +1221,13 @@ construction:
|
||||
fix-range`) or in the diff is `scope: fix`; nothing below the floor earns a
|
||||
pass. `NO FINDINGS` is the expected reviewer result for a clean range, never a
|
||||
failed review.
|
||||
3. **Two full passes plus one fix-check is the cap**, stepped by the
|
||||
[transition table](review-policy.md#transitions); a
|
||||
[scoped pass](review-policy.md#scoped-pass) counts as a full pass. A pass that applied no edits
|
||||
3. **Two rounds is the cap** (R51), stepped by the
|
||||
[transition table](review-policy.md#transitions); a full pass, a
|
||||
[scoped pass](review-policy.md#scoped-pass) and a fix-check each count as a round. A pass that applied no edits
|
||||
continues to Step 10: the loop ships with its deferred and noted items named in
|
||||
the PR body. A blocking finding still open after the last allowed step stops
|
||||
the run and names the finding (path, severity, problem) for the owner instead
|
||||
of a third full pass.
|
||||
of a third round; the owner may grant one scoped fix-check.
|
||||
|
||||
`gstack-review-scope summary --since <REVIEW_SINCE>` reads the branch's review-log
|
||||
rows for this invocation and writes the counters the ship receipt carries
|
||||
|
||||
+6
-2
@@ -962,8 +962,12 @@ blank (`PREREG_INCOMPLETE`). `arms` resolves the `eval-arms` model role against
|
||||
`lib/pricing.ts` and stops on an unpriced model (`EVAL_PRICE_MISSING`); `dry-run`
|
||||
runs every row through the stub model for $0 and flags malformed rows
|
||||
(`EVAL_DRY_RUN_FAILED`); `pilot start|settle|release|report` runs three priced
|
||||
items, records actual spend, extrapolates to the full set against the cap
|
||||
(`EVAL_BUDGET_EXHAUSTED`) and prints the spend table the approval question shows.
|
||||
items, records actual spend, extrapolates each priced arm to the full set against
|
||||
the cap (`EVAL_BUDGET_EXHAUSTED`) and prints the spend table the approval question
|
||||
shows. An arm on a subscription with no extra charge is declared with
|
||||
`pilot start --seat-metered <arm,...>`: it reserves $0, settles as `unknown`, and
|
||||
stays out of the projection until one of its attempts settles a real charge.
|
||||
A run stops at the first quota or rate-limit error on such an arm.
|
||||
`results --check <prereg> <results.md>` refuses a finished results file unless one
|
||||
table holds a filled row for every arm, each `<model>#no-gstack` row beside its
|
||||
variants (`EVAL_REPORT_NO_GSTACK_ROW`); no number leaves the run before it passes.
|
||||
|
||||
@@ -314,6 +314,20 @@ network.
|
||||
|
||||
**Expected result.** `OUTSIDE_STATUS: completed`.
|
||||
|
||||
<a id="outside-review-truncated"></a>
|
||||
### `... outside review unavailable: the reviewer hit its output token limit and the response is cut off (...)`
|
||||
|
||||
**Meaning.** The api runner's provider stopped at the output token limit
|
||||
(Anthropic `stop_reason: max_tokens`, OpenAI `finish_reason: length`), so the
|
||||
review ends mid-sentence and its findings may be missing.
|
||||
|
||||
**Fix.** Re-run with a larger `--max-tokens` (`gstack-outside-voice run` or
|
||||
`gstack-autoplan run`), or shorten the prompt. `gstack-autoplan run` sends a
|
||||
default of 16384 to both providers, and OpenAI counts reasoning tokens toward
|
||||
it; `gstack-outside-voice run` sends that default to Anthropic only.
|
||||
|
||||
**Expected result.** `OUTSIDE_STATUS: completed`.
|
||||
|
||||
<a id="outside-review-missing-markers"></a>
|
||||
### `... outside review unavailable: the response lacks the markers this gate requires (...)`
|
||||
|
||||
|
||||
+77
-10
@@ -65,10 +65,12 @@ A run that writes a record ends with one line the parent greps:
|
||||
GSTACK_RESULT: skill=<name> status=<status> run=<dir>
|
||||
```
|
||||
|
||||
`status` is one of `complete`, `gate_pending`, `incomplete`, `interrupted` or
|
||||
`refused`; `run` is where the run's record is. `/autoplan`
|
||||
`status` is one of `complete`, `reviewed`, `gate_pending`, `incomplete`,
|
||||
`interrupted` or `refused`; `run` is where the run's record is. `/autoplan`
|
||||
always ends `gate_pending` when it reaches the final gate, because the gate is
|
||||
written and never approved. Pure queries (`gstack-artifact schema`,
|
||||
written and never approved. `/autoplan --ceo-only` ends `reviewed`: it runs the
|
||||
CEO phase alone, asks nothing, writes no gate and approves nothing (see
|
||||
[CEO-only runs](#ceo-only-runs)). Pure queries (`gstack-artifact schema`,
|
||||
`gstack-gate render`) print no result line.
|
||||
|
||||
Not every workflow writes this line or a run directory today:
|
||||
@@ -374,7 +376,14 @@ the plan.
|
||||
refuses to open without one (`CONSENSUS_MISSING`).
|
||||
3. **Loop `next` until `status=gate_pending`.** Eng always runs last. The gate
|
||||
(`gate.json`, `gate_rev`) lists user challenges as approval items,
|
||||
disagreements as auto items and `p1` plan approval; nothing is approved.
|
||||
disagreements as auto items, each open Critical or High finding as an
|
||||
auto item (a twin pair is one item; `a` applies the reviewer's fix to the
|
||||
plan and is recommended, `b` carries it into the build as a blocking task)
|
||||
and `p1` plan approval; nothing is approved. An unanswered auto item whose
|
||||
recommendation changes the plan keeps the plan's direction, so only `rec`,
|
||||
`p1b` (approve with the recommended changes) or an explicit `<id>a` applies
|
||||
a fix; a finding carried with `b` stays
|
||||
open, so a rerun that raises it again asks again.
|
||||
4. **Answer the gate** with the reply grammar and the revision you read:
|
||||
```bash
|
||||
$AP answer --out <dir> --gate-rev 1 --reply "rec" --by human # every recommendation, plan changes included (APPLY: ...; amend_pending)
|
||||
@@ -385,12 +394,18 @@ the plan.
|
||||
means an agent relayed them, and then `--owner-reply` (or `-` for stdin,
|
||||
`--owner-reply-file <path>`) stores the owner's words verbatim
|
||||
(`OWNER_REPLY_REQUIRED`); owner words that parse to different choices are
|
||||
refused (`GATE_OWNER_REPLY_MISMATCH`). An unattended run refuses an answer
|
||||
refused (`GATE_OWNER_REPLY_MISMATCH`). The words default to the owner's: an
|
||||
agent that answers in its own words (a Captain deciding on its own
|
||||
authority) must pass `--words-by agent:<id>` with its own `--by` id. They
|
||||
are then recorded as `answer_words` with `words_by`, never as
|
||||
`owner_reply`; a later gate never carries them (`CARRY: no`), and
|
||||
`gstack-approvals import` lists that row under the agent, not the owner.
|
||||
An unattended run refuses an answer
|
||||
without `--by` (`ANSWER_BY_REQUIRED`, exit 3) and records the value as
|
||||
`answered_by` on every decision row and in the review record's gate line,
|
||||
so a gate an agent closed is distinguishable from one the owner closed.
|
||||
`gstack-autoplan recommend` changes one recommendation before relay; it
|
||||
bumps the gate's `rec_rev`, and a later `rec` needs the `--rec-hash` from
|
||||
bumps the gate's `rec_rev`, and a later `rec` or `p1b` needs the `--rec-hash` from
|
||||
the render the owner saw (`GATE_REC_STALE`). Every `GATE:` line is followed
|
||||
by `GATE_SPEND:` and `GATE_REC_COST:` (also `gate_cost` in `--json`; see
|
||||
`autoplan/DRIVER.md`).
|
||||
@@ -440,7 +455,19 @@ Concurrency and recovery: every state write is a locked read-modify-write
|
||||
(`test/autoplan-run-recovery.test.ts`, `test/autoplan-run-concurrency.test.ts`,
|
||||
`test/autoplan-answer-invalidation.test.ts`). `--spend-cap` admits each attempt
|
||||
through `spend.json` (`SPEND_CAP_EXCEEDED` refuses, exit 3); host-subagent
|
||||
spend is `unknown` and counted in `worst_case`. `--deadline <min>` makes the
|
||||
spend is `unknown` and counted in `worst_case`. Under a cap, in any session
|
||||
kind (or unattended on Capy), each host-subagent attempt, and each api attempt
|
||||
whose model has no price, reserves the default per voice unless
|
||||
`next --estimate-usd <n>` sets the reservation, which the run keeps for later
|
||||
phases (`run` reserves an api attempt's priced worst case, prompt bytes / 3
|
||||
input tokens plus the output limit, and settles it at its token usage; every
|
||||
native call that went out and bound no result, whether it failed, was cancelled,
|
||||
had its result refused, or was in flight when the process died (the next `run`
|
||||
finds it), settles at its usage, or at its full reservation when the usage is
|
||||
unknown, while one refused before anything was sent, such as with no API key,
|
||||
keeps its attempt open for the rerun, and a failed outside call leaves the phase
|
||||
native-only and stays counted at its reservation); `submit
|
||||
--usage-usd <n>` settles an attempt at its real cost. `--deadline <min>` makes the
|
||||
`run` subcommand cancel the children it started and marks the run
|
||||
`interrupted`.
|
||||
|
||||
@@ -520,9 +547,8 @@ A gate revision after the first (`gate_rev` 2 and later, written after a
|
||||
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 `rec` or "do it"
|
||||
(not item tokens), no item there left pending, and the owner's words
|
||||
recorded; that gate was not itself carried; every accepted change incorporated
|
||||
all of: the earlier gate approved (`p1a` or `p1b`), no item there left
|
||||
pending, and 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 (any later edit refuses, an ELI5-only one
|
||||
included); no urgent or
|
||||
@@ -531,6 +557,13 @@ 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.
|
||||
|
||||
An earlier answer given item by item (`<id><option>` tokens, not `rec` or "do
|
||||
it") carries only what the owner already answered: every item on the new gate
|
||||
must be carried by origin (the same proposal, so the same choice), and only an
|
||||
unreviewed post-cap delta (below) may be new. The carry replays the owner's own
|
||||
`p1` choice and records their tokens verbatim as its basis; any new or changed
|
||||
item, "do it" cases included, goes to the owner.
|
||||
|
||||
- **"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, whose recommended option changes no plan text, and is no round
|
||||
@@ -560,6 +593,40 @@ and `gstack-owner-brief render --captain` opens with "Nothing pending.
|
||||
Carried forward from gate_rev <m>", followed by any items taken under "do it"
|
||||
without being shown to the owner.
|
||||
|
||||
## CEO-only runs
|
||||
|
||||
`/autoplan --ceo-only` runs the CEO review by itself and asks nothing. It runs
|
||||
in the session that invokes the skill; `gstack-autoplan`, the driver a parent
|
||||
agent uses, has no CEO-only mode yet, and `run.json` does not accept the
|
||||
`reviewed` status. It needs Claude Code: other hosts inline autoplan's sections
|
||||
and do not carry this one yet. It is for a harness or a crew that wants the strategy pass
|
||||
without the full gauntlet, such as an eval comparing gstack's CEO review with a
|
||||
plain prompt.
|
||||
|
||||
- **What runs:** Phase 0, the outside-reviewer preflight and the CEO phase.
|
||||
Design, DX, Eng and the final gate do not run.
|
||||
- **How it decides:** every question takes its recommendation and is logged in
|
||||
the Decision Audit Trail. The review mode comes from plan-ceo-review's 0E
|
||||
rule (file count, then greenfield, added capability or fix), not autoplan's
|
||||
fixed SELECTIVE EXPANSION, and the mode line says `CEO-only run`. Consent,
|
||||
destructive and irreversible options take the conservative choice.
|
||||
- **What it never does:** apply a user challenge (the plan's direction stays;
|
||||
each challenge is listed), chain to another skill, promote the CEO plan, or
|
||||
approve anything.
|
||||
- **One model:** add `--no-outside` to skip the outside voice, so every
|
||||
reviewer runs on the host model.
|
||||
- **Where it writes:** the run directory (`.gstack/tmp/autoplan/` in the
|
||||
repository), the reviewed plan, and CEO plans and task files under
|
||||
`GSTACK_STATE_ROOT`. Point that variable inside a trial folder to keep every
|
||||
write there.
|
||||
- **What it leaves:** the reviewed plan (its original is kept at the restore
|
||||
point), `<run dir>/ceo-review.md` (user challenges, scope added,
|
||||
conservative choices, findings and the audit trail), and the last line
|
||||
`GSTACK_RESULT: skill=autoplan status=reviewed run=<dir>`.
|
||||
|
||||
`test/skill-e2e-autoplan-ceo-only.test.ts` runs it on a plan that makes the
|
||||
interactive CEO review ask a question and fails if this mode asks anything.
|
||||
|
||||
## Owner brief
|
||||
|
||||
`gstack-owner-brief render --out <dir>` is what the owner sees at the final
|
||||
|
||||
Reference in new issue
Block a user