From 76af52b16e95ff75084c222852a660ffc9984dbb Mon Sep 17 00:00:00 2001 From: Juan Barbat Date: Wed, 28 Oct 2026 16:01:41 -0300 Subject: [PATCH 1/2] fix(gs): let workshop sync update clones with untracked session files Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01A9D7pi162SmxcH6aX85M58 --- bin/gs | 4 +++- tests/gs_test.sh | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/bin/gs b/bin/gs index bc38e3d..c136e10 100755 --- a/bin/gs +++ b/bin/gs @@ -372,7 +372,9 @@ cmd_sync() { printf 'gs sync — %s — workshop (local clones)\n' "$(date '+%Y-%m-%d %H:%M')" for repo in $REPOS; do [ -d "$CLONES/$repo/.git" ] || die "missing clean clone $CLONES/$repo" - [ -z "$(git -C "$CLONES/$repo" status --porcelain)" ] || die "$CLONES/$repo has local changes; clean clones only" + # Untracked files (.engram/, odd/ left by sessions run in the clone) survive + # checkout and pull untouched; only tracked edits would be at risk. + [ -z "$(git -C "$CLONES/$repo" status --porcelain --untracked-files=no)" ] || die "$CLONES/$repo has local changes; clean clones only" git -C "$CLONES/$repo" checkout -q main git -C "$CLONES/$repo" fetch -q --tags origin git -C "$CLONES/$repo" pull -q --ff-only origin main diff --git a/tests/gs_test.sh b/tests/gs_test.sh index 0f1d159..13adec7 100755 --- a/tests/gs_test.sh +++ b/tests/gs_test.sh @@ -104,6 +104,28 @@ OUT=$(cd "$W" && "$GS" 2>&1); L=$(cat "$CALLS" 2>/dev/null) check "consumer: inside a gentle-shell checkout gs still opens the main build" \ '[[ "$L" == "launch $NPM --link" ]]' +# --- sync (workshop) ---------------------------------------------------------- + +setup workshop +# Clones need an origin with main; the path keeps the Gentleman-Programming name. +O="$T/Gentleman-Programming"; mkdir -p "$O" +for r in gentle-shell gentle-ai engram; do + c="$HOME/dev/gentle/$r" + git -C "$c" -c user.email=t@t -c user.name=t commit -q --allow-empty -m init + git -C "$c" branch -M main + git clone -q --bare "$c" "$O/$r.git"; git -C "$c" remote set-url origin "$O/$r.git" + git -C "$c" fetch -q origin +done +git -C "$HOME/dev/gentle/gentle-shell" checkout -q --detach +mkdir -p "$HOME/dev/gentle/gentle-shell/.engram" "$HOME/dev/gentle/gentle-shell/odd/checks" +touch "$HOME/dev/gentle/gentle-shell/.engram/x" "$HOME/dev/gentle/gentle-shell/odd/checks/y" +OUT=$("$GS" sync 2>&1); rc=$? +check "workshop sync: untracked session files and detached HEAD do not block it" \ + '[[ "$OUT" != *"clean clones only"* ]] && [ "$(git -C "$HOME/dev/gentle/gentle-shell" branch --show-current)" = main ] && [ -e "$HOME/dev/gentle/gentle-shell/.engram/x" ]' +echo tracked >> "$HOME/dev/gentle/gentle-shell/VERSION"; git -C "$HOME/dev/gentle/gentle-shell" add VERSION +OUT=$("$GS" sync 2>&1); rc=$? +check "workshop sync: tracked edits in a clone still stop it" '[ $rc -ne 0 ] && [[ "$OUT" == *"clean clones only"* ]]' + # --- sync (consumer) ---------------------------------------------------------- setup consumer; remote_main HEAD; main=$(sha_of) From 44d8821f0fff3f1340b86e8120cfb99a6f614c08 Mon Sep 17 00:00:00 2001 From: Juan Barbat Date: Wed, 28 Oct 2026 16:23:34 -0300 Subject: [PATCH 2/2] test(gs): model a main-channel package in the sync smoke test The smoke test stubbed gentle-shell without a package, so the new launch target check failed, and it still expected exit 0 after a failed gentle-ai sync. Also run tests/gs_test.sh in CI. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01A9D7pi162SmxcH6aX85M58 --- .github/workflows/ci.yml | 3 +++ scripts/test-gs-sync.sh | 14 +++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 41cab9e..74bd563 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -88,6 +88,9 @@ jobs: - name: Check gs sync runs gentle-ai sync run: bash scripts/test-gs-sync.sh + - name: Check gs launches what gs sync updates + run: bash tests/gs_test.sh + - name: Check Nix Home Manager preflight contracts run: bash scripts/test-nix-preflight.sh diff --git a/scripts/test-gs-sync.sh b/scripts/test-gs-sync.sh index f98a219..0a214c8 100644 --- a/scripts/test-gs-sync.sh +++ b/scripts/test-gs-sync.sh @@ -21,7 +21,8 @@ cat >"$shims/_shim" <>"$log" case "\$name \$1" in - "gentle-shell --version") echo "gentle-shell 4.2.0" ;; + "gentle-shell --version") echo "gentle-shell 4.2.0-main.abc1234def56" ;; + node\ *) echo "gentle-shell 4.2.0-main.abc1234def56" ;; "go install") for arg in "\$@"; do case "\$arg" in *cmd/engram@*) printf '#!/bin/sh\necho "engram 3.0.0"\n' >"\$GOBIN/engram"; chmod +x "\$GOBIN/engram" ;; @@ -32,13 +33,13 @@ esac exit 0 EOF chmod +x "$shims/_shim" -for name in gentle-shell pi go home-manager; do ln -s _shim "$shims/$name"; done +for name in gentle-shell pi go home-manager node; do ln -s _shim "$shims/$name"; done ai_dir="$ws/ai"; mkdir -p "$ai_dir"; ln -s "$shims/_shim" "$ai_dir/gentle-ai" printf '{"schema":"gentle-pi.dev-binary/v1","path":"%s"}\n' "$ai_dir/gentle-ai" >"$home/.pi/gentle-ai/dev-binary.json" run_sync() { : >"$log" - env -i HOME="$home" PATH="$shims:/usr/bin:/bin" TERM=dumb "$@" \ + env -i HOME="$home" PATH="$shims:/usr/bin:/bin" TERM=dumb GS_SHELL_REMOTE="$ws/no-remote" "$@" \ bash "$repo_root/bin/gs" sync 2>&1 } ai_sync_calls() { grep '^gentle-ai|sync' "$log" || true; } @@ -66,8 +67,11 @@ out=$(run_sync GS_SKIP_AI_SYNC=1) || fail 'skipped gs sync exited non-zero' "$ou [[ -z "$(ai_sync_calls)" ]] || fail 'gentle-ai sync ran despite GS_SKIP_AI_SYNC=1' "$(cat "$log")" [[ "$out" == *'gentle-ai sync'*'skipped (GS_SKIP_AI_SYNC=1)'* ]] || fail 'missing skip status line' "$out" -# 4. A failing gentle-ai sync is reported with its output, and gs sync goes on. -out=$(run_sync FAKE_AI_SYNC_FAIL=1) || fail 'gs sync aborted on gentle-ai sync failure' "$out" +# 4. A failing gentle-ai sync is reported with its output, gs sync goes on to the +# closing report, and then exits 1 with INCOMPLETE instead of claiming success. +out=$(run_sync FAKE_AI_SYNC_FAIL=1) && fail 'gs sync exited 0 despite a failed gentle-ai sync' "$out" +[[ "$out" == *'INCOMPLETE — failed: gentle-ai sync'* ]] || fail 'failed step not in INCOMPLETE summary' "$out" +[[ "$out" != *'no Pi restart needed'* ]] || fail 'claimed no restart needed after a failure' "$out" [[ "$out" == *'gentle-ai sync'*'FAILED'*'Error: partial sync'* ]] || fail 'failure output not surfaced' "$out" [[ "$out" == *'versions:'* ]] || fail 'sync stopped before the closing report' "$out"