Cutting the delay between "Enter" and the agent starting
KISS Sorcar — task-start latency optimization, validated end to end
with screenshots and database timings. Development model:
claude-fable-5; independent read-only review:
gpt-5.6-sol.
The problem
When a user submits a task, Sorcar performs several git and setup
operations before the agent's first step. Measured on the production
database, the gap between submission (start_ts) and the agent's
first event was 1.5–1.7 seconds on every task, and
8.2 seconds for the first task after a daemon restart.
Where the time went (measured, not guessed)
cProfile of a real end-to-end run plus targeted git timings on this repository:
| Step on the submit path | Cost |
|---|---|
git worktree add (full checkout of the task's isolated worktree) | 0.86–1.0 s |
First git reset --hard in a fresh worktree (index re-hash of every file) | 0.94 s (if used naively) |
Orphan-worktree reclaim + sweep maintenance, git status | ~0.2 s |
Lazy model-SDK imports on the first task (KISSAgent._reset) | 0.3–6 s (cold) |
The fix
- Spare worktree pool
(
src/kiss/agents/sorcar/worktree_pool.py): while a task runs, a background thread pre-creates the next task's worktree and warms its index. At submit time the agent consumes the ready spare and only pays for a cheapgit reset --hardonto the current branch tip, an untracked-file sweep, and the dirty-state copy. The orphan reclaim/sweep maintenance moved into the background refill too. If the pool is empty or the spare fails any validation, the code falls back to the exact pre-existing inline path. - Startup import prewarm
(
_prewarm_task_dependenciesinsrc/kiss/server/server.py): a daemon thread imports the lazily loaded model-SDK modules and warms the model registry at server startup, so the first task no longer pays for them.
Submit path: before vs after
Measured results (real server, real UI, real model)
An isolated RemoteAccessServer built from this change was driven
through its real web UI (Playwright); timings below are from the server's own
SQLite event log (submission timestamp vs the first agent event), model
claude-fable-5:
| Scenario | Before | After |
|---|---|---|
| Steady state (task N>1 in a repo) | 1.5–1.7 s | 0.17–0.33 s |
| First task in a repo / after restart | 1.6–8.2 s | ~2 s |
Screenshot evidence
Safety: independent review and hardening
A read-only review by gpt-5.6-sol (instructed to report only
substantiated problems) found 1 high, 2 medium and 2 low issues — all fixed
and covered by new tests:
- High: a spare orphaned by a daemon crash could be
squash-merged into whatever branch the user later checked out. Spares now
carry a durable
kiss-sparegit-config marker; reclaim discards marked spares and never merges them (the marker is cleared the moment a task consumes the spare). - Medium: a spare whose checkout was switched externally
could route a task onto the wrong branch — consumption now verifies the
recorded branch is actually checked out. And untracked files dropped into an
idle spare could have been committed as task output — consumption now runs
git clean -fdq. - Low:
discard_all()now joins in-flight refill threads; the test suite's pool-disable env gate is set unconditionally.
Verification
- 27 new end-to-end pool tests (real git repositories, no mocks), all passing.
- ~5,500 impacted existing tests (agents/sorcar, server, vscode, core) run in 8 parallel splits: all pass (one unrelated live-API network flake passed on retry).
uv run check --full(ruff, mypy, pyright, compileall): all pass.
The pool leaves one idle kiss/wt-* branch per
repository between tasks. It is contentless, marked, excluded from all
maintenance while the daemon owns it, and automatically discarded by the next
reclaim pass if the daemon dies. Background refills can be disabled with
KISS_DISABLE_WORKTREE_POOL=1.