fix: session-start hang, phase derivation between beads, atomic bead claim#73
Merged
mrmaxsteel merged 4 commits intomainfrom Mar 19, 2026
Merged
fix: session-start hang, phase derivation between beads, atomic bead claim#73mrmaxsteel merged 4 commits intomainfrom
mrmaxsteel merged 4 commits intomainfrom
Conversation
…ld start Session-start hook hung indefinitely due to two issues: 1. ParseInput called io.ReadAll(os.Stdin) before the session-start branch, blocking forever if the caller didn't close stdin 2. instruct queried beads (via guard.ActiveWorktreePath) before the protected-branch check, triggering 5.5s dolt cold starts per bd call Fixes: - Add ParseInputNonBlocking with ModeCharDevice check + goroutine timeout - Move session-start handling before ParseInput - Extract RenderIdleIfProtected to instruct package (shared by hook + instruct) - Reorder instruct: protected-branch check before guard/worktree (skips beads) - Pre-warm dolt via `bd dolt start` on session-start (300ms, makes subsequent bd calls ~50ms instead of ~5.5s) - Add queryActiveEpics (open+in_progress only) for DiscoverActiveSpecs - Add IsValidPhase to state package (includes "done", unlike IsValidMode) - 12s timeout safety net on slow-path instruct subprocess Result: session-start on main goes from infinite hang to ~0.5s; feature branches with warm dolt complete in ~0.3s. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ead plan DerivePhaseFromChildren returned "plan" when some beads were closed and some open but none in_progress (the gap between completing one bead and claiming the next). This caused a spurious warning: "stored phase 'implement' disagrees with child-derived phase 'plan'". Closed beads prove implementation has started. "plan" should only mean no work has begun (all beads open or no beads created). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… bead ClaimBead used `bd update --status=in_progress` which always succeeded, allowing two concurrent `mindspec next` invocations to claim the same bead. Now uses `bd update --claim` which atomically sets assignee + status and fails if the bead was already claimed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The test stubs in stubActiveEpics returned all epics regardless of --status arg. After DiscoverActiveSpecs switched to queryActiveEpics (open+in_progress only), closed epics leaked through the unfiltered stubs, failing TestMixedRepo_ActiveAndDone and TestActiveSpecs_DeriveFromBeads. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.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.
Summary
Three fixes for issues encountered during multi-bead spec workflows:
io.ReadAll(os.Stdin)blocking before the session-start branch, andinstructtriggering 5.5s dolt cold starts before the protected-branch short-circuitDerivePhaseFromChildrentreated "some closed, some open" asplaninstead ofimplementClaimBeadusedbd update --status=in_progresswhich always succeeded; two concurrentmindspec nextinvocations could claim the same beadChanges
Session-start hang (commit 1)
internal/hook/hook.go—ParseInputNonBlocking(ModeCharDevice check + 100ms goroutine timeout)internal/instruct/instruct.go—RenderIdleIfProtectedshared function (eliminates duplication)internal/state/state.go—IsValidPhase(includes "done", unlikeIsValidMode)internal/phase/derive.go—queryActiveEpics(open+in_progress only, fewer bd calls); useIsValidPhasecmd/mindspec/hook.go— Pre-warm dolt viabd dolt start, fast-path for protected branches, 12s timeout on slow pathcmd/mindspec/instruct.go— Protected-branch check before guard/worktree (avoids beads queries on main)Phase derivation (commit 2)
internal/phase/derive.go— "some closed + some open, none in_progress" now returnsimplement(between beads) instead ofplanAtomic bead claim (commit 3)
internal/next/beads.go—ClaimBeadusesbd update --claim(atomic, fails if already claimed) instead of--status=in_progressTest plan
go vetclean on all changed packagesgo testpasses for hook, bead, phase, state, next packagesbd dolt statusconfirms server running after session-startmindspec instructon feature branch with warm dolt: 0.3sbd update --claimverified: first claim succeeds, second fails with "already claimed"implementnotplan🤖 Generated with Claude Code