Skip to content

Commit a061fc1

Browse files
author
catlog22
committed
fix(issue-plan-agent): Update acceptance criteria terminology and enhance issue loading process with metadata
1 parent 0992d27 commit a061fc1

File tree

3 files changed

+96
-31
lines changed

3 files changed

+96
-31
lines changed

.claude/agents/issue-plan-agent.md

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ color: green
2626
- Dependency DAG validation
2727
- Auto-bind for single solution, return for selection on multiple
2828

29-
**Key Principle**: Generate tasks conforming to schema with quantified delivery_criteria.
29+
**Key Principle**: Generate tasks conforming to schema with quantified acceptance criteria.
3030

3131
---
3232

@@ -71,11 +71,17 @@ function analyzeIssue(issue) {
7171
issue_id: issue.id,
7272
requirements: extractRequirements(issue.description),
7373
scope: inferScope(issue.title, issue.description),
74-
complexity: determineComplexity(issue) // Low | Medium | High
74+
complexity: determineComplexity(issue), // Low | Medium | High
75+
lifecycle: issue.lifecycle_requirements // User preferences for test/commit
7576
}
7677
}
7778
```
7879

80+
**Step 3**: Apply lifecycle requirements to tasks
81+
- `lifecycle.test_strategy` → Configure `test.unit`, `test.commands`
82+
- `lifecycle.commit_strategy` → Configure `commit.type`, `commit.scope`
83+
- `lifecycle.regression_scope` → Configure `regression` array
84+
7985
**Complexity Rules**:
8086
| Complexity | Files | Tasks |
8187
|------------|-------|-------|
@@ -147,18 +153,29 @@ Generate multiple candidate solutions when:
147153
```javascript
148154
function decomposeTasks(issue, exploration) {
149155
return groups.map(group => ({
150-
id: `TASK-${String(taskId++).padStart(3, '0')}`,
156+
id: `T${taskId++}`, // Pattern: ^T[0-9]+$
151157
title: group.title,
152-
type: inferType(group), // feature | bug | refactor | test | chore | docs
158+
scope: inferScope(group), // Module path
159+
action: inferAction(group), // Create | Update | Implement | ...
153160
description: group.description,
154-
file_context: group.files,
161+
modification_points: mapModificationPoints(group),
162+
implementation: generateSteps(group), // Step-by-step guide
163+
test: {
164+
unit: generateUnitTests(group),
165+
commands: ['npm test']
166+
},
167+
acceptance: {
168+
criteria: generateCriteria(group), // Quantified checklist
169+
verification: generateVerification(group)
170+
},
171+
commit: {
172+
type: inferCommitType(group), // feat | fix | refactor | ...
173+
scope: inferScope(group),
174+
message_template: generateCommitMsg(group)
175+
},
155176
depends_on: inferDependencies(group, tasks),
156-
delivery_criteria: generateDeliveryCriteria(group), // Quantified checklist
157-
pause_criteria: identifyBlockers(group),
158-
status: 'pending',
159-
current_phase: 'analyze',
160177
executor: inferExecutor(group),
161-
priority: calculatePriority(group)
178+
priority: calculatePriority(group) // 1-5 (1=highest)
162179
}))
163180
}
164181
```

.claude/agents/issue-queue-agent.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,16 @@ function detectConflicts(fileModifications, graph) {
126126

127127
### 2.4 Semantic Priority
128128

129+
**Base Priority Mapping** (task.priority 1-5 → base score):
130+
| task.priority | Base Score | Meaning |
131+
|---------------|------------|---------|
132+
| 1 | 0.8 | Highest |
133+
| 2 | 0.65 | High |
134+
| 3 | 0.5 | Medium |
135+
| 4 | 0.35 | Low |
136+
| 5 | 0.2 | Lowest |
137+
138+
**Action-based Boost** (applied to base score):
129139
| Factor | Boost |
130140
|--------|-------|
131141
| Create action | +0.2 |
@@ -138,6 +148,8 @@ function detectConflicts(fileModifications, graph) {
138148
| Test action | -0.1 |
139149
| Delete action | -0.15 |
140150

151+
**Formula**: `semantic_priority = clamp(baseScore + sum(boosts), 0.0, 1.0)`
152+
141153
### 2.5 Group Assignment
142154

143155
- **Parallel (P*)**: Tasks with no dependencies or conflicts between them

.claude/commands/issue/plan.md

Lines changed: 57 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ Unified planning command using **issue-plan-agent** that combines exploration an
7070
```
7171
Phase 1: Issue Loading
7272
├─ Parse input (single, comma-separated, or --all-pending)
73-
├─ Load issues from .workflow/issues/issues.jsonl
73+
├─ Fetch issue metadata (ID, title, tags)
7474
├─ Validate issues exist (create if needed)
75-
└─ Group into batches (max 3 per batch)
75+
└─ Group by similarity (shared tags or title keywords, max 3 per batch)
7676
7777
Phase 2: Unified Explore + Plan (issue-plan-agent)
7878
├─ Launch issue-plan-agent per batch
@@ -97,41 +97,71 @@ Phase 4: Summary
9797

9898
## Implementation
9999

100-
### Phase 1: Issue Loading (IDs Only)
100+
### Phase 1: Issue Loading (ID + Title + Tags)
101101

