Skip to content

fix(work): supervisor-dispatched jobs can claim their own assigned item - #393

Merged
getappz merged 1 commit into
masterfrom
task/16
Aug 7, 2026
Merged

fix(work): supervisor-dispatched jobs can claim their own assigned item#393
getappz merged 1 commit into
masterfrom
task/16

Conversation

@getappz

@getappz getappz commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Summary

  • agentflare work now sets AGENTFLARE_AGENT from its own explicit --agent flag before claiming, so a job spawned headless by the supervisor (no parent agent process to sniff) resolves its own claim identity to the agent it was actually dispatched as, instead of falling back to cli and losing to item::claim's BlockedByAssignee check against its own assignee.
  • worktree_repo_root now detects when the process's cwd is itself a linked worktree and redirects to the main checkout (via --git-common-dir), so a job spawned from inside an existing item worktree creates its new worktree alongside it in the shared .worktrees/ location instead of nesting inside it.

Both found live while dogfooding the item #15 quota/supervisor feature (PR #392) against this repo's own running daemon and real backend.db -- reproduced, fixed, and re-verified live (restarted the daemon from the exact bug-triggering cwd and confirmed both the claim and the worktree path are now correct).

Test plan

  • cargo test --workspace (targeted: work/supervisor/quota/worktree/branch/main_worktree) -- all pass
  • cargo clippy --all-targets --all-features -- -D warnings -- clean
  • Live repro against the real daemon: before the fix, dispatch failed with item held by ? (0s) -- cannot claim; after, claim succeeds and a real headless claude-code turn starts
  • Live repro of the worktree nesting: daemon restarted from inside .worktrees/task/16; new item worktree correctly landed at the top-level .worktrees/task/13, not nested

Summary by CodeRabbit

  • New Features

    • Added support for resolving the main checkout when working from a linked Git worktree.
    • New worktrees are now created from the main checkout, improving consistency across worktree-based workflows.
    • Explicit agent selections are validated and applied before work begins.
  • Bug Fixes

    • Unknown agents continue to fail immediately with a clear error.
    • Repository path handling now works correctly for both primary and linked worktrees.

Live dogfooding of the item #15 quota/supervisor feature surfaced two bugs
in the pre-existing autonomous dispatch pipeline, both fixed here:

1. `agentflare work`'s claim step resolves its own identity via
   `claims::owner_id()`, which falls back to agent-detector's parent-process/
   env sniffing when AGENTFLARE_AGENT isn't set. Spawned headless by the
   supervisor (no parent agent process, no session env), that sniffing finds
   nothing and falls back further to owner "cli" -- which then loses to
   `item::claim`'s BlockedByAssignee check against whatever agent the item
   was actually assigned to. Fix: `run_work` now sets AGENTFLARE_AGENT from
   its own explicit `--agent` flag before claiming, since that flag is
   already a stronger, unambiguous statement of identity than any sniffing --
   true whether a human types it directly or the supervisor dispatches it,
   so both stay in sync through the one shared code path.

