Skip to content

feat: flare doctor — claim-worktree health sweep + safe reclaim (#235) - #305

Merged
getappz merged 3 commits into
masterfrom
item-235-flare-doctor
Jul 22, 2026
Merged

feat: flare doctor — claim-worktree health sweep + safe reclaim (#235)#305
getappz merged 3 commits into
masterfrom
item-235-flare-doctor

Conversation

@getappz

@getappz getappz commented Jul 22, 2026

Copy link
Copy Markdown
Owner

flare doctor — claim-worktree health sweep + safe reclaim

Item: #235
Spec: 2026-07-21-worktree-doctor-sweep

One command agentflare git doctor sweeping all agentflare claim worktrees with health flags:

Flag Detection
dirty git status --porcelain non-empty
stale untouched >N days (default 14, configurable)
missing-worktree path no longer exists
duplicate-branch same branch in >1 worktree
missing-upstream no remote branch
orphaned item completed/cancelled but worktree remains
zombie claiming session pid dead

Usage:

  • agentflare git doctor — text output, exit 1 on violations
  • --format json|markdown — structured output
  • --reclaim — removes clean stale/orphaned/zombie lanes (snapshots first)
  • --force — also reclaim dirty lanes (use with caution)
  • --staleness-days N — custom stale threshold

Touchpoints:

  • crates/flare-git-core/src/doctor.rs (new) — detection + reclaim + formatting
  • crates/flare-git-core/src/lib.rs — added pub mod doctor
  • src/cli/git.rsDoctor variant on GitCommand + handler

Tests: 4 new + 85 existing = 89 passing

Summary by CodeRabbit

  • New Features
    • Added agentflare git doctor to inspect claim worktrees for health issues.
    • Reports dirty, stale, missing, duplicate, orphaned, and other problematic worktrees.
    • Supports text, JSON, and Markdown output formats.
    • Added optional reclaiming of safe worktrees, with a force option.
    • Returns a failure status when violations are detected.

Agentflare-Agent: 1
Agentflare-Branch: item-235-flare-doctor
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@getappz, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 38 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3944747b-4fa6-4512-9998-45857089b077

📥 Commits

Reviewing files that changed from the base of the PR and between 077a229 and daebc93.

📒 Files selected for processing (2)
  • crates/flare-git-core/src/doctor.rs
  • src/cli/git.rs
📝 Walkthrough

Walkthrough

Adds a public git worktree health scanner with lane flags, summaries, reclamation, and text/JSON/Markdown output. Wires these capabilities into a new agentflare git doctor CLI command with configurable staleness, reclaim, force, and format options.

Changes

Git Doctor

Layer / File(s) Summary
Worktree health scanning
crates/flare-git-core/src/doctor.rs, crates/flare-git-core/src/lib.rs
Defines serializable lane health models, parses worktree entries, detects dirty, stale, missing, duplicate, and upstream conditions, computes summaries, and adds scan tests.
Reclaim and report formatting
crates/flare-git-core/src/doctor.rs
Reclaims eligible worktrees with optional force behavior and renders reports as text, JSON, or Markdown.
CLI command integration
src/cli/git.rs
Adds GitCommand::Doctor, doctor arguments, command dispatch, optional reclamation, format selection, and violation-based exit status.

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

Sequence Diagram(s)

sequenceDiagram
  participant Operator
  participant GitDoctorCLI
  participant DoctorCore
  participant GitWorktrees
  Operator->>GitDoctorCLI: Run git doctor
  GitDoctorCLI->>DoctorCore: scan(repo_root, staleness_days)
  DoctorCore->>GitWorktrees: List and inspect worktrees
  GitWorktrees-->>DoctorCore: Worktree health data
  opt Reclaim enabled
    GitDoctorCLI->>DoctorCore: reclaim(report, force)
    DoctorCore->>GitWorktrees: Remove eligible worktrees and prune
  end
  DoctorCore-->>GitDoctorCLI: DoctorReport
  GitDoctorCLI-->>Operator: Render report and exit status
Loading

Possibly related PRs

  • getappz/agentflare#304: Extends the same git CLI dispatch with related worktree orphan auditing and pruning.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: a new flare doctor command for worktree health checks and reclaiming.
Description check ✅ Passed The description covers the summary, usage, and testing, with only the Notes for reviewers section 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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch item-235-flare-doctor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

🧹 Nitpick comments (1)
crates/flare-git-core/src/doctor.rs (1)

158-166: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider named constants for violation thresholds.

5 and 3 are magic numbers; naming them (e.g. const DIRTY_VIOLATION_THRESHOLD: usize = 5;) would aid future tuning/readability.

🤖 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 `@crates/flare-git-core/src/doctor.rs` around lines 158 - 166, Define named
constants for the dirty and stale worktree violation thresholds in the doctor
logic, then replace the magic values in the dirty_count and stale_count
comparisons and messages. Keep the current threshold values and reported
behavior unchanged.
🤖 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/flare-git-core/src/doctor.rs`:
- Around line 72-81: Update parse_worktree_list to represent detached HEAD
entries distinctly instead of encoding them as a branch placeholder, then update
scan’s branch_lanes grouping and has_upstream check to run only when
entry.detached is false; preserve normal branch processing for attached
worktrees.
- Around line 94-98: Update the worktree health evaluation around the existing
Dirty and Stale flag pushes to compute each worktree’s actual elapsed time since
its last activity, and only add HealthFlag::Stale when that duration meets or
exceeds staleness_days. Ensure the stale_count violation and reclaim logic
consume only genuinely stale flags, preserving protection for fresh and
non-dirty worktrees.
- Around line 218-221: Update the lane cleanup branch around remove_dir_all to
handle failures explicitly, logging a diagnostic that includes the affected path
and error details. Only run worktree prune and add the lane to reclaimed after
successful removal, preserving the existing success behavior.
- Around line 350-352: Fix is_dirty to detect uncommitted changes from git
status --porcelain output rather than inverting the exit-status-only run_in_ok
result. Ensure it returns true when the worktree has porcelain status output, so
HealthFlag::Dirty and reclaim safety checks behave correctly.
- Around line 16-24: Update doctor::scan() to consume the orphan results from
worktree::audit_orphans() and emit HealthFlag::Orphaned with the detected item
state. Add the required zombie detection in the same scan path and emit
HealthFlag::Zombie for matching worktrees, so the report counts reflect both
states instead of remaining zero.

In `@src/cli/git.rs`:
- Around line 487-489: Update the scan command’s success check around
report.violations so every failing health flag—including MissingWorktree,
MissingUpstream, Orphaned, and Zombie—causes a nonzero exit status. Either
populate report.violations for each failing flag or use an explicit report
predicate that includes all of them, while preserving success for clean reports.
- Around line 472-480: Update the args.reclaim handling around doctor::reclaim
so status messages do not write to machine-readable stdout. Route both the
reclaimed lane messages and the empty-result message to stderr, while preserving
the existing JSON output on stdout.
- Around line 158-160: Update crates/flare-git-core/src/doctor.rs::scan to use
the configured staleness_days threshold when determining whether an existing
worktree is stale, rather than marking every worktree stale. Compare each
worktree’s actual age against the threshold before adding the stale flag,
preserving fresh lanes as non-stale and allowing --reclaim to target only stale
worktrees. Add coverage for both fresh and stale lanes.
- Around line 150-151: Constrain the DoctorArgs.format argument to the
documented text, json, and markdown values using the CLI’s value-enum or
value-parser mechanism instead of accepting arbitrary strings. Ensure invalid
values such as “jso” are rejected during argument parsing, and add coverage
verifying invalid input fails.

---

Nitpick comments:
In `@crates/flare-git-core/src/doctor.rs`:
- Around line 158-166: Define named constants for the dirty and stale worktree
violation thresholds in the doctor logic, then replace the magic values in the
dirty_count and stale_count comparisons and messages. Keep the current threshold
values and reported behavior unchanged.
🪄 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: 8cd05cab-1972-42c4-b1cb-de93ce2b19dd

📥 Commits

Reviewing files that changed from the base of the PR and between 5743b32 and 077a229.

📒 Files selected for processing (3)
  • crates/flare-git-core/src/doctor.rs
  • crates/flare-git-core/src/lib.rs
  • src/cli/git.rs

Comment on lines +16 to +24
pub enum HealthFlag {
Dirty,
Stale { days: u64 },
MissingWorktree,
DuplicateBranch { other_paths: Vec<String> },
MissingUpstream,
Orphaned { item_state: String },
Zombie,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the relevant file and inspect the implementation around scan/summary/format/reclaim.
git ls-files 'crates/flare-git-core/src/doctor.rs'
wc -l crates/flare-git-core/src/doctor.rs
sed -n '1,260p' crates/flare-git-core/src/doctor.rs
printf '\n--- SECOND CHUNK ---\n'
sed -n '260,420p' crates/flare-git-core/src/doctor.rs

Repository: getappz/agentflare

Length of output: 12986


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1,260p' crates/flare-git-core/src/doctor.rs
printf '\n---\n'
sed -n '260,420p' crates/flare-git-core/src/doctor.rs

Repository: getappz/agentflare

Length of output: 12893


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search the repository for any construction or handling of Orphaned/Zombie states.
rg -n --hidden --glob '!target' 'HealthFlag::(Orphaned|Zombie)|\bOrphaned\b|\bZombie\b|orphaned|zombie' crates

printf '\n--- file list ---\n'
git ls-files 'crates/**/doctor.rs' 'crates/**/claim*' 'crates/**/session*' 'crates/**/worktree*' 'crates/**/snapshot*'

Repository: getappz/agentflare

Length of output: 2505


🏁 Script executed:

#!/bin/bash
set -euo pipefail

wc -l crates/flare-git-core/src/worktree.rs
sed -n '430,580p' crates/flare-git-core/src/worktree.rs
printf '\n---\n'
rg -n 'audit|orphaned|zombie|doctor|HealthFlag' crates/flare-git-core/src/worktree.rs crates/flare-git-core/src/doctor.rs

Repository: getappz/agentflare

Length of output: 10939


doctor::scan() still doesn’t emit orphaned/zombie flags
crates/flare-git-core/src/worktree.rs::audit_orphans() already detects orphaned worktrees, but crates/flare-git-core/src/doctor.rs::scan() never consumes it, and nothing here constructs HealthFlag::Zombie. The doctor report’s orphaned/zombie counts stay at 0, so those states never surface in this path.

🤖 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 `@crates/flare-git-core/src/doctor.rs` around lines 16 - 24, Update
doctor::scan() to consume the orphan results from worktree::audit_orphans() and
emit HealthFlag::Orphaned with the detected item state. Add the required zombie
detection in the same scan path and emit HealthFlag::Zombie for matching
worktrees, so the report counts reflect both states instead of remaining zero.

Comment thread crates/flare-git-core/src/doctor.rs
Comment thread crates/flare-git-core/src/doctor.rs Outdated
Comment thread crates/flare-git-core/src/doctor.rs Outdated
Comment thread crates/flare-git-core/src/doctor.rs
Comment thread src/cli/git.rs Outdated
Comment thread src/cli/git.rs
Comment thread src/cli/git.rs
Comment thread src/cli/git.rs
getappz added 2 commits July 22, 2026 18:27
- is_dirty checked run_in_ok's exit status, but `git status --porcelain`
  always exits 0 -- it was always false, so --reclaim's dirty guard never
  actually fired. Now checks for non-empty output instead.
- Stale was unconditionally pushed for every lane regardless of actual
  age, making every worktree "stale" and, combined with the dirty bug,
  making --reclaim eligible to delete every lane in the repo. Now derived
  from days since the worktree's last commit, compared against the
  threshold.
- Orphaned (item completed/cancelled but worktree remains) was declared
  in the enum/summary/formatters but never computed. Wired scan() to
  accept a sequence_id -> state group_name map (built by the CLI layer
  from the items/states tables, mirroring claimed_sequence_ids) and flag
  lanes whose item is completed or cancelled.
- Zombie (dead claiming-session pid) stays unimplemented -- no existing
  pid-liveness primitive in this codebase to build on; documented on the
  enum variant and left for a follow-up rather than rushed in.
- Fixed clippy (useless format!, collapsible if) and fmt findings from
  CI; added coverage for the two correctness fixes.

Agentflare-Agent: claude-code_2-1-217_agent
Agentflare-Branch: item-235-flare-doctor
- Detached-HEAD worktrees were encoded as a fake branch name ("detached
  at <rev>"), which then flowed into has_upstream and duplicate-branch
  checks meant for real branches. Split into a separate detached_at
  field so detached lanes are excluded from branch-only checks, same as
  they should be.
- reclaim() silently dropped a lane when remove_dir_all failed (e.g. a
  Windows file lock) with no diagnostic at all -- now logs which lane
  and why.
- --format accepted any string and silently fell back to text output on
  a typo; now a clap ValueEnum so an invalid value is a parse error.
- reclaim's status lines were printed to stdout, corrupting
  `--reclaim --format json` output for a script piping it to `jq`. Moved
  to stderr, alongside the JSON/markdown/text report on stdout.

Agentflare-Agent: claude-code_2-1-217_agent
Agentflare-Branch: item-235-flare-doctor
@getappz
getappz merged commit 79d4b98 into master Jul 22, 2026
16 checks passed
@getappz
getappz deleted the item-235-flare-doctor branch July 22, 2026 13:12
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