opencode-swarm 7.14.0 → 7.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/cli/index.js +2114 -553
- package/dist/commands/close.d.ts +14 -1
- package/dist/commands/registry.d.ts +3 -3
- package/dist/commands/turbo.d.ts +4 -4
- package/dist/config/constants.d.ts +12 -1
- package/dist/config/index.d.ts +2 -2
- package/dist/config/schema.d.ts +116 -0
- package/dist/index.js +5288 -2128
- package/dist/parallel/file-locks.d.ts +50 -2
- package/dist/services/skill-improver.d.ts +1 -0
- package/dist/services/status-service.d.ts +29 -0
- package/dist/state.d.ts +17 -0
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/lean-turbo-acquire-locks.d.ts +36 -0
- package/dist/tools/lean-turbo-plan-lanes.d.ts +35 -0
- package/dist/tools/lean-turbo-review.d.ts +34 -0
- package/dist/tools/lean-turbo-run-phase.d.ts +44 -0
- package/dist/tools/lean-turbo-runner-status.d.ts +36 -0
- package/dist/tools/lean-turbo-status.d.ts +44 -0
- package/dist/tools/tool-names.d.ts +1 -1
- package/dist/tools/update-task-status.d.ts +7 -4
- package/dist/turbo/lean/conflicts.d.ts +100 -0
- package/dist/turbo/lean/evidence.d.ts +91 -0
- package/dist/turbo/lean/index.d.ts +27 -0
- package/dist/turbo/lean/integration.d.ts +137 -0
- package/dist/turbo/lean/phase-ready.d.ts +105 -0
- package/dist/turbo/lean/planner.d.ts +115 -0
- package/dist/turbo/lean/reviewer.d.ts +124 -0
- package/dist/turbo/lean/risk.d.ts +47 -0
- package/dist/turbo/lean/runner.d.ts +322 -0
- package/dist/turbo/lean/state.d.ts +61 -0
- package/dist/turbo/lean/task-completion.d.ts +53 -0
- package/package.json +1 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lean Turbo Module — barrel export.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports all public symbols from sub-modules so consumers can import
|
|
5
|
+
* everything from a single path:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { LeanTurboRunner, planLeanTurboLanes, LeanTurboLane, ... } from './turbo/lean';
|
|
9
|
+
* ```
|
|
10
|
+
*/
|
|
11
|
+
export type { LaneDispatchResult, LaneResult, LaneStatus, LeanTurboPhaseResult, } from './runner';
|
|
12
|
+
export { LeanTurboRunner } from './runner';
|
|
13
|
+
export type { LeanTurboLanePlan, PlanPhase, PlanTask, } from './planner';
|
|
14
|
+
export { GLOBAL_FILES_LIST, isGlobalFile, isPathSafe, isProtectedPath, normalizePath, PROTECTED_PATTERNS_LIST, pathsConflict, planLeanTurboLanes, readTaskScopes, } from './planner';
|
|
15
|
+
export type { LeanTurboCounters, LeanTurboDegradedTask, LeanTurboLane, LeanTurboPersistedState, LeanTurboRunState, LeanTurboStatus, } from './state';
|
|
16
|
+
export { emptyCounters, emptyPersisted, emptyRunState, isLeanTurboRunActive, isStateUnreadable, loadLeanTurboRunState, pauseLeanTurboRun, repairStateUnreadable, resetLeanTurboRun, saveLeanTurboRunState, } from './state';
|
|
17
|
+
export type { LeanTurboConfig } from '../../config/schema';
|
|
18
|
+
export type { LaneEvidence, PhaseEvidence } from './evidence';
|
|
19
|
+
export { listLaneEvidence, readLaneEvidence, readPhaseEvidence, writeLaneEvidence, writePhaseEvidence, } from './evidence';
|
|
20
|
+
export type { LeanTurboPhaseReadyConfig, LeanTurboPhaseReadyResult, } from './phase-ready';
|
|
21
|
+
export { verifyLeanTurboPhaseReady } from './phase-ready';
|
|
22
|
+
export type { TaskRiskAssessment, TaskRiskCategory } from './risk';
|
|
23
|
+
export { assessTaskRisk } from './risk';
|
|
24
|
+
export type { LeanTurboPhaseCriticConfig, PhaseCriticResult, } from './integration';
|
|
25
|
+
export { dispatchPhaseCritic } from './integration';
|
|
26
|
+
export type { LeanTurboPhaseReviewerConfig, PhaseReviewerResult, } from './reviewer';
|
|
27
|
+
export { dispatchPhaseReviewer } from './reviewer';
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { type LaneEvidence, listLaneEvidence, readPhaseEvidence } from './evidence';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for phase critic dispatch.
|
|
4
|
+
*/
|
|
5
|
+
export interface LeanTurboPhaseCriticConfig {
|
|
6
|
+
/**
|
|
7
|
+
* Override the critic agent name.
|
|
8
|
+
* Default: derived from `generatedAgentNames` via `{swarmId}_critic` pattern
|
|
9
|
+
* when a swarm has multiple critics, or `critic` for the default swarm.
|
|
10
|
+
*/
|
|
11
|
+
criticAgent?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Timeout in milliseconds for the critic dispatch.
|
|
14
|
+
* Default: no timeout (critic is awaited indefinitely).
|
|
15
|
+
*/
|
|
16
|
+
timeoutMs?: number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Result of a phase critic dispatch.
|
|
20
|
+
*/
|
|
21
|
+
export interface PhaseCriticResult {
|
|
22
|
+
/** Critic verdict */
|
|
23
|
+
verdict: 'APPROVED' | 'NEEDS_REVISION' | 'REJECTED' | 'ESCALATE_TO_HUMAN';
|
|
24
|
+
/** Human-readable reason for the verdict */
|
|
25
|
+
reason?: string;
|
|
26
|
+
/** Path to the persisted critic evidence file */
|
|
27
|
+
evidencePath: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Reviewer evidence record (lean-turbo-reviewer.json).
|
|
31
|
+
*/
|
|
32
|
+
interface ReviewerEvidence {
|
|
33
|
+
phase: number;
|
|
34
|
+
verdict: 'APPROVED' | 'NEEDS_REVISION' | 'REJECTED';
|
|
35
|
+
reason?: string | null;
|
|
36
|
+
timestamp: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Resolves the default critic agent name from the generated agent names.
|
|
40
|
+
*
|
|
41
|
+
* Uses the `{swarmId}_critic` pattern for named swarms and bare `critic`
|
|
42
|
+
* for the default swarm. Follows the same suffix-based resolution used by
|
|
43
|
+
* `getCanonicalAgentRole` so that arbitrary swarm prefixes are handled correctly.
|
|
44
|
+
*/
|
|
45
|
+
declare function resolveDefaultCriticAgent(generatedAgentNames: string[]): string;
|
|
46
|
+
/**
|
|
47
|
+
* Reads the reviewer evidence from .swarm/evidence/{phase}/lean-turbo-reviewer.json.
|
|
48
|
+
*
|
|
49
|
+
* @returns Parsed reviewer evidence, or null if file does not exist or is invalid
|
|
50
|
+
*/
|
|
51
|
+
declare function readReviewerEvidence(directory: string, phase: number): Promise<ReviewerEvidence | null>;
|
|
52
|
+
/**
|
|
53
|
+
* Compiles a structured boundary review package from reviewer and phase evidence.
|
|
54
|
+
*/
|
|
55
|
+
interface CriticPackage {
|
|
56
|
+
phase: number;
|
|
57
|
+
sessionID: string;
|
|
58
|
+
/** Reviewer verdict if available */
|
|
59
|
+
reviewerVerdict?: 'APPROVED' | 'NEEDS_REVISION' | 'REJECTED';
|
|
60
|
+
/** Whether reviewer evidence was missing or invalid */
|
|
61
|
+
reviewerMissing: boolean;
|
|
62
|
+
/** Safety concerns noted during compilation */
|
|
63
|
+
safetyConcerns: string[];
|
|
64
|
+
laneSummaries: Array<{
|
|
65
|
+
laneId: string;
|
|
66
|
+
taskIds: string[];
|
|
67
|
+
files: string[];
|
|
68
|
+
status: LaneEvidence['status'];
|
|
69
|
+
agent?: string;
|
|
70
|
+
}>;
|
|
71
|
+
filesChanged: string[];
|
|
72
|
+
testResults: {
|
|
73
|
+
totalLanes: number;
|
|
74
|
+
completedLanes: number;
|
|
75
|
+
failedLanes: number;
|
|
76
|
+
};
|
|
77
|
+
degradationSummary: {
|
|
78
|
+
totalDegraded: number;
|
|
79
|
+
resolvedDegraded: number;
|
|
80
|
+
pendingDegraded: number;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
declare function compileCriticPackage(directory: string, phase: number, sessionID: string): Promise<CriticPackage>;
|
|
84
|
+
/**
|
|
85
|
+
* Parses a critic verdict from the agent's text response.
|
|
86
|
+
*
|
|
87
|
+
* Looks for a verdict marker line: `VERDICT: APPROVED`, `VERDICT: NEEDS_REVISION`,
|
|
88
|
+
* `VERDICT: REJECTED`, or `VERDICT: ESCALATE_TO_HUMAN` (case-insensitive).
|
|
89
|
+
* Returns null if no marker is found.
|
|
90
|
+
*
|
|
91
|
+
* The optional reason is extracted from a `REASON:` marker line that follows
|
|
92
|
+
* the verdict marker on a subsequent line.
|
|
93
|
+
*/
|
|
94
|
+
declare function parseCriticVerdict(responseText: string): {
|
|
95
|
+
verdict: 'APPROVED' | 'NEEDS_REVISION' | 'REJECTED' | 'ESCALATE_TO_HUMAN';
|
|
96
|
+
reason?: string;
|
|
97
|
+
} | null;
|
|
98
|
+
/**
|
|
99
|
+
* Writes the critic verdict to the evidence file.
|
|
100
|
+
* Uses atomic write (temp file + rename) to prevent partial-file artifacts.
|
|
101
|
+
*/
|
|
102
|
+
declare function writeCriticEvidence(directory: string, phase: number, verdict: 'APPROVED' | 'NEEDS_REVISION' | 'REJECTED' | 'ESCALATE_TO_HUMAN', reason?: string): Promise<string>;
|
|
103
|
+
/**
|
|
104
|
+
* Test-only dependency-injection seam.
|
|
105
|
+
* Allows tests to intercept critic dispatch without mock.module leakage.
|
|
106
|
+
*/
|
|
107
|
+
export declare const _internals: {
|
|
108
|
+
compileCriticPackage: typeof compileCriticPackage;
|
|
109
|
+
parseCriticVerdict: typeof parseCriticVerdict;
|
|
110
|
+
writeCriticEvidence: typeof writeCriticEvidence;
|
|
111
|
+
dispatchCriticAgent: (directory: string, pkg: CriticPackage, agentName: string, timeoutMs: number) => Promise<string>;
|
|
112
|
+
resolveDefaultCriticAgent: typeof resolveDefaultCriticAgent;
|
|
113
|
+
readReviewerEvidence: typeof readReviewerEvidence;
|
|
114
|
+
listLaneEvidence: typeof listLaneEvidence;
|
|
115
|
+
readPhaseEvidence: typeof readPhaseEvidence;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Dispatch a read-only critic agent to evaluate boundary conditions for a completed Lean Turbo phase.
|
|
119
|
+
*
|
|
120
|
+
* Steps:
|
|
121
|
+
* 1. Read reviewer evidence from `.swarm/evidence/{phase}/lean-turbo-reviewer.json`
|
|
122
|
+
* 2. Read lane and phase evidence from `.swarm/evidence/{phase}/lean-turbo/`
|
|
123
|
+
* 3. Compile a boundary review package with safety concerns noted
|
|
124
|
+
* 4. Dispatch a read-only critic agent (tools: write=false, edit=false, patch=false)
|
|
125
|
+
* 5. Parse the verdict from the agent's response
|
|
126
|
+
* 6. Write the verdict to `.swarm/evidence/{phase}/lean-turbo-critic.json`
|
|
127
|
+
* 7. Return the result
|
|
128
|
+
*
|
|
129
|
+
* @param directory - Project root directory
|
|
130
|
+
* @param phase - Phase number being reviewed
|
|
131
|
+
* @param sessionID - Lean Turbo session ID
|
|
132
|
+
* @param config - Optional configuration overrides
|
|
133
|
+
* @returns PhaseCriticResult with verdict, optional reason, and evidence path
|
|
134
|
+
* @throws Error if dispatch fails or response cannot be parsed (fail-closed)
|
|
135
|
+
*/
|
|
136
|
+
export declare function dispatchPhaseCritic(directory: string, phase: number, sessionID: string, config?: LeanTurboPhaseCriticConfig): Promise<PhaseCriticResult>;
|
|
137
|
+
export {};
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lean Turbo Phase Boundary Gate Verification.
|
|
3
|
+
*
|
|
4
|
+
* Provides a synchronous helper to check whether a Lean Turbo phase is ready
|
|
5
|
+
* to advance to the next phase — i.e., whether all gates (lane completion,
|
|
6
|
+
* lock clearance, degraded task resolution, reviewer/critic approval) have
|
|
7
|
+
* been satisfied.
|
|
8
|
+
*/
|
|
9
|
+
import { listActiveLocks } from '../../parallel/file-locks';
|
|
10
|
+
import { listLaneEvidence } from './evidence';
|
|
11
|
+
import { readPersisted } from './state';
|
|
12
|
+
/**
|
|
13
|
+
* Configuration options for phase gate checks.
|
|
14
|
+
* Passed optionally so callers can control whether reviewer/critic checks run.
|
|
15
|
+
*/
|
|
16
|
+
export interface LeanTurboPhaseReadyConfig {
|
|
17
|
+
phase_reviewer?: boolean;
|
|
18
|
+
phase_critic?: boolean;
|
|
19
|
+
integrated_diff_required?: boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Result of the Lean Turbo phase readiness check.
|
|
23
|
+
*/
|
|
24
|
+
export interface LeanTurboPhaseReadyResult {
|
|
25
|
+
ok: boolean;
|
|
26
|
+
reason: string;
|
|
27
|
+
evidence?: {
|
|
28
|
+
lanes: string[];
|
|
29
|
+
degradedTasks: string[];
|
|
30
|
+
reviewerVerdict?: string;
|
|
31
|
+
criticVerdict?: string;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Shape of the plan.json file read by _internals.readPlanJson.
|
|
36
|
+
*/
|
|
37
|
+
interface PlanJson {
|
|
38
|
+
phases: Array<{
|
|
39
|
+
id?: number;
|
|
40
|
+
tasks: Array<{
|
|
41
|
+
id: string;
|
|
42
|
+
status: string;
|
|
43
|
+
}>;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Shape of the reviewer evidence file (lean-turbo-reviewer.json).
|
|
48
|
+
*/
|
|
49
|
+
interface ReviewerEvidence {
|
|
50
|
+
phase: number;
|
|
51
|
+
verdict: 'APPROVED' | 'NEEDS_REVISION' | 'REJECTED';
|
|
52
|
+
reason?: string | null;
|
|
53
|
+
timestamp: string;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Shape of the critic evidence file (lean-turbo-critic.json).
|
|
57
|
+
*/
|
|
58
|
+
interface CriticEvidence {
|
|
59
|
+
phase: number;
|
|
60
|
+
verdict: 'APPROVED' | 'NEEDS_REVISION' | 'REJECTED' | 'ESCALATE_TO_HUMAN';
|
|
61
|
+
reason?: string | null;
|
|
62
|
+
timestamp: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Test-only seam. Replaces the lock-list and state-load functions so tests
|
|
66
|
+
* can inject mock results without touching the real `file-locks` module or
|
|
67
|
+
* the module-level `stateUnreadable` flag used by `loadLeanTurboRunState`.
|
|
68
|
+
*/
|
|
69
|
+
export declare const _internals: {
|
|
70
|
+
listActiveLocks: typeof listActiveLocks;
|
|
71
|
+
readPersisted: typeof readPersisted;
|
|
72
|
+
readPlanJson: (dir: string) => PlanJson | null;
|
|
73
|
+
readReviewerEvidence: (dir: string, phase: number) => ReviewerEvidence | null;
|
|
74
|
+
readCriticEvidence: (dir: string, phase: number) => CriticEvidence | null;
|
|
75
|
+
listLaneEvidence: typeof listLaneEvidence;
|
|
76
|
+
listLaneEvidenceSync: (dir: string, phase: number) => string[];
|
|
77
|
+
verifyLeanTurboPhaseReady: typeof verifyLeanTurboPhaseReady;
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Synchronously verify whether a Lean Turbo phase is ready to advance.
|
|
81
|
+
*
|
|
82
|
+
* Checks are performed in fail-fast order:
|
|
83
|
+
* 1. Read `.swarm/turbo-state.json` via readPersisted → null/unreadable → ok: false
|
|
84
|
+
* 2. Find a session with status === 'running' and phase === args.phase and strategy === 'lean'
|
|
85
|
+
* If sessionID is provided, also require sessionId === sessionID → none → ok: false
|
|
86
|
+
* 3. Validate session.lanes is a non-empty array → empty → ok: false
|
|
87
|
+
* 4. Check all eligible lanes have status 'completed' or 'failed' → not → ok: false
|
|
88
|
+
* 5. Check no active lane locks exist for lanes in this phase → locks → ok: false
|
|
89
|
+
* 6. Check all degraded tasks in lane plan are resolved → pending/in_progress → ok: false
|
|
90
|
+
* 7. Check integrated diff evidence exists (when required) → missing → ok: false
|
|
91
|
+
* 8. Check reviewer approval if phase_reviewer enabled → missing/rejected → ok: false
|
|
92
|
+
* 9. Check critic approval if phase_critic enabled → missing/rejected → ok: false
|
|
93
|
+
* 10. All checks pass → ok: true
|
|
94
|
+
*
|
|
95
|
+
* Supports two calling conventions for backward compatibility:
|
|
96
|
+
* - New: verifyLeanTurboPhaseReady(dir, phase, sessionID?, config?)
|
|
97
|
+
* - Legacy: verifyLeanTurboPhaseReady(dir, phase, config?) — config was previously the 3rd param
|
|
98
|
+
*
|
|
99
|
+
* @param directory - Project root directory
|
|
100
|
+
* @param phase - Phase number to verify readiness for
|
|
101
|
+
* @param sessionIDOrConfig - Optional session ID (string) OR config object (legacy 3rd-param style)
|
|
102
|
+
* @param config - Optional config; defaults to { phase_reviewer: true, phase_critic: true, integrated_diff_required: true }
|
|
103
|
+
*/
|
|
104
|
+
export declare function verifyLeanTurboPhaseReady(directory: string, phase: number, sessionIDOrConfig?: string | LeanTurboPhaseReadyConfig, config?: LeanTurboPhaseReadyConfig): LeanTurboPhaseReadyResult;
|
|
105
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lane Planning Engine for Lean Turbo.
|
|
3
|
+
*
|
|
4
|
+
* Lean Turbo is a parallel execution strategy that dispatches up to N non-conflicting
|
|
5
|
+
* coder lanes concurrently. This module implements the lane planner that partitions
|
|
6
|
+
* phase tasks into parallel lanes based on file-scope conflicts.
|
|
7
|
+
*
|
|
8
|
+
* ## Lane Planning Algorithm
|
|
9
|
+
*
|
|
10
|
+
* The planner operates in several phases:
|
|
11
|
+
*
|
|
12
|
+
* 1. **Task Extraction**: Extract tasks for the specified phase, filtering out
|
|
13
|
+
* already-completed tasks.
|
|
14
|
+
*
|
|
15
|
+
* 2. **Scope Resolution**: For each task, resolve its file scope:
|
|
16
|
+
* - Use provided scopes map if available
|
|
17
|
+
* - Otherwise, read from `.swarm/scopes/scope-{taskId}.json`
|
|
18
|
+
* - Fall back to `files_touched` from plan.json if `require_declared_scope` is false
|
|
19
|
+
* - If no scope available and `require_declared_scope` is true, serialize the task
|
|
20
|
+
*
|
|
21
|
+
* 3. **Conflict Detection**: Classify each task's files into:
|
|
22
|
+
* - **Global files**: High-risk files that affect all coders (package.json, etc.)
|
|
23
|
+
* → marked as degraded with reason "global file conflict"
|
|
24
|
+
* - **Protected paths**: Paths containing security-sensitive patterns
|
|
25
|
+
* → marked as degraded with reason "protected path" (if `degrade_on_risk` is true)
|
|
26
|
+
* → serialized otherwise
|
|
27
|
+
* - **Normal files**: Regular scoped files that need conflict checking
|
|
28
|
+
*
|
|
29
|
+
* 4. **Lane Assignment**:
|
|
30
|
+
* - Sort tasks by dependency order (tasks with no deps first)
|
|
31
|
+
* - For each non-conflicting task group, create a lane (up to `max_parallel_coders`)
|
|
32
|
+
* - Tasks with conflicts are serialized or degraded based on `conflict_policy`
|
|
33
|
+
*
|
|
34
|
+
* 5. **Counter Population**: Track planned lanes, serialized tasks, and degraded tasks.
|
|
35
|
+
*
|
|
36
|
+
* ## Conflict Detection Rules
|
|
37
|
+
*
|
|
38
|
+
* Two tasks conflict if:
|
|
39
|
+
* - They touch the **same file**
|
|
40
|
+
* - One task touches a **parent directory** of a file the other task touches
|
|
41
|
+
* (e.g., `src/auth/` vs `src/auth/login.ts`)
|
|
42
|
+
* - A task touches a **global file** (affects all coders)
|
|
43
|
+
* - A task touches a **protected path** (security-sensitive areas)
|
|
44
|
+
*
|
|
45
|
+
* ## Path Normalization
|
|
46
|
+
*
|
|
47
|
+
* All paths are normalized to POSIX-style (forward slashes, no trailing slash)
|
|
48
|
+
* before conflict detection. This ensures consistent behavior across platforms.
|
|
49
|
+
*/
|
|
50
|
+
import type { LeanTurboConfig } from '../../config/schema';
|
|
51
|
+
import type { LeanTurboCounters, LeanTurboDegradedTask, LeanTurboLane } from './state';
|
|
52
|
+
export { GLOBAL_FILES_LIST, isGlobalFile, isPathSafe, isProtectedPath, normalizePath, PROTECTED_PATTERNS_LIST, pathsConflict, readTaskScopes, } from './conflicts';
|
|
53
|
+
/**
|
|
54
|
+
* A single task within a plan phase.
|
|
55
|
+
* Matches the structure stored in .swarm/plan.json.
|
|
56
|
+
*/
|
|
57
|
+
export interface PlanTask {
|
|
58
|
+
id: string;
|
|
59
|
+
description: string;
|
|
60
|
+
status: 'pending' | 'in_progress' | 'completed' | 'blocked';
|
|
61
|
+
depends?: string[];
|
|
62
|
+
files_touched?: string[];
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* A phase within a plan, containing multiple tasks.
|
|
66
|
+
*/
|
|
67
|
+
export interface PlanPhase {
|
|
68
|
+
id: number;
|
|
69
|
+
name: string;
|
|
70
|
+
tasks: PlanTask[];
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The complete lane plan produced by `planLeanTurboLanes`.
|
|
74
|
+
* Describes how phase tasks are partitioned into parallel lanes.
|
|
75
|
+
*/
|
|
76
|
+
export interface LeanTurboLanePlan {
|
|
77
|
+
/** The phase number this plan covers */
|
|
78
|
+
phase: number;
|
|
79
|
+
/** Unique identifier for this lane plan (planId from run state) */
|
|
80
|
+
planId: string;
|
|
81
|
+
/** The computed parallel lanes */
|
|
82
|
+
lanes: LeanTurboLane[];
|
|
83
|
+
/** Tasks that were degraded (risk conditions detected) */
|
|
84
|
+
degradedTasks: LeanTurboDegradedTask[];
|
|
85
|
+
/** Tasks that were serialized (conflicts resolved by ordering) */
|
|
86
|
+
serializedTasks: string[];
|
|
87
|
+
/** Human-readable summary when all tasks are degraded */
|
|
88
|
+
degradationSummary?: string;
|
|
89
|
+
/** Execution counters for this planning run */
|
|
90
|
+
counters: LeanTurboCounters;
|
|
91
|
+
/** Map of taskId -> array of dependency taskIds that are in other lanes.
|
|
92
|
+
* The runner must serialize execution of these tasks until the referenced
|
|
93
|
+
* dependencies complete. */
|
|
94
|
+
crossLaneDependencies: Record<string, string[]>;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Partition phase tasks into parallel lanes based on file-scope conflicts.
|
|
98
|
+
*
|
|
99
|
+
* This is the main entry point for Lean Turbo lane planning. It:
|
|
100
|
+
* 1. Extracts tasks for the specified phase
|
|
101
|
+
* 2. Resolves file scopes for each task
|
|
102
|
+
* 3. Detects conflicts between tasks
|
|
103
|
+
* 4. Assigns non-conflicting tasks to parallel lanes
|
|
104
|
+
* 5. Serializes or degrades conflicting tasks based on config
|
|
105
|
+
*
|
|
106
|
+
* @param directory - Project root directory
|
|
107
|
+
* @param phaseNumber - Phase number to plan
|
|
108
|
+
* @param plan - The full plan object (from .swarm/plan.json)
|
|
109
|
+
* @param config - Lean Turbo configuration
|
|
110
|
+
* @param scopes - Optional pre-loaded scopes map (taskId -> file paths)
|
|
111
|
+
* @returns Complete lane plan with lanes, degraded tasks, and counters
|
|
112
|
+
*/
|
|
113
|
+
export declare function planLeanTurboLanes(directory: string, phaseNumber: number, plan: {
|
|
114
|
+
phases: PlanPhase[];
|
|
115
|
+
}, config: LeanTurboConfig, scopes?: Record<string, string[]>): LeanTurboLanePlan;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { type LaneEvidence, listLaneEvidence, readPhaseEvidence } from './evidence';
|
|
2
|
+
import { readPersisted } from './state';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration options for phase reviewer dispatch.
|
|
5
|
+
*/
|
|
6
|
+
export interface LeanTurboPhaseReviewerConfig {
|
|
7
|
+
/**
|
|
8
|
+
* Override the reviewer agent name.
|
|
9
|
+
* Default: derived from `generatedAgentNames` via `{swarmId}_reviewer` pattern
|
|
10
|
+
* when a swarm has multiple reviewers, or `reviewer` for the default swarm.
|
|
11
|
+
*/
|
|
12
|
+
reviewerAgent?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Timeout in milliseconds for the reviewer dispatch.
|
|
15
|
+
* Default: no timeout (reviewer is awaited indefinitely).
|
|
16
|
+
*/
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Require a diff summary in the compiled review package.
|
|
20
|
+
* When true, the package must include an `integratedDiffSummary` field.
|
|
21
|
+
* Default: false.
|
|
22
|
+
*/
|
|
23
|
+
requireDiffSummary?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Result of a phase reviewer dispatch.
|
|
27
|
+
*/
|
|
28
|
+
export interface PhaseReviewerResult {
|
|
29
|
+
/** Reviewer verdict */
|
|
30
|
+
verdict: 'APPROVED' | 'NEEDS_REVISION' | 'REJECTED';
|
|
31
|
+
/** Human-readable reason for the verdict */
|
|
32
|
+
reason?: string;
|
|
33
|
+
/** Path to the persisted reviewer evidence file */
|
|
34
|
+
evidencePath: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolves the default reviewer agent name from the generated agent names.
|
|
38
|
+
*
|
|
39
|
+
* Uses the `{swarmId}_reviewer` pattern for named swarms and bare `reviewer`
|
|
40
|
+
* for the default swarm. Follows the same suffix-based resolution used by
|
|
41
|
+
* `getCanonicalAgentRole` so that arbitrary swarm prefixes are handled correctly.
|
|
42
|
+
*/
|
|
43
|
+
declare function resolveDefaultReviewerAgent(generatedAgentNames: string[]): string;
|
|
44
|
+
/**
|
|
45
|
+
* Compiles a structured review package from lane and phase evidence.
|
|
46
|
+
*/
|
|
47
|
+
interface ReviewPackage {
|
|
48
|
+
phase: number;
|
|
49
|
+
sessionID: string;
|
|
50
|
+
laneSummaries: Array<{
|
|
51
|
+
laneId: string;
|
|
52
|
+
taskIds: string[];
|
|
53
|
+
files: string[];
|
|
54
|
+
status: LaneEvidence['status'];
|
|
55
|
+
agent?: string;
|
|
56
|
+
}>;
|
|
57
|
+
filesChanged: string[];
|
|
58
|
+
testResults: {
|
|
59
|
+
totalLanes: number;
|
|
60
|
+
completedLanes: number;
|
|
61
|
+
failedLanes: number;
|
|
62
|
+
};
|
|
63
|
+
buildStatus: 'unknown' | 'passed' | 'failed';
|
|
64
|
+
degradationSummary: {
|
|
65
|
+
totalDegraded: number;
|
|
66
|
+
resolvedDegraded: number;
|
|
67
|
+
pendingDegraded: number;
|
|
68
|
+
};
|
|
69
|
+
integratedDiffSummary?: string;
|
|
70
|
+
}
|
|
71
|
+
declare function compileReviewPackage(directory: string, phase: number, sessionID: string, requireDiffSummary: boolean): Promise<ReviewPackage>;
|
|
72
|
+
/**
|
|
73
|
+
* Parses a reviewer verdict from the agent's text response.
|
|
74
|
+
*
|
|
75
|
+
* Looks for a verdict marker line: `VERDICT: APPROVED`, `VERDICT: NEEDS_REVISION`,
|
|
76
|
+
* or `VERDICT: REJECTED` (case-insensitive). Returns null if no marker is found.
|
|
77
|
+
*
|
|
78
|
+
* The optional reason is extracted from a `REASON:` marker line that follows
|
|
79
|
+
* the verdict marker on a subsequent line.
|
|
80
|
+
*/
|
|
81
|
+
declare function parseReviewerVerdict(responseText: string): {
|
|
82
|
+
verdict: 'APPROVED' | 'NEEDS_REVISION' | 'REJECTED';
|
|
83
|
+
reason?: string;
|
|
84
|
+
} | null;
|
|
85
|
+
/**
|
|
86
|
+
* Writes the reviewer verdict to the evidence file.
|
|
87
|
+
* Uses atomic write (temp file + rename) to prevent partial-file artifacts.
|
|
88
|
+
*/
|
|
89
|
+
declare function writeReviewerEvidence(directory: string, phase: number, verdict: 'APPROVED' | 'NEEDS_REVISION' | 'REJECTED', reason?: string): Promise<string>;
|
|
90
|
+
/**
|
|
91
|
+
* Test-only dependency-injection seam.
|
|
92
|
+
* Allows tests to intercept reviewer dispatch without mock.module leakage.
|
|
93
|
+
*/
|
|
94
|
+
export declare const _internals: {
|
|
95
|
+
compileReviewPackage: typeof compileReviewPackage;
|
|
96
|
+
parseReviewerVerdict: typeof parseReviewerVerdict;
|
|
97
|
+
writeReviewerEvidence: typeof writeReviewerEvidence;
|
|
98
|
+
dispatchReviewerAgent: (directory: string, pkg: ReviewPackage, agentName: string, timeoutMs: number) => Promise<string>;
|
|
99
|
+
resolveDefaultReviewerAgent: typeof resolveDefaultReviewerAgent;
|
|
100
|
+
listLaneEvidence: typeof listLaneEvidence;
|
|
101
|
+
readPhaseEvidence: typeof readPhaseEvidence;
|
|
102
|
+
readPersisted: typeof readPersisted | null;
|
|
103
|
+
};
|
|
104
|
+
/**
|
|
105
|
+
* Dispatch a read-only reviewer agent to evaluate a completed Lean Turbo phase.
|
|
106
|
+
*
|
|
107
|
+
* Steps:
|
|
108
|
+
* 1. Read all lane evidence from `.swarm/evidence/{phase}/lean-turbo/`
|
|
109
|
+
* 2. Read phase evidence from `.swarm/evidence/{phase}/lean-turbo/lean-turbo-phase.json`
|
|
110
|
+
* 3. Compile a combined review package
|
|
111
|
+
* 4. Dispatch a read-only reviewer agent (tools: write=false, edit=false, patch=false)
|
|
112
|
+
* 5. Parse the verdict from the agent's response
|
|
113
|
+
* 6. Write the verdict to `.swarm/evidence/{phase}/lean-turbo-reviewer.json`
|
|
114
|
+
* 7. Return the result
|
|
115
|
+
*
|
|
116
|
+
* @param directory - Project root directory
|
|
117
|
+
* @param phase - Phase number being reviewed
|
|
118
|
+
* @param sessionID - Lean Turbo session ID
|
|
119
|
+
* @param config - Optional configuration overrides
|
|
120
|
+
* @returns PhaseReviewerResult with verdict, optional reason, and evidence path
|
|
121
|
+
* @throws Error if dispatch fails or response cannot be parsed (fail-closed)
|
|
122
|
+
*/
|
|
123
|
+
export declare function dispatchPhaseReviewer(directory: string, phase: number, sessionID: string, config?: LeanTurboPhaseReviewerConfig): Promise<PhaseReviewerResult>;
|
|
124
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Risk Classification for Lean Turbo Tasks.
|
|
3
|
+
*
|
|
4
|
+
* This module provides risk assessment for tasks based on their file scopes,
|
|
5
|
+
* determining whether tasks can execute in parallel or must be serialized/degraded.
|
|
6
|
+
*
|
|
7
|
+
* ## Risk Categories
|
|
8
|
+
*
|
|
9
|
+
* - **normal**: Regular scoped files that can be parallelized
|
|
10
|
+
* - **global**: Tasks touching global files (affects all coders) → always degraded
|
|
11
|
+
* - **protected**: Tasks touching protected paths → degraded or serialized based on config
|
|
12
|
+
* - **no-scope**: Tasks without declared scope when `require_declared_scope` is true → serialized
|
|
13
|
+
* - **invalid-scope**: Tasks with invalid scope entries → serialized
|
|
14
|
+
*/
|
|
15
|
+
import type { LeanTurboConfig } from '../../config/schema';
|
|
16
|
+
/**
|
|
17
|
+
* Risk category classification for a task.
|
|
18
|
+
*/
|
|
19
|
+
export type TaskRiskCategory = 'normal' | 'global' | 'protected' | 'no-scope' | 'invalid-scope';
|
|
20
|
+
/**
|
|
21
|
+
* Result of risk assessment for a task.
|
|
22
|
+
*/
|
|
23
|
+
export interface TaskRiskAssessment {
|
|
24
|
+
/** The risk category */
|
|
25
|
+
category: TaskRiskCategory;
|
|
26
|
+
/** Human-readable reason for the classification (undefined for 'normal') */
|
|
27
|
+
reason?: string;
|
|
28
|
+
/** Files that contributed to the risk assessment */
|
|
29
|
+
files: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Assess the risk category of a task based on its file scope.
|
|
33
|
+
*
|
|
34
|
+
* Classification priority (first match wins):
|
|
35
|
+
* 1. Global files → 'global' (always degraded)
|
|
36
|
+
* 2. Protected paths → 'protected' (degraded if degrade_on_risk, else serialized)
|
|
37
|
+
* 3. Invalid scope entries → 'invalid-scope' (serialized)
|
|
38
|
+
* 4. No declared scope (when required) → 'no-scope' (serialized)
|
|
39
|
+
* 5. Otherwise → 'normal'
|
|
40
|
+
*
|
|
41
|
+
* @param files - The task's file scope (already validated and normalized)
|
|
42
|
+
* @param hasDeclaredScope - Whether the task has an explicit declared scope
|
|
43
|
+
* @param hasInvalidScope - Whether the scope contains invalid/unsafe entries
|
|
44
|
+
* @param config - Lean Turbo configuration
|
|
45
|
+
* @returns Risk assessment with category, reason, and contributing files
|
|
46
|
+
*/
|
|
47
|
+
export declare function assessTaskRisk(files: string[], hasDeclaredScope: boolean, hasInvalidScope: boolean, config: LeanTurboConfig): TaskRiskAssessment;
|