2. Item-worktree creation resolved its target directory from `git rev-parse
   --show-toplevel` on the process's cwd, which is a perfectly valid answer
   from *inside* an existing linked worktree -- so a job spawned from inside
   one item worktree nested a fresh worktree inside it instead of alongside
   it in the shared `.worktrees/` location. Fix: `worktree_repo_root` now
   detects a linked-worktree cwd and redirects to the main checkout (derived
   from `--git-common-dir`, which always points at the shared `.git`
   regardless of which worktree it's read from).

Both confirmed live against this repo's own running daemon and real
backend.db: reproduced the original failures, applied the fix, restarted
the daemon from the exact same (previously bug-triggering) linked-worktree
cwd, and confirmed the claim succeeds and the worktree lands in the correct
shared location.
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds canonical main-worktree resolution, redirects linked-worktree operations to that checkout, and sets the selected explicit agent identity before work claims begin.

Changes

Worktree and agent flow

Layer / File(s) Summary
Canonical main worktree resolution
crates/flare-git-core/src/branch.rs
Adds main_worktree_root, which resolves absolute and repository-relative Git common directories. Tests cover linked and main checkouts.
Worktree root integration
src/mcp_server.rs
Redirects linked-worktree roots to the main checkout while preserving test overrides and fallback behavior.
Explicit agent claim identity
src/cli/work.rs
Canonicalizes an explicit --agent and sets AGENTFLARE_AGENT before claims or worker threads start.

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

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix for supervisor-dispatched jobs claiming their assigned work items.
Description check ✅ Passed The description explains both fixes and their rationale, and it documents testing; the optional reviewer notes section is not included.
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/16

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 `@crates/flare-git-core/src/branch.rs`:
- Around line 190-193: Update main_worktree_root to avoid deriving the checkout
root from --git-common-dir when the repository uses an external Git directory;
instead obtain the canonical main-worktree root explicitly or return None to
skip redirection for that case. Add a regression test covering git init
--separate-git-dir with a linked worktree, verifying worktree placement is not
redirected to the external Git directory.
🪄 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: 2ab7a4aa-b964-4172-8130-4ceadb954f7c

📥 Commits

Reviewing files that changed from the base of the PR and between 0ab3275 and 3d9d8c2.

📒 Files selected for processing (3)
  • crates/flare-git-core/src/branch.rs
  • src/cli/work.rs
  • src/mcp_server.rs

Comment on lines +190 to +193
pub fn main_worktree_root(repo_root: &Path) -> Option<PathBuf> {
let common_dir = run_in_opt(repo_root, &["rev-parse", "--git-common-dir"])?;
repo_root.join(common_dir).parent().map(Path::to_path_buf)
}

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

base="$(mktemp -d)"
trap 'rm -rf "$base"' EXIT

main="$base/main"
git_dir="$base/git-data"
linked="$base/linked"

git init --initial-branch=main --separate-git-dir="$git_dir" "$main"
git -C "$main" config user.email test@example.com
git -C "$main" config user.name test
git -C "$main" commit --allow-empty -m initial
git -C "$main" worktree add -b linked-branch "$linked"

printf 'common dir: '
git -C "$linked" rev-parse --git-common-dir
printf 'primary worktree: '
git -C "$linked" worktree list --porcelain | sed -n '1s/^worktree //p'

Repository: getappz/agentflare

Length of output: 426


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(branch\.rs|mcp_server\.rs)$|branch|worktree' | head -80

printf '%s\n' '--- branch symbols and callers ---'
rg -n -C 8 'main_worktree_root|worktree_repo_root|git-common-dir|worktree list' crates src tests 2>/dev/null | head -240

printf '%s\n' '--- branch outline/size ---'
if command -v ast-grep >/dev/null 2>&1; then
  ast-grep outline crates/flare-git-core/src/branch.rs
fi
wc -l crates/flare-git-core/src/branch.rs 2>/dev/null || true

printf '%s\n' '--- complete separate-git-dir reproduction ---'
base="$(mktemp -d)"
trap 'rm -rf "$base"' EXIT
main="$base/main"
git_dir="$base/git-data"
linked="$base/linked"

git --version
git init --initial-branch=main --separate-git-dir="$git_dir" "$main" >/dev/null
git -C "$main" config user.email test@example.com
git -C "$main" config user.name test
git -C "$main" commit --allow-empty -m initial >/dev/null
git -C "$main" worktree add -b linked-branch "$linked" >/dev/null

printf 'main=%s\n' "$main"
printf 'git_dir=%s\n' "$git_dir"
printf 'linked=%s\n' "$linked"
printf '%s\n' 'main .git:'
cat "$main/.git"
printf '%s\n' 'linked .git:'
cat "$linked/.git"
printf '%s\n' 'linked rev-parse --git-dir:'
git -C "$linked" rev-parse --git-dir
printf '%s\n' 'linked rev-parse --git-common-dir:'
git -C "$linked" rev-parse --git-common-dir
printf '%s\n' 'linked worktree list --porcelain:'
git -C "$linked" worktree list --porcelain
printf '%s\n' 'linked worktree list:'
git -C "$linked" worktree list
printf '%s\n' 'metadata files:'
find "$git_dir" -maxdepth 3 -type f -print | sort

Repository: getappz/agentflare

Length of output: 22949


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base="$(mktemp -d)"
trap 'rm -rf "$base"' EXIT
main="$base/main"
git_dir="$base/git-data"
linked="$base/linked"

git init --initial-branch=main --separate-git-dir="$git_dir" "$main" >/dev/null
git -C "$main" config user.email test@example.com
git -C "$main" config user.name test
git -C "$main" commit --allow-empty -m initial >/dev/null
git -C "$main" worktree add -b linked-branch "$linked" >/dev/null

printf '%s\n' '--- config ---'
git -C "$main" config --local --list --show-origin
printf '%s\n' 'core.worktree values:'
for dir in "$main" "$linked"; do
  printf 'repo=%s\n' "$dir"
  git -C "$dir" config --local --get core.worktree || printf '%s\n' '<unset>'
done

printf '%s\n' '--- rev-parse values ---'
for dir in "$main" "$linked"; do
  printf 'repo=%s\n' "$dir"
  for arg in --show-toplevel --git-dir --git-common-dir --absolute-git-dir; do
    printf '%s: ' "$arg"
    git -C "$dir" rev-parse --path-format=absolute "$arg" 2>&1 || true
  done
done

printf '%s\n' '--- worktree metadata ---'
printf 'common config core.worktree: '
git --git-dir="$git_dir" config --get core.worktree || printf '%s\n' '<unset>'
printf 'main .git file: '
cat "$main/.git"
printf 'linked commondir: '
cat "$git_dir/worktrees/linked/commondir"
printf 'linked gitdir file: '
cat "$git_dir/worktrees/linked/gitdir"
printf '%s\n' '--- environment-sensitive candidates ---'
git -C "$linked" rev-parse --git-path worktrees
git -C "$linked" rev-parse --git-path commondir
git -C "$linked" rev-parse --git-path config

Repository: getappz/agentflare

Length of output: 1544


Handle repositories with an external Git directory before redirecting worktree placement.

--git-common-dir returns the external Git directory for git init --separate-git-dir, so main_worktree_root returns its parent instead of the main checkout. git worktree list --porcelain is not a sufficient replacement because Git reports that external directory as the first worktree in this setup. Use an explicit canonical-root source or skip the redirect for this case. Add a regression test with a separate Git directory and linked worktree.

🤖 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/branch.rs` around lines 190 - 193, Update
main_worktree_root to avoid deriving the checkout root from --git-common-dir
when the repository uses an external Git directory; instead obtain the canonical
main-worktree root explicitly or return None to skip redirection for that case.
Add a regression test covering git init --separate-git-dir with a linked
worktree, verifying worktree placement is not redirected to the external Git
directory.

@getappz
getappz merged commit ae7dc40 into master Aug 7, 2026
23 of 25 checks passed
@getappz
getappz deleted the task/16 branch August 7, 2026 06:31
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