Claude Agent Teams: Refactor sessions to support multi-repository (0..N repos)#950
Claude Agent Teams: Refactor sessions to support multi-repository (0..N repos)#950Connoropolous wants to merge 5 commits intomainfrom
Conversation
Sessions now carry their own `repositoryIds: string[]` instead of being nested under a single repository key. This eliminates the per-repo AgentSessionManager map in favor of a single global instance with an ActivitySinkResolver callback, removes the issueRepositoryCache from RepositoryRouter, and introduces a v4.0 persistence format with migration from v2.0 and v3.0. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
You have run out of free Bugbot PR reviews for this billing cycle. This will reset on April 3. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |
Guided TourRead in this order. Each section builds on the previous. Stop 1: The New Data Model
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cd6fd8695e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…onByIssueId searches all sessions Addresses two valid review comments on PR #950: P1: hasActiveSession callback now filters by repositoryIds.includes(repositoryId) instead of returning true for any repo with an active session on the issue. P2: findSessionByIssueId now uses getSessionsByIssueId (all statuses) with active-session preference, preserving repository mapping after session completion. Also adds getSessionsByIssueId mock to all 12 EdgeWorker test files and updates the missing-session-recovery test assertion to match the new code path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Resolves conflicts in prompt-assembly-utils.ts (repositories plural + default fields) and missing-session-recovery test (attachmentsDir + array syntax). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…all sites Leaf services now accept singular parameters instead of arrays: - PromptBuilder: 6 methods changed from repositories[] to repository - RunnerSelectionService: buildAllowedTools/buildDisallowedTools take repository - GitService: createGitWorktree takes repository - ActivityPoster: 6 methods changed from repositoryIds[] to repositoryId - config-types: createWorkspace handler takes repository (singular) EdgeWorker (orchestrator layer) updated: - Removed all [repository] and [repositoryId] array wrappings at call sites - createLinearAgentSession accepts repository + repositoryIds separately - initializeAgentRunner accepts singular repository - Event handlers use clear primaryRepository naming - Router result extraction happens at proper orchestration boundaries The remaining repositories[0] extractions in EdgeWorker are at legitimate boundaries: router results, ActivitySinkResolver callback, and PromptAssemblyInput data container. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Every repositories[0] extraction is replaced with proper multi-repo handling: - getPrimaryRepository() helper: single authorized place for the [0] convention, documented as "used for workspace creation and git operations" - ActivitySinkResolver: iterates all repositoryIds to find valid tracker - Event handlers (subroutine, validation): pass full repositories array - handleIssueUnassigned/ContentUpdate: resolve and pass all repos - handleAgentSessionCreated: access check iterates ALL repos, selection activity shows ALL repo names, initializeAgentRunner receives full set - initializeAgentRunner: accepts repositories[], maps all IDs to session, uses getPrimaryRepository() only for workspace creation - handleRepositorySelectionResponse: passes full set through - buildAllowedTools/buildDisallowedTools: merge tools from ALL repos via flatMap + Set (union strategy) - postRepositorySelectionActivity: accepts repositories[], formats all names, iterates to find valid tracker for posting - assemblePrompt: uses getPrimaryRepository() for label-based prompt Result: ONE [0] in the entire edge-worker source (inside getPrimaryRepository). Previously there were 6 in EdgeWorker.ts alone plus others in leaf services. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
repositoryIds: string[]directly — supporting 0, 1, or N repositories per sessionMap<string, AgentSessionManager>to a single globalagentSessionManagerwith anActivitySinkResolvercallbackrepositories: RepositoryConfig[](plural);issueRepositoryCache,getCachedRepository,restoreIssueRepositoryCacheremoved entirelyGuided Tour
Layer 1: Data Model (
packages/core)Start here — these define the new shape of everything.
CyrusAgentSession.ts—repositoryIds: string[]added,issueIdremovedconfig-types.ts— Handler callbacks:repository→repositories,repositoryId→repositoryIdsPersistenceManager.ts— NewSerializableEdgeWorkerState(flat), v2→v4 and v3→v4 migration logicLayer 2: Architecture (
packages/edge-worker/src)These are the structural changes that make multi-repo possible.
AgentSessionManager.ts— NewActivitySinkResolvertype; constructor takes resolver callback instead of fixed sink;createLinearAgentSession/createChatSessionacceptrepositoryIdsRepositoryRouter.ts—RepositoryRoutingResult.repositories(plural);selectRepositoriesFromResponse; cache removedtypes.ts—EdgeWorkerEventsemitrepositoryIds: string[]prompt-assembly/types.ts—PromptAssemblyInput.repositories(was singular)Layer 3: EdgeWorker (the big one)
EdgeWorker.ts— SingleagentSessionManagercreated in constructor with resolver;resolveRepositories()helper;findSessionByIssueId()replaces cache lookups; all webhook handlers, serialization, and event emissions updatedLayer 4: Service Methods (mechanical wrapping)
PromptBuilder.ts— 6 methods:repository→repositories, extract[0]!internallyRunnerSelectionService.ts—buildAllowedTools/buildDisallowedTools: same patternActivityPoster.ts— 6 methods:repositoryId→repositoryIdsGitService.ts—createGitWorktree:repository→repositoriesLayer 5: Consumers
ChatSessionHandler.ts— Passes() => noopSinkand[]for repositoryIdsWorkerService.ts(CLI) — Handler signatures and event log messages updatedLayer 6: Tests (17 files)
PersistenceManager.migration.test.ts— 9 tests covering v2→v4, v3→v4, v4 direct, error casesRepositoryRouter.test.ts— Cache tests removed, assertions updated for pluralAgentSessionManager.*.test.ts(5 files) — Constructor and method signature updatesEdgeWorker.*.test.ts(11 files) — Singular manager mock, removed cache references,issueId→issueContextGitService.test.ts— Array wrappingEliminated Patterns (grep-verified zero matches in src/)
agentSessionManagers(old per-repo Map)getCachedRepository/getIssueRepositoryCache/restoreIssueRepositoryCacheselectRepositoryFromResponse(singular)routingResult.repository(singular)session.issueId(deprecated field)Test plan
pnpm build— 15/15 packages cleanpnpm typecheck— 15/15 packages cleanpnpm --filter cyrus-core test:run— 44/44 tests passpnpm --filter cyrus-edge-worker test:run— 536/536 test assertions pass (26 pre-existing EACCES uncaught exceptions from/tmp/test-cyrus-home/logs/owned byagentops— identical onmain)pnpm test:packages:run— all package test assertions pass (gemini-runner has same pre-existing EACCES issue)🤖 Generated with Claude Code