Skip to content

fix(gs): launch the gentle-shell that gs sync updates - #79

Merged
barbatdev merged 2 commits into
mainfrom
fix/gs-launch-sync
Oct 28, 2026
Merged

barbatdev merged 2 commits into
mainfrom
fix/gs-launch-sync

Conversation

@barbatdev

Copy link
Copy Markdown
Owner

Problem

gs sync upgraded one gentle-shell install while gs launched another. Consumer gs started the legacy ~/.pi/agent/git/.../gentle-pi checkout that nothing updates any more, so new profiles and rendering never showed up. On agent-gentle, workshop sync stopped on untracked session files in the clone (.engram/, odd/).

Changes

  • Launch and sync share shell_root():
    • Standard workstations: always the main-channel build behind gentle-shell on PATH.
    • Workshop (agent-gentle): inside a gentle-shell clone or worktree, Pi loads that path (like gs run); elsewhere, the synced clone.
  • gs sync prints gs opens:, checks the build against GitHub main, and exits 1 with INCOMPLETE when a step fails or the build is stale.
  • Workshop sync ignores untracked files in clones; tracked edits still stop it.
  • tests/gs_test.sh (offline, 15 cases) and docs/gs-sync.md.

Verification

  • tests/gs_test.sh: all pass. The launcher and sync cases fail on the old bin/gs.
  • Deployed to agent-gentle, agent-dev and agent-refactoria. gs --version matches main, and on agent-gentle a worktree loads itself.

🤖 Generated with Claude Code

https://claude.ai/code/session_01A9D7pi162SmxcH6aX85M58

barbatdev and others added 2 commits October 28, 2026 16:01
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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A9D7pi162SmxcH6aX85M58
@barbatdev

Copy link
Copy Markdown
Owner Author

Fleet review (glm)

Lens: requirements + adversarial. The effective diff is two commits: (1) workshop sync now ignores untracked files when checking clone cleanliness (--untracked-files=no), and (2) test/CI updates — test-gs-sync.sh models a main-channel package (version carries a main SHA, node stub answers for the launch-target probe) and asserts exit 1 + INCOMPLETE after a failed step, tests/gs_test.sh adds two workshop sync cases (untracked session files + detached HEAD pass; staged tracked edits still block), and CI runs tests/gs_test.sh. I verified the larger behavior described in the PR body (shell_root(), 'gs opens:', freshness check, INCOMPLETE exit) already exists on main; this PR only adds the untracked-files change and the test coverage for it, plus fixes the smoke test that the launch-target check on main had broken. The test stubs are consistent with the head implementation: GS_SHELL_REMOTE points at a nonexistent path so the ls-remote freshness check degrades to a WARN (offline-safe), and the node stub's version string matches gentle-shell --version so the launch-target comparison passes. The new git fixture work is correct (setup workshop creates an origin remote, so remote set-url is valid; --allow-empty guarantees a commit before branch -M). No blocking defects found: the cleanliness change cannot silently lose work — git still refuses checkout/pull when an untracked file collides with an incoming tracked path, and staged new files are still shown by status --porcelain --untracked-files=no so tracked-edit protection is preserved.

Suggestion (1)

  • New comment overstates the guarantee: colliding untracked paths can still block sync (bin/gs:376): The added comment says untracked files 'survive checkout and pull untouched; only tracked edits would be at risk.' That is not fully true: if an untracked file in a clone sits at a path that an incoming commit on origin/main makes tracked, git checkout -q main or git pull -q --ff-only still refuses with 'untracked working tree files would be overwritten', so gs sync dies with a raw git error every run until the file is removed manually. The behavior fails safely (no data loss, sync reports failure), so this is not blocking — but the comment and the PR's stated goal ('untracked session files survive checkout and pull untouched') promise more than the code delivers, and the resulting git error gives the operator no hint that removing the untracked file fixes it.

Question (1)

  • docs/gs-sync.md is listed as a change but is not in the diff (PR body): The PR body's Changes section lists 'docs/gs-sync.md' among the deliverables, but the diff contains only .github/workflows/ci.yml, bin/gs, scripts/test-gs-sync.sh and tests/gs_test.sh. The body also describes the shell_root()/gs opens/INCOMPLETE behavior as if introduced here, when it already exists on main (this PR's base contains it) — only the untracked-files fix and the test coverage are new.

Verdict: ready to merge

@barbatdev

Copy link
Copy Markdown
Owner Author

Fleet review (glmflash)

Lens: adversarial

