claude-code-workflow 6.3.12 → 6.3.13

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.
@@ -49,12 +49,14 @@ color: green
49
49
  ```
50
50
  Phase 1: Issue Understanding (5%)
51
51
  ↓ Fetch details, extract requirements, determine complexity
52
- Phase 2: ACE Exploration (30%)
52
+ Phase 2: ACE Exploration (25%)
53
53
  ↓ Semantic search, pattern discovery, dependency mapping
54
- Phase 3: Solution Planning (50%)
54
+ Phase 3: Solution Planning (45%)
55
55
  ↓ Task decomposition, 5-phase lifecycle, acceptance criteria
56
- Phase 4: Validation & Output (15%)
56
+ Phase 4: Validation & Output (10%)
57
57
  ↓ DAG validation, conflict detection, solution registration
58
+ Phase 5: Conflict Analysis (15%)
59
+ ↓ Gemini CLI multi-solution conflict detection
58
60
  ```
59
61
 
60
62
  #### Phase 1: Issue Understanding
@@ -199,6 +201,67 @@ for (const issue of issues) {
199
201
  }
200
202
  ```
201
203
 
204
+ #### Phase 5: Conflict Analysis (Gemini CLI)
205
+
206
+ **Trigger**: When batch contains 2+ solutions
207
+
208
+ **Conflict Types Analyzed**:
209
+ 1. **File Conflicts**: Modified file overlaps
210
+ 2. **API Conflicts**: Interface/breaking changes
211
+ 3. **Data Model Conflicts**: Schema changes
212
+ 4. **Dependency Conflicts**: Package version conflicts
213
+ 5. **Architecture Conflicts**: Pattern violations
214
+
215
+ **Gemini CLI Call**:
216
+ ```javascript
217
+ function analyzeConflictsGemini(solutions, projectRoot) {
218
+ if (solutions.length < 2) return { conflicts: [], safe_parallel: [solutions.map(s => s.id)] };
219
+
220
+ const solutionSummaries = solutions.map(sol => ({
221
+ issue_id: sol.issue_id,
222
+ solution_id: sol.id,
223
+ files_modified: extractFilesFromTasks(sol.tasks),
224
+ api_changes: extractApiChanges(sol.tasks),
225
+ data_changes: extractDataChanges(sol.tasks)
226
+ }));
227
+
228
+ const prompt = `
229
+ PURPOSE: Detect conflicts between solution implementations; identify all conflict types; provide resolution recommendations
230
+ TASK: • Analyze file overlaps • Check API breaking changes • Detect schema conflicts • Find dependency conflicts • Identify architecture violations
231
+ MODE: analysis
232
+ CONTEXT: Solution summaries
233
+ EXPECTED: JSON conflict report with type, severity, solutions_affected, resolution_strategy
234
+ RULES: $(cat ~/.claude/workflows/cli-templates/protocols/analysis-protocol.md) | Mark severity (high/medium/low) | Provide recommended_order
235
+
236
+ SOLUTIONS:
237
+ ${JSON.stringify(solutionSummaries, null, 2)}
238
+
239
+ OUTPUT FORMAT:
240
+ {
241
+ "conflicts": [{
242
+ "type": "file_conflict|api_conflict|data_conflict|dependency_conflict|architecture_conflict",
243
+ "severity": "high|medium|low",
244
+ "solutions_affected": ["SOL-GH-123-1", "SOL-GH-123-2"],
245
+ "summary": "brief description",
246
+ "resolution_strategy": "sequential|parallel_with_coordination|refactor_merge",
247
+ "recommended_order": ["SOL-GH-123-1", "SOL-GH-123-2"],
248
+ "rationale": "why this order"
249
+ }],
250
+ "safe_parallel": [["SOL-GH-124-1", "SOL-GH-125-1"]]
251
+ }
252
+ `;
253
+
254
+ const taskId = Bash({
255
+ command: `ccw cli -p "${prompt}" --tool gemini --mode analysis --cd "${projectRoot}"`,
256
+ run_in_background: true, timeout: 900000
257
+ });
258
+ const output = TaskOutput({ task_id: taskId, block: true });
259
+ return JSON.parse(extractJsonFromMarkdown(output));
260
+ }
261
+ ```
262
+
263
+ **Integration**: After Phase 4 validation, call `analyzeConflictsGemini()` and merge results into return summary.
264
+
202
265
  ---
203
266
 
204
267
  ## 2. Output Requirements
@@ -224,8 +287,17 @@ Each line is a solution JSON containing tasks. Schema: `cat .claude/workflows/cl
224
287
  ```json
225
288
  {
226
289
  "bound": [{ "issue_id": "...", "solution_id": "...", "task_count": N }],
227
- "pending_selection": [{ "issue_id": "...", "solutions": [{ "id": "SOL-001", "description": "...", "task_count": N }] }],
228
- "conflicts": [{ "file": "...", "issues": [...] }]
290
+ "pending_selection": [{ "issue_id": "GH-123", "solutions": [{ "id": "SOL-GH-123-1", "description": "...", "task_count": N }] }],
291
+ "conflicts": [{
292
+ "type": "file_conflict|api_conflict|data_conflict|dependency_conflict|architecture_conflict",
293
+ "severity": "high|medium|low",
294
+ "solutions_affected": ["SOL-GH-123-1", "SOL-GH-123-2"],
295
+ "summary": "brief description",
296
+ "resolution_strategy": "sequential|parallel_with_coordination",
297
+ "recommended_order": ["SOL-GH-123-1", "SOL-GH-123-2"],
298
+ "recommended_resolution": "Use sequential execution: SOL-GH-123-1 first",
299
+ "resolution_options": [{ "strategy": "...", "rationale": "..." }]
300
+ }]
229
301
  }
230
302
  ```
231
303
 
@@ -37,7 +37,7 @@ color: orange
37
37
  {
38
38
  solutions: [{
39
39
  issue_id: string, // e.g., "ISS-20251227-001"
40
- solution_id: string, // e.g., "SOL-20251227-001"
40
+ solution_id: string, // e.g., "SOL-ISS-20251227-001-1"
41
41
  task_count: number, // Number of tasks in this solution
42
42
  files_touched: string[], // All files modified by this solution
43
43
  priority: string // Issue priority: critical | high | medium | low
@@ -52,11 +52,13 @@ color: orange
52
52
  ### 1.2 Execution Flow
53
53
 
54
54
  ```
55
- Phase 1: Solution Analysis (20%)
55
+ Phase 1: Solution Analysis (15%)
56
56
  | Parse solutions, collect files_touched, build DAG
57
- Phase 2: Conflict Detection (30%)
58
- | Identify file overlaps between solutions
59
- Phase 3: Conflict Resolution (25%)
57
+ Phase 2: Conflict Detection (25%)
58
+ | Identify all conflict types (file, API, data, dependency, architecture)
59
+ Phase 2.5: Clarification (15%)
60
+ | Surface ambiguous dependencies, BLOCK until resolved
61
+ Phase 3: Conflict Resolution (20%)
60
62
  | Apply ordering rules, update DAG
61
63
  Phase 4: Ordering & Grouping (25%)
62
64
  | Topological sort, assign parallel/sequential groups
@@ -86,22 +88,106 @@ function buildDependencyGraph(solutions) {
86
88
  }
87
89
  ```
88
90
 
89
- ### 2.2 Conflict Detection
91
+ ### 2.2 Conflict Detection (5 Types)
90
92
 
91
- Conflict when multiple solutions modify same file:
93
+ Detect all conflict types between solutions:
92
94
  ```javascript
93
- function detectConflicts(fileModifications, graph) {
94
- return [...fileModifications.entries()]
95
- .filter(([_, solutions]) => solutions.length > 1)
96
- .map(([file, solutions]) => ({
97
- type: 'file_conflict',
98
- file,
99
- solutions,
100
- resolved: false
101
- }))
95
+ function detectConflicts(solutions, graph) {
96
+ const conflicts = [];
97
+ const fileModifications = buildFileModificationMap(solutions);
98
+
99
+ // 1. File conflicts (multiple solutions modify same file)
100
+ for (const [file, solIds] of fileModifications.entries()) {
101
+ if (solIds.length > 1) {
102
+ conflicts.push({
103
+ type: 'file_conflict', severity: 'medium',
104
+ file, solutions: solIds, resolved: false
105
+ });
106
+ }
107
+ }
108
+
109
+ // 2. API conflicts (breaking interface changes)
110
+ const apiChanges = extractApiChangesFromAllSolutions(solutions);
111
+ for (const [api, changes] of apiChanges.entries()) {
112
+ if (changes.some(c => c.breaking)) {
113
+ conflicts.push({
114
+ type: 'api_conflict', severity: 'high',
115
+ api, solutions: changes.map(c => c.solution_id), resolved: false
116
+ });
117
+ }
118
+ }
119
+
120
+ // 3. Data model conflicts (schema changes to same model)
121
+ const dataChanges = extractDataChangesFromAllSolutions(solutions);
122
+ for (const [model, changes] of dataChanges.entries()) {
123
+ if (changes.length > 1) {
124
+ conflicts.push({
125
+ type: 'data_conflict', severity: 'high',
126
+ model, solutions: changes.map(c => c.solution_id), resolved: false
127
+ });
128
+ }
129
+ }
130
+
131
+ // 4. Dependency conflicts (package version conflicts)
132
+ const depChanges = extractDependencyChanges(solutions);
133
+ for (const [pkg, versions] of depChanges.entries()) {
134
+ if (versions.length > 1 && !versionsCompatible(versions)) {
135
+ conflicts.push({
136
+ type: 'dependency_conflict', severity: 'medium',
137
+ package: pkg, solutions: versions.map(v => v.solution_id), resolved: false
138
+ });
139
+ }
140
+ }
141
+
142
+ // 5. Architecture conflicts (pattern violations)
143
+ const archIssues = detectArchitectureViolations(solutions);
144
+ conflicts.push(...archIssues.map(issue => ({
145
+ type: 'architecture_conflict', severity: 'low',
146
+ pattern: issue.pattern, solutions: issue.solutions, resolved: false
147
+ })));
148
+
149
+ return conflicts;
150
+ }
151
+ ```
152
+
153
+ ### 2.2.5 Clarification (BLOCKING)
154
+
155
+ **Purpose**: Surface ambiguous dependencies for user/system clarification
156
+
157
+ **Trigger Conditions**:
158
+ - High severity conflicts with no clear resolution order
159
+ - Circular dependencies detected
160
+ - Multiple valid resolution strategies
161
+
162
+ **Clarification Logic**:
163
+ ```javascript
164
+ function generateClarifications(conflicts, solutions) {
165
+ const clarifications = [];
166
+
167
+ for (const conflict of conflicts) {
168
+ if (conflict.severity === 'high' && !conflict.recommended_order) {
169
+ clarifications.push({
170
+ conflict_id: `CFT-${clarifications.length + 1}`,
171
+ question: `${conflict.type}: Which solution should execute first?`,
172
+ options: conflict.solutions.map(solId => ({
173
+ value: solId,
174
+ label: getSolutionSummary(solId, solutions)
175
+ })),
176
+ requires_user_input: true
177
+ });
178
+ }
179
+ }
180
+
181
+ return clarifications;
102
182
  }
103
183
  ```
104
184
 
185
+ **Blocking Behavior**: Agent BLOCKS execution until clarifications are resolved
186
+ - Return `clarifications` array in output
187
+ - Main agent presents to user via AskUserQuestion
188
+ - Agent waits for response before proceeding to Phase 3
189
+ - No best-guess fallback - explicit user decision required
190
+
105
191
  ### 2.3 Resolution Rules
106
192
 
107
193
  | Priority | Rule | Example |
@@ -161,7 +247,7 @@ Queue Item ID format: `S-N` (S-1, S-2, S-3, ...)
161
247
  {
162
248
  "item_id": "S-1",
163
249
  "issue_id": "ISS-20251227-003",
164
- "solution_id": "SOL-20251227-003",
250
+ "solution_id": "SOL-ISS-20251227-003-1",
165
251
  "status": "pending",
166
252
  "execution_order": 1,
167
253
  "execution_group": "P1",
@@ -189,7 +275,9 @@ Queue Item ID format: `S-N` (S-1, S-2, S-3, ...)
189
275
  }
190
276
  ```
191
277
 
192
- ### 3.3 Return Summary
278
+ ### 3.3 Return Summary (Brief)
279
+
280
+ Return brief summaries; full conflict details in separate files:
193
281
 
194
282
  ```json
195
283
  {
@@ -197,11 +285,27 @@ Queue Item ID format: `S-N` (S-1, S-2, S-3, ...)
197
285
  "total_solutions": N,
198
286
  "total_tasks": N,
199
287
  "execution_groups": [{ "id": "P1", "type": "parallel", "count": N }],
288
+ "conflicts_summary": [{
289
+ "id": "CFT-001",
290
+ "type": "api_conflict",
291
+ "severity": "high",
292
+ "summary": "Brief 1-line description",
293
+ "resolution": "sequential",
294
+ "details_path": ".workflow/issues/conflicts/CFT-001.json"
295
+ }],
296
+ "clarifications": [{
297
+ "conflict_id": "CFT-002",
298
+ "question": "Which solution should execute first?",
299
+ "options": [{ "value": "S-1", "label": "Solution summary" }],
300
+ "requires_user_input": true
301
+ }],
200
302
  "conflicts_resolved": N,
201
303
  "issues_queued": ["ISS-xxx", "ISS-yyy"]
202
304
  }
203
305
  ```
204
306
 
307
+ **Full Conflict Details**: Write to `.workflow/issues/conflicts/{conflict-id}.json`
308
+
205
309
  ---
206
310
 
207
311
  ## 4. Quality Standards
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: execute
3
3
  description: Execute queue with codex using DAG-based parallel orchestration (solution-level)
4
- argument-hint: "[--parallel <n>] [--executor codex|gemini|agent]"
4
+ argument-hint: ""
5
5
  allowed-tools: TodoWrite(*), Bash(*), Read(*), AskUserQuestion(*)
6
6
  ---
7
7
 
@@ -21,27 +21,22 @@ Minimal orchestrator that dispatches **solution IDs** to executors. Each executo
21
21
  ## Usage
22
22
 
23
23
  ```bash
24
- /issue:execute [FLAGS]
25
-
26
- # Examples
27
- /issue:execute # Execute with default parallelism
28
- /issue:execute --parallel 4 # Execute up to 4 tasks in parallel
29
- /issue:execute --executor agent # Use agent instead of codex
30
-
31
- # Flags
32
- --parallel <n> Max parallel executors (default: 3)
33
- --executor <type> Force executor: codex|gemini|agent (default: codex)
34
- --dry-run Show DAG and batches without executing
24
+ /issue:execute
35
25
  ```
36
26
 
27
+ **Parallelism**: Determined automatically by task dependency DAG (no manual control)
28
+ **Executor & Dry-run**: Selected via interactive prompt (AskUserQuestion)
29
+
37
30
  ## Execution Flow
38
31
 
39
32
  ```
40
- Phase 1: Get DAG
41
- └─ ccw issue queue dag → { parallel_batches: [["S-1","S-2"], ["S-3"]] }
33
+ Phase 1: Get DAG & User Selection
34
+ ├─ ccw issue queue dag → { parallel_batches: [["S-1","S-2"], ["S-3"]] }
35
+ └─ AskUserQuestion → executor type (codex|gemini|agent), dry-run mode
42
36
 
43
- Phase 2: Dispatch Parallel Batch
44
- ├─ For each solution ID in batch (parallel):
37
+ Phase 2: Dispatch Parallel Batch (DAG-driven)
38
+ ├─ Parallelism determined by DAG (no manual limit)
39
+ ├─ For each solution ID in batch (parallel - all at once):
45
40
  │ ├─ Executor calls: ccw issue detail <id> (READ-ONLY)
46
41
  │ ├─ Executor gets FULL SOLUTION with all tasks
47
42
  │ ├─ Executor implements all tasks sequentially (T1 → T2 → T3)
@@ -55,7 +50,7 @@ Phase 3: Next Batch
55
50
 
56
51
  ## Implementation
57
52
 
58
- ### Phase 1: Get DAG
53
+ ### Phase 1: Get DAG & User Selection
59
54
 
60
55
  ```javascript
61
56
  // Get dependency graph and parallel batches
@@ -77,9 +72,37 @@ console.log(`
77
72
  - Parallel in batch 1: ${dag.parallel_batches[0]?.length || 0}
78
73
  `);
79
74
 
75
+ // Interactive selection via AskUserQuestion
76
+ const answer = AskUserQuestion({
77
+ questions: [
78
+ {
79
+ question: 'Select executor type:',
80
+ header: 'Executor',
81
+ multiSelect: false,
82
+ options: [
83
+ { label: 'Codex (Recommended)', description: 'Autonomous coding with full write access' },
84
+ { label: 'Gemini', description: 'Large context analysis and implementation' },
85
+ { label: 'Agent', description: 'Claude Code sub-agent for complex tasks' }
86
+ ]
87
+ },
88
+ {
89
+ question: 'Execution mode:',
90
+ header: 'Mode',
91
+ multiSelect: false,
92
+ options: [
93
+ { label: 'Execute (Recommended)', description: 'Run all ready solutions' },
94
+ { label: 'Dry-run', description: 'Show DAG and batches without executing' }
95
+ ]
96
+ }
97
+ ]
98
+ });
99
+
100
+ const executor = answer['Executor'].toLowerCase().split(' ')[0]; // codex|gemini|agent
101
+ const isDryRun = answer['Mode'].includes('Dry-run');
102
+
80
103
  // Dry run mode
81
- if (flags.dryRun) {
82
- console.log('### Parallel Batches:\n');
104
+ if (isDryRun) {
105
+ console.log('### Parallel Batches (Dry-run):\n');
83
106
  dag.parallel_batches.forEach((batch, i) => {
84
107
  console.log(`Batch ${i + 1}: ${batch.join(', ')}`);
85
108
  });
@@ -87,13 +110,11 @@ if (flags.dryRun) {
87
110
  }
88
111
  ```
89
112
 
90
- ### Phase 2: Dispatch Parallel Batch
113
+ ### Phase 2: Dispatch Parallel Batch (DAG-driven)
91
114
 
92
115
  ```javascript
93
- const parallelLimit = flags.parallel || 3;
94
- const executor = flags.executor || 'codex';
95
-
96
- // Process first batch (all solutions can run in parallel)
116
+ // Parallelism determined by DAG - no manual limit
117
+ // All solutions in same batch have NO file conflicts and can run in parallel
97
118
  const batch = dag.parallel_batches[0] || [];
98
119
 
99
120
  // Initialize TodoWrite
@@ -105,24 +126,16 @@ TodoWrite({
105
126
  }))
106
127
  });
107
128
 
108
- // Dispatch all in parallel (up to limit)
109
- const chunks = [];
110
- for (let i = 0; i < batch.length; i += parallelLimit) {
111
- chunks.push(batch.slice(i, i + parallelLimit));
112
- }
113
-
114
- for (const chunk of chunks) {
115
- console.log(`\n### Executing Solutions: ${chunk.join(', ')}`);
129
+ console.log(`\n### Executing Solutions (DAG batch 1): ${batch.join(', ')}`);
116
130
 
117
- // Launch all in parallel
118
- const executions = chunk.map(solutionId => {
119
- updateTodo(solutionId, 'in_progress');
120
- return dispatchExecutor(solutionId, executor);
121
- });
131
+ // Launch ALL solutions in batch in parallel (DAG guarantees no conflicts)
132
+ const executions = batch.map(solutionId => {
133
+ updateTodo(solutionId, 'in_progress');
134
+ return dispatchExecutor(solutionId, executor);
135
+ });
122
136
 
123
- await Promise.all(executions);
124
- chunk.forEach(id => updateTodo(id, 'completed'));
125
- }
137
+ await Promise.all(executions);
138
+ batch.forEach(id => updateTodo(id, 'completed'));
126
139
  ```
127
140
 
128
141
  ### Executor Dispatch
@@ -35,16 +35,11 @@ interface Issue {
35
35
  affected_components?: string[];// Files/modules affected
36
36
  reproduction_steps?: string[]; // Steps to reproduce
37
37
 
38
- // Discovery context (when source='discovery')
39
- discovery_context?: {
40
- discovery_id: string; // Source discovery session
41
- perspective: string; // bug, test, quality, etc.
42
- category: string; // Finding category
43
- file: string; // Primary affected file
44
- line: number; // Line number
45
- snippet?: string; // Code snippet
46
- confidence: number; // Agent confidence (0-1)
38
+ // Extended context (minimal, for planning hints)
39
+ extended_context?: {
40
+ location?: string; // file:line when specific line matters
47
41
  suggested_fix?: string; // Suggested remediation
42
+ notes?: string; // Additional notes (user clarifications or discovery hints)
48
43
  };
49
44
 
50
45
  // Closed-loop requirements (guide plan generation)
@@ -290,6 +285,108 @@ const lifecycle = {
290
285
  issueData.lifecycle_requirements = lifecycle;
291
286
  ```
292
287
 
288
+ ### Phase 4.5: Follow-up Questions (Intelligent Suggestions)
289
+
290
+ ```javascript
291
+ // Analyze parsed data and suggest follow-up questions for missing/unclear fields
292
+ const suggestions = [];
293
+
294
+ // Check for missing critical fields
295
+ if (!issueData.expected_behavior) {
296
+ suggestions.push({
297
+ field: 'expected_behavior',
298
+ question: 'What is the expected behavior?',
299
+ hint: 'Describe what should happen when working correctly'
300
+ });
301
+ }
302
+
303
+ if (!issueData.actual_behavior && issueData.source !== 'discovery') {
304
+ suggestions.push({
305
+ field: 'actual_behavior',
306
+ question: 'What is the actual behavior?',
307
+ hint: 'Describe what currently happens (error message, wrong output, etc.)'
308
+ });
309
+ }
310
+
311
+ if (!issueData.affected_components?.length) {
312
+ suggestions.push({
313
+ field: 'affected_components',
314
+ question: 'Which files or modules are affected?',
315
+ hint: 'e.g., src/auth/login.ts, src/api/users.ts'
316
+ });
317
+ }
318
+
319
+ if (!issueData.reproduction_steps?.length && issueData.source === 'text') {
320
+ suggestions.push({
321
+ field: 'reproduction_steps',
322
+ question: 'How can this issue be reproduced?',
323
+ hint: 'Step-by-step instructions to trigger the issue'
324
+ });
325
+ }
326
+
327
+ // Ask follow-up questions if any suggestions exist
328
+ let userNotes = '';
329
+ if (suggestions.length > 0) {
330
+ console.log(`
331
+ ## Suggested Clarifications
332
+
333
+ The following information would help with issue resolution:
334
+ ${suggestions.map((s, i) => `${i+1}. **${s.question}** - ${s.hint}`).join('\n')}
335
+ `);
336
+
337
+ const followUpAnswer = AskUserQuestion({
338
+ questions: [{
339
+ question: 'Would you like to provide additional details?',
340
+ header: 'Clarify',
341
+ multiSelect: false,
342
+ options: [
343
+ { label: 'Add details', description: 'Provide clarifications for suggested questions' },
344
+ { label: 'Skip', description: 'Continue without additional details (Recommended)' }
345
+ ]
346
+ }]
347
+ });
348
+
349
+ if (followUpAnswer.includes('Add details')) {
350
+ // Collect additional notes via "Other" input
351
+ const notesAnswer = AskUserQuestion({
352
+ questions: [{
353
+ question: 'Enter additional details (address any of the suggested questions):',
354
+ header: 'Details',
355
+ multiSelect: false,
356
+ options: [
357
+ { label: 'Use template', description: 'Expected: ... | Actual: ... | Files: ...' }
358
+ ]
359
+ }]
360
+ });
361
+
362
+ if (notesAnswer.customText) {
363
+ userNotes = notesAnswer.customText;
364
+
365
+ // Parse structured input if provided
366
+ const expectedMatch = userNotes.match(/expected:\s*([^|]+)/i);
367
+ const actualMatch = userNotes.match(/actual:\s*([^|]+)/i);
368
+ const filesMatch = userNotes.match(/files?:\s*([^|]+)/i);
369
+
370
+ if (expectedMatch && !issueData.expected_behavior) {
371
+ issueData.expected_behavior = expectedMatch[1].trim();
372
+ }
373
+ if (actualMatch && !issueData.actual_behavior) {
374
+ issueData.actual_behavior = actualMatch[1].trim();
375
+ }
376
+ if (filesMatch && !issueData.affected_components?.length) {
377
+ issueData.affected_components = filesMatch[1].split(/[,\s]+/).filter(f => f.includes('/') || f.includes('.'));
378
+ }
379
+ }
380
+ }
381
+ }
382
+
383
+ // Store user notes in extended context
384
+ issueData.extended_context = {
385
+ ...(issueData.extended_context || {}),
386
+ notes: userNotes || null
387
+ };
388
+ ```
389
+
293
390
  ### Phase 5: User Confirmation
294
391
 
295
392
  ```javascript
@@ -377,6 +474,9 @@ const newIssue = {
377
474
  affected_components: issueData.affected_components || [],
378
475
  reproduction_steps: issueData.reproduction_steps || [],
379
476
 
477
+ // Extended context (universal)
478
+ extended_context: issueData.extended_context || null,
479
+
380
480
  // Closed-loop lifecycle requirements
381
481
  lifecycle_requirements: issueData.lifecycle_requirements || {
382
482
  test_strategy: 'auto',
@@ -395,9 +495,11 @@ const newIssue = {
395
495
  // Ensure directory exists
396
496
  Bash('mkdir -p .workflow/issues');
397
497
 
398
- // Append to issues.jsonl
498
+ // Append to issues.jsonl with proper newline handling
399
499
  const issuesPath = '.workflow/issues/issues.jsonl';
400
- Bash(`echo '${JSON.stringify(newIssue)}' >> "${issuesPath}"`);
500
+ const jsonLine = JSON.stringify(newIssue);
501
+ // Ensure file ends with newline before appending, then append with trailing newline
502
+ Bash(`[ -s "${issuesPath}" ] && [ -n "$(tail -c 1 "${issuesPath}")" ] && printf '\\n' >> "${issuesPath}"; printf '%s\\n' '${jsonLine}' >> "${issuesPath}"`);
401
503
 
402
504
  console.log(`
403
505
  ## Issue Created