What happens between pressing Enter in the chat box and the agent's first model call, measured on the daemon's own history database and logs, and what was changed on 16 September 2026.
The history database (~/.kiss/sorcar.db) stamps every task with the moment the daemon received it (start_ts) and records every event with a timestamp. For the last 30 chat tasks, the gap between the submit stamp and the first recorded event was 2.2 to 6 seconds (median 3.7 s). Tasks started by the cron agent, which runs with the pre-run classifier switched off, showed a gap of 0.02 s. That difference points straight at the classifier.
The daemon log (kiss-web-stderr.log) shows the sequence for one typical task on claude-fable-5:
06:31:21.527 run command received
06:31:21.989 POST /v1/messages 400 Bad Request <- classifier attempt 1
"thinking.type.disabled" is not supported for this model
06:31:22.973 POST /v1/messages 200 OK <- classifier attempt 2
06:31:24.103 Task classified: is_simple=True is_development=False
06:31:24.144 Step 1/10000 start <- agent's first model call
Three things fell out of the measurements:
claude-fable-5, claude-opus-5, Opus 4.6 and later) to disable thinking. They reject that with HTTP 400, so every task paid 0.3–0.7 s for a request that could never succeed, then ran the fallback.output_format) added about 280 input tokens and 1.0–1.4 s of latency on the live API (2.1 s plain vs 3.5 s with schema on fable-5; 0.64 s vs 1.6 s on haiku-4-5). The prompt already demands a bare JSON object and the parser tolerates fences and prose.git worktree add on this repository takes 1.5 s. A spare-worktree pool exists, but it only refilled after a worktree task had consumed a spare, so the first development task of every daemon session (and any task after a spare vanished) ran the checkout inline, after the classifier had finished.claude-fable-5, drawn to scale from the measured components (classifier 2.1 s, wasted 400 attempt 0.46 s, git worktree add 1.5 s, spare consumption 0.3 s).src/kiss/agents/sorcar/task_classifier.py)Anthropic models now get one plain request: no schema grammar and no attempt to disable thinking. The Anthropic adapter already chooses the correct thinking mode per model, and with the classifier's 1000-token output cap a fixed-budget model gets no thinking budget at all. Gemini and OpenAI-compatible providers keep their structured-output request and plain fallback unchanged.
Verdicts are stored in ~/.kiss/task_classifier_cache.json, keyed by a SHA-256 of the classifier prompt, the model name, the complete model configuration (endpoint, credentials, headers, all hashed, never stored) and the task text. A memo is reused for at most seven days, the file holds at most 2000 entries, writes are atomic and merge whatever another process wrote in the meantime. In the last 40 chat prompts in the history database, 12 were repeats of an earlier prompt.
src/kiss/server/task_runner.py)Just before the classifier is consulted, the task runner asks the worktree pool to prepare a spare on a background thread, but only when all of the following hold: the client has worktrees switched on, the working directory is a git repository that is not itself a Sorcar worktree, and a classifier round trip is actually about to happen (enabled, not a cc/ or codex/ model, no memo). The preparation skips the pool's orphan-reclaim pass, so it never merges anything into the user's branch; it only adds a checkout under .kiss-worktrees/. If the verdict says "development", the run resets that spare onto the branch tip in about 60–300 ms instead of paying 1.5 s. If not, the spare stays pooled for the next development task.
Measured end to end through the real VSCodeServer._run_task path with the real claude-fable-5 model and a scratch repository holding this project's 4,900 tracked files. "Agent starts" is the moment the main agent begins its first model call. The "before" rows replay the previous classifier request shape with the pool refilled only after acquisition and no memo, exactly as the daemon behaved before the change.
| Scenario | Before | After | Speed-up |
|---|---|---|---|
| Development task, first time this session | 6.49 s | 3.26 s | 2.0× |
| Development task, prompt seen before | 4.35 s | 0.07 s | 62× |
| Simple task, first time | 4.30 s | 3.76 s | 1.1× |
| Simple task, prompt seen before | 3.82 s | 0.008 s | >400× |
The Anthropic API was noticeably slower during the benchmark run than in the daemon logs from earlier in the day (classifier 2.9–4.3 s instead of 2.1 s), which is why the "first time" rows show the classifier's own latency rather than a fixed constant. Whole-task time for a trivial "reply done" prompt went from 8.3 s to 4.4 s on a repeated prompt; the remainder is the model writing its answer.
For a prompt the daemon has never seen, the launch is now bounded by a single classifier call on the task's own model: about 2 s on claude-fable-5, 0.6 s on claude-haiku-4-5. Every request shape was tried on the live API (non-streaming, effort: low, small max_tokens, system-prompt placement, forced tool use); the plain request is the fastest. Classifying with a faster sibling model would remove the floor but was rejected on the data: on 40 real prompts from the history database, claude-haiku-4-5 agreed with claude-fable-5 on the worktree decision only 28 times (70%). Users who do not want the floor can switch off "Classify tasks before running" in the settings; launch then takes about 0.1 s plus a pooled worktree reset.
src/kiss/tests/agents/sorcar/test_task_classifier.py (33 offline against a local OpenAI-compatible stand-in, 15 live including a claude-fable-5 single-attempt check) and 9 new end-to-end tests in src/kiss/tests/server/test_launch_prewarms_worktree_pool.py through a real server, agent, git repository and model endpoint. No mocks.except.test_gemini_conversation_handoff.py::TestGeminiToAnthropicLive, which fails on an anthropic-workspace-id header rejection).uv run check --full (compileall, ruff, mypy, pyright): clean.gpt-5.6-sol checked lock ordering, main-tree safety of the background checkout, the Anthropic adapter's behaviour with the new request, existing tests, and the cache. Their findings (cache key ignored credentials and headers; multi-process writes could clobber memos; a spare could appear for users with worktrees off; nested spares for sub-agent runs; two type-check errors in a test) were all fixed before the final test run.