/** * The durable `workflow_run` record, and what a history has to hold to be a * run's own. * * One immutable value per workflow run, written before the root document is * imported. The journal is parsed, never trusted: a record that does not * describe a workflow run is refused rather than coerced, and a record made * from a different base is refused rather than quietly standing in for this * run's. * * Recognition is deliberately narrow. A record identifies a run only when it is * the root coroutine's own successfully settled Yield, under the canonical type * *and* the canonical name, holding a closed value of exactly the three members * a run has. Anything looser lets a same-typed Yield written under another name, * or by a child coroutine, stand in for the record that was removed — and a * recorded terminal result would then be reused as though it settled the run. */ import { StaleInputError } from "@executablemd/durable-streams"; import type { DurableEvent, EffectDescription } from "@executablemd/durable-streams"; /** * One workflow run of a Git definition: an opaque identifier, the base that was * asked for, and the commit that base resolved to once. */ export interface GitWorkflowRunV1 { readonly runId: string; readonly base: string; readonly pinnedCommit: string; } /** * One workflow run of a retained source bundle. * * No base and no pinned commit: this run started from exact bytes rather than * from a repository state, and a synthetic Git field here would be a claim * about a repository the run never had. The bundle hash is what the retained * definition is, and the exact target is what distinguishes two runs over it. */ export interface SourceBundleWorkflowRunV2 { readonly runId: string; readonly definitionVersion: 2; readonly bundleHash: string; readonly targetPath?: string; } /** The value a journal records, and the value `getWorkflowRun()` answers with. */ export type WorkflowRun = GitWorkflowRunV1 | SourceBundleWorkflowRunV2; export declare const WORKFLOW_RUN = "workflow_run"; /** Whether this run is the Git one, narrowing to it when it is. */ export declare function isGitWorkflowRun(run: WorkflowRun): run is GitWorkflowRunV1; /** * The record as a plain value, in the order its version presents it. * * Object-member order is presentation and not identity — a canonical-JSON * container sorts these keys under its own rule without changing the value — * but one ordinary spelling per version is what keeps a retained record byte * for byte what it always was. */ export declare function workflowRunValue(run: WorkflowRun): Record; /** * How the record identifies itself. * * The members past the type and the name are for a reader and never for * matching: divergence detection compares only type and name. A version-2 * description says so and carries its bundle hash; it invents no Git field. */ export declare function describeWorkflowRun(run: WorkflowRun): EffectDescription; /** * The same description for a Git run that has not resolved its commit yet. * * A programmatic run allocates its identifier and resolves its base inside the * durable operation this description names, so the description exists before * the run does. Version 2 has no equivalent: its run arrives whole. */ export declare function describeGitWorkflowRun(base: string): EffectDescription; /** * The workflow run a stored value describes, or `undefined` if it describes none. * * Closed over its three members: a value carrying a fourth is not a run with * something extra, it is a value this version cannot account for. * * Total over anything the journal can hold. Classification and enumeration are * both the value's to refuse — `Array.isArray` throws for a revoked proxy, and * `Object.entries` runs the traps and the getters — so both happen inside one * guarded read and a refusal is an answer rather than an exception carrying the * journal's own text out with it. */ export declare function readWorkflowRun(value: unknown): WorkflowRun | undefined; /** * What one installation requires of a history that is not empty. * * The two installations differ in one thing, and it is not strictness. A * retained run was created by a host before anything executed, so a history of * its own is something it must have: none, or one that failed, means the * recorded work is not this run's. A programmatic run allocates itself on first * execution, and §6 records a base that would not resolve as a *failed* effect — * so a history whose only record is that failure is this run's history, and * replaying it reproduces the failure rather than asking Git again. */ export interface RunHistoryRules { /** Whether a non-empty history must carry a successful record. */ readonly required: boolean; /** The recorded run, or a refusal naming what it disagrees about. */ agree(recorded: WorkflowRun): WorkflowRun; } /** * Hold a retained history to the run it belongs to. * * An empty history is the ordinary live start and is held to nothing. Otherwise * every canonical record it carries must read as a workflow run and agree with * this installation, and it may carry at most one — the record is written before * the root document is imported, so two describe two runs. */ export declare function admitWorkflowRunHistory(retained: readonly DurableEvent[], rules: RunHistoryRules): WorkflowRun | undefined; /** * The journal holds something that is not a workflow run. * * The stored value is described, never quoted: it is external journal data, and * reporting it would carry whatever it happened to hold into logs and rendered * output. */ export declare function malformedRecord(): StaleInputError; /** A journal with history offers no record that says whose history it is. */ export declare function missingRunEvidence(): StaleInputError; /** * A journal carries more than one entry under the canonical run identity. * * The tally is this module's own count rather than anything the journal said, * so naming it carries nothing across. Failed entries count: the question is * how many runs the history describes, not how many of them finished. */ export declare function duplicateRunRecords(records: number): StaleInputError; /** * The recorded run is not the retained run this execution was installed with. * * The differing fields are named and their values are not. A run id may be * caller-selected and a base may be any revision expression, so both are * external text on the same terms as retained props: naming a field says what * disagrees without carrying the disagreement into logs and rendered output. */ export declare function retainedRunMismatch(fields: readonly string[]): StaleInputError; /** * The recorded run started from a different base than this run supplied. * * Both bases are named, because here they are the two things a caller has to * compare to understand the refusal, and both came from that caller rather than * from the journal's opaque content. They are named in the sentence only: the * description this error retains still holds nothing but the effect's type and * name. */ export declare function baseMismatch(recorded: string, supplied: string): StaleInputError;