Skip to content

agent tool description claims the write-contention refusal names release; it does not, and the liveness filter says it could not #6272

Description

@Hmbown

Reported from a live session: a rerun into the same folder hit a write-scope contention refusal, and the remedy that actually cleared it (release) was not discoverable from the error — it had to be found by reading the agent tool description.

The confirmed mismatch

crates/tui/src/tools/subagent/mod.rs:9580 (the action enum description) says:

release clears write claims whose owner is no longer running — the remediation a write-scope contention refusal names; pass agent_id to clear one, omit it to sweep.

The refusal it refers to (crates/tui/src/tools/subagent/coord/ledger.rs:587) names four remedies, and release is not among them:

write-scope contention with {owner}: requested roots {..}, files {..}, contracts {..}
overlap its writable roots {..}, files {..}, contracts {..}.
Claim disjoint sibling write_roots (for example tmp/scan/worker-a and
tmp/scan/worker-b) or exact_files for each output. A read-only worker uses
write_authority=read_only without a write claim. Otherwise serialize the writers
or use worktree isolation; a nested path inside an existing writable root still
overlaps.

So the description asserts a property of an error message that the error message does not have. That is the minimum defect, and it is not in dispute.

The part that needs a repro before anyone writes a fix

The obvious fix — "have the refusal name release when the blocking owner is inactive" — may be unreachable, because the refusal already cannot fire for an inactive owner. register_claim filters the overlap search on liveness (ledger.rs:563-571):

.find(|existing| {
    !existing.isolated_worktree
        && existing.claim.owner != claim.owner
        && owner_is_active(&existing.claim.owner)     // <-- here
        && existing.claim.overlaps(&claim)
})

If owner_is_active is false, the stale claim is skipped and no refusal is raised at all. release_stale_claims (ledger.rs:625) clears exactly the complement — owners for which that predicate is false. On this reading the two sets are disjoint and release can never be the remedy for a contention refusal, which would make the description wrong in a stronger sense than "incomplete".

But the reporter observed the refusal and observed release clearing it. Both callers pass the same predicate, built by active_coordination_owners (mod.rs:5127):

self.agents.keys().chain(self.worker_records.keys())
    .filter(|id| self.is_live_coordination_owner(id))

That set is built from in-process maps. In a fresh session those maps do not contain owners persisted by a previous process, so a stale owner should read as inactive — which again predicts no refusal.

So one of these is true, and a repro should say which:

  1. The refusal the reporter hit was raised somewhere other than ledger.rs:587 (there may be a second contention path — validate_write_scope, mod.rs:4214, is a candidate).
  2. The stale owner was present in agents/worker_records and is_live_coordination_owner returned true for a process that no longer exists — a liveness bug, and the real defect.
  3. The owner id is reconstructed identically on a rerun, so the blocking claim belonged to a live owner, and release clearing it was incidental rather than causal.

Acceptance

  • A repro that names which of the three above actually happened, with the refusal text captured verbatim.
  • Then either: the refusal names the sweep in the case where it is genuinely the remedy, or mod.rs:9580 stops asserting that it does.
  • If (2) turns out to be the cause, that liveness bug is the headline and this text issue is a symptom.

Do not "fix" this by adding release to the refusal text unconditionally — that would point users at a sweep that, on the current liveness filter, cannot clear the claim that just blocked them.

Related: #5562 / SHA-6272 (stale write claims locking sub-agents out), release_stale_write_claims at mod.rs:4200.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions