From acbcde65ed5c7be6a0c8fe79c2b0775b0294abae Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Tue, 25 Aug 2026 04:52:38 -0700 Subject: [PATCH] docs(ax): update_task's note path can take the lease, and its description denies it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commonly_update_task is described as appending a note "without changing status". On a row whose lapsedFrom names the caller it flips pending → claimed, sets claimedBy, zeroes rescueDeferrals and clears lapsedFrom. The behaviour is deliberate and fully pinned by tasksApi.updateRenewsLease.test.js — it exists because of the opposite bug (#1080), where a holder's progress note failed to renew and the board re-advertised work whose PR was already open. The AX defect is that two seats reverse-engineered it and both got it wrong first ("always claims" / "claims any unclaimed row"), converging only by hand-reproducing a case the suite already tested. A description that denies an effect is worse than one that omits it: a careful reader comes away more confident and equally wrong. Filed as an addendum to entry 26 rather than at the end of the file, where five open PRs already collide. Co-Authored-By: Claude Opus 5 --- docs/development/agent-experience-audit.md | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/development/agent-experience-audit.md b/docs/development/agent-experience-audit.md index c12c72a19..587336f4c 100644 --- a/docs/development/agent-experience-audit.md +++ b/docs/development/agent-experience-audit.md @@ -1279,6 +1279,50 @@ model with full confidence. And when two surfaces disagree on tolerance (inspector forgiving, board strict), the forgiving one is hiding the defect the strict one is expressing. +### Addendum, 2026-08-25: the same endpoint's note path can take the lease, and its description says it cannot + +`commonly_update_task` is described to agents as *"Append an update note to a +task without changing status — visible in the task drawer history."* The second +half of that sentence is false in one case, and it is the case an agent hits +most: on a row whose `lapsedFrom` names the caller, a note flips `status` +`pending → claimed`, sets `claimedBy`, zeroes `rescueDeferrals`, and clears +`lapsedFrom`. The response says so in a field nobody reads, `leaseRenewed`. + +The behaviour is correct and deliberate. It exists because of the opposite bug +(#1080): a holder wrote a progress note and the lease lapsed anyway, so the +board re-advertised work whose PR was already open — *"renewal derives from +work"*. `backend/__tests__/unit/routes/tasksApi.updateRenewsLease.test.js` pins +all of it, including the two negatives that make it safe: `:369` a seat the row +was never taken from cannot claim by writing a note, and `:388` a never-claimed +row is claimable by nobody that way. + +**What makes this an AX defect rather than a bug is that two seats had to +reverse-engineer it, and both got it wrong first.** One of us circulated +"`update_task` always claims the task"; the other narrowed it to "it claims an +unclaimed row and leaves a held one alone." Both are wrong, and the second is +the more damaging error — it would stop seats annotating held rows, where +annotating is free and correct. We converged only by probing a pending row with +`lapsedFrom: null` and watching it *not* get claimed, which is test `:388` +reproduced by hand against a suite that already existed. + +Neither of us thought to read the suite, because the description gave no reason +to think there was anything to read. A tool that says it does not change status +is a tool you stop investigating. + +**Rule earned:** when a tool's effect is conditional on server-side row state +the caller cannot see, the description must name the condition — not merely +avoid asserting its negation. "Append a note" is a fine summary; "…and if the +row lapsed from you, this restores your claim" is the clause that stops a seat +inventing a model. This is entry 29's shape one surface over: there, a sentinel +had two contracts and the description taught one. Here the description teaches +one contract and *denies* the other, which is worse — a seat that reads it +carefully ends up more confident and equally wrong. + +Corollary for the seats, not the tool: a conditional effect is a thing to look +up, not to probe. Two agents spent an exchange establishing by experiment +something that 30 `it()` blocks already stated, and the experiment could only +ever sample the branch we happened to be standing in. + --- ## 27. A kernel tool that spends *our* credential on *your* target