diff --git a/.github/workflows/windows-smoke.yml b/.github/workflows/windows-smoke.yml index 525f50553..326a4b52a 100644 --- a/.github/workflows/windows-smoke.yml +++ b/.github/workflows/windows-smoke.yml @@ -6,7 +6,7 @@ name: Windows Smoke # 1. ./setup --host claude into an isolated HOME under Git Bash, with # gstack's own browser (Playwright Chromium). # 2. The core skills start: gstack-skill-start for /ship, /review, -# /qa-only, /investigate and /browse answers SKILL_START_PROTO: 1. +# /qa-only and /browse answers SKILL_START_PROTO: 1. # 3. /browse on gstack's own browser: the installed browse runs a piped # chain flow against a local page from an unrelated directory. # 4. The fastest Windows-safe free tests for those paths (list below). @@ -69,7 +69,7 @@ jobs: SMOKE_HOME="$RUNNER_TEMP/smoke-home" USERPROFILE="$(cygpath -w "$SMOKE_HOME")" export HOME="$SMOKE_HOME" USERPROFILE - for skill in ship review qa-only investigate browse; do + for skill in ship review qa-only browse; do set +e OUT=$(timeout 120 "$SMOKE_HOME/.claude/skills/gstack/bin/gstack-skill-start" --skill "$skill" --model claude 2>&1 < /dev/null) CODE=$? diff --git a/AGENTS.md b/AGENTS.md index 7a9f75c16..bbfdc843b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -28,7 +28,6 @@ CLIs (`gstack-ios-qa-daemon`, `gstack-ios-qa-mint`, `gstack-ios-qa-regen`) are i | `/test-audit` | build + review | | `/codex` | build + review | | `/claude-code` | build + review | -| `/investigate` | build + review | | `/verify` | build + review | | `/design-review` | build + review | | `/design-shotgun` | build + review | diff --git a/CLAUDE.md b/CLAUDE.md index dc9249489..83cd96c33 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -22,8 +22,7 @@ need most are `bun run test` (free acceptance) and `bun run eval:bg:pr` (paid, changed coverage). Retired names such as `test:evals` and `test:e2e` print their replacement and exit 1. -Paid evals require `ANTHROPIC_API_KEY`. Codex E2E tests (`test/codex-e2e.test.ts`, -`test/codex-e2e-sol-scope.test.ts`) use Codex's own auth — the hermetic runner copies +Paid evals require `ANTHROPIC_API_KEY`. Codex E2E tests (`test/codex-e2e.test.ts` and the other Codex files) use Codex's own auth — the hermetic runner copies only `auth.json` from `${CODEX_HOME:-~/.codex}` and pins `CODEX_HOME` in the child env — no `OPENAI_API_KEY` env var needed. @@ -550,7 +549,7 @@ Key routing rules: - Architecture → invoke /autoplan - Design system/plan review → invoke /design-consultation or /plan-design-review - Full review pipeline → invoke /autoplan -- Bugs/errors → invoke /investigate, unless one read-only check confirms the cause and a narrow fix (then fix it and run the focused test) +- Bugs/errors → fix them directly: one read-only check of the cause, or a focused failing test first when the cause is unclear; then fix it and run that test - QA/testing site behavior → invoke /qa or /qa-only - Code review/diff check → invoke /review - Visual polish → invoke /design-review diff --git a/NOTICE.md b/NOTICE.md index cf8a99447..68e9070eb 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -138,9 +138,8 @@ design is not a defect, missing context; independent agreement as the strongest text is carried; this entry is a courtesy. Also ideas, not text, from `pstack/skills/blast-radius`, `recall`, `benchmark-checklist`, `architect` (`references/design-red-flags.md`) and `principle-attack-the-premise` -(Garry, 2026-10-26: "Do it, send"): the one safety fact, the five-rung evidence ladder and -the shared-premise red flag in `investigate/sections/blast-radius.md.tmpl` and -`investigate/SKILL.md.tmpl`; the transcript-mining fallback and its status-tagged brief in +(Garry, 2026-10-26: "Do it, send"): the one safety fact and the five-rung evidence ladder in +/ship's Blast radius PR section (`ship/sections/pr-body.md.tmpl`); the transcript-mining fallback and its status-tagged brief in `context-restore/sections/transcripts.md.tmpl`; the seven questions in `benchmark/sections/vet-the-number.md.tmpl`; the second-shape comparison and the eight design red flags in Section 1 of `plan-eng-review/sections/review-sections.md.tmpl`; and the generated diff --git a/README.md b/README.md index d50b3570a..f1990a2ac 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,6 @@ Each skill feeds into the next. `/office-hours` writes a design doc that `/plan- | `/deslop-shared-libs` | **Shared Code Reviewer** | Find worthwhile shared-code extractions in recent work. Compares up to five opportunities and recommends the best three, with source evidence, reliability gains, and total code savings. Recommendations only. | | `/write-skill` | **Skill Editor** | Write a new agent skill or improve one without bloat. A five-step edit ladder (fix the tool first, add nothing the model already does, edit the line instead of appending, lowest level that reaches the agent, rule not story), `gstack-skill-bill` for size per level and growth since the last diet, a drift check that every gstack command and flag a skill names still exists, and full diets proven by a before/after/no-skill test. | | `/test-audit` | **Test Auditor** | Sweep existing tests for ones that cost more than they protect: source greps, duplicates, assertion-free probes, test-only exports. Every candidate carries an evidence card; report-only unless you approve a batch. | -| `/investigate` | **Debugger** | Systematic root-cause debugging. Iron Law: no fixes without investigation. Traces data flow, tests hypotheses, stops after 3 failed fixes. | | `/verify` | **Build Loop** | Run the repo's own checks (typecheck, lint, build) and the tests `/ship`'s pre-gate would pick for your change, fix what breaks, repeat until green, then hand off to `/ship`. Asks once before running repo commands; never calls a change green when no test covers it. | | `/design-review` | **Designer Who Codes** | Same audit as /plan-design-review, then fixes what it finds. Atomic commits, before/after screenshots. If you have impeccable installed, its engine runs first and every mechanical finding arrives tagged with its rule id. | | `/devex-review` | **DX Tester** | Live developer experience audit. Actually tests your onboarding: navigates docs, tries the getting started flow, times TTHW, triggers CLI errors the way an agent meets them (`gstack-error-capture`: no terminal, contained, redacted). Compares against `/plan-devex-review` scores — the boomerang that shows if your plan matched reality. | diff --git a/SKILL.md b/SKILL.md index dbc31bf86..d314cb816 100644 --- a/SKILL.md +++ b/SKILL.md @@ -178,8 +178,8 @@ Only run skills the user explicitly invokes. This preference persists across ses If `PROACTIVE` is `core` (default): **invoke the Skill tool** only for the four core skills when the user's request matches their rule below: `/office-hours`, `/review`, `/qa` and `/ship`. For every other rule, answer directly and do not suggest the skill; -the user types it. The browser step above and the **Failures** branches below follow the -same rule: under `core`, name `/browse` or `/investigate` for the user to type instead of +the user types it. The browser step above follows the +same rule: under `core`, name `/browse` for the user to type instead of invoking it. A skill the user names is always invoked, whatever this setting says, and a step inside a skill that is already running (a handoff, a review gate, a subagent it dispatches) is part of that skill, not a suggestion. @@ -199,7 +199,7 @@ quality gates that produce better results than answering inline. - User asks to review design of a plan → invoke `/plan-design-review` - User asks about developer experience of a plan, API/CLI/SDK design → invoke `/plan-devex-review` - User wants all reviews done automatically, "review everything" → invoke `/autoplan` -- User reports a bug, error, failing test or broken behavior, "why is this broken", "this doesn't work", "wtf", "something's wrong" → check the cause by **Failures** below, then fix it or invoke `/investigate` +- User reports a bug, error, failing test or broken behavior, "why is this broken", "this doesn't work", "wtf", "something's wrong" → check the cause by **Failures** below, then fix it test-first - User asks to test the site, find bugs, QA, "does this work", "check the deploy" → invoke `/qa` - User asks to just report bugs without fixing → invoke `/qa-only` - User asks to review code, check the diff, pre-landing review, "look at my changes" → invoke `/review` @@ -259,10 +259,10 @@ Generic “second opinion”, “outside review”, or “cross-model review” - Voice trigger "quality check", "test the app", "run QA" → invoke `/qa` - Voice trigger "bug report", "just check for bugs" → invoke `/qa-only` -**Failures: check the cause, then route.** A failing test, error or stack trace shows where a failure surfaces, not its root cause. +**Failures: check the cause, then fix it test-first.** A failing test, error or stack trace shows where a failure surfaces, not its root cause. -- **Cause supplied** (the user names a specific cause and a narrow fix): make one read-only check of the evidence it rests on, such as the named file, test or log. If that confirms the cause, make the fix and run the focused test that covers it. If it does not, invoke `/investigate` before any further tracing or edit. -- **Cause unknown:** make one read-only triage pass over the supplied evidence and the directly implicated test and code. Do not reproduce, test hypotheses or search wider. If that pass establishes the root cause and a narrow fix, make the fix and run the focused test that covers it. If the cause, scope or intended behavior is still unclear, invoke `/investigate` before any broader diagnosis or edit. +- **Cause supplied** (the user names a specific cause and a narrow fix): make one read-only check of the evidence it rests on, such as the named file, test or log. If that confirms the cause, make the fix and run the focused test that covers it. If it does not, leave the suggested fix alone: reproduce the failure with a focused failing test, trace it to the root cause, fix that and rerun the test. +- **Cause unknown:** make one read-only triage pass over the supplied evidence and the directly implicated test and code. Do not reproduce, test hypotheses or search wider. If that pass establishes the root cause and a narrow fix, make the fix and run the focused test that covers it. If the cause or scope is still unclear, write or run a focused test that fails on the bug before any edit, trace the failure to its root cause, fix that and rerun the test. If the intended behavior is unclear, ask the user. **Route only to skills in your available-skills list.** A rule above whose skill is not listed there is turned off on this install; skip it, never try to invoke it. diff --git a/SKILL.md.tmpl b/SKILL.md.tmpl index f354a83be..58e44e0a2 100644 --- a/SKILL.md.tmpl +++ b/SKILL.md.tmpl @@ -54,8 +54,8 @@ Only run skills the user explicitly invokes. This preference persists across ses If `PROACTIVE` is `core` (default): **invoke the Skill tool** only for the four core skills when the user's request matches their rule below: `/office-hours`, `/review`, `/qa` and `/ship`. For every other rule, answer directly and do not suggest the skill; -the user types it. The browser step above and the **Failures** branches below follow the -same rule: under `core`, name `/browse` or `/investigate` for the user to type instead of +the user types it. The browser step above follows the +same rule: under `core`, name `/browse` for the user to type instead of invoking it. A skill the user names is always invoked, whatever this setting says, and a step inside a skill that is already running (a handoff, a review gate, a subagent it dispatches) is part of that skill, not a suggestion. @@ -75,7 +75,7 @@ quality gates that produce better results than answering inline. - User asks to review design of a plan → invoke `/plan-design-review` - User asks about developer experience of a plan, API/CLI/SDK design → invoke `/plan-devex-review` - User wants all reviews done automatically, "review everything" → invoke `/autoplan` -- User reports a bug, error, failing test or broken behavior, "why is this broken", "this doesn't work", "wtf", "something's wrong" → check the cause by **Failures** below, then fix it or invoke `/investigate` +- User reports a bug, error, failing test or broken behavior, "why is this broken", "this doesn't work", "wtf", "something's wrong" → check the cause by **Failures** below, then fix it test-first - User asks to test the site, find bugs, QA, "does this work", "check the deploy" → invoke `/qa` - User asks to just report bugs without fixing → invoke `/qa-only` - User asks to review code, check the diff, pre-landing review, "look at my changes" → invoke `/review` @@ -115,10 +115,10 @@ quality gates that produce better results than answering inline. - User asks to tune question sensitivity, "stop asking me that" → invoke `/plan-tune` - User asks for code quality dashboard, "health check" → invoke `/health`{{VOICE_TRIGGER_ROUTING}} -**Failures: check the cause, then route.** A failing test, error or stack trace shows where a failure surfaces, not its root cause. +**Failures: check the cause, then fix it test-first.** A failing test, error or stack trace shows where a failure surfaces, not its root cause. -- **Cause supplied** (the user names a specific cause and a narrow fix): make one read-only check of the evidence it rests on, such as the named file, test or log. If that confirms the cause, make the fix and run the focused test that covers it. If it does not, invoke `/investigate` before any further tracing or edit. -- **Cause unknown:** make one read-only triage pass over the supplied evidence and the directly implicated test and code. Do not reproduce, test hypotheses or search wider. If that pass establishes the root cause and a narrow fix, make the fix and run the focused test that covers it. If the cause, scope or intended behavior is still unclear, invoke `/investigate` before any broader diagnosis or edit. +- **Cause supplied** (the user names a specific cause and a narrow fix): make one read-only check of the evidence it rests on, such as the named file, test or log. If that confirms the cause, make the fix and run the focused test that covers it. If it does not, leave the suggested fix alone: reproduce the failure with a focused failing test, trace it to the root cause, fix that and rerun the test. +- **Cause unknown:** make one read-only triage pass over the supplied evidence and the directly implicated test and code. Do not reproduce, test hypotheses or search wider. If that pass establishes the root cause and a narrow fix, make the fix and run the focused test that covers it. If the cause or scope is still unclear, write or run a focused test that fails on the bug before any edit, trace the failure to its root cause, fix that and rerun the test. If the intended behavior is unclear, ask the user. **Route only to skills in your available-skills list.** A rule above whose skill is not listed there is turned off on this install; skip it, never try to invoke it. diff --git a/TODOS.md b/TODOS.md index d8f46ebdd..ef2d092e8 100644 --- a/TODOS.md +++ b/TODOS.md @@ -4182,26 +4182,6 @@ Shipped as `/careful`, `/freeze`, `/guard`, and `/unfreeze` in v0.6.5. Includes Shipped in v0.6.5. TemplateContext in gen-skill-docs.ts bakes skill name into preamble telemetry line. Analytics CLI (`bun run analytics`) for querying. /retro integration shows skills-used-this-week. -### /investigate scoped debugging enhancements (gated on telemetry) - -**What:** Six enhancements to /investigate auto-freeze, contingent on telemetry showing the freeze hook actually fires in real debugging sessions. - -**Why:** /investigate v0.7.1 auto-freezes edits to the module being debugged. If telemetry shows the hook fires often, these enhancements make the experience smarter. If it never fires, the problem wasn't real and these aren't worth building. - -**Context:** All items are prose additions to `investigate/SKILL.md.tmpl`. No new scripts. - -**Items:** -1. Stack trace auto-detection for freeze directory (parse deepest app frame) -2. Freeze boundary widening (ask to widen instead of hard-block when hitting boundary) -3. Post-fix auto-unfreeze + full test suite run -4. Debug instrumentation cleanup (tag with DEBUG-TEMP, remove before commit) -5. Debug session persistence (~/.gstack/investigate-sessions/ — save investigation for reuse) -6. Investigation timeline in debug report (hypothesis log with timing) - -**Effort:** M (all 6 combined) -**Priority:** P3 -**Depends on:** Telemetry data showing freeze hook fires in real /investigate sessions - ## Context Intelligence ### Context recovery preamble diff --git a/bin/gstack-artifacts-init b/bin/gstack-artifacts-init index 7edeef8bc..0b02526ea 100755 --- a/bin/gstack-artifacts-init +++ b/bin/gstack-artifacts-init @@ -1,7 +1,7 @@ #!/usr/bin/env bash # gstack-artifacts-init — set up ~/.gstack/ as a git repo synced to a private # git host (GitHub or GitLab) so a remote gbrain can ingest your artifacts -# (CEO plans, designs, /investigate reports) as a federated source. +# (CEO plans, designs, retros) as a federated source. # # Replaces gstack-brain-init in v1.27.0.0 (per D4 hard-delete; no compat # shim). Existing users are migrated by gstack-upgrade/migrations/v1.27.0.0.sh. diff --git a/bin/gstack-codex-probe b/bin/gstack-codex-probe index 981ae1499..9f0a4f115 100755 --- a/bin/gstack-codex-probe +++ b/bin/gstack-codex-probe @@ -837,7 +837,7 @@ _gstack_codex_log_event() { _gstack_codex_log_hang() { # Invoked when a codex invocation times out (exit 124). Records an - # operational learning so future /investigate sessions surface the pattern. + # operational learning so future debugging sessions surface the pattern. # Best-effort: errors swallowed. local _mode="${1:-unknown}" local _prompt_size="${2:-0}" diff --git a/bin/gstack-config b/bin/gstack-config index 3c1bfee96..d89934cf7 100755 --- a/bin/gstack-config +++ b/bin/gstack-config @@ -347,7 +347,6 @@ lookup_default() { timeline_stop_hook) echo "yes" ;; # yes | no — controls ./setup timeline Stop hook registration (#2677) skill_routing) bun --no-env-file --config="${GSTACK_BUN_CONFIG:-/dev/null}" "$(dirname "$0")/gstack-skill-route" --default skill_routing ;; # on | off — the skill-routing UserPromptSubmit hook; default: SWITCH_DEFAULT in lib/skill-route.ts (docs/skill-routing-and-contracts.md) skill_contracts) bun --no-env-file --config="${GSTACK_BUN_CONFIG:-/dev/null}" "$(dirname "$0")/gstack-skill-route" --default skill_contracts ;; # on | off — the skill-contract Stop hook; default: SWITCH_DEFAULT in lib/skill-route.ts (docs/skill-routing-and-contracts.md) - investigate_gate) bun --no-env-file --config="${GSTACK_BUN_CONFIG:-/dev/null}" "$(dirname "$0")/gstack-skill-route" --default investigate_gate ;; # on | off — the /investigate enforcement hooks; default: SWITCH_DEFAULT in lib/skill-route.ts (docs/skill-gates.md) ceo_review_gate) bun --no-env-file --config="${GSTACK_BUN_CONFIG:-/dev/null}" "$(dirname "$0")/gstack-skill-route" --default ceo_review_gate ;; # on | off — the /plan-ceo-review enforcement hooks; default: SWITCH_DEFAULT in lib/skill-route.ts (docs/skill-gates.md) autoplan_gate) bun --no-env-file --config="${GSTACK_BUN_CONFIG:-/dev/null}" "$(dirname "$0")/gstack-skill-route" --default autoplan_gate ;; # on | off — the /autoplan stop gate; default: SWITCH_DEFAULT in lib/skill-route.ts (docs/skill-gates.md) @@ -1146,7 +1145,7 @@ $_gstack_legacy_root" ship_measure_max_rounds ship_measure_sweep_cases ship_measure_sweep_budget_usd ship_rerun_backend test_backend \ plan_review_tier implementation_tier model_frontier_claude model_frontier_openai \ model_smart_claude model_smart_openai codex_effort review_effort_tiers agent_model harness_start_hook \ - skill_routing skill_contracts investigate_gate ceo_review_gate autoplan_gate; do + skill_routing skill_contracts ceo_review_gate autoplan_gate; do gstack_config_select "$KEY" VALUE="$_gstack_cfg_value" SOURCE="default" @@ -1172,7 +1171,7 @@ $_gstack_legacy_root" ship_measure_max_rounds ship_measure_sweep_cases ship_measure_sweep_budget_usd ship_rerun_backend test_backend \ plan_review_tier implementation_tier model_frontier_claude model_frontier_openai \ model_smart_claude model_smart_openai codex_effort review_effort_tiers agent_model harness_start_hook \ - skill_routing skill_contracts investigate_gate ceo_review_gate autoplan_gate; do + skill_routing skill_contracts ceo_review_gate autoplan_gate; do printf ' %-24s %s\n' "$KEY:" "$(lookup_default "$KEY")" done ;; diff --git a/bin/gstack-doctor-components.sh b/bin/gstack-doctor-components.sh index 3b8d2def1..c76cbb652 100644 --- a/bin/gstack-doctor-components.sh +++ b/bin/gstack-doctor-components.sh @@ -8,7 +8,7 @@ GSTACK_DOCTOR_COMPONENTS="claude codex patch codex-cli browse-bundle browser cso GSTACK_DOCTOR_CORE="runtime pins state-root privacy cores revision floor wrapper" GSTACK_DOCTOR_HOSTS="claude codex" GSTACK_DOCTOR_OPTIONAL="codex-cli cso" -GSTACK_DOCTOR_SKILLS="autoplan plan-ceo-review plan-design-review plan-devex-review plan-tune office-hours spec review ship investigate codex cso browse qa qa-only design-review design-consultation design-html design-motion devex-review scrape skillify canary benchmark make-pdf diagram pair-agent open-gstack-browser setup-browser-cookies connect-chrome land-and-deploy benchmark-models captain careful claude-code context-restore context-save design-shotgun deslop-shared-libs document-generate document-release eval-plan freeze gstack-upgrade guard health ios-clean ios-design-review ios-fix ios-qa ios-sync landing-report learn write-skill modernize retro runaway-stopper setup-deploy setup-gbrain sync-gbrain test-audit ubicloud unfreeze verify review-voice" +GSTACK_DOCTOR_SKILLS="autoplan plan-ceo-review plan-design-review plan-devex-review plan-tune office-hours spec review ship codex cso browse qa qa-only design-review design-consultation design-html design-motion devex-review scrape skillify canary benchmark make-pdf diagram pair-agent open-gstack-browser setup-browser-cookies connect-chrome land-and-deploy benchmark-models captain careful claude-code context-restore context-save design-shotgun deslop-shared-libs document-generate document-release eval-plan freeze gstack-upgrade guard health ios-clean ios-design-review ios-fix ios-qa ios-sync landing-report learn write-skill modernize retro runaway-stopper setup-deploy setup-gbrain sync-gbrain test-audit ubicloud unfreeze verify review-voice" GSTACK_BROWSER_LAZY_REASON="lazy; gstack-browser-ensure installs on first use" GSTACK_DOCTOR_HELPERS="gstack-pregate gstack-plan-reality gstack-artifact gstack-autoplan" GSTACK_DOCTOR_NOT_NEEDED="none" @@ -65,7 +65,7 @@ gstack_doctor_component_kind() { # gstack_doctor_skill_requires SKILL — the non-core ids the skill needs; returns 1 for an unknown skill. gstack_doctor_skill_requires() { case "$1" in - autoplan|plan-ceo-review|plan-design-review|plan-devex-review|plan-tune|office-hours|spec|review|ship|investigate) printf '%s' 'claude codex patch' ;; + autoplan|plan-ceo-review|plan-design-review|plan-devex-review|plan-tune|office-hours|spec|review|ship) printf '%s' 'claude codex patch' ;; codex) printf '%s' 'claude codex patch codex-cli' ;; cso) printf '%s' 'claude cso' ;; browse|qa|qa-only|design-review|design-consultation|design-html|design-motion|devex-review|scrape|skillify|canary|benchmark|make-pdf|diagram|pair-agent|open-gstack-browser|setup-browser-cookies|connect-chrome|land-and-deploy) printf '%s' 'claude codex patch browse-bundle browser' ;; @@ -78,7 +78,7 @@ gstack_doctor_skill_requires() { # gstack_doctor_skill_optional SKILL — ids that SKIP when absent; returns 1 for an unknown skill. gstack_doctor_skill_optional() { case "$1" in - autoplan|plan-ceo-review|plan-design-review|plan-devex-review|plan-tune|office-hours|spec|review|ship|investigate) printf '%s' 'codex-cli' ;; + autoplan|plan-ceo-review|plan-design-review|plan-devex-review|plan-tune|office-hours|spec|review|ship) printf '%s' 'codex-cli' ;; codex) printf '%s' '' ;; cso) printf '%s' '' ;; browse|qa|qa-only|design-review|design-consultation|design-html|design-motion|devex-review|scrape|skillify|canary|benchmark|make-pdf|diagram|pair-agent|open-gstack-browser|setup-browser-cookies|connect-chrome|land-and-deploy) printf '%s' 'codex-cli' ;; diff --git a/bin/gstack-hook-check b/bin/gstack-hook-check index 63f5e5eb5..1a0b9961b 100755 --- a/bin/gstack-hook-check +++ b/bin/gstack-hook-check @@ -15,7 +15,7 @@ # `_hook_command_path ` argument in /setup (or the setup named by # --setup, when another tree's setup registers 's hooks), plus every # gstack path on a `command:` line in a skill's frontmatter (/*/SKILL.md: -# /autoplan, /careful, /freeze, /guard, /investigate, /plan-ceo-review). Claude +# /autoplan, /careful, /freeze, /guard, /plan-ceo-review). Claude # Code runs those while the skill is active, and a broken PreToolUse one blocks # the tool call. test/setup-hook-syntax-gate.test.ts keeps the lists equal. # diff --git a/bin/gstack-install-registry.sh b/bin/gstack-install-registry.sh index 282387789..9e6b46804 100644 --- a/bin/gstack-install-registry.sh +++ b/bin/gstack-install-registry.sh @@ -342,7 +342,8 @@ gstack_disabled_normalize() { # GSTACK_RETIRED_SKILLS — skills gstack keeps off every host whatever # disabled_skills says; an existing gstack-owned entry is removed like a -# disabled one. The source stays for the skill that reads it: plan-eng-review -# is /autoplan's eng-phase methodology (GST-1698). Same list as RETIRED_SKILLS -# in scripts/discover-skills.ts (test/retired-skills.test.ts). -GSTACK_RETIRED_SKILLS="plan-eng-review" +# disabled one. plan-eng-review's source stays as /autoplan's eng-phase +# methodology (GST-1698); investigate's source is gone (GST-1717), and its old +# entries are still removed. Same list as RETIRED_SKILLS in +# scripts/discover-skills.ts (test/retired-skills.test.ts). +GSTACK_RETIRED_SKILLS="plan-eng-review investigate" diff --git a/bin/gstack-relink b/bin/gstack-relink index 7479a46b3..a41fcfc52 100755 --- a/bin/gstack-relink +++ b/bin/gstack-relink @@ -255,23 +255,29 @@ _cleanup_skill_entry() { _report_foreign "$entry" return 0 fi + if [ -n "$skill" ]; then + src="$INSTALL_DIR/$skill/SKILL.md"; [ -f "$RENDER_DIR/$skill/SKILL.md" ] && src="$RENDER_DIR/$skill/SKILL.md" + fi if [ -L "$entry" ]; then rm -f "$entry" elif [ -d "$entry" ]; then # Whole-directory removal needs proof that nothing of the user's is inside: - # the marker (we created the dir) or a directory holding nothing but links. - if [ -f "$entry/.gstack-owned" ] || { [ -L "$entry/SKILL.md" ] && _dir_only_links "$entry"; }; then + # the marker (we created the dir) while the skill's source still exists, or + # a directory holding nothing but links. A retired skill's marked entry + # takes the path below, so a real SKILL.md is backed up and the user's + # files stay. + if { [ -f "$entry/.gstack-owned" ] && { [ -z "$skill" ] || [ -f "$src" ]; }; } || { [ -L "$entry/SKILL.md" ] && _dir_only_links "$entry"; }; then rm -rf "$entry" else # Otherwise only what is ours goes: the SKILL.md, the marker, and our # runtime-asset links. The user's files stay, and so does the directory # if it is not empty afterwards. A real SKILL.md that differs from our - # source (raw, or with its name: rewritten to the entry name) is a - # customized file: moved to the backup root, never deleted. + # source (raw, or with its name: rewritten to the entry name), or one + # whose source is gone (a retired skill), is treated as customized: + # moved to the backup root, never deleted. if [ -f "$entry/SKILL.md" ] && [ ! -L "$entry/SKILL.md" ] && [ -n "$skill" ]; then - src="$INSTALL_DIR/$skill/SKILL.md"; [ -f "$RENDER_DIR/$skill/SKILL.md" ] && src="$RENDER_DIR/$skill/SKILL.md" - if [ -f "$src" ] && ! cmp -s "$entry/SKILL.md" "$src" \ - && ! sed "1,/^---\$/ s/^name:[[:space:]].*/name: ${entry##*/}/" "$src" | cmp -s - "$entry/SKILL.md"; then + if [ ! -f "$src" ] || { ! cmp -s "$entry/SKILL.md" "$src" \ + && ! sed "1,/^---\$/ s/^name:[[:space:]].*/name: ${entry##*/}/" "$src" | cmp -s - "$entry/SKILL.md"; }; then if ! _backup_skill_md "$entry/SKILL.md" "${entry##*/}"; then echo " kept ${entry##*/}/SKILL.md: could not back up the customized file — left untouched" >&2 return 0 @@ -454,6 +460,12 @@ for skill_dir in "$INSTALL_DIR"/*/; do fi SKILL_COUNT=$((SKILL_COUNT + 1)) done +# A retired skill whose source is gone (investigate, GST-1717) never reaches +# the loop above, so its old gstack-owned entries go here, plain and prefixed. +for skill in ${GSTACK_RETIRED_SKILLS:-}; do + _cleanup_skill_entry "$SKILLS_DIR/$skill" "$skill" + _cleanup_skill_entry "$SKILLS_DIR/gstack-$skill" "$skill" +done # Patch SKILL.md name: fields to match prefix setting. When a gbrain render # is active the loop above links SKILL.md from RENDER_DIR — the file the host diff --git a/bin/gstack-skill-gates b/bin/gstack-skill-gates index 3e41e75ff..f9cd27eb0 100755 --- a/bin/gstack-skill-gates +++ b/bin/gstack-skill-gates @@ -2,9 +2,9 @@ /** * gstack-skill-gates — the opt-in enforcement hooks' helper (GST-1461). * - * gstack-skill-gates settings [--investigate] [--ceo-review] [--autoplan] + * gstack-skill-gates settings [--ceo-review] [--autoplan] * Print a Claude Code settings JSON that registers the named gates (the - * /investigate gate, the /plan-ceo-review gate, the /autoplan gate) and turns their switches on, for \`claude -p --settings \` or the Agent + * /plan-ceo-review gate, the /autoplan gate) and turns their switches on, for \`claude -p --settings \` or the Agent * SDK's settings, so a headless run needs no settings.json edit. * gstack-skill-gates hook-timeout * Print the hook timeout in seconds (HOOK_TIMEOUT_S) that ./setup registers. @@ -23,18 +23,8 @@ import { HOOK_TIMEOUT_S, gatesRoot, readGateRecord, recordPath, type GateHook, t const ROOT = path.resolve(import.meta.dir, '..'); /** Claude Code runs a hook command through a shell: quote the path as gstack-settings-hook's gsQuoteCmd does. */ const shellCommand = (p: string) => (/[\s$`"\\]/.test(p) ? `"${p.replace(/[\\"$`]/g, ch => `\\${ch}`)}"` : p); -const USAGE = 'Usage: gstack-skill-gates settings [--investigate] [--ceo-review] [--autoplan]\n gstack-skill-gates hook-timeout\n gstack-skill-gates report [--session ] [--expect armed] [--json]'; +const USAGE = 'Usage: gstack-skill-gates settings [--ceo-review] [--autoplan]\n gstack-skill-gates hook-timeout\n gstack-skill-gates report [--session ] [--expect armed] [--json]'; const HOOKS: Record }> = { - '--investigate': { - env: 'GSTACK_INVESTIGATE_GATE', - source: 'investigate-gate', - events: [ - { event: 'PreToolUse', shim: 'investigate-gate-pre', matcher: 'Edit|Write|MultiEdit|NotebookEdit|Bash' }, - { event: 'PostToolUse', shim: 'investigate-gate-post', matcher: 'Bash' }, - { event: 'PostToolUseFailure', shim: 'investigate-gate-post-failure', matcher: 'Bash' }, - { event: 'Stop', shim: 'investigate-gate-stop' }, - ], - }, '--ceo-review': { env: 'GSTACK_CEO_REVIEW_GATE', source: 'ceo-review-gate', diff --git a/bin/gstack-skill-route b/bin/gstack-skill-route index 00366c5d0..fc8fed2a8 100755 --- a/bin/gstack-skill-route +++ b/bin/gstack-skill-route @@ -4,7 +4,7 @@ * * Usage: * gstack-skill-route --explain "" [--json] [--threshold ] - * gstack-skill-route --default + * gstack-skill-route --default * * Prints the switch state, the threshold, every candidate skill with its * score and best trigger, the route the hook would take and the exact line it @@ -16,13 +16,13 @@ import { ROUTE_THRESHOLD, SWITCH_DEFAULT, hintLine, installedSkillsDir, readSkillSwitch, routePrompt, scanInstalledSkills, } from '../lib/skill-route'; -const USAGE = 'Usage: gstack-skill-route --explain "" [--json] [--threshold ]\n gstack-skill-route --default '; +const USAGE = 'Usage: gstack-skill-route --explain "" [--json] [--threshold ]\n gstack-skill-route --default '; const args = process.argv.slice(2); if (args[0] === '--help' || args[0] === '-h') { process.stdout.write(`${USAGE}\n`); process.exit(0); } if (args[0] === '--default') { const name = args[1] ?? ''; if (!Object.hasOwn(SWITCH_DEFAULT, name)) { - process.stderr.write(`SKILL_ROUTE_USAGE: --default needs skill_routing, skill_contracts, investigate_gate, ceo_review_gate or autoplan_gate\n${USAGE}\n`); + process.stderr.write(`SKILL_ROUTE_USAGE: --default needs skill_routing, skill_contracts, ceo_review_gate or autoplan_gate\n${USAGE}\n`); process.exit(2); } process.stdout.write(`${SWITCH_DEFAULT[name as keyof typeof SWITCH_DEFAULT]}\n`); diff --git a/bin/gstack-skill-start b/bin/gstack-skill-start index 2daa50d8b..825a5d0cb 100755 --- a/bin/gstack-skill-start +++ b/bin/gstack-skill-start @@ -677,7 +677,7 @@ EOR ) _routing_offer() { if [ "$_PROACTIVE" = "core" ]; then - _GSTACK_CORE_RULES="$_CORE_ROUTING_RULES" awk '/^- .* → invoke \// { if (!done) { print ENVIRON["_GSTACK_CORE_RULES"]; done = 1 } next } { print }' + _GSTACK_CORE_RULES="$_CORE_ROUTING_RULES" awk '/^- .* → / { if (!done) { print ENVIRON["_GSTACK_CORE_RULES"]; done = 1 } next } { print }' else cat fi | sed "s|__BIN__|$_BIN|g" @@ -702,7 +702,7 @@ Key routing rules: - Architecture → invoke /autoplan - Design system/plan review → invoke /design-consultation or /plan-design-review - Full review pipeline → invoke /autoplan -- Bugs/errors → invoke /investigate, unless one read-only check confirms the cause and a narrow fix (then fix it and run the focused test) +- Bugs/errors → fix them directly: one read-only check of the cause, or a focused failing test first when the cause is unclear; then fix it and run that test - QA/testing site behavior → invoke /qa or /qa-only - Code review/diff check → invoke /review - Visual polish → invoke /design-review diff --git a/bin/gstack-team-init b/bin/gstack-team-init index f27cfe7dd..e1a152b1a 100755 --- a/bin/gstack-team-init +++ b/bin/gstack-team-init @@ -73,7 +73,7 @@ git clone --depth 1 https://github.com/garrytan/gstack.git ~/.claude/skills/gsta cd ~/.claude/skills/gstack && ./setup --team ``` -Skills like /qa, /ship, /review, /investigate, and /browse become available after install. +Skills like /qa, /ship, /review and /browse become available after install. Use /browse for all web browsing (Aside first, the bundled gstack browser as fallback). Use ~/.claude/skills/gstack/... for gstack file paths.' else SNIPPET='## gstack (REQUIRED — global install) @@ -100,7 +100,7 @@ If GSTACK_MISSING: STOP. Do not proceed. Tell the user: Do not skip skills, ignore gstack errors, or work around missing gstack. -Using gstack skills: After install, skills like /qa, /ship, /review, /investigate, +Using gstack skills: After install, skills like /qa, /ship, /review and /browse are available. Use /browse for all web browsing (Aside first, the bundled gstack browser as fallback). Use the resolved install path above for gstack file paths (default: ~/.claude/skills/gstack). diff --git a/careful/bin/hook-extract.sh b/careful/bin/hook-extract.sh index 1bab63457..24b0f0136 100644 --- a/careful/bin/hook-extract.sh +++ b/careful/bin/hook-extract.sh @@ -163,7 +163,7 @@ gstack_hook_decision() { # bin/gstack-state-root.sh, the one bash implementation of the chain # bin/gstack-paths uses (docs/state-root.md). Hooks run on every Edit/Bash # call, so the twin is pure bash — never spawn gstack-paths from a hook. The -# writers (/freeze, /guard, /unfreeze, /investigate) resolve through +# writers (/freeze, /guard, /unfreeze) resolve through # gstack-paths; a reader that used a different chain failed OPEN whenever # GSTACK_HOME was set (#1459). test/hook-scripts.test.ts pins parity. # Printed WITHOUT a trailing newline: callers capture with a sentinel diff --git a/changelog.d/capy-gst-1717-retire-investigate.md b/changelog.d/capy-gst-1717-retire-investigate.md new file mode 100644 index 000000000..cac3a51b9 --- /dev/null +++ b/changelog.d/capy-gst-1717-retire-investigate.md @@ -0,0 +1 @@ +- **`/investigate` is cut (GST-1717).** A preregistered eval found no gain: on 18 real bug-fix tasks that plain Opus 5.5 solves about half the time, 20 trials each, Opus told to use `/investigate` passed 50.0% of trials against 47.2% with no skill (+2.8 points, 95% CI -5.8 to +11.4, below the 10-point lift the run could detect), and a passing fix cost about 43% more ([GEVA-380 R3c](https://github.com/garrytan/gstack-evals/blob/c90ab277/wiki/board/2026-11-07-geva-380-r3c-results.md)). `./setup` no longer installs it on any host, and the next `./setup`, relink or upgrade removes an existing gstack-owned `investigate` / `gstack-investigate` entry, even though its source is gone. A skill you wrote yourself under that name is left alone, and the entry is retired through the `disabled_skills` path so config can't re-enable it. Because the source is gone, gstack can't tell its own unedited copy from one you edited, so a real-file `SKILL.md` left in such an entry is moved to the backup root instead of deleted. Relink does this for the Claude skills folder, and setup does it for every copied `gstack-investigate` entry on another host, in a separate backup folder per host, and lists each one in its "Moved N customized SKILL.md file(s)" line. Your own files in that directory stay. The opt-in /investigate gate goes with it: `./setup` removes its four Claude Code hooks from settings, `gstack-config`, `gstack-skill-gates` and `gstack-skill-route` drop its switch, and `./setup` now rejects `--investigate-gate` and `--no-investigate-gate` as unknown options, so drop them from install scripts. If setup can't remove those hooks, it now prints a warning with the command to rerun. The shared contract stop hook and gate machinery stay, because `/review`, the `/plan-ceo-review` gate and the `/autoplan` gate use them. Bug reports now get fixed directly. That means one read-only check of the cause, and when it is still unclear, a focused test that fails on the bug before any edit, then the root-cause fix and a rerun. The router, the CLAUDE.md routing block gstack offers, `/ship`'s test-failure triage, `/ios-fix`, `/learn`, `/verify` and the docs say so and no longer name `/investigate`. One line still does: `/review`'s checklist routes timing flakes to `/investigate` until its no-loss replay and repin land (GST-1744), because that checklist is pinned to replay-tested bytes. diff --git a/docs/OPENCLAW.md b/docs/OPENCLAW.md index ae2ca5a44..d42ec5faf 100644 --- a/docs/OPENCLAW.md +++ b/docs/OPENCLAW.md @@ -17,7 +17,7 @@ No compatibility matrices. The prompt is the bridge. │ │ ├── Native skills (conversational) ├── Generates native skills │ office-hours, ceo-review, │ via gen-skill-docs pipeline - │ investigate, retro │ + │ retro │ │ ├── Generates gstack-lite ├── sessions_spawn(runtime: "acp") │ (planning discipline) │ │ │ @@ -105,7 +105,6 @@ ready to build, spawn a FULL session that references the saved plan. Published to ClawHub. Install with `clawhub install`: - `gstack-openclaw-office-hours` — Product interrogation (6 forcing questions) - `gstack-openclaw-ceo-review` — Strategic challenge (10-section review, 4 modes) -- `gstack-openclaw-investigate` — Operational debugging (4-phase methodology) - `gstack-openclaw-retro` — Operational retrospective (weekly review) Source lives in `openclaw/skills/` in the gstack repo. These are hand-crafted @@ -181,8 +180,8 @@ The agent should: 4. Verify with a test spawn **Native skills or ACP.** `./setup --host openclaw` (or `./setup` run inside an -OpenClaw session) gives OpenClaw's own agent gstack's skills: the four task -skills (review, investigate, qa, ship) listed directly and one +OpenClaw session) gives OpenClaw's own agent gstack's skills: the three task +skills (review, qa, ship) listed directly and one `gstack` router skill for the rest in `$OPENCLAW_STATE_DIR/skills`, rendered with OpenClaw's tool names and pointing at the installed gstack, so the agent runs gstack's workflows and helpers itself. Use ACP when the work should happen in a Claude @@ -190,7 +189,7 @@ Code session with the full install (hooks, Claude tool names); use native skills when the OpenClaw agent should do it. Both can be installed together. OpenClaw serves a registered skill's `SKILL.md` whole or refuses it, and its limit is 32,768 bytes when it does not know the model's context window -(32,000 characters with a 100K-200K-token one), so a skill over 32,000 bytes (review, investigate and ship among the four) is +(32,000 characters with a 100K-200K-token one), so a skill over 32,000 bytes (review and ship among the three) is registered as a short entry that tells the agent to read the skill's full `SKILL.md` in `gstack/skills/`, a plain copy of every skill that OpenClaw reads in pages (`.gstack-skills/` under `--skills all`). diff --git a/docs/OPENCLAW_PUBLISHING.md b/docs/OPENCLAW_PUBLISHING.md index 04e311957..8defe424e 100644 --- a/docs/OPENCLAW_PUBLISHING.md +++ b/docs/OPENCLAW_PUBLISHING.md @@ -16,7 +16,7 @@ clawhub publish openclaw/skills/gstack-openclaw-office-hours \ --version 1.0.0 --changelog "description of changes" ``` -Repeat for each skill: `gstack-openclaw-ceo-review`, `gstack-openclaw-investigate`, +Repeat for each skill: `gstack-openclaw-ceo-review`, `gstack-openclaw-retro`. Bump `--version` on each update. **Auth:** `clawhub login` (opens browser for GitHub auth). `clawhub whoami` to verify. diff --git a/docs/PROJECT_STRUCTURE.md b/docs/PROJECT_STRUCTURE.md index c89d0879b..f9592f4b5 100644 --- a/docs/PROJECT_STRUCTURE.md +++ b/docs/PROJECT_STRUCTURE.md @@ -68,7 +68,6 @@ gstack/ ├── land-and-deploy/ # /land-and-deploy skill (merge → deploy → canary verify) ├── modernize/ # /modernize skill (aging PR → MODERNIZE / REWORK / CLOSE disposition, range-diff proof, pinned-lease push) ├── office-hours/ # /office-hours skill (YC Office Hours — startup diagnostic + builder brainstorm) -├── investigate/ # /investigate skill (systematic root-cause debugging) ├── verify/ # /verify skill (build-and-verify loop over bin/gstack-checks + lib/checks.ts; tests come from lib/pregate) ├── spec/ # /spec skill (five-phase spec → GitHub issue, optional agent spawn, /ship auto-closes) ├── retro/ # Retrospective skill (includes /retro global cross-project mode) diff --git a/docs/TESTING_INTERNALS.md b/docs/TESTING_INTERNALS.md index 5c549acb6..079244f3d 100644 --- a/docs/TESTING_INTERNALS.md +++ b/docs/TESTING_INTERNALS.md @@ -1969,7 +1969,7 @@ for arm in rule removed none; do GSTACK_SAFETY_ARM=$arm bun run scripts/test-paid-shards.ts --tier gate --case safety-ship-stale-evidence done for arm in current before none; do - GSTACK_ROUTER_ARM=$arm bun run scripts/test-paid-shards.ts --tier gate --case router-failure-contradicted-cause + GSTACK_ROUTER_ARM=$arm bun run scripts/test-paid-shards.ts --tier gate --case router-failure-verified-cause done ``` @@ -1986,8 +1986,8 @@ ran, the session ended normally) still assert. This departs from "the same panel" above because these contracts are gstack's own rules: a plain model was never asked to keep them, so a failing `none` trial is the measurement, not a regression, and a red verdict would block the gate on behavior nobody -specified. The router's `none` arm has no "routed to /investigate" metric, -since no skill is installed to route to; it records plain metrics instead: +specified. The router's `none` arm has no repro-first metric, +since it never sees the router's rules; it records plain metrics instead: whether the suite is green, whether an edit came before the first read, and how many read-only calls came before the first edit. The first paid comparison with these arms gets its own preregistration. diff --git a/docs/hosts.md b/docs/hosts.md index 337213637..35c2bea02 100644 --- a/docs/hosts.md +++ b/docs/hosts.md @@ -61,10 +61,10 @@ characters, the part of a description Hermes's skills index shows. | Harness | What gstack registers | Install | Verify | Remove | |---|---|---|---|---| -| OpenClaw | default `--skills hybrid`: `gstack-review`, `gstack-investigate`, `gstack-qa` and `gstack-ship` as top-level skills plus one `gstack` router skill whose body lists every other skill and its path, in `${OPENCLAW_STATE_DIR:-~/.openclaw}/skills`. With OpenClaw 2026.10.6's 19 eligible bundled skills, hybrid's skills section stays in full format under its 18,000-character budget; `--skills all` (17,971, compact format, descriptions cut) lists every skill as its own folder, `--skills router` (6,701) only the router. OpenClaw serves a registered skill's `SKILL.md` whole or refuses it ("Skill instructions cannot be partially served"), at most 32,768 bytes when its model catalog has no context window for the model and 32,000 characters with a 100K-200K-token window, so any registered skill over 32,000 bytes (three of the four in hybrid, 25 of 62 in `all` today) is a short entry `SKILL.md` with the same name and description that sends the agent to its full `SKILL.md` in a plain copy of every skill (the router's `gstack/skills/`, or `.gstack-skills/` under `--skills all`, a folder OpenClaw's skill scan skips), which OpenClaw reads in pages. A context window under 100K tokens lowers that budget to 16,000 characters, which this limit does not cover | `./setup --host openclaw`, or `./setup` inside OpenClaw | `openclaw skills list --json` shows `gstack` and the four task skills (source `openclaw-managed`, eligible) | `gstack-harness unregister openclaw` | +| OpenClaw | default `--skills hybrid`: `gstack-review`, `gstack-qa` and `gstack-ship` as top-level skills plus one `gstack` router skill whose body lists every other skill and its path, in `${OPENCLAW_STATE_DIR:-~/.openclaw}/skills`. With OpenClaw 2026.10.6's 19 eligible bundled skills, hybrid's skills section stays in full format under its 18,000-character budget; `--skills all` (17,971, compact format, descriptions cut) lists every skill as its own folder, `--skills router` (6,701) only the router. OpenClaw serves a registered skill's `SKILL.md` whole or refuses it ("Skill instructions cannot be partially served"), at most 32,768 bytes when its model catalog has no context window for the model and 32,000 characters with a 100K-200K-token window, so any registered skill over 32,000 bytes (three of the four in hybrid, 25 of 62 in `all` today) is a short entry `SKILL.md` with the same name and description that sends the agent to its full `SKILL.md` in a plain copy of every skill (the router's `gstack/skills/`, or `.gstack-skills/` under `--skills all`, a folder OpenClaw's skill scan skips), which OpenClaw reads in pages. A context window under 100K tokens lowers that budget to 16,000 characters, which this limit does not cover | `./setup --host openclaw`, or `./setup` inside OpenClaw | `openclaw skills list --json` shows `gstack` and the four task skills (source `openclaw-managed`, eligible) | `gstack-harness unregister openclaw` | | Hermes | every skill, as `gstack-` in one `gstack` category, in `${HERMES_HOME:-~/.hermes}/skills/gstack`. Hermes's skills index lists only skills registered as folders, so a router would leave the rest out of the listing (`skill_view` still loads an unlisted skill by name, but nothing tells the model it exists) | `./setup --host hermes`, or `./setup` inside Hermes | `hermes skills list` shows the `gstack-*` skills in category `gstack` | `gstack-harness unregister hermes` | | dots (feder-cr) | the `gstack` router in `/home/dot/skills/gstack`, which the Dot lists every turn | `./setup` on the Dot's computer, or `gstack-harness register dots` | the Dot's Skills page, or `GET /skills` | `gstack-harness unregister dots` | -| QM | three skills (`gstack-investigate`, `gstack-review`, `gstack-plan`) in QM's own skill format, from `qm/sandbox/skills/` ([qm/README.md](../qm/README.md)). `gstack-harness register qm --yes` still stops before sending anything: QM imports a skill pack by cloning the whole repository and caps it at 32 MiB and 5,000 files, and gstack's repository is about 73 MiB ([why](troubleshooting.md#harness-qm-pack-too-large)) | copy `qm/sandbox/skills/gstack-*` into `/sandbox/skills/` and run `qm up` | ask it for a gstack skill | remove the folders and run `qm up` again | +| QM | two skills (`gstack-review`, `gstack-plan`) in QM's own skill format, from `qm/sandbox/skills/` ([qm/README.md](../qm/README.md)). `gstack-harness register qm --yes` still stops before sending anything: QM imports a skill pack by cloning the whole repository and caps it at 32 MiB and 5,000 files, and gstack's repository is about 73 MiB ([why](troubleshooting.md#harness-qm-pack-too-large)) | copy `qm/sandbox/skills/gstack-*` into `/sandbox/skills/` and run `qm up` | ask it for a gstack skill | remove the folders and run `qm up` again | | Muse Code, Grok Build | nothing: both read `~/.claude/skills`, so the Claude install (`./setup`) is what they see, with Claude tool names | `./setup` | `gstack-harness status` runs `muse skills list` / `grok inspect` and reports *discovered (Claude-rendered, unverified)* | `gstack-uninstall` | | Grok Bot, any chat-only skill library | nothing on disk | `gstack-harness register generic --print`, then paste the router and block into the app | ask it for a gstack skill | delete the pasted text | | any other harness | the router in an Agent Skills folder you name, and/or a 600-byte routing block between `` markers in a context file you name | `gstack-harness register generic --skills-dir ` and/or `--context `; `gstack-harness manifest generic` prints every skill as JSON | the harness's own skill list | `gstack-harness unregister generic` | @@ -123,14 +123,13 @@ Four methodology skills that work directly in your OpenClaw agent, no Claude Cod session needed. Install from ClawHub: ``` -clawhub install gstack-openclaw-office-hours gstack-openclaw-ceo-review gstack-openclaw-investigate gstack-openclaw-retro +clawhub install gstack-openclaw-office-hours gstack-openclaw-ceo-review gstack-openclaw-retro ``` | Skill | What it does | |-------|-------------| | `gstack-openclaw-office-hours` | Product interrogation with 6 forcing questions | | `gstack-openclaw-ceo-review` | Strategic challenge with 4 scope modes | -| `gstack-openclaw-investigate` | Root cause debugging methodology | | `gstack-openclaw-retro` | Weekly engineering retrospective | These are conversational skills. Your OpenClaw agent runs them directly via chat. diff --git a/docs/inventory/evidence.json b/docs/inventory/evidence.json index c2ce800cd..cc04c39ce 100644 --- a/docs/inventory/evidence.json +++ b/docs/inventory/evidence.json @@ -177,67 +177,6 @@ "plan": "none", "seed": "GEVA-2, 2026-11-02: health: untested (never measured)" }, - "investigate": { - "status": "no-effect", - "grandfathered": true, - "sources": [ - "GST-814 (HARN-1, 2026-11-02)", - "org-gstack-gbrain handoffs/HARN-14/hard-follow-2026-11/decision-rule.md" - ], - "models": [ - "claude-opus-5-5" - ], - "prompt": "preset", - "scope": "skill step procedure follow rate: 0 of 46 gstack sessions ran any step (0 of 30 on the claude_code preset); body loaded every time; the outcome check voided at ceiling (no-gstack fixed 8 of 8)", - "masked": true, - "r48": { - "verdict": "CUT-CANDIDATE", - "ref": "GST-938 (GSTA-251 plans hooks-or-drop)" - }, - "plan": "R3 (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-01-geva-101-r48-follow-ups-prereg (#112, main f0b81c0) and its amendment (open PR #133), told /investigate vs no gstack on SWE-bench Verified, preset, Opus 5.5 then GPT-6.1 Sol", - "prior": [ - { - "ref": "gstack-evals #109 stacks round 1 TB2 (GEVA-89, GEVA-93), wiki/board/2026-11-01-geva-93-stacks-round-1-tb2-results", - "why": [ - "stub", - "no-exposure", - "unmasked" - ], - "note": "Opus +3 points [-3, +9] at 1.9x cost; debug report in 0 of 111 trials; particle-1 -5 [-14, +1]" - }, - { - "ref": "GSTA-12 Lane B quick look 2, wiki/board/2026-10-29-gsta-12-laneb-quick-2-results", - "why": [ - "stub", - "unmasked" - ], - "note": "Opus gstack minus none -7 [-13, -2] at 1.7x cost; did not replicate" - }, - { - "ref": "GSTA-30 Lane B quick look 3 (#89), wiki/board/2026-10-30-gsta-30-laneb-quick-3-results", - "why": [ - "stub", - "small-tier" - ], - "note": "gstack arms on Sonnet, particle-1, DeepSeek and Haiku; Opus ran the none arm only" - }, - { - "ref": "HARN-14 replication (harness-evals #181, unmerged)", - "why": [ - "stub", - "small-tier" - ], - "note": "Haiku 5.5: gstack minus none +3.5 [-12.3, +20.0]" - } - ], - "seed": "GEVA-2, 2026-11-02: investigate: measurement under way (R3)", - "note": "Only Opus 5.5 (claude-opus-5-5) was measured; GPT-6.1 Sol was not, so this verdict covers one of the two frontier models ETHOS.md 9 names. L1 (gstack-evals#247, wiki/board/2026-11-06-geva-350-l1-overhaul-results) measured the after pin's untold routing package and told record completeness, not this row's procedure follow rate, so the no-effect status and its CUT-CANDIDATE stand; its three verdicts are this row's caveats. GPT-6.1 Sol has not run L1.", - "caveats": [ - "L1 (GEVA-2, gstack-evals#247, merged 28be250; Opus 5.5, claude_code preset, 80 SWE-rebench tasks, no-gstack arm, pins 82db89ab → 699a41cb), untold: the routing package (route hook, contracts and the description rewrite) helps, untold /investigate opens 17/80 vs 0/80, B1 +21.3 [+12.5, +30.0], R48 KEEP; it needs routing or contracts on, and hook vs contracts aren't split; the description rewrite alone had no effect (off-u 0/80)", - "L1 (GEVA-2, gstack-evals#247, merged 28be250; Opus 5.5, claude_code preset, 80 SWE-rebench tasks, no-gstack arm, pins 82db89ab → 699a41cb), told: /investigate record completeness with the contract Stop hook is untested, B3 +10.0 [0, +20]; probe I failed its blind audit, R48 FIX-METHOD; the weak item is 'Fix names an edited file', and 5 of 7 misses were fixes written by an inline Python heredoc", - "L1 (GEVA-2, gstack-evals#247, merged 28be250; Opus 5.5, claude_code preset, 80 SWE-rebench tasks, no-gstack arm, pins 82db89ab → 699a41cb), pass rate: O5 +5.0 at a 65% control, MDD 18.7, descriptive only" - ] - }, "ios-clean": { "status": "untested", "grandfathered": true, diff --git a/docs/pregate.md b/docs/pregate.md index d94299f81..8947c7d19 100644 --- a/docs/pregate.md +++ b/docs/pregate.md @@ -1792,8 +1792,8 @@ reason. Blocking would stop about a fifth of gstack's and two fifths of gbrain's test-touching commits, including correct fixes, so the check warns. A repo that wants it stricter keeps the warning in its review rule (the /review -checklist has the same rule) and /investigate's flake playbook says what to do -with a timing flake: never raise the bound. +checklist has the same rule and says what to do with a timing flake: find the +timing cause and fix it, never raise the bound). ## Sealed output: the pre-push hook and `gstack-pregate sealed` diff --git a/docs/recommended-harness.md b/docs/recommended-harness.md index 93abb8d36..1cd0cdf32 100644 --- a/docs/recommended-harness.md +++ b/docs/recommended-harness.md @@ -66,7 +66,7 @@ Two things the first week taught every crew: - **Most threads never install.** The rule installs right before the first workflow, so a thread that does not plan one never gets `/review` or - `/investigate` either. If most threads in a project run a workflow, bake + `/qa` either. If most threads in a project run a workflow, bake gstack into the project's Capy Setup snapshot instead ([capy-install.md#bake-into-a-capy-snapshot](capy-install.md#bake-into-a-capy-snapshot)): each session then pays only the check. @@ -369,7 +369,7 @@ harness on it as of that date, with the workaround the crews use. shard subagents per run pay it nine times. Workaround: bake gstack into the project Setup snapshot. - Threads that plan no workflow never install, so `/review` and - `/investigate` go unused. Workaround: a crew rule that names the workflow + `/qa` go unused. Workaround: a crew rule that names the workflow each thread will run, or the snapshot. - gstack versions drift between machines in one thread (`behind=1` on one, current on another); every minimum bump was a crew-wide broadcast (eight in diff --git a/docs/skill-gates.md b/docs/skill-gates.md index b32d3aeb9..c6d872b23 100644 --- a/docs/skill-gates.md +++ b/docs/skill-gates.md @@ -4,7 +4,6 @@ gstack's advisory skills say what a careful agent does; a gate makes one rule ho | Gate | Switch | What it holds | |---|---|---| -| /investigate | `investigate_gate` | After /investigate loads, changes to tracked source wait until the session states a root cause | | /plan-ceo-review | `ceo_review_gate` | After /plan-ceo-review loads, the final stop (of the session or of a review subagent) waits until the review's output file ranks the top risks | | /autoplan | `autoplan_gate` | After /autoplan loads, the session's final stop waits until every applicable phase has published its report and the Final Approval Gate is written (GST-1666) | @@ -12,42 +11,23 @@ gstack's advisory skills say what a careful agent does; a gate makes one rule ho The switch resolves like the contract hook's, and the first one set wins: -1. The `./setup` flag: `--investigate-gate` / `--no-investigate-gate` (also `--investigate-gate=on|off`), `--ceo-review-gate` / `--no-ceo-review-gate` (also `--ceo-review-gate=on|off`), or `--autoplan-gate` / `--no-autoplan-gate` (also `--autoplan-gate=on|off`). A flag is saved to config. -2. The environment: `GSTACK_INVESTIGATE_GATE=on|off`, `GSTACK_CEO_REVIEW_GATE=on|off` or `GSTACK_AUTOPLAN_GATE=on|off`. Not saved. -3. Config: `gstack-config set investigate_gate on|off`, `gstack-config set ceo_review_gate on|off` or `gstack-config set autoplan_gate on|off`. -4. The default, `SWITCH_DEFAULT` in `lib/skill-route.ts`: off for all three. +1. The `./setup` flag: `--ceo-review-gate` / `--no-ceo-review-gate` (also `--ceo-review-gate=on|off`), or `--autoplan-gate` / `--no-autoplan-gate` (also `--autoplan-gate=on|off`). A flag is saved to config. +2. The environment: `GSTACK_CEO_REVIEW_GATE=on|off` or `GSTACK_AUTOPLAN_GATE=on|off`. Not saved. +3. Config: `gstack-config set ceo_review_gate on|off` or `gstack-config set autoplan_gate on|off`. +4. The default, `SWITCH_DEFAULT` in `lib/skill-route.ts`: off for both. -Setup registers a gate's shims in `~/.claude/settings.json` when its switch is on (four for /investigate; Stop and SubagentStop for /plan-ceo-review; Stop for /autoplan) and removes them when it is off. The hooks also read the switch on every call, so `gstack-config set investigate_gate off` takes effect on the next tool call or stop. +Setup registers a gate's shims in `~/.claude/settings.json` when its switch is on (Stop and SubagentStop for /plan-ceo-review; Stop for /autoplan) and removes them when it is off. The hooks also read the switch on every call, so `gstack-config set ceo_review_gate off` takes effect on the next stop. ### Headless runs For `claude -p` or an eval harness, skip settings.json: - gstack-skill-gates settings --investigate --ceo-review --autoplan > gates.json + gstack-skill-gates settings --ceo-review --autoplan > gates.json claude -p "" --settings gates.json -Name any of the gates. The JSON registers their hooks from this checkout and sets each named gate's switch (`GSTACK_INVESTIGATE_GATE=on`, `GSTACK_CEO_REVIEW_GATE=on`, `GSTACK_AUTOPLAN_GATE=on`) through its `env` block. Under the Agent SDK, pass the same JSON as settings or include `user` in `settingSources` after running setup; with neither, or with `--bare`, no hook loads and no `fired` marker appears. Check every session with `gstack-skill-gates report --session --expect armed` ([exit codes](#exit-codes)). +Name any of the gates. The JSON registers their hooks from this checkout and sets each named gate's switch (`GSTACK_CEO_REVIEW_GATE=on`, `GSTACK_AUTOPLAN_GATE=on`) through its `env` block. Under the Agent SDK, pass the same JSON as settings or include `user` in `settingSources` after running setup; with neither, or with `--bare`, no hook loads and no `fired` marker appears. Check every session with `gstack-skill-gates report --session --expect armed` ([exit codes](#exit-codes)). -## The /investigate gate - -Once a session loads /investigate (the Skill tool, a typed `/investigate`, or a Read of its installed SKILL.md), the gate arms on the next Edit, Write, MultiEdit, NotebookEdit or Bash call. From then until the session states a root cause: - -- **Edit, Write, MultiEdit and NotebookEdit** on a tracked source file are denied before they run. -- **Bash** can't be judged from its command line (`sed -i`, a redirect, `tee`, `python -c`, `perl -pi`, `node -e`, `git apply`, a heredoc, a build step that rewrites a file). So the gate compares the tree instead. When it arms it pins a baseline: the repository root, HEAD, its refs and stash list, and the content of every tracked file that differs from HEAD. Every later Edit/Write/Bash hook call, every PostToolUse and PostToolUseFailure on Bash, and Stop compare the tree with that baseline under a session lock. A tracked source file whose content changed is put back and the call or stop is blocked with the files named. Because every check uses the same baseline, a write from a background command or a parallel call is caught at the next check whichever call made it. - -A change is not a fix write, and is folded into the baseline, when the file's new content is its blob in HEAD (`git checkout -- f`, `git stash`), in a commit that existed before the baseline (`git checkout `, `git bisect`), in a commit a remote-tracking ref holds (`git pull`, `git merge origin/main`), or in a stash entry that existed before (`git stash pop`). A commit made after the baseline that touches a gated file is undone with `git reset --soft` to its newest ancestor that existed before (or that a remote-tracking ref holds), so a fix committed on another branch moves that branch back to its own tip, including a branch behind the baseline HEAD (`git checkout main && && git commit` from a feature branch moves main back, and the feature branch is untouched). An amend or rebase of commits from before the baseline is undone to that branch's tip when the gate armed: the baseline HEAD on the same branch, or the other branch's own recorded tip. A branch created after the baseline goes back to that newest older ancestor. The commit stays in the reflog, those index entries are reset, the files are put back, and the block names the commit's other changes still staged. A commit pushed to a remote counts as upstream, so a fix pushed in the same command is not undone. - -**What counts as source.** A file tracked by git that is not a test path (gstack-diff-scope's test rule plus `fixtures/`), a doc (`.md`, `.mdx`, `.rst`, `.txt`, `.adoc`), a lockfile (`bun.lock`, `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml`, `Cargo.lock`, `go.sum`, `uv.lock`, `poetry.lock`, `Gemfile.lock` and the like), a `linguist-generated` path (read from the baseline commit's attributes, so a `.gitattributes` written after the gate armed exempts nothing), or a tracked symlink. New untracked files are never blocked, so a repro script, a new failing test and temporary logging in a scratch file all work. Changes to excluded files are recorded as `observed`, never reverted. - -**What releases it.** A line in the assistant's own reply, after at least one Read, Grep, Glob or Bash call since the load: - - Root cause: - -That is the `Root cause:` label of the skill's DEBUG REPORT. A placeholder (`[what was actually wrong]`, `TBD`) or a hedge (`unknown`, `unclear`, `still investigating`, `see below`, `n/a`) does not count, and neither does the text inside a tool result or a file. The block text names the form, and the rejected value when there was one. After the release the gate does nothing for the rest of that /investigate run; a new load arms it again. - -**Restoring is careful.** The gate never writes through a symlink: it refuses a parent that is not a real directory inside the repository and replaces the path with a rename, so a tracked file swapped for a link to `~/.bashrc` comes back as the file and the link's target is untouched. Files clean at the baseline come back through `git cat-file --filters` (line endings and LFS smudging intact); files dirty at the baseline come back from a raw copy in the session directory, keyed by the sha256 of its bytes. A file clean at the baseline keeps its current permissions (the tree's mode only when the file is gone); a file dirty at the baseline gets its baseline mode back. Each restored file's index entry goes back to the baseline's, including a change that was only staged, so a rejected edit that was `git add`ed is not left staged and staged work is not dropped. Before a file is put back, the content it replaced is copied into the session directory and the block names that copy, so an edit made in an editor or another session at the same time is never lost. A path that cannot be put back (it is now a directory, the disk is read-only) gives [GATE_RESTORE_FAILED](#gate-restore-failed). - -**Known gaps.** Paths marked assume-unchanged or skip-worktree, untracked files that shadow a tracked module, and ignored directories are outside the check. While the gate is armed it also puts back an edit you make to tracked source in your editor, since it cannot tell who changed the file. +The /investigate gate was retired with /investigate (GST-1717). `./setup` removes its hooks from `~/.claude/settings.json` whatever `investigate_gate` says. ## The /plan-ceo-review gate @@ -107,21 +87,20 @@ Every decision is one marker line: | Event | When | Fields | |---|---|---| -| `fired` | the gate armed for a skill run (once per run; for /plan-ceo-review and /autoplan, its first final stop) | `skill`, `on` (the hook event), `root` (the repository folder name, /investigate only) | -| `blocked` | each refusal | `skill`, `via=edit|bash|stop`, `files` (for /plan-ceo-review's `via=stop`, the candidate files checked, or `none`), `missing` and `published` (/autoplan: the next missing step and the phases published so far), `n` (blocks so far in this run) | -| `released` | the root cause was stated, an output file ranks the top risks, or the /autoplan run is complete (once per run) | `skill`, `cause` (first 12 hex of the cause's sha256), `file` (the output file's name) or `phases` (the phases /autoplan published), `blocked` | +| `fired` | the gate armed for a skill run (once per run; for /plan-ceo-review and /autoplan, its first final stop) | `skill`, `on` (the hook event) | +| `blocked` | each refusal | `skill`, `via=stop`, `files` (for /plan-ceo-review's `via=stop`, the candidate files checked, or `none`), `missing` and `published` (/autoplan: the next missing step and the phases published so far), `n` (blocks so far in this run) | +| `released` | an output file ranks the top risks, or the /autoplan run is complete (once per run) | `skill`, `file` (the output file's name) or `phases` (the phases /autoplan published), `blocked` | | `exhausted` | /plan-ceo-review and /autoplan: a fourth final stop after 3 blocks, which goes through | `skill`, `missing` (/autoplan), `blocked` | -| `observed` | an excluded file changed | `skill`, `files` | | `skipped` | the gate could not arm (once per session and reason), or an /autoplan run drives the gstack-autoplan runner (once per run) | `skill`, `reason=no-git|no-head|runner` | | `error` | a hook error; the call or stop went through | `code`, `why` | Where each one lands: -- **Session record** (the count source): `$GSTACK_STATE_ROOT/skill-gates//record.json`, mode 0600, written atomically under the session lock. Per gate and skill run it holds `fired_at`, `released_at`, `cause` or `file`, `blocked`, `observed`, `exhausted_at`, `agent` (a subagent's run), `phases` and `published` (/autoplan), plus the gate's `errors` and `skipped` reasons. `gstack-skill-gates report` reads these. -- **Trace log**: `$GSTACK_STATE_ROOT/hooks/investigate-gate.log`, `hooks/ceo-review-gate.log` and `hooks/autoplan-gate.log`, one line per marker in the contract hook's shape (`