Skip to content

fix(work): guarantee a claim release on every execute_work exit path - #470

Merged
getappz merged 3 commits into
masterfrom
task/100
Aug 12, 2026
Merged

getappz merged 3 commits into
masterfrom
task/100

Conversation

@getappz

@getappz getappz commented Aug 12, 2026 •

Copy link
Copy Markdown
Owner

Summary

Item #94 exited its job cleanly (agent_jobs.state='exited') while its item_claims row stayed claimed forever, wedging it past all 3 retries — and the same pattern hit #64 and #68. execute_work only released the claim on the intentional bail-out points it explicitly called release_and_comment/item_release for; item_done returning Err fell through none of them, and a panic anywhere after the claim was acquired skipped them all outright.

Adds ClaimGuard, a Drop-based backstop created right after the claim succeeds and disarmed only once the claim's fate is definitively decided (item_done Ok, or a confirmed-successful explicit release). Drop runs on every remaining exit, including an unwinding panic, so the claim is released immediately instead of waiting out the job's timeout or the claim's own TTL.

Test plan

  • cargo test --bin agentflare -- cli::work:: — 44 tests pass, including 3 new ClaimGuard tests (drop-releases, disarmed-stays-held, releases-through-panic-unwind)
  • cargo fmt --check, cargo clippy clean
  • cargo build clean

Summary by CodeRabbit

  • Bug Fixes

    • Work items are now automatically released when processing ends unexpectedly, including during panic recovery.
    • Work remains safely claimed when completion or hold-release operations fail.
    • Claim release behavior is now more reliable across successful and unsuccessful processing outcomes.
  • Tests

    • Added coverage for automatic release, confirmed claim decisions, failed operations, and panic handling.

Item #94 exited its job cleanly (agent_jobs.state='exited') while its
item_claims row stayed 'claimed' forever, wedging it past all 3 retries.
execute_work only released the claim on the intentional bail-out points
it explicitly called release_and_comment/item_release for; item_done
returning Err fell through none of them, and a panic anywhere after the
claim was acquired skipped them all outright.

Add ClaimGuard, a Drop-based backstop created right after the claim
succeeds and disarmed only once the claim's fate is definitively
decided (item_done Ok, or a confirmed-successful explicit release).
Drop runs on every remaining exit, including an unwinding panic, so
the claim is released immediately instead of waiting out the job's
timeout or the claim's own TTL.

Agentflare-Agent: claude-code
Agentflare-Branch: task/100
Agentflare-Item: 100
@coderabbitai

coderabbitai Bot commented Aug 12, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds a panic-safe ClaimGuard to execute_work. The guard releases claims on failed completion, failed hold release, or panic unwinding. Successful claim outcomes disarm the guard. Tests cover each lifecycle path.

Changes

Claim lifecycle protection

Layer / File(s) Summary
ClaimGuard cleanup
src/cli/work.rs
ClaimGuard releases an acquired claim during drop unless the workflow disarms it.
Workflow outcome handling
src/cli/work.rs
Hold and completion paths disarm the guard only after successful outcomes. Tests cover release, disarming, and panic unwinding.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely identifies the main change: guaranteeing claim release on every execute_work exit path.
Description check ✅ Passed The description provides a detailed summary and complete test plan, but it omits the optional Notes for reviewers section.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch task/100

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/cli/work.rs`:
- Around line 914-923: Update the item_release handling in the mcp claim-release
flow to inspect the successful response payload and disarm claim_guard only when
its released field is true. Do not treat every Ok result as a release; preserve
the guard fallback when released is false or the request fails.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fe922ba6-5aed-4e4e-8895-70f200bddd3e

📥 Commits

Reviewing files that changed from the base of the PR and between 6a9791b and 8f70e25.

📒 Files selected for processing (1)
  • src/cli/work.rs

Comment thread src/cli/work.rs
Comment on lines +914 to +923
if mcp
.item_release(ItemRequest {
action: "release".into(),
id: Some(item_id.into()),
..Default::default()
})
.is_ok()
{
claim_guard.disarm();
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# The lean-ctx tools are unavailable in this review context.
# Locate AgentflareMcp::item_release and inspect its semantic response contract.
rg -n -C 8 --type rust '\bfn\s+item_release\b|\bitem_release\s*\(' src
rg -n -C 8 --type rust '"released"|released\s*:|action:\s*"release"' src

Repository: getappz/agentflare

Length of output: 21062


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- item_release implementation ---'
sed -n '619,684p' src/mcp_server/item.rs

printf '%s\n' '--- release backend implementation and result shape ---'
rg -n -C 12 --type rust 'pub\s+(fn|async fn)\s+release\b|fn\s+release\b|struct\s+ItemRequest|item_release\(' .

Repository: getappz/agentflare

Length of output: 20771


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
import re

server = Path("src/mcp_server/item.rs").read_text()
work = Path("src/cli/work.rs").read_text()
backend = Path("crates/agentflare-db-kit/src/claim.rs").read_text()

release_body = re.search(
    r'pub\(crate\) fn item_release\b.*?(?=\n    pub\(crate\) fn item_done\b)',
    server,
    re.S,
).group(0)
assert 'Ok(serde_json::json!({"released": ok, "item_id": item_id}).to_string())' in release_body
assert 'agentflare_backend::claim::release' in release_body
assert 'conn.execute(&sql, params.as_slice())? > 0' in backend

hold = re.search(
    r'if let Some\(reason\) = detect_hold_signal.*?(?=\n            \})',
    work,
    re.S,
).group(0)
assert '.item_release(ItemRequest' in hold
assert '.is_ok()' in hold
assert 'claim_guard.disarm()' in hold

print("item_release returns a JSON `released` boolean, while the hold path checks only Result::is_ok().")
print("The backend release result is the affected-row boolean, so Ok(...released:false...) is representable.")
PY

Repository: getappz/agentflare

Length of output: 355


Disarm the claim guard only when released is true.

item_release can return Ok({"released": false, ...}) when no claim is deleted. .is_ok() treats this as success and suppresses the guard fallback. Parse the response and disarm only when released == true.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/cli/work.rs` around lines 914 - 923, Update the item_release handling in
the mcp claim-release flow to inspect the successful response payload and disarm
claim_guard only when its released field is true. Do not treat every Ok result
as a release; preserve the guard fallback when released is false or the request
fails.

Source: Coding guidelines

@getappz
getappz merged commit e4252fc into master Aug 12, 2026
17 checks passed
@getappz
getappz deleted the task/100 branch August 12, 2026 15:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant