cc-workspace 4.1.4 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -88,13 +88,13 @@ my-workspace/
88
88
  │ ├── .claude/
89
89
  │ │ ├── settings.json <- env vars + hooks
90
90
  │ │ └── hooks/
91
- │ │ ├── block-orchestrator-writes.sh
92
91
  │ │ ├── session-start-context.sh
93
92
  │ │ ├── validate-spawn-prompt.sh
94
- │ │ └── ... <- 11 scripts
93
+ │ │ └── ... <- 9 scripts (all warning-only)
95
94
  │ ├── CLAUDE.md <- orchestrator profile
96
95
  │ ├── workspace.md <- filled by workspace-init
97
96
  │ ├── constitution.md <- filled by workspace-init
97
+ │ ├── .sessions/ <- session state (gitignored, created per session)
98
98
  │ ├── templates/
99
99
  │ │ ├── workspace.template.md
100
100
  │ │ ├── constitution.template.md
@@ -110,6 +110,70 @@ my-workspace/
110
110
 
111
111
  ---
112
112
 
113
+ ## Parallel sessions (branch isolation)
114
+
115
+ Run multiple features in parallel without branch conflicts.
116
+
117
+ ### The problem
118
+
119
+ When two orchestrator sessions dispatch teammates to the same repo, branches get mixed:
120
+ teammates from session B may branch off session A's code. The result is interleaved commits
121
+ and confused agents.
122
+
123
+ ### The solution
124
+
125
+ Each feature gets a **session** — a named scope that maps to a dedicated `session/{name}`
126
+ branch in each impacted repo. Branches are created from a configurable **source branch**
127
+ (e.g., `preprod`, `develop`) defined per repo in `workspace.md`.
128
+
129
+ ### Setup source branches (one time)
130
+
131
+ In `workspace.md`, add the `Source Branch` column to the service map:
132
+
133
+ ```markdown
134
+ | Service | Repo | Type | CLAUDE.md | Source Branch | Description |
135
+ |----------|-------------|----------|-----------|---------------|----------------|
136
+ | api | ../api | backend | ✓ | preprod | REST API |
137
+ | frontend | ../frontend | frontend | ✓ | preprod | Vue/Quasar SPA |
138
+ ```
139
+
140
+ ### How it works
141
+
142
+ 1. The team-lead identifies impacted repos during planning (Phase 2)
143
+ 2. After plan approval, **Phase 2.5** creates a session:
144
+ - Writes `.sessions/{name}.json` with impacted repos only
145
+ - Spawns a Task subagent to run `git branch session/{name} {source}` in each repo
146
+ - Uses `git branch` (NOT `git checkout -b`) to avoid disrupting other sessions
147
+ 3. Teammates receive the session branch in their spawn prompt — they do NOT create their own branches
148
+ 4. PRs go from `session/{name}` → `source_branch` (never to main directly)
149
+
150
+ ### Session CLI commands
151
+
152
+ ```bash
153
+ cc-workspace session list # show active sessions + branches
154
+ cc-workspace session status feature-auth # commits per repo on session branch
155
+ cc-workspace session close feature-auth # interactive: create PRs, delete branches, clean up
156
+ ```
157
+
158
+ `session close` asks for confirmation before every action (PR creation, branch deletion, JSON cleanup).
159
+
160
+ ### Parallel workflow
161
+
162
+ ```bash
163
+ # Terminal 1
164
+ cd orchestrator/
165
+ claude --agent team-lead
166
+ # → "Implement OAuth" → creates session/feature-auth in api/ + frontend/
167
+
168
+ # Terminal 2
169
+ cd orchestrator/
170
+ claude --agent team-lead
171
+ # → "Add billing" → creates session/feature-billing in api/ + frontend/
172
+ # Both sessions are fully isolated — different branches, no conflicts
173
+ ```
174
+
175
+ ---
176
+
113
177
  ## How it works
114
178
 
115
179
  ### The problem
