DeepSeek Harness turn/end blocked 28s by git snapshot
In a simple "hello" exchange the model finishes in 1.9 seconds, yet the UI keeps spinning until 30.6 seconds — the extra 28.7 seconds is neither the model nor the network but the serial hook that closes the turn. The hook takes a full git workspace snapshot, and the workspace holds 120,000 untracked files that .gitignore does not cover (#10601). Break it into three stages.
DeepSeek Harness: Before: the model had already finished
Two timestamps in the log draw the responsibility line clearly: the content landed at 1.939 seconds.
0.000s turn/start.1.939s assistant/message + step/end— model output complete, this step over.30.607s turn/end— the turn formally ends only here.- Nothing is generated during those 28.7 seconds; it is pure teardown waiting.
- That is where "the UI is still spinning although the answer is long done" comes from.
DSH plugin: When: the serial hook blocks turn/end
agent/turn-stopping is dispatched as serial and awaited, so turn/end is always written after it.
- In
agent.ts, at turn end it sees there is no next step and thenawait this.dispatch.serial('agent/turn-stopping', ...). - Because it waits serially, the subsequent turn/end write queues behind it.
- The hook lands in
workspace-changes/src/index.ts, which doesawait recorders.get(...).stopping(turn). stopping()then reachesawait snapshotTree(...)inrecorder.ts.- The snapshot finally runs
git add --all --ignore-errors, writing a private index. - No step in that chain is asynchronous in the background, so the duration is added directly to user wait time.
DeepSeek Harness: After: the cost is untracked files, not tracked files
git add --all computes a blob hash for every untracked file and writes it to the index; 120,000 files means 120,000 computations.
- The community measured on Windows 11 + git 2.52.0.windows.1 with a repo of 16,000 tracked files.
- Tracked files are fast:
git ls-filestakes only 0.065 seconds andgit status --porcelain0.11 seconds. - But
git ls-files --others --exclude-standardlisted 121,182 untracked, non-ignored files. - One temp directory contributed 115,776 of them, about 4.98 GB.
git add --alltherefore takes 66.03 seconds, andgit write-treeanother 1.65.- A single snapshot totals 67.7 seconds — that explains the tens of seconds of stall you see.
DSH Plugin: One table showing where it is slow
The same set of commands before and after .gitignore, with the entire difference in the untracked-files row.
| Operation | Before | After | Note |
|---|---|---|---|
git ls-files | 0.065s | 0.065s | tracked files are fast |
git status --porcelain | 0.11s | 0.11s | stat only, no index change |
git ls-files --others | 121,182 | 67 | untracked file count |
git add --all | 66.03s | 0.13s | where the gap comes from |
git write-tree | 1.65s | 0.04s | shrinks with the index |
| one snapshot total | 67.7s | 0.17s | stall gone |
DeepSeek Harness: Reduction checklist: fix .gitignore first
Adding build outputs, temp directories, and cache directories to .gitignore is the cheapest, highest-return step.
- Count current untracked files with
git ls-files --others --exclude-standard | wc -l. - Aggregate by directory to find the top contributors:
git ls-files --others --exclude-standard | cut -d/ -f1 | sort | uniq -c | sort -rn | head. - Add build temp directories such as
.dsh-build-tmp/, download caches, and model weight directories to.gitignore. - Count again and confirm the number is down to the tens.
- Time
git add --allmanually to confirm it dropped from tens of seconds to under one. - Send a message in DSH again and watch whether turn/end follows step/end closely.
This is the step the community used to bring a snapshot from 67.7 seconds to 0.17.
DSH plugin: Why "more cache" or "a faster disk" does not solve it
The cost scales linearly with file count; without reducing the count, all you can do is wait.
- The slow action is hashing every new file, not disk cache hit rate.
- A faster disk helps somewhat, but a 4.98 GB temp directory still has to be traversed.
- More memory does not change the algorithmic complexity of
git add --all. - Conclusion: reduce the file count first, then talk about hardware.
Fix direction: move the snapshot off the blocking path
The plugin already listens on turn/end, which shows the "record after the turn ends" path already exists.
- In
turn-stopping, record only the coordinates needed (HEAD, baseline tree). - Turn
snapshotTreeinto an asynchronous write-back after the turn ends so the UI's running state flips immediately. - Accept the risk: the working tree may change in between, so the snapshot no longer corresponds exactly to "the moment the turn ended".
- Another route keeps precision and only makes the wait visible: emit a "generating change summary" status first.
- The latter does not change the duration; it just stops it from looking like the model is still thinking.
Cautions
Reduce the file count before buying faster hardware.
- Do not look only at total duration. Splitting it into step/end and turn/end timestamps makes responsibility obvious.
- Do not suspect the model first. No generation happens during this wait.
.gitignoremust cover temp directories, not just source build outputs.- Note that
--ignore-errorssilently skips overlong paths, so the snapshot may be incomplete. - In long sessions this is a recurring cost, paid every turn, and worth fixing once.
If your turn/end delay is only a second or two, it may just be normal teardown overhead; tens of seconds means the workspace holds a large number of untracked files.See DeepSeek Harness slow startup: causes and fixes for dsh for the related case.
FAQ
No, the model is not still running: the DeepSeek Harness session log writes assistant/message and step/end at 1.939 seconds, while turn/end lands at 30.607 seconds; the 28.7 seconds in between is all spent in the turn-stopping hook.
Those seconds go into a full git workspace snapshot in DeepSeek Harness. agent/turn-stopping is a serial, awaited hook, and workspace-changes calls snapshotTree() inside it, which runs git add --all and therefore hashes every untracked file as a blob.
DeepSeek Harness is still slow because the slow part is not tracked files. The community measured a repo with only 16,000 tracked files but 120,000 untracked files not covered by .gitignore, one temp directory of which accounted for 115,000 files and 4.98 GB.
Yes, ignoring those directories helps in DeepSeek Harness: after the community added the relevant directories to .gitignore, untracked files dropped from 121,182 to 67 and a single snapshot went from 67.7 seconds to 0.17, with the stall disappearing.
A bigger cache or a different machine does not help because the DeepSeek Harness snapshot cost is computing a blob hash and writing an index entry for every untracked file — a workload problem, not a cache hit problem. Without reducing the file count, one snapshot takes that long.
Related Terms
- agent/turn-stopping
- agent/turn-stopping is a serial hook dispatched before a turn ends and must finish before turn/end is written. Any work synchronously awaited inside it directly lengthens the whole turn teardown.— https://github.com/deepseek-ai/deepseek-harness/discussions/10601
- snapshotTree
- snapshotTree is the workspace-changes function that records a workspace snapshot, generating a full tree state through git add and write-tree. Its duration correlates directly with the number of workspace files.— https://github.com/deepseek-ai/deepseek-harness/discussions/10601
- git add --all
- git add --all is the git command that stages every change in the workspace including untracked files. It computes a blob hash for each new file, so the number of untracked files is the main cost driver.— https://github.com/deepseek-ai/deepseek-harness/discussions/10601
- turn/end
- turn/end is the session event marking that a turn has formally ended, and the frontend closes the running state on it. While it is blocked, the UI keeps showing the model as working.— https://github.com/deepseek-ai/deepseek-harness/discussions/10601
Sources
- #10601 — workspace-changes: git snapshot in agent/turn-stopping can block turn/end· deepseek-ai (GitHub Discussions)
- deepseek-ai/deepseek-harness (source repository)· GitHub