Reviewed head 44d8821 (PR #79). The net diff against main is small: the workshop clean-clone check in bin/gs now ignores untracked files (git status --porcelain --untracked-files=no), a CI step running tests/gs_test.sh, and test updates. Everything else the PR body claims (shared shell_root(), gs opens: line, GitHub-main freshness check, INCOMPLETE exit) already exists on main, so the branch's residual diff is what merges. Attacked the changed line's failure paths: untracked/tracked conflicts at pull time, detached HEAD checkout, partial-sync mid-loop aborts, and the offline claim of the new test cases (the test's setup() exports GS_SHELL_REMOTE to a local fake remote, and the freshness check only runs in consumer mode, so the workshop cases are genuinely offline). No blocking defects found: the relaxation matches the stated intent, tracked edits and staged files still stop sync, and the detached-HEAD + untracked case is covered. Two non-blocking suggestions on the collision edge case and a test-assertion gap, plus one question about the PR body describing code already on main.

Suggestion (2)

  • Untracked file colliding with an upstream path makes pull fail ungracefully (bin/gs:377): The relaxation lets untracked files survive, but if an untracked file sits at a path that upstream main adds as a tracked file, git pull -q --ff-only (line 380) fails with 'untracked working tree files would be overwritten by merge'. Under set -euo pipefail, cmd_sync aborts mid-loop with a raw git error: earlier repos already updated, later repos and the build steps never run, and the run ends without the INCOMPLETE summary. The new comment 'only tracked edits would be at risk' overstates the safety. Not blocking because the outcome is a visible, recoverable error rather than silent corruption, and any other git failure at that point already behaves the same way.
  • First workshop sync test captures rc but never asserts success (tests/gs_test.sh:118): The new case runs OUT=$("$GS" sync 2>&1); rc=$? and then checks only that the output lacks 'clean clones only', that the clone landed on main, and that the untracked file survived. rc is unused in this check, so a regression that makes workshop sync exit 1 for any reason after the clone step (launch-target mismatch, build step failure, INCOMPLETE) would still pass this test.

Question (1)

  • PR body describes changes that are already on main (.github/PULL_REQUEST_TEMPLATE or PR body): The Changes section lists the launch/sync shell_root() unification, the gs opens: output, the GitHub-main staleness check, and the INCOMPLETE exit as changes of this PR, but all of that is already present on main at the merge base; the actual diff of head 44d8821 is only the untracked-files relaxation in bin/gs, the CI step, and the test updates. Can the author confirm the feature landed via another merged branch, so the body does not misdescribe what this PR merges?

Verdict: ready to merge

@barbatdev

Copy link
Copy Markdown
Owner Author

Fleet review (deepseek)

Lens: evidence + test design. The single behaviour this PR changes in bin/gs — git status --porcelain --untracked-files=no in the workshop clone loop (bin/gs:377) — is correct: untracked .engram/ and odd/ files no longer stop the sync, tracked edits still do, and git never deletes untracked files on checkout/pull, so the comment at bin/gs:375-376 holds. The aligned test-gs-sync.sh change (main-channel shim version + node * case + GS_SHELL_REMOTE, exit 1 with INCOMPLETE — failed: gentle-ai sync) matches what bin/gs:399-404 and bin/gs:488-498 actually do. The added tests/gs_test.sh workshop case does discriminate the new guard from the old one, but its evidence stops at the guard: the fixture cannot complete a workshop sync and the case asserts nothing about the exit status, so a green here does not show that gs sync finishes with untracked session files present. Remaining findings are non-blocking: the newly reachable checkout/pull collision path, the inconsistent handling of the Mac app clones, and a PR body that describes the launcher unification already merged on main instead of this 4-file diff. Note: the PR message claims tests/gs_test.sh and docs/gs-sync.md as changes; both already exist on main.

Suggestion (3)

  • Workshop sync case captures rc but never asserts it, and the fixture cannot let the sync finish (tests/gs_test.sh:122): OUT=$("$GS" sync 2>&1); rc=$? at line 122 records the exit status and nothing reads it (the script runs set -uo pipefail, no -e). The check at 123-124 only asserts that clean clones only is absent, that branch --show-current is main (which git checkout -q main at bin/gs:378 sets before the pull), and that .engram/x still exists (git never removes untracked files, so that assertion is close to vacuous). Meanwhile the fake workshop clones built at lines 112-121 contain no cmd/, no engram/plugin/pi, and no VERSION/bin tracked content, so the workshop branch of cmd_sync reaches (cd "$CLONES/engram/plugin/pi" && npm install ...) at bin/gs:386, the cd fails, and set -euo pipefail (bin/gs:4) ends the sync with exit 1 before gs opens: and the INCOMPLETE report. So this case is green for a run that aborted. To evidence the PR's own claim ("workshop sync ignores untracked files in clones"), assert rc -eq 0 plus an expected report line, and create the directories the workshop branch installs into so the sync can reach its closing report. Without that, the case cannot distinguish "untracked files do not block" from "the sync failed for another reason".
  • Untracked files now abort checkout/pull with a raw git error instead of the clean clones only guard (bin/gs:378): Allowing untracked files through the guard (bin/gs:377) makes a new failure path reachable: git checkout -q main and git pull -q --ff-only origin main (bin/gs:378-380) both refuse when an untracked file in the clone would be overwritten by a tracked path arriving from main. That is not hypothetical in these repos — sessions run inside the clone already leave odd/ and .engram/ untracked (the PR body says so), and odd/tasks/*.md is exactly the kind of file that also gets committed upstream. The sync still fails loudly (set -euo pipefail, exit 1), so this is not a correctness hole, but the clear clean clones only diagnostic is replaced by a raw git message with no gs: prefix and no closing report. Consider catching that case and naming the colliding path, or mention it in docs/gs-sync.md as the one remaining case where untracked files block a workshop sync.
  • PR body and verification describe the launcher unification, not this diff (bin/gs): The diff is 4 files, +37/-6, and the only bin/gs hunk is the untracked-files flag. Everything the body lists under "Changes" — launch and sync sharing shell_root(), gs sync printing gs opens:, checking the build against GitHub main, exiting 1 with INCOMPLETE, tests/gs_test.sh (15 cases) and docs/gs-sync.md — is already on main at this head (verified in bin/gs at 44d8821: lines 264-275, 478-498, 523-531) and is not part of this PR. Without a linked issue, the body is the only statement of acceptance criteria, so as written it cannot be checked against the diff. What this PR actually does is finish that feature: the two commits align scripts/test-gs-sync.sh with the merged INCOMPLETE/main-channel behaviour (the old step asserted exit 0 on a failed gentle-ai sync and a plain 4.2.0 shim, both of which the merged bin/gs rejects) and relax the workshop guard. Please rewrite "Changes" and "Verification" around those two commits so the merge record matches the code, and keep the offline-test claim limited to what the new workshop case actually proves (see the tests/gs_test.sh finding).

Question (1)

  • Mac app clones still count untracked files, unlike the workshop clones this PR relaxes (bin/gs:419): The MAC_APP_REPOS loop keeps the full git status --porcelain (bin/gs:419) while the workshop loop now passes --untracked-files=no. The rationale in the comment at bin/gs:375-376 (sessions leave .engram/ and odd/ behind; untracked files survive checkout and pull untouched) applies verbatim to ~/dev/gentle/gentle-dot and ~/dev/gentle/gentle-shell-desktop on a workstation. Is the difference intentional (app repos are never opened by an agent session) or just untouched by this change? If intentional, a one-line comment there would keep the two rules from looking contradictory.

Verdict: ready to merge

@barbatdev
barbatdev merged commit ada9387 into main Oct 28, 2026
1 check passed
@barbatdev
barbatdev deleted the fix/gs-launch-sync branch October 28, 2026 19:43
@barbatdev

Copy link
Copy Markdown
Owner Author

Post-merge QA (glm)

The merged change itself is small and correct: workshop sync now ignores untracked files (git status --untracked-files=no) so session artifacts like .engram/ and odd/ no longer stop clone updates, while tracked edits still block, with offline tests covering both sides. However, the PR body describes a much larger change — sharing shell_root() between launch and sync, the 'gs opens:' version check against GitHub main, the INCOMPLETE exit — and none of that is in this PR's merge diff. I verified that all of it does exist on main's bin/gs and docs/gs-sync.md, landed by commit 5298997 which is an ancestor of this merge, not part of PR #79. So the end state is coherent and the tests match reality, but the PR description overstates what this PR contributed.

Suggestion (1)

  • Untracked file colliding with a new upstream tracked file now fails later, at pull (bin/gs:375): Ignoring untracked files moves the failure for a path collision (clone has untracked odd/README.md and upstream adds a tracked odd/README.md) from the explicit 'clean clones only' check to the git pull --ff-only step, which dies with raw git output instead of the summary. Rare edge case; a pre-pull check for untracked paths that would be overwritten would keep the failure message consistent.

Question (1)

  • PR body claims the launcher fix, but the merge diff does not contain it (bin/gs): The body lists 'Launch and sync share shell_root()' as this PR's core change, yet the merged diff for bin/gs only changes the workshop untracked-files check. The launcher behavior was already on main via commit 5298997 before this merge. If that commit reached main without going through a reviewed PR, the launcher fix (the riskier half: launch target selection, staleness exit) was never PR-reviewed. Worth confirming how 5298997 landed; the description should also be corrected so the merge record matches what the merge actually introduced.

Verdict: clean

@barbatdev

Copy link
Copy Markdown
Owner Author

Post-merge QA (deepseek)

Lens: evidence + test design. The single behaviour this PR changes in bin/gs — git status --porcelain --untracked-files=no in the workshop clone loop (bin/gs:377) — is correct: untracked .engram/ and odd/ files no longer stop the sync, tracked edits still do, and git never deletes untracked files on checkout/pull, so the comment at bin/gs:375-376 holds. The aligned test-gs-sync.sh change (main-channel shim version + node * case + GS_SHELL_REMOTE, exit 1 with INCOMPLETE — failed: gentle-ai sync) matches what bin/gs:399-404 and bin/gs:488-498 actually do. The added tests/gs_test.sh workshop case does discriminate the new guard from the old one, but its evidence stops at the guard: the fixture cannot complete a workshop sync and the case asserts nothing about the exit status, so a green here does not show that gs sync finishes with untracked session files present. Remaining findings are non-blocking: the newly reachable checkout/pull collision path, the inconsistent handling of the Mac app clones, and a PR body that describes the launcher unification already merged on main instead of this 4-file diff. Note: the PR message claims tests/gs_test.sh and docs/gs-sync.md as changes; both already exist on main.

Suggestion (3)

  • Workshop sync case captures rc but never asserts it, and the fixture cannot let the sync finish (tests/gs_test.sh:122): OUT=$("$GS" sync 2>&1); rc=$? at line 122 records the exit status and nothing reads it (the script runs set -uo pipefail, no -e). The check at 123-124 only asserts that clean clones only is absent, that branch --show-current is main (which git checkout -q main at bin/gs:378 sets before the pull), and that .engram/x still exists (git never removes untracked files, so that assertion is close to vacuous). Meanwhile the fake workshop clones built at lines 112-121 contain no cmd/, no engram/plugin/pi, and no VERSION/bin tracked content, so the workshop branch of cmd_sync reaches (cd "$CLONES/engram/plugin/pi" && npm install ...) at bin/gs:386, the cd fails, and set -euo pipefail (bin/gs:4) ends the sync with exit 1 before gs opens: and the INCOMPLETE report. So this case is green for a run that aborted. To evidence the PR's own claim ("workshop sync ignores untracked files in clones"), assert rc -eq 0 plus an expected report line, and create the directories the workshop branch installs into so the sync can reach its closing report. Without that, the case cannot distinguish "untracked files do not block" from "the sync failed for another reason".
  • Untracked files now abort checkout/pull with a raw git error instead of the clean clones only guard (bin/gs:378): Allowing untracked files through the guard (bin/gs:377) makes a new failure path reachable: git checkout -q main and git pull -q --ff-only origin main (bin/gs:378-380) both refuse when an untracked file in the clone would be overwritten by a tracked path arriving from main. That is not hypothetical in these repos — sessions run inside the clone already leave odd/ and .engram/ untracked (the PR body says so), and odd/tasks/*.md is exactly the kind of file that also gets committed upstream. The sync still fails loudly (set -euo pipefail, exit 1), so this is not a correctness hole, but the clear clean clones only diagnostic is replaced by a raw git message with no gs: prefix and no closing report. Consider catching that case and naming the colliding path, or mention it in docs/gs-sync.md as the one remaining case where untracked files block a workshop sync.
  • PR body and verification describe the launcher unification, not this diff (bin/gs): The diff is 4 files, +37/-6, and the only bin/gs hunk is the untracked-files flag. Everything the body lists under "Changes" — launch and sync sharing shell_root(), gs sync printing gs opens:, checking the build against GitHub main, exiting 1 with INCOMPLETE, tests/gs_test.sh (15 cases) and docs/gs-sync.md — is already on main at this head (verified in bin/gs at 44d8821: lines 264-275, 478-498, 523-531) and is not part of this PR. Without a linked issue, the body is the only statement of acceptance criteria, so as written it cannot be checked against the diff. What this PR actually does is finish that feature: the two commits align scripts/test-gs-sync.sh with the merged INCOMPLETE/main-channel behaviour (the old step asserted exit 0 on a failed gentle-ai sync and a plain 4.2.0 shim, both of which the merged bin/gs rejects) and relax the workshop guard. Please rewrite "Changes" and "Verification" around those two commits so the merge record matches the code, and keep the offline-test claim limited to what the new workshop case actually proves (see the tests/gs_test.sh finding).

Question (1)

  • Mac app clones still count untracked files, unlike the workshop clones this PR relaxes (bin/gs:419): The MAC_APP_REPOS loop keeps the full git status --porcelain (bin/gs:419) while the workshop loop now passes --untracked-files=no. The rationale in the comment at bin/gs:375-376 (sessions leave .engram/ and odd/ behind; untracked files survive checkout and pull untouched) applies verbatim to ~/dev/gentle/gentle-dot and ~/dev/gentle/gentle-shell-desktop on a workstation. Is the difference intentional (app repos are never opened by an agent session) or just untouched by this change? If intentional, a one-line comment there would keep the two rules from looking contradictory.

Verdict: clean

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant