Files
gstack/test/fixtures/multi-agent-wave/reference-run/ceo-native.md
Garry Tanandcapy-ai[bot] 45a678323b v1.91.70.0 feat(unattended): artifact contract + validator, unattended session kind, findings-derived review log, gate list (multi-agent wave B-core) (#3115)
* v1.91.70.0 feat(unattended): artifact contract, unattended session kind, findings-derived review log, gate list

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test: register test/fixtures/multi-agent-wave as a free fixture (consumer: headless-artifacts.test.ts)

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>
2026-10-10 23:26:08 +00:00

233 lines
34 KiB
Markdown

INPUT: ceo 2937ab4ea148f3a53969c36337bdca53476892d5f7e8d8b1556bfa8763df6845
Reviewed from the native prompt file (202 lines, 41937 bytes, read in full). Receipts below are from the live checkout at origin/main `91bbd9e` (v1.91.69.0), the plan's stated base.
## Step 0
Mode: SELECTIVE EXPANSION. Expand where the plan under-builds the contract a parent agent depends on (artifact schema, adoption measurement, a session kind that does not collide with the eval harness); hold or defer where it builds breadth no incident in the feedback paid for.
### Premise table
| # | Premise (from the plan) | Receipt | Verdict |
|---|---|---|---|
| 1 | Capy user-scope installer `--check` is a bare `test -x` chain that prints nothing on failure | `~/.capy/drive/user-garry-tan/skills/gstack/scripts/install.sh:7-16` (`test -x ... \|\| return 1` chain), `:32-35` (`--check` calls `check_install; exit 0` with no output on the failure path) | VALID |
| 2 | `bin/gstack-doctor` has one `check_*` row per component, fix lines, exits 1 on `fail` | `bin/gstack-doctor:8-12` (contract), `:441-456` (row calls and `[ "$N_FAIL" -eq 0 ]`) | VALID |
| 3 | Doctor has no `--check`, `--json`, `--require` or Node row | grep of `bin/gstack-doctor` for those tokens: only an unrelated `server-node.mjs` hit at `:296` | VALID |
| 4 | `gstack-session-kind` classifies `spawned \| headless \| interactive`; only `spawned` is honored as an explicit `GSTACK_SESSION_KIND` override | `bin/gstack-session-kind:33-36` (spawned override), `:47-50` (`GSTACK_HEADLESS` → headless), `:59-62` (`CI`/`GITHUB_ACTIONS` → headless) | VALID |
| 5 | `headless` means BLOCK on any question; only `spawned` auto-continues | `scripts/resolvers/preamble/generate-ask-user-format.ts:15` (spawned auto-chooses), `:28` (`headless` → `BLOCKED — AskUserQuestion unavailable`) | VALID |
| 6 | Upgrade/telemetry/lake/proactive prompts are suppressed for `spawned` only | `bin/gstack-skill-start:96` (`if [ "$_SESSION_KIND" != "spawned" ]` around update-check), `:394` (same guard around the emission block) | VALID |
| 7 | Snapshot `init` wraps a raw plan in `## Implementation plan` | `bin/gstack-autoplan-snapshot.ts:413-417` | VALID |
| 8 | Review-log `status`/`unresolved` are model-written; nothing derives them from findings | `bin/gstack-review-log:88-93` (passes the record straight to `bindReview`), `lib/review-evidence.ts:178-186` (binds commit/tree only) | VALID, with a nuance the plan omits: `reviewFreshness` already downgrades `status: clean` with `issues_found > 0` to `UNVERIFIED` at read time (`lib/review-evidence.ts:238-240`). The gap is write-time derivation, not detection. |
| 9 | `gstack-version-bump write` stamps VERSION/package.json/locks with one hard-coded post-write hook | `bin/gstack-version-bump:373-402` (`regenAgentsDigest`, opt-in via `--regen-digest`, hard-coded path) | VALID |
| 10 | `bin/gstack-next-version` already reads the live PR queue and sibling worktrees | `bin/gstack-next-version:78-106` (`claimed`, `siblings`, `active_siblings`, `pickNextSlot`) | VALID |
| 11 | `gstack-evidence` records tree fingerprints | `bin/gstack-evidence:10-13`, `:388-396` (`tree`, `dirty`, wtree TOCTOU guard) | VALID |
| 12 | `ship/sections/measure.md.tmpl:3-16` holds a never-loosen rerun discipline | `ship/sections/measure.md.tmpl:3-7` | VALID |
| 13 | `codex_reviews` defaults to `enabled` | `bin/gstack-config:251` | VALID |
| 14 | Paid evals (Step 6) run before Step 16's cheap generation checks | `ship/sections/tests.md.tmpl:54` (Step 6 evals), `ship/SKILL.md.tmpl:371-389` (Step 16 stage 1 runs declared generation/build commands) | VALID |
| 15 | `/investigate` has no flake, polluter, race or backlog content | grep of `investigate/SKILL.md.tmpl`: one table row `Race condition` at `:165`, no recipe; no flake/polluter/backlog hits | VALID (one-line mention of race, no recipe) |
| 16 | Preregistration/panels/budgets are gstack-internal | `scripts/ship-measure.ts:22-28` (admission budget), `scripts/judge-calibration.ts:42` (`split: 'dev' \| 'heldout'`), `test/helpers/periodic-exclude-data.ts:79-92` (`EVAL_POLICY`) | VALID |
| 17 | Doctor's codex fix line does not name the `OPENAI_API_KEY` login path | `bin/gstack-doctor:186` (`codex login, or export CODEX_API_KEY`), `TODOS.md:1639-1641` (CI-only use of `codex login --with-api-key`) | VALID |
| 18 | `./setup --status` always exits 0 | `setup:146` (`exit 0` unconditionally after status lines) | VALID |
| 19 | `--no-browser` is undocumented; only `GSTACK_SKIP_PLAYWRIGHT=1` exists | grep for `--no-browser` in `setup`: none; `setup:2071-2072` (`GSTACK_SKIP_PLAYWRIGHT`), `setup:1505-1512` (`GSTACK_SKIP_FONTS`) | VALID |
| 20 | `GSTACK_CODEX_MODEL` is honored by `select-model` | `bin/gstack-codex-probe:143-147`, `:456`, `:497` | VALID |
| 21 | No resolver reads `capabilities.questions` (basis for "no capy host") | only `scripts/host-config.ts:178` validates the value; no reader under `scripts/resolvers/` | VALID |
| 22 | Module-size ratchet is 800 lines / 150 per function; `gstack-next-version` 822, snapshot 865, `setup` 4662 must not grow | `test/module-size-ratchet.test.ts:2`; `wc -l`: 822 / 865 / 4662 | VALID |
| 23 | `nativeDispatchPrompt` already exists in `snapshot.json` | `bin/gstack-autoplan-snapshot.ts:661-669` | VALID |
| 24 | Running Bun 1.3.14 vs repo requirement 1.4.2 | `bun --version` → 1.3.14; `package.json:80-81` (`engines.bun: ">=1.4.2"`), `.github/workflows/arm-setup-smoke.yml:33` | VALID (note: `engines` is a range `>=`, not a pin) |
| 25 | "13 of 27 threads ran no gstack workflow beyond the installer" and the causal reading that tooling gaps (not low perceived value) explain it | No artifact in the repo or the prompt file; the plan offers no per-thread reason | UNVERIFIED (the load-bearing premise of the wave) |
| 26 | Effort: A 1.5 d, B 1.5 d, C 1 d, D 1.5 d CC+gstack | No basis given; PR B alone names 5 new binaries, 4 lib modules, 11 pre-gate checks each with a fixture | UNVERIFIED (see Section 3) |
| 27 | `bin/gstack-hermetic-run` can be "reused" by the `hermetic` check | no such file in `bin/`; `buildHermeticEnv()` exists at `test/helpers/hermetic-env.ts` | FALSE as worded (the bin is new; the helper is reusable) |
### What already exists (relevant to scope)
| Capability | Where | Plan's use |
|---|---|---|
| Readiness rows with fix lines and exit 1 on fail | `bin/gstack-doctor:441-456` | A1 adds a mode, not a tool. Correct. |
| Session kind classification and preamble branching | `bin/gstack-session-kind`, `generate-ask-user-format.ts:15-28` | A3 reuses; see Section 6 for the collision. |
| Raw plan wrapping | `bin/gstack-autoplan-snapshot.ts:413-417` | A4 documents it. Correct. |
| Native dispatch prompt in snapshot | `bin/gstack-autoplan-snapshot.ts:661` | A6 exposes it. Correct. |
| Queue-aware next version | `bin/gstack-next-version:78-106` | B2 `--next` wraps it. Correct. |
| Tree/wtree fingerprints | `bin/gstack-evidence:388-396`, `gstack-wtree` | B3 composes them. Correct. |
| Read-time inconsistency guard on review rows | `lib/review-evidence.ts:238-240` | A7 should cite it and add only the write-time derivation. |
| Opt-in code execution posture for repo-declared regen | `bin/gstack-version-bump:373-385` | B4/B8 should inherit it; they currently do not (Section 6). |
| Admission budget, calibration splits, EVAL_POLICY | `scripts/ship-measure.ts:22-28`, `scripts/judge-calibration.ts:42`, `periodic-exclude-data.ts:79-92` | D1 generalizes all three at once. |
| Hermetic env builder | `test/helpers/hermetic-env.ts` | B8 `hermetic` check; the bin wrapper is new. |
### Dream state
A parent agent on any machine runs one install line, greps one `gstack: ok` line, then runs `/autoplan` or `/ship` unattended and receives versioned, schema-checked JSONL it can act on without reading prose, while the owner receives a one-page numbered gate list. The plan's PR A reaches most of that for autoplan. PR B reaches it for ship if the receipt and restamp land together. PRs C and D are breadth that the dream state does not need in this wave.
### Alternatives considered against the plan
1. **Ship PR A alone as the wave, then B trimmed, then measure adoption before C/D.** Rejected by the plan implicitly (four stacked PRs, D1). This review recommends it (Section 1, 3).
2. **A fourth session kind (`unattended`) instead of redefining `headless`.** Not considered by the plan; required (Section 6).
3. **One artifact-schema module with a version field instead of six independently described files.** Not considered; required (Section 2).
4. **Chromium always lazy, dropping `--light` as a user-facing mode.** Not considered; optional simplification (Section 4).
5. **Premise table + three rows instead of 17 reality rows in plan reviews.** Not considered; recommended (Section 4).
## Sections 1-11
### 1. Is this the right problem? Could a reframing yield 10x?
**Finding 1.1 (High): the wave treats "13 of 27 threads ran nothing" as a supply failure and never measures whether PR A changes it.** Premise 25 is unverified; an equally plausible reading is that in a coordinator-driven serial queue, an agent with a working harness still prefers the repo's own tooling because the gstack workflows cost context and time they cannot see the return on. If that is the cause, PR A fixes the install lie and nobody runs autoplan anyway, and PRs B-D build on an unmeasured assumption. Fix: make adoption a release promise. A10 already writes `analytics/autoplan-timing.jsonl`; add a `session_kind` and `consumer` field there and to the ship receipt, and define P7: "of the next 10 Capy threads that install gstack, at least 6 run one headless workflow whose artifacts the parent consumed (grep/jq in the parent transcript)". Gate PR C and D on that number, not on the queue.
The thesis itself (machine-readable first) is right and is the 10x lever: a parent agent that can `jq` a decision list is the product. The reframing that follows is that the artifact contract is the product, and the tools are implementations of it. The plan has it backwards: 17 new binaries, zero schema.
### 2. Are the premises stated or assumed? Which could be wrong?
The repo-level premises are stated and almost all hold (table above; 23 VALID, 1 FALSE as worded, 3 UNVERIFIED). The ones that could be wrong and matter:
**Finding 2.1 (High): six output files, five JSONL shapes, no schema owner, no version field.** `tasks.jsonl` is said to reuse today's per-phase schema; `decisions.jsonl` and `findings.jsonl` list fields inline; the ship receipt is a fenced block; `pregate.json` and `timing.json` are described by example. In six months a parent agent written against PR A's `findings.jsonl` breaks when PR C adds `incident` and PR D adds `contributor`. Fix: one `lib/headless-artifacts.ts` that owns every schema with `schema_version`, exports the TypeScript types, and backs a `bin/gstack-artifact validate <file>` that `phase-close`, `gstack-ship-receipt` and `gstack-pregate` call before writing. Align `findings.jsonl` rows with the review-log row shape so A7's derivation is one function over one type.
**Finding 2.2 (Medium): A7's "becomes impossible" overstates.** `reviewFreshness` already returns UNVERIFIED for `clean` with open issues (`lib/review-evidence.ts:238-240`); the real gap is that the inconsistent row is still written and the log reader trusts the model's `unresolved` count. Fix: scope A7 to "bindReview rejects at write time when `--findings` is given; interactive rows without `--findings` are tagged `status_source: claimed`", and cite the existing read-time guard so a reviewer does not believe detection is new.
**Finding 2.3 (Medium): runtime-pin semantics are underdefined for the machine the promise is made on.** `engines.bun` is `>=1.4.2`, a range (package.json:80-81); the plan's message prints `pinned 1.4.2`. More important, the Capy cloud image ships Bun 1.3.14, so on the default image `--check` can never end `gstack: ok` unless the installer upgrades Bun, and A2 does not say it does. P1 ("on a healthy machine it ends with `gstack: ok`") is then unmeetable on the primary target. Fix: A2 runs `bun upgrade --version <pin>` (or `mise`) when the row fails and the pin is satisfiable, and A1 distinguishes `range` from `exact` pins in the message.
### 3. 6-month regret scenario
**Finding 3.1 (High): the effort table is not credible and the delivery shape depends on it.** PR B is five new binaries (`restamp`, `tree-receipt`, `regen`, `ship-receipt`, `pregate`), four lib modules, a queue-replay test with three PRs, a mirror repo and a CHANGELOG re-header, plus eleven pre-gate checks each with its own fixture, in 1.5 CC-days. PR D is a new skill with thirteen sections and three lib extractions in 1.5 days. If the real multiplier is 3-4x, PRs C and D sit behind A and B for weeks, the stacked base drifts, and the plan's own queue incident (restamp after reorder) hits this wave first, before B2 exists to fix it. Fix: re-estimate per binary with a fixture count; cut PR B to B1, B2 (`--version`, `--next` only; `--after` and `mirror` in a follow-up), B3, B5 and the four pre-gate checks whose incidents the plan actually names fixtures for (`regen`, `strays`, `literals`, `lanes`+pipefail). Ship `guards`, `patches`, `integrator`, `hermetic`, `links`, `lint`, `ratchets` as a second pre-gate PR after one repo has run the first four for a week.
**Finding 3.2 (Medium): the pre-gate becomes a second CI that drifts from the repo's real CI.** `lanes`, `guards`, `platform`, `integrator` re-derive what `.github/workflows/*.yml` already encode. Six months out, a repo changes its CI matrix and the pre-gate still reports `Tiers ran: unit serial` from a stale mapping. Fix: `lanes` and `platform` read the workflow files for the lane list (A1's `runtime pins` already parses them); the pre-gate never carries its own lane taxonomy.
**Finding 3.3 (Medium): seventeen reality rows make plan reviews longer at the moment context budget is the binding constraint.** The plan notes every template edit must clear `test/parity-suite.test.ts` and the budget ratchets but adds 17 conditional rows plus incident notes to four templates. The regret is a plan review that spends its budget on rows whose scope detector misfires (`Transaction semantics`, `Recall`, `Units` need a scope signal that does not exist today). Fix: ship the four rows that this review itself demonstrates value for and that need no scope detection (premise table with receipts, already-done git check, surface check, binding decisions); defer the other 13 to a follow-up gated on scope-detection evidence from `detectDxScope`.
### 4. Alternatives dismissed without sufficient analysis
**Finding 4.1 (Medium): D1 (four stacked PRs) is argued on reviewability, not on the queue cost the plan itself calls an incident.** Stacked PRs in a repo whose `/ship` cannot yet gate ahead (B2 `--after` is in PR B) means PR B restamps by hand after A merges, PR C after B, PR D after C: exactly the "manual stamp edits" P3 promises to eliminate. Fix: land A; land a minimal B (B1+B2 `--next`+B3+B5) next; dogfood on C and D so the queue tool is proven by the wave that ships it.
**Finding 4.2 (Low): `--light` as a user mode versus Chromium always lazy.** D9 picks flag plus lazy install. If `gstack-browser-ensure` exists, the flag is redundant: default installs skip Chromium and the first browser skill installs it. One less mode in the installer, one less `SKIP browser (light mode)` state, and A2 shrinks. Fix: make lazy the default; keep `--with-browser` for machines that want the 86 s up front.
**Finding 4.3 (Low): A9's free-text owner reply grammar (`all except 3=b 7=no`, `d10 yes`, `1a 2a`).** The plan correctly refuses to guess, but five accepted dialects is a parser that grows with every owner. Fix: one grammar (`<id><option>` tokens plus `all`), and the rendered gate list prints the exact reply shape it accepts; the quoted feedback replies become fixtures that must parse under that one grammar or be reported as unparsed.
**Finding 4.4 (Medium): D1 `/eval-plan` is a product, not a section.** Thirteen bullets, three lib extractions (`pricing`, `spend-ledger`, `measure-bar`), a QA soak section and sealed-path self-checks, for one known consumer (gbrain-evals). The alternative never weighed: ship the three steps that prevent spend incidents (preregistration checklist, `$0` stub dry run, priced pilot with the ledger) and leave instrument audit, sealed paths, memory preflight and soak to the repo that needs them until a second consumer appears.
### 5. Competitive risk
Harness vendors (Claude Code, Codex, Capy itself) are converging on native structured subagent protocols and plan modes. The durable advantage gstack can hold is the opinionated artifact contract and the incident-derived checks, not the plumbing. The risk is that a harness ships a native "decision list" format first and gstack's five ad-hoc JSONL shapes become a translation burden. Finding 2.1's single versioned schema module is the hedge: a stable contract a harness can adopt or map to. No further finding; nothing in the plan is uniquely exposed beyond that.
### 6. Risks the plan does not cover
**Finding 6.1 (Critical): redefining `headless` changes behavior for every existing `GSTACK_HEADLESS` and CI session.** Today `GSTACK_HEADLESS` (set by the eval/E2E harness, `bin/gstack-session-kind:47-50`) and `CI`/`GITHUB_ACTIONS` (`:59-62`) both classify as `headless`, and `headless` means BLOCK on any question (`generate-ask-user-format.ts:28`). A3 makes `headless` take the recommended option and continue. Every paid eval and E2E case that today proves a skill BLOCKS correctly at a question would instead auto-continue, which changes measured behavior under the same case names and silently rewrites what a PASS means. The plan's own validation discipline (AGENTS.md "never rejudge a failure to manufacture a pass") is violated by construction. Fix: add a fourth kind, `unattended`, honored only as an explicit `GSTACK_SESSION_KIND=unattended` override; leave `headless` semantics untouched; the autoplan headless section and `{{HOST_ADAPTATION}}` key on `unattended`. The Capy installer and skill set `unattended`. Rename P2's wording accordingly.
**Finding 6.2 (High): pin files that execute repo-declared commands drop the opt-in posture `gstack-version-bump` deliberately adopted.** `.gstack/generated.json` commands (B4), `.gstack/pregate.json` guard patterns and ratchet commands (B8), and `release_tool` (B1/B2) all run code from the target repo. `bin/gstack-version-bump:373-385` explains why the same class of action is behind `--regen-digest`: presence-sniffing in a hostile clone is arbitrary code execution. Fix: `gstack-regen`, `gstack-pregate` and `gstack-restamp` run declared commands only when invoked from `/ship` or with `--allow-repo-commands`; standalone invocations list the commands and exit 3 with `repo commands not executed (pass --allow-repo-commands)`.
**Finding 6.3 (Medium): B3's "same-modulo-stamps" verdict depends on `stamp_paths` listing everything `release_tool` touches.** A repo's restamp script that regenerates a lockfile or a docs index outside `stamp_paths` produces `TREE: changed gate=required`, and P3's "a tree that already passed never gets a second gate" fails for exactly the repos the policy was built for. Fix: the receipt classifies the diff against `stamp_paths` plus `.gstack/generated.json` paths, and reports the residual set by name.
**Finding 6.4 (Medium): B2 `--after <pr>` merges the predecessor's head into the branch.** Under `history: squash-ok` the PR then shows the predecessor's commits until it merges, and a later predecessor force-push leaves a merge commit the receipt's `PREDECESSOR MOVED` refusal cannot undo. Fix: gate-ahead builds the predecessor merge in a throwaway worktree (`gstack-wtree` already exists), gates there, records the predecessor head in `gate-ahead.json`, and never touches the PR branch's history.
**Finding 6.5 (Medium): D3 auto-detects contributor mode from "the maintainer set" that nothing defines.** Fix: read `CODEOWNERS` when present, else `gh api repos/:owner/:repo/collaborators` with push permission, else off; record the source in the finding.
**Finding 6.6 (Low): A2's thin wrapper "fetches the repo script"** before the repo exists on the machine. Fix: the wrapper clones (or reuses) the repo under the existing lock and then executes `bin/gstack-capy-install` from the clone; say so, because that is the only path that does not depend on a raw-content URL.
### 7. Scope: NOT in scope (confirmed and additions)
The plan's four exclusions hold and are correctly reasoned (capy host target verified against premise 21). This review adds: `--after`/`mirror` in B2, seven of eleven pre-gate checks, thirteen of seventeen reality rows, ten of thirteen `/eval-plan` bullets, and the free-text gate grammar dialects. See Required outputs.
### 8. Release promises vs. delivery
P1, P2 (with `unattended`), P3 (with 6.3/6.4), P4 (trimmed) are deliverable and testable as stated. P5 is deliverable at four rows. P6 is three products in one promise; split it into P6a (lane-check and evidence bundle, cheap and consumed by the coordinator today) and defer P6b (`/eval-plan`) and P6c (owner brief beyond the gate list, which A9 and D3's first paragraph already give). Nothing flagged beyond that.
### 9. Validation plan
Validation item 2 (headless autoplan end to end on this plan with an API-key Codex login) is the strongest proof in the document and should be the PR A merge gate. Item 3's queue replay is good but must include the squash-history case (6.4) and a `release_tool` that touches a non-stamp path (6.3). Item 1's incident fixtures need the incident-to-test map committed as a file (`test/fixtures/multi-agent-wave/INCIDENTS.md`) rather than a PR body, or it is lost on merge. Low; no further flag.
### 10. Gate decisions D1-D10 (auto-decided per instruction, recommended option taken unless amended)
D1 four stacked PRs: taken as recorded, amended by 3.1/4.1 (A, then minimal B, then C/D gated on adoption). D2 session kind not host: taken, amended by 6.1 (`unattended`, not `headless`). D3 installer in repo: taken. D4 `.gstack/*.json` pin files: taken, amended by 6.2 (opt-in execution). D5 pre-gate blocks by default: taken for the four shipped checks. D6 never approves: taken. D7 `/eval-plan`: taken as the name, scope amended by 4.4. D8 forced-CRITICAL list: taken. D9 lazy Chromium: taken, amended by 4.2 (lazy by default). D10 contributor mode on by default: taken, amended by 6.5 (defined maintainer source).
### 11. Trajectory and compounding
If PR A lands with a versioned artifact module and an adoption metric, every later tool has a contract to write to and a number to justify itself against. If it lands as specified, the wave compounds the wrong thing: more binaries over an unversioned contract, measured by nothing. That is the only strategic fork in the document.
## Required outputs
### NOT in scope (this wave)
- A `capy` host target (plan's exclusion, verified: `scripts/host-config.ts:178` is the only reader of `capabilities.questions`).
- Installing semgrep/gitleaks; native Windows `/cso`; closing issues from `investigate --backlog` (plan's exclusions, hold).
- B2 `--after <pr>` and `mirror` (deferred until B2 `--version`/`--next` has one week of use).
- Pre-gate checks `guards`, `patches`, `integrator`, `hermetic`, `links`, `lint`, `ratchets` (second pre-gate PR).
- Reality rows other than premise table, already-done, surface check, binding decisions.
- `/eval-plan` beyond preregistration checklist, `$0` stub dry run, priced pilot with ledger; QA soak section.
- Owner-reply dialects beyond one grammar.
### What already exists
See the Step 0 table. Net: every P0 item is a mode, a derivation or a composition over an existing tool, which is the right shape; the plan correctly avoids rebuilding doctor, next-version, evidence or the snapshot.
### Error & Rescue registry
| Error | Where | Rescue |
|---|---|---|
| `gstack: fail <components>` | A1 `--check` | Each FAIL row carries the exact command; installer reruns `--check` after fixing. |
| `FAIL runtime pins` on the default Capy image (Bun 1.3.14) | A1/A2 | Installer upgrades Bun when the pin is satisfiable (2.3); otherwise the row names the image. |
| `BLOCKED — AskUserQuestion unavailable` under `GSTACK_HEADLESS` | A3 as written | Preserved by introducing `unattended` (6.1); evals keep today's semantics. |
| `AUTOPLAN_RESULT: gate_pending` | A4 | Parent reads `decisions.jsonl`; never auto-approved (D6). |
| `review-log: status mismatch` exit 2 | A7 | Caller re-derives from `findings.jsonl`; interactive rows tagged `status_source: claimed`. |
| `artifact: schema_version N unsupported` | new `gstack-artifact validate` (2.1) | Parent upgrades gstack or pins the reader. |
| `repo commands not executed (pass --allow-repo-commands)` exit 3 | B4/B8/B2 standalone (6.2) | `/ship` passes the flag; operators opt in explicitly. |
| `TREE: changed gate=required (<paths>)` after a pure restamp | B3 | Receipt names residual paths; add them to `generated.json` or `stamp_paths` (6.3). |
| `PREDECESSOR MOVED <old> -> <new>` | B3 | Re-run gate-ahead in the throwaway worktree (6.4). |
| Spec loop at cap without 7+ | A8 | Headless default: one cheap pass over the listed issues; approval gate blocked by `spec not re-verified`. |
| Outside voice auth failure in unattended | A5 | `gstack-codex-login` from `OPENAI_API_KEY`; policy text names it. |
### Failure Modes registry
| Failure mode | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Eval semantics silently change under `headless` (6.1) | High if shipped as written | Critical: PASS redefined without a case change | Fourth kind `unattended`. |
| Artifact schema drift across PRs A-D (2.1) | High | High: parent agents break | One versioned schema module + validator. |
| Repo-declared command execution in untrusted clones (6.2) | Medium | High | Opt-in flag, exit 3 standalone. |
| PR A ships, adoption unchanged (1.1) | Unknown, unmeasured | High: wave built on a false premise | Adoption metric as P7; gate C/D on it. |
| Effort 3-4x the table (3.1) | High | Medium: C/D rot, manual restamps of the wave itself | Trim B; A then minimal B. |
| Pre-gate lane taxonomy drifts from CI (3.2) | Medium over 6 months | Medium | Derive lanes from workflow files. |
| Review context budget exceeded by 17 rows (3.3) | Medium | Medium: parity-suite red or review quality diluted | Four rows now. |
| `same-modulo-stamps` never true for repos with a regenerating release tool (6.3) | Medium | Medium: P3 unmet | Classify against generated registry too. |
| Bun pin unmeetable on default image (2.3) | Certain today | Medium: P1 unmet on primary target | Installer upgrades Bun. |
### Dream state delta
Dream: one install line, one grep, unattended autoplan/ship with versioned artifacts, owner sees a numbered gate list. Plan as written reaches: install line and grep (A1/A2), unattended autoplan (A3/A4, once 6.1 is fixed), owner gate list (A9/D3). Missing from the plan: the versioned contract (2.1) and the proof anyone drives it (1.1). Over-reaching relative to the dream: eleven pre-gate checks, seventeen rows, thirteen eval-plan bullets.
### Completion Summary
- Premises checked: 27; VALID 23, FALSE as worded 1 (#27), UNVERIFIED 3 (#25 adoption cause, #26 effort, and the range-vs-pin nuance in #24 counted under VALID).
- Findings: 1 Critical (6.1), 5 High (1.1, 2.1, 3.1, 6.2, and 3.1's shape consequence in 4.1 counted once), 9 Medium, 4 Low.
- Unverified claims remaining in the plan after this review: 2 (adoption cause, effort table).
- Gate decisions auto-decided: 10 of 10, 6 amended.
## Scores
| Dimension | Score | Note |
|---|---|---|
| Premises valid | 8 | Repo facts are right; the wave's causal premise is unmeasured. |
| Right problem | 8 | Machine-readable-first is the correct thesis; the contract should be the product. |
| Scope calibration | 4 | ~17 binaries, 1 skill, 4 schemas, 17 rows in 5.5 claimed days. |
| Alternatives explored | 6 | D1-D10 are real decisions; the trim-and-measure path and `unattended` were not weighed. |
| Risks covered | 5 | Headless/eval collision and repo-command execution are uncovered. |
| 6-month trajectory | 6 | Strong if the schema and metric land in A; weak if breadth lands first. |
## Tasks
```json
[
{"id": "T1", "priority": "P1", "component": "session-kind", "title": "Add `unattended` session kind as an explicit override; leave `headless` semantics unchanged; key autoplan headless section and HOST_ADAPTATION on it", "files": ["bin/gstack-session-kind", "scripts/resolvers/preamble/generate-ask-user-format.ts", "bin/gstack-skill-start", "test/gstack-session-kind.test.ts", "test/gstack-skill-start.test.ts", "test/spawned-consent-rule.test.ts"], "effort_human": "3 days", "effort_cc": "3 hours", "source_finding": "6.1"},
{"id": "T2", "priority": "P1", "component": "artifacts", "title": "One versioned schema module for tasks/decisions/findings/timing/pregate/ship-receipt with `gstack-artifact validate`", "files": ["lib/headless-artifacts.ts", "bin/gstack-artifact", "lib/autoplan-export.ts", "lib/review-evidence.ts", "test/headless-artifacts.test.ts"], "effort_human": "1 week", "effort_cc": "5 hours", "source_finding": "2.1"},
{"id": "T3", "priority": "P1", "component": "measurement", "title": "Adoption metric P7: record session_kind and consumer in autoplan-timing.jsonl and the ship receipt; gate PRs C/D on it", "files": ["lib/autoplan-export.ts", "bin/gstack-ship-receipt", "docs/TESTING_INTERNALS.md"], "effort_human": "2 days", "effort_cc": "2 hours", "source_finding": "1.1"},
{"id": "T4", "priority": "P1", "component": "pin-files", "title": "Repo-declared commands (generated.json, pregate.json, release_tool) run only under /ship or --allow-repo-commands; standalone exits 3", "files": ["lib/regen.ts", "lib/pregate/index.ts", "lib/restamp.ts", "bin/gstack-regen", "bin/gstack-pregate", "bin/gstack-restamp"], "effort_human": "2 days", "effort_cc": "2 hours", "source_finding": "6.2"},
{"id": "T5", "priority": "P1", "component": "delivery-shape", "title": "Re-estimate per binary with fixture counts; cut PR B to B1, B2 (--version/--next), B3, B5 and pre-gate checks regen/strays/literals/lanes", "files": [".gstack/tmp/autoplan/*/ceo-implementation.md"], "effort_human": "1 day", "effort_cc": "1 hour", "source_finding": "3.1, 4.1"},
{"id": "T6", "priority": "P2", "component": "installer", "title": "Installer upgrades Bun when the pin is satisfiable; runtime-pins row distinguishes range from exact pins", "files": ["bin/gstack-capy-install", "lib/runtime-pins.ts", "bin/gstack-doctor", "test/gstack-doctor.test.ts"], "effort_human": "2 days", "effort_cc": "2 hours", "source_finding": "2.3"},
{"id": "T7", "priority": "P2", "component": "tree-receipt", "title": "Classify the receipt diff against stamp_paths plus generated.json paths; name residuals", "files": ["lib/tree-receipt.ts", "bin/gstack-tree-receipt", "test/gstack-restamp-queue.test.ts"], "effort_human": "2 days", "effort_cc": "2 hours", "source_finding": "6.3"},
{"id": "T8", "priority": "P2", "component": "restamp", "title": "Gate-ahead in a throwaway worktree; never merge the predecessor into the PR branch; add squash-history replay case", "files": ["lib/restamp.ts", "test/gstack-restamp-queue.test.ts"], "effort_human": "3 days", "effort_cc": "3 hours", "source_finding": "6.4"},
{"id": "T9", "priority": "P2", "component": "plan-reviews", "title": "Ship four reality rows (premise table, already-done, surface check, binding decisions); defer 13 behind scope-detection evidence", "files": ["scripts/resolvers/plan-reality.ts", "plan-eng-review/SKILL.md.tmpl", "plan-ceo-review/SKILL.md.tmpl", "autoplan/sections/*.tmpl", "test/parity-suite.test.ts"], "effort_human": "3 days", "effort_cc": "3 hours", "source_finding": "3.3"},
{"id": "T10", "priority": "P2", "component": "review-log", "title": "Scope A7 to write-time derivation; tag rows without --findings as status_source: claimed; cite existing read-time guard", "files": ["lib/review-evidence.ts", "bin/gstack-review-log", "test/review-evidence.test.ts"], "effort_human": "1 day", "effort_cc": "1 hour", "source_finding": "2.2"},
{"id": "T11", "priority": "P2", "component": "pregate", "title": "Derive lanes and platform tiers from .github/workflows/*.yml rather than a pre-gate-owned taxonomy", "files": ["lib/pregate/lanes.ts", "lib/runtime-pins.ts"], "effort_human": "2 days", "effort_cc": "2 hours", "source_finding": "3.2"},
{"id": "T12", "priority": "P3", "component": "review", "title": "Define the maintainer set source for contributor mode (CODEOWNERS, then collaborators API, else off) and record it", "files": ["review/SKILL.md.tmpl", "bin/gstack-issue-guard"], "effort_human": "1 day", "effort_cc": "1 hour", "source_finding": "6.5"},
{"id": "T13", "priority": "P3", "component": "installer", "title": "Make Chromium lazy by default via gstack-browser-ensure; replace --light with --with-browser", "files": ["bin/gstack-capy-install", "bin/gstack-browser-ensure", "scripts/resolvers/browse.ts", "setup"], "effort_human": "2 days", "effort_cc": "2 hours", "source_finding": "4.2"},
{"id": "T14", "priority": "P3", "component": "gate-list", "title": "One reply grammar for the gate list; quoted owner replies become parse fixtures", "files": ["lib/gate-list.ts", "bin/gstack-gate", "test/gate-list.test.ts"], "effort_human": "1 day", "effort_cc": "1 hour", "source_finding": "4.3"},
{"id": "T15", "priority": "P3", "component": "eval-plan", "title": "Cut /eval-plan to preregistration checklist, $0 stub dry run, priced pilot with ledger; defer the rest", "files": ["eval-plan/SKILL.md.tmpl", "lib/spend-ledger.ts", "lib/pricing.ts"], "effort_human": "1 day", "effort_cc": "1 hour", "source_finding": "4.4"}
]
```
Recommendation: Proceed with PR A amended (an `unattended` session kind instead of redefining `headless`, one versioned artifact-schema module, and an adoption metric as a release promise), then a trimmed PR B, and defer the bulk of C and D until adoption is measured, because the repo-level premises hold and the thesis is right, but as written the wave silently changes eval semantics for every `GSTACK_HEADLESS` session and builds seventeen binaries over an unversioned contract that nothing proves a parent agent will drive.