@gobing-ai/ts-dual-workflow-engine 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -8
- package/dist/extensions.d.ts +6 -0
- package/dist/extensions.d.ts.map +1 -1
- package/dist/extensions.js +1 -0
- package/dist/persistence.d.ts +11 -0
- package/dist/persistence.d.ts.map +1 -1
- package/dist/persistence.js +66 -8
- package/dist/run-lifecycle.d.ts +20 -3
- package/dist/run-lifecycle.d.ts.map +1 -1
- package/dist/run-lifecycle.js +35 -4
- package/dist/service.js +4 -4
- package/dist/state-machine.d.ts.map +1 -1
- package/dist/state-machine.js +16 -4
- package/dist/transition-flow.d.ts.map +1 -1
- package/dist/transition-flow.js +16 -4
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/extensions.ts +7 -0
- package/src/persistence.ts +88 -36
- package/src/run-lifecycle.ts +42 -4
- package/src/service.ts +4 -4
- package/src/state-machine.ts +16 -4
- package/src/transition-flow.ts +16 -4
- package/src/types.ts +15 -0
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ The package exposes:
|
|
|
39
39
|
| Export | Purpose |
|
|
40
40
|
|--------|---------|
|
|
41
41
|
| `MemoryWorkflowPersistenceAdapter` | In-memory persistence for tests and short-lived runs |
|
|
42
|
-
| `DbWorkflowPersistenceAdapter` | DB-backed persistence over `@gobing-ai/ts-db` |
|
|
42
|
+
| `DbWorkflowPersistenceAdapter` | DB-backed persistence over `@gobing-ai/ts-db` with atomic `commitTransition` (ADR-020) |
|
|
43
43
|
| `applyWorkflowEngineSchema()` | Installs the package-owned DB schema (idempotent; safe to call before every write) |
|
|
44
44
|
| `WORKFLOW_ENGINE_SCHEMA_SQL` | Raw SQL DDL for the 5 workflow engine tables |
|
|
45
45
|
|
|
@@ -222,7 +222,7 @@ sequenceDiagram
|
|
|
222
222
|
RL->>SM: invoke loop(lifecycle)
|
|
223
223
|
|
|
224
224
|
loop Each state
|
|
225
|
-
SM->>RL: enter(stateId, transitionsTaken)
|
|
225
|
+
SM->>RL: enter(stateId, transitionsTaken, persist=true)
|
|
226
226
|
RL->>P: saveWorkflowState(stateId, data)
|
|
227
227
|
RL->>P: savePhase(stateId, 'running')
|
|
228
228
|
|
|
@@ -267,8 +267,8 @@ sequenceDiagram
|
|
|
267
267
|
H-->>SM: ActionResult
|
|
268
268
|
end
|
|
269
269
|
|
|
270
|
-
SM->>RL:
|
|
271
|
-
RL
|
|
270
|
+
SM->>RL: commitHop(from, to, trigger, transitionsTaken, phase?)
|
|
271
|
+
Note right of RL: atomic batch: transition + state + phase (ADR-020)
|
|
272
272
|
SM->>SM: transitionsTaken += 1
|
|
273
273
|
|
|
274
274
|
alt iteration bound exceeded
|
|
@@ -309,7 +309,7 @@ sequenceDiagram
|
|
|
309
309
|
RL->>TF: invoke loop(lifecycle)
|
|
310
310
|
|
|
311
311
|
loop Each node
|
|
312
|
-
TF->>RL: enter(nodeId, transitionsTaken)
|
|
312
|
+
TF->>RL: enter(nodeId, transitionsTaken, persist=true)
|
|
313
313
|
RL->>P: saveWorkflowState(nodeId, data)
|
|
314
314
|
RL->>P: savePhase(nodeId, 'running')
|
|
315
315
|
|
|
@@ -353,8 +353,8 @@ sequenceDiagram
|
|
|
353
353
|
Note over TF: proceed immediately
|
|
354
354
|
end
|
|
355
355
|
|
|
356
|
-
TF->>RL:
|
|
357
|
-
RL
|
|
356
|
+
TF->>RL: commitHop(from, to, trigger, transitionsTaken, phase?)
|
|
357
|
+
Note right of RL: atomic batch: transition + state + phase (ADR-020)
|
|
358
358
|
TF->>TF: transitionsTaken += 1
|
|
359
359
|
|
|
360
360
|
alt iteration bound exceeded
|
|
@@ -848,7 +848,7 @@ When no `events` option is provided, the engine incurs zero observability overhe
|
|
|
848
848
|
`RunLifecycle` is the shared bookkeeping layer both drivers delegate to. It manages:
|
|
849
849
|
|
|
850
850
|
- **Run identity** — generates a `runId` (or honors caller-provided), timestamps, and run record
|
|
851
|
-
- **Persistence sequencing** — `createRun` → `
|
|
851
|
+
- **Persistence sequencing** — `createRun` → per-step atomic `commitHop` (transition + state + phase in one batch) → `finalizeRun` at the end (ADR-020)
|
|
852
852
|
- **Observability** — wraps the full run in an OTel span, emits span events, logs each lifecycle event through `@gobing-ai/ts-infra` logger, and optionally emits `WorkflowEngineEvents` via an injected `EventBus`
|
|
853
853
|
- **Error resilience** — `warnActionFailed()` logs non-fatal warnings for `onError: 'continue'` actions
|
|
854
854
|
|
|
@@ -869,6 +869,20 @@ const result = await RunLifecycle.run(
|
|
|
869
869
|
);
|
|
870
870
|
```
|
|
871
871
|
|
|
872
|
+
For multi-step runs, use `commitHop` instead of separate `recordTransition` + `saveWorkflowState` calls:
|
|
873
|
+
|
|
874
|
+
```ts
|
|
875
|
+
// commitHop atomically commits transition, state, and optional phase (ADR-020)
|
|
876
|
+
await lifecycle.commitHop(fromState, toState, trigger, transitionsTaken);
|
|
877
|
+
```
|
|
878
|
+
|
|
879
|
+
On the next iteration, call `enter()` with `persist: false` to skip persistence (the state was already
|
|
880
|
+
persisted atomically by commitHop):
|
|
881
|
+
|
|
882
|
+
```ts
|
|
883
|
+
await lifecycle.enter(targetState, transitionsTaken, false);
|
|
884
|
+
```
|
|
885
|
+
|
|
872
886
|
## Error Handling
|
|
873
887
|
|
|
874
888
|
| Error Class | When |
|
package/dist/extensions.d.ts
CHANGED
|
@@ -42,6 +42,12 @@ export interface LoadWorkflowExtensionsOptions {
|
|
|
42
42
|
* own; the embedder supplies the import policy.
|
|
43
43
|
*/
|
|
44
44
|
readonly moduleLoader: (absPath: string) => Promise<Record<string, unknown>>;
|
|
45
|
+
/**
|
|
46
|
+
* Optional canonical-path resolver for symlink-safe confinement (ADR-022).
|
|
47
|
+
* Forwarded to the shared loader. Node-facing embedders should supply
|
|
48
|
+
* `createNodeFileSystem().realPath` to enable symlink-escape rejection.
|
|
49
|
+
*/
|
|
50
|
+
readonly realPath?: (absPath: string) => string;
|
|
45
51
|
}
|
|
46
52
|
/**
|
|
47
53
|
* Import each extension module behind an explicit trust gate and register
|
package/dist/extensions.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAKlD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAGjD,6GAA6G;AAC7G,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE3D;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACjC,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC/B;AAED,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,sEAAsE;IACtE,QAAQ,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAKlD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAGjD,6GAA6G;AAC7G,MAAM,MAAM,uBAAuB,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAE3D;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEzD;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB;IACjC,kCAAkC;IAClC,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,qEAAqE;IACrE,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC/B;AAED,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC1C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,sEAAsE;IACtE,QAAQ,CAAC,MAAM,CAAC,EAAE,uBAAuB,CAAC;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7E;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CACnD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,8BAA8B,CAChD,IAAI,EAAE,kBAAkB,EACxB,IAAI,EAAE,SAAS,oBAAoB,EAAE,EACrC,OAAO,EAAE,6BAA6B,GACvC,OAAO,CAAC,IAAI,CAAC,CAsCf"}
|
package/dist/extensions.js
CHANGED
|
@@ -42,6 +42,7 @@ export async function loadWorkflowExtensionsIntoHost(host, refs, options) {
|
|
|
42
42
|
allowExtensions: options.allowExtensions,
|
|
43
43
|
logger: options.logger,
|
|
44
44
|
moduleLoader: options.moduleLoader,
|
|
45
|
+
realPath: options.realPath,
|
|
45
46
|
};
|
|
46
47
|
await loadExtensionModules(sharedRefs, sharedOptions, async (sharedRef, extension) => {
|
|
47
48
|
await registerExtensionOnHost(host, sharedRef, extension, options.logger);
|
package/dist/persistence.d.ts
CHANGED
|
@@ -24,6 +24,13 @@ export declare class DbWorkflowPersistenceAdapter implements WorkflowPersistence
|
|
|
24
24
|
saveTransition(runId: string, from: string, to: string, trigger: string | null): Promise<void>;
|
|
25
25
|
/** Save the latest workflow state snapshot. */
|
|
26
26
|
saveWorkflowState(runId: string, state: string, data: Record<string, unknown>): Promise<void>;
|
|
27
|
+
commitTransition(runId: string, from: string, to: string, trigger: string | null, state: string, data: Record<string, unknown>, phase?: {
|
|
28
|
+
phase: string;
|
|
29
|
+
status: WorkflowStatus;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
private transitionRow;
|
|
32
|
+
private stateRow;
|
|
33
|
+
private phaseRow;
|
|
27
34
|
/** Insert a running action row. Returns the row id for later finalization. */
|
|
28
35
|
saveActionStart(runId: string, node: string, kind: string): Promise<string>;
|
|
29
36
|
/** Finalize an action row with duration, ok flag, and optional redacted result. */
|
|
@@ -89,6 +96,10 @@ export declare class MemoryWorkflowPersistenceAdapter implements WorkflowPersist
|
|
|
89
96
|
saveTransition(runId: string, from: string, to: string, trigger: string | null): Promise<void>;
|
|
90
97
|
/** Save the latest workflow state snapshot. */
|
|
91
98
|
saveWorkflowState(runId: string, state: string, data: Record<string, unknown>): Promise<void>;
|
|
99
|
+
commitTransition(runId: string, from: string, to: string, trigger: string | null, state: string, data: Record<string, unknown>, phase?: {
|
|
100
|
+
phase: string;
|
|
101
|
+
status: WorkflowStatus;
|
|
102
|
+
}): Promise<void>;
|
|
92
103
|
/** Load a single run by id. */
|
|
93
104
|
loadRun(runId: string): Promise<WorkflowRunRecord | undefined>;
|
|
94
105
|
/** List persisted workflow runs. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"persistence.d.ts","sourceRoot":"","sources":["../src/persistence.ts"],"names":[],"mappings":"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"}
|
package/dist/persistence.js
CHANGED
|
@@ -40,21 +40,71 @@ export class DbWorkflowPersistenceAdapter {
|
|
|
40
40
|
}
|
|
41
41
|
/** Save one phase/state execution record. */
|
|
42
42
|
async savePhase(runId, phase, status) {
|
|
43
|
-
const
|
|
44
|
-
await this.db.run(
|
|
45
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, `${runId}:phase:${phase}:${crypto.randomUUID()}`, runId, phase, status, new Date(now).toISOString(), status === 'running' ? null : new Date(now).toISOString(), now, now);
|
|
43
|
+
const op = this.phaseRow(runId, phase, status, Date.now());
|
|
44
|
+
await this.db.run(op.sql, ...op.params);
|
|
46
45
|
}
|
|
47
46
|
/** Save one transition record. */
|
|
48
47
|
async saveTransition(runId, from, to, trigger) {
|
|
49
|
-
const
|
|
50
|
-
await this.db.run(
|
|
51
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, `${runId}:transition:${from}:${to}:${crypto.randomUUID()}`, runId, from, to, trigger, 'done', now, now);
|
|
48
|
+
const op = this.transitionRow(runId, from, to, trigger, Date.now());
|
|
49
|
+
await this.db.run(op.sql, ...op.params);
|
|
52
50
|
}
|
|
53
51
|
/** Save the latest workflow state snapshot. */
|
|
54
52
|
async saveWorkflowState(runId, state, data) {
|
|
53
|
+
const op = this.stateRow(runId, state, data, Date.now());
|
|
54
|
+
await this.db.run(op.sql, ...op.params);
|
|
55
|
+
}
|
|
56
|
+
async commitTransition(runId, from, to, trigger, state, data, phase) {
|
|
57
|
+
await this.ensureSchema();
|
|
55
58
|
const now = Date.now();
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
const ops = [
|
|
60
|
+
this.transitionRow(runId, from, to, trigger, now),
|
|
61
|
+
this.stateRow(runId, state, data, now),
|
|
62
|
+
];
|
|
63
|
+
if (phase) {
|
|
64
|
+
ops.push(this.phaseRow(runId, phase.phase, phase.status, now));
|
|
65
|
+
}
|
|
66
|
+
await this.db.batch(ops);
|
|
67
|
+
}
|
|
68
|
+
// Row builders shared by the individual save methods and commitTransition so
|
|
69
|
+
// the single-write and atomic-batch paths can never drift apart (ADR-020).
|
|
70
|
+
transitionRow(runId, from, to, trigger, now) {
|
|
71
|
+
return {
|
|
72
|
+
sql: `INSERT INTO transition_runs (id, run_id, from_state, to_state, trigger, status, created_at, updated_at)
|
|
73
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
74
|
+
params: [
|
|
75
|
+
`${runId}:transition:${from}:${to}:${crypto.randomUUID()}`,
|
|
76
|
+
runId,
|
|
77
|
+
from,
|
|
78
|
+
to,
|
|
79
|
+
trigger,
|
|
80
|
+
'done',
|
|
81
|
+
now,
|
|
82
|
+
now,
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
stateRow(runId, state, data, now) {
|
|
87
|
+
return {
|
|
88
|
+
sql: `INSERT INTO workflow_states (id, run_id, state, data_json, created_at, updated_at)
|
|
89
|
+
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
90
|
+
params: [`${runId}:state:${state}:${crypto.randomUUID()}`, runId, state, JSON.stringify(data), now, now],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
phaseRow(runId, phase, status, now) {
|
|
94
|
+
return {
|
|
95
|
+
sql: `INSERT INTO phase_runs (id, run_id, phase, status, started_at, completed_at, created_at, updated_at)
|
|
96
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
97
|
+
params: [
|
|
98
|
+
`${runId}:phase:${phase}:${crypto.randomUUID()}`,
|
|
99
|
+
runId,
|
|
100
|
+
phase,
|
|
101
|
+
status,
|
|
102
|
+
new Date(now).toISOString(),
|
|
103
|
+
status === 'running' ? null : new Date(now).toISOString(),
|
|
104
|
+
now,
|
|
105
|
+
now,
|
|
106
|
+
],
|
|
107
|
+
};
|
|
58
108
|
}
|
|
59
109
|
/** Insert a running action row. Returns the row id for later finalization. */
|
|
60
110
|
async saveActionStart(runId, node, kind) {
|
|
@@ -195,6 +245,14 @@ export class MemoryWorkflowPersistenceAdapter {
|
|
|
195
245
|
async saveWorkflowState(runId, state, data) {
|
|
196
246
|
this.states.push({ runId, state, data });
|
|
197
247
|
}
|
|
248
|
+
async commitTransition(runId, from, to, trigger, state, data, phase) {
|
|
249
|
+
// Memory adapter: synchronous pushes are atomic by nature.
|
|
250
|
+
this.transitions.push({ runId, from, to, trigger });
|
|
251
|
+
this.states.push({ runId, state, data });
|
|
252
|
+
if (phase) {
|
|
253
|
+
this.phases.push({ runId, phase: phase.phase, status: phase.status });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
198
256
|
/** Load a single run by id. */
|
|
199
257
|
async loadRun(runId) {
|
|
200
258
|
return this.runs.get(runId);
|
package/dist/run-lifecycle.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type EventBus, type Logger } from '@gobing-ai/ts-infra';
|
|
2
2
|
import type { WorkflowEngineEvents } from './events';
|
|
3
|
-
import type { WorkflowPersistenceAdapter, WorkflowRunOptions, WorkflowRunResult } from './types';
|
|
3
|
+
import type { WorkflowPersistenceAdapter, WorkflowRunOptions, WorkflowRunResult, WorkflowStatus } from './types';
|
|
4
4
|
/** Workflow dialect carried on every run, span, and persisted record. */
|
|
5
5
|
export type WorkflowMode = WorkflowRunResult['mode'];
|
|
6
6
|
/** Keys of the runtime builtin namespace, single-sourced for resolver + validator. */
|
|
@@ -53,8 +53,25 @@ export declare class RunLifecycle {
|
|
|
53
53
|
* The caller is responsible for ensuring the run exists and emitting the resumed event.
|
|
54
54
|
*/
|
|
55
55
|
static resume(workflowName: string, mode: WorkflowMode, deps: RunLifecycleDeps, runId: string, externalKey: string | undefined, loop: (lifecycle: RunLifecycle) => Promise<WorkflowRunResult>): Promise<WorkflowRunResult>;
|
|
56
|
-
/**
|
|
57
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Persist the current state/node snapshot and mark its phase running.
|
|
58
|
+
* When `persist` is false (set by driver loops after `commitHop` already
|
|
59
|
+
* persisted the state atomically), only emit the enter observability —
|
|
60
|
+
* skip `saveWorkflowState` + `savePhase` to avoid duplicate INSERT rows.
|
|
61
|
+
*/
|
|
62
|
+
enter(stateOrNodeId: string, transitionsTaken: number, persist?: boolean): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Atomically commit a transition together with the resulting state snapshot
|
|
65
|
+
* and optional phase record, then emit the transition observability event.
|
|
66
|
+
* Replaces the old `recordTransition` + `enter` pair at every driver hop
|
|
67
|
+
* site so a crash between writes is impossible (ADR-020). Emits
|
|
68
|
+
* `workflow.node.transition` only after the batch commits — a rolled-back
|
|
69
|
+
* batch emits nothing (R1.6).
|
|
70
|
+
*/
|
|
71
|
+
commitHop(from: string, to: string, trigger: string | null, transitionsTaken: number, phase?: {
|
|
72
|
+
phase: string;
|
|
73
|
+
status: WorkflowStatus;
|
|
74
|
+
}): Promise<void>;
|
|
58
75
|
/** Persist a transition and emit its observability event. */
|
|
59
76
|
recordTransition(from: string, to: string, trigger: string | null): Promise<void>;
|
|
60
77
|
/** Finalize the run as succeeded and return its result. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-lifecycle.d.ts","sourceRoot":"","sources":["../src/run-lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,QAAQ,EAAa,KAAK,MAAM,EAAc,MAAM,qBAAqB,CAAC;AAEtG,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,KAAK,EACR,0BAA0B,EAC1B,kBAAkB,EAElB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"run-lifecycle.d.ts","sourceRoot":"","sources":["../src/run-lifecycle.ts"],"names":[],"mappings":"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"}
|
package/dist/run-lifecycle.js
CHANGED
|
@@ -109,10 +109,17 @@ export class RunLifecycle {
|
|
|
109
109
|
return await loop(lifecycle);
|
|
110
110
|
}, { attributes: { 'workflow.name': workflowName, 'workflow.mode': mode } });
|
|
111
111
|
}
|
|
112
|
-
/**
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
/**
|
|
113
|
+
* Persist the current state/node snapshot and mark its phase running.
|
|
114
|
+
* When `persist` is false (set by driver loops after `commitHop` already
|
|
115
|
+
* persisted the state atomically), only emit the enter observability —
|
|
116
|
+
* skip `saveWorkflowState` + `savePhase` to avoid duplicate INSERT rows.
|
|
117
|
+
*/
|
|
118
|
+
async enter(stateOrNodeId, transitionsTaken, persist = true) {
|
|
119
|
+
if (persist) {
|
|
120
|
+
await this.persistence.saveWorkflowState(this.runId, stateOrNodeId, { transitionsTaken });
|
|
121
|
+
await this.persistence.savePhase(this.runId, stateOrNodeId, 'running');
|
|
122
|
+
}
|
|
116
123
|
addSpanEvent('workflow.node.enter', {
|
|
117
124
|
runId: this.runId,
|
|
118
125
|
node: stateOrNodeId,
|
|
@@ -124,6 +131,30 @@ export class RunLifecycle {
|
|
|
124
131
|
transitionsTaken,
|
|
125
132
|
});
|
|
126
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Atomically commit a transition together with the resulting state snapshot
|
|
136
|
+
* and optional phase record, then emit the transition observability event.
|
|
137
|
+
* Replaces the old `recordTransition` + `enter` pair at every driver hop
|
|
138
|
+
* site so a crash between writes is impossible (ADR-020). Emits
|
|
139
|
+
* `workflow.node.transition` only after the batch commits — a rolled-back
|
|
140
|
+
* batch emits nothing (R1.6).
|
|
141
|
+
*/
|
|
142
|
+
async commitHop(from, to, trigger, transitionsTaken, phase) {
|
|
143
|
+
await this.persistence.commitTransition(this.runId, from, to, trigger, to, { transitionsTaken }, phase);
|
|
144
|
+
addSpanEvent('workflow.node.transition', {
|
|
145
|
+
runId: this.runId,
|
|
146
|
+
from,
|
|
147
|
+
to,
|
|
148
|
+
...(trigger === null ? {} : { trigger }),
|
|
149
|
+
});
|
|
150
|
+
void this.events?.emit('workflow.node.transition', {
|
|
151
|
+
runId: this.runId,
|
|
152
|
+
from,
|
|
153
|
+
to,
|
|
154
|
+
trigger,
|
|
155
|
+
externalKey: this.externalKey,
|
|
156
|
+
});
|
|
157
|
+
}
|
|
127
158
|
/** Persist a transition and emit its observability event. */
|
|
128
159
|
async recordTransition(from, to, trigger) {
|
|
129
160
|
await this.persistence.saveTransition(this.runId, from, to, trigger);
|
package/dist/service.js
CHANGED
|
@@ -191,11 +191,11 @@ export class WorkflowService {
|
|
|
191
191
|
});
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
|
-
// Commit: persist the transition +
|
|
195
|
-
//
|
|
194
|
+
// Commit: atomically persist the transition + state snapshot in a single
|
|
195
|
+
// batch (ADR-020), then emit the external-only requested event. No phase
|
|
196
|
+
// record — external transitions don't drive phase tracking.
|
|
196
197
|
const trigger = transition.trigger ?? null;
|
|
197
|
-
await lifecycle.
|
|
198
|
-
await this.persistence.saveWorkflowState(runId, toState, {});
|
|
198
|
+
await lifecycle.commitHop(currentState, toState, trigger, 0);
|
|
199
199
|
void options?.events?.emit('workflow.transition.requested', {
|
|
200
200
|
runId,
|
|
201
201
|
from: currentState,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-machine.d.ts","sourceRoot":"","sources":["../src/state-machine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD,OAAO,KAAK,EAER,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACpB,MAAM,SAAS,CAAC;AAGjB,yDAAyD;AACzD,MAAM,WAAW,yBAAyB;IACtC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC;CACpD;AAED,wEAAwE;AACxE,qBAAa,kBAAkB;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,yBAAyB;IAE/D,6DAA6D;IACvD,GAAG,CAAC,QAAQ,EAAE,uBAAuB,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAU1G,iFAAiF;IAC3E,MAAM,CACR,QAAQ,EAAE,uBAAuB,EACjC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,iBAAiB,CAAC;YAWf,IAAI;
|
|
1
|
+
{"version":3,"file":"state-machine.d.ts","sourceRoot":"","sources":["../src/state-machine.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD,OAAO,KAAK,EAER,uBAAuB,EACvB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACpB,MAAM,SAAS,CAAC;AAGjB,yDAAyD;AACzD,MAAM,WAAW,yBAAyB;IACtC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC;CACpD;AAED,wEAAwE;AACxE,qBAAa,kBAAkB;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,yBAAyB;IAE/D,6DAA6D;IACvD,GAAG,CAAC,QAAQ,EAAE,uBAAuB,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAU1G,iFAAiF;IAC3E,MAAM,CACR,QAAQ,EAAE,uBAAuB,EACjC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,EACvB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,iBAAiB,CAAC;YAWf,IAAI;CAoIrB"}
|
package/dist/state-machine.js
CHANGED
|
@@ -28,6 +28,10 @@ export class StateMachineDriver {
|
|
|
28
28
|
const iterationBound = workflow.iterationBound ?? 50;
|
|
29
29
|
const defaultOnError = workflow.defaultOnError;
|
|
30
30
|
let isResume = resumeFromState !== undefined;
|
|
31
|
+
// When `commitHop` has already persisted the new state's snapshot +
|
|
32
|
+
// phase atomically (every iteration after the first), `enter` must skip
|
|
33
|
+
// the persist half to avoid duplicate INSERT rows (ADR-020).
|
|
34
|
+
let persistedViaHop = false;
|
|
31
35
|
if (current === undefined) {
|
|
32
36
|
const label = resumeFromState ?? workflow.initialState;
|
|
33
37
|
throw new FSMError(`State "${label}" is not declared`);
|
|
@@ -36,10 +40,12 @@ export class StateMachineDriver {
|
|
|
36
40
|
if (isResume) {
|
|
37
41
|
// Resume: skip enter actions on the first iteration (already ran before pause).
|
|
38
42
|
isResume = false;
|
|
43
|
+
persistedViaHop = true; // state already persisted before the pause
|
|
39
44
|
}
|
|
40
45
|
else {
|
|
41
|
-
// 1. Persist current state snapshot before work starts
|
|
42
|
-
|
|
46
|
+
// 1. Persist current state snapshot before work starts (skipped when
|
|
47
|
+
// commitHop already persisted it atomically on the previous hop).
|
|
48
|
+
await lifecycle.enter(current.id, transitionsTaken, !persistedViaHop);
|
|
43
49
|
// 2. Execute this state's on-enter actions in declaration order.
|
|
44
50
|
const enter = options.dryRun
|
|
45
51
|
? EMPTY_OUTCOME
|
|
@@ -112,9 +118,14 @@ export class StateMachineDriver {
|
|
|
112
118
|
vars = mergeSetVars(vars, exit.result.setVars);
|
|
113
119
|
if (exit.outcome === 'fail')
|
|
114
120
|
return await lifecycle.fail(current.id, transitionsTaken, exit.result?.error);
|
|
115
|
-
// 7.
|
|
121
|
+
// 7. Atomically commit the transition + new state snapshot + phase in a
|
|
122
|
+
// single batch (ADR-020). The next iteration's `enter` becomes
|
|
123
|
+
// observe-only; a crash between writes is now impossible.
|
|
116
124
|
transitionsTaken += 1;
|
|
117
|
-
await lifecycle.
|
|
125
|
+
await lifecycle.commitHop(current.id, nextTransition.to, nextTransition.trigger ?? null, transitionsTaken, {
|
|
126
|
+
phase: nextTransition.to,
|
|
127
|
+
status: 'running',
|
|
128
|
+
});
|
|
118
129
|
if (transitionsTaken > iterationBound) {
|
|
119
130
|
return await lifecycle.fail(current.id, transitionsTaken, 'iteration-bound-exceeded');
|
|
120
131
|
}
|
|
@@ -122,6 +133,7 @@ export class StateMachineDriver {
|
|
|
122
133
|
if (nextState === undefined)
|
|
123
134
|
throw new FSMError(`Transition target "${nextTransition.to}" is not declared`);
|
|
124
135
|
current = nextState;
|
|
136
|
+
persistedViaHop = true;
|
|
125
137
|
}
|
|
126
138
|
}
|
|
127
139
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transition-flow.d.ts","sourceRoot":"","sources":["../src/transition-flow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD,OAAO,KAAK,EAER,yBAAyB,EACzB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACpB,MAAM,SAAS,CAAC;AAGjB,2DAA2D;AAC3D,MAAM,WAAW,2BAA2B;IACxC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC;CACpD;AAED,0EAA0E;AAC1E,qBAAa,oBAAoB;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,2BAA2B;IAEjE,+DAA+D;IACzD,GAAG,CAAC,QAAQ,EAAE,yBAAyB,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAU5G,qFAAqF;IAC/E,MAAM,CACR,QAAQ,EAAE,yBAAyB,EACnC,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,iBAAiB,CAAC;YAWf,IAAI;
|
|
1
|
+
{"version":3,"file":"transition-flow.d.ts","sourceRoot":"","sources":["../src/transition-flow.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AAEjD,OAAO,KAAK,EAER,yBAAyB,EACzB,0BAA0B,EAC1B,kBAAkB,EAClB,iBAAiB,EACpB,MAAM,SAAS,CAAC;AAGjB,2DAA2D;AAC3D,MAAM,WAAW,2BAA2B;IACxC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC;CACpD;AAED,0EAA0E;AAC1E,qBAAa,oBAAoB;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,2BAA2B;IAEjE,+DAA+D;IACzD,GAAG,CAAC,QAAQ,EAAE,yBAAyB,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAU5G,qFAAqF;IAC/E,MAAM,CACR,QAAQ,EAAE,yBAAyB,EACnC,KAAK,EAAE,MAAM,EACb,cAAc,EAAE,MAAM,EACtB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,OAAO,GAAE,kBAAuB,GACjC,OAAO,CAAC,iBAAiB,CAAC;YAWf,IAAI;CAmHrB"}
|
package/dist/transition-flow.js
CHANGED
|
@@ -28,6 +28,10 @@ export class TransitionFlowDriver {
|
|
|
28
28
|
const iterationBound = workflow.iterationBound ?? 50;
|
|
29
29
|
const defaultOnError = workflow.defaultOnError;
|
|
30
30
|
let isResume = resumeFromNode !== undefined;
|
|
31
|
+
// When `commitHop` has already persisted the new node's snapshot +
|
|
32
|
+
// phase atomically (every iteration after the first), `enter` must skip
|
|
33
|
+
// the persist half to avoid duplicate INSERT rows (ADR-020).
|
|
34
|
+
let persistedViaHop = false;
|
|
31
35
|
if (current === undefined) {
|
|
32
36
|
const label = resumeFromNode ?? workflow.initialNode;
|
|
33
37
|
throw new FSMError(`Node "${label}" is not declared`);
|
|
@@ -36,10 +40,12 @@ export class TransitionFlowDriver {
|
|
|
36
40
|
if (isResume) {
|
|
37
41
|
// Resume: skip enter + node action on the first iteration (already ran before pause).
|
|
38
42
|
isResume = false;
|
|
43
|
+
persistedViaHop = true; // node already persisted before the pause
|
|
39
44
|
}
|
|
40
45
|
else {
|
|
41
|
-
// 1. Persist current node snapshot before action execution
|
|
42
|
-
|
|
46
|
+
// 1. Persist current node snapshot before action execution (skipped when
|
|
47
|
+
// commitHop already persisted it atomically on the previous hop).
|
|
48
|
+
await lifecycle.enter(current.id, transitionsTaken, !persistedViaHop);
|
|
43
49
|
// 2. Execute the node action when one is configured (skipped in dry-run).
|
|
44
50
|
if (options.dryRun) {
|
|
45
51
|
if (current.action !== undefined) {
|
|
@@ -90,9 +96,14 @@ export class TransitionFlowDriver {
|
|
|
90
96
|
if (edge === undefined) {
|
|
91
97
|
return await lifecycle.fail(current.id, transitionsTaken, 'no-passing-edge');
|
|
92
98
|
}
|
|
93
|
-
// 5.
|
|
99
|
+
// 5. Atomically commit the edge transition + new node snapshot + phase in
|
|
100
|
+
// a single batch (ADR-020). The next iteration's `enter` becomes
|
|
101
|
+
// observe-only; a crash between writes is now impossible.
|
|
94
102
|
transitionsTaken += 1;
|
|
95
|
-
await lifecycle.
|
|
103
|
+
await lifecycle.commitHop(current.id, edge.to, edge.condition?.kind ?? null, transitionsTaken, {
|
|
104
|
+
phase: edge.to,
|
|
105
|
+
status: 'running',
|
|
106
|
+
});
|
|
96
107
|
// 6. Enforce the iteration bound after taking the transition.
|
|
97
108
|
if (transitionsTaken > iterationBound) {
|
|
98
109
|
return await lifecycle.fail(current.id, transitionsTaken, 'iteration-bound-exceeded');
|
|
@@ -102,6 +113,7 @@ export class TransitionFlowDriver {
|
|
|
102
113
|
if (nextNode === undefined)
|
|
103
114
|
throw new FSMError(`Edge target "${edge.to}" is not declared`);
|
|
104
115
|
current = nextNode;
|
|
116
|
+
persistedViaHop = true;
|
|
105
117
|
}
|
|
106
118
|
}
|
|
107
119
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -229,6 +229,16 @@ export interface WorkflowPersistenceAdapter {
|
|
|
229
229
|
savePhase(runId: string, phase: string, status: WorkflowStatus): Promise<void>;
|
|
230
230
|
saveTransition(runId: string, from: string, to: string, trigger: string | null): Promise<void>;
|
|
231
231
|
saveWorkflowState(runId: string, state: string, data: Record<string, unknown>): Promise<void>;
|
|
232
|
+
/**
|
|
233
|
+
* Atomically persist a transition together with its resulting state snapshot
|
|
234
|
+
* and optional phase record in a single DB batch — either all commit or none.
|
|
235
|
+
* Prevents partial-state windows when the process fails between saveTransition()
|
|
236
|
+
* and saveWorkflowState().
|
|
237
|
+
*/
|
|
238
|
+
commitTransition(runId: string, from: string, to: string, trigger: string | null, state: string, data: Record<string, unknown>, phase?: {
|
|
239
|
+
phase: string;
|
|
240
|
+
status: WorkflowStatus;
|
|
241
|
+
}): Promise<void>;
|
|
232
242
|
/** Two-phase action persistence: insert a running row at action start. Returns the action row id. */
|
|
233
243
|
saveActionStart(runId: string, node: string, kind: string): Promise<string>;
|
|
234
244
|
/** Finalize an action row with duration, ok, result. */
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/ts-dual-workflow-engine",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.8",
|
|
4
4
|
"description": "@gobing-ai/ts-dual-workflow-engine — State-machine and transition-flow workflow runtime.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
"release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-dual-workflow-engine-v<version> && git push --tags' && exit 1"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@gobing-ai/ts-db": "^0.4.
|
|
52
|
-
"@gobing-ai/ts-infra": "^0.4.
|
|
53
|
-
"@gobing-ai/ts-runtime": "^0.4.
|
|
51
|
+
"@gobing-ai/ts-db": "^0.4.8",
|
|
52
|
+
"@gobing-ai/ts-infra": "^0.4.8",
|
|
53
|
+
"@gobing-ai/ts-runtime": "^0.4.8",
|
|
54
54
|
"zod": "^4.1.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
package/src/extensions.ts
CHANGED
|
@@ -51,6 +51,12 @@ export interface LoadWorkflowExtensionsOptions {
|
|
|
51
51
|
* own; the embedder supplies the import policy.
|
|
52
52
|
*/
|
|
53
53
|
readonly moduleLoader: (absPath: string) => Promise<Record<string, unknown>>;
|
|
54
|
+
/**
|
|
55
|
+
* Optional canonical-path resolver for symlink-safe confinement (ADR-022).
|
|
56
|
+
* Forwarded to the shared loader. Node-facing embedders should supply
|
|
57
|
+
* `createNodeFileSystem().realPath` to enable symlink-escape rejection.
|
|
58
|
+
*/
|
|
59
|
+
readonly realPath?: (absPath: string) => string;
|
|
54
60
|
}
|
|
55
61
|
|
|
56
62
|
/**
|
|
@@ -102,6 +108,7 @@ export async function loadWorkflowExtensionsIntoHost(
|
|
|
102
108
|
allowExtensions: options.allowExtensions,
|
|
103
109
|
logger: options.logger,
|
|
104
110
|
moduleLoader: options.moduleLoader,
|
|
111
|
+
realPath: options.realPath,
|
|
105
112
|
};
|
|
106
113
|
|
|
107
114
|
await loadExtensionModules<WorkflowExtensionKind>(sharedRefs, sharedOptions, async (sharedRef, extension) => {
|
package/src/persistence.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DbAdapter } from '@gobing-ai/ts-db';
|
|
1
|
+
import type { DbAdapter, DbBatchOp } from '@gobing-ai/ts-db';
|
|
2
2
|
import { RunCollisionError } from './errors';
|
|
3
3
|
import { WORKFLOW_ENGINE_SCHEMA_SQL } from './schema-sql';
|
|
4
4
|
import type {
|
|
@@ -68,51 +68,86 @@ export class DbWorkflowPersistenceAdapter implements WorkflowPersistenceAdapter
|
|
|
68
68
|
|
|
69
69
|
/** Save one phase/state execution record. */
|
|
70
70
|
async savePhase(runId: string, phase: string, status: WorkflowStatus): Promise<void> {
|
|
71
|
-
const
|
|
72
|
-
await this.db.run(
|
|
73
|
-
`INSERT INTO phase_runs (id, run_id, phase, status, started_at, completed_at, created_at, updated_at)
|
|
74
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
75
|
-
`${runId}:phase:${phase}:${crypto.randomUUID()}`,
|
|
76
|
-
runId,
|
|
77
|
-
phase,
|
|
78
|
-
status,
|
|
79
|
-
new Date(now).toISOString(),
|
|
80
|
-
status === 'running' ? null : new Date(now).toISOString(),
|
|
81
|
-
now,
|
|
82
|
-
now,
|
|
83
|
-
);
|
|
71
|
+
const op = this.phaseRow(runId, phase, status, Date.now());
|
|
72
|
+
await this.db.run(op.sql, ...op.params);
|
|
84
73
|
}
|
|
85
74
|
|
|
86
75
|
/** Save one transition record. */
|
|
87
76
|
async saveTransition(runId: string, from: string, to: string, trigger: string | null): Promise<void> {
|
|
88
|
-
const
|
|
89
|
-
await this.db.run(
|
|
90
|
-
`INSERT INTO transition_runs (id, run_id, from_state, to_state, trigger, status, created_at, updated_at)
|
|
91
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
92
|
-
`${runId}:transition:${from}:${to}:${crypto.randomUUID()}`,
|
|
93
|
-
runId,
|
|
94
|
-
from,
|
|
95
|
-
to,
|
|
96
|
-
trigger,
|
|
97
|
-
'done',
|
|
98
|
-
now,
|
|
99
|
-
now,
|
|
100
|
-
);
|
|
77
|
+
const op = this.transitionRow(runId, from, to, trigger, Date.now());
|
|
78
|
+
await this.db.run(op.sql, ...op.params);
|
|
101
79
|
}
|
|
102
80
|
|
|
103
81
|
/** Save the latest workflow state snapshot. */
|
|
104
82
|
async saveWorkflowState(runId: string, state: string, data: Record<string, unknown>): Promise<void> {
|
|
83
|
+
const op = this.stateRow(runId, state, data, Date.now());
|
|
84
|
+
await this.db.run(op.sql, ...op.params);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async commitTransition(
|
|
88
|
+
runId: string,
|
|
89
|
+
from: string,
|
|
90
|
+
to: string,
|
|
91
|
+
trigger: string | null,
|
|
92
|
+
state: string,
|
|
93
|
+
data: Record<string, unknown>,
|
|
94
|
+
phase?: { phase: string; status: WorkflowStatus },
|
|
95
|
+
): Promise<void> {
|
|
96
|
+
await this.ensureSchema();
|
|
105
97
|
const now = Date.now();
|
|
106
|
-
|
|
107
|
-
|
|
98
|
+
const ops: DbBatchOp[] = [
|
|
99
|
+
this.transitionRow(runId, from, to, trigger, now),
|
|
100
|
+
this.stateRow(runId, state, data, now),
|
|
101
|
+
];
|
|
102
|
+
if (phase) {
|
|
103
|
+
ops.push(this.phaseRow(runId, phase.phase, phase.status, now));
|
|
104
|
+
}
|
|
105
|
+
await this.db.batch(ops);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Row builders shared by the individual save methods and commitTransition so
|
|
109
|
+
// the single-write and atomic-batch paths can never drift apart (ADR-020).
|
|
110
|
+
|
|
111
|
+
private transitionRow(runId: string, from: string, to: string, trigger: string | null, now: number): DbBatchOp {
|
|
112
|
+
return {
|
|
113
|
+
sql: `INSERT INTO transition_runs (id, run_id, from_state, to_state, trigger, status, created_at, updated_at)
|
|
114
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
115
|
+
params: [
|
|
116
|
+
`${runId}:transition:${from}:${to}:${crypto.randomUUID()}`,
|
|
117
|
+
runId,
|
|
118
|
+
from,
|
|
119
|
+
to,
|
|
120
|
+
trigger,
|
|
121
|
+
'done',
|
|
122
|
+
now,
|
|
123
|
+
now,
|
|
124
|
+
],
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private stateRow(runId: string, state: string, data: Record<string, unknown>, now: number): DbBatchOp {
|
|
129
|
+
return {
|
|
130
|
+
sql: `INSERT INTO workflow_states (id, run_id, state, data_json, created_at, updated_at)
|
|
108
131
|
VALUES (?, ?, ?, ?, ?, ?)`,
|
|
109
|
-
`${runId}:state:${state}:${crypto.randomUUID()}`,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
132
|
+
params: [`${runId}:state:${state}:${crypto.randomUUID()}`, runId, state, JSON.stringify(data), now, now],
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private phaseRow(runId: string, phase: string, status: WorkflowStatus, now: number): DbBatchOp {
|
|
137
|
+
return {
|
|
138
|
+
sql: `INSERT INTO phase_runs (id, run_id, phase, status, started_at, completed_at, created_at, updated_at)
|
|
139
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
140
|
+
params: [
|
|
141
|
+
`${runId}:phase:${phase}:${crypto.randomUUID()}`,
|
|
142
|
+
runId,
|
|
143
|
+
phase,
|
|
144
|
+
status,
|
|
145
|
+
new Date(now).toISOString(),
|
|
146
|
+
status === 'running' ? null : new Date(now).toISOString(),
|
|
147
|
+
now,
|
|
148
|
+
now,
|
|
149
|
+
],
|
|
150
|
+
};
|
|
116
151
|
}
|
|
117
152
|
|
|
118
153
|
/** Insert a running action row. Returns the row id for later finalization. */
|
|
@@ -318,6 +353,23 @@ export class MemoryWorkflowPersistenceAdapter implements WorkflowPersistenceAdap
|
|
|
318
353
|
this.states.push({ runId, state, data });
|
|
319
354
|
}
|
|
320
355
|
|
|
356
|
+
async commitTransition(
|
|
357
|
+
runId: string,
|
|
358
|
+
from: string,
|
|
359
|
+
to: string,
|
|
360
|
+
trigger: string | null,
|
|
361
|
+
state: string,
|
|
362
|
+
data: Record<string, unknown>,
|
|
363
|
+
phase?: { phase: string; status: WorkflowStatus },
|
|
364
|
+
): Promise<void> {
|
|
365
|
+
// Memory adapter: synchronous pushes are atomic by nature.
|
|
366
|
+
this.transitions.push({ runId, from, to, trigger });
|
|
367
|
+
this.states.push({ runId, state, data });
|
|
368
|
+
if (phase) {
|
|
369
|
+
this.phases.push({ runId, phase: phase.phase, status: phase.status });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
321
373
|
/** Load a single run by id. */
|
|
322
374
|
async loadRun(runId: string): Promise<WorkflowRunRecord | undefined> {
|
|
323
375
|
return this.runs.get(runId);
|
package/src/run-lifecycle.ts
CHANGED
|
@@ -183,10 +183,17 @@ export class RunLifecycle {
|
|
|
183
183
|
);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
/**
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
/**
|
|
187
|
+
* Persist the current state/node snapshot and mark its phase running.
|
|
188
|
+
* When `persist` is false (set by driver loops after `commitHop` already
|
|
189
|
+
* persisted the state atomically), only emit the enter observability —
|
|
190
|
+
* skip `saveWorkflowState` + `savePhase` to avoid duplicate INSERT rows.
|
|
191
|
+
*/
|
|
192
|
+
async enter(stateOrNodeId: string, transitionsTaken: number, persist = true): Promise<void> {
|
|
193
|
+
if (persist) {
|
|
194
|
+
await this.persistence.saveWorkflowState(this.runId, stateOrNodeId, { transitionsTaken });
|
|
195
|
+
await this.persistence.savePhase(this.runId, stateOrNodeId, 'running');
|
|
196
|
+
}
|
|
190
197
|
addSpanEvent('workflow.node.enter', {
|
|
191
198
|
runId: this.runId,
|
|
192
199
|
node: stateOrNodeId,
|
|
@@ -199,6 +206,37 @@ export class RunLifecycle {
|
|
|
199
206
|
});
|
|
200
207
|
}
|
|
201
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Atomically commit a transition together with the resulting state snapshot
|
|
211
|
+
* and optional phase record, then emit the transition observability event.
|
|
212
|
+
* Replaces the old `recordTransition` + `enter` pair at every driver hop
|
|
213
|
+
* site so a crash between writes is impossible (ADR-020). Emits
|
|
214
|
+
* `workflow.node.transition` only after the batch commits — a rolled-back
|
|
215
|
+
* batch emits nothing (R1.6).
|
|
216
|
+
*/
|
|
217
|
+
async commitHop(
|
|
218
|
+
from: string,
|
|
219
|
+
to: string,
|
|
220
|
+
trigger: string | null,
|
|
221
|
+
transitionsTaken: number,
|
|
222
|
+
phase?: { phase: string; status: WorkflowStatus },
|
|
223
|
+
): Promise<void> {
|
|
224
|
+
await this.persistence.commitTransition(this.runId, from, to, trigger, to, { transitionsTaken }, phase);
|
|
225
|
+
addSpanEvent('workflow.node.transition', {
|
|
226
|
+
runId: this.runId,
|
|
227
|
+
from,
|
|
228
|
+
to,
|
|
229
|
+
...(trigger === null ? {} : { trigger }),
|
|
230
|
+
});
|
|
231
|
+
void this.events?.emit('workflow.node.transition', {
|
|
232
|
+
runId: this.runId,
|
|
233
|
+
from,
|
|
234
|
+
to,
|
|
235
|
+
trigger,
|
|
236
|
+
externalKey: this.externalKey,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
202
240
|
/** Persist a transition and emit its observability event. */
|
|
203
241
|
async recordTransition(from: string, to: string, trigger: string | null): Promise<void> {
|
|
204
242
|
await this.persistence.saveTransition(this.runId, from, to, trigger);
|
package/src/service.ts
CHANGED
|
@@ -257,11 +257,11 @@ export class WorkflowService {
|
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
-
// Commit: persist the transition +
|
|
261
|
-
//
|
|
260
|
+
// Commit: atomically persist the transition + state snapshot in a single
|
|
261
|
+
// batch (ADR-020), then emit the external-only requested event. No phase
|
|
262
|
+
// record — external transitions don't drive phase tracking.
|
|
262
263
|
const trigger = transition.trigger ?? null;
|
|
263
|
-
await lifecycle.
|
|
264
|
-
await this.persistence.saveWorkflowState(runId, toState, {});
|
|
264
|
+
await lifecycle.commitHop(currentState, toState, trigger, 0);
|
|
265
265
|
void options?.events?.emit('workflow.transition.requested', {
|
|
266
266
|
runId,
|
|
267
267
|
from: currentState,
|
package/src/state-machine.ts
CHANGED
|
@@ -67,6 +67,10 @@ export class StateMachineDriver {
|
|
|
67
67
|
const iterationBound = workflow.iterationBound ?? 50;
|
|
68
68
|
const defaultOnError = workflow.defaultOnError;
|
|
69
69
|
let isResume = resumeFromState !== undefined;
|
|
70
|
+
// When `commitHop` has already persisted the new state's snapshot +
|
|
71
|
+
// phase atomically (every iteration after the first), `enter` must skip
|
|
72
|
+
// the persist half to avoid duplicate INSERT rows (ADR-020).
|
|
73
|
+
let persistedViaHop = false;
|
|
70
74
|
|
|
71
75
|
if (current === undefined) {
|
|
72
76
|
const label = resumeFromState ?? workflow.initialState;
|
|
@@ -77,9 +81,11 @@ export class StateMachineDriver {
|
|
|
77
81
|
if (isResume) {
|
|
78
82
|
// Resume: skip enter actions on the first iteration (already ran before pause).
|
|
79
83
|
isResume = false;
|
|
84
|
+
persistedViaHop = true; // state already persisted before the pause
|
|
80
85
|
} else {
|
|
81
|
-
// 1. Persist current state snapshot before work starts
|
|
82
|
-
|
|
86
|
+
// 1. Persist current state snapshot before work starts (skipped when
|
|
87
|
+
// commitHop already persisted it atomically on the previous hop).
|
|
88
|
+
await lifecycle.enter(current.id, transitionsTaken, !persistedViaHop);
|
|
83
89
|
|
|
84
90
|
// 2. Execute this state's on-enter actions in declaration order.
|
|
85
91
|
const enter = options.dryRun
|
|
@@ -159,15 +165,21 @@ export class StateMachineDriver {
|
|
|
159
165
|
if (exit.result?.setVars) vars = mergeSetVars(vars, exit.result.setVars);
|
|
160
166
|
if (exit.outcome === 'fail') return await lifecycle.fail(current.id, transitionsTaken, exit.result?.error);
|
|
161
167
|
|
|
162
|
-
// 7.
|
|
168
|
+
// 7. Atomically commit the transition + new state snapshot + phase in a
|
|
169
|
+
// single batch (ADR-020). The next iteration's `enter` becomes
|
|
170
|
+
// observe-only; a crash between writes is now impossible.
|
|
163
171
|
transitionsTaken += 1;
|
|
164
|
-
await lifecycle.
|
|
172
|
+
await lifecycle.commitHop(current.id, nextTransition.to, nextTransition.trigger ?? null, transitionsTaken, {
|
|
173
|
+
phase: nextTransition.to,
|
|
174
|
+
status: 'running',
|
|
175
|
+
});
|
|
165
176
|
if (transitionsTaken > iterationBound) {
|
|
166
177
|
return await lifecycle.fail(current.id, transitionsTaken, 'iteration-bound-exceeded');
|
|
167
178
|
}
|
|
168
179
|
const nextState = states.get(nextTransition.to);
|
|
169
180
|
if (nextState === undefined) throw new FSMError(`Transition target "${nextTransition.to}" is not declared`);
|
|
170
181
|
current = nextState;
|
|
182
|
+
persistedViaHop = true;
|
|
171
183
|
}
|
|
172
184
|
}
|
|
173
185
|
}
|
package/src/transition-flow.ts
CHANGED
|
@@ -67,6 +67,10 @@ export class TransitionFlowDriver {
|
|
|
67
67
|
const iterationBound = workflow.iterationBound ?? 50;
|
|
68
68
|
const defaultOnError = workflow.defaultOnError;
|
|
69
69
|
let isResume = resumeFromNode !== undefined;
|
|
70
|
+
// When `commitHop` has already persisted the new node's snapshot +
|
|
71
|
+
// phase atomically (every iteration after the first), `enter` must skip
|
|
72
|
+
// the persist half to avoid duplicate INSERT rows (ADR-020).
|
|
73
|
+
let persistedViaHop = false;
|
|
70
74
|
|
|
71
75
|
if (current === undefined) {
|
|
72
76
|
const label = resumeFromNode ?? workflow.initialNode;
|
|
@@ -77,9 +81,11 @@ export class TransitionFlowDriver {
|
|
|
77
81
|
if (isResume) {
|
|
78
82
|
// Resume: skip enter + node action on the first iteration (already ran before pause).
|
|
79
83
|
isResume = false;
|
|
84
|
+
persistedViaHop = true; // node already persisted before the pause
|
|
80
85
|
} else {
|
|
81
|
-
// 1. Persist current node snapshot before action execution
|
|
82
|
-
|
|
86
|
+
// 1. Persist current node snapshot before action execution (skipped when
|
|
87
|
+
// commitHop already persisted it atomically on the previous hop).
|
|
88
|
+
await lifecycle.enter(current.id, transitionsTaken, !persistedViaHop);
|
|
83
89
|
|
|
84
90
|
// 2. Execute the node action when one is configured (skipped in dry-run).
|
|
85
91
|
if (options.dryRun) {
|
|
@@ -138,9 +144,14 @@ export class TransitionFlowDriver {
|
|
|
138
144
|
return await lifecycle.fail(current.id, transitionsTaken, 'no-passing-edge');
|
|
139
145
|
}
|
|
140
146
|
|
|
141
|
-
// 5.
|
|
147
|
+
// 5. Atomically commit the edge transition + new node snapshot + phase in
|
|
148
|
+
// a single batch (ADR-020). The next iteration's `enter` becomes
|
|
149
|
+
// observe-only; a crash between writes is now impossible.
|
|
142
150
|
transitionsTaken += 1;
|
|
143
|
-
await lifecycle.
|
|
151
|
+
await lifecycle.commitHop(current.id, edge.to, edge.condition?.kind ?? null, transitionsTaken, {
|
|
152
|
+
phase: edge.to,
|
|
153
|
+
status: 'running',
|
|
154
|
+
});
|
|
144
155
|
|
|
145
156
|
// 6. Enforce the iteration bound after taking the transition.
|
|
146
157
|
if (transitionsTaken > iterationBound) {
|
|
@@ -151,6 +162,7 @@ export class TransitionFlowDriver {
|
|
|
151
162
|
const nextNode = nodes.get(edge.to);
|
|
152
163
|
if (nextNode === undefined) throw new FSMError(`Edge target "${edge.to}" is not declared`);
|
|
153
164
|
current = nextNode;
|
|
165
|
+
persistedViaHop = true;
|
|
154
166
|
}
|
|
155
167
|
}
|
|
156
168
|
}
|
package/src/types.ts
CHANGED
|
@@ -259,6 +259,21 @@ export interface WorkflowPersistenceAdapter {
|
|
|
259
259
|
savePhase(runId: string, phase: string, status: WorkflowStatus): Promise<void>;
|
|
260
260
|
saveTransition(runId: string, from: string, to: string, trigger: string | null): Promise<void>;
|
|
261
261
|
saveWorkflowState(runId: string, state: string, data: Record<string, unknown>): Promise<void>;
|
|
262
|
+
/**
|
|
263
|
+
* Atomically persist a transition together with its resulting state snapshot
|
|
264
|
+
* and optional phase record in a single DB batch — either all commit or none.
|
|
265
|
+
* Prevents partial-state windows when the process fails between saveTransition()
|
|
266
|
+
* and saveWorkflowState().
|
|
267
|
+
*/
|
|
268
|
+
commitTransition(
|
|
269
|
+
runId: string,
|
|
270
|
+
from: string,
|
|
271
|
+
to: string,
|
|
272
|
+
trigger: string | null,
|
|
273
|
+
state: string,
|
|
274
|
+
data: Record<string, unknown>,
|
|
275
|
+
phase?: { phase: string; status: WorkflowStatus },
|
|
276
|
+
): Promise<void>;
|
|
262
277
|
/** Two-phase action persistence: insert a running row at action start. Returns the action row id. */
|
|
263
278
|
saveActionStart(runId: string, node: string, kind: string): Promise<string>;
|
|
264
279
|
/** Finalize an action row with duration, ok, result. */
|