feat(desktop): worktree agent data sync, retired persona cleanup, mcp_command reconciliation#728
Open
wpfleger96 wants to merge 9 commits into
Open
feat(desktop): worktree agent data sync, retired persona cleanup, mcp_command reconciliation#728wpfleger96 wants to merge 9 commits into
wpfleger96 wants to merge 9 commits into
Conversation
1f346d8 to
22f245a
Compare
55e79bd to
737773a
Compare
737773a to
4481cd9
Compare
4481cd9 to
da60cbe
Compare
504604a to
1403051
Compare
wpfleger96
added a commit
that referenced
this pull request
May 26, 2026
Extracts shared `patch_json_records` helper to eliminate duplicated read→parse→mutate→write boilerplate between scrub and reconcile functions. Fixes retired persona `is_active` guard to handle edge case where display name already ends with " (retired)" but persona is still active. Simplifies `RETIRED_PERSONAS` tuple by dropping unused name field. Adds 3 new reconcile tests (absent key, null value, codex provider).
wpfleger96
added a commit
that referenced
this pull request
May 26, 2026
Extracts shared `patch_json_records` helper to eliminate duplicated read→parse→mutate→write boilerplate between scrub and reconcile functions. Fixes retired persona `is_active` guard to handle edge case where display name already ends with " (retired)" but persona is still active. Simplifies `RETIRED_PERSONAS` tuple by dropping unused name field. Adds 3 new reconcile tests (absent key, null value, codex provider).
372266b to
789e51e
Compare
wpfleger96
added a commit
that referenced
this pull request
May 27, 2026
Extracts shared `patch_json_records` helper to eliminate duplicated read→parse→mutate→write boilerplate between scrub and reconcile functions. Fixes retired persona `is_active` guard to handle edge case where display name already ends with " (retired)" but persona is still active. Simplifies `RETIRED_PERSONAS` tuple by dropping unused name field. Adds 3 new reconcile tests (absent key, null value, codex provider).
f852c06 to
64ce7c2
Compare
Worktree dev builds (SPROUT_SHARE_IDENTITY=1) had empty agent panels because each worktree gets its own data directory that was never seeded. Copy-with-overwrite on every launch syncs the 3 agent JSON files from the canonical dev data dir, so worktrees see the same agents/personas/ teams as the main instance. Also cleans up 6 retired personas (Orchestrator, Researcher, Planner, Builder, Refactor, Reviewer) that persist from before the Solo/Kit/Scout transition -- unmodified ones are removed, user-customized ones are soft-deprecated as "<Name> (retired)" with is_active=false.
Both files grew past the default 500/900-line limits with the worktree sync and retired persona migration additions.
…sona migration The worktree agent-data sync copied managed-agents.json with runtime_pid values intact, causing the worktree instance to kill the canonical instance's running agents. The retired persona migration removed unmodified records, orphaning persona_id references in managed-agents.json and teams.json. The same-dir guard used byte comparison, which fails on case-insensitive APFS. Key changes: - Scrub runtime_pid and 5 other volatile fields from copied managed-agents.json after sync (new scrub_managed_agents_runtime_state) - Always soft-deprecate retired personas (never delete), preserving the user's display name and refreshing updated_at - Use fs::canonicalize for the same-dir guard to handle symlinks and case-insensitive FS - Extract sibling_data_dir helper to DRY legacy_data_dir and canonical_dev_data_dir - Expand module docstring, SHARED_AGENT_FILES doc, and lib.rs ordering comment - Replace tautology test, expand idempotency test to 4 cases, fix clippy &mut Vec → &mut [_] lint
cargo fmt expanded assert! blocks from single lines to multi-line, pushing the file past the 620-line limit.
Existing agents created before #584 have mcp_command = "sprout-mcp-server" stored in managed-agents.json. At spawn, this registers the full 49-tool messaging MCP server — goose prefers live tools over CLI instructions. On every launch, reconcile_provider_mcp_commands() reads the JSON, looks up each agent_command in the discovery table, and patches mcp_command to the canonical value (empty string for goose/claude/codex, "sprout-dev-mcp" for sprout-agent). Unknown/custom agents are left untouched. Signed-off-by: Will Pfleger <wpfleger@block.xyz>
Extracts shared `patch_json_records` helper to eliminate duplicated read→parse→mutate→write boilerplate between scrub and reconcile functions. Fixes retired persona `is_active` guard to handle edge case where display name already ends with " (retired)" but persona is still active. Simplifies `RETIRED_PERSONAS` tuple by dropping unused name field. Adds 3 new reconcile tests (absent key, null value, codex provider).
…tion Main (`ab71ade9`) removed the `migrate_legacy_data_dir` call and `mod migration` declaration. Rebase kept `mod migration` (needed for worktree sync + reconciliation) but left the legacy migration functions and their 8 tests as dead code. This commit removes them and drops the file-size override from 870 to 610.
64ce7c2 to
055fb80
Compare
`isPersonaActive` returned true for all non-builtins regardless of `isActive`, so retired personas (is_active=false, is_builtin=false) still appeared in the agents panel. Fix the predicate to check `isActive` directly, and pass `libraryPersonas` (already filtered) to `UnifiedAgentsSection` instead of raw query data.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Worktree dev builds launched with
SPROUT_SHARE_IDENTITY=1show empty agent panels because each worktree gets its own~/Library/Application Support/<identifier>/data directory that starts with empty defaults. Meanwhile, users who ran Sprout before the Solo/Kit/Scout transition still carry 6 retired personas (Builder, Orchestrator, Planner, Refactor, Researcher, Reviewer) that clutter their persona list. And agents created before #584 still havemcp_command = "sprout-mcp-server"stored, causing goose to register the full 49-tool messaging MCP instead of using the CLI.This PR adds three on-launch data management features plus a frontend fix:
Worktree agent-data sync
sync_shared_agent_data()inmigration.rscopiesmanaged-agents.json,personas.json, andteams.jsonfrom the canonicalxyz.block.sprout.app.devdata directory to the current worktree's data directory, overwriting on every launch. Copy-if-not-exists (the oldmigrate_legacy_data_dirpattern) doesn't work here because pre-existing worktree data dirs already have empty/default files from prior launches.Guards:
SPROUT_SHARE_IDENTITY=1, validSPROUT_PRIVATE_KEY, canonical dir differs from current dir (viafs::canonicalizefor case-insensitive APFS / symlinks), canonical dir exists.After the copy,
scrub_managed_agents_runtime_state()stripsruntime_pidand 5 other process-local fields from the copiedmanaged-agents.jsonusingserde_json::Value. Without this, the worktree'skill_stale_tracked_processessees the canonical instance's PIDs as stale and kills its running agents.agents/packs/is intentionally excluded — recursive directory sync is out of scope. Pack personas appear in the worktree but ACP processes that read pack files at runtime may fail; install packs in the worktree separately if needed.Retired persona migration
migrate_retired_personas()inpersonas.rsappends" (retired)"to retired personas' display names, setsis_active = false, and refreshesupdated_at. Called frommerge_personas()after the existing demotion pass.Always soft-deprecates, never deletes. Removing records would orphan
persona_idreferences inmanaged-agents.jsonandteams.json—resolve_persona_envfails closed on missing personas, blocking agent spawn. The cost is 6 extra records for pre-transition users. Preserves the user's display name (a persona renamed to "My Reviewer" becomes "My Reviewer (retired)"). Idempotent: already-retired records (suffix present ANDis_active = false) are skipped on subsequent launches.The frontend's
isPersonaActivepreviously returnedtruefor all non-builtins regardless ofisActive, so retired personas still appeared in the agents panel. Fixed by checkingpersona.isActivedirectly and passing filteredlibraryPersonastoUnifiedAgentsSectioninstead of raw query data.mcp_commandprovider reconciliationreconcile_provider_mcp_commands()inmigration.rsreadsmanaged-agents.jsonon every launch, looks up eachagent_commandin the discovery table viaknown_acp_provider(), and patchesmcp_commandto the provider's canonical value. Goose/claude/codex get""(no MCP server), sprout-agent gets"sprout-dev-mcp", unknown/custom agents are left untouched. This closes the gap left by #584 which only fixed creation-time defaults — existing agents with stalemcp_command = "sprout-mcp-server"now get corrected automatically.Other changes
patch_json_records()helper shared byscrub_managed_agents_runtime_stateandreconcile_mcp_commands_in_file— eliminates duplicated read→parse→mutate→write boilerplate, includes parse-failure loggingRETIRED_PERSONASconstant from(&str, &str, &str)to(&str, &str)by dropping the unused display name fieldmigrate_legacy_data_dir,migrate_file,LEGACY_FILES) after upstream deletion inab71ade9migration.rsmodule docstring to cover worktree sync and provider reconciliationlib.rssetup hook:sync_shared_agent_data→reconcile_provider_mcp_commands→ ... →restore_managed_agents_on_launchmigration.rs(610) andpersonas.rs(980)