* fix(capy): reviewer installs leave the lane's launch environment alone; gstack-upgrade counts capy-server leases (GST-395/396/401/402)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(capy-install): restore the header comment prefixes; borrow the lane's launch file only when it reaches a Bun (GST-401)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(capy-install): gate the store-download borrow test to Linux like its siblings
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>
* fix(gstack-lease): take the lease lock with mkdir, not ln -s, so Git Bash on Windows stops waiting out 300 tries (#3330)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(gstack-lease): count lock retries instead of timing them
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(lease): a dead lock that cannot be renamed aside no longer spins lock()
break_lock ignored drop()'s failure and returned 0, so lock() continued past
the tries limit forever when a dead holder's .lock could not be renamed aside
(a Windows file in use). It now removes the lock in place under .lock.break,
and returns 1 when that also fails so the try is counted. touch no longer calls
a dead holder 'live' in its exit-3 message.
Adds the /ship coverage-audit tests for the lock's failure paths.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(lease): pre-landing review fixes
- drop() reports a lock released once it is renamed aside, so a failed
delete of the renamed copy never makes unlock or break_lock rm -rf a new
taker's lock at .lock.
- An ownerless lock is broken only once it is 2 s old, so a live taker whose
claim landed after a breaker read the claims keeps the lock it is about to
publish. An empty owner, an invalid pid or a stray file counts as ownerless.
- touch's prune stays out of the lock (top-level lease files only) and clears
leftover renamed-aside locks; a failed claim write leaves no claim.
- Docs and CHANGELOG state the mixed-version and dead-break-lock limits
instead of claiming exclusion that does not hold.
Tests pin each fix (each fails against the previous bin), make the exclusion
case wait on a release file, prove the parallel case is mutually exclusive,
and check the Git Bash holder keeps its lock.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(lease): second /ship review pass
- orphaned() reads the clock before the lock's mtime and requires the mtime
unchanged at the end, so a lock made meanwhile is never removed; an mtime
a minute or more ahead (clock skew) counts as old.
- Claims older than 2 minutes are abandoned and pruned by touch.
- A waiter skips the claim and mkdir while the lock is visibly held; the
wait is bounded at 300 tries or 30 s; a non-numeric tries value falls back
to 300; ten quick retries (not three) before a lock nobody holds is
reported as untakeable; pid 1 is a valid holder again.
- Tests: deterministic young-lock clock, skew and non-numeric owner shapes,
abandoned claims, the deadline, explicit 30 s timeouts.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* fix(lease): third /ship review pass
- A claim is honored while its claimant lives; only a claim with no start
identity expires after 2 minutes, so a stalled live taker keeps its lock on
hosts that record identities (the remaining case is a documented limit).
- A late taker never overwrites an owner already in its directory, and does
not remove a directory that has one.
- A lock whose owner file exists but cannot be read is never ownerless.
- Zero-padded owner pids count as dead; --help prints the whole usage;
touch prunes only claims whose claimant is gone or recorded no identity.
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* docs(lease): scope 'never by age' to locks that name a holder
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>
* fix(capy): the install trailer says behind=<n> and its fix, so a stale 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>
* fix(capy): a lease records its holder's start identity, so a pid reused 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>
* fix(lease): stale needs proof (unreadable or malformed identities keep 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>
* fix(lease): one directory lock for publish, prune and reclaim; reclaim 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>
* fix(lease): the directory lock names its holder; broken only when the 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>
* fix(lease): breakers serialize on .lock.break and reread the lock before 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>
* docs(lease): name the double-crash limit of breaking a dead lock
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* test(third-party-actions): the Aside drive-offer detector matches the 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>
* docs(changelog): field list 2 F1 entry; capy/support.env GSTACK_MIN_REVISION to this release
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
* chore: stamp v1.91.113.0
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>
The field wave's residual gaps are closed. v1.91.96.0 through v1.91.100.0
shipped the install-honesty and unattended-`/autoplan` wave
(`docs/designs/GSTACK_FIELD_WAVE_2026_10_12.md`); running that release on a
Capy machine left six gaps, each reproduced here with a test that failed
first.