Skip to content

fix: session-start hang, phase derivation between beads, atomic bead claim#73

Merged
mrmaxsteel merged 4 commits intomainfrom
fix/session-start-stdin-hang
Mar 19, 2026
Merged

fix: session-start hang, phase derivation between beads, atomic bead claim#73
mrmaxsteel merged 4 commits intomainfrom
fix/session-start-stdin-hang

Conversation

@mrmaxsteel
Copy link
Owner

@mrmaxsteel mrmaxsteel commented Mar 19, 2026

Summary

Three fixes for issues encountered during multi-bead spec workflows:

  1. Session-start hook hang — hung indefinitely due to io.ReadAll(os.Stdin) blocking before the session-start branch, and instruct triggering 5.5s dolt cold starts before the protected-branch short-circuit
  2. Spurious phase mismatch warning — "stored phase 'implement' disagrees with child-derived phase 'plan'" fired constantly between beads because DerivePhaseFromChildren treated "some closed, some open" as plan instead of implement
  3. Two agents claim same beadClaimBead used bd update --status=in_progress which always succeeded; two concurrent mindspec next invocations could claim the same bead

Changes

Session-start hang (commit 1)

  • internal/hook/hook.goParseInputNonBlocking (ModeCharDevice check + 100ms goroutine timeout)
  • internal/instruct/instruct.goRenderIdleIfProtected shared function (eliminates duplication)
  • internal/state/state.goIsValidPhase (includes "done", unlike IsValidMode)
  • internal/phase/derive.goqueryActiveEpics (open+in_progress only, fewer bd calls); use IsValidPhase
  • cmd/mindspec/hook.go — Pre-warm dolt via bd dolt start, fast-path for protected branches, 12s timeout on slow path
  • cmd/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 returns implement (between beads) instead of plan

Atomic bead claim (commit 3)

  • internal/next/beads.goClaimBead uses bd update --claim (atomic, fails if already claimed) instead of --status=in_progress

Test plan

  • go vet clean on all changed packages
  • go test passes for hook, bead, phase, state, next packages
  • Session-start on main: 0.5s (was: infinite hang)
  • bd dolt status confirms server running after session-start
  • mindspec instruct on feature branch with warm dolt: 0.3s
  • bd update --claim verified: first claim succeeds, second fails with "already claimed"
  • Phase derivation: "some closed, some open" returns implement not plan

🤖 Generated with Claude Code

mrmaxsteel and others added 3 commits March 19, 2026 20:27
…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>
@mrmaxsteel mrmaxsteel changed the title fix(hook): prevent session-start hang from stdin blocking and dolt cold start fix: session-start hang, phase derivation between beads, atomic bead claim Mar 19, 2026
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>
@mrmaxsteel mrmaxsteel merged commit 73f35e4 into main Mar 19, 2026
6 checks passed
@mrmaxsteel mrmaxsteel deleted the fix/session-start-stdin-hang branch March 19, 2026 20:41
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