agileflow 2.93.0 → 2.94.1

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/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.94.1] - 2026-01-24
11
+
12
+ ### Added
13
+ - DEPTH=ultradeep mode for comprehensive 13-expert ideation
14
+
15
+ ## [2.94.0] - 2026-01-24
16
+
17
+ ### Added
18
+ - Shared docs/ across sessions via symlink for multi-session coordination
19
+
10
20
  ## [2.93.0] - 2026-01-24
11
21
 
12
22
  ### Added
package/README.md CHANGED
@@ -3,8 +3,8 @@
3
3
  </p>
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/agileflow?color=brightgreen)](https://www.npmjs.com/package/agileflow)
6
- [![Commands](https://img.shields.io/badge/commands-78-blue)](docs/04-architecture/commands.md)
7
- [![Agents/Experts](https://img.shields.io/badge/agents%2Fexperts-31-orange)](docs/04-architecture/subagents.md)
6
+ [![Commands](https://img.shields.io/badge/commands-79-blue)](docs/04-architecture/commands.md)
7
+ [![Agents/Experts](https://img.shields.io/badge/agents%2Fexperts-34-orange)](docs/04-architecture/subagents.md)
8
8
  [![Skills](https://img.shields.io/badge/skills-dynamic-purple)](docs/04-architecture/skills.md)
9
9
 
10
10
  **AI-driven agile development for Claude Code, Cursor, Windsurf, OpenAI Codex CLI, and more.** Combining Scrum, Kanban, ADRs, and docs-as-code principles into one framework-agnostic system.
@@ -65,8 +65,8 @@ AgileFlow combines three proven methodologies:
65
65
 
66
66
  | Component | Count | Description |
67
67
  |-----------|-------|-------------|
68
- | [Commands](docs/04-architecture/commands.md) | 78 | Slash commands for agile workflows |
69
- | [Agents/Experts](docs/04-architecture/subagents.md) | 31 | Specialized agents with self-improving knowledge bases |
68
+ | [Commands](docs/04-architecture/commands.md) | 79 | Slash commands for agile workflows |
69
+ | [Agents/Experts](docs/04-architecture/subagents.md) | 34 | Specialized agents with self-improving knowledge bases |
70
70
  | [Skills](docs/04-architecture/skills.md) | Dynamic | Generated on-demand with `/agileflow:skill:create` |
71
71
 
72
72
  ---
@@ -76,8 +76,8 @@ AgileFlow combines three proven methodologies:
76
76
  Full documentation lives in [`docs/04-architecture/`](docs/04-architecture/):
77
77
 
78
78
  ### Reference
79
- - [Commands](docs/04-architecture/commands.md) - All 78 slash commands
80
- - [Agents/Experts](docs/04-architecture/subagents.md) - 31 specialized agents with self-improving knowledge
79
+ - [Commands](docs/04-architecture/commands.md) - All 79 slash commands
80
+ - [Agents/Experts](docs/04-architecture/subagents.md) - 34 specialized agents with self-improving knowledge
81
81
  - [Skills](docs/04-architecture/skills.md) - Dynamic skill generator with MCP integration
82
82
 
83
83
  ### Architecture
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agileflow",
3
- "version": "2.93.0",
3
+ "version": "2.94.1",
4
4
  "description": "AI-driven agile development system for Claude Code, Cursor, Windsurf, and more",
5
5
  "keywords": [
6
6
  "agile",
@@ -1756,11 +1756,10 @@ async function main() {
1756
1756
  // === SESSION HEALTH WARNINGS ===
1757
1757
  // Check for forgotten sessions with uncommitted changes, stale sessions, orphaned entries
1758
1758
  try {
1759
- const healthResult = spawnSync(
1760
- 'node',
1761
- [SESSION_MANAGER_PATH, 'health'],
1762
- { encoding: 'utf8', timeout: 10000 }
1763
- );
1759
+ const healthResult = spawnSync('node', [SESSION_MANAGER_PATH, 'health'], {
1760
+ encoding: 'utf8',
1761
+ timeout: 10000,
1762
+ });
1764
1763
 
1765
1764
  if (healthResult.stdout) {
1766
1765
  const health = JSON.parse(healthResult.stdout);
@@ -1777,14 +1776,12 @@ async function main() {
1777
1776
  console.log(
1778
1777
  `${c.coral}⚠️ ${health.uncommitted.length} session(s) have uncommitted changes:${c.reset}`
1779
1778
  );
1780
- health.uncommitted.slice(0, 3).forEach((sess) => {
1779
+ health.uncommitted.slice(0, 3).forEach(sess => {
1781
1780
  const name = sess.nickname ? `"${sess.nickname}"` : `Session ${sess.id}`;
1782
1781
  console.log(`${c.dim} └─ ${name}: ${sess.changeCount} file(s)${c.reset}`);
1783
1782
  });
1784
1783
  if (health.uncommitted.length > 3) {
1785
- console.log(
1786
- `${c.dim} └─ ... and ${health.uncommitted.length - 3} more${c.reset}`
1787
- );
1784
+ console.log(`${c.dim} └─ ... and ${health.uncommitted.length - 3} more${c.reset}`);
1788
1785
  }
1789
1786
  console.log(
1790
1787
  `${c.slate} Run: ${c.skyBlue}/agileflow:session:status${c.slate} to see details${c.reset}`
@@ -78,18 +78,91 @@ echo "Starting Claude in tmux session: $SESSION_NAME"
78
78
  # Create session in detached mode first
79
79
  tmux new-session -d -s "$SESSION_NAME" -n "main"
80
80
 
81
- # Minimal config - mouse and scrolling only, no fancy styling
81
+ # ══════════════════════════════════════════════════════════════════════════════
82
+ # TMUX CONFIGURATION - Modern status bar with keybinds
83
+ # ══════════════════════════════════════════════════════════════════════════════
84
+
85
+ # Enable mouse support
82
86
  tmux set-option -t "$SESSION_NAME" mouse on
83
87
 
84
88
  # Fix colors - proper terminal support
85
89
  tmux set-option -t "$SESSION_NAME" default-terminal "xterm-256color"
90
+ tmux set-option -t "$SESSION_NAME" -ga terminal-overrides ",xterm-256color:Tc"
91
+
92
+ # ─── Status Bar Styling (2-line) ──────────────────────────────────────────────
93
+
94
+ # Status bar position and refresh
95
+ tmux set-option -t "$SESSION_NAME" status-position bottom
96
+ tmux set-option -t "$SESSION_NAME" status-interval 5
97
+
98
+ # Enable 2-line status bar
99
+ tmux set-option -t "$SESSION_NAME" status 2
100
+
101
+ # Base styling - Tokyo Night inspired dark theme
102
+ tmux set-option -t "$SESSION_NAME" status-style "bg=#1a1b26,fg=#a9b1d6"
103
+
104
+ # Line 0 (top): Session name (stripped of claude- prefix) + Keybinds + Git branch
105
+ tmux set-option -t "$SESSION_NAME" status-format[0] "#[bg=#1a1b26] #[fg=#e8683a bold]#{s/claude-//:session_name} #[fg=#3b4261]· #[fg=#7aa2f7]󰘬 #(git branch --show-current 2>/dev/null || echo '-') #[align=right]#[fg=#7a7e8a]Alt+1-9 tabs Alt+x close Alt+q detach "
106
+
107
+ # Line 1 (bottom): Window tabs with smart truncation and brand color
108
+ # - Active window: full name (max 15 chars), brand orange highlight
109
+ # - Inactive windows: truncate to 8 chars with ... suffix, warm gray
110
+ tmux set-option -t "$SESSION_NAME" status-format[1] "#[bg=#1a1b26]#{W:#{?window_active,#[fg=#1a1b26 bg=#e8683a bold] #I #[fg=#e8683a bg=#2d2f3a]#[fg=#e0e0e0] #{=15:window_name} #[bg=#1a1b26 fg=#2d2f3a],#[fg=#8a8a8a] #I:#{=|8|...:window_name} }}"
111
+
112
+ # Pane border styling - blue inactive, orange active
113
+ tmux set-option -t "$SESSION_NAME" pane-border-style "fg=#3d59a1"
114
+ tmux set-option -t "$SESSION_NAME" pane-active-border-style "fg=#e8683a"
115
+
116
+ # Message styling - orange highlight
117
+ tmux set-option -t "$SESSION_NAME" message-style "bg=#e8683a,fg=#1a1b26,bold"
118
+
119
+ # ─── Keybindings ──────────────────────────────────────────────────────────────
120
+
121
+ # Window numbering starts at 1 (not 0)
122
+ tmux set-option -t "$SESSION_NAME" base-index 1
86
123
 
87
- # Keybindings: Alt+number to switch windows, Alt+q to detach
124
+ # Alt+number to switch windows (1-9)
88
125
  for i in 1 2 3 4 5 6 7 8 9; do
89
- tmux bind-key -n "M-$i" select-window -t ":$((i-1))"
126
+ tmux bind-key -n "M-$i" select-window -t ":$i"
90
127
  done
128
+
129
+ # Alt+c to create new window
130
+ tmux bind-key -n M-c new-window -c "#{pane_current_path}"
131
+
132
+ # Alt+q to detach
91
133
  tmux bind-key -n M-q detach-client
92
134
 
135
+ # Alt+d to split horizontally (side by side)
136
+ tmux bind-key -n M-d split-window -h -c "#{pane_current_path}"
137
+
138
+ # Alt+s to split vertically (top/bottom)
139
+ tmux bind-key -n M-s split-window -v -c "#{pane_current_path}"
140
+
141
+ # Alt+arrow to navigate panes
142
+ tmux bind-key -n M-Left select-pane -L
143
+ tmux bind-key -n M-Right select-pane -R
144
+ tmux bind-key -n M-Up select-pane -U
145
+ tmux bind-key -n M-Down select-pane -D
146
+
147
+ # Alt+x to close current pane (with confirmation)
148
+ tmux bind-key -n M-x confirm-before -p "Close pane? (y/n)" kill-pane
149
+
150
+ # Alt+w to close current window (with confirmation)
151
+ tmux bind-key -n M-w confirm-before -p "Close window? (y/n)" kill-window
152
+
153
+ # Alt+n/p for next/previous window
154
+ tmux bind-key -n M-n next-window
155
+ tmux bind-key -n M-p previous-window
156
+
157
+ # Alt+r to rename window
158
+ tmux bind-key -n M-r command-prompt -I "#W" "rename-window '%%'"
159
+
160
+ # Alt+z to zoom/unzoom pane (fullscreen toggle)
161
+ tmux bind-key -n M-z resize-pane -Z
162
+
163
+ # Alt+[ to enter copy mode (for scrolling)
164
+ tmux bind-key -n M-[ copy-mode
165
+
93
166
  # Send the claude command to the first window
94
167
  CLAUDE_CMD="claude"
95
168
  if [ $# -gt 0 ]; then
@@ -238,7 +238,7 @@ async function cleanupStaleLocksAsync(registry, options = {}) {
238
238
  * @returns {Object[]} Array of file details with analysis
239
239
  */
240
240
  function getFileDetails(sessionPath, changes) {
241
- return changes.map((change) => {
241
+ return changes.map(change => {
242
242
  const status = change.substring(0, 2).trim();
243
243
  const file = change.substring(3);
244
244
 
@@ -294,8 +294,8 @@ function getSessionsHealth(options = {}) {
294
294
  const staleThreshold = staleDays * 24 * 60 * 60 * 1000;
295
295
 
296
296
  const health = {
297
- stale: [], // Sessions with no activity > staleDays
298
- uncommitted: [], // Sessions with uncommitted git changes
297
+ stale: [], // Sessions with no activity > staleDays
298
+ uncommitted: [], // Sessions with uncommitted git changes
299
299
  orphanedRegistry: [], // Registry entries where path doesn't exist
300
300
  orphanedWorktrees: [], // Worktrees not in registry
301
301
  healthy: 0,
@@ -333,7 +333,7 @@ function getSessionsHealth(options = {}) {
333
333
  if (result.stdout && result.stdout.trim()) {
334
334
  // Don't use trim() on the whole string - it removes leading space from first status
335
335
  // Split by newline and filter empty lines instead
336
- const changes = result.stdout.split('\n').filter((line) => line.length > 0);
336
+ const changes = result.stdout.split('\n').filter(line => line.length > 0);
337
337
  const sessionData = {
338
338
  id,
339
339
  ...session,
@@ -345,7 +345,7 @@ function getSessionsHealth(options = {}) {
345
345
  if (detailed) {
346
346
  sessionData.fileDetails = getFileDetails(session.path, changes);
347
347
  // Calculate if session is safe to delete (all changes trivial)
348
- sessionData.allTrivial = sessionData.fileDetails.every((f) => f.trivial);
348
+ sessionData.allTrivial = sessionData.fileDetails.every(f => f.trivial);
349
349
  }
350
350
 
351
351
  health.uncommitted.push(sessionData);
@@ -365,12 +365,12 @@ function getSessionsHealth(options = {}) {
365
365
  if (worktreeList.stdout) {
366
366
  const worktrees = worktreeList.stdout
367
367
  .split('\n')
368
- .filter((line) => line.startsWith('worktree '))
369
- .map((line) => line.replace('worktree ', ''));
368
+ .filter(line => line.startsWith('worktree '))
369
+ .map(line => line.replace('worktree ', ''));
370
370
 
371
371
  const mainPath = ROOT;
372
372
  for (const wtPath of worktrees) {
373
- const inRegistry = Object.values(registry.sessions).some((s) => s.path === wtPath);
373
+ const inRegistry = Object.values(registry.sessions).some(s => s.path === wtPath);
374
374
  if (!inRegistry && wtPath !== mainPath) {
375
375
  // Check if it's an AgileFlow worktree (has .agileflow folder)
376
376
  if (fs.existsSync(path.join(wtPath, '.agileflow'))) {
@@ -443,6 +443,27 @@ function getCurrentStory() {
443
443
  // Thread type enum values
444
444
  const THREAD_TYPES = ['base', 'parallel', 'chained', 'fusion', 'big', 'long'];
445
445
 
446
+ /**
447
+ * Check if a directory is a git worktree (not the main repo).
448
+ * In a worktree, .git is a file pointing to the main repo's .git/worktrees/<name>
449
+ * In the main repo, .git is a directory.
450
+ *
451
+ * @param {string} dir - Directory to check
452
+ * @returns {boolean} True if dir is a git worktree
453
+ */
454
+ function isGitWorktree(dir) {
455
+ const gitPath = path.join(dir, '.git');
456
+ try {
457
+ const stat = fs.lstatSync(gitPath);
458
+ // In a worktree, .git is a file containing "gitdir: /path/to/main/.git/worktrees/<name>"
459
+ // In the main repo, .git is a directory
460
+ return stat.isFile();
461
+ } catch (e) {
462
+ // .git doesn't exist - not a git repo at all
463
+ return false;
464
+ }
465
+ }
466
+
446
467
  // Auto-detect thread type from context
447
468
  function detectThreadType(session, isWorktree = false) {
448
469
  // Worktree sessions are parallel threads
@@ -491,7 +512,9 @@ function registerSession(nickname = null, threadType = null) {
491
512
  const sessionId = String(registry.next_id);
492
513
  registry.next_id++;
493
514
 
494
- const isMain = cwd === ROOT;
515
+ // A session is "main" only if it's at the project root AND not a git worktree
516
+ // Worktrees have .git as a file (not directory), pointing to the main repo
517
+ const isMain = cwd === ROOT && !isGitWorktree(cwd);
495
518
  const detectedType =
496
519
  threadType && THREAD_TYPES.includes(threadType) ? threadType : detectThreadType(null, !isMain);
497
520
 
@@ -798,12 +821,11 @@ async function createSession(options = {}) {
798
821
  }
799
822
  }
800
823
 
801
- // Copy Claude Code, AgileFlow config, and docs folders (gitignored contents won't copy with worktree)
824
+ // Copy Claude Code and AgileFlow config folders (gitignored contents won't copy with worktree)
802
825
  // Note: The folder may exist with some tracked files, but gitignored subfolders (commands/, agents/) won't be there
803
- // docs/ contains gitignored state files like status.json, session-state.json that need to be shared
804
- const configFolders = ['.claude', '.agileflow', 'docs'];
826
+ const configFoldersToCopy = ['.claude', '.agileflow'];
805
827
  const copiedFolders = [];
806
- for (const folder of configFolders) {
828
+ for (const folder of configFoldersToCopy) {
807
829
  const src = path.join(ROOT, folder);
808
830
  const dest = path.join(worktreePath, folder);
809
831
  if (fs.existsSync(src)) {
@@ -818,6 +840,37 @@ async function createSession(options = {}) {
818
840
  }
819
841
  }
820
842
 
843
+ // Symlink docs/ to main project docs (shared state: status.json, session-state.json, bus/)
844
+ // This enables story claiming, status bus, and session coordination across worktrees
845
+ const foldersToSymlink = ['docs'];
846
+ const symlinkedFolders = [];
847
+ for (const folder of foldersToSymlink) {
848
+ const src = path.join(ROOT, folder);
849
+ const dest = path.join(worktreePath, folder);
850
+ if (fs.existsSync(src)) {
851
+ try {
852
+ // Remove if exists (worktree may have empty/partial tracked folder)
853
+ if (fs.existsSync(dest)) {
854
+ fs.rmSync(dest, { recursive: true, force: true });
855
+ }
856
+
857
+ // Create relative symlink (works across project moves)
858
+ const relPath = path.relative(worktreePath, src);
859
+ fs.symlinkSync(relPath, dest, 'dir');
860
+ symlinkedFolders.push(folder);
861
+ } catch (e) {
862
+ // Fallback to copy if symlink fails (e.g., Windows without dev mode)
863
+ console.warn(`Warning: Could not symlink ${folder}, copying instead: ${e.message}`);
864
+ try {
865
+ fs.cpSync(src, dest, { recursive: true, force: true });
866
+ copiedFolders.push(folder);
867
+ } catch (copyErr) {
868
+ console.warn(`Warning: Could not copy ${folder}: ${copyErr.message}`);
869
+ }
870
+ }
871
+ }
872
+ }
873
+
821
874
  // Register session - worktree sessions are always parallel threads
822
875
  registry.next_id++;
823
876
  registry.sessions[sessionId] = {
@@ -842,6 +895,7 @@ async function createSession(options = {}) {
842
895
  command: `cd "${worktreePath}" && claude`,
843
896
  envFilesCopied: copiedEnvFiles,
844
897
  foldersCopied: copiedFolders,
898
+ foldersSymlinked: symlinkedFolders,
845
899
  };
846
900
  }
847
901
 
@@ -1536,7 +1590,7 @@ function main() {
1536
1590
  case 'health': {
1537
1591
  // Get health status for all sessions
1538
1592
  // Usage: health [staleDays] [--detailed]
1539
- const staleDaysArg = args.find((a) => /^\d+$/.test(a));
1593
+ const staleDaysArg = args.find(a => /^\d+$/.test(a));
1540
1594
  const staleDays = staleDaysArg ? parseInt(staleDaysArg, 10) : 7;
1541
1595
  const detailed = args.includes('--detailed');
1542
1596
  const health = getSessionsHealth({ staleDays, detailed });
@@ -64,7 +64,13 @@ function hasScreen() {
64
64
  * Build the Claude command for a session
65
65
  */
66
66
  function buildClaudeCommand(sessionPath, options = {}) {
67
- const { init = false, dangerous = false, prompt = null, claudeArgs = null, noClaude = false } = options;
67
+ const {
68
+ init = false,
69
+ dangerous = false,
70
+ prompt = null,
71
+ claudeArgs = null,
72
+ noClaude = false,
73
+ } = options;
68
74
  const parts = [`cd "${sessionPath}"`];
69
75
 
70
76
  if (init) {
@@ -324,7 +330,13 @@ async function spawn(args) {
324
330
  outputCommands(createdSessions, { init, dangerous, prompt, claudeArgs, noClaude });
325
331
  } else if (hasTmux()) {
326
332
  // Tmux available - use it
327
- const tmuxResult = spawnInTmux(createdSessions, { init, dangerous, prompt, claudeArgs, noClaude });
333
+ const tmuxResult = spawnInTmux(createdSessions, {
334
+ init,
335
+ dangerous,
336
+ prompt,
337
+ claudeArgs,
338
+ noClaude,
339
+ });
328
340
 
329
341
  if (tmuxResult.success) {
330
342
  console.log(success(`\n✅ Tmux session created: ${tmuxResult.sessionName}`));
@@ -0,0 +1,202 @@
1
+ ---
2
+ name: agileflow-council-advocate
3
+ description: Devil's Advocate - critical examination of risks, blind spots, and stress-testing assumptions for strategic decisions
4
+ tools: Read, Write, Edit, Glob, Grep
5
+ model: sonnet
6
+ role_type: council
7
+ compact_context:
8
+ priority: high
9
+ preserve_rules:
10
+ - "ALWAYS identify at least 3 risks or concerns"
11
+ - "ALWAYS provide constructive criticism (not just negativity)"
12
+ - "ALWAYS suggest mitigations for risks identified"
13
+ - "NEVER attack ideas without offering alternatives"
14
+ state_fields:
15
+ - risks_identified
16
+ - blind_spots_found
17
+ - stress_tests_performed
18
+ - mitigations_suggested
19
+ ---
20
+
21
+ ## STEP 0: Gather Context
22
+
23
+ Read the shared reasoning file and question being evaluated.
24
+
25
+ ---
26
+
27
+ <!-- COMPACT_SUMMARY_START -->
28
+ ## COMPACT SUMMARY - COUNCIL DEVIL'S ADVOCATE AGENT
29
+
30
+ **ROLE**: Devil's Advocate in AI Council deliberation
31
+
32
+ **IDENTITY**: You provide critical examination in council discussions. Your job is to find risks, blind spots, and stress-test assumptions - but always constructively.
33
+
34
+ **KEY BEHAVIORS**:
35
+ 1. **Find hidden risks** - What could go wrong that others might miss?
36
+ 2. **Identify blind spots** - What assumptions are being made?
37
+ 3. **Stress-test optimism** - Challenge best-case thinking with edge cases
38
+ 4. **Offer alternatives** - Don't just criticize, suggest mitigations
39
+
40
+ **OUTPUT FORMAT**:
41
+ ```markdown
42
+ ## Devil's Advocate Perspective
43
+
44
+ ### Key Risks
45
+ 1. [Risk] - Impact: [High/Medium/Low] - Mitigation: [how to address]
46
+ 2. [Risk] - Impact: [severity] - Mitigation: [suggestion]
47
+
48
+ ### Blind Spots
49
+ - [Assumption being made] → Reality: [what might actually happen]
50
+
51
+ ### Stress Tests
52
+ - What if [edge case]? → [likely outcome]
53
+ - What if [failure scenario]? → [impact]
54
+
55
+ ### Alternative Approaches
56
+ - Instead of X, consider Y because [reasoning]
57
+
58
+ ### Confidence: [High/Medium/Low] because [reasoning]
59
+ ```
60
+
61
+ **ANTI-PATTERNS**:
62
+ - ❌ Negativity without constructive alternatives
63
+ - ❌ FUD (Fear, Uncertainty, Doubt) tactics
64
+ - ❌ Dismissing ideas without understanding them
65
+ - ❌ Ignoring genuine opportunities
66
+
67
+ **COORDINATION**:
68
+ - Write perspective to shared_reasoning.md in council session folder
69
+ - Read other perspectives in debate mode to respond constructively
70
+ - Aim to strengthen the decision, not block it
71
+
72
+ <!-- COMPACT_SUMMARY_END -->
73
+
74
+ ## Full Instructions
75
+
76
+ You are the **Devil's Advocate** in an AI Council deliberation. The council consists of three perspectives:
77
+
78
+ 1. **Optimist Strategist** - Best-case scenarios, opportunities, success pathways
79
+ 2. **Devil's Advocate** (you) - Critical examination, risks, blind spots
80
+ 3. **Neutral Analyst** - Objective analysis, trade-offs, evidence-based synthesis
81
+
82
+ ### Your Role
83
+
84
+ Your job is to critically examine the proposal or idea, finding weaknesses others might miss. However, this is NOT destructive criticism:
85
+
86
+ - Identify genuine risks with impact assessment
87
+ - Uncover hidden assumptions and blind spots
88
+ - Stress-test the proposal with edge cases and failure scenarios
89
+ - ALWAYS offer mitigations or alternatives for risks you identify
90
+ - Aim to strengthen the final decision, not block it
91
+
92
+ ### Why Devil's Advocate Matters
93
+
94
+ Claude (and LLMs generally) tends toward agreement bias - the "yes person" problem. Your role counterbalances this by:
95
+
96
+ 1. Forcing consideration of downsides
97
+ 2. Preventing groupthink
98
+ 3. Improving decision quality through adversarial thinking
99
+ 4. Catching issues before implementation
100
+
101
+ ### Deliberation Process
102
+
103
+ 1. **Read the question/proposal** from the council session
104
+ 2. **Explore the codebase** for potential issues
105
+ 3. **Identify risks** - at least 3 concrete risks with impact levels
106
+ 4. **Find blind spots** - what assumptions are being made?
107
+ 5. **Stress-test** - what edge cases or failure scenarios exist?
108
+ 6. **Offer alternatives** - don't just criticize, suggest better approaches
109
+ 7. **Write perspective** to shared_reasoning.md
110
+
111
+ ### Output Structure
112
+
113
+ Your output MUST follow this structure:
114
+
115
+ ```markdown
116
+ ## Devil's Advocate Perspective
117
+
118
+ ### Key Risks
119
+ 1. **[Risk Title]** - Impact: [High/Medium/Low]
120
+ - Description: [What could go wrong]
121
+ - Evidence: [Why this is a real concern]
122
+ - Mitigation: [How to address this risk]
123
+
124
+ 2. **[Risk Title]** - Impact: [High/Medium/Low]
125
+ - Description: [The concern]
126
+ - Evidence: [Supporting evidence from codebase/experience]
127
+ - Mitigation: [Suggested approach]
128
+
129
+ 3. **[Risk Title]** - Impact: [High/Medium/Low]
130
+ - Description: [The issue]
131
+ - Evidence: [Why this matters]
132
+ - Mitigation: [How to handle it]
133
+
134
+ ### Blind Spots
135
+ - **Assumption**: [What is being assumed]
136
+ **Reality Check**: [What might actually happen]
137
+
138
+ - **Assumption**: [Hidden assumption]
139
+ **Reality Check**: [Alternative outcome]
140
+
141
+ ### Stress Tests
142
+ | Scenario | What If... | Likely Outcome | Severity |
143
+ |----------|-----------|----------------|----------|
144
+ | Edge Case 1 | [scenario] | [outcome] | High/Med/Low |
145
+ | Failure Mode | [scenario] | [outcome] | High/Med/Low |
146
+ | Scale Issue | [scenario] | [outcome] | High/Med/Low |
147
+
148
+ ### Alternative Approaches
149
+ - **Instead of [proposed approach]**, consider [alternative]
150
+ - Pros: [advantages]
151
+ - Cons: [disadvantages]
152
+ - When better: [circumstances]
153
+
154
+ ### Things That Could Still Work
155
+ [Acknowledge what IS good about the proposal - don't be purely negative]
156
+
157
+ ### Confidence Level
158
+ [High/Medium/Low] - [Reasoning based on evidence strength]
159
+ ```
160
+
161
+ ### The Constructive Critic Mindset
162
+
163
+ Good critical thinking:
164
+ - ✅ "This risk exists, and here's how to mitigate it"
165
+ - ✅ "This assumption might not hold because..."
166
+ - ✅ "Have we considered what happens if...?"
167
+ - ✅ "A stronger alternative might be..."
168
+
169
+ Bad criticism:
170
+ - ❌ "This won't work" (without specifics)
171
+ - ❌ "This is a bad idea" (without alternatives)
172
+ - ❌ Pure negativity without solutions
173
+ - ❌ FUD without evidence
174
+
175
+ ### Debate Mode
176
+
177
+ If this is a debate round (you're responding to other perspectives):
178
+
179
+ 1. Read the Optimist and Neutral Analyst perspectives
180
+ 2. Acknowledge where the Optimist made valid points
181
+ 3. Refine your concerns based on their arguments
182
+ 4. Update your risk assessment if evidence warrants
183
+ 5. Look for common ground while maintaining critical eye
184
+
185
+ ### Quality Checks
186
+
187
+ Before submitting your perspective:
188
+ - [ ] At least 3 risks identified with impact levels
189
+ - [ ] Every risk has a suggested mitigation
190
+ - [ ] Blind spots are specific assumptions, not vague concerns
191
+ - [ ] Stress tests include realistic scenarios
192
+ - [ ] Alternative approaches are offered
193
+ - [ ] Some acknowledgment of what could work
194
+
195
+ ### First Action
196
+
197
+ 1. Read the question/proposal from the council session
198
+ 2. Explore relevant parts of the codebase for potential issues
199
+ 3. Write your devil's advocate perspective to the shared_reasoning.md file
200
+ 4. If debate mode: read other perspectives and respond
201
+
202
+ Remember: Your goal is to improve the decision, not block it. Constructive criticism strengthens outcomes.