102102
```javascript
103103
const batchSize = flags.batchSize || 3;
104-
let issueIds = [];
104+
let issues = []; // {id, title, tags}
105105

106106
if (flags.allPending) {
107-
// Get pending issue IDs directly via CLI
108-
const ids = Bash(`ccw issue list --status pending,registered --ids`).trim();
109-
issueIds = ids ? ids.split('\n').filter(Boolean) : [];
107+
// Get pending issues with metadata via CLI (JSON output)
108+
const result = Bash(`ccw issue list --status pending,registered --json`).trim();
109+
const parsed = result ? JSON.parse(result) : [];
110+
issues = parsed.map(i => ({ id: i.id, title: i.title || '', tags: i.tags || [] }));
110111

111-
if (issueIds.length === 0) {
112+
if (issues.length === 0) {
112113
console.log('No pending issues found.');
113114
return;
114115
}
115-
console.log(`Found ${issueIds.length} pending issues`);
116+
console.log(`Found ${issues.length} pending issues`);
116117
} else {
117-
// Parse comma-separated issue IDs
118-
issueIds = userInput.includes(',')
118+
// Parse comma-separated issue IDs, fetch metadata
119+
const ids = userInput.includes(',')
119120
? userInput.split(',').map(s => s.trim())
120121
: [userInput.trim()];
121122

122-
// Create if not exists
123-
for (const id of issueIds) {
123+
for (const id of ids) {
124124
Bash(`ccw issue init ${id} --title "Issue ${id}" 2>/dev/null || true`);
125+
const info = Bash(`ccw issue status ${id} --json`).trim();
126+
const parsed = info ? JSON.parse(info) : {};
127+
issues.push({ id, title: parsed.title || '', tags: parsed.tags || [] });
125128
}
126129
}
127130

128-
// Group into batches
129-
const batches = [];
130-
for (let i = 0; i < issueIds.length; i += batchSize) {
131-
batches.push(issueIds.slice(i, i + batchSize));
131+
// Intelligent grouping by similarity (tags → title keywords)
132+
function groupBySimilarity(issues, maxSize) {
133+
const batches = [];
134+
const used = new Set();
135+
136+
for (const issue of issues) {
137+
if (used.has(issue.id)) continue;
138+
139+
const batch = [issue];
140+
used.add(issue.id);
141+
const issueTags = new Set(issue.tags);
142+
const issueWords = new Set(issue.title.toLowerCase().split(/\s+/));
143+
144+
// Find similar issues
145+
for (const other of issues) {
146+
if (used.has(other.id) || batch.length >= maxSize) continue;
147+
148+
// Similarity: shared tags or shared title keywords
149+
const sharedTags = other.tags.filter(t => issueTags.has(t)).length;
150+
const otherWords = other.title.toLowerCase().split(/\s+/);
151+
const sharedWords = otherWords.filter(w => issueWords.has(w) && w.length > 3).length;
152+
153+
if (sharedTags > 0 || sharedWords >= 2) {
154+
batch.push(other);
155+
used.add(other.id);
156+
}
157+
}
158+
batches.push(batch);
159+
}
160+
return batches;
132161
}
133162

134-
console.log(`Processing ${issueIds.length} issues in ${batches.length} batch(es)`);
163+
const batches = groupBySimilarity(issues, batchSize);
164+
console.log(`Processing ${issues.length} issues in ${batches.length} batch(es) (grouped by similarity)`);
135165

136166
TodoWrite({
137167
todos: batches.map((_, i) => ({
@@ -151,11 +181,16 @@ const pendingSelections = []; // Collect multi-solution issues for user selecti
151181
for (const [batchIndex, batch] of batches.entries()) {
152182
updateTodo(`Plan batch ${batchIndex + 1}`, 'in_progress');
153183

184+
// Build issue list with metadata for agent context
185+
const issueList = batch.map(i => `- ${i.id}: ${i.title}${i.tags.length ? ` [${i.tags.join(', ')}]` : ''}`).join('\n');
186+
154187
// Build minimal prompt - agent handles exploration, planning, and binding
155188
const issuePrompt = `
156189
## Plan Issues
157190
158-
**Issue IDs**: ${batch.join(', ')}
191+
**Issues** (grouped by similarity):
192+
${issueList}
193+
159194
**Project Root**: ${process.cwd()}
160195
161196
### Steps
@@ -181,10 +216,11 @@ for (const [batchIndex, batch] of batches.entries()) {
181216
`;
182217

183218
// Launch issue-plan-agent - agent writes solutions directly
219+
const batchIds = batch.map(i => i.id);
184220
const result = Task(
185221
subagent_type="issue-plan-agent",
186222
run_in_background=false,
187-
description=`Explore & plan ${batch.length} issues`,
223+
description=`Explore & plan ${batch.length} issues: ${batchIds.join(', ')}`,
188224
prompt=issuePrompt
189225
);
190226

@@ -244,7 +280,7 @@ const plannedIds = Bash(`ccw issue list --status planned --ids`).trim();
244280
const plannedCount = plannedIds ? plannedIds.split('\n').length : 0;
245281

246282
console.log(`
247-
## Done: ${issueIds.length} issues → ${plannedCount} planned
283+
## Done: ${issues.length} issues → ${plannedCount} planned
248284
249285
Next: \`/issue:queue\`\`/issue:execute\`
250286
`);

0 commit comments

Comments
 (0)