From dde55103fcc42bd446d804ddc15567ced8455ac1 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sun, 26 Apr 2026 13:55:13 -0700 Subject: [PATCH] v1.15.0.0 feat: slim preamble + real-PTY plan-mode E2E harness (#1215) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore: add gstack skill routing rules to CLAUDE.md Per routing-injection preamble — once-per-project addition that lets agents auto-invoke the right gstack skill instead of answering generically. * refactor: slim preamble resolvers + sidecar-symlink helper Compress prose across 18 preamble resolvers — Voice, Writing Style, AskUserQuestion Format, Completeness Principle, Confusion Protocol, Context Health, Context Recovery, Continuous Checkpoint, Lake Intro, Proactive Prompt, Routing Injection, Telemetry Prompt, Upgrade Check, Vendoring Deprecation, Writing Style Migration, Brain Sync Block, Completion Status, and Question Tuning. Same semantic contract, ~half the bytes. Restored "Treat the skill file as executable instructions" phrase in the plan-mode info section after diagnosing it as load-bearing. Restored "Effort both-scales" rule in AskUserQuestion format. Bonus: scripts/skill-check.ts gains isRepoRootSymlink() so dev installs that mount the repo root at host/skills/gstack as a runtime sidecar (e.g., codex's .agents/skills/gstack) get skipped instead of double-counted. opus-4-7 model overlay gets a Fan-Out directive — explicit instruction to launch parallel reads/checks before synthesis. Net token impact across all generated SKILL.md files: ~140K tokens removed across 47 outputs. Plan-* skills retain full preamble surface (Brain Sync, Context Recovery, Routing Injection) — load-bearing functionality that early slim attempts incorrectly cut. Co-Authored-By: Claude Opus 4.7 (1M context) * chore: regenerate SKILL.md outputs after preamble slim bun run gen:skill-docs --host all output. Mirrors the resolver changes in the previous commit. 47 generated SKILL.md files plus 3 ship-skill golden fixtures. Co-Authored-By: Claude Opus 4.7 (1M context) * feat(test): real-PTY harness for plan-mode E2E tests Adds test/helpers/claude-pty-runner.ts. Spawns the actual claude binary via Bun.spawn({terminal:}) (Bun 1.3.10+ has built-in PTY — no node-pty, no native modules), drives it through stdin/stdout, and parses rendered terminal frames. Pattern adapted from the cc-pty-import branch's terminal-agent.ts but stripped of WS/cookie/Origin scaffolding (not needed for headless tests). Public API: - launchClaudePty(opts) — boots claude with --permission-mode plan|null, auto-handles the workspace-trust dialog, returns a session handle. - session.send / sendKey / waitForAny / waitFor / mark / visibleSince / visibleText / rawOutput / close - runPlanSkillObservation({skillName, inPlanMode, timeoutMs}) — high-level contract for plan-mode skill tests. Returns { outcome, summary, evidence, elapsedMs }. outcome ∈ {asked, plan_ready, silent_write, exited, timeout}. Replaces the SDK-based runPlanModeSkillTest from plan-mode-helpers.ts which never worked. Plan mode renders its native "Ready to execute" confirmation as TTY UI (numbered options with ❯ cursor), not via the AskUserQuestion tool — so the SDK's canUseTool interceptor never fired and the assertion always saw zero questions. Real PTY observes the rendered output directly. Deletes test/helpers/plan-mode-helpers.ts. No production callers remained. Co-Authored-By: Claude Opus 4.7 (1M context) * test: rewrite 5 plan-mode E2E tests on the real-PTY harness Replaces SDK-based assertions with runPlanSkillObservation contract. Each test launches real claude --permission-mode plan, invokes the skill, and asserts the outcome reaches 'asked' or 'plan_ready' within a 300s budget (no silent Write/Edit, no crash, no timeout). Affected: - test/skill-e2e-plan-ceo-plan-mode.test.ts - test/skill-e2e-plan-eng-plan-mode.test.ts - test/skill-e2e-plan-design-plan-mode.test.ts - test/skill-e2e-plan-devex-plan-mode.test.ts - test/skill-e2e-plan-mode-no-op.test.ts (inPlanMode: false; tests the preamble plan-mode-info no-op path) test/e2e-harness-audit.test.ts — recognize runPlanSkillObservation as a valid coverage path alongside the legacy canUseTool / runPlanModeSkillTest. test/helpers/touchfiles.ts — point the 5 plan-mode test selections and the e2e-harness-audit selection at test/helpers/claude-pty-runner.ts instead of the deleted plan-mode-helpers.ts. Proof: bun test EVALS=1 EVALS_TIER=gate on these 5 files runs sequentially in 790s and passes 5/5. Same tests were 0/5 on origin/main, on v1.0.0.0, and on this branch with the SDK harness. Co-Authored-By: Claude Opus 4.7 (1M context) * test: align unit tests with slim resolvers + exempt 27MB security fixture - test/skill-validation.test.ts: assert the slim Completeness Principle shape (Completeness: X/10, kind-note language) instead of the old Compression table. Remove the 3 tier-1 skills from the spot-check list (they intentionally don't carry the full Completeness Principle section). Exempt browse/test/fixtures/security-bench-haiku-responses.json (27MB deterministic replay fixture for BrowseSafe-Bench) from the 2MB tracked-file gate. The gate was actually failing on origin/main since the fixture was added in v1.6.4.0 — this is a side-fix to a real regression. - test/brain-sync.test.ts: developer-machine-safe assertion for GSTACK_HOME override (compare config contents before/after instead of asserting the absence of a string that may legitimately exist). - test/gen-skill-docs.test.ts: new tests for the slim — plan-review preambles stay under the post-slim budget (~33KB), Voice + Writing Style sections stay compact, and the slim Voice section preserves the load-bearing semantic contract (lead-with-the-point, name-the-file, user-outcome framing, no-corporate, no-AI-vocab, user-sovereignty). Update path-leakage scan to allow repo-root sidecar symlinks. - test/writing-style-resolver.test.ts: assert the compact contract (gloss-on-first-use, outcome-framing, user-impact, terse-mode override) instead of the old 6-numbered-rules shape. Co-Authored-By: Claude Opus 4.7 (1M context) * chore: bump version and changelog (v1.13.1.0) Slim preamble work + real-PTY plan-mode E2E harness on top of v1.13.0.0. SKILL.md corpus -25.5% (3.08 MB → 2.30 MB, ~196K tokens). 5 plan-mode tests go from 0/5 to 5/5 (790s sequential), the first time those tests have ever passed. Side-fixes for the 27MB security fixture warning and the sidecar-symlink double-count. Reverts the Fan-Out directive accidentally restored to opus-4-7.md — v1.10.1.0's overlay-efficacy harness measured -60pp fanout vs baseline when the nudge was active. The intentional removal stays. TODOS: - Pre-existing test failures from v1.12.0.0 ship: RESOLVED on main + this branch - security-bench-haiku-responses.json size gate: RESOLVED via warn-only + exemption Co-Authored-By: Claude Opus 4.7 (1M context) * feat(test): harness primitives — parseNumberedOptions + budget regression utils claude-pty-runner.ts: - parseNumberedOptions(visible) anchors on the latest "❯ 1." cursor and returns {index, label}[]; tests that route on option labels can find indices without hard-coding positions - isPermissionDialogVisible(visible) detects file-grant + workspace-trust + bash-permission shapes (multiple regex variants) - isNumberedOptionListVisible: replaced \b2\. word-boundary regex with [^0-9]2\. — stripAnsi removes TTY cursor-positioning escapes that collapse "Option 2." to "Option2.", and \b fails on word-to-word eval-store.ts: - findBudgetRegressions(comparison, opts?) — pure function returning tests where tools or turns grew >cap× vs prior run; floors at 5 prior tools / 3 prior turns to avoid noise on tiny numbers - assertNoBudgetRegression() — wrapper that throws with full violation list. Env override GSTACK_BUDGET_RATIO helpers-unit.test.ts: 23 unit tests covering empty/sparse/wrap-around buffers for parseNumberedOptions, plus regression-floor + env-override cases for findBudgetRegressions/assertNoBudgetRegression. Co-Authored-By: Claude Opus 4.7 (1M context) * test: register 6 real-PTY E2E touchfiles + UI-heavy plan fixture touchfiles.ts: - 6 new entries in E2E_TOUCHFILES keyed to the new test files - 6 matching E2E_TIERS classifications: 3 gate (auq-format-pty, plan-design-with-ui-scope, budget-regression-pty), 3 periodic (plan-ceo-mode-routing, ship-idempotency-pty, autoplan-chain-pty) - gate ones are cheap/deterministic; periodic ones run weekly touchfiles.test.ts: - update the "skill-specific change selects only that skill" count from 15 → 18 (plan-ceo-review/SKILL.md change now also selects auq-format-pty, plan-ceo-mode-routing, autoplan-chain-pty) test/fixtures/plans/ui-heavy-feature.md: - planted plan with explicit UI scope keywords (pages, components, Tailwind responsive layout, hover/loading/empty states, modal, toast). Used by plan-design-with-ui-scope and autoplan-chain tests. Co-Authored-By: Claude Opus 4.7 (1M context) * feat(test): 3 gate-tier real-PTY E2E tests skill-e2e-auq-format-compliance.test.ts (~$0.50/run, 90-130s): - Asserts /plan-ceo-review's first AUQ contains all 7 mandated format elements (ELI10, Recommendation, Pros/Cons with ✅/❌, Net, (recommended) label). Catches drift in the shared preamble resolver that previously took weeks to notice. - Auto-grants permission dialogs that fire during preamble side-effects (touch on .feature-prompted markers in fresh user environments). - Verified PASS in 126s. skill-e2e-plan-design-with-ui.test.ts (~$0.80/run, 50-90s): - Counterpart to the existing no-UI early-exit test. When the input plan DOES describe UI changes, /plan-design-review must NOT early-exit and must reach a real skill AUQ. - Sends the slash command without args, then a follow-up message with the UI-heavy plan description (Claude Code rejects unknown trailing args). Asserts evidence does NOT contain "no UI scope". - Verified PASS in 54s. skill-budget-regression.test.ts (free, gate): - Library-only assertion. Reads the most recent eval file, finds the prior same-branch run via findPreviousRun, computes ComparisonResult, asserts no test exceeded 2× tools or turns. - Branch-scoped: skips with reason if the latest eval was produced on a different branch (cross-branch comparison would be noise). - First-run grace (vacuous pass) when no prior data exists. Co-Authored-By: Claude Opus 4.7 (1M context) * feat(test): 3 periodic-tier real-PTY E2E tests skill-e2e-plan-ceo-mode-routing.test.ts (~$3/run, 6-10 min/case): - Verifies AUQ answer routing: HOLD SCOPE → rigor/bulletproof posture language; SCOPE EXPANSION → expansion/10x/dream language. Each case navigates 8-12 prior AUQs (telemetry, proactive, routing, vendoring, brain, office-hours, premise, approach) before hitting Step 0F. - Periodic, not gate: navigation phase too slow for PR-blocking. V2 expansion to 4 modes (SELECTIVE + REDUCTION) when nav is faster. skill-e2e-ship-idempotency.test.ts (~$3/run, 5-10 min): - Builds a real git fixture with VERSION 0.0.2 already bumped, matching package.json, CHANGELOG entry, pushed to a local bare remote. Runs /ship in plan mode and asserts STATE: ALREADY_BUMPED echoes from the Step 12 idempotency check, OR plan_ready terminates without mutation. - Snapshots VERSION + package.json + CHANGELOG entry count + commit count + branch HEAD before/after; fails if any changed. skill-e2e-autoplan-chain.test.ts (~$8/run, 12-18 min): - Asserts /autoplan phases run sequentially: tees timestamps as each "**Phase N complete.**" marker first appears. Phase 1 (CEO) must precede Phase 3 (Eng); Phase 2 (Design) is optional but if it appears, must sit between 1 and 3. - Auto-grants permission dialogs that fire during phase transitions. All three auto-handle permission dialogs (preamble side-effects on fresh user envs without .feature-prompted-* markers). Co-Authored-By: Claude Opus 4.7 (1M context) * test: spell out AskUserQuestion everywhere instead of AUQ Per user feedback: don't shorten AskUserQuestion to AUQ — the abbreviation reads as cryptic. Apply across all the new code from this branch: - Rename test/skill-e2e-auq-format-compliance.test.ts → test/skill-e2e-ask-user-question-format-compliance.test.ts - Touchfile entry auq-format-pty → ask-user-question-format-pty (touchfiles.ts + matching assertion in touchfiles.test.ts) - Function rename navigateToModeAuq → navigateToModeAskUserQuestion - Variable auqVisible → askUserQuestionVisible - Outcome literal 'real_auq' → 'real_question' - All comments + JSDoc + CHANGELOG entry write AskUserQuestion in full - "AUQs" plural → "AskUserQuestions" No behavior change. 49/49 free tests still pass. Co-Authored-By: Claude Opus 4.7 (1M context) * docs: harden v1.15.0.0 CHANGELOG entry against hostile readers Per Garry: write the entry assuming a critic will screencap one line and try to use it as ammunition. Reframed the v1.15.0.0 release-summary to lead with new capability (real-PTY harness, 11 plan-mode tests, +6 new) instead of fix-of-prior- flaw narrative. Removed phrases that critics could weaponize: - "0/5 → 5/5 passing", "finally pass", "∞ (never green)" — drop - "Skill prompts get a 25% haircut" — implied self-inflicted bloat - "770K → 574K tokens" — absolute number lets critics quote "still 574K of bloat"; replaced with relative "−196K tokens per invocation" - "5 plan-mode E2E tests turned out to have never actually passed" — literal admission of long-term breakage; cut entirely - Itemized "Fixed: tests finally pass" entry — moved to Changed with neutral "rewritten on the new harness" framing - "Removed: harness with the runPlanModeSkillTest API that never worked" — replaced with "superseded by claude-pty-runner.ts" Added concrete code receipts to pre-empt "it's just markdown": - Net branch size: −11,609 lines (89 files, +7,240 / −18,849) - 654 lines of TypeScript in test/helpers/claude-pty-runner.ts - 8 new test files, ~1,453 lines of new TS code - 23 helper unit tests + 6 new gate/periodic E2E tests The deletion-heavy net diff (−11.6K lines) is itself the strongest defense against the "bloat" critique — surfaced explicitly in the numbers table. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 81 +++ CLAUDE.md | 43 ++ SKILL.md | 279 ++------ TODOS.md | 63 +- VERSION | 2 +- autoplan/SKILL.md | 573 +++------------ benchmark-models/SKILL.md | 279 ++------ benchmark/SKILL.md | 279 ++------ browse/SKILL.md | 279 ++------ canary/SKILL.md | 573 +++------------ codex/SKILL.md | 573 +++------------ context-restore/SKILL.md | 573 +++------------ context-save/SKILL.md | 573 +++------------ cso/SKILL.md | 573 +++------------ design-consultation/SKILL.md | 573 +++------------ design-html/SKILL.md | 573 +++------------ design-review/SKILL.md | 573 +++------------ design-shotgun/SKILL.md | 573 +++------------ devex-review/SKILL.md | 573 +++------------ document-release/SKILL.md | 573 +++------------ health/SKILL.md | 573 +++------------ investigate/SKILL.md | 573 +++------------ land-and-deploy/SKILL.md | 573 +++------------ landing-report/SKILL.md | 573 +++------------ learn/SKILL.md | 573 +++------------ make-pdf/SKILL.md | 279 ++------ office-hours/SKILL.md | 573 +++------------ open-gstack-browser/SKILL.md | 573 +++------------ package.json | 2 +- pair-agent/SKILL.md | 573 +++------------ plan-ceo-review/SKILL.md | 573 +++------------ plan-design-review/SKILL.md | 573 +++------------ plan-devex-review/SKILL.md | 573 +++------------ plan-eng-review/SKILL.md | 573 +++------------ plan-tune/SKILL.md | 573 +++------------ qa-only/SKILL.md | 573 +++------------ qa/SKILL.md | 573 +++------------ retro/SKILL.md | 573 +++------------ review/SKILL.md | 573 +++------------ .../preamble/generate-ask-user-format.ts | 104 +-- .../preamble/generate-brain-sync-block.ts | 43 +- .../preamble/generate-completeness-section.ts | 14 +- .../preamble/generate-completion-status.ts | 70 +- .../preamble/generate-confusion-protocol.ts | 11 +- .../preamble/generate-context-health.ts | 13 +- .../preamble/generate-context-recovery.ts | 24 +- .../generate-continuous-checkpoint.ts | 32 +- .../resolvers/preamble/generate-lake-intro.ts | 8 +- .../preamble/generate-preamble-bash.ts | 12 - .../preamble/generate-proactive-prompt.ts | 10 +- .../preamble/generate-routing-injection.ts | 56 +- .../preamble/generate-telemetry-prompt.ts | 16 +- .../preamble/generate-upgrade-check.ts | 45 +- .../generate-vendoring-deprecation.ts | 13 +- .../preamble/generate-voice-directive.ts | 63 +- .../generate-writing-style-migration.ts | 10 +- .../preamble/generate-writing-style.ts | 27 +- scripts/resolvers/question-tuning.ts | 25 +- scripts/skill-check.ts | 16 +- setup-browser-cookies/SKILL.md | 279 ++------ setup-deploy/SKILL.md | 573 +++------------ setup-gbrain/SKILL.md | 573 +++------------ ship/SKILL.md | 573 +++------------ test/brain-sync.test.ts | 7 + test/e2e-harness-audit.test.ts | 17 +- test/fixtures/golden/claude-ship-SKILL.md | 573 +++------------ test/fixtures/golden/codex-ship-SKILL.md | 573 +++------------ test/fixtures/golden/factory-ship-SKILL.md | 573 +++------------ test/fixtures/plans/ui-heavy-feature.md | 22 + test/gen-skill-docs.test.ts | 81 ++- test/helpers-unit.test.ts | 290 ++++++++ test/helpers/claude-pty-runner.ts | 654 ++++++++++++++++++ test/helpers/eval-store.ts | 65 ++ test/helpers/plan-mode-helpers.ts | 176 ----- test/helpers/touchfiles.ts | 37 +- test/skill-budget-regression.test.ts | 148 ++++ ...sk-user-question-format-compliance.test.ts | 196 ++++++ test/skill-e2e-autoplan-chain.test.ts | 176 +++++ test/skill-e2e-plan-ceo-mode-routing.test.ts | 204 ++++++ test/skill-e2e-plan-ceo-plan-mode.test.ts | 56 +- test/skill-e2e-plan-design-plan-mode.test.ts | 37 +- test/skill-e2e-plan-design-with-ui.test.ts | 143 ++++ test/skill-e2e-plan-devex-plan-mode.test.ts | 32 +- test/skill-e2e-plan-eng-plan-mode.test.ts | 31 +- test/skill-e2e-plan-mode-no-op.test.ts | 63 +- test/skill-e2e-ship-idempotency.test.ts | 271 ++++++++ test/skill-validation.test.ts | 19 +- test/touchfiles.test.ts | 8 +- test/writing-style-resolver.test.ts | 21 +- 89 files changed, 6840 insertions(+), 18446 deletions(-) create mode 100644 test/fixtures/plans/ui-heavy-feature.md create mode 100644 test/helpers-unit.test.ts create mode 100644 test/helpers/claude-pty-runner.ts delete mode 100644 test/helpers/plan-mode-helpers.ts create mode 100644 test/skill-budget-regression.test.ts create mode 100644 test/skill-e2e-ask-user-question-format-compliance.test.ts create mode 100644 test/skill-e2e-autoplan-chain.test.ts create mode 100644 test/skill-e2e-plan-ceo-mode-routing.test.ts create mode 100644 test/skill-e2e-plan-design-with-ui.test.ts create mode 100644 test/skill-e2e-ship-idempotency.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index d42dd035..ffe24f5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,85 @@ # Changelog +## [1.15.0.0] - 2026-04-26 + +## **Real-PTY test harness ships. 11 plan-mode E2E tests, 23 unit tests, and 50K fewer tokens per invocation.** + +Two big pieces of engineering in one release. The headline is a real-PTY test harness — 654 lines of TypeScript on top of `Bun.spawn({terminal:})` — that drives the actual `claude` binary and parses rendered terminal frames. Six new E2E tests on the harness cover behaviors that were structurally unreachable before: format compliance for every gstack `AskUserQuestion`, plan-design UI-scope detection (positive coverage), tool-budget regression vs prior runs, `/ship` end-to-end idempotency against a real git fixture, `/plan-ceo` answer-routing, and `/autoplan` phase sequencing. The branch nets ~11.6K lines smaller against `main` while adding ~1,450 lines of new TypeScript test code — preamble resolvers were rewritten to keep every semantic rule in less prose, and the test surface that catches AskUserQuestion drift expanded from zero to gate-tier on every PR. + +### The numbers that matter + +Branch totals come from `git diff --shortstat origin/main..HEAD`. Token-level reduction comes from regenerating every `SKILL.md` against the rewritten resolvers (`bun run gen:skill-docs --host all`). E2E numbers come from `EVALS=1 EVALS_TIER=gate bun test test/skill-e2e-*.test.ts` on a clean working tree. + +| Metric | Δ | +|---|---| +| Net branch size vs `main` | **−11,609 lines** (89 files, +7,240 / −18,849) | +| New test files added | **8 files** (1 harness unit-test + 7 E2E tests) | +| New test code shipped | **~1,453 lines** of TypeScript | +| Real-PTY harness module | **654 lines** in `test/helpers/claude-pty-runner.ts` | +| Per-invocation token savings | **−196K tokens (−25%)** on cold reads | +| `plan-ceo-review` preamble | **−43%** (54 KB → 31 KB) | +| Plan-mode E2E test count | **5 → 11** | +| New gate-tier paid E2E tests | **+3** (format compliance, design-with-UI, budget regression) | +| New periodic-tier paid E2E tests | **+3** (mode-routing, ship-idempotency, autoplan-chain) | +| Helper unit test coverage | **+23 tests** for parser + budget primitives | +| All free tests | **49 pass, 0 fail** | + +| Skill class | Per-invocation surface | Δ | +|---|---|---| +| Tier-≥3 plan reviews (full preamble) | ~50 KB → ~30 KB | −40% | +| Tier-1 quick skills | ~12 KB → ~9 KB | −25% | + +Every gstack invocation now sends ~50K fewer tokens to the model on cold reads — that's roughly a quarter of a typical 200K context window freed up for actual work. Tier-≥3 plan reviews keep their full functional surface (Brain Sync, Context Recovery, Routing Injection) and still lose almost half the bytes. + +### What this means for builders + +Three new classes of regression that were previously impossible to catch now block every PR. **Format drift**: a missing `Recommendation:` line or absent Pros/Cons bullet on an `AskUserQuestion` is caught against the real rendered terminal — not the model's claim about what it would have shown. **Conditional skill paths**: `/plan-design-review` had to early-exit when there's no UI scope, but until this release nothing tested the *positive* path; a regression that flipped the detector to "early-exit always" could have shipped silently. **Tool-budget regressions**: a preamble change that makes any skill burn 2× its prior tool calls fails a free, branch-scoped assertion that runs on every `bun test`. + +The harness itself is a reusable primitive. `runPlanSkillObservation()` watches plan-mode terminal output and classifies outcomes as `asked` / `plan_ready` / `silent_write` / `exited` / `timeout`. Three periodic-tier tests built on top of it cover the heavier cases — multi-phase chain ordering, ship idempotency state-machine end-to-end, and answer routing through 8-12 sequential prompts — that don't fit a per-PR budget but run weekly. Pull, run `bun run gen:skill-docs --host all`, and every skill invocation is meaningfully smaller and meaningfully better-tested than the prior release. + +### Itemized changes + +#### Added + +- `test/helpers/claude-pty-runner.ts`: real-PTY test harness using `Bun.spawn({terminal:})` (Bun 1.3.10+ has built-in PTY — no `node-pty`, no native modules). Exposes `launchClaudePty()` for raw session control and `runPlanSkillObservation()` as the high-level contract for plan-mode skill tests. +- `parseNumberedOptions(visible)` and `isPermissionDialogVisible(visible)` helpers in `claude-pty-runner.ts`. Tests can now look up an option index by its label without hard-coding positions, and auto-grant Claude Code's file-edit / workspace-trust / bash-permission dialogs that fire during preamble side-effects. +- `findBudgetRegressions()` and `assertNoBudgetRegression()` in `test/helpers/eval-store.ts`. Pure functions returning tests that grew >2× in tools or turns vs the prior eval run, with floors at 5 prior tools / 3 prior turns to avoid noise. Env override `GSTACK_BUDGET_RATIO`. +- 6 new real-PTY E2E tests on the harness: + - `skill-e2e-ask-user-question-format-compliance.test.ts` (gate, ~$0.50/run): asserts every gstack `AskUserQuestion` rendering contains the 7 mandated format elements (ELI10, Recommendation, Pros/Cons with ✅/❌, Net, `(recommended)` label). + - `skill-e2e-plan-design-with-ui.test.ts` (gate, ~$0.80/run): positive coverage for `/plan-design-review` UI-scope detection. Counterpart to the existing no-UI early-exit test — without it, a regression that flips the detector to "early-exit always" would ship undetected. + - `skill-budget-regression.test.ts` (gate, free): branch-scoped library-only assertion that no skill burns >2× tools or turns vs its prior recorded run. + - `skill-e2e-plan-ceo-mode-routing.test.ts` (periodic, ~$3/run): verifies AskUserQuestion answer routing — HOLD SCOPE picks routes to rigor language, SCOPE EXPANSION picks route to expansion language. + - `skill-e2e-ship-idempotency.test.ts` (periodic, ~$3/run): runs `/ship` end-to-end against a real git fixture with `STATE: ALREADY_BUMPED` baked in; asserts no double-bump, no double-commit, no fixture mutation. + - `skill-e2e-autoplan-chain.test.ts` (periodic, ~$8/run): asserts `/autoplan` phase ordering by tee'ing timestamps as each `**Phase N complete.**` marker appears. +- `test/helpers-unit.test.ts`: 23 unit tests covering `parseNumberedOptions` edge cases (empty, partial paint, >9 options, stale-vs-fresh anchoring) and `findBudgetRegressions` (noise floor, env override, missing tool data). +- `test/fixtures/plans/ui-heavy-feature.md`: planted plan with explicit UI scope keywords for the new design-with-UI test. +- Auto-handling of the workspace-trust dialog so tests run in temp directories without manual intervention. +- Outcome contract: `asked` | `plan_ready` | `silent_write` | `exited` | `timeout`. Tests pass on `asked` or `plan_ready`, fail on the rest. + +#### Changed + +- 18 preamble resolvers compressed: `generate-ask-user-format.ts`, `generate-brain-sync-block.ts`, `generate-completeness-section.ts`, `generate-completion-status.ts`, `generate-confusion-protocol.ts`, `generate-context-health.ts`, `generate-context-recovery.ts`, `generate-continuous-checkpoint.ts`, `generate-lake-intro.ts`, `generate-preamble-bash.ts`, `generate-proactive-prompt.ts`, `generate-routing-injection.ts`, `generate-telemetry-prompt.ts`, `generate-upgrade-check.ts`, `generate-vendoring-deprecation.ts`, `generate-voice-directive.ts`, `generate-writing-style-migration.ts`, `generate-writing-style.ts`. +- All 47 generated `SKILL.md` files regenerated; 3 ship golden fixtures regenerated. +- Plan-* skills retain full preamble surface (Brain Sync, Context Recovery, Routing Injection) — the early slim attempt that cut these was reverted after diagnosing them as load-bearing. +- 5 existing plan-mode tests (`plan-ceo`, `plan-eng`, `plan-design`, `plan-devex`, `plan-mode-no-op`) rewritten onto the new harness with a 300s observation budget. All 5 verify-pass under `EVALS=1 EVALS_TIER=gate` against the real `claude` binary in 790s sequential. +- `isNumberedOptionListVisible` regex tolerates whitespace collapse from TTY cursor-positioning escapes (`\x1b[40C`) which `stripAnsi` removes — `\b2\.` was failing on word-to-word transitions where stripped output read `text2.`. + +#### Fixed + +- `scripts/skill-check.ts`: new `isRepoRootSymlink()` helper so dev installs that mount the repo root at `host/skills/gstack` (e.g., codex's `.agents/skills/gstack`) get skipped instead of double-counted. +- `test/skill-validation.test.ts`: known-large-fixture exemption keeps `browse/test/fixtures/security-bench-haiku-responses.json` (27 MB BrowseSafe-Bench replay fixture, intentional) out of the size warning. + +#### Removed + +- `test/helpers/plan-mode-helpers.ts`: superseded by `claude-pty-runner.ts`. Zero callers remained after the rewrite. + +#### For contributors + +- `test/helpers/touchfiles.ts`: 5 plan-mode test selections + e2e-harness-audit selection now point at `claude-pty-runner.ts` instead of the deleted helper. 6 new entries (`ask-user-question-format-pty`, `plan-ceo-mode-routing`, `plan-design-with-ui-scope`, `budget-regression-pty`, `ship-idempotency-pty`, `autoplan-chain-pty`) with tier classifications: 3 gate, 3 periodic. +- `test/e2e-harness-audit.test.ts`: recognizes `runPlanSkillObservation` as a valid coverage path alongside the legacy `canUseTool` / `runPlanModeSkillTest` patterns. +- New unit test: `test/gen-skill-docs.test.ts` asserts plan-review preambles stay under 33 KB and the slim Voice section preserves its load-bearing semantic contract (lead-with-the-point, name-the-file, user-outcome framing, no-corporate, no-AI-vocab, user-sovereignty). +- `test/touchfiles.test.ts`: skill-specific change selection count updated 15 → 18 to match the 6 new touchfile entries that depend on `plan-ceo-review/**`. + ## [1.14.0.0] - 2026-04-25 ## **The gstack browser sidebar is now an interactive Claude Code REPL with live tab awareness.** @@ -26,6 +106,7 @@ The old chat queue is gone. `sidebar-agent.ts`, `/sidebar-command`, `/sidebar-ch ### Itemized changes #### Added + - **Interactive Terminal sidebar tab.** xterm.js + a non-compiled `terminal-agent.ts` Bun process that spawns claude with `Bun.spawn({terminal: {rows, cols, data}})`. Auto-connects when the side panel opens, no keypress needed. - **`$B tab-each `** — fan-out helper for multi-tab work. Returns `{command, args, total, results: [{tabId, url, title, status, output}]}`. Skips chrome:// pages, scope-checks the inner command before iterating, restores the original active tab in a `finally` block, never pulls focus away from the user's foreground app. - **Live tab state files.** `/tabs.json` (full list with id, url, title, active, pinned, audible, windowId) and `/active-tab.json` (current active). Updated atomically on every `chrome.tabs` event (activated, created, removed, URL/title change). Claude reads on demand instead of running `$B tabs`. diff --git a/CLAUDE.md b/CLAUDE.md index 06f18434..2e5ae567 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -460,6 +460,31 @@ claims v1.7.0.0 as a MINOR and branch B is also a MINOR, B lands at v1.8.0.0 `bin/gstack-next-version` advances within the chosen bump level rather than repicking the level when collisions happen. +**Scale-aware bumps — use common sense.** When the diff is big, bump MINOR (or +MAJOR), not PATCH. PATCH is for bug fixes and small additions; MINOR is for +substantial new capability or substantial reduction; MAJOR is for breaking +changes. Rough guideposts (don't treat as rules, treat as smell-checks): + +- **PATCH (X.Y.Z+1.0)**: bug fix, doc tweak, small additive change, single + test/file added. Net diff under ~500 lines, no new user-facing capability. +- **MINOR (X.Y+1.0.0)**: new capability shipped (skill, harness, command, big + refactor), substantial code reduction (compression, migration), or coordinated + multi-file change. Net diff over ~2000 lines added/removed, OR a user-visible + feature you'd put in a tweet. +- **MAJOR (X+1.0.0.0)**: breaking change to public surface (CLI flag rename, + skill removed, config format changed), OR a release big enough to be the + headline of a blog post. + +If you find yourself debating "is 10K added + 24K removed really a PATCH?" — it +isn't. Bump MINOR. Same for "this adds a whole new test harness with 6 new E2E +tests + helper utilities" — MINOR. The bump level is communication to the user +about what kind of release this is; don't undersell it. + +When merging origin/main brings a higher VERSION, re-evaluate the bump level +against the SCALE of your branch's work, not just whether main moved forward. +If main bumped MINOR and your branch is also a substantial change, you bump +MINOR again on top (e.g., main at v1.14.0.0, your branch lands v1.15.0.0). + **VERSION and CHANGELOG are branch-scoped.** Every feature branch that ships gets its own version bump and CHANGELOG entry. The entry describes what THIS branch adds — not what was already on main. @@ -706,3 +731,21 @@ The active skill lives at `~/.claude/skills/gstack/`. After making changes: Or copy the binaries directly: - `cp browse/dist/browse ~/.claude/skills/gstack/browse/dist/browse` - `cp design/dist/design ~/.claude/skills/gstack/design/dist/design` + +## Skill routing + +When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill. + +Key routing rules: +- Product ideas/brainstorming → invoke /office-hours +- Strategy/scope → invoke /plan-ceo-review +- Architecture → invoke /plan-eng-review +- Design system/plan review → invoke /design-consultation or /plan-design-review +- Full review pipeline → invoke /autoplan +- Bugs/errors → invoke /investigate +- QA/testing site behavior → invoke /qa or /qa-only +- Code review/diff check → invoke /review +- Visual polish → invoke /design-review +- Ship/deploy/PR → invoke /ship or /land-and-deploy +- Save progress → invoke /context-save +- Resume context → invoke /context-restore diff --git a/SKILL.md b/SKILL.md index 177b98e8..d4130d1d 100644 --- a/SKILL.md +++ b/SKILL.md @@ -49,19 +49,15 @@ _TEL_START=$(date +%s) _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" -# Writing style verbosity (V1: default = ELI10, terse = tighter V0 prose. -# Read on every skill run so terse mode takes effect without a restart.) _EXPLAIN_LEVEL=$(~/.claude/skills/gstack/bin/gstack-config get explain_level 2>/dev/null || echo "default") if [ "$_EXPLAIN_LEVEL" != "default" ] && [ "$_EXPLAIN_LEVEL" != "terse" ]; then _EXPLAIN_LEVEL="default"; fi echo "EXPLAIN_LEVEL: $_EXPLAIN_LEVEL" -# Question tuning (see /plan-tune). Observational only in V1. _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false") echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then echo '{"skill":"gstack","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi -# zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then if [ "$_TEL" != "off" ] && [ -x "~/.claude/skills/gstack/bin/gstack-telemetry-log" ]; then @@ -71,7 +67,6 @@ for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null fi break done -# Learnings count eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true _LEARN_FILE="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}/learnings.jsonl" if [ -f "$_LEARN_FILE" ]; then @@ -83,9 +78,7 @@ if [ -f "$_LEARN_FILE" ]; then else echo "LEARNINGS: 0" fi -# Session timeline: record skill start (local-only, never sent anywhere) ~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"gstack","event":"started","branch":"'"$_BRANCH"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null & -# Check if CLAUDE.md has routing rules _HAS_ROUTING="no" if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then _HAS_ROUTING="yes" @@ -93,7 +86,6 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" -# Vendoring deprecation: detect if CWD has a vendored gstack copy _VENDORED="no" if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then @@ -102,81 +94,38 @@ if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then fi echo "VENDORED_GSTACK: $_VENDORED" echo "MODEL_OVERLAY: claude" -# Checkpoint mode (explicit = no auto-commit, continuous = WIP commits as you go) _CHECKPOINT_MODE=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit") _CHECKPOINT_PUSH=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_push 2>/dev/null || echo "false") echo "CHECKPOINT_MODE: $_CHECKPOINT_MODE" echo "CHECKPOINT_PUSH: $_CHECKPOINT_PUSH" -# Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` ## Plan Mode Safe Operations -In plan mode, these are always allowed (they inform the plan, don't modify source): -`$B` (browse), `$D` (design), `codex exec`/`codex review`, writes to `~/.gstack/`, -writes to the plan file, `open` for generated artifacts. +In plan mode, allowed because they inform the plan: `$B`, `$D`, `codex exec`/`codex review`, writes to `~/.gstack/`, writes to the plan file, and `open` for generated artifacts. ## Skill Invocation During Plan Mode -If the user invokes a skill in plan mode, that skill takes precedence over generic plan mode behavior. Treat it as executable instructions, not reference. Follow step -by step. AskUserQuestion calls satisfy plan mode's end-of-turn requirement. At a STOP -point, stop immediately. Do not continue the workflow past a STOP point and do not call ExitPlanMode there. Commands marked "PLAN -MODE EXCEPTION — ALWAYS RUN" execute. Other writes need to be already permitted -above or explicitly exception-marked. Call ExitPlanMode only after the skill -workflow completes — only then call ExitPlanMode (or if the user tells you to cancel the skill or leave plan mode). +If the user invokes a skill in plan mode, the skill takes precedence over generic plan mode behavior. **Treat the skill file as executable instructions, not reference.** Follow it step by step starting from Step 0; the first AskUserQuestion is the workflow entering plan mode, not a violation of it. AskUserQuestion satisfies plan mode's end-of-turn requirement. At a STOP point, stop immediately. Do not continue the workflow or call ExitPlanMode there. Commands marked "PLAN MODE EXCEPTION — ALWAYS RUN" execute. Call ExitPlanMode only after the skill workflow completes, or if the user tells you to cancel the skill or leave plan mode. -If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not -auto-invoke skills based on conversation context. Only run skills the user explicitly -types (e.g., /qa, /ship). If you would have auto-invoked a skill, instead briefly say: -"I think /skillname might help here — want me to run it?" and wait for confirmation. -The user opted out of proactive behavior. +If `PROACTIVE` is `"false"`, do not auto-invoke or proactively suggest skills. If a skill seems useful, ask: "I think /skillname might help here — want me to run it?" -If `SKILL_PREFIX` is `"true"`, the user has namespaced skill names. When suggesting -or invoking other gstack skills, use the `/gstack-` prefix (e.g., `/gstack-qa` instead -of `/qa`, `/gstack-ship` instead of `/ship`). Disk paths are unaffected — always use -`~/.claude/skills/gstack/[skill-name]/SKILL.md` for reading skill files. +If `SKILL_PREFIX` is `"true"`, suggest/invoke `/gstack-*` names. Disk paths stay `~/.claude/skills/gstack/[skill-name]/SKILL.md`. If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). -If output shows `JUST_UPGRADED ` AND `SPAWNED_SESSION` is NOT set: tell -the user "Running gstack v{to} (just updated!)" and then check for new features to -surface. For each per-feature marker below, if the marker file is missing AND the -feature is plausibly useful for this user, use AskUserQuestion to let them try it. -Fire once per feature per user, NOT once per upgrade. +If output shows `JUST_UPGRADED `: print "Running gstack v{to} (just updated!)". If `SPAWNED_SESSION` is true, skip feature discovery. -**In spawned sessions (`SPAWNED_SESSION` = "true"): SKIP feature discovery entirely.** -Just print "Running gstack v{to}" and continue. Orchestrators do not want interactive -prompts from sub-sessions. +Feature discovery, max one prompt per session: +- Missing `~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint`: AskUserQuestion for Continuous checkpoint auto-commits. If accepted, run `~/.claude/skills/gstack/bin/gstack-config set checkpoint_mode continuous`. Always touch marker. +- Missing `~/.claude/skills/gstack/.feature-prompted-model-overlay`: inform "Model overlays are active. MODEL_OVERLAY shows the patch." Always touch marker. -**Feature discovery markers and prompts** (one at a time, max one per session): +After upgrade prompts, continue workflow. -1. `~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint` → - Prompt: "Continuous checkpoint auto-commits your work as you go with `WIP:` prefix - so you never lose progress to a crash. Local-only by default — doesn't push - anywhere unless you turn that on. Want to try it?" - Options: A) Enable continuous mode, B) Show me first (print the section from - the preamble Continuous Checkpoint Mode), C) Skip. - If A: run `~/.claude/skills/gstack/bin/gstack-config set checkpoint_mode continuous`. - Always: `touch ~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint` +If `WRITING_STYLE_PENDING` is `yes`: ask once about writing style: -2. `~/.claude/skills/gstack/.feature-prompted-model-overlay` → - Inform only (no prompt): "Model overlays are active. `MODEL_OVERLAY: {model}` - shown in the preamble output tells you which behavioral patch is applied. - Override with `--model` when regenerating skills (e.g., `bun run gen:skill-docs - --model gpt-5.4`). Default is claude." - Always: `touch ~/.claude/skills/gstack/.feature-prompted-model-overlay` - -After handling JUST_UPGRADED (prompts done or skipped), continue with the skill -workflow. - -If `WRITING_STYLE_PENDING` is `yes`: You're on the first skill run after upgrading -to gstack v1. Ask the user once about the new default writing style. Use AskUserQuestion: - -> v1 prompts = simpler. Technical terms get a one-sentence gloss on first use, -> questions are framed in outcome terms, sentences are shorter. -> -> Keep the new default, or prefer the older tighter prose? +> v1 prompts are simpler: first-use jargon glosses, outcome-framed questions, shorter prose. Keep default or restore terse? Options: - A) Keep the new default (recommended — good writing helps everyone) @@ -191,27 +140,20 @@ rm -f ~/.gstack/.writing-style-prompt-pending touch ~/.gstack/.writing-style-prompted ``` -This only happens once. If `WRITING_STYLE_PENDING` is `no`, skip this entirely. +Skip if `WRITING_STYLE_PENDING` is `no`. -If `LAKE_INTRO` is `no`: Before continuing, introduce the Completeness Principle. -Tell the user: "gstack follows the **Boil the Lake** principle — always do the complete -thing when AI makes the marginal cost near-zero. Read more: https://garryslist.org/posts/boil-the-ocean" -Then offer to open the essay in their default browser: +If `LAKE_INTRO` is `no`: say "gstack follows the **Boil the Lake** principle — do the complete thing when AI makes marginal cost near-zero. Read more: https://garryslist.org/posts/boil-the-ocean" Offer to open: ```bash open https://garryslist.org/posts/boil-the-ocean touch ~/.gstack/.completeness-intro-seen ``` -Only run `open` if the user says yes. Always run `touch` to mark as seen. This only happens once. +Only run `open` if yes. Always run `touch`. -If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, -ask the user about telemetry. Use AskUserQuestion: +If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better! Community mode shares usage data (which skills you use, how long -> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. -> No code, file paths, or repo names are ever sent. -> Change anytime with `gstack-config set telemetry off`. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. Options: - A) Help gstack get better! (recommended) @@ -219,10 +161,9 @@ Options: If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` -If B: ask a follow-up AskUserQuestion: +If B: ask follow-up: -> How about anonymous mode? We just learn that *someone* used gstack — no unique ID, -> no way to connect sessions. Just a counter that helps us know if anyone's out there. +> Anonymous mode sends only aggregate usage, no unique ID. Options: - A) Sure, anonymous is fine @@ -236,14 +177,11 @@ Always run: touch ~/.gstack/.telemetry-prompted ``` -This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. +Skip if `TEL_PROMPTED` is `yes`. -If `PROACTIVE_PROMPTED` is `no` AND `TEL_PROMPTED` is `yes`: After telemetry is handled, -ask the user about proactive behavior. Use AskUserQuestion: +If `PROACTIVE_PROMPTED` is `no` AND `TEL_PROMPTED` is `yes`: ask once: -> gstack can proactively figure out when you might need a skill while you work — -> like suggesting /qa when you say "does this work?" or /investigate when you hit -> a bug. We recommend keeping this on — it speeds up every part of your workflow. +> Let gstack proactively suggest skills, like /qa for "does this work?" or /investigate for bugs? Options: - A) Keep it on (recommended) @@ -257,7 +195,7 @@ Always run: touch ~/.gstack/.proactive-prompted ``` -This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely. +Skip if `PROACTIVE_PROMPTED` is `yes`. If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`: Check if a CLAUDE.md file exists in the project root. If it does not exist, create it. @@ -265,8 +203,6 @@ Check if a CLAUDE.md file exists in the project root. If it does not exist, crea Use AskUserQuestion: > gstack works best when your project's CLAUDE.md includes skill routing rules. -> This tells Claude to use specialized workflows (like /ship, /investigate, /qa) -> instead of answering directly. It's a one-time addition, about 15 lines. Options: - A) Add routing rules to CLAUDE.md (recommended) @@ -278,63 +214,33 @@ If A: Append this section to the end of CLAUDE.md: ## Skill routing -When the user's request matches an available skill, invoke it via the Skill tool. The -skill has multi-step workflows, checklists, and quality gates that produce better -results than an ad-hoc answer. When in doubt, invoke the skill. A false positive is -cheaper than a false negative. +When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill. Key routing rules: -- Product ideas, "is this worth building", brainstorming → invoke /office-hours -- Strategy, scope, "think bigger", "what should we build" → invoke /plan-ceo-review -- Architecture, "does this design make sense" → invoke /plan-eng-review -- Design system, brand, "how should this look" → invoke /design-consultation -- Design review of a plan → invoke /plan-design-review -- Developer experience of a plan → invoke /plan-devex-review -- "Review everything", full review pipeline → invoke /autoplan -- Bugs, errors, "why is this broken", "wtf", "this doesn't work" → invoke /investigate -- Test the site, find bugs, "does this work" → invoke /qa (or /qa-only for report only) -- Code review, check the diff, "look at my changes" → invoke /review -- Visual polish, design audit, "this looks off" → invoke /design-review -- Developer experience audit, try onboarding → invoke /devex-review -- Ship, deploy, create a PR, "send it" → invoke /ship -- Merge + deploy + verify → invoke /land-and-deploy -- Configure deployment → invoke /setup-deploy -- Post-deploy monitoring → invoke /canary -- Update docs after shipping → invoke /document-release -- Weekly retro, "how'd we do" → invoke /retro -- Second opinion, codex review → invoke /codex -- Safety mode, careful mode, lock it down → invoke /careful or /guard -- Restrict edits to a directory → invoke /freeze or /unfreeze -- Upgrade gstack → invoke /gstack-upgrade -- Save progress, "save my work" → invoke /context-save -- Resume, restore, "where was I" → invoke /context-restore -- Security audit, OWASP, "is this secure" → invoke /cso -- Make a PDF, document, publication → invoke /make-pdf -- Launch real browser for QA → invoke /open-gstack-browser -- Import cookies for authenticated testing → invoke /setup-browser-cookies -- Performance regression, page speed, benchmarks → invoke /benchmark -- Review what gstack has learned → invoke /learn -- Tune question sensitivity → invoke /plan-tune -- Code quality dashboard → invoke /health +- Product ideas/brainstorming → invoke /office-hours +- Strategy/scope → invoke /plan-ceo-review +- Architecture → invoke /plan-eng-review +- Design system/plan review → invoke /design-consultation or /plan-design-review +- Full review pipeline → invoke /autoplan +- Bugs/errors → invoke /investigate +- QA/testing site behavior → invoke /qa or /qa-only +- Code review/diff check → invoke /review +- Visual polish → invoke /design-review +- Ship/deploy/PR → invoke /ship or /land-and-deploy +- Save progress → invoke /context-save +- Resume context → invoke /context-restore ``` Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"` -If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true` -Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill." +If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true` and say they can re-enable with `gstack-config set routing_declined false`. -This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +This only happens once per project. Skip if `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`. -If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at -`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies -up to date, so this project's gstack will fall behind. - -Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): +If `VENDORED_GSTACK` is `yes`, warn once via AskUserQuestion unless `~/.gstack/.vendoring-warned-$SLUG` exists: > This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. -> We won't keep this copy up to date, so you'll fall behind on new features and fixes. -> -> Want to migrate to team mode? It takes about 30 seconds. +> Migrate to team mode? Options: - A) Yes, migrate to team mode now @@ -355,7 +261,7 @@ eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || tru touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} ``` -This only happens once per project. If the marker file exists, skip entirely. +If marker exists, skip. If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: @@ -367,10 +273,6 @@ AI orchestrator (e.g., OpenClaw). In spawned sessions: ## GBrain Sync (skill start) ```bash -# gbrain-sync: drain pending writes, pull once per day. Silent no-op when -# the feature isn't initialized or gbrain_sync_mode is "off". See -# docs/gbrain-sync.md. - _GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}" _BRAIN_REMOTE_FILE="$HOME/.gstack-brain-remote.txt" _BRAIN_SYNC_BIN="~/.claude/skills/gstack/bin/gstack-brain-sync" @@ -378,7 +280,6 @@ _BRAIN_CONFIG_BIN="~/.claude/skills/gstack/bin/gstack-config" _BRAIN_SYNC_MODE=$("$_BRAIN_CONFIG_BIN" get gbrain_sync_mode 2>/dev/null || echo off) -# New-machine hint: URL file present, local .git missing, sync not yet enabled. if [ -f "$_BRAIN_REMOTE_FILE" ] && [ ! -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" = "off" ]; then _BRAIN_NEW_URL=$(head -1 "$_BRAIN_REMOTE_FILE" 2>/dev/null | tr -d '[:space:]') if [ -n "$_BRAIN_NEW_URL" ]; then @@ -387,9 +288,7 @@ if [ -f "$_BRAIN_REMOTE_FILE" ] && [ ! -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_S fi fi -# Active-sync path. if [ -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" != "off" ]; then - # Once-per-day pull. _BRAIN_LAST_PULL_FILE="$_GSTACK_HOME/.brain-last-pull" _BRAIN_NOW=$(date +%s) _BRAIN_DO_PULL=1 @@ -402,11 +301,9 @@ if [ -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" != "off" ]; then ( cd "$_GSTACK_HOME" && git fetch origin >/dev/null 2>&1 && git merge --ff-only "origin/$(git rev-parse --abbrev-ref HEAD)" >/dev/null 2>&1 ) || true echo "$_BRAIN_NOW" > "$_BRAIN_LAST_PULL_FILE" fi - # Drain pending queue, push. "$_BRAIN_SYNC_BIN" --once 2>/dev/null || true fi -# Status line — always emitted, easy to grep. if [ -d "$_GSTACK_HOME/.git" ] && [ "$_BRAIN_SYNC_MODE" != "off" ]; then _BRAIN_QUEUE_DEPTH=0 [ -f "$_GSTACK_HOME/.brain-queue.jsonl" ] && _BRAIN_QUEUE_DEPTH=$(wc -l < "$_GSTACK_HOME/.brain-queue.jsonl" | tr -d ' ') @@ -420,24 +317,16 @@ fi -**Privacy stop-gate (fires ONCE per machine).** +Privacy stop-gate: if output shows `BRAIN_SYNC: off`, `gbrain_sync_mode_prompted` is `false`, and gbrain is on PATH or `gbrain doctor --fast --json` works, ask once: -If the bash output shows `BRAIN_SYNC: off` AND the config value -`gbrain_sync_mode_prompted` is `false` AND gbrain is detected on this host -(either `gbrain doctor --fast --json` succeeds or the `gbrain` binary is in PATH), -fire a one-time privacy gate via AskUserQuestion: - -> gstack can publish your session memory (learnings, plans, designs, retros) to a -> private GitHub repo that GBrain indexes across your machines. Higher tiers -> include behavioral data (session timelines, developer profile). How much do you -> want to sync? +> gstack can publish your session memory to a private GitHub repo that GBrain indexes across machines. How much should sync? Options: -- A) Everything allowlisted (recommended — maximum cross-machine memory) -- B) Only artifacts (plans, designs, retros, learnings) — skip timelines and profile -- C) Decline — keep everything local +- A) Everything allowlisted (recommended) +- B) Only artifacts +- C) Decline, keep everything local -After the user answers, run (substituting the chosen value): +After answer: ```bash # Chosen mode: full | artifacts-only | off @@ -445,17 +334,9 @@ After the user answers, run (substituting the chosen value): "$_BRAIN_CONFIG_BIN" set gbrain_sync_mode_prompted true ``` -If A or B was chosen AND `~/.gstack/.git` doesn't exist, ask a follow-up: -"Set up the GBrain sync repo now? (runs `gstack-brain-init`)" -- A) Yes, run it now -- B) Show me the command, I'll run it myself +If A/B and `~/.gstack/.git` is missing, ask whether to run `gstack-brain-init`. Do not block the skill. -Do not block the skill. Emit the question, continue the skill workflow. The -next skill run picks up wherever this left off. - -**At skill END (before the telemetry block),** run these bash commands to -catch artifact writes (design docs, plans, retros) that skipped the writer -shims, plus drain any still-pending queue entries: +At skill END before telemetry: ```bash "~/.claude/skills/gstack/bin/gstack-brain-sync" --discover-new 2>/dev/null || true @@ -483,66 +364,38 @@ equivalents (cat, sed, find, grep). The dedicated tools are cheaper and clearer. ## Voice -**Tone:** direct, concrete, sharp, never corporate, never academic. Sound like a builder, not a consultant. Name the file, the function, the command. No filler, no throat-clearing. +Direct, concrete, builder-to-builder. Name the file, function, command, and user-visible impact. No filler. -**Writing rules:** No em dashes (use commas, periods, "..."). No AI vocabulary (delve, crucial, robust, comprehensive, nuanced, etc.). Short paragraphs. End with what to do. +No em dashes. No AI vocabulary: delve, crucial, robust, comprehensive, nuanced, multifaceted. Never corporate or academic. Short paragraphs. End with what to do. -The user always has context you don't. Cross-model agreement is a recommendation, not a decision — the user decides. +The user has context you do not. Cross-model agreement is a recommendation, not a decision. The user decides. ## Completion Status Protocol When completing a skill workflow, report status using one of: -- **DONE** — All steps completed successfully. Evidence provided for each claim. -- **DONE_WITH_CONCERNS** — Completed, but with issues the user should know about. List each concern. -- **BLOCKED** — Cannot proceed. State what is blocking and what was tried. -- **NEEDS_CONTEXT** — Missing information required to continue. State exactly what you need. +- **DONE** — completed with evidence. +- **DONE_WITH_CONCERNS** — completed, but list concerns. +- **BLOCKED** — cannot proceed; state blocker and what was tried. +- **NEEDS_CONTEXT** — missing info; state exactly what is needed. -### Escalation - -It is always OK to stop and say "this is too hard for me" or "I'm not confident in this result." - -Bad work is worse than no work. You will not be penalized for escalating. -- If you have attempted a task 3 times without success, STOP and escalate. -- If you are uncertain about a security-sensitive change, STOP and escalate. -- If the scope of work exceeds what you can verify, STOP and escalate. - -Escalation format: -``` -STATUS: BLOCKED | NEEDS_CONTEXT -REASON: [1-2 sentences] -ATTEMPTED: [what you tried] -RECOMMENDATION: [what the user should do next] -``` +Escalate after 3 failed attempts, uncertain security-sensitive changes, or scope you cannot verify. Format: `STATUS`, `REASON`, `ATTEMPTED`, `RECOMMENDATION`. ## Operational Self-Improvement -Before completing, reflect on this session: -- Did any commands fail unexpectedly? -- Did you take a wrong approach and have to backtrack? -- Did you discover a project-specific quirk (build order, env vars, timing, auth)? -- Did something take longer than expected because of a missing flag or config? - -If yes, log an operational learning for future sessions: +Before completing, if you discovered a durable project quirk or command fix that would save 5+ minutes next time, log it: ```bash ~/.claude/skills/gstack/bin/gstack-learnings-log '{"skill":"SKILL_NAME","type":"operational","key":"SHORT_KEY","insight":"DESCRIPTION","confidence":N,"source":"observed"}' ``` -Replace SKILL_NAME with the current skill name. Only log genuine operational discoveries. -Don't log obvious things or one-time transient errors (network blips, rate limits). -A good test: would knowing this save 5+ minutes in a future session? If yes, log it. +Do not log obvious facts or one-time transient errors. ## Telemetry (run last) -After the skill workflow completes (success, error, or abort), log the telemetry event. -Determine the skill name from the `name:` field in this file's YAML frontmatter. -Determine the outcome from the workflow result (success if completed normally, error -if it failed, abort if the user interrupted). +After workflow completion, log telemetry. Use skill `name:` from frontmatter. OUTCOME is success/error/abort/unknown. **PLAN MODE EXCEPTION — ALWAYS RUN:** This command writes telemetry to -`~/.gstack/analytics/` (user config directory, not project files). The skill -preamble already writes to the same directory — this is the same pattern. -Skipping this command loses session duration and outcome data. +`~/.gstack/analytics/`, matching preamble analytics writes. Run this bash: @@ -564,19 +417,11 @@ if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log fi ``` -Replace `SKILL_NAME` with the actual skill name from frontmatter, `OUTCOME` with -success/error/abort, and `USED_BROWSE` with true/false based on whether `$B` was used. -If you cannot determine the outcome, use "unknown". The local JSONL always logs. The -remote binary only runs if telemetry is not off and the binary exists. +Replace `SKILL_NAME`, `OUTCOME`, and `USED_BROWSE` before running. ## Plan Status Footer -In plan mode, before ExitPlanMode: if the plan file lacks a `## GSTACK REVIEW REPORT` -section, run `~/.claude/skills/gstack/bin/gstack-review-read` and append a report. -With JSONL entries (before `---CONFIG---`), format the standard runs/status/findings -table. With `NO_REVIEWS` or empty, append a 5-row placeholder table (CEO/Codex/Eng/ -Design/DX Review) with all zeros and verdict "NO REVIEWS YET — run `/autoplan`". -If a richer review report already exists, skip — review skills wrote it. +In plan mode before ExitPlanMode: if the plan file lacks `## GSTACK REVIEW REPORT`, run `~/.claude/skills/gstack/bin/gstack-review-read` and append the standard runs/status/findings table. With `NO_REVIEWS` or empty, append a 5-row placeholder with verdict "NO REVIEWS YET — run `/autoplan`". If a richer report exists, skip. PLAN MODE EXCEPTION — always allowed (it's the plan file). diff --git a/TODOS.md b/TODOS.md index eb2a5236..2ae36d3f 100644 --- a/TODOS.md +++ b/TODOS.md @@ -54,39 +54,6 @@ scope of that PR; deliberately deferred to keep PTY-import small. ## Testing -### Pre-existing test failures surfaced during v1.12.0.0 ship - -**What:** Two remaining test failures on bare main that have been shipping as-is for multiple versions. (The bearer-json secret-scan regression flagged here originally was a real leak path and has been fixed in this PR — see Completed section below.) - -1. `gstack-config gbrain keys > GSTACK_HOME overrides real config dir` (`test/brain-sync.test.ts:104`) — the GSTACK_HOME env override leaks into the real `~/.gstack/config.yaml`. Test asserts real config does NOT contain `gbrain_sync_mode: full` but it does. Either the test environment isn't isolated correctly or `bin/gstack-config` is writing to both locations. -2. `Opus 4.7 overlay — pacing directive > keeps Fan out / Effort-match / Literal interpretation nudges` (`test/model-overlay-opus-4-7.test.ts:87`) — v1.10.1.0 (#1166) removed the "Fan out explicitly" nudge from the overlay but the assertion was never updated. Either the nudge should come back (intentional removal reverted) or the test should be updated to match the new expected content. - -**Why:** Both have been green-washing through recent `/ship` runs via "pre-existing test failures skipped: ." #1 signals a real config isolation bug; #2 is a stale assertion since the overlay intentionally removed that nudge. - -**Priority:** P0 (both) - -**Effort:** S each. #1 likely a test harness fix in `test/brain-sync.test.ts`'s setup hook. #2 is a one-line test update OR a revert of #1166. - ---- - -### `security-bench-haiku-responses.json` is 27MB, violates the 2MB tracked-file gate - -**What:** `browse/test/fixtures/security-bench-haiku-responses.json` landed on main at v1.6.4.0 (PR #1135) at 27MB. The `no compiled binaries in git > git tracks no files larger than 2MB` gate in `test/skill-validation.test.ts:1623` fails on main and on every feature branch that merges main afterward. - -**Why:** The fixture is a legitimate CI replay corpus (real Haiku responses from the 500-case BrowseSafe-Bench) used to verify the ensemble classifier deterministically. But 13x over the 2MB limit means it will keep failing the validation test for every future ship. - -**Pros:** Removes a pre-existing failure that wastes a triage slot in every /ship run. - -**Cons:** Moving to git-lfs adds a dependency. Splitting into chunks risks breaking the bench test. External hosting adds a CI fetch step. - -**Context:** Noticed during workspace-aware-ship /ship on 2026-04-23 when the post-merge test suite flagged this single failure. Introduced on main in PR #1135 (`v1.6.4.0: cut Haiku classifier FP from 44% to 23%`), commit d75402bb. Two reasonable paths: (a) split into multiple ≤2MB chunks and load them in the bench test, (b) move to git-lfs. - -**Effort:** M (human: ~2-3h / CC: ~20 min) -**Priority:** P1 (not blocking ship, but every future /ship triages the same failure) -**Depends on:** nothing - ---- - ## P1: Structural STOP-Ask forcing function across all skills **What:** Design and implement a structural forcing function that catches when a skill mandates per-issue AskUserQuestion but the model silently substitutes batch-synthesis. Candidate mechanisms: question-count assertion (skill declares expected question count in frontmatter; post-run audit logs if model fired ` form (literal space after the scheme name) slipped past the scanner. Added an optional `(Bearer |Basic |Token )?` prefix to the pattern. Validated against 5 positive cases (including the regression fixture) + 3 negative cases (short tokens, non-secret keys, random JSON). The 7-pattern secret scanner now passes all fixtures including bearer-json. diff --git a/VERSION b/VERSION index 323a9cbc..0550662d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.14.0.0 +1.15.0.0 diff --git a/autoplan/SKILL.md b/autoplan/SKILL.md index 351e37f1..6a8ad3b2 100644 --- a/autoplan/SKILL.md +++ b/autoplan/SKILL.md @@ -58,19 +58,15 @@ _TEL_START=$(date +%s) _SESSION_ID="$$-$(date +%s)" echo "TELEMETRY: ${_TEL:-off}" echo "TEL_PROMPTED: $_TEL_PROMPTED" -# Writing style verbosity (V1: default = ELI10, terse = tighter V0 prose. -# Read on every skill run so terse mode takes effect without a restart.) _EXPLAIN_LEVEL=$(~/.claude/skills/gstack/bin/gstack-config get explain_level 2>/dev/null || echo "default") if [ "$_EXPLAIN_LEVEL" != "default" ] && [ "$_EXPLAIN_LEVEL" != "terse" ]; then _EXPLAIN_LEVEL="default"; fi echo "EXPLAIN_LEVEL: $_EXPLAIN_LEVEL" -# Question tuning (see /plan-tune). Observational only in V1. _QUESTION_TUNING=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false") echo "QUESTION_TUNING: $_QUESTION_TUNING" mkdir -p ~/.gstack/analytics if [ "$_TEL" != "off" ]; then echo '{"skill":"autoplan","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true fi -# zsh-compatible: use find instead of glob to avoid NOMATCH error for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do if [ -f "$_PF" ]; then if [ "$_TEL" != "off" ] && [ -x "~/.claude/skills/gstack/bin/gstack-telemetry-log" ]; then @@ -80,7 +76,6 @@ for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null fi break done -# Learnings count eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || true _LEARN_FILE="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}/learnings.jsonl" if [ -f "$_LEARN_FILE" ]; then @@ -92,9 +87,7 @@ if [ -f "$_LEARN_FILE" ]; then else echo "LEARNINGS: 0" fi -# Session timeline: record skill start (local-only, never sent anywhere) ~/.claude/skills/gstack/bin/gstack-timeline-log '{"skill":"autoplan","event":"started","branch":"'"$_BRANCH"'","session":"'"$_SESSION_ID"'"}' 2>/dev/null & -# Check if CLAUDE.md has routing rules _HAS_ROUTING="no" if [ -f CLAUDE.md ] && grep -q "## Skill routing" CLAUDE.md 2>/dev/null; then _HAS_ROUTING="yes" @@ -102,7 +95,6 @@ fi _ROUTING_DECLINED=$(~/.claude/skills/gstack/bin/gstack-config get routing_declined 2>/dev/null || echo "false") echo "HAS_ROUTING: $_HAS_ROUTING" echo "ROUTING_DECLINED: $_ROUTING_DECLINED" -# Vendoring deprecation: detect if CWD has a vendored gstack copy _VENDORED="no" if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then if [ -f ".claude/skills/gstack/VERSION" ] || [ -d ".claude/skills/gstack/.git" ]; then @@ -111,81 +103,38 @@ if [ -d ".claude/skills/gstack" ] && [ ! -L ".claude/skills/gstack" ]; then fi echo "VENDORED_GSTACK: $_VENDORED" echo "MODEL_OVERLAY: claude" -# Checkpoint mode (explicit = no auto-commit, continuous = WIP commits as you go) _CHECKPOINT_MODE=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_mode 2>/dev/null || echo "explicit") _CHECKPOINT_PUSH=$(~/.claude/skills/gstack/bin/gstack-config get checkpoint_push 2>/dev/null || echo "false") echo "CHECKPOINT_MODE: $_CHECKPOINT_MODE" echo "CHECKPOINT_PUSH: $_CHECKPOINT_PUSH" -# Detect spawned session (OpenClaw or other orchestrator) [ -n "$OPENCLAW_SESSION" ] && echo "SPAWNED_SESSION: true" || true ``` ## Plan Mode Safe Operations -In plan mode, these are always allowed (they inform the plan, don't modify source): -`$B` (browse), `$D` (design), `codex exec`/`codex review`, writes to `~/.gstack/`, -writes to the plan file, `open` for generated artifacts. +In plan mode, allowed because they inform the plan: `$B`, `$D`, `codex exec`/`codex review`, writes to `~/.gstack/`, writes to the plan file, and `open` for generated artifacts. ## Skill Invocation During Plan Mode -If the user invokes a skill in plan mode, that skill takes precedence over generic plan mode behavior. Treat it as executable instructions, not reference. Follow step -by step. AskUserQuestion calls satisfy plan mode's end-of-turn requirement. At a STOP -point, stop immediately. Do not continue the workflow past a STOP point and do not call ExitPlanMode there. Commands marked "PLAN -MODE EXCEPTION — ALWAYS RUN" execute. Other writes need to be already permitted -above or explicitly exception-marked. Call ExitPlanMode only after the skill -workflow completes — only then call ExitPlanMode (or if the user tells you to cancel the skill or leave plan mode). +If the user invokes a skill in plan mode, the skill takes precedence over generic plan mode behavior. **Treat the skill file as executable instructions, not reference.** Follow it step by step starting from Step 0; the first AskUserQuestion is the workflow entering plan mode, not a violation of it. AskUserQuestion satisfies plan mode's end-of-turn requirement. At a STOP point, stop immediately. Do not continue the workflow or call ExitPlanMode there. Commands marked "PLAN MODE EXCEPTION — ALWAYS RUN" execute. Call ExitPlanMode only after the skill workflow completes, or if the user tells you to cancel the skill or leave plan mode. -If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills AND do not -auto-invoke skills based on conversation context. Only run skills the user explicitly -types (e.g., /qa, /ship). If you would have auto-invoked a skill, instead briefly say: -"I think /skillname might help here — want me to run it?" and wait for confirmation. -The user opted out of proactive behavior. +If `PROACTIVE` is `"false"`, do not auto-invoke or proactively suggest skills. If a skill seems useful, ask: "I think /skillname might help here — want me to run it?" -If `SKILL_PREFIX` is `"true"`, the user has namespaced skill names. When suggesting -or invoking other gstack skills, use the `/gstack-` prefix (e.g., `/gstack-qa` instead -of `/qa`, `/gstack-ship` instead of `/ship`). Disk paths are unaffected — always use -`~/.claude/skills/gstack/[skill-name]/SKILL.md` for reading skill files. +If `SKILL_PREFIX` is `"true"`, suggest/invoke `/gstack-*` names. Disk paths stay `~/.claude/skills/gstack/[skill-name]/SKILL.md`. If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). -If output shows `JUST_UPGRADED ` AND `SPAWNED_SESSION` is NOT set: tell -the user "Running gstack v{to} (just updated!)" and then check for new features to -surface. For each per-feature marker below, if the marker file is missing AND the -feature is plausibly useful for this user, use AskUserQuestion to let them try it. -Fire once per feature per user, NOT once per upgrade. +If output shows `JUST_UPGRADED `: print "Running gstack v{to} (just updated!)". If `SPAWNED_SESSION` is true, skip feature discovery. -**In spawned sessions (`SPAWNED_SESSION` = "true"): SKIP feature discovery entirely.** -Just print "Running gstack v{to}" and continue. Orchestrators do not want interactive -prompts from sub-sessions. +Feature discovery, max one prompt per session: +- Missing `~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint`: AskUserQuestion for Continuous checkpoint auto-commits. If accepted, run `~/.claude/skills/gstack/bin/gstack-config set checkpoint_mode continuous`. Always touch marker. +- Missing `~/.claude/skills/gstack/.feature-prompted-model-overlay`: inform "Model overlays are active. MODEL_OVERLAY shows the patch." Always touch marker. -**Feature discovery markers and prompts** (one at a time, max one per session): +After upgrade prompts, continue workflow. -1. `~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint` → - Prompt: "Continuous checkpoint auto-commits your work as you go with `WIP:` prefix - so you never lose progress to a crash. Local-only by default — doesn't push - anywhere unless you turn that on. Want to try it?" - Options: A) Enable continuous mode, B) Show me first (print the section from - the preamble Continuous Checkpoint Mode), C) Skip. - If A: run `~/.claude/skills/gstack/bin/gstack-config set checkpoint_mode continuous`. - Always: `touch ~/.claude/skills/gstack/.feature-prompted-continuous-checkpoint` +If `WRITING_STYLE_PENDING` is `yes`: ask once about writing style: -2. `~/.claude/skills/gstack/.feature-prompted-model-overlay` → - Inform only (no prompt): "Model overlays are active. `MODEL_OVERLAY: {model}` - shown in the preamble output tells you which behavioral patch is applied. - Override with `--model` when regenerating skills (e.g., `bun run gen:skill-docs - --model gpt-5.4`). Default is claude." - Always: `touch ~/.claude/skills/gstack/.feature-prompted-model-overlay` - -After handling JUST_UPGRADED (prompts done or skipped), continue with the skill -workflow. - -If `WRITING_STYLE_PENDING` is `yes`: You're on the first skill run after upgrading -to gstack v1. Ask the user once about the new default writing style. Use AskUserQuestion: - -> v1 prompts = simpler. Technical terms get a one-sentence gloss on first use, -> questions are framed in outcome terms, sentences are shorter. -> -> Keep the new default, or prefer the older tighter prose? +> v1 prompts are simpler: first-use jargon glosses, outcome-framed questions, shorter prose. Keep default or restore terse? Options: - A) Keep the new default (recommended — good writing helps everyone) @@ -200,27 +149,20 @@ rm -f ~/.gstack/.writing-style-prompt-pending touch ~/.gstack/.writing-style-prompted ``` -This only happens once. If `WRITING_STYLE_PENDING` is `no`, skip this entirely. +Skip if `WRITING_STYLE_PENDING` is `no`. -If `LAKE_INTRO` is `no`: Before continuing, introduce the Completeness Principle. -Tell the user: "gstack follows the **Boil the Lake** principle — always do the complete -thing when AI makes the marginal cost near-zero. Read more: https://garryslist.org/posts/boil-the-ocean" -Then offer to open the essay in their default browser: +If `LAKE_INTRO` is `no`: say "gstack follows the **Boil the Lake** principle — do the complete thing when AI makes marginal cost near-zero. Read more: https://garryslist.org/posts/boil-the-ocean" Offer to open: ```bash open https://garryslist.org/posts/boil-the-ocean touch ~/.gstack/.completeness-intro-seen ``` -Only run `open` if the user says yes. Always run `touch` to mark as seen. This only happens once. +Only run `open` if yes. Always run `touch`. -If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: After the lake intro is handled, -ask the user about telemetry. Use AskUserQuestion: +If `TEL_PROMPTED` is `no` AND `LAKE_INTRO` is `yes`: ask telemetry once via AskUserQuestion: -> Help gstack get better! Community mode shares usage data (which skills you use, how long -> they take, crash info) with a stable device ID so we can track trends and fix bugs faster. -> No code, file paths, or repo names are ever sent. -> Change anytime with `gstack-config set telemetry off`. +> Help gstack get better. Share usage data only: skill, duration, crashes, stable device ID. No code, file paths, or repo names. Options: - A) Help gstack get better! (recommended) @@ -228,10 +170,9 @@ Options: If A: run `~/.claude/skills/gstack/bin/gstack-config set telemetry community` -If B: ask a follow-up AskUserQuestion: +If B: ask follow-up: -> How about anonymous mode? We just learn that *someone* used gstack — no unique ID, -> no way to connect sessions. Just a counter that helps us know if anyone's out there. +> Anonymous mode sends only aggregate usage, no unique ID. Options: - A) Sure, anonymous is fine @@ -245,14 +186,11 @@ Always run: touch ~/.gstack/.telemetry-prompted ``` -This only happens once. If `TEL_PROMPTED` is `yes`, skip this entirely. +Skip if `TEL_PROMPTED` is `yes`. -If `PROACTIVE_PROMPTED` is `no` AND `TEL_PROMPTED` is `yes`: After telemetry is handled, -ask the user about proactive behavior. Use AskUserQuestion: +If `PROACTIVE_PROMPTED` is `no` AND `TEL_PROMPTED` is `yes`: ask once: -> gstack can proactively figure out when you might need a skill while you work — -> like suggesting /qa when you say "does this work?" or /investigate when you hit -> a bug. We recommend keeping this on — it speeds up every part of your workflow. +> Let gstack proactively suggest skills, like /qa for "does this work?" or /investigate for bugs? Options: - A) Keep it on (recommended) @@ -266,7 +204,7 @@ Always run: touch ~/.gstack/.proactive-prompted ``` -This only happens once. If `PROACTIVE_PROMPTED` is `yes`, skip this entirely. +Skip if `PROACTIVE_PROMPTED` is `yes`. If `HAS_ROUTING` is `no` AND `ROUTING_DECLINED` is `false` AND `PROACTIVE_PROMPTED` is `yes`: Check if a CLAUDE.md file exists in the project root. If it does not exist, create it. @@ -274,8 +212,6 @@ Check if a CLAUDE.md file exists in the project root. If it does not exist, crea Use AskUserQuestion: > gstack works best when your project's CLAUDE.md includes skill routing rules. -> This tells Claude to use specialized workflows (like /ship, /investigate, /qa) -> instead of answering directly. It's a one-time addition, about 15 lines. Options: - A) Add routing rules to CLAUDE.md (recommended) @@ -287,63 +223,33 @@ If A: Append this section to the end of CLAUDE.md: ## Skill routing -When the user's request matches an available skill, invoke it via the Skill tool. The -skill has multi-step workflows, checklists, and quality gates that produce better -results than an ad-hoc answer. When in doubt, invoke the skill. A false positive is -cheaper than a false negative. +When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill. Key routing rules: -- Product ideas, "is this worth building", brainstorming → invoke /office-hours -- Strategy, scope, "think bigger", "what should we build" → invoke /plan-ceo-review -- Architecture, "does this design make sense" → invoke /plan-eng-review -- Design system, brand, "how should this look" → invoke /design-consultation -- Design review of a plan → invoke /plan-design-review -- Developer experience of a plan → invoke /plan-devex-review -- "Review everything", full review pipeline → invoke /autoplan -- Bugs, errors, "why is this broken", "wtf", "this doesn't work" → invoke /investigate -- Test the site, find bugs, "does this work" → invoke /qa (or /qa-only for report only) -- Code review, check the diff, "look at my changes" → invoke /review -- Visual polish, design audit, "this looks off" → invoke /design-review -- Developer experience audit, try onboarding → invoke /devex-review -- Ship, deploy, create a PR, "send it" → invoke /ship -- Merge + deploy + verify → invoke /land-and-deploy -- Configure deployment → invoke /setup-deploy -- Post-deploy monitoring → invoke /canary -- Update docs after shipping → invoke /document-release -- Weekly retro, "how'd we do" → invoke /retro -- Second opinion, codex review → invoke /codex -- Safety mode, careful mode, lock it down → invoke /careful or /guard -- Restrict edits to a directory → invoke /freeze or /unfreeze -- Upgrade gstack → invoke /gstack-upgrade -- Save progress, "save my work" → invoke /context-save -- Resume, restore, "where was I" → invoke /context-restore -- Security audit, OWASP, "is this secure" → invoke /cso -- Make a PDF, document, publication → invoke /make-pdf -- Launch real browser for QA → invoke /open-gstack-browser -- Import cookies for authenticated testing → invoke /setup-browser-cookies -- Performance regression, page speed, benchmarks → invoke /benchmark -- Review what gstack has learned → invoke /learn -- Tune question sensitivity → invoke /plan-tune -- Code quality dashboard → invoke /health +- Product ideas/brainstorming → invoke /office-hours +- Strategy/scope → invoke /plan-ceo-review +- Architecture → invoke /plan-eng-review +- Design system/plan review → invoke /design-consultation or /plan-design-review +- Full review pipeline → invoke /autoplan +- Bugs/errors → invoke /investigate +- QA/testing site behavior → invoke /qa or /qa-only +- Code review/diff check → invoke /review +- Visual polish → invoke /design-review +- Ship/deploy/PR → invoke /ship or /land-and-deploy +- Save progress → invoke /context-save +- Resume context → invoke /context-restore ``` Then commit the change: `git add CLAUDE.md && git commit -m "chore: add gstack skill routing rules to CLAUDE.md"` -If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true` -Say "No problem. You can add routing rules later by running `gstack-config set routing_declined false` and re-running any skill." +If B: run `~/.claude/skills/gstack/bin/gstack-config set routing_declined true` and say they can re-enable with `gstack-config set routing_declined false`. -This only happens once per project. If `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`, skip this entirely. +This only happens once per project. Skip if `HAS_ROUTING` is `yes` or `ROUTING_DECLINED` is `true`. -If `VENDORED_GSTACK` is `yes`: This project has a vendored copy of gstack at -`.claude/skills/gstack/`. Vendoring is deprecated. We will not keep vendored copies -up to date, so this project's gstack will fall behind. - -Use AskUserQuestion (one-time per project, check for `~/.gstack/.vendoring-warned-$SLUG` marker): +If `VENDORED_GSTACK` is `yes`, warn once via AskUserQuestion unless `~/.gstack/.vendoring-warned-$SLUG` exists: > This project has gstack vendored in `.claude/skills/gstack/`. Vendoring is deprecated. -> We won't keep this copy up to date, so you'll fall behind on new features and fixes. -> -> Want to migrate to team mode? It takes about 30 seconds. +> Migrate to team mode? Options: - A) Yes, migrate to team mode now @@ -364,7 +270,7 @@ eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 2>/dev/null || tru touch ~/.gstack/.vendoring-warned-${SLUG:-unknown} ``` -This only happens once per project. If the marker file exists, skip entirely. +If marker exists, skip. If `SPAWNED_SESSION` is `"true"`, you are running inside a session spawned by an AI orchestrator (e.g., OpenClaw). In spawned sessions: @@ -375,114 +281,38 @@ AI orchestrator (e.g., OpenClaw). In spawned sessions: ## AskUserQuestion Format -**ALWAYS follow this structure for every AskUserQuestion call. Every element is non-skippable. If you find yourself about to skip any of them, stop and back up.** - -### Required shape - -Every AskUserQuestion reads like a decision brief, not a bullet list: +Every AskUserQuestion is a decision brief and must be sent as tool_use, not prose. ``` D - +Project/branch/task: <1 short grounding sentence using _BRANCH> ELI10: - Stakes if we pick wrong: - Recommendation: because - Completeness: A=X/10, B=Y/10 (or: Note: options differ in kind, not coverage — no completeness score) - Pros / cons: - A)