refactor(issue): simplify extended_context and add follow-up questions

- Replace discovery_context with minimal extended_context (3 fields: location, suggested_fix, notes)
- Add Phase 4.5: intelligent follow-up questions for missing issue fields
- Fix JSONL append to ensure proper newline handling
- Update schema and plan.md to use new extended_context structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
catlog22
2025-12-29 11:33:59 +08:00
parent 7c389d5028
commit 0d82c9fa03
3 changed files with 127 additions and 52 deletions

View File

@@ -35,16 +35,11 @@ interface Issue {
affected_components?: string[];// Files/modules affected
reproduction_steps?: string[]; // Steps to reproduce
// Discovery context (when source='discovery')
discovery_context?: {
discovery_id: string; // Source discovery session
perspective: string; // bug, test, quality, etc.
category: string; // Finding category
file: string; // Primary affected file
line: number; // Line number
snippet?: string; // Code snippet
confidence: number; // Agent confidence (0-1)
// Extended context (minimal, for planning hints)
extended_context?: {
location?: string; // file:line when specific line matters
suggested_fix?: string; // Suggested remediation
notes?: string; // Additional notes (user clarifications or discovery hints)
};
// Closed-loop requirements (guide plan generation)
@@ -290,6 +285,108 @@ const lifecycle = {
issueData.lifecycle_requirements = lifecycle;
```
### Phase 4.5: Follow-up Questions (Intelligent Suggestions)
```javascript
// Analyze parsed data and suggest follow-up questions for missing/unclear fields
const suggestions = [];
// Check for missing critical fields
if (!issueData.expected_behavior) {
suggestions.push({
field: 'expected_behavior',
question: 'What is the expected behavior?',
hint: 'Describe what should happen when working correctly'
});
}
if (!issueData.actual_behavior && issueData.source !== 'discovery') {
suggestions.push({
field: 'actual_behavior',
question: 'What is the actual behavior?',
hint: 'Describe what currently happens (error message, wrong output, etc.)'
});
}
if (!issueData.affected_components?.length) {
suggestions.push({
field: 'affected_components',
question: 'Which files or modules are affected?',
hint: 'e.g., src/auth/login.ts, src/api/users.ts'
});
}
if (!issueData.reproduction_steps?.length && issueData.source === 'text') {
suggestions.push({
field: 'reproduction_steps',
question: 'How can this issue be reproduced?',
hint: 'Step-by-step instructions to trigger the issue'
});
}
// Ask follow-up questions if any suggestions exist
let userNotes = '';
if (suggestions.length > 0) {
console.log(`
## Suggested Clarifications
The following information would help with issue resolution:
${suggestions.map((s, i) => `${i+1}. **${s.question}** - ${s.hint}`).join('\n')}
`);
const followUpAnswer = AskUserQuestion({
questions: [{
question: 'Would you like to provide additional details?',
header: 'Clarify',
multiSelect: false,
options: [
{ label: 'Add details', description: 'Provide clarifications for suggested questions' },
{ label: 'Skip', description: 'Continue without additional details (Recommended)' }
]
}]
});
if (followUpAnswer.includes('Add details')) {
// Collect additional notes via "Other" input
const notesAnswer = AskUserQuestion({
questions: [{
question: 'Enter additional details (address any of the suggested questions):',
header: 'Details',
multiSelect: false,
options: [
{ label: 'Use template', description: 'Expected: ... | Actual: ... | Files: ...' }
]
}]
});
if (notesAnswer.customText) {
userNotes = notesAnswer.customText;
// Parse structured input if provided
const expectedMatch = userNotes.match(/expected:\s*([^|]+)/i);
const actualMatch = userNotes.match(/actual:\s*([^|]+)/i);
const filesMatch = userNotes.match(/files?:\s*([^|]+)/i);
if (expectedMatch && !issueData.expected_behavior) {
issueData.expected_behavior = expectedMatch[1].trim();
}
if (actualMatch && !issueData.actual_behavior) {
issueData.actual_behavior = actualMatch[1].trim();
}
if (filesMatch && !issueData.affected_components?.length) {
issueData.affected_components = filesMatch[1].split(/[,\s]+/).filter(f => f.includes('/') || f.includes('.'));
}
}
}
}
// Store user notes in extended context
issueData.extended_context = {
...(issueData.extended_context || {}),
notes: userNotes || null
};
```
### Phase 5: User Confirmation
```javascript
@@ -377,6 +474,9 @@ const newIssue = {
affected_components: issueData.affected_components || [],
reproduction_steps: issueData.reproduction_steps || [],
// Extended context (universal)
extended_context: issueData.extended_context || null,
// Closed-loop lifecycle requirements
lifecycle_requirements: issueData.lifecycle_requirements || {
test_strategy: 'auto',
@@ -395,9 +495,11 @@ const newIssue = {
// Ensure directory exists
Bash('mkdir -p .workflow/issues');
// Append to issues.jsonl
// Append to issues.jsonl with proper newline handling
const issuesPath = '.workflow/issues/issues.jsonl';
Bash(`echo '${JSON.stringify(newIssue)}' >> "${issuesPath}"`);
const jsonLine = JSON.stringify(newIssue);
// Ensure file ends with newline before appending, then append with trailing newline
Bash(`[ -s "${issuesPath}" ] && [ -n "$(tail -c 1 "${issuesPath}")" ] && printf '\\n' >> "${issuesPath}"; printf '%s\\n' '${jsonLine}' >> "${issuesPath}"`);
console.log(`
## Issue Created

View File

@@ -207,7 +207,7 @@ ${issueList}
### Steps
1. Fetch: \`ccw issue status <id> --json\`
2. Load project context (project-tech.json + project-guidelines.json)
3. **If source=discovery**: Use discovery_context (file, line, snippet, suggested_fix) as planning hints
3. **If extended_context exists**: Use extended_context (location, suggested_fix, notes) as planning hints
4. Explore (ACE) → Plan solution (respecting guidelines)
5. Register & bind: \`ccw issue bind <id> --solution <file>\`

View File

@@ -42,43 +42,21 @@
"items": { "type": "string" },
"description": "Issue labels/tags"
},
"discovery_context": {
"extended_context": {
"type": "object",
"description": "Enriched context from issue:discover (only when source=discovery)",
"description": "Minimal extended context for planning hints",
"properties": {
"discovery_id": {
"location": {
"type": "string",
"description": "Source discovery session ID"
},
"perspective": {
"type": "string",
"enum": ["bug", "ux", "test", "quality", "security", "performance", "maintainability", "best-practices"]
},
"category": {
"type": "string",
"description": "Finding category (e.g., edge-case, race-condition)"
},
"file": {
"type": "string",
"description": "Primary affected file"
},
"line": {
"type": "integer",
"description": "Line number in primary file"
},
"snippet": {
"type": "string",
"description": "Code snippet showing the issue"
},
"confidence": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "Agent confidence score"
"description": "file:line format (e.g., 'src/auth.ts:42')"
},
"suggested_fix": {
"type": "string",
"description": "Suggested remediation from discovery"
"description": "Suggested remediation"
},
"notes": {
"type": "string",
"description": "Additional notes (user clarifications or discovery hints)"
}
}
},
@@ -143,15 +121,10 @@
"context": "Connection pool cleanup only happens when MAX_POOL_SIZE is reached...",
"source": "discovery",
"labels": ["bug", "resource-leak", "critical"],
"discovery_context": {
"discovery_id": "DSC-20251228-182237",
"perspective": "bug",
"category": "resource-leak",
"file": "storage/sqlite_store.py",
"line": 59,
"snippet": "if len(self._pool) >= self.MAX_POOL_SIZE:\n self._cleanup_stale_connections()",
"confidence": 0.85,
"suggested_fix": "Implement periodic cleanup or weak references"
"extended_context": {
"location": "storage/sqlite_store.py:59",
"suggested_fix": "Implement periodic cleanup or weak references",
"notes": null
},
"affected_components": ["storage/sqlite_store.py"],
"lifecycle_requirements": {