What version of eigent are you using?
Desktop 1.0.6 (Windows installer Eigent.Setup.1.0.6.exe); backend source == release tag v1.0.6 (verified byte-identical for every file cited below)
System information
- OS: Windows 11 (10.0.26300) x64
- Space: single-agent session, Git enabled, checkout_mode = primary_checkout (the User Worktree is checked out directly)
- Workspace: an existing git repository on branch
master, ~2.8k untracked paths at the time of the incident (mostly vendored third-party projects) - Runtime state dir: ~/.eigent (journal: ~/.eigent/run-journal.sqlite3; git state: ~/.eigent/workspace-git)
Problem description
Provenance & confidence (please read first)
This report was produced by an AI-assisted investigation (LLM-driven source reading + journal queries), not by reading a hand-maintained design doc. Treat it accordingly:
- Empirically observed: the refusals themselves, the
git_status/read-only asymmetry, the Run row withterminal_detail = "Run background Terminal sessions did not … Git finalization: t6", and the two orphan rows in the journal. These are quoted from our actual state. - Verified by code reading, not individually re-executed: the five dead ends below, and the claim that
nothing ever produces
discarded(our grep forstate="discarded"acrossbackend/appreturned 0 hits). Line numbers are cited so you can confirm each one quickly; if any is wrong in currentmain, that is my error and the conclusion may need re-derivation. - What we did NOT do: we did not build a minimal reproduction from a clean clone. Our environment is a production desktop install that we then hand-repaired (see "Severity"), so the recovery path we describe is a workaround, not a validated product flow. The precise internal trigger sequence — in particular whether the cancel-during-background-command step is the only way to reach this state — needs your confirmation.
What happened
Once a Run terminates while a workspace mutation intent is still open, the intent is
quarantined (git_mutation_intents.status = 'prepared' or 'needs_attention').
From that moment every mutating tool call in the Space is refused — in that task, in new
tasks, and after a full app restart — while read-only tools keep working.
Symptom set as observed by the agent:
write_to_file,shell_exec,shell_write_content_to_fileall fail withWorkspace operation is still active or needs attentionshell_execadditionally gets the hint "Wait for or stop the active background Terminal session, then retry." — but the referenced session id no longer exists:shell_kill_process('t6')returnsNo active session found with ID 't6'.git_statusreportsstate: ready,pending_managed_paths: [], healthy diagnostics.- The Run that caused it carries
terminal_detail = "Run background Terminal sessions did not stop before Git finalization: t6".
Expected: an operator can abandon / reconcile the abandoned operation (there is a declared transition for it), or at minimum a new task can start writing. Actual: total write lockout. The only escape we found was editing our own copy of the journal DB.
Steps to reproduce (deterministic)
- Create/enable a Git Space with
checkout_mode = primary_checkoutandsession_mode = single-agent. - In a task, call
shell_execwithblock=falseon a long-running command (e.g.python -m http.server). Abroad_processmutation intent is created with statusprepared. - Before that command exits, stop/cancel the task from the desktop UI (or let the turn fail).
The Run is terminalized with
terminal_detail = "Run background Terminal sessions did not stop before Git finalization: <id>", andmark_broad_write_needs_attention()quarantines the intent (intent →needs_attention, change set →needs_attention). - Start a brand new task in the same Space and call any mutating tool.
→ Refused:
Workspace operation is still active or needs attention. - Quit and restart the desktop app entirely. → Still refused.
Variant that leaves prepared instead of needs_attention (same effect): make an exact_path
write fail after the intent row is created — e.g. change the worktree from a second process
between prepare and checkpoint, which raises RepositoryStateChangedError
(content.py:423-432). No code path collects the intent afterwards; it simply stays prepared.
Why it cannot be recovered (five dead ends)
| Attempt | Why it fails | Code |
|---|---|---|
| Restart the app | startup reconciliation deliberately scans only statuses=("prepared",) and additionally requires the change set to be open; the docstring states quarantine is intentional ("One ambiguous ChangeSet is quarantined without blocking reconciliation of others") | workspace_git/mutation.py:1519-1525 |
| Finalize / end the offending Run | finalization returns deferred_mutation as soon as it sees a prepared/needs_attention intent, and it only advances open change sets; it never touches intent status | workspace_git/lifecycle.py:183-209 |
Reviewed re-capture (retry_broad_write_checkpoint) | it is the only code that flips needs_attention back, but its first statement requires a path-budget overflow receipt under <state_root>/path-budget/<digest>.json. Such a receipt is written only by the post-dispatch budget-overflow branch, so a non-budget failure never produces one → ContentRepositoryError("No path-budget overflow receipt is available"). It also requires a live in-process PreparedWorkspaceExecution, which a new task cannot obtain | mutation.py:766-782, mutation.py:829-846, agent/toolkit/terminal_toolkit.py:278 |
| Discard it | the journal state machine allows needs_attention → {open, discarded}, but no code in the application ever produces discarded (grep for state="discarded" → 0 hits) | run_journal/store.py:7584-7587 |
| Start a new task | the fence matches on (repository_id, worktree_ref), and worktree_ref is the binding's target_ref — identical for every task in the Space. The comment says it explicitly: "A Run lease admits a task, not every operation within it. This transaction also fences separate service instances." | run_journal/store.py:7807-7824; mutation.py:1331; mutation.py:1123, :1185 (exclusive_worktree=True for all writes) |
There is also no UI affordance: the renderer never calls POST /spaces/{space_id}/git/checkpoints
(the only route accepting an explicit paths list), and POST /git/save-point commits only
pending_managed_paths, which is empty here, so it raises NoCheckpointChangesError.
Yet the machine-readable refusal tells the agent to
Review existing content and checkpoint explicitly selected batches of at most 500 paths
(workspace_git/backend.py:91-97) — an instruction that cannot be carried out.
log
Refusal raised by the fence (run_journal/store.py:7810-7824):
SELECT intents.intent_id FROM git_mutation_intents intents
JOIN git_change_sets active USING (change_set_id)
JOIN git_change_sets target ON target.change_set_id = ?
WHERE active.repository_id = target.repository_id
AND active.worktree_ref = target.worktree_ref
AND intents.status IN ('prepared', 'needs_attention')
LIMIT 1
→ raise OutboxLeaseLostError("Workspace operation is still active or needs attention")Run row of the Run that created the orphan (values from our journal):
run_id 1793076977815-7897
status failed
terminal_reason error
terminal_detail Run background Terminal sessions did not stop before Git finalization: t6Change set / intent left behind:
changeset_fc1583809… state=needs_attention base_commit=f6623d4… run=1793076977815-7897
gitintent_d90b06bb… status=needs_attention trigger=terminal.execute scope=broad_processThe tool call that never got captured:
tool_calls: shell_exec status=outcome_unknown safety_class=unsafe_writeBackend log lines (desktop main.log, timestamps local):
16:27:22 shell_write_content_to_file → Repository changed before checkpoint; refresh status
16:13:59 write_to_file → Terminal/ file write was not started: Workspace operation is
still active or needs attention(The log excerpts that matter are quoted above. The full desktop log is available on request; we are not attaching it by default because it contains local paths and model endpoints, and we will not upload the journal file at all.)
Additional context
Suspected cause and suggested modifications
The state machine has an entry transition with no exit. Three possible fixes, in order of invasiveness:
-
Implement the declared disposal. Expose an explicit "abandon this workspace operation" action (a controller route + a button in the Space's version-history panel) that performs the already-allowed transition
needs_attention → discardedon the change set and closes the intent, without ever touching the user's files. This is the missing producer of a state the schema already admits. -
Let startup reconciliation handle quarantined intents.
reconcile_startup()currently scans onlystatuses=("prepared",)(mutation.py:1519-1525). Consider a reviewed path forneeds_attentionrows — reusingretry_broad_write_checkpoint's semantics but without the receipt precondition, since a receipt is only produced by the post-dispatch budget branch. -
Don't let the fence be cross-task without an exit. If cross-service fencing is required (
store.py:7808-7809), pair it with a bounded lease/expiry forpreparedintents, so a dead Run cannot block the checkout forever.
Diagnostics should name the blocker
The refusal text is currently identical to an unrelated condition and carries an actionable-but-false hint:
OutboxLeaseLostErroris reused both for lost terminal/outbox leases and for this intent fence;terminal_toolkit.py:1138-1141appends "stop the active background Terminal session" to any instance of that error class, even when no session exists. Suggest a dedicated error class for the fence and attaching the blockingintent_id(+ its Run id and status) to the message, e.g.blocked_by_intent=gitintent_d90b06bb… (status=needs_attention, run=…, trigger=terminal.execute)— we only found it by opening the SQLite journal with a read-only client.
Severity
Total loss of the write surface for a Git Space, not recoverable by the user through any documented
action, and it silently affects every new task in the Space. We had to hand-edit ~/.eigent/run-journal.sqlite3
(a guarded single-row UPDATE git_mutation_intents SET status='completed' WHERE intent_id=? AND status='needs_attention')
after verifying via git status that the abandoned operation had left nothing uncaptured. That is not a
workaround we would recommend to anyone, and the data-loss risk of alternative workarounds (deleting and
re-initialising the Space's repository binding) is worse.
Disclosure
Our installed copy has exactly two modified files relative to tag v1.0.6, neither of which is cited above:
backend/app/tool_validation.py (a pre-dispatch quoted-scalar coercion patch) and
backend/app/agent/listen_chat_agent.py. All files cited in this report were verified byte-identical to
tag v1.0.6 before filing.
Activity
0No activity yet.