Build Log · July 31, 2026

Concurrent Sessions Get Real Isolation

Fixed gate-evidence collisions, then rejected a homegrown session-lock scheme mid-build in favor of Claude Code's native git-worktree isolation — proven with a real two-worktree end-to-end test, including a genuine merge conflict correctly surfaced.

  • OS
  • Gates
  • Concurrency
  • Worktrees
gates fixed for session identity (Schema, Extraction, Test-Setup, Dependency/Env-Config, Deploy)
5

gates fixed for session identity (Schema, Extraction, Test-Setup, Dependency/Env-Config, Deploy)

hook tests passing (29 new across both halves of the session)
75

hook tests passing (29 new across both halves of the session)

live cross-session commit collisions observed while building the fix
4

live cross-session commit collisions observed while building the fix

two-worktree proof checks run and passed
9

two-worktree proof checks run and passed

Timeline

  1. Start

    Zeb asked why concurrent sessions always end in him forcing through a warning

    Investigated the actual gate-check mechanism rather than answering from impression

  2. 20m

    Found the real bug

    gate-evidence.<slug>.json / .transcripts.json are fixed repo-root paths with no session identity

  3. 60m

    Shipped sessionKey() scoping

    5 new regression tests; my own staged commit got swept into the other session's commit twice while landing this

  4. 90m

    Zeb: 'don't scope it down — the objective is clean concurrent sessions, full stop'

    Reframed from 'ship the narrow gate fix' to 'solve the actual stated objective'

  5. 110m

    Built session-start.mjs/session-finish.mjs (custom worktree launcher + lock)

    A homegrown concurrency protocol on top of git

  6. 120m

    Found Claude Code's native EnterWorktree/ExitWorktree tools mid-build

    Paused before making another edit, per Zeb's explicit instruction, to think it through

  7. 130m

    External review: scrap the custom scripts, use native worktrees, don't ask to choose between them

    Corrected a real factual error in Claude's own reasoning (worktrees DO fix retro.mjs's dirty-tree check) and caught an overstated 'additive migration' code comment

  8. 150m

    Reverted custom scripts, configured worktree.baseRef:head + .worktreeinclude + concurrency.md

    Clean pivot, no preserved scaffolding

  9. 170m

    Ran a real two-worktree proof — all 9 checks passed

    Found and fixed a real worktree bug in checkPushGate (never reapplied from checkCommitGate's earlier fix), and 2 new tool-behavior findings

  10. 190m

    Cleaned up test scaffolding, merged the real fix into master

    The merge-back step itself got swept into the other session's commit — a 4th live collision, now understood as a residual, accepted limitation

What shipped

  • sessionKey() in .claude/hooks/lib/marker.mjs — derives a per-session key from the hook payload's session_id (falling back to transcript_path)

  • markerPath()/transcriptPath() take a sessKey parameter, defaulting to 'unscoped' for backward compatibility

  • gate-dispatch.mjs and gate-transcript-capture.mjs compute and thread the real session key through checkMarker/checkTranscript/appendTranscript

  • checkPushGate fixed to use --git-common-dir instead of --show-toplevel + '.git/hooks' — the same worktree-awareness fix already applied to checkCommitGate, never reapplied to this sibling function; found live by the two-worktree proof itself

  • .claude/settings.json: worktree.baseRef set to 'head' (repo is 200+ commits ahead of origin; the 'fresh' default would silently branch from a stale remote)

  • .worktreeinclude at repo root — copies required .env files into every new EnterWorktree-created worktree

  • .claude/rules/concurrency.md — the full operating rule, what worktrees do/don't isolate, the merge-back procedure, and 2 real tool-behavior findings from the proof

  • 9-point two-worktree end-to-end proof, actually executed (not asserted): local-HEAD basing, independent uncommitted changes, independent staging index, independent commits, retro dirty-tree isolation, hook/gate-evidence resolution, env file availability, and real conflict detection on merge

  • session-start.mjs / session-finish.mjs — built, then fully reverted before ever being committed, once Claude Code's native EnterWorktree/ExitWorktree tools were found mid-session

“I don't understand, reason through it with the crucible please”
— Zeb, redirecting Claude from unilateral technical judgment back to a structured, external second opinion at the exact moment a file-tool routing mystery could have been guessed at instead of verified