revert: Restore lite-execute and schema to 196b805, keep 50k context protection

- Revert cost-aware batching and multi-agent parallelism in lite-execute
- Remove execution_group and task complexity from schema
- Remove execution_group rules from lite-plan
- Keep 50k context threshold protection in lite-plan

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
catlog22
2025-11-28 15:34:14 +08:00
parent 96dd9bef5f
commit 3e6ed5e4c3
3 changed files with 76 additions and 124 deletions

View File

@@ -149,14 +149,14 @@ Input Parsing:
Execution: Execution:
├─ Step 1: Initialize result tracking (previousExecutionResults = []) ├─ Step 1: Initialize result tracking (previousExecutionResults = [])
├─ Step 2: Task grouping & batch creation (cost-aware) ├─ Step 2: Task grouping & batch creation
│ ├─ Build hybrid dependencies (explicit depends_on + file conflicts via modification_points) │ ├─ Extract explicit depends_on (no file/keyword inference)
│ ├─ Phase 1: Group by execution_group (explicit parallel groups) │ ├─ Group: independent tasks → single parallel batch (maximize utilization)
│ ├─ Phase 2: Cost-aware batching (MAX_BATCH_COST=8, Low=1/Medium=2/High=4) │ ├─ Group: dependent tasks → sequential phases (respect dependencies)
│ └─ Create TodoWrite list for batches │ └─ Create TodoWrite list for batches
├─ Step 3: Launch execution ├─ Step 3: Launch execution
│ ├─ Parallel batches: ⚡ concurrent via multiple tool calls │ ├─ Phase 1: All independent tasks (⚡ single batch, concurrent)
│ └─ Sequential batches: → one by one │ └─ Phase 2+: Dependent tasks by dependency order
├─ Step 4: Track progress (TodoWrite updates per batch) ├─ Step 4: Track progress (TodoWrite updates per batch)
└─ Step 5: Code review (if codeReviewTool ≠ "Skip") └─ Step 5: Code review (if codeReviewTool ≠ "Skip")
@@ -181,96 +181,67 @@ previousExecutionResults = []
**Dependency Analysis & Grouping Algorithm**: **Dependency Analysis & Grouping Algorithm**:
```javascript ```javascript
const MAX_BATCH_COST = 8 // Workload limit per batch (tunable) // Use explicit depends_on from plan.json (no inference from file/keywords)
function extractDependencies(tasks) {
// Task cost based on complexity (Low=1, Medium=2, High=4)
function calculateTaskCost(task) {
const costs = { Low: 1, Medium: 2, High: 4 }
return costs[task.complexity] || 1
}
// Build hybrid dependencies: explicit depends_on + implicit file conflicts
function buildDependencies(tasks) {
const taskIdToIndex = {} const taskIdToIndex = {}
tasks.forEach((t, i) => { taskIdToIndex[t.id] = i }) tasks.forEach((t, i) => { taskIdToIndex[t.id] = i })
const fileOwner = {} // Track which task last modified each file
return tasks.map((task, i) => { return tasks.map((task, i) => {
const deps = new Set() // Only use explicit depends_on from plan.json
const deps = (task.depends_on || [])
// 1. Explicit depends_on .map(depId => taskIdToIndex[depId])
;(task.depends_on || []).forEach(depId => { .filter(idx => idx !== undefined && idx < i)
const idx = taskIdToIndex[depId] return { ...task, taskIndex: i, dependencies: deps }
if (idx !== undefined && idx < i) deps.add(idx)
})
// 2. Implicit file conflicts via modification_points
;(task.modification_points || []).forEach(mp => {
if (fileOwner[mp.file] !== undefined && fileOwner[mp.file] !== i) {
deps.add(fileOwner[mp.file])
}
fileOwner[mp.file] = i
})
return { ...task, taskIndex: i, dependencies: [...deps], cost: calculateTaskCost(task) }
}) })
} }
// Group into cost-aware batches with execution_group priority // Group into batches: maximize parallel execution
function createExecutionCalls(tasks, executionMethod) { function createExecutionCalls(tasks, executionMethod) {
const tasksWithDeps = buildDependencies(tasks) const tasksWithDeps = extractDependencies(tasks)
const processed = new Set() const processed = new Set()
const calls = [] const calls = []
let parallelIdx = 1, sequentialIdx = 1
// Phase 1: Group by execution_group (explicit parallel groups) // Phase 1: All independent tasks → single parallel batch (maximize utilization)
const groups = {} const independentTasks = tasksWithDeps.filter(t => t.dependencies.length === 0)
tasksWithDeps.forEach(t => { if (independentTasks.length > 0) {
if (t.execution_group) { independentTasks.forEach(t => processed.add(t.taskIndex))
groups[t.execution_group] = groups[t.execution_group] || []
groups[t.execution_group].push(t)
}
})
Object.entries(groups).forEach(([groupId, groupTasks]) => {
groupTasks.forEach(t => processed.add(t.taskIndex))
calls.push({ calls.push({
method: executionMethod, method: executionMethod,
executionType: "parallel", executionType: "parallel",
groupId: `P${parallelIdx++}`, groupId: "P1",
taskSummary: groupTasks.map(t => t.title).join(' | '), taskSummary: independentTasks.map(t => t.title).join(' | '),
tasks: groupTasks tasks: independentTasks
}) })
}) }
// Phase 2: Process remaining by dependency order with cost-aware batching // Phase 2: Dependent tasks → sequential batches (respect dependencies)
let sequentialIndex = 1
let remaining = tasksWithDeps.filter(t => !processed.has(t.taskIndex)) let remaining = tasksWithDeps.filter(t => !processed.has(t.taskIndex))
while (remaining.length > 0) { while (remaining.length > 0) {
const ready = remaining.filter(t => t.dependencies.every(d => processed.has(d))) // Find tasks whose dependencies are all satisfied
if (ready.length === 0) { ready.push(...remaining) } // Break circular deps const ready = remaining.filter(t =>
t.dependencies.every(d => processed.has(d))
)
// Cost-aware batch creation if (ready.length === 0) {
let batch = [], batchCost = 0 console.warn('Circular dependency detected, forcing remaining tasks')
const stillReady = [...ready] ready.push(...remaining)
while (stillReady.length > 0) {
const idx = stillReady.findIndex(t => batchCost + t.cost <= MAX_BATCH_COST)
if (idx === -1) break
const task = stillReady.splice(idx, 1)[0]
batch.push(task)
batchCost += task.cost
} }
if (batch.length === 0) batch = [ready[0]] // At least one task
batch.forEach(t => processed.add(t.taskIndex)) // Group ready tasks (can run in parallel within this phase)
ready.forEach(t => processed.add(t.taskIndex))
calls.push({ calls.push({
method: executionMethod, method: executionMethod,
executionType: batch.length > 1 ? "parallel" : "sequential", executionType: ready.length > 1 ? "parallel" : "sequential",
groupId: batch.length > 1 ? `P${parallelIdx++}` : `S${sequentialIdx++}`, groupId: ready.length > 1 ? `P${calls.length + 1}` : `S${sequentialIndex++}`,
taskSummary: batch.map(t => t.title).join(batch.length > 1 ? ' | ' : ' → '), taskSummary: ready.map(t => t.title).join(ready.length > 1 ? ' | ' : ' → '),
tasks: batch tasks: ready
}) })
remaining = remaining.filter(t => !processed.has(t.taskIndex)) remaining = remaining.filter(t => !processed.has(t.taskIndex))
} }
return calls return calls
} }
@@ -287,30 +258,14 @@ TodoWrite({
### Step 3: Launch Execution ### Step 3: Launch Execution
**Execution Flow**: Parallel batches concurrently (multiple agents) → Sequential batches in order **Execution Flow**: Parallel batches concurrently → Sequential batches in order
**Unified executeBatch dispatcher**:
```javascript
// Dispatch batch to appropriate executor (Agent or Codex)
function executeBatch(batch) {
if (batch.method === "Agent") {
return executeBatchAgent(batch) // See Option A below
} else {
return executeBatchCodex(batch) // See Option B below
}
}
```
**Execution orchestration**:
```javascript ```javascript
const parallel = executionCalls.filter(c => c.executionType === "parallel") const parallel = executionCalls.filter(c => c.executionType === "parallel")
const sequential = executionCalls.filter(c => c.executionType === "sequential") const sequential = executionCalls.filter(c => c.executionType === "sequential")
// Phase 1: Launch all parallel batches (MULTIPLE CONCURRENT AGENTS) // Phase 1: Launch all parallel batches (single message with multiple tool calls)
// Each batch → separate agent instance → true parallelism
if (parallel.length > 0) { if (parallel.length > 0) {
TodoWrite({ todos: executionCalls.map(c => ({ status: c.executionType === "parallel" ? "in_progress" : "pending" })) }) TodoWrite({ todos: executionCalls.map(c => ({ status: c.executionType === "parallel" ? "in_progress" : "pending" })) })
// Promise.all dispatches each batch to its own agent concurrently
parallelResults = await Promise.all(parallel.map(c => executeBatch(c))) parallelResults = await Promise.all(parallel.map(c => executeBatch(c)))
previousExecutionResults.push(...parallelResults) previousExecutionResults.push(...parallelResults)
TodoWrite({ todos: executionCalls.map(c => ({ status: parallel.includes(c) ? "completed" : "pending" })) }) TodoWrite({ todos: executionCalls.map(c => ({ status: parallel.includes(c) ? "completed" : "pending" })) })
@@ -353,12 +308,10 @@ ${task.acceptance.map((criterion, i) => `${i + 1}. ${criterion}`).join('\n')}
` `
} }
// executeBatch(batch) - called for each parallel/sequential batch Task(
function executeBatchAgent(batch) { subagent_type="code-developer",
return Task( description="Implement planned tasks",
subagent_type="code-developer", prompt=`
description=`Implement batch ${batch.groupId}: ${batch.taskSummary}`,
prompt=`
${originalUserInput ? `## Original User Request\n${originalUserInput}\n\n` : ''} ${originalUserInput ? `## Original User Request\n${originalUserInput}\n\n` : ''}
## Implementation Plan ## Implementation Plan
@@ -366,8 +319,8 @@ function executeBatchAgent(batch) {
**Summary**: ${planObject.summary} **Summary**: ${planObject.summary}
**Approach**: ${planObject.approach} **Approach**: ${planObject.approach}
## Batch ${batch.groupId}: Task Breakdown (${batch.tasks.length} tasks) ## Task Breakdown (${planObject.tasks.length} tasks)
${batch.tasks.map((task, i) => formatTaskForAgent(task, i)).join('\n')} ${planObject.tasks.map((task, i) => formatTaskForAgent(task, i)).join('\n')}
${previousExecutionResults.length > 0 ? `\n## Previous Execution Results\n${previousExecutionResults.map(result => ` ${previousExecutionResults.length > 0 ? `\n## Previous Execution Results\n${previousExecutionResults.map(result => `
[${result.executionId}] ${result.status} [${result.executionId}] ${result.status}
@@ -408,11 +361,10 @@ ${result.notes ? `Notes: ${result.notes}` : ''}
Read exploration files for comprehensive context from multiple angles.` : ''} Read exploration files for comprehensive context from multiple angles.` : ''}
## Requirements ## Requirements
MUST complete ALL ${batch.tasks.length} tasks in batch ${batch.groupId}. MUST complete ALL ${planObject.tasks.length} tasks listed above in this single execution.
Return only after all tasks in this batch are fully implemented and tested. Return only after all tasks are fully implemented and tested.
` `
) )
}
``` ```
**Result Collection**: After completion, collect result following `executionResult` structure (see Data Structures section) **Result Collection**: After completion, collect result following `executionResult` structure (see Data Structures section)
@@ -444,11 +396,7 @@ ${task.acceptance.map((criterion, i) => ` - ${criterion}`).join('\n')}
` `
} }
// executeBatch(batch) - called for each parallel/sequential batch codex --full-auto exec "
const timeoutByComplexity = { "Low": 2400000, "Medium": 3600000, "High": 6000000 }
function executeBatchCodex(batch) {
return Bash(`codex --full-auto exec "
${originalUserInput ? `## Original User Request\n${originalUserInput}\n\n` : ''} ${originalUserInput ? `## Original User Request\n${originalUserInput}\n\n` : ''}
## Implementation Plan ## Implementation Plan
@@ -456,8 +404,8 @@ ${originalUserInput ? `## Original User Request\n${originalUserInput}\n\n` : ''}
TASK: ${planObject.summary} TASK: ${planObject.summary}
APPROACH: ${planObject.approach} APPROACH: ${planObject.approach}
### Batch ${batch.groupId}: Task Breakdown (${batch.tasks.length} tasks) ### Task Breakdown (${planObject.tasks.length} tasks)
${batch.tasks.map((task, i) => formatTaskForCodex(task, i)).join('\n')} ${planObject.tasks.map((task, i) => formatTaskForCodex(task, i)).join('\n')}
${previousExecutionResults.length > 0 ? `\n### Previous Execution Results\n${previousExecutionResults.map(result => ` ${previousExecutionResults.length > 0 ? `\n### Previous Execution Results\n${previousExecutionResults.map(result => `
[${result.executionId}] ${result.status} [${result.executionId}] ${result.status}
@@ -501,15 +449,30 @@ Read exploration files for comprehensive architectural, pattern, and constraint
` : ''} ` : ''}
## Requirements ## Requirements
MUST complete ALL ${batch.tasks.length} tasks in batch ${batch.groupId}. MUST complete ALL ${planObject.tasks.length} tasks listed above in this single execution.
Return only after all tasks in this batch are fully implemented and tested. Return only after all tasks are fully implemented and tested.
Complexity: ${planObject.complexity} Complexity: ${planObject.complexity}
" --skip-git-repo-check -s danger-full-access`, { timeout: timeoutByComplexity[planObject.complexity] || 3600000 }) " --skip-git-repo-check -s danger-full-access
}
``` ```
**Note**: Timeout based on complexity (Low=40min, Medium=60min, High=100min) is handled in `executeBatchCodex`. **Execution with tracking**:
```javascript
// Launch CLI in foreground (NOT background)
// Timeout based on complexity: Low=40min, Medium=60min, High=100min
const timeoutByComplexity = {
"Low": 2400000, // 40 minutes
"Medium": 3600000, // 60 minutes
"High": 6000000 // 100 minutes
}
bash_result = Bash(
command=cli_command,
timeout=timeoutByComplexity[planObject.complexity] || 3600000
)
// Update TodoWrite when execution completes
```
**Result Collection**: After completion, analyze output and collect result following `executionResult` structure **Result Collection**: After completion, analyze output and collect result following `executionResult` structure
@@ -576,8 +539,8 @@ codex --full-auto exec "[Verify plan acceptance criteria at ${plan.json}]" --ski
## Best Practices ## Best Practices
**Input Modes**: In-memory (lite-plan), prompt (standalone), file (JSON/text) **Input Modes**: In-memory (lite-plan), prompt (standalone), file (JSON/text)
**Task Grouping**: Hybrid dependencies (explicit depends_on + file conflicts) + execution_group priority + cost-aware batching (MAX_BATCH_COST=8) **Task Grouping**: Based on explicit depends_on only; independent tasks run in single parallel batch
**Execution**: Parallel batches via single Claude message with multiple tool calls; cost balances workload (Low=1, Medium=2, High=4) **Execution**: All independent tasks launch concurrently via single Claude message with multiple tool calls
## Error Handling ## Error Handling

View File

@@ -415,8 +415,6 @@ Generate plan.json with:
3. **Substantial tasks**: Each task should represent 15-60 minutes of work 3. **Substantial tasks**: Each task should represent 15-60 minutes of work
4. **True dependencies only**: Only use depends_on when Task B cannot start without Task A's output 4. **True dependencies only**: Only use depends_on when Task B cannot start without Task A's output
5. **Prefer parallel**: Most tasks should be independent (no depends_on) 5. **Prefer parallel**: Most tasks should be independent (no depends_on)
6. **Explicit parallel groups**: Assign same `execution_group` ID (e.g., "group-1") to tasks that can run concurrently; null for others
7. **Task complexity**: Set `complexity` (Low/Medium/High) for workload balancing - executor uses cost units (1/2/4)
## Execution ## Execution
1. Read ALL exploration files for comprehensive context 1. Read ALL exploration files for comprehensive context

View File

@@ -118,15 +118,6 @@
"pattern": "^T[0-9]+$" "pattern": "^T[0-9]+$"
}, },
"description": "Task IDs this task depends on (e.g., ['T1', 'T2'])" "description": "Task IDs this task depends on (e.g., ['T1', 'T2'])"
},
"execution_group": {
"type": ["string", "null"],
"description": "Parallel execution group ID. Tasks with same group ID run concurrently. null = use depends_on logic"
},
"complexity": {
"type": "string",
"enum": ["Low", "Medium", "High"],
"description": "Task complexity for workload balancing (Low=1, Medium=2, High=4 cost units)"
} }
} }
}, },