Skip to content

fix(supervisor): resolve each item's own project dir, not the daemon's cwd - #437

Merged
getappz merged 7 commits into
masterfrom
task/63
Aug 11, 2026
Merged

fix(supervisor): resolve each item's own project dir, not the daemon's cwd#437
getappz merged 7 commits into
masterfrom
task/63

Conversation

@getappz

@getappz getappz commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Closes #63.

resolve_project() (src/mcp_server.rs) resolves "the current project" from std::env::current_dir() -- fine for a CLI invocation, but the daemon calls it once through a single AgentflareMcp::default() built at startup from wherever it happened to be launched. That one cwd-bound project was baked into discovery, dispatch, and execution alike. Confirmed live: a daemon started from an unrelated repo's directory never dispatched a ready-for-work item in this project despite 12s discovery ticks for 50+ minutes.

Fix

  • New project_dirs table (crates/agentflare-backend, migration 0010) -- folder→project reverse index, same shape as feat(bridge): daemon polls a registry of repos, not one env var #422's bridge_repos but unconditional (no GitHub-remote gate). resolve_project() upserts into it.
  • AgentflareMcp::for_project_dir(repo_root) -- pins resolution to a given folder instead of process cwd.
  • run_discovery_tick iterates every row in project_dirs, not one cwd-resolved project.
  • dispatch_item includes the item's project folder path as a job arg; WorkItemExecutor/execute_work use it via for_project_dir when present, falling back to prior cwd-based behavior when absent (backward compatible with already-queued jobs or a human running agentflare work directly).
  • New regression test: run_discovery_tick_dispatches_ready_items_from_every_registered_project_not_just_one.

