Skip to content

fix(swarm): restore the lead's team task list when a session is resumed - #236

Merged
GoDiao merged 3 commits into
GoDiao:mainfrom
WhitePepperLambSoup:fix/lead-task-list-after-restart
Nov 3, 2026
Merged

GoDiao merged 3 commits into
GoDiao:mainfrom
WhitePepperLambSoup:fix/lead-task-list-after-restart

Conversation

@WhitePepperLambSoup

@WhitePepperLambSoup WhitePepperLambSoup commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Closes #216

After the CLI process of a lead session restarts, the lead can no longer see its team's tasks. getTaskListId() resolves the lead's list from leaderTeamName, which only TeamCreate sets, so the new process falls through to the session id and reads tasks/<session id>/. TaskList then returns nothing, and tasks the lead creates go to a different list than the one the teammates read.

This adds restoreLeaderTeamName() to src/utils/swarm/reconnection.ts. It looks through teams/*/config.json for a team whose leadSessionId is the current session id and sets it as the leader's team name again. If the session led more than one team it uses the newest. restoreLeaderTeamNameOnResume() in the same file wraps it behind isAgentSwarmsEnabled(), and runHeadless calls that after the resumed conversation is loaded, for --resume and --continue.

A forked resume (--resume <id> --fork-session) gets a new session id while the team's leadSessionId is still the old one, so the fork does not take over the team. This is intentional: a fork is a new lead.

Only the task list resolution is restored. AppState.teamContext is still not set for the lead after a restart, so that part is unchanged and I left it out of this PR.

Tests, run on Windows with bun 1.4.4:

  • bun test src/utils/swarm/reconnection.test.ts: 7 pass. Four cover restoreLeaderTeamName(): they set up a team on disk with leadSessionId set to the current session, call it without TeamCreate, and expect getTaskListId() to return the team name and listTasks to find the team's task; a team led by another session is ignored, the newest team wins, and no teams directory is a no-op. Three cover restoreLeaderTeamNameOnResume(): with agent teams enabled the team name is restored, with them disabled nothing is restored, and when the current session id differs from the team's leadSessionId (the fork case) getTaskListId() still returns the session id.
  • Mutations: with the setLeaderTeamName call removed, the first and the newest-team tests fail. With the isAgentSwarmsEnabled() check removed, the disabled test fails. With the leadSessionId comparison removed, the fork test and the other-session test fail.
  • bun test --randomize --seed=1 on that file together with inProcessRunner.test.ts and init.teamCleanup.test.ts: 10 pass.
  • bun test src/server/__tests__/teams.test.ts src/server/__tests__/team-watcher.test.ts: 56 pass.

A full bun test on my Windows machine also shows failures in git, worktree, artifact and session tests (git init and 5 s timeouts). I ran the conversations, sessions and business-flow files on main and on this branch and got the same kinds of failures on both; the swarm tests pass. init.teamCleanup.test.ts passes in the subset runs but hit its 5 s timeout once in the full run, on a cold import.

Not run: I did not start the desktop app, so the restart scenario from the issue (stop a session with a team, send another message) was not exercised end to end. runHeadless makes one call to restoreLeaderTeamNameOnResume() inside the --resume/--continue check, and that call itself has no test; the function it calls does. The desktop lint and vitest were not run because no desktop code changed.

I added a line under Unreleased in CHANGELOG.md.

After the CLI process of a lead session restarts, nothing sets the leader's
team name again, so getTaskListId() falls back to the session id and
TaskList cannot see the team's tasks.

Look for the team whose leadSessionId is the resumed session and set it as
the leader's team name again. runHeadless does this for --resume and
--continue when agent teams are enabled.

Closes GoDiao#216

@GoDiao GoDiao left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the fix, and for the clear description. The approach matches what #216 suggests, and I checked the main points against the code: the team name goes through the same sanitizeName as TeamCreate, leadSessionId equals the session id after a non-fork resume, and a missing teams/ directory or a malformed config.json is skipped without throwing.

Two changes before we merge.

1. Test the call site, not only restoreLeaderTeamName().
The four tests call restoreLeaderTeamName() directly. Nothing covers the call in runHeadless (src/cli/print.ts) or the isAgentSwarmsEnabled() gate, so removing or moving that call would leave every test green and bring #216 back. #216 asks for a test that starts the resume path.

Could you move the gated call into a small function, for example restoreLeaderTeamNameOnResume() in reconnection.ts, have runHeadless call it, and add tests for it:

  • agent teams enabled: the team name is restored and getTaskListId() returns it
  • agent teams disabled: nothing is restored

2. Pin down the forked resume.
--resume <id> --fork-session creates a new session id, while the team's leadSessionId is still the old one, so the fork does not adopt the team. That looks right to us, because a fork is a new lead, and it fits where we are taking agent teams (#162). It is not tested or mentioned yet. Could you:

  • add a test where the current session id differs from the team's leadSessionId (the fork case) and getTaskListId() does not return the team name
  • add one sentence to the PR description saying a forked resume intentionally does not take over the team

CI runs will need our approval after each push, since this is your first contribution. We will approve them.

restoreLeaderTeamNameOnResume() holds the agent-teams check, runHeadless calls it, and the tests cover the enabled, disabled and forked-resume cases.
@WhitePepperLambSoup

Copy link
Copy Markdown
Contributor Author

Pushed 6e46cd8 for both points.

  1. The isAgentSwarmsEnabled() check now lives in restoreLeaderTeamNameOnResume() in reconnection.ts, and runHeadless calls it under the --resume/--continue check. New tests cover agent teams enabled (the team name is restored and getTaskListId() returns it) and disabled (nothing is restored). The one-line call in runHeadless itself is still not exercised by a test; the function it calls is.
  2. Added a test where the current session id differs from the team's leadSessionId (the fork case): getTaskListId() keeps returning the session id. The description now says a forked resume intentionally does not take over the team.

reconnection.test.ts: 7 pass, and 10 pass in a randomized run together with inProcessRunner.test.ts and init.teamCleanup.test.ts. Removing the enabled check, or the leadSessionId comparison, makes the matching new test fail.

@GoDiao GoDiao left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the quick follow-up. Both points are in, and I checked them locally: the tests pass on their own and in a randomized run, and removing the agent-teams check makes the disabled test fail. I added the thanks line to the CHANGELOG entry.

@GoDiao
GoDiao merged commit 15d7705 into GoDiao:main Nov 3, 2026
4 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.

A resumed lead session cannot see its team's tasks

2 participants