refactor(capy): the stale-copy owner action moves from the frozen bootstrapper to bin/gstack-capy-session

The checkout-owned full wrapper reads GSTACK_CAPY_MIN_WRAPPER through
gstack_support_env_get(gstack_source_root) and compares it with the
bootstrapper revision it was handed, so the rule can change without
recopying the Drive skill. The bootstrapper keeps only what a stale checkout
cannot do: clone, fetch, read the remote minimum, fast-forward, exec.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-13 01:27:58 +00:00
1 parent 6e10ca3c32
commit 866f994d79
3 files changed
+11 -14

No files matched your search

+8 -1
View File
@@ -9,7 +9,8 @@
# GSTACK_CAPY_LOCK_HELD=1 install mode: .gstack-capy-install.lock is held on an
# inherited descriptor; nothing here takes it again
# Because this file moves with the checkout, session-start behavior can change
# without anyone recopying the Drive skill.
# without anyone recopying the Drive skill; that includes the stale-copy owner
# action, read from capy/support.env through gstack_support_env_get.
#
# Usage: gstack-capy-session [--check] --root <checkout> [gstack-capy-install arguments]
# --check certify only (bin/gstack-capy-install --check: the doctor with the
@@ -26,4 +27,10 @@ if [ ! -x "$INSTALLER" ]; then
exit 1
fi
[ "${GSTACK_CAPY_REMOTE_MIN:-}" = unknown ] && echo "gstack-capy-session: remote minimum unknown (fetch failed); the revision row is local only"
# shellcheck source=bin/gstack-install-verdict.sh
. "$ROOT/bin/gstack-install-verdict.sh"
MIN_WRAPPER=$(gstack_support_env_get "$(gstack_source_root "$ROOT/bin")" GSTACK_CAPY_MIN_WRAPPER)
if [ -n "$MIN_WRAPPER" ] && [ "${GSTACK_CAPY_WRAPPER_REV:-0}" -lt "$MIN_WRAPPER" ] 2>/dev/null; then
echo "gstack-capy-skill: owner action — Drive copy is wrapper ${GSTACK_CAPY_WRAPPER_REV:-0}, minimum $MIN_WRAPPER; copy $ROOT/capy/skill/gstack/ (CAPY_WRAPPER_STALE)"
fi
exec "$INSTALLER" "$@"
+2 -12
View File
@@ -27,7 +27,7 @@ root="${GSTACK_CAPY_ROOT:-$HOME/.local/share/gstack}"
repo="${GSTACK_CAPY_REPO:-https://github.com/garrytan/gstack.git}"
echo "gstack-capy-skill: wrapper $GSTACK_CAPY_WRAPPER ($self)"
check=0 skill="" revision="" min_note="" remote_min="" min_wrapper=""
check=0 skill="" revision="" min_note="" remote_min=""
args=()
while [ $# -gt 0 ]; do
case "$1" in
@@ -80,20 +80,17 @@ if [ ! -f "$root/setup" ]; then
|| { echo "gstack: fail installer — could not clone $repo; fix: check network access, then $fix_cmd"; exit 1; }
fi
remote_min=$(support_get "$(cat "$root/capy/support.env" 2>/dev/null)" GSTACK_MIN_REVISION)
min_wrapper=$(support_get "$(cat "$root/capy/support.env" 2>/dev/null)" GSTACK_CAPY_MIN_WRAPPER)
else
# The remote's rules: fetch the default branch into FETCH_HEAD (never --depth
# into an existing checkout), read capy/support.env from it.
default=$(bounded git -C "$root" ls-remote --symref origin HEAD 2>/dev/null | sed -n 's|^ref: refs/heads/\([^[:space:]]*\)[[:space:]]*HEAD$|\1|p' | head -1)
remote_min="" min_wrapper="" min_note=""
remote_min="" min_note=""
if [ -n "$default" ] && bounded git -C "$root" fetch --quiet origin "$default" 2>/dev/null; then
support=$(git -C "$root" show FETCH_HEAD:capy/support.env 2>/dev/null)
remote_min=$(support_get "$support" GSTACK_MIN_REVISION)
min_wrapper=$(support_get "$support" GSTACK_CAPY_MIN_WRAPPER)
else
min_note="remote minimum unknown (fetch failed)"
echo "gstack-capy-skill: $min_note; checking the local install only"
min_wrapper=$(support_get "$(cat "$root/capy/support.env" 2>/dev/null)" GSTACK_CAPY_MIN_WRAPPER)
fi
installed=$(head -1 "$root/VERSION" 2>/dev/null)
if [ -n "$remote_min" ] && below "${installed:-0}" "$remote_min"; then
@@ -125,13 +122,6 @@ else
fi
session="$root/bin/gstack-capy-session"
if [ -n "$min_wrapper" ] && below "$GSTACK_CAPY_WRAPPER" "$min_wrapper"; then
if [ ! -x "$session" ]; then
echo "gstack: fail wrapper — Capy skill wrapper $GSTACK_CAPY_WRAPPER is older than this checkout's minimum $min_wrapper and $root has no bin/gstack-capy-session; fix: copy $root/capy/skill/gstack/ over the skill folder (CAPY_WRAPPER_STALE)"
exit 1
fi
echo "gstack-capy-skill: owner action — Drive copy is wrapper $GSTACK_CAPY_WRAPPER, minimum $min_wrapper; copy $root/capy/skill/gstack/ (CAPY_WRAPPER_STALE)"
fi
if [ ! -x "$session" ]; then
echo "gstack: fail installer — checkout at $root (installed $(head -1 "$root/VERSION" 2>/dev/null || echo unknown)) predates bin/gstack-capy-session${min_note:+ and the $min_note}; fix: check network access to $(git -C "$root" remote get-url origin 2>/dev/null || echo "$repo"), then $fix_cmd (it fast-forwards a checkout below the supported minimum)"
exit 1
+1 -1
View File
@@ -71,7 +71,7 @@ function fixture(opts: { minWrapper?: number } = {}) {
git(['clone', '-q', '--single-branch', '--depth', '1', `file://${bare}`, root], host.base);
write(path.join(seed, 'VERSION'), '1.0.1\n');
write(path.join(seed, 'capy/support.env'), `GSTACK_MIN_REVISION=1.0.1\nGSTACK_CAPY_MIN_WRAPPER=${opts.minWrapper ?? 1}\n`);
for (const rel of ['capy/skill/gstack/scripts/install.sh', 'capy/skill/gstack/SKILL.md', 'bin/gstack-capy-session']) {
for (const rel of ['capy/skill/gstack/scripts/install.sh', 'capy/skill/gstack/SKILL.md', 'bin/gstack-capy-session', 'bin/gstack-install-verdict.sh']) {
if (fs.existsSync(path.join(REPO, rel))) write(path.join(seed, rel), fs.readFileSync(path.join(REPO, rel), 'utf8'), 0o755);
}
write(path.join(seed, 'bin/gstack-capy-install'), STUB_INSTALLER, 0o755);