ChatGPTClaudeGrok / X / CursorGemini

DeepSeek Harness turn/end blocked 28s by git snapshot

TroubleshootingPublished 2026-10-30Author: DeepSeek Plugin Market
DeepSeek HarnessDSHperformanceturn/endgitsnapshotworkspace-changes
The model finishes in 1.9 seconds but DeepSeek Harness spins 28 more: a serial turn-stopping hook snapshots 120k untracked files. Fix .gitignore.

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.

  1. 0.000s turn/start.
  2. 1.939s assistant/message + step/end — model output complete, this step over.
  3. 30.607s turn/end — the turn formally ends only here.
  4. Nothing is generated during those 28.7 seconds; it is pure teardown waiting.
  5. 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.

  1. In agent.ts, at turn end it sees there is no next step and then await this.dispatch.serial('agent/turn-stopping', ...).
  2. Because it waits serially, the subsequent turn/end write queues behind it.
  3. The hook lands in workspace-changes/src/index.ts, which does await recorders.get(...).stopping(turn).
  4. stopping() then reaches await snapshotTree(...) in recorder.ts.
  5. The snapshot finally runs git add --all --ignore-errors, writing a private index.
  6. 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.

  1. The community measured on Windows 11 + git 2.52.0.windows.1 with a repo of 16,000 tracked files.
  2. Tracked files are fast: git ls-files takes only 0.065 seconds and git status --porcelain 0.11 seconds.
  3. But git ls-files --others --exclude-standard listed 121,182 untracked, non-ignored files.
  4. One temp directory contributed 115,776 of them, about 4.98 GB.
  5. git add --all therefore takes 66.03 seconds, and git write-tree another 1.65.
  6. 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.

OperationBeforeAfterNote
git ls-files0.065s0.065stracked files are fast
git status --porcelain0.11s0.11sstat only, no index change
git ls-files --others121,18267untracked file count
git add --all66.03s0.13swhere the gap comes from
git write-tree1.65s0.04sshrinks with the index
one snapshot total67.7s0.17sstall gone

DeepSeek Harness: Reduction checklist: fix .gitignore first

Adding build outputs, temp directories, and cache directories to .gitignore is the cheapest, highest-return step.

  1. Count current untracked files with git ls-files --others --exclude-standard | wc -l.
  2. Aggregate by directory to find the top contributors: git ls-files --others --exclude-standard | cut -d/ -f1 | sort | uniq -c | sort -rn | head.
  3. Add build temp directories such as .dsh-build-tmp/, download caches, and model weight directories to .gitignore.
  4. Count again and confirm the number is down to the tens.
  5. Time git add --all manually to confirm it dropped from tens of seconds to under one.
  6. 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.

  1. The slow action is hashing every new file, not disk cache hit rate.
  2. A faster disk helps somewhat, but a 4.98 GB temp directory still has to be traversed.
  3. More memory does not change the algorithmic complexity of git add --all.
  4. 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.

  1. In turn-stopping, record only the coordinates needed (HEAD, baseline tree).
  2. Turn snapshotTree into an asynchronous write-back after the turn ends so the UI's running state flips immediately.
  3. Accept the risk: the working tree may change in between, so the snapshot no longer corresponds exactly to "the moment the turn ended".
  4. Another route keeps precision and only makes the wait visible: emit a "generating change summary" status first.
  5. 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.

  1. Do not look only at total duration. Splitting it into step/end and turn/end timestamps makes responsibility obvious.
  2. Do not suspect the model first. No generation happens during this wait.
  3. .gitignore must cover temp directories, not just source build outputs.
  4. Note that --ignore-errors silently skips overlong paths, so the snapshot may be incomplete.
  5. 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.

ChatGPTClaudeGrok / X / CursorGemini

FAQ

The model already finished replying in DeepSeek Harness but the UI keeps spinning for over ten seconds — is the model still running?

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.

Where exactly do those 28 seconds go in DeepSeek Harness?

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.

Why is DeepSeek Harness still slow when there are not many tracked files?

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.

Does adding .gitignore really help in DeepSeek Harness?

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.

Why does a bigger cache or a different machine not solve it in DeepSeek Harness?

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