Files
gstack/gstack-upgrade/migrations/v1.67.0.0.sh
T
Garry TanandClaude Fable 5 9af589bb73 fix(setup): render the gbrain :user variant to an out-dir — global installs stay git-clean
On a global-git install with gbrain, ./setup and 'gstack-config
gbrain-refresh' ran gen:skill-docs:user IN PLACE inside the install
checkout, rewriting ~16 TRACKED SKILL.md files. The checkout stayed
permanently dirty, every /gstack-upgrade 'git stash' saved a redundant
snapshot of generated content, and the growing stash list invited a 'git
stash pop' that would lay stale instruction markdown from an older gstack
over the current version — a quiet wrong-rules failure mode.

Fix, wired through machinery that already existed (gen-skill-docs
--out-dir + the symlink install layer): brain-aware SKILL.md now renders
into the untracked ~/.gstack/render/claude, and both Claude installers
serve the render when present — setup's link_claude_skill_dirs prefers
$GSTACK_HOME/render/claude/<skill>/SKILL.md, and bin/gstack-relink does
the same so a later config change can't silently flip skills back to the
blockless canonical source. setup wipes and rebuilds the render each run,
repoints installed skills after a successful render, and removes a stale
render (re-linking canonical) when gbrain is gone. gbrain-refresh renders
to the out-dir and repoints via relink; its 'this dirties the install's
git tree' caveat is retired because it no longer does.

A one-time upgrade migration (gstack-upgrade/migrations/v1.67.0.0.sh, F12)
restores the legacy dirt: unstaged modifications to SKILL.md / sections/
*.md files in the install checkout are git-checkout'd back to canonical;
anything outside that footprint (user edits, untracked files, staged work)
is left alone and reported. Idempotent, non-fatal, symlinked installs
skipped.

Tests: render-preference behavior for both installers, static pins that
every executable :user invocation carries --out-dir and the caveat text is
gone, migration fixture (restore/leave/idempotent/no-op matrix), and the
existing out-dir render test now asserts 'git status --porcelain' gains
zero new entries across a full :user render.

Fixes #2569

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 09:49:31 -07:00

74 lines
2.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# Migration: v1.67.0.0 — restore tracked files dirtied by the legacy in-place
# gbrain render (#2569, F12).
#
# Why a migration: pre-v1.67, gbrain-enabled setups ran `gen:skill-docs:user
# --host claude` IN PLACE inside the global install checkout
# (~/.claude/skills/gstack), rewriting ~16 TRACKED SKILL.md files. The
# checkout stayed permanently dirty, and every /gstack-upgrade `git stash`
# saved a redundant snapshot of generated content — stashes that invite a
# dangerous `git stash pop` of stale instruction markdown over a newer
# version. v1.67 renders to an untracked out-dir (~/.gstack/render/claude)
# instead, so this migration does the ONE-TIME cleanup of the legacy dirt:
# `git checkout --` on the modified generated files.
#
# Restore scope is deliberately narrow: only UNSTAGED MODIFIED files whose
# path is a SKILL.md or lives under a sections/ directory — the exact
# footprint of the legacy render. Anything else a user changed by hand
# (untracked files, staged work, non-generated edits) is left alone and
# reported. setup's gbrain step re-renders the brain-aware variant into the
# out-dir immediately after migrations run, so no capability is lost.
#
# Affected: global-git installs that ever ran ./setup or `gstack-config
# gbrain-refresh` with gbrain detected, before v1.67.0.0.
#
# Idempotent: a clean checkout is a no-op. Non-fatal throughout.
set -u
INSTALL_DIR="${GSTACK_INSTALL_DIR:-$HOME/.claude/skills/gstack}"
# Only operate on a real (non-symlink) git checkout that looks like gstack.
[ -d "$INSTALL_DIR" ] || exit 0
[ -L "$INSTALL_DIR" ] && exit 0
[ -f "$INSTALL_DIR/VERSION" ] || exit 0
git -C "$INSTALL_DIR" rev-parse --is-inside-work-tree >/dev/null 2>&1 || exit 0
_DIRTY="$(git -C "$INSTALL_DIR" status --porcelain 2>/dev/null || true)"
[ -n "$_DIRTY" ] || exit 0
_RESTORED=0
_LEFT=0
while IFS= read -r _LINE; do
[ -n "$_LINE" ] || continue
_CODE="${_LINE:0:2}"
_PATH="${_LINE:3}"
# Legacy render footprint: unstaged modifications to generated markdown.
case "$_CODE" in
" M")
case "$_PATH" in
SKILL.md|*/SKILL.md|*/sections/*.md)
if git -C "$INSTALL_DIR" checkout -- "$_PATH" 2>/dev/null; then
_RESTORED=$((_RESTORED + 1))
else
_LEFT=$((_LEFT + 1))
fi
;;
*) _LEFT=$((_LEFT + 1)) ;;
esac
;;
*) _LEFT=$((_LEFT + 1)) ;;
esac
done <<EOF_DIRTY
$_DIRTY
EOF_DIRTY
if [ "$_RESTORED" -gt 0 ]; then
echo " v1.67.0.0: restored $_RESTORED tracked file(s) dirtied by the legacy in-place gbrain render (#2569)."
echo " Brain-aware blocks now render to ~/.gstack/render/claude — the checkout stays clean from here on."
fi
if [ "$_LEFT" -gt 0 ]; then
echo " v1.67.0.0: left $_LEFT non-render change(s) in $INSTALL_DIR untouched (not the legacy render footprint)."
fi
exit 0