refactor(mcp): split item_inner's dispatch arms into src/mcp_server/item.rs - #187
Conversation
…tem.rs item_inner was a single 411-line function (the file's largest, and its top complexity hotspot per both ast-grep line-span scanning and lean-ctx's own health scoring) dispatching all 13 item actions inline. Moved each arm's body verbatim into its own item_<action> method in a new submodule; item_inner is now just the match dispatch. No behavior change.
📝 WalkthroughWalkthroughItem MCP handling is extracted from ChangesItem MCP handlers
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (3)
src/mcp_server/item.rs (3)
188-245: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winProgress-sender fallback duplicated with
item_done.The
PROGRESS_SENDER.try_with(|ps| f(item, &repo_root, target, ps.as_ref())).unwrap_or_else(|_| f(item, &repo_root, target, None))fallback (lines 218-226) is repeated almost verbatim initem_donebelow (lines 313-321), only swappingcreate_worktreeforpush_and_open_pr. Worth factoring into a shared helper that takes the worktree fn as a parameter.🤖 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/item.rs` around lines 188 - 245, Factor the repeated PROGRESS_SENDER.try_with fallback pattern from item_claim and item_done into a shared helper that accepts the worktree operation as a parameter. Update item_claim’s create_worktree call and item_done’s push_and_open_pr call to use the helper while preserving their existing arguments and fallback behavior.
278-348: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMirrors the same progress-sender fallback in
item_claim.Lines 313-321 duplicate the
PROGRESS_SENDER.try_with(...).unwrap_or_else(...)fallback already flagged initem_claimabove, just callingpush_and_open_prinstead ofcreate_worktree. Same extraction opportunity applies here.🤖 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/item.rs` around lines 278 - 348, Extract the duplicated PROGRESS_SENDER.try_with(...).unwrap_or_else(...) fallback in item_done into the shared helper introduced for item_claim, preserving the existing push_and_open_pr invocation and fallback behavior. Update item_done to call that helper while keeping the surrounding PR URL handling unchanged.
10-51: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider a shared "required field" helper.
The
ok_or_else(...)?+.trim().is_empty()pattern here (name, lines 11-16) repeats near-identically for every required field across all 13 handlers in this file (id initem_get/item_delete/item_claim/etc., label_id initem_add_label/item_remove_label, state_id initem_update_state). A small generic helper would cut this boilerplate significantly.♻️ Example helper
fn require_field(value: Option<String>, field: &str, action: &str) -> Result<String, ErrorData> { let v = value .ok_or_else(|| ErrorData::invalid_params(format!("{field} is required for {action}"), None))?; if v.trim().is_empty() { return Err(ErrorData::invalid_params(format!("{field} is required"), None)); } Ok(v) }🤖 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/item.rs` around lines 10 - 51, Introduce a shared required-field helper near the item handlers that validates an optional string for both presence and non-blank content, using the field and action to produce the existing error messages. Replace the repeated validation patterns across handlers including item_create, item_get/item_delete/item_claim, item_add_label/item_remove_label, and item_update_state, while preserving each handler’s current required-field semantics and error behavior.
🤖 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.
Nitpick comments:
In `@src/mcp_server/item.rs`:
- Around line 188-245: Factor the repeated PROGRESS_SENDER.try_with fallback
pattern from item_claim and item_done into a shared helper that accepts the
worktree operation as a parameter. Update item_claim’s create_worktree call and
item_done’s push_and_open_pr call to use the helper while preserving their
existing arguments and fallback behavior.
- Around line 278-348: Extract the duplicated
PROGRESS_SENDER.try_with(...).unwrap_or_else(...) fallback in item_done into the
shared helper introduced for item_claim, preserving the existing
push_and_open_pr invocation and fallback behavior. Update item_done to call that
helper while keeping the surrounding PR URL handling unchanged.
- Around line 10-51: Introduce a shared required-field helper near the item
handlers that validates an optional string for both presence and non-blank
content, using the field and action to produce the existing error messages.
Replace the repeated validation patterns across handlers including item_create,
item_get/item_delete/item_claim, item_add_label/item_remove_label, and
item_update_state, while preserving each handler’s current required-field
semantics and error behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: f18c1ea7-b508-4109-bd4c-cfbf30bf25e1
📒 Files selected for processing (2)
src/mcp_server.rssrc/mcp_server/item.rs
Discovery tick dispatches purely on the ready-for-work label, so items #184/#185/#186/#187 (go/no-go candidates from #166's spec) whose own description says "Decision pending — not dispatched" got auto-dispatched and re-dispatched across multiple agents anyway -- the prose was never actually enforced. Add a needs-decision label that blocks run_discovery_tick even while ready-for-work is also present. Stripping ready-for-work alone wouldn't have been durable: redispatch unconditionally re-attaches it, so the new label has to keep gating on its own until a human clears it. Agentflare-Agent: claude-code Agentflare-Branch: fix/dispatch-failure-ceiling-any-reason
reconcile_orphaned_jobs already tries to release a dead job's claim via release_and_comment under a with_owner_override scope before calling restore_ready_for_work, but that release is best-effort (let _ = ...) and its failure is silent. When it silently doesn't take, restore_ready_for_work still puts ready-for-work back on the item -- so run_discovery_tick sees it as dispatchable, but the actual dispatch (and redispatch) both refuse with "blocked_by_live_claim" until the claim's TTL naturally expires (up to 4h). Reproduced live on items #185/#187: both sat stuck for the better part of an hour with no visible error, owner strings confirmed to be their own now- dead job's claim. restore_ready_for_work now releases the claim itself too, using the same owner string reconcile_orphaned_jobs already constructs -- defense in depth, not a replacement for the earlier release, so it only ever touches the dead job's own lease. Agentflare-Agent: claude-code Agentflare-Branch: fix/dispatch-failure-ceiling-any-reason
…ent dispatch (#619) WorkflowEngine::recover() resumes non-terminal sdd_loop runs after a daemon restart by calling execute_workflow() directly, bypassing execute_work's run_in_worktree/EXECUTE_WORK_CWD_LOCK entirely (PR #601 only guards fresh dispatches from the job queue). A resumed run's agent dispatch still went through the ambient-cwd run_headless, so it silently inherited whatever worktree another concurrently-running item's chdir happened to have set -- confirmed live on items #186/#187, both binding into task/186's worktree. Thread the item's own worktree path through StepInvocation.cwd (persisted on WorkItemData, read at step-execution time so a resumed run still has it) and dispatch via run_headless_in instead, mirroring the pattern app_send_hook already uses for App workflows. This removes the dependency on global process cwd for this call path rather than trying to widen the lock to cover the recovery path too. Also splits the execute_work_impl dispatch-fixture tests out of work.rs into work_dispatch_fixture_tests.rs to stay under the file's LOC gate. Agentflare-Agent: claude-code Agentflare-Branch: task/191-opencode-agentflare-work-dispatch-doesn Agentflare-Item: 191 Agentflare-Session: 0be92ce2-29ad-46d0-9303-597ede893b7b Co-authored-by: shiva <shiva@gosysinfo.tech>
Summary
item_innerwas a single 411-line function (the file's largest, and its top complexity hotspot per both ast-grep line-span scanning and lean-ctx's own health scoring) dispatching all 13itemactions inline.item_<action>method in a newsrc/mcp_server/item.rssubmodule;item_inneris now just thematchdispatch. No behavior change.Test plan
cargo test --bin agentflare— 437 passedcargo fmt --checkcargo clippy --bin agentflare— no new warningsSummary by CodeRabbit
Bug Fixes
New Features