/** * RuntimeStateManager — integration layer wiring all M2 store components. * * Single entry point for CLI and diagnostician runner to interact with * task/run state. Owns the lifecycle of: * - SqliteConnection (shared by TaskStore + RunStore) * - SqliteTaskStore + SqliteRunStore * - DefaultLeaseManager (with event emission) * - DefaultRetryPolicy * - DefaultRecoverySweep * * Usage: * ```typescript * const mgr = new RuntimeStateManager({ workspaceDir: process.cwd() }); * await mgr.initialize(); * const task = await mgr.acquireLease({ ... }); * await mgr.close(); * ``` */ import type { SqliteConnection } from './sqlite-connection.js'; import type { TaskStore, TaskStoreFilter, TaskStoreUpdatePatch } from './task/task-store.js'; import type { RunStore, RunRecord, TolerantRunListResult } from './run/run-store.js'; import type { AcquireLeaseOptions } from './lifecycle/lease-manager.js'; import type { RetryPolicy, RetryPolicyConfig } from './lifecycle/retry-policy.js'; import type { RecoveryResult } from './lifecycle/recovery-sweep.js'; import type { PDErrorCategory } from '../error-categories.js'; import type { TaskRecord } from '../task-status.js'; import { type StoreEventEmitter } from './event-emitter.js'; import { type TaskArtifactCasInput } from './task/sqlite-task-store.js'; import type { PIArtifactStore } from '../internalization/pi-artifact.js'; import type { PainDiagnosisRecord, PainDiagnosisWriteInput } from './pain-diagnosis/pain-diagnosis-store.js'; import type { CommitRecord } from './commit/commit-store.js'; import type { CandidateRecord } from './candidate/candidate-store.js'; import type { ArtifactRecord, ArtifactWithCandidates } from './artifact/artifact-store.js'; export type { CommitRecord } from './commit/commit-store.js'; export type { CandidateRecord } from './candidate/candidate-store.js'; export type { ArtifactRecord, ArtifactWithCandidates } from './artifact/artifact-store.js'; export type { PainDiagnosisRecord, PainDiagnosisWriteInput } from './pain-diagnosis/pain-diagnosis-store.js'; export interface RuntimeStateManagerOptions { /** Workspace directory — DB created at /.pd/state.db */ workspaceDir: string; /** Optional custom emitter (defaults to storeEmitter singleton) */ emitter?: StoreEventEmitter; /** Optional retry policy config */ retryPolicyConfig?: RetryPolicyConfig; /** Open DB in readonly mode — skips schema init/migration, no writes allowed */ readonly?: boolean; } export declare class RuntimeStateManager { private readonly options; private _connection; private _taskStore; private _runStore; private _commitStore; private _candidateStore; private _artifactStore; private _piArtifactStore; private _painDiagnosisStore; private leaseManager; private retryPolicy; private recoverySweep; private readonly emitter; private _initialized; constructor(options: RuntimeStateManagerOptions); /** Initialize all store components. Must be called before any other method. */ initialize(): Promise; get isInitialized(): boolean; get workspaceDir(): string; /** Readonly accessors for internal stores — used by CLI DiagnosticianRunner setup. */ get connection(): SqliteConnection; get taskStore(): TaskStore; get runStore(): RunStore; get piArtifactStore(): PIArtifactStore; private assertInitialized; /** Close the state manager and release resources. */ close(): Promise; createTask(record: Omit): Promise; getTask(taskId: string): Promise; listTasks(filter?: TaskStoreFilter): Promise; updateTask(taskId: string, patch: TaskStoreUpdatePatch): Promise; deleteTask(taskId: string): Promise; getRunsByTask(taskId: string): Promise; /** * Tolerant variant of getRunsByTask: returns valid runs AND any * schema-degraded historical rows instead of throwing MalformedRunError. * * Used by the runner execution/completion path so a malformed historical * run row does not block recovery of a task that still has a valid run * (the one created by acquireLease). Callers MUST surface a non-empty * degradedRuns list via telemetry/notes — silent swallowing is a bug (ERR-002). */ getValidRunsByTaskTolerant(taskId: string): Promise; getRun(runId: string): Promise; acquireLease(options: AcquireLeaseOptions): Promise; releaseLease(taskId: string, owner: string): Promise; renewLease(taskId: string, owner: string, durationMs?: number): Promise; forceExpireLease(taskId: string): Promise; isLeaseExpired(task: TaskRecord): boolean; /** Mark a task as succeeded and emit task_succeeded event. */ markTaskSucceeded(taskId: string, resultRef?: string): Promise; /** Mark a task as failed and emit task_failed event. */ markTaskFailed(taskId: string, lastError: PDErrorCategory, failureReason?: string): Promise; /** Mark a task as retry_wait and emit task_retried event. Per D-03: retry with backoff. * Sets leaseExpiresAt to now + backoffMs so that canRetryNow() gates correctly. */ markTaskRetryWait(taskId: string, errorCategory: PDErrorCategory, failureReason?: string): Promise; /** * Write output payload to a run record. * Per D-04: DiagnosticianOutputV1 JSON serialized into RunRecord.outputPayload. */ updateRunOutput(runId: string, outputPayload: string): Promise; /** * Observe malformed historical run rows without blocking the caller. * * The execution/completion path tolerates schema-invalid historical run * rows (they must not block recovery of a task that has a valid run from * acquireLease). But tolerance MUST be observable — silently swallowing * degraded rows is a bug (ERR-002). This emits a structured * degradation_triggered event naming the affected runIds so operators can * find and quarantine them via `pd runtime internalization integrity-repair`. */ private observeMalformedRuns; runRecoverySweep(): Promise<{ recovered: number; errors: string[]; }>; detectExpiredLeases(): Promise; recoverTask(taskId: string): Promise; updateTaskDiagnosticJson(taskId: string, diagnosticJson: string): Promise; /** * Narrow CAS (PRI-629): apply patch only when the task's diagnostic_json is * still byte-equal to expectedDiagnosticJson. Returns null on precondition * failure — caller re-reads and re-evaluates (idempotent-or-conflict). */ updateTaskIfDiagnosticJsonUnchanged(taskId: string, expectedDiagnosticJson: string | null, patch: TaskStoreUpdatePatch): Promise; /** * Atomically records an Owner decision only while both the task metadata and * every artifact row used by its evidence snapshot remain unchanged. */ updateTaskIfDiagnosticJsonAndArtifactsUnchanged(input: TaskArtifactCasInput): Promise; getRetryPolicy(): RetryPolicy; getCommitByTaskId(taskId: string): Promise; getCandidatesByTaskId(taskId: string): Promise; getCandidate(candidateId: string): Promise; updateCandidateStatus(candidateId: string, patch: { status: CandidateRecord['status']; }): Promise; transitionCandidateStatus(candidateId: string, expectedStatus: CandidateRecord['status'], newStatus: CandidateRecord['status']): Promise; archivePrinciple(principleId: string): Promise; getArtifact(artifactId: string): Promise; /** * Persist the diagnostician's root-cause attribution for a pain. Called by * PainSignalBridge.onDiagnosisComplete when pain_diagnosis_persistence is on. * Idempotent per (taskId, diagnosisId). */ recordPainDiagnosis(input: PainDiagnosisWriteInput): Promise; /** All persisted diagnoses for a pain (multiple rows = re-diagnosis / mixed attribution). */ getDiagnosesByPainId(painId: string): Promise; getArtifactWithCandidates(artifactId: string): Promise; } //# sourceMappingURL=runtime-state-manager.d.ts.map