mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-14 08:59:01 +02:00
restore-deps: 'cp -r SRC node_modules' with an existing node_modules NESTS the copy and leaves stale deps active — rm first. register-gstack-skills: 'ln -snf' hard-errors under set -eu when a REAL directory occupies the gstack slot — clear a non-symlink leftover first. CI workspaces are fresh today; a reusable composite must survive dirty reruns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
106 lines
5.1 KiB
YAML
106 lines
5.1 KiB
YAML
name: Register gstack skills for PTY tests
|
|
description: >
|
|
Register the skills PTY smokes invoke, in every place claude looks:
|
|
user-scoped ($HOME/.claude/skills — a gstack root symlink for the
|
|
preamble's absolute runtime paths, plus per-skill REAL-FILE copies because
|
|
claude's interactive-TUI skill scanner does not follow the
|
|
/github/home -> /__w cross-mount symlink), and project-scoped
|
|
(<repo>/.claude/skills, which the TUI reads for /slash commands and which
|
|
is gitignored so absent on a fresh CI checkout). Also pre-seeds every
|
|
one-time preamble marker so no PTY child takes a first-run branch mid-test
|
|
(feature discovery under ~/.claude/skills/gstack trips Claude Code's
|
|
sensitive-file permission prompt — the documented intermittent
|
|
scope-gate-question-NOT-observed failure).
|
|
|
|
Ends with the fail-fast verification loop the legacy matrix copy grew
|
|
after a silent "Unknown command" + 35-min-timeout incident: a dangling
|
|
symlink or renamed committed target fails HERE, in seconds, with a named
|
|
path — never as a wedged PTY session at the shard wall. Every consuming
|
|
lane inherits the loop by construction (it previously existed only in the
|
|
matrix copy; the sliced + periodic copies had silently dropped it).
|
|
inputs:
|
|
skills:
|
|
description: Space-separated skill dirs (each must have SKILL.md + sections/).
|
|
required: false
|
|
default: office-hours plan-ceo-review plan-eng-review plan-design-review
|
|
runs:
|
|
using: composite
|
|
steps:
|
|
- shell: bash
|
|
env:
|
|
SKILLS: ${{ inputs.skills }}
|
|
run: |
|
|
set -eu
|
|
SKILLS_DIR="$HOME/.claude/skills"
|
|
REPO="$GITHUB_WORKSPACE"
|
|
mkdir -p "$SKILLS_DIR"
|
|
# The gstack root stays a symlink — the preamble's runtime bash resolves
|
|
# ~/.claude/skills/gstack/bin/* and ~/.claude/skills/gstack/<skill>/sections/*
|
|
# through it, and bash follows cross-mount symlinks fine.
|
|
# `ln -snf` replaces a stale SYMLINK but hard-errors on a REAL
|
|
# directory under set -eu — clear a real-dir leftover first so the
|
|
# composite is rerun-safe (codex diff review). CI HOMEs are fresh;
|
|
# this only fires on a dirty rerun.
|
|
if [ -d "$SKILLS_DIR/gstack" ] && [ ! -L "$SKILLS_DIR/gstack" ]; then
|
|
rm -rf "${SKILLS_DIR:?}/gstack"
|
|
fi
|
|
ln -snf "$REPO" "$SKILLS_DIR/gstack"
|
|
for s in $SKILLS; do
|
|
rm -rf "${SKILLS_DIR:?}/$s"
|
|
mkdir -p "$SKILLS_DIR/$s"
|
|
cp "$REPO/$s/SKILL.md" "$SKILLS_DIR/$s/SKILL.md"
|
|
cp -R "$REPO/$s/sections" "$SKILLS_DIR/$s/sections"
|
|
done
|
|
PROJ_SKILLS="$REPO/.claude/skills"
|
|
mkdir -p "$PROJ_SKILLS"
|
|
for s in $SKILLS; do
|
|
rm -rf "${PROJ_SKILLS:?}/$s"
|
|
mkdir -p "$PROJ_SKILLS/$s"
|
|
cp "$REPO/$s/SKILL.md" "$PROJ_SKILLS/$s/SKILL.md"
|
|
cp -R "$REPO/$s/sections" "$PROJ_SKILLS/$s/sections"
|
|
done
|
|
# Pre-seed every ONE-TIME preamble marker so no PTY child ever takes a
|
|
# first-run branch mid-test.
|
|
mkdir -p "$HOME/.gstack"
|
|
touch "$HOME/.gstack/.activated" \
|
|
"$HOME/.gstack/.first-loop-tip-shown" \
|
|
"$HOME/.gstack/.telemetry-prompted" \
|
|
"$HOME/.gstack/.proactive-prompted" \
|
|
"$HOME/.gstack/.completeness-intro-seen" \
|
|
"$HOME/.gstack/.plan-tune-nudge-shown"
|
|
# These two resolve through the gstack root symlink into $REPO —
|
|
# untracked scratch in the CI checkout, exactly where the preamble looks.
|
|
touch "$SKILLS_DIR/gstack/.feature-prompted-continuous-checkpoint" \
|
|
"$SKILLS_DIR/gstack/.feature-prompted-model-overlay"
|
|
echo "--- registry under $SKILLS_DIR ---"
|
|
ls -la "$SKILLS_DIR/gstack"
|
|
# ── Fail-fast verification ──────────────────────────────────────────
|
|
# A dangling symlink or moved/renamed committed target must fail here
|
|
# with a named path, not resurface as a silent "Unknown command" and a
|
|
# PTY session wedged to its wall timeout.
|
|
for f in \
|
|
"$SKILLS_DIR/gstack/bin/gstack-update-check" \
|
|
"$SKILLS_DIR/gstack/scripts/gen-skill-docs.ts"; do
|
|
if [ ! -e "$f" ]; then
|
|
echo "ERROR: gstack root symlink dangles or target moved: $f" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
for s in $SKILLS; do
|
|
if [ ! -e "$SKILLS_DIR/$s/SKILL.md" ]; then
|
|
echo "ERROR: skill-registry target missing: $SKILLS_DIR/$s/SKILL.md" >&2
|
|
exit 1
|
|
fi
|
|
grep -m1 "^name: $s\$" "$SKILLS_DIR/$s/SKILL.md" >/dev/null \
|
|
|| { echo "ERROR: $s SKILL.md missing 'name: $s' frontmatter" >&2; exit 1; }
|
|
# Sections must exist BOTH as the copied real files (TUI reads) and
|
|
# through the gstack root symlink (the preamble's runtime paths).
|
|
for d in "$SKILLS_DIR/$s/sections" "$SKILLS_DIR/gstack/$s/sections"; do
|
|
if [ ! -d "$d" ] || [ -z "$(ls -A "$d")" ]; then
|
|
echo "ERROR: skill sections missing or empty: $d" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
done
|
|
echo "skill registry OK"
|