perf(opencode): overlap the pre-turn snapshot with the provider request
Issue for this PR
Issues are disabled on this fork; motivation below. Stacked on #26; re-target after it merges.
Type of change
- Refactor / code improvement
What does this PR do?
SessionProcessor.create awaited snapshot.track() ā a chain of git subprocesses (init/add/write-tree) over the worktree ā before the provider stream could start, putting the whole git cost inside time-to-first-token on every step. The original eager await existed because the AI SDK can execute tools before emitting step events, so the snapshot must exist before any tool mutates files ā a data-ordering constraint, not a "before the provider request" constraint.
Now the track forks (Effect.forkIn(scope)) and the stream starts immediately; an idempotent awaitSnapshot on the processor handle is the barrier, taken at exactly the consumption points: the shared tool execute wrapper (before any tool runs ā the case the original comment warned about) and step-start. The provider's TTFT absorbs the git work. snapshot.track is internally mutex-locked, so the forked fiber serializes safely against the step-finish re-track; orDie keeps a git failure failing the turn as before, and forkIn(scope) ties the fiber to the turn's scope so interruption still cancels it. snapshot_ms (#25) now reports the true concurrent git cost rather than serial prep.
How did you verify your code works?
snapshot-tool-race.test.ts (the purpose-built test for this exact barrier) passes, plus test/snapshot 55/55, and prompt.test.ts + compaction.test.ts + retry.test.ts 145/145. bun run typecheck clean, bun run lint 0 errors. Fleet-wide effect visible post-release as prep_ms dropping while snapshot_ms holds steady.
Checklist
- I have tested my changes locally
- I have not included unrelated changes in this PR
š¤ Generated with Claude Code
Summary by cubic
Overlaps the pre-turn snapshot with the provider request to reduce time-to-first-token while preserving tool ordering. Previously we awaited the snapshot before starting the provider stream; now we track the snapshot concurrently and gate tool execution and step-start behind an idempotent awaitSnapshot.
- Adds
awaitSnapshottoSessionProcessor.Handle; tracks the snapshot in a forked fiber bound to the turn scope, preserving failure and cancellation semantics. - Gates tool execution via
SessionTools.resolveandstep-startonawaitSnapshot, ensuring tools cannot mutate files before the snapshot lands. - Maintains snapshot serialization via the internal mutex;
snapshot_msmeasures the concurrent git cost, andprep_msshould drop accordingly.
Review notes
- Focus on concurrency boundaries:
forkIn(scope),Fiber.join, and the cached result on the handle. - If tests construct fake processors, provide
awaitSnapshot(e.g.,Effect.succeed(undefined)).