Skip to content

fix: close 16 defects from adversarial bug-hunt sweep - #128

Merged
jkyberneees merged 8 commits into
mainfrom
fix/bug-hunt-wave-127
Sep 18, 2026
Merged

jkyberneees merged 8 commits into
mainfrom
fix/bug-hunt-wave-127

Conversation

@jkyberneees

Copy link
Copy Markdown
Contributor

Four bug-hunt rounds, 16 validated defects fixed RED-first (each with a regression test):

  • client: readLoop goroutine leak on reconnect into a full Events channel
  • server: spawn-failure error formatting (%!w, nil deref); frozen diagnostics tail
  • workspace: foreign /new resurrected on cross-instance save
  • tui: bg_job kick dropped in batches; bell-latch desync; stale queued hint; reconnect double-dial; jobs watcher double-arm; runCtxCum leak on error; orphan turn from usage straggler
  • upgrade: Windows swap rollback; slow-link download timeout; missing fsync
  • update: pseudo-version compare

Adversarial review passed (2 reviewers, no blockers/majors). Race suite 9/9 pkgs, vet clean, lint 0 issues.

A reconnect during a delta firehose abandons the old client's Events
channel with no reader; when the buffer was full, readLoop parked
forever on its next send, leaking a goroutine and an MBs-scale buffer
per drop. Close now closes a done channel and every Events send
selects on it, so a closed client's readLoop always exits.
waitSpawned rendered %!w(<nil>) when a server died or timed out with no
stderr tail, and dereferenced a nil scanner on the top-of-loop token
check. The diagnostics tail also froze at the startup banner once the
token was found, so a server that failed after printing it showed
irrelevant output. The tail now follows all post-banner output (with
partial-line buffering), and empty tails are omitted from the error.
reloadLocked merged only foreign additions, so a stale in-memory entry
for a directory this instance was NOT saving republished dead drafts
and session ids over a fresher on-disk state — e.g. a /new cleared by
another bodek instance resurrected on this instance's next Save of any
other cwd. The on-disk store now wins for every cwd except the one the
caller is writing.
The bg_job handler returned a per-event fetch cmd, which ingestWireBatch
discards — a push frame arriving with any other frame lost its immediate
jobs snapshot and waited for the next watcher tick. The kick now rides
the flushKicks flag like the agents/memory refreshes, coalescing into
one fetch per burst.
- approvalSendErrMsg now restores the popped head's BEL latch with it,
  keeping apprBells 1:1 with apprDeadlines; the urgent window previously
  latched the wrong request for the rest of the session.
- a mid-queue approval expiry repaints the form: the 'N queued' hint
  stayed stale up to the head's TTL when the head itself survived.
- scheduleReconnect stamps each chain with a generation; a manual retry
  supersedes a pending backoff tick instead of racing it into two
  concurrent hook dials. Superseded outcomes drop (closing any socket).
- upgrade: the Windows aside-swap now rolls the old binary back when the
  install rename fails, instead of stranding the executable as .old.
- upgrade: release-asset downloads use a per-request 10m deadline instead
  of inheriting the API client's overall 10s timeout, which cut every
  multi-MB transfer over a slow link.
- upgrade: the new binary is fsynced before the rename, honoring the
  'never leaves a truncated binary' contract on crash.
- update: Go pseudo-version stamps (v0.1.3-0.2026...) compare by their
  release prefix, so commit-installed builds still see newer releases.
- tui: reopening the jobs tab invalidates the in-flight tab tick, which
  used to pass the stale seq check and arm a second watcher chain.
- the rollback-fail path now tags its error (tmpKeptError) so the
  deferred temp cleanup keeps the staged new binary instead of deleting
  it — both halves survive for recovery.
- pseudo-version detection anchors on '-0.' + a 14-digit timestamp so a
  genuine semver prerelease (1.2.0-0.1) is not misparsed.
- slow-link test margins widened (300ms vs 2s) for CI stability.
- the error close-out now resets runCtxCum like done, so a failed run's
  pre-v2.3 cumulative cannot under-report the next run's ctx gauge.
- a usage frame trailing a finalized turn no longer opens an orphan
  turn card (busy wedge): usage is telemetry — thinking/token frames
  keep the lazy-open fallback, so nothing drops.
- regression tests for both, plus the stale-wakeArmed mid-turn case.
@jkyberneees
jkyberneees merged commit 17d36f1 into main Sep 18, 2026
9 checks passed
@jkyberneees
jkyberneees deleted the fix/bug-hunt-wave-127 branch September 18, 2026 15:07
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