docs: Add issue parser estimation strategy
Critical enhancement for real-world usage - parser must handle: - Unformatted issues (estimate from content) - Incomplete metadata (best-guess + confidence score) - Oversized issues (auto-decompose before queuing) Three-level estimation: 1. Structured metadata → extract directly (95%+ confidence) 2. Content analysis → AI estimates from description (50-95%) 3. Minimal info → defaults + warn user (<50%) 50% rule enforcement: - Detect issues > 50% of agent's context limit - Auto-decompose into sub-issues using Opus - Create sub-issues in Gitea with dependencies - Label parent as EPIC Confidence-based workflow: - ≥60%: Queue automatically - 30-59%: Queue with warning - <30%: Don't queue, request more details Makes coordinator truly autonomous - handles whatever users throw at it. Refs #158 (COORD-002) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
753
docs/3-architecture/issue-parser-estimation-strategy.md
Normal file
753
docs/3-architecture/issue-parser-estimation-strategy.md
Normal file
@@ -0,0 +1,753 @@
|
|||||||
|
# Issue Parser Estimation Strategy
|
||||||
|
|
||||||
|
**Status:** Proposed (Phase 0 Enhancement)
|
||||||
|
**Related Issues:** COORD-002 (Issue Parser Agent)
|
||||||
|
**Priority:** Critical (P0) - Required for real-world usage
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
|
||||||
|
Not all issues will follow the formatted metadata structure used in COORD-XXX issues. The issue parser must handle:
|
||||||
|
|
||||||
|
1. **Unformatted issues** - Just title and description, no metadata
|
||||||
|
2. **Incomplete metadata** - Some fields present, others missing
|
||||||
|
3. **Oversized issues** - Exceed 50% rule, need decomposition
|
||||||
|
4. **Varying formats** - Different teams use different templates
|
||||||
|
|
||||||
|
**The parser must make intelligent estimates when metadata is missing.**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Estimation Strategy
|
||||||
|
|
||||||
|
### Level 1: Structured Metadata (Best Case)
|
||||||
|
|
||||||
|
**When issue has formatted metadata:**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Context Estimate
|
||||||
|
|
||||||
|
- Files to modify: 3
|
||||||
|
- Implementation complexity: medium (20000 tokens)
|
||||||
|
- **Total estimated: 46800 tokens**
|
||||||
|
- **Recommended agent: glm**
|
||||||
|
|
||||||
|
## Difficulty
|
||||||
|
|
||||||
|
medium
|
||||||
|
```
|
||||||
|
|
||||||
|
**Action:**
|
||||||
|
|
||||||
|
- Extract directly from markdown
|
||||||
|
- **Confidence: HIGH** (95%+)
|
||||||
|
- Use values as-is
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Level 2: Content Analysis (Common Case)
|
||||||
|
|
||||||
|
**When metadata is missing, analyze issue content:**
|
||||||
|
|
||||||
|
#### 2.1 Analyze Title and Description
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def estimate_from_content(issue: dict) -> dict:
|
||||||
|
"""Estimate metadata from issue content using AI."""
|
||||||
|
|
||||||
|
client = anthropic.Anthropic()
|
||||||
|
|
||||||
|
response = await client.messages.create(
|
||||||
|
model="claude-sonnet-4-5",
|
||||||
|
max_tokens=2000,
|
||||||
|
messages=[{
|
||||||
|
"role": "user",
|
||||||
|
"content": f"""Analyze this issue and estimate resource requirements.
|
||||||
|
|
||||||
|
Title: {issue['title']}
|
||||||
|
Description:
|
||||||
|
{issue['body']}
|
||||||
|
|
||||||
|
Estimate:
|
||||||
|
1. **Files to modify**: How many files will likely be touched?
|
||||||
|
- Count mentions of specific files, modules, components
|
||||||
|
- Look for scope indicators (refactor, add feature, fix bug)
|
||||||
|
|
||||||
|
2. **Implementation complexity**:
|
||||||
|
- Low: Simple CRUD, config changes, one-file fixes
|
||||||
|
- Medium: Multi-file changes, business logic, API development
|
||||||
|
- High: Architecture changes, complex refactoring, new systems
|
||||||
|
|
||||||
|
3. **Context estimate**:
|
||||||
|
- Use formula: (files × 7000) + complexity + tests + docs
|
||||||
|
- Low: ~20-40K tokens
|
||||||
|
- Medium: ~40-80K tokens
|
||||||
|
- High: ~80-150K tokens
|
||||||
|
|
||||||
|
4. **Difficulty**: low/medium/high
|
||||||
|
|
||||||
|
5. **Confidence**: 0-100% (based on clarity of issue description)
|
||||||
|
|
||||||
|
Return JSON:
|
||||||
|
{{
|
||||||
|
"estimated_context": <integer>,
|
||||||
|
"difficulty": "low" | "medium" | "high",
|
||||||
|
"assigned_agent": "haiku" | "sonnet" | "glm" | "opus",
|
||||||
|
"confidence": <integer 0-100>,
|
||||||
|
"reasoning": "Brief explanation of estimates"
|
||||||
|
}}
|
||||||
|
"""
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
|
||||||
|
metadata = json.loads(response.content[0].text)
|
||||||
|
|
||||||
|
# Add metadata source
|
||||||
|
metadata['source'] = 'content_analysis'
|
||||||
|
|
||||||
|
return metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
**Confidence factors:**
|
||||||
|
|
||||||
|
| Factor | High Confidence | Low Confidence |
|
||||||
|
| ----------------------- | ---------------------------- | ----------------------- |
|
||||||
|
| **Description length** | >500 chars, detailed | <100 chars, vague |
|
||||||
|
| **Specific mentions** | Files, modules, APIs named | Generic "fix the thing" |
|
||||||
|
| **Acceptance criteria** | Clear checklist | None provided |
|
||||||
|
| **Technical details** | Stack traces, logs, examples | "It's broken" |
|
||||||
|
| **Scope clarity** | Well-defined boundaries | Open-ended |
|
||||||
|
|
||||||
|
**Confidence scoring:**
|
||||||
|
|
||||||
|
````python
|
||||||
|
def calculate_confidence(issue: dict, analysis: dict) -> int:
|
||||||
|
"""Calculate confidence score 0-100."""
|
||||||
|
|
||||||
|
score = 50 # Start at neutral
|
||||||
|
|
||||||
|
# Description length
|
||||||
|
if len(issue['body']) > 500:
|
||||||
|
score += 15
|
||||||
|
elif len(issue['body']) < 100:
|
||||||
|
score -= 20
|
||||||
|
|
||||||
|
# Specific file/module mentions
|
||||||
|
code_patterns = r'(`[^`]+`|\.ts|\.py|\.js|src/|components/)'
|
||||||
|
mentions = len(re.findall(code_patterns, issue['body']))
|
||||||
|
score += min(mentions * 5, 20)
|
||||||
|
|
||||||
|
# Acceptance criteria
|
||||||
|
if '- [ ]' in issue['body'] or '- [x]' in issue['body']:
|
||||||
|
score += 10
|
||||||
|
|
||||||
|
# Technical details (stack traces, logs, code blocks)
|
||||||
|
if '```' in issue['body']:
|
||||||
|
score += 10
|
||||||
|
|
||||||
|
# Scope keywords
|
||||||
|
scope_keywords = ['refactor', 'implement', 'add', 'fix', 'update']
|
||||||
|
if any(kw in issue['title'].lower() for kw in scope_keywords):
|
||||||
|
score += 5
|
||||||
|
|
||||||
|
return max(0, min(100, score))
|
||||||
|
````
|
||||||
|
|
||||||
|
**Action:**
|
||||||
|
|
||||||
|
- Use AI to estimate from content
|
||||||
|
- **Confidence: MEDIUM** (50-80%)
|
||||||
|
- Comment confidence on issue
|
||||||
|
|
||||||
|
**Example comment:**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
🤖 Estimated metadata (confidence: 65%):
|
||||||
|
|
||||||
|
- Estimated context: 52,000 tokens
|
||||||
|
- Difficulty: medium
|
||||||
|
- Recommended agent: glm
|
||||||
|
|
||||||
|
📊 Reasoning:
|
||||||
|
|
||||||
|
- Mentions 3 components (UserService, AuthMiddleware, tests)
|
||||||
|
- Requires API changes (medium complexity)
|
||||||
|
- Has acceptance criteria (+confidence)
|
||||||
|
- Description is detailed (+confidence)
|
||||||
|
|
||||||
|
Note: These are estimates. Actual usage may vary.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Level 3: Minimal Information (Worst Case)
|
||||||
|
|
||||||
|
**When issue is very vague:**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Title: Fix the login bug
|
||||||
|
Body: Login doesn't work
|
||||||
|
```
|
||||||
|
|
||||||
|
**Action:**
|
||||||
|
|
||||||
|
- Use conservative defaults
|
||||||
|
- **Confidence: LOW** (<50%)
|
||||||
|
- Warn user, suggest more details
|
||||||
|
|
||||||
|
**Default estimates:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
DEFAULT_ESTIMATES = {
|
||||||
|
'estimated_context': 50000, # Conservative default
|
||||||
|
'difficulty': 'medium',
|
||||||
|
'assigned_agent': 'sonnet', # Safe middle-tier agent
|
||||||
|
'confidence': 30,
|
||||||
|
'reasoning': 'Minimal information provided, using defaults'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example comment:**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
⚠️ Low confidence estimate (30%):
|
||||||
|
|
||||||
|
- Estimated context: 50,000 tokens
|
||||||
|
- Difficulty: medium
|
||||||
|
- Recommended agent: sonnet
|
||||||
|
|
||||||
|
📝 Suggestion: For better estimates, please add:
|
||||||
|
|
||||||
|
- Which files/components are affected
|
||||||
|
- Expected scope (one file? multiple modules?)
|
||||||
|
- Acceptance criteria or definition of "done"
|
||||||
|
- Any relevant logs, stack traces, or examples
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Oversized Issue Detection & Decomposition
|
||||||
|
|
||||||
|
### 50% Rule Enforcement
|
||||||
|
|
||||||
|
**Before queuing, check if issue exceeds 50% of target agent's limit:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def check_and_decompose(issue: dict, metadata: dict) -> List[dict]:
|
||||||
|
"""Check if issue exceeds 50% rule. If so, decompose."""
|
||||||
|
|
||||||
|
# Get agent limit
|
||||||
|
agent = metadata['assigned_agent']
|
||||||
|
agent_limit = AGENT_PROFILES[agent]['context_limit']
|
||||||
|
max_issue_size = agent_limit * 0.5
|
||||||
|
|
||||||
|
# Check if oversized
|
||||||
|
if metadata['estimated_context'] > max_issue_size:
|
||||||
|
logger.warning(
|
||||||
|
f"Issue #{issue['number']} exceeds 50% rule: "
|
||||||
|
f"{metadata['estimated_context']} > {max_issue_size}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Decompose into sub-issues
|
||||||
|
sub_issues = await decompose_epic(issue, metadata)
|
||||||
|
|
||||||
|
# Comment on parent issue
|
||||||
|
await gitea_client.comment_on_issue(
|
||||||
|
issue['number'],
|
||||||
|
f"⚠️ Issue exceeds 50% rule ({metadata['estimated_context']:,} tokens)\n\n"
|
||||||
|
f"Auto-decomposing into {len(sub_issues)} sub-issues...\n\n"
|
||||||
|
f"This issue will be converted to an EPIC tracking the sub-issues."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Label as epic
|
||||||
|
await gitea_client.add_label(issue['number'], 'epic')
|
||||||
|
|
||||||
|
return sub_issues
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Single issue, good to go
|
||||||
|
return [issue]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Automatic Epic Decomposition
|
||||||
|
|
||||||
|
**When issue is oversized, use AI to break it down:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def decompose_epic(issue: dict, metadata: dict) -> List[dict]:
|
||||||
|
"""Decompose oversized issue into sub-issues."""
|
||||||
|
|
||||||
|
client = anthropic.Anthropic()
|
||||||
|
|
||||||
|
# Get max issue size for target agent
|
||||||
|
agent = metadata['assigned_agent']
|
||||||
|
max_size = AGENT_PROFILES[agent]['context_limit'] * 0.5
|
||||||
|
|
||||||
|
response = await client.messages.create(
|
||||||
|
model="claude-opus-4-5", # Use Opus for decomposition
|
||||||
|
max_tokens=4000,
|
||||||
|
messages=[{
|
||||||
|
"role": "user",
|
||||||
|
"content": f"""This issue is too large ({metadata['estimated_context']:,} tokens)
|
||||||
|
and must be broken into smaller sub-issues.
|
||||||
|
|
||||||
|
**Original Issue:**
|
||||||
|
Title: {issue['title']}
|
||||||
|
Body: {issue['body']}
|
||||||
|
|
||||||
|
**Constraints:**
|
||||||
|
- Each sub-issue must be ≤ {max_size:,} tokens
|
||||||
|
- Sub-issues should be independently completable
|
||||||
|
- Maintain logical order (dependencies)
|
||||||
|
- Cover all aspects of original issue
|
||||||
|
|
||||||
|
**Instructions:**
|
||||||
|
1. Identify logical breakdown points
|
||||||
|
2. Create 3-6 sub-issues
|
||||||
|
3. Estimate context for each
|
||||||
|
4. Define dependencies (what must come first)
|
||||||
|
|
||||||
|
Return JSON array:
|
||||||
|
[
|
||||||
|
{{
|
||||||
|
"title": "Sub-issue title",
|
||||||
|
"description": "Detailed description",
|
||||||
|
"estimated_context": <integer>,
|
||||||
|
"difficulty": "low" | "medium" | "high",
|
||||||
|
"depends_on": [<array of titles this depends on>]
|
||||||
|
}},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
|
||||||
|
Ensure NO sub-issue exceeds {max_size:,} tokens.
|
||||||
|
"""
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
|
||||||
|
sub_issues = json.loads(response.content[0].text)
|
||||||
|
|
||||||
|
# Validate all sub-issues fit 50% rule
|
||||||
|
for sub in sub_issues:
|
||||||
|
if sub['estimated_context'] > max_size:
|
||||||
|
raise ValueError(
|
||||||
|
f"Sub-issue '{sub['title']}' still exceeds limit: "
|
||||||
|
f"{sub['estimated_context']} > {max_size}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create sub-issues in Gitea
|
||||||
|
created_issues = []
|
||||||
|
issue_map = {} # title -> issue number
|
||||||
|
|
||||||
|
for sub in sub_issues:
|
||||||
|
# Create issue
|
||||||
|
new_issue = await gitea_client.create_issue(
|
||||||
|
title=f"[SUB] {sub['title']}",
|
||||||
|
body=f"""**Parent Epic:** #{issue['number']} - {issue['title']}
|
||||||
|
|
||||||
|
## Objective
|
||||||
|
{sub['description']}
|
||||||
|
|
||||||
|
## Context Estimate
|
||||||
|
- **Total estimated: {sub['estimated_context']:,} tokens**
|
||||||
|
- Difficulty: {sub['difficulty']}
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
{format_dependencies(sub['depends_on'], issue_map)}
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
Auto-generated from epic decomposition.
|
||||||
|
""",
|
||||||
|
labels=['sub-issue', f"p{issue.get('priority', 1)}"],
|
||||||
|
milestone=issue.get('milestone')
|
||||||
|
)
|
||||||
|
|
||||||
|
created_issues.append(new_issue)
|
||||||
|
issue_map[sub['title']] = new_issue['number']
|
||||||
|
|
||||||
|
# Update parent issue to reference sub-issues
|
||||||
|
sub_issue_list = '\n'.join(
|
||||||
|
f"- #{i['number']} {i['title']}"
|
||||||
|
for i in created_issues
|
||||||
|
)
|
||||||
|
|
||||||
|
await gitea_client.comment_on_issue(
|
||||||
|
issue['number'],
|
||||||
|
f"## Sub-Issues Created\n\n{sub_issue_list}\n\n"
|
||||||
|
f"This issue is now an EPIC. Close this when all sub-issues complete."
|
||||||
|
)
|
||||||
|
|
||||||
|
return created_issues
|
||||||
|
```
|
||||||
|
|
||||||
|
**Example decomposition:**
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Original Issue #200: "Refactor authentication system"
|
||||||
|
Estimated: 180,000 tokens (EXCEEDS 50% rule for Opus: 100K limit)
|
||||||
|
|
||||||
|
Auto-decomposed into:
|
||||||
|
├─ #201 [SUB] Extract auth middleware (45K tokens) → Ready
|
||||||
|
├─ #202 [SUB] Implement JWT service (38K tokens) → Blocked by #201
|
||||||
|
├─ #203 [SUB] Add token refresh logic (32K tokens) → Blocked by #202
|
||||||
|
├─ #204 [SUB] Update auth guards (28K tokens) → Blocked by #202
|
||||||
|
└─ #205 [SUB] Add integration tests (35K tokens) → Blocked by #201,#202,#203,#204
|
||||||
|
|
||||||
|
Total: 178K tokens across 5 sub-issues
|
||||||
|
Each sub-issue: ≤50K tokens ✅
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Confidence-Based Workflow
|
||||||
|
|
||||||
|
### High Confidence (95%+)
|
||||||
|
|
||||||
|
- **Source:** Structured metadata in issue body
|
||||||
|
- **Action:** Use values directly, queue immediately
|
||||||
|
- **Comment:** "✅ Metadata detected, high confidence"
|
||||||
|
|
||||||
|
### Medium Confidence (50-95%)
|
||||||
|
|
||||||
|
- **Source:** Content analysis
|
||||||
|
- **Action:** Use estimates, queue with note
|
||||||
|
- **Comment:** "📊 Estimated from content (confidence: X%)"
|
||||||
|
|
||||||
|
### Low Confidence (<50%)
|
||||||
|
|
||||||
|
- **Source:** Minimal info, using defaults
|
||||||
|
- **Action:** Use defaults, warn user
|
||||||
|
- **Comment:** "⚠️ Low confidence - please add details"
|
||||||
|
- **Optional:** Wait for user to update issue before queuing
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Confidence Thresholds
|
||||||
|
|
||||||
|
```python
|
||||||
|
class ConfidenceThresholds:
|
||||||
|
"""Confidence-based behavior thresholds."""
|
||||||
|
|
||||||
|
AUTO_QUEUE = 60 # ≥60% confidence: Queue automatically
|
||||||
|
WARN_USER = 50 # <50% confidence: Warn user
|
||||||
|
WAIT_FOR_UPDATE = 30 # <30% confidence: Don't queue, wait for update
|
||||||
|
```
|
||||||
|
|
||||||
|
**Workflow:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def handle_issue_assignment(issue: dict):
|
||||||
|
"""Handle issue assigned to @mosaic."""
|
||||||
|
|
||||||
|
# Parse metadata (structured or estimated)
|
||||||
|
metadata = await parse_issue_metadata(issue)
|
||||||
|
|
||||||
|
# Check confidence
|
||||||
|
if metadata['confidence'] >= ConfidenceThresholds.AUTO_QUEUE:
|
||||||
|
# High/medium confidence - queue it
|
||||||
|
await queue_manager.enqueue(issue, metadata)
|
||||||
|
|
||||||
|
await gitea_client.comment_on_issue(
|
||||||
|
issue['number'],
|
||||||
|
f"🤖 Added to coordinator queue\n\n"
|
||||||
|
f"**Metadata** (confidence: {metadata['confidence']}%):\n"
|
||||||
|
f"- Estimated context: {metadata['estimated_context']:,} tokens\n"
|
||||||
|
f"- Difficulty: {metadata['difficulty']}\n"
|
||||||
|
f"- Assigned agent: {metadata['assigned_agent']}\n\n"
|
||||||
|
f"{metadata.get('reasoning', '')}"
|
||||||
|
)
|
||||||
|
|
||||||
|
elif metadata['confidence'] >= ConfidenceThresholds.WARN_USER:
|
||||||
|
# Low confidence - queue but warn
|
||||||
|
await queue_manager.enqueue(issue, metadata)
|
||||||
|
|
||||||
|
await gitea_client.comment_on_issue(
|
||||||
|
issue['number'],
|
||||||
|
f"⚠️ Low confidence estimate ({metadata['confidence']}%)\n\n"
|
||||||
|
f"Using best-guess estimates:\n"
|
||||||
|
f"- Estimated context: {metadata['estimated_context']:,} tokens\n"
|
||||||
|
f"- Difficulty: {metadata['difficulty']}\n"
|
||||||
|
f"- Assigned agent: {metadata['assigned_agent']}\n\n"
|
||||||
|
f"💡 For better estimates, please add:\n"
|
||||||
|
f"- Which files/components are affected\n"
|
||||||
|
f"- Expected scope\n"
|
||||||
|
f"- Acceptance criteria\n\n"
|
||||||
|
f"Queued anyway - work will proceed with these estimates."
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Very low confidence - don't queue
|
||||||
|
await gitea_client.comment_on_issue(
|
||||||
|
issue['number'],
|
||||||
|
f"❌ Cannot queue - insufficient information ({metadata['confidence']}%)\n\n"
|
||||||
|
f"Please add more details:\n"
|
||||||
|
f"- What files/components need changes?\n"
|
||||||
|
f"- What is the expected scope?\n"
|
||||||
|
f"- What are the acceptance criteria?\n\n"
|
||||||
|
f"Re-assign to @mosaic when ready."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Unassign from coordinator
|
||||||
|
await gitea_client.unassign_issue(issue['number'], 'mosaic')
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Edge Cases
|
||||||
|
|
||||||
|
### Case 1: Issue Updated After Queuing
|
||||||
|
|
||||||
|
**User adds details after low-confidence queuing:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
@app.post('/webhook/gitea')
|
||||||
|
async def handle_webhook(payload: dict):
|
||||||
|
"""Handle Gitea webhooks."""
|
||||||
|
|
||||||
|
if payload['action'] == 'edited':
|
||||||
|
issue = payload['issue']
|
||||||
|
|
||||||
|
# Check if already in queue
|
||||||
|
if queue_manager.has_issue(issue['number']):
|
||||||
|
# Re-parse with updated content
|
||||||
|
new_metadata = await parse_issue_metadata(issue)
|
||||||
|
|
||||||
|
# Update queue
|
||||||
|
queue_manager.update_metadata(issue['number'], new_metadata)
|
||||||
|
|
||||||
|
await gitea_client.comment_on_issue(
|
||||||
|
issue['number'],
|
||||||
|
f"🔄 Issue updated - re-estimated metadata:\n"
|
||||||
|
f"- Estimated context: {new_metadata['estimated_context']:,} tokens\n"
|
||||||
|
f"- Difficulty: {new_metadata['difficulty']}\n"
|
||||||
|
f"- Confidence: {new_metadata['confidence']}%"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Case 2: Decomposition Creates More Oversized Issues
|
||||||
|
|
||||||
|
**If decomposed sub-issue still exceeds 50% rule:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Recursive decomposition
|
||||||
|
async def decompose_epic(issue: dict, metadata: dict, depth: int = 0) -> List[dict]:
|
||||||
|
"""Decompose with recursion limit."""
|
||||||
|
|
||||||
|
if depth > 2:
|
||||||
|
raise ValueError(
|
||||||
|
f"Issue #{issue['number']} cannot be decomposed enough. "
|
||||||
|
f"Manual intervention required."
|
||||||
|
)
|
||||||
|
|
||||||
|
sub_issues = await ai_decompose(issue, metadata)
|
||||||
|
|
||||||
|
# Check if any sub-issue is still too large
|
||||||
|
oversized = [s for s in sub_issues if s['estimated_context'] > max_size]
|
||||||
|
|
||||||
|
if oversized:
|
||||||
|
# Recursively decompose oversized sub-issues
|
||||||
|
final_issues = []
|
||||||
|
for sub in sub_issues:
|
||||||
|
if sub['estimated_context'] > max_size:
|
||||||
|
# Decompose further
|
||||||
|
sub_sub_issues = await decompose_epic(sub, sub, depth + 1)
|
||||||
|
final_issues.extend(sub_sub_issues)
|
||||||
|
else:
|
||||||
|
final_issues.append(sub)
|
||||||
|
return final_issues
|
||||||
|
|
||||||
|
return sub_issues
|
||||||
|
```
|
||||||
|
|
||||||
|
### Case 3: No Clear Decomposition
|
||||||
|
|
||||||
|
**If AI can't find good breakdown points:**
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Comment on issue, unassign from coordinator
|
||||||
|
await gitea_client.comment_on_issue(
|
||||||
|
issue['number'],
|
||||||
|
f"❌ Cannot auto-decompose this issue.\n\n"
|
||||||
|
f"Estimated at {metadata['estimated_context']:,} tokens "
|
||||||
|
f"(exceeds {max_size:,} limit), but no clear breakdown found.\n\n"
|
||||||
|
f"**Manual action needed:**\n"
|
||||||
|
f"1. Break this into smaller sub-issues manually\n"
|
||||||
|
f"2. Assign sub-issues to @mosaic\n"
|
||||||
|
f"3. This issue can become an EPIC tracking sub-issues\n\n"
|
||||||
|
f"Unassigning from coordinator."
|
||||||
|
)
|
||||||
|
|
||||||
|
await gitea_client.unassign_issue(issue['number'], 'mosaic')
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Implementation Checklist
|
||||||
|
|
||||||
|
**Phase 0 (COORD-002) must include:**
|
||||||
|
|
||||||
|
- [ ] Structured metadata extraction (existing plan)
|
||||||
|
- [ ] Content analysis estimation (NEW)
|
||||||
|
- [ ] Confidence scoring (NEW)
|
||||||
|
- [ ] Best-guess defaults (NEW)
|
||||||
|
- [ ] 50% rule validation (NEW)
|
||||||
|
- [ ] Automatic epic decomposition (NEW)
|
||||||
|
- [ ] Recursive decomposition handling (NEW)
|
||||||
|
- [ ] Confidence-based workflow (NEW)
|
||||||
|
- [ ] Update issue handling (NEW)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
|
||||||
|
**Parser handles all issue types:**
|
||||||
|
|
||||||
|
- ✅ Formatted issues → High confidence, extract directly
|
||||||
|
- ✅ Unformatted issues → Medium confidence, estimate from content
|
||||||
|
- ✅ Vague issues → Low confidence, use defaults + warn
|
||||||
|
- ✅ Oversized issues → Auto-decompose, create sub-issues
|
||||||
|
- ✅ Updated issues → Re-parse, update queue
|
||||||
|
|
||||||
|
**No manual intervention needed for:**
|
||||||
|
|
||||||
|
- Well-formatted issues
|
||||||
|
- Clear descriptions (even without metadata)
|
||||||
|
- Oversized issues (auto-decompose)
|
||||||
|
|
||||||
|
**Manual intervention only for:**
|
||||||
|
|
||||||
|
- Very vague issues (<30% confidence)
|
||||||
|
- Issues that can't be decomposed
|
||||||
|
- Edge cases requiring human judgment
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Example Scenarios
|
||||||
|
|
||||||
|
### Scenario 1: Well-Formatted Issue
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Issue #300: [COORD-020] Implement user profile caching
|
||||||
|
|
||||||
|
## Context Estimate
|
||||||
|
|
||||||
|
- Files: 4
|
||||||
|
- Total: 52,000 tokens
|
||||||
|
- Agent: glm
|
||||||
|
|
||||||
|
## Difficulty
|
||||||
|
|
||||||
|
medium
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result:**
|
||||||
|
|
||||||
|
- ✅ Extract directly
|
||||||
|
- Confidence: 95%
|
||||||
|
- Queue immediately
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Scenario 2: Clear But Unformatted Issue
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Issue #301: Add caching to user profile API
|
||||||
|
|
||||||
|
Need to cache user profiles to reduce database load.
|
||||||
|
|
||||||
|
Files affected:
|
||||||
|
|
||||||
|
- src/api/users/users.service.ts
|
||||||
|
- src/cache/cache.service.ts
|
||||||
|
- src/api/users/users.controller.ts
|
||||||
|
- tests/users.service.spec.ts
|
||||||
|
|
||||||
|
Acceptance criteria:
|
||||||
|
|
||||||
|
- [ ] Cache GET /users/:id requests
|
||||||
|
- [ ] 5 minute TTL
|
||||||
|
- [ ] Invalidate on update/delete
|
||||||
|
- [ ] Add tests
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result:**
|
||||||
|
|
||||||
|
- 📊 Estimate from content
|
||||||
|
- Files: 4 → 28K base
|
||||||
|
- Clear scope → Medium complexity (20K)
|
||||||
|
- Tests mentioned → 10K
|
||||||
|
- **Total: ~58K tokens**
|
||||||
|
- Confidence: 75%
|
||||||
|
- Queue with note
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Scenario 3: Vague Issue
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Issue #302: Fix user thing
|
||||||
|
|
||||||
|
Users are complaining
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result:**
|
||||||
|
|
||||||
|
- ⚠️ Minimal info
|
||||||
|
- Use defaults (50K, medium, sonnet)
|
||||||
|
- Confidence: 25%
|
||||||
|
- Comment: "Please add details"
|
||||||
|
- Don't queue (<30% threshold)
|
||||||
|
- Unassign from @mosaic
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Scenario 4: Oversized Issue
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
Issue #303: Refactor entire authentication system
|
||||||
|
|
||||||
|
We need to modernize our auth:
|
||||||
|
|
||||||
|
- Replace session-based auth with JWT
|
||||||
|
- Add OAuth2 support
|
||||||
|
- Implement refresh tokens
|
||||||
|
- Add MFA
|
||||||
|
- Update all protected routes
|
||||||
|
- Migration for existing users
|
||||||
|
```
|
||||||
|
|
||||||
|
**Result:**
|
||||||
|
|
||||||
|
- 📊 Estimate: 180K tokens
|
||||||
|
- ⚠️ Exceeds 50% rule (>100K)
|
||||||
|
- Auto-decompose into sub-issues:
|
||||||
|
- #304: Extract JWT service (35K)
|
||||||
|
- #305: Add OAuth2 integration (40K)
|
||||||
|
- #306: Implement refresh tokens (28K)
|
||||||
|
- #307: Add MFA support (32K)
|
||||||
|
- #308: Update route guards (22K)
|
||||||
|
- #309: User migration script (18K)
|
||||||
|
- Label #303 as EPIC
|
||||||
|
- Queue sub-issues
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The issue parser must be **robust and intelligent** to handle real-world issues:
|
||||||
|
|
||||||
|
- ✅ Extract structured metadata when available
|
||||||
|
- ✅ Estimate from content when missing
|
||||||
|
- ✅ Use confidence scores to guide behavior
|
||||||
|
- ✅ Auto-decompose oversized issues
|
||||||
|
- ✅ Warn users on low confidence
|
||||||
|
- ✅ Handle edge cases gracefully
|
||||||
|
|
||||||
|
**This makes the coordinator truly autonomous** - it can handle whatever issues users throw at it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Document Version:** 1.0
|
||||||
|
**Created:** 2026-01-31
|
||||||
|
**Status:** Proposed - Update COORD-002
|
||||||
|
**Priority:** Critical (P0) - Required for real-world usage
|
||||||
Reference in New Issue
Block a user