@source-repo/continuity
What a component keeps when the process implementing it is replaced: versioned state snapshots, adjacent forward migrations with reviewed defaults, the work a running activation was holding, a record of every value that moved — and the replacement itself, under a fence.
npm install @source-repo/continuity- Held state is explicit, and the rule is enforced — state is structure-cloned before every step, so state living in a language's object layout is refused with the reason rather than discovered at a handoff.
- One reviewed transform per adjacent version — vK to vN walks the chain, which is one place per version where somebody had to decide what a new field means.
- Three outcomes, not three degrees of success —
total,defaultedwith the field, value and grounds recorded, andimpossible, which refuses rather than inventing a value nothing downstream could tell from a measured one. - No separate dry run — migration is a pure function of an immutable snapshot, so the thing that was checked is the thing that runs.
- Determinism is checked, not assumed — every step runs twice and its outputs are compared, which catches the two that actually happen: a clock and a random value.
- A barrier, and one instant —
holdExecutionqueues arriving calls rather than rejecting them, andcaptureAtBarriertakes the values and the outstanding work in the same held breath or refuses. - Doing the work and recording it are one act —
RpcManagedRuntimearms the timer and registers the obligation in the same call, so there is no order of statements in which one happens and the other does not; a managed timer's callback runs on the component's own serial chain, where a barrier can hold it, rather than wherever the event loop delivers it. - Unmanaged writes are caught in the act — a component held at a barrier whose revision moves anyway is being changed by something the runtime never dispatched, and
settleMsrefusesunmanaged-mutationinstead of sealing values from after the barrier under a position from before it. - Every obligation gets a disposition — a timer, a call in flight, a lease or a subscription the successor says nothing about is
unhonourable, never assumed; a timer has no default policy because every policy is catastrophic somewhere. - The plan is proved twice — once while preparing, once against the snapshot actually taken at the barrier, because a component that took on work in between is owed a different set of things.
- The commit point is the compare-and-swap and nothing else — before it, abandoning is free and no caller can tell; after it, the coordinator will not put the incumbent back, because the successor may already have touched the plant.
- A fence has two halves — the local one is what the activation holds, and the one at the sink is the only one that survives a partition, because it does not require the stale activation to know anything.
- A store says what it can guarantee —
linearizable,durable,fencedAtTheSink, stated rather than implied; the in-memory reference store answersfalseto all three. - Callers address a name — a resolution carries the epoch it was taken under, and that is its shelf life; holding the address without it is a destination that stops being correct silently.
- It crosses languages — a snapshot written here verifies to the same content hash in
SourceRpc.Continuity, and a journal written here chains to the same hashes and yields the same replay plan there. Checked against fixtures both suites read verbatim rather than asserted; a chain is a stronger claim than a digest, because it is over every document and the order they are in. - Positions cross as decimal strings — JSON has one numeric type and it is a double, and a sequence position that rounds is a successor that reprocesses input or skips it. A position that arrived as a number is refused, never converted.
- Portable is stronger than cloneable — a
Date, aUint8Array, aMapand abigintall clone perfectly and none of them cross a language boundary as themselves. - A journal makes recover forward a procedure —
failed-after-commitused to be an instruction; now a snapshot plus the inputs recorded after it can be replayed into a revision that can take them. A snapshot and a journal join atlastAppliedInputSequence, and a gap refuses rather than replaying what is left, because the state on the other side of a gap never existed. - Effects on replay are declared —
suppress-effectsrebuilds state with outputs fenced, since re-applying a hundred inputs re-runs a hundred handlers;honour-idempotencyis safe only where the sinks actually deduplicate, which is a claim about the plant. - Retention is what stays replayable — compaction takes a snapshot rather than a date, and refuses where it would leave a journal that looked whole and could no longer carry the snapshot it was kept for. The chain makes an altered or removed entry detectable rather than merely absent.
- A manifest describes a revision and does not approve one — an artifact that could authorise itself by asserting its own capabilities would make the approval path decorative.
Full documentation: the package README. On npm: @source-repo/continuity.