mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
* feat(capy-install): project floor, --for none, WARN for behind and Node-only pins, in-place review-voice upgrade, /cso env hygiene (harness lane F)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* cso: tighten the env-hygiene and tooling-error paragraphs so the controller template stays under the 18,000-byte compactness bound
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* capy skill: name the fail heads once and describe the WARN behind outcome consistently
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* doctor: name the floor field PROJECT_FLOOR_FIELD (it is a JSON field name, not a key)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: pre-landing review fixes
Red team (/ship Step 9): a Drive copy at a bootstrapper revision below GSTACK_CAPY_MIN_WRAPPER is a WARN with the owner action in a direct (no-provenance) run too; only a pre-bootstrapper copy fails. A --for none session never self-upgrades. Stale fail-behind comment in gstack-capy-session; SKILL.md says ok not-needed is not an install verdict. Tests: UPGRADE_SKIPPED collapse, silent in-place message, direct-run WARN, wrapper-2 --for none handover.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: adversarial review fixes
Trailer fix after a skipped upgrade is one runnable command (the refusal prose stays on the WARN behind row); the floor reader is whitespace-insensitive and names a non-version value instead of calling it absent. Found by /ship Step 11 (Claude adversarial, Codex adversarial + structured).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix: review pass 2 fixes (floor reader bounds, cso start is resumed not retried)
The floor reader reads only the first 64 KiB, takes the first occurrence of the field (the top-level one) and names a non-version string truthfully; /cso resumes a start whose ID never printed instead of retrying it (a run may already exist). Found by /ship pass 2 (Claude adversarial, Codex adversarial + structured).
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: the floor evaluation marker lives in the fixture, not /tmp
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test: take main's host-conformance C3 fix (#3479) so the remote gate can run on this tree
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* RESTAMP: 1.91.257.0 -> 1.91.258.0 (stamped; queue_mode=stamp-at-merge base=origin/main@7f53ddb1f3cc policy=base origin/main@7f53ddb1f3cc:.gstack/ship-policy.json)
ship, next in queue at 1.91.258.0
STAMP_VERSION: 1.91.258.0
claims: ignored (stamp-at-merge)
stage version: done (wrote 1.91.258.0)
stage release_tool: done (bun scripts/gen-agents-digest.ts)
stage changelog: done (Unreleased -> 1.91.258.0)
stage stamp_paths: skipped (no stamp_paths in policy)
stage mirror: skipped (no mirror in policy)
stage apply: done (4 file(s))
FILES: CHANGELOG.md VERSION agents-digest/gstack-AGENTS.md package.json
MERGE_CONDITION: sha=831ac3c3cb3077f16b2f1a7802297523a2db1349
next: NEW_VERSION=1.91.258.0; commit and push only the stamp diff above (FILES), with no edit of your own: the train's verify accepts the CHANGELOG only as the base's plus this PR's entry re-headed; merge with the expected head 831ac3c3cb and recompute on a stale queue.
GSTACK_RESULT: skill=restamp status=complete run=/workspace/gstack/.gstack/tmp/restamp-journal.json
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>
127 lines
8.3 KiB
Bash
Executable File
127 lines
8.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# gstack-capy-session — the full Capy skill wrapper: the checkout-owned half
|
|
# of a Capy session start. The frozen bootstrapper the Drive skill holds
|
|
# (capy/skill/gstack/scripts/install.sh) clones or fast-forwards this checkout
|
|
# and execs this script with its own arguments plus:
|
|
# GSTACK_CAPY_WRAPPER_REV / GSTACK_CAPY_WRAPPER_PATH the bootstrapper that ran
|
|
# GSTACK_CAPY_REMOTE_MIN the supported minimum from origin's capy/support.env, or `unknown`
|
|
# GSTACK_CAPY_REMOTE_VERSION origin's VERSION read in this run (wrapper 2+; the
|
|
# installer's `latest: <ver> available` trailer note)
|
|
# GSTACK_CAPY_UPGRADED_FROM the version it fast-forwarded from (setup is never skipped then)
|
|
# GSTACK_CAPY_CLONED the repo it just cloned (wrapper 2+; the installer's `cloned` line)
|
|
# 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; that includes the stale-copy owner
|
|
# action, read from capy/support.env through gstack_support_env_get.
|
|
#
|
|
# Always current when online: the bootstrapper moves a checkout only when it
|
|
# is below GSTACK_MIN_REVISION (the floor). Above the floor, this half keeps
|
|
# the checkout at the tip of origin's default branch: in install mode, when
|
|
# the bootstrapper's FETCH_HEAD is ahead of HEAD, it runs
|
|
# bin/gstack-upgrade --ff-only-no-reinstall (every refusal of gstack-upgrade
|
|
# applies: dirty, diverged, pinned, Bun hold, a live workflow lease) and on
|
|
# success re-execs the NEW checkout's copy of this script with the same
|
|
# arguments (GSTACK_CAPY_SELF_UPGRADED=1 stops a second attempt) before the
|
|
# installer runs. Lines: `SELF_UPGRADE: done <old> -> <new>` | `skipped (<reason>)`
|
|
# | `none (current)` | `disabled (<the variable: GSTACK_CAPY_SELF_UPGRADE=off or
|
|
# GSTACK_CAPY_AUTO_UPGRADE=0>)`.
|
|
# --check never moves the checkout; behind origin it prints
|
|
# `gstack-capy-session: behind origin/<branch>: <installed> -> <remote> (<n> commits); fix: ...`.
|
|
# Whenever the session ends behind origin (--check, or a self-upgrade that was
|
|
# skipped or disabled) it exports GSTACK_CAPY_BEHIND=<n>, GSTACK_CAPY_BEHIND_TO=<remote
|
|
# VERSION> and GSTACK_CAPY_BEHIND_FIX=<command> for the installer's trailer notes
|
|
# `behind=<n> (latest: <remote>); fix: <command>`. The command never contains
|
|
# "; " (the note separator): after a refused self-upgrade it is bin/gstack-upgrade,
|
|
# which prints its own refusal and fix. A skipped self-upgrade (refused, busy,
|
|
# or gstack-upgrade missing) also exports GSTACK_CAPY_BEHIND_BLOCKED=<reason; fix: …
|
|
# (CODE)>, and the installer adds a `WARN behind` row with that reason and code:
|
|
# the trailer stays ok (the installed revision is above the supported minimum),
|
|
# and the note names the fix. A `--for none` session certifies nothing and moves
|
|
# nothing, so it never self-upgrades (it behaves like --check).
|
|
#
|
|
# Usage: gstack-capy-session [--check] --root <checkout> [gstack-capy-install arguments]
|
|
# --check certify only (bin/gstack-capy-install --check: the doctor with the
|
|
# launch PATH and the remote minimum, the verdict, the launch line)
|
|
# default install or repair (bin/gstack-capy-install)
|
|
# The last line is the `gstack:` trailer; the exit status is the installer's.
|
|
set -u
|
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
|
CHECK=0 SKILL=""
|
|
prev=""
|
|
for a in "$@"; do
|
|
[ "$prev" = --root ] && ROOT="$a"
|
|
case "$prev" in --for|--only) SKILL="$a" ;; esac
|
|
[ "$a" = --check ] && CHECK=1
|
|
prev="$a"
|
|
done
|
|
[ "$SKILL" = none ] && CHECK=1
|
|
INSTALLER="$ROOT/bin/gstack-capy-install"
|
|
if [ ! -x "$INSTALLER" ]; then
|
|
echo "gstack: fail installer — $INSTALLER is missing; fix: bash ${GSTACK_CAPY_WRAPPER_PATH:-<skill>/scripts/install.sh} (re-clones or fast-forwards)"
|
|
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
|
|
|
|
# Behind origin? Only when the bootstrapper fetched (a known remote minimum
|
|
# means FETCH_HEAD is origin's default branch tip) and that tip is not already
|
|
# in HEAD.
|
|
behind() {
|
|
[ -n "${GSTACK_CAPY_REMOTE_MIN:-}" ] && [ "$GSTACK_CAPY_REMOTE_MIN" != unknown ] || return 1
|
|
git -C "$ROOT" rev-parse -q --verify FETCH_HEAD >/dev/null 2>&1 || return 1
|
|
! git -C "$ROOT" merge-base --is-ancestor FETCH_HEAD HEAD 2>/dev/null
|
|
}
|
|
disabled() { echo "SELF_UPGRADE: disabled ($1)"; behind && report_behind "unset $1, then $RERUN"; } # VAR — the switch that turned the fast-forward off
|
|
# report_behind FIX — hand the installer the gap and the command that closes
|
|
# it, for the trailer's `behind=<n>` note (lanes relay only the trailer).
|
|
report_behind() {
|
|
export GSTACK_CAPY_BEHIND="$(git -C "$ROOT" rev-list --count HEAD..FETCH_HEAD 2>/dev/null || echo '?')" \
|
|
GSTACK_CAPY_BEHIND_TO="$(git -C "$ROOT" show FETCH_HEAD:VERSION 2>/dev/null | head -1)" GSTACK_CAPY_BEHIND_FIX="$1"
|
|
}
|
|
unset GSTACK_CAPY_BEHIND GSTACK_CAPY_BEHIND_TO GSTACK_CAPY_BEHIND_FIX GSTACK_CAPY_BEHIND_BLOCKED
|
|
RERUN="bash ${GSTACK_CAPY_WRAPPER_PATH:-<skill>/scripts/install.sh}${SKILL:+ --for $SKILL}"
|
|
if [ "$CHECK" -eq 1 ]; then
|
|
if behind; then
|
|
# The rerun fast-forwards only what gstack-upgrade would move; otherwise
|
|
# gstack-upgrade itself names the refusal and its fix.
|
|
if [ "${GSTACK_CAPY_SELF_UPGRADE:-}" = off ]; then report_behind "unset GSTACK_CAPY_SELF_UPGRADE, then $RERUN"
|
|
elif [ "${GSTACK_CAPY_AUTO_UPGRADE:-1}" = 0 ]; then report_behind "unset GSTACK_CAPY_AUTO_UPGRADE, then $RERUN"
|
|
elif [ "$(git -C "$ROOT" symbolic-ref -q --short HEAD 2>/dev/null)" != "$(sed -n "1s/.*branch '\([^']*\)' of .*/\1/p" "$(git -C "$ROOT" rev-parse --absolute-git-dir 2>/dev/null)/FETCH_HEAD" 2>/dev/null)" ] \
|
|
|| [ -n "$(git -C "$ROOT" status --porcelain --untracked-files=no 2>/dev/null)" ] \
|
|
|| ! git -C "$ROOT" merge-base --is-ancestor HEAD FETCH_HEAD 2>/dev/null; then report_behind "$ROOT/bin/gstack-upgrade"
|
|
else report_behind "$RERUN"
|
|
fi
|
|
BRANCH=$(git -C "$ROOT" symbolic-ref -q --short HEAD 2>/dev/null || echo HEAD)
|
|
echo "gstack-capy-session: behind origin/$BRANCH: $(head -1 "$ROOT/VERSION" 2>/dev/null) -> $GSTACK_CAPY_BEHIND_TO ($GSTACK_CAPY_BEHIND commits); fix: $GSTACK_CAPY_BEHIND_FIX$([ "$GSTACK_CAPY_BEHIND_FIX" = "$RERUN" ] && echo ' (fast-forwards)')"
|
|
fi
|
|
elif [ "${GSTACK_CAPY_SELF_UPGRADED:-}" != 1 ]; then
|
|
if [ "${GSTACK_CAPY_SELF_UPGRADE:-}" = off ]; then disabled GSTACK_CAPY_SELF_UPGRADE
|
|
elif [ "${GSTACK_CAPY_AUTO_UPGRADE:-1}" = 0 ]; then disabled GSTACK_CAPY_AUTO_UPGRADE
|
|
elif [ -z "${GSTACK_CAPY_REMOTE_MIN:-}" ] || [ "$GSTACK_CAPY_REMOTE_MIN" = unknown ]; then echo "SELF_UPGRADE: none (remote minimum unknown)"
|
|
elif ! behind; then echo "SELF_UPGRADE: none (current)"
|
|
elif [ ! -x "$ROOT/bin/gstack-upgrade" ]; then echo "SELF_UPGRADE: skipped ($ROOT/bin/gstack-upgrade is missing)"; report_behind "$RERUN"
|
|
export GSTACK_CAPY_BEHIND_BLOCKED="$ROOT/bin/gstack-upgrade is missing; fix: $RERUN (UPGRADE_REFUSED)"
|
|
else
|
|
OLD=$(head -1 "$ROOT/VERSION" 2>/dev/null)
|
|
UP_OUT=$("$ROOT/bin/gstack-upgrade" --ff-only-no-reinstall 2>&1); UP_RC=$?
|
|
[ -n "$UP_OUT" ] && printf '%s\n' "$UP_OUT"
|
|
if [ "$UP_RC" -eq 0 ]; then
|
|
echo "SELF_UPGRADE: done $OLD -> $(head -1 "$ROOT/VERSION" 2>/dev/null)"
|
|
export GSTACK_CAPY_UPGRADED_FROM="$OLD" GSTACK_CAPY_SELF_UPGRADED=1
|
|
exec "$ROOT/bin/gstack-capy-session" "$@"
|
|
fi
|
|
REASON=$(printf '%s\n' "$UP_OUT" | sed -n 's/^gstack-upgrade: \(.*\); nothing was changed; fix: /\1; fix: /p' | tail -1)
|
|
[ -n "$REASON" ] || REASON=$(printf '%s\n' "$UP_OUT" | sed -n 's/^gstack-upgrade: //p' | tail -1)
|
|
echo "SELF_UPGRADE: skipped (${REASON:-gstack-upgrade exit $UP_RC})"
|
|
report_behind "$ROOT/bin/gstack-upgrade"
|
|
export GSTACK_CAPY_BEHIND_BLOCKED="${REASON:-gstack-upgrade exit $UP_RC; fix: $ROOT/bin/gstack-upgrade (UPGRADE_REFUSED)}"
|
|
fi
|
|
fi
|
|
exec "$INSTALLER" "$@"
|