From 3a72986c4943f8e211612806ea0b22fb8d615e8e Mon Sep 17 00:00:00 2001 From: John Banner Date: Mon, 23 Mar 2026 14:13:52 -0700 Subject: [PATCH 1/3] feat: add /solve (end-to-end issue resolution) and /memory (persistent session memory) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /solve TICKET-ID: chains fetch → investigate → code → review → test → ship from a single ticket ID. Supports Linear, Jira, and GitHub Issues. /memory: indexes patterns, fixes, and lessons from past sessions so future sessions compound. Integrates with /investigate (search before debugging), /review (check for anti-patterns), and /ship (auto-index after shipping). Storage: ~/.gstack/memory/ as markdown files (patterns.md, fixes.md, lessons.md, anti-patterns.md). Includes backfill from git history and quality filters to prevent indexing bad fixes. --- memory/SKILL.md | 277 ++++++++++++++++++++++++++++++++++++++++++++++++ solve/SKILL.md | 239 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 516 insertions(+) create mode 100644 memory/SKILL.md create mode 100644 solve/SKILL.md diff --git a/memory/SKILL.md b/memory/SKILL.md new file mode 100644 index 000000000..3c549e588 --- /dev/null +++ b/memory/SKILL.md @@ -0,0 +1,277 @@ +--- +name: memory +version: 1.0.0 +description: | + Persistent session memory across Claude Code sessions. Indexes patterns, fixes, + and lessons from past work so future sessions compound instead of starting from + zero. Automatically hooks into /investigate, /review, /ship, and /solve. + Use when asked to "remember this", "what did we fix before", "search memory", + or "show patterns". Also triggered automatically by other skills. +allowed-tools: + - Bash + - Read + - Write + - Edit + - Grep + - Glob + - AskUserQuestion +--- + +## Preamble (run first) + +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null || .claude/skills/gstack/bin/gstack-slug 2>/dev/null || echo 'SLUG=unknown')" +MEMORY_DIR="$HOME/.gstack/memory" +mkdir -p "$MEMORY_DIR" + +# Count indexed knowledge +PATTERN_COUNT=$(grep -c "^## Pattern:" "$MEMORY_DIR/patterns.md" 2>/dev/null || echo "0") +FIX_COUNT=$(grep -c "^## Fix:" "$MEMORY_DIR/fixes.md" 2>/dev/null || echo "0") +LESSON_COUNT=$(grep -c "^## Lesson:" "$MEMORY_DIR/lessons.md" 2>/dev/null || echo "0") + +echo "MEMORY_DIR: $MEMORY_DIR" +echo "PATTERNS: $PATTERN_COUNT" +echo "FIXES: $FIX_COUNT" +echo "LESSONS: $LESSON_COUNT" +``` + +# /memory — Persistent Session Memory + +You are a **knowledge curator** who makes every session smarter than the last. +Every bug found, fix shipped, and lesson learned gets indexed so future sessions +can retrieve it instantly. + +## Arguments + +- `/memory` — show memory stats and recent entries +- `/memory search ` — search across all memory for a pattern, fix, or lesson +- `/memory add pattern` — manually add a pattern (interactive) +- `/memory add lesson` — manually add a lesson learned +- `/memory import` — backfill from git history (one-time) +- `/memory prune` — remove outdated or low-quality entries + +## Storage Format + +Memory lives in `~/.gstack/memory/` as markdown files: + +``` +~/.gstack/memory/ + patterns.md — Bug patterns (what goes wrong and why) + fixes.md — Fixes that worked (what we did and how) + lessons.md — Lessons learned (decisions, preferences, style) + anti-patterns.md — Fixes that failed or were reverted +``` + +### patterns.md format + +```markdown +## Pattern: [Short descriptive name] +**First seen:** [TICKET or PR] on [DATE] +**Seen again:** [TICKET, TICKET, ...] (updated each time) +**Domain:** [payment | auth | notifications | voice | ui | config | ...] +**Bug:** [One-line: what goes wrong] +**Root cause:** [One-line: why it happens] +**Detection:** [How to spot this pattern in code — grep pattern or code smell] +**Files:** [Common file patterns where this occurs] +**Tags:** [comma-separated searchable tags] +``` + +### fixes.md format + +```markdown +## Fix: [TICKET] — [Short description] +**Date:** [DATE] +**PR:** #[NUMBER] +**Domain:** [domain] +**What broke:** [one line] +**What fixed it:** [one line] +**Diff summary:** [key changes — not the full diff, just the insight] +**Quality:** [merged_clean | had_review_feedback | was_reverted | caused_followup] +**Files:** [files modified] +**Tags:** [comma-separated] +``` + +### lessons.md format + +```markdown +## Lesson: [Short name] +**Source:** [Who said it — user feedback, code review, prod incident] +**Date:** [DATE] +**Context:** [What happened] +**Rule:** [The lesson in one sentence] +**Tags:** [comma-separated] +``` + +### anti-patterns.md format + +```markdown +## Anti-pattern: [Short name] +**Source:** [TICKET or PR that was reverted/failed] +**Date:** [DATE] +**What was tried:** [The fix that didn't work] +**Why it failed:** [Root cause of failure] +**Better approach:** [What should have been done instead] +**Tags:** [comma-separated] +``` + +## How Memory Is Used By Other Skills + +### During /investigate +At the START of investigation, before reading any code: +```bash +if [ -f ~/.gstack/memory/patterns.md ]; then + echo "🧠 Searching memory for related patterns..." + grep -i -B 1 -A 8 "KEYWORD" ~/.gstack/memory/patterns.md 2>/dev/null || true +fi +``` + +If a match is found, cite it and apply the pattern: +> 💡 **Memory match:** Pattern "[NAME]" (seen in [TICKETS]). +> Root cause was [X]. Checking if same pattern applies here... + +### During /review +Check if the diff introduces a known anti-pattern: +```bash +if [ -f ~/.gstack/memory/anti-patterns.md ]; then + # Extract file paths from diff, search anti-patterns for matches + grep -i "CHANGED_FILE_PATTERN" ~/.gstack/memory/anti-patterns.md 2>/dev/null || true +fi +``` + +### During /ship (Phase 6 — post-ship) +Append to fixes.md: +```bash +cat >> ~/.gstack/memory/fixes.md << EOF + +## Fix: [IDENTIFIER] — [TITLE] +**Date:** $(date +%Y-%m-%d) +**PR:** #[NUMBER] +**Domain:** [domain] +**What broke:** [summary] +**What fixed it:** [summary] +**Diff summary:** [key insight] +**Quality:** merged_clean +**Files:** [files] +**Tags:** [tags] +EOF +``` + +### During /solve (Phase 6 — post-solve) +Append both a fix AND check if a new pattern emerged: +- If the same root cause was seen before → update the "Seen again" field +- If it's a new root cause → append a new pattern entry + +## Commands + +### `/memory search ` + +Search all memory files for the query. Show matches with context: + +```bash +MEMORY_DIR="$HOME/.gstack/memory" +echo "🔍 Searching memory for: $QUERY" +echo "" +for file in patterns.md fixes.md lessons.md anti-patterns.md; do + if [ -f "$MEMORY_DIR/$file" ]; then + MATCHES=$(grep -i -B 1 -A 8 "$QUERY" "$MEMORY_DIR/$file" 2>/dev/null) + if [ -n "$MATCHES" ]; then + echo "📁 $file:" + echo "$MATCHES" + echo "---" + fi + fi +done +``` + +Present results grouped by file, with the most relevant match highlighted. + +### `/memory add pattern` + +Interactive — ask for: +1. Short name +2. Domain +3. Bug description (one line) +4. Root cause (one line) +5. Detection method (grep pattern or code smell) +6. Tags + +Then append to patterns.md. + +### `/memory import` + +One-time backfill from git history: + +```bash +# Get last 6 months of merged PRs with their commit messages +git log --oneline --since="6 months ago" --merges --format="%h %s" | + head -100 +``` + +For each PR: +1. Extract the ticket ID from the title (e.g., `[RES-4662]`) +2. Read the commit message for context +3. Classify: is this a bug fix, feature, refactor, or chore? +4. For bug fixes: extract the pattern and add to patterns.md +5. Skip: docs changes, config changes, dependency updates + +**Quality filter during import:** +- Check if the commit was reverted within 7 days → anti-pattern, not pattern +- Check if there was a follow-up fix on the same files within 48h → incomplete fix +- Only index commits that survived without follow-up issues + +### `/memory prune` + +Review entries and remove: +- Patterns that haven't been "seen again" in 6+ months +- Fixes for code that no longer exists (check if files still exist) +- Duplicate patterns (merge them) + +### `/memory` (no args) + +Show memory dashboard: + +``` +🧠 gstack Memory — [PROJECT] +━━━━━━━━━━━━━━━━━━━━ +Patterns: 12 indexed (3 seen 2+ times) +Fixes: 47 indexed (oldest: 2024-09-15) +Lessons: 8 indexed +Anti-pats: 3 indexed + +Top domains: notifications (8), payment (6), voice (4) +Last updated: 2024-03-23 + +Recent patterns: + • "Operator Precedence in PHP Guards" (seen 3x) + • "Missing Terminal Status Guard" (seen 2x) + • "Null Check on Optional Relation" (seen 2x) +``` + +## Integration with conductor.json + +Projects can configure memory behavior in `conductor.json`: + +```json +{ + "memory": { + "auto_index": true, + "domains": ["payment", "auth", "voice", "notifications", "ui"], + "exclude_paths": ["tests/", "docs/", "config/"], + "quality_threshold": "merged_clean" + } +} +``` + +## Philosophy + +**Search Your Own History First.** Before investigating any bug, check if you've +seen this pattern before. The cost of checking is near-zero. The cost of +re-investigating from scratch is 10 minutes you'll never get back. + +This extends gstack's "Search Before Building" principle from external knowledge +(Layer 1-3) to internal knowledge (Layer 0 — your own past work). Layer 0 is +the highest-confidence source because it's YOUR codebase, YOUR bugs, YOUR fixes. + +Memory compounds. After 100 sessions, you've indexed 100 patterns. Session 101 +starts with 100 patterns to search against. That's the difference between a tool +and a teammate. diff --git a/solve/SKILL.md b/solve/SKILL.md new file mode 100644 index 000000000..055e5ac05 --- /dev/null +++ b/solve/SKILL.md @@ -0,0 +1,239 @@ +--- +name: solve +version: 1.0.0 +description: | + End-to-end autonomous issue resolution. Takes a ticket ID from Linear, Jira, + or GitHub Issues and chains: fetch → investigate → code → test → review → ship. + Use when asked to "solve this ticket", "fix PROJ-123", "resolve this issue", + or "take this ticket and ship it". + Proactively suggest when the user pastes a ticket URL or mentions a ticket ID. +allowed-tools: + - Bash + - Read + - Write + - Edit + - Grep + - Glob + - Agent + - AskUserQuestion + - WebSearch +--- + +## Preamble (run first) + +```bash +eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null || .claude/skills/gstack/bin/gstack-slug 2>/dev/null || echo 'SLUG=unknown')" +_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") +echo "BRANCH: $_BRANCH" +echo "SLUG: $SLUG" + +# Check for memory from past solves +MEMORY_DIR="$HOME/.gstack/memory" +if [ -d "$MEMORY_DIR" ] && [ -f "$MEMORY_DIR/patterns.md" ]; then + PATTERN_COUNT=$(grep -c "^## Pattern:" "$MEMORY_DIR/patterns.md" 2>/dev/null || echo "0") + echo "MEMORY: $PATTERN_COUNT patterns indexed" +else + echo "MEMORY: none (run /solve to start building)" +fi +``` + +# /solve — End-to-End Issue Resolution + +You are a **Senior Engineer** who takes a ticket and ships a fix. No hand-holding, +no partial work, no "I investigated and here's what I found." You find the bug, +write the fix, test it, review your own code, and ship the PR. + +## Arguments + +- `/solve PROJ-123` — resolve a ticket by identifier (Linear, Jira, GitHub) +- `/solve https://linear.app/team/issue/PROJ-123` — resolve from URL +- `/solve` — if on a branch named after a ticket (e.g., `fix/PROJ-123-foo`), auto-detect + +## Phase 0: Fetch Ticket + +Determine the ticket system and fetch full context: + +**Linear** (identifiers like `RES-123`, `ENG-456`, or linear.app URLs): +```bash +# Extract identifier from URL or argument +IDENTIFIER="$1" +if echo "$IDENTIFIER" | grep -q "linear.app"; then + IDENTIFIER=$(echo "$IDENTIFIER" | grep -oE '[A-Z]+-[0-9]+' | tail -1) +fi + +# Fetch via Linear GraphQL API (requires LINEAR_API_KEY in env) +curl -s -X POST https://api.linear.app/graphql \ + -H "Authorization: $LINEAR_API_KEY" \ + -H "Content-Type: application/json" \ + -d "$(jq -n --arg id "$IDENTIFIER" '{ + query: "query($filter: IssueFilter) { issues(filter: $filter, first: 1) { nodes { id identifier title description state { name } assignee { name } comments { nodes { body createdAt user { name } } } labels { nodes { name } } } } }", + variables: { filter: { identifier: { eq: $id } } } + }')" +``` + +**GitHub Issues** (identifiers like `#123`, or github.com URLs): +```bash +gh issue view "$ISSUE_NUMBER" --json title,body,comments,labels,assignees +``` + +**Jira** (identifiers like `JIRA-123`, or atlassian.net URLs): +```bash +curl -s "$JIRA_BASE_URL/rest/api/3/issue/$IDENTIFIER" \ + -H "Authorization: Basic $JIRA_AUTH" +``` + +If the ticket system can't be detected, use AskUserQuestion to ask. + +Present the ticket to yourself: +``` +Ticket: [IDENTIFIER] — [TITLE] +Status: [STATE] +Description: [DESCRIPTION] +Comments: [COMMENT_COUNT] comments +Labels: [LABELS] +``` + +## Phase 1: Investigate (chain /investigate) + +Read the skill file and follow its protocol: +```bash +cat ~/.claude/skills/gstack/investigate/SKILL.md +``` + +Run the full investigation: read the codebase, trace the chain, identify root cause. + +**Memory boost:** Before investigating, check if a similar pattern exists: +```bash +if [ -f ~/.gstack/memory/patterns.md ]; then + # Search for related patterns by file, domain, or keyword + grep -i -A 10 "KEYWORD_FROM_TICKET" ~/.gstack/memory/patterns.md || true +fi +``` + +If a matching pattern is found, cite it: +> 💡 **Similar pattern found:** [Pattern name] from [TICKET]. Previous fix: [summary]. +> Applying same approach with adaptations for this case. + +**Gate:** After investigation, assess confidence (0-100). +- If confidence >= 80%: proceed to code. +- If confidence < 80%: use AskUserQuestion to present findings and ask whether to proceed. + Show: root cause, proposed fix, risk assessment, and the confidence score. + +## Phase 2: Code the Fix + +1. Create a branch: `fix/[IDENTIFIER]-short-description` +2. Write the fix based on the investigation +3. Write tests (contract tests FIRST, then unit, then feature) +4. Commit with message: `[IDENTIFIER] Fix: one-line summary` + +**Rules:** +- Read the consumer before writing the producer +- Follow existing code patterns in the file you're modifying +- Never skip tests — Boil the Lake + +## Phase 3: Self-Review (chain /review) + +Read the review skill and run it against your own diff: +```bash +cat ~/.claude/skills/gstack/review/SKILL.md +``` + +Review your own code. If the review finds issues, fix them before proceeding. +This is NOT optional — you review your own code like a different person wrote it. + +## Phase 4: Test + +Run the project's test suite: +```bash +# Detect test framework and run +if [ -f "artisan" ]; then + php artisan config:clear && php artisan test --parallel +elif [ -f "package.json" ]; then + npm test +elif [ -f "Cargo.toml" ]; then + cargo test +elif [ -f "go.mod" ]; then + go test ./... +elif [ -f "pytest.ini" ] || [ -f "setup.py" ]; then + pytest +fi +``` + +If tests fail, fix and re-run. Do not proceed with failing tests. + +## Phase 5: Ship (chain /ship) + +Read the ship skill and follow its protocol: +```bash +cat ~/.claude/skills/gstack/ship/SKILL.md +``` + +Create the PR, linking back to the original ticket. + +## Phase 6: Update Memory + +After shipping, append to the memory: + +```bash +mkdir -p ~/.gstack/memory + +# Append pattern +cat >> ~/.gstack/memory/patterns.md << 'PATTERN_EOF' + +## Pattern: [SHORT_NAME] +**Source:** [IDENTIFIER], PR #[NUMBER] +**Domain:** [domain] +**Bug:** [one-line description of what was wrong] +**Fix:** [one-line description of what fixed it] +**Files:** [list of files modified] +**Tags:** [comma-separated tags for future search] +PATTERN_EOF +``` + +## Non-interactive philosophy + +Like `/ship`, `/solve` makes decisions autonomously. It only asks the user when: +1. Confidence is below 80% after investigation (Phase 1 gate) +2. Tests fail after 2 fix attempts +3. The ticket system can't be detected + +Everything else is autonomous: fetch, investigate, code, review, test, ship. + +## Ticket update + +After shipping, update the ticket: +- **Linear:** Post a comment with the PR link and move to "In Review" +- **GitHub:** Post a comment linking the PR +- **Jira:** Transition to "In Review" and post PR link + +## Example + +``` +> /solve RES-4662 + +Fetching RES-4662 from Linear... +✅ Ticket: RES-4662 — Notifications sent to recovered sequences + Status: Todo | Labels: Bug + Description: "Debtor notifications are being sent to sequences with + status 'recovered', which should never receive notifications..." + +🔍 Investigating... +💡 Similar pattern found: "Terminal Status Guard" from RES-4508. + Previous fix: added status check at top of notification job handle(). + +Root cause: NotifyPhoneChangeAbcJustice.php:45 + `!$this->sequence->status === 'recovered'` — PHP operator precedence. + `!` binds before `===`, so this evaluates as `false === 'recovered'` + which is always false. The guard never blocks. + +Confidence: 95% — proceeding to code. + +📝 Fix: wrap in parentheses `!($this->sequence->status === 'recovered')` + Also found same pattern in 3 other notification jobs. + Branch: fix/RES-4662-notification-terminal-guard + +✅ Tests pass (47 assertions) +✅ Self-review: PASS +✅ PR #4389 created, linked to RES-4662 +✅ Memory updated: "Operator Precedence in PHP Guards" +``` From e57560dd302187568797b6692fc47f0cb5f5920c Mon Sep 17 00:00:00 2001 From: John Banner Date: Mon, 23 Mar 2026 14:19:13 -0700 Subject: [PATCH 2/3] fix: use standard gstack preamble for /solve and /memory skills Both skills now include the full preamble (update check, session tracking, telemetry, repo mode, lake intro, proactive config) matching the pattern used by /ship, /investigate, /review, and other existing skills. --- memory/SKILL.md | 39 ++++++++++++++++++++++++++++++++++++--- solve/SKILL.md | 34 ++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 5 deletions(-) diff --git a/memory/SKILL.md b/memory/SKILL.md index 3c549e588..94ceb0b1f 100644 --- a/memory/SKILL.md +++ b/memory/SKILL.md @@ -20,11 +20,34 @@ allowed-tools: ## Preamble (run first) ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null || .claude/skills/gstack/bin/gstack-slug 2>/dev/null || echo 'SLUG=unknown')" +_UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true) +[ -n "$_UPD" ] && echo "$_UPD" || true +mkdir -p ~/.gstack/sessions +touch ~/.gstack/sessions/"$PPID" +_SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') +find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +_PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") +_BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") +echo "BRANCH: $_BRANCH" +echo "PROACTIVE: $_PROACTIVE" +eval "$(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null || true)" +REPO_MODE=${REPO_MODE:-unknown} +echo "REPO_MODE: $REPO_MODE" +_LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") +echo "LAKE_INTRO: $_LAKE_SEEN" +_TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) +_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") +_TEL_START=$(date +%s) +_SESSION_ID="$$-$(date +%s)" +echo "TELEMETRY: ${_TEL:-off}" +echo "TEL_PROMPTED: $_TEL_PROMPTED" +mkdir -p ~/.gstack/analytics +echo '{"skill":"memory","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 +for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do [ -f "$_PF" ] && ~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true; break; done + +# Memory-specific: count indexed knowledge MEMORY_DIR="$HOME/.gstack/memory" mkdir -p "$MEMORY_DIR" - -# Count indexed knowledge PATTERN_COUNT=$(grep -c "^## Pattern:" "$MEMORY_DIR/patterns.md" 2>/dev/null || echo "0") FIX_COUNT=$(grep -c "^## Fix:" "$MEMORY_DIR/fixes.md" 2>/dev/null || echo "0") LESSON_COUNT=$(grep -c "^## Lesson:" "$MEMORY_DIR/lessons.md" 2>/dev/null || echo "0") @@ -35,6 +58,16 @@ echo "FIXES: $FIX_COUNT" echo "LESSONS: $LESSON_COUNT" ``` +If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills — only invoke +them when the user explicitly asks. The user opted out of proactive suggestions. + +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow". + +If `LAKE_INTRO` is `no`: Before continuing, introduce the Completeness Principle. +Tell the user: "gstack follows the **Boil the Lake** principle — always do the complete +thing when AI makes the marginal cost near-zero. Read more: https://garryslist.org/posts/boil-the-ocean" +Then run `touch ~/.gstack/.completeness-intro-seen` to mark as seen. + # /memory — Persistent Session Memory You are a **knowledge curator** who makes every session smarter than the last. diff --git a/solve/SKILL.md b/solve/SKILL.md index 055e5ac05..2621c55ef 100644 --- a/solve/SKILL.md +++ b/solve/SKILL.md @@ -22,10 +22,30 @@ allowed-tools: ## Preamble (run first) ```bash -eval "$(~/.claude/skills/gstack/bin/gstack-slug 2>/dev/null || .claude/skills/gstack/bin/gstack-slug 2>/dev/null || echo 'SLUG=unknown')" +_UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/skills/gstack/bin/gstack-update-check 2>/dev/null || true) +[ -n "$_UPD" ] && echo "$_UPD" || true +mkdir -p ~/.gstack/sessions +touch ~/.gstack/sessions/"$PPID" +_SESSIONS=$(find ~/.gstack/sessions -mmin -120 -type f 2>/dev/null | wc -l | tr -d ' ') +find ~/.gstack/sessions -mmin +120 -type f -delete 2>/dev/null || true +_PROACTIVE=$(~/.claude/skills/gstack/bin/gstack-config get proactive 2>/dev/null || echo "true") _BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown") echo "BRANCH: $_BRANCH" -echo "SLUG: $SLUG" +echo "PROACTIVE: $_PROACTIVE" +eval "$(~/.claude/skills/gstack/bin/gstack-repo-mode 2>/dev/null || true)" +REPO_MODE=${REPO_MODE:-unknown} +echo "REPO_MODE: $REPO_MODE" +_LAKE_SEEN=$([ -f ~/.gstack/.completeness-intro-seen ] && echo "yes" || echo "no") +echo "LAKE_INTRO: $_LAKE_SEEN" +_TEL=$(~/.claude/skills/gstack/bin/gstack-config get telemetry 2>/dev/null || true) +_TEL_PROMPTED=$([ -f ~/.gstack/.telemetry-prompted ] && echo "yes" || echo "no") +_TEL_START=$(date +%s) +_SESSION_ID="$$-$(date +%s)" +echo "TELEMETRY: ${_TEL:-off}" +echo "TEL_PROMPTED: $_TEL_PROMPTED" +mkdir -p ~/.gstack/analytics +echo '{"skill":"solve","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 +for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do [ -f "$_PF" ] && ~/.claude/skills/gstack/bin/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true; break; done # Check for memory from past solves MEMORY_DIR="$HOME/.gstack/memory" @@ -37,6 +57,16 @@ else fi ``` +If `PROACTIVE` is `"false"`, do not proactively suggest gstack skills — only invoke +them when the user explicitly asks. The user opted out of proactive suggestions. + +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow". + +If `LAKE_INTRO` is `no`: Before continuing, introduce the Completeness Principle. +Tell the user: "gstack follows the **Boil the Lake** principle — always do the complete +thing when AI makes the marginal cost near-zero. Read more: https://garryslist.org/posts/boil-the-ocean" +Then run `touch ~/.gstack/.completeness-intro-seen` to mark as seen. + # /solve — End-to-End Issue Resolution You are a **Senior Engineer** who takes a ticket and ships a fix. No hand-holding, From 2f531a21edb4a70dd859ae4164b10b658ca7d021 Mon Sep 17 00:00:00 2001 From: John Banner Date: Mon, 23 Mar 2026 14:21:16 -0700 Subject: [PATCH 3/3] feat: add SKILL.md.tmpl templates for /solve and /memory Follow gstack convention: .tmpl files use {{PREAMBLE}} placeholder, SKILL.md files are auto-generated with the standard preamble expanded. Both skills include skill-specific preamble extensions (memory count, pattern count) after the standard block. --- memory/SKILL.md | 2 + memory/SKILL.md.tmpl | 274 +++++++++++++++++++++++++++++++++++++++++++ solve/SKILL.md | 2 + solve/SKILL.md.tmpl | 233 ++++++++++++++++++++++++++++++++++++ 4 files changed, 511 insertions(+) create mode 100644 memory/SKILL.md.tmpl create mode 100644 solve/SKILL.md.tmpl diff --git a/memory/SKILL.md b/memory/SKILL.md index 94ceb0b1f..b000b0a9c 100644 --- a/memory/SKILL.md +++ b/memory/SKILL.md @@ -16,6 +16,8 @@ allowed-tools: - Glob - AskUserQuestion --- + + ## Preamble (run first) diff --git a/memory/SKILL.md.tmpl b/memory/SKILL.md.tmpl new file mode 100644 index 000000000..b5f8d9fcf --- /dev/null +++ b/memory/SKILL.md.tmpl @@ -0,0 +1,274 @@ +--- +name: memory +version: 1.0.0 +description: | + Persistent session memory across Claude Code sessions. Indexes patterns, fixes, + and lessons from past work so future sessions compound instead of starting from + zero. Automatically hooks into /investigate, /review, /ship, and /solve. + Use when asked to "remember this", "what did we fix before", "search memory", + or "show patterns". Also triggered automatically by other skills. +allowed-tools: + - Bash + - Read + - Write + - Edit + - Grep + - Glob + - AskUserQuestion +--- + +{{PREAMBLE}} + +```bash +# Memory-specific: count indexed knowledge +MEMORY_DIR="$HOME/.gstack/memory" +mkdir -p "$MEMORY_DIR" +PATTERN_COUNT=$(grep -c "^## Pattern:" "$MEMORY_DIR/patterns.md" 2>/dev/null || echo "0") +FIX_COUNT=$(grep -c "^## Fix:" "$MEMORY_DIR/fixes.md" 2>/dev/null || echo "0") +LESSON_COUNT=$(grep -c "^## Lesson:" "$MEMORY_DIR/lessons.md" 2>/dev/null || echo "0") + +echo "MEMORY_DIR: $MEMORY_DIR" +echo "PATTERNS: $PATTERN_COUNT" +echo "FIXES: $FIX_COUNT" +echo "LESSONS: $LESSON_COUNT" +``` +# /memory — Persistent Session Memory + +You are a **knowledge curator** who makes every session smarter than the last. +Every bug found, fix shipped, and lesson learned gets indexed so future sessions +can retrieve it instantly. + +## Arguments + +- `/memory` — show memory stats and recent entries +- `/memory search ` — search across all memory for a pattern, fix, or lesson +- `/memory add pattern` — manually add a pattern (interactive) +- `/memory add lesson` — manually add a lesson learned +- `/memory import` — backfill from git history (one-time) +- `/memory prune` — remove outdated or low-quality entries + +## Storage Format + +Memory lives in `~/.gstack/memory/` as markdown files: + +``` +~/.gstack/memory/ + patterns.md — Bug patterns (what goes wrong and why) + fixes.md — Fixes that worked (what we did and how) + lessons.md — Lessons learned (decisions, preferences, style) + anti-patterns.md — Fixes that failed or were reverted +``` + +### patterns.md format + +```markdown +## Pattern: [Short descriptive name] +**First seen:** [TICKET or PR] on [DATE] +**Seen again:** [TICKET, TICKET, ...] (updated each time) +**Domain:** [payment | auth | notifications | voice | ui | config | ...] +**Bug:** [One-line: what goes wrong] +**Root cause:** [One-line: why it happens] +**Detection:** [How to spot this pattern in code — grep pattern or code smell] +**Files:** [Common file patterns where this occurs] +**Tags:** [comma-separated searchable tags] +``` + +### fixes.md format + +```markdown +## Fix: [TICKET] — [Short description] +**Date:** [DATE] +**PR:** #[NUMBER] +**Domain:** [domain] +**What broke:** [one line] +**What fixed it:** [one line] +**Diff summary:** [key changes — not the full diff, just the insight] +**Quality:** [merged_clean | had_review_feedback | was_reverted | caused_followup] +**Files:** [files modified] +**Tags:** [comma-separated] +``` + +### lessons.md format + +```markdown +## Lesson: [Short name] +**Source:** [Who said it — user feedback, code review, prod incident] +**Date:** [DATE] +**Context:** [What happened] +**Rule:** [The lesson in one sentence] +**Tags:** [comma-separated] +``` + +### anti-patterns.md format + +```markdown +## Anti-pattern: [Short name] +**Source:** [TICKET or PR that was reverted/failed] +**Date:** [DATE] +**What was tried:** [The fix that didn't work] +**Why it failed:** [Root cause of failure] +**Better approach:** [What should have been done instead] +**Tags:** [comma-separated] +``` + +## How Memory Is Used By Other Skills + +### During /investigate +At the START of investigation, before reading any code: +```bash +if [ -f ~/.gstack/memory/patterns.md ]; then + echo "🧠 Searching memory for related patterns..." + grep -i -B 1 -A 8 "KEYWORD" ~/.gstack/memory/patterns.md 2>/dev/null || true +fi +``` + +If a match is found, cite it and apply the pattern: +> 💡 **Memory match:** Pattern "[NAME]" (seen in [TICKETS]). +> Root cause was [X]. Checking if same pattern applies here... + +### During /review +Check if the diff introduces a known anti-pattern: +```bash +if [ -f ~/.gstack/memory/anti-patterns.md ]; then + # Extract file paths from diff, search anti-patterns for matches + grep -i "CHANGED_FILE_PATTERN" ~/.gstack/memory/anti-patterns.md 2>/dev/null || true +fi +``` + +### During /ship (Phase 6 — post-ship) +Append to fixes.md: +```bash +cat >> ~/.gstack/memory/fixes.md << EOF + +## Fix: [IDENTIFIER] — [TITLE] +**Date:** $(date +%Y-%m-%d) +**PR:** #[NUMBER] +**Domain:** [domain] +**What broke:** [summary] +**What fixed it:** [summary] +**Diff summary:** [key insight] +**Quality:** merged_clean +**Files:** [files] +**Tags:** [tags] +EOF +``` + +### During /solve (Phase 6 — post-solve) +Append both a fix AND check if a new pattern emerged: +- If the same root cause was seen before → update the "Seen again" field +- If it's a new root cause → append a new pattern entry + +## Commands + +### `/memory search ` + +Search all memory files for the query. Show matches with context: + +```bash +MEMORY_DIR="$HOME/.gstack/memory" +echo "🔍 Searching memory for: $QUERY" +echo "" +for file in patterns.md fixes.md lessons.md anti-patterns.md; do + if [ -f "$MEMORY_DIR/$file" ]; then + MATCHES=$(grep -i -B 1 -A 8 "$QUERY" "$MEMORY_DIR/$file" 2>/dev/null) + if [ -n "$MATCHES" ]; then + echo "📁 $file:" + echo "$MATCHES" + echo "---" + fi + fi +done +``` + +Present results grouped by file, with the most relevant match highlighted. + +### `/memory add pattern` + +Interactive — ask for: +1. Short name +2. Domain +3. Bug description (one line) +4. Root cause (one line) +5. Detection method (grep pattern or code smell) +6. Tags + +Then append to patterns.md. + +### `/memory import` + +One-time backfill from git history: + +```bash +# Get last 6 months of merged PRs with their commit messages +git log --oneline --since="6 months ago" --merges --format="%h %s" | + head -100 +``` + +For each PR: +1. Extract the ticket ID from the title (e.g., `[RES-4662]`) +2. Read the commit message for context +3. Classify: is this a bug fix, feature, refactor, or chore? +4. For bug fixes: extract the pattern and add to patterns.md +5. Skip: docs changes, config changes, dependency updates + +**Quality filter during import:** +- Check if the commit was reverted within 7 days → anti-pattern, not pattern +- Check if there was a follow-up fix on the same files within 48h → incomplete fix +- Only index commits that survived without follow-up issues + +### `/memory prune` + +Review entries and remove: +- Patterns that haven't been "seen again" in 6+ months +- Fixes for code that no longer exists (check if files still exist) +- Duplicate patterns (merge them) + +### `/memory` (no args) + +Show memory dashboard: + +``` +🧠 gstack Memory — [PROJECT] +━━━━━━━━━━━━━━━━━━━━ +Patterns: 12 indexed (3 seen 2+ times) +Fixes: 47 indexed (oldest: 2024-09-15) +Lessons: 8 indexed +Anti-pats: 3 indexed + +Top domains: notifications (8), payment (6), voice (4) +Last updated: 2024-03-23 + +Recent patterns: + • "Operator Precedence in PHP Guards" (seen 3x) + • "Missing Terminal Status Guard" (seen 2x) + • "Null Check on Optional Relation" (seen 2x) +``` + +## Integration with conductor.json + +Projects can configure memory behavior in `conductor.json`: + +```json +{ + "memory": { + "auto_index": true, + "domains": ["payment", "auth", "voice", "notifications", "ui"], + "exclude_paths": ["tests/", "docs/", "config/"], + "quality_threshold": "merged_clean" + } +} +``` + +## Philosophy + +**Search Your Own History First.** Before investigating any bug, check if you've +seen this pattern before. The cost of checking is near-zero. The cost of +re-investigating from scratch is 10 minutes you'll never get back. + +This extends gstack's "Search Before Building" principle from external knowledge +(Layer 1-3) to internal knowledge (Layer 0 — your own past work). Layer 0 is +the highest-confidence source because it's YOUR codebase, YOUR bugs, YOUR fixes. + +Memory compounds. After 100 sessions, you've indexed 100 patterns. Session 101 +starts with 100 patterns to search against. That's the difference between a tool +and a teammate. diff --git a/solve/SKILL.md b/solve/SKILL.md index 2621c55ef..3c76b3ffa 100644 --- a/solve/SKILL.md +++ b/solve/SKILL.md @@ -18,6 +18,8 @@ allowed-tools: - AskUserQuestion - WebSearch --- + + ## Preamble (run first) diff --git a/solve/SKILL.md.tmpl b/solve/SKILL.md.tmpl new file mode 100644 index 000000000..bb7d64848 --- /dev/null +++ b/solve/SKILL.md.tmpl @@ -0,0 +1,233 @@ +--- +name: solve +version: 1.0.0 +description: | + End-to-end autonomous issue resolution. Takes a ticket ID from Linear, Jira, + or GitHub Issues and chains: fetch → investigate → code → test → review → ship. + Use when asked to "solve this ticket", "fix PROJ-123", "resolve this issue", + or "take this ticket and ship it". + Proactively suggest when the user pastes a ticket URL or mentions a ticket ID. +allowed-tools: + - Bash + - Read + - Write + - Edit + - Grep + - Glob + - Agent + - AskUserQuestion + - WebSearch +--- + +{{PREAMBLE}} + +```bash +# Check for memory from past solves +MEMORY_DIR="$HOME/.gstack/memory" +if [ -d "$MEMORY_DIR" ] && [ -f "$MEMORY_DIR/patterns.md" ]; then + PATTERN_COUNT=$(grep -c "^## Pattern:" "$MEMORY_DIR/patterns.md" 2>/dev/null || echo "0") + echo "MEMORY: $PATTERN_COUNT patterns indexed" +else + echo "MEMORY: none (run /solve to start building)" +fi +``` +# /solve — End-to-End Issue Resolution + +You are a **Senior Engineer** who takes a ticket and ships a fix. No hand-holding, +no partial work, no "I investigated and here's what I found." You find the bug, +write the fix, test it, review your own code, and ship the PR. + +## Arguments + +- `/solve PROJ-123` — resolve a ticket by identifier (Linear, Jira, GitHub) +- `/solve https://linear.app/team/issue/PROJ-123` — resolve from URL +- `/solve` — if on a branch named after a ticket (e.g., `fix/PROJ-123-foo`), auto-detect + +## Phase 0: Fetch Ticket + +Determine the ticket system and fetch full context: + +**Linear** (identifiers like `RES-123`, `ENG-456`, or linear.app URLs): +```bash +# Extract identifier from URL or argument +IDENTIFIER="$1" +if echo "$IDENTIFIER" | grep -q "linear.app"; then + IDENTIFIER=$(echo "$IDENTIFIER" | grep -oE '[A-Z]+-[0-9]+' | tail -1) +fi + +# Fetch via Linear GraphQL API (requires LINEAR_API_KEY in env) +curl -s -X POST https://api.linear.app/graphql \ + -H "Authorization: $LINEAR_API_KEY" \ + -H "Content-Type: application/json" \ + -d "$(jq -n --arg id "$IDENTIFIER" '{ + query: "query($filter: IssueFilter) { issues(filter: $filter, first: 1) { nodes { id identifier title description state { name } assignee { name } comments { nodes { body createdAt user { name } } } labels { nodes { name } } } } }", + variables: { filter: { identifier: { eq: $id } } } + }')" +``` + +**GitHub Issues** (identifiers like `#123`, or github.com URLs): +```bash +gh issue view "$ISSUE_NUMBER" --json title,body,comments,labels,assignees +``` + +**Jira** (identifiers like `JIRA-123`, or atlassian.net URLs): +```bash +curl -s "$JIRA_BASE_URL/rest/api/3/issue/$IDENTIFIER" \ + -H "Authorization: Basic $JIRA_AUTH" +``` + +If the ticket system can't be detected, use AskUserQuestion to ask. + +Present the ticket to yourself: +``` +Ticket: [IDENTIFIER] — [TITLE] +Status: [STATE] +Description: [DESCRIPTION] +Comments: [COMMENT_COUNT] comments +Labels: [LABELS] +``` + +## Phase 1: Investigate (chain /investigate) + +Read the skill file and follow its protocol: +```bash +cat ~/.claude/skills/gstack/investigate/SKILL.md +``` + +Run the full investigation: read the codebase, trace the chain, identify root cause. + +**Memory boost:** Before investigating, check if a similar pattern exists: +```bash +if [ -f ~/.gstack/memory/patterns.md ]; then + # Search for related patterns by file, domain, or keyword + grep -i -A 10 "KEYWORD_FROM_TICKET" ~/.gstack/memory/patterns.md || true +fi +``` + +If a matching pattern is found, cite it: +> 💡 **Similar pattern found:** [Pattern name] from [TICKET]. Previous fix: [summary]. +> Applying same approach with adaptations for this case. + +**Gate:** After investigation, assess confidence (0-100). +- If confidence >= 80%: proceed to code. +- If confidence < 80%: use AskUserQuestion to present findings and ask whether to proceed. + Show: root cause, proposed fix, risk assessment, and the confidence score. + +## Phase 2: Code the Fix + +1. Create a branch: `fix/[IDENTIFIER]-short-description` +2. Write the fix based on the investigation +3. Write tests (contract tests FIRST, then unit, then feature) +4. Commit with message: `[IDENTIFIER] Fix: one-line summary` + +**Rules:** +- Read the consumer before writing the producer +- Follow existing code patterns in the file you're modifying +- Never skip tests — Boil the Lake + +## Phase 3: Self-Review (chain /review) + +Read the review skill and run it against your own diff: +```bash +cat ~/.claude/skills/gstack/review/SKILL.md +``` + +Review your own code. If the review finds issues, fix them before proceeding. +This is NOT optional — you review your own code like a different person wrote it. + +## Phase 4: Test + +Run the project's test suite: +```bash +# Detect test framework and run +if [ -f "artisan" ]; then + php artisan config:clear && php artisan test --parallel +elif [ -f "package.json" ]; then + npm test +elif [ -f "Cargo.toml" ]; then + cargo test +elif [ -f "go.mod" ]; then + go test ./... +elif [ -f "pytest.ini" ] || [ -f "setup.py" ]; then + pytest +fi +``` + +If tests fail, fix and re-run. Do not proceed with failing tests. + +## Phase 5: Ship (chain /ship) + +Read the ship skill and follow its protocol: +```bash +cat ~/.claude/skills/gstack/ship/SKILL.md +``` + +Create the PR, linking back to the original ticket. + +## Phase 6: Update Memory + +After shipping, append to the memory: + +```bash +mkdir -p ~/.gstack/memory + +# Append pattern +cat >> ~/.gstack/memory/patterns.md << 'PATTERN_EOF' + +## Pattern: [SHORT_NAME] +**Source:** [IDENTIFIER], PR #[NUMBER] +**Domain:** [domain] +**Bug:** [one-line description of what was wrong] +**Fix:** [one-line description of what fixed it] +**Files:** [list of files modified] +**Tags:** [comma-separated tags for future search] +PATTERN_EOF +``` + +## Non-interactive philosophy + +Like `/ship`, `/solve` makes decisions autonomously. It only asks the user when: +1. Confidence is below 80% after investigation (Phase 1 gate) +2. Tests fail after 2 fix attempts +3. The ticket system can't be detected + +Everything else is autonomous: fetch, investigate, code, review, test, ship. + +## Ticket update + +After shipping, update the ticket: +- **Linear:** Post a comment with the PR link and move to "In Review" +- **GitHub:** Post a comment linking the PR +- **Jira:** Transition to "In Review" and post PR link + +## Example + +``` +> /solve RES-4662 + +Fetching RES-4662 from Linear... +✅ Ticket: RES-4662 — Notifications sent to recovered sequences + Status: Todo | Labels: Bug + Description: "Debtor notifications are being sent to sequences with + status 'recovered', which should never receive notifications..." + +🔍 Investigating... +💡 Similar pattern found: "Terminal Status Guard" from RES-4508. + Previous fix: added status check at top of notification job handle(). + +Root cause: NotifyPhoneChangeAbcJustice.php:45 + `!$this->sequence->status === 'recovered'` — PHP operator precedence. + `!` binds before `===`, so this evaluates as `false === 'recovered'` + which is always false. The guard never blocks. + +Confidence: 95% — proceeding to code. + +📝 Fix: wrap in parentheses `!($this->sequence->status === 'recovered')` + Also found same pattern in 3 other notification jobs. + Branch: fix/RES-4662-notification-terminal-guard + +✅ Tests pass (47 assertions) +✅ Self-review: PASS +✅ PR #4389 created, linked to RES-4662 +✅ Memory updated: "Operator Precedence in PHP Guards" +```