feat(groups): session Claude unique par groupe via --add-dir (P6)
All checks were successful
Deploy site (production) / build-and-deploy (push) Successful in 21s

Un groupe lance désormais UNE seule session Claude couvrant tous ses
repos/worktrees (--add-dir) au lieu d'une session par repo, pour
travailler en simultané dans une conversation partagée. Le transport WS,
le flow control et l'attach sont inchangés (toujours 1 session, 1 channel).

- shared: SessionSummary.addedDirs/groupId (champs additifs optionnels) ;
  CreateGroupSessionRequest / GroupSessionResponse.
- server: --add-dir dans claude-launcher ; spawn({addDirs,groupId}) +
  persistance (migration #8 : sessions.added_dirs/group_id) ;
  POST /api/v1/groups/:id/session (résolution des dirs cote serveur) ;
  resume re-relie les dirs ; deleteGroup nullifie group_id.
- web: GroupSessionModal (modes feature / checkouts principaux) ;
  createGroupFeature/createGroupSession ; badge « groupe · N depots » ;
  sessionsInGroup dedupliquee.
- site: textes EN/FR « une seule session Claude » + diagramme hub.
- tests: claude-launcher, pty-manager (groupe), group-manager ;
  acceptation P5 etendue (session de groupe multi-repo). 272 tests verts.
This commit is contained in:
2026-06-22 10:06:23 +02:00
parent ca6700d6ce
commit c6ea9f7930
23 changed files with 619 additions and 226 deletions

View File

@@ -97,4 +97,15 @@ describe('GroupManager', () => {
db.prepare('DELETE FROM repos WHERE id = ?').run('repo-a');
expect(gm.getGroup(g.id).repoIds).toEqual(['repo-b']);
});
it('deleteGroup : désorpheline le group_id des sessions de groupe (P6)', () => {
const g = gm.createGroup({ label: 'A' });
// session de groupe liée à g (insérée directement, comme le ferait PtyManager.spawn).
db.prepare(
'INSERT INTO sessions (id, cwd, command, created_at, group_id) VALUES (?, ?, ?, ?, ?)',
).run('sess-1', '/tmp/a', 'claude', new Date().toISOString(), g.id);
expect(gm.deleteGroup(g.id)).toBe(true);
const row = db.prepare('SELECT group_id FROM sessions WHERE id = ?').get('sess-1') as { group_id: string | null };
expect(row.group_id).toBeNull();
});
});