* test(#3886): a timed-out git commit reports commit_timeout, not commit_failed (failing first)
* fix(#3886): git commit timeout reported as commit_timeout; 30s band; stale-lock surfaced
cmdCommit's git commit invocation did not distinguish a spawnSync
timeout from a real non-zero exit (#2608 fixed this for the staging
loop only): a slow pre-commit hook crossing the 10s cap was
SIGTERM'd mid-hook and reported as reason commit_failed with whatever
partial stderr git had flushed (in the reporter's case an incidental
CRLF warning), while the kill left a stale .git/index.lock blocking
the next attempt.
All three commit sites now check isSpawnTimeout before the
nothing-to-commit/ordinary-failure branches: cmdCommit reports
reason commit_timeout + timed_out:true and names the stale lock's
path (surfaced, not auto-deleted — deleting a lock a live git holds
is destructive; the caller recovers deliberately); the subrepo
counterparts do the same within their per-repo result / rollback
error. The commit calls also move to the 30s band the push call
already uses — husky+lint-staged alone idles ~4s on Windows before
any task runs.
* fix(#3886): review fold-ins — git-path lock resolution, shared band constant, executor contract row, precedence pin
- The stale-lock path is resolved via git rev-parse --git-path
index.lock, never a literal .git/index.lock join (#3588 row 8's
class: a linked worktree's .git is a FILE, so the literal path cannot
exist there while the real lock — under <gitdir>/worktrees/<name>/ —
blocks the next commit; this repo leans on linked worktrees).
- COMMIT_TIMEOUT_MS hoisted; all three sites and their messages build
from it (the subrepo variant also regains the stdout fallback the
primary site had).
- agents/gsd-executor.md's commit-result contract gains the
commit_timeout row with the OPPOSITE retry advice from
staging_timeout (remove the stale lock, then retry once) — an
executor matching the doc previously had no handling for the new
reason.
- Precedence pin: a timeout whose partial output contains 'nothing to
commit' must still read as a timeout (branch-reorder mutant).
Emitted-Drift-Ack-Growth: gsd-executor.md — #3886: +commit_timeout row to the commit-result contract with the retry guidance OPPOSITE staging_timeout's (remove the stale lock, then retry once); the executor previously had no handling for the new reason.
* chore(#3886): changeset fragment (pr number backfilled after PR creation)
* chore(#3886): backfill changeset PR number (4046)
---------
Co-authored-by: sim <sim@local>