mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-06 00:44:07 +01:00
* feat(skills): absorb pstack's blast radius, transcript recall, vet-the-number and design-twice as lazy sections
/investigate gains sections/blast-radius.md (the one safety fact, the five-rung
evidence ladder, look where grep stops, confirmed and cleared risks apart) with a
Phase 4 pointer, and the shared-premise red flag before the 3-strike rule.
/context-restore is carved for the first time: on NO_CHECKPOINTS, sections/transcripts.md
mines this workspace's own transcripts (host stores declared in hosts/*.ts and rendered
by TRANSCRIPT_SOURCES / TRANSCRIPT_DISCOVERY), a fixed 7-day window, never another
project's folder, subagents return only findings, redaction before anything leaves,
git and gh verify every artifact, a status-tagged brief. /benchmark is carved for the
first time: sections/vet-the-number.md asks the seven questions before Phase 5 reports
a comparison. /plan-eng-review gains sections/design-twice.md (a second structurally
distinct shape compared on interface depth, the eight design red flags), bound by the
autoplan methodology snapshot alongside review-sections.md.
Ideas from pstack (Lauren Tan, MIT), rewritten in gstack's words; NOTICE.md. Garry,
2026-10-26: "Do it, send". Guards: carve-guards entries and measured lines, eleven
plan-eng-review pins re-measured, periodic carve-section-loading cases for the two new
carves, free tests with a fixture transcript.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(pstack-c1): fold design-twice into Section 1 of the eng review section, per-host transcript table, fixture and slice-cap repairs
The remote free run reported nine failures in seven files, all from this
branch: four tests encode plan-eng-review's one companion section
(eng-scope-entry-ap, autoplan-methodology-names, plan-review-cases,
review-checklists), so the design-twice pass now lives inside Section 1 of
sections/review-sections.md with the skeleton, manifest and
bin/gstack-autoplan-snapshot.ts back at main; the Codex render of
context-restore named ~/.claude/ through the all-hosts transcript table, so
{{TRANSCRIPT_SOURCES}} renders this host's row only; the transcript fixture
is declared in FREE_FIXTURES; and the two new periodic cases took the census
to 39 slices, so evals-periodic.yml's eval-slices max-parallel goes 38 -> 40.
Pins: 11 plan-eng-review re-pins at +2,896; maxSizeRatio 1.2400 (measured
1.2389); Codex gpt-5.4 overlay 159,588 bytes.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pre-landing review fixes
The discovery block takes the none branch from hosts/*.ts's transcripts
declaration instead of a host-name fallthrough, and the resolver header says
where each half lives; the T2 subagent brief frames the transcript as data,
not instructions; the test asserts table/block agreement per host, imports
CAPY_NO_TRANSCRIPTS, and drops the fixture-shape case nothing reads.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs: ADDING_A_HOST.md lists the optional transcripts HostConfig field
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
---------
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
349 lines
17 KiB
Cheetah
349 lines
17 KiB
Cheetah
---
|
|
name: context-restore
|
|
preamble-tier: 2
|
|
version: 1.0.0
|
|
description: |
|
|
Restore working context saved earlier by /context-save. Loads the most recent
|
|
saved state (preferring the current branch, falling back across branches) so
|
|
you can pick up where you left off — even across Conductor workspace handoffs.
|
|
Use when asked to "resume", "restore context", "where was I", or
|
|
"pick up where I left off". Pair with /context-save. (gstack)
|
|
allowed-tools:
|
|
- Bash
|
|
- Read
|
|
- Glob
|
|
- Grep
|
|
- AskUserQuestion
|
|
triggers:
|
|
- resume where i left off
|
|
- restore context
|
|
- where was i
|
|
- pick up where i left off
|
|
- context restore
|
|
---
|
|
|
|
{{PREAMBLE}}
|
|
|
|
# /context-restore — Restore Saved Working Context
|
|
|
|
You are a **Staff Engineer reading a colleague's meticulous session notes** to
|
|
pick up exactly where they left off. Your job is to load the most recent saved
|
|
context and present it clearly so the user can resume work without losing a beat.
|
|
|
|
**HARD GATE:** Do NOT implement code changes. This skill only reads saved
|
|
context files and presents the summary.
|
|
|
|
**Default: prefer the most recent checkpoint saved on the CURRENT branch; if
|
|
this branch has none, fall back to the most recent across ALL branches.** The
|
|
fallback is for Conductor workspace handoff — a context saved on one branch can
|
|
be resumed from another. The current-branch preference exists because every
|
|
worktree of a repo shares one checkpoints directory (same origin-derived slug),
|
|
so without it `/context-restore` in one worktree could silently load a sibling
|
|
worktree's newer checkpoint.
|
|
|
|
**Do NOT hard-filter the candidate set to the current branch** — other-branch
|
|
checkpoints stay in the set as a fallback. They are just ordered *after* the
|
|
current branch's own, so a current-branch save is never shadowed by a newer
|
|
sibling-worktree save. (`/context-save list` is the flow that hard-scopes to one
|
|
branch.)
|
|
|
|
---
|
|
|
|
## Detect command
|
|
|
|
Parse the user's input:
|
|
|
|
- `/context-restore` → load the most recent saved context (current branch first, then any branch)
|
|
- `/context-restore <title-fragment-or-number>` → load a specific saved context
|
|
- `/context-restore list` → tell the user "Use `/context-save list` — listing
|
|
lives on the save side" and exit. No mode detection here.
|
|
|
|
---
|
|
{{SECTION_INDEX:context-restore}}
|
|
---
|
|
|
|
## Restore flow
|
|
|
|
### Step 1: Find saved contexts
|
|
|
|
```bash
|
|
{{SLUG_SETUP}}
|
|
GSTACK_STATE_ROOT=$(~/.claude/skills/gstack/bin/gstack-paths --get GSTACK_STATE_ROOT); : "${GSTACK_STATE_ROOT:?gstack-paths failed; reinstall with ./setup or /gstack-upgrade}"
|
|
CHECKPOINT_DIR="$GSTACK_STATE_ROOT/projects/$SLUG/checkpoints"
|
|
# Project identity: canonical remote (root only when there is no remote).
|
|
PROJECT_REMOTE=$(~/.claude/skills/gstack/bin/gstack-slug --get PROJECT_REMOTE 2>/dev/null) || true
|
|
PROJECT_ROOT=$(~/.claude/skills/gstack/bin/gstack-slug --get PROJECT_ROOT 2>/dev/null) || true
|
|
LEGACY_SLUG=$(~/.claude/skills/gstack/bin/gstack-slug --get LEGACY_SLUG 2>/dev/null) || true
|
|
echo "PROJECT_IDENTITY: ${PROJECT_REMOTE:-${PROJECT_ROOT:-unknown}}"
|
|
echo "PROJECT_ROOT: ${PROJECT_ROOT:-unknown}"
|
|
if [ -n "${LEGACY_SLUG:-}" ] && [ -d "$GSTACK_STATE_ROOT/projects/$LEGACY_SLUG/checkpoints" ]; then
|
|
LEGACY_N=$(find "$GSTACK_STATE_ROOT/projects/$LEGACY_SLUG/checkpoints" -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
if [ "$LEGACY_N" -gt 0 ]; then
|
|
echo "LEGACY_BUCKET: projects/$LEGACY_SLUG holds $LEGACY_N earlier checkpoint(s) from the bucket this project shared with other nested-group repos; review and copy with: ~/.claude/skills/gstack/bin/gstack-slug --adopt-legacy"
|
|
fi
|
|
fi
|
|
if [ ! -d "$CHECKPOINT_DIR" ]; then
|
|
echo "NO_CHECKPOINTS"
|
|
else
|
|
# Use find + sort instead of ls -1t. Two reasons:
|
|
# 1. Canonical order is the filename YYYYMMDD-HHMMSS prefix (stable across
|
|
# copies/rsync). Filesystem mtime drifts and is not authoritative.
|
|
# 2. On macOS, `find ... | xargs ls -1t` with zero results falls back to
|
|
# listing cwd. `sort -r` on empty input cleanly returns nothing.
|
|
# Scan the 200 newest so a current-branch checkpoint sitting below a burst of
|
|
# sibling-worktree saves can still be found; the result is capped at 20 below.
|
|
ALL=$(find "$CHECKPOINT_DIR" -maxdepth 1 -name "*.md" -type f 2>/dev/null | sort -r | head -200)
|
|
if [ -z "$ALL" ]; then
|
|
echo "NO_CHECKPOINTS"
|
|
else
|
|
# Order current-branch checkpoints first, other branches after. A git branch
|
|
# is checked out in at most one worktree, and all worktrees of a repo share
|
|
# one checkpoints dir (same origin-derived slug), so without this preference
|
|
# `/context-restore` in worktree A could load worktree B's newer checkpoint.
|
|
# Cross-branch resume (Conductor handoff) is preserved as the fallback: when
|
|
# the current branch has no checkpoint, the full newest-first set is used.
|
|
# CURRENT_BRANCH may be pre-set (tests); otherwise resolve it from git.
|
|
: "${CURRENT_BRANCH:=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)}"
|
|
SAME=""; OTHER=""
|
|
while IFS= read -r f; do
|
|
[ -n "$f" ] || continue
|
|
b=$(grep -m1 '^branch:' "$f" 2>/dev/null | sed 's/^branch:[[:space:]]*//')
|
|
if [ -n "$CURRENT_BRANCH" ] && [ "$b" = "$CURRENT_BRANCH" ]; then
|
|
SAME="${SAME}${f}
|
|
"
|
|
else
|
|
OTHER="${OTHER}${f}
|
|
"
|
|
fi
|
|
done <<EOF
|
|
$ALL
|
|
EOF
|
|
# Identity check: only checkpoints stamped with THIS project's
|
|
# identity are candidates for "latest". Unstamped (older) checkpoints are
|
|
# trusted unless the directory demonstrably holds another project's files.
|
|
CLASSIFIED=$(printf '%s%s' "$SAME" "$OTHER" | grep -v '^[[:space:]]*$' \
|
|
| ~/.claude/skills/gstack/bin/gstack-slug --classify-checkpoints 2>/dev/null)
|
|
if [ -z "$CLASSIFIED" ]; then
|
|
echo "IDENTITY_CHECK_FAILED"
|
|
printf '%s%s' "$SAME" "$OTHER" | grep -v '^[[:space:]]*$' | head -20 | sed 's/^/UNVERIFIED /'
|
|
else
|
|
FOREIGN_N=$(printf '%s\n' "$CLASSIFIED" | grep -c '^foreign' || true)
|
|
VERIFIED=$(printf '%s\n' "$CLASSIFIED" | awk -F '\t' -v f="$FOREIGN_N" \
|
|
'$(1) == "match" || $(1) == "match-root" || ($(1) == "unstamped" && f == 0) { print $(2) }')
|
|
# Cap at 20: a user with 10k saved files shouldn't blow the context window.
|
|
FILES=$(printf '%s\n' "$VERIFIED" | head -20)
|
|
if [ -n "$FILES" ]; then echo "$FILES"; else echo "NO_VERIFIED_CHECKPOINTS"; fi
|
|
# A newer checkpoint of another branch that names the same task.
|
|
printf '%s\n' "$VERIFIED" | ~/.claude/skills/gstack/bin/gstack-slug --newer-task "$CURRENT_BRANCH" 2>/dev/null || true
|
|
printf '%s\n' "$CLASSIFIED" | awk -F '\t' -v f="$FOREIGN_N" \
|
|
'$(1) == "match-root" { print "ROOT_DIFFERS " $(2) }
|
|
$(1) == "foreign" { print "FOREIGN " $(2) }
|
|
$(1) == "unstamped" && f > 0 { print "UNVERIFIED " $(2) }' | head -40
|
|
if [ "$FOREIGN_N" -gt 0 ]; then
|
|
echo "SHARED_BUCKET: $FOREIGN_N checkpoint(s) here were saved by another project"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
# Checkpoints saved in a repository below this directory (BELOW_CWD, POINTER).
|
|
~/.claude/skills/gstack/bin/gstack-slug --checkpoints-below 2>/dev/null || true
|
|
```
|
|
|
|
**Candidates include every `.md` file in the directory**, but they are ordered
|
|
**current-branch-first** (the branch is read from each file's `branch:`
|
|
frontmatter). Other-branch files stay in the set as a fallback, which preserves
|
|
Conductor workspace handoff when the current branch has no checkpoint of its own.
|
|
|
|
**Project identity.** Plain path lines are this project's checkpoints (verified by
|
|
the `remote:`/`project_root:` stamp, or unstamped in a directory no other project
|
|
has written to). Prefixed lines are never candidates for "latest":
|
|
- `FOREIGN <path>`: saved by a different project that shares this directory.
|
|
- `UNVERIFIED <path>`: saved before identity stamps, in a directory another
|
|
project also uses, so it may belong to either.
|
|
- `ROOT_DIFFERS <path>`: same repository, different checkout (Conductor workspace
|
|
handoff or a moved clone). This is information, not a mismatch; the file is
|
|
also listed as a plain path line.
|
|
- `NEWER_TASK <path>`: another branch of this repository saved a newer checkpoint
|
|
for the same task (its worktree is below this main tree, or the title or ticket
|
|
matches). The file is also a plain path line, after the current branch's own.
|
|
- `BELOW_CWD <path>` / `POINTER <path>`: saved by a session in a repository below
|
|
this directory (`POINTER`: a /context-save started here recorded it). These are
|
|
candidates, not mismatches.
|
|
|
|
### Step 2: Load the right file
|
|
|
|
- If the user specified a title fragment or number: find the matching file among
|
|
the candidates, including `FOREIGN` and `UNVERIFIED` lines (a user may restore
|
|
another project's checkpoint deliberately).
|
|
- Otherwise: load the **first plain path line returned by Step 1 above**, which
|
|
is the newest `YYYYMMDD-HHMMSS` checkpoint of this project for the current
|
|
branch, or, if the current branch has none, the newest across all branches.
|
|
- **No useful branch discriminator.** When `CURRENT_BRANCH` is empty, `HEAD`, or
|
|
otherwise cannot discriminate (cwd is not a git repo, or branch is unknown), do
|
|
not silently load newest if the recent candidates (e.g. last 24 hours, or the
|
|
three newest) carry **different titles**. Show those titles with timestamps via
|
|
AskUserQuestion, mark newest as the recommended option, and wait. Only auto-load
|
|
newest when every recent candidate shares one title (same topic).
|
|
- **Do not silently take an older checkpoint.** If Step 1 printed `NEWER_TASK`,
|
|
or a `BELOW_CWD`/`POINTER` file is newer than the first plain path line (or
|
|
there is none), show both (title, branch, saved time, and `project_root` for a
|
|
file below this directory), propose the newer one as the continuation, and ask
|
|
via AskUserQuestion which to load. A `BELOW_CWD`/`POINTER` file is not a
|
|
PROJECT MISMATCH: say "Saved in `{project_root}`, below this directory."
|
|
- If Step 1 printed `NO_VERIFIED_CHECKPOINTS` or `IDENTITY_CHECK_FAILED`, do not
|
|
present any checkpoint as the latest one. Say "No checkpoint in this directory
|
|
is verified as this project's", list the `FOREIGN`/`UNVERIFIED` titles with the
|
|
`remote:`/`project_root:` they name (or "unknown"), and ask which one, if any, to
|
|
open.
|
|
|
|
**Report identity problems before any summary.** If the chosen file is `FOREIGN`
|
|
or `UNVERIFIED`, start with:
|
|
"PROJECT MISMATCH: this checkpoint was saved by `{remote or project_root from its
|
|
frontmatter, or 'an unknown project'}`, not this project (`{PROJECT_IDENTITY}`)."
|
|
If Step 1 printed `SHARED_BUCKET`, say "This checkpoints directory also holds N
|
|
checkpoint(s) from another project; only this project's are listed." If it printed
|
|
`LEGACY_BUCKET`, relay that line. If the chosen file is `ROOT_DIFFERS`, add
|
|
"Saved from another checkout of this repository at `{project_root}`." as info.
|
|
|
|
**Check whether the branch moved since the save.** The stamp records the commit
|
|
the save was made at in `head:`. Before presenting, compare it with this
|
|
checkout. FILE is the checkpoint path chosen in Step 2, one of the paths Step 1
|
|
printed:
|
|
|
|
```bash
|
|
FILE="<the FILE path printed above>"
|
|
SAVED_HEAD=$(tr -d '\r' < "$FILE" 2>/dev/null | sed -n '1{/^---$/!q;d;}; /^---$/q; /^head:/{s/^head:[[:space:]]*//p;q;}')
|
|
REPO_DIR=.
|
|
git rev-parse --git-dir >/dev/null 2>&1 || REPO_DIR=$(tr -d '\r' < "$FILE" 2>/dev/null | sed -n '1{/^---$/!q;d;}; /^---$/q; /^worktree:/{s/^worktree:[[:space:]]*//p;q;}')
|
|
[ -n "$REPO_DIR" ] || REPO_DIR=$(tr -d '\r' < "$FILE" 2>/dev/null | sed -n '1{/^---$/!q;d;}; /^---$/q; /^project_root:/{s/^project_root:[[:space:]]*//p;q;}')
|
|
CURRENT_HEAD=$(git -C "${REPO_DIR:-.}" rev-parse --verify -q HEAD 2>/dev/null)
|
|
if [ -z "$SAVED_HEAD" ]; then
|
|
echo "HEAD_UNTRACKED"
|
|
elif [ -z "$CURRENT_HEAD" ]; then
|
|
echo "HEAD_NOT_CHECKED"
|
|
elif [ "$SAVED_HEAD" = "$CURRENT_HEAD" ]; then
|
|
echo "HEAD_UNCHANGED"
|
|
elif ! printf '%s\n' "$SAVED_HEAD" | grep -Eqx '[0-9a-f]{40}([0-9a-f]{24})?' \
|
|
|| ! git -C "$REPO_DIR" cat-file -e "$SAVED_HEAD^{commit}" 2>/dev/null; then
|
|
echo "HEAD_UNKNOWN $SAVED_HEAD"
|
|
elif git -C "$REPO_DIR" merge-base --is-ancestor "$SAVED_HEAD" "$CURRENT_HEAD"; then
|
|
echo "HEAD_MOVED $(git -C "$REPO_DIR" rev-list --count "$SAVED_HEAD..$CURRENT_HEAD")"
|
|
git -C "$REPO_DIR" log --oneline -10 "$SAVED_HEAD..$CURRENT_HEAD"
|
|
else
|
|
echo "HEAD_DIVERGED $SAVED_HEAD"
|
|
fi
|
|
```
|
|
|
|
- `HEAD_UNCHANGED` or `HEAD_NOT_CHECKED`: nothing to add.
|
|
- `HEAD_MOVED <n>`: print `<n> commit(s) landed since this save:` and the commit
|
|
lines above the Remaining Work groups, and list **every** Remaining Work item
|
|
under **Verify first**: the saving session could not know about those commits,
|
|
so any step may already be done.
|
|
- `HEAD_DIVERGED` or `HEAD_UNKNOWN`: print `This checkpoint was saved at {first 7
|
|
characters of head}, which is not in this checkout's history (saved on another
|
|
branch, rebased, reset, or not fetched).` and list every item under Verify first.
|
|
- `HEAD_UNTRACKED`: print `This checkpoint predates commit tracking; restore cannot tell whether work landed after it.`
|
|
and sort as usual.
|
|
|
|
**Sort Remaining Work by provenance.** Keep every item's original text and saved
|
|
order, and drop nothing. When the head check printed `HEAD_MOVED`, `HEAD_DIVERGED`
|
|
or `HEAD_UNKNOWN`, skip the rules below: every item goes under Verify first,
|
|
whatever its marker. Put an item under **Verify first** when it:
|
|
- ends in `(path assumed)` or `(code read)`;
|
|
- ends in `(path run)` but its text reports a failure;
|
|
- ends in a parenthesised trailing marker that is **not** one of the five English
|
|
literals `(path run)`, `(path read)`, `(path assumed)`, `(target state checked)`,
|
|
`(code read)` — treat a translated or unknown marker as `(path assumed)` (→ Verify
|
|
first), never as unmarked Next steps;
|
|
- has no marker and is a writing step (migration, sync, insert, import, a dialog that
|
|
writes) or names a concrete path (a runnable command, CLI flag or switch, config key
|
|
or value, or file or directory path).
|
|
|
|
Every other item goes under **Next steps**: `(path run)` with a successful outcome,
|
|
`(path read)`, `(target state checked)`, and unmarked items that neither write nor
|
|
name a concrete path. Verifying means read-only inspection: read the file or the
|
|
target, or run a command that changes nothing. Checkpoints saved before provenance
|
|
markers existed have none, so their concrete-path items land under Verify first. When
|
|
the file has no provenance markers at all, print this line above the groups:
|
|
`This checkpoint predates provenance markers; items naming commands, paths or writes are listed under Verify first.`
|
|
|
|
Read the chosen file and present a summary:
|
|
|
|
```
|
|
RESUMING CONTEXT
|
|
════════════════════════════════════════
|
|
Title: {title}
|
|
Branch: {branch from frontmatter}
|
|
Saved: {timestamp, human-readable}
|
|
Duration: Last session was {formatted duration} (if available)
|
|
Status: {status}
|
|
════════════════════════════════════════
|
|
|
|
### Summary
|
|
{summary from saved file}
|
|
|
|
### Remaining Work
|
|
{commit-tracking lines, if any}
|
|
{legacy banner line, if it applies}
|
|
|
|
Next steps
|
|
{Next steps items, in saved order, original text}
|
|
|
|
Verify first (inspect read-only before executing anything)
|
|
{Verify first items, in saved order, original text}
|
|
|
|
### Notes
|
|
{notes}
|
|
```
|
|
|
|
If the current branch differs from the saved context's branch, note this:
|
|
"This context was saved on branch `{branch}`. You are currently on
|
|
`{current branch}`. You may want to switch branches before continuing."
|
|
|
|
### Step 3: Offer next steps
|
|
|
|
After presenting, ask via AskUserQuestion:
|
|
|
|
- A) Continue working on the remaining items
|
|
- B) Show the full saved file
|
|
- C) Just needed the context, thanks
|
|
|
|
If A, take the first Remaining Work item in saved order. If it is under Next steps,
|
|
suggest starting there. If it is under Verify first, suggest verifying it (read-only)
|
|
before doing it or any later item, so a later runnable step never jumps ahead of an
|
|
unverified earlier one.
|
|
|
|
---
|
|
|
|
## If no saved contexts exist
|
|
|
|
If Step 1 printed `NO_CHECKPOINTS` and no `BELOW_CWD` or `POINTER` line, tell the user
|
|
in one line that nothing was saved with `/context-save`, then rebuild the context from
|
|
what the machine already holds:
|
|
|
|
{{SECTION:transcripts}}
|
|
|
|
If it also printed `LEGACY_BUCKET`, relay that line: earlier checkpoints are kept
|
|
in the old bucket until the user copies them with `gstack-slug --adopt-legacy`.
|
|
|
|
---
|
|
|
|
## Important Rules
|
|
|
|
- **Never modify code.** This skill only reads saved files, this workspace's own
|
|
transcripts and git state, and presents them.
|
|
- **Prefer the current branch's own checkpoint, but keep all branches in the
|
|
fallback set.** Cross-branch resume (Conductor handoff) works when the
|
|
current branch has no checkpoint, and a sibling worktree's newer save never
|
|
shadows this branch's own.
|
|
- **Never present another project's checkpoint as "latest".** Identity is the
|
|
canonical remote (the root only when there is no remote); a mismatch is
|
|
reported before any summary.
|
|
- **A checkpoint describes the commit it was saved at.** When commits landed
|
|
after the save, every Remaining Work item is verified before it is run.
|
|
- **"Most recent" means the filename `YYYYMMDD-HHMMSS` prefix**, not
|
|
`ls -1t` (filesystem mtime). Filenames are stable across file-system
|
|
operations; mtime is not.
|
|
- **This is a gstack skill, not a Claude Code built-in.** When the user types
|
|
`/context-restore`, invoke this skill via the Skill tool.
|