Repository navigation
v1.91.113.0 fix(capy): trailers say behind=<n> with a fix, a skipped upgrade is never ok, and a reused pid no longer holds the upgrade lease - #3232
Merged
Conversation
…e install shows in every relayed trailer (field list 2 item 13, #2378) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…ed after a wake is reclaimed; a skipped session-start upgrade ends fail behind, never ok (GBRA-105, GBRA-106) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
|
😎 This pull request was merged. |
Contributor
E2E Evals: ✅ PASS121 executed, 0 reused rule/judge records | 0 manual accepted (unscored; no score-cache credit) | $92.99 rule/judge cost | reconcile exit: 0 Profile: pr / mode Full gate restored by:
Derived dependents (scripts/pr-dependencies.ts):
Rule and judge shards
Fail-closed reconciliationSliced lane: planner → duration-packed executors → fail-closed report. Behavior cases run a pre-registered 3-trial panel (PASS at 2/3 with no contract violation); a PASS 2/3 is shown with its failed trial, never as a clean pass. Reused results retain their original provenance and expiry. |
…p the lease), ps identity is timezone-independent, and reclaim never deletes a concurrent renewal Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…m rereads the record judged stale, so a renewal is never deleted or hidden Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
… holder is dead or its pid reused, unlock removes only its own lock, touch exits 3 under a live holder Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…ore removing it; gstack-skill-start reports a lease the held lock kept out 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>
… product name, not the English idiom (paid run 38988927989 tpa-absent-linux false positive) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
…EVISION to this release 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>
garrytan
added a commit
that referenced
this pull request
Oct 17, 2026
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fast track from field list 2: item 13 (installs fall behind without saying so) plus two S1 reports from the field, GBRA-105 and GBRA-106 (a session-start upgrade wedged by a reused pid and still reported ok).
A stale install now shows in the trailer
Lanes relay only the
gstack:trailer.--checkalready printedgstack-capy-session: behind origin/<branch>: … fix: …, but on its own line, so every handoff lost it. Now, whenever a session ends behind origin's default branch,bin/gstack-capy-sessionexports the gap (GSTACK_CAPY_BEHIND,_BEHIND_TO,_BEHIND_FIX), andbin/gstack-capy-installwrites it into the ok trailer:latest:note rather than appearing next to it.<root>/bin/gstack-upgradefor a dirty, detached, non-default-branch or diverged checkout (it names its own refusal);unset <VAR>, then …when upgrades are switched off. It never contains the;note separator. The session's ownbehind originline now prints the same fix.behind=as "run the fix".A skipped upgrade is never ok
GBRA-106's
install.sh(install mode) printedok 1.91.101.0and exited 0 without upgrading or saying why. Now, when the session-start self-upgrade is skipped (busy, refused, or gstack-upgrade missing), the trailer is:and the exit status is 1, so the install verdict refuses workflows until the fix runs. An explicit opt-out (
GSTACK_CAPY_SELF_UPGRADE=offorGSTACK_CAPY_AUTO_UPGRADE=0) stays ok, with thebehind=note.--checkstill never moves the checkout and stays ok withbehind=.A reused pid no longer holds the lease
GBRA-105's lease named pid 379, which after a machine wake was dbus-daemon. GBRA-106's named pid 357, which had become a 49-second-old systemd-logind. Every upgrade then refused with
UPGRADE_BUSYuntil someone setGSTACK_UPGRADE_FORCE=1.bin/gstack-lease touchnow records the holder's start identity: boot id plus the/procstart time on Linux,ps -o lstartelsewhere. When the recorded identity doesn't match the current process, the lease is stale, andlivereclaims it with agstack-lease: reclaimed stale lease …line. A lease without an identity (written by an older gstack) goes stale after the 120-minute prune window.livenames each holder aspid <p> (<age>m <command>), so UPGRADE_BUSY's message and thefail behindtrailer show who holds the lease.listprintsalive|dead|stale.Lease safety (after review)
A stale verdict needs proof: two well-formed identities from the same backend that differ. An identity that can't be read right now, a malformed record, or a backend change keeps the lease, because a live session is never reclaimed without evidence. The
psfallback readslstartunderLC_ALL=C TZ=UTC0, so it doesn't depend on the caller's timezone or locale.touch(publish by temp file plus rename, prune) and reclaim (reread, then delete only the record judged stale) run under one lock in the lease directory: a symlink.lockwhose target names its holder's pid and start identity, created atomically withln -s. The lock is broken only when its holder is dead or its pid was reused, never by age. Breakers serialize on a second symlink,.lock.break, and reread.lockbefore removing it, andunlockremoves only the caller's own lock. While a live process holds the lock,touchexits 3 without writing (gstack-skill-startreports that the session holds no workflow lease) and a reclaim keeps the lease.Known limit: clearing a dead
.lock.breakis not atomic, so two gstack-lease processes killed in a row (one holding.lock, the next while breaking it) can still let a reclaim delete a renewal. A single crash is always safe. The bin comment and docs/capy-install.md say this. Out of scope here:bin/gstack-upgrade's own window betweengstack-lease liveand the fast-forward. That file is #3225's, and the window already existed before this PR.Tests
--checkbehind (clean, dirty, detached, topic branch, disabled); install mode skipped or disabled; a live holder blocking with UPGRADE_BUSY and the holder named; a reused pid reclaimed so the checkout upgrades; current and offline sessions exporting nothing, inherited values dropped.fail behindconversion with exit 1.liveline now ends in the holder's command (gstack-lease and gstack-upgrade-bin tests).test/gstack-lease.test.ts also covers the review findings with BASH_ENV fault injection: an unreadable
/procandps, malformed and other-backend records, a timezone and locale change, a renewal racing a reclaim, a dead or reused-pid lock holder, a live holder's aged lock (never broken), two breakers of one dead lock, a dead.lock.break, unlock leaving a lock that changed hands, and gstack-skill-start's no-lease report. Mutation checks confirm the lock tests fail when the reread, the ownership check or the liveness check is removed. Focused run on this tree (main 86091dd merged): 377 pass, 0 fail across 12 files. Typecheck is clean. Five review rounds ended at 0 P0, 0 P1, 0 P2, 1 P3 (the known limit above).Gate repair
The paid gate at 641916c failed
tpa-absent-linuxon a detector false positive. A Defer option reading "I set this aside … no browsing now" matched the Aside drive-offer check, which matchedasidecase-insensitively.test/helpers/third-party-actions.tsnow matches the product nameAsideonly. The captured answer is a free regression in test/third-party-actions.test.ts, and a mutation check fails it when the old pattern is restored. At c7a2983 the gate is GREEN: rule 108/108, judge 24/24, $89.82 known.Release note for the stamp
At stamp time,
capy/support.envGSTACK_MIN_REVISION goes up to this PR's own version. This is an install-honesty fix, which is the documented rule, so older checkouts getfail revisionwith afix:line at--checkand are fast-forwarded at install-mode session start.Refs #2378 (its Capy half; the Claude Code update-check half is planned as W12 in the field list 2 wave).