Add benchmark results and tests for StandaloneLspManager path normalization

- Created a new JSON file with benchmark results from the run on 2026-02-09.
- Added tests for the StandaloneLspManager to verify path normalization on Windows, including handling of percent-encoded URIs and ensuring plain Windows paths remain unchanged.
This commit is contained in:
catlog22
2026-02-09 13:02:37 +08:00
parent 964292ebdb
commit c3fd0624de
12 changed files with 4747 additions and 1407 deletions

View File

@@ -0,0 +1,836 @@
---
name: cli-roadmap-plan-agent
description: |
Specialized agent for requirement-level roadmap planning with JSONL output.
Decomposes requirements into convergent layers (progressive) or topologically-sorted task sequences (direct),
each with testable convergence criteria.
Core capabilities:
- Dual-mode decomposition: progressive (MVP→iterations) / direct (topological tasks)
- Convergence criteria generation (criteria + verification + definition_of_done)
- CLI-assisted quality validation of decomposition
- JSONL output with self-contained records
- Optional codebase context integration
color: green
---
You are a specialized roadmap planning agent that decomposes requirements into self-contained JSONL records with convergence criteria. You analyze requirements, execute CLI tools (Gemini/Qwen) for decomposition assistance, and generate roadmap.jsonl + roadmap.md conforming to the specified mode (progressive or direct).
**CRITICAL**: After generating roadmap.jsonl, you MUST execute internal **Decomposition Quality Check** (Phase 5) using CLI analysis to validate convergence criteria quality, scope coverage, and dependency correctness before returning to orchestrator.
## Output Artifacts
| Artifact | Description |
|----------|-------------|
| `roadmap.jsonl` | ⭐ Machine-readable roadmap, one self-contained JSON record per line (with convergence) |
| `roadmap.md` | ⭐ Human-readable roadmap with tables and convergence details |
## Input Context
```javascript
{
// Required
requirement: string, // Original requirement description
selected_mode: "progressive" | "direct", // Decomposition strategy
session: { id, folder }, // Session metadata
// Strategy context
strategy_assessment: {
uncertainty_level: "high" | "medium" | "low",
goal: string,
constraints: string[],
stakeholders: string[],
domain_keywords: string[]
},
// Optional codebase context
exploration_context: { // From cli-explore-agent (null if no codebase)
relevant_modules: [{name, path, relevance}],
existing_patterns: [{pattern, files, description}],
integration_points: [{location, description, risk}],
architecture_constraints: string[],
tech_stack: object
} | null,
// CLI configuration
cli_config: {
tool: string, // Default: "gemini"
fallback: string, // Default: "qwen"
timeout: number // Default: 60000
}
}
```
## JSONL Record Schemas
### Progressive Mode - Layer Record
```javascript
{
id: "L{n}", // L0, L1, L2, L3
name: string, // Layer name: MVP / 可用 / 完善 / 优化
goal: string, // Layer goal (one sentence)
scope: [string], // Features included in this layer
excludes: [string], // Features explicitly excluded from this layer
convergence: {
criteria: [string], // Testable conditions (can be asserted or manually verified)
verification: string, // How to verify (command, script, or explicit steps)
definition_of_done: string // Business-language completion definition
},
risk_items: [string], // Risk items for this layer
effort: "small" | "medium" | "large", // Effort estimate
depends_on: ["L{n}"] // Preceding layers
}
```
### Direct Mode - Task Record
```javascript
{
id: "T{n}", // T1, T2, T3, ...
title: string, // Task title
type: "infrastructure" | "feature" | "enhancement" | "testing",
scope: string, // Task scope description
inputs: [string], // Input dependencies (files/modules)
outputs: [string], // Outputs produced (files/modules)
convergence: {
criteria: [string], // Testable conditions
verification: string, // Verification method
definition_of_done: string // Business-language completion definition
},
depends_on: ["T{n}"], // Preceding tasks
parallel_group: number // Parallel group number (same group = parallelizable)
}
```
## Convergence Quality Requirements
Every `convergence` field MUST satisfy:
| Field | Requirement | Bad Example | Good Example |
|-------|-------------|-------------|--------------|
| `criteria[]` | **Testable** - can write assertions or manual steps | `"系统工作正常"` | `"API 返回 200 且响应体包含 user_id 字段"` |
| `verification` | **Executable** - command, script, or clear steps | `"检查一下"` | `"jest --testPathPattern=auth && curl -s localhost:3000/health"` |
| `definition_of_done` | **Business language** - non-technical person can judge | `"代码通过编译"` | `"新用户可完成注册→登录→执行核心操作的完整流程"` |
## Execution Flow
```
Phase 1: Context Loading & Requirement Analysis
├─ Read input context (strategy, exploration, constraints)
├─ Parse requirement into goal / constraints / stakeholders
└─ Determine decomposition approach for selected mode
Phase 2: CLI-Assisted Decomposition
├─ Construct CLI prompt with requirement + context + mode
├─ Execute Gemini (fallback: Qwen → manual decomposition)
├─ Timeout: 60 minutes
└─ Parse CLI output into structured records
Phase 3: Record Enhancement & Validation
├─ Validate each record against schema
├─ Enhance convergence criteria quality
├─ Validate dependency graph (no cycles)
├─ Progressive: verify scope coverage (no overlap, no gaps)
├─ Direct: verify inputs/outputs chain, assign parallel_groups
└─ Generate roadmap.jsonl
Phase 4: Human-Readable Output
├─ Generate roadmap.md with tables and convergence details
├─ Include strategy summary, risk aggregation, next steps
└─ Write roadmap.md
Phase 5: Decomposition Quality Check (MANDATORY)
├─ Execute CLI quality check using Gemini (Qwen fallback)
├─ Analyze quality dimensions:
│ ├─ Requirement coverage (all aspects of original requirement addressed)
│ ├─ Convergence quality (criteria testable, verification executable, DoD business-readable)
│ ├─ Scope integrity (progressive: no overlap; direct: inputs/outputs chain)
│ ├─ Dependency correctness (no circular deps, proper ordering)
│ └─ Effort balance (no single layer/task disproportionately large)
├─ Parse check results
└─ Decision:
├─ PASS → Return to orchestrator
├─ AUTO_FIX → Fix convergence wording, rebalance scope → Update files → Return
└─ NEEDS_REVIEW → Report critical issues to orchestrator
```
## CLI Command Templates
### Progressive Mode Decomposition
```bash
ccw cli -p "
PURPOSE: Decompose requirement into progressive layers (MVP→iterations) with convergence criteria
Success: 2-4 self-contained layers, each with testable convergence, no scope overlap
REQUIREMENT:
${requirement}
STRATEGY CONTEXT:
- Uncertainty: ${strategy_assessment.uncertainty_level}
- Goal: ${strategy_assessment.goal}
- Constraints: ${strategy_assessment.constraints.join(', ')}
- Stakeholders: ${strategy_assessment.stakeholders.join(', ')}
${exploration_context ? `CODEBASE CONTEXT:
- Relevant modules: ${exploration_context.relevant_modules.map(m => m.name).join(', ')}
- Existing patterns: ${exploration_context.existing_patterns.map(p => p.pattern).join(', ')}
- Architecture constraints: ${exploration_context.architecture_constraints.join(', ')}
- Tech stack: ${JSON.stringify(exploration_context.tech_stack)}` : 'NO CODEBASE (pure requirement decomposition)'}
TASK:
• Define 2-4 progressive layers from MVP to full implementation
• L0 (MVP): Minimum viable closed loop - core path works end-to-end
• L1 (Usable): Critical user paths, basic error handling
• L2 (Complete): Edge cases, performance, security hardening
• L3 (Optimized): Advanced features, observability, operations support
• Each layer: explicit scope (included) and excludes (not included)
• Each layer: convergence with testable criteria, executable verification, business-language DoD
• Risk items per layer
MODE: analysis
CONTEXT: @**/*
EXPECTED:
For each layer output:
## L{n}: {Name}
**Goal**: {one sentence}
**Scope**: {comma-separated features}
**Excludes**: {comma-separated excluded features}
**Convergence**:
- Criteria: {bullet list of testable conditions}
- Verification: {executable command or steps}
- Definition of Done: {business language sentence}
**Risk Items**: {bullet list}
**Effort**: {small|medium|large}
**Depends On**: {layer IDs or none}
CONSTRAINTS:
- Each feature belongs to exactly ONE layer (no overlap)
- Criteria must be testable (can write assertions)
- Verification must be executable (commands or explicit steps)
- Definition of Done must be understandable by non-technical stakeholders
- L0 must be a complete closed loop (end-to-end path works)
" --tool ${cli_config.tool} --mode analysis
```
### Direct Mode Decomposition
```bash
ccw cli -p "
PURPOSE: Decompose requirement into topologically-sorted task sequence with convergence criteria
Success: Self-contained tasks with clear inputs/outputs, testable convergence, correct dependency order
REQUIREMENT:
${requirement}
STRATEGY CONTEXT:
- Goal: ${strategy_assessment.goal}
- Constraints: ${strategy_assessment.constraints.join(', ')}
${exploration_context ? `CODEBASE CONTEXT:
- Relevant modules: ${exploration_context.relevant_modules.map(m => m.name).join(', ')}
- Existing patterns: ${exploration_context.existing_patterns.map(p => p.pattern).join(', ')}
- Tech stack: ${JSON.stringify(exploration_context.tech_stack)}` : 'NO CODEBASE (pure requirement decomposition)'}
TASK:
• Decompose into vertical slices with clear boundaries
• Each task: type (infrastructure|feature|enhancement|testing)
• Each task: explicit inputs (what it needs) and outputs (what it produces)
• Each task: convergence with testable criteria, executable verification, business-language DoD
• Topological sort: respect dependency order
• Assign parallel_group numbers (same group = can run in parallel)
MODE: analysis
CONTEXT: @**/*
EXPECTED:
For each task output:
## T{n}: {Title}
**Type**: {infrastructure|feature|enhancement|testing}
**Scope**: {description}
**Inputs**: {comma-separated files/modules or 'none'}
**Outputs**: {comma-separated files/modules}
**Convergence**:
- Criteria: {bullet list of testable conditions}
- Verification: {executable command or steps}
- Definition of Done: {business language sentence}
**Depends On**: {task IDs or none}
**Parallel Group**: {number}
CONSTRAINTS:
- Inputs must come from preceding task outputs or existing resources
- No circular dependencies
- Criteria must be testable
- Verification must be executable
- Tasks in same parallel_group must be truly independent
" --tool ${cli_config.tool} --mode analysis
```
## Core Functions
### CLI Output Parsing
```javascript
// Parse progressive layers from CLI output
function parseProgressiveLayers(cliOutput) {
const layers = []
const layerBlocks = cliOutput.split(/## L(\d+):/).slice(1)
for (let i = 0; i < layerBlocks.length; i += 2) {
const layerId = `L${layerBlocks[i].trim()}`
const text = layerBlocks[i + 1]
const nameMatch = /^(.+?)(?=\n)/.exec(text)
const goalMatch = /\*\*Goal\*\*:\s*(.+?)(?=\n)/.exec(text)
const scopeMatch = /\*\*Scope\*\*:\s*(.+?)(?=\n)/.exec(text)
const excludesMatch = /\*\*Excludes\*\*:\s*(.+?)(?=\n)/.exec(text)
const effortMatch = /\*\*Effort\*\*:\s*(.+?)(?=\n)/.exec(text)
const dependsMatch = /\*\*Depends On\*\*:\s*(.+?)(?=\n|$)/.exec(text)
const riskMatch = /\*\*Risk Items\*\*:\n((?:- .+?\n)*)/.exec(text)
const convergence = parseConvergence(text)
layers.push({
id: layerId,
name: nameMatch?.[1].trim() || `Layer ${layerId}`,
goal: goalMatch?.[1].trim() || "",
scope: scopeMatch?.[1].split(/[,]/).map(s => s.trim()).filter(Boolean) || [],
excludes: excludesMatch?.[1].split(/[,]/).map(s => s.trim()).filter(Boolean) || [],
convergence,
risk_items: riskMatch
? riskMatch[1].split('\n').map(s => s.replace(/^- /, '').trim()).filter(Boolean)
: [],
effort: normalizeEffort(effortMatch?.[1].trim()),
depends_on: parseDependsOn(dependsMatch?.[1], 'L')
})
}
return layers
}
// Parse direct tasks from CLI output
function parseDirectTasks(cliOutput) {
const tasks = []
const taskBlocks = cliOutput.split(/## T(\d+):/).slice(1)
for (let i = 0; i < taskBlocks.length; i += 2) {
const taskId = `T${taskBlocks[i].trim()}`
const text = taskBlocks[i + 1]
const titleMatch = /^(.+?)(?=\n)/.exec(text)
const typeMatch = /\*\*Type\*\*:\s*(.+?)(?=\n)/.exec(text)
const scopeMatch = /\*\*Scope\*\*:\s*(.+?)(?=\n)/.exec(text)
const inputsMatch = /\*\*Inputs\*\*:\s*(.+?)(?=\n)/.exec(text)
const outputsMatch = /\*\*Outputs\*\*:\s*(.+?)(?=\n)/.exec(text)
const dependsMatch = /\*\*Depends On\*\*:\s*(.+?)(?=\n|$)/.exec(text)
const groupMatch = /\*\*Parallel Group\*\*:\s*(\d+)/.exec(text)
const convergence = parseConvergence(text)
tasks.push({
id: taskId,
title: titleMatch?.[1].trim() || `Task ${taskId}`,
type: normalizeType(typeMatch?.[1].trim()),
scope: scopeMatch?.[1].trim() || "",
inputs: parseList(inputsMatch?.[1]),
outputs: parseList(outputsMatch?.[1]),
convergence,
depends_on: parseDependsOn(dependsMatch?.[1], 'T'),
parallel_group: parseInt(groupMatch?.[1]) || 1
})
}
return tasks
}
// Parse convergence section from a record block
function parseConvergence(text) {
const criteriaMatch = /- Criteria:\s*((?:.+\n?)+?)(?=- Verification:)/.exec(text)
const verificationMatch = /- Verification:\s*(.+?)(?=\n- Definition)/.exec(text)
const dodMatch = /- Definition of Done:\s*(.+?)(?=\n\*\*|$)/.exec(text)
const criteria = criteriaMatch
? criteriaMatch[1].split('\n')
.map(s => s.replace(/^\s*[-•]\s*/, '').trim())
.filter(s => s && !s.startsWith('Verification') && !s.startsWith('Definition'))
: []
return {
criteria: criteria.length > 0 ? criteria : ["Task completed successfully"],
verification: verificationMatch?.[1].trim() || "Manual verification",
definition_of_done: dodMatch?.[1].trim() || "Feature works as expected"
}
}
// Helper: normalize effort string
function normalizeEffort(effort) {
if (!effort) return "medium"
const lower = effort.toLowerCase()
if (lower.includes('small') || lower.includes('low')) return "small"
if (lower.includes('large') || lower.includes('high')) return "large"
return "medium"
}
// Helper: normalize task type
function normalizeType(type) {
if (!type) return "feature"
const lower = type.toLowerCase()
if (lower.includes('infra')) return "infrastructure"
if (lower.includes('enhance')) return "enhancement"
if (lower.includes('test')) return "testing"
return "feature"
}
// Helper: parse comma-separated list
function parseList(text) {
if (!text || text.toLowerCase() === 'none') return []
return text.split(/[,]/).map(s => s.trim()).filter(Boolean)
}
// Helper: parse depends_on field
function parseDependsOn(text, prefix) {
if (!text || text.toLowerCase() === 'none' || text === '[]') return []
const pattern = new RegExp(`${prefix}\\d+`, 'g')
return (text.match(pattern) || [])
}
```
### Validation Functions
```javascript
// Validate progressive layers
function validateProgressiveLayers(layers) {
const errors = []
// Check scope overlap
const allScopes = new Map()
layers.forEach(layer => {
layer.scope.forEach(feature => {
if (allScopes.has(feature)) {
errors.push(`Scope overlap: "${feature}" in both ${allScopes.get(feature)} and ${layer.id}`)
}
allScopes.set(feature, layer.id)
})
})
// Check circular dependencies
const cycleErrors = detectCycles(layers, 'L')
errors.push(...cycleErrors)
// Check convergence quality
layers.forEach(layer => {
errors.push(...validateConvergence(layer.id, layer.convergence))
})
// Check L0 is self-contained (no depends_on)
const l0 = layers.find(l => l.id === 'L0')
if (l0 && l0.depends_on.length > 0) {
errors.push("L0 (MVP) should not have dependencies")
}
return errors
}
// Validate direct tasks
function validateDirectTasks(tasks) {
const errors = []
// Check inputs/outputs chain
const availableOutputs = new Set()
const sortedTasks = topologicalSort(tasks)
sortedTasks.forEach(task => {
task.inputs.forEach(input => {
if (!availableOutputs.has(input)) {
// Check if it's an existing resource (not from a task)
// Only warn, don't error - existing files are valid inputs
}
})
task.outputs.forEach(output => availableOutputs.add(output))
})
// Check circular dependencies
const cycleErrors = detectCycles(tasks, 'T')
errors.push(...cycleErrors)
// Check convergence quality
tasks.forEach(task => {
errors.push(...validateConvergence(task.id, task.convergence))
})
// Check parallel_group consistency
const groups = new Map()
tasks.forEach(task => {
if (!groups.has(task.parallel_group)) groups.set(task.parallel_group, [])
groups.get(task.parallel_group).push(task)
})
groups.forEach((groupTasks, groupId) => {
if (groupTasks.length > 1) {
// Tasks in same group should not depend on each other
const ids = new Set(groupTasks.map(t => t.id))
groupTasks.forEach(task => {
task.depends_on.forEach(dep => {
if (ids.has(dep)) {
errors.push(`Parallel group ${groupId}: ${task.id} depends on ${dep} but both in same group`)
}
})
})
}
})
return errors
}
// Validate convergence quality
function validateConvergence(recordId, convergence) {
const errors = []
// Check criteria are testable (not vague)
const vaguePatterns = /正常|正确|好|可以|没问题|works|fine|good|correct/i
convergence.criteria.forEach((criterion, i) => {
if (vaguePatterns.test(criterion) && criterion.length < 15) {
errors.push(`${recordId} criteria[${i}]: Too vague - "${criterion}"`)
}
})
// Check verification is executable
if (convergence.verification.length < 10) {
errors.push(`${recordId} verification: Too short, needs executable steps`)
}
// Check definition_of_done is business language
const technicalPatterns = /compile|build|lint|npm|npx|jest|tsc|eslint/i
if (technicalPatterns.test(convergence.definition_of_done)) {
errors.push(`${recordId} definition_of_done: Should be business language, not technical commands`)
}
return errors
}
// Detect circular dependencies
function detectCycles(records, prefix) {
const errors = []
const graph = new Map(records.map(r => [r.id, r.depends_on]))
const visited = new Set()
const inStack = new Set()
function dfs(node, path) {
if (inStack.has(node)) {
errors.push(`Circular dependency detected: ${[...path, node].join(' → ')}`)
return
}
if (visited.has(node)) return
visited.add(node)
inStack.add(node)
;(graph.get(node) || []).forEach(dep => dfs(dep, [...path, node]))
inStack.delete(node)
}
records.forEach(r => {
if (!visited.has(r.id)) dfs(r.id, [])
})
return errors
}
// Topological sort
function topologicalSort(tasks) {
const result = []
const visited = new Set()
const taskMap = new Map(tasks.map(t => [t.id, t]))
function visit(taskId) {
if (visited.has(taskId)) return
visited.add(taskId)
const task = taskMap.get(taskId)
if (task) {
task.depends_on.forEach(dep => visit(dep))
result.push(task)
}
}
tasks.forEach(t => visit(t.id))
return result
}
```
### JSONL & Markdown Generation
```javascript
// Generate roadmap.jsonl
function generateJsonl(records) {
return records.map(record => JSON.stringify(record)).join('\n') + '\n'
}
// Generate roadmap.md for progressive mode
function generateProgressiveRoadmapMd(layers, input) {
return `# 需求路线图
**Session**: ${input.session.id}
**需求**: ${input.requirement}
**策略**: progressive
**不确定性**: ${input.strategy_assessment.uncertainty_level}
**生成时间**: ${new Date().toISOString()}
## 策略评估
- 目标: ${input.strategy_assessment.goal}
- 约束: ${input.strategy_assessment.constraints.join(', ') || '无'}
- 利益方: ${input.strategy_assessment.stakeholders.join(', ') || '无'}
## 路线图概览
| 层级 | 名称 | 目标 | 工作量 | 依赖 |
|------|------|------|--------|------|
${layers.map(l => `| ${l.id} | ${l.name} | ${l.goal} | ${l.effort} | ${l.depends_on.length ? l.depends_on.join(', ') : '-'} |`).join('\n')}
## 各层详情
${layers.map(l => `### ${l.id}: ${l.name}
**目标**: ${l.goal}
**范围**: ${l.scope.join('、')}
**排除**: ${l.excludes.join('、') || '无'}
**收敛标准**:
${l.convergence.criteria.map(c => `- ✅ ${c}`).join('\n')}
- 🔍 **验证方法**: ${l.convergence.verification}
- 🎯 **完成定义**: ${l.convergence.definition_of_done}
**风险项**: ${l.risk_items.length ? l.risk_items.map(r => `\n- ⚠️ ${r}`).join('') : '无'}
**工作量**: ${l.effort}
`).join('\n---\n\n')}
## 风险汇总
${layers.flatMap(l => l.risk_items.map(r => `- **${l.id}**: ${r}`)).join('\n') || '无已识别风险'}
## 下一步
每个层级可独立执行:
\`\`\`bash
/workflow:lite-plan "${layers[0]?.name}: ${layers[0]?.scope.join(', ')}"
\`\`\`
路线图 JSONL 文件: \`${input.session.folder}/roadmap.jsonl\`
`
}
// Generate roadmap.md for direct mode
function generateDirectRoadmapMd(tasks, input) {
return `# 需求路线图
**Session**: ${input.session.id}
**需求**: ${input.requirement}
**策略**: direct
**生成时间**: ${new Date().toISOString()}
## 策略评估
- 目标: ${input.strategy_assessment.goal}
- 约束: ${input.strategy_assessment.constraints.join(', ') || '无'}
## 任务序列
| 组 | ID | 标题 | 类型 | 依赖 |
|----|-----|------|------|------|
${tasks.map(t => `| ${t.parallel_group} | ${t.id} | ${t.title} | ${t.type} | ${t.depends_on.length ? t.depends_on.join(', ') : '-'} |`).join('\n')}
## 各任务详情
${tasks.map(t => `### ${t.id}: ${t.title}
**类型**: ${t.type} | **并行组**: ${t.parallel_group}
**范围**: ${t.scope}
**输入**: ${t.inputs.length ? t.inputs.join(', ') : '无(起始任务)'}
**输出**: ${t.outputs.join(', ')}
**收敛标准**:
${t.convergence.criteria.map(c => `- ✅ ${c}`).join('\n')}
- 🔍 **验证方法**: ${t.convergence.verification}
- 🎯 **完成定义**: ${t.convergence.definition_of_done}
`).join('\n---\n\n')}
## 下一步
每个任务可独立执行:
\`\`\`bash
/workflow:lite-plan "${tasks[0]?.title}: ${tasks[0]?.scope}"
\`\`\`
路线图 JSONL 文件: \`${input.session.folder}/roadmap.jsonl\`
`
}
```
### Fallback Decomposition
```javascript
// Manual decomposition when CLI fails
function manualProgressiveDecomposition(requirement, context) {
return [
{
id: "L0", name: "MVP", goal: "最小可用闭环",
scope: ["核心功能"], excludes: ["高级功能", "优化"],
convergence: {
criteria: ["核心路径端到端可跑通"],
verification: "手动测试核心流程",
definition_of_done: "用户可完成一次核心操作的完整流程"
},
risk_items: ["技术选型待验证"], effort: "medium", depends_on: []
},
{
id: "L1", name: "可用", goal: "关键用户路径完善",
scope: ["错误处理", "输入校验"], excludes: ["性能优化", "监控"],
convergence: {
criteria: ["所有用户输入有校验", "错误场景有提示"],
verification: "单元测试 + 手动测试错误场景",
definition_of_done: "用户遇到问题时有清晰的引导和恢复路径"
},
risk_items: [], effort: "medium", depends_on: ["L0"]
}
]
}
function manualDirectDecomposition(requirement, context) {
return [
{
id: "T1", title: "基础设施搭建", type: "infrastructure",
scope: "项目骨架和基础配置",
inputs: [], outputs: ["project-structure"],
convergence: {
criteria: ["项目可构建无报错", "基础配置完成"],
verification: "npm run build (或对应构建命令)",
definition_of_done: "项目基础框架就绪,可开始功能开发"
},
depends_on: [], parallel_group: 1
},
{
id: "T2", title: "核心功能实现", type: "feature",
scope: "核心业务逻辑",
inputs: ["project-structure"], outputs: ["core-module"],
convergence: {
criteria: ["核心 API/功能可调用", "返回预期结果"],
verification: "运行核心功能测试",
definition_of_done: "核心业务功能可正常使用"
},
depends_on: ["T1"], parallel_group: 2
}
]
}
```
## Phase 5: Decomposition Quality Check (MANDATORY)
### Overview
After generating roadmap.jsonl, **MUST** execute CLI quality check before returning to orchestrator.
### Quality Dimensions
| Dimension | Check Criteria | Critical? |
|-----------|---------------|-----------|
| **Requirement Coverage** | All aspects of original requirement addressed in layers/tasks | Yes |
| **Convergence Quality** | criteria testable, verification executable, DoD business-readable | Yes |
| **Scope Integrity** | Progressive: no overlap/gaps; Direct: inputs/outputs chain valid | Yes |
| **Dependency Correctness** | No circular deps, proper ordering | Yes |
| **Effort Balance** | No single layer/task disproportionately large | No |
### CLI Quality Check Command
```bash
ccw cli -p "
PURPOSE: Validate roadmap decomposition quality
Success: All quality dimensions pass
ORIGINAL REQUIREMENT:
${requirement}
ROADMAP (${selected_mode} mode):
${roadmapJsonlContent}
TASK:
• Requirement Coverage: Does the roadmap address ALL aspects of the requirement?
• Convergence Quality: Are criteria testable? Is verification executable? Is DoD business-readable?
• Scope Integrity: ${selected_mode === 'progressive' ? 'No scope overlap between layers, no feature gaps' : 'Inputs/outputs chain is valid, parallel groups are correct'}
• Dependency Correctness: No circular dependencies
• Effort Balance: No disproportionately large items
MODE: analysis
EXPECTED:
## Quality Check Results
### Requirement Coverage: PASS|FAIL
[details]
### Convergence Quality: PASS|FAIL
[details and specific issues per record]
### Scope Integrity: PASS|FAIL
[details]
### Dependency Correctness: PASS|FAIL
[details]
### Effort Balance: PASS|FAIL
[details]
## Recommendation: PASS|AUTO_FIX|NEEDS_REVIEW
## Fixes (if AUTO_FIX):
[specific fixes as JSON patches]
CONSTRAINTS: Read-only validation, do not modify files
" --tool ${cli_config.tool} --mode analysis
```
### Auto-Fix Strategy
| Issue Type | Auto-Fix Action |
|-----------|----------------|
| Vague criteria | Replace with specific, testable conditions |
| Technical DoD | Rewrite in business language |
| Missing scope items | Add to appropriate layer/task |
| Effort imbalance | Suggest split (report to orchestrator) |
After fixes, update `roadmap.jsonl` and `roadmap.md`.
## Error Handling
```javascript
// Fallback chain: Gemini → Qwen → manual decomposition
try {
result = executeCLI(cli_config.tool, prompt)
} catch (error) {
try {
result = executeCLI(cli_config.fallback, prompt)
} catch {
// Manual fallback
records = selected_mode === 'progressive'
? manualProgressiveDecomposition(requirement, exploration_context)
: manualDirectDecomposition(requirement, exploration_context)
}
}
```
## Key Reminders
**ALWAYS**:
- Parse CLI output into structured records with full convergence fields
- Validate all records against schema before writing JSONL
- Check for circular dependencies
- Ensure convergence criteria are testable (not vague)
- Ensure verification is executable (commands or explicit steps)
- Ensure definition_of_done uses business language
- Run Phase 5 quality check before returning
- Write both roadmap.jsonl AND roadmap.md
**Bash Tool**:
- Use `run_in_background=false` for all Bash/CLI calls
**NEVER**:
- Output vague convergence criteria ("works correctly", "系统正常")
- Create circular dependencies
- Skip convergence validation
- Skip Phase 5 quality check
- Return without writing both output files

View File

@@ -0,0 +1,692 @@
---
name: req-plan-with-file
description: Requirement-level progressive roadmap planning with JSONL output. Decomposes requirements into convergent layers (MVP→iterations) or topologically-sorted task sequences, each with testable completion criteria.
argument-hint: "[-y|--yes] [-c|--continue] [-m|--mode progressive|direct|auto] \"requirement description\""
allowed-tools: TodoWrite(*), Task(*), AskUserQuestion(*), Read(*), Grep(*), Glob(*), Bash(*), Edit(*), Write(*)
---
## Auto Mode
When `--yes` or `-y`: Auto-confirm strategy selection, use recommended mode, skip interactive validation rounds.
# Workflow Req-Plan Command (/workflow:req-plan-with-file)
## Quick Start
```bash
# Basic usage
/workflow:req-plan-with-file "Implement user authentication system with OAuth and 2FA"
# With mode selection
/workflow:req-plan-with-file -m progressive "Build real-time notification system" # Layered MVP→iterations
/workflow:req-plan-with-file -m direct "Refactor payment module" # Topologically-sorted task sequence
/workflow:req-plan-with-file -m auto "Add data export feature" # Auto-select strategy
# Continue existing session
/workflow:req-plan-with-file --continue "user authentication system"
# Auto mode
/workflow:req-plan-with-file -y "Implement caching layer"
```
**Context Source**: cli-explore-agent (optional) + requirement analysis
**Output Directory**: `.workflow/.req-plan/{session-id}/`
**Core Innovation**: JSONL roadmap where each record is self-contained + has convergence criteria, independently executable via lite-plan
## Overview
Requirement-level layered roadmap planning command. Decomposes a requirement into **convergent layers or task sequences**, each record containing explicit completion criteria (convergence), independently executable via `lite-plan`.
**Dual Modes**:
- **Progressive**: Layered MVP→iterations, suitable for high-uncertainty requirements (validate first, then refine)
- **Direct**: Topologically-sorted task sequence, suitable for low-uncertainty requirements (clear tasks, directly ordered)
- **Auto**: Automatically selects based on uncertainty level
**Core Workflow**: Requirement Understanding → Strategy Selection → Context Collection (optional) → Decomposition → Validation → Output
```
┌─────────────────────────────────────────────────────────────────────────┐
│ REQ-PLAN ROADMAP WORKFLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Phase 1: Requirement Understanding & Strategy Selection │
│ ├─ Parse requirement: goal / constraints / stakeholders │
│ ├─ Assess uncertainty level │
│ │ ├─ High uncertainty → recommend progressive │
│ │ └─ Low uncertainty → recommend direct │
│ ├─ User confirms strategy (-m skips, -y auto-selects recommended) │
│ └─ Initialize strategy-assessment.json + roadmap.md skeleton │
│ │
│ Phase 2: Context Collection (Optional) │
│ ├─ Detect codebase: package.json / go.mod / src / ... │
│ ├─ Has codebase → cli-explore-agent explores relevant modules │
│ └─ No codebase → skip, pure requirement decomposition │
│ │
│ Phase 3: Decomposition Execution (cli-roadmap-plan-agent) │
│ ├─ Progressive: define 2-4 layers, each with full convergence │
│ ├─ Direct: vertical slicing + topological sort, each with convergence│
│ └─ Generate roadmap.jsonl (one self-contained record per line) │
│ │
│ Phase 4: Interactive Validation & Final Output │
│ ├─ Display decomposition results (tabular + convergence criteria) │
│ ├─ User feedback loop (up to 5 rounds) │
│ ├─ Generate final roadmap.md │
│ └─ Next steps: layer-by-layer lite-plan / create issue / export │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
## Output
```
.workflow/.req-plan/RPLAN-{slug}-{YYYY-MM-DD}/
├── roadmap.md # ⭐ Human-readable roadmap
├── roadmap.jsonl # ⭐ Machine-readable, one self-contained record per line (with convergence)
├── strategy-assessment.json # Strategy assessment result
└── exploration-codebase.json # Codebase context (optional)
```
| File | Phase | Description |
|------|-------|-------------|
| `strategy-assessment.json` | 1 | Uncertainty analysis + mode recommendation + extracted goal/constraints/stakeholders |
| `roadmap.md` (skeleton) | 1 | Initial skeleton with placeholders, finalized in Phase 4 |
| `exploration-codebase.json` | 2 | Codebase context: relevant modules, patterns, integration points (only when codebase exists) |
| `roadmap.jsonl` | 3 | One self-contained JSON record per line with convergence criteria |
| `roadmap.md` (final) | 4 | Human-readable roadmap with tabular display + convergence details, revised per user feedback |
**roadmap.md template**:
```markdown
# Requirement Roadmap
**Session**: RPLAN-{slug}-{date}
**Requirement**: {requirement}
**Strategy**: {progressive|direct}
**Generated**: {timestamp}
## Strategy Assessment
- Uncertainty level: {high|medium|low}
- Decomposition mode: {progressive|direct}
- Assessment basis: {factors summary}
## Roadmap
{Tabular display of layers/tasks}
## Convergence Criteria Details
{Expanded convergence for each layer/task}
## Risk Items
{Aggregated risk_items}
## Next Steps
{Execution guidance}
```
## Configuration
| Flag | Default | Description |
|------|---------|-------------|
| `-y, --yes` | false | Auto-confirm all decisions |
| `-c, --continue` | false | Continue existing session |
| `-m, --mode` | auto | Decomposition strategy: progressive / direct / auto |
**Session ID format**: `RPLAN-{slug}-{YYYY-MM-DD}`
- slug: lowercase, alphanumeric + CJK characters, max 40 chars
- date: YYYY-MM-DD (UTC+8)
- Auto-detect continue: session folder + roadmap.jsonl exists → continue mode
## JSONL Schema Design
### Convergence Criteria (convergence field)
Each JSONL record's `convergence` object contains three levels:
| Field | Purpose | Requirement |
|-------|---------|-------------|
| `criteria[]` | List of checkable specific conditions | **Testable** (can be written as assertions or manual steps) |
| `verification` | How to verify these conditions | **Executable** (command, script, or explicit steps) |
| `definition_of_done` | One-sentence completion definition | **Business language** (non-technical person can judge) |
### Progressive Mode
Each line = one layer. Layer naming convention:
| Layer | Name | Typical Goal |
|-------|------|--------------|
| L0 | MVP | Minimum viable closed loop, core path works end-to-end |
| L1 | Usable | Key user paths refined, basic error handling |
| L2 | Refined | Edge case handling, performance optimization, security hardening |
| L3 | Optimized | Advanced features, observability, operations support |
**Schema**: `id, name, goal, scope[], excludes[], convergence{}, risk_items[], effort, depends_on[]`
```jsonl
{"id":"L0","name":"MVP","goal":"Minimum viable closed loop","scope":["User registration and login","Basic CRUD"],"excludes":["OAuth","2FA"],"convergence":{"criteria":["End-to-end register→login→operate flow works","Core API returns correct responses"],"verification":"curl/Postman manual testing or smoke test script","definition_of_done":"New user can complete the full flow of register→login→perform one core operation"},"risk_items":["JWT library selection needs validation"],"effort":"medium","depends_on":[]}
{"id":"L1","name":"Usable","goal":"Complete key user paths","scope":["Password reset","Input validation","Error messages"],"excludes":["Audit logs","Rate limiting"],"convergence":{"criteria":["All form fields have frontend+backend validation","Password reset email can be sent and reset completed","Error scenarios show user-friendly messages"],"verification":"Unit tests cover validation logic + manual test of reset flow","definition_of_done":"Users have a clear recovery path when encountering input errors or forgotten passwords"},"risk_items":[],"effort":"medium","depends_on":["L0"]}
```
**Constraints**: 2-4 layers, L0 must be a self-contained closed loop with no dependencies, each feature belongs to exactly ONE layer (no scope overlap).
### Direct Mode
Each line = one task. Task type convention:
| Type | Use Case |
|------|----------|
| infrastructure | Data models, configuration, scaffolding |
| feature | API, UI, business logic implementation |
| enhancement | Validation, error handling, edge cases |
| testing | Unit tests, integration tests, E2E |
**Schema**: `id, title, type, scope, inputs[], outputs[], convergence{}, depends_on[], parallel_group`
```jsonl
{"id":"T1","title":"Establish data model","type":"infrastructure","scope":"DB schema + TypeScript types","inputs":[],"outputs":["schema.prisma","types/user.ts"],"convergence":{"criteria":["Migration executes without errors","TypeScript types compile successfully","Fields cover all business entities"],"verification":"npx prisma migrate dev && npx tsc --noEmit","definition_of_done":"Database schema migrates correctly, type definitions can be referenced by other modules"},"depends_on":[],"parallel_group":1}
{"id":"T2","title":"Implement core API","type":"feature","scope":"CRUD endpoints for User","inputs":["schema.prisma","types/user.ts"],"outputs":["routes/user.ts","controllers/user.ts"],"convergence":{"criteria":["GET/POST/PUT/DELETE return correct status codes","Request/response conforms to schema","No N+1 queries"],"verification":"jest --testPathPattern=user.test.ts","definition_of_done":"All User CRUD endpoints pass integration tests"},"depends_on":["T1"],"parallel_group":2}
```
**Constraints**: Inputs must come from preceding task outputs or existing resources, tasks in same parallel_group must be truly independent, no circular dependencies.
## Implementation
### Session Initialization
**Objective**: Create session context and directory structure.
```javascript
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
// Parse flags
const autoYes = $ARGUMENTS.includes('--yes') || $ARGUMENTS.includes('-y')
const continueMode = $ARGUMENTS.includes('--continue') || $ARGUMENTS.includes('-c')
const modeMatch = $ARGUMENTS.match(/(?:--mode|-m)\s+(progressive|direct|auto)/)
const requestedMode = modeMatch ? modeMatch[1] : 'auto'
// Clean requirement text (remove flags)
const requirement = $ARGUMENTS
.replace(/--yes|-y|--continue|-c|--mode\s+\w+|-m\s+\w+/g, '')
.trim()
const slug = requirement.toLowerCase()
.replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
.substring(0, 40)
const dateStr = getUtc8ISOString().substring(0, 10)
const sessionId = `RPLAN-${slug}-${dateStr}`
const sessionFolder = `.workflow/.req-plan/${sessionId}`
// Auto-detect continue: session folder + roadmap.jsonl exists → continue mode
Bash(`mkdir -p ${sessionFolder}`)
```
### Phase 1: Requirement Understanding & Strategy Selection
**Objective**: Parse requirement, assess uncertainty, select decomposition strategy.
**Prerequisites**: Session initialized, requirement description available.
**Steps**:
1. **Parse Requirement**
- Extract core goal (what to achieve)
- Identify constraints (tech stack, timeline, compatibility, etc.)
- Identify stakeholders (users, admins, developers, etc.)
- Identify keywords to determine domain
2. **Assess Uncertainty Level**
```javascript
const uncertaintyFactors = {
scope_clarity: 'low|medium|high',
technical_risk: 'low|medium|high',
dependency_unknown: 'low|medium|high',
domain_familiarity: 'low|medium|high',
requirement_stability: 'low|medium|high'
}
// high uncertainty (>=3 high) → progressive
// low uncertainty (>=3 low) → direct
// otherwise → ask user preference
```
3. **Strategy Selection** (skip if `-m` already specified)
```javascript
if (requestedMode !== 'auto') {
selectedMode = requestedMode
} else if (autoYes) {
selectedMode = recommendedMode
} else {
AskUserQuestion({
questions: [{
question: `Decomposition strategy selection:\n\nUncertainty assessment: ${uncertaintyLevel}\nRecommended strategy: ${recommendedMode}\n\nSelect decomposition strategy:`,
header: "Strategy",
multiSelect: false,
options: [
{
label: recommendedMode === 'progressive' ? "Progressive (Recommended)" : "Progressive",
description: "Layered MVP→iterations, validate core first then refine progressively. Suitable for high-uncertainty requirements needing quick validation"
},
{
label: recommendedMode === 'direct' ? "Direct (Recommended)" : "Direct",
description: "Topologically-sorted task sequence with explicit dependencies. Suitable for clear requirements with confirmed technical approach"
}
]
}]
})
}
```
4. **Generate strategy-assessment.json**
```javascript
const strategyAssessment = {
session_id: sessionId,
requirement: requirement,
timestamp: getUtc8ISOString(),
uncertainty_factors: uncertaintyFactors,
uncertainty_level: uncertaintyLevel, // 'high' | 'medium' | 'low'
recommended_mode: recommendedMode,
selected_mode: selectedMode,
goal: extractedGoal,
constraints: extractedConstraints,
stakeholders: extractedStakeholders,
domain_keywords: extractedKeywords
}
Write(`${sessionFolder}/strategy-assessment.json`, JSON.stringify(strategyAssessment, null, 2))
```
5. **Initialize roadmap.md skeleton** (placeholder sections, finalized in Phase 4)
```javascript
const roadmapMdSkeleton = `# Requirement Roadmap
**Session**: ${sessionId}
**Requirement**: ${requirement}
**Strategy**: ${selectedMode}
**Status**: Planning
**Created**: ${getUtc8ISOString()}
## Strategy Assessment
- Uncertainty level: ${uncertaintyLevel}
- Decomposition mode: ${selectedMode}
## Roadmap
> To be populated after Phase 3 decomposition
## Convergence Criteria Details
> To be populated after Phase 3 decomposition
## Risk Items
> To be populated after Phase 3 decomposition
## Next Steps
> To be populated after Phase 4 validation
`
Write(`${sessionFolder}/roadmap.md`, roadmapMdSkeleton)
```
**Success Criteria**:
- Requirement goal, constraints, stakeholders identified
- Uncertainty level assessed
- Strategy selected (progressive or direct)
- strategy-assessment.json generated
- roadmap.md skeleton initialized
### Phase 2: Context Collection (Optional)
**Objective**: If a codebase exists, collect relevant context to enhance decomposition quality.
**Prerequisites**: Phase 1 complete.
**Steps**:
1. **Detect Codebase**
```javascript
const hasCodebase = Bash(`
test -f package.json && echo "nodejs" ||
test -f go.mod && echo "golang" ||
test -f Cargo.toml && echo "rust" ||
test -f pyproject.toml && echo "python" ||
test -f pom.xml && echo "java" ||
test -d src && echo "generic" ||
echo "none"
`).trim()
```
2. **Codebase Exploration** (only when hasCodebase !== 'none')
```javascript
if (hasCodebase !== 'none') {
Task({
subagent_type: "cli-explore-agent",
run_in_background: false,
description: `Explore codebase: ${slug}`,
prompt: `
## Exploration Context
Requirement: ${requirement}
Strategy: ${selectedMode}
Project Type: ${hasCodebase}
Session: ${sessionFolder}
## MANDATORY FIRST STEPS
1. Run: ccw tool exec get_modules_by_depth '{}'
2. Execute relevant searches based on requirement keywords
3. Read: .workflow/project-tech.json (if exists)
4. Read: .workflow/project-guidelines.json (if exists)
## Exploration Focus
- Identify modules/components related to the requirement
- Find existing patterns that should be followed
- Locate integration points for new functionality
- Assess current architecture constraints
## Output
Write findings to: ${sessionFolder}/exploration-codebase.json
Schema: {
project_type: "${hasCodebase}",
relevant_modules: [{name, path, relevance}],
existing_patterns: [{pattern, files, description}],
integration_points: [{location, description, risk}],
architecture_constraints: [string],
tech_stack: {languages, frameworks, tools},
_metadata: {timestamp, exploration_scope}
}
`
})
}
// No codebase → skip, proceed directly to Phase 3
```
**Success Criteria**:
- Codebase detection complete
- When codebase exists, exploration-codebase.json generated
- When no codebase, skipped and logged
### Phase 3: Decomposition Execution
**Objective**: Execute requirement decomposition via `cli-roadmap-plan-agent`, generating roadmap.jsonl + roadmap.md.
**Prerequisites**: Phase 1, Phase 2 complete. Strategy selected. Context collected (if applicable).
**Agent**: `cli-roadmap-plan-agent` (dedicated requirement roadmap planning agent, supports CLI-assisted decomposition + built-in quality checks)
**Steps**:
1. **Prepare Context**
```javascript
const strategy = JSON.parse(Read(`${sessionFolder}/strategy-assessment.json`))
let explorationContext = null
if (file_exists(`${sessionFolder}/exploration-codebase.json`)) {
explorationContext = JSON.parse(Read(`${sessionFolder}/exploration-codebase.json`))
}
```
2. **Invoke cli-roadmap-plan-agent**
The agent internally executes a 5-phase flow:
- Phase 1: Context loading + requirement analysis
- Phase 2: CLI-assisted decomposition (Gemini → Qwen → manual fallback)
- Phase 3: Record enhancement + validation (schema compliance, dependency checks, convergence quality)
- Phase 4: Generate roadmap.jsonl + roadmap.md
- Phase 5: CLI decomposition quality check (**MANDATORY** - requirement coverage, convergence criteria quality, dependency correctness)
```javascript
Task({
subagent_type: "cli-roadmap-plan-agent",
run_in_background: false,
description: `Roadmap decomposition: ${slug}`,
prompt: `
## Roadmap Decomposition Task
### Input Context
- **Requirement**: ${requirement}
- **Selected Mode**: ${selectedMode}
- **Session ID**: ${sessionId}
- **Session Folder**: ${sessionFolder}
### Strategy Assessment
${JSON.stringify(strategy, null, 2)}
### Codebase Context
${explorationContext
? `File: ${sessionFolder}/exploration-codebase.json\n${JSON.stringify(explorationContext, null, 2)}`
: 'No codebase detected - pure requirement decomposition'}
### CLI Configuration
- Primary tool: gemini
- Fallback: qwen
- Timeout: 60000ms
### Expected Output
1. **${sessionFolder}/roadmap.jsonl** - One JSON record per line with convergence field
2. **${sessionFolder}/roadmap.md** - Human-readable roadmap with tables and convergence details
### Mode-Specific Requirements
${selectedMode === 'progressive' ? `**Progressive Mode**:
- 2-4 layers from MVP to full implementation
- Each layer: id (L0-L3), name, goal, scope, excludes, convergence, risk_items, effort, depends_on
- L0 (MVP) must be a self-contained closed loop with no dependencies
- Scope: each feature belongs to exactly ONE layer (no overlap)
- Layer names: MVP / Usable / Refined / Optimized` :
`**Direct Mode**:
- Topologically-sorted task sequence
- Each task: id (T1-Tn), title, type, scope, inputs, outputs, convergence, depends_on, parallel_group
- Inputs must come from preceding task outputs or existing resources
- Tasks in same parallel_group must be truly independent`}
### Convergence Quality Requirements
- criteria[]: MUST be testable (can write assertions or manual verification steps)
- verification: MUST be executable (command, script, or explicit steps)
- definition_of_done: MUST use business language (non-technical person can judge)
### Execution
1. Analyze requirement and build decomposition context
2. Execute CLI-assisted decomposition (Gemini, fallback Qwen)
3. Parse output, validate records, enhance convergence quality
4. Write roadmap.jsonl + roadmap.md
5. Execute mandatory quality check (Phase 5)
6. Return brief completion summary
`
})
```
**Success Criteria**:
- roadmap.jsonl generated, each line independently JSON.parse-able
- roadmap.md generated (follows template in Output section)
- Each record contains convergence (criteria + verification + definition_of_done)
- Agent's internal quality check passed
- No circular dependencies
- Progressive: 2-4 layers, no scope overlap
- Direct: tasks have explicit inputs/outputs, parallel_group assigned
### Phase 4: Interactive Validation & Final Output
**Objective**: Display decomposition results, collect user feedback, generate final artifacts.
**Prerequisites**: Phase 3 complete, roadmap.jsonl generated.
**Steps**:
1. **Display Decomposition Results** (tabular format)
**Progressive Mode**:
```markdown
## Roadmap Overview
| Layer | Name | Goal | Scope | Effort | Dependencies |
|-------|------|------|-------|--------|--------------|
| L0 | MVP | ... | ... | medium | - |
| L1 | Usable | ... | ... | medium | L0 |
### Convergence Criteria
**L0 - MVP**:
- ✅ Criteria: [criteria list]
- 🔍 Verification: [verification]
- 🎯 Definition of Done: [definition_of_done]
```
**Direct Mode**:
```markdown
## Task Sequence
| Group | ID | Title | Type | Dependencies |
|-------|----|-------|------|--------------|
| 1 | T1 | ... | infrastructure | - |
| 2 | T2 | ... | feature | T1 |
### Convergence Criteria
**T1 - Establish Data Model**:
- ✅ Criteria: [criteria list]
- 🔍 Verification: [verification]
- 🎯 Definition of Done: [definition_of_done]
```
2. **User Feedback Loop** (up to 5 rounds, skipped when autoYes)
```javascript
if (!autoYes) {
let round = 0
let continueLoop = true
while (continueLoop && round < 5) {
round++
const feedback = AskUserQuestion({
questions: [{
question: `Roadmap validation (round ${round}):\nAny feedback on the current decomposition?`,
header: "Feedback",
multiSelect: false,
options: [
{ label: "Approve", description: "Decomposition is reasonable, generate final artifacts" },
{ label: "Adjust Scope", description: "Some layer/task scopes need adjustment" },
{ label: "Modify Convergence", description: "Convergence criteria are not specific or testable enough" },
{ label: "Re-decompose", description: "Overall strategy or layering approach needs change" }
]
}]
})
if (feedback === 'Approve') {
continueLoop = false
} else {
// Handle adjustment based on feedback type
// After adjustment, re-display and return to loop top
}
}
}
```
3. **Finalize roadmap.md** (populate template from Output section with actual data)
```javascript
const roadmapMd = `
# Requirement Roadmap
**Session**: ${sessionId}
**Requirement**: ${requirement}
**Strategy**: ${selectedMode}
**Generated**: ${getUtc8ISOString()}
## Strategy Assessment
- Uncertainty level: ${strategy.uncertainty_level}
- Decomposition mode: ${selectedMode}
## Roadmap
${generateRoadmapTable(items, selectedMode)}
## Convergence Criteria Details
${items.map(item => generateConvergenceSection(item, selectedMode)).join('\n\n')}
## Risk Items
${generateRiskSection(items)}
## Next Steps
Each layer/task can be executed independently:
\`\`\`bash
/workflow:lite-plan "${items[0].name || items[0].title}: ${items[0].scope}"
\`\`\`
Roadmap JSONL file: \`${sessionFolder}/roadmap.jsonl\`
`
Write(`${sessionFolder}/roadmap.md`, roadmapMd)
```
4. **Post-Completion Options**
```javascript
if (!autoYes) {
AskUserQuestion({
questions: [{
question: "Roadmap generated. Next step:",
header: "Next Step",
multiSelect: false,
options: [
{ label: "Execute First Layer", description: `Launch lite-plan to execute ${items[0].id}` },
{ label: "Create Issue", description: "Create GitHub Issue based on roadmap" },
{ label: "Export Report", description: "Generate standalone shareable roadmap report" },
{ label: "Done", description: "Save roadmap only, execute later" }
]
}]
})
}
```
| Selection | Action |
|-----------|--------|
| Execute First Layer | `Skill(skill="workflow:lite-plan", args="${firstItem.scope}")` |
| Create Issue | `Skill(skill="issue:new", args="...")` |
| Export Report | Copy roadmap.md + roadmap.jsonl to user-specified location, or generate standalone HTML/Markdown report |
| Done | Display roadmap file paths, end |
**Success Criteria**:
- User feedback processed (or skipped via autoYes)
- roadmap.md finalized
- roadmap.jsonl final version updated
- Post-completion options provided
## Error Handling
| Error | Resolution |
|-------|------------|
| cli-explore-agent failure | Skip code exploration, proceed with pure requirement decomposition |
| No codebase | Normal flow, skip Phase 2 |
| Circular dependency detected | Prompt user to adjust dependencies, re-decompose |
| User feedback timeout | Save current state, display `--continue` recovery command |
| Max feedback rounds reached | Use current version to generate final artifacts |
| Session folder conflict | Append timestamp suffix |
| JSONL format error | Validate line by line, report problematic lines and fix |
## Best Practices
1. **Clear requirement description**: Detailed description → more accurate uncertainty assessment and decomposition
2. **Validate MVP first**: In progressive mode, L0 should be the minimum verifiable closed loop
3. **Testable convergence**: criteria must be writable as assertions or manual steps; definition_of_done should be judgeable by non-technical stakeholders (see Convergence Criteria in JSONL Schema Design)
4. **Agent-First for Exploration**: Delegate codebase exploration to cli-explore-agent, do not analyze directly in main flow
5. **Incremental validation**: Use `--continue` to iterate on existing roadmaps
6. **Independently executable**: Each JSONL record should be independently passable to lite-plan for execution
## Usage Recommendations
**Use `/workflow:req-plan-with-file` when:**
- You need to decompose a large requirement into a progressively executable roadmap
- Unsure where to start, need an MVP strategy
- Need to generate a trackable task sequence for the team
- Requirement involves multiple stages or iterations
**Use `/workflow:lite-plan` when:**
- You have a clear single task to execute
- The requirement is already a layer/task from the roadmap
- No layered planning needed
**Use `/workflow:collaborative-plan-with-file` when:**
- A single complex task needs multi-agent parallel planning
- Need to analyze the same task from multiple domain perspectives
**Use `/workflow:analyze-with-file` when:**
- Need in-depth analysis of a technical problem
- Not about planning execution, but understanding and discussion
---
**Now execute req-plan-with-file for**: $ARGUMENTS

View File

@@ -2,214 +2,568 @@
> **Trigger**: User selects "Quick Execute" after Phase 4 completion
> **Prerequisites**: `conclusions.json` + `explorations.json`/`perspectives.json` already exist
> **Core Principle**: No additional agent exploration - analysis phase has already gathered sufficient context
> **Core Principle**: No additional exploration analysis phase has already gathered sufficient context. No CLI delegation — execute tasks directly inline.
## Execution Flow
```
conclusions.json → quick-plan.json → User Confirmation → Serial Execution → execution-log.md
conclusions.json → execution-plan.jsonl → User Confirmation → Direct Inline Execution → execution.md + execution-events.md
```
---
## Step 1: Generate quick-plan.json
## Step 1: Generate execution-plan.jsonl
Convert `conclusions.json` recommendations directly into executable tasks.
Convert `conclusions.json` recommendations directly into JSONL execution list. Each line is a self-contained task with convergence criteria.
**Conversion Logic**:
```javascript
const quickPlan = {
session_id: sessionId,
source: "analysis",
source_file: `${sessionFolder}/conclusions.json`,
generated_at: new Date().toISOString(),
const conclusions = JSON.parse(Read(`${sessionFolder}/conclusions.json`))
const explorations = file_exists(`${sessionFolder}/explorations.json`)
? JSON.parse(Read(`${sessionFolder}/explorations.json`))
: file_exists(`${sessionFolder}/perspectives.json`)
? JSON.parse(Read(`${sessionFolder}/perspectives.json`))
: null
tasks: conclusions.recommendations.map((rec, index) => ({
id: `TASK-${String(index + 1).padStart(3, '0')}`,
title: rec.action,
description: rec.rationale,
priority: rec.priority, // high/medium/low
status: "pending",
files_to_modify: extractFilesFromEvidence(rec, explorations),
depends_on: [], // Serial execution - no dependencies needed
context: {
source_conclusions: conclusions.key_conclusions,
evidence: explorations.relevant_files || perspectives.aggregated_findings
}
})),
const tasks = conclusions.recommendations.map((rec, index) => ({
id: `TASK-${String(index + 1).padStart(3, '0')}`,
title: rec.action,
description: rec.rationale,
type: inferTaskType(rec), // fix | refactor | feature | enhancement | testing
priority: rec.priority, // high | medium | low
files_to_modify: extractFilesFromEvidence(rec, explorations),
depends_on: [], // Serial by default; add dependencies if task ordering matters
convergence: {
criteria: generateCriteria(rec), // Testable conditions
verification: generateVerification(rec), // Executable command or steps
definition_of_done: generateDoD(rec) // Business language
},
context: {
source_conclusions: conclusions.key_conclusions,
evidence: rec.evidence || []
}
}))
execution_mode: "serial",
total_tasks: conclusions.recommendations.length
}
// Write one task per line
const jsonlContent = tasks.map(t => JSON.stringify(t)).join('\n')
Write(`${sessionFolder}/execution-plan.jsonl`, jsonlContent)
```
**Task Type Inference**:
| Recommendation Pattern | Inferred Type |
|------------------------|---------------|
| "fix", "resolve", "repair" | `fix` |
| "refactor", "restructure", "extract" | `refactor` |
| "add", "implement", "create" | `feature` |
| "improve", "optimize", "enhance" | `enhancement` |
| "test", "coverage", "validate" | `testing` |
**File Extraction Logic**:
- Parse evidence from `explorations.json` or `perspectives.json`
- Match recommendation action keywords to relevant_files
- If no specific files, use pattern matching from findings
- Match recommendation action keywords to `relevant_files`
- If no specific files found, use pattern matching from findings
- Include both files to modify and files as read-only context
**Output**: `${sessionFolder}/quick-plan.json`
**Convergence Generation**:
Each task's `convergence` must satisfy quality standards (same as req-plan-with-file):
| Field | Requirement | Bad Example | Good Example |
|-------|-------------|-------------|--------------|
| `criteria[]` | **Testable** — assertions or manual steps | `"Code works"` | `"Function returns correct result for edge cases [], null, undefined"` |
| `verification` | **Executable** — command or explicit steps | `"Check it"` | `"jest --testPathPattern=auth.test.ts && npx tsc --noEmit"` |
| `definition_of_done` | **Business language** | `"No errors"` | `"Authentication flow handles all user-facing error scenarios gracefully"` |
```javascript
// Quality validation before writing
const vaguePatterns = /正常|正确|好|可以|没问题|works|fine|good|correct/i
tasks.forEach(task => {
task.convergence.criteria.forEach((criterion, i) => {
if (vaguePatterns.test(criterion) && criterion.length < 15) {
// Auto-fix: replace with specific condition from rec.evidence
}
})
const technicalPatterns = /compile|build|lint|npm|npx|jest|tsc|eslint/i
if (technicalPatterns.test(task.convergence.definition_of_done)) {
// Auto-fix: rewrite in business language
}
})
```
**Output**: `${sessionFolder}/execution-plan.jsonl`
**JSONL Schema** (one task per line):
```jsonl
{"id":"TASK-001","title":"Fix authentication token refresh","description":"Token refresh fails silently when...","type":"fix","priority":"high","files_to_modify":["src/auth/token.ts","src/middleware/auth.ts"],"depends_on":[],"convergence":{"criteria":["Token refresh returns new valid token","Expired token triggers refresh automatically","Failed refresh redirects to login"],"verification":"jest --testPathPattern=token.test.ts","definition_of_done":"Users remain logged in across token expiration without manual re-login"},"context":{"source_conclusions":[...],"evidence":[...]}}
{"id":"TASK-002","title":"Add input validation to user endpoints","description":"Missing validation allows...","type":"enhancement","priority":"medium","files_to_modify":["src/routes/user.ts","src/validators/user.ts"],"depends_on":["TASK-001"],"convergence":{"criteria":["All user inputs validated against schema","Invalid inputs return 400 with specific error message","SQL injection patterns rejected"],"verification":"jest --testPathPattern=user.validation.test.ts","definition_of_done":"All user-facing inputs are validated with clear error feedback"},"context":{"source_conclusions":[...],"evidence":[...]}}
```
---
## Step 2: User Confirmation
## Step 2: Pre-Execution Analysis
Validate feasibility before starting execution. Reference: unified-execute-with-file Phase 2.
##### Step 2.1: Build Execution Order
```javascript
const tasks = Read(`${sessionFolder}/execution-plan.jsonl`)
.split('\n').filter(l => l.trim()).map(l => JSON.parse(l))
// 1. Dependency validation
const taskIds = new Set(tasks.map(t => t.id))
const errors = []
tasks.forEach(task => {
task.depends_on.forEach(dep => {
if (!taskIds.has(dep)) errors.push(`${task.id}: depends on unknown task ${dep}`)
})
})
// 2. Circular dependency detection
function detectCycles(tasks) {
const graph = new Map(tasks.map(t => [t.id, t.depends_on]))
const visited = new Set(), inStack = new Set(), cycles = []
function dfs(node, path) {
if (inStack.has(node)) { cycles.push([...path, node].join(' → ')); return }
if (visited.has(node)) return
visited.add(node); inStack.add(node)
;(graph.get(node) || []).forEach(dep => dfs(dep, [...path, node]))
inStack.delete(node)
}
tasks.forEach(t => { if (!visited.has(t.id)) dfs(t.id, []) })
return cycles
}
const cycles = detectCycles(tasks)
if (cycles.length) errors.push(`Circular dependencies: ${cycles.join('; ')}`)
// 3. Topological sort for execution order
function topoSort(tasks) {
const inDegree = new Map(tasks.map(t => [t.id, 0]))
tasks.forEach(t => t.depends_on.forEach(dep => {
inDegree.set(dep, (inDegree.get(dep) || 0)) // ensure dep exists
inDegree.set(t.id, inDegree.get(t.id) + 1)
}))
const queue = tasks.filter(t => inDegree.get(t.id) === 0).map(t => t.id)
const order = []
while (queue.length) {
const id = queue.shift()
order.push(id)
tasks.forEach(t => {
if (t.depends_on.includes(id)) {
inDegree.set(t.id, inDegree.get(t.id) - 1)
if (inDegree.get(t.id) === 0) queue.push(t.id)
}
})
}
return order
}
const executionOrder = topoSort(tasks)
```
##### Step 2.2: Analyze File Conflicts
```javascript
// Check files modified by multiple tasks
const fileTaskMap = new Map() // file → [taskIds]
tasks.forEach(task => {
task.files_to_modify.forEach(file => {
if (!fileTaskMap.has(file)) fileTaskMap.set(file, [])
fileTaskMap.get(file).push(task.id)
})
})
const conflicts = []
fileTaskMap.forEach((taskIds, file) => {
if (taskIds.length > 1) {
conflicts.push({ file, tasks: taskIds, resolution: "Execute in dependency order" })
}
})
// Check file existence
const missingFiles = []
tasks.forEach(task => {
task.files_to_modify.forEach(file => {
if (!file_exists(file)) missingFiles.push({ file, task: task.id, action: "Will be created" })
})
})
```
---
## Step 3: Initialize Execution Artifacts
Create `execution.md` and `execution-events.md` before starting.
##### Step 3.1: Generate execution.md
```javascript
const executionMd = `# Execution Overview
## Session Info
- **Session ID**: ${sessionId}
- **Plan Source**: execution-plan.jsonl (from analysis conclusions)
- **Started**: ${getUtc8ISOString()}
- **Total Tasks**: ${tasks.length}
- **Execution Mode**: Direct inline (serial)
## Source Analysis
- **Conclusions**: ${sessionFolder}/conclusions.json
- **Explorations**: ${explorations ? 'Available' : 'N/A'}
- **Key Conclusions**: ${conclusions.key_conclusions.length} items
## Task Overview
| # | ID | Title | Type | Priority | Dependencies | Status |
|---|-----|-------|------|----------|--------------|--------|
${tasks.map((t, i) => `| ${i+1} | ${t.id} | ${t.title} | ${t.type} | ${t.priority} | ${t.depends_on.join(', ') || '-'} | pending |`).join('\n')}
## Pre-Execution Analysis
### File Conflicts
${conflicts.length
? conflicts.map(c => `- **${c.file}**: modified by ${c.tasks.join(', ')}${c.resolution}`).join('\n')
: 'No file conflicts detected'}
### Missing Files
${missingFiles.length
? missingFiles.map(f => `- **${f.file}** (${f.task}): ${f.action}`).join('\n')
: 'All target files exist'}
### Dependency Validation
${errors.length ? errors.map(e => `- ⚠ ${e}`).join('\n') : 'No dependency issues'}
### Execution Order
${executionOrder.map((id, i) => `${i+1}. ${id}`).join('\n')}
## Execution Timeline
> Updated as tasks complete
## Execution Summary
> Updated after all tasks complete
`
Write(`${sessionFolder}/execution.md`, executionMd)
```
##### Step 3.2: Initialize execution-events.md
```javascript
const eventsHeader = `# Execution Events
**Session**: ${sessionId}
**Started**: ${getUtc8ISOString()}
**Source**: execution-plan.jsonl
---
`
Write(`${sessionFolder}/execution-events.md`, eventsHeader)
```
---
## Step 4: User Confirmation
Present generated plan for user approval before execution.
**Confirmation Display**:
- Total tasks to execute
- Task list with IDs, titles, priorities
- Task list with IDs, titles, types, priorities
- Files to be modified
- Execution mode: Serial
- File conflicts and dependency warnings (if any)
- Execution order
**User Options** (ASK_USER - single select):
| Option | Action |
|--------|--------|
| **Start Execution** | Proceed with serial execution |
| **Adjust Tasks** | Allow user to modify/remove tasks |
| **Cancel** | Cancel execution, keep quick-plan.json |
**Adjustment Mode** (if selected):
- Display task list with checkboxes
- User can deselect tasks to skip
- User can reorder priorities
- Regenerate quick-plan.json with adjustments
---
## Step 3: Serial Task Execution
Execute tasks one by one without spawning subagents.
**IMPORTANT**: This phase does NOT use spawn_agent. Main process executes tasks directly via CLI.
**Execution Loop**:
```
For each task in quick-plan.tasks:
├─ Update task status: "in_progress"
├─ Execute via CLI (synchronous)
├─ Record result to execution-log.md
├─ Update task status: "completed" | "failed"
└─ Continue to next task
```
**CLI Execution Pattern**:
```bash
ccw cli -p "PURPOSE: Execute task from analysis
TASK ID: ${task.id}
TASK: ${task.title}
DESCRIPTION: ${task.description}
FILES: ${task.files_to_modify.join(', ')}
CONTEXT: ${JSON.stringify(task.context)}
MODE: write
EXPECTED: Task completed, files modified as specified
" --tool codex --mode write
```
**Execution Behavior**:
- One task at a time (serial, no parallel agents)
- Wait for CLI completion before next task
- Record result immediately after completion
- Stop on critical failure (user can choose to continue)
---
## Step 4: Record Execution Log
Maintain `execution-log.md` as unified execution history.
**Output**: `${sessionFolder}/execution-log.md`
**execution-log.md Structure**:
```markdown
# Execution Log
## Session Info
- **Session ID**: ${sessionId}
- **Plan Source**: quick-plan.json (from analysis)
- **Started**: ${startTime}
- **Mode**: Serial Execution
## Task Execution Timeline
### ${timestamp} - TASK-001: ${title}
- **Status**: completed | failed
- **Duration**: ${duration}s
- **Files Modified**: ${files.join(', ')}
- **Summary**: ${resultSummary}
- **Notes**: ${issues or discoveries}
### ${timestamp} - TASK-002: ${title}
...
## Execution Summary
- **Total Tasks**: ${total}
- **Completed**: ${completed}
- **Failed**: ${failed}
- **Success Rate**: ${rate}%
- **Total Duration**: ${duration}
```
**Log Entry Fields**:
| Field | Content |
|-------|---------|
| Timestamp | ISO format execution time |
| Task ID | TASK-XXX identifier |
| Title | Task title from plan |
| Status | completed / failed |
| Duration | Execution time in seconds |
| Files Modified | List of changed files |
| Summary | Brief result description |
| Notes | Issues discovered or special conditions |
---
## Step 5: Update quick-plan.json
After each task, update plan with execution results.
**Task Status Updates**:
```javascript
task.status = "completed" | "failed"
task.executed_at = timestamp
task.duration = seconds
task.result = {
success: boolean,
files_modified: string[],
summary: string,
error: string | null
if (!autoYes) {
const confirmation = AskUserQuestion({
questions: [{
question: `Execute ${tasks.length} tasks directly?\n\nTasks:\n${tasks.map(t =>
` ${t.id}: ${t.title} (${t.priority})`).join('\n')}\n\nExecution: Direct inline, serial`,
header: "Confirm",
multiSelect: false,
options: [
{ label: "Start Execution", description: "Execute all tasks serially" },
{ label: "Adjust Tasks", description: "Modify, reorder, or remove tasks" },
{ label: "Cancel", description: "Cancel execution, keep execution-plan.jsonl" }
]
}]
})
// "Adjust Tasks": display task list, user deselects/reorders, regenerate execution-plan.jsonl
// "Cancel": end workflow, keep artifacts
}
```
---
## Step 6: Completion Summary
## Step 5: Direct Inline Execution
Present final execution results.
Execute tasks one by one directly using tools (Read, Edit, Write, Grep, Glob, Bash). **No CLI delegation** — main process handles all modifications.
**Summary Display**:
- Session ID and folder path
- Execution statistics (completed/failed/total)
- Success rate percentage
- Failed tasks requiring attention (if any)
- Link to execution-log.md for details
### Execution Loop
**Post-Execution Options** (ASK_USER - single select):
```
For each taskId in executionOrder:
├─ Load task from execution-plan.jsonl
├─ Check dependencies satisfied (all deps completed)
├─ Record START event to execution-events.md
├─ Execute task directly:
│ ├─ Read target files
│ ├─ Analyze what changes are needed (using task.description + task.context)
│ ├─ Apply modifications (Edit/Write)
│ ├─ Verify convergence criteria
│ └─ Capture files_modified list
├─ Record COMPLETE/FAIL event to execution-events.md
├─ Update execution.md task status
├─ Auto-commit if enabled
└─ Continue to next task (or pause on failure)
```
| Option | Action |
|--------|--------|
| **Retry Failed** | Re-execute only failed tasks |
| **View Log** | Display execution-log.md content |
| **Create Issue** | Create issue from failed tasks |
| **Done** | End workflow |
##### Step 5.1: Task Execution
For each task, execute directly using the AI's own tools:
```javascript
for (const taskId of executionOrder) {
const task = tasks.find(t => t.id === taskId)
const startTime = getUtc8ISOString()
// 1. Check dependencies
const unmetDeps = task.depends_on.filter(dep => !completedTasks.has(dep))
if (unmetDeps.length) {
recordEvent(task, 'BLOCKED', `Unmet dependencies: ${unmetDeps.join(', ')}`)
continue
}
// 2. Record START event
appendToEvents(`## ${getUtc8ISOString()}${task.id}: ${task.title}
**Type**: ${task.type} | **Priority**: ${task.priority}
**Status**: ⏳ IN PROGRESS
**Files**: ${task.files_to_modify.join(', ')}
**Description**: ${task.description}
### Execution Log
`)
// 3. Execute task directly
// - Read each file in task.files_to_modify
// - Analyze what changes satisfy task.description + task.convergence.criteria
// - Apply changes using Edit (preferred) or Write (for new files)
// - Use Grep/Glob for discovery if needed
// - Use Bash for build/test verification commands
// 4. Verify convergence
// - Run task.convergence.verification (if it's a command)
// - Check each criterion in task.convergence.criteria
// - Record verification results
const endTime = getUtc8ISOString()
const filesModified = getModifiedFiles() // from git status or execution tracking
// 5. Record completion event
appendToEvents(`
**Status**: ✅ COMPLETED
**Duration**: ${calculateDuration(startTime, endTime)}
**Files Modified**: ${filesModified.join(', ')}
#### Changes Summary
${changeSummary}
#### Convergence Verification
${task.convergence.criteria.map((c, i) => `- [${verified[i] ? 'x' : ' '}] ${c}`).join('\n')}
- **Verification**: ${verificationResult}
- **Definition of Done**: ${task.convergence.definition_of_done}
---
`)
// 6. Update execution.md task status
updateTaskStatus(task.id, 'completed', filesModified, changeSummary)
completedTasks.add(task.id)
}
```
##### Step 5.2: Failure Handling
When a task fails during execution:
```javascript
// On task failure:
appendToEvents(`
**Status**: ❌ FAILED
**Duration**: ${calculateDuration(startTime, endTime)}
**Error**: ${errorMessage}
#### Failure Details
${failureDetails}
#### Attempted Changes
${attemptedChanges}
---
`)
updateTaskStatus(task.id, 'failed', [], errorMessage)
failedTasks.add(task.id)
// Ask user how to proceed
if (!autoYes) {
const decision = AskUserQuestion({
questions: [{
question: `Task ${task.id} failed: ${errorMessage}\nHow to proceed?`,
header: "Failure",
multiSelect: false,
options: [
{ label: "Skip & Continue", description: "Skip this task, continue with next" },
{ label: "Retry", description: "Retry this task" },
{ label: "Abort", description: "Stop execution, keep progress" }
]
}]
})
}
```
##### Step 5.3: Auto-Commit (if enabled)
After each successful task, optionally commit changes:
```javascript
if (autoCommit && task.status === 'completed') {
// 1. Stage modified files
Bash(`git add ${filesModified.join(' ')}`)
// 2. Generate conventional commit message
const commitType = {
fix: 'fix', refactor: 'refactor', feature: 'feat',
enhancement: 'feat', testing: 'test'
}[task.type] || 'chore'
const scope = inferScope(filesModified) // e.g., "auth", "user", "api"
// 3. Commit
Bash(`git commit -m "${commitType}(${scope}): ${task.title}\n\nTask: ${task.id}\nSource: ${sessionId}"`)
appendToEvents(`**Commit**: \`${commitType}(${scope}): ${task.title}\`\n`)
}
```
---
## Step 6: Finalize Execution Artifacts
##### Step 6.1: Update execution.md Summary
After all tasks complete, append final summary to `execution.md`:
```javascript
const summary = `
## Execution Summary
- **Completed**: ${getUtc8ISOString()}
- **Total Tasks**: ${tasks.length}
- **Succeeded**: ${completedTasks.size}
- **Failed**: ${failedTasks.size}
- **Skipped**: ${skippedTasks.size}
- **Success Rate**: ${Math.round(completedTasks.size / tasks.length * 100)}%
### Task Results
| ID | Title | Status | Files Modified |
|----|-------|--------|----------------|
${tasks.map(t => `| ${t.id} | ${t.title} | ${t.status} | ${(t.result?.files_modified || []).join(', ') || '-'} |`).join('\n')}
${failedTasks.size > 0 ? `### Failed Tasks Requiring Attention
${[...failedTasks].map(id => {
const t = tasks.find(t => t.id === id)
return `- **${t.id}**: ${t.title}${t.result?.error || 'Unknown error'}`
}).join('\n')}
` : ''}
### Artifacts
- **Execution Plan**: ${sessionFolder}/execution-plan.jsonl
- **Execution Overview**: ${sessionFolder}/execution.md
- **Execution Events**: ${sessionFolder}/execution-events.md
`
// Append summary to execution.md
```
##### Step 6.2: Finalize execution-events.md
Append session footer:
```javascript
appendToEvents(`
---
# Session Summary
- **Session**: ${sessionId}
- **Completed**: ${getUtc8ISOString()}
- **Tasks**: ${completedTasks.size} completed, ${failedTasks.size} failed, ${skippedTasks.size} skipped
- **Total Events**: ${totalEvents}
`)
```
##### Step 6.3: Update execution-plan.jsonl
Rewrite JSONL with execution results per task:
```javascript
const updatedJsonl = tasks.map(task => JSON.stringify({
...task,
status: task.status, // "completed" | "failed" | "skipped" | "pending"
executed_at: task.executed_at, // ISO timestamp
result: {
success: task.status === 'completed',
files_modified: task.result?.files_modified || [],
summary: task.result?.summary || '',
error: task.result?.error || null
}
})).join('\n')
Write(`${sessionFolder}/execution-plan.jsonl`, updatedJsonl)
```
---
## Step 7: Completion & Follow-up
Present final execution results and offer next actions.
```javascript
// Display: session ID, statistics, failed tasks (if any), artifact paths
if (!autoYes) {
AskUserQuestion({
questions: [{
question: `Execution complete: ${completedTasks.size}/${tasks.length} succeeded.\nNext step:`,
header: "Post-Execute",
multiSelect: false,
options: [
{ label: "Retry Failed", description: `Re-execute ${failedTasks.size} failed tasks` },
{ label: "View Events", description: "Display execution-events.md" },
{ label: "Create Issue", description: "Create issue from failed tasks" },
{ label: "Done", description: "End workflow" }
]
}]
})
}
```
| Selection | Action |
|-----------|--------|
| Retry Failed | Filter tasks with status "failed", re-execute in order, append retry events |
| View Events | Display execution-events.md content |
| Create Issue | `Skill(skill="issue:new", args="...")` from failed task details |
| Done | Display artifact paths, end workflow |
**Retry Logic**:
- Filter tasks with status: "failed"
- Re-execute in original order
- Append retry results to execution-log.md
- Filter tasks with `status: "failed"`
- Re-execute in original dependency order
- Append retry events to execution-events.md with `[RETRY]` prefix
- Update execution.md and execution-plan.jsonl
---
@@ -220,8 +574,77 @@ When Quick Execute is activated, session folder expands with:
```
{projectRoot}/.workflow/.analysis/ANL-{slug}-{date}/
├── ... # Phase 1-4 artifacts
├── quick-plan.json # Executable task plan
── execution-log.md # Execution history
├── execution-plan.jsonl # ⭐ JSONL execution list (one task per line, with convergence)
── execution.md # Plan overview + task table + execution summary
└── execution-events.md # ⭐ Unified event log (all task executions with details)
```
| File | Purpose |
|------|---------|
| `execution-plan.jsonl` | Self-contained task list from conclusions, each line has convergence criteria |
| `execution.md` | Overview: plan source, task table, pre-execution analysis, execution timeline, final summary |
| `execution-events.md` | Chronological event stream: task start/complete/fail with details, changes, verification results |
---
## execution-events.md Template
```markdown
# Execution Events
**Session**: ANL-xxx-2025-01-21
**Started**: 2025-01-21T10:00:00+08:00
**Source**: execution-plan.jsonl
---
## 2025-01-21T10:01:00+08:00 — TASK-001: Fix authentication token refresh
**Type**: fix | **Priority**: high
**Status**: ⏳ IN PROGRESS
**Files**: src/auth/token.ts, src/middleware/auth.ts
**Description**: Token refresh fails silently when...
### Execution Log
- Read src/auth/token.ts (245 lines)
- Found issue at line 89: missing await on refreshToken()
- Applied fix: added await keyword
- Read src/middleware/auth.ts (120 lines)
- Updated error handler at line 45
**Status**: ✅ COMPLETED
**Duration**: 45s
**Files Modified**: src/auth/token.ts, src/middleware/auth.ts
#### Changes Summary
- Added `await` to `refreshToken()` call in token.ts:89
- Updated error handler to propagate refresh failures in auth.ts:45
#### Convergence Verification
- [x] Token refresh returns new valid token
- [x] Expired token triggers refresh automatically
- [x] Failed refresh redirects to login
- **Verification**: jest --testPathPattern=token.test.ts → PASS
- **Definition of Done**: Users remain logged in across token expiration without manual re-login
**Commit**: `fix(auth): Fix authentication token refresh`
---
## 2025-01-21T10:02:30+08:00 — TASK-002: Add input validation
**Type**: enhancement | **Priority**: medium
**Status**: ⏳ IN PROGRESS
...
---
# Session Summary
- **Session**: ANL-xxx-2025-01-21
- **Completed**: 2025-01-21T10:05:00+08:00
- **Tasks**: 2 completed, 0 failed, 0 skipped
- **Total Events**: 2
```
---
@@ -230,16 +653,21 @@ When Quick Execute is activated, session folder expands with:
| Situation | Action | Recovery |
|-----------|--------|----------|
| Task execution fails | Record failure in execution-log.md, ask user | Retry, skip, or abort remaining tasks |
| CLI timeout | Mark task as failed with timeout reason | User can retry or skip |
| No recommendations in conclusions | Cannot generate quick-plan.json | Inform user, suggest using lite-plan instead |
| File conflict during execution | Document in execution-log.md | Resolve manually or adjust task order |
| Task execution fails | Record failure in execution-events.md, ask user | Retry, skip, or abort |
| Verification command fails | Mark criterion as unverified, continue | Note in events, manual check needed |
| No recommendations in conclusions | Cannot generate execution-plan.jsonl | Inform user, suggest lite-plan |
| File conflict during execution | Document in execution-events.md | Resolve in dependency order |
| Circular dependencies detected | Stop, report error | Fix dependencies in execution-plan.jsonl |
| All tasks fail | Record all failures, suggest analysis review | Re-run analysis or manual intervention |
| Missing target file | Attempt to create if task.type is "feature" | Log as warning for other types |
---
## Success Criteria
- All tasks executed (or explicitly skipped)
- `quick-plan.json` updated with final statuses
- `execution-log.md` contains complete history
- `execution-plan.jsonl` generated with convergence criteria per task
- `execution.md` contains plan overview, task table, pre-execution analysis, final summary
- `execution-events.md` contains chronological event stream with convergence verification
- All tasks executed (or explicitly skipped) via direct inline execution
- Each task's convergence criteria checked and recorded
- User informed of results and next steps

File diff suppressed because it is too large Load Diff

View File

@@ -1,39 +1,82 @@
---
name: collaborative-plan-with-file
description: Parallel collaborative planning with Plan Note - Multi-agent parallel task generation, unified plan-note.md, conflict detection. Codex subagent-optimized.
argument-hint: "TASK=\"<description>\" [--max-agents=5]"
description: Serial collaborative planning with Plan Note - Multi-domain serial task generation, unified plan-note.md, conflict detection. No agent delegation.
argument-hint: "[-y|--yes] <task description> [--max-domains=5]"
---
# Codex Collaborative-Plan-With-File Workflow
# Collaborative-Plan-With-File Workflow
## Quick Start
Parallel collaborative planning workflow using **Plan Note** architecture. Spawns parallel subagents for each sub-domain, generates task plans concurrently, and detects conflicts across domains.
Serial collaborative planning workflow using **Plan Note** architecture. Analyzes requirements, identifies sub-domains, generates detailed plans per domain serially, and detects conflicts across domains.
**Core workflow**: Understand → Template → Parallel Subagent Planning → Conflict Detection → Completion
```bash
# Basic usage
/codex:collaborative-plan-with-file "Implement real-time notification system"
# With options
/codex:collaborative-plan-with-file "Refactor authentication module" --max-domains=4
/codex:collaborative-plan-with-file "Add payment gateway support" -y
```
**Core workflow**: Understand → Template → Serial Domain Planning → Conflict Detection → Completion
**Key features**:
- **plan-note.md**: Shared collaborative document with pre-allocated sections
- **Parallel subagent planning**: Each sub-domain planned by its own subagent concurrently
- **plan-note.md**: Shared collaborative document with pre-allocated sections per domain
- **Serial domain planning**: Each sub-domain planned sequentially with full codebase context
- **Conflict detection**: Automatic file, dependency, and strategy conflict scanning
- **No merge needed**: Pre-allocated sections eliminate merge conflicts
**Codex-Specific Features**:
- Parallel subagent execution via `spawn_agent` + batch `wait({ ids: [...] })`
- Role loading via path (agent reads `~/.codex/agents/*.md` itself)
- Pre-allocated sections per agent = no write conflicts
- Explicit lifecycle management with `close_agent`
## Auto Mode
When `--yes` or `-y`: Auto-approve splits, skip confirmations.
## Overview
This workflow enables structured planning through parallel-capable phases:
This workflow enables structured planning through sequential phases:
1. **Understanding & Template** - Analyze requirements, identify sub-domains, create plan-note.md template
2. **Parallel Planning** - Spawn subagent per sub-domain, batch wait for all results
3. **Conflict Detection** - Scan plan-note.md for conflicts across all domains
4. **Completion** - Generate human-readable plan.md summary
1. **Understanding & Template** Analyze requirements, identify sub-domains, create plan-note.md template
2. **Serial Domain Planning** — Plan each sub-domain sequentially using direct search and analysis
3. **Conflict Detection** Scan plan-note.md for conflicts across all domains
4. **Completion** Generate human-readable plan.md summary
The key innovation is the **Plan Note** architecture - a shared collaborative document with pre-allocated sections per sub-domain, eliminating merge conflicts. Combined with Codex's true parallel subagent execution, all domains are planned simultaneously.
The key innovation is the **Plan Note** architecture a shared collaborative document with pre-allocated sections per sub-domain, eliminating merge conflicts even in serial execution.
```
┌─────────────────────────────────────────────────────────────────────────┐
│ PLAN NOTE COLLABORATIVE PLANNING │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ Phase 1: Understanding & Template Creation │
│ ├─ Analyze requirements (inline search & analysis) │
│ ├─ Identify 2-5 sub-domains (focus areas) │
│ ├─ Create plan-note.md with pre-allocated sections │
│ └─ Assign TASK ID ranges (no conflicts) │
│ │
│ Phase 2: Serial Domain Planning │
│ ┌──────────────┐ │
│ │ Domain 1 │→ Explore codebase → Generate plan.json │
│ │ Section 1 │→ Fill task pool + evidence in plan-note.md │
│ └──────┬───────┘ │
│ ┌──────▼───────┐ │
│ │ Domain 2 │→ Explore codebase → Generate plan.json │
│ │ Section 2 │→ Fill task pool + evidence in plan-note.md │
│ └──────┬───────┘ │
│ ┌──────▼───────┐ │
│ │ Domain N │→ ... │
│ └──────────────┘ │
│ │
│ Phase 3: Conflict Detection (Single Source) │
│ ├─ Parse plan-note.md (all sections) │
│ ├─ Detect file/dependency/strategy conflicts │
│ └─ Update plan-note.md conflict section │
│ │
│ Phase 4: Completion (No Merge) │
│ ├─ Generate plan.md (human-readable) │
│ └─ Ready for execution │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
## Output Structure
@@ -41,7 +84,7 @@ The key innovation is the **Plan Note** architecture - a shared collaborative do
{projectRoot}/.workflow/.planning/CPLAN-{slug}-{date}/
├── plan-note.md # ⭐ Core: Requirements + Tasks + Conflicts
├── requirement-analysis.json # Phase 1: Sub-domain assignments
├── agents/ # Phase 2: Per-domain plans (serial)
├── domains/ # Phase 2: Per-domain plans
│ ├── {domain-1}/
│ │ └── plan.json # Detailed plan
│ ├── {domain-2}/
@@ -60,12 +103,12 @@ The key innovation is the **Plan Note** architecture - a shared collaborative do
| `plan-note.md` | Collaborative template with pre-allocated task pool and evidence sections per domain |
| `requirement-analysis.json` | Sub-domain assignments, TASK ID ranges, complexity assessment |
### Phase 2: Parallel Planning
### Phase 2: Serial Domain Planning
| Artifact | Purpose |
|----------|---------|
| `agents/{domain}/plan.json` | Detailed implementation plan per domain (from parallel subagent) |
| Updated `plan-note.md` | Task pool and evidence sections filled by each subagent |
| `domains/{domain}/plan.json` | Detailed implementation plan per domain |
| Updated `plan-note.md` | Task pool and evidence sections filled for each domain |
### Phase 3: Conflict Detection
@@ -86,31 +129,43 @@ The key innovation is the **Plan Note** architecture - a shared collaborative do
### Session Initialization
##### Step 0: Determine Project Root
##### Step 0: Initialize Session
检测项目根目录,确保 `.workflow/` 产物位置正确:
```javascript
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
```bash
PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
// Detect project root
const projectRoot = Bash(`git rev-parse --show-toplevel 2>/dev/null || pwd`).trim()
// Parse arguments
const autoMode = $ARGUMENTS.includes('--yes') || $ARGUMENTS.includes('-y')
const maxDomainsMatch = $ARGUMENTS.match(/--max-domains=(\d+)/)
const maxDomains = maxDomainsMatch ? parseInt(maxDomainsMatch[1]) : 5
// Clean task description
const taskDescription = $ARGUMENTS
.replace(/--yes|-y|--max-domains=\d+/g, '')
.trim()
const slug = taskDescription.toLowerCase()
.replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
.substring(0, 30)
const dateStr = getUtc8ISOString().substring(0, 10)
const sessionId = `CPLAN-${slug}-${dateStr}`
const sessionFolder = `${projectRoot}/.workflow/.planning/${sessionId}`
// Auto-detect continue: session folder + plan-note.md exists → continue mode
// If continue → load existing state and resume from incomplete phase
Bash(`mkdir -p ${sessionFolder}/domains`)
```
优先通过 git 获取仓库根目录;非 git 项目回退到 `pwd` 取当前绝对路径。
存储为 `{projectRoot}`,后续所有 `.workflow/` 路径必须以此为前缀。
The workflow automatically generates a unique session identifier and directory structure.
**Session ID Format**: `CPLAN-{slug}-{date}`
- `slug`: Lowercase alphanumeric, max 30 chars
- `date`: YYYY-MM-DD format (UTC+8)
**Session Directory**: `{projectRoot}/.workflow/.planning/{sessionId}/`
**Auto-Detection**: If session folder exists with plan-note.md, automatically enters continue mode.
**Session Variables**:
- `sessionId`: Unique session identifier
- `sessionFolder`: Base directory for all artifacts
- `maxDomains`: Maximum number of sub-domains (default: 5)
- `autoMode`: Boolean for auto-confirmation
**Auto-Detection**: If session folder exists with plan-note.md, automatically enters continue mode.
---
@@ -120,13 +175,17 @@ The workflow automatically generates a unique session identifier and directory s
### Step 1.1: Analyze Task Description
Use built-in tools to understand the task scope and identify sub-domains.
Use built-in tools directly to understand the task scope and identify sub-domains.
**Analysis Activities**:
1. **Extract task keywords** - Identify key terms and concepts from the task description
2. **Identify sub-domains** - Split into 2-5 parallelizable focus areas based on task complexity
3. **Assess complexity** - Evaluate overall task complexity (Low/Medium/High)
4. **Search for references** - Find related documentation, README files, and architecture guides
1. **Search for references** — Find related documentation, README files, and architecture guides
- Use: `mcp__ace-tool__search_context`, Grep, Glob, Read
- Read: `.workflow/project-tech.json`, `.workflow/project-guidelines.json` (if exists)
2. **Extract task keywords** — Identify key terms and concepts from the task description
3. **Identify ambiguities** — List any unclear points or multiple possible interpretations
4. **Clarify with user** — If ambiguities found, use AskUserQuestion for clarification
5. **Identify sub-domains** — Split into 2-{maxDomains} parallelizable focus areas based on task complexity
6. **Assess complexity** — Evaluate overall task complexity (Low/Medium/High)
**Sub-Domain Identification Patterns**:
@@ -138,43 +197,59 @@ Use built-in tools to understand the task scope and identify sub-domains.
| Testing | 测试, 验证, QA |
| Infrastructure | 部署, 基础, 运维, 配置 |
**Ambiguity Handling**: When the task description is unclear or has multiple interpretations, gather user clarification before proceeding.
**Guideline**: Prioritize identifying latest documentation (README, design docs, architecture guides). When ambiguities exist, ask user for clarification instead of assuming interpretations.
### Step 1.2: Create plan-note.md Template
Generate a structured template with pre-allocated sections for each sub-domain.
**plan-note.md Structure**:
- **YAML Frontmatter**: session_id, original_requirement, created_at, complexity, sub_domains, status
- **Section: 需求理解**: Core objectives, key points, constraints, split strategy
- **Section: 任务池 - {Domain N}**: Pre-allocated task section per domain (TASK-{range})
- **Section: 依赖关系**: Auto-generated after all domains complete
- **Section: 冲突标记**: Populated in Phase 3
- **Section: 上下文证据 - {Domain N}**: Evidence section per domain
```yaml
---
session_id: CPLAN-{slug}-{date}
original_requirement: "{task description}"
created_at: "{ISO timestamp}"
complexity: Low | Medium | High
sub_domains: ["{domain-1}", "{domain-2}", ...]
domain_task_id_ranges:
"{domain-1}": [1, 100]
"{domain-2}": [101, 200]
status: planning
---
```
**Sections**:
- `## 需求理解` — Core objectives, key points, constraints, split strategy
- `## 任务池 - {Domain N}` — Pre-allocated task section per domain (TASK-{range})
- `## 依赖关系` — Auto-generated after all domains complete
- `## 冲突标记` — Populated in Phase 3
- `## 上下文证据 - {Domain N}` — Evidence section per domain
**TASK ID Range Allocation**: Each domain receives a non-overlapping range of 100 IDs (e.g., Domain 1: TASK-001~100, Domain 2: TASK-101~200).
### Step 1.3: Generate requirement-analysis.json
Create the sub-domain configuration document.
**requirement-analysis.json Structure**:
| Field | Purpose |
|-------|---------|
| `session_id` | Session identifier |
| `original_requirement` | Task description |
| `complexity` | Low / Medium / High |
| `sub_domains[]` | Array of focus areas with descriptions |
| `sub_domains[].focus_area` | Domain name |
| `sub_domains[].description` | Domain scope description |
| `sub_domains[].task_id_range` | Non-overlapping TASK ID range |
| `sub_domains[].estimated_effort` | Effort estimate |
| `sub_domains[].dependencies` | Cross-domain dependencies |
| `total_domains` | Number of domains identified |
```javascript
Write(`${sessionFolder}/requirement-analysis.json`, JSON.stringify({
session_id: sessionId,
original_requirement: taskDescription,
complexity: complexity, // Low | Medium | High
sub_domains: subDomains.map(sub => ({
focus_area: sub.focus_area,
description: sub.description,
task_id_range: sub.task_id_range,
estimated_effort: sub.estimated_effort,
dependencies: sub.dependencies // cross-domain dependencies
})),
total_domains: subDomains.length
}, null, 2))
```
**Success Criteria**:
- 2-5 clear sub-domains identified
- Latest documentation identified and referenced (if available)
- Ambiguities resolved via user clarification (if any found)
- 2-{maxDomains} clear sub-domains identified
- Each sub-domain can be planned independently
- Plan Note template includes all pre-allocated sections
- TASK ID ranges have no overlap (100 IDs per domain)
@@ -182,163 +257,134 @@ Create the sub-domain configuration document.
---
## Phase 2: Parallel Sub-Domain Planning
## Phase 2: Serial Sub-Domain Planning
**Objective**: Spawn parallel subagents for each sub-domain, generating detailed plans and updating plan-note.md concurrently.
**Objective**: Plan each sub-domain sequentially, generating detailed plans and updating plan-note.md.
**Execution Model**: Parallel subagent execution - all domains planned simultaneously via `spawn_agent` + batch `wait`.
**Key API Pattern**:
```
spawn_agent × N → wait({ ids: [...] }) → verify outputs → close_agent × N
```
**Execution Model**: Serial inline execution — each domain explored and planned directly using search tools, one at a time.
### Step 2.1: User Confirmation (unless autoMode)
Display identified sub-domains and confirm before spawning agents.
Display identified sub-domains and confirm before starting.
```javascript
// User confirmation
if (!autoMode) {
// Display sub-domains for user approval
// Options: "开始规划" / "调整拆分" / "取消"
AskUserQuestion({
questions: [{
question: `已识别 ${subDomains.length} 个子领域:\n${subDomains.map((s, i) =>
`${i+1}. ${s.focus_area}: ${s.description}`).join('\n')}\n\n确认开始规划?`,
header: "Confirm",
multiSelect: false,
options: [
{ label: "开始规划", description: "逐域进行规划" },
{ label: "调整拆分", description: "修改子领域划分" },
{ label: "取消", description: "退出规划" }
]
}]
})
}
```
### Step 2.2: Parallel Subagent Planning
### Step 2.2: Serial Domain Planning
**⚠️ IMPORTANT**: Role files are NOT read by main process. Pass path in message, agent reads itself.
**Spawn All Domain Agents in Parallel**:
For each sub-domain, execute the full planning cycle inline:
```javascript
// Create agent directories first
subDomains.forEach(sub => {
// mkdir: ${sessionFolder}/agents/${sub.focus_area}/
})
for (const sub of subDomains) {
// 1. Create domain directory
Bash(`mkdir -p ${sessionFolder}/domains/${sub.focus_area}`)
// Parallel spawn - all agents start immediately
const agentIds = subDomains.map(sub => {
return spawn_agent({
message: `
## TASK ASSIGNMENT
// 2. Explore codebase for domain-relevant context
// Use: mcp__ace-tool__search_context, Grep, Glob, Read
// Focus on:
// - Modules/components related to this domain
// - Existing patterns to follow
// - Integration points with other domains
// - Architecture constraints
### MANDATORY FIRST STEPS (Agent Execute)
1. **Read role definition**: ~/.codex/agents/cli-lite-planning-agent.md (MUST read first)
2. Read: ${projectRoot}/.workflow/project-tech.json
3. Read: ${projectRoot}/.workflow/project-guidelines.json
4. Read: ${sessionFolder}/plan-note.md (understand template structure)
5. Read: ${sessionFolder}/requirement-analysis.json (understand full context)
// 3. Generate detailed plan.json
const plan = {
session_id: sessionId,
focus_area: sub.focus_area,
description: sub.description,
task_id_range: sub.task_id_range,
generated_at: getUtc8ISOString(),
tasks: [
// For each task within the assigned ID range:
{
id: `TASK-${String(sub.task_id_range[0]).padStart(3, '0')}`,
title: "...",
complexity: "Low | Medium | High",
depends_on: [], // TASK-xxx references
scope: "...", // Brief scope description
modification_points: [ // file:line → change summary
{ file: "...", location: "...", change: "..." }
],
conflict_risk: "Low | Medium | High",
estimated_effort: "..."
}
// ... more tasks
],
evidence: {
relevant_files: [...],
existing_patterns: [...],
constraints: [...]
}
}
Write(`${sessionFolder}/domains/${sub.focus_area}/plan.json`, JSON.stringify(plan, null, 2))
---
// 4. Sync summary to plan-note.md
// Read current plan-note.md
// Locate pre-allocated sections:
// - Task Pool: "## 任务池 - ${toTitleCase(sub.focus_area)}"
// - Evidence: "## 上下文证据 - ${toTitleCase(sub.focus_area)}"
// Fill with task summaries and evidence
// Write back plan-note.md
}
```
## Sub-Domain Context
**Focus Area**: ${sub.focus_area}
**Description**: ${sub.description}
**TASK ID Range**: ${sub.task_id_range[0]}-${sub.task_id_range[1]}
**Session**: ${sessionId}
**Task Summary Format** (for plan-note.md task pool sections):
## Dual Output Tasks
### Task 1: Generate Complete plan.json
Output: ${sessionFolder}/agents/${sub.focus_area}/plan.json
Include:
- Task breakdown with IDs from assigned range (${sub.task_id_range[0]}-${sub.task_id_range[1]})
- Dependencies within and across domains
- Files to modify with specific locations
- Effort and complexity estimates per task
- Conflict risk assessment for each task
### Task 2: Sync Summary to plan-note.md
**Locate Your Sections** (pre-allocated, ONLY modify these):
- Task Pool: "## 任务池 - ${toTitleCase(sub.focus_area)}"
- Evidence: "## 上下文证据 - ${toTitleCase(sub.focus_area)}"
**Task Summary Format**:
### TASK-{ID}: {Title} [${sub.focus_area}]
```markdown
### TASK-{ID}: {Title} [{focus-area}]
- **状态**: pending
- **复杂度**: Low/Medium/High
- **依赖**: TASK-xxx (if any)
- **范围**: Brief scope description
- **修改点**: file:line - change summary
- **修改点**: `file:location`: change summary
- **冲突风险**: Low/Medium/High
**Evidence Format**:
- 相关文件: File list with relevance
- 现有模式: Patterns identified
- 约束: Constraints discovered
## Execution Steps
1. Explore codebase for domain-relevant files
2. Generate complete plan.json
3. Extract task summaries from plan.json
4. Read ${sessionFolder}/plan-note.md
5. Locate and fill your pre-allocated task pool section
6. Locate and fill your pre-allocated evidence section
7. Write back plan-note.md
## Important Rules
- ONLY modify your pre-allocated sections (do NOT touch other domains)
- Use assigned TASK ID range exclusively: ${sub.task_id_range[0]}-${sub.task_id_range[1]}
- Include conflict_risk assessment for each task
## Success Criteria
- [ ] Role definition read
- [ ] plan.json generated with detailed tasks
- [ ] plan-note.md updated with task pool and evidence
- [ ] All tasks within assigned ID range
`
})
})
// Batch wait - TRUE PARALLELISM (key Codex advantage)
const results = wait({
ids: agentIds,
timeout_ms: 900000 // 15 minutes for all planning agents
})
// Handle timeout
if (results.timed_out) {
const completed = agentIds.filter(id => results.status[id].completed)
const pending = agentIds.filter(id => !results.status[id].completed)
// Option: Continue waiting or use partial results
// If most agents completed, proceed with partial results
}
// Verify outputs exist
subDomains.forEach((sub, index) => {
const agentId = agentIds[index]
if (results.status[agentId].completed) {
// Verify: agents/${sub.focus_area}/plan.json exists
// Verify: plan-note.md sections populated
}
})
// Batch cleanup
agentIds.forEach(id => close_agent({ id }))
```
**Evidence Format** (for plan-note.md evidence sections):
```markdown
- **相关文件**: file list with relevance
- **现有模式**: patterns identified
- **约束**: constraints discovered
```
**Domain Planning Rules**:
- Each domain modifies ONLY its pre-allocated sections in plan-note.md
- Use assigned TASK ID range exclusively
- Include `conflict_risk` assessment for each task
- Reference cross-domain dependencies explicitly
### Step 2.3: Verify plan-note.md Consistency
After all agents complete, verify the shared document.
After all domains are planned, verify the shared document.
**Verification Activities**:
1. Read final plan-note.md
2. Verify all task pool sections are populated
3. Verify all evidence sections are populated
4. Check for any accidental cross-section modifications
5. Validate TASK ID uniqueness across all domains
4. Validate TASK ID uniqueness across all domains
5. Check for any section format inconsistencies
**Success Criteria**:
- All subagents spawned and completed (or timeout handled)
- `agents/{domain}/plan.json` created for each domain
- `domains/{domain}/plan.json` created for each domain
- `plan-note.md` updated with all task pools and evidence sections
- Task summaries follow consistent format
- No TASK ID overlaps across domains
- All agents closed properly
---
@@ -350,13 +396,28 @@ After all agents complete, verify the shared document.
Extract all tasks from all "任务池" sections.
**Extraction Activities**:
1. Read plan-note.md content
2. Parse YAML frontmatter for session metadata
3. Identify all "任务池" sections by heading pattern
4. Extract tasks matching pattern: `### TASK-{ID}: {Title} [{domain}]`
5. Parse task details: status, complexity, dependencies, modification points, conflict risk
6. Consolidate into unified task list
```javascript
// parsePlanNote(markdown)
// - Extract YAML frontmatter between `---` markers
// - Scan for heading patterns: /^(#{2,})\s+(.+)$/
// - Build sections array: { level, heading, start, content }
// - Return: { frontmatter, sections }
// extractTasksFromSection(content, sectionHeading)
// - Match: /### (TASK-\d+):\s+(.+?)\s+\[(.+?)\]/
// - For each: extract taskId, title, author
// - Parse details: status, complexity, depends_on, modification_points, conflict_risk
// - Return: array of task objects
// parseTaskDetails(content)
// - Extract via regex:
// - /\*\*状态\*\*:\s*(.+)/ → status
// - /\*\*复杂度\*\*:\s*(.+)/ → complexity
// - /\*\*依赖\*\*:\s*(.+)/ → depends_on (extract TASK-\d+ references)
// - /\*\*冲突风险\*\*:\s*(.+)/ → conflict_risk
// - Extract modification points: /- `([^`]+):\s*([^`]+)`:\s*(.+)/ → file, location, summary
// - Return: { status, complexity, depends_on[], modification_points[], conflict_risk }
```
### Step 3.2: Detect Conflicts
@@ -370,21 +431,74 @@ Scan all tasks for three categories of conflicts.
| dependency_cycle | critical | Circular dependencies in task graph (DFS detection) | Remove or reorganize dependencies |
| strategy_conflict | medium | Multiple high-risk tasks in same file from different domains | Review approaches and align on single strategy |
**Detection Activities**:
1. **File Conflicts**: Group modification points by file:location, identify locations modified by multiple domains
2. **Dependency Cycles**: Build dependency graph from task dependencies, detect cycles using depth-first search
3. **Strategy Conflicts**: Group tasks by files they modify, identify files with high-risk tasks from multiple domains
**Detection Functions**:
```javascript
// detectFileConflicts(tasks)
// Build fileMap: { "file:location": [{ task_id, task_title, source_domain, change }] }
// For each location with modifications from multiple domains:
// → conflict: type='file_conflict', severity='high'
// → include: location, tasks_involved, domains_involved, modifications
// → resolution: 'Coordinate modification order or merge changes'
// detectDependencyCycles(tasks)
// Build dependency graph: { taskId: [dependsOn_taskIds] }
// DFS with recursion stack to detect cycles:
function detectCycles(tasks) {
const graph = new Map(tasks.map(t => [t.id, t.depends_on || []]))
const visited = new Set(), inStack = new Set(), cycles = []
function dfs(node, path) {
if (inStack.has(node)) { cycles.push([...path, node].join(' → ')); return }
if (visited.has(node)) return
visited.add(node); inStack.add(node)
;(graph.get(node) || []).forEach(dep => dfs(dep, [...path, node]))
inStack.delete(node)
}
tasks.forEach(t => { if (!visited.has(t.id)) dfs(t.id, []) })
return cycles
}
// detectStrategyConflicts(tasks)
// Group tasks by files they modify
// For each file with tasks from multiple domains:
// Filter for high/medium conflict_risk tasks
// If >1 high-risk from different domains:
// → conflict: type='strategy_conflict', severity='medium'
// → resolution: 'Review approaches and align on single strategy'
```
### Step 3.3: Generate Conflict Artifacts
Write conflict results and update plan-note.md.
**conflicts.json Structure**:
- `detected_at`: Detection timestamp
- `total_conflicts`: Number of conflicts found
- `conflicts[]`: Array of conflict objects with type, severity, tasks involved, description, suggested resolution
```javascript
// 1. Write conflicts.json
Write(`${sessionFolder}/conflicts.json`, JSON.stringify({
detected_at: getUtc8ISOString(),
total_tasks: allTasks.length,
total_domains: subDomains.length,
total_conflicts: allConflicts.length,
conflicts: allConflicts // { type, severity, tasks_involved, description, suggested_resolution }
}, null, 2))
**plan-note.md Update**: Locate "冲突标记" section and populate with conflict summary markdown. If no conflicts found, mark as "✅ 无冲突检测到".
// 2. Update plan-note.md "## 冲突标记" section
// generateConflictMarkdown(conflicts):
// If empty: return '✅ 无冲突检测到'
// For each conflict:
// ### CONFLICT-{padded_index}: {description}
// - **严重程度**: critical | high | medium
// - **涉及任务**: TASK-xxx, TASK-yyy
// - **涉及领域**: domain-a, domain-b
// - **问题详情**: (based on conflict type)
// - **建议解决方案**: ...
// - **决策状态**: [ ] 待解决
// replaceSectionContent(markdown, sectionHeading, newContent):
// Find section heading position via regex
// Find next heading of same or higher level
// Replace content between heading and next section
// If section not found: append at end
```
**Success Criteria**:
- All tasks extracted and analyzed
@@ -413,16 +527,85 @@ Create a human-readable summary from plan-note.md content.
| 冲突报告 (Conflict Report) | Summary of detected conflicts or "无冲突" |
| 执行指令 (Execution) | Command to execute the plan |
```javascript
const planMd = `# Collaborative Plan
**Session**: ${sessionId}
**Requirement**: ${taskDescription}
**Created**: ${getUtc8ISOString()}
**Complexity**: ${complexity}
**Domains**: ${subDomains.length}
## 需求理解
${requirementSection}
## 子领域拆分
| # | Focus Area | Description | TASK Range | Effort |
|---|-----------|-------------|------------|--------|
${subDomains.map((s, i) => `| ${i+1} | ${s.focus_area} | ${s.description} | ${s.task_id_range[0]}-${s.task_id_range[1]} | ${s.estimated_effort} |`).join('\n')}
## 任务概览
${subDomains.map(sub => {
const domainTasks = allTasks.filter(t => t.source_domain === sub.focus_area)
return `### ${sub.focus_area}\n\n` +
domainTasks.map(t => `- **${t.id}**: ${t.title} (${t.complexity}) ${t.depends_on.length ? '← ' + t.depends_on.join(', ') : ''}`).join('\n')
}).join('\n\n')}
## 冲突报告
${allConflicts.length === 0
? '✅ 无冲突检测到'
: allConflicts.map(c => `- **${c.type}** (${c.severity}): ${c.description}`).join('\n')}
## 执行
\`\`\`bash
/workflow:unified-execute-with-file "${sessionFolder}/plan-note.md"
\`\`\`
**Session artifacts**: \`${sessionFolder}/\`
`
Write(`${sessionFolder}/plan.md`, planMd)
```
### Step 4.2: Display Completion Summary
Present session statistics and next steps.
**Summary Content**:
- Session ID and directory path
- Total domains planned
- Total tasks generated
- Conflict status
- Execution command for next step
```javascript
// Display:
// - Session ID and directory path
// - Total domains planned
// - Total tasks generated
// - Conflict status (count and severity)
// - Execution command for next step
if (!autoMode) {
AskUserQuestion({
questions: [{
question: `规划完成:\n- ${subDomains.length} 个子领域\n- ${allTasks.length} 个任务\n- ${allConflicts.length} 个冲突\n\n下一步:`,
header: "Next Step",
multiSelect: false,
options: [
{ label: "Execute Plan", description: "使用 unified-execute 执行计划" },
{ label: "Review Conflicts", description: "查看并解决冲突" },
{ label: "Export", description: "导出 plan.md" },
{ label: "Done", description: "保存产物,稍后执行" }
]
}]
})
}
```
| Selection | Action |
|-----------|--------|
| Execute Plan | `Skill(skill="workflow:unified-execute-with-file", args="${sessionFolder}/plan-note.md")` |
| Review Conflicts | Display conflicts.json content for manual resolution |
| Export | Copy plan.md + plan-note.md to user-specified location |
| Done | Display artifact paths, end workflow |
**Success Criteria**:
- `plan.md` generated with complete summary
@@ -433,73 +616,27 @@ Present session statistics and next steps.
## Configuration
| Parameter | Default | Description |
|-----------|---------|-------------|
| Flag | Default | Description |
|------|---------|-------------|
| `--max-domains` | 5 | Maximum sub-domains to identify |
---
## Error Handling & Recovery
| Situation | Action | Recovery |
|-----------|--------|----------|
| **Subagent timeout** | Check `results.timed_out`, continue `wait()` or use partial results | Reduce scope, plan remaining domains with new agent |
| **Agent closed prematurely** | Cannot recover closed agent | Spawn new agent with domain context |
| **Parallel agent partial failure** | Some domains complete, some fail | Use completed results, re-spawn for failed domains |
| **plan-note.md write conflict** | Multiple agents write simultaneously | Pre-allocated sections prevent this; if detected, re-read and verify |
| **Section not found in plan-note** | Agent creates section defensively | Continue with new section |
| **No tasks generated** | Review domain description | Retry with refined description via new agent |
| **Conflict detection fails** | Continue with empty conflicts | Note in completion summary |
| **Session folder conflict** | Append timestamp suffix | Create unique folder |
### Codex-Specific Error Patterns
```javascript
// Safe parallel planning with error handling
try {
const agentIds = subDomains.map(sub => spawn_agent({ message: buildPlanPrompt(sub) }))
const results = wait({ ids: agentIds, timeout_ms: 900000 })
if (results.timed_out) {
const completed = agentIds.filter(id => results.status[id].completed)
const pending = agentIds.filter(id => !results.status[id].completed)
// Re-spawn for timed-out domains
const retryIds = pending.map((id, i) => {
const sub = subDomains[agentIds.indexOf(id)]
return spawn_agent({ message: buildPlanPrompt(sub) })
})
const retryResults = wait({ ids: retryIds, timeout_ms: 600000 })
retryIds.forEach(id => { try { close_agent({ id }) } catch(e) {} })
}
} finally {
// ALWAYS cleanup
agentIds.forEach(id => {
try { close_agent({ id }) } catch (e) { /* ignore */ }
})
}
```
---
| `-y, --yes` | false | Auto-confirm all decisions |
## Iteration Patterns
### New Planning Session (Parallel Mode)
### New Planning Session
```
User initiates: TASK="task description"
├─ No session exists → New session mode
├─ Analyze task and identify sub-domains
├─ Analyze task with inline search tools
├─ Identify sub-domains
├─ Create plan-note.md template
├─ Generate requirement-analysis.json
├─ Execute parallel planning:
│ ├─ spawn_agent × N (one per sub-domain)
│ ├─ wait({ ids: [...] }) ← TRUE PARALLELISM
│ └─ close_agent × N
├─ Serial domain planning:
│ ├─ Domain 1: explore → plan.json → fill plan-note.md
│ ├─ Domain 2: explore → plan.json → fill plan-note.md
│ └─ Domain N: ...
├─ Verify plan-note.md consistency
├─ Detect conflicts
@@ -514,24 +651,24 @@ User resumes: TASK="same task"
├─ Session exists → Continue mode
├─ Load plan-note.md and requirement-analysis.json
├─ Identify incomplete domains (empty task pool sections)
├─ Spawn agents for incomplete domains only
├─ Plan remaining domains serially
└─ Continue with conflict detection
```
### Agent Lifecycle Management
---
```
Subagent lifecycle:
├─ spawn_agent({ message }) → Create with role path + task
├─ wait({ ids, timeout_ms }) → Get results (ONLY way to get output)
└─ close_agent({ id }) → Cleanup (MUST do, cannot recover)
## Error Handling & Recovery
Key rules:
├─ Pre-allocated sections = no write conflicts
├─ ALWAYS use wait() to get results, NOT close_agent()
├─ Batch wait for all domain agents: wait({ ids: [a, b, c, ...] })
└─ Verify plan-note.md after batch completion
```
| Situation | Action | Recovery |
|-----------|--------|----------|
| No codebase detected | Normal flow, pure requirement planning | Proceed without codebase context |
| Codebase search fails | Continue with available context | Note limitation in plan-note.md |
| Domain planning fails | Record error, continue with next domain | Retry failed domain or plan manually |
| Section not found in plan-note | Create section defensively | Continue with new section |
| No tasks generated for a domain | Review domain description | Refine scope and retry |
| Conflict detection fails | Continue with empty conflicts | Note in completion summary |
| Session folder conflict | Append timestamp suffix | Create unique folder |
| plan-note.md format inconsistency | Validate and fix format after each domain | Re-read and normalize |
---
@@ -540,25 +677,17 @@ Key rules:
### Before Starting Planning
1. **Clear Task Description**: Detailed requirements lead to better sub-domain splitting
2. **Reference Documentation**: Ensure latest README and design docs are identified
2. **Reference Documentation**: Ensure latest README and design docs are identified during Phase 1
3. **Clarify Ambiguities**: Resolve unclear requirements before committing to sub-domains
### During Planning
1. **Review Plan Note**: Check plan-note.md between phases to verify progress
2. **Verify Domains**: Ensure sub-domains are truly independent and parallelizable
1. **Review Plan Note**: Check plan-note.md between domains to verify progress
2. **Verify Independence**: Ensure sub-domains are truly independent and have minimal overlap
3. **Check Dependencies**: Cross-domain dependencies should be documented explicitly
4. **Inspect Details**: Review `agents/{domain}/plan.json` for specifics when needed
### Codex Subagent Best Practices
1. **Role Path, Not Content**: Pass `~/.codex/agents/*.md` path in message, let agent read itself
2. **Pre-allocated Sections**: Each agent only writes to its own sections - no write conflicts
3. **Batch wait**: Use `wait({ ids: [a, b, c] })` for all domain agents, not sequential waits
4. **Handle Timeouts**: Check `results.timed_out`, re-spawn for failed domains
5. **Explicit Cleanup**: Always `close_agent` when done, even on errors (use try/finally)
6. **Verify After Batch**: Read plan-note.md after all agents complete to verify consistency
7. **TASK ID Isolation**: Pre-assigned non-overlapping ranges prevent ID conflicts
4. **Inspect Details**: Review `domains/{domain}/plan.json` for specifics when needed
5. **Consistent Format**: Follow task summary format strictly across all domains
6. **TASK ID Isolation**: Use pre-assigned non-overlapping ranges to prevent ID conflicts
### After Planning
@@ -566,6 +695,26 @@ Key rules:
2. **Review Summary**: Check plan.md for completeness and accuracy
3. **Validate Tasks**: Ensure all tasks have clear scope and modification targets
## When to Use
**Use collaborative-plan-with-file when:**
- A complex task spans multiple sub-domains (backend + frontend + database, etc.)
- Need structured multi-domain task breakdown with conflict detection
- Planning a feature that touches many parts of the codebase
- Want pre-allocated section organization for clear domain separation
**Use lite-plan when:**
- Single domain, clear task with no sub-domain splitting needed
- Quick planning without conflict detection
**Use req-plan-with-file when:**
- Requirement-level progressive roadmap needed (MVP → iterations)
- Higher-level decomposition before detailed planning
**Use analyze-with-file when:**
- Need in-depth analysis before planning
- Understanding and discussion, not task generation
---
**Now execute collaborative-plan-with-file for**: $TASK
**Now execute collaborative-plan-with-file for**: $ARGUMENTS

View File

@@ -0,0 +1,920 @@
---
name: req-plan-with-file
description: Requirement-level progressive roadmap planning with JSONL output. Decomposes requirements into convergent layers (MVP→iterations) or topologically-sorted task sequences, each with testable completion criteria.
argument-hint: "[-y|--yes] [-c|--continue] [-m|--mode progressive|direct|auto] \"requirement description\""
---
# Codex Req-Plan-With-File Prompt
## Overview
Requirement-level roadmap planning with **JSONL output and convergence criteria**. Decomposes a requirement into self-contained layers or tasks, each with testable completion criteria, independently executable via `lite-plan`.
**Core workflow**: Requirement Understanding → Strategy Selection → Context Collection (optional) → Decomposition → Validation → Quality Check → Output
**Dual modes**:
- **Progressive**: Layered MVP→iterations, suitable for high-uncertainty requirements (validate first, then refine)
- **Direct**: Topologically-sorted task sequence, suitable for low-uncertainty requirements (clear tasks, directly ordered)
- **Auto**: Automatically selects based on uncertainty assessment
## Auto Mode
When `--yes` or `-y`: Auto-confirm strategy selection, use recommended mode, skip interactive validation rounds.
## Quick Start
```bash
# Basic usage
/codex:req-plan-with-file "Implement user authentication system with OAuth and 2FA"
# With mode selection
/codex:req-plan-with-file -m progressive "Build real-time notification system"
/codex:req-plan-with-file -m direct "Refactor payment module"
/codex:req-plan-with-file -m auto "Add data export feature"
# Continue existing session
/codex:req-plan-with-file --continue "user authentication system"
# Auto mode (skip all confirmations)
/codex:req-plan-with-file -y "Implement caching layer"
```
## Target Requirement
**$ARGUMENTS**
## Execution Process
```
Step 0: Session Setup
├─ Parse flags (-y, -c, -m) and requirement text
├─ Generate session ID: RPLAN-{slug}-{date}
└─ Create session folder (or detect existing → continue mode)
Step 1: Parse Requirement & Select Strategy
├─ Extract goal, constraints, stakeholders, domain keywords
├─ Assess uncertainty (5 dimensions)
├─ Select mode: progressive / direct
├─ Write strategy-assessment.json
└─ Initialize roadmap.md skeleton
Step 2: Explore Codebase (Optional)
├─ Detect project markers (package.json, go.mod, etc.)
├─ Has codebase → search relevant modules, patterns, integration points
│ ├─ Read project-tech.json / project-guidelines.json (if exists)
│ ├─ Search modules/components related to the requirement
│ └─ Write exploration-codebase.json
└─ No codebase → skip
Step 3: Decompose Requirement → roadmap.jsonl
├─ Step 3.1: Build decomposition (requirement + strategy + context)
│ ├─ Generate JSONL records with convergence criteria
│ └─ Apply convergence quality requirements
├─ Step 3.2: Validate records
│ ├─ Schema compliance, scope integrity
│ ├─ No circular dependencies (topological sort)
│ ├─ Convergence quality (testable criteria, executable verification, business DoD)
│ └─ Write roadmap.jsonl
└─ Step 3.3: Quality check (MANDATORY)
├─ Requirement coverage, convergence quality, scope integrity
├─ Dependency correctness, effort balance
└─ Auto-fix or report issues
Step 4: Validate & Finalize → roadmap.md
├─ Display decomposition (tabular + convergence details)
├─ User feedback loop (up to 5 rounds, skip if -y)
├─ Write final roadmap.md
└─ Next step options: lite-plan / issue / export / done
```
## Configuration
| Flag | Default | Description |
|------|---------|-------------|
| `-y, --yes` | false | Auto-confirm all decisions |
| `-c, --continue` | false | Continue existing session |
| `-m, --mode` | auto | Decomposition strategy: progressive / direct / auto |
**Session ID format**: `RPLAN-{slug}-{YYYY-MM-DD}`
- slug: lowercase, alphanumeric + CJK characters, max 40 chars
- date: YYYY-MM-DD (UTC+8)
- Auto-detect continue: session folder + roadmap.jsonl exists → continue mode
## Implementation Details
### Session Setup
##### Step 0: Initialize Session
```javascript
const getUtc8ISOString = () => new Date(Date.now() + 8 * 60 * 60 * 1000).toISOString()
// Parse flags
const autoYes = $ARGUMENTS.includes('--yes') || $ARGUMENTS.includes('-y')
const continueMode = $ARGUMENTS.includes('--continue') || $ARGUMENTS.includes('-c')
const modeMatch = $ARGUMENTS.match(/(?:--mode|-m)\s+(progressive|direct|auto)/)
const requestedMode = modeMatch ? modeMatch[1] : 'auto'
// Clean requirement text
const requirement = $ARGUMENTS
.replace(/--yes|-y|--continue|-c|--mode\s+\w+|-m\s+\w+/g, '')
.trim()
const slug = requirement.toLowerCase()
.replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
.substring(0, 40)
const dateStr = getUtc8ISOString().substring(0, 10)
const sessionId = `RPLAN-${slug}-${dateStr}`
const sessionFolder = `.workflow/.req-plan/${sessionId}`
// Auto-detect continue: session folder + roadmap.jsonl exists → continue mode
// If continue → skip to Step 4 (load existing roadmap.jsonl, display, collect feedback)
Bash(`mkdir -p ${sessionFolder}`)
```
### Phase 1: Parse Requirement & Select Strategy
**Objective**: Parse requirement, assess uncertainty, select decomposition strategy.
##### Step 1.1: Analyze Requirement & Select Strategy
Parse the requirement, evaluate uncertainty across 5 dimensions, and select decomposition strategy in one step:
```javascript
// 1. Extract from requirement text
const goal = extractCoreGoal(requirement) // What to achieve
const constraints = extractConstraints(requirement) // Tech stack, timeline, compatibility
const stakeholders = extractStakeholders(requirement) // Users, admins, developers
const domainKeywords = extractDomainKeywords(requirement) // Domain-specific terms
// 2. Assess uncertainty (each: low | medium | high)
const uncertaintyFactors = {
scope_clarity: '...', // Is scope well-defined?
technical_risk: '...', // Known tech vs experimental?
dependency_unknown: '...', // Are dependencies clear?
domain_familiarity: '...', // Team knows this domain?
requirement_stability: '...' // Will requirements change?
}
// >=3 high → progressive, >=3 low → direct, otherwise → ask
// 3. Select strategy
let selectedMode
if (requestedMode !== 'auto') {
selectedMode = requestedMode
} else if (autoYes) {
selectedMode = recommendedMode
} else {
AskUserQuestion({
questions: [{
question: `Decomposition strategy selection:\n\nUncertainty assessment: ${uncertaintyLevel}\nRecommended strategy: ${recommendedMode}\n\nSelect decomposition strategy:`,
header: "Strategy",
multiSelect: false,
options: [
{
label: recommendedMode === 'progressive' ? "Progressive (Recommended)" : "Progressive",
description: "Layered MVP→iterations, validate core first then refine progressively. Suitable for high-uncertainty requirements needing quick validation"
},
{
label: recommendedMode === 'direct' ? "Direct (Recommended)" : "Direct",
description: "Topologically-sorted task sequence with explicit dependencies. Suitable for clear requirements with confirmed technical approach"
}
]
}]
})
}
// 4. Write strategy assessment
Write(`${sessionFolder}/strategy-assessment.json`, JSON.stringify({
session_id: sessionId,
requirement, timestamp: getUtc8ISOString(),
uncertainty_factors: uncertaintyFactors,
uncertainty_level: uncertaintyLevel,
recommended_mode: recommendedMode,
selected_mode: selectedMode,
goal, constraints, stakeholders,
domain_keywords: domainKeywords
}, null, 2))
// 5. Initialize roadmap.md skeleton
const roadmapMdSkeleton = `# Requirement Roadmap
**Session**: ${sessionId}
**Requirement**: ${requirement}
**Strategy**: ${selectedMode}
**Status**: Planning
**Created**: ${getUtc8ISOString()}
## Strategy Assessment
- Uncertainty level: ${uncertaintyLevel}
- Decomposition mode: ${selectedMode}
- Assessment basis: ${Object.entries(uncertaintyFactors).map(([k,v]) => `${k}=${v}`).join(', ')}
## Roadmap
> To be populated after Phase 3 decomposition
## Convergence Criteria Details
> To be populated after Phase 3 decomposition
## Risk Items
> To be populated after Phase 3 decomposition
## Next Steps
> To be populated after Phase 4 validation
`
Write(`${sessionFolder}/roadmap.md`, roadmapMdSkeleton)
```
**Success Criteria**:
- Requirement goal, constraints, stakeholders, domain keywords identified
- Uncertainty level assessed across 5 dimensions
- Strategy selected (progressive or direct)
- strategy-assessment.json generated
- roadmap.md skeleton initialized
### Phase 2: Explore Codebase (Optional)
**Objective**: If a codebase exists, collect relevant context to enhance decomposition quality.
##### Step 2.1: Detect & Explore
If a codebase exists, directly search for relevant context. No agent delegation — use search tools inline.
```javascript
const hasCodebase = Bash(`
test -f package.json && echo "nodejs" ||
test -f go.mod && echo "golang" ||
test -f Cargo.toml && echo "rust" ||
test -f pyproject.toml && echo "python" ||
test -f pom.xml && echo "java" ||
test -d src && echo "generic" ||
echo "none"
`).trim()
if (hasCodebase !== 'none') {
// 1. Read project metadata (if exists)
// - .workflow/project-tech.json (tech stack info)
// - .workflow/project-guidelines.json (project conventions)
// 2. Search codebase for modules related to the requirement
// Use: Grep, Glob, Read, or mcp__ace-tool__search_context
// Focus on:
// - Modules/components related to the requirement
// - Existing patterns to follow
// - Integration points for new functionality
// - Architecture constraints
// 3. Write findings to exploration-codebase.json:
Write(`${sessionFolder}/exploration-codebase.json`, JSON.stringify({
project_type: hasCodebase,
relevant_modules: [...], // [{name, path, relevance}]
existing_patterns: [...], // [{pattern, files, description}]
integration_points: [...], // [{location, description, risk}]
architecture_constraints: [...],
tech_stack: {languages, frameworks, tools},
_metadata: {timestamp: getUtc8ISOString(), exploration_scope: '...'}
}, null, 2))
}
// No codebase → skip, proceed to Phase 3
```
**Success Criteria**:
- Codebase detection complete
- When codebase exists, exploration-codebase.json generated with relevant modules, patterns, integration points
- When no codebase, skipped and proceed to Phase 3
### Phase 3: Decompose Requirement
**Objective**: Execute requirement decomposition, generating validated roadmap.jsonl.
##### Step 3.1: Generate JSONL Records
Directly decompose the requirement into JSONL records based on the selected mode. Analyze the requirement yourself and produce the records, referencing strategy assessment and codebase context (if available).
```javascript
// Load context
const strategy = JSON.parse(Read(`${sessionFolder}/strategy-assessment.json`))
let explorationContext = null
if (file_exists(`${sessionFolder}/exploration-codebase.json`)) {
explorationContext = JSON.parse(Read(`${sessionFolder}/exploration-codebase.json`))
}
```
**Progressive mode** — generate 2-4 layers:
```javascript
// Each layer must have:
// - id: L0, L1, L2, L3
// - name: MVP / Usable / Refined / Optimized
// - goal: what this layer achieves
// - scope[]: features included
// - excludes[]: features explicitly deferred
// - convergence: { criteria[], verification, definition_of_done }
// - risk_items[], effort (small|medium|large), depends_on[]
//
// Rules:
// - L0 (MVP) = self-contained closed loop, no dependencies
// - Each feature in exactly ONE layer (no overlap)
// - 2-4 layers total
// - Convergence MUST satisfy quality requirements (see below)
const layers = [
{
id: "L0", name: "MVP",
goal: "...",
scope: ["..."], excludes: ["..."],
convergence: {
criteria: ["... (testable)"],
verification: "... (executable command or steps)",
definition_of_done: "... (business language)"
},
risk_items: [], effort: "medium", depends_on: []
},
// L1, L2, ...
]
```
**Direct mode** — generate topologically-sorted tasks:
```javascript
// Each task must have:
// - id: T1, T2, ...
// - title, type (infrastructure|feature|enhancement|testing)
// - scope, inputs[], outputs[]
// - convergence: { criteria[], verification, definition_of_done }
// - depends_on[], parallel_group
//
// Rules:
// - Inputs must come from preceding task outputs or existing resources
// - Same parallel_group = truly independent
// - No circular dependencies
// - Convergence MUST satisfy quality requirements (see below)
const tasks = [
{
id: "T1", title: "...", type: "infrastructure",
scope: "...", inputs: [], outputs: ["..."],
convergence: {
criteria: ["... (testable)"],
verification: "... (executable)",
definition_of_done: "... (business language)"
},
depends_on: [], parallel_group: 1
},
// T2, T3, ...
]
```
##### Step 3.2: Validate Records
Validate all records before writing JSONL. Fix any issues found.
```javascript
const items = selectedMode === 'progressive' ? layers : tasks
const errors = []
// 1. Schema validation: each record has convergence with all 3 fields
items.forEach(item => {
if (!item.convergence?.criteria?.length) errors.push(`${item.id}: missing convergence.criteria`)
if (!item.convergence?.verification) errors.push(`${item.id}: missing convergence.verification`)
if (!item.convergence?.definition_of_done) errors.push(`${item.id}: missing convergence.definition_of_done`)
})
// 2. Convergence quality check
const vaguePatterns = /正常|正确|好|可以|没问题|works|fine|good|correct/i
items.forEach(item => {
item.convergence.criteria.forEach((criterion, i) => {
if (vaguePatterns.test(criterion) && criterion.length < 15) {
errors.push(`${item.id} criteria[${i}]: Too vague - "${criterion}"`)
}
})
if (item.convergence.verification.length < 10) {
errors.push(`${item.id} verification: Too short, needs executable steps`)
}
const technicalPatterns = /compile|build|lint|npm|npx|jest|tsc|eslint/i
if (technicalPatterns.test(item.convergence.definition_of_done)) {
errors.push(`${item.id} definition_of_done: Should be business language, not technical commands`)
}
})
// 3. Circular dependency detection
function detectCycles(records, prefix) {
const graph = new Map(records.map(r => [r.id, r.depends_on]))
const visited = new Set(), inStack = new Set(), cycleErrors = []
function dfs(node, path) {
if (inStack.has(node)) { cycleErrors.push(`Circular: ${[...path, node].join(' → ')}`); return }
if (visited.has(node)) return
visited.add(node); inStack.add(node)
;(graph.get(node) || []).forEach(dep => dfs(dep, [...path, node]))
inStack.delete(node)
}
records.forEach(r => { if (!visited.has(r.id)) dfs(r.id, []) })
return cycleErrors
}
errors.push(...detectCycles(items, selectedMode === 'progressive' ? 'L' : 'T'))
// 4. Mode-specific validation
if (selectedMode === 'progressive') {
// Check 2-4 layers
if (items.length < 2 || items.length > 4) errors.push(`Expected 2-4 layers, got ${items.length}`)
// Check L0 is self-contained (no depends_on)
const l0 = items.find(l => l.id === 'L0')
if (l0 && l0.depends_on.length > 0) errors.push("L0 (MVP) should not have dependencies")
// Check scope overlap
const allScopes = new Map()
items.forEach(layer => {
layer.scope.forEach(feature => {
if (allScopes.has(feature)) {
errors.push(`Scope overlap: "${feature}" in both ${allScopes.get(feature)} and ${layer.id}`)
}
allScopes.set(feature, layer.id)
})
})
} else {
// Check inputs/outputs chain
const availableOutputs = new Set()
items.forEach(task => {
task.inputs.forEach(input => {
if (!availableOutputs.has(input)) {
// Only warn for non-existing resources - existing files are valid inputs
}
})
task.outputs.forEach(output => availableOutputs.add(output))
})
// Check parallel_group consistency (same group tasks should not depend on each other)
const groups = new Map()
items.forEach(task => {
if (!groups.has(task.parallel_group)) groups.set(task.parallel_group, [])
groups.get(task.parallel_group).push(task)
})
groups.forEach((groupTasks, groupId) => {
if (groupTasks.length > 1) {
const ids = new Set(groupTasks.map(t => t.id))
groupTasks.forEach(task => {
task.depends_on.forEach(dep => {
if (ids.has(dep)) {
errors.push(`Parallel group ${groupId}: ${task.id} depends on ${dep} but both in same group`)
}
})
})
}
})
}
// 5. Fix errors if any, then write
// If errors found → fix records and re-validate
// Write roadmap.jsonl (one JSON record per line)
const jsonlContent = items.map(item => JSON.stringify(item)).join('\n')
Write(`${sessionFolder}/roadmap.jsonl`, jsonlContent)
```
##### Step 3.3: Quality Check (MANDATORY)
After generating roadmap.jsonl, execute a self-check across 5 quality dimensions before proceeding.
```javascript
const roadmapJsonlContent = Read(`${sessionFolder}/roadmap.jsonl`)
// Quality dimensions to verify:
//
// | Dimension | Check Criteria | Critical? |
// |------------------------|-------------------------------------------------------------|-----------|
// | Requirement Coverage | All aspects of original requirement addressed in layers/tasks | Yes |
// | Convergence Quality | criteria testable, verification executable, DoD business-readable | Yes |
// | Scope Integrity | Progressive: no overlap/gaps; Direct: inputs/outputs chain valid | Yes |
// | Dependency Correctness | No circular deps, proper ordering | Yes |
// | Effort Balance | No single layer/task disproportionately large | No |
//
// Decision after check:
// - PASS → proceed to Phase 4
// - AUTO_FIX → fix convergence wording, rebalance scope, update roadmap.jsonl
// - NEEDS_REVIEW → report issues to user in Phase 4 feedback
// Auto-fix strategy:
// | Issue Type | Auto-Fix Action |
// |-----------------|----------------------------------------------|
// | Vague criteria | Replace with specific, testable conditions |
// | Technical DoD | Rewrite in business language |
// | Missing scope | Add to appropriate layer/task |
// | Effort imbalance| Split oversized layer/task |
// After auto-fixes, update roadmap.jsonl
```
**Success Criteria**:
- roadmap.jsonl generated, each line independently JSON.parse-able
- Each record contains convergence (criteria + verification + definition_of_done)
- Quality check passed (all critical dimensions)
- No circular dependencies
- Progressive: 2-4 layers, no scope overlap, L0 self-contained
- Direct: tasks have explicit inputs/outputs, parallel_group assigned correctly
### Phase 4: Validate & Finalize
**Objective**: Display decomposition results, collect user feedback, generate final artifacts.
##### Step 4.1: Display Results & Collect Feedback
Display the decomposition as a table with convergence criteria, then run feedback loop.
**Progressive Mode display format**:
```markdown
## Roadmap Overview
| Layer | Name | Goal | Scope | Effort | Dependencies |
|-------|------|------|-------|--------|--------------|
| L0 | MVP | ... | ... | medium | - |
| L1 | Usable | ... | ... | medium | L0 |
### Convergence Criteria
**L0 - MVP**:
- Criteria: [criteria list]
- Verification: [verification]
- Definition of Done: [definition_of_done]
```
**Direct Mode display format**:
```markdown
## Task Sequence
| Group | ID | Title | Type | Dependencies |
|-------|----|-------|------|--------------|
| 1 | T1 | ... | infrastructure | - |
| 2 | T2 | ... | feature | T1 |
### Convergence Criteria
**T1 - Establish Data Model**:
- Criteria: [criteria list]
- Verification: [verification]
- Definition of Done: [definition_of_done]
```
**Feedback loop**:
```javascript
const items = Read(`${sessionFolder}/roadmap.jsonl`)
.split('\n').filter(l => l.trim()).map(l => JSON.parse(l))
// Display tabular results + convergence for each item (using format above)
if (!autoYes) {
let round = 0, continueLoop = true
while (continueLoop && round < 5) {
round++
const feedback = AskUserQuestion({
questions: [{
question: `Roadmap validation (round ${round}):\nAny feedback on the current decomposition?`,
header: "Feedback",
multiSelect: false,
options: [
{ label: "Approve", description: "Decomposition is reasonable, generate final artifacts" },
{ label: "Adjust Scope", description: "Some layer/task scopes need adjustment" },
{ label: "Modify Convergence", description: "Convergence criteria not specific enough" },
{ label: "Re-decompose", description: "Overall strategy or layering needs change" }
]
}]
})
if (feedback === 'Approve') continueLoop = false
// else: apply adjustment, re-write roadmap.jsonl, re-display, loop
}
}
```
##### Step 4.2: Write roadmap.md & Next Steps
Generate final roadmap.md using the generation templates below, then provide post-completion options.
**Progressive mode roadmap.md generation**:
```javascript
const roadmapMd = `# Requirement Roadmap
**Session**: ${sessionId}
**Requirement**: ${requirement}
**Strategy**: progressive
**Uncertainty**: ${strategy.uncertainty_level}
**Generated**: ${getUtc8ISOString()}
## Strategy Assessment
- Uncertainty level: ${strategy.uncertainty_level}
- Decomposition mode: progressive
- Assessment basis: ${Object.entries(strategy.uncertainty_factors).map(([k,v]) => `${k}=${v}`).join(', ')}
- Goal: ${strategy.goal}
- Constraints: ${strategy.constraints.join(', ') || 'None'}
- Stakeholders: ${strategy.stakeholders.join(', ') || 'None'}
## Roadmap Overview
| Layer | Name | Goal | Effort | Dependencies |
|-------|------|------|--------|--------------|
${items.map(l => `| ${l.id} | ${l.name} | ${l.goal} | ${l.effort} | ${l.depends_on.length ? l.depends_on.join(', ') : '-'} |`).join('\n')}
## Layer Details
${items.map(l => `### ${l.id}: ${l.name}
**Goal**: ${l.goal}
**Scope**: ${l.scope.join(', ')}
**Excludes**: ${l.excludes.join(', ') || 'None'}
**Convergence Criteria**:
${l.convergence.criteria.map(c => \`- ${c}\`).join('\n')}
- **Verification**: ${l.convergence.verification}
- **Definition of Done**: ${l.convergence.definition_of_done}
**Risk Items**: ${l.risk_items.length ? l.risk_items.map(r => \`\n- ${r}\`).join('') : 'None'}
**Effort**: ${l.effort}
`).join('\n---\n\n')}
## Risk Summary
${items.flatMap(l => l.risk_items.map(r => \`- **${l.id}**: ${r}\`)).join('\n') || 'No identified risks'}
## Next Steps
Each layer can be executed independently:
\\\`\\\`\\\`bash
/workflow:lite-plan "${items[0]?.name}: ${items[0]?.scope.join(', ')}"
\\\`\\\`\\\`
Roadmap JSONL file: \\\`${sessionFolder}/roadmap.jsonl\\\`
`
```
**Direct mode roadmap.md generation**:
```javascript
const roadmapMd = `# Requirement Roadmap
**Session**: ${sessionId}
**Requirement**: ${requirement}
**Strategy**: direct
**Generated**: ${getUtc8ISOString()}
## Strategy Assessment
- Goal: ${strategy.goal}
- Constraints: ${strategy.constraints.join(', ') || 'None'}
- Assessment basis: ${Object.entries(strategy.uncertainty_factors).map(([k,v]) => `${k}=${v}`).join(', ')}
## Task Sequence
| Group | ID | Title | Type | Dependencies |
|-------|----|-------|------|--------------|
${items.map(t => `| ${t.parallel_group} | ${t.id} | ${t.title} | ${t.type} | ${t.depends_on.length ? t.depends_on.join(', ') : '-'} |`).join('\n')}
## Task Details
${items.map(t => `### ${t.id}: ${t.title}
**Type**: ${t.type} | **Parallel Group**: ${t.parallel_group}
**Scope**: ${t.scope}
**Inputs**: ${t.inputs.length ? t.inputs.join(', ') : 'None (starting task)'}
**Outputs**: ${t.outputs.join(', ')}
**Convergence Criteria**:
${t.convergence.criteria.map(c => \`- ${c}\`).join('\n')}
- **Verification**: ${t.convergence.verification}
- **Definition of Done**: ${t.convergence.definition_of_done}
`).join('\n---\n\n')}
## Next Steps
Each task can be executed independently:
\\\`\\\`\\\`bash
/workflow:lite-plan "${items[0]?.title}: ${items[0]?.scope}"
\\\`\\\`\\\`
Roadmap JSONL file: \\\`${sessionFolder}/roadmap.jsonl\\\`
`
```
**Write and provide post-completion options**:
```javascript
Write(`${sessionFolder}/roadmap.md`, roadmapMd)
// Post-completion options
if (!autoYes) {
AskUserQuestion({
questions: [{
question: "Roadmap generated. Next step:",
header: "Next Step",
multiSelect: false,
options: [
{ label: "Execute First Layer", description: `Launch lite-plan to execute ${items[0].id}` },
{ label: "Create Issue", description: "Create GitHub Issue based on roadmap" },
{ label: "Export Report", description: "Generate standalone shareable roadmap report" },
{ label: "Done", description: "Save roadmap only, execute later" }
]
}]
})
}
```
| Selection | Action |
|-----------|--------|
| Execute First Layer | `Skill(skill="workflow:lite-plan", args="${firstItem.scope}")` |
| Create Issue | `Skill(skill="issue:new", args="...")` |
| Export Report | Copy roadmap.md + roadmap.jsonl to user-specified location |
| Done | Display roadmap file paths, end |
**Success Criteria**:
- User feedback processed (or skipped via autoYes)
- roadmap.md finalized with full tables and convergence details
- roadmap.jsonl final version updated
- Post-completion options provided
## Session Folder Structure
```
.workflow/.req-plan/RPLAN-{slug}-{YYYY-MM-DD}/
├── roadmap.md # Human-readable roadmap
├── roadmap.jsonl # Machine-readable, one self-contained record per line
├── strategy-assessment.json # Strategy assessment result
└── exploration-codebase.json # Codebase context (optional)
```
| File | Phase | Description |
|------|-------|-------------|
| `strategy-assessment.json` | 1 | Uncertainty analysis + mode recommendation + extracted goal/constraints/stakeholders/domain_keywords |
| `roadmap.md` (skeleton) | 1 | Initial skeleton with placeholders, finalized in Phase 4 |
| `exploration-codebase.json` | 2 | Codebase context: relevant modules, patterns, integration points (only when codebase exists) |
| `roadmap.jsonl` | 3 | One self-contained JSON record per line with convergence criteria |
| `roadmap.md` (final) | 4 | Human-readable roadmap with tabular display + convergence details, revised per user feedback |
## JSONL Schema
### Convergence Criteria
Each record's `convergence` object:
| Field | Purpose | Requirement |
|-------|---------|-------------|
| `criteria[]` | List of checkable specific conditions | **Testable** - can be written as assertions or manual steps |
| `verification` | How to verify these conditions | **Executable** - command, script, or explicit steps |
| `definition_of_done` | One-sentence completion definition | **Business language** - non-technical person can judge |
### Progressive Mode (one layer per line)
| Layer | Name | Typical Goal |
|-------|------|--------------|
| L0 | MVP | Minimum viable closed loop, core path end-to-end |
| L1 | Usable | Key user paths refined, basic error handling |
| L2 | Refined | Edge cases, performance, security hardening |
| L3 | Optimized | Advanced features, observability, operations |
**Schema**: `id, name, goal, scope[], excludes[], convergence{}, risk_items[], effort, depends_on[]`
```jsonl
{"id":"L0","name":"MVP","goal":"Minimum viable closed loop","scope":["User registration and login","Basic CRUD"],"excludes":["OAuth","2FA"],"convergence":{"criteria":["End-to-end register→login→operate flow works","Core API returns correct responses"],"verification":"curl/Postman manual testing or smoke test script","definition_of_done":"New user can complete register→login→perform one core operation"},"risk_items":["JWT library selection needs validation"],"effort":"medium","depends_on":[]}
{"id":"L1","name":"Usable","goal":"Complete key user paths","scope":["Password reset","Input validation","Error messages"],"excludes":["Audit logs","Rate limiting"],"convergence":{"criteria":["All form fields have frontend+backend validation","Password reset email can be sent and reset completed","Error scenarios show user-friendly messages"],"verification":"Unit tests cover validation logic + manual test of reset flow","definition_of_done":"Users have a clear recovery path when encountering input errors or forgotten passwords"},"risk_items":[],"effort":"medium","depends_on":["L0"]}
```
**Constraints**: 2-4 layers, L0 must be a self-contained closed loop with no dependencies, each feature belongs to exactly ONE layer (no scope overlap).
### Direct Mode (one task per line)
| Type | Use Case |
|------|----------|
| infrastructure | Data models, configuration, scaffolding |
| feature | API, UI, business logic implementation |
| enhancement | Validation, error handling, edge cases |
| testing | Unit tests, integration tests, E2E |
**Schema**: `id, title, type, scope, inputs[], outputs[], convergence{}, depends_on[], parallel_group`
```jsonl
{"id":"T1","title":"Establish data model","type":"infrastructure","scope":"DB schema + TypeScript types","inputs":[],"outputs":["schema.prisma","types/user.ts"],"convergence":{"criteria":["Migration executes without errors","TypeScript types compile successfully","Fields cover all business entities"],"verification":"npx prisma migrate dev && npx tsc --noEmit","definition_of_done":"Database schema migrates correctly, type definitions can be referenced by other modules"},"depends_on":[],"parallel_group":1}
{"id":"T2","title":"Implement core API","type":"feature","scope":"CRUD endpoints for User","inputs":["schema.prisma","types/user.ts"],"outputs":["routes/user.ts","controllers/user.ts"],"convergence":{"criteria":["GET/POST/PUT/DELETE return correct status codes","Request/response conforms to schema","No N+1 queries"],"verification":"jest --testPathPattern=user.test.ts","definition_of_done":"All User CRUD endpoints pass integration tests"},"depends_on":["T1"],"parallel_group":2}
```
**Constraints**: Inputs must come from preceding task outputs or existing resources, tasks in same parallel_group must be truly independent, no circular dependencies.
## Convergence Quality Requirements
Every `convergence` field MUST satisfy these quality standards:
| Field | Requirement | Bad Example | Good Example |
|-------|-------------|-------------|--------------|
| `criteria[]` | **Testable** - can write assertions or manual steps | `"System works correctly"` | `"API returns 200 and response body contains user_id field"` |
| `verification` | **Executable** - command, script, or clear steps | `"Check it"` | `"jest --testPathPattern=auth && curl -s localhost:3000/health"` |
| `definition_of_done` | **Business language** - non-technical person can judge | `"Code compiles"` | `"New user can complete register→login→perform core operation flow"` |
**NEVER** output vague convergence criteria ("works correctly", "system is normal"). **ALWAYS** ensure:
- criteria are testable (can be written as assertions or manual verification steps)
- verification is executable (commands or explicit steps)
- definition_of_done uses business language (non-technical stakeholders can judge)
## Fallback Decomposition
When normal decomposition fails or produces empty results, use fallback templates:
**Progressive fallback**:
```javascript
[
{
id: "L0", name: "MVP", goal: "Minimum viable closed loop",
scope: ["Core functionality"], excludes: ["Advanced features", "Optimization"],
convergence: {
criteria: ["Core path works end-to-end"],
verification: "Manual test of core flow",
definition_of_done: "User can complete one full core operation"
},
risk_items: ["Tech selection needs validation"], effort: "medium", depends_on: []
},
{
id: "L1", name: "Usable", goal: "Refine key user paths",
scope: ["Error handling", "Input validation"], excludes: ["Performance optimization", "Monitoring"],
convergence: {
criteria: ["All user inputs validated", "Error scenarios show messages"],
verification: "Unit tests + manual error scenario testing",
definition_of_done: "Users have clear guidance and recovery paths when encountering problems"
},
risk_items: [], effort: "medium", depends_on: ["L0"]
}
]
```
**Direct fallback**:
```javascript
[
{
id: "T1", title: "Infrastructure setup", type: "infrastructure",
scope: "Project scaffolding and base configuration",
inputs: [], outputs: ["project-structure"],
convergence: {
criteria: ["Project builds without errors", "Base configuration complete"],
verification: "npm run build (or equivalent build command)",
definition_of_done: "Project foundation ready for feature development"
},
depends_on: [], parallel_group: 1
},
{
id: "T2", title: "Core feature implementation", type: "feature",
scope: "Core business logic",
inputs: ["project-structure"], outputs: ["core-module"],
convergence: {
criteria: ["Core API/functionality callable", "Returns expected results"],
verification: "Run core feature tests",
definition_of_done: "Core business functionality works as expected"
},
depends_on: ["T1"], parallel_group: 2
}
]
```
## Error Handling
| Situation | Action |
|-----------|--------|
| No codebase detected | Normal flow, skip Phase 2 |
| Codebase search fails | Proceed with pure requirement decomposition |
| Circular dependency in records | Fix dependency graph before writing JSONL |
| User feedback timeout | Save current state, display `--continue` recovery command |
| Max feedback rounds (5) | Use current version to generate final artifacts |
| Session folder conflict | Append timestamp suffix |
| JSONL format error | Validate line by line, report problematic lines and fix |
| Quality check fails | Auto-fix if possible, otherwise report to user in feedback loop |
| Decomposition produces empty results | Use fallback decomposition templates |
## Best Practices
1. **Clear requirement description**: Detailed description leads to more accurate uncertainty assessment and decomposition
2. **Validate MVP first**: In progressive mode, L0 should be the minimum verifiable closed loop
3. **Testable convergence**: criteria must be writable as assertions or manual steps; definition_of_done should be judgeable by non-technical stakeholders
4. **Incremental validation**: Use `--continue` to iterate on existing roadmaps
5. **Independently executable**: Each JSONL record should be independently passable to lite-plan for execution
## When to Use
**Use req-plan-with-file when:**
- You need to decompose a large requirement into a progressively executable roadmap
- Unsure where to start, need an MVP strategy
- Need to generate a trackable task sequence for the team
- Requirement involves multiple stages or iterations
**Use lite-plan when:**
- You have a clear single task to execute
- The requirement is already a layer/task from the roadmap
- No layered planning needed
**Use collaborative-plan-with-file when:**
- A single complex task needs multi-agent parallel planning
- Need to analyze the same task from multiple domain perspectives
**Use analyze-with-file when:**
- Need in-depth analysis of a technical problem
- Not about planning execution, but understanding and discussion
---
**Now execute the req-plan-with-file workflow for**: $ARGUMENTS

View File

@@ -50,9 +50,19 @@ async function killProcess(pid: string): Promise<boolean> {
}
/**
* Stop command handler - stops the running CCW dashboard server
* @param {Object} options - Command options
* Clean up React frontend process on the given port
*/
async function cleanupReactFrontend(reactPort: number): Promise<void> {
const reactPid = await findProcessOnPort(reactPort);
if (reactPid) {
console.log(chalk.gray(` Cleaning up React frontend on port ${reactPort}...`));
const killed = await killProcess(reactPid);
if (killed) {
console.log(chalk.green(' React frontend stopped!'));
}
}
}
export async function stopCommand(options: StopOptions): Promise<void> {
const port = Number(options.port) || 3456;
const reactPort = port + 1; // React frontend runs on port + 1
@@ -92,6 +102,7 @@ export async function stopCommand(options: StopOptions): Promise<void> {
await new Promise(resolve => setTimeout(resolve, 500));
if (shutdownResponse && 'ok' in shutdownResponse && shutdownResponse.ok) {
await cleanupReactFrontend(reactPort);
console.log(chalk.green.bold('\n Server stopped successfully!\n'));
process.exit(0);
}
@@ -102,6 +113,7 @@ export async function stopCommand(options: StopOptions): Promise<void> {
}).catch(() => null);
if (!postCheck) {
await cleanupReactFrontend(reactPort);
console.log(chalk.green.bold('\n Server stopped successfully!\n'));
process.exit(0);
}

View File

@@ -110,7 +110,7 @@ export async function viewCommand(options: ViewOptions): Promise<void> {
if (frontend === 'react') {
urlPath = '/react';
}
const url = `http://${browserHost}:${port}${urlPath}/?path=${encodeURIComponent(result.path!)}`;
const url = `http://${browserHost}:${port}${urlPath}/?path=${encodeURIComponent(workspacePath)}`;
if (options.browser !== false) {
console.log(chalk.cyan(' Opening in browser...'));

View File

@@ -0,0 +1,453 @@
{
"summary": {
"timestamp": "2026-02-09 11:26:54",
"source": "src",
"k": 10,
"coarse_k": 100,
"query_count": 7,
"avg_jaccard_topk": 0.39589733329229126,
"avg_rbo_topk": 0.23139636799510202,
"staged": {
"success": 7,
"avg_latency_ms": 32194.107242865222
},
"dense_rerank": {
"success": 7,
"avg_latency_ms": 2643.366857132741
}
},
"comparisons": [
{
"query": "class Config",
"staged": {
"strategy": "staged",
"query": "class Config",
"latency_ms": 43041.41250002384,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\api\\semantic.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\parsers\\factory.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\graph_expander.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\watcher\\file_watcher.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\embedding_manager.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\lsp\\server.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\api\\references.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\__init__.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\chain_search.py"
],
"stage_stats": {
"stage_times": {
"stage1_binary_ms": 9864.638805389404,
"stage2_expand_ms": 13012.29190826416,
"stage3_cluster_ms": 13297.565460205078,
"stage4_rerank_ms": 6821.892261505127
},
"stage_counts": {
"stage1_candidates": 100,
"stage2_expanded": 149,
"stage3_clustered": 20,
"stage4_reranked": 20
}
},
"error": null
},
"dense_rerank": {
"strategy": "dense_rerank",
"query": "class Config",
"latency_ms": 3209.129799991846,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\chunker.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\vector_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\query_parser.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\code_extractor.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\embedding_manager.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\migration_manager.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\registry.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\sqlite_store.py"
],
"stage_stats": null,
"error": null
},
"jaccard_topk": 0.1111111111111111,
"rbo_topk": 0.05429729885142857,
"staged_unique_files_topk": 10,
"dense_unique_files_topk": 10,
"staged_unique_dirs_topk": 8,
"dense_unique_dirs_topk": 4
},
{
"query": "def search",
"staged": {
"strategy": "staged",
"query": "def search",
"latency_ms": 37827.209600031376,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\global_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\ranking.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\ann_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\index_tree.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\graph_expander.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\enrichment.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\vector_store.py"
],
"stage_stats": {
"stage_times": {
"stage1_binary_ms": 531.8794250488281,
"stage2_expand_ms": 27009.481191635132,
"stage3_cluster_ms": 7948.509931564331,
"stage4_rerank_ms": 2268.9380645751953
},
"stage_counts": {
"stage1_candidates": 100,
"stage2_expanded": 101,
"stage3_clustered": 20,
"stage4_reranked": 20
}
},
"error": null
},
"dense_rerank": {
"strategy": "dense_rerank",
"query": "def search",
"latency_ms": 2540.472400009632,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\query_parser.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\vector_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\hybrid_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\index_tree.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\registry.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\code_extractor.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\chain_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\chunker.py"
],
"stage_stats": null,
"error": null
},
"jaccard_topk": 0.26666666666666666,
"rbo_topk": 0.2983708721671428,
"staged_unique_files_topk": 9,
"dense_unique_files_topk": 10,
"staged_unique_dirs_topk": 4,
"dense_unique_dirs_topk": 4
},
{
"query": "LspBridge",
"staged": {
"strategy": "staged",
"query": "LspBridge",
"latency_ms": 24744.686599999666,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\vector_meta_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\graph_expander.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\registry.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\merkle_tree.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\code_extractor.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\sqlite_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\embedding_manager.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\global_index.py"
],
"stage_stats": {
"stage_times": {
"stage1_binary_ms": 517.8542137145996,
"stage2_expand_ms": 12839.622735977173,
"stage3_cluster_ms": 9154.959678649902,
"stage4_rerank_ms": 2160.0701808929443
},
"stage_counts": {
"stage1_candidates": 100,
"stage2_expanded": 100,
"stage3_clustered": 20,
"stage4_reranked": 20
}
},
"error": null
},
"dense_rerank": {
"strategy": "dense_rerank",
"query": "LspBridge",
"latency_ms": 2482.5908999741077,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\vector_meta_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\graph_expander.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\registry.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\hybrid_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\index_tree.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\sqlite_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\code_extractor.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\chunker.py"
],
"stage_stats": null,
"error": null
},
"jaccard_topk": 0.5384615384615384,
"rbo_topk": 0.36639083062285716,
"staged_unique_files_topk": 10,
"dense_unique_files_topk": 10,
"staged_unique_dirs_topk": 4,
"dense_unique_dirs_topk": 4
},
{
"query": "graph expansion",
"staged": {
"strategy": "staged",
"query": "graph expansion",
"latency_ms": 25239.59050002694,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\chain_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\gpu_support.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\code_extractor.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\embedding_manager.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\index_tree.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\vector_meta_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\global_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\hybrid_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\migration_manager.py"
],
"stage_stats": {
"stage_times": {
"stage1_binary_ms": 631.9081783294678,
"stage2_expand_ms": 12570.756196975708,
"stage3_cluster_ms": 9557.724952697754,
"stage4_rerank_ms": 2409.7683429718018
},
"stage_counts": {
"stage1_candidates": 100,
"stage2_expanded": 100,
"stage3_clustered": 20,
"stage4_reranked": 20
}
},
"error": null
},
"dense_rerank": {
"strategy": "dense_rerank",
"query": "graph expansion",
"latency_ms": 2574.1938000023365,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\index_tree.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\migration_manager.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\hybrid_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\chain_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\global_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\registry.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\sqlite_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\vector_store.py"
],
"stage_stats": null,
"error": null
},
"jaccard_topk": 0.42857142857142855,
"rbo_topk": 0.13728894791142857,
"staged_unique_files_topk": 10,
"dense_unique_files_topk": 10,
"staged_unique_dirs_topk": 4,
"dense_unique_dirs_topk": 4
},
{
"query": "clustering strategy",
"staged": {
"strategy": "staged",
"query": "clustering strategy",
"latency_ms": 28572.93939998746,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\ranking.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\registry.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\global_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\embedding_manager.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\chunker.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\sqlite_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\__init__.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\vector_store.py"
],
"stage_stats": {
"stage_times": {
"stage1_binary_ms": 659.6193313598633,
"stage2_expand_ms": 14207.426309585571,
"stage3_cluster_ms": 11513.370037078857,
"stage4_rerank_ms": 2117.546319961548
},
"stage_counts": {
"stage1_candidates": 100,
"stage2_expanded": 100,
"stage3_clustered": 20,
"stage4_reranked": 20
}
},
"error": null
},
"dense_rerank": {
"strategy": "dense_rerank",
"query": "clustering strategy",
"latency_ms": 2536.551799982786,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\index_tree.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\hybrid_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\code_extractor.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\vector_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\__init__.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\gpu_support.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\chain_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\enrichment.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\chunker.py"
],
"stage_stats": null,
"error": null
},
"jaccard_topk": 0.17647058823529413,
"rbo_topk": 0.07116480920571429,
"staged_unique_files_topk": 10,
"dense_unique_files_topk": 10,
"staged_unique_dirs_topk": 4,
"dense_unique_dirs_topk": 4
},
{
"query": "error handling",
"staged": {
"strategy": "staged",
"query": "error handling",
"latency_ms": 23812.726000010967,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\enrichment.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\vector_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\hybrid_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\chain_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\registry.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\chunker.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\__init__.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\index_tree.py"
],
"stage_stats": {
"stage_times": {
"stage1_binary_ms": 475.42428970336914,
"stage2_expand_ms": 12454.935789108276,
"stage3_cluster_ms": 8576.019525527954,
"stage4_rerank_ms": 2265.360116958618
},
"stage_counts": {
"stage1_candidates": 100,
"stage2_expanded": 100,
"stage3_clustered": 20,
"stage4_reranked": 20
}
},
"error": null
},
"dense_rerank": {
"strategy": "dense_rerank",
"query": "error handling",
"latency_ms": 2648.7773999869823,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\__init__.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\registry.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\hybrid_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\index_tree.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\chain_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\chunker.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\code_extractor.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\embedding_manager.py"
],
"stage_stats": null,
"error": null
},
"jaccard_topk": 0.6666666666666666,
"rbo_topk": 0.21230026104857144,
"staged_unique_files_topk": 10,
"dense_unique_files_topk": 10,
"staged_unique_dirs_topk": 4,
"dense_unique_dirs_topk": 4
},
{
"query": "how to parse json",
"staged": {
"strategy": "staged",
"query": "how to parse json",
"latency_ms": 42120.1860999763,
"num_results": 9,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\index_tree.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\code_extractor.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\enrichment.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\registry.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\ranking.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\chain_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\hybrid_search.py"
],
"stage_stats": {
"stage_times": {
"stage1_binary_ms": 570.8920955657959,
"stage2_expand_ms": 30054.06880378723,
"stage3_cluster_ms": 9285.51697731018,
"stage4_rerank_ms": 2142.771005630493
},
"stage_counts": {
"stage1_candidates": 100,
"stage2_expanded": 100,
"stage3_clustered": 20,
"stage4_reranked": 20
}
},
"error": null
},
"dense_rerank": {
"strategy": "dense_rerank",
"query": "how to parse json",
"latency_ms": 2511.8518999814987,
"num_results": 10,
"topk_paths": [
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\cli\\commands.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\chain_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\index_tree.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\code_extractor.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\dir_index.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\hybrid_search.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\search\\ranking.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\chunker.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\storage\\sqlite_store.py",
"d:\\claude_dms3\\codex-lens\\src\\codexlens\\semantic\\ann_index.py"
],
"stage_stats": null,
"error": null
},
"jaccard_topk": 0.5833333333333334,
"rbo_topk": 0.4799615561585714,
"staged_unique_files_topk": 9,
"dense_unique_files_topk": 10,
"staged_unique_dirs_topk": 4,
"dense_unique_dirs_topk": 4
}
]
}

View File

@@ -517,10 +517,11 @@ class LspBridge:
# Parse URI
uri = from_item.get("uri", "")
if uri.startswith("file:///"):
fp = uri[8:] if uri[8:9].isalpha() and uri[9:10] == ":" else uri[7:]
elif uri.startswith("file://"):
fp = uri[7:]
if uri.startswith("file://"):
raw = unquote(uri[7:]) # keep leading slash for Unix paths
if raw.startswith("/") and len(raw) > 2 and raw[2] == ":":
raw = raw[1:]
fp = raw
else:
fp = uri

View File

@@ -21,6 +21,7 @@ import sys
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple
from urllib.parse import unquote, urlparse
logger = logging.getLogger(__name__)
@@ -341,6 +342,7 @@ class StandaloneLspManager:
Returns:
ServerState for the appropriate language server, or None
"""
file_path = self._normalize_file_path(file_path)
language_id = self.get_language_id(file_path)
if not language_id:
logger.debug(f"No language server configured for: {file_path}")
@@ -357,6 +359,43 @@ class StandaloneLspManager:
# Start new server
return await self._start_server(language_id)
def _normalize_file_path(self, file_path_or_uri: str) -> str:
"""Normalize a file path that may be an LSP file URI or URI-path.
LSP responses often contain `file://` URIs with percent-encoding
(e.g. `file:///d%3A/...`). Some code paths may forward the parsed
URI path (`/d%3A/...`) without the scheme. On Windows, `Path(...)`
would interpret that as a root path on the current drive, producing
invalid paths like `D:\\d%3A\\...`.
"""
if not file_path_or_uri:
return file_path_or_uri
raw = str(file_path_or_uri).strip()
if raw.startswith("file:"):
try:
parsed = urlparse(raw)
if parsed.scheme == "file":
raw = unquote(parsed.path)
else:
raw = raw.replace("file:///", "").replace("file://", "")
except Exception:
raw = raw.replace("file:///", "").replace("file://", "")
# Decode percent-encoded segments (e.g. d%3A -> d:)
if "%3a" in raw.lower():
try:
raw = unquote(raw)
except Exception:
pass
# Windows: file URI paths frequently look like "/C:/path"; strip the extra slash.
if raw.startswith("/") and len(raw) > 2 and raw[2] == ":":
raw = raw[1:]
return raw
async def _initialize_server(self, state: ServerState) -> None:
"""Send initialize request and wait for response via the message queue.
@@ -771,6 +810,7 @@ class StandaloneLspManager:
def _to_text_document_identifier(self, file_path: str) -> Dict[str, str]:
"""Create TextDocumentIdentifier from file path."""
file_path = self._normalize_file_path(file_path)
uri = Path(file_path).resolve().as_uri()
return {"uri": uri}
@@ -783,6 +823,7 @@ class StandaloneLspManager:
async def _open_document(self, state: ServerState, file_path: str) -> None:
"""Send textDocument/didOpen notification."""
file_path = self._normalize_file_path(file_path)
resolved_path = Path(file_path).resolve()
try:
@@ -1044,7 +1085,7 @@ class StandaloneLspManager:
"""
# Determine language from item's uri
uri = item.get("uri", "")
file_path = uri.replace("file:///", "").replace("file://", "")
file_path = self._normalize_file_path(uri)
state = await self._get_server(file_path)
if not state:
@@ -1075,7 +1116,7 @@ class StandaloneLspManager:
"""
# Determine language from item's uri
uri = item.get("uri", "")
file_path = uri.replace("file:///", "").replace("file://", "")
file_path = self._normalize_file_path(uri)
state = await self._get_server(file_path)
if not state:

View File

@@ -0,0 +1,48 @@
"""Tests for StandaloneLspManager path normalization (Windows URI handling)."""
from __future__ import annotations
import platform
from codexlens.lsp.standalone_manager import StandaloneLspManager
def test_normalize_file_uri_percent_encoded_windows_drive() -> None:
if platform.system() != "Windows":
return
manager = StandaloneLspManager(workspace_root="D:/Claude_dms3/codex-lens")
raw = "file:///d%3A/Claude_dms3/codex-lens/src/codexlens/lsp/standalone_manager.py"
normalized = manager._normalize_file_path(raw)
assert normalized.lower().startswith("d:/")
assert "%3a" not in normalized.lower()
assert "d%3a" not in normalized.lower()
assert "/d%3a" not in normalized.lower()
def test_normalize_uri_path_percent_encoded_windows_drive() -> None:
if platform.system() != "Windows":
return
manager = StandaloneLspManager(workspace_root="D:/Claude_dms3/codex-lens")
raw = "/d%3A/Claude_dms3/codex-lens/src/codexlens/lsp/standalone_manager.py"
normalized = manager._normalize_file_path(raw)
assert normalized.lower().startswith("d:/")
assert "%3a" not in normalized.lower()
def test_normalize_plain_windows_path_is_unchanged() -> None:
if platform.system() != "Windows":
return
manager = StandaloneLspManager(workspace_root="D:/Claude_dms3/codex-lens")
raw = r"D:\Claude_dms3\codex-lens\src\codexlens\lsp\standalone_manager.py"
normalized = manager._normalize_file_path(raw)
assert normalized == raw