import { JobStateStore, type JobInitInput } from './job-state-store.js'; import { type JobState, type JobStatusSummary } from './job-types.js'; export interface CheckpointDoneInput { jobId: string; sliceId: string; commitSha?: string; } export interface CheckpointSkipInput { jobId: string; sliceId: string; reason: string; } export interface BlockInput { jobId: string; sliceId: string; reason: string; } /** `peaks job add-slice` input, after the command has trimmed and checked shape. */ export interface AddSliceInput { readonly jobId: string; readonly label: string; } /** * What `addSlice` did. `added: false` is a SUCCESSFUL outcome — the label was * already a slice of this job, so the honest answer is the slice that exists and * a state file that was not written. */ export interface AddSliceOutcome { readonly added: boolean; readonly sliceId: string; readonly label: string; readonly total: number; } export declare class JobOrchestrator { private readonly store; constructor(store: JobStateStore); init(input: JobInitInput): JobState; /** * existing job. * * WHY THIS EXISTS. The slice list was decided once, at `peaks job init * --slice-list`, and nothing could add to it. A wave that discovers its slices * one at a time — which is how every split wave in this repository actually * runs — could not record the slices it found: `checkpoint --slice-id * ` answered `SLICE_NOT_FOUND` and `progress` kept reporting the * `total` `init` wrote, so the ledger recorded the tooling's limitation instead * of the work. * * THE CONTRACT, and the parts of it that are the point: * - **idempotent and honest** — a label already registered on this job is * reported as itself with `added: false`, and `state.json` is never opened * for writing. Re-running the command cannot grow the job. * - **never a silent duplicate** — an empty or whitespace-only label throws; * the command turns that into a structured refusal before it gets here. * - **`total` follows** — the count every reader derives from `slices.length` * (`status`, `progress`, `continue`), so nothing has to be re-initialised. * * The label is the caller's string; `sliceId` stays the CLI's own * `slice-NNN`, so `checkpoint`/`block` resolve the new slice by either name * through the existing D7 resolution in `job-commands.ts`. */ addSlice(input: AddSliceInput): AddSliceOutcome; /** * Atomically apply a mutation to a job's state under a single-process lock. * * Uses the M2.1 single-process lock model (lock file checked with `existsSync`). * State is loaded before lock acquisition, so `fn` receives a consistent snapshot; * if `tryAcquireLock` throws because the job is already locked, `fn` is never called * and the error bubbles to the caller. Concurrent `mutate` calls in the same process * will collide, so callers MUST serialize externally or wrap calls in a higher-level * mutex if multi-process support is added later. * * @param jobId - The job identifier whose state will be mutated. * @param fn - Pure transform applied to the loaded state to produce the next state. * @returns The new state after the mutation is persisted. */ private mutate; checkpointDone(input: CheckpointDoneInput): Promise; checkpointSkipped(input: CheckpointSkipInput): Promise; checkpointFailed(input: { jobId: string; sliceId: string; reason: string; }): Promise; blockSlice(input: BlockInput): Promise; status(jobId: string): JobStatusSummary; continueNow(jobId: string): { remaining: number; next: string | undefined; }; }