1import type { NamespaceNode } from "../Namespace.ts";
2
3/**
4 * Persisted state for an {@link Action}. Actions share the same FQN
5 * namespace as resources but are discriminated by `kind: "action"`. The
6 * engine keys action state in the {@link State} store under the same `fqn`
7 * field as resources.
8 */
9export type ActionState = RunningActionState | RanActionState;
10
11export type ActionStatus = ActionState["status"];
12
13interface BaseActionState {
14 readonly kind: "action";
15 /** Type of the Action (e.g. "NightlySync"). Mirrors `resourceType` for resources. */
16 actionType: string;
17 /** Namespace of the Action. */
18 namespace: NamespaceNode | undefined;
19 /** Fully qualified name (namespace + logical id). */
20 fqn: string;
21 /** Logical id of the Action (stable across runs). */
22 logicalId: string;
23 /** Current status. */
24 status: ActionStatus;
25 /** FQNs of nodes that depend on this Action's output. */
26 downstream: string[];
27 /** Hash of the resolved input, used to skip noop runs. */
28 inputHash: string;
29 /** Resolved input snapshot from the most recent attempt. */
30 input: unknown;
31}
32
33/**
34 * The Action body has started but persistence after success has not yet
35 * occurred. On resume the engine treats this as "should run" since the
36 * effect may have completed but the output wasn't durably recorded.
37 */
38export interface RunningActionState extends BaseActionState {
39 status: "running";
40}
41
42/**
43 * The Action body completed and its output is durably persisted. Future
44 * plans skip the body when `inputHash` matches the new resolved input
45 * (unless `--force` is set).
46 */
47export interface RanActionState extends BaseActionState {
48 status: "ran";
49 /** Materialized output value returned by the Action body. */
50 output: unknown;
51}