Skip to content

feat(handoff): verified continuation commit, structured payload, duplicate-item reuse, assignee freeze - #365

Merged
getappz merged 5 commits into
masterfrom
task/236
Jul 29, 2026
Merged

feat(handoff): verified continuation commit, structured payload, duplicate-item reuse, assignee freeze#365
getappz merged 5 commits into
masterfrom
task/236

Conversation

@getappz

@getappz getappz commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Item #236 (Handoff hardening, QuorumGit adoption) — narrowed scope per the spec artifact's own 2026-07-28 review update:

  • Verified continuation commit: HandoffRequest.last_commit is verified against the repo (git cat-file -e) and, when the item's own task/<seq> branch already exists, checked for reachability from it (git merge-base --is-ancestor). A fabricated/typo'd OID is rejected — verified, not trusted.
  • Structured payload: completed/remaining are now required fields, blockers optional — all stored in the attached asset's metadata.
  • Duplicate-item fix: a handoff without item_id now checks the recipient's existing open items for a name or thread match and reuses it instead of blindly creating a new one. Genuinely new work (no match) still auto-creates.
  • Freeze (item-claim half): agentflare_backend::item::claim gains a BlockedByAssignee outcome — a freshly handed-off, never-claimed item can only be claimed by its assignee until accepted. Excludes completed/cancelled items so reopening old work still works normally.

Deferred, not silently dropped: the shim-side "deny push/commit to the handoff's branch by non-parties" half of the freeze spec. It needs new branch→item→assignee resolution wiring in flare-git-shim mirroring the existing scope-check (#234), and the spec's own review note already downgraded it to defense-in-depth since the motivating incident (opencode stray-diff-on-main) is covered by #234's scope enforcement plus the opencode branch-guard plugin.

Test plan

  • cargo test --workspace — 1676 tests, 0 failed
  • cargo clippy --workspace --all-features -- -D warnings — clean
  • New tests: verified-commit accept/reject, structured payload storage, duplicate-item reuse (by name and by thread), still-creates-when-no-match, claim blocked-by-assignee + claim-by-assignee-succeeds

Summary by CodeRabbit

  • New Features
    • Handoffs now store completed, remaining, optional blockers, and optional commit references, and return these in handoff metadata.
    • When an item_id isn’t provided, handoffs can reuse an existing matching open item (by name or thread).
    • Commit references are validated before being accepted.
    • Item claiming now returns explicit acquired/held/assignee-blocked outcomes (including a blocking reason).
  • Bug Fixes
    • Prevented claiming items assigned to a different agent unless the item is complete or cancelled.

…icate-item reuse, assignee freeze

Item #236 (handoff hardening, QuorumGit adoption):

- HandoffRequest gains last_commit: verified via `git cat-file -e` and,
  when the item's task/<seq> branch already exists, `git merge-base
  --is-ancestor` -- a fabricated or unreachable OID is rejected, not
  trusted.
- HandoffRequest gains required completed/remaining and optional
  blockers fields, stored in the asset's metadata.
- A handoff without item_id now reuses an existing open item assigned
  to the recipient (matched by name or thread_id) instead of blindly
  creating a duplicate; genuinely new work still auto-creates.
- agentflare_backend::item::claim gains a BlockedByAssignee outcome:
  a freshly handed-off, never-claimed item can only be claimed by its
  assignee until accepted, excluding completed/cancelled items.

Deferred: shim-side branch-push denial for open handoffs (spec's own
review already downgraded this to defense-in-depth, covered in large
part by #234's scope enforcement and the opencode branch-guard plugin).
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds explicit claim outcomes with assignee-based blocking, updates MCP claim responses, and extends handoffs with structured progress fields, item reuse, commit continuity validation, and expanded asset metadata.

Changes

Claim and handoff flow

Layer / File(s) Summary
Assignee-aware claim outcomes
crates/agentflare-backend/src/item.rs, src/mcp_server/item.rs
Claims now return acquired, held, or assignee-blocked outcomes; MCP responses expose the new states.
Structured handoff processing
src/mcp_server/types.rs, src/mcp_server/handoff.rs, src/mcp_prompts.rs
Handoffs accept progress and commit fields, reuse matching open items, validate continuation commits, and persist expanded metadata.
Handoff reuse and continuity tests
src/mcp_server/tests/artifact_tests.rs
Tests cover item reuse, new-item fallback, structured metadata, and valid or invalid commit OIDs.

Sequence Diagram(s)

sequenceDiagram
  participant MCPClient
  participant HandoffImpl
  participant GitRepository
  participant ItemStore
  MCPClient->>HandoffImpl: submit structured handoff
  HandoffImpl->>GitRepository: verify last_commit reachability
  GitRepository-->>HandoffImpl: validation result
  HandoffImpl->>ItemStore: reuse or create open item
  ItemStore-->>HandoffImpl: item identifier
  HandoffImpl-->>MCPClient: persist handoff asset metadata
Loading

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately captures the main handoff hardening changes in the PR.
Description check ✅ Passed The description covers the required summary and test plan, with only the reviewer-notes section and one optional test item missing.
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/236

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: 5

🧹 Nitpick comments (2)
src/mcp_server/handoff.rs (1)

79-92: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Name match wins over an explicit thread_id mismatch.

The find predicate short-circuits on i.name == name, so a handoff carrying thread_id: Some("t-2") can be attached to an item whose metadata thread is t-1 purely because the names collide. Consider preferring a thread match and only falling back to name when thread_id is None (or the thread agrees).

🤖 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/mcp_server/handoff.rs` around lines 79 - 92, Update the handoff item
predicate in the find call so an explicit thread_id takes precedence: require
the item metadata thread to match when thread_id is Some, allowing a name match
only when the thread agrees; when thread_id is None, retain name-based matching.
Ensure a matching name cannot override a conflicting thread ID.
src/mcp_server/tests/artifact_tests.rs (1)

589-611: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

This test requires the source tree to be a live git checkout.

worktree_repo_root() falls back to a path-walk when .git is absent, so running the suite from an exported/vendored tree (crates.io tarball, distro packaging) makes rev-parse HEAD fail and the test panics rather than skipping. Consider initializing a throwaway repo in the temp dir, or gating on run_in(..., ["rev-parse","HEAD"]).is_ok().

🤖 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/mcp_server/tests/artifact_tests.rs` around lines 589 - 611, The test
handoff_accepts_an_existing_last_commit_oid must not panic when the source tree
is not a live Git checkout. Before requiring HEAD, probe rev-parse through
worktree_repo_root() and return early when it fails, or initialize an isolated
temporary repository; preserve the existing assertion for checkout environments.
🤖 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 `@crates/agentflare-backend/src/item.rs`:
- Around line 633-670: Update the assignee comparison in claim to canonicalize
both the owner and assignee agent identities before checking for a mismatch.
Adjust agent_part or the comparison around claim so aliases such as “claude:1”
resolve to the same canonical form as assignee_agent, while preserving the
existing BlockedByAssignee behavior for genuinely different agents.

In `@src/mcp_server/handoff.rs`:
- Around line 52-54: The handoff flow performs blocking Git work while the
backend DB mutex is held. Update the surrounding with_backend_db logic to
resolve and retain item.sequence_id under the lock, then call
verify_continuation_commit after the closure completes, preserving the existing
continuation and error behavior.
- Around line 135-152: Update the validation guards in handoff_impl to reject
empty completed and remaining values after trimming, matching the existing
required-field checks for recipient, name, and content. Perform this validation
before constructing the metadata in the handoff flow so empty progress fields
cannot be accepted.
- Around line 264-282: Validate the caller-controlled oid in the handoff
validation flow before using it with git: reject leading-dash values and require
it to resolve specifically to a commit, not merely any existing Git object.
Update the cat-file check near the existing last_commit error and ensure the
same validated commit-only value is used by the later merge-base call.

In `@src/mcp_server/types.rs`:
- Around line 238-243: Update the handoff prompt and its request examples to
explicitly include the required completed and remaining fields, matching the
fields defined on the structured payload in the relevant handoff types. Ensure
generated handoff requests contain both fields so they deserialize successfully.

---

Nitpick comments:
In `@src/mcp_server/handoff.rs`:
- Around line 79-92: Update the handoff item predicate in the find call so an
explicit thread_id takes precedence: require the item metadata thread to match
when thread_id is Some, allowing a name match only when the thread agrees; when
thread_id is None, retain name-based matching. Ensure a matching name cannot
override a conflicting thread ID.

In `@src/mcp_server/tests/artifact_tests.rs`:
- Around line 589-611: The test handoff_accepts_an_existing_last_commit_oid must
not panic when the source tree is not a live Git checkout. Before requiring
HEAD, probe rev-parse through worktree_repo_root() and return early when it
fails, or initialize an isolated temporary repository; preserve the existing
assertion for checkout environments.
🪄 Autofix (Beta)

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 Plus

Run ID: 642210e0-0c4e-4695-b430-f0e29def2515

📥 Commits

Reviewing files that changed from the base of the PR and between aa6c902 and acbffe8.

📒 Files selected for processing (5)
  • crates/agentflare-backend/src/item.rs
  • src/mcp_server/handoff.rs
  • src/mcp_server/item.rs
  • src/mcp_server/tests/artifact_tests.rs
  • src/mcp_server/types.rs

Comment thread crates/agentflare-backend/src/item.rs Outdated
Comment thread src/mcp_server/handoff.rs Outdated
Comment thread src/mcp_server/handoff.rs
Comment thread src/mcp_server/handoff.rs Outdated
Comment thread src/mcp_server/types.rs
getappz added 4 commits July 29, 2026 10:30
…oad off the DB lock (#366)

- cargo fmt (item.rs claim closure, types.rs schemars doc attr)
- item::claim: canonicalize both sides of the assignee/owner comparison so
  an alias owner (claude:1) isn't wrongly BlockedByAssignee against its own
  canonical handoff assignee (claude-code)
- verify_continuation_commit: resolve the target branch under the backend
  DB lock, then run all git subprocess checks after releasing it, matching
  the existing item_claim split; validate oid is a plain hex id and force
  commit-type resolution (oid^{commit}) so a non-hex/flag-like value or a
  blob/tree/tag can't pass as a continuation commit
- handoff_impl: reject empty completed/remaining instead of silently
  accepting an empty structured payload
- mcp_prompts: document completed/remaining as required handoff fields
  so generated requests don't fail deserialization
…rning from racing concurrent with_temp_home callers

The assertion read AGENTFLARE_HOME_OVERRIDE outside GLOBAL_STATE_LOCK,
the lock that guards every set/remove of it. A concurrent thread's own
correctly-scoped with_temp_home call could be transiently holding the
var set at the exact moment this test read it, flaking the check on a
var this test was never entitled to observe in the first place. Fix:
acquire the same lock before reading.
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