fyflow-orchestration 0.9.0 → 0.10.0
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/AGENTS.md +160 -24
- package/README.md +1 -1
- package/dist/browser/chunks/{chunk-3U4UZC34.js → chunk-NA6QQAZ2.js} +469 -113
- package/dist/browser/index.js +5 -1
- package/dist/browser/scheduler/index.js +13 -2
- package/dist/node/chunks/{chunk-3U4UZC34.js → chunk-NA6QQAZ2.js} +469 -113
- package/dist/node/index.js +5 -1
- package/dist/node/scheduler/index.js +13 -2
- package/dist/types/src/definition/regions.d.ts +12 -0
- package/dist/types/src/driver/driver.d.ts +185 -10
- package/dist/types/src/scheduler/runtime.d.ts +17 -1
- package/package.json +1 -1
package/dist/node/index.js
CHANGED
|
@@ -99,6 +99,7 @@ import {
|
|
|
99
99
|
groupsNamedBy,
|
|
100
100
|
innermostMapOf,
|
|
101
101
|
innermostRegionOf,
|
|
102
|
+
inputOf,
|
|
102
103
|
isDerivation,
|
|
103
104
|
isSuspended,
|
|
104
105
|
isTerminal,
|
|
@@ -141,9 +142,10 @@ import {
|
|
|
141
142
|
transformShapeProblem,
|
|
142
143
|
validateDefinition,
|
|
143
144
|
violatedRules,
|
|
145
|
+
visibilityKey,
|
|
144
146
|
witness,
|
|
145
147
|
workersOf
|
|
146
|
-
} from "./chunks/chunk-
|
|
148
|
+
} from "./chunks/chunk-NA6QQAZ2.js";
|
|
147
149
|
|
|
148
150
|
// src/definition/assist.ts
|
|
149
151
|
var TYPES = ["object", "array", "string", "number", "integer", "boolean", "null"];
|
|
@@ -391,6 +393,7 @@ export {
|
|
|
391
393
|
groupsNamedBy,
|
|
392
394
|
innermostMapOf,
|
|
393
395
|
innermostRegionOf,
|
|
396
|
+
inputOf,
|
|
394
397
|
isDerivation,
|
|
395
398
|
isSuspended,
|
|
396
399
|
isTerminal,
|
|
@@ -440,6 +443,7 @@ export {
|
|
|
440
443
|
tsTypeOf,
|
|
441
444
|
validateDefinition,
|
|
442
445
|
violatedRules,
|
|
446
|
+
visibilityKey,
|
|
443
447
|
witness,
|
|
444
448
|
workersOf
|
|
445
449
|
};
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
derivePool,
|
|
12
12
|
emptyState,
|
|
13
13
|
groupsNamedBy,
|
|
14
|
+
inputOf,
|
|
14
15
|
isDerivation,
|
|
15
16
|
isTerminal,
|
|
16
17
|
languagesOf,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
retryPolicyProblem,
|
|
26
27
|
validateDefinition,
|
|
27
28
|
workersOf
|
|
28
|
-
} from "../chunks/chunk-
|
|
29
|
+
} from "../chunks/chunk-NA6QQAZ2.js";
|
|
29
30
|
|
|
30
31
|
// src/scheduler/bootstrap.ts
|
|
31
32
|
import { WorkerManager } from "fyflow-scheduler";
|
|
@@ -350,6 +351,12 @@ function createRuntime(document, code = {}, options = {}) {
|
|
|
350
351
|
return p;
|
|
351
352
|
}
|
|
352
353
|
} : view;
|
|
354
|
+
const reading = {
|
|
355
|
+
...registry,
|
|
356
|
+
joinPolicy: startOptions.joinPolicy ?? liveToken,
|
|
357
|
+
...options.defaultRetryPolicy !== void 0 ? { defaultRetryPolicy: options.defaultRetryPolicy } : {},
|
|
358
|
+
...options.validateBoundaries !== void 0 ? { validateBoundaries: options.validateBoundaries } : {}
|
|
359
|
+
};
|
|
353
360
|
const driver = new Driver(flow, {
|
|
354
361
|
runId,
|
|
355
362
|
scheduler: scheduler2,
|
|
@@ -408,7 +415,11 @@ function createRuntime(document, code = {}, options = {}) {
|
|
|
408
415
|
catchUp();
|
|
409
416
|
const entry = acc.entry(taskId);
|
|
410
417
|
return entry === void 0 ? null : freezeDeep(entry);
|
|
411
|
-
}
|
|
418
|
+
},
|
|
419
|
+
// **`RN-R45` — the reading, over this run's own events** (W205, P42 decision 7): the export, handed the options this
|
|
420
|
+
// function built the run's driver with — `registry`, its `joinPolicy`, and the two runtime-wide ones — so a reading
|
|
421
|
+
// builds the driver `start` built. The live driver is only READ (`checkpoint()`, a copy).
|
|
422
|
+
inputOf: (target) => inputOf(flow, reading, driver.checkpoint(), target)
|
|
412
423
|
};
|
|
413
424
|
}
|
|
414
425
|
}
|
|
@@ -309,6 +309,18 @@ export declare function sharedRegionDepth(regions: RegionSets[], a: string, b: s
|
|
|
309
309
|
* own, so for an edge's `needs` this is the node the edge hangs on (`FD-C3`).
|
|
310
310
|
*/
|
|
311
311
|
export declare function outputIsVisible(regions: RegionSets[], readerId: string, readerPath: string, targetId: string, targetPath: string): boolean;
|
|
312
|
+
/**
|
|
313
|
+
* **The key an output is filed under for a reader that shares `depth` regions with it** — the first `depth` SEGMENTS of
|
|
314
|
+
* its branch path, joined, or `null` for a path with fewer (`-` has none) — W199,
|
|
315
|
+
* [ADR 0124](../../doc/decisions/0124-a-reference-read-costs-its-answer.md). An output is visible to a reader exactly
|
|
316
|
+
* when the two have the SAME key at their shared depth ({@link outputIsVisible}), so the driver files a node's outputs
|
|
317
|
+
* under it once and a read takes its group, where it searched every output (Q735).
|
|
318
|
+
*
|
|
319
|
+
* **Segment-wise and never by string** (ADR 0044 rule 3): a segment holds no `.`, so two keys of `depth` segments are
|
|
320
|
+
* equal exactly when their segments are — `1` and `10` stay distinct, and `1.0` at depth 1 is `1`. At depth 0 every path
|
|
321
|
+
* has the one key `''`.
|
|
322
|
+
*/
|
|
323
|
+
export declare function visibilityKey(path: string, depth: number): string | null;
|
|
312
324
|
/**
|
|
313
325
|
* Which region a node belongs to, and via which branch — the value the task id's
|
|
314
326
|
* `branchPath` segment is derived from (`spec/task-id.md` section 1).
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* driver turns out to need a second copy of run truth, that is a finding about the
|
|
13
13
|
* spec rather than a thing to build.
|
|
14
14
|
*/
|
|
15
|
-
import type { Finding, FlowDefinition, GateTypes, Languages, NodeTypes, Pools, TerminalState, Workers } from '../definition/types.js';
|
|
15
|
+
import type { Finding, FlowDefinition, FlowNode, GateTypes, Languages, NodeTypes, Pools, TerminalState, Workers } from '../definition/types.js';
|
|
16
16
|
import { type SchemaFinding } from '../definition/schema.js';
|
|
17
17
|
import { type LogRecord, type OutcomeRecord } from './log.js';
|
|
18
18
|
import { type PredicateRegistry } from './routing.js';
|
|
@@ -377,6 +377,68 @@ export declare class ResumeRefused extends Error {
|
|
|
377
377
|
* function, two callers, so the two cannot refuse different checkpoints.
|
|
378
378
|
*/
|
|
379
379
|
export declare function resumeRefusal(definition: FlowDefinition, checkpoint: unknown, runId?: string): ResumeRefused | null;
|
|
380
|
+
/**
|
|
381
|
+
* **What a reading asks about** — `RN-R45`, P42 decision 2 and stop-and-ask (c1). A node id and a branch path, for ANY node
|
|
382
|
+
* kind the definition has; a node id ALONE, which asks for every path the run reached it on (decision 4); or a task id —
|
|
383
|
+
* a task's or an ask's — which names both, as `spec/task-id.md` mints it (attempt `0`).
|
|
384
|
+
*/
|
|
385
|
+
export type InputTarget = {
|
|
386
|
+
readonly taskId: string;
|
|
387
|
+
} | {
|
|
388
|
+
readonly nodeId: string;
|
|
389
|
+
readonly branchPath?: string;
|
|
390
|
+
};
|
|
391
|
+
/**
|
|
392
|
+
* **Which value a node was handed** — `RN-R45`, P42 decision 3: a task's `envelope` (`{ settings, input, transform? }`,
|
|
393
|
+
* `FD-C5`, `FD-C6` — what `dispatch` built for `createTask`, before any transform); the value `enter` composed for an ask,
|
|
394
|
+
* a gate, a fanout, a map or an end (`received`); and for a JOIN, an `arrival` at an arriving branch's path — what that
|
|
395
|
+
* branch carried in, a LATE arrival after an early firing included — or the `collection` it handed on when it fired, at
|
|
396
|
+
* its opener's path.
|
|
397
|
+
*/
|
|
398
|
+
export type HandedAs = 'envelope' | 'received' | 'arrival' | 'collection';
|
|
399
|
+
/** **One thing a node was handed, on one branch path** — `RN-R45`. */
|
|
400
|
+
export interface NodeInput {
|
|
401
|
+
readonly nodeId: string;
|
|
402
|
+
readonly kind: FlowNode['kind'];
|
|
403
|
+
/** The branch path it was handed on — a task's and an ask's the one their id carries; a join's arrival the arriving branch's. */
|
|
404
|
+
readonly branchPath: string;
|
|
405
|
+
readonly handed: HandedAs;
|
|
406
|
+
/**
|
|
407
|
+
* The value, AS THE READING'S DRIVER BUILT IT: not copied again and not frozen beyond what the composition already
|
|
408
|
+
* froze — a value a `needs` reference composed is that driver's shared frozen copy (ADR 0126), the rest is the caller's
|
|
409
|
+
* own. It reaches nothing a live run holds: the reading's driver is its own and is dropped when the reading returns.
|
|
410
|
+
*/
|
|
411
|
+
readonly value: unknown;
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* **Why a reading has no answer** — `RN-R45`, P42 decision 5. Returned, never thrown.
|
|
415
|
+
*
|
|
416
|
+
* | reason | when | `index` |
|
|
417
|
+
* |---|---|---|
|
|
418
|
+
* | `format` | the run is not `{ header, events[] }`, its `header.format` is not {@link CHECKPOINT_FORMAT}, its header's three fields are not format 1's types, its `runId` is not a run id (`ID-R2`), or its entry payload does not clone — `resumeRefusal`'s header half, the one function | `null` |
|
|
419
|
+
* | `flow` | `header.flowId` or `header.version` is not the definition's — no migration | `null` |
|
|
420
|
+
* | `unknown` | the target cannot name anything in this run: a node id the definition lacks, a branch path that is not `spec/task-id.md`'s grammar, or a task id that does not parse, names another run, an attempt other than `0`, or a node that is neither a task nor an ask | `null` |
|
|
421
|
+
* | `not-reached` | the replay did not hand the target anything within the stored prefix a reading verifies — which ends at the first `halted`, `stopped` or `cancelRequested` element, or at the stored events' end | the prefix's length |
|
|
422
|
+
* | `diverged` | the replay produced something other than a stored element BEFORE it reached the target — `RN-R20`'s refusal, by its position | that position |
|
|
423
|
+
*/
|
|
424
|
+
export type InputRefusalReason = 'format' | 'flow' | 'unknown' | 'not-reached' | 'diverged';
|
|
425
|
+
/** **What a reading returns** — every answer for its target, in the order the replay reached them, or why there is none. */
|
|
426
|
+
export type InputReading = {
|
|
427
|
+
readonly ok: true;
|
|
428
|
+
readonly inputs: readonly NodeInput[];
|
|
429
|
+
} | {
|
|
430
|
+
readonly ok: false;
|
|
431
|
+
readonly reason: InputRefusalReason;
|
|
432
|
+
readonly index: number | null;
|
|
433
|
+
readonly message: string;
|
|
434
|
+
};
|
|
435
|
+
/**
|
|
436
|
+
* **What a reading builds its driver from** — the `DriverOptions` a run's routing reads, and nothing else (`RN-R45`, P42
|
|
437
|
+
* decision 1): the registry's members — `runtime.registry` IS this minus `joinPolicy` — and the three options a runtime
|
|
438
|
+
* adds. **No callback, no scheduler, no `createTask`, no run id**: a member not named here is not read, so a reading cannot
|
|
439
|
+
* call a consumer. `joinPolicy` omitted is `liveToken`, as `runtime.start` and `runtime.resume` default it.
|
|
440
|
+
*/
|
|
441
|
+
export type InputOfOptions = Pick<DriverOptions, 'predicates' | 'workers' | 'nodeTypes' | 'pools' | 'groups' | 'languages' | 'gateTypes' | 'joinPolicy' | 'defaultRetryPolicy' | 'validateBoundaries'>;
|
|
380
442
|
/**
|
|
381
443
|
* A settlement carrying THIS driver's run id for a task it never dispatched —
|
|
382
444
|
* `ID-R4`, and the inbound counterpart of `ID-R1`
|
|
@@ -1054,7 +1116,8 @@ export declare class Driver {
|
|
|
1054
1116
|
* - **`held`** — map id → the instance's branch path → its item. Written by the MAP ARM, where the instance opens —
|
|
1055
1117
|
* by a run and by a resume's replay alike, so a replay REBUILDS it (`RN-R19`; P38's brief: *taken at rest*, never by
|
|
1056
1118
|
* a caller's call) — **BY REFERENCE to the element the map received** (the driver's own: an edge-hop clone, or a
|
|
1057
|
-
* `needs` composition's
|
|
1119
|
+
* `needs` composition's value — since W202 an element of a reference's SHARED copy, which this keeps alive while held),
|
|
1120
|
+
* and as its OWN copy only where `kept` says that element is the caller's
|
|
1058
1121
|
* (P35 (f)'s reason for `asked.input`). Every READ clones (`JP-R11`), so no node receives the held object.
|
|
1059
1122
|
* **Released when NOTHING of that instance may still be entered** — its own branch terminal (`branch.arrived`,
|
|
1060
1123
|
* `branch.lost` or `branch.exited`, `JP-R5`: one per opened branch) AND no branch of a region NESTED in it still live
|
|
@@ -1397,10 +1460,47 @@ export declare class Driver {
|
|
|
1397
1460
|
private replayStored;
|
|
1398
1461
|
/** One fed element — a settlement, a resolution, an earlier resume, a stop, a pause, an unpause or an attempt — at stored position `i`. */
|
|
1399
1462
|
private feedOne;
|
|
1400
|
-
/**
|
|
1463
|
+
/**
|
|
1464
|
+
* The listener's boundary, for the replay: a refusal routing raises stops the run (`OR-R10`); a refused resume passes
|
|
1465
|
+
* through — and so does a reading's cut (`InputCut`, W205), which is no refusal and halts nothing.
|
|
1466
|
+
*/
|
|
1401
1467
|
private feed;
|
|
1402
1468
|
/** `RN-R20`'s refusal, naming the position and both elements there. */
|
|
1403
1469
|
private diverged;
|
|
1470
|
+
/**
|
|
1471
|
+
* **A cut site offers what a node was handed** (`RN-R45`, W205). An offer for another path is dropped. `own` is the
|
|
1472
|
+
* position of the node's OWN element where it is already appended — a task's `dispatched`, a join's firing — and `null`
|
|
1473
|
+
* where it is the NEXT append, which `append` confirms (`Reading.pending`). Either way it is TAKEN only at a STORED
|
|
1474
|
+
* position, so a composition whose element lies past the prefix the reading verifies is never answered.
|
|
1475
|
+
*/
|
|
1476
|
+
private offer;
|
|
1477
|
+
/** **An offer taken** — kept, and for a target with a path the replay ends HERE: nothing after it is replayed (`InputCut`). */
|
|
1478
|
+
private take;
|
|
1479
|
+
/**
|
|
1480
|
+
* **A READING — what a node was handed, by replaying the run to that node's composition** — `RN-R45`, W205,
|
|
1481
|
+
* [ADR 0128](../../doc/decisions/0128-what-a-node-was-handed-is-read-by-replaying-the-run-to-its-composition.md), P42
|
|
1482
|
+
* decisions 1-7. The export {@link inputOf} and `RunHandle.inputOf` are this and nothing else. In order:
|
|
1483
|
+
*
|
|
1484
|
+
* 1. **The header**, refused by `resumeRefusal`'s own header half (`format`, `flow`) — never its halt half.
|
|
1485
|
+
* 2. **The target**, refused `unknown` where it cannot name anything in this run.
|
|
1486
|
+
* 3. **A driver built exactly as `runtime.resume` builds one** (`RN-R25`) — the definition, the registry's members, a
|
|
1487
|
+
* `joinPolicy` defaulting to `liveToken` — with NO callback and a scheduler that is handed nothing (`DEAF_SCHEDULER`).
|
|
1488
|
+
* 4. **The replay** (`readStep`) over the stored PREFIX it can verify, the cut armed: it stops at the target's
|
|
1489
|
+
* composition and returns what it found.
|
|
1490
|
+
*
|
|
1491
|
+
* Private and STATIC, so the export and the handle reach one road, and a test reaches the reading's own driver — its
|
|
1492
|
+
* count — by a cast (ADR 0121 rule 7): `driver` is `null` where a refusal came before one was built.
|
|
1493
|
+
*/
|
|
1494
|
+
private static read;
|
|
1495
|
+
/**
|
|
1496
|
+
* **The reading's replay** — `resume()`'s first half (`resumeStep` to `replayStored`) with the cut armed, and nothing of
|
|
1497
|
+
* its second: no in-flight set, no `resumed`, nothing handed on. **The prefix it verifies ENDS at the first `halted`,
|
|
1498
|
+
* `stopped` or `cancelRequested` element** (P42 decision 5): after one a halted driver composes nothing more, whether the
|
|
1499
|
+
* replay regenerates it (a predicate's throw) or cannot (a consumer's callback, a refused result, an owner's call) —
|
|
1500
|
+
* what follows is `not-reached`, never `diverged` (P42 retro (a1) corrected the reason). Everything
|
|
1501
|
+
* before is compared as `RN-R20` compares it.
|
|
1502
|
+
*/
|
|
1503
|
+
private readStep;
|
|
1404
1504
|
/**
|
|
1405
1505
|
* **What is IN FLIGHT: dispatched, and no settlement in the log** — `RN-R21`, in dispatch order. A
|
|
1406
1506
|
* `user_action` settlement IS a settlement: a parked task is waiting on a person, not on a worker, and
|
|
@@ -1705,7 +1805,8 @@ export declare class Driver {
|
|
|
1705
1805
|
* described **today** before this method existed, and this adds **no retention**.
|
|
1706
1806
|
* *(Q19 is RESOLVED since W53 —
|
|
1707
1807
|
* [ADR 0046](../../doc/decisions/0046-a-run-retains-what-its-tasks-returned-and-a-region-releases-its-collection-when-its-join-becomes-terminal.md)
|
|
1708
|
-
* — and `RN-R13` names `outputsCache` by id: it holds results **by reference
|
|
1808
|
+
* — and `RN-R13` names `outputsCache` by id: it holds results **by reference** — and since W202 (ADR 0126) one shared
|
|
1809
|
+
* COPY per output a reference has read, the one thing in it that is not the log's object,
|
|
1709
1810
|
* asserted by IDENTITY at ladder L2 rather than by a byte count, because a
|
|
1710
1811
|
* reachability SET cannot tell an alias from a copy. W53's readers found that
|
|
1711
1812
|
* from both ends — the rule's stated test returned a third name on a `needs`
|
|
@@ -1733,6 +1834,11 @@ export declare class Driver {
|
|
|
1733
1834
|
* node or an edge that declares a `needs` asks, and a run that declares none never builds it.
|
|
1734
1835
|
*/
|
|
1735
1836
|
private outputs;
|
|
1837
|
+
/**
|
|
1838
|
+
* The projection, made empty on first need and NOT caught up — `outputs()` catches it up; a `"$payload"` read, which
|
|
1839
|
+
* reads no output, only needs somewhere to keep its shared copy (W202).
|
|
1840
|
+
*/
|
|
1841
|
+
private cache;
|
|
1736
1842
|
/**
|
|
1737
1843
|
* **One task's entry, made what its effective outcome now says** — `outputs()`'s catch-up, for a task one of whose records
|
|
1738
1844
|
* was just read. An entry is `{ path, result }` with `result` the record's own object (`RN-R13`); a node's list is kept
|
|
@@ -1747,12 +1853,42 @@ export declare class Driver {
|
|
|
1747
1853
|
* that LOSES its effective outcome is removed — **reachable**: `ID-R4` asks only whether a settlement's id was dispatched,
|
|
1748
1854
|
* so a settlement event repeated for a settled task is appended, and a `done` then a `user_action` leaves the task with
|
|
1749
1855
|
* none (W195's conformity reader, C4; `test/driver/log-view.test.ts` drives it and the resolution that places it again).
|
|
1856
|
+
*
|
|
1857
|
+
* **Both a replacement and a removal let go of the entry OBJECT, and with it the entry's shared copy** (W202, ADR 0126,
|
|
1858
|
+
* `OutputEntry.shared`): a replaced output's next read makes a new copy of the new result, and a removed one's copy is
|
|
1859
|
+
* held nowhere — `RN-R13`'s v0.33 term, released with what it belongs to (ADR 0046).
|
|
1750
1860
|
*/
|
|
1751
1861
|
private place;
|
|
1862
|
+
/**
|
|
1863
|
+
* **One entry's change carried into every index built for its node** — W199, ADR 0124: called by `place` at each of
|
|
1864
|
+
* its cases (a first entry, an append, an insertion at a first-record position, a replacement in place, a removal), so
|
|
1865
|
+
* the index is caught up exactly when `byNode` is and can say nothing `byNode` does not. `was` is the entry leaving
|
|
1866
|
+
* (`null` for an insertion), `now` the one arriving (`null` for a removal); a replacement keeps its path, so its
|
|
1867
|
+
* position. A group is ordered by PATH, not by first record, so where `byNode` inserted mid-list the group finds its own
|
|
1868
|
+
* place by a binary search. An entry with fewer than `depth` segments is visible to no reader at that depth and is filed
|
|
1869
|
+
* nowhere.
|
|
1870
|
+
*/
|
|
1871
|
+
private reindex;
|
|
1872
|
+
/**
|
|
1873
|
+
* **A node's outputs grouped for readers at `depth`** — built on the first read at that depth from the node's current
|
|
1874
|
+
* entries (each one examined), each group sorted ONCE, and from then on kept by `reindex`.
|
|
1875
|
+
*/
|
|
1876
|
+
private groupsOf;
|
|
1877
|
+
/** The depth `reader` and `target` share — `sharedRegionDepth`, a graph fact, asked once per pair. */
|
|
1878
|
+
private depthOf;
|
|
1879
|
+
/**
|
|
1880
|
+
* **The outputs of `nodeId` visible to `atNodeId` at `here`, in `compareBranchPaths` order** — W199, ADR 0124: ADR
|
|
1881
|
+
* 0044's predicate answered by a LOOKUP. The predicate is `visibilityKey(here, d) === visibilityKey(path, d)` at the depth
|
|
1882
|
+
* `d` the two share (`outputIsVisible`), so the visible outputs are exactly the group filed under the reader's key: what
|
|
1883
|
+
* the filter over every output and the sort of its survivors returned until W199, found in the cost of its answer.
|
|
1884
|
+
* **The index's own list, never handed out** — the caller maps it to copies.
|
|
1885
|
+
*/
|
|
1886
|
+
private visibleOutputs;
|
|
1752
1887
|
/**
|
|
1753
1888
|
* The projection above, CAUGHT UP to `at` — `RN-R13` v0.30, ADR 0121 (W195). **Not run state and not a second copy of
|
|
1754
1889
|
* it**: `byNode`'s entries hold each result BY REFERENCE (the log record's own object) — so it is a holder of results and
|
|
1755
|
-
*
|
|
1890
|
+
* not a copy of them; since W202 an entry a reference has read also holds its ONE shared copy (`OutputEntry.shared`,
|
|
1891
|
+
* `RN-R13` v0.33), and `payload` the run's payload's — `tasks` is one slot per task id
|
|
1756
1892
|
* read — its node, its path, its first-record position and its current entry — and `orders` the same positions, per
|
|
1757
1893
|
* node, parallel to its list. Everything here is the merge `effectiveOutcome` computed from the task's own records at the moment they were
|
|
1758
1894
|
* read, and `at` says which records have been; so there is nothing here that could disagree with the log. `ADR 0014`'s
|
|
@@ -1783,7 +1919,7 @@ export declare class Driver {
|
|
|
1783
1919
|
*
|
|
1784
1920
|
* **Extracted by [W42](../../doc/work-items/w42-what-a-decision-sees.md) rather
|
|
1785
1921
|
* than copied.** An EDGE's `needs` resolves by exactly these rules — same grammar,
|
|
1786
|
-
* same visibility predicate, same order, same
|
|
1922
|
+
* same visibility predicate, same order, same shared copy (since W202, ADR 0126) — and the ONE difference
|
|
1787
1923
|
* is that an edge has no `"."` form, which is a property of the caller rather than
|
|
1788
1924
|
* of this expression. *Two callers, one resolution, so a change to what a reference
|
|
1789
1925
|
* means cannot mean two different things to a worker and to a predicate*, which is
|
|
@@ -1794,12 +1930,21 @@ export declare class Driver {
|
|
|
1794
1930
|
* @throws UnresolvedReference — `FD-R3`.
|
|
1795
1931
|
*/
|
|
1796
1932
|
private resolveReference;
|
|
1933
|
+
/** **An output entry's shared copy**, made at its first read and kept on the entry (`OutputEntry.shared`) — counted. */
|
|
1934
|
+
private sharedOf;
|
|
1935
|
+
/**
|
|
1936
|
+
* **`"$payload"`'s value in a run: the run's ONE shared copy** — W202, ADR 0126, P41 decision 2: `sharedCopy` of the
|
|
1937
|
+
* header's `entryPayload` (`runPayload()`), made at the first `"$payload"` read of the run — or of a resume, whose driver
|
|
1938
|
+
* is a new one — and handed to every reader at both sites. Counted once. `FD-R4` does not come here: it composes from
|
|
1939
|
+
* the caller's own value, before a header exists, and keeps its clone.
|
|
1940
|
+
*/
|
|
1941
|
+
private sharedPayload;
|
|
1797
1942
|
/**
|
|
1798
1943
|
* **The value the run ENTERED with — the header's copy** (`RN-R15`, P38 fact 4 and (f)): taken at `start()` from the
|
|
1799
1944
|
* caller's payload, or on a resume from the stored header, and held for the life of the run. What a `"$payload"`
|
|
1800
1945
|
* reference composes from, in both doors; `FD-R4` hands the caller's own value instead, because it runs before the
|
|
1801
|
-
* header exists. Read only where a node or an edge composes the token — a run declaring none never
|
|
1802
|
-
* (P38 decision 2). The header is set before the first `enter` in both doors, so `null` here is not a run.
|
|
1946
|
+
* header exists. Read only where a node or an edge composes the token — a run declaring none never copies it
|
|
1947
|
+
* (P38 decision 2); since v0.47 read ONCE, by `sharedPayload`, whose one shared copy every reader is handed. The header is set before the first `enter` in both doors, so `null` here is not a run.
|
|
1803
1948
|
*/
|
|
1804
1949
|
private runPayload;
|
|
1805
1950
|
/**
|
|
@@ -1825,8 +1970,8 @@ export declare class Driver {
|
|
|
1825
1970
|
* received anyway* is `ctx.outcome`, which it already has. So there is no
|
|
1826
1971
|
* `edgeValue` parameter here and none is derivable — see the call site. **A
|
|
1827
1972
|
* `"$payload"` key needs none either** (v0.44, ADR 0117): it is not derived from what
|
|
1828
|
-
* the NODE received but read from the run's header
|
|
1829
|
-
* copy
|
|
1973
|
+
* the NODE received but read from the run's header — since v0.47 the run's ONE shared
|
|
1974
|
+
* copy of it, `sharedPayload()` above, the object a worker's `"$payload"` key is handed (ADR 0126).
|
|
1830
1975
|
*
|
|
1831
1976
|
* @throws UnresolvedReference — `FD-R3`, from the same expression a node's does.
|
|
1832
1977
|
*/
|
|
@@ -2128,3 +2273,33 @@ export declare class Driver {
|
|
|
2128
2273
|
effectiveOutcomeOf(taskId: string): OutcomeRecord | null;
|
|
2129
2274
|
terminalStateOf(taskId: string): TerminalState | null;
|
|
2130
2275
|
}
|
|
2276
|
+
/**
|
|
2277
|
+
* **What a node was HANDED, read after the fact by replaying the run to that node's composition** — `RN-R45`, W205,
|
|
2278
|
+
* [ADR 0128](../../doc/decisions/0128-what-a-node-was-handed-is-read-by-replaying-the-run-to-its-composition.md), P42
|
|
2279
|
+
* (a1)-(d1) and decisions 1-6. `run` is a run's stored header and events — `driver.checkpoint()`, or what a store appended
|
|
2280
|
+
* (`RN-R14`, `RN-R18`); `options` the registry and options the run's driver was built with (`runtime.registry`, plus its
|
|
2281
|
+
* `joinPolicy` where it was not `liveToken`).
|
|
2282
|
+
*
|
|
2283
|
+
* **It IS the driver's composition, not a second one**: a driver built for the stored run, deaf — no callback, nothing
|
|
2284
|
+
* handed to a scheduler — replays the stored events through `enter`, `compose` and `dispatch` exactly as a resume does and
|
|
2285
|
+
* stops at the target's composition. So an answer is EXACT, and it costs the run's prefix up to the target — O(run) at
|
|
2286
|
+
* worst, never O(the answer) — and a reading re-runs the run's predicates and gate types, as a resume does (ADR 0002 rule
|
|
2287
|
+
* 4, Q9: an impure one diverges, or answers what its re-run computes).
|
|
2288
|
+
*
|
|
2289
|
+
* | target | kind | `inputs` |
|
|
2290
|
+
* |---|---|---|
|
|
2291
|
+
* | `{ taskId }`, or `{ nodeId, branchPath }` | a task | ONE `envelope`: `{ settings, input, transform? }` — its one dispatch's, retries and a resolution included; BEFORE its transform (ADR 0051) |
|
|
2292
|
+
* | the same | an ask | ONE `received` — equal to its `asked` element's `input` |
|
|
2293
|
+
* | `{ nodeId, branchPath }` | a gate, fanout, map or end | ONE `received` — what `enter` composed for it on that path |
|
|
2294
|
+
* | `{ nodeId, branchPath }` | a join, at an ARRIVING branch's path | ONE `arrival` — what that branch carried in, a late arrival included |
|
|
2295
|
+
* | `{ nodeId, branchPath }` | a join, at its OPENER's path | ONE `collection` — what it handed on when it fired |
|
|
2296
|
+
* | `{ nodeId }` | any | EVERY one of the above the replay reached, in the order it reached them — one replay to the stored end |
|
|
2297
|
+
*
|
|
2298
|
+
* A path target answers the FIRST composition on its path: an end shared by two sibling regions can be reached twice on one
|
|
2299
|
+
* path (`end` is exempt from `FD-V19`), and only the node id alone lists both.
|
|
2300
|
+
*
|
|
2301
|
+
* **Never throws for the run or the target** — `InputReading`'s refusals; it throws what `new Driver` throws for a
|
|
2302
|
+
* definition or a registry that cannot run (`InvalidDefinition`, `MissingJoinPolicy`, a `RegistryError`), as a resume does.
|
|
2303
|
+
* **What it does not detect**: a definition or a registry changed AFTER the target — the cut verifies the prefix it replays.
|
|
2304
|
+
*/
|
|
2305
|
+
export declare function inputOf(flow: FlowDefinition, options: InputOfOptions, run: Checkpoint, target: InputTarget): InputReading;
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
import { FyflowScheduler } from 'fyflow-scheduler';
|
|
65
65
|
import type { ResourceGroup, WorkerManager } from 'fyflow-scheduler';
|
|
66
66
|
import { Driver } from '../driver/driver.js';
|
|
67
|
-
import type { Checkpoint, CheckpointHeader, DriverOptions, RunStopOptions } from '../driver/driver.js';
|
|
67
|
+
import type { Checkpoint, CheckpointHeader, DriverOptions, InputReading, InputTarget, RunStopOptions } from '../driver/driver.js';
|
|
68
68
|
import type { NodeRun, RunOutcome, RunState } from '../fold/run-state.js';
|
|
69
69
|
import type { Finding, FlowDefinition, JsonSchema, RegistryCode, RegistryDocument, ResolvedPool, ResolvedRegistry, RetryPolicy, TaskNode } from '../definition/types.js';
|
|
70
70
|
import type { PoolFactory } from './bootstrap.js';
|
|
@@ -361,6 +361,22 @@ export interface RunHandle {
|
|
|
361
361
|
* materialising the rest (`RN-R44`); the first read of an entry freezes it once, O(the entry). `null` for a task the run never dispatched or asked, whatever the id names (`toString` included).
|
|
362
362
|
*/
|
|
363
363
|
node(taskId: string): NodeRun | null;
|
|
364
|
+
/**
|
|
365
|
+
* **What a node of this run was HANDED, read by replaying the run so far to that node's composition** — `RN-R45`, W205,
|
|
366
|
+
* [ADR 0128](../../doc/decisions/0128-what-a-node-was-handed-is-read-by-replaying-the-run-to-its-composition.md), P42
|
|
367
|
+
* decision 7: the root export {@link inputOf} over THIS run's own `driver.checkpoint()`, with the registry, `joinPolicy`
|
|
368
|
+
* and options this runtime built the run's driver with — one function, so a live answer and a stored one cannot differ.
|
|
369
|
+
* A task's answer is its dispatch's envelope `{ settings, input, transform? }`; an ask's, a gate's, a fanout's, a map's
|
|
370
|
+
* and an end's what reached it; a join's an arrival at an arriving branch's path or its collection at its opener's; a
|
|
371
|
+
* node id ALONE every path the run reached it on. A target not yet reached is `not-reached`.
|
|
372
|
+
*
|
|
373
|
+
* **Synchronous, O(the run's prefix), never cached**: each call copies the run's events (`checkpoint()`) and replays them
|
|
374
|
+
* on a driver of its own, deaf — nothing is appended to this run, nothing handed to a scheduler, no callback called, and
|
|
375
|
+
* no object of this run's reachable through an answer. Read it at a quiescent point (`RN-R12`); mid-step it reads the
|
|
376
|
+
* prefix appended so far. On a released run (`done` settled) it answers from what the run delivered. **BREAKING in
|
|
377
|
+
* 0.10.0 for code that BUILDS a `RunHandle`** (P42 decision 9) — a new member, as `state` was in 0.9.0.
|
|
378
|
+
*/
|
|
379
|
+
inputOf(target: InputTarget): InputReading;
|
|
364
380
|
}
|
|
365
381
|
export interface Runtime {
|
|
366
382
|
/** **Exactly the object this runtime hands its own drivers** (decision 14) — spread it into `new Driver(…)` beside a `runId`, a `scheduler` and a `joinPolicy` and you have built what `start` builds. */
|