@microdelta/resolution
v0.3.1
Published
Reuse Resolution: current candidate eligibility, source policy and direct-child validation
Readme
Reuse Resolution
This package owns current candidate eligibility, current source policy,
direct-child and nested-call validation with consumed-output cutoff, keyed
template instance resolution with tracked gates, honest misses, and execution
through injected admission and History ports. Every export is a
project-private @alpha declaration; spellings are not a public contract. See
the execution contract, the
M3 plan and the
package map.
It consumes Definition, Tracking, History and Materialization through their generated alpha declarations only. It never touches History rows, stores no finality answer, and never replays a body as validation. Admission policy and run lifetime belong to Run Supervision; storage consistency to History.
Binding family
IResolutionFamily<TInputs, THelpers> is the family a facade passes to
Definition's declarations(). Callbacks receive:
inputs: the declared input slots as one tracked record, so consumed configuration fields become evidence;helpers: each declared helper as a tracked function, so an actually called helper's implementation becomes evidence and an uncalled one does not;- sources also receive
previous(the eligible previous result's immutable{ data }carrier, orundefined) andoutcome, whosefresh(data)andretain(previous)mint the only outcomes a source may return; - memos receive Definition's declared
calls; each resolves to{ data }, a lazy view over the child's exact retained result; - supplied steps receive
args, a frozen array that behaves as the plain argument list under every ordinary idiom. Its elements are read through the observedargumentbinding. Its shape is evidence too: readinglengthor a position past the end, anincheck and key reflection all record it. An unreconstructible argument throws on read; - every callback receives
untracked(view, key), the explicit observed untracked read: it returns a scalar member without consuming it, the capture records the read, and any derived argument a memo passes afterwards is recorded unjustified, as is any forwarded path chosen afterwards.
Resolution order
createResolution(options).resolve({ step, requestKey, lease }):
- Candidates: History results for the step's scoped subject and compatibility version, latest publication first: those published in the run's environment and those a recorded promotion admits into it. A promoted candidate keeps its original provenance, so its recorded dependencies (for example a trial run's children or members) are read as historical evidence of the same analysis only; every current dependency is resolved, and executed if needed, in the run's own environment, and the acceptance is recorded there (RUN-017).
- Own evidence: each candidate's actually called implementation, consumed inputs and called helpers are compared with current facts. A source's reads of its previous result are history and are not compared.
- Source policy: only for a still-eligible source candidate is the
current finality hook evaluated.
trueretains the exact reference with a new acceptance record.falseor no hook admits the source check, which receives the eligible carrier and returns fresh data (a new publication, even when equal) orretainof exactly that carrier. Anything else fails. - Direct children (memos): each recorded witness is reconnected through
Definition; the current child is resolved under current policy and shared
within the request; only the child output facts the memo consumed are
compared. A different subject occupying the uniquely reconnected slot is
the current child. Unsupported or unreconnectable witnesses are honest
misses; a missing or wrong-scope historical child is an integrity failure.
Child views are delivered inside a
{ data }carrier, so awaiting a call never reads the child'sthenmember. 4a. Nested calls (memos whose calls carry version-2 witnesses): a memo whose supplied slot is unbound is judged with no child work at all. After the memo's own evidence, every recorded witness is reconnected through Definition before any child is resolved; then, call by call in order, its arguments are rebuilt (forwarded origins from current bindings or the current output of an earlier call, forwarded paths and derived values only when recorded as justified, never an unreconstructible one); the current child (a sibling source or memo, or the implementation currently supplied to a callable slot under that slot's subject) is validated or executed under normal admission; and only the facts consumed from that call are compared. The first failure is a distinct miss:changed,changed-child-output,missing-binding,ambiguous-binding,unreconstructible-argument,unjustified-argumentorunsupported-evidence. Children obtained while validating are shared with the parent's execution, so none runs twice in a request. A memo whose supplied slot is missing or ambiguous fails withunbound-stepbefore admission, and a body that returns while a call it started is unsettled fails without publishing. Before any attempt ends, the memo waits for every call its body started to settle, so no child write races the ending. If the body threw, its own failure is still the one reported. That wait has no default deadline, but run cancellation bounds it (#106): each child's admission, body and nested waits run through Run Supervision's ports, so once a hard stop or an operator deadline takes effect every in-flight child ends interrupted, even one whose author code never settles, and the parent ends cancelled (or with its own error when its body had already thrown), never with a partial publication. - Admission and execution: work that validation could not avoid is
admitted before any claim, attempt or body. A denial or a cancellation is a
typed
refusedoutcome whosedispositioncarries the decision's kind. Admitted work allocates an attempt keyed by the request key and the structural invocation, with the complete current intent digest, then runs under capture and publishes through History. - Run cancellation: every admitted body and current-policy hook runs
through the optional
executionport (Run Supervision's cancellation port), and each publication commit first asks it whether cancellation forbids committing now, in the same synchronous turn as the commit. An interrupted execution, or output a hard stop discards before its commit, ends the attemptinterruptedwith astoppedending and reports arefusedoutcome with dispositioncancelled. Anunsettledexecution (its body met an external operation deferred until a later time, or with an unknown outcome) ends the attemptinterruptedwith anunsettledending and reports arefusedoutcome with dispositiondenied, so the step stays pending and a strict consumer waits. Each admitted body's execution names its claimed attempt (scoped subject and History attempt identity), by which Supervision addresses and correlates its external operations. Without the port, work runs unsupervised and every commit may proceed.
The members of one members request, strict fold or outcome fold resolve concurrently
within Run Supervision's bounded active window: Resolution presents each
member to the port's member() in canonical key order, and members are
started and reported in that order (RUN-002). A member not yet started holds
nothing. Without the port, members resolve one at a time.
Template instances
A template instance is addressed by its template step descriptor plus a
member key. Resolving one (directly, or for every member through
resolveMembers({ template, step, requestKey, lease })):
- Discovery: the template's composition-level collection source is resolved under its current source policy, once per request.
- Keying: its exact current result is keyed by Definition before any gate
or member body. A rejected snapshot (duplicate or missing key, failed
custom key, malformed snapshot) admits no gate or member work: a members
request reports
rejectedwith Definition's diagnostic, and a direct request fails withcollection-rejected. A member absent from the current snapshot is anunbound-step. - Gate: each member's gate runs once per request in its own tracking
frame, over the declared inputs and helpers and the member's current record
at the
memberbinding. Its observations are the member's gate evidence and enter no step's provenance. An explicitfalseis askippedoutcome that admits, publishes and retracts nothing; a non-boolean result or a throw is agate-failure, never a skip. - Instance: a required instance resolves like any source or memo, with
its member binding. Its callbacks read
member, a view of the member's current record at thememberbinding; those reads are the step's own observations and are validated against the current record on reuse. A forwardedmemberorigin is rebuilt from the same record. So an unread discovery field changes nothing, while a consumed member field reruns only the member steps that consumed it.
Candidates are found by subject, which an instance keeps when its template is
renamed or its collection moves to another slot. That is changed
correspondence: a template-bearing candidate whose recorded step differs from
the current descriptor is a correspondence miss, never a remap.
resolveMembers resolves members in canonical key order, independently: a
member whose evidence is ready completes and publishes while discovery is
open or siblings fail, are refused or are skipped. Each member reports its
normal outcome or its member-attributable typed failure (execution failure,
gate-failure, unbound-step and the like). Run-level failures
(admission-failure, observer-failure, integrity, wrong-intent,
invalid-request, History or host errors) reject the whole request. A
cancelled child makes its parent's refusal cancelled, whichever child was
refused first. A gate returning a promise or thenable is a gate-failure.
Strict folds
resolveFold({ step, requestKey, lease }) resolves one strict fold (CMP-8,
RUN-005, RUN-010). resolve and check refuse a fold step with
invalid-request, because their outcomes cannot express waiting, readiness
failure or coverage.
- Members: the fold's consumed template step is settled for every
current member exactly as
resolveMemberssettles it, so a ready member completes whatever the fold then decides. A run-level failure rejects the fold request itself, so an outage is never a terminal member failure. - Readiness, failure first: a failed or cancelled required member, a
rejected snapshot or cancelled discovery work makes the outcome
failed. It names the failed, cancelled and pending keys and whether discovery is open. Otherwise, open discovery, denied discovery work or a pending member makes itwaiting. Neither runs the body, admits fold work or publishes. - Validate or execute: only a ready fold looks at its candidates. The
body receives one explicit entry per current member in canonical key
order:
succeededwith a lazy view of the member's accepted result, orskippedwith no data (reading a skipped entry's data throws). - Coverage: a
reusedorpublishedfold carriescoverage: { required, skipped, closed: true }. The framework derives it from the members it delivered, never from the body's result.
The fold's evidence is the template step it consumed, its membership-and-status
fact (each member key, included with its exact result or skipped), and the
facts its body read from each included member's entry. The gate's raw reads
stay the instance's own evidence. A candidate is reused only when three things
hold:
- It consumed the same template step. A renamed step or template, or a moved
collection, is a
correspondencemiss, never a remap. - Its recorded fact equals the one current discovery and gate outcomes
establish now (
changed-membershipotherwise). - Each consumed member fact is unchanged (
changed-member-outputotherwise). So a gate flip, insertion or deletion reruns the fold, while a reorder or a threshold edit that flips no gate reruns nothing. A member change reruns the fold only when a fact it consumed changed. Skips and deletions retract nothing.
Outcome folds
resolveOutcomeFold({ step, requestKey, lease }) resolves one outcome
(tolerant) fold (RUN-010). resolve and check refuse it with
invalid-request, as they refuse a strict fold.
- Members: the consumed template step is settled for every current
member exactly as
resolveMemberssettles it. Each member settles assucceeded(an accepted result),skipped,failed(a member-attributable typed failure) orcancelled(work withdrawn from this run: refused by admission as cancelled, or interrupted by a stop). Denied work leaves itpending, which is unsettled. - Completeness: no member status fails an outcome fold. A rejected
snapshot or cancelled discovery work makes the outcome
failed, because no population can be established in this pass. Otherwise open discovery, denied discovery work or a pending member makes itwaiting, with the partial coverage settled so far. Neither runs the body, admits fold work or publishes: the fold never claims a complete set while anything is unsettled. - Validate or execute: once every member of a closed population has
settled, the fold is validated or executed exactly as a strict fold is. Its
body receives one entry per member with its settled status (only
succeededcarries data). Its membership-and-status fact also recordsfailedandcancelledmembers, so repairing a failed member changes it and the fold reconsiders, while unaffected member results are reused. - Coverage: every outcome except
failedcarriescoverage: { succeeded, skipped, failed, cancelled, pending, openDiscovery, complete }, derived from how members settled, never from the body.completeis true exactly when discovery is closed and nothing is pending; it is never a claim of complete success.
An outcome fold records version-4 provenance, so neither fold contract ever
accepts the other's result: such a candidate is unsupported-evidence.
Checks and recovery
check({ step }) reports reusable, execution-required, uncertain at a
needed source boundary, or skipped for a gated-out instance, without
admission, attempts, bodies or writes.
recover({ step, requestKey }) recomputes the attempt key and intent without
running author code and reports the identified execution's durable outcome,
a strict or outcome fold's included. A different intent is rejected and nothing is
executed automatically.
Durable evidence
Provenance, acceptance and attempt-ending records use the versioned formats
microdelta.resolution.provenance, .acceptance and .attempt-ending,
documented in src/evidence.ts. History stores them opaquely. Version 1 keeps
its M3 meaning (sources and memos with direct children by slot). Version 2 of
provenance records a nested memo's ordered calls (each witness, exact child
result and call binding) or a supplied step; version 2 of acceptance names
each call's current result by position. Version 3 of provenance records a
strict fold's consumed template step, its membership-and-status fact (keys in
canonical order, each included with its exact result or skipped) and its
consumed member facts at entry bindings; version 3 of acceptance names each
included member's current result by key. Any other version is unsupported
evidence. A supported record must carry the step's own implementation
observation, and a source record has no child edges. A fold record's consumed
template step is template-bearing with no member key, and its consumed member
facts name only included members. A record that breaks any of these is an
integrity failure.
An outcome's trace is the requested step's own lifecycle; its diagnostics
cover the whole request, including nested children, once each.
A write that needs the request's writer lease (a claim, a publication, an
acceptance, an attempt ending) may be refused by History because the lease
expired or a successor took it over. That write records nothing, and the step
that needed it is denied with reason lease-lost instead of the request
rejecting with History's stale-writer error. An attempt it had claimed stays
incomplete for the successor to recover (EXP-8 ruling R).
Failure messages and diagnostics name the step and the failure kind. They never
repeat what author or caller code threw; the thrown value is the failure's
cause.
Tests
Owner tests cover the outcome-envelope registry and the typed family (tsd).
Behavioral suites run in the facade's assembly tests
(packages/core/test/resolution, packages/core/test/nested for nested
validation, packages/core/test/keyed for template instances and
packages/core/test/fold for strict folds, each including separate-process
restarts) because only assembly may
compose History with Node's real SQLite capability. An on-demand mutation-control runner there
plants single wrong behaviors into this package's emitted build.