Note on provenance: pushed manually. The dispatched agent (item #63) did this work correctly and verified it, but push_and_open_pr matched the branch task/63 against PR #189 -- an unrelated, already-merged PR from July that happened to share this branch name historically -- and treated it as "PR already exists" without checking it was actually open, so the real work was never pushed or opened as a PR. Rescued from the worktree; verified independently before pushing (build clean, cargo test --bin agentflare supervisor:: 12/12 passing).

Test plan

  • cargo build --bin agentflare -- clean
  • cargo test --bin agentflare supervisor:: -- 12 passed, 0 failed

Summary by CodeRabbit

  • New Features

    • Added project directory registration and persistence.
    • Background discovery now supports multiple registered projects and routes work to each project’s folder.
    • Added project-scoped resolution for links and worktrees.
  • Bug Fixes

    • Improved project path handling for daemon and direct CLI workflows.
    • Preserved compatibility with existing job formats and self-repair tasks.

… project dir, not the daemon's cwd

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

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 59 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

Run ID: 4e6b9d00-c0d9-4cb3-b426-4133c8967712

📥 Commits

Reviewing files that changed from the base of the PR and between 25a098d and 4e2905d.

📒 Files selected for processing (2)
  • src/cli/work.rs
  • src/supervisor.rs
📝 Walkthrough

Walkthrough

The backend now persists project-to-folder mappings. MCP project resolution registers these mappings. Supervisor discovery scans all registered projects and passes each folder path to work jobs, which create project-specific MCP contexts.

Changes

Project directory dispatch

Layer / File(s) Summary
Project directory registry
crates/agentflare-backend/src/db.rs, crates/agentflare-backend/src/migrations/0010_project_dirs.sql, crates/agentflare-backend/src/lib.rs, crates/agentflare-backend/src/project_dir.rs
Adds the project_dirs table, persistence operations, public module export, and tests for round-tripping, updates, and multiple entries.
Project directory registration
src/mcp_server.rs
Adds repository-root-scoped MCP construction and registers canonical project directories during existing and new project resolution.
Multi-project discovery
src/supervisor.rs
Scans all registered project directories, preserves project-specific labels and paths, and dispatches ready items with their folder paths. Tests cover multiple registered projects.
Project-specific work context
src/cli/work.rs, src/supervisor.rs
Extends queued job arguments with an optional repository path. Work execution uses that path when present and retains compatibility with legacy jobs and project-agnostic self-repair jobs.

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

Sequence Diagram(s)

sequenceDiagram
  participant Supervisor
  participant ProjectDirs
  participant WorkQueue
  participant execute_work
  participant AgentflareMcp
  Supervisor->>ProjectDirs: list registered project directories
  ProjectDirs-->>Supervisor: project IDs and folder paths
  Supervisor->>WorkQueue: enqueue item, agent, and folder path
  WorkQueue->>execute_work: invoke job arguments
  execute_work->>AgentflareMcp: create context for repository root
  AgentflareMcp-->>execute_work: project-scoped work context
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the primary supervisor fix: resolving each item's project directory instead of using the daemon's current directory.
Description check ✅ Passed The description explains the problem, solution, compatibility behavior, regression test, and test results, although the reviewer notes section and some template checks are omitted.
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/63

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

getappz added a commit that referenced this pull request Aug 11, 2026
…ini/Cloudflare AI Gateway support (#439)

* feat(flare-proxy): provider registry with native Anthropic/Gemini, zero new deps (item #438)

Replaces the hardcoded 3-provider ProviderKind match (NvidiaNim/OpenRouter/
LmStudio) with a small provider-kind registry: OpenAiCompatible (covers
NVIDIA NIM, OpenRouter, LM Studio, and any future OpenAI-compatible
endpoint via config alone), Anthropic, and Gemini.

Patterns vendored (not depended on) from litellm-rust (avivsinai, MIT) --
see items #436 (rig-core, rejected: streaming not raw-JSON-shaped) and
#437 (litellm-rs rejected for dependency weight; litellm-rust's design is
the reference). Zero new Cargo.toml/Cargo.lock entries: reuses reqwest/
serde_json/futures already in flare-proxy instead of importing an SSE
macro crate, an error crate, or a trait-object abstraction.

- providers/openai_compat.rs: header/auth wiring only -- request/response
  translation stays shape_xlat's existing Anthropic<->OpenAI logic.
- providers/anthropic.rs: near-passthrough. Since flare-proxy's own wire
  format already IS Anthropic Messages, native Anthropic upstream needs no
  shape_xlat at all -- forward.rs proxies the byte stream unchanged.
- providers/gemini.rs: new Anthropic<->Gemini translator, scoped to text +
  tool_calls (no image/audio/video/file parts -- out of scope for this
  proxy's actual traffic). New shape_xlat::gemini_chunk_to_anthropic_sse /
  gemini_finish_stream read Gemini's candidates[0].content.parts shape
  instead of OpenAI's choices[0].delta.

forward.rs restructured to dispatch per ProviderKind before body
translation (each upstream needs a different request shape and URL path,
not just different headers), with a shared stream_translated_sse() helper
for the two paths that do need chunk-by-chunk translation.

65/65 tests pass, clippy clean (-A unsafe_code -A clippy::pedantic),
fmt clean.

Agentflare-Agent: claude-code_2-1-225_agent
Agentflare-Branch: task/438
Agentflare-Item: 438

* feat(flare-proxy): Cloudflare AI Gateway support via cf_gateway_anthropic/cf_gateway_openai (item #438)

Cloudflare AI Gateway is a URL prefix in front of a provider's own wire
protocol, not a distinct protocol -- gateway.ai.cloudflare.com/v1/{acct}/
{gw}/anthropic still speaks Anthropic's real Messages API shape,
.../compat still speaks OpenAI's /chat/completions shape. So this needed
no new ProviderKind: just two new provider prefixes whose base_url is
built from CF_AI_GATEWAY_ACCOUNT_ID/CF_AI_GATEWAY_ID, reusing the
Anthropic and OpenAiCompatible paths added earlier in this item unchanged.

cf-aig-authorization (Cloudflare's own gateway-level auth, separate from
the underlying provider's API key) is optional, set from
CF_AI_GATEWAY_TOKEN via the extra_headers mechanism already added for
OpenRouter. Missing CF_AI_GATEWAY_ACCOUNT_ID/ID fails fast to
default_free() at startup rather than shipping an unusable base_url.

Closes the Cloudflare AI Gateway gap identified earlier this session
(flare-proxy previously had zero Cloudflare support of any kind).

68/68 tests pass, clippy clean, fmt clean. Zero new dependencies (single
file changed).

Agentflare-Agent: claude-code_2-1-225_agent
Agentflare-Branch: task/438
Agentflare-Item: 438

* refactor(flare-proxy): registry.toml-driven providers, no more hardcoded match arms (item #438)

Per appz-cli's registry/toolchains.toml pattern (crates/appz-core/src/
toolchain_registry.rs) -- scoped down to just what flare-proxy needs: no
HTTP-fetch-with-cache-TTL layer, just an embedded TOML file (include_str!)
parsed once via OnceLock. Adding a provider -- another OpenAI-compatible
endpoint, another Cloudflare AI Gateway route -- is now a
registry/providers.toml edit, not a Rust change.

providers/registry.rs owns the load/lookup/resolve logic:
- RegistrySpec mirrors ProviderEntry's shape (prefix, id, kind, base_url,
  api_key_env, extra_headers) plus two fields the hardcoded version didn't
  need: base_url_template ({ENV_VAR} placeholders, for Cloudflare's
  per-user account_id/gateway_id) and gateway_auth_env/gateway_auth_header
  (an extra header sourced from an env var, distinct from the provider's
  own api_key_env -- Cloudflare's cf-aig-authorization).
- resolve() returns None when a template references an unset env var, so
  from_env()'s existing 'fail fast to default_free()' behavior is
  preserved without each provider needing its own bespoke resolver
  function (deleted cloudflare_gateway_base_url/_headers and
  openrouter_headers -- all three are now registry.toml entries).

default_free() now also builds its 3 built-in providers by resolving them
out of the same registry, instead of duplicating base_url/api_key_env/
extra_headers as separate hardcoded ProviderEntry literals -- one source
of truth for provider identity, whether reached via MODEL=... or the
zero-config default path.

toml crate added to flare-proxy's Cargo.toml -- already a workspace
dependency (root Cargo.toml), so this adds zero new entries to
Cargo.lock.

74/74 tests pass, clippy clean, fmt clean.

Agentflare-Agent: claude-code_2-1-225_agent
Agentflare-Branch: task/438
Agentflare-Item: 438

* feat(flare-proxy): download the provider registry on demand, not just embed it (item #438)

The embedded-only registry.toml from the previous commit still required a
new agentflare release to add a provider -- it was baked into the binary
at compile time. This closes that gap the same way appz-cli's toolchain
registry does (crates/appz-core/src/{toolchain_registry,registry_cache}.rs),
scoped down to flare-proxy's single call site: no offline-mode CLI flag,
no generic <T> cache helper, since there's nothing else to share it with.

registry() -- used by find()/resolve()/known_prefixes(), and by this
module's own tests -- reads only the embedded TOML plus whatever's
already cached on disk. It never touches the network, so it stays fast
and deterministic in cargo test (77 tests in 0.02s, unchanged from
before this commit).

ensure_fresh() is the explicit 'download on demand' step: called once
from router() before the first ProviderConfig::from_env() read, it
checks the local cache (~/.agentflare/flare-proxy-providers-registry.json)
against a 24h TTL and, if stale, attempts one 3s-timeout live fetch of
registry/providers.toml's latest published version, merging remote
entries over the embedded ones by prefix. Any failure -- offline, DNS,
non-2xx, malformed TOML -- silently falls back to the embedded copy;
existing installs never break because of a network hiccup, they just
don't get the newest provider until the next successful refresh.

dirs and ureq added to flare-proxy's Cargo.toml -- both already
workspace dependencies via the main agentflare crate, so this adds zero
new entries to Cargo.lock (confirmed via diff).

77/77 tests pass, clippy clean (including a result_large_err fix --
ureq::Error is >270 bytes, fetch_remote() now maps to a plain String
error immediately, matching appz-cli's own Result<String, String>
convention for the same kind of fetch function), fmt clean.

Agentflare-Agent: claude-code_2-1-225_agent
Agentflare-Branch: task/438
Agentflare-Item: 438

* data(flare-proxy): populate registry with the broader OpenAI-compatible provider set (item #438)

Adds 17 more openai_compatible entries alongside the original NVIDIA NIM/
OpenRouter/LM Studio/Cloudflare-Gateway set: OpenAI itself, DeepSeek, Groq,
Mistral, xAI, Together, Perplexity, Fireworks, DeepInfra, Moonshot,
Novita, Hyperbolic, Cerebras, SambaNova, GitHub Models, Cohere
(compatibility API), and local Ollama.

base_url values are each provider's own documented OpenAI-SDK-compatible
endpoint as of this session -- best-effort from public docs/conventions,
not individually live-verified against every provider's current docs
given how many there are. Flagged as such directly in the TOML file. If
one drifts, that's exactly what the on-demand refresh added in the
previous commit exists to fix -- correcting registry/providers.toml
upstream reaches every existing install on its next refresh, no new
release needed.

Two new regression tests: every_registry_entry_has_a_resolvable_static_or_
gateway_url (catches a future copy-paste mistake -- a provider block
missing both base_url and base_url_template -- at test time instead of a
confusing request-time 'no route' error) and
broad_openai_compatible_providers_are_present_and_resolve (locks in that
all 17 new prefixes parse, resolve, and produce a well-formed URL).

79/79 tests pass, clippy clean, fmt clean.

Agentflare-Agent: claude-code_2-1-225_agent
Agentflare-Branch: task/438
Agentflare-Item: 438

* data(flare-proxy): add siliconflow, llm7, requesty free-tier coding providers (item #438)

Three more openai_compatible registry entries, picked for coding use:
siliconflow (Qwen3-Coder-480B, DeepSeek-V3.2/R1, permanently free after
identity verification), llm7 (Qwen2.5-Coder-32B, DeepSeek-R1, no-cost
daily tier), requesty (300+ model router, cleanest ToS rating of the
free aggregators surveyed).

base_url values verified against each provider's own docs (SiliconFlow
quickstart, LLM7 live /v1/models endpoint, Requesty gateway docs), not
guessed. Extended broad_openai_compatible_providers_are_present_and_resolve
to cover all three.

11/11 flare-proxy registry tests pass, clippy clean, fmt clean.

Agentflare-Agent: claude-code_2-1-225_agent
Agentflare-Branch: task/438
Agentflare-Item: 438

* data(flare-proxy): add free-tier quota metadata to provider registry (item #438)

New optional RegistrySpec fields -- free_type, monthly_tokens,
credit_tokens -- record each provider's documented free-tier budget:
recurring-daily/monthly (renews), recurring-uncapped (rate-limited, no
token cap), one-time-initial (signup credit), or unmetered (local).
Populated for all 21 providers with a documented free tier; omitted for
paid-key/pass-through providers (openai, xai, together's paid tier,
perplexity, moonshot, anthropic, cf_gateway_*) with no free-tier data.

Data pulled from OmniRoute's freeModelCatalog.data.ts (pool-deduped to
each provider's max), not estimated -- source noted in the TOML header.

Metadata only: no selection/load-balancing logic reads these fields yet.
That's the natural follow-up once this shape has been used for a bit,
scoped separately per plan.

New test (every_registry_free_type_is_a_known_value) catches a typo'd
free_type value against a known set. 12/12 flare-proxy registry tests
pass, clippy clean, fmt clean.

Agentflare-Agent: claude-code_2-1-225_agent
Agentflare-Branch: task/438
Agentflare-Item: 438

* feat(flare-proxy): free-tier usage tracking and fallback selection (item #438)

New providers::quota module: tracks per-provider token consumption
against the free_type/monthly_tokens/credit_tokens registry metadata
added in the previous commit, and picks the first available provider
from a priority-ordered candidate list.

Design ported (not copied) from two real implementations, researched
and cloned locally for reference:
- LiteLLM's provider budget limiter (BerriAI/litellm, MIT,
  litellm/router_strategy/budget_limiter.py) -- the get-or-init window
  start / reset-if-expired / increment-if-current three-way branch,
  without its Redis multi-instance sync (flare-proxy is one process).
- OmniRoute's emergency fallback (diegosouzapw/OmniRoute, MIT,
  open-sse/services/emergencyFallback.ts) -- fail-open on tracking
  errors, walk a short ordered candidate list rather than re-rank a
  large pool.

Usage persists to flare-proxy-usage.json next to the existing registry
cache (agentflare_home(), now pub(super) for quota.rs to reuse rather
than duplicating the home-dir helper).

Scope boundary, called out in the module doc: this is the tracking/
selection primitive only, not wired into forward.rs's request path yet.
ProviderConfig/ModelRoute resolve to exactly one provider per request
today; using select_best for real routing needs a candidate list there,
and record_consumption needs a real token count, which the streaming
response path doesn't currently extract. Both are separate design
decisions, tracked as the next item #438 follow-up rather than bolted
on here unreviewed.

7 new tests (budget exhaustion, window expiry reset, one-time credits
never resetting, priority-ordered fallback selection). 87/87 flare-proxy
tests pass (stable across repeated runs -- fixed a test-isolation race
by reusing crate::test_env_lock() instead of a module-local mutex, since
AGENTFLARE_HOME_OVERRIDE is a process-global env var shared with
registry.rs's own tests). clippy clean, fmt clean.

Agentflare-Agent: claude-code_2-1-225_agent
Agentflare-Branch: task/438
Agentflare-Item: 438

* fix(flare-proxy): address CodeRabbit findings on PR #439

- forward.rs: apply provider.extra_headers on the Anthropic/Gemini paths too, not just OpenAI-compat -- Cloudflare AI Gateway's cf-aig-authorization was silently dropped for cf_gateway_anthropic
- mod.rs: from_env() route construction now resolves MODEL prefixes (e.g. nvidia_nim) to their registry id (nvidia-nim) before storing provider_id, fixing a 400 "unknown provider" for any prefix whose id differs from its prefix
- gemini.rs: recursively strip JSON-Schema fields Gemini's function-calling API rejects ($schema, etc.) from tool input_schema before sending
- shape_xlat.rs: gemini_finish_stream now reports stop_reason "tool_use" whenever a tool block was opened, even when Gemini's finishReason is "STOP" (its common case for a turn containing a functionCall)
- registry.rs: merge() no longer lets a remote/cached registry entry override an existing provider's base_url/api_key_env, only its quota metadata -- a compromised publish path could otherwise redirect API traffic to an attacker host
- providers.toml: remove github_models -- GitHub retired the Models inference endpoint (410)
- registry.rs: add the missing test_env_lock() guard to resolve_template_substitutes_multiple_placeholders
- Cargo.toml: drop ureq's unused json feature
- forward.rs: remove a dead strip_think_tags() call whose result was discarded

Agentflare-Agent: claude-code_2-1-225_agent
Agentflare-Branch: task/438
Agentflare-Item: 438
Reconciles #63's per-project discovery-tick rewrite with #435's
autonomous in-review sweep (run_review_sweep/self_repair_or_gate),
both of which touched supervisor.rs independently after this branch
was cut.

- enqueue_work_job (new in #435, shared by dispatch_item and
  self_repair_or_gate) now takes an optional folder_path so #63's
  per-project dispatch can still thread its item's own project
  directory through, while self_repair_or_gate (still single-project
  via run_review_sweep's cwd-based resolve_project) passes None.
- Test additions from both branches kept intact and separated
  (seed_ready_item_in_project/its regression test from #63;
  throwaway_repo/seed_in_review_item/seed_gate_label and the six
  review-sweep tests from #435) rather than interleaved -- git's
  merge algorithm had conflated them due to structural similarity
  between two unrelated new functions.

All 19 supervisor:: tests pass, fmt clean, clippy clean (CI flags).

Agentflare-Agent: claude-code
Agentflare-Branch: task/63
Agentflare-Item: 63

@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: 2

🤖 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/mcp_server.rs`:
- Around line 996-1005: Add a context-aware repository-root helper that returns
the root established by for_project_dir, falling back to the process root when
no scoped root exists. Update both register_bridge_repo and register_project_dir
to use this helper instead of Self::repo_root(), and add a regression test
verifying distinct process and scoped roots refresh the scoped project’s
registry entries with the scoped root.

In `@src/supervisor.rs`:
- Around line 144-152: Update dispatch_item and its callers to scope
item_remove_label and item_add_label to the current ProjectBatch rather than
mcp.resolve_project(conn), ensuring queued items from other projects are mutated
successfully. Before comment_impl creates a comment, validate that the item
belongs to the batch project and reject cross-project comments.
🪄 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: db315dfe-2962-4a95-9dec-e7ee427f3475

📥 Commits

Reviewing files that changed from the base of the PR and between 408ad35 and 25a098d.

📒 Files selected for processing (7)
  • crates/agentflare-backend/src/db.rs
  • crates/agentflare-backend/src/lib.rs
  • crates/agentflare-backend/src/migrations/0010_project_dirs.sql
  • crates/agentflare-backend/src/project_dir.rs
  • src/cli/work.rs
  • src/mcp_server.rs
  • src/supervisor.rs

Comment thread src/mcp_server.rs
Comment on lines +996 to +1005
fn register_project_dir(&self, conn: &rusqlite::Connection, project_id: &str) {
let repo_root = Self::repo_root();
let folder_path = std::fs::canonicalize(&repo_root).unwrap_or(repo_root);
let _ = agentflare_backend::project_dir::upsert(
conn,
project_id,
&folder_path.to_string_lossy(),
crate::claims::now(),
);
}

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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

command -v ctx_search || true
command -v ctx_callgraph || true
command -v ctx_compose || true
command -v ctx_read || true
command -v ctx_shell || true
agentflare memory context 2>/dev/null || agentflare memory search 2>/dev/null || true
printf '%s\n' '--- candidate files ---'
git ls-files 'src/mcp_server.rs' 'src/cli/work.rs' '*project_dir*' '*bridge*' '*test*' | head -200
printf '%s\n' '--- relevant symbols ---'
rg -n -C 8 'register_project_dir|register_bridge_repo|for_project_dir|worktree_repo_root_override|resolve_project|repo_root|project_dirs' src/mcp_server.rs src/cli/work.rs

Repository: getappz/agentflare

Length of output: 35424


🏁 Script executed:

printf '%s\n' '--- mcp_server implementation ---'
sed -n '520,660p' src/mcp_server.rs
sed -n '840,1010p' src/mcp_server.rs
printf '%s\n' '--- backend registry upserts ---'
sed -n '1,240p' crates/agentflare-backend/src/project_dir.rs
sed -n '1,240p' crates/agentflare-backend/src/bridge_repo.rs
printf '%s\n' '--- dispatch path ---'
rg -n -C 12 'dispatch_item|project_dirs|folder_path|WorkItemExecutor' src crates/agentflare-jobs
printf '%s\n' '--- existing mcp tests and constructors ---'
rg -n -C 10 'for_project_dir|register_project_dir|register_bridge_repo|project_dir::|bridge_repo::|resolve_project' src/mcp_server/tests src/mcp_server.rs src/cli/work.rs

Repository: getappz/agentflare

Length of output: 50374


🏁 Script executed:

printf '%s\n' '--- supervisor dispatch implementation ---'
rg -n -A 90 -B 10 '^fn dispatch_item|dispatch_item\(' src/supervisor.rs
printf '%s\n' '--- RepoId remote resolution ---'
rg -n -A 100 -B 15 'struct RepoId|impl RepoId|resolve_from_remote' src crates
printf '%s\n' '--- project-dir consumers ---'
rg -n -A 35 -B 15 'project_dir::list|project_dirs::list|folder_path.*PathBuf|PathBuf.*folder_path' src crates
printf '%s\n' '--- focused tests ---'
rg -n -A 80 -B 15 'project_dir|for_project_dir|folder_path|dispatch_item' src/mcp_server/tests src/supervisor.rs src/cli/work.rs

Repository: getappz/agentflare

Length of output: 50375


🏁 Script executed:

printf '%s\n' '--- exact dispatch argument construction ---'
sed -n '235,275p' src/supervisor.rs
printf '%s\n' '--- project directory discovery ---'
sed -n '64,110p' src/supervisor.rs
printf '%s\n' '--- focused test names ---'
rg -n '^\s*fn [a-zA-Z0-9_]*' src/mcp_server.rs src/mcp_server/tests src/cli/work.rs src/supervisor.rs | rg 'project|repo|dispatch|worktree|cwd|root'
printf '%s\n' '--- read-only source invariant verifier ---'
python3 - <<'PY'
from pathlib import Path
import re

mcp = Path("src/mcp_server.rs").read_text()
backend = Path("crates/agentflare-backend/src/project_dir.rs").read_text()
supervisor = Path("src/supervisor.rs").read_text()

def body(name, start_after=0):
    pat = re.compile(rf'fn\s+{name}\s*\([^{{]*\)\s*(?:->[^{{]+)?\s*\{{')
    match = pat.search(mcp, start_after)
    if not match:
        raise SystemExit(f"missing {name}")
    depth = 0
    for i in range(match.end(), len(mcp)):
        if mcp[i] == "{":
            depth += 1
        elif mcp[i] == "}":
            if depth == 0:
                return mcp[match.start():i+1]
            depth -= 1
    raise SystemExit(f"unterminated {name}")

for_project = body("for_project_dir")
bridge = body("register_bridge_repo")
project_dir = body("register_project_dir")
valid_link_branch = mcp[mcp.index("Ok(project) => {"):mcp.index("Err(agentflare_backend::Error::NotFound", mcp.index("Ok(project) => {"))]

checks = {
    "for_project_dir sets worktree override": "worktree_repo_root_override: Some(repo_root)" in for_project,
    "bridge registration reads process root": "let repo_root = Self::repo_root();" in bridge,
    "project-dir registration reads process root": "let repo_root = Self::repo_root();" in project_dir,
    "valid link refreshes bridge registry": "self.register_bridge_repo(conn, &project.id);" in valid_link_branch,
    "valid link refreshes project-dir registry": "self.register_project_dir(conn, &project.id);" in valid_link_branch,
    "project-dir upsert updates by project id": "ON CONFLICT(project_id) DO UPDATE" in backend,
    "discovery forwards registered folder": "Some(folder_path)" in supervisor and "enqueue_work_job(queue, item, agent, Some(folder_path))" in supervisor,
}
for label, ok in checks.items():
    print(f"{'PASS' if ok else 'FAIL'}: {label}")
if all(checks.values()):
    print("RESULT: the scoped override is not used by either registry refresh, and the project-dir row is later used for dispatch.")
else:
    raise SystemExit(1)
PY

Repository: getappz/agentflare

Length of output: 9269


Use the scoped repository root for both registry refreshes.

for_project_dir sets the target root, but register_bridge_repo and register_project_dir still call Self::repo_root(). A daemon worker can overwrite the target project's registry entries with the daemon's repository. Later dispatches can create worktrees in the wrong repository.

Add a context-aware root helper. Use it in both methods. Add a regression test with different process and scoped roots.

🤖 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.rs` around lines 996 - 1005, Add a context-aware
repository-root helper that returns the root established by for_project_dir,
falling back to the process root when no scoped root exists. Update both
register_bridge_repo and register_project_dir to use this helper instead of
Self::repo_root(), and add a regression test verifying distinct process and
scoped roots refresh the scoped project’s registry entries with the scoped root.

Comment thread src/supervisor.rs
Comment on lines +144 to +152
if dispatch_item(
mcp,
queue,
&item,
agent,
&folder_path,
&label_id_by_name,
&ready_id,
) {

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 | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# The lean-ctx tools are unavailable in this review environment.
# Inspect project scoping in the item mutation handlers.
rg -n -C 8 -P 'fn item_(remove_label|add_label)|fn comment_impl|resolve_project\(' src/mcp_server.rs
rg -n -C 5 -P 'dispatch_item\(|item_remove_label|item_add_label|comment_impl' src/supervisor.rs

Repository: getappz/agentflare

Length of output: 12914


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== item mutation definitions and item lookup paths =='
rg -n -C 12 'item_remove_label|item_add_label|comment_impl|ItemRequest|CommentRequest|item::(get|update)|get_by_id|resolve_project' src

printf '%s\n' '== supervisor discovery and MCP construction =='
rg -n -C 12 'project_dirs|ProjectBatch|folder_path|AgentflareMcp|supervisor' src/supervisor.rs src

Repository: getappz/agentflare

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 15 'fn (item_remove_label|item_add_label|comment_impl)|item_remove_label|item_add_label|comment_impl' src
rg -n -C 15 'struct AgentflareMcp|impl AgentflareMcp|fn resolve_project|fn.*item' src

Repository: getappz/agentflare

Length of output: 50375


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== source files =='
git ls-files 'src/*' | sed -n '1,160p'

printf '%s\n' '== mutation symbols across tracked files =='
rg -n -C 10 'item_remove_label|item_add_label|comment_impl|remove_label|add_label' --glob '*.rs' .

Repository: getappz/agentflare

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
for p in Path("src").rglob("*.rs"):
    text = p.read_text(errors="replace")
    terms = ("remove_label", "add_label", "comment_impl", "ItemRequest", "CommentRequest")
    if any(t in text for t in terms):
        print(f"== {p} ==")
        for i, line in enumerate(text.splitlines(), 1):
            if any(t in line for t in terms):
                lo, hi = max(1, i - 8), i + 12
                lines = text.splitlines()
                for n in range(lo, min(len(lines), hi) + 1):
                    print(f"{n}:{lines[n-1]}")
                print()
PY

Repository: getappz/agentflare

Length of output: 50375


🌐 Web query:

getappz/agentflare src/mcp_server.rs item_remove_label comment_impl

💡 Result:

In the getappz/agentflare repository, src/mcp_server.rs is a core file responsible for implementing the project's Model Context Protocol (MCP) server functionality [1][2][3]. The item_remove_label comment implementation (or reference) refers to logic within the agentflare MCP server's skill or tool management system [4]. Based on the codebase structure: 1. Context: src/mcp_server.rs handles the registration and exposure of agentflare's own internal skills (such as skill_search and skill_load) to various supported AI coding agents [4]. 2. Functionality: The project uses a skill-registry crate for managing these skills [4]. Logic regarding labels, items, or skill organization is often found within crates/skill-registry (e.g., src/search.rs or src/load.rs) rather than exclusively in mcp_server.rs [4]. 3. Technical Role: src/mcp_server.rs typically acts as the bridge that maps these registry operations to MCP-compatible tool calls [3][4]. If you are looking for specific code, note that mcp_server.rs underwent significant refactoring as part of the implementation of gateway aggregation and skill discovery [3][4]. Reviewing the crates/skill-registry module alongside src/mcp_server.rs will likely reveal the exact implementation of label removal or modification logic.

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== item label handlers =='
sed -n '830,885p' src/mcp_server/item.rs

printf '%s\n' '== comment create and item resolution =='
rg -n -C 18 'pub.*comment_impl|resolve_item_id|action.as_str\(\)|"create"' src/mcp_server/comment.rs | sed -n '1,260p'

printf '%s\n' '== project-batch discovery and MCP construction =='
sed -n '40,175p' src/supervisor.rs
rg -n -C 10 'struct AgentflareMcp|backend_project_link_override|worktree_repo_root_override|for_test|default\(' src/mcp_server.rs src/mcp_server/*.rs src/dashboard/server.rs | sed -n '1,320p'

Repository: getappz/agentflare

Length of output: 31344


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path

for name, ranges in {
    "src/mcp_server/item.rs": [(204, 216), (840, 884)],
    "src/mcp_server/comment.rs": [(1, 260)],
    "src/supervisor.rs": [(40, 175), (272, 306)],
    "src/mcp_server.rs": [(80, 180), (885, 940)],
}.items():
    p = Path(name)
    if not p.exists():
        continue
    lines = p.read_text(errors="replace").splitlines()
    print(f"== {name} ==")
    for lo, hi in ranges:
        for n in range(max(1, lo), min(len(lines), hi) + 1):
            print(f"{n}:{lines[n-1]}")
PY

Repository: getappz/agentflare

Length of output: 26933


Scope post-dispatch mutations to the batch project.

item_remove_label and item_add_label restrict item IDs to mcp.resolve_project(conn). For another ProjectBatch, both calls fail after enqueueing, and ready-for-work remains, causing redispatch on each tick. comment_impl bypasses resolve_item_id and can create comments across project boundaries. Use project-scoped mutation calls and validate the comment's project.

🤖 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/supervisor.rs` around lines 144 - 152, Update dispatch_item and its
callers to scope item_remove_label and item_add_label to the current
ProjectBatch rather than mcp.resolve_project(conn), ensuring queued items from
other projects are mutated successfully. Before comment_impl creates a comment,
validate that the item belongs to the batch project and reject cross-project
comments.

getappz and others added 5 commits August 11, 2026 14:40
Reconciles per-project folder_path dispatch (item #63) with item #82's
waiting/Wait(reason) tracking and cooling-down/wait logging, which
landed to master independently (#446) while this branch was open.
Both behaviors kept: dispatch_item still threads folder_path, and
both the cooldown and Wait branches now log + increment waiting.

All 19 supervisor:: tests pass, fmt clean, clippy clean (CI flags).

Agentflare-Agent: claude-code
Agentflare-Branch: task/63
Agentflare-Item: 63
Agentflare-Agent: claude-code
Agentflare-Branch: task/63
Agentflare-Item: 63
@getappz
getappz merged commit bacd717 into master Aug 11, 2026
16 checks passed
@getappz
getappz deleted the task/63 branch August 11, 2026 09:59
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.

[ponytail#540] Regex-based over-engineering pre-filter for ponytail-review

1 participant