diff --git a/autoplan/SKILL.md b/autoplan/SKILL.md index 95763b393..d80aa2f09 100644 --- a/autoplan/SKILL.md +++ b/autoplan/SKILL.md @@ -255,7 +255,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -365,7 +366,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -725,7 +727,7 @@ bun -e 'console.log(require("fs").realpathSync(process.argv[1]))' "$HOME/.claude Fresh external RESTORE_PATH: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-') DATETIME=$(date +%Y%m%d-%H%M%S) diff --git a/autoplan/SKILL.md.tmpl b/autoplan/SKILL.md.tmpl index 6d0f624fb..f9b6df70c 100644 --- a/autoplan/SKILL.md.tmpl +++ b/autoplan/SKILL.md.tmpl @@ -204,7 +204,7 @@ Resolve SNAPSHOT_TOOL once: Fresh external RESTORE_PATH: ```bash {{SLUG_EVAL}} -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null | tr '/' '-') DATETIME=$(date +%Y%m%d-%H%M%S) diff --git a/autoplan/sections/tasks-aggregator.md b/autoplan/sections/tasks-aggregator.md index fca58d8ac..33ce339df 100644 --- a/autoplan/sections/tasks-aggregator.md +++ b/autoplan/sections/tasks-aggregator.md @@ -6,8 +6,9 @@ Before rendering the Final Approval Gate output block below, aggregate the per-phase task lists each review skill wrote. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -TASKS_DIR="${HOME}/.gstack/projects/${SLUG:-unknown}" +TASKS_DIR="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" BRANCH=$(git branch --show-current 2>/dev/null || echo unknown) # Commit window: last 5 commits on this branch. Drops stale standalone reviews. COMMITS_RECENT=$(git log --format=%H -n 5 2>/dev/null | tr '\n' '|' | sed 's/|$//') diff --git a/bin/gstack-brain-cache b/bin/gstack-brain-cache index 5ced064c0..038ef63c0 100755 --- a/bin/gstack-brain-cache +++ b/bin/gstack-brain-cache @@ -23,6 +23,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync, renameSync, statSyn import { join, dirname } from 'path'; import { homedir, hostname } from 'os'; import { spawnSync } from 'child_process'; +import { resolveStateRoot } from '../lib/state-root'; import { execGbrainJson, spawnGbrain } from '../lib/gbrain-exec'; import { BRAIN_CACHE_ENTITIES, @@ -37,7 +38,7 @@ import { // Paths + meta // ────────────────────────────────────────────────────────────────────────── -const GSTACK_HOME = process.env.GSTACK_HOME || join(homedir(), '.gstack'); +const GSTACK_HOME = resolveStateRoot(); interface CacheMeta { /** Version of the schema pack the cache was built against. Mismatch → full rebuild. */ diff --git a/bin/gstack-evidence b/bin/gstack-evidence index 8343fe9cf..878c38557 100755 --- a/bin/gstack-evidence +++ b/bin/gstack-evidence @@ -39,6 +39,7 @@ import { mkdirpSync } from "../lib/fs-utils"; import { join, dirname } from "path"; import { spawnSync } from "child_process"; import { appendJsonl, readJsonl } from "../lib/jsonl-store"; +import { resolveStateRoot } from "../lib/state-root"; import { scan, applyRedactions } from "../lib/redact-engine"; const BIN_DIR = dirname(Bun.fileURLToPath(import.meta.url)); @@ -93,10 +94,10 @@ function currentWtree(): string | undefined { } function ledgerPath(): { dir: string; file: string; logsDir: string } { - const home = process.env.GSTACK_HOME || (process.env.HOME ? join(process.env.HOME, ".gstack") : undefined); - // No resolvable home: skip bookkeeping (a literal "~" dir in cwd would land - // inside the repo and perturb the fingerprint it exists to compute). - if (!home) throw new Error("no GSTACK_HOME/HOME — bookkeeping skipped"); + const home = resolveStateRoot(); + // No resolvable home: skip bookkeeping (the relative ".gstack" fallback would + // land inside the repo and perturb the fingerprint it exists to compute). + if (home === ".gstack") throw new Error("no GSTACK_HOME/HOME — bookkeeping skipped"); // ONE gstack-slug spawn: its output carries both SLUG= and BRANCH= lines // (same branch→filename sanitization as reviews.jsonl). const slugOut = spawnSync(join(BIN_DIR, "gstack-slug"), { encoding: "utf-8" }); diff --git a/bin/gstack-gbrain-detect b/bin/gstack-gbrain-detect index 3774f6bac..ece1d7c87 100755 --- a/bin/gstack-gbrain-detect +++ b/bin/gstack-gbrain-detect @@ -44,8 +44,9 @@ import { readGbrainVersion, } from "../lib/gbrain-local-status"; import { gbrainConfigDir, isTransactionModePooler } from "../lib/gbrain-exec"; +import { resolveStateRoot } from "../lib/state-root"; -const STATE_DIR = process.env.GSTACK_HOME || join(userHome(), ".gstack"); +const STATE_DIR = resolveStateRoot(); const SCRIPT_DIR = __dirname; const CONFIG_BIN = join(SCRIPT_DIR, "gstack-config"); // Honors GBRAIN_HOME with gbrain's own configDir() semantics (#2521: diff --git a/bin/gstack-redact-prepush b/bin/gstack-redact-prepush index 8ca1fa0f5..af3eebbec 100755 --- a/bin/gstack-redact-prepush +++ b/bin/gstack-redact-prepush @@ -27,10 +27,10 @@ */ import { spawnSync } from "child_process"; import * as fs from "fs"; -import * as os from "os"; import * as path from "path"; import { normalizeWithMap, scan, type Finding } from "../lib/redact-engine"; import { mkdirpSync } from "../lib/fs-utils"; +import { resolveStateRoot } from "../lib/state-root"; const ZERO = /^0+$/; let emptyTree: string | undefined; @@ -416,7 +416,7 @@ function scanAddedLines(added: string, opts: Parameters[1]): Findin function logSkip(reason: string): void { try { - const home = process.env.GSTACK_HOME || path.join(os.homedir(), ".gstack"); + const home = resolveStateRoot(); const dir = path.join(home, "security"); // mkdirpSync, not bare mkdirSync: bun-on-Windows EEXIST (#2635). This site // is try-wrapped by the caller, so the old failure was a silent skip-log diff --git a/bin/gstack-taste-update b/bin/gstack-taste-update index 4782552d2..7510a8bbf 100755 --- a/bin/gstack-taste-update +++ b/bin/gstack-taste-update @@ -32,8 +32,9 @@ import * as fs from 'fs'; import * as path from 'path'; import { execSync } from 'child_process'; +import { resolveStateRoot } from '../lib/state-root'; -const STATE_DIR = process.env.GSTACK_STATE_DIR || path.join(process.env.HOME || '/', '.gstack'); +const STATE_DIR = resolveStateRoot(); const SCHEMA_VERSION = 1; const SESSION_CAP = 50; const DECAY_PER_WEEK = 0.05; diff --git a/canary/SKILL.md b/canary/SKILL.md index 01b2e6551..8a9f1215b 100644 --- a/canary/SKILL.md +++ b/canary/SKILL.md @@ -236,7 +236,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -694,8 +695,9 @@ Per-page and overall status: BROKEN if any confirmed CRITICAL alert occurred; ot Log the result for the review dashboard: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -mkdir -p ~/.gstack/projects/$SLUG +mkdir -p "$GSTACK_STATE_ROOT"/projects/$SLUG ``` Write a JSONL entry: `{"skill":"canary","timestamp":"","status":"","url":"","duration_min":,"alerts":}` diff --git a/canary/SKILL.md.tmpl b/canary/SKILL.md.tmpl index daa894dd7..2aaf7d709 100644 --- a/canary/SKILL.md.tmpl +++ b/canary/SKILL.md.tmpl @@ -224,8 +224,9 @@ Per-page and overall status: BROKEN if any confirmed CRITICAL alert occurred; ot Log the result for the review dashboard: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" {{SLUG_EVAL}} -mkdir -p ~/.gstack/projects/$SLUG +mkdir -p "$GSTACK_STATE_ROOT"/projects/$SLUG ``` Write a JSONL entry: `{"skill":"canary","timestamp":"","status":"","url":"","duration_min":,"alerts":}` diff --git a/careful/SKILL.md b/careful/SKILL.md index 5dae5e972..80220827a 100644 --- a/careful/SKILL.md +++ b/careful/SKILL.md @@ -36,8 +36,9 @@ patterns before running. If a destructive command is detected, you'll be warned and can choose to proceed or cancel. ```bash -mkdir -p ~/.gstack/analytics -echo '{"skill":"careful","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 +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/analytics +echo '{"skill":"careful","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_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true ``` ## What's protected diff --git a/careful/SKILL.md.tmpl b/careful/SKILL.md.tmpl index 8fba18fb0..f3101d290 100644 --- a/careful/SKILL.md.tmpl +++ b/careful/SKILL.md.tmpl @@ -31,8 +31,9 @@ patterns before running. If a destructive command is detected, you'll be warned and can choose to proceed or cancel. ```bash -mkdir -p ~/.gstack/analytics -echo '{"skill":"careful","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 +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/analytics +echo '{"skill":"careful","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_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true ``` ## What's protected diff --git a/codex/SKILL.md b/codex/SKILL.md index b6dc9b47d..75d607908 100644 --- a/codex/SKILL.md +++ b/codex/SKILL.md @@ -239,7 +239,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -349,7 +350,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -554,7 +556,7 @@ This keeps the skill working whether installed as a Claude Code plugin container where `HOME` may be unset and `/tmp` may be read-only. ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" ``` After this, every subsequent bash block in this skill uses `"$PLAN_ROOT"` and diff --git a/codex/SKILL.md.tmpl b/codex/SKILL.md.tmpl index 16babf5a9..9d45b5a39 100644 --- a/codex/SKILL.md.tmpl +++ b/codex/SKILL.md.tmpl @@ -123,7 +123,7 @@ This keeps the skill working whether installed as a Claude Code plugin container where `HOME` may be unset and `/tmp` may be read-only. ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" ``` After this, every subsequent bash block in this skill uses `"$PLAN_ROOT"` and diff --git a/context-restore/SKILL.md b/context-restore/SKILL.md index 55dcabce7..3fb251c13 100644 --- a/context-restore/SKILL.md +++ b/context-restore/SKILL.md @@ -240,7 +240,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -417,8 +418,9 @@ Parse the user's input: ### Step 1: Find saved contexts ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" CHECKPOINT_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/checkpoints" if [ ! -d "$CHECKPOINT_DIR" ]; then echo "NO_CHECKPOINTS" diff --git a/context-restore/SKILL.md.tmpl b/context-restore/SKILL.md.tmpl index d2e78ca89..8deb6a840 100644 --- a/context-restore/SKILL.md.tmpl +++ b/context-restore/SKILL.md.tmpl @@ -68,7 +68,7 @@ Parse the user's input: ```bash {{SLUG_SETUP}} -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" CHECKPOINT_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/checkpoints" if [ ! -d "$CHECKPOINT_DIR" ]; then echo "NO_CHECKPOINTS" diff --git a/context-save/SKILL.md b/context-save/SKILL.md index 458589f80..24f6d4e8b 100644 --- a/context-save/SKILL.md +++ b/context-save/SKILL.md @@ -239,7 +239,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -406,7 +407,8 @@ If the user types `/context-save resume` or `/context-save restore`, tell them: ### Step 1: Gather state ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" ``` Collect the current working state: @@ -466,8 +468,9 @@ inject shell metacharacters into any subsequent command. The sanitizer is an allowlist: only `a-z 0-9 - .` survive. ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" CHECKPOINT_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/checkpoints" mkdir -p "$CHECKPOINT_DIR" TIMESTAMP=$(date +%Y%m%d-%H%M%S) @@ -553,8 +556,9 @@ Restore later with /context-restore. ### Step 1: Gather saved contexts ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" CHECKPOINT_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/checkpoints" if [ -d "$CHECKPOINT_DIR" ]; then echo "CHECKPOINT_DIR=$CHECKPOINT_DIR" diff --git a/context-save/SKILL.md.tmpl b/context-save/SKILL.md.tmpl index 6eaa19666..40cae5fcd 100644 --- a/context-save/SKILL.md.tmpl +++ b/context-save/SKILL.md.tmpl @@ -118,7 +118,7 @@ allowlist: only `a-z 0-9 - .` survive. ```bash {{SLUG_SETUP}} -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" CHECKPOINT_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/checkpoints" mkdir -p "$CHECKPOINT_DIR" TIMESTAMP=$(date +%Y%m%d-%H%M%S) @@ -205,7 +205,7 @@ Restore later with /context-restore. ```bash {{SLUG_SETUP}} -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" CHECKPOINT_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/checkpoints" if [ -d "$CHECKPOINT_DIR" ]; then echo "CHECKPOINT_DIR=$CHECKPOINT_DIR" diff --git a/design-consultation/SKILL.md b/design-consultation/SKILL.md index 2088b776c..09667b1aa 100644 --- a/design-consultation/SKILL.md +++ b/design-consultation/SKILL.md @@ -262,7 +262,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -372,7 +373,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -477,9 +479,10 @@ A `PRODUCT.md` (impeccable's product-context file) already answers the product q Look for office-hours output: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5 +ls "$GSTACK_STATE_ROOT"/projects/$SLUG/*office-hours* 2>/dev/null | head -5 ls .context/*office-hours* .context/attachments/*office-hours* 2>/dev/null | head -5 ``` diff --git a/design-consultation/SKILL.md.tmpl b/design-consultation/SKILL.md.tmpl index 5d704aa5f..100025a3b 100644 --- a/design-consultation/SKILL.md.tmpl +++ b/design-consultation/SKILL.md.tmpl @@ -89,9 +89,10 @@ A `PRODUCT.md` (impeccable's product-context file) already answers the product q Look for office-hours output: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat {{SLUG_EVAL}} -ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5 +ls "$GSTACK_STATE_ROOT"/projects/$SLUG/*office-hours* 2>/dev/null | head -5 ls .context/*office-hours* .context/attachments/*office-hours* 2>/dev/null | head -5 ``` diff --git a/design-consultation/sections/proposal-and-preview.md b/design-consultation/sections/proposal-and-preview.md index bf0d3aacd..964235917 100644 --- a/design-consultation/sections/proposal-and-preview.md +++ b/design-consultation/sections/proposal-and-preview.md @@ -294,7 +294,7 @@ Apply the proposed system to realistic product screens: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/design-system-$(date +%Y%m%d)" mkdir -p "$_DESIGN_DIR" echo "DESIGN_DIR: $_DESIGN_DIR" diff --git a/design-consultation/sections/proposal-and-preview.md.tmpl b/design-consultation/sections/proposal-and-preview.md.tmpl index b4948c81d..61c22429e 100644 --- a/design-consultation/sections/proposal-and-preview.md.tmpl +++ b/design-consultation/sections/proposal-and-preview.md.tmpl @@ -102,7 +102,7 @@ Apply the proposed system to realistic product screens: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/design-system-$(date +%Y%m%d)" mkdir -p "$_DESIGN_DIR" echo "DESIGN_DIR: $_DESIGN_DIR" diff --git a/design-html/SKILL.md b/design-html/SKILL.md index 0e7e5a45f..aaa45be9b 100644 --- a/design-html/SKILL.md +++ b/design-html/SKILL.md @@ -243,7 +243,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -457,7 +458,7 @@ Detect context with the CEO-plan check and the design-artifact checks below: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true _CEO=$(ls -t "$GSTACK_STATE_ROOT/projects/$SLUG/ceo-plans/"*.md 2>/dev/null | head -1) [ -n "$_CEO" ] && echo "CEO_PLAN: $_CEO" || echo "NO_CEO_PLAN" @@ -465,7 +466,7 @@ _CEO=$(ls -t "$GSTACK_STATE_ROOT/projects/$SLUG/ceo-plans/"*.md 2>/dev/null | he ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true _APPROVED=$(ls -t "$GSTACK_STATE_ROOT/projects/$SLUG/designs/"*/approved.json 2>/dev/null | head -1) [ -n "$_APPROVED" ] && echo "APPROVED: $_APPROVED" || echo "NO_APPROVED" diff --git a/design-html/SKILL.md.tmpl b/design-html/SKILL.md.tmpl index d62c7f963..11eeb0d3a 100644 --- a/design-html/SKILL.md.tmpl +++ b/design-html/SKILL.md.tmpl @@ -59,7 +59,7 @@ Detect context with the CEO-plan check and the design-artifact checks below: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true _CEO=$(ls -t "$GSTACK_STATE_ROOT/projects/$SLUG/ceo-plans/"*.md 2>/dev/null | head -1) [ -n "$_CEO" ] && echo "CEO_PLAN: $_CEO" || echo "NO_CEO_PLAN" @@ -67,7 +67,7 @@ _CEO=$(ls -t "$GSTACK_STATE_ROOT/projects/$SLUG/ceo-plans/"*.md 2>/dev/null | he ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true _APPROVED=$(ls -t "$GSTACK_STATE_ROOT/projects/$SLUG/designs/"*/approved.json 2>/dev/null | head -1) [ -n "$_APPROVED" ] && echo "APPROVED: $_APPROVED" || echo "NO_APPROVED" diff --git a/design-review/SKILL.md b/design-review/SKILL.md index 3f58096bf..b03a1dd45 100644 --- a/design-review/SKILL.md +++ b/design-review/SKILL.md @@ -240,7 +240,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -350,7 +351,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -821,7 +823,7 @@ On **B**, continue without scans. On **C**, run `~/.claude/skills/gstack/bin/gst ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" REPORT_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/design-audit-$(date +%Y%m%d)" RUN_ID="$(date +%H%M%S)-$$" mkdir -p "$REPORT_DIR/screenshots" "$REPORT_DIR/dom/$RUN_ID" @@ -1866,9 +1868,10 @@ Write the report to `$REPORT_DIR` (already set up in the setup phase): **Also write a summary to the project index:** ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" ``` -Write a one-line summary to `~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md` with a pointer to the full report in `$REPORT_DIR`. +Write a one-line summary to `/{user}-{branch}-design-audit-{datetime}.md` (`PROJECT_DIR` printed above) with a pointer to the full report in `$REPORT_DIR`. **Per-finding additions** (beyond standard design audit report): - Fix Status: verified / best-effort / reverted / deferred diff --git a/design-review/SKILL.md.tmpl b/design-review/SKILL.md.tmpl index d73394c88..49f26241d 100644 --- a/design-review/SKILL.md.tmpl +++ b/design-review/SKILL.md.tmpl @@ -96,7 +96,7 @@ If `DESIGN_NOT_AVAILABLE`: skip mockup generation — the fix loop works without ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" REPORT_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/design-audit-$(date +%Y%m%d)" RUN_ID="$(date +%H%M%S)-$$" mkdir -p "$REPORT_DIR/screenshots" "$REPORT_DIR/dom/$RUN_ID" @@ -288,7 +288,7 @@ Write the report to `$REPORT_DIR` (already set up in the setup phase): ```bash {{SLUG_SETUP}} ``` -Write a one-line summary to `~/.gstack/projects/{slug}/{user}-{branch}-design-audit-{datetime}.md` with a pointer to the full report in `$REPORT_DIR`. +Write a one-line summary to `/{user}-{branch}-design-audit-{datetime}.md` (`PROJECT_DIR` printed above) with a pointer to the full report in `$REPORT_DIR`. **Per-finding additions** (beyond standard design audit report): - Fix Status: verified / best-effort / reverted / deferred diff --git a/design-shotgun/SKILL.md b/design-shotgun/SKILL.md index c8c7205e5..4241cc7f3 100644 --- a/design-shotgun/SKILL.md +++ b/design-shotgun/SKILL.md @@ -257,7 +257,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -456,7 +457,7 @@ Check for prior design exploration sessions for this project: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true _PREV=$(find "$GSTACK_STATE_ROOT/projects/$SLUG/designs/" -name "approved.json" -maxdepth 2 2>/dev/null | sort -r | head -5) [ -n "$_PREV" ] && echo "PREVIOUS_SESSIONS_FOUND" || echo "NO_PREVIOUS_SESSIONS" @@ -512,8 +513,9 @@ ls src/ app/ pages/ components/ 2>/dev/null | head -30 ``` ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true -ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5 +ls "$GSTACK_STATE_ROOT"/projects/$SLUG/*office-hours* 2>/dev/null | head -5 ``` If DESIGN.md exists, tell the user: "I'll follow your design system in DESIGN.md by @@ -592,7 +594,7 @@ will migrate it to schema v1 on the next write. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true _TASTE=$(find "$GSTACK_STATE_ROOT/projects/$SLUG/designs/" -name "approved.json" -maxdepth 2 2>/dev/null | sort -r | head -10) ``` @@ -615,7 +617,7 @@ Set up the output directory: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/-$(date +%Y%m%d)" mkdir -p "$_DESIGN_DIR" echo "DESIGN_DIR: $_DESIGN_DIR" diff --git a/design-shotgun/SKILL.md.tmpl b/design-shotgun/SKILL.md.tmpl index e5e60b3b3..07cee2764 100644 --- a/design-shotgun/SKILL.md.tmpl +++ b/design-shotgun/SKILL.md.tmpl @@ -66,7 +66,7 @@ Check for prior design exploration sessions for this project: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true _PREV=$(find "$GSTACK_STATE_ROOT/projects/$SLUG/designs/" -name "approved.json" -maxdepth 2 2>/dev/null | sort -r | head -5) [ -n "$_PREV" ] && echo "PREVIOUS_SESSIONS_FOUND" || echo "NO_PREVIOUS_SESSIONS" @@ -122,8 +122,9 @@ ls src/ app/ pages/ components/ 2>/dev/null | head -30 ``` ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true -ls ~/.gstack/projects/$SLUG/*office-hours* 2>/dev/null | head -5 +ls "$GSTACK_STATE_ROOT"/projects/$SLUG/*office-hours* 2>/dev/null | head -5 ``` If DESIGN.md exists, tell the user: "I'll follow your design system in DESIGN.md by @@ -165,7 +166,7 @@ designs to bias generation toward the user's demonstrated taste. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true _TASTE=$(find "$GSTACK_STATE_ROOT/projects/$SLUG/designs/" -name "approved.json" -maxdepth 2 2>/dev/null | sort -r | head -10) ``` @@ -188,7 +189,7 @@ Set up the output directory: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/-$(date +%Y%m%d)" mkdir -p "$_DESIGN_DIR" echo "DESIGN_DIR: $_DESIGN_DIR" diff --git a/devex-review/SKILL.md b/devex-review/SKILL.md index 3251bca55..8305db0b2 100644 --- a/devex-review/SKILL.md +++ b/devex-review/SKILL.md @@ -242,7 +242,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -352,7 +353,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol diff --git a/document-generate/SKILL.md b/document-generate/SKILL.md index 40d3a5af6..e2cb4324f 100644 --- a/document-generate/SKILL.md +++ b/document-generate/SKILL.md @@ -242,7 +242,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/document-release/SKILL.md b/document-release/SKILL.md index c832c6ead..43f702aff 100644 --- a/document-release/SKILL.md +++ b/document-release/SKILL.md @@ -240,7 +240,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/freeze/SKILL.md b/freeze/SKILL.md index 6ed7115fe..d77c65f45 100644 --- a/freeze/SKILL.md +++ b/freeze/SKILL.md @@ -41,8 +41,9 @@ Lock file edits to a specific directory. Any Edit or Write operation targeting a file outside the allowed path will be **blocked** (not just warned). ```bash -mkdir -p ~/.gstack/analytics -echo '{"skill":"freeze","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 +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/analytics +echo '{"skill":"freeze","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_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true ``` ## Setup diff --git a/freeze/SKILL.md.tmpl b/freeze/SKILL.md.tmpl index df8eaf433..bd58c167a 100644 --- a/freeze/SKILL.md.tmpl +++ b/freeze/SKILL.md.tmpl @@ -36,8 +36,9 @@ Lock file edits to a specific directory. Any Edit or Write operation targeting a file outside the allowed path will be **blocked** (not just warned). ```bash -mkdir -p ~/.gstack/analytics -echo '{"skill":"freeze","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 +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/analytics +echo '{"skill":"freeze","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_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true ``` ## Setup diff --git a/gstack-upgrade/SKILL.md b/gstack-upgrade/SKILL.md index 88579d50c..42d767189 100644 --- a/gstack-upgrade/SKILL.md +++ b/gstack-upgrade/SKILL.md @@ -58,7 +58,8 @@ Tell user: "Auto-upgrade enabled. Future updates will install automatically." Th **If "Not now":** Write snooze state with escalating backoff (first snooze = 24h, second = 48h, third+ = 1 week), then continue with the current skill. Do not mention the upgrade again. ```bash -_SNOOZE_FILE="$HOME/.gstack/update-snoozed" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_SNOOZE_FILE="$GSTACK_STATE_ROOT/update-snoozed" _REMOTE_VER="{new}" _CUR_LEVEL=0 if [ -f "$_SNOOZE_FILE" ]; then @@ -328,10 +329,11 @@ running. Interpret the `DAEMON_CHECK` result: ### Step 5: Write marker + clear cache ```bash -mkdir -p ~/.gstack -echo "$OLD_VERSION" > ~/.gstack/just-upgraded-from -rm -f ~/.gstack/last-update-check -rm -f ~/.gstack/update-snoozed +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT" +echo "$OLD_VERSION" > "$GSTACK_STATE_ROOT"/just-upgraded-from +rm -f "$GSTACK_STATE_ROOT"/last-update-check +rm -f "$GSTACK_STATE_ROOT"/update-snoozed ``` ### Step 6: Show What's New diff --git a/gstack-upgrade/SKILL.md.tmpl b/gstack-upgrade/SKILL.md.tmpl index 3032c48f3..70a030f6e 100644 --- a/gstack-upgrade/SKILL.md.tmpl +++ b/gstack-upgrade/SKILL.md.tmpl @@ -55,7 +55,8 @@ Tell user: "Auto-upgrade enabled. Future updates will install automatically." Th **If "Not now":** Write snooze state with escalating backoff (first snooze = 24h, second = 48h, third+ = 1 week), then continue with the current skill. Do not mention the upgrade again. ```bash -_SNOOZE_FILE="$HOME/.gstack/update-snoozed" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_SNOOZE_FILE="$GSTACK_STATE_ROOT/update-snoozed" _REMOTE_VER="{new}" _CUR_LEVEL=0 if [ -f "$_SNOOZE_FILE" ]; then @@ -325,10 +326,11 @@ running. Interpret the `DAEMON_CHECK` result: ### Step 5: Write marker + clear cache ```bash -mkdir -p ~/.gstack -echo "$OLD_VERSION" > ~/.gstack/just-upgraded-from -rm -f ~/.gstack/last-update-check -rm -f ~/.gstack/update-snoozed +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT" +echo "$OLD_VERSION" > "$GSTACK_STATE_ROOT"/just-upgraded-from +rm -f "$GSTACK_STATE_ROOT"/last-update-check +rm -f "$GSTACK_STATE_ROOT"/update-snoozed ``` ### Step 6: Show What's New diff --git a/guard/SKILL.md b/guard/SKILL.md index ab16b395b..b322d7245 100644 --- a/guard/SKILL.md +++ b/guard/SKILL.md @@ -49,8 +49,9 @@ and `/freeze` skill directories. Both must be installed (they are installed toge by the gstack setup script). ```bash -mkdir -p ~/.gstack/analytics -echo '{"skill":"guard","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 +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/analytics +echo '{"skill":"guard","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_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true ``` ## Setup diff --git a/guard/SKILL.md.tmpl b/guard/SKILL.md.tmpl index 2f3ee990e..c9955828f 100644 --- a/guard/SKILL.md.tmpl +++ b/guard/SKILL.md.tmpl @@ -45,8 +45,9 @@ and `/freeze` skill directories. Both must be installed (they are installed toge by the gstack setup script). ```bash -mkdir -p ~/.gstack/analytics -echo '{"skill":"guard","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 +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/analytics +echo '{"skill":"guard","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_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true ``` ## Setup diff --git a/health/SKILL.md b/health/SKILL.md index edfd466e0..3bd0466ab 100644 --- a/health/SKILL.md +++ b/health/SKILL.md @@ -238,7 +238,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -630,7 +631,8 @@ DETAILS: Lint (3 warnings) ## Step 5: Persist to Health History ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" ``` Only when a numeric composite exists, append one JSONL line to @@ -660,8 +662,10 @@ Read the last 10 entries from `~/.gstack/projects/$SLUG/health-history.jsonl` (i file exists and has prior entries). ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG -tail -10 ~/.gstack/projects/$SLUG/health-history.jsonl 2>/dev/null || echo "NO_HISTORY" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" +tail -10 "$GSTACK_STATE_ROOT"/projects/$SLUG/health-history.jsonl 2>/dev/null || echo "NO_HISTORY" ``` **Compare like-for-like coverage.** For each history row, form the set of categories diff --git a/health/SKILL.md.tmpl b/health/SKILL.md.tmpl index 0bca08478..1f490169e 100644 --- a/health/SKILL.md.tmpl +++ b/health/SKILL.md.tmpl @@ -310,8 +310,9 @@ Read the last 10 entries from `~/.gstack/projects/$SLUG/health-history.jsonl` (i file exists and has prior entries). ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" {{SLUG_SETUP}} -tail -10 ~/.gstack/projects/$SLUG/health-history.jsonl 2>/dev/null || echo "NO_HISTORY" +tail -10 "$GSTACK_STATE_ROOT"/projects/$SLUG/health-history.jsonl 2>/dev/null || echo "NO_HISTORY" ``` **Compare like-for-like coverage.** For each history row, form the set of categories diff --git a/investigate/SKILL.md b/investigate/SKILL.md index 92203c6f9..ed6e3c65b 100644 --- a/investigate/SKILL.md +++ b/investigate/SKILL.md @@ -277,7 +277,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/ios-clean/SKILL.md b/ios-clean/SKILL.md index 1ad8eebc4..59ae68d41 100644 --- a/ios-clean/SKILL.md +++ b/ios-clean/SKILL.md @@ -240,7 +240,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/ios-design-review/SKILL.md b/ios-design-review/SKILL.md index 89fe68717..e069b839f 100644 --- a/ios-design-review/SKILL.md +++ b/ios-design-review/SKILL.md @@ -242,7 +242,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/ios-fix/SKILL.md b/ios-fix/SKILL.md index c45b7e228..3631e8f0d 100644 --- a/ios-fix/SKILL.md +++ b/ios-fix/SKILL.md @@ -243,7 +243,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/ios-qa/SKILL.md b/ios-qa/SKILL.md index 1e2596c4f..155b00d29 100644 --- a/ios-qa/SKILL.md +++ b/ios-qa/SKILL.md @@ -246,7 +246,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -356,7 +357,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -460,7 +462,8 @@ UDID, tunnel address, and accessor hash. Invalidate the cache when: - The daemon reports the cached UDID is no longer connected. ```bash -SESSION="$HOME/.gstack/ios-qa-session.json" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +SESSION="$GSTACK_STATE_ROOT/ios-qa-session.json" if [ -f "$SESSION" ] && [ "$COLD" != "1" ]; then CACHED_UDID=$(python3 -c "import json,os; d=json.load(open(os.path.expanduser('$SESSION'))); print(d['udid'])") CACHED_PORT=$(python3 -c "import json,os; d=json.load(open(os.path.expanduser('$SESSION'))); print(d['daemon_port'])") diff --git a/ios-qa/SKILL.md.tmpl b/ios-qa/SKILL.md.tmpl index 2e147c5ae..cafb47f4b 100644 --- a/ios-qa/SKILL.md.tmpl +++ b/ios-qa/SKILL.md.tmpl @@ -84,7 +84,8 @@ UDID, tunnel address, and accessor hash. Invalidate the cache when: - The daemon reports the cached UDID is no longer connected. ```bash -SESSION="$HOME/.gstack/ios-qa-session.json" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +SESSION="$GSTACK_STATE_ROOT/ios-qa-session.json" if [ -f "$SESSION" ] && [ "$COLD" != "1" ]; then CACHED_UDID=$(python3 -c "import json,os; d=json.load(open(os.path.expanduser('$SESSION'))); print(d['udid'])") CACHED_PORT=$(python3 -c "import json,os; d=json.load(open(os.path.expanduser('$SESSION'))); print(d['daemon_port'])") diff --git a/ios-sync/SKILL.md b/ios-sync/SKILL.md index 1eb47a5aa..cdf5011fa 100644 --- a/ios-sync/SKILL.md +++ b/ios-sync/SKILL.md @@ -240,7 +240,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/land-and-deploy/SKILL.md b/land-and-deploy/SKILL.md index 905b2afee..888e329fa 100644 --- a/land-and-deploy/SKILL.md +++ b/land-and-deploy/SKILL.md @@ -235,7 +235,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -345,7 +346,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -658,12 +660,13 @@ Unknown scope is not docs-only. `DOCS_ONLY=true` requires SCOPE_DOCS and no othe Check for prior setup confirmation and changed configuration (not proof of a successful deploy): ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -if [ ! -f ~/.gstack/projects/$SLUG/land-deploy-confirmed ]; then +if [ ! -f "$GSTACK_STATE_ROOT"/projects/$SLUG/land-deploy-confirmed ]; then echo "FIRST_RUN" else # Check if deploy config has changed since confirmation - SAVED_HASH=$(cat ~/.gstack/projects/$SLUG/land-deploy-confirmed 2>/dev/null) + SAVED_HASH=$(cat "$GSTACK_STATE_ROOT"/projects/$SLUG/land-deploy-confirmed 2>/dev/null) CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1) # Also hash workflow files that affect deploy behavior WORKFLOW_HASH=$(find .github/workflows -maxdepth 1 \( -name '*deploy*' -o -name '*cd*' \) 2>/dev/null | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) @@ -995,9 +998,10 @@ is passed/skipped/not-needed; inline fixes stopped before merge and cannot appea For rollback include revert SHA or PR URL and unresolved work. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" mkdir -p .gstack/deploy-reports eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -mkdir -p ~/.gstack/projects/$SLUG +mkdir -p "$GSTACK_STATE_ROOT"/projects/$SLUG ``` Pass one JSON entry to `~/.claude/skills/gstack/bin/gstack-review-log ''` for diff --git a/land-and-deploy/SKILL.md.tmpl b/land-and-deploy/SKILL.md.tmpl index 22205a256..724645497 100644 --- a/land-and-deploy/SKILL.md.tmpl +++ b/land-and-deploy/SKILL.md.tmpl @@ -120,12 +120,13 @@ Unknown scope is not docs-only. `DOCS_ONLY=true` requires SCOPE_DOCS and no othe Check for prior setup confirmation and changed configuration (not proof of a successful deploy): ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" {{SLUG_EVAL}} -if [ ! -f ~/.gstack/projects/$SLUG/land-deploy-confirmed ]; then +if [ ! -f "$GSTACK_STATE_ROOT"/projects/$SLUG/land-deploy-confirmed ]; then echo "FIRST_RUN" else # Check if deploy config has changed since confirmation - SAVED_HASH=$(cat ~/.gstack/projects/$SLUG/land-deploy-confirmed 2>/dev/null) + SAVED_HASH=$(cat "$GSTACK_STATE_ROOT"/projects/$SLUG/land-deploy-confirmed 2>/dev/null) CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1) # Also hash workflow files that affect deploy behavior WORKFLOW_HASH=$(find .github/workflows -maxdepth 1 \( -name '*deploy*' -o -name '*cd*' \) 2>/dev/null | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) @@ -454,9 +455,10 @@ is passed/skipped/not-needed; inline fixes stopped before merge and cannot appea For rollback include revert SHA or PR URL and unresolved work. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" mkdir -p .gstack/deploy-reports {{SLUG_EVAL}} -mkdir -p ~/.gstack/projects/$SLUG +mkdir -p "$GSTACK_STATE_ROOT"/projects/$SLUG ``` Pass one JSON entry to `~/.claude/skills/gstack/bin/gstack-review-log ''` for diff --git a/land-and-deploy/sections/first-run-validation.md b/land-and-deploy/sections/first-run-validation.md index fa9414544..392a52bf9 100644 --- a/land-and-deploy/sections/first-run-validation.md +++ b/land-and-deploy/sections/first-run-validation.md @@ -172,11 +172,12 @@ and still refresh deployment facts before each merge approval." Save the deploy config fingerprint so we can detect future changes: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -mkdir -p ~/.gstack/projects/$SLUG +mkdir -p "$GSTACK_STATE_ROOT"/projects/$SLUG CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1) WORKFLOW_HASH=$(find .github/workflows -maxdepth 1 \( -name '*deploy*' -o -name '*cd*' \) 2>/dev/null | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) -echo "${CURRENT_HASH}-${WORKFLOW_HASH}" > ~/.gstack/projects/$SLUG/land-deploy-confirmed +echo "${CURRENT_HASH}-${WORKFLOW_HASH}" > "$GSTACK_STATE_ROOT"/projects/$SLUG/land-deploy-confirmed ``` Continue to Step 2. diff --git a/land-and-deploy/sections/first-run-validation.md.tmpl b/land-and-deploy/sections/first-run-validation.md.tmpl index bf7b87869..cb0d9cd3c 100644 --- a/land-and-deploy/sections/first-run-validation.md.tmpl +++ b/land-and-deploy/sections/first-run-validation.md.tmpl @@ -134,11 +134,12 @@ and still refresh deployment facts before each merge approval." Save the deploy config fingerprint so we can detect future changes: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" {{SLUG_EVAL}} -mkdir -p ~/.gstack/projects/$SLUG +mkdir -p "$GSTACK_STATE_ROOT"/projects/$SLUG CURRENT_HASH=$(sed -n '/## Deploy Configuration/,/^## /p' CLAUDE.md 2>/dev/null | shasum -a 256 | cut -d' ' -f1) WORKFLOW_HASH=$(find .github/workflows -maxdepth 1 \( -name '*deploy*' -o -name '*cd*' \) 2>/dev/null | xargs cat 2>/dev/null | shasum -a 256 | cut -d' ' -f1) -echo "${CURRENT_HASH}-${WORKFLOW_HASH}" > ~/.gstack/projects/$SLUG/land-deploy-confirmed +echo "${CURRENT_HASH}-${WORKFLOW_HASH}" > "$GSTACK_STATE_ROOT"/projects/$SLUG/land-deploy-confirmed ``` Continue to Step 2. diff --git a/landing-report/SKILL.md b/landing-report/SKILL.md index baf5d02b5..7a847cff6 100644 --- a/landing-report/SKILL.md +++ b/landing-report/SKILL.md @@ -237,7 +237,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/learn/SKILL.md b/learn/SKILL.md index 65f1bc32f..a0862e7a6 100644 --- a/learn/SKILL.md +++ b/learn/SKILL.md @@ -238,7 +238,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -491,7 +492,7 @@ Show summary statistics about the project's learnings. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" LEARN_FILE="$GSTACK_STATE_ROOT/projects/$SLUG/learnings.jsonl" if [ -f "$LEARN_FILE" ]; then TOTAL=$(wc -l < "$LEARN_FILE" | tr -d ' ') diff --git a/learn/SKILL.md.tmpl b/learn/SKILL.md.tmpl index 90d08d229..bb66d2a88 100644 --- a/learn/SKILL.md.tmpl +++ b/learn/SKILL.md.tmpl @@ -141,7 +141,7 @@ Show summary statistics about the project's learnings. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" LEARN_FILE="$GSTACK_STATE_ROOT/projects/$SLUG/learnings.jsonl" if [ -f "$LEARN_FILE" ]; then TOTAL=$(wc -l < "$LEARN_FILE" | tr -d ' ') diff --git a/office-hours/SKILL.md b/office-hours/SKILL.md index f966d7c63..22b1c45fc 100644 --- a/office-hours/SKILL.md +++ b/office-hours/SKILL.md @@ -273,7 +273,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -383,7 +384,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -533,8 +535,9 @@ eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" 3. Use Grep/Glob to map the codebase areas most relevant to the user's request. 4. **List existing design docs for this project:** ```bash + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat - ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null + ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-design-*.md 2>/dev/null ``` If design docs exist, list them: "Prior designs for this project: [titles + dates]" @@ -642,8 +645,9 @@ After the user states the problem (first question in Phase 2A or 2B), search exi Extract 3-5 significant keywords from the user's problem statement and grep across design docs: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat -grep -li "\|\|" ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null +grep -li "\|\|" "$GSTACK_STATE_ROOT"/projects/$SLUG/*-design-*.md 2>/dev/null ``` If matches found, read the matching design docs and surface them: diff --git a/office-hours/SKILL.md.tmpl b/office-hours/SKILL.md.tmpl index 898a77b77..cd674c9eb 100644 --- a/office-hours/SKILL.md.tmpl +++ b/office-hours/SKILL.md.tmpl @@ -86,8 +86,9 @@ Understand the project and the area the user wants to change. 3. Use Grep/Glob to map the codebase areas most relevant to the user's request. 4. **List existing design docs for this project:** ```bash + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat - ls -t ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null + ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-design-*.md 2>/dev/null ``` If design docs exist, list them: "Prior designs for this project: [titles + dates]" @@ -148,8 +149,9 @@ After the user states the problem (first question in Phase 2A or 2B), search exi Extract 3-5 significant keywords from the user's problem statement and grep across design docs: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat -grep -li "\|\|" ~/.gstack/projects/$SLUG/*-design-*.md 2>/dev/null +grep -li "\|\|" "$GSTACK_STATE_ROOT"/projects/$SLUG/*-design-*.md 2>/dev/null ``` If matches found, read the matching design docs and surface them: diff --git a/office-hours/sections/design-and-handoff.md b/office-hours/sections/design-and-handoff.md index de78c5761..71a0576fe 100644 --- a/office-hours/sections/design-and-handoff.md +++ b/office-hours/sections/design-and-handoff.md @@ -5,19 +5,21 @@ Write the design document to the project directory. ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) ``` **Design lineage:** Before writing, check for existing design docs on this branch: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat -PRIOR=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) +PRIOR=$(ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) ``` If `$PRIOR` exists, the new doc gets a `Supersedes:` field referencing it. This creates a revision chain — you can trace how a design evolved across office hours sessions. -Write to `~/.gstack/projects/{slug}/{user}-{branch}-design-{datetime}.md`. +Write to `/{user}-{branch}-design-{datetime}.md` (`PROJECT_DIR` printed by the setup block above). **Repo copy (dual-write, #703 + #2000).** When the session runs inside a git repository, ALSO write the doc to `docs/designs/{topic-slug}.md` in the repo — @@ -450,7 +452,7 @@ This must feel earned, not broadcast. If the evidence doesn't support it, skip e with a narrative arc (not a data table). The arc tells the STORY of their journey in second person, referencing specific things they said across sessions. Then open it: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" open "$GSTACK_STATE_ROOT/builder-journey.md" ``` @@ -582,8 +584,9 @@ eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null || true)" 2. Log the selection to analytics: ```bash -mkdir -p ~/.gstack/analytics -echo '{"skill":"office-hours","event":"resources_shown","count":NUM_RESOURCES,"categories":"CAT1,CAT2","ts":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/analytics +echo '{"skill":"office-hours","event":"resources_shown","count":NUM_RESOURCES,"categories":"CAT1,CAT2","ts":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}' >> "$GSTACK_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true ``` 3. Use AskUserQuestion to offer opening the resources: diff --git a/office-hours/sections/design-and-handoff.md.tmpl b/office-hours/sections/design-and-handoff.md.tmpl index 5924dd706..0f74cafe8 100644 --- a/office-hours/sections/design-and-handoff.md.tmpl +++ b/office-hours/sections/design-and-handoff.md.tmpl @@ -10,12 +10,13 @@ DATETIME=$(date +%Y%m%d-%H%M%S) **Design lineage:** Before writing, check for existing design docs on this branch: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat -PRIOR=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) +PRIOR=$(ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-$BRANCH-design-*.md 2>/dev/null | head -1) ``` If `$PRIOR` exists, the new doc gets a `Supersedes:` field referencing it. This creates a revision chain — you can trace how a design evolved across office hours sessions. -Write to `~/.gstack/projects/{slug}/{user}-{branch}-design-{datetime}.md`. +Write to `/{user}-{branch}-design-{datetime}.md` (`PROJECT_DIR` printed by the setup block above). **Repo copy (dual-write, #703 + #2000).** When the session runs inside a git repository, ALSO write the doc to `docs/designs/{topic-slug}.md` in the repo — @@ -316,7 +317,7 @@ This must feel earned, not broadcast. If the evidence doesn't support it, skip e with a narrative arc (not a data table). The arc tells the STORY of their journey in second person, referencing specific things they said across sessions. Then open it: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" open "$GSTACK_STATE_ROOT/builder-journey.md" ``` @@ -448,8 +449,9 @@ eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null || true)" 2. Log the selection to analytics: ```bash -mkdir -p ~/.gstack/analytics -echo '{"skill":"office-hours","event":"resources_shown","count":NUM_RESOURCES,"categories":"CAT1,CAT2","ts":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/analytics +echo '{"skill":"office-hours","event":"resources_shown","count":NUM_RESOURCES,"categories":"CAT1,CAT2","ts":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}' >> "$GSTACK_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true ``` 3. Use AskUserQuestion to offer opening the resources: diff --git a/open-gstack-browser/SKILL.md b/open-gstack-browser/SKILL.md index 0b6d71d8a..22efff582 100644 --- a/open-gstack-browser/SKILL.md +++ b/open-gstack-browser/SKILL.md @@ -208,6 +208,7 @@ may have persisted from a crash. This prevents "already connected" false positives and Chromium profile lock conflicts. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" # Kill any existing browse server if [ -f "$(git rev-parse --show-toplevel 2>/dev/null)/.gstack/browse.json" ]; then _OLD_PID=$(cat "$(git rev-parse --show-toplevel)/.gstack/browse.json" 2>/dev/null | grep -o '"pid":[[:space:]]*[0-9]*' | grep -o '[0-9]*') @@ -217,7 +218,7 @@ if [ -f "$(git rev-parse --show-toplevel 2>/dev/null)/.gstack/browse.json" ]; th rm -f "$(git rev-parse --show-toplevel)/.gstack/browse.json" fi # Clean Chromium profile locks (can persist after crashes) -_PROFILE_DIR="$HOME/.gstack/chromium-profile" +_PROFILE_DIR="$GSTACK_STATE_ROOT/chromium-profile" for _LF in SingletonLock SingletonSocket SingletonCookie; do rm -f "$_PROFILE_DIR/$_LF" 2>/dev/null || true done diff --git a/open-gstack-browser/SKILL.md.tmpl b/open-gstack-browser/SKILL.md.tmpl index bb6b8893d..5ea932ea7 100644 --- a/open-gstack-browser/SKILL.md.tmpl +++ b/open-gstack-browser/SKILL.md.tmpl @@ -37,6 +37,7 @@ may have persisted from a crash. This prevents "already connected" false positives and Chromium profile lock conflicts. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" # Kill any existing browse server if [ -f "$(git rev-parse --show-toplevel 2>/dev/null)/.gstack/browse.json" ]; then _OLD_PID=$(cat "$(git rev-parse --show-toplevel)/.gstack/browse.json" 2>/dev/null | grep -o '"pid":[[:space:]]*[0-9]*' | grep -o '[0-9]*') @@ -46,7 +47,7 @@ if [ -f "$(git rev-parse --show-toplevel 2>/dev/null)/.gstack/browse.json" ]; th rm -f "$(git rev-parse --show-toplevel)/.gstack/browse.json" fi # Clean Chromium profile locks (can persist after crashes) -_PROFILE_DIR="$HOME/.gstack/chromium-profile" +_PROFILE_DIR="$GSTACK_STATE_ROOT/chromium-profile" for _LF in SingletonLock SingletonSocket SingletonCookie; do rm -f "$_PROFILE_DIR/$_LF" 2>/dev/null || true done diff --git a/pair-agent/SKILL.md b/pair-agent/SKILL.md index 154975b2f..04f805b64 100644 --- a/pair-agent/SKILL.md +++ b/pair-agent/SKILL.md @@ -239,7 +239,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/plan-ceo-review/SKILL.md b/plan-ceo-review/SKILL.md index 28e9c767d..27eec7f80 100644 --- a/plan-ceo-review/SKILL.md +++ b/plan-ceo-review/SKILL.md @@ -258,7 +258,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -368,7 +369,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -572,8 +574,9 @@ Read any `/office-hours` design doc as the problem, constraints and approach sou **Handoff note check** (reuses $SLUG and $BRANCH from the design doc check above): ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat -HANDOFF=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1) +HANDOFF=$(ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1) [ -n "$HANDOFF" ] && echo "HANDOFF_FOUND: $HANDOFF" || echo "NO_HANDOFF" ``` In a separate shell, first recompute $SLUG and $BRANCH with the design-doc commands. @@ -1119,7 +1122,7 @@ summary; the summary cannot serve as the plan. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" CEO_PLANS="$GSTACK_STATE_ROOT/projects/$SLUG/ceo-plans" mkdir -p "$CEO_PLANS" echo "CEO_PLANS=$CEO_PLANS" diff --git a/plan-ceo-review/SKILL.md.tmpl b/plan-ceo-review/SKILL.md.tmpl index a4f877f39..ad2bf3bc7 100644 --- a/plan-ceo-review/SKILL.md.tmpl +++ b/plan-ceo-review/SKILL.md.tmpl @@ -118,8 +118,9 @@ Read any `/office-hours` design doc as the problem, constraints and approach sou **Handoff note check** (reuses $SLUG and $BRANCH from the design doc check above): ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat -HANDOFF=$(ls -t ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1) +HANDOFF=$(ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null | head -1) [ -n "$HANDOFF" ] && echo "HANDOFF_FOUND: $HANDOFF" || echo "NO_HANDOFF" ``` In a separate shell, first recompute $SLUG and $BRANCH with the design-doc commands. @@ -502,7 +503,7 @@ summary; the summary cannot serve as the plan. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" CEO_PLANS="$GSTACK_STATE_ROOT/projects/$SLUG/ceo-plans" mkdir -p "$CEO_PLANS" echo "CEO_PLANS=$CEO_PLANS" diff --git a/plan-ceo-review/sections/review-sections.md b/plan-ceo-review/sections/review-sections.md index dd22c7ac7..91cf656a0 100644 --- a/plan-ceo-review/sections/review-sections.md +++ b/plan-ceo-review/sections/review-sections.md @@ -787,8 +787,9 @@ Rules: backslashes serialize cleanly — never use hand-rolled `echo` / `printf`. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -TASKS_DIR="${HOME}/.gstack/projects/${SLUG:-unknown}" +TASKS_DIR="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" mkdir -p "$TASKS_DIR" TASKS_FILE="$TASKS_DIR/tasks-ceo-review-$(date +%Y%m%d-%H%M%S).jsonl" COMMIT=$(git rev-parse HEAD 2>/dev/null || echo unknown) @@ -1006,10 +1007,11 @@ skip Review Log, success telemetry and the next-skill handoff. After producing the Completion Summary, remove this branch's handoff notes only if the storage policy permits cleanup. Otherwise retain them and report that cleanup was not performed. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat # gstack-slug prints both SLUG and BRANCH; eval sets them in this shell. eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -rm -f ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true +rm -f "$GSTACK_STATE_ROOT"/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true ``` ## Review Log diff --git a/plan-ceo-review/sections/review-sections.md.tmpl b/plan-ceo-review/sections/review-sections.md.tmpl index c613b02b4..4e62799f3 100644 --- a/plan-ceo-review/sections/review-sections.md.tmpl +++ b/plan-ceo-review/sections/review-sections.md.tmpl @@ -525,10 +525,11 @@ skip Review Log, success telemetry and the next-skill handoff. After producing the Completion Summary, remove this branch's handoff notes only if the storage policy permits cleanup. Otherwise retain them and report that cleanup was not performed. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat # gstack-slug prints both SLUG and BRANCH; eval sets them in this shell. {{SLUG_EVAL}} -rm -f ~/.gstack/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true +rm -f "$GSTACK_STATE_ROOT"/projects/$SLUG/*-$BRANCH-ceo-handoff-*.md 2>/dev/null || true ``` ## Review Log diff --git a/plan-design-review/SKILL.md b/plan-design-review/SKILL.md index 48574b18c..bc1edba87 100644 --- a/plan-design-review/SKILL.md +++ b/plan-design-review/SKILL.md @@ -264,7 +264,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -374,7 +375,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -786,7 +788,7 @@ First, set up the output directory. Name it after the screen/feature being desig ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/-$(date +%Y%m%d)" mkdir -p "$_DESIGN_DIR" echo "DESIGN_DIR: $_DESIGN_DIR" @@ -1124,7 +1126,7 @@ offer to generate a visual mockup showing what the improved version would look l ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/ideal-$(date +%Y%m%d)" mkdir -p "$_DESIGN_DIR" _ROOT=$(git rev-parse --show-toplevel 2>/dev/null) diff --git a/plan-design-review/SKILL.md.tmpl b/plan-design-review/SKILL.md.tmpl index db88a19fb..5667fddd9 100644 --- a/plan-design-review/SKILL.md.tmpl +++ b/plan-design-review/SKILL.md.tmpl @@ -224,7 +224,7 @@ First, set up the output directory. Name it after the screen/feature being desig ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/-$(date +%Y%m%d)" mkdir -p "$_DESIGN_DIR" echo "DESIGN_DIR: $_DESIGN_DIR" @@ -298,7 +298,7 @@ offer to generate a visual mockup showing what the improved version would look l ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _DESIGN_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/designs/ideal-$(date +%Y%m%d)" mkdir -p "$_DESIGN_DIR" _ROOT=$(git rev-parse --show-toplevel 2>/dev/null) diff --git a/plan-design-review/sections/review-sections.md b/plan-design-review/sections/review-sections.md index a68fe6138..afde48a36 100644 --- a/plan-design-review/sections/review-sections.md +++ b/plan-design-review/sections/review-sections.md @@ -333,8 +333,9 @@ Rules: backslashes serialize cleanly — never use hand-rolled `echo` / `printf`. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -TASKS_DIR="${HOME}/.gstack/projects/${SLUG:-unknown}" +TASKS_DIR="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" mkdir -p "$TASKS_DIR" TASKS_FILE="$TASKS_DIR/tasks-design-review-$(date +%Y%m%d-%H%M%S).jsonl" COMMIT=$(git rev-parse HEAD 2>/dev/null || echo unknown) diff --git a/plan-devex-review/SKILL.md b/plan-devex-review/SKILL.md index 007d9d1f4..91e520040 100644 --- a/plan-devex-review/SKILL.md +++ b/plan-devex-review/SKILL.md @@ -236,7 +236,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -346,7 +347,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol diff --git a/plan-devex-review/sections/review-sections.md b/plan-devex-review/sections/review-sections.md index 73411184a..4feece5ed 100644 --- a/plan-devex-review/sections/review-sections.md +++ b/plan-devex-review/sections/review-sections.md @@ -700,8 +700,9 @@ Rules: backslashes serialize cleanly — never use hand-rolled `echo` / `printf`. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -TASKS_DIR="${HOME}/.gstack/projects/${SLUG:-unknown}" +TASKS_DIR="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" mkdir -p "$TASKS_DIR" TASKS_FILE="$TASKS_DIR/tasks-devex-review-$(date +%Y%m%d-%H%M%S).jsonl" COMMIT=$(git rev-parse HEAD 2>/dev/null || echo unknown) diff --git a/plan-eng-review/SKILL.md b/plan-eng-review/SKILL.md index 2057da923..1461ab99d 100644 --- a/plan-eng-review/SKILL.md +++ b/plan-eng-review/SKILL.md @@ -284,7 +284,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -394,7 +395,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol diff --git a/plan-eng-review/sections/review-sections.md b/plan-eng-review/sections/review-sections.md index 57c85f4eb..cf8facb51 100644 --- a/plan-eng-review/sections/review-sections.md +++ b/plan-eng-review/sections/review-sections.md @@ -735,14 +735,15 @@ When these test and eval choices are resolved, write the Test Plan Artifact belo After resolving the Test review decisions, record the approved test requirements in an artifact for `/qa` and `/qa-only`. List any unresolved choices separately as pending, not required implementation. Update this artifact if later approved decisions change the tests. Use the Review record and write policy above. ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG # sets SLUG and BRANCH +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" # sets SLUG and BRANCH TEST_PLAN_USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) ``` Use `SLUG` and the sanitized `BRANCH` from gstack-slug, `TEST_PLAN_USER` for {user}, and `DATETIME` for {datetime}. Set {date} to today. Read the local origin URL with `git remote get-url origin` and use its owner/repo; without an origin, write `local-only`. No network request is needed. -Write to `~/.gstack/projects/{slug}/{user}-{branch}-eng-review-test-plan-{datetime}.md`: +Write to `/{user}-{branch}-eng-review-test-plan-{datetime}.md` (`PROJECT_DIR` printed above): ```markdown # Test Plan @@ -1170,8 +1171,9 @@ Rules: backslashes serialize cleanly — never use hand-rolled `echo` / `printf`. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -TASKS_DIR="${HOME}/.gstack/projects/${SLUG:-unknown}" +TASKS_DIR="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" mkdir -p "$TASKS_DIR" TASKS_FILE="$TASKS_DIR/tasks-eng-review-$(date +%Y%m%d-%H%M%S).jsonl" COMMIT=$(git rev-parse HEAD 2>/dev/null || echo unknown) diff --git a/plan-tune/SKILL.md b/plan-tune/SKILL.md index 7606358ec..7067ef4e4 100644 --- a/plan-tune/SKILL.md +++ b/plan-tune/SKILL.md @@ -248,7 +248,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -505,7 +506,8 @@ explicit. 3. ALWAYS touch the marker, regardless of choice: ```bash - touch ~/.gstack/.question-tuning-prompted + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + touch "$GSTACK_STATE_ROOT"/.question-tuning-prompted ``` 4. If A or B: enable: @@ -564,7 +566,7 @@ explicit. # Ensure profile exists ~/.claude/skills/gstack/bin/gstack-developer-profile --read >/dev/null # Update declared dimensions atomically - eval "$(~/.claude/skills/gstack/bin/gstack-paths)" + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _PROFILE="$GSTACK_STATE_ROOT/developer-profile.json" bun -e " const fs = require('fs'); @@ -584,7 +586,8 @@ explicit. 2. Touch the marker so the Setup gate doesn't re-fire: ```bash - touch ~/.gstack/.declared-setup-prompted + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + touch "$GSTACK_STATE_ROOT"/.declared-setup-prompted ``` Touch it even if the user bails out partway — they were asked; they chose not to complete. The Setup gate respects that. They can rerun the 5-Q @@ -640,7 +643,7 @@ Parse the JSON. Present in **plain English**, not raw floats: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _LOG="$GSTACK_STATE_ROOT/projects/$SLUG/question-log.jsonl" if [ ! -f "$_LOG" ]; then echo "NO_LOG" @@ -734,7 +737,7 @@ is a trust boundary (Codex #15 in the design doc). 3. After Y, write: ```bash - eval "$(~/.claude/skills/gstack/bin/gstack-paths)" + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _PROFILE="$GSTACK_STATE_ROOT/developer-profile.json" bun -e " const fs = require('fs'); @@ -780,7 +783,7 @@ cycle cost-to-date. ```bash ~/.claude/skills/gstack/bin/gstack-question-preference --stats eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _LOG="$GSTACK_STATE_ROOT/projects/$SLUG/question-log.jsonl" if [ -f "$_LOG" ]; then bun -e " @@ -832,7 +835,7 @@ any that misfired via `always-ask`. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _LOG="$GSTACK_STATE_ROOT/projects/$SLUG/question-log.jsonl" [ ! -f "$_LOG" ] && echo 'NO_LOG' || bun -e " const lines = require('fs').readFileSync('$_LOG','utf-8').trim().split('\n').filter(Boolean); @@ -864,7 +867,7 @@ adoption: high-traffic unmarked questions are the next candidates to retrofit. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _LOG="$GSTACK_STATE_ROOT/projects/$SLUG/question-log.jsonl" [ ! -f "$_LOG" ] && echo 'NO_LOG' || bun -e " const lines = require('fs').readFileSync('$_LOG','utf-8').trim().split('\n').filter(Boolean); diff --git a/plan-tune/SKILL.md.tmpl b/plan-tune/SKILL.md.tmpl index dc1214d4c..9b0f82077 100644 --- a/plan-tune/SKILL.md.tmpl +++ b/plan-tune/SKILL.md.tmpl @@ -156,7 +156,8 @@ explicit. 3. ALWAYS touch the marker, regardless of choice: ```bash - touch ~/.gstack/.question-tuning-prompted + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + touch "$GSTACK_STATE_ROOT"/.question-tuning-prompted ``` 4. If A or B: enable: @@ -215,7 +216,7 @@ explicit. # Ensure profile exists ~/.claude/skills/gstack/bin/gstack-developer-profile --read >/dev/null # Update declared dimensions atomically - eval "$(~/.claude/skills/gstack/bin/gstack-paths)" + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _PROFILE="$GSTACK_STATE_ROOT/developer-profile.json" bun -e " const fs = require('fs'); @@ -235,7 +236,8 @@ explicit. 2. Touch the marker so the Setup gate doesn't re-fire: ```bash - touch ~/.gstack/.declared-setup-prompted + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + touch "$GSTACK_STATE_ROOT"/.declared-setup-prompted ``` Touch it even if the user bails out partway — they were asked; they chose not to complete. The Setup gate respects that. They can rerun the 5-Q @@ -291,7 +293,7 @@ Parse the JSON. Present in **plain English**, not raw floats: ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _LOG="$GSTACK_STATE_ROOT/projects/$SLUG/question-log.jsonl" if [ ! -f "$_LOG" ]; then echo "NO_LOG" @@ -385,7 +387,7 @@ is a trust boundary (Codex #15 in the design doc). 3. After Y, write: ```bash - eval "$(~/.claude/skills/gstack/bin/gstack-paths)" + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _PROFILE="$GSTACK_STATE_ROOT/developer-profile.json" bun -e " const fs = require('fs'); @@ -431,7 +433,7 @@ cycle cost-to-date. ```bash ~/.claude/skills/gstack/bin/gstack-question-preference --stats eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _LOG="$GSTACK_STATE_ROOT/projects/$SLUG/question-log.jsonl" if [ -f "$_LOG" ]; then bun -e " @@ -483,7 +485,7 @@ any that misfired via `always-ask`. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _LOG="$GSTACK_STATE_ROOT/projects/$SLUG/question-log.jsonl" [ ! -f "$_LOG" ] && echo 'NO_LOG' || bun -e " const lines = require('fs').readFileSync('$_LOG','utf-8').trim().split('\n').filter(Boolean); @@ -515,7 +517,7 @@ adoption: high-traffic unmarked questions are the next candidates to retrofit. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _LOG="$GSTACK_STATE_ROOT/projects/$SLUG/question-log.jsonl" [ ! -f "$_LOG" ] && echo 'NO_LOG' || bun -e " const lines = require('fs').readFileSync('$_LOG','utf-8').trim().split('\n').filter(Boolean); diff --git a/qa-only/SKILL.md b/qa-only/SKILL.md index ff95a7910..5334eec52 100644 --- a/qa-only/SKILL.md +++ b/qa-only/SKILL.md @@ -238,7 +238,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -348,7 +349,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -582,7 +584,7 @@ permissions or fixed paths override them. Do not create a forbidden second copy. Use this session's existing project slug and state directory for the project copy. If unknown or not writable within the supplied permissions, report that copy as blocked; still write the permitted local report. Do not run state-setup helpers. -Write identical content to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md`. +Write identical content to `$GSTACK_STATE_ROOT/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` (the state root this session resolved). Get `{user}`/`{branch}` from `git config user.name`/`git branch --show-current` (fallbacks: `unknown-user`/`detached`); sanitize like `{target}`. Use UTC `YYYYMMDDTHHMMSSZ`. If that destination exists, choose a fresh suffixed filename; never replace a prior report. diff --git a/qa-only/SKILL.md.tmpl b/qa-only/SKILL.md.tmpl index 55169e631..60f7efd9f 100644 --- a/qa-only/SKILL.md.tmpl +++ b/qa-only/SKILL.md.tmpl @@ -177,7 +177,7 @@ permissions or fixed paths override them. Do not create a forbidden second copy. Use this session's existing project slug and state directory for the project copy. If unknown or not writable within the supplied permissions, report that copy as blocked; still write the permitted local report. Do not run state-setup helpers. -Write identical content to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md`. +Write identical content to `$GSTACK_STATE_ROOT/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` (the state root this session resolved). Get `{user}`/`{branch}` from `git config user.name`/`git branch --show-current` (fallbacks: `unknown-user`/`detached`); sanitize like `{target}`. Use UTC `YYYYMMDDTHHMMSSZ`. If that destination exists, choose a fresh suffixed filename; never replace a prior report. diff --git a/qa/SKILL.md b/qa/SKILL.md index ef409b146..fb51c768c 100644 --- a/qa/SKILL.md +++ b/qa/SKILL.md @@ -242,7 +242,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -352,7 +353,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -579,9 +581,10 @@ Prefer the richer of recent project test plans and plans in conversation over gi 1. **Project-scoped test plans:** Find the latest for this repo: ```bash + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" - ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 + ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 ``` 2. **Conversation context:** Prior `/plan-eng-review` or `/plan-ceo-review` test plans. 3. Fall back to git diff only if neither exists. @@ -748,9 +751,10 @@ Write the Output Structure report locally and copy the same content to project c **Project-scoped:** Write test outcome artifact for cross-session context: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` +Write to `/{user}-{branch}-test-outcome-{datetime}.md` (`PROJECT_DIR` printed above) **Per-issue additions:** - Fix Status: verified / best-effort / reverted / deferred diff --git a/qa/SKILL.md.tmpl b/qa/SKILL.md.tmpl index ea46ebad7..391102800 100644 --- a/qa/SKILL.md.tmpl +++ b/qa/SKILL.md.tmpl @@ -113,9 +113,10 @@ Prefer the richer of recent project test plans and plans in conversation over gi 1. **Project-scoped test plans:** Find the latest for this repo: ```bash + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat {{SLUG_EVAL}} - ls -t ~/.gstack/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 + ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-test-plan-*.md 2>/dev/null | head -1 ``` 2. **Conversation context:** Prior `/plan-eng-review` or `/plan-ceo-review` test plans. 3. Fall back to git diff only if neither exists. @@ -272,7 +273,7 @@ Write the Output Structure report locally and copy the same content to project c ```bash {{SLUG_SETUP}} ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-test-outcome-{datetime}.md` +Write to `/{user}-{branch}-test-outcome-{datetime}.md` (`PROJECT_DIR` printed above) **Per-issue additions:** - Fix Status: verified / best-effort / reverted / deferred diff --git a/retro/SKILL.md b/retro/SKILL.md index d538002de..72fa3b897 100644 --- a/retro/SKILL.md +++ b/retro/SKILL.md @@ -258,7 +258,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -1121,8 +1122,9 @@ Considering the full cross-project picture. ### Global Step 8: Load history & compare ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat -ls -t ~/.gstack/retros/global-*.json 2>/dev/null | head -5 +ls -t "$GSTACK_STATE_ROOT"/retros/global-*.json 2>/dev/null | head -5 ``` **Only compare against a prior retro with the same `window` value** (e.g., 7d vs 7d). If the most recent prior retro has a different window, skip comparison and note: "Prior global retro used a different window — skipping comparison." @@ -1134,18 +1136,21 @@ If no prior global retros exist, append: "First global retro recorded — run ag ### Global Step 9: Save snapshot ```bash -mkdir -p ~/.gstack/retros +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/retros ``` Determine the next unused sequence number for today, using the same session-reminder date as Global Step 1: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat today="" next=1 -while [ -e "$HOME/.gstack/retros/global-${today}-${next}.json" ]; do next=$((next + 1)); done +while [ -e "$GSTACK_STATE_ROOT/retros/global-${today}-${next}.json" ]; do next=$((next + 1)); done +echo "RETRO_FILE: $GSTACK_STATE_ROOT/retros/global-${today}-${next}.json" ``` -Use the Write tool to save JSON to `~/.gstack/retros/global-${today}-${next}.json`: +Use the Write tool to save JSON to the printed `RETRO_FILE`: ```json { diff --git a/retro/SKILL.md.tmpl b/retro/SKILL.md.tmpl index 44a63fea1..8ff30d38a 100644 --- a/retro/SKILL.md.tmpl +++ b/retro/SKILL.md.tmpl @@ -667,8 +667,9 @@ Considering the full cross-project picture. ### Global Step 8: Load history & compare ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat -ls -t ~/.gstack/retros/global-*.json 2>/dev/null | head -5 +ls -t "$GSTACK_STATE_ROOT"/retros/global-*.json 2>/dev/null | head -5 ``` **Only compare against a prior retro with the same `window` value** (e.g., 7d vs 7d). If the most recent prior retro has a different window, skip comparison and note: "Prior global retro used a different window — skipping comparison." @@ -680,18 +681,21 @@ If no prior global retros exist, append: "First global retro recorded — run ag ### Global Step 9: Save snapshot ```bash -mkdir -p ~/.gstack/retros +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/retros ``` Determine the next unused sequence number for today, using the same session-reminder date as Global Step 1: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat today="" next=1 -while [ -e "$HOME/.gstack/retros/global-${today}-${next}.json" ]; do next=$((next + 1)); done +while [ -e "$GSTACK_STATE_ROOT/retros/global-${today}-${next}.json" ]; do next=$((next + 1)); done +echo "RETRO_FILE: $GSTACK_STATE_ROOT/retros/global-${today}-${next}.json" ``` -Use the Write tool to save JSON to `~/.gstack/retros/global-${today}-${next}.json`: +Use the Write tool to save JSON to the printed `RETRO_FILE`: ```json { diff --git a/retro/sections/report-format.md b/retro/sections/report-format.md index 7b506c0b8..4c160c868 100644 --- a/retro/sections/report-format.md +++ b/retro/sections/report-format.md @@ -51,9 +51,10 @@ Narrative covering: Check review JSONL logs for plan completion data from /ship runs this period: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA" +cat "$GSTACK_STATE_ROOT"/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA" ``` If plan completion data exists within the retro time window: diff --git a/retro/sections/report-format.md.tmpl b/retro/sections/report-format.md.tmpl index 2f327db88..e41284b8d 100644 --- a/retro/sections/report-format.md.tmpl +++ b/retro/sections/report-format.md.tmpl @@ -49,9 +49,10 @@ Narrative covering: Check review JSONL logs for plan completion data from /ship runs this period: ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -cat ~/.gstack/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA" +cat "$GSTACK_STATE_ROOT"/projects/$SLUG/*-reviews.jsonl 2>/dev/null | grep '"skill":"ship"' | grep '"plan_items_total"' || echo "NO_PLAN_DATA" ``` If plan completion data exists within the retro time window: diff --git a/review/SKILL.md b/review/SKILL.md index c77b48537..5cf8d0edf 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -240,7 +240,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -350,7 +351,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol diff --git a/scripts/resolvers/preamble/generate-context-recovery.ts b/scripts/resolvers/preamble/generate-context-recovery.ts index 31ade38a9..e2bf727f2 100644 --- a/scripts/resolvers/preamble/generate-context-recovery.ts +++ b/scripts/resolvers/preamble/generate-context-recovery.ts @@ -17,7 +17,8 @@ At session start or after compaction, recover recent project context. \`\`\`bash eval "$(${binDir}/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=\${_BRANCH:-unknown} -_PROJ="\${GSTACK_HOME:-$HOME/.gstack}/projects/\${SLUG:-unknown}" +eval "$(${binDir}/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/\${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/scripts/resolvers/preamble/generate-search-before-building.ts b/scripts/resolvers/preamble/generate-search-before-building.ts index dc41a9b9a..84ccba5ae 100644 --- a/scripts/resolvers/preamble/generate-search-before-building.ts +++ b/scripts/resolvers/preamble/generate-search-before-building.ts @@ -18,7 +18,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: \`\`\`bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(${ctx.paths.binDir}/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true \`\`\``; } diff --git a/scripts/resolvers/tasks-section.ts b/scripts/resolvers/tasks-section.ts index 0941c3942..202e8cce3 100644 --- a/scripts/resolvers/tasks-section.ts +++ b/scripts/resolvers/tasks-section.ts @@ -59,8 +59,9 @@ Rules: backslashes serialize cleanly — never use hand-rolled \`echo\` / \`printf\`. \`\`\`bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -TASKS_DIR="\${HOME}/.gstack/projects/\${SLUG:-unknown}" +TASKS_DIR="$GSTACK_STATE_ROOT/projects/\${SLUG:-unknown}" mkdir -p "$TASKS_DIR" TASKS_FILE="$TASKS_DIR/tasks-${phase}-$(date +%Y%m%d-%H%M%S).jsonl" COMMIT=$(git rev-parse HEAD 2>/dev/null || echo unknown) @@ -105,8 +106,9 @@ Before rendering the Final Approval Gate output block below, aggregate the per-phase task lists each review skill wrote. \`\`\`bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" -TASKS_DIR="\${HOME}/.gstack/projects/\${SLUG:-unknown}" +TASKS_DIR="$GSTACK_STATE_ROOT/projects/\${SLUG:-unknown}" BRANCH=$(git branch --show-current 2>/dev/null || echo unknown) # Commit window: last 5 commits on this branch. Drops stale standalone reviews. COMMITS_RECENT=$(git log --format=%H -n 5 2>/dev/null | tr '\\n' '|' | sed 's/|$//') diff --git a/scripts/resolvers/testing.ts b/scripts/resolvers/testing.ts index 71c72d984..6b8367cb9 100644 --- a/scripts/resolvers/testing.ts +++ b/scripts/resolvers/testing.ts @@ -489,14 +489,15 @@ ${subheading} Test Plan Artifact After resolving the Test review decisions, record the approved test requirements in an artifact for \`/qa\` and \`/qa-only\`. List any unresolved choices separately as pending, not required implementation. Update this artifact if later approved decisions change the tests. Use the Review record and write policy above. \`\`\`bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG # sets SLUG and BRANCH +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" # sets SLUG and BRANCH TEST_PLAN_USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) \`\`\` Use \`SLUG\` and the sanitized \`BRANCH\` from gstack-slug, \`TEST_PLAN_USER\` for {user}, and \`DATETIME\` for {datetime}. Set {date} to today. Read the local origin URL with \`git remote get-url origin\` and use its owner/repo; without an origin, write \`local-only\`. No network request is needed. -Write to \`~/.gstack/projects/{slug}/{user}-{branch}-eng-review-test-plan-{datetime}.md\`: +Write to \`/{user}-{branch}-eng-review-test-plan-{datetime}.md\` (\`PROJECT_DIR\` printed above): \`\`\`markdown # Test Plan @@ -614,12 +615,13 @@ ${subheading} Test Plan Artifact After producing the coverage diagram, write a test plan artifact so \`/qa\` and \`/qa-only\` can consume it: \`\`\`bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) \`\`\` -Write to \`~/.gstack/projects/{slug}/{user}-{branch}-ship-test-plan-{datetime}.md\`: +Write to \`/{user}-{branch}-ship-test-plan-{datetime}.md\` (\`PROJECT_DIR\` printed above): \`\`\`markdown # Test Plan diff --git a/scripts/resolvers/utility.ts b/scripts/resolvers/utility.ts index 4b614a2ea..f4e4b5e2e 100644 --- a/scripts/resolvers/utility.ts +++ b/scripts/resolvers/utility.ts @@ -33,7 +33,8 @@ export function generateSlugEval(ctx: TemplateContext): string { } export function generateSlugSetup(ctx: TemplateContext): string { - return `eval "$(${ctx.paths.binDir}/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG`; + return `eval "$(${ctx.paths.binDir}/gstack-paths)"; : "\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(${ctx.paths.binDir}/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG"`; } export function generateBaseBranchDetect(_ctx: TemplateContext): string { diff --git a/setup-deploy/SKILL.md b/setup-deploy/SKILL.md index 20e90c26f..e104d19ba 100644 --- a/setup-deploy/SKILL.md +++ b/setup-deploy/SKILL.md @@ -239,7 +239,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/setup-gbrain/SKILL.md b/setup-gbrain/SKILL.md index 37029db04..342b74bd9 100644 --- a/setup-gbrain/SKILL.md +++ b/setup-gbrain/SKILL.md @@ -238,7 +238,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -905,10 +906,11 @@ configured Mac is a first-class doctor path: every step detects existing state, repairs only what's missing, and reports here. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" ~/.claude/skills/gstack/bin/gstack-gbrain-detect 2>/dev/null || true ~/.claude/skills/gstack/bin/gstack-config get transcript_ingest_mode 2>/dev/null || echo "off" ~/.claude/skills/gstack/bin/gstack-config get artifacts_sync_mode 2>/dev/null || echo "off" -[ -f ~/.gstack/.gbrain-sync-state.json ] && cat ~/.gstack/.gbrain-sync-state.json || echo "{}" +[ -f "$GSTACK_STATE_ROOT"/.gbrain-sync-state.json ] && cat "$GSTACK_STATE_ROOT"/.gbrain-sync-state.json || echo "{}" ``` Read `gbrain_mcp_mode` from the detect output and pick the right verdict @@ -1040,13 +1042,14 @@ this at build time. failures are not evidence of a competing run: ```bash - if ! mkdir -p ~/.gstack; then - echo "ERROR: Cannot create setup-gbrain lock parent ~/.gstack." >&2 + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + if ! mkdir -p "$GSTACK_STATE_ROOT"; then + echo "ERROR: Cannot create setup-gbrain lock parent $GSTACK_STATE_ROOT." >&2 exit 1 fi - if ! mkdir ~/.gstack/.setup-gbrain.lock.d; then - if [ -d ~/.gstack/.setup-gbrain.lock.d ]; then - echo "Another /setup-gbrain instance is running. Wait for it, or remove ~/.gstack/.setup-gbrain.lock.d only if you are sure it is stale." >&2 + if ! mkdir "$GSTACK_STATE_ROOT"/.setup-gbrain.lock.d; then + if [ -d "$GSTACK_STATE_ROOT"/.setup-gbrain.lock.d ]; then + echo "Another /setup-gbrain instance is running. Wait for it, or remove $GSTACK_STATE_ROOT/.setup-gbrain.lock.d only if you are sure it is stale." >&2 else echo "ERROR: Cannot acquire setup-gbrain lock." >&2 fi diff --git a/setup-gbrain/SKILL.md.tmpl b/setup-gbrain/SKILL.md.tmpl index 4fb40d750..051082621 100644 --- a/setup-gbrain/SKILL.md.tmpl +++ b/setup-gbrain/SKILL.md.tmpl @@ -543,10 +543,11 @@ configured Mac is a first-class doctor path: every step detects existing state, repairs only what's missing, and reports here. ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" ~/.claude/skills/gstack/bin/gstack-gbrain-detect 2>/dev/null || true ~/.claude/skills/gstack/bin/gstack-config get transcript_ingest_mode 2>/dev/null || echo "off" ~/.claude/skills/gstack/bin/gstack-config get artifacts_sync_mode 2>/dev/null || echo "off" -[ -f ~/.gstack/.gbrain-sync-state.json ] && cat ~/.gstack/.gbrain-sync-state.json || echo "{}" +[ -f "$GSTACK_STATE_ROOT"/.gbrain-sync-state.json ] && cat "$GSTACK_STATE_ROOT"/.gbrain-sync-state.json || echo "{}" ``` Read `gbrain_mcp_mode` from the detect output and pick the right verdict @@ -678,13 +679,14 @@ this at build time. failures are not evidence of a competing run: ```bash - if ! mkdir -p ~/.gstack; then - echo "ERROR: Cannot create setup-gbrain lock parent ~/.gstack." >&2 + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + if ! mkdir -p "$GSTACK_STATE_ROOT"; then + echo "ERROR: Cannot create setup-gbrain lock parent $GSTACK_STATE_ROOT." >&2 exit 1 fi - if ! mkdir ~/.gstack/.setup-gbrain.lock.d; then - if [ -d ~/.gstack/.setup-gbrain.lock.d ]; then - echo "Another /setup-gbrain instance is running. Wait for it, or remove ~/.gstack/.setup-gbrain.lock.d only if you are sure it is stale." >&2 + if ! mkdir "$GSTACK_STATE_ROOT"/.setup-gbrain.lock.d; then + if [ -d "$GSTACK_STATE_ROOT"/.setup-gbrain.lock.d ]; then + echo "Another /setup-gbrain instance is running. Wait for it, or remove $GSTACK_STATE_ROOT/.setup-gbrain.lock.d only if you are sure it is stale." >&2 else echo "ERROR: Cannot acquire setup-gbrain lock." >&2 fi diff --git a/ship/SKILL.md b/ship/SKILL.md index 263b7645c..512158849 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -240,7 +240,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -350,7 +351,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -1051,7 +1053,8 @@ git config --get core.hooksPath >/dev/null 2>&1 || _HOOKS_CONFIG_STATUS=$? if [ -n "$_HOOK_PATH" ] && [ -n "$_HOOKS_DIR" ] && [ "$_HOOKS_CONFIG_STATUS" = "1" ] && [ ! -L "$_HOOKS_DIR" ]; then _HOOKS_IN_GIT_DIR="yes" fi -_PREPUSH_PROMPTED=$([ -f "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" ] && echo "yes" || echo "no") +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PREPUSH_PROMPTED=$([ -f "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ] && echo "yes" || echo "no") if [ "$_REDACT_PREPUSH" = "true" ] && [ "$_HOOKS_IN_GIT_DIR" = "yes" ] && [ "$_HOOK_STATE" != "unmanaged" ]; then ~/.claude/skills/gstack/bin/gstack-redact install-prepush-hook || exit $? fi @@ -1088,7 +1091,8 @@ Branch on the echoed values: ALWAYS (after either answer, but NOT if the question itself failed to render — a failed AskUserQuestion must re-offer next time): ```bash - touch "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + touch "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ``` 3. **Declined earlier** — continue without comment. @@ -1174,8 +1178,7 @@ The shell supplies the branch. Run this automatically, without confirmation. After a successful ship, show the non-blocking /plan-tune nudge once per machine: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" -export GSTACK_STATE_ROOT +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _NUDGE_MARKER="$GSTACK_STATE_ROOT/.plan-tune-nudge-shown" _QT=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false") if [ ! -f "$_NUDGE_MARKER" ] && [ "$_QT" = "false" ]; then diff --git a/ship/SKILL.md.tmpl b/ship/SKILL.md.tmpl index 89f383778..1154b6dcf 100644 --- a/ship/SKILL.md.tmpl +++ b/ship/SKILL.md.tmpl @@ -500,7 +500,8 @@ git config --get core.hooksPath >/dev/null 2>&1 || _HOOKS_CONFIG_STATUS=$? if [ -n "$_HOOK_PATH" ] && [ -n "$_HOOKS_DIR" ] && [ "$_HOOKS_CONFIG_STATUS" = "1" ] && [ ! -L "$_HOOKS_DIR" ]; then _HOOKS_IN_GIT_DIR="yes" fi -_PREPUSH_PROMPTED=$([ -f "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" ] && echo "yes" || echo "no") +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PREPUSH_PROMPTED=$([ -f "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ] && echo "yes" || echo "no") if [ "$_REDACT_PREPUSH" = "true" ] && [ "$_HOOKS_IN_GIT_DIR" = "yes" ] && [ "$_HOOK_STATE" != "unmanaged" ]; then ~/.claude/skills/gstack/bin/gstack-redact install-prepush-hook || exit $? fi @@ -537,7 +538,8 @@ Branch on the echoed values: ALWAYS (after either answer, but NOT if the question itself failed to render — a failed AskUserQuestion must re-offer next time): ```bash - touch "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + touch "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ``` 3. **Declined earlier** — continue without comment. @@ -622,8 +624,7 @@ The shell supplies the branch. Run this automatically, without confirmation. After a successful ship, show the non-blocking /plan-tune nudge once per machine: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" -export GSTACK_STATE_ROOT +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _NUDGE_MARKER="$GSTACK_STATE_ROOT/.plan-tune-nudge-shown" _QT=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false") if [ ! -f "$_NUDGE_MARKER" ] && [ "$_QT" = "false" ]; then diff --git a/ship/sections/pr-body.md b/ship/sections/pr-body.md index f784e36f5..fb2c0f2cb 100644 --- a/ship/sections/pr-body.md +++ b/ship/sections/pr-body.md @@ -10,7 +10,7 @@ then return here for a new lookup, fresh body and both redaction scans before pu 1. Resolve the archive directory and branch: ```bash - eval "$(~/.claude/skills/gstack/bin/gstack-paths)" + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug)" CURRENT_BRANCH=$(git branch --show-current) SPEC_ARCHIVES="$GSTACK_STATE_ROOT/projects/$SLUG/specs" diff --git a/ship/sections/pr-body.md.tmpl b/ship/sections/pr-body.md.tmpl index aa098242d..3a7b15e92 100644 --- a/ship/sections/pr-body.md.tmpl +++ b/ship/sections/pr-body.md.tmpl @@ -8,7 +8,7 @@ then return here for a new lookup, fresh body and both redaction scans before pu 1. Resolve the archive directory and branch: ```bash - eval "$(~/.claude/skills/gstack/bin/gstack-paths)" + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug)" CURRENT_BRANCH=$(git branch --show-current) SPEC_ARCHIVES="$GSTACK_STATE_ROOT/projects/$SLUG/specs" diff --git a/ship/sections/test-coverage.md b/ship/sections/test-coverage.md index a8020e1f8..8759b758f 100644 --- a/ship/sections/test-coverage.md +++ b/ship/sections/test-coverage.md @@ -288,12 +288,13 @@ Coverage line: `Test Coverage Audit: N new code paths. M covered (Y% any test, X After producing the coverage diagram, write a test plan artifact so `/qa` and `/qa-only` can consume it: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-ship-test-plan-{datetime}.md`: +Write to `/{user}-{branch}-ship-test-plan-{datetime}.md` (`PROJECT_DIR` printed above): ```markdown # Test Plan diff --git a/skillify/SKILL.md b/skillify/SKILL.md index 759b9d327..23e038c96 100644 --- a/skillify/SKILL.md +++ b/skillify/SKILL.md @@ -237,7 +237,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/spec/SKILL.md b/spec/SKILL.md index c07a362d9..b65e2f6b1 100644 --- a/spec/SKILL.md +++ b/spec/SKILL.md @@ -238,7 +238,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -348,7 +349,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol diff --git a/spec/sections/gate-and-file.md b/spec/sections/gate-and-file.md index 95186ca9d..fa2941b6b 100644 --- a/spec/sections/gate-and-file.md +++ b/spec/sections/gate-and-file.md @@ -287,7 +287,7 @@ Resolve the archive path via the existing `gstack-paths` helper (handles `GSTACK_HOME`, `CLAUDE_PLUGIN_DATA`, Windows fallback): ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug)" ARCHIVE_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/specs" mkdir -p "$ARCHIVE_DIR" diff --git a/spec/sections/gate-and-file.md.tmpl b/spec/sections/gate-and-file.md.tmpl index 975db7d18..9bd0ec7f8 100644 --- a/spec/sections/gate-and-file.md.tmpl +++ b/spec/sections/gate-and-file.md.tmpl @@ -156,7 +156,7 @@ Resolve the archive path via the existing `gstack-paths` helper (handles `GSTACK_HOME`, `CLAUDE_PLUGIN_DATA`, Windows fallback): ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$(~/.claude/skills/gstack/bin/gstack-slug)" ARCHIVE_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/specs" mkdir -p "$ARCHIVE_DIR" diff --git a/sync-gbrain/SKILL.md b/sync-gbrain/SKILL.md index bf60cfae3..610df456c 100644 --- a/sync-gbrain/SKILL.md +++ b/sync-gbrain/SKILL.md @@ -239,7 +239,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 diff --git a/test-audit/SKILL.md b/test-audit/SKILL.md index 2246b78d9..591346ce3 100644 --- a/test-audit/SKILL.md +++ b/test-audit/SKILL.md @@ -234,7 +234,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -426,15 +427,17 @@ Retirement card, complete before any edit: `test`, `detects`, `non_test_callers` ## Step 1: Scope and seeds ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" DATETIME=$(date +%Y%m%d-%H%M%S) -REPORT=~/.gstack/projects/$SLUG/test-audit-$DATETIME.md +REPORT="$GSTACK_STATE_ROOT"/projects/$SLUG/test-audit-$DATETIME.md DEFAULT_BRANCH=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||') echo "REPORT: $REPORT" echo "DEFAULT_BRANCH: ${DEFAULT_BRANCH:-unknown}" git ls-files | grep -cE '(^|/)(tests?|spec|__tests__)/|(^|/)test_[^/]+\.py$|_test\.(go|py|rb|ts|js|exs)$|\.(test|spec)\.[jt]sx?$|_spec\.rb$|Test\.(java|kt)$' | sed 's/^/TESTFILES:/' -ls -t ~/.gstack/projects/$SLUG/*-"$BRANCH"-eng-review-test-plan-*.md 2>/dev/null | head -1 | sed 's/^/SEED_PLAN:/' +ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-"$BRANCH"-eng-review-test-plan-*.md 2>/dev/null | head -1 | sed 's/^/SEED_PLAN:/' ``` - Scope is the paths given, else the whole repository. With more than 300 test files diff --git a/test-audit/SKILL.md.tmpl b/test-audit/SKILL.md.tmpl index 1507bab54..6158ed0b2 100644 --- a/test-audit/SKILL.md.tmpl +++ b/test-audit/SKILL.md.tmpl @@ -48,15 +48,16 @@ repo, 10 candidates). ## Step 1: Scope and seeds ```bash +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" setopt +o nomatch 2>/dev/null || true # zsh compat {{SLUG_SETUP}} DATETIME=$(date +%Y%m%d-%H%M%S) -REPORT=~/.gstack/projects/$SLUG/test-audit-$DATETIME.md +REPORT="$GSTACK_STATE_ROOT"/projects/$SLUG/test-audit-$DATETIME.md DEFAULT_BRANCH=$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||') echo "REPORT: $REPORT" echo "DEFAULT_BRANCH: ${DEFAULT_BRANCH:-unknown}" git ls-files | grep -cE '(^|/)(tests?|spec|__tests__)/|(^|/)test_[^/]+\.py$|_test\.(go|py|rb|ts|js|exs)$|\.(test|spec)\.[jt]sx?$|_spec\.rb$|Test\.(java|kt)$' | sed 's/^/TESTFILES:/' -ls -t ~/.gstack/projects/$SLUG/*-"$BRANCH"-eng-review-test-plan-*.md 2>/dev/null | head -1 | sed 's/^/SEED_PLAN:/' +ls -t "$GSTACK_STATE_ROOT"/projects/$SLUG/*-"$BRANCH"-eng-review-test-plan-*.md 2>/dev/null | head -1 | sed 's/^/SEED_PLAN:/' ``` - Scope is the paths given, else the whole repository. With more than 300 test files diff --git a/test/fixtures/context-budget.json b/test/fixtures/context-budget.json index aea3dabd0..6069603b6 100644 --- a/test/fixtures/context-budget.json +++ b/test/fixtures/context-budget.json @@ -62,6 +62,6 @@ "spec": 14993, "sync-gbrain": 13975, "test-audit": 10439, - "unfreeze": 393 + "unfreeze": 448 } } diff --git a/test/fixtures/golden/claude-ship-SKILL.md b/test/fixtures/golden/claude-ship-SKILL.md index 263b7645c..512158849 100644 --- a/test/fixtures/golden/claude-ship-SKILL.md +++ b/test/fixtures/golden/claude-ship-SKILL.md @@ -240,7 +240,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -350,7 +351,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -1051,7 +1053,8 @@ git config --get core.hooksPath >/dev/null 2>&1 || _HOOKS_CONFIG_STATUS=$? if [ -n "$_HOOK_PATH" ] && [ -n "$_HOOKS_DIR" ] && [ "$_HOOKS_CONFIG_STATUS" = "1" ] && [ ! -L "$_HOOKS_DIR" ]; then _HOOKS_IN_GIT_DIR="yes" fi -_PREPUSH_PROMPTED=$([ -f "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" ] && echo "yes" || echo "no") +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PREPUSH_PROMPTED=$([ -f "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ] && echo "yes" || echo "no") if [ "$_REDACT_PREPUSH" = "true" ] && [ "$_HOOKS_IN_GIT_DIR" = "yes" ] && [ "$_HOOK_STATE" != "unmanaged" ]; then ~/.claude/skills/gstack/bin/gstack-redact install-prepush-hook || exit $? fi @@ -1088,7 +1091,8 @@ Branch on the echoed values: ALWAYS (after either answer, but NOT if the question itself failed to render — a failed AskUserQuestion must re-offer next time): ```bash - touch "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" + eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + touch "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ``` 3. **Declined earlier** — continue without comment. @@ -1174,8 +1178,7 @@ The shell supplies the branch. Run this automatically, without confirmation. After a successful ship, show the non-blocking /plan-tune nudge once per machine: ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-paths)" -export GSTACK_STATE_ROOT +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _NUDGE_MARKER="$GSTACK_STATE_ROOT/.plan-tune-nudge-shown" _QT=$(~/.claude/skills/gstack/bin/gstack-config get question_tuning 2>/dev/null || echo "false") if [ ! -f "$_NUDGE_MARKER" ] && [ "$_QT" = "false" ]; then diff --git a/test/fixtures/golden/codex-ship-SKILL.md b/test/fixtures/golden/codex-ship-SKILL.md index f3a1dc513..ad7a62f75 100644 --- a/test/fixtures/golden/codex-ship-SKILL.md +++ b/test/fixtures/golden/codex-ship-SKILL.md @@ -248,7 +248,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$($GSTACK_BIN/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$($GSTACK_BIN/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -358,7 +359,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$($GSTACK_BIN/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -1415,12 +1417,13 @@ Coverage line: `Test Coverage Audit: N new code paths. M covered (Y% any test, X After producing the coverage diagram, write a test plan artifact so `/qa` and `/qa-only` can consume it: ```bash -eval "$($GSTACK_ROOT/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$($GSTACK_ROOT/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$($GSTACK_ROOT/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-ship-test-plan-{datetime}.md`: +Write to `/{user}-{branch}-ship-test-plan-{datetime}.md` (`PROJECT_DIR` printed above): ```markdown # Test Plan @@ -3162,7 +3165,8 @@ git config --get core.hooksPath >/dev/null 2>&1 || _HOOKS_CONFIG_STATUS=$? if [ -n "$_HOOK_PATH" ] && [ -n "$_HOOKS_DIR" ] && [ "$_HOOKS_CONFIG_STATUS" = "1" ] && [ ! -L "$_HOOKS_DIR" ]; then _HOOKS_IN_GIT_DIR="yes" fi -_PREPUSH_PROMPTED=$([ -f "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" ] && echo "yes" || echo "no") +eval "$($GSTACK_ROOT/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PREPUSH_PROMPTED=$([ -f "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ] && echo "yes" || echo "no") if [ "$_REDACT_PREPUSH" = "true" ] && [ "$_HOOKS_IN_GIT_DIR" = "yes" ] && [ "$_HOOK_STATE" != "unmanaged" ]; then $GSTACK_ROOT/bin/gstack-redact install-prepush-hook || exit $? fi @@ -3199,7 +3203,8 @@ Branch on the echoed values: ALWAYS (after either answer, but NOT if the question itself failed to render — a failed AskUserQuestion must re-offer next time): ```bash - touch "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" + eval "$($GSTACK_ROOT/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + touch "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ``` 3. **Declined earlier** — continue without comment. @@ -3264,7 +3269,7 @@ then return here for a new lookup, fresh body and both redaction scans before pu 1. Resolve the archive directory and branch: ```bash - eval "$($GSTACK_ROOT/bin/gstack-paths)" + eval "$($GSTACK_ROOT/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$($GSTACK_ROOT/bin/gstack-slug)" CURRENT_BRANCH=$(git branch --show-current) SPEC_ARCHIVES="$GSTACK_STATE_ROOT/projects/$SLUG/specs" @@ -3453,8 +3458,7 @@ The shell supplies the branch. Run this automatically, without confirmation. After a successful ship, show the non-blocking /plan-tune nudge once per machine: ```bash -eval "$($GSTACK_ROOT/bin/gstack-paths)" -export GSTACK_STATE_ROOT +eval "$($GSTACK_ROOT/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _NUDGE_MARKER="$GSTACK_STATE_ROOT/.plan-tune-nudge-shown" _QT=$($GSTACK_ROOT/bin/gstack-config get question_tuning 2>/dev/null || echo "false") if [ ! -f "$_NUDGE_MARKER" ] && [ "$_QT" = "false" ]; then diff --git a/test/fixtures/golden/factory-ship-SKILL.md b/test/fixtures/golden/factory-ship-SKILL.md index 0ad0b9e0e..cef698391 100644 --- a/test/fixtures/golden/factory-ship-SKILL.md +++ b/test/fixtures/golden/factory-ship-SKILL.md @@ -228,7 +228,8 @@ At session start or after compaction, recover recent project context. ```bash eval "$($GSTACK_BIN/gstack-slug 2>/dev/null)" _BRANCH=$(git branch --show-current 2>/dev/null | tr -cd 'a-zA-Z0-9._/-') || :; _BRANCH=${_BRANCH:-unknown} -_PROJ="${GSTACK_HOME:-$HOME/.gstack}/projects/${SLUG:-unknown}" +eval "$($GSTACK_BIN/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PROJ="$GSTACK_STATE_ROOT/projects/${SLUG:-unknown}" if [ -d "$_PROJ" ]; then echo "--- RECENT ARTIFACTS ---" find "$_PROJ/ceo-plans" "$_PROJ/checkpoints" -type f -name "*.md" 2>/dev/null | xargs -r ls -t 2>/dev/null | head -3 @@ -338,7 +339,8 @@ Then build the complete version of what remains. **Eureka:** When first-principles reasoning contradicts conventional wisdom, name it and log: ```bash -jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> ~/.gstack/analytics/eureka.jsonl 2>/dev/null || true +eval "$($GSTACK_BIN/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +jq -n --arg ts "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --arg skill "SKILL_NAME" --arg branch "$(git branch --show-current 2>/dev/null)" --arg insight "ONE_LINE_SUMMARY" '{ts:$ts,skill:$skill,branch:$branch,insight:$insight}' >> "$GSTACK_STATE_ROOT/analytics/eureka.jsonl" 2>/dev/null || true ``` ## Completion Status Protocol @@ -1395,12 +1397,13 @@ Coverage line: `Test Coverage Audit: N new code paths. M covered (Y% any test, X After producing the coverage diagram, write a test plan artifact so `/qa` and `/qa-only` can consume it: ```bash -eval "$($GSTACK_ROOT/bin/gstack-slug 2>/dev/null)" && mkdir -p ~/.gstack/projects/$SLUG +eval "$($GSTACK_ROOT/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +eval "$($GSTACK_ROOT/bin/gstack-slug 2>/dev/null)" && mkdir -p "$GSTACK_STATE_ROOT/projects/$SLUG" && echo "PROJECT_DIR: $GSTACK_STATE_ROOT/projects/$SLUG" USER=$(whoami) DATETIME=$(date +%Y%m%d-%H%M%S) ``` -Write to `~/.gstack/projects/{slug}/{user}-{branch}-ship-test-plan-{datetime}.md`: +Write to `/{user}-{branch}-ship-test-plan-{datetime}.md` (`PROJECT_DIR` printed above): ```markdown # Test Plan @@ -3428,7 +3431,8 @@ git config --get core.hooksPath >/dev/null 2>&1 || _HOOKS_CONFIG_STATUS=$? if [ -n "$_HOOK_PATH" ] && [ -n "$_HOOKS_DIR" ] && [ "$_HOOKS_CONFIG_STATUS" = "1" ] && [ ! -L "$_HOOKS_DIR" ]; then _HOOKS_IN_GIT_DIR="yes" fi -_PREPUSH_PROMPTED=$([ -f "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" ] && echo "yes" || echo "no") +eval "$($GSTACK_ROOT/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +_PREPUSH_PROMPTED=$([ -f "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ] && echo "yes" || echo "no") if [ "$_REDACT_PREPUSH" = "true" ] && [ "$_HOOKS_IN_GIT_DIR" = "yes" ] && [ "$_HOOK_STATE" != "unmanaged" ]; then $GSTACK_ROOT/bin/gstack-redact install-prepush-hook || exit $? fi @@ -3465,7 +3469,8 @@ Branch on the echoed values: ALWAYS (after either answer, but NOT if the question itself failed to render — a failed AskUserQuestion must re-offer next time): ```bash - touch "${GSTACK_HOME:-$HOME/.gstack}/.redact-prepush-prompted" + eval "$($GSTACK_ROOT/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" + touch "$GSTACK_STATE_ROOT/.redact-prepush-prompted" ``` 3. **Declined earlier** — continue without comment. @@ -3530,7 +3535,7 @@ then return here for a new lookup, fresh body and both redaction scans before pu 1. Resolve the archive directory and branch: ```bash - eval "$($GSTACK_ROOT/bin/gstack-paths)" + eval "$($GSTACK_ROOT/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" eval "$($GSTACK_ROOT/bin/gstack-slug)" CURRENT_BRANCH=$(git branch --show-current) SPEC_ARCHIVES="$GSTACK_STATE_ROOT/projects/$SLUG/specs" @@ -3719,8 +3724,7 @@ The shell supplies the branch. Run this automatically, without confirmation. After a successful ship, show the non-blocking /plan-tune nudge once per machine: ```bash -eval "$($GSTACK_ROOT/bin/gstack-paths)" -export GSTACK_STATE_ROOT +eval "$($GSTACK_ROOT/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" _NUDGE_MARKER="$GSTACK_STATE_ROOT/.plan-tune-nudge-shown" _QT=$($GSTACK_ROOT/bin/gstack-config get question_tuning 2>/dev/null || echo "false") if [ ! -f "$_NUDGE_MARKER" ] && [ "$_QT" = "false" ]; then diff --git a/test/fixtures/state-root-selection-golden.json b/test/fixtures/state-root-selection-golden.json new file mode 100644 index 000000000..71d41558f --- /dev/null +++ b/test/fixtures/state-root-selection-golden.json @@ -0,0 +1,64 @@ +{ + "recordedAt": "96764e80", + "note": "paid evals selected by touching each W1 source file, recorded on the base commit before the change", + "modules": { + "lib/state-root.ts": { + "sources": [ + "browse/src/config.ts", + "lib/cso/state.ts" + ], + "e2e": [ + "aside-browse-basic", + "aside-browse-flow", + "benchmark-workflow", + "browse-basic", + "browse-snapshot", + "canary-workflow", + "carve-section-loading", + "cso-diff-mode", + "cso-full-audit", + "cso-infra-scope", + "design-review-detector-shim-dom", + "design-review-fix", + "diagram-authoring-quality", + "diagram-triplet", + "qa-b6-static", + "qa-b7-spa", + "qa-b8-checkout", + "qa-fix-loop", + "qa-only-no-fix", + "qa-quick" + ], + "llmJudge": [ + "browse/SKILL.md reference" + ] + }, + "bin/gstack-state-root.sh": { + "sources": [ + "bin/gstack-paths", + "careful/bin/hook-extract.sh" + ], + "e2e": [ + "investigate-owned-abort", + "investigate-owned-completion", + "investigate-owned-ending-error", + "plan-design-with-ui-scope" + ], + "llmJudge": [] + }, + "hosts/claude/hooks/hook-log.ts": { + "sources": [ + "hosts/claude/hooks/question-log-hook.ts", + "hosts/claude/hooks/question-preference-hook.ts", + "hosts/claude/hooks/auq-error-fallback-hook.ts", + "hosts/claude/hooks/timeline-stop-hook.ts", + "hosts/claude/hooks/memorable-user-prompt-hook.ts" + ], + "e2e": [ + "auto-decide-preserved", + "docsync-spawned" + ], + "llmJudge": [] + } + } +} diff --git a/test/helpers/carve-guards.ts b/test/helpers/carve-guards.ts index 914bb0743..1fd607dc4 100644 --- a/test/helpers/carve-guards.ts +++ b/test/helpers/carve-guards.ts @@ -166,7 +166,7 @@ export const CARVE_GUARDS: Record = { // wave's headline capability) grows the union to 1.195x. Deliberate: // the section is on-demand (loads only for Apple store targets), so // per-invocation cost for non-iOS ships is one manifest line. - maxSizeRatio: 1.397, // Shared advisory identity/dedup + critical-severity validation: 248,065 union bytes / 187,706 baseline = 1.3216 (2026-09-17). + test value bar in the lazy Step 7 section (value cards, weak paths, gate table, base control, machine checks; ~13.6KB): measured 1.396 (2026-09-29). + maxSizeRatio: 1.402, // Shared advisory identity/dedup + critical-severity validation: 248,065 union bytes / 187,706 baseline = 1.3216 (2026-09-17). + test value bar in the lazy Step 7 section (value cards, weak paths, gate table, base control, machine checks; ~13.6KB): measured 1.396 (2026-09-29). + W1 guarded state-root resolution (`eval gstack-paths; : "${GSTACK_STATE_ROOT:?…}"`) in the Context Recovery preamble, the eureka log and each state-writing bash block; measured 1.401 (2026-09-30). }, 'plan-ceo-review': { skill: 'plan-ceo-review', @@ -184,7 +184,7 @@ export const CARVE_GUARDS: Record = { // v1.65 merge: provisional larger-of-both-waves budget; re-measured below. // Fork port wave 2 (#703): the repo-doc-preference block in the design // check grew every plan-review skeleton ~0.7KB. Measured values noted. - maxSkeletonBytes: 80_150, // + depth-specific output and 0H/0I feasibility boundary clarity + the Aside probe's failure reason; measured 80,111. + maxSkeletonBytes: 80_700, // + depth-specific output and 0H/0I feasibility boundary clarity + the Aside probe's failure reason; measured 80,111. + W1 guarded state-root resolution (`eval gstack-paths; : "${GSTACK_STATE_ROOT:?…}"`) in the Context Recovery preamble, the eureka log and each state-writing bash block; measured 80,649 (2026-09-30). minUnionBytes: 123_600, // token-reduction Phases 1-2 (v1.69.x branch): preamble bash -> bin/gstack-skill-start, onboarding -> gated emission; measured union 137,346 mustContain: ['SCOPE EXPANSION', 'SELECTIVE EXPANSION', 'HOLD SCOPE', 'SCOPE REDUCTION'], // Default-on Codex outside-voice (codexPreflight block + CODEX_MODE branch @@ -221,7 +221,7 @@ export const CARVE_GUARDS: Record = { // 1.08 → 1.10: the scope-gate exceptions block (+ its adversarial-review // hardening: host-anchored mode signal, precedence, passing-mention // guards) and the plan-mode preamble reword land the union at 1.092. - maxSizeRatio: 1.169, // + clarity rules for saved decisions/setup gates + the Aside probe's failure reason; measured 1.1504. + test value bar and Tests to Retire in the lazy Test review section (~2.6KB); measured 1.168 + maxSizeRatio: 1.174, // + clarity rules for saved decisions/setup gates + the Aside probe's failure reason; measured 1.1504. + test value bar and Tests to Retire in the lazy Test review section (~2.6KB); measured 1.168 + W1 guarded state-root resolution (`eval gstack-paths; : "${GSTACK_STATE_ROOT:?…}"`) in the Context Recovery preamble, the eureka log and each state-writing bash block; measured 1.173 (2026-09-30). }, 'plan-design-review': { skill: 'plan-design-review', @@ -407,7 +407,9 @@ do not launch the downstream skill or open a browser.`, // the cross-session decision-memory nudge) lands this carved skeleton just over // the strict 1.05; headroom for the shared preamble additions. // v1.64+v1.65 merge sums both waves' preamble growth; measured 1.073. - maxSizeRatio: 1.08, + // + W1 guarded state-root resolution in the Context Recovery preamble, the + // eureka log and the office-hours lookup; measured 1.082 (2026-09-30). + maxSizeRatio: 1.083, }, cso: { skill: 'cso', @@ -664,7 +666,7 @@ do not launch the downstream skill or open a browser.`, }, behavioral: 'prompt', maxSkeletonBytes: 63_500, // + v2.0 {{ASIDE_SETUP}}/{{BROWSE_FALLBACK}} (replaces the browse setup block); measured 61_253 - maxSizeRatio: 1.095, // + v1.81 Aside contract + gstack-browser fallback block (1.080 on v1.91.7.0) + the shared test value bar at 8a.5 ({{TEST_VALUE_BAR:qa}}); measured 1.094 + maxSizeRatio: 1.102, // + v1.81 Aside contract + gstack-browser fallback block (1.080 on v1.91.7.0) + the shared test value bar at 8a.5 ({{TEST_VALUE_BAR:qa}}); measured 1.094 + W1 guarded state-root resolution (`eval gstack-paths; : "${GSTACK_STATE_ROOT:?…}"`) in the Context Recovery preamble, the eureka log and each state-writing bash block; measured 1.101 (2026-09-30) minUnionBytes: 69_500, // measured union 70,385 // 'aside repl' pins the Aside contract; '$B goto' pins the fallback block in the always-loaded skeleton. mustContain: ['bug', 'aside repl', '$B goto', 'fix', 'Health Score Rubric', 'regression'], diff --git a/test/helpers/touchfiles-data.ts b/test/helpers/touchfiles-data.ts index 10acaa314..1d23a7b12 100644 --- a/test/helpers/touchfiles-data.ts +++ b/test/helpers/touchfiles-data.ts @@ -21,7 +21,7 @@ * Each test lists the file patterns that, if changed, require the test to run. */ export const E2E_TOUCHFILES: Record = { - 'ship-skipped-queued-finding': [ + 'ship-skipped-queued-finding': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'package.json', 'bun.lock', '.github/docker/Dockerfile.ci', 'scripts/resolvers/review.ts', 'scripts/resolvers/sections.ts', 'scripts/resolvers/index.ts', 'scripts/resolvers/types.ts', 'scripts/gen-skill-docs.ts', 'scripts/host-config.ts', @@ -36,29 +36,29 @@ export const E2E_TOUCHFILES: Record = { 'test/helpers/agent-sdk-runner.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eval-budgets.ts' ], - 'investigate-owned-completion': ['investigate/**', 'freeze/**', 'guard/**', 'unfreeze/**', 'careful/bin/hook-extract.sh', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/workflow-boundaries-fixture.ts', 'test/skill-e2e-investigate-owned-completion.test.ts', 'test/helpers/e2e-gate.ts'], - 'investigate-owned-abort': ['investigate/**', 'freeze/**', 'guard/**', 'unfreeze/**', 'careful/bin/hook-extract.sh', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/workflow-boundaries-fixture.ts', 'test/skill-e2e-investigate-owned-termination.test.ts', 'test/helpers/e2e-gate.ts'], - 'investigate-owned-ending-error': ['investigate/**', 'freeze/**', 'guard/**', 'unfreeze/**', 'careful/bin/hook-extract.sh', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/workflow-boundaries-fixture.ts', 'test/skill-e2e-investigate-owned-termination.test.ts', 'test/helpers/e2e-gate.ts'], - 'shared-libs-review-path-eligibility': ['review/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'lib/review-evidence.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'bin/gstack-wtree', 'test/helpers/shared-libs-eval-fixture.ts', 'test/skill-e2e-shared-libs-paths.test.ts', 'test/helpers/shared-libs-path-fixture.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-index-flags-*.json', 'test/fixtures/shared-libs-resolved-reads-public.json', 'test/helpers/shared-libs-review-start-evidence.ts'], - 'shared-libs-review-index-flags': ['review/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'lib/review-evidence.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'bin/gstack-wtree', 'test/helpers/shared-libs-eval-fixture.ts', 'test/skill-e2e-shared-libs-paths.test.ts', 'test/helpers/shared-libs-path-fixture.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-index-flags-*.json', 'test/fixtures/shared-libs-paths-max-turns-public.json', 'test/fixtures/shared-libs-resolved-reads-public.json', 'test/helpers/shared-libs-review-start-evidence.ts'], - 'shared-libs-review-prior-coverage': ['review/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'lib/review-evidence.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'bin/gstack-wtree', 'test/helpers/shared-libs-eval-fixture.ts', 'test/skill-e2e-shared-libs-paths.test.ts', 'test/helpers/shared-libs-path-fixture.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-index-flags-*.json', 'test/fixtures/shared-libs-resolved-reads-public.json', 'test/helpers/shared-libs-review-start-evidence.ts'], + 'investigate-owned-completion': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'investigate/**', 'freeze/**', 'guard/**', 'unfreeze/**', 'careful/bin/hook-extract.sh', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/workflow-boundaries-fixture.ts', 'test/skill-e2e-investigate-owned-completion.test.ts', 'test/helpers/e2e-gate.ts'], + 'investigate-owned-abort': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'investigate/**', 'freeze/**', 'guard/**', 'unfreeze/**', 'careful/bin/hook-extract.sh', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/workflow-boundaries-fixture.ts', 'test/skill-e2e-investigate-owned-termination.test.ts', 'test/helpers/e2e-gate.ts'], + 'investigate-owned-ending-error': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'investigate/**', 'freeze/**', 'guard/**', 'unfreeze/**', 'careful/bin/hook-extract.sh', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/workflow-boundaries-fixture.ts', 'test/skill-e2e-investigate-owned-termination.test.ts', 'test/helpers/e2e-gate.ts'], + 'shared-libs-review-path-eligibility': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'review/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'lib/review-evidence.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'bin/gstack-wtree', 'test/helpers/shared-libs-eval-fixture.ts', 'test/skill-e2e-shared-libs-paths.test.ts', 'test/helpers/shared-libs-path-fixture.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-index-flags-*.json', 'test/fixtures/shared-libs-resolved-reads-public.json', 'test/helpers/shared-libs-review-start-evidence.ts'], + 'shared-libs-review-index-flags': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'review/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'lib/review-evidence.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'bin/gstack-wtree', 'test/helpers/shared-libs-eval-fixture.ts', 'test/skill-e2e-shared-libs-paths.test.ts', 'test/helpers/shared-libs-path-fixture.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-index-flags-*.json', 'test/fixtures/shared-libs-paths-max-turns-public.json', 'test/fixtures/shared-libs-resolved-reads-public.json', 'test/helpers/shared-libs-review-start-evidence.ts'], + 'shared-libs-review-prior-coverage': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'review/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'lib/review-evidence.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'bin/gstack-wtree', 'test/helpers/shared-libs-eval-fixture.ts', 'test/skill-e2e-shared-libs-paths.test.ts', 'test/helpers/shared-libs-path-fixture.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-index-flags-*.json', 'test/fixtures/shared-libs-resolved-reads-public.json', 'test/helpers/shared-libs-review-start-evidence.ts'], 'shared-libs-codex-read-only': ['deslop-shared-libs/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/index.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/shared-libs-eval-fixture.ts', 'test/helpers/codex-session-runner.ts', 'test/helpers/skill-fixture.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/eval-budgets.ts', 'test/codex-e2e-shared-libs.test.ts', 'test/helpers/e2e-gate.ts', 'hosts/codex.ts', 'hosts/define-host.ts', 'scripts/resolvers/constants.ts', 'test/fixtures/shared-libs-readonly-substitution-ci16358.json', 'test/helpers/agent-sdk-runner.ts'], // Shared-code audit and scoped review lifecycle 'shared-libs-read-only': ['deslop-shared-libs/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/index.ts', 'test/helpers/shared-libs-eval-fixture.ts', 'test/skill-e2e-shared-libs.test.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-readonly-substitution-ci16358.json', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/shared-libs-review-start-evidence.ts', 'test/helpers/shared-libs-path-fixture.ts'], 'shared-libs-unsupported-git': ['deslop-shared-libs/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/index.ts', 'test/helpers/shared-libs-eval-fixture.ts', 'test/skill-e2e-shared-libs.test.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-readonly-substitution-ci16358.json', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/shared-libs-review-start-evidence.ts', 'test/helpers/shared-libs-path-fixture.ts'], - 'shared-libs-review-lifecycle': ['deslop-shared-libs/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/index.ts', 'test/helpers/shared-libs-eval-fixture.ts', 'review/**', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'lib/review-evidence.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'bin/gstack-wtree', 'test/skill-e2e-shared-libs.test.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-index-flags-*.json', 'test/helpers/shared-libs-path-fixture.ts', 'test/fixtures/shared-libs-lifecycle-r59-stage-scope-public.json', 'test/helpers/shared-libs-review-start-evidence.ts'], - 'shared-libs-review-revalidation': ['deslop-shared-libs/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/index.ts', 'test/helpers/shared-libs-eval-fixture.ts', 'review/**', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'lib/review-evidence.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'bin/gstack-wtree', 'test/skill-e2e-shared-libs.test.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/helpers/shared-libs-review-start-evidence.ts', 'test/fixtures/shared-libs-review-start-public.json', 'test/fixtures/shared-libs-revalidation-max-turns-public.json', 'test/fixtures/shared-libs-index-flags-*.json', 'test/helpers/shared-libs-path-fixture.ts' ], + 'shared-libs-review-lifecycle': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'deslop-shared-libs/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/index.ts', 'test/helpers/shared-libs-eval-fixture.ts', 'review/**', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'lib/review-evidence.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'bin/gstack-wtree', 'test/skill-e2e-shared-libs.test.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-index-flags-*.json', 'test/helpers/shared-libs-path-fixture.ts', 'test/fixtures/shared-libs-lifecycle-r59-stage-scope-public.json', 'test/helpers/shared-libs-review-start-evidence.ts'], + 'shared-libs-review-revalidation': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'deslop-shared-libs/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/index.ts', 'test/helpers/shared-libs-eval-fixture.ts', 'review/**', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'lib/review-evidence.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'bin/gstack-wtree', 'test/skill-e2e-shared-libs.test.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/helpers/shared-libs-review-start-evidence.ts', 'test/fixtures/shared-libs-review-start-public.json', 'test/fixtures/shared-libs-revalidation-max-turns-public.json', 'test/fixtures/shared-libs-index-flags-*.json', 'test/helpers/shared-libs-path-fixture.ts' ], 'shared-libs-opportunity-judgment': ['deslop-shared-libs/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/index.ts', 'test/helpers/shared-libs-eval-fixture.ts', 'test/skill-e2e-shared-libs-periodic.test.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/llm-judge.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-readonly-substitution-ci16358.json', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/shared-libs-plan-actor.ts', 'test/helpers/shared-libs-plan-excerpt.ts'], 'shared-libs-pr-coverage': ['deslop-shared-libs/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/index.ts', 'test/helpers/shared-libs-eval-fixture.ts', 'test/skill-e2e-shared-libs-periodic.test.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/llm-judge.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts', 'test/fixtures/shared-libs-readonly-substitution-ci16358.json', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/shared-libs-plan-actor.ts', 'test/helpers/shared-libs-plan-excerpt.ts'], 'shared-libs-plan-callers': ['test/helpers/shared-libs-plan-actor.ts', 'scripts/resolvers/confidence.ts', 'test/helpers/shared-libs-plan-excerpt.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'deslop-shared-libs/**', 'scripts/resolvers/shared-libs.ts', 'scripts/resolvers/index.ts', 'test/helpers/shared-libs-eval-fixture.ts', 'plan-eng-review/**', 'test/skill-e2e-shared-libs-periodic.test.ts', 'test/fixtures/plan-scope-recovery-av.json', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'test/helpers/e2e-gate.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/agent-sdk-runner.ts', 'test/helpers/llm-judge.ts', 'lib/claude-bin.ts', 'lib/eval-model.ts'], - 'ship-docsync-missing-marker': ['ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], - 'ship-docsync-missing-asset': ['ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], - 'ship-docsync-launch-failure': ['ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], - 'ship-docsync-timeout-unsettled': ['ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], - 'ship-docsync-late-result': ['ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], - 'ship-docsync-stale-before': ['ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], - 'ship-docsync-stale-after': ['ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], - 'ship-docsync-recovery': ['ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], + 'ship-docsync-missing-marker': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], + 'ship-docsync-missing-asset': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], + 'ship-docsync-launch-failure': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], + 'ship-docsync-timeout-unsettled': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], + 'ship-docsync-late-result': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], + 'ship-docsync-stale-before': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], + 'ship-docsync-stale-after': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], + 'ship-docsync-recovery': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/testing.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/docsync-*.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts'], 'review-exploratory-small-cli': ['bin/gstack-qa-evidence', 'lib/qa-evidence.ts', 'lib/fs-atomic.ts', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-evidence.ts', 'bin/gstack-qa-deadline', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'review/**', 'ship/**', 'qa/**', 'scripts/resolvers/qa.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/qa-callers-fixture.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-fixture.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/session-runner.ts', 'test/skill-e2e-qa-callers.test.ts', 'scripts/resolvers/testing.ts', 'test/helpers/skill-census.ts'], 'ship-exploratory-small-cli': ['bin/gstack-qa-evidence', 'lib/qa-evidence.ts', 'lib/fs-atomic.ts', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-evidence.ts', 'bin/gstack-qa-deadline', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'review/**', 'ship/**', 'qa/**', 'scripts/resolvers/qa.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/qa-callers-fixture.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-fixture.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/session-runner.ts', 'test/skill-e2e-qa-callers.test.ts', 'scripts/resolvers/testing.ts', 'test/helpers/skill-census.ts'], 'ship-exploratory-unavailable': ['bin/gstack-qa-evidence', 'lib/qa-evidence.ts', 'lib/fs-atomic.ts', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-evidence.ts', 'bin/gstack-qa-deadline', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'review/**', 'ship/**', 'qa/**', 'scripts/resolvers/qa.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/review-army.ts', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/qa-callers-fixture.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-fixture.ts', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/session-runner.ts', 'test/skill-e2e-qa-callers.test.ts', 'scripts/resolvers/testing.ts', 'test/helpers/skill-census.ts'], @@ -69,13 +69,13 @@ export const E2E_TOUCHFILES: Record = { 'qa-functional-cli-fix': ['bin/gstack-qa-evidence', 'bin/gstack-qa-deadline', 'lib/qa-evidence.ts', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'lib/fs-atomic.ts', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-evidence.ts', 'qa/**', 'qa-only/**', 'scripts/resolvers/qa.ts', 'scripts/resolvers/utility.ts', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/skill-fixture.ts', 'test/skill-e2e-qa-functional-fix.test.ts', 'test/helpers/office-hours-attempt.ts'], 'qa-functional-webhook-fix': ['bin/gstack-qa-evidence', 'bin/gstack-qa-deadline', 'lib/qa-evidence.ts', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'lib/fs-atomic.ts', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-evidence.ts', 'qa/**', 'qa-only/**', 'scripts/resolvers/qa.ts', 'scripts/resolvers/utility.ts', 'scripts/resolvers/sections.ts', 'scripts/gen-skill-docs.ts', 'test/helpers/qa-functional-*.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/skill-fixture.ts', 'test/skill-e2e-qa-functional-fix.test.ts', 'test/helpers/office-hours-attempt.ts'], // Browse core (+ test-server dependency) - 'browse-basic': [ 'browse/src/**', 'browse/test/test-server.ts', 'test/skill-e2e-bws.test.ts'], - 'browse-snapshot': [ 'browse/src/**', 'browse/test/test-server.ts', 'test/skill-e2e-bws.test.ts'], + 'browse-basic': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/skill-e2e-bws.test.ts'], + 'browse-snapshot': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/skill-e2e-bws.test.ts'], // Aside-driven browsing skills — live E2E against the Aside AI browser, the // primary browser (test/skill-e2e-aside.test.ts self-skips without a running Aside) - 'aside-browse-basic': [ 'browse/**', 'scripts/resolvers/browse.ts', 'scripts/resolvers/aside.ts', 'browse/test/test-server.ts', 'browse/test/fixtures/basic.html', 'test/helpers/aside-available.ts', 'test/skill-e2e-aside.test.ts', 'test/helpers/e2e-gate.ts'], - 'aside-browse-flow': [ 'browse/**', 'scripts/resolvers/browse.ts', 'scripts/resolvers/aside.ts', 'browse/test/test-server.ts', 'browse/test/fixtures/forms.html', 'test/helpers/aside-available.ts', 'test/skill-e2e-aside.test.ts', 'test/helpers/e2e-gate.ts'], + 'aside-browse-basic': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'browse/**', 'scripts/resolvers/browse.ts', 'scripts/resolvers/aside.ts', 'browse/test/test-server.ts', 'browse/test/fixtures/basic.html', 'test/helpers/aside-available.ts', 'test/skill-e2e-aside.test.ts', 'test/helpers/e2e-gate.ts'], + 'aside-browse-flow': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'browse/**', 'scripts/resolvers/browse.ts', 'scripts/resolvers/aside.ts', 'browse/test/test-server.ts', 'browse/test/fixtures/forms.html', 'test/helpers/aside-available.ts', 'test/skill-e2e-aside.test.ts', 'test/helpers/e2e-gate.ts'], 'aside-qa-quick': [ 'qa/**', 'scripts/resolvers/browse.ts', 'scripts/resolvers/aside.ts', 'browse/test/test-server.ts', 'browse/test/fixtures/basic.html', 'test/helpers/aside-available.ts', 'test/skill-e2e-aside.test.ts', 'test/helpers/e2e-gate.ts'], 'aside-scrape-json': [ 'scrape/**', 'scripts/resolvers/aside.ts', 'browse/test/test-server.ts', 'browse/test/fixtures/basic.html', 'test/helpers/aside-available.ts', 'test/skill-e2e-aside.test.ts', 'test/helpers/e2e-gate.ts'], 'aside-canary-quick': [ 'canary/**', 'scripts/resolvers/aside.ts', 'browse/test/test-server.ts', 'browse/test/fixtures/basic.html', 'test/helpers/aside-available.ts', 'test/skill-e2e-aside.test.ts', 'test/helpers/e2e-gate.ts'], @@ -89,7 +89,7 @@ export const E2E_TOUCHFILES: Record = { // through the real runner, plus the script wiring that gates + maps it // (token-reduction Phase 2: generate-first-run-guidance.ts was deleted; the // gate + token→tip map live in bin/gstack-skill-start's emission layer). - 'first-task-scaffold': [ 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'bin/gstack-first-task-detect', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'test/skill-e2e-first-task-scaffold.test.ts', 'test/helpers/session-runner.ts'], + 'first-task-scaffold': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'bin/gstack-first-task-detect', 'scripts/resolvers/preamble/generate-preamble-bash.ts', 'test/skill-e2e-first-task-scaffold.test.ts', 'test/helpers/session-runner.ts'], // SKILL.md setup + preamble (depend on ROOT SKILL.md + gen-skill-docs) 'skillmd-setup-discovery': [ 'SKILL.md', 'SKILL.md.tmpl', 'scripts/gen-skill-docs.ts', 'test/skill-e2e-bws.test.ts'], @@ -97,16 +97,16 @@ export const E2E_TOUCHFILES: Record = { 'skillmd-outside-git': [ 'SKILL.md', 'SKILL.md.tmpl', 'scripts/gen-skill-docs.ts', 'test/skill-e2e-bws.test.ts'], 'session-awareness': [ 'SKILL.md', 'SKILL.md.tmpl', 'scripts/gen-skill-docs.ts', 'test/skill-e2e-bws.test.ts'], - 'operational-learning': [ 'scripts/resolvers/preamble.ts', 'bin/gstack-learnings-log', 'test/skill-e2e-bws.test.ts'], + 'operational-learning': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'scripts/resolvers/preamble.ts', 'bin/gstack-learnings-log', 'test/skill-e2e-bws.test.ts'], // QA (+ test-server dependency). /qa drives Aside first (the resolver) and // the browse binary as fallback (browse/src), so both are deps. - 'qa-quick': ['bin/gstack-qa-deadline', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'qa/**', 'scripts/resolvers/browse.ts', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/skill-e2e-qa-workflow.test.ts', 'test/helpers/aside-available.ts', 'test/fixtures/qa-only-browser-probe.ts', 'test/helpers/bootstrap-retention.ts', 'test/helpers/qa-browser-deadline-evidence.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-only-cleanup.ts'], - 'qa-b6-static': [ 'qa/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/helpers/llm-judge.ts', 'browse/test/fixtures/qa-eval.html', 'test/fixtures/qa-eval-ground-truth.json', 'test/skill-e2e-qa-bugs.test.ts', 'test/helpers/aside-available.ts'], - 'qa-b7-spa': [ 'qa/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/helpers/llm-judge.ts', 'browse/test/fixtures/qa-eval-spa.html', 'test/fixtures/qa-eval-spa-ground-truth.json', 'test/skill-e2e-qa-bugs.test.ts', 'test/helpers/aside-available.ts'], - 'qa-b8-checkout': [ 'qa/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/helpers/llm-judge.ts', 'browse/test/fixtures/qa-eval-checkout.html', 'test/fixtures/qa-eval-checkout-ground-truth.json', 'test/skill-e2e-qa-bugs.test.ts', 'test/helpers/aside-available.ts'], - 'qa-only-no-fix': ['test/helpers/qa-only-cleanup.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/fixtures/qa-only-observation-public.json', 'test/fixtures/qa-only-charter-public.json', 'test/fixtures/qa-only-browser-probe.ts', 'browse/test/fixtures/qa-only.html', 'test/helpers/qa-browser-deadline-evidence.ts', 'bin/gstack-qa-deadline', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'qa-only/**', 'qa/sections/**', 'qa/templates/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/skill-e2e-qa-workflow.test.ts', 'test/helpers/aside-available.ts', 'test/helpers/bootstrap-retention.ts', 'test/helpers/qa-evidence-producer.ts'], - 'qa-fix-loop': ['bin/gstack-qa-deadline', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'qa/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/skill-e2e-qa-workflow.test.ts', + 'qa-quick': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'bin/gstack-qa-deadline', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'qa/**', 'scripts/resolvers/browse.ts', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/skill-e2e-qa-workflow.test.ts', 'test/helpers/aside-available.ts', 'test/fixtures/qa-only-browser-probe.ts', 'test/helpers/bootstrap-retention.ts', 'test/helpers/qa-browser-deadline-evidence.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-only-cleanup.ts'], + 'qa-b6-static': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'qa/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/helpers/llm-judge.ts', 'browse/test/fixtures/qa-eval.html', 'test/fixtures/qa-eval-ground-truth.json', 'test/skill-e2e-qa-bugs.test.ts', 'test/helpers/aside-available.ts'], + 'qa-b7-spa': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'qa/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/helpers/llm-judge.ts', 'browse/test/fixtures/qa-eval-spa.html', 'test/fixtures/qa-eval-spa-ground-truth.json', 'test/skill-e2e-qa-bugs.test.ts', 'test/helpers/aside-available.ts'], + 'qa-b8-checkout': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'qa/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/helpers/llm-judge.ts', 'browse/test/fixtures/qa-eval-checkout.html', 'test/fixtures/qa-eval-checkout-ground-truth.json', 'test/skill-e2e-qa-bugs.test.ts', 'test/helpers/aside-available.ts'], + 'qa-only-no-fix': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/helpers/qa-only-cleanup.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/fixtures/qa-only-observation-public.json', 'test/fixtures/qa-only-charter-public.json', 'test/fixtures/qa-only-browser-probe.ts', 'browse/test/fixtures/qa-only.html', 'test/helpers/qa-browser-deadline-evidence.ts', 'bin/gstack-qa-deadline', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'qa-only/**', 'qa/sections/**', 'qa/templates/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/skill-e2e-qa-workflow.test.ts', 'test/helpers/aside-available.ts', 'test/helpers/bootstrap-retention.ts', 'test/helpers/qa-evidence-producer.ts'], + 'qa-fix-loop': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'bin/gstack-qa-deadline', 'lib/qa-deadline.ts', 'lib/claude-code-windows-job.ts', 'qa/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'browse/test/test-server.ts', 'test/skill-e2e-qa-workflow.test.ts', 'test/helpers/aside-available.ts', 'test/fixtures/qa-only-browser-probe.ts', 'test/helpers/bootstrap-retention.ts', 'test/helpers/qa-browser-deadline-evidence.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-only-cleanup.ts'], 'qa-bootstrap': ['test/helpers/bootstrap-retention.ts', 'qa/**', 'ship/**', 'test/skill-e2e-qa-workflow.test.ts', 'scripts/resolvers/testing.ts', 'test/helpers/aside-available.ts', 'test/fixtures/qa-only-browser-probe.ts', 'test/helpers/qa-browser-deadline-evidence.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-only-cleanup.ts'], @@ -117,7 +117,7 @@ export const E2E_TOUCHFILES: Record = { 'review-enum-completeness': [ 'review/**', 'test/fixtures/review-eval-enum*.rb', 'test/skill-e2e-review.test.ts', 'test/fixtures/fake-impeccable.ts', 'test/helpers/fake-impeccable.ts'], 'review-base-branch': [ 'review/**', 'test/skill-e2e-review-attribution.test.ts'], - 'review-design-lite': [ 'review/**', 'test/fixtures/review-eval-design-slop.*', 'test/helpers/fake-impeccable.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/impeccable-detect-sample.json', 'lib/design-catalog.ts', 'lib/design-detect-contract.ts', 'bin/gstack-design-detect.ts', 'scripts/resolvers/design-checklist.ts', 'scripts/resolvers/review-army.ts', 'test/skill-e2e-review.test.ts' + 'review-design-lite': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'review/**', 'test/fixtures/review-eval-design-slop.*', 'test/helpers/fake-impeccable.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/impeccable-detect-sample.json', 'lib/design-catalog.ts', 'lib/design-detect-contract.ts', 'bin/gstack-design-detect.ts', 'scripts/resolvers/design-checklist.ts', 'scripts/resolvers/review-army.ts', 'test/skill-e2e-review.test.ts' ], @@ -185,7 +185,7 @@ export const E2E_TOUCHFILES: Record = { // include question-tuning.ts and generate-ask-user-format.ts because the // AUTO_DECIDE preamble injection lives there and changes can flip the // regression test outcome between 'asked' and 'auto_decided'. - 'plan-ceo-review-plan-mode': [ + 'plan-ceo-review-plan-mode': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/helpers/plan-count-fixture.ts', @@ -203,7 +203,7 @@ export const E2E_TOUCHFILES: Record = { "test/fixtures/eng-option-b-scope-al.json", 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts'], - 'plan-eng-review-plan-mode': [ + 'plan-eng-review-plan-mode': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'lib/claude-public-transcript.ts', 'test/fixtures/auto-decide-recommendation-361c.json', @@ -226,7 +226,7 @@ export const E2E_TOUCHFILES: Record = { "scripts/resolvers/preamble/generate-preamble-bash.ts", 'test/fixtures/pty-companion-cli.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/helpers/owned-claude-transcript.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'scripts/resolvers/testing.ts', 'test/helpers/plan-mode-evidence.ts', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts'], - 'plan-design-review-plan-mode': [ + 'plan-design-review-plan-mode': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'lib/claude-public-transcript.ts', 'test/fixtures/auto-decide-recommendation-361c.json', @@ -252,7 +252,7 @@ export const E2E_TOUCHFILES: Record = { "scripts/resolvers/preamble/generate-preamble-bash.ts", 'test/fixtures/pty-companion-cli.ts', 'test/helpers/plan-seed-submission.ts', 'test/fixtures/plan-seed-cli.ts', 'test/helpers/owned-claude-transcript.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'test/helpers/plan-mode-evidence.ts', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/review-eval-design-slop.html', 'test/helpers/aside-available.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/fake-impeccable.ts', 'test/helpers/llm-judge.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts'], - 'plan-devex-review-plan-mode': [ + 'plan-devex-review-plan-mode': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/fixtures/auto-decide-recommendation-361c.json', @@ -273,7 +273,7 @@ export const E2E_TOUCHFILES: Record = { // (--max-concurrency + --retry 1), so worst-case cost is ~2x a single // pass of each, sharing the API budget with sibling tests — not the // sequential ~+10min a local read suggests. - 'plan-mode-no-op': [ + 'plan-mode-no-op': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/fixtures/auto-decide-recommendation-361c.json', @@ -304,21 +304,21 @@ export const E2E_TOUCHFILES: Record = { // INSIDE the existing 4 plan-X-review-plan-mode test files (covered // transitively by the entries above). Two new standalone files exist for // skills with no prior plan-mode test: - 'office-hours-auto-mode': [ + 'office-hours-auto-mode': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/fixtures/auto-decide-recommendation-361c.json', 'test/fixtures/auto-decide-target-361c.json', 'test/fixtures/native-auto-decide-ag.json', 'test/helpers/native-auto-decide.ts', 'test/fixtures/auto-decide-current-declaration-6aef.json', 'test/fixtures/auto-decide-explanatory-mode-043a.json', 'test/fixtures/auto-decide-explanatory-mode-749df.json', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/question-tuning.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble.ts', 'test/helpers/claude-pty-runner.ts', 'test/fixtures/design-ui-boxed-question.json', 'test/helpers/hermetic-skill-runtime.ts', 'test/helpers/pty-trust-dialog.ts', 'test/skill-e2e-office-hours-auto-mode.test.ts', 'test/fixtures/eng-d2-truncated-border-0bcd.json', 'test/fixtures/eng-d1-clipped-elision-1579.json', 'test/fixtures/eng-d2-planning-prelude-4d.json', 'test/fixtures/ceo-approach-z-call.json', 'test/fixtures/ceo-approach-z-screen.txt', 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-fixture.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts', 'test/helpers/pty-screen.ts'], - 'office-hours-phase4-fork': [ 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/question-tuning.ts', 'test/helpers/llm-judge.ts', 'test/skill-e2e-office-hours-phase4.test.ts' ], + 'office-hours-phase4-fork': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'bin/gstack-skill-start', 'bin/gstack-skill-end', 'office-hours/**', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/preamble/generate-completion-status.ts', 'scripts/resolvers/preamble.ts', 'scripts/resolvers/question-tuning.ts', 'test/helpers/llm-judge.ts', 'test/skill-e2e-office-hours-phase4.test.ts' ], 'llm-judge-recommendation': ['codex/**', 'test/helpers/llm-judge.ts', 'test/llm-judge-recommendation.test.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'codex/SKILL.md.tmpl', 'scripts/resolvers/review.ts'], // v1.21+ AUTO_DECIDE preserve eval (periodic). Verifies the Tool resolution // fix doesn't trip the legitimate /plan-tune opt-in path: when the user has // written a never-ask preference, AUQ should still auto-decide rather than // surfacing the question. Touches the question-tuning + preference // infrastructure plus the resolvers that own the AUTO_DECIDE preamble. - 'auto-decide-preserved': [ + 'auto-decide-preserved': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'hosts/claude/hooks/hook-log.ts', 'lib/claude-public-transcript.ts', 'test/fixtures/auto-decide-recommendation-361c.json', @@ -362,7 +362,7 @@ export const E2E_TOUCHFILES: Record = { "test/fixtures/ceo-hold-commitment-ar.json", 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/ceo-finding-fixture.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'test/helpers/owned-claude-transcript.ts', 'scripts/resolvers/tasks-section.ts', 'test/fixtures/ceo-expansion-pacing-77.json', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/plan-count-artifacts.ts', 'test/helpers/plan-count-file-permission.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts'], - 'plan-design-with-ui-scope': [ 'test/helpers/pty-screen.ts', + 'plan-design-with-ui-scope': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/helpers/pty-screen.ts', 'lib/claude-public-transcript.ts', 'test/fixtures/autoplan-public-narration-ad.json', 'test/helpers/plan-count-fixture.ts', @@ -401,7 +401,7 @@ export const E2E_TOUCHFILES: Record = { // devex, office-hours + future PR2 carves). One file iterating CARVE_GUARDS; // the selector sets GSTACK_CARVE_SKILL= to scope cost to the changed // skill (D-CODEX A). Touching the registry/helper or sections.ts runs all. - 'carve-section-loading': [ + 'carve-section-loading': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'bin/gstack-review-log', 'bin/gstack-review-read', 'lib/review-evidence.ts', 'bin/gstack-slug', 'bin/gstack-wtree', 'bin/gstack-config', 'bin/gstack-brain-enqueue', 'test/fixtures/autoplan-amend-input-77.json', @@ -424,7 +424,7 @@ export const E2E_TOUCHFILES: Record = { // review-phase AskUserQuestion). Uses runPlanSkillFloorCheck — minimal // "did agent fire ANY AUQ?" observer that exits early on first non-permission // numbered-option render. ~1-3 min typical wall time per test, ~$2-6 total. - 'plan-eng-finding-floor': [ 'test/helpers/pty-screen.ts', + 'plan-eng-finding-floor': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/helpers/pty-screen.ts', 'lib/claude-public-transcript.ts', 'test/fixtures/plan-create-prepublication-491.json', 'test/fixtures/plan-create-combined-permission-70b.json', 'test/fixtures/plan-floor-quote-70b.json', 'test/fixtures/plan-create-permission-361c.json', @@ -440,7 +440,7 @@ export const E2E_TOUCHFILES: Record = { "scripts/resolvers/preamble/generate-preamble-bash.ts", 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'scripts/resolvers/testing.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts'], - 'plan-ceo-finding-floor': [ 'test/helpers/pty-screen.ts', + 'plan-ceo-finding-floor': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/helpers/pty-screen.ts', 'lib/claude-public-transcript.ts', 'test/fixtures/plan-create-prepublication-491.json', 'test/fixtures/plan-create-combined-permission-70b.json', 'test/fixtures/plan-floor-quote-70b.json', 'test/fixtures/plan-create-permission-361c.json', @@ -452,7 +452,7 @@ export const E2E_TOUCHFILES: Record = { 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'scripts/resolvers/tasks-section.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts'], - 'plan-design-finding-floor': [ 'test/helpers/pty-screen.ts', + 'plan-design-finding-floor': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/helpers/pty-screen.ts', 'lib/claude-public-transcript.ts', 'test/fixtures/plan-create-prepublication-491.json', 'test/fixtures/plan-create-combined-permission-70b.json', 'test/fixtures/plan-floor-quote-70b.json', @@ -469,7 +469,7 @@ export const E2E_TOUCHFILES: Record = { "scripts/resolvers/preamble/generate-preamble-bash.ts", 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'test/helpers/ceo-finding-fixture.ts', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/eng-cache-writer-decision.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/owned-claude-transcript.ts', 'test/helpers/plan-count-pending-exit.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-count-transcript.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts'], - 'plan-devex-finding-floor': [ 'test/helpers/pty-screen.ts', + 'plan-devex-finding-floor': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/helpers/pty-screen.ts', 'test/fixtures/plan-floor-dx-custom-491.json', 'test/fixtures/plan-floor-dx-editor-hint.json', 'lib/claude-public-transcript.ts', 'test/fixtures/plan-create-prepublication-491.json', 'test/fixtures/plan-create-combined-permission-70b.json', 'test/fixtures/plan-floor-quote-70b.json', 'test/fixtures/plan-floor-product-type-70b.json', @@ -487,7 +487,7 @@ export const E2E_TOUCHFILES: Record = { // a model fires one AUQ then batches the rest into a "## Decisions to // confirm" plan write. runPlanSkillFloorCheck cannot detect that shape // (it exits on first AUQ); runPlanSkillCounting can. - 'plan-eng-multi-finding-batching': [ + 'plan-eng-multi-finding-batching': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'lib/claude-public-transcript.ts', 'test/fixtures/plan-create-prepublication-491.json', 'test/fixtures/plan-create-combined-permission-70b.json', 'test/fixtures/plan-create-permission-361c.json', @@ -526,7 +526,7 @@ export const E2E_TOUCHFILES: Record = { "scripts/resolvers/preamble/generate-preamble-bash.ts", 'test/fixtures/pty-companion-cli.ts', 'lib/fs-atomic.ts', 'test/helpers/owned-claude-transcript.ts', 'test/fixtures/webfetch-permission.json', 'test/helpers/plan-skill-questions.ts', 'test/fixtures/eng-auq-validation-error.json', 'test/fixtures/bash-directory-permission.json', 'test/fixtures/design-tasks-bash-permission.json', 'test/fixtures/read-permission.json', 'test/fixtures/ceo-split-e5-numbered-description-491.json', 'test/helpers/plan-skill-question-events.ts', 'test/helpers/plan-skill-question-hook-scope.ts', 'test/helpers/skill-census.ts', 'test/helpers/ceo-finding-fixture.ts', 'test/helpers/plan-review-decisions.ts', 'test/helpers/plan-review-cases.ts', 'test/helpers/llm-judge.ts', 'lib/eval-model.ts', 'test/skill-e2e-plan-decision-classification.test.ts', 'test/fixtures/plan-decision-classification.ts', 'scripts/resolvers/testing.ts', 'test/fixtures/eng-file-permission-repaint.json', 'test/helpers/auto-decision-state.ts', 'test/helpers/autoplan-artifact-digest.ts', 'test/helpers/autoplan-artifact-permission.ts', 'test/helpers/autoplan-artifact-recorder.ts', 'test/helpers/dx-selected-navigation.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/native-auto-decide.ts', 'test/helpers/plan-count-pending-question.ts', 'test/helpers/plan-floor-review.ts', 'test/helpers/plan-floor-target.ts', 'test/helpers/plan-scope-selection.ts', 'test/helpers/plan-seed-submission.ts'], - 'plan-ceo-split-overflow': [ + 'plan-ceo-split-overflow': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'lib/claude-public-transcript.ts', 'test/fixtures/plan-create-prepublication-491.json', 'test/fixtures/plan-create-combined-permission-70b.json', 'test/fixtures/plan-create-permission-361c.json', @@ -551,16 +551,16 @@ export const E2E_TOUCHFILES: Record = { // Agent SDK. Gate-tier (deterministic stub server, fixed inputs); fires // when the skill template, the verify helper, the artifacts-init helper, // or the detect script changes. - 'setup-gbrain-remote': [ + 'setup-gbrain-remote': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/helpers/setup-gbrain-sandbox.ts', 'test/helpers/office-hours-attempt.ts', 'test/helpers/eval-store.ts', 'test/helpers/e2e-helpers.ts', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'lib/eval-model.ts','setup-gbrain/sections/brain-init.md.tmpl', 'setup-gbrain/sections/claude-md-persist.md.tmpl', 'setup-gbrain/sections/manifest.json', 'test/helpers/setup-gbrain-fixture.ts', 'setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'bin/gstack-artifacts-init', 'bin/gstack-gbrain-detect', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-remote.test.ts', 'test/helpers/e2e-gate.ts'], - 'setup-gbrain-bad-token': [ 'setup-gbrain/sections/brain-init.md.tmpl', 'setup-gbrain/sections/manifest.json', 'test/helpers/setup-gbrain-fixture.ts', 'setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-bad-token.test.ts', + 'setup-gbrain-bad-token': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'setup-gbrain/sections/brain-init.md.tmpl', 'setup-gbrain/sections/manifest.json', 'test/helpers/setup-gbrain-fixture.ts', 'setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-bad-token.test.ts', 'test/helpers/setup-gbrain-sandbox.ts', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/helpers/setup-gbrain-fixture-command.ts', 'bin/gstack-gbrain-detect', 'lib/gbrain-local-status.ts', 'lib/gbrain-exec.ts', 'bin/gstack-gbrain-lib.sh', 'bin/gstack-egress-lib.sh', 'bin/gstack-egress-receipt', 'test/helpers/office-hours-attempt.ts', 'test/helpers/e2e-gate.ts'], // v1.34.0.0 split-engine Path 4 + Step 4.5 Yes (local PGLite for code). // Periodic-tier per codex #12 (AgentSDK harness is non-deterministic). // Fires when the setup-gbrain template, install/verify/init helpers, or // the agent-sdk-runner harness changes. - 'setup-gbrain-path4-local-pglite': [ 'setup-gbrain/sections/brain-init.md.tmpl', 'setup-gbrain/sections/claude-md-persist.md.tmpl', 'setup-gbrain/sections/manifest.json', 'test/helpers/setup-gbrain-fixture.ts', 'setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'bin/gstack-gbrain-install', 'bin/gstack-gbrain-detect', 'lib/gbrain-local-status.ts', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-path4-local-pglite.test.ts', + 'setup-gbrain-path4-local-pglite': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'setup-gbrain/sections/brain-init.md.tmpl', 'setup-gbrain/sections/claude-md-persist.md.tmpl', 'setup-gbrain/sections/manifest.json', 'test/helpers/setup-gbrain-fixture.ts', 'setup-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-mcp-verify', 'bin/gstack-gbrain-install', 'bin/gstack-gbrain-detect', 'lib/gbrain-local-status.ts', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-setup-gbrain-path4-local-pglite.test.ts', 'test/helpers/setup-gbrain-sandbox.ts', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/helpers/setup-gbrain-fixture-command.ts', 'lib/gbrain-exec.ts', 'bin/gstack-gbrain-lib.sh', 'bin/gstack-egress-lib.sh', 'bin/gstack-egress-receipt', 'test/helpers/office-hours-attempt.ts', 'test/helpers/e2e-gate.ts'], // AskUserQuestion format regression (RECOMMENDATION + Completeness: N/10) @@ -617,7 +617,7 @@ export const E2E_TOUCHFILES: Record = { // Expanded coverage (CT3) — 6 non-plan-review skills inherit Pros/Cons via preamble // /plan-tune (v1 observational) - 'plan-tune-inspect': [ 'plan-tune/**', 'scripts/question-registry.ts', 'scripts/psychographic-signals.ts', 'scripts/one-way-doors.ts', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'bin/gstack-developer-profile', 'test/skill-e2e-plan-tune.test.ts'], + 'plan-tune-inspect': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'plan-tune/**', 'scripts/question-registry.ts', 'scripts/psychographic-signals.ts', 'scripts/one-way-doors.ts', 'bin/gstack-question-log', 'bin/gstack-question-preference', 'bin/gstack-developer-profile', 'test/skill-e2e-plan-tune.test.ts'], // /plan-tune cathedral (T16 — 5 E2E scenarios, all gate per D12) @@ -649,19 +649,19 @@ export const E2E_TOUCHFILES: Record = { 'scripts/resolvers/testing.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/review.ts', 'test/helpers/llm-judge.ts', 'test/helpers/office-hours-attempt.ts', 'test/helpers/office-hours-completion.ts'], // Ship - 'ship-managed-hook-refresh': ['ship/**', 'bin/gstack-redact', 'bin/gstack-config', 'scripts/gen-skill-docs.ts', + 'ship-managed-hook-refresh': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'bin/gstack-redact', 'bin/gstack-config', 'scripts/gen-skill-docs.ts', 'test/helpers/ship-hook-actor.ts', 'test/helpers/workflow-excerpt.ts', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-ship-hook-refresh.test.ts', 'test/helpers/e2e-gate.ts'], - 'ship-unmanaged-hook-consent': ['ship/**', 'bin/gstack-redact', 'bin/gstack-config', 'scripts/gen-skill-docs.ts', + 'ship-unmanaged-hook-consent': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'bin/gstack-redact', 'bin/gstack-config', 'scripts/gen-skill-docs.ts', 'test/helpers/ship-hook-actor.ts', 'test/helpers/workflow-excerpt.ts', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-ship-hook-consent.test.ts', 'test/helpers/e2e-gate.ts'], - 'ship-local-hook-preservation': ['ship/**', 'bin/gstack-redact', 'bin/gstack-config', 'scripts/gen-skill-docs.ts', + 'ship-local-hook-preservation': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'bin/gstack-redact', 'bin/gstack-config', 'scripts/gen-skill-docs.ts', 'test/helpers/ship-hook-actor.ts', 'test/helpers/workflow-excerpt.ts', 'test/helpers/agent-sdk-runner.ts', 'test/skill-e2e-ship-hook-consent.test.ts', 'test/helpers/e2e-gate.ts'], - 'ship-base-branch': [ 'ship/**', 'bin/gstack-repo-mode', 'test/skill-e2e-review-attribution.test.ts', + 'ship-base-branch': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'bin/gstack-repo-mode', 'test/skill-e2e-review-attribution.test.ts', 'scripts/resolvers/testing.ts' ], 'ship-local-workflow': [ 'ship/**', 'scripts/gen-skill-docs.ts', 'test/skill-e2e-workflow.test.ts', @@ -671,29 +671,29 @@ export const E2E_TOUCHFILES: Record = { ], // Retro - 'retro': [ 'bin/gstack-retro-metrics', 'retro/**', 'test/skill-e2e-retro.test.ts'], - 'retro-base-branch': [ 'bin/gstack-retro-metrics', 'retro/**', 'test/skill-e2e-retro.test.ts'], + 'retro': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'bin/gstack-retro-metrics', 'retro/**', 'test/skill-e2e-retro.test.ts'], + 'retro-base-branch': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'bin/gstack-retro-metrics', 'retro/**', 'test/skill-e2e-retro.test.ts'], // CSO - 'cso-full-audit': [ 'cso/**', 'lib/cso/**', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/skill-e2e-cso.test.ts'], - 'cso-diff-mode': [ 'cso/**', 'lib/cso/**', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/skill-e2e-cso.test.ts'], - 'cso-infra-scope': [ 'cso/**', 'lib/cso/**', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/skill-e2e-cso.test.ts'], + 'cso-full-audit': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'cso/**', 'lib/cso/**', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/skill-e2e-cso.test.ts'], + 'cso-diff-mode': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'cso/**', 'lib/cso/**', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/skill-e2e-cso.test.ts'], + 'cso-infra-scope': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'cso/**', 'lib/cso/**', 'lib/redact-engine.ts', 'lib/redact-patterns.ts', 'test/skill-e2e-cso.test.ts'], // Learnings - 'learnings-show': [ 'learn/**', 'bin/gstack-learnings-search', 'bin/gstack-learnings-log', 'scripts/resolvers/learnings.ts', 'test/skill-e2e-learnings.test.ts'], + 'learnings-show': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'learn/**', 'bin/gstack-learnings-search', 'bin/gstack-learnings-log', 'scripts/resolvers/learnings.ts', 'test/skill-e2e-learnings.test.ts'], // Session Intelligence (timeline, context recovery, /context-save + /context-restore) - 'timeline-event-flow': [ 'bin/gstack-timeline-log', 'bin/gstack-timeline-read', 'test/skill-e2e-session-intelligence.test.ts'], - 'context-recovery-artifacts': [ 'scripts/resolvers/preamble.ts', 'bin/gstack-timeline-log', 'bin/gstack-slug', 'learn/**', 'test/skill-e2e-session-intelligence.test.ts'], - 'context-save-writes-file': [ 'context-save/**', 'bin/gstack-slug', 'test/skill-e2e-session-intelligence.test.ts'], - 'context-restore-loads-latest': [ 'context-restore/**', 'bin/gstack-slug', 'test/skill-e2e-session-intelligence.test.ts'], + 'timeline-event-flow': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'bin/gstack-timeline-log', 'bin/gstack-timeline-read', 'test/skill-e2e-session-intelligence.test.ts'], + 'context-recovery-artifacts': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'scripts/resolvers/preamble.ts', 'bin/gstack-timeline-log', 'bin/gstack-slug', 'learn/**', 'test/skill-e2e-session-intelligence.test.ts'], + 'context-save-writes-file': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'context-save/**', 'bin/gstack-slug', 'test/skill-e2e-session-intelligence.test.ts'], + 'context-restore-loads-latest': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'context-restore/**', 'bin/gstack-slug', 'test/skill-e2e-session-intelligence.test.ts'], // Context skills E2E (live-fire, Skill-tool routing path) — see // test/skill-e2e-context-skills.test.ts. These are periodic-tier because // each one spawns claude -p and costs ~$0.20-$0.40. Collectively they // verify the thing the /checkpoint → /context-save rename was for. 'context-save-routing': [ 'context-save/**', 'scripts/resolvers/preamble.ts', 'test/skill-e2e-context-skills.test.ts'], - 'context-save-then-restore-roundtrip': [ 'context-save/**', 'context-restore/**', 'bin/gstack-slug', 'test/skill-e2e-context-skills.test.ts'], + 'context-save-then-restore-roundtrip': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'context-save/**', 'context-restore/**', 'bin/gstack-slug', 'test/skill-e2e-context-skills.test.ts'], 'context-restore-fragment-match': [ 'context-restore/**', 'test/skill-e2e-context-skills.test.ts'], 'context-restore-empty-state': [ 'context-restore/**', 'test/skill-e2e-context-skills.test.ts'], 'context-restore-list-delegates': [ 'context-restore/**', 'test/skill-e2e-context-skills.test.ts'], @@ -724,7 +724,7 @@ export const E2E_TOUCHFILES: Record = { 'test/helpers/skill-fixture.ts', 'test/helpers/outside-voice-fixture.ts', 'test/helpers/outside-voice-evidence.ts', 'test/fixtures/outside-async-task-m-events.json', 'test/skill-e2e-outside-voice.test.ts', 'test/helpers/outside-voice-receipt.ts', 'test/helpers/e2e-gate.ts'], - 'outside-voice-claude-code-to-codex': [ + 'outside-voice-claude-code-to-codex': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'review/**', 'codex/**', 'hosts/claude.ts', 'hosts/define-host.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/index.ts', 'scripts/resolvers/outside-voice.ts', 'scripts/resolvers/constants.ts', 'scripts/resolvers/review.ts', @@ -733,7 +733,7 @@ export const E2E_TOUCHFILES: Record = { 'test/helpers/skill-fixture.ts', 'test/helpers/outside-voice-fixture.ts', 'test/helpers/outside-voice-evidence.ts', 'test/fixtures/outside-async-task-m-events.json', 'test/skill-e2e-outside-voice.test.ts', 'test/helpers/codex-session-runner.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/outside-voice-receipt.ts'], // Disabled means no extra plan review, including a native Agent fallback. - 'outside-plan-disabled-no-fallback': [ + 'outside-plan-disabled-no-fallback': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/fixtures/disabled-retained-record.json', 'scripts/resolvers/testing.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', @@ -759,7 +759,7 @@ export const E2E_TOUCHFILES: Record = { // Coverage audit (shared fixture) + triage + gates - 'ship-coverage-audit': [ + 'ship-coverage-audit': ['bin/gstack-state-root.sh', 'lib/state-root.ts', "test/fixtures/coverage-audit-aw.json", @@ -804,20 +804,20 @@ export const E2E_TOUCHFILES: Record = { "scripts/resolvers/preamble/generate-preamble-bash.ts", "scripts/resolvers/preamble/generate-completion-status.ts", 'test/helpers/coverage-audit.ts', 'test/helpers/office-hours-attempt.ts', 'scripts/resolvers/testing.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/resolvers/review.ts' ], - 'ship-triage': [ 'ship/**', 'bin/gstack-repo-mode', 'test/skill-e2e-triage.test.ts', + 'ship-triage': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'bin/gstack-repo-mode', 'test/skill-e2e-triage.test.ts', 'scripts/resolvers/testing.ts' ], - 'ship-docsync': [ 'ship/**', 'document-release/**', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/sections.ts', 'test/skill-e2e-ship-docsync.test.ts', + 'ship-docsync': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/sections.ts', 'test/skill-e2e-ship-docsync.test.ts', 'scripts/resolvers/testing.ts', 'test/helpers/docsync-*.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts'], - 'ship-docsync-completion': ['ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts'], - 'ship-docsync-current': ['ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts'], - 'ship-docsync-failure': ['ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts'], - 'ship-docsync-store': ['ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts'], + 'ship-docsync-completion': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts'], + 'ship-docsync-current': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts'], + 'ship-docsync-failure': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts'], + 'ship-docsync-store': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'ship/**', 'document-release/**', 'test/skill-e2e-ship-docsync.test.ts', 'test/helpers/docsync-*.ts', 'test/helpers/session-runner.ts', 'test/helpers/hermetic-env.ts', 'bin/gstack-skill-start', 'bin/gstack-session-kind', 'scripts/resolvers/sections.ts', 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'scripts/gen-skill-docs.ts', 'scripts/resolvers/testing.ts', 'test/helpers/qa-checkpoint-evidence.ts', 'test/helpers/qa-functional-observer.ts', 'test/helpers/e2e-gate.ts', 'test/helpers/qa-evidence-producer.ts', 'test/helpers/qa-functional-fixture.ts'], // #2733 behavioral proof: the JSON contract survives a firing gate inside a // spawned-marked subagent. Deps name every behavior under test — the // session-kind override, the skill-start gates, both hooks + the shared // directive, and the AUQ prose rule — so changing any of them selects it. - 'docsync-spawned': [ + 'docsync-spawned': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'hosts/claude/hooks/hook-log.ts', 'ship/sections/pr-body.md', 'document-release/**', 'ship/sections/documentation.md', @@ -844,15 +844,15 @@ export const E2E_TOUCHFILES: Record = { "scripts/resolvers/preamble/generate-preamble-bash.ts", "scripts/resolvers/preamble/generate-completion-status.ts", 'scripts/resolvers/preamble/generate-ask-user-format.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/review-eval-design-slop.html', 'test/helpers/aside-available.ts', 'test/helpers/fake-impeccable.ts', 'test/helpers/llm-judge.ts', 'test/helpers/office-hours-attempt.ts'], - 'design-review-fix': [ 'design-review/**', 'scripts/resolvers/aside.ts', 'scripts/resolvers/design.ts', 'lib/design-catalog.ts', 'browse/src/**', 'scripts/gen-skill-docs.ts', 'test/skill-e2e-design.test.ts', + 'design-review-fix': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'design-review/**', 'scripts/resolvers/aside.ts', 'scripts/resolvers/design.ts', 'lib/design-catalog.ts', 'browse/src/**', 'scripts/gen-skill-docs.ts', 'test/skill-e2e-design.test.ts', 'scripts/resolvers/testing.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/review-eval-design-slop.html', 'test/helpers/aside-available.ts', 'test/helpers/fake-impeccable.ts', 'test/helpers/llm-judge.ts', 'test/helpers/office-hours-attempt.ts'], // Design detector (user-installed impeccable engine) through the fake engine shim: source mode on a diff and DOM mode on a served page. - 'design-review-detector-shim': [ 'design-review/**', 'scripts/resolvers/design.ts', 'lib/design-catalog.ts', 'lib/design-detect-contract.ts', 'lib/dom-dump-script.ts', 'lib/dom-dump.js', 'bin/gstack-design-detect.ts', 'test/helpers/fake-impeccable.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/impeccable-detect-sample.json', 'test/fixtures/review-eval-design-slop.*', 'test/skill-e2e-design.test.ts', + 'design-review-detector-shim': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'design-review/**', 'scripts/resolvers/design.ts', 'lib/design-catalog.ts', 'lib/design-detect-contract.ts', 'lib/dom-dump-script.ts', 'lib/dom-dump.js', 'bin/gstack-design-detect.ts', 'test/helpers/fake-impeccable.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/impeccable-detect-sample.json', 'test/fixtures/review-eval-design-slop.*', 'test/skill-e2e-design.test.ts', 'scripts/resolvers/testing.ts', 'test/helpers/aside-available.ts', 'test/helpers/llm-judge.ts', 'test/helpers/office-hours-attempt.ts'], - 'design-review-detector-shim-dom': [ 'design-review/**', 'scripts/resolvers/design.ts', 'lib/design-detect-contract.ts', 'lib/dom-dump-script.ts', 'lib/dom-dump.js', 'bin/gstack-design-detect.ts', 'browse/src/**', 'test/helpers/fake-impeccable.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/impeccable-detect-sample.json', 'test/fixtures/review-eval-design-slop.*', 'test/skill-e2e-design.test.ts', + 'design-review-detector-shim-dom': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'design-review/**', 'scripts/resolvers/design.ts', 'lib/design-detect-contract.ts', 'lib/dom-dump-script.ts', 'lib/dom-dump.js', 'bin/gstack-design-detect.ts', 'browse/src/**', 'test/helpers/fake-impeccable.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/impeccable-detect-sample.json', 'test/fixtures/review-eval-design-slop.*', 'test/skill-e2e-design.test.ts', 'scripts/resolvers/testing.ts', 'test/helpers/aside-available.ts', 'test/helpers/llm-judge.ts', 'test/helpers/office-hours-attempt.ts'], - 'design-review-plugin-handoff': [ 'design-review/**', 'scripts/resolvers/design.ts', 'scripts/resolvers/testing.ts', 'lib/design-catalog.ts', 'lib/design-detect-contract.ts', 'bin/gstack-design-detect.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/fake-impeccable.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/impeccable-detect-sample.json', 'test/fixtures/review-eval-design-slop.html', 'test/skill-e2e-design.test.ts', 'test/helpers/aside-available.ts', 'test/helpers/llm-judge.ts', 'test/helpers/office-hours-attempt.ts'], - 'design-html-slop-gate': [ 'design-html/**', 'scripts/resolvers/design.ts', 'lib/design-detect-contract.ts', 'bin/gstack-design-detect.ts', 'test/helpers/fake-impeccable.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/impeccable-detect-sample.json', 'test/skill-e2e-design.test.ts', 'test/fixtures/review-eval-design-slop.html', 'test/helpers/aside-available.ts', 'test/helpers/llm-judge.ts', 'test/helpers/office-hours-attempt.ts'], + 'design-review-plugin-handoff': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'design-review/**', 'scripts/resolvers/design.ts', 'scripts/resolvers/testing.ts', 'lib/design-catalog.ts', 'lib/design-detect-contract.ts', 'bin/gstack-design-detect.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/fake-impeccable.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/impeccable-detect-sample.json', 'test/fixtures/review-eval-design-slop.html', 'test/skill-e2e-design.test.ts', 'test/helpers/aside-available.ts', 'test/helpers/llm-judge.ts', 'test/helpers/office-hours-attempt.ts'], + 'design-html-slop-gate': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'design-html/**', 'scripts/resolvers/design.ts', 'lib/design-detect-contract.ts', 'bin/gstack-design-detect.ts', 'test/helpers/fake-impeccable.ts', 'test/fixtures/fake-impeccable.ts', 'test/fixtures/impeccable-detect-sample.json', 'test/skill-e2e-design.test.ts', 'test/fixtures/review-eval-design-slop.html', 'test/helpers/aside-available.ts', 'test/helpers/llm-judge.ts', 'test/helpers/office-hours-attempt.ts'], // /diagram (diagram-render bundle consumers). Triplet = deterministic // functional (gate); authoring quality = LLM-judged benchmark (periodic). @@ -860,23 +860,23 @@ export const E2E_TOUCHFILES: Record = { // bin/gstack-render.ts): Aside when it is running, the browse daemon // otherwise — so both engines are deps. Triplet = deterministic functional // (gate); authoring quality = LLM-judged benchmark (periodic). - 'diagram-triplet': [ 'diagram/**', 'lib/diagram-render/**', 'lib/aside-render.ts', 'bin/gstack-render.ts', 'test/helpers/aside-available.ts', 'browse/src/**', 'test/skill-e2e-diagram.test.ts', 'test/helpers/llm-judge.ts'], - 'diagram-authoring-quality': [ 'diagram/**', 'lib/diagram-render/**', 'lib/aside-render.ts', 'bin/gstack-render.ts', 'test/helpers/aside-available.ts', 'browse/src/**', 'test/helpers/llm-judge.ts', 'test/skill-e2e-diagram.test.ts'], + 'diagram-triplet': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'diagram/**', 'lib/diagram-render/**', 'lib/aside-render.ts', 'bin/gstack-render.ts', 'test/helpers/aside-available.ts', 'browse/src/**', 'test/skill-e2e-diagram.test.ts', 'test/helpers/llm-judge.ts'], + 'diagram-authoring-quality': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'diagram/**', 'lib/diagram-render/**', 'lib/aside-render.ts', 'bin/gstack-render.ts', 'test/helpers/aside-available.ts', 'browse/src/**', 'test/helpers/llm-judge.ts', 'test/skill-e2e-diagram.test.ts'], // gstack-upgrade 'gstack-upgrade-happy-path': [ 'gstack-upgrade/**', 'test/skill-e2e-workflow.test.ts', 'test/fixtures/coverage-audit-fixture.ts', 'test/helpers/coverage-audit.ts', 'test/helpers/office-hours-attempt.ts'], // Deploy skills 'land-and-deploy-workflow': [ 'land-and-deploy/**', 'scripts/gen-skill-docs.ts', 'test/skill-e2e-deploy.test.ts'], - 'land-and-deploy-first-run': [ 'land-and-deploy/**', 'scripts/gen-skill-docs.ts', 'bin/gstack-slug', 'test/skill-e2e-deploy.test.ts'], - 'land-and-deploy-review-gate': [ 'land-and-deploy/**', 'bin/gstack-review-read', 'test/skill-e2e-deploy.test.ts'], - 'canary-workflow': [ 'canary/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'test/skill-e2e-deploy.test.ts'], - 'benchmark-workflow': [ 'benchmark/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'test/skill-e2e-deploy.test.ts'], + 'land-and-deploy-first-run': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'land-and-deploy/**', 'scripts/gen-skill-docs.ts', 'bin/gstack-slug', 'test/skill-e2e-deploy.test.ts'], + 'land-and-deploy-review-gate': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'land-and-deploy/**', 'bin/gstack-review-read', 'test/skill-e2e-deploy.test.ts'], + 'canary-workflow': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'canary/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'test/skill-e2e-deploy.test.ts'], + 'benchmark-workflow': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'benchmark/**', 'scripts/resolvers/aside.ts', 'browse/src/**', 'test/skill-e2e-deploy.test.ts'], 'setup-deploy-workflow': [ 'setup-deploy/**', 'scripts/gen-skill-docs.ts', 'test/skill-e2e-deploy.test.ts'], // Autoplan - 'autoplan-dual-voice': [ + 'autoplan-dual-voice': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'test/helpers/hermetic-env.ts', 'test/helpers/autoplan-dual-voice-evidence.ts', 'test/fixtures/autoplan-dual-false-positive-6bd.json', @@ -1043,7 +1043,7 @@ export const E2E_TOUCHFILES: Record = { // frontmatter. Touched by anything that changes resolver output, gen // pipeline, detection helper, refresh subcommand, or the on-demand // docs the resolver points to. - 'office-hours-brain-writeback': [ 'office-hours/sections/**', + 'office-hours-brain-writeback': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'office-hours/sections/**', 'scripts/resolvers/gbrain.ts', 'scripts/gen-skill-docs.ts', 'bin/gstack-gbrain-detect', @@ -1064,8 +1064,8 @@ export const E2E_TOUCHFILES: Record = { 'scripts/resolvers/gbrain.ts', 'test/skill-e2e-gbrain-roundtrip-local.test.ts' ], - 'sync-gbrain-read-ready': ['sync-gbrain/SKILL.md.tmpl', 'sync-gbrain/SKILL.md', 'bin/gstack-gbrain-read-capability.ts', 'lib/gbrain-exec.ts', 'test/helpers/sync-gbrain-readiness-fixture.ts', 'test/helpers/sync-gbrain-readiness-verdict.ts', 'test/skill-e2e-sync-gbrain-readiness.test.ts', 'test/helpers/e2e-gate.ts'], - 'sync-gbrain-read-unknown': ['sync-gbrain/SKILL.md.tmpl', 'sync-gbrain/SKILL.md', 'bin/gstack-gbrain-read-capability.ts', 'lib/gbrain-exec.ts', 'test/helpers/sync-gbrain-readiness-fixture.ts', 'test/helpers/sync-gbrain-readiness-verdict.ts', 'test/skill-e2e-sync-gbrain-readiness.test.ts', 'test/helpers/e2e-gate.ts'], + 'sync-gbrain-read-ready': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'sync-gbrain/SKILL.md.tmpl', 'sync-gbrain/SKILL.md', 'bin/gstack-gbrain-read-capability.ts', 'lib/gbrain-exec.ts', 'test/helpers/sync-gbrain-readiness-fixture.ts', 'test/helpers/sync-gbrain-readiness-verdict.ts', 'test/skill-e2e-sync-gbrain-readiness.test.ts', 'test/helpers/e2e-gate.ts'], + 'sync-gbrain-read-unknown': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'sync-gbrain/SKILL.md.tmpl', 'sync-gbrain/SKILL.md', 'bin/gstack-gbrain-read-capability.ts', 'lib/gbrain-exec.ts', 'test/helpers/sync-gbrain-readiness-fixture.ts', 'test/helpers/sync-gbrain-readiness-verdict.ts', 'test/skill-e2e-sync-gbrain-readiness.test.ts', 'test/helpers/e2e-gate.ts'], // WS2 arm benchmark — with-skill vs without-skill agentic arms scored on // the git diff left behind (research instrument, never a release gate). @@ -1497,7 +1497,7 @@ export const E2E_TIERS: Record = { export const LLM_JUDGE_TOUCHFILES: Record = { 'review/SKILL.md workflow': ['review/**', 'qa/**', 'scripts/resolvers/qa.ts', 'scripts/resolvers/review-army.ts', 'scripts/resolvers/review.ts', 'scripts/resolvers/sections.ts', 'test/skill-llm-eval.test.ts', 'test/helpers/workflow-judge-input.ts', 'test/helpers/workflow-judge-cache.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts', 'test/helpers/workflow-excerpt.ts'], 'setup-browser-cookies/SKILL.md workflow': ['setup-browser-cookies/SKILL.md.tmpl', 'setup-browser-cookies/SKILL.md', 'BROWSER.md', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/manual-judge-review-fixture.ts', '.github/cookie-workflow-manual-review.json', 'test/helpers/workflow-judge-input.ts', 'test/skill-llm-eval.test.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/llm-judge.ts', 'test/helpers/workflow-excerpt.ts', 'test/helpers/workflow-judge-cache.ts'], - 'browse/SKILL.md reference': ['browse/sections/**', 'browse/SKILL.md', 'browse/SKILL.md.tmpl', 'browse/src/**', 'test/skill-llm-eval.test.ts', 'SKILL.md', 'SKILL.md.tmpl', 'gstack/llms.txt', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts', 'test/helpers/workflow-excerpt.ts', 'test/helpers/workflow-judge-cache.ts', 'test/helpers/workflow-judge-input.ts'], + 'browse/SKILL.md reference': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'browse/sections/**', 'browse/SKILL.md', 'browse/SKILL.md.tmpl', 'browse/src/**', 'test/skill-llm-eval.test.ts', 'SKILL.md', 'SKILL.md.tmpl', 'gstack/llms.txt', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts', 'test/helpers/workflow-excerpt.ts', 'test/helpers/workflow-judge-cache.ts', 'test/helpers/workflow-judge-input.ts'], 'setup block': ['browse/SKILL.md', 'browse/SKILL.md.tmpl', 'scripts/resolvers/aside.ts', 'scripts/resolvers/browse.ts', 'test/skill-llm-eval.test.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts', 'test/helpers/workflow-excerpt.ts', 'test/helpers/workflow-judge-cache.ts', 'test/helpers/workflow-judge-input.ts'], 'qa/SKILL.md workflow': ['qa/**', 'qa/sections/**', 'qa/SKILL.md', 'qa/SKILL.md.tmpl', 'scripts/resolvers/qa.ts', 'scripts/resolvers/utility.ts', 'scripts/resolvers/sections.ts', 'test/helpers/workflow-judge-input.ts', 'test/skill-llm-eval.test.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts', 'test/helpers/workflow-excerpt.ts', 'test/helpers/workflow-judge-cache.ts'], 'qa/SKILL.md health rubric': ['qa/sections/**', 'qa/SKILL.md', 'qa/SKILL.md.tmpl', 'test/skill-llm-eval.test.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts', 'test/helpers/workflow-excerpt.ts', 'test/helpers/workflow-judge-cache.ts', 'test/helpers/workflow-judge-input.ts'], @@ -1545,7 +1545,7 @@ export const LLM_JUDGE_TOUCHFILES: Record = { 'retro/SKILL.md instructions': ['retro/sections/**', 'retro/SKILL.md', 'retro/SKILL.md.tmpl', 'test/skill-llm-eval.test.ts', 'test/helpers/workflow-judge-input.ts', 'test/helpers/workflow-judge-cache.ts', 'scripts/eval-input-cache.ts', 'test/helpers/workflow-excerpt.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts'], 'qa-only/SKILL.md workflow': ['qa-only/**', 'qa-only/SKILL.md', 'qa-only/SKILL.md.tmpl', 'qa/**', 'scripts/resolvers/qa.ts', 'scripts/resolvers/utility.ts', 'scripts/resolvers/sections.ts', 'test/skill-llm-eval.test.ts', 'test/helpers/workflow-judge-input.ts', 'test/helpers/workflow-judge-cache.ts', 'scripts/eval-input-cache.ts', 'test/helpers/workflow-excerpt.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts'], 'gstack-upgrade/SKILL.md upgrade flow': ['gstack-upgrade/SKILL.md', 'gstack-upgrade/SKILL.md.tmpl', 'test/skill-llm-eval.test.ts', 'test/helpers/workflow-judge-input.ts', 'test/helpers/workflow-judge-cache.ts', 'scripts/eval-input-cache.ts', 'test/helpers/workflow-excerpt.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts'], - 'sync-gbrain/SKILL.md read-only readiness': ['sync-gbrain/SKILL.md', 'sync-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-read-capability.ts', 'test/skill-llm-eval.test.ts', 'test/helpers/workflow-judge-input.ts', 'test/helpers/workflow-judge-cache.ts', 'scripts/eval-input-cache.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts', 'test/helpers/workflow-excerpt.ts'], + 'sync-gbrain/SKILL.md read-only readiness': ['bin/gstack-state-root.sh', 'lib/state-root.ts', 'sync-gbrain/SKILL.md', 'sync-gbrain/SKILL.md.tmpl', 'bin/gstack-gbrain-read-capability.ts', 'test/skill-llm-eval.test.ts', 'test/helpers/workflow-judge-input.ts', 'test/helpers/workflow-judge-cache.ts', 'scripts/eval-input-cache.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts', 'test/helpers/workflow-excerpt.ts'], // Voice directive 'voice directive tone': ['scripts/resolvers/preamble.ts', 'review/SKILL.md', 'review/SKILL.md.tmpl', 'scripts/gen-skill-docs.ts', 'test/skill-llm-eval.test.ts', 'test/fixtures/eval-baselines.json', 'test/helpers/cookie-workflow-judge-input.ts', 'test/helpers/cookie-workflow-manual-review.ts', 'test/helpers/llm-judge.ts', 'test/helpers/workflow-excerpt.ts', 'test/helpers/workflow-judge-cache.ts', 'test/helpers/workflow-judge-input.ts'], diff --git a/test/pty-upgrade-isolation.test.ts b/test/pty-upgrade-isolation.test.ts index 71cf24817..423489d31 100644 --- a/test/pty-upgrade-isolation.test.ts +++ b/test/pty-upgrade-isolation.test.ts @@ -100,11 +100,15 @@ describe('seeded PTY update-check isolation', () => { const state = path.join(home, 'explicit-state'); fs.mkdirSync(state); fs.writeFileSync(path.join(state, 'config.yaml'), 'update_check: true\nartifacts_sync_mode_prompted: true\n'); + // gstack-update-check reads its cache from the same state root GSTACK_HOME selects (docs/state-root.md). + const stateCache = path.join(state, 'last-update-check'); + fs.writeFileSync(stateCache, UPGRADE); const output = runPreamble(home, { ...env, GSTACK_HOME: state }); expect(output).toContain(UPGRADE.trim()); expect(output).toContain('GSTACK_INSTRUCTION_BEGIN: upgrade-flow'); expect(output).toContain('UPDATE_CHECK: true'); expect(fs.readFileSync(cache, 'utf8')).toBe(UPGRADE); + expect(fs.readFileSync(stateCache, 'utf8')).toBe(UPGRADE); expect(fs.existsSync(networkLog)).toBe(false); }); }); diff --git a/test/setup-gbrain-fixture.test.ts b/test/setup-gbrain-fixture.test.ts index 7053e095b..9b0ea860b 100644 --- a/test/setup-gbrain-fixture.test.ts +++ b/test/setup-gbrain-fixture.test.ts @@ -33,6 +33,11 @@ describe('setup-gbrain documented lock acquisition', () => { fs.mkdirSync(parent); fs.writeFileSync(lock, 'preserve lock file'); } + // The block resolves the state root through the installed gstack-paths. + const installBin = path.join(home, '.claude', 'skills', 'gstack', 'bin'); + fs.mkdirSync(installBin, { recursive: true }); + for (const b of ['gstack-paths', 'gstack-state-root.sh']) fs.copyFileSync(path.join(import.meta.dir, '..', 'bin', b), path.join(installBin, b)); + fs.chmodSync(path.join(installBin, 'gstack-paths'), 0o755); const source = fs.readFileSync(path.join(import.meta.dir, '..', 'setup-gbrain', 'SKILL.md.tmpl'), 'utf8'); const script = source.match(/\*\*Concurrent-run lock\.\*\*[\s\S]*?```bash\n([\s\S]*?)\n ```/)?.[1]; expect(script).toBeDefined(); diff --git a/test/skill-positional-literals.test.ts b/test/skill-positional-literals.test.ts index 84ef43d03..6a48dd89b 100644 --- a/test/skill-positional-literals.test.ts +++ b/test/skill-positional-literals.test.ts @@ -93,8 +93,9 @@ for (const host of ['claude', 'codex'] as const) for (const args of [[], tenArgu test(`${label}: upgrade snooze advances the same-version level`, () => { mkdirSync(join(root, '.gstack'), { recursive: true }); writeFileSync(join(root, '.gstack/update-snoozed'), '{new} 1 0\n'); - const block = skill(host, 'gstack-upgrade').match(/```bash\n(_SNOOZE_FILE=[\s\S]*?)```/)![1]; - expect(run(apply(block)).status).toBe(0); + const block = skill(host, 'gstack-upgrade').match(/```bash\n((?:eval [^\n]*gstack-paths[^\n]*\n)?_SNOOZE_FILE=[\s\S]*?)```/)![1]; + // The block resolves the state root first; this fixture has no install, so it supplies the root directly. + expect(run(apply(block), { GSTACK_STATE_ROOT: join(root, '.gstack') }).status).toBe(0); expect(readFileSync(join(root, '.gstack/update-snoozed'), 'utf8')).toMatch(/^\{new\} 2 \d+\n$/); }); test(`${label}: health error preserves its diagnostic argument`, () => { diff --git a/test/state-root-ratchet.allowlist.json b/test/state-root-ratchet.allowlist.json new file mode 100644 index 000000000..f828584c5 --- /dev/null +++ b/test/state-root-ratchet.allowlist.json @@ -0,0 +1,15 @@ +[ + { "path": "test/", "match": "*", "reason": "tests build fixtures with explicit state roots on purpose" }, + { "path": "browse/test/", "match": "*", "reason": "tests build fixtures with explicit state roots on purpose" }, + { "path": "docs/", "match": "*", "reason": "docs and design records quote paths as illustrative prose" }, + { "path": "CHANGELOG.md", "match": "*", "reason": "release history quotes old and new chains as prose" }, + { "path": "TODOS.md", "match": "*", "reason": "backlog prose, never executed" }, + { "path": "lib/state-root.ts", "match": "*", "reason": "the TS owner of the chain" }, + { "path": "bin/gstack-state-root.sh", "match": "*", "reason": "the bash owner of the chain" }, + { "path": "careful/bin/check-careful.sh", "match": "_GSTACK_HOME_DIR=\"${GSTACK_HOME:-$HOME/.gstack}\"", "reason": "partial-upgrade fallback, reached only when an older hook-extract.sh lacks gstack_hook_state_root (legacy order kept on purpose)" }, + { "path": "gstack-upgrade/SKILL.md.tmpl", "match": "elif [ -d \"$HOME/.gstack/repos/gstack/.git\" ]; then", "reason": "the relocated gstack checkout is an install location, not state" }, + { "path": "gstack-upgrade/SKILL.md.tmpl", "match": "INSTALL_DIR=\"$HOME/.gstack/repos/gstack\"", "reason": "the relocated gstack checkout is an install location, not state" }, + { "path": "scripts/resolvers/review.ts", "match": "*", "reason": "PENDING CROSS-OWNER (W5): delete this entry once the W1 handoff's review.ts / new-module edits are applied" }, + { "path": "scripts/resolvers/design.ts", "match": "*", "reason": "PENDING CROSS-OWNER (W5): delete this entry once the W1 handoff's design.ts edits are applied" }, + { "path": "scripts/test-free-shards.ts", "match": "*", "reason": "PENDING CROSS-OWNER (W2): delete this entry once the W1 handoff's test-free-shards.ts edit is applied" } +] diff --git a/test/state-root-ratchet.test.ts b/test/state-root-ratchet.test.ts new file mode 100644 index 000000000..e0f27815a --- /dev/null +++ b/test/state-root-ratchet.test.ts @@ -0,0 +1,155 @@ +/** + * Ratchet (a): one state-root rule. Outside the two owners (lib/state-root.ts, + * bin/gstack-state-root.sh) and the committed allowlist, no tracked file may + * hand-roll the chain, and every gstack-paths eval must fail stop. + */ +import { describe, test, expect } from 'bun:test'; +import { spawnSync } from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import { resolveStateRoot } from '../lib/state-root'; + +const ROOT = path.resolve(import.meta.dir, '..'); +const ALLOWLIST_PATH = 'test/state-root-ratchet.allowlist.json'; + +type AllowEntry = { path: string; match: string; reason?: string }; +type Violation = { file: string; line: number; text: string; rule: string }; + +const CHAIN = /\\?\$\{GSTACK_(?:HOME|STATE_ROOT|STATE_DIR):-/; +const HOMEDIR_GSTACK = /homedir\(\)\s*,\s*['"`]\.gstack['"`]/g; +const PROSE_ROOT = /(~|\\?\$HOME|\\?\$\{HOME\})\/\.gstack(?![\w-])/; +const PATHS_EVAL = /eval "\$\(.*gstack-paths"?\)"/; +const GUARD = ': "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"'; +const GUARD_TS = ': "\\${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"'; + +const isProseSource = (f: string) => f.endsWith('.tmpl') || f.startsWith('scripts/resolvers/'); +const isExecutable = (f: string, text: string) => + /^#!.*\b(bash|sh)\b/.test(text) || f.endsWith('.sh') || f === 'setup'; + +/** Lines inside ```bash / ```sh fences (template-literal fences \`\`\` included), comments skipped. */ +function bashBlockLines(text: string): Array<[number, string]> { + const out: Array<[number, string]> = []; + let inBash = false; + text.split('\n').forEach((line, i) => { + const fence = line.trim().match(/^(?:\\`|`){3}(\w*)/); + if (fence) { inBash = inBash ? false : /^(bash|sh)$/.test(fence[1]); return; } + if (inBash && !line.trim().startsWith('#')) out.push([i + 1, line]); + }); + return out; +} + +export function scan(files: Map): Violation[] { + const v: Violation[] = []; + for (const [file, text] of files) { + const lines = text.split('\n'); + const code = (line: string) => !/^\s*(#|\/\/|\*)/.test(line) || isProseSource(file); + lines.forEach((line, i) => { + if (code(line) && CHAIN.test(line)) v.push({ file, line: i + 1, text: line.trim(), rule: 'inline state-root chain' }); + }); + if (/\.(ts|js|mjs)$/.test(file) || !file.includes('.')) { + for (const m of text.matchAll(HOMEDIR_GSTACK)) { + const line = text.slice(0, m.index).split('\n').length; + v.push({ file, line, text: lines[line - 1].trim(), rule: 'homedir() joined with .gstack' }); + } + } + if (isProseSource(file)) { + for (const [n, line] of bashBlockLines(text)) { + if (PROSE_ROOT.test(line)) v.push({ file, line: n, text: line.trim(), rule: 'executable ~/.gstack in a bash block' }); + } + lines.forEach((line, i) => { + if (/export GSTACK_STATE_ROOT\b/.test(line)) v.push({ file, line: i + 1, text: line.trim(), rule: 'export GSTACK_STATE_ROOT in prose' }); + }); + } + if (isProseSource(file) || isExecutable(file, text)) { + lines.forEach((line, i) => { + if (code(line) && PATHS_EVAL.test(line) && !line.includes(GUARD) && !line.includes(GUARD_TS)) { + v.push({ file, line: i + 1, text: line.trim(), rule: 'unguarded gstack-paths eval' }); + } + }); + } + } + return v; +} + +function allowed(v: Violation, allow: AllowEntry[]): boolean { + return allow.some((a) => (a.path.endsWith('/') ? v.file.startsWith(a.path) : v.file === a.path) + && (a.match === '*' || v.text === a.match)); +} + +function report(violations: Violation[]): string { + if (violations.length === 0) return ''; + return [ + ...violations.map((x) => `${x.file}:${x.line} ${x.text} (${x.rule})`), + 'Rule: gstack state lives under one root resolved by one chain; a hand-rolled chain drifts (plugin installs and GSTACK_HOME users silently split their state), and an unguarded eval writes under / when the resolver is missing.', + 'Fix: in prose and executables use eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; ' + GUARD + + ' (hooks and bins may source bin/gstack-state-root.sh instead); in TS use resolveStateRoot() from lib/state-root.ts.', + `Allowlist: ${ALLOWLIST_PATH}, keyed on file path plus matched line text, each entry with a reason. Legitimate only for illustrative prose, an install location that is not state, or a named partial-upgrade fallback.`, + ].join('\n'); +} + +function trackedFiles(): Map { + const r = spawnSync('git', ['ls-files', '-z'], { cwd: ROOT, encoding: 'utf-8', timeout: 30_000 }); + const files = new Map(); + for (const f of r.stdout.split('\0').filter(Boolean)) { + if (/\.(png|jpg|jpeg|gif|webp|pdf|ico|woff2?|ttf|lock|onnx|bin|zip)$/i.test(f)) continue; + const full = path.join(ROOT, f); + try { + const stat = fs.lstatSync(full); + if (!stat.isFile() || stat.size > 2_000_000) continue; + files.set(f, fs.readFileSync(full, 'utf-8')); + } catch { /* deleted in the working tree */ } + } + return files; +} + +const allow: AllowEntry[] = JSON.parse(fs.readFileSync(path.join(ROOT, ALLOWLIST_PATH), 'utf-8')); + +describe('ratchet (a): one state-root rule', () => { + test('every allowlist entry carries a reason', () => { + const missing = allow.filter((a) => !a.reason || !a.reason.trim()); + expect(missing.map((a) => `${a.path} :: ${a.match}`)).toEqual([]); + }); + + test('no tracked file outside the owners and the allowlist hand-rolls the state root', () => { + expect(report(scan(trackedFiles()).filter((x) => !allowed(x, allow)))).toBe(''); + }); + + test('planted violations fail with file:line, the Fix: text and the allowlist path', () => { + const planted = new Map([ + ['bin/gstack-planted', '#!/usr/bin/env bash\n# a comment "${GSTACK_HOME:-$HOME/.gstack}" is prose\nX="${GSTACK_HOME:-$HOME/.gstack}"\neval "$("$(dirname "$0")/gstack-paths")"\n'], + ['lib/planted.ts', "import { homedir } from 'os';\nconst r = join(\n homedir(), '.gstack');\n"], + ['planted/SKILL.md.tmpl', 'Prose ~/.gstack is fine.\n```bash\n# comment ~/.gstack is fine\nmkdir -p ~/.gstack/analytics\nexport GSTACK_STATE_ROOT\neval "$(~/.claude/skills/gstack/bin/gstack-paths)"\n```\n'], + ]); + const found = scan(planted); + expect(found.map((x) => `${x.file}:${x.line}:${x.rule}`)).toEqual([ + 'bin/gstack-planted:3:inline state-root chain', + 'bin/gstack-planted:4:unguarded gstack-paths eval', + 'lib/planted.ts:3:homedir() joined with .gstack', + 'planted/SKILL.md.tmpl:4:executable ~/.gstack in a bash block', + 'planted/SKILL.md.tmpl:5:export GSTACK_STATE_ROOT in prose', + 'planted/SKILL.md.tmpl:6:unguarded gstack-paths eval', + ]); + const message = report(found); + expect(message).toContain('planted/SKILL.md.tmpl:4 mkdir -p ~/.gstack/analytics'); + expect(message).toContain('Fix: '); + expect(message).toContain('resolveStateRoot()'); + expect(message).toContain(ALLOWLIST_PATH); + }); + + test('allowlist entries are keyed on text, not line numbers: inserting a line above still passes', () => { + const file = 'careful/bin/check-careful.sh'; + const text = fs.readFileSync(path.join(ROOT, file), 'utf-8'); + const shifted = new Map([[file, `# inserted line\n${text}`]]); + const found = scan(shifted); + expect(found.length).toBeGreaterThan(0); + expect(found.filter((x) => !allowed(x, allow))).toEqual([]); + }); + + test('guarded evals and the owners pass, and the TS owner resolves the documented default', () => { + const ok = new Map([ + ['x/SKILL.md.tmpl', '```bash\neval "$(~/.claude/skills/gstack/bin/gstack-paths)"; ' + GUARD + '\nls "$GSTACK_STATE_ROOT"/projects\n```\n'], + ]); + expect(scan(ok)).toEqual([]); + expect(resolveStateRoot({ HOME: '/h' }, 'linux')).toBe('/h/.gstack'); + }); +}); diff --git a/test/state-root-resolver-failure.test.ts b/test/state-root-resolver-failure.test.ts new file mode 100644 index 000000000..f7ea042dd --- /dev/null +++ b/test/state-root-resolver-failure.test.ts @@ -0,0 +1,73 @@ +/** + * Resolver failure is fail-stop (W1). With bin/gstack-state-root.sh removed + * from an install, a real bash block copied from a generated SKILL.md and a + * migrated executable both stop with the reinstall message, and nothing is + * written outside the test's temp root. + */ +import { describe, test, expect } from 'bun:test'; +import { spawnSync } from 'child_process'; +import * as fs from 'fs'; +import * as os from 'os'; +import * as path from 'path'; +import { resolveStateRoot } from '../lib/state-root'; + +const ROOT = path.resolve(import.meta.dir, '..'); + +/** The first ```bash block of a generated SKILL.md that evals gstack-paths. */ +function contextRecoveryBlock(): string { + const md = fs.readFileSync(path.join(ROOT, 'qa', 'SKILL.md'), 'utf-8'); + const start = md.indexOf('## Context Recovery'); + const open = md.indexOf('```bash\n', start) + '```bash\n'.length; + return md.slice(open, md.indexOf('\n```', open)); +} + +/** Every path under dir, except a host git wrapper's own log dir (not gstack's). */ +function listTree(dir: string): string[] { + const out: string[] = []; + for (const e of fs.readdirSync(dir, { withFileTypes: true })) { + if (e.name === '.capy') continue; + const p = path.join(dir, e.name); + out.push(p); + if (e.isDirectory() && !e.isSymbolicLink()) out.push(...listTree(p)); + } + return out; +} + +describe('state-root resolver failure', () => { + test('a generated SKILL.md block and a migrated bin stop with the reinstall message when the twin is missing', () => { + const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-resolver-fail-')); + try { + const home = path.join(tmp, 'home'); + const bin = path.join(home, '.claude', 'skills', 'gstack', 'bin'); + const cwd = path.join(tmp, 'cwd'); + fs.mkdirSync(bin, { recursive: true }); + fs.mkdirSync(cwd); + for (const b of ['gstack-paths', 'gstack-slug', 'gstack-config']) { + fs.copyFileSync(path.join(ROOT, 'bin', b), path.join(bin, b)); + fs.chmodSync(path.join(bin, b), 0o755); + } + const env = { + PATH: process.env.PATH ?? '', HOME: home, TMPDIR: path.join(tmp, 'tmp'), + GSTACK_HOME: '', GSTACK_STATE_ROOT: '', GSTACK_STATE_DIR: '', CLAUDE_PLUGIN_DATA: '', + }; + expect(resolveStateRoot(env)).toBe(`${home}/.gstack`); + const before = listTree(tmp).sort(); + + const block = contextRecoveryBlock(); + expect(block).toContain('gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"'); + const skill = spawnSync('bash', ['-c', block + '\necho REACHED_END'], { cwd, env, encoding: 'utf-8', timeout: 30_000 }); + expect(skill.status).not.toBe(0); + expect(skill.stderr).toContain('gstack-paths failed; reinstall with ./setup or /gstack-upgrade'); + expect(skill.stdout).not.toContain('REACHED_END'); + + const config = spawnSync('bash', [path.join(bin, 'gstack-config'), 'set', 'telemetry', 'off'], { cwd, env, encoding: 'utf-8', timeout: 30_000 }); + expect(config.status).toBe(1); + expect(config.stderr).toContain('cannot resolve the gstack state root'); + expect(config.stderr).toContain('reinstall with ./setup or /gstack-upgrade'); + + expect(listTree(tmp).sort()).toEqual(before); + } finally { + fs.rmSync(tmp, { recursive: true, force: true }); + } + }); +}); diff --git a/test/state-root-touchfile-superset.test.ts b/test/state-root-touchfile-superset.test.ts new file mode 100644 index 000000000..890657796 --- /dev/null +++ b/test/state-root-touchfile-superset.test.ts @@ -0,0 +1,35 @@ +/** + * Touchfile coverage for W1's new modules: touching lib/state-root.ts, + * bin/gstack-state-root.sh or hosts/claude/hooks/hook-log.ts must select at + * least the paid evals their source files selected on the base commit + * (test/fixtures/state-root-selection-golden.json, recorded before the move). + */ +import { describe, test, expect } from 'bun:test'; +import * as fs from 'fs'; +import * as path from 'path'; +import { E2E_TOUCHFILES, LLM_JUDGE_TOUCHFILES, GLOBAL_TOUCHFILES, selectTests } from './helpers/touchfiles'; +import { resolveStateRoot } from '../lib/state-root'; + +const ROOT = path.resolve(import.meta.dir, '..'); +const GOLDEN = 'test/fixtures/state-root-selection-golden.json'; +const golden: { modules: Record } = + JSON.parse(fs.readFileSync(path.join(ROOT, GOLDEN), 'utf-8')); + +describe('W1 touchfile superset', () => { + test('the golden covers every new state-root module', () => { + expect(Object.keys(golden.modules).sort()).toEqual(['bin/gstack-state-root.sh', 'hosts/claude/hooks/hook-log.ts', 'lib/state-root.ts']); + expect(typeof resolveStateRoot).toBe('function'); + }); + + for (const [mod, rec] of Object.entries(golden.modules)) { + test(`touching ${mod} selects a superset of ${rec.sources.join(', ')}`, () => { + const e2e = new Set(selectTests([mod], E2E_TOUCHFILES, GLOBAL_TOUCHFILES).selected); + const llm = new Set(selectTests([mod], LLM_JUDGE_TOUCHFILES, GLOBAL_TOUCHFILES).selected); + const missing = [...rec.e2e.filter((t) => !e2e.has(t)), ...rec.llmJudge.filter((t) => !llm.has(t))]; + expect(missing, [ + `${mod} selects fewer paid evals than its source files did: ${missing.join(', ')}`, + `Fix: add ${mod} to those entries in test/helpers/touchfiles-data.ts (golden: ${GOLDEN}).`, + ].join('\n')).toEqual([]); + }); + } +}); diff --git a/unfreeze/SKILL.md b/unfreeze/SKILL.md index e7e59380f..7fa6e8e95 100644 --- a/unfreeze/SKILL.md +++ b/unfreeze/SKILL.md @@ -25,8 +25,9 @@ Use when asked to "unfreeze", "unlock edits", "remove freeze", or Remove the edit restriction set by `/freeze`, allowing edits to all directories. ```bash -mkdir -p ~/.gstack/analytics -echo '{"skill":"unfreeze","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 +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/analytics +echo '{"skill":"unfreeze","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_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true ``` ## Clear the boundary diff --git a/unfreeze/SKILL.md.tmpl b/unfreeze/SKILL.md.tmpl index 58f4c3a4b..04a4022da 100644 --- a/unfreeze/SKILL.md.tmpl +++ b/unfreeze/SKILL.md.tmpl @@ -21,8 +21,9 @@ sensitive: true Remove the edit restriction set by `/freeze`, allowing edits to all directories. ```bash -mkdir -p ~/.gstack/analytics -echo '{"skill":"unfreeze","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 +eval "$(~/.claude/skills/gstack/bin/gstack-paths)"; : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}" +mkdir -p "$GSTACK_STATE_ROOT"/analytics +echo '{"skill":"unfreeze","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_STATE_ROOT"/analytics/skill-usage.jsonl 2>/dev/null || true ``` ## Clear the boundary