Making chat tasks launch faster in KISS Sorcar

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.

1. What "launch" costs today

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:

0 s1 s2 s 3 s4 s5 s6 s Before agent starts at 4.4 s 400 (wasted) classifier LLM call git worktree add After agent starts at 2.4 s spare worktree prepared in the background while the classifier waits After, repeat verdict from cache, pooled spare: agent starts at 0.07 s
Launch of a development task on 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).

2. What was changed

Classifier request shape (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.

Verdict memo

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.

Checkout overlapped with the classifier (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.

3. Results

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.

ScenarioBeforeAfterSpeed-up
Development task, first time this session6.49 s3.26 s2.0×
Development task, prompt seen before4.35 s0.07 s62×
Simple task, first time4.30 s3.76 s1.1×
Simple task, prompt seen before3.82 s0.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.

4. The limit that remains

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.

5. Verification