Add project grouping to dashboard#16
Conversation
| // Check if team name already exists | ||
| const existing = deps.db.query("SELECT id FROM team WHERE name = ? AND status = 'active'").get(args.name) | ||
| const projectId = deps.directory | ||
| const existing = deps.db.query("SELECT id FROM team WHERE name = ? AND project_id = ? AND status = 'active'").get(args.name, projectId) |
There was a problem hiding this comment.
Same-name teams are now allowed across projects, but branch/worktree names remain global. team_spawn still builds worktree names from only teamName/memberName, and preservedBranchName() still writes to ensemble/preserved/${teamName}/${memberName}. Two projects can now both create my-team/alice, but both would target the same worktree/preserved branch namespace.
There was a problem hiding this comment.
Suggested fix: use a stable team-scoped identifier in branch/worktree names rather than the display team name. team.id seems safer than team.name because names are now only unique within a project.
For example:
- worktree name:
ensemble-${teamId}-${memberName} - preserved branch:
ensemble/preserved/${teamId}/${memberName}
Then update the preservation/listing call sites to use that same namespace.
|
|
||
| if (purge.includes("*")) { | ||
| return deps.db.query("SELECT id, name, time_updated FROM team WHERE status = 'archived' ORDER BY time_updated DESC, name ASC") | ||
| return deps.db.query("SELECT id, name, project_id, time_updated FROM team WHERE status = 'archived' ORDER BY time_updated DESC, name ASC") |
There was a problem hiding this comment.
purge: ['*'] still selects archived teams globally, but the branch cleanup later runs using the current deps.directory. That can purge archived team records from other projects while listing/deleting preserved branches in the current project’s repo path.
There was a problem hiding this comment.
Suggestion: scope purge target resolution to the current project unless cross-project purge is explicitly intended.
For purge: ['*'] - filter archived teams with project_id = deps.directory. For explicit names, you could either apply the same current project filter or add an explicit project selector. If crossproject purge remains supported the preview should show project/path and branch cleanup should run against each target project path instead of always using the current deps.directory..
| time_updated INTEGER NOT NULL, | ||
| lead_agent TEXT | ||
| ); | ||
| INSERT INTO team (id, name, project_id, lead_session_id, status, delegate, time_created, time_updated, lead_agent) |
There was a problem hiding this comment.
Existing teams are migrated into project "default", but startup recovery now filters stale members by input.directory. Pre-migration teams stuck in busy can be skipped after upgrade because their project_id is "default" rather than the current directory.
There was a problem hiding this comment.
handle legacy "default" teams during recovery;
A conservative option is to let start up recovery include project_id = 'default' as a legacy fallback when filtering by input.directory. a more complete option is to migrate active "default" teams into the current project on first startup if there is no ambiguity.
A regression test should cover a version-7 DB with a busy member migrated to "default" -- then startup recovery from a real project dir.
Result
Problem
OpenCode Ensemble currently treats teams as globally scoped in the dashboard and several maintenance paths. That creates two practical issues once a user runs multiple OpenCode instances or works across multiple project directories:
Solution
projectlayer keyed by the team lead's working directory.project_idinstead of globally.projects[].teamsfrom/api/statewhile keeping the existing flatteamsarray for compatibility.team_createto accept an optionalproject_name; otherwise generate a short random display name.Verification
bun run typecheckbun testbun run build