@@ -149,6 +213,7 @@ parallel in each repo via Agent Teams.
149
213
  ```
150
214
  CLARIFY -> ask max 5 questions if ambiguity
151
215
  PLAN -> write the plan in ./plans/, wait for approval
216
+ SESSION -> create session branches in impacted repos (Phase 2.5)
152
217
  SPAWN -> Wave 1: API/data in parallel
153
218
  Wave 2: frontend with validated API contract
154
219
  Wave 3: infra/config if applicable
@@ -160,14 +225,13 @@ REPORT -> final summary
160
225
  ### Security — path-aware writes
161
226
 
162
227
  The orchestrator can write in `orchestrator/` (plans, workspace.md, constitution.md)
163
- but never in sibling repos. The `block-orchestrator-writes.sh` hook dynamically
164
- detects if the target path is in a repo (presence of `.git/`).
228
+ but never in sibling repos. A `PreToolUse` hook in the team-lead agent frontmatter
229
+ dynamically checks if the target path is inside orchestrator/ before allowing writes.
165
230
 
166
231
  Protection layers:
167
232
  1. `disallowedTools: Bash` in agent frontmatter
168
233
  2. `tools` whitelist in agent frontmatter (note: `allowed-tools` is the skill equivalent)
169
- 3. `PreToolUse` path-aware hook in agent frontmatter
170
- 4. `block-orchestrator-writes.sh` hook in .claude/hooks/
234
+ 3. `PreToolUse` path-aware hook in agent frontmatter (team-lead only — teammates write freely in their worktrees)
171
235
 
172
236
  ---
173
237
 
@@ -200,15 +264,15 @@ next one starts. The plan on disk is the source of truth.
200
264
 
201
265
  ---
202
266
 
203
- ## The 9 hooks + 1 agent-level hook
267
+ ## The 9 hooks (settings.json) + 1 agent-level hook (team-lead frontmatter)
204
268
 
205
269
  All hooks in settings.json are **non-blocking** (exit 0 + warning). No hook blocks the session.
206
270
 
207
271
  | Hook | Event | Effect |
208
272
  |------|-------|--------|
209
- | **block-orchestrator-writes** | `PreToolUse` Write\|Edit\|MultiEdit | Blocks writes in repos, allows orchestrator/. **Agent frontmatter only** (team-lead) — not in settings.json, so teammates can write freely. |
210
- | **validate-spawn-prompt** | `PreToolUse` Teammate | Warning if missing context (rules, UX, tasks) |
211
- | **session-start-context** | `SessionStart` | Injects active plans + first session detection |
273
+ | **path-aware write guard** | `PreToolUse` Write\|Edit\|MultiEdit | Blocks writes outside orchestrator/. **Agent frontmatter only** (team-lead) — not in settings.json, so teammates write freely in worktrees. |
274
+ | **validate-spawn-prompt** | `PreToolUse` Teammate | Warning if missing context (rules, UX, tasks, session branch) |
275
+ | **session-start-context** | `SessionStart` | Injects active plans + active sessions + first session detection |
212
276
  | **user-prompt-guard** | `UserPromptSubmit` | Warning if code requested in a repo |
213
277
  | **subagent-start-context** | `SubagentStart` | Injects active plan + constitution |
214
278
  | **permission-auto-approve** | `PermissionRequest` | Auto-approve Read/Glob/Grep |
@@ -339,6 +403,20 @@ Both `init` and `update` are safe to re-run:
339
403
 
340
404
  ---
341
405
 
406
+ ## Changelog v4.1.4 -> v4.2.0
407
+
408
+ | # | Feature | Detail |
409
+ |---|---------|--------|
410
+ | 1 | **Session management** | Branch isolation for parallel features. Each session creates `session/{name}` branches in impacted repos only. `git branch` (no checkout) to avoid disrupting other sessions. |
411
+ | 2 | **Source branch per repo** | `workspace.md` service map now includes a `Source Branch` column (e.g., `preprod`, `develop`). Session branches are created from this branch. |
412
+ | 3 | **Phase 2.5 in dispatch** | New phase between Plan and Dispatch: creates session JSON + branches via Task subagent. |
413
+ | 4 | **CLI session commands** | `cc-workspace session list/status/close`. Close is interactive — asks confirmation before PRs, branch deletion, JSON cleanup. |
414
+ | 5 | **Session-aware hooks** | `session-start-context.sh` detects active sessions. `validate-spawn-prompt.sh` warns if session branch missing from spawn prompt. |
415
+ | 6 | **Spawn templates updated** | Teammates use `session/{name}` branch (already exists). No more `feature/[name]` — teammates never create their own branches. |
416
+ | 7 | **merge-prep session-aware** | Reads `.sessions/` for branch names and source branches. PRs target source branch, not hardcoded main. |
417
+
418
+ ---
419
+
342
420
  ## Changelog v4.1.0 -> v4.1.4
343
421
 
344
422
  | # | Fix | Detail |
package/bin/cli.js CHANGED
@@ -256,65 +256,10 @@ function generateSettings(orchDir) {
256
256
  fs.writeFileSync(path.join(orchDir, ".claude", "settings.json"), JSON.stringify(settings, null, 2) + "\n");
257
257
  }
258
258
 
259
- // ─── Block hook (inline, always regenerated) ────────────────
260
- function generateBlockHook(hooksDir) {
261
- const blockHook = `#!/usr/bin/env bash
262
- # block-orchestrator-writes.sh v${PKG.version}
263
- # PreToolUse hook: blocks writes to sibling repos. Allows writes within orchestrator/.
264
- set -euo pipefail
265
-
266
- INPUT=$(cat)
267
-
268
- FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.path // empty' 2>/dev/null) || FILE_PATH=""
269
-
270
- if [ -z "$FILE_PATH" ]; then
271
- cat << 'EOF'
272
- {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Cannot determine target path. Delegate to a teammate."}}
273
- EOF
274
- exit 0
275
- fi
276
-
277
- ORCH_DIR="\${CLAUDE_PROJECT_DIR:-.}"
278
- ORCH_ABS="$(cd "$ORCH_DIR" 2>/dev/null && pwd)" || ORCH_ABS=""
279
-
280
- if [ -d "$(dirname "$FILE_PATH")" ]; then
281
- TARGET_ABS="$(cd "$(dirname "$FILE_PATH")" 2>/dev/null && pwd)/$(basename "$FILE_PATH")"
282
- else
283
- TARGET_ABS="$FILE_PATH"
284
- fi
285
-
286
- if [ -n "$ORCH_ABS" ]; then
287
- case "$TARGET_ABS" in
288
- "$ORCH_ABS"/*)
289
- exit 0
290
- ;;
291
- esac
292
- fi
293
-
294
- PARENT_DIR="$(dirname "$ORCH_ABS" 2>/dev/null)" || PARENT_DIR=""
295
- if [ -n "$PARENT_DIR" ]; then
296
- for repo_dir in "$PARENT_DIR"/*/; do
297
- [ -d "$repo_dir/.git" ] || continue
298
- REPO_ABS="$(cd "$repo_dir" 2>/dev/null && pwd)"
299
- case "$TARGET_ABS" in
300
- "$REPO_ABS"/*)
301
- REPO_NAME=$(basename "$REPO_ABS")
302
- cat << EOF
303
- {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"BLOCKED: Cannot write in repo $REPO_NAME/. Delegate to a teammate via Agent Teams."}}
304
- EOF
305
- exit 0
306
- ;;
307
- esac
308
- done
309
- fi
310
-
311
- cat << 'EOF'
312
- {"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"BLOCKED: Write target is outside orchestrator/. Delegate to a teammate."}}
313
- EOF
314
- exit 0
315
- `;
316
- fs.writeFileSync(path.join(hooksDir, "block-orchestrator-writes.sh"), blockHook, { mode: 0o755 });
317
- }
259
+ // ─── Block hook ──────────────────────────────────────────────
260
+ // block-orchestrator-writes is now ONLY in team-lead agent frontmatter.
261
+ // It is NOT in settings.json (would be inherited by teammates, blocking their writes).
262
+ // The generateBlockHook() function was removed in v4.1.4.
318
263
 
319
264
  // ─── CLAUDE.md content ──────────────────────────────────────
320
265
  function claudeMdContent() {
@@ -356,6 +301,7 @@ Run once. Idempotent — can be re-run to re-diagnose.
356
301
  - Templates: \`./templates/\`
357
302
  - Service profiles: \`./plans/service-profiles.md\`
358
303
  - Active plans: \`./plans/*.md\`
304
+ - Active sessions: \`./.sessions/*.json\`
359
305
 
360
306
  ## Skills (9)
361
307
  - **dispatch-feature**: 4 modes, clarify → plan → waves → collect → verify
@@ -382,6 +328,8 @@ Run once. Idempotent — can be re-run to re-diagnose.
382
328
  11. Compact after each cycle
383
329
  12. Hooks are warning-only — never blocking
384
330
  13. Retrospective cycle after each completed feature
331
+ 14. Session branches for parallel isolation — teammates use session/{name}, never create own branches
332
+ 15. Never \`git checkout -b\` in repos — use \`git branch\` (no checkout) to avoid disrupting parallel sessions
385
333
  `;
386
334
  }
387
335
 
@@ -398,9 +346,9 @@ function planTemplateContent() {
398
346
  [Clarify answers]
399
347
 
400
348
  ## Impacted services
401
- | Service | Impacted | Branch | Teammate | Status |
402
- |---------|----------|--------|----------|--------|
403
- | | yes/no | feature/[name] | | ⏳ |
349
+ | Service | Impacted | Session Branch | Teammate | Status |
350
+ |---------|----------|----------------|----------|--------|
351
+ | | yes/no | session/[name] | | ⏳ |
404
352
 
405
353
  ## Waves
406
354
  - Wave 1: [producers]
@@ -518,6 +466,13 @@ function updateLocal() {
518
466
  ok("_TEMPLATE.md updated");
519
467
  }
520
468
 
469
+ // ── .sessions/ (create if missing) ──
470
+ const sessionsDir = path.join(orchDir, ".sessions");
471
+ if (!fs.existsSync(sessionsDir)) {
472
+ mkdirp(sessionsDir);
473
+ ok(".sessions/ created");
474
+ }
475
+
521
476
  // ── NEVER touch: workspace.md, constitution.md, plans/*.md, service-profiles.md ──
522
477
  info(`${c.dim}workspace.md, constitution.md, plans/ — preserved${c.reset}`);
523
478
 
@@ -534,6 +489,7 @@ function setupWorkspace(workspacePath, projectName) {
534
489
  mkdirp(path.join(orchDir, ".claude", "hooks"));
535
490
  mkdirp(path.join(orchDir, "plans"));
536
491
  mkdirp(path.join(orchDir, "templates"));
492
+ mkdirp(path.join(orchDir, ".sessions"));
537
493
  ok("Structure created");
538
494
 
539
495
  // ── Templates ──
@@ -615,6 +571,7 @@ function setupWorkspace(workspacePath, projectName) {
615
571
  if (!fs.existsSync(gi)) {
616
572
  fs.writeFileSync(gi, [
617
573
  ".claude/bash-commands.log", ".claude/worktrees/", ".claude/modified-files.log",
574
+ ".sessions/",
618
575
  "plans/*.md", "!plans/_TEMPLATE.md", "!plans/service-profiles.md", ""
619
576
  ].join("\n"));
620
577
  ok(".gitignore");
@@ -745,6 +702,7 @@ function doctor() {
745
702
  check("plans/", fs.existsSync(path.join(cwd, "plans")), "missing");
746
703
  check("templates/", fs.existsSync(path.join(cwd, "templates")), "missing");
747
704
  check(".claude/hooks/", fs.existsSync(path.join(cwd, ".claude", "hooks")), "missing");
705
+ check(".sessions/", fs.existsSync(path.join(cwd, ".sessions")), "missing — run: npx cc-workspace update");
748
706
  const configured = !fs.readFileSync(path.join(cwd, "workspace.md"), "utf8").includes("[UNCONFIGURED]");
749
707
  check("workspace.md configured", configured, "[UNCONFIGURED] — run: claude --agent workspace-init");
750
708
  } else if (hasOrch) {
@@ -766,6 +724,37 @@ function doctor() {
766
724
  log("");
767
725
  }
768
726
 
727
+ // ─── Session helpers ─────────────────────────────────────────
728
+ function findOrchDir() {
729
+ const cwd = process.cwd();
730
+ if (fs.existsSync(path.join(cwd, "workspace.md"))) return cwd;
731
+ if (fs.existsSync(path.join(cwd, "orchestrator", "workspace.md")))
732
+ return path.join(cwd, "orchestrator");
733
+ return null;
734
+ }
735
+
736
+ function readSessions(orchDir) {
737
+ const sessDir = path.join(orchDir, ".sessions");
738
+ if (!fs.existsSync(sessDir)) return [];
739
+ return fs.readdirSync(sessDir)
740
+ .filter(f => f.endsWith(".json"))
741
+ .map(f => {
742
+ try {
743
+ return JSON.parse(fs.readFileSync(path.join(sessDir, f), "utf8"));
744
+ } catch { return null; }
745
+ })
746
+ .filter(Boolean);
747
+ }
748
+
749
+ function sessionStatusBadge(status) {
750
+ const badges = {
751
+ active: `${c.green}active${c.reset}`,
752
+ closed: `${c.dim}closed${c.reset}`,
753
+ closing: `${c.yellow}closing${c.reset}`,
754
+ };
755
+ return badges[status] || status;
756
+ }
757
+
769
758
  // ─── CLI ────────────────────────────────────────────────────
770
759
  const args = process.argv.slice(2);
771
760
  const command = args[0];
@@ -832,6 +821,15 @@ switch (command) {
832
821
  log(` ${c.cyan}npx cc-workspace doctor${c.reset}`);
833
822
  log(` Check all components are installed and consistent.`);
834
823
  log("");
824
+ log(` ${c.cyan}npx cc-workspace session list${c.reset}`);
825
+ log(` Show all active sessions and their branches.`);
826
+ log("");
827
+ log(` ${c.cyan}npx cc-workspace session status${c.reset} ${c.dim}<name>${c.reset}`);
828
+ log(` Show detailed session info: commits per repo on session branch.`);
829
+ log("");
830
+ log(` ${c.cyan}npx cc-workspace session close${c.reset} ${c.dim}<name>${c.reset}`);
831
+ log(` Interactive close: create PRs, delete branches, clean up.`);
832
+ log("");
835
833
  log(` ${c.cyan}npx cc-workspace version${c.reset}`);
836
834
  log(` Show package and installed versions.`);
837
835
  log("");
@@ -845,6 +843,184 @@ switch (command) {
845
843
  break;
846
844
  }
847
845
 
846
+ case "session": {
847
+ const subCmd = args[1];
848
+ const orchDir = findOrchDir();
849
+ if (!orchDir) {
850
+ fail("No orchestrator/ found. Run from workspace root or orchestrator/.");
851
+ process.exit(1);
852
+ }
853
+
854
+ switch (subCmd) {
855
+ case "list": {
856
+ log(BANNER_SMALL);
857
+ step("Active sessions");
858
+ const sessions = readSessions(orchDir);
859
+ if (sessions.length === 0) {
860
+ info(`${c.dim}No sessions found in .sessions/${c.reset}`);
861
+ } else {
862
+ for (const s of sessions) {
863
+ const repoCount = Object.keys(s.repos || {}).length;
864
+ log(` ${c.bold}${s.name}${c.reset} ${sessionStatusBadge(s.status)} ${c.dim}created: ${s.created}${c.reset} ${c.dim}repos: ${repoCount}${c.reset}`);
865
+ for (const [name, repo] of Object.entries(s.repos || {})) {
866
+ const branchIcon = repo.branch_created ? `${c.green}✓${c.reset}` : `${c.red}✗${c.reset}`;
867
+ log(` ${c.dim}├──${c.reset} ${name} ${repo.session_branch} ${branchIcon}`);
868
+ }
869
+ }
870
+ }
871
+ log("");
872
+ break;
873
+ }
874
+
875
+ case "status": {
876
+ const sessionName = args[2];
877
+ if (!sessionName) {
878
+ fail("Usage: cc-workspace session status <name>");
879
+ process.exit(1);
880
+ }
881
+ log(BANNER_SMALL);
882
+ const sessionFile = path.join(orchDir, ".sessions", `${sessionName}.json`);
883
+ if (!fs.existsSync(sessionFile)) {
884
+ fail(`Session "${sessionName}" not found`);
885
+ process.exit(1);
886
+ }
887
+ const session = JSON.parse(fs.readFileSync(sessionFile, "utf8"));
888
+ step(`Session: ${session.name}`);
889
+ log(` ${c.dim}Status${c.reset} ${sessionStatusBadge(session.status)}`);
890
+ log(` ${c.dim}Created${c.reset} ${session.created}`);
891
+ log("");
892
+
893
+ for (const [name, repo] of Object.entries(session.repos || {})) {
894
+ const repoPath = path.resolve(orchDir, repo.path);
895
+ log(` ${c.bold}${name}${c.reset} ${c.dim}(${repo.path})${c.reset}`);
896
+ log(` source: ${repo.source_branch} → session: ${repo.session_branch}`);
897
+ if (repo.branch_created && fs.existsSync(path.join(repoPath, ".git"))) {
898
+ try {
899
+ const commits = execSync(
900
+ `git -C "${repoPath}" log ${repo.session_branch} --oneline --not ${repo.source_branch} 2>/dev/null || echo "(no commits yet)"`,
901
+ { encoding: "utf8", timeout: 5000 }
902
+ ).trim();
903
+ log(` ${c.dim}commits:${c.reset}`);
904
+ for (const line of commits.split("\n").slice(0, 10)) {
905
+ log(` ${line}`);
906
+ }
907
+ } catch {
908
+ log(` ${c.yellow}(could not read commits)${c.reset}`);
909
+ }
910
+ }
911
+ log("");
912
+ }
913
+ break;
914
+ }
915
+
916
+ case "close": {
917
+ const sessionName = args[2];
918
+ if (!sessionName) {
919
+ fail("Usage: cc-workspace session close <name>");
920
+ process.exit(1);
921
+ }
922
+ const sessionFile = path.join(orchDir, ".sessions", `${sessionName}.json`);
923
+ if (!fs.existsSync(sessionFile)) {
924
+ fail(`Session "${sessionName}" not found`);
925
+ process.exit(1);
926
+ }
927
+ const session = JSON.parse(fs.readFileSync(sessionFile, "utf8"));
928
+
929
+ log(BANNER_SMALL);
930
+ step(`Closing session: ${session.name}`);
931
+ log("");
932
+
933
+ const readline = require("readline");
934
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
935
+ const ask = (q) => new Promise(r => rl.question(q, r));
936
+
937
+ (async () => {
938
+ // Step 1: offer to create PRs
939
+ for (const [name, repo] of Object.entries(session.repos || {})) {
940
+ if (!repo.branch_created) continue;
941
+ const repoPath = path.resolve(orchDir, repo.path);
942
+ const answer = await ask(
943
+ ` Create PR ${c.cyan}${repo.session_branch}${c.reset} → ${c.cyan}${repo.source_branch}${c.reset} in ${c.bold}${name}${c.reset}? [y/N] `
944
+ );
945
+ if (answer.toLowerCase() === "y") {
946
+ try {
947
+ const result = execSync(
948
+ `cd "${repoPath}" && gh pr create --base "${repo.source_branch}" --head "${repo.session_branch}" --title "${session.name}: ${name}" --body "Session: ${session.name}"`,
949
+ { encoding: "utf8", timeout: 30000 }
950
+ );
951
+ ok(`PR created: ${result.trim()}`);
952
+ } catch (e) {
953
+ fail(`PR creation failed: ${e.stderr || e.message}`);
954
+ }
955
+ }
956
+ }
957
+
958
+ // Step 2: offer to delete session branches
959
+ for (const [name, repo] of Object.entries(session.repos || {})) {
960
+ if (!repo.branch_created) continue;
961
+ const repoPath = path.resolve(orchDir, repo.path);
962
+ // Check for unpushed commits before offering deletion
963
+ let unpushed = "";
964
+ try {
965
+ unpushed = execSync(
966
+ `git -C "${repoPath}" log "${repo.session_branch}" --oneline --not --remotes 2>/dev/null`,
967
+ { encoding: "utf8", timeout: 5000 }
968
+ ).trim();
969
+ } catch { /* branch may not have remote tracking */ }
970
+ if (unpushed) {
971
+ warn(`${name}: ${unpushed.split("\\n").length} unpushed commit(s) on ${repo.session_branch}`);
972
+ }
973
+ const answer = await ask(
974
+ ` Delete branch ${c.cyan}${repo.session_branch}${c.reset} in ${c.bold}${name}${c.reset}?${unpushed ? ` ${c.red}(has unpushed commits)${c.reset}` : ""} [y/N] `
975
+ );
976
+ if (answer.toLowerCase() === "y") {
977
+ try {
978
+ // Use -D (force) — user already confirmed, branch may not be merged yet
979
+ execSync(`git -C "${repoPath}" branch -D "${repo.session_branch}"`,
980
+ { encoding: "utf8", timeout: 5000 });
981
+ ok(`Branch deleted in ${name}`);
982
+ } catch (e) {
983
+ fail(`Branch deletion failed in ${name}: ${e.stderr || e.message}`);
984
+ }
985
+ }
986
+ }
987
+
988
+ // Step 3: offer to delete session JSON
989
+ const answer = await ask(
990
+ ` Delete session file ${c.dim}.sessions/${sessionName}.json${c.reset}? [y/N] `
991
+ );
992
+ if (answer.toLowerCase() === "y") {
993
+ fs.unlinkSync(sessionFile);
994
+ ok("Session file deleted");
995
+ } else {
996
+ session.status = "closed";
997
+ fs.writeFileSync(sessionFile, JSON.stringify(session, null, 2) + "\n");
998
+ ok("Session marked as closed");
999
+ }
1000
+
1001
+ rl.close();
1002
+ log("");
1003
+ })();
1004
+ break;
1005
+ }
1006
+
1007
+ default:
1008
+ if (!subCmd) {
1009
+ log(BANNER_SMALL);
1010
+ log(`\n ${c.bold}Usage:${c.reset}`);
1011
+ log(` ${c.cyan}cc-workspace session list${c.reset} ${c.dim}Show active sessions${c.reset}`);
1012
+ log(` ${c.cyan}cc-workspace session status${c.reset} ${c.dim}<name>${c.reset} ${c.dim}Detailed session info${c.reset}`);
1013
+ log(` ${c.cyan}cc-workspace session close${c.reset} ${c.dim}<name>${c.reset} ${c.dim}Interactive close${c.reset}`);
1014
+ log("");
1015
+ } else {
1016
+ fail(`Unknown session subcommand: ${subCmd}`);
1017
+ log(` Usage: ${c.cyan}cc-workspace session${c.reset} ${c.dim}list | status <name> | close <name>${c.reset}`);
1018
+ process.exit(1);
1019
+ }
1020
+ }
1021
+ break;
1022
+ }
1023
+
848
1024
  default: {
849
1025
  fail(`Unknown command: ${command}`);
850
1026
  log(` Run: ${c.cyan}npx cc-workspace --help${c.reset}`);
@@ -16,14 +16,26 @@ maxTurns: 50
16
16
 
17
17
  You are a focused implementer. You receive tasks and deliver clean code.
18
18
 
19
+ ## Git workflow (CRITICAL — do this first)
20
+ You are in a temporary worktree. If you don't commit, YOUR WORK WILL BE LOST.
21
+
22
+ 1. **FIRST**: check out the session branch specified in your instructions:
23
+ `git checkout session/{name}`
24
+ 2. **Verify**: `git branch --show-current` must show `session/{name}`
25
+ 3. **Commit after each logical unit** — never wait until the end
26
+ 4. **Before reporting back**: `git status` must show clean working tree.
27
+ If anything is uncommitted: COMMIT IT NOW before reporting.
28
+
19
29
  ## Workflow
20
- 1. Read the repo's CLAUDE.md follow its conventions strictly
21
- 2. Implement the assigned tasks from the plan
22
- 3. Use the **LSP tool** for code navigation (go-to-definition, find-references)
23
- 4. Run existing tests fix any regressions you introduce
24
- 5. Identify and remove dead code exposed by your changes
25
- 6. Commit on the feature branch with conventional commits
26
- 7. Report back: files changed, tests pass/fail, dead code found, blockers
30
+ 1. Check out the session branch (see Git workflow above)
31
+ 2. Read the repo's CLAUDE.md follow its conventions strictly
32
+ 3. Implement the assigned tasks from the plan
33
+ 4. Use the **LSP tool** for code navigation (go-to-definition, find-references)
34
+ 5. Run existing tests fix any regressions you introduce
35
+ 6. Identify and remove dead code exposed by your changes
36
+ 7. Commit on the session branch with conventional commits — after each unit, not at the end
37
+ 8. Before reporting: `git status` — must be clean. `git log --oneline -5` — include in report
38
+ 9. Report back: files changed, tests pass/fail, dead code found, commits (hash+message), blockers
27
39
 
28
40
  ## Rules
29
41
  - Follow existing patterns in the codebase — consistency over preference
@@ -61,6 +61,60 @@ On startup, check if `./workspace.md` contains `[UNCONFIGURED]`.
61
61
  | **C — Go direct** | Immediate dispatch, no interactive plan |
62
62
  | **D — Single-service** | 1 repo, no waves, for targeted fixes |
63
63
 
64
+ ## Session management
65
+
66
+ Sessions provide branch isolation when running multiple features in parallel.
67
+ Each session maps to a `session/{name}` branch in each impacted repo.
68
+
69
+ ### On startup: detect active sessions
70
+ After loading workspace.md, scan `./.sessions/` for active session JSON files.
71
+ If active sessions exist, display them:
72
+ > Active sessions: [name1] (repos: api, front), [name2] (repos: api)
73
+
74
+ ### Creating a session (Phase 2.5 — after Plan approval, before Dispatch)
75
+ 1. Derive the session name from the feature name (slugified, e.g., `feature-auth`)
76
+ 2. Read `workspace.md` for the source branch per repo (Source Branch column)
77
+ 3. Identify which repos are impacted by the plan
78
+ 4. Write `.sessions/{name}.json`:
79
+ ```json
80
+ {
81
+ "name": "feature-auth",
82
+ "created": "2026-02-25",
83
+ "status": "active",
84
+ "repos": {
85
+ "api": {
86
+ "path": "../api",
87
+ "source_branch": "preprod",
88
+ "session_branch": "session/feature-auth",
89
+ "branch_created": true
90
+ }
91
+ }
92
+ }
93
+ ```
94
+ 5. Spawn a Task subagent (with Bash) to create the branches:
95
+ - `git -C ../[repo] branch session/{name} {source_branch}` for each repo
96
+ - CRITICAL: use `git branch` (NOT `git checkout -b`) — checkout would
97
+ disrupt other sessions' working directories
98
+ - If the branch already exists, verify it and skip creation
99
+ 6. Verify branches were created (Task subagent reports back)
100
+ 7. Update the session JSON: set `branch_created: true` for each successful branch
101
+
102
+ ### During dispatch
103
+ - Include the session branch in every teammate spawn prompt
104
+ - Teammates use the session branch — they do NOT create their own branches
105
+ - The spawn prompt must say: "Branch session/{name} ALREADY EXISTS.
106
+ Create your worktree from this branch. ALL commits go on this branch."
107
+
108
+ ### During collection
109
+ - Verify commits are on the session branch via Task subagent:
110
+ `git -C ../[repo] log session/{name} --oneline`
111
+ - If a teammate committed on a different branch, flag it as a blocker
112
+
113
+ ### Session close
114
+ Session close is handled by the CLI: `cc-workspace session close {name}`
115
+ The team-lead does NOT close sessions — the user does via the CLI.
116
+ Close requires user approval before each action (PR, branch delete, JSON delete).
117
+
64
118
  ## Auto-discovery of repos
65
119
 
66
120
  On startup AND during config:
@@ -102,10 +156,16 @@ Explore subagents are read-only, they do NOT need a worktree.
102
156
  ## Commit granularity enforcement
103
157
 
104
158
  When collecting teammate reports:
159
+ - **FIRST: verify commits exist on the session branch** — spawn a Task subagent (Bash):
160
+ `git -C ../[repo] log session/{name} --oneline -10`
161
+ If 0 commits → the teammate forgot to commit. DO NOT accept the report.
162
+ Re-dispatch with explicit instruction to checkout + commit.
105
163
  - **Check commit count vs plan** — the plan defines N commit units, the teammate must have N+ commits
106
164
  - **Flag giant commits** — any commit >400 lines gets flagged in the session log
107
165
  - **If a teammate made a single commit for all tasks**: ask them to split via SendMessage
108
166
  before accepting the wave as complete
167
+ - **If teammate reports "done" but 0 commits**: the worktree was likely cleaned up.
168
+ Check if the worktree still exists. If lost, re-dispatch entirely.
109
169
  - **Progress tracker** in the plan must be updated after each teammate report
110
170
 
111
171
  ## What you NEVER do
@@ -117,9 +177,12 @@ When collecting teammate reports:
117
177
  - Accept a single giant commit covering multiple tasks — enforce atomic commits
118
178
  - Let the context grow (compact after each cycle)
119
179
  - Launch wave 2 before wave 1 has reported
180
+ - Create branches with `git checkout -b` in repos — always `git branch` (no checkout)
181
+ - Let teammates create their own branches — they use the session branch
120
182
 
121
183
  ## What you CAN write
122
184
  - Plans in `./plans/`
185
+ - Session files in `./.sessions/`
123
186
  - `./workspace.md` and `./constitution.md`
124
187
  - Any file in your orchestrator/ directory
125
188
 
@@ -39,9 +39,10 @@ Check silently (no questions to the user):
39
39
  | 4 | `./templates/workspace.template.md` exists | Flag |
40
40
  | 5 | `./templates/constitution.template.md` exists | Flag |
41
41
  | 6 | `./.claude/settings.json` exists and contains `AGENT_TEAMS` + `SUBAGENT_MODEL` | Regenerate if missing |
42
- | 7 | `./.claude/hooks/` contains all 11 hooks | List the missing ones |
42
+ | 7 | `./.claude/hooks/` contains all 9 hooks | List the missing ones |
43
43
  | 8 | All hooks are executable (`chmod +x`) | Auto-fix |
44
44
  | 9 | `./CLAUDE.md` exists | Flag |
45
+ | 10 | `./.sessions/` exists | Create the directory |
45
46
 
46
47
  ### Phase 2: Global diagnostic
47
48
 
@@ -83,10 +84,10 @@ Re-run: npx cc-workspace update --force
83
84
  4. Present a summary table to the user:
84
85
 
85
86
  ```
86
- | Repo | Type | CLAUDE.md | .claude/ | Tests | Git clean |
87
- |------|------|-----------|----------|-------|-----------|
88
- | apidocker | Laravel | ✅ | ✅ hooks:3 | ✅ pest | ✅ |
89
- | frontend | Vue/Quasar | ✅ | ❌ | ✅ vitest | ⚠️ 12 files |
87
+ | Repo | Type | CLAUDE.md | Source Branch | .claude/ | Tests | Git clean |
88
+ |------|------|-----------|---------------|----------|-------|-----------|
89
+ | apidocker | Laravel | ✅ | preprod | ✅ hooks:3 | ✅ pest | ✅ |
90
+ | frontend | Vue/Quasar | ✅ | preprod | ❌ | ✅ vitest | ⚠️ 12 files |
90
91
  ```
91
92
 
92
93
  5. Regenerate `./plans/service-profiles.md` with all collected info
@@ -119,6 +120,19 @@ If some repos don't have a `CLAUDE.md`:
119
120
 
120
121
  ### Phase 4: Interactive configuration
121
122
 
123
+ **If `./workspace.md` is already configured** (no `[UNCONFIGURED]`):
124
+
125
+ Check for missing columns or outdated info:
126
+ 1. If the service map is missing the `Source Branch` column:
127
+ - For each repo, detect the likely source branch:
128
+ - `git -C ../repo symbolic-ref refs/remotes/origin/HEAD 2>/dev/null` (strip refs/remotes/origin/)
129
+ - Or check for common branches: `preprod`, `develop`, `main`, `master`
130
+ - Or read from existing sections (e.g., "Branches de travail" notes)
131
+ - Present the proposed source branches and ask for confirmation
132
+ - Add the `Source Branch` column to the existing service map table
133
+ 2. If new repos are detected (not in the service map), propose adding them
134
+ 3. Update the orchestrator version in the header
135
+
122
136
  **If `./workspace.md` contains `[UNCONFIGURED]`:**
123
137
 
124
138
  1. Read `./templates/workspace.template.md` — this is the reference structure.
@@ -134,6 +148,10 @@ If some repos don't have a `CLAUDE.md`:
134
148
  **Service map section:**
135
149
  - Present detected repos with their auto-detected type
136
150
  - Pre-fill roles based on CLAUDE.md files read in Phase 3
151
+ - For each repo, ask the **Source Branch** (the integration branch from which
152
+ session branches will be created, e.g., `preprod`, `develop`, `main`).
153
+ Pre-fill by detecting the default branch: `git -C ../repo symbolic-ref refs/remotes/origin/HEAD 2>/dev/null`
154
+ or fallback to `main`
137
155
  - Ask for confirmation + corrections
138
156
  - For each service: role in one sentence
139
157
 
@@ -175,7 +193,7 @@ Present a summary table:
175
193
  ╠═══════════════════════════════════════════════════════╣
176
194
  ║ ║
177
195
  ║ Orchestrator structure ✅ OK ║
178
- ║ Hooks (11/11) ✅ OK ║
196
+ ║ Hooks (9/9) ✅ OK ║
179
197
  ║ Settings (Agent Teams) ✅ OK ║
180
198
  ║ Global components ✅ OK (or ⚠️ details) ║
181
199
  ║ workspace.md ✅ Configured ║
@@ -19,7 +19,12 @@ Scope: ONLY inter-service alignment. Not code quality, not bugs.
19
19
 
20
20
  ## Setup
21
21
 
22
- Read `./workspace.md` for the service map.
22
+ 1. Read `./workspace.md` for the service map
23
+ 2. Check `./.sessions/` for the active session related to the current feature
24
+ - If a session exists, all checks MUST run against the **session branch**
25
+ (e.g., `session/feature-auth`), not against `main` or the default branch
26
+ - Use `git -C ../[repo] show session/{name}:[file]` to read files from the
27
+ session branch without checking it out
23
28
 
24
29
  ## Checks (parallel Explore subagents via Task, Haiku)
25
30
 
@@ -88,6 +88,41 @@ vs done per service, visible at a glance.
88
88
 
89
89
  Independent services go in the same wave. Save. **Present plan, wait for approval.**
90
90
 
91
+ ## Phase 2.5: Session setup (branch isolation)
92
+
93
+ After plan approval and before dispatch:
94
+
95
+ 1. Derive session name from feature name (slug: lowercase, hyphens, no spaces)
96
+ 2. Read `./workspace.md` — extract the `Source Branch` column for each service
97
+ 3. Identify repos impacted by the plan
98
+ 4. Write `./.sessions/{session-name}.json`:
99
+ ```json
100
+ {
101
+ "name": "{session-name}",
102
+ "created": "{date}",
103
+ "status": "active",
104
+ "repos": {
105
+ "{service}": {
106
+ "path": "../{service}",
107
+ "source_branch": "{from workspace.md}",
108
+ "session_branch": "session/{session-name}",
109
+ "branch_created": false
110
+ }
111
+ }
112
+ }
113
+ ```
114
+ 5. Spawn a **Task subagent with Bash** to create branches in each impacted repo:
115
+ ```
116
+ git -C ../[repo] branch session/{name} {source_branch}
117
+ ```
118
+ - Use `git branch` — NOT `git checkout -b` (checkout disrupts other sessions)
119
+ - If the branch already exists, verify it points to the right source and skip
120
+ - The Task subagent reports success/failure per repo
121
+ 6. Update the session JSON: set `branch_created: true` for each successful branch
122
+
123
+ > **Why a Task subagent?** The team-lead has `disallowedTools: Bash`.
124
+ > Branch creation requires shell access, so it must be delegated.
125
+
91
126
  ## Phase 3: Spawn teammates (Agent Teams)
92
127
 
93
128
  Use the **Teammate** tool to spawn teammates. Each teammate is an independent
@@ -101,6 +136,8 @@ For EVERY teammate, include in the spawn prompt:
101
136
  3. API contract (if applicable)
102
137
  4. Instruction to read repo CLAUDE.md first
103
138
  5. Instruction to escalate architectural decisions not in the plan
139
+ 6. Session branch: `session/{name}` — tell the teammate this branch ALREADY EXISTS,
140
+ all commits go on it, they must NOT create other branches
104
141
 
105
142
  See @references/spawn-templates.md for the full templates per service type
106
143
  (backend, frontend, infra, explore).
@@ -148,6 +185,9 @@ On each teammate report:
148
185
  2. Update the **progress tracker** table (commits done / planned)
149
186
  3. Note dead code found
150
187
  4. Verify commit count and sizes — flag if a teammate made a single giant commit
188
+ 4b. Verify commits are on the session branch (not on a rogue branch):
189
+ - Spawn a Task subagent (Bash): `git -C ../[repo] log session/{name} --oneline -5`
190
+ - If teammate committed on a different branch, flag it as a blocker
151
191
  5. If a teammate failed → analyze, correct plan, re-dispatch
152
192
  6. **Session log** entry: `[HH:MM] teammate-[service]: [status], [N] commits, [N] files, tests [pass/fail]`
153
193
  7. If current wave done → launch next wave
@@ -18,6 +18,10 @@ Reference file for dispatch-feature. Loaded on-demand when Claude needs reminder
18
18
  10. **NEVER keep code details in your context** — summarize to 3 lines in the plan, then compact
19
19
  11. **NEVER assume repos are listed in workspace.md** — scan `../` at feature start for
20
20
  any new `.git` repos that may have appeared since last configuration
21
+ 12. **NEVER let teammates create their own branches** — they must use the session branch
22
+ (`session/{name}`). The team-lead creates it during Phase 2.5.
23
+ 13. **NEVER use `git checkout -b` in repos** — use `git branch {name} {source}` (no checkout).
24
+ Checkout changes the working directory, which disrupts other sessions running in parallel.
21
25
 
22
26
  ## Common mistakes to watch for
23
27
 
@@ -31,3 +35,8 @@ Reference file for dispatch-feature. Loaded on-demand when Claude needs reminder
31
35
  | Giant commit (500+ lines) | PR unreadable, impossible to review | Split into atomic commits (~300 lines max) per logical unit |
32
36
  | Single commit at the end | All-or-nothing, no partial rollback | Commit after each logical unit — data, logic, API, tests |
33
37
  | Task without commit boundary | Teammate guesses the split | Plan must define commit units per task |
38
+ | Teammate creates own branch | Report shows commits on `feature/xxx` instead of `session/xxx` | Re-dispatch with explicit session branch instruction |
39
+ | `git checkout -b` in repo | Other session's worktree on wrong branch | Always use `git branch` (no checkout) for session branch creation |
40
+ | No session created before dispatch | Branches mixed between parallel sessions | Always run Phase 2.5 before Phase 3 |
41
+ | Teammate reports done with 0 commits | Worktree cleaned up, changes LOST | Verify commits on session branch before accepting report |
42
+ | Teammate didn't checkout session branch | Commits on wrong branch or detached HEAD | Git workflow section must be FIRST in spawn prompt |
@@ -11,6 +11,21 @@ Reference file for dispatch-feature. Loaded on-demand, not at skill activation.
11
11
  ```
12
12
  You are teammate-[service]. Read the CLAUDE.md in your repo first.
13
13
 
14
+ ## Git workflow (CRITICAL — read first)
15
+ You are working in a temporary worktree. If you don't commit, YOUR WORK WILL BE LOST
16
+ when the worktree is cleaned up.
17
+
18
+ 1. FIRST THING: check out the session branch:
19
+ git checkout session/{session-name}
20
+ 2. Verify you are on the right branch:
21
+ git branch --show-current (must show: session/{session-name})
22
+ 3. Commit AFTER EACH logical unit — do NOT wait until the end
23
+ 4. Before reporting back, verify ALL changes are committed:
24
+ git status (must show: nothing to commit, working tree clean)
25
+ 5. If git status shows uncommitted changes when you're done: COMMIT THEM NOW
26
+
27
+ Branch `session/{session-name}` ALREADY EXISTS. Do NOT create other branches.
28
+
14
29
  ## Constitution (non-negotiable)
15
30
  [paste all rules from your workspace's constitution.md]
16
31
 
@@ -30,7 +45,8 @@ You are teammate-[service]. Read the CLAUDE.md in your repo first.
30
45
  6. **Atomic commits** — follow the commit plan below
31
46
  7. If you hit an architectural decision NOT covered by the plan: STOP and
32
47
  report the dilemma instead of guessing
33
- 8. Report back: files created/modified, tests pass/fail, dead code found,
48
+ 8. **Before reporting back**: run `git status` — if anything is uncommitted, commit it NOW
49
+ 9. Report back: files created/modified, tests pass/fail, dead code found,
34
50
  commits made (hash + message), blockers
35
51
 
36
52
  ## Commit strategy (mandatory)
@@ -43,7 +59,6 @@ You are teammate-[service]. Read the CLAUDE.md in your repo first.
43
59
  - **Commit message format**: `feat(domain): description` or `fix(domain): description`
44
60
  - **Each commit must compile and pass tests** — no broken intermediate states
45
61
  - **Commit as you go** — do NOT accumulate all changes for a single final commit
46
- - Branch: `feature/[name]` — create it on your first commit
47
62
  ```
48
63
 
49
64
  ## Frontend teammate spawn template
@@ -51,6 +66,21 @@ You are teammate-[service]. Read the CLAUDE.md in your repo first.
51
66
  ```
52
67
  You are teammate-[service]. Read the CLAUDE.md in your repo first.
53
68
 
69
+ ## Git workflow (CRITICAL — read first)
70
+ You are working in a temporary worktree. If you don't commit, YOUR WORK WILL BE LOST
71
+ when the worktree is cleaned up.
72
+
73
+ 1. FIRST THING: check out the session branch:
74
+ git checkout session/{session-name}
75
+ 2. Verify you are on the right branch:
76
+ git branch --show-current (must show: session/{session-name})
77
+ 3. Commit AFTER EACH logical unit — do NOT wait until the end
78
+ 4. Before reporting back, verify ALL changes are committed:
79
+ git status (must show: nothing to commit, working tree clean)
80
+ 5. If git status shows uncommitted changes when you're done: COMMIT THEM NOW
81
+
82
+ Branch `session/{session-name}` ALREADY EXISTS. Do NOT create other branches.
83
+
54
84
  ## Constitution (non-negotiable)
55
85
  [paste all rules from your workspace's constitution.md]
56
86
 
@@ -72,8 +102,9 @@ You are teammate-[service]. Read the CLAUDE.md in your repo first.
72
102
  6. List any dead code (unused components, composables, store actions, CSS)
73
103
  7. **Atomic commits** — follow the commit plan below
74
104
  8. If you hit an architectural decision NOT covered by the plan: STOP and escalate
75
- 9. Report back: files created/modified, tests pass/fail, dead code found,
76
- UX compliance, commits made (hash + message), blockers
105
+ 9. **Before reporting back**: run `git status` — if anything is uncommitted, commit it NOW
106
+ 10. Report back: files created/modified, tests pass/fail, dead code found,
107
+ UX compliance, commits made (hash + message), blockers
77
108
 
78
109
  ## Commit strategy (mandatory)
79
110
  - **One commit per logical unit** — each task = one commit minimum
@@ -86,7 +117,6 @@ You are teammate-[service]. Read the CLAUDE.md in your repo first.
86
117
  - **Commit message format**: `feat(domain): description` or `fix(domain): description`
87
118
  - **Each commit must compile and pass tests** — no broken intermediate states
88
119
  - **Commit as you go** — do NOT accumulate all changes for a single final commit
89
- - Branch: `feature/[name]` — create it on your first commit
90
120
  ```
91
121
 
92
122
  ## Infra/Config teammate spawn template
@@ -94,6 +124,21 @@ You are teammate-[service]. Read the CLAUDE.md in your repo first.
94
124
  ```
95
125
  You are teammate-[service]. Read the CLAUDE.md in your repo first.
96
126
 
127
+ ## Git workflow (CRITICAL — read first)
128
+ You are working in a temporary worktree. If you don't commit, YOUR WORK WILL BE LOST
129
+ when the worktree is cleaned up.
130
+
131
+ 1. FIRST THING: check out the session branch:
132
+ git checkout session/{session-name}
133
+ 2. Verify you are on the right branch:
134
+ git branch --show-current (must show: session/{session-name})
135
+ 3. Commit AFTER EACH logical unit — do NOT wait until the end
136
+ 4. Before reporting back, verify ALL changes are committed:
137
+ git status (must show: nothing to commit, working tree clean)
138
+ 5. If git status shows uncommitted changes when you're done: COMMIT THEM NOW
139
+
140
+ Branch `session/{session-name}` ALREADY EXISTS. Do NOT create other branches.
141
+
97
142
  ## Constitution (non-negotiable)
98
143
  [paste all rules from your workspace's constitution.md]
99
144
 
@@ -108,9 +153,9 @@ You are teammate-[service]. Read the CLAUDE.md in your repo first.
108
153
  5. **Atomic commits** — one commit per logical config change
109
154
  6. Commit message format: `chore(service): description`
110
155
  7. If you hit an architectural decision NOT covered by the plan: STOP and escalate
111
- 8. Report back: files modified, consistency check results,
156
+ 8. **Before reporting back**: run `git status` — if anything is uncommitted, commit it NOW
157
+ 9. Report back: files modified, consistency check results,
112
158
  commits made (hash + message), blockers
113
- - Branch: `feature/[name]`
114
159
  ```
115
160
 
116
161
  ## Explore/Haiku subagent template (read-only)
@@ -134,3 +179,6 @@ When a teammate reports back:
134
179
  - **Architectural decision not in plan** (blocking): STOP the wave, escalate to user
135
180
  - **No report after extended time**: send a status request via SendMessage
136
181
  - **Max re-dispatches per teammate per wave**: 2. After that, escalate to user.
182
+ - **0 commits reported**: the teammate likely forgot to commit. Check the worktree
183
+ with a Task subagent before accepting the report. If changes exist uncommitted,
184
+ re-dispatch with explicit instruction to commit.
@@ -36,6 +36,24 @@ if [ -d "$PROJECT_DIR/plans" ]; then
36
36
  fi
37
37
  fi
38
38
 
39
+ # Detect active sessions
40
+ SESSIONS_DIR="$PROJECT_DIR/.sessions"
41
+ if [ -d "$SESSIONS_DIR" ]; then
42
+ ACTIVE_SESSIONS=""
43
+ for session_file in "$SESSIONS_DIR"/*.json; do
44
+ [ -f "$session_file" ] || continue
45
+ SESSION_NAME=$(jq -r '.name // empty' "$session_file" 2>/dev/null) || continue
46
+ SESSION_STATUS=$(jq -r '.status // "unknown"' "$session_file" 2>/dev/null) || continue
47
+ [ "$SESSION_STATUS" != "active" ] && continue
48
+ SESSION_REPOS=$(jq -r '[.repos | keys[]] | join(", ")' "$session_file" 2>/dev/null) || SESSION_REPOS="?"
49
+ ACTIVE_SESSIONS+=" - $SESSION_NAME (repos: $SESSION_REPOS)\n"
50
+ done
51
+ if [ -n "$ACTIVE_SESSIONS" ]; then
52
+ OUTPUT+="[Session context] Active sessions:\n$ACTIVE_SESSIONS"
53
+ OUTPUT+="Session branches are already created. Teammates must use these branches.\n\n"
54
+ fi
55
+ fi
56
+
39
57
  # Check workspace.md exists
40
58
  if [ ! -f "$PROJECT_DIR/workspace.md" ]; then
41
59
  OUTPUT+="[WARNING] No workspace.md found. Run setup-workspace.sh first.\n"
@@ -67,6 +67,14 @@ if ! echo "$PROMPT" | grep -qiE '(escalat|STOP and report|STOP and escalate|repo
67
67
  WARNINGS+="- Missing escalation instruction. Include: 'If you hit an architectural decision NOT covered by the plan: STOP and escalate.'\n"
68
68
  fi
69
69
 
70
+ # Check 7: Session branch instruction (if sessions exist)
71
+ SESSIONS_DIR="${CLAUDE_PROJECT_DIR:-.}/.sessions"
72
+ if [ -d "$SESSIONS_DIR" ] && ls "$SESSIONS_DIR"/*.json >/dev/null 2>&1; then
73
+ if ! echo "$PROMPT" | grep -qiE '(session/|session branch|ALREADY EXISTS.*branch)' 2>/dev/null; then
74
+ WARNINGS+="- Active sessions exist but no session branch found in spawn prompt. Include the session branch instruction.\n"
75
+ fi
76
+ fi
77
+
70
78
  # Report — ALL checks are warnings only (v4.0)
71
79
  ISSUES=""
72
80
  [ -n "$BLOCKERS" ] && ISSUES+="$BLOCKERS"
@@ -18,9 +18,12 @@ You prepare the merge. You do NOT merge. You produce a merge readiness report.
18
18
 
19
19
  ## Phase 1: Collect branch info
20
20
 
21
- 1. Read `./workspace.md` for the service map
21
+ 1. Read `./workspace.md` for the service map (including Source Branch column)
22
22
  2. Read `./plans/{feature-name}.md` for the active plan
23
- 3. For each service with status ✅, identify the feature branch name
23
+ 3. Check `.sessions/` for the session associated with this feature
24
+ - If a session exists, use `session/{name}` as the branch name
25
+ - Use the session's `source_branch` as the merge target (not hardcoded `main`)
26
+ 4. For each service with status ✅, identify the session branch name
24
27
 
25
28
  ## Phase 2: Conflict detection
26
29
 
@@ -38,7 +41,7 @@ For lightweight read-only cross-checks, spawn Explore subagents (Task, Haiku).
38
41
  For each service, generate a PR summary:
39
42
 
40
43
  ```markdown
41
- ### [service] — PR: feature/[name] → [target]
44
+ ### [service] — PR: session/[name] → [source_branch]
42
45
 
43
46
  **Changes**: [N] files modified, [M] files created, [D] files deleted
44
47
 
@@ -72,7 +75,7 @@ Write to `./plans/{feature-name}.md`:
72
75
  ### Merge readiness
73
76
  | Service | Branch | Up-to-date | Conflicts | Tests | Ready |
74
77
  |---------|--------|-----------|-----------|-------|-------|
75
- | [name] | feature/[x] | ✅/❌ | none/[list] | ✅/❌ | ✅/❌ |
78
+ | [name] | session/[x] | ✅/❌ | none/[list] | ✅/❌ | ✅/❌ |
76
79
 
77
80
  ### Merge order
78
81
  1. [service] — [branch] → [target]
@@ -13,10 +13,10 @@ allowed-tools: Read, Write, Glob, Grep
13
13
 
14
14
  # Refresh Service Profiles
15
15
 
16
- 1. Read `./workspace.md` to get the list of repos and their paths
16
+ 1. Read `./workspace.md` to get the list of repos, their paths, and their **source branches**
17
17
  2. For each repo listed, read its CLAUDE.md
18
- 3. Extract per repo: stack, patterns, auth, tests, conventions, special notes
19
- 4. Write result to `./plans/service-profiles.md`
18
+ 3. Extract per repo: stack, patterns, auth, tests, conventions, source branch, special notes
19
+ 4. Write result to `./plans/service-profiles.md` — include the source branch per repo
20
20
  5. Add today's date in the header
21
21
 
22
22
  If a repo doesn't exist or has no CLAUDE.md, mark it as "[not found]".
@@ -8,9 +8,11 @@
8
8
 
9
9
  ## Service map
10
10
  <!-- Auto-generated from sibling repo scan. Review and add descriptions. -->
11
- | Service | Repo | Type | CLAUDE.md | Description |
12
- |---------|------|------|-----------|-------------|
11
+ | Service | Repo | Type | CLAUDE.md | Source Branch | Description |
12
+ |---------|------|------|-----------|---------------|-------------|
13
13
  <!-- [AUTO_DISCOVERED_REPOS] -->
14
+ <!-- Source Branch: the integration branch per repo (e.g., preprod, develop, main).
15
+ Session branches are created from this branch. Never push directly to it. -->
14
16
 
15
17
  ## Inter-service relationships
16
18
  <!-- Describe the request flow between services -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-workspace",
3
- "version": "4.1.4",
3
+ "version": "4.2.0",
4
4
  "description": "Claude Code multi-workspace orchestrator — skills, hooks, agents, and templates for multi-service projects",
5
5
  "bin": {
6
6
  "cc-workspace": "./bin/cli.js"