@cotal-ai/runtime 0.0.1 → 0.24.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/dist/fork.d.ts +144 -0
- package/dist/fork.d.ts.map +1 -0
- package/dist/fork.js +310 -0
- package/dist/fork.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/journal-store.d.ts +54 -0
- package/dist/journal-store.d.ts.map +1 -0
- package/dist/journal-store.js +60 -0
- package/dist/journal-store.js.map +1 -0
- package/dist/mesh-handler.d.ts +310 -0
- package/dist/mesh-handler.d.ts.map +1 -0
- package/dist/mesh-handler.js +731 -0
- package/dist/mesh-handler.js.map +1 -0
- package/dist/migrate.d.ts +147 -0
- package/dist/migrate.d.ts.map +1 -0
- package/dist/migrate.js +256 -0
- package/dist/migrate.js.map +1 -0
- package/dist/resolve-checkpoint.d.ts +75 -0
- package/dist/resolve-checkpoint.d.ts.map +1 -0
- package/dist/resolve-checkpoint.js +108 -0
- package/dist/resolve-checkpoint.js.map +1 -0
- package/dist/run-context.d.ts +42 -0
- package/dist/run-context.d.ts.map +1 -0
- package/dist/run-context.js +55 -0
- package/dist/run-context.js.map +1 -0
- package/dist/run-driver.d.ts +124 -0
- package/dist/run-driver.d.ts.map +1 -0
- package/dist/run-driver.js +228 -0
- package/dist/run-driver.js.map +1 -0
- package/package.json +31 -16
- package/README.md +0 -6
package/dist/fork.d.ts
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fork a run from a named step: the fork cut, and the two things a fork must not re-decide.
|
|
3
|
+
*
|
|
4
|
+
* `fork(runId, fromStepKey)` is the repair verb. It does not roll the parent back — the parent is
|
|
5
|
+
* untouched — it starts a NEW run that owns the parent's history up to a named step and re-runs from
|
|
6
|
+
* there. Two decisions inside that sentence carry the whole design:
|
|
7
|
+
*
|
|
8
|
+
* **The cut is computed by a DRY WALK IN MIGRATION MODE, and the mode is load-bearing rather than a
|
|
9
|
+
* default.** A resume's replay short-circuits a settled scope: it takes the scope's recorded outcome
|
|
10
|
+
* and never walks its branches, so every entry underneath it is accounted for in one step. That is
|
|
11
|
+
* exactly right for a resume, which only needs to get past it, and exactly wrong here — the step a
|
|
12
|
+
* caller wants to cut at can be INSIDE such a scope, and a walk that never entered it would either
|
|
13
|
+
* miss the cut entirely or sweep entries that come after the cut point into the prefix. A fork with a
|
|
14
|
+
* silently-too-large prefix is the worst available outcome: it looks like a fork, drives like a fork,
|
|
15
|
+
* and has already replayed the work the caller forked to avoid.
|
|
16
|
+
*
|
|
17
|
+
* **The prefix inherits the parent's pins UNCHANGED, seed included.** The child gets a new run id and
|
|
18
|
+
* `resolvePins` defaults a seed to the run id, so re-resolving pins for the child would reseed it —
|
|
19
|
+
* and a reseeded prefix redecides every pure draw inside history it was supposed to be copying. A
|
|
20
|
+
* parent that drew `left` would give a child that draws `right`, and nothing would diverge, because a
|
|
21
|
+
* `pick` is pure and no entry records it. So the parent's `RunPins` are copied verbatim; the fresh
|
|
22
|
+
* derivation begins at the frontier, which is what "fork" means.
|
|
23
|
+
*
|
|
24
|
+
* What this file does NOT do, named rather than implied:
|
|
25
|
+
* - It does not respawn agents. `onFork` is specified to mint a fresh agent at the frontier, and
|
|
26
|
+
* `spawn` rides the durable-action machinery this host does not have, so a cut containing a
|
|
27
|
+
* spawn is REFUSED (L5019) rather than copied and hoped about.
|
|
28
|
+
* - It does not cut worktree branches. There is no worktree plane in this tree; a caller that
|
|
29
|
+
* asks for one is refused rather than told a branch exists.
|
|
30
|
+
* - It does not record LINEAGE. The child's run record cannot say it is a fork of anything: a
|
|
31
|
+
* run's spec has no such field, and inventing one changes the run record's shape — the kind of
|
|
32
|
+
* change the `migration` record was raised for a decision on rather than made here.
|
|
33
|
+
* `commitFork` reports `lineageRecorded: false` so the gap is something a caller reads rather
|
|
34
|
+
* than something a reader has to notice.
|
|
35
|
+
*/
|
|
36
|
+
import type { KV } from "@nats-io/kv";
|
|
37
|
+
import { Journal, type JournalEntry, type JournalInit, type JournalStore, type LookupVerdict, type RunPins, type StepKey } from "@cotal-ai/lang";
|
|
38
|
+
/** One reason a fork was refused, carrying the code a reader repairs against — the `L` catalogue
|
|
39
|
+
* in `@cotal-ai/lang`. */
|
|
40
|
+
export interface ForkRefusal {
|
|
41
|
+
readonly code: string;
|
|
42
|
+
readonly step?: string;
|
|
43
|
+
readonly why: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ForkPlan {
|
|
46
|
+
readonly parent: string;
|
|
47
|
+
readonly child: string;
|
|
48
|
+
readonly at: number;
|
|
49
|
+
readonly actor: string;
|
|
50
|
+
/** The step the child re-runs FROM. Never part of the cut: the cut is what happened before it. */
|
|
51
|
+
readonly fromStep: string;
|
|
52
|
+
/** The parent's pins, verbatim — what the child must be created under, not what it would resolve. */
|
|
53
|
+
readonly pins: RunPins;
|
|
54
|
+
/** The entries the child inherits as history, in the parent's recorded order. */
|
|
55
|
+
readonly cut: readonly JournalEntry[];
|
|
56
|
+
readonly admissible: boolean;
|
|
57
|
+
readonly refusals: readonly ForkRefusal[];
|
|
58
|
+
}
|
|
59
|
+
export interface ForkRequest {
|
|
60
|
+
readonly parent: string;
|
|
61
|
+
/** The child's run id. Allocated by the caller, because minting an id is not this file's decision. */
|
|
62
|
+
readonly child: string;
|
|
63
|
+
/** A journal key string, e.g. `/checkpoint:approve#0`. Not a sequence number: it survives edits. */
|
|
64
|
+
readonly fromStepKey: string;
|
|
65
|
+
/** The program the parent was running. */
|
|
66
|
+
readonly source: string;
|
|
67
|
+
readonly entries: readonly JournalEntry[];
|
|
68
|
+
/** Read back from the parent's run record. Never re-derived: see the header. */
|
|
69
|
+
readonly pins: RunPins;
|
|
70
|
+
readonly actor: string;
|
|
71
|
+
readonly now: () => number;
|
|
72
|
+
readonly file?: string;
|
|
73
|
+
/**
|
|
74
|
+
* The `newProgramHash` fork option. Accepted only to be REFUSED: the run record carries no
|
|
75
|
+
* program hash to pin — a declared but unbuilt capability this branch does not invent — so a fork
|
|
76
|
+
* "pinned to" one would record nothing at all and the caller would have been told it happened.
|
|
77
|
+
*/
|
|
78
|
+
readonly newProgramHash?: string;
|
|
79
|
+
/** Worktree branches for the child. There is no worktree plane in this tree, so asking for one
|
|
80
|
+
* is refused. */
|
|
81
|
+
readonly worktreeBranches?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/** The walk reached the step the caller wants to cut at. Not an error: it is where the cut ENDS. */
|
|
84
|
+
export declare class CutReached extends Error {
|
|
85
|
+
readonly step: string;
|
|
86
|
+
constructor(step: string);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A read-only journal that stops the walk when it first LOOKS UP a named step.
|
|
90
|
+
*
|
|
91
|
+
* Before delegating, never after: `Journal.lookup` marks a key consumed as its first act, so a stop
|
|
92
|
+
* placed after the delegation would put the cut step itself into the prefix — and the child would
|
|
93
|
+
* then replay the very step it was forked to re-run.
|
|
94
|
+
*
|
|
95
|
+
* Exported because the fork's correctness claim is about WHICH WALK produced the cut, and a suite
|
|
96
|
+
* that cannot run the wrong walk cannot show the right one is different from it.
|
|
97
|
+
*/
|
|
98
|
+
export declare class CutJournal extends Journal {
|
|
99
|
+
private readonly cutAt;
|
|
100
|
+
constructor(init: JournalInit, cutAt: string);
|
|
101
|
+
lookup(key: StepKey, inputHash: string): LookupVerdict;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Compute the fork cut. Reads; never writes.
|
|
105
|
+
*
|
|
106
|
+
* The plan is the product whether or not the fork is admissible — a refused fork owes the caller the
|
|
107
|
+
* code and the step, because that is what makes the next attempt a repair rather than a guess.
|
|
108
|
+
*/
|
|
109
|
+
export declare function planFork(req: ForkRequest): Promise<ForkPlan>;
|
|
110
|
+
export interface ForkCommitResult {
|
|
111
|
+
readonly child: string;
|
|
112
|
+
readonly copied: number;
|
|
113
|
+
/**
|
|
114
|
+
* Always false, and reported rather than omitted.
|
|
115
|
+
*
|
|
116
|
+
* The child's record cannot say it is a fork of anything: `RunSpecValue` has no lineage field,
|
|
117
|
+
* and adding one — or a `fork` record kind beside `migration` — changes the run record's shape,
|
|
118
|
+
* which is a decision to raise rather than one to make in this file. A caller that needs the
|
|
119
|
+
* lineage has it in the {@link ForkPlan} it passed; nothing durable carries it yet.
|
|
120
|
+
*/
|
|
121
|
+
readonly lineageRecorded: false;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Copy the cut into the child, and only then create the child's record.
|
|
125
|
+
*
|
|
126
|
+
* THE ORDER IS THE DURABILITY ARGUMENT. A crash between the two writes is the case this has to be
|
|
127
|
+
* right for, and writing the spec first would leave a child that has a record and half a history —
|
|
128
|
+
* which `driveRun` would happily take over, replaying a prefix that stops in the middle of the
|
|
129
|
+
* parent's past and then performing effects from there. Written last, a crash leaves journal entries
|
|
130
|
+
* under an id with no record: `driveRun` refuses a run it cannot read a record for, and `startRun`
|
|
131
|
+
* refuses a run whose journal already has records. **Neither entry point will touch it**, which is
|
|
132
|
+
* the failure a partial fork should have.
|
|
133
|
+
*
|
|
134
|
+
* The entries are rewritten onto the child's run id as they are copied. That is not bookkeeping: a
|
|
135
|
+
* journal takes only its own run's entries, and the prefix is now the CHILD's history — the parent's
|
|
136
|
+
* copy stays exactly where it was, because fork is not rollback.
|
|
137
|
+
*/
|
|
138
|
+
export declare function commitFork(kv: KV, endpoint: string, plan: ForkPlan, store: JournalStore): Promise<ForkCommitResult>;
|
|
139
|
+
/** A fork the plan refused, offered for commit anyway. The plan carries the per-step reason. */
|
|
140
|
+
export declare class ForkNotAdmissible extends Error {
|
|
141
|
+
readonly plan: ForkPlan;
|
|
142
|
+
constructor(plan: ForkPlan);
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=fork.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork.d.ts","sourceRoot":"","sources":["../src/fork.ts"],"names":[],"mappings":"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"}
|
package/dist/fork.js
ADDED
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
import { createRunSpec, readRunRecord } from "@cotal-ai/core";
|
|
2
|
+
import { Journal, JournalReadOnlyError, journalEntryKeyString, run as runProgram, RunDivergence, UnwalkableScope, ScopeBranchMissing, stepKeyString, } from "@cotal-ai/lang";
|
|
3
|
+
/** The journal kinds that open a scope, i.e. whose entry can ENCLOSE a cut point. */
|
|
4
|
+
const SCOPE_KINDS = new Set(["parallel", "race", "fanOut", "conclave"]);
|
|
5
|
+
/** The walk reached the step the caller wants to cut at. Not an error: it is where the cut ENDS. */
|
|
6
|
+
export class CutReached extends Error {
|
|
7
|
+
step;
|
|
8
|
+
constructor(step) {
|
|
9
|
+
super(`the dry walk reached the cut at ${step}`);
|
|
10
|
+
this.step = step;
|
|
11
|
+
this.name = "CutReached";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A read-only journal that stops the walk when it first LOOKS UP a named step.
|
|
16
|
+
*
|
|
17
|
+
* Before delegating, never after: `Journal.lookup` marks a key consumed as its first act, so a stop
|
|
18
|
+
* placed after the delegation would put the cut step itself into the prefix — and the child would
|
|
19
|
+
* then replay the very step it was forked to re-run.
|
|
20
|
+
*
|
|
21
|
+
* Exported because the fork's correctness claim is about WHICH WALK produced the cut, and a suite
|
|
22
|
+
* that cannot run the wrong walk cannot show the right one is different from it.
|
|
23
|
+
*/
|
|
24
|
+
export class CutJournal extends Journal {
|
|
25
|
+
cutAt;
|
|
26
|
+
constructor(init, cutAt) {
|
|
27
|
+
super(init);
|
|
28
|
+
this.cutAt = cutAt;
|
|
29
|
+
}
|
|
30
|
+
lookup(key, inputHash) {
|
|
31
|
+
if (stepKeyString(key) === this.cutAt)
|
|
32
|
+
throw new CutReached(this.cutAt);
|
|
33
|
+
return super.lookup(key, inputHash);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/** A handler that performs nothing: the cut is computed from records, never by doing anything. */
|
|
37
|
+
function dryHandler(now) {
|
|
38
|
+
const stop = (_req, ctx) => {
|
|
39
|
+
throw new ForkFrontier(stepKeyString(ctx.key));
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
now,
|
|
43
|
+
spawn: stop, turn: stop, ask: stop, checkpoint: stop, sleep: stop,
|
|
44
|
+
wait: stop, notify: stop, monitor: stop, openConclave: stop, closeConclave: stop,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** The walk ran out of records before it reached the cut. The caller named a step this run never got
|
|
48
|
+
* to, which is a different failure from naming one it never recorded. */
|
|
49
|
+
class ForkFrontier extends Error {
|
|
50
|
+
step;
|
|
51
|
+
constructor(step) {
|
|
52
|
+
super(`the dry walk reached the frontier at ${step}`);
|
|
53
|
+
this.step = step;
|
|
54
|
+
this.name = "ForkFrontier";
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Compute the fork cut. Reads; never writes.
|
|
59
|
+
*
|
|
60
|
+
* The plan is the product whether or not the fork is admissible — a refused fork owes the caller the
|
|
61
|
+
* code and the step, because that is what makes the next attempt a repair rather than a guess.
|
|
62
|
+
*/
|
|
63
|
+
export async function planFork(req) {
|
|
64
|
+
const refusals = [];
|
|
65
|
+
if (req.newProgramHash !== undefined) {
|
|
66
|
+
refusals.push({
|
|
67
|
+
code: "L5002",
|
|
68
|
+
why: "a fork onto a new program would have to pin its hash on the child's run record, and a run's spec carries no program hash — that pin is declared and unbuilt. Refused rather than accepted and dropped.",
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (req.worktreeBranches === true) {
|
|
72
|
+
refusals.push({
|
|
73
|
+
code: "L5019",
|
|
74
|
+
why: "a fork is specified to get its own worktree branches cut from the parent's branch head, and there is no worktree plane in this tree to cut one from",
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
const journal = new CutJournal({ run: req.parent, entries: req.entries, readOnly: true }, req.fromStepKey);
|
|
78
|
+
// THE CALLER'S LIST IS AN APPEND LOG, and every step in it appears at least twice.
|
|
79
|
+
//
|
|
80
|
+
// The stream is append-only: settling appends a second record rather than editing the first, so a
|
|
81
|
+
// completed step is a pending record AND a settled one. `RunJournalAppender.steps()` replays every
|
|
82
|
+
// step record in order and the driver seeds a journal straight from it, which is the only shape a
|
|
83
|
+
// real caller holds — this file's own suite was building a KEYED view with `Journal.entries()` and
|
|
84
|
+
// was therefore blind to it. Filtering `req.entries` put both rows of every copied step into the
|
|
85
|
+
// prefix, and `commitFork` COPIES the prefix, so the doubling was written to the child's durable
|
|
86
|
+
// stream as real records. Silent, with every key correct and simply two of each.
|
|
87
|
+
//
|
|
88
|
+
// The fold is the journal's, not a second copy of the rule: the entries below are its keyed view,
|
|
89
|
+
// last write per step in the order the run performed them.
|
|
90
|
+
const entries = journal.entries();
|
|
91
|
+
const recorded = entries.some((e) => journalEntryKeyString(e) === req.fromStepKey);
|
|
92
|
+
if (!recorded) {
|
|
93
|
+
refusals.push({
|
|
94
|
+
code: "L5017",
|
|
95
|
+
step: req.fromStepKey,
|
|
96
|
+
why: "the journal has no entry under this key; a fork cuts at a step the parent actually recorded, and a key that names nothing would silently cut at the end of history",
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
let reached = false;
|
|
100
|
+
try {
|
|
101
|
+
await runProgram(req.source, {
|
|
102
|
+
runId: req.parent,
|
|
103
|
+
handler: dryHandler(req.now),
|
|
104
|
+
journal,
|
|
105
|
+
// MIGRATION MODE. See the header: a resume's short-circuit would carry a settled scope's whole
|
|
106
|
+
// subtree into the prefix, and the entries it swept in are precisely the ones after the cut.
|
|
107
|
+
migration: true,
|
|
108
|
+
pins: req.pins,
|
|
109
|
+
...(req.file !== undefined ? { file: req.file } : {}),
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
const err = unwrapCut(e);
|
|
114
|
+
if (err instanceof CutReached) {
|
|
115
|
+
reached = true;
|
|
116
|
+
}
|
|
117
|
+
else if (err instanceof RunDivergence) {
|
|
118
|
+
refusals.push({
|
|
119
|
+
code: "L5001",
|
|
120
|
+
step: err.stepKey,
|
|
121
|
+
why: "the source diverged from the recorded journal before the cut was reached, so what the prefix contains is not decided; migrate the run or fork on the source it recorded",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
else if (err instanceof UnwalkableScope) {
|
|
125
|
+
refusals.push({
|
|
126
|
+
code: "L5014",
|
|
127
|
+
step: err.scopeKey,
|
|
128
|
+
why: `the walk could not enter a scope on the way to the cut: ${err.message}`,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
else if (err instanceof ScopeBranchMissing) {
|
|
132
|
+
// Its OWN code and not L5014's. Both stop the walk at a scope, but a conclave stops it because
|
|
133
|
+
// the handle was never journalled, and this one because the arm is gone from the source — and
|
|
134
|
+
// a refusal that named the wrong reason would send the author to fix the wrong thing.
|
|
135
|
+
refusals.push({
|
|
136
|
+
code: "L5022",
|
|
137
|
+
step: err.scopeKey,
|
|
138
|
+
why: `the walk reached ${err.scopeKey} on the way to the cut and the source no longer declares `
|
|
139
|
+
+ `${err.missing.map((k) => `\`${k}\``).join(", ")}, the branch${err.missing.length === 1 ? "" : "es"} the run settled on; `
|
|
140
|
+
+ `fork on the source it recorded, or restore the arm name`,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
else if (!(err instanceof ForkFrontier) && !(err instanceof JournalReadOnlyError)) {
|
|
144
|
+
throw e;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const orphaned = new Set(journal.orphans().map((e) => journalEntryKeyString(e)));
|
|
148
|
+
// THE FRONTIER PROJECTION. A scope that ENCLOSES the cut point must not be copied as settled.
|
|
149
|
+
//
|
|
150
|
+
// The dry walk runs in migration mode, which descends into a recorded scope rather than
|
|
151
|
+
// short-circuiting it, so the cut point inside a branch is reached and everything before it is
|
|
152
|
+
// consumed — including the scope's own entry. The CHILD, though, replays in RESUME mode, where a
|
|
153
|
+
// settled scope entry is taken wholesale: it reads the recorded result, never re-enters, and its
|
|
154
|
+
// first live step lands after the whole combinator. The child then never re-runs the step it was
|
|
155
|
+
// forked for, and the recorded result it inherits already contains the answer for the branch the
|
|
156
|
+
// caller wanted re-decided. Admissible, no refusal, wrong child — the worst shape available.
|
|
157
|
+
//
|
|
158
|
+
// Dropping the enclosing entry makes the child re-enter the combinator: sibling branches replay
|
|
159
|
+
// from their own entries, which are still in the cut, and the branch holding the cut point runs
|
|
160
|
+
// live from it. That is what a fork means here.
|
|
161
|
+
const enclosing = entries.filter((e) => {
|
|
162
|
+
const k = journalEntryKeyString(e);
|
|
163
|
+
return SCOPE_KINDS.has(e.kind) && req.fromStepKey.startsWith(`${k}/b:`);
|
|
164
|
+
});
|
|
165
|
+
// Re-entering is only sound for a scope whose branches all RUN. A `race` decided a winner, and a
|
|
166
|
+
// child that re-enters would race again — re-deciding, on a fresh handler, something the parent
|
|
167
|
+
// recorded. That is not a fork of the run, it is a different run. Refused rather than re-raced,
|
|
168
|
+
// and refused rather than copied-as-settled, because both of those are silent.
|
|
169
|
+
for (const e of enclosing) {
|
|
170
|
+
if (e.kind === "parallel" || e.kind === "fanOut")
|
|
171
|
+
continue;
|
|
172
|
+
refusals.push({
|
|
173
|
+
code: "L5020",
|
|
174
|
+
step: journalEntryKeyString(e),
|
|
175
|
+
why: `the cut is inside a \`${e.kind}\`, whose outcome this run already decided. Re-entering it would decide it again on a fresh handler, and copying it settled would make the child skip the step it was forked to re-run. Fork at a step outside the ${e.kind}, or at the ${e.kind} itself.`,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
// L5018 says one specific thing: the program's own path does not arrive at this step. When the
|
|
179
|
+
// walk stopped for a reason of its OWN — it diverged (L5001), it could not enter a scope (L5014),
|
|
180
|
+
// or the step sits inside a scope whose losing arms the walk does not enter at all (L5020) — the
|
|
181
|
+
// path may well arrive there and that sentence is FALSE. Emitting it beside the true refusal is
|
|
182
|
+
// worse than emitting nothing: both codes are actionable and they prescribe opposite repairs, so
|
|
183
|
+
// the caller re-keys a step that was correct all along instead of forking before the conclave. A
|
|
184
|
+
// refusal set is only a repair instruction if every code in it is true.
|
|
185
|
+
//
|
|
186
|
+
// L5020 is in that set for a reason worth stating: a `race` LOSER's step is recorded, settled, and
|
|
187
|
+
// genuinely performed by the parent — every arm of a race runs — yet the migration walk enters
|
|
188
|
+
// only the recorded winner, so `reached` is false for a key the program does reach. L5018 there
|
|
189
|
+
// told the caller their program does not go somewhere it does go.
|
|
190
|
+
//
|
|
191
|
+
// This runs after the projection because the gate has to be able to SEE L5020; a `some()` over a
|
|
192
|
+
// refusal set that has not been filled yet is a check that cannot fail.
|
|
193
|
+
const stoppedForAnotherReason = refusals.some((r) => r.code === "L5001" || r.code === "L5014" || r.code === "L5020");
|
|
194
|
+
if (!reached && recorded && !stoppedForAnotherReason) {
|
|
195
|
+
refusals.push({
|
|
196
|
+
code: "L5018",
|
|
197
|
+
step: req.fromStepKey,
|
|
198
|
+
why: "the step is recorded, but this replay never reached it — the program's own path does not arrive there. A cut that was never reached is the whole journal, and copying the whole journal is not a fork.",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
const projected = new Set(enclosing.map((e) => journalEntryKeyString(e)));
|
|
202
|
+
// A REFUSED PLAN CARRIES NO CUT. An unreached walk already returned nothing, on the ground that a
|
|
203
|
+
// plausible-looking prefix is worse than an empty one; a plan that was reached and then refused
|
|
204
|
+
// has exactly the same hazard and had exactly the opposite behaviour. A caller reading `cut`
|
|
205
|
+
// without reading `admissible` got something usable-shaped out of a fork that will not happen.
|
|
206
|
+
const cut = reached && refusals.length === 0
|
|
207
|
+
? entries.filter((e) => {
|
|
208
|
+
const k = journalEntryKeyString(e);
|
|
209
|
+
return !orphaned.has(k) && !projected.has(k);
|
|
210
|
+
})
|
|
211
|
+
: [];
|
|
212
|
+
// The respawn decision, at plan time rather than at commit time. `onFork: "respawn"` mints a
|
|
213
|
+
// fresh agent at the frontier and `"adopt"` shares the parent's; both name a durable agent
|
|
214
|
+
// handle, and `spawn` rides the durable-action machinery this host does not have. Copying the
|
|
215
|
+
// prefix anyway would produce a child that owns turns taken by an agent it can neither address
|
|
216
|
+
// nor replace.
|
|
217
|
+
for (const e of cut) {
|
|
218
|
+
if (e.kind !== "spawn")
|
|
219
|
+
continue;
|
|
220
|
+
refusals.push({
|
|
221
|
+
code: "L5019",
|
|
222
|
+
step: journalEntryKeyString(e),
|
|
223
|
+
why: "the cut contains a spawn, and a fork must respawn or adopt that agent at the frontier; both ride the durable-action machinery an agent handle comes from, which has not landed on this host. Fork at a step before the spawn, or wait for it.",
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
parent: req.parent,
|
|
228
|
+
child: req.child,
|
|
229
|
+
at: req.now(),
|
|
230
|
+
actor: req.actor,
|
|
231
|
+
fromStep: req.fromStepKey,
|
|
232
|
+
pins: req.pins,
|
|
233
|
+
cut,
|
|
234
|
+
admissible: refusals.length === 0,
|
|
235
|
+
refusals,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Copy the cut into the child, and only then create the child's record.
|
|
240
|
+
*
|
|
241
|
+
* THE ORDER IS THE DURABILITY ARGUMENT. A crash between the two writes is the case this has to be
|
|
242
|
+
* right for, and writing the spec first would leave a child that has a record and half a history —
|
|
243
|
+
* which `driveRun` would happily take over, replaying a prefix that stops in the middle of the
|
|
244
|
+
* parent's past and then performing effects from there. Written last, a crash leaves journal entries
|
|
245
|
+
* under an id with no record: `driveRun` refuses a run it cannot read a record for, and `startRun`
|
|
246
|
+
* refuses a run whose journal already has records. **Neither entry point will touch it**, which is
|
|
247
|
+
* the failure a partial fork should have.
|
|
248
|
+
*
|
|
249
|
+
* The entries are rewritten onto the child's run id as they are copied. That is not bookkeeping: a
|
|
250
|
+
* journal takes only its own run's entries, and the prefix is now the CHILD's history — the parent's
|
|
251
|
+
* copy stays exactly where it was, because fork is not rollback.
|
|
252
|
+
*/
|
|
253
|
+
export async function commitFork(kv, endpoint, plan, store) {
|
|
254
|
+
if (!plan.admissible)
|
|
255
|
+
throw new ForkNotAdmissible(plan);
|
|
256
|
+
// THIS GUARD DOES NOT COVER A CRASHED FORK, and saying so is the point of the comment. The record
|
|
257
|
+
// is written LAST precisely so a partial fork is un-drivable, which means that after a crash there
|
|
258
|
+
// is no record here to find and this check passes. What refuses the retry is one layer out: a
|
|
259
|
+
// caller gets a durable store by activating the child's journal, and an activation expecting a NEW
|
|
260
|
+
// run refuses one whose journal already has records. Both halves have cells — including one that
|
|
261
|
+
// shows a HAND-BUILT store letting the prefix land twice, because a fence in another component is
|
|
262
|
+
// a claim this file cannot make on its own.
|
|
263
|
+
const existing = await readRunRecord(kv, endpoint, plan.child);
|
|
264
|
+
if (existing !== undefined) {
|
|
265
|
+
throw new ForkNotAdmissible({
|
|
266
|
+
...plan,
|
|
267
|
+
admissible: false,
|
|
268
|
+
refusals: [
|
|
269
|
+
{
|
|
270
|
+
code: "L5017",
|
|
271
|
+
why: `run ${plan.child} already has a record; a fork mints a new run, and copying a prefix into one that already exists would rewrite somebody's history`,
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
for (const entry of plan.cut) {
|
|
277
|
+
await store.append({ ...entry, run: plan.child });
|
|
278
|
+
}
|
|
279
|
+
await createRunSpec(kv, endpoint, plan.child, { pins: plan.pins, createdAt: plan.at });
|
|
280
|
+
return { child: plan.child, copied: plan.cut.length, lineageRecorded: false };
|
|
281
|
+
}
|
|
282
|
+
/** A fork the plan refused, offered for commit anyway. The plan carries the per-step reason. */
|
|
283
|
+
export class ForkNotAdmissible extends Error {
|
|
284
|
+
plan;
|
|
285
|
+
constructor(plan) {
|
|
286
|
+
super(`run ${plan.parent} cannot fork at ${plan.fromStep}: ` +
|
|
287
|
+
plan.refusals.map((r) => `${r.code}${r.step !== undefined ? ` at ${r.step}` : ""}`).join(", ") +
|
|
288
|
+
`. The plan carries the per-step reason; nothing was written.`);
|
|
289
|
+
this.plan = plan;
|
|
290
|
+
this.name = "ForkNotAdmissible";
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* A scope wraps what its body threw, so the cut's stop can arrive inside a wrapper.
|
|
295
|
+
*
|
|
296
|
+
* Structural rather than by class: the interpreter's scope errors carry the original on `reason`,
|
|
297
|
+
* and a fork that only recognised the bare form would report "never reached" for every cut inside a
|
|
298
|
+
* `parallel` — a refusal that reads exactly like the caller naming an unreachable step.
|
|
299
|
+
*/
|
|
300
|
+
function unwrapCut(e) {
|
|
301
|
+
let cur = e;
|
|
302
|
+
for (let i = 0; i < 8; i += 1) {
|
|
303
|
+
const inner = cur?.reason;
|
|
304
|
+
if (inner === undefined || inner === cur)
|
|
305
|
+
return cur;
|
|
306
|
+
cur = inner;
|
|
307
|
+
}
|
|
308
|
+
return cur;
|
|
309
|
+
}
|
|
310
|
+
//# sourceMappingURL=fork.js.map
|
package/dist/fork.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fork.js","sourceRoot":"","sources":["../src/fork.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cotal-ai/runtime` — where a cotal-lang run is HOSTED.
|
|
3
|
+
*
|
|
4
|
+
* `packages/lang` is the language: the parser and validator, the effect interface, the step journal,
|
|
5
|
+
* the interpreter, the simulator, the dry run. It depends on nothing in this repo, and that
|
|
6
|
+
* independence is what lets a program be tested without a broker. This package is the other half:
|
|
7
|
+
* the mesh bindings that make a run durable and drivable — the journal's storage over the
|
|
8
|
+
* activation barrier, the effect handler over the real planes, and the run driver hosted on the
|
|
9
|
+
* manager daemon.
|
|
10
|
+
*/
|
|
11
|
+
export { RunJournalStore, RunJournalUnavailable } from "./journal-store.js";
|
|
12
|
+
export { startRun, driveRun, PauseToken, type RunLease, type DriveRequest, type DriveOutcome, type AdoptingHandler } from "./run-driver.js";
|
|
13
|
+
export { MeshHandler, EpfSettleWatcher, CheckpointAnswerMissing, NotYetDurable, waitConsumerName, waitConsumerConfig, rearmOutstandingPauses, outstandingPauseTokens, type MeshHandlerBinding, type SettleWatcher, } from "./mesh-handler.js";
|
|
14
|
+
export { resolveCheckpoint, openCheckpointToken, CheckpointNotOpen, type ResolveCheckpointDeps, type ResolveCheckpointRequest, type ResolveCheckpointResult, } from "./resolve-checkpoint.js";
|
|
15
|
+
export { renderRunContext, UnrenderableNotice, type RunContextRender } from "./run-context.js";
|
|
16
|
+
export { migrateRun, commitMigration, MigrationNotAdmissible, type MigrateRequest, type MigrateReport, type MigrateOrphan, type MigrateOverrides, type MigrateDivergence, type OrphanVerdict, } from "./migrate.js";
|
|
17
|
+
export { planFork, commitFork, ForkNotAdmissible, CutJournal, CutReached, type ForkRequest, type ForkPlan, type ForkRefusal, type ForkCommitResult, } from "./fork.js";
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,YAAY,EAAE,KAAK,YAAY,EAAE,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC5I,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,KAAK,kBAAkB,EACvB,KAAK,aAAa,GACnB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EACL,UAAU,EACV,eAAe,EACf,sBAAsB,EACtB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,QAAQ,EACR,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,UAAU,EACV,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,gBAAgB,GACtB,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@cotal-ai/runtime` — where a cotal-lang run is HOSTED.
|
|
3
|
+
*
|
|
4
|
+
* `packages/lang` is the language: the parser and validator, the effect interface, the step journal,
|
|
5
|
+
* the interpreter, the simulator, the dry run. It depends on nothing in this repo, and that
|
|
6
|
+
* independence is what lets a program be tested without a broker. This package is the other half:
|
|
7
|
+
* the mesh bindings that make a run durable and drivable — the journal's storage over the
|
|
8
|
+
* activation barrier, the effect handler over the real planes, and the run driver hosted on the
|
|
9
|
+
* manager daemon.
|
|
10
|
+
*/
|
|
11
|
+
export { RunJournalStore, RunJournalUnavailable } from "./journal-store.js";
|
|
12
|
+
export { startRun, driveRun, PauseToken } from "./run-driver.js";
|
|
13
|
+
export { MeshHandler, EpfSettleWatcher, CheckpointAnswerMissing, NotYetDurable, waitConsumerName, waitConsumerConfig, rearmOutstandingPauses, outstandingPauseTokens, } from "./mesh-handler.js";
|
|
14
|
+
export { resolveCheckpoint, openCheckpointToken, CheckpointNotOpen, } from "./resolve-checkpoint.js";
|
|
15
|
+
export { renderRunContext, UnrenderableNotice } from "./run-context.js";
|
|
16
|
+
export { migrateRun, commitMigration, MigrationNotAdmissible, } from "./migrate.js";
|
|
17
|
+
export { planFork, commitFork, ForkNotAdmissible, CutJournal, CutReached, } from "./fork.js";
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAA6E,MAAM,iBAAiB,CAAC;AAC5I,OAAO,EACL,WAAW,EACX,gBAAgB,EAChB,uBAAuB,EACvB,aAAa,EACb,gBAAgB,EAChB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,GAGvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,GAIlB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAyB,MAAM,kBAAkB,CAAC;AAC/F,OAAO,EACL,UAAU,EACV,eAAe,EACf,sBAAsB,GAOvB,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,QAAQ,EACR,UAAU,EACV,iBAAiB,EACjB,UAAU,EACV,UAAU,GAKX,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The step journal's durable half: a `JournalStore` over the run journal's activation barrier.
|
|
3
|
+
*
|
|
4
|
+
* `packages/lang` decides WHAT is recorded and WHEN, and hands whole entries to a store that owns
|
|
5
|
+
* only durability. This is that store for a run hosted on the mesh, and it is deliberately thin —
|
|
6
|
+
* every hard property it has (one authoritative writer, a serial pump, a head that advances only
|
|
7
|
+
* from a PubAck) belongs to `RunJournalAppender` and is not re-implemented here.
|
|
8
|
+
*
|
|
9
|
+
* What this file DOES own is the translation between two vocabularies that must not be confused:
|
|
10
|
+
*
|
|
11
|
+
* - The language's `append` contract is "resolve only once the entry is somewhere a resume on
|
|
12
|
+
* another host will find it". The appender's resolution is a PubAck, which is exactly that, so
|
|
13
|
+
* the awaits line up with no buffering in between.
|
|
14
|
+
* - The language reads a store's refusal as `L5010 JournalAppendRejected` — "the log said no",
|
|
15
|
+
* the run losing its ability to record, never an effect result. Every terminal state of the
|
|
16
|
+
* barrier is that: superseded, stalled, or refused. So this store throws for all of them, and
|
|
17
|
+
* lets the journal wrap them, rather than inventing an outcome for a step whose effect already
|
|
18
|
+
* ran.
|
|
19
|
+
*
|
|
20
|
+
* A store that could return to the interpreter after a barrier failure would be worse than one that
|
|
21
|
+
* throws: the interpreter would carry on driving a run this process no longer speaks for.
|
|
22
|
+
*/
|
|
23
|
+
import type { JournalEntry, JournalStore } from "@cotal-ai/lang";
|
|
24
|
+
import { RunJournalAppender } from "@cotal-ai/core";
|
|
25
|
+
/**
|
|
26
|
+
* A run whose journal writer is finished, offered to the interpreter as a durability failure.
|
|
27
|
+
*
|
|
28
|
+
* The distinction the barrier draws — someone else took the run, versus this appender lost its head
|
|
29
|
+
* — is preserved in `cause` for the driver, and flattened for the language, which has exactly one
|
|
30
|
+
* correct response to both: stop, and record nothing else.
|
|
31
|
+
*/
|
|
32
|
+
export declare class RunJournalUnavailable extends Error {
|
|
33
|
+
readonly run: string;
|
|
34
|
+
readonly cause: unknown;
|
|
35
|
+
/**
|
|
36
|
+
* True when it is NOT known whether the entry landed.
|
|
37
|
+
*
|
|
38
|
+
* A refusal is determinate: the stream said no, and nothing was written. A stalled append is not —
|
|
39
|
+
* the publish may already be on disk — and the language's L5010 text says which, because "the
|
|
40
|
+
* entry was not recorded" is a claim, and making it about an ambiguous append would be the
|
|
41
|
+
* durability layer telling the run the more comfortable of two possibilities.
|
|
42
|
+
*/
|
|
43
|
+
readonly indeterminate: boolean;
|
|
44
|
+
constructor(run: string, cause: unknown);
|
|
45
|
+
}
|
|
46
|
+
export declare class RunJournalStore implements JournalStore {
|
|
47
|
+
private readonly appender;
|
|
48
|
+
constructor(appender: RunJournalAppender);
|
|
49
|
+
/** True once the underlying appender is finished. A driver polls this to stop early rather than
|
|
50
|
+
* discovering it on the next entry — the interpreter has no such concept and does not need one. */
|
|
51
|
+
get isFinished(): boolean;
|
|
52
|
+
append(entry: JournalEntry): Promise<void>;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=journal-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"journal-store.d.ts","sourceRoot":"","sources":["../src/journal-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,kBAAkB,EAAoC,MAAM,gBAAgB,CAAC;AAEtF;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAY5C,QAAQ,CAAC,GAAG,EAAE,MAAM;aACF,KAAK,EAAE,OAAO;IAZlC;;;;;;;OAOG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;gBAGrB,GAAG,EAAE,MAAM,EACF,KAAK,EAAE,OAAO;CAMnC;AAED,qBAAa,eAAgB,YAAW,YAAY;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,kBAAkB;IAEzD;wGACoG;IACpG,IAAI,UAAU,IAAI,OAAO,CAExB;IAEK,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;CAqBjD"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { RunJournalAppender, RunSuperseded, RunJournalStalled } from "@cotal-ai/core";
|
|
2
|
+
/**
|
|
3
|
+
* A run whose journal writer is finished, offered to the interpreter as a durability failure.
|
|
4
|
+
*
|
|
5
|
+
* The distinction the barrier draws — someone else took the run, versus this appender lost its head
|
|
6
|
+
* — is preserved in `cause` for the driver, and flattened for the language, which has exactly one
|
|
7
|
+
* correct response to both: stop, and record nothing else.
|
|
8
|
+
*/
|
|
9
|
+
export class RunJournalUnavailable extends Error {
|
|
10
|
+
run;
|
|
11
|
+
cause;
|
|
12
|
+
/**
|
|
13
|
+
* True when it is NOT known whether the entry landed.
|
|
14
|
+
*
|
|
15
|
+
* A refusal is determinate: the stream said no, and nothing was written. A stalled append is not —
|
|
16
|
+
* the publish may already be on disk — and the language's L5010 text says which, because "the
|
|
17
|
+
* entry was not recorded" is a claim, and making it about an ambiguous append would be the
|
|
18
|
+
* durability layer telling the run the more comfortable of two possibilities.
|
|
19
|
+
*/
|
|
20
|
+
indeterminate;
|
|
21
|
+
constructor(run, cause) {
|
|
22
|
+
super(`run ${run} can no longer record: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
23
|
+
this.run = run;
|
|
24
|
+
this.cause = cause;
|
|
25
|
+
this.indeterminate = cause instanceof RunJournalStalled;
|
|
26
|
+
this.name = "RunJournalUnavailable";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class RunJournalStore {
|
|
30
|
+
appender;
|
|
31
|
+
constructor(appender) {
|
|
32
|
+
this.appender = appender;
|
|
33
|
+
}
|
|
34
|
+
/** True once the underlying appender is finished. A driver polls this to stop early rather than
|
|
35
|
+
* discovering it on the next entry — the interpreter has no such concept and does not need one. */
|
|
36
|
+
get isFinished() {
|
|
37
|
+
return this.appender.isFinished;
|
|
38
|
+
}
|
|
39
|
+
async append(entry) {
|
|
40
|
+
// A run's journal takes only that run's entries. The language checks this where a journal is
|
|
41
|
+
// SEEDED; this is the other end, where an entry is made durable — a PubAck on one run's subject
|
|
42
|
+
// must never be what makes another run's journal report an entry as recorded.
|
|
43
|
+
if (entry.run !== this.appender.run) {
|
|
44
|
+
throw new Error(`entry belongs to run ${entry.run}, but this store appends to run ${this.appender.run}; a journal entry is durable on its own run's subject or not at all`);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
// The stamp is the entry's own clock — its end when it has one, its start while it is pending
|
|
48
|
+
// — carried so a record is readable on its own. It is NOT the journal's ordering: the PubAck
|
|
49
|
+
// sequence is that, and nothing downstream sorts by this.
|
|
50
|
+
await this.appender.append(entry, entry.endedAt ?? entry.startedAt);
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
if (e instanceof RunSuperseded || e instanceof RunJournalStalled) {
|
|
54
|
+
throw new RunJournalUnavailable(this.appender.run, e);
|
|
55
|
+
}
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=journal-store.js.map
|