mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
docs(designs): multi-agent wave plan — honest install, unattended autoplan, queue-aware ship, cheap-first gates (#3113)
* docs(designs): multi-agent wave plan with autoplan review record and artifacts Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(designs): record owner approval of the multi-agent wave gate (all recommended options) 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>
This commit is contained in:
1 parent
5b9b8372c4
commit
dfccf5a750
12 files changed
+1585
No files matched your search
@@ -0,0 +1,393 @@
|
||||
<!-- /autoplan restore point: "/workspace/gstack/.gstack/tmp/autoplan/main-autoplan-restore-20261010-203029.md" -->
|
||||
# gstack multi-agent wave (October 10, 2026): honest install, unattended autoplan, queue-aware ship, cheap-first gates
|
||||
|
||||
## URGENT, outside this plan
|
||||
|
||||
None. Six reviewer passes (CEO, DX, Eng; native Claude and outside gpt-6-astra each) raised no security or data-loss finding the plan does not own. The one live defect found during the run, the user-scope installer's `--check` certifying an absent install (`~/.capy/drive/user-garry-tan/skills/gstack/scripts/install.sh:7-16`, `if --check; then check_install; exit 0; fi`), is owned by A2/D3 and is worked around today by running the full install on every Capy machine (86 s).
|
||||
|
||||
## Implementation plan
|
||||
|
||||
### Context
|
||||
|
||||
Base: origin/main `91bbd9e` (v1.91.69.0). Input: feedback from 28 Capy threads that built gbrain and gbrain-evals over a month on cloud machines with no Claude Code harness, a coordinator thread merging PRs one at a time, full test gates on ephemeral Ubicloud VMs, and preregistered paid evals. 13 of the 28 threads ran no gstack workflow beyond the installer; several times the skills were not installed and the check did not say so. The others used each repo's own release tooling because `/ship` does not fit a shared serial merge queue.
|
||||
|
||||
Evidence gathered for this plan (2026-10-10, this machine, a fresh Capy cloud box):
|
||||
- **The install check lies by omission (P0-1 reproduced).** The user-scope Capy installer's `--check` (`~/.capy/drive/user-garry-tan/skills/gstack/scripts/install.sh`, a bare `test -x` chain) printed nothing and exited 1 in 0.004 s with nothing installed. The cause is `if --check; then check_install; exit 0; fi` under `set -e`: a failed probe exits 1 with no message, and through a pipe (`... | tail`) the caller sees the pipe's status, 0, which is the "exited 0 with nothing installed" report. A full install then took 86 s, most of it Chromium (114.7 MiB headless shell plus fonts). After it, `bin/gstack-doctor` reported `7 ok, 2 warn, 3 not configured, 0 fail` and exited 0 with Bun 1.3.14 against the repo's tested 1.4.2 (`warn`, not `fail`), no Node row, no telemetry row.
|
||||
- **What exists already is more than the feedback assumes.** `bin/gstack-doctor` (456 lines) has one `check_*` row per component with a fix line and exits 1 on `fail` (bin/gstack-doctor:3-16, :455-456). `bin/gstack-session-kind` already classifies `spawned | headless | interactive` (`GSTACK_HEADLESS`, `CI`, `GSTACK_SESSION_KIND=spawned`), and the preamble's AskUserQuestion rules branch on it (scripts/resolvers/preamble/generate-ask-user-format.ts:15-29). The snapshot tool wraps a raw plan in `## Implementation plan` automatically (bin/gstack-autoplan-snapshot.ts:415), so no literal heading is required of the user. `gstack-wtree` and `gstack-evidence` already compute reviewed-tree fingerprints (bin/gstack-evidence:13, :392). `bin/gstack-next-version` already reads the live PR queue and sibling worktrees (bin/gstack-next-version:80-105). `ship/sections/measure.md.tmpl:3-16` already holds a never-loosen rerun discipline. `codex_reviews` defaults to `enabled` (bin/gstack-config:251), so the outside voice is on by default.
|
||||
- **Where the gaps actually are.** `headless` means BLOCK on any question (generate-ask-user-format.ts:28); only `spawned` auto-continues, and `spawned` would auto-approve autoplan's final gate. Upgrade, telemetry, lake and proactive prompts are suppressed for `spawned` only (bin/gstack-skill-start:394). Review-log `status`/`unresolved` are model-written free text; nothing derives them from findings (bin/gstack-review-log:92-97, lib/review-evidence.ts:226-243). `bin/gstack-version-bump write` stamps VERSION, package.json and npm lockfiles only, with one hard-coded post-write hook (`scripts/gen-agents-digest.ts`, :374-402); there is no `release:restamp` hook, no `--after <pr>`, no tree-equality receipt in the PR body, and no CHANGELOG re-heading tool. Paid evals in `/ship` (Step 6) run before Step 16's cheap checks. `/investigate` has no flake, polluter, race or backlog content (investigate/SKILL.md.tmpl, 278 lines). No user-facing eval workflow exists; preregistration, panels, admission budgets and dev/held-out splits are all gstack-internal (scripts/ship-measure.ts:22-28, scripts/judge-calibration.ts:42, test/helpers/periodic-exclude-data.ts:79-90). Codex outside voice from an API key works today (`printenv OPENAI_API_KEY | codex login --with-api-key` then `gstack-codex-probe role-ready exec` printed `CODEX_SEL: gpt-6-astra`, `AUTH_OK`, `MODEL_OK` in 3.7 s) but no helper, doctor fix line or doc names it (grep: only TODOS.md:1640 and CI tests).
|
||||
- **Owner input received during review (binding).** Garry's second message adds: accept a subagent on another model family as a full outside voice and never report a missing Codex CLI as degraded; store learnings and telemetry durably on ephemeral machines or say plainly they are skipped; allow guarded tool calls when no publication guard is installed and say so; list the linked issue's deferred asks in the gate template; name the ship mode `--queue-mode stamp-at-merge`. These are folded into A3, B1, B3, C2 and D1 below.
|
||||
- **Repo constraints that shape every item.** SKILL.md is generated from `.tmpl` (edit templates, run `bun run gen:skill-docs`). New logic goes in `lib/*.ts` behind thin `bin/` entrypoints; `test/module-size-ratchet.test.ts` caps *registered* owner modules at 800 lines and 150 per function (test/helpers/module-size.ts:202), so every new module this wave adds is registered in `test/fixtures/module-size-ratchet.json`, and `bin/gstack-next-version` (822 lines), `bin/gstack-autoplan-snapshot.ts` (865) and `setup` (4662), unregistered today, are not grown. Four skills have no parity headroom at all (ship 1.539/1.539, review 1.234/1.2345, autoplan 1.129/1.1305, qa 1.127/1.1272 union-byte ratios against `test/helpers/parity-harness.ts` guards, measured during this review), so prose added to them must be replaced byte for byte or printed by a bin instead. State paths resolve only through `lib/state-root.ts` / `bin/gstack-paths`. Prose in templates cannot be pinned by sentence (docs/test-value-bar.md); new rows get structural tests plus eval cases. Every template edit must clear `bun test test/parity-suite.test.ts` and the context-budget ratchets.
|
||||
|
||||
The wave has one thesis: **gstack must be a tool a parent agent can drive and verify without reading prose.** Every artifact below is machine-readable first (one grep-able line, a JSONL file, a fenced receipt) and human-readable second.
|
||||
|
||||
### Release promises (the finish line)
|
||||
|
||||
- **P1. The install check cannot lie.** On a machine with nothing installed, one command prints one PASS/FAIL line per component with the exact fix command, ends with `gstack: fail <components>` and exits non-zero. On a healthy machine it ends with `gstack: ok <revision>`. A parent agent confirms a subagent machine by grepping that one line. Planning-only machines skip Chromium.
|
||||
- **P2. /autoplan, the plan reviews and /cso run unattended on a host with no Claude Code harness.** Under the new `unattended` session kind they need no hooks, no AskUserQuestion, no Skill or Agent tool, prompt for nothing, and write `plan.md`, `review-record.md`, `tasks.jsonl`, `decisions.jsonl`, `findings.jsonl`, `timing.json` and a `run.json` manifest, all under one versioned schema with a validator, that a parent agent consumes without reading prose. The approval gate is never auto-approved: it is written as a gate list and the run ends.
|
||||
- **P3. "Ship, next in queue at <version>" works.** The stamp happens when the PR is next, from main's VERSION at that moment, through the repo's own release tool when it has one, across every stamp location at once. The PR carries a tree-equality receipt and a handoff record with identifiers first. Zero manual stamp edits; a pure restamp of a gated tree is reported as `gate-reuse: eligible` with the identity it rests on (tree modulo stamp and generated paths, runtime pins, lockfile, CI config), and the coordinator reuses the gate instead of paying for it again.
|
||||
- **P4. Cheap checks run before any paid or remote gate.** A two-minute pre-gate replays every mechanical-drift incident in the feedback (stale goldens, scratch files, pipe-hidden failures, unrun platform lanes, old constants in nightly-only tests, dropped vendored hunks) and refuses to start a gate while any fails.
|
||||
- **P5. Plans, reviews and investigations carry the gbrain lessons as required rows,** each one tied to the incident that earned it, each failing the review when missing.
|
||||
- **P6. Measurement, coordination and the owner's view get first-class support:** a preregistered-eval workflow with a budget ledger, lane-ownership and evidence-bundle tools, and a one-page owner brief with numbered decisions.
|
||||
- **P7. Adoption is measured, not assumed.** The durable carriers are the ones that already leave the machine: `run.json.consumed_by` in the parent-chosen `--out` directory and the ship receipt's `session_kind` and `artifacts_consumed: <n>/<m>` fields in merged PR bodies; `gstack-ship-receipt census --repo <r> --since <tag>` computes the number from merged PRs through `gh`, deduplicated by run and thread id, against an owner-maintained thread cohort (threads that never invoke gstack count in the denominator); state-root analytics stay a bonus labeled `durable=no`. The tier-3 items in PRs D and E start only after the first ten Capy threads on B-runner show at least six unattended runs whose artifacts a parent consumed (see UC1).
|
||||
|
||||
### Delivery shape
|
||||
|
||||
Seven pull requests, merged in order; each is shippable alone and each later one assumes the earlier one merged. Every item below carries a tier: **tier 1** ships in its PR; **tier 2** ships in the same PR only if green and otherwise moves to the next PR without blocking; **tier 3** waits for the P7 adoption number. The Eng review sized B and C at two review sittings each (3,500–6,000 LOC `[estimated]`), so each is split at its natural seam; the halves can be built in parallel on fresh machines, stacked. Queue cost: six extra full gates over a single PR, two of them made cheaper by the pre-gate they ship. The stack dogfoods itself: C-stamp's restamp tool stamps every PR behind it. (See decisions D1, D12 and UC1.)
|
||||
|
||||
| PR | Items | Theme | Depends on |
|
||||
|---|---|---|---|
|
||||
| A | A1–A3 | Honest install (small, lands first) | — |
|
||||
| B-core | B0, B1, B7, B9, B10, B12 | Artifact contract, `unattended` kind, gate list, derived review log | A (`GSTACK_LAUNCH`) |
|
||||
| B-runner | B2, B3, B4, B8, B11 | Unattended runner, outside-voice runner, checklists, spend ledger | B-core |
|
||||
| C-stamp | C1, C2, C3, C5, C7 | Ship policy, restamp, receipt, handoff, history policy | B-core (schemas) |
|
||||
| C-pregate | C4, C6, C8 | Regen registry, issue links, pre-gate | C-stamp (trust boundary) |
|
||||
| D | D1–D3 | Plan rows, sideways review, investigate recipes | B-runner (tier 2 rows need `scope`) |
|
||||
| E | E1–E3 | `/eval-plan`, coordination tools, owner brief | B-runner (ledger), C-stamp (bundle) |
|
||||
|
||||
Every PR's acceptance lists the union byte delta per skill it touches and the `CARVE_GUARDS`/`capture-context-budget` refresh commit, because ship, review, autoplan and qa have zero parity headroom today. Shared files have one owner each: `bin/gstack-skill-start` (B-core), `scripts/resolvers/outside-voice*.ts` (B-runner), `ship/SKILL.md.tmpl` and `ship/sections/*` (C-stamp, then C-pregate), the ratchet fixture (one commit per PR by that PR's author).
|
||||
|
||||
### PR A: honest install (P0-1)
|
||||
|
||||
**A1. `gstack-doctor --check`: the one-line verdict.** Add a `--check` mode to `bin/gstack-doctor` that prints one line per component as `PASS <component>` or `FAIL <component> — fix: <exact command>` and ends with `gstack: ok <revision>` or `gstack: fail <component,component>`, exit 1 on any FAIL, exit 0 only when every required component passes. Components: Claude skills, Codex skills, the Astra behavioral patch (grep of the generated patch heading, as the Capy installer does today), browse bundle, Chromium launch and render (reuse setup's launch probe plus a `setContent` smoke render through `bin/gstack-render.ts`), runtime pins, privacy state. Readiness is relative to a requested workflow: `--for <skill>` (or `--require <csv>`) names the components that skill needs (planning skills: both host profiles and the patch; browser skills: plus the browse bundle and Chromium; `/cso`: plus the native helper) and the trailer names the workflow it certified (`gstack: ok 1.91.70.0 for=autoplan`). Without either flag the required set is the installed hosts, and the trailer says so. Wrong-host, missing-authentication and incompatible-runtime fixtures are tested, not just missing files. One component table feeds the row ids, `--for` resolution, `--require`, the trailer and the `--json` keys, so a parent never maps between vocabularies. The doctor stays Bash 3.2 and reports without Bun (bin/gstack-doctor:22; the P1 scenario is a machine with Bun absent or below floor), so the table's source of truth is `lib/doctor-components.ts` and the file the doctor sources, `bin/gstack-doctor-components.sh`, is generated from it at build time with a freshness test, the way the agents digest is. `--check` is fixtured with `bun` removed from `PATH`, with an empty installed-host set (never `ok`), and with an unmet `--revision` (never `ok`). The `gstack runtime` row distinguishes gstack's security floor (1.3.3) from its supported minimum (1.4.2, bin/gstack-bun-version.sh:15); 1.3.14 is FAIL in `--check`. `--for <skill>` prints `required: claude codex patch` before the rows, and each row is `PASS | FAIL | SKIP <id>` where SKIP marks a component the selected workflow, host or runner does not need (`not_applicable`) or an optional one that is absent (`optional`). A healthy Capy machine with Claude subagents as the outside voice and no Codex CLI passes `--for autoplan`. `--json` emits the same rows as objects; `--check` never runs the paid Codex probe unless `--live` is also passed. Silent success becomes impossible: every path prints the trailer. `./setup --status` exits 1 when any install row reports broken section links instead of always 0 (setup:136-146).
|
||||
- New rows: `gstack runtime` (Bun against gstack's own floor and tested version, today's `bun` row) and `project pins`, which reads the *target repository's* (named by `--project <path>`, default the git toplevel of the cwd, never the gstack checkout) `packageManager`, `engines`, `.tool-versions`, `.nvmrc`, `.bun-version` and the CI matrix (`bun-version:` / `node-version:` in `.github/workflows/*.yml`) through new `lib/runtime-pins.ts`, compares with the running Bun and Node, distinguishes a range (`>=1.4.2`) from an exact pin, keeps platform-specific CI pins with their lane (the Windows workflow pins Bun and Node separately, .github/workflows/windows-free-tests.yml:83, and must not block a Linux run), prints each effective constraint with its source, and reports `FAIL project pins — bun 1.3.14 outside engines.bun >=1.4.2 (package.json:80; CI windows-free-tests.yml:89 pins 1.4.2); fix: bun upgrade --version 1.4.2`. A mismatch against the project's pin is FAIL in `--check` (a gate on an unpinned runtime is the incident), `warn` in the dashboard. `privacy` prints `telemetry=<mode> artifacts_sync=<mode> codex_reviews=<v> update_check=<v>` from `gstack-config get`, values only (doctor's clamp rule). `cores` prints the CPU count and, under 8 cores, the remote-gate recommendation (consumed by E2).
|
||||
- Tests: `test/gstack-doctor.test.ts` gains the `--check` fixtures (healthy, each missing component, runtime mismatch), pinned on the trailer tokens and exit codes, not prose.
|
||||
|
||||
**A2. The Capy installer lives in the repo.** New `bin/gstack-capy-install` (bash, Capy cloud Ubuntu only, under 200 lines): clone or reuse `~/.local/share/gstack` under the existing lock (never reset, never auto-pulled), `./setup --host claude --no-prefix`, `./setup --host codex --model gpt-6-astra`, then `gstack-doctor --check --for <skill>` (default `--require claude,codex,patch`). Chromium is lazy by default: the installer skips it (`GSTACK_SKIP_PLAYWRIGHT=1 GSTACK_SKIP_FONTS=1`), new `bin/gstack-browser-ensure` installs it on first use from the browse preflight in `scripts/resolvers/browse.ts`, and `--with-browser` installs it up front for machines that want the 86 s now; `--only <skill>` installs the host profiles and checks only that skill's declared dependencies. `./setup` gains the documented `--no-browser` flag (today only the undocumented env var, setup:2072). Runtime preflight runs before `./setup`, because `setup` exits at once when Bun is absent or below its floor (setup:296): the installer checks Bun, Node and `jq`, installs or upgrades Bun through `setup`'s own checksum-verified recipe (`BUN_VERSION=<v> bash install`, setup:296-305; `bun upgrade --version` does not exist on 1.3.14) when gstack's supported minimum or the project's pin requires it and the two agree, and stops with both constraints printed when they conflict. The default Capy image ships 1.3.14, so P1 is unmeetable there otherwise. `--revision <ref>` pins the checkout for a fleet; an existing checkout at a different revision is reused and reported as `gstack: ok <installed> (requested <ref>; run gstack-upgrade to move)`, never pulled. Chromium skip precedence is one sentence in `setup --help`: `--no-browser` sets `GSTACK_SKIP_PLAYWRIGHT=1` internally; the installer passes it unless `--with-browser`. Planning-only install time is measured separately from browser bootstrap and printed. The user-scope Capy skill becomes a thin wrapper: clone or reuse the repo under the same lock, then run `bin/gstack-capy-install` from the clone (no raw-content URL), and grep `gstack: ok`. Its SKILL.md tells a parent agent to confirm each subagent machine with that grep and never to pipe `--check` through `tail`.
|
||||
- Acceptance: a fresh machine with nothing installed fails loudly, naming each missing component; the default install finishes without downloading Chromium and reports `SKIP browser (lazy; gstack-browser-ensure installs on first use)` rather than PASS; the first browser skill run installs it.
|
||||
|
||||
**A3. Durable state on ephemeral machines.** The state-root chain already exists (`GSTACK_STATE_ROOT` > `GSTACK_HOME` > `GSTACK_STATE_DIR` > `CLAUDE_PLUGIN_DATA` > `~/.gstack`, lib/state-root.ts), so learnings, decisions, review logs and timing are a defaults-and-honesty fix: `gstack-skill-start` prints `STATE_ROOT: <path> durable=<yes|no>` once per session, detecting an ephemeral root (a Capy cloud home, a tmpfs, or `GSTACK_EPHEMERAL=1`), and in unattended runs prints `learnings: skipped (state root is ephemeral; set GSTACK_STATE_ROOT)` instead of writing silently to a disk that will vanish. Ephemerality is detected from an environment marker the host sets (`GSTACK_EPHEMERAL=1`, which the Capy installer exports into the skill's launch configuration), not a path heuristic. A durable root is opt-in: the owner sets `GSTACK_STATE_ROOT` (the Capy drive is governed by Capy's own drive policy, which excludes memory and verification evidence, so the installer only prints the option and never writes there by default). Because a child shell's export cannot configure the parent's later commands (lib/state-root.ts:22 reads each process's own environment), the installer prints a machine-readable launch configuration (`GSTACK_LAUNCH: GSTACK_SESSION_KIND=unattended GSTACK_STATE_ROOT=<path> GSTACK_EPHEMERAL=1`) that the parent supplies to every gstack command it launches; the doctor `state root` row shows the durability verdict.
|
||||
|
||||
### PR B (B-core, B-runner): unattended autoplan and the artifact contract (P0-2)
|
||||
|
||||
Item ids are stable across review phases, so B5 and B6 (folded into B2 and B0 during the CEO phase) are not reused.
|
||||
|
||||
**B0. One versioned artifact contract.** New `lib/headless-artifacts.ts` owns every schema this wave writes (`tasks`, `decisions`, `findings`, `timing`, `run` manifest, `pregate`, `ship-receipt`), each with `schema_version`, exported TypeScript types, and `bin/gstack-artifact validate <file>`, which `phase-close`, `gstack-ship-receipt` and `gstack-pregate` call before writing. `run.json` is the manifest: required phases, each reviewer's outcome and model, input hashes, decision and finding counts, artifact paths and hashes; `gstack-artifact validate run.json` is the deterministic completion check a parent runs first, and it fails on an interrupted run, a missing reviewer, a stale artifact or a findings total that disagrees with the file. `findings.jsonl` rows share the review-log row shape so B7's derivation is one function over one type, and every finding carries a run-bound id (`<run>-<phase>-F<n>`) that decisions, tasks and the URGENT block reference. `tasks.jsonl` rows carry run-bound ids, `depends_on`, `acceptance` (the command or check that proves the task), `findings` (ids) and `blocked_by` (decision ids); `validate` checks referential integrity across the files and binds each reviewer's immutable output file (path and hash) into `run.json`, so a count that merely matches cannot hide an omitted finding. One transport contract for every new command: `--json` means JSON only on stdout and diagnostics on stderr; without it, one terminal line `GSTACK_RESULT: skill=<name> status=<complete|gate_pending|incomplete|interrupted|refused> run=<dir>` where the run directory holds fixed file names; a shared exit-code table (0 ok, 1 fail, 2 usage, 3 refused or needs a flag) printed in every new bin's `--help`, with a `--help` coverage test; every error line carries a reason code with a `docs/troubleshooting.md` anchor and a `fix:` clause from a new sibling table `lib/result-codes.ts` (same `{anchor, summary, fix}` shape as `lib/gate-outcomes.ts`, whose `state` enum is for gate outcomes only; `test/troubleshooting-anchors.test.ts` covers both tables; the artifact errors are an enumerated list, not a wildcard): `GUARD_NOT_INSTALLED`, `REPO_COMMANDS_NOT_AUTHORIZED`, `POLICY_SOURCE_UNAVAILABLE`, `RESTAMP_PREDECESSOR_MOVED`, `GATE_REUSE_NOT_ELIGIBLE`, `QUEUE_STALE`, `REVIEW_STATUS_MISMATCH`, `EXECUTION_UNKNOWN`, and the `ARTIFACT_*` list. The terminal-line rule applies to workflow commands (`run`, `next`, `submit`, `answer`, `restamp`, `pregate`); pure query commands (`schema`, `validate --json`, the doctor trailer) keep their own documented output. Every reviewer returns canonical structured findings (the `findings.jsonl` row shape) beside its prose, and the aggregate reconciles their exact ids, keeping disagreements and resolutions as separate fields; a hash of the prose proves which file was kept, not completeness, so the reconciliation is the completeness check. `validate` also rejects duplicate ids, dependency cycles, malformed JSONL tails, and artifact paths that escape the run directory (symlinks included). Formats are versioned from day one with every reader and writer enumerated (`tasks` v2 adds `depends_on`, `acceptance`, `findings`, `blocked_by` over today's `source_finding` rows, scripts/resolvers/tasks-section.ts:77; v1 rows still aggregate), and `bindReview` validates a `--findings` file before it consumes the start token (lib/review-evidence.ts:188). `gstack-artifact schema <name>` prints the JSON Schema for the version it validates, so a parent on another machine can read the contract; `gstack-artifact ack <run.json> --consumer <thread-id>` is the idempotent consumption report behind P7 (it sets `run.json.consumed_by` and appends to `analytics/autoplan-timing.jsonl`); `validate --json` returns `{valid, errors:[{code, path, message}]}`.
|
||||
|
||||
**B1. A real unattended session kind.** A fourth kind, `unattended`, honored only as the explicit `GSTACK_SESSION_KIND=unattended` override (bin/gstack-session-kind:17-23). `headless` (set by `GSTACK_HEADLESS`, `CI`, `GITHUB_ACTIONS`, and by the eval harness) keeps today's BLOCK semantics, so no paid eval or E2E case changes meaning. `unattended` gets its own semantics in the preamble and `gstack-skill-start`:
|
||||
- `unattended` is added to the kind whitelist in `gstack-skill-start` (bin/gstack-skill-start:93 coerces any unknown kind to `interactive`, the inverse of the intent), an unknown kind prints `SESSION_KIND: interactive (unknown kind '<x>')` so a mixed-version fleet is loud, the explicit override outranks `GSTACK_HEADLESS`, and the eval harness never sets it.
|
||||
- No prompts of any kind: update check, upgrade offer, lake intro, telemetry, proactive and routing blocks are suppressed as they are for `spawned` (bin/gstack-skill-start:96, :394). Privacy is enforced at the writers and sync entrypoints, not only at the prompts: the analytics and artifact-sync calls that run outside the onboarding guard (bin/gstack-skill-start:179, :310) and the review-log sync enqueue (bin/gstack-review-log:99) all check the session kind and skip with an explicit line, inherited enabled settings and pending queue work included; the test instruments the egress sinks and asserts no subprocess or network call, not merely the absence of a prompt. The upgrade offer becomes one line at skill end (`UPGRADE_AVAILABLE: <version>; run /gstack-upgrade`). An unanswered consent is `off`; the preamble never records consent in headless.
|
||||
- Questions: a `(recommended)` option is taken and the decision appended to `decisions.jsonl` with `kind: "auto"`; a question with no recommendation, a consent, a destructive option or an approval gate is appended with `kind: "gate"` and `status: "pending"`, and the skill stops at its next gate with the pending list printed. Unattended never approves and never publishes or syncs data. `spawned` and `headless` keep today's behavior.
|
||||
- The Capy adaptation text in the preamble (no Skill tool, no slash commands, plain text is not delivered) moves from the user-scope skill into a `{{HOST_ADAPTATION:unattended}}` resolver so every host's render carries it.
|
||||
- The autoplan frontmatter `PreToolUse` hooks deny every matched call with `Autoplan publication guard is unavailable` when `autoplan/bin/phase-publication-hook` is absent. A host that honors the frontmatter but lacks the file is blocked outright. The hook renders only for the Claude host (scripts/resolvers/composition.ts:11) and on Capy it is present but never executed, so "file absent" is the wrong condition: on Claude Code, deny-on-absent stays (a broken install must not pass as an absent one, autoplan/bin/phase-publication-hook:4-9); on every host that does not execute hooks, and whenever `SESSION_KIND=unattended`, `gstack-skill-start` prints `autoplan guard: not enforced by this host; publication order is unverified (GUARD_NOT_INSTALLED)` once, and B0's validator requires that line in `review-record.md` for an unattended run. That satisfies the owner's ask (guarded calls proceed and the run says so) without weakening the one host that enforces the guard.
|
||||
- Tests: `test/gstack-session-kind.test.ts`, `test/gstack-skill-start.test.ts`, `test/spawned-consent-rule.test.ts` gain the unattended cases; negative controls prove unattended never emits the telemetry prompt and that `GSTACK_HEADLESS` still blocks.
|
||||
|
||||
**B2. autoplan unattended profile and the output files.** A new lazy section `autoplan/sections/unattended.md.tmpl` (loaded when `SESSION_KIND: unattended`) and a `bun gstack-autoplan-snapshot export` step implemented in new `lib/autoplan-export.ts` (the snapshot bin stays at 865 lines), writing through B0's schemas. Export writes, beside `ACTIVE_PLAN`: `plan.md` (the `## Implementation plan` section only; warn above 30 KB), `review-record.md` (the `## Review record` section and decision audit trail), `tasks.jsonl` (the aggregated per-phase tasks, same schema as today's `tasks-<phase>-*.jsonl`), `decisions.jsonl` (every auto, taste, user-challenge and gate item with `id`, `phase`, `kind`, `recommended`, `options`, `cost`, `status`), `findings.jsonl` (every finding with `phase`, `voice`, `model`, `severity`, `file`, `line`, `title`, `resolution`), `timing.json` (per-phase start/end, outside-voice wall time, total; see B10) and `run.json`. One executable owns the unattended loop as an explicit, durable state machine: `bin/gstack-autoplan` (thin, over `lib/autoplan-run.ts`). On a host where the parent dispatches the native reviewer itself (Capy: the native voice is a subagent the parent starts from the prompt file, exactly how this review ran), the loop is `next` and `submit`: `next --out <dir>` prepares the current phase (snapshot, methodology, prompt), persists the dispatch intent with an attempt id, prints the voice, the prompt path, the expected result path and the model family required, and exits with `GSTACK_RESULT: status=awaiting_result`; `submit --out <dir> --phase <p> --voice native|outside --result <file> --model <id> --attempt <id>` validates the result (full-read receipt, canonical findings, model family), binds its hash into `run.json` atomically, runs the B11 reconciliation and `phase-close`, and advances. `run --plan <file> --out <dir> [--runner ...] [--outside-model <id>] [--deadline <min>] [--spend-cap <usd>] [--plan-heading <text>]` is `next` in a loop and exists only when a runner can execute both voices itself. `status --out <dir>` prints the manifest. `answer --out <dir> --gate-rev <rev> --reply "<grammar>"` rejects a stale, conflicting or partially parsed reply atomically with no writes; applies the choices; and when a non-recommended choice changes reviewed content (scope, architecture, test plan), recomputes the affected review-input identities and reopens the dependent phases with Eng last, the same rule today's option D follows, instead of finalizing a plan no reviewer evaluated. Only a reply that changes nothing reviewed finalizes `plan.md` and `run.json.status` without another paid phase. `resume --out <dir>` takes exclusive ownership of the run (a second writer is refused), reconciles any attempt whose dispatch intent exists without a terminal record as `EXECUTION_UNKNOWN` (the provider may have finished and charged; snapshot hashes prove input identity, not completion), and never redispatches an ambiguous attempt automatically. `--deadline` cancels every child the runner started; spend by a host-dispatched subagent is reported as `unknown` in the ledger, not as zero. The minimal shared spend ledger (`lib/spend-ledger.ts`: atomic `spent + reserved + next_estimate <= cap` under a file lock, reservations retained for unsettled attempts after a crash) lands here, not in PR E, because `--spend-cap` needs it. `--out` must be a directory the parent chose (durable or not, reported per artifact class); nothing is written beside `ACTIVE_PLAN` by default. The final approval gate in unattended runs is written as a gate list (B9) into `decisions.jsonl` and printed; the run ends with `GSTACK_RESULT: skill=autoplan status=gate_pending run=<dir>`. Lanes read `tasks.jsonl`, not the plan. `docs/unattended.md` is the parent-agent quickstart: install, run, validate, answer, ack, five copy-paste commands, linked from the user-scope Capy skill. Each run also appends `session_kind` and, when the parent reports it, `consumer` to `analytics/autoplan-timing.jsonl` (P7).
|
||||
- `cso` in unattended runs prints `GSTACK_RESULT: skill=cso status=<...> run=<dir>` at exit (the run directory holds `findings.jsonl` and the run id), and `gstack-doctor --check` lists the CSO native helper as an optional component (`SKIP cso (toolchain absent)`, never silent).
|
||||
- A plan with any layout is accepted (already true: snapshot `init` wraps raw text; document it in the headless section) and `--plan-heading <text>` names a custom heading when a repo's plans use one.
|
||||
- Acceptance: this plan, reviewed on this machine with no Claude Code harness, produces the files; a parent agent runs `gstack-artifact validate run.json`, lists pending decisions with `jq 'select(.status=="pending")' decisions.jsonl`, and never reads the record. Interrupted-run, missing-reviewer and omitted-finding fixtures fail validation; `test/autoplan-run-recovery.test.ts` kills the runner before and after result persistence and asserts no automatic duplicate charge; `test/autoplan-run-concurrency.test.ts` covers two writers, revision conflicts and exactly-once `ack`; `test/autoplan-answer-invalidation.test.ts` proves a scope-changing answer reopens only the affected phases and a stale reply writes nothing. New bash bins and their tests are classified for the Windows subset (`scripts/test-free-shards.ts --windows-only`).
|
||||
|
||||
**B3. A pluggable outside voice, with a model parameter.** The outside voice becomes a runtime choice, not a render-time one: `outsideVoiceFor(ctx)` picks the provider when `gen:skill-docs` renders (scripts/resolvers/outside-voice.ts:8-12) and the rendered prose shells out to `codex` directly (scripts/resolvers/outside-voice-steps.ts:177), so a per-run `--runner` cannot live there without rendering three branches into every zero-headroom skill. New `bin/gstack-outside-voice run --runner <r> --prompt <file> --out <file> [--model <id>] [--result <file>]` over `lib/outside-voice-runner.ts`; the resolver names the bin and the runner table once. Every runner implements one adapter interface: declared capabilities (repository access or supplied-input only), input digest, actual model metadata, cancellation, terminal result and usage; the `api` runner either runs a bounded repository tool loop or is labeled `supplied-input review` in the record, never silently equal to a repo-reading review. Runners: `codex-cli` (today's path), `api` (a direct call with `OPENAI_API_KEY` or `ANTHROPIC_API_KEY` through new `lib/outside-voice-api.ts`, model from `--outside-model`), or `host-subagent` (the host runs a subagent on another model family from the paste-ready prompt and returns its output file; the parent passes `--outside-result <file> --outside-model <id>`). A completed `host-subagent` or `api` review is a full outside voice: `OUTSIDE_STATUS: completed provider=<runner> model=<id>`, never `[single-model]` or degraded, and the model id is recorded in `findings.jsonl` and the voices log row. A missing Codex CLI is reported as `codex-cli unavailable; using <runner>` only when another runner is configured; with none, today's unavailable text stands. New `bin/gstack-codex-login`: `printenv OPENAI_API_KEY | codex login --with-api-key`, then `codex login status`, exit non-zero with the probe's hint on failure; `outsideVoiceFailurePolicy()` (outside-voice.ts:217) and the doctor codex row (bin/gstack-doctor:186) name it as the unattended remedy. `--outside-model <id>` sets `GSTACK_CODEX_MODEL` for the CLI runner (bin/gstack-codex-probe:144-147 already honors it). The outside voice stays on by default (`codex_reviews: enabled`). The same-family rule holds for every runner: the outside model must differ in family from the native reviewer, and the record says which was which. Runner selection is decoupled from host identity (today `outsideVoiceFor` picks the provider from the harness, scripts/resolvers/outside-voice.ts:8-12), so a Codex-host render can still receive an outside voice from a Claude subagent and a Capy run can use either. The Capy dispatch path (a Capy subagent reading the native prompt file with its own file-reading tool) is the one this plan's own review ran on; the PR body records it.
|
||||
|
||||
**B4. Condensed operative checklists.** Each review skill gets `sections/checklist.md.tmpl`, at most 200 lines, holding the executable steps and required outputs of that phase; the deep methodology stays in `review-sections.md`. In unattended runs and under an explicit `light` mode the phase loads the checklist plus only the deep sections the detected scope needs (the manifest gains a `scope` field per section: `always`, `ui`, `dx`, `db`, `perf`, `security`, `incident`), and prints one index line: `Skipped sections: <ids> (scope: <detected>)`. Interactive runs are unchanged. `nativeDispatchPrompt` already exists in `snapshot.json`; `bun gstack-autoplan-snapshot prompt <phase>` prints it so a parent can paste it to a subagent. The manifest's `scope` field is optional with `always` as the default, its "passive, no machine predicate" note is updated with the schema, and the only consumer is the runner (interactive renders ignore it; `test/qa-lazy-sections.test.ts:127` keeps asserting passive resolution for them). A checklist is "replace, never add": the methodology bytes it summarizes leave the eager text, and an obligation-to-section map (every required output names the section that produces it) proves nothing required was dropped, with a conservative fallback that loads the deep section whenever scope detection is uncertain and behavioral negative controls for omitted obligations. A test caps every checklist at 200 lines and checks the obligation map, not just the section count.
|
||||
|
||||
**B7. Review-log status derived from findings.** `reviewFreshness` already downgrades a `clean` row with open issues to UNVERIFIED at read time (lib/review-evidence.ts:238-240); the gap is at write time. `bindReview` (lib/review-evidence.ts:178-186, the single choke point) accepts `--findings <path>`: it derives `status` (`clean` when no finding is open, else `issues_open`), `unresolved`, `issues_found` and `critical_gaps` from the file and rejects a row whose claimed values disagree. The row carries separate `findings_total`, `findings_open` and `findings_resolved`; `reviewFreshness` (lib/review-evidence.ts:238) is updated in the same change to read `findings_open`, since today it rejects any row with `issues_found > 0` even when every finding is resolved. A rejected row exits 1 with `review-log: status mismatch: claimed clean, findings.jsonl has 176 open (REVIEW_STATUS_MISMATCH)` on stderr (the wrapper drops the `2>/dev/null` at bin/gstack-review-log:95 and stops converting binding failures into `invalid JSON`); usage becomes 2 (today 1, :81-84); the legacy `critical` and Codex counters keep their conservative reading. Rows written without `--findings` are tagged `status_source: claimed`. Unattended autoplan always logs through `--findings`. Phase subagents that cannot write the JSONL return findings to the parent, who writes `findings.jsonl` and logs from it; the hand-written `status: clean` beside `issues_found: 176` is rejected at write time.
|
||||
|
||||
**B8. Spec-review loop at the cap.** The loop stops today on PASS, repeated issues or the third launch, and a missing score alone does not force another launch (scripts/resolvers/spec-review.ts:182-192); the 1-10 score is reported but not a stop rule. At the cap it now prints the unresolved gaps as a numbered list and takes one of two recorded paths, never a resample of unchanged input: a deterministic issue-verification pass that checks only the listed issues after their fixes are applied and is recorded as `issues verified: k of N`, never promoted to a full-review verdict and never a fourth model launch past the cap (unattended default), or `N fixes unconfirmed` with the approval gate blocked by a `spec not re-verified` decision. An interactive user may raise `--max-rounds`, and each extra launch requires a changed input hash. `DONE_WITH_CONCERNS` always names the missing points next to the score.
|
||||
|
||||
**B9. One gate list instead of four-option sequences.** New `lib/gate-list.ts` with `bin/gstack-gate` (`render`, `parse`): a gate list is numbered items each with a recommended default and lettered options, and one reply grammar: `all` (every item takes its recommendation), or `<id><option>` tokens (`d3b uc1a`), or `all except <tokens>` (every item takes its recommendation except the listed overrides; the two forms are the same operation, so there is no exclusion-vs-override ambiguity). Bare `yes`/`no` are rejected as ambiguous across multi-option items and reported as unparsed. Every item has a stable id that survives pagination (a list longer than seven items is rendered in pages with the same ids), items are marked `auto` (taken if unanswered) or `approval` (stays pending until answered; the owner's direction stands), and a reply binds to the gate revision it answers (`gate_rev` in `decisions.jsonl`); a reply against a stale revision is refused with the current one. The rendered list prints the exact reply shape it accepts and the resolved JSON it will produce; `parse` returns the resolved choices as JSON and lists unparsed tokens instead of guessing. autoplan's Phase 4 renders the gate through it (interactive and unattended); CEO Step 0 taste decisions join the same list instead of being asked mid-run. The owner replies quoted in the feedback (`d10 yes`, `1a 2a`, a 33-decision approve-all) are parse fixtures.
|
||||
|
||||
**B10. Wall time and a pre-run estimate.** `phase-close` records phase start and end, outside-voice wall time and native wall time to `timing.json` and to `$GSTACK_STATE_ROOT/analytics/autoplan-timing.jsonl`. Phase 0 prints an estimate from plan bytes and detected scope using the recorded history (median of the last 10 runs per phase, `no history` when empty), and the final report prints the actual whole-cycle time next to it.
|
||||
|
||||
**B11. Reconciliation when outside passes run beside native ones.** The outside Bash dispatch may start while the native reviewer runs (both bind to the same snapshot), and `phase-close` gains an explicit numbered operation: for each outside finding record `confirmed`, `disagree` or `new` against the native result, and for the Eng dispatch verify the prior-phase consensus summaries are present in the prompt before sending (a test fixture with a missing CEO summary is refused). The completion barrier and phase order are unchanged.
|
||||
|
||||
**B12. URGENT, outside this plan.** The final report and `plan.md` get a fixed first block `## URGENT, outside this plan` listing security or data-loss findings any voice raised that the plan does not own, each with a suggested owner and the finding id; `None` when empty. A test asserts the block precedes the summary.
|
||||
|
||||
### PR C (C-stamp, C-pregate): queue-aware ship and the pre-gate (P0-3, P0-4)
|
||||
|
||||
**C1. A repo ship policy, machine-readable.** New pin file `.gstack/ship-policy.json` (schema in `lib/ship-policy.ts`, containment-checked like `.gstack/version-path`): `bump` (`patch` | `ask`), `release_tool` (for example `bun run release:restamp`), `stamp_paths` (globs where `(was X)`, `since:` and version mentions are rewritten), `changelog` (`CHANGELOG.md`), `mirror` (path of a paired repo to restamp identically), `history` (`merge-only` | `squash-ok`), `preregistration_shas` (must be ancestors of HEAD), `pr_count` (`default_pr_count`, `split_requires_owner`; consumed by D1). `gstack-version-bump classify` returns `bumpPolicy` from it, and Step 12 skips the MINOR/MAJOR prompts under `bump: patch`. Without the file nothing changes.
|
||||
- Trust boundary for every pin file that names a command (`release_tool`, `.gstack/generated.json`, `.gstack/pregate.json`): file presence never authorizes execution. `gstack-restamp`, `gstack-regen` and `gstack-pregate` run repo-declared commands only when invoked from `/ship` or `/land-and-deploy`, or with `--allow-repo-commands`; standalone they list the commands and exit 3 with `repo commands not executed (pass --allow-repo-commands)`. Commands are read from the committed file on `origin/<base>`, not the working tree, so a PR cannot smuggle a command into its own gate; `mirror` writes are confined to the declared path and refused outside it. This is the opt-in posture `gstack-version-bump --regen-digest` already takes (bin/gstack-version-bump:373-385). Pinning the command string does not freeze its implementation (`bun run release:restamp` still executes the PR's package script), so the record names the policy revision, the command, its working directory and the trust scope. Each tool has `init` (writes a commented example with `$schema` and every key at its default), `validate`, and `show-effective` (the resolved configuration with the source revision of each value); `docs/ship-policy.md` shows gstack's own files. A PR that introduces or changes a policy file runs under the reviewed bootstrap path: `--policy-from head` is accepted only with `--allow-repo-commands`, and the receipt records `policy: head (unreviewed)` so the coordinator sees it. A refusal happens before any write and says `nothing written`. When `origin/<base>` is unresolvable (shallow or detached gate checkouts), the tool exits 3 with `policy source unavailable — fix: git fetch origin <base> --depth=1 (POLICY_SOURCE_UNAVAILABLE)` and never falls back to the working tree silently. Every repo-controlled input (`mirror`, `stamp_paths`, `generated.json` commands, `pregate.json` guard patterns, `banned-terms.json`) has named containment fixtures: no `..`, no symlink escape, globs confined to the repo, regex size caps; `gstack-regen --check` is code execution even in a scratch worktree and sits behind the same opt-in. The policy schema includes `queue_mode`, and examples live in sibling `*.example.json` files or `init --explain`, since JSON carries no comments.
|
||||
|
||||
**C2. `gstack-restamp`: the whole stamp in one step.** New `bin/gstack-restamp` over `lib/restamp.ts` and `lib/changelog-check.ts`. Modes (`--queue-mode stamp-at-merge` is the umbrella: no claim at PR-open time, stamp only when told the PR is next): `--version X`, `--next` (under stamp-at-merge: the configured version source at the base revision through `lib/version-source.ts` (explicit version paths, pinned manifests, three- vs four-component versions and no-version repos are the resolver's existing cases, lib/version-source.ts:135) plus the policy bump, nothing else; the claim reader stays for the legacy claim-at-open mode and the output says `claims: ignored (stamp-at-merge)`), with `--expect-base <sha>` and `--expect-order <pr,pr,...>` so the allocation records the base SHA and queue order it assumed and re-validates both immediately before merge, returning a structured `QUEUE_STALE` result with the exact recomputation command when either moved; the allocation and the receipt bind to repository, base SHA, the ordered predecessor chain, the PR head and the synthetic integration tree, and the merge itself is serialized with server-enforced conditions (the GitHub merge API's expected head `sha`) so a check-to-merge race is rejected by the server rather than assumed away, and where the host cannot enforce the condition the coordinator recomputes after the merge and the receipt says so, `--after <pr>` (gate-ahead: build the merge of the branch and the predecessor's head in a throwaway worktree, stamp N+1 relative to the predecessor's version there, gate there, and record the predecessor head in `.gstack/tmp/gate-ahead.json`; the PR branch's history is never touched, so squash-history PRs stay clean; `gate-ahead.json` records the evidence branch key, because review and evidence ledgers are keyed by branch (bin/gstack-review-log:18-22, bin/gstack-evidence:13) and a synthetic worktree branch would otherwise grade MISSING on the real PR), `--base <ref>`. It runs `gstack-version-bump write`, the policy's `release_tool`, re-heads the CHANGELOG (the PR's section moves to the top under the new version, above its predecessor's; duplicate headings and a top entry not equal to VERSION are errors), rewrites `stamp_paths` mentions (exact `(was X)`, `since: X` and `vX` patterns only, within the declared globs, shown as a dry-run diff before writing), regenerates the generated-file registry (C4), repeats everything in `mirror`, and prints the diff limited to stamp files. Idempotent: a second run with the same version is a no-op; `--dry-run` prints the full diff and writes nothing. The release tool runs under a contract: in an isolated staging worktree, with an enumerated allowed-output set, recording preimages and postimages, applied to the branch only after validation; an output outside the allowed set is a refusal, and rollback refuses to overwrite a file whose current bytes differ from the recorded postimage (a later user edit) and stops with a conflict. Every restamp journals its intended inputs and completed writes (`.gstack/tmp/restamp-journal.json`) so a failure after any stage resumes or rolls back without touching unrelated edits (the version writer already has a half-write state, bin/gstack-version-bump:489; the journal extends it across the release tool, the CHANGELOG, `stamp_paths` and the mirror), with injected-failure tests after every stage. `--queue-mode` defaults from `ship-policy.json`, else legacy, so the flag is not required on every call. Tier 1: `--version`, `--next`, `--after` (the gate-ahead is what makes a reorder cheap, so the P3 promise needs it), `release_tool`, CHANGELOG re-heading, `stamp_paths`. Tier 2: `mirror`. Considered and rejected: stamping on main after merge with a release commit. The gate must run on the stamped tree (CHANGELOG and version live in the PR, and a post-merge commit is itself unreviewed), which is why the owner's requirement is stamp-at-merge.
|
||||
|
||||
**C3. The tree-equality receipt.** New `bin/gstack-tree-receipt` (lib/tree-receipt.ts): prints gated head, current head, both `HEAD^{tree}` hashes, both `gstack-wtree` fingerprints, the diff limited to stamp files, the predecessor head the gate assumed, and one verdict line. Inputs are explicit: `--gated <sha>` and `--predecessor <sha>`, or `--from .gstack/tmp/gate-ahead.json`; `--json` emits the receipt as an object. Gates compare committed heads only (a working-tree fingerprint can name objects unreachable once the VM is gone, bin/gstack-wtree:24). The diff is classified hunk by hunk, not path by path, with per-file-type rules: lockfile rules match only the root package's `version` keys (where `syncNpmLockfiles` writes, bin/gstack-version-bump:486); `stamp_paths` rules match `(was X)`, `since: X` and `vX` only when `X` equals the old version and the replacement equals the new one, so a dependency pin that happens to equal the old version classifies as `changed` (a named negative fixture); a CHANGELOG heading moving is permitted; any other changed byte counts as a change (bin/gstack-evidence:505 compares allowed filenames today, which would let a changed generated prompt pass; npm lockfile metadata changes on every stamp, bin/gstack-version-bump:486, which would otherwise fail every restamp). Equivalence is defined per reusable gate, not once: a gate that consumes VERSION or generated bytes (the agents-digest freshness test does, test/agents-digest.test.ts:22) is stamp-sensitive and always reruns on the final stamped tree; the receipt lists which gates are reusable and which reran, keeping both the raw and the normalized identity. The verdict names the residual hunks: `TREE: same-modulo-stamps` or `TREE: changed (<residual paths>)`. Tree identity alone is not gate evidence: the receipt also records the identity the earlier gate ran under (runtime pins, lockfile hash, CI workflow hashes, the gate command's `cmd_sha256` from `gstack-evidence`) plus the executed test selection and the container or runner image when known) and prints `gate-reuse: eligible` only when every one matches, else `gate-reuse: not-eligible (<what changed>) — fix: <rerun command> (GATE_REUSE_NOT_ELIGIBLE)`. The minimum portable evidence lands in this PR, not in E: `gstack-evidence bundle` exports the comparison material itself (the committed tree id and the stamped-file normalization inputs), the complete required-lane manifest, executed selections, source run URLs, outcomes, artifact hashes and producer-bound run metadata, distinguishes imported assertions from independently verified evidence, and refuses reuse when execution identity is unknown or comparison objects are missing; the receipt reads a bundle from a fresh coordinator machine after the gate VM is gone, without rerunning the gate (`gstack-evidence` is machine-local by design, bin/gstack-evidence:27-38, so the bundle is the portable form). It refuses `eligible` when the recorded predecessor head is no longer the predecessor PR's head (fetch and compare) and prints `PREDECESSOR MOVED <old> -> <new> — fix: gstack-restamp --after <pr> again (RESTAMP_PREDECESSOR_MOVED)`. `/ship` Step 16 and `/land-and-deploy` consult it; when the receipt says eligible, a repeated full gate needs `--force-gate <reason>`, and the coordinator makes the call.
|
||||
|
||||
**C4. Generated files regenerate, never hand-merge.** New pin `.gstack/generated.json` (`[{paths, command}]`) and `bin/gstack-regen` (lib/regen.ts) with two verbs: `--check` runs each command in a scratch copy and reports stale entries without touching the tree (the pre-gate's freshness check), and `--write` regenerates in place (what the restamp and the conflict rule use, where changed output is the point, not staleness). Step 3 conflict rule: for a path in the registry take either side, then `gstack-regen`; the agent never hand-merges a generated file. Used by C2, the pre-gate (C8) and Step 16 stage 1. gstack's own entry is `bun run gen:skill-docs --host all` plus the agents digest and llms files.
|
||||
|
||||
**C5. The handoff record, identifiers first.** New `bin/gstack-ship-receipt` renders a fenced ```` ```gstack-ship-receipt ```` block: `pr`, `head`, `version`, `base`, `gated_tree`, `gate` (lanes with pass/fail/skip counts), `ci` (check counts), `spend_usd`, `predecessor`. The block carries `schema_version` and is validated by `gstack-artifact`. It is the first section of the PR body and the first lines of the completion message to the coordinator, so truncation loses prose, not identifiers. `/ship` Step 18 writes it; `/land-and-deploy` reads it with `gstack-ship-receipt read --pr <n> --json`.
|
||||
|
||||
**C6. Issue links and flake issues.** Step 18 adds `Fixes #N` only on explicit closure intent: a `Fixes #N`/`Closes #N` trailer in a commit or an explicit `--issue N`, never a bare mention (a context reference, a partial fix or a deferred ask is `Refs #N`); it reads each through `gstack-issue-guard`, and maps failing CI run ids to bot-opened flake issues (search issues whose body names the run id) as `Flake: #N (run <id>)`. Spec-archive links keep today's rules.
|
||||
|
||||
**C7. History policy and preregistration ancestry.** Under `history: merge-only` `/ship` refuses a squash or rebase plan and says why; `gstack-evidence ancestor <sha>` checks each `preregistration_shas` entry is an ancestor of HEAD before push, and the receipt records the result.
|
||||
|
||||
**C8. `gstack-pregate`: cheap checks before anything paid.** New `bin/gstack-pregate` over `lib/pregate/*.ts` (one file per check, each under 150 lines, registry in `lib/pregate/index.ts`). It runs at the entry of ship Step 5, before Step 6's paid evals and before any remote gate, under `set -o pipefail`, aggregates exit codes itself, and prints a table plus `pregate.json`. A failing check blocks; a repo downgrades a named check to `warn` in `.gstack/pregate.json`, except `secrets` and required-lane obligations, which cannot be downgraded. Every check records the input identity it ran against (the tree, the generated outputs, the selection) with the same before/after fingerprinting `gstack-evidence` uses (bin/gstack-evidence:394), and publication requires results that are current after final generation and stamping; a timed-out or tool-unavailable check is `incomplete`, never `pass`. The lane list and platform tiers come from the repo's `.github/workflows/*.yml` (the same parser as `project pins`), never from a pre-gate-owned taxonomy, so a changed CI matrix changes the pre-gate. Two stages with separate clocks: the mechanical preflight (`regen --check`, `secrets`, `strays`, `literals`, `links`, `lint`, `patches`, `ratchets`) is the two-minute promise, measured on gstack and gbrain and printed per check; test execution (`lanes`, `guards`, `hermetic`) is the second stage with its own measured time, never folded into the first. Lane discovery reads what the workflows actually encode and defers to runner-owned selection manifests where the matrix is computed by a planner (gstack's Windows matrix comes from `scripts/test-free-shards.ts`, .github/workflows/windows-free-tests.yml:62, and exclusive classification lives in TypeScript, scripts/test-free-shards.ts:243); `--explain` prints each selected test's provenance and each uncertain mapping. A `requires-remote` entry names the workflow and job to dispatch and the receipt that clears it. Tier 1 (this PR, each with a named incident fixture): `regen`, `secrets`, `strays`, `literals`, `lanes` with the pipefail fix. Tier 2 (same PR if green, else a follow-up pre-gate PR): `guards`, `ratchets`, `lint`, `links`, `hermetic`, `patches`, `integrator`. Checks:
|
||||
- `regen`: every `.gstack/generated.json` entry fresh (C4, `--check` in a `git worktree` scratch, never a copy of a tree with `node_modules`), including goldens outside the repo's regen script when listed there.
|
||||
- `guards`: new exported interface members, CLI commands, error codes and migrations detected from the diff (patterns in `.gstack/pregate.json` `guards`), each mapped to the guard tests it must run (CLI contract rows, `--help` coverage, canonical-writers census, upgrade replay, serial-files manifest); runs them and reports which ran.
|
||||
- `ratchets`: module-size and any repo-declared ratchet test file; on a failure it prints the exact fixture entry the repo's convention would need (an interface-member growth needs a registered `movedFrom`/`reason` line; the documented exception is for data tables and generated literals, test/helpers/module-size.ts:225) and leaves the append to the author, so the ratchet is never auto-exempted.
|
||||
- `lint`: semgrep, gitleaks, actionlint and shellcheck when installed and when touched paths match; absent tools print `not installed`, never PASS. `secrets` runs `gstack-redact` over the branch's added lines regardless.
|
||||
- `links`: relative links and anchors in changed markdown resolve to tracked files (lib/md-links.ts).
|
||||
- `lanes`: maps touched source files to the test files that depend on them through `lib/pregate/import-graph.ts`, which unions static imports, repo-path string literals (`test/gstack-doctor.test.ts` spawns `bin/gstack-doctor`; no import names it), the `bin/<name>` → `test/<name>*.test.ts` convention and explicit `.gstack/pregate.json` dependency declarations for subprocess targets, runtime file reads and generated inputs; an uncertain mapping selects conservatively (more tests, never fewer) and a touched file with no lane is `FAIL lanes — <file> has no test lane`, not a pass (`test/helpers/touchfile-closure.ts` follows only helpers and fixtures and calls itself a lower bound, so it is the precedent, not the implementation). `requires-remote` obligations are persisted in `pregate.json` and the ship receipt, and publication requires their matching receipts; printing the line is not enforcement. It classifies them serial, E2E, platform or unit from the workflow files, runs the local ones, and prints `Tiers ran: unit serial; requires-remote: windows`. It blocks only on lanes that can run here (a serial file under the touched area that did not run); a platform lane this machine cannot run becomes a `requires-remote` entry the gate must include, so a Windows-affecting change on a Linux box has a forward path and never merges with that lane unrun.
|
||||
- `literals`: for every changed exported constant, grep the whole test tree, nightly-only and Heavy-only files included, for the old literal; for every changed spawn, retry or timeout call, grep for spies and assertions on it; for every migration, grep SQL fixtures that DROP or ALTER the objects it touches.
|
||||
- `hermetic`: new and changed test files run once through new `bin/gstack-hermetic-run` over `lib/hermetic-env.ts` (the builder moves out of `test/helpers/hermetic-env.ts:112`, which keeps a re-export, because a shipped bin must not import test code), which allocates an isolated `HOME`, sets `GIT_CONFIG_GLOBAL=/dev/null` and `GIT_CONFIG_NOSYSTEM=1` (today's helper keeps `HOME` and accepts overrides after scrubbing, test/helpers/hermetic-env.ts:49, :147), and distinguishes an isolation failure from a fixture that legitimately needs an identity; a planted home file and a global git identity are the fixtures.
|
||||
- `strays`: files added at the repo root or outside the usual directories, warning on `zz-*`, `tmp*`, `scratch*`.
|
||||
- `patches`: for `patchedDependencies`, the key matches the installed version and every hunk in the committed patch is present in a regenerated one; dropped hunks are listed and each needs explicit confirmation.
|
||||
- `integrator` (`--after-merge`): doc size caps, ratchets, registry goldens and manifest freshness after each lane merge, with the ratchet-conflict rule printed: re-sum to the measured count and keep both notes.
|
||||
- The Step 5 lane example in `ship/sections/tests.md.tmpl:26-30` collects PIDs and `wait $pid || fail` per lane; every recorded command carries `pipefail`; nothing judges success from `tail`.
|
||||
- Acceptance: a fixture per incident in the feedback (stale goldens, scratch script, Heavy-only old constant, Windows-only change without its lane, dropped vendored hunk, `| tail`-hidden verify failure) is caught locally before any remote gate starts.
|
||||
|
||||
### PR D: plan rows, sideways review, investigate recipes (P1-5, P1-6, P1-7)
|
||||
|
||||
**D1. Reality rows in plan reviews.** New resolver `scripts/resolvers/plan-reality.ts` renders `{{PLAN_REALITY_ROWS}}` into plan-eng-review's Scope Challenge, plan-ceo-review 0A/0B, the autoplan CEO and Eng phase files and the Pre-Gate table. In unattended runs a missing applicable row sets the phase `incomplete` (not a warning); interactive runs keep the two-repair-then-warn rule. Tier 1 (no scope detection needed, demonstrated by this plan's own review): the premise table, already done, surface check, binding decisions, numbers, deferred asks, PR count. Tier 2 (need a scope signal the manifest's `scope` field (B4) supplies: `db`, `perf`, `search`, `incident`): transaction semantics, recall, units, perf table, falsification box, stop reasons, reporter environment, prior art, contract assumptions, dependency fallback, obligation tiers. Rows, each with a one-line incident note in the deep section:
|
||||
- Premise table: `premise | claim | file:line receipt | verdict`; every factual claim in the draft gets a receipt or the tag `unverified`, and the count of unverified claims is a scored line in the Completion Summary.
|
||||
- Prior art: for a vendored or patched dependency or a known library behavior, search the upstream tracker and open PRs, then record `adopt <link>` or `diverge because <reason>`.
|
||||
- Transaction semantics (driver, pool or protocol change): what happens to the surrounding transaction on idle, pooled and reserved connections; crash robot reruns after the change.
|
||||
- Binding decisions: read `gstack-decision-search --scope branch --recent 50` and a `## Binding decisions` section of the plan; they outrank project defaults and CLAUDE.md, and a reintroduced overruled default is a finding.
|
||||
- Already done: `git fetch origin <base>` then `git log origin/<base> --since=<ask date> -- <named files>` and `--grep` for the named symbols; a hit is reported before any task is written.
|
||||
- Surface check: every named CLI command or HTTP route verified against the registry or `--help`; a miss is a finding.
|
||||
- Contract assumptions: a row per default or flag assumed from another repo, rechecked against merged code before the next stage.
|
||||
- Numbers: every number tagged `[measured: <source>]` or `[estimated]`; cited baselines tagged `[re-measure before gate]`.
|
||||
- Falsification box (incident or outage plans): top hypothesis, the evidence that kills it, a time box, and the plan's action in each branch.
|
||||
- Stop reasons: every new stop reason, hold code and progress line names the test that triggers it.
|
||||
- Reporter environment: a repro that does not match the reporter's environment is `class not confirmed` and may not say `ruled out`.
|
||||
- Units: a diff that changes an input's units or accuracy lists every threshold that consumes it and shows the re-tune.
|
||||
- Perf table: machine, engine, data size, N, p50/p95, statistics state, base and branch interleaved, pathological and steady-state cases, `reproduced on main?`.
|
||||
- Recall: a search or query-plan change reports recall against exact search across scope buckets.
|
||||
- PR count: read `pr_count` from `.gstack/ship-policy.json` (C1); default one PR per wave; a proposed split shows the queue cost per extra PR and checks user-facing references across the split.
|
||||
- Dependency fallback: what ships without each predecessor and by when.
|
||||
- Deferred asks: the linked issue's requests the plan consciously leaves out, each with its reason, listed in the gate template so a reviewer sees them.
|
||||
- Obligation tiers: must-ship, next PR, deferred, under a stated size budget; the ledger status column carries the tier.
|
||||
|
||||
**D2. Review and cso look sideways.** In `review/checklist.md` and `review/SKILL.md.tmpl` Step 4: for every finding grep the same pattern at every other ingress, command and call site and rate severity on the worst sibling; a forced-CRITICAL list (data loss, trust-boundary bypass, uncapped spend) that no lane can downgrade; for a new cache in front of a guarded or metered call, name the policy checks a hit skips; when imports move from static to dynamic, list top-level side effects no longer loaded and who reads each registry; when a function changes, find modules that claim parity with it (docstrings, parity tests) and flag unchanged ones; flag pool queries awaited inside a caller's transaction on hot read paths. The plan-completion resolver (scripts/resolvers/plan-gates.ts:464-490) adds an `Undeclared behavior changes` line listing diff behavior the author's summary omits, and the approval step refuses `approved` without a recorded read of the full diff. Test-only flake fixes require a number: the probability the test still fails on a real regression, before and after (shared resolver with D3). `/cso` adopts the sibling sweep for its findings and nothing else.
|
||||
|
||||
**D3. Investigate recipes and backlog mode.** `investigate/SKILL.md.tmpl` gains lazy sections (new `investigate/sections/` with a manifest): `flake` (reproduce on main; failure rate over N runs; cause inside or outside the PR; compare the metric with the last N green runs of branch and main via `gh run list`; forced probe that fails before and passes after; never loosen; a rerun only when the cause is known and outside the PR; a required `## Flake evidence` PR-body section with the probe's before and after output and a routing note for the owner; reruns through `ship-measure`'s discipline, paid evals never retried), `polluter` (file order from the CI log, bisect the earlier files with `ship-measure free --files`, report the polluter and what it leaked), `race` (timing probes, forced interleaving, concurrent-writer template), `eval-bisect` (distinct revisions, a cached cheap scorer, detached runs), and `backlog` (`investigate --backlog`: each issue classified `still_open`, `already_fixed <commit>`, `partially_fixed` or `owned_elsewhere`, a reproduction required for `still_open`, contributor PRs counted as evidence only, read-only output table, no issue closing). Phase 1 adds: read call sites before calling two queries duplicates; `git update-index --refresh` before timing anything that spawns git in a benchmark worktree (`benchmark/SKILL.md.tmpl` too).
|
||||
|
||||
### PR E: `/eval-plan`, coordination tools, owner brief (P1-8, P2-9, P2-10)
|
||||
|
||||
**E1. `/eval-plan`: preregistered, paid evals for the user's product.** New skill `eval-plan/SKILL.md.tmpl` with sections, generalizing gstack's internal machinery:
|
||||
- Preregistration checklist: bars, arms, decoys, stop rules, and a held-out exposure section listing every held-out set the plan touches, its prior exposure and reservations.
|
||||
- Scorer dry run on dev data with measured accuracy per scorer and a stop when any is below its bar (lib generalization of `scripts/judge-calibration.ts` `compare()` over user JSONL).
|
||||
- `$0` stub-model dry run over real input shapes, plus the checklist: price-table entry present, output budget sized for reasoning models, the retry prompt's actual text. `PRICING` moves to `lib/pricing.ts` (re-exported for tests).
|
||||
- Priced pilot (n≈3) with extrapolation and an error band before the approval ask.
|
||||
- Budget ledger `lib/spend-ledger.ts`, extracted from `scripts/ship-measure.ts`'s admission logic and made honest about what it can promise: admission is estimated (the price table excludes some billing categories, test/helpers/pricing.ts:4, and actual cost reconciles after each call), the ledger refuses a new call when `spent + reserved >= cap` under a file lock shared by concurrent workers, and the spend-vs-cap table regenerated at every gate shows `spent`, `reserved`, `unknown charges` and the worst-case overrun (one in-flight call per worker). `budget_exhausted` stops the run.
|
||||
- Arms from the project's model rules (`gstack-models resolve --role eval-arms --json`, falling back to the repo's own policy doc) with per-arm cost shown.
|
||||
- Instrument audit: read every failing transcript of one dev round before freezing the scorer; check generated worlds for duplicate identifiers, word-like codes and generic phrases; mutation tests in both directions.
|
||||
- Steady state: background queues empty at snapshot time, queue depth recorded as a gate.
|
||||
- Sealed paths declared in the plan; the skill refuses to quote from them, self-checks output for n-grams from them and adds a provenance line.
|
||||
- Long runs persist off the machine: `gstack-detach --sync-cmd <cmd> --sync-every <min>`.
|
||||
- Memory preflight: concurrency times measured per-process memory, refuse above 70% of RAM.
|
||||
- Power and risk sentences cite the committed power output, with a sensitivity table over the obvious rule alternatives (lib generalization of `scripts/lib/measure-bar.ts`).
|
||||
- QA soak: `qa/sections/soak.md.tmpl` runs hours of mixed ingest against a long-lived server with a watchdog for `CPU pinned, no progress`.
|
||||
- Tiers: tier 1 is the preregistration checklist, the `$0` stub dry run and the priced pilot with the ledger (the three steps behind the spend incidents); tier 3 (after P7) is the instrument audit, steady state, sealed paths, off-machine persistence, memory preflight, power tables and QA soak.
|
||||
|
||||
**E2. Coordination tools.** `bin/gstack-lane-check` (lib/lane-ownership.ts): given in-flight branches and PRs, diff each and flag planned files that intersect, with the PR number and hunk count. `gstack-evidence bundle` (shipped in C-stamp, C3) gains `gstack-evidence verify <bundle>` here, which reruns the probes or marks each claim `verified` or `unverified`; E1's priced pilot consumes the `lib/spend-ledger.ts` that B-runner ships (B2), adding the pricing table extraction (`lib/pricing.ts` from test/helpers/pricing.ts:4, whose exclusions are documented) rather than a second ledger. `{{COORDINATOR_CONTRACT}}` resolver: a block in the plan template (merge conditions, behavior-change notices, spend caps, handoff format) that every lane's subagent prompt inherits word for word; the prompt printer (B4) includes it. Cloud VMs: the repo's `scripts/ubicloud/ubi-runner.sh` adopts the user-scope skill's owner tag, `list --mine`, `usage`, own-VMs-only `gc`, record-before-create teardown, plus a VM-id scratch file under `~/.capy/work` and a resume checklist in docs/TESTING_INTERNALS.md; long benchmarks and gates documented as watched background operations, one before/after pair per machine, with a warning before profiling very large imports. `test_backend` config (`local` | `ubicloud`) with the doctor `cores` row (A1) recommending `ubicloud` under 8 cores for full unit and E2E tiers. `ship/sections/handoff.md.tmpl`: the no-PR automation handoff (verify-only checks, branch, SHA, inputs, before/after metrics, escalation rule). The Capy skill says to store and use full thread ids in cross-thread messages.
|
||||
|
||||
**E3. What the owner sees.** autoplan Phase 4 defaults to a one-page brief: plain-language summary, the `URGENT, outside this plan` block (B12), and the numbered decisions rendered through the gate list (B9): seven per page with stable ids, `auto` items showing the default taken if unanswered, `approval` items staying pending until answered; the full review goes to `review-record.md`. After the final gate, every `draft direction stands until the owner decides` line in the plan is rewritten to the decided option and date (phase-close operation). Contributor-PR mode in `/review` (`--contributor`, auto-detected when the PR author is outside the maintainer set, read from an explicit `maintainers` list in `.gstack/ship-policy.json`, else repository push permission through the collaborators API, else off, with the source recorded; `CODEOWNERS` is file ownership, not maintenance authority, and is not used): outside PRs are evidence first; the output is a table (evidence value, risk surfaces, `accept`, `supersede` or `decline`), where `accept` keeps a direct path for a correct outside implementation judged by the same bar as a maintainer change, `supersede` rewrites from our own plan after a plain-language approval, and credit lines (`Contributed by @handle`, `Co-Authored-By:`, `Supersedes #N`) are emitted only when a diagnosis or test carried over. Banned terms: `.gstack/banned-terms.json` (`terms`, `allowed_paths`) checked by `bin/gstack-banned-terms` on every diff from `/review` and `/document-release`; findings name the term, file and the allowed locations.
|
||||
|
||||
### Not in this wave
|
||||
|
||||
- **A `capy` host target in `hosts/`.** No resolver branches on `capabilities.questions` (scripts/host-config.ts:178 only validates it, and every host declares it); a new host means regenerating every skill for one more render with no behavior behind it. Headless behavior keys on the session kind so every host gets it. Revisit if a host-specific tool rewrite is needed (decision D2).
|
||||
- **Installing semgrep or gitleaks for the user.** The pre-gate runs them when present and says `not installed` when not.
|
||||
- **Native Windows for `/cso` and the Windows lane itself.** The pre-gate reports a Windows lane as `not run here`; running it stays CI's job.
|
||||
- **Closing issues from `investigate --backlog`.** Read-only by design.
|
||||
- **Stamping on main after merge.** Considered under C2 and rejected: the gate must run on the stamped tree.
|
||||
- **A pre-gate that runs platform lanes this machine lacks.** It records `requires-remote` and the gate includes the lane (C8).
|
||||
|
||||
### Validation
|
||||
|
||||
1. **Incident fixtures.** Each incident in the feedback becomes a fixture or scripted scenario under `test/fixtures/multi-agent-wave/`, named by section and incident (`p0-4-stale-goldens`, `p0-3-queue-reorder`, `p1-6-cache-skips-guard`, ...), and a test shows the tool catching it or producing the artifact. The incident-to-test map is committed as `test/fixtures/multi-agent-wave/INCIDENTS.md`, not left in a PR body.
|
||||
2. **Unattended autoplan end to end.** PR B-runner is proven by running `/autoplan` on this plan from a Capy machine with no Claude Code harness, with the outside voice from either a Codex login made from `OPENAI_API_KEY` or a cross-family host subagent (the gate accepts either and records which ran); the PR body links the output files, the `gstack-artifact validate run.json` result, and a parent-agent transcript that consumes them with `jq` and `grep` alone. This is PR B-runner's merge gate.
|
||||
3. **Queue replay.** `test/gstack-restamp-queue.test.ts` builds three PRs on a fixture repo with a `release:restamp` script, a mirror repo, a CHANGELOG, `(was X)` doc mentions and `since:` fields, reorders the queue twice, and asserts zero manual edits, correct `(was X)` text, CHANGELOG order, mirror parity, and tree receipts that say `same-modulo-stamps` / `gate-reuse: eligible` after a pure restamp and `changed` after a real edit, including the `PREDECESSOR MOVED` refusal, a squash-history PR under gate-ahead (branch history untouched), a `release_tool` that regenerates a path outside `stamp_paths` (classified through `generated.json`), a metadata-only lockfile change (eligible) versus a dependency change (not eligible), and an environment change under an identical tree (`gate-reuse: not-eligible`).
|
||||
4. **Acceptance matrix.** `docs/designs/multi-agent-wave-2026-10-10/ACCEPTANCE.md` lists every feature with its PR, tier, commands, artifact capabilities and deferred asks; the owner's gate approves that matrix, each PR body quotes its rows, and `gstack-doctor --json` exposes which capabilities the installed revision shipped.
|
||||
5. **Repo gates per AGENTS.md.** `bun run typecheck`, `bun run typecheck:test` (the test-code type ratchet CI runs, .github/workflows/free-tests.yml:223), `bun run build`, `bun run format:cso:check`, the credential scan, `bun run gen:skill-docs --host all` with a clean diff, `bun test test/parity-suite.test.ts` with each PR's byte delta and `CARVE_GUARDS` refresh in the same commit, the context-budget, skill-size-floor and module-size ratchets (every new module registered), `test/spawnsync-timeout-tripwire.test.ts`, the selected quality judges for changed prompts, then one `bun run test` on the frozen tree per PR, with incomplete platform and provider coverage reported separately; `bun run eval:bg:pr` for the changed prompts (autoplan, plan reviews, review, investigate, ship) with every trial reported.
|
||||
|
||||
### Effort
|
||||
|
||||
Estimated per component (`[estimated]`; the CEO review found the first table uncredible, the Eng review re-counted B and C from what the text implies rather than the table's bin count, and these numbers are re-measured after PR A ships, `[re-measure before gate]`). Byte-budget work on ship, review, autoplan and qa (zero parity headroom) is the long pole for D, not the code.
|
||||
|
||||
| PR | New bins / libs / fixtures | Est. LOC | Human team | CC+gstack | Compression |
|
||||
|---|---|---|---|---|---|
|
||||
| A | 3 / 2 / 10 | 800–1,200 | 1 week | 0.5 day | ~10x |
|
||||
| B-core | 3 / 3 / 10 | 1,500–2,500 | 1.5 weeks | 1 day | ~8x |
|
||||
| B-runner | 2 / 4 / 12 | 2,000–3,500 | 2 weeks | 1.5 days | ~8x |
|
||||
| C-stamp | 3 / 5 / 10 (tier 1) | 2,000–3,000 | 2 weeks | 1.5 days | ~8x |
|
||||
| C-pregate | 3 / 8 / 10 (tier 1) | 2,000–3,000 | 2 weeks | 1.5 days | ~8x |
|
||||
| D | 0 / 1 / 12 (tier 1) | 600–900 | 1.5 weeks | 1 day | ~8x |
|
||||
| E | 3 / 4 / 10 (tier 1) | 1,500–2,500 | 2 weeks | 1.5 days | ~7x |
|
||||
|
||||
### Gate decisions (2026-10-10; owner replied `all` at 21:30 UTC: every item approved on its recommended option)
|
||||
|
||||
Reply grammar: `all` approves every recommendation; `<id><option>` overrides one item (`d1b uc1a`); `all except d9b` combines them.
|
||||
|
||||
**User challenges (both reviewers recommended changing the stated direction; the owner accepted the recommendation):**
|
||||
|
||||
- **UC1. Scope of the committed wave.** You asked for every section, P0 first. Both the native (Claude) and outside (gpt-6-astra) CEO voices recommend committing P0 now and gating the breadth on adoption evidence, because the premise that tooling gaps (not low perceived value) kept 13 of 28 threads off gstack is unmeasured, and the first effort table was not credible. (a) **Recommended:** commit the whole stacked wave (seven PRs per D12) with the tiering above; tier 1 of every PR is funded now, tier 3 waits for the P7 number (six of the next ten Capy threads run an unattended workflow a parent consumed). Cost: the full breadth lands weeks later than a single push, and two PRs may ship thinner. (b) P0 only (A, B-core, B-runner, C-stamp, C-pregate), then decide. (c) Everything as first written, no tiers. If wrong about (a): tier-3 items the fleet needed wait one measurement cycle.
|
||||
|
||||
**Decisions (recommendation first):**
|
||||
|
||||
- **D1. Delivery shape.** (a) **Recommended:** a stacked wave merged in order, install alone first, restamp tool dogfooded on every PR behind it (count per D12) [cost: one full gate per PR]. (b) One PR [one gate, a diff nobody can review]. (c) One PR per section, ten [nine extra gates].
|
||||
- **D2. Unattended as a session kind, not a host.** (a) **Recommended:** a fourth kind `unattended`, explicit override only; `headless` keeps BLOCK so no eval case changes meaning; the outside-voice runner is decoupled from host identity (B3) [cost: none beyond B1]. (b) A new `hosts/capy.ts` target [one more render of every skill, no behavior behind it today].
|
||||
- **D3. The Capy installer moves into the repo** as `bin/gstack-capy-install`; the user-scope skill clones first, then runs it [cost: one drive-skill edit after PR A merges]. (b) Keep the logic in the drive skill [untested, invisible to CI].
|
||||
- **D4. Repo policy lives in `.gstack/*.json` pin files**, with command execution opt-in (`--allow-repo-commands` or under `/ship`), read from the committed base-branch copy [cost: one flag in `/ship`]. (b) `gstack-config` keys [per machine, not per repo]. (c) CLAUDE.md prose [the current failure mode].
|
||||
- **D5. The pre-gate blocks by default** for lanes it can run; platform lanes it cannot run become `requires-remote`; a repo downgrades a named check to `warn` [cost: a first-run surprise in repos with existing drift]. (b) Advisory by default [the incidents recur].
|
||||
- **D6. Unattended never approves.** The gate is written and the run ends. (b) Auto-approve the recommended option as `spawned` does [a production brain was blocked on exactly this kind of unread decision].
|
||||
- **D7. Eval skill name:** (a) **Recommended** `/eval-plan` [`measure` collides with `ship-measure`]. (b) `/measure`. (c) `/preregister`.
|
||||
- **D8. Review severity vocabulary:** (a) **Recommended** keep CRITICAL/INFORMATIONAL and add a forced-CRITICAL list [no collision with Greptile reply tiers or `preamble-tier`]. (b) Introduce Tier A/B.
|
||||
- **D9. Chromium:** (a) **Recommended** lazy by default through `gstack-browser-ensure`, `--with-browser` to front-load, `--only <skill>` kept [cost: one helper; first browser skill run pays 86 s]. (b) Eager by default with `--light` to skip [every planning machine pays 86 s unless it remembers the flag].
|
||||
- **D10. Contributor-PR mode is on for outside authors by default**, emits the table with a direct `accept` path, maintainer set from the policy's `maintainers` list, else repository push permission (Eng amendment: `CODEOWNERS` is file ownership, not maintenance authority). (b) Opt-in per repo.
|
||||
- **D11. Gate reuse rests on identity, not tree equality alone** (tree modulo stamps and generated paths, plus runtime pins, lockfile, CI config, gate command hash); the receipt says `eligible` or names what changed, and the coordinator decides [cost: a slightly longer receipt]. (b) Tree equality alone refuses a second gate [unsound: dependencies and runtime can change under an identical tree].
|
||||
- **D12. PR shape after the Eng sizing.** (a) **Recommended:** seven PRs: A, B-core, B-runner, C-stamp, C-pregate, D, E; B and C split at their natural seams, halves built in parallel and stacked [cost: two more gates than five PRs, each diff one review sitting]. (b) Keep five PRs and accept two review sittings for B and C.
|
||||
|
||||
### Accepted review amendments
|
||||
|
||||
Recorded by the autoplan run in the Review record below; the Implementation plan above already reflects accepted ones.
|
||||
|
||||
## Review record
|
||||
|
||||
### Autoplan run log (2026-10-10, base `91bbd9e`, Capy cloud machine, no Claude Code harness)
|
||||
|
||||
Host: Capy (Claude model, no Skill tool, no AskUserQuestion, no hooks). Session kind: unattended in practice (every question auto-decided on the recommended option and logged; the final gate is written, not approved). Outside voice: Codex CLI, logged in from `OPENAI_API_KEY` with `codex login --with-api-key` (6 s), `gstack-codex-probe role-ready exec` selected `gpt-6-astra` (3.7 s). Native voice: a Capy subagent on the Claude family reading `native-prompt.md` with its own file-reading tool. Snapshot tool: `~/.claude/skills/gstack/bin/gstack-autoplan-snapshot.ts` (`init`, `methodology`, `create`); restore point `.gstack/tmp/autoplan/main-autoplan-restore-20261010-203029.md`. Scope: UI no; DX yes (52 term matches, threshold 2). Methodology loaded for CEO: 2,484 lines / 160,290 bytes (the P0-2 incident, measured). Deviations from the Claude Code driver: the native and outside passes ran in parallel against the same snapshot (`sha256 2937ab4e…`), reconciled below (the plan's B11); the `phase-publication-hook` guard is not installed on this host, so publication order is unverified (the plan's B1 hook note); review-log rows were not written because this machine's state root is ephemeral (the plan's A3). Owner input arrived mid-phase (Garry's second message) and was folded into the plan before the DX phase as binding decisions.
|
||||
|
||||
Timing (UTC, from artifact mtimes): CEO phase 20:30 → 20:45 wall (outside voice 169 s; native 6 min 24 s; amendments 8 min).
|
||||
|
||||
### Phase 1 CEO review: Step 0
|
||||
|
||||
Premise table: the native reviewer checked 27 premises against `91bbd9e`: 23 VALID, 1 FALSE as worded (`bin/gstack-hermetic-run` described as reused; it is new over the existing `buildHermeticEnv()`), 3 UNVERIFIED (the causal reading of "13 of 28 threads", the effort table, and range-vs-pin on `engines.bun`). The outside reviewer checked 11: 6 VALID, 4 FALSE (Codex auth supplies every host's outside reviewer; the spec loop requires 7/10; `touchfile-closure.ts` is a general source-to-test map; tree equality alone is gate evidence), 1 UNVERIFIED (two-minute pre-gate and effort). Every FALSE premise is corrected in the plan above (B3, B8, C8 `lanes`, C3/D11). Full tables: `docs/designs/multi-agent-wave-2026-10-10/ceo-native.md`, `ceo-outside.md`.
|
||||
|
||||
What already exists: see Context. Mode: SELECTIVE EXPANSION. Spec Review Loop: not run (no `office-hours` spec; the plan is its own spec; the CEO voices served as the review).
|
||||
|
||||
### Phase 1 CEO review: dual voices
|
||||
|
||||
```
|
||||
CEO DUAL VOICES — CONSENSUS TABLE:
|
||||
Dimension Claude(native) gpt-6-astra(Codex) Consensus
|
||||
1. Premises valid? 8 5 DISAGREE → taste (repo facts hold; wave premise unmeasured)
|
||||
2. Right problem to solve? 8 6 CONFIRMED with caveat (artifact contract is the product)
|
||||
3. Scope calibration correct? 4 2 CONFIRMED: too wide → UC1 + tiers
|
||||
4. Alternatives sufficiently explored? 6 2 CONFIRMED: trim-and-measure, unattended kind, post-merge stamping, single schema now weighed
|
||||
5. Competitive/market risks covered? 5 3 CONFIRMED: contract/schema is the hedge (B0)
|
||||
6. 6-month trajectory sound? 6 3 DISAGREE → resolved by B0 + P7
|
||||
CONFIRMED = completed subagent + outside. Both completed. Provider codex, model gpt-6-astra, status completed.
|
||||
```
|
||||
|
||||
Reconciliation of the parallel passes (each outside finding against the native result):
|
||||
|
||||
| # | Outside finding (gpt-6-astra) | Native | Disposition |
|
||||
|---|---|---|---|
|
||||
| 1 | One environment treated as a market; adoption unmeasured | 1.1 same | confirmed → UC1, P7 |
|
||||
| 2 | "Never re-gate an equal tree" unsound (Critical) | 6.3 partial | confirmed → C3 rewritten, D11 |
|
||||
| 3 | Machine-readable ≠ verifiable; needs run manifest + validator | 2.1 same | confirmed → B0 |
|
||||
| 4 | Session mode doesn't solve host compatibility; outside reviewer is host-bound | not raised | new → B3 decouples runner from host; Capy dispatch proven by this run |
|
||||
| 5 | PR A hostage to twelve initiatives | 3.1/4.1 same | confirmed → A split from B; tiers |
|
||||
| 6 | Queue design manages churn instead of eliminating it; alternatives unweighed | not raised | disagree in part: post-merge stamping rejected (gate must run on the stamped tree, owner requirement); accepted: constrain `stamp_paths` rewriting, mirror tier 2 |
|
||||
| 7 | Pre-gate can block the gate it needs (Windows lane) | 3.2 adjacent | confirmed → `requires-remote`, lanes from workflow files |
|
||||
| 8 | Pin files acquire undefined authority | 6.2 same | confirmed → trust boundary in C1 |
|
||||
| 9 | `/eval-plan` second product; ledger not a hard cap | 4.4 same | confirmed → tiers; ledger honesty |
|
||||
| 10 | Row compliance ≠ review quality; spec loop 7+ misread | 3.3 adjacent | confirmed → tiers on rows; B8 rewritten (no resampling) |
|
||||
| 11 | Install verdict lacks intended-readiness definition | 2.3 adjacent | confirmed → `--for <skill>`, two runtime rows, Bun upgrade |
|
||||
| 12 | Contributor rewriting undermines maintenance | 6.5 adjacent | confirmed → `accept` path, maintainer set defined |
|
||||
| — | (native only) `headless` redefinition changes eval semantics (Critical) | 6.1 | accepted → `unattended` fourth kind (B1) |
|
||||
| — | (native only) gate-ahead merge pollutes squash PRs | 6.4 | accepted → throwaway worktree (C2) |
|
||||
| — | (native only) lazy Chromium by default; one gate grammar; wrapper clones first; INCIDENTS.md committed | 4.2, 4.3, 6.6, 9 | accepted (A2, B9, A2, Validation) |
|
||||
|
||||
Single-voice criticals: native 6.1 (accepted, mechanical: the eval harness sets `GSTACK_HEADLESS`); outside #2 (accepted, D11).
|
||||
|
||||
### Phase 1 CEO review: sections 1-11
|
||||
|
||||
1. Right problem: both voices agree the thesis (machine-readable first) is right and that the artifact contract, not the tool count, is the product. Accepted: B0. 2. Premises: table above; four corrected. 3. Six-month regret: effort table replaced with per-binary estimates tagged `[estimated]`; tiers added. 4. Alternatives: four alternatives now recorded (trim-and-measure → UC1; `unattended` kind → B1; post-merge stamping → rejected with reason; single schema → B0). 5. Competitive: schema module is the hedge. 6. Uncovered risks: `headless` collision, repo-command trust, receipt residuals, squash history, maintainer set, wrapper bootstrap: all accepted. 7. Scope: tiers; exclusions extended. 8. Promises: P2 reworded for `unattended`, P3 for identity-based reuse, P7 added. 9. Validation: INCIDENTS.md, squash and residual-path replay cases, lockfile-change case. 10. Gate decisions: D1-D10 re-stated, D11 and UC1 added. 11. Design: skipped (no UI scope).
|
||||
|
||||
### Phase 1 CEO review: required outputs
|
||||
|
||||
NOT in scope, What already exists, Error & Rescue registry (11 rows) and Failure Modes registry (9 rows), dream state delta and Completion Summary: in `docs/designs/multi-agent-wave-2026-10-10/ceo-native.md` (native) and `ceo-outside.md` (outside); the plan's "Not in this wave" absorbs the additions. Completion: premises 27+11 checked; findings 2 Critical, 10 High, 9 Medium, 4 Low across both voices; 2 unverified claims remain in the plan (adoption cause, effort), both tagged.
|
||||
|
||||
<!-- autoplan-accepted:ceo -->
|
||||
- B0 versioned artifact contract with `run.json` manifest and `gstack-artifact validate`; validation fixtures for interrupted run, missing reviewer, omitted finding.
|
||||
- B1 fourth session kind `unattended` (explicit override only); `headless` semantics unchanged; negative control that `GSTACK_HEADLESS` still blocks.
|
||||
- B3 pluggable outside-voice runner (`codex-cli` | `api` | `host-subagent`), decoupled from host identity; cross-family subagent is a full voice; model recorded.
|
||||
- B8 spec loop at cap: no resample of unchanged input; cheap pass on listed issues or `spec not re-verified` block; score shown with missing points.
|
||||
- B9 one gate-list reply grammar; quoted owner replies are parse fixtures.
|
||||
- C1 trust boundary: repo-declared commands run only under `/ship`/`/land-and-deploy` or `--allow-repo-commands`, read from the committed base-branch copy; standalone exit 3.
|
||||
- C2 gate-ahead in a throwaway worktree; `stamp_paths` rewriting limited to exact patterns with a dry-run diff; `--after` and `mirror` tier 2.
|
||||
- C3 receipt classifies against `stamp_paths` + `generated.json`; `gate-reuse: eligible` requires runtime pins, lockfile, CI config and gate command identity; coordinator decides.
|
||||
- C8 pre-gate tiers; lanes and platform tiers derived from workflow files; `requires-remote` for lanes this machine cannot run; per-check time budget printed.
|
||||
- A1 `--for <skill>` readiness; `gstack runtime` and `project pins` rows; range vs exact pins; wrong-host/auth/runtime fixtures. A2 lazy Chromium default, `--with-browser`, Bun upgrade when satisfiable, wrapper clones then runs.
|
||||
- D1 rows tiered (7 tier 1, 11 tier 2 behind manifest scope). E1 `/eval-plan` tiered; ledger states estimated admission, lock-shared refusal, worst-case overrun. E3 contributor table includes `accept`; maintainer set from CODEOWNERS → collaborators API → off.
|
||||
- P7 adoption metric; effort table per binary, `[estimated]` and `[re-measure before gate]`; `test/fixtures/multi-agent-wave/INCIDENTS.md` committed.
|
||||
<!-- /autoplan-accepted:ceo -->
|
||||
|
||||
### Phase 1 close and Phase 2
|
||||
|
||||
Phase 1 closed with the amendments above applied to the Implementation plan before the DX snapshot was taken. Phase 2 skipped: no UI scope detected (the plan's "component" means an install component, not a view).
|
||||
|
||||
### Phase 2.5 DX review (snapshot `ffe83107…f11753`, product type: developer tool, primary user: an AI agent)
|
||||
|
||||
Timing (UTC, from artifact mtimes): 20:45 → 20:54 wall (outside voice 173 s; native 6 min 7 s; amendments 3 min). Both voices ran in parallel against the same snapshot.
|
||||
|
||||
Personas traced by both voices: (A) a parent Capy agent installing gstack on a subagent machine, running autoplan unattended and consuming the outputs; (B) a coordinator running "ship, next in queue" for three PRs that reorder twice. Native TTHW: current unbounded (the check cannot be trusted) and nonexistent (needs the harness); target ≈ 25 s to `gstack: ok … for=autoplan` plus one run and one `jq`, closed only with an answer step. Outside TTHW estimate for the plan as snapshotted: 45–90 min of manual glue for persona A, 30–60 min plus a gate for persona B.
|
||||
|
||||
```
|
||||
DX DUAL VOICES — CONSENSUS TABLE (0-10, plan as snapshotted → with amendments):
|
||||
Dimension Claude(native) gpt-6-astra(Codex) Consensus
|
||||
1. Onboarding / TTHW 2 → 8 3 CONFIRMED: Bun preflight before setup, lazy Chromium, quickstart
|
||||
2. Discoverability 3 → 7 3 CONFIRMED: one component table, init scaffolds, show-effective
|
||||
3. Error messages 4 → 7 5 CONFIRMED: reason codes + anchors + fix clauses
|
||||
4. CLI / flag consistency 3 → 7 3 CONFIRMED: one result grammar, exit table, --json everywhere
|
||||
5. Machine-readability 5 → 9 4 CONFIRMED: run-bound ids, referential integrity, envelope
|
||||
6. Docs / examples 2 → 6 3 CONFIRMED: docs/unattended.md, docs/ship-policy.md, ACCEPTANCE.md
|
||||
7. Owner gate experience 4 → 8 3 CONFIRMED: stable ids, pagination, auto vs approval, gate_rev
|
||||
8. Recoverability 2 → 7 2 CONFIRMED: answer/resume, restamp journal, all-or-nothing refusal
|
||||
Native initial 3.1 → target 7.6. Outside recommendation: revise before implementation. Both completed; provider codex, model gpt-6-astra.
|
||||
```
|
||||
|
||||
Reconciliation (outside finding → native → disposition): #1 gate reuse can certify changed generated behavior / lockfile metadata (Critical) → not raised natively → accepted, C3 now classifies hunks and permits only version transforms; #2 installer fails before its repair step (setup:296), no `jq` check, no revision pin → native F8 adjacent → accepted (A2); #3 readiness contradicts runners → native F2 (Critical, two vocabularies) → accepted, component table with required/optional/not_applicable (A1); #4 pins can repair the wrong environment → accepted, `--project`, per-lane pins, no Bun change on conflict (A1/A2); #5 no unattended invocation or continuation contract → native F1 (Critical, no answer step) → accepted, `bin/gstack-autoplan run|status|answer|resume` (B2); #6 task file cannot replace the plan → accepted, run-bound ids, deps, acceptance, finding refs (B0); #7 no common transport → native F7/F12/F16 → accepted, `GSTACK_RESULT`, exit table, `--json` (B0); #8 state root does not cross the parent–child boundary → native F11 (drive conflicts with policy) → accepted, launch configuration line, durable `--out`, drive opt-in, `ack` (A3, B0, B2); #9 no authoritative version-selection rule under reorder → native F3 → accepted, claims ignored, `--expect-base/--expect-order`, `QUEUE_STALE` (C2); #10 cross-machine reuse before portable evidence → accepted, minimal `gstack-evidence bundle` moves into PR C (C3); #11 restamp check/apply semantics and recovery → native F14 → accepted, `regen --check|--write`, journal, all-or-nothing (C2, C4); #12 base-only pin files have no first-use path → native F13 → accepted, `init|validate|show-effective`, reviewed bootstrap path (C1); #13 two-minute pre-gate assumes CI info YAML lacks → accepted, two stages, runner-owned selection manifests, `--explain` (C8); #14 gate can conceal decisions or misread approval → accepted, pagination with stable ids, `auto` vs `approval`, `gate_rev`, no bare yes/no (B9, E3); #15 findings-derived status collides with the freshness reader and the wrapper → native F16 → accepted (B7); #16 tiers do not define merge gates → native F4 → accepted, `--after` tier 1, ACCEPTANCE.md matrix (C2, Validation 4). Native-only: F5 reason codes with anchors (accepted, B0), F6 `ack` (accepted), F9 `--for` over `--require` (accepted), F10 hook-absent vs hook-failed (accepted, B1), F15 capability sentence (corrected). No disagreements remained after reconciliation.
|
||||
|
||||
Required outputs: developer journey map, DX Implementation Checklist (15 items), magical moments (4), competitive benchmark (Claude Code `--output-format json`, Codex `exec` JSONL, Capy subagent summaries: gstack's `run.json` + validator is the only deterministic completion check of the four), empathy narratives and both persona traces are in `docs/designs/multi-agent-wave-2026-10-10/dx-native.md` and `dx-outside.md`.
|
||||
|
||||
<!-- autoplan-accepted:dx -->
|
||||
- B2 `bin/gstack-autoplan run|status|answer|resume` with `--out <dir>`; answer validates against `gate_rev`, finalizes `plan.md` and `run.json.status`, never repeats a completed paid phase; acceptance test covers answer and resume.
|
||||
- A1 one component table (`lib/doctor-components.ts`); rows `PASS|FAIL|SKIP <id>`; `--for <skill>` prints the required set; `--check` never runs the paid probe without `--live`; healthy Capy + Claude-subagent + no-Codex fixture passes `--for autoplan`; `--project <path>`, per-lane CI pins with sources.
|
||||
- A2 runtime preflight before `./setup` (Bun, Node, jq); Bun upgrade only when gstack floor and project pin agree; `--revision`; stale-checkout line; Chromium precedence sentence; planning-only install time measured.
|
||||
- A3 `GSTACK_EPHEMERAL` marker; durable root opt-in; `GSTACK_LAUNCH:` configuration line the parent supplies to every command.
|
||||
- B0 run-bound ids across findings/tasks/decisions; `depends_on`, `acceptance`, `blocked_by`; referential integrity and reviewer-output binding in `validate`; `GSTACK_RESULT:` grammar; exit-code table with `--help` coverage test; reason codes with anchors and `fix:` for every new error line; `gstack-artifact schema|ack|validate --json`.
|
||||
- B1 hook-absent renders an inline allow command (`GUARD_NOT_INSTALLED`); hook-present-but-broken keeps denying.
|
||||
- B7 `findings_total/open/resolved`; `reviewFreshness` updated with the writer; rejected row exits 1 with a typed reason.
|
||||
- B9 one grammar (`all`, `<id><option>`, `all except <tokens>`), no bare yes/no, stable ids across pages, `auto` vs `approval`, `gate_rev` binding.
|
||||
- C1 `init|validate|show-effective`; reviewed bootstrap path for new policies (`policy: head (unreviewed)` in the receipt); refusal before any write, `nothing written`.
|
||||
- C2 `--next` ignores claims under stamp-at-merge; `--expect-base/--expect-order` with `QUEUE_STALE`; restamp journal with resume/rollback and injected-failure tests; `--dry-run`; `--queue-mode` default from policy; `--after` tier 1, `mirror` tier 2.
|
||||
- C3 explicit inputs and `--json`; hunk-level classification with permitted version transforms only; executed selection and runner image in the identity; minimal `gstack-evidence bundle` in PR C; fix clauses on refusals.
|
||||
- C4 `regen --check` vs `--write`. C5 `schema_version` and `read --pr`. C8 two stages with separate clocks; runner-owned selection manifests; `--explain`; `requires-remote` names workflow, job and clearing receipt.
|
||||
- E3 pagination, `auto` vs `approval` semantics. Validation 4 `ACCEPTANCE.md` matrix. `docs/unattended.md` quickstart and `docs/ship-policy.md`.
|
||||
<!-- /autoplan-accepted:dx -->
|
||||
|
||||
### Phase 3 Eng review (snapshot `d7365d92…fd14b17`, methodology 2223 lines)
|
||||
|
||||
Timing (UTC, from artifact mtimes): 20:54 → 21:18 wall (outside voice 227 s, Codex `gpt-6-astra`, RC=3 `OUTSIDE_STATUS completed`; native Claude subagent 11 min 22 s with a verified full read, 230 lines / 67384 bytes / SHA-256 `77ee24a4…3511f`; reconciliation and amendments 12 min). Both voices ran in parallel against the same snapshot, taken from the plan as amended by the DX phase. Phase order held: Eng ran last.
|
||||
|
||||
```
|
||||
ENG DUAL VOICES — CONSENSUS TABLE (0-10, plan as snapshotted):
|
||||
Dimension Claude(native) gpt-6-astra(Codex) Consensus
|
||||
1. Architecture soundness 6 5 CONFIRMED: right layering; runner, outside voice and doctor table misplaced (F1/F2/F5 ↔ #5/#8/#11)
|
||||
2. Edge-case coverage 6 4 CONFIRMED: queue/receipt edges strong; recovery, race, privacy, shallow-clone, lane edges missing
|
||||
3. Test plan 7 5 CONFIRMED: incident fixtures right; add recovery, concurrency, invalidation, portability, stamp-sensitive tests
|
||||
4. Right-sized diff 4 3 CONFIRMED: B and C are each two review sittings; split at natural seams (D12)
|
||||
5. Reversibility 8 4 DISAGREE → native position kept with the outside caveat: new kinds/flags/pins are additive, but
|
||||
journaling alone cannot make arbitrary release commands reversible (#4 accepted into C2)
|
||||
6. Maintainability under ratchets 5 4 CONFIRMED: ratchet premise wrong (unregistered bins), four zero-headroom skills unbudgeted
|
||||
Native: revise-then-proceed. Outside: revise before approval. Both completed; provider codex, model gpt-6-astra.
|
||||
```
|
||||
|
||||
Reconciliation (outside finding → native → disposition): #1 gate reuse confuses permitted edits with equivalent behavior (High; test/agents-digest.test.ts:22, bin/gstack-version-bump:486) → native F14 (per-file-type rules, negative fixture) → accepted, C3 defines equivalence per reusable gate, reruns stamp-sensitive gates, keeps raw and normalized identity, replaces the impossible "changed lockfile, identical tree" fixture; #2 portable bundle lacks data and provenance (bin/gstack-wtree:24, bin/gstack-evidence:23) → native F13 adjacent (branch key) → accepted, C3 compares committed heads only, bundle carries comparison material and producer-bound metadata, refuses reuse on unknown identity; #3 check-to-merge race and integration-tree identity → not raised natively → accepted, C2 binds allocation to base SHA, ordered predecessor chain, PR head and synthetic tree, merge serialized with server-enforced head condition; #4 journaling cannot make arbitrary release commands reversible (bin/gstack-version-bump:475) → not raised natively → accepted, C2 release-tool contract (isolated staging worktree, allowed outputs, pre/postimages, conflict-aware rollback); #5 durable state machine, attempt ids, `execution_unknown` (bin/gstack-autoplan-snapshot.ts:659) → native F1 (Critical: no verb for the host-dispatched reviewer) → accepted together, B2 is `next`/`submit` over `lib/autoplan-run.ts` with attempt ids, exclusive ownership, `EXECUTION_UNKNOWN` reconciliation, no automatic redispatch; #6 gate answers can finalize an unreviewed plan (bin/gstack-autoplan-snapshot.ts:777) → not raised natively → accepted, `answer --gate-rev` rejects stale replies atomically and reopens dependent phases, Eng last, when a choice changes reviewed content; #7 hashing prose cannot detect omitted findings (:677) → not raised natively → accepted, canonical structured findings per reviewer reconciled by id, validator rejects duplicates, cycles, malformed tails, path escapes; #8 runner adapters not equivalent (lib/outside-review-result.ts:4) → native F2 (runtime choice in a render-time resolver; scripts/resolvers/outside-voice.ts:8-12) → accepted together, `bin/gstack-outside-voice` with one adapter interface, `api` labeled `supplied-input review` unless it runs a repo tool loop; #9 spend controls promised in B, implemented in E (scripts/ship-measure.ts:409, test/helpers/pricing.ts:4) → native dependency map agrees (E1 ledger) → accepted, minimal `lib/spend-ledger.ts` moves to B-runner, host-subagent spend reported `unknown`, `--deadline` cancels children; #10 suppressing prompts does not enforce privacy (bin/gstack-skill-start:179, :310; bin/gstack-review-log:99) → native F7 adjacent (kind whitelist, :93) → both accepted, B1 whitelists `unattended`, enforces at writers and sync entrypoints, tests egress sinks; #11 installer bootstrap and false-success paths (bin/gstack-doctor:22, setup:296, bin/gstack-bun-version.sh:15) → native F5 (table language) and F11 (`bun upgrade --version` absent on 1.3.14) → all accepted, A1 table generated into sourced bash, floor vs supported minimum, empty host set and unmet `--revision` never `ok`; A2 uses setup's checksum recipe; #12 CI parsing and import closure cannot establish lane coverage (test/gstack-doctor.test.ts:25, scripts/test-free-shards.ts:235) → native F15 (bash bins unmapped) → accepted, `lanes` unions imports, path literals, the bin→test convention and declared dependencies, conservative on uncertainty, persisted `requires-remote` obligations; #13 pre-gate lacks late-change invalidation (bin/gstack-evidence:394) → not raised natively → accepted, every check records input identity, results must be current after stamping, `secrets` and required lanes cannot be downgraded, timeout is `incomplete`; #14 ratchet premise inaccurate, `--add-rationale` weakens the convention (test/helpers/module-size.ts:202, :225) → native premise correction agrees → accepted, Context corrected, every new module registered, `--add-rationale` removed (pre-gate prints the entry, author appends); #15 artifact and review-log migration (scripts/resolvers/tasks-section.ts:77, lib/review-evidence.ts:188) → native F8 (exit codes contradict) and F9 (codes do not fit `gate-outcomes`) → all accepted, B0 `tasks` v2 enumerated, `bindReview` validates before consuming the token, `lib/result-codes.ts` sibling table, B7 exit matrix fixed (usage 2, rejected row 1); #16 `--next` bypasses the version-source contract (lib/version-source.ts:135) → not raised natively → accepted, C2 reads the configured source at the base revision; #17 hermetic runner does not isolate (test/helpers/hermetic-env.ts:49, :118, :147) → native F16 (bin would import test code) → both accepted, `lib/hermetic-env.ts`, isolated `HOME`, `GIT_CONFIG_GLOBAL=/dev/null`, `GIT_CONFIG_NOSYSTEM=1`; #18 checklist reduction and fourth spec pass lack an acceptance criterion (scripts/resolvers/spec-review.ts:Line truncated
|
||||
|
||||
Native-only findings: F4 guard clause targets the wrong host (scripts/resolvers/composition.ts:11; Claude Code deny-on-absent must stay) → accepted, B1 rewritten, `GUARD_NOT_INSTALLED` printed by `gstack-skill-start` under hosts that do not execute hooks and under `unattended`, validator requires the line; F6 zero parity headroom on ship/review/autoplan/qa → accepted, Context and Delivery shape carry the measured ratios and each PR's byte-delta acceptance; F12 shallow-clone policy path → accepted (`POLICY_SOURCE_UNAVAILABLE`, exit 3); F17 Validation 2 tied to Codex → accepted (either runner); F18 pin-file containment fixtures → accepted (C1); F19 Windows classification → accepted (B2 acceptance). Rejected: none. Deferred: the outside architecture's `gstack-capy-install → dependency-free bootstrap` box is already A2's preflight and needs no new component.
|
||||
|
||||
Required outputs: Scope Challenge, architecture diagrams, failure-mode registries (14 outside, 19 native rows), test-plan maps (20 named new or extended test files from the outside voice; folded into B2, C2, C3, C8 acceptance above), size analysis and the per-PR complexity table are in `docs/designs/multi-agent-wave-2026-10-10/eng-native.md` and `eng-outside.md`. New decision D12 (seven PRs) is recorded in Gate decisions with the recommended option.
|
||||
|
||||
<!-- autoplan-accepted:eng -->
|
||||
- Context: ratchet premise corrected (registered modules only; new modules registered; parity headroom ratios).
|
||||
- Delivery shape: seven PRs (A, B-core, B-runner, C-stamp, C-pregate, D, E) with dependency column, byte-delta acceptance, one owner per shared file; Effort re-counted with LOC; D12 added; D1 reworded.
|
||||
- A1 table generated into sourced bash; no-Bun, empty-host and unmet-revision fixtures; floor vs supported minimum. A2 Bun via setup's checksum recipe.
|
||||
- B0 `lib/result-codes.ts`; terminal-line rule scoped to workflow commands; canonical findings reconciled by id; validator rejects duplicates, cycles, malformed tails, path escapes; versioned `tasks` v2; `bindReview` validates before consuming the token.
|
||||
- B1 guard line printed by `gstack-skill-start` on non-executing hosts and under `unattended`; `unattended` whitelisted at skill-start:93; privacy enforced at writers and sync entrypoints with egress-sink tests.
|
||||
- B2 `next`/`submit` state machine with attempt ids, exclusive ownership, `EXECUTION_UNKNOWN`, `answer --gate-rev` with phase reopening; `lib/spend-ledger.ts` in B-runner; recovery, concurrency and invalidation tests; Windows classification.
|
||||
- B3 `bin/gstack-outside-voice` runtime runner with one adapter interface; `api` labeled when supplied-input only. B4 `scope` optional/`always`, obligation map, replace-never-add. B7 exit matrix (usage 2, rejected 1). B8 deterministic issue verification, no fourth launch.
|
||||
- C1 `POLICY_SOURCE_UNAVAILABLE`, containment fixtures, `queue_mode` in schema, example files. C2 version source via resolver; release-tool contract; race binding and conditional merge; evidence branch key. C3 committed heads only; per-gate equivalence; per-file-type rules; bundle provenance; split lockfile fixtures. C6 explicit closure intent. C8 non-downgradable checks, input identity and invalidation, ratchet entry printed not appended, lanes union and conservative selection, persisted remote obligations, `lib/hermetic-env.ts`, regen in a `git worktree`.
|
||||
- E1 consumes B-runner's ledger; E2 `verify` over C3's bundle; E3 maintainers from policy or push permission. P7 measured through `run.json.consumed_by`, the ship receipt and `gstack-ship-receipt census`. Validation 2 accepts either outside runner; Validation 5 adds `typecheck:test`, `build`, `format:cso:check`, credential scan, quality judges.
|
||||
<!-- /autoplan-accepted:eng -->
|
||||
|
||||
### Phase 4 close (21:18 UTC; gate written, approved by the owner at 21:30 UTC)
|
||||
|
||||
Whole cycle: 20:30 → 21:18 UTC, 48 min wall for three dual-voice phases on a 4-core Capy machine, no Claude Code harness. Outside voice total 569 s of Codex `gpt-6-astra` (three runs, spend not metered by the CLI); native reviewers were Claude subagents on this thread's model, spend unmetered. 65 reviewer findings reconciled (64 accepted, 1 partially accepted, 0 rejected); 13 gate items (UC1, D1–D12), all pending the owner.
|
||||
|
||||
Files beside this plan in `docs/designs/multi-agent-wave-2026-10-10/`: `decisions.jsonl` (13 rows, `kind` approval or auto, `recommended`, `gate_rev` 1), `findings.jsonl` (65 rows: phase, voice, model, severity, disposition, plan items, native counterpart), `tasks.jsonl` (28 rows, schema v2: pr, tier, `depends_on`, `findings`, `blocked_by` the gate), `timing.json`, `run.json` (manifest with artifact hashes, `status: gate_pending`), and the six reviewer reports. These are the B0 file set, written by hand this once because the tooling that writes and validates them is what PR B-core builds; their shapes are the v1 contract B0 freezes.
|
||||
|
||||
Deviations from the skill as written, stated rather than implied: the Spec Review Loop (B8's subject) was not run on this plan; no `review-log` JSONL rows were written because the state root on this machine is ephemeral (A3's subject) and nothing durable was configured; the phase-publication-hook guard was not enforced because this host executes no hooks (B1's subject), so publication order rests on the snapshot hashes recorded per phase; every mid-run question was auto-decided on the recommended option and logged in the phase records. The final gate below is the one decision the run does not take.
|
||||
|
||||
Gate: the owner replied `all` ("Take recommendations and approve"), so UC1 and D1–D12 are decided on option (a). Nothing reviewed changed, so no phase reopens. Implementation starts with PR A and PR B-core in parallel on fresh machines (disjoint files; A owns `bin/gstack-doctor`, `setup`, `bin/gstack-capy-install`; B-core owns `lib/headless-artifacts.ts`, `bin/gstack-skill-start`, `bin/gstack-session-kind`, `bin/gstack-review-log`), B-runner and C-stamp stacked on B-core.
|
||||
@@ -0,0 +1,232 @@
|
||||
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.
|
||||
@@ -0,0 +1,47 @@
|
||||
1. **[Severity: High] One operating environment is being treated as a market.** The 27 threads describe two related repositories, one coordinator pattern, and one cloud environment—not 27 independent customer validations. Thirteen threads stopping after installation does not establish whether installation, workflow relevance, latency, or preference for existing tooling caused abandonment. Before funding B–D, test a minimal headless workflow across unrelated repositories and measure repeat usage, operator interventions, completion time, and cost against their existing workflows.
|
||||
|
||||
2. **[Severity: Critical] “A tree that passed never gets another gate” is an invalid release promise.** B3 treats equality modulo stamp paths as sufficient to refuse another gate, although runtime, dependencies, test selection, model configuration, and external state can change without changing that tree. Existing reuse explicitly requires complete input and environment identity, trusted receipt provenance, and bounded age; unknown inputs disable reuse ([scripts/eval-input-cache.ts:4](/workspace/gstack/scripts/eval-input-cache.ts:4), [scripts/eval-input-cache.ts:63](/workspace/gstack/scripts/eval-input-cache.ts:63)). Stamp-path globs also do not prove that every change within those files is behaviorally irrelevant. Replace P3 with per-check reuse under the existing identity contract, retaining release-specific freshness requirements and checks of the final stamped artifacts.
|
||||
|
||||
3. **[Severity: High] Machine-readable output is being confused with independently verifiable work.** Six files and derived counters can make an incomplete review internally consistent: an omitted finding disappears from both the findings file and its totals. The snapshot implementation explicitly distinguishes input identity from evidence that a reviewer consumed it, while plan-review records bypass the stronger diff-review binding path ([bin/gstack-autoplan-snapshot.ts:659](/workspace/gstack/bin/gstack-autoplan-snapshot.ts:659), [lib/review-evidence.ts:183](/workspace/gstack/lib/review-evidence.ts:183)). A4 lacks a versioned run manifest binding required phases, reviewer outcomes, decisions, input hashes, and artifact completeness. Make that manifest and a deterministic completion validator the primary deliverable; test interrupted runs, missing reviewers, stale artifacts, and omitted findings before promising that parents need only `jq`.
|
||||
|
||||
4. **[Severity: High] Session mode does not solve host compatibility.** D2 dismisses a host adapter by examining question capabilities, but host identity already determines which outside reviewer executes: Codex renders select Claude Code, while other renders select Codex ([scripts/resolvers/outside-voice.ts:8](/workspace/gstack/scripts/resolvers/outside-voice.ts:8)). Consequently, the proposed Codex login helper and `GSTACK_CODEX_MODEL` override cannot supply outside coverage for every installed profile. The existing native dispatch also requires a child’s native file-reading tool, which a session-kind flag cannot create ([bin/gstack-autoplan-snapshot.ts:661](/workspace/gstack/bin/gstack-autoplan-snapshot.ts:661)). Define host capabilities, reviewer transport, and approval policy separately, then prove the exact Capy dispatch path before rejecting an adapter.
|
||||
|
||||
5. **[Severity: High] The urgent repair is hostage to twelve initiatives.** PR A combines installation fixes with artifact export, checklist extraction, interactive decision redesign, score-loop changes, timing prediction, and reviewer reconciliation. That contradicts the stated reason for splitting the wave—landing P0 earlier—and makes the 1.5-day estimate unsupported by any demonstrated throughput or validation budget. Ship readiness reporting independently, then one headless review-to-pending-gate flow; require adoption evidence before adding the remaining A features or starting B–D.
|
||||
|
||||
6. **[Severity: High] The queue design assumes version churn must be managed rather than eliminated.** B1–B7 build a release coordinator spanning predecessor merges, version allocation, historical text rewriting, generated files, and a second repository. The existing allocator already solves collision avoidance by advancing beyond claimed versions; that is different from stamping the next item against main at merge time ([bin/gstack-next-version:92](/workspace/gstack/bin/gstack-next-version:92)). The plan does not compare this machinery with assigning versions after merge, separating release metadata from feature PRs, or delegating stamping entirely to the repository’s release tool. Prototype those alternatives first, and remove generic historical-text rewriting and cross-repository mutation unless their necessity and recovery semantics are demonstrated.
|
||||
|
||||
7. **[Severity: High] The pre-gate can prevent the gate needed to satisfy it.** B8 blocks remote gates until relevant platform lanes have run, while the exclusions explicitly leave Windows execution to CI; a Linux machine with a Windows-affecting change therefore has no specified forward path. Its proposed dependency foundation is also narrower than claimed: `paidTestClosure` follows test helpers and fixtures and explicitly calls its result a lower bound ([test/helpers/touchfile-closure.ts:1](/workspace/gstack/test/helpers/touchfile-closure.ts:1), [test/helpers/touchfile-closure.ts:25](/workspace/gstack/test/helpers/touchfile-closure.ts:25)). Running affected tests plus hermetic reruns is not substantiated as a two-minute operation. Separate local deterministic preflight, remote coverage execution, and final evidence acceptance; scope blockers to prerequisites that can actually exist at that stage.
|
||||
|
||||
8. **[Severity: High] Repository policy files acquire authority the plan never defines.** B1, B2, and B4 introduce executable commands and cross-repository writes under a workflow advertised as unattended, but containment of a JSON file does not establish trust in its commands or mirror target. The existing version writer deliberately requires opt-in before executing a target repository’s generator, specifically because file presence must not silently authorize code execution ([bin/gstack-version-bump:374](/workspace/gstack/bin/gstack-version-bump:374)). Define which trusted actor authorizes command execution, allowed write roots, and policy changes, preserving existing host permissions. Without that boundary, “headless never approves” is a prose promise sitting above tools with unspecified authority.
|
||||
|
||||
9. **[Severity: High] `/eval-plan` is a second product with an overstated budget guarantee.** D1 expands the wave into experimental design, scorer calibration, financial admission, data isolation, remote persistence, and long-running workload supervision without establishing demand for that product. The proposed source machinery explicitly uses estimated admission costs reconciled after execution, and its pricing helper excludes several billing categories ([scripts/ship-measure.ts:22](/workspace/gstack/scripts/ship-measure.ts:22), [test/helpers/pricing.ts:4](/workspace/gstack/test/helpers/pricing.ts:4)). Extracting those functions does not establish a hard cap across independent workers or machines. Defer D1; if pursued separately, distinguish estimated admission limits from enforceable spending limits and validate concurrent reservations, unknown charges, crash recovery, and held-out-data isolation.
|
||||
|
||||
10. **[Severity: High] Review compliance is replacing evidence of review quality.** C1 makes more rows mandatory, A6 removes methodology based on detected scope, and validation mainly asks whether known incidents produce expected artifacts; none establishes whether the resulting reviews catch unfamiliar failures at an acceptable false-positive rate. A8 additionally assumes a 7/10 stopping criterion that the cited implementation does not use: it stops on PASS, repeated issues, or three launches, and explicitly says a missing score does not require another review ([scripts/resolvers/spec-review.ts:182](/workspace/gstack/scripts/resolvers/spec-review.ts:182)). “Rerun until 7+” risks selecting a favorable reviewer result instead of confirming repairs. Compare current and condensed workflows on a fixed, held-out incident panel, and gate on unresolved consequential findings rather than row presence or a resampled overall score.
|
||||
|
||||
11. **[Severity: Medium] The install verdict still lacks a definition of intended readiness.** A1 defaults requirements to installed hosts, which cannot reveal a missing intended host unless the caller already knows to request it; checking a patch heading establishes presence, not behavior. Runtime discovery also mixes compatibility requirements with CI selections: this repository declares Bun `>=1.4.2`, while its runtime helper separately distinguishes a security floor and supported minimum ([package.json:80](/workspace/gstack/package.json:80), [bin/gstack-bun-version.sh:10](/workspace/gstack/bin/gstack-bun-version.sh:10)). Make readiness explicitly relative to a requested workflow, host, and capability set, distinguishing gstack’s runtime from the target project’s requirements. Test wrong-host, missing-authentication, and incompatible-runtime cases, not just missing files.
|
||||
|
||||
12. **[Severity: High] Default contributor rewriting undermines the maintenance model.** D3’s prescribed output offers `supersede` or `decline`, omitting acceptance of a correct external implementation; D10 makes this treatment the default based on authorship. Reimplementing working contributions adds maintainer load precisely while this wave creates more tools, schemas, and platform obligations to maintain. Evaluate contribution behavior and evidence under the same acceptance criteria as maintainer changes, preserve a direct accept path, and reserve rewriting for demonstrated architectural or quality problems.
|
||||
|
||||
Premise verdicts:
|
||||
The 27-thread sample establishes broad demand for this wave — UNVERIFIED; supplied context lacks independent adoption evidence or a causal comparison.
|
||||
Doctor currently lacks `--check` and treats below-tested Bun as a warning — VALID; [bin/gstack-doctor:34](/workspace/gstack/bin/gstack-doctor:34), [bin/gstack-doctor:138](/workspace/gstack/bin/gstack-doctor:138).
|
||||
`setup --status` exits successfully after reporting installation checks — VALID; [setup:136](/workspace/gstack/setup:136).
|
||||
Headless question failure blocks, and onboarding suppression currently targets spawned sessions — VALID; [scripts/resolvers/preamble/generate-ask-user-format.ts:29](/workspace/gstack/scripts/resolvers/preamble/generate-ask-user-format.ts:29), [bin/gstack-skill-start:394](/workspace/gstack/bin/gstack-skill-start:394).
|
||||
Raw plans need no literal implementation heading — VALID; normalization wraps raw input, subject to boundary validation ([bin/gstack-autoplan-snapshot.ts:409](/workspace/gstack/bin/gstack-autoplan-snapshot.ts:409)).
|
||||
Review-log counters are not derived from findings — VALID; supplied records are bound and stamped, although diff freshness already rejects nonzero issue counts ([lib/review-evidence.ts:178](/workspace/gstack/lib/review-evidence.ts:178), [lib/review-evidence.ts:238](/workspace/gstack/lib/review-evidence.ts:238)).
|
||||
Codex authentication alone supplies every host’s outside reviewer — FALSE; Codex-host reviews select Claude Code ([scripts/resolvers/outside-voice.ts:8](/workspace/gstack/scripts/resolvers/outside-voice.ts:8)).
|
||||
The existing spec-review loop requires a score of at least seven — FALSE; its stopping rules use PASS, repeated findings, and launch count ([scripts/resolvers/spec-review.ts:188](/workspace/gstack/scripts/resolvers/spec-review.ts:188)).
|
||||
The existing touchfile closure provides general source-to-test dependency coverage — FALSE; its traversal is restricted to helpers and fixtures and is explicitly incomplete ([test/helpers/touchfile-closure.ts:1](/workspace/gstack/test/helpers/touchfile-closure.ts:1)).
|
||||
Tree equality alone establishes reusable gate evidence — FALSE; current reuse requires complete consumed inputs, runtime identity, provenance, and freshness ([scripts/eval-input-cache.ts:4](/workspace/gstack/scripts/eval-input-cache.ts:4)).
|
||||
Extracting ship-measure admission logic establishes a hard spending cap — FALSE; admission uses estimates and reconciles actual costs afterward ([scripts/ship-measure.ts:22](/workspace/gstack/scripts/ship-measure.ts:22)).
|
||||
The full pre-gate takes two minutes and PR A takes 1.5 days — UNVERIFIED; no supporting measurements are supplied.
|
||||
|
||||
Scores:
|
||||
premises valid: 5/10
|
||||
right problem: 6/10
|
||||
scope calibration: 2/10
|
||||
alternatives explored: 2/10
|
||||
risks covered: 3/10
|
||||
6-month trajectory: 3/10
|
||||
|
||||
Recommendation: Re-scope to capability-specific readiness and one verifiable headless review flow, deferring B–D because broad adoption demand is unproven and the proposed gate-reuse guarantee is unsound.
|
||||
@@ -0,0 +1,13 @@
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-uc1", "label": "UC1", "title": "Scope of the committed wave", "options": ["a", "b", "c"], "recommended": "a", "kind": "user_challenge", "status": "approved", "default_if_unanswered": null, "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d1", "label": "D1", "title": "Delivery shape", "options": ["a", "b", "c"], "recommended": "a", "kind": "approval", "status": "approved", "default_if_unanswered": null, "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d2", "label": "D2", "title": "Unattended as a session kind, not a host", "options": ["a", "b"], "recommended": "a", "kind": "auto", "status": "approved", "default_if_unanswered": "a", "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d3", "label": "D3", "title": "The Capy installer moves into the repo", "options": ["a", "b"], "recommended": "a", "kind": "auto", "status": "approved", "default_if_unanswered": "a", "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d4", "label": "D4", "title": "Repo policy lives in `.gstack/*.json` pin files, command execution opt-in, read from the committed base copy", "options": ["a", "b", "c"], "recommended": "a", "kind": "auto", "status": "approved", "default_if_unanswered": "a", "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d5", "label": "D5", "title": "The pre-gate blocks by default", "options": ["a", "b"], "recommended": "a", "kind": "auto", "status": "approved", "default_if_unanswered": "a", "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d6", "label": "D6", "title": "Unattended never approves", "options": ["a", "b"], "recommended": "a", "kind": "approval", "status": "approved", "default_if_unanswered": null, "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d7", "label": "D7", "title": "Eval skill name:", "options": ["a", "b", "c"], "recommended": "a", "kind": "auto", "status": "approved", "default_if_unanswered": "a", "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d8", "label": "D8", "title": "Review severity vocabulary:", "options": ["a", "b"], "recommended": "a", "kind": "auto", "status": "approved", "default_if_unanswered": "a", "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d9", "label": "D9", "title": "Chromium:", "options": ["a", "b"], "recommended": "a", "kind": "auto", "status": "approved", "default_if_unanswered": "a", "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d10", "label": "D10", "title": "Contributor-PR mode is on for outside authors by default", "options": ["a", "b"], "recommended": "a", "kind": "auto", "status": "approved", "default_if_unanswered": "a", "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d11", "label": "D11", "title": "Gate reuse rests on identity, not tree equality alone", "options": ["a", "b"], "recommended": "a", "kind": "auto", "status": "approved", "default_if_unanswered": "a", "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-d12", "label": "D12", "title": "PR shape after the Eng sizing", "options": ["a", "b"], "recommended": "a", "kind": "approval", "status": "approved", "default_if_unanswered": null, "gate_rev": 1, "source": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md#gate-decisions-2026-10-10-awaiting-owner", "chosen": "a", "answered_at": "2026-10-10T21:30Z", "reply": "all"}
|
||||
@@ -0,0 +1,165 @@
|
||||
INPUT: dx ffe831074afb86b6b394d6b0e0383034883c94a61dbce855e29b0738e5f11753
|
||||
|
||||
Read: `/workspace/gstack/.gstack/tmp/autoplan/autoplan-dx-U0huJT/native-prompt.md`, lines 1-229, SHA-256 `482ebf2c…e83fe` (verified with `sha256sum`); the embedded implementation plan hashes to `ffe83107…f11753` (verified against `dx-implementation.md`). Repo at `origin/main 91bbd9e`, VERSION `1.91.69.0`. Every claim below marked **confirmed** or **refuted** was checked against a file:line on this checkout. The dispatch asked me to ask questions or stop at the gate; I auto-decided every one with the recommended option and record that in Findings and Tasks.
|
||||
|
||||
## Step 0
|
||||
|
||||
### Persona A: parent Capy agent installing gstack on a subagent machine, running autoplan unattended, consuming the outputs
|
||||
|
||||
**Who they are.** A Capy thread's Capy, running a Claude or GPT model on a cloud Ubuntu box. No Claude Code harness, no Skill tool, no AskUserQuestion, no hooks. Reads files with a native tool, runs bash, and spends the owner's money by the token. It judges a tool by one question: *can I grep the answer?*
|
||||
|
||||
**What they do today (confirmed).** They run `scripts/install.sh --check` from the user-scope drive skill. `check_install` is a bare `test -x`/`test -s`/`grep -Fq` chain (`~/.capy/drive/user-garry-tan/skills/gstack/scripts/install.sh:7-14`) under `set -euo pipefail` (`:2`), invoked as `check_install; exit 0` (`:32-35`). A failed probe kills the script with status 1 and *no output*; the only success text is printed from inside the Playwright smoke (`:24`). Through `| tail` the parent sees exit 0 and nothing printed. The plan's P0-1 reproduction stands.
|
||||
|
||||
**Empathy narrative.** The parent asks "is gstack ready on machine 3?" and gets silence. Silence is the one thing an agent cannot act on: it isn't PASS, isn't FAIL, and its training says "no output from a check means fine." Then it tells a subagent to "load gstack and run autoplan," the subagent spends 20 minutes of model time improvising a review because the skills are not on disk, and the parent only learns this when the output lacks a `## Review record`. Thirteen of 28 threads did exactly that. The fix the parent wants is not a better doctor dashboard (the dashboard already exists: `bin/gstack-doctor:51-53` prints `%-15s %-15s %s` rows and exits 1 on fail, `:455-456`); it wants one grep-stable line it can put in a `test`.
|
||||
|
||||
**Developer journey trace (current), with friction points.**
|
||||
|
||||
1. Parent reads drive `SKILL.md`, runs `bash scripts/install.sh --check`. **F1:** silent exit 1, or exit 0 through a pipe. No row, no fix hint, no revision.
|
||||
2. Runs the installer without flags: clone + `./setup --host claude --no-prefix` + `./setup --host codex --model gpt-6-astra` + `bunx playwright install --with-deps chromium` (`install.sh:43-50`). **F2:** 86 s, 115 MiB of Chromium, on a machine that will only plan. There is no flag to skip it; the only skip is the undocumented `GSTACK_SKIP_PLAYWRIGHT=1` on `setup` (`setup:2071`).
|
||||
3. Runs `bin/gstack-doctor`. **F3:** 13 rows of prose-ish columns; "7 ok, 2 warn, 3 not configured, 0 fail", exit 0, with Bun 1.3.14 vs tested 1.4.2 as `warn`. The parent cannot tell whether a gate on this Bun is sound. No `--json` (only `--live` and `-h` are parsed, `bin/gstack-doctor:37-38`).
|
||||
4. Dispatches a subagent: "read `~/.claude/skills/autoplan/SKILL.md` and run it on plan.md". **F4:** the preamble classifies the session as `interactive` (any ambiguity → interactive, `bin/gstack-session-kind:16-18`), so the skill calls AskUserQuestion, which does not exist; the fallback prose brief ends the subagent's turn. If the parent sets `GSTACK_HEADLESS=1`, the skill BLOCKs at the first question (`generate-ask-user-format.ts:28`). If it sets `GSTACK_SESSION_KIND=spawned`, the final approval gate is auto-approved (`:15`, exception only for destructive options).
|
||||
5. The run ends with a prose record. **F5:** The parent has to read `ACTIVE_PLAN` to find decisions; `tasks-<phase>-*.jsonl` exist per phase but no aggregate, no manifest, no completion check.
|
||||
6. **F6:** Learnings and decisions went to `~/.gstack` on the ephemeral box (`lib/state-root.ts:22-29` chain ends at `$HOME/.gstack`) and are gone at wake.
|
||||
7. **F7:** Codex outside voice needs `codex login`; the plan shows it works from `OPENAI_API_KEY` in 3.7 s but nothing in doctor (`bin/gstack-doctor:186` says "codex login, or export CODEX_API_KEY") or `outsideVoiceFailurePolicy()` (`scripts/resolvers/outside-voice.ts:216`) names the API-key path.
|
||||
|
||||
**First-time roleplay (post-plan).** "`bin/gstack-capy-install --only autoplan` … `gstack: ok 1.91.70.0 for=autoplan`. Good. `GSTACK_SESSION_KIND=unattended`, run autoplan. … `AUTOPLAN_RESULT: gate_pending run=…/run.json decisions=…`. `gstack-artifact validate run.json` → exit 0. `jq 'select(.status=="pending")' decisions.jsonl` → 7 gates. Now what? I answer them… how? I re-run autoplan? Does it redo three review phases? There is no `resume`." That last question is the biggest hole in the plan (Finding 1).
|
||||
|
||||
**TTHW.** Current: zero to a trusted "ready" verdict on a fresh machine is unbounded (the check cannot be trusted), and zero to a consumed unattended review does not exist (needs the harness). Target per plan: ≈ 25 s to `gstack: ok … for=autoplan` without Chromium (clone + two `setup` renders), ≈ one run to `AUTOPLAN_RESULT`, and one `jq` to the gate list. Add the missing resume step and the full loop closes in two commands.
|
||||
|
||||
### Persona B: coordinator agent running "ship, next in queue at <version>" for three PRs that reorder twice
|
||||
|
||||
**Who they are.** A single thread merging one PR at a time into a repo with its own `release:restamp` script, a CHANGELOG, `(was X)` doc mentions, a mirror repo, and a full test gate that costs 40+ minutes on an Ubicloud VM. It decides order from CI state and risk, so order changes.
|
||||
|
||||
**Empathy narrative.** Today `/ship` Step 12 claims a version at PR-open time by reading the live PR queue and sibling worktrees (`bin/gstack-next-version:80-105`, `pickNextSlot` bumps past the highest claim). The moment PR 2 is merged before PR 1, PR 1's claim is wrong, its CHANGELOG heading is under the wrong version, every `(was X)` in docs is stale, and the mirror disagrees. The coordinator hand-edits five files in two repos, reruns the 40-minute gate because "the tree changed," and does it again on the next reorder. The feedback says they stopped using `/ship` and used the repo's own tool. What they want: "stamp when it's my turn, in one command, show me the tree is the same thing I already gated."
|
||||
|
||||
**Developer journey trace (plan's target), with friction points.**
|
||||
|
||||
1. Coordinator decides PR 3 is next. Runs `gstack-restamp --queue-mode stamp-at-merge --next` in PR 3's worktree. **F8:** `--next` is defined as "main's VERSION at this moment plus the policy bump, through the next-version queue reader" (plan C2). The queue reader's whole job is to read *claims* (`bin/gstack-next-version:80-105`). Under stamp-at-merge nobody claims, so either the reader is bypassed (then say so) or stale claims from pre-wave PRs bump the number unexpectedly. Unspecified.
|
||||
2. `gstack-restamp` wants to run `bun run release:restamp` from `.gstack/ship-policy.json`. **F9:** standalone it exits 3 with `repo commands not executed (pass --allow-repo-commands)` (C1). Correct posture, but the coordinator, who is not inside `/ship`, has to learn one more flag, and the plan doesn't say whether the stamp of VERSION/CHANGELOG/`stamp_paths` still happens (partial stamp) or nothing happens. A half-stamped tree with the release tool skipped is the worst outcome.
|
||||
3. Receipt: `gstack-tree-receipt` prints `TREE: same-modulo-stamps` and `gate-reuse: eligible`. **F10:** invocation unspecified. The receipt needs the gated head and the predecessor head; where does the coordinator get them, `gate-ahead.json`? `gstack-evidence`? The plan names outputs, not inputs.
|
||||
4. Reorder #1: PR 1 now goes before PR 3. PR 3 needs `--after 1` (gate-ahead). **F11:** `--after` and `mirror` are **tier 2** (C2). The P3 promise ("works") is tier 1; the piece that makes reordering cheap can slide to the next PR. The coordinator's headline use case is then half-delivered.
|
||||
5. Reorder #2: `PREDECESSOR MOVED <old> -> <new>` refusal. Good. **F12:** what does the coordinator do next? The line has no fix clause (criterion 3), while every doctor row has one (`bin/gstack-doctor:53`).
|
||||
6. Merge. **F13:** the ship receipt (C5) is the first fenced block of the PR body, good, but has no `schema_version` field listed even though B0 says `ship-receipt` is in the contract, and there is no reader (`gstack-ship-receipt read --pr N`) for `/land-and-deploy` to consume it with; the plan says `/land-and-deploy` "reads it" without a command.
|
||||
|
||||
**TTHW.** Current: one reorder ≈ 20 minutes of hand edits plus a repeated 40-minute gate. Target: `gstack-restamp --after <pr>` + `gstack-tree-receipt` ≈ 2 minutes, zero manual edits, gate reused when the receipt says `eligible`. Achievable only if `--after` is tier 1 and the receipt's inputs are named.
|
||||
|
||||
## Passes 1-8
|
||||
|
||||
| # | Dimension | Pre | Post | The change that moves it |
|
||||
|---|---|---|---|---|
|
||||
| 1 | Onboarding / TTHW | 2 | 8 | A1+A2 as written, plus one copy-paste "parent agent quickstart" doc with the three commands (install, run, validate) and the resume command (Finding 1). Without resume the loop is open and the score is 6. |
|
||||
| 2 | Discoverability | 3 | 7 | `gstack-doctor --for <skill>` prints *which* components it required (today the row names and the `--require` ids are different vocabularies). `bun gstack-autoplan-snapshot prompt <phase>` surfaces the existing `nativeDispatchPrompt` (`bin/gstack-autoplan-snapshot.ts:661`). Add `gstack-artifact schema <name>` so a parent can read the contract without the repo. |
|
||||
| 3 | Error messages | 4 | 7 | Plan has fix clauses for doctor rows and `review-log: status mismatch`; missing for `PREDECESSOR MOVED`, `gate-reuse: not-eligible`, `repo commands not executed`, validator failures, hook `guard unavailable`. Repo convention is a reason code in `lib/gate-outcomes.ts` with a `docs/troubleshooting.md` anchor (`lib/gate-outcomes.ts:69-79`); route every new error family through it. |
|
||||
| 4 | CLI / flag consistency | 3 | 7 | One component-id vocabulary; one verdict grammar (`PASS|FAIL|SKIP <id>` rows and a key=value trailer); `--for` *or* `--require`, not both; one exit-code table for the five new bins (exit 2 = usage everywhere, as doctor does at `:12`, `:39`); state `--queue-mode` default and whether it is required. |
|
||||
| 5 | Machine-readability | 5 | 9 | B0 is the right shape. Add `--json` on `gstack-doctor --check` (planned), `gstack-artifact validate` (not planned), `gstack-tree-receipt`, `gstack-pregate` (planned), `gstack-gate parse` (planned); one terminal-status line grammar shared by autoplan and cso (`AUTOPLAN_RESULT:` vs `CSO_RUN:` today are two shapes). Enumerate `run.json.status`. |
|
||||
| 6 | Docs / examples | 2 | 6 | The plan names fixtures and `INCIDENTS.md` but no user-facing doc for unattended runs, ship policy, or the receipt. Each pin file (`ship-policy.json`, `generated.json`, `pregate.json`, `banned-terms.json`) needs a copy-paste example and a `gstack-<tool> init` that writes it. |
|
||||
| 7 | Owner gate experience | 4 | 8 | B9 gate list + E3 one-page brief + fixtures from real owner replies (`d10 yes`, `1a 2a`) is strong. The reply grammar is printed with the list. Gap: deferred asks and `URGENT` block come in different PRs (D1 tier 1 vs B12), so the brief is incomplete for one release. |
|
||||
| 8 | Recoverability | 2 | 7 | Interrupted-run validation fails loudly (B0). Missing: resume from `gate_pending` with answers (Finding 1); resume of an interrupted phase without redoing finished phases (`run.json` has phase outcomes, so this is a flag away); a `--dry-run` on `gstack-restamp` is implied ("shown as a dry-run diff before writing") but not a flag. |
|
||||
|
||||
## Findings
|
||||
|
||||
**Finding 1 — Critical. Unattended autoplan has an exit (`gate_pending`) but no entry for the answer.** B2 ends the run with `AUTOPLAN_RESULT: gate_pending …` and D6 says the run never approves. B9 gives `gstack-gate parse` which turns `all except 3b` into JSON. Nothing in B2, B9, or E3 says how the parent feeds that JSON back and gets `plan.md` finalized ("every `draft direction stands until the owner decides` line is rewritten to the decided option", E3, is a phase-close operation with no trigger named). The parent's only option is to re-run autoplan, which redoes three paid phases. Evidence: the snapshot CLI has `init` (`bin/gstack-autoplan-snapshot.ts:833`) and the plan adds `export` and `prompt`; no `resume`/`answer`. Fix: `bun gstack-autoplan-snapshot answer --decisions decisions.jsonl --reply "<grammar>"` (or `--answers <json from gstack-gate parse>`) that validates ids against pending gates, appends `kind:"owner"` rows with `status:"decided"`, runs the Phase 4 close operations (rewrite draft lines, write final `plan.md`, update `run.json.status` to `complete`), and prints `AUTOPLAN_RESULT: complete …`. Document it as step 3 of the parent quickstart.
|
||||
|
||||
**Finding 2 — Critical. Two vocabularies for the same component.** A1 rows are "Claude skills, Codex skills, the Astra behavioral patch, browse bundle, Chromium…"; A2's default is `--require claude,codex,patch`; the trailer is `gstack: fail <component,component>`. If the trailer prints "Claude skills" the parent cannot map it to a `--require` id, and `--json` keys are a third guess. Today's doctor already has this problem in miniature (`row ok codex …` vs `check_codex`, `bin/gstack-doctor:186,446`). Fix: one `COMPONENTS` table in `lib/doctor-components.ts` (id, human label, probe, fix, needed-by skills), used for row ids, `--require`, the trailer, `--json` keys, and `--for` resolution; `--for <skill>` prints `required: claude codex patch` before the rows.
|
||||
|
||||
**Finding 3 — High. `--next` under stamp-at-merge is self-contradictory.** C2: "`--next` (main's VERSION at this moment plus the policy bump, through the next-version queue reader)". The queue reader resolves collisions against *claimed* PR versions (`bin/gstack-next-version:80-105`, `pickNextSlot`). Stamp-at-merge means no PR claims a version until it is next, so either the reader finds stale claims from pre-wave PRs and over-bumps, or it is a no-op. Fix: under `--queue-mode stamp-at-merge`, `--next` reads `origin/<base>:VERSION` + policy bump only and prints `claims: ignored (stamp-at-merge)`; the reader stays for the legacy claim-at-open mode. State the default `--queue-mode` (recommend: read from `ship-policy.json`, else legacy) so the flag is not required on every call.
|
||||
|
||||
**Finding 4 — High. The P3 promise depends on tier-2 pieces.** `--after <pr>` and `mirror` are tier 2 in C2; the queue replay test (Validation 3) asserts both. If they slide to a follow-up, PR C ships a restamp that works only when the PR is already next and the gate must rerun after every reorder, which is the incident. Fix: promote `--after` to tier 1 (it is the gate-ahead that makes reorders cheap) and leave `mirror` tier 2; or re-word P3 to "stamp when next" and move the reorder claim to the follow-up. Auto-decided: promote `--after`.
|
||||
|
||||
**Finding 5 — High. Error lines without a fix clause or docs anchor.** Criterion 3 asks for problem + cause + fix + link. Plan lines that have none: `PREDECESSOR MOVED <old> -> <new>`, `gate-reuse: not-eligible (<what changed>)`, `repo commands not executed (pass --allow-repo-commands)` (fix, no cause/link), `Autoplan publication guard is unavailable`, `autoplan guard: not installed; publication order is unverified`, every `gstack-artifact validate` failure. The repo already has the mechanism: reason codes in `lib/gate-outcomes.ts` each carrying a `docs/troubleshooting.md#anchor` (`lib/gate-outcomes.ts:69,74,79`; AGENTS.md "Key conventions"). Fix: add `RESTAMP_PREDECESSOR_MOVED`, `GATE_REUSE_NOT_ELIGIBLE`, `REPO_COMMANDS_NOT_AUTHORIZED`, `ARTIFACT_INVALID_*`, `GUARD_NOT_INSTALLED` to gate-outcomes with anchors, and have each bin print `<line> — fix: <text> (<anchor>)`.
|
||||
|
||||
**Finding 6 — High. P7's "parent consumed the artifacts" has no command behind it.** B2: "when the parent reports it, `consumer`". Tier 3 of PRs D and E unlock only on that number. Without a one-liner the number is zero forever and the plan's own gate never opens. Fix: `gstack-artifact ack <run.json> --consumer <thread-id>` appends the row to `analytics/autoplan-timing.jsonl` and sets `run.json.consumed_by`; the parent quickstart puts it after `validate`. Pair it with `gstack-artifact validate` returning exit 0 only when consumed? No, keep them separate: validate is a check, ack is a report.
|
||||
|
||||
**Finding 7 — Medium. Terminal-status lines use two grammars.** `AUTOPLAN_RESULT: gate_pending run=<path> decisions=<path> …` (B2) and `CSO_RUN: <run id> findings=<path>` (B2 cso bullet). The parent needs one regex. Fix: `GSTACK_RESULT: skill=<name> status=<complete|gate_pending|incomplete|interrupted> run=<path>` from every unattended skill, with the status enum also in `run.json.status`. Keep the old prefixes as aliases for one release if any test pins them.
|
||||
|
||||
**Finding 8 — Medium. Three ways to skip Chromium with no stated precedence.** `GSTACK_SKIP_PLAYWRIGHT=1` (env, `setup:2071`), the new `./setup --no-browser` (A2), and lazy-by-default in `gstack-capy-install` with `--with-browser` to override (D9). Fix: `--no-browser` sets the env var internally; the installer passes `--no-browser` unless `--with-browser`; document one sentence of precedence in `setup --help` (there is a test for help coverage, `test/setup-help.test.ts`).
|
||||
|
||||
**Finding 9 — Medium. `--for <skill>` and `--require <csv>` are two spellings of one input.** A1 offers both. Pick `--for <skill>` (resolves to ids via the component table) and let `--require <csv>` exist only as the low-level form it resolves to; print the resolved set so the parent learns the ids. Also state how `--check` composes with `--live` (`bin/gstack-doctor:37`): `--check` must never run the paid probe unless `--live` is also passed.
|
||||
|
||||
**Finding 10 — Medium. The hook-absent semantics are described as one behavior but are two.** B1: "the hook allows the call when no guard is installed and prints `autoplan guard: not installed`". Confirmed: the shell hook's backstop *denies* when its TS half fails (`autoplan/bin/phase-publication-hook:4-9`), which is the right behavior for a broken install. If the hook *file* is absent, a host that honors frontmatter fails the hook before anything prints; the only place to change that is the `{{AUTOPLAN_PUBLICATION_HOOK}}` render (`autoplan/SKILL.md.tmpl:31`). Fix: split the two cases in the plan text: (a) hook file absent → the resolver renders a one-line inline `command` that prints the "not installed" allow JSON; (b) hook present, `.ts` fails → keep deny. The review record carries line (a).
|
||||
|
||||
**Finding 11 — Medium. Durable state on a Capy drive conflicts with drive policy.** A3 sets `GSTACK_STATE_ROOT` to `~/.capy/drive/project-user-*/gstack-state`. Capy's drive guidance for agents says the drive is not for memory, scratch, or verification evidence; learnings and review logs are exactly that. The alternative the plan already names, "say plainly they are skipped", is honest and cheap. Fix: default to `learnings: skipped (ephemeral)` plus the `STATE_ROOT: <path> durable=no` line; make the drive root an explicit opt-in (`GSTACK_STATE_ROOT` set by the owner or `--state-root drive`). Owner decision recorded as: opt-in. Also specify how "a Capy cloud home" is detected (an env marker, not a path heuristic).
|
||||
|
||||
**Finding 12 — Medium. `gstack-artifact validate` has no `--json` and no exit-code contract.** B0 calls it "the deterministic completion check a parent runs first" but lists only failure causes. Fix: exit 0 valid, 1 invalid (with one `INVALID <reason-code> <path>` line per problem), 2 usage; `--json` emits `{valid, errors:[{code, path, message}]}`; `gstack-artifact schema <name>` prints the JSON Schema for the version it validates, so a parent on another machine can read the contract.
|
||||
|
||||
**Finding 13 — Medium. Pin files have no scaffold.** Four new JSON pin files (`ship-policy.json`, `generated.json`, `pregate.json`, `banned-terms.json`) each with a schema, none with an `init`/example. A coordinator adopting the wave hand-writes JSON against a TypeScript type they must open the repo to read. Fix: `gstack-restamp init-policy`, `gstack-regen init`, `gstack-pregate init` write a commented example (`"$schema"`, every key with its default), and `docs/ship-policy.md` shows the gstack repo's own files.
|
||||
|
||||
**Finding 14 — Low. Partial-stamp behavior when repo commands are refused.** C1 exit 3 "lists the commands" standalone. It must also say whether VERSION/CHANGELOG/`stamp_paths` were written. Fix: refuse before any write (all-or-nothing) and say `nothing written`.
|
||||
|
||||
**Finding 15 — Low. The "Not in this wave" premise on hosts is slightly off.** It says "no resolver reads `capabilities.questions`". Confirmed no resolver *branches* on it, but `scripts/host-config.ts:178` validates it and every host declares it (`hosts/claude.ts:7`, `hosts/codex.ts:7`). The decision (D2a) still holds; just correct the sentence so a future reader doesn't re-litigate it on a false premise.
|
||||
|
||||
**Finding 16 — Low. Review-log mismatch exit code.** B7 uses exit 2 for `status mismatch`; `bin/gstack-review-log:95-97` exits 1 for invalid JSON and doctor uses 2 for usage (`bin/gstack-doctor:12`). Use 1 for a rejected row, keep 2 for usage, and put the table in `--help`.
|
||||
|
||||
## Required outputs
|
||||
|
||||
### Developer journey map (post-plan, both personas)
|
||||
|
||||
| Stage | Parent agent (Persona A) | Coordinator (Persona B) | Artifact / line consumed |
|
||||
|---|---|---|---|
|
||||
| Install | `bin/gstack-capy-install --only autoplan` | same, `--only ship` | `gstack: ok <rev> for=autoplan` |
|
||||
| Preflight | `gstack-doctor --check --for autoplan --json` | `gstack-doctor --check --for ship` | `PASS|FAIL|SKIP <id>` rows, trailer |
|
||||
| Run | `GSTACK_SESSION_KIND=unattended` + autoplan on plan.md | `gstack-restamp --after <pr>` then `/ship` | `GSTACK_RESULT: …`, stamp-only diff |
|
||||
| Verify | `gstack-artifact validate run.json` | `gstack-tree-receipt --gated <sha>` | exit 0 / `gate-reuse: eligible` |
|
||||
| Decide | `jq 'select(.status=="pending")' decisions.jsonl` → owner → **`snapshot answer --reply "all except 3b"`** (missing today) | reuse gate or `--force-gate <reason>` | `decisions.jsonl` `kind:"owner"` |
|
||||
| Report | `gstack-artifact ack run.json --consumer <thread>` (missing today) | PR body ` ```gstack-ship-receipt ` first | P7 counter / receipt |
|
||||
| Recover | interrupted → `validate` fails → `snapshot resume` (missing) | `PREDECESSOR MOVED` → rerun `--after <new>` (needs fix clause) | reason codes with anchors |
|
||||
|
||||
### DX Implementation Checklist
|
||||
|
||||
- [ ] `lib/doctor-components.ts`: single component table (id, label, probe, fix, needed-by); row ids, `--require`, trailer, `--json` keys and `--for` all read it (Finding 2).
|
||||
- [ ] `gstack-doctor --check`: `PASS|FAIL|SKIP <id> [— fix: …]` rows; trailer `gstack: <ok|fail> rev=<v> for=<skill|hosts> fail=<csv>`; `--json`; `--check` never runs the paid probe without `--live` (Finding 9).
|
||||
- [ ] `./setup --no-browser` documented in `setup --help`; precedence sentence over `GSTACK_SKIP_PLAYWRIGHT` and `--with-browser` (Finding 8).
|
||||
- [ ] `GSTACK_RESULT:` one grammar for autoplan and cso; `run.json.status` enum (Finding 7).
|
||||
- [ ] `bun gstack-autoplan-snapshot answer|resume` and its doc (Finding 1).
|
||||
- [ ] `gstack-artifact validate --json`, exit table, `schema <name>`, `ack` (Findings 6, 12).
|
||||
- [ ] Parent-agent quickstart doc: install → run → validate → answer → ack, every command copy-paste, under `docs/unattended.md`; the user-scope skill links it.
|
||||
- [ ] `gstack-restamp`: `--queue-mode` default from policy; `--next` ignores claims under stamp-at-merge and says so; `--dry-run`; all-or-nothing when repo commands are refused; `--after` tier 1 (Findings 3, 4, 14).
|
||||
- [ ] `gstack-tree-receipt`: documented inputs (`--gated <sha>`, `--predecessor <sha>` or `--from gate-ahead.json`), `--json`, fix clause on `PREDECESSOR MOVED` and `not-eligible` (Findings 5, F10).
|
||||
- [ ] `gstack-ship-receipt`: `schema_version` in the block; `read --pr N` for `/land-and-deploy` (F13).
|
||||
- [ ] New reason codes in `lib/gate-outcomes.ts` with `docs/troubleshooting.md` anchors for every new error family (Finding 5).
|
||||
- [ ] `init` scaffolds for the four pin files plus `docs/ship-policy.md` (Finding 13).
|
||||
- [ ] Plan text fixes: hook-absent split (Finding 10), state-root opt-in (Finding 11), hosts sentence (Finding 15), review-log exit code (Finding 16).
|
||||
- [ ] Exit-code table shared by all new bins (0 ok, 1 fail, 2 usage, 3 refused/needs flag) in each `--help`; a `--help` coverage test for the new bins alongside `test/setup-help.test.ts`.
|
||||
|
||||
### Magical moments
|
||||
|
||||
1. **`gstack: ok 1.91.70.0 for=autoplan` in under 30 seconds on a fresh box**, with Chromium never downloaded. The parent greps one line and moves on. The first time this fires on a machine that used to take 86 s and lie, the trust problem ends.
|
||||
2. **`gstack-artifact validate run.json && jq 'select(.status=="pending")' decisions.jsonl`** returning seven numbered gates with recommendations and costs, no prose read. Then `snapshot answer --reply "all except 3b"` and the plan is final. Two commands, zero paid reruns. This moment exists only with Finding 1 fixed.
|
||||
3. **`gate-reuse: eligible`** after a restamp, with the identity it rests on printed (tree-modulo-stamps, pins, lockfile, CI hashes). A 40-minute gate skipped with a receipt that a reviewer can audit. This is the moment the coordinator stops using the repo's own tool.
|
||||
4. **`OUTSIDE_STATUS: completed provider=host-subagent model=gpt-6-astra`** in a Capy run with no Codex CLI, never `[single-model]`. The outside voice becomes a model choice, not a CLI install.
|
||||
|
||||
### Competitive benchmark: how Claude Code, Codex, and Capy natively expose structured subagent output
|
||||
|
||||
- **Claude Code** subagents (Task/Agent tool) return a single free-text result to the parent; structured output exists only when the prompt asks for JSON and the parent parses it, with no schema or validator. `claude -p --output-format json` wraps the final message in a typed envelope (`result`, `session_id`, cost) but the content is still prose. gstack's B0 (`run.json` manifest + `schema_version` + validator) is ahead of the harness: it is the only one of the three that gives a parent a deterministic completion check.
|
||||
- **Codex CLI** `codex exec` streams JSONL events and exits with the agent's status; the final message is text. There is no artifact manifest. gstack's `GSTACK_RESULT:` line plus files is comparable to Codex's last JSONL event; the gain is files with hashes a parent can validate later.
|
||||
- **Capy** subagents end with a completion summary (`message_agent` final), a `tools.subagent.get` summary field, and files attached by path; `pr_create` gives a PR with checks and findings a parent reads through `tools.pr.getFindings` as structured rows. The pattern Capy already uses, identifiers first then prose and a typed findings list, is the pattern C5 and B2 adopt. Where gstack should match Capy: Capy's subagent output is addressable without knowing a path (by subagent index); gstack's `AUTOPLAN_RESULT` names paths, so the parent must parse a line to find the files. Printing the run directory once (`run=<dir>`) with fixed file names inside (`run.json`, `decisions.jsonl`, …) matches Capy's "one handle, known shape" and shortens the parent's regex to one capture group.
|
||||
- **Net:** with Finding 1 (resume) and Finding 7 (one result grammar) fixed, gstack's unattended contract is stricter than any of the three harnesses' native output, which is the right place for a tool whose primary user is another agent.
|
||||
|
||||
## Scores
|
||||
|
||||
- Initial overall (plan as written, averaged over the eight passes): **3.1 / 10**. The artifact contract and gate list are strong; the loop is open at the answer step, component naming is split, the queue-mode semantics contradict the queue reader, and errors mostly lack fix clauses.
|
||||
- Target overall (with the tasks below, all P1 and P2): **7.6 / 10**. Remaining gap is docs volume and the tier-2 slide risk on `mirror`.
|
||||
|
||||
## Tasks
|
||||
|
||||
```json
|
||||
[
|
||||
{"id":"DX-1","priority":"P1","component":"autoplan","title":"Add `gstack-autoplan-snapshot answer|resume`: consume gate-list replies, run Phase 4 close operations, finalize plan.md and run.json.status","files":["lib/autoplan-export.ts","bin/gstack-autoplan-snapshot.ts","lib/gate-list.ts","autoplan/sections/unattended.md.tmpl","docs/unattended.md","test/autoplan-unattended.test.ts"],"effort_human":"3 days","effort_cc":"3 hours","source_finding":"Finding 1"},
|
||||
{"id":"DX-2","priority":"P1","component":"doctor","title":"One component table for row ids, --require, --for resolution, trailer and --json keys; print the resolved required set","files":["lib/doctor-components.ts","bin/gstack-doctor","bin/gstack-capy-install","test/gstack-doctor.test.ts"],"effort_human":"2 days","effort_cc":"2 hours","source_finding":"Finding 2"},
|
||||
{"id":"DX-3","priority":"P1","component":"restamp","title":"Define --queue-mode default (from ship-policy) and make --next ignore claims under stamp-at-merge with an explicit line","files":["lib/restamp.ts","bin/gstack-restamp","lib/ship-policy.ts","bin/gstack-next-version","test/gstack-restamp-queue.test.ts"],"effort_human":"1 day","effort_cc":"1.5 hours","source_finding":"Finding 3"},
|
||||
{"id":"DX-4","priority":"P1","component":"restamp","title":"Promote --after <pr> (gate-ahead) to tier 1 so P3's reorder promise ships in PR C","files":["lib/restamp.ts","bin/gstack-restamp","test/gstack-restamp-queue.test.ts"],"effort_human":"2 days","effort_cc":"2 hours","source_finding":"Finding 4"},
|
||||
{"id":"DX-5","priority":"P1","component":"errors","title":"Reason codes with docs/troubleshooting.md anchors for PREDECESSOR MOVED, gate-reuse not-eligible, repo commands refused, artifact invalid, guard not installed; every line carries fix:","files":["lib/gate-outcomes.ts","docs/troubleshooting.md","lib/tree-receipt.ts","lib/restamp.ts","lib/headless-artifacts.ts","autoplan/bin/phase-publication-hook"],"effort_human":"1.5 days","effort_cc":"1.5 hours","source_finding":"Finding 5"},
|
||||
{"id":"DX-6","priority":"P1","component":"artifacts","title":"`gstack-artifact ack <run.json> --consumer <id>` so the P7 adoption number is measurable; `validate --json`, exit table, `schema <name>`","files":["lib/headless-artifacts.ts","bin/gstack-artifact","docs/unattended.md","test/gstack-artifact.test.ts"],"effort_human":"1 day","effort_cc":"1.5 hours","source_finding":"Finding 6, Finding 12"},
|
||||
{"id":"DX-7","priority":"P1","component":"docs","title":"Parent-agent quickstart (install → run → validate → answer → ack) with copy-paste commands; link from the user-scope Capy skill","files":["docs/unattended.md","README.md","agents-digest/gstack-AGENTS.md"],"effort_human":"1 day","effort_cc":"1 hour","source_finding":"Pass 6, Pass 1"},
|
||||
{"id":"DX-8","priority":"P2","component":"unattended","title":"One terminal-status grammar `GSTACK_RESULT: skill= status= run=` for autoplan and cso; run.json.status enum","files":["lib/headless-artifacts.ts","autoplan/sections/unattended.md.tmpl","cso/SKILL.md.tmpl","test/autoplan-unattended.test.ts"],"effort_human":"0.5 day","effort_cc":"45 min","source_finding":"Finding 7"},
|
||||
{"id":"DX-9","priority":"P2","component":"tree-receipt","title":"Specify receipt inputs (--gated, --predecessor | --from gate-ahead.json), add --json, fix clause on refusals","files":["lib/tree-receipt.ts","bin/gstack-tree-receipt","ship/SKILL.md.tmpl","land-and-deploy/SKILL.md.tmpl"],"effort_human":"1 day","effort_cc":"1 hour","source_finding":"Journey F10, Finding 5"},
|
||||
{"id":"DX-10","priority":"P2","component":"ship-receipt","title":"schema_version in the fenced receipt and `gstack-ship-receipt read --pr N` for /land-and-deploy","files":["bin/gstack-ship-receipt","lib/headless-artifacts.ts","land-and-deploy/SKILL.md.tmpl"],"effort_human":"0.5 day","effort_cc":"45 min","source_finding":"Journey F13"},
|
||||
{"id":"DX-11","priority":"P2","component":"setup","title":"Document ./setup --no-browser and the precedence over GSTACK_SKIP_PLAYWRIGHT and --with-browser in setup --help","files":["setup","bin/gstack-capy-install","test/setup-help.test.ts"],"effort_human":"0.5 day","effort_cc":"30 min","source_finding":"Finding 8"},
|
||||
{"id":"DX-12","priority":"P2","component":"pin-files","title":"`init` scaffolds for ship-policy.json, generated.json, pregate.json, banned-terms.json plus docs/ship-policy.md with gstack's own examples","files":["bin/gstack-restamp","bin/gstack-regen","bin/gstack-pregate","bin/gstack-banned-terms","docs/ship-policy.md"],"effort_human":"1 day","effort_cc":"1 hour","source_finding":"Finding 13"},
|
||||
{"id":"DX-13","priority":"P2","component":"plan-text","title":"Split hook-absent vs hook-failed semantics; make drive state root opt-in; correct the hosts capabilities sentence; align review-log exit codes","files":["scripts/resolvers/autoplan-publication-hook.ts","autoplan/bin/phase-publication-hook","bin/gstack-skill-start","bin/gstack-review-log"],"effort_human":"0.5 day","effort_cc":"45 min","source_finding":"Findings 10, 11, 15, 16"},
|
||||
{"id":"DX-14","priority":"P3","component":"restamp","title":"--dry-run flag and all-or-nothing refusal when repo commands are not authorized (`nothing written`)","files":["lib/restamp.ts","bin/gstack-restamp"],"effort_human":"0.5 day","effort_cc":"30 min","source_finding":"Finding 14"},
|
||||
{"id":"DX-15","priority":"P3","component":"cli","title":"Shared exit-code table (0/1/2/3) in every new bin's --help and a --help coverage test for the new bins","files":["bin/gstack-artifact","bin/gstack-restamp","bin/gstack-tree-receipt","bin/gstack-pregate","bin/gstack-gate","test/new-bin-help.test.ts"],"effort_human":"0.5 day","effort_cc":"45 min","source_finding":"Pass 4"}
|
||||
]
|
||||
```
|
||||
|
||||
Recommendation: approve with changes because the artifact contract and gate list are the right shape for an agent-first tool, but the unattended loop has no answer/resume step, the install verdict uses two component vocabularies, and `--next` contradicts the queue reader under stamp-at-merge; fixing DX-1 through DX-7 before PR B and PR C land turns a promising plan into one a parent can drive end to end without reading prose.
|
||||
@@ -0,0 +1,51 @@
|
||||
1. **[Severity: Critical] Gate reuse can certify changed behavior.** C3 excludes entire stamp and generated-file paths, allowing a changed generated program or prompt to qualify as `same-modulo-stamps`; the existing evidence implementation likewise compares allowed filenames, not permitted byte changes ([bin/gstack-evidence:505](/workspace/gstack/bin/gstack-evidence:505)). Conversely, requiring identical raw lockfile hashes rejects ordinary npm restamps because the version writer updates lockfile version metadata ([bin/gstack-version-bump:486](/workspace/gstack/bin/gstack-version-bump:486)). Define narrowly permitted version transformations, retain all other generated bytes in the comparison, and capture actual runtime/container identity and executed test selection alongside declared pins. Test changed generated behavior, metadata-only lockfile changes, and dependency changes separately.
|
||||
|
||||
2. **[Severity: High] The fresh-machine installer fails before its repair step.** A2 upgrades Bun after setup and doctor, but setup exits immediately when Bun is absent or below its floor ([setup:296](/workspace/gstack/setup:296)). Skipping Chromium also leaves the dependency installation and full build on the cold path ([setup:1808](/workspace/gstack/setup:1808)), so planning-only TTHW remains unmeasured. Preflight and provision the runtime before setup, explicitly check `jq`, and measure a clean planning installation separately from browser bootstrap. Add a requested revision and an explicit stale-checkout remedy: “reuse, never pull” otherwise gives different subagents different command contracts.
|
||||
|
||||
3. **[Severity: High] Readiness requirements contradict the supported outside runners.** A1 requires both host profiles and the Astra patch for planning, while B3 promises a complete workflow using an API or another-family host subagent without Codex CLI. A1 also makes the native helper required for `--for cso`, while B2 describes its absence as optional `SKIP`. Define readiness against the selected host, runner, model and workflow capabilities, with `required`, `optional` and `not_applicable` represented explicitly. Test a healthy Capy-plus-Claude-subagent installation with no Codex installation and require an honest workflow-specific verdict.
|
||||
|
||||
4. **[Severity: High] Runtime-pin checks can repair the wrong environment.** A2 enters the gstack checkout for setup without specifying how doctor identifies the target project, and A1 combines project constraints with every CI runtime declaration. Those declarations are platform-specific: the Windows workflow pins Bun and Node separately ([windows-free-tests.yml:83](/workspace/gstack/.github/workflows/windows-free-tests.yml:83)), so treating them as universal local requirements can block a Linux planning run. Require explicit `--project` and workflow/lane selection, show each effective constraint’s source, and distinguish compatibility ranges from tested configurations. Resolve conflicts before changing Bun, and separate gstack’s runtime from a target project’s incompatible runtime.
|
||||
|
||||
5. **[Severity: High] There is no complete unattended invocation or continuation contract.** B2–B4 introduce export, prompt printing and flags such as `--outside-model`, but never identify one executable that owns launch, phase dispatch, termination and resumption. The current snapshot CLI exposes preparation operations, not an unattended runner ([bin/gstack-autoplan-snapshot.ts:830](/workspace/gstack/bin/gstack-autoplan-snapshot.ts:830)), and package binaries expose only browse and make-pdf ([package.json:7](/workspace/gstack/package.json:7)). Specify a runnable parent-agent sequence with explicit project, input, output directory, runner, deadline and spend limit, plus `status`, decision application and `resume` operations. Acceptance must include answering a pending gate and resuming without repeating completed paid reviews.
|
||||
|
||||
6. **[Severity: High] The promised task file cannot replace reading the plan.** B2 preserves today’s task schema, which contains titles, files and free-text source findings but omits verification commands, dependencies and acceptance criteria—even though verification appears in the human task format ([tasks-section.ts:42](/workspace/gstack/scripts/resolvers/tasks-section.ts:42), [tasks-section.ts:77](/workspace/gstack/scripts/resolvers/tasks-section.ts:77)). Its IDs are phase-local `T1`, `T2`, while B2’s finding fields do not explicitly include the finding IDs later required by B12. Define globally unique run-bound IDs, task dependencies, acceptance checks, finding references and blocking decision references. Validate referential integrity and bind each reviewer’s immutable result to the manifest; matching a model-written count cannot establish that findings were not omitted.
|
||||
|
||||
7. **[Severity: High] “Machine-readable first” has no common transport contract.** A1 mixes JSON rows with an always-required text trailer, B2 prints whitespace-delimited paths, C5 provides a Markdown fence without specifying its payload encoding, and C3 supplies text verdicts without a JSON interface. A parent must invent separate parsers, and paths containing spaces immediately break naïve token extraction. Require a shared result envelope and exit-code table, JSON-only stdout under `--json`, diagnostics on stderr, explicit `gate_pending` versus `incomplete` states, and machine-readable recovery actions. Give every new command—including gate, restamp, regen, pregate, receipt, lane-check, hermetic-run and banned-terms tools—discoverable `--help`, argument ownership and a tested `jq` example.
|
||||
|
||||
8. **[Severity: High] Durable state does not yet survive the parent–child boundary.** A3 says the installer “sets” `GSTACK_STATE_ROOT`, but a child shell’s export cannot configure later parent-launched commands; the resolver only consults the environment supplied to each process ([lib/state-root.ts:22](/workspace/gstack/lib/state-root.ts:22)). B2 additionally writes deliverables beside `ACTIVE_PLAN`, which may remain on ephemeral storage despite a durable state root. Return a machine-readable launch configuration that the parent explicitly supplies, require a durable run output directory, and report durability separately for each artifact class. Add an idempotent consumption-acknowledgment command tied to the manifest hash; otherwise P7’s adoption threshold depends on undocumented reporting.
|
||||
|
||||
9. **[Severity: High] Queue reordering lacks an authoritative version-selection rule.** C2 delegates `--next` to an allocator that deliberately advances beyond the highest version claimed by any queued PR ([bin/gstack-next-version:97](/workspace/gstack/bin/gstack-next-version:97)), conflicting with “main’s version plus one when this PR is next.” Checking a predecessor’s head does not establish that it remains the predecessor after reordering, nor that the base stayed unchanged during stamping and gating. Introduce an explicit stamp-at-merge allocation mode with expected base SHA, queue order, predecessor identity and requested-version semantics. Revalidate those expectations immediately before merge and return a structured stale-queue result with the exact recomputation command.
|
||||
|
||||
10. **[Severity: High] Cross-machine gate reuse arrives before portable evidence.** C3 promises reuse of gates run on ephemeral machines, but portable bundles appear only in E2. Existing evidence is explicitly machine-local, and checking every recorded label does not prove every required lane ran ([bin/gstack-evidence:27](/workspace/gstack/bin/gstack-evidence:27)); C5’s aggregate counts do not close that gap. Move the minimum portable evidence package into C, including named required lanes, executed selections, source run URLs, outcomes and artifact hashes. Verify it from a fresh coordinator machine after deleting the gate VM, without rerunning the gate.
|
||||
|
||||
11. **[Severity: High] Restamp has incompatible check/apply semantics and no recovery transaction.** C2 requires generators to produce changed stamp outputs, while C4 immediately uses `git diff --exit-code` to classify changed generated outputs as stale. The underlying version writer already has partial-write failure states ([bin/gstack-version-bump:489](/workspace/gstack/bin/gstack-version-bump:489)); adding release hooks, changelog rewrites and a mirror creates more failure boundaries than “same version is a no-op” addresses. Separate `regen --check` from `regen --write`, define one authoritative release-tool interface, and journal each restamp’s intended inputs and completed writes. Provide resume/rollback behavior that preserves unrelated edits, with injected failures after every stage.
|
||||
|
||||
12. **[Severity: High] Base-only pin files have no first-use workflow.** C1 reads executable policy from the base branch, so a PR introducing `.gstack/generated.json` or its first release command cannot use that declaration to validate itself—even with execution permission. Moreover, a base-declared `bun run release:restamp` still executes the PR’s package script, so pinning the command string does not freeze its implementation; the existing version writer explicitly documents execution of target-repository code ([bin/gstack-version-bump:374](/workspace/gstack/bin/gstack-version-bump:374)). Add policy initialization, validation and effective-configuration inspection, showing source revision, command, working directory and trust scope. Specify an explicit reviewed bootstrap path for new policies and test standalone, ship-invoked and changed-policy behavior.
|
||||
|
||||
13. **[Severity: High] The two-minute pre-gate assumes CI information that YAML does not contain.** C8 promises lane discovery from workflows, but this repository computes its Windows matrix by executing a planner ([windows-free-tests.yml:62](/workspace/gstack/.github/workflows/windows-free-tests.yml:62)) and defines exclusive test classification in TypeScript ([scripts/test-free-shards.ts:243](/workspace/gstack/scripts/test-free-shards.ts:243)). Running all locally affected tests can also exceed two minutes; printing an overrun does not preserve the promised fast feedback. Separate bounded mechanical preflight from required test execution, consume runner-owned selection manifests, and expose `--explain` for selection provenance and uncertainty. Remote requirements must name the workflow/job to dispatch and the receipt needed to clear them.
|
||||
|
||||
14. **[Severity: High] The owner gate can conceal decisions or misinterpret approval.** E3 caps the brief at seven decisions, while this plan presents twelve, and provides no overflow behavior; it also promises defaults for unanswered items despite B1’s pending approval semantics and UC1’s instruction that the owner’s direction stands. B9 leaves `all except d9b` ambiguous between exclusion and override, while bare `yes`/`no` cannot identify a choice across multiple open or multi-option decisions. Preserve every stable decision ID through pagination, explicitly distinguish automatic choices from required approvals, and bind replies to a gate revision. Define the grammar with expected resolved JSON, reject ambiguous aliases, and keep unanswered approvals pending.
|
||||
|
||||
15. **[Severity: Medium] Findings-derived status will collide with existing freshness and error handling.** B7 derives both `clean` and `issues_found`, but the current freshness reader rejects a review whenever `issues_found > 0`, even if its status is clean ([lib/review-evidence.ts:238](/workspace/gstack/lib/review-evidence.ts:238)). Its proposed detailed mismatch error would also be swallowed by the shell wrapper, which suppresses stderr and converts binding failures into “invalid JSON” with exit 1 ([bin/gstack-review-log:88](/workspace/gstack/bin/gstack-review-log:88)). Define separate total/open/resolved counts and update both reader and writer together. Preserve typed errors through the wrapper and test a review with many findings, all resolved.
|
||||
|
||||
16. **[Severity: Medium] Tiering does not define what each merge gate requires.** C2 marks mirror and gate-ahead support tier 2, but Validation 3 requires both in PR C’s queue replay; delivery also alternates between moving tier-2 work to “the next PR” and a separate follow-up. E2 and E3 lack explicit tiers despite the assertion that every item has one. Publish a feature-to-PR-to-tier acceptance matrix, including commands, artifact capabilities and deferred asks. The owner’s gate should approve that concrete matrix, and each installed revision should expose which capabilities actually shipped.
|
||||
|
||||
Persona traces:
|
||||
|
||||
- **Parent Capy agent:** (1) Bootstrap stops before Bun repair or performs an unexpected full build (#2); (2) readiness requires irrelevant hosts or runtime pins (#3–4); (3) the parent invents launch and continuation glue (#5); (4) it resolves artifact references, storage and parser differences before acknowledging consumption (#6–8). **Estimated TTHW:** 45–90 minutes with manual integration to obtain and consume the first valid `gate_pending` bundle; unattended completion has no bounded path as specified.
|
||||
- **Queue coordinator, three PRs reordered twice:** (1) Establish effective policy (#12); (2) choose the next PR and reconcile requested versus allocated version (#9); (3) restamp and recover generator/mirror failures (#11); (4) retrieve gate evidence from the old machine and establish reuse after each reorder (#1, #10). **Estimated TTHW:** 30–60 minutes of orchestration plus one gate’s runtime, with roughly 15–30 additional minutes per reorder and possible full reruns when evidence is unavailable. These are review estimates, not measured runs.
|
||||
|
||||
Scores:
|
||||
|
||||
| Dimension | Score |
|
||||
|---|---:|
|
||||
| Onboarding/TTHW | 3/10 |
|
||||
| Discoverability | 3/10 |
|
||||
| Error messages | 5/10 |
|
||||
| Consistency of CLI/flags | 3/10 |
|
||||
| Machine-readability of outputs | 4/10 |
|
||||
| Docs/examples | 3/10 |
|
||||
| Owner gate experience | 3/10 |
|
||||
| Recoverability after failure | 2/10 |
|
||||
|
||||
Recommendation: Revise before implementation because neither persona has a complete executable path through installation, approval, reordering and recovery, and gate reuse can incorrectly certify changed generated behavior.
|
||||
@@ -0,0 +1,263 @@
|
||||
INPUT: eng d7365d9256d2c956908fce530731115816ab6b477152e78602fc69d51fd14b17
|
||||
|
||||
Read receipt: `/workspace/gstack/.gstack/tmp/autoplan/autoplan-eng-JwMe0e/native-prompt.md`, 230 lines, 67384 bytes, SHA-256 `77ee24a4…3511f` (matches the dispatch); the six lines over 2000 chars (54, 58, 65, 74, 100, 102) were re-read in full via `sed`. Every file:line below was opened on `origin/main 91bbd9e` (v1.91.69.0) in `/workspace/gstack`.
|
||||
|
||||
Prior-phase decisions treated as binding: UC1 (tiered scope, P7 gate), B0 (one artifact contract + validator), B1 (`unattended` as a fourth kind), C1 (repo commands opt-in), C3/D11 (gate reuse on identity), B2 (one runner), A1 (one doctor table), A2 (Bun preflight), C2 (stamp-at-merge, restamp journal), hunk-level receipts, GSTACK_RESULT grammar, gate-list ids/pagination/gate_rev, two-stage pre-gate. Where the plan's own premise table is wrong I say so with the receipt; the plan's D1 "premise table" row would have caught two of these.
|
||||
|
||||
## Scope Challenge
|
||||
|
||||
**What already exists (verified).** `bin/gstack-doctor` is 456 lines of Bash 3.2 with one `check_*` per row, `--live`, exit 1 on `fail` / 2 on usage (bin/gstack-doctor:3-16, :35-40, :442-454). `bin/gstack-session-kind` classifies `spawned|headless|interactive` with the explicit override only honoring `spawned` (bin/gstack-session-kind:33-36); `bin/gstack-skill-start:93` whitelists exactly those three and coerces anything else to `interactive`. The preamble's AUQ rule branches on the same three (scripts/resolvers/preamble/generate-ask-user-format.ts:15-29). `lib/state-root.ts:22-29` owns the chain and `test/state-root-ratchet.test.ts` rejects hand-rolled chains and bare `~/.gstack` prose. `bindReview` is the single write choke point and `reviewFreshness` already downgrades `issues_found > 0` at read time (lib/review-evidence.ts:178-186, :238-240); the bash wrapper swallows binding errors as `invalid JSON` (bin/gstack-review-log:88-96). `gstack-version-bump write` has one opt-in post-write hook with the exact trust rationale C1 wants to generalize (bin/gstack-version-bump:373-402) and a half-write recovery path (:483-495). `gstack-evidence` records `tree`/`wtree` and compares changed paths against an allow-list by filename (bin/gstack-evidence:388-396, :498-512) and is machine-local by design (:27-30). `outsideVoiceFor` picks the provider from `ctx.host` at render time (scripts/resolvers/outside-voice.ts:8-12). The spec-review loop stops on PASS / repeats / third launch and "a missing score alone does not require another review" (scripts/resolvers/spec-review.ts:186-192). The autoplan publication hook renders only for `host === 'claude'` and denies when the file is absent (scripts/resolvers/composition.ts:11, :16-23); the shell backstop denies when the TS half fails (autoplan/bin/phase-publication-hook:4-9). `lib/gate-outcomes.ts:1-18` is the reason-code contract with a `docs/troubleshooting.md` anchor test (`test/troubleshooting-anchors.test.ts`). `setup` honors `GSTACK_SKIP_PLAYWRIGHT=1` (setup:2071-2072) and `GSTACK_SKIP_FONTS=1` (setup:1512), prints a checksum-verified Bun install recipe when Bun is missing (setup:296-305), and `--status` always exits 0 (setup:136-146). Section manifests exist for 21 skills and are documented as PASSIVE registries with no machine predicate (autoplan/sections/manifest.json:5). `investigate/` has no `sections/` dir (278-line monolith; parity ratio 0.787 of a 1.12 cap, so it has room).
|
||||
|
||||
**What the plan rebuilds vs. extends.** PR A extends doctor/setup (right call). PR B's B7/B8/B9/B12 extend existing choke points (right call). PR B's B2/B3 are new subsystems disguised as "thin": a prompt-assembling, dispatching, resumable state machine (B2) and a runtime outside-voice runner (B3) where today only render-time prose exists. PR C is almost entirely new code (restamp, receipt, regen, pregate, ship-policy, ship-receipt, evidence bundle): seven libs and six bins is a realistic count, and the queue-replay fixture alone is a multi-repo harness. PR D is template + resolver work with budget consequences (Section 4). PR E is tier-3 except E1 tier 1 and the E2 bundle, which C3 already pulls forward.
|
||||
|
||||
**Complexity check (my estimate, `[estimated]`, counting what the text implies rather than the table's "3/1/8").**
|
||||
|
||||
| PR | New bins | New libs | Template/resolver edits | Fixtures | Est. LOC | Fits one review sitting? |
|
||||
|---|---|---|---|---|---|---|
|
||||
| A | `gstack-capy-install`, `gstack-browser-ensure` (+ doctor `--check/--for/--require/--json/--project`) | `runtime-pins.ts`, component table (see F5 on language) | setup `--no-browser`, `--status` exit, browse preflight | ~10 (healthy, each missing, wrong host, no auth, range vs exact pin, Windows lane pin) | 900-1200 | yes |
|
||||
| B | `gstack-artifact`, `gstack-autoplan`, `gstack-gate`, `gstack-codex-login`, (+ `gstack-outside-voice`, F2) | `headless-artifacts.ts`, `autoplan-export.ts`, `autoplan-run.ts`, `gate-list.ts`, `outside-voice-api.ts` | preamble (unattended), skill-start, session-kind, autoplan skeleton + `sections/unattended`, 4 checklists, spec-review, review-evidence, review-log, phase-close | ~20 | 3500-5000 | **no** — B2+B3 alone are a PR |
|
||||
| C | `gstack-restamp`, `gstack-tree-receipt`, `gstack-regen`, `gstack-pregate`, `gstack-ship-receipt`, `gstack-hermetic-run` | `ship-policy.ts`, `restamp.ts`, `changelog-check.ts`, `tree-receipt.ts`, `regen.ts`, `pregate/*` (≥6 files), `md-links.ts`, evidence bundle | ship Steps 3/5/12/16/18, land-and-deploy, tests.md.tmpl | ~16 + a 3-PR multi-repo replay | 4000-6000 | **no** — pregate and restamp are separate review sittings |
|
||||
| D | — | `plan-reality.ts` resolver | 6 template files | ~12 | 600-900 | yes, but every edit lands on a zero-headroom skill (Section 4) |
|
||||
| E | `gstack-lane-check`, `gstack-banned-terms`, (`gstack-evidence verify`) | `lane-ownership.ts`, `spend-ledger.ts`, `pricing.ts`, eval-plan sections | new skill + qa/ship sections | ~10 | 1500-2500 | yes (tier 1 only) |
|
||||
|
||||
**PR count vs the repo's own constraints.** The repo has no hard PR-count rule, but it has three byte-level ratchets that bite per PR (parity, context-budget, skill-size) and a release process where every PR is a full gate. Five PRs is the right *shape*; two of them are the wrong *size*. Recommended split (keeps D1(a) in spirit, seven PRs, not ten): B → B-core (B0, B1, B7, B9, B12, B10) and B-runner (B2, B3, B4, B8, B11); C → C-stamp (C1, C2, C3, C5, C7) and C-pregate (C4, C8, C6). Each half is still shippable alone and the queue cost is two extra gates, which the pre-gate PR is designed to make cheaper. Record as decision E1 below.
|
||||
|
||||
## Section 1 Architecture
|
||||
|
||||
```
|
||||
parent agent (Capy thread / coordinator)
|
||||
| GSTACK_LAUNCH: GSTACK_SESSION_KIND=unattended GSTACK_STATE_ROOT=<p> GSTACK_EPHEMERAL=1
|
||||
v
|
||||
bin/gstack-capy-install ──> ./setup --host claude|codex (--no-browser) ──> bin/gstack-doctor --check --for <skill>
|
||||
| ^ table: doctor-components (F5: bash, not TS)
|
||||
| ├─ lib/runtime-pins.ts <── <project>/package.json, .tool-versions, .github/workflows/*.yml
|
||||
| └─ bin/gstack-browser-ensure (lazy Chromium)
|
||||
v
|
||||
bin/gstack-autoplan {run|next|submit|status|answer|resume} ── lib/autoplan-run.ts (state machine; F1)
|
||||
| reads: autoplan/sections/*.md (+ scope field, F9), snapshot.json (nativeDispatchPrompt), gate list
|
||||
| writes: --out/<run>/{plan.md, review-record.md, tasks.jsonl, decisions.jsonl, findings.jsonl, timing.json, run.json}
|
||||
| via lib/headless-artifacts.ts <── bin/gstack-artifact {validate|schema|ack}
|
||||
| outside voice: bin/gstack-outside-voice --runner codex-cli|api|host-subagent (F2) ── lib/outside-voice-api.ts
|
||||
| review log: bin/gstack-review-log --findings findings.jsonl ── lib/review-evidence.ts bindReview (B7)
|
||||
| gate: bin/gstack-gate {render|parse} ── lib/gate-list.ts (ids, gate_rev, pages)
|
||||
| state root: $GSTACK_STATE_ROOT/analytics/autoplan-timing.jsonl (EPHEMERAL on Capy — F3)
|
||||
v
|
||||
/ship (Step 5) bin/gstack-pregate ── lib/pregate/{index,regen,secrets,strays,literals,lanes,import-graph,...}.ts
|
||||
| pins: .gstack/pregate.json .gstack/generated.json (read from origin/<base>, F12)
|
||||
| writes: pregate.json (schema via headless-artifacts)
|
||||
/ship (Step 12/16/18)
|
||||
bin/gstack-restamp --queue-mode stamp-at-merge --next|--version|--after <pr> ── lib/restamp.ts, lib/changelog-check.ts
|
||||
| pins: .gstack/ship-policy.json (lib/ship-policy.ts) journal: .gstack/tmp/restamp-journal.json gate-ahead: .gstack/tmp/gate-ahead.json
|
||||
| calls: bin/gstack-version-bump write, <release_tool> (opt-in), bin/gstack-regen --write, mirror
|
||||
bin/gstack-tree-receipt --from gate-ahead.json ── lib/tree-receipt.ts <── bin/gstack-evidence bundle (portable), gstack-wtree
|
||||
| verdict: TREE: same-modulo-stamps | changed (<paths>) gate-reuse: eligible | not-eligible (<what>)
|
||||
bin/gstack-ship-receipt ── fenced ```gstack-ship-receipt``` (first PR-body section) ── /land-and-deploy read --json
|
||||
```
|
||||
|
||||
**Module-size ratchet risks (corrected).** The plan's premise that `bin/gstack-next-version` (822), `bin/gstack-autoplan-snapshot.ts` (865) and `setup` (4662) are capped by `test/module-size-ratchet.test.ts` is false: the fixture caps 47 `newModules` and 4 `residualFiles`, none of which are those three (test/fixtures/module-size-ratchet.json; the residual list is `scripts/test-free-shards.ts`, `scripts/test-paid-shards.ts`, `browse/src/server.ts`, `test/helpers/claude-pty-runner.ts`). Keeping those bins thin is still good hygiene, but the real ratchet exposure is on files the plan edits and does not name: `scripts/resolvers/plan-gates.ts` (571/800, D2 adds the `Undeclared behavior changes` line), `scripts/resolvers/spec-review.ts` (288/800, B8), `autoplan/bin/phase-publication-hook.ts` (B1), `scripts/lib/measure-bar.ts` (E1), `scripts/resolvers/outside-voice-steps.ts` (B3). All five are in `newModules`, so the 150-line function cap applies to every function touched. New `lib/*` files are *not* ratcheted unless added to the fixture; the plan's "each under 150 lines" for `lib/pregate/*` is unenforced until it adds them.
|
||||
|
||||
**State-root compliance.** Every new path in the plan goes through `GSTACK_STATE_ROOT` or a parent-chosen `--out` dir, which is correct. Two traps: (1) templates and resolvers must use the `gstack-paths --get GSTACK_STATE_ROOT` form with the `:?` guard or `test/state-root-ratchet.test.ts` fails on the first fence (test/state-root-ratchet.test.ts:18-25); (2) `docs/unattended.md` must not write `~/.gstack` in prose (PROSE_ROOT regex, :20) — write `$GSTACK_STATE_ROOT`. The `--out` directory is outside the chain by design; `run.json` should record `state_root` and `state_root_durable` so the validator can say which artifact class landed where.
|
||||
|
||||
**Generated-template compliance.** All prose edits are `.tmpl` + `bun run gen:skill-docs --host all` (scripts/gen-skill-docs.ts:34, :116 supports `all`). The new `{{HOST_ADAPTATION:unattended}}` and `{{PLAN_REALITY_ROWS}}` and `{{COORDINATOR_CONTRACT}}` resolvers must be registered in `scripts/resolvers/index.ts:52` and will be rendered into every host, so each costs bytes on every skill that includes it (Section 4). B4's `scope` field on `sections/manifest.json` reverses the documented passive-registry design (autoplan/sections/manifest.json:5 "No machine predicate here — see docs/designs/v2_PLAN.md:663"); `test/qa-lazy-sections.test.ts:127` asserts passive resolution without inlining. The reversal is defensible for an unattended runner that assembles prompts itself, but the plan must update the note, the `$schema`, and that test rather than add a field nobody reads.
|
||||
|
||||
## Section 2 Edge cases and failure modes
|
||||
|
||||
Registry (critical gap = no test AND no handling AND silent, marked ★):
|
||||
|
||||
| # | Mode | Detection | Handling in plan | Test in plan | Gap |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | `--check` piped through `tail`, caller sees pipe status | trailer line + non-zero exit | A1 prints trailer on every path; SKILL.md says never pipe | doctor fixtures on trailer tokens | ok |
|
||||
| 2 | Bun absent on the target machine, doctor table in TS | `bun` missing | **none** — A1 puts the table in `lib/doctor-components.ts`, doctor is Bash 3.2 and promises to report without Bun (bin/gstack-doctor:22) | none | ★ F5 |
|
||||
| 3 | `bun upgrade --version <pin>` unsupported on Bun 1.3.14 | `bun upgrade --help` lists only `--canary` | A2 assumes the flag | none | ★ F11 |
|
||||
| 4 | Project pin and gstack floor conflict | both constraints printed, installer stops | yes | partial (runtime mismatch fixture) | add conflict fixture |
|
||||
| 5 | `unattended` set but older `gstack-skill-start` coerces to `interactive` (bin/gstack-skill-start:93) | nothing | not mentioned | not mentioned | ★ F7 |
|
||||
| 6 | `GSTACK_SESSION_KIND=unattended` and `GSTACK_HEADLESS=1` both set (eval harness + parent) | override wins at step 0 | implied, not stated | negative control for HEADLESS only | state precedence; add both-set test |
|
||||
| 7 | Capy host: hook file present, hooks never executed | nothing | B1 detects *file absence* only (composition.ts:21-23) | guard fixtures on absence | ★ F4 |
|
||||
| 8 | Claude host: hook file absent (broken install) → B1 turns deny into allow | prints `guard: not installed` | weakens the guard where it ships | — | F4 |
|
||||
| 9 | P7 counter written to ephemeral state root; telemetry `off` in unattended | — | A3 says learnings skipped; B0/B10 still write analytics | none | ★ F3 |
|
||||
| 10 | Unattended runner needs a *native* reviewer the host must dispatch (this review) | run blocks or shells to Codex for both voices | B2 has no `submit`/`next` verb | acceptance says "produces the files" | ★ F1 |
|
||||
| 11 | Outside runner `api` returns truncated/empty output; `host-subagent` result file missing | `OUTSIDE_STATUS` | B3 "never degraded" for *completed*; missing-file path unspecified | — | add refused + reason code |
|
||||
| 12 | Same-family check when `--outside-model` is a Claude id and native is Claude | model-family table | stated as a rule | none | add fixture |
|
||||
| 13 | `findings.jsonl` count matches, content omitted | hash binding in run.json | yes (B0) | omitted-finding fixture | ok |
|
||||
| 14 | `answer` against stale `gate_rev` | refused with current rev | yes (B9) | parse fixtures | ok |
|
||||
| 15 | `d10 yes` (id + bare yes) | ambiguous for multi-option | rejects bare yes/no; id+yes unspecified | fixture quoted | specify: `yes` after an id = recommended option; print the mapping |
|
||||
| 16 | Interrupted run with half-written `decisions.jsonl` | validate fails | `resume` from first incomplete gate | interrupted fixture | ok; require atomic rename on every writer |
|
||||
| 17 | B7 mismatch exit code | exit 2 vs 1 contradiction inside B7; usage today exits 1 (bin/gstack-review-log:81-84) | — | — | F8 |
|
||||
| 18 | `origin/<base>` absent (shallow clone on gate VM) when reading policy | git fails | not stated | none | ★ F12 |
|
||||
| 19 | `--after <pr>` throwaway worktree writes evidence under a synthetic branch key | `gstack-evidence check` on real branch finds nothing | not stated | queue replay doesn't cover | ★ F13 |
|
||||
| 20 | Old VERSION string equals an unrelated dependency version in a lockfile or doc | permitted-transform regex rewrites it | hunk classification per path, patterns "exact" | — | add fixture (F14) |
|
||||
| 21 | Two restamps race (two coordinators) on one branch | journal | journal is per-run | injected failures per stage | add lock on journal path |
|
||||
| 22 | Predecessor head moves between gate and merge | `PREDECESSOR MOVED` refusal | yes (C3) | replay includes it | ok |
|
||||
| 23 | Pre-gate `lanes` for a touched *bash* bin (no importer) | import graph finds nothing → PASS | not stated | — | ★ F15 |
|
||||
| 24 | `gstack-hermetic-run` imports `test/helpers/hermetic-env.ts` from a shipped bin | product typecheck scope | not stated | — | F16 |
|
||||
| 25 | `regen --check` scratch copy of a large repo with `node_modules` | time | "two-minute promise" measured | measured on gstack and gbrain | ok if scratch copy uses `git worktree`, not `cp -r` |
|
||||
| 26 | `.gstack/ship-policy.json` `mirror` or `stamp_paths` escapes repo (`..`, symlink) | containment check | C1 says containment-checked | none named | add escape fixtures |
|
||||
| 27 | Pre-gate downgraded check still fails hard on a first run in a drifted repo | `warn` map | yes (D5) | — | ok |
|
||||
| 28 | 10x: 33-decision gate list, pagination ids | stable ids | yes | approve-all fixture | ok |
|
||||
| 29 | 10x: `findings.jsonl` with 176 open across 4 voices, `validate` referential integrity | O(n) | yes | — | ok |
|
||||
| 30 | Ratchet fixture refresh forgotten → CI red on parity/context budget on every PR B-E | ratchet tests | plan says "clear parity"; no per-PR receipt | — | F6 |
|
||||
|
||||
## Section 3 Test plan
|
||||
|
||||
```
|
||||
codepath test file (free unless marked) fixture
|
||||
───────────────────────────────────────── ────────────────────────────────────────────────── ─────────────────────────────
|
||||
A doctor --check/--for/--require/--json test/gstack-doctor.test.ts (extend) healthy; each missing; wrong host;
|
||||
no auth; bun range vs exact; win lane pin
|
||||
lib/runtime-pins.ts test/runtime-pins.test.ts (new) package.json engines, .tool-versions, nvmrc,
|
||||
workflows w/ per-lane pins
|
||||
gstack-capy-install test/gstack-capy-install.test.ts (new, PATH-stubbed) no-bun; old-bun; conflict; --with-browser; --revision mismatch
|
||||
gstack-browser-ensure test/gstack-browser-ensure.test.ts (new) stub playwright dir; lazy marker
|
||||
setup --no-browser / --status exit 1 test/setup-status.test.ts (extend) broken section link registry
|
||||
B session-kind unattended test/gstack-session-kind.test.ts (extend) override; both-set; unknown value
|
||||
skill-start unattended test/gstack-skill-start.test.ts (extend) no telemetry prompt; UPGRADE_AVAILABLE one-liner
|
||||
preamble AUQ rule test/spawned-consent-rule.test.ts (extend) unattended never records consent
|
||||
headless-artifacts + gstack-artifact test/headless-artifacts.test.ts (new) valid; interrupted; missing reviewer; omitted finding;
|
||||
count/hash mismatch; stale artifact; schema print
|
||||
autoplan-run state machine test/gstack-autoplan-run.test.ts (new) next→submit→close per phase; answer stale rev; resume
|
||||
autoplan export test/autoplan-snapshot.test.ts (extend) plan.md >30KB warn; custom heading
|
||||
outside-voice runner test/gstack-outside-voice.test.ts (new) codex-cli stub; api stub; host-subagent result; same-family refusal
|
||||
review-log --findings test/review-log.test.ts (extend) clean/176-open mismatch; resolved-all → clean
|
||||
gate-list test/gate-list.test.ts (new) `all`, `d3b uc1a`, `all except d9b`, `yes`, `d10 yes`, 33 items/pages
|
||||
spec-review cap test/spec-review*.test.ts (extend) cap → cheap pass vs unconfirmed decision
|
||||
URGENT block order test/autoplan-report-order.test.ts (new) block precedes summary
|
||||
E2E unattended run (paid) test/skill-e2e-autoplan-unattended.test.ts (new) this plan; PR B merge gate (Validation 2)
|
||||
C ship-policy init/validate/show test/ship-policy.test.ts (new) containment escapes; head vs base
|
||||
restamp test/gstack-restamp.test.ts (new) --version; --next; --after; idempotent; dry-run; journal injected failures
|
||||
queue replay test/gstack-restamp-queue.test.ts (new) 3 PRs, mirror, CHANGELOG, (was X), since:, reorder x2
|
||||
tree-receipt test/gstack-tree-receipt.test.ts (new) same-modulo-stamps; changed; lockfile-only; predecessor moved; dep==old version
|
||||
regen test/gstack-regen.test.ts (new) stale golden; --write; command not authorized (exit 3)
|
||||
pregate (per check) test/pregate-<check>.test.ts (new, one per lib file) INCIDENTS.md map: stale goldens, scratch, heavy-only const, win-only change, dropped hunk, |tail
|
||||
ship-receipt test/gstack-ship-receipt.test.ts (new) render; read --json; truncation keeps identifiers
|
||||
ship/sections/tests.md.tmpl pipefail test/ship-tests-section.test.ts (structural) fence contains pipefail + wait $pid
|
||||
D plan-reality resolver test/plan-reality-rows.test.ts (structural) every row id rendered where declared; unattended → incomplete
|
||||
plan-gates undeclared-changes test/plan-gates*.test.ts (extend) fixture diff with omitted behavior
|
||||
investigate sections + manifest test/investigate-lazy-sections.test.ts (new) manifest passive + scope; CARVE_GUARDS entry
|
||||
eval cases (paid) eval:bg:pr on plan-eng-review/plan-ceo-review/review/investigate
|
||||
E spend-ledger test/spend-ledger.test.ts (new) spent+reserved>=cap under lock; unknown charges
|
||||
lane-check test/gstack-lane-check.test.ts (new) two branches intersecting
|
||||
banned-terms test/gstack-banned-terms.test.ts (new) allowed_paths
|
||||
eval-plan skill test/context-budget-ratchet (new ceiling), parity (new invariant), skill-size-budget
|
||||
```
|
||||
|
||||
**Free vs paid.** Everything above is free except the unattended E2E (Validation 2), the `eval:bg:pr` prompt evals for changed templates (Validation 5), and B4's checklist quality (needs a judge). The plan correctly routes paid work after cheap checks; the `pregate` PR should be the one that proves it.
|
||||
|
||||
**Gaps.** (1) No test for the mixed-version case (new `gstack-session-kind` + old `gstack-skill-start`), which is exactly a fleet-upgrade state. (2) No test that `gstack-doctor --check` works with Bun absent (the P1 scenario). (3) No `--help` coverage test exists today (no `test/help-coverage*.test.ts`), so B0's "with a `--help` coverage test" is a new test, name it: `test/bin-help-exit-table.test.ts`, asserting every new bin prints the exit table and exits 0. (4) The `lanes` check needs a fixture where a touched bash bin maps to its spawning test via path literal (F15). (5) The queue replay has no concurrent-restamp case. (6) `gstack-artifact ack` idempotency and the P7 census have no test because the collection path is undefined (F3). (7) Windows: every new bin that is bash (`gstack-capy-install`, `gstack-codex-login`, doctor) must be excluded from or made safe for the curated Windows subset (`scripts/test-free-shards.ts --windows-only`); plan is silent. (8) New test files need durations entries or the shard packer uses a default; check `scripts/free-test-durations.json` handling in the first PR and document it in INCIDENTS.md.
|
||||
|
||||
## Section 4 Performance/size
|
||||
|
||||
**Context budget impact (measured now, `bun` over `test/helpers/parity-harness.ts` invariants against `test/fixtures/parity-baseline-v1.64.1.0.json`):**
|
||||
|
||||
| skill | union bytes | ratio | maxSizeRatio | headroom |
|
||||
|---|---|---|---|---|
|
||||
| ship | 288 804 | 1.539 | 1.539 | **~0 B** |
|
||||
| review | 133 923 | 1.234 | 1.2345 | **~60 B** |
|
||||
| autoplan | 115 143 | 1.129 | 1.1305 | **~150 B** |
|
||||
| qa | 94 736 | 1.127 | 1.1272 | **~20 B** |
|
||||
| plan-eng-review | 145 423 | 1.167 | 1.187 | ~2.5 KB |
|
||||
| plan-ceo-review | 160 085 | 1.055 | 1.081 | ~4 KB |
|
||||
| plan-devex-review | 129 397 | 1.033 | 1.08 | ~6 KB |
|
||||
| investigate (monolith) | 47 702 | 0.787 | 1.12 | ~20 KB |
|
||||
| cso | 33 508 | 0.374 | 1.08 | large |
|
||||
|
||||
Every template edit the plan makes to ship (C5, C6, C7, C8 Step 5, Step 16 stage 1, E2 handoff), review (D2, E3 contributor mode), autoplan (B1 guard line, B2 unattended section, B12, E3 brief, B4 checklist) and qa (E1 soak) will fail `test/parity-suite.test.ts` on the first byte and require a `CARVE_GUARDS` `maxSizeRatio` bump plus a `capture-context-budget` refresh in the same commit. The plan says "every template edit must clear parity" but budgets no bytes and names no receipt; that is a per-PR hidden cost of a few hours and, more importantly, a design pressure: prefer bins that *print* the text (`gstack-gate render`, `gstack-ship-receipt`, `gstack-pregate` table) over prose that *describes* it, and put new prose in lazy sections (they count in the union ratio but not in the eager context-budget ceiling). B4's four 200-line checklists are ~30-40 KB of new union bytes across the plan-review skills; plan-eng-review has 2.5 KB of headroom. B4 should be budgeted as "checklist replaces, never adds": the deep section shrinks by what the checklist takes, with the union byte delta recorded in the PR body. `eval-plan` is a new skill and `test/context-budget-ratchet.test.ts` fails when "a skill exists with no ceiling at all" — PR E must add the fixture entry.
|
||||
|
||||
**Parity-suite risk.** Low for mechanics (it ran green in 269 ms here), high for byte discipline per the table. Also `test/skill-size-budget.test.ts` is a shrink floor; B4 moving methodology out of eager text could trip it on plan-* skills if the skeleton shrinks past the floor — check `minBytes` before carving.
|
||||
|
||||
**Two-minute pre-gate plausibility.** Stage 1 (`regen --check`, `secrets`, `strays`, `literals`, `links`, `lint`, `patches`, `ratchets`) is plausible on gstack only if `regen --check` uses a `git worktree` scratch (not `cp -r` of a repo with `node_modules`) and `gen:skill-docs --host all` fits — today that generation plus the agents digest is tens of seconds; `ratchets` (`module-size`, `state-root`, `parity`, `context-budget`) is a few seconds each. `literals` is a grep over the test tree (fast). `lint` depends on semgrep being absent (prints `not installed`). So ~60-100 s on gstack, measured `[estimated]`; on gbrain unknown. Stage 2 (`lanes`, `guards`, `hermetic`) is unbounded by design (it runs tests) and the plan correctly keeps its own clock. Risk: `lanes` import-graph closure over `scripts/` and `test/` (~2000-line files) is a few seconds with a cached graph; without caching it reparses on every run — cache keyed by `HEAD^{tree}` under `.gstack/tmp`.
|
||||
|
||||
## Findings
|
||||
|
||||
**F1 — Critical. The unattended runner has no verb for the host-dispatched reviewer.** B2 defines `run|status|answer|resume` and claims `run` "owns the unattended loop", yet the native voice on a Capy machine is a subagent the *parent* dispatches from a prompt file (exactly how this review ran; `nativeDispatchPrompt` lives in `snapshot.json`, bin/gstack-autoplan-snapshot.ts). Either `run` blocks forever waiting for a result nobody can hand it, or it shells both voices to Codex and violates B3's same-family rule. Evidence: B3 names runners for the *outside* voice only; nothing names how the native voice is produced without Claude Code. Change: make `lib/autoplan-run.ts` an explicit state machine with `next` (prints the phase, the voice, the prompt path and the expected result path, then exits `gate_pending`-style with `GSTACK_RESULT: status=awaiting_result`) and `submit --phase <p> --voice native|outside --result <file> --model <id>` (validates, binds hash into `run.json`, runs B11 reconciliation and `phase-close`). `run` becomes `next` in a loop only when a runner can execute both voices locally (`api` for both with two families).
|
||||
|
||||
**F2 — High. B3 puts a runtime decision in a render-time resolver.** `outsideVoiceFor(ctx)` picks the provider from `ctx.host` when `gen:skill-docs` renders (scripts/resolvers/outside-voice.ts:8-12); the rendered prose shells out to `codex` directly (scripts/resolvers/outside-voice-steps.ts:177, :327). A `--runner codex-cli|api|host-subagent` flag chosen per run cannot live there without rendering all three branches into every host's SKILL.md (bytes the zero-headroom skills do not have). Change: new `bin/gstack-outside-voice run --runner <r> --prompt <file> --out <file> [--model <id>] [--result <file>]` over `lib/outside-voice-runner.ts`; the resolver names the bin and the runner table once; `outsideVoiceFailurePolicy()` keeps owning the failure prose and adds `codex-cli unavailable; using <runner>`.
|
||||
|
||||
**F3 — Critical. P7's adoption number has no durable collection path.** B0 `ack`, B2 and B10 append to `$GSTACK_STATE_ROOT/analytics/autoplan-timing.jsonl`; A3 declares the Capy state root ephemeral by default and B1 makes unanswered telemetry consent `off`. UC1(a) gates PRs D/E tier 3 on "six of ten threads … a parent consumed", which this design cannot count. Change: make the durable carriers the ones that already leave the machine — `run.json.consumed_by` in the parent-chosen `--out` dir and the `gstack-ship-receipt` block (`session_kind`, `artifacts_consumed: <n>/<m>`) in PR bodies — and add `gstack-ship-receipt census --repo <r> --since <tag>` that computes the P7 number from merged PR bodies via `gh`. Analytics on the state root stay as a bonus, labeled `durable=no`.
|
||||
|
||||
**F4 — High. B1's guard clause detects the wrong condition and loosens the right one.** The publication hook renders only for `host === 'claude'` (scripts/resolvers/composition.ts:11) and the file-absent branch denies (composition.ts:16-23). On Capy the file is *present* (installed from the repo) and hooks are never executed, so "hook file absent" never fires and nothing reports the guard as unenforced; on Claude Code, absence means a broken install, where turning deny into allow removes the only backstop (autoplan/bin/phase-publication-hook:4-9 exists precisely so a broken install cannot pass as absent). Change: keep deny-on-absent for the Claude render; emit `autoplan guard: not enforced by this host; publication order is unverified (GUARD_NOT_INSTALLED)` from `gstack-skill-start` when `SESSION_KIND=unattended` (and on every non-claude host render), and have B0's validator require that line in `review-record.md` for unattended runs. The owner's direction ("allow guarded tool calls when no guard is installed and say so") is satisfied because unattended hosts never ran the hook anyway.
|
||||
|
||||
**F5 — High. A1's component table in TypeScript breaks the doctor's "reports without Bun" contract.** `bin/gstack-doctor` is Bash 3.2 + POSIX "so it still reports when bun or python3 is missing" (bin/gstack-doctor:22), and P1's scenario is a machine with nothing installed, Bun possibly below floor or absent (A2 says the Capy image ships 1.3.14; `setup` exits at :296 without Bun). A `lib/doctor-components.ts` table is unreadable in that state. Change: the table is `bin/gstack-doctor-components.sh` (sourced, one row per component: id, label, probe fn, fix, needed_by); if `--json` or `--for` resolution wants TS, generate the bash table from `lib/doctor-components.ts` at build time with a freshness test, the way `agents-digest` is generated (bin/gstack-version-bump:373-385 describes that pattern). `--check` must be exercised in a fixture with `bun` removed from `PATH`.
|
||||
|
||||
**F6 — High. Byte budgets are unaccounted on four skills with zero headroom.** See Section 4 table: ship 1.539/1.539, review 1.234/1.2345, autoplan 1.129/1.1305, qa 1.127/1.1272 (`test/helpers/parity-harness.ts:247` default plus per-skill `CARVE_GUARDS`). Every PR B-E lands prose on at least one of them. Change: each PR's acceptance lists the union byte delta per skill and the `CARVE_GUARDS`/`capture-context-budget` refresh commit; B4 becomes "replace, never add"; any text a bin can print is printed by the bin.
|
||||
|
||||
**F7 — Medium. `unattended` must be whitelisted in `gstack-skill-start` or it silently becomes `interactive`.** bin/gstack-skill-start:93 `case "$_SESSION_KIND" in spawned|headless|interactive) ;; *) _SESSION_KIND="interactive"`. B1 names session-kind, the preamble and skill-start but not this line, and the failure is the inverse of the intent (prompts return). Change: add `unattended` to the case; add a test that an *unknown* kind prints `SESSION_KIND: interactive (unknown kind '<x>' from gstack-session-kind)` so a mixed-version fleet is loud; state that the step-0 override outranks `GSTACK_HEADLESS` and that the eval harness never sets it.
|
||||
|
||||
**F8 — Medium. B7's exit codes contradict themselves and the code.** The text says a status mismatch is "exit 2" and three sentences later "a rejected row exits 1 … usage stays 2"; today the wrapper's usage exits 1 (bin/gstack-review-log:81-84) and swallows binding errors into `invalid JSON` (:88-96). Change: adopt B0's table — usage 2, rejected row 1 with `review-log: status mismatch: claimed clean, findings.jsonl has 176 open (REVIEW_STATUS_MISMATCH)` on stderr — and fix the wrapper to pass stderr through (drop the `2>/dev/null` on :95).
|
||||
|
||||
**F9 — Medium. New reason codes do not fit `lib/gate-outcomes.ts`'s contract.** `GateOutcome.state` is `ran|not_run|unavailable` with snake_case keys and one anchor each, checked by `test/troubleshooting-anchors.test.ts` (lib/gate-outcomes.ts:1-18). `QUEUE_STALE`, `GATE_REUSE_NOT_ELIGIBLE`, `REPO_COMMANDS_NOT_AUTHORIZED` are not gate states, and `ARTIFACT_INVALID_*` is a wildcard that cannot have one anchor. Change: a sibling `lib/result-codes.ts` with the same `{anchor, summary, fix}` shape and an enumerated artifact-error list; extend the anchor test to both tables; keep snake_case or document the SCREAMING_CASE convention for CLI-visible codes.
|
||||
|
||||
**F10 — Medium. B4's `scope` field reverses a documented design without touching its guards.** autoplan/sections/manifest.json:5 says the manifest is PASSIVE with "No machine predicate here"; `test/qa-lazy-sections.test.ts:127` asserts passive resolution. Change: update the note and `$schema`, make `scope` optional with `always` default, and have the *runner* (F1) be the only consumer; interactive renders ignore it, as the plan says.
|
||||
|
||||
**F11 — Medium. A2 relies on `bun upgrade --version <pin>`, which Bun 1.3.14 does not offer.** `bun upgrade --help` on this machine lists only `--canary`. `setup:296-305` already prints the checksum-verified `BUN_VERSION=… bash install` recipe. Change: the installer runs that recipe (download, print sha256, install to `~/.bun`, re-exec), and A1's `gstack runtime` fix line quotes it.
|
||||
|
||||
**F12 — Medium. Reading policy from `origin/<base>` has no shallow-clone or offline path.** C1 reads pins from the committed base copy; gate VMs often have shallow or detached checkouts. Change: when `origin/<base>` is unresolvable, exit 3 with `policy source unavailable — fix: git fetch origin <base> --depth=1 (POLICY_SOURCE_UNAVAILABLE)`; never fall back to the working tree silently. Also say plainly in `docs/ship-policy.md` that the base read protects the command *string* only (`bun run release:restamp` executes the PR's script), as C1 already concedes.
|
||||
|
||||
**F13 — Medium. Gate-ahead worktree evidence is keyed by the wrong branch.** Review and evidence ledgers are keyed by slug and branch (bin/gstack-review-log:18-22, :99; `gstack-evidence` ledger path, bin/gstack-evidence:13). A throwaway merge worktree with a synthetic branch writes under that name, so the receipt's `gstack-evidence check` on the real branch finds nothing and grades MISSING. Change: `gate-ahead.json` records the evidence branch key and `gstack-tree-receipt --from` reads it; or export `GSTACK_REVIEW_BRANCH`/evidence branch to the real PR branch inside the worktree.
|
||||
|
||||
**F14 — Medium. Permitted version transforms need per-file-type rules and a negative fixture.** C3 classifies hunks; the risk is an old VERSION (or its npm form) coinciding with an unrelated dependency version in `package-lock.json`/`bun.lock` or a doc. Change: lockfile rules match only the root package's `version` keys (`syncNpmLockfiles` already knows where they are, bin/gstack-version-bump:486); `stamp_paths` rules match `(was X)`, `since: X`, `vX` only when `X` equals the old VERSION *and* the replacement equals the new one; fixture where a dependency pin equals the old version must classify as `changed`.
|
||||
|
||||
**F15 — Medium. `lanes` cannot map bash bins to their tests through an import graph.** `test/gstack-doctor.test.ts` spawns `bin/gstack-doctor`; no TS imports it. The precedent (`test/helpers/touchfile-closure.ts:1-6, :20`) already adds string-literal path matching. Change: `lib/pregate/import-graph.ts` unions static imports with repo-path literals and the `bin/<name>` → `test/<name>*.test.ts` naming convention, and reports "no lane found" as `FAIL lanes — <file> has no test lane` rather than passing.
|
||||
|
||||
**F16 — Low. `bin/gstack-hermetic-run` would import test code.** `buildHermeticEnv` lives in `test/helpers/hermetic-env.ts:112` (554 lines); a shipped bin importing it crosses the product/test typecheck boundary (`bun run typecheck` vs `typecheck:test`). Change: move the env builder to `lib/hermetic-env.ts`, re-export from the helper.
|
||||
|
||||
**F17 — Low. Validation 2 ties PR B's merge gate to Codex CLI.** "a Codex login from `OPENAI_API_KEY` only" contradicts B3's acceptance of a Claude subagent as a full outside voice. Change: the gate accepts either runner and records which ran.
|
||||
|
||||
**F18 — Low. Security surface of new pin files is named but not fixtured.** `mirror`, `stamp_paths`, `generated.json` commands, `pregate.json` guards patterns and `banned-terms.json` are all repo-controlled inputs; containment (no `..`, no symlink escape, globs confined to the repo), command execution only under `--allow-repo-commands`, and regex-size caps for `banned-terms`/`guards` patterns need named fixtures. `gstack-regen --check` is still code execution even in a scratch copy and must sit behind the same opt-in.
|
||||
|
||||
**F19 — Low. Windows subset.** New bash bins and tests must be classified for `scripts/test-free-shards.ts --windows-only`; the plan is silent, and the windows lane is exactly the "unrun platform lane" incident C8 targets.
|
||||
|
||||
## Required outputs
|
||||
|
||||
**NOT in scope (confirmed and extended).** The plan's "Not in this wave" list stands (no `capy` host, no semgrep/gitleaks install, no native Windows `/cso`, no issue closing, no stamping on main, no running platform lanes locally). Add: no telemetry transport for P7 (receipts carry it, F3); no change to `headless` BLOCK semantics or any eval case meaning; no Greptile-tier vocabulary (D8a); no automatic `CARVE_GUARDS` bumps (each is a human-visible commit).
|
||||
|
||||
**What already exists (receipts).** Doctor rows + exit codes (bin/gstack-doctor:3-16); session kinds and override (bin/gstack-session-kind:33-66); skill-start kind whitelist (bin/gstack-skill-start:93); AUQ branch rule (generate-ask-user-format.ts:15-29); state-root chain and ratchet (lib/state-root.ts:22-29, test/state-root-ratchet.test.ts); `bindReview`/`reviewFreshness` (lib/review-evidence.ts:178-243); review-log wrapper (bin/gstack-review-log:81-99); version-bump opt-in regen and half-write path (bin/gstack-version-bump:373-402, :483-495); evidence fingerprints and allow-list compare (bin/gstack-evidence:388-396, :498-512); render-time outside voice (outside-voice.ts:8-12, :217-230); spec-review stop rules (spec-review.ts:186-192); publication hook render + backstop (composition.ts:7-32, phase-publication-hook:1-15); gate-outcome contract (lib/gate-outcomes.ts:1-18); setup skip flags and status exit (setup:1512, :2071-2072, :136-146); Bun floor/tested (bin/gstack-bun-version.sh:16-17; package.json:80-81 `>=1.4.2`; windows-free-tests.yml:85-89); passive manifests (autoplan/sections/manifest.json:5); parity/context/skill-size ratchets (test/parity-suite.test.ts, test/context-budget-ratchet.test.ts, test/skill-size-budget.test.ts); module-size fixture (test/fixtures/module-size-ratchet.json: 47 + 4 entries, none of the three bins the plan names).
|
||||
|
||||
**Completion Summary.** The plan is well grounded (most receipts check out) and its decisions are right; it is under-specified where it matters most for P2 (how a native reviewer's output gets back into the run) and over-confident where the repo's ratchets are strictest (ship/review/autoplan/qa bytes). Two premises are wrong (module-size caps on the three bins; `bun upgrade --version`), one promise is unmeasurable as designed (P7), one safety change targets the wrong host (B1 guard), and one component sits in the wrong language for its own promise (doctor table). None of these change the decisions; they change PR B's shape and every PR's acceptance list. Unverified claims in the plan I could not check on this machine: the 86 s Chromium number, the 3.7 s Codex probe, gbrain's pre-gate timing, and the per-PR effort table (all `[estimated]`; the plan labels them so).
|
||||
|
||||
**Dependencies and parallelization.**
|
||||
- A is independent and lands first (D1). F5 and F11 are inside A.
|
||||
- B-core (B0, B1, B7, B9, B10, B12) depends only on A's `GSTACK_LAUNCH` line; B-runner (B2, B3, B4, B8, B11) depends on B-core's schemas and on F1/F2's bins. These two halves can be developed in parallel on fresh machines with B-runner stacked on B-core's branch.
|
||||
- C-stamp (C1, C2, C3, C5, C7) depends on B0 (receipt schema) only; C-pregate (C4, C8, C6) depends on C1 (pin trust boundary) and B0 (`pregate.json`). C-stamp and B-runner are disjoint and can run in parallel; C-pregate stacks on C-stamp.
|
||||
- D depends on B4 (`scope`) for tier 2 only; tier-1 rows can start after B-core. D's byte budget work (F6) is the long pole, not the code.
|
||||
- E tier 1 (E1 ledger + pilot) depends on nothing in B-D except `lib/pricing.ts` extraction; E2 bundle moved into C3. E tier 3 waits for P7 via F3's census.
|
||||
- Shared files with one owner each: `bin/gstack-skill-start` (B-core), `scripts/resolvers/outside-voice*.ts` (B-runner), `ship/SKILL.md.tmpl` and `ship/sections/*` (C-stamp then C-pregate, serialized), `test/fixtures/carve-guards`/`CARVE_GUARDS` (one commit per PR, the PR's author).
|
||||
|
||||
## Scores
|
||||
|
||||
- Architecture: 6/10 — right layering (lib behind bin, pins, one validator) but F1/F2/F5 put three components in the wrong place or language.
|
||||
- Edge cases: 6/10 — queue and receipt edges are strong; session-kind, shallow-clone, worktree-key and bash-lane edges are missing.
|
||||
- Test plan: 7/10 — incident fixtures and the queue replay are the right instruments; missing the no-Bun doctor case, mixed-version kinds, concurrent restamp, Windows classification, and any P7 test.
|
||||
- Right-sized diff: 4/10 — A, D, E are sized; B and C are each two review sittings and the byte budget is unbudgeted.
|
||||
- Reversibility: 8/10 — every new behavior is behind a new kind, a new pin file, or a new flag; `headless` and interactive paths are untouched; guard change (F4) is the one irreversible-feeling edit and it should not ship as written.
|
||||
- Maintainability under ratchets: 5/10 — the plan names the ratchets but misidentifies which files they cap and does not budget the four zero-headroom skills.
|
||||
|
||||
## Tasks
|
||||
|
||||
```json
|
||||
[
|
||||
{"id":"T1","priority":"P1","component":"autoplan-runner","title":"Add next/submit verbs to gstack-autoplan so a host-dispatched native reviewer can return results; run becomes a loop over next only when both voices are local","files":["bin/gstack-autoplan","lib/autoplan-run.ts","lib/headless-artifacts.ts","test/gstack-autoplan-run.test.ts"],"effort_human":"4d","effort_cc":"0.5d","source_finding":"F1"},
|
||||
{"id":"T2","priority":"P1","component":"outside-voice","title":"Create bin/gstack-outside-voice (codex-cli|api|host-subagent) and point the resolver prose at it instead of rendering runner branches","files":["bin/gstack-outside-voice","lib/outside-voice-runner.ts","lib/outside-voice-api.ts","scripts/resolvers/outside-voice.ts","scripts/resolvers/outside-voice-steps.ts","test/gstack-outside-voice.test.ts"],"effort_human":"3d","effort_cc":"0.5d","source_finding":"F2"},
|
||||
{"id":"T3","priority":"P1","component":"adoption","title":"Carry P7 in run.json.consumed_by and the ship receipt; add gstack-ship-receipt census over merged PR bodies; label state-root analytics durable=no","files":["lib/headless-artifacts.ts","bin/gstack-artifact","bin/gstack-ship-receipt","docs/unattended.md","test/gstack-ship-receipt.test.ts"],"effort_human":"2d","effort_cc":"0.25d","source_finding":"F3"},
|
||||
{"id":"T4","priority":"P1","component":"autoplan-guard","title":"Keep deny-on-absent in the Claude render; emit GUARD_NOT_INSTALLED from gstack-skill-start for unattended/non-hook hosts and require it in review-record.md","files":["scripts/resolvers/composition.ts","bin/gstack-skill-start","lib/headless-artifacts.ts","test/autoplan-publication-guard.test.ts","test/gstack-skill-start.test.ts"],"effort_human":"1d","effort_cc":"0.15d","source_finding":"F4"},
|
||||
{"id":"T5","priority":"P1","component":"doctor","title":"Component table as sourced bash (or build-time generated from TS with a freshness test); --check fixture with bun removed from PATH","files":["bin/gstack-doctor","bin/gstack-doctor-components.sh","lib/doctor-components.ts","test/gstack-doctor.test.ts"],"effort_human":"2d","effort_cc":"0.25d","source_finding":"F5"},
|
||||
{"id":"T6","priority":"P1","component":"budgets","title":"Per-PR union-byte delta and CARVE_GUARDS/capture-context-budget refresh in acceptance; B4 checklists replace rather than add; new eval-plan ceiling","files":["test/helpers/carve-guards.ts","test/fixtures/context-budget*.json","docs/designs/multi-agent-wave-2026-10-10/ACCEPTANCE.md"],"effort_human":"1d per PR","effort_cc":"0.1d per PR","source_finding":"F6"},
|
||||
{"id":"T7","priority":"P1","component":"session-kind","title":"Whitelist unattended in gstack-skill-start; loud fallback for unknown kinds; state override-vs-HEADLESS precedence and test both-set","files":["bin/gstack-skill-start","bin/gstack-session-kind","test/gstack-skill-start.test.ts","test/gstack-session-kind.test.ts"],"effort_human":"0.5d","effort_cc":"0.1d","source_finding":"F7"},
|
||||
{"id":"T8","priority":"P2","component":"review-log","title":"Adopt exit table (usage 2, rejected 1 with REVIEW_STATUS_MISMATCH); pass bindReview stderr through the wrapper","files":["bin/gstack-review-log","lib/review-evidence.ts","test/review-log.test.ts"],"effort_human":"0.5d","effort_cc":"0.1d","source_finding":"F8"},
|
||||
{"id":"T9","priority":"P2","component":"reason-codes","title":"lib/result-codes.ts with enumerated artifact/queue/policy codes and anchors; extend troubleshooting-anchors test","files":["lib/result-codes.ts","lib/gate-outcomes.ts","docs/troubleshooting.md","test/troubleshooting-anchors.test.ts"],"effort_human":"0.5d","effort_cc":"0.1d","source_finding":"F9"},
|
||||
{"id":"T10","priority":"P2","component":"sections-manifest","title":"Optional scope field with updated passive-registry note and schema; runner is the only consumer; update qa-lazy-sections test","files":["autoplan/sections/manifest.json","plan-*-review/sections/manifest.json","scripts/resolvers/sections.ts","test/qa-lazy-sections.test.ts"],"effort_human":"0.5d","effort_cc":"0.1d","source_finding":"F10"},
|
||||
{"id":"T11","priority":"P2","component":"installer","title":"Replace bun upgrade --version with setup's checksum-verified BUN_VERSION install recipe; doctor fix line quotes it","files":["bin/gstack-capy-install","bin/gstack-doctor","test/gstack-capy-install.test.ts"],"effort_human":"0.5d","effort_cc":"0.1d","source_finding":"F11"},
|
||||
{"id":"T12","priority":"P2","component":"ship-policy","title":"POLICY_SOURCE_UNAVAILABLE (exit 3) when origin/<base> is unresolvable; never fall back to the working tree; document the command-string-only trust boundary","files":["lib/ship-policy.ts","docs/ship-policy.md","test/ship-policy.test.ts"],"effort_human":"0.5d","effort_cc":"0.1d","source_finding":"F12"},
|
||||
{"id":"T13","priority":"P2","component":"restamp","title":"Record the evidence branch key in gate-ahead.json and read it in the receipt; concurrent-restamp lock on the journal","files":["lib/restamp.ts","lib/tree-receipt.ts","test/gstack-restamp-queue.test.ts"],"effort_human":"1d","effort_cc":"0.15d","source_finding":"F13"},
|
||||
{"id":"T14","priority":"P2","component":"tree-receipt","title":"Per-file-type permitted transforms (root package version keys only in lockfiles) and a dep-equals-old-version negative fixture","files":["lib/tree-receipt.ts","test/gstack-tree-receipt.test.ts","test/fixtures/multi-agent-wave/p0-3-dep-equals-old-version/"],"effort_human":"1d","effort_cc":"0.15d","source_finding":"F14"},
|
||||
{"id":"T15","priority":"P2","component":"pregate","title":"Import graph unions repo-path literals and bin→test naming; 'no lane found' is FAIL; cache graph by HEAD tree","files":["lib/pregate/import-graph.ts","lib/pregate/lanes.ts","test/pregate-lanes.test.ts"],"effort_human":"1.5d","effort_cc":"0.2d","source_finding":"F15"},
|
||||
{"id":"T16","priority":"P3","component":"pregate","title":"Move buildHermeticEnv to lib/hermetic-env.ts and re-export from the test helper","files":["lib/hermetic-env.ts","test/helpers/hermetic-env.ts","bin/gstack-hermetic-run"],"effort_human":"0.5d","effort_cc":"0.1d","source_finding":"F16"},
|
||||
{"id":"T17","priority":"P3","component":"validation","title":"PR B merge gate accepts either outside runner and records it","files":["docs/designs/multi-agent-wave-2026-10-10/ACCEPTANCE.md"],"effort_human":"0.1d","effort_cc":"0.05d","source_finding":"F17"},
|
||||
{"id":"T18","priority":"P2","component":"pin-security","title":"Containment, opt-in and regex-size fixtures for ship-policy mirror/stamp_paths, generated.json, pregate.json guards, banned-terms.json","files":["lib/ship-policy.ts","lib/regen.ts","lib/pregate/index.ts","bin/gstack-banned-terms","test/ship-policy.test.ts","test/gstack-regen.test.ts"],"effort_human":"1d","effort_cc":"0.15d","source_finding":"F18"},
|
||||
{"id":"T19","priority":"P3","component":"windows-lane","title":"Classify every new bash bin and test for the curated Windows subset; add durations entries","files":["scripts/test-free-shards.ts","scripts/free-test-durations.json","scripts/free-test-durations-windows.json"],"effort_human":"0.5d","effort_cc":"0.1d","source_finding":"F19"},
|
||||
{"id":"T20","priority":"P1","component":"delivery","title":"Split B into B-core/B-runner and C into C-stamp/C-pregate (seven PRs, stacked); record as decision E1 with the two-gate cost","files":["docs/designs/multi-agent-wave-2026-10-10/ACCEPTANCE.md"],"effort_human":"0.2d","effort_cc":"0.05d","source_finding":"Scope Challenge"}
|
||||
]
|
||||
```
|
||||
|
||||
**Auto-decided (recommended options, recorded, no questions asked):** UC1(a), D1(a) amended by T20 (seven PRs: A, B-core, B-runner, C-stamp, C-pregate, D, E), D2(a), D3(a), D4(a) with F12's refusal, D5(a), D6(a), D7(a), D8(a), D9(a), D10(a), D11(a) with F14's per-file-type rules. New decision E1 (PR split) recommended (a) as above; (b) keep five PRs and accept two review sittings per PR.
|
||||
|
||||
Recommendation: revise-then-proceed because the decisions are sound and most receipts hold, but PR B cannot meet P2 without a submit/next verb and a runtime outside-voice bin (F1, F2), P7 is unmeasurable as designed (F3), the guard change targets the wrong host (F4), the doctor table is in a language the P1 scenario lacks (F5), and four skills have zero parity headroom for the prose every later PR adds (F6).
|
||||
@@ -0,0 +1,162 @@
|
||||
1. **[Severity: High] Gate reuse still confuses permitted edits with equivalent behavior.** C3 permits version substitutions without establishing that the affected gates are insensitive to those substitutions; existing digest checks explicitly consume VERSION and generated bytes ([test/agents-digest.test.ts:22](/workspace/gstack/test/agents-digest.test.ts:22), [test/agents-digest.test.ts:44](/workspace/gstack/test/agents-digest.test.ts:44)). Requiring an unchanged raw lockfile hash also conflicts with permitting lockfile version-metadata changes, which the writer performs ([bin/gstack-version-bump:486](/workspace/gstack/bin/gstack-version-bump:486)). Define equivalence separately for each reusable gate, retain raw and normalized identities, and rerun stamp-sensitive checks on every final stamped tree. Replace the impossible “changed tracked lockfile with an identical tree” fixture with distinct tests for metadata-only lockfile changes and environment changes under an identical tree.
|
||||
|
||||
2. **[Severity: High] The portable bundle lacks the data and provenance needed to justify reuse.** Working-tree fingerprints can reference unreachable Git objects, including uncommitted content; copying their hashes does not preserve those objects after the VM disappears ([bin/gstack-wtree:24](/workspace/gstack/bin/gstack-wtree:24)). Existing evidence deliberately distinguishes command freshness from trust and warns that existing labels cannot prove all required lanes ran ([bin/gstack-evidence:23](/workspace/gstack/bin/gstack-evidence:23)). Export the comparison material, complete required-lane manifest, executed selections, and producer-bound run metadata; distinguish imported assertions from independently verified evidence. Unknown execution identity or missing comparison objects must prevent reuse.
|
||||
|
||||
3. **[Severity: High] Queue validation remains vulnerable to a check-to-merge race.** C2 rechecks base and order “immediately before merge,” but another coordinator can advance either after that check. Gate-ahead also needs an explicit identity for the complete integration tree, including the ordered predecessor chain, rather than only one predecessor head. Bind allocation and receipts to repository, base SHA, ordered predecessors, PR head, and synthetic merge tree, then require a serialized merge operation with server-enforced head/base conditions or recompute when those conditions cannot be enforced.
|
||||
|
||||
4. **[Severity: High] Journaling cannot make arbitrary release commands reversible.** C2 promises dry-run purity and rollback across `release_tool`, generators, and a mirror, although those commands can modify undeclared files or perform external actions. The existing version writer already exposes partial completion, including successful VERSION writes followed by failed manifest or lockfile writes ([bin/gstack-version-bump:475](/workspace/gstack/bin/gstack-version-bump:475)). Define a release-tool contract: execute in an isolated staging tree, enumerate allowed outputs, record preimages and postimages, and apply only after validation. Rollback must refuse to overwrite subsequent user edits, and mirror updates need an explicit partial-commit recovery state.
|
||||
|
||||
5. **[Severity: High] The unattended runner needs a durable state machine, not just snapshots.** B2’s promise never to repeat completed paid work fails when the provider finishes but the process dies before recording completion; snapshot hashes establish input identity, not whether execution finished ([bin/gstack-autoplan-snapshot.ts:659](/workspace/gstack/bin/gstack-autoplan-snapshot.ts:659)). Specify attempt IDs, exclusive run ownership, atomic manifest transitions, cancellation, and an `execution_unknown` recovery state. Persist dispatch intent before launching and provider results before phase completion; ambiguous attempts must require reconciliation rather than automatic redispatch. Keep acknowledgments separate from immutable completion evidence.
|
||||
|
||||
6. **[Severity: High] Gate answers can finalize a plan that reviewers never evaluated.** B2 finalizes after applying choices and promises never to rerun completed paid phases, but a nonrecommended answer can materially change scope, architecture, and downstream reviews. The current close packet explicitly invalidates itself after implementation or accepted-decision edits ([bin/gstack-autoplan-snapshot.ts:777](/workspace/gstack/bin/gstack-autoplan-snapshot.ts:777)). Require the caller to supply the expected `gate_rev`—the proposed `answer` signature currently has no such input—and atomically reject stale, conflicting, or partially parsed replies. Recompute affected review-input identities and reopen dependent phases when an answer changes reviewed content.
|
||||
|
||||
7. **[Severity: High] Hashing reviewer prose cannot detect omitted findings.** B0 claims that binding an immutable output file prevents omitted findings, but a matching hash proves only which file was retained; it does not prove that `findings.jsonl` contains everything that prose reported. Existing snapshot code explicitly limits its guarantees to explained bytes, not completeness or approval ([bin/gstack-autoplan-snapshot.ts:677](/workspace/gstack/bin/gstack-autoplan-snapshot.ts:677)). Require canonical structured findings from each reviewer and reconcile their exact IDs into the aggregate, preserving disagreements and resolutions separately. Validation must also reject duplicate IDs, dependency cycles, malformed JSONL, and artifact paths escaping the run directory.
|
||||
|
||||
8. **[Severity: High] The runner adapters do not yet define equivalent review capabilities.** B3 treats a direct API response, CLI review, and supplied subagent file as interchangeable without specifying repository access, complete input delivery, terminal execution evidence, or model-family verification. Existing outside-review handling separates transport execution from review verdict, while native dispatch requires successful full-file reads ([lib/outside-review-result.ts:4](/workspace/gstack/lib/outside-review-result.ts:4), [bin/gstack-autoplan-snapshot.ts:661](/workspace/gstack/bin/gstack-autoplan-snapshot.ts:661)). Define one adapter interface covering capabilities, input digest, actual model metadata, cancellation, terminal result, and usage. An API adapter needs either a bounded repository tool loop or an explicitly limited supplied-input review; label coverage accordingly.
|
||||
|
||||
9. **[Severity: High] PR B promises spending controls before their implementation arrives in PR E.** `--spend-cap` and concurrent native/outside work require durable admission control in B, not a later ledger extraction. Current measurement admission operates on in-memory batch accounting, and its pricing source explicitly excludes billing categories ([scripts/ship-measure.ts:409](/workspace/gstack/scripts/ship-measure.ts:409), [test/helpers/pricing.ts:4](/workspace/gstack/test/helpers/pricing.ts:4)). Move the minimal shared ledger into B, atomically check `spent + reserved + next_estimate`, and retain reservations for unknown-charge attempts after crashes. Specify whether deadlines cancel every child and how externally dispatched subagent spending is bounded or reported as unknown.
|
||||
|
||||
10. **[Severity: High] Suppressing prompts does not enforce unattended privacy semantics.** Skill startup already performs analytics work and invokes artifact synchronization outside the spawned-only onboarding guard ([bin/gstack-skill-start:179](/workspace/gstack/bin/gstack-skill-start:179), [bin/gstack-skill-start:310](/workspace/gstack/bin/gstack-skill-start:310)). Review logging also independently enqueues records for synchronization ([bin/gstack-review-log:99](/workspace/gstack/bin/gstack-review-log:99)). Enforce unattended restrictions at the relevant writers and synchronization entrypoints, including inherited enabled settings and pending work from earlier sessions. Test actual subprocess/network-call absence, not merely absence of telemetry prompts.
|
||||
|
||||
11. **[Severity: High] The installer has unresolved bootstrap and false-success paths.** A1 moves component probes into TypeScript even though doctor deliberately works without Bun, and A2’s upgrade command cannot bootstrap a missing Bun executable ([bin/gstack-doctor:22](/workspace/gstack/bin/gstack-doctor:22), [setup:296](/workspace/gstack/setup:296)). Checking only the security floor also allows Bun 1.3.14 when gstack’s supported minimum is 1.4.2 ([bin/gstack-bun-version.sh:15](/workspace/gstack/bin/gstack-bun-version.sh:15)). Preserve a dependency-free diagnostic/bootstrap path, distinguish the security floor from the supported runtime, and define missing-tool installation or refusal explicitly. An empty installed-host set and an unmet `--revision` request must not produce the same success certification as a satisfied installation.
|
||||
|
||||
12. **[Severity: High] CI parsing and import closure cannot establish complete lane coverage.** C8’s import graph misses subprocess targets, runtime file reads, generated inputs, and computed imports; doctor tests themselves exercise the binary through subprocesses and filesystem fixtures ([test/gstack-doctor.test.ts:25](/workspace/gstack/test/gstack-doctor.test.ts:25), [test/gstack-doctor.test.ts:107](/workspace/gstack/test/gstack-doctor.test.ts:107)). Windows selection is computed by a planner, and serial classification belongs to runner code ([.github/workflows/windows-free-tests.yml:62](/workspace/gstack/.github/workflows/windows-free-tests.yml:62), [scripts/test-free-shards.ts:235](/workspace/gstack/scripts/test-free-shards.ts:235)). Define supported workflow constructs and explicit dependency declarations, with conservative selection when mappings are uncertain. Persist remote-lane obligations and require their matching receipts at merge; printing `requires-remote` is insufficient enforcement.
|
||||
|
||||
13. **[Severity: High] The pre-gate lacks a precise late-change invalidation rule.** C8 runs before paid evaluations, but later fixes, stamping, regeneration, and integration can invalidate its findings and executed selections. Existing evidence captures fingerprints before and after execution specifically to reject changing inputs ([bin/gstack-evidence:394](/workspace/gstack/bin/gstack-evidence:394)). Give every pre-gate check an input identity and prerequisite set, then require current results after final generation and before publication. Define how timeout, unavailable tooling, and repository-requested `warn` downgrades affect acceptance; mandatory credential and required-lane checks must not silently become advisory.
|
||||
|
||||
14. **[Severity: High] The ratchet assumptions are inaccurate, and `--add-rationale` weakens the enforced convention.** The size test checks explicitly registered modules and residuals; it does not automatically cap every new library, and the three oversized files cited by the plan are absent from the current residual list ([test/helpers/module-size.ts:202](/workspace/gstack/test/helpers/module-size.ts:202), [test/fixtures/module-size-ratchet.json:51](/workspace/gstack/test/fixtures/module-size-ratchet.json:51)). Its documented exception is for data tables or generated literals, not growing interface logic ([test/helpers/module-size.ts:225](/workspace/gstack/test/helpers/module-size.ts:225)). Register every new owner module and intended residual limit, remove automatic rationale-based exemptions, and split artifact schemas, validation, storage, and orchestration into cohesive modules. Preserve both the 800-line module and 150-line function limits.
|
||||
|
||||
15. **[Severity: High] Artifact and review-log changes require an explicit migration.** B2 calls its task format the existing schema, but current tasks contain `source_finding` and local task IDs, without the proposed dependency, acceptance, and finding-reference fields ([scripts/resolvers/tasks-section.ts:77](/workspace/gstack/scripts/resolvers/tasks-section.ts:77)). B7 also changes findings semantics while freshness still checks `critical` and legacy Codex counters, and binding consumes the start token before completing validation ([lib/review-evidence.ts:188](/workspace/gstack/lib/review-evidence.ts:188), [lib/review-evidence.ts:238](/workspace/gstack/lib/review-evidence.ts:238)). Version the formats, enumerate all readers/writers, preserve conservative legacy behavior, and validate findings before consuming a token. Include resolved critical findings and rejected-write retries in migration tests.
|
||||
|
||||
16. **[Severity: Medium] `--next` bypasses the existing version-source contract.** C2 hard-codes `origin/<base>:VERSION`, although the repository already supports explicit version paths, pinned package manifests, and absent/ambiguous/broken outcomes ([lib/version-source.ts:135](/workspace/gstack/lib/version-source.ts:135)). Read the configured version source from the selected base revision through the shared resolver. Make no-version repositories, monorepos, and three- versus four-component versions explicit supported cases rather than silently inventing a new allocation path.
|
||||
|
||||
17. **[Severity: Medium] The proposed hermetic runner does not provide the isolation it advertises.** `buildHermeticEnv()` retains `HOME`, supports a nonhermetic escape hatch, and accepts overrides after scrubbing ([test/helpers/hermetic-env.ts:49](/workspace/gstack/test/helpers/hermetic-env.ts:49), [test/helpers/hermetic-env.ts:118](/workspace/gstack/test/helpers/hermetic-env.ts:118), [test/helpers/hermetic-env.ts:147](/workspace/gstack/test/helpers/hermetic-env.ts:147)). Wrapping it alone does not remove home dotfiles or Git identity. Extract a production-owned environment helper, allocate an isolated home, disable global/system Git configuration, and distinguish isolation failures from fixtures legitimately requiring a configured identity.
|
||||
|
||||
18. **[Severity: Medium] Checklist reduction and the extra spec pass lack a trustworthy acceptance criterion.** A 200-line cap and scope enum prove structure, not preservation of review obligations; B4 can silently exclude a required methodology section after a false-negative scope decision. B8 also adds a fourth launch after the existing explicit three-launch cap, then checks only previous issues, potentially missing regressions introduced by their fixes ([scripts/resolvers/spec-review.ts:188](/workspace/gstack/scripts/resolvers/spec-review.ts:188)). Maintain an obligation-to-section map with conservative scope fallback and behavioral evaluations for omitted requirements. Distinguish deterministic issue verification from another model review, preregister its budget, and never promote issue-only verification to an unchanged full-review verdict.
|
||||
|
||||
19. **[Severity: Medium] P7 cannot measure adoption from the proposed events alone.** Run and acknowledgment events cannot establish the denominator of the first ten Capy threads, especially threads that never invoke gstack, and ephemeral local analytics disappear unless a parent retains them. The state-root resolver selects a location; it does not establish durability ([lib/state-root.ts:22](/workspace/gstack/lib/state-root.ts:22)). Define an owner-maintained thread cohort, deduplicate consumption by run/thread identity, and let the parent collect consent-compatible receipts durably. Assign a tier to every item—the current omissions make the adoption gate and funded scope ambiguous.
|
||||
|
||||
20. **[Severity: Medium] Automatic issue closure exceeds the available evidence.** C6 turns every issue mentioned in commit messages into `Fixes #N`, including context references, partial fixes, and explicitly deferred asks. E3 also treats CODEOWNERS as a maintainer list, although file ownership and repository maintenance authority are different concepts. Require explicit closure intent plus satisfaction of the linked issue’s remaining obligations, and use explicit maintainer configuration or repository permissions for contributor classification.
|
||||
|
||||
21. **[Severity: Medium] The shared command contract is not yet internally consistent.** B0’s universal terminal-line rule conflicts with commands whose primary output is schemas, parsed JSON, or doctor trailers; B7 assigns both exit 2 and exit 1 to status mismatch. C1 proposes commented examples in `.json` files and omits the queue-mode key that C2 reads, while B0 conflates structural validity with completed execution. Publish the schemas and command/status/exit-code matrix first, including `gate_pending`, partial answers, interrupted runs, and JSON-only errors. Keep wire-contract assertions exact while testing workflow prose behaviorally, as required by [docs/test-value-bar.md:23](/workspace/gstack/docs/test-value-bar.md:23).
|
||||
|
||||
22. **[Severity: Medium] The delivery estimate and validation sequence omit substantial required work.** PR B contains a runner, crash recovery, provider adapters, schema migration, gate parsing, and prompt reduction; PR C contains transactional mutation, queue coordination, evidence portability, and dependency analysis, so neither is supported by the stated small library counts. Validation also omits the test-code type ratchet that CI explicitly runs ([.github/workflows/free-tests.yml:223](/workspace/gstack/.github/workflows/free-tests.yml:223)). Re-estimate B and C by these components, move optional breadth behind explicit tiers, and add build, credential scanning, applicable formatting, and selected quality judges before the final acceptance sequence. Preserve the final frozen-tree free-suite run and report incomplete platform/provider coverage separately.
|
||||
|
||||
Architecture:
|
||||
|
||||
```text
|
||||
Parent / coordinator
|
||||
|
|
||||
+--> gstack-capy-install --> dependency-free bootstrap --> setup
|
||||
| |
|
||||
| v
|
||||
+--> gstack-doctor --> doctor-components + runtime-pins
|
||||
|
|
||||
+--> gstack-autoplan run/status/answer/resume
|
||||
| |
|
||||
| v
|
||||
| autoplan-run [exclusive owner + durable transitions]
|
||||
| +--> snapshot / prompt preparation
|
||||
| +--> runner adapters ----> CLI / API / host-subagent
|
||||
| | |
|
||||
| | +--> shared spend ledger + result classifier
|
||||
| +--> gate-list <---- reply + expected gate_rev
|
||||
| |
|
||||
| v
|
||||
| Parent-selected run directory
|
||||
| immutable inputs + reviewer outputs + attempt records
|
||||
| plan.md / review-record.md
|
||||
| tasks.jsonl / decisions.jsonl / findings.jsonl / timing.json
|
||||
| run.json [atomic manifest]
|
||||
| |
|
||||
| +--> artifact schema registry + strict validator
|
||||
| +--> separate consumption acknowledgments
|
||||
|
|
||||
+--> /ship or explicit --allow-repo-commands
|
||||
|
|
||||
Committed base pin files + policy revision
|
||||
ship-policy.json / generated.json / pregate.json
|
||||
|
|
||||
v
|
||||
policy validation + authorization
|
||||
|
|
||||
+-------+------------------+
|
||||
| |
|
||||
v v
|
||||
restamp + regen pregate registry
|
||||
| |
|
||||
isolated staged tree selection manifests
|
||||
preimages / postimages local + remote lane obligations
|
||||
restamp journal |
|
||||
| v
|
||||
+--> final stamped tree --> gate execution
|
||||
|
|
||||
v
|
||||
portable evidence bundle
|
||||
[content + execution identity]
|
||||
|
|
||||
tree-receipt eligibility
|
||||
|
|
||||
ship-receipt in PR body
|
||||
|
|
||||
serialized conditional merge
|
||||
|
||||
Persistent analytics / learnings:
|
||||
all callers --> state-root.ts / gstack-paths
|
||||
--> explicit durability and session policy
|
||||
```
|
||||
|
||||
Test plan gaps:
|
||||
|
||||
- **Paid completion followed by process death:** add `test/autoplan-run-recovery.test.ts`, killing the runner before and after result persistence and asserting no automatic duplicate charge.
|
||||
- **Concurrent `resume`, `answer`, and `ack`:** add `test/autoplan-run-concurrency.test.ts`, checking exclusive ownership, revision conflicts, and exactly-once acknowledgment accounting.
|
||||
- **Answer changes reviewed architecture:** add `test/autoplan-answer-invalidation.test.ts`, proving only affected phases reopen and stale replies perform no writes.
|
||||
- **Structured output omits a reviewer finding:** add `test/headless-artifact-integrity.test.ts`, including duplicate IDs, cycles, malformed tails, traversal, symlinks, and unknown schema versions.
|
||||
- **Imported evidence survives VM destruction:** add `test/evidence-bundle-portability.test.ts`, verifying from a fresh clone without the original object database or logs.
|
||||
- **Version-only edits affect a gate:** add `test/tree-receipt-stamp-sensitive.test.ts`, including version-dependent generated output and lockfile metadata versus dependency changes.
|
||||
- **Base moves after the final queue check:** extend `test/gstack-restamp-queue.test.ts` with concurrent coordinators and a move between validation and merge.
|
||||
- **Release command modifies undeclared state:** add `test/restamp-command-boundary.test.ts`, including dry-run side effects, rollback conflicts, and mirror partial completion.
|
||||
- **API/host-subagent claims completion without usable coverage:** add `test/outside-runner-contract.test.ts`, exercising missing input, truncation, refusal, unknown family, and missing terminal evidence.
|
||||
- **Unattended inherits enabled synchronization:** extend `test/gstack-skill-start.test.ts` with instrumented egress sinks and pending telemetry work.
|
||||
- **Missing Bun and unmet revision pin:** extend `test/gstack-doctor.test.ts` and add `test/gstack-capy-install.test.ts`, covering zero registered hosts and unsupported bootstrap runtimes.
|
||||
- **Dependencies bypass static imports:** add `test/pregate-selection.test.ts`, covering subprocess targets, computed reads, deleted/renamed files, and unsupported workflow expressions.
|
||||
- **Late generation invalidates preflight:** add `test/pregate-invalidation.test.ts`, proving publication rejects stale checks and uncleared remote obligations.
|
||||
- **Concurrent spending and unknown charges:** add `test/spend-ledger.test.ts`, including a worker dying after dispatch and before settlement.
|
||||
- **Legacy review rows and rejected writes:** extend review-evidence tests with resolved critical findings, old counters, and reusable start tokens after validation rejection.
|
||||
- **Custom version sources:** extend queue tests with package-manifest pins, monorepos, and absent/ambiguous version sources.
|
||||
- **Hermetic mode still sees operator configuration:** add `test/gstack-hermetic-run.test.ts` with a planted home file and global Git identity.
|
||||
- **Reduced checklist misses an applicable obligation:** add behavioral scope-negative controls and mutation cases; a section-count assertion is insufficient.
|
||||
- **Adoption denominator includes nonusers:** add `test/adoption-cohort.test.ts`, covering zero-run threads, duplicate acknowledgments, and ephemeral worker loss.
|
||||
- **Issue references are mistaken for completed work:** add `test/ship-issue-intent.test.ts`, covering references, partial fixes, cross-repository IDs, and deferred asks.
|
||||
|
||||
Failure modes:
|
||||
|
||||
| Failure mode | Detection | Handling | Test |
|
||||
|---|---|---|---|
|
||||
| Provider finished; completion record missing | Dispatched attempt lacks terminal receipt | Mark execution unknown; reconcile | `autoplan-run-recovery` |
|
||||
| Two processes resume one run | Run ownership/revision conflict | Refuse second writer | `autoplan-run-concurrency` |
|
||||
| Reply targets an old gate | Expected revision mismatch | Reject without mutation | `autoplan-answer-invalidation` |
|
||||
| Artifact aggregate drops findings | Reviewer-ID reconciliation | Mark incomplete | `headless-artifact-integrity` |
|
||||
| Receipt references vanished Git objects | Bundle completeness check | Refuse reuse | `evidence-bundle-portability` |
|
||||
| Version transform changes gate behavior | Gate-specific equivalence check | Run affected checks on stamped tree | `tree-receipt-stamp-sensitive` |
|
||||
| Queue changes during merge | Conditional merge rejection | Recompute allocation and integration tree | `gstack-restamp-queue` |
|
||||
| Release hook partially writes or mirror fails | Journal and output comparison | Resume or conflict-aware rollback | `restamp-command-boundary` |
|
||||
| Pre-gate misses a dynamic dependency | Unsupported mapping/dependency declaration | Select conservative coverage | `pregate-selection` |
|
||||
| Required remote lane never runs | Required-lane receipt reconciliation | Block publication | `pregate-invalidation` |
|
||||
| Worker dies with reserved spending | Unsettled attempt record | Retain reservation/unknown charge | `spend-ledger` |
|
||||
| Unattended session inherits sync consent | Session policy at egress entrypoint | Skip synchronization explicitly | `gstack-skill-start` |
|
||||
| Requested installation revision differs | Resolved commit comparison | Report unmet pin without updating | `gstack-capy-install` |
|
||||
| Rollback encounters subsequent user edits | Current bytes differ from postimage | Stop with conflict; preserve edits | `restamp-command-boundary` |
|
||||
|
||||
Scores:
|
||||
|
||||
| Dimension | Score |
|
||||
|---|---:|
|
||||
| Architecture soundness | 5/10 |
|
||||
| Edge-case coverage | 4/10 |
|
||||
| Test plan | 5/10 |
|
||||
| Right-sized diff | 3/10 |
|
||||
| Reversibility | 4/10 |
|
||||
| Maintainability under the repo’s ratchets | 4/10 |
|
||||
|
||||
Recommendation: Revise before approval because gate reuse, unattended recovery, and transactional restamping lack enforceable contracts, while several proposed integrations bypass existing repository guarantees.
|
||||
@@ -0,0 +1,65 @@
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-1", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "One environment treated as a market; adoption unmeasured", "disposition": "accepted", "plan_items": ["UC1", "P7"], "native_counterpart": "1.1", "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-2", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "Critical", "title": "Never re-gate an equal tree is unsound", "disposition": "accepted", "plan_items": ["C3", "D11"], "native_counterpart": "6.3", "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-3", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Machine-readable is not verifiable; needs run manifest + validator", "disposition": "accepted", "plan_items": ["B0"], "native_counterpart": "2.1", "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-4", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Session mode does not solve host compatibility; outside reviewer host-bound", "disposition": "accepted", "plan_items": ["B3"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-5", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Urgent repair hostage to twelve initiatives", "disposition": "accepted", "plan_items": ["A1", "A2", "A3", "D1"], "native_counterpart": "3.1", "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-6", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Queue design manages churn; alternatives unweighed", "disposition": "partially_accepted", "plan_items": ["C2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-7", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Pre-gate can block the gate it needs (Windows lane)", "disposition": "accepted", "plan_items": ["C8"], "native_counterpart": "3.2", "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-8", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Pin files acquire undefined authority", "disposition": "accepted", "plan_items": ["C1"], "native_counterpart": "6.2", "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-9", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "/eval-plan is a second product; ledger not a hard cap", "disposition": "accepted", "plan_items": ["E1"], "native_counterpart": "4.4", "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-10", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "Row compliance is not review quality; spec loop misread", "disposition": "accepted", "plan_items": ["D1", "B8"], "native_counterpart": "3.3", "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-11", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "Install verdict lacks intended-readiness definition", "disposition": "accepted", "plan_items": ["A1", "A2"], "native_counterpart": "2.3", "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-outside-12", "phase": "ceo", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "Contributor rewriting undermines maintenance", "disposition": "accepted", "plan_items": ["E3"], "native_counterpart": "6.5", "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-native-6.1", "phase": "ceo", "voice": "native", "model": "claude-fable-5-1", "severity": "Critical", "title": "headless redefinition changes eval semantics", "disposition": "accepted", "plan_items": ["B1", "D2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-ceo-native-6.4", "phase": "ceo", "voice": "native", "model": "claude-fable-5-1", "severity": "High", "title": "gate-ahead merge pollutes squash PRs", "disposition": "accepted", "plan_items": ["C2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/ceo-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-1", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "Critical", "title": "gate reuse can certify changed generated behavior / lockfile metadata", "disposition": "accepted", "plan_items": ["C3"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-2", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "installer fails before its repair step; no jq check; no revision pin", "disposition": "accepted", "plan_items": ["A2"], "native_counterpart": "F8", "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-3", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "Critical", "title": "readiness contradicts runners", "disposition": "accepted", "plan_items": ["A1"], "native_counterpart": "F2", "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-4", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "pins can repair the wrong environment", "disposition": "accepted", "plan_items": ["A1", "A2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-5", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "Critical", "title": "no unattended invocation or continuation contract", "disposition": "accepted", "plan_items": ["B2"], "native_counterpart": "F1", "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-6", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "task file cannot replace the plan", "disposition": "accepted", "plan_items": ["B0"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-7", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "no common transport", "disposition": "accepted", "plan_items": ["B0"], "native_counterpart": "F7", "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-8", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "state root does not cross the parent-child boundary", "disposition": "accepted", "plan_items": ["A3", "B0", "B2"], "native_counterpart": "F11", "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-9", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "no authoritative version-selection rule under reorder", "disposition": "accepted", "plan_items": ["C2"], "native_counterpart": "F3", "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-10", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "cross-machine reuse before portable evidence", "disposition": "accepted", "plan_items": ["C3"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-11", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "restamp check/apply semantics and recovery", "disposition": "accepted", "plan_items": ["C2", "C4"], "native_counterpart": "F14", "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-12", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "base-only pin files have no first-use path", "disposition": "accepted", "plan_items": ["C1"], "native_counterpart": "F13", "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-13", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "two-minute pre-gate assumes CI info YAML lacks", "disposition": "accepted", "plan_items": ["C8"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-14", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "gate can conceal decisions or misread approval", "disposition": "accepted", "plan_items": ["B9", "E3"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-15", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "findings-derived status collides with freshness reader and wrapper", "disposition": "accepted", "plan_items": ["B7"], "native_counterpart": "F16", "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-outside-16", "phase": "dx", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "tiers do not define merge gates", "disposition": "accepted", "plan_items": ["C2", "Validation 4"], "native_counterpart": "F4", "source": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-native-F5", "phase": "dx", "voice": "native", "model": "claude-fable-5-1", "severity": "Medium", "title": "reason codes with anchors", "disposition": "accepted", "plan_items": ["B0"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-native-F6", "phase": "dx", "voice": "native", "model": "claude-fable-5-1", "severity": "Medium", "title": "ack acknowledgment", "disposition": "accepted", "plan_items": ["B0"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-native-F9", "phase": "dx", "voice": "native", "model": "claude-fable-5-1", "severity": "Medium", "title": "--for over --require", "disposition": "accepted", "plan_items": ["A1"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-native-F10", "phase": "dx", "voice": "native", "model": "claude-fable-5-1", "severity": "Medium", "title": "hook-absent vs hook-failed", "disposition": "accepted", "plan_items": ["B1"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-dx-native-F15", "phase": "dx", "voice": "native", "model": "claude-fable-5-1", "severity": "Medium", "title": "capability sentence corrected", "disposition": "accepted", "plan_items": ["Context"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/dx-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-1", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Gate reuse confuses permitted edits with equivalent behavior", "disposition": "accepted", "plan_items": ["C3"], "native_counterpart": "F14", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-2", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Portable bundle lacks data and provenance for reuse", "disposition": "accepted", "plan_items": ["C3"], "native_counterpart": "F13", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-3", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Queue validation vulnerable to check-to-merge race", "disposition": "accepted", "plan_items": ["C2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-4", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Journaling cannot make arbitrary release commands reversible", "disposition": "accepted", "plan_items": ["C2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-5", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Unattended runner needs a durable state machine", "disposition": "accepted", "plan_items": ["B2"], "native_counterpart": "F1", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-6", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Gate answers can finalize an unreviewed plan", "disposition": "accepted", "plan_items": ["B2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-7", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Hashing prose cannot detect omitted findings", "disposition": "accepted", "plan_items": ["B0"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-8", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Runner adapters lack equivalent review capabilities", "disposition": "accepted", "plan_items": ["B3"], "native_counterpart": "F2", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-9", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Spend controls promised in B, implemented in E", "disposition": "accepted", "plan_items": ["B2", "E1"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-10", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Suppressing prompts does not enforce unattended privacy", "disposition": "accepted", "plan_items": ["B1"], "native_counterpart": "F7", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-11", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Installer bootstrap and false-success paths", "disposition": "accepted", "plan_items": ["A1", "A2"], "native_counterpart": "F5", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-12", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "CI parsing and import closure cannot establish lane coverage", "disposition": "accepted", "plan_items": ["C8"], "native_counterpart": "F15", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-13", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Pre-gate lacks late-change invalidation rule", "disposition": "accepted", "plan_items": ["C8"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-14", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Ratchet assumptions inaccurate; --add-rationale weakens convention", "disposition": "accepted", "plan_items": ["Context", "C8"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-15", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "High", "title": "Artifact and review-log changes need explicit migration", "disposition": "accepted", "plan_items": ["B0", "B7"], "native_counterpart": "F8", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-16", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "--next bypasses the version-source contract", "disposition": "accepted", "plan_items": ["C2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-17", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "Hermetic runner does not isolate", "disposition": "accepted", "plan_items": ["C8"], "native_counterpart": "F16", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-18", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "Checklist reduction and extra spec pass lack acceptance criterion", "disposition": "accepted", "plan_items": ["B4", "B8"], "native_counterpart": "F10", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-19", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "P7 cannot measure adoption from proposed events", "disposition": "accepted", "plan_items": ["P7"], "native_counterpart": "F3", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-20", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "Automatic issue closure exceeds evidence; CODEOWNERS not maintainers", "disposition": "accepted", "plan_items": ["C6", "E3"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-21", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "Shared command contract inconsistent", "disposition": "accepted", "plan_items": ["B0", "B7", "C1"], "native_counterpart": "F8", "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-outside-22", "phase": "eng", "voice": "outside", "model": "gpt-6-astra", "severity": "Medium", "title": "Delivery estimate and validation omit required work", "disposition": "accepted", "plan_items": ["Delivery shape", "Effort", "Validation 5"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-native-F1", "phase": "eng", "voice": "native", "model": "claude-fable-5-1", "severity": "Critical", "title": "Runner has no verb for host-dispatched reviewer", "disposition": "accepted", "plan_items": ["B2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-native-F3", "phase": "eng", "voice": "native", "model": "claude-fable-5-1", "severity": "Critical", "title": "P7 has no durable collection path", "disposition": "accepted", "plan_items": ["P7"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-native-F4", "phase": "eng", "voice": "native", "model": "claude-fable-5-1", "severity": "High", "title": "Guard clause detects wrong condition on wrong host", "disposition": "accepted", "plan_items": ["B1"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-native-F6", "phase": "eng", "voice": "native", "model": "claude-fable-5-1", "severity": "High", "title": "Zero parity headroom on four skills unbudgeted", "disposition": "accepted", "plan_items": ["Context", "Delivery shape"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-native-F12", "phase": "eng", "voice": "native", "model": "claude-fable-5-1", "severity": "Medium", "title": "Shallow-clone policy path missing", "disposition": "accepted", "plan_items": ["C1"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-native-F17", "phase": "eng", "voice": "native", "model": "claude-fable-5-1", "severity": "Low", "title": "Validation 2 tied to Codex CLI", "disposition": "accepted", "plan_items": ["Validation 2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-native-F18", "phase": "eng", "voice": "native", "model": "claude-fable-5-1", "severity": "Low", "title": "Pin-file containment not fixtured", "disposition": "accepted", "plan_items": ["C1"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-native.md"}
|
||||
{"schema_version": 1, "run": "maw-20261010", "id": "maw-20261010-eng-native-F19", "phase": "eng", "voice": "native", "model": "claude-fable-5-1", "severity": "Low", "title": "Windows subset classification missing", "disposition": "accepted", "plan_items": ["B2"], "native_counterpart": null, "source": "docs/designs/multi-agent-wave-2026-10-10/eng-native.md"}
|
||||
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"run": "maw-20261010",
|
||||
"generator": "hand-written by the autoplan run; `gstack-artifact validate` (B0) does not exist yet",
|
||||
"status": "approved",
|
||||
"base": "91bbd9e",
|
||||
"host": "capy",
|
||||
"required_phases": [
|
||||
"ceo",
|
||||
"dx",
|
||||
"eng"
|
||||
],
|
||||
"phases": {
|
||||
"ceo": {
|
||||
"snapshot": "2937ab4e\u2026f6845",
|
||||
"native": {
|
||||
"model": "claude-fable-5-1",
|
||||
"status": "completed"
|
||||
},
|
||||
"outside": {
|
||||
"model": "gpt-6-astra",
|
||||
"provider": "codex",
|
||||
"status": "completed"
|
||||
}
|
||||
},
|
||||
"dx": {
|
||||
"snapshot": "ffe83107\u2026f11753",
|
||||
"native": {
|
||||
"model": "claude-fable-5-1",
|
||||
"status": "completed"
|
||||
},
|
||||
"outside": {
|
||||
"model": "gpt-6-astra",
|
||||
"provider": "codex",
|
||||
"status": "completed"
|
||||
}
|
||||
},
|
||||
"eng": {
|
||||
"snapshot": "d7365d9256d2c956908fce530731115816ab6b477152e78602fc69d51fd14b17",
|
||||
"native": {
|
||||
"model": "claude-fable-5-1",
|
||||
"status": "completed",
|
||||
"read_receipt": "77ee24a4\u20263511f"
|
||||
},
|
||||
"outside": {
|
||||
"model": "gpt-6-astra",
|
||||
"provider": "codex",
|
||||
"status": "completed",
|
||||
"rc": 3,
|
||||
"wall_s": 227
|
||||
}
|
||||
}
|
||||
},
|
||||
"counts": {
|
||||
"decisions": 13,
|
||||
"decisions_pending": 0,
|
||||
"findings": 65,
|
||||
"findings_accepted": 64,
|
||||
"findings_partial": 1,
|
||||
"tasks": 28
|
||||
},
|
||||
"deviations": [
|
||||
"Spec Review Loop not run",
|
||||
"review-log JSONL rows not written (ephemeral state root)",
|
||||
"phase-publication-hook guard not enforced (no hooks on this host)",
|
||||
"artifacts hand-written, not validated by tooling"
|
||||
],
|
||||
"artifacts": {
|
||||
"decisions.jsonl": {
|
||||
"path": "docs/designs/multi-agent-wave-2026-10-10/decisions.jsonl",
|
||||
"sha256": "8c7ed4e1554816cc40861c322d5d69a957036cc52db91c1f1d1e9181e39a11e8"
|
||||
},
|
||||
"findings.jsonl": {
|
||||
"path": "docs/designs/multi-agent-wave-2026-10-10/findings.jsonl",
|
||||
"sha256": "9da0491bd136a5378877a596256179068c934a07b6eedcfd43d8a14ee9b0823b"
|
||||
},
|
||||
"tasks.jsonl": {
|
||||
"path": "docs/designs/multi-agent-wave-2026-10-10/tasks.jsonl",
|
||||
"sha256": "01d5e708ffe98d638cdb40a4b8dc65bba279ee43cc9ce90677f631b90448de4d"
|
||||
},
|
||||
"timing.json": {
|
||||
"path": "docs/designs/multi-agent-wave-2026-10-10/timing.json",
|
||||
"sha256": "c8427164fd266959dbb3ccab3a7b52213d110a27baa0b9a55a764178c33187bc"
|
||||
},
|
||||
"ceo-native.md": {
|
||||
"path": "docs/designs/multi-agent-wave-2026-10-10/ceo-native.md",
|
||||
"sha256": "66311c4da064b0286314f98129671ad8fc9201f37bfc96bd2a9d9662c9121c8a"
|
||||
},
|
||||
"ceo-outside.md": {
|
||||
"path": "docs/designs/multi-agent-wave-2026-10-10/ceo-outside.md",
|
||||
"sha256": "f62fec9e324dc5af02b6d2b3eb5759530ff0c35b711f392b11d33e3afd353b49"
|
||||
},
|
||||
"dx-native.md": {
|
||||
"path": "docs/designs/multi-agent-wave-2026-10-10/dx-native.md",
|
||||
"sha256": "369f85927d3f8efbb1ae26dfcfbb8020ece20a5cd236dd1112ec4d83f69f760a"
|
||||
},
|
||||
"dx-outside.md": {
|
||||
"path": "docs/designs/multi-agent-wave-2026-10-10/dx-outside.md",
|
||||
"sha256": "52c7965134a421db205a63f287abe65c3f3c98c58c1bfe11d4ecee7dd2508068"
|
||||
},
|
||||
"eng-native.md": {
|
||||
"path": "docs/designs/multi-agent-wave-2026-10-10/eng-native.md",
|
||||
"sha256": "c0bd9a0cf33baeda2cc60d1f105c26944e7a9f6cd6e125b9b680a4ffb352ab77"
|
||||
},
|
||||
"eng-outside.md": {
|
||||
"path": "docs/designs/multi-agent-wave-2026-10-10/eng-outside.md",
|
||||
"sha256": "ab84692aedfe5c2c5e7b82958715e8d84bf593805595f621c48726d350cb9631"
|
||||
},
|
||||
"plan.md": {
|
||||
"path": "docs/designs/MULTI_AGENT_WAVE_2026_10_10.md",
|
||||
"sha256": "225b0e74b1cf8442dbcd4b93cfc868d0428fe8ac1d53e37b394416f7ad527fd4"
|
||||
}
|
||||
},
|
||||
"gate_rev": 1,
|
||||
"gate": {
|
||||
"reply": "all",
|
||||
"answered_at": "2026-10-10T21:30Z",
|
||||
"gate_rev": 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-a1", "pr": "A", "item": "A1", "title": "`gstack-doctor --check`: the one-line verdict", "tier": 1, "depends_on": [], "findings": ["maw-20261010-ceo-outside-5", "maw-20261010-ceo-outside-11", "maw-20261010-dx-outside-3", "maw-20261010-dx-outside-4", "maw-20261010-dx-native-F9", "maw-20261010-eng-outside-11"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item A1 and Validation", "status": "ready"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-a2", "pr": "A", "item": "A2", "title": "The Capy installer lives in the repo", "tier": 1, "depends_on": [], "findings": ["maw-20261010-ceo-outside-5", "maw-20261010-ceo-outside-11", "maw-20261010-dx-outside-2", "maw-20261010-dx-outside-4", "maw-20261010-eng-outside-11"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item A2 and Validation", "status": "ready"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-a3", "pr": "A", "item": "A3", "title": "Durable state on ephemeral machines", "tier": 1, "depends_on": [], "findings": ["maw-20261010-ceo-outside-5", "maw-20261010-dx-outside-8"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item A3 and Validation", "status": "ready"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b0", "pr": "B-core", "item": "B0", "title": "One versioned artifact contract", "tier": 1, "depends_on": ["A"], "findings": ["maw-20261010-ceo-outside-3", "maw-20261010-dx-outside-6", "maw-20261010-dx-outside-7", "maw-20261010-dx-outside-8", "maw-20261010-dx-native-F5", "maw-20261010-dx-native-F6", "maw-20261010-eng-outside-7", "maw-20261010-eng-outside-15", "maw-20261010-eng-outside-21"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B0 and Validation", "status": "ready"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b1", "pr": "B-core", "item": "B1", "title": "A real unattended session kind", "tier": 1, "depends_on": ["A"], "findings": ["maw-20261010-ceo-native-6.1", "maw-20261010-dx-native-F10", "maw-20261010-eng-outside-10", "maw-20261010-eng-native-F4"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B1 and Validation", "status": "ready"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b7", "pr": "B-core", "item": "B7", "title": "Review-log status derived from findings", "tier": 1, "depends_on": ["A"], "findings": ["maw-20261010-dx-outside-15", "maw-20261010-eng-outside-15", "maw-20261010-eng-outside-21"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B7 and Validation", "status": "ready"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b9", "pr": "B-core", "item": "B9", "title": "One gate list instead of four-option sequences", "tier": 1, "depends_on": ["A"], "findings": ["maw-20261010-dx-outside-14"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B9 and Validation", "status": "ready"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b10", "pr": "B-core", "item": "B10", "title": "Wall time and a pre-run estimate", "tier": 1, "depends_on": ["A"], "findings": [], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B10 and Validation", "status": "ready"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b12", "pr": "B-core", "item": "B12", "title": "URGENT, outside this plan", "tier": 1, "depends_on": ["A"], "findings": [], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B12 and Validation", "status": "ready"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b2", "pr": "B-runner", "item": "B2", "title": "autoplan unattended profile and the output files", "tier": 1, "depends_on": ["B-core"], "findings": ["maw-20261010-dx-outside-5", "maw-20261010-dx-outside-8", "maw-20261010-eng-outside-5", "maw-20261010-eng-outside-6", "maw-20261010-eng-outside-9", "maw-20261010-eng-native-F1", "maw-20261010-eng-native-F19"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B2 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b3", "pr": "B-runner", "item": "B3", "title": "A pluggable outside voice, with a model parameter", "tier": 1, "depends_on": ["B-core"], "findings": ["maw-20261010-ceo-outside-4", "maw-20261010-eng-outside-8"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B3 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b4", "pr": "B-runner", "item": "B4", "title": "Condensed operative checklists", "tier": 1, "depends_on": ["B-core"], "findings": ["maw-20261010-eng-outside-18"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B4 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b8", "pr": "B-runner", "item": "B8", "title": "Spec-review loop at the cap", "tier": 1, "depends_on": ["B-core"], "findings": ["maw-20261010-ceo-outside-10", "maw-20261010-eng-outside-18"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B8 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-b11", "pr": "B-runner", "item": "B11", "title": "Reconciliation when outside passes run beside native ones", "tier": 1, "depends_on": ["B-core"], "findings": [], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item B11 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-c1", "pr": "C-stamp", "item": "C1", "title": "A repo ship policy, machine-readable", "tier": 1, "depends_on": ["B-core"], "findings": ["maw-20261010-ceo-outside-8", "maw-20261010-dx-outside-12", "maw-20261010-eng-outside-21", "maw-20261010-eng-native-F12", "maw-20261010-eng-native-F18"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item C1 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-c2", "pr": "C-stamp", "item": "C2", "title": "`gstack-restamp`: the whole stamp in one step", "tier": 1, "depends_on": ["B-core"], "findings": ["maw-20261010-ceo-outside-6", "maw-20261010-ceo-native-6.4", "maw-20261010-dx-outside-9", "maw-20261010-dx-outside-11", "maw-20261010-dx-outside-16", "maw-20261010-eng-outside-3", "maw-20261010-eng-outside-4", "maw-20261010-eng-outside-16"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item C2 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-c3", "pr": "C-stamp", "item": "C3", "title": "The tree-equality receipt", "tier": 1, "depends_on": ["B-core"], "findings": ["maw-20261010-ceo-outside-2", "maw-20261010-dx-outside-1", "maw-20261010-dx-outside-10", "maw-20261010-eng-outside-1", "maw-20261010-eng-outside-2"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item C3 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-c5", "pr": "C-stamp", "item": "C5", "title": "The handoff record, identifiers first", "tier": 1, "depends_on": ["B-core"], "findings": [], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item C5 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-c7", "pr": "C-stamp", "item": "C7", "title": "History policy and preregistration ancestry", "tier": 1, "depends_on": ["B-core"], "findings": [], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item C7 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-c4", "pr": "C-pregate", "item": "C4", "title": "Generated files regenerate, never hand-merge", "tier": 1, "depends_on": ["C-stamp"], "findings": ["maw-20261010-dx-outside-11"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item C4 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-c6", "pr": "C-pregate", "item": "C6", "title": "Issue links and flake issues", "tier": 1, "depends_on": ["C-stamp"], "findings": ["maw-20261010-eng-outside-20"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item C6 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-c8", "pr": "C-pregate", "item": "C8", "title": "`gstack-pregate`: cheap checks before anything paid", "tier": 1, "depends_on": ["C-stamp"], "findings": ["maw-20261010-ceo-outside-7", "maw-20261010-dx-outside-13", "maw-20261010-eng-outside-12", "maw-20261010-eng-outside-13", "maw-20261010-eng-outside-14", "maw-20261010-eng-outside-17"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item C8 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-d1", "pr": "D", "item": "D1", "title": "Reality rows in plan reviews", "tier": 1, "depends_on": ["B-runner"], "findings": ["maw-20261010-ceo-outside-5", "maw-20261010-ceo-outside-10"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item D1 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-d2", "pr": "D", "item": "D2", "title": "Review and cso look sideways", "tier": 1, "depends_on": ["B-runner"], "findings": ["maw-20261010-ceo-native-6.1"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item D2 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-d3", "pr": "D", "item": "D3", "title": "Investigate recipes and backlog mode", "tier": 1, "depends_on": ["B-runner"], "findings": [], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item D3 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-e1", "pr": "E", "item": "E1", "title": "`/eval-plan`: preregistered, paid evals for the user's product", "tier": 1, "depends_on": ["B-runner", "C-stamp"], "findings": ["maw-20261010-ceo-outside-9", "maw-20261010-eng-outside-9"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item E1 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-e2", "pr": "E", "item": "E2", "title": "Coordination tools", "tier": 1, "depends_on": ["B-runner", "C-stamp"], "findings": [], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item E2 and Validation", "status": "blocked_on_dependency"}
|
||||
{"schema_version": 2, "run": "maw-20261010", "id": "maw-20261010-e3", "pr": "E", "item": "E3", "title": "What the owner sees", "tier": 1, "depends_on": ["B-runner", "C-stamp"], "findings": ["maw-20261010-ceo-outside-12", "maw-20261010-dx-outside-14", "maw-20261010-eng-outside-20"], "blocked_by": ["maw-20261010-uc1", "maw-20261010-d1", "maw-20261010-d12"], "acceptance": "see docs/designs/MULTI_AGENT_WAVE_2026_10_10.md item E3 and Validation", "status": "blocked_on_dependency"}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"run": "maw-20261010",
|
||||
"started_at": "2026-10-10T20:30:29Z",
|
||||
"phases": [
|
||||
{
|
||||
"phase": "ceo",
|
||||
"start_utc": "20:30:29",
|
||||
"end_utc": "20:44:52",
|
||||
"wall_s": 863,
|
||||
"outside_s": 169,
|
||||
"native_s": 384,
|
||||
"amend_s": 480
|
||||
},
|
||||
{
|
||||
"phase": "dx",
|
||||
"start_utc": "20:44:52",
|
||||
"end_utc": "20:54:02",
|
||||
"wall_s": 550,
|
||||
"outside_s": 173,
|
||||
"native_s": 367,
|
||||
"amend_s": 183
|
||||
},
|
||||
{
|
||||
"phase": "eng",
|
||||
"start_utc": "20:54:02",
|
||||
"end_utc": "21:18:00",
|
||||
"wall_s": 1438,
|
||||
"outside_s": 227,
|
||||
"native_s": 682,
|
||||
"amend_s": 720
|
||||
},
|
||||
{
|
||||
"phase": "close",
|
||||
"start_utc": "21:18:00",
|
||||
"note": "decisions/findings/tasks/timing written by hand; B0 tooling does not exist yet"
|
||||
}
|
||||
],
|
||||
"total_wall_s": 2828,
|
||||
"outside_model": "gpt-6-astra",
|
||||
"native_model": "claude-fable-5-1",
|
||||
"host": "capy",
|
||||
"session_kind": "unattended (in practice)",
|
||||
"spend_usd": "unknown (Codex CLI three runs; subagent spend not metered)",
|
||||
"source": "artifact mtimes under ~/.capy/work/gstack-plan"
|
||||
}
|
||||
Reference in new issue
Block a user