import type { ExecutionBinding, StoreContext } from '@mstar-harness/engine'; import type { WorkflowSelectionView } from '../types.ts'; import type { WorkflowLedgerTarget } from './workflow-ledger.ts'; /** The active-set resolver result: the session's active lifecycle or a clear error. */ export type ActiveWorkflowSelection = WorkflowSelectionView; /** * What the carrying session can tell the resolver (structural — no * dsh-session import, so cold/raw Session consumers can build one too). * `sessionId` is the durable picker key (`session.header.id`), NEVER a * scope fallback; * Agent exists; `selectedWorkflowId` is the session's durable pick (loaded * by the caller from the engine-status store); `planId` is the plan row the * CURRENT operation names, which identifies its owning lifecycle without * asking the session to pick one. Every field is optional — an omitted hint * just misses the corresponding rung. */ export interface SessionHint { cwd?: string; sessionId?: string; selectedWorkflowId?: string; planId?: string; } /** * Test-only observability hook : * whether a snapshot path is still cached. Production code never calls * this — the eviction contract (delete → key gone) is asserted by the * workflow-selection spec. */ export declare function _terminalStatusCacheHas(snapshotPath: string): boolean; /** * Resolve the ACTIVE lifecycle this session writes under (root v2 * `status.json` `workflows[]` — the list holds non-terminal lifecycles * only, removal-at-terminal). This is the ONLY resolver the agent-flow * writer / ledger may use: no bound entry → a clear error, never a terminal * snapshot, never the root v1 file, and never the registry's first entry. * * This is the FILE route (primary spec §2.1): the root v2 document is the * pre-activation registry. While the harness's execution authority is ACTIVE * that registry is retired, so a source consumer asks the route first * ({@link readExecutionWorkflowSource}) instead of calling this resolver. * * EVERY registry entry is validated before N or the candidates are exposed * (see {@link activeEntriesOf}). Active-set definition: membership in * `workflows[]` — the engine lifecycle enum's non-terminal states are * `running` AND `paused`, so a PAUSED lifecycle stays in the active set (it * is still the operator's current lifecycle; only a TERMINAL lifecycle is * never a write target). * @param harnessDir - the resolved `{HARNESS_DIR}`. * @param hint - the carrying session's structural identity, when it has one * (omitted ⇒ the automatic rungs and the durable pick miss; unique-active * still works). */ export declare function resolveActiveWorkflow(harnessDir: string, hint?: SessionHint): ActiveWorkflowSelection; /** * §5 the execution-authority source read of the active-set rule: the ONE * place a source consumer decides between the two authority routes. * * - `files` — the store predates migration 4, records `legacy`/`staged`, or no * store file exists. The caller keeps its unchanged file readers * ({@link resolveActiveWorkflow}); absence of a store is not an authority * verdict (§2.1), so the pre-activation route is untouched. * - `active` — the execution authority is ACTIVE. The state comes from * `readExecutionAuthority` in ONE read transaction and is normalized into * the SAME active-set input the file route builds (registry rows, plan rows * with their own recorded scope, integration topology), so the binding order * is shared and can never disagree between the routes. The retired * `status.json` / snapshots are NOT read: the DB registry is the whole * active set, its ids are addressed exactly (no newest/unique guess), and a * `workflow.selection.unbound-multi-active` set stays an error. * - `unavailable` — the authority exists and cannot be read (corrupt, drifted, * busy, below-floor runtime, missing `node:sqlite` capability). A refusal, * never a fallback to the retired files (§5). * * The selected lifecycle's materialized state travels with the verdict, so a * caller never re-reads a document to find out what it selected. */ export type ExecutionWorkflowSourceRead = { readonly kind: 'active'; readonly workflowId: string; readonly dir: string; readonly snapshot: Record; readonly storeId: string; readonly epoch: number; } | { readonly kind: 'error'; readonly selection: ActiveWorkflowSelection; readonly storeId?: string; readonly epoch?: number; } | { readonly kind: 'unavailable'; readonly code: string; readonly message: string; }; /** * Stable code + message of a thrown store refusal (engine `StoreError` / * `StoreReadError` carry `code`; anything else is reported as itself). * * Exported for the sibling gate module: a caller that must convert a store * refusal into a GATE VIOLATION (the sync dispatch gate's authority check, * which cannot await the route read) reports the same stable code — one * extraction rule for the whole plugin, never a second sniffing copy. */ export declare function refusalOf(error: unknown): { code: string; message: string; }; /** * §5 read the active-set source through the authoritative adapter. * @param context - the control harness `StoreContext` (its `harnessDir` is the * one root the route is probed on — never a cwd-local or worktree store). * @param hint - the carrying session's structural identity + durable pick, * forwarded verbatim to the shared binding rule. */ export declare function readExecutionWorkflowSource(context: StoreContext, hint?: SessionHint): Promise; /** * Persist an explicitly native-adopted canonical C1 binding. Picker selection * never manufactures this authority witness. */ export declare function adoptExecutionBinding(harnessDir: string, sessionId: string, cwd: string, executionBinding: ExecutionBinding, selectedGeneration?: { storeId: string; epoch: number; }): boolean; /** Clear a stale native binding while preserving the user selection/floor. */ export declare function clearExecutionBinding(harnessDir: string, sessionId: string, cwd: string, selectedGeneration?: { storeId: string; epoch: number; }): boolean; /** * Resolve the explicit target consumed by the workflow ledger. Active targets * require a canonical C1 binding AND a current SQL session row; the legacy * route is served only when the source read says the pre-activation files are * authoritative. This resolver is the single selection point of the ledger's * explicit route, but it does NOT make resolve→append atomic: F2 holds the * maintenance exclusion around its own append, this call runs outside it, so an * activation may still complete between the two (recorded residual seam §4.3). * * The harness resolved here is the session's SELECTED harness, not the * workspace's: a native bind carrying an explicit `harness` adopts its * binding into THAT control root (the adoption receipt), and the returned * target carries the same root so the ledger's write path (scan bound, event * append) acts on the store the binding lives in. * * Resolution ORDER: the receipt is read FIRST and the workspace probe runs * only for a session that never selected an explicit root. The receipt names * a root the carrying workspace may not even declare (a bind into another * control root from a workspace with no harness at all); probing the * workspace first would answer `null` there and drop every ledger event of a * perfectly valid binding. */ export declare function resolveExecutionLedgerTarget(sessionId: string, cwd: string): Promise; /** The plan rows of one materialized snapshot ([] when the doc has no plans * array) — the gate readers' own view of {@link ExecutionWorkflowSourceRead}. */ export declare function activeRowsOf(snapshot: Record): Record[]; /** * Resolve the workflow the catalog/panel READ path aggregates (compass * v3.0.0 § Catalog selection rule): the SAME binding order as the write * path, else the latest terminal snapshot by mtime (history view — only * when the active registry is EMPTY), else a clear error. Never reads the * root v1 `plans[]` as primary or a fallback, and never consults retired * machine event files to infer an active set or writer. * @param harnessDir - the resolved `{HARNESS_DIR}`. * @param hint - forwarded verbatim to the active-set resolver. */ export declare function resolveReadWorkflow(harnessDir: string, hint?: SessionHint): WorkflowSelectionView; /** One catalog-registration refusal: the stable code + its actionable message. */ export interface CatalogRegistrationRefusal { readonly code: string; readonly message: string; } /** * The catalog-registration gate for a root-visible workflow (state-projection * contract §3 step 3; issue contract §7): a workflow whose registration * operation is still `prepared`/`execution-written` is HALF-registered — the * snapshot and the root `workflows[]` entry are visible while the catalog * journal has not published its delta — so it must not be dispatched against * until it is reconciled. * * Root workflow ROUTING stays JSON-owned: this guard never selects a * lifecycle, never reads a selection from the store, and never invents one; * the caller passes the workflow id its OWN JSON selection produced. What the * guard adds is the registration verdict, which lives only in the store's * journal, through the engine's own `assertCatalogExecutionCommitted` (the * single registration authority — no second journal reader here). * * Pre-activation exclusion (issue contract §7, engine `coordination.ts` * parity): a MISSING store (`store.not-initialized`) or a STAGED one * (`store.not-active`) is not a catalog verdict — the legacy authority is * still in force and the workflow is never retro-refused. Every other failure * (corrupt store, below-floor/missing-capability runtime, schema drift, a * conflict) is returned as a refusal rather than swallowed: a dispatch that * cannot prove the registration is never allowed to proceed on a guess. * @param harnessDir - the resolved `{HARNESS_DIR}`. * @param workflowId - the workflow the caller's JSON selection resolved. * @returns the refusal, or `null` when the registration is committed (or the * store makes no catalog claim yet). */