From 9d9d4c31bb87b1215ca4ccba911318de8e04599b Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Mon, 2 Nov 2026 06:49:46 -0800 Subject: [PATCH] v1.93.0.0 wave(browse): #3700 #3773 #3779 #3817 #3842 (#3858) * feat(loadouts): Opus 5.5 lite loadout with explicit model detection (GSTA-161) modules/loadouts.json gains the lite loadout (eleven coaching preamble pieces dropped; upgrade-check and brain-sync kept) and a models map that sends only claude-opus-5-5 (and its dated snapshots) to it. Detection is explicit: Capy passes --model to the installer, Claude Code opts in with ./setup --loadout lite; anything unmapped, invalid or unreadable serves full. The checkout always stays the full render. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(loadouts): Opus 5.5 lite, detection order, Capy SKILL_ROOT, setup --loadout; troubleshooting anchors; changelog fragment (GSTA-161) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(loadouts): claude_loadout read through CONFIG_BIN; capy skill context ceiling for the --model line (GSTA-161) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (prune keeps a reused loadout folder, GSTACK_LOADOUT_SKILL_ROOT, claude_loadout guard test) (GSTA-161) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (Capy skill passes the model as GSTACK_MODEL so an older installer ignores it) (GSTA-161) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (prune only owned, unused folders; full root from the canonical Claude install; fallback reason and source) (GSTA-161) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: loadout source example, install and resolve commands, Capy launch-file note (GSTA-161) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(autoplan): --ceo-only runs the CEO review alone with zero questions (GST-537) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: classify autoplan-ceo-only in the Codex host metadata Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: select autoplan-ceo-only on the skills it runs; say it needs Claude Code Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: run autoplan-ceo-only in the marathon lane Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: changelog entry for /autoplan --ceo-only Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: count the autoplan-ceo-only paid file in the shard inventory Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat: gstack-autoplan next --ceo-only runs the CEO review alone and ends reviewed (GST-537) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: budget and close recover a budget-stopped CEO-only run; reviewed is at rest for checkpoints Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: CEO-only finish leaves owner-only cap items undecided, refuses marker phase flags and a plan at the report path, and replaces ceo-review.md atomically Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: the CEO-only report-path guard compares real paths and refuses at the finish when the report file is the plan Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CEO_ONLY_CONFLICT covers a plan linked to the report path Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(browse): failing-first device and touch tests (GSTA-227 F3, #3058) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: the report-path guard keeps the lexical check, skips safe hard links and compares bigint file ids Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * browse: per-session tabs for parallel agents; tab pins stop moving the shared active tab (#1961) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * browse: keep the server.ts tab-pin anchor comment for source guards Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(browse): per-tab phone emulation and touch input ($B device, tap, swipe, pinch, touch; #3058, GSTA-227 F3) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: copy lib/result-codes-autoplan.ts into the fixtures that copy the result-code tables, and keep it out of derived closures Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(qa, aside): touch pointer in /qa, Aside touch recipe verified on Aside 1.26, report Phones row (GSTA-227 F3, #3058) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * browse: drop the sessions paragraph from /browse to stay inside the parity cap Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * changelog: F3 phone emulation and touch entry Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * browse: hide the ps window in the session harness walk Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: the decisions.jsonl status list names auto_kept Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(browse): session cap, LRU eviction, headed release and scoped auto-session rows Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * browse: sessions keep tab picks, sidebar claude stays on the user's tab, pinned rebuild and state load rebind the scope Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (touch cancel with no finger held is a no-op) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: Step 11 review fixes (headed tab adoption, pinned viewport reset, CLI retry keeps --session, rebuild id remap, --json notice, evicted-session reset, device kept after annotated captures, render opts out of sessions, session: client ids reserved) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: headed viewport auto keeps the user's active tab; render's closetab opts out of sessions Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: sessions, phone emulation and touch in BROWSER.md; session: client ids reserved; TODOS #1961/#3058 shipped Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: copy lib/result-codes-browse.ts into the fixtures that copy the result-code modules lib/result-codes.ts imports ./result-codes-browse, so fixtures with an explicit lib list failed with 'Cannot find module'. pr-dependencies lists it with the other reason-code modules as selection data. GSTA-227 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat: gstack-clean reclaims per-repo .gstack scratch and logs, dry run by default (#2627) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * browse: name the Aside touch recipe's coordinates / with a numeric grammar The generated-bash lint (INV-3) rejected the free-text / placeholders. GSTA-227 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(clean): leave hard-linked and unreadable logs alone; cut through a no-follow descriptor checked against the evaluated inode Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(clean): tests that expect tmp removal see a quiet ps, so a codex elsewhere on the host cannot flake them Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(clean): an unlistable tmp/ skips its group; liveness asked once per group on apply; BROWSER.md says which logs reset at daemon start Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(clean): write output synchronously so --json reaches a pipe whole; test fixes for root, wrapper git and the ps stub Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(clean): every change rechecks the verified folder and the measured file; full writes; EPIPE ends output quietly Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(clean): the early-close test checks the bin's own exit status Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: list gstack-clean among the standalone CLIs Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(clean): copied-install fixtures and the dependency list carry lib/result-codes-clean.ts The remote pregate failed 11 cases in gstack-doctor, plan-tune-cathedral and timeline-stop-hook-slug: each copies an explicit list of lib/result-codes-*.ts files into a temp install, and lib/result-codes.ts now imports ./result-codes-clean. Same lists GSTA-168 extended for result-codes-eval. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore: regenerate gstack-doctor-components.sh for the gstack-clean result codes Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * qa: fold the touch pointer into the Phases sentence to fit the parity size cap after the main merge qa's union was 95988 bytes (ratio 1.142 > maxSizeRatio 1.142, train 75). Now 95975 (1.1419). GSTA-227 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: #3058 troubleshooting 'Fixed in' lines name v1.92.10.0 after the main merge GSTA-227 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(clean): classify gstack-tasks rows files (tasks-*) as kept, after main added the producer Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: #3058 troubleshooting 'Fixed in' lines name v1.92.20.0 after the main merge GSTA-227 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (restore the lib/result-codes-plan.ts ratchet row the base merge dropped) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(clean): the producer scan ends a name at a colon, so prose after a path names the file (train 78) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: #3058 troubleshooting 'Fixed in' lines name v1.92.30.0 after the main merge GSTA-227 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * outside-voice-api: caller receipts, stop_reason, OpenAI max_completion_tokens A caller can write its own egress receipt (sink, consent, payload class); the default stays the outside-voice receipt, so existing callers send and record the same bytes. The result carries the provider's stop reason, and an OpenAI request gets max_completion_tokens only when the caller sets an output limit (GST-537 PR 3). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * autoplan gate: item classes and the owner's answer provenance Gate items carry item_class, ruleKept() names the keep-direction pick, and answered decision rows record owner_mode, owner_named, rec_hash and answered_role parsed from the owner's own words (GST-537 PR 3). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * autoplan: shadow-mode decider module, frozen prompt and spend accounting The switch (off by default, fixed at init), the lease, one model call per gate with a page retry and a truncation retry under one deadline, the once-per-revision record with its journaled sha256, the 10% audit mark, and shadow-labeled ledger rows that count against the cap, show as shadow= on GATE_SPEND and stay out of the per-voice price (GST-537 PR 3). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * gstack-autoplan: shadow every published gate, the shadow export, config and docs Every driver command that prints a gate records the shadow first and checkpoints after it; answer waits for a live lease and records a dead one retrospectively; gstack-autoplan shadow exports answered revisions for taste-replay R2 (SHADOW_BEFORE_ANSWER otherwise); gstack-config autoplan_shadow / autoplan_shadow_model; gstack-artifact validate checks the record's hash (GST-537 PR 3). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * changelog: shadow mode for the CEO-only decider (GST-537 PR 3) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * autoplan shadow: /ship review fixes - register test/fixtures/shadow-fake-model.ts in FREE_FIXTURES (free-fixtures inventory failed) - withRunLock: count dead-owner reclaims apart from waitMs iterations, so a long wait can still reclaim - buildPage: fill the decider template in one pass, so slot names inside the plan stay plan text - revise regate: the shared publish step prints the GATE and ESCALATE lines once Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * autoplan shadow: reply files never follow a link; the retry reserves against the current cap - a model reply is written by temp file and rename, so a link at the reply path is replaced, not written through - each shadow reservation reads the run's current spend cap, so a budget change during the call is not undone Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * outside-voice-api: write the egress receipt under the caller's env callModel resolved the receipt's state root from process.env even when the caller passed env, so the shadow tests wrote to the real ~/.gstack (the free-suite home-containment guard caught it). The receipt now uses req.env; the shadow tests give the fake model a private HOME, GSTACK_HOME and GSTACK_STATE_ROOT, and the receipt test no longer sets process.env to pass. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(autoplan): CEO-only runs leave main's open Critical/High finding items to the owner (no decision row) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(autoplan): shadow pages carry main's finding items; a finding item is never ceo_eligible After merging #3767 (main 90a170c), open Critical/High findings are gate items (item_class finding). The shadow records one row per gate item, so its pages now include them. A CEO-only run leaves those items to the owner, so their rows are ceo_eligible false (rule_pick was already null). The shadow fixtures follow main's gate (items, counts, "Do it" tokens); the two merge adapters follow main's doNext(out, rest, price) and its per-voice reservation (--estimate-usd). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: #3058 troubleshooting 'Fixed in' lines name v1.92.61.0 after the main merge GSTA-227 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(investigate): drop the five-phase step procedure (GST-938) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * changelog: GST-938 fragment wording Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(investigate): keep the web-search sanitize guard; update BROWSER.md and docs/skills.md (review pass 1) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * changelog: GST-938 wording Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(investigate): pin the web-search guard; changelog size (adversarial review) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(clean): classify restamp-staging.json, the restamp producer main gained after #3779's base (wave browse) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (wave browse): scoped newtab keeps a concurrent tab switch; shadow validate catches a deleted record; a failed shadow publish is journaled Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: a failed shadow publish journals under the run lock and never throws (wave browse review pass 2) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(unattended): shadow publish failures and the missing-record validate check (wave browse docs audit) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * changelog: wave browse integrator fixes fragment Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: shadow reader dedupes by run folder, so two runs started in the same second both count (wave browse CI free-suite 16) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore: regenerate doctor result anchors in generator order (wave browse rebuild without #3818) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * changelog: name the wave fragment after the rebuilt wave 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> --- BROWSER.md | 47 +- CHANGELOG.md | 51 ++ CONTRIBUTING.md | 1 + TODOS.md | 9 +- VERSION | 2 +- agents-digest/gstack-AGENTS.md | 2 +- autoplan/shadow-decider.md | 76 +++ bin/gstack-artifact | 5 +- bin/gstack-autoplan | 173 ++++-- bin/gstack-capy-install | 45 +- bin/gstack-clean | 12 + bin/gstack-config | 37 +- bin/gstack-doctor-components.sh | 14 +- bin/gstack-render-claude.sh | 31 +- bin/gstack-skill-start | 33 +- browse/SKILL.md | 11 + browse/sections/command-list.md | 5 + browse/src/browse-client.ts | 6 +- browse/src/browser-manager.ts | 133 ++++- browse/src/cli.ts | 15 +- browse/src/commands.ts | 9 +- browse/src/device-emulation.ts | 225 ++++++++ browse/src/meta-commands.ts | 17 +- browse/src/routes/commands.ts | 2 +- browse/src/server.ts | 36 +- browse/src/session-name.ts | 98 ++++ browse/src/session-tabs.ts | 162 ++++++ browse/src/snapshot.ts | 3 + browse/src/tab-session.ts | 46 ++ browse/src/terminal-agent.ts | 1 + browse/src/token-registry.ts | 4 + browse/src/touch-commands.ts | 185 ++++++ browse/src/write-commands.ts | 6 + browse/test/fixtures/touch.html | 64 +++ browse/test/session-name.test.ts | 127 ++++ browse/test/session-tabs.test.ts | 414 ++++++++++++++ browse/test/token-registry.test.ts | 5 + browse/test/touch-commands.test.ts | 257 +++++++++ .../_lib/browse-client.ts | 6 +- devex-review/SKILL.md | 11 + docs/PROJECT_STRUCTURE.md | 2 +- docs/REMOTE_BROWSER_ACCESS.md | 2 +- docs/capy-install.md | 7 + docs/loadouts.md | 53 +- docs/skills.md | 2 +- docs/standalone-binaries.md | 1 + docs/troubleshooting.md | 126 +++- docs/unattended.md | 91 ++- gstack/llms.txt | 5 + investigate/SKILL.md | 181 +----- investigate/SKILL.md.tmpl | 136 +---- investigate/sections/manifest.json | 4 +- lib/aside-render.ts | 9 +- lib/autoplan-budget.ts | 7 +- lib/autoplan-ceo-only.ts | 5 +- lib/autoplan-gate-cost.ts | 15 +- lib/autoplan-gate.ts | 41 +- lib/autoplan-revise.ts | 6 +- lib/autoplan-run.ts | 20 +- lib/autoplan-shadow-reader.ts | 128 +++++ lib/autoplan-shadow.ts | 448 +++++++++++++++ lib/autoplan-state.ts | 2 + lib/gate-list.ts | 17 + lib/headless-artifacts.ts | 3 + lib/model-detect.ts | 56 ++ lib/outside-voice-api.ts | 24 +- lib/repo-state-clean.ts | 429 ++++++++++++++ lib/result-codes-autoplan.ts | 8 +- lib/result-codes-browse.ts | 54 ++ lib/result-codes-clean.ts | 30 + lib/result-codes.ts | 10 +- modules/loadouts.json | 20 + modules/loadouts.schema.json | 11 + package.json | 2 +- qa/SKILL.md | 4 +- qa/SKILL.md.tmpl | 4 +- qa/templates/qa-report-template.md | 1 + scripts/modules-cli.ts | 52 +- scripts/modules-install.ts | 214 +++++++ scripts/modules-render.ts | 5 + scripts/modules.ts | 21 + scripts/pr-dependencies.ts | 2 +- scripts/resolvers/aside.ts | 11 + .../preamble/generate-preamble-bash.ts | 19 +- scripts/resolvers/types.ts | 4 + setup | 60 +- test/aside-driver.test.ts | 5 +- test/autoplan-shadow-driver.test.ts | 426 ++++++++++++++ test/autoplan-shadow.test.ts | 333 +++++++++++ test/fixtures/module-size-ratchet.json | 13 +- test/fixtures/shadow-fake-model.ts | 59 ++ test/gstack-capy-install.test.ts | 45 +- test/gstack-clean-producers.test.ts | 115 ++++ test/gstack-clean.test.ts | 540 ++++++++++++++++++ test/helpers/carve-guards.ts | 11 +- test/helpers/doctor-fixture.ts | 2 +- test/helpers/free-fixtures-data.ts | 1 + test/helpers/placeholder-allowlist.ts | 2 + test/investigate-recipes.test.ts | 30 +- test/learnings-callers-unavailable.test.ts | 2 +- test/loadout-setup.test.ts | 66 +++ test/model-detect.test.ts | 88 +++ test/module-loadouts-install.test.ts | 151 +++++ test/module-loadouts.test.ts | 46 +- test/plan-tune-cathedral.test.ts | 2 +- test/skill-validation.test.ts | 2 +- test/timeline-stop-hook-slug.test.ts | 2 + 107 files changed, 6131 insertions(+), 548 deletions(-) create mode 100644 autoplan/shadow-decider.md create mode 100755 bin/gstack-clean create mode 100644 browse/src/device-emulation.ts create mode 100644 browse/src/session-name.ts create mode 100644 browse/src/session-tabs.ts create mode 100644 browse/src/touch-commands.ts create mode 100644 browse/test/fixtures/touch.html create mode 100644 browse/test/session-name.test.ts create mode 100644 browse/test/session-tabs.test.ts create mode 100644 browse/test/touch-commands.test.ts create mode 100644 lib/autoplan-shadow-reader.ts create mode 100644 lib/autoplan-shadow.ts create mode 100644 lib/model-detect.ts create mode 100644 lib/repo-state-clean.ts create mode 100644 lib/result-codes-browse.ts create mode 100644 lib/result-codes-clean.ts create mode 100644 scripts/modules-install.ts create mode 100644 test/autoplan-shadow-driver.test.ts create mode 100644 test/autoplan-shadow.test.ts create mode 100644 test/fixtures/shadow-fake-model.ts create mode 100644 test/gstack-clean-producers.test.ts create mode 100644 test/gstack-clean.test.ts create mode 100644 test/loadout-setup.test.ts create mode 100644 test/model-detect.test.ts create mode 100644 test/module-loadouts-install.test.ts diff --git a/BROWSER.md b/BROWSER.md index 13aa70909..69be0410d 100644 --- a/BROWSER.md +++ b/BROWSER.md @@ -99,7 +99,7 @@ through CDP before `goto`, as the cookbook does), and no `file://` navigation. | `/scrape` | Prototype the extraction with `aside repl`, hand back the table, list, or prices as structured data. Read-only. | | `/devex-review` | Walk the real onboarding flow and time it, carrying the cookbook inline. | | `/ship`, `/spec`, `/land-and-deploy`, `/setup-deploy`, `/office-hours` | Third-party web actions (vendor dashboards, API keys, webhooks) offered as an Aside drive across your real sessions, with the one-question consent gate for anything mutating. | -| `/plan-ceo-review`, `/plan-eng-review`, `/plan-devex-review`, `/design-consultation`, `/review`, `/investigate`, `/cso`, `/office-hours` | Web research runs through `aside exec` in your real browser (`{{ASIDE_RESEARCH}}`), one read-only request per question, answers treated as untrusted content. No Aside → the same queries go to the host's WebSearch tool; no WebSearch either → "Search unavailable" once and the skill proceeds on in-distribution knowledge. | +| `/plan-ceo-review`, `/plan-eng-review`, `/plan-devex-review`, `/design-consultation`, `/review`, `/cso`, `/office-hours` | Web research runs through `aside exec` in your real browser (`{{ASIDE_RESEARCH}}`), one read-only request per question, answers treated as untrusted content. No Aside → the same queries go to the host's WebSearch tool; no WebSearch either → "Search unavailable" once and the skill proceeds on in-distribution knowledge. | ### Local-HTML rendering @@ -619,7 +619,7 @@ Storage stays intact by default. With explicit approval on a Chromium target, `- | Command | Description | |---------|-------------| -| `tabs` | List open tabs | +| `tabs` | List open tabs, with each owned tab's owner (a pair-agent client or `session:`) | | `tab ` | Switch to tab | | `newtab [url] [--json]` | Open new tab; `--json` returns `{tabId, url}` for programmatic use | | `closetab [id]` | Close tab | @@ -634,6 +634,27 @@ in a long script, `status`, `tabs`, `tab `, `newtab`, `closetab`, not responding. A command that needs the stuck tab times out after 30s and names it, so you can switch away or close it without `--force-restart`. +**Sessions keep parallel agents on their own tabs (#1961).** A call from a +known agent harness (Claude Code, Codex and others) gets a session named after +that agent process; its first command opens the session's own tab and later +commands stay there without moving the shared active tab. Name one with +`--session ` or `BROWSE_SESSION=`; `BROWSE_SESSION=` turns it off. +`--tab-id` and `BROWSE_TAB` win over a session and no longer move the active +tab either. Sessions isolate tabs, not cookies or storage, and only the local +root token can name one. + +### Phone emulation + touch + +This is Chromium emulating a phone on one tab, not Safari; a real device still goes through `/ios-qa`. + +| Command | Description | +|---------|-------------| +| `device [ [--landscape] \| off \| --list]` | Emulate a phone on the current tab only (size, pixel ratio, touch, `pointer: coarse`, user agent) from Playwright's devices table; other tabs stay desktop. `off` restores; `viewport` and `responsive` refuse while a device is active | +| `tap <@ref\|sel\|x y> [--hold ]` | Touch and release at an element's center or at x y. Needs `device` first | +| `swipe <@ref\|sel\|x y> [--distance ] [--ms ]` | One-finger swipe (default 300px over 300ms). Needs `device` first | +| `pinch <@ref\|sel\|x y> ` | Two-finger pinch; scale above 1 zooms in, below 1 zooms out. Needs `device` first | +| `touch [down\|move \| up \| cancel]` | Hold, move and lift numbered fingers for multi-finger flows; no action lists held fingers. Needs `device` first | + ### Extraction | Command | Description | @@ -1515,6 +1536,19 @@ capacity each), flushed to disk asynchronously via `Bun.write()`: - Network: `.gstack/browse-network.log` - Dialog: `.gstack/browse-dialog.log` +The console, network, dialog and startup-error logs start empty at each daemon +start and can grow while one daemon runs; `browse-daemon.log` and the +`browse-audit.jsonl` command audit build up across restarts, and codex scratch +files collect in `.gstack/tmp/`. +Run `gstack-clean` in the repository to see what it would reclaim, then +`gstack-clean --apply` to do it: it cuts these logs to their last 256 KB +once they pass 1 MB, removes `tmp/codex-*` files older than 7 days, and +trims the audit log only when you pass `--audit-days N`. It never touches +`browse.json`, `session-state.json`, `chromium-profile/`, `browser-skills/` +or `browse-states/`, and it skips the browse files while the daemon runs. +Deleting the whole `.gstack/` folder instead also deletes your cookies and +browser skills. + The `console`, `network`, and `dialog` commands read from the in-memory buffers (not disk) so capture is real-time even when disk is slow. @@ -1732,6 +1766,9 @@ the global `~/.gstack/browser-skills/foo/` only inside project-a. | `BROWSE_HEADLESS_SKIP` | 0 | Skip Chromium launch entirely (test harness only) | | `BROWSE_TUNNEL` | 0 | Activate the dual-listener tunnel architecture (requires `NGROK_AUTHTOKEN`) | | `BROWSE_TUNNEL_LOCAL_ONLY` | 0 | Test-only — bind both listeners locally without ngrok | +| `BROWSE_SESSION` | unset (agent harness calls get `agent-`) | CLI side: this caller's session name; empty or `off` turns sessions off | +| `BROWSE_SESSION_IDLE_MINUTES` | 120 | Daemon side: close (headless) or release (headed) a session's tabs after this many idle minutes; 0 keeps them | +| `BROWSE_MAX_SESSIONS` | 16 | Daemon side: most sessions at once; the least recently used idle one is closed to make room | | `CHROMIUM_PROFILE` | unset | Explicit headed Chromium profile directory (used by gbrowser's gbd per-workspace); honored by headed launch and profile-lock cleanup, not used by headless sessions | | `GSTACK_DISABLE_GPU` | unset | On macOS, headless Chromium starts with a GPU-taming flag set (it stops runaway GPU-process spin) that also disables WebGL: `getContext('webgl')`/`'webgl2'` return `null`, so WebGL pages (three.js, Mapbox GL, sigma.js) show their fallback. Set `GSTACK_DISABLE_GPU=off` to enable WebGL (`off` is the only recognized value). | | `GSTACK_BROWSE_MAX_HTML_BYTES` | 52428800 (50MB) | `load-html` size cap | @@ -1776,7 +1813,11 @@ browse/ │ ├── cdp-inspector.ts # $B inspect — persistent CDP session per page │ ├── activity.ts # ActivityEntry, CircularBuffer, SSE subscribers, privacy filtering │ ├── buffers.ts # Console/network/dialog circular buffers (O(1) ring) -│ ├── tab-session.ts # Per-tab session state (load-html replay, ref map scope) +│ ├── tab-session.ts # Per-tab session state (load-html replay, ref map scope, device profile, held fingers) +│ ├── session-name.ts # CLI: which session a call belongs to (--session, BROWSE_SESSION, agent harness pid) +│ ├── session-tabs.ts # Daemon: per-session and per-tab-id request scopes, idle sweep, session cap +│ ├── device-emulation.ts # $B device: per-tab phone emulation over the tab's own CDP session +│ ├── touch-commands.ts # $B tap/swipe/pinch/touch via Input.dispatchTouchEvent │ ├── token-registry.ts # Mint/validate/revoke for root + setup keys + scoped tokens │ ├── sse-session-cookie.ts # 30-min HttpOnly cookie for /activity/stream + /inspector/events │ ├── pty-session-cookie.ts # Separate scope: live Claude PTY auth diff --git a/CHANGELOG.md b/CHANGELOG.md index b89ddb8c9..efebba172 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,56 @@ # Changelog +## [1.93.0.0] - 2026-11-02 + +**`gstack-clean` reclaims the throwaway files that pile up in a repository's `.gstack/` folder, and changes nothing unless you pass `--apply`** (#2627). Until now nothing removed old codex scratch files or trimmed the browse logs, so the folder only grew (the reporter measured 8.3 MB in six weeks), and the only way out was deleting the whole folder, which also deletes your cookies, Chromium profile and browser skills. + +### Added +- **`bin/gstack-clean`** (rules in `lib/repo-state-clean.ts`): cleans `/.gstack` only. It removes top-level `tmp/codex-*` files older than 7 days, cuts `browse-console.log`, `browse-network.log`, `browse-dialog.log`, `browse-daemon.log` and `browse-startup-error.log` to their last 256 KB once they pass 1 MB (in place, starting after a line break, so a running writer keeps appending), and with `--audit-days N` drops `browse-audit.jsonl` entries older than N days while keeping every line without a readable timestamp. Everything else, including `browse.json`, `session-state.json`, `chromium-profile/`, `browser-skills/`, `browse-states/` and the other `tmp/` entries, is never touched. +- **Safety:** it refuses (exit 3) outside git, when `.gstack` is a symlink, when `BROWSE_STATE_FILE` names another folder, and when the folder is, holds or sits inside the gstack state root. It skips symlinks, logs with more than one hard link, files it cannot read and files changed in the last hour, skips browse files while the browse daemon runs and `tmp/` while codex runs, and skips a group when it cannot tell. Right before each change it checks that the folder is still the one it verified and that the file still has the size, time and inode it measured, and it stops that change if either moved. `--json` prints the same result as one versioned object. +- Result codes `GSTACK_CLEAN_USAGE`, `GSTACK_CLEAN_TARGET_REFUSED`, `GSTACK_CLEAN_PARTIAL` and `GSTACK_CLEAN_INTERNAL`, each with a docs/troubleshooting.md entry; BROWSER.md names the command where it lists the log files. + +### Tests +- `test/gstack-clean.test.ts`: the dry run changes no byte or mtime, `--apply` removes exactly the allowlist, protected files survive at any age, every refusal touches nothing, links out of the folder, hard-linked and unreadable logs and a symlinked `tmp/` are left alone, a log with no line break in its tail and a malformed audit file are left alone, an unlistable `tmp/` skips only its group, `--json` reaches a pipe whole with hundreds of actions and a reader that closes early is not an error, live and unknown liveness skip their group, the cut keeps the inode and a line boundary, audit trimming keeps the right lines byte for byte, exit codes 0, 1, 2 and 3, and the `--json` shape. +- `test/gstack-clean-producers.test.ts`: every tracked producer of a `.gstack/tmp/` entry is classified as scratch or kept, and any new `codex-*` name must be added to the scratch list on purpose. + +**Capy sessions on Claude Opus 5.5 can read a smaller set of gstack skills (GSTA-161).** Before, every model read the full shared preamble on every skill. Now Opus 5.5 gets the `lite` loadout: the same skills without eleven coaching sections of the preamble, about 10.7 KB less on land-and-deploy. Every other model, and any model gstack is not told about, still gets the full skills, and the checkout's committed SKILL.md files stay full. + +### Added +- **`lite` loadout and a model map** in `modules/loadouts.json`. `lite` drops the voice, writing-style, completeness, confusion, evidence, context-health, question-tuning, search-before-building, repo-mode, brain-health and context-recovery pieces and keeps the upgrade check and brain sync. `models` maps only `claude-opus-5-5` (and its dated snapshots) to it; a point release or any other suffix gets full. +- **Explicit model detection** (`lib/model-detect.ts`): an explicit loadout, then `--model`, then `GSTACK_MODEL`, else full. An unmapped, invalid or unreadable model id serves full and never errors (`LOADOUT_INVALID_MODEL` names an id that is not a plain model id). +- **`gstack-capy-install --model `** (passed through `scripts/install.sh`), or `GSTACK_MODEL=` in its environment, which the Capy skill uses because an older installer ignores it: prints `LOADOUT` and `SKILL_ROOT` lines and, for lite, writes a second launch file that adds `GSTACK_LOADOUT_SKILL_ROOT`. The install verdict and its key do not change. +- **`gstack-modules install` and `resolve`**: `install` publishes the lite tree under `/loadouts/-/` as a complete skill tree (rendered files plus every tracked runtime asset), keyed by the selection, model, full root, host and source tree, reused only after its files re-hash against the lock, and never replaced in place. A failure serves full with `LOADOUT_FALLBACK`. `render --bind-model --full-root ` renders the same start lines for eval arms. +- **`./setup --loadout `** for Claude Code: saved as `claude_loadout` and rendered through setup's own Claude render into the untracked render dir; `--loadout full` returns to the committed files. +- **The start line carries the model.** A lite skill's start command names its model and the full install root, so `gstack-skill-start` prints `LOADOUT` and `LOADOUT_RULE` lines, plus `LOADOUT_MISMATCH` when `GSTACK_MODEL` names another model, telling that session to read the full SKILL.md instead. + +### Tests +- `test/model-detect.test.ts`, `test/module-loadouts-install.test.ts`, `test/loadout-setup.test.ts`, and new cases in `test/module-loadouts.test.ts` and `test/gstack-capy-install.test.ts` cover the mapping and its near misses, hostile model ids, the start-line rules, two models on one machine, runtime assets from `SKILL_ROOT`, identity changes, concurrent publishers, and `./setup --loadout`. + +**Shadow mode for the CEO-only decider: in a driver run that turns it on, every approval gate gets one hidden model prediction per question, recorded before the owner answers (GST-537).** Before the CEO-only mode is allowed to decide for the owner, we need to know how often it would pick what he picks. The only decider before this was a fixed rule that always keeps the plan's direction, which agrees with the reviewers on every past item, so it measured nothing. Now a model (Opus 5.5 by default, about $0.07 per gate on your own API key) predicts each answer, and taste-replay round 2 in gstack-evals compares the predictions with the owner's real answers. Nothing routes to the owner differently. + +### Added +- **`gstack-config set autoplan_shadow on`** (off by default; `GSTACK_AUTOPLAN_SHADOW=1|0` overrides it for one run) turns shadow mode on at a run's first `next`, stored in `run.json` so a resumed run cannot flip it. `autoplan_shadow_model` picks the model (default `claude-opus-5-5`). It stays off with `SHADOW: off ()` when there is no API key for the model, the model has no price, the run is `--no-outside` or CEO-only. +- **Every driver command that prints a gate** (`next`, `submit`, `close`, `resume`, `revise`, `run`, `check`) runs the shadow first and prints `SHADOW: recorded model= usd=` or `SHADOW: failed `, then the gate, then the checkpoint. The prediction goes to the private `/shadow/ceo-only.r.jsonl`: action, pick, confidence, cites and reason per question, beside the keep-direction rule's pick, the frozen question, the prompt hash and the cost, plus a 10% audit mark for a later live switch. The run lock is free during the call; another writer pays nothing while a call is in flight, `answer` waits for it, and after a crash `answer` records that gate first, marked retrospective. A shadow failure never blocks a gate or an answer. +- **Shadow calls count against `--spend-cap`** under their own ledger ids labeled `shadow`, show as `shadow=` on `GATE_SPEND`, and never change the per-voice price on `GATE_REC_COST`. A call that was sent and then lost to a crash settles `unknown`, never released as undispatched. +- **`gstack-autoplan shadow --out | --runs [--json]`**, a read-only export for gstack-evals: one row per item of each answered gate, its shadow status (recorded, malformed, failed, missing, retrospective, off) and the owner's answer with how he gave it. An unanswered or interrogated gate refuses `SHADOW_BEFORE_ANSWER`. +- **Answered decision rows record how the owner answered**, parsed from his own words: `owner_mode` (`rec`, `rec_except`, `tokens`, `as_is`, `unparsed`), `owner_named`, the `rec_hash` answered against and `answered_role`. +- `gstack-artifact validate ` checks each shadow record against its journaled sha256. + +### Changed +- The shared model-call transport can write a caller's own egress receipt, returns the provider's stop reason (a reply cut at the output limit is retried at 8,000 tokens, not treated as malformed), and sends the output limit to OpenAI models as `max_completion_tokens` when a caller sets one. Existing callers send the same bytes as before. + +- **Parallel agents no longer take over each other's browser tab.** When several agents drive gstack's own browser at once, each agent process now gets its own tab on its first command, and its `goto`, `snapshot`, `click` and chain commands stay on that tab (#1961). Name a session yourself with `--session ` or `BROWSE_SESSION=` (subagents that share one agent process need this); `BROWSE_SESSION=` turns it off. Sessions isolate tabs, not logins, so cookies and storage stay shared. A session idle for 120 minutes is closed (`BROWSE_SESSION_IDLE_MINUTES`, 0 keeps it), at most 16 run at once (`BROWSE_MAX_SESSIONS`), and only the local root token can name one. `tabs` now shows each tab's owner. +- **`BROWSE_TAB` and `--tab-id` no longer move the active tab for everyone else.** A command pinned to a tab used to switch the daemon's active tab while it ran, so another caller's command during that window landed on the pinned tab, and two overlapping pinned commands could leave the daemon on the wrong tab. Each request now runs on its own tab without touching the shared one. +- **QA can now test phone pages with real touch input.** `$B device "iPhone 15"` makes the current tab act like that phone (size, pixel ratio, touch support, mobile user agent and `pointer: coarse`) without resizing any other tab; `$B device off` restores it and `$B device --list` names every profile. `$B tap`, `$B swipe`, `$B pinch` and `$B touch down|move|up ` send real touch events, including several fingers at once (hold a joystick while tapping fire) (#3058). This is Chromium emulating a phone, not Safari; /qa's report names the phones it emulated and what still needs a real device through /ios-qa. When the Aside browser drives, its cookbook has a touch recipe, checked on Aside 1.26. +- New result codes, each with a troubleshooting entry: `BROWSE_SESSION_INVALID`, `BROWSE_SESSION_ROOT_ONLY`, `BROWSE_SESSION_TAB_RESET`, `BROWSE_SESSION_LIMIT`, `BROWSE_DEVICE_UNKNOWN`, `BROWSE_DEVICE_CDP_FAILED`, `BROWSE_TOUCH_NO_DEVICE`. + +- **`/investigate` drops its five-phase step procedure** (GST-938). Harness Evals watched Opus 5.5 run `/investigate` on 46 bug-fix sessions: the procedure loaded every time and the model followed none of its steps (0 of 30 with Claude Code's real system prompt), while a gstack session cost $0.46 against $0.22 without gstack (HARN-14, gn-hard-follow-2026-11). The skill keeps the parts that still do a job. Those are the Iron Law, the arguments table, a short "How to work" with the three lessons that cost real time before (read call sites before calling two paths duplicates, refresh the git index before timing, test a shared premise before a third fix), the 3-strike and more-than-5-files asks, the Scope Lock with its freeze hooks, the recipes and `--backlog`, the prior-learnings search and the DEBUG REPORT. The generated Claude skill shrinks from 29.8 KB to 20.2 KB. Gone: the Phase 1-5 walkthrough, the pattern table, the web-research block and the hypothesis-keyed learnings re-pull. The research block's rule stays as a guard, so a web search names the error class and library, never raw error text, hostnames, paths or secrets. BROWSER.md and docs/skills.md now describe the short skill. +- Behavior change: the model is no longer told to work through Phases 1-5 in order, and `/investigate` no longer carries the Aside web-research block. Hook-enforced versions of the two steps that could change results (reproduce first, verify after) are planned as an opt-in Claude Code candidate that ships only if a preregistered eval shows it helps. + +- **Opening a tab for one browser session no longer undoes another caller's tab switch.** If a different caller switched the active tab while a session's new tab was opening, the daemon used to switch back; it now keeps the newer choice. +- **`gstack-artifact validate` catches a deleted shadow record.** A revision the journal says was recorded, but whose record file is gone, now fails as `ARTIFACT_STALE` instead of passing. +- **A shadow that fails while saving its record is journaled and never stops the run.** The failure is written as `shadow_failed` under the run lock and its lease is dropped; if the lock stays taken, the lease expires and the next command settles that revision. + ## [1.92.83.0] - 2026-11-02 **The sealed guard no longer rescans commits the destination already holds, so a stacked branch retargeted after its parent squash-landed stops being refused for the parent's history.** gstack-evals#22 listed its parent #6's seven commits after #6 landed as one squash. One of them had added and later deleted a held-out file, so the guard refused a push whose every byte GitHub already had as `refs/pull/6/head`, and the queue sat blocked for about 50 minutes until a waiver landed. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 634968ea6..18845d8bf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -206,6 +206,7 @@ the new defaults. | Validate periodic cases plus the weekly gate census on a branch | `gh workflow run evals-periodic.yml --ref -f include_gate_census=true` | CI runners + API spend | Pushed branch, `gh` with workflow rights | | Run the opt-in ML, gitleaks and Swift checks | `gh workflow run platform-qualification.yml --ref ` | Free CI runners | Pushed branch, `gh` with workflow rights | | Render one module loadout as an eval arm ([loadouts](docs/loadouts.md)) | `bin/gstack-modules render --host claude --modules full,-section:/ --out-dir ` (`--loadout full` for the control) | Free, about a second per host | Bun 1.4.2 | +| See which loadout a model gets, or publish it as Capy would ([loadouts](docs/loadouts.md#how-gstack-picks-a-loadout)) | `bin/gstack-modules resolve --model `; `bin/gstack-modules install --model --full-root ~/.claude/skills --store ` | Free, under a second | Bun 1.4.2 | | Look at past local eval runs | `bun run eval:list` / `eval:compare` / `eval:summary` | Free | Local eval history | | See a case's pass rate across recent weekly runs | `bun run eval:pass-rates --case ` | Free | `gh` with repo read access | | Measure one red case alone against the ship bar (MEETS 9/10) | `bun run scripts/ship-measure.ts measure --case --round baseline` ([bar](docs/TESTING_INTERNALS.md#ship-measure)) | API spend for 10 trials (12 for behavior); asks above $2/trial, stops at $25/case | Same as paid coverage | diff --git a/TODOS.md b/TODOS.md index 215b3b0aa..83709f2a4 100644 --- a/TODOS.md +++ b/TODOS.md @@ -2,6 +2,13 @@ ## NEXT PRIORITY +### P3: gstack-clean follow-ups (filed 2026-10-31, GSTA-226 E2, #2627) + +- **Codex templates delete their own scratch.** The `/codex` review, challenge and consult blocks leave `codex-err-*`, `codex-resp-*`, `codex-out-*` and `codex-prompt-*` mktemp files under `TMP_ROOT` (which falls back to `.gstack/tmp`) and the `codex-prompt.*` and `codex-focus.*` free-text files in `.gstack/tmp`; `gstack-clean` removes them after 7 days. Delete each file at the end of its block instead. Skill prose for every user, so it needs its own review. **Effort:** S. **Priority:** P3. +- **Codex scratch lands at the git toplevel.** `bin/gstack-paths` falls back to the relative `.gstack/tmp` when `TMPDIR` and `TMP` are unset, so a /codex run from a subfolder leaves scratch in `/.gstack/tmp`, where `gstack-clean` does not look. Make the fallback `$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.gstack/tmp`. **Effort:** S. **Priority:** P3. +- **`gstack-clean` reclaims `browse-daemon.log.1`.** The browse CLI renames `browse-daemon.log` to `.1` past 10 MB and nothing ever removes the copy. Add it to the browse group (remove when older than an hour and the daemon is not running). **Effort:** S. **Priority:** P3. +- **Browse hints `gstack-clean` when `.gstack/tmp` is large.** Print one line at daemon start when `.gstack/tmp` or the browse logs pass a size threshold, naming `gstack-clean`. **Effort:** S. **Priority:** P3. + ### P2/P3: Gemini design provider follow-ups (filed 2026-10-31, from the #990 build) - **Run the live Gemini check once a key exists** — every Gemini request and response shape in `design/src/provider.ts` follows Google's docs and stubbed fixtures; nobody has sent one to the live API. `GEMINI_API_KEY=... bun run design/scripts/live-gemini-check.ts` (about $0.04) settles the image body, the thinking-image pick and the OpenAI-compatible vision endpoint. A JPEG answer is refused today. **Effort:** S. **Priority:** P2. @@ -95,7 +102,7 @@ The 73 open feature requests (75 with #3061 and #3122, which the themes below al - **Ship, review and safety (9 issues)** — `/review` shadowing the native command (#422), a silent-deploy-failures checklist section (#2089), Conductor TODO proposals and a /ship block on unresolved ones (#2090), a light-ship mode (#2188), auditing an issue before filing it (#2642), maintainer-gated CI versus failures in landing-report (#2766), Flutter pubspec versions with build metadata (#2833), a deterministic fact-consistency check for document skills (#2887), a dual-engine /careful guard (#2888). **Priority:** P3. - **Planning reviews (8 issues)** — marketplace-aware skill recommendations (#419), founder-signal reasoning in the YC recommendation (#513), grounded verification of paths, math and flags in spec reviews (#973), a substrate-neutral existing-capability check (#1685), `/autoplan --tier` (#1955), autoplan workspace safeguards (#1959), splitting one-way architecture forks from two-way defects (#2490), an evidence ledger for "is demand real?" (#2350). **Priority:** P3. - **State root and project identity (6 issues)** — repo-local portable context artifacts (#1161), design-doc archive and prune (#1536), umbrella directories over several repos (#1923), reclaiming per-repo `.gstack` state (#2627), nested repos declaring themselves projects (#2649), references and disposition on decision events (#2678). **Priority:** P3. -- **Browser and mobile QA (6 issues)** — a remote browser via `BROWSER_WS_ENDPOINT` (#974), per-session tab isolation (#1961), an opt-in User-Agent suffix and default headers (#2459), skipping the Aside probe on Linux and Windows (#2864), activity-ring copies on caught-up reads (#2973), touch input and mobile Safari (#3058). **Priority:** P3. +- **Browser and mobile QA (5 issues)** — a remote browser via `BROWSER_WS_ENDPOINT` (#974), an opt-in User-Agent suffix and default headers (#2459), skipping the Aside probe on Linux and Windows (#2864), activity-ring copies on caught-up reads (#2973), and mobile Safari, the half of #3058 that Chromium touch emulation (GSTA-227) did not cover. Per-session tabs (#1961) shipped in GSTA-227. **Priority:** P3. - **Prompt cost and skill shape (4 issues)** — a response-header template (#1376), caching preamble output within a session (#1572), the Fable 5 prompting-guide audit (#1994), per-skill benefit in `gstack-context-bill` (#2889). **Priority:** P3. - **Customization and localization (3 issues)** — locale-aware skills (#481), a tutorial on new customization dimensions (#489), a `SKILL.local.md` overlay (#1759). **Priority:** P3. - **Retro (2 issues)** — a team-scoped multi-repo retro (#402), ADR and issue awareness in the narrative (#2284). **Priority:** P3. diff --git a/VERSION b/VERSION index 097482065..a1f1aa2fc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.92.83.0 +1.93.0.0 diff --git a/agents-digest/gstack-AGENTS.md b/agents-digest/gstack-AGENTS.md index b332bb94e..fe09cc4ba 100644 --- a/agents-digest/gstack-AGENTS.md +++ b/agents-digest/gstack-AGENTS.md @@ -1,4 +1,4 @@ -# gstack digest v1.92.83.0 — regenerate/re-copy after upgrading gstack +# gstack digest v1.93.0.0 — regenerate/re-copy after upgrading gstack Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed for agent hosts without a full skill install. The full skills add workflows, diff --git a/autoplan/shadow-decider.md b/autoplan/shadow-decider.md new file mode 100644 index 000000000..aaafdfcbc --- /dev/null +++ b/autoplan/shadow-decider.md @@ -0,0 +1,76 @@ + +# Predict the owner's answer at an autoplan approval gate + +You are predicting how one specific person, the owner of this plan, will answer +the questions at the final approval gate of an autoplan review. You are not +deciding for him and nothing you write is shown to him. Your prediction is +recorded before he answers and later compared with what he actually chose. + +Be a forecaster, not an advocate. A good prediction is one that turns out to +match his answer. Do not argue for what you think is best for the plan. + +## What you have + +- The plan's Implementation plan section, as reviewed. +- Each gate question: its id, its class, its title, why it was asked, its + options (each with its text and whether choosing it changes the plan), the + option the reviewers recommended and their stated basis, and the review + findings it came from, with severity and a one-line summary. + +The reviewers' recommendation is one input among several. It is often right +and sometimes wrong. Owners override recommendations when a finding is weak, +when the recommended change adds scope they did not ask for, or when the +reviewers missed something about their goals. Weigh it; do not copy it. + +## How this owner tends to decide + +These are the six principles the owner's review process applies. Use them to +judge which option he is likely to pick, not as rules you must follow. + +1. Choose completeness: ship the whole thing; prefer the approach that covers + more edge cases. +2. Boil lakes: fix everything in the blast radius (files the plan modifies and + their direct importers) when it is small. +3. Pragmatic: when two options fix the same thing, pick the cleaner one, fast. +4. DRY: reject what duplicates existing functionality; reuse what exists. +5. Explicit over clever: a short obvious fix beats a large abstraction. +6. Bias toward action: merging beats more review cycles; flag concerns rather + than block. + +## For each question, predict + +- action: what the owner would do with it. + - `decide`: he answers it now by picking one option. + - `send_back`: he sends the plan back for more work before answering. + - `hold`: he leaves it open or asks questions first. +- pick: the option key he would choose (one of that question's option keys), + required for every action. For `send_back` or `hold`, give the option he + would most likely settle on afterwards. +- confidence: a number from 0 to 1, the probability that he picks exactly that + option. +- cites: the finding ids from that question's findings that most shaped your + prediction (an empty list when none did). Cite only ids listed for that + question. +- reason: one or two sentences, at most 400 characters. + +## The plan + +{{PLAN}} + +## The questions + +{{QUESTIONS}} + +## Output contract + +Reply with exactly one fenced `json` block holding one JSON array and nothing +else. The array has exactly one object per question id listed below, in any +order, with no duplicates and no other ids: + +{{IDS}} + +Each object has exactly these fields: + +```json +{"id": "", "action": "decide | send_back | hold", "pick": "