Skip to content

fix(desktop): repair team-persona mismatch and deduplicate legacy imports#949

Merged
wpfleger96 merged 1 commit into
block:mainfrom
wpfleger96:wpfleger/track-a-team-sync-repair
Jun 10, 2026
Merged

fix(desktop): repair team-persona mismatch and deduplicate legacy imports#949
wpfleger96 merged 1 commit into
block:mainfrom
wpfleger96:wpfleger/track-a-team-sync-repair

Conversation

@wpfleger96

@wpfleger96 wpfleger96 commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Background

This is the fix for the bug this channel is named for: editing a persona's system_prompt in the source config and relaunching the app did not update the prompt shown in the UI.

Wes's #930 ("persona is source of truth at spawn") was supposed to fix exactly this. It made the spawn site resolve the prompt from the live persona record instead of a cached or pack-bundled copy. And it worked — for teams created after #852.

But the symptom persisted for older teams. The reason is upstream of Wes's change: for teams created before #852, sync_team_from_dir never ran. A team/persona identity mismatch combined with a null source_dir meant the sync gate never passed, so the source files on disk were never read back into the persona records. Wes's spawn-time code faithfully read the "live record" — but that record had never been refreshed from disk, so it still held the stale prompt.

#949 closes that upstream gap. It backfills source_dir, fixes the matching predicate so sync actually fires for legacy teams, and dedupes the duplicate personas those legacy imports left behind. Once sync refreshes the records, Wes's #930 spawn-time read finally sees the edited prompt — and system prompts update on relaunch the way they always should have.

Problem

Teams created before #852 have a three-way identity mismatch:

  • PersonaRecord.source_team holds the pack manifest ID (e.g. com.wpfleger.sietch-tabr)
  • Sync/delete predicates compared against TeamRecord.id (a UUID)
  • TeamRecord.source_dir was often null for legacy teams, so the sync gate never passed

This caused sync_team_from_dir to skip these teams entirely, and repeated imports created duplicate PersonaRecords.

Solution

Shared matching predicate: team_persona_key() extracts the directory name from source_dir (the manifest ID), falling back to team.id. Sync, delete-cascade, and import all route through it — the three paths can never disagree about identity.

One-time launch repair (idempotent, self-healing):

  1. Backfills source_dir by matching persona source_team values to directories under teams/
  2. Deduplicates PersonaRecords sharing (source_team, slug) — keeps newest by updated_at desc / id asc, repoints TeamRecord.persona_ids and ManagedAgentRecord.persona_id to the winner
  3. Self-healing scrub removes any dangling references pointing to persona IDs not in the surviving set (crash-window recovery)

Crash safety: Saves are ordered reference-holders-first (teams, agents) before the personas they point to. A crash mid-repair leaves over-pointing (harmless, converges on next launch) rather than dangling references (permanent).

File size: Repair logic extracted to team_repair.rs to stay within the 1020-line cap on teams.rs.

Constraints honored

  • Symlinked source directory is read-only; all repairs touch only JSON records
  • team.id fallback namespace (UUIDs) is near-disjoint from manifest IDs (dotted reverse-domain) — collision near-zero probability (noted, not fixed)

Changes

  • desktop/src-tauri/src/managed_agents/team_repair.rs — new module: team_persona_key(), backfill_source_dirs(), dedup_personas(), sync_team_personas()
  • desktop/src-tauri/src/managed_agents/teams.rs — uses shared team_persona_key in sync/delete/import paths
  • desktop/src-tauri/src/managed_agents/mod.rs — declares and re-exports team_repair

@wpfleger96 wpfleger96 requested a review from a team as a code owner June 10, 2026 18:09
@wpfleger96 wpfleger96 force-pushed the wpfleger/track-a-team-sync-repair branch from 1c222c9 to a65a37e Compare June 10, 2026 19:03
@wpfleger96 wpfleger96 changed the title fix(desktop): repair team sync for pre-PR-852 teams fix(desktop): repair team-persona mismatch and deduplicate legacy imports Jun 10, 2026
…orts

Teams created before PR 852 store the pack manifest ID (e.g.
com.wpfleger.sietch-tabr) in PersonaRecord.source_team but the sync and
delete predicates compared against TeamRecord.id (a UUID). Additionally
TeamRecord.source_dir was often null, so the sync gate never passed.

Introduces team_persona_key() as the single derivation used by sync,
delete-cascade, and import — extracts the directory name from source_dir
(the manifest ID), falling back to team.id for non-directory teams.

On launch, sync_team_personas runs a one-time idempotent repair:
1. Backfills source_dir by matching persona source_team values to
   directories under teams/
2. Deduplicates PersonaRecords sharing (source_team, slug) — keeps
   newest by updated_at desc / id asc, repoints TeamRecord.persona_ids
   and ManagedAgentRecord.persona_id to the winner
3. Self-healing scrub removes any dangling references that point to
   persona IDs not in the surviving set (crash-window recovery)

Saves are ordered reference-holders-first (teams, agents) before the
personas they point to, so a crash mid-repair leaves over-pointing
(harmless) rather than dangling references (permanent).

All repair logic — derivation, backfill, dedup, and the launch
orchestrator — lives in team_repair.rs, keeping teams.rs under the
base 1000-line file size limit without a per-file override.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
@wpfleger96 wpfleger96 force-pushed the wpfleger/track-a-team-sync-repair branch from a65a37e to a4d6620 Compare June 10, 2026 19:07
@wpfleger96 wpfleger96 merged commit 929cc88 into block:main Jun 10, 2026
13 checks passed
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