/** * `plan_parallel_work` — the borrow checker's verdict, rendered for a swarm * (change: add-parallel-work-plan, PARALLEL-WORK-COORDINATION proposal 2). * * Composes the footprint projection + pairwise hazard classifier (proposal 1) * into the one conclusion an orchestrator should ask for before fanning work out * across worktrees: of these N proposed tasks, which subset is safe to edit * concurrently, which must be ordered, and what is the minimum wall-clock even * with unlimited agents? * * Conclusion over graph: the primary payload is the *schedule* (waves + critical * path), not a node-and-edge graph for the agent to color by hand. The conflict * graph rides along as supporting evidence with witnesses. * * Stateless `render(state)`: the tool holds nothing between calls. To re-plan * after a wave completes, the caller re-invokes with the remaining tasks. There * is no lease, no "release," no memory of which agent took which task — the * harness owns state and dispatch (north star `c6d1ad07`: OpenLore computes * conclusions; it never grows a coordinator). * * Advisory by default: the plan blocks nothing on its own. WAW conflicts and * unorderable RAW cycles are emitted as policy-shaped `GovernanceFinding`s * (`parallel-work-conflict` / `parallel-work-cycle`, registered in * `FINDING_CODE_REGISTRY`) so the *caller* that invokes this tool can classify * them with `resolveEnforcementClass(code, policy)` and choose to block in its own * orchestration/CI. The bundled `openlore enforce` commit gate is diff-based and * never runs the planner, so it never sees — and never blocks on — these findings * (add-finding-enforcement-policy). */ import { type TaskDescriptor, type HazardVerdict } from './change-footprint.js'; import type { GovernanceFinding } from './enforcement-policy.js'; export interface PlanParallelWorkInput { directory: string; /** Caller-supplied task list. OpenLore schedules; it never invents or decomposes the list. */ tasks: TaskDescriptor[]; /** Forwarded to the footprint projection (call-distance read-set bound). */ readMaxDistance?: number; /** Forwarded to the footprint projection (backward affected-set hop depth). */ affectedMaxDepth?: number; /** Forwarded to the footprint projection (ambient fan-in percentile). */ ambientFanInPercentile?: number; } /** A task's footprint, rendered for the plan. Every region list is capped to * {@link FOOTPRINT_LIST_CAP} with an authoritative uncapped count, so a task seeded * on a god-function (huge read-set / ambient deps) or a whole file (large write-set) * cannot bloat the response (no-silent-truncation). */ export interface RenderedFootprint { taskId: string; writeSet: Array<{ id: string; name: string; filePath: string; writeMode: string; }>; writeSetCount: number; writeSetTruncated: boolean; readSet: string[]; readSetCount: number; readSetTruncated: boolean; affectedSet: string[]; affectedSetCount: number; affectedSetTruncated: boolean; ambientReadDeps: string[]; ambientReadDepCount: number; ambientReadDepsTruncated: boolean; couplingNeighbors: string[]; couplingNeighborCount: number; couplingNeighborsTruncated: boolean; unresolvedSeeds: string[]; } /** One pairwise verdict in the conflict graph (supporting evidence, not a graph to traverse). */ export interface ConflictPair { taskA: string; taskB: string; hazard: HazardVerdict['kind']; direction?: HazardVerdict['direction']; witnesses: string[]; } /** One scheduled wave. */ export interface Wave { wave: number; /** Tasks safe to dispatch together in this wave. */ taskIds: string[]; /** Predecessor tasks (in earlier waves) that this wave's RAW dependencies wait on. */ waitsOn: string[]; } export interface CriticalPath { /** Minimum number of sequential rounds even with unlimited agents (== the schedule depth). */ rounds: number; /** A witnessing longest chain of hard-ordered tasks. */ chain: string[]; /** Plain-language read of the parallelism ceiling. */ summary: string; } export interface ParallelWorkPlan { taskCount: number; footprints: RenderedFootprint[]; /** Pairwise hazards (only the non-`none` pairs), as supporting evidence with witnesses. Capped — see `conflictCount`. */ conflicts: ConflictPair[]; /** Total non-`none` pairs (uncapped); `conflicts` is truncated to {@link CONFLICT_LIST_CAP} when this exceeds it. */ conflictCount: number; conflictsTruncated: boolean; /** The computed answer: an ordered list of dispatch waves (always complete). */ waves: Wave[]; criticalPath: CriticalPath; /** Low-risk pairs surfaced as warnings (shared-append / WAR / soft-coupling) — non-serializing. Capped — see `advisoryCount`. */ advisories: Array<{ kind: HazardVerdict['kind']; taskA: string; taskB: string; witnesses: string[]; note: string; }>; advisoryCount: number; advisoriesTruncated: boolean; /** * Governance findings (WAW conflicts / unorderable RAW cycles), shaped so a caller * can classify them with `resolveEnforcementClass`. Advisory by default; capped — * see `findingCount`. The bundled `openlore enforce` gate does NOT run the planner, * so it never sees these — the invoking caller/CI applies the policy. */ findings: GovernanceFinding[]; findingCount: number; findingsTruncated: boolean; /** Greedy + topological; not optimal — stated plainly. */ scheduling: string; /** Standing known-unknowable disclosure. */ disclosure: string; /** Set only when a large plan was shrunk to fit the response budget (counts stay authoritative). */ truncationNote?: string; } export declare function computePlanParallelWork(input: PlanParallelWorkInput): Promise; /** MCP dispatch entry. */ export declare function handlePlanParallelWork(input: PlanParallelWorkInput): Promise; //# sourceMappingURL=plan-parallel-work.d.ts.map