Skip to content

v1.87.0 ·

Finished work stayed claimed

The problem

Completing an instance never releases its claim. recordCompletion (packages/cli/src/commands/transition.ts) is the single writer of status: complete, and it does not touch the claim store, so <git-common-dir>/aidlc/claims/<instance>.yaml stays behind. Reproduced twice on CLI 1.77.0 (see the item): aidlc claim then aidlc transition (deployment → complete) leaves the file; a following aidlc release removes it.

Two readers then trust the leftover file:

  1. The session-start hook. claimsHere (packages/cli/src/hooks/lifecycle-context.ts) reports every claim recorded from this checkout, stale or not, so every new session is told a finished instance is "claimed in this checkout". The phase guard reads the same list.
  2. aidlc status --prune and the doctor's prune-registry migration. orphanedClaims (packages/cli/src/concurrency/claim-store.ts) drops only claims whose worktree_path no longer exists. A claim taken in the primary checkout has no worktree_path, so prune answers "nothing to prune" while the file sits there.

How it could be solved

The release command already did the right thing; finishing an instance just never called it. So the fix shares one release step between the two, and the finished instance's release is logged against whichever session held the lock, the same record a person releasing by hand would leave. If the lock cannot be removed at that moment, the instance still finishes and a warning says so, because the finished record is what matters and a stray lock can be cleaned up later.

That cleanup needed a careful rule. A lock is now treated as left behind when its instance is finished, but finished according to the checkout the lock was taken in, not the one the cleanup runs from. Two checkouts of one repository can disagree: the main one may say an instance is done while a second checkout has reopened it and is working on it. Reading the wrong one would delete a lock someone is using, which is worse than leaving a stale one for another day.

How AIDLC solves it

Finishing an instance now lets go of its claim. The aidlc transition that completes an instance removes the claim file, records the release against the session that held it, and says so in one line. Before, the claim stayed behind after completion, so every new session in that checkout was told a finished instance was still claimed there, for as long as nobody noticed and removed the file by hand. One such claim misled sessions for eleven days.

Claims already left behind by an older version are now treated as orphaned. aidlc status warns about a claim on a completed instance, and aidlc status --prune and aidlc doctor remove it, where before they answered that there was nothing to prune. The session-start hook leaves such a claim out. Completion is read in the checkout the claim was taken in, so a second checkout that has reopened an instance keeps its claim even while the main checkout says the instance is done.

Patch: no command, flag or file format changes. Completion prints one new line when it releases a claim, and status --prune now says why it released each claim.

Changes

  • packages/cli/src/commands/transition.ts — completion releases the claim at both completion sites; a failure is a warning.
  • packages/cli/src/commands/claim.ts — releaseClaim, shared with aidlc release.
  • packages/cli/src/concurrency/claim-store.ts — instanceIsComplete; orphaned claims carry a reason and are read in the claim's own checkout.
  • packages/cli/src/commands/status.ts, packages/cli/src/doctor/migrations/prune-registry.ts — use the shared rule.
  • packages/cli/src/hooks/lifecycle-context.ts — the hook skips completed instances.
  • packages/cli/test/completed-claim-left-behind.test.ts — new, 17 tests; packages/cli/test/lifecycle-hooks-harness.test.ts — fixture reads as open.
  • packages/content/docs/state-and-concurrency.md — one paragraph.
  • .aidlc/knowledge/decisions/completion-releases-claim.yaml — the decision.
  • packages/website/content/blog/overrides/completed-claim-left-behind-post.json and -options.md — this release's post.