Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .agents/pm/extensions/.managed-extensions.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"version": 1,
"updated_at": "2026-10-28T17:20:30.881Z",
"updated_at": "2026-10-29T00:12:02.978Z",
"entries": [
{
"name": "builtin-calendar",
Expand Down
5 changes: 3 additions & 2 deletions .agents/pm/features/pm-gh1576.toon
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ status: open
priority: 1
tags[3]: concurrency,"gh:1576",sdk
created_at: "2026-10-24T16:12:40.963Z"
updated_at: "2026-10-24T16:12:40.963Z"
updated_at: "2026-10-28T22:03:09.177Z"
author: "harness:codex"
estimated_minutes: 360
acceptance_criteria: "Define a public optional expected revision/hash contract across single-item and bulk SDK/CLI/MCP mutations; Check each precondition under the authoritative item lock at commit time and refuse stale writes without acknowledged item or history changes; Return typed current revision and bounded actionable change evidence; Deterministic concurrent controls cover closeout tags, collection cleanup, claim and bulk partial outcomes without weakening lease fencing."
Expand All @@ -18,6 +18,7 @@ dependencies[3]{id,kind,created_at,author,source_kind,author_source}:
pm-a8lnoh,related,"2026-10-24T16:12:40.963Z","harness:codex","cli:create:dep",detected
pm-gh1359,related,"2026-10-24T16:12:40.963Z","harness:codex","cli:create:dep",detected
pm-gh1549,related,"2026-10-24T16:12:40.963Z","harness:codex","cli:create:dep",detected
comments[1]{created_at,author,text}:
comments[2]{created_at,author,text}:
"2026-10-24T16:12:40.963Z","harness:codex","Intake source: https://github.com/unbraind/pm-cli/issues/1576. Duplicate check: complete strict 3098-item all-status corpus with no omissions plus fresh request-specific searches and current open/in_progress lists; no existing owner matches this distinct acceptance. Full live related lineage was read. Existing delivered lease-fencing/focus behavior remains historically closed. This report is planning evidence; no current independent reproduction or fix is claimed. Queued open and unclaimed under the SDK outcome milestone."
"2026-10-28T22:03:09.177Z","harness:codex","Additional consumer report: https://github.com/unbraind/pm-cli/issues/1620 (2026-10-28). Its proposed option 2 is the existing expected-revision acceptance here, so reuse this canonical feature instead of creating duplicate work. Reporter describes a separate-process ordinary SDK writer entering between a transaction inspect and apply, losing title/parent/relation/assignee changes. Current source uses WORKSPACE_TRANSACTION_LOCK_ID for the coordinator while item writes take their own lock; the report is consistent with those distinct scopes, but this intake has not independently executed its probe. Include precise transaction-isolation documentation and a native concurrent-writer negative control in this feature. The host-bound recovery delivery only guards compensation ownership; it does not claim general read-decide-write isolation. Item remains open and unclaimed."
body: "## Summary\n\nAgents and extensions often read an item, decide something from it (which PR delivered it, whether its acceptance criteria are met, which checkpoint to resume), and then write. Neither the SDK nor the CLI can apply that write only if the item is still the revision the decision was based on. `update`, `close`, `comments --add` and their SDK equivalents take no expected item hash or revision. A concurrent edit between the read and the write is silently built on, so the agent records a decision about state that no longer exists.\n\nThe lock-TTL fencing from #1549 makes a single mutation safe. It doesn't cover the gap between an agent's read and its later write.\n\n## Evidence (fleet packages, 2026-10-24, pm 2026.10.20–2026.10.24)\n\nRead-only reviews blocked three separate packages on this class in one day, each reproduced against the real SDK:\n\n- **pm-github closeout:** the delivering PR is chosen from the item's `delivery-pr` tag. If the tag changes from PR #1 to PR #2 while evidence is being fetched, the item still closes with PR #1's provenance. The workaround is a scoped `beforeMutation` hash guard under the item lock plus one retry. The API the agent asked for: `close(id, reason, { expectedItemHash })`.\n- **pm-rlm:** failure cleanup read a checkpoint, then saved that stale copy over a concurrent resume's answer, steps and token charges.\n- **pm-slack:** outbox retry cleanup deleted a peer drainer's newer group mappings.\n\nEach package had to hand-roll its own guard.\n\n## Reproduction (plain directory, no git)\n\n1. Agent A: `pm get <id> --json`, records `item_hash` H1, decides to close based on the tags it read.\n2. Agent B: `pm update <id> --tags delivery-pr:2`, so the hash is now H2.\n3. Agent A: `pm close <id> \"Delivered by PR 1\"` succeeds. There is no way to say \"only if the hash is still H1\".\n\n## Expected\n\nAn optional precondition on every mutating command and SDK method, for example `--expect-hash <item_hash>` / `{ expectedItemHash }`, checked under the item lock as part of the write. On mismatch the write is refused with a typed code (for example `stale_precondition`, exit 2). The refusal returns the current hash, plus a compact summary of which fields changed since H1, so the agent can re-decide in one call. The same option belongs on `update`, `close`, `claim`, `comments/notes/learnings --add`, `files/docs/test --add` and bulk mutations (one hash per id).\n\n## Why core\n\nRead-decide-write is the normal agent loop on a shared tracker. Without a native precondition, every package re-implements a lock-plus-hash check, and the races above ship until a reviewer finds them. This is part of making many agents on one tracker safe without git (#1516 measured the throughput side of the same workload)."
Loading
Loading