Bombay Behavior
Bombay Behavior provides a pure, statically typed actor transition algebra and
a catalogue of reusable actors. A behavior processes one communication at a
time and returns explicit Actions: typed communications, staged fresh actor
creations, and its next behavior or termination decision. Scheduling,
transport, clocks, allocation, and effect execution remain interpreter
responsibilities.
This guide has three documentation classes:
- Canonical contracts describe the current behavior algebra, application authoring syntax, composition laws, and interpreter obligations.
- Actor catalogue documents the current reusable atomic actors and their normalized aggregate laws.
- Engineering records preserve research, rejected alternatives, audit evidence, and implementation campaign decisions. They explain why the current design exists but are not an additional public API contract.
The generated API references are published alongside this guide:
When a historical engineering record conflicts with the crate API or a canonical contract, the current public types and canonical contract govern.
Actor transition algebra
This is the canonical semantic map for Bombay Behavior.
Laws and project constructions
The actor-model laws preserved here are:
- an actor processes one accepted communication at a time;
- a transition may communicate with known recipients, create fresh actors, and designate the behavior used for the next communication; and
- a newly allocated actor address is fresh with respect to the actor configuration.
Bombay derives a concrete typed construction from those laws:
Behavior::transition(ActiveTurn, Behavior::Event)
-> Result<Actions<Addr, Phase, Sends, Birth>, Error>
Actions is the only effect boundary. Typed send products, closed creation
products, initialization effects, phases, explicit termination, structural
event paths, creator-local child routing, and interpretation order are Bombay
constructions and policies, not claims about the surface syntax of Agha's
formal calculus.
Four orthogonal roles
| Role | Owns |
|---|---|
Protocol | canonical destination identity, address namespace, message type |
Behavior::Event | every public and interpreter-originated event accepted by a concrete behavior |
Behavior | state, initialization, and the pure event fold |
Actions | all communications, staged fresh creations, and the next-state verdict |
A protocol is not a behavior. It has no state, initialization, internal event lanes, effects, errors, phases, or birth capabilities. A behavior is not a protocol supertrait: senders need only the destination's public signature, not proof of its current implementation.
A nominal actor may implement both traits and use Behavior::Protocol = Self.
Transparent wrappers preserve the inner protocol while changing the concrete
behavior and event/effect algebra.
InitializationTurn and ActiveTurn cannot be constructed directly by an
application. The public initialize(&mut B) and
delegate_transition(&mut B, event) functions are trusted composition and
runtime ports: they can be called repeatedly, and the latter can run before
initialization. The consuming behavior_actors::Activate::initialize path
enforces one initialization for its owned definition and returns Active<B>
for mailbox ingress. A runtime or wrapper using the raw ports must enforce
the same lifecycle order itself.
Destination evidence
| Type | Evidence | Requires address lookup? |
|---|---|---|
Recipient<P> | logical address with canonical protocol P | yes |
Delivery<P> | logical recipient plus P::Msg | yes |
ChildDelivery<P, O> | message to a committed local occurrence/nonce binding | no protocol-wide lookup |
EstablishedRecipient<P> | exact installed endpoint for P | no |
EstablishedDelivery<P> | exact endpoint plus P::Msg | no |
EstablishedActor<B> | exact installed B endpoint and matching lifecycle authority | no |
P is always canonical protocol identity. O is structural occurrence
evidence used to navigate duplicate child declarations. It is not a key,
address, or second identity.
The runtime selects exact message endpoints through
RecipientAddress::Established<P>. Actor-hosting namespaces additionally
select EndpointAddress::Installed<B>, which projects the endpoint and keeps
matching B::Event authority. Generic application types and protocol
owners do not implement key traits or carry runtime types. The inert endpoint
family requires Clone, not Send. InterpretSends and concrete request
interpretation require Send when an effect actually crosses an asynchronous
executor boundary.
Fresh creation
CreateChild<A, C> stages a concrete child behavior, creator-local nonce, and
CreationKind. Staging allocates nothing. The nonce cannot be converted to an
address and proves neither identity nor freshness.
The interpreter performs, in order:
- reserve an address fresh with respect to the actor configuration, without publishing a live endpoint;
- run the child's pure definition initialization fold;
- install the endpoint and commit the creator-local nonce binding;
- interpret and settle all initialization effects; and
- permit ordinary mailbox ingress only if initialization continues and its effects settle successfully.
Only the committed path may produce
ChildCreationOutcome<C, Occurrence>::Established(CommittedChild<C, Occurrence>).
A later named report may return EstablishedCreation<C, Occurrence>::Installed
with the same committed product. Allocation rejection returns
the complete staged creation before the initialization fold. Pure
initialization rejection returns the current child and exact error; host
rejection after that fold returns the current child and uninterpreted
initialization Actions. A caught pure initialization panic returns the
extant current child through InitializationPanicked, without claiming an
initialization error or accepted actions. None of these outcomes commits a
binding. Once committed,
an initialization-effect failure belongs to the installed child's drain and
cannot be reported as a creation rejection. An initialization Stop still
settles its final actions and never enables ordinary ingress.
A nonce collision is rejection, never replacement or overwrite.
These commit and settlement steps are Bombay policy, not Agha's allocation
law. Agha et al.'s newadr and initbeh
are separate model operations; Bombay
packages fresh allocation, the pure fold, and establishment into one staged
creation request.
CreationKind::ReplacementIncarnation records Behavior-authored provenance.
It is still fresh allocation. A runtime may report a restart only after the
corresponding replacement creation commits successfully.
Creation facts are indexed by canonical child protocol and structural
occurrence—not by an entire parent behavior. A fact can be strengthened to
EstablishedActor<RoleChild<Parent, Occurrence>> only at the topology boundary
where ChildRole<Parent> genuinely proves the concrete installed behavior.
This avoids forcing arbitrary consumers or wrappers to pretend to be actors.
Event and effect composition
EventLayer<Owned, Inner> forms a closed event sum. Here identifies the
current owner and Inside<Path> identifies an inner owner. InjectEvent
constructs exactly the selected lane; it never searches by payload type.
SendLayer<Owned, Inner> is the corresponding named effect product.
SendsFor<Event> proves that every interpreter request returning to its
emitter has a valid structural ingress. InterpretSends visits inner effects
before wrapper-owned effects, preserving initialization and delegated
transition order. Independent named lanes retain their own order; no global
order is invented between unrelated lanes.
Composition must preserve these invariants:
- every accepted event produces one result;
- mapping sends preserves creations and the exact next verdict;
- mapping the next verdict preserves sends and creation order;
- wrapping cannot drop, duplicate, reinterpret, or consume an inner lane;
- duplicate structural occurrences remain distinct;
- established, child, and external destinations are not reindexed merely because the emitting behavior is wrapped; and
- controlled failure emits no partial effects.
Static interpretation
ActionItem fixes the outcome vocabulary of each emitted value.
SendSettlements fixes the complete product shape independently of any
runtime. InterpretItem and InterpretSends are the monomorphized runtime
capabilities. Closed child sums use DispatchBirth and one
EstablishChild<Occurrence, Child> implementation per concrete occurrence.
Missing support fails to compile.
ChildOccurrenceProduct is the structural product needed by runtimes that
retain creator-local state per direct child occurrence. Behavior owns the closed
Behavior leaf / ChildChoice / Never recursion and supplies the same
ChildHead / ChildTail<_> navigation evidence used by child creation. A
runtime-owned ChildOccurrenceShape supplies only the storage constructor.
This is a type-level derived construction, not an actor operation: it performs
no transition, allocation, binding, lookup, or effect interpretation.
Transitive descendants remain owned by the concrete child actors that may
create them.
ResolveChildOccurrence<O> supplies the inverse static connection needed when
an effect names O: the running emitter and occurrence determine one exact
direct child behavior and structural position. Generated nominal roles are
resolved through BehaviorBase only while protocol and birth topology remain
identical; raw ChildHead/ChildTail<_> positions inspect the emitter's own
closed birth node. Consequently a supervision wrapper that replaces a direct
child with a proxy cannot silently reinterpret the base role. This is
navigation evidence derived from Behavior, not a new identity or effect.
No core path uses trait objects, runtime protocol registries, reflection, downcasting, type-name dispatch, serialization, or unsafe type escape hatches.
Code map
crates/behavior/src/transition.rs: protocol and behavior contractscrates/behavior/src/effects/: actions and static send interpretationcrates/behavior/src/actor/addressing.rs: logical and established recipientscrates/behavior/src/actor/creation.rs: staged creation and structural dispatchcrates/actors/src/protocol/established.rs: exact creation, observation, and shutdown protocolscrates/actors/src/requirements.rs: occurrence-preserving installation requirements
Bombay Behavior core type inventory
Scope
This document inventories the types exported at the root of the
bombay-behavior crate (behavior in Rust source). The authority for the list
is crates/behavior/src/lib.rs; semantic descriptions are taken from the
defining modules in crates/behavior/src. It describes the current source
surface, not a proposed redesign.
The inventory includes public traits, structs, enums, and type aliases. It
also identifies the crate's public functions and #[behavior] macro because
they construct or consume the types. It excludes private module items,
test-only fixtures, and downstream types generated in a user's crate except
for a separate description of the macro-generated family.
Some root exports are marked #[doc(hidden)]. They remain listed because they
are publicly nameable implementation contracts, but ordinary behavior authors
should not treat them as the preferred API.
Semantic center
The central relationship is:
Protocol = address namespace × public message type
Behavior = protocol
× complete event algebra
× send product
× birth algebra
× phase menu
× controlled error
BehaviorActed<B>
= Result<
Actions<BehaviorAddr<B>, B::Ph, B::Sends, B::Birth>,
B::Error,
>
Actions = sends
× ordered staged fresh creations
× (Continue | Goto(phase) | Stop(Stopped))
Protocol is stable public communication identity. Behavior is the concrete
stateful fold implementing that protocol. They are deliberately separate:
recipients and deliveries do not recursively carry a destination behavior's
sends, births, phases, or errors.
Actions is Bombay's typed realization of actor-transition effects. It is not
described as a literal Agha effect triple: the Rust surface additionally owns
typed send products, creator-local child routing, phases, controlled errors,
termination, initialization effects, and Bombay interpretation ordering.
Protocol, behavior, and transition types
| Type | Kind | Semantic role |
|---|---|---|
Protocol | Trait | Associates one stable public actor identity with Addr: Address and Msg. It is independent of transition implementation. |
MessageProtocol<A, M> | Zero-state struct | Reusable nominal-free protocol signature for address type A and message type M. |
Behavior | Trait | Pure initialized fold from one complete typed event to BehaviorActed<Self>. Associated types expose protocol, event, sends, phases, controlled error, and birth capability. |
BehaviorActed<B> | Type alias | Exact controlled result type of behavior B: Acted<BehaviorAddr<B>, B::Ph, B::Sends, B::Birth, B::Error>. |
BehaviorAddr<B> | Type alias | Projects the address namespace from B::Protocol. |
BehaviorMessage<B> | Type alias | Projects the public message type from B::Protocol. |
InitializationTurn | Non-constructible struct | Prevents direct caller fabrication; trusted public composition ports can issue it repeatedly, while the consuming Activate path enforces one initialization for its owned definition. |
ActiveTurn | Non-constructible struct | Lifecycle- or composition-issued authority to invoke one active transition. |
BehaviorLayer<B> | Trait | Statically constructs one fully concrete behavior from another; closures implement it without trait objects or effects. |
BehaviorBase | Trait | Projects a composed wrapper to its authored base behavior without exposing wrapper depth. |
LogicalHostRequirements | Trait | Derives the ordered, duplicate-preserving logical protocol hosts required by a behavior's sends and transitive births. It is static evidence, not allocation. |
The associated type equation of Behavior is:
Behavior {
Protocol: Protocol
Event: UserEvent<Addr = BehaviorAddr<Self>,
Message = BehaviorMessage<Self>>
Sends: SendEffects + SendsFor<Event>
Ph
Error
Birth: BirthMode
}
Next-state and action types
| Type | Kind | Semantic role |
|---|---|---|
Never | Uninhabited enum | Proves absence. It is used for no phase transitions, impossible events, and empty closed sums. |
Stopped | Unit struct | Payload-free normal behavior-termination marker. Lifecycle provenance belongs in typed protocols, not this seat. |
Step<Ph, R> | Enum | Exhaustive next verdict: Continue, Goto(Ph), or Stop(R). |
Become<Ph> | Type alias | Behavior next verdict, fixed to Step<Ph, Stopped>. |
Actions<A, Ph, Sends, Birth> | Struct | Explicit transition product with named sends, ordered creates, and become_ legs. The interpreter commits creations before same-action sends that may depend on them. |
Acted<A, Ph, Sends, Birth, E> | Type alias | Result<Actions<A, Ph, Sends, Birth>, E>. |
AppendSend<Input, Path> | Trait | Appends one input to a statically selected send lane while preserving creation and next-state legs. |
Actions constructors (cont, stop, goto, send, and create) construct
values only. They do not interpret sends, allocate actors, change runtime
state, or perform lifecycle work.
Addressing and installed-capability types
| Type | Kind | Semantic role |
|---|---|---|
Address | Trait | Defines a pure logical address namespace and its creator-local Nonce. A nonce is correlation, not address or freshness proof. |
MailAddr | Newtype struct | Built-in u64 logical address whose nonce is also u64. |
EndpointAddress | Trait | Runtime-owned projection from a logical address namespace and protocol to an exact endpoint representation. |
Recipient<P> | Struct | Pure logical destination for protocol P; proves protocol/address/message agreement but not installation. |
EstablishedRecipient<P> | Struct | Inert runtime-issued capability for one exact installed incarnation of protocol P. Its endpoint is not directly exposed. |
InterpretEstablished<P> | Trait | Explicit power-user boundary that consumes an established recipient's exact endpoint. |
EstablishedActor<B> | Struct | Exact installed capability that preserves both the public recipient and the concrete installed behavior type B. |
Delivery<P> | Struct | Pure logical communication containing Recipient<P> and P::Msg. |
EstablishedDelivery<P> | Struct | Pure communication to one exact EstablishedRecipient<P> without logical-address resolution. |
The capability strength increases from logical naming to installed evidence:
Recipient<P>
logical protocol destination
EstablishedRecipient<P>
exact installed endpoint for P
EstablishedActor<B>
exact installed endpoint for B::Protocol
+ static proof of concrete behavior B
None of these values has an ambient send, shutdown, allocation, or lookup method. Effects cross explicit interpreter traits.
Event and ingress types
| Type | Kind | Semantic role |
|---|---|---|
User<A, M> | Struct | Base public user-message event with from: A and message: M. |
UserEvent | Trait | Constructs and extracts the public User lane through a complete composed event algebra. |
EventLayer<Owned, Inner> | Enum | Concrete coproduct `Owned(Owned) |
ComposedEvent | Trait | Identifies an event algebra's inner event and its structure-preserving injection. |
EventIngress<Source, Input> | Trait | Owner-selected construction of one input lane without caller-visible wrapper paths. |
ChildInputIngress<Source, Input> | Trait | Constructs a private parent-to-child input in the concrete child's event algebra. |
InjectEvent<Input, Path> | Trait | Low-level path-indexed injection into a structural event coproduct. |
Ingress<Input, Path> | Zero-state struct | Address-free capability selecting one exact interpreter-return ingress member. |
Here | Unit struct | Compile-time path selecting the current event or send layer. |
Inside<Path> | Zero-state struct | Compile-time path selecting an inner layer. |
Here and Inside<Path> are structural proof types, not runtime routing
addresses. Ordinary composed templates should prefer semantic
EventIngress<Source, Input> implementations so callers do not count wrapper
depth.
Send products and interpretation types
Send-product algebra
| Type | Kind | Semantic role |
|---|---|---|
SendEffects | Trait | Closed value algebra with empty, ordered append, and statically selected lane emission. |
SendsFor<Event> | Marker trait | Proves that a send product's returning interpreter requests are lawful for the exact complete event algebra. |
SendInput<Input, Path> | Trait | Selects one request lane at compile time and emits into it. |
Own | Uninhabited marker enum | Selects a named send product's own semantic lane. |
NoSends | Unit struct | Named empty send product. |
SendLayer<Owned, Inner> | Struct | Named product of wrapper-owned and inner send effects. Interpretation preserves inner-to-outer authored order. |
LogicalDeliveryProtocols | Trait | Projects possible logical recipients from a send product while preserving order and duplicates. |
InterpreterRequest | Trait | Declares a request's emitter continuation and its possible logical recipient protocols. |
InterpreterRequests<M> | Struct | Ordered send lane of runtime-local requests with no actor address. |
NoReturnToEmitter | Uninhabited enum | Declares that an interpreter request produces no later local event. |
ReturnsToEmitter<Input, Path> | Zero-state struct | Declares a later local event of Input at a compile-time event path. |
ReportToParent<R> | Struct | Transfers an owned report through the established creator/child relationship; the interpreter attaches the exact occurrence-local creation ID. |
The crate supplies SendEffects/interpretation implementations for selected
Vec<T> lanes, including logical deliveries, established deliveries,
creator-local child deliveries and inputs, and Vec<Never>. A general
Vec<T> is a send value container, but only statically supported effect kinds
receive interpreter meaning.
Interpreter capabilities
| Type | Kind | Semantic role |
|---|---|---|
ActionItem | Trait | Fixes one action item's accepted receipt, rejection, and prerequisite types for every runtime. |
ItemSettlement<Item, Accepted, Rejection, Prerequisite> | Enum | Conserves one attempted item across acceptance, rejection, prerequisite blocking, and interpreter corruption. |
SettledItem<Item, Settlement> | Enum | Distinguishes an attempted item from an untouched item after earlier corruption. |
Interpretation<Settlement> | Enum | Owns the complete product after successful traversal or corruption. |
SendSettlements | Trait | Projects one concrete sends product to its runtime-independent settlement product. |
ActionSettlements | Trait | Projects one concrete Actions product to its complete creation-and-send settlement. |
BehaviorSettlements | Trait | Blanket projection from a concrete behavior to that exact action-settlement type without restating internal birth or send bounds. |
InterpretItem<Item, RootEvent, Path> | Trait | Lets one concrete runtime attempt exactly one statically selected action item. |
InterpretSends<Interpreter, RootEvent, Path> | Trait | Exhaustively interprets one complete send product in structural order. |
SourceSettlementCustody<Host, RootEvent> | Trait | Offers at most one emitter-return settlement input in declared order while retaining the exact residual product. |
SourceCustody<Residual> | Enum | Distinguishes an exhausted product, exactly one admitted input, and closed admission with complete residual ownership. |
These are statically dispatched interpreter obligations. A composite send product cannot silently omit a lane: the concrete interpreter must implement every required capability or fail to compile. Source custody uses the same declared order, but returns after one successful admission so the runtime can process that input and all transitive effects before offering the next one.
Staged creation and lifecycle result types
Creation values and outcomes
| Type | Kind | Semantic role |
|---|---|---|
CreationId | Newtype struct | Opaque correlation within one statically declared child occurrence; it is not an address, route, identity, or provenance. |
CreationSequence | Struct | Checked source of non-reused IDs. Retaining one per occurrence is sufficient; one owner may share a sequence across several occurrences for a stronger guarantee. |
CreationKind | Enum | Behavior-owned intent: ordinary Birth or fresh Replacement { previous }. |
CreateChild<A, New> | Struct | Pure staged request containing a creation ID, owned child behavior, and creation intent. |
Creations<Item> | Struct | One ordered creation batch. Runtime route preparation accepts the whole batch or returns it untouched. |
RoutedCreation<A, New> | Hidden struct | Interpreter-private pairing of a complete creation with its selected runtime route. |
AllocationRejection | Enum | Typed fresh-address failures: exhaustion or already-claimed proposed address. |
ChildNamespaceExhausted | Struct | The interpreter cannot route the complete declared creation batch without partial route consumption. |
CreationRejection | Enum | Complete rejected-child reasons after routing: allocation, initialization, or environment/commit failure. |
CommittedChild<C, Occurrence> | Struct | One committed child's ID, creation kind, occurrence, and exact installed C actor. |
EstablishedCreation<C, Occurrence> | Enum | Named Installed(CommittedChild) or Rejected result for one concrete child occurrence. |
ObserveCreation<P, Occurrence> | Struct | Same-action request for the exact protocol/occurrence creation result; returns CreationResolved<P::Addr> and depends on CreationCorrelation<P, Occurrence>. |
ChildDelivery<P, Occurrence> | Struct | Same-action public-protocol delivery to a declared creator-local child occurrence. |
ChildInput<Child, Source, Input, Occurrence> | Struct | Private typed input to a concrete declared child event lane. |
ChildReport<R> | Struct | Parent event payload containing the interpreter-attached creation ID and owned report. |
Creation is staged, not performed by CreateChild. Behavior issues a
CreationId and emits the child in the same transition. Exact correlation is
the static child occurrence together with that ID. Independent layers may use
equal numeric IDs for distinct occurrences; reuse within one occurrence remains
invalid. The interpreter privately selects routes for the complete Creations
batch before it hosts any child. A creation becomes established only after fresh
allocation, initialization, installation, commit, and binding. Behavior never
constructs or stores a runtime route.
Heterogeneous creation products
| Type | Kind | Semantic role |
|---|---|---|
Children<A, Product> | Struct | Builder for a pure ordered heterogeneous product of staged direct-child creations. |
NoChildren | Unit struct | Empty heterogeneous creation product. |
ChildCons<A, C, Earlier> | Struct | One creation appended to an earlier heterogeneous product. |
ChildProduct<A> | Trait | Sealed recursive conversion from Children's product into one ordered Creations batch over a closed child choice. |
Each Children::child or Children::create call adds one new structural child
occurrence, so conversion into Creations is total. Equal IDs in distinct
occurrences remain distinct correlations. Runtime route exhaustion returns the
entire batch through ChildNamespaceExhausted.
Closed birth and topology type algebra
Birth capability and child sums
| Type | Kind | Semantic role |
|---|---|---|
BirthMode | Trait | Associates a behavior with its closed child type algebra. |
NoBirths | Unit struct | Birth mode whose child algebra is Never. |
Births<C> | Zero-state struct | Birth mode admitting closed child algebra C. |
ChildChoice<Head, Tail> | Enum | Closed recursive heterogeneous sum of concrete child behaviors. |
ChildHead | Unit struct | Structural position selecting a sum's head. |
ChildTail<Position> | Zero-state struct | Structural position selecting inside a sum's tail. |
ChildPosition<Children, Child> | Sealed proof trait | Proves that an exact child behavior occupies an exact structural position. |
BirthNodeAppend<Tail> | Sealed composition trait | Appends one closed direct-child algebra after another while preserving existing positions and creation order. |
BirthNodeAt<Position> | Hidden sealed trait | Inverse projection from a structural position to its child type. |
ChildChoice is a creation-only sum, not a message envelope, behavior trait
object, registry, or runtime dispatcher.
Nominal child roles and occurrence resolution
| Type | Kind | Semantic role |
|---|---|---|
ChildRole<Parent> | Trait | Authored proof that one nominal role names one exact direct child and structural position of Parent. |
ChildOccurrence<Parent> | Trait | Declares the sealed descriptor used to resolve one nominal or raw structural occurrence. |
DeclaredChildOccurrence | Hidden unit struct | Descriptor for an authored nominal child role. |
StructuralChildOccurrence<Position> | Hidden zero-state struct | Descriptor for a raw structural child position. |
ChildOccurrenceResolution<Parent, Occurrence> | Hidden sealed trait | Restricts which descriptor may resolve a given occurrence. |
ResolveChildOccurrence<Occurrence> | Sealed trait | Resolves an occurrence against the concrete emitter, following topology-transparent BehaviorBase wrappers lawfully. |
ResolvedChild<Emitter, Occurrence> | Type alias | Projects the exact resolved child behavior. |
ResolvedChildPosition<Emitter, Occurrence> | Type alias | Projects the exact resolved structural birth position. |
RoleChild<Parent, Role> | Type alias | Projects the child behavior selected by one nominal role. |
RoleProtocol<Parent, Role> | Type alias | Projects the canonical protocol of a role-selected child. |
Nominal roles distinguish duplicate occurrences even when they contain the same child behavior. A role and its structural position are topology evidence, not protocol identity, actor identity, or runtime lookup keys.
Interpreter dispatch and child occurrence products
| Type | Kind | Semantic role |
|---|---|---|
ChildCreationOutcome<C, Occurrence> | Enum | Established child, initialization rejection retaining child/error, caught pure-fold panic retaining the extant child, or host rejection retaining child/uninterpreted initialization actions/reason. |
EstablishChild<Occurrence, C> | Trait | Concrete interpreter ownership port returning the fixed ChildCreationOutcome result for C at one exact occurrence. |
ChildCreationProduct<A, Occurrence> | Hidden trait | Runtime-independent result product for a closed creation-only child sum. |
DispatchBirth<A, Host> | Trait | Exhaustive static dispatch over one closed creation-only child sum. |
ChildOccurrenceShape | Trait | Downstream type constructor defining empty and per-child representations for a direct-child occurrence product. |
ChildOccurrenceProduct<Shape> | Sealed trait | Selects a shape-owned static representation for a closed direct-child algebra. |
ChildOccurrences<Children, Shape> | Type alias | Occurrence-preserving representation selected by one child shape. |
These types let an interpreter prove support for every child alternative at compile time. They do not perform runtime protocol lookup or erase the child behavior type.
Protocol and host projections
| Type | Kind | Semantic role |
|---|---|---|
NoBirthProtocols | Unit struct | Empty projected protocol product. |
BirthProtocol<P, Tail> | Zero-state struct | One protocol occurrence followed by a remaining projected product. |
BirthProtocolHead | Unit struct | Structural position selecting the current projected protocol. |
BirthProtocolTail<Position> | Zero-state struct | Structural position selecting inside the remaining protocol projection. |
BirthProtocolAt<P, Position> | Marker trait | Static membership evidence for one protocol occurrence. |
BirthProtocolProduct | Hidden trait | Closed append operation over protocol products. |
BirthProtocols | Trait | Projects a behavior's own protocol and every protocol reachable through transitive births. |
BirthNodeProtocols | Hidden trait | Recursively projects protocols from one closed birth node. |
BirthNodeLogicalHosts | Hidden trait | Recursively projects logical host requirements from one closed birth node. |
Protocol products preserve repeated occurrences. They are static evidence and perform no allocation, hosting, normalization, or runtime lookup.
Finite sequence observation
The Behavior crate exposes no mailbox reducer or finite-stream result. Its
contract ends at one initialization or event transition and the resulting
Actions. Runtime scheduling belongs to Bombay. Repository tests use
behavior_testkit::drive, whose Trace preserves the final active behavior,
ordered sends and creations, initialization-inclusive transition count,
pending mailbox suffix, and an exhaustive DriveDisposition distinguishing a
drained mailbox from BehaviorStopped(Stopped).
Public companion operations
| Item | Kind | Role |
|---|---|---|
initialize | Hidden function | Canonical wrapper boundary invoking one inner initialization fold and returning its complete actions unchanged. |
delegate_transition | Hidden function | Canonical wrapper boundary invoking one inner event fold and returning its complete actions unchanged. |
behavior | Attribute macro | Generates the mechanical Protocol, Behavior, BehaviorBase, closed send product, and closed birth-role wiring for an inherent impl. |
For an actor named Actor, the macro may generate these concrete types when
the corresponding declarations exist:
ActorSends
ActorSends<Field> one uninhabited selector per send lane
ActorActions fluent action-extension trait
ActorChildren closed ChildChoice alias
ActorChildren<Field> one nominal role type per child declaration
ActorChild namespace containing role values
The exact generated Rust names preserve the actor and authored field names. The macro does not create actors, interpret effects, introduce a dynamic envelope, or grant capabilities absent from the handwritten core types.
Boundary summary
The crate intentionally stops at the pure algebra and minimal typed interpreter contracts:
Behaviorconsumes one event and returnsActions.Actionsowns sends, staged fresh creations, and next behavior or stop.- event, send, and birth products are concrete closed sums/products;
- recipients distinguish logical identity from exact installed capability;
- creation IDs remain creator-local correlation rather than actor identity;
- interpreter traits realize typed effects without dynamic dispatch; and
- reducers observe folds without becoming a scheduler or runtime.
Scheduling, mailbox transport, clocks, endpoint allocation, installation, effect settlement, and actor execution remain interpreter responsibilities.
Nominal Behavior attribute
#[behavior::behavior(...)] is the single generated authoring path. It
preserves an ordinary inherent impl and emits the adjacent nominal Protocol,
pure Behavior, optional named send product, and optional closed child product
that a careful user could write by hand.
use behavior::{Actions, BehaviorActed, Delivery, MailAddr};
struct QueryPoolProtocol;
struct QueryStarter;
#[behavior::behavior(
addr = MailAddr,
message = behavior::Never,
sends = {
query_pool: Vec<Delivery<QueryPoolProtocol>>,
},
)]
impl QueryStarter {
fn init(&mut self) -> BehaviorActed<Self> {
Ok(Actions::cont().send_query_pool(/* typed delivery */))
}
fn receive(&mut self, _: MailAddr, message: behavior::Never) -> BehaviorActed<Self> {
match message {}
}
}
addr and message are required because they establish public protocol
identity. The other declarations are capability-denying defaults:
- omitted
sendsmeansNoSends; - omitted
birthsmeansNoBirths; and - omitted
errormeansNever.
Those defaults do not infer or grant an effect. Code that tries to send, create,
or return an error absent from the declaration fails to type-check.
An advanced author may still supply an existing concrete sends or birth type
instead of a generated { ... } product; this preserves handwritten catalogue
products without adding another macro path.
Named send products
For an actor System, sends = { workers: Vec<Delivery<Workers>> } generates:
- the nominal
SystemSendsproduct with a namedworkersfield; - the uninhabited lane selector
SystemSendsWorkers; - the
SystemActionsextension trait, with a fluentsend_workersmethod; SendEffects, preserving each field independently in declaration order;SendInput<_, SystemSendsWorkers>, used throughSendEffects::send;SendsFor<Event>, requiring every field to be lawful for the complete event; andInterpretSends, requiring and visiting every field in declaration order.
Two fields with identical product and payload types still have different lane
selector types. No recursive position, type-name search, runtime registry, or
catch-all effect value is generated. An interpreter missing any constituent
delivery or request implementation fails its InterpretSends bound.
Generated fluent methods reuse the same Actions value and SendInput proof:
they append exactly once to their named lane while preserving existing sends,
creation order, and Continue, Goto, or Stop. They do not interpret an
effect or introduce a second effect representation. Existing concrete send
products remain directly constructible for advanced composition code.
Closed birth products
For an actor System, this declaration:
births = {
workers: Workers,
query_reply: ManagedQueryReply,
query_starter: ManagedQueryStarter,
},
creation_settlements = return_to_creator,
generates SystemChildren as the exact recursive ChildChoice produced by
calling Children::child_at in that declaration order. The generated behavior's
birth capability is Births<SystemChildren>. The field labels record semantic
roles. It also generates SystemChildrenRoutes, with one nominally distinct
typed route per role. The same route stages that role's creation and constructs
ChildDelivery<Child::Protocol, Role> or
ObserveEstablishedCreation<Child, Role>, so dependent effects do
not repeat an untyped nonce. Values and nonces remain explicitly authored.
These names extend the macro's existing generated namespace: SystemChild,
SystemChildren, SystemChildrenRoutes, and one SystemChildrenRole marker
for each field (for example, SystemChildrenWorkers). As with SystemSends
and its lane selectors, callers must leave those actor-prefixed names to the
macro. Using the same child behavior in two fields still produces two
incompatible role types; sharing a protocol cannot accidentally exchange their
routes.
The same declaration also generates the SystemChild selector namespace. Its
inhabited values, such as SystemChild::Workers, implement
ChildRole<System, Child = Workers>. Each role also carries a structural
ChildHead/ChildTail<_> position proving where that exact behavior occurs in
SystemChildren, and implements ChildOccurrence<System> for the sealed
running-emitter resolver. This is the Behavior-owned proof consumed by static
application topology and existing heterogeneous effect products:
Application::new(System::new())
.child(SystemChild::Workers, workers)
.child(SystemChild::Queries, queries)
The selector contains no nonce and performs no creation. SystemChildrenRoutes
remains the separate creator-local routing product used when the parent stages
creation and local dependent effects.
A route is routing intent only. It is not an actor identity, proof of freshness, or successful installation fact.
Generated and handwritten child products share the same sealed occurrence
product. A static runtime may implement ChildOccurrenceShape once to project
any SystemChildren into its own occurrence-preserving direct-child storage.
The macro emits no runtime storage or shape implementation, and equal child
types at two named roles still receive distinct structural positions.
Every real birth declaration must state who owns its completed creation
settlements. creation_settlements = return_to_creator makes the generated
event type CreationEvent<Addr, SystemChildren, Message> and requires an
authored transition with this shape:
fn creations_settled(
&mut self,
settlements: behavior::CreationsSettled<behavior::MailAddr, SystemChildren>,
) -> behavior::BehaviorActed<Self> {
// Decide from the exact accepted or rejected creation settlements.
}
Use creation_settlements = retain_for_retirement when the actor has no lawful
transition for those runtime results. Its birth capability becomes
RetirementBirths<SystemChildren>; the interpreter retains the exact
settlement as terminal source custody rather than dropping it or manufacturing
a no-op callback. Omitting births, or explicitly declaring NoBirths, needs
no settlement policy because no creation request can exist.
ResolveChildOccurrence<Role> maps such a generated role to the exact child
and position of the concrete behavior currently being interpreted. It follows
BehaviorBase through wrappers only when they preserve both the canonical
protocol and complete birth algebra. Wrappers that rewrite child topology do
not inherit the base role; their own ChildHead/ChildTail<_> positions remain
available. The mapping is sealed and type-level, not generated runtime code.
This is Bombay's staged creation policy: Children builds typed CreateChild
requests, and the interpreter must commit them before dependent same-action
sends and interpreter requests. The macro does not allocate, establish, replace,
choose nonces, or infer lifecycle provenance. DispatchBirth still requires
one concrete EstablishChild<Occurrence, Child> implementation for every
alternative. The position distinguishes duplicate occurrences without making
the role a protocol identity or forcing the parent type onto ordinary creation
creation-result consumers.
Exact initialization and transition results
An optional inherent init(&mut self) -> BehaviorActed<Self> becomes the
initialization fold. If it is absent, initialization returns the explicit empty
Actions::cont(). The required
receive(&mut self, from, message) -> BehaviorActed<Self> becomes the user
transition fold. Rust checks both method results against the generated complete
Actions<Addr, Never, Sends, Birth> and declared error type.
The macro generates no runtime context, constructor, scheduler, mailbox, executor, interpreter, or template policy. Initialization and receive bodies remain application-authored pure folds.
Why one attribute
Rust attribute macros transform an attributed item and their output is
unhygienic. The implementation therefore resolves absolute paths through the
direct bombay-behavior dependency or the bombay-rs facade, including Cargo
renames. See the Rust Reference on procedural macros.
Serde-style stacked helper attributes are formally supported for derive
macros, which can register inert helpers. An impl-level attribute macro has no
equivalent helper-registration mechanism. Keeping sends, births, and errors as
named sections of the one owning #[behavior] invocation avoids expansion
order as an API constraint and keeps one coherent generated fold.
This construction is Bombay policy, not an actor-model primitive. Actions
remains Bombay's typed realization of communications, fresh actor creation,
and the next behavior or termination.
Behavior layer laws
This document is the acceptance contract for every same-actor behavior composition in Bombay. It is deliberately a semantic review rule, not another Rust trait hierarchy.
BehaviorLayer<B> has one narrow job: construct a fully concrete output
behavior from B without forcing a generic owner to name that output type.
The trait does not make an output lawful merely because the associated type
implements Behavior. The concrete output behavior owns and must prove the
event, effect, initialization, error, birth, and lifecycle transformation
described below.
Provenance of the laws
Agha's actor calculus supplies the foundational boundary: an actor processes a communication, may send communications, create actors, and designate its next behavior. Actor configurations compose through their explicit receptionist and external-name interfaces. Agha does not specify Bombay's same-mailbox Rust wrapper order, typed event sums, typed effect products, initialization fold, controlled errors, shutdown protocol, or structural child occurrences.
Consequently:
- single-communication processing, actor-owned behavior change, messaging to known actors, and fresh actor creation are actor-model laws;
Behavior,Actions,EventLayer,SendLayer, initialization ordering, creator-local child routing, and structural occurrences are derived Bombay constructions; and- wrapper precedence, error policy, shutdown policy, buffering, retries, and lifecycle reactions are deliberate Bombay policy choices owned by concrete behaviors.
References:
- Gul Agha et al., A Foundation for Actor Computation.
- Gul Agha, Actors: A Model of Concurrent Computation in Distributed Systems.
Construction is not transition composition
For a layer value L and behavior B:
L.layer(B) -> Output
is construction only. It must not initialize an actor, process an event,
allocate an address, interpret an effect, or acquire a runtime capability.
Output: Behavior defines the operational composition.
The blanket Fn(B) -> Output implementation exists only so ordinary concrete
constructors and closures can be reused by topology owners. It is not evidence
that two actor laws have been composed. A catalogue migration is complete only
when the resulting concrete behavior satisfies every applicable law below and
the former bespoke implementation has been deleted.
Do not add associated types, marker types, *Layer configuration wrappers, or
aliases merely to make a generic call nameable. A new concrete wrapper is
admissible only when it owns a distinct state or event/effect transformation.
Enforcement hierarchy
No single checker can establish these laws. The repository uses four complementary levels, in this order:
- this semantic contract decides what the behavior must mean;
- concrete Rust sums, products, capabilities, and compile-fail tests make representable invariants static;
- compiler and Clippy hard failures reject unsafe code, suspicious constructs,
ignored
must_useresults, mutation hidden in debug assertions, placeholders, and unexplained lint suppressions; and - pure-fold, model, exhaustive, property, fuzz, and interpreter-path tests establish the observable transition laws.
Broad style, complexity, and performance lint groups are review inputs rather than repository gates. They must not compel aliases, wrappers, boxing, helper traits, or reordered branches that obscure the actor algebra. Conversely, a lint suppression is not evidence that a semantic law holds. Clippy cannot prove effect conservation, error atomicity, lifecycle authority, initialization order, or higher-order reuse; those require the types and independent tests above.
L1: stable public capability
A transparent layer preserves B::Protocol exactly. A layer may change the
public protocol only when protocol adaptation is its documented semantic law.
Changing the internal event algebra, send product, child topology, error,
phase, or state representation does not by itself create a new public
destination identity.
The address namespace is preserved. A logical recipient, established recipient, creator-local child route, and established parent relationship keep their original routing intent through composition. A layer must not replace one with another to satisfy a bound.
L2: closed and single-owned event algebra
The output event is the exhaustive sum of its owned inputs and the preserved inner event algebra.
- Every accepted event has exactly one semantic owner.
- An inner event is injected once and delegated once.
- An owned event is either handled by the layer or deliberately transformed into one exact inner event.
- Equal payload representations from different sources or child occurrences remain distinguishable by their semantic source.
- No implementation searches by payload type, wrapper depth, type name, or a runtime registry.
- Adding an unrelated outer layer cannot change which existing law owns an input.
Public domain messages and private system inputs are members of the same
closed Behavior::Event sum, but only the domain-message lane belongs to
Behavior::Protocol.
Two ingress traits that differ only because Rust cannot prove their blanket implementations disjoint do not establish two semantic input laws. Such a split must be redesigned around explicit event ownership rather than exposed as architecture.
L3: one deterministic fold
One output event produces one Result<Actions, Error>. A layer does not run an
interpreter, spawn work, call a clock, or perform another actor turn.
For a delegated event, the inner fold is invoked exactly once. For an owned event, the layer's documented transition is invoked exactly once. If an owned transition also induces an inner transition, the order and combined state commit are part of that layer's law and must be tested as one atomic fold.
Construction layers may be nested:
b.layer(first).layer(second)
Nesting is observationally associative with the equivalent composed closure: it must not change event ownership or action order merely because the caller grouped construction differently. This is observational equivalence, not Rust type equality.
L4: effect conservation
The output Actions product retains every inner effect and every layer-owned
effect in semantically named lanes.
- Inner sends are not dropped, duplicated, consumed, or reinterpreted.
- The order within each inner lane is unchanged.
- Layer-owned sends occupy an owned lane; positional
.inner.inneraccess is not a public semantic interface. - Mapping sends preserves creations and the exact next-behavior verdict.
- Mapping the verdict preserves sends and creation order.
- A wrapper may consume an inner effect only when effect interception is its explicitly documented law and the replacement outcome is complete and observable.
The normal structural interpretation order is inner effects before outer-owned effects. A different order requires a named product, a stated reason, and complete order tests; it cannot arise accidentally from field layout.
L5: initialization conservation
Construction performs no initialization. Output initialization invokes the inner initialization fold exactly once unless construction or outer validation rejects before an actor definition exists.
For successful transparent composition:
- inner initialization effects retain their exact values and order;
- outer initialization effects are added in the layer's named send and birth lanes;
- the declared structural order is inner before outer; and
- the combined initialization
becomedecision is explicit.
If initialization fails, no partial Actions exists. Validation or staged
state changes performed before delegation must not leave the output in a
partially initialized state.
Every pair of initialization-owning layers must be tested in both wrapper orders. Commutativity must never be assumed.
L6: controlled-error atomicity
An output error is a truthful exhaustive sum of inner failures and failures owned by the layer. It must retain the rejected input and any other owned value needed for recovery.
- Delegated errors preserve the inner cause without reclassification.
- Returning
Erremits no partial effects. - State needed by a retry remains unchanged unless the documented error law says ownership was consumed.
- A layer cannot mutate one submachine and then discover that another submachine rejects the same transition.
- Expected availability, capacity, overlap, stale-input, and shutdown outcomes do not become opaque actor crashes after mailbox admission. They are successful typed responses or reports when a customer can recover.
Compiler difficulty coordinating two mutable folds is evidence that their joint transition has not yet been modeled atomically; it is not permission to add a wrapper or weaken an error bound.
L7: birth conservation and freshness
A transparent layer preserves the complete inner birth algebra. A topology-owning layer appends its children as distinct structural occurrences.
- Inner creations retain order, concrete child behavior, nonce, and
CreationKind. - An inner child is never reinterpreted as a layer-owned child.
- Duplicate protocols remain distinct occurrences.
- A nonce remains correlation data, not an address or freshness proof.
- Same-action creation dependencies retain Bombay's creation-before-send interpretation policy.
- Replacement provenance is carried explicitly and cannot be inferred from address or sequence reuse.
A layer that changes topology must expose the changed birth algebra. It may not claim transparent topology merely so an old role or interpreter continues to compile.
L8: next behavior and lifecycle authority
The inner Continue, phase change, or termination verdict is preserved unless
the layer owns a documented lifecycle law that changes it.
Exactly one layer owns each lifecycle fact. Observation, shutdown, replacement, timeout, retry, and termination facts retain their source and generation. Duplicate, stale, contradictory, and late facts have exhaustive outcomes. Shutdown is defined in every intermediate state.
An outer lifecycle layer may suppress an inner verdict only when its state sum explicitly represents why and what later event completes the decision. It may not use a boolean, inferred ordering, or a later cleanup branch to repair a temporarily invalid combination.
Wrapper order is policy. For example, StopOnShutdown<Stash<B>> and
Stash<StopOnShutdown<B>> are not presumed equivalent; each applicable order
must either have a documented law and tests or be statically unavailable.
L9: higher-order reuse
A higher-order actor may retain a separate implementation only for a genuinely joint law that cannot be expressed by composing existing concrete behaviors. The review must name the correlated state and prove why separate actors or ordinary same-actor layers would alter the semantics.
It is not sufficient that two implementations call the same private helper. If a higher-order template merely selects, parameterizes, or translates an existing behavior, it must be ordinary typed composition and the duplicate fold must be deleted.
Examples of required review:
- keyed affinity should be a layer over the ordinary pool unless key binding is inseparable from the pool's assignment/termination commit;
- fixed supervision may own one shared fleet state machine, but pools must not copy its restart, installation, or shutdown transitions; and
- a queue inside a pool is reusable only when its ownership, admission, completion, and interruption laws are actually the same—not merely because both use FIFO storage.
L10: static metadata follows the real algebra
Protocol hosting, interpreter obligations, and birth requirements are structural projections of the concrete output behavior. They must not be owner-authored duplicate lists, runtime registries, or aliases that repeat an already inferable composed type.
Static proof traits may expose an associated product when a generic framework must consume the product. Such a trait is metadata, not an operational layer, and its tests must use the real owner/interpreter path rather than a synthetic lookalike proof.
Required evidence
A concrete layer is not accepted by a compile-only example. Its evidence must cover every applicable item:
- construction without naming the output behavior type;
- complete initialization
Actionsand order; - every owned event and a delegated inner event;
- complete sends, births, and next verdict;
- inner and owned controlled errors with state conservation;
- both relevant wrapper orders;
- duplicate, stale, contradictory, and shutdown lifecycle facts;
- a complete outer interpreter path for every new effect capability;
- an independent model or exhaustive trace for stateful laws; and
- restoration or simulation of the original defect proving that the regression fails for the intended reason.
Assertions observe a completed transition. They never perform the transition, move required state, or call a required function inside the assertion.
Rejection checklist
Reject a proposed layer or helper if any answer is “no”:
- Does it own a distinct state or event/effect transformation?
- Is every event owned exactly once?
- Are inner effects, births, errors, and verdicts conserved?
- Is initialization order explicit?
- Is failure atomic across every coordinated state machine?
- Does it work through every relevant existing wrapper order?
- Does it delete more bespoke caller machinery than it adds?
- Can a user construct the composition without naming its output type?
- Do tests exercise the public behavior law rather than its helper types?
A compiler error is diagnostic evidence for this review. Passing the compiler is the final representation check, not the design criterion.
Established capabilities
This document defines the production contract for runtime-issued exact actor capabilities. It does not claim that a downstream runtime has already adopted the contract.
The shared TerminalOutcome preserves the distinct execution cause
Crash::CapabilityFailed when live execution acquires a capability task failure
as its primary stop cause. It differs from a Behavior failure, affine Environment failure,
actor-task panic and owner cancellation. Bombay owns detection, retirement,
joining and full recoverable result custody; reusable actor templates preserve
the supplied cause and apply their explicit policies. An operation failure
discovered after an actor has completed remains a coexisting full-result fact
and does not rewrite that actor's terminal outcome. A returned domain rejection
is interpreted by its owning policy and is not automatically this crash cause.
Cancellation selected first remains the primary cause even if a capability
failure is also available; the full runtime result retains that failure.
Semantic classification
Actor-model laws:
- actors may communicate only through acquaintances they possess;
- actor names may be communicated; and
- creation allocates a fresh actor name.
Derived Bombay constructions:
- protocol-indexed endpoint families selected by a runtime-owned address type;
- opaque inert recipient capabilities;
- structural child occurrences and named effect products; and
- distinct protocol and concrete-behavior capability strengths.
Deliberate Bombay policies:
- a behavior stages creation with a
CreationIdthat is non-reused within one statically declared child occurrence before allocation; - all creations in one
Actionsvalue commit before dependent sends or interpreter requests from that value; - successful creation reports an exact installed concrete actor;
- observation and orderly shutdown have explicit correlation identities and complete accepted/rejected fact algebras; and
- public interpretation traits are power-user authority boundaries.
Capability ladder
logical name exact protocol endpoint
Recipient<P> EstablishedRecipient<P>
│ address lookup │ endpoint transferred directly
▼ ▼
Delivery<P> EstablishedDelivery<P>
CreateChild<A, C> --commit--> CommittedChild<C, O>
│ │ ID, kind, occurrence, actor
│ ▼
└───────────────> EstablishedCreation<C, O>
│ Installed or Rejected
▼
EstablishedActor<C>
Recipient<P> is a typed logical name. It remains necessary where a stable
name, external address, discovery result, or transport address is the intended
semantics. Resolving it to a live endpoint is runtime work.
EstablishedRecipient<P> is an inert capability for one exact protocol
endpoint. Its representation is
<P::Addr as RecipientAddress>::Established<P>. It exposes no endpoint
accessor and no direct send method. Transfer occurs through the public
InterpretEstablished<P> boundary, so this is a power-user API boundary, not
exclusive runtime authority. It does not prove which behavior is installed.
EstablishedActor<B> retains the runtime-owned Installed<B> value. That
value projects the exact protocol endpoint and carries lifecycle authority
for the same installed B. Its public constructor accepts only this one
installed value. Message-only consumers may project an
EstablishedRecipient<B::Protocol>; that projection cannot recreate the
stronger actor. Generic orderly shutdown transfers the full B value and
typed ingress without address lookup.
Why the address owns both capability families
An address used only for exact messages implements RecipientAddress.
An actor-hosting namespace implements EndpointAddress, which supplies
both projections:
impl EndpointAddress for RuntimeAddr {
type Established<P> = RuntimeEndpoint<P>
where
P: Protocol<Addr = Self>;
type Installed<B> = RuntimeInstalled<B>
where
B: Behavior<Protocol: Protocol<Addr = Self>>;
fn recipient<B>(installed: &Self::Installed<B>) -> RuntimeEndpoint<B::Protocol>
where
B: Behavior<Protocol: Protocol<Addr = Self>>,
{
installed.recipient()
}
}
This preserves static dispatch without imposing an associated endpoint or key
on every domain protocol. Each concrete P, including a generic
instantiation, produces a distinct EstablishedRecipient<P> capability type.
The runtime may reuse one message endpoint representation across protocols;
Installed<B> remains indexed by the concrete behavior because two behaviors
can share P while accepting different events. There is no hashing,
TypeId, dynamic dispatch, erased storage, or manually authored protocol key.
The associated endpoint must be Clone, because an established recipient is
transferable acquaintance evidence. It is deliberately not unconditionally
Send. Local interpreters may use closure-owned or otherwise thread-local
endpoints. InterpretSends requires the complete concrete delivery or request
to be Send when an asynchronous interpreter moves it, which naturally
requires both the endpoint and P::Msg to be sendable on that path. This keeps
executor policy out of inert capability construction and avoids propagating a
false P::Msg: Send obligation through local creation, observation, and
shutdown facts.
MailAddr intentionally implements only Address. It is a neutral logical
address used by pure examples and tests, not a commitment to a runtime endpoint
representation.
Creation transaction
CreateChild<A, C> owns one creator-issued CreationId, concrete child C,
and explicit birth or replacement intent. The runtime interprets the ordered
creation batch and each routed child as a transaction:
prepare one route for every creation without partial consumption
-> allocate fresh address
-> initialize child
-> interpret initialization actions
-> install exact endpoint and matching B::Event control
-> commit creator-local (protocol occurrence, CreationId) binding
-> issue EstablishedActor<B> and publish CommittedChild<B, O>
Route preparation rejection returns the complete untouched batch with
ChildNamespaceExhausted. After routing commits, any failed child step publishes
a rejected creation result and commits no binding. The owning
ChildCreationOutcome settlement separately retains the current child and exact
initialization value. The child rejection sum is:
Allocation(Exhausted | AddressAlreadyClaimed);InitializationFailed; orEnvironmentFailed.
The allocator's fresh address is independent of CreationId. Address has no
derivation operation. A CreationSequence retained by one child occurrence
makes reuse within that occurrence unrepresentable. Independent occurrences may
issue equal numeric IDs because the occurrence is part of every binding. Stable
identity and replacement are higher-level constructions; replacement never
means overwriting an address.
A static runtime can derive its creator-local binding product with
ChildOccurrenceProduct and a runtime-owned ChildOccurrenceShape. The sealed
product retains each direct behavior leaf together with its existing structural
occurrence; the runtime decides what endpoint and nonce storage that leaf
contains. It adds no runtime value or capability to Behavior, and it does not flatten
descendant actors into the current creator's namespace.
Effects retain their nominal occurrence rather than the running wrapper type.
ResolveChildOccurrence<O> maps that occurrence back to the exact direct child
and position for the concrete emitter. Generated roles pass transparently
through wrappers only when Behavior::Protocol and Behavior::Birth are both
unchanged. Raw structural positions always refer to the emitter's current
birth node, including a topology-changing wrapper's proxy child. No endpoint,
binding, or runtime lookup is performed by this type-level resolution.
ChildCreationOutcome<C, O>::Established directly owns
CommittedChild<C, O>: ID, CreationKind, occurrence, and exact actor.
It cannot nest a rejected EstablishedCreation. The later
EstablishedCreation<C, O> report uses the same committed product on success
and a typed CreationRejection without authority on failure. The parent
behavior is absent from the report type; C is the actual child behavior,
not a protocol-only guess reconstructed after the fact.
Same-action local delivery
ChildDelivery<P, O> carries only the child protocol, structural occurrence,
nonce, and message. The active parent instance supplies the creator namespace
as interpreter context. The interpreter resolves its committed local binding;
it does not derive an address or consult an application-wide protocol table.
If the corresponding creation was rejected or no binding exists, delivery must fail through the interpreter's typed error. It may not be silently dropped, redirected, or resolved to an older incarnation.
Exact observation
ObserveEstablishedCreation<C, O> requests the committed result of a staged
creation. It returns EstablishedCreation<C, O> to the emitting behavior.
ObserveEstablished::new(id, recipient) constructs one affine whole request
with a freshly allocated private correlation. Construct the request outside every
Behavior fold, then transfer it into the owning Actions lane; a Behavior may own
or emit that prepared request, but does not issue fresh request identity during
its fold. The observer-local numeric ObservationId selects a registration slot,
not cancellation authority. A never-accepted whole original returned by rejection
may be serially retried with its original correlation. New construction always
issues a new private correlation, even for the same id and endpoint. Acceptance
consumes the original, including when later Started publication fails; it never
reconstructs a rejected Observe request after commitment.
ObservationAuthority<P> owns one protocol-indexed permission to attempt
cancellation of an exact accepted relationship. It is affine, not a claim that
the observed actor or membership is still live. The advanced interpreter consumes
the complete Observe request when issuing the original authority, commits its
same identity in its sole membership owner, and only then publishes Started.
Public interpretation and issuance remain trusted advanced-host boundaries;
the type system does not prove that a custom host actually committed membership.
ObservationRelationship<P> is cloneable nonauthorizing identity. It owns one
strong Arc<ObservationId> and an invariant protocol brand; the numeric ID is
derived from that allocation. Its read-only identity may be compared with a
runtime-owned membership value. It cannot be publicly reconstructed, rebranded,
or converted back into cancellation permission. Each new request owns fresh identity even when its numeric slot is reused;
acceptance transfers that same original identity into the committed relationship.
CancelObservation::new(authority) consumes the exact protocol-matched grant.
The request owns that grant until successful cancellation consumes it into a
nonauthorizing receipt, or rejection returns the whole original request.
Retrying that rejected cancellation retains the same relationship, not a fresh
Observe attempt. EstablishedObservation<P> reports exactly:
Started { authority };Stopped { relationship, outcome, at };Cancelled { relationship };ObserveRejected { request, reason }; orCancelRejected { request, reason }.
Stopped carries no actionable permission. Every rejected Observe or Cancel
retains its complete original request. Duplicate live numeric slots reject an
Observe with IdAlreadyBound; a missing exact relationship rejects a Cancel
with NotObserved, including when another relationship now occupies that ID.
The interpreter serializes completion's irrevocable decision to admit a Stopped fact and exact-member removal against cancellation. Cancellation winning that cut publishes Cancelled and permits no later Stopped. Completion winning the cut publishes Stopped and returns a later Cancel as NotObserved. If conversion occurs outside the membership guard, NotObserved may arrive before Stopped; this does not turn a cancellation rejection into an observation terminal fact. Retirement takes the same notification-admission owner before draining control. An already acquired converted event that cannot be admitted remains an original returned event in the existing task hierarchy.
The exact monitor owns one target control sum, emits its original Observe once,
and consumes Started only when its request correlation matches. Its cancellation
transfer must be returned in the aggregate's typed Actions lane. A rejected
cancellation coexists with eventual Stopped and remains consumingly recoverable;
its arrival does not suppress the terminal reaction. Monitor into_parts and
the target's consuming rejection methods preserve all original current values
on the wrong extraction kind. StopOnShutdown::into_inner consumes that wrapper
without inventing mutable active-actor authority.
Legacy address-based ObservePeer<A> remains a different operation. It asks a
runtime to select an incarnation by logical address and therefore still needs
name-resolution policy. It must not be described as equivalent to exact
capability observation. Its total action settlement accepts with unit after the
relationship is established, or returns the complete request with
PeerObservationRejection::UnknownAddress when neither a live incarnation nor
authoritative retained termination can be selected. It has no prerequisite;
missing interpreter support is corruption rather than unknown-address
rejection.
Exact orderly shutdown
ShutdownEstablished<B, TargetPath> combines:
EstablishedActor<B>;- an observer-local
ShutdownId; and Ingress<ShutdownRequested, TargetPath>proving where shutdown entersB::Event.
The interpreter receives the exact installed actor and typed ingress. The request
therefore remains an explicit event/effect transformation, not an ambient
mailbox or runtime shutdown side channel. Immediate resolution is either
Accepted or a typed AlreadyStopping/AlreadyStopped rejection. Later
termination remains a separate observation fact.
ShutdownEstablished is an ActionItem under the repository's single total
settlement law. Acceptance consumes the request and leaves its ShutdownId.
Either lawful rejection returns the complete original request and exact actor
capability. InterpretEstablishedShutdown can return only
Result<(), ShutdownRejection>; it no longer selects an arbitrary output type.
The request retains its original capability until acceptance and supplies a
clone to the concrete admission port so rejection remains ownership-complete.
Runtime obligations
A conforming interpreter needs only capabilities justified by the values it drives:
- one concrete message endpoint family and, for actor-hosting namespaces,
one concrete installed
Bfamily with matching control; - fresh allocation independent of occurrence-local creation IDs;
- creator-instance protocol-occurrence/
CreationIdbindings for local child effects, which may be derived statically from the sealed direct-child occurrence product; - exact endpoint delivery, observation, and typed shutdown interpretation;
- logical-address resolution only for retained
Recipient<P>paths; and - creation-before-dependent-effects ordering.
This contract can remove application-wide per-protocol actor spaces from exact
internal paths. It does not make logical address resolution disappear where
Recipient<P> is intentionally used.
Composition invariants
Premains the only protocol identity.- Structural occurrences are navigation evidence only.
- Wrapping an emitter does not reindex an established or child destination.
- Rejected creation carries no endpoint capability.
- Exact capabilities are transferred only through
Actionsand explicit interpretation boundaries; they perform no ambient effect themselves. - Creation, observation, and shutdown correlation IDs are not actor identity.
- No wrapper or domain generic is forced to implement
Behaviorunless an operation genuinely requires its concrete event or birth algebra.
Behavior adapter contract
An adapter drives one concrete B: Behavior. It owns execution and resource
capabilities while the behavior owns state and semantic decisions.
Universal execution law
For each actor incarnation an adapter must:
- consume
Bthroughinitializeexactly once; - completely interpret initialization
Actionsbefore accepting mailbox events; - process at most one accepted
B::Eventat a time; - call
transitionexactly once for that event; - if the fold succeeds, interpret the returned
Actionsexactly once; and - commit its
Continue,Goto, orStopverdict exactly once.
A controlled fold error commits no partial actions. Cancellation or adapter failure must not manufacture a successful semantic fact.
Static boundary
The adapter is monomorphized over the complete behavior and its capabilities:
B: Behavior
B::Sends: InterpretSends<Adapter, B::Event, Here>
B::Birth::Child: DispatchBirth<B::Protocol::Addr, Adapter>
The concrete bounds vary with the host design, but every lane and child
alternative must have a compile-time implementation. A universal driver may
be generic; it may not erase events, effects, endpoints, futures, or child
types to achieve universality. EndpointAddress itself does not require every
endpoint to be Send. A thread-safe driver instead requires Send on the
concrete Actions, request, event, endpoint, or future that it actually moves
across its asynchronous boundary.
Actions commitment
For one successful Actions value, the adapter interprets:
- creations in vector order, reserving a fresh unpublished address, running each child's pure initialization fold, then privately committing its host, exact endpoint, and creator-local binding when those steps succeed;
- each committed child's initialization effects once before public publication or ordinary ingress;
- every named sends lane in its structural
InterpretSendsorder; and - the next-behavior or termination verdict.
All creation attempts precede sends and interpreter requests from the same action. This is a Bombay policy that makes create-then-send and create-then-observe deterministic. It is not a general actor-model ordering guarantee between independent actors.
Within SendLayer, inner effects are interpreted before wrapper-owned effects.
Within a vector lane, values retain vector order. The algebra deliberately
does not invent an order between independent product lanes beyond the product's
own InterpretSends implementation.
An expected rejection retains its complete request and does not suppress independent later effects. Interpreter corruption retains the settled prefix, faulting value, and untouched suffix. Neither outcome can be reinterpreted as successful readiness, restart, or observation. A child whose private host was already committed remains an established birth even when its initialization effects later fail; it drains without public publication.
Fresh creation
CreateChild<A, C> contains a CreationId that is non-reused within its exact
static child occurrence, a concrete child, and CreationKind. The adapter must
not derive an address from the creation ID. Equal numeric IDs belonging to
different occurrences remain different correlations.
For each ordered creation batch and routed request it must:
- prepare every required runtime route without partially consuming the batch;
- allocate an address fresh with respect to the actor configuration;
- initialize the concrete child exactly once;
- install a private runtime host, exact endpoint for
C::Protocol, and matchingC::Eventcontrol authority; - atomically commit the creator-local protocol-occurrence/
CreationIdbinding; - report
EstablishedCreation<C, Occurrence>::Installedafter that commitment, without claiming initialization-effect success; and - interpret the child's initialization actions once, then make a continuing successful child publicly resolvable before ordinary ingress.
Failure before host commitment publishes no capability. Failure after private
commitment preserves the established birth and its exact action settlement but
does not publish the endpoint to logical resolution. After ownership transfer,
InitializationRejected returns the current child and exact error, while
InitializationPanicked returns the extant current child after a caught pure
initialization panic, and
HostRejected returns the current child, uninterpreted initialization actions,
and matching CreationRejection. Before transfer, rejection returns the
original creation batch with ChildNamespaceExhausted. An allocation collision
is CreationRejection::Allocation(AddressAlreadyClaimed) when the selected
allocator rejects it; it can never mean replacement.
DispatchBirth recursively selects the concrete child. One
EstablishChild<Occurrence, C> implementation is required per structural
occurrence. Position is interpreter navigation evidence, not hosting
identity.
When the adapter retains exact child bindings, it may derive their complete
direct-child product through ChildOccurrenceProduct. Behavior owns and seals
the leaf, ChildChoice, and Never recursion; the adapter's
ChildOccurrenceShape chooses only its storage constructor. The product is
compile-time structure and performs
none of the creation transaction above. Each installed child derives its own
product from its own Behavior::Birth, so creator namespaces are not flattened
or shared.
When an effect carries a nominal occurrence, require
Emitter: ResolveChildOccurrence<Occurrence> and select the mapper-owned slot
with its associated Position. This sealed projection accepts generated roles,
raw ChildHead/ChildTail<_> paths, and genuinely transparent wrappers. It
rejects inherited roles when a wrapper changes protocol or birth topology; an
adapter must not repair that mismatch with a registry or runtime search.
Destination interpretation
The three delivery paths have different obligations:
| Request | Adapter action |
|---|---|
Delivery<P> | resolve Recipient<P>::address() under the runtime's logical-name policy, then deliver P::Msg |
ChildDelivery<P, O> | resolve the current creator's committed (O, nonce) binding, then deliver |
EstablishedDelivery<P> | transfer the exact endpoint and deliver directly |
The adapter must preserve P statically throughout. It may not coalesce two
protocols merely because their addresses or message layouts match.
EndpointAddress lets the runtime's own address newtype select the endpoint
family. InterpretEstablished, InterpretEstablishedDelivery, and the exact
observation/shutdown interpreter traits are public power-user boundaries. They
do not grant endpoint access outside the explicit transfer call, but they are
not sealed as exclusive runtime authority.
Event injection
Interpreter-originated facts return through their declared
ReturnsToEmitter<Input, Path>. The adapter retains the corresponding
Ingress<Input, Path> or equivalent static constructor and enqueues exactly
the root B::Event it produces.
Ancestor reports instead carry their destination ingress in the request and
declare NoReturnToEmitter. For ReportShutdownPlan<P, Path>, the adapter
calls into_event and enqueues that exact root event. It must not deliver the
plan to the emitting inner lane, mutate the coordinator directly, or treat the
request as successful installation before the coordinator consumes the event.
It must not inspect payload types to find a lane. Repeated fact types at different wrapper depths are distinct because their structural paths are distinct.
Observation
Exact observation uses ObservationId as observer-local relationship
correlation and the supplied endpoint as incarnation identity. The adapter
must return a complete EstablishedObservation<P> fact for start, cancel,
rejection, or eventual stop. Duplicate live IDs and cancellation of a missing
relationship are explicit rejections.
Address-based ObservePeer<A> remains separate. A missing live address is not
proof that the requested incarnation stopped. Without a selected live
incarnation or authoritative retained terminal fact, the adapter returns its
own error rather than fabricating PeerStopped.
Orderly shutdown
ShutdownEstablished<B, Path> supplies an exact endpoint and a typed
Ingress<ShutdownRequested, Path>. The adapter injects that event through the
normal mailbox boundary. It returns EstablishedShutdownResolved<P>::Accepted
only when the request is admitted, or the precise rejection otherwise.
The same attempt returns the generic action settlement: accepted leaves the
exact ShutdownId, while AlreadyStopping or AlreadyStopped returns the
complete ShutdownEstablished request. The concrete shutdown port returns only
Result<(), ShutdownRejection> and cannot substitute another settlement
shape.
Acceptance is not termination. Eventual termination is reported through the observation relationship.
Environment and liveness
Clock reads, timer queues, bounded-mailbox waiting, task scheduling, endpoint storage, networking, persistence, and operating-system failures are adapter concerns. They must enter or leave the fold only through declared typed events, effects, and errors.
Backpressure may delay a concrete interpretation future. The adapter must not silently drop or reorder accepted effects. Fairness, scheduling policy, and resource limits are runtime policy and should be documented by the runtime, not presented as laws of this crate.
Conformance checklist
- initialization once and before mailbox events;
- one event at a time and one fold per accepted event;
- creations before dependent sends/requests;
- fresh allocation independent of nonce;
- rejected creation commits no binding or endpoint;
- static interpretation for every effect and child occurrence;
- exact endpoints bypass logical-name resolution;
- all returning facts use their declared structural ingress;
- ancestor reports use only the explicit ingress capability carried by the request;
- no erased envelopes, registries, downcasts, or hidden side effects; and
- no success, restart, stop, or observation fact is inferred from failed mechanics.
Exact observation ownership
The exact observation interpreter consumes the whole ObserveEstablished<P>
or CancelObservation<P> request. It must preserve that original on rejection,
commit the issued exact relationship before publishing Started, and consume a
successful cancellation grant into its nonauthorizing relationship receipt.
Stopped retains exact relationship, outcome and notification timestamp without
retaining cancellation permission. Completion decision and cancellation share
one membership owner; a reused numeric ID cannot authorize an old grant. A
custom interpreter is a trusted issuance boundary, not a compiler-proven
registration service. See Established capabilities
for requests constructed outside folds, original input emission, serial
never-accepted retry, both legal control arrival orders, and retirement
admission custody.
Actor composition
bombay-behavior-actors exposes concrete actor folds and typed event/effect
transformations. Applications connect those actors with ordinary typed
composition. A constructor recipe is not a separate actor template when it
only selects policy values, forwards to another constructor, or hides a nested
type.
These compositions are derived Bombay constructions. They preserve the pure
behavior boundary: one typed input produces complete Actions and a next
behavior decision, while the interpreter alone realizes delivery, creation,
observation, scheduling, and shutdown effects.
The composition map
There are two orthogonal ways to compose Behavior Actors. They may be used together, but they do not mean the same thing.
| Question | Static construction | What it proves |
|---|---|---|
| Does another law transform this actor's mailbox fold? | Behavior::layer with an existing concrete transformation | the complete resulting Behavior, including event, sends, births, phase, error, initialization, and next decision |
| May this actor send to a transferable destination? | DeliveryRoute | one exact protocol and its logical, established, or mixed concrete send product |
| Must a transferable destination use this actor's address namespace? | DeliveryRoute<Protocol: Protocol<Addr = BehaviorAddr<Owner>>> | the same logical, established, or mixed route, constrained to BehaviorAddr<Owner> |
| Which actors can this actor create? | Behavior::Birth, BirthProtocols | the closed, occurrence-preserving fresh-child algebra |
LogicalHostRequirements separately derives the ordered product of every
intentional logical Delivery<P> in the root and its transitive births,
including interpreter requests that carry a logical recipient. It excludes
established-incarnation delivery and creator-local child effects while
retaining repeated protocol occurrences. A runtime
may recursively require its own static Hosts<P> proof for that product; the
projection creates no host and performs no lookup.
Same-mailbox layers
A layer constructs a new concrete behavior around one existing behavior. Both participate in one mailbox fold. Put the domain state machine at the center and add only transformations that own a distinct event/effect law:
StopOnShutdown root lifecycle transformation
└── ReceiveTimeout activity/timer transformation
└── Stash bounded hold/replay transformation
└── DomainBehavior application transition law
Callers compose at the value level; Rust infers the full nested type:
let behavior = domain
.layer(|inner| Stash::new(inner, |_, message| admission(message)))
.layer(|inner| ReceiveTimeout::new(inner, timer, idle, on_idle))
.layer(StopOnShutdown::new);
BehaviorLayer itself performs no actor effect. Each concrete transformation
still owns and documents its event routing, initialization order, sends
product, failure, and terminal decision. Reordering layers can therefore
change the program and must be chosen from those laws, not from type
convenience.
Actor-to-actor topology
Independent actors keep independent mailboxes. They compose through typed routes and explicit ownership, not by flattening their protocols into one envelope. Atomic actors have two intentionally different worker relationships:
FixedSupervisor ─┐
├─> StableProxy ─> worker
DynamicSupervisor┘
FifoPool ────────> direct worker
KeyedPool ────────> direct worker
The supervisors reuse the one stable-service and worker-replacement law owned
by StableProxy. Pools own assignment, completion, recovery, and worker
retirement directly; they contain no supervisor or stable proxy. A Router,
queue, workflow, or domain actor remains a peer whose own transition law is
connected through a typed transferable capability.
The short responsibility map is
atomic-actor-architecture.md. The sole
application-facing construction and worker-authoring syntax is
atomic-actor-devx.md; this composition guide does not
repeat either specification.
Choose the owner of the law
The catalogue is a vocabulary of state-transition laws, not a list of stacks that must always be used together:
| Required law | Owning actor or transformation |
|---|---|
| domain state and protocol | the application behavior or one catalogue core |
| bounded admission or ordering | Buffer, PriorityQueue, OrderGate, Sequencer, WorkQueue |
| one-recipient selection | Router with RoundRobin, LeastLoaded, ConsistentHash, or RendezvousHash |
| fan-out to a membership snapshot | Topic or PubSub |
| stable service identity across successive workers | StableProxy |
| declared-roster coordinated recovery | FixedSupervisor |
| bounded keyed service membership | DynamicSupervisor |
| global FIFO jobs or keyed-affinity jobs | FifoPool or KeyedPool |
| delayed replacement | the corresponding backoff supervision transformation |
| same-mailbox timing, observation, stashing, or shutdown | the concrete layer owning that transformation |
| ordered child shutdown | ShutdownCoordinator or HeterogeneousShutdownCoordinator |
If two laws belong to different actors, connect them with DeliveryRoute. If
one law transforms the same mailbox fold, construct it with Behavior::layer.
If neither is true, the application is defining a new topology or a genuinely
new transition law; hiding that fact in a generic wrapper would be incorrect.
Application routing and atomic actors
FixedSupervisor owns declared-roster recovery and DynamicSupervisor owns
bounded keyed service membership. Neither also owns application command
selection. Both rely on StableProxy for the stable service protocol and
worker replacement, so application clients receive only the service
capability—not a worker route or supervisor control capability.
A pool is different: it accepts customer jobs and owns direct assignment to its workers. It never exposes those worker routes. FIFO owns one global admission order; keyed pooling owns per-role queues and future-admission affinity. Their complete differences are owned by the two pool documents, not by a selector mode in this guide.
When an application command selects a worker, that selection is application state-transition policy. Keep it in the application behavior and emit a typed delivery to the chosen stable service, or place a concrete routing actor beside the supervisor and give it those capabilities. The supervisor remains responsible for recovery and stable service ownership; the router remains responsible for command admission and destination selection. This composition makes both protocols and both rejection laws visible to Rust.
Router is deliberately unicast and transfers ownership of a command to one
selected route; it does not require the command to be Clone. Use Topic or
PubSub when fan-out and its explicit cloning cost are the intended law.
Bombay application provisioning
Bombay must distinguish two creation owners:
Root::Birthcontains only children the root behavior can itself create and correlate with creator-issuedCreationIdvalues.Applicationcontains peers provisioned by the application declaration.
An application child must therefore stop appearing in the root's births = { ... } declaration merely to make it runnable. That old arrangement forces
the declaration to name the child's fully composed type before Rust can infer
it. It is the source of aliases such as ManagedTask = StopOnShutdown<Task<...>>.
The application declaration should instead store each semantic slot and child
value in its own heterogeneous product. Its private application-child owner
keeps one sequence for those declarations and lowers the resulting values
through the existing Children product. That sequence is independent of the
root's creation state; equal numeric IDs remain distinct because root and
application children occupy different static occurrences. Bombay selects
runtime routes only when it interprets that creation batch. The public spelling
contains values, not nested type declarations:
struct Tasks;
struct Events;
let application = Application::new(root)
.child(Tasks, WorkQueue::new(worker).stop_on_shutdown())
.child(
Events,
Topic::<MailAddr, Event, Recipient<EventSink>>::new()
.stop_on_shutdown(),
);
Tasks and Events are nominal application roles, not aliases for actor
types. The expression passed to each child call determines the concrete
child type. Each call returns the next inferred application-product type, so
neither the composed child types nor the final Application<...> type is
written by the caller. A zero-state generic actor such as an empty Topic
still needs enough protocol arguments to determine its law; that is real
protocol information, not a mechanical nesting alias.
Internally, Bombay needs one private application-child product with an associated
Product: ChildProduct<MailAddr>. It should return
Children<MailAddr, Product> after issuing IDs from the application-child
sequence. The running application then uses exactly these Behavior projections:
type RootNode<R> = <<R as Behavior>::Birth as BirthMode>::Child;
type AppNode<L> = <<L as StageApplicationChildren>::Product
as ChildProduct<MailAddr>>::Choice;
type RunningNode<R, L> =
<RootNode<R> as BirthNodeAppend<AppNode<L>>>::Output;
impl<R, L, Routes> Behavior for RunningApplication<R, L, Routes>
where
R: Behavior<Protocol: Protocol<Addr = MailAddr>> + BehaviorBase,
L: StageApplicationChildren<Routes>,
RootNode<R>: BirthNodeAppend<AppNode<L>>,
{
type Birth = Births<RunningNode<R, L>>;
// init:
// 1. initialize R;
// 2. issue one ID per application-child occurrence;
// 3. lower L through Children::into_creates;
// 4. call BirthNodeAppend::append_creations(root, application);
// 5. rebuild Actions with the original sends and become decision.
// transition:
// delegate to R, then call append_creations(root_creates, Vec::new()).
}
BirthNodeAppend keeps RootNode<R> as an exact structural prefix. Existing
root roles therefore retain the same child and position after application
peers are appended. It also preserves every child value, creation ID,
CreationKind, and within-lane order, with root creations before application
creations. It does not allocate, install, or validate address freshness. Bombay
still owns runtime route selection and must surface initialization-twice or
child-product rejection as a typed private running-application error rather
than panic.
This lets Bombay delete its root-shaped vacant-slot machinery:
RuntimeApplicationTopology, EmptyApplicationTopology, VacantChild,
OccupiedChild, AvailableApplicationRole, FillApplicationRoleAt, and
InjectApplicationChildAt. A smaller role-indexed application product remains
because it owns a genuine law absent from Children: semantic naming,
deferred creation-ID issuance, and construction of runtime child handles.
The runtime must derive installation storage from the running application's
combined birth algebra, not from Root::Birth. Consequently
BirthProtocols automatically includes the root, genuine root
children, application peers, and every transitive birth. Intentional logical
destinations remain concrete in the composed send products. Behavior does not
fabricate a completeness proof by asking the application to repeat those
destinations in metadata, and Bombay must not add a protocol registry to
compensate.
Unicast and broadcast in Bombay
Router is only unicast. Use RoundRobin, LeastLoaded, ConsistentHash, or
RendezvousHash; each successful route transfers one owned message to at most
one member. Bombay must remove stale Router<Broadcast> examples and imports,
not recreate a broadcast strategy or compatibility wrapper.
Fan-out is the distinct Topic/PubSub state-transition law:
Topic<A, P, Route>owns one insertion-ordered membership snapshot;TopicMessage::Publish(P)sends a clone to every member.PubSub<A, K, P, Route>additionally owns keyed topic introduction, known-empty retention, and per-topic membership;PubSubMessage::Publish { topic, value }fans out within one key.
Choose Route truthfully. EstablishedRecipient<P> broadcasts to exact
installed incarnations and adds no logical-host requirement. Recipient<P>
broadcasts to intentional logical identities, so the application topology must
install that concrete protocol. For stable replaceable workers, subscribe the
logical stable protocol, not a stale exact worker incarnation. Different
destination protocols remain different actors and are connected with typed
MessageAdapters; Topic never erases them into a common envelope.
Root shutdown
Use StopOnShutdown<B> when a shutdown request means that this actor stops
directly. Use FinalizeOnShutdown<B> when shutdown must run one typed finalizer
and preserve all of its sends, creations, and terminal decision. If shutdown
must first be delegated to a coordinator, place that coordinator at the root;
no guardian alias or builder is required.
These transformations compose like every other wrapper:
let direct = StopOnShutdown::new(application);
let finalizing = FinalizeOnShutdown::new(application, finalize);
let coordinated = ShutdownCoordinator::new(application, plan);
The choice is deliberate Bombay policy. It is not automatic discovery of a nested shutdown handler.
Plans derived from committed children
ShutdownCoordinator and HeterogeneousShutdownCoordinator own the distinct
homogeneous and heterogeneous ordered-shutdown folds. A topology-owning
application that cannot construct its plan until children commit records those
committed EstablishedChild capabilities in its own state. Once complete, it
emits ReportShutdownPlan::new(plan) in its ordinary Actions send product.
The interpreter returns the corresponding typed InstallShutdownPlan<P> event
to the coordinator.
The topology owner must preserve the following policy:
- only a successfully committed creation contributes a child target;
- rejection remains a typed application transition failure;
- every declared role contributes exactly once;
- an early shutdown request remains pending until installation; and
- a plan installs at most once.
This is ordinary actor communication between two concrete folds. A generic child-plan wrapper cannot own these laws because it does not own the application's topology or role state.
Observation
Watch<B> is the recurring logical-name observation transformation. It
continues observing later incarnations of the same logical peer.
TerminationMonitor owns a correlated, exact-once observation lifecycle and
consumes its terminal relationship. Exact-incarnation monitoring uses that
same monitor law with an established target and an ordinary typed reaction.
Those recurrence laws are different, so they remain separate folds. Target aliases and established-watch wrappers add no law and are unnecessary.
Pools and shutdown products
FifoPool and KeyedPool each implement their public transition law
directly. They may reuse private data helpers, but neither delegates its
Behavior fold to a hidden generic actor engine. FIFO assignment and
persistent key affinity have different state transitions.
Likewise, homogeneous and heterogeneous shutdown coordinators retain separate folds. Their phase products select different concrete child effect lanes, so a generic execution engine would hide the very distinction the public types are meant to prove.
Audit record
The complete catalogue classification and change ledger are in Actor-template composition audit. The broader capability and adversarial-test record remains in Behavior Actors template-law audit.
External actor-system interface
This note records the intended public boundary between a Bombay actor system and every external consumer: HTTP handlers, command-line programs, tests, embedded callers, and future authenticated transports. It is an architecture decision, not a claim that the downstream Bombay runtime already implements the complete contract.
Decision
Bombay should expose one transport-neutral, statically typed actor interface:
ActorInterface<Api>
Api is an application-defined named product of actor capabilities exported
to the environment. The interface can also establish a real external
actor/customer with a fresh typed address, an exact reply capability, and one
affine receive authority.
Conceptually:
struct Api {
orders: EstablishedRecipient<Orders>,
discovery: EstablishedRecipient<ServiceDiscovery>,
}
let interface: ActorInterface<Api> = application.interface();
let mut caller = interface.external::<OrderReplies>()?;
caller
.send(
&interface.api().orders,
OrderCommand::Get {
reply_to: caller.recipient(),
},
)
.await?;
let reply = caller.receive().await?;
The names above are illustrative. The semantic contract, rather than a particular spelling, is the decision.
The interface is not the actor runtime. Scheduling, mailbox interpretation, allocation, transport, topology ownership, and lifecycle control remain private runtime responsibilities. In particular, an untrusted external consumer does not receive the complete application topology or root shutdown authority merely because it can communicate with exported actors.
Source classification
Actor-model law
An actor configuration exposes receptionists: internal actor names known to the environment. It may also know external actors: actor names outside the configuration. Communication can expand these sets by transferring actor names. This is the actor-model basis for an explicit interface formed from exported actor capabilities and real external customer endpoints.
Primary sources:
- Gul Agha et al., A Foundation for Actor Computation.
- Gul Agha, Actors: A Model of Concurrent Computation in Distributed Systems.
The actor model does not require a global application router, a service
registry, a public/private modifier on behavior types, or a generic ask
operation.
Derived Bombay construction
Apiis a closed, statically typed product that names the initial receptionist set.- An external actor owns a fresh address, a cloneable exact send capability, and an affine receiver.
- A request carries its reply capability explicitly.
User::fromrecords the truthful origin of the communication; it is not implicitly the reply protocol. - Logical and exact destinations remain distinct. A stable logical name may be appropriate for discovery, proxies, or transport. An exact recipient identifies one installed incarnation and must never retarget.
- Discovery is an ordinary typed actor protocol that can return additional actor capabilities. It is not privileged runtime lookup.
Deliberate Bombay policy
- Application composition explicitly chooses the initial exports. Root is not automatically public, and declared children are not automatically public.
- HTTP, CLI, tests, embedded integrations, and future transport gateways use the same interface contract.
- A future Zenoh/KERI boundary authenticates an external identity and decides
which typed interface or capabilities it may receive. Authentication,
serialization, and transport do not enter the
Behavioralgebra. - Lifecycle ownership and the external communication interface are separate capabilities.
Visibility law
Bombay does not need a PublicActor behavior marker or inferred visibility.
The same behavior may be public in one application and private in another.
An internal actor is externally reachable exactly when:
- its capability occurs in the initial
Apireceptionist product; or - an already reachable actor later sends its capability to an external actor.
An actor that has not crossed that boundary remains private because the external party cannot name it. Discovery changes reachability only by returning a typed capability through ordinary actor communication.
Discovery
Registry and Resolver already supply the basic reusable discovery
construction. No new Behavior Actors template and no new Behavior Core
algebra are required.
Passing only a discovery receptionist to a particular boundary is a valid application policy when discovery is intentionally the sole bootstrap capability. It is not a universal actor-system rule. Another application may export several static receptionists, only its root protocol, or no discovery actor at all.
A heterogeneous global registry would require erasure or a closed
application-specific protocol product. Bombay must not add an untyped global
envelope, runtime protocol registry, Any, or string-based lookup to simulate
heterogeneous discovery.
Request and reply
Actor-native request/reply is customer passing: the request contains a typed
reply capability. A truthful external caller therefore needs a real endpoint
that an internal actor can retain and use. Fabricating the target address as
the sender, using an unclaimed raw address, or treating User::from as an
implicit reply destination violates this contract.
The fundamental interface operation is external actor creation plus typed
send and receive. A generic ask convenience may be derived later, but only
after Bombay defines:
- timeout ownership;
- cancellation and overlap;
- whether the reply actor remains live after caller cancellation;
- late-reply handling;
- correlation and stale-reply behavior; and
- the typed result when an exact late delivery is rejected.
This is not cosmetic. In the current exact-delivery runtime contract, sending to a closed exact endpoint is an interpretation failure. Closing a temporary reply port on timeout could therefore make a correct but late reply fail the replying actor's action. Bombay must choose and test that policy explicitly rather than silently adopting dead letters or dropping the message.
Behavior Actors support
The catalogue audit classified every stored or received recipient by semantic
role. All arbitrary customer/reply destinations are now parameterized by the
sealed DeliveryRoute construction, which projects Protocol and Sends, instead of being hard-coded as
Recipient<Reply>:
| Route capability | Produced effect |
|---|---|
Recipient<P> | Delivery<P> |
EstablishedRecipient<P> | EstablishedDelivery<P> |
ReplyRoute<P> | ordered ReplyDelivery<P> values retaining each logical/exact alternative |
DeliveryRoute projects the protocol and concrete sends product from the route
itself. Stateful templates such as dynamic supervision therefore cannot pair a
retained route with an unrelated nominal reply protocol.
Creator-local child communication is not a transferable DeliveryRoute.
Behavior retains a creator-issued CreationId; Bombay privately maps that ID
and the statically selected child occurrence to its runtime route.
Registry membership, configured downstream destinations, worker completion targets, stable proxy identity, and transport names remain logical where that is their actual domain. The route migration changes only customer-passing seams. Compile-contract tests instantiate every such template with exact and mixed routes, while interpreter tests prove that a mixed lane neither converts capabilities nor reorders deliveries.
The shutdown coordinators also support creation-dependent topology. They may
start in AwaitingPlan. The child composition constructs the plan only after
every configured creation commits and emits ReportShutdownPlan through its
returned Actions. The report's final destination is inferred from the
complete static actor composition; application code supplies neither a path
nor a route. Interpretation enqueues exactly one InstallShutdownPlan event
for the owning coordinator. An earlier shutdown request remains retained until
that event is folded. Homogeneous and heterogeneous plans are different event
types and cannot be substituted. This is explicit Bombay lifecycle policy,
not a new actor-model primitive.
Both additions live wholly in Behavior Actors and use existing Core events, effect products, exact recipients, child routes, and composition paths. No new Behavior Core algebra is required.
Rejected designs
The following alternatives fail the intended laws:
| Design | Reason rejected |
|---|---|
Application::tell/ask as a central router | Makes the application object an ambient runtime service and obscures the sending actor. |
| Discovery as the mandatory only public actor | Confuses one useful bootstrap policy with the actor-system interface law. |
| Publicness inferred from actor type or topology | Visibility is capability reachability chosen by composition. |
PublicActor marker trait | Couples reusable behavior semantics to one application's export policy. |
| Expose the complete topology handle | Leaks private children and installation structure. |
| Give all clients shutdown authority | Conflates communication capability with lifecycle ownership. |
| Fabricate target-as-origin | Claims an external actor identity and endpoint that do not exist. |
Use User::from as reply capability | Provenance and the reply protocol are distinct facts. |
| Dynamic heterogeneous registry | Requires erasure, runtime protocol lookup, or a closed application-specific sum. |
Treat generic ask as harmless sugar | Hides timeout, cancellation, and late exact-delivery semantics. |
Required runtime work
The downstream Bombay runtime should implement this contract holistically:
- establish a fresh external actor endpoint with truthful typed origin;
- expose a named application-specific receptionist product;
- retain cloneable send capability and affine receive authority;
- keep exact recipients incarnation-bound and return the original message on admission rejection;
- separate the client interface from topology and lifecycle ownership;
- adapt HTTP, CLI, tests, and later transport gateways to this same boundary;
- preserve accepted-prefix draining and deterministic endpoint closure; and
- keep raw runtime address and mailbox representations private.
The complete composition must prove that a protocol mismatch cannot compile, receive authority cannot be cloned, private topology cannot be acquired from the client interface, a stale exact endpoint cannot retarget, and capability transfer can deliberately expand the reachable receptionist set.
Audit hypotheses
The architecture audit produced the following verdicts:
- A central application router is not the actor interface.
- Discovery is not universally the sole boundary.
- Publicness cannot be inferred from names or topology.
- Publicness is not a property of a reusable behavior type.
- The initial interface is an explicit typed receptionist product.
- Capability transfer may expand the reachable interface.
- An external caller must own a real endpoint and truthful origin.
- A fabricated or unclaimed origin is invalid.
User::fromis provenance, not an implicit reply protocol.- Requests carry reply capabilities explicitly.
- Receive authority is affine.
- Send capability is transferable and cloneable where its endpoint permits.
- An exact capability never retargets to a later incarnation.
- Exact admission rejection preserves the rejected message.
- A logical reply route is not universally sufficient.
DeliveryRoutesupplies the existing static logical/exact abstraction.- The public interface must not expose the complete topology.
- The public interface must not imply lifecycle authority.
- Generic
askrequires a separate explicit lifecycle policy. - The interface is transport-neutral and exposes no raw mailbox address.
These conclusions require no new actor template and no new Behavior Core algebra. The Behavior Actors route-capability and late shutdown-plan work is complete; the explicit unified interface itself remains downstream runtime work.
Atomic runtime interpretation and settlement
Status: H02 static representation proven; H07 has lowered total item,
heterogeneous creation, and complete Actions settlement into Behavior; H10
fixes each named settlement product independently of the runtime; H03 records
the required Bombay terminal-custody contract. Bombay will change to implement
this architecture; its present interfaces do not constrain the atomic design.
This document is the sole normative owner of
generic action settlement, creation
dependency ordering, initialization settlement, terminal custody, residual
retirement, and the Bombay Engine integration requirement.
Ownership
Actions is Bombay's typed realization of actor transition effects:
communications, staged fresh actor creation, and the next behavior or
termination decision. It is not literally Agha's effect triple: typed products,
termination, initialization, creator-local routing, and interpretation order
include derived constructions and Bombay policy.
Behavior owns only pure static products. Bombay interprets concrete capabilities and owns runtime custody. Neither aggregate transitions nor generic Behavior machinery may contain tasks, clocks, channels, address spaces, observation publishers, callbacks, or runtime handles.
Total item law
For each declared item, interpretation produces exactly one outcome:
Accepted { receipt-or-promised-success }
Rejected { complete-original-item, exact-reason }
Blocked { complete-untouched-item, exact-prerequisite }
Blocked means a declared dependency did not commit; it is not a generic
failure or a reason to suppress independent siblings. A corrupt interpreter
result retains the recorded accepted/rejected prefix, the exact faulting item,
and the complete unattempted heterogeneous remainder.
The product law is total and static:
- lanes and items are interpreted in their declared stable order;
- rejection does not short-circuit independent later items;
- every accepted item is consumed and leaves only its specified receipt;
- every rejected item returns its complete original value;
- dependency-blocked items remain untouched with the exact prerequisite;
- no positional traversal, runtime graph, erased envelope, callback, or per-template settlement adapter is permitted; and
- a stopping actor is not required to receive its own settlement.
Any generic Behavior representation must prove this law through two unrelated catalogue templates and two wrapper orders without placeholders before catalogue migration begins.
Each action item fixes its accepted receipt, rejection, and prerequisite
types. SendSettlements projects a concrete sends product to one complete
settlement product without naming a runtime, root event, or structural path.
InterpretSends may decide only the attempted outcomes within that fixed
product. A stopping aggregate can therefore retain one typed settlement value
without coupling its state to Bombay or adding a template-specific adapter.
The H02 non-production model satisfies that representation gate with a recursive
heterogeneous product and the existing SendLayer inner-to-outer order. It
preserves complete values after rejection/blocking, attempts independent later
items, retains exact prefix/fault/remainder at every product position, and
rejects a deliberate short-circuit trace. This proves stable-Rust realizability,
not production implementation or Bombay custody.
Creation dependencies
Actor-model creation requires fresh allocation. A creating Behavior issues one
checked CreationId, non-reused within the exact statically declared child
occurrence, and emits the complete child in the existing creation leg. The
occurrence and ID together are creator-visible correlation, not an address,
runtime route, actor identity, or establishment capability. Independent pure
layers do not share an ID issuer; equal numeric IDs in distinct occurrences are
lawful. The interpreter commits a successful fresh creation before same-action
communications or observations that name that occurrence and ID. This ordering
is Bombay policy.
Creations owns one ordered creation batch. Bombay accepts that whole batch for
routing or returns it unchanged with ChildNamespaceExhausted; it may not
consume only a prefix. On acceptance Bombay privately pairs every request with
one route from the current creator namespace, then the generic creation
interpreter attempts each routed request independently in declared order. An
expected child-establishment rejection does not suppress later creations or
independent sends. Interpreter corruption retains the completed prefix and the
exact routed remainder.
Behavior exposes no ChildRoute and never constructs an Address::Nonce.
Bombay owns the private mapping from exact typed child occurrence plus
CreationId to its runtime route. Child communications, lifecycle requests,
reports, and creation prerequisites travel through that typed occurrence and
carry its ID; runtime code resolves the pair to a route. An ID from one
occurrence cannot satisfy another occurrence even when their numeric values are
equal. A rejected routed creation remains in runtime settlement custody with its
child, ID, kind, and selected route. Retirement transfers unresolved batches
and routed creations through the same parent-to-root custody path as every other
affine runtime value.
After a complete interpretation, one typed CreationsSettled input returns the
entire creation leg to a live creator. The batch remains the ownership and
ordering unit; templates do not rebuild a per-child settlement join. NoBirths
requires no custody port. If creator admission is closed, the unchanged batch
continues through parent-to-root custody.
There is no separate route request, result, aggregate waiting phase, or
ReturnToEmitter continuation. Route preparation is the first generic step of
interpreting the one creation leg through the unchanged Driver. It creates no
mailbox, task, registry, callback, or lifecycle service.
An establishment attempt that accepts ownership of a child definition returns
an authoritative ChildCreationOutcome<C, Occurrence>. Established owns the exact
capability. InitializationRejected returns the current child and exact initialization
error. InitializationPanicked returns the extant current child after a caught
pure fold panic, without claiming a typed initialization error or actions.
HostRejected returns the current child, uninterpreted initialization
actions, and exact reason. These are semantic creation rejections, not
interpreter corruption, and none reconstructs the pre-initialization child.
Rejection or corruption before ownership transfer instead returns the complete
original CreateChild through ItemSettlement.
A behavior declares that request through exactly one of two domain operations:
CreateChild::birth or CreateChild::replacement. CreationKind remains
authoritative carried provenance for interpreters and typed results; it is not
a third, context-free application constructor. Closed child-product composition
may reconstruct the same request internally while preserving its owned kind.
Semantic creation rejection blocks only operations whose typed prerequisite is the
uncommitted child binding. After creation commits, rejection of one observation
blocks only operations that depend on that observation; lawful child delivery,
input, shutdown, termination observation, and unrelated observations remain
independently eligible. The dependency relation is expressed by the concrete
child-operation item type and its InterpretItem::Prerequisite:
ObserveCreation, ObserveEstablishedCreation, ObserveChild,
ChildDelivery, ChildInput, and ShutdownChild are the only current
same-action child families. The runtime transaction retains their typed
resolution by exact child protocol, occurrence, and CreationId.
ObserveCreation<P, Occurrence> returns CreationResolved<P::Addr>; it does not
erase P to its address. No general dependency graph, lane index, or wrapper
position is consulted.
A route collision can only falsify Bombay's accepted batch-preparation receipt; it is interpreter corruption, never replacement. Namespace exhaustion rejects the untouched batch before route transfer. Replacement provenance is explicit typed semantic data and never inferred from address reuse, route reuse, or sequence arithmetic.
Initialization and activation order
Initialization is part of the Behavior contract. Its actions are interpreted before ordinary mailbox ingress and compose with wrapper initialization in one defined order:
fresh address reservation, no live endpoint
-> pure definition initialization fold
-> endpoint installation and creator-local binding commit
-> total initialization-action interpretation
-> initialization settlement and residual custody
-> activation authorization and attempt, where required
-> exact readiness or failure fact, where required
-> ordinary ingress only after a continuing, successfully settled initialization
Initialization may stop with final actions. Accepted initialization effects are settled before ordinary ingress, even when the resulting actor is stopping. Activation capacity is actor-side admission before an action is emitted; it is not relabelled as an interpreter rejection afterward.
Reservation rejection retains the complete staged creation without running
the fold. Pure initialization rejection retains the current child and exact
error. Host rejection after the fold retains the current child and
uninterpreted initialization Actions. After installation commits, an effect
rejection or interpreter fault belongs to the installed child's drain and
cannot be recast as a creation rejection. A stopped initialization settles
its final actions without permitting ordinary ingress. Fresh allocation is
the actor-model requirement; this packaging and ordering are Bombay policy.
Exact Bombay changes for worker initialization and activation
Bombay must keep the installed child closed to ordinary traffic and retain its
complete initialization-action settlement in the existing child lifecycle
host. The settlement never moves into StableProxy state or a supervisor report.
After ChildCreationOutcome::Established, an atomic owner retains its non-cloneable worker
state and emits InitializeWorker<W, P>. That request contains only a
cloneable exact worker target, opaque worker and initialization correlations,
and the moved activation plan. It does not move the aggregate's worker, name the
worker's action product, or search by runtime type.
The request is an ordinary ActionItem with accepted unit and uninhabited
capability rejection or prerequisite. Bombay interprets it by locating the
already existing child host through the supplied established target. Missing
support is interpreter corruption, not an invented runtime rejection. The host
calls InitializeWorker::resolve with exactly one
WorkerInitializationOutcome::ReadyForActivation | EffectsRejected | Stopped result, which
reunites that result with the affine plan as WorkerInitializationReport. The request
and result run inside the unchanged Driver and retirement barrier.
Bombay's local environment must interpret that request through its exact
installed-child capability. Acceptance transfers the request to the already
existing child lifecycle host; it is therefore a non-rejecting local custody
operation. Missing typed support is interpreter corruption and retains the
complete request. It is not an expected mailbox, observer, or capacity
rejection. When initialization settles, the host returns exactly one Tier-1
WorkerInitializationReport input:
ReadyForActivation { worker evidence, original plan, one activation permit }
EffectsRejected {
worker evidence,
original plan,
failure: EffectsRejected | InterpreterCorrupt
}
Stopped { worker evidence, original plan, exact stop }
The host derives failure by inspecting the complete settlement's
SettlementStatus without consuming that settlement. Accepted selects the
Initialized alternative and cannot inhabit WorkerInitializationFailure.
Expected capability rejection or dependency blocking selects
EffectsRejected; interpreter corruption or an unattempted suffix selects
InterpreterCorrupt. The exact settlement remains attached to the concrete
worker environment. StableProxy drains the worker and reports only this
semantic classification, its activation plan, and the exact worker result.
A foreign worker or initialization correlation returns the complete request
and report unchanged. Failed creator admission follows RecoverEvent and the
existing parent-to-root custody path. No template-specific Bombay conversion,
second mailbox, or detached task is allowed.
If StableProxy admission closes, Bombay retains the complete
WorkerInitializationReport input, including the activation plan, beside the worker
environment's exact action settlement. Parent retirement transfer moves both
values outward as one statically typed residual. This is not a lookup token and
does not require StableProxy to reopen or consume its own settlement.
On Initialized, the aggregate consumes the permit and plan with a fresh
activation attempt and exact worker target into BeginActivation<W, P>.
Activation capacity is checked by the aggregate before this request exists.
It is never reported as an interpreter rejection.
The selected Communication control capability has one rejection:
ControlClosed, which returns the complete submitted value. Consequently the
activation-start action has exactly one expected rejection, OwnerStopped,
and that rejection retains the complete BeginActivation. Mailbox fullness,
observer capacity, generic host failure, and activation capacity are not
members of this sum.
ActivationPlan::activate(self) is a statically dispatched future. After
accepting the request, Bombay must enqueue the matching Started input to the
owner before polling the plan. It then retains the typed
JoinHandle<WorkerActivation<W, P>> in the existing local environment, polls
it with the existing input schedule, and returns the matching ready or rejected
input through the unchanged Driver. Only an accepted request's consumed plan
may produce readiness.
The H22 production contract realizes that shape without a task in Behavior.
ActivationPermit retains the exact established worker target already supplied
to the initialization host. BeginActivation::new therefore accepts only the
returned plan and permit; no caller can substitute another same-protocol
worker. A private activation correlation is reserved one-to-one from the
permit's non-reused worker attempt. BeginActivation::started produces the
WorkerActivation value holding the matching started alternative. Consuming
BeginActivation::activate moves the plan exactly once to a
WorkerActivation holding either readiness or rejection. The public runtime
values expose consuming projections, not constructors for their private
alternatives.
Bombay must own activation work in one statically typed task product within the
actor's existing environment. Completed results return through the actor's
ordinary typed system-input admission. Unfinished work and completed results
whose owner admission has closed transfer through the existing retirement
barrier to the root custodian. The task product retains its concrete output; it
must not reduce it to JoinHandle<()>, abort and discard it, or detach it. This
is Bombay runtime work, not a Behavior-side runtime. It introduces no second
Driver, mailbox, erased output, or lifecycle service. Cancellation never claims
physical cancellation unless the concrete plan provides it.
H14 supplies the generic ClassifySettlement projection over the complete H10
static settlement product. Bombay inspects SettlementStatus::Accepted,
Rejected, or Corrupt without consuming or reconstructing that product and
without template-specific traversal. Rejected includes both a capability
rejection and a declared dependency block. Rejected or blocked effects take
precedence over a simultaneous initialization stop; the unchanged next decision
remains present in the complete settlement and the installed worker then drains
normally. Corrupt includes an interpreter fault or any unattempted suffix and
therefore enters the existing residual-custody path with the entire settlement.
ActionSettlements projects a concrete Actions type to this complete static
settlement without naming an interpreter. BehaviorSettlements applies that
projection to a concrete behavior without forcing lifecycle owners to restate
the behavior's internal creation and send bounds. Lifecycle-host requests use
the associated type when they must return a worker's initialization settlement;
they do not restate the creation vector or send-product structure in each actor
template. Both projections perform no interpretation and change no custody.
Named products interpret their fields in declared order. A corrupt earlier
field retains the untouched later fields as unattempted; a lawful rejection
does not stop independent later fields. The SendProduct derive and the
#[behavior] generated products share the ordered traversal and source-custody
generator while preserving their domain field names. The unused two-product
tuple helper and the private Actors declarative macro were removed. Named
products return complete named settlement shapes.
StableProxy's ProxyEffects declares worker observation, initialization,
activation, shutdown, service delivery, owner outcome, and diagnostic lanes in
that order. Runtime-local and structural-owner operations use the one
InterpreterRequests product; service forwarding uses exact established
delivery. A concrete compile witness proves the actual StableProxy::Sends
implements InterpretSends, so no vector-only lane or template adapter remains.
Creation is still interpreted first by Actions; therefore the observation's
exact CreationCorrelation<WorkerProtocol, ChildHead> refers to that same
action. Creation settlement and the later ChildStopped event are independent
admissions. The proxy retains either arrival order explicitly and returns both
values if creation rejection contradicts an already observed stop. Bombay must
not serialize those admissions merely to simplify the aggregate.
Terminal custody
Every affine terminal settlement follows one ownership path:
child lifecycle host
-> admitting parent
-> next structural owner when parent admission is closed
-> non-rejecting root custodian
-> existing Engine retirement barrier
Closure returns the exact value; it never logs or discards it. Outward transfer does not reopen a stopped actor. The path uses the existing Driver and actor graph—no detached task, second mailbox, second lifecycle framework, or Behavior-side runtime is permitted.
Forced retirement transfers unresolved creations, initialization and activation settlements, assignment joins, customer outcomes, diagnostics, timers, shutdowns, and queued ingress as explicit residual ownership. Actor-graph retirement and external work are distinct; the root barrier cannot claim quiescence while externally owned terminal work remains unaccounted for.
An aggregate may retain its unresolved actor-owned values in a private terminal alternative of the final concrete behavior. Runtime work already accepted from that aggregate remains in the environment residual. Bombay transfers both products without inspecting, flattening, or reconstructing either one. This separation prevents a pool-specific residual lane while preserving the exact owner of every affine value.
FIFO and KeyedPool name the identical local stop cause once as
ForcedRetirementCause: exhausted worker-shutdown identifiers, a deadline not
scheduled with its complete interpreter result, or the exact elapsed deadline. The cause does not own a
worker roster and does not perform transfer. Each aggregate retains its own
unresolved workers; Bombay later moves the complete concrete behavior and
environment residual together.
Exact concrete rejection vocabulary
Concrete reasons come from the selected locked dependency contracts:
- asynchronous Communication send waits for bounded capacity and rejects only
with the closed complete payload;
Fullbelongs totry_send; - control send is unbounded and rejects only
ControlClosed(complete_item); - Address claim distinguishes
AddressInUseand permanent registration-ID exhaustion; - Observe distinguishes
SubjectExists,UnknownSubject, and exact generation-checked retirement; - Timers use exact branded schedule tokens and exact-current cancellation; and
- orderly shutdown distinguishes accepted, already stopping, and already stopped, with child absence separately reported as not established.
H23 places exact orderly shutdown on the same generic item settlement as every
other action. Accepted exact shutdown leaves ShutdownId; either selected
rejection returns the complete ShutdownEstablished request and its exact
actor capability. The concrete endpoint port returns only
Result<(), ShutdownRejection>. StableProxy can therefore use exact shutdown
for post-commit worker drain without a proxy-specific adapter or weaker child
route.
Generic settlement must not fabricate mailbox-full, observer-capacity, activation-capacity, or typed exhaustion variants unsupported by those APIs.
ObserveChild<P, Occurrence> names the concrete child protocol because its
same-action dependency is the one existing
CreationCorrelation<P, Occurrence>. The selected child-observation path
clones the hosted exact termination observation after the typed child binding
commits. It therefore has accepted unit, no capability rejection, and only the
creation prerequisite; SubjectExists applies to subject registration and is
not fabricated as a child-observation rejection. Equal address types do not
make two child protocols substitutable.
H05 makes this boundary executable without adding runtime dependencies to Behavior. A research-local witness checks 13 exact facts against the selected Address 0.2.0, Communication 0.1.2, Timers 0.1.0, frozen Bombay Observe, and locked Behavior shutdown sources. Under Bombay's pinned Rust 1.96, the exact Address suite passes 14/14, the Timers suite passes 2/2, and Communication's library target builds (it has no library unit tests). These releases cannot be linked into the Rust-1.95 atomic experiment; that MSRV boundary is intentional evidence that concrete capability interpretation remains a Bombay concern.
Required Bombay target
The atomic architecture targets the following Bombay contract. Bombay's present method signatures are historical evidence only; they are not reasons to weaken, defer, or reshape the accepted actor laws.
Bombay must:
- return or re-home the complete rejected parent event;
- move the final concrete Behavior value into retirement ownership instead of
dropping it after
Step::Stop,Exhausted, or a Driver error; - return a typed residual from environment retirement;
- carry the Behavior and environment residual through the existing Driver retirement barrier; and
- deposit that one typed transfer at a non-rejecting root custodian before the barrier releases.
Typed application assembly
Bombay must also reverse its current root-first application construction order for actor templates whose required destinations are application-owned actors. The target order is:
declare application actors by semantic role
-> install them through Bombay's existing runtime ownership
-> obtain their typed logical or exact capabilities
-> construct the pure root from those capabilities
-> initialize and activate the root
-> return the typed root and application lifecycle capabilities
The one-time root constructor belongs to Bombay application assembly. It is not
a Behavior, is never retained by an atomic actor, performs no I/O, and cannot
be invoked from a transition. Bombay remains the sole owner of address
allocation, mailboxes, installation, activation, and startup rollback. If peer
installation or root activation fails, Bombay retires every already installed
application actor through the same retirement path and returns the exact startup
failure.
Application code names semantic roles, concrete actor behaviors, and typed
capabilities only. It does not name MailAddr, child-product positions,
occurrence paths, generated type aliases, or final composed behavior types.
Role selection must be inferred and statically checked; the public source shape
must not expose the existing TopologyAt cursor or ApplicationRoute product.
No registry, dynamic capability map, erased protocol, second mailbox, second
Driver, or atomic-template-specific host is permitted.
H139 shows why returning only ApplicationHandle<Root::Protocol> after root
activation is too late. A full Start message happens to infer every dynamic
type from its payload. The lawful Stop { key } message does not name worker or
activation types, so current stable Rust reports E0283 at Application::new.
Adding a turbofish, alias, explicit root type, fabricated Recipient, or dummy
worker field is rejected. The lifecycle actor's concrete
DynamicLifecycle<Key, Worker, Plan> protocol must supply those types during
role-first assembly instead.
This application-assembly change and the retirement-custody change are separate Bombay responsibilities. Either may be implemented first, but both must use the existing Driver and both must pass the final atomic integration probe.
H148 historically derived the target source equation independently of Bombay's
current root-first API: typed lifecycle and diagnostic capabilities enter one
pure root constructor, the six-policy dynamic(entries, activation, unexpected_exit, actor_drain, lifecycle, diagnostics) call infers its key,
worker, and activation types. The real production construction test and five
command paths now own that contract; H589 removes the superseded facsimile while
retaining H148's compiler evidence in Git. Removing lifecycle provenance or
delivering Stop without the typed supervisor capability produced E0277/E0308
or E0282. Bombay must change its assembly order to supply this context; Behavior
Actors must not add an annotation, raw address, callback, or alternate
constructor to compensate for the present runtime API.
This is the required Bombay architecture. A stopped aggregate can retain complete affine terminal values in its private final state, so returning only environment state would still lose ownership. Bombay changes to carry both the final concrete Behavior and the environment residual. Atomic implementation may target this contract before the upstream patch lands; it must not add a local workaround. Final end-to-end verification records the upstream revision that supplies these five links. H03 authorizes neither a Behavior-side custodian nor a second Driver, mailbox, task, or lifecycle service.
Bombay's application facade re-exports only the non-hidden semantic names listed
by atomic-actor-devx.md. Interpreter code may import
the doc-hidden associated request, event, effect, and settlement types through
behavior_actors::atomic; none becomes a second application spelling.
Required Bombay implementation
Bombay must realize the target through its sole Driver path. No Bombay source is modified by this campaign, but upstream will make the following coherent replacement. Applying only a subset leaves affine values unowned.
crates/bombay/src/interpret.rs
- Delete the
InterpretationError<C, S>two-leg short-circuit sum. Expected capability rejection is data inItemSettlement; it is no longer a commit error. - Implement the generic creation leg in two stages. First, one
InterpretItem<Creations<CreateChild<BehaviorAddr<C>, C>>, ...>call either returns the complete batch untouched withChildNamespaceExhaustedor returns one orderedCreations<RoutedCreation<BehaviorAddr<C>, C>>value. ThenEstablishChild<Occurrence, C>settles each routed child independently. Its output is fixed by the routed creation, not selected by Bombay.Establishedreturns the exact established capability.InitializationRejectedreturns the current routed child and exact initialization error.InitializationPanickedreturns the extant current routed child after a caught pure fold panic.HostRejectedreturns the current routed child, uninterpreted initializationActions, and exact reason. Corruption retains the exact routed suffix throughInterpreterFault. - Replace
CommitActions::commit's creationforloop and subsequentInterpretSends::interpret(...).map_err(...)with exactly oneactions.interpret::<_, B::Event, behavior::Here>(&mut self.capabilities)call. That call already owns creation-before-sends order, lawful continuation, heterogeneous residuals, and the exactbecomeverdict. - Return the resulting
ActionSettlementto the existing host settlement admission. Do not discard it after checkingComplete, and do not convertInterpretation::Corruptinto an error lacking the settlement value. - Keep
ActionInterpreteras the one concrete capability product used by the existing EngineDriver; add no loop, mailbox, task, registry, or adapter.
crates/bombay/src/application_runtime.rs
-
Delete the blanket
SendInterpreterimplementation and migrate everyInterpretRequest,InterpretDelivery,InterpretEstablishedDelivery,InterpretChildDelivery, andInterpretChildInputimplementation to the single staticInterpretItem<Item, RootEvent, Path>ownership port. Runtime implementations do not select a settlement associated type;ActionItemandSendSettlementsalready fix the exact result vocabulary. -
Each implementation must return its complete capability-specific sum:
- async logical and exact delivery acceptance consumes the message and
returns only its receipt;
UnknownorClosedreturns the original delivery and exact reason; - child delivery/input consults the exact occurrence binding. A rejected
creation resolution produces
Blocked { item, prerequisite }; genuine absence producesRejected { item, MissingChild }; closed admission returns the original item; - timer scheduling uses only the selected Timers reason/token contract;
- Observe uses only
SubjectExists,UnknownSubject, and exact generation facts; - control ingress returns
ControlClosed(complete event)instead of ignoringControlSender::send; - child shutdown returns its exact accepted/already-stopping/not-established settlement and never reports success merely because a local fact was enqueued; and
ReportToParent<Report>returns the complete report when parent admission is closed.
- async logical and exact delivery acceptance consumes the message and
returns only its receipt;
-
Replace the legacy nonce-oriented
CreationResultswith a typed current-action prerequisite store keyed by the exact declared child occurrence plusCreationId. Beginning an action clears only the prior action transaction. Equal numeric runtime routes and distinct occurrences cannot satisfy each other's prerequisite. -
Child hosting consumes
RoutedCreationand returnsChildCreationOutcome<C, Occurrence>as its accepted receipt. Successful commit returnsEstablished. A pure initialization error returnsInitializationRejectedwith the routed child and exact error. A caught pure initialization panic returnsInitializationPanickedwith the extant current routed child. Allocation rejection occurs before initialization and returns the complete routed creation. Host rejection after initialization returnsHostRejectedwith the routed child and still-uninterpreted initializationActions. Post-commit initialization-action failure is not a creation rejection; it enters the created child's drain. -
Remove every
let _ = control.send(...)and equivalent ignoredControlSenderresult. Each becomes an owned item settlement or terminal residual.
crates/bombay/src/local.rs and crates/bombay/src/launch.rs
CommitActions must expose the complete action settlement rather than
Result<(), E>. Initialization follows the same path as active turns. A child
is not published live until its initialization ActionSettlement has been
admitted or transferred; an initialization Step::Stop still settles its final
actions. A post-commit corrupt or rejected initialization effect drains the
installed incarnation and retains all residuals; it cannot be mapped back to
CreationRejection or roll back an accepted prefix.
For every creating behavior, host settlement owns one generic creation-entry
pass before ordinary ingress. In authored order it moves each entry into
ChildCreationSettled<C, Occurrence> and attempts the statically declared
creator system lane. This is one generic Births<C> capability, not an atomic
template adapter. A live creator receives the exact ChildCreationOutcome, including
the non-cloneable child and initialization value on rejection. If admission is
closed, the control send returns the complete root event;
RecoverEvent<ChildCreationSettled<C, Occurrence>, Path> recovers the exact
product and the lifecycle host retains it. A stopping creator may transfer the
entry directly without reopening its mailbox. NoBirths requires no
settlement lane or placeholder event.
The same mechanism must work for a non-atomic creating catalogue template and
for an atomic aggregate before Bombay accepts it. The Driver does not inspect a
variant, runtime type, or positional index: the concrete environment is
monomorphized over the creator event, child occurrence, and compile-time path.
The result of each attempted admission is an admitted receipt or the complete
still-host-owned creation product, never a discarded ControlSender result.
Source action results
Creation uses the one generic action-settlement path, not the source-result protocol used by emitted requests. The same Driver operation must also process request items whose exact interpretation result returns to the emitting actor. Stable-proxy owner operations and direct-pool assignments are the first two unrelated request consumers.
Behavior supplies only static declarations and owned values:
- the concrete action owns its accepted receipt, rejection, prerequisite, and source identity;
- the generic source-action product fixes every returned input as the exact
SettledItem<Item, ItemSettlement<...>>and preserves authored order; ProxyOperationuses that exact result. Its accepted value isProxyInputReceipt, containing the creator-local child route, exact proxy, and operation correlation; rejection retains the complete operation;AssignmentDeliveryuses the same exact result. Its accepted value contains exact worker and assignment correlation; rejection retains the complete assignment delivery;PrepareWorkersuses the same start settlement for fixed supervision and direct-pool recovery. Its accepted value is an exactWorkerPreparationStartedreceipt. The separately injected, laterWorkerPreparationowns the source, ordered selected roles, complete prepared submissions or an exact worker/source rejection, and untouched suffix; and- structural composition processes inner then owned lanes, matching the one declared interpretation order.
An action may not choose another result type or translate the generic alternatives. Such a hook is a per-template settlement adapter: it can duplicate the interpretation law and can silently discard the exact rejected or unattempted action. Domain-specific receipts remain lawful as the accepted type; later domain outcomes and application replies remain separate protocols.
Those declarations perform no delivery. Bombay must add one generic static
operation over the settlement products returned by CommitActions. For each
source result, that operation must:
- retain ownership in the current actor host;
- construct the statically selected current-actor system input;
- submit it through the existing unbounded control capability;
- remove it from host custody only after control admission succeeds;
- recover the exact input from the returned closed-control event when admission fails; and
- after the first closed admission, transfer the current value and every later value without trying to reopen the actor.
The Driver processes one admitted result turn and the complete transitive action
chain it produces before offering the next result from the earlier product.
Only after that chain is quiescent or transferred outward may it continue with
the next ordinary User communication. This must be an iterative queue owned by
the existing Driver; it is not a recursive call, detached task, second mailbox,
or second actor loop. The ordering does not claim termination or fairness: an
actor can continually produce more source results and starve ordinary traffic.
SourceSettlementCustody::offer_next_to_source is the static operation over a
complete ActionSettlement. It visits creation before sends, preserves every
named field and wrapper order, and returns SourceCustody::Admitted immediately
after exactly one successful admission. Exhausted proves no source input
remains; Closed returns the current input and untouched suffix in the exact
residual product. The operation composes through every generated named sends
product, catalogue-owned named product, and SendLayer without Bombay matching
product fields, variants, or structural positions by hand. The generic source
product supplies the exact input type once. Neither FixedSupervisor,
DynamicSupervisor, FIFO pool, nor keyed pool supplies a runtime admission
adapter.
Exact Bombay and Timers changes for scheduling
Timer scheduling follows the same total action law; it is not a
FixedSupervisor exception. ScheduleAfter currently declares the later
TimerElapsed input but has no ActionItem settlement. The present Bombay
interpreter computes Instant::now().checked_add(after) and returns only unit
or DeadlineOverflow. The selected bombay-timers 0.1.0 queue replaces an
existing equal key and panics when its internal generation or insertion
sequence exhausts. Those are current implementation constraints to replace,
not the architecture.
Behavior Actors must give both scheduling requests ordinary total contracts:
ScheduleAfter:
accepted = TimerScheduled { id, generation }
rejected = DeadlineOverflow
| QueueGenerationExhausted
| QueueSequenceExhausted
ScheduleAt:
accepted = TimerScheduled { id, generation }
rejected = QueueGenerationExhausted
| QueueSequenceExhausted
The two rejection sums remain distinct because an already absolute
ScheduleAt cannot incur relative-deadline overflow. Adding an impossible
alternative merely to share a type is prohibited. Generic ItemSettlement
already returns the complete original request for every rejection and preserves
independent later lanes. TimerScheduled is only the exact scheduling receipt;
the eventual TimerElapsed remains a later typed input and must never be
fabricated at acceptance.
Timers must change its existing queue operation to return a closed error before
changing counters, current-key state, or heap ownership. Bombay maps those two
queue errors without collapsing them, maps relative deadline overflow only for
ScheduleAfter, and returns the exact TimerScheduled receipt after insertion.
The current queue token remains Timers-owned scheduling authority; a Behavior
does not receive or recreate it. Poison recovery, memory exhaustion, and polling
are not new Behavior rejection alternatives.
FixedSupervisor privately selects a non-reused timer identity for each delayed recovery and carries an explicit generation. Two overlapping delayed recoveries therefore cannot use the same queue key, because the selected queue truthfully treats equal keys as replacement. Immediate recovery emits no schedule and owns no dummy timer. Recovery identity, timer identity, and release ordinal stay distinct values; none is inferred from another or from product position.
Bombay implements these as the same generic static InterpretItem operations
used by unrelated timing templates. It must not inspect FixedSupervisor,
introduce a schedule adapter, create another timer queue, or return unit after
successful scheduling. Its Driver settles scheduling in the named action order,
admits the exact settlement through the normal source-result operation, and
retains it through the existing retirement product if actor admission closes.
Exact Bombay changes for atomic diagnostics
Behavior Actors supplies one shared DiagnosticAction<Route, Diagnostic> for
the identical diagnostic law used by FixedSupervisor, DynamicSupervisor,
FifoPool, and KeyedPool. The action is exactly:
Deliver { route, diagnostic }
| Terminal { diagnostic }
The route type selects the real rejection vocabulary statically:
Recipient<P> uses LogicalDeliveryReason, EstablishedRecipient<P> uses
ExactDeliveryReason, and the route-free Infallible alternative uses
Never. Bombay must add ordinary InterpretItem implementations for these
three concrete route families. It must not switch on a runtime protocol, erase
the diagnostic, or add an aggregate-specific interpreter.
For Deliver, Bombay consumes the route and diagnostic through the existing
logical or exact delivery capability. Accepted delivery returns
DiagnosticAccepted::Delivered. Rejection returns the complete original
DiagnosticAction and the exact selected delivery reason. Waiting for bounded
Communication capacity is backpressure and is not a rejection variant.
For Terminal, Bombay performs no delivery and returns
DiagnosticAccepted::Terminal(diagnostic). The complete diagnostic therefore
remains inside the accepted action settlement owned by the current actor host.
When source admission is closed or the source stops in the same transition,
that settlement follows the normal child-to-parent-to-root custody path and
ends at the non-rejecting root custodian. Terminal transfer is not a log call,
detached task, second mailbox, or special Driver branch.
The first production consumer is FixedSupervisor's exact non-ready initial
proxy transition. It emits
DiagnosticAction::Terminal(FixedDiagnostic::ProxyOutcomeFailed(...)) together
with Step::Stop. Bombay must settle the diagnostic before completing actor
retirement, retain the complete role/outcome payload, and then transfer the
remaining proxy child graph through the same retirement barrier. The aggregate
does not serialize that payload, log it as a substitute for custody, or keep a
second runtime coordination object.
The next production consumer is FixedSupervisor's committed-roster shutdown.
Its final Step::Stop retains the complete private FixedShutdown state. That
state includes ready workers and readiness values, locally cancelled initial
inputs, exact initial-input settlements, completed initial outcomes, proxy-stop
results, rejected proxy births, and any non-accepted shutdown operation reunited
with an exact proxy exit. Bombay must move that concrete stopped supervisor
value through the same retirement product. It must not inspect those private
alternatives, convert retained values to logs, or require FixedSupervisor to
publish a runtime-specific residual. A pending proxy creation remains an
ordinary previously emitted creation action: Bombay returns its exact generic
settlement. FixedSupervisor alone decides whether that means one newly committed
proxy must be shut down, an exact creation rejection means absence, or wrong
provenance must be returned unchanged. Recovery and deadline shutdown states
target this same generic retirement contract; they do not justify a
supervisor-specific Driver branch.
H119 realizes FixedSupervisor's deadline half. The first
RetireActorGraphAfter shutdown emits one ordinary relative scheduling action.
Its exact non-accepted result or its exact elapsed timer selects Step::Stop;
the private deadline state retains that exact cause while FixedShutdown
retains every unresolved member value. Bombay must therefore transfer the final
concrete supervisor and the environment's residual actions together. It must
not inspect the deadline state, synthesize per-member shutdown reports, or
discard the supervisor after extracting Exit or unit. The same generic
retirement product also covers an external worker-source operation that is
still resolving when the deadline forces retirement.
Bombay's required change is deliberately ignorant of FixedSupervisor's private
decomposition. FixedShutdown decides declaration-order roster completion,
FixedShutdownMember owns one member's remaining startup values, and
ProxyStoppingMember decides the exact shutdown-settlement/exit join. Bombay
only executes typed actions, returns their complete settlements, observes exact
child exit, and transfers the stopped concrete behavior to parent then root
custody. No in-memory supervisor coordinator belongs in Bombay.
CommitActions must preserve these receipts with every other item settlement.
ActiveEnvironment::retire, the Driver retirement barrier, and the root runner
must return or retain the terminal diagnostic exactly as specified by the
general custody changes in this document. A delivered diagnostic rejection is
already terminal settlement and must never be reintroduced into the aggregate
to emit a recursive diagnostic.
Exact Bombay change for keyed customer rejection
AA-40 requires more custody than an ordinary reply delivery for one case only:
a synchronously rejected submission must preserve the original customer route
inside the rejected action while a clone targets the KeyedOutcome::Rejected
message. Behavior Actors represents that invariant with the
CustomerDelivery<P> action item. The public KeyedOutcome remains free of an
address generic, and neither the key nor payload is cloned.
Bombay must add one generic static InterpretItem<CustomerDelivery<P>, ..>
implementation alongside its existing logical and established delivery
implementations. It exhaustively handles the four concrete alternatives:
Logical { delivery }
Established { delivery }
RejectedLogical { delivery, customer }
RejectedEstablished { delivery, customer }
The ordinary alternatives delegate to the corresponding existing logical or
established delivery capability. The rejected alternatives do the same while
holding customer untouched. Acceptance consumes the complete action and
returns unit, matching ordinary delivery. Logical rejection reconstructs the
same CustomerDelivery with the returned Delivery, original customer, and
ReplyDelivery::Logical(reason); exact rejection does the corresponding
operation with EstablishedDelivery and
ReplyDelivery::Established(reason). Interpreter corruption likewise returns
the complete reconstructed action and exact fault. The uninhabited delivery
prerequisite remains uninhabited.
This is one generic customer-action interpreter, not a KeyedPool branch or
settlement adapter. It does not inspect keys, roles, outcomes, bindings, pool
state, or aggregate type. Bounded Communication send capacity remains
backpressure. When actor admission is closed, the complete returned
CustomerDelivery travels in the existing environment residual and
parent-to-root custody path; the Driver must not reopen the stopped pool or
discard the original route. The research-local Bombay probe must cover logical
acceptance, logical rejection, exact acceptance, exact rejection, and source
retirement after rejection using the unchanged Engine Driver.
The operation returns a residual product. A residual owns every result not admitted to the source, including the current result at closure and the exact untouched suffix. That product joins the environment retirement value described below. Bombay may not map it to a log, unit, a generic error, or a recreated request. The root runner is the final non-rejecting owner.
This is the required upstream change. The atomic worktree does not modify Bombay production source and does not add an in-memory coordination object to StableProxy or either supervisor. H34 demonstrates the stable-Rust ownership shape with distinct proxy and assignment results in both wrapper orders; the real Driver and retirement proof remains a Bombay integration gate.
H38 adds one exact interpreter obligation. Bombay's environment statically
implements PrepareWorkers for the application's concrete method-free
WorkerSource<Role, Worker, Plan> declaration. It invokes that source outside
Behavior. H45 requires that action to expose each selected application role only
as &Role: it owns immutable private role names while the pending supervisor
state retains every unique member-role authority. Bombay must neither request
Role: Clone nor reconstruct a role from roster position. It returns the same source authority and role names with every completed
result. WorkerPreparation contains three late domain outcomes: a complete
non-empty sequence of prepared submissions, an exact prepared prefix plus
worker rejection and untouched suffix, or source rejection before the first
worker submission. Corrupt or unattempted starts remain generic settlement
alternatives; source rejection after an accepted start is a late outcome.
The action is handled by the same InterpretItem and source-admission machinery
used for proxy operations, assignment delivery, and unrelated catalogue
actions. Bombay adds no dynamic registry, callback inside the actor, factory
actor mailbox, erased worker envelope, or FixedSupervisor Driver branch. The
result enters the same generic source-admission queue and retirement product
described above.
The concrete static interpreter consumes the request with start() and
returns its exact receipt before awaiting the source. It injects the later
WorkerPreparation through the typed control lane. The returned
StartingWorkerPreparation advances the first attempt with
accept(submission), reject(reason), or reject_source(reason).
Acceptance returns
ControlFlow::Continue(PendingWorkerPreparation) while a selected name remains
and ControlFlow::Break(WorkerPreparation) only after the last one. The initial
request alone implements the action traits; the pending value cannot be
re-emitted as fresh work after it owns a prepared prefix. This progression is
the arity proof: Bombay never supplies a role to an accepted result and performs
no separate length validation. If the Driver retires during preparation, its actor-owned source task retains
the StartingWorkerPreparation or PendingWorkerPreparation value until it
can return a complete result through exact typed retirement custody.
Every request also carries one private non-reused preparation ticket. Bombay must preserve it by moving the request, start receipt, and later result through the typed event paths; it must not inspect, construct, compare, log, or reconstruct the ticket. Exact matching is FixedSupervisor policy.
If shutdown overlaps an emitted preparation, Bombay must not report actor retirement merely because ordinary mailbox admission is closing. The started progress cursor remains in the actor-owned source task until interpretation produces its complete result. Open control admission returns that result to the draining FixedSupervisor; closed control admission moves it into typed retirement custody. In neither case may Bombay cancel the request, recreate its source, call a FixedSupervisor-specific adapter, or treat successful preparation as permission to issue replacement work. The aggregate owns that late-result decision.
The clean-room FixedSupervisor now realizes its half of this contract. Its
shutdown member retains the private preparation expectation beside the exact
StableProxy drain, and the stopped aggregate retains the complete generic result
after admission. Bombay must therefore preserve the final monomorphized
FixedSupervisor value when interpreting Step::Stop; extracting only a unit
exit status would lose the source, rejection, prepared submissions, or
interpreter fault held there. The required Bombay change remains the generic
Driver retirement product described below. It is not a FixedSupervisor branch
and does not require Bombay to know PreparationExpectation.
The FixedSupervisor's concrete internal event includes the typed start
settlement and separate late preparation result but does not constrain the
worker source in its
unrelated proxy variants. PrepareWorkers<Source, Role, Worker, Plan> is the
static source selector because that action type uniquely identifies its
associated result. The named sends product includes one worker_preparations
source-action lane. The generic source-admission operation injects the start settlement through
EventIngress and the later result through InjectEvent; the Driver must not pattern-match
FixedSupervisor, special-case the lane, or add a callback. The declared fixed-supervisor lane
order places worker preparation after proxy creation/observation work and before
proxy input, scheduling, lifecycle, reply, and diagnostic work. A later
production slice must compile this exact product before automatic recovery is
claimed.
FixedSupervisor proxy operations independently use Here as their static
same-actor source. Bombay therefore admits their exact generic
ProxyOperation<Here, Worker, Plan> result through ordinary EventIngress; the
operation does not use the aggregate event type as a recursive selector. The
worker-preparation result remains a distinct input type for that same current
actor, with no positional routing or runtime lookup. Bombay's static generic
admission implementation selects the actor through the action/result type pair;
it does not inspect the action value or infer a runtime destination.
Direct pools use this same interpreter operation for automatic recovery with a
non-empty sequence containing exactly one semantic role. The pool retains the
recovery decision and role authority while the action owns the source and role
name. Bombay gains no pool branch: the same static request progression returns
the source, name, and one WorkerSubmission or the exact rejection. Initial
pool construction remains separate and may invoke its factory before a
Behavior exists. A pool never stores or calls that factory from a transition.
Engine retirement and root custody
ActiveEnvironment::retire, the local inbox drain, Driver, run_with, and
the application result must carry one typed retirement product through the
existing barrier. That product owns both the final monomorphized Behavior and
the environment residual; it does not inspect or erase the Behavior. Parent
rejection transfers the complete terminal value outward. The application root
owns a non-rejecting custodian and releases the barrier only after that product
is empty. This is the H03 contract as completed by H28's final-Behavior link,
not a new service: retirement remains part of the same Driver future.
Required upstream verification
- compile every concrete
InterpretItemimplementation without a universal error type; - replay H07 rejection, blocking, corruption, and wrapper-order suites against
ApplicationCapabilities; - prove creation semantic rejection continues to an independent timer or delivery while only its exact child operation is blocked;
- prove two heterogeneous creation alternatives return branch-preserving receipts and exact rejected requests;
- prove
ChildCreationSettledreturns a non-cloneable child through one live creator and through closed creator admission into host custody, using the same generic code for an unrelated creating template; - prove stopping initialization settles final actions before publication or retirement;
- inject control closure at every parent/root transfer and recover the exact value at the root custodian; and
- run the unchanged single Engine
Driverthrough the retirement barrier with no detached work and no ignored must-use result.
Stable proxy
Status: feature-complete for the local aggregate. Initial start, readiness,
exact pre-ready return, replacement, owner shutdown, complete local custody,
independent models, compile contracts, and two stateful fuzz targets are
implemented and verified. Parent-to-root terminal transfer remains assigned to
Bombay and does not belong in this aggregate.
Actor-graph deadline policy belongs to the owning supervisor, pool, or Bombay
root, not StableProxy. The complete transition
matrix is owned only by
actor-laws/proxy.md.
Responsibility
StableProxy owns one stable service identity and at most one current worker.
It owns initial worker start, replacement, unavailability, exact worker exit,
creation, initialization, activation, worker return, and structural-owner
outcomes and diagnostics.
It does not own restart strategy, restart budgets, fixed membership, dynamic membership, pool jobs, customer outcomes, or runtime execution. Its public service protocol is exactly the worker protocol. Start, replace, and shutdown control remain private to the structural owner.
Fresh worker creation is the actor-model law. Stable service identity across successive workers is a derived proxy construction. Creation-before-dependent- operation ordering, owner-only control, activation admission, and lifecycle publication are Bombay policies.
One aggregate, separated concerns
StableProxy<W, P> is the only Behavior in this family. Its six files have
one domain owner each:
mod.rsowns the aggregate, every state transition, construction, and the soleBehaviorimplementation;state.rspassively owns the complete current and terminal state sums;protocol.rsowns commands, events, outcomes, diagnostics, and returned terminal data;effects.rsowns the seven named action lanes and their declared order;operation.rsowns the parent request, receipt, rejection, and settlement contract;worker/mod.rsowns the proxy's pending and current worker values, creation correlation, and shutdown/stop correlation.
The shared atomic/worker/initialization.rs and
atomic/worker/activation.rs modules own the worker-host request and result
protocol used by every atomic aggregate. They are not StableProxy child files.
The pending proxy worker stores only creation and activation values; the
emitted CreateChild owns the concrete worker until settlement, so no worker
type marker exists in proxy state.
The worker module is not another actor aggregate. The worker's own Behavior
owns its domain decisions; this module stores only the parent proxy's exact
typed authority and observations. No transition verb or arrival path owns a
file, and mod.rs controls the public surface.
Fixed and dynamic supervisors consume
typed proxy outcomes and must not reproduce worker creation, forwarding,
readiness, replacement, or return transitions. Direct pools own a different
direct-worker lifecycle and never consume proxy state.
Mnesis core was inspected as a DDD structure reference. Its useful pattern here
is a flat, concept-owned kernel with one aggregate root and passive state. Its
AggregateRoot, command dispatch, event replay, versioning, and persistence do
not belong in StableProxy: the existing Behavior contract already owns the
pure actor transition, and adding Mnesis would create a second aggregate model.
Current state and action law
The implemented closed phases are:
Dormant
Starting {
Creating { stopped: Option<exact stop> }
| Initializing { stopped: Option<exact stop> }
| Activating { progress: WaitingForStart | Running, stopped: Option<exact stop> }
| ReturningWorker(WorkerStopping)
}
Ready
EmptyInitial | EmptyAfter
Replacing { ReturningPredecessor | SuccessorResultAwaitingShutdown }
ShuttingDownCreation | ShuttingDownInitialization | ShuttingDownActivation
ShuttingDownWorker | ShuttingDownReplacement
Stopped(complete private retirement values)
H77 corrects the initial literal state lowering. Creation, initialization, and
activation remain different phases, but each owns the same exact current truth:
the correlated stop is absent or has arrived once. One Option<ChildStopped<_>>
directly represents that invariant; separate *AfterStop phase and result sums
would store arrival history and duplicate transitions. Activation separately
owns WaitingForStart | Running. Its stop presence remains visible to the exact
readiness transition, so stop-before-ready still cannot publish availability.
No phase is represented by coordinated flags or several correlated optional
fields.
An accepted start issues one checked CreationId, enters Creating, and emits
the complete worker submission in CreateChild in that same transition. The proxy
does not wait for a route result and stores no runtime nonce. Bombay either
routes the complete creation batch or returns it unchanged with
ChildNamespaceExhausted; the proxy then returns the complete worker and
activation policy through generic creation settlement. A foreign or stale
creation result cannot replace the pending submission.
Every staged worker creation emits one exact ObserveChild request in the same
Actions. A committed worker emits one InitializeWorker request.
Successful initialization returns the original activation plan and one
non-cloneable ActivationPermit; only those values can construct
BeginActivation. Bombay admits Started before polling the plan. Only a
matching Ready received after matching Started opens service routing.
Readiness arriving before Started is returned unchanged as a diagnostic. A
foreign worker, initialization, activation, shutdown result, or worker stop is
also returned unchanged and cannot advance state. WorkerAttempt carries
opaque issued evidence; its ordinal is diagnostic data and cannot prove
identity. Two proxies' first attempts therefore remain distinct. Address reuse,
timing, and adjacency prove nothing.
Service input in Ready becomes one EstablishedDelivery to the exact current
worker. Service input in every other phase becomes one Unavailable owner
outcome containing the original sender and command.
An initialization-effects rejection, activation-start rejection, or accepted
activation rejection begins exact orderly shutdown of the committed worker.
For initialization, Bombay retains the complete concrete action settlement in
the worker environment and returns only the closed
WorkerInitializationFailure classification plus the affine activation plan.
The proxy reports the terminal owner result only after both shutdown resolution
and exact worker stop arrive. Either arrival order retains all proxy-owned
values. If the worker stopped earlier, later initialization or activation input
consumes the stored exact stop without reopening service. If readiness arrives
first, the worker becomes ready and a later stop follows the ordinary
ready-worker law; H77 deliberately preserves this ordering distinction.
WorkerStartResult has three decisions: creation rejected, worker ready, or
committed worker unavailable. WorkerCreationRejection owns the exact
pre-commit cause. ProxyDrain<W, P> owns the exact committed-worker cause and
retained values. The outer result never repeats those nested causes as another
alternative. Its ready and unavailable alternatives carry the opaque worker
attempt, not a worker actor or route.
ProxyDrain<W, P> is a visible ownership result. Its six alternatives name current worker
outcomes: initialization rejected, stopped, or completed after an observed
stop; and activation start rejected, activation rejected, or activation
completed after an observed stop. A rejection that may follow an emitted
worker-shutdown request owns Option of its exact shutdown receipt. None
means no request was emitted because the worker had already stopped; it does
not encode which transition function ran. Initialization-returned and
observer-returned stops remain separately owned because they are independent
authorities. There is no duplicate cause, forwarding constructor, projection
operation, string case label, runtime lookup, callback, or generic policy.
It never owns the worker's complete initialization action settlement. That
runtime value follows Bombay's typed environment-retirement path. This keeps
proxy and supervisor types independent of the worker's concrete send and birth
products without erasing or discarding them.
The unexpected-initialization diagnostic owns the complete
WorkerInitializationReport<W, P> directly. That typed input exposes no runtime action
product in ordinary application syntax and needs no second custody wrapper.
H124 normalizes all unexpected worker inputs to the same ownership equation.
Each ProxyDiagnostic alternative owns the complete returned input and the
copyable current ProxyPhase. The exact expected creation, worker attempt,
initialization, activation, or shutdown authority remains solely in proxy
state. It is neither cloned into the diagnostic nor moved out while correct
admission is still possible.
ProxyPhase reports the current proxy work, not private stored-stop presence.
Creating remains Creating and initializing remains Initializing after an
exact early worker stop. The stop still participates in the later transition;
the public projection cannot inspect or authorize it.
Replacement first issues the successor's checked creation ID while retaining
its complete CreateChild value. The exact predecessor remains owned, stable service
admission closes, and the proxy requests exact predecessor shutdown. If the
creation sequence is exhausted, the complete successor returns and the ready
worker remains unchanged. The retained creation is emitted only after the exact
predecessor stop arrives.
Predecessor stop and shutdown settlement form an order-independent join. If the
stop arrives first, one successor creation is staged immediately and the exact
pending shutdown correlation remains with the replacement. If shutdown settles
first, the complete prepared successor remains local until stop. A successor
that becomes ready before the late predecessor settlement is retained and is
not published early. Replacement from EmptyAfter needs no predecessor return
and still uses a fresh creation ID and the same WorkerStart transitions. Overlap
returns the complete submitted successor.
WorkerStart and ProxyReplacement are separate private closed state values.
The former retains only creation-through-readiness or pre-ready return; the
latter retains only proxy replacement values and the predecessor/successor
join. Neither advances itself: StableProxy admits each event, selects the next
aggregate state, and returns the complete Actions. Bombay
continues to own namespace binding, actor creation, task execution, delivery of
typed results, and terminal custody. No OCC store, lock, allocator, runtime
handle, or second lifecycle service exists in the aggregate.
Both components consume the same private WorkerStopping transition. It owns
only the exact shutdown-result/stop reunion and returns every unrelated input
unchanged. It owns no start, replacement, supervisor, pool, or runtime policy.
Owner shutdown is total in every implemented live phase. It closes stable service admission immediately, emits no ordinary owner result, and never emits the same worker shutdown twice. An uncreated successor is returned through the existing replacement-cancellation outcome and is never created. Pending creation still settles because its worker has already moved into the emitted action. Initialization and activation results remain owned but cannot publish readiness.
Initialization and activation each retain only their own exact unfinished work
and worker-return values during owner shutdown. They share WorkerStopping
for the shutdown-result/stop reunion, but no generic outer return product: an
initializing worker has no pending domain value corresponding to activation's
real waiting/running value. Introducing a unit or marker merely to share that
outer product is forbidden. A ready successor waiting for its predecessor
result is returned before the proxy stops. Both the predecessor result and
successor departure
remain owned regardless of arrival order. A rejected or already stopped
successor waits only for the predecessor result.
Initialization represents its returned work directly as
Option<WorkerInitializationRetirement> while the worker departs. There is no
parallel pending/returned enum: absence is the pending condition and presence
owns the one complete returned value. A duplicate remains outside that option
and is returned complete through diagnostics.
A stopped initialization is one outcome. Its optional initialization_stop
owns a second stop returned by the initializer only when the enclosing stopped
worker already owns the independently observed stop. Absence means the
initializer's stop is already the enclosing worker stop. There is no
StoppedAfterObservation alternative because that would preserve arrival order
instead of current ownership.
Once the worker has stopped, each shutdown join stores one current product:
the worker, the exact stop, the initialization or activation value still being
awaited or returned, and Option<EstablishedShutdownResolved<_>>. A receipt is
present only when the proxy emitted and settled a shutdown request; it is absent
when the worker had already stopped and no request was emitted. There are no
separate AfterDeparture, AfterStop, or terminal *AfterStop alternatives.
Those labels described arrival history and did not change any later decision.
ProxyRetirement is not an application API or runtime service. It is the final
private state of the concrete stopped StableProxy. Bombay must move that
whole behavior together with its environment residual through the existing
Driver retirement barrier, as specified in
atomic-runtime-settlement.md.
Total interpretation
Actions::creates owns worker creation. ProxyEffects declares these send
lanes in stable order:
worker observations
worker initializations
worker activations
worker shutdowns
worker deliveries
owner outcomes
diagnostics
Every runtime-local or structural-owner lane uses the one
InterpreterRequests product. Worker service uses EstablishedDelivery.
Actions still interprets child creation before these sends. A compile witness
requires the concrete StableProxy::Sends, not a homogeneous stand-in, to
implement the generic total interpretation contract.
The operation module also owns the crate-private receipt correlation evidence. Its tests prove that receipt consumption returns one affine operation id while the established proxy capability remains repeatable. FixedSupervisor has no parallel receipt model or fixture module.
No proxy-specific interpreter adapter exists. Corruption retains the committed
prefix and exact untouched suffix under
atomic-runtime-settlement.md.
Bombay realization
Bombay owns concrete child hosts, initialization action settlements, activation
work, task retention, mailboxes,
observation, and retirement. The exact single-Driver changes required for
initialization, activation, parent/root custody, and retirement residuals are
normative in
atomic-runtime-settlement.md.
Bombay production remains read-only in this campaign; no Behavior-side runtime
substitute is permitted.
Verification and external integration
The local aggregate is feature-complete. Current verification supplies
exhaustive small shutdown interleavings, independent models, generated longer
sequences, two stateful fuzz targets, optimized replay, compile denials, and
affine drop witnesses. The ready-worker target exercises exact and foreign
shutdown inputs; the replacement target exercises overlap, cancellation,
successor creation, and both predecessor-return orders. They share only the
identical four-state worker-return join.
Bombay must supply the documented parent-to-root custody contract. The atomic
implementation targets that contract without adding a Behavior-side runtime.
This external integration requirement prevents a whole-system completion claim;
it does not reopen StableProxy's locally owned transitions.
The repository-wide gates are owned by
atomic-actor-verification.md.
There is one canonical construction path described in
atomic-actor-devx.md. No legacy With* wrapper,
structural route, compatibility alias, public nonce, readiness flag, erased
callback, or application-authored final behavior type may return.
StableProxy deliberately has no ActorDrainPolicy and schedules no deadline.
The AA-01 law leaves deadline expiry and forced actor-graph retirement outside
its implementation evidence. FixedSupervisor, DynamicSupervisor, FifoPool, and
KeyedPool own their aggregate shutdown policies; Bombay root custody owns the
remaining runtime transfer. Adding a proxy timer lane would take on another
aggregate's responsibility and is prohibited.
Fixed supervisor
Status: active. The current implementation covers construction, startup,
coordinated recovery, diagnostics, lifecycle publication, management queries,
shutdown, and forced retirement. Four stateful fuzz targets cover shutdown,
recovery, delayed release, and three-role recovery correlation. Remaining
owner-partitioned viable mutation review, the local closure audit, the final
repository gates, and Bombay root custody remain open.
The complete transition matrix is owned by
actor-laws/fixed-supervisor.md.
This document maps that law to the current Bombay Behavior design without
repeating the matrix or its research history.
Responsibility
FixedSupervisor owns:
- one non-empty ordered roster of unique application roles;
- one
StableProxyfor each role; - each role's current membership and worker status;
- activation capacity across unresolved proxy operations;
OneForOne,OneForAll, andRestForOnerecovery selection;- restart eligibility, limits, release timing, and recovery correlation;
- topology-failure disposition;
- optional lifecycle publication;
- operational diagnostics;
- status and capability queries; and
- complete actor-graph retirement.
It does not create or replace workers directly. StableProxy owns worker
startup, replacement, service forwarding, and worker shutdown. The supervisor
coordinates several stable proxies without copying that law.
It is neither a dynamic membership supervisor nor a pool. Its roster never gains a role after construction, contains no customer job, and is not selected by a mode flag shared with another aggregate.
Construction
fixed(...) is the sole construction path. Applications provide:
- a function that prepares the initial
WorkerSubmissionfor each role; - validated
OrderedRoles; ActivationPolicy;Recovery;FailureReaction;ActorDrainPolicy; andDiagnosticDisposition.
Lifecycle publication is the builder's only optional transformation. Every semantic policy is explicit; no no-op recipient or placeholder callback is required.
The initial worker function runs only while the builder prepares the roster. It
is not stored in the resulting behavior and cannot run during a transition.
Automatic recovery instead emits one typed PrepareWorkers action. Its exact
settlement returns the worker source, prepared submissions, rejection, and
untouched role names without an application callback inside the actor.
Construction either returns one supervisor owning every prepared role and
submission, or returns FixedConstructionRejected. Its workers value is the
shared InitialWorkerRejection: the function, accepted prefix, rejected role
and reason, and unexamined suffix. The shared representation is owned by
atomic-actor-architecture.md; this document
owns only fixed-supervisor construction policy. A duplicate role is rejected by
OrderedRoles before the supervisor exists.
Roster ownership
The roster preserves declaration order. Each role has one immutable roster position used only when the law observes order. The position is not an address, actor identity, or public path.
A role is owned by either an independent member or one admitted recovery. An admitted recovery owns a non-empty unique set of participating members and the stable position of its trigger. A member cannot occur in two recoveries. When a member returns to service it can leave the recovery independently; the recovery retires after its final participant leaves.
The current roster uses its existing ordered collections to answer selection, authorization, query, and shutdown-order questions. It does not keep parallel membership tables or before/after history.
Startup and activation capacity
Initialization emits one fresh StableProxy creation and one observation
request for every declared role. Creation results are accepted only for the
expected creation ID and kind. A rejected creation leaves that role without a
proxy; a foreign or stale result is returned unchanged.
ActivationPolicy limits unresolved proxy operations, not actor count. The
supervisor authorizes waiting roles in declaration order while capacity exists.
An exact accepted proxy-operation settlement is required before the matching
proxy outcome is admissible. Capacity is released when the admitted outcome
resolves, even if another member value still awaits its matching stop.
A ready outcome makes the member available through its stable proxy. A failed startup follows the configured diagnostic disposition and topology reaction. Stopping the complete supervisor reuses ordinary supervisor shutdown; retiring one member reuses that member's exact proxy retirement.
Worker-stop classification and recovery selection
Recovery policy classifies an exact worker stop:
Temporarynever starts automatic recovery;Transientrecovers only after an abnormal stop; andPermanentrecovers after every exact stop.
An ineligible stop leaves the member empty. An eligible stop selects members by strategy:
OneForOneselects only the stopped role;OneForAllselects the complete eligible roster; andRestForOneselects the stopped role and eligible roles declared after it.
Normal or abnormal classification is derived from the exact owned worker stop at this decision. The accepted stop does not retain a second classification that could disagree with the terminal outcome.
Selection is all-or-nothing. If a required role cannot participate, the unchanged roster is restored and the configured topology response is applied. Disjoint recoveries may coexist, but their role sets may not overlap.
One accepted selection moves the sole WorkerSource into one PrepareWorkers
action. The supervisor retains the exact recovery correlation and cannot issue
replacement work until the source result returns. The action's accepted start
receipt establishes only that source work began. A later prepared result pairs
each selected role with exactly one WorkerSubmission in declaration order.
Worker or source rejection is a late typed result; interpreter corruption and
no-attempt return the original request in the start settlement.
The worker source has one private custody sum: it is available to the supervisor, owned by one emitted worker preparation, or retained as that complete returned preparation while the supervisor retires. There is no parallel optional return field. A shutdown return changes custody only; it cannot reopen recovery or issue another preparation.
Restart admission and release
Returned prepared workers are not yet authorized replacements. In the same actor transition that accepts the exact preparation result, the supervisor carries every prepared worker directly into one restart-admission transaction. It does not store a separately observable prepared roster phase. The transaction combines:
- its checked lifetime recovery count;
- the inclusive restart window and maximum;
- checked release calculation;
- one non-reused recovery ID; and
- for delayed release, one non-reused timer ID and generation.
RestartLimit owns the inclusive window and maximum. RestartRelease owns the
release calculation.
The transaction commits all of those values together or returns them unchanged.
It cannot partially spend a count, history entry, recovery ID, or timer key.
Its six denial alternatives are stored once in RecoveryDenialReason; recovery
admission moves that value and the complete triggering worker stop into
RecoveryDenied, which exposes both by reference without rebuilding a parallel
diagnostic sum. Denied prepared workers remain in the topology response selected
by FailureReaction; admitted workers become one durable recovery batch. Timer
schedule rejection does not use this stop transfer: its diagnostic owns the
rejected timer request while topology retains the worker stop.
The requested replacement count retains the native non-empty roster cardinality.
Only an accepted budget charge is narrowed to the configured u32 limit, so an
oversized coordinated recovery is reported as an unchanged restart-limit denial
rather than internal corruption.
Release policies are:
- immediate;
- constant delay;
- linear delay,
initial × ordinal; and - exponential delay,
initial × 2^(ordinal - 1).
Arithmetic is checked before applying the configured maximum. Overflow is a
typed denial, never saturation or panic. Delayed release emits the generic
ScheduleAfter action. Only its exact accepted timer may later authorize the
recovery; rejected, stale, foreign, early, or duplicate timer inputs cannot.
Replacement progress
Each participating member keeps its stable proxy and one current replacement product. That product owns:
- the previous worker attempt and readiness;
- the exact predecessor stop, if it has arrived; and
- the current proxy response: input pending, outcome pending, returned outcome, or rejected input.
The exact input settlement, predecessor stop, and proxy outcome update only their corresponding value. The accepted input settlement must precede the proxy outcome. After that admission, predecessor stop and proxy outcome may arrive in either order and produce the same restart. Duplicate, stale, early, or foreign inputs return unchanged.
Successful readiness returns the member to service and publishes lifecycle events in semantic order. Input rejection or proxy failure preserves the complete diagnostic payload and enters member retirement or supervisor shutdown according to policy. The next waiting member may use released activation capacity even while an earlier predecessor stop remains outstanding.
Diagnostics and lifecycle publication
DiagnosticDisposition has two exhaustive choices:
DeliverToattempts one typed diagnostic delivery; orTerminatetransfers the diagnostic with the stopped behavior.
FixedDiagnostic distinguishes startup failure, proxy-input rejection, worker
preparation failure, restart denial, restart scheduling failure, unavailable
service work, and unexpected typed input. Unexpected input retains the complete
input while the unchanged supervisor retains every expected correlation.
Terminal startup failure likewise leaves every unrelated roster member inside
the stopped supervisor; only Bombay's generic retirement transfer may move that
remaining ownership outward.
A preparation failure is valid for every recovery strategy and selected roster
size. Non-trigger participants return to their exact prior member states; the
trigger remains stopped. The diagnostic retains the prepared prefix, exact
rejection or interpreter disposition, and untouched suffix, while the supervisor
separately retains the reusable worker source and applies FailureReaction.
FailureReaction is independent of diagnostic delivery:
RetireMemberremoves only the unavailable role; orStopSupervisorretires the complete supervisor.
Optional FixedLifecycle publication reports started and restarted workers,
ineligible and admitted worker stops, unavailable service commands, and member
retirement. When no lifecycle recipient is configured, no lifecycle value is
constructed. Lifecycle publication never replaces the operational diagnostic
that owns a failure cause.
Management protocol
The public FixedCommand protocol contains:
Status, returning every member status in declaration order;Capability, returning the stable service capability for one role or the exact unavailable/unknown-role result; andShutdown.
Queries do not copy or change roster ownership. They remain admissible while the
supervisor is operating or draining. Only a ready member yields a service
capability; startup, empty, recovering, stopping, and retired members return the
corresponding UnavailablePhase.
Service commands use the stable proxy. If the proxy has no ready worker, the
supervisor accepts the exact returned command only while that proxy is still a
live member. The command is then moved to either the configured lifecycle
publication or WorkerUnavailable diagnostic. It is never stored as roster
state.
Shutdown and retirement
The first shutdown closes recovery admission and projects every live member in declaration order. It emits at most one shutdown operation for each live stable proxy. Repeated shutdown emits none.
Each proxy retirement is a commutative join of its exact shutdown-operation result and exact proxy exit. Either may arrive first; neither alone retires the member. A rejected operation remains complete. A member already stopping is adopted without issuing a duplicate operation.
Other work already outside the actor remains required during shutdown:
- proxy creation results;
- initial and replacement proxy-operation settlements;
- proxy outcomes and worker stops;
- worker-preparation start settlements and later source results; and
- restart-schedule settlements and exact elapsed timers.
Late results may restore custody or finish retirement but may not reopen recovery or issue replacement work.
WaitForActorGraph waits for every owned proxy to retire.
RetireActorGraphAfter also emits one exact deadline schedule. Its accepted
timer, or an exact schedule rejection/no-attempt/corruption, selects forced
retirement while preserving the complete unresolved supervisor and exact cause.
Foreign, stale, early, and duplicate deadline inputs return unchanged.
The stopped value retains every worker, submission, operation, result, stop, timer request, and diagnostic still owned by the supervisor. Logging is not custody, and Behavior does not spawn a task to dispose of those values. Because retirement consumes or drops that complete value instead of inspecting each private field in the producing crate, the corresponding fields carry checked dead-code expectations. Replacing them with unit is forbidden by the drop-count ownership regressions.
Bombay collaboration
Behavior owns the pure transition and complete Actions. Bombay must:
- interpret the named action lanes in declared order;
- return every exact accepted, rejected, blocked, corrupt, or unattempted item;
- admit initialization actions before ordinary ingress;
- move the complete stopped supervisor into parent/root retirement custody;
- keep accepting settlements through that retirement path without reopening the stopped actor; and
- use the existing Engine driver and retirement barrier.
The required Bombay change is specified in
atomic-runtime-settlement.md. It is an upstream
composition requirement, not permission to add a FixedSupervisor-specific
driver, detached task, mailbox, settlement adapter, or callback.
Canonical construction and application syntax are owned by
atomic-actor-devx.md. Verification ownership is mapped
in atomic-actor-verification.md.
Dynamic supervisor
Status: feature-complete locally. DynamicSupervisor implements bounded keyed
services, all five management commands, replacement, unexpected worker exit,
lifecycle and diagnostic publication, shutdown, and forced retirement. Focused
unit and model evidence plus four stateful fuzz targets cover cancellation,
transferred cancellation, shutdown, and unexpected exit. Three viable mutation
partitions have no survivors, direct construction inference and authority
forgery have compile contracts, and the obsolete legacy supervision corpus is
gone. Final fresh campaign audits and Bombay root custody remain open.
The complete transition law and exhaustive input matrix have one owner:
actor-laws/dynamic-supervisor.md.
This document is the shorter component guide.
Responsibility
DynamicSupervisor owns a bounded keyed service table; a fresh non-reused
generation for every accepted start or semantic rebind; start, replace, stop,
query, and cancel commands; exact operation correlation and affine cancellation
authority; overlap rejection; lifecycle and diagnostic publication; unexpected
worker-exit policy; capacity release; and complete shutdown or forced retirement.
It delegates stable forwarding and worker replacement to the single
StableProxy implementation. It owns no fixed roster, coordinated restart
strategy, restart budget, worker factory, pool queue, customer outcome, runtime
task, mailbox, address allocation, or actor loop. It is not a configurable
FixedSupervisor.
Fresh proxy and worker allocation follows the actor-model freshness law. The keyed table, generation policy, operation correlations, phase-sensitive cancellation, bounded terminal-operation retention, and durable lifecycle reporting are Bombay policies.
Construction
The canonical constructor is:
dynamic(
entries,
activation,
unexpected_exit,
actor_drain,
lifecycle,
diagnostics,
)
entries is a validated EntryCapacity; activation is a validated
ActivationPolicy. Both have checked constructors from usize, return
ZeroCapacity for zero, and cannot be exchanged accidentally. Every other
argument is a distinct required policy or typed capability. There is no default,
callback, marker, empty route, structural path, application alias, or public
proxy factory.
The complete application syntax and public products are owned by
atomic-actor-devx.md.
Commands and outcomes
The application protocol has exactly five commands:
Startadmits a new key and complete worker submission;Replacechanges the worker behind an existing stable service;Stopdrains and removes one ready, empty, creating, or waiting service;Queryreturns the current public service projection; andCanceluses the exact affine authority returned by an accepted start or replacement.
Start and replace share WorkerChangeReceipt and
WorkerChangeRejection<Reason> because their accepted and rejected ownership is
identical; their reason sums remain distinct. Stop, query, and cancellation keep
their own result sums because their outcomes and retained values differ.
DynamicLifecycle owns durable later outcomes. DynamicDiagnostic owns
malformed, stale, contradictory, or rejected runtime input according to the
configured disposition.
If global shutdown arrives while proxy creation is unresolved, the entry keeps
one of two private current phases: supervisor shutdown, or drain of an already
admitted explicit Stop. Both are publicly Draining, but later creation
settlement preserves their different terminal lifecycle outcomes. No optional
stop marker or repeated operation correlation encodes that distinction.
Service and cancellation rules
An accepted start reserves one fresh entry generation, proxy creation ID, ordered management operation, and cancellation authority before committing the entry. The management-operation order also determines which waiting service receives the next activation authorization. The table itself is the source of that ordering and capacity use; there is no second queue or occupancy flag. The affine cancellation authority owns the key and this globally non-reused operation. Entry generation remains lifecycle identity rather than a duplicate cancellation coordinate.
Cancellation returns a worker submission only while the supervisor still owns
it locally. After transfer to the proxy, cancellation is logical: the worker is
not reconstructed or returned, and a later successful proxy report cannot
publish Started or Replaced. The supervisor retains the exact worker result,
proxy-shutdown result, and proxy exit until all three resolve. Only then does it
publish OperationCancelled and EntryRetired(Cancellation), remove the entry,
and release entry capacity. Private proxy-input custody names input rejection
and a later proxy report as distinct terminal outcomes; rejection is never
encoded as an absent report. Cancellation completion constructs the ordered
OperationCancelled then EntryRetired(Cancellation) pair as one closed domain
operation; callers cannot select another retirement cause or omit either
message. The aggregate selects the exact entry once from the creation carried by
the proxy input result; retirement custody does not accept or repeat that
correlation. Arrival order does not change the result.
One matching terminal cancellation record is retained until the next accepted change. This makes immediate replay truthful without an unbounded history. Once the entry retires or its key is rebound, the old authority is stale. A rebound key always receives a fresh generation.
Replacement does not copy StableProxy's predecessor-drain logic. StableProxy reports replacement only after its own predecessor has drained. The supervisor validates that report against the retained current service and stores the exact successor or typed unavailability outcome.
Explicit stop and automatic unexpected-exit retirement remain different operations. Its terminal failure is one shutdown-settlement reason together with zero or one exact proxy stop. Those values are independent: the reason is reported unchanged, while stop absence permits restoration and stop presence requires retirement. The representation stores neither arrival order nor four copies of the same optional stop. Stale, duplicate, foreign, wrong-generation, and wrong-operation inputs return or diagnose their complete values without mutating the current entry. Proxy input and exit select their exact entry once; phase transitions do not repeat that same creation comparison or expose an unreachable second wrong-proxy failure.
Shutdown
Global shutdown arrives through the existing typed ShutdownRequested control
event; it is not a sixth application command. The supervisor closes new
mutations, converts every retained entry exactly once, preserves each unfinished
worker change, and drains according to ActorDrainPolicy.
WaitForActorGraph waits for every entry. RetireActorGraphAfter schedules one
deadline and stops after its exact timer or scheduling rejection while retaining
the unresolved supervisor state for runtime custody. Behavior Actors creates no
second Driver, mailbox, task, registry, or shutdown service.
Bombay collaboration
Bombay must assemble applications role-first: establish lifecycle and diagnostic actors, supply their typed capabilities to one pure root construction, and only then activate the root. DynamicSupervisor stores those capabilities; it never looks them up or creates them through an application callback.
Bombay must also carry stopped-child output through parent admission to a
non-rejecting root custodian and its existing retirement barrier. The required
Engine and custody changes are specified in
atomic-runtime-settlement.md. They are runtime
composition work, not DynamicSupervisor behavior or a reason to adapt this
aggregate to Bombay's current limitations.
Verification
Current evidence covers admission and overlap, cancellation replay and stale
authority, pre- and post-transfer cancellation, all six worker/shutdown/exit
orders, retained capacity, fresh same-key reuse, replacement, explicit stop,
unexpected exit, repeated shutdown, forced residual transfer, and complete
named action lanes. Four dedicated fuzz targets each pass 4,096-input replays in
development and optimized profiles. Direct real construction infers without a
final aggregate type, and compile-fail contracts reject duplicated or forged
cancellation authority. Same-signature foreign authority remains AA-20's
runtime-stale case rather than a fabricated compiler-only owner brand. The
verification plan and remaining whole-catalogue gates are owned by
atomic-actor-verification.md.
FIFO pool
Status: feature-complete for the local aggregate; final whole-catalogue audits
and Bombay root custody remain.
Ordered dispatch, assignment reunion, direct-worker start/activation, recovery
eligibility, role retirement, and dispatch shutdown custody have focused models
and production tests. Complete worker-source settlement, immediate and delayed
replacement release, restart-limit denial, topology disposition, and exact
restart schedule/timer correlation now have focused production tests. Exact
operating stops before readiness and their returned initialization/activation
values now have focused production tests without another worker state machine.
Standalone pre-ready failures now stop their installed worker and wait for the
exact shutdown/exit reunion before recovery. Late activation results during
drain advance only the exact existing progress and never restore eligibility.
Restart schedules outside the actor remain pending through drain; accepted
restart timers transfer their cancelled replacement immediately. Orderly
retirement releases the drained roster, while forced retirement retains the
unchanged unresolved roster and exact cause in the final concrete behavior.
Generated operating and FIFO-only fuzz evidence now exist. The canonical
worker and direct-construction compile contract, two actual timer-wrapper
orders, six focused compile denials, and result-type inversion are retained.
The warning-custody audit, complete viable mutation audit, and replacement
performance baseline are retained. The complete FIFO suite passes 90/90 in
debug and optimized modes. Its 305-candidate mutation run has 118 caught, 187
unviable, zero surviving, and zero timed-out candidates without exclusions.
The optimized canonical fixture sustains a median 2,732,844 complete assignment
cycles/s across five 100,000-cycle runs; this is pure aggregate throughput, not
mailbox or Engine throughput.
Bombay owns the documented terminal-custody change. The complete
transition law is owned only by
actor-laws/fifo-pool.md.
A test-only independent customer desk now compares the production aggregate's
public customer actions after every input in all 24 permutations of assignment
delivery acceptance, completion, exact worker exit, and shutdown. Its
oldest-observation queue independently predicts first-terminal precedence,
route and payload custody, shutdown return, and terminal uniqueness. It reads no
private aggregate state. Recovery and full-drain sequence parity are closed by
the direct independent comparisons below.
An independent recovery desk also matches all 20 combinations of operating or
shutdown ownership, both topology reactions, and every preparation outcome.
An independent retirement desk matches every ordering of one ready worker's
stop, shutdown return, deadline rejection, accepted scheduling, and exact
deadline firing. It proves normal role release and forced role retention without
reading private pool state. A generated queue desk additionally compares 192
shrinkable scripts of up to 128 repeated submissions, delivery acceptances, and
completions against the real aggregate after every applicable input. Its sole
VecDeque predicts bounded admission and oldest-first dispatch without reading
private pool state; a newest-first inversion fails on the exact next payload.
The FIFO-only fuzz target retains the real emitted assignment plus concrete
stale and foreign inputs. It discovered and now preserves both exact worker-stop
orders in which accepted or rejected delivery settlement reunites the final
assignment prerequisite. Three named seeds, 5,000 development runs, and 5,000
optimized runs preserve terminal uniqueness and every unrelated action lane
without importing a private pool phase.
Responsibility
FifoPool owns direct worker incarnations, one bounded global backlog,
immutable admission order, direct assignment authority, completion correlation,
worker interruption, at-least-once retry or exact failure return, direct-worker
restart policy, exactly one terminal customer outcome per accepted job, and
complete shutdown extraction and drain.
It contains no stable proxy or hidden supervisor. It owns no key, binding,
per-role queue, affinity selector, or keyed management operation. It is not a
mode of KeyedPool.
Collaboration and policy
Fresh worker allocation is the actor-model law. FIFO admission, circular idle selection, immutable retry ordering, interruption disposition, restart policy, and terminal customer custody are Bombay pool constructions. Assignment and direct-worker lifecycle values may be shared with KeyedPool only after the same law suite passes both consumers without modes, ignored outcomes, or weaker errors.
The pool retains the canonical customer obligation and route. A worker receives only a moved assignment containing execution payload and affine completion authority. Delivery settlement, completion, and exact worker exit form the four-way order-independent join owned by the aggregate; a generic settlement lane cannot replace it.
A queued obligation stores only its immutable admission order and an optional assigned role. The role remains necessary for a later assigned-job return. The event that caused retry does not remain in queue state because worker exit and rejected delivery have identical future behavior. Forced-retirement and emitted diagnostic payloads are different: they remain complete affine custody until Bombay or the selected diagnostic route accepts them.
Exact worker shutdown reuses the generic protocol described in
atomic-runtime-settlement.md: the lifecycle
host retains the complete ActionItemResult<ShutdownEstablished<...>>, while
the pool later receives only EstablishedShutdownResolved<P> and the exact
ChildStopped input. Direct-worker state must not duplicate the emitted request
or import StableProxy's private worker state.
The initial factory is consumed only while constructing the all-or-none roster.
Its rejection is the shared InitialWorkerRejection described in
atomic-actor-architecture.md, embedded in
FifoConstructionRejected beside FIFO policy. Automatic recovery never calls
that closure inside Behavior; permanent and
transient recovery emit the generic typed worker-source request for one role,
which Bombay executes and settles outside the actor. Temporary recovery has no
source. One shared worker classification defines normal and abnormal stops for
both fixed supervision and FIFO. FIFO then owns the closed decision to prepare,
wait while its single affine source is in use, retire the role, or stop the
pool. A second simultaneous eligible stop cannot duplicate the source and waits
in declaration order. FIFO still owns budget, timing, and topology disposition.
An exact preparation start receipt records that the source has begun; it does
not assert a prepared worker. The later successful, worker-rejected, or
source-rejected result returns the affine source exactly once. A corrupt or
unattempted start returns the original request. Under RetireRole, that
source immediately serves the first waiting role before ordinary dispatch;
under StopPool, ordinary shutdown drains surviving workers. Delayed restart
release admits only its exact schedule result and timer once.
When an exact temporary worker stop becomes actionable, RetireRole removes
that role from future dispatch, fills any surviving ready worker from the same
ordered backlog, and returns the remaining backlog only when no serviceable or
recoverable role remains. StopPool enters the ordinary pool shutdown path and
requests shutdown only from workers that have not already stopped. A temporary
worker never occupies recovery state.
An exact initialization or activation failure without a prior stop does not
retire an installed worker by assertion. The pool transfers the complete
returned failure through diagnostics, requests exact worker shutdown, and reuses
the same Stopping shutdown/exit reunion as rejected assignment delivery.
Activation failure releases its occupied capacity and admits the next waiting
role in declaration order. Recovery begins only after both shutdown settlement
and exact worker exit arrive.
Once pool shutdown begins, an activation already dispatched remains correlated
until its exact result returns. Exact Started advances that worker from
dispatched to activating. Duplicate or foreign starts are diagnostic-only. A
later readiness or rejection retires the activation progress into diagnostics;
it cannot make the worker idle, assign queued work, or start recovery. The same
worker remains in the ordinary exact shutdown/exit reunion.
FIFO and KeyedPool implement that retirement equation once in the direct-worker
model. Retirement stores Option<WorkerStartupCustody>: an inhabited value is
the exact activation plan, initialization attempt, activation permit, activation
start, or activation still owned locally; absence means no startup value remains.
It does not use service readiness to stand for absence. The shared transition
admits only exact initialization and activation inputs and returns complete
foreign, duplicate, rejected, or late values for aggregate diagnostics. Queue,
customer, binding, recovery, deadline, and final actor policy remain outside it.
A worker creation emitted before shutdown remains admissible during drain. If the exact actor is established after admission closes, FIFO starts no worker initialization or activation work: it emits one exact shutdown and holds the activation value in the drain member until exact stop. If that stop was already observed, FIFO retires the member immediately and transfers the activation to diagnostic custody. Rejected creation transfers the returned worker, activation, and any prior stop together. Foreign or reversed creation results leave every pending member unchanged.
An emitted worker-preparation request remains pending after shutdown even when all direct workers have retired. Its exact start settlement and, if started, later source result have distinct correlation phases. Only its private exact ticket can release that drain member. The late successful, worker-rejected, or source-rejected result restores the affine source when valid and transfers every cancelled submission, rejection, and stopped-worker value to diagnostics. It cannot create a replacement or emit a restart schedule. A foreign return leaves the member unchanged, and normal pool retirement waits for the exact return. Corrupt or unattempted starts return the original request before source work begins.
An emitted restart schedule likewise remains pending until its exact accepted, rejected, corrupt, or unattempted return. Shutdown then transfers the stopped worker, uncommitted replacement, and schedule return to diagnostics without creating another worker. A foreign schedule cannot release that custody. Once Timers accepts the schedule, Timers alone owns whether an elapsed event will be published; pool shutdown immediately transfers the cancelled replacement and does not wait for that event. Any later elapsed event is stale input. Worker restart and actor-drain schedules use distinct identities and cannot settle one another.
Normal retirement is the payload-free Stopped alternative; it retains no
role or shutdown history. Shutdown-identifier exhaustion, an exact returned
deadline schedule, or exact deadline firing instead enters the private
ForcedRetirement alternative. That alternative owns the unchanged ordered
drain roster and the exact cause. It does not reinterpret unresolved worker
creation, activation, assignment, recovery, or shutdown values as completed.
Bombay moves the whole final concrete behavior together with its environment
residual through the existing Driver retirement barrier. FIFO adds no residual
action lane, runtime adapter, actor loop, or task. Work still outside the actor,
including an accepted activation or worker-source request, remains in Bombay's
environment residual rather than being duplicated inside the pool.
Realization gate
Canonical construction, submission, outcomes, and the required
assignment.complete(result) worker expression are owned by
atomic-actor-devx.md. The implementation must prove
capacity zero/full boundaries, FIFO fill, multiple interrupted reinsertion,
every assignment join order, authority reunion, duplicate/stale/foreign
completion, recovery, complete shutdown, and exactly one terminal outcome.
No supervisor, proxy, generic pool engine, selector placeholder, structural
parent route, named interpreter send lane, helper type alias, or alternate pool
spelling may survive.
Repository-wide verification status and remaining gates are owned by
atomic-actor-verification.md.
Keyed pool
Status: feature-complete locally. The aggregate implementation and both bounded independent
models are executable. Separate retained stateful targets isolate binding
management from assignment/retirement custody. The assignment/quarantine
partition has all 18 viable candidates caught, including exact nonzero-role
completion selection; the binding value/table partition has all 22 viable
candidates caught, including public evidence privacy and exact caller request
identity. The recovery partition has all 13 viable candidates caught, including
exact delayed recovery of a nonzero role through preparation, scheduling, and
timer release. The binding and recovery partitions retain 49 and twelve
compiler-unviable substitutions as inventory only. The shutdown partition has
both viable candidates caught; thirteen compiler-unviable defaults remain
inventory only. The complete fresh 232-candidate owner inventory is classified:
all 72 executable candidates are caught and 160 compiler-unviable substitutions
remain inventory only. Final campaign audits and Bombay root custody remain
open.
Production includes canonical construction, pre-ready admission,
generation-exact binding management, exact successful direct-worker startup,
role-local dispatch, both accepted-delivery/completion orders, foreign
completion rejection, exact busy-worker interruption, permanent role
extraction, rejected-delivery authority reunion, worker quarantine, direct
worker preparation, immediate and delayed replacement release, exact restart
schedule/timer correlation, and shutdown custody for creating, initializing,
waiting for activation capacity, activating, ready, busy, quarantined, waiting
for the recovery source, preparing, scheduling a restart, and waiting for its
timer. An unused activation plan remains in the terminal diagnostic action
until the interpreter settles that action; it is not destroyed when the worker
exits. Forced retirement retains the unchanged unresolved worker roster and
exact cause in the final concrete behavior; a non-cloneable pending worker
value proves that custody survives Step::Stop. Generic settlement and FIFO's
complete direct-worker consumer are retained, and Bombay owns the documented
transfer of final behavior and actions through its retirement barrier. The
private diagnostic-cause sum carries a checked dead-code expectation because
the selected custodian receives it intact; KeyedPool does not expose lifecycle
internals merely to make the producer inspect them. The
complete transition law is owned only by
actor-laws/keyed-pool.md.
Responsibility
KeyedPool owns direct workers, one bounded queue per semantic role, stable
future-admission affinity, bounded bindings, non-reused binding generations,
exact generation-or-current-absence management expectations, rebalance/unbind,
assignment and completion, permanent role retirement/binding removal, exactly
one terminal customer outcome per accepted job, and complete shutdown drain.
It has no global FIFO queue, circular selector, stable proxy, or supervisor. It
is neither a wrapper nor mode of FifoPool.
Collaboration and policy
Fresh worker allocation is the actor-model law. Concrete key selection, binding capacity, fresh global generation, same-role rebalance semantics, immutable admitted affinity, per-role admission, and automatic binding extraction are deliberate keyed-pool policies.
The binding table solely owns each retained key. Accepted work retains only opaque generation/role evidence and its immutable admitted role. Unbind or rebalance affects future admission; it cannot retarget queued, assigned, or retried work. Permanent role unavailability removes every binding and returns all role-owned work in one transition. Temporary recovery retains bindings.
The initial factory is construction-only. KeyedConstructionRejected embeds
the shared InitialWorkerRejection described in
atomic-actor-architecture.md beside keyed
policy. Permanent and transient recovery use
the same generic one-role worker-source request as FIFO and fixed supervision;
Bombay executes it outside Behavior, while KeyedPool owns eligibility, budget,
timing, binding consequences, and topology disposition. Temporary recovery has
no worker source.
FIFO and KeyedPool use one passive direct-worker component for successful child
creation, initialization, activation admission, and readiness. That component
cannot inspect a queue or binding and cannot emit Actions. KeyedPool alone
selects the correlated role, applies the global activation capacity, and drains
only that role's oldest queued job when its worker becomes ready. Creation
identity and lifecycle kind are selected once by the shared ordered mapping;
the member transition does not repeat that correlation test.
They also use one direct-worker retirement transition. Its
Option<WorkerStartupCustody> owns only a concrete startup value still local to
the pool; absence means none remains. An exact stopped initialization joins the
authoritative worker stop with shutdown and returns the unused activation plan.
An exact activation Started receipt advances the retained attempt without a
diagnostic; a later terminal or duplicate activation remains complete for the
owning pool's diagnostics. This component chooses no binding, queue, customer,
deadline, recovery, or final actor outcome.
FIFO and KeyedPool now also consume the same private AssignedJob join. A
completion received before its delivery receipt remains attached to the
assignment; the exact receipt releases one customer completion. When the
receipt arrives first, the exact completion releases that outcome directly.
Worker identity alone is insufficient: the completion authority must identify
the retained assignment. An exact worker stop applies the configured
interruption policy once. If retry is selected but recovery retires the role,
the job remains an assigned obligation and is returned as
RolePermanentlyUnavailable; it is never reclassified as queued. Role
retirement extracts that role's queue and every binding in the same aggregate
transition.
FIFO and KeyedPool also use one private ForcedRetirementCause: exhausted
worker-shutdown identifiers, a deadline that was not scheduled with its
complete interpreter result, or the exact elapsed deadline. Each pool still
owns its own unresolved workers, customer extraction, and terminal transition;
there is no common pool or retirement engine.
FIFO and KeyedPool consume the same exact preparation start settlement and later source-result law. The shared worker value correlates the exact preparation request through both phases, retains the stopped worker and complete replacement submission, and changes from waiting for schedule acceptance to waiting for the exact timer. It cannot choose a role, touch a queue or binding, apply a failure policy, emit an action, or create a worker. KeyedPool alone serializes its affine recovery source, applies restart limits and release timing, returns work, removes bindings when a role becomes permanently unavailable, and selects role retirement or pool shutdown.
Rejected delivery returns the complete assignment to the shared authority reunion. An exact return restores the existing customer obligation to its original role queue at its immutable admission order, then quarantines that worker. The role remains unavailable until both the shutdown response and the exact worker exit arrive in either order. Only then may recovery or permanent role retirement begin. A returned assignment that contradicts an already received completion returns the customer obligation once, preserves both contradictory inputs in diagnostics, and drains the pool.
The independent keyed_model checks two keys/roles, zero and one capacities,
all eligibility classes, exact management expectations, immutable affinity,
every delivery/completion/stop permutation, retry order, retirement, shutdown,
22,621 bounded prefixes, and five deliberate inversions in debug and optimized
builds. The production keyed_binding_sequences fuzz target separately drives
only management commands through the real aggregate. Its scenario stores one
absent-or-bound value, issued and stale generations, and one foreign generation;
it owns no assignment, customer, or worker-retirement state. Five thousand
development and five thousand optimized inputs preserve same-role stability,
role-changing and post-unbind freshness, exact stale rejection, and foreign
generation rejection.
The separate keyed_assignment_sequences target begins with one ready worker
and one accepted binding, then drives accepted or rejected delivery, completion,
exact and foreign stops, shutdown settlement, and replay. Five thousand
development and five thousand optimized sequences preserve the original job,
payload or result, role, generation, logical customer, and one terminal outcome.
Its model retains the current post-stop phase because a late receipt can release
terminal custody, and permits ReturnedQueued when rejected delivery restores
an assignment to its admitted role immediately before that role retires. It
delivers no input after Step::Stop, matching the interpreter lifecycle law.
Realization gate
Canonical construction, submission, management, outcomes, and worker completion
syntax are owned by atomic-actor-devx.md. The exact
production-source binding harness proves final-generation issuance, permanent
exhaustion without wrap, complete rejected-key return, and preservation of an
existing binding when rebinding cannot issue a generation. Real aggregate
traces now cover shutdown from every reachable direct-worker and recovery
phase, including activation-capacity and recovery-source waiters. A separate
account-directory model now matches the production aggregate's automatic and
explicit binding, capacity rejection and release, same-role preservation,
cross-role generation, stale expectation, and unbind results without reading
production state. The same independent customer-custody model now compares all
24 delivery-acceptance, completion, exact-exit, and shutdown orders against FIFO
and KeyedPool. The keyed adapter also proves that every customer outcome retains
the original binding generation and role. Recovery/shutdown parity is now
closed by direct public traces: concurrent failures serialize the one recovery
source in role order; every accepted, rejected, corrupt, or unattempted worker
preparation and restart-schedule result returned after shutdown emits no new
worker work; an accepted restart timer is cancelled; and accepted or rejected
worker shutdown settlement reunites with the exact worker exit in either order.
The separate workshop oracle checks the same ownership equations and explores
22,621 generated recovery/shutdown prefixes in debug and optimized builds. It
uses no production state projection or second actor transition. Bombay's
separately documented root transfer remains an upstream integration
requirement.
No generic pool engine, optional key, selector mode, cloned key evidence,
generation inference, permanent tombstone, stable proxy, structural completion
route, helper alias, or compatibility spelling may enter the replacement.
Repository-wide verification status and remaining gates are owned by
atomic-actor-verification.md.
Normalized aggregate laws
These documents state the complete control states, retained data, transitions, outputs, rejections, and terminal paths for the five atomic actor aggregates. They are implementation-independent domain contracts. The adjacent catalogue documents map those laws onto Bombay's concrete typed behavior algebra.
Each law distinguishes actor-model requirements, derived library construction, and deliberate Bombay policy. Pseudocode identifiers describe semantic values; they are not additional exported Rust names.
Stable proxy falsification model
Status and authority
This document is the AA-01 independent semantic model for a stable worker
proxy. It is a non-production falsification oracle. It does not define Rust
types, a second behavior algebra, an interpreter contract, or evidence that a
solution-matrix row is implemented. It is authoritative for the StableProxy
behavior it covers. stable-proxy.md maps
that behavior to the selected implementation, and
atomic-runtime-settlement.md
owns runtime interpretation and custody.
The actor-model law used here is fresh allocation: creating a replacement must establish a fresh actor rather than overwrite an existing address. Staged creator-local correlation, commit-before-dependent-effects ordering, exact installation, activation, owner-only control, and drain policy are Bombay derivations or policy choices. The stable public identity and replacement protocol are template laws.
Runtime route selection is owned normatively by
docs/atomic-runtime-settlement.md. The proxy issues and stores only a
CreationId; it never chooses or stores a runtime route before emitting
CreateChild. Any older reservation label in the transition oracle denotes that
creator-visible correlation before interpretation and routed evidence only
inside the returned runtime settlement. It does not authorize a separate route
request, waiting phase, public route, or combined value shared by proxy and
Bombay.
Older shutdown labels that distinguish worker-return order describe semantic
cases, not required Rust variants. Once the worker has stopped, the retained
current product is the worker, exact stop, remaining initialization or
activation value, and an optional exact shutdown receipt. Absence is lawful only
when no shutdown request was emitted. H147 proves that separate
AfterDeparture, AfterStop, and terminal *AfterStop Rust alternatives add
arrival history without changing an accepted transition.
The model must falsify designs that make any of these claims false:
- One proxy owns at most one installed worker incarnation.
- A service command is sent only to the exact current incarnation in
Ready. - Installation is not readiness.
- Replacement drains the exact predecessor and creates a fresh successor.
- Every accepted transition produces one next state and one complete named action product; no effect is ambient.
- Shutdown closes admission and settles every definition, creation, initialization, activation, and incarnation the proxy still owns.
- Stale, duplicate, foreign, and contradictory facts cannot advance state.
“Atomic” below means one local Behavior fold commits a state and its complete
Actions. It does not claim atomic delivery, creation, or cross-actor work.
Semantic values
The following names are model vocabulary, not proposed public Rust names.
BirthKind =
Initial
| Replacement { replaces: WorkerIncarnation }
BirthAttempt = the fresh, non-reused CreationId issued by the proxy
RoutedBirth = interpreter-private { attempt: BirthAttempt, route }
WorkerRecipient = proxy-private routable capability for one committed worker birth
WorkerIncarnationEvidence = opaque non-routable provenance for that birth
WorkerIncarnation = private { recipient: WorkerRecipient, evidence: WorkerIncarnationEvidence }
InitSettlement = complete initialization effects retained by the lifecycle host
InitAttempt = exact correlation for host-owned initialization work
ActivationPlan = the concrete work required before the worker may be ready
ActivationPermit = a one-shot capability issued only after successful initialization
ActivationAttempt = a fresh, non-reused correlation for one consumed permit and plan
ActivationPhase =
AwaitingStartSettlement
| InFlight
DrainCompletion =
ReturnToEmpty
| StopProxy
ActivationFailure =
StartRejected { request, reason }
| Rejected { rejection }
WorkerInitializationFailure = EffectsRejected | InterpreterCorrupt
InitFailure = { failure: WorkerInitializationFailure, activation }
ExitEvidence =
FromInitialization {
observation, stop
}
| FromObserver {
observation, stop
}
ExitSettlement =
Observed { evidence: ExitEvidence }
| Transferred {
observation, worker: WorkerIncarnation, reason
}
WorkerDrain =
Exited { exit: ExitSettlement }
| Transferred { residual }
PreReadyStop =
DuringInit {
activation,
exit: ExitSettlement
}
| InitializedAfterExit {
permit, activation,
exit: ExitSettlement
}
| ReadyAfterExit {
attempt, proof,
exit: ExitSettlement
}
BirthDrainResult =
FoldRejected {
creation, error, definition_and_plan
}
| HostRejected {
creation, rejection, prepared_init, activation
}
| Contradiction { facts }
InitDrainWork =
Initialized { permit, activation }
| Rejected { rejection, activation }
InitDrainResult =
WorkAndExit {
work: InitDrainWork,
exit: ExitSettlement
}
| FailureAndExit {
failure: InitFailure,
exit: ExitSettlement
}
| StoppedDuringInit {
activation,
exit: ExitSettlement
}
| Transferred {
phase: InitDrainPhase,
residual
}
InitDrainPhase =
AwaitingBoth { attempt: InitAttempt }
| InitSettled {
work: InitDrainWork
}
| ExitObserved {
attempt: InitAttempt,
exit: ExitSettlement
}
ActivationDrainResult =
StartRejected { request, reason }
| ReadyAfterCancel { proof }
| RejectedAfterCancel { rejection }
ActivationDrainPhase =
AwaitingBoth { phase: ActivationPhase }
| ActivationSettled { result: ActivationDrainResult }
| ExitObserved {
phase: ActivationPhase,
exit: ExitSettlement
}
An attempt is neither an address nor proof of freshness. A creation becomes an installed incarnation only when the interpreter commits fresh host ownership. All correlation values are exact and non-reused. Two proxies issuing their first worker attempt must still produce unequal evidence; a local ordinal is diagnostic data, not identity. Sequence arithmetic, address reuse, timing, or adjacency cannot manufacture provenance.
The proxy never publishes WorkerRecipient or the private
WorkerIncarnation product. It alone routes service through
worker.recipient. Parent outcomes project only
WorkerIncarnationEvidence; the stable proxy remains the sole externally
routable service capability.
Every payload in this model is complete and owned. Field names therefore use
domain words such as worker, request, rejection, stop, and exit
without repeating complete_ or structural implementation phrases.
Complete state sum
Each variant owns exactly the values listed. kind is the closed BirthKind
sum, not an optional predecessor.
Dormant
no installation has ever been attempted
Creating {
kind, reservation: ChildReservation,
stopped: Option<ExitSettlement>
}
one complete worker definition and activation plan have moved into the
staged creation action and its unresolved settlement; `stopped` is exactly
absence or one authoritative pre-birth stop
Initializing {
kind, worker: WorkerIncarnation, attempt: InitAttempt,
stopped: Option<ExitSettlement>
}
installation committed; the lifecycle host owns unresolved initialization
settlement and the activation plan under this exact correlation; the proxy
retains at most one already-arrived exact stop
Activating {
kind, worker: WorkerIncarnation,
attempt: ActivationAttempt, phase: ActivationPhase,
stopped: Option<ExitSettlement>
}
one exact permit and plan were consumed by one `BeginActivation` action;
stop absence/presence remains current input to the readiness decision
Ready { worker: WorkerIncarnation }
the one routable exact current worker
EmptyInitial
the initial operation failed before installation committed
EmptyAfter { previous: WorkerIncarnation }
a previously installed incarnation is terminally settled
StoppingForReplacement {
current: WorkerIncarnation,
next: ChildReservation,
replacement
}
the predecessor is being drained; the successor definition has not moved
into creation yet
DrainingCreation { kind, reservation: ChildReservation }
shutdown owns an unresolved creation settlement
DrainingCreationAfterExit {
kind, reservation: ChildReservation,
exit: ExitSettlement
}
shutdown owns creation settlement and already has the pre-birth exit
DrainingInitFailure {
kind, worker: WorkerIncarnation, failure: InitFailure,
completion: DrainCompletion
}
DrainingInit {
kind, worker: WorkerIncarnation, phase: InitDrainPhase
}
DrainingActivationFailure {
kind, worker: WorkerIncarnation, attempt: ActivationAttempt,
failure: ActivationFailure, completion: DrainCompletion
}
DrainingActivation {
kind, worker: WorkerIncarnation, attempt: ActivationAttempt,
phase: ActivationDrainPhase
}
DrainingWorker { worker: WorkerIncarnation }
one shutdown/worker-exit observation remains unresolved
Stopped
terminal; no mailbox event is admitted
EmptyInitial and EmptyAfter are distinct because only the latter has
replacement provenance. The drain variants contain closed work/cause and join
sums; they do not coordinate optional fields, cross-product flags, or
already-settled live combinations.
At most one of Initializing, Activating, Ready,
StoppingForReplacement, DrainingInit*, DrainingActivation*,
or DrainingWorker can exist, proving the installed-incarnation bound
structurally. Creating and DrainingCreation* own no installed capability.
The only empty-state projections are explicit:
PreBirthEmpty(Initial) = EmptyInitial
PreBirthEmpty(Replacement { replaces }) = EmptyAfter { previous: replaces }
PostBirthEmpty(worker) = EmptyAfter { previous: worker }
Therefore an initial attempt that committed an incarnation and then stopped or
failed cannot return to EmptyInitial.
Complete input sum
There are three ingress lanes. They are distinct protocols, even where the table presents them together.
Public service input
Service { sender, command }
Private owner control input
InstallInitial { definition, activation }
Replace { definition, activation }
Shutdown
Only the established structural owner can construct the control capability. Clients can construct only the worker's concrete service protocol.
Lifecycle facts
BirthResult =
FoldRejected { reservation, definition_and_plan, error }
| HostRejected {
reservation, behavior, init_actions, activation, reason
}
| Committed {
reservation, worker: WorkerIncarnation, init: InitAttempt
}
InitResult =
Initialized {
worker: WorkerIncarnation, init: InitAttempt,
permit: ActivationPermit, activation: ActivationPlan
}
| EffectsRejected {
worker: WorkerIncarnation, init: InitAttempt,
failure: WorkerInitializationFailure, activation: ActivationPlan
}
| Stopped {
worker: WorkerIncarnation, init: InitAttempt,
activation: ActivationPlan,
exit: ExitEvidence
}
ActivationStartResult =
Accepted { worker: WorkerIncarnation, attempt: ActivationAttempt }
| Rejected {
worker: WorkerIncarnation, attempt: ActivationAttempt,
request, reason
}
ActivationResult =
Ready { worker: WorkerIncarnation, attempt: ActivationAttempt, proof }
| Rejected { worker: WorkerIncarnation, attempt: ActivationAttempt, rejection }
| ReadyAfterCancel { worker: WorkerIncarnation, attempt: ActivationAttempt, proof }
| RejectedAfterCancel {
worker: WorkerIncarnation, attempt: ActivationAttempt, rejection
}
WorkerExit =
BeforeBirth {
reservation: ChildReservation,
evidence: ExitEvidence
}
| AfterBirth {
worker: WorkerIncarnation,
evidence: ExitEvidence
}
ProxyDiagnostic =
UnexpectedWorkerReservation { phase, result: ReservationResult }
| UnexpectedWorkerStart { phase, worker: WorkerStart }
| UnexpectedWorkerStop { phase, stopped: WorkerStop }
| UnexpectedWorkerInitialization { phase, initialization: WorkerInitialization }
| UnexpectedWorkerActivation { phase, activation: WorkerActivation }
| UnexpectedWorkerShutdown { phase, shutdown: WorkerShutdown }
Each alternative owns the complete unexpected input. phase is observable
status data, not correlation authority. The exact expected reservation,
worker, attempt, or shutdown token remains solely in the unchanged proxy state.
Requiring the diagnostic to own that token as well would either duplicate an
affine authority or make unchanged-state preservation impossible in safe Rust.
The split between activation start settlement and later activation result is
intentional. A typed Ready value injected by a test is only model input. It
is not implementation evidence.
Delivery rejection for service sends, parent reports, worker shutdowns, and logical cancellation remains lifecycle-host settlement rather than proxy mailbox input. Those settlements are still mandatory and must be witnessed in AA-06; excluding them from this input sum does not discard them or make the proxy their universal rejection owner.
The proxy's normal template report remains the solution's four-variant sum:
ParentReport =
InitialInstallation { outcome: InstallationOutcome }
| Replacement { outcome: ReplacementOutcome }
| WorkerStopped { stop }
| Unavailable { sender, phase, command }
InstallationOutcome =
Rejected { definition_and_plan, phase, reason }
| Resolved { result: WorkerStartResult }
ReplacementOutcome =
Rejected { definition_and_plan, phase, reason }
| CancelledBeforeBirth {
replaces: WorkerIncarnationEvidence,
replacement,
reason: Shutdown
}
| Resolved {
replaces: WorkerIncarnationEvidence, result: WorkerStartResult
}
WorkerStartResult =
CreationRejected {
rejection: WorkerCreationRejection,
activation,
stopped: None | ExactWorkerStop
}
| Ready { attempt: WorkerAttempt, readiness }
| Unavailable { attempt: WorkerAttempt, drain: WorkerDrain }
WorkerCreationRejection is the sole owner of the exact pre-commit cause and
returned worker value. WorkerDrain is the sole owner of the exact committed
worker failure cause and every proxy-held affine value. WorkerStartResult
classifies only the three decisions its consumer makes; it does not repeat
either nested cause as another outer alternative. stopped is present on a
creation rejection only when the proxy had already accepted the exact stop.
Every attempt and replaces field in ParentReport is opaque evidence. A
WorkerStopped report likewise contains terminal provenance and no
WorkerRecipient. The owner can correlate lifecycle without acquiring a
delivery route around the stable proxy.
Control rejection and contradiction are nested outcomes, not additional top-level parent-report variants. Shutdown completes through behavior termination and lifecycle-host settlement; it is not a fifth parent report. An owner never rebuilds one atomic outcome by joining several lower-level reports.
ProxyDiagnostic is deliberately not smuggled into that sum. It travels on
the separate model-required diagnostics lane to the
established owner. This is part of the recorded product disagreement and must
be reconciled before production authority changes.
For non-shutdown operations, the atomic parent outcome carries every
proxy-owned leftover. A creation rejection carries the complete creation
rejection, activation plan, and any already accepted exact stop. An unavailable
committed worker carries one WorkerDrain, whose exhaustive alternative owns
the exact initialization, activation, early-stop, shutdown, and stop values.
The lifecycle host separately retains the complete concrete action settlement
in the worker environment. That settlement follows the one runtime retirement
path; it is not a second Behavior outcome channel.
During shutdown there is deliberately no parent report. The lifecycle host
receives BirthDrainResult for a pre-birth behavior/host rejection
or contradiction. After commit it receives exactly one
InitDrainResult: completed
initialization work plus exact exit settlement, a stop produced
by initialization with the unstarted plan, or a forced transfer containing the
complete unresolved join and residual ownership. These are the only
initialization-leftover alternatives; none may be dropped or reconstructed.
This enriches the solution's abbreviated nested outcomes only inside the
non-production oracle and remains an AA-06 runtime proof obligation.
Initialization and activation share only WorkerStopping, whose
shutdown-result/stop law is identical. Their outer shutdown state is not one
generic product: initialization has no pending domain value, whereas activation
owns a real waiting-for-start or running value. A unit, marker, ignored field,
or fabricated initialization token may not be introduced to make those shapes
appear substitutable. Each concrete state must store only the exact work and
worker-return values that remain current.
For initialization, the current work value is exactly
Option<WorkerInitializationRetirement>: absence means the result has not
returned; presence owns the complete result while worker departure remains
unfinished. A second result cannot replace the first and is returned complete.
No separate pending/returned initialization sum is lawful. Its stopped outcome
owns Option<initialization stop> rather than an arrival-order alternative:
presence retains the initializer's independently returned stop when the
enclosing worker already owns the observed stop; absence means the initializer
stop occupies that enclosing worker slot.
WorkerExit::BeforeBirth is admissible only in a state that owns the same
ChildReservation. AfterBirth is admissible only in a state that owns its worker
incarnation. The attempt and creator-local route are
reserved, stored, emitted, compared, and retired as one private product. No
field is inferred from the other; no optional field, address inference, or
timing decides which provenance is present.
The lifecycle host, not Initializing, owns the linear InitSettlement and
ActivationPlan while initialization work is unresolved. The proxy owns only
InitAttempt. A matching InitResult transfers the exact permit/plan,
failure-classification/plan, or stopped/plan product back once while the host
retains the complete rejected or corrupt settlement for retirement. This is an intentional
falsification-model clarification of the solution's shorthand
Initializing { initialization_settlement, activation_plan }; AA-02 and
AA-06 must prove a concrete locked-boundary realization before that shorthand
can become Rust state.
Authorized readiness chain
Readiness is valid only if all of this chain is realized through the locked
Behavior -> Actions boundary:
- Fresh creation commits one exact installed incarnation, closed to service.
- Initialization actions settle successfully for that incarnation.
- Settlement issues the one-shot
ActivationPermittied to that incarnation. - The proxy reserves a fresh
ActivationAttemptand, in the same fold, consumes the permit and concrete plan into an explicitBeginActivationaction. - The activation interpreter accepts or rejects that complete action. If accepted, it—not a test harness or arbitrary client—owns the in-flight work and the unique authority able to produce the result.
- Only
Ready { worker, attempt, proof }from that accepted work can enterReady. - Rejection, stop, cancellation, a foreign incarnation, a wrong attempt, a duplicate, or a late result cannot publish readiness.
Who produces Ready: the concrete immediate or asynchronous activation
interpreter designated by the consumed permit and plan. Who authorizes it: the
exact one-shot permit obtained from successful initialization settlement. What
starts asynchronous work: the real BeginActivation item in Actions. What
correlates it: both exact incarnation and fresh activation attempt. Who owns it
while in flight: the lifecycle host's exact activation settlement record,
while the proxy owns the corresponding semantic waiting state.
The concrete capability representation and interpreter integration remain
open for AA-03 and AA-06. If they cannot be expressed without changing locked
foundations, that is a falsification result. This section must not be replaced
by a helper that mints Ready directly.
Required falsification-model effect product
The governing solution currently names these six proxy lanes:
worker_deliveries
worker_creations
worker_creation_observations
worker_stop_observations
worker_shutdowns
parent_reports
That product is insufficient for this model because the proxy itself emits two activation operations and must continue after rejecting lifecycle facts. The minimum model product therefore adds three concrete typed send lanes:
activation_begin_requests: BeginActivation
activation_cancel_requests: CancelActivation
diagnostics: ProxyDiagnostic
The resulting nine-lane product is a non-production falsification requirement, not an amendment to design authority. It records a focused disagreement with the solution's six-lane product. AA-03 may not implement activation until the solution, matrix, retained-core decision, and research audit are reconciled or an existing named lane is proven to own the same exact operation without reinterpretation.
The fold returns the real Actions, including its next behavior or
termination decision. That decision is not an extra effect lane.
Initialization settlement remains an interpreter/lifecycle-host responsibility
and is not an ambient behavior effect.
An emitted item transfers its complete value to action settlement. State retains only correlation and ownership that legitimately coexist with the emitted item. Creations still precede dependent observations and sends. The relative order of the independent named send lanes remains an interpreter obligation; AA-06 must prove the selected order rather than infer it from product position.
Creation must commit before same-action sends or observations that depend on
the new child. Initialization settlement precedes BeginActivation.
Replacement shutdown precedes successor creation because the successor
definition remains in StoppingForReplacement until the predecessor's exact
worker exit. No row emits both service delivery and unavailability.
Ownership table
| Value | Owned before acceptance | Owned after action emission | On rejection or final settlement |
|---|---|---|---|
| Initial/replacement definition and plan | incoming owner-control value, or StoppingForReplacement while predecessor drains | complete staged creation item/settlement | returned complete by typed control or creation rejection; never reconstructed |
| Child reservation | proxy state and matching birth/exit observation contracts | the attempt and creator-local route travel as one private product | consumed once by matching result; a mismatched attempt or route is rejected complete and neither is inferred |
| Worker incarnation | interpreter host after committed birth; proxy stores the worker capability | delivery/stop actions borrow no second ownership claim; host retains lifecycle ownership | moves through a drain until exact exit settlement; never inferred from a nonce |
| Initialization actions | committed initialization settlement | concrete worker environment | every item is accepted, rejected, or not attempted; the exact settlement transfers through runtime retirement and never enters proxy state |
| Activation plan before initialization settles | staged birth settlement, then lifecycle-host initialization work | proxy stores only InitAttempt; the matching result returns the same plan with a permit, failure classification, or stop | host rejection returns it with prepared initialization; effects rejection retains it through exit drain; it is never reconstructed or silently dropped |
| Activation permit and initialized plan | exact Initialized result | consumed together by BeginActivation, or by a typed not-activated shutdown/early-stop settlement | start rejection returns the complete unaccepted request; accepted work remains host-owned |
| Activation attempt and result authority | reserved by proxy; authority minted only by accepted activation interpretation | host owns in-flight authority, proxy owns matching waiting correlation | exact result consumes once; cancellation closes publication but does not fabricate physical cancellation |
| Service sender and command | incoming Service value | exact worker delivery only in Ready, otherwise complete Unavailable parent report | delivery rejection belongs to the lifecycle host; proxy does not replay implicitly |
| Replacement definition | incoming Replace, then StoppingForReplacement | moves to creation only after exact predecessor stop | overlap returns it complete; shutdown settles it without creating a successor |
| Worker-exit observation | lifecycle host under the child reservation or worker incarnation | remains host-owned while the proxy stores correlation; one authoritative init stop or observed exit yields ExitSettlement | satisfied exactly once, or transferred to residual lifecycle ownership; a later result is diagnostic |
| Worker exit | lifecycle host until delivered in WorkerExit | consumed into an early-stop join, drain, empty transition, or one parent report | duplicates, wrong-provenance, and foreign results move to diagnostics |
| Unexpected lifecycle input | incoming lifecycle result | complete input and current phase move once to diagnostics; proxy state is unchanged | diagnostic delivery rejection is settled by the lifecycle host without recursion |
| Parent report | proxy action item until interpretation | parent-delivery settlement record | rejection never rewinds proxy state and is settled by the proxy host |
Transition notation
The total matrix uses these cells:
I0: accept initial install; reserve one fresh attempt/route product, enterCreating(Initial), and emit one fresh creation plus its observation.R0: accept replacement fromReady; reserve the complete successor attempt/route product before emitting one exact predecessor shutdown, then enterStoppingForReplacementretaining the complete successor.R1: accept replacement fromEmptyAfter; reserve the complete successor product and enterCreating(Replacement { replaces })and emit creation.C-: reject control with the complete submitted definition/plan and current phase. Repeated shutdown is handled byS=instead.D+: deliver the untouched command once to the exactReadyincarnation.U: emit oneUnavailablereport with untouched sender, command, and phase.S0: close immediately, settle any control outcome, and enterStopped.S1: close admission and enter the corresponding drain variant without duplicating an already-emitted action.S=: shutdown is already in progress; remain in the same drain state and emit no duplicate child shutdown or cancellation.BR,IR,AS,AR,WE: apply the matching-result rules below.F-: keep the same semantic state and emit exactly oneProxyDiagnosticondiagnostics; every other action lane is empty and the next decision isContinue.NA: the event is not admitted becauseStoppedis terminal. Late settlements are owned by the lifecycle host, not a stopped mailbox.
All BR, IR, AS, AR, and WE cells mean “matching exact correlation
only”; every mismatch is F-.
F- is a continuing diagnostic law, not Behavior::Error. The concrete
diagnostic alternative names the unexpected input kind, retains the complete
original input, and carries the current observable proxy phase. It does not
copy the proxy's expected correlation authority out of unchanged state.
Creating and initializing each have one public phase; their privately retained
optional stop is ownership needed by the later result, not another public work
phase.
If diagnostic delivery is rejected, the lifecycle host settles that complete
action directly and does not recursively send another diagnostic.
Reservation of both attempt and route is a pure, fallible precondition of
I0, R0, and R1. A collision or exhaustion of either component returns
the complete input through the appropriate
nested InstallationOutcome::Rejected or ReplacementOutcome::Rejected and
leaves the state unchanged. It never
emits creation and never replaces an existing binding.
Total state/event matrix
| State | Install | Replace | Service | Shutdown | Birth result | Init result | Activation start | Activation result | Worker exit |
|---|---|---|---|---|---|---|---|---|---|
Dormant | I0 | C- | U | S0 | F- | F- | F- | F- | F- |
Creating | C- | C- | U | S1 | BR | F- | F- | F- | WE |
Initializing | C- | C- | U | S1 | F- | IR | F- | F- | WE |
Activating | C- | C- | U | S1 | F- | F- | AS | AR | WE |
Ready | C- | R0 | D+ | S1 | F- | F- | F- | F- | WE |
EmptyInitial | C- | C- | U | S0 | F- | F- | F- | F- | F- |
EmptyAfter | C- | R1 | U | S0 | F- | F- | F- | F- | F- |
StoppingForReplacement | C- | C- | U | S1 | F- | F- | F- | F- | WE |
DrainingCreation | C- | C- | U | S= | BR | F- | F- | F- | WE |
DrainingCreationAfterExit | C- | C- | U | S= | BR | F- | F- | F- | F- |
DrainingInitFailure | C- | C- | U | S1/S= | F- | F- | F- | F- | WE |
DrainingInit | C- | C- | U | S= | F- | IR | F- | F- | WE |
DrainingActivationFailure | C- | C- | U | S1/S= | F- | F- | F- | F- | WE |
DrainingActivation | C- | C- | U | S= | F- | F- | AS | AR | WE |
DrainingWorker | C- | C- | U | S= | F- | F- | F- | F- | WE |
Stopped | NA | NA | NA | NA | NA | NA | NA | NA | NA |
The matrix is exhaustive over the complete input categories. Nested outcome rules below make each matching cell exhaustive over its result sum.
Shutdown expansion (S0, S1, and S=)
Dormant,EmptyInitial, andEmptyAfterown no unresolved child work; they return the terminal behavior decision and transfer shutdown settlement to the lifecycle host (S0); no parent report is fabricated.Creatingwith no stop becomesDrainingCreation; with a stop it becomesDrainingCreationAfterExit. The already-transferred creation is not reconstructed or physically cancelled.Initializingwith no stop becomesDrainingInit(AwaitingBoth)and initiates exact worker drain. With a stop it usesExitObservedand emits no duplicate worker shutdown.ActivatingbecomesDrainingActivation, preserving itsActivationPhaseinAwaitingBothwhen no stop is present, logically closes readiness publication, and emits the exact cancellation/drain actions. With a stop it preserves the exit inExitObservedand emits no duplicate worker shutdown.ReadybecomesDrainingWorkerand emits one exact worker shutdown plus its worker-exit observation.StoppingForReplacementsettles the still-local replacement definition as a nestedReplacementOutcome::CancelledBeforeBirth, becomesDrainingWorkerfor the predecessor, and does not create the successor.- Shutdown in
DrainingInitFailureorDrainingActivationFailurechangesDrainCompletionfromReturnToEmptytoStopProxy. It does not lose the complete failure and emits no duplicate child drain (S1). If the completion is alreadyStopProxy, shutdown isS=. - All shutdown-owned drain states use
S=. They retain their exact state and emit no duplicate cancellation, shutdown, or observation.
Matching-result rules
Birth result (BR)
- In
Creatingwithout a stop,FoldRejectedorHostRejectedemits the corresponding complete initial/replacement outcome and entersEmptyInitialforInitialorEmptyAfter { previous: replaces }forReplacement.CommittedentersInitializingwith the exact initialization correlation while the lifecycle host retains the linear settlement and plan. - In
Creatingwith a stop, rejection produces oneContradictioncontaining both complete authoritative inputs, then enters the pre-commit empty state. Commit entersInitializingcarrying that stop; it cannot publish readiness. - In
DrainingCreation, rejection settles shutdown and entersStopped.FoldRejectedandHostRejectedtransfer their complete values through the correspondingBirthDrainResultvariant. Commit entersDrainingInit(AwaitingBoth); exact initialization and terminal ownership must both settle. - In
DrainingCreationAfterExit, rejection produces the complete contradiction, transfers it asBirthDrainResult::Contradiction, and entersStopped. Commit entersDrainingInitwith the retained exit inExitObserved.
No creation rejection can coexist with a successful birth report. A committed creation is never reclassified as rejected because later initialization fails.
Init result (IR)
- In
Initializingwithout a stop, only a result matching both worker andInitAttemptis admissible.Initializedreserves a fresh activation attempt and, in the same fold, emits the exactBeginActivationaction and entersActivatingwithAwaitingStartSettlement.EffectsRejectedentersDrainingInitFailurewith both proxy-owned values returned by the result—the closed failure classification and still-unstarted activation plan—then initiates exact drain.Stoppedsettles its returned plan as never activated and satisfies the outstanding stop observation with that same authoritative result. Itsexitfield must beExitEvidence::FromInitialization. It emitsStoppedBeforeReadycarryingPreReadyStop::DuringInitplus the correspondingExitSettlement::Observed, then entersEmptyAfter { previous: worker }. - In
Initializingwith a stop, every outcome settles initialization without beginning activation. Only the exact initialization attempt is admissible.InitializedemitsPreReadyStop::InitializedAfterExitcarrying its returned permit/plan and the already-satisfied worker-exit observation.Stoppedreconciles its exact stop with the stored exit and requiresexit = ExitEvidence::FromObserver, then emitsDuringInitwith the correspondingObservedsettlement; it does not settle the observation twice.EffectsRejectedcombines its failure classification and returned plan with the same exit settlement. The lifecycle host retains the complete action settlement. None can emitReady; all enterEmptyAfter { previous: worker }. DrainingInitFailurealready owns a settled rejection. It waits only for the exact worker exit. WithReturnToEmpty, it emits oneInitEffectsRejectedparent outcome carrying the failure classification, unstarted plan, and exit settlement. WithStopProxy, it transfers exactlyInitDrainResult::FailureAndExitinstead. Shutdown changes only that completion and never discards either value.- In
DrainingInit, an exact init result changesAwaitingBothtoInitSettledand stores one exhaustiveInitDrainWork, including the permit/plan or failure-classification/plan pair. FromExitObserved, the same result closes the join by transferringWorkAndExitto the lifecycle host and entersStopped.InitResult::Stoppedcloses directly asStoppedDuringInit: fromAwaitingBothit satisfies and retires the exact outstanding observation with the same authoritative stop; fromExitObservedit reconciles the already-satisfied observation and does not settle it twice. A separate worker exit changesAwaitingBothtoExitObserved, or closesInitSettledasWorkAndExit. A foreign correlation emitsProxyDiagnostic; two authoritative inputs with the same correlation but conflicting stop data emit the atomicContradictionretaining both. No live state represents both obligations settled.
The exact host ordering used to interpret and settle initialization actions is an open foundational realization. These rules state the ownership constraint; they do not claim that AA-01 implements it.
Activation start (AS) and result (AR)
- In
Activating(AwaitingStartSettlement)without a stop, exact start acceptance changes onlyphasetoInFlight; the host then owns work and result authority. Exact start rejection entersDrainingActivationFailurewith the complete unaccepted request and initiates exact drain. - An activation result is authorized only in
Activating(InFlight). ExactReadyentersReadyand emits the initial/replacement readiness outcome. Exact rejection entersDrainingActivationFailure, retains the complete rejection, and initiates exact drain. A result received before start acceptance isF-; the activation interpreter must causally publish acceptance before releasing its result authority. Cancellation-qualified results in a non-cancelling state areF-and cannot be reinterpreted. - In
Activatingwith a stop, exact start acceptance changesphasetoInFlight; exact start rejection emits its complete atomic outcome with the retained stop. An authorized exact result inInFlightemitsPreReadyStop::ReadyAfterExitforReady, carrying the attempt, authority proof, and worker-exit settlement. A rejection emits the complete activation-rejection outcome with the same exit settlement. Every case entersEmptyAfter { previous: worker }; none publishes readiness. DrainingActivationFailurealready owns a settled activation failure. It waits only for the exact worker exit, then emits the complete failure outcome. Shutdown changes its drain completion toStoppedwithout discarding failure provenance.- In
DrainingActivation, matching start acceptance changes the activation phase insideAwaitingBothorExitObservedfromAwaitingStartSettlementtoInFlight. Matching start rejection changesAwaitingBothtoActivationSettled, or closesExitObservedand stops. An exact late result fromInFlightfollows the same two transitions. Neither ordinaryReadyracing with cancellation norReadyAfterCancelis published. A result before start acceptance isF-under the causal interpreter policy; a result afterActivationSettledis a duplicate. OrdinaryReady/Rejectedracing the logical close are normalized into the corresponding after-cancel settlement without publishing availability. An exact stop changesAwaitingBothtoExitObserved, or closesActivationSettledand stops. No live state represents both obligations settled.
The causal acceptance-before-result rule is an explicit Bombay interpreter
policy and an AA-06 proof obligation, not a general actor-model guarantee. If
the real boundary cannot enforce it, AA-03 must replace ActivationPhase
with a truthful closed out-of-order join before production code exists.
Worker exit (WE)
Creatingwith no stop accepts onlyBeforeBirthwith its child reservation, stores that exact stop, and still waits for the creation result to decide whether a birth committed.DrainingCreationsimilarly becomesDrainingCreationAfterExit.- Every
WorkerExitmust carryExitEvidence::FromObserver; initialization exit evidence provenance on this ingress is wrong-kind and followsF-. An accepted exit is stored or emitted only after wrapping that delivered value inExitSettlement::Observed. InitializingandActivatingwith no stop accept onlyAfterBirthwith their worker incarnation and store that exact stop. A second stop isF-.Readyemits one completeWorkerStoppedreport and entersEmptyAfter { previous: worker }.StoppingForReplacementemits the predecessor stop report and, in the same fold, transfers the retained successor through the already-reserved complete attempt/route product into the fresh creation action, enteringCreating(Replacement { replaces })with that same reservation.- In any
DrainingInit*,DrainingActivation*, orDrainingWorkerstate, onlyAfterBirthwith the owned worker incarnation can advance. It either completes the failure drain, recordsExitObserved, or closes a shutdown join whose work is already settled. A second stop isF-. Final outcome waits for any still-owned settlement. DrainingWorkerconsumes the exact stop, returns the behavior's terminal decision, and transfers final shutdown settlement to the lifecycle host. It emits no invented parent-report variant.
Overlap, stale input, and outcome laws
- A second install or an install after
Dormantis a typed rejection that returns its complete definition and plan. - A replacement is accepted only in
ReadyorEmptyAfter. Every overlap returns the complete submitted successor. - Every non-ready service command produces exactly one complete
Unavailablereport. No retry or implicit replay is promised. - A lifecycle fact can advance only the state owning its exact worker and attempt and expecting that fact kind. Wrong-kind facts are not reinterpreted.
- Duplicate stop, creation, initialization, activation-start, activation
result, and readiness inputs preserve proxy state and emit exactly one closed
ProxyDiagnosticretaining the complete input. - Parent-report delivery rejection never rewinds the transition. The lifecycle host settles the complete rejected report.
- A pre-readiness failure after committed installation is not reported as final until the exact incarnation and all transferred initialization or activation work are settled or transferred to terminal residual ownership.
First-slice non-features and proof obligations
AA-01 intentionally does not provide:
- Rust proxy types, a fold, an executable local algebra, or test helpers;
- the real initialization fold/settlement implementation and its exact host ordering;
- a selected Rust representation for
ActivationPermit, result authority, out-of-order activation joins, or asynchronous work; - accepted and rejected interpreter traces;
- delivery settlement/rejection realization for service or parent reports;
- deadline expiry, forced retirement, root residual settlement, or leak-free late-work ownership;
- wrapper composition or initialization-order proof;
- catalogue parity, builder syntax, compiler/DevX acceptance, or migration;
- edits to the locked kernel, legacy actors, macros, testkit, or interpreters.
Consequently this document is ready only to guide AA-02’s focused failing
regressions and to be compared against production authority. It is not
implementation evidence. “Ready for comparison and policy enrichment” later
requires real Behavior/Actions integration, accepted and rejected
interpreter traces, applicable catalogue parity, wrapper-composition proof,
complete shutdown/residual ownership, and compiler and DevX acceptance.
If AA-02 through AA-06 cannot realize any table cell through the locked boundary, the experiment records that focused counterexample. It must not invent a parallel actor contract or silently weaken the table.
Fixed-supervisor falsification model
Status and authority
This document is the AA-10 independent semantic model for one fixed
supervisor. It is a non-production falsification oracle. It does not select
Rust types, define another behavior algebra, implement a supervisor, or mark a
row in docs/engineering/atomic-actor-solution.md implemented. That document remains the
production design authority.
AA-10 is blocked, not complete. The state and transition oracle requires typed child terminal/drain settlement to return an emitted install or replacement when the proxy terminates without its normal outcome, but the locked boundary has no implementation witness for that transfer yet.
Fresh allocation is the actor-model law used here: every stable proxy and every worker incarnation behind it is freshly established rather than written over an existing address. Creator-local child reservations, commit before dependent effects, exact installed capabilities, action settlement, activation authorization, and actor-graph drain are Bombay derivations or policy choices. Ordered semantic roles, stable proxies, recovery policy, and fixed-topology failure reaction are template laws.
Runtime route selection is owned normatively by
docs/atomic-runtime-settlement.md. The supervisor issues and stores only a
CreationId for each proxy creation; it never chooses or stores a runtime route
before emitting CreateChild. Any older reservation label below denotes that
creator-visible correlation before interpretation and routed evidence only in
the returned runtime settlement. It does not authorize a separate route
request, waiting state, or public route.
The model must falsify designs that make any of these claims false:
- The roster is non-empty, ordered, and contains each semantic role once.
- Each live role owns at most one exact stable proxy.
- The supervisor consumes one atomic proxy outcome; it never reconstructs worker creation, initialization, activation, or readiness.
- A role is advertised ready only after its exact proxy reports a successful initial or replacement outcome.
- The configured positive activation bound limits unresolved proxy install/replace operations across the complete supervisor.
- A recovery admission is selected from one immutable roster snapshot. The membership partition, prepared replacements, fallible correlations, and budget charge commit in one fold; failure before that fold changes none of them. Later cross-actor realization resolves each admitted participant independently.
- Recovery timing, readiness, and activation authorization are independent prerequisites joined without flags.
- Stale, duplicate, foreign, wrong-kind, and contradictory facts cannot advance a member or charge recovery twice.
- Query is a total projection and cannot mutate topology, budget, recovery, authorization, or lifecycle ownership.
- Shutdown closes recovery and drains every owned or still-creating proxy exactly once while leaving unrelated application children untouched.
- Every accepted input produces one next state and one complete named action product. No effect is ambient.
“Atomic” means one local Behavior fold commits one supervisor state and its
complete Actions. It does not claim atomic delivery, creation, or work across
actors.
Model vocabulary
The following names describe the oracle. They are not proposed public Rust names.
Role = one semantic member label, unique inside this supervisor
RoleOrder = the declaration order retained for the supervisor lifetime
ProxyBirthAttempt = fresh non-reused CreationId issued by the supervisor
RoutedProxyBirth = interpreter-private { attempt: ProxyBirthAttempt, route }
ExactProxy = externally routable exact capability for one committed stable proxy
WorkerAttempt = opaque non-routable evidence for one started worker
OperationKind =
Initial
| Replacement { recovery: RecoveryTicket, replaces: WorkerAttempt }
OperationTicket = one affine ID paired with one private supervisor witness
ProxyControl =
Start { definition, activation }
| Replace { definition, activation }
OperationInput = {
ticket: OperationTicket,
kind: OperationKind,
control: ProxyControl,
}
RecoveryTicket = fresh non-reused correlation for one complete recovery decision
RecoveryOrdinal = checked one-based lifetime recovery count of the triggering role
TimerCorrelation = { timer, generation }
RecoveryParticipant =
Stopped {
previous: WorkerAttempt,
stop,
}
| Online { current: WorkerAttempt }
| AwaitingInitial { phase: InitialPhase }
FailureReaction = RetireMember | StopSupervisor
ActorDrainPolicy =
WaitForActorGraph
| RetireActorGraphAfter { deadline }
DiagnosticDisposition =
DeliverTo { route }
| Terminate
LifecyclePublication =
NotPublished
| Published { route }
An attempt, ticket, timer generation, role, or child route is correlation, not actor identity or evidence of fresh allocation. Only committed creation yields an exact installed proxy. Sequence arithmetic, address reuse, timing, or adjacency cannot manufacture provenance.
Every payload named below is complete and owned. A state may retain an exact correlation after its value has moved into an action, but it cannot retain a second ownership claim on the moved value.
WorkerIncarnationEvidence is correlation and lifecycle provenance, not a
recipient or transferable delivery capability. The proxy alone retains the
routable worker capability. The only service capability the supervisor may
publish or return is ProxyIncarnation, preserving the stable proxy as the one
public communication path for the role.
OperationKind is supervisor settlement metadata. Only the matching concrete
ProxyControl value is delivered to AA-01's private control protocol; the
fixed supervisor does not widen the proxy message with its recovery ticket.
The action settlement owns the metadata and control as one staged source-side
product so rejection can return both without asking the proxy to echo either.
Construction domain
Worker-preparation correction
The original draft stored one callable factory and instructed the recovery
transition to invoke it. That representation is rejected: application execution
is not part of a pure Behavior transition. A callable used to prepare the
initial roster is consumed before the supervisor exists and is not stored in the
successful behavior.
Automatic recovery instead emits one typed batch worker-preparation action. Its
accepted start settlement proves only that source work began. Its later typed
result must return the complete worker-source authority, the ordered selected
immutable role names, every WorkerSubmission, any exact rejection, and every
untouched role name.
Bombay interprets the statically selected capability; FixedSupervisor owns
selection and the later state change. No callback, registry, erased response, or
runtime lookup exists in Behavior.
H38 selects the typed batch action rather than a mandatory factory actor. The actor alternative adds a delivery settlement and later reply join; delivery acceptance cannot mean workers were prepared. The action uses H35 source settlement directly. Bombay will implement its exact static interpreter and retirement transfer through the required generic runtime contract. The atomic implementation targets that contract and does not wait for the present Bombay API shape. The old callable instruction below is not an alternate accepted path.
The source contract is a method-free static declaration over the concrete
role, worker, and activation-plan types. One non-empty action owns the complete
selected immutable-name batch; it never owns the unique member-role authorities,
which remain in the pending recovery. Bombay observes each name only as
&Role. Its late result is exactly a complete prepared group, the prepared
prefix plus a worker rejection and untouched suffix, or a source rejection
before the first submission. Corruption and no-attempt remain generic start
settlement alternatives; FixedSupervisor must not repeat them in another sum.
One complete definition contains:
FixedDefinition = {
initial_factory: ConstructionOnly,
roles: OrderedNonEmptyUnique<Role>,
activation,
activation_limit: PositiveMaximum,
recovery: {
eligibility,
worker_source: OnlyWhenAutomatic,
strategy,
budget,
timing,
},
failure_reaction: FailureReaction,
actor_drain: ActorDrainPolicy,
diagnostics: DiagnosticDisposition,
lifecycle: LifecyclePublication,
}
Duplicate roles, an empty roster, invalid timing, a non-positive activation limit, and a rejected initial factory call are construction failures. The initial factory prepares every initial definition before the supervisor exists or any initialization action is emitted. A failure returns all still-owned inputs and identifies the exact role; it cannot leave a partially constructed behavior. Success drops the callable before producing the behavior.
Replacement definitions arrive only through the typed batch action above. A closed heterogeneous worker sum is valid only when every variant exposes one common concrete public protocol. Different public protocols are different supervisors, not one erased envelope.
The canonical recovery value is constructed as
Recovery::permanent(source, strategy, limit, release),
Recovery::transient(source, strategy, limit, release), or
Recovery::temporary(). The first two variants own the concrete source that
crosses the worker-preparation action. The temporary alternative has no source,
strategy, limit, or release field and requires no placeholder or annotation.
Lifecycle publication is genuinely optional. NotPublished constructs no
event and requires no dummy route. Diagnostics remain mandatory through the
independent DeliverTo | Terminate choice.
Every selected route retains its concrete logical, established, or mixed capability form and its corresponding static hosting obligations. The model does not normalize those routes into one runtime-selected envelope.
AA-10 deliberately selects no builder, typestate marker, callback, trait, or finished behavior spelling.
Normalized supervisor state
SupervisorState =
Operating {
definition,
fleet: FixedFleet,
budget: BudgetState,
}
| Draining {
definition,
fleet: DrainingFleet,
budget: BudgetState,
deadline: DeadlinePhase,
}
| Stopped
FixedFleet is one order-preserving partition of the declared roles. Each role
has exactly one topology authority, held either by an independent Member or
by one participant in exactly one RecoveryBatch. The immutable role value is
allocated once. Lifecycle events, diagnostics, and an emitted worker-preparation
action may retain read-only role names, but those names cannot select, move,
recover, or retire a member. The application-facing values expose &Role, not
the private shared representation.
RecoveryBatch and its
participants are one dependent semantic value: there is no separately mutable
member map plus batch map whose flags may disagree. A later Rust experiment
must find a concrete representation that preserves this equation.
The configured activation maximum is immutable. Occupancy is not a second
counter: every occupied slot is owned by exactly one initial operation or one
replacement whose input or proxy outcome is still pending. An exact proxy
outcome releases the slot even when the predecessor stop is still outstanding.
Capacity is the count of those exact unresolved operations. Waiting roles are
discovered from RoleOrder, so a separate queue cannot disagree with member
state.
The budget stores only committed evidence inside its active inclusive window. It is bounded by the configured maximum; a maximum of zero stores no admitted charge and denies every otherwise eligible decision.
Complete independent-member sum
Member =
CreatingProxy {
role,
reservation: ProxyReservation,
initial: OperationInput,
}
| CreatingProxyAfterExit {
role,
reservation: ProxyReservation,
initial: OperationInput,
exit,
}
| Starting {
role,
proxy: ProxyIncarnation,
phase: InitialPhase,
}
| Online {
role,
proxy: ProxyIncarnation,
worker: WorkerIncarnationEvidence,
}
| Empty {
role,
proxy: ProxyIncarnation,
previous: WorkerIncarnationEvidence,
cause,
}
| Stopping {
role,
proxy: ProxyIncarnation,
cause,
phase: ProxyStopPhase,
}
| Retired {
role,
cause,
}
InitialPhase =
WaitingForAuthorization { input: OperationInput }
| Dispatched {
ticket: OperationTicket,
settlement: InputSettlementCorrelation,
}
| AwaitingOutcome { ticket: OperationTicket }
ProxyStopPhase =
ShutdownDispatched { settlement: ShutdownSettlementCorrelation }
| AwaitingExit
| Rejected { rejection }
Starting owns no worker incarnation. Its unresolved operation kind is always
Initial. Empty is possible only after one installed worker has stopped or
a replacement attempt against such a predecessor has failed. Failure before
any worker installation is irrecoverable for the current proxy contract and
therefore enters Stopping or whole-supervisor drain; it cannot manufacture
an Empty predecessor.
Stopping preserves a rejected shutdown reason while still waiting for exact
proxy exit. Rejection is not reclassified as a successful stop. Under
RetireActorGraphAfter, the deadline may transfer the remaining ownership to
the lifecycle host; under WaitForActorGraph, the member can wait forever.
The accepted-stop carrier owns the unchanged other members and one complete stopped member. Recovery eligibility borrows the stopped member's terminal outcome when choosing policy; it does not store a second normal/abnormal label beside the outcome that already determines it.
Recovery partition
An admitted recovery batch owns all selected member-role authorities as one value. Its preparation action may temporarily own read-only names for those same roles, never another topology authority:
RecoveryBatch = {
ticket: RecoveryTicket,
trigger: RosterPosition,
release: RestartReleaseState,
members: Vec<RecoveryMember>,
}
RestartReleaseState =
Ready
| Scheduling { correlation: TimerCorrelation, request_settlement }
| Waiting { correlation: TimerCorrelation }
RecoveryMember =
Waiting { participant, prepared_replacement }
| Replacing { participant_identity, replacement: WorkerReplacement }
WorkerReplacement = {
previous_worker,
readiness,
stopped: None | Some { complete_stop },
response: ReplacementResponse,
}
ReplacementResponse =
InputPending { witness }
| OutcomePending { operation }
| OutcomeReturned { complete_outcome }
| InputRejected { complete_settlement }
RosterPosition is assigned once at roster construction and moves with the
unique member authority. Admission proves that members is non-empty, contains
each selected position once, and contains trigger. OneForOne selects one
position, OneForAll selects every eligible position, and RestForOne selects
the trigger position and its eligible suffix. Physical collection placement is
not declaration order.
The trigger position remains after that participant returns independently. A
return removes exactly one position from members and restores one independent
roster owner; the recovery retires only when members becomes empty. This is
the same one-collection ownership equation as H32/H32r. It needs neither a
parallel recovery table nor a split recovery when a middle participant returns.
Every participant owns exactly one prepared replacement until it is emitted.
After emission, WorkerReplacement stores two independent current values: the
optional complete predecessor stop and the proxy operation's current response.
An online participant has no stop; an already-stopped participant has one. The
response changes from input pending to outcome pending only after the exact
receipt, then to the complete returned outcome. Exact input rejection instead
stores the complete settlement without erasing an already-owned stop. Operating
recovery consumes a completed or rejected product immediately; shutdown may
retain the same product while proxy retirement remains independent.
An AwaitingInitial subject owns the complete InitialPhase; the enclosing
Waiting phase separately owns exactly one prepared replacement. It cannot
issue replacement until the initial operation atomically reports a ready
worker, providing the exact predecessor. Initial failure removes that
participant through the configured topology-failure reaction; it never treats
an initial-empty proxy as replaceable.
The stopped trigger begins with Some(complete_stop) and input pending; an
online coordinated peer begins with no stop and input pending. A matching peer
stop fills only the optional stop and cannot open another recovery decision. If
the replacement outcome arrives first, OutcomeReturned retains it until the
exact stop report or its typed delivery settlement closes the reunion. Thus
stop and replacement outcome are order-independent and each is accepted once.
StableProxy and FixedSupervisor own different work here. StableProxy owns the
worker shutdown-resolution and successor-start sequence and emits the exact
predecessor WorkerStopped report before its later replacement outcome.
FixedSupervisor owns admission of those two owner reports, lifecycle
publication, recovery capacity, and the rule that replacement readiness is not
published until both reports have been admitted. The supervisor never repeats
StableProxy's worker-control sequence.
The triggering worker's stop classification is consumed when recovery
disposition is chosen; its immutable roster position remains as the recovery's
trigger identity.
RecoveryOrdinal is consumed when the release is calculated. The committed
budget charge remains solely in BudgetState, where the next admission needs
it. None is copied into RecoveryBatch. Immediate release and an accepted exact
timer both produce RestartReleaseState::Ready; their arrival history cannot
change a later decision. Schedule rejection transfers the exact rejected
request directly into the configured failure reaction and therefore is not a
stored release phase.
Readiness is derived from the participant's current worker ownership, timing
from the batch's current RestartReleaseState, and authorization from current
activation occupancy. There is no stored cross-product of these prerequisites.
The transition that sees all three immediately emits the replacement input and
stores one WorkerReplacement with exact stop absence or presence and
InputPending; a ready-to-issue phase is never committed between turns.
Recovery batches may overlap in time only when their selected role sets are disjoint. Exact recovery tickets distinguish them. A role already in any batch, stopping, retired, or awaiting an admitted replacement is not selectable by another batch.
Complete input sum
Inputs arrive through distinct concrete protocol lanes.
Management input
Management =
QueryStatus { reply_to }
| QueryCapability { role, reply_to }
| Shutdown
Query routes are temporary reply capabilities. They never become durable lifecycle or diagnostic owners.
Stable-proxy lifecycle input
ProxyBirthResult =
FoldRejected { reservation, proxy_definition, error }
| HostRejected { reservation, proxy, init_actions, reason }
| Committed { reservation, proxy: ProxyIncarnation }
ProxyInputResult = ActionItemResult<
ProxyOperation,
ProxyInputReceipt { proxy: ProxyIncarnation, ticket: OperationTicket },
ProxyInputRejection,
Never,
>
ProxyReport =
InitialInstallation { outcome }
| Replacement { outcome }
| WorkerStopped { stop, observed_at }
| Unavailable { sender, phase, command }
ProxyExit =
BeforeBirth { reservation: ProxyReservation, exit }
| AfterBirth { proxy: ProxyIncarnation, exit }
ProxyShutdownSettlement =
Accepted { proxy: ProxyIncarnation, correlation }
| Rejected { proxy: ProxyIncarnation, correlation, request, reason }
The generic result owns whether interpretation was attempted. Acceptance owns
only the receipt promised by the request. Rejection and corruption return the
complete ProxyOperation; an unattempted result retains it unchanged. There is
no supervisor-specific result sum or conversion seam.
ProxyReport is always accompanied by its exact child-source capability. The
source capability, expected pending operation kind, and the replacement
outcome's nested exact replaces: WorkerIncarnationEvidence together provide
report correlation. That evidence must equal the participant's retained prior
evidence. An initial operation occurs at most once for one exact proxy, and each
later replacement names a fresh predecessor, so a delayed earlier report
cannot satisfy a later pending operation. The supervisor does not add its
operation or recovery ticket to AA-01's flat four-variant proxy report.
Install/replace settlement is discharged before a later report from that input,
so Dispatched -> AwaitingOutcome -> report is a causal Bombay interpreter
policy, not an inference from arrival timing.
Recovery and drain input
RestartScheduleSettlement =
Accepted { recovery: RecoveryTicket, timer: TimerCorrelation }
| Rejected {
recovery: RecoveryTicket,
timer: TimerCorrelation,
request,
reason,
}
RestartElapsed = { recovery: RecoveryTicket, timer: TimerCorrelation, observed_at }
DrainDeadlineSettlement =
Accepted { timer: TimerCorrelation }
| Rejected { timer: TimerCorrelation, request, reason }
DrainDeadline = { timer: TimerCorrelation, observed_at }
ActionSettlement = one closed lane-specific accepted, rejected, or
not-attempted settlement from the current action product
Workers and callers cannot author observed_at. The interpreter's monotonic
clock supplies it with the authoritative stop or timer fact. A regressing
clock fact is a typed rejection and cannot prune future evidence.
Unexpected input
UnexpectedInput = { input: FixedSupervisorEvent }
A mismatched input moves complete into one diagnostic outcome. The exact
expected route, proxy, operation, recovery, or timer authority remains solely
in current supervisor state. Copying it into the diagnostic would duplicate
admission authority; moving it would contradict unchanged-state preservation.
The unexpected input is not
dropped, reinterpreted, or converted into Behavior::Error merely because it
was unexpected.
Public projections
Status is a total read-only projection in declaration order:
MemberStatus =
CreatingProxy
| WaitingForActivationAuthorization
| AwaitingProxyOutcome
| Ready { proxy: ProxyIncarnation }
| Empty
| Recovering
| Stopping
| Retired
CapabilityResult =
Ready { role, proxy: ProxyIncarnation }
| Unavailable { role, phase }
| UnknownRole { submitted_role }
The projections are exact:
| Owned state | Status | Capability |
|---|---|---|
CreatingProxy* | CreatingProxy | Unavailable |
Starting(WaitingForAuthorization) | WaitingForActivationAuthorization | Unavailable |
Starting(Dispatched | AwaitingOutcome) | AwaitingProxyOutcome | Unavailable |
Online | Ready { proxy } | Ready { proxy } |
Empty | Empty | Unavailable |
any RecoveryMember | Recovering | Unavailable |
Stopping or any drain member | Stopping | Unavailable |
Retired or drained member | Retired | Unavailable |
The supervisor never reports proxy-private worker-creation, initialization, or activation phases. Query emits one management reply and leaves the complete state byte-for-byte semantically unchanged. Reply rejection belongs to action settlement and cannot mutate the snapshot that was already produced.
Production witness H121 realizes this table directly from the current roster owners. It sorts status by immutable roster position and scans capability by borrowed role equality. Logical and exact reply routes select two named lanes in the ordinary action product; no copied roster, lookup table, query actor, or template-specific interpreter exists.
Production witness H123 realizes the exact initial-failure
StopSupervisor row through the ordinary FixedShutdown transition. It emits
one failure diagnostic, stops only committed proxies, retains pending proxy
creations, and makes repeated shutdown idempotent. It adds no alternate drain
state or response product.
Lifecycle and diagnostic values
LifecycleEvent =
Started { role, proxy: ProxyIncarnation }
| Restarted {
role,
proxy: ProxyIncarnation,
recovery: RecoveryTicket,
}
| WorkerStopped { role, stop, disposition }
| Unavailable { role, sender, phase, command }
| MemberRetired { role }
RecoveryDisposition =
Ineligible
| Admitted { recovery: RecoveryTicket }
RecoveryDenialReason =
RecoveryTicketsExhausted
| RestartTimersExhausted
| RestartLimitReached { active, requested, maximum }
| ClockRegressed { previous, observed }
| RecoveryCountExhausted { admitted }
| ReleaseCalculationFailed { reason }
OperationalDiagnostic =
WorkerPreparationFailed {
trigger,
prepared,
reason,
remaining,
}
| RecoveryDenied { trigger, stop, reason: RecoveryDenialReason }
| ProxyCreationRejected { role, rejection }
| ProxyInputRejected { role, input, reason }
| ProxyOutcomeFailed { role, outcome }
| ProxyDied { role, exit }
| ProxyShutdownRejected { role, proxy, request, reason }
| UnexpectedInput { input: FixedSupervisorEvent }
| ForcedRetirement { role, residual, cause }
FactoryRejected is construction custody, not an operational diagnostic: the
construction-only callable is consumed before a successful Behavior exists.
WorkerPreparationFailed is emitted only after an operating recovery action
returns its source. It retains the prepared prefix and untouched roles but not
the reusable source authority.
Started and Restarted exist only after the exact atomic proxy-ready
outcome. They expose the stable proxy capability and no worker recipient or
worker evidence. Committed proxy creation, accepted proxy input, and a worker
creation fact hidden inside the proxy cannot fabricate either event. The
supervisor retains WorkerIncarnationEvidence privately for correlation. A
replacement failure is never Restarted.
MemberRetired reports the durable topology change and carries only the role.
The exact operational diagnostic emitted before the configured failure reaction
is the sole owner of why retirement began. Repeating a summarized cause in the
lifecycle value would create a second authority; labeling shutdown or deadline
arrival as that cause would retain transition history rather than current
topology. Forced actor-graph retirement remains a distinct runtime-custody
diagnostic and does not fabricate MemberRetired.
RecoveryDenied is the sole observable owner of both the triggering stop and
the exact denial reason. Repeating the reason in lifecycle output would create
a second semantic cause. A rejected recovery therefore constructs no
WorkerStopped lifecycle event: lifecycle owns ineligible and admitted stops;
the operational diagnostic owns rejected recovery.
The Rust realization uses this one RecoveryDenialReason sum in restart
admission and stores that same value with the complete triggering worker stop
in RecoveryDenied. Public inspection borrows both values; a second private
six-way sum or variant-for-variant diagnostic conversion is not part of the
law. The post-denial topology retains a stop-less trigger and the unissued
prepared workers separately. A later restart-schedule rejection is different:
its diagnostic owns the rejected timer request, so topology continues to own
the worker stop. Both paths use the same topology reaction only after that
ownership distinction has been made.
If lifecycle publication is NotPublished, lifecycle events are not built and
no discard operation occurs. Diagnostics always follow the selected
disposition. DeliverTo emits one delivery; rejection becomes one terminal
UndeliverableDiagnostic. Terminate emits the diagnostic directly to
terminal settlement and selects Stop after preserving every other action in
the turn. Neither path recursively diagnoses diagnostic rejection.
Required semantic action product
The governing solution names these fixed-supervisor lanes:
proxy_creations
proxy_creation_observations
proxy_stop_observations
proxy_install_inputs
proxy_replacement_inputs
proxy_shutdowns
worker_preparations
restart_schedules
lifecycle_events
management_replies
terminal_diagnostics
delivery_rejections
This is the semantic lane inventory, not a second interpretation-order
specification. The generic ordering rule is owned by
atomic-runtime-settlement.md:
among the currently retained fixed-supervisor lanes, worker preparation
precedes proxy operations, which precede restart scheduling, lifecycle
publication, management replies, and diagnostics. Adding a missing lane must
preserve that relative order and the creation-dependency rules above.
The oracle requires those operations plus a truthful representation of a
route-delivered operational diagnostic. terminal_diagnostics can represent
Terminate but cannot by itself represent DeliverTo(route), and
lifecycle_events cannot own diagnostics because lifecycle publication is
optional. Until an existing concrete product is proven to carry both choices
without reinterpretation, the minimum model product treats this as one closed
semantic lane:
diagnostics =
Deliver { route, diagnostic }
| Terminal { diagnostic }
delivery_rejections denotes the closed interpreter settlement product, not a
catch-all message envelope. An input rejected by the supervisor becomes an
OperationalDiagnostic::UnexpectedInput; rejection of an emitted action
arrives through its lane-specific settlement. These are different laws.
This diagnostic-product mismatch is a falsification finding, not a production API amendment. A later executable task must reconcile the solution, matrix, retained-core decision, and research audit before adding a production lane.
The fold still returns the real Actions with its next behavior or terminal
decision. Creation commits before same-action observations or proxy input.
Within one recovery release, replacement inputs are emitted in declaration
order. Diagnostics are emitted before a terminal verdict in the same action.
The interpreter order and every accepted, rejected, or not-attempted settlement
remain focused proof obligations.
Ownership table
| Value | Before acceptance | After action emission | Rejection or final settlement |
|---|---|---|---|
| Initial definition and activation | construction, then CreatingProxy or Starting(WaitingForAuthorization) | proxy creation settlement, then exact install-input settlement | returned complete on factory/creation/input rejection; never reconstructed |
| Proxy reservation | exact member state | staged creation and observation settlement share one scoped reservation | consumed by matching birth/exit result; collision never overwrites a route |
| Exact proxy | member after committed proxy birth | inputs and shutdowns borrow its delivery authority; member retains lifecycle ownership | moves through exact stop or forced-transfer drain; never inferred from role |
| Activation slot | free capacity derived from member states | one operation ticket in a dispatched/awaiting state | released only by matching atomic proxy outcome, rejected input, or forced drain transfer |
| Prepared replacement | recovery participant | exact replacement-input settlement | returned on input rejection or transferred through drain; never cloned for retry |
| Recovery decision | local prepared candidate before admission | committed budget plus one exact RecoveryBatch | pre-commit failure changes no peer; post-commit facts consume only matching participants |
| Timer request | recovery batch | restart-schedule settlement | rejected request remains diagnostic ownership; accepted timer is consumed once by exact input |
| Worker stop | exact proxy report | recovery owner, configured lifecycle event, diagnostic, or explicit publication omission | no second copy is retained to trigger another decision; shutdown moves only a stop still required by an unresolved failure path |
| Proxy report | exact child-source lifecycle input | lifecycle/diagnostic action or state transition | wrong source/kind moves complete into one unexpected-input diagnostic |
| Query route | one management request | one management-reply settlement | rejection is owned by the supervisor host; it never becomes durable ownership |
| Lifecycle event route | selected definition | lifecycle-event settlement | rejection cannot rewind readiness/recovery and transfers outward complete |
| Diagnostic | transition-local value | diagnostic delivery or terminal settlement | one terminal non-recursive outcome on rejection |
| Delayed prepared work at shutdown | recovery batch | no later recovery action; it transfers into drain settlement | late timer is consumed stale and cannot revive the batch |
| Outstanding child drain | draining fleet | proxy shutdown/observation settlement | exact stop closes once; deadline transfers residual ownership without fabricating stop |
Transition notation
The matrices use these cells:
Q: emit the total status or capability reply and preserve state.S0: close admission, cancel un-emitted recovery work, and enter one drain.S=: shutdown is already active; preserve the drain and emit no duplicate shutdown or deadline request.PB,PE,IS,PR,PS,RS,TF,DDS,DF,SET: apply the matching proxy-birth, proxy-exit, input-settlement, proxy-report, proxy-shutdown, restart-schedule, timer, drain-deadline-schedule, deadline, or lane-settlement rule.F-: preserve the semantic state and emit exactly one complete unexpected-input diagnostic through the configured disposition.NA:Stoppedadmits no mailbox input; late settlement belongs to the surviving lifecycle host.
All runtime-input cells require exact expected source, kind, operation, and
correlation for admission.
Every mismatch is F-. F- continues only under successful diagnostic
delivery. Terminate or an undeliverable diagnostic preserves other actions
and selects terminal settlement as specified above.
Total supervisor-mode matrix
| Mode | Status query | Capability query | Shutdown | Proxy birth/exit | Proxy input/report | Recovery timer | Drain timer | Delivery settlement |
|---|---|---|---|---|---|---|---|---|
Operating | Q | Q | S0 | PB/PE | IS/PR/PS | RS/TF | F- | SET |
Draining | Q | Q | S= | PB/PE | IS/PR/PS | RS/TF | DDS/DF | SET |
Stopped | NA | NA | NA | NA | NA | NA | NA | NA |
Queries remain available during orderly drain and show Stopping or
Retired. DrainDeadline* in Operating and a recovery timer with no exact
batch are F-. Late recovery facts during Draining settle or diagnose the
cancelled exact batch but cannot emit a replacement.
Total operating-member matrix
Nested phase rules below exhaust each non-empty cell. The five Needs* row
labels are readable projections of WorkerReplacement's stop/response product,
not five separately stored Rust alternatives.
| Owned member state | Proxy birth | Proxy exit | Input settlement | Initial outcome | Replacement outcome | Worker stopped | Unavailable |
|---|---|---|---|---|---|---|---|
CreatingProxy | PB | PE | F- | F- | F- | F- | F- |
CreatingProxyAfterExit | PB | F- | F- | F- | F- | F- | F- |
Starting | F- | PE | IS | PR | F- | F- | PR |
Online | F- | PE | F- | F- | F- | PR | PR |
Empty | F- | PE | F- | F- | F- | F- | PR |
RecoveryMember(AwaitingInitial) | F- | PE | IS | PR | F- | F- | PR |
RecoveryMember(Waiting) | F- | PE | F- | F- | F- | PR | PR |
RecoveryMember(NeedsReceiptAndStop) | F- | PE | IS | F- | F- | PR | PR |
RecoveryMember(NeedsReceipt) | F- | PE | IS | F- | F- | F- | PR |
RecoveryMember(NeedsStopAndOutcome) | F- | PE | F- | F- | PR | PR | PR |
RecoveryMember(NeedsOutcome) | F- | PE | F- | F- | PR | F- | PR |
RecoveryMember(NeedsStop) | F- | PE | F- | F- | F- | PR | PR |
Stopping | F- | PE | F- | F- | F- | F- | PR |
Retired | F- | F- | F- | F- | F- | F- | F- |
The exact proxy source is checked before report kind. A report from another owned role is foreign to this member and cannot be redirected by equal payload shape. An unowned application child never enters this matrix.
Initialization and proxy creation
Initialization reserves every ProxyReservation and pairs one affine initial
OperationTicket with one private witness per role in declaration order before
emitting the first creation. Each retained initial OperationInput owns that
ticket. Pair allocation is fresh by construction: another reservation cannot
collide with it, and process-wide allocation failure is not relabelled as a
supervisor rejection. If any proxy-route reservation rejects, every locally
paired operation is retired, the complete ordered initial set is returned, and
no proxy creation, observation, input, or partial fleet is emitted. Otherwise
one creation bundle per role contains:
- the fresh stable-proxy creation;
- exact creation observation;
- exact established-capability observation; and
- exact termination observation.
This is a Bombay staged-creation policy and a future real-boundary proof obligation, not a general actor-model guarantee.
An emitted proxy operation retains its private witness only until one exact settlement is admitted. Admission consumes the witness's correlation authority. An accepted settlement retains the operation ID only when a later exact proxy outcome requires it; a rejected, corrupt, or unattempted settlement already owns the complete operation and ID. Retaining the consumed witness beside that settlement would store arrival history rather than current authority. Foreign settlement admission returns the witness and complete settlement unchanged. Roster search attempts this affine admission in declaration order and restores every declined member to the same position without allocation or a separate owner query.
For an exact ProxyBirthResult:
CommittedentersStarting(WaitingForAuthorization)with the exact proxy and retained initial input. It does not consume activation capacity merely because the proxy exists.FoldRejectedorHostRejectedretains the complete initial input and applies the configured topology-failure reaction.RetireMemberentersRetiredbecause no proxy exists.StopSupervisorstarts global drain.- In
CreatingProxyAfterExit, rejection plus authoritative exit is one contradiction retaining both facts. Commit proves an installed but already terminal proxy and retires that role or begins whole-supervisor drain; it never sends the initial input.
An exact ProxyExit::BeforeBirth changes CreatingProxy to
CreatingProxyAfterExit. An AfterBirth exit for the exact live proxy is
handled by the stable-proxy-death reaction. RetireMember preserves the exact
cause and removes only that role from live topology; StopSupervisor begins
global drain. No worker failure is inferred from proxy death.
If proxy exit races an emitted initial or replacement operation, the exit
atomically transfers that operation's input/report settlement to the surviving
lifecycle host and releases its activation slot by transfer, not by pretending
an atomic proxy outcome arrived. The affected participant leaves its recovery
batch. The complete exit moves into the ProxyDied diagnostic settlement;
the retired member retains only exact terminal correlation. Any later proxy
report or rejected-report settlement meets that same host-owned exit and is
returned as one contradiction retaining both facts. The actor does not clone
the exit into state or forget the pending operation merely to retire quickly.
This host-transfer equation is required for totality but remains an open shared-settlement realization. An executable prototype must prove that the locked interpreter can perform the transfer; otherwise the focused finding is recorded rather than replaced by a mailbox flag.
After every transition that releases activation capacity, the supervisor scans waiting roles in declaration order. It reserves at most the remaining positive capacity and emits each eligible initial or replacement input in that same order. Reservation and emission are one commit.
Initial operation settlement and outcome
Authorization changes:
Starting(WaitingForAuthorization { input: { ticket, kind, control } })
-> Starting(Dispatched { ticket, settlement })
+ proxy_install_inputs(control)
whose source settlement owns { ticket, kind, control }
The complete input leaves state. The fold consumes its already-reserved ticket; authorization never performs a new fallible correlation allocation. Exact settlement then follows:
AcceptedchangesDispatchedtoAwaitingOutcome; the operation ticket remains occupied.Rejectedreleases the ticket and returns the exact input. It emits oneProxyInputRejecteddiagnostic and applies the configured failure reaction. There is no automatic retry.- A proxy outcome before accepted settlement is
F-; the interpreter must discharge install settlement before making the later child report admissible.
In AwaitingOutcome, an exact InitialInstallation result releases the
operation ticket:
Ready { worker }stores that opaque evidence inOnlineand, when selected, emitsStarted { role, proxy }.- Any complete non-ready result is retained inside
ProxyOutcomeFailed, then appliesRetireMemberby draining the still-live proxy or begins whole-supervisor drain. It never emitsStarted.
An Unavailable report from the exact owned proxy is expected in every live
non-retired member phase. It is relayed as one lifecycle event when publication
is selected, preserving role, sender, proxy phase, and command. Without
lifecycle publication, the same complete value is an operational diagnostic;
it is never discarded. This is a deliberate model policy required to preserve
the command obligation when no lifecycle consumer exists.
Recovery admission
Only an exact WorkerStopped from Online can trigger automatic recovery. It
first produces the semantic Empty subject, then evaluates one transaction
from the pre-transition fleet snapshot:
- classify the complete stop as normal or abnormal;
- apply permanent, transient, or temporary eligibility;
- select one-for-one, one-for-all, or rest-for-one roles in declaration order;
- reject every role already recovering, stopping, retired, or owned by an admitted replacement;
- include an initially unresolved role required by a coordinated strategy as
AwaitingInitialrather than pretending it is ready; - reserve one exact batch-preparation correlation, emit the typed ordered worker-preparation action, and enter the pending preparation phase;
- join its exact source settlement, retaining the source authority, every prepared submission, every selected role, and any rejection;
- reserve the recovery ticket, one operation ticket per selected replacement, and every fallible timer correlation;
- prune budget evidence at the interpreter-authored stop time using an inclusive window;
- reject clock regression explicitly;
- validate the complete multi-role budget charge;
- compute the checked one-based delay from the triggering role's next
admitted
RecoveryOrdinal; and - commit the membership partition, prepared ownership, exact correlations, budget charge, and resulting actions in one transition.
Steps 1–5 are pure local preparation. Step 6 commits only the pending preparation phase and its action. Steps 8–13 occur only after the exact batch settlement succeeds. Failure changes no peer, emits no replacement, and charges no budget. The trigger's authoritative stop has still occurred; the configured failure reaction applies to that exact now-empty role.
Every selected replacement receives one new affine operation pair before the recovery commits. A pair cannot equal another live or retired pair. The complete ordered candidate set therefore has no operation-allocation rejection case; worker preparation, recovery/timer correlation, budget, and checked release remain the fallible pre-commit decisions.
The delay ordinal is deliberately the triggering role's admitted recovery ordinal. Selected peers do not combine their unrelated history into one delay. This fills a policy choice left implicit by the governing documents and must be compared before production work.
No source selects a reset rule. Bombay therefore preserves the triggering
role's lifetime admitted count and advances it with checked arithmetic.
Exhaustion is RecoveryCountExhausted { trigger, admitted }; it does not wrap,
saturate, panic, reset implicitly, or masquerade as delay arithmetic overflow.
This is a deliberate Bombay representation policy, not an actor-model
guarantee.
Eligibility is total:
| Policy | Normal stop | Abnormal stop |
|---|---|---|
| Permanent | eligible | eligible |
| Transient | ineligible | eligible |
| Temporary | ineligible | ineligible |
Ineligible termination is not topology failure. The exact role remains
Empty behind its live stable proxy and no recovery budget is charged. This
is the AA-10 policy selected for the feature catalogue's previously open
“retires or leaves empty” choice. It allows status to distinguish an intact
but unavailable role from a lost proxy. Only explicit proxy/topology failure
uses FailureReaction.
When lifecycle publication is configured, the exact ineligible stop moves into
the role's WorkerStopped { disposition: Ineligible } event. The durable
Empty role then owns no second stop. When publication is omitted, no lifecycle
event is constructed and the explicit omission policy discharges the already
classified stop. Empty retains no historical report merely for a later
shutdown. Ineligibility is not an operational failure and therefore does not
manufacture a diagnostic merely to dispose of the stop.
Selection is total:
- one-for-one selects the triggering role;
- one-for-all selects every restartable role in the immutable snapshot;
- rest-for-one selects the trigger and every restartable role declared after it;
- an online peer contributes its exact current worker;
- the trigger contributes its exact stopped predecessor;
- an initial operation that was already admitted may contribute one
AwaitingInitialparticipant; - every other unresolved or terminal role is not restartable.
If a coordinated strategy requires an inadmissible role rather than one of the explicitly supported awaiting-initial phases, admission rejects the complete decision. It never silently shrinks the selected set.
Factory rejection identifies its role and returns every prepared peer
definition. Budget denial reports active attempts, requested replacement
count, and maximum. A maximum of zero denies every non-empty decision. Each
admission appends one charge whose replacements equals participant count to
the supervisor's BudgetState; the sum of active charge counts never exceeds
the maximum. RecoveryBatch does not duplicate that evidence.
Recovery prerequisite transitions
Immediate timing starts in Ready. Delayed timing emits one exact schedule
request for the batch. Schedule acceptance changes Scheduling to Waiting;
rejection applies the topology-failure reaction to the trigger and returns every
still-unemitted replacement. An exact timer input changes Waiting to Ready
once. Stale, duplicate, wrong-generation, foreign, early, or regressing timer
inputs are F-.
Replacement issue requires these three current truths:
- its exact predecessor/readiness prerequisite is satisfied;
- its batch release is
Ready; and - activation capacity is currently available.
Readiness and timing remain satisfied in their owning participant and release state.
Capacity is derived afresh and is consumed only in the transition that issues
the input, so no stored phase claims a slot before issuance. That transition
consumes the prepared input's already-reserved OperationTicket, emits the
complete replacement input, and stores NeedsReceipt for a stopped participant
or NeedsReceiptAndStop for an online participant. No fallible correlation
allocation remains after batch admission. A batch may issue several newly
eligible participants in one turn only up to available capacity, in declaration
order.
An AwaitingInitial participant continues to process its exact InitialPhase:
- accepted input settlement advances normally;
- a ready initial outcome emits
Started, supplies the exact predecessor, satisfies readiness, and may issue replacement in the same fold; - rejected input or non-ready atomic outcome cannot satisfy readiness. The participant leaves the batch and applies topology failure for that role;
- the remaining admitted participants continue. Already emitted replacements are not rolled back, because atomicity governed admission, not cross-actor completion.
The recovery batch retires only after every participant has left through a ready replacement, typed failure reaction, shutdown transfer, or forced retirement. Its ticket is never reused.
Replacement settlement and outcome
For an exact NeedsReceiptAndStop or NeedsReceipt participant:
- accepted settlement changes only the remaining requirements and retains its occupied operation ticket;
- rejected settlement releases the ticket, retains the complete input in one diagnostic, and applies topology failure to that role without retry.
An exact WorkerStopped for the owned predecessor is accepted in
NeedsReceiptAndStop, NeedsStopAndOutcome, or NeedsStop. It removes only
the stop requirement, may emit the optional lifecycle event, and never opens or
charges another recovery decision. A duplicate or stop for another worker is
F-.
For an exact NeedsStopAndOutcome or NeedsOutcome participant:
- every exact replacement outcome releases the activation ticket immediately;
- if worker stop is already owned,
Replacement::Ready { worker }leaves the recovery batch, entersOnline, and optionally emits oneRestartedcarrying the exact proxy and recovery ticket; - if worker stop is still required, the complete result enters
NeedsStop; readiness is not externally published until the matching stop report or its typed delivery settlement closes the join; - after the join closes, every rejection, activation failure, pre-ready stop,
or contradiction applies topology failure and never emits
Restarted; - an initial-installation report is wrong-kind even if its nested payload has the same worker type.
An exact WorkerStopped in NeedsStop closes the reunion and
applies the retained outcome once. Rejection of the stop report delivery closes
the same semantic join through lane settlement while preserving the rejected
report with the lifecycle host; it cannot fabricate a successful stop event.
Operating recovery consumes a terminal WorkerReplacement immediately through
one exhaustive replacement disposition. A ready outcome plus the exact stop is
Restarted; a non-ready outcome plus the exact stop is ProxyFailed; an input
rejection is InputRejected; every other current combination remains
Waiting. This disposition is a transition result, never another stored member
phase. It retains complete stop, outcome, or rejected-input ownership and chooses
no diagnostic disposition, topology reaction, lifecycle route, or action.
Shutdown retains the unchanged WorkerReplacement instead: retirement requires
both proxy retirement and return of the emitted replacement input or outcome.
The transient terminal values preserve ownership explicitly. Restarted and
ProxyFailed carry StoppedWorker { readiness, stop }; InputRejected carries
RejectedReplacement { previous, readiness, stopped, operation, returned_worker }. The aggregate then consumes those values once. Success moves the successor's
attempt/readiness into Online; predecessor readiness is discharged only after
the exact stop has joined. Failure moves predecessor attempt/readiness into the
selected member-retirement or supervisor-drain reaction. When lifecycle
publication is configured, the exact stop moves into WorkerStopped; otherwise
a failed replacement retains it with retirement ownership. There is no separate
stop-presence flag or duplicated stop claim.
For successful replacement with lifecycle publication, WorkerStopped precedes
Restarted in the declared lifecycle lane. Omitted publication constructs no
lifecycle value. Input rejection and non-ready proxy outcome construct exactly
one complete operational diagnostic and apply the configured topology reaction;
neither can fabricate Restarted. Removing a terminal participant releases its
operation occupancy and allows the existing declaration-order authorization
transition to consider the next waiting participant in the same turn.
An exact spontaneous WorkerStopped opens a new recovery only in Online.
While RecoveryMember::Waiting still owns an online participant, its first
exact stop changes that participant to stopped and does not open another batch.
A second stop while the member is empty or recovering is stale unless it is the
one explicit replacement reunion above. A stop of an unrelated
peer that remains independent and online may trigger its own disjoint batch.
Failure reaction
Every topology failure first constructs one exact diagnostic. Then:
RetireMemberasks the exact live proxy to stop and entersStopping; if no proxy was committed or it already exited, the member entersRetireddirectly. Shutdown settlement and exact proxy exit form one order-independent join. Accepted settlement retires its receipt; non-accepted settlement remains complete in the retired member after exact exit. Unrelated roles and disjoint recovery batches remain live.StopSupervisorbegins the same global drain as management shutdown after preserving the diagnostic in that action.
Factory rejection, budget denial, checked-delay failure, schedule rejection, proxy creation rejection, proxy input rejection, failed atomic proxy outcome, and stable-proxy death enter this equation. Ineligibility does not.
If diagnostic disposition is Terminate, it dominates RetireMember: the
diagnostic action selects terminal settlement and the lifecycle host owns the
remaining fleet drain. This is not silently converted into the configured
topology reaction. A production realization must reconcile the actor's
immediate stop verdict with exact child ownership before claiming this path is
implemented.
Shutdown normalization
The first Shutdown atomically:
- closes management mutation and recovery admission;
- logically cancels every un-emitted prepared initial/replacement input;
- preserves emitted input settlement and atomic-outcome ownership;
- cancels delayed recovery batches so no later timer can issue work;
- transforms every member into one exhaustive drain member;
- emits at most one shutdown request for every exact installed proxy;
- retains every pending proxy creation until it resolves; and
- for deadline policy, reserves and emits one exact drain timer.
The drain partition is:
DrainMember =
ResolvingProxyBirth {
role,
reservation: ProxyReservation,
retained_initial,
prior_exit: NoExit | Exited { exit },
}
| DrainingProxy {
role,
proxy: ProxyIncarnation,
outstanding: ProxyOutstanding,
phase: ProxyStopPhase,
}
| Drained {
role,
result: Graceful | Absent | Forced { residual },
}
ProxyOutstanding =
Idle
| InitialWaiting { input: OperationInput }
| InitialEmitted { ticket, settlement_or_outcome }
| RecoveryWaiting { recovery, prepared }
| ReplacementEmitted { recovery, ticket, settlement_or_outcome }
These variants own the complete values that were local at shutdown. There is
no pending flag and no attempt to reconstruct an emitted input. Recovery
batches dissolve into their per-proxy outstanding ownership plus cancelled
timer settlement.
For ResolvingProxyBirth, exact rejection enters Drained(Absent) and creates
no proxy. Exact commit emits one shutdown for the installed proxy and enters
DrainingProxy. Commit after an authoritative pre-birth exit records a
contradiction and counts the already terminal proxy drained; it cannot send to
the dead proxy.
For DrainingProxy, shutdown settlement rejection stores the complete request
and reason in ProxyStopPhase::Rejected while the exact exit observation
remains live. It does not count as drained. An exact proxy exit consumes every
outstanding operation through the child's typed drain settlement, preserves
its complete result with the lifecycle host, and enters Drained(Graceful).
The normal proxy report lane is not required to fabricate an initial or
replacement outcome during child shutdown.
This exact child-drain settlement is a model requirement exposed by AA-01 and remains an open real-boundary proof obligation. If the interpreter cannot return emitted install/replacement ownership when the child suppresses normal parent reports during shutdown, later prototype work must record the focused kernel gap rather than drop the operation ticket.
Repeated shutdown is S=. It emits no duplicate proxy shutdown or deadline.
Late restart timers and schedule settlements can settle cancelled requests but
cannot restore a RecoveryBatch or emit replacement.
Deadline drain
DeadlinePhase =
WaitingWithoutDeadline
| Scheduling { timer, settlement }
| Waiting { timer }
| Fired { timer, observed_at }
WaitForActorGraph uses WaitingWithoutDeadline and may wait indefinitely.
RetireActorGraphAfter uses exact interpreter-authored monotonic time. Wrong,
duplicate, stale, early, or regressing deadline facts are F-.
Exact deadline-schedule acceptance changes Scheduling to Waiting. Exact
rejection cannot silently weaken RetireActorGraphAfter into an unbounded
wait. In the rejecting fold, every unresolved member moves to
Drained(Forced { residual }); the residual preserves the complete rejected
request, timer correlation, capability reason, and every other outstanding
ownership claim. The rejection is the forced-retirement cause and transfers
directly to the surviving lifecycle host. Any operational diagnostic follows
the selected disposition independently; its delivery cannot gate or undo the
forced transfer.
When the exact deadline fires, the aggregate atomically selects forced
retirement for every unresolved DrainMember. Its terminal ownership must
contain the role, proxy or reservation, current semantic phase, every
outstanding operation/timer/action settlement, and cause. The implementation
may project this as Drained(Forced { residual }) per member or retain the
complete stopped supervisor plus its exact deadline cause; it must not store
both. Ownership transfers to the surviving lifecycle host. No accepted
shutdown, proxy exit, worker readiness, successful restart, or normal lifecycle
event is fabricated.
The supervisor selects normal actor termination only when every member is
Drained and every action in the final transition has a settlement owner. A
forced actor-graph summary is not the application result: the live root remains
in residual settlement until late activation, proxy, and delivery ownership
resolves.
Unrelated application children and application births never enter
DrainingFleet, are never queried, and are never stopped by this actor.
Stale, overlap, and contradiction laws
- A proxy birth or exit advances only the role owning its exact reservation or installed capability.
- An install/replacement settlement advances only its exact role, proxy, operation ticket, and expected dispatched phase.
- An atomic proxy report advances only the exact child source and expected operation kind. Nested payload similarity cannot substitute for kind.
- A duplicate report after its operation ticket released is rejected and cannot release another slot.
- A worker stop outside
Onlinecannot start recovery. - One role cannot join two recovery batches. Overlap rejects the new complete decision before factory results, budget, or peers commit.
- A timer fact advances only its exact recovery ticket and timer generation.
- Proxy creation rejection plus authoritative exit is a contradiction retaining both facts. Commit plus prior exit retires the exact dead proxy.
- A report claiming readiness from a proxy already authoritatively stopped is
a contradiction, never a transient
Started/Restarted. - A delivery rejection cannot roll back an already committed member or budget transition. Its lane-specific payload moves to settlement.
- Diagnostic delivery rejection is terminal and non-recursive.
- No stale or contradictory input is consumed merely to make a later fact easier to accept.
Feature-catalogue trace
| Requirement family | Model evidence |
|---|---|
FS-BUILD | construction domain, non-empty unique role order, common protocol, optional lifecycle publication |
FS-TOPOLOGY | normalized state, independent-member sum, exact proxy creation, activation occupancy derived from operation states |
FS-FACTS | complete proxy lifecycle input, operating-member matrix, predecessor stop/outcome join, stale and contradiction laws |
FS-OPERATE | total status/capability projection, distinct query/lifecycle/diagnostic routes |
FS-ELIGIBILITY | complete permanent/transient/temporary table and selected intact-proxy Empty policy |
FS-STRATEGY | immutable snapshot, ordered selection, awaiting-initial participant, disjoint overlap law |
FS-BUDGET | pre-commit pruning, inclusive window, atomic counted charge, zero maximum, clock regression |
FS-TIMING | exact timer settlement, trigger ordinal, checked delay, eight-state prerequisite sum |
FS-ATOMICITY | twelve-step recovery preparation and one batch/budget commit |
FS-FAILURE | exact operational diagnostic followed by RetireMember or whole-supervisor drain |
FS-SHUTDOWN | normalized drain partition, pending creation, emitted-operation transfer, exact shutdown rejection, deadline residual |
Every family remains a model claim, not implementation or verification status. Open shared settlement, diagnostic, activation, terminal-lift, and deadline mechanisms keep their dependent production coverage rows open.
Independent-model obligations
AA-10 defines the oracle structure for later tests. The executable model must not copy the implementation branch structure. It should use independent vocabulary such as roster cells, admitted recovery batches, occupied permits, and drain obligations, then compare complete traces.
At minimum later tasks must cover:
- empty and duplicate-role construction rejection;
- initial proxy creation in declaration order and partial-preparation failure;
- proxy exit before/after birth resolution;
- positive activation bounds of one and two with declaration-order release;
- foreign, duplicate, and stale initial or replacement operation IDs, including proof that every new operation pair differs from every earlier pair;
- accepted and rejected install settlement before atomic proxy outcome;
- total query projections in every member phase;
- lifecycle and capability projections that expose only the stable proxy as a service capability;
- permanent/transient/temporary eligibility for normal and abnormal stops;
- one-for-one, one-for-all, and rest-for-one selection from immutable snapshots;
- unresolved-initial participants and overlapping failure rejection;
- zero budget, inclusive window boundary, pruning, multi-role atomic charge, and clock regression;
- immediate, constant, linear, and exponential timing, checked overflow, and all eight readiness/timer/authorization gates;
- factory rejection at every selected position with no peer mutation;
- stale/duplicate/foreign report, timer, exit, and settlement facts;
- lifecycle omitted versus delivered and both diagnostic dispositions;
- shutdown from every member phase, creation/exit ordering, rejected child shutdown, rejected deadline scheduling, late timers, and forced residual transfer; and
- complete named actions and accepted/rejected interpreter settlement.
First-slice non-features and open findings
AA-10 intentionally does not provide:
- Rust supervisor, fleet, recovery, builder, protocol, event, or effect types;
- an executable fold or independent model implementation;
- a shared recovery framework or reuse of dynamic-supervisor/pool state;
- proxy-private worker creation, initialization, activation, or worker routing;
- an interpreter for activation capacity, delivery settlement, timers, diagnostics, deadlines, or residual root ownership;
- wrapper-composition, initialization-order, compiler/DevX, or migration proof;
- a capability-safe heterogeneous public-protocol supervisor; or
- edits to the locked kernel, legacy actors, macros, testkit, or runtime.
The model records four findings requiring later comparison:
- the governing documents leave ineligible fixed members “retired or empty”
without a selected construction policy; this oracle deliberately leaves an
intact live proxy
Empty; - delayed coordinated recovery does not state which member history determines its one-based delay; this oracle uses the triggering role's admitted recovery ordinal;
- the named fixed-supervisor effect product has terminal diagnostics but no explicit route-delivered diagnostic operation even though lifecycle publication is optional; and
- normal proxy death or shutdown with an emitted install/replace operation requires a typed child terminal/drain settlement that returns outstanding ownership even when no normal proxy outcome can reach the supervisor.
These are falsification results, not changes to production authority. Before an executable task relies on any new mechanism, the solution, matrix, retained-core decision, and research audit must be reconciled together or the existing real boundary must be proven to express the same law unchanged.
This blocked specification may be compared with the independent dynamic supervisor, FIFO pool, and keyed pool models, but no comparison may treat its child-terminal settlement requirement as implemented or extract machinery from that assumption. It does not make AA-11 eligible: executable fixed supervision depends on AA-07's completed proxy boundary audit and eventual AA-10 closure.
Dynamic-supervisor falsification model
Status and authority
This document is the AA-20 independent semantic model for one dynamic
supervisor. It is a non-production falsification oracle. It does not select
Rust types, define another behavior algebra, implement a supervisor, or mark a
production coverage row implemented. docs/engineering/atomic-actor-solution.md remains
the production design authority.
Fresh allocation is the actor-model law used here: every accepted entry owns a fresh stable proxy, and every worker behind that proxy is a fresh actor. Creator-local creation correlations, exact capability settlement, operation tokens, generation-safe key reuse, activation authorization, and actor-graph drain are Bombay derivations or policy choices. Bounded keyed membership, explicit management operations, one durable lifecycle owner, unexpected-exit handling, and phase-sensitive cancellation are dynamic-supervisor template laws.
Runtime route selection is owned normatively by
docs/atomic-runtime-settlement.md. An accepted start issues and stores one
CreationId for its proxy; it never chooses or stores a runtime route before
emitting CreateChild. The generic creation settlement returns that exact
correlation and every rejected value. There is no separate route request,
waiting state, public route, or dynamic-supervisor-specific creation result.
The model is deliberately not a fixed supervisor with an optional role list. It contains no worker factory, fixed roster, restart eligibility, restart strategy, restart budget, or backoff policy.
The model must falsify designs that make any of these claims false:
- The configured positive entry limit bounds every retained entry, including entries still creating, cancelling, draining, or retiring.
- A semantic key is management identity only. It is never converted into a proxy route, nonce, incarnation, or proof of freshness.
- Every accepted start reserves one fresh entry generation, proxy creation ID, cancel authority, and initial-operation correlation before commitment.
- Admission and realization are distinct.
StartAcceptedandReplaceAcceptednever claim readiness. - The stable proxy is the only externally routable service capability. Worker incarnation evidence is opaque and non-routable.
- At most one management mutation owns an entry at a time. Every submitted worker definition is either locally owned, transferred once, returned once, or held by exact settlement ownership.
- Request reply routes are temporary. One mandatory durable lifecycle route owns later realization, unavailability, cancellation completion, and retirement facts.
- Cancellation is exact and phase-sensitive. It cannot claim that emitted creation, delivery, initialization, or activation work was physically cancelled.
- Removing an entry releases capacity only after every exact outstanding fact and action settlement transfers or resolves. Reusing its key receives a fresh generation and fresh proxy creation ID.
- Stale, duplicate, foreign, wrong-generation, wrong-operation, wrong-kind, and contradictory inputs cannot mutate a current entry.
- Query is a total read-only semantic projection and never exposes private definitions, routes, tokens, or proxy-private phases.
- Global shutdown closes mutation and drains every retained or still-creating entry exactly once while preserving request, lifecycle, diagnostic, and residual ownership.
- Every accepted input produces one next state and one complete named action product. No effect is ambient.
“Atomic” means one local Behavior fold commits one supervisor state and its
complete Actions. It does not claim atomic delivery, creation, cancellation,
or work across actors.
Model vocabulary
The following names describe the oracle. They are not proposed public Rust names.
Key = application-authored semantic management key
EntryGeneration = fresh non-reused supervisor correlation for one accepted key use
EntryIdentity = { key, generation: EntryGeneration }
ProxyBirthAttempt = fresh non-reused CreationId issued by the supervisor
RoutedCreation = the existing interpreter-private route selected for that CreationId
ProxyIncarnation = externally routable exact capability for one committed stable proxy
WorkerIncarnationEvidence = opaque non-routable provenance for one installed worker
WorkerSubmission = complete owned worker definition and activation work for one attempt
OperationKind = Start | Replace
OperationCorrelation = fresh non-reused checked operation identity and admission order
CancelAuthority = opaque caller-held capability for one OperationCorrelation
OperationEvidence = opaque non-cancelling projection used in durable events
ProxyOperationWitness = private affine authority retained before one emitted
proxy input settles
ProxyOperationId = opaque affine correlation returned only after that exact
proxy input is accepted
PreparedProxyInput =
Initial { worker: WorkerSubmission }
| Replacement { worker: WorkerSubmission }
DefinitionOwnership = Present { worker: WorkerSubmission } | Transferred
StopCorrelation = fresh non-reused correlation for one explicit entry stop
ShutdownCorrelation = structural exact { EntryIdentity, ProxyIncarnation }
DeadlineCorrelation = { timer, generation }
Key, EntryGeneration, operation correlations, proxy-operation IDs, attempts,
timer generations, and child routes are correlation—not actor identity. Only a
committed creation yields ProxyIncarnation. Sequence arithmetic, address
reuse, key equality, timing, or adjacency cannot manufacture provenance.
The proxy alone retains its routable worker recipient. The dynamic supervisor
retains only WorkerIncarnationEvidence for exact replacement and stop
correlation. Status, capabilities, request replies, and lifecycle events never
grant a route around the stable proxy.
Every checked sequence has an explicit exhaustion outcome and never wraps. Collision against retained state is a controlled contradiction, not an invented result of the opaque StableProxy operation pair. Failed correlation issue returns every still-owned input and commits no entry, operation, activation occupancy, or action.
Construction domain
One complete construction contains:
DynamicDefinition = {
entry_limit: PositiveMaximum,
activation: ActivationContract,
activation_limit: PositiveMaximum,
unexpected_exit: KeepEmpty | Retire,
actor_drain: ActorDrainPolicy,
lifecycle: DurableLifecycleRoute,
diagnostics: DeliverTo { route } | Terminate,
}
The durable lifecycle route is mandatory and selected once. It preserves its concrete logical, established, or mixed capability form and static hosting obligations. No request route can replace it. Diagnostics are independently mandatory through either a concrete route or terminal settlement.
ActivationContract is the selected static interpretation law. Each
WorkerSubmission is the complete owned per-attempt definition and activation
work after that law is applied. The fold never reconstructs, looks up, or
clones an affine activation plan after admission.
A zero entry limit, zero activation limit, invalid deadline, or missing policy is a construction failure. There is no fixed factory, worker roster, recovery policy, callback, no-op route, default unexpected-exit behavior, or public structural path.
A closed heterogeneous worker sum is valid only when every variant exposes one common concrete public service protocol. Different public protocols require different dynamic supervisors; the model never erases them into one envelope.
AA-20 selects no builder, typestate spelling, public enum names, or macro.
Supervisor state
SupervisorState =
Operating {
definition,
entries: BoundedEntryTable,
allocators,
}
| Draining {
definition,
entries: BoundedDrainTable,
deadline: DeadlinePhase,
allocators,
}
| Stopped
BoundedEntryTable contains at most entry_limit entries. Every key occurs at
most once. Every entry stores its exact generation and proxy creation ID or
exact proxy; the table does not derive either from the key.
The table need not retain declaration order. Activation fairness is explicit:
each waiting start or replacement already stores its ordered
OperationCorrelation, and newly available capacity issues the lowest
outstanding operation first. There is no separately
mutable queue whose contents can disagree with entry phases.
Activation occupancy is derived from exact dispatched or awaiting proxy-input states. A prepared but un-emitted input owns its complete worker submission but does not occupy activation capacity. Reserving capacity, forming the infallible proxy-operation pair, and emitting the prepared input are one transition.
Transaction-local start preparation
Start preparation is not a stored entry phase:
PreparedStart = {
identity: EntryIdentity,
creation: CreationId,
operation: OperationCorrelation,
cancel: CancelAuthority,
input: PreparedProxyInput,
}
PreparedReplace = {
identity: EntryIdentity,
proxy: ProxyIncarnation,
base: ReplaceBase,
operation: OperationCorrelation,
cancel: CancelAuthority,
input: PreparedProxyInput,
}
For one Start, the supervisor checks shutdown, duplicate key, and table capacity;
then fallibly reserves every field above before committing anything. Failure
returns the complete worker and an exact reason. Success commits
Entry::Starting(CreatingProxy) and emits the acceptance reply, fresh empty
proxy creation, exact creation observation, and exact proxy-exit observation in
one action.
There is therefore no observable stored Reserved phase between acceptance
and creation emission. A future executable design that introduces such a phase
must identify the real intervening settlement or prerequisite. A label that is
only a transient local preparation value must not appear in public query.
Operating entry sum
Every retained entry is exactly one of:
Entry =
Starting(StartEntry)
| Ready(ReadyEntry)
| Empty(EmptyEntry)
| Replacing(ReplaceEntry)
| Stopping(StopEntry)
| Cancelling(CancelEntry)
| Retiring(RetireEntry)
No entry has pending, ready, cancelled, stopping, or retired flags.
Start entry
StartEntry =
CreatingProxy {
identity,
creation: CreationId,
operation,
input: PreparedProxyInput,
}
| WaitingForAuthorization {
identity,
proxy: ProxyIncarnation,
operation,
input: PreparedProxyInput,
}
| AwaitingInputReceipt {
identity,
proxy: ProxyIncarnation,
operation,
witness: ProxyOperationWitness,
}
| AwaitingOutcome {
identity,
proxy: ProxyIncarnation,
operation,
proxy_input: ProxyOperationId,
}
CreatingProxy and WaitingForAuthorization own the worker inside the
prepared input. AwaitingInputReceipt retains only correlation; the input has moved
to source-side delivery settlement. AwaitingOutcome exists only after exact
input acceptance.
Available entries
ReadyEntry = {
identity,
proxy: ProxyIncarnation,
worker: WorkerIncarnationEvidence,
last_operation: TerminalOperation,
}
EmptyEntry = {
identity,
proxy: ProxyIncarnation,
previous: WorkerIncarnationEvidence,
cause,
last_operation: TerminalOperation,
}
TerminalOperation =
Committed { operation: OperationCorrelation, kind: OperationKind }
| Cancelled { operation: OperationCorrelation, kind: OperationKind }
| None
At most one terminal operation is retained per entry. It makes an immediate
matching repeated cancel truthful without an unbounded operation-history
table. Accepting the next start/replace mutation retires the prior terminal
record; older authorities then produce CancellationReceipt::Stale. This is a deliberate
bounded-retention policy, not an actor-model rule.
Replacement entry
ReplaceBase =
WasReady { worker: WorkerIncarnationEvidence }
| WasEmpty { previous: WorkerIncarnationEvidence, cause }
ReplaceEntry =
WaitingForAuthorization {
identity,
proxy,
base: ReplaceBase,
operation,
input: PreparedProxyInput,
}
| AwaitingInputReceipt {
identity,
proxy,
base: ReplaceBase,
operation,
witness,
}
| AwaitingOutcome {
identity,
proxy,
base: ReplaceBase,
operation,
proxy_input,
}
StableProxy is the sole owner of predecessor drain. It does not publish a
replacement outcome until its internal predecessor shutdown/exit reunion has
closed, and its owner never reconstructs that outcome from lower-level worker
events. DynamicSupervisor therefore owns no predecessor-stop phase. The
replacement outcome's nested replaces evidence must equal the retained base;
that validation is the supervisor's only predecessor responsibility after
input transfer.
Explicit stop entry
StopOrigin =
Available { prior: ReadyEntry | EmptyEntry }
| Starting { phase: StartOutstanding }
StopEntry =
WaitingForProxyBirth {
identity,
creation: CreationId,
stop: StopCorrelation,
start_outstanding,
}
| ShutdownDispatched {
identity,
proxy,
stop,
origin: StopOrigin,
shutdown: ShutdownCorrelation,
settlement,
}
| AwaitingProxyExit {
identity,
proxy,
stop,
origin: StopOrigin,
shutdown: ShutdownCorrelation,
}
| ExitBeforeShutdownSettlement {
identity,
proxy,
stop,
origin: StopOrigin,
exit,
shutdown: ShutdownCorrelation,
}
Stop during a pending start is accepted and waits for exact proxy creation. It is not operation-token cancellation: it closes the complete entry by key, reports stop admission to its caller, and transfers the interrupted start obligation to the durable lifecycle owner. Stop during replacement or cancellation is rejected as unavailable and does not steal that operation.
Cancellation entry
CancelKind = Start | Replace { base: ReplaceBase }
CancelEntry =
AwaitingProxyBirth {
identity,
creation: CreationId,
operation,
kind: Start,
definition_disposition: ReturnedToCancelReply,
}
| DrainingProxy {
identity,
proxy,
operation,
kind: CancelKind,
outstanding: CancelOutstanding,
shutdown: ProxyStopPhase,
}
CancelOutstanding =
NoTransferredInput
| InputSettlement { witness: ProxyOperationWitness, settlement }
| ProxyOutcome { proxy_input: ProxyOperationId }
| OutcomeBeforeExit { outcome }
ProxyStopPhase =
NotEmitted
| ShutdownDispatched { shutdown, settlement }
| AwaitingExit { shutdown }
| Rejected { shutdown, request, reason }
| ExitObserved { exit }
StartOutstanding =
DefinitionLocal { operation, input }
| InputEmitted { operation, witness: ProxyOperationWitness, settlement_or_outcome }
RetireOutstanding =
Idle
| Start { outstanding: StartOutstanding }
| Replace { operation, base, witness: ProxyOperationWitness, settlement_or_outcome }
| Cancellation { operation, outstanding: CancelOutstanding }
| Stop { stop, settlement_or_exit }
Cancellation before proxy-input emission returns the complete submitted worker
through CancellationReceipt::Returned. If proxy creation is already in flight, the
entry still waits for its exact result; a late committed proxy is drained.
Cancellation after input emission returns CancellationReceipt::Pending. It never
returns the worker, even if later delivery rejection transfers that input to
the lifecycle host. A late ready result is retained only for drain and never
emits Started or Replaced.
Retirement entry
RetireCause =
UnexpectedWorkerExit
| StableProxyExit
| StartFailed
| ReplacementFailedAfterTopologyLoss
| Cancellation
| Stop
| DiagnosticTermination
RetireEntry = {
identity,
proxy: NoProxy | Pending(CreationId) | Exact(ProxyIncarnation),
cause: RetireCause,
outstanding: RetireOutstanding,
shutdown: ProxyStopPhase,
}
An entry is removed only when outstanding is empty and its proxy is rejected
before birth or authoritatively exited. Removal is a state transition, not a
permanent tombstone.
Public command sum
Management =
Start { key, worker, reply_to }
| Replace { key, worker, reply_to }
| Stop { key, reply_to }
| Query { key, reply_to }
| Cancel { authority: CancelAuthority, reply_to }
Here worker means the complete WorkerSubmission, not merely the inner
behavior after its activation work has been separated. A future Rust surface
may infer and hide that product, but its ownership cannot disappear.
AA-20 omits a separate Retire command. For a ready or empty entry, explicit
Stop already drains the stable proxy and releases the key after exact exit.
Retirement after unexpected exit is policy-driven and automatic. Adding
Retire without another state-transition law would create two public ways to
perform the same operation.
Every request route is used once for its immediate reply and then moves to
action settlement. It is never stored as the durable lifecycle owner. A
CancelAuthority is returned in every cancel reply (or its rejected-delivery
settlement), so the supervisor never retains or clones that affine authority.
Request receipts
WorkerChangeReceipt = { key, cancel: CancelAuthority }
WorkerChangeRejection<Reason> = { key, worker, reason: Reason }
Start result =
Result<WorkerChangeReceipt, WorkerChangeRejection<StartRejection>>
Replace result =
Result<WorkerChangeReceipt, WorkerChangeRejection<ReplaceRejection>>
Stop result = Result<Key, StopRejection<Key>>
CancellationReceipt =
Returned { authority, worker }
| Pending { authority, phase }
| Committed { authority, resulting_phase }
| Cancelled { authority }
| Stale { authority }
| Draining { authority }
QueryReply = Unknown { key } | Known { key, phase }
Start rejection reasons include AlreadyExists, AtCapacity, ShuttingDown,
entry-generation exhaustion, management-operation exhaustion, and
proxy-creation exhaustion. Replace rejection includes unknown, unavailable,
shutting down, and management-operation exhaustion. Every rejection owns
the complete submitted worker. Stop rejection includes unknown, unavailable,
already stopping, shutting down, and stop-correlation exhaustion or collision;
no stop state or shutdown action is committed when that reservation fails.
Public query projection
PublicPhase =
CreatingProxy
| WaitingForActivationAuthorization
| AwaitingProxyOutcome
| Ready { proxy: ProxyIncarnation }
| Empty
| Stopping
| Replacing
| Cancelling
| Draining
| Retiring
Query is a byte-for-byte semantic no-op on entry state, allocator state,
capacity, and activation occupancy. It exposes no worker definition, child
route, entry generation, proxy-operation correlation, cancel authority, proxy-private
installation phase, or worker recipient. Delayed lifecycle events retain the
opaque entry generation because they cross key reuse; an immediate query does
not expose bookkeeping its caller cannot use. During global shutdown it
projects the drain phase. Absence is Unknown, not an Option combined with
flags.
Durable lifecycle and diagnostic values
DynamicLifecycle =
Started { identity, operation: OperationEvidence, proxy: ProxyIncarnation }
| StartFailed {
identity, operation: OperationEvidence,
definition: DefinitionOwnership, reason,
}
| Replaced { identity, operation: OperationEvidence, proxy: ProxyIncarnation }
| ReplacementFailed {
identity, operation: OperationEvidence,
definition: DefinitionOwnership, reason,
}
| StopFinished {
identity,
proxy,
result: Result<ProxyExit, StopFailure>,
}
| UnexpectedWorkerStopped {
identity, observed_at, disposition: KeptEmpty | Retiring,
}
| OperationCancelled { identity, operation: OperationEvidence, kind, result }
| WorkerChangeInterrupted {
identity,
operation: OperationEvidence,
interruption:
ExplicitStop { stop: OperationEvidence }
| SupervisorShutdown { change: Start | Replacement },
definition: DefinitionOwnership,
}
| CommandUnavailable { identity, sender, proxy_phase, command }
| EntryRetired { identity, cause }
DynamicDiagnostic =
CorrelationReservationRejected { key, operation_kind, reason }
| ProxyCreationRejected { identity, rejection }
| ProxyInputRejected { identity, operation, input, reason }
| ProxyOutcomeFailed { identity, operation, outcome }
| ProxyShutdownRejected { identity, proxy, request, reason }
| StableProxyDied { identity, proxy, exit }
| RejectedLifecycleFact { rejected }
| ForcedRetirement { identity, residual, cause }
Lifecycle events never expose a routable worker recipient. Exact predecessor
evidence from a worker-stop report moves into the entry; the durable event gets
the disjoint observation time and semantic disposition. Service capability
publication is limited to the stable proxy. A failure or interruption carries
a still-local worker definition as Present; after proxy-input emission it
says Transferred and the exact input settlement remains the sole owner.
Every event delivery has exact settlement ownership; delivery rejection cannot
roll back admission or realization.
When one transition emits both lifecycle and diagnostic values, the exact source payload moves to only one of them. The other receives an explicit semantic classification or a disjoint field projection. The model never clones an affine rejection, exit, worker definition, command, or incarnation fact merely to satisfy two consumers.
Diagnostics follow DeliverTo(route) | Terminate. Diagnostic delivery
rejection is terminal and non-recursive. Lifecycle and diagnostics are
independent lanes: neither substitutes for the other. Terminate transfers
the diagnostic and every still-live entry obligation to the surviving
lifecycle host and enters the same global drain equation; it does not drop the
diagnostic or continue an ordinary entry-local transition.
Stable-proxy input sum
ProxyCreationResult = the generic CreationsSettled result for the exact
CreationId and StableProxy creation
ProxyInputResult = ActionItemResult<
ProxyOperation,
ProxyInputReceipt { creation, proxy, operation: ProxyOperationId },
ProxyInputRejection,
Never,
>
ProxyReport =
Initial { outcome }
| Replacement { outcome }
| WorkerStopped { stop, observed_at }
| Unavailable { sender, phase, command }
ProxyExit = { identity, proxy, exit }
ProxyShutdownSettlement =
Accepted { identity, proxy, shutdown }
| Rejected { identity, proxy, shutdown, request, reason }
The generic result owns whether interpretation was attempted. Acceptance owns
only the receipt promised by the request. Rejection and corruption return the
complete ProxyOperation; an unattempted result retains it unchanged. There is
no supervisor-specific result sum or conversion seam.
Every proxy report is accompanied by its exact child-source capability. The
entry generation, exact source, expected operation kind, and replacement
outcome's nested replaces evidence form complete correlation. The supervisor
does not widen AA-01's proxy protocol with its cancel authority.
The source carried by a proxy input result or proxy exit selects its entry once before the phase matrix. A source that selects no entry is returned as rejected input. After selection, the phase checks only its distinct operation or worker evidence; it does not compare the selecting source with itself again or invent a second phase-local wrong-source outcome.
Required semantic action product
The minimum model product is:
proxy_creations
proxy_creation_observations
proxy_stop_observations
proxy_initial_inputs
proxy_replacement_inputs
proxy_shutdowns
management_replies
lifecycle_events
diagnostics = Deliver { route, diagnostic } | Terminal { diagnostic }
deadline_schedules
rejected_inputs
delivery_settlements
All operations are named semantic lanes. No positional traversal, nested
.inner, runtime registry, dynamic envelope, callback, or direct effect occurs
inside the fold. The actual production product may use existing lower-order
lanes only if it proves this complete observable algebra without
reinterpretation.
Ownership table
| Value | Actor-side owner before emission | Settlement/host owner after emission | Terminal law |
|---|---|---|---|
| Start worker | prepared start, then creating/waiting entry | proxy-input settlement after emission | returned only before transfer; otherwise lifecycle host settles it |
| Replace worker | prepared replace in waiting entry | replacement-input settlement after emission | same phase boundary as start |
| Entry generation | exact retained entry | never emitted as authority | retired only when entry removal closes all facts |
| Cancel authority | accepting caller | moved into one cancel request | every reply or rejected-delivery settlement returns it; the supervisor never retains it |
| Operation correlation | active entry | lifecycle/diagnostic settlement after terminal realization | at most one bounded terminal record remains |
| Stable proxy | entry after committed birth | actions borrow delivery authority; lifecycle host retains ownership | exact exit or forced transfer retires it |
| Worker evidence | entry or replacement join | never a routable action capability | replaced by fresh evidence or retired with exact stop |
| Activation slot | derived from dispatched/awaiting state | host owns unresolved proxy input/outcome | matching outcome, input rejection, or forced transfer releases it |
| Request reply | incoming command | request-delivery settlement | never becomes durable lifecycle ownership |
| Lifecycle event | transition-local value | lifecycle-delivery settlement | rejection transfers outward without state rewind |
| Diagnostic | transition-local value | diagnostic route or terminal settlement | rejection is terminal and non-recursive |
Management-command matrix
| Entry phase | Start same key | Replace | Stop | Cancel matching active op | Query |
|---|---|---|---|---|---|
| absent | admit if capacity and preparation succeed | reject/return worker | reject unknown | unknown/stale | Unknown |
Starting::CreatingProxy | reject/return worker | reject/return worker | accept and wait birth | return worker, wait birth | CreatingProxy |
Starting::WaitingForAuthorization | reject/return worker | reject/return worker | accept and drain proxy | return worker, drain proxy | waiting authorization |
Starting::AwaitingInputReceipt | reject/return worker | reject/return worker | accept and drain outstanding | pending cancellation | awaiting outcome |
Starting::AwaitingOutcome | reject/return worker | reject/return worker | accept and drain outstanding | pending cancellation | awaiting outcome |
Ready | reject/return worker | admit replacement | accept stop | matching last op committed | ready capability |
Empty | reject/return worker | admit replacement | accept stop | matching last op terminal | empty |
Replacing::WaitingForAuthorization | reject/return worker | reject/return worker | reject unavailable | return replacement, restore base | replacing |
| emitted/awaiting replacement | reject/return worker | reject/return worker | reject unavailable | pending cancellation | replacing |
Stopping | reject/return worker | reject/return worker | reject already stopping | active start/replace token is shutdown-owned | stopping |
Cancelling | reject/return worker | reject/return worker | reject unavailable | already cancelled | cancelling |
Retiring | reject/return worker | reject/return worker | reject unavailable | shutdown-owned or stale | retiring |
During global drain, every mutation rejects with ShuttingDown; Cancel
returns CancellationReceipt::Draining; Query remains total.
Lifecycle-fact matrix
Notation:
Aaccepts the exact fact and performs the named phase transition;Jaccepts one side of an order-independent join;Dsettles only the matching drain obligation;Fpreserves state and emits one complete rejected-fact diagnostic.
Exact entry generation and proxy source are checked before this table. A fact
that fails either check is always F.
| Entry phase | Proxy birth | Proxy exit | Input settlement | Initial outcome | Replacement outcome | Worker stop | Unavailable | Shutdown settlement |
|---|---|---|---|---|---|---|---|---|
Starting::CreatingProxy | A | A | F | F | F | F | F | F |
Starting::WaitingForAuthorization | F | A | F | F | F | F | A | F |
Starting::AwaitingInputReceipt | F | A | A | F | F | F | A | F |
Starting::AwaitingOutcome | F | A | F | A | F | F | A | F |
Ready | F | A | F | F | F | A | A | F |
Empty | F | A | F | F | F | F | A | F |
Replacing::WaitingForAuthorization | F | A | F | F | F | A | A | F |
Replacing::AwaitingInputReceipt | F | A | A | F | F | F | A | F |
Replacing::AwaitingOutcome | F | A | F | F | A | F | A | F |
Stopping | A or F by origin | J | D | D | D | D | A | J |
Cancelling | D | D | D | D | D | D | A | D |
Retiring | D | D | D | D | D | D | A | D |
global DrainEntry | D | D | D | D | D | D | D | D |
WorkerStopped while replacement still waits for authorization changes a
WasReady base into the exact empty predecessor; it does not open an
unexpected-exit policy decision or a second operation because the accepted
replacement already owns the service mutation. For WasEmpty, another stop is
F. After replacement input transfer, StableProxy consumes predecessor stop
privately and emits only the completed replacement outcome. A later
WorkerStopped report can describe only the successor selected by that completed
outcome and is processed after the replacement result.
Stopping accepts proxy birth only when its origin still owns the matching
pending proxy creation. Input and outcome values in stopping/cancelling/
retiring states never publish success; they settle exact outstanding ownership
through the drain equation.
Start transitions
Successful preparation commits the entry before emitting StartAccepted and
the proxy creation bundle. Acceptance delivery rejection cannot unreserve the
entry or recover the worker; the durable lifecycle owner remains authoritative
for later realization.
The entry retains only OperationCorrelation. CancelAuthority moves to the
request reply action and then to its delivery settlement; the supervisor never
stores or clones the caller's cancellation capability. A later Cancel
returns that authority to the fold, which privately projects the matching
correlation.
Exact proxy rejection removes the entry after preserving StartFailed with
the still-local definition as Present and all creation settlement. Exact
proxy commit stores ProxyIncarnation and either:
- waits with the complete prepared input when activation capacity is full; or
- consumes the lowest waiting
OperationCorrelation, forms the infallibleProxyOperationWitness/ProxyOperationIdpair, emits the initial input, and entersAwaitingInputReceiptin the same transition.
Input settlement acceptance enters AwaitingOutcome. Rejection releases
activation occupancy, marks the durable StartFailed definition
Transferred, preserves the complete input in diagnostic settlement, drains
the empty stable proxy, and never retries automatically.
An exact initial Ready outcome releases occupancy, enters Ready, stores the
opaque worker evidence privately, and emits exactly one durable Started with
the stable proxy. Any non-ready outcome emits StartFailed and retires/drains
the proxy. A report before accepted input settlement is rejected; the
interpreter must discharge source settlement before later child report
admission.
After every occupancy release, waiting starts/replacements issue in increasing
OperationCorrelation up to the positive limit.
Replace transitions
Replace is admissible only from Ready or Empty. Before acceptance, the supervisor
constructs one complete transaction-local PreparedReplace, reserving its
operation/cancel pair. A failure
returns the complete replacement and leaves the base entry unchanged. Success
moves the cancel authority into the acceptance action and every other field
into Replacing::WaitingForAuthorization; no partial replacement phase is
committed.
While waiting for authorization, cancellation returns the replacement and
restores the exact base with a bounded Cancelled terminal record.
Authorization forms the infallible proxy-operation pair and moves the
replacement input to settlement.
Input rejection restores the base, emits one durable ReplacementFailed with
definition Transferred, and preserves the complete input through diagnostic
settlement. Input acceptance enters AwaitingOutcome.
StableProxy joins the ready predecessor's exact stop with successor work before
publishing one replacement outcome; an empty proxy has no predecessor drain.
DynamicSupervisor consumes that one completed outcome. A ready result enters
Ready with fresh worker evidence and emits one Replaced after validating the
nested replaces evidence against the retained base. A pre-birth failure enters
Empty with the validated replaces evidence; a post-birth failure enters
Empty with the successor attempt returned by StableProxy. Both emit
ReplacementFailed. This is the same EmptyAfter projection owned by AA-01;
the supervisor does not infer which worker the proxy now names. It never reports
success or returns an already-transferred worker definition.
Cancellation after emission suppresses both outcomes, drains the exact proxy,
and eventually emits OperationCancelled to the durable lifecycle owner. It
cannot revive the predecessor or pretend the proxy stopped creating work.
Stop, unexpected exit, and unavailability
Stop accepts a ready, empty, or still-starting entry. Its immediate reply describes admission only. The durable lifecycle owner receives completion or runtime rejection.
Before StopAccepted, the fold fallibly reserves StopCorrelation. For a
committed proxy, the exact shutdown correlation is then formed totally from
the retained entry identity and proxy capability, and one shutdown is emitted.
Settlement rejection preserves the request and reason. If the proxy is
otherwise live, a stop-originating ready/empty entry is restored only when no
authoritative exit has arrived; a failed StopFinished is durable realization,
not an admission rejection. If exit and settlement arrive in the other order, the
closed join preserves both and never resurrects the entry.
For a still-creating proxy, stop retains the creation result obligation. Exact
creation rejection removes the entry. Exact commit emits one shutdown. Any
locally owned initial definition transfers to the durable
WorkerChangeInterrupted { interruption: ExplicitStop } outcome. An
already-emitted input resolves first: rejection moves the complete returned
input to diagnostic custody, while acceptance requires the exact late proxy
result in the same lifecycle value. No Transferred label substitutes for an
owned worker value.
An exact spontaneous worker stop from Ready first makes the entry unavailable
and emits UnexpectedWorkerStopped. KeepEmpty enters Empty and continues to
consume capacity. Retire enters Retiring, shuts down the stable proxy, and
releases capacity only after exact drain. Neither policy restarts a worker.
Stable-proxy death in any phase retires that exact entry generation. It settles the active operation through typed host transfer, emits the corresponding lifecycle failure and diagnostic, and cannot affect another entry or later use of the same key.
Every exact Unavailable report is delivered once to the mandatory durable
lifecycle route with key, generation, sender, phase, and command intact. It is
valid during starting, ready, empty, replacing, stopping, cancelling, and
draining. Foreign or stale source capability is a diagnostic, not a fold error
that loses the command.
Cancellation and bounded token retention
Cancel first selects the semantic key and matches the checked, supervisor-global operation carried by its opaque authority. Operations never wrap or reuse, so entry generation is not a second cancellation coordinate; it remains the durable identity of the retained entry and its lifecycle messages.
- A matching locally owned definition returns once.
- A matching transferred definition returns
CancellationReceipt::Pendingand moves the entry into exact drain. - A matching active cancellation returns
CancellationReceipt::Cancelled. - A matching most-recent committed record returns
CancellationReceipt::Committed. - A matching most-recent cancelled record returns
CancellationReceipt::Cancelled. - An older, removed-generation, foreign, or never-issued authority returns
CancellationReceipt::Stalewithout mutation. - Global drain returns
CancellationReceipt::Draining.
The model retains no unbounded operation history. When a new operation is
accepted, the prior terminal record retires. This bounded policy must be
documented in any public cancellation contract; indefinite Committed
answers would require a growing tombstone set or an impermissible provenance
inference from sequence arithmetic.
Entry retirement and key reuse
Capacity counts every retained entry, including cancelling and retiring ones. An entry is removed only after:
- pending proxy creation resolves;
- every emitted proxy input settles or transfers;
- every proxy-private activation outcome resolves or transfers;
- exact proxy shutdown/exit resolves or transfers;
- lifecycle, diagnostic, and rejected-delivery ownership has a host; and
- forced-retirement residual ownership, if any, moves to the surviving root.
Removal retains no key tombstone. A later Start for the same key reserves a
fresh supervisor-global EntryGeneration, proxy creation ID, and operation.
Because child routes and generations are never reused, a late fact from an old
entry cannot match the new entry. It is returned complete as a stale diagnostic.
Allocator exhaustion rejects future starts with their workers intact. It does not justify reuse of an old generation or child route.
Global shutdown normalization
The first global shutdown atomically:
- closes start, replace, and stop admission;
- makes cancel report
CancellationReceipt::Drainingwhile query remains available; - transfers every locally owned start/replacement definition to one durable
WorkerChangeInterrupted { interruption: SupervisorShutdown { change } }outcome and retains already-emitted definitions through their exact input settlement or proxy outcome; - preserves every emitted creation, proxy-input settlement, and atomic proxy outcome obligation;
- transforms every retained entry into one exhaustive drain entry;
- emits at most one shutdown for every committed live proxy;
- retains every pending proxy creation until exact commit/rejection; and
- reserves and emits one exact deadline request when selected.
DrainEntry =
ResolvingProxyCreation { identity, creation: CreationId, local_values }
| DrainingProxy {
identity,
proxy,
outstanding: EntryOutstanding,
shutdown: ProxyStopPhase,
}
| Drained { identity, result: Graceful | Absent | Forced { residual } }
EntryOutstanding =
Idle
| StartWaiting { operation, input }
| StartEmitted { operation, witness: ProxyOperationWitness, settlement_or_outcome }
| ReplaceWaiting { operation, base, input }
| ReplaceEmitted { operation, base, witness: ProxyOperationWitness, settlement_or_outcome }
| ExplicitStop { stop, settlement_or_exit }
| Cancellation { operation, outstanding }
| Retirement { cause, outstanding }
Repeated shutdown is idempotent. Late creation, proxy report, input settlement,
shutdown settlement, and proxy exit can settle only their exact drain entry.
They cannot restore mutation or emit Started/Replaced.
Exact proxy exit consumes every outstanding operation through the same typed child terminal/drain settlement required by AA-01 and AA-10. If the runtime cannot return emitted input ownership when normal proxy reports are suppressed during shutdown, that is a shared real-boundary blocker; the model does not replace it with flags, abort, logging, or dropped values.
Deadline drain
DeadlinePhase =
WaitingWithoutDeadline
| Scheduling { correlation, settlement }
| Waiting { correlation }
| Fired { correlation, observed_at }
WaitForActorGraph may wait indefinitely and owns no timer.
RetireActorGraphAfter reserves one exact correlation. Reservation failure or
schedule rejection immediately forces every unresolved entry into
Drained(Forced { residual }) with that exact rejection as cause; it never
degrades into unbounded waiting.
When the exact deadline fires, every unresolved entry transfers its complete phase, proxy or creation ID, operation, local values, and action settlements to the surviving lifecycle host. No accepted stop, ready worker, successful replacement, or normal exit is fabricated. The root remains live until residual external work settles.
The supervisor selects normal termination only when every entry is drained and every final action has a settlement owner.
Stale, overlap, and contradiction laws
- A management key alone never matches a lifecycle fact.
- A proxy creation or exit advances only its exact entry generation and creation ID or exact capability.
- A proxy-input settlement advances only its exact generation, source,
ProxyOperationWitness, operation kind, and dispatched phase. - An initial outcome cannot satisfy replacement, and replacement cannot satisfy initial.
- Replacement outcome evidence must name the exact retained predecessor.
- A duplicate report cannot release another activation slot.
- A cancel authority cannot cancel another operation on the same key.
- An old-generation fact cannot advance a reused key.
- A stop correlation cannot consume a replacement or cancellation settlement.
- A delivery rejection cannot roll back an already committed state transition.
- Contradictory authoritative facts retain both sides and transfer to diagnostics.
- Diagnostic rejection is terminal and never recursively diagnoses itself.
Every rejected fact preserves the semantic entry state and emits one complete
RejectedLifecycleFact through the configured diagnostic disposition.
Feature-catalogue trace
| Requirement family | Model evidence |
|---|---|
DS-BUILD | construction domain, mandatory durable lifecycle owner, independent diagnostics, no fixed policy |
DS-START | complete pre-commit preparation, fresh proxy, admission/realization split, stable capability publication |
DS-QUERY | total public projection with no Option/flags or private phase leakage |
DS-STOP | pending-birth stop, exact shutdown/exit join, durable completion |
DS-REPLACE | base-preserving preparation, exact predecessor join, fresh ready outcome |
DS-EXIT | complete unexpected-stop policy and durable unavailability |
DS-RETENTION | bounded complete table, fresh global generation, no tombstones, exact stale diagnostics |
DS-CANCEL | phase-exact definition ownership, logical post-transfer cancellation, bounded terminal token law |
DS-SHUTDOWN | mutation closure, exhaustive entry drain, pending creation, deadline residual transfer |
Every row remains a model claim, not implementation status.
Independent-model obligations
The later executable oracle must use independent vocabulary and compare complete traces. At minimum it must cover:
- zero/invalid construction limits and missing mandatory policies;
- start at capacity, duplicate key, and every correlation reservation failure;
- proxy creation commit/rejection/exit orderings;
- activation limits one and two with operation-order issuance;
- accepted/rejected proxy-input settlement before atomic outcome;
- start cancellation in every phase, including late commit/readiness;
- replace from ready and empty, including StableProxy's internal predecessor stop/outcome orders and the supervisor's single completed result;
- replacement cancellation before and after definition transfer;
- stop during pending start, ready, empty, replacement, and cancellation;
- unexpected worker exit under both policies;
- total query projection in every operating and draining phase;
- operation-token replay, bounded terminal retention, and old-token staleness;
- remove/reuse of one key with a fresh generation and late old facts;
- foreign/duplicate/wrong-kind/wrong-source lifecycle facts;
- request, lifecycle, diagnostic, and shutdown delivery rejection;
- shutdown from every entry phase, pending creation, late ready, and repeated shutdown;
- deadline reservation failure, schedule rejection, exact firing, and residual root settlement; and
- complete named actions for every accepted/rejected transition.
Falsification findings and comparison obligations
AA-20 records these findings for governing-document reconciliation:
- The clean-room task text called durable lifecycle ownership optional, while the feature, solution, and DevX contracts make one durable route mandatory. This model follows the governing mandatory law.
- The solution/type inventory list a public
Retirecommand, but the feature law already makesStoprelease ready/empty entries and automatically removes fully retired entries. No distinctRetiretransition remains; retaining both would violate the one-operation/one-spelling DevX target. - The solution/query vocabulary stores and exposes
Reserved, but successful preparation and proxy creation emission are one fold. Without a real intervening settlement,Reservedis transaction-local and not queryable. - The cancellation contract did not bound how long terminal operation tokens
remain recognizable. This model retains only the active or most recent
terminal operation per entry; older tokens return
CancellationReceipt::Stale. - The stop requirements both restrict admission to available/empty and require
stop during pending start to wait for creation. This model selects the latter
explicit race law and accepts stop during
Starting. - The governing dynamic action product omits explicit route-delivered diagnostic, deadline, fact-rejection, and child-terminal settlement operations required by its own laws. Activation remains proxy-private; the supervisor authorizes it by emitting the opaque proxy input, not through a second activation lane.
- The current production template conflates key with child nonce, retains the
start reply route as lifecycle owner, exposes
Option<phase>, has no entry limit/generation/cancellation/deadline law, and reconstructs worker progress instead of consuming AA-01's atomic proxy outcome. It is comparison evidence, not an implementation candidate for this model. - Governing dynamic syntax supplies
workerper operation while construction selects activation, but it does not state where a possibly affine concrete activation plan is materialized or returned. This model treatsworkeras the completeWorkerSubmissiononce the static activation contract is applied. An executable design must infer and hide that composition without cloning, reconstructing, or asking users to spell the product.
These findings do not authorize production surface. The solution, feature catalogue, type inventory, coverage matrix, DevX contract, retained-core decision, and research audit must be reconciled before an executable task relies on a changed law.
This specification is ready to compare with the FIFO and keyed-pool models. It does not make AA-21 eligible: executable dynamic supervision still depends on AA-07's completed proxy and interpreter boundary audit.
FIFO-pool falsification model
Status and authority
This document is the AA-30 independent semantic model for one FIFO worker
pool. It is a non-production falsification oracle. It does not select Rust
types, define another behavior algebra, implement a pool, or mark a production
coverage row implemented. docs/engineering/atomic-actor-solution.md remains the
production design authority.
The actor-model laws used here are isolated processing of one communication, communications to known recipients, fresh actor creation, and explicit next behavior. The pool itself, direct worker ownership, FIFO admission, bounded backlog, circular worker selection, activation authorization, recovery, at-least-once retry, opaque completion authority, action settlement, and actor-graph drain are derived constructions or deliberate Bombay policies. Neither Agha's actor model nor message-arrival indeterminacy supplies FIFO pool semantics.
The model is deliberately not a supervisor containing a work queue. It reuses policy values where their meanings are identical, but it contains no stable proxy, supervisor member state, proxy operation, public worker capability, nested supervisor behavior, keyed affinity, or redispatched supervisor event.
Runtime route selection is owned normatively by
docs/atomic-runtime-settlement.md. The pool issues and stores only a
CreationId for each direct worker creation; it never chooses or stores a
runtime route before emitting CreateChild. Any older reservation label below
denotes that creator-visible correlation before interpretation and routed
evidence only in the returned runtime settlement. It does not authorize a
separate route request, waiting state, or public route.
AA-30 is exercised by the clean-room FifoPool aggregate and independent
customer, recovery, retirement, and queue models. H04b proves typed assignment
completion lowering and H02 implements total action settlement. H03 assigns
terminal lifecycle custody to Bombay through the documented upstream contract;
that Engine/root integration remains an upstream gate rather than a FIFO
aggregate gap. Legacy WorkerPool tests remain historical characterization
evidence, not an architecture or independent model.
The model must falsify designs that make any of these claims false:
- The roster is non-empty, ordered, and contains each semantic worker role once.
- Every installed or replacement worker is a freshly created actor. A role, route, attempt, or sequence value is not actor identity.
- The stable pool actor is the only public service capability. Exact worker capabilities remain pool-private.
- The pool directly owns worker creation, initialization, activation, readiness, recovery, and drain. No nested proxy or supervisor owns them.
- Every accepted job has exactly one authoritative customer obligation and is either queued, assigned, or being terminally settled—never two of them.
- Backlog capacity counts queued jobs only. Zero capacity permits immediate assignment and rejects waiting ownership.
- Every worker has at most one assignment. Only an exact ready incarnation is eligible.
- No queued job coexists with an eligible idle worker after a completed fold.
- Jobs leave the queue in admission order. Worker choice starts at one circular declaration-order cursor and advances after each assignment.
- A completion must match its opaque authority, assignment, creator-local child nonce, private worker-birth evidence, role, and active phase. It resolves one customer outcome.
- A matching worker stop resolves its active assignment once before recovery.
Retryis at-least-once execution and preserves FIFO admission order. - Assignment delivery, completion, and worker stop may arrive in any order. Their join cannot duplicate a retry, terminal customer outcome, or recovery decision.
- Irrecoverable topology loss cannot strand accepted queued or assigned work.
- Shutdown closes admission, returns every queued and assigned job once, disables dispatch/retry, and drains every owned or still-creating worker.
- Stale, duplicate, foreign, wrong-worker-birth, and contradictory facts preserve current ownership and have an explicit diagnostic disposition.
- Every accepted input produces one next state and one complete named action product. No effect is ambient.
“Atomic” means one local Behavior fold commits one pool state and its complete
Actions. It does not claim atomic delivery, creation, execution, recovery, or
customer observation across actors.
Model vocabulary
The following names describe the oracle. They are not proposed public Rust names.
Role = one semantic worker label, unique inside this pool
RoleOrder = immutable declaration order retained for the pool lifetime
WorkerBirthAttempt = fresh non-reused CreationId issued by the pool
RoutedWorkerBirth = interpreter-private { attempt: WorkerBirthAttempt, route }
WorkerBirthEvidence = opaque non-routable evidence for one committed birth
WorkerRecipient = pool-private exact routable capability for one committed worker
WorkerIncarnation = { recipient: WorkerRecipient, evidence: WorkerBirthEvidence }
BirthKind = Initial | Replacement { recovery: RecoveryTicket, replaces: WorkerBirthEvidence }
WorkerSubmission = complete owned worker definition and activation work
RequestCorrelation = caller-authored opaque submission correlation, echoed only
JobId = fresh non-reused customer-visible correlation issued by the pool
AdmissionOrdinal = fresh non-reused private total order for one accepted job
AssignmentId = fresh non-reused private correlation for one dispatch attempt
CompletionAuthority = opaque affine authority bound to AssignmentId and WorkerBirthEvidence
CompletionCorrelation = pool-retained non-authorizing half of that exact authority
CompletionEvidence = consumed CompletionAuthority paired with one result
DispatchCorrelation = {
assignment: AssignmentId,
retained: CompletionCorrelation,
authority: CompletionAuthority,
}
RecoveryTicket = fresh non-reused correlation for one role recovery
RecoveryOrdinal = one-based admitted recovery count for that role
TimerCorrelation = { timer, generation }
ActivationTicket = fresh non-reused correlation occupying one authorization slot
ShutdownCorrelation = exact { role, WorkerIncarnation }
A child route, role, attempt, job, admission ordinal, assignment, recovery
ticket, timer, or activation ticket is correlation, not actor identity. Only
committed fresh creation yields WorkerIncarnation. Its recipient is routable
only inside the pool's effects and lifecycle state; customers and worker
messages never receive it as a service capability.
CompletionAuthority has no public constructor or inspectable fields. It is
the affine authority half of one freshly issued pair; the pool retains the
non-authorizing CompletionCorrelation half. The worker can only consume its
half with the assignment to form
assignment.complete(result). The resulting completion chooses no customer,
pool address, role, parent path, send lane, or inspectable worker evidence.
The current ChildReport boundary attaches only the creator-local child nonce,
not an exact WorkerIncarnation. Exact completion matching therefore combines
that nonce with the authority's opaque WorkerBirthEvidence; retained
comparison evidence cannot manufacture a completion. A concrete lowering of
that private evidence remains a blocker rather than an interpreter assumption.
Every correlation allocator has one complete result:
Reservation<T> = Reserved(T) | Rejected(Exhausted | Collision { candidate: T })
A rejection never wraps, overwrites, or reuses a correlation. The transition rules below say whether it rejects an unaccepted submission, returns an accepted job, retires a worker attempt, or begins actor-graph failure. Merely declaring a value fresh is not a total allocation law.
Construction domain
One complete definition contains:
FifoPoolDefinition = {
initial_factory,
roles: NonEmptyOrderedUnique<Role>,
activation: ActivationContract,
activation_limit: PositiveMaximum,
recovery: RecoveryPolicy,
backlog_capacity: NonNegativeMaximum,
interruption: Fail | Retry,
distribution: FIFO,
actor_drain: ActorDrainPolicy,
diagnostics: DeliverTo { route } | Terminate,
}
RecoveryPolicy contains eligibility, the typed worker source required by
permanent or transient recovery, restart budget and inclusive window, immediate
or checked delayed timing, and RetireRole | StopPool topology failure.
Temporary recovery contains no worker source. Recovery is always one role at a
time; a FIFO pool has no one-for-all or rest-for-one strategy.
ActorDrainPolicy is exactly:
WaitForActorGraph
RetireActorGraphAfter { deadline }
Construction fails for an empty roster, duplicate role, zero activation limit, invalid deadline, missing policy, or worker protocol that cannot accept the exact assignment product. FIFO construction has no selector or placeholder selector.
Before the pool exists, the initial factory is invoked for every role in
declaration order and every worker reservation is fallibly prepared. If one
factory or reservation fails, the construction error owns every already
prepared definition and correlation; no partial fleet, budget charge,
activation occupancy, or Actions commits. This is a Bombay all-or-none
initial-preparation policy. Later creation and activation realization resolve
independently for each committed role.
The worker sum may be heterogeneous only when every variant accepts one common concrete assignment protocol and produces one common concrete result type. Static closed sums are allowed; erasure and runtime registries are not.
AA-30 selects no builder spelling, typestate, public identifier representation, trait, wrapper, alias, or macro.
Pool state
FifoPoolState =
Operating {
definition,
members: OrderedMemberMap,
backlog: Fifo<QueuedJob>,
cursor: RoleCursor,
activation: ActivationCapacity,
budget: RestartBudget,
allocators,
}
| Draining {
definition,
members: OrderedDrainMap,
deadline: DeadlinePhase,
allocators,
}
| Stopped
The Rust aggregate also has Constructed, which owns the prepared worker
roster before its one initialization transition, and ForcedRetirement, which
owns unresolved retiring workers plus the exact shutdown-ID or deadline
failure after a terminal transition. The sketch above describes the operating
law; those two additional source states preserve initialization rejection and
terminal custody. A real interpreter still has to prove transfer of the
ForcedRetirement value to the parent or root custodian.
OrderedMemberMap has exactly one member for each declared role. Roles are
never inserted, removed, or reordered. Retired remains as a terminal cell so
cursor order and late-fact classification do not depend on sequence arithmetic.
The backlog owns jobs in original admission order. backlog_capacity is the
new-admission waiting bound: a submission may queue only while the current
backlog length is below it. An interrupted already-accepted assignment under
Retry may re-enter even when that admission bound is full. Reinsertion is by
its immutable AdmissionOrdinal, before every later-admitted queued job and
after every earlier-admitted queued job; it is not unconditional front
insertion. The absolute queue bound is therefore
backlog_capacity + roles.len(), because each role can contribute at most its
one formerly active assignment. There is no unbounded overflow and no second
in-flight map: a live assignment is owned by its exact member.
RoleCursor denotes the first declaration-order position inspected for the
next assignment. Selection wraps once, skips non-idle and retired roles, and
chooses the first exact Idle. After assignment to role r, the cursor moves
to the declaration-order successor of r; retirement never compacts or
reorders the ring.
ActivationCapacity is derived from member states containing an occupied
ActivationTicket. A declaration-order queue is derived from members waiting
for authorization; correlated flags or a second occupancy counter are
forbidden.
Customer ownership sums
CustomerObligation = {
job: JobId,
admitted_at: AdmissionOrdinal,
customer: CustomerRoute,
retained_payload,
}
QueuedJob = {
obligation: CustomerObligation,
assigned_role: None | Some(Role),
}
AssignedJob = {
obligation: CustomerObligation,
assignment: AssignmentId,
completion: CompletionCorrelation,
worker: WorkerIncarnation,
role: Role,
}
AssignmentCommand = {
execution_payload,
completion: CompletionAuthority,
}
The queue owns QueuedJob. One busy member owns AssignedJob. The worker owns
only the moved AssignmentCommand; it never owns the customer route or the
canonical customer obligation.
Both Fail and Retry require a retained canonical payload while a cloned
execution payload is sent to the worker. Clone preparation occurs before the
candidate transition commits. If application cloning unwinds, no next state or
Actions exists; the model does not promise to reconstruct a value already
moved into a panicking fold.
The customer protocol is one closed sum:
AdmissionOutcome =
Accepted { request: RequestCorrelation, job: JobId }
| Rejected { request: RequestCorrelation, payload, reason: AdmissionRejection }
TerminalCustomerOutcome =
Completed { job: JobId, role: Role, result }
| ReturnedQueued { job: JobId, payload, reason: QueuedReturnReason }
| ReturnedAssigned { job: JobId, role: Role, payload, reason: AssignedReturnReason }
RequestCorrelation is supplied by the caller and echoed unchanged in both
admission outcomes. The pool neither allocates it nor trusts it as internal
job identity; customers using one route can still distinguish concurrent
requests. Admission commit moves it into the emitted Accepted; it is not
retained in the accepted job. Rejection moves it into Rejected. If either
delivery rejects, the lifecycle host owns that complete outcome. Accepted
and Rejected are admission outcomes. The other three are the only terminal
outcomes. A rejected submission was never accepted. A terminal outcome removes
its obligation from actor state when the delivery attempt is emitted; rejected
outcome delivery transfers that exact complete value to the lifecycle host and
never reconstructs an active job.
Complete member sum
Member =
Creating {
role,
reservation: WorkerReservation,
kind: BirthKind,
prior_stop: NoStop | StoppedBeforeBirth { stop },
}
| Initializing {
role,
worker: WorkerIncarnation,
kind: BirthKind,
init_attempt: InitAttempt,
}
| WaitingForActivationAuthorization {
role,
worker: WorkerIncarnation,
kind: BirthKind,
activation_permit,
activation_plan,
}
| ActivationDispatched {
role,
worker: WorkerIncarnation,
kind: BirthKind,
ticket: ActivationTicket,
begin_attempt: ActivationAttempt,
}
| Activating {
role,
worker: WorkerIncarnation,
kind: BirthKind,
ticket: ActivationTicket,
activation_attempt,
}
| DrainingPreReady {
role,
worker: WorkerIncarnation,
outstanding: PreReadyOutstanding,
cause: PreReadyDrainCause,
after_drain: Recover { stop } | Retire { reason },
}
| Idle {
role,
worker: WorkerIncarnation,
}
| Busy {
role,
worker: WorkerIncarnation,
job: AssignedJob,
join: AssignmentJoin,
}
| Recovering {
role,
predecessor: WorkerIncarnation,
stop,
recovery: RecoveryPhase,
}
| Stopping {
role,
worker: WorkerIncarnation,
outstanding: StopOutstanding,
after_stop: Retire { reason } | StopPool { reason },
}
| Retired {
role,
reason,
}
AssignmentJoin is the exhaustive delivery/completion/stop join:
AssignmentJoin =
AwaitingDeliveryAndTerminal { delivery: AssignmentDeliveryAttempt }
| DeliveryAcceptedAwaitingTerminal
| CompletionBeforeDelivery {
delivery: AssignmentDeliveryAttempt,
completion: CompletionEvidence,
later_stop: NoStop | Stopped { stop },
}
| StopBeforeDelivery {
delivery: AssignmentDeliveryAttempt,
stop,
later_completion: NoCompletion | Completed { completion: CompletionEvidence },
}
The delivery effect and lifecycle host own the moved AssignmentCommand while
settlement is unresolved; actor state retains only AssignmentDeliveryAttempt
and the non-authorizing completion half. CompletionBeforeDelivery owns the
returned completion input, while StopBeforeDelivery owns the authoritative
stop. They are disjoint, so no queue entry duplicates the stop. A completion
after StopBeforeDelivery is retained for stale or contradictory settlement;
a stop after
CompletionBeforeDelivery is retained in later_stop for one recovery after
completion settles. Shutdown transfers any unresolved join intact.
The lifecycle host—not Initializing—owns the linear initialization
settlement and activation plan while initialization is unresolved. Actor state
retains only InitAttempt. An exact InitResult later transfers either the
activation permit and plan, the rejected settlement and plan, or the stopped
plan back once. ActivationDispatched likewise retains correlation while the
host owns the moved begin request. These are the same ownership laws as AA-01,
applied directly rather than by reusing proxy state.
PreReadyOutstanding, StopOutstanding, and every recovery variant own
complete exact values rather than booleans. A definition or activation plan is
either local, moved into an action settlement, or transferred to the lifecycle
host—never reconstructed.
Recovery phase
RecoveryPhase =
Scheduling {
recovery: RecoveryTicket,
timer: TimerCorrelation,
prepared: PreparedReplacement,
schedule_attempt: RestartScheduleAttempt,
}
| WaitingForTimer {
recovery: RecoveryTicket,
timer: TimerCorrelation,
prepared: PreparedReplacement,
}
| ReadyToCreate {
recovery: RecoveryTicket,
prepared: PreparedReplacement,
}
PreparedReplacement owns a fresh worker definition, fresh
WorkerReservation, checked delay evidence, and one budget charge. Emitting
the creation moves the member to Creating; no recovery phase and creating
phase own the definition simultaneously.
Recovery eligibility is total:
| Policy | Normal stop | Abnormal stop |
|---|---|---|
| Permanent | eligible | eligible |
| Transient | retire role | eligible |
| Temporary | retire role | retire role |
One matching stop can admit at most one recovery. Preparation invokes the factory, reserves the recovery and worker-birth correlations, prunes budget at the interpreter-authored monotonic stop time using an inclusive window, validates the charge, and computes checked timing before commit. Any failure commits no recovery, timer, creation, or budget charge; it applies the selected topology-failure reaction to the already stopped role.
Immediate timing enters ReadyToCreate. Delayed timing enters Scheduling.
Exact schedule acceptance enters WaitingForTimer; exact rejection applies
topology failure and returns the complete prepared replacement to lifecycle
settlement. An exact timer moves to ReadyToCreate and emits creation in that
same fold. Stale or duplicate schedule/timer facts cannot create another
worker.
Complete input sum
FifoPoolInput =
Submit { request: RequestCorrelation, payload, customer: CustomerRoute }
| Shutdown
| WorkerBirthResolved(WorkerBirthResolution)
| InitializationSettled(InitializationSettlement)
| ActivationBeginSettled(ActivationBeginSettlement)
| WorkerReady(WorkerReadyFact)
| WorkerStopped(WorkerStopFact)
| AssignmentSettled(AssignmentSettlement)
| WorkerCompleted { child: ChildRouteNonce, completion: CompletionEvidence }
| RestartScheduleSettled(RestartScheduleSettlement)
| RestartTimerElapsed(RestartTimerFact)
| WorkerShutdownSettled(WorkerShutdownSettlement)
| DrainDeadlineSettled(DeadlineScheduleSettlement)
| DrainDeadlineElapsed(DeadlineFact)
Every fact carries exact role, reservation or incarnation, operation kind, and correlation required by its transition. A payload match without exact source and phase is never sufficient.
WorkerBirthResolution is:
Committed {
role,
reservation: WorkerReservation,
worker: WorkerIncarnation,
init_attempt: InitAttempt,
}
Rejected {
role,
reservation: WorkerReservation,
returned_submission: WorkerSubmission,
reason,
}
Successful installation is authoritative before worker initialization effects are interpreted. Initialization settlement and later activation are separate facts; commit cannot be rolled back because one later effect rejects.
InitializationSettlement is one host-authored result:
ReadyForActivation {
role, worker, init_attempt,
permit: ActivationPermit,
plan: ActivationPlan,
}
Rejected {
role, worker, init_attempt,
rejected_settlement,
plan: ActivationPlan,
reason,
}
Stopped {
role, worker, init_attempt,
stop,
plan: ActivationPlan,
}
The actor never stores the unresolved settlement or plan. Only this exact result moves the lawful next values into actor state or drain ownership.
AssignmentSettlement is attached to the creator-local child nonce:
Accepted { child: ChildRouteNonce, assignment }
Rejected {
child: ChildRouteNonce,
assignment,
returned: AssignmentCommand,
reason,
}
Rejection proves only that this delivery was not accepted. It is not fabricated
worker termination. Before the pool resolves the job, AuthorityReunion
consumes the returned command's affine authority together with the exact
retained CompletionCorrelation:
AuthorityReunion =
Cancelled { assignment, worker_birth: WorkerBirthEvidence }
| Mismatch { retained, returned_authority }
Cancelled proves that neither half remains live. Mismatch transfers both
complete values to terminal settlement and cannot requeue, complete, or reuse
the assignment. After successful reunion the pool safely requeues the proven-
unaccepted job, quarantines and drains the exact worker, and applies recovery
only after exact terminal or forced transfer.
Required semantic action product
The pool fold's minimum model product is:
FifoPoolActions = {
worker_creations: [CreateWorker { reservation, submission }],
worker_creation_observations: [ObserveWorkerCreation { reservation }],
worker_stop_observations: [ObserveWorkerStop { child: ChildRouteNonce }],
activation_begins: [BeginWorkerActivation { child, attempt, permit, plan }],
activation_cancellations: [CancelWorkerActivation { child, attempt }],
worker_assignments: [DeliverAssignment { child, assignment, command }],
worker_shutdowns: [ShutdownWorker { child, correlation }],
restart_schedules: [ScheduleRestart { recovery, timer, deadline }],
deadline_schedules: [ScheduleDrainDeadline { timer, deadline }],
admission_deliveries: [DeliverAdmission { route, outcome }],
terminal_customer_deliveries: [DeliverCustomerTerminal { route, outcome }],
diagnostic_deliveries: [DeliverDiagnostic { route, diagnostic }],
terminal_diagnostic_transfers: [TransferTerminalDiagnostic { diagnostic }],
rejected_fact_transfers: [TransferRejectedFact { fact, reason }],
forced_drain_transfers: [TransferDrainResidual { residual, cause }],
}
WorkerCompletionActions = {
parent_completion_reports: [ReportCompletionToParent {
authority: CompletionEvidence,
}],
}
The heterogeneous product has one closed owned-operation view for ordered application and rejection ownership:
OwnedOperation =
CreateWorker { reservation, submission }
| ObserveWorkerCreation { reservation }
| ObserveWorkerStop { child: ChildRouteNonce }
| BeginWorkerActivation { child, attempt, permit, plan }
| CancelWorkerActivation { child, attempt }
| DeliverAssignment { child, assignment, command }
| ShutdownWorker { child, correlation }
| ScheduleRestart { recovery, timer, deadline }
| ScheduleDrainDeadline { timer, deadline }
| DeliverAdmission { route, outcome }
| DeliverCustomerTerminal { route, outcome }
| DeliverDiagnostic { route, diagnostic }
| TransferTerminalDiagnostic { diagnostic }
| TransferRejectedFact { fact, reason }
| TransferDrainResidual { residual, cause }
| ReportCompletionToParent { authority: CompletionEvidence }
AppliedOperation =
WorkerCreationSubmitted { reservation }
| WorkerCreationObservationStarted { reservation }
| WorkerStopObservationStarted { child }
| WorkerActivationBeginDelivered { child, attempt }
| WorkerActivationCancelDelivered { child, attempt }
| AssignmentDelivered { child, assignment }
| WorkerShutdownDelivered { child, correlation }
| RestartScheduleSubmitted { recovery, timer }
| DrainDeadlineScheduleSubmitted { timer }
| AdmissionDelivered { request }
| CustomerTerminalDelivered { job }
| DiagnosticDelivered { diagnostic_correlation }
| TerminalDiagnosticTransferred { diagnostic }
| RejectedFactTransferred { fact, reason }
| DrainResidualTransferred { residual, cause }
| ParentCompletionReported { child, completion_correlation }
AppliedOperation records exactly which owned operation crossed the runtime
boundary without pretending that creation, observation, scheduling, worker
execution, or customer receipt has already completed. Their later domain facts
remain distinct inputs. Each applied variant carries the correlation needed to
match that later fact; where the draft vocabulary does not yet define a
diagnostic correlation, that missing allocator is part of the lowering
blocker, not permission to use a generic label.
Because the locked Environment::apply is ordered but non-transactional, one
action application reports a closed prefix result:
ActionBatchSettlement =
Complete { applied: [AppliedOperation] }
| Partial {
applied_prefix: [AppliedOperation],
rejected: { operation: OwnedOperation, reason },
not_attempted_suffix: [OwnedOperation],
}
OwnedOperation is the exact flattened sum of FifoPoolActions and
WorkerCompletionActions; it is not an erased envelope. Partial owns the
rejected operation and returns every unattempted payload to the lifecycle host
in authored order. A rejected ReportCompletionToParent therefore returns the
complete CompletionEvidence through rejected.operation; no separate generic
parent-rejection label is needed. The current runtime supplies neither this
complete prefix result nor the rejected parent operation: local parent
reporting discards a closed-parent send rejection. Both are explicit AA-30
blockers.
These are named semantic operations. No positional .inner traversal, proxy lane,
supervisor report, runtime registry, callback, channel, untyped envelope, or
ambient effect occurs inside the fold. A production representation may reuse
existing concrete lower-order products only after proving that it preserves
every lane, source, ordering, and rejection owner.
Initialization creation is interpreted before dependent observations. For one fold, admission outcomes precede assignments, terminal customer outcomes precede successor assignments, and diagnostics precede a terminal verdict. These interpretation orders are explicit Bombay policies, not general actor- model ordering guarantees.
Ownership table
| Value | Actor-side owner | Owner after emission | Terminal law |
|---|---|---|---|
| Unaccepted submission | incoming Submit | rejected-admission settlement | returned complete; never enters queue/member state |
| Queued customer obligation | FIFO backlog | terminal-customer settlement when removed | assigned once or returned once |
| Assigned customer obligation | exact Busy member | terminal-customer settlement on result/interruption/shutdown | removed before another terminal outcome can emit |
| Execution payload and completion authority | transition-local candidate | assignment-delivery settlement, then exact worker | rejection returns both; state retains only canonical obligation/correlation |
| Unsettled assignment join | exact Busy member with one customer obligation | exact assignment/completion/stop settlement, or lifecycle host on forced drain | one exhaustive variant selects one terminal job disposition |
| Initialization plan/settlement | lifecycle host while unresolved; actor retains InitAttempt only | exact InitializationSettlement transfers the lawful next values | never duplicated in Initializing |
| Worker definition | construction/recovery candidate | creation settlement | rejection returns exact definition; commit establishes fresh worker |
| Exact worker capability | one member | effects borrow delivery authority; lifecycle host owns forced residual | exact stop or forced transfer retires it |
| Activation slot | activation member phase | activation settlement after emission | ready, rejection, stop, or forced transfer releases once |
| Recovery candidate | local preparation then Recovering | timer/creation settlement | rejection returns complete prepared ownership |
| Customer outcome | transition-local | route delivery settlement | rejection transfers outward; never recreates job |
| Diagnostic | transition-local | diagnostic route or terminal settlement | rejection is terminal and non-recursive |
| Drain residual | exact drain member | surviving lifecycle host | never fabricated as successful stop/completion |
Transition notation
AD: submission admission and immediate assignment/queue/rejection.WB: worker birth resolution.IN: initialization settlement.AZ: activation authorization release.AS: activation-begin settlement.WR: exact readiness.WS: exact worker stop.DS: assignment delivery settlement.CP: exact completion.RS/RT: restart schedule settlement / timer fact.S0: first shutdown normalization.S=: repeated shutdown, no duplicate effects.WD: worker drain settlement or exit.DDS/DF: drain-deadline schedule settlement / exact deadline.F-: preserve semantic ownership and emit one complete rejected-fact diagnostic through the configured disposition.NA:Stoppedaccepts no actor input; late settlements belong to the surviving lifecycle host.
All cells below assume exact creator-local nonce, private worker-birth evidence,
role, kind, correlation, and phase. Any mismatch is F-.
Total pool-mode matrix
| Mode | Submit | Shutdown | Worker lifecycle | Assignment settlement/completion | Recovery timer | Drain timer |
|---|---|---|---|---|---|---|
Operating | AD | S0 | WB/IN/AS/WR/WS/WD | DS/CP | RS/RT | F- |
Draining | reject unchanged | S= | WB/IN/AS/WR/WS/WD | DS/CP | RS/RT | DDS/DF |
Stopped | NA | NA | NA | NA | NA | NA |
Submission during drain emits
Rejected { request, payload, ShuttingDown }; the
customer route and payload never enter pool ownership.
Total operating-member matrix
| Member | Birth | Init | Activation settlement | Ready | Stop | Assignment settlement | Completion | Restart timer |
|---|---|---|---|---|---|---|---|---|
Creating | WB | F- | F- | F- | join | F- | F- | F- |
Initializing | F- | IN | F- | F- | WS | F- | F- | F- |
| waiting authorization | F- | F- | F- | F- | WS | F- | F- | F- |
ActivationDispatched | F- | F- | AS | F- | WS | F- | F- | F- |
Activating | F- | F- | F- | WR | WS | F- | F- | F- |
DrainingPreReady | F- | settle | settle | settle stale | WD | F- | F- | settle only |
Idle | F- | F- | F- | F- | WS | F- | F- | F- |
Busy::AwaitingDeliveryAndTerminal | F- | F- | F- | F- | join | join | join | F- |
Busy::CompletionBeforeDelivery | F- | F- | F- | F- | join | join | F- | F- |
Busy::StopBeforeDelivery | F- | F- | F- | F- | F- | join | stale CP | F- |
Busy::DeliveryAcceptedAwaitingTerminal | F- | F- | F- | F- | WS | F- | CP | F- |
Recovering | WB only after emission | F- | F- | F- | F- | F- | stale CP | RS/RT |
Stopping | F- | settle | settle | settle stale | WD | settle | stale CP | settle only |
Retired | F- | F- | F- | F- | F- | F- | stale CP | F- |
Creating may observe an exact stop before its birth observation because child
report and creation-observation paths have no global arrival order. It stores
that one stop. Later commit joins the exact incarnation and resolves it without
advertising readiness; later creation rejection plus prior authoritative stop
is a contradiction retaining both facts.
Initialization and activation
Initialization emits every preflighted worker creation in declaration order.
Each member enters Creating before the action is exposed. Creation rejection
retires or stops the pool according to topology failure and never reports a
successful birth.
Creation commit enters Initializing { init_attempt }; the lifecycle host
retains the unresolved initialization settlement and activation plan. Exact
ReadyForActivation transfers its permit and plan into
WaitingForActivationAuthorization. Rejected or Stopped transfers its
complete plan into DrainingPreReady; neither rolls back the committed worker
or begins recovery while an installed actor remains owned.
After every transition that releases activation capacity, AZ scans waiting
roles in declaration order. Reserving an ActivationTicket, changing the
member to ActivationDispatched, and emitting BeginActivation are one fold.
Ticket exhaustion/collision enters DrainingPreReady with complete activation
ownership; it cannot leave a silent installed worker waiting forever.
The model accepts begin settlement in either order with worker stop and forced
drain; no synchronous interpreter guarantee is assumed. Acceptance enters
Activating. Rejection enters DrainingPreReady and returns the complete
activation request through exact settlement. Exact Ready releases its ticket and invokes
the same fill_fifo transition used after completion and retry. If backlog is
non-empty, the member becomes Busy directly; it never transiently commits
Idle.
Stop or forced transfer in any occupied activation phase releases exactly one ticket. Late readiness after stop is stale and never restores eligibility.
Admission
Submit { request, payload, customer } evaluates one transaction:
- reject
ShuttingDownorNoRecoverableWorkersbefore allocation; - inspect worker availability and queue occupancy; when no
Idlemember exists and the new-admission bound is full, rejectBacklogFullbefore allocation; - reserve one fresh accepted-job pair
{ JobId, AdmissionOrdinal }; pair exhaustion/collision rejects withJobCorrelationUnavailableand retires every candidate; - if an
Idlemember exists, reserve one freshDispatchCorrelation; rejection emitsDispatchCorrelationUnavailableand does not fall back to queue admission; - prepare the retained execution clone and exact assignment action; unwind produces no transition;
- commit
Busy, advance the cursor, emitAccepted { request, job }, then emit the assignment; or - when step 2 proved queue capacity, append the obligation with
NeverAssignedinAdmissionOrdinalorder and emitAccepted { request, job }.
These branches are disjoint. Full/unserviceable rejection performs no internal allocation; job-pair rejection and immediate-dispatch rejection return the original request, route, and payload; no preparation failure silently queues a job or leaves it accepted without an owner. Every candidate from an uncommitted attempt is retired and never reused.
The Accepted delivery attempt does not gate assignment or queue ownership.
Its rejection transfers the complete admission outcome to the lifecycle host;
it cannot roll the job back or become its terminal outcome.
FIFO fill and correlation failure
fill_fifo is a pure finite transition applied after readiness, completion,
retry insertion, assignment return, or member retirement:
- maintain the queue sorted by immutable
AdmissionOrdinal; a newly retried job is inserted after all smaller ordinals and before all larger ordinals; - while both the queue and an exact
Idlemember exist, inspect the smallest ordinal and the first idle role at or after the cursor; - prepare its execution clone and reserve a fresh assignment/completion pair;
- on success, remove that head, commit the member to
Busy, advance the cursor, and append one assignment action; - on correlation exhaustion/collision, remove that head and emit exactly one
terminal outcome selected by its origin—
ReturnedQueuedforNeverAssigned, orReturnedAssignedforRetried—then continue; and - on no idle member or empty queue, stop.
Returning a job on correlation failure is necessary because leaving it at the head beside an idle worker would violate the maintained invariant and could wait forever after permanent exhaustion. A clone unwind produces no fold and therefore does not run step 5.
After every committed operating transition:
backlog.is_empty() OR no member is Idle
Assignment settlement and completion
Delivery settlement, completion report, and worker stop have no assumed
cross-path order. AssignmentJoin consumes them as follows:
AwaitingDeliveryAndTerminal + AcceptedbecomesDeliveryAcceptedAwaitingTerminal.AwaitingDeliveryAndTerminal + completionbecomesCompletionBeforeDelivery; no customer outcome is emitted yet.AwaitingDeliveryAndTerminal + stopbecomesStopBeforeDelivery; no interruption outcome or recovery is admitted until delivery settles.CompletionBeforeDelivery + stopretains that stop inlater_stop.StopBeforeDelivery + completionretains the completion inlater_completion; stop remains the winning terminal ordering.CompletionBeforeDelivery + AcceptedresolvesCompleted, then evaluates its retained later stop once when present.StopBeforeDelivery + AcceptedappliesFail | Retryonce, diagnoses any retained later completion as stale, then evaluates recovery once.- Any exact
Rejectedsettlement first attempts affineAuthorityReunion. With no retained completion it safely reinserts the proven-unaccepted job byAdmissionOrdinalwith its prior assigned role, quarantines the worker, and evaluates any retained stop once. If a completion is already retained, the claimed returned authority contradicts its prior consumption; the fold emitsReturnedAssigned { ContradictoryAssignmentSettlement }, transfers both facts terminally, and begins whole-pool drain.
No branch relies on synchronous settlement. The customer obligation remains in
the exact Busy join until one settlement branch selects its disposition.
Wrong assignment, child nonce, worker-birth evidence, or phase is F-.
A completion matches the current assignment only when its creator-local child
nonce, opaque WorkerBirthEvidence, assignment correlation, and retained
non-authorizing half all agree. It never relies on the interpreter attaching an
exact incarnation capability. A matching completion after accepted delivery:
- removes the active correlation;
- consumes the canonical obligation;
- emits
Completedonce; - makes the exact member available;
- runs
fill_fifoin the same fold; and - commits
Idleonly if no queued job can be dispatched.
A duplicate, cross-worker, old-birth, unknown-authority, or wrong-phase completion preserves all current state and emits the complete result as a stale operational diagnostic. It never selects a customer route from a tombstone.
Interruption and recovery
An exact stop of Idle makes the role unavailable and evaluates recovery.
An exact stop after accepted delivery removes the active completion
correlation:
FailemitsReturnedAssigned { WorkerStopped }once;Retryretains the canonical obligation and reinserts it by immutableAdmissionOrdinalwith its prior assigned role; the next dispatch reserves fresh authority only when an exact idle worker is available; and- dispatch-correlation rejection for that retried head emits
ReturnedAssigned { RetryPreparationRejected }once.
Ordered reinsertion handles multiple interrupted workers: if A was admitted
before B, stopping A then B still produces [A, B], not [B, A]. Retry is not
a new admission: it may exceed the new-admission backlog limit, but the
complete queue remains bounded by backlog_capacity + roles.len().
fill_fifo may assign the job to a different exact idle role in the same fold.
Because the prior worker may have executed before stopping, this is explicitly
at-least-once execution.
The queue retains only the immutable ordinal and whether the obligation was
previously assigned to one role. Worker exit and rejected delivery have
identical future ordering, dispatch, shutdown, and customer-outcome semantics,
so retaining which event caused reinsertion would store history rather than
current queue truth.
The authoritative stop remains solely in AssignmentJoin until recovery
consumes it, then in Recovering; it is never copied into the queued job.
Duplicate stop during recovery is F-.
After assignment resolution, recovery eligibility and preparation run from
the exact stop. Eligible recovery emits the same typed non-empty worker-source
request used by fixed supervision, selecting this one role. Bombay interprets
that request outside Behavior and returns its complete generic settlement;
the pool owns what the returned submission or rejection means. Worker-source
failure, budget denial, clock regression, checked delay overflow,
recovery/birth correlation rejection, creation rejection, or schedule rejection
applies RetireRole | StopPool. RetireRole runs
fill_fifo across surviving members. If no live or recoverable member remains,
every queued job receives ReturnedQueued { NoRecoverableWorkers } in FIFO
order. StopPool enters the ordinary shutdown normalization rather than
dropping jobs through a terminal error.
Shutdown normalization
The first Shutdown atomically:
- closes admission;
- removes every queued obligation in FIFO order and emits one
ReturnedQueued { PoolShutdown }for each; - removes every assigned correlation and emits one
ReturnedAssigned { PoolShutdown }for each; - disables retry, recovery admission, readiness publication, and successor dispatch;
- cancels un-emitted prepared replacement and restart-timer ownership;
- transforms every member into one exhaustive drain member;
- emits at most one exact shutdown request per committed worker;
- retains every pending worker creation until commit or rejection; and
- for deadline policy, reserves and emits one exact drain timer.
The drain sum is:
DrainMember =
ResolvingCreation {
role,
worker,
activation,
creation_kind,
stop: None | Some,
}
| Worker {
role,
actor,
work: Created { activation }
| Initializing
| WaitingForActivation
| ActivationDispatched
| Activating
| Idle,
shutdown: NotRequested | Waiting { request, stop },
}
| AwaitingWorkerPreparation { role, request }
| AwaitingRestartSchedule { role, request }
| Drained { role }
The worker work sum owns actor-side correlations for unresolved initialization, activation, assignment, and shutdown operations; the lifecycle host owns their moved requests and linear values. Customer ownership has already moved to terminal customer outcomes; a late completion is stale diagnostic data and cannot be retained as another job.
Creation rejection enters Drained. An exact creation accepted during drain
starts no initialization or activation work. Without a prior stop it emits one
shutdown and enters Worker { work: Created { activation }, ... }; the
activation value stays local until exact stop transfers it to diagnostic
custody. With a prior exact stop, the worker is already retired and the
activation transfers immediately. A foreign or reversed result changes no
member. Shutdown rejection remains in the existing request/stop reunion while
exact stop observation is still possible; rejection alone never completes the
drain.
An exact worker stop consumes every matching outstanding child-side
settlement, preserves complete unresolved values with the lifecycle host, and
enters Drained(Graceful). Repeated shutdown is S=. Late restart, readiness,
assignment, and completion facts settle or diagnose their exact cancelled
ownership and can never reopen admission or dispatch.
Deadline drain
DeadlinePhase =
WaitingWithoutDeadline
| Scheduling { timer: TimerCorrelation, attempt: DeadlineScheduleAttempt }
| Waiting { timer: TimerCorrelation }
| Fired { timer: TimerCorrelation, observed_at }
WaitForActorGraph may wait indefinitely. RetireActorGraphAfter uses exact
interpreter-authored monotonic time. Deadline correlation reservation failure
cannot silently weaken it to unbounded waiting: the rejecting shutdown fold
immediately transfers every unresolved member as
forced-retirement custody.
Exact schedule acceptance enters Waiting. Exact schedule rejection also
forces every unresolved member in that same fold. The residual preserves the
complete timer request, rejection reason, worker or birth ownership, and every
outstanding action settlement. Exact deadline firing performs the same forced
transfer with the deadline fact as cause.
Forced transfer does not fabricate worker stop, assignment acceptance, completion, successful creation, recovery, or customer delivery. The stable root remains alive in lifecycle-host settlement until every transferred external obligation is resolved.
The pool selects normal actor termination only when every member is Drained
and every action in the final turn has an exact settlement owner. Unrelated
application children never enter this drain.
Stale, overlap, and contradiction laws
- A role alone never identifies a worker fact.
- Birth resolution advances only its exact role, reservation, attempt, and expected birth kind.
- Initialization, activation, and readiness advance only their exact installed incarnation and expected phase.
- Assignment settlement advances only its exact child nonce, private birth
evidence, assignment, and
AssignmentJoinvariant. - Completion advances only the exact active accepted assignment and consumed opaque authority.
- A completion carrying old worker-birth evidence cannot release a new worker birth's job.
- Stop is consumed once by the exact member or drain state. It cannot admit a second recovery or interruption.
- A recovery schedule or timer advances only its exact recovery and timer generation.
- A delivery rejection cannot roll back already committed job, budget, cursor, or worker state; its complete payload moves to settlement.
- Customer-outcome rejection cannot recreate a queue entry or assignment.
- Contradictory authoritative facts preserve both facts for diagnostic or residual settlement.
- Diagnostic rejection is terminal and non-recursive.
Every rejected fact preserves current semantic ownership and emits one
complete RejectedPoolFact through the selected diagnostic disposition.
Feature-catalogue trace
| Requirement family | Model evidence |
|---|---|
FP-BUILD | complete construction product, non-empty unique roles, static assignment/result protocol, explicit diagnostics/activation/drain |
FP-TOPOLOGY | direct fresh workers, exhaustive pre-ready and recovery sums, activation capacity, no proxy/supervisor state |
FP-ADMIT | complete customer submission, bounded queue, zero-capacity rule, typed reservation failure, accepted/rejected split |
FP-ASSIGN | private affine authority, one busy assignment, exact circular cursor, maintained no-idle-with-backlog invariant |
FP-COMPLETE | nonce plus private birth evidence, affine reunion, order-independent settlement/completion/stop join, one terminal result |
FP-INTERRUPT | fail/retry sum, admission-ordinal reinsertion, at-least-once law, no duplicated stop fact |
FP-FAILURE | one-role retirement, surviving dispatch, complete stranded-work return, separate diagnostics |
FP-RETENTION | accepted-job customer obligations plus assignment-only completion correlation and bounded action-settlement joins, no permanent tombstones |
FP-SHUTDOWN | admission closure, exact queued/assigned extraction, pending creation, direct worker drain, forced residual transfer |
Every row remains a model claim, not implementation status.
Independent-model obligations
The later executable model must use independent vocabulary such as worker cells, one queue, one accepted-job ledger, and one cursor. It must not copy these production-oriented phase names or the eventual implementation branches.
At minimum it must cover:
- empty/duplicate construction, missing policy, zero activation limit, and factory/reservation failure at every role with no partial fleet;
- worker birth commit/rejection and stop-before-birth in both orders;
- initialization and activation acceptance/rejection, limits one and two, and declaration-order authorization;
- readiness with empty and non-empty backlog and no intermediate idle state;
- capacity zero, exact capacity, backlog full, shutdown admission, and no recoverable worker;
- job, assignment, completion, recovery, activation, birth, and timer exhaustion/collision transitions;
- cursor wrap, retired-role skipping, multiple idle workers, and FIFO batches;
- completion followed by successor dispatch in one complete action;
- duplicate, cross-worker, old-worker-birth, wrong-token, and unknown-token completion with result preservation;
- assignment settlement before stop, stop before settlement, and replay of each side;
- completion-before-stop and stop-before-completion under
FailandRetry; - ordered reinsertion for two or more interrupted workers and immediate reassignment to another idle worker;
- permanent/transient/temporary recovery for normal and abnormal stop, budget boundaries, checked timing, schedule rejection, and restart exhaustion;
- role retirement with surviving capacity and complete stranded-job return;
- admission, customer outcome, assignment, diagnostic, creation, activation, timer, and shutdown delivery rejection;
- shutdown from every member phase, every queued/assigned cardinality, pending birth, pending assignment join, repeated shutdown, and late completion;
- deadline reservation failure, schedule rejection, exact firing, and forced lifecycle-host residual transfer; and
- complete named actions for every accepted and rejected transition.
Properties after every generated step must include:
accepted_jobs = queued_jobs + assigned_jobs + terminally_settling_jobs
queued_jobs <= backlog_capacity + worker_roles
new submission queues only when queued_jobs < backlog_capacity
queued admission ordinals are strictly increasing
each worker has at most one assigned job
each job has at most one terminal customer outcome
backlog.is_empty() OR no worker is Idle
cursor is one position in the immutable declaration-order ring
active completion correlations are unique
retired correlations are never reissued
Falsification findings and comparison obligations
AA-30 records these findings for governing-document reconciliation:
- Unconditional retry-front insertion reverses two interrupted jobs when stop
order differs from admission order. The corrected queue stores immutable
AdmissionOrdinaland reinserts by that order. - The initial draft stored linear initialization settlement and activation
plan in actor state. The corrected model follows AA-01: the lifecycle host
owns them while actor state retains only
InitAttempt. - The initial draft rejected completion before assignment settlement while
accepting stop in that order, despite no locked causal guarantee. The
corrected
AssignmentJoinexhausts settlement, completion, and stop order. ChildReportattaches only a creator-local nonce. The corrected completion law combines that nonce with opaque authority-carriedWorkerBirthEvidence; it does not claim an interpreter-attached exact incarnation.- The initial retry queue copied the authoritative stop fact. Corrected queue provenance owns only admission ordinal, prior role, and semantic interruption class; the join/recovery path owns the stop once.
- Rejected assignment delivery returns the affine authority. Corrected
AuthorityReunionconsumes returned authority and retained non-authorizing evidence exactly once before safe reinsertion. - The initial admission outcomes could not correlate concurrent requests on
one customer route. Both corrected variants echo caller
RequestCorrelation, distinct from pool-issuedJobId. - The initial admission algorithm allocated before full-backlog rejection and allowed queue fallback to overlap preparation failure. Corrected branches classify first, allocate only for an admissible candidate, and give every preparation failure one typed rejection.
- Generic settlement labels hid payload ownership and the locked runtime's non-transactional apply/closed-parent-report loss. The corrected model names every operation, exact settlement, applied prefix, rejected operation, and owned unattempted suffix. Concrete lowering remains blocked.
- Recovery reuses fixed-supervisor policy values but not state; its selected
topology failure is
RetireRole | StopPool. Backlog capacity bounds new admission while retry preserves the finite absolutecapacity + rolesqueue bound. - The current production pool contains stable proxies and a nested fleet ownership fold, exposes assignment/job identifiers to workers, and lacks the selected activation/opaque-authority/deadline laws. It is comparison evidence, not an implementation candidate. Its 550 green tests do not validate this oracle, and the current independent FIFO property model has one worker, so it cannot expose the two-worker retry-order counterexample.
These are falsification findings, not production API amendments. Before an executable task relies on any selected mechanism, the feature catalogue, solution, inventory, retained-core decision, research audit, and interpreter boundary must be reconciled together or the existing concrete boundary must be proved to express the same law unchanged.
First-slice non-features and blocker
AA-30 intentionally provides no:
- Rust pool, worker, job, assignment, completion, recovery, builder, protocol, event, effect, or error type;
- executable fold, model test, property, fuzz target, or benchmark;
- proxy, supervisor, keyed-affinity, shared recovery engine, or nested pool;
- interpreter for creation, activation, assignment, customer delivery, diagnostics, deadlines, or residual root ownership;
- wrapper-composition, initialization-order, compiler/DevX, or migration proof; or
- change to the locked kernel, actors, macros, testkit, runtime, examples, or public API.
The semantic document is complete enough for bounded comparison, but AA-30 is
[!], not complete as an executable aggregate prerequisite. H04b proves that
assignment.complete(result) lowers one unforgeable private parent completion
without exposing a path or customer capability. H02 owns exact report and
prefix/suffix action settlement. H03 records the required Bombay custody path;
that upstream runtime work is not implemented in this repository. A clean-room
FIFO model must still execute the independent-model obligations above before
production can cite AA-30 as executable evidence. AA-40 may compare this law,
but neither aggregate may extract shared production machinery before the same
suite passes both real consumers.
Keyed-pool falsification model
Status and authority
This document is the AA-40 independent semantic model for one keyed worker pool. It is a non-production falsification oracle. It does not select Rust types, define another behavior algebra, implement a pool, or mark a production coverage row implemented. For the clean-room campaign this document owns the keyed aggregate law; lower-authority solution and synthesis documents must be corrected when they disagree. Rust representation remains separately unselected.
The semantic state, input, action, admission, assignment, retirement, and drain
sums are complete in this revision. The independent bounded oracles in
src/keyed_model.rs and its keyed/recovery.rs child pass in debug and
optimized builds. They separately explore 22,621 binding/assignment prefixes
and 22,621 recovery/shutdown prefixes. The first rejects deliberate generation
reuse, stale mutation, accepted-work retargeting, cross-role queue consumption,
and duplicate terminal outcomes. The second rejects candidate loss and worker
restart after shutdown while checking one exact worker source, restart schedule,
timer, and worker-shutdown owner after every prefix. AA-30 now supplies the first real
assignment-authority consumer, and docs/atomic-runtime-settlement.md owns the
retained total interpretation and terminal-custody contracts. This oracle does
not duplicate either implementation.
The actor-model laws used here are isolated processing of one communication, communications to known recipients, fresh actor creation, and explicit next behavior. Direct worker ownership, key selection, affinity, bounded per-role backlogs, bounded binding retention, generation-safe reuse, opaque completion authority, recovery, action settlement, and actor-graph drain are derived constructions or deliberate Bombay policies. The actor model does not supply keyed-pool semantics.
The keyed pool is one independent aggregate transition. It is not a FIFO-pool wrapper, supervisor, stable proxy, nested behavior, or specialization that stores or forwards another template's state. It may compare AA-30's recorded value laws, but it owns distinct member, partition, binding, management, and transition sums. It may reuse only the separately proven direct-worker and assignment laws; it never depends on FIFO aggregate state.
Runtime route selection is owned normatively by
docs/atomic-runtime-settlement.md. The pool issues and stores only a
CreationId for each direct worker creation; it never chooses or stores a
runtime route before emitting CreateChild. Any older reservation label below
denotes that creator-visible correlation before interpretation and routed
evidence only in the returned runtime settlement. It does not authorize a
separate route request, waiting state, or public route.
The clean-room crates/actors keyed pool is subordinate implementation evidence
against this model, never authority for it. It owns direct workers, bounded
generation-safe bindings, Unbind, typed management outcomes, and per-role
backlogs. Any implementation that instead nests a WorkerPool, routes through
stable-proxy nonces, retains unbounded generationless bindings, omits typed
management, or admits work to a stopping role is the obsolete architecture
falsified by this model; it is not a parallel keyed-pool design.
The model must falsify designs that make any of these claims false:
- The worker roster is non-empty, ordered, and contains each semantic role once.
- Every installed or replacement worker is freshly created. Role, key, binding generation, route, and correlation are not actor identity.
- One submitted key is separate from its owned payload and is evaluated by one concrete selector only when no binding is retained.
- A binding proposed by admission commits if and only if the same transition accepts the submitted customer obligation. An explicit management rebalance may instead bind an absent key without submitting work.
- Binding capacity and per-role backlog capacity are distinct bounds. Zero backlog capacity lawfully permits immediate assignment only.
- Every accepted job permanently retains its admitted role. Rebalance, unbind, retry, and worker replacement cannot retarget it.
- Work for one role never consumes another role's backlog capacity or idle worker.
- Every live member phase projects to exactly one of
AssignableNow,BacklogAdmissible, orUnavailable, without a wildcard or inferred readiness. - No serviceable queued job for a role coexists with that role in eligible idle state after a completed transition.
- Each role has at most one active assignment. Completion matches opaque authority, exact worker-birth evidence, retained role, and active phase.
- Rebalance and unbind change future admission only. Every accepted queued or assigned job retains its original role and single customer obligation. Every management mutation is guarded by an exact absence or generation expectation.
- Reusing an unbound key receives a fresh opaque binding generation. Late management inputs for an absent or different generation cannot mutate it.
- The transition that makes a role permanently unusable returns that role's queued and assigned work once, removes every retained binding to the role atomically, releases binding capacity, and diagnoses each removed key and generation. Temporary recovery retains bindings.
- Shutdown closes admission, returns every accepted job from every role once, removes all bindings, and drains every owned or still-creating worker.
- Stale, duplicate, foreign, wrong-role, wrong-generation, wrong-worker- birth, and contradictory inputs preserve current ownership and have an explicit diagnostic disposition.
- Every accepted input produces one next state and one complete named action product. No effect is ambient.
“Atomic” means one local Behavior transition commits one keyed-pool state and its
complete Actions. It does not claim atomic delivery, creation, worker
execution, external activation, or customer observation across actors.
Model vocabulary
The following names are oracle vocabulary, not proposed public Rust names.
Role = one semantic worker label, unique inside this pool
RoleOrder = immutable declaration order retained for the pool lifetime
Key = caller-supplied semantic affinity key
BindingGeneration = fresh opaque non-reused correlation
Binding = { key: Key, generation: BindingGeneration, role: Role }
AdmittedBindingEvidence = opaque copyable { generation: BindingGeneration, role: Role }
WorkerBirthAttempt = fresh non-reused CreationId issued by the pool
RoutedWorkerBirth = interpreter-private { attempt: WorkerBirthAttempt, route }
WorkerBirthEvidence = opaque non-routable evidence for one committed birth
WorkerRecipient = pool-private routable capability for one committed worker
CurrentWorker = { recipient: WorkerRecipient, evidence: WorkerBirthEvidence }
RequestCorrelation = caller-authored opaque submission correlation, echoed only
JobId = fresh non-reused customer-visible correlation issued by the pool
AdmissionOrdinal = fresh non-reused private total order for one accepted job
AssignmentId = fresh non-reused private correlation for one dispatch attempt
CompletionAuthority = opaque affine authority bound to assignment and worker birth
CompletionCorrelation = pool-retained non-authorizing half of that authority
ManagementRequest = fresh caller-visible correlation
BindingExpectation = Absent | Exact(BindingGeneration)
RebalanceCommand = {
request: ManagementRequest,
key: Key,
expected: BindingExpectation,
target: Role,
reply: ManagementRoute,
}
UnbindCommand = {
request: ManagementRequest,
key: Key,
expected: BindingExpectation,
reply: ManagementRoute,
}
ManagementCommand = Rebalance(RebalanceCommand) | Unbind(UnbindCommand)
RecoveryTicket = fresh non-reused correlation for one role recovery
ActivationTicket = fresh non-reused authorization occupancy correlation
BindingGeneration, like every other correlation above, is not an actor
identity. It is allocated freshly for a newly committed binding rather than
derived from a per-key counter. Removing a binding therefore leaves no
permanent key tombstone. A later use of the same key obtains a fresh generation
from the fallible allocator.
Binding is the sole pool owner of its retained Key. A job accepted through
that binding receives only AdmittedBindingEvidence; it does not clone, own,
or keep the key. Multiple jobs may copy the non-authorizing evidence without
creating a binding, changing affinity, or extending binding retention. A
rejected admission or management command returns its one complete owned key.
The selector is one concrete statically known function Fn(&Key) -> Role. It
cannot select an address, nonce, worker birth, customer route, assignment lane,
or behavior implementation. Its output is validated against the immutable
semantic roster. It is evaluated at most once for one unbound submission and
is not evaluated for an already-bound key.
Every correlation allocator is total:
Reservation<T> = Reserved(T) | Rejected(Exhausted | Collision { candidate: T })
Rejection never wraps, overwrites, reuses, or guesses a correlation. A later transition table must name the complete owner returned by every allocation failure before this model can be complete.
Construction domain
KeyedPoolDefinition = {
initial_factory,
roles: NonEmptyOrderedUnique<Role>,
selector: one concrete Fn(&Key) -> Role,
activation: ActivationContract,
activation_limit: PositiveMaximum,
recovery: RecoveryPolicy,
backlog_capacity_per_role: NonNegativeMaximum,
binding_capacity: PositiveMaximum,
interruption: Fail | Retry,
actor_drain: ActorDrainPolicy,
diagnostics: DeliverTo { route } | Terminate,
}
ActorDrainPolicy =
WaitForActorGraph
| RetireActorGraphAfter { deadline }
Construction fails for an empty or duplicate roster, zero activation limit, zero binding capacity, invalid drain deadline, missing selector or policy, or a worker protocol that cannot accept the exact assignment product. Backlog capacity is non-negative because zero is immediate-assignment-only. There is no global backlog capacity and no FIFO placeholder selector.
The worker sum may be heterogeneous only when every variant accepts one common concrete assignment protocol and produces one common concrete result type. Static closed sums are allowed; erasure, a runtime registry, and downcasting are not.
AA-40 selects no builder spelling, typestate, public identifier representation, trait, wrapper, alias, or macro.
Top-level ownership state
KeyedPoolState =
Operating {
definition,
roles: OrderedRoleTable,
bindings: BoundedBindingMap,
activation: ActivationCapacity,
budget: RestartBudget,
allocators,
}
| Draining {
definition,
roles: OrderedDrainRoleTable,
bindings: DrainingBindingMap,
deadline: DeadlinePhase,
allocators,
}
| Stopped
The Rust aggregate also has Constructed, which owns the prepared worker
roster before initialization, and ForcedRetirement, which owns unresolved
retiring workers plus the exact shutdown-ID or deadline failure after a
terminal transition. The sketch above describes the operating law; those two
additional source states preserve initialization rejection and terminal
custody. A real interpreter still has to prove transfer of the
ForcedRetirement value to the parent or root custodian.
OrderedRoleTable has exactly one RoleCell for each declared role. One cell
owns both that role's member phase and its FIFO queue; there is no separately
mutable member map and partition map whose phases can disagree. Roles are never
inserted, removed, compacted, or reordered.
Retired membership remains a terminal role cell so late inputs and binding
removal never depend on index arithmetic. Irrecoverable is a transition
cause, not a second retained terminal phase.
BoundedBindingMap owns at most binding_capacity entries. It contains only
currently retained bindings. Absence is not stored. Unbind and the transition
committing permanent role unavailability delete entries and release capacity
immediately. Queued and assigned work does not keep a binding entry alive; it
owns admitted-binding evidence and its admission record independently.
Each role partition owns one FIFO queue with its own full
backlog_capacity_per_role. A job admitted to role R can inhabit only R's
queue or active assignment state. It cannot consume the queue budget of role
S, even when S is idle. Retry reinserts within the same role by immutable
admission ordinal rather than unconditional front insertion.
Accepted-work ownership
CustomerObligation = {
job: JobId,
admitted_at: AdmissionOrdinal,
admitted_binding: AdmittedBindingEvidence,
customer: CustomerRoute,
retained_payload,
}
QueuedJob = {
obligation: CustomerObligation,
origin: NeverAssigned | Retried { interruption },
}
AssignedJob = {
obligation: CustomerObligation,
assignment: AssignmentId,
completion: CompletionCorrelation,
worker: CurrentWorker,
}
One accepted job has one authoritative CustomerObligation. It is owned by
exactly one per-role queue, exact member assignment, or terminal settlement.
The admitted-binding evidence is historical correlation data; it neither owns
the key nor preserves or recreates a removed binding. The caller's
RequestCorrelation is moved into the emitted Accepted admission outcome
and is not retained in actor state. Retry may retain a canonical payload while
an execution value has escaped to a worker, but that represents one semantic
obligation and explicitly at-least-once execution, not exactly-once external
side effects.
Total admission target eligibility
Admission never asks whether a role is merely “alive” or “eventually ready.” It exhaustively projects the exact member phase:
AdmissionTargetEligibility =
AssignableNow { current_worker }
| BacklogAdmissible
| Unavailable { reason }
AssignableNow = ReadyIdle
BacklogAdmissible =
Prepared
| Creating
| Initializing
| WaitingForActivationAuthorization
| ActivationDispatched
| Activating
| ReadyBusy
| RecoveryAdmitted
| DrainingPreReady { after_drain: ResumeRecovery }
Unavailable =
Stopping
| Retired
| DrainingPreReady { after_drain: Retire }
| ShutdownOwned
The projection is a total match over the eventual complete keyed-member sum.
DrainingPreReady is classified from its stored exhaustive disposition, not
from a cause, timer, or inferred future. A new or existing binding can accept
to BacklogAdmissible only when that role's queue has capacity.
For an unbound submission, selection, role validation, binding-generation reservation, job/ordinal reservation, capacity checks, and either immediate assignment preparation or queue insertion preparation all succeed before one binding and one customer obligation commit together. Any rejection returns the complete submission and retains neither proposed binding nor job. The detailed failure and transition sums are enumerated below.
Generation-safe management
Management target eligibility is a separate total projection because a rebalance admits no job and therefore asks neither immediate assignability nor queue capacity:
ManagementTargetEligibility =
Bindable
| Unavailable { reason }
Bindable =
Prepared
| Creating
| Initializing
| WaitingForActivationAuthorization
| ActivationDispatched
| Activating
| ReadyIdle
| ReadyBusy
| RecoveryAdmitted
| DrainingPreReady { after_drain: ResumeRecovery }
Unavailable =
Stopping
| Retired
| DrainingPreReady { after_drain: Retire }
| ShutdownOwned
An unknown role is UnknownTarget, not an eligibility member. An
irrecoverable input synchronously chooses and commits a permanently unavailable
member transition; it is never retained as a parallel member phase. Temporary
RecoveryAdmitted and DrainingPreReady { ResumeRecovery } remain bindable
and retain existing bindings. Entering terminal pre-ready drain, Stopping,
Retired, or global shutdown ownership atomically extracts every binding for
that role. Thus no permanently unusable role retains future-admission affinity.
The management protocol is exhaustive:
ManagementOutcome =
BoundAbsent {
request,
current: AdmittedBindingEvidence,
}
| Rebalanced {
request,
prior: AdmittedBindingEvidence,
current: AdmittedBindingEvidence,
}
| RebalanceUnchanged {
request,
current: AdmittedBindingEvidence,
}
| Unbound {
request,
removed: Binding,
}
| AlreadyUnbound {
command: UnbindCommand,
}
| Rejected {
command: ManagementCommand,
reason:
StaleExpectation { actual: Absent | Exact(BindingGeneration) }
| UnknownTarget
| TargetUnavailable
| BindingCapacityExhausted
| GenerationReservationRejected
| ShuttingDown,
}
Successful management outcomes need not echo a key: absent rebalance moves
the command's key into Binding; successful existing rebalance retains the
table's existing key; successful unbind returns the removed Binding and its
key. Every rejected outcome returns the complete command unchanged.
ManagementRoute follows the same explicit capability-clone custody law as the
customer route. The transition prepares one target clone before committing a rejected
outcome so the original route can remain inside the returned command. A
successful outcome consumes the command route as its delivery target and does
not retain it. Rejected outcome delivery returns the target clone and the
complete command, including the original route. This duplicates routable
capability data only; request, key, expectation, and generation evidence remain
single semantic values.
For a key absent from BoundedBindingMap:
| Input | Result |
|---|---|
Rebalance { expected: Absent, target } and target is Bindable, capacity remains, generation reservation succeeds | Move the command key into Binding { fresh generation, target }; emit BoundAbsent. |
Rebalance { expected: Absent, .. } but target is unknown/unavailable, capacity is full, or generation reservation rejects | Preserve absence; return the complete command in the exact typed rejection. |
Rebalance { expected: Exact(_), .. } | Preserve absence; return the complete command as StaleExpectation { actual: Absent }. |
Unbind { expected: Absent, .. } | Preserve absence; return AlreadyUnbound with the complete command. |
Unbind { expected: Exact(_), .. } | Preserve absence; return the complete command as StaleExpectation { actual: Absent }. |
For a key bound at exact generation g to role r:
| Input | Result |
|---|---|
Either command with expected: Absent or Exact other than g | Preserve the binding; return the complete command as StaleExpectation { actual: Exact(g) }. |
Rebalance { expected: Exact(g), target: r } and r is still Bindable | Explicit accepted no-op: preserve generation g, consume no capacity or allocator value, and emit RebalanceUnchanged. |
Rebalance { expected: Exact(g), target } for another Bindable role and fresh generation g2 is reserved | Move the stored key into Binding { generation: g2, role: target }; emit Rebalanced { prior: {g, r}, current: {g2, target} }. No second table entry exists. |
| Exact rebalance to an unknown or unavailable target, or fresh-generation reservation rejection | Preserve the complete old binding; return the complete command in the exact rejection. |
Unbind { expected: Exact(g) } | Remove and return the complete Binding, release capacity immediately, and emit Unbound. |
Expectation comparison precedes target validation. No stale command can observe a later binding and then mutate it. Role-changing rebalance prepares the fresh generation before replacing the old binding, so allocation failure cannot leave an absent entry. Same-role rebalance deliberately preserves the generation: a management request that changes no affinity does not invalidate otherwise exact later inputs.
Complete role and worker sum
RoleCell = {
role,
member: KeyedMember,
backlog: Fifo<QueuedJob>,
}
KeyedMember =
Prepared { submission, reservation, kind }
| Creating { reservation, kind, prior_stop }
| Initializing { worker, kind, init_attempt }
| WaitingForActivationAuthorization { worker, kind, permit, plan }
| ActivationDispatched { worker, kind, ticket, begin_attempt }
| Activating { worker, kind, ticket, activation_attempt }
| DrainingPreReady { worker, outstanding, cause, after_drain }
| Idle { worker }
| Busy { worker, assigned: AssignedJob, join: AssignmentJoin }
| Recovering { predecessor, stop, phase: RecoveryPhase }
| Stopping { worker, outstanding, after_stop }
| Retired { reason }
BirthKind = Initial | Replacement { recovery, replaces: WorkerBirthEvidence }
AfterPreReadyDrain = ResumeRecovery { stop } | Retire { reason }
AfterWorkerStop = Retire { reason } | StopPool { reason }
Each RoleCell owns at most one active assignment and one queue. Only Idle
is assignable. A worker becoming ready first inspects its own role queue and
commits directly to Busy when work exists; it commits Idle only when that
queue is empty.
The direct-worker creation, initialization, activation, and exact-stop law is identical to AA-30: fresh creation commits before initialization settlement; the lifecycle host owns affine initialization and activation work while the actor retains exact correlations; readiness requires committed birth, successful initialization, accepted activation, and exact readiness proof; post-commit failure drains before recovery or retirement. This document uses that shared value law but does not embed FIFO aggregate state, cursor, or global queue behavior.
Activation occupancy is derived from ActivationDispatched and Activating.
Waiting roles are released in immutable roster order. Reserving a ticket,
moving permit and plan to the activation action, and storing the dispatched
phase are one transition. Ticket failure drains the installed worker with the
complete activation ownership; it never strands a silent worker.
Recovery sum
RecoveryPhase =
Scheduling { recovery, timer, prepared, schedule_attempt }
| WaitingForTimer { recovery, timer, prepared }
| ReadyToCreate { recovery, prepared }
PreparedReplacement = {
submission,
reservation: WorkerReservation,
checked_release,
charge,
}
Recovery is one role at a time. It shares the complete Permanent | Transient | Temporary eligibility, inclusive monotonic restart limit, checked immediate or
delayed release, and exact timer-correlation law with AA-30. It owns no fixed
strategy and selects RetireRole | StopPool on topology failure. Permanent and
transient recovery retain a typed worker source; temporary recovery retains no
source. Eligible recovery selects one role through the same non-empty
worker-source request as AA-30. Bombay executes the request outside Behavior;
the keyed pool alone interprets the returned submission or rejection.
Temporary recovery preserves every binding to the role. A terminal recovery decision enters permanent role unavailability below. Factory, budget, clock, release, timer, reservation, creation, initialization, or activation failure cannot silently shrink ownership or fabricate a ready worker.
Admission protocol and outcomes
KeyedPoolInput =
Submit { request, key, payload, customer }
| Rebalance(RebalanceCommand)
| Unbind(UnbindCommand)
| Shutdown
| WorkerBirthResolved(WorkerBirthResolution)
| InitializationSettled(InitializationSettlement)
| ActivationBeginSettled(ActivationBeginSettlement)
| WorkerReady(WorkerReady)
| WorkerStopped(WorkerStopped)
| AssignmentSettled(AssignmentSettlement)
| WorkerCompleted { child: ChildRouteNonce, completion: CompletionEvidence }
| RestartScheduleSettled(RestartScheduleSettlement)
| RestartTimerElapsed(RestartElapsed)
| WorkerShutdownSettled(WorkerShutdownSettlement)
| DrainDeadlineSettled(DeadlineScheduleSettlement)
| DrainDeadlineElapsed(DrainDeadlineElapsed)
AdmissionOutcome =
Accepted { request, job, binding: AdmittedBindingEvidence }
| Rejected { request, key, payload, customer, reason }
AdmissionRejection =
ShuttingDown
| BindingCapacityReached
| UnknownSelectedRole
| RoleUnavailable
| RoleBacklogFull
| NoRecoverableWorkers
| BindingGenerationUnavailable
| JobCorrelationUnavailable
| AssignmentCorrelationUnavailable
The customer route follows one explicit clone law. Before commit, the transition
clones it once: the clone targets the admission outcome, while the original
moves to the accepted CustomerObligation or the rejected outcome. Rejected
delivery therefore returns a complete delivery containing the targeting clone
and the original route. Payload, key, customer obligation, and completion
authority are never cloned by this rule.
Admission is one transaction:
- reject global shutdown or absence of every serviceable or recoverable role;
- borrow the submitted key to search the binding table;
- for an existing binding, bypass the selector and use its exact role and generation;
- for an absent binding, invoke the selector exactly once, validate the role, check binding capacity, and reserve a fresh generation;
- project the exact role phase to
AssignableNow,BacklogAdmissible, orUnavailableand check only that role's queue capacity; - reserve fresh job and admission-ordinal correlations;
- for immediate assignment, reserve the assignment/completion pair and prepare execution and customer-route clones; preparation failure does not fall back to queuing;
- commit a proposed binding if and only if the same transition commits the customer obligation; and
- emit acceptance followed by assignment, append to that role's queue, or return the complete unchanged submission through one rejection.
No Key: Clone law follows from this transition. An absent accepted key moves
into Binding; an existing binding already owns its key and the submitted
equal key is consumed by the accepted command. Rejection returns the submitted
key. A concrete map representation may impose only the comparison or borrowing
law actually needed; compiler convenience cannot strengthen it to cloning.
Zero per-role backlog capacity permits immediate assignment and rejects
waiting ownership. A retry may temporarily make one role's queue length
capacity + 1, because that role has at most one interrupted assignment. It
is inserted by immutable AdmissionOrdinal, never at the unconditional front.
After every operating transition, independently for each role:
role.backlog.is_empty() OR role.member is not Idle
No fill operation can consume another role's queue or worker.
Assignment and completion law
The complete AssignmentJoin and affine AuthorityReunion law is identical
to AA-30 and is applied inside one RoleCell:
AssignmentJoin =
AwaitingDeliveryAndTerminal { delivery }
| DeliveryAcceptedAwaitingTerminal
| CompletionBeforeDelivery { delivery, completion, later_stop }
| StopBeforeDelivery { delivery, stop, later_completion }
Delivery acceptance, rejection, completion, and exact worker stop are accepted in every runtime-permitted order. Completion matches only the child nonce, opaque worker-birth evidence, assignment, retained non-authorizing half, admitted role, exact worker birth, and active phase. Binding-table contents are not part of completion matching and cannot retarget accepted work.
Matching completion emits one Completed outcome and fills only the admitted
role's oldest queue entry. Matching exit after accepted delivery applies Fail | Retry once; retry retains the admitted role and reinserts by ordinal in that
same role. Assignment rejection must reunite the returned affine authority
with the retained half before the proven-unaccepted job can requeue. Mismatch
or a completion that already consumed the authority transfers both inputs
terminally and begins whole-pool drain. Duplicate, stale, foreign, old-birth,
or wrong-role completion preserves current ownership and transfers the complete
result through diagnostics.
Exactly one terminal customer outcome exists for each accepted job:
TerminalCustomerOutcome =
Completed { job, role, result }
| ReturnedQueued { job, role, payload, reason }
| ReturnedAssigned { job, role, payload, reason }
QueuedReturnReason =
PoolShutdown
| RolePermanentlyUnavailable
| NoRecoverableWorkers
| DispatchCorrelationUnavailable
AssignedReturnReason =
PoolShutdown
| RolePermanentlyUnavailable
| WorkerStopped
| AssignmentReturnedUnaccepted
| RetryPreparationRejected
| ContradictoryAssignmentSettlement
Removal from the queue or member precedes emission. Rejected customer delivery transfers the complete outcome outward and never recreates work.
Permanent role unavailability
Temporary recovery retains role bindings. The transition selecting permanent
unavailability atomically creates one RoleRetirement:
RoleRetirement = {
role,
queued: [QueuedJob],
assignment: NoAssignment | AssignmentDrain { assigned, join },
bindings: [Binding],
worker: WorkerDrain,
cause,
}
In that transition:
- admission and management projection for the role becomes unavailable;
- every queued obligation is extracted in admission order and moved to one
ReturnedQueuedoutcome; - an active assignment loses retry authority and moves with its complete join
to terminal assignment settlement; it produces completion if completion had
already lawfully won, otherwise exactly one
ReturnedAssigned; - every binding whose role matches is removed from the table, releasing capacity immediately;
- each complete removed
Bindingmoves to one diagnostic containing its key, generation, role, and cause; and - the exact worker and all outstanding effects enter drain.
The table is the only key owner. Jobs retain only generation and role evidence, so binding extraction never needs to clone a key and never invalidates customer ownership. A rejected diagnostic transfers its complete removed binding to the lifecycle host; it cannot restore the table entry.
No later rebalance, unbind, completion, recovery, or worker-ready input can revive the retired role. Another role remains independently serviceable.
Required semantic action product
KeyedPoolActions = {
worker_creations,
worker_creation_observations,
worker_stop_observations,
activation_begins,
activation_cancellations,
role_assignments,
worker_shutdowns,
restart_schedules,
deadline_schedules,
admission_deliveries,
management_deliveries,
terminal_customer_deliveries,
diagnostic_deliveries,
terminal_diagnostic_transfers,
rejected_input_transfers,
forced_drain_transfers,
}
WorkerCompletionActions = {
parent_completion_reports,
}
Each field is a concrete named typed lane, not a generic pool envelope or a
flattened FIFO aggregate product. Generic item interpretation and settlement
are owned only by atomic-runtime-settlement.md:
accepted items leave their receipt, rejected items return the complete request,
blocked items retain the exact prerequisite, independent later items continue,
and corruption retains the committed prefix plus exact remainder. Creation is
the prerequisite only for operations using its uncommitted child binding.
Interpretation order is creation before its exact dependents; management and admission outcomes before assignments they announce; terminal customer outcomes before successor assignments; per-role assignments in roster order when one transition releases several roles; and diagnostics before a terminal verdict. These are Bombay policies, not actor-model guarantees.
Total mode and member matrices
| Mode | Submit | Management | Shutdown | Worker lifecycle | Assignment/completion | Recovery timer | Drain timer |
|---|---|---|---|---|---|---|---|
Operating | admit/reject | exact management table | normalize drain | settle exact role | settle exact role join | settle exact recovery | reject input |
Draining | reject unchanged | reject unchanged | idempotent | settle drain only | settle drain only | settle cancelled work | settle exact deadline |
Stopped | not admitted | not admitted | not admitted | host-owned late input | host-owned late input | host-owned late input | host-owned late input |
| Member phase | Birth | Init | Activation settlement | Ready | Stop | Assignment settlement | Completion | Restart timer |
|---|---|---|---|---|---|---|---|---|
Prepared/Creating | exact birth join | reject | reject | reject | exact pre-birth join | reject | reject | reject |
Initializing | reject | accept exact | reject | reject | exact drain | reject | reject | reject |
| waiting authorization | reject | reject | reject | reject | exact drain | reject | reject | reject |
ActivationDispatched | reject | reject | accept exact | reject | exact join | reject | reject | reject |
Activating | reject | reject | reject | accept exact | exact join | reject | reject | reject |
DrainingPreReady | settle only | settle only | settle only | stale | drain | reject | reject | settle only |
Idle | reject | reject | reject | reject | interrupt/recover | reject | stale | reject |
| busy delivery pending | reject | reject | reject | reject | join | join | join | reject |
| busy completion-first | reject | reject | reject | reject | join | join | duplicate | reject |
| busy stop-first | reject | reject | reject | reject | duplicate | join | retain stale/contradictory | reject |
| busy delivery accepted | reject | reject | reject | reject | interrupt/recover | reject | complete | reject |
Recovering | only after exact creation emission | reject | reject | reject | reject | reject | stale | settle exact |
Stopping/retirement drain | settle only | settle only | settle only | stale | drain | settle only | stale | settle only |
Retired | reject | reject | reject | reject | reject | reject | reject | reject |
Every cell means exact role, reservation or worker birth, generation where applicable, operation kind, assignment, worker-birth evidence, and phase. Mismatch preserves state and moves the complete input to one non-recursive diagnostic. Management expectations are evaluated by their separate exhaustive tables above and never by this lifecycle matrix.
Shutdown normalization and drain
The first Shutdown atomically:
- closes submission and management admission;
- extracts and removes every binding, moving each complete key, generation, and role value to terminal custody or a selected binding-removal diagnostic;
- extracts every per-role queue in role order and FIFO order and emits one
ReturnedQueued { PoolShutdown }per obligation; - removes the customer right from every active assignment and emits exactly
one
ReturnedAssigned { PoolShutdown }; any retained completion becomes stale diagnostic ownership and the remaining affine delivery join moves to drain; - disables dispatch, retry, recovery admission, and readiness publication;
- retains pending creation, initialization, activation, assignment, timer, and shutdown settlement ownership in one exact drain role;
- emits at most one shutdown per committed direct worker; and
- reserves and emits one exact deadline request for deadline policy.
DrainRole =
ResolvingBirth { role, reservation, local_values, prior_stop }
| DrainingWorker { role, worker, outstanding, stop_phase }
| Drained { role, result: Absent | Graceful | Forced { residual } }
DeadlinePhase =
WaitingWithoutDeadline
| Scheduling { timer, attempt }
| Waiting { timer }
| Fired { timer, observed_at }
Repeated shutdown emits nothing twice. Birth rejection is Absent; birth
commit is drained; commit plus prior authoritative stop is a contradiction,
not readiness. Shutdown rejection retains the request and reason while exact
stop observation remains live.
Deadline reservation or scheduling rejection cannot weaken the configured
bound into an infinite wait. It transfers every unresolved role, binding
removal, assignment join, child operation, timer, and external authority to
Forced { residual }. Exact deadline firing does the same. It fabricates no
stop, readiness, completion, acceptance, or recovery. The pool selects normal
termination only when every role is drained and every final action has an exact
settlement owner.
Stale, overlap, and conservation laws
- Key equality alone never matches a management input after generation changes.
- A role alone never identifies a worker or assignment input.
- Binding expectation comparison precedes target validation and mutation.
- Birth, initialization, activation, stop, recovery, assignment, and completion inputs advance only exact retained correlations and phases.
- Rebalance, unbind, recovery, replacement, and retry never retarget accepted work.
- One role cannot consume another role's queue capacity or idle worker.
- A removed binding generation is never reissued or inferred from arithmetic.
- Customer, management, diagnostic, and rejected-input delivery rejection never rolls back committed state and returns the complete value.
- Contradictory authoritative inputs retain both values in terminal custody.
- Diagnostic rejection is terminal and never recursively diagnosed.
After every generated step:
accepted obligations = queued + assigned + terminally settling
retained bindings <= binding capacity
each retained key has exactly one fresh generation and one role
each accepted job has one immutable admitted role
each role has at most one assignment
each job has at most one terminal customer outcome
each role queue is ordinal-ordered and <= backlog capacity + 1
role queue is empty OR its member is not Idle
permanently unavailable roles retain no binding
Feature trace and executable evidence
| Requirement family | Model evidence |
|---|---|
KP-BUILD | construction domain, concrete selector, direct-worker protocol, distinct positive binding and activation limits |
KP-AFFINITY | atomic absent binding plus job, exact eligibility, per-role capacity, immutable admitted role |
KP-REBALANCE | generation expectations, same-role unchanged, fresh role-changing generation, complete command recovery |
KP-END | complete direct-worker lifecycle, assignment join, permanent role extraction, shutdown and drain |
KP-RETENTION | sole key-owning binding table, no tombstone, fresh generation, stale denial |
The independent keyed_model uses bins, leases, work slips, and service cells
rather than these specification names. Its focused tests cover two roles and
keys, zero and one capacity limits, every member eligibility, absent and
exact expectations, same-role and cross-role rebalance, unbind and reuse, stale
generations, permanent retirement, assignment delivery/completion/stop
permutations, retry ordering, shutdown, and forced residuals. A depth-four
enumeration checks the complete invariant set after all 22,621 prefixes. Its
keyed/recovery.rs child uses a workshop, ordered bays, one recruiter, and a
candidate-ownership map. A separate depth-four enumeration checks 22,621
recovery/shutdown prefixes, including concurrent stops, declaration-order
source release, preparation returned during shutdown, all restart-schedule
dispositions, accepted-timer cancellation, and both worker receipt/exit orders.
The inversion tests corrupt each of the seven named laws independently. The
audit rejects generation reuse as GenerationReused, stale mutation as
StaleMutation, accepted-work movement as QueueLocation, cross-role
assignment as AssignmentRole, and double settlement as DuplicateTerminal.
AA-30's feature-complete production slice now supplies the first real opaque
completion and affine-authority consumer. Generic total interpretation and
terminal custody remain owned by docs/atomic-runtime-settlement.md. AA-40 is
the retained keyed semantic oracle; production lowering must reuse those
contracts rather than reopen or duplicate them.
The retained production startup checkpoint uses one passive direct-worker transition owner for both AA-30 and AA-40 successful creation, initialization, activation start, and readiness. AA-40 alone locates the role, applies activation capacity, and drains that role's queue. A Primary job queued before readiness is not consumed when Replica becomes ready; the deliberate cross-role inversion fails at that exact assertion. Foreign worker identity, wrong creation kind, foreign initialization, foreign activation, and duplicate activation start all retain the selected role and move the complete input to diagnostics. Assignment reunion, recovery, permanent role extraction, and shutdown now have clean-room production witnesses. Catalogue-wide compatibility, Bombay runtime lowering, and the final fixed-point audits remain open; this document does not claim those wider gates.
The production recovery suite also crosses shutdown after the exact restart schedule has been accepted and before its timer arrives. A drop-tracked worker submission remains owned by the terminal cancellation diagnostic until that action is released; shutdown emits no replacement creation and stops immediately. Deliberately dropping the submission during the shutdown transition fails at the ownership assertion.
Engineering records
These documents preserve the design campaign behind Bombay Behavior: research classification, candidate type inventories, acceptance targets, verification evidence, downstream-impact analysis, and audit results. They remain published for traceability, but they are not an additional public API surface.
The canonical contracts, actor catalogue, normalized aggregate laws, current crate documentation, and production types govern whenever terminology or an intermediate proposal in these records differs from the retained design.
The repository quality audit and checklist records the 2026-09-28 review of all workspace crates, verified gaps, and completion criteria for further simplification and testing work.
The interpreter ownership and startup PRD uses the current working code as its baseline. It specifies complete assignment and proxy rejection return, generic retained acceptance, coherent creation and initialization custody, and downstream interpreter acceptance tests. It also defines staged implementation gates before broader consolidation.
Repository quality audit and completion checklist
Audit date: 2026-09-28. Baseline: 435560ce7bea8ad3330ee2d42e5034f837a80602.
Scope: all five workspace crates, their test and benchmark targets, the nested
macro fixture and fuzz workspaces, public documentation, and verification gates.
The repository has a substantial typed algebra and meaningful adversarial tests. It also has gaps between its documented contracts, current composition surface, and what the tests prove. Address those gaps before expanding the catalogue. The best initial reductions are redundant test interfaces and dependencies, repeated effect-product machinery, and stale documentation. Aggregate state must be reduced only after proving that the removed distinction is unnecessary.
This is an audit and work list, not a claim that every branch is verified. All crates were inventoried; manual review covered public entry points, effect and creation interpretation, every actor family, representative transitions and test oracles, macros, and tooling. Source scans supplement that review. No line or branch coverage measurement, complete mutation campaign, or downstream Bombay Engine integration run was performed for this audit. Production code and existing tests were not changed at the audited baseline. Repairs made on the working branch are recorded below.
Inventory and interpretation of the evidence
Counts below are physical Rust lines, including comments and inline tests. They measure review surface, not essential complexity or production-only size. Integration counts include Rust support modules and macro fixture sources.
| Crate | src files / lines | Integration and fixture files / lines | Responsibility and audit focus |
|---|---|---|---|
bombay-behavior | 10 / 6,844 | 14 / 3,739 | Pure algebra, custody, structural composition, interpreter contracts |
bombay-behavior-actors | 125 / 58,913 | 42 / 35,188 | Catalogue policies, lifecycle state, ownership, public construction |
bombay-behavior-macros | 1 / 1,503 | 9 / 215 | Parsing, generated products, dependency resolution, diagnostics |
bombay-behavior-testkit | 2 / 182 | 30 / 7,435 | Independent oracles, finite driver, properties and compositions |
behavior-mutants-gate | 1 / 278 | 0 / 0 | Mutation verdict integrity; three inline unit tests |
The fuzz manifest declares 21 binaries. Its target directory has 28 Rust files; the additional files include support modules and must not be counted as seven missing campaigns. The scheduled workflow currently lists all 21 binaries. There are also two benchmark programs and one core example.
Evidence labels used below:
- Confirmed: directly visible in source or reproduced by a focused probe.
- Coverage gap: the cited test or gate cannot establish the stated law; this does not by itself prove a production failure.
- Design candidate: a bounded investigation with explicit acceptance criteria; it is not authorization to introduce a new abstraction.
Priority: P1 affects contract correctness or trust in verification; P2 affects maintainability, developer experience, or breadth of evidence; P3 is supporting cleanup. Check an item only after its completion evidence is recorded against a revision.
Behavior release boundary
This branch completes and releases the five Behavior workspace crates. A17's
production Bombay interpreter witness and the PRD's Bombay/Address runtime
packages follow the Behavior release against immutable published versions.
They remain open handoff obligations with their original acceptance criteria;
their absence must not be described as a passing downstream test or silently
used to change the Behavior algebra. Finish the Behavior-owned A12, A13, and
A20 reviews and required Behavior gates before merging this branch to main.
The release workflow creates its version PR after successful main CI, then
publishes from the versioned main revision. Record those CI and publication
outcomes separately from the later Bombay integration result.
P1 — close contract and verification gaps
-
A01 — Reconcile creation, installation, and initialization ordering. Confirmed document conflict.
docs/actor-transition-algebra.md, “Fresh creation,” orders initialization and its effects before installation.docs/atomic-runtime-settlement.md, “Initialization and activation order,” orders installation commit before the pure initialization fold, and describesInitializeWorkerafterChildCreationOutcome::Established.actor/creation.rs::ChildCreationOutcomeadditionally represents initialization and host rejection before successful establishment. A runtime author cannot implement both descriptions as one unconditional order. Complete when: one normative contract distinguishes definition initialization, effect settlement, endpoint establishment, activation, and ordinary ingress; all canonical docs agree, and an interpreter trace proves the order plus each rejection and initialization-stop case. Any distinction between ordinary and atomic workers must be explicit and compositional. Classification: deliberate Bombay policy and derived composition, not a new actor-model guarantee. Resolve the law before changing production semantics. -
A02 — Support ordinary, unrenamed Cargo dependencies in both macros. Confirmed source defect; external probe recorded below.
behavior-macros/src/lib.rs::crate_pathemits the name returned byproc_macro_crate. The manifests expose libraries namedbehaviorandbehavior_actors, while unrenamed package keys arebombay-behaviorandbombay-behavior-actors. Facade resolution already compensates for this package/library distinction; direct dependency resolution does not. Current fixtures rename the direct dependencies, which avoids the failing case. Complete when: external consumers compile#[behavior]and#[pool_worker]with unrenamed dependencies, renamed dependencies, facade only, renamed facade, and direct plus facade dependencies. Preserve the existing missing-dependency diagnostic. Use a failing consumer fixture before editing expansion paths. -
A03 — Restore logical-host projection through atomic actor products. Confirmed missing composition.
actors/src/atomic/requests.rsderives send interpretation and settlement but notLogicalDeliveryProtocols.FifoRequests,KeyedRequests,FixedSupervisorRequests, andDynamicSupervisorRequestsuse this macro. The handwrittenProxyEffectsalso lacks this projection. There are no atomic implementations inactors/src/requirements.rseither. The currentbehavior-testkit/tests/logical_host_requirements.rsproves a syntheticDeliveryOutcomestree, not these actual atomic families. Complete when: real FIFO, keyed, fixed, dynamic, and proxy compositions satisfy the documented projection, including transitive children and two wrapper orders. Assert exact ordered protocol products and duplicate occurrences. AuditCustomerDeliveryandDiagnosticActionas part of the law: their logical routes must not disappear merely because they travel inInterpreterRequests, whose current projection is empty for every item. An empty blanket implementation is not a repair. -
A04 — Make compile-denial tests fail for the intended reason. Confirmed false-positive examples. The
ChildRoutecompile-fail example inactors/src/composition/message_adapter.rsimports a removed type, so it does not prove rejection of a current creator-local capability. TheActivationPermitduplicate-move example inactors/src/atomic/worker/initialization.rsomits the requiredW: Behaviorand endpoint bounds. It can fail before testing affine ownership. The incomplete-interpreter example inbehavior/src/actor/creation.rsalso used an implementation signature that obscured the missing child-host bound. Complete when: each safety fixture has a compiling lawful counterpart; one deliberate invalid operation produces the intended diagnostic. Exercise the counterfactual by removing that invalid operation or weakening the relevant invariant in an isolated probe. Keep removal-of-obsolete-name tests separate from capability-denial evidence. Rustdoccompile_failalone proves that compilation fails, not why it fails. -
A05 — Make the mutation verdict reject malformed and incomplete runs. Confirmed source defects; adversarial probe recorded below.
mutants-gate/src/main.rs::usablerejects a failed baseline if present but does not require a successful baseline.talliescountsFailureandSuccessmutant outcomes toward completion while treating them as neither missed nor timed out.checkmatches counts per function, so duplicated outcomes can substitute for a different candidate in that same function. Three tests cover a clean run, a survivor, and a failed baseline only. Complete when: the gate verifies an explicitly supported baseline mode, exact candidate/outcome correspondence, valid outcome categories, missing and duplicate outcomes, unknown candidates, timeouts, viability collapse, stale baseline entries, malformed input, and command exit status. Include a case with a caught mutant plus a failed mutant at an otherwise satisfied floor. Reuse the report's actual candidate identity; function counts alone cannot prove campaign completeness. -
A06 — Strengthen partial property-test oracles to complete observations. Coverage gap. In
behavior-testkit/tests/catalogue_models.rs, the sequencer model checks delivery payloads and state, but not outcome messages, destinations, creation emptiness, or the next verdict. The order-gate model similarly checks selected deliveries. Incatalogue_invariants.rs, successful configuration and readiness updates often checkresult.is_ok()and state, discarding successfulActions. Some deduplication checks inspect element zero without excluding extra elements. These tests are useful, but do not support the existing audit's claim of complete outputs after every step. Complete when: each generated step checks every declared effect lane, cardinality and order, destination, rejected owned input, and next verdict. Check initialization too. Use distinct owned payloads for custody-sensitive cases. Demonstrate failures for extra replies, wrong recipients, unexpected stopping, and dropped ownership; retain an independently structured oracle.
P2 — improve verification reach and remove repeated machinery
-
A07 — Extend mutation evidence to the actor catalogue. Coverage gap. Both mutation derivations in
flake.nixselect only--package bombay-behavior; test packages are core and testkit. They do not mutatebombay-behavior-actorsor run its integration tests as a selected mutation test package. A strict core verdict says nothing about the largest crate's survivors.actors/tests/mutation_contracts.rsis an ordinary test file, not evidence of an actor mutation campaign. Complete when: independently reviewable actor-family campaigns exercise their relevant unit, integration, and model suites; survivors have a killed regression or a reviewed equivalence argument. Ratchet viability separately from mutant detection, and label coverage by crate and law. Fix A05 first. The current candidate listing forbombay-behavior-actorscontains 3,125 mutations; no actor-wide campaign verdict is claimed by this branch. Verified selected-slice gate:nix build .#mutants-actors --no-link --no-write-lock-filepassed four independently ratcheted actor campaigns. Health caught 7/7 viable mutations; WorkQueue caught 2/2 viable among four candidates; PubSub membership caught 6/6; publication caught 1/1 viable among five candidates. The remaining six whole-function replacements could not compile. All four reports had zero missed viable mutations and zero timeouts.mutants/actors/*.jsonrecords separate viability floors by function; the Nix command runs actor and testkit suites. Other actor laws retain their focused campaign evidence below, and A20 tracks their broader law coverage without claiming an actor-wide mutation verdict. -
A08 — Make external-consumer and packaging gates exercise their claims. Confirmed gate gap.
behavior-macros/tests/crate_resolution.rs::facade_package_sibling_targets_resolve_the_library_cratecallscargo check -p bombay-rswithout--examplesor--all-targets. The intended witness isfixtures/facade/examples/sibling_target.rs, which defaultcargo checkdoes not select. The Nix package check only lists archive contents; the script's archive mode uses--no-verify. Complete when: explicit target selection checks the sibling example and both macro entry points; deliberately breaking that example breaks the gate. Build extracted published packages in a suitable release lane and compile a minimal consumer of the actual documented dependency declarations. Record package-content checks separately from package build verification. -
A09 — Remove test-only indirection and unused test dependencies. Confirmed simplification.
behavior-testkit/src/lib.rs::InitializeTestforwards unchanged tobehavior_actors::Activateand adds a second spelling of the same operation.criterionis a dev dependency, but the benchmark is a handwrittenmainand no Rust source uses Criterion. Four testkit files contain 15#[tokio::test]tests collectively and no.awaitat all:stash_properties,fsm_properties,compositions, anderror_paths. Several destination-only fixtures implement an inertBehavioreven though the route contract needs onlyProtocol. Complete when: callers importActivatedirectly, unused dependencies disappear from manifests and the lockfile where appropriate, synchronous tests use the ordinary harness, and destination fixtures keep only required capability implementations. Preserve fixtures whose actual purpose is to test behavior or creation. Report deleted surface and compile impact. -
A10 — Define what the finite test driver preserves on error and in time. Confirmed limitation.
behavior-testkit/src/lib.rs::drivereturns onlyB::Erroron a later failed transition, dropping accumulated successful effects and the active behavior.Tracealso appends products lane by lane, losing the boundaries between turns. Interpreting that accumulated product afterward cannot establish per-turn runtime effect order. The error test fails on the first event and checks only the remaining mailbox length. Complete when: a success-success-error case exposes or explicitly documents the successful prefix's custody, and order-sensitive tests inspect per-turn actions or an independent interpreter trace. Do not describe the accumulation driver as a full runtime witness. Reuse existing action and settlement products if a richer error observation is needed. -
A11 — Give named effect-product derivation one maintained implementation. Implemented and verified. Eleven generic named actor products now use
SendProductand#[behavior]generated products use the same procedural generators for send effects, logical-host projection, ordered interpretation, settlement classification, and source custody. The two settlement representations remain explicit. The oldsend_product!module,BufferSends, andatomic::request_product!were removed.requirements.rsretains only the exceptionalReplyDeliveriesandHeterogeneousShutdownSendsprojections. The unused publicbehavior::settle_in_ordertuple helper was removed after a source search found no Rust caller in this workspace or adjacent Bombay/Address repositories. Named products generate their ordered traversal directly; the normative settlement document states that law. Complete when: a law table compares complete equations before selecting shared machinery. A retained derivation must preserve semantic field names, declared order, corruption suffixes, source admission, and logical-host projection, and delete repeated implementations. Prove two unrelated real products and both wrapper orders before catalogue migration. Do not merge products with different ownership or retirement laws, or introduce a public product framework merely to save typing. Evidence: the equation inventory, two unrelated actor products, source-custody tests in bothSendLayerorders, and workspace--all-targetstests support the retained design. A clean Nix gate ona8f15b7passed all ten active aarch64-darwin checks, including 843/843 optimized Nextest tests. -
A12 — Reassess aggregate decomposition using retained current values. Design candidate. FIFO's root has 4,097 lines, stable proxy's root 3,506, fixed recovery 3,080, and dynamic supervisor's root 2,839. These counts include comments and tests; they are review triggers, not evidence of redundant states. FIFO keeps most transition concerns at its visibility root, while fixed supervision spreads substantial transition authority among roster, recovery, start, outcome, and shutdown implementations. Several roots use
mem::replace(..., Stopped)or a dormant placeholder during a transition. Complete when: each family has the full AGENTS aggregate-drift record, including before/after states, alternatives, branches, production lines, modules, public spellings, and the future-needed value in every subordinate alternative. Investigate direct owned-data joins and one commit point; preserve distinctions needed for rejection, concurrency, or terminal custody. Reduce responsibilities only where a falsifying law proves redundancy. Keep FIFO assignment policy and keyed binding policy distinct. Reviewed distinction: StableProxy's data-freeDormantandEmptyInitialstates are not duplicate history labels.Dormantadmits its one initial worker submission;EmptyInitialfollows a rejected initial creation and rejects another submission with the observableOverlapandProxyPhase::EmptyInitial.proxy_command_recoveryandproxyexercise this path. Merging the states would admit a second initial worker or change the documented outcome. The other subordinate alternatives and family measurements still require the full A12 checkpoint. Dynamic supervision'sWorkerChangeDisposition::{Committed,Cancelled}retains the answer to a future repeated cancellation. InDynamicEntryPhase::Available, the disposition selects the same committed or cancelled receipt again.dynamic::ready_service_accepts_one_replacement_on_its_current_proxyanddynamic::accepted_start_cancellation_retires_before_fresh_key_reuseexercise these answers. Deleting the disposition would change that later reply even though service availability is the same. This read-only distinction does not close the wider family audit. The stopped and forced-retirement alternatives in StableProxy, fixed supervision, FIFO, and keyed pooling still own workers, submissions, shutdown results, or rejection causes for terminal custody. Their fields have no local read path in some cases because the whole stopped behavior is retained for the interpreter. The downstream terminal-return witness in A17 and the interpreter ownership PRD must prove each transfer before any such alternative can be removed. This is a custody question left open by the source-only review.Decomposition baseline at
e9c8d8c(read-only): the figures below count physical lines in each family source tree, including comments and embedded tests. Production lines exclude whole spans under#[cfg(test)]but retain comments and blank lines. Match arrows are a search diagnostic, not semantic transition branch counts.Family Root control representation Source modules Physical lines Production lines Match arrows Still required Stable proxy ProxyState: 8 alternatives6 5,946 5,076 398 Enumerate each nested shutdown and retirement value against exact terminal custody. Fixed supervisor FixedRoster: 5 alternatives; recovery owns a separate 2-way policy state19 10,104 9,512 718 Check roster/recovery joins and every retained prepared worker. Dynamic supervisor SupervisorAvailability: 2 alternatives; each keyedDynamicEntryPhasehas 14 alternatives8 4,120 4,020 246 Check whether per-key transitions remain an entity invariant rather than a second aggregate authority. FIFO pool PoolState: 5 alternatives5 4,783 4,783 311 Check backlog, cursor, per-member custody and forced retirement separately. Keyed pool KeyedPoolState: 5 alternatives10 5,755 5,536 268 Check binding generations and per-role order without importing FIFO policy. These sums have no proposed deletion yet. The source scan found no semantic
boolfield in these families; the observed boolean signatures are membership or equality predicates. TheFifoDispatchalternatives that return the same queued job still select different lawful actions: unavailable worker may enqueue it, while exhausted assignment correlation returns an explicit rejection. Fixed recovery'sLeaveEmptyandSourceUnavailableboth retain recovery state but select a lifecycle fact versus exact input rejection. Those alternatives cannot be merged by payload shape alone. This baseline does not replace the future-needed-value and production measurements for every subordinate alternative; A12 remains open.A12 single-worker creation batch, pre-edit law: StableProxy stages one worker creation per start. Its creation settlement therefore consumes exactly one matching result; a zero- or multi-item settlement is an unexpected input that must return the complete original ordered batch to the owner while the proxy remains in its creating phase. This is Bombay's derived staged-creation correlation policy, not an actor-model allocation law. The public caller syntax and observable transition stay unchanged. The focused
proxy_command_recoverymalformed-batch regression covers the complete returned batch. Existing lower-order products areCreationSettlement,CreationsSettled,WorkerCreation::Unexpected, and the owner diagnostic; no new type, bound, port, or interpreter operation is needed. The candidate implementation uses the owned vector's exact-one conversion so a failed cardinality check returns every item without cloning or reconstructing a different batch. This local simplification does not close the family-wide A12 inventory.A12 single-worker creation batch, retained checkpoint: Root
ProxyStateremains 8 alternatives before/after; the touchedWorkerCreationsum remains 3 andCreationSettlementremains 3. Its three top-level settlement branches and five reachable cardinality paths are unchanged; two impossiblepop() == Nonebranches are gone. The touched production method is 83 → 70 lines; the six-module family is 5,959 → 5,946 physical source lines, including unchanged embedded tests, and 5,089 → 5,076 production lines after excluding the 870 lines under#[cfg(test)]. Public spellings and module count are unchanged.WorkerCreation::Initializingstill owns the committed worker, activation, and possible prior stop;WorkerCreation::Rejectedowns the rejected worker, activation, and possible prior stop;WorkerCreation::Unexpectedowns the pending worker, possible prior stop, and the entire anomalous batch for the owner diagnostic. No arrival-history label, repeated cause, false cardinality, nested transition authority, semantic boolean, or structural caller syntax was added. Cross-checkeddocs/actor-laws/proxy.md,docs/engineering/atomic-actor-essence.md, anddocs/engineering/atomic-actor-retained-core.md; the focused 53-test proxy recovery suite passes. Disposition:passfor this local simplification; A12 remains open for the complete family inventory and terminal custody.A12 exact-one creation batch, pre-edit law: Actor research requires fresh creation but does not prescribe a Rust batch API. Bombay's ordered
Creations<Item>is a derived effect product. A single-child aggregate may consume exactly one result; if the batch has zero or multiple items, the conversion must return the complete original batch in order. The intended caller syntax iscreations.into_one(), returningResult<Item, Creations<Item>>withoutClone. A focused external caller test will require one move-only item to succeed and empty/two-item batches to return intact; the prior API fails that test because it has no such operation. The existing lower-order value is the privately ownedVec<Item>inCreations; its exact-one array conversion already appears twice in StableProxy, while DynamicSupervisor manually checks length then has an unreachable empty branch. The design stage adds only the batch operation and its focused test. Separate migration stages will apply that proven operation to those two aggregates, preserving their currentCreationSettlementoutcomes and interpreter requirements. Before editing, the core batch has no state sum; StableProxy has eight root states and six modules, DynamicSupervisor has two root availability states, fourteen entry phases, and eight modules. No new actor state, effect lane, trait bound, interpreter capability, or type is proposed. The public batch method count grows by one in the design stage; branch and line measurements follow each retained stage. The external caller initially failed with threeE0599diagnostics at the intendedinto_onecalls. After the core method was added, the focused Nix-pinned test passed for one, zero, and two move-only workers. The design stage adds 13 production source lines and 22 net test lines; it changes no aggregate state, subordinate alternative, transition branch, module, or interpreter path. On failure, the originalVec<Item>is returned inside the same orderedCreationsvalue; there is no clone, dropped item, arrival history, repeated cause, false cardinality, nested authority, semantic boolean, or positional caller syntax. The actor transition and retained-core creation laws were cross-checked. Disposition:passfor the batch API; aggregate migrations and their separate checkpoints remain open. A12 exact-one aggregate migration, retained checkpoint: StableProxy now consumes rejected and settled worker batches through that operation. Its eight root states, threeWorkerCreationalternatives, six modules, and public spellings are unchanged; the two cardinality success/failure choices are unchanged. Its production source falls by two lines, from 5,076 to 5,074. DynamicSupervisor now consumes its proxy-settlement batch through the same operation. Its two availability states, fourteen entry phases, eight modules, and public spellings are unchanged. The same reachable success/failure choice remains, while the old impossiblenext() == Nonebranch afterlen() == 1is gone. Its production source falls by five lines, from 4,020 to 4,015. The dynamic regression now checks empty and two-item rejection as the exactProxyCreationsSettledevent, including both untouched routes, creation IDs, kinds, item order, and the retainedCreatingProxyphase; that test changes by+47/-3lines. All 29 dynamic and 53 proxy recovery tests pass with the Nix toolchain. The full malformed batch remains owned by the rejected event, with no arrival history, repeated cause, false cardinality, nested authority, semantic boolean, or structural caller path.docs/actor-laws/proxy.md,docs/actor-laws/dynamic-supervisor.md, and the normalized atomic-actor documents were cross-checked. Disposition:passfor the mechanical migrations; the remaining family inventories and interpreter terminal custody still keep A12 open. A clean detached-worktreenix flake check -L --max-jobs 2at44922e2passed all eight active aarch64-darwin checks, including 845/845 optimized Nextest tests, Rustdoc, Clippy, package, and formatting. The first run caught three test-only consuming calls inside assertions; those calls now execute before the assertions and the full rerun passes.A12 StableProxy root values, read-only review: at the
e9c8d8ccheckpoint the one control-state sum remained eight alternatives in six modules and 5,076 production lines. The retained values below are used by a later decision or returned as terminal custody; these rows do not claim that every nested sum has been reviewed.ProxyStatealternativeExact current value required later DormantNo worker; the one initial submission is still admissible. StartingStart kind and current creation, initialization, activation, or return value select correlation, rejection return, and the next effect. ReadyThe exact current worker capability and attempts select service delivery, stop, and replacement. EmptyInitialNo worker; initial-start authority is spent, so another initial submission must return Overlap.EmptyAfterThe previous worker attempt is needed as replacement provenance. ReplacingThe predecessor departure or successor result plus outstanding shutdown correlation must be joined before publication. ShuttingDownUnresolved worker, creation, initialization, activation, and departure values must settle or transfer before retirement. StoppedProxyRetirementowns the exact residual values for the runtime custodian; A17/T16 must prove that transfer.WorkerStoppingis a direct independent join: the shutdown request is either awaiting its exact ID or has its resolution, while the exact worker stop is absent or present. The resolved-plus-stopped combination immediately becomesStoppedWorker, so no extra arrival-order state is stored.proxy_command_recoveryexercises stop-first and resolution-first joins. The root table and this join matchdocs/stable-proxy.mdanddocs/actor-laws/proxy.md; they justify retention, not a new deletion. The nested sums are inventoried next; other family inventories and the interpreter's terminal transfer still keep A12 open.A12 StableProxy nested values, read-only checkpoint: The current
state.rshas the same eight root alternatives and sixteen subordinate sums with fifty alternatives as the earlier baseline. Across the six family modules, current source has 5,950 physical and 5,080 production lines, 398=>tokens as a search diagnostic, and fifteen top-level public declarations. This review changes none of those counts, no transition branch, and no public spelling. Each row names what a later transition or terminal custodian still needs; paired alternatives below have different legal next operations even where they carry the same payload shape.Subordinate sum Alternatives and future-needed current values PreReadyFailureInitializationEffects: failure plus activation plan;ActivationStart: complete request plus refusal;Activation: plan rejection. These select distinct owner outcomes after worker return.WorkerStartKindInitial: no predecessor;Replacement: replaced attempt plus predecessor shutdown status, selecting lifecycle provenance and join.PredecessorShutdownSettled: no outstanding shutdown;Awaiting: exact shutdown ID to match its later resolution.ReplacementCompletionReady: current worker plus result;ReadyAfterStop: worker attempt, result, exact stop;Empty: worker attempt plus result. Each joins a predecessor shutdown differently.ProxyReplacementReturningPredecessor: predecessor departure, pending successor, staged creation;SuccessorResultAwaitingShutdown: replaced attempt, shutdown ID, successor completion.ActivationProgressWaitingForStartandRunningeach retain the exact activation attempt; onlyRunninghas admittedStarted, so readiness can be consumed there.ActivationDuringDeparturePending: activation progress still awaiting input;Returned: completed activation value owed at retirement.WorkerActivationShutdownDeparting: activation state plus worker departure;WaitingForActivation: activation progress, current worker, optional shutdown resolution, exact stop.WorkerStartPhaseCreating: pending worker and optional early stop;Initializing: committed worker and optional stop;Activating: committed worker, progress, optional stop;ReturningWorker: departure and precise pre-ready failure.WorkerInitializationShutdownDeparting: optional returned initialization value plus worker departure;WaitingForInitialization: worker, optional shutdown resolution, exact stop.WorkerInitializationRetirementInitialized: permit plus plan;EffectsRejected: failure plus plan;Stopped: plan plus optional earlier initialization stop.WorkerActivationRetirementStartRejected: complete request plus refusal;Ready: readiness value;Rejected: application rejection.WorkerStartRetirementResult: start result;Initialization: initialization retirement, worker, optional shutdown resolution, stop;Activation: activation retirement, worker, optional resolution, stop;ReadyWorker: result plus returned worker;ReplacementCompletion: exact successor completion.ProxyRetirementEmpty: optional previous attempt;ReplacementCancelled: replaced and successor attempts plus predecessor;Worker: returned worker;WorkerStart: optional replaced attempt plus start retirement;ReplacementAfterPredecessor: replaced attempt, predecessor resolution, start retirement. These are whole stopped-state custody.ProxyShutdownStarting: start state;ReturningWorker: departure;ReturningWorkerStart: kind, departure, failure;ReturningPredecessor: departure and both attempts;ReturningSuccessor: replaced attempt, predecessor return, successor departure and result;Initializing: kind and initialization shutdown;Activating: kind and activation shutdown;WaitingForPredecessor: replaced attempt, shutdown ID, start retirement.PredecessorReturnAwaiting: shutdown ID;Returned: exact shutdown resolution.The subordinate sums store current affine custody and correlation, not an independently dispatched behavior. Optional stops and resolutions mean exactly absence or presence of one fact; they do not encode mutually exclusive aggregate phases. No arrival-history-only alternative, repeated failure cause, false worker cardinality, nested transition authority, semantic boolean, or structural user syntax was found. The actor transition law, proxy law, stable-proxy guide, and normalized atomic documents were cross-checked. Disposition:
passfor the StableProxy source model; A12 remains open for the other families and downstream terminal custody. A clean detached-worktreenix flake check -L --max-jobs 2at7574bcbpassed all eight active aarch64-darwin checks, including 845/845 optimized Nextest tests, Rustdoc, Clippy, package, formatting, audit, and deny gates.A12 shared pool assignment join, read-only review: FIFO and keyed pools both use the four-alternative private
AssignmentDeliverysum. This is one current job obligation with an exact assignment correlation; it is not a second actor transition authority. The owning aggregate still selects the completeActions. The shared assignment module has 811 production lines and 311 embedded-test lines; this review changes zero states, alternatives, transition branches, modules, production lines, or public spellings.AssignmentDeliveryalternativeExact value needed by a future decision AwaitingReceiptNo delivery receipt exists; completion or stop must be retained until acceptance or rejection settles the moved assignment. AcceptedThe exact receipt has committed delivery; the next matching completion or stop can select the one customer disposition. CompletionHasPriorityThe completion is authoritative if delivery is accepted; an optional later exact stop must still drive worker recovery once. WorkerExitHasPriorityThe exact stop is authoritative if delivery is accepted; an optional later completion must remain available for stale or contradictory settlement. CompletionHasPriorityandWorkerExitHasPriorityare observable order decisions, not duplicate arrival labels. Collapsing them into a product of two optional facts would lose which terminal event won. The two focusedatomic::pool::assignmentorder tests and the FIFO/keyed pool law documents cross-check the retained values.AssignmentReceiptOutcome,WorkerCompletionOutcome,AssignmentRejectionOutcome, andWorkerExitOutcomereturn different complete values to their respective callers; they have not been merged by their common job fields. This review finds no semantic boolean, repeated cause, false cardinality, structural caller path, or redundant nested actor. Disposition:passfor this shared join only; both pool families' member, recovery, and retirement sums remain under A12 review. The PRD now states FIFO's clone-and-retry policy explicitly.A12 FIFO/keyed root values, read-only review: each pool has one five-alternative root sum. This review changes zero states, subordinate alternatives, transition branches, production lines, modules, or public spellings. FIFO remains five modules and 4,783 production lines; keyed remains ten modules and 5,536 production lines. The exact future-needed values differ inside their operating products:
Root alternative FIFO current value Keyed current value ConstructedThe ordered prepared worker roster must be returned intact if initialization ID reservation fails. The same prepared roster must survive failure before the per-role queues and binding table exist. OperatingFifoOperatingowns the ordered members, admission-ordinal backlog, and next role cursor. These select a global FIFO dispatch.KeyedOperatingowns one queue per role, current worker cells, and the bounded key binding table with generations. These select exact key affinity.Draining/RetiringThe exact unresolved RetiringWorkervector andShutdownDeadlinesettle worker results and a bounded drain.The same shared worker-retirement values and deadline settle keyed drain; key/queue outcomes are emitted when retirement begins. StoppedWhen committed, no worker or job remains in the actor root and later input is rejected; it is also the temporary replacement during a consuming transition. When committed, no worker or binding remains in the actor root and later input is rejected; it is also the temporary replacement during a consuming transition. ForcedRetirementThe unresolved workers and exact cause ( WorkerShutdownIdsExhausted,DeadlineNotScheduled, orDeadlineElapsed) must transfer to the runtime custodian.The same kind of unresolved worker vector and exact cause must transfer; the per-role binding policy does not turn that into a FIFO backlog. Constructedis a pre-initialization ownership phase andForcedRetirementis terminal residual custody, not duplicates of the operationalStoppedstate. Both are source-level extensions to the three-state operational sketches in the normalized FIFO/keyed law documents; those documents now name the distinction. Existing FIFO forced-retirement tests and keyed lifecycle tests check retained workers locally. They do not prove the parent-to-root transfer, which remains A17/T16 work. The source scan found no arrival-history root alternative, repeated cause, false role cardinality, nested actor authority, semantic boolean, or positional caller syntax. Disposition:passfor these root sums only; member, recovery, deadline, and terminal-custody alternatives remain under A12 review.A12 fixed supervisor root/recovery values, read-only review: the root
FixedRosterremains five alternatives in nineteen modules and 9,512 production lines;SupervisorRecoveryStateremains a separate two-way policy sum. This review changes zero states, alternatives, transition branches, production lines, modules, or public spellings.Value Exact current value needed by a future decision FixedRoster::NewThe ordered prepared worker roster either stages one proxy per role or returns complete members on creation-ID exhaustion. FixedRoster::OperatingThe ordered RosterOwnercells retain exact proxy, worker, pending preparation, and recovery obligations for role decisions.FixedRoster::ShuttingDownFixedShutdownretains the unresolved roster and shutdown joins while late exact facts continue to settle.FixedRoster::TerminatingExact owners and prepared submissions survive a terminal diagnostic or forced end for runtime custody; A17/T16 must prove transfer. FixedRoster::StoppedWhen committed, no roster value remains and new events cannot be accepted; it is also the temporary replacement during a consuming transition. SupervisorRecoveryState::AutomaticStrategy, eligibility, restart budget, correlation, and WorkerSourceCustodyselect a lawful preparation or denial.SupervisorRecoveryState::TemporaryAutomatic restart authority is absent; a worker stop leaves the role empty. Within automatic recovery,
WorkerSourceCustody::Availableowns the concrete source,PreparingWorkersmeans the in-flight request owns it, andRetirementowns the exact returned source value. These are current ownership positions, not three records of arrival history.RecoveryChoice::LeaveEmptyandSourceUnavailableboth return recovery state but produce different observable outcomes: the former is the selected policy decision, the latter rejects a new preparation because the source is already transferred or retired. The fixed-supervisor initialization and recovery suites cover those distinctions locally. The normalized fixed supervisor law and retained-core document were cross-checked. The residue scan found no new arrival history, repeated cause, false cardinality, nested transition authority, semantic boolean, or structural caller path. Disposition:passfor these root/recovery sums only; individual roster owner, shutdown, preparation, and terminal-custody alternatives remain under A12 review.A12 dynamic supervisor entry values, read-only review: the aggregate has one
SupervisorAvailabilitysum withAccepting(ActorDrainPolicy)andShuttingDown(ShutdownDeadline). It owns a key map ofDynamicEntryvalues; each entry retains its exact creation, generation, operation, and one of fourteenDynamicEntryPhasealternatives. The entry module provides status and shutdown projections but does not implementBehavioror choose the aggregateActions. This review changes zero states, alternatives, transition branches, production lines, modules, or public spellings. The family remains eight modules and 4,015 production lines after the exact-one batch migration.DynamicEntryPhasealternativeExact current value needed later CreatingProxyThe untransferred worker submission is returned on stop or cancellation, or sent to the committed proxy after its birth. ShuttingDownProxyCreationThe enclosing creation correlation remains pending; a committed proxy must be drained and a rejected birth must retire with shutdown provenance. DrainingProxyCreationStopThe same birth correlation remains pending, but a rejected birth must retire as an explicit stop. This cause changes the lifecycle report. WaitingForActivationThe exact established proxy and still-owned worker submission await activation capacity. WaitingForProxyInputThe exact proxy and input witness await ownership settlement of the initial operation. WaitingForProxyOutcomeThe proxy and accepted operation ID await the later initial worker result. CancellingProxyCreationThe enclosing creation and operation correlations remain after the worker was returned; a later committed proxy still requires retirement. StoppingProxyCreationThe original start operation and untransferred submission must be returned by explicit stop while proxy birth still settles. AvailableThe exact proxy, ready-or-empty service value, and latest committed-or-cancelled disposition answer service and repeated cancellation queries. ReplacementQueuedThe current service and successor submission coexist until capacity permits transfer. ReplacementAwaitingReceiptCurrent service, exact proxy, and witness await successor input acceptance or rejection. ReplacementAwaitingOutcomeCurrent service, exact proxy, and accepted operation ID await replacement outcome. StoppingThe proxy, restorable service, shutdown settlement, and optional exact stop must join before stop completes or rolls back. RetiringProxyRetirementowns the exact proxy, shutdown and transferred-input custody, and possible stop through terminal drain.The three data-free creation phases still have the enclosing entry's correlation values and distinct future lifecycle outcomes; merging them by payload size would erase shutdown, explicit-stop, or cancellation cause. The dynamic-supervisor law and normalized atomic documents were cross-checked against the source and local dynamic tests. No arrival-history value without a future decision, duplicated cause, false key cardinality, nested actor authority, semantic boolean, or structural caller syntax was found in these phase sums. Disposition:
passfor the entry and availability sums only; nested retirement, operation, and terminal transfer still keep A12 open.A12 shared pool member and recovery checkpoint, read-only: FIFO and keyed pooling share the current member/assignment ownership model, but each root selects its own queue policy.
MemberStatehas four alternatives,WorkerPhaseseven,RecoveringWorkerfour,ShutdownJointhree,RetirementStatusfive,WorkerStartupCustodyfive,WorkerShutdownStatustwo,PoolRecoveryStatethree, andWorkerRecoverySourcetwo. The shared pool subtree has six modules, 3,760 physical lines, 3,414 production lines, 198 match arrows, and twelve top-level public declarations. This read-only batch changes zero control states, subordinate alternatives, transition branches, lines, modules, or public spellings.Current sum Exact value needed by its next owner MemberState::{Creating,Worker,Recovering,Retired}The pending birth with activation and possible early stop; the established worker phase; the previous attempt plus recovery work; or absence of a live role. WorkerPhase::{Initializing,WaitingForActivation,ActivationDispatched,Activating,Idle,Busy,Stopping}Initialization correlation and possible stop; exact permit and plan; dispatched or accepted activation attempt with possible stop; available worker; one assigned job obligation; or the exact shutdown join. Dispatch and acceptance select different later acknowledgements. RecoveringWorker::{WaitingForSource,Preparing,Scheduling,WaitingForTimer}Previous attempt and stop, plus respectively unavailable source, preparation ticket, pending replacement and timer, or the same replacement awaiting its timer. The latter two distinguish whether schedule admission still has to settle. ShutdownJoin::{AwaitingBoth,AwaitingStop,AwaitingSettlement}Exact shutdown ID; accepted shutdown resolution awaiting stop; or exact stop awaiting settlement. Either arrival order must reunite both facts once. RetirementStatus::{AwaitingCreation,Established,AwaitingPreparation,AwaitingRestartSchedule,Drained}Pending birth; current worker, optional startup and shutdown join; prior stop with preparation ticket; pending replacement with timer; or no remaining worker custody. WorkerStartupCustody::{ActivationPlan,Initializing,ActivationPermit,ActivationStart,Activation}Respectively untransferred plan, initialization attempt, permit with plan, dispatched start, or accepted activation correlation returned during retirement. WorkerShutdownStatus::{NotRequested,Waiting}No shutdown authority yet, or the exact outstanding join. PoolRecoveryState::{Permanent,Transient,Temporary}andWorkerRecoverySource::{Available,AwaitingReturn}Permanent/transient retain source, limit, release and failure policy; temporary has no source and retains failure policy. Available owns the concrete source; awaiting return records that the preparation request currently owns it. WorkerRecoveryDecision,WorkerCustody,RetirementReturns, and the event-specific retirement outcomes are consuming transition products, not independently stored actor control states. Their distinct payloads return exact source, worker, assignment, or replacement custody to the root. A product of optional values would allow a worker to be both idle and busy or lose which shutdown fact remains; a direct vector already represents the ordered role roster. The normalized FIFO/keyed laws, actor transition law, and runtime settlement law were cross-checked. No arrival-only state, duplicated cause, false role cardinality, nested behavior authority, semantic boolean, or structural caller syntax was found. Disposition:passfor the shared member/recovery source model.A12 keyed binding checkpoint, read-only:
RoleCellowns one role's member, admission-ordinal queue, and capacity.BindingTableowns bounded key-to-role bindings and a checked generation sequence.AdmissionBindinghas two short-lived alternatives:Retainedkeeps the submitted key and existing evidence, whileReservedowns the fresh table reservation until admission commits or returns the key.BindingExpectation::{Absent,Exact}distinguishes a missing binding from one exact generation; the threeBindingReservationRejectedreasons return the same key with distinct capacity, conflict, or sequence-exhaustion causes. These are current authority/return values, not another keyed actor. Keyed pooling remains ten modules, 5,755 physical and 5,536 production lines, 268 match arrows, and twenty top-level public declarations before and after this review. Root states, subordinate alternatives, transition branches, and public spellings change by zero. The normalized keyed law and binding generation tests were cross-checked; the residue scan found no arrival history, duplicate cause, false cardinality, nested transition authority, semantic boolean, or structural caller path. Disposition:passfor source binding custody.A12 fixed roster and dynamic retirement checkpoint, read-only: Fixed supervision remains nineteen modules, 10,104 physical and 9,512 production lines, 718 match arrows, and twenty-seven top-level public declarations. Dynamic supervision remains eight modules, 4,115 physical and 4,015 production lines, 246 match arrows, and twenty-five such declarations. Both roots, all subordinate sums, transition branches, lines, modules, and public spellings are unchanged by this review.
Current sum Exact future-needed value RosterOwner::{Starting,Stopping,Online,Empty,Recovery,Unrecovered,Retired}Respectively a proxy start correlation; proxy shutdown join; established proxy, worker and readiness; empty role identity; recovery batch/preparation; unrecovered worker and reason; or terminal returned proxy custody. Its status projection does not select aggregate Actions.ProxyStartingMembersix alternativesPending creation with original operation; established proxy awaiting authorization; dispatched input witness; accepted input ID awaiting outcome; exact rejected input; or exact rejected birth. Each has a different next input or return value. RecoveryRosterOwner::{Preparing,Admitted}andRecoveryMember::{Waiting,Replacing}Preparation owns the source/ticket; admitted batch owns prepared participants. A waiting member owns an untransferred submission; replacing owns an established proxy and exact replacement result. RecoveryParticipant::{Stopped,InitialInputDispatched,AwaitingInitialOutcome,Online}Original stopped member; proxy with dispatched input witness; accepted operation ID; or current ready member. ReplacementResponse::{InputPending,OutcomePending,OutcomeReturned,InputRejected}Exact input witness, accepted operation ID, returned outcome, or returned operation with rejection reason. RestartReleaseState::{Ready,Scheduling,Waiting}likewise distinguishes no timer from timer admission and an admitted timer.Fixed ProxyShutdown::{Dispatched,Accepted,Rejected}Unsettled input witness, accepted operation ID, or complete rejected shutdown. An optional exact ChildStoppedin the containing member joins independently.Dynamic ProxyShutdown::{AwaitingSettlement,Accepted,Rejected}Pending shutdown witness, accepted operation ID, or rejection; the entry's stop remains independently correlated. Dynamic RetiringWorksix alternativesDistinct start failure, unexpected stop, interrupted start, supervisor shutdown with optional service, cancelled returned worker change, or transferred proxy input with exact purpose and custody. These causes select different public lifecycle reports. Dynamic ProxyInputPurposefour alternatives andProxyInputCustodyfour alternativesThe purpose distinguishes cancellation, interrupted start, initial start, and replacement with service. Custody distinguishes unadmitted witness, accepted operation, rejected input, and returned proxy outcome. The pair determines exact terminal return without replaying a control. Fixed recovery policy, dynamic key/operation correlation, source ownership, and the five normalized atomic laws were cross-checked with the source. Data-free retirement causes are observable lifecycle policy, and separate accepted-input versus returned-outcome states are needed to prevent false readiness/restart. No arrival-only state, repeated cause, false worker/key cardinality, nested
Behaviorimplementation, semantic boolean, or structural application syntax was found. Disposition:passfor these source-level subordinate sums. Terminal transfer and caught-panic ownership remain the real-runtime A17/T16 witness; this review makes no claim about those downstream effects.A12 final source-review disposition:
pass. The five root control sums, the StableProxy nested sums, the shared worker/recovery joins, keyed binding, fixed roster/recovery, and dynamic entry/retirement sums above retain exact values used by later decisions or returned in terminal custody. The before/after production source and syntactic branch counts are identical for each read-only family checkpoint; the earlier exact-one changes record their separate reductions and branch deletions. Counts, module ownership, public spellings, residue scans, and normalized-law cross-checks are recorded at each checkpoint. Earlier statements that A12 was open describe the staged review at those revisions. No state or module deletion is justified by a falsifying law, so none is proposed. Actual parent-to-root terminal transfer and post-commit panic limits are interpreter obligations in A17 and the PRD, not source-level aggregate-decomposition claims.FIFO and keyed pools each move their root state out with
mem::replace(..., Stopped)during initialization and transition; fixed supervision similarly substitutesStoppedor a temporary recovery value, and StableProxy substitutesDormantduring one consuming transition. Normal return commits the result once, but a caught pure-fold panic before that commit may leave only the placeholder in the actor while owned state unwinds. This is an inference from the source, not a proven runtime outcome. The PRD's T16 panic and root-custody witness must test it before these sites can be called safe or refactored. A passing ordinary transition suite cannot decide that law. -
A13 — Audit public bounds, hidden exports, and extension ownership. Confirmed surface requiring review. The current source has 10
#[doc(hidden)]annotation sites in core and 22 in actors, including members and re-exports. The public-surface inventory classifies each site by contract owner. These annotations do not make an item private. Conversely, an associated type mentioning a value does not by itself justify exporting it. SomeProtocolimpls carried transition-related bounds:CacherequiredK: Clone + EqandV: Clone;ResolverrequiredK: Clone + Eqbefore the focused A13 repair below. The four timer wrapper structs already have only one generic parameter,B; their effect signatures project address, phase, sends, and births from it, so they are a successful example rather than a gap. Complete when: classify each public item as application API, generated code obligation, or runtime port; identify each trait's lawful implementors. Move bounds only after caller-facing compile witnesses prove the narrower law. Measure diagnostics and compile cost. Document required runtime ports openly and keep representation private where Rust permits. Add no aliases, defaults, visibility, or generic parameters solely to silence the compiler. Progress: the public-surface inventory now accounts for all 78 top-level public traits by lawful implementor role, including the laterProxyControlAdmissioninterpreter port. Its actor-suite implementors and the isolated Bombay source identify its owner; the latter is not yet a successful A17 runtime witness. The inventory confirms thatStashStatushas multiple real wrapper implementations; its name alone is not grounds for deletion. Externally authored child roles and generic logical-host owners have compile witnesses for the newly visible types. The inventory now classifies the hiddenChildProduct::stagemethod as a sealed structural conversion: onlyNoChildrenandChildConsimplement the trait,Children::into_createsis its sole production caller, and the interpreter consumes the resultingCreationseffect. Callingstagea runtime port had overstated its public contract. This correction changes zero Rust items, bounds, states, branches, modules, or public spellings. The cache/resolver protocol-bound comparison found no material compile-time difference in its measured pair; the caller diagnostics improved. The protocol-only caller now covers 20 catalogue actors, keeping construction and transition bounds at the operations that need them. A separate caller now proves that 14 unwrapped catalogue actors expose their read-onlyBehaviorBaseprojection without requiring the cloning, comparison, or ordering used only by construction or transition. This also includes keyedDeduplicatorandOrderGateand a priority queue with opaque priority data. The proxy operation ID is now crate-private; focused external fixtures distinguish forbidden ID naming, receipt construction, and double settlement. Review of remaining hidden runtime ports and the rest of the public surface is still required. A repeatable cold actor-library check on aarch64-darwin used the same Nix-pinned Cargo 1.95.0 and two separate fresh targets per revision:main@435560ctook 184.54/180.09 seconds and this branch at1aeaed1took 4.46/3.97 seconds. The actor.rmetasizes were 27 MiB and 5.8 MiB. The measurement record gives the command and scope. This is a whole-branch comparison, not causal evidence for any one A13 bound; future bound edits still need focused caller diagnostics and their own cost check. A post-repair source scan of catalogueProtocolandBehaviorBaseimpl headers found no remaining copying/comparison/ordering bounds exceptRouter'sRoute: Clone + PartialEq, which is also required by its currentRoutingStrategy<Route>contract. Changing that contract would require a distinct membership and policy law, not a mechanical bound deletion. The read-only Router review found no legitimate transition for a route without identity comparison:newremoves duplicates,Add/Removeselect exact members, and selection clones the policy candidate so a failed route leaves it unchanged. A logical or established route whose endpoint lacks equality cannot currently satisfy that membership law. Narrowing only the protocol/base headers would create a nameable router that cannot be constructed or run; no bound edit was retained. Round-robin's cursor repair and the least-loaded/Rendezvous traces exercise the current comparable-route contract. This resolves the isolated header question, while A13's broader hidden-port review remains open.Pre-edit A13 transferable-route owner law: Logical, established, and mixed reply routes are transferable acquaintances. For any actor
Owner,DeliveryRouteplus its associated protocol address equal toBehaviorAddr<Owner>already proves that the emitted delivery belongs to the owner's address namespace. The separate sealedDeliveryRouteFor<Owner>duplicates the same three implementations and forwards everydeliver_forcall toDeliveryRoute::deliver; no production consumer uses it. The only external caller isbehavior-testkit::owner_scoped_delivery. This is a derived static composition law, while excluding creator-localChildRoutefrom a standalone transferable route is a deliberate Bombay policy. Before deleting the trait, change that test's generic caller to use the associated address equality and preserve its logical/exact/mixed outcome assertions. Add a negative compile fixture that rejects naming the redundant trait; the fixture must fail on the prior public API for that exact name. The lower-orderDeliveryRoute,Recipient,EstablishedRecipient,ReplyRoute, and their concrete send products remain. No new wrapper, route variant, runtime lookup, or child-binding capability is proposed. The negative rustdoc fixture failed on the prior API because its old-trait name still compiled. After deletion, that fixture and the existing wrong-protocol fixture pass, and the external owner-scoped delivery caller passes in debug and optimized builds with the associated-address bound for logical, exact, and mixed routes. The three touched production Rust files fall from 686 to 608 physical/production lines; the public trait count falls from 78 to 77. Root control states, subordinate alternatives, actor transition branches, and modules remain unchanged. The only removed public spelling isDeliveryRouteFor; existing route ownership stays with each concrete capability and its send product. No arrival history, repeated cause, false cardinality, nested authority, semantic boolean, or positional caller syntax was introduced. The actor transition law, composition guide, and normalized atomic documents were cross-checked. Disposition:pass; this is a deliberate source-breaking interface deletion and requires the release PR's breaking-change label. A13 remains open for the remaining surface review.Pre-edit A13 returned-worker custody documentation law: A host rejection of a staged worker returns
WorkerCreationRejection::HostRejectedcontainingWorkerRecovery<W>. Its consuminginto_retirement()returns the exact current worker and untouched initialization actions to the custodian; no replacement worker or reconstructed actions are permitted. A committed worker outcome separately carries opaqueWorkerAttemptevidence whosecreation()projection is only creator-local correlation. These are existing derived ownership contracts. The externalproxy_command_recovery::host_rejection_keeps_worker_and_initialization_actions_togethercase consumesWorkerRecovery, and proxy/fixed-supervisor callers nameWorkerAttempt. Before editing, Nix-pinned Rustdoc omits both structs from theatomicindex and has no item pages. The focused regression is visible pages and methods while those external callers continue passing. Only documentation hiding on these already-public types/methods and their grouped re-export may change; their private constructors, fields, result types, actor states, transition branches, and interpreter operations stay unchanged. Post-edit, Nix-pinned Rustdoc lists both types on theatomicindex with item pages and showsinto_retirement()andcreation()on those pages. The focused external host-rejection test passes. Actor hidden annotation sites fall from 54 to 50. Root control states, subordinate alternatives, transition branches, modules, and public Rust spellings remain unchanged; the two touched Rust files fall from 440 to 436 lines as four hidden markers are removed. This edit changes documentation visibility only.WorkerRecoverystill owns the rejected worker and all unattempted initialization actions, whileWorkerAttemptstill owns only exact issued correlation. No arrival history, repeated cause, false cardinality, nested authority, semantic boolean, or positional caller syntax was introduced. The creation/lifecycle law, stable-proxy guide, and normalized atomic documents were cross-checked. Disposition:passfor this custody documentation port; A13 remains open for the wider surface review.Pre-edit A13 worker initialization and activation port law: A host receives the exact
InitializeWorkerrequest, observes its established target and correlation, and consumes it once to produce a completeWorkerInitializationReport. A successful report alone carries theActivationPermitthat can constructBeginActivation; the interpreter admitsStartedbefore polling the plan and returns the exactWorkerActivationresult. These are existing derived custody and ordering contracts, not new runtime effects. Externalproxy_command_recovery,stable_proxy_shutdown_model,fifo_pool, and interpreter-contract caller tests already name these concrete types and exercise accepted, rejected, stopped, and activation-start-rejected paths. Before editing, Nix-pinned Rustdoc omits the initialization and activation items and their public methods from theatomicpages because their declarations and grouped re-export are hidden. The focused regression is complete visible Rustdoc for the already-public host and owner methods while the external transition suites retain their results. Only documentation hiding may change; private constructors, fields, correlation tokens, return types, effects, states, branches, and interpreter ordering remain as before. Post-edit, Nix-pinned Rustdoc lists all nine named types on theatomicindex with their existing public method pages. The external proxy recovery and shutdown model suites pass 53 and 7 tests. Actor hidden annotation sites fall from 50 to 23. The three touched Rust files fall from 755 to 728 lines by deleting 27 hiding markers. Root control states, subordinate alternatives, transition branches, modules, and public Rust spellings are unchanged. The worker request still owns its exact target, worker attempt, initialization attempt, and affine activation plan; the permit still authorizes just that worker and attempt; the returned activation still owns its original result or rejection. No arrival history, repeated cause, false cardinality, nested authority, semantic boolean, or positional caller syntax was introduced. The actor creation/lifecycle law, stable-proxy guide, and normalized atomic documents were cross-checked. Disposition:passfor this visibility batch; A13 remains open for the other hidden ports and the full public surface review.Pre-edit A13 creation-observation port law: A local interpreter may observe the exact same-action child creation named by protocol, occurrence, and creator-local
CreationId. The publicObserveCreation<P, Occurrence>request carries that typed prerequisite; it does not establish freshness or let a foreign child protocol substitute because its payload shape matches. This is an existing Bombay observation policy. Externalcreation_observation_settlementandchild_shutdown_interpretationcallers already name and interpret the request, while its Rustdoc compile-fail fixture rejects a different protocol. Before editing, Rustdoc hides the existing item page. The focused regression is a visible request page and retained external and compile-fail results. Only its documentation marker may change; request fields, constructor, prerequisite, effect order, behavior state, and interpreter semantics remain unchanged. Post-edit, the actor Rustdoc index links toObserveCreationand itsnewmethod. The two external caller suites pass 5 tests and actor doctests pass 44, including the wrong-protocol compile-fail example. Hidden actor annotation sites fall from 23 to 22; the touched production file loses one marker line. Control states, subordinate alternatives, branches, modules, and public Rust spellings stay unchanged. The request retains the same exact typed prerequisite and current creation ID. No arrival history, repeated cause, false cardinality, nested authority, semantic boolean, or positional caller syntax was introduced. The actor creation/lifecycle law and normalized observation document were cross-checked. Disposition:passfor this runtime-port documentation; A13 remains open for the broader surface review.Pre-edit A13 proxy outcome and correlation documentation law: A caller inspecting an initial or replacement proxy result must distinguish
WorkerStartResult::CreationRejectedfromReadyandUnavailable; the latter owns the exactProxyDraincause and worker lifecycle values. A trusted interpreter inspectingProxyOperationmust read its exact creator-local creation correlation before admitting private control. These are existing derived ownership contracts, not additional actor powers. External proxy, fixed-supervisor, and dynamic-supervisor tests already match these products and callProxyOperation::creation(). Before editing, Nix-pinned Rustdoc omitsWorkerStartResultandProxyDrainfrom theatomicindex and has no item pages; it also omitsmethod.creationfrom the visibleProxyOperationpage. The focused regression is their visible Rustdoc contract with those existing external callers still passing. The edit removes documentation hiding from the domain outcome, exact correlation method, and their grouped re-export; it does not add a Rust spelling, constructor, capability, bound, lane, state, or transition. Post-edit, Nix-pinned Rustdoc listsProxyDrainandWorkerStartResultin theatomicindex with item pages and listsmethod.creationon theProxyOperationpage. The focused external proxy, dynamic-supervisor, and fixed-supervisor suites pass 53, 29, and 98 tests. Actor hidden annotation sites fall from 56 to 54. StableProxy's eight root states, the fixed roster's five, and the dynamic entry's fourteen alternatives remain unchanged; their branches, modules, production public spellings, and ownership values are unchanged.ProxyDrainstill owns one precise committed-worker failure cause, andcreation()still exposes only the creator-local correlation. No arrival history, repeated cause, false cardinality, nested authority, semantic boolean, or positional caller syntax was introduced. The proxy law, stable-proxy guide, and normalized atomic documents were cross-checked. Disposition:passfor these visible contracts; A13 remains open for the wider surface review.Pre-edit A13 customer-delivery documentation law: A keyed-pool interpreter must name the concrete
CustomerDelivery<P>action to return both the attempted delivery and original customer route on rejection. An assignment interpreter must callAssignWorker::target()to obtain a clone of the exact worker recipient before consumingsettle. These are existing derived ownership ports, not new actor powers. The public caller syntax is already exercised by the keyed-pool external compile suite and the interpreter-contract assignment fixture; the four customer alternatives retain their existing complete settlement equation. Before editing, Nix-pinned Rustdoc omitsCustomerDeliveryfrom theatomicindex and has no item page, and omitsmethod.targetfrom the visibleAssignWorkerpage. Removing their two declaration markers and separating the existing grouped re-export exposes only those already-public names. The focused regression is their Rustdoc visibility with the existing external callers still passing. No constructor, method, bound, trait, effect lane, control state, transition branch, or interpreter operation changes. Post-edit, Nix-pinned Rustdoc listsCustomerDeliveryin theatomicindex, generates its item page, and listsmethod.targetonAssignWorker. The 23 keyed-pool tests and three external assignment-delivery tests pass. Actor hidden annotations fall from 58 to 56; the two declaration markers are gone while the sealedCompletesAssignmentsre-export remains hidden. The root aggregate states, subordinate alternatives, transition branches, source modules, and Rust public spellings are unchanged. The exact worker recipient capability remains insideAssignWorker, and the original customer route remains inside a rejectedCustomerDelivery. No arrival history, repeated cause, false cardinality, nested authority, semantic boolean, or positional caller syntax was introduced. The keyed-customer rejection law indocs/atomic-runtime-settlement.mdand the normalized pool law were cross-checked. Disposition:passfor these two visible ports; the wider A13 surface and compile-cost review remain open. A clean detached-worktreenix flake check -L --max-jobs 2at19a38c2passed all eight active aarch64-darwin checks, including Rustdoc and optimized Nextest.Pre-edit A13 diagnostic-port visibility law: An external interpreter must name the concrete
DiagnosticAction,DiagnosticAccepted, and sealedDiagnosticRoutecontract to settle a routed or terminal diagnostic. The accepted value either records delivery or transfers the exact diagnostic into terminal custody; it cannot silently discard the latter. This is a derived interpreter ownership port, not a new actor transition. The externaldiagnostic_actionand interpreter-contractsource_free_custodysuites already use this syntax, including the constructor methods. Before editing, Nix-pinned Rustdoc builds but omits all three names from theatomicindex and has no item pages for them. The focused regression is visible Rustdoc for these already-public names and methods while the existing caller suites continue to pass. The only proposed change removes documentation hiding annotations on the three declarations, their four methods, and grouped re-export. It adds no type, bound, capability, effect lane, or transition branch. Post-edit, Nix-pinned Rustdoc lists all three names in theatomicindex, generates their item pages, and lists all four methods. The threediagnostic_actiontests and four external source-free custody tests pass. Actor hidden annotations fall from 66 to 58. Aggregate control states, subordinate alternatives, transition branches, modules, and Rust public spellings are unchanged; the documentation-only edit removes eight source lines. The complete diagnostic remains owned by the terminal accepted variant until the interpreter transfers it. The residue scan finds no arrival history, duplicate cause, false cardinality, nested transition authority, semantic boolean, or structural caller syntax. The actor transition and retained-diagnostic laws were cross-checked againstdocs/actor-transition-algebra.mdanddocs/engineering/atomic-actor-retained-core.md. Disposition:passfor these diagnostic item pages; A13 remains open for other ports.The creation settlement review found a narrower documentation defect: external caller suites name
CreationSettlement,CreationSettlements, andCreationsSettledto retain or return exact child-creation custody, but none appeared as an item in the generated crate-root Rustdoc index. Their public visibility and settlement equations already existed; showing them changes no actor transition. A later read-only downstream inspection found that Bombay'sActionInterpreterexplicitly requiresInterpretCreationsin itsCommitActionsimplementation. That is a real interpreter port and supplies the missing caller evidence to show the trait in Rustdoc as well. The three externally named settlement ports are now listed by Rustdoc:cargo doc -p bombay-behavior --no-deps --lockedsucceeded with the Nix-provided Rust 1.95 toolchain, and all three crate-root index links and item pages exist. The externalgenerated_creation_custody,custody, and actor caller suites already name these products. Downstream compilation and runtime witnesses remain part of A17; this source inspection does not claim they pass. A second Nix-provided Rustdoc build after exposingInterpretCreationsconfirmed that all four names have crate-root index links and item pages. Thebombay-behavior-docNix check passed on signed commit4cd3b7f, including Rustdoc, the book, and published-document checks.The next required runtime method is
CreateChild::into_parts. The externalcreation_initialization_order,creation_settlement_custody, andbehavior_generationcaller suites consume it to retain the exact child, creator-local ID, and provenance. The real Bombay child host also names it after consuming aRoutedCreation. Its Rust visibility is already public, but#[doc(hidden)]conceals it from an interpreter author reading the API. This is a derived ownership port, not a new actor transition. The focused documentation repair removes that one marker and gives the method an explicit custody description; it adds no type, bound, effect lane, control state, or interpreter capability. The existing external callers are its compile witnesses. The post-edit check is that Rustdoc lists the method onCreateChildand those callers still compile. Post-edit,cargo doc -p bombay-behavior --no-deps --lockedgeneratedstruct.CreateChild.htmlwithmethod.into_partsand its custody text.cargo check --lockedpassed for the externalbehavior-testkit/tests/creation_initialization_orderandbehavior/tests/creation_settlement_custodycallers;mdbook build docs, formatter, and diff checks passed. The source change is+6/-1physical lines of documentation only; tests and public type counts are unchanged. Aggregate control states, subordinate alternatives, transition branches, modules, and public spellings are unchanged. The owned child, ID, and kind remain the same complete product; no arrival history, duplicated cause, false cardinality, nested authority, semantic boolean, or positional caller syntax was introduced. The actor transition and creation-custody laws were cross-checked. Disposition:passfor this one documentation port. A13's wider surface review remains open.CreationCorrelation<P, Occurrence>is another required, already-public effect prerequisite. The externalbehavior/tests/action_interpretationand actorinterpreter_request_settlementcallers name it inActionItemimplementations; its compile-fail example rejects exchange of equal IDs at different occurrences. The derived law is typed correlation to exactly one creation settlement, without granting child-hosting authority. The focused visibility repair removes its Rustdoc hiding marker, retains its private representation and public constructor unchanged, and adds no transition or type. The pre-edit regression is the absence of its item in the generated crate-root Rustdoc index despite those external compile witnesses. The post-edit checks are the Rustdoc index and the occurrence-mismatch doctest. Post-edit Rustdoc has a crate-root link and item page, the focused E0308 compile-fail doctest passed, andcargo check --lockedpassed for bothaction_interpretationandinterpreter_request_settlementexternal tests. The source change deletes one hidden annotation; tests, public types, control states, subordinate alternatives, transition branches, modules, and public spellings are unchanged. No history, repeated cause, false cardinality, nested authority, semantic boolean, or positional syntax was introduced. The actor transition and occurrence laws were cross-checked. Disposition:passfor this visible prerequisite, with A13 still open for other items.CreationId::getis likewise an existing public runtime port concealed in Rustdoc. The actor catalogue derives shutdown and operation correlations from its occurrence-local number; external actor tests also inspect it. Exposing the existing method does not make the number an actor identity or a freshness proof, so its documentation now says that explicitly. The pre-edit symptom was its absence from generated Rustdoc despite external callers. The source edit removes one hidden marker and adds only custody text: zero types, bounds, lanes, states, branches, modules, or public spellings change. The current owned ID remains the sole value. The residue scan is clear for history, repeated cause, false cardinality, nested authority, semantic boolean, and structural syntax. Cross-checks: actor creation law and the exact-correlation consumers. Nix-pinned Rustdoc builtCreationIdwith a visiblemethod.getitem, andcargo check -p bombay-behavior-actors --tests --lockedpassed. Disposition:passfor this documentation-only port; A13 remains open for the wider surface.The assignment custody repair narrowed
AssignWorker::receiptandAssignWorker::into_partsfrom public to atomic-module-only after the external consuming settlement passed. Compile-fail fixtures now reject those two old assembly paths withE0624and reject double settlement withE0382; FIFO/keyed callers, benchmark, and fuzz campaigns use the actual exact-delivery result. The later proxy settlement stage also narrowed its operation ID, request decomposition, and receipt construction after external privacy and closed-control witnesses. The public-surface inventory reflects 81 actor#[doc(hidden)]sites at that checkpoint. The wider port and bound review remains open.Pre-edit A13 interpreter-name law: an external interpreter implementing
InterpretItem<AssignWorker<Worker, Job>, ...>orInterpretItem<ProxyOperation<Source, Worker, Plan>, ...>must name the corresponding opaque accepted receipt in its return type. The interpreter also namesProxyControland the immediate proxy result when implementing the concreteProxyControlAdmissionport. These are derived ownership products, not additional actor powers; their fields and receipt constructors stay private. Existing external assignment and proxy caller suites, plus the inspected Bombay interpreter source, prove the naming syntax. Before editing, Nix-pinnedcargo doc -p bombay-behavior-actors --no-deps --lockedbuilt successfully, but the generatedatomicindex and item files omittedAssignWorker,AssignmentReceipt,ProxyControl,ProxyOperation,ProxyInputReceipt, andProxyInputResultbecause their declarations or grouped re-exports carried#[doc(hidden)]. The focused regression is that these existing names and their custody descriptions appear in Rustdoc while the external caller and compile-fail fixtures keep compiling unchanged. The edit is limited to documentation visibility and regrouping the already public re-exports; it introduces no trait, type, constructor, effect lane, bound, or actor transition. The ownership proof remains the consumingsettlemethods and the existing static interpreter port. Aggregate states, subordinate alternatives, branches, modules, production public spellings, and current values are unchanged by this visibility repair; there is no arrival history, repeated cause, false cardinality, nested authority, semantic boolean, or structural caller path to retain. Disposition will bepassonly after the item pages, external fixtures, and Nix documentation gate are checked. The focused Rustdoc build now lists all six types in theatomicindex and generates their item pages. The actor source has 75 hidden annotation sites, six fewer than the preceding checkpoint. All 13 external fixture test functions passed in debug and optimized profiles. Four compile-fail snapshots changed only the diagnostic's displayed type path; their E0382 and E0599 failures remain the intended ownership and privacy errors. A clean Nix flake check at signed commitb8b9842passed all eight available macOS checks, including Rustdoc, published-document checks, and 843 optimized Nextest tests. Disposition:passfor these six runtime item pages; A13 remains open for the rest of the surface.Pre-edit A13 worker-preparation port law: a trusted worker-source interpreter consumes the owner-emitted
PrepareWorkersrequest, borrows its current source and role, then consumes either an accepted submission or its exact rejection. A multi-role request continues through the ownedPendingWorkerPreparationuntil the completeWorkerPreparationreturns. The public methods already express that affine progression; the interpreter must be able to discover those types and methods without guessing hidden Rustdoc paths. This is a derived Bombay ownership port, not a new actor-model operation. External fixed/FIFO actor tests and the inspected Bombayworker_preparation.rsuse this exact syntax; no replacement trait or constructor is proposed. Before editing, Nix-pinned Rustdoc omitted the three types from theatomicindex. The focused regression is that the existing three types and six progression methods become visible while their constructors, tickets, fields, and owner settlement remain private. This visibility-only batch changes zero control states, subordinate alternatives, transition branches, modules, or Rust public spellings; the exact current source, role, prepared prefix, ticket, and remaining roles retain their one owners. The residue scan is clear for arrival history, duplicated cause, false cardinality, nested transition authority, semantic booleans, and structural caller syntax. Cross-checks are the worker-preparation custody law and normalized atomic pool/supervisor documents. Disposition requires Rustdoc visibility, the external actor suites, and the clean gate. Nix-pinned Rustdoc now lists the three structs inatomicand all six progression methods on their item pages. Actor hidden annotation sites fell from 75 to 66. The externalfifo_poolandfixed_supervisor_initializationactor suites passed 91 and 98 tests, respectively. The first clean Nix attempt at signed commita74b287stopped during optimized compilation when the machine ran out of disk space; it reported no Rust law failure. After cleaning this repository's generated Cargo target directory, the retry with two concurrent Nix builds passed all ten available macOS checks, including Rustdoc and 843 optimized Nextest tests. Disposition:passfor this documentation port; A13 remains open for the rest of the public surface.A13 release disposition: The public-surface inventory now assigns the canonical export groups across all five crates to application, generated-code, test-only, or interpreter ownership. It names lawful implementor roles for all 77 top-level public traits and classifies all 10 remaining core and 22 remaining actor Rustdoc-hidden sites. The still-hidden items are structural products/proofs or the internal
HostedInitializationalias; the public requests, receipts, rejections, and custody methods that interpreters must name are visible. Theatomic::pool::CustomerDeliveryre-export remains hidden only because its canonicalatomicpage is visible. Caller-facing compile witnesses justify the narrower catalogue bounds, and the Router comparison law explains the one retained identity bound. The redundantDeliveryRouteFortrait was removed after its only external caller passed withDeliveryRoute's associated-address bound in debug and optimized builds; its negative rustdoc fixture failed on the prior API and now passes. The repeatable cold compile comparison and caller diagnostics are recorded in the inventory. No further bound or visibility edit is justified by the current evidence. The complete Nix gate at7574bcbpassed, including Rustdoc and 845 optimized tests. Disposition:passfor the Behavior public-surface review. A17 separately retains the real Bombay runtime witness; closing A13 does not claim that integration. -
A14 — State the trust scope of initialization capabilities accurately. Confirmed documentation/API mismatch.
InitializationTurnsays only the lifecycle boundary can issue initialization exactly once. However the public, doc-hiddenbehavior::initialize(&mut B)can be called repeatedly, anddelegate_transitioncan be called before initialization.Active<B>provides a consuming application path, but it does not restrict those public wrapper ports. They are necessary seams to assess, not proof of universal enforcement. Complete when: specify which operations are trusted wrapper/runtime ports and which invariant the application-facing types actually enforce. Add a legal-user witness for that scope. If stronger enforcement is required, establish its composition law before changing the API; hiding rustdoc is insufficient. -
A15 — Make coding-rule enforcement agree with the actual repository. Confirmed local-rule deviations. Timer-domain tests invoke mutable
acceptinsideassert!; catalogue models carry readiness decisions as booleans. Many rustdoc fixtures haveusesections despite the explicit fully-qualified-snippet rule. PrivateOperatingtypes in both pools are ambiguous outside their immediate context. These are repository-policy issues; ordinary predicates are not automatically invalid semantic state. Complete when: move transitions outside assertions, express semantic alternatives using domain sums, update snippets and ambiguous domain names, and add focused checks for rules that can be enforced reliably. Keep assertions observational in debug and optimized tests. Audit nested.sends.inner.inneraccess intestkit/tests/compositions.rs: interpreter structure tests may inspect structure, while ordinary consumer tests must prove syntax that survives an unrelated layer. -
A16 — Restore developer-facing documentation accuracy. Confirmed drift. The root README installs
0.14while workspace packages are0.17.0; four distinct linked documents moved todocs/engineering/. It still namesInstallBirthand claims stricter birth-algebra equality than the current role resolver documents. Historical engineering records describe removed templates and tests. Their historical status is useful, but an old “pass” cannot certify current code. Generated-HTML checking does not validate the repository README or compile all book examples. Complete when: root and crate entry points have working links and runnable current examples; current contracts use retained names; historical verdicts clearly identify revisions. Add README link and consumer-snippet checks. Prefer links to one normative law over copied explanations in several docs. -
A17 — Validate the real downstream interpreter contract. Coverage gap explicitly acknowledged by existing docs.
atomic-runtime-settlement.mdsays Bombay will change to implement the architecture. Local tests include useful scripted interpreters, fabricated established endpoints, and aProxyRuntimeWitnessthat returns corruption for every item. Such tests prove static composition and local custody, not a working host, fresh allocation, admission, or retirement transfer. Complete when: a recorded downstream revision runs an end-to-end witness for initial and replacement creation, collisions/exhaustion, initialization stop/rejection, activation, rejection followed by independent effects, closed emitter admission, and parent-to-root residual transfer. Prove that failed replacement establishment never reports restart success. Keep allocation, scheduling, and transport in the interpreter. A read-only inspection found a realEstablishChildimplementation in the sibling Bombay checkout, but that checkout has substantial uncommitted work on a separate branch. This branch has not run the required downstream revision/witness, so the scripted testkit trace is not labeled integration evidence. Isolated downstream probe: an unmodified snapshot of the sibling worktree compiledbombay-rs --libunder its Nix-pinned Rust 1.96 toolchain against published behavior crates. With Cargo patches pointing to this branch, compilation stopped atE0046:EntityAdmission<D>has not declared the newerInterpreterRequest::LogicalProtocolsprojection. Source review found another test-localInitializationRequestimplementation with the same omission. The candidate law for a temporary compatibility probe is that both requests use already-owned capabilities and emit no logical actorDelivery, so their emitting actor's logical-host product is empty. This is an inference from the downstream source, not an actor-model guarantee or a successful A17 witness. No change was made to the sibling checkout. In a second isolated copy, those two requests declared the empty logical protocol product and Cargo patched both behavior crates to signed commit4cd3b7f.bombay-rs --libthen compiled, and five selected downstream targets passed 27 tests:application_children(1),application_terminal_custody(2),entity_application(1),entity_runtime(11), andrun_with(12). The selected tests include real application admission, activation failure, passivation, terminal custody, and caller compile fixtures. The snapshot came from the dirty downstream worktree ata7a66e391273; it is not a recorded clean revision, and the temporary projections are not present in that repository. These tests do not prove replacement-establishment failure, every creation collision and exhaustion path, independent sends after rejection, or parent-to-root residual transfer. A17 stays open.Address
0.3.0is now published. In the isolated Bombay worktreecodex/interpreter-ownership, Cargo resolves that release while Behavior and Communication remain path-patched research inputs. Bombay's all-target compile passes against this mixed graph. Its current library run passes 174 of 179 tests; five older startup assertions still expect publication after initialization stop or task unwind after a caught pure fold panic. This is neither an immutable integration revision nor an A17 verdict. The original Bombay checkout remains untouched.
P3 — make supporting evidence intentional
-
A18 — Correct benchmark meaning before using performance results. Confirmed benchmark mismatch.
protocol_matrix.rs::measure_fsmclaims alternating phase changes, but transitionsA -> Bonce and then always stays inB. The base benchmark calls the inherent macro-authored receive method directly, whereas composed cases use activated behaviors. Observing only empty action sizes also weakens confidence that intended work survives optimization. Both benchmark programs report a single elapsed interval. Complete when: preflight traces prove the advertised workload, inputs and resulting state are observable, comparison paths and units are explicit, and repeated measurements report variability. Remove Criterion if these remain custom benchmarks. MeasureMachinecloning with a real state and held queue before attempting to change its rollback design. -
A19 — Preserve truthful error sources and error ownership. Confirmed implementation gap.
MachineError,TerminationMonitorError, andTerminationPropagationErrormanually implementstd::error::Errorwithout forwarding an enclosed error as its source. Their fields retain the cause, but standard error-chain consumers cannot reach it. Some public aggregate failures have only their domain sum, so formatting expectations also need an explicit caller contract. Complete when: required display/source contracts usethiserrorwith truthful source relationships and appropriate narrow bounds; caller tests prove the cause and complete owned rejection survive. Do not reclassify ordinary rejections or settlements as fatal behavior errors. -
A20 — Maintain a law-to-evidence ledger instead of a test-count claim. Coverage gap. Existing model, fuzz, compile, and mutation evidence is spread across crate-local suites and historical audits. A filename or green suite does not identify which invalid implementation it rejects. Complete when: each law maps to a focused example, a complete trace or independent model, relevant wrapper orders, invalid-use fixture, boundary and ownership cases, and mutation/counterfactual evidence. Mark a layer inapplicable with a reason. Add coverage measurement to locate unexecuted code, but never use a percentage as proof of the law. Keep the workflow's fuzz-target list synchronized with the manifest; it is currently complete. Assignment custody evidence correction: the external delivery fixture previously compared the original
Box<str>payload allocation with the address of the deliveredBoxhandle. A temporary equality assertion made both affected tests fail, proving that the old pointer comparison did not identify the payload. The retained fixture now records the deliveredstrallocation and contents. It verifies that FIFO's currentJob: Clonepolicy sends a distinct copied allocation, while rejection returns the original allocation. Two same-typed requests settle in reverse order and return their own receipts. Debug and optimized external fixture runs pass. This evidence satisfies the Behavior-side return shape of the clarified T02 law: the pool keeps its original job for retry and the worker receives a copy. The runtime close-after-resolution witness remains downstream. The source analysis is in the implementation ledger. Pre-commit host-refusal custody: the externaltests/interpreter-contract/tests/startup_host_rejection.rscaller runs a pure initialization with two ordered values whose types do not implementClone. It then requiresHostRejectedto return the mutated current child, both original send allocations in order, the untouched staged nested child, the route, creation IDs, kinds, and continuation decision. Debug and optimized fixture tests pass. This proves the Behavior-side return shape; no production host or Address reservation participates, so it does not establish full T11 or close A17.Behavior-side law ledger disposition: the focused entries below and the detailed A07/A20 records in this document identify the tested law, an independent trace or model where the law has a sequence, the applicable wrapper and invalid-use boundary, and an actual red baseline, mutant, or isolated counterfactual. A compile-fail fixture is inapplicable to a numeric capacity or stale-event decision that is intentionally decided at runtime; the focused return-value test is its boundary proof. A wrapper-order test is inapplicable to a standalone catalogue policy with no wrapper-owned lane; generic lane composition is tested separately. These are explicit inapplicability reasons, not inferred coverage from a green suite.
Law cluster Focused and independent evidence Boundary, composition, and counterfactual Core total interpretation and source custody total_interpretation,source_settlement_admission,action_interpretation, and the externalsource_free_custodyandsource_progressionfixtures inspect exact prefixes, residual values, and source admission.Both SendLayerorders, a mixed retirement creation/product and closed source are exercised; the original unconditionalExhaustedcode failed the focused fixture. External compile denials protect affine settlement authority. Real Driver execution remains A17.Fresh creation, occurrences, and startup return creation,child_occurrence_product, generated creation custody, and external host-refusal/panic fixtures inspect IDs, kinds, routes, current child, error, untouched effects, and complete batch order.Compile-fail and external privacy fixtures reject forged authority; the exact-one caller test was red before into_one. Public publication and Address races remain A17.Stable proxy and supervision proxy,proxy_command_recovery,stable_proxy_shutdown_model, fixed initialization/recovery, and dynamic cancellation/sequence suites check current attempts and ordered joins.The external proxy privacy fixture denies forged receipts and duplicate settlement; A07 activation, fixed, and dynamic mutation slices reject wrong correlations. Runtime restart success remains A17. FIFO and keyed work FIFO's independent admission queue and completion tests, keyed binding/assignment models, and the external assignment delivery fixture inspect payload, role, generation, correlation, and complete return. External compile failures deny receipt assembly and double settlement. A07 FIFO and keyed mutations fail the named tests. Actual Communication closure remains A17. Lifecycle, time, and workflow Exact termination and shutdown models, timing invariants, lease tests, acknowledgement and dependency workflow models compare full event sequences and returned values. Typed observation/shutdown fixtures cover authority; termination, lease, and workflow entries record specific counterfactuals. Numeric time and generation boundaries are runtime rejections, so compile denial is inapplicable. Routing, discovery, operations, persistence Independent routing, pub-sub, health, cache, readiness, and configuration models compare ordered state and complete Actionsafter generated operations.Admission, buffer, publication, health, versioned routing, and cache entries record actual mutants or isolated one-line counterfactuals. These standalone policies have no separate wrapper-order law. Machine, stash, and state replay fsm_properties,stash_properties,two_buffer, and focused algebra traces check held ownership, replay order, and phase transitions.The machine/stash/cache entry records a wrong phase equality and a no-op drain rejected by focused tests. Static infallibility of stashed inner behavior is the invalid-use boundary. Macros and public API Direct/facade Cargo fixtures, generated creation tests, Rustdoc compile-fail cases, and the external assignment/proxy privacy cases compile through public APIs. Ordinary unrenamed dependencies and facade-first Actors resolution both compile; earlier broken expansions and fixtures failed for the intended compiler diagnostics. Wrapper composition is covered by universal_layersandcompositions.Testkit and mutation tooling Driver/error properties, independent catalogue models, mutation-gate unit tests, and the pinned Nix coverage measurement exercise the verification machinery itself. A05's missing/failed-run counterfactuals fail the strict gate. Coverage percentages only locate unexecuted branches; the gate requires complete candidate accounting and catches every viable mutant in its selected campaign. Disposition:
passfor the Behavior-side law-to-evidence ledger. The detailed entries retain their specific limits; this table does not claim that one mutant proves every branch of a catalogue or that a pure model is a real interpreter. Earlier statements that A20 was open describe staged evidence reviews. A17 and the PRD's P5 matrix own the remaining runtime evidence.
Follow-on after the audit checklist
- Complete the Interpreter ownership and startup PRD against released Behavior crates. The Behavior-owned request, settlement, retained-custody, panic-outcome, caller-migration, documentation, and external-fixture work in P0–P4 is implemented on this branch; the implementation ledger names the exact local witnesses and their limits. T02 was clarified to retain FIFO's existing clone-and-retry policy after its ownership contradiction was proved from source. The original PRD edit remains untouched in the other worktree. Follow its ordered work packages P0–P5 and prove every acceptance trace T01–T24 against the real Address and Bombay interpreters. Include complete rejection and terminal custody, external consumer compilation, release and downstream lock verification, and every required repository gate. Apply its architecture checkpoints and definition of done; the audit checklist does not narrow the PRD's scope. Its optional P6 consolidation follows the blocking contracts only where an independent law proves the deletion. P5's integrated witnesses are required to close A17; the A20 ledger explicitly marks its pure-versus-runtime limits. Those downstream rows remain open after this Behavior release until that proof exists.
Coverage map and what should remain distinct
This table locates the continuation work for every family. Existing evidence is valuable even where A06 or A20 requires a stronger oracle.
| Area | Existing evidence inspected | Remaining audit obligation |
|---|---|---|
| Core actions, sending, source custody | total_interpretation, action_interpretation, source admission/custody and settlement tests | Complete-product assertions already exist; retain them across A11 and prove hostile source admission |
| Core addressing, creation, occurrences, event paths | creation/custody tests, child-occurrence product tests, generated creation tests, compile-fail rustdoc | A01/A14/A17; distinguish logical address, exact endpoint, and creator-local correlation |
| Machine and stash | fsm_properties, error_paths, stash_properties, two_buffer, fuzz sequences | Retain rollback and infallible replay laws; A09/A10/A18 |
| Composition and activation | algebra, universal_layers, init_contract, compositions, owner-scoped delivery | A03/A13/A14; prove consumer inference separately from structural interpretation |
| Stable proxy | proxy, stable_proxy_shutdown_model, operation-settlement tests, shutdown/replacement fuzz | A01/A07/A12/A17; preserve return/stop joins and retained affine activation data |
| Fixed supervision | initialization, recovery diagnostic, construction and protocol tests; four sequence fuzz targets | A03/A07/A12/A17; preserve ordered role policy, independent outcomes, and complete batch return |
| Dynamic supervision | dynamic, cancellation and shutdown fuzz targets | A03/A07/A12/A17; preserve key/generation/operation correlation and transferred cancellation |
| FIFO pool | split FIFO integration tests, independent admission-queue property, fifo_pool_sequences | A03/A06/A07/A12; prove fairness, retry placement, assignment custody, capacity, and termination independently |
| Keyed pool | binding, customer, assignment, lifecycle and compile tests; two keyed fuzz targets | A03/A06/A07/A12; preserve binding generations and per-role order separately from FIFO policy |
| Lifecycle and shutdown | shutdown models, heterogeneous shutdown, exact termination model, propagation sequences | A01/A17/A19; distinguish watch recurrence from exact-once monitoring and homogeneous from heterogeneous ownership |
| Time | receive-timeout model, timing invariants, init/composition tests, timer settlement tests | A06/A15; prove exhaustion and stale/duplicate input in both profiles; keep one-shot, periodic, deadline and inactivity policies distinct |
| Routing | catalogue models, routing/correlation invariants, exact reply tests | A06/A07/A11; distinguish sequencer gap closure from explicit watermark release and queue policy from delivery acceptance |
| Discovery | registry/topic/pub-sub models, presence fuzz, resolver unit tests | Assert snapshot order, stale versions, recipient identity, and complete rejected commands; retain read-only resolver authority |
| Operations and persistence | configuration/readiness/health/cache models | A06/A13; preserve health tombstones, fixed readiness membership, version conflicts, LRU ownership |
| Workflow | workflow invariants, barrier/latch tests, catalogue fuzz | Assert complete activations and terminal/stale cases; latch, reusable barrier, and dependency workflow have different laws |
| Macros and published consumers | parser permutations, behavior generation, facade fixture workspace | A02/A08; parser success and token text cannot substitute for consumer compilation |
| Testkit and quality tooling | driver properties, independent models, gate unit tests, Nix/CI definitions | A05–A10/A18/A20 |
Retain the pure effect boundary, concrete protocol sums/products, owned
rejections, sealed structural authority, explicit restart provenance, and
deterministic time inputs. A source scan found no dynamic-dispatch/type-erasure
escape hatch in the reviewed Rust implementation. Heap allocation, an Arc
correlation witness, or a long type is not independently evidence of waste.
Completion method
Work in this order: repair evidence integrity (A04/A05/A08), resolve current contract blockers (A01–A03), strengthen the relevant oracles (A06/A07/A17), then remove proven duplication (A09/A11–A16/A18/A19).
For every retained semantic batch, record the actor-model, derived, or Bombay policy law before editing; write the failing caller or transition regression; identify the existing compositions it reuses or deletes; and complete the AGENTS provenance and aggregate-drift checkpoints. Do not count this audit as those future experiments' approval or evidence. Respect the repository's cumulative surface checkpoints.
Every proposed deletion must answer: what law or consumer needs this code, what breaks if it is removed, and which independent test detects that break? Every proposed abstraction must identify repeated semantics it deletes and prove two real substitutions. Shared test setup may be reduced; independent oracles must not be implemented by calling the production decision logic.
Verification record
A19 pre-edit error-chain law
Classification: Rust caller contract and deliberate Bombay ownership policy.
When an aggregate error encloses a causal error, Error::source exposes that
exact cause. An ordinary unexpected-report rejection has no causal source and
retains its report; a machine failure retains its exact user event. The focused
actors/tests/error_sources.rs caller checks all three error types and the
owned values. It failed three tests on the previous implementations because
every source was None. The existing error sums and fields remain the complete
model; the edit changes formatting and source forwarding only. No actor state,
effect, interpreter action, wrapper order, or public type shape changes. The
aggregate-drift checkpoint is therefore unchanged for control states,
subordinate alternatives, branches, modules, and public spellings, with no
new arrival history, repeated cause, false cardinality, nested authority,
semantic boolean, or positional user syntax. Disposition: pass for the
pre-edit model; source implementation and caller verification follow.
The three existing error shapes now derive thiserror::Error and identify only
their enclosed cause as a source. Their manual Debug implementations still
avoid a Debug requirement on the owned event or report. The focused caller
suite passes all three cases, including None for ordinary unexpected-report
rejections and direct inspection of the retained event/report. No source or
payload type was added; manual display/error implementations were deleted.
Post-edit aggregate states, alternatives, branches, modules, and public
spellings remain unchanged. The residue scan and law cross-check remain as
recorded before the edit. Disposition: pass.
A08 published consumer and A16 entry-point evidence
The macro consumer test explicitly selects the facade sibling example. A
temporary compile_error! appended to that example made the focused gate
fail with the deliberate diagnostic; the file was restored. The release-lane
package script now assembles all three publishable archives, then extracts
their actual contents and runs cargo check on a fresh consumer. Its manifest
uses the root README's dependency declarations and patches only the three
local archives so unpublished sibling packages resolve. The consumer compiles
both #[behavior] and #[pool_worker] from the archived libraries. The
archive build and consumer check passed. The Nix package check remains a
separate content-list gate because its sandbox has no registry access; it does
not claim to build an extracted archive.
The root and three crate READMEs now have current entry points, working local
links, and runnable commands. The root consumer dependencies select the
packaged versions. Repository link tests cover the READMEs; the core example
and macro consumer passed. Historical engineering verdicts now identify the
last committed evidence revision (1f20cc4) and point to this current audit.
The current root README no longer describes the repaired A03 projection as
open. These are documentation and verification changes: no actor control
state, effect lane, or public Rust spelling changed. Disposition: pass.
A08 Nix test-runner ownership, before edit
The flake's bombay-behavior-nextest check already runs every workspace unit
and integration test in an optimized build; the separate
bombay-behavior-doctest check runs the workspace doctests. The
bombay-behavior package check also invokes Cargo's test phase, repeating
those executable tests and the slow external macro consumer builds. In the
committed hash-policy gate, Nextest passed 834/834 cases while the package
check separately ran the same crate_resolution cases. The derived gate law
is one owner for each test class plus an independent package build: keep
Nextest as the workspace executable test gate, keep the doctest check, and
make the package derivation build without its duplicate test phase. No source
test, target, profile, package artifact, dependency policy, or public API is
removed. The focused regression is a full Nix check whose log must show all
workspace cases under Nextest and the doctests under their own derivation,
while the package derivation completes without another unit/integration test
run. Expected files are flake.nix and this audit; production Rust delta and
public type delta are both zero.
At signed commit 326f1c2, the clean-worktree nix flake check -L passed all
eight active aarch64-darwin checks. Nextest ran 836/836 workspace tests;
the doctest derivation passed separately. The package derivation built and
installed with doCheck = false and no checkPhase, so it did not repeat the
workspace executable suite. The check omitted incompatible systems as usual.
No actor state, public spelling, production Rust line, or test case changed.
Disposition: pass for the gate ownership repair.
A10 finite-driver contract
drive now explicitly documents that its successful Trace appends send and
creation lanes separately and loses turn boundaries. On a later controlled
error it returns only the cause, dropping the active behavior and successful
prefix actions; the mailbox keeps the suffix after the rejected input. A
success-success-error caller test confirms two successful transitions occurred
before the error and that one unconsumed event remains. Order-sensitive A01
creation tests use their own per-operation interpreter trace rather than
accumulating Trace effects. The focused error-path suite passed five tests.
This is a documented limited driver, not a runtime-ordering witness. No actor
or testkit state shape, effect lane, public spelling, or transition branch
changed. Disposition: pass.
A18 benchmark workload and measurement
The protocol-matrix benchmark now activates the base behavior through the
same owned path as its composed cases. A preflight trace checks three exact
FSM phases (A -> B -> A -> B), accumulated state 6, and an empty held
queue; the timed workload changes phase on every event and observes its final
state and phase. The clone measurement uses a 64-value state and 128 held
messages before timing, so it measures the actual rollback copy. The FIFO
benchmark keeps its complete submit/assignment/settlement/completion cycle
and passes the full customer outcome slice to black_box. Both programs now
start each sample from a fresh definition and report minimum, median, maximum,
and sample count. Matrix rates are transitions per second except clone
operations per second; FIFO reports completed cycles per second. The custom
benchmark lane no longer depends on Criterion. Three-sample low-iteration
preflights ran successfully for both programs; those timings are correctness
smoke checks, not performance conclusions. No actor law or state type changed.
Disposition: pass.
A11 unused tuple-settlement export, before deletion
Classification: derived ordered-effect product law. A named send product
itself owns its declared lane names, settlement shape, left-to-right
interpretation, and intact unattempted suffix. The public, doc-hidden
settle_in_order function offers only a two-product tuple settlement; it
establishes no additional actor capability or independent invariant. A source
search across the Behavior workspace and adjacent Bombay/Address repositories
found its declaration and two re-exports but no Rust caller. Existing
named-product tests and external interpretation callers exercise their own
generated traversal, so deleting the dead export changes no transition,
effect, or ownership return. Expected production touch: core
effects/sending.rs, effects/mod.rs, and lib.rs, about 29 deleted lines,
zero new public types, and one removed public function; update the two
documents that name it. Core behavior control states, subordinate
alternatives, branches, and modules remain unchanged. The future-needed
values are each product's named lanes and complete settlements, already
retained by its implementation. No arrival history, repeated cause, false
cardinality, nested authority, semantic boolean, or structural caller syntax
is proposed. Cross-check: actor transition algebra and atomic runtime
settlement law. Disposition: pass for this deletion design, pending gates
and final measurements.
The unused function and both re-exports are removed. The core production
delta is +2/-31/net -29 physical lines, tests +0/-0/net 0, public types
+0/-0, and public functions -1; effects/sending.rs is
1,505 → 1,477 lines. Control states, subordinate alternatives, transition
branches, and module counts are unchanged, and each named product still owns
the same fields and settlement return. The source scan found no remaining
Rust call site; the Behavior, actors, and testkit Nextest run passed 808/808
cases, with formatter, book build, and diff checks passing. The residue and
law-document cross-checks above still hold. Disposition: pass for the
unused-export deletion; A11 remains open for generated logical projection.
A11 pre-edit product-equivalence law
Classification: derived product law and Rust interface cleanup. Buffer's
released target deliveries and factual outcomes have exactly the same two
named lanes as the existing routing DeliveryOutcomes: deliveries before
outcomes for interpretation and append, each lane returned intact on
corruption, the same source-custody admission, and the same ordered logical
host projection. Neither product owns a Buffer-only invariant; Buffer's queue
and overflow policy live in BufferState. The caller-level syntax should
therefore be Behavior::Sends = DeliveryOutcomes<TargetSends, ReplySends> for
Buffer just as for Sequencer and OrderGate. A focused type assertion in
routing/buffer.rs is the pre-edit regression. The existing product,
named-product interpretation, tuple source custody, and wrapper composition
are reused;
the duplicate public BufferSends implementation and its duplicate logical
projection will be deleted. No transition, owned effect, interpreter
operation, ordering, or error semantics changes.
Aggregate-drift checkpoint: Buffer's control state remains its queue and
policy product before and after, with no subordinate state/result alternative
or transition branch changes. The exact future-needed values remain the
queued owned payloads and reply routes, positive capacity, and overflow
policy. The proposed edit deletes a transparent send-product name and its
implementations without adding modules, states, branches, public spellings,
arrival history, repeated cause, false cardinality, nested authority,
semantic boolean, or positional consumer syntax. The source law is the
ordered effect-product equation in actor-transition-algebra.md; the
normalized FIFO and routing laws remain distinct and are cross-checked.
Disposition: pass for the model, pending the focused test and edit. This
does not declare all A11 derivation paths unified.
The focused Buffer caller failed before the edit with E0271: its associated
Sends was BufferSends, not the required DeliveryOutcomes. It passed after
the edit. The two products had the same public field names and six identical
laws (SendEffects, SendsFor, settlement classification/unattempted,
source custody, interpretation, and logical-host projection). Buffer now
uses the existing routing product; Sequencer, OrderGate, and other routing
templates already use it. The former BufferSends spelling and its duplicate
projection are gone; there is no compatibility alias. In the affected source,
routing/buffer.rs fell from 591 to 498 lines despite the new focused test,
and requirements.rs deleted ten net lines. Modules, control states,
subordinate alternatives, and transition branches remain unchanged; the one
deleted public type is the only public spelling delta. Every surviving state
alternative still retains its prior current values. The residue scan and
law-document cross-check found no new history, duplicated cause, cardinality
assumption, nested authority, semantic boolean, or structural consumer syntax.
Disposition: pass for this duplicate deletion. Broader A11 derivation work
remains open.
The remaining product paths have different type equations. This inventory is the input to the next A11 design experiment; matching method names alone does not justify merging them.
| Product path | Product and settlement shape | Ordered lanes and shared law | Distinct obligation |
|---|---|---|---|
Former handwritten DeliveryOutcomes, LeaseSends, PresenceSends | Two generic fields; settlement reuses the same product with settled field types | Empty/append, left-before-right interpretation, unattempted suffix on corruption, source custody, classification, and logical projection | Their public domain field names differ; all three now use the shared private derivation. |
Private send_product!, formerly atomic::request_product! | One or more generic named fields; settlement reuses the product | The same ordered operations and projection are generated together | Source custody must preserve every earlier settlement and every unvisited owned field across any arity. |
#[behavior] generated sends | Generated fields may have concrete types; a separate generated settlement struct holds associated settlement types | Generated lane order, corruption suffix, and source custody use the same transition equation | Separate settlement representation and caller lane methods are part of the generated API; this path currently has no generated logical-host projection. |
The named generic products now share the private derivation and retain their public field names and both wrapper orders. The generated product still needs a lawful public projection witness before its distinct settlement shape can share an implementation. This inventory itself changed no product or API.
A11 generated-send projection law, before implementation
Classification: derived typed-composition law. A #[behavior] send product's
logical destinations are exactly the ordered, duplicate-preserving append of
its declared lane projections. A lane with no logical destination contributes
NoBirthProtocols; a nested interpreter-request lane contributes only its
declared logical protocols. The caller syntax is
<Bootstrap as LogicalHostRequirements>::LogicalHosts, without a handwritten
LogicalDeliveryProtocols for BootstrapSends implementation. Existing
behavior/tests/behavior_generation.rs has precisely that handwritten
implementation, so removing it is the focused pre-edit compile regression.
The expected product is FirstDestination followed by SecondDestination.
The macro already generates the named send lanes and settlement interpretation;
the implementation should reuse each field's LogicalDeliveryProtocols and
the existing BirthProtocolProduct::Append. No new runtime operation,
transition, wrapper, or host lookup is required. The caller must still compose
through existing wrapper projections in either order.
Aggregate-drift checkpoint: the generated behavior's control states,
subordinate alternatives, transition branches, production modules, and
public type spellings are unchanged. Each lane's current value remains in its
existing generated field. The proposed implementation adds one derived trait
impl to the existing generated product and deletes the handwritten witness;
it stores no history, duplicates no cause, asserts no cardinality, creates no
nested transition authority or semantic boolean, and exposes no positional
consumer path. The law is cross-checked with actor-transition-algebra.md and
the normalized logical-host contracts in this audit. Disposition: pass for
the pre-edit model, pending the focused failing regression.
Removing the handwritten BootstrapSends projection produced E0277 in the
Nix-pinned behavior_generation caller: both BootstrapSends and a second
generated product with a request lane and repeated delivery destination lack
LogicalDeliveryProtocols. An unconditional generated impl using the field
projections then failed E0446 for existing private interpreter-request types.
That candidate was removed, the test fixture restored, and the experiment
recorded in the root DEAD_ENDS.md. A11 remains open; a public
interface law must be established before another generated projection edit.
A later public wrapper probe avoided E0446 but lost the concrete structural
product needed for recursive host traversal. Its reopen record is also in
DEAD_ENDS.md; no generated projection was retained. The existing
behavior_generation caller now uses a direct PhantomData type check in
place of a one-implementation Same trait to keep the exact structural
product requirement visible with less test machinery. All 18 cases in that
test binary passed under the Nix-pinned toolchain.
A11 named generic product derivation, before implementation
Classification: derived ordered-product law and private implementation
consolidation. LeaseSends<OutcomeSends, Schedules> and
PresenceSends<ReplySends, Schedules> each own two named generic lanes. For
both, empty and append act lane by lane; interpretation visits the first lane
before the second, preserves the unattempted suffix on corruption, and
continues to the second lane after lawful rejection. Source admission visits
in the same order and returns the complete named product. Settlement status
combines both lanes, and logical-host projection appends their protocol
occurrences in order. The distinct public field names remain part of the
contract. This is the same law already derived by the private atomic
request_product! macro for generic named lanes. Moving that existing
derivation to the actor-crate root and giving it a domain-general send-product
name can delete the duplicate implementations; it adds no public framework.
Focused characterization before production edits passed in the Nix-pinned
toolchain: requirements::tests::lease_and_presence_products_keep_both_wrapper_orders
proves both logical orders through SendLayer; total_interpretation
checks Lease's corrupt suffix and Presence's rejection followed by its
independent schedule lane. These tests pass on the prior implementation
because this stage removes duplication without changing the observable law.
The existing four atomic request products and the core ordered settlement
and source-custody products remain the lower-order witnesses. The expected
design-stage files are the private derivation module, its four atomic import
sites, the actor crate root, Lease, Presence, their two projection impls in
requirements.rs, and the focused tests. The expected production delta is
roughly 200 fewer lines, with zero new or removed public types; later product
migration is a separate measured stage.
Aggregate-drift checkpoint: Lease and Presence control states, subordinate
alternatives, transition branches, modules by count, and public spellings
remain unchanged. The exact current values are Lease's owned outcomes and
schedule requests and Presence's owned replies and schedule requests. No
arrival history, repeated cause, false cardinality, nested authority,
semantic boolean, or positional consumer syntax is introduced. The ordered
send-product equation in actor-transition-algebra.md and the normalized
timing and presence contracts are cross-checked. Disposition: pass for the
pre-edit model and both focused caller witnesses.
The design stage moved the existing 273-line private macro from atomic
requests to the actor-crate send-product owner, changed its private name, and
retained field rustdoc. Lease and Presence now invoke it with their original
public field names. Their handwritten SendEffects, SendsFor, settlement,
source-custody, interpretation, and separately maintained logical-projection
impls were deleted. Both wrapper-order projections passed, all five focused
total-interpretation cases passed, and the actor crate's 158 unit tests passed
under nix develop. The retained production representation is 196 physical
lines smaller; focused tests add 56 lines, yielding 169 fewer actor src
lines including its new unit test. Public types and spellings, modules by
count, aggregate states, subordinate alternatives, and transition branches
are unchanged. Every surviving product field still owns its prior value.
The residue scan and law-document cross-check remain as recorded above.
Disposition: pass for the design stage. Other generic products are a
separate mechanical migration; the distinct proc-macro-generated settlement
shape remains an open A11 design question.
The measured mechanical stage applies only the proven syntax to
DeliveryOutcomes, WorkQueueSends, BreakerSends,
TerminalPropagationSends, and ProxyEffects, and removes their matching
separate projection impls. Each has generic named fields, the same ordered
interpretation/source-custody/settlement equation, and an existing real
consumer. Expected production delta is roughly 800 fewer lines, with zero
new or removed public types. No new effect lane, bound, state, or fixture is
authorized by this migration; a mismatch reopens the derivation instead of
adding a one-off branch.
The mechanical stage now uses the shared derivation at those five sites and
deletes their former implementations. ProxyEffects retains all seven public
field names and their declared order while losing its nested tuple source
plumbing. The actor src tree changed by +111/-1,104 physical lines, net
-993 including the focused unit test; the production representation alone is
about 1,020 lines smaller. No public type was added or removed. The actor
all-target Cargo check, formatter, 812 workspace Nextest cases, and current
workspace coverage run passed under the Nix toolchain. The full 21-check Nix
flake gate also passed from a clean worktree at signed commit 65caa65.
Post-migration aggregate-drift checkpoint: the affected routing, timing,
discovery, lifecycle, and stable-proxy control states, subordinate result
alternatives, and transition branches are identical before and after; no
aggregate transition function changed. The current values in every surviving
send alternative are the same owned effect lanes, including all seven stable
proxy lanes. Modules remain constant by count because the derivation module
moved from atomic/ to the actor root. Public spellings remain constant. The
residue scan found no new arrival history, repeated cause, false cardinality,
nested transition authority, semantic boolean, or positional consumer syntax.
The ordered product law in actor-transition-algebra.md and the normalized
atomic, routing, timing, discovery, and lifecycle contracts were cross-checked.
Disposition: pass for the retained representation.
A11 named-send equation inventory
The two current general derivations preserve the same named field order and the same complete settlement alternatives, but they have different authoring inputs. This is a pre-design comparison, not approval for a shared public product framework.
| Equation | send_product! in actors | #[behavior] proc macro |
|---|---|---|
| Authored product | A generic named struct with semantic fields. | A behavior declaration generates a named sends struct, a distinct settlement struct, lane selectors, and fluent action methods. |
| Empty and append | Delegates to each field in declaration order. | Delegates to each field in declaration order. |
| Interpretation | Traverses fields in declaration order; corruption preserves the committed prefix and makes the untouched suffix unattempted. | The same ordered traversal and corrupt suffix equation. |
| Settlement status | Combines every named field. | Combines every generated settlement field. |
| Source custody | Offers each field in order; admission or closure retains the complete remaining product. | The same ordered offer and remaining-product equation. |
| Event-lane routing | Composes the fields' SendsFor<Event> proofs; authored code selects a concrete field. | Composes the fields' SendsFor<Event> proofs and generates SendInput selectors and fluent methods. |
| Logical hosts | Appends every field's LogicalDeliveryProtocols in interpretation order. | No generated LogicalDeliveryProtocols implementation is present. A lawful addition must distinguish logical deliveries from exact and interpreter-request lanes. |
ReplyDeliveries has a logical and an established route with distinct target
laws; HeterogeneousShutdownSends has its own finite target shape. Their
manual projections in requirements.rs are therefore evidence of semantic
exceptions, not merely missed macro invocations. The next A11 hypothesis must
prove the logical-host equation through two unrelated proc-macro behaviors and
both wrapper orders before it can subsume either derivation. The aggregate
drift checkpoint for this inventory has no retained production change: control
states, subordinate alternatives, branches, production lines, modules, and
public spellings are unchanged; no new history, cause, cardinality, transition
authority, semantic boolean, or positional consumer syntax is introduced.
Disposition: pass for the evidence record only.
The next focused A11 law is a derived logical-host projection: a generated
named send product contributes each field's LogicalDeliveryProtocols in its
declared order, including duplicates, while exact delivery and interpreter
request fields contribute their existing empty projection. The user-level
syntax is a LogicalHostRequirements bound on two unrelated generated
behaviors and LogicalDeliveryProtocols bounds on both SendLayer orders;
the expected type product spells the actual protocol order. Before changing
the proc macro, caller tests in behavior_attribute.rs and compositions.rs
must fail solely because the generated named sends lack this trait. Existing
field projections, BirthProtocolProductAppend, and wrapper composition are
the lower-order laws. No actor state, effect lane, transition, generic
parameter, runtime port, or policy is added. Pre-edit aggregate drift:
control states, subordinate alternatives, transition branches, production
lines, modules, and public spellings stay unchanged until the focused witness
fails; the generated product retains only its authored fields. No arrival
history, repeated cause, false cardinality, nested authority, semantic
boolean, or positional caller syntax is proposed. Cross-checks:
actor-transition-algebra.md, behavior-layer-laws.md, and normalized
atomic documents. Disposition: pass for the proposed projection model,
pending the red caller witness.
The two caller tests failed before production with only E0277: generated
PrinterSends and GeneratedBaseSends lacked
LogicalDeliveryProtocols. Printer's direct logical-host bound and both
SendLayer orders failed for that missing trait; GeneratedBase failed for
the same reason. The first draft of the wrapper type assertion expected the
outer lane before the inner lane and produced an unrelated E0271; it was
corrected to the existing inner-before-outer law in SendLayer before this
red result was accepted. A 24-line generated-impl candidate then reopened the
design. It conflicted with an existing handwritten BootstrapSends impl and
leaked private protocol/request types from public generated products (E0446).
The candidate and focused test edits were removed; the exact falsifier and
post-experiment drift checkpoint are in DEAD_ENDS.md. The missing projection
remains open and requires an explicit visibility/ownership model before code.
Next A11 visibility hypothesis, before implementation: a generated named send
product is part of a public actor's interface only when that actor exports it.
The #[behavior] attribute is applied to an impl, so it cannot inspect the
actor declaration's visibility. The current unconditional pub product makes
private actors' private request and destination types leak through the
generated LogicalDeliveryProtocols::Protocols associated type. A Rust 1.95
scratch compile confirmed that a private product may implement this public
trait with a private projected protocol, while a public product triggers
E0446 for the same private protocol. The candidate syntax keeps
sends = { ... } module-private and permits the ordinary Rust visibility
spelling sends = pub { ... } when a public actor deliberately exports the
product. Generated lane selectors, settlement product, and fluent trait must
have the same visibility as the named product; no consumer supplies a no-op
route, marker, or policy. The generated projection appends every field's
existing LogicalDeliveryProtocols::Protocols in declared order, retaining
duplicates and the empty projections of exact and request lanes. Private
Bootstrap, Printer, and GeneratedBase are real callers; a public actor
with public recipient protocol will prove the exported form. The prior
two-behavior E0277 regressions and both SendLayer orders are the focused red
witnesses, supplemented by a red parser witness for sends = pub { ... }.
The existing BirthProtocolProduct::Append, field projections, product
settlement, and wrapper laws are the lower-order contracts. No actor control
state, event, effect lane, interpreter operation, or policy is authorized to
change. Pre-edit drift: product fields and order, actor states, transition
branches, production modules, and public actor spellings remain fixed; the
generated product's public exposure is the one intentional interface change
for private actors. The values needed later are each declared send lane's
logical-host protocol product and its exact settlement custody. There is no
arrival history, repeated cause, false cardinality, nested authority,
semantic boolean, or positional caller syntax in the proposed model.
Cross-checks are the actor transition algebra, behavior layer law, recursive
host consumer in logical_host_requirements, and normalized atomic docs.
Disposition: pass for the hypothesis only, pending focused red witnesses
and a full generated-product visibility inventory.
The focused pre-edit callers now fail in the intended places. compositions
reports three E0277 diagnostics because GeneratedBaseSends lacks
LogicalDeliveryProtocols: one direct owner and each SendLayer order.
behavior_attribute reports the same three E0277 diagnostics for
PrinterSends; its deliberately public actor is rejected at sends = pub
by the old parser, so the later missing Behavior diagnostic is a consequence
of that parse failure. No route, birth, or settlement mismatch appears in the
red logs. The public actor emits a real reply, and neither private caller
supplies an inert policy or placeholder.
The design-stage candidate now derives LogicalDeliveryProtocols from the
named fields and makes generated send products and their companions private
by default, with an explicit Rust pub form for an exported actor product.
The private BootstrapSends handwritten projection was deleted. The two
focused testkit suites passed 6 and 11 tests, including their exact direct
and both-order host products; core generated-product and source-admission
suites passed 19 and 6 tests. The duplicate-lane test retains two occurrences
of the same destination after an empty interpreter-request lane, while a
request-only product projects the empty host product. An external fixture
successfully names both the public send and settlement products and checks
its exact logical host type. All workspace targets passed cargo check
under the Nix toolchain. The macro unit suite passed 6 tests. The full Nix
gate for this candidate remains pending.
Post-design aggregate-drift checkpoint: actor control states, subordinate
state alternatives, transition branches, send field order, settlement
alternatives, and runtime ports are unchanged. The macro parser still has
the same Existing/Generated send alternatives; Generated now retains the
explicit Rust visibility needed for its public interface. The macro source
grew from 1,499 to 1,534 physical lines, including the visibility parser and
one generated logical-host implementation template. The eight-line
handwritten BootstrapSends projection disappeared; production module count
is unchanged. Existing generated product names are retained, but products
for private actors cease to be unnecessarily public, and the new exported
form is public by declaration. The surviving values are each named send lane,
its exact ordered settlement, and its statically projected logical-host
protocols. The residue scan finds no arrival history, repeated cause, false
cardinality, nested transition authority, semantic boolean, or structural
consumer syntax. The actor transition, wrapper-order, and recursive host
contracts were cross-checked. Disposition: pass for this focused projection
and visibility stage, pending its full gate. The wider A11 one-implementation
derivation remains open.
Clean 24fe931 Nix gate: all ten active aarch64-darwin checks passed,
including the optimized Nextest campaign (841/841). The four macro fixture
checks took about three minutes each under the shared fixture lock; all passed.
Next A11 consolidation hypothesis, before implementation: an authored generic
named send product and a #[behavior] generated named product have the same
field-order, empty/append, logical-host, interpretation, source-custody, and
settlement-status equations. Their Rust settlement representations differ:
the authored product is generic over each lane and reuses its own struct with
settled lane parameters, while the behavior attribute generates a separate
nominal settlement struct for concrete authored lane types. A shared
procedural generator should own the traversal equations, with that
representation difference explicit. The caller syntax for an authored
product is a normal named generic struct with a SendProduct derive; its
public fields, generic parameters, settlement associated type, and ordered
Actions remain identical. The first focused regression will use a real
two-lane effect fixture and fail on the old code because this derive does not
exist; it must then prove accepted, retained, closed, and corrupt suffix
custody. Two unrelated existing products, DeliveryOutcomes and LeaseSends,
and both SendLayer orders must pass before any catalogue migration. The
lower-order contracts are the current send_product! equations, the
generated product just proved above, ActionItem settlement products, and
the recursive logical-host proof. No new actor state, event, effect lane,
runtime port, or no-op caller input is authorized. Pre-edit aggregate drift:
all actor control sums, subordinate alternatives, transition branches,
product fields, and public spellings remain fixed. The candidate may add one
proc-macro entry point while deleting the 273-line declarative derivation
after proof; source-line reduction is diagnostic, not acceptance. Each lane's
owned settlement and declared order remain the only future-needed values.
The residue scan finds no proposed arrival history, repeated cause, false
cardinality, nested transition authority, semantic boolean, or positional
caller syntax. Cross-checks: actor transition algebra, A11 equation table,
source settlement law, wrapper law, and normalized routing/timing contracts.
Disposition: pass for the hypothesis only; implementation must reopen if a
third representation or caller placeholder is required.
The pre-edit SourceAdmissionSends<ProxySends, AssignmentSends> caller now
uses the proposed derive on two real source-action lanes. It checks the exact
generic settlement type; empty/append; accepted first admission; closed
second admission with complete residual custody; corrupt first result and
unattempted second suffix; and rejected-result retention. On the old code,
its first diagnostic is E0433 for the absent behavior_macros::SendProduct;
the remaining seven diagnostics are the missing trait implementations that
this derive is meant to produce. No unrelated interpreter or route mismatch
appears in the red log.
Retained A11 derivation batch: SendProduct now derives the named generic
product contract for authored lanes. The two unrelated actor products
DeliveryOutcomes and LeaseSends passed the 167 actor unit tests in debug
and optimized builds. The source-custody fixture passed eight tests, including
both orders around SendLayer; the generated-product fixture passed nineteen.
All eleven former send_product! declarations compile through the derive,
and workspace --all-targets tests passed. The 273-line declarative macro
and its module were deleted. One procedural settlement generator now owns
classification and ordered source custody for both authored and generated
products; the interpreter traversal is shared as well. The remaining duplicate
effect-product trait generation between the two procedural paths must be
consolidated before A11 closes.
Aggregate-drift checkpoint for this retained derivation batch: actor control
states, subordinate alternatives, transition branches, effect lanes, and
public product spellings are unchanged (zero added or removed). Eleven macro
invocations became eleven ordinary named structs with one derive each; one
actor source module was removed. Every surviving field owns the same current
lane value needed for ordered interpretation and exact settlement return.
The residue scan found no new arrival-history state, duplicated cause, false
cardinality, nested transition authority, semantic boolean, or positional
consumer syntax. Cross-checks: the actor transition algebra, A11 equation
table, source-settlement law, both wrapper orders, routing and timing
contracts. Disposition: pass for the already-proven representation migration;
the remaining common trait generation is still open.
Follow-up A11 consolidation: named_send_contract now generates all five
send-product traits for both authored and #[behavior] products, while
named_settlement_contract generates their classification and source custody.
Only their truthful settlement representations and generated fluent lanes
remain separate. This removes another 100 net production lines from the macro
crate. The focused generated and authored tests (19 and 8) and workspace
--all-targets tests passed after the consolidation. The first clean Nix gate
for the preceding migration commit reached the documentation checker, which
found six consuming calls inside new assertions. The test moved those calls
before the assertions; its focused tests and check_assertion_effects.py now
pass. A clean gate on the corrected consolidation is pending.
Aggregate-drift checkpoint for consolidation: actor control sums, subordinate
alternatives, transition branches, product fields, modules, and public
spellings all remain unchanged (zero delta). The macro crate replaces two
copies of the same five trait equations with one generator; no current actor
value is added or removed. The residue scan remains clear for arrival history,
repeated causes, false cardinality, nested transition authority, semantic
booleans, and structural caller syntax. Cross-checks: actor transition law,
A11 equations, source-custody law, generated behavior contract, both wrapper
orders, normalized routing and timing documents. Disposition: pass; the clean
Nix gate passed all ten checks with 843/843 optimized tests.
A12/A13 least-loaded membership owner, before edit
Classification: deliberate Bombay routing policy and derived Rust ownership
law. Router owns the ordered, duplicate-free member list. LeastLoaded
needs exactly one current LoadEvidence per member position, not a second
copy of each recipient. On add it appends Unknown; on removal it deletes that
position; observations find the member in the Router list and update only
the aligned evidence; selection uses the first minimum observed load. An
unknown, stale, or conflicting observation still returns the exact request.
The caller syntax is Router<A, Recipient<P>, LeastLoaded> and
router.load_evidence(&recipient); it borrows for observation and exposes
neither an index nor a
route parameter on the policy's stored state. Two different protocol routes
can use the same policy type. The existing independent least-loaded trace is
the pure transition oracle; the external protocol_bounds compile witness
must first fail solely on the old required generic argument.
Before-edit aggregate control is one Router membership list, with
LeastLoaded holding a parallel list of recipient/evidence pairs. After the
candidate, Router remains the only member identity owner and the policy holds
an aligned evidence list. The subordinate sum remains Unknown or Observed
(version, load); its current version/load are needed for every later
selection and stale/conflict decision. Neither the actor's control states nor
its effects, errors, interpreter path, transition branches, or module count
change. Expected files: routing/router.rs, the external protocol caller,
the routing model, and this audit; expected production delta is negative and
no public type is added. The public policy loses one generic parameter and
its direct recipient lookup; Router gains one semantic evidence lookup.
The current Router::new and membership transitions are the only legitimate
policy-hook callers; direct policy use with an unrelated member slice must
return typed unknown evidence, not index-panic. The residue scan finds no
arrival history, duplicated cause, false cardinality, nested authority,
semantic boolean, or structural user syntax. Cross-checks are the actor
transition algebra and normalized routing law. Disposition: pass for the
model, pending the focused red compile witness and implementation.
Post-edit checkpoint: protocol_bounds failed before the production edit with
only the two expected E0107 diagnostics for LeastLoaded's unwanted route
parameter, then compiled with Router<MailAddr, Recipient<Destination>, LeastLoaded>. The independent 384-case least-loaded trace passed in debug
and optimized builds. All eight focused router unit tests passed, including
the exact owned observation returned when a direct policy caller supplies a
member slice without matching policy evidence. mdbook build docs passed.
The Router control state remains one ordered, duplicate-free membership list;
the four message alternatives and three Router error alternatives remain four
and three. Policy evidence remains the same two-alternative sum, Unknown or
Observed(version, load), and its three typed rejection alternatives remain
three. There is no new transition authority, module, effect, or interpreter
path. The production portion of routing/router.rs fell from 879 to 863
physical lines; one private recipient/evidence product disappeared. The
public policy loses its route parameter and direct identity lookup, while the
Router gains the recipient-based evidence lookup. Router owns current member
identity and order; the aligned policy evidence owns the current load and
version needed for selection and stale/conflict decisions. Rechecking the
actor transition algebra and normalized routing laws found no arrival-history
state, repeated cause, false cardinality, nested transition authority,
semantic boolean, or positional syntax exposed to callers. Disposition:
pass.
A12/A13 hash-token membership owner, before edit
Classification: deliberate Bombay hash-routing policy and derived Rust
ownership law. The Router already owns its ordered, duplicate-free recipient
list. Both hash policies need exactly one current Unknown or Observed(version,
token) evidence value at each member position; neither needs a second owned
copy of each recipient. Add appends Unknown, removal deletes the same position,
observation resolves the recipient in the Router snapshot and updates only
that position, and selection considers the observed tokens in membership
order. Unknown, stale, and conflicting observations must return their exact
owned request. The caller syntax is ConsistentHash<K> or
RendezvousHash<K> inside Router<A, Route, _>, with
router.member_token_evidence(&recipient) for current evidence. One policy
type must work with different eligible route types for the same key.
The focused pre-edit caller regression is the external keyed-routing test's
Router<..., RendezvousHash<u64>> and a corresponding ConsistentHash<u64>
type witness; they must fail only because the old policy demands an unrelated
route parameter. Existing rendezvous membership/permutation properties and
the consistent-hash removal test are the independent transition witnesses.
The direct policy boundary must return UnknownRecipient with the complete
observation when its passed member slice has no aligned evidence, without an
index panic. Existing RoutingStrategy hooks, MemberTokenObservation, and
the two concrete selectors are reused; no runtime or effect product changes.
Aggregate-drift precheck: Router retains one ordered member list before and
after. HashMembership changes from a second list of recipient/evidence
products to the aligned evidence list; its Unknown and Observed alternatives
stay two, and their current version/token remain necessary for later
selection and stale/conflict decisions. Router's four messages and three
errors, the hash policies' three rejection alternatives, all actor control
states, transition alternatives, modules, and interpreter paths stay the
same. Expected edit files are routing/router.rs, the keyed routing caller,
this audit, and any focused direct-policy regression. Expected production
delta is negative, with one private product and two public route parameters
removed, no public type added, and one Router evidence lookup added. The
residue scan finds no arrival history, repeated cause, false cardinality,
nested authority, semantic boolean, or structural caller syntax. Cross-checks
are the actor transition algebra and normalized routing law. Disposition:
pass for the proposed model, pending the red witness and implementation.
Post-edit checkpoint: the external keyed-routing caller failed on the old
policy shape with exactly two E0107 diagnostics, one for each unwanted route
parameter. The new caller syntax compiles. The nine focused router unit tests
pass, including complete typed return of an untracked observation from both
hash policies. All nine routing invariant tests pass in debug and optimized
builds; their rendezvous trace includes membership edits, evidence versions,
selection, and token-order permutation. mdbook build docs passes. The full
workspace gate for this batch is pending.
The Router control state and its four commands/three errors are unchanged.
The policy evidence still has two alternatives and the hash rejection still
has three. The production portion of routing/router.rs fell from 863 to 850
physical lines, despite adding the recipient-based Router lookup; the private
HashMember<Route> product and the second recipient list disappeared. Both
public policies lost only their route parameter and direct recipient lookup;
their key parameter remains because two key types and their hash functions
are valid substitutions. No new type, trait, module, effect, actor transition
branch, or interpreter path was introduced. Router retains member identity
and order; the policy retains only current version/token evidence. The shared
private member-index operation serves all three evidence lookups. Rechecking
the actor transition algebra and normalized routing law found no arrival
history, repeated cause, false cardinality, nested transition authority,
semantic boolean, or structural caller syntax. Disposition: pass for this
representation, subject to the pending full gate.
A12/A13 router observation rejection custody, before edit
Classification: derived affine Rust ownership law; neither Agha's transition
effects nor hash-routing policy prescribes a Rust error representation. A
policy consumes one typed observation. If it cannot accept that observation,
it must return the same owned observation with one concrete reason, and
Router must retain exactly that returned value while discarding the mutated
policy candidate. Acceptance commits the candidate and returns empty
continuing Actions. There is no need for an observation to implement
Clone: it can own a move-only payload. The current trait returns only
Self::Error; Router therefore clones the input first, and the built-in
LeastLoadedError<Route> and HashPolicyError<Route> carry a second copy of
it. One semantic cause has two owners and every runnable policy observation
inherits a cloning bound.
The caller syntax is a running Router<MailAddr, Recipient<Destination>, ObservedSelection> accepting the existing non-cloneable
AcceptedPayloads(Vec<u8>), followed by a rejecting move-only observation
whose exact allocation returns through RouterError::Policy. The external
protocol_bounds caller must first fail only because the old Router behavior
requires R::Observation: Clone. A focused pure transition test will then
prove pointer identity, complete rejected ownership, policy-state rollback,
and one successful commit. The public policy seam should return one named
observation/reason product; the aggregate RouterError::Policy remains the
sole actor error owner. Once the observation moves to that product,
LeastLoadedError and HashPolicyError have the same three payload-free
reason alternatives and ownership equation. One shared
MemberEvidenceError should replace both, without a compatibility alias.
RoundRobin remains statically unable to observe. Existing RoutingStrategy
hooks, typed RouterMessage, policy candidate rollback, delivery effects,
and interpreter path are reused. No policy needs a no-op placeholder.
Pre-edit drift checkpoint: Router retains one ordered member list and the
same four message and three actor-error alternatives. Least-loaded evidence
remains Unknown/Observed(version, load); hash evidence remains
Unknown/Observed(version, token). The shared policy reason retains the same
three alternatives without duplicating the observation. The proposed public
surface adds one named rejection product and one shared reason sum, removes
two policy-specific reason types and the observation-clone bound from Router's
transition; no new actor state, effect, interpreter capability, module, or
transition branch is authorized. Expected production delta is negative after
removing redundant payloads, manual Debug implementations, and cloning.
Current membership/evidence and the actual rejected observation are exactly
the values needed for later decisions and ownership return. The residue scan
finds no arrival history, repeated cause, false cardinality, nested
authority, semantic boolean, or structural caller syntax in the proposed
model. Cross-checks are actor-transition-algebra.md, the routing law in
atomic-actor-other-templates.md, and existing wrapper composition tests.
Disposition: pass for the proposed model, pending red caller and pure
transition witnesses.
The external caller now constructs and advances the existing
ObservedSelection with non-cloneable AcceptedPayloads. Before any
production edit, its explicit Behavior witness fails with E0277 naming only
the missing AcceptedPayloads: Clone bound; the subsequent initialize
E0599 is the same unmet Behavior obligation. There is no unrelated route,
send, or address diagnostic. The acceptance action and committed policy state
are asserted in the caller test. Before production code, the rejecting
move-only custody regression failed because the named rejection product did
not exist and the Router still required Observation: Clone. After the
interface and built-in policy changes, all 11 focused router unit tests and
all four external protocol-bound tests pass. The pure rejection test checks
the returned Box allocation address, complete reason, discarded candidate
mutation, subsequent accepted observation, empty observation effects, and
the resulting delivery. LeastLoaded and the two hash policies use the same
rejection product; RoundRobin's uninhabited observation remains unchanged.
Post-design drift checkpoint: Router still has one control state, four
messages, three actor-error alternatives, and the same four transition arms.
Least-loaded and hash evidence each retain Unknown/Observed; their two
three-alternative reason sums became one three-alternative sum. The production
portion of routing/router.rs grew from 850 to 866 physical lines because
each rejection now explicitly returns its owned observation and reason; two
manual Debug implementations and the Router observation clone disappeared.
The two removed public error names were replaced by the shared reason and
named ownership product, so the public type count is unchanged. Modules,
effect lanes, and interpreter capabilities are unchanged. The current
member evidence is the only policy-local value needed for the next selection;
the returned observation is needed only for the exact rejection. The residue
scan finds no arrival history, repeated cause, false cardinality, nested
transition authority, semantic boolean, or structural caller syntax. The
actor transition, routing, and wrapper composition laws were cross-checked.
Disposition: pass for the design stage; testkit migration and the full gate
are separate follow-up work.
Mechanical caller migration at f936dc8 changed only the routing invariant
test's three rejection patterns to inspect the one returned observation and
the shared reason. Its generic route witness no longer asks for an unrelated
observation-cloning bound. All 10 focused routing invariant tests pass in both
Nix-pinned debug and optimized builds. This stage adds no production type,
transition, branch, module, or policy.
The clean-worktree nix flake check -L at signed commit 26d2261 passed all
eight active aarch64-darwin checks, including optimized Nextest with
838/838 passing cases, doctests, Clippy, Rustdoc, documentation links,
formatting, package build, and deny. This closes the gate for the router
observation-custody design and its mechanical test migration; A12/A13 retain
their broader audits.
A13 pre-edit base-projection law
Classification: derived, read-only composition law. BehaviorBase on an
unwrapped catalogue actor returns &Self; it neither constructs nor advances
that actor. Naming this projection must therefore require only the bounds that
make the actor type well formed. Clone and Eq on a command key or payload
belong to construction or transition when those operations actually copy or
compare values. The caller syntax is a BehaviorBase<Base = Self> bound on
Acknowledgements, Resolver, Configuration, and Readiness with opaque
non-Clone, non-Eq domain values. Its observable product is the same shared
reference, with no action or state transition. The focused external
protocol_bounds witness must fail only because the current base-projection
impls carry those operation bounds. Existing Protocol witnesses and the
runtime transition tests are the lower-order contracts; no new effect lane,
interpreter operation, wrapper, or actor state is proposed. Before-edit
control states, subordinate alternatives, transition branches, and public
spellings are unchanged. The current values in each actor remain its exact
records, bindings, versioned configuration, or dependency observations. The
residue scan finds no proposed arrival history, repeated cause, false
cardinality, nested transition authority, semantic boolean, or positional
caller syntax. Cross-check: BehaviorBase in transition.rs, the actor
transition algebra, and the normalized catalogue contracts. Disposition:
pass for the proposed law. The external witness failed before production
edits with ten E0277 diagnostics, all from the four named BehaviorBase
impls demanding Clone or Eq for opaque Key/Value. There was no route,
protocol, or associated-type failure. This is one repeated interface-bound
error, so the edit is limited to removing those operation bounds from the
four read-only impls.
The same derived read-only law applies to Machine, Topic, PubSub,
Presence, Lease, Barrier, Workflow, and OrderGate. Their base method
also returns &Self, while the current impls demand copying, equality, or
ordering used by other operations. The second focused caller stage names
BehaviorBase<Base = Self> for the already opaque payload/key/phase types,
including an OrderGate whose key has no Ord. It must fail before editing
these eight impls solely on their operation bounds. The existing actor
type/route bounds stay in place. No state or action changes; the ownership,
residue, and law cross-check above apply to this extension too.
The initial caller draft reused protocol-only topic aliases whose
Subscription deliberately is not a delivery route; that unrelated error
was corrected before accepting the red result. With lawful concrete routes,
the second stage failed with sixteen E0277 diagnostics, all for the eight
impls' copying, equality, or ordering bounds. No route error remained.
After both stages, the focused external caller passes all three cases and
workspace Nextest passes 827/827. The twelve production files changed by
+0/-16 physical lines (1,517 → 1,501); the external caller gained one
compile witness with twelve concrete substitutions. Aggregate control sums,
subordinate alternatives, transition branches, modules, public spellings,
stored values, effect lanes, and interpreter operations are unchanged before
and after. Every surviving state alternative still owns the same current
value. The residue scan found no arrival history, repeated cause, false
cardinality, nested transition authority, semantic boolean, or positional
caller syntax. The BehaviorBase contract and catalogue laws remain the
cross-check. Disposition: pass for this bound-only batch; A13 remains open
for other public ports and protocol bounds.
The complete nix flake check -L passed all eight declared local checks at
signed commit 790d194, including the optimized workspace Nextest and
release-test lanes. Later routing bound and test-only batches passed their
focused Nix tests; they still need the final flake run together.
A13 pre-edit routing identity law
Classification: derived, typed-protocol identity law. Deduplicator and
OrderGate command types name a key and two concrete delivery routes;
identifying those messages neither compares nor copies the key. The
Deduplicator base projection also only borrows Self. The caller syntax is
an external Protocol<Msg = ...> bound for both actors and a
BehaviorBase<Base = Self> bound for Deduplicator, with an opaque key that
implements no Clone, Eq, or Ord. The complete observable product is the
same associated message type or shared reference, without a transition.
The focused protocol caller must fail on the old impl-only key bounds before
production changes. Existing route and actor type bounds, transition tests,
and the preceding base-projection witness are lower-order contracts. The
candidate changes no event, effect, state, runtime port, wrapper, or public
spelling. Baseline control sums, subordinate alternatives, transition
branches, and modules remain fixed; the deduplicator retains its FIFO key
window and the gate its watermark and held map. The residue scan finds no
proposed history, duplicate cause, false cardinality, nested authority,
semantic boolean, or positional caller syntax. Cross-check: actor transition
algebra and the routing catalogue contract. Disposition: pass for the
pre-edit law. The external caller failed before production with six E0277
diagnostics: Clone/Eq from the deduplicator's two impls and
Clone/Ord from the order gate's protocol impl. No route or message-shape
error occurred.
The focused external caller and the 827-case workspace Nextest suite pass
after removing those three impl-level lines. These two production modules
changed +0/-3 physical lines (752 → 749); test syntax adds two protocol
substitutions and one base-projection substitution. Control states,
subordinate alternatives, transition branches, modules, public spellings,
effect lanes, interpreter operations, and retained values are unchanged.
The deduplicator still owns its FIFO key window; the order gate still owns its
watermark and held map. The residue scan and law cross-check above remain
clear. Disposition: pass for this interface-only batch; A13 remains open.
A13 pre-edit priority-protocol law
Classification: derived, typed-protocol identity law. A priority-queue
command carries an application priority as owned data; only construction and
release ordering require Ord. BinaryHeap<Entry<T, P>> can be stored before
Entry<T, P>: Ord is available; the ordering proof is needed when operations
use the heap. The external syntax is a Protocol<Msg = PriorityQueueMessage<...>> and BehaviorBase<Base = Self> bound with an
opaque priority type. Neither syntax constructs or transitions the queue.
The prior design is expected to fail this caller solely at the struct's
P: Ord bound. The existing priority selection/FIFO trace and construction
tests witness the lower-order operation contract. The candidate removes the
bound from the aggregate declaration and the two read-only impls, retaining
it on construction and Behavior. It adds no new state, effect, policy,
interpreter operation, or public spelling. Baseline control phases remain
Active and Exhausted; the current values remain capacity, next token, and
owned heap entries. Transition branches, subordinate alternatives, modules,
and wrapper products are unchanged. The residue scan finds no proposed
arrival-history state, repeated cause, false cardinality, nested authority,
semantic boolean, or positional caller syntax. Cross-check: the actor
transition algebra and routing priority law. Disposition: pass for the
pre-edit model. The external caller failed before production with exactly two
E0277 diagnostics, both requiring Value: Ord at protocol identity and
base projection. No route, message, or heap-storage error appeared.
The focused caller and all 827 workspace Nextest cases pass after the bound
move. The production module changed +1/-3, net two fewer physical lines
(446 → 444); no transition branch changed. Its control phases remain
Active/Exhausted, the heap still owns the same entries, and all priority
comparison remains on construction and Behavior. Subordinate alternatives,
modules, public spellings, effect lanes, and interpreter operations are
unchanged. The residue scan found no arrival history, repeated cause, false
cardinality, nested authority, semantic boolean, or positional consumer
syntax. The actor transition and routing priority laws were cross-checked.
Disposition: pass for this bound-only stage; A13 remains open.
No-op and compiler-friction checkpoint for the three A13 bound batches:
they remove 21 net production lines of repeated read-only/identity bounds,
with zero new public types, traits, policies, adapters, or wrapper syntax.
An unrelated BehaviorLayer does not require a caller edit, and no public
name describes structural position. The external test file grew 47 net lines
of direct compile witnesses; eight aliases (11 physical lines) name existing
concrete routes and products, so test protocol plumbing did not exceed the
21-line production deletion. No test supplies a no-op policy or discarded
effect to satisfy the new surface. The remaining Router trait-level bound
is a separate design question rather than compiler fallout from these edits.
A13 pre-edit protocol-bound law
The next A13 protocol-identity witness is WorkQueue with a
ReplyRoute<ExactDestination> worker route. ReplyRoute is a sealed, lawful
DeliveryRoute and can carry logical or exact recipients, but deliberately
does not claim PartialEq between them. The derived identity law requires
only the address and the two route protocol/message relationships to name
WorkQueueMessage; worker-route cloning and equality belong to queue state
inspection and availability transitions. The caller syntax is an external
Protocol<Addr = ExactAddr, Msg = WorkQueueMessage<...>> bound, with no
construction or transition. On the prior representation the intended focused
test must fail solely because the aggregate declaration and protocol impl
require ReplyRoute<ExactDestination>: PartialEq. The existing protocol-only
caller suite, Recipient, ReplyRoute, and DeliveryRoute are the lower-order
witnesses. The candidate moves the bound to existing methods/Behavior, adds
no semantic state, effect, public type, or runtime port. Its control states,
subordinate alternatives, branches, modules, and public spellings stay
unchanged; the route remains the same owned field. No history, repeated cause,
false cardinality, nested authority, semantic boolean, or positional syntax
is proposed. Cross-checks: actor-transition-algebra.md, behavior-layer-laws.md,
and the normalized FIFO law. Disposition: pass for the pre-edit model,
pending the red caller witness and measured implementation.
The first draft witness also failed because the ordinary MailAddr lacks an
exact-endpoint family; that failure was unrelated to route equality. The
corrected external caller uses an EndpointAddress and failed with only
E0277: ReplyRoute<ExactDestination> does not implement PartialEq, which
the previous WorkQueue declaration demanded. Moving Clone + PartialEq
from the aggregate declaration, BehaviorBase, and Protocol to the existing
construction/transition impls made that caller compile without changing queue
operation. The measured source change is production +7/-4/net +3, including
the public bound explanation, test +35/-3/net +32, and public API +0/-0
types. The one aggregate module grows from 263 to 266 production lines; the
direct state product remains capacity,
available workers, and waiting jobs, with no control-state enum or subordinate
sum. The five production if selections and three command arms remain eight
transition branches by the same count before and after. Every stored route is
still required for later dispatch or withdrawal. The residue scan found no
new arrival history, repeated cause, false cardinality, nested authority,
semantic boolean, or structural caller syntax. Disposition: pass for this
protocol-bound batch; the rest of A13 remains open. The focused debug and
optimized caller tests, two FIFO unit tests, workspace all-target compile,
all 824 workspace Nextest cases, and the documentation book build passed
through Nix. The complete nix flake check -L passed all ten declared checks
at signed commit 870f4b2, including the optimized 824-case Nextest run,
release tests, doctests, Clippy, package, documentation, formatting,
dependency-audit, and dependency-policy gates.
The same derived protocol-identity law applies to Topic and PubSub:
their message sums contain owned publication, topic, and route values, and
name an address, without requiring those values to be cloned, compared, or
delivered. The intended caller syntax projects Protocol::Msg from each
actor with non-Clone, non-Eq publication and route types. The focused
actors/tests/protocol_bounds.rs compile witness must fail on the prior
impls. Clone, Eq, PartialEq, and DeliveryRoute remain necessary on the
actual transition impls. The lower-order TopicMessage, PubSubMessage,
Protocol, and Behavior contracts already express this distinction; no
new protocol, effect lane, wrapper, or interpreter capability is proposed.
Pre-edit aggregate-drift checkpoint: both actors have one membership state
(Vec<Route> for Topic, Vec<TopicMembership<K, Route>> for PubSub) and
the same control states, subordinate alternatives, transition branches,
production lines, modules, and public spellings before and after this proposed
bound change. The future-needed values are each ordered subscriber route,
and for PubSub each retained topic identity. No arrival-history state,
repeated cause, false cardinality, nested authority, semantic boolean, or
positional consumer syntax is proposed. The discovery contracts and the
Protocol/Behavior distinction in actor-transition-algebra.md are
cross-checked. Disposition: pass for the proposed narrower protocol law,
pending its pre-edit regression and implementation.
The pre-edit caller produced ten E0277 diagnostics for transition-only
DeliveryRoute, Clone, Eq, and PartialEq bounds. After narrowing only the
two Protocol impls, the caller passes; the Behavior impls retain their
transition bounds. Aggregate control states remain one for each actor;
subordinate state and result alternatives and transition branches remain
unchanged. Production lines in these two files changed from 323 and 184 to
317 and 179; modules and public spellings remain unchanged. The ordered
subscriber routes and retained topic identities remain the future-needed
state, with no residue from the pre-edit scan. The discovery and actor
transition law cross-check remains valid. Disposition: pass for this focused
bound repair; A13 remains open for the rest of the public surface.
The same identity equation also applies to Machine<A,S,M,P,E>: its protocol
is (A, M), independent of phase copying and comparison. A caller must be
able to project Protocol::Msg = M while P lacks Copy and PartialEq;
execution still requires those bounds. The focused caller is added to
actors/tests/protocol_bounds.rs before production edit. Existing Machine,
Protocol, and Behavior are the only required layers, with no interpreter
effect or wrapper change. Pre-edit drift checkpoint: the machine's state,
held queue, phase, transition function, one control state, subordinate
Advance alternatives, branch count, modules, and public spellings are
unchanged by this bound proposal. Its future-needed values are the current
phase, owned state and held messages. No arrival history, repeated cause,
false cardinality, nested authority, semantic boolean, or positional syntax
is proposed. The machine and actor transition law documents were
cross-checked. Disposition: pass for the proposed bound repair, pending
the failing caller and implementation.
The pre-edit machine caller failed with E0277 for Phase: Copy + PartialEq.
After narrowing Machine's Protocol impl, it passes while its constructor,
phase access, and Behavior impl retain the execution bounds. Production
lines in machine.rs changed from 198 to 194; all state alternatives,
transition branches, modules, and public spellings remain unchanged. The
future-needed values and residue scan are unchanged from the pre-edit
checkpoint. Disposition: pass for this protocol-only repair.
The next A13 protocol-bound batch applies that already-proven identity law to
Configuration, Health, Readiness, and Registry. Their command types
contain owned configuration or key values; naming those commands does not
clone or compare them. The route and destination bounds remain on each actor
struct because those types currently define its retained delivery capability;
the proposed edit removes only C: Clone + Eq or K: Clone + Eq from its
Protocol impl. A caller projects each exact command sum with a non-Clone,
non-Eq value and a lawful concrete result route. The compile regression in
actors/tests/protocol_bounds.rs precedes the four source edits. Existing
Protocol, command sums, result protocols, DeliveryRoute, and Behavior
are reused. No interpreter, wrapper, effect lane, or public spelling changes.
Pre-edit aggregate-drift checkpoint: configuration has one current
ConfigurationState; health retains ordered component states; readiness
retains ordered fixed dependencies; registry retains ordered key-recipient
bindings. Their control states, subordinate alternatives, transition
branches, modules, and public spellings do not change. The future-needed
values remain respectively the current version/value, component evidence,
dependency evidence, and key-recipient binding. There is no proposed arrival
history, repeated cause, false cardinality, nested authority, semantic
boolean, or positional syntax. The operations/discovery contracts and
actor-transition-algebra.md were cross-checked. Expected source change:
four Protocol impls, roughly four bound lines deleted; no new public type.
Disposition: pass for the proposed identity-law migration, pending its
failing caller and implementation.
The pre-edit caller failed with eight E0277 diagnostics, two for each actor's
unneeded Clone + Eq requirement. It passes after removing only those four
Protocol bound lines. Execution, construction, and retained route bounds
remain unchanged. The four aggregates retain all prior current values,
alternatives, transition branches, and modules; production source is four
lines smaller and public spellings are unchanged. The pre-edit residue scan
and law cross-check still hold. Disposition: pass for this identity-law
migration. A13 remains open for bounds on other protocols and the remaining
public-surface review.
The same derived identity law reaches Correlator, Acknowledgements, and
Barrier: their command sums can carry non-Clone, non-Eq correlation keys,
payloads, and barrier members while a lawful result route remains named.
Clone/Eq are execution bounds; their structs and message sums do not
require them. The caller projects the three message sums in
actors/tests/protocol_bounds.rs before production edits, using the existing
Recipient<MessageProtocol<...>> route. The proposed edit only removes
transition bounds from three Protocol impls, reusing their existing result
protocols, routes, and Behavior impls. No wrapper or interpreter path changes.
Pre-edit aggregate-drift checkpoint: correlator retains its ordered key
lifecycle states; acknowledgements retains ordered records; barrier retains
member order and its current generation/state. Their control states,
subordinate alternatives, transition branches, modules, and public spellings
are unchanged by this proposal. The future-needed values remain the keys,
reply recipients, pending acknowledgement payloads, members, and generation.
The residue scan finds no proposed arrival-history state, repeated cause,
false cardinality, nested authority, semantic boolean, or positional syntax.
The routing, workflow, and actor transition contracts were cross-checked.
Expected source delta is four deleted bound lines, no new public types.
Disposition: pass for the proposed identity-law migration, pending its
failing caller and implementation.
The pre-edit caller failed with eight E0277 diagnostics for Key and Value.
After removing four bound lines from the three Protocol impls, that caller
passes; the execution impls retain their bounds. Current states, subordinate
alternatives, branches, modules, and public spellings are unchanged. The
retained values, residue scan, and routing/workflow law cross-check remain as
recorded above. Disposition: pass for the focused identity-law migration.
Presence, Lease, and Workflow expose one further instance of the same
derived law. Each public struct requires K: Clone + Eq merely to name its
type, although its private fields can store a non-Clone, non-Eq K and its
message sum can own one. The constructor and Behavior impl genuinely need
those bounds for their policies; the protocol identity does not. The intended
caller projects Protocol::Msg for each actor with a non-Clone, non-Eq
key and a lawful result route. The compile witness is added to
actors/tests/protocol_bounds.rs before touching production. The change
removes the bound at each struct declaration and its Protocol impl only;
constructor and execution bounds remain, and no new trait or port is needed.
Pre-edit aggregate-drift checkpoint: presence retains its ordered records and
timer mapping, lease retains one vacant/held/exhausted state and timer ID, and
workflow retains its validated definition and current run state. Their
control states, subordinate alternatives, branches, modules, and public
spellings do not change. The future-needed values are respectively the
participant records, current lease holder/generation, and workflow steps and
result route. No arrival-history state, repeated cause, false cardinality,
nested authority, semantic boolean, or positional syntax is proposed. The
discovery, time, workflow, and actor transition contracts were cross-checked.
Expected production delta is six bounds removed across three files, three
physical source lines deleted, and no new public types. Disposition: pass
for the proposed bound relocation, pending the focused failing caller and
implementation.
The pre-edit caller failed with six E0277 diagnostics at the three struct
declarations. With their K parameters unbounded and the same bounds removed
from their Protocol impls, it passes. Constructors and transitions retain
their Clone + Eq requirements; the focused presence, lease, and workflow
transition tests pass. The three production files contain three fewer physical
lines, with unchanged states, alternatives, branches, modules, and public
spellings. The future-needed values, residue scan, and cross-checked laws
remain as recorded above. Disposition: pass for this bound relocation.
Router has a distinct protocol-only bound: its strategy's observation must
be Clone to execute the current rollback transition, but the public
RouterMessage can own an observation without cloning it. An application
strategy with a non-Clone observation must still be able to name the router
protocol. A focused compile caller in actors/tests/protocol_bounds.rs
implements a real membership-selection and observation-update policy, then
projects Protocol::Msg; it precedes the proposed one-line Protocol bound
removal. RoutingStrategy, RouterMessage, Recipient, and Behavior are
the existing pieces. No runtime, wrapper, or effect lane changes.
Pre-edit aggregate-drift checkpoint: router retains its ordered recipients
and one concrete strategy. Its control state, subordinate alternatives,
transition branches, modules, and public spellings do not change. Future
decisions need that recipient order and the strategy's current observation
state. No arrival-history state, repeated cause, false cardinality, nested
authority, semantic boolean, or positional syntax is proposed. The routing
and actor transition contracts were cross-checked. Expected production delta
is one deleted bound line, no new public type. Disposition: pass for the
proposed narrower identity law, pending the failing caller and implementation.
The pre-edit caller failed with E0277 for its owned, non-Clone observation.
After deleting R::Observation: Clone from only the router's Protocol impl,
it passes; the transition impl retains the rollback bound. The existing router
transition tests pass. One production line was removed; state, alternatives,
branches, modules, public spellings, future-needed values, and the residue
scan are unchanged. Disposition: pass for this identity-law repair.
Classification: derived Rust protocol identity. A cache or resolver recipient
names an address and a command type without running a transition or copying a
binding definition. Thus Protocol for Cache<K,V> and Resolver<K> does
not require K: Clone + Eq or V: Clone; those laws are needed by the
respective transition or borrowed construction operations. The caller syntax
in actors/tests/protocol_bounds.rs names each actor protocol with non-Clone,
non-Eq payload types. It is the focused compile witness and must fail on the
prior bounds. The existing MessageProtocol, Recipient, CacheMessage, and
ResolverMessage products are reused. No new trait, alias, constructor,
runtime port, wrapper obligation, or effect lane is proposed.
Aggregate-drift checkpoint: cache and resolver state, protocol sums, results,
transition branches, production modules, and public spellings stay unchanged.
The future-needed cache values are its ordered entries, capacity, and exact
keys/values; the resolver needs its immutable key-recipient bindings. No
alternative is added or deleted. The residue scan finds no proposed arrival
history, duplicated cause, false cardinality, nested authority, semantic
boolean, or positional syntax. The persistence and discovery contracts and
the Protocol/Behavior separation in actor-transition-algebra.md are
cross-checked. Disposition: pass for the narrower protocol law, pending the
pre-edit regression and implementation.
The caller failed on the prior implementation with five E0277 diagnostics
requiring Clone/Eq on protocol-only payloads. The same caller passes after
removing those bounds from the two Protocol implementations; the bounds
remain on actual transition and borrowed-construction operations. No type,
module, aggregate control state, subordinate alternative, transition branch,
or public spelling changed. The residue scan and law cross-check remain as
recorded before the edit. Disposition: pass for these two bound repairs.
The wider hidden-export and extension-port inventory in A13 remains open.
Compile-cost comparison for those two bounds, on aarch64-darwin with the
Nix-provided Rust 1.95 toolchain: two detached checkouts at 1559a87 differed
only by restoring K: Clone + Eq, V: Clone to Cache's Protocol impl and
K: Clone + Eq to Resolver's Protocol impl. Both used the same warm
CARGO_TARGET_DIR, CARGO_BUILD_JOBS=2, CARGO_INCREMENTAL=0, and
cargo check -p bombay-behavior-actors --lib --locked. Before each measured
run, cargo clean -p bombay-behavior-actors removed only the actor package's
artifacts. Both runs visibly checked that crate: restored bounds took 187.07
seconds wall time; narrower bounds took 187.84 seconds. The 0.77-second
difference is under 1% and does not establish a compile-time improvement.
The supported benefit is the narrower protocol law and the removal of five
misleading E0277 caller diagnostics. This pair does not measure every generic
bound or the full workspace build.
The public FifoError and KeyedError sums are application-visible aggregate
failure contracts, and FixedBuilder is the inferred application
construction value. The trusted core initialize and delegate_transition
ports and the RoutedCreation child-host value are required by wrapper or
interpreter authors. Their existing rustdoc describes the relevant trust and
custody rules; the #[doc(hidden)] markers were removed so readers can find
those contracts. This changes documentation visibility only. Other hidden
generated obligations and runtime ports still need the full ownership
classification before A13 can close.
The follow-up inventory found a second hiding site for KeyedError and
FixedBuilder: their atomic re-exports still shared #[doc(hidden)] groups
with interpreter products. They now belong to the visible re-export groups.
The Nix-pinned Rustdoc build succeeded, and
target/doc/behavior_actors/atomic/index.html contains direct public links to
both items. The public-surface inventory
classifies the remaining annotation sites. Trait implementor ownership and a
repeatable compile-cost comparison remain open before A13 can close.
A13 FIFO aggregate error export, before implementation
Classification: deliberate Bombay public error policy, not an actor-model law.
FifoError is the FIFO aggregate's complete transition-failure sum, with
distinct InitializationUnavailable and WorkerCreationsExhausted
alternatives. An application that owns FIFO initialization must be able to
match those alternatives without naming the private fifo_pool module, just
as it can match the sibling KeyedError. The intended caller syntax is
behavior_actors::atomic::FifoError in an exhaustive match. The external
actors/tests/protocol_bounds.rs fixture will first require that path and
must fail before the re-export; then it will pass after the smallest
atomic visibility edit. The runtime-facing Behavior::Error remains the
same type and no failure or ownership transition changes.
Aggregate-drift checkpoint: FIFO control states remain Constructed,
Operating, Draining, Stopped, and ForcedRetirement. Subordinate states,
result alternatives, transition branches, and module count are unchanged by
the proposed re-export. The retained current values are the prepared workers,
operating members/backlog/cursor, draining workers/deadline, and terminal
forced-retirement members/cause in their existing variants. A12 still owns the
question of whether every terminal field is needed. Public
spellings increase by one, with zero public types added or removed. The edit
adds no arrival history, repeated cause, false cardinality, nested transition
authority, semantic boolean, or structural user syntax. It is cross-checked
with actor-transition-algebra.md, the FIFO law, and the existing aggregate
error vocabulary. Expected files: atomic/mod.rs, the external compile
fixture, and this audit; expected production line delta is zero or one.
The external caller failed before the edit with E0432 for the inaccessible
behavior_actors::atomic::FifoError path and passed after the one-spelling
re-export. The production diff is +1/-1 line; no type, state, alternative,
transition branch, or module changed. The existing two alternatives and their
error displays are unchanged. The Nix-pinned Rustdoc build passed and the
public atomic index links FifoError alongside KeyedError and
FixedBuilder. The residue scan and law cross-check remain as
recorded above. Disposition: pass for this export repair; A13's wider trait
ownership and compile-cost review remains open.
A13 authored-role and logical-product documentation, before edit
Classification: Rust caller contract and derived typed-composition policy.
A manually authored direct-child role lawfully implements ChildRole and
ChildOccurrence with DeclaredChildOccurrence; an application host can
constrain the exact logical-host product with BirthProtocolProduct. Both
are existing external caller paths in established_capabilities.rs and
logical_host_requirements.rs. Their public proof names and the required
ChildOccurrence::Resolution associated type must be discoverable in
Rustdoc. Before editing, a Nix-pinned cargo doc -p bombay-behavior --no-deps
build succeeded but the crate index omitted DeclaredChildOccurrence and
BirthProtocolProduct, and the declared-occurrence page was absent. The
private Recipient::new has an ineffective #[doc(hidden)] marker to remove.
The intended edit changes documentation display only; it adds no alias,
default, bound, constructor, implementor, actor transition, or runtime port.
Aggregate-drift checkpoint: all actor control states, subordinate alternatives,
transition branches, production modules, and public spellings are identical
before and after. The future-needed role values remain its declared parent,
child behavior, and structural position; the logical product retains its
ordered protocol occurrences. Four ineffective or misleading documentation
markers are removed, with no arrival history, repeated cause, false
cardinality, nested authority, semantic boolean, or structural user syntax.
The actor-transition, creation, and logical-host laws are cross-checked.
Disposition: pass for the model; Rustdoc visibility and external caller
witnesses still need post-edit verification.
The four markers were removed as modeled. The core annotation count fell
from 22 to 18; no visibility modifier, trait bound, type, implementation,
aggregate state, branch, module, or public spelling changed. The Nix-pinned
Rustdoc index now links both DeclaredChildOccurrence and
BirthProtocolProduct, and both documentation pages exist. The
established_capabilities and logical_host_requirements external caller
suites passed 20 tests, including manual nominal occurrence and generic
logical-host use. The residue scan and law cross-check remain as recorded
above. Disposition: pass for the documentation repair. A13's wider trait
implementor and compile-cost review remains open.
A15 coding-rule progress
Timer-domain tests now call the mutating accept operation before assertions,
so optimized and debug test profiles execute the same transition. Generated
readiness cases use the closed ReadinessStatus sum rather than a semantic
boolean. The pool-private values formerly named only Operating are now
FifoOperating and KeyedOperating; each remains under its existing root
state variant with no state or branch change. The nested .sends.inner.inner
uses in testkit/tests/compositions.rs are structural composition tests that
explicitly assert nesting order. All 92 rustdoc import lines across 26 source
files now use qualified paths or were removed where the negative example
tested only a missing alias. scripts/check_rustdoc_imports.py enforces that
rule in the Nix documentation gate; a hidden-import counterexample fails the
check. Core delivery compile-fail examples now require only Protocol, the
capability they actually test, rather than an inert Behavior fixture.
Direct rustc --error-format=json probes of the three logical-delivery
examples produced only E0308; replacing the wrong protocol, address, or
payload with the declared one compiled in each case. Rustdoc's
compile_fail,E0308 tag alone does not enforce that error code, so this
counterfactual check supplies the failure-reason evidence. A direct compiler
check now validates the expected diagnostic for all 45 tagged compile-fail
snippets in the Nix documentation gate; it found and corrected five stale
tags. The optimized timer-domain tests passed both cases, and
nix flake check passed all ten checks on aarch64-darwin, including the
documentation and doctest gates. The post-check edits in this audit batch
only add the A11 inventory and this verification result; its Rustdoc examples,
scripts, and Nix gate definition are the checked snapshot. Disposition: pass.
A15 timer admission outcome, before implementation
The earlier A15 pass covered the listed fixtures, but a repository-wide scan
found a remaining semantic boolean: TimerLease::accept and
OneShotSchedule::accept mutate the schedule and return bool. Their callers
use that value to choose whether to run a timer reaction. Reopen A15 for this
specific policy violation; the prior evidence remains valid for its scope.
Classification: deliberate Bombay timer policy, not an actor-model guarantee.
An event matching the currently armed timer is admitted once and consumes that
schedule. A foreign, stale, duplicate, cancelled, or exhausted event is
ignored without changing schedule state. The complete local result is
TimerAdmission::{Accepted, Ignored}; the wrapper still emits the same
Actions and invokes the same reaction only on Accepted. The caller syntax
matches that sum instead of using a mutating boolean in a guard. The focused
domain regression checks exact, duplicate, cancelled, foreign-ID, and
foreign-generation arrivals, including the surviving schedule. Existing
timer-wrapper tests and both wrapper orders remain lower-order witnesses.
Pre-edit aggregate-drift checkpoint: TimerLease retains its four states
(NeverIssued, Armed, Idle, Exhausted), and OneShotSchedule retains its
two states (Unscheduled, Scheduled). The new return sum represents one
transition's disposition; it stores no future state. The future-needed values
remain the armed generation and the one-shot ID, generation, and deadline.
No actor aggregate state, public spelling, or effect lane changes. The edit is
expected to touch the domain and four wrappers, replacing two boolean returns
and four guard uses with one private sum and exhaustive matches. Record exact
line, branch, and module deltas after the edit. The residue scan found no
arrival history, repeated cause, false cardinality, nested transition
authority, or positional user syntax. Cross-check the explicit effect law in
docs/actor-transition-algebra.md and the timer wrapper Rustdoc.
Disposition: pass for the pre-edit model.
The pre-edit Nix-toolchain compile witness failed with seven E0433
diagnostics because TimerAdmission did not exist. After implementation,
the two mutating domain operations return the private Accepted | Ignored sum,
and Deadline, OneShot, Periodic, and ReceiveTimeout match it exhaustively.
The original ID guard and all action lanes remain in place. Domain tests
passed 3/3 in debug and optimized builds; all nine actor time unit tests
passed in both profiles. The actor timer-settlement integration target passed
4/4, including both wrapper orders, and the independent receive-timeout model
target passed 6/6. Full repository gates remain pending for this batch.
Post-edit aggregate-drift checkpoint: TimerLease remains four states and
OneShotSchedule two; no stored subordinate alternative or future-needed value
changed. The former boolean result is the new two-case local admission sum.
Across the four wrappers, top-level event arms changed from 12 to 11 because
Deadline now matches its owned arrival once; eight explicit admission arms
replace four boolean guard decisions. The five affected modules remain five,
and no public spelling changed. Measured production diff is +56/-34 (net +22)
lines; the focused tests are +22/-3 (net +19). This is a domain-model repair,
not a code-reduction claim. No arrival history, repeated cause, false
cardinality, nested transition authority, semantic boolean, or structural
caller syntax remains in this timer admission path. The actor effect law and
all four wrapper Rustdoc contracts were cross-checked. Disposition: pass
for this retained batch, pending the full Nix gate before A15 closes.
The same scan found InactivityModel::notification in the independent
testkit: it consumes a live token, returns a semantic bool, and three caller
assertions perform that mutation inside assert!. This is a second A15
checkpoint. The testkit policy is that one matching notification consumes and
returns its exact current token; a stale or duplicate notification returns
absence and leaves the live token unchanged. Option<u64> is the direct domain
representation of one accepted token or absence, with no new protocol type.
The caller must bind the outcome before asserting, so debug and optimized
builds execute the same transition. A focused testkit caller expecting
Some(1) versus None is the pre-edit compile regression. The lower-order
witnesses are the existing receive-timeout model trace, the actor timer-domain
test, and both timer wrapper orders. This changes no production actor state,
interpreter path, or effect lane. The model keeps its two optional tokens;
its future-needed value is still the current live token. Expected files are
the model and its one caller test. Record post-edit measurements and gates
before closing A15.
The standalone Nix-pinned rustc caller included the actual testkit model
module and required notification to return Option<u64>. Before the edit it
failed with the intended E0308 (bool versus Option<u64>); afterward it
compiled. The 6-test independent receive-timeout trace passed in debug and
optimized builds. It now binds each admission before asserting, checks that a
stale notification leaves token 1 live, and checks that an accepted one returns
that same token. The model still uses two optional tokens and the same two
transition branches; it adds no public type, state, module, or effect lane.
Measured testkit model diff is +4/-5 (net -1) lines and its caller test is
+7/-3 (net +4). The retained current values remain the last issued token and
the one live token. The residue scan found no arrival-history alternative,
repeated cause, false cardinality, nested authority, semantic boolean, or
positional syntax in this path. Cross-check against the receive-timeout
Rustdoc and the actor transition effect law passed. Disposition: pass for
this batch; A15 awaits the full Nix gate on the signed revision.
A whole-crate assertion scan found additional test and fuzz assertions that
called state-changing methods inside the macro: actor transition, model
initialize/activity, collection insert/remove, mailbox receive,
sequence issue/reserve, and iterator next. The test-execution law is that
setup and transitions run before observation; assertions inspect retained
results and complete actions only. This is test discipline, not a new actor
transition. The calls were moved to named locals in their original order,
including four observational iterator calls, across 34 Rust files (+304/-258
lines, net +46). All changes are in test or fuzz code, including inline test
modules; no production type, state, effect lane, or branch changed. A repeated
whole-crate scan for these methods inside assertion macros found zero sites.
The Nix-pinned workspace Nextest run after this batch passed 819/819 tests,
including the macro dependency-resolution fixtures and the affected actor,
core, and testkit suites. The changed fuzz targets and clean-snapshot flake
gate remain to be checked.
The next A15 scan found five generated bool selectors in the independent
testkit models. Each selected a domain event or exact/foreign source; the
tuple generators also supplied values unused by some event alternatives.
This is a test-model policy issue, not a new actor law. Before editing, the
static scan rg 'any::<bool>\(\)' crates --glob '*.rs' found exactly these
five sites. The expected regression is that the same scan finds none after
the edit, while the three affected model targets still compare every generated
turn with their independent oracle in debug and optimized builds. The direct
test data model is a separate closed sum for buffer, priority, rate, lease,
and observation operations, with each variant owning only the data it uses.
No production state, effect product, interpreter path, or public spelling is
changed. The five-site static regression now finds no any::<bool>() under
crates/. The three edited test binaries passed 7/7 cases in both debug and
release mode with the Nix-pinned toolchain. Their measured test-only diff is
+219/-137 (net +82) lines; production is +0/-0, with no public API change.
Production control states, subordinate alternatives, transition branches, and
modules are unchanged. The independent models retain their prior observable
decisions. Five boolean event/source selectors and two numeric operation tags
became seven closed input sums with 18 alternatives. Each alternative owns its
current event data, such as an offered value, acquired cost, exact report
target, or elapsed timer source. The three test modules remain three. The
residue scan found no retained arrival history, repeated cause, false
cardinality, nested transition authority, semantic selector boolean, or
unused field on these generated operations. The actor transition and relevant
routing, timing, and observation laws were cross-checked. Disposition:
pass for this test-model batch; the complete A15 gate remains open.
A wider assertion scan then found mutating helper calls hidden behind local
names such as query, put, acquire, release, hold, and offer, plus
interpreter methods called inside assertions. The earlier method-name scan did
not catch these. Hoist those transitions and ownership transfers before
assertions, run the affected tests, and repeat the broader scan before A15
can close.
The follow-up batch hoisted local actor transitions, interpreter calls,
initialization, creation decomposition, source admission, and consuming
iteration before their assertions. It also replaced 91 assertion-side
unattempted().into_inputs().len()/is_empty(),
into_requests().len()/is_empty(), and
into_deliveries().is_empty() chains with existing borrowed len,
is_empty, or as_slice().is_empty observations. The direct forms inspect
the same ordered product without transferring custody merely to count it.
Across 25 Rust files the test-only diff is +206/-663 (net -457) lines;
production is +0/-0 and public API, actor control states, effect lanes,
transition branches, and module count are unchanged. All source-file edits
are inside #[cfg(test)]; the remaining files are tests or fuzz targets.
The surviving current values are the original actor state and exact owned
effect products; no arrival history, repeated cause, false cardinality,
nested authority, semantic boolean, or positional consumer path was added.
The actor transition and source-custody laws were cross-checked. The broader
assertion scan now finds no selected mutating or consuming method inside an
assertion; its ten remaining &mut matches compare a returned source with a
temporary source and do not invoke a transition. Workspace test compilation
and formatting passed. Local Nextest discovery and a direct actor test binary
then stalled in macOS _dyld_start before the Rust harness ran, so that run
is not test evidence. scripts/check_assertion_effects.py now enforces the
identified consuming-method and mutating-helper patterns in the Nix document
gate; five checker regression cases cover detection and allowed observation.
The local checker and its tests passed. A clean-snapshot Nix gate and changed
fuzz-target build remain required before this batch or A15 can close.
The clean detached snapshot at signed commit 9c9973e passed
nix flake check --max-jobs 1 --no-write-lock-file, including package,
Nextest, Clippy, Rustdoc, doctests, formatting, deny, and the document gate
with both assertion-checker scripts in the flake source set. The Nix-provided
fuzz runner then built all manifest targets after the assertion edits with
nix run .#fuzz -- build; its separate lockfile was synchronized to the
workspace crate versions. These runs complete the pending A15 verification.
The later A20 priority-property edit has its own focused baseline and
counterfactual evidence and does not change the A15 assertion discipline.
Disposition: pass; A15 is closed.
A07 actor mutation evidence: routing-buffer capacity
At revision 0274dab, a focused campaign mutated the bounded buffer's
below-capacity guard (routing/buffer.rs:261). The law is Bombay's declared
overflow policy: an offer below capacity is retained, while an offer at
capacity follows the configured rejection or eviction branch and preserves
ownership of every value. The actor unit tests and the independent FIFO and
overflow property in behavior-testkit/tests/routing_invariants.rs are the
relevant witnesses.
The command selected bombay-behavior-actors, filtered to the guard and its
comparison operators, and set --test-workspace true --test-tool nextest --no-shuffle --minimum-test-timeout 180 -- --profile mutants with
PROPTEST_CASES=32. The mutation log confirmed test_packages=All for each
mutant. The unmutated baseline passed; all five selected mutants built and
were caught by actor buffer tests, primarily
every_overflow_policy_preserves_or_returns_all_owned_values. This is a
five-mutant routing-law result, not an actor-wide mutation verdict. A07 still
needs independently reviewable campaigns for the other actor families and a
separate viability ratchet.
A07 stable-proxy activation admission experiment, before simplification
Classification: derived exact-correlation law. BeginActivation consumes one
ActivationPermit and issues an ActivationAttempt containing that permit's
WorkerAttempt. Every WorkerActivation constructor in
atomic/worker/activation.rs carries that same worker evidence alongside the
attempt; its fields are private, and the permit cannot be duplicated. Thus a
matching activation attempt already proves the matching worker for every
constructible input. The public transition still accepts only its exact
activation and returns a foreign input intact through the diagnostic lane.
The caller syntax is a normal StableProxy::on(request.started()), including
two independently constructed requests with equal worker payload and endpoint
values but different non-forgeable attempt tokens.
At signed revision 855458a, the Nix-pinned campaign first selected the only
mutant in stable_proxy/state.rs; it was unviable because ProxyPhase has no
Default, so it supplied no mutation verdict. Its unmutated baseline ran 590
actor tests. The separate activation-guard campaign selected all five
mutations at stable_proxy/mod.rs:314 and ran 702 actor/testkit tests per
mutant; it skipped a second baseline, while the full 812-test workspace run
had passed at this revision. Four were caught; changing && to || survived.
The survivor falsified the claim that both comparisons are independently
necessary; existing tests already reject foreign activation. The focused
caller regression distinguished exact
attempt tokens despite equal payload and endpoint values. It passed before
and after the edit; the complete 51-test proxy recovery suite and optimized
focused witness also passed after it. The private guard now retains only the
exact activation comparison and deletes the redundant worker input. No public
type, effect lane, interpreter operation, wrapper, or actor-model law changed.
The existing activation and shutdown models remain the lower-order transition
witnesses.
Aggregate-drift checkpoint before the edit: stable proxy's control states are
Dormant, Starting, Ready, EmptyInitial, EmptyAfter, Replacing,
ShuttingDown, and Stopped; they remain the same. No subordinate state or
result alternative, production module, or public spelling changes. The only
transition branch affected is the exact activation admission predicate. Every
surviving state keeps the same current worker, attempt, activation plan, and
pending custody. The proposal stores no arrival history or repeated cause,
assumes no cardinality, adds no nested transition authority or semantic
boolean, and exposes no positional syntax. It is cross-checked with the
stable-proxy and atomic actor laws and the complete-effect rule in
actor-transition-algebra.md. Expected and actual files: this audit, one proxy
caller test, and stable_proxy/mod.rs. The production diff is +7/-11 lines,
net -4; the test adds 47 lines. Public types added/removed: 0/0. Control states,
subordinate alternatives, transition branch count, modules, and public
spellings remain unchanged. The exact retained current values and residue
scan remain as recorded before the edit. At signed revision 7163ec3, the
Nix-pinned post-edit campaign ran a 591-test actor baseline and selected the
remaining viable equality mutation. The mutated build ran 703 actor/testkit
tests; six actor tests failed, so the mutant was caught. The unselected
function-wide replacement proposes Ok(Default::default()), which cannot
construct the returned affine activation. This is a stable-proxy
activation-law verdict, not an actor-wide result. The full Nix flake gate
passed from a clean worktree at 7163ec3, including workspace Nextest,
Clippy, docs, doctests, formatting, dependency policy, and packaging.
Disposition: pass for the retained representation and focused mutation
slice.
A07 actor mutation evidence: FIFO completion correlation
Classification: Bombay's creator-local correlation and assignment-custody
policy. A worker completion has exact assignment authority, while its outer
ChildReport separately names the child creation. The pool accepts a
completion only when both identify the current busy worker. A foreign child
report must not release the assignment or send a completed customer outcome.
At signed revision 0a40bdc, a Nix-pinned campaign selected the three
guard mutations at fifo_pool/mod.rs:276: unconditional true, unconditional
false, and inverted child-ID equality. The baseline ran 591 actor tests; the
full Nix gate had passed on the same production code at 7163ec3. Every
mutated command selected the 813-test workspace suite. All three mutants
built and were caught. fifo_pool::delivery_and_completion_orders_complete_once
caught unconditional acceptance; the FIFO correlation suite caught rejection
and inversion. No production edit was needed. This is one FIFO correlation-law
slice, not a full FIFO mutation campaign or an actor-wide verdict.
A07 actor mutation evidence: keyed binding expectations
Classification: Bombay's keyed-binding compare-and-set policy. A binding
command carries the expectation it observed; a stale generation returns the
complete command and current expectation without changing placement. A
rebalance to the already bound role returns the existing binding. At signed
revision 0a40bdc, a separate Nix-pinned campaign inverted the two guards
at keyed_pool/mod.rs:1069 and :1146. Its baseline ran 591 actor tests;
mutated commands selected the 813-test workspace suite. Both mutants built
and were caught by the keyed construction-and-commands integration test,
which checks generations, owned rejection, and an independent directory
placement. No production edit was needed. This is a keyed binding-law slice;
other keyed and actor-family laws still require mutation review.
A07 actor mutation evidence: dynamic cancellation authority
Classification: Bombay's keyed-operation correlation and affine authority
policy. Cancellation of a live service requires the exact current operation;
the actor returns a stale authority unchanged and retains the active entry.
At signed revision f9f39dc, a Nix-pinned campaign inverted the operation
comparison at dynamic_supervisor/mod.rs:885. Its baseline passed 591 actor
tests; the mutated build selected the 814-test workspace suite. The mutant
built and was caught by four dynamic integration tests, including
accepted_start_cancellation_retires_before_fresh_key_reuse. A separate
isolated counterfactual changed != to >: current authority still passed
the guard, but an older authority for the same reused key was accepted. The
focused integration test failed exactly at its stale-after-reuse assertion
(dynamic.rs:3309). The counterfactual was reverted. No branch production
edit was needed. This is one dynamic-supervision law slice, not a full
family campaign. The full Nix flake gate passed all ten checks on f9f39dc
before the counterfactual; the latter ran in a separate detached worktree.
A07 actor mutation evidence: fixed replacement correlation
Classification: Bombay's exact predecessor-correlation and returned-custody
policy. A fixed supervisor accepts a replacement outcome only while that
member awaits an outcome for the same predecessor worker. A foreign outcome
returns unchanged with the pending member; a matching outcome remains held
until the independent predecessor-stop leg resolves. At signed revision
ebcfa9d, a Nix-pinned campaign selected three mutations at
fixed_supervisor/recovery/mod.rs:1876: unconditional guard acceptance,
unconditional refusal, and inverted predecessor equality. Its baseline
passed 591 actor tests; each mutated build selected the 814-test workspace
suite. All three built and were caught. The foreign-outcome integration test
caught unconditional acceptance; valid replacement and coordinated-restart
tests caught refusal and inversion. No production edit was needed. This is
one fixed-supervision replacement-law slice, not a full family campaign.
A07 child creation resolution regression, before test edit
Classification: Bombay's exact staged-creation correlation policy. A
ChildShutdownPlan may mark a declared child established only when both the
reported creation ID and creation kind equal the values stored in that child's
Awaiting state. A report with exactly one mismatched component returns the
complete report, leaves the child awaiting, and permits the later exact report.
The existing test used a replacement report with both components mismatched,
so replacing the || admission rejection with && passed every actor test;
the workspace mutant was mislabeled caught only when unrelated macro fixture
tests timed out. A focused test will present wrong-kind/same-ID and
right-kind/wrong-ID reports independently before the exact birth. No
production type, bound, wrapper, interpreter port, transition branch, or
public spelling changes.
Aggregate-drift checkpoint: Planning remains Collecting or Reported;
each child remains NotRequested, Awaiting { creation, kind }, or
Established { creation }. The future-needed values are the declared
position, exact staged ID and kind while awaiting, and committed ID for plan
construction. Production states, subordinate alternatives, branches, lines,
modules, and public spellings are unchanged before and after this test-only
experiment. The residue scan finds no arrival-history state, repeated cause,
false cardinality, nested authority, semantic boolean, or structural user
syntax. This is cross-checked with actor-transition-algebra.md and
atomic-runtime-settlement.md. Disposition: pass for the regression model;
the focused counterfactual and baseline still need verification.
A05 nested test-timeout verdict, before implementation
Classification: deliberate Bombay verification policy. A CaughtMutant
summary is not proof that a law assertion failed when the selected test runner
itself timed out a test and returned failure. The verdict must reject any
selected mutant whose Nextest log contains a timed-out test, even if another
test failed, and must require inspectable log evidence for every claimed
caught mutant. A focused gate fixture will submit a complete, otherwise valid
campaign with CaughtMutant and a Nextest TIMEOUT line; the prior gate
accepts it. The implementation will reuse cargo-mutants' log_path and
existing Outcome/candidate identity, and the Nix mutation profile will let
the outer cargo-mutants timeout classify genuinely stalled commands. No actor
algebra, aggregate state, transition, interpreter effect, public Rust
spelling, or wrapper changes.
Aggregate-drift checkpoint: actor control states, subordinate alternatives,
branches, production lines, modules, and public spellings are unchanged; the
gate adds only report validation and removes the runner's premature timeout.
No arrival history, repeated cause, false cardinality, nested authority,
semantic boolean, or structural user syntax enters actor code. The gate law
is cross-checked with the A05 report-identity law above and the Nix mutation
derivation. Disposition: pass for the pre-edit model; the failing fixture
and retained verdict still need verification.
The adversarial gate fixture failed on the prior implementation: check
returned Ok(()) for a complete CaughtMutant report whose only Nextest
evidence was TIMEOUT. The repaired gate requires a safe relative log path
and a FAIL test line for each caught mutant, rejects any TIMEOUT line even
alongside a failure, and applies the same validation when seeding a baseline.
The mutation Nextest profile no longer terminates individual tests after ten
seconds; cargo-mutants owns the command timeout and reports Timeout to the
strict verdict. All 12 gate tests, targeted Clippy, and formatting passed.
The actual three-mutant child-creation campaign passed the repaired verdict
(3 viable / 3 total) with three test failures and zero timeouts. The gate's
production portion grew from 252 to 294 lines; its test portion added 62 net
lines. The actor file added 46 test lines and no production lines; the Nextest
profile shrank from six to four lines. Aggregate states, alternatives,
branches, modules, and public spellings remain unchanged. The residue scan
and law cross-check remain as recorded above. Disposition: pass for this
verification repair. The full Nix flake gate passed all ten
aarch64-darwin checks on signed revision 81bda41.
A07 actor mutation evidence: catalogue correlation and admission
At signed revision 337bc2e, one Nix-pinned campaign selected ten mutations
across registry unbinding, child creation settlement, configuration version
conflict, one-shot timer admission, and barrier duplicate arrival. The baseline
passed 591 actor tests; mutated commands selected the 814-test workspace.
Every mutant built and cargo-mutants labeled every one caught, but one label
was false evidence: the child-creation || to && mutant passed all actor
tests, while four unrelated macro fixture tests hit the ten-second Nextest
timeout. The other nine mutants had named actor test failures. The focused
child-creation regression on 81bda41 passed unmutated and failed under
|| to && at the wrong-kind/same-ID assertion. A separate Nix-pinned,
actor-only rerun selected all three current creation-correlation mutations;
its 593-test baseline passed, all three mutants built and failed actor
assertions, and the strengthened gate accepted its complete report with no
timeout, miss, or unviable candidate. This closes the false-positive slice;
other catalogue laws still need review, so A07 remains open.
| Family and law | Original mutation result with a real actor oracle |
|---|---|
| Registry: exact recipient required to unbind | One inversion caught by discovery::registry::tests::mutations_are_atomic_and_stale_unbind_is_typed. |
| Child shutdown: exact creation ID and kind | Two comparison inversions caught by child-shutdown tests; the initially false ` |
| Configuration: same-version equality | One inversion caught by operations::configuration::tests::stale_and_conflicting_candidates_return_ownership_atomically. |
| One-shot timer: ID and generation admission | Guard-true, guard-false, && to ` |
| Barrier: duplicate participant arrival | One inversion caught by workflow::barrier::tests::generation_releases_exact_membership_in_arrival_order. |
A07 actor mutation evidence: state and ownership
Classification: derived state-transition and returned-custody laws. A machine
replays held messages when Goto changes phase, a stash releases its held
FIFO when its route admits delivery, and a cache evicts the oldest entry only
when an absent key enters a full cache. Replacing an existing key returns its
old value without evicting another entry. At signed revision c3a54b3, a
Nix-pinned actor-only campaign selected four mutations: machine phase equality,
stash drain removal, cache && to ||, and cache capacity equality. Its
baseline passed 593 actor tests. All four mutants built and failed named
actor tests, with zero misses, timeouts, or unviable candidates. Machine and
stash failures came from algebra; both cache mutations failed its recency
and replacement-custody tests. The repaired mutation verdict accepted the
complete report (4 viable / 4 total). No production edit was needed. These
are three law slices; the actor-wide 3,125-candidate inventory is not claimed
as fully tested.
A07 actor mutation evidence: lease renewal authority
Classification: derived generation-correlation law. A held lease may renew only
when both the holder and observed generation match its current state. A stale
generation or different holder returns a typed rejection without scheduling a
new expiry. The independent model in timing_invariants compares the outcome,
scheduled generation, and state after each generated operation.
At signed revision a334b0b, a Nix-toolchain campaign selected all eight
mutations of Lease::successor with cargo mutants --package bombay-behavior-actors --test-package bombay-behavior-actors --test-package bombay-behavior-testkit --test-tool nextest --no-shuffle --minimum-test-timeout 180 -f crates/actors/src/time/lease.rs -F successor.
The unmutated actor baseline passed 593 tests; a separate unmutated testkit
run passed 112 tests, including the independent lease model. Mutated commands
selected both packages (705 tests). Seven candidates compiled and failed
named tests; the eighth was unviable because TimerGeneration has no
Default. There were no survivors or timeouts. The strict mutation gate
accepted the complete report with a viability floor of seven
(7 viable / 8 total), keeping compiler rejection distinct from test
detection. The false stale-generation guard was caught by the independent
model; the other six viable changes were caught by lease unit tests. This is
one lease law slice, not an actor-wide verdict.
A07 actor mutation evidence: termination observation and propagation
Classification: derived exact-correlation and terminal-custody laws. A termination monitor consumes only the selected peer or observation report in its lawful phase. A propagation target accepts one matching terminal report; foreign or later reports return their complete value without changing state.
At signed revision 812fe86, the Nix-toolchain monitor campaign selected all
ten TerminationObservationTarget::react candidates in
lifecycle/termination_monitor.rs. Its actor baseline passed 593 tests, and
mutated commands selected actor and testkit suites (705 tests). Eight viable
mutations failed named actor or independent-model tests; two whole-function
replacements could not compile because Actions has no Default. No survivor
or timeout occurred, and the strict gate accepted 8 viable / 10 total.
Both lifecycle campaigns used cargo mutants --package bombay-behavior-actors --test-package bombay-behavior-actors --test-package bombay-behavior-testkit --test-tool nextest --no-shuffle --minimum-test-timeout 180; reproduce the monitor selection with
-f crates/actors/src/lifecycle/termination_monitor.rs -F '::react'.
The matching propagation campaign selected nine correlation mutations in
lifecycle/termination_propagation.rs. Eight were caught, but replacing
PeerTermination::matches with unconditional acceptance survived: the peer
test exercised only a matching report. The strict gate correctly rejected
that original report. Signed commit f0dce34 added a foreign-peer report
that must return intact while observation remains active, followed by an
accepted selected-peer report; it also made the independent child-sequence
model inspect initialization and every successful effect lane. Both focused
tests passed. A separate one-mutant rerun failed the new peer test, with no
timeout, and the strict gate accepted its complete 1 viable / 1 total
report. The original eight results and the focused rerun are separate
revision-specific evidence, not a claim that all actor mutations were run.
The propagation selection is reproducible with
-f crates/actors/src/lifecycle/termination_propagation.rs -F '::matches|match guard self.state|replace && with'.
A07 actor mutation evidence: work admission and availability
Classification: deliberate Bombay FIFO and bounded-admission policy. A submission consumes the oldest available worker or joins the bounded waiting queue; at capacity its complete value is returned. An availability notice dispatches the oldest waiting value or joins the unique worker queue.
At signed revision 69adab4, the Nix-toolchain campaign selected four
WorkQueue::submit and WorkQueue::announce candidates in
routing/work_queue.rs. The actor baseline passed, and mutated commands ran
the actor and testkit suites. Both whole-function default replacements were
unviable because the complete Actions product has no Default. The two
viable admission and duplicate-availability guard changes compiled and failed
named work-queue unit tests. The strict gate accepted 2 viable / 4 total,
with no survivor or timeout. Reproduce with cargo mutants --package bombay-behavior-actors --test-package bombay-behavior-actors --test-package bombay-behavior-testkit --test-tool nextest --no-shuffle --minimum-test-timeout 180 -f crates/actors/src/routing/work_queue.rs -F '::submit|::announce'. The independent two-FIFO property was subsequently
strengthened to inspect all action lanes with non-Clone work values and unique
reply destinations; this newer oracle passed, but was not part of the earlier
mutant verdict.
A07 actor mutation evidence: keyed publication membership
Classification: deliberate Bombay keyed-membership and ordered-publication policy. A topic is retained after its last subscriber leaves; a repeated subscription is idempotent. Unsubscription of an unknown topic or absent recipient returns the complete command without changing membership.
At signed revision d7d0832, a Nix-toolchain campaign selected all six
PubSub::subscribe and PubSub::unsubscribe candidates in
discovery/pub_sub.rs. The actor baseline passed, every candidate compiled,
and named pub-sub unit tests failed under each mutation. The strict gate
accepted 6 viable / 6 total, with no survivor or timeout. Reproduce with
cargo mutants --package bombay-behavior-actors --test-package bombay-behavior-actors --test-package bombay-behavior-testkit --test-tool nextest --no-shuffle --minimum-test-timeout 180 -f crates/actors/src/discovery/pub_sub.rs -F 'PubSub<A, K, P, Route>::subscribe|PubSub<A, K, P, Route>::unsubscribe'.
A later independent sequence model checks topic order, exact membership,
every successful action lane, and rejected publication custody with distinct
owned strings. It passed after the campaign and is not credited with the
earlier mutant kills. Publication-loop and downstream delivery laws remain
outside this mutation slice. Aggregate-drift checkpoint for the test-only
batch: control states 1 → 1 (active), named subordinate state sums 0 → 0,
aggregate error variants 3 → 3, message arms 3 → 3, production lines
234 → 234, modules 1 → 1, and public spellings unchanged. The current topic key,
retained membership list, and introduction order remain the only future-needed
values. The test's map and order list model the observable order; no production
arrival history, repeated cause, false cardinality, nested authority,
semantic boolean, or structural user syntax was added. Cross-checks were
actor-transition-algebra.md, atomic-runtime-settlement.md, and the PubSub
row in engineering/atomic-actor-other-templates.md. Disposition: pass for
this test-only evidence batch; capacity, retirement, and delivery settlement
remain independent future laws in that normalized catalogue record.
A second Nix-toolchain campaign at signed revision 888e78a selected the
five PubSub::transition candidates. Four whole-function replacements were
unviable because they attempted invalid BehaviorActed construction; the
topic-equality inversion compiled and failed the existing publication unit
test. The strict gate accepted 1 viable / 5 total, with no survivor or
timeout. Since that runner stopped after the unit failure, a separate
isolated-worktree counterfactual changed only publication's topic comparison
from == to != and ran the new independent property alone. It failed and
shrunk to two commands: subscribe to topic 0, then publish to topic 0; the
actor incorrectly returned NoSubscribers. The temporary edit and worktree
were removed. This proves the independent oracle's topic-selection
sensitivity, not the publication clone-loop or transport settlement law.
A07 actor mutation evidence: health observation versions
Classification: deliberate Bombay component-correlation and version-commit policy. A health observation updates only its selected component. Older evidence returns a stale error with its owned evidence; equal-version conflicting evidence returns a conflict; equal-version identical evidence is idempotent. A removal tombstone cannot be undone by an older observation.
The Nix-toolchain campaign selected all seven candidates in
Health<A, K, Route>::commit at revision ab0d65f. The actor baseline and
mutated actor and testkit suites passed or failed as expected. Every candidate
compiled, then a named health test failed; the relevant tests were
stale_and_conflicting_evidence_preserve_committed_state and
tombstone_rejects_resurrection_and_report_aggregates_worst_status. The strict
gate accepted 7 viable / 7 total, with no survivor or timeout. Reproduce
the selection with cargo mutants --package bombay-behavior-actors --test-package bombay-behavior-actors --test-package bombay-behavior-testkit --test-tool nextest --no-shuffle --minimum-test-timeout 180 -f crates/actors/src/operations/health.rs -F 'Health<A, K, Route>::commit'. This is one operation-law slice, not an
actor-wide mutation verdict.
A20 ledger entry: stable-proxy activation correlation
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition and custody | proxy_command_recovery::equal_worker_values_and_endpoints_do_not_share_activation_authority checks every effect lane, unchanged phase, exact diagnostic return, admission by the original owner, and admission of the target's own activation. |
| Independent trace and composition | stable_proxy_shutdown_model explores activation, return, stop, and shutdown order; stable_proxy_owner_composition checks owner projection. These models do not themselves forge inconsistent worker and activation evidence. |
| Invalid construction | ActivationPermit is affine and has a compile-fail duplication example; WorkerActivation has private fields and constructors that couple worker and activation evidence. The application cannot synthesize the inconsistent pair required by the surviving pre-edit && to ` |
| Counterfactual | Four of five pre-edit guard mutants were caught. The && to ` |
A20 ledger entry: FIFO completion child correlation
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition | fifo_pool::delivery_and_completion_orders_complete_once places completion before and after the assignment receipt, then wraps an authorized completion with a foreign child ID. It checks no customer completion, one diagnostic, and later return of the still-assigned job during shutdown. |
| Independent trace | fifo_pool::correlation::completed_work_advances_and_wraps_worker_selection checks completed work and worker selection across roles. The FIFO model and fuzz targets cover broader ordering; this mutation slice does not certify them. |
| Composition and invalid construction | The completion authority is issued only from an assignment, but a ChildReport can carry a different creator-local child ID. The pool must check both. This is an aggregate ingress decision, not a wrapper-order transformation. |
| Ownership limit | The public FifoDiagnostic is intentionally opaque. The test checks diagnostic cardinality and that shutdown returns the still-assigned job, but it cannot inspect the exact foreign completion inside the terminal diagnostic. That custody seam remains for A17/A20. |
| Counterfactual | All three guard mutants at fifo_pool/mod.rs:276 built and were caught; viable selection and detection are recorded separately. |
A20 ledger entry: keyed binding expectations
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition | keyed_pool::compile::keyed_construction_and_commands_need_only_domain_types exercises absent, exact, stale, same-role, cross-role, and removed bindings with distinct request IDs and generation values. |
| Independent trace | The test compares placement to a separate account directory; keyed pool assignment and lifecycle tests and keyed fuzz targets cover other transitions, not every binding counterfactual. |
| Composition and invalid construction | BindingExpectation is an exhaustive absent-or-exact sum; stale rejection returns the complete command and current generation. This is an aggregate binding decision, not a wrapper-order transformation. |
| Counterfactual | Both selected guard inversions in keyed_pool/mod.rs built and were caught by the binding integration test. The remaining keyed aggregate laws still need mutation slices. |
A20 ledger entry: dynamic cancellation authority
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition and custody | dynamic::accepted_start_cancellation_retires_before_fresh_key_reuse checks current cancellation, returned authority and submission, exact retirement before key reuse, and the old authority's stale reply after reuse. It checks all send lanes and the continuing verdict for replayed and stale cancellation. |
| Independent trace | Dynamic cancellation and shutdown fuzz targets explore orderings. The focused integration trace has concrete expected replies but is not an independent state model. |
| Composition and invalid construction | CancelAuthority<Key> carries the key and operation; the actor compares it with the entry's current operation. This is a keyed aggregate decision, not a wrapper-order law. The authority is returned by start or replacement receipt and retained in cancellation replies. |
| Counterfactual | Inverting != to == built and failed four dynamic integration tests. Replacing != with > in an isolated worktree left current-token cancellation intact but made the old token after same-key reuse pass; the focused test failed at the stale assertion. These checks do not certify all dynamic entry phases. |
A20 ledger entry: fixed replacement correlation
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition and custody | fixed_supervisor_initialization::replacement_rejects_an_outcome_for_another_predecessor checks the complete foreign outcome returned through the diagnostic path. Valid receipt and coordinated-restart tests check a matching outcome waits for the independent predecessor stop. |
| Independent trace | fixed_supervisor_initialization explores 90 lawful replacement arrival orders across recovery strategies. This is a local typed interpreter trace, not the downstream host required by A17. |
| Composition and invalid construction | The outcome and pending replacement each carry exact predecessor evidence; the guard compares those values after the input settlement advanced to outcome-pending. A foreign predecessor can be reported by a runtime, so this is an aggregate ingress check rather than a static invalid-construction case. |
| Counterfactual | Guard-true, guard-false, and equality-inversion mutants at recovery/mod.rs:1876 all built and were caught by fixed-supervision integration tests. This slice does not cover scheduling, policy selection, or terminal retirement. |
A20 ledger entries: catalogue admission laws
| Law | Focused transition and broader witness | Boundary and counterfactual limit |
|---|---|---|
| Registry stale unbind | The registry unit test returns the stale recipient and keeps its binding; discovery model tests cover broader bind/lookup sequences. | Recipient equality is a runtime command condition, not a compile-denial capability. One comparison inversion was caught; snapshot ordering is outside this slice. |
| Child creation ID and kind | creation_resolution_requires_matching_id_and_kind_independently returns each single-component mismatch with exact expected values and then accepts the lawful birth. The surrounding shutdown plan exercises nested event paths. | Runtime reports can carry mismatched typed facts. The ` |
| Configuration version conflict | stale_and_conflicting_candidates_return_ownership_atomically checks stale version and same-version value conflict; catalogue models cover update sequences. | This is a runtime version decision. The same-version comparison inversion was caught; no downstream persistence claim is made. |
| One-shot timer admission | The one-shot unit trace checks initialization scheduling, matching generation, and exact-once firing; timing composition tests check wrapper use. | A timer ID or generation mismatch is a typed runtime input. Four guard mutants built and failed the focused test; broader periodic and deadline laws are separate. |
| Barrier duplicate arrival | The barrier unit trace checks exact membership and arrival-order release; workflow invariant tests cover reusable generations. | A repeat arrival is an ordinary command rejection. One equality inversion was caught; it does not certify the future/stale-generation branches. |
A20 pre-edit acknowledgement model law
Classification: deliberate Bombay participant-correlation policy. Begin
normalizes declaration order; each declared participant can acknowledge once;
the final acknowledgement completes; cancellation succeeds only while pending.
Unknown, duplicate, unexpected, completed, and cancelled operations each
return the exact rejected command or participant through the one reply lane,
without changing the retained lifecycle. Terminal records remain distinct.
The existing property checks begin/acknowledge in two separate batches and
never emits Cancel, so it cannot prove the cancellation or interleaving law.
The test-only replacement will generate mixed operations and varied reply
recipients, compare every ordered reply and complete outcome, the full record
order and current participant state, empty creations, and continuing verdict
after every step. Its independent pending model retains original declaration
and accepted participants, then derives remaining participants; terminal
states discard those lists. It does not perform the actor's in-place removal.
The existing concrete actor and Actions products remain the lower-order
contracts. No production symbol,
state, effect, runtime port, wrapper, or public spelling changes. Pre-edit
control phases Pending/Completed/Cancelled, subordinate error/outcome
alternatives, transition branches, production lines/modules, and public
spellings stay fixed. Every retained model value is needed for a future
acceptance, duplicate, or exact-order decision. No arrival-history residue is
added to production; the model's accepted order is observable and therefore
lawful. No repeated cause, false cardinality, nested authority, semantic
boolean, or positional caller syntax is proposed. Cross-check: actor
transition algebra and the routing correlation catalogue. Disposition:
pass for the test-only model before implementation.
The focused correlation suite passed all three cases in the Nix toolchain.
The previous no-op reply-actor macro was deleted; both model routes use the
existing concrete MessageProtocol. The new generated trace mixes Begin,
Acknowledge, and Cancel, and a deterministic trace reaches unknown,
unexpected, duplicate, completed, and cancelled replies. Every turn checks
the exact reply recipient/outcome, all current records and order, empty
creations, and Continue. In a disposable Behavior worktree, a one-line
counterfactual changed only a repeat-cancellation reply from Cancelled to
Completed. Both tests failed for that exact outcome; the generated trace
shrunk to Begin, Cancel, Cancel on one key. The mutation and worktree were
removed. This proves the new oracle detects wrong terminal rejection
classification, not every acknowledgement defect. Test source changed
+298/-119/net +179 physical lines; production +0/-0, public API
+0/-0, control states, subordinate alternatives, transition branches, and
modules are unchanged. The surviving model alternatives own exactly their
future-needed current values; no arrival-history, repeated-cause, false
cardinality, nested-authority, semantic-boolean, or positional-syntax residue
remains. The actor transition and routing correlation contracts were
cross-checked. Disposition: pass for this test-only evidence batch; A20
remains open. The pure trace does not prove host delivery admission or a
wrapper-order interaction, and its isolated counterfactual covers one
terminal branch only.
A20 pre-edit correlation reply custody
Classification: deliberate Bombay keyed-correlation policy. Begin retains
one exact reply recipient while pending. A matching Resolve or Cancel emits
one terminal result to that recipient, then removes its authority; a later
reply is rejected with complete key/value ownership. The current generated
property uses the same reply route for every Begin and checks only the first
send's payload on successful terminal transitions. It cannot detect a wrong
destination, duplicate send, creation, or stop verdict. The test-only
candidate varies reply recipients, retains their address in the independent
model only while pending, and compares all successful action lanes and the
exact retained state after every operation. Existing CorrelatorError cases
already check the owned rejected values. No production state, transition,
effect, interpreter operation, wrapper, public spelling, module, or line
changes are proposed. Control phases Pending/Completed/Cancelled and every
subordinate alternative remain fixed; the pending reply address is the exact
future-needed current value. The residue scan finds no proposed arrival
history, repeated cause, false cardinality, nested authority, semantic
boolean, or positional consumer syntax. Cross-check: actor transition
algebra and routing correlation law. Disposition: pass for the test-only
model before implementation.
The focused three-case correlation suite passes after the property varies
reply addresses for each Begin. Successful Begin asserts empty sends and
creations plus Continue; matching Resolve and Cancel each assert exactly one
send to the retained recipient, the complete result, empty creations, and
Continue. The record comparison now checks the retained pending recipient
alongside its key and phase. Every existing rejection still checks its exact
returned key, value, or submitted reply route. Test source changed
+26/-10/net +16 physical lines (393 → 409); production +0/-0, public
types, control states, subordinate alternatives, transition branches, and
modules are unchanged. The pending recipient is discarded from the model
upon terminal settlement, matching the current-value law. No history,
repeated cause, false cardinality, nested authority, semantic boolean, or
positional consumer syntax was added. The actor transition and correlation
laws were cross-checked. Disposition: pass for this test-only batch; a
dedicated route mutation and real interpreter delivery remain outside it.
A20 ledger entries: machine, stash, and cache
| Law | Focused transition and broader witness | Boundary and counterfactual limit |
|---|---|---|
| Machine phase replay | algebra::fsm_is_receive_plus_become_policy observes phase change and held-message replay; fsm_properties separately exercises self-Goto, generated sequences, and rollback. | Inverting phase equality built and failed the changed-phase algebra test. The self-Goto oracle is separate; this mutation slice does not test every error rollback branch. |
| Stash FIFO release | algebra::stash_release_delivers_the_trigger_then_drains_the_held_fifo checks trigger-first delivery and held FIFO order; stash_properties covers generated hold/release sequences. | Replacing drain_into with a no-op built and failed the algebra test. The statically infallible inner behavior is the applicable compile-time constraint; runtime route choices remain typed. |
| Cache replacement and eviction | Cache unit tests check recency, full-capacity eviction, and complete replacement/removal custody; catalogue_invariants compares generated operations with an independent ordered-entry model. | Zero capacity is rejected at construction. Both capacity-condition mutations built and failed cache tests; this slice does not certify other persistence mechanisms. |
A20 ledger entry: bounded-buffer capacity and ownership
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition | routing/buffer.rs tests zero capacity, FIFO release and empty outcomes, and every overflow policy with distinct values. |
| Independent trace | behavior-testkit/tests/routing_invariants.rs::buffer_preserves_fifo_and_returns_every_unaccepted_value compares retained FIFO values and returned custody after each generated command; it does not claim transport admission. |
| Composition | The buffer is a standalone Behavior whose destination lanes are concrete DeliveryRoutes. There is no wrapper-order law for its capacity decision; route typing is checked separately in composition/delivery_route.rs. |
| Invalid construction and boundaries | BufferConfiguration::new(0, ..) returns ZeroCapacity; a runtime numeric capacity is validated at construction. The full-queue tests distinguish Reject, DropNewest, and DropOldest ownership. |
| Counterfactual | The five guard/operator mutants in the A07 slice were all caught by actor buffer tests. This evidence covers this capacity branch only. |
A20 ledger entry: lease holder and generation correlation
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition | time::lease::tests::acquire_renew_release_and_stale_elapsed_are_generation_safe and wrong_holder_and_matching_expiry_are_distinct check renewal, wrong-holder rejection, stale expiry, release, and continued state. |
| Independent trace | timing_invariants::lease_matches_exclusive_generation_ownership_after_every_event compares each generated outcome, scheduled generation, and held/vacant state against its own model; it caught the false stale-generation guard. |
| Composition | recursive_reply_protocols and exact-reply template tests prove typed reply routes; this mutation slice does not establish a separate wrapper-order law for Lease. |
| Invalid construction and boundaries | Holder and generation are concrete typed inputs. generation_exhaustion_is_terminal_and_never_wraps checks the upper sequence boundary; the model's generated sequence stays below it. |
| Counterfactual | Seven viable successor mutations were caught; one replacement could not compile because TimerGeneration has no Default. Other lease transition branches remain outside this campaign. |
A20 ledger entry: termination observation and propagation
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition and custody | Logical-monitor unit tests check matching reaction actions, duplicate rejection, exact foreign-report return, and continued user delegation. Established-monitor integration tests check requested, observing, cancelled, and observed phases with exact observation IDs; the independent model also covers rejection. The propagation peer regression checks foreign return before selected-peer publication and stop. |
| Independent trace | exact_termination_model compares generated exact-report phases and reaction count but does not inspect every action lane. terminal_outcome_sequences independently predicts selected-child acceptance, foreign return, discharge, publication, and later rejection; since f0dce34 it also checks initialization and every effect lane on successful steps. It does not model PeerTermination. |
| Composition and invalid construction | The exact monitor composes inside StopOnShutdown, and the logical monitor appears in the universal-layer tests. No two-order wrapper proof is claimed for propagation. A child target requires a typed creation ID and protocol occurrence; a foreign report is a runtime input returned through the typed error. |
| Counterfactual | Eight viable monitor mutations were caught and two were unviable. The propagation campaign exposed an unconditional-peer-acceptance survivor; its isolated post-regression rerun was caught by the new focused test. The two campaign reports retain their separate baselines and verdicts. |
A20 ledger entry: work admission and availability
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition and custody | routing::work_queue unit tests cover FIFO worker selection, queued dispatch, and zero-capacity rejection. The newer independent property also checks every emitted assignment and outcome, recipient, empty creation lane, and continuing verdict. |
| Independent trace | routing_invariants::work_queue_matches_two_coupled_fifo_capabilities tracks waiting work and available workers in separate deques, with unique reply recipients and a non-Clone work payload. It checks the complete observable state after every generated operation. It does not claim transport admission. |
| Composition and boundaries | Exact reply-route template tests cover logical and established customer routes. The property generates capacities including zero and repeated worker notices and withdrawals; it does not check downstream worker execution. |
| Counterfactual | Two viable guard mutants failed actor unit tests; two whole-function replacements were unviable. The strengthened independent property passed after this campaign, so the original verdict is not attributed to it. |
A20 ledger entry: keyed publication membership
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition and custody | discovery::pub_sub unit tests check first subscription order, duplicate suppression, and publication rejection for known empty and unknown topics. The independent property checks the exact returned topic, recipient, and original publication allocation. |
| Independent trace | catalogue_invariants::pub_sub_preserves_topic_membership_and_rejected_publications tracks membership in a map plus introduction order, then compares every current topic, recipient order, successful action lane, and rejection after generated commands. Each publication has a distinct owned string. It does not interpret transport admission. |
| Composition and boundaries | Exact reply template tests exercise established publication routes. The property explores absent topics, empty retained topics, duplicate recipients, and re-subscription; it does not prove scheduling or downstream delivery. Topic/member capacity and topic retirement still need laws as recorded in atomic-actor-other-templates.md. |
| Counterfactual | All six selected membership mutants compiled and failed named unit tests. The separate topic-selection campaign caught one viable equality inversion and had four unviable replacements. An isolated rerun of that inversion against only the independent property failed with a two-command counterexample. Publication clone-loop and transport settlement evidence remain open. |
A20 ledger entry: health observation versions
| Evidence layer | Current witness and limit |
|---|---|
| Focused transition and custody | The two operations::health tests check component selection, stale and equal-version conflicts, idempotence, tombstone retention, and report aggregation. Error variants retain the rejected component and evidence. |
| Independent trace | behavior-testkit/tests/catalogue_invariants.rs::health_tombstones_and_versions_match_an_independent_map compares generated health operations and complete rejected evidence with an independent ordered component map. It does not interpret host delivery admission. |
| Composition and boundaries | The health actor returns its report through a typed route. Version equality and ordering are explicit in the pure transition; the focused test includes a removed component and a later older observation. No wrapper-order claim is made for this standalone actor. |
| Counterfactual | All seven selected Health::commit mutations compiled and failed named health tests. The strict gate accepted 7 viable / 7 total; other health methods remain outside this slice. |
A20 ledger entries: ordered routing and workflow
The next A20 test-only experiment targets the circuit breaker's deliberate
single-flight and reset policy. A model built from the documented public
contract owns a free slot with consecutive failures, one accepted attempt,
a cooling generation, or a single trial. It predicts every reply recipient
and outcome, timer request, next phase, empty creation lane, and continuation
after each generated command. Successful admission must retain the same
attempt until its matching completion; unrelated completions return the exact
command; stale timer evidence changes nothing. The existing focused breaker
tests and typed TimerElapsed input are lower-order witnesses. No production
type, state, transition, or interpreter port is proposed. The pre-edit
aggregate control sum is Closed/Idle, Closed/Awaiting, Open,
Probing/Available, Probing/Awaiting, and Exhausted; the test adds no alternative
to it. The model's phase is an independent expected-value oracle, not another
production transition authority. The source has one aggregate module and its
existing branches remain unchanged. A mutation of the reset-generation guard
must fail this oracle before the test is retained. Cross-checks are the actor
transition algebra and normalized routing catalogue law. Disposition:
pass for the test design, pending the counterfactual and measurements.
The retained circuit_breaker_model compares generated command sequences
with an independently named single-flight/cooling/trial oracle, plus a
deterministic open/reopen trace. Each successful step compares ordered reply
recipients and complete outcomes, exact reset requests, phase and owned
attempt, empty creation lane, and continuing verdict; each invalid completion
checks its exact returned command. The generated instructions include current
and foreign completions, matching/stale timer generations, and a foreign
timer ID. Attempt-number and generation exhaustion remain in the focused
unit tests, since generated traces cannot approach u64::MAX. This is a pure
transition proof; it does not establish timer scheduling or delivery by a
real runtime. Both tests passed in debug and optimized Nix builds.
The isolated one-line counterfactual inverted the timer-generation equality
guard. Both tests failed; the generated oracle shrank to Admit, Fail, Admit, Fail, ElapsedCurrent, where the actual phase remained open instead of
offering one trial. The temporary mutation and worktree were removed. This
proves the model detects incorrect matching-reset admission, not every
possible circuit-breaker defect. The test-only batch changes production
+0/-0/net 0, tests +419/-0/net +419, public API +0/-0 types,
production lines 404 → 404, production modules 1 → 1, and production
transition branches unchanged. The control sum remains Closed/Idle,
Closed/Awaiting, Open, Probing/Available, Probing/Awaiting, Exhausted; its
owned values and future decisions remain those stated above. The residue
scan found no production arrival history, repeated cause, false cardinality,
nested authority, semantic boolean, or structural caller syntax. The
transition algebra and routing catalogue record were cross-checked.
Disposition: pass for this test-only evidence batch; A20 remains open. The
complete nix flake check -L passed all ten declared checks at signed commit
ad4d198, including optimized Nextest with 826 passing cases, release tests,
doctests, Clippy, package, documentation, formatting, dependency-audit, and
dependency-policy gates.
| Law | Focused transition and independent trace | Remaining proof boundary |
|---|---|---|
| Sequencer gap release | routing::sequencer tests missing, stale, duplicate, and maximum positions. catalogue_models::sequencer_matches_an_independent_gap_map_after_every_offer compares deliveries, outcomes, state, and empty creation lane after each generated offer. | Generated positions stay below exhaustion. The maximum-position unit test covers that separate boundary; no sequencer mutation slice is recorded. |
| Deduplicator retention | routing::deduplicator tests duplicate custody, eviction, and zero capacity. catalogue_models::deduplicator_matches_an_independent_fifo_window_after_every_delivery compares both send lanes and retained keys, and checks that the exact boxed value allocation travels through admission or rejection. | The model varies positive capacities; zero capacity is a constructor rejection in the focused test. Transport admission and a dedicated mutation slice remain unproved. |
| Order-gate watermark | catalogue_models::order_gate_matches_an_independent_watermark_map_after_every_operation compares ordered releases, duplicate and stale-open outcomes, watermark, held count, and empty creation lane after each generated operation. | The generated trace does not prove a runtime delivery receipt or a dedicated guard mutation slice. |
| Priority selection | routing::priority_queue tests stable priority/FIFO ties and full/empty outcomes. routing_invariants::priority_queue_matches_stable_max_priority_selection compares an independent ordered list after each offer or release, including exact delivery and reply recipients, reply depth, empty creation lane, and continuing verdict. An isolated Released.remaining = queued.len() + 1 counterfactual compiled and failed at the new remaining-depth assertion on a one-offer, one-release trace. | The generated priorities cover 0–7 and positive capacities below eight; exhaustion and real host admission are outside this property. The recorded counterfactual covers the release-depth branch, not every routing branch. |
| Rate admission | routing::rate_limiter tests accepted/rejected ownership and saturating refill. routing_invariants::rate_limiter_matches_saturating_token_arithmetic compares capacity, available tokens, rejection reasons and returned value, and admitted delivery after each generated operation. | Its generated path constructs positive token costs and capacities; no host admission or dedicated mutation slice is recorded. |
| Round-robin membership cursor | routing::router tests cursor repair after removal. routing_invariants::round_robin_keeps_the_same_next_recipient_across_membership_edits tracks an independent member list and next recipient through generated edits and routes. | The pure trace does not prove transport admission. |
| Consistent-hash membership stability | routing::router tests one three-member removal over 128 keys. routing_invariants::consistent_hash_membership_edits_preserve_unaffected_key_owners checks complete actions, Unknown admission, varying tokens and keys, aligned evidence, addition, and removal in 128 generated cases. An isolated no-removal mutant failed at the exact surviving member token, shrinking to tokens [0,1,2,3]. | The relational property does not independently calculate every ring point or prove host delivery admission; the focused actor test covers same-version conflict but stale token evidence still needs a direct witness. |
| Least-loaded evidence | routing::router tests unknown, stale, conflicting, tied, and newly lower evidence. routing_invariants::least_loaded_matches_versioned_membership_and_selection models member order and latest evidence after mixed add, remove, observe, and route operations; a deterministic trace covers re-addition. | An isolated max-load selection counterfactual failed both new tests; pure routing still does not prove host delivery admission or the other policy families. |
| Rendezvous member stability | routing::router tests one keyed route, conflicting token, and exact stale-version return. routing_invariants::rendezvous_membership_edits_only_move_keys_to_or_from_the_changed_member checks exact route actions, token evidence, membership edits, and order-independent ownership for 128 generated distinct-token cases. An isolated index-dependent score counterfactual failed at the permutation assertion with a minimal [0,1,2,3] token set. | This relational law does not independently calculate each score or prove host delivery and every tie case. |
| Latch release | workflow::latch tests threshold order and zero-count startup. workflow_invariants::latch_releases_each_accepted_route_exactly_once compares an independent waiting list, release phase, and exact recipient order through generated arrivals. | The property does not interpret delivery admission or record a dedicated mutation slice. |
| Dependency workflow | workflow_invariants::workflow_matches_an_independent_dependency_run tracks step and run phases across start, completion, failure, and cancellation, including invalid early completion and failure. | The focused property and workflow unit tests do not provide a real interpreter trace or dedicated mutation slice for every branch. |
A20 pre-edit consistent-hash membership law
Classification: deliberate Bombay ring-routing policy. With a fixed key hash,
stable member tokens, and a positive replica count, adding an Unknown member
changes no existing assignment; accepting its token can move a key only to
that member. Removing a member can change only keys it owned. These are
relational consequences of the stated clockwise ring selection law and do
not require a test to duplicate the point-mixing function. A generated test
will vary four distinct tokens and extra keys, compare complete route and
observation Actions, member order and token evidence, then check the two
before/after ownership relations. The focused three-member removal test and
the typed MemberTokenObservation are the lower-order witnesses. An isolated
mutation that leaves removed token evidence in the policy or changes the
selected eligible point must fail the generated law before retention.
The Router's one ordered membership control state, Unknown/Observed token
evidence, three hash rejections, transition branches, modules, production
lines, and public spellings remain unchanged. Current member order and each
latest token/version are the only values needed for later selection and
stale/conflict decisions. The proposed test adds no actor state, arrival
history, repeated cause, false cardinality, nested transition authority,
semantic boolean, or structural caller syntax. Cross-checks are the actor
transition algebra and normalized routing catalogue law. Disposition: pass
for the test model, pending the generated trace and counterfactual.
Post-edit, all ten routing invariant tests pass in debug and optimized Nix
builds. The new consistent-hash property checks every successful action lane
and continuation, exact keyed deliveries, member order and token evidence,
then the before/after key-owner relations. A shared typed route assertion now
serves the ring and rendezvous properties, deleting their duplicate action
check without adding a production bound. In an isolated Behavior worktree
with its own Cargo target, deleting only HashMembership::removed made the
new property fail at the exact survivor-token assertion; proptest shrank to
tokens = [0,1,2,3] with no extra keys. The mutant and worktree were removed.
This counterfactual proves detection of missing token-position repair, not
the ring mixer or runtime admission.
The test-only batch changes production +0/-0/net 0, tests
+98/-16/net +82, and public API +0/-0 types; the routing invariant file
is 790 to 872 physical lines. Router control, hash evidence alternatives,
rejections, branches, production modules, and all future-needed member and
token values remain unchanged. The residue scan and law-document cross-check
above found no new production history, repeated cause, false cardinality,
nested authority, semantic boolean, or positional caller syntax. Disposition:
pass for this evidence batch; A20 remains open for other laws.
A20 pre-edit hash-token version law
Classification: deliberate Bombay evidence policy, shared by consistent and
rendezvous selection. Once a member has Observed(version, token), an older
observation must return its exact owned value with Stale and leave the
committed evidence unchanged. The same version/token is idempotent, the same
version with a different token is a conflict, and a newer version replaces the
current token. The existing rendezvous example covers conflict only. A focused
pure Router test will prove stale ownership, idempotence, newer acceptance,
all action lanes, and evidence state. An isolated inversion of the version
ordering guard must fail the stale or newer assertion before retention.
No production type, branch, module, public spelling, or state alternative is
proposed. Router's one ordered membership list and each current token/version
remain exactly the values used by later selection and rejection. The test
adds no arrival-history state, repeated cause, false cardinality, nested
authority, semantic boolean, or structural caller syntax. Cross-checks are
the actor transition algebra and normalized routing catalogue law.
Disposition: pass for the test model, pending the focused witness and
counterfactual.
Post-edit, the focused router test passed in debug and optimized Nix builds. It checks the exact stale observation in both returned policy fields, no evidence mutation, idempotent same-version replay, a newer accepted token, and empty send/create lanes with continuation. The existing conflicting-token test now also checks the exact returned observation in both fields. In a separate Behavior worktree and Cargo target, inverting only the hash evidence version guard made the new test fail at the stale-return assertion. The mutant and worktree were removed. This counterfactual tests ordering of version admission; it does not prove the hash score or host delivery.
The actor source test section changes +84/-14/net +70 physical lines;
production Rust, public types, control states, evidence alternatives,
transition branches, and modules stay unchanged. The current member order
and latest version/token remain the only future-needed values. No arrival
history, repeated cause, false cardinality, nested authority, semantic
boolean, or structural caller syntax was introduced. The actor transition
and normalized routing laws were cross-checked. Disposition: pass for this
focused evidence batch; A20 remains open elsewhere.
A20 pre-edit least-loaded evidence law
Classification: deliberate Bombay routing policy. A member is ineligible
until it has versioned load evidence; the least load wins and declaration
order breaks ties. Unknown, stale, and same-version conflicting observations
return the exact evidence without changing membership or load. Removal
retires a member's evidence, so re-addition begins unknown. A source-only
unit test checks selected examples but no generated trace combines all of
these operations. The test-only candidate uses an ordered list of members
with optional latest (version, load) evidence and independently scans for
the first minimum, rather than calling the strategy's selection logic.
Mixed generated and deterministic traces will compare complete delivery or
rejection, every action lane, member order, and evidence after every step.
Existing Router/LeastLoaded types and actor unit tests are the
lower-order contracts. No production type, trait, state, effect, wrapper,
interpreter port, or public spelling changes. Baseline actor control remains
one router with an ordered membership list; subordinate evidence is Unknown
or Observed. Its latest version/load pair and membership order are the exact
future-needed values. Production branches, lines, and modules remain fixed.
The residue scan finds no proposed arrival history, repeated cause, false
cardinality, nested transition authority, semantic boolean, or positional
consumer syntax. Cross-check: actor transition algebra and normalized
routing catalogue law. Disposition: pass for this test-only model before
implementation.
The retained independent model uses an ordered member list and each member's optional latest version/load reading. It predicts the first minimum, exact reply or returned value, all effect lanes, and membership and reading state after every operation. The deterministic trace covers tied loads, stale and conflicting observations, removal, and re-addition; 384 generated traces mix those operations. The seven focused routing invariant tests passed after a clean Nix development build; both new tests also passed in the optimized Nix build. An isolated one-line counterfactual changed the selection from minimum to maximum. Both new tests failed for the intended recipient-selection law, and the generated trace shrank to two observed members followed by one route. The counterfactual worktree was removed.
The isolated worktree temporarily shared the main checkout's Cargo target
directory. A later main-checkout test linked its mutated artifact and failed;
that failure is excluded from the baseline evidence. Cleaning both affected
packages and rebuilding the unchanged main actor source restored the seven
passing tests. Future worktree counterfactuals need separate Cargo targets.
This test-only batch changes production +0/-0/net 0, tests
+235/-1/net +234, and public API +0/-0 types; the test file is
414 → 648 lines. Production router.rs stays at 1,311 physical lines and
its modules, branches, and public spellings are unchanged. Router control
remains an ordered membership list with each member Unknown or Observed;
the optional latest reading and order are precisely the future-needed values.
The test model adds no production state or nested transition authority. The
residue scan found no arrival history, repeated cause, false cardinality,
semantic boolean, or structural caller syntax. The actor transition algebra
and normalized routing catalogue law were cross-checked. Disposition: pass
for this evidence batch; A20 remains open for the rest of the ledger.
A20 ledger entries: catalogue versioning and membership
Next A20 routing candidate, before test changes: the Rendezvous policy's
stable member token is an explicit versioned fact, and adding an eligible
member can only keep an existing key assignment or move that key to the new
member. Removing a nonselected member cannot change an assignment. With
distinct tokens, reversing declaration order must preserve each key's owner
because the score depends on key and token, with no tie to break. This is a
deliberate Bombay highest-score policy, not an actor-model requirement. The
existing rendezvous_hash_is_deterministic_and_rejects_conflicting_tokens
unit test checks only one key before a conflicting observation; it does not
exercise membership edits across a key set. A generated relational trace
will compare the complete route Actions, member order, token evidence,
permutation ownership, and the before/after owner of each key through add and
removal without copying
the hash mixer. The existing router, MemberTokenObservation, and
RoutingStrategy are the lower-order contracts. No production state, type,
effect lane, public spelling, interpreter port, or policy changes. Router
control remains an ordered member list; the hash policy's only subordinate
alternatives are Unknown and Observed(version, token). Order and latest token
are precisely the future-needed values. Production branches, lines, and
modules remain unchanged. The proposed property introduces no arrival
history, repeated cause, false cardinality, nested authority, semantic
boolean, or positional caller syntax. Cross-check: actor transition algebra
and normalized routing catalogue law. Disposition: pass for the test
design, pending the counterfactual and measurements.
The retained test compares key owners before and after adding an unknown
member, accepting its token, reversing the fully observed membership order,
and removing one member. Every route checks the exact keyed payload and
recipient, empty creation lane, and continuation; membership operations check
all lanes and the policy's current evidence. The focused property passed in
debug and optimized Nix builds. A disposable worktree with a separate Cargo
target changed the Rendezvous score to depend on the member index. The
property failed at the order-independence assertion and shrank to distinct
tokens [0, 1, 2, 3] with no extra keys. The worktree and its generated seed
were removed. This proves the property rejects index-dependent selection;
the relation alone does not certify the exact mixer or every token branch.
This test-only batch changes production +0/-0/net 0, tests
+145/-5/net +140, public API +0/-0 types, and the test file
648 → 788 physical lines. Router production remains 1,311 lines, with
unchanged branches, modules, public spellings, and aggregate control state.
The surviving subordinate Unknown and Observed alternatives still own the
same latest version/token needed for future routing and rejection. The
residue scan found no production arrival history, repeated cause, false
cardinality, nested transition authority, semantic boolean, or positional
consumer syntax. The actor transition and normalized routing laws were
cross-checked. Disposition: pass for the test-only evidence batch; A20
remains open for other catalogue and runtime laws.
| Law | Focused transition and independent trace | Remaining proof boundary |
|---|---|---|
| Configuration version | operations::configuration tests stale and equal-version conflicting candidates with complete value return. catalogue_invariants::configuration_is_a_monotonic_atomic_register compares each generated proposal with an independent optional version/value register, including idempotent equality and every action lane. | The recorded equality mutant covers one guard only; persistence and host delivery do not follow from this pure actor trace. |
| Readiness evidence | catalogue_invariants::readiness_matches_per_dependency_version_registers compares three independent version/status slots after generated known and unknown observations, including stale and equal-version conflicts with returned evidence. | The generated range cannot reach numeric version exhaustion, and no dedicated readiness mutation slice is recorded. |
| Registry identity | discovery::registry tests atomic stale unbind. catalogue_invariants::registry_matches_atomic_compare_and_remove_bindings compares a separate ordered binding list and exact bind, unbind, and lookup outcomes after generated commands. | The existing inversion covers exact-recipient comparison only; snapshot ordering and host delivery have no dedicated counterfactual here. |
| Topic membership | catalogue_invariants::topic_is_an_ordered_idempotent_membership_snapshot models first subscription order, duplicate subscription, unsubscribe, and publication to every current recipient or exact empty-topic rejection. | This standalone topic law differs from keyed PubSub membership; no topic-specific mutation slice or host admission is claimed. |
Next A20 test-only boundary hypothesis, before edit: readiness version evidence
is ordered by the full u64 domain. At the maximum version, lower evidence is
stale, equal identical evidence is idempotent, and equal contradictory evidence
is rejected with the exact input; no arithmetic or wraparound manufactures a
newer observation. This is Bombay's version policy, not an actor-model law.
The existing independent three-dependency register model already checks the
complete state and action product after each generated command but samples
only small versions. Add u64::MAX - 1 and u64::MAX to that model's input
domain, retaining its own comparison equation and exact error checks. No
production state, type, lane, branch, public spelling, module, or line changes.
The root readiness state remains a fixed ordered list of Unknown or
Observed(version,status); each observed pair is exactly the current value
needed by the next comparison and query. No arrival-history, repeated cause,
false cardinality, nested authority, semantic boolean, or structural syntax is
introduced. Cross-checks: actor transition law and normalized operations
catalogue. Disposition: pass for the test design, pending the run.
The widened register model passed in debug and optimized profiles. Each
generated trace still checks exact rejection data, the complete empty action
product on acceptance, and all three dependency slots after every command.
The change is test-only (+0/-0 production lines, zero states, branches,
modules, and public spellings); the current evidence pair and fixed ordered
membership remain the only future-needed values. The residue scan and law
cross-check above remain unchanged. Disposition: pass for the boundary
evidence, with A20 still open for other catalogue and runtime laws.
A disposable worktree made readiness treat u64::MAX as stale even after
version zero. The widened independent model failed and shrank to two commands:
commit (dependency 1, version 0, Ready), then offer the same dependency at
u64::MAX; the model required acceptance while the mutant returned the exact
wrong Stale error. This counterfactual demonstrates that the new boundary
input is exercised and the oracle rejects the bad ordering. The mutant and its
generated seed were removed; production remains unchanged.
The remaining catalogue laws need equally specific entries, so A20 remains open.
A20 Nix coverage measurement
The flake now provides nix run .#coverage -- --lcov --output-path target/coverage.lcov, using the pinned Rust toolchain, its LLVM tools, and
Nix-provided cargo-llvm-cov. The default development shell also includes
cargo-llvm-cov. A full --workspace --lib --tests --locked run passed after
the A11 migration and produced target/coverage.lcov; the macro fixture
integration tests ran too. Instrumented source-line observations were: core
1,584/1,834 (86.4%), actors 25,438/30,848 (82.5%), macros 727/899 (80.9%),
testkit 70/73 (95.9%), and mutation gate 309/348 (88.8%). The actor files
with the least measured execution among files of at least 30 instrumented
lines include stable-proxy state (15/71), the shared send-product derivation
(21/46), dynamic-supervisor event (21/43), dynamic-supervisor entry retirement
(43/85), stable-proxy protocol (46/87), and delivery-route composition
(81/149). Some generic code depends on which concrete products are
instantiated, so these counts identify review targets, not necessarily missing
runtime transitions. A20 remains open until the law ledger and
counterfactuals cover the full catalogue.
Repair ledger: dependency resolution and mutation verdicts
Before production edits, the blockers are A02 and A05. The macro law is a
derived Cargo/Rust naming contract: generated paths must name the actual extern
crate for ordinary package declarations and explicit renames. An external
consumer with both direct dependencies and the facade is the focused witness;
its unrenamed #[behavior] and #[pool_worker] uses must compile, while the
existing renamed and missing-dependency witnesses retain their outcomes. The
smallest expected production change is in behavior-macros/src/lib.rs (roughly
10 lines), with a fixture manifest/source, its lockfile, and the fixture test
harness. No public types are added or removed; generated behavior and actor
products and the existing facade resolution are reused.
The mutation-gate law is deliberate Bombay verification policy: exactly one
successful baseline and exactly one terminal, valid outcome for each discovered
mutant identity are required before a coverage verdict. A report missing its
baseline, duplicating an outcome, or reporting Failure must fail even if its
function-level viability floor is met. Focused adversarial report tests will
fail on the current gate before changing it. The expected production edit is
mutants-gate/src/main.rs (roughly 45 lines); the report's own mutant name and
existing function-floor products are reused. No public types are added or
removed. Neither repair changes actor transitions or interpreter effects.
Initial cumulative scope includes this audit and its two index links: three changed documentation files. The macro fixture required its own package because Cargo rejects the same package under renamed and unrenamed keys in one manifest; the repair adds seven macro and gate files rather than the estimated five. This ledger will be updated with measured production/test/API deltas at the next checkpoint.
Repair batch result on codex/repository-quality-repairs
- A02 complete. Both unrenamed direct package declarations compile the
ordinary
#[behavior]and#[pool_worker]expansions in an external consumer. The direct-plus-facade fixture now also uses a renamed direct actors dependency and exercises both entry points. Facade-only, renamed-facade, facade sibling example, and missing-dependency fixtures retain their outcomes. All fourcrate_resolutiontests passed after the fixture lockfile was regenerated offline. A later Nix run exposed that the nested lockfile had selected four registry versions absent from Nix's vendored root lockfile. The nestedindexmap,syn,toml_edit, andunicode-identversions now match the root lock; all four macro consumer tests passed again under--offline --locked. - A04 complete. The adapter denial now supplies a current
ChildDeliveryand a compilingRecipientcounterpart. The activation permit denial has the required generic bounds and a compiling single-transfer counterpart. The heterogeneous birth denial has the interpreter's real future signature; an isolated compiler probe produced E0277, and the same source compiled after adding the missing concrete child host. Filtered doctests passed. - A05 complete. The gate reads the cargo-mutants
namefield as candidate identity, requires exactly one successful baseline and one valid outcome per selected identity, rejects unknown or duplicate identities and malformed reports, and keeps function-level viability floors. Debug and release unit tests passed. Direct CLI probes returned exit 0 for a complete clean report and exit 1 for missing baseline, failed mutant, duplicate outcome, and an invalidSuccessmutant;emit-baselineproduced the expected viability inventory. - A08 and A16 partially repaired. The facade sibling example is explicitly selected by the macro consumer gate. The root README now has current local links, package versions, and interpreter trait names. Its code example compiled as an external offline path-dependent consumer, and a repository test checks its local document targets. Published package verification and the remaining entry-point and historical-document review stay open.
Scope checkpoint after this batch: 15 changed files; production
+87 / -67 / net +20 lines; tests, fixtures, and doctests
+295 / -43 / net +252 lines; public API +0 / -0 types. These counts use
line comparisons with inline test modules separated from production. The
positive production delta is new verdict validation, not a code reduction.
No actor aggregate control state, subordinate alternative, transition branch,
module, or public spelling changed; the aggregate-drift checkpoint has the
same before and after representation. The residue scan found no new arrival
history, repeated cause, cardinality assumption, nested transition authority,
semantic boolean, or positional user syntax in this batch. Its disposition is
pass for these tooling and fixture repairs; it makes no claim about A01/A03
or the catalogue simplification candidates. Actor-transition and normalized
atomic laws were cross-checked for scope, with no actor transition changed.
A03 pre-production projection law and failing caller
Classification: this is Bombay's derived static hosting requirement, not an
actor-model transition law. A concrete send product must expose, in declared
interpretation order, every protocol that one of its values can address
logically. The type-level product must retain repeated occurrences. An
InterpreterRequests<CustomerDelivery<P>> lane can address P logically even
when a particular value selects its exact variant, so its projection contains
one P. A DiagnosticAction<Recipient<P>, Diagnostic> can similarly target
P; an established-only or terminal-only route adds none. A wrapper appends
its inner requirements before its owned requirements, and transitive child
requirements follow the actor's sends. This projection is evidence only; it
cannot perform routing, allocation, or any Actions effect.
The pre-edit external consumer uses the public
LogicalDeliveryProtocols::Protocols syntax. It requires
InterpreterRequests<CustomerDelivery<CustomerProtocol>> to equal
BirthProtocol<CustomerProtocol, NoBirthProtocols> and requires
FifoRequests<NoSends, ...> to implement LogicalDeliveryProtocols.
cargo check --offline failed for the two intended reasons: the first product
is currently NoBirthProtocols, and the FIFO product has no implementation.
An independent diagnostic caller required
InterpreterRequests<DiagnosticAction<Recipient<DiagnosticProtocol>, ()>>
to contain that protocol while its EstablishedRecipient counterpart remained
empty. It failed only the logical-route assertion, confirming that the defect
is the blanket empty request projection rather than the exact-route case.
Focused assertions now live in
behavior-testkit/tests/logical_host_requirements.rs. Before production edits,
cargo test -p bombay-behavior-testkit --test logical_host_requirements --no-run
fails on the customer request, logical diagnostic request, and both wrapper
orders; the exact diagnostic counterpart remains the empty control case.
The existing lower-order pieces are LogicalDeliveryProtocols,
BirthProtocolProduct::Append, SendLayer's inner-to-outer ordering,
BirthNodeLogicalHosts, InterpreterRequests, the four atomic request
products, and ProxyEffects. Customer delivery and diagnostic routing are
already interpreted through typed request lanes. No interpreter action or
acceptance contract needs to change. A candidate design would give request
items a separately owned logical-protocol projection, then make
InterpreterRequests<M> project M rather than always returning empty. It
must prove two real nonempty request families, all five atomic families,
transitive children, and two wrapper orders without mandatory placeholder
inputs.
The selected public contract extends the existing InterpreterRequest seam
with an associated logical-protocol product. Each request already owns its
return continuation and possible recipient; a separate public trait would
split one request law across two implementations. The caller writes the same
InterpreterRequests<Request> syntax; only static
LogicalHostRequirements::LogicalHosts evidence changes. Host-free schedule,
observation, exact lifecycle, and parent-report requests select
NoBirthProtocols. CustomerDelivery<P> selects P; diagnostic route
capability selects logical P or empty. The request-product derivation appends
authored field products in interpretation order; ProxyEffects does the same.
Existing BirthProtocolProduct::Append, wrapper ordering, transitive birth
projection, request interpretation, and all runtime ports are reused. No
transition, rejection, effect, or custody value changes.
Pre-edit shape checkpoint: the five atomic actor control-state sums and their
subordinate alternatives remain exactly as declared; this batch changes no
state or result variant, transition branch, or source module. The affected
implementation files currently total 6,619 physical lines across 11 files;
the five atomic aggregate trees have 37 module declarations and 76 enum or
interpreter-request declarations by source scan. Public type and trait names
remain unchanged; one associated type is added to the existing request port.
Every surviving alternative retains precisely its prior current values.
The residue scan found no proposed arrival-history state, duplicate cause,
cardinality assumption, nested transition authority, semantic boolean, or
positional caller syntax. Cross-check docs/actor-transition-algebra.md,
docs/behavior-layer-laws.md, docs/atomic-runtime-settlement.md, and the
normalized atomic-family laws after the projection is compiled. Disposition
is pass: the post-edit implementation files total 6,707 physical lines,
net +88 for this type-level repair. Actor control states, subordinate
alternatives, transition branches, and source modules remain unchanged; the
same residue scan found no new historical state, duplicate cause, false
cardinality, nested transition authority, semantic boolean, or positional
caller syntax. The only public shape change is
InterpreterRequest::LogicalProtocols; no new public type or trait name was
added. The normalized actor laws and the transition and layer documents were
cross-checked; the stale claim that every interpreter request is host-free was
corrected in the core and composition documentation. Focused regressions
assert exact logical customer and diagnostic projections, exact-route
exclusion, two wrapper orders, and duplicate occurrence preservation. Real
FIFO, keyed, fixed, dynamic, and stable-proxy behavior compositions compile
with their expected protocol products or positions, including a transitive
worker host. The full workspace gate passed 797/797 tests.
A01 creation-order policy before documentation repair
Agha et al., “A Foundation for Actor Computation,” section 3.1,
separate fresh address allocation (newadr) from behavior initialization
(initbeh). They do not prescribe Bombay's host commit, initialization-action
settlement, or activation sequence. The contradictory orders in the two local
documents are the pre-edit failing contract witness.
Bombay's selected policy is: reserve a fresh address without publishing a live
endpoint; run the child's pure definition initialization; install the endpoint
and commit the creator-local nonce binding only after that fold succeeds; then
settle all initialization actions before ordinary ingress. Reservation failure
returns the whole staged request. A pure initialization error returns the
current child and exact error. An installation failure after a successful fold
returns the current child and uninterpreted initialization actions. After
commit, an effect rejection or interpreter fault belongs to the installed
child's drain and cannot retroactively become a creation rejection.
Initialization Stop still settles its final actions and never enables
ordinary ingress. For atomic workers, InitializeWorker is a later exact-host
request that reports the committed worker's initialization-effect outcome; it
does not redo the pure fold or establish a second birth. Activation follows a
successful ready report. This is a policy and derived composition law, not an
additional actor-model guarantee. The remaining A01 proof obligation is an
interpreter trace for each success, rejection, and stop path.
The interpreter port currently cannot realize this custody law directly:
RoutedCreation exposes the child only by shared borrow or by consuming the
complete request. A host that calls initialize(&mut child) before commit
would have to dismantle and rebuild the rejected request, obscuring exact
ownership. The pre-production regression in
behavior-testkit/tests/creation_initialization_order.rs calls
initialize(creation.child_mut()) through the staged request and fails with
E0599 because that borrow is absent. The intended syntax leaves the same
RoutedCreation owned by the host for InitializationRejected or
HostRejected, and permits Established only after successful installation.
The smallest public repair is one mutable-child borrow on the existing
RoutedCreation runtime port. It reuses initialize, CreateChild,
ChildCreationOutcome, and EstablishChild; it adds no transition type,
effect lane, interpreter trait, or actor state. The aggregate-drift checkpoint
is unchanged before and after: no aggregate control sum, subordinate
alternative, branch, module, or public type name changes. The new method owns
no arrival history, repeated cause, cardinality assumption, nested authority,
semantic boolean, or positional syntax. The retained disposition is recorded
below.
A01 retained result: RoutedCreation::child_mut is the single new runtime
borrow port. It lets an EstablishChild host call the real pure initialization
fold while retaining the complete request for typed rejection. The six-case
testkit host trace checks reservation, fold, install/commit, effect settlement,
and ingress authorization, including allocation failure, pure fold failure,
host refusal with uninterpreted actions, initialization stop, and a post-commit
effect failure that cannot undo birth. The focused tests and the 804-test
workspace run passed. docs/actor-transition-algebra.md,
docs/atomic-runtime-settlement.md, and docs/adapter-contract.md now state
the same order; the atomic worker InitializeWorker request is explicitly
later effect-settlement observation, not a second pure fold. Post-edit drift
disposition is pass: no control-state sum, subordinate alternative,
transition branch, source module, or public type name changed; one public
borrow method was added and no historical state, false cardinality, nested
authority, semantic boolean, or positional syntax was introduced. The testkit
host witnesses that the typed port can realize this policy; the downstream
Bombay Engine implementation still needs its own integration verification.
The isolated probes used Rust 1.95.0 and local path dependencies on the audited crates. No production mutation was made. Their relevant results are:
| Probe | Observation |
|---|---|
Unrenamed bombay-behavior dependency; ordinary #[behavior::behavior] counter | cargo check --offline fails with E0433: cannot find bombay_behavior; the identical source compiles after renaming the dependency to behavior |
Renamed core plus unrenamed bombay-behavior-actors; ordinary #[pool_worker] assignment completion | Fails with E0433: cannot find bombay_behavior_actors; renaming the dependency and corresponding authored path to actors compiles |
require_projection::<FifoRequests<NoSends, NoSends, NoSends, NoSends, NoSends, NoSends, NoSends, NoSends, NoSends>>(), with T: LogicalDeliveryProtocols | Fails with E0277, establishing the missing product law even when every lane already has a projection |
fn duplicate<W>(permit: actors::atomic::ActivationPermit<W>) { let _accepted = permit; } | Still fails with E0277 after removing the second move; the existing negative example is not specific evidence of affine ownership |
| Mutation report with no baseline, one caught and one unviable mutant | Gate exits 0 and prints mutation coverage: 1 viable / 2 total |
Mutation report with a successful baseline, one caught and one Failure mutant | Gate again exits 0 with the same coverage line |
External caller invokes delegate_transition before initialization, then calls initialize twice on the same owned counter | Test passes and observes all three state changes; the public composition ports rely on caller discipline |
The mutation probes use two candidates with file a.rs and function name f,
and baseline {"floors":{"a.rs::f":1},"known_zero_viable":[]}. Outcomes use
the gate's existing summary and scenario schema; the tested command is
behavior-mutants-gate check OUT OUT/baseline.json. These are acceptance bugs
in the verdict tool, not results of a real mutation campaign.
A14 trusted-port evidence
The public InitializationTurn constructor remains private. The documentation
now states the narrower truth: initialize(&mut B) and
delegate_transition(&mut B, event) are trusted wrapper/runtime ports that
can issue turns repeatedly or out of lifecycle order; the consuming
Activate::initialize application path owns the once-per-definition rule.
behavior/tests/trusted_ports.rs is a legal external caller witness: it
delegates an event before initialization, then initializes the same definition
twice, inspects all three complete empty Actions, and observes the three
state changes. The focused test passed. This repair changes no actor effect,
state type, or turn constructor visibility.
A09 testkit simplification
The testkit now imports the existing behavior_actors::Activate trait directly
in its six callers; the forwarding InitializeTest trait and its public
spelling are gone. The unused Criterion declaration and its 458 lockfile
lines were removed without updating unrelated dependency versions. Fifteen
tests with no .await now use the synchronous harness. Destination-only
fixtures in the catalogue model files implement Protocol alone; they no
longer pretend to own an inert Behavior lifecycle. The testkit bench check
and all migrated test targets passed. This deletes one duplicate public trait,
one dev dependency graph, and nine generated inert behavior implementations;
it changes no actor transition or runtime effect law. The existing consuming
Activate path and its tests are the caller-facing proof, so no replacement
interface or no-op fixture was introduced.
The routing-invariant fixtures and work-queue unit tests also dropped their
destination-only inert Behavior implementations. Their routes require only
Protocol; the routing and queue tests still pass with those narrower
fixtures.
The pub-sub unit destination likewise now implements only Protocol.
A06 property-oracle evidence
The sequencer, deduplicator, and order-gate models now compare complete
ordered delivery and reply vectors, including destination addresses, reply
alternatives, rejected payloads, creation emptiness, and next verdict after
each generated step. Their initialization actions are checked too. The
deduplicator uses a non-Clone boxed payload and checks its original pointer
through accepted delivery or duplicate rejection, so equal reconstructed
values cannot masquerade as returned ownership. Configuration, readiness,
health, cache, registry, and topic models now inspect successful actions,
reply cardinality and destinations, and initialization rather than discarding
those observations. Readiness generation carries the domain status enum
instead of a semantic boolean; stale errors check the committed version.
The independent map, deque, and list oracles remain separate from the
production transition methods. Focused model and invariant suites passed.
The work-queue model now checks initialization and every step's full
assignment/outcome vectors, recipient, creation lane, and next verdict. It
tracks distinct non-Clone owned work and unique reply destinations through
both waiting and available FIFO queues; the focused property passed.
The keyed pub-sub model now independently checks topic introduction order,
recipient membership, complete successful actions, exact rejected commands,
and the original allocation of an undelivered owned publication. Its full
seven-test invariant suite passed.
Four isolated temporary counterfactuals each caused the targeted property to
fail: an extra reply, a wrong destination, a stop verdict, and a lost original
boxed payload. The temporary test target and its regression artifact were
removed afterward. No production actor state or effect type changed.
Source evidence at the audited revision:
-
A02/A08: macro resolution and consumer harness.
-
A03/A11: atomic product derivation and current projection tests.
-
A04: obsolete adapter fixture and activation permit fixture.
-
A05/A07: verdict implementation and gate definitions.
-
A06: catalogue models and catalogue invariants.
-
A09/A10: testkit API and driver.
-
A18: benchmark workload.
-
cargo nextest run --workspace: 783 passed, 0 skipped. Three nested Cargo fixture tests were reported slow; compilation and execution overlapped other audit builds, so these timings are not a performance baseline. -
mdbook build docs: passed with this audit included in the book. -
python3 -m unittest scripts/test_check_published_docs.py: three passed. -
python3 scripts/check_published_docs.py target/doc: passed after building the updated guide. All local links in this audit also resolve in the source tree. -
git diff --check: passed. -
nix flake check: passed against the clean audited baseline, including its optimized nextest run (783 passed, 0 skipped), build, Clippy, documentation, doctests, formatting, dependency checks, and package-content check. The added documentation is checked separately by the book build and link checks above. The mutation campaign is a separate Nix package and was not run; its focused verdict probes are recorded above.
Change scope: one audit document and links from the engineering index and book
contents. Production +0 / -0 / net 0; tests +0 / -0 / net 0; public API
+0 types / -0 types. Every checklist item remains open; documenting a finding
does not implement its repair. This paragraph records the audited baseline;
the branch repair scope is measured above.
Repair verification on codex/repository-quality-repairs:
cargo nextest run --workspace: 790 passed, 0 skipped before the final gate-only test for viability collapse; that additional test passed in both debug and release, making 791 current workspace tests.cargo test -p behavior-mutants-gate --bin behavior-mutants-gateand the matching--releaseinvocation: 10 passed each.cargo test -p bombay-behavior-macros --test crate_resolution: 4 passed.- Filtered actor adapter and activation-permit doctests and the core
heterogeneous-birth doctest passed. An isolated
rustccounterfactual confirmed E0277 for the missing host and successful compilation after adding its exact implementation. - The README example compiled in an offline external path-dependent consumer;
python3 -m unittest scripts/test_check_published_docs.pypassed four tests. nix flake check: passed all 13 checks with the new fixture and audit document included in the Git source. The mutation campaign itself is a separate package and was not run.mdbook build docs,python3 scripts/check_published_docs.py target/doc, andgit diff --check HEAD: passed after the repair record was written.
Final verification of this repair batch before its branch commit:
cargo nextest run --workspace --no-fail-fast --test-threads 4: 810 passed, 0 skipped. The three external macro consumers were slow while sharing the fixture target; no test failed.cargo test -p bombay-behavior-macros --test crate_resolution --offline: 4 passed with the nested lockfile aligned to the root vendored set.nix flake check: passed all applicable aarch64-darwin checks, including optimized nextest, build, Clippy, docs, doctests, formatting, audit, deny, and package-content verification. The first run exposed the nested fixture lockfile mismatch; the corrected run passed.bash scripts/check_published_packages.sh: passed. It assembled the three archives and compiled an extracted-package consumer using the README declarations and both public macro entry points.cargo bench --profile devpreflights forprotocol_matrixandfifo_poolpassed with three short samples each. These are workload checks, not performance claims.mdbook build docs, README link tests,cargo fmt --all -- --check, andgit diff --check HEADpassed.
At that checkpoint, thirteen checklist items were complete and seven remained
open. The source changes under crates/*/src were +609/-430 physical
lines (net +179), including the stricter mutation verdict and logical-host
projection. The branch deletes the duplicate BufferSends and test-only
InitializeTest public names and adds one associated logical-protocol type
and one child-borrow method to existing ports. No actor control-state variant
was added. This scope measurement is diagnostic; it does not certify the seven
open design and integration items.
The later branch currently has fourteen complete items and six open items.
After nix flake update, nix flake check passed all ten applicable
aarch64-darwin checks on signed commit d332af9, including build, Nextest,
Clippy, docs, doctests, both formatting checks, audit, deny, and package
verification. These gates do not close the six remaining design, mutation,
and downstream integration criteria.
Against merge base 435560ce7bea8ad3330ee2d42e5034f837a80602, the
current branch's source changes are net −78 lines in behavior/src, −1,089
in actors/src, −4 in behavior-macros/src, and −8 in
behavior-testkit/src: 1,179 fewer production and testkit source lines.
The crates/ tree as a whole is net +332 lines because focused tests and the
stricter mutation gate grew, alongside benchmark and supporting changes.
These git diff --numstat counts are diagnostics; the six
open checklist items still decide whether the remaining interfaces and
aggregate states are essential.
References
Repository acceptance criteria are in AGENTS.md. Current semantic references
are actor transition algebra,
behavior layer laws,
atomic runtime settlement, and the normalized
proxy, fixed supervisor,
dynamic supervisor,
FIFO pool, and keyed pool
laws. Their ordering conflict
is tracked by A01; this audit does not silently choose a new semantic law.
Rust interface guidance comes from the Rust API Guidelines. The rustdoc book describes compile-fail tests; the Cargo check documentation describes target selection. Rust guidance is distinct from Bombay's stricter local policies on naming, semantic booleans, imports, and static dispatch.
Public surface inventory
This inventory supports A13. #[doc(hidden)]
changes Rustdoc display, not Rust visibility. The counts below are annotation
sites in source, so a grouped re-export and its original declaration each
count once. The current branch has 10 sites in crates/behavior/src and 22
in crates/actors/src; the earlier audit counted 25 and 89 before the
documentation visibility review.
Hidden core declarations
| Owner | Annotated declarations | Contract owner | Visibility decision to review |
|---|---|---|---|
actor/creation.rs, occurrence proof | StructuralChildOccurrence, ChildCreationProduct, ChildOccurrenceResolution, ResolveChildOccurrenceDescriptor, BirthNodeAt, ChildOccurrenceProductAt | Generated code obligation | The macro and structural child products implement these proofs. A visibility change needs compile-pass and forged-occurrence compile-fail witnesses. |
actor/creation.rs, protocol projection | BirthModeProtocols, BirthNodeProtocols, BirthNodeLogicalHosts | Generated code obligation | These traits project closed birth and logical-host products; consumers can name the resulting associated types without constructing the proof nodes. |
actor/creation.rs, creation staging | ChildProduct::stage | Sealed structural conversion | Children::into_creates calls this method to turn the closed heterogeneous product into an ordered Creations batch. Only NoChildren and ChildCons implement the sealed trait. The interpreter receives the resulting batch through the creation effect; it does not call stage. |
ChildOccurrence::Resolution and DeclaredChildOccurrence are now visible
because manually authored roles must name them. BirthProtocolProduct is
visible because generic logical-host owners constrain and append that public
product. Rustdoc lists both names and the caller suites exercise them. The
private Recipient::new no longer carries an ineffective documentation marker.
CreationSettlement, CreationSettlements, and CreationsSettled are now
visible because external callers name them when retaining or returning exact
child-creation custody. InterpretCreations is also visible: the real Bombay
runtime names it in the bound for its action interpreter. This is a source
witness for required port visibility, not yet an A17 integration verdict.
CreateChild::into_parts is visible because external creation-interpreter
tests and the inspected Bombay child host consume it to retain exact owned
creation parts across acceptance or rejection. The method did not gain a new
capability; its Rustdoc now describes the existing custody transfer.
CreationCorrelation<P, Occurrence> is visible because external ActionItem
implementations name it as an exact, non-authoritative creation prerequisite.
Its occurrence parameter and private fields keep equal numeric IDs at
different child positions distinct.
CreationId::get is also visible because the actor catalogue derives
shutdown and operation correlations from it, and external interpreter tests
inspect those exact values. Its Rustdoc now states that the numeric projection
is neither actor identity nor proof of a committed fresh child.
Hidden actor declarations
| Owner | Annotated declarations | Contract owner | Visibility decision to review |
|---|---|---|---|
atomic/{dynamic_supervisor,fifo_pool,fixed_supervisor,keyed_pool}/{event,requests}.rs | DynamicSupervisorEvent, DynamicSupervisorRequests, FifoEvent, FifoRequests, FixedSupervisorEvent, FixedSupervisorRequests, KeyedEvent, KeyedRequests | Runtime port | Public associated event and send products let an interpreter carry complete typed lanes; the aggregate owns their transition semantics. |
atomic/fixed_supervisor/lifecycle.rs | FixedLifecycleRoute | Generated code obligation | This sealed route proof is implemented for the finite fixed-supervision lifecycle forms. |
atomic/pool/mod.rs | CompletesAssignments | Generated code obligation | The sealed completion capability belongs to generated pool workers and declared completion products. |
atomic/stable_proxy/{effects,protocol}.rs and atomic/mod.rs | ProxyEffects and its re-export; ProxyEvent as a concrete associated event type | Structural event/effect products | The host and typed proxy effects must keep rejection custody and every ordered lane; application callers use the typed Behavior projections and event ingress. ProxyDrain, WorkerStartResult, and ProxyOperation::creation are now visible because callers match the concrete outcomes or inspect the exact creation correlation. |
atomic/worker/mod.rs | HostedInitialization | Internal type alias | WorkerRecovery::into_retirement returns the actual Actions type through this alias. The exact worker and actions are now visible on WorkerRecovery; WorkerAttempt is a visible opaque correlation value. Whether the alias itself needs an external spelling requires a real host caller. |
lifecycle/shutdown_coordinator.rs | HeterogeneousShutdownItem, ChoiceSettlements, HeterogeneousShutdownChoiceSettlement | Generated code obligation | The closed heterogeneous choice product supplies the typed settlement shape. |
atomic/mod.rs, atomic/pool/mod.rs, and lifecycle/shutdown_coordinator.rs | Grouped re-exports of the declarations above | Same as original declaration | The re-export annotations add no second capability; each name remains publicly reachable through its parent module. CustomerDelivery is now visible because external interpreters must name it; FixedBuilder, FifoError, and KeyedError are visible because applications name the inferred builder and aggregate errors. |
This table classifies ownership but does not by itself justify retaining each public spelling. In particular, an associated type that mentions one of these values is not proof that applications must name it. Closing A13 still requires caller-facing compile witnesses, a trait-implementor inventory, and a repeatable compile-cost comparison before changing visibility or bounds. The branch-level cold comparison below measures aggregate compiler impact; it does not replace a focused before/after comparison for a future individual bound.
AssignWorker::target and CustomerDelivery are now visible runtime ports.
The interpreter obtains a clone of the exact recipient capability through
target, then consumes settle to transfer the delivery while the request
retains its receipt. A rejected keyed outcome retains the original customer
route in the complete CustomerDelivery action. The former public receipt
and into_parts assembly methods remain atomic-module-only.
ProxyDrain and WorkerStartResult are visible domain outcome sums because
external supervisor and proxy callers match their concrete alternatives.
ProxyOperation::creation is visible for trusted interpreters selecting the
exact proxy; its numeric value remains creator-local correlation, not actor
identity or installation evidence. ProxyEffects and ProxyEvent remain
hidden structural products reachable through typed behavior projections and
event ingress; their documentation status grants no extra authority.
WorkerRecovery and WorkerAttempt are visible opaque custody and
correlation values. WorkerRecovery::into_retirement consumes the failed
worker and untouched initialization actions together. WorkerAttempt::creation
reveals only the creator-local correlation; both constructors and their
fields remain private. The HostedInitialization alias still has no public
crate-root spelling, so its external naming need remains open for the real
host witness.
ObserveCreation<P, Occurrence> is now visible as the typed same-action
creation-observation request. Its prerequisite remains exact
CreationCorrelation<P, Occurrence>, and its creator-local numeric ID does
not establish a child or substitute for the protocol and occurrence types.
The complete worker initialization and activation port is visible: the
existing InitializeWorker, WorkerInitializationOutcome,
WorkerInitializationReport, InitializationAttempt, ActivationPermit,
BeginActivation, WorkerActivation, ActivationStartRejection, and
WorkerInitializationFailure types and their already-public methods now have
Rustdoc pages. The permit remains non-forgeable, and the host must admit
Started before polling the activation plan. Exposing these pages adds no
constructor, field, interpreter effect, or transition.
The P2 assignment custody witness narrowed two formerly public methods after
external caller tests proved the consuming settle operation. Compile-fail
fixtures reject receipt extraction and splitting the request with E0624,
while a second settlement fails with E0382. The corresponding real FIFO and
keyed caller suites, benchmark, and fuzz campaigns use actual exact-delivery
admission. This closes those two spellings only; proxy assembly remains open.
The P2 proxy owner settlement also made the previously doc-hidden
ProxyOperationId export unnecessary. External interpreters now receive the
complete control through ProxyControlAdmission and return the exact actor or
control; only the owner retains the ID. A dedicated external fixture rejects
the ID name with E0603, while the receipt-constructor fixture independently
rejects new with E0599. The ID remains available to private supervisor
state and receipt settlement through a crate-private re-export.
The exact assignment and proxy interpreter products are now visible in the
atomic Rustdoc index: AssignWorker, AssignmentReceipt, ProxyControl,
ProxyOperation, ProxyInputReceipt, and ProxyInputResult. These were
already public Rust names required by external InterpretItem and
ProxyControlAdmission implementations. Their fields and owner-only receipt
constructors remain private; hiding the item pages served no capability
boundary. Six actor annotation sites were removed, leaving 75.
The worker-preparation interpreter path is also visible: PrepareWorkers,
PendingWorkerPreparation, WorkerPreparation, and both request phases'
source_and_role, accept, and reject methods. External fixed/FIFO tests
and Bombay's inspected source already name this progression. The ticket,
constructors, fields, and owner settlement remain private. Nine more actor
annotation sites were removed, leaving 66.
DiagnosticRoute, DiagnosticAction, DiagnosticAccepted, and their four
existing constructors are now visible in Rustdoc. External actor and
interpreter-contract suites already name these products to settle routed and
route-free diagnostics, while the sealed route trait still limits its
implementors. Removing eight documentation annotations changes no Rust
visibility, constructor, or transition; 58 actor annotations remained at that
checkpoint, with 56 after exposing the customer-delivery ports.
The proxy outcome and correlation documentation repair leaves 54 sites.
The returned-worker custody repair leaves 50 actor sites.
The worker initialization and activation port repair leaves 23 actor sites.
The creation-observation port repair leaves 22 actor sites.
WorkQueue now keeps worker-route cloning and equality on construction and
transition operations, where queue inspection and duplicate availability use
them. Its protocol identity accepts a lawful ReplyRoute without PartialEq.
The external protocol-only caller failed on the previous aggregate bound with
only E0277 and passes after the bound move; the FIFO transition suite still
exercises the comparable route used by a running queue.
Twelve unwrapped catalogue actors now expose BehaviorBase<Base = Self> for
opaque domain types without inheriting bounds used only by construction or
transition: Acknowledgements, Resolver, Configuration, Readiness,
Machine, Topic, PubSub, Presence, Lease, Barrier, Workflow, and
OrderGate. The external caller test failed on their former Clone, Eq,
Copy, PartialEq, or Ord bounds and passes after removing only those
impl-level bounds. Their stored state, protocol, and transition contracts are
unchanged. Router remains structurally bound by its route and strategy
policy and needs a separate owner review.
Deduplicator and OrderGate now also expose protocol identity for an opaque
key. Deduplicator exposes its read-only base projection on the same terms.
The key comparison bounds remain on the operations that actually deduplicate
or order messages. A focused caller failed before the change only on these
three impl-level bounds and passed afterward; the routing transition suite
remained green.
PriorityQueue now also accepts an opaque priority type at protocol identity
and base projection. Its Ord proof remains required where construction and
transition use the heap. The external caller failed on the old aggregate
bound and passes after this move; the stable-priority selection trace remains
green.
A source scan after these batches found Router as the only catalogue
Protocol or BehaviorBase impl whose header still carries a copying,
comparison, or ordering bound. Its route equality is used for membership and
its current RoutingStrategy<Route> contract requires Clone + PartialEq.
The aggregate review retained those bounds: construction deduplicates by
route identity, Add/Remove compare exact members, and a route attempt clones
its policy candidate for rollback on rejection. A type-level router with an
uncomparable route would have no lawful construction or transition, so
removing only a repeated header bound would add no usable protocol contract.
Public trait implementors
The source declares 77 top-level public traits: 55 in behavior and 22 in
behavior-actors (counted with rg '^pub trait '). The following inventory
accounts for every declaration. “Author” means an application defining its
own typed actor, effect, or child protocol; “interpreter” means the code that
settles an exact typed request. A structural proof may be public because a
generated or manually authored product must implement it, even if ordinary
applications should not mention it directly.
| Owner | Traits | Lawful implementors and reason for the port |
|---|---|---|
transition.rs | Protocol, Behavior | Authors declare a stable typed destination and its pure transition; wrappers implement the same contract for composed actors. |
transition.rs | LogicalHostRequirements, BehaviorBase | Blanket logical-host projection for any qualifying behavior; authored behaviors and wrappers expose the underlying base. |
transition.rs | BehaviorLayer | A concrete construction closure or an authored construction type; the existing blanket closure implementation is the common case. |
user_event.rs | UserEvent, ComposedEvent, EventIngress, ChildInputIngress, InjectEvent, RecoverEvent | Authored event sums and structural wrapper event products; these preserve a typed user-message lane and lossless event injection/recovery. |
actor/addressing.rs | Address, EndpointAddress | Address-space authors and concrete address types; the latter provides the endpoint form used at the interpreter edge. |
actor/addressing.rs | InterpretEstablished | Interpreters of an exact established recipient capability. |
actor/creation.rs | ChildRole, ChildOccurrence, ResolveChildOccurrence, EstablishChild, ChildCreationProduct, DispatchBirth | Authors or generated role declarations establish exact child positions; structural birth products and interpreter adapters resolve and dispatch them. |
actor/creation.rs | ChildPosition, BirthNodeAppend, ChildOccurrenceResolution, ResolveChildOccurrenceDescriptor, BirthNodeAt, ChildOccurrenceShape, ChildOccurrenceProduct, ChildOccurrenceProductAt, ChildProduct | Closed structural child-position and birth-product proofs; source and generated products implement these to retain exact occurrence and custody. |
actor/creation.rs | BirthMode, BirthProtocolAt, BirthProtocolProduct, BirthProtocols, BirthModeProtocols, BirthNodeProtocols, BirthNodeLogicalHosts | Authored birth modes and structural projections enumerate child protocols and logical hosts without runtime lookup. |
effects/actions.rs | ActionSettlements, BehaviorSettlements, CreationSettlements, AppendSend | Complete structural action/creation settlements and typed send-product append operations. |
effects/actions.rs | InterpretCreations | Interpreter-bound traversal of one exact ordered creation product. |
effects/sending.rs | ClassifySettlement, ActionItem, SendSettlements, SendInput, SendEffects, LogicalDeliveryProtocols, SendsFor, SourceAction, SourceSettlementCustody, ReturnToEmitterFor, InterpreterRequest | Authors and concrete structural products define the request, settlement, routing, and logical-destination equations. The #[behavior] macro derives the logical-host projection for named sends in field order; other authored custom sends products state it explicitly. A generated product is private by default and may be exported with sends = pub { ... } when its field types are public. |
effects/sending.rs | InterpretItem, InterpretSends, SourceAdmission | Interpreters settle exact items; concrete send products traverse them; actor ingress admits an exact returned source action. |
activation.rs | Activate | Blanket implementation for a behavior that can enter its consuming initialization path. |
atomic/diagnostic.rs, atomic/fixed_supervisor/lifecycle.rs, atomic/pool/mod.rs | DiagnosticRoute, FixedLifecycleRoute, CompletesAssignments | Sealed diagnostic, fixed-lifecycle, and completion products; only the declared finite alternatives implement them. |
atomic/worker/mod.rs, atomic/worker/preparation.rs | ActivationPlan, WorkerSource | An author supplies a concrete worker activation plan and a source that returns complete prepared or rejected worker custody. |
atomic/stable_proxy/operation.rs | ProxyControlAdmission | A trusted interpreter of a statically selected proxy child implements this port. It receives a concrete ProxyControl and returns the exact admitted actor or the owned control with a reason; the operation ID stays with ProxyOperation. The actor suites provide local interpreters, and the isolated Bombay runtime has an implementation, but its unmerged source does not prove production integration. |
composition/delivery_route.rs | DeliveryRoute | Sealed transferable logical, established, and mixed routes. An owner constrains its associated protocol address to BehaviorAddr<Owner> when needed; no second route trait is required. |
lifecycle/child_shutdown.rs | BeginShutdownPhases, DeclareShutdownPhase, FinishShutdownPhases, AssignAt, AllAssigned | Structural shutdown-plan composition and the finite proof that every required child was assigned. |
lifecycle/shutdown_coordinator.rs | ShutdownTargetAt | A typed child-position shutdown target in a heterogeneous plan. |
lifecycle/termination_monitor.rs, lifecycle/termination_propagation.rs | TerminationObservationTarget, TerminationTarget | Exact recipient forms for observing and propagating termination. |
protocol/established.rs | InterpretEstablishedObservation, InterpretEstablishedShutdown | Interpreters of exact established observation and shutdown requests. |
routing/router.rs | RoutingStrategy, RouteKey | Authors select a concrete policy and define the application message key; the catalogue supplies round-robin, least-loaded, consistent-hash, and rendezvous strategies. |
stash.rs | StaticallyInfallible, StashStatus | The former is sealed to infallible forms; the latter projects stashed-message status through multiple existing wrappers. |
The table identifies implementor roles, not proof that every spelling should
stay public. In particular, the structural rows still need external compile
witnesses before visibility can be reduced. StashStatus already has multiple
real wrapper implementations, so treating it as a redundant one-implementation
trait would be incorrect. Any further bound change needs a new caller diagnostic
and focused compile-cost comparison for that particular edit.
Canonical export ownership
The crate roots are the canonical export lists. This table classifies their complete groups by the owner that justifies the public names; mixed groups are further split by the hidden-item and trait tables above. No public spelling is retained merely because a private associated type mentions it.
| Crate-root export group | Public contract owner |
|---|---|
Core actor::addressing | Application address, logical/exact recipient and delivery capabilities; exact-delivery admission belongs to the interpreter. |
Core actor::creation | Authored child roles and staged creation are application algebra; occurrence/birth products are generated structural proofs; establishment, creation settlement, and exact returned custody are interpreter ports. |
Core effects::{actions,sending} and next | Actions, typed send products, and next decisions are application algebra; Interpret*, settlement, and source-admission contracts are interpreter ports; product traversal and append proofs are structural. |
Core transition and user_event | Protocol, Behavior, BehaviorLayer, turns, and event ingress are application and wrapper contracts; event-path and logical-host projections are structural composition proofs. |
Actors activation and atomic | Active and aggregate constructors/outcomes are application contracts; exact assignment, proxy, worker, observation, diagnostic, and preparation custody is the interpreter surface; event/request products and sealed completion/lifecycle traits are structural. The hidden-item table identifies every intentionally hidden member. |
Actors composition, machine, shutdown, stash, termination, and watch | Concrete application wrappers and protocol products; DeliveryRoute is the single sealed transferable route equation. |
Actors lifecycle and protocol | Authored shutdown/observation capabilities and results are application contracts; exact lifecycle, timer, observation, and shutdown admission are interpreter ports; closed child-position products are structural. |
Actors discovery, operations, persistence, routing, time, and workflow | Application templates, commands, outcomes, configuration values, and typed policies. Their concrete effects use the core interpreter ports. |
Macros SendProduct, behavior, and pool_worker | Generated-code obligations producing the same concrete products and bounds available to handwritten callers. |
Testkit TestRecipient, Mailbox, DriveDisposition, Trace, drive, and model | Test-only caller and independent-model API; none is a production interpreter. |
| Mutation gate binary | No public library API; the command and CI verdict contract are the exported developer interface. |
All 77 top-level public traits have implementor roles in the preceding table.
The 10 remaining hidden core sites are sealed or generated birth/occurrence
proofs. The 22 remaining hidden actor sites are structural event/effect
products, sealed generated proof traits and their grouped re-exports, plus
the internal HostedInitialization type alias. The CustomerDelivery
re-export from atomic::pool remains hidden because the canonical
atomic::CustomerDelivery page is visible. External interpreters can name
the actual request, outcome, receipt, and rejection values without naming
those hidden structural products. A further visibility reduction would need
its own caller witness; it is not part of this release review.
Cold actor-library compile comparison
On aarch64-darwin, the Nix shell at 1aeaed1 supplied Cargo 1.95.0 for both
revisions. Each run used a newly absent CARGO_TARGET_DIR, the same command
shape (cargo check --manifest-path REV/Cargo.toml -p bombay-behavior-actors --lib --locked --offline -q), and ran sequentially.
The actor crate's Cargo fingerprint recorded the same feature list, profile,
compiler configuration, and dependency identities in both revisions.
| Revision | Cold run 1 | Cold run 2 | Actor .rmeta |
|---|---|---|---|
main at 435560c | 184.54 s | 180.09 s | 27 MiB |
Audit branch at 1aeaed1 | 4.46 s | 3.97 s | 5.8 MiB |
The measured command checks the actor library only, not tests, downstream applications, or release builds. The revisions differ in many source files, including macro and actor implementations; the observation cannot attribute the difference to any one A13 bound or predict downstream compile time. It does show that this branch's actor-library metadata and cold check cost did not grow under this controlled comparison.
Exact observation surface migration
Two owners represent distinct laws: ObservationAuthority<P> owns one exact
protocol-matched cancellation attempt, and ObservationRelationship<P> owns
cloneable nonauthorizing accepted identity. ObserveEstablished::new(id, recipient)
owns fresh private correlation without another public issuer, key, or error type;
construction occurs outside Behavior folds. CancelObservation::new consumes authority and loses its former Copy/Clone API. The established report
sum returns whole rejected requests, so it requires the owning RecipientAddress
capability contract. Its Started, Stopped and Cancelled variants no longer
carry a separately forgeable numeric identity. The sealed target request port
is mutable one-shot emission, the public monitor constructor consumes a prepared
Observe, and consuming monitor/target/shutdown access recovers whole owned
values. These existing-signature and variant changes are breaking even though
there are only three new public nominal types.
PRD: Complete interpreter ownership and startup contracts
Date: 2026-09-29. Status: Behavior-owned contracts implemented on the repository-quality release branch; cross-repository P5 acceptance pending. The implementation ledger separates local proof from the future Bombay/Address runtime witnesses. This revision supersedes the 2026-09-28 creation-and-delivery proposal in this file.
1. Objective and implementation baseline
Make the current Behavior and Behavior Actors code fully interpretable by Bombay through small, concrete capability interpreters. Complete rejected request return, retained acceptance, and startup custody before attempting a broader redundancy refactor. Preserve the existing pure policies and aggregate transition authorities.
The implementation baseline is current Behavior commit
6ef850f47852b4a90a6eae7633757a9e5a1c9d6b, not the older registry release.
The review began at de8f1bf264366da29992c0061d5bae9888353bd5 with 37 modified
files; those pending implementation/test changes were committed during review
as b46923c; 6ef850f then updated the audit's follow-on work description.
They are part of this baseline, not changes to revert or recreate.
At implementation start, record the then-current merged commit or complete tree
snapshot and recheck the seven contract files fingerprinted in section 13.
The adjacent Bombay working tree was also inspected, based on HEAD
a7a66e3912731923015560463cd2c9e5e76fc041 with substantial pending changes.
Observations below about Driver, launch, and local hosting refer to that current
source. Its lock still selects Behavior/Actors 0.17.0 and Address 0.2.0. That is
an integration constraint to update, not the upstream design baseline. Do not
make current source conform to an obsolete locked signature.
The deliverable of this review is this PRD. It does not claim that its proposed methods compile, that startup has already been repaired, or that all catalogue machinery is redundant. A prescribed API below must first pass its specified external regression; compiler fallout cannot invent additional architecture.
Success criteria
- A runtime can attempt real delivery of
AssignWorkerandProxyOperationand return the complete original request on rejection, without cloning during settlement or assembling private correlations. FIFO's existing retry policy deliberately copies aClonejob before delivery and retains the original. - Explicitly retained acceptance survives continuing turns through the existing generic custody path. Ordinary discharged acceptance does not accumulate.
- Fresh commitment, initialization completion, endpoint publication, and worker activation have distinct, implementable meanings. Initialization runs once.
- Every supported failure has an exact outcome and a surviving owner for every value the protocol still owns. Consumed actions are never reconstructed.
- A downstream fixture using public APIs, real Communication, and real Driver proves the contracts. Manually manufactured successful host results do not count as that evidence.
- Runtime code contains no duplicate pool, proxy, or supervisor transition law.
Scope boundary
| Behavior / Actors owns | Bombay and primitive runtimes own |
|---|---|
| Pure policies and aggregate state | Delivery and admission attempts |
| Typed requests and source correlations | Tasks, clocks, endpoint publication |
| Consuming settlement and complete rejection return | Concrete initialization execution and startup evidence |
| Accepted-value retention declaration and static composition | Retained runtime values and parent-to-root retirement |
| Readiness, recovery, assignment, and supervision decisions | Observation of actual execution and termination |
Do not add a supervisor abstraction, runtime registry, erased envelope, second Driver, callback-driven behavior, or lifecycle engine. Do not change FIFO/keyed selection, fixed/dynamic supervision policy, or actor cardinality to make their implementations look alike. Distributed execution, Entity redesign, Mnesis, Selo, and transport replacement are outside this project.
2. Current-code findings
Verified contract defects
| ID | Current owner and evidence | Consequence |
|---|---|---|
| D1 | actors/src/atomic/pool/assignment.rs: AssignWorker::into_parts exposes target, assignment, receipt; returned is restricted to crate::atomic. receipt() can separately reproduce receipt evidence. | Actual transport rejection returns the assignment but an external interpreter cannot reconstruct the complete request lawfully. Public decomposition also separates evidence that ought to stay together during settlement. |
| D2 | actors/src/atomic/stable_proxy/operation.rs: private request fields, public into_parts, and public ProxyInputReceipt::new. | A runtime can issue acceptance, but cannot return a rejected control after consuming the operation. Independently supplied creation, endpoint, and operation evidence can be assembled in the accepted constructor. |
| D3 | behavior/src/effects/sending.rs: SourceSettlementCustody for Vec<ActionItemResult<Item>> unconditionally returns Exhausted(self). DiagnosticAccepted::Terminal owns its diagnostic. | Continuing Driver execution drops terminal evidence. The same blanket path also warrants correction for rejected, blocked, corrupt, and untouched requests; they are residual ownership, not discharged receipts. |
| D4 | behavior/src/actor/creation.rs: HostRejected requires current child plus untouched initialization Actions. | It cannot represent a failure after consuming those actions. Adding a reason or exposing a constructor cannot solve this ownership mismatch. |
| D5 | Current Bombay establish_child waits for spawn_owned_with publication; its panic/cancel/early-end branch panics. SpawnError::from_local treats settlement failure as unreachable. | The runtime cannot currently realize the documented established-child/initialization-result distinction for all startup outcomes. |
| D6 | Current Bombay LocalEnvironment::activate calls Address try_claim before commit(actions). Address makes a claimed endpoint resolvable immediately. | Publication callback delay does not hide the endpoint. Moving the fallible claim after effects would create the D4 failure instead. |
| D7 | Current Driver calls environment.publish() in rejected-initialization, corrupt-initialization, and accepted-initialization-stop branches. | Hidden Address reservation alone cannot satisfy the selected no-publication law; these generic startup branches must retire without publishing. |
Paths in this table are relative to crates/ in the named repository.
Existing machinery to retain
ItemSettlement,SettledItem,Interpretation, andActionSettlementalready own total ordered interpretation, rejection, corruption, and suffixes.SourceActions/SourceSettlementsalready return exact results to a live emitter.SourceCustody::{Exhausted, Retained, Admitted, Closed}already distinguishes terminal retention from live ingress.- Driver already keeps
Retainedsettlements across continuing turns and sends them to retirement. Fix D3 upstream; do not special-case diagnostics in Driver or retain every successful receipt there. InitializeWorker::resolvealready reunites a host result with the original plan and issues the correlated activation permit. It is not permission to initialize a definition a second time.WorkerInitializationFailurealready distinguishes rejected effects from interpreter corruption. Child action settlements belong in runtime custody, not in a new supervisor error sum.ShutdownEstablished::settledemonstrates an owner consuming a request at an interpreter seam.CustomerDeliverydemonstrates complete rejected-route custody. Neither proves a universal delivery wrapper is necessary.
Documentation conflicts to fix with implementation
atomic-runtime-settlement.md and actor-transition-algebra.md commit a fresh
host before interpreting initialization effects. adapter-contract.md also
contains an effects-before-endpoint sequence and a general short-circuit
failure statement. Reconcile these with section 6, including the difference
between ordinary rejection and corruption. Do not treat stale historical
engineering proposals as another normative contract.
The generic testkit Driver explicitly accumulates actions without interpreting
them. proxy_operation_settlement.rs presently proves type compatibility using
an absent value, not rejected transport custody. Existing creation-order tests
use a model host. These are useful tests with narrower claims than downstream
execution; retain that distinction in reports.
3. Laws and authority
| Law | Classification | Required implementation consequence |
|---|---|---|
| Newly allocated actor identity is fresh; creation and behavior replacement are different operations. | Actor research | No collision overwrite, reused endpoint substitution, or inferred replacement provenance. |
| A pure transition returns explicit communication, creation, and next-behavior effects. | Bombay's typed realization of actor operations | Execution remains outside Behavior; no Tokio or hidden delivery in transitions. |
| Rejection returns every still-owned input; acceptance transfers the payload exactly once. | Derived affine protocol | The request owner retains correlation during interpretation and reconstructs only from the actual returned payload. |
| Success status and remaining custody are independent. | Derived composition law | An accepted terminal value can have Accepted status and Retained custody simultaneously. |
| Commit fresh creation before dependent same-action operations; interpret initialization before ordinary transitions. | Bombay policy | Dependent operations see the committed child, and initialization is never replayed. |
| Public logical resolution remains absent until successful continuing initialization. | Bombay publication policy selected by this PRD | Use exclusive hidden reservation; private host commitment is distinct from publication. |
| Rejection continues independent effects; corruption preserves prefix, faulting value, and untouched suffix. | Existing Bombay interpretation law | No ? that loses siblings; no rollback fiction. |
| Each aggregate chooses its own complete next state and actions. | Library design constraint | Request settlement performs transfer/reunion only, never recovery, retry, or supervisor transitions. |
Primary research checked for this revision: Agha, Mason, Smith, and Talcott,
A Foundation for Actor Computation, §3, especially pp. 19–20, distinguishes
fresh newadr, initialization, and receptionists. It does not prescribe
Bombay's startup transaction, rejection sums, readiness, or publication API.
Primary paper.
Owner-controlled reconstruction follows the information-hiding criterion: request representation belongs to its owner, execution to its interpreter. This is a design inference, not an actor theorem. Parnas. The aggregate remains responsible for its invariant; subordinate protocol values do not become transition authorities. Evans, Aggregates. Use concrete types to express the resulting capabilities and ownership. Rust API guidance.
4. Delivery settlement contract
4.1 Compare complete ownership equations first
| Dimension | AssignWorker<P, Job> | ProxyOperation<Source, Worker, Plan> |
|---|---|---|
| Payload | Assignment<Job>, including completion authority | ProxyControl<Worker, Plan>; start/replacement carries definition and plan, shutdown does not |
| Destination | Already-established exact worker recipient | Creator-local proxy creation resolved to an exact private control endpoint |
| Evidence held by owner | Assignment receipt/correlation | Creation ID, affine operation ID, and static source identity |
| Accepted receipt | Original assignment receipt | Original creation/operation evidence plus exact resolved EstablishedActor<StableProxy<...>> |
| Expected rejection | ExactDeliveryReason | ChildInputReason, including missing binding or closed control |
| Subsequent result | Worker completion, possibly racing exit/admission | Proxy outcome, possibly racing exit/admission |
| Live settlement destination | Source = Self | Declared Source |
They share the owner retains evidence while a lower capability consumes or
returns the payload law. They do not share the same destination resolution,
receipt, rejection, or later outcome. Keep both concrete request types. Reuse
ItemSettlement and existing delivery capability; do not introduce a shared
public envelope or a universal reconstruction trait.
FIFO's accepted-worker retry policy retains its original Job while the worker
receives a clone. A rejected assignment returns that retained original to the
pool; the settlement method must not create another copy. A job type without
Clone cannot satisfy this policy after accepted delivery and worker loss.
Support for move-only FIFO jobs would require an explicit at-most-once policy
or a worker protocol that returns the job, with different failure semantics.
That policy is outside this PRD.
4.2 Required external syntax and transfer
The intended interpreter call for each request is a consuming
request.settle(&mut delivery_capability).await. This operation exists only
at effect interpretation. It returns that request's existing
ItemSettlement<Self, Self::Accepted, Self::Rejection, Self::Prerequisite>.
It does not return SettledItem; product traversal owns attempted/untouched
provenance. No application constructor or wrapper-depth argument is added.
Assignment implementation: inside the owning module, separate the receipt
from EstablishedDelivery<P>, invoke the existing
InterpretItem<EstablishedDelivery<P>, RootEvent, Path> capability, and reunite
its returned delivery with the receipt. Root/event path generics belong only
on this interpreter method if needed; they must not enter pool construction.
Proxy implementation: the lower operation takes the original CreationId
and ProxyControl, resolves the exact private proxy, and returns either its
exact established actor after control admission, or the same control and its
reason/fault. It must never receive the private operation ID. The owner adds
that held ID and the held creation correlation to acceptance or rejection.
The existing ChildInput accepted unit does not provide the exact actor needed
by ProxyInputReceipt. Select one narrow static interpreter seam,
ProxyControlAdmission<Worker, Plan>, with one admission method taking those
creation/control values. Its return is the existing ItemSettlement shape
with ProxyControl as returned item, exact proxy actor as accepted value,
ChildInputReason as rejection, and Never as prerequisite. No new result
wrapper is needed. The seam is an intentional third-party interpreter port;
its generic parameters select concrete worker/plan protocols, not policy.
The port's implementation must use the containing ProxyOperation interpreter's
statically selected child occurrence. Equal numeric IDs in different creator
namespaces or occurrences must remain distinct. Do not add runtime type lookup
or guess the child role from the ID.
This specifies an API candidate with one new interpreter trait, not a proven signature. Before production, compile the external syntax with two concrete worker/plan substitutions and a real proxy control rejection. If existing static child admission can return the same exact evidence without a new seam, use it and delete this proposed trait from the ledger. Do not ship both ports. No implementation agent may widen the seam to implement lifecycle policy.
4.3 Complete settlement table
| Lower-capability outcome | Assignment owner | Proxy owner |
|---|---|---|
| Accepted | Consume payload once; return the held receipt | Consume control once; join exact admitted actor with held creation/operation evidence |
| Rejected | Return exact target, actual returned assignment, and held receipt as Self | Return original creation, actual returned control, held operation, unchanged source as Self |
| Corrupt before transfer | Return complete request and exact fault | Return complete request and exact fault |
| No committed proxy binding | Not applicable to an exact recipient | Return complete request with existing missing-binding reason |
| Untouched after earlier product corruption | Product retains original request unchanged | Product retains original request unchanged |
Both current prerequisites are Never; do not add a synthetic blocked state.
Acceptance proves admission, not execution, completion, readiness, or restart.
A close between resolution and admission is a real rejection, not corruption
and not a preflight-liveness success.
The lower interpreter is trusted to return the actual payload and destination it received. Rust cannot distinguish all same-typed runtime values or prove a foreign interpreter honest. The owner API must make receipt substitution unavailable to ordinary callers, keep evidence out of the lower port, and test runtime identity preservation under two simultaneous requests. Do not claim a compile-fail test proves same-type runtime identity.
4.4 Close the old assembly surface
After the external proof succeeds, remove or narrow external
AssignWorker::receipt, AssignWorker::into_parts,
ProxyOperation::into_parts, and ProxyInputReceipt::new wherever they permit
independent construction of evidence. Keep only necessary read-only inspection
and owner-internal terminal decomposition. AssignWorker::returned remains
owner-private or is inlined into its sole owner path; it must not become the
public fix. Inventory all legitimate downstream consumers before narrowing.
Negative fixtures must reject external receipt assembly, double settlement, wrong protocol/source/occurrence, and using an operation after moving it. Tests inside the owning module do not establish these privacy guarantees. Cancellation must not drop the only settlement future while it owns the request. Bombay keeps the transfer alive to a settled result or retains its actual owned in-flight work through retirement; neither method creates a task.
5. Generic retained acceptance
5.1 Selected representation
Keep acceptance status separate from custody. Add an owner-controlled consuming
operation to the existing ActionItem contract, with the intended signature:
fn retain_accepted(accepted: Self::Accepted) -> Option<Self::Accepted>;
Some(value) is exactly one accepted value still requiring terminal custody;
None means that receipt has been discharged. This is a residual value, not a
boolean semantic flag or a second settlement vocabulary. Provide the ordinary
discharged default on this existing method so existing unit-receipt items do
not acquire a mandatory no-op policy, extra bound, or marker. The method's
documentation must say that its default permits destruction of the receipt.
DiagnosticAction overrides it: Delivered discharges; Terminal(value)
returns that exact accepted value. Review every current type Accepted owner
before retaining the default. Receipts carrying remaining authority must opt
in where they actually use the source-free custody path. Do not apply this
operation to SourceSettlements: their receipt must first return to the source.
The proof needs both retained and discharged real requests, not a new generic
accepted wrapper at every call site. Keep DiagnosticAccepted as its current
concrete product unless the proof demonstrates that replacing it removes more
machinery than it adds. No new public type is needed for this design.
5.2 Source-free collection algorithm
When offering a Vec<ActionItemResult<Item>> to custody:
- Consume its members in their existing order.
- For attempted acceptance, use
Item::retain_accepted; omit only discharged receipts, retain accepted values returned asSomeunchanged in kind. - Keep complete rejection, blocking, corruption, and untouched items.
- Return
Exhaustedonly for an empty residual; otherwise returnRetained. - Perform no send, source admission, reinterpretation, or automatic retry.
Classification remains independent: retained acceptance is Accepted, ordinary
rejection is Rejected, and corruption/untouched work is Corrupt. Driver must
inspect interpretation status before custody compacts discharged values. A
residual is not a complete historical success log. Do not reinterpret its
absence of discharged receipts as evidence those effects never occurred.
The accepted-retention operation is idempotent on its retained values: once it
returns Some, reapplying it must preserve that same value as Some. It cannot
perform effects or consume authority that its result still promises to own.
Re-offering a retained residual preserves it. A product with an earlier retained
lane still offers a later source lane. If that later lane is admitted, the
product returns Admitted with the retained sibling; after processing the
source input, the next offer still returns that sibling. Source closure keeps
the entire remainder. Preserve existing inner-before-owned SendLayer order
and named product order.
Audit offer_source_in_order, ActionSettlement composition, Actors'
send_product!, and generated/handwritten products. Change only machinery whose
current implementation fails this law. Driver's current Retained path should
need regression coverage, not diagnostic-aware production code.
5.3 Memory and retirement law
After any number of continuing turns, stored custody grows only with values whose ownership is still outstanding. Successful unit receipts and delivered diagnostics cannot create a growing retained history. Deliberately retained diagnostics do occupy memory until retirement; do not silently cap or drop them. Changing diagnostic disposition or bounded-retention policy is separate work.
At stop, source exhaustion, transition error, source closure, or interpreter failure, every retained value travels through the same typed retirement product as final behavior and runtime residuals. No actor must stop merely because it selected terminal custody for one diagnostic.
6. Creation, initialization, and publication
6.1 Commitment decision
Retain the current normative host commitment before effect interpretation law. Reject the proposed shortcut of moving a fallible address claim after consuming initialization actions. Select Address-owned hidden reservation and consuming publication as the runtime realization. This work is a dependency of complete startup acceptance, not code to place in Behavior.
The coherent sequence is:
exclusive fresh reservation, absent from logical resolution
-> pure initialization exactly once
-> private host and exact creator-local binding committed
-> Established becomes true; host owns child and initialization work
-> total initialization-effect interpretation exactly once
-> initialization status recorded; source settlements processed lawfully
-> continuing successful child may be publicly published
-> ordinary mailbox transitions permitted
For atomic workers, initialization additionally supplies one activation permit;
the aggregate's existing BeginActivation and activation result decide service
availability. Worker initialization success is not application readiness. A
stable proxy may be publicly present while its worker is unavailable, according
to its existing service policy.
Established proves a fresh concrete child host and committed occurrence/ID
binding with explicit CreationKind. It does not prove initialization
effects accepted, endpoint publicly resolvable, child still live, activation
finished, or service ready. A child may stop before the creator consumes that
creation result. Success must contain EstablishedCreation::Installed; the
existing nested rejected alternative must never be emitted as successful host
commitment.
Bombay must separate its internal child-host commitment acknowledgement from
the public spawn/publication result. Parent establish_child must not wait for
public readiness as its evidence of commitment. Root spawning can continue to
wait for publication, but its failure must return the full unpublished terminal
custody. Use the existing host/binding/task ownership; do not add another host
registry or recreate proxy state in a spawn service.
Correct Driver's three early terminal initialization branches to retire without
calling public publish. The private commitment acknowledgement must already
have transferred child ownership, so this does not strand a parent waiting for
its creation result. Retain public publish only on the continuing successful
path after required source-settlement processing. A source transition that
stops or fails before that point also remains unpublished.
Replace SpawnError::from_local's unreachable settlement case with an owned
unpublished terminal return, preserving the existing concrete local outcome:
final behavior, all action settlements, admitted control/user inputs,
activation work, descendants, and factual disposition. Do not project it to
bare Ended(Completion) or a coarse reason while dropping the residual. The
root consumer either receives that product or its exact typed terminal
projection. The design-stage external fixture must compile this product before
changing broad spawn signatures or adding projection bounds across callers.
6.2 Address reservation requirements
The Address owner must supply an affine reservation that excludes competitors without making the endpoint resolvable. Registration identity allocation, collision checks, and generation exhaustion happen before irreversible effects. Publication consumes that exact reservation without a second fallible claim or new generation allocation. Release and published lease retirement affect only their own generation; stale retirement cannot remove a later registration.
No endpoint reference granted by reservation alone proves a committed birth.
The host and creator-local binding must exist before issuing Established.
Private parent reports, exact child control, and lifecycle observation must work
without public logical resolution. Public logical self-send during unpublished
initialization gets the ordinary absent-address rejection; do not install a
hidden resolver exception. Exact/private traffic can be admitted according to
its capability, but must not run ordinary child transitions before initialization.
Prove bounded-capacity progress for nested creation and early child reports. Never wait for a parent's initialization to complete while that parent waits for the same child's public publication. If reservation cannot meet these requirements, stop the startup stage, record the counterexample, and revise this PRD before production; do not silently switch commitment semantics.
6.3 Ownership equation and complete outcomes
Before the pure fold, the interpreter owns the routed creation. After a successful fold and before commitment it owns current child + untouched Actions. After commitment the child host owns execution and its effects; after interpretation it owns current child + exact settlement + unfinished runtime work. These products are not interchangeable.
The outer ItemSettlement::Accepted(ChildCreationOutcome::...) records that
the establishment port accepted ownership and returned an outcome. Only the
inner Established result proves a birth; an outer accepted item containing
initialization rejection or panic must never be counted as a successful child.
| Event / milestone | Creation result | Initialization/terminal result and custody |
|---|---|---|
| Batch routing refuses | Existing whole-batch rejection | No child fold; unchanged batch and namespace reason remain owned. |
| Allocation/reservation refuses | Existing item rejection with complete routed creation | No child fold; exact runtime allocation failure remains available where projected reason is coarser. |
Pure fold returns Err | InitializationRejected { creation, error } | Current child, exact error, original correlation/provenance; no Actions were returned and no binding committed. Release reservation. |
| Host refuses after successful pure fold | HostRejected { creation, initialization, reason } | Current child and untouched Actions only. Release reservation. No accepted effects, readiness, birth, or restart. |
| Host commits | Established with installed evidence | Host owns initialization once; later failure does not undo this birth. |
| Expected effect rejection after accepted prefix | Still committed Established | Interpret independent later effects. EffectsRejected(EffectsRejected) report; exact accepted/rejected settlement and descendants stay with host. No public publication. Drain child. |
| Interpreter returns typed corruption | Still committed Established | EffectsRejected(InterpreterCorrupt) report; complete prefix, faulting request, untouched suffix, and runtime work remain owned. No publication. |
Initialization selects Stop, all effects accepted | Still committed Established | Settle final effects, produce exact normal stop, never publish or run ordinary ingress; InitializeWorker resolves Stopped. |
Initialization selects Stop with rejected/corrupt effects | Still committed Established | Corruption outranks rejection; either outranks stop for initialization classification. Preserve the stop decision in settlement and independent termination evidence. |
| Successful continuing initialization | Established | Record success, settle source custody, permit publication; later request can resolve ReadyForActivation once with its plan and permit. |
| Cooperative owner shutdown before child fold | No false commitment | Return untouched creation using existing environment-failure projection; retain exact cancellation disposition in runtime custody. Do not relabel it as initialization failure. |
| Cooperative owner shutdown after fold, before commitment | HostRejected | Return current child and untouched Actions with environment-failure projection, retaining cancellation disposition downstream. |
| Shutdown after commitment, before publication | Committed birth remains true | Close publication/ingress, settle or retain accepted work, then exact cancellation/stop and complete retirement. Return plan/report if source has closed. |
| Child panics after commitment, before publication | Committed birth remains true | Authoritative panicked termination; no readiness or public endpoint. Preserve extant host-owned custody; apply the panic limits below. |
A committed replacement is a fresh birth explicitly designated as replacement. Only commitment authorizes a creation-level restart diagnostic. Successful service replacement still requires the proxy's existing ready outcome. Neither an attempted replacement nor an uncommitted failure produces successful restart.
6.4 Pure-initialization panic and cancellation limits
A panic in the pure initialization fold is not C::Error, not an accepted
initialization, and not HostRejected with invented empty Actions. The current
creation sum lacks that distinction. The selected minimal extension is
ChildCreationOutcome::InitializationPanicked { creation }, retaining the
current routed child, ID, route, and kind. Mirror it as
WorkerCreationRejection::WorkerPanicked { worker } when settling a worker;
proxy creation retains the original generic settlement. It grants no actor
capability, retry permission, or guarantee that a partially mutated child is
safe to run again. It is a creation outcome, not a new aggregate control state.
Write a focused external panic witness before adding these variants. Bombay must catch the pure fold while the current child remains in an outer owned slot, release its reservation, and return this outcome. Do not downcast, erase, or put a panic payload in Behavior. Any exact runtime panic evidence belongs to the existing runtime termination reporting contract. Reconcile exhaustive creation matching and both public rustdocs in the same design stage.
Pure initialization is synchronous. Cooperative cancellation is observed before or after it, not by inventing a half-completed successful Actions value. During asynchronous effect interpretation, cancellation must preserve an already-owned transfer until it settles or is retained by the existing custodian. Aborting a task or dropping a future that owns the sole payload is not successful custody transfer.
The guarantee has a necessary limit: arbitrary Rust code can move an input
into a local and panic, destroying that value during unwind. Neither a new enum
nor catch_unwind can reconstruct it. Process abort and forced destruction of
the owner likewise cannot promise recovery. Tests must distinguish (a) typed
rejection/corruption with complete return, (b) cooperative shutdown with retained
work, and (c) panic with truthful termination and recovery of extant outer
custody. Never assert recovery of a payload already destroyed by arbitrary
panicking user code. Panic after a transfer must not fabricate the original
request or report successful receipt. If a runtime path loses extant custody
merely by where it stores its task locals, repair that runtime path.
6.5 InitializeWorker is settlement observation
Accepting this request transfers the original plan to the already committed
child's host. The host observes its actual initialization result, then calls
resolve once. It must never invoke initialize, replay effects, or create a
replacement child to satisfy this request.
| Available evidence when the request is resolved | Report |
|---|---|
| Initialization corrupt | EffectsRejected(InterpreterCorrupt) |
| Initialization rejected/blocked | EffectsRejected(EffectsRejected) |
| No initialization failure, exact termination already known | Stopped with that exact stop |
| Successful continuing initialization, no terminal evidence yet | ReadyForActivation with original plan and one permit |
| Initialization still executing | Keep the request/plan owned until an outcome is known; do not fabricate success |
Failure evidence takes priority over a simultaneous stop. If readiness was already issued and termination arrives later, keep both facts; do not retract or issue a second initialization report. Ordinary observation and initialization observation are independent consumers of the same execution evidence. Neither may steal the other's notification. Store evidence in the existing typed child host, scoped to its concrete endpoint and retirement lifetime, not in a global cache or protocol registry.
A request arriving after child termination must still settle using retained
host evidence. Closed source admission returns the complete report, including
plan or permit, to retirement alongside the child's settlement. Missing typed
host support is InterpreterFault::MissingCapability with the untouched
request, not successful Stopped, empty evidence, or a fabricated mailbox
rejection. Duplicate/foreign reports remain subject to the existing aggregate
correlation rules and must not issue another activation permit.
7. Repository-wide impact and simplification
| Surface | Required work | What must remain unchanged in meaning |
|---|---|---|
behavior/src/effects/sending.rs | Accepted retention declaration; complete vector residual custody | SourceActions transfer, interpretation order and static result shape |
behavior/src/effects/actions.rs | Verify creation/send custody joins; change only a failing join | Current pending creation-custody fixes and exact step |
behavior/src/actor/creation.rs | Precise commitment docs; pure-fold panic outcome | Freshness, occurrence, ordered batches, owned failures |
behavior/src/{lib.rs,effects/mod.rs} | Curate only necessary public exports/docs | No runtime dependency, umbrella request trait, or new user syntax |
actors/src/atomic/pool/assignment.rs | Consuming settle and removal of independent receipt assembly | Customer/job conservation, completion authority, retry order |
actors/src/atomic/stable_proxy/operation.rs | Consuming settle and private receipt construction; narrow interpreter seam | Source, operation, creation, control, exact endpoint |
actors/src/atomic/diagnostic.rs | Declare retained versus discharged acceptance | Route-free terminal policy does not stop the actor |
actors/src/atomic/worker/{mod.rs,initialization.rs} | Panic rejection mapping; settlement-observation documentation | Existing activation plan/permit and failure classification |
actors/src/{lib.rs,atomic/mod.rs,atomic/stable_proxy/mod.rs} | Curate the actual interpreter port and remove superseded exports | Small application surface; no duplicate aliases for settlement mechanics |
Atomic proxy, pool, supervisor consumers and proxy_creation.rs | Exhaustive panic outcome/custody handling after focused proof | No new aggregate engine, policy, or public constructor inputs |
actors/src/send_product.rs, macros, handwritten equivalents | Prove identical retention, order, and source progression | Named products and inferred application syntax |
| Testkit, integration tests, compile fixtures, fuzz targets | Add external ownership oracles and changed-sequence coverage | Pure models stay independent of interpreter implementation |
| Canonical docs and five actor catalogue/law documents | Reconcile commitment, source-free custody, panic, publication | Distinct template policies and cardinalities |
| Bombay Driver | Continuing-turn/mixed-custody regressions; remove publication on failed or stopping initialization | One generic driver; no diagnostic/proxy dispatch |
| Bombay local/launch/application runtime/child bindings/terminal projection | Early private commitment, startup evidence, real leaf interpretation, full failure custody | Actual delivery/tasks/observation owned by runtime |
| Address | Exclusive hidden reservation, promotion, exact-generation release | One registration authority, no competing Bombay registry |
Before migration, enumerate exhaustive creation consumers with:
rg -l 'ChildCreationOutcome::|WorkerCreationRejection::' crates --glob '*.rs'
Current production matches include atomic/pool/worker.rs,
atomic/stable_proxy/worker/mod.rs, atomic/proxy_creation.rs,
atomic/worker/mod.rs, atomic/fifo_pool/mod.rs,
atomic/keyed_pool/{mod.rs,shutdown.rs}, and core creation dispatch.
The same search includes integration tests, benchmarks, and fuzz targets; keep
those in the migration ledger rather than treating a library-only build as
complete. Paths prefixed atomic/ here are under crates/actors/src/.
Related request families must be checked: ordinary logical/exact deliveries,
CustomerDelivery, PrepareWorkers, BeginActivation, observation, shutdown,
parent reporting, timers, and RetirementBirths. They do not automatically need
new APIs. BeginActivation must respect the existing started-before-plan-poll
contract in the witness; no new activation framework is in scope.
The proposed reduction is specific: remove downstream envelope reconstruction, public independent receipt assembly, duplicate initialization execution, and startup panic/empty-residual assumptions. Keep one total settlement algebra, one source-custody traversal, and one Driver retirement path. Do not call the work code reduction if its measured production delta is positive.
After the blockers pass, a separate consolidation stage may compare repeated named-product traversal or request transfer code. A merge requires identical ownership, ordering, error, source, and retirement equations plus deletion of the competing mechanism. Similar field names and matching branch counts are insufficient. The five aggregate state machines are not a preapproved target.
8. External interpreter fixture
Add an isolated downstream Cargo fixture at
tests/interpreter-contract/, with its own [workspace] and lockfile. It must
not become a production workspace member or pull Bombay/Tokio into core. The
fixture depends on the current candidate Behavior, Actors, Bombay Engine,
Bombay, Address, and Communication graph. During development explicit path
patches are permitted and recorded; CI/release uses immutable compatible
revisions. A script rejects duplicate Behavior versions and mismatched sources
using Cargo metadata. Do not accidentally test registry Behavior inside Driver
against a different local Behavior in the fixture.
Use public exports only. Implement narrow real capabilities in this fixture
where production leaves are still absent; final acceptance also runs through
Bombay's completed production leaves. No cfg(test) access, test-only public
constructors, copied Driver, fake task host, erased actor, or manually successful
host receipt may substitute for that final run.
Required fixture files: Cargo.toml, Cargo.lock, README.md, test modules
delivery.rs, custody.rs, startup.rs, shutdown.rs, and compile fixtures
under tests/compile/{pass,fail}/. Group shared concrete test protocols in one
support module only when they own real common protocol definitions. Use domain
names in source; requirement IDs below belong only in documentation/manifests.
Use non-Clone, non-Copy plans, definitions, diagnostics, and completion
authority. FIFO/keyed jobs implement Clone under their existing retry law;
record the original and transferred allocations separately and require exact
return of the retained original on rejection. Get requests from real
FIFO/keyed/supervisor transitions. A second
same-typed request provides a substitution adversary. Observe payload identity
through owned unique values and final recovery, not invented IDs guessed from
sequence arithmetic. An independent drop ledger may instrument payload lifetime
but cannot supply authority or reconstruct missing payloads.
Deterministic control gates force close-after-resolution, settle-before-stop, stop-before-report, and source-close races. No sleeps or probabilistic timing. At each observable cut, each issued value is in exactly one allowed owner: aggregate, pending request, accepted destination, returned rejection/report, active runtime work, or terminal custodian. Final explicit discharge is recorded separately. Count conservation after every event, not just at shutdown.
9. Mandatory acceptance matrix
All rows run in debug and optimized builds where executable. Compile failures must fail for the named ownership violation, not unrelated missing bounds.
| ID | Required scenario and oracle |
|---|---|
| T01 | Accepted assignment reaches real exact worker once; original receipt returns to source; completion is still separate. |
| T02 | Closed assignment and close-after-resolution return the exact original FIFO job, target, move-only completion authority, and original correlation. Settlement makes no copy; the pool's documented retry policy already cloned the worker payload. Source may recover/retry under that law. |
| T03 | Two same-typed assignment requests settle in either order without receipt exchange; external assembly and double settlement fail to compile. |
| T04 | Proxy start, replacement, and shutdown each use real private-control admission; accepted receipt identifies original operation, creation, and exact proxy. |
| T05 | Proxy missing binding, closed control, and close race return complete definition/plan/control and original operation/source. Wrong occurrence and forged receipt fail to compile. |
| T06 | Emit terminal diagnostic on a continuing turn, process at least three later turns, then retire; recover the exact non-cloneable diagnostic once. No premature drop. |
| T07 | Many successful ordinary receipts and delivered diagnostics leave no retained receipt history. A routed diagnostic rejection remains complete in terminal custody. |
| T08 | Mixed product: retained acceptance, source action, creation receipt, independent request. Both legal wrapper orders preserve order; source admission progresses; closure retains every remaining lane. |
| T09 | Typed rejection at every position still attempts independent later items. Corruption at every position preserves exact prefix, fault, untouched suffix. Source-free rejected/blocked values survive continuation. |
| T10 | Pure initialization rejection returns current move-only child and exact error, no actions, binding, publication, or successful restart. |
| T11 | Reservation/host refusal returns the appropriate untouched creation or current child plus complete untouched Actions. No effect is attempted. |
| T12 | Initialization accepts one effect, rejects the next, accepts an independent later effect; no rollback, no reconstructed Actions, no publication; complete runtime settlement survives to root. |
| T13 | Initialization accepts a prefix then reports corruption; suffix remains untouched and owned. Ready/activation cannot be manufactured. |
| T14 | Initialization stops with final effects; effects settle once, no ordinary event runs, no endpoint becomes public, and exact stop reaches observation and initialization consumers. Include stop plus rejection and corruption. |
| T15 | Initialization succeeds once; InitializeWorker arrives before and after completion and after termination. Correct report/plan/permit each time; no second initialization or activation authority. |
| T16 | Panic during pure initialization returns typed uncommitted panic outcome and extant child custody. Panic after commitment is exact termination, not creation rejection or a parent panic. Test stated unwind limits explicitly. |
| T17 | Cooperative cancellation before fold, after fold, during an in-flight effect, after commitment, and just before publication retains actual ownership and prevents false success. |
| T18 | Termination and initialization report arrive in both orders; independent observer still receives exact terminal evidence. Parent closes before each admission; full values reach root. |
| T19 | Nested child startup and early private reports progress with bounded mailbox capacity; no parent/child publication deadlock. Public logical self-send while unpublished has the documented rejection. |
| T20 | Address reserve/competing claim/resolve/publish/release races, registration exhaustion, and stale retirement preserve exclusivity, hidden visibility, and exact generation. |
| T21 | Shutdown during assignment/proxy/initialization/activation preserves already-admitted work and pending reports/plans through the real retirement barrier. Never-ready work remains explicitly owned; cancellation is not fabricated completion. |
| T22 | Ordinary creating actor and an atomic owner both work; FIFO plus fixed supervisor provide unrelated template witnesses. Test both Watch<ReceiveTimeout<B>> and ReceiveTimeout<Watch<B>> with meaningful observation and timing policies. |
| T23 | Fixed/dynamic supervision, proxy replacement, FIFO/keyed work execute through public Bombay applications after leaf integration. Acceptance cannot masquerade as ready/completed/restarted. |
| T24 | Handwritten/generated equivalent products, heterogeneous child occurrences, source closure and RetirementBirths preserve identical custody without structural application syntax. |
Sensitivity proof: restore or simulate each original defect in an isolated
candidate and demonstrate the corresponding test fails for that law. T06 must
fail on the current unconditional Exhausted implementation even though
immediate-stop tests may pass. T02/T05 must reach actual rejection after payload
transfer; privacy-only compilation is insufficient.
Extend existing independent models and sequence targets for changed inputs,
including fifo_pool_sequences, keyed_assignment_sequences,
keyed_binding_sequences, and relevant lifecycle/catalogue sequences. Use
Address's concurrency verification for reservation interleavings. Do not copy
implementation matches into an alleged independent oracle.
10. Ordered implementation work packages
Each package starts with law, external syntax, failing regression, and reuse/ deletion ledger before production. Design and mechanical migration are separate. Do not silently make an unresolved design choice during migration.
| Package | Work and prerequisites | Completion evidence |
|---|---|---|
| P0 — Baseline and red witnesses | Pin merged/current trees, snapshot dirty changes separately, inventory every affected symbol, create external fixture and dependency-source check. | Current-source D1–D7 evidence; focused failing ownership/retention/startup tests; exact anticipated change ledger. |
| P1 — Retained acceptance | Implement section 5 in core and diagnostic owner. Prove source-free failures and mixed product composition. | T06–T09, T22/T24 focused witnesses; no diagnostic-specific Driver logic or ordinary receipt accumulation. |
| P2 — Complete delivery | Implement section 4, assignment first and proxy second; compare existing ports before publishing the proxy seam. Narrow old assembly API only after real witnesses pass. | T01–T05; two same-typed concurrent requests; privacy, move, source and occurrence failures. |
| P3 — Startup ownership proof | Establish Address reservation and private child commitment witness; add pure-initialization panic outcome and smallest ordinary/atomic consumers; specify root unpublished return concretely. | T10–T20; one ownership trace for every row in section 6; no consumed Actions in HostRejected. |
| P4 — Current caller migration | Only after P1–P3 prove APIs, mechanically update all consumers, generated/handwritten products, tests, docs and runtime leaves. | No new abstraction discovered; fixed/dynamic/proxy/FIFO/keyed checks and public syntax preserved. Discovery of new semantic plumbing reopens its design package. |
| P5 — Integrated runtime and release | Complete actual Bombay leaf interpreters using proven contracts; run shutdown/activation races and all required gates; select immutable compatible dependency graph. | T01–T24 on production runtime; complete terminal ownership at root; reproducible versions and command logs. |
| P6 — Optional consolidation | Separate follow-up after blockers are closed. Compare complete ownership equations and delete demonstrably repeated machinery. | Independent law proof and measured deletion; no promise that the entire catalogue or Bombay runtime can be collapsed. |
No package may claim complete integration from a local path-patched compile. P5 requires an immutable candidate graph and a coordinated release/pin plan. Version according to the actual API break; do not assume narrowing constructors or extending exhaustive public sums is patch-compatible. Release publication itself is separate from this documentation deliverable.
Required verification commands
Use the pinned toolchain through nix develop -c when Cargo is absent from the
shell. In the Behavior repository:
nix develop -c cargo nextest run --workspace
nix develop -c cargo test --workspace --doc
nix flake check
The fixture must document and run:
nix develop -c cargo test --locked --manifest-path tests/interpreter-contract/Cargo.toml
nix develop -c cargo test --locked --release --manifest-path tests/interpreter-contract/Cargo.toml
Wire fixture execution into the authoritative CI gate; an isolated workspace
is not covered by --workspace. Run applicable Bombay and Address gates too.
Activate the three currently ignored Bombay local-publication regressions.
Preserve absence while effects are pending and after rejection/corruption.
The current successful test expects visibility after activate but before
publish; move that successful visibility assertion to the explicit publication
milestone selected here and add an absence check before publication. Record this
intentional test-contract correction; never weaken the pending/failure checks.
Log actual command outcomes and environmental failures separately. Focused
proof comes before broad migration and repeated full-suite runs.
11. Architecture checkpoints and budgets
The target adds zero aggregate control states. The core acceptance method adds no new public type. The proxy port is at most one new interpreter trait; Address reservation is its own capability. The pure-fold panic extension adds variants to existing creation/rejection sums, not a universal startup framework. These are proposed limits, not measured implementation success.
Current aggregate top-level control sums relevant to review:
- Proxy:
Dormant,Starting,Ready,EmptyInitial,EmptyAfter,Replacing,ShuttingDown,Stopped(8). - FIFO:
Constructed,Operating,Draining,Stopped,ForcedRetirement(5). - Keyed pool:
Constructed,Operating,Retiring,Stopped,ForcedRetirement(5).
Do not pretend those counts measure fixed/dynamic state held in their owned member/entry products. Before each semantic experiment, write the complete control sum and every subordinate alternative for the aggregate actually changed. Record the exact current value each surviving alternative owns. A protocol result is not an additional aggregate state or dispatcher.
Every retained batch must have this record, before and after:
| Required measurement | Required explanation |
|---|---|
| Aggregate control states | Complete domain sum, not just number |
| Subordinate state and result alternatives | Every retained value and the future decision needing it |
| Transition branches | Same counting method before/after; report measurement method |
| Production lines and modules | Separate production from inline/integration tests and generated code |
| Public spellings/types/traits/variants | List additions and removals, including doc-hidden exports |
| Residue scan | Arrival history, repeated causes, false cardinality, nested transition authority, semantic booleans, structural user syntax |
| Contract cross-check | Canonical runtime/transition/layer contracts and all five normalized actor laws |
| Provenance | Each new symbol linked to pre-edit law, failing regression, real consumers, and deleted/reused machinery |
| Disposition | pass or reopen; missing measurements mean reopen |
The required cross-check documents are runtime settlement, transition algebra, layer laws, and the normalized proxy, fixed supervisor, dynamic supervisor, FIFO pool, and keyed pool laws. Review their ownership equations; historical representation names are not instructions to reintroduce removed machinery.
On reopen, remove only that experiment's production representation, record
its falsifier in DEAD_ENDS.md, and return to the last retained design. Never
revert pending user work. No blanket bounds, new no-op policies, hidden
compatibility wrappers, default generics, erased futures, or visibility fixes
may be introduced to silence compiler errors.
Expected Behavior design owners: the core sending/creation files, assignment, proxy operation, diagnostic, worker creation, their curated export surfaces, and focused tests. Runtime owners: local host, launch, child bindings, application capabilities, terminal projection, and Address registration. The implementation ledger must name the exact files and estimated deltas before editing; this cross-repository scope will likely cross the repository's review thresholds and must be presented as such.
Per AGENTS.md, more than 15 changed files, more than 500 net new production
lines, or more than three new public types requires explicit expanded-scope
authorization before further production edits. Count cumulatively across
packages; do not reset counts per commit. Report unrelated pre-existing changes
separately. This PRD does not waive that rule or mark an experiment retained.
12. Definition of done and prohibited shortcuts
The blockers are complete only when:
- All T01–T24 rows have concrete evidence against one candidate dependency graph; external compile and real runtime results are reported separately.
- Every ownership row in sections 4–6 has an executable witness and a truthful public contract; no startup outcome hits a known unconditional panic branch.
- Generic custody retains requested evidence across continuing turns and retires it once; discharged receipts do not accumulate.
- Initialization occurs once, creation commitment is explicit, and unpublished failure returns real custody instead of pre-interpretation fiction.
- Complete source/API/doc migration and required gates pass; ignored tests or temporary source patches are not counted as completion.
- Each retained batch passes the drift/provenance checkpoint and reports its actual capability additions separately from deletions.
Reject any patch that makes reconstruction fields public, clones jobs to recover rejection, stores all receipts forever, treats diagnostics specially in Driver, reinitializes a worker, converts consumed effects into empty Actions, conflates admission with completion, discards startup residuals after logging, or moves runtime mechanics into pure Behavior. Also reject arbitrary state-machine unification justified solely by hoped-for line reduction.
13. Review evidence and reproducibility
The review traced current core action/item/creation composition, assignment and proxy request ownership, diagnostics, worker startup, proxy/pool/supervisor consumers, named send-product custody generation, testkit limitations, canonical and normalized laws, and adjacent Bombay launch/Driver/host interpretation. This is a holistic review of these contracts, not a claim that every unrelated catalogue branch was executed or proved redundant.
Current physical source inventory (includes inline tests/comments, so these are review-surface counts rather than production-only budgets):
| Crate | src Rust files / lines | tests Rust files / lines |
|---|---|---|
| Behavior | 10 / 6,764 | 15 / 3,796 |
| Actors | 125 / 57,860 | 44 / 35,604 |
| Macros | 1 / 1,499 | 10 / 255 |
| Testkit | 2 / 173 | 31 / 8,112 |
| Mutation gate | 1 / 612 | 0 / 0 |
Nested fuzz targets, benchmarks, and nested fixture workspaces are additional verification surfaces, not included in this table.
Current-source SHA-256 fingerprints, captured before this document revision:
File under crates/ | SHA-256 |
|---|---|
behavior/src/effects/sending.rs | a768908396a6878be079290c4dda02b39297449ad34f3e48c349880dacc6c468 |
behavior/src/effects/actions.rs | 189cb846f51c0b07d38789cf1bca5b291b73e86c58b9428063ab9bf61db78ae9 |
behavior/src/actor/creation.rs | 9d490247c4e1e3b7252643bb262249fadf14c82b774d09290d31259127a7b181 |
actors/src/atomic/pool/assignment.rs | 719e30c321f234ebbb1777c3c4fcb3c547a0a54111b4556a1564732972c14903 |
actors/src/atomic/stable_proxy/operation.rs | 55fdb78ee174ce1b772fb7fd179600004411c3b07a2aa505207fd22e0a175df5 |
actors/src/atomic/diagnostic.rs | ef8f9f873284db7a534ad6536a77cd5a675f91e963b56674aa7f17fa2fe85b09 |
actors/src/atomic/worker/initialization.rs | 09487c21e7368911242aceaf202fa81f4627037bd6f21a9f76bb56e42a46a3f6 |
The adjacent downstream record already contains debug/optimized diagnostic reproductions and the ignored visibility failure. Those are prior evidence, not a claim this review reran the full runtime.
This review also compiled a disposable external Cargo crate with path
dependencies on the current Behavior and Actors source, using Cargo 1.95.0.
Its non-cloneable Diagnostic(Box<str>) was placed in
Vec<ActionItemResult<DiagnosticAction<Infallible, Diagnostic>>> as
Accepted(DiagnosticAccepted::Terminal(value)). Calling the public
SourceSettlementCustody::<(), ()>::offer_next_to_source returned Exhausted.
The test demanding Retained failed with exactly
terminal diagnostic was classified as exhausted in the debug build.
This verifies D3 on the current code; it is not the T06 real-Driver witness.
The disposable probe is outside the repository and is not a retained test.
Documentation validation: mdbook build docs passed, the four published-doc
checker unit tests passed, the rustdoc-import scan passed, and acceptance-ID,
work-package, navigation, and diff-whitespace checks passed. Full runtime
verification of the proposed implementation remains P1–P5 work.
The reviewer also started nix develop -c cargo nextest run --workspace,
nix flake check, and an optimized build of the disposable diagnostic probe.
These did not produce final test verdicts during the review and were explicitly
interrupted by the reviewer. They are unverified, not passing checks and not
demonstrated code failures. The standalone rustdoc-error-code script also could
not run outside the Nix environment because Cargo was absent from that shell;
its authoritative flake invocation is included in the unverified full gate.
The optimized diagnostic failure recorded in the adjacent downstream review
must not be presented as a newly completed optimized run against this source.
Task-attributable implementation delta for this PRD: production +0 / -0 / net 0; retained tests +0 / -0 / net 0; public types +0 / -0. No production
experiment is marked retained by this documentation change.
Interpreter contract implementation ledger
This ledger records implementation evidence against the separately maintained ownership and startup PRD. It does not change that specification or treat a scripted host as a production interpreter.
Merged Behavior candidate, 2026-09-30
The release branch merged current main at 835cf8d into the repaired
Behavior candidate at bd613ae. The merge retained both ordinary direct
dependency and facade-first Actors macro witnesses. Its five macro-resolution
fixtures passed, followed by all ten local aarch64-darwin Nix checks and the
external interpreter fixture in debug and optimized builds. The user's revised
PRD was copied from the original worktree into this release branch, then T02
was clarified to preserve FIFO's existing clone-and-retry law. The original
uncommitted file remains untouched.
The external workspace now has a README and a Cargo-metadata guard. The guard
requires one local resolved package for each of core Behavior, Actors, and
macros, rejecting a duplicate or registry copy of these crates. It passed on
the merged candidate. It does not assert that the later Bombay/Address graph
is immutable or integrated. The retained-diagnostic witness now carries one
non-cloneable value across three later source-free offers, adding a discharged
diagnostic on each offer; each resulting residual contains only the original
terminal value. Debug and optimized tests pass. This strengthens the
Behavior-side T06/T07 law, while the real Driver retirement remains P5.
The precommit creation fixture also now rejects pure initialization with a
non-cloneable child and error, verifies that into_actor refuses to issue an
established capability, and returns the exact current child, route, ID, kind,
and error allocation. Debug and optimized tests pass. This is the Behavior
ownership shape for T10; the real reservation and publication trace remains
downstream.
The external mixed-product fixture now combines retirement creation custody,
an accepted terminal diagnostic, a live source request, and an independent
rejection. It exercises two SendLayer orders, requires source admission to
progress past retained siblings, and verifies that closed admission returns
every lane and the continuation verdict. The focused tests pass in debug and
optimized builds. This
is the Behavior-side composition portion of T08/T24, not a real Driver trace.
The canonical adapter contract was corrected to distinguish private host
commitment and EstablishedCreation::Installed from later effect settlement
and public resolution. It now states that ordinary rejection continues
independent effects while corruption owns an untouched suffix. This is a
documentation correction to the existing Behavior algebra and the PRD's
selected startup order; no runtime publication implementation is claimed.
Before/after for this evidence-only batch: aggregate control states and
subordinate alternatives unchanged; production transition branches and
production lines unchanged; one fixture README and one graph-check script
added; one CI step and three external test files changed; public spellings
unchanged.
The terminal value remains the sole future-needed residual. No arrival-history
state, duplicate cause, cardinality assumption, nested transition authority,
semantic boolean, or structural caller syntax was added. Cross-checks:
actor-transition-algebra.md, atomic-runtime-settlement.md,
behavior-layer-laws.md, and the five normalized actor-law documents.
Disposition: pass for this Behavior-side evidence batch. The PRD's real
runtime acceptance matrix is still open.
| PRD package | Behavior-side disposition | Downstream dependency |
|---|---|---|
| P0 | Current source fingerprints, red ownership regressions, isolated external fixture, and single-source graph guard recorded. | Add immutable Bombay/Address/Communication revisions to the same graph for P5. |
| P1 | Core retains only outstanding accepted values; terminal diagnostics survive repeated turns and ordinary receipts discharge; both wrapper orders and closed source return pass. | Real Driver continuation and root retirement. |
| P2 | Assignment and proxy requests consume their lower capability, keep private correlation, and return complete actual payloads on rejection; two same-typed requests and compile denials pass. | Real Communication and exact private proxy-control admission, including close races. |
| P3 | Core returns exact pure rejection, uncommitted panic, and untouched host refusal; worker panic maps to its owned rejection and public docs distinguish commitment from publication. | Address reservation, private host acknowledgement, effect settlement, termination, and public publication. |
| P4 | Behavior-owned aggregate consumers, generated/handwritten products, exports, tests, and canonical docs use the retained contract; workspace and external fixture gates pass locally. | Bombay and Address callers migrate after this crate release. |
These dispositions cover the Behavior column of the PRD's scope table. They are not claims that the cross-repository P0–P5 packages or T01–T24 runtime matrix are complete. P5 and A17 remain open until the released dependency graph runs the real leaf interpreters.
P0 snapshot, 2026-09-29
Behavior is at 605d0634644a435f4bba31ce1768253abcafe23e on
codex/repository-quality-repairs. The PRD is an uncommitted user edit and is
excluded from this snapshot. The isolated Bombay worktree is at
f462b8ec25a4cc43aabf89fe448c09e3da9d12b7 with pending local changes;
the Address checkout is at 87f7af4fd67671bbcf05c59f5da687b40ce7ba98.
Neither adjacent repository was edited for this ledger.
File under crates/ | Current SHA-256 |
|---|---|
behavior/src/effects/sending.rs | 4e8c578ae4a99383471fea3f7fc579f1c066d249505ae242fad9e47b952e09c2 |
behavior/src/effects/actions.rs | 189cb846f51c0b07d38789cf1bca5b291b73e86c58b9428063ab9bf61db78ae9 |
behavior/src/actor/creation.rs | c47b085f147ce1443f634b3527a426030ec165923c0e833b8bb1cbb0d610172f |
actors/src/atomic/pool/assignment.rs | 308f3d4a78e06be1e027a1a3410ff374a72c2eb4897bcfbc278425cc1ecbbf8c |
actors/src/atomic/stable_proxy/operation.rs | fda908e08c7a285d6b7e32555402ea9c8aac32f45f06a2dc631e1efd0b4220f3 |
actors/src/atomic/diagnostic.rs | 8a36bdb2d6f28d5279c52a131fdf999f881ec7394f81a89982e686813538f8e6 |
actors/src/atomic/worker/initialization.rs | 09487c21e7368911242aceaf202fa81f4627037bd6f21a9f76bb56e42a46a3f6 |
| PRD defect | Current-source result | Remaining proof |
|---|---|---|
| D1 assignment ownership | AssignWorker::settle consumes the request and returns its original receipt or complete request; decomposition and reconstruction are crate-private. External acceptance/rejection and compile-fail fixtures pass. | Real worker admission, two concurrent same-typed requests, and production source return. |
| D2 proxy ownership | ProxyOperation::settle uses ProxyControlAdmission; operation decomposition is test-private and the receipt constructor is private. External privacy fixtures pass. | Real start/replacement/shutdown admission and failure races. |
| D3 retained acceptance | Generic ActionItem::retain_accepted compacts discharged receipts; diagnostic ownership opts into retention. External custody fixtures pass in both profiles. | Real Driver continuation and retirement with the exact retained value. |
| D4 startup ownership | HostRejected still requires untouched initialization Actions. InitializationPanicked already exists and has an external pure-fold custody witness. | Host commitment and every post-commit failure outcome through the real runtime. |
| D5 child commitment | Isolated Bombay establish_child still awaits spawn_owned_with, which waits for publication. A pre-publication end can still reach a panic branch. | Private commitment acknowledgement and exact unpublished terminal return. |
| D6 hidden address reservation | Address now exposes try_reserve and consuming Reservation::publish; the isolated Bombay LocalEnvironment::prepare uses it. | Runtime interleavings, bounded nested progress, and immutable dependency graph. |
| D7 premature publication | The isolated Driver now calls publish only on the continuing initialization path after pending settlements progress; the early terminal branches no longer call it. | Production startup rejection/corruption/stop traces and public-resolution absence. |
The fixture at tests/interpreter-contract passed all 12 baseline test functions in
debug and optimized profiles. Its assignment host and startup panic witness
are local interpreters, so they do not close the production runtime matrix.
The fixture is already wired into .github/workflows/checks.yml in both
profiles. A clean Nix flake check at the Behavior snapshot passed eight
available aarch64-darwin checks, including 843 optimized Nextest tests.
Next ownership proof
T03 focused law before extending the fixture: two independent FIFO pools
can have same-typed worker deliveries outstanding at once, even when their
creator-local child IDs are numerically equal. Settling those deliveries in
reverse order must return each opaque accepted receipt to its originating
pool. Both pools must continue without a foreign-receipt diagnostic, and each
later worker completion must produce exactly its own customer outcome. The
existing AssignWorker::settle and FIFO receipt correlation supply the
lower-order behavior; no new production representation is proposed.
The external regression now passes in debug and optimized profiles. It uses
two separate pool instances with equal numeric child IDs, settles their
same-typed requests in reverse order, rejects any diagnostic or early customer
outcome, and checks both later completions. This is a focused T03 witness,
not a production transport witness.
T01/T02 pointer witness correction: the previous fixture recorded the
address of a Box<str> handle and compared it with the address of its heap
payload. Those different addresses could not prove job custody. Requiring
equality first made both baseline assignment tests fail, exposing the weak
assertion. Source inspection then found FifoPool requires Job: Clone and
clones the queued payload into Assignment; the pool retains the original so
it can return it if delivery fails. The corrected fixture records the worker
payload's actual heap address, requires it to differ from the original, and
requires a rejected assignment to return the original address. The concurrent
test checks both copied worker payloads in reverse settlement order. No
production symbol or actor transition changed.
The earlier PRD's T02 requirement for a move-only job is not proved by this FIFO
fixture: Box<str> implements Clone, and the current FIFO bound rules out a
non-Clone job. The full ownership equation must be revisited before claiming
T02 complete; a test using a cloneable value cannot certify a move-only law.
This is a real policy conflict, not just a missing test: FIFO's dispatch
clones queued.customer.payload into Assignment while retaining the original
in AssignedJob. On accepted delivery followed by a worker stop,
Interruption::Fail returns that original as ReturnedAssigned, and
Interruption::Retry requeues it. A single non-Clone job cannot be both
transferred to an independently executing worker and retained by the pool for
those later paths. The public pool is the only current producer of this exact
assignment request besides the similarly clone-bound keyed pool. A future
move-only witness therefore needs a changed post-acceptance return/retry law
or a distinct lawful producer; merely changing the Job: Clone bound would
make the existing transition impossible. No such policy change was made here.
The revised T02 preserves FIFO's existing clone-and-retry law. It requires a rejected delivery to return the pool's exact original job and affine completion authority without another settlement-time clone. The pointer witness proves this Behavior-side distinction; the real close-after-resolution runtime trace remains open for Bombay integration.
The remaining work is to establish the exact pre-commit versus post-commit ownership equation with a production host witness, then cover PRD T10–T21 and the catalogue composition cases. Any Behavior production-shape edit needs a focused failing law regression and aggregate-drift record first. Real Bombay changes are outside this Behavior branch while the instruction to leave that repository untouched remains in force. A local or path-patched compile does not close P5 or audit item A17.
T11 Behavior-side host-refusal custody, before fixture edit
The derived Bombay pre-commit law says a host refusal after a successful pure
initialization returns the current child and the complete untouched Actions;
no request has crossed the interpreter boundary. The existing public
ChildCreationOutcome::HostRejected already carries those values, but the
action_interpretation host-rejection case supplies Actions::cont() and
cannot detect loss of a pending send. The next external fixture will run a
move-only child's pure initialization, retain two ordered move-only sends, then
construct and decompose the public host-rejection outcome. It will check the
original allocations, route, creation ID, kind, send order, and continuation
verdict. No production type, bound, host operation, or state change is
proposed. This proves the Behavior-side ownership equation only; a real
reservation refusal and non-interpretation trace remain mandatory for T11.
The external startup_host_rejection fixture now passes in debug and
optimized profiles. OwnedText has no Clone implementation. The test checks
that pure initialization moves both pending sends into Actions, then a host
refusal returns the mutated current child, both exact send allocations in
order, the original route, creation ID, birth kind, and Continue decision. The
first version adds 116 test lines and zero production lines, types, states, branches,
modules, or public spellings. No arrival history, repeated cause, false
cardinality, nested transition authority, semantic boolean, or positional
caller syntax is introduced. Cross-checks: the actor transition algebra and
PRD section 6.3. Disposition: pass for the external Behavior-side custody
witness. T11 remains open because the fixture does not own an Address
reservation or a production child host and therefore cannot prove that a real
host attempts no effects after refusing commitment.
Creation-lane extension, before edit: the first fixture observes two sends
and Continue but leaves the creation leg empty. A host refusal must also
return a staged nested creation untouched. Extend the same fixture with one
move-only nested child, check its exact allocation, ID, and birth kind after
decomposing HostRejected, and keep the existing ordered-send assertions.
This is a test-only strengthening of the same derived law; no actor or
interpreter type changes.
The extension passes in debug and optimized profiles. Grandchild owns a
separate non-Clone value; its staged request stays in the returned creation
leg with the exact allocation, nested creation ID, and birth kind. The
fixture now has 157 test lines and checks sends, creation, and next-behavior
decision together. Production state, branches, modules, and public API remain
unchanged. The nested child is current data required for a possible later
commit, not arrival history; no duplicated cause, false cardinality, nested
aggregate authority, semantic boolean, or positional caller syntax is added.
Cross-checks remain the actor transition algebra and PRD section 6.3.
Disposition: pass for the Behavior-side complete-actions witness; T11 still
requires the real host refusal and absence of effect attempts.
Atomic actor essence and clean-room boundary (engineering record)
Status
This document defines a non-production falsification and model experiment. It is intentionally smaller than the existing atomic-actor catalogue. It may test whether a simpler template state machine and ordinary authoring surface can satisfy selected laws, but it does not authorize production actor types, change implementation status, establish catalogue parity, or permit migration.
atomic-actor-solution.md remains the production
design authority. Its foundational prototype dependency order and coverage
matrix remain binding for production implementation. If this experiment
falsifies that order or supplies a replacement law, the solution, coverage
matrix, retained-core decision, and research audit must be updated together
before any production edit uses the result.
No Rust API is selected here. Capitalized names in equations name semantic states, not authorized public types.
Why this extraction exists
The detailed documents contain important discoveries, but they currently mix four different designs:
- the irreducible actor transition algebra;
- interpreter settlement, hosting, and lifecycle mechanics;
- the identity of each reusable actor template; and
- a maximal catalogue of policies and failure handling.
That mixture makes a local template change appear to require a universal activation service, a dependency-aware action language, recursive terminal lifting, residual root ownership, typestate builders, and every final policy at once. It recreates the conditions for compiler-driven design: a compiler error in one layer appears to authorize another wrapper, path, marker, alias, or associated type in every other layer.
The clean-room experiment begins from the semantic identity of each actor and tests one independently stated policy at a time. This is an experimental ordering, not a replacement production dependency order. An omitted policy is recorded as outside that experimental slice; it is never represented by a no-op callback, dummy route, default generic, or placeholder variant. A slice that omits a foundational solution law cannot count as implementation evidence for any coverage row that depends on that law.
Authority labels
Every law used by the experiment is labelled with one of these authorities:
- Actor-model law — required by the actor semantics used by Bombay.
- Bombay derivation — a typed construction used to realize an actor-model law without effects inside the behavior fold.
- Template law — the defining contract of one reusable actor.
- Policy choice — one deliberately selected answer where actor research does not prescribe a result.
The primary actor reference is Agha, Mason, Smith, and Talcott,
“A Foundation for Actor Computation”.
Its actor language identifies asynchronous send, fresh actor creation, and
become as coordination primitives, and makes an actor's current behavior a
deterministic function of the messages it has received. Agha's
1986 monograph is the
foundational source for the open, dynamically reconfigurable actor model.
Those sources do not define supervisors, restart strategies, worker pools, readiness handshakes, delivery-rejection APIs, shutdown deadlines, fluent builders, or Rust effect products. Those are Bombay template or policy laws and must be presented as such.
Irreducible actor boundary
The clean-room experiment evaluates templates through the repository's locked equation:
initialize : Behavior -> Transition
receive : (Behavior, one Communication) -> Transition
Transition = Communications
* FreshCreations
* (NextBehavior | Stop)
This means:
- Actor-model law: an actor reacts using only its local behavior and one received communication.
- Actor-model law: a reaction may send communications to known actors, create fresh actors, and designate its next behavior.
- Bombay derivation:
Behavioris a deterministic, directly testable fold andActionsis the explicit typed representation of those consequences. - Bombay policy: initialization is a separate fold interpreted before the first mailbox communication.
- Bombay policy: normal termination and controlled transition rejection are explicit results even though they are not part of Agha's primitive three-operation presentation.
The executable prototype must implement the real Behavior trait and return
the real Actions type from crates/behavior. A smaller local algebra may be
used only as an independent model oracle whose vocabulary and transitions are
kept separate from the executable prototype. Passing against a local algebra
is not evidence that a template composes with Bombay's actor contract or that
the interpreter can realize its effects.
Scheduling, allocation, endpoint installation, clocks, observation, transport, and effect settlement are interpreter operations. The fold may request them through a typed effect, or later receive a typed fact about them, but it may not perform them.
Actions is not a transactional promise across actors. Atomicity means that
one local fold chooses one next state and one complete effect value. It does
not mean that several messages are delivered atomically, that emitted effects
succeed, or that a later rejection rolls back the source transition.
What makes a template atomic
An atomic actor template is one standalone behavior with:
- one coherent domain responsibility;
- one private exhaustive state sum;
- one closed input sum containing public commands and exact runtime facts;
- one explicit
Actionsproduct containing only effects that actor can emit; - one initialization transition;
- total transitions for every state/input pair; and
- no dependency on another wrapper to complete its own lifecycle law.
An atomic template may create another atomic actor. That is topology, not
behavioral decomposition. For example, a fixed supervisor may own stable
proxies, but its supervision decision is still one direct fold; it is not a
stack of Supervise, relay, fleet, and positional-ingress wrappers.
A generic wrapper remains appropriate only for a genuinely orthogonal same-actor transformation such as stashing or a receive timeout. Adding a wrapper must not be necessary to understand a supervisor or pool's internal ownership.
The common semantic spine
The templates share laws, not a universal runtime state machine.
Ownership follows the phase
Every owned value has one visible owner in each phase. The important boundary is emission of an action:
ActorOwns(value)
-> action not emitted: actor can reject and return value
-> action emitted: action/interpreter owns value
-> settlement fact: actor owns the returned result or exact capability
A cancellation or rejection may return a worker definition, job, result, or request only if that phase still owns it. No later state reconstructs the value from an id, clones it to escape ownership, or claims to return a value already moved into an action.
Creation is fresh and staged
- Actor-model law: actor creation allocates a fresh actor. Changing the behavior of an existing actor is a different operation.
- Bombay derivation: a behavior emits a staged create request correlated by a creator-local value, then receives an accepted or rejected creation fact.
- Bombay policy: the accepted fact carries the exact installed capability needed by the creator. A correlation value is not an actor identity and a collision is rejection, never replacement.
- Template law: replacement means creating a fresh successor and carrying explicit predecessor provenance. It never means overwriting an address.
The minimal lifecycle equation is:
OwnedDefinition
-> CreationEmitted
-> Installed(exact incarnation) | CreationRejected(complete request)
Installed
-> Ready(exact incarnation) when the template requires readiness
-> Stopping(exact incarnation)
-> Stopped(exact terminal fact)
Installation and readiness remain different facts. The first experiment may attempt to express readiness through ordinary typed protocol composition, but it must prove the complete authority chain:
- which exact capability authorizes production of
Ready; - which actor or interpreter owns that capability in every phase;
- how
Readyis correlated to the exact installed incarnation and one non-reused activation attempt or generation; - which explicit
Actionseffect initiates asynchronous activation work; - who owns the activation plan and exact incarnation while that work is in flight;
- how accepted, rejected, stopped, cancelled, stale, duplicate, and late outcomes settle; and
- why application code and an unrelated worker cannot forge readiness for the incarnation.
Injecting a typed Ready value directly from a test is model input only. It
does not falsify the need for an activation capability or demonstrate an
interpreter path. A universal activation plan, permit, task, authorization
ticket, or hydration service remains unselected by this experiment until the
ordinary-protocol attempt either proves this complete chain or fails with a
focused compile/interpreter witness. The production solution's existing
activation blocker remains authoritative meanwhile.
Correlation and provenance are exact
Facts are accepted only for the exact outstanding operation and incarnation. Role, key, address reuse, sequence adjacency, or arrival time cannot substitute for provenance. Stale, duplicate, foreign, and contradictory facts are explicit outcomes that preserve their owned data and leave valid state unchanged.
Private operation, incarnation, timer, assignment, job, completion, and binding correlations may be distinct types. Their constructors belong to the actor or test interpreter. A type is added only when it prevents a demonstrated invalid exchange; “the compiler needs a different name” is not evidence.
Acceptance, realization, and termination are separate
These facts must never collapse:
request admitted
effect accepted by interpreter
actor installed
actor ready
operation completed
outcome delivery attempted
actor terminated
Each template uses only the distinctions observable in its contract. A
dynamic StartAccepted does not mean Started; a pool Accepted does not mean
Completed; emitting a lifecycle report does not mean its destination
received it; and a terminal report is not proof that its source has stopped.
Shutdown closes ownership
Shutdown is a typed input, not ambient cancellation. It closes new admission, extracts or returns values that cannot complete, and resolves every actor or creation still owned by the template before stopping. Duplicate shutdown does not duplicate effects.
The initial experiment selects one complete policy: wait for all owned actor facts. Deadline retirement, forced transfer, uncancellable external work, and residual root-run ownership remain a separate interpreter experiment. They are not encoded as flags or dummy variants in each template.
Retained state is bounded by a domain rule
Every table, queue, correlation set, and tombstone family needs a capacity or retirement law. “Keep it forever for stale detection” is not accepted. Old facts are made harmless with non-reused correlation and generation evidence, not unbounded historical state.
The five template identities
The following table is the catalogue essence. Policies may enrich a row, but they may not change the row's responsibility.
| Template | Unique responsibility | State it must own | Defining invariant |
|---|---|---|---|
| Stable proxy | Preserve one public service identity across fresh worker incarnations | zero or one exact worker incarnation, one install/replace operation, readiness, and drain | Only the exact ready current incarnation receives service commands; replacement never reuses an actor identity. |
| Fixed supervisor | Preserve an ordered, declared role topology under a selected recovery policy | one member state per declared role, exact stable-proxy ownership, and one recovery decision | One immutable topology snapshot prepares admission; its membership partition, correlations, prepared ownership, and budget commit together, while participant realization resolves independently. |
| Dynamic supervisor | Manage a bounded set of keyed stable services through explicit operations | keyed entries, fresh entry generations, operation ownership, exact stable proxies, and retirement | A key is management identity only; reuse after retirement creates a fresh generation and old facts cannot affect it. |
| FIFO pool | Own a fixed worker set and one customer obligation for each admitted job | worker lifecycle, FIFO backlog, active assignments, completion authority, and customer routes | No job is both queued and assigned; each admitted job has at most one terminal customer outcome; older serviceable work cannot coexist with eligible idle capacity. |
| Keyed pool | Add stable future-admission affinity to direct worker ownership | independent direct-worker lifecycle, one obligation per accepted job, bounded key-to-role bindings, and per-role queues | Selection is retained at admission; rebalance/unbind changes future admission only and never retargets accepted work. There is no global backlog or worker-selection cursor. |
Stable proxy
The proxy is the first template because both supervisors depend on its one atomic report boundary. Its minimum complete contract covers:
- empty construction;
- initial installation;
- exact readiness before routing;
- command return while unavailable;
- spontaneous worker stop;
- replacement by fresh creation;
- overlap, stale fact, and creation rejection; and
- shutdown during every live phase.
The owner receives one flat report sum. It does not reconstruct readiness from separate creation and stop observations. Service clients cannot address the owner-control protocol.
Fixed supervisor
The fixed supervisor owns semantic roles and stable proxies. It does not own application children and it does not convert a role into a nonce or address. Recovery eligibility, strategy, budget, and release timing are separate policy dimensions. They should first be implemented as local, exhaustive values in the fixed supervisor. A shared recovery abstraction is extracted only after a second direct actor demonstrates the identical transition law and the extraction deletes code.
The essential recovery transaction is:
observe exact terminal fact
-> classify eligibility
-> select roles from an immutable ordered snapshot
-> prepare every replacement and fallible correlation
-> admit or reject the complete decision
-> commit member states and Actions once
Dynamic supervisor
The dynamic supervisor is not a fixed supervisor with an optional role list.
It owns bounded keyed membership and explicit Start, Replace, Stop,
Query, and retirement operations. Request replies describe admission; a
separate durable route, if selected by the experiment, owns later lifecycle
facts. Cancellation is added only with an exhaustive ownership table showing
which phases still own and can return the submitted worker.
No fixed factory, restart strategy, restart budget, or no-op policy belongs in this template.
FIFO pool
The pool creates and observes workers directly. A stable proxy is unnecessary because customers address the stable pool, not individual workers.
The pool retains the customer destination and issues opaque completion authority with each assignment. The worker may consume an assignment to form a completion, but cannot choose or substitute the customer. Completion must match both the issued authority and exact worker incarnation.
Retry, if selected, is explicitly at-least-once execution. The pool retains the canonical customer obligation and a retryable payload while the worker owns an execution value. This is one semantic obligation represented by multiple Rust values, not an exactly-once side-effect claim.
Keyed pool
The keyed pool is a separate direct fold sharing only proven value laws with FIFO. A submitted key is mapped to a semantic role by one concrete selector; the selected role and fresh binding generation are retained as opaque admitted evidence with accepted work, while the binding alone owns the key. Per-role backlog and binding capacity are different bounds. Keyed pooling has no global backlog, circular worker cursor, or cross-worker serviceability law.
Rebalance and unbind use exact absence/generation expectations and affect only later submissions. Admission-created bindings require accepted work; management may explicitly bind an absent key. Absence has no stored entry, a role-changing rebalance issues a fresh generation, and a same-role rebalance preserves the current generation as an accepted no-op. Worker replacement keeps role affinity without keeping an address. Committing permanent role unavailability returns that role's work once and removes every binding to it; temporary recovery retains bindings.
Concerns deliberately outside the first falsification slices
The detailed documents contain candidate answers for the following concerns. The experiment may omit them from an early model or focused falsification slice because they are not part of a template's identity:
- a universal activation plan/permit/request/fact protocol;
- a global activation-authorization limit shared across owners;
- per-item delivery settlement with source/host fallback;
- transitive settlement priority before later mailbox traffic;
- creation-scoped dependency bundles for every child operation;
- total terminal projection through arbitrary heterogeneous wrapper stacks;
- deadline retirement and residual root-run ownership;
- final builder syntax and one typestate marker per configuration axis;
- wrapper-depth diagnostic budgets; and
- catalogue-wide delivery-rejection migration.
Each remains a production prerequisite wherever the solution and coverage matrix say it is. Omitting one limits the result to model/falsification evidence; it does not make a dependent actor implemented. The experiment may propose a simpler replacement only after a focused compile/interpreter witness and composition through two unrelated actors without caller plumbing. That proposal changes no production authority until all governing documents are updated together.
Delivery failure deserves special care. The actor model specifies asynchronous
send and fairness assumptions at the semantic level; it does not prescribe
Bombay's capability-level transport rejection API. The clean-room folds must
preserve emitted values in the real Actions. Until accepted and rejected
interpreter traces prove the selected settlement law, those folds remain
non-production evidence and cannot satisfy dependent solution rows.
Clean-room crate method
The temporary crate must not import crates/actors, its protocols, aliases,
builders, macros, tests, or helper state. Its executable prototypes must depend
on crates/behavior and implement the locked Behavior/Actions boundary.
A tiny local algebra is permitted only in tests as an independent model oracle
and must never be adapted into executable evidence or a second actor contract.
The crate is developed in this dependency order:
real Behavior/Actions smoke witness + independent model vocabulary
-> stable proxy
-> fixed supervisor
-> dynamic supervisor
-> FIFO pool
-> keyed pool
For each step:
- Write one plain-language law and a complete state/event/ownership table.
- Write the smallest pure-fold regression that fails because the actor does not yet exist, using domain vocabulary only.
- Add the private state sum and public protocol before transition code.
- Assert the complete real
Actionsresult, including empty lanes and next verdict. - Add stale, duplicate, overlap, rejection, and shutdown cases before adding another policy.
- Add deterministic accepted and rejected interpreter traces proving creation/effect order and retained ownership.
- Add compile failures only for actual capability violations.
- Audit every new prototype symbol against its pre-edit law and regression.
No common helper is extracted on first use. On second use, compare the two ownership and transition equations. Extract only if they are identical, the new abstraction owns a semantic law, and total prototype source decreases. Similar variant names are not enough.
Questions the experiment must answer
The clean-room crate exists to answer these questions with code rather than another speculative type inventory:
- What is the smallest creation result that returns exact installed capability and complete rejection ownership without structural paths?
- Can explicit typed worker readiness be expressed as ordinary protocol composition while proving the exact producer authority, incarnation and attempt correlation, asynchronous initiation effect, in-flight ownership, and accepted/rejected/cancelled/late settlement needed to avoid a universal activation subsystem?
- How does
assignment.complete(result)produce a statically typed private parent report without exposing a parent path, customer route, helper trait, or generated alias? - Can each actor expose one named semantic action product while keeping its interpreter requirements inferred and closed?
- Which public protocols genuinely need distinct acceptance, lifecycle, diagnostic, and terminal destinations?
- Which recovery values have exactly the same law in fixed supervision and the two pools after all three folds exist?
- Can shutdown be complete with the initial wait-for-owned-actors policy before deadline retirement is introduced as an independent extension?
- Can ordinary construction infer the finished behavior type without a public marker for every missing choice?
A negative answer is useful evidence. It authorizes only the smallest semantic surface named by that failed witness, not a general wrapper or utility family.
Ready for comparison and policy enrichment
The non-production experiment is ready to compare with the governing solution and enrich with omitted policy only when:
- all five prototype folds implement the real
Behavior/Actionsboundary and pass pure state-machine tests against independent models; - each has accepted and rejected end-to-end interpreter traces;
- invalid protocol/capability exchanges fail at compile time;
- no ordinary example names a nonce, occurrence, path, generated effect product, typestate proof marker, or parent-report carrier;
- no valid example supplies a no-op policy or placeholder type;
- shared abstractions delete more prototype machinery than they add; and
- the legacy implementation has not been imported, wrapped, or retained by a compatibility layer.
This gate authorizes comparison and policy enrichment only. Safe migration additionally requires:
- applicable feature-catalogue parity recorded in the solution matrix;
- the complete selected initialization-settlement and activation path;
- accepted/rejected delivery settlement with surviving ownership;
- wrapper-composition and initialization-order proofs in applicable orders;
- complete orderly/forced shutdown and residual ownership handling;
- compiler-pass/fail and ordinary DevX acceptance; and
- every production prerequisite in the solution marked implemented and verified through its required interpreter and test layers.
Only after those requirements compose and the governing documents are updated together may the repository produce a migration/deletion proposal.
Disposition of the existing atomic documents
atomic-actor-features.mdremains the exhaustive edge-case quarry. A row is promoted only when its policy enters an explicit implementation stage.atomic-actor-solution.mdremains the production design authority. Its open foundational mechanisms and dependency order remain production blockers; this experiment may test or falsify them but cannot bypass them.atomic-actor-type-inventory.mdis a list of hypotheses. No candidate name is authorized until the clean-room compiler and fold witnesses need its law.atomic-actor-devx.mdremains the eventual usability acceptance target, not the first source of builder types.atomic-actor-retained-core.mdgoverns the retained production-core audit. The temporary crate uses the real locked behavior boundary while testing whether template-level candidate machinery is necessary.atomic-actor-research-audit.mdremains the dead-end and discovery record.atomic-actor-other-templates.mdremains out of scope; the clean-room work does not trigger a catalogue-wide cleanup.
This keeps the detailed learning without making its accumulated candidate machinery the architecture of the replacement.
Atomic actor architecture map (engineering record)
Status: feature-complete normalization map. This document owns dependency direction and responsibility placement only. It does not restate aggregate state machines, runtime settlement, public construction syntax, or verification matrices.
Protocol vocabulary and reuse
Atomic protocols use names that expose timing and ownership. An input enters
a behavior transition. A command carries application intent. A runtime
request remains owned until interpretation returns either an exact receipt
or a rejection containing the complete request and reason. An outcome or
report is a later domain or lifecycle result, not proof of send acceptance. A
reply is an application-level answer to a command.
When several operations have the same complete ownership and timing equation,
that equation is represented once and parameterized by its domain request,
receipt, rejection, or outcome values. Result<Receipt, (Request, Rejection)>
is preferred for the exact two-alternative case. A separate sum is justified
only by different alternatives, ordering, correlation, or retirement—not by a
different aggregate name or message suffix.
Semantic authority map
| Concern | One normative owner |
|---|---|
| Stable-proxy transition law | actor-laws/proxy.md |
| Fixed-supervisor transition law | actor-laws/fixed-supervisor.md |
| Dynamic-supervisor transition law | actor-laws/dynamic-supervisor.md |
| FIFO-pool transition law | actor-laws/fifo-pool.md |
| Keyed-pool transition law | actor-laws/keyed-pool.md |
| Generic interpretation, settlement, and terminal custody | atomic-runtime-settlement.md |
| Application construction and usage | atomic-actor-devx.md |
| Verification and evidence gates | atomic-actor-verification.md |
The family documents in this directory own collaboration boundaries, policy classification, and realization gates. They link to the authoritative law matrices instead of copying them.
Worker means the concrete application-defined W: Behavior, not a Bombay
trait or wrapper. Atomic templates may privately host that value through fresh
creation, initialization, activation, exact capability ownership, and stop.
WorkerSubmission<W, P> is the affine construction product pairing the worker
with its activation work; it is not another actor abstraction. Stable service
identity and replacement belong to StableProxy; roster and recovery policy
belong to supervisors; assignments belong to pools; bindings belong to
KeyedPool. Shared worker-hosting data may encode only equations that are
identical in every real consumer and may never select those contextual policies.
InitialWorkerRejection is the one shared construction result used by Fixed,
FIFO, and Keyed construction. It owns the callable, prepared prefix, unavailable
role and reason, and untouched suffix. Each aggregate construction error embeds
that value beside only its own policies. The private preparation operation runs
before a behavior exists; it cannot run application code during a transition.
The StableProxy source location does not make intrinsic worker values
proxy-owned. ActivationPlan, WorkerSubmission, opaque worker/
initialization/activation correlations, initialization and activation requests
and results, exact established-worker custody, and pre-commit worker rejection
are aggregate-neutral data. FIFO and KeyedPool now share only their proven
direct-worker relationship beneath the private atomic::pool::worker
hierarchy, while retaining the canonical atomic::*
application/interpreter spelling.
PendingWorker, WorkerStartResult, and ProxyDrain remain StableProxy
concerns in their present form; a pool must not translate or ignore their proxy
outcomes.
Dependency direction
Behavior interpretation law
-> validated values and boundary records
-> StableProxy
-> FixedSupervisor
-> DynamicSupervisor
-> direct-pool semantic values
-> FifoPool
-> KeyedPool
-> inferred construction and concrete protocols
Bombay Engine
-> Behavior + Behavior Actors
-> Address + Communication + Observe + Timers
The arrows denote use, never ownership reversal. There are no aggregate cycles.
FixedSupervisor and DynamicSupervisor are independent folds that collaborate
with the one StableProxy law. FifoPool and KeyedPool are independent direct
worker folds; neither contains a proxy or supervisor and neither implements the
other.
Repository responsibility
crates/behaviorowns the pureBehaviorfold,Actions, and only generic statically typed interpretation products proven useful across the catalogue.crates/actorsowns the five reusable aggregate laws and their concrete protocols, policies, builders, and pure folds.- Bombay owns the single Engine
Driver, tasks, mailboxes, actor installation, activation, capability interpretation, observation, timers, and retirement. - Communication owns mailboxes; Address owns identity and leases; Observe owns completion publication; Timers owns scheduling state.
crates/behavior-testkitand the nested atomic experiment own independent models and falsification evidence, never production semantics.
Module ownership
Aggregate directories expose the ownership hierarchy directly. A child file
uses the shortest name that is meaningful beneath its parent, and the aggregate
mod.rs alone selects the public surface. For example:
stable_proxy/
mod.rs
state.rs
protocol.rs
effects.rs
operation.rs
worker/
mod.rs
initialization.rs
activation.rs
Thus stable_proxy/worker/mod.rs reads as the proxy's exact worker ownership, without a
redundant proxy_worker_start.rs filename. Nested modules do not widen
visibility or create another transition owner; their parents control what may
be used by sibling concerns and what reaches applications.
The fixed supervisor follows the same rule without decorative directories:
fixed_supervisor/
mod.rs
member.rs
role.rs
proxy/
mod.rs
start.rs
stop.rs
outcome.rs
recovery/
mod.rs
correlation.rs
preparation.rs
stop.rs
restart/
mod.rs
admission.rs
shutdown/
mod.rs
member.rs
Each group uses its existing principal concern as mod.rs; there is no empty
module whose only job is to make the tree deeper.
FIFO uses the same ownership rule with fewer concerns:
fifo_pool/
mod.rs
member.rs
job.rs
policy.rs
protocol.rs
requests.rs
mod.rs is the only transition authority. member.rs stores the current
direct-worker relationship, job.rs owns customer custody and assignment
reunion, and the other three modules separate validated application policy,
application protocol, and interpreter requests. None receives arbitrary pool
inputs or emits Actions independently. Creation, activation, readiness,
assignment, recovery, and retirement are member alternatives, not one child
module per arrival path.
Forbidden edges include Behavior depending on Behavior Actors, pools depending on StableProxy, supervisors depending on pool-private models, any Behavior fold depending on runtime services, or any component introducing a second Driver, mailbox, registry, lifecycle service, erased envelope, or dynamic capability map.
Law classification
Fresh allocation, isolated processing of one communication, sending to known recipients, and explicit next behavior are actor-model laws. Stable identity, supervision strategy, admission, affinity, retry, settlement products, and terminal customer outcomes are derived constructions. Creation-before-dependent operations, initialization settlement before ordinary ingress, diagnostic disposition, drain policy, and root terminal custody are deliberate Bombay policies. Family documents label policy choices without presenting them as Agha guarantees.
Campaign boundary
Entity/Mnesis persistence, ActorInterface, external actors, receptionists,
HTTP, discovery, clustering, and process-exit policy are downstream constraints,
not part of the five-fold implementation. Their migration requirements are
recorded separately and cannot justify a compatibility alias in this campaign.
The normalized catalogue, canonical syntax prototype, exact dependency contracts, and total-settlement representation gates pass. Bombay owns the documented four-link terminal-custody change externally. Local production begins after the independent evidence commit and explicit clean-room deletion commit.
Developer-experience acceptance contract (engineering record)
Normative ownership: this is the single owner of canonical application
construction and usage syntax for all five families. Aggregate law documents
own transitions; atomic-runtime-settlement.md
owns interpretation/custody; atomic-actor-verification.md
owns evidence gates. Other documents link here rather than repeating builder
orders or worker authoring syntax.
This document specifies what ordinary source must express for the five atomic actors. All five component construction syntaxes are selected and implemented. Bombay's role-first application assembly and runtime custody changes remain upstream work; component construction does not claim that integration complete.
The earlier code snippets are withdrawn. They looked copy-pasteable while
leaving hosting, activation, effect lowering, replies, rejected delivery, and
shutdown undefined. They also exposed or assumed manual Protocol
implementations, runtime addresses, turbofish, handwritten behavior dispatch,
pseudo-send, and unproven initialization syntax. None is an accepted API.
Application-defined workers
The application defines the actual worker as an ordinary concrete Behavior.
It may be one domain behavior or an application-defined closed sum of domain
behaviors. No Worker trait, runtime context, wrapper base class, callback, or
atomic worker behavior is required. A template receives that behavior through a
typed WorkerSubmission and owns only its relationship to the actor. In a pool,
the worker receives Assignment<Job> and returns its result with
assignment.complete(result); it does not know the pool, customer, completion
route, structural parent path, or assignment identity.
Canonical public path
The Behavior Actors component has one external spelling for this family:
behavior_actors::atomic::Name. Atomic names are not repeated at the component
crate root. This module path communicates ownership; it is not structural actor
routing.
Bombay must not glob-re-export behavior_actors::atomic. Its ordinary facade
selects only these categories from the implemented families:
- construction and behavior:
StableProxy,fixed,FixedSupervisor,dynamic,DynamicSupervisor,fifo,FifoPool,keyed, andKeyedPool; - worker definition and activation:
ActivationPlan,ImmediateActivation,WorkerSubmission,ActivationPolicy,EntryCapacity,BacklogCapacity,BindingCapacity, andZeroCapacity; - required policy:
ActorDrainPolicy,DiagnosticDisposition,Recovery,Strategy,RestartLimit,RestartRelease,FailureReaction,UnexpectedExit,PoolRecovery,PoolFailureReaction, andInterruption; - application protocols:
FixedCommand,DynamicCommand, their reply values, query/status values, cancellation authority and outcomes, lifecycle values, diagnostics, FIFO/keyed customer outcomes, binding commands and replies, and typed policy errors; and - validated fixed topology:
OrderedRoles,DuplicateRole, and the complete construction rejection.
Bombay's interpreter implementation imports doc-hidden request products, effect products, internal events, worker preparation, activation requests, proxy control, and settlements directly from the component module when their associated types require it. Bombay does not re-export those names. Therefore ordinary application autocomplete and rustdoc contain domain construction, policies, commands, capabilities, and outcomes rather than runtime plumbing.
Status and acceptance gate
The five component construction paths are feature-complete: each is
implemented, inferred from domain values, and covered by a compile-pass
contract. Bombay's role-first assembly and complete runtime journey remain
active. That upstream journey requires focused proof of all the following
together:
- imports and an ordinary concrete worker definition;
- construction with an inferred final behavior type;
- hosting through the current concrete interpreter boundary;
- initialization before activation;
- immediate and asynchronous readiness;
- command/job submission with concrete typed recipients;
- request replies, durable lifecycle events, customer outcomes, and the selected diagnostic disposition;
- rejected-delivery settlement after the source actor has stopped;
- orderly and forced shutdown; and
- pure-fold testing of the complete named
Actionsproduct.
The same prototype must include compile failures for each missing semantic
choice and each invalid capability exchange. A snippet containing ignore,
pseudocode, an undisclosed helper, or a support alias supplied only to appease
inference is not evidence.
What users choose
Users choose only domain and policy values:
- semantic roles and dynamic keys;
- concrete worker definitions, jobs, and results;
- a fixed topology or dynamic entry capacity;
- immediate activation or a concrete statically dispatched activation plan;
- a positive maximum number of unresolved activation authorizations;
- recovery, strategy, budget, release, interruption, and
ActorDrainPolicyvalues where those policies are meaningful; - concrete request, lifecycle, customer, and diagnostic routes; and
- FIFO distribution, or keyed distribution with one key-to-role selector.
Users do not choose or construct child nonces, actor addresses, installation attempts, generations, activation attempts, timer identities, job identities, assignment identities, completion tokens, structural parent paths, effect lane positions, or settlement owners.
Construction laws
- A constructor or complete builder produces one concrete actor; users do not name its full generic return type.
- When a family has optional construction transformations, incomplete builders
do not implement
Behaviorand have nobuildmethod. - One documented order exists for each actor. Repeatable topology entries are the only deliberately repeatable step.
- A builder exposes no choice that its actor does not use. Dynamic supervision has no fixed factory or recovery placeholder; FIFO has no selector; pools have no proxy choice.
- Missing required choices are typestate, not runtime flags.
- Duplicate roles and other value-dependent failures are typed build errors.
- No valid construction supplies a no-op callback, dummy recipient, empty factory, marker route, reply alias, output alias, or explicit generic merely to satisfy implementation plumbing.
- Every route preserves its concrete logical, established, exact, or mixed delivery kind.
Diagnostic construction has exactly two semantic choices:
DeliverTo(concrete route)
Terminate
Terminate is the autonomous case. There is no mandatory
.diagnostics_to(...), optional route plus hidden default, or recursive
diagnostic fallback. A literal Diagnostics<Route>::Terminate is also
rejected because it leaves an unused route type to infer. The selected builder
must offer one route-bearing choice and one route-free choice without asking
the user to name their typestate result.
Shared activation journey
Each fixed/dynamic supervisor and pool builder selects activation mode and a
positive limit on unresolved activation authorizations. A proxy has the
structural limit of its single pending worker. “Activation authorization” has
one user-facing meaning:
an owner has decided that a worker may progress toward Ready. A supervisor
reserves conservatively when it emits the opaque proxy install because it sees
no later progress facts; a direct pool reserves when it emits
BeginActivation after initialization settlement. The difference is a
documented template boundary, not two counting laws. The resulting journey is:
build definition
-> supervisor records activation authorization before proxy install
(a direct pool waits until initialization settles)
-> run the pure initialization fold
-> establish the host and commit installed-but-not-ready
-> interpret and settle initialization Actions
-> direct pool records activation authorization
-> activation consumes the installation permit and plan
-> exact Ready or ActivationRejected fact
-> only Ready publishes or accepts work
The ordinary worker author supplies a concrete activation value; they do not
send BeginActivation, mint an attempt, or route ActivationResolved.
Immediate activation uses a route/plan-free builder choice; it must not require
an annotation for an otherwise unused plan type.
Initialization failure, rejected initialization effects, and stopping during
initialization are observable pre-readiness outcomes; activation is never used
to hide or rerun them.
Cancellation recovers a worker definition only before the install/create
action that transfers it. Cancellation after that ownership boundary is
reported as pending—even if activation has not begun—and never claims that
external hydration stopped. Late readiness is drained and never published.
Stable proxy journey
Most users never construct a proxy. Fixed and dynamic supervisors create and control one per stable service identity.
A direct proxy owner must be able to:
- construct an empty proxy without repeating the worker type;
- retain a private exact control capability;
- install the initial worker or request replacement by moving a definition;
- give clients only the stable service capability;
- exhaustively receive readiness, replacement, stop, unavailability, and contradiction reports; and
- shut down while creation or activation is unresolved.
Application clients cannot address install, replacement, readiness, or shutdown control. No public method sets a nonce, incarnation, or ready flag.
Fixed-supervisor journey
The selected semantic order is:
fixed(
factory,
ordered non-empty roles,
activation policy,
recovery policy,
topology-failure reaction,
ActorDrainPolicy,
diagnostic disposition,
)
-> optional lifecycle publication
-> build and prepare every initial worker
The construction-only worker factory returns one WorkerSubmission: the worker
definition and the concrete activation work for that exact attempt. Immediate
workers use the inferred immediate construction; activated workers provide one
owned plan per factory result. A multi-role actor never clones or rediscovers
one plan stored beside the roster.
The successful supervisor does not store this callable or carry its type.
Automatic recovery obtains submissions through a typed batch worker source in
Actions. H38 selects the Bombay-interpreted typed worker-source action. An
ordinary factory actor remains valid application architecture, but is not
mandatory because accepted delivery still needs a later reply join. Canonical
recovery-source syntax therefore names the application worker source and its
typed rejection only. Bombay will implement the generic static interpreter and
retirement transfer; no runtime path, source-result route, callback, or composed
Behavior type appears in application code.
H52 proves the candidate diagnostic protocol for operating preparation failure.
The diagnostic actor names
FixedDiagnostic<Role, Worker, Plan, Source> directly as its domain message;
no application alias is required. Source is the already-selected recovery
capability type and statically selects its worker and source rejection types;
the source value itself is never placed in a diagnostic. Applications inspect
one owned WorkerPreparationFailure through role(), prepared(),
remaining_roles(), and one exhaustive borrowed
WorkerPreparationFailureReason. They never name immutable role storage,
preparation tickets, action settlements, request lanes, or parent paths. This
shape is implemented by the current fixed supervisor.
H53 lowers this protocol surface into the actors crate. Construction of the payload from an operating recovery remains the next independent transition stage; the public syntax no longer depends on that implementation detail.
The source implements method-free WorkerSource<Role, Worker, Plan> by naming
only its worker-level and whole-source rejection types. Applications never
construct PrepareWorkers, inspect WorkerPreparation, or name their generic
types. Those interpreter-facing products remain hidden behind the supervisor's
own recovery transition and the generic action-settlement path.
Recovery owns the complete automatic-recovery choice. Canonical construction
is Recovery::permanent(source, strategy, limit, release),
Recovery::transient(source, strategy, limit, release), or
Recovery::temporary(). Permanent and transient recovery retain the concrete
worker source that Bombay will invoke outside Behavior. Temporary recovery
carries neither a source nor ignored strategy, budget, or release values. The
activation policy owns only its positive authorization maximum; callers use
ActivationPolicy::new(usize) and receive ZeroCapacity instead of constructing
NonZeroUsize. The plan belongs to each worker submission. Passing all required values to
fixed avoids a user-visible chain of structural builder stages; omitting any
value is an ordinary missing-argument compile error.
The seven direct arguments were compared with a named policy product and another typestate builder stage. The product would merely regroup unrelated choices and the builder would expose more construction machinery without preventing another invalid exchange. The direct form remains canonical because the factory, roles, activation, recovery, topology reaction, drain policy, and diagnostics all have distinct semantic types. Lifecycle publication remains the one truthful builder transformation because it changes the resulting action protocol.
Roles are application domain values such as SearchRole::Index, not static
strings, child positions, or runtime addresses. The factory receives a borrowed
role and returns that role's concrete worker or a typed rejection. build
prepares roles in declaration order. FixedConstructionRejected.workers is one
InitialWorkerRejection containing the factory, every prepared member, the
unavailable role and reason, and all untouched roles; the outer value contains
only fixed policy. No partial supervisor exists.
The external lifecycle route is genuinely optional. Omitting it creates an autonomous supervisor; it does not create a discard sink. Operational failures still follow the selected diagnostic disposition. Route-free diagnostic termination is inferred directly; it requires neither a dummy route nor a route-type annotation.
All workers in one fixed supervisor expose one common public service protocol. Different behavior implementations may be variants of a closed concrete sum, but ordinary users should not need to hand-write forwarding solely because the supervisor API failed to compose existing behavior forms. Capability-safe heterogeneous public protocols remain a separate, unselected feature.
The lifecycle consumer, when configured, receives one flat semantic event sum
containing exact ready proxy capabilities and complete failure facts. It never
walks .inner, .own, WithParent, a slot number, or wrapper depth.
Dynamic-supervisor journey
The canonical constructor is one dynamic(entries, activation, unexpected_exit, actor_drain, lifecycle, diagnostics) call. Applications construct entries
with EntryCapacity::new(usize) and activation with
ActivationPolicy::new(usize). Both return ZeroCapacity, but their values
cannot be exchanged. Every executable semantic policy appears once; there is no
staged builder, factory, callback, default, marker, or public proxy type.
ActorDrainPolicy is mandatory because global shutdown is executable.
A named policy product or another builder would only move these six unrelated
choices behind an extra public name, so the direct form remains canonical.
Bombay constructs the lifecycle and diagnostic peers first, passes their typed
capabilities to this call, and then activates the returned supervisor;
applications never name the final behavior type. H148 historically derived the
role-first inference shape with stable Rust. The real
role_first_construction_returns_the_complete_behavior integration test now
owns that syntax; H589 removes the superseded private facsimile. The earlier
H135/H137 fragments remain rejected because they fabricated capabilities before
the code under test.
Bombay must declare and establish application actors by semantic role before it
constructs the root. Its one-time application assembly supplies typed logical
or exact capabilities for those actors to the root constructor. The lifecycle
actor's concrete protocol then supplies the dynamic supervisor's key, worker,
and activation types; the diagnostic actor supplies its own concrete protocol.
The resulting root capability makes every command, including Stop { key },
infer without an alias, annotation, turbofish, raw runtime address, structural
path, dummy route, or fixture capability. Bombay performs this assembly outside
Behavior; the constructor performs no I/O and is never stored in the
supervisor.
Bombay's assembly method names remain an upstream choice, but the required
source shape is role-first topology, typed peer capabilities, one pure root
construction, then ordinary delivery through the returned root handle. A
root-first API that asks the user to repair missing types is not compatible.
H148's frozen proof compiled all five commands through the returned typed
capability. Current dynamic integration tests exercise all five real commands,
and the real construction test retains the inferred behavior syntax. Removing
worker and activation provenance from the lifecycle capability failed at
construction; detaching Stop from that capability failed with E0282.
The durable lifecycle route is mandatory and configured once. A start,
replace, stop, query, or cancel request carries only its temporary reply route.
No request may become, replace, or transfer the durable owner. Stop is the
single public operation that drains and removes a ready or empty entry; there
is no second retirement command.
Start and replace replies distinguish admission from realization. Admission
returns an opaque CancelAuthority. Later readiness or failure goes only to
the configured durable lifecycle route. Cancellation has the complete
phase-sensitive outcomes selected by AA-20; only cancellation before
definition transfer returns the definition.
Start and replace share one direct result equation because their accepted
ownership is identical: WorkerChangeReceipt or WorkerChangeRejection with
an operation-specific reason. Stop uses its own direct Result. Cancellation
returns CancellationReceipt::{Returned, Pending, Committed, Cancelled, Stale, Draining}; query returns its independent known/unknown reply. There is no
catch-all DynamicReply and no alias that merely renames Result.
Start preparation is transaction-local: successful admission commits the
creating-proxy entry and emits proxy creation in the same transition. Query
therefore exposes creating proxy, waiting for activation authorization,
awaiting proxy outcome, ready, empty, stopping, replacing, cancelling,
draining, or retiring—never a stored Reserved phase. It does not invent
worker progress absent from the proxy report protocol. An unknown key is a
distinct outcome.
FIFO-pool journey
The selected construction spelling is one direct call:
fifo(
initial_factory,
ordered unique roles,
ActivationPolicy,
PoolRecovery,
BacklogCapacity,
Interruption,
ActorDrainPolicy,
DiagnosticDisposition,
)
Every argument has a distinct domain type, so exchanging backlog and interruption is a compile
error. BacklogCapacity::new(0) is valid because zero means no waiting work. A named policy
product owns no new invariant; a fluent form adds missing-state machinery. Neither is retained.
The call prepares the complete roster before a pool exists. Success returns a
FifoPool whose type does not contain the callable. Failure returns
FifoConstructionRejected: its workers field is the shared
InitialWorkerRejection, beside only FIFO policy.
Automatic PoolRecovery owns its source, limit, release, and RetireRole | StopPool reaction.
Temporary recovery owns only the reaction and needs no source placeholder. No transition invokes
the factory, and users never name preparation or interpreter machinery.
Submission moves a caller request correlation, payload, and concrete customer
route to the pool. Accepted and Rejected both echo the request correlation;
the pool never trusts it as its internal job identity. Admission either returns
the payload in Rejected or creates one authoritative customer obligation
identified by a fresh opaque job value and immutable admission ordinal.
Terminal customer outcomes are exactly:
Completed { job, role, result }
ReturnedQueued { job, payload, reason }
ReturnedAssigned { job, role, payload, reason }
The pool, not the worker, retains and selects the customer destination. Retry is explicitly at-least-once execution: the pool keeps the authoritative obligation and retained retry payload while a cloned execution payload is at the worker. An interrupted retry is reinserted by its immutable admission ordinal. This preserves admission order even when several active workers stop in a different order; unconditional front insertion would reverse those jobs.
Pool-worker authoring
The intended worker expression is now a production compile contract in
crates/actors/tests/fifo_pool/compile.rs:
#![allow(unused)] fn main() { fn transition(&mut self, assignment: Assignment<Job>) -> WorkerActed<Self> { let result = self.process(assignment.payload()); Ok(Actions::cont().with_send(assignment.complete(result))) } }
The behavior_actors::atomic::pool_worker attribute is syntax derivation only.
It reads the authored Assignment<Job> input and declared result type, rewrites
the intentionally unresolved WorkerActed<Self> spelling, and emits the sole
ordinary Protocol, Behavior, and BehaviorBase implementations. There is
no public WorkerActed type, worker trait, adapter actor, callback, result
marker, or alternate transition. The worker body names no parent path, lane,
assignment identity, completion route, composed behavior type, or alias.
Exact source inspection and the compile experiment distinguish the mechanisms:
| Mechanism | Result |
|---|---|
inherent Assignment::complete | cleanly constructs and consumes the affine completion value, but cannot select the worker's already-declared Behavior::Sends |
| extension trait or associated type | has the same associated-send boundary; a worker-specific associated trait would become a forbidden second actor trait |
| generic closure/builder | can infer a wrapper's output type, but changes the owning authoring form and does not make the shown inherent method a Behavior fold |
| typestate | proves builder completeness but does not solve send-product selection |
stable return-position impl Trait | cannot supply the named Behavior::Sends associated type on stable Rust 1.95 |
| syntax-only macro derivation | eligible: it can preserve the authored method, rewrite WorkerActed<Self> during expansion, and emit the one concrete Behavior implementation with its completion send type |
The earlier private declarative stand-in remains historical evidence for two
unrelated workers and exact typed interpretation. Production now proves the
same generated completion capability through the actual
ReceiveTimeout<Deadline<Worker>> and Deadline<ReceiveTimeout<Worker>>
orders. Both composed send products satisfy total settlement and the one sealed
pool-completion capability without an adapter or placeholder reaction.
The production surface exposes only Assignment<Job> and opaque
Completion<Result> values. Rustdoc proves that external code cannot mint a
pool-issued job identifier, construct an assignment, forge a completion, or
complete the same assignment twice. It also rejects missing macro declarations
and a transition that receives an unrelated input. All 38 Actors rustdoc
contracts pass on stable Rust 1.95. Deliberately deriving the completion lane
from Job instead of the declared result fails at the authored
assignment.complete(result) expression with the exact SearchJob versus
SearchResult mismatch.
The complete canonical FIFO journey occupies 38 meaningful lines including
imports, domain types, worker, worker declaration, construction, and
initialization. The worker implementation occupies six meaningful lines and
the direct fifo(...) call ten. It needs no final aggregate annotation, helper
alias, turbofish, structural actor concept, interpreter request, or completion
route. The direct eight-domain-value constructor remains the single selected
construction spelling.
Keyed-pool journey
The selected construction spelling is one direct call:
keyed(
initial_factory,
ordered unique roles,
key-to-role selector,
ActivationPolicy,
PoolRecovery,
per-role BacklogCapacity,
BindingCapacity,
Interruption,
ActorDrainPolicy,
DiagnosticDisposition,
)
The callable factory exists only during construction; the successful
KeyedPool does not store it. KeyedConstructionRejected.workers is the shared
InitialWorkerRejection, beside the selector and keyed policy. The selector is
the one concrete Fn(&Key) -> Role value used by
future admission; it is not a boxed callback, runtime registry, or distribution
mode. ActivationPolicy, BacklogCapacity, and BindingCapacity are distinct
types, so their numeric storage cannot make them interchangeable.
Keyed admission has one selected model:
Submit { key, payload, reply_to }
selector: concrete Fn(&Key) -> Role
There is no KeyedJob and no SelectWorker trait. The submitted key is
explicit; one statically stored concrete function or closure maps that key to
a role. FIFO accepts no selector.
The selected semantic order reuses FIFO's accepted-job and direct-worker value
laws but substitutes per-role backlog and keyed distribution for global FIFO
backlog and circular worker selection. Rebalance and unbind use typed
management replies, carry Absent | Exact(binding_generation) expectation,
and affect future admission only. Role-changing rebalance issues a fresh
generation; same-role rebalance is an accepted no-op preserving it. An absent
key may be bound either with accepted admission or by explicit rebalance.
Absence stores no Unbound entry, and accepted jobs retain only opaque
{ generation, role } evidence rather than a cloned key.
Committing permanent role unavailability automatically unbinds every retained binding for that role, releases binding capacity, and emits exact key/generation diagnostics. Temporary recovery retains bindings. An idle ready target assigns immediately. Prepared, creating, initializing, waiting-for-authorization, activation-dispatched, activating, busy, recovering, or pre-ready-draining-with-retained-recovery targets may admit only to their own backlog when capacity remains. Stopping, retired, terminally draining, and shutdown-owned targets reject without retaining either a new binding or the submitted job; zero backlog therefore accepts a fresh key only when its selected target is ready-idle.
Rebalance uses a separate exhaustive target projection because it carries no job: every admission-backlog phase above, including temporary recovery, is bindable without consulting queue capacity, while stopping, retired, terminally draining, shutdown-owned, and unknown targets reject the complete command. Stale absence or generation expectation is rejected before target validation and cannot mutate a later binding.
Bombay hosting and sending
The component construction and command spellings are selected. Bombay's
application-level hosting spelling remains upstream work. Its accepted example
must reuse the concrete interpreter contracts after the documented Bombay
changes; it may not
invent RuntimeAddr::application(), a global envelope, implicit ambient
runtime, or a template-specific host. Likewise, application communication
must use the existing typed delivery values rather than a pseudocode send.
This is a usability requirement, not permission to widen core constructors or add convenience traits. If the current boundary cannot express the journey, the prototype must identify the precise missing semantic law before any new production symbol is proposed.
A forced actor-graph summary is not the final application return while an external activation or late exact drain remains unresolved. The root run future continues to own and process that residual settlement and resolves only after it is empty. Ordinary source must not receive a cloneable residual report and accidentally treat it as completed shutdown.
ActorDrainPolicy has exactly WaitForActorGraph and
RetireActorGraphAfter { deadline }. The latter retires the represented actor
graph after the deadline; it does not promise bounded process exit. No separate
process-exit policy is selected in this design.
Pure-fold testing
Each template must expose its ordinary Behavior initialization and event
fold to tests. A test supplies concrete typed facts, consumes one state plus
one event, and asserts the complete named Actions product and next verdict.
It does not spawn tasks, sleep, consult a clock, predict nonces, discard
actions, or use a test-only dynamic envelope.
Activation, monotonic time, delivery rejection, late settlement, child stop, completion, and shutdown are typed inputs. Interpreter witnesses separately prove that those facts can be produced and that settlement survives the source actor.
Required compiler diagnostics
Focused compile-fail fixtures must reject:
- build before every mandatory semantic choice;
- fixed or pool topology with no role;
- zero activation-authorization limit;
- a worker with the wrong service or assignment protocol;
- completion with the wrong result or without the issued assignment;
- worker selection or substitution of the customer destination;
- a FIFO selector, or keyed construction without its one selector;
- cross-domain key, worker, reply, lifecycle, or operation capabilities when their semantic Rust types differ;
- use of an established proxy capability as another protocol; and
- application construction of attempts, generations, job IDs, completion tokens, parent paths, or settlement owners; and
- ordinary application errors that expose a topology-derived settlement product, occurrence path, or type whose diagnostic grows with wrapper depth;
- a terminal lift that omits a heterogeneous child variant or exact source provenance; and
- treating an actor-side forced summary as the final root-run result while residual activation ownership remains.
Diagnostics are measured at the missing or invalid semantic choice. An error whose useful cause is hidden inside nested associated types, positional effect paths, or a repeated turbofish fails the DevX gate even if the invalid program eventually fails to compile.
Runtime instances with the same public protocol are deliberately not assigned
compiler-only owner brands. For DynamicSupervisor, an authority issued by
another same-signature instance is accepted as typed input and returns
CancellationReceipt::Stale by AA-20's exact operation correlation. Adding an
owner marker solely to turn that runtime law into a compiler denial would add a
generic parameter with no application substitution law.
Copy-paste deliverables
Before the API can be called selected, the repository needs five complete, warning-clean examples—proxy owner, fixed supervisor, dynamic supervisor, FIFO pool, and keyed pool—plus a sixth example showing pool assignment completion through two wrapper orders. Each must cover the entire acceptance gate at the top of this document. At present none exists; this is recorded as an implementation blocker, not concealed with aspirational snippets.
Exhaustive feature catalogue for atomic actor templates (engineering record)
This document records the observable requirements that the replacement actor templates must satisfy. It intentionally makes no decision about Rust types, builders, internal state layout, shared helpers, or implementation strategy. Those decisions belong in a later design document.
The catalogue was assembled from the existing supervisor, proxy, pool, interpreter, model, property, exhaustive, and fuzz contracts. Existing implementation names are not requirements unless the behavior itself appears below.
The atomic templates in scope are:
- stable worker proxy;
- fixed supervisor;
- dynamic supervisor;
- FIFO worker pool; and
- keyed worker pool.
Every feature group below has a stable identifier in brackets. Individual
bullet requirements are referenced by one-based ordinal: the first bullet
under [PX-REPLACE] is PX-REPLACE-01, the second is PX-REPLACE-02, and so
on. Adding a requirement appends a new ordinal; existing IDs are not reused.
Requirements shared by every template
[SH-ACTOR] Actor boundary
- The template is one concrete, statically dispatched
Behaviorfold. - One input is processed at a time.
- A successful transition returns all communications, fresh creations, and
the next behavior or termination decision through
Actions. - Initialization is a pure fold whose actions are interpreted before mailbox inputs.
- No template performs delivery, allocation, observation, scheduling, clock access, or shutdown directly.
- No template depends on an executor, transport, global registry, runtime query, ambient context, or hidden side channel.
- No template wraps an arbitrary application behavior merely to gain lifecycle machinery.
[SH-STATIC] Static protocol and capability safety
- Public commands, private parent-to-child inputs, child-to-parent reports, runtime facts, effect lanes, errors, phases, reply recipients, child behavior alternatives, and creation products are statically known.
- No trait object,
Any, downcast, erased message, runtime protocol lookup, string key, serialization envelope, or unsafe type escape is permitted. - A semantic role or dynamic management key is never treated as an actor address, established capability, creator-local nonce, or proof of freshness.
- A creator-local nonce is never treated as an actor identity or proof that a child was installed.
- Every send to an exact installed actor uses an established capability or an exact creator-local child binding, as appropriate. A stable proxy never exports its worker recipient; owner-visible worker incarnation data is opaque non-routable evidence.
- Heterogeneous workers are represented by one closed application-defined sum whose variants remain statically dispatched.
[SH-CREATE] Fresh creation and provenance
- Every new actor incarnation is staged as a fresh creation.
- A nonce collision, allocation exhaustion, address collision, initialization rejection, installation failure, or commit failure never overwrites an existing binding.
- Initial birth and replacement incarnation remain distinct provenance.
- Replacement provenance is attached when fresh creation commits, but a
successful
Restartedoutcome is reported only after that exact replacement incarnation becomes ready. - A rejected replacement never emits a successful restart or birth outcome.
- Same-action dependent observations or sends rely on the documented creation-before-send interpretation order and retain typed rejection.
- Converted or generated nonce values cannot silently repeat a previously issued creator-local nonce.
- Exhausted nonce, attempt, operation-ticket, timer identity, timer generation, assignment, and job sequences have typed outcomes; production panics are not accepted.
[SH-READY] Installation, activation, and readiness
- Committed actor creation proves installation, not application readiness.
- The runtime first runs the concrete behavior's pure
initfold exactly once. If that fold rejects, no host is committed and none of its returned effects exists. Activation never callsinita second time. - After a successful init fold, the runtime establishes provisional/exact host
ownership and commits an installed-but-not-ready incarnation before
interpreting any initialization
Actions. - Initialization actions may partially succeed. Their later rejection cannot reject or erase the already committed installation and cannot pretend an earlier delivery, creation, or state transition did not occur.
- Host/allocation rejection before commit, initialization-fold rejection, initialization-effect rejection after commit, and initialization stop after commit are distinct typed pre-readiness outcomes. Post-commit rejection or stop drains the exact installed incarnation and issues no activation permit.
- Every worker definition selects one concrete statically dispatched activation plan. An immediate plan performs no external work but still resolves readiness only after behavior initialization succeeds.
- Committed installation yields an actor-retained exact installed-but-closed incarnation, initialization-settlement ownership, and the same concrete activation plan moved into creation. Only complete successful initialization settlement yields that plan plus the distinct one-shot activation permit tied to the incarnation; no plan is cloned or rediscovered.
- The activation interpreter accepts a typed
BeginActivationrequest owning an opaque attempt, the one-shot installation permit, and the concrete plan. The actor's activating state retains the exact incarnation and attempt correlation, not the moved permit or plan. - Start rejection returns the complete unaccepted request through the
surviving action settlement; accepted activation eventually produces one exact
ActivationResolvedfact: ready or activation rejected. - Fixed and dynamic supervisors and both pools select a positive maximum
number of unresolved activation authorizations. Authorization means the
owner's decision to let one worker progress toward
Ready; that is the one user-facing counting law. A supervisor deliberately reserves its ticket conservatively when it emits the opaque proxy install input, while a direct pool reserves when it emitsBeginActivationafter initialization settles. A proxy has a structural local limit of one. Reserved definitions beyond an owner's bound remain actor-owned and have not crossed that template's authorization boundary. - Readiness is correlated to the exact installed incarnation and activation generation; a role, key, nonce, or timestamp alone is insufficient.
- A supervisor does not publish
StartedorRestarted, a proxy does not route a command, and a pool does not assign a job until the exact worker is ready. - Initialization actions and their settlement complete before an immediate-ready outcome or external activation request can make the worker routable. Failure prevents readiness but never reverses installation.
- Ready, rejected, stopped, and shutdown facts that may race are joined once by the actor that owns their exact correlations.
- Duplicate, stale, foreign, or contradictory activation facts preserve all authoritative inputs and cannot make an incarnation ready.
- Asynchronous hydration, I/O, and wall-clock waiting remain outside the pure behavior fold; only their typed completion fact enters the fold.
- Activation rejection is distinct from creation rejection and ordinary terminal outcome throughout recovery, diagnostics, and shutdown.
- Cancellation returns a definition only before the exact install/create
action that transfers it. The interval after that transfer but before
BeginActivationis already logical cancellation and cannot claim to return the definition. After activation emission, cancellation additionally closes publication irrevocably but does not claim that external hydration was physically cancelled. - A ready fact arriving after logical cancellation is a typed
ReadyAfterCancellationoutcome. The incarnation is never advertised and is drained exactly once. - A rejection arriving after logical cancellation resolves the attempt without fabricating readiness and retains the exact rejection.
- Forced shutdown may retire the actor-side attempt while external activation remains in flight. The activation settlement owner survives the actor and owns any later ready/rejected fact; a stopped actor is never its destination.
[SH-CORRELATE] Fact correlation and transition integrity
- Every creation, stop, shutdown rejection, timer, completion, and child report is correlated to its exact pending operation and incarnation.
- A stale, duplicate, foreign, malformed, wrong-provenance, or contradictory fact cannot advance, revive, overwrite, or partially mutate current state.
- When rejection must return owned input or authoritative facts, the complete values are returned without reconstruction or cloning used to escape ownership.
- Preparation and validation finish before the single state commit.
- A failed multi-member decision performs no partial budget charge, member reservation, replacement send, assignment, dequeue, or binding change.
- Effect products use semantic lane names and append every lane exactly once in lane order.
- Adding an unrelated outer behavior composition cannot drop, duplicate, reorder, reinterpret, or require callers to recount a structural path for the template's events or effects.
[SH-DELIVERY] Delivery acceptance and rejection
- Emitting one delivery in
Actionsmeans one delivery attempt, not proof that the destination received or processed the value. - Logical, established, child-local, and structural-parent routes remain distinct concrete route kinds; a template never upgrades or downgrades one implicitly.
- Every named effect product projects one closed rejection sum with a variant per delivery lane. Each variant owns the complete payload, route kind, and capability-level reason.
- Every emitted item has one ordered settlement: accepted, rejected, or not attempted because an exact declared prerequisite rejected. A settlement is a named per-lane product, so several failures in one action cannot collapse into one sum value or discard later payloads.
- A rejected prerequisite suppresses only its dependent effects and returns them untouched with an opaque reference to the one authoritative prerequisite settlement. The rejection payload is not cloned into every dependent item. Independent later lanes are still interpreted in their documented order.
- Interpreting an action establishes a typed settlement owner before the first delivery attempt. Settlement outlives the source turn and source actor.
- Every lane declares a primary rejection consumer and the surviving settlement owner used if that consumer is stopped or unreachable.
- A continuing actor may receive its typed rejection as a later runtime fact; the settlement owner retains fallback ownership until that fact is admitted.
- The Driver gives the current action's transitive settlement chain priority on the typed system lane. It admits each fact to the live source or transfers it to the surviving host; the next ordinary user communication is admitted only if that chain quiesces or transfers outward. Thus unresolved batches do not accumulate across separate user turns.
- Settlement discharge is local to that source/host chain and does not impose a global actor-system barrier. Actions emitted while processing a settlement fact are themselves settled before the next ordinary source communication.
- This priority rule only prevents accumulation across separate ordinary user turns. A transitive settlement chain may be unbounded or non-terminating; the iterative Driver queue prevents stack growth but proves neither memory bounds, fairness, nor eventual return to ordinary traffic.
- The application root remains the live owner of transferred settlement until every external activation, late lifecycle fact, and required exact drain has resolved. A final runner result is not produced while residual ownership exists.
- A delivery emitted by a stopping actor, or rejected after the actor stops, goes directly to settlement. Returning it to the emitter is forbidden.
- A delivery rejection cannot roll back the already committed source-actor transition or pretend that the corresponding state change never occurred.
- Automatic retry is forbidden unless the template exposes and applies an explicit retry policy; otherwise rejection is a typed terminal or diagnostic outcome.
- A closed exact endpoint, unavailable logical destination, rejected child input, rejected structural-parent report, and late reply are distinct where their recovery choices differ.
- Long-lived customer and lifecycle destinations are retained by the actor that selected them. A worker receives no forgeable same-protocol capability whose substitution could redirect a pool or supervisor outcome.
- Delivery-rejection tests exercise both the source fold and the interpreter
return path; inspecting the emitted
Actionsvalue alone is insufficient.
[SH-DIAGNOSTIC] Diagnostic disposition
- Every template that can produce an operational diagnostic has exactly one
semantic disposition:
DeliverTo(Route) | Terminate. This is a domain sum, not authorization for a RustDiagnostics<Route>::Terminatevalue whose unusedRouterequires annotation. The builder must infer either a route-bearing concrete state or a route-free terminal state. An owner-created proxy is fixed toDeliverToits exact structural parent and needs no second builder route. DeliverToattempts one delivery to the concrete logical, exact, or mixed route. It requires no second callback or fallback route.- Rejection of a diagnostic delivery produces one terminal
UndeliverableDiagnosticsettlement containing the diagnostic and route reason. It never emits another diagnostic delivery. Terminateperforms no diagnostic delivery. The fold emits the first diagnostic through a named terminal-settlement request inActionsand selectsStep::Stop; the interpreter settles that explicit value with the surviving lifecycle host.- A root host retains terminal or residual settlement in the live application run future and returns a final value only after it is settled; a child host transfers through the child's typed lifecycle settlement. Neither path depends on the stopped actor's mailbox.
- If an intermediate host also stops, the exact settlement transfers outward through its statically known host chain without reinterpretation. It cannot remain forever in an unreachable host.
Terminatestops in the diagnostic-producing turn after preserving its other actions. Rejected diagnostic delivery stops a still-live source when its settlement fact is admitted; if it cannot be admitted, the host/root terminal settlement is the final disposition.- Diagnostics are never silently logged, dropped, retried, or converted to a
coarse
Crash. - A template without an external diagnostic consumer uses
Terminate; it never supplies a dummy recipient or no-op callback.
[SH-SHUTDOWN] Shutdown
- Shutdown is a typed input accepted in every live phase.
- The first shutdown request begins one terminal drain; later requests do not duplicate effects.
- Every owned established child is asked to stop at most once.
- Pending child creation is resolved before the owner decides whether a child must be stopped or is already absent.
- Rejected pending creation counts as drained and creates no fabricated child.
- A child installed while shutdown is pending is asked to stop exactly once.
- An exact shutdown rejection retains its capability-level reason.
- Delayed work or recovery retained at shutdown is cancelled without being emitted later; its exact timer fact can be consumed without reviving it.
- Termination occurs only after every owned child is resolved and drained.
- The final action retains all required reports and shutdown effects before selecting normal termination.
- Every owner chooses one
ActorDrainPolicy:WaitForActorGraphorRetireActorGraphAfter { deadline }. The former may wait indefinitely; the latter bounds actor-graph retirement only. Neither is a process-exit policy. - Deadline-retirement facts use interpreter-authored monotonic time and exact
timer identity/generation. Rejection of deadline scheduling immediately
forces the same typed host transfer with scheduling rejection as its cause;
it never degrades
RetireActorGraphAfterto unbounded waiting. - Deadline retirement preserves a typed forced-retirement fact containing the affected child, incarnation, phase, outstanding ownership, and cause.
- Deadline retirement atomically transfers that outstanding ownership to the surviving lifecycle host. The actor-side member then counts as drained; exact late facts and rejected effects settle with the host and cannot revive the actor.
RetireActorGraphAfterbounds actor-graph drain, not an uncancellable external activation. The root run future remains pending in a typed residual-drain state until transferred work resolves; an actor-side forced summary is not the final application result.- Forced retirement never fabricates an accepted shutdown, normal child stop, successful activation, completed job, or completed restart.
1. Stable worker proxy
[PX-IDENTITY] Identity and ownership
- One proxy provides one stable public worker-protocol identity.
- At most one worker incarnation is current behind that proxy.
- Every worker incarnation is a fresh child creation.
- The proxy alone owns the mapping from its internal worker nonce to current incarnation state and the routable worker recipient. Parent outcomes expose only opaque non-routable incarnation evidence.
- A replacement never replaces an actor at an existing address.
[PX-INIT] Initialization and installation
- Initialization is explicit and cannot run twice.
- The matching committed-creation fact proves only installed-but-not-ready; initialization effects still require exact settlement, and the initial worker remains unroutable until its later readiness outcome.
- Initial creation is tagged as ordinary birth, never inferred replacement.
- Worker creation is observed exactly once.
- Worker termination is observed for the exact created incarnation.
- A rejected initial creation leaves the proxy live but empty unless shutdown is already pending.
- A worker stop that becomes observable before its creation-resolution fact is either joined correctly in either order or made impossible by the later architecture; it can never result in a dead worker becoming ready.
- A rejected creation and an authoritative stop for the supposedly rejected incarnation form a typed contradiction retaining both facts.
[PX-ACTIVATE] Worker activation
- Successful worker creation moves the proxy to an installed-and-initializing phase. Only complete initialization-action settlement yields the exact activation permit and moves it to activating; even immediate activation cannot bypass that phase.
- A reported-ready fact makes only the exact installed incarnation routable.
- Activation rejection drains the exact installed incarnation, then leaves the stable proxy live and empty and returns the exact rejection plus terminal or forced drain fact to its owner.
- Rejection of the
BeginActivationaction preserves the complete unaccepted permit, plan, and request reason, drains the installed incarnation, and is a distinct proxy outcome from rejection by an accepted activation plan. - Initialization-fold rejection and host rejection leave the stable proxy live and empty without interpreting initialization effects. Post-commit initialization-effect rejection first drains the exact installed incarnation, then reports its rejection plus terminal-or-forced drain fact and leaves the proxy empty. All are distinct from activation rejection and none starts activation.
- Worker stop before readiness reports one stopped-during-activation outcome;
it is never reported as
Startedfollowed by an inferred stop. - Replacement is reported as completed only after the replacement incarnation is ready, not merely installed.
- Shutdown during activation resolves the exact activation/stop join and does not publish a transient ready capability.
- A command received while installed or activating is returned complete as unavailable.
- “Atomic installation/activation outcome” means one proxy fold commits one
state and one
Actionsvalue; it makes no cross-actor atomic-delivery claim.
[PX-ROUTE] Command routing and unavailability
- In the ready phase, one admitted command produces one delivery to the exact current worker incarnation.
- No command is delivered to a prior, pending, rejected, stopped, stopping, or shutdown worker incarnation.
- In every non-ready live phase, the complete original sender and command are returned exactly once as expected unavailability.
- Unavailability distinguishes at least initial installation, replacement, empty, stopping-for-replacement, and shutdown phases sufficiently for an owner to choose retry or rejection without guessing.
- Creation rejection is expected unavailability, not an actor crash.
[PX-REPLACE] Replacement
- A replacement request carries the complete new worker definition.
- Only one replacement attempt is owned at a time.
- An overlapping replacement is rejected with the submitted definition intact.
- If a worker is running, replacement first requests shutdown of that exact incarnation.
- If the proxy is empty after a known prior incarnation, replacement can stage a fresh successor immediately.
- A replacement request reserves all locally fallible correlation state before asking the old worker to stop.
- The fresh creation is explicitly tagged with the exact incarnation it replaces.
- Worker-stop reporting and replacement creation effects are both preserved when emitted by the same transition.
- A rejected replacement leaves the proxy empty and preserves the last successfully installed incarnation as provenance for a later attempt.
- A successful replacement becomes routable only after its exact activation resolves ready; committed creation alone remains non-routable.
- Stale creation and stop facts cannot affect a later attempt.
[PX-STOP] Worker termination
- A spontaneous stop of the exact current worker is reported once and makes the proxy empty.
- A stale or duplicate stop is rejected without state change.
- A stop expected by replacement is reported once and cannot initiate a second replacement.
- Worker stop and creation resolution are order-independent where the runtime can deliver them in either order.
[PX-SHUTDOWN] Proxy shutdown
- Shutdown while ready asks the exact worker to stop and waits for its stop fact.
- Shutdown while worker creation is pending waits for the creation result.
- If that creation commits, the exact worker is stopped once; if rejected, the proxy terminates without a shutdown request.
- Shutdown while replacement is stopping the old worker cancels the not-yet- created replacement and continues draining the old worker.
- Shutdown while replacement creation is pending resolves that creation and drains any committed incarnation.
- Shutdown after an already observed worker stop never sends to the dead incarnation.
- A shutdown rejection is accepted only for the exact pending worker shutdown and preserves its typed reason.
- The proxy terminates normally only after it owns no live or unresolved worker.
- Rejection of a parent report during drain preserves the complete report and route rejection through the configured diagnostic/error path; it does not reopen the proxy or repeat worker shutdown.
2. Fixed supervisor
[FS-BUILD] Definition and construction
- Construction requires a worker factory, at least one semantic child role, a complete recovery policy, and a topology-failure reaction.
- An incomplete definition cannot implement
Behavioror callbuild. - Duplicate semantic roles are rejected before initialization.
- An empty fixed fleet cannot be built.
- Initial factory rejection is typed and occurs before any initialization action is emitted.
- One factory constructs both initial and replacement workers for each role.
- A homogeneous fleet uses one worker behavior type.
- A heterogeneous fleet may use one closed worker-behavior sum only when every variant implements the same public protocol. Different public protocols are separate typed supervisor instances unless a later law proves a capability-safe heterogeneous product; they are never collapsed to one weak common envelope.
- Application actors and application births remain outside supervisor ownership; the supervisor never adopts unrelated child occurrences.
- Lifecycle publication is an explicit route-selected capability, not a hard-coded logical recipient. Logical, established, and mixed routes retain their existing static hosting obligations.
- A supervisor whose children are autonomous can be built without a lifecycle consumer and without supplying a no-op route; query and diagnostic laws must still remain truthful.
- Construction selects an activation contract, activation-authorization limit,
ActorDrainPolicy, and diagnostic disposition (DeliverToorTerminate). These are semantic requirements, not optional callbacks; lifecycle-event publication remains the independent optional capability. - When lifecycle publication is selected,
StartedandRestartedcarry the exact stable proxy capability needed to communicate with the ready role. They never carry a worker recipient; worker provenance remains private supervisor correlation evidence.
[FS-TOPOLOGY] Stable child topology
- Exactly one stable proxy is staged for every declared role, in declaration order.
- Every staged proxy has exact creation and termination observation.
- A role is associated with its proxy through supervisor-owned state, never by converting the role into a nonce.
- A role becomes available only after its proxy is committed, its
install/replace input is accepted, and that exact proxy emits its single
atomic
Readyoutcome. - After proxy commit, the supervisor may retain the worker definition in a
WaitingForActivationAuthorizationstate. It does not send the install input until one global activation slot is reserved for that role. - The supervisor owns at most its configured positive number of unresolved proxy install/replace operations. Waiting roles are authorized in declaration order and retain their definitions without entering the proxy.
- The exact established proxy capability is retained and can be reported to the supervisor's owner.
- Stable-proxy creation rejection cannot fabricate a worker, replacement, or availability outcome.
- Stable-proxy stop retires only the matching role unless the configured failure reaction stops the complete supervisor.
- A stopped proxy is never sent another worker or shutdown command.
- Facts for application children, foreign proxies, or stale proxy generations are returned or rejected complete without changing owned topology.
[FS-FACTS] Worker lifecycle facts
- Worker readiness/stop reports are accepted only from the exact owned proxy and exact pending supervisor operation. Replacement outcomes additionally match their nested prior-incarnation evidence; no supervisor recovery ticket is injected into the proxy protocol. Worker creation, initialization, and activation progress remain proxy-private; the supervisor consumes only the proxy's atomic outcome.
- Initial worker creation rejection is reported once and leaves no routable worker.
- Replacement creation rejection is reported once and is not called a completed restart.
- Duplicate, stale, malformed, foreign, and wrong-provenance reports do not advance the role.
- Authoritative facts that may arrive in either order form an exactly-once join. Install-action settlement is discharged before any later proxy report, so that specific order is causal rather than guessed.
- A contradictory stable-creation, worker-creation, worker-stop, or proxy-stop combination returns all conflicting facts without consuming the valid side of the join.
- Expected command-unavailability reports from an owned proxy are relayed to the authored owner with role, sender, phase, and command intact.
[FS-OPERATE] Status, capability, and diagnostics
- The public management protocol provides a typed status query without exposing private member-state variants or structural paths.
- A status snapshot identifies every declared role and its supervisor-visible semantic phase: creating proxy, waiting for activation authorization, awaiting proxy outcome, ready, empty, recovering, stopping, or retired. It does not invent worker-creation or activation progress the proxy never reports.
- A ready role's snapshot or capability query may return its exact stable proxy capability; non-ready roles return a typed availability result rather than a sentinel or absent capability paired with flags.
- Request replies and durable lifecycle/diagnostic publication are separate route capabilities. A temporary query caller never silently becomes the owner of later events.
- Omitting lifecycle-event publication never omits diagnostics. If diagnostic
delivery is rejected,
[SH-DELIVERY]requires the complete diagnostic to remain owned by the typed rejection path; it is never discarded or reclassified as a successful lifecycle event. The exact return-state equation remains a shared delivery-design blocker. - Querying does not mutate recovery budget, role order, readiness, or child ownership.
[FS-ELIGIBILITY] Restart eligibility
- Permanent workers are eligible after normal or abnormal termination.
- Transient workers are eligible only after abnormal termination.
- Temporary workers are never automatically restarted.
- Ineligibility retires or leaves empty according to the documented topology policy and is not reported as budget failure.
- A supervision-failure exit is classified as abnormal for transient policy.
[FS-STRATEGY] Restart strategies
- One-for-one selects only the triggering role.
- One-for-all selects every currently restartable role.
- Rest-for-one selects the trigger and currently restartable roles declared after it.
- Selection uses declaration order and an immutable pre-transition snapshot.
- A role that is unresolved, retired, stopping, or already owned by an admitted replacement cannot be selected as though it were ready.
- An initially unresolved member required by a coordinated strategy is retained in the recovery decision until its exact proxy outcome resolves the readiness prerequisite; it is never silently excluded.
- Overlapping failures and duplicate worker stops cannot duplicate a selected replacement or charge a second decision accidentally.
- Every selected replacement preserves the concrete worker variant for its role.
[FS-BUDGET] Restart budget
- The budget applies to complete admitted replacement decisions, not individual mutation steps.
- A configured maximum of zero denies every otherwise eligible decision.
- The time window boundary is inclusive.
- Evidence older than the window is pruned before admission.
- Future-dated evidence is not incorrectly discarded by an out-of-order fact; interpreter-authored monotonic timestamps that regress are typed rejection.
- Rejected decisions do not partially charge the budget.
- Atomic multi-role decisions are either admitted and charged once or rejected without charge.
- Stored budget evidence remains bounded by the active window.
- Aged-out attempts make capacity available again.
- Denial reports the attempts in the window, requested replacement count, and configured maximum.
- Every budget timestamp is supplied by the interpreter's monotonic clock at the authoritative lifecycle fact; workers and callers cannot choose it.
[FS-TIMING] Restart timing
- Immediate timing satisfies only the timer prerequisite. A replacement input is emitted in the triggering transition only when readiness and activation authorization are also satisfied; otherwise the prepared definition waits.
- Constant, linear, and exponential delays are one-based, checked, bounded, and reject zero or inverted configuration.
- Delay arithmetic overflow is typed.
- Each role's admitted recovery count advances with checked arithmetic. The count has no implicit reset; exhaustion is a typed denial rather than wrap, saturation, panic, or a fabricated delay overflow.
- A delayed decision retains every prepared replacement definition until its exact timer fires or shutdown cancels it.
- Timer identity and generation are exact correlation data.
- A stale, duplicate, wrong-generation, or foreign timer cannot release a decision.
- Timer arrival, readiness resolution, and activation authorization are three independent prerequisites. Their closed eight-state sum joins every admissible order exactly once; no pair of booleans or incomplete four-state join represents it.
- An activation slot is reserved atomically with the replacement input and is released only by that proxy operation's atomic terminal outcome or by transferred forced-retirement ownership.
- A delayed multi-role batch emits each selected replacement once and in declaration order.
- Denied delayed replacement leaves the affected role retired or empty rather than stranded in an installing phase.
[FS-ATOMICITY] Factory and decision atomicity
- Every selected worker definition is prepared before the first replacement command or member-state commit.
- If any factory call rejects, no selected peer is partially reserved, stopped, replaced, or budget-charged.
- Factory rejection identifies the semantic role and retains the typed factory error.
- Replacement preparation never requires a no-op factory, callback, selector, or placeholder for an unaffected template.
[FS-FAILURE] Failure reaction
RetireMemberretires the exact role whose topology can no longer be preserved and leaves unrelated live roles operating.StopSupervisorbegins an orderly drain of every owned stable proxy and ultimately selects the advertised terminal result.- Budget denial, backoff exhaustion, factory rejection, stable proxy death, and worker creation rejection each enter the configured reaction through a typed diagnostic.
- The diagnostic is emitted before any terminal verdict in the same action.
[FS-SHUTDOWN] Fixed-supervisor shutdown
- Shutdown drains only supervisor-owned stable proxies.
- Unrelated application children and their creation facts remain untouched.
- Both established and still-installing proxies are included in the drain.
- A pending proxy installation is resolved without duplicate shutdown.
- Delayed replacement batches are cancelled and never emitted after shutdown.
- Final pending creation rejection completes shutdown even when ordinary failure policy would retire only one child.
- Shutdown rejection preserves the exact proxy and rejection reason.
- The final proxy stop selects normal supervisor termination after preserving all reports in that transition.
3. Dynamic supervisor
[DS-BUILD] Definition and protocol
- Construction requires an explicit policy for unexpected worker exit and a maximum retained-entry capacity.
- No fixed-child list, fixed factory, restart strategy, restart budget, or placeholder callback is required.
- The public management protocol has typed start, stop, replace, and query commands.
- Every command names a semantic management key and carries its concrete logical, established, or mixed reply route.
- Start and replace carry complete owned worker definitions.
- The management key is separate from every creator-local proxy and worker nonce.
- Construction selects entry capacity, activation, activation-authorization
limit, unexpected-exit,
ActorDrainPolicy, one durable lifecycle route, and one diagnostic disposition. No temporary request recipient becomes the durable lifecycle owner, and no no-op callback or dummy route represents diagnostic termination.
[DS-START] Start
- Start acceptance is distinct from committed installation.
- A new key reserves one dynamic membership entry and stages one fresh stable proxy.
StartAcceptedidentifies the management key but does not claim the proxy or worker is installed.Startedis emitted only after the exact stable proxy capability is committed and the initial worker reports ready.Startedreturns the exact established stable proxy recipient.- A duplicate key returns the submitted worker unchanged with
AlreadyExists. - Start while global shutdown is in progress returns the worker unchanged with
ShuttingDown. - Rejected stable-proxy creation returns a typed start failure and retires the reserved entry.
- Rejected initial-worker creation returns a typed start failure and drains or retires the empty proxy without fabricating availability.
- The start reply route is used only for the start request. The one durable lifecycle route selected on the supervisor builder owns every later realization, unexpected-exit, and proxy-unavailability event. Individual start requests cannot replace or transfer that owner.
[DS-QUERY] Query
- Query returns
Unknownfor an unknown key; absence is a semantic outcome, notNonepaired with another phase field. - A known key returns one exhaustive public phase: reserved, creating proxy,
waiting for activation authorization, awaiting proxy outcome, ready, empty,
stopping, replacing, cancelling, draining, or retiring. The supervisor does
not expose worker-creation or activation progress absent from
ProxyReport. - Query never changes membership or lifecycle state.
- Query during global shutdown reports the exact draining state rather than a guessed ready value.
[DS-STOP] Stop
- Stop of an available or empty key is acknowledged as accepted and requests shutdown of that exact stable proxy.
- Stop completion is reported only after the matching proxy exit.
- Unknown, already retired, already stopping, or otherwise unavailable keys return typed rejection without state change.
- Exact runtime shutdown rejection is returned with its capability reason and does not masquerade as admission rejection.
- Stop waits for the exact pending proxy creation when invoked during start; it cannot send to an uncommitted child.
[DS-REPLACE] Replace
- Replace is distinct from dynamic stop followed by a new key.
- Replace retains the stable proxy and requests one fresh worker incarnation.
- Acceptance is reported before realization.
- The submitted worker is returned intact when the key is unknown, stopping, replacing, retired, or globally shutting down.
Replacedis emitted only after the replacement incarnation is ready.- Replacement rejection preserves exact creation reason and does not report success.
- Replacement provenance names the exact prior worker incarnation.
- Stop and replace wait for their exact runtime facts and cannot consume one another's report.
[DS-EXIT] Unexpected exit and unavailability
- A matching unexpected worker stop makes the key empty before policy is applied.
KeepEmptypreserves the stable proxy for an explicit later replace.Retiredrains the stable proxy and retires the key.- A stale or foreign worker stop cannot vacate another key or incarnation.
- Every unavailable proxy command is returned to the durable lifecycle owner exactly once with key, sender, phase, and command intact.
- Unavailability is supported during installation, replacement, empty, stopping, and global shutdown phases.
[DS-RETENTION] Capacity, retirement, and key reuse
- Reserving a new key at capacity returns the submitted worker unchanged with a typed capacity rejection.
- Retired entries are removed after all proxy, activation, delivery, and shutdown facts for their exact entry generation are resolved.
- A removed key may be started again with a fresh entry generation and fresh proxy; it is never address reuse or actor replacement.
- Every delayed fact and private report carries enough exact generation and capability evidence that an earlier use of the same key cannot affect the new entry.
KeepEmptyentries continue to consume capacity; explicitStopis required to drain the proxy and release the key.- The table retains no permanent tombstone. A late fact for a removed generation is returned as a typed stale diagnostic with its complete payload.
[DS-CANCEL] Accepted-operation cancellation
- Start and replace acceptance return an opaque operation token distinct from
the management key and actor nonce. Its constructor and fields are private,
but the accepting caller can move the value into
Cancel. - Operation phase is exhaustive: proxy creation emitted with actor-owned definition, proxy committed while waiting for activation authorization with actor-owned definition, install input emitted, awaiting the proxy's atomic outcome, ready, cancelling, or shutdown-owned.
- Cancellation in a phase that still owns the definition returns it. Emitting an install/replacement input transfers the definition; no later outcome may claim to return it.
- Cancellation after definition transfer is logical. It suppresses
Started/Replaced, waits for delivery/creation/activation settlement, and drains any incarnation that commits or becomes ready. - Cancellation names that exact token and returns one exhaustive
CancellationReceipt:Returned,Pending,Committed,Cancelled,Stale, orDraining. - Cancelling start drains any committed proxy after all transferred install and activation ownership settles. A late ready incarnation is never advertised.
- Cancelling replacement never revives the old worker or reports replacement success. A late replacement incarnation is drained exactly once.
- Stop is a lifecycle operation on the entry, not an alias for cancelling an unrelated start or replace token.
[DS-SHUTDOWN] Dynamic-supervisor shutdown
- Global shutdown rejects new management mutation commands.
- Every known installing, available, empty, replacing, or stopping stable proxy is resolved and drained exactly once.
- Entries already removed from the table are absent and require no effect.
- Pending creation rejection counts as drained.
- The supervisor terminates normally only after every dynamic entry is retired.
- Outer shutdown composition can deliver the shutdown input and interpret all dynamic outcome and child lifecycle lanes without path-specific caller code.
4. FIFO worker pool
[FP-BUILD] Definition and construction
- Construction requires a worker factory, at least one unique semantic worker role, a complete recovery policy, a backlog capacity, an interruption policy, and FIFO distribution.
- An incomplete definition cannot implement
Behavioror callbuild. - A zero-worker pool is rejected before it can accept owned work.
- Duplicate worker roles are rejected before initialization.
- Factory rejection before activation emits no partial worker fleet.
- The worker public message protocol must accept the pool's exact assignment product; a wrong worker protocol fails statically.
- The customer reply protocol retains the exact job, result, role, admission, completion, and terminal-return outcomes selected by the pool.
- Construction or application hosting selects a typed operational-diagnostic disposition. Expected diagnostics cannot disappear into an unnamed lane or require a no-op callback.
- Construction also selects worker activation and
ActorDrainPolicy; installation alone is never the implicit activation policy.
[FP-TOPOLOGY] Worker topology and recovery
- The pool directly owns one fresh worker-incarnation sequence per declared semantic role; no stable proxy is required because workers are private to the stable pool actor.
- Initial worker creation, worker stop, replacement, budget, timing, failure, and shutdown are interpreted by the pool's own fold.
- Pool lifecycle handling is not delegated to a nested supervisor behavior or proxy whose events are redispatched.
- Fresh worker replacement preserves the semantic pool role and future dispatch eligibility without preserving a worker address.
- Every direct-worker member has exhaustive pre-ready phases for creation, installed initialization settlement, waiting for activation authorization, activation dispatched, and activating. Each phase owns only its exact definition, incarnation, settlement, permit, plan, or attempt.
- Each pool owns one positive activation-authorization limit, a declaration-order waiting-role queue, and exact occupied tickets. An installed worker may wait with its activation permit and plan until authorization; it is never silently counted as activating.
- Reserving a slot and emitting
BeginActivationare one commit. Mandatory action settlement establishes accepted activation before the next ordinary pool input; the exact ready, rejected, stopped, or forced-transfer outcome releases the slot once. - Installed, waiting-for-authorization, dispatched, or activating workers remain ineligible for dispatch until their exact readiness fact commits.
- Every transition that makes a worker ready first checks the applicable
backlog. It commits
Busyplus assignment of the oldest eligible queued job when one exists, and commitsIdleonly when none exists. - Post-commit initialization rejection, activation rejection, cancellation, and shutdown own distinct pre-ready drain causes; recovery or retirement begins only after exact terminal or forced-transfer settlement.
- Worker-incarnation replacement never changes the role used for assignment.
- Restart denial or irrecoverable worker loss cannot strand queued or assigned jobs.
[FP-ADMIT] Job admission
- Submission carries a complete owned job, concrete customer reply route, and caller-authored request correlation echoed by both admission outcomes. The request correlation is never trusted as pool job identity.
- The pool retains the customer route; it is never copied into the worker assignment or echoed back by worker code.
- After classifying shutdown, serviceability, and full backlog, the pool assigns a fresh job id and immutable admission ordinal without trusting a customer-supplied id as either internal correlation.
- If an eligible worker is idle, the job is accepted and assigned in the same transition.
- Assignment state is committed before the dispatch effect is exposed.
- If no worker is idle and backlog capacity remains, the job is accepted once and appended in FIFO order.
- Capacity is the new-admission waiting bound and counts only jobs currently in
the backlog; assigned work is owned by a busy worker state.
Retryis not a new admission and may temporarily place one formerly assigned job per worker beyond that bound, so the absolute queue bound is capacity plus roster size. - Capacity zero is valid and permits only immediate assignment.
- A full backlog returns the customer, reply recipient, and payload unchanged.
- Full/unserviceable rejection occurs before job allocation. Job-correlation and immediate-dispatch preparation failures are distinct typed rejections; failure does not silently fall back to queue admission.
- Admission during shutdown returns the owned job unchanged.
- A payload clone needed for dispatch occurs before admission state commits. Unwind cannot leave a recorded assignment or dequeue because no transition was returned, but caller ownership recovery is not promised across panic.
[FP-ASSIGN] Assignment
- Every dispatch carries a fresh opaque affine completion authority and owned job payload. The pool separately retains job id, customer route, exact worker role, and incarnation.
- Token fields and constructors are private. Worker code can only move the
token through
assignment.complete(result); it cannot construct a token or choose a customer destination. - A job has exactly one authoritative customer obligation and active
correlation at a time. Under
Retry, the pool retains the canonical obligation/payload while a worker owns a cloned execution payload; those are deliberately two Rust values but never two customer completions. - No job is simultaneously queued and assigned.
- No worker owns more than one assignment unless the worker protocol explicitly defines concurrency; the current template is single-assignment.
- Idle-worker selection uses a circular declaration-order cursor. Each assignment selects the first ready idle worker at or after the cursor, skipping unavailable roles, then moves the cursor to the next surviving role. Becoming idle does not jump a worker ahead of the cursor.
- Removing or retiring a worker does not reorder surviving FIFO jobs.
- A dispatch batch preserves FIFO job order even when internal worker entries are removed.
- Retry reinserts by immutable admission ordinal, not unconditionally at the front. Multiple interrupted workers therefore preserve their original admission order regardless of stop arrival order.
- No serviceable queued job may coexist with an eligible idle worker. This invariant is restored in the same fold that processes initial readiness, replacement readiness, retry recovery, or completion; a later submission therefore cannot jump ahead of older backlog.
- Rejected assignment delivery returns the exact worker command. A typed reunion consumes its affine completion authority with pool-retained evidence once before ordered reinsertion, then quarantines the exact worker for drain/recovery. This is safe return, not an at-least-once interruption; delivery settlement, completion, and worker stop may arrive in any order and join without a second job disposition.
[FP-COMPLETE] Completion
- A matching completion authority resolves to exactly one active pool-owned record containing assignment id, job id, customer route, worker role, and private worker-birth evidence. The locked parent report supplies only the creator-local child nonce, so matching combines that nonce with opaque authority-carried birth evidence; it does not claim the interpreter attaches an exact incarnation capability.
- Matching completion returns the result exactly once, releases that worker, and immediately dispatches the oldest eligible backlog job when present.
- Duplicate completion is stale and cannot emit a second customer outcome.
- Cross-worker completion is stale and cannot release either worker.
- Completion from an old worker incarnation is stale and preserves the current assignment.
- A stale completion returns the complete result through the separate operational-diagnostic disposition without mutating current ownership or sending another customer outcome.
- Completion and successor dispatch effects coexist in one action without loss or reordering.
- Completion may precede assignment-delivery settlement. Completion, settlement, and worker stop form one exhaustive order-independent join; no synchronous settlement guarantee is assumed.
[FP-INTERRUPT] Worker interruption
- A worker stop matching an idle worker makes only that exact worker unavailable and enters recovery.
- A worker stop matching a busy worker first resolves ownership of its exact assignment.
Failreturns the interrupted job exactly once with a typed interruption outcome.Retryretains the complete job for at-least-once execution and reinserts it by its immutable admission ordinal. Queue metadata retains prior role and a semantic interruption classification, never a second copy of the authoritative stop fact.- Retry is explicitly at-least-once processing: the worker may have performed application work before stopping, so the pool does not claim exactly-once execution or side effects.
- Clone preparation needed to retain a retry copy occurs before state commit.
If cloning unwinds, no new pool state or
Actionscommit exists; the design makes no promise that Rust unwind restores caller ownership or prevents the actor from failing. - Worker stop, assignment settlement, completion, and worker creation facts that can arrive in different orders join exactly once without losing, duplicating, or prematurely failing the assignment.
- Duplicate worker stop during replacement cannot cause a second retry or failure outcome.
- A returned assignment after restart exhaustion or shutdown is consumed as stale without a second customer outcome.
[FP-FAILURE] Retirement and failure
- An irrecoverably retired worker causes no future dispatch to that role.
- Backlog work that can run on other FIFO workers remains eligible.
- If no live or recoverable worker can ever serve queued work, every stranded job is returned exactly once with a typed terminal reason.
- Pool failure diagnostics and customer job outcomes remain separate named effect lanes.
- A supervision failure cannot be reconstructed from a missing worker or sequence arithmetic.
[FP-RETENTION] Correlation retention
- Every accepted queued or assigned job retains its customer route. Only an active assignment retains full completion correlation; queued jobs have no completion authority.
- Terminal customer outcome removes the active correlation after its delivery
attempt is emitted; any rejected delivery follows
[SH-DELIVERY]and is not reconstructed from a tombstone. - Completion tokens and assignment/job ids are never reused before typed exhaustion, so an old token cannot match a later job.
- The pool retains no unbounded
ReturnedAssignmenttombstone table. - A completion for a non-active token is an unknown/stale operational diagnostic carrying the result; it cannot select a customer.
[FP-SHUTDOWN] FIFO-pool shutdown
- Shutdown immediately removes and returns every queued job exactly once.
- Every assigned job is returned exactly once according to the documented shutdown outcome; later completion is stale.
- No retry or successor dispatch is emitted after shutdown begins.
- Pending worker creation and delayed recovery are resolved or cancelled without losing jobs.
- Every owned worker incarnation is drained exactly once.
- Worker stop and completion in either admissible order complete the drain without duplicate job outcomes.
- The pool terminates normally only after every job has been returned or completed and every owned worker is drained.
5. Keyed worker pool
Keyed pooling reuses only the FIFO laws listed here. No unlisted FIFO requirement is inherited implicitly.
| Concern | Shared FIFO law | Keyed substitution or restriction |
|---|---|---|
| Direct workers | Fresh direct-worker creation, exact readiness, one active assignment per worker, and complete lifecycle/drain ownership apply. | Each worker has one immutable semantic role; no proxy or supervisor state is reused. |
| Customer ownership | One authoritative obligation, correlated admission outcome, opaque completion authority, typed assignment-delivery settlement, and one terminal customer outcome apply. | Accepted work additionally retains non-authorizing admitted-binding evidence { generation, role }; it does not retain or clone the submitted key. |
| Queue order | Immutable admission ordinal and retry reinsertion by that ordinal apply. | There is one FIFO queue per role. A role's retry may add at most its one formerly active assignment beyond that role's admission bound, for an absolute per-role bound of capacity + 1. There is no global retry-overflow equation. |
| Dispatch eligibility | Only exact ready-idle workers are immediately assignable, and readiness cannot leave older serviceable work queued. | The retained role selects the only candidate worker. The no-serviceable-backlog invariant is scoped to that role; another idle role cannot serve it. |
| Worker selection | None. | There is no circular worker cursor or cross-worker scan. Binding/selection chooses a role before dispatch. |
| Capacity | Zero backlog means immediate assignment only. | Backlog capacity is per role, and binding-table capacity is a separate positive bound. There is no global backlog capacity. |
| Recovery and interruption | Recovery eligibility, exact lifecycle correlation, Fail, explicit at-least-once Retry, and complete stranded-work return apply as value laws. | Recovery and retry preserve the admitted role. Rebalance cannot retarget accepted work. |
| Shutdown and settlement | Admission closes, every accepted job settles once, workers drain exactly once, and action/rejection settlement remains explicit. | Every role partition is extracted, all bindings are removed, and late keyed management/completion facts are generation-classified. |
[KP-BUILD] Definition and selector
- Construction requires one concrete statically dispatched selector.
- FIFO construction requires no selector, placeholder selector, or no-op policy.
- Every keyed submission carries its semantic key separately from its owned payload; the payload implements no pool-specific key trait.
- The selector maps the submitted key to a semantic worker role, never to an actor nonce or address.
- Captured selector state remains concrete and statically dispatched.
- Selection is evaluated once at admission and its chosen role is retained by accepted work.
- Construction selects a per-role backlog capacity and a maximum number of retained key bindings. No ambiguous global backlog capacity is inferred.
[KP-AFFINITY] Affinity and admission
- A valid binding routes new work only to the selected role's worker incarnations.
- Fresh worker-incarnation replacement preserves affinity automatically.
- If the selected role is busy, only that role's backlog capacity determines targeted admission.
- Work bound to one role is never stolen by another idle role.
- A retired selected role refuses new work with the complete owned job.
- Committing permanent unavailability for one affinity role terminates that role's queued and assigned work without disturbing live roles or their jobs.
- Target eligibility is an exhaustive admission sum:
AssignableNow,BacklogAdmissible, orUnavailable. The total projection covers every private member variant and every pre-ready drain disposition; no state falls through to a default branch. - Ready-idle is
AssignableNow. Prepared, creating, initializing, waiting-for-authorization, activation-dispatched, activating, ready-busy, and admitted recovery areBacklogAdmissible. Stopping, retired, and shutdown-owned areUnavailable.DrainingPreReadyprojects by its retained exhaustive after-drain sum: recover or retire. Irrecoverability is a transition cause that commits a permanently unavailable phase, not a second retained member state. - A key with no retained binding is selected once by the concrete selector and bound atomically with accepted admission when binding capacity permits and the target is either immediately assignable or backlog-admissible with remaining per-role capacity. Rejection, including zero-capacity backlog for a non-idle target, retains neither the job nor a new binding.
[KP-REBALANCE] Rebalance
- Rebalance is an explicit typed command.
- Every rebalance carries
Absent | Exact(binding_generation)expectation. Rebalance of an absent key requiresAbsent; rebalance of an existing key requires its exact current generation. - A valid rebalance changes future admission only.
- Already queued and assigned jobs retain the role chosen when they were accepted.
- Rebalance target eligibility is a separate exhaustive projection. Prepared,
creating, initializing, waiting-for-authorization, activation-dispatched,
activating, ready-idle, ready-busy, admitted recovery, and
pre-ready-draining-with-
ResumeRecoveryare bindable. Stopping, retired, terminal pre-ready drain, and shutdown-owned are unavailable. An unknown or unavailable target returns the complete request and preserves the prior binding. - An explicitly unbound key can be bound by a valid rebalance.
- A role-changing rebalance reserves a fresh generation before atomically replacing the old binding. A same-role rebalance is an accepted no-op that preserves the current generation.
- Stale absence or generation expectation returns the complete command unchanged and cannot validate or mutate a later binding.
- Repeated and short rebalance sequences match an independent binding model.
- Rebalance never creates, replaces, revives, or proves a worker actor.
- Explicit
Unbindremoves only the future-admission binding. Already queued and assigned work retains its selected role. - Unbind of an existing key requires its exact current generation. A stale absence or generation expectation returns the complete command unchanged.
- Unbinding an absent key returns typed
AlreadyUnbound { key }and leaves state unchanged. Idempotence is an explicit protocol outcome, never guessed from map mutation success.
[KP-END] Keyed completion, interruption, and shutdown
- Completion correlation includes the retained affinity role in addition to assignment, job, and incarnation.
- Retry returns interrupted work to the same retained role unless a separate explicit policy says otherwise; rebalance does not retarget it.
- A permanently retired role's returned work cannot later be revived by a stale lifecycle or completion fact.
- Shutdown returns jobs from every affinity partition exactly once and drains every owned worker incarnation.
- Assignment, customer response, lifecycle, recovery timer, and shutdown lanes remain independently named and are all interpreted at the same actor path.
[KP-RETENTION] Binding retention
- Binding capacity counts only currently retained key-to-role bindings, not queued jobs or active assignments.
- Rebalance does not allocate a second binding entry for an existing key.
- Unbind releases capacity immediately because queued/assigned jobs retain their role independently.
- A later submission for the same unbound key invokes the selector again and creates a new binding generation.
- Late rebalance/unbind outcomes are correlated to the binding generation so they cannot modify a later reuse of the same key.
- The transition committing permanent unavailability atomically unbinds every
retained binding to that role, releases their binding capacity, and emits
one typed diagnostic per removed key/generation.
Irrecoverableis one cause for that transition, not a retained terminal phase besideRetired. Temporary recovery and pre-ready drain withResumeRecoveryretain bindings. Already queued or assigned jobs retain admitted-binding evidence needed for their single terminal outcome; no permanently unusable binding remains.
[API] Builder and public API requirements
- Builders encode missing versus selected semantic choices in their types.
- Builder values do not implement
Behavior. - Only complete built definitions implement
Behavior. - Value-dependent invalidity, such as duplicate roles or invalid delay bounds, returns a typed build error.
- Builders never request child nonces, timer ids, assignment ids, job ids, or actor addresses from the caller.
- Fixed, dynamic, FIFO, and keyed construction expose only choices meaningful to that template.
- No valid construction requires a no-op callback, dummy selector, empty factory, marker route, explicit output alias, wrapper path, or generic type annotation whose only purpose is satisfying internal machinery.
- Resulting behavior types are inferable from ordinary factory, role, selector, worker, and reply values.
- Public names describe semantic state, policy, command, result, or error—not structural positions such as inner depth or wrapper ownership.
- Each template has one documented fluent order. Typestate exposes only the next meaningful choices; arbitrary equivalent call permutations are not a second advertised API.
- Ordinary construction requires neither explicit reply-protocol aliases nor turbofish that repeats types already present in factory, worker, selector, route, or command values.
- Infallible and fallible worker construction are both truthful: an
infallible factory does not manufacture an
Option/error branch, and a fallible factory preserves its exact typed error. - Worker code completes a pool assignment with
assignment.complete(result). It never namesReportToParent, a parent path, customer recipient, assignment id, or send-product lane. - Every template has one complete copy-paste example covering imports, worker definition, construction, hosting, activation/readiness, command or job submission, replies/diagnostics, and shutdown.
- Compiler diagnostics are measured with focused compile-pass/fail fixtures; the typestate syntax remains a hypothesis until those fixtures show the error at the missing semantic choice rather than in nested associated types.
[EFFECT] Effect and interpreter requirements
Each template exposes one named effect product containing only lanes it can actually emit. Across the catalogue these include:
- worker-command deliveries;
- private parent-to-proxy installation and replacement inputs;
- proxy and worker creation observation;
- proxy and worker termination observation;
- exact child shutdown requests;
- restart scheduling requests;
- parent lifecycle and unavailability reports;
- dynamic management replies;
- pool worker assignments;
- pool customer outcomes; and
- supervision or pool terminal diagnostics.
Requirements:
- Every lane has a concrete static interpreter requirement.
- Interpretation order is documented and tested.
- Same-action creation is committed before any bundled creation observation, child-termination observation, child delivery, child input, or child shutdown.
- Every current same-action occurrence-dependent operation is owned by one
staged creation-scoped bundle:
ObserveCreation,ObserveEstablishedCreation,ObserveChild,ChildDelivery,ChildInput, andShutdownChild. The bundle explicitly selects direct creation → occurrence effects or creation → required observations → occurrence effects. Independent effects remain outside; no dependency is inferred from lane position, wrapper path, role, or lookup. - Creation rejection produces one authoritative prerequisite settlement and
returns every bundled observation/delivery/input/shutdown untouched as
NotAttempted. After accepted creation, every required observation is attempted in named lane/item order even when another rejects, and every rejection is preserved independently. All later occurrence effects becomeNotAttemptedwith the first rejected observation in that order as their deterministicblocked_byprerequisite. A future occurrence-dependent operation must extend the closed bundle and tests before same-action use. Independent effects still run. ObserveEstablishedCreationbelongs to the bundle because its input is the pre-commit child route and its result supplies the exact committed capability. Established delivery, observation, and shutdown cannot enter because they require that capability as input; logical delivery is occurrence-independent and child reports originate from the child. These inclusions and exclusions are part of the completeness proof.- One lane's interpreter failure does not allow a later lane to pretend an
earlier required effect succeeded. A dependent item settles as
NotAttempted; an independent item is still interpreted. - Append and reducer operations preserve every lane, creation, and terminal verdict exactly once.
- Fixed and heterogeneous birth products dispatch to concrete installers without erased runtime selection.
- A minimal interpreter witness consumes every lane of every atomic template.
- Every delivery lane also has an interpreter-rejection witness satisfying
[SH-DELIVERY]; a success-only interpreter does not implement the template.
[VERIFY] Required verification per template
Each atomic template must have:
- focused example tests for every transition above;
- compile-pass tests for complete builder syntax;
- compile-fail tests for incomplete builders and protocol mismatch;
- complete-action assertions, not effect counts alone;
- both event orders for every legitimately unordered fact join;
- independent model tests using vocabulary different from production state;
- exhaustive small-state tests for lifecycle, budget, backlog, binding, and shutdown boundaries;
- property tests asserting invariants after every generated step;
- fuzz coverage for stale, duplicate, contradictory, overlap, and shutdown sequences;
- debug and optimized regressions for lifecycle facts that must not be accepted twice; and
- an interpreter-level witness for initialization ordering and every named lane.
- readiness/activation races, delivery rejection, bounded retention, key reuse, and deadline-retirement tests where those laws apply.
- exhaustive keyed-admission projection over every member and after-drain variant, with no wildcard model branch;
- readiness, recovery, and completion sequences proving that serviceable backlog never coexists with eligible idle capacity;
- a root-driver trace in which actor-side forced retirement occurs before late activation readiness, followed by exact incarnation drain and only then the final runner result;
- compile-pass/fail terminal lifts for heterogeneous children, duplicate role values, and one/two/eight wrapper layers with a recorded diagnostic-size bound; and
- interpreter traces for accepted/rejected creation and required observation,
covering
ObserveCreation,ObserveEstablishedCreation,ObserveChild, child delivery, child input, and child shutdown while proving dependentNotAttemptedvalues and continued independent effects; the traces must include multiple required-observation rejections and prove that all are preserved while downstream items reference the first rejection in named interpretation order.
The original defect must be demonstrably reproduced by each regression it is intended to prevent. A green build without that counterexample is not evidence that the feature is implemented.
[NONFEATURE] Existing surfaces that are not features
The replacement need not preserve these implementation artifacts:
- arbitrary
Behaviorsupervision wrappers; - fluent extension methods;
BehaviorLayerconstruction of supervisors or pools;FixedFleetOwnership, slot products, ownership redispatch, or positional nesting paths;- a pool implemented by nesting or forwarding another pool;
- duplicated flattened copies of another template's effect product;
- a public
utilsmodule; - a mandatory policy whose only legitimate implementation is a no-op; or
- compatibility aliases that keep both old and new architectures alive.
- stable proxy children inside a pool, because the stable pool actor already owns private worker incarnation routing.
Their observable laws are included above where required. The artifacts themselves are deleted after the replacement templates and migrations are proven.
Minimal type equations and actor relationships (engineering record)
This document derives types from the feature catalogue. It deliberately does not turn every phase, error reason, effect lane, builder axis, or compiler obligation into a public type.
The previous 64-name inventory is withdrawn. It confused four categories:
- semantic values a user deliberately chooses or observes;
- behavior definitions and public protocols;
- private state-machine proof types; and
- generated/interpreter plumbing.
Only the first two categories are candidates for the ordinary public API. Private sums remain real sums—they are not converted to flags—but their names do not become concepts users must import.
This document is not a second runtime-state specification. The sole normative
ownership equations and legal transitions are in
atomic-actor-solution.md. If a candidate public
protocol described here cannot be projected from those equations without
losing ownership or provenance, the candidate is invalid; the inventory does
not amend the runtime state machine.
Minimization test
A new public type is allowed only when all four answers are concrete:
- Which distinct semantic state, outcome, or capability does it own?
- Which invalid exchange or transition becomes impossible because it is a separate type?
- Why can an existing core type or a variant of an existing actor protocol not express it?
- Which caller-written type, alias, marker, route, or callback does it remove?
The following do not justify a public type: naming a generic parameter, shortening a nested type, satisfying a bound, forwarding an event, hiding a structural path, or making rustdoc look symmetrical.
Visibility budget
There are four visibility bands.
| Band | Contents | Ordinary user names it? |
|---|---|---|
| Public semantic | actor builder/definition, command/outcome sums, real policy sums | Yes |
| Public but inferred | protocol identity and typestate return forms whose exact spelling is compiler-prototype dependent | Normally no |
| Crate-private semantic | exhaustive runtime phases, joins, correlations, tickets, drain states | No |
| Generated/interpreter | named effect products, occurrence positions, request lanes, lowering traits | No |
If the compile prototype makes the last two bands appear in ordinary source, the DevX is rejected. They are not promoted to public concepts to make the prototype compile.
Existing core: seven retained concepts
The exhaustive decision is in
atomic-actor-retained-core.md. The atomic
actors reuse only these irreducible concepts: Protocol, Behavior,
Actions, Step, logical/exact recipients, staged CreateChild, and
TerminalOutcome. Current support and interpreter carrier types may remain
internally, but the actor modules do not duplicate or re-export them.
Shared public semantic values
These are shared only because every consuming actor gives them the same law. There is no shared runtime ownership engine.
Recovery
Permanent { strategy, limit, release }
Transient { strategy, limit, release }
Temporary
- Owns the complete eligibility and replacement decision policy.
Temporarycannot accidentally carry unused restart settings.- Used by fixed supervisor, FIFO pool, and keyed pool.
- Dynamic supervision does not use it; replacement there is an explicit management operation.
Strategy
OneForOne | OneForAll | RestForOne
- Selects an ordered candidate set from one immutable topology snapshot.
- Shared by the three actors that use
Recovery.
RestartLimit
{ maximum: u32, window: Duration }
- Owns atomic sliding-window admission.
- The catalogue defines the exact zero, boundary, monotonic-time, and out-of-order laws; no boolean “within limit” escapes the fold.
RestartRelease
Immediate
Constant { delay }
Linear { initial, maximum }
Exponential { initial, maximum }
- Combines timing and backoff in one sum; there is no optional backoff or separate timing flag.
- Checked construction rejects zero delay and maximum below initial.
- Delay arithmetic is checked and its failure is a typed denial outcome.
ActorDrainPolicy
WaitForActorGraph
RetireActorGraphAfter { deadline }
WaitForActorGraphmakes potentially indefinite actor-graph waiting an explicit choice.RetireActorGraphAfterschedules an interpreter-clock deadline and produces exact forced-retirement facts for every unresolved child/job. It bounds only actor-graph retirement; transferred uncancellable work keeps the root run future alive.- If later requirements prove more than one deadline-retirement action, this sum gains semantic variants; it does not gain booleans.
This is not a process-exit policy. Final application completion separately requires residual root settlement to become empty. No bounded process-exit or emergency-abandonment policy is selected.
Activation and diagnostic choices
Activation has two semantic modes: immediate readiness after initialization,
or a concrete statically dispatched activation plan resolved through the
exact request/fact capability in the solution. The plan type is inferred from
the value supplied to .activation(...); the inventory does not authorize a
boxed callback, erased future, universal activation trait object, or a public
type per activation phase. Nor does it authorize
Activation<Plan>::Immediate, which would leave an unused plan type to infer;
immediate and plan-bearing builder states must both remain inferred.
The activation-authorization limit uses ActivationPolicy, constructed from a
plain usize with typed ZeroCapacity rejection. Its one user-facing law is
“at most this many owner authorizations may be unresolved.” A supervisor
conservatively records authorization when it emits the opaque proxy install
input because the proxy exposes no progress facts. A direct pool records it
when initialization has settled and it emits BeginActivation. The counting
law is identical; the template-specific authorization boundary is explicit.
A separate activation-limit spelling is not justified. Nor is there a second
ActivationAuthorization capability: the exact
installation-issued permit authorizes BeginActivation, while crate-private
waiting variants and occupied tickets prove the concurrency bound.
Diagnostic disposition is one real semantic sum because its alternatives carry different capabilities:
Diagnostics<Route> = DeliverTo(Route) | Terminate
The notation does not authorize a literal generic enum: its Terminate
variant would leave Route unconstrained. The builder must infer a
route-bearing concrete state for DeliverTo or a route-free concrete state
for Terminate; whether either policy type is an ordinary public name remains
a compile-prototype question. It replaces mandatory .diagnostics_to(...);
absence is not encoded by an Option, and autonomous actors terminate on a
diagnostic without a dummy route or type annotation.
Per-item action settlements, activation attempts, installed incarnations, and rejection products are interpreter-facing typed capabilities. Ordinary application code does not construct them, but they are not erased or dynamically dispatched.
Open terminal and dependency boundary
TerminalSettlement now has a semantic equation but is not yet an authorized
universal public type. One concrete application/root terminal sum must receive
total statically dispatched lifts from every heterogeneous child and wrapper
terminal sum while preserving exact provenance. Duplicate semantic roles do
not identify a source. The solution's Static terminal-projection equation
is normative; its Rust spelling remains Open until wrapper-depth diagnostics
and heterogeneous lifting are compile-proven.
NonEmptyResidualSettlement is crate-private root-run state. It keeps exact
external activation and late-drain ownership alive after the actor graph is
forcibly retired. The final runner result does not exist until that state is
empty; ordinary users receive no cloneable residual report or topology
product.
CreationBundle<CreateRequest, AfterCreation> and
LoweredAction<Independent, Creations> are semantic names for the selected
interpreter dependency equation, not approved ordinary imports. Each bundle
owns one creation and the closed current occurrence-dependent set:
ObserveCreation, ObserveEstablishedCreation, ObserveChild,
ChildDelivery, ChildInput, and ShutdownChild. Its sum selects effects
that depend directly on creation or also on required-observation acceptance.
Every required observation is attempted in named lane/item order; if several
reject, every rejection is retained and later NotAttempted effects reference
the first rejection in that order. This replaces positional dependency
discovery and runtime lookup. A core prototype must determine whether existing
named Actions products lower truthfully or need a minimal interpreter-facing
product change.
Why there are no shared public builder markers
Missing/present and empty/non-empty proofs are typestate implementation
details. One internal proof family can serve every builder, but ordinary users
must not import Unset, Set, NoMembers, Members, WithEvents, or a type
per axis. The compile-pass/fail prototype must discover whether Rust can keep
those states inferred. If not, the builder spelling is still open.
1. Stable proxy
Current public surface
StableProxy<Worker, Plan>— the behavior definition and stable service identity.StableProxy::immediate()andStableProxy::activated()are its two inferred constructions.ActivationPlan,ImmediateActivation, andWorkerSubmission— application worker definition and its concrete activation work.ProxyPhase,ProxyOutcome,InitialWorkerOutcome,ReplacementOutcome, andProxyDiagnostic— owner-observable lifecycle and diagnostic products.
The service protocol is exactly the worker protocol. Owner control, initialization and activation requests, effect products, operation correlation, drain custody, and settlement inputs remain technically public only where the associated Behavior/interpreter contract requires them; they are doc-hidden and are not part of Bombay's ordinary facade.
Runtime ownership reference
The proxy performs one direct fold. Its complete Dormant, creation/stop,
initialization/stop, activation/stop, ready, replacement, and drain equations are
normative only in the solution's Stable worker proxy solution. In
particular, the worker definition is transferred when the creation action is
emitted; no inventory shorthand may place it back in a later creation state.
“Atomic” means one local state/action commit, not atomic cross-actor delivery.
2. Fixed supervisor
Current public surface
fixed(...)andFixedSupervisor<…>— the one inferred construction and resulting behavior. The returnedFixedBuilderis doc-hidden because ordinary source never names it.OrderedRolesandDuplicateRole— validated non-empty unique topology.ActivationPolicy,Recovery,FailureReaction, andActorDrainPolicy— every required supervisor policy.FixedCommand— status, capability, and shutdown management.FixedSnapshot— read-only role/order/phase/availability projection.FixedLifecycle— durable lifecycle outcomes only when.publish_lifecycle(route)is selected.FixedDiagnostic— exact operational failures selected byDiagnosticDisposition.InitialWorkerRejection— shared construction-only worker rejection with the function, prepared prefix, exact role/reason, and untouched suffix.FixedConstructionRejected— fixed policies plus oneInitialWorkerRejection.FailureReaction—RetireMember | StopSupervisor; one exhaustive response when fixed topology can no longer be preserved.
Lifecycle publication is optional. An autonomous supervisor is a complete valid actor and provides no discard callback or dummy recipient.
The selected names are FixedCommand, FixedSnapshot, FixedLifecycle, and
FixedDiagnostic. Construction and preparation rejections retain their exact
typed values. Request products, internal events, lifecycle-route traits, and
worker-preparation action products are doc-hidden interpreter contracts.
Builder semantic state
factory
ordered non-empty role declarations
Recovery
fixed-topology failure reaction: RetireMember | StopSupervisor
activation policy
positive activation-authorization limit
ActorDrainPolicy
diagnostic disposition
optional lifecycle DeliveryRoute
Only typestate proof markers are internal. Duplicate roles and factory
rejection are value failures returned by build; construction performs no
actor effect.
Runtime ownership reference
The solution's Fixed supervisor solution is the sole member, recovery transaction, activation-authorization, and drain equation. The decision is prepared completely before the supervisor commits any member reservation. Factory rejection, budget denial, overlap, clock regression, or timer exhaustion returns every value still owned at that phase in one typed outcome.
Different workers may be different behavior variants behind one closed worker sum only when they expose one common public service protocol. Workers with different public protocols require separate supervisors; the design does not erase them into a universal envelope.
3. Dynamic supervisor
Current public surface
DynamicSupervisor<…>— one keyed behavior that creates proxies.DynamicCommand<Key, Worker>—Start,Replace,Stop,Query, andCancel, each retaining submitted values.WorkerChangeReceipt<Key>andWorkerChangeRejection<Key, Worker, Reason>— the shared start/replace result products; the rejection reason remains operation-specific.CancellationReceipt<Worker>— the exhaustive affine cancellation result.QueryReply<Key>— the exhaustive known/unknown read-only result.DynamicLifecycle<Key, Worker, Plan>— durable later readiness, unavailability, cancellation, and retirement reports sent to the configured lifecycle owner.DynamicDiagnostic<Key, Worker, Plan>— exact operational contradictions and runtime rejections handled by the independently selected diagnostic policy.UnexpectedExit—KeepEmpty | Retire; the complete policy after an unrequested exact worker stop.EntryCapacity— the positive maximum retained keyed entries. It is distinct fromActivationPolicy, so exchanging the two policies does not compile.
Request replies and durable lifecycle events use distinct protocol capabilities. A temporary request caller never becomes the long-lived owner by accident.
Command and operation sums
Start { key, worker, reply_to }
Stop { key, reply_to }
Replace { key, worker, reply_to }
Query { key, reply_to }
Cancel { operation, reply_to }
Start and replace use the same result equation without a naming alias:
Result<WorkerChangeReceipt<Key>, WorkerChangeRejection<Key, Worker, Reason>>
Accepted operations return a supervisor-issued opaque cancellation authority.
Cancellation is then exhaustive within CancellationReceipt:
Returned { authority, worker }
Pending { authority, phase }
Committed { authority, resulting_phase }
Cancelled { authority }
Stale { authority }
Draining { authority }
Runtime ownership reference
AA-20 and the normalized dynamic document are the sole entry-state and cancellation ownership equation. Transaction-local preparation reserves every correlation before committing the creating-proxy entry and its creation action; there is no stored or queryable reservation phase. The state distinguishes emitted proxy creation, committed proxy waiting for activation authorization, emitted install, awaiting one atomic proxy outcome, readiness, cancellation, drain, and retirement. Worker creation and activation remain proxy-private. Only phases that still own a worker definition may return it.
The table has a configured maximum. Stop drains a ready or empty entry and
eventually removes it; reuse of the same key creates a fresh generation. Facts
from earlier generations are stale diagnostics and cannot mutate the new entry.
No permanent tombstone is required.
4. Shared pool values
Only values with identical FIFO and keyed meaning are shared.
Assignment<Job>
{ payload, opaque_completion_authority }
- Delivered to one exact worker incarnation.
- Token fields and construction are private.
- Consuming
assignment.complete(result)creates the only valid completion. - Issuance creates an affine worker-held authority and pool-retained non-authorizing comparison evidence; retained evidence cannot manufacture a completion.
- The authority is privately bound to one worker-birth evidence value. Worker code cannot inspect it; rejected assignment delivery returns the authority for typed reunion with the pool half.
- Contains no customer recipient, parent path, role, job ID, or public token constructor unless the pool itself needs those fields privately.
Completion<Result>
{ result, consumed_completion_authority }
- Moves from worker to owning pool.
- Cannot select or substitute a customer destination.
- The locked parent report supplies a creator-local child nonce. That nonce, authority-carried private worker-birth evidence, and the retained pool half must all match the active assignment before completion commits.
Interruption
Fail | Retry
Failreturns the accepted payload to the customer once.Retryis explicitly at-least-once execution because the pool retained a retryable payload before assignment.
Customer and diagnostic protocols
The customer outcome is one closed sum:
Accepted { request: RequestCorrelation, job: JobId }
Rejected { request: RequestCorrelation, payload, reason }
Completed { job: JobId, role, result }
ReturnedQueued { job: JobId, payload, reason }
ReturnedAssigned { job: JobId, role, payload, reason }
A stale/duplicate/foreign completion is not another customer outcome. It goes
to a separate operational diagnostic sum carrying the complete result,
authority context, child nonce, private birth evidence, and reason. Its disposition is the builder's
DeliverTo(route) | Terminate choice; it is never an unnamed lane, mandatory
dummy destination, or no-op callback.
Whether customer acceptance and terminal outcome are one protocol or two capabilities remains a DX/type-safety prototype question. The law is fixed: one accepted job produces at most one terminal customer outcome.
RequestCorrelation is caller-authored and echoed only; the pool never trusts
it as job identity or retains it after moving the admission outcome.
JobId is an opaque public correlation value because a
shared customer route must distinguish several accepted jobs; its constructor
is private. AdmissionOrdinal, assignment IDs, completion authorities,
private worker-birth evidence, and generations are private newtypes. Pool-owned
issuers are non-wrapping, and the values remain distinct so one correlation kind
cannot substitute for another.
5. FIFO pool
Current public surface
fifo(…) -> Result<FifoPool<…>, FifoConstructionRejected<…>>is the sole production construction spelling.FifoPool<…>— one direct worker-owning fold.FifoCommand,FifoOutcome, and their exact admission and return reason sums.- shared
Assignment,Completion,Interruption, direct-pool recovery, drain, and diagnostics values above, plus the FIFO-specificBacklogCapacity. - one immutable private admission ordinal per accepted job; retry reinserts by ordinal rather than by stop-arrival order.
There is no Fifo marker, distribution mode, shared pool builder, policy bag,
or build stage. The aggregate name and constructor already select FIFO.
Runtime ownership reference
The solution's FIFO pool solution is the sole worker initialization,
activation authorization, job, completion, return, fairness-cursor, and drain
equation. Outcome
delivery is staged in the action settlement rather than represented as an
invented Completing or Returning actor phase. Only active jobs exist in
the table. After the single terminal customer action is committed, the active
correlation is removed; settlement owns a rejected terminal outcome without
recreating an active job or an unbounded tombstone.
6. Keyed pool
Current public surface
keyed(…) -> Result<KeyedPool<…>, KeyedConstructionRejected<…>>is the sole production construction spelling and does not reuse FIFO's constructor or a shared pool builder;KeyedPool<…>— a separate direct fold, not aFifoPoolwrapper;KeyedCommand<Key, Job>—Submit { key, payload, reply_to },Rebalance { key, expected, target, reply_to }, andUnbind { key, expected, reply_to }, whereexpectedisAbsent | Exact(binding_generation);- keyed management/customer outcome sum; and
- one inferred concrete closure
Fn(&Key) -> Rolestored by the pool.
There is no public KeyedJob or SelectWorker trait. Submission supplies the
key explicitly. The constructor statically stores one key-to-role selector
without a boxed callback, selector trait, or second application-defined type.
Runtime ownership reference
The solution's Keyed pool solution is the sole binding, rebalance,
unbinding, admitted-partition, and role-retirement equation. First accepted
submission may create a generation-tagged binding after selector validation;
an explicit rebalance with Absent expectation may also create a binding
without work. The table stores only live bound entries, never a persistent
unbound value. The binding is the sole owner of its key. Queued and assigned
jobs retain copyable, non-authorizing { binding_generation, admitted_role }
evidence rather than cloning or owning the key. Rebalance and unbind affect
only future admission. Unbinding an absent key with Absent expectation
returns the selected idempotent AlreadyUnbound { key } outcome.
Existing rebalance and unbind require Exact(current_generation). A
role-changing rebalance prepares a fresh generation before replacing the old
binding; a same-role rebalance is an accepted no-op preserving the generation.
Every stale expectation returns the complete command unchanged. Rebalance
uses its own total target projection: temporary recovery and pre-ready drain
with retained recovery are bindable, while stopping, terminal drain, retained
retirement, and shutdown ownership are unavailable.
Capacity and retention
- There is no shared public
Capacitysum. These are different domain laws, not interchangeable configuration values. - Dynamic-supervisor maximum entries is a mandatory positive
EntryCapacity; an unbounded dynamic table is not a valid construction. - Backlog capacity is explicitly per role.
- Pool backlog capacity is a
usizebecause zero lawfully means immediate assignment only. - Binding-table capacity is a separate positive
NonZeroUsizebound. - Unbind removes only future-admission affinity and releases binding capacity; already queued and assigned jobs retain their admitted role and generation.
- Reusing a key creates a fresh binding generation.
- Completion/rebalance facts from earlier generations are diagnostics only.
- The transition committing permanent role unavailability unbinds every
retained binding to that role, releases capacity, and emits exact
key/generation diagnostics. Irrecoverability is a cause, not a retained
terminal phase beside
Retired; temporary recovery retains bindings. Existing work keeps its admitted evidence only until its terminal outcome.
Worker, job, recovery, and drain states are keyed-pool-private even where their variant names resemble FIFO. Sharing a runtime state type is allowed only after both folds prove identical ownership and transitions; similarity is not proof.
Actor topology and dependency direction
FixedSupervisor ──creates/controls──▶ StableProxy ──creates──▶ Worker
DynamicSupervisor ─creates/controls─▶ StableProxy ──creates──▶ Worker
FifoPool ─────────────creates/assigns/observes directly─────▶ Worker
KeyedPool ─────────────creates/assigns/observes directly─────▶ Worker
- Proxy depends only on the retained behavior algebra and neutral interpreter carriers.
- Fixed and dynamic supervisors depend on proxy; neither depends on a pool.
- FIFO and keyed pools contain neither supervisor nor proxy and do not contain one another.
- Shared recovery/pool values are pure semantic values, never an engine.
- All five templates are direct
Behaviorfolds. They are not assembled fromBehaviorLayers or a universal ownership utility. crates/behaviornever depends on an actor template.
Types deliberately absent
- no
Supervisor<Mode>orPool<Mode>runtime engine; - no shared
Member,Slot,Fleet,Ownership, orChildrenruntime; - no
utilsmodule; - no public
WithParent,Inner,AtPath, occurrence, or effect-lane path; - no public typestate marker per builder axis;
- no customer route inside an assignment/completion;
- no optional selector in FIFO;
- no stable proxy in either pool;
- no public nonce, timer, job, assignment, operation, token, or generation constructor;
- no callback/boolean where a semantic sum owns mutually exclusive choices;
- no compatibility wrapper retaining legacy and replacement templates.
Selected public surfaces
| Family | Component construction | Status |
|---|---|---|
| Stable proxy | StableProxy::immediate() or StableProxy::activated() | selected |
| Fixed supervisor | fixed(...), optional lifecycle publication, then build() | selected |
| Dynamic supervisor | dynamic(entries, activation, unexpected_exit, actor_drain, lifecycle, diagnostics) | selected |
| FIFO pool | fifo(factory, roles, activation, recovery, backlog, interruption, actor_drain, diagnostics) | selected |
| Keyed pool | keyed(factory, roles, selector, activation, recovery, backlog, bindings, interruption, actor_drain, diagnostics) | selected |
Generated component rustdoc lists 102 semantic items for the five implemented families: 43 structs, 53 enums, two traits, and four construction functions. These are construction, policies, commands, capabilities, typed rejections, and domain outcomes—not structural actor machinery. Reduce this count only when two names own the same law; hiding a real outcome is not surface curation.
There is one external component path: behavior_actors::atomic::Name; the crate
root exposes zero duplicate atomic paths. Interpreter reexports are doc-hidden
at the facade or at their definitions. Generated rustdoc excludes sampled
effect products, request products, internal events, worker preparation,
activation operations, proxy control, and settlement carriers. They remain
statically reachable but are not candidates for Bombay's ordinary facade.
Bombay must selectively re-export only the semantic categories in
atomic-actor-devx.md. It must not glob the component
module or expose request products, internal events, effect products, preparation state,
activation requests, proxy control, or settlement carriers.
All five component construction paths are selected. Bombay's selective facade, role-first application assembly, and runtime settlement/custody changes remain upstream integration work rather than component public-name questions.
Minimal core used by atomic supervisors and pools (engineering record)
This is a deletion audit, not a preservation promise. “Already in core” is not a reason to keep a type, and “the compiler needs a name” is not a semantic law. The atomic actors should expose their own domain protocols while depending on the smallest common actor algebra underneath them.
There are three different questions which must not be collapsed:
- what is irreducible actor algebra;
- which existing interpreter carriers are still needed internally; and
- which names an atomic-actor user must see.
Most of the current names belong only to the second category. They must not be re-exported as supervisor/pool concepts or counted as atomic-template types.
A. Irreducible retained algebra
These existing concepts have independent laws. The five atomic actors use
them directly and must not recreate them in actors.
| Existing concept | Retention law |
|---|---|
Protocol | A nominal, statically known address/message identity. Equal Rust field shapes do not make two protocols interchangeable. |
Behavior | Pure initialization and one-communication fold. No executor, clock, hydration, or delivery occurs inside it. |
Actions | The sole successful effect boundary: typed communications, staged fresh creation, and next behavior/termination. |
Step | Exhaustive continuation/become/stop verdict. Stopped and Never are supporting proof types, not new template concepts. |
Recipient and EstablishedRecipient | Distinct logical and exact delivery capabilities. One must never be silently widened into the other. |
CreateChild | One staged fresh-child request with explicit provenance. It never means replacement or successful installation. |
TerminalOutcome | One authoritative terminal fact, preserving normal exit versus crash provenance. |
BehaviorBase, User, Births<Child>, NoBirths, CreationKind, Exit, and
Crash are current representation/support types for those seven laws. They
remain only while the existing algebra and interpreter need them. The atomic
actor modules do not wrap, alias, or re-export them merely to make their own
signatures look uniform.
This is the minimal retained semantic kernel. It is seven concepts, not the dozens of lifecycle and topology types currently visible in the crate.
B. Existing interpreter carriers used internally
The following values may be used to lower an atomic actor's Actions into the
current interpreter. They are not components from which supervisors or pools
are composed, and they are not automatically part of ordinary DevX.
| Existing family | Internal use | Decision |
|---|---|---|
Address, EndpointAddress | Runtime-owned identity and exact endpoint families | Keep in the interpreter boundary; do not expose numeric identity in builders. |
ChildRoute, ChildDelivery, ChildInput, ChildReport | Creator-local exact child routing and source-attributed reports | Use only inside direct owner/child folds. A route is correlation, not actor identity. |
InterpreterRequests, InterpreterRequest | Closed typed scheduling, observation, shutdown, and structural-report lane | Keep one generic request lane; do not add a supervisor request trait and a pool request trait. |
ObserveCreation, CreationResolved | Committed or rejected staged creation without an exact endpoint capability | Reuse internally where only that fact is required. Do not add template-specific WorkerCreationResolved. |
ObserveEstablishedCreation, EstablishedCreation | Same-action committed or rejected creation with the exact installed capability | Reuse internally for stable proxies: fixed and dynamic supervisors must retain the exact committed proxy rather than reconstructing it from an address. |
ObserveChild, ChildStopped | Exact child terminal observation | Reuse internally; do not add template-specific WorkerStopped. |
ShutdownChild, ChildShutdownRejected, ShutdownRequested | Exact child drain request/rejection and actor shutdown ingress | Reuse internally where their current facts are complete. Deadline retirement remains ActorDrainPolicy. |
ScheduleAfter, TimerElapsed, TimerId, TimerGeneration | Interpreter-clock delay and exact timer correlation | Reuse internally. IDs are actor-owned and never builder inputs. |
ReportToParent | Structural lowering of a child report | Keep private to actor/interpreter integration. A worker user writes assignment.complete(result), never this type. |
DeliveryRoute | Preserve a statically selected logical or exact destination; constrain its associated protocol address to the owner where needed | Reuse for lifecycle/customer routes; do not force every actor to support a mixed route. |
None of these rows proves that every current helper trait around the value must
survive. SendEffects, SendsFor, InterpretSends, InterpretDelivery,
InterpretChildDelivery, InterpretChildInput, and InterpretRequest are
implementation machinery to audit as one interpreter equation. Keep only the
smallest closed set used by the actual named effect product. Do not mirror it
with template-local traits.
Required interpreter-contract replacement
The current InterpretSends contract returns one Interpreter::Error and
documents that later lanes remain unconsumed after failure. That is
incompatible with the selected ownership law: the source fold has already
committed, so every later value still needs a surviving owner.
The replacement must interpret the named effect product into the equally
named, ordered per-item settlement product from the solution. Every item is
Accepted, Rejected, or NotAttempted after an exact dependency failure.
Independent later items are interpreted; dependent later items are returned
untouched. All current same-action occurrence-dependent operations must be
grouped with their creation prerequisite: ObserveCreation,
ObserveEstablishedCreation, ObserveChild, ChildDelivery, ChildInput,
and ShutdownChild. The staged sum distinguishes direct dependence on
creation from dependence on required-observation acceptance. All required
observations are attempted in named order; if several reject, their rejections
remain independently owned and later dependent effects cite the first
rejection's correlation. No lane index, path, lookup table, or general graph
is authorized.
ReturnsToEmitter/NoReturnToEmitter cannot be the general answer
because the emitter may stop in the same action. They are replaced or narrowed
to the live-primary-admission optimization only after typed host settlement is
proven. The Driver gives the transitive settlement chain priority before the
source's next ordinary user communication, so unresolved batches do not
accumulate across separate user turns. This supplies no decreasing measure:
the iterative queue alone proves neither bounded memory, termination,
fairness, nor eventual return to ordinary traffic.
C. Conditional machinery, not selected core
| Current family | Keep only if |
|---|---|
EstablishedChild | A focused use proves that a combined local-route/exact-actor helper product removes real ownership plumbing beyond the retained EstablishedCreation fact. |
ObserveEstablished, ShutdownEstablished | The operation genuinely starts from a transferable exact capability rather than the owner's child namespace. |
ReplyRoute, ReplyDelivery, ReplyDeliveries | One real API accepts both logical and exact reply routes. Exact-only callers must not pay for a mixed sum. |
EventIngress, InjectEvent, Here, Inside, EventLayer, SendLayer | The current closed wrapper/interpreter composition truly needs them. Ordinary actor and template APIs never expose positions or paths. |
ChildRole, ChildOccurrence, ResolveChildOccurrence, ChildHead, ChildTail, Children, ChildChoice | A closed authored birth product needs exact structural occurrence. Runtime supervisor roles and pool workers are values, not one type per role. |
ReportTerminalOutcome | A fold must publish a terminal override independently of its own Step::Stop. |
Activate, Initialized, Active | Pure one-time Behavior initialization. They do not prove asynchronous worker readiness. |
operations::Readiness | Its existing fixed-dependency/version law is independently needed. It is not worker activation. |
| shutdown coordinators and wrappers | An application explicitly composes that outer lifecycle policy. They are not internal pieces of an atomic supervisor or pool. |
C.1 Complete disposition of current public core families
This table closes the audit over the families currently re-exported by
crates/behavior/src/lib.rs. It is a family-level disposition rather than a
promise to preserve every helper name.
| Current public family | Atomic-actor decision |
|---|---|
Address, MailAddr, EndpointAddress | Keep as protocol/interpreter identity support. Builders never accept raw addresses. |
Protocol, MessageProtocol | Keep nominal Protocol. Keep MessageProtocol only as an explicitly structural helper; it must not collapse two domain protocols merely because address/message shapes match. |
Behavior, BehaviorActed, BehaviorAddr, BehaviorMessage, InitializationTurn, ActiveTurn | Keep as the pure fold contract and truthful aliases/turn witnesses. Atomic actors implement this contract directly. |
BehaviorLayer, BehaviorBase, initialize, delegate_transition | Keep for genuine transparent wrappers and initialization composition. Supervisors/pools do not use them as a behavioral decomposition. |
Actions, Step, Stopped, Never, Become, Acted, AppendSend | Keep the explicit transition result. Convenience aliases remain only while they preserve all three action legs and the exact verdict. |
SendEffects, SendsFor, NoSends, SendLayer and generated named products | Keep only the product/append laws required by concrete behaviors. Ordinary atomic-actor source sees semantic lane names, never SendLayer nesting. |
SendInterpreter, InterpretSends, InterpretDelivery, InterpretEstablishedDelivery, InterpretChildDelivery, InterpretChildInput, InterpretRequest | Replace the success/one-error short-circuit equation with complete per-item settlement; retain only the static concrete dispatch pieces that realize it. |
InterpreterRequest, InterpreterRequests, ReportToParent, Own, SendInput | Keep interpreter-facing and hidden from ordinary users. ReportToParent must compose behind assignment.complete, not appear in worker code. |
NoReturnToEmitter, ReturnsToEmitter | Remove as a universal rejection law. A live emitter may be a primary consumer, but host settlement is the required surviving owner. |
Recipient, EstablishedRecipient, Delivery, EstablishedDelivery, EstablishedActor, InterpretEstablished | Keep logical and exact capability families distinct. Atomic protocols preserve the concrete selected route. |
CreateChild, CreationKind, CreationRejection, AllocationRejection, BirthMode, NoBirths, Births | Keep staged fresh creation and its typed rejection. Do not create template-specific duplicates. |
ChildRoute, ChildDelivery, ChildInput, ChildReport, EstablishedCreation | Keep as exact creator/child carriers where their facts remain complete; never expose structural routing in ordinary actor DevX. |
ChildChoice, role/occurrence/position traits, fold/mapping traits, Children, and birth protocol products | Keep the closed authored-child algebra for macros/interpreters and other templates. Atomic runtime roles are private values, not one public type-level role per member. |
User, UserEvent, Ingress, EventIngress, ChildInputIngress, InjectEvent, EventLayer, ComposedEvent, Here, Inside | Keep typed event composition for real wrappers/interpreters. Paths and nesting markers remain generated/internal to ordinary atomic-actor source. |
LogicalHostRequirements, LogicalDeliveryProtocols, birth logical-host projections | Keep static evidence for intentional logical routes, including requests that carry a logical recipient. Exact and creator-local routes add no logical-host obligation. |
the #[behavior] owning macro | Keep syntax generation for the same concrete algebra. Do not add supervisor/pool or completion macros until ordinary composition is proven impossible. |
Behavior intentionally exposes no finite mailbox reducer. One-turn
initialization and event transitions are the algebraic boundary; Bombay owns
runtime sequencing. The unpublished testkit alone owns finite test sequencing
through drive and reports DriveDisposition::{MailboxDrained, BehaviorStopped(Stopped)} without a semantic boolean.
D. Delete or replace with the legacy templates
These names encode the architecture being replaced or incomplete policy shapes. Widespread use does not make them core.
- old
Proxy,Supervisor,DynamicSupervisor,WorkerPool, andKeyedWorkerPoolimplementations and their aliases; ChildTopology,FixedFleetOwnership, slot/fleet ownership state, and wrapper/path-specificWithParentforms;SupervisionEvent,SupervisionSends,SupervisorSends,PoolSends, and products whose fields exist only because the old templates are nested;WorkerCreationResolved,WorkerStopped,ReplacementRequested,ReplacementResolution,ReportWorkerCreationResolved,ReportWorkerStopped, andReportProxyUnavailablein their legacy shapes;- the existing
RestartConfiguration/RestartPolicysplit if it permits contradictory or meaningless combinations; - the current
RestartDenialif it cannot retain overlap, clock-order, activation, or forced-retirement reasons required by the catalogue; and - assignment/completion types that expose customer routes or require workers to echo correlation fields.
Deletion happens only after the five replacements have feature and interpreter witnesses and all callers are migrated. No compatibility layer keeps the old concepts alive under new names.
E. Core gaps exposed by the laws
Five requirements are not solved by the retained kernel:
- Authoritative installation and initialization settlement. The pure init
fold must precede host commitment, but initialization
Actionsmust follow commitment of an installed-but-not-ready incarnation. Their partial success cannot be rolled back. The interpreter needs distinct pre-commit init/host rejection and post-commit initialization-effect settlement. - Incarnation activation. The selected semantic equation splits committed
installation into an actor-retained exact incarnation and a one-shot
activation permit.
BeginActivationconsumes the permit and concrete plan; exact resolved/cancelled/late facts settle readiness. Hydration/I/O remains outsideBehavior. The retained algebra and interpreter do not yet realize this equation. - Creation-scoped dependency lowering. One creation and the complete
current set of same-action occurrence operations—
ObserveCreation,ObserveEstablishedCreation,ObserveChild,ChildDelivery,ChildInput, andShutdownChild—must lower as one named staged bundle, with direct and after-required-observation alternatives. Independent effects stay outside. ExistingActionsdoes not yet prove that grouping without structural paths or runtime lookup. - Rejected delivery and terminal projection. The selected semantic equation projects ordered per-item settlement from each named effect-product lane. Every item is accepted, rejected, or not attempted after an exact dependency failure; several failures remain distinct. A not-attempted item references the one authoritative prerequisite settlement rather than cloning its payload. A statically known host owns settlement after source commit or termination and transfers unresolved values outward to a root runner. Current interpretation does not realize that equation, and a committed fold cannot be rolled back. Its root boundary needs total heterogeneous static lifts, duplicate-role provenance, compositional wrapper transfer, and wrapper-depth-independent compiler diagnostics.
- Residual root lifetime. Actor-side forced retirement may transfer an uncancellable activation or future late incarnation to the root. The run future must remain a live typed owner until those facts resolve and the exact incarnation drains; a finished error value is not an owner. The current runner has no such residual state.
These are foundational interpreter equations, not permission to add a broad “lifecycle engine,” callback, ambient query, or template-local interpreter trait. Their semantic ownership is selected; the smallest concrete association with the retained core still requires a focused end-to-end regression and compile/interpreter witness.
F. Visibility rule
An ordinary user of a supervisor or pool should normally see only:
- the actor's builder and public command/outcome protocol;
- their domain role/key/job/result/worker types;
- selected recovery, actor-specific capacity bounds, shutdown, and diagnostic policies; and
- logical or exact recipients they deliberately provide.
They must not see child routes, occurrence positions, interpreter requests, parent-report plumbing, effect-lane products, nonce/timer issuers, activation joins, completion correlations, or typestate proof markers. If Rust makes an internal proof type appear in diagnostics, that is an implementation problem to measure—not a semantic reason to promote the type into the public model.
Solution design for the atomic actor catalogue (engineering record)
This document follows and cross-references
atomic-actor-features.md. The feature catalogue is
the requirement source; this document may not silently weaken it.
The minimal existing-core decision is in
atomic-actor-retained-core.md, the minimized
state/type equations are in
atomic-actor-type-inventory.md, and the
independent DevX research cross-check is in
atomic-actor-research-audit.md. The
disposition of every other current actor template is recorded separately in
atomic-actor-other-templates.md; those
later repairs are not part of the five-actor implementation stage.
Status language
- Design-covered means this document names the state, transition, effect, error, or test mechanism that satisfies the referenced requirement.
- Open means the requirement is recorded but its complete public type, transition, effect, and interpreter path have not all been selected.
- Implemented means production code and focused regressions exist.
- Verified means independent models, exhaustive/property/fuzz coverage, interpreter witnesses, and repository gates pass.
No new supervisor or pool production implementation exists. Several rows are still open after the research/DevX audit; the coverage matrix must not present them as design-covered. Nothing in this document may be reported as implemented or verified yet.
Coverage is dependency-closed: a row is Open whenever its observable
transition depends on an open shared law, even if its local state transition is
otherwise specified. A row may be Design-covered only when its own law and
every foundational law it invokes are design-covered.
Open design blockers
Production work is blocked until these are resolved with public types and focused regressions:
- an interpreter witness for pure initialization, authoritative installed-but-not-ready host commit, partial initialization-effect settlement, and post-commit drain in that exact order;
- compile/interpreter realization of the selected activation
permit/request/fact capability plus owner authorization and bounded
occupied-ticket states, without moving hydration or I/O into
Behavior; - compile/interpreter realization of action settlement and delivery-rejection ownership for every lifecycle, management, customer, parent-report, and diagnostic route, including turn-local settlement priority and creation-scoped dependency bundles; the prototype must preserve the explicitly unbounded transitive-chain limitation rather than claiming termination or fairness;
- interpreter realization of the selected
WaitForActorGraphversusRetireActorGraphAfter { deadline }actor-drain policy, exact forced-retirement fact, and live root residual state for uncancellable work; - a compile-proven total terminal lift across heterogeneous children, duplicate roles, and wrapper orders, with a semantic root sum and bounded diagnostics;
- a compile-proven ordinary syntax that does not expose parent paths, reply aliases, repeated turbofish, or 64 public support names; and
- end-to-end interpreter witnesses for readiness and rejected deliveries.
The opaque pool completion token, customer-route ownership, separate stale
diagnostic lane, canonical builder order, common-protocol heterogeneous fleet
limit, dynamic durable-owner split, and precise FIFO rotation are selected
local directions below. They are not implemented, and any matrix row that also
depends on open settlement, activation, shutdown, or completion lowering
remains Open.
Contradiction-closure ledger
This table is the cross-document authority for the latest specification
review. Selected/Open realization means the semantic equation has one answer
but its retained-core association, compiler proof, or interpreter witness is
still missing; dependent coverage rows remain Open.
| Review issue | One selected answer | Status and authority |
|---|---|---|
| Initialization versus installation | Run the pure init fold first; establish and commit an installed-but-not-ready host before interpreting its initialization Actions; then settle those effects before activation. Post-commit rejection drains but never rewinds installation or prior effects. | Selected/Open realization; Causal creation and activation policy, SH-READY, proxy/pool state equations |
| Rejected delivery after source stop | Ordered per-item action settlement is owned by a statically known host and transfers outward if hosts stop. The live root retains residual ownership and returns a final value only after it settles. No emitter mailbox is required. | Selected/Open realization; Action settlement and rejection ownership, SH-DELIVERY |
| Turn-local settlement priority | The Driver processes settlement before the source's next ordinary user communication, so chains do not accumulate across separate user turns. No memory, termination, fairness, or eventual-return bound is claimed. | Selected/Open realization; Action settlement and rejection ownership, SH-DELIVERY |
| Exact activation capability | Committed installation yields actor-retained exact incarnation plus one-shot installation permit; BeginActivation consumes that permit and the concrete plan. The owner separately records one bounded occupied authorization ticket, released by exact settlement or outcome. | Selected/Open realization; Exact activation capability, SH-READY |
| Activation concurrency and late readiness | Every owner counts unresolved activation authorizations. Supervisors reserve conservatively at opaque proxy install; pools reserve at BeginActivation. Owners store waiting definitions or permits, exact occupied tickets, and an ordered authorization queue. Cancellation is logical after ownership transfer; late ready drains without publication. | Selected/Open realization; SH-READY, actor state equations |
| Startup-limit meaning | The public law always bounds unresolved activation authorizations. Supervisor reservation is deliberately earlier and conservative because its proxy reports no progress; pool reservation occurs at direct activation emission. | Selected; SH-READY, activation journeys and inventory |
| Dynamic cancellation ownership | Transaction-local preparation, proxy-create-emitted, waiting-for-authorization, install-emitted, awaiting atomic proxy outcome, ready, cancelling, and shutdown-owned phases are distinct. Worker installation and activation progress remain proxy-private. Only definition-owning phases return it. | Selected/Open realization; Dynamic supervisor solution, DS-CANCEL |
| Diagnostic failure | `Diagnostics[Route] = DeliverTo(Route) | Terminate`; failed diagnostic delivery becomes terminal settlement and never sends recursively. Proxy uses its mandatory exact parent. |
| Readiness/query wording | Routability begins only at exact Ready; dynamic public phase exposes only supervisor-observable reservation, proxy creation, activation authorization, awaiting-proxy-outcome, cancellation, drain, and retirement phases. | Reconciled; SH-READY, DS-QUERY |
| Competing runtime state machines | This solution is the sole normative ownership equation. The type inventory now references it and contains no duplicate private state tables. | Reconciled; type inventory introduction and runtime references |
| Keyed submission model | Submit { key, payload, reply_to } plus one concrete Fn(&Key) -> Role; no KeyedJob or SelectWorker. | Selected; keyed solution, inventory, DevX |
| Key retirement | The transition committing permanent role unavailability automatically unbinds every retained binding for that role, releases capacity, and diagnoses exact generations. Temporary recovery retains bindings; irrecoverability is a cause, not a parallel terminal phase. | Selected; KP-RETENTION |
| Pool ownership language | Exactly one authoritative customer obligation/correlation, not one Rust value. Retry deliberately retains a canonical payload and dispatches a clone. | Reconciled; Job ownership, FP-ASSIGN |
| Pool customer outcomes | Admission is `Accepted | Rejected; terminal outcomes are exactly Completed |
| FIFO readiness ordering | No serviceable backlog may coexist with eligible idle capacity. Initial/replacement readiness, recovery, and completion commit directly to Busy with the oldest eligible queued job before Idle is possible. | Selected; Pool member state, FP-TOPOLOGY, FP-ASSIGN |
| FIFO retry capacity | Backlog capacity bounds new waiting admissions. Retry may add at most one formerly assigned job per role beyond that admission bound, preserving an absolute capacity + roster size queue bound and original admission order. | Selected; Job ownership, FP-ADMIT, FP-INTERRUPT |
| Pool completion authority | Each dispatch issues affine worker-held authority plus pool-retained non-authorizing evidence, both bound to private worker-birth evidence. Completion matches the locked report's creator-local nonce plus that opaque evidence; the interpreter does not supply an exact incarnation capability. | Selected/Open lowering; Job ownership, shared pool inventory |
| Rejected worker assignment | Exact mailbox rejection consumes returned affine authority through typed reunion, reinserts the proven-unaccepted job by immutable admission ordinal, and quarantines the worker. Delivery settlement, completion, and worker stop form one exhaustive order-independent join. | Selected/Open realization; Admission and dispatch, FP-ASSIGN |
| Pool initialization ownership | While worker initialization is unresolved, the lifecycle host owns its linear settlement and activation plan; pool state retains only exact initialization correlation until one result transfers the lawful next values. | Reconciled/Open realization; Pool member state, AA-01 initialization law |
| Pool action application | Every action operation and settlement is a closed exact sum. Non-transactional apply returns applied prefix, rejected operation, and owned unattempted suffix; parent completion rejection returns the complete report instead of being discarded. | Selected/Open runtime boundary; FIFO pool effects, action settlement |
| Keyed admission completeness | One total match maps every member state to AssignableNow, BacklogAdmissible, or Unavailable; a separate total management projection maps targets to Bindable or Unavailable. Pre-ready drain uses its stored recover-or-retire disposition in both. | Selected; Keyed pool solution, KP-AFFINITY, KP-REBALANCE |
| Dynamic lifecycle owner | One mandatory durable route is selected on the builder; request routes are temporary and cannot transfer lifecycle ownership. | Selected; dynamic construction, DS-START |
| Forced-retirement termination | Deadline retirement atomically transfers exact outstanding ownership to the surviving host and records RetiredForced; that member is actor-side drained while late facts settle with the host. | Selected/Open realization; Shutdown representation for owners, SH-SHUTDOWN |
| Drain terminology and scope | The sole public sum is `ActorDrainPolicy = WaitForActorGraph | RetireActorGraphAfter { deadline }`. It governs actor-graph retirement only; residual-root settlement and process exit are separate. |
| Root residual lifetime | Forced actor-graph retirement moves the still-live root run future to ActorGraphDrained { residual }; no final error/result exists until late activation and exact drain ownership settle. | Selected/Open realization; Action settlement and rejection ownership, Shutdown representation for owners |
| Terminal settlement lowering | One concrete root terminal sum receives total provenance-preserving static lifts from every heterogeneous child/wrapper sum. Duplicate roles never identify origin; outward lifts compose; wrapper-depth diagnostics must remain bounded. | Selected/Open realization; Static terminal-projection equation, type inventory |
| Settlement dependency representation | A closed staged bundle covers every current same-action occurrence operation: creation-result observation, exact established-creation observation, child-termination observation, child delivery, child input, and child shutdown. It selects direct-after-creation or after-required-observation ordering; rejection returns downstream values NotAttempted. | Selected/Open realization; Creation-scoped dependency equation, retained-core prototype |
| Coverage matrix | Counts are mechanically derived and open dependencies propagate to every observable row. | Reconciled; Coverage matrix |
| Public DevX | All previous aspirational snippets are withdrawn. Five complete examples plus completion/wrapper proof are required before syntax is selected. | Open; atomic-actor-devx.md |
| Other templates and Entity/Mnesis | Other template dispositions are a separate staged audit. Entity hosting, family drain, Mnesis hydration, and durable completion remain outside this five-actor stage. | Explicit scope boundary; other-template and research-audit documents |
System boundary
Five independent concrete folds are built in this order:
Proxy<Worker>;FixedSupervisor<Role, Worker, ...>;DynamicSupervisor<Key, Worker, ...>;FifoPool<Role, Job, Result, Worker, ...>; andKeyedPool<Role, Key, Job, Result, Worker, Selector, ...>.
The names above describe intended semantic families, not final generic spelling. No shared runtime ownership engine is introduced during these five implementations. Configuration values may be shared because they are the same policy values; mutable lifecycle state and transition code are not shared until independent folds prove one smaller lawful extraction.
Every fold returns one named send product, one closed child-creation product,
and one exhaustive next verdict. There are no lifecycle flags, parallel
Option fields encoding a join, or boolean transition results. A predicate may
be calculated transiently, but stored and returned semantic state is always a
sum type.
Causal creation and activation policy
The central design decision is causal two-stage activation.
owner creates empty stable proxy
-> runtime commits or rejects proxy creation
-> only after commit, owner sends InstallInitial(worker)
-> proxy stages fresh worker birth
-> runtime runs the worker's pure init fold
-> runtime establishes exact host ownership and commits the
installed-but-not-ready incarnation
-> runtime interprets and settles initialization Actions
-> partial initialization failure drains; success issues activation permit
-> installed worker enters its selected activation mode
-> exact ready/rejected/stopped/shutdown fact resolves activation
-> proxy returns one atomic readiness outcome
This is a deliberate Bombay policy, not an actor-model guarantee. It solves a major source of correlated flags:
- worker creation cannot be reported before stable proxy creation commits;
- the owner retains the worker definition until a proxy capability exists;
- stable creation rejection cannot coexist with a legitimate worker report;
Startedalways contains the exact stable proxy capability. The proxy keeps the routable worker recipient private; any owner-visible worker incarnation value is opaque non-routable correlation evidence and is not published inStartedorRestarted.
Pools have no proxy. They create workers directly, then apply the same installation-versus-readiness distinction in their own fold. They do not share the proxy's runtime state or transition implementation.
The runtime may still deliver a worker stop before the proxy receives matching installation or readiness resolution. Those joins remain inside the proxy, which owns all exact correlations. The proxy eventually emits one atomic parent outcome:
Ready(incarnation)
ActivationStartRejected(incarnation, complete unaccepted request,
terminal-or-forced drain fact)
ActivationRejected(incarnation, complete rejection,
terminal-or-forced drain fact)
InitializationFoldRejected(attempt, complete error)
InstallationRejected(attempt, complete host rejection and prepared init)
InitializationEffectsRejected(incarnation, failure classification,
terminal-or-forced drain fact)
StoppedBeforeReady(incarnation, complete stop fact)
Contradiction(complete conflicting facts)
Owners therefore never reconstruct readiness from separate creation, activation, and stop reports. The semantic activation request/fact capability is selected below; its association with the retained core and interpreter is still an open implementation design. Committed creation alone is not an implementation of this law.
H41b later corrected the custody split: Bombay retains the complete concrete
initialization settlement in the worker environment and transfers it through
runtime retirement. The proxy outcome above carries only the closed failure
classification. The normative contract is
atomic-runtime-settlement.md.
The capability boundary is explicit:
StableProxyCapability = externally routable service recipient
WorkerRecipient = proxy-private routable child capability
WorkerIncarnationEvidence = opaque non-routable lifecycle correlation
The proxy's internal installed-incarnation product owns both
WorkerRecipient and WorkerIncarnationEvidence. Atomic parent outcomes
project only the evidence. Fixed and dynamic supervisors retain that evidence
for exact replaces and stop correlation, but their status, capability,
Started, and Restarted values expose only StableProxyCapability.
Exact activation capability
The semantic capability is selected even though its final Rust integration is not compile-proven:
CommittedInstallation<Plan> {
incarnation: InstalledIncarnation,
initialization: InitializationSettlement,
activation_plan: Plan,
}
InitializationResolved<Plan> =
Initialized {
incarnation,
activation_permit: ActivationPermit,
activation_plan: Plan,
}
| EffectsRejected { incarnation, settlement }
| Stopped { incarnation, stop_fact }
BeginActivation<Plan> {
attempt: ActivationAttempt,
permit: ActivationPermit,
plan: Plan,
}
ActivationResolved<Rejection> =
Ready { attempt }
| Rejected { attempt, rejection }
CancelActivation { attempt }
The creation request owns the concrete activation plan alongside the worker
definition. On committed installation the interpreter returns that same plan
with the exact incarnation and initialization settlement; it never clones,
reconstructs, or looks it up. InstalledIncarnation is exact and closed to
public traffic.
ActivationPermit is the distinct one-shot capability tied to that committed
installation. The emitted request consumes the permit and plan. Concurrency
authorization is not a second capability: the owning supervisor or pool
represents it by moving the member from its waiting sum variant into an exact
occupied-ticket entry in the same commit that emits the install or activation
request. The actor stores the incarnation and attempt; no linear value is
owned by both state and Actions.
The exact order is normative:
pure init fold
-> establish provisional/exact host ownership
-> commit InstalledIncarnation, still closed to mailbox/user traffic
-> interpret initialization Actions
-> discharge their complete settlement
-> issue ActivationPermit, drain, or record stop
An init-fold error occurs before host commit and executes no initialization
effect. Host/allocation rejection returns the prepared post-init behavior and
uninterpreted initialization Actions to settlement; it also executes no
initialization effect. Once installation commits, earlier initialization
effects are authoritative. A later initialization-effect rejection produces
EffectsRejected and drains that exact installed incarnation without issuing
an activation permit; it never reclassifies installation as rejected. An
initialization Step::Stop similarly reports StoppedBeforeReady after all
required initialization effects settle. BeginActivation never initializes
the behavior again. An immediate plan may resolve Ready; a reported plan
runs its concrete asynchronous future outside the fold. Both remain statically
dispatched.
Each fixed supervisor, dynamic supervisor, FIFO pool, and keyed pool owns an
activation-authorization limit and never owns more than that positive number
of unresolved authorization tickets. Authorization always means the owner has
allowed a worker to progress toward Ready. A supervisor reserves
conservatively with the opaque proxy install input because it receives no
worker-progress facts; a direct pool reserves with BeginActivation after
initialization settlement. A proxy's local state has a structural limit of
one. Definitions waiting for authorization remain in owner state. An occupied
ticket is released only by its exact action settlement or terminal outcome.
CancelActivation is logical. If external work cannot be cancelled, the
activation interpreter retains it but closes publication irrevocably. Late
Ready becomes ReadyAfterCancellation and transfers the exact incarnation
to drain; late rejection becomes RejectedAfterCancellation. If the actor has
already terminated or been forcibly retired, its lifecycle host—not its
mailbox—owns the unresolved activation record, including the exact
incarnation needed to settle or drain that late result.
Action settlement and rejection ownership
The source actor is never the universal owner of delivery rejection. Before interpreting any action, the runtime creates one typed settlement record for every item in the complete named effect product:
ItemSettlement<AcceptedFact, Rejection> =
Accepted { item, fact: AcceptedFact }
| Rejected { item, rejection: Rejection }
| NotAttempted { item, value, blocked_by: SettlementItem }
ActionSettlement<NamedLanes> {
each semantic lane: ordered settlements for every emitted item
}
Each lane's Rejection is a closed sum projected from that lane's concrete
effect type. It owns the complete payload, route kind, and exact reason.
NotAttempted is required when a declared prerequisite—for example a
same-action child creation—rejects. It returns the untouched dependent value
and refers to the one authoritative prerequisite settlement by an opaque,
non-reused item correlation. It does not clone or duplicate the prerequisite's
owned rejection into every dependent item. Independent later lanes are still
interpreted. Thus every emitted item has one settlement, several failures in
one action are all preserved, and a short-circuit cannot silently discard a
later value. For creation dependencies, SettlementItem is exactly the
bundle-scoped correlation of the rejected prerequisite defined below: the
creation settlement, or the first rejected required observation in named
interpretation order. It is not a general item ID, lane index, structural
position, or lookup key.
The complete named settlement product is retained by the actor's lifecycle host and outlives both the transition and actor. A continuing actor may be the primary consumer of rejection facts in lane order, but settlement transfers ownership of each fact to its mailbox only after admission succeeds. The host retains all remaining items. If the actor is stopping, has stopped, rejects a fact, or stops while processing an earlier fact, the lifecycle host remains the owner of every unadmitted item.
Settlement has a mandatory turn-local priority order. Before the Driver admits
the source actor's next ordinary User communication, it processes every item
in the current action batch in lane order by doing exactly one of:
admit the typed settlement fact on the source's system lane
or
transfer the item to the source's surviving host settlement
Actions produced while folding an admitted settlement fact create a new batch processed by the same priority rule before ordinary source traffic resumes. This is a per-source/host-chain priority rule, not a global scheduling barrier. It proves only that unresolved settlement does not accumulate across separate ordinary user turns: the next such turn is not admitted until the transitive settlement chain quiesces or ownership transfers outward.
This is not a decreasing measure or a bound on the transitive chain. A settlement handler may emit actions whose handlers emit more actions; that can consume unbounded time or memory and can starve ordinary traffic. An iterative Driver queue prevents call-stack growth only—it does not prove queue bounds, termination, fairness, or eventual return to user messages. A stronger claim requires a future typed transfer/budget policy and its own progress proof.
Installation establishes this ownership recursively and statically. A child host's unresolved settlement transfers into its parent's typed host settlement when the parent actor cannot consume it. If that parent also stops, the value continues outward without reinterpretation. The root application runner is the terminal live owner. It does not turn unresolved ownership into a finished error value. Its internal run state is:
Running
ActorGraphDrained {
actor_summary,
residual: NonEmptyResidualSettlement,
}
Settled {
actor_summary,
terminal_settlement,
}
NonEmptyResidualSettlement owns the concrete external activation tasks,
their exact correlations, any incarnation produced by a late ready fact, and
the capability needed to drain that incarnation. It is affine runtime state,
represented by a closed statically dispatched product/sum—not an erased boxed
future, cloneable report, registry, or actor mailbox. The root run future
remains alive in ActorGraphDrained and consumes late facts until it can
transition to Settled; only then may it return the final result to its caller.
An API may publish the actor-side forced summary separately, but that
observation is not the final application result and transfers no residual
ownership.
There is no global settlement registry, lookup, logging fallback, or emitter
mailbox that must remain alive forever. A host may apply only the lane-specific
recovery named below; otherwise outward transfer to the still-live root is the
terminal disposition. RetireActorGraphAfter therefore bounds actor-graph retirement,
not completion of external work that the interpreter cannot cancel. A future
bounded-process-exit policy would need an explicit typed emergency-abandonment
outcome; it is not silently implied by RetireActorGraphAfter or Drop.
The internal host product may be topology-derived, but that structural type is
not the ordinary root error. Each actor/template projects terminal settlement
into one named semantic TerminalSettlement sum at its hosting boundary;
lane variants own the exact rejected values and carry a semantic role as a
value when needed. There is no generated variant per topology position and no
exposed ChildChoice, SendLayer, Inside, or occurrence path. The root runner
returns only the root's named projection (normally inferred through the run
method). A compile-pass/fail and diagnostic-size prototype must prove that an
ordinary application neither names nor receives the recursive host product;
until then the public root error and every dependent DevX row remain Open.
Static terminal-projection equation
TerminalSettlement is not a universal erased envelope. For one concrete
root terminal sum R, hosting constructs a closed family of statically
dispatched lifts:
lift_own: OwnTerminal -> R
lift_child_i: (ExactOrigin_i, ChildTerminal_i) -> R
lift_wrapper_j: WrapperTerminal_j<InnerTerminal> -> R
The family must satisfy all of these laws:
- Total heterogeneous lifting. Every variant of every concrete child or
wrapper terminal sum maps to exactly one variant of
R; no common erased payload,Any, trait object, serialization, or catch-all variant exists. - Exact provenance preservation. Each lift consumes the exact source
occurrence/capability, incarnation or generation, semantic lane, and owned
rejection.
Rmay carry a semantic role as a value, but a duplicate role value never substitutes for exact source provenance. - Compositional outward transfer. Passing through hosts is function
composition:
lift_A_to_C = lift_B_to_C ∘ lift_A_to_B. A wrapper may lift its own terminal variants, but it cannot inspect, discard, duplicate, reorder, or reinterpret an inner terminal variant. A terminal-transparent wrapper changes only the inferred private lift composition. A wrapper with a genuinely new terminal law requires a semantic variant inR, never a structural-position variant or caller-authored path. - Closed duplicate-role handling. Two children with equal semantic role values retain different exact origins internally. Their lifts may target the same domain variant only when that variant stores the distinguishing exact provenance; equality of role values is never the injection key.
- Bounded diagnostics. Compile fixtures at one, two, and eight repetitions of a terminal-transparent wrapper must expose the same root semantic type and primary error. Diagnostics must contain none of the private structural product/path names, and their recorded rendered-size budget may not grow with wrapper depth.
The Rust mechanism that supplies these lifts is deliberately unselected. A
user-written callback or alias per child would violate the no-plumbing law;
automatic structural projection could reproduce the original type explosion.
The prototype must prove one inferred concrete mechanism through two
heterogeneous children, duplicate role values, and two wrapper orders before
TerminalSettlement becomes an authorized public name.
Creation-scoped dependency equation
Settlement does not use a general runtime dependency graph. The selected
cross-lane dependency is any current occurrence-dependent operation authored
against a child created in the same action. The closed current set is
ObserveCreation, ObserveEstablishedCreation, ObserveChild,
ChildDelivery, ChildInput, and ShutdownChild. Lowering groups them
structurally at authorship time:
CreationBundle<CreateRequest, AfterCreation> {
creation: CreateRequest,
after_creation: AfterCreation,
}
AfterCreation =
Direct(OccurrenceEffects)
| AfterRequiredObservation {
required: RequiredObservations {
creation_observations,
established_creation_observations,
child_termination_observations,
},
then: OccurrenceEffects,
}
OccurrenceEffects {
child_deliveries,
child_inputs,
child_shutdowns,
}
LoweredAction<Independent, Creations> {
independent: Independent,
creations: closed heterogeneous product of CreationBundle values,
}
Each dependent value is physically owned by the same bundle as its one creation prerequisite; it does not name a lane index, wrapper path, runtime role, or registry key. The interpreter handles one bundle as follows:
creation accepted(exact binding), Direct(effects)
-> interpret every occurrence-dependent effect against that binding
in named lane order
creation accepted(exact binding), AfterRequiredObservation { required, then }
-> interpret every required observation against that binding in this
named order:
1. creation_observations, in item order
2. established_creation_observations, in item order
3. child_termination_observations, in item order
-> preserve the authoritative settlement of every accepted and rejected
observation; one rejection never suppresses another observation
-> if all required observations settle accepted,
interpret every occurrence-dependent effect in `then`
one or more required observations rejected
-> retain every authoritative observation settlement independently
-> select the first rejected observation in the named order above as the
deterministic blocking prerequisite
-> return every dependent delivery, input, and shutdown untouched as
NotAttempted {
value,
blocked_by: bundle-scoped correlation of that first rejection,
}
creation rejected(rejection)
-> retain the one authoritative creation settlement
-> return every observation, delivery, input, and shutdown item as
NotAttempted {
value,
blocked_by: bundle-scoped creation-settlement correlation,
}
Each bundle-scoped correlation is created while producing the corresponding
creation or observation settlement and is copied only as non-authoritative
correlation. When several required observations reject, every rejection keeps
its own correlation and owned value; only the first correlation in named
interpretation order is copied into downstream NotAttempted settlements.
Resolving it requires no lookup because the enclosing staged settlement owns
the prerequisite and all dependents together. Independent effects remain
outside the bundle and are still interpreted. Multiple child creations are a
closed heterogeneous product of independent bundles, not a map. Direct
encodes creation → occurrence effects;
AfterRequiredObservation encodes creation → required observations →
occurrence effects. A future occurrence-dependent operation must be added to
this closed product and its settlement tests before it can lawfully appear in
the same action; there is no catch-all request and no general DAG API.
The completeness boundary is explicit. Ordinary logical delivery is
independent of a creator-local occurrence. ObserveEstablishedCreation is
inside the bundle because it is authored from the pre-commit ChildRoute and
returns the exact established capability only after creation commits.
EstablishedDelivery, ObserveEstablished, and ShutdownEstablished are
different: they require that resulting established capability as their input,
so they cannot lawfully be authored in the original creation action.
ChildReport originates at the child rather than the creating actor. Thus the
six families above exhaust the current operations that can consume the
pre-commit occurrence in the same creating action; adding another such family
reopens this equation.
Fresh allocation is the actor-model law. Using a creator-local route to stage an exact-capability observation after commit is Bombay's derived construction. Requiring that observation for stable proxies, interpreting the three required observation lanes in the named order above, and selecting the first rejection as the downstream blocking correlation are deliberate Bombay policy choices; they are not guarantees supplied by the actor model.
The retained Actions surface does not yet expose this grouping. A focused
core prototype must prove either a truthful lowering from existing named
products or the smallest required interpreter-facing product change. Until it
does, NotAttempted and every same-action dependent-effect row remain Open;
adding handwritten paths or a runtime dependency registry is not an allowed
fallback.
The required lane ownership is:
| Lane | Primary recovery while source lives | Surviving owner |
|---|---|---|
| proxy → worker command | proxy, which may report unavailable without replaying implicitly | proxy lifecycle host |
| owner → proxy install/replace | fixed/dynamic supervisor operation state | supervisor lifecycle host |
| pool → worker assignment | pool active job state | pool lifecycle host with complete customer obligation |
| fresh proxy/worker creation and creation observation | owning proxy/supervisor/pool pending-creation state | owning actor lifecycle host |
| activation begin/cancel | owning actor activation state | owning actor lifecycle host with exact unresolved activation record |
| child shutdown/observation request | owning proxy/supervisor/pool drain state | owning actor lifecycle host |
| restart/deadline scheduling request | owning recovery/drain state | owning actor lifecycle host |
| parent report | none after source commit; no implicit replay | reporting child's lifecycle host terminal settlement |
| management reply | none after source commit; no implicit replay | dynamic-supervisor host terminal settlement |
| lifecycle event | none after source commit; no implicit replay | supervisor host terminal settlement |
| customer outcome | none after active correlation removal; no implicit replay | pool host terminal settlement with complete outcome |
| finalization/task/terminal/shutdown report | none; the source may stop in the same action | source lifecycle host directly |
| operational diagnostic | policy below | lifecycle host directly |
This settlement is a foundational interpreter contract, not an actor delivery,
callback, global registry, or retry engine. Its precise association with the
current Behavior/Actions types and child lifecycle facts must be proven by
the focused core prototype before any dependent actor row can be implemented.
It does not add a fourth behavior effect: Actions still contains the explicit
communications and creations, and settlement is their typed interpretation
result.
Diagnostic policy
Every configurable diagnostic-producing actor selects one semantic sum:
Diagnostics[Route] = DeliverTo(Route) | Terminate
That notation is the domain equation, not the selected Rust representation.
A generic enum would leave Route unconstrained for Terminate and violate
the no-placeholder law. The compile prototype must instead produce an inferred
route-bearing builder state for delivery and an inferred route-free state for
termination, with one exhaustive internal transition law. It may not solve
inference with a default generic, public marker argument, or dummy route.
DeliverTo makes one attempt. Rejection immediately produces terminal
UndeliverableDiagnostic { diagnostic, reason } in action settlement; it does
not send a diagnostic about the diagnostic. Terminate skips delivery and
emits the original diagnostic through a named terminal-settlement interpreter
request in Actions while selecting Step::Stop in that same turn. The
interpreter settles that explicit request directly with the lifecycle host.
This gives an autonomous actor a truthful construction without a dummy route
or ambient side channel while keeping the exact value recoverable.
An owner-created proxy has a mandatory exact structural parent, so its
diagnostic disposition is fixed to DeliverTo(parent) by construction rather
than exposed as another builder axis. Rejection follows the same terminal,
non-recursive settlement law.
Terminate stops the actor in the diagnostic-producing action after preserving
its other required effects. A later UndeliverableDiagnostic rejection causes
a still-live actor's admitted settlement fact to select Step::Stop; if that
fact cannot be admitted, the host/root terminal disposition applies directly.
A diagnostic policy therefore has no recursive send, hidden effect, or
indefinite retained error state.
1. Stable worker proxy solution
Construction and protocol
Proxy<Worker> is created empty. Its public protocol is exactly the worker's
public protocol, preserving stable service identity. Its owner has a private
typed control protocol:
InstallInitial(Worker)
Replace(Worker)
Shutdown
The proxy reports to its established parent through one concrete report sum:
InitialInstallation(InstallationOutcome)
Replacement(ReplacementOutcome)
WorkerStopped(complete stop fact)
Unavailable(original sender, lifecycle phase, complete command)
Runtime state
Dormant
CreatingInitial {
attempt,
}
CreatingInitialAfterStop {
attempt,
stop_fact,
}
InitializingInitial {
incarnation,
initialization_settlement,
activation_plan,
}
InitializingInitialAfterStop {
incarnation,
initialization_settlement,
activation_plan,
stop_fact,
}
ActivatingInitial {
incarnation,
activation_attempt,
}
ActivatingInitialAfterStop {
incarnation,
activation_attempt,
stop_fact,
}
Ready {
incarnation,
}
EmptyInitial
EmptyAfter {
last_incarnation,
}
StoppingForReplacement {
current_incarnation,
reserved_attempt,
replacement_worker,
}
CreatingReplacement {
attempt,
replaces,
}
CreatingReplacementAfterStop {
attempt,
replaces,
stop_fact,
}
InitializingReplacement {
incarnation,
replaces,
initialization_settlement,
activation_plan,
}
InitializingReplacementAfterStop {
incarnation,
replaces,
initialization_settlement,
activation_plan,
stop_fact,
}
ActivatingReplacement {
incarnation,
replaces,
activation_attempt,
}
ActivatingReplacementAfterStop {
incarnation,
replaces,
activation_attempt,
stop_fact,
}
DrainingCreation {
attempt,
creation_kind,
}
DrainingCreationAfterStop {
attempt,
creation_kind,
stop_fact,
}
DrainingWorker {
incarnation,
}
DrainingInitialization {
incarnation,
initialization_settlement,
activation_plan,
}
DrainingActivation {
incarnation,
activation_attempt,
cause: ActivationDrainCause,
}
ActivationDrainCause is a closed private sum containing the complete
unaccepted BeginActivation request, accepted-plan rejection, logical
cancellation, or shutdown provenance. It is not an optional rejection field;
the final proxy outcome consumes exactly the variant that caused the drain.
EmptyInitial and EmptyAfter are distinct because replacement provenance is
present only in the latter. No Option<incarnation> hides that distinction.
Transition rules
InstallInitialis accepted only inDormant. Fresh nonce reservation and collision checking precede the creation action.CommandinReadyemits one exact child delivery. Every other live state emits oneUnavailablereport containing the untouched sender and payload.- Successful committed creation enters
Initializing*, owning the exact installed incarnation and initialization settlement. Complete successful settlement yields the one-shot permit and entersActivating*; only an exact ready fact entersReady. Immediate activation may collapse the activation request/resolution, but never the committed initialization phase. - Initialization-fold or host rejection executes no initialization action and
returns the proxy to the appropriate empty state. Initialization-effect
rejection after commit enters
DrainingInitialization; it reports the distinct rejection only after the exact installed incarnation is terminally settled, and cannot become empty earlier. Activation rejection follows the same drain-before-outcome law throughDrainingActivation. ReplaceinReadyreserves the replacement nonce first, then emits one shutdown request and owns the replacement definition.ReplaceinEmptyAfterstages the replacement immediately.Replaceelsewhere returns the submitted worker in a typed rejection.- A matching stop in
Creating*moves to the correspondingAfterStopstate and emits no premature parent result. - Matching installed creation from
Creating*AfterStopenters the matching initialization/stop join and can emit onlyStoppedBeforeReady, neverReady, even if later initialization and activation facts succeed. - Rejected creation from an
AfterStopstate producesContradictionwith both authoritative facts. - Matching stop from
ReadyemitsWorkerStoppedand becomesEmptyAfter. - Matching stop from
StoppingForReplacementemits the stop report, stages the already-reserved replacement, and moves toCreatingReplacement. - Any pre-readiness failure after installation commits first stores its complete cause and drains that incarnation. The proxy emits its one atomic parent outcome and becomes empty only when the exact terminal or forced transfer fact closes that drain. The owning supervisor's occupied authorization ticket therefore cannot be released on a merely intermediate failure fact.
- Stale, duplicate, wrong-kind, and wrong-worker inputs preserve proxy state and emit one complete worker-specific diagnostic carrying the current proxy phase. The expected correlation remains solely in proxy state.
- Shutdown maps every live state to its corresponding drain state. It never sends to an already observed-dead worker and never realizes a cancelled replacement definition.
- A parent-report delivery rejection never rewinds these states. The complete rejected report goes directly to the proxy host's terminal settlement; the proxy does not recursively diagnose failure of its mandatory structural report.
Proxy effects
worker_deliveries
worker_creations
worker_creation_observations
worker_stop_observations
worker_shutdowns
parent_reports
The interpreter order is creations first by Actions, then observation
requests, worker deliveries/shutdowns, and finally parent reports. Focused
tests assert the complete product for every transition, including empty lanes.
2. Fixed supervisor solution
Construction
The earlier five-axis product was incomplete. The candidate construction proof must account for:
Factory = Missing | Selected(factory)
Roles = Empty | NonEmpty(ordered unique roles)
Activation = Missing | Immediate | Reported(concrete activation contract)
ActivationAuthorizationLimit = Missing | MaximumUnresolved(positive bound)
Recovery = Missing | Selected(policy)
Failure = Missing | Selected(reaction)
ActorDrain = Missing | Selected(ActorDrainPolicy)
Diagnostics = Missing | DeliverTo(concrete route) | Terminate
Events = NotPublished | Published(concrete delivery route)
Events is not a mandatory proof axis: an autonomous fleet builds in
NotPublished without a dummy route. When selected, the concrete route kind is
preserved and receives ready stable-proxy capabilities and lifecycle events,
never worker recipients. A typed status query remains available independently.
The exact minimal typestate and
diagnostic-disposition types are open; this list is a semantic checklist, not
permission to publish one marker type per line.
build checks duplicate roles and prepares every initial factory result before
constructing the behavior. Infallible and fallible factories need distinct
truthful construction forms. A heterogeneous worker sum is accepted only when
all variants share one public protocol.
Member state
Declared { role, initial_worker }
CreatingProxy { role, initial_worker, proxy_attempt }
ProxyStoppedBeforeCreation {
role,
initial_worker,
proxy_attempt,
stop_fact,
}
WaitingForActivationAuthorization {
role,
proxy_route,
proxy_capability,
prepared_worker,
operation_ticket,
}
InstallDispatched {
role,
proxy_route,
proxy_capability,
operation,
}
AwaitingProxyOutcome {
role,
proxy_route,
proxy_capability,
operation,
}
Online {
role,
proxy_route,
proxy_capability,
worker_evidence,
}
Empty {
role,
proxy_route,
proxy_capability,
last_worker_evidence,
}
AwaitingInitialThenReplace {
role,
exact pending initial proxy operation,
recovery_ticket,
prepared_replacement,
}
WaitingToRestart {
role,
proxy capability,
prior_worker_evidence,
recovery_ticket,
prepared_replacement_and_operation_ticket,
}
ReplacementDispatched {
role,
proxy capability,
prior_worker_evidence,
recovery_ticket,
operation_ticket,
}
AwaitingReplacementProxyOutcome {
role,
proxy capability,
prior_worker_evidence,
recovery_ticket,
operation_ticket,
}
Stopping { role, exact proxy ownership }
Retired { role, terminal reason }
The proxy-creation join has only CreatingProxy and
ProxyStoppedBeforeCreation. Installed plus prior stop retires the dead proxy;
rejected plus prior stop returns both facts as a contradiction. Worker
creation/readiness is not reconstructed in this actor because the proxy returns
one atomic readiness outcome. The proxy's installed-but-activating states
remain hidden here.
Proxy commit moves the retained initial definition to
WaitingForActivationAuthorization. The supervisor owns one global bounded
activation-admission state with an ordered waiting-role queue and an exact set
of occupied operation IDs. Each initial ID is paired once with a private
witness during proxy-reservation preparation before any initialization creation
is emitted. Opaque pair identity makes collision unrepresentable; process-wide
allocation failure is not a supervisor rejection. Reserving an activation slot and emitting the
already-ticketed proxy install input are one commit. Mandatory settlement discharge resolves
InstallDispatched before another ordinary input or proxy report: acceptance
enters AwaitingProxyOutcome, while rejection returns the complete install
input to recovery. The slot is released only by the matching proxy atomic
outcome or forced ownership transfer during drain.
An initial outcome matches the exact proxy source and expected initial
operation. A replacement outcome additionally matches its nested replaces
WorkerIncarnationEvidence against the participant's exact retained prior
evidence. The supervisor does not inject or require its recovery ticket in the
proxy protocol; source, operation kind, and predecessor evidence are already a
complete correlation.
For replacement, one fresh affine operation pair per selected participant is created during recovery preparation and stored with that participant's prepared replacement. The issuing transition consumes the stored ID; it never allocates one. Worker preparation, recovery/timer correlation, budget, and checked release remain the recoverable pre-commit failures. No operation ID can overwrite or reuse another pair.
Recovery decision
This proposal originally copied trigger classification, budget charge, release
provenance, and an eight-way prerequisite product into the admitted recovery.
H76 rejected that Rust representation without changing the semantic law. The
normative current representation is owned by
fixed-supervisor.md:
the participant order is before_trigger / trigger / after_trigger; restart
release stores only Ready, an unsettled schedule, or one awaited timer;
participant readiness remains in its current subject; activation availability
is derived at issue time. Values already consumed by admission remain only in
their actual owner, never as recovery breadcrumbs.
Before admission:
- classify eligibility from the complete terminal outcome;
- select the complete ordered candidate set;
- reject any overlap with an existing recovery ticket;
- call every selected factory and retain every result locally;
- reserve one exact operation ticket for every prepared replacement;
- prune budget evidence using the event time;
- reject out-of-order time explicitly rather than discarding future evidence;
- validate the atomic budget charge;
- compute checked delay and reserve exact timer correlation; and
- commit admission membership, correlations, prepared ownership, and budget charge once.
Any failure before step 10 leaves every non-trigger member and the budget
unchanged. Later participant delivery, readiness, and replacement realization
may fail independently and are never rolled back as a batch. The trigger's
authoritative stop is represented as Empty; the
configured failure reaction then maps the topology to RetireMember or
StopSupervisor drain.
Fixed supervisor effects
proxy_creations
proxy_creation_observations
proxy_stop_observations
proxy_install_inputs
proxy_replacement_inputs
proxy_shutdowns
restart_schedules
lifecycle_events
management_replies
terminal_diagnostics
delivery_rejections
The AA-10 oracle remains blocked on the interpreter-facing child-terminal settlement that must return an emitted install or replacement when a proxy terminates without its normal outcome. These effect names do not implement that transfer, and no fixed-supervisor row depending on it may be promoted until a locked-boundary witness exists.
3. Dynamic supervisor solution
Construction and public protocol
The builder has at least these mandatory choices:
UnexpectedExit = Missing | KeepEmpty | Retire
EntryLimit = Missing | MaximumEntries(non-zero bound)
Activation = Missing | Immediate | Reported(concrete activation contract)
ActivationAuthorizationLimit = Missing | MaximumUnresolved(positive bound)
ActorDrain = Missing | Selected(ActorDrainPolicy)
Diagnostics = Missing | DeliverTo(concrete route) | Terminate
Lifecycle = Missing | DeliverTo(concrete durable route)
Its public messages are:
Start { key, worker, reply_to }
Stop { key, reply_to }
Replace { key, worker, reply_to }
Query { key, reply_to }
Cancel { operation, reply_to }
Each request contains only its request reply route. The builder's one durable lifecycle route owns every later event; start cannot select, transfer, or replace it.
Entry state
CreatingProxy { entry_generation, operation, worker, proxy_attempt }
WaitingForActivationAuthorization {
entry_generation, operation, worker, proxy
}
InstallDispatched { entry_generation, operation, proxy, install_attempt }
AwaitingProxyOutcome { entry_generation, operation, proxy, install_attempt }
Ready { entry_generation, proxy, incarnation }
Empty { entry_generation, proxy, last_incarnation }
Stopping { entry_generation, proxy, stop_operation, shutdown_observation }
Replacing { entry_generation, proxy, prior_incarnation,
phase: ReplacementPhase }
Cancelling { entry_generation, proxy_or_pending_proxy, operation,
phase: CancellationPhase }
Draining { entry_generation, phase: DrainEntryPhase }
Retiring { entry_generation, exact_unresolved_ownership }
Start preparation is transaction-local: it reserves every correlation before
committing CreatingProxy and the creation action together. There is no stored
or public Reserved phase.
ReplacementPhase and CancellationPhase are closed private sums mirroring
the ownership boundary above: definition local before input emission, proxy
creation emitted, waiting for activation authorization with committed proxy
and definition, install input emitted, awaiting atomic proxy outcome, ready,
or shutdown-owned. Only the first three variants own a returnable worker
definition. DrainEntryPhase owns any pending proxy creation, transferred
install settlement, unresolved proxy outcome, exact proxy shutdown, and
forced-retirement deadline.
The supervisor owns a bounded activation-admission set and an operation-order
waiting queue. Reserving a slot and emitting install/replace are one commit.
Mandatory settlement discharge moves InstallDispatched to
AwaitingProxyOutcome before another ordinary command or proxy report. The
proxy alone owns worker creation, initialization, activation, and their races;
it publishes no progress facts and returns one atomic terminal outcome. That
outcome releases the slot.
The table is keyed by a semantic Key; proxy nonces are generated and retained
separately. Duplicate start and capacity rejection return the submitted worker.
Started is emitted only from AwaitingProxyOutcome after the proxy's atomic
Ready outcome and contains the retained exact proxy capability.
Retirement removes the entry after all exact facts resolve. Reusing the same
key creates a fresh entry_generation, so old facts cannot affect the new
entry. KeepEmpty consumes capacity. Accepted start/replacement operations
carry separate cancellation tokens; cancellation is a total match over each
operation phase, not a boolean on the entry.
Request reply routes are not retained in long-lived entry state after their reply action is emitted; rejection ownership moves to action settlement. The durable lifecycle route and diagnostic policy are supervisor-global builder state rather than duplicated per entry.
The public query projection is exhaustive but does not expose owned values:
CreatingProxy | WaitingForActivationAuthorization | AwaitingProxyOutcome | Ready | Empty | Stopping | Replacing | Cancelling | Draining | Retiring; an absent key returns Unknown. It does not claim to
distinguish worker installation from activation because the proxy protocol
publishes no such progress facts. No public Option plus phase flags encodes
this sum.
Stop is the single public command that drains and removes a ready or empty
entry. There is no parallel retirement command; automatic retirement remains
the distinct UnexpectedExit::Retire policy.
Stop, replace, query, unexpected exit, unavailability, and global shutdown are total matches over the entry sum. Commands rejected by the current state return every owned input. A shutdown state owns a drain sum per entry:
The Draining variant above is the one shutdown equation; there is no second
informal AwaitingProxyCreation | StoppingProxy | Retired state list.
No management mutation is accepted after the global actor enters drain.
Dynamic supervisor effects
proxy_creations
proxy_creation_observations
proxy_stop_observations
proxy_install_inputs
proxy_replacement_inputs
proxy_shutdowns
management_replies
lifecycle_events
terminal_diagnostics
delivery_rejections
4. FIFO pool solution
Construction
The builder product is:
Factory = Missing | Selected(factory)
Workers = Empty | NonEmpty(ordered unique roles)
Activation = Missing | Immediate | Reported(concrete activation contract)
ActivationAuthorizationLimit = Missing | MaximumUnresolved(positive bound)
Recovery = Missing | Selected(policy)
Backlog = Missing | Selected(capacity)
Interruption = Missing | Fail | Retry
Distribution = Missing | FIFO
ActorDrain = Missing | Selected(ActorDrainPolicy)
Diagnostics = Missing | DeliverTo(concrete route) | Terminate
Only the complete product builds. Retry and Fail both retain a job copy
while it is assigned because the worker owns the dispatched message. Payload
clone occurs before admission commit; the customer route never leaves the
pool. The public behavior therefore states its required clone bounds honestly;
if cloning unwinds, no pool state or Actions value has committed. This law
does not promise to recover a caller value already moved into the fold during
unwind.
Job ownership
QueuedJob {
job_id,
admission_ordinal,
customer_route,
retained_payload,
origin: NeverAssigned
| Retried { prior_role, interruption_classification },
}
AssignedJob {
assignment_id,
completion_correlation,
worker_birth_evidence,
job_id,
customer_route,
retained_payload,
worker_role,
worker_incarnation,
}
The FIFO backlog owns only QueuedJob. A busy member owns its single
AssignedJob; there is no separate in-flight collection. A dispatch gives the
worker an affine completion authority paired with the assigned record's
non-authorizing completion_correlation. The worker cannot inspect or
construct either value, and retained correlation cannot manufacture a result.
The invariant is one authoritative customer obligation and active completion
correlation, not one physical Rust value. Under Retry, the pool retains the
canonical retry payload while the worker owns a cloned execution payload;
neither value independently authorizes a customer outcome.
The canonical pool protocol is:
Accepted { request, job }
Rejected { request, payload, reason }
Completed { job, role, result }
ReturnedQueued { job, payload, reason }
ReturnedAssigned { job, role, payload, reason }
Both admission variants echo the caller's opaque request correlation;
Accepted also returns the distinct pool-issued job identity. The pool never
trusts request correlation as internal identity or retains it in the accepted
job: admission commit moves it into Accepted, while rejection moves it into
Rejected. Accepted and Rejected are admission outcomes. The remaining
three are the only terminal customer outcomes. No generic Returned variant
exists: a role is known only after assignment. Rejection of any emitted outcome
transfers that exact value to action settlement and cannot recreate an active
job.
Pool member state
The pool creates workers directly. The stable pool actor retains the mapping from semantic role to each fresh worker incarnation; no stable proxy exists in this topology. Pool lifecycle rules use the same policy values as fixed supervision but are implemented directly in the pool fold. Its complete pre-ready ownership progression is:
Prepared { role, worker_definition }
Creating { role, creation_attempt }
Initializing {
role, incarnation, initialization_attempt
}
WaitingForActivationAuthorization {
role, incarnation, activation_permit, activation_plan
}
ActivationDispatched { role, incarnation, activation_attempt }
Activating { role, incarnation, activation_attempt }
DrainingPreReady {
role, incarnation,
cause: initialization rejection | activation-start rejection
| accepted-plan rejection | cancellation | shutdown
after_drain: ResumeRecovery(recovery decision) | Retire(terminal reason)
}
Recovering { role, exact decision, prepared worker or exact timer }
Stopping { role, exact outstanding worker ownership }
Retired { role, terminal reason }
The pool also owns one declaration-order activation waiting queue and an exact
bounded set of occupied activation tickets. While initialization is unresolved,
the lifecycle host owns the linear settlement and activation plan; actor state
retains only initialization_attempt. Its exact result transfers the permit
and plan into the waiting state. Reserving a ticket and emitting
BeginActivation are one commit. Activation settlement, stop, and forced drain
may arrive in either order; no synchronous interpreter guarantee is assumed.
Rejected dispatch returns the
complete request to recovery; exact ready, activation rejection, stop, or
forced transfer releases the ticket once. None of these pre-ready states is
eligible for job dispatch. A post-commit initialization or activation failure
enters DrainingPreReady and cannot enter recovery or accept work until exact
terminal or forced-transfer settlement. Ready worker states are split into:
Idle { worker route, private worker-birth evidence }
Busy {
worker route,
private worker-birth evidence,
assigned_job,
assignment_delivery_completion_stop_join,
}
Recovery states that originate from Busy own one interruption resolution:
ReturnFailure(assigned_job)
RequeueRetry(assigned_job)
The exhaustive assignment join orders delivery settlement, completion, and worker stop before it emits or requeues the selected alternative. No permanent returned-assignment tombstone is stored. A later result with that token becomes an operational stale diagnostic and cannot select a customer.
The pool maintains this invariant after every fold:
no serviceable queued job coexists with an eligible Idle member
Initial readiness, replacement readiness, recovery completion, and job
completion all run the same oldest-eligible dequeue transition. If backlog is
serviceable, the transition commits the worker directly to Busy and emits
the assignment; it never commits an intermediate Idle. Only an empty or
currently ineligible backlog permits Idle. FIFO assignment to a newly ready
role advances the circular cursor exactly as an ordinary assignment to that
role; readiness does not create a separate fairness rule.
Admission and dispatch
- Preserve the caller request correlation and classify shutdown, serviceability, idle availability, and full backlog before allocation.
- Reject full or unserviceable submission unchanged; otherwise reserve one
fresh
{ job identity, admission ordinal }pair. - Apply the maintained invariant: an eligible idle role implies there is no older serviceable backlog job; this is a state proof, not a runtime assert.
- Select the next ready idle role from the circular declaration-order cursor.
- If idle, reserve a fresh assignment/correlation-authority pair and clone the
execution payload before commit; retain the canonical payload, customer
obligation, and non-authorizing correlation, then commit
Busyand emit one assignment carrying the affine authority. - A job-pair or immediate-dispatch preparation failure rejects through its own typed branch and never falls back to queue admission.
- Otherwise move the original payload into the queue ordered by immutable admission ordinal.
Capacity is the new-admission waiting bound. A zero-capacity pool takes branch
4 or 6. Retry is not new admission: it may insert one formerly active job per
role beyond that bound, so the complete queue remains bounded by capacity plus
roster size.
Queue-to-worker dispatch reserves its assignment/correlation-authority pair at
dispatch time. Exhaustion or collision returns that accepted queue head through
its exact ReturnedQueued or ReturnedAssigned provenance and continues the
finite FIFO fill; it cannot strand a serviceable head beside an idle worker.
Completion resolves the private authority, then validates the report's
creator-local child nonce plus private worker-birth evidence before mutation.
A match uses the pool-retained customer
route, emits one customer result, advances the cursor, and either commits the
member directly to Busy with the oldest eligible queued job or to Idle when
none exists. A mismatch emits the complete result only on the separate
diagnostic disposition and preserves active ownership.
Recovery and shutdown
Worker lifecycle uses the same policy values as fixed supervision but remains
pool-owned transition code over direct worker children. A busy-worker stop
first resolves its exact
AssignedJob according to Fail or Retry, then begins recovery. Irrecoverable
retirement redistributes general FIFO backlog to surviving workers; if no live
or recoverable worker exists, all stranded jobs receive one terminal outcome.
Retry reinserts the interrupted assignment by its immutable admission
ordinal, preserving order even when several workers stop in a different order
from their jobs' admission. The queue stores only prior role and semantic
interruption classification; recovery alone owns the authoritative stop. Exact
assignment-delivery rejection uses typed affine reunion before the same ordered
reinsertion and is a safe pre-execution return. Delivery settlement,
completion, and worker stop join in any order without a second job disposition.
Shutdown atomically extracts all queued and assigned jobs into customer
outcomes, transfers their active completion joins, cancels delayed recovery,
and drains every direct worker child. Later completions are stale diagnostics
and emit no second customer outcome. ActorDrainPolicy explicitly chooses
WaitForActorGraph or exact RetireActorGraphAfter retirement with forced
facts.
FIFO pool effects
CreateWorker { reservation, submission }
ObserveWorkerCreation { reservation }
ObserveWorkerStop { child_nonce }
BeginWorkerActivation { child_nonce, attempt, permit, plan }
CancelWorkerActivation { child_nonce, attempt }
DeliverAssignment { child_nonce, assignment, command }
ShutdownWorker { child_nonce, correlation }
ScheduleRestart { recovery, timer, deadline }
ScheduleDrainDeadline { timer, deadline }
DeliverAdmission { route, outcome }
DeliverCustomerTerminal { route, outcome }
DeliverDiagnostic { route, diagnostic }
TransferTerminalDiagnostic { diagnostic }
TransferUnexpectedInput { input }
TransferDrainResidual { residual, cause }
ReportCompletionToParent { completion_evidence }
Each operation has its own accepted/rejected settlement carrying the complete request or outcome. Ordered non-transactional application returns an applied prefix, one exact rejected operation, and the closed owned unattempted suffix. The current runtime does not yet provide that result, and local parent report delivery discards closed-parent rejection; both remain explicit blockers.
5. Keyed pool solution
The keyed pool is a separate direct fold. It reuses configuration value types and assignment/outcome value types, not a nested FIFO pool behavior.
Its builder replaces Distribution = FIFO and global backlog with:
Distribution = Keyed(concrete selector)
Backlog = PerRole(capacity)
Bindings = Maximum(capacity)
Runtime binding state contains only retained entries:
Bound { key, generation, worker_role }
Absence is represented by no table entry. There is no persistent Unbound
state, per-key counter, or tombstone. Each Bound entry is the sole pool owner
of its retained key. Each accepted job stores only copyable, non-authorizing
admitted-binding evidence { generation, worker_role }; it does not clone or
own the key. Each role has its own bounded FIFO queue, so a busy selected role
cannot consume another role's capacity or idle worker.
Rebalance validates an exact absence/generation expectation and the target's
separate management eligibility, then changes only the binding table. It never
walks or edits queued or assigned jobs. Unbind removes only
future-admission affinity and releases binding capacity; a later admission or
explicit unbound rebalance allocates a fresh opaque generation without
retaining history for the absent key.
For admission, the current target member projects to exactly one of:
AssignableNow = Idle
BacklogAdmissible = Prepared
| Creating
| Initializing
| WaitingForActivationAuthorization
| ActivationDispatched
| Activating
| Busy
| Recovering
| DrainingPreReady { after_drain: ResumeRecovery }
Unavailable = Stopping
| Retired
| DrainingPreReady { after_drain: Retire }
| any member after global shutdown owns it
This is a total match over the complete member sum. DrainingPreReady is not
classified from its cause by guesswork: the fold has already retained one
exhaustive after_drain disposition. There is no wildcard/default arm.
An admission-created binding is committed only with accepted work.
AssignableNow dispatches immediately; BacklogAdmissible requires remaining
capacity in that role's queue; Unavailable rejects. If the per-role capacity
is zero and the target is not ready-idle, the job and proposed binding are both
returned unchanged. This law distinguishes eventual service eligibility from
immediate assignability without inferring readiness from a role or binding.
Management-created bindings obey a different total projection because they admit no job and consume no backlog capacity:
Bindable = Prepared
| Creating
| Initializing
| WaitingForActivationAuthorization
| ActivationDispatched
| Activating
| Idle
| Busy
| Recovering
| DrainingPreReady { after_drain: ResumeRecovery }
Unavailable = Stopping
| Retired
| DrainingPreReady { after_drain: Retire }
| any member after global shutdown owns it
Unknown role is a distinct typed rejection. Irrecoverable is not retained as
another member variant: the irrecoverable decision commits a permanently
unavailable drain or retired phase. Temporary Recovering and
DrainingPreReady { ResumeRecovery } remain bindable and retain bindings.
Every management command carries one expectation:
BindingExpectation = Absent | Exact(BindingGeneration)
For an absent key, rebalance requires Absent, a bindable target, free binding
capacity, and a freshly reserved generation; success moves the command key
into the new binding without a job. Exact(_) is stale. Unbind with Absent
returns the typed AlreadyUnbound outcome; unbind with Exact(_) is stale.
For a binding at exact generation g, both rebalance and unbind require
Exact(g). Absent or another generation returns the complete command
unchanged with actual = Exact(g). A rebalance to the same bindable role is an
accepted no-op preserving g. A role-changing rebalance reserves fresh g2
before atomically moving the stored key into { g2, target }; failure
preserves the old binding and returns the complete command. Exact unbind
removes and returns the complete binding. Expectation comparison precedes
target validation, so stale commands cannot inspect then mutate a later
generation.
The per-role keyed queue obeys the same ready-time dequeue invariant as FIFO:
a readiness or recovery transition for role R commits directly to Busy
with R's oldest queued job when one exists. A new job for R cannot observe
an idle member while an older serviceable job remains in that role's queue.
The transition committing permanent unavailability extracts and terminates
that role's queued and assigned work once and atomically removes every retained
binding to that role, releasing capacity and emitting exact key/generation
diagnostics. Entering terminal pre-ready drain, terminal stopping, or retained
Retired state cannot leave a binding behind. Retry returns work to its
retained role while that role remains recoverable, and temporary recovery
retains bindings. A later rebalance cannot revive returned work. Shutdown
extracts every partition, deletes each returned active correlation after
committing its single customer outcome, removes every binding, and drains the
concrete direct-worker lifecycle owned by this fold. Late completions have no
customer destination and follow the operational diagnostic disposition; no
permanent tombstone is retained.
Shutdown representation for owners
Fixed supervisor transforms proxy ownership, while both pools transform direct worker ownership, into the same semantic drain alternatives:
AwaitingChildCreation {
exact attempt,
prior stop fact if it arrived first,
}
AwaitingChildActivation {
exact incarnation and activation generation,
prior stop or rejection fact if it arrived first,
}
StoppingChild {
exact route and capability,
}
Retired {
terminal fact or creation rejection,
}
RetiredForced {
exact forced-retirement fact,
host_transfer_receipt,
}
Pending recovery decisions move to:
CancelledTimer { exact timer identity and generation }
The retained replacement definitions are dropped as cancelled owned values;
they are never created. Exact later timer arrival consumes CancelledTimer.
Deadline retirement atomically transfers the complete outstanding ownership to the
surviving lifecycle host and records its typed receipt in RetiredForced.
That alternative is actor-side drained: exact late lifecycle, activation,
creation, delivery-settlement, and timer facts belong to the host and cannot
revive the actor. The actor selects Stop only when every drain member is
Retired or RetiredForced and every accepted pool job is completed, returned,
or included in an exact forced transfer.
If the surviving host is the application root, that transfer moves the root
run future to ActorGraphDrained { residual }; it does not complete the
future. A late ready fact is admitted to that residual state, the exact late
incarnation is drained, and only complete settlement permits Settled and the
final runner return.
WaitForActorGraph owns no deadline timer.
RetireActorGraphAfter owns one exact deadline timer and can enter
RetiredForced only from a still-outstanding alternative.
If the interpreter rejects that deadline schedule, the rejecting settlement
is itself the authoritative inability to enforce the configured bound. In the
same settlement turn, every still-outstanding member transfers to the
surviving host and enters RetiredForced with
DeadlineScheduleRejected { request, reason } as its cause. This immediate
forced transfer emits no accepted timer or child-stop fact and never falls back
to WaitForActorGraph.
Delivery rejection of a shutdown or final report never counts as a child
terminal fact.
Error and outcome boundary
Expected domain rejection is a successful transition with a typed outcome and unchanged state. Examples: duplicate dynamic key, full backlog, stale completion, unavailable proxy, budget denial, invalid rebalance, and overlapping replacement.
Controlled behavior error is reserved for contradictory authoritative runtime facts or violated interpreter contracts that cannot be represented as an ordinary request rejection. Every such error owns the complete conflicting facts. Sequence exhaustion that occurs before an externally authoritative transition returns a typed domain rejection with the submitted owned value.
No production branch uses panic!, expect, unreachable!, a sentinel, or an
empty collection as a semantic failure.
Coverage matrix
The range in each row covers every bullet currently present under that feature group in the feature catalogue. Counts are generated from the catalogue's top-level requirement bullets; a changed count invalidates this table. Open shared activation, settlement, completion-lowering, or drain realization is inherited by every row whose observable transition uses it.
| Requirement range | Design mechanism | Status |
|---|---|---|
| SH-ACTOR-01..07 | Five direct folds and explicit Actions are selected; authoritative initialization/interpreter ordering remains open | Open |
| SH-STATIC-01..06 | Closed event/report sums, concrete generics, role/nonce/capability separation | Design-covered |
| SH-CREATE-01..08 | Fresh staged creates, provenance, and typed exhaustion selected; initialization and dependent-effect settlement remain open | Open |
| SH-READY-01..21 | Authoritative installation before initialization effects, exact activation request/fact, concurrency, logical cancellation, and late settlement; retained-core/interpreter realization absent | Open |
| SH-CORRELATE-01..07 | Exact correlation newtypes, prepare-before-commit, owned rejection values, named lanes | Design-covered |
| SH-DELIVERY-01..18 | Route preservation, turn-local settlement priority, live-root residual ownership, and semantic terminal projection selected; progress bounds and realization absent | Open |
| SH-DIAGNOSTIC-01..09 | `DeliverTo | Terminate`, outward transfer, and non-recursive failure selected; terminal-settlement realization absent |
| SH-SHUTDOWN-01..16 | Dedicated drain sums, host transfer, live residual root, and selected ActorDrainPolicy; deadline/interpreter witness absent | Open |
| PX-IDENTITY-01..05 | One stable proxy with one exhaustive incarnation state | Design-covered |
| PX-INIT-01..08 | Empty proxy, private install, and local creation/initialization/stop join; initialization settlement remains open | Open |
| PX-ACTIVATE-01..10 | Installed/activating states and distinct initialization/start/activation outcomes; activation input remains open | Open |
| PX-ROUTE-01..05 | Ready-only delivery and complete unavailable report; activation and settlement inherited | Open |
| PX-REPLACE-01..11 | Reserved nonce and provenance specified; activation and report settlement inherited | Open |
| PX-STOP-01..04 | Exact stop matching and AfterStop joins; parent-report settlement inherited | Open |
| PX-SHUTDOWN-01..09 | Creation/activation/worker drain; parent-report rejection remains open | Open |
| FS-BUILD-01..13 | Canonical semantic axes known; minimal activation/diagnostic typestate and syntax remain open | Open |
| FS-TOPOLOGY-01..11 | Ordered role/member sum, waiting authorization, and exact proxy route/capability ownership; activation realization absent | Open |
| FS-FACTS-01..07 | Atomic proxy outcomes specified; readiness and lifecycle settlement inherited | Open |
| FS-OPERATE-01..06 | Status/capability snapshot and diagnostic disposition known; public protocol and settlement open | Open |
| FS-ELIGIBILITY-01..05 | Exhaustive Recovery policy match on complete terminal outcome | Design-covered |
| FS-STRATEGY-01..08 | Immutable snapshot selection and ticketed unresolved participants | Design-covered |
| FS-BUDGET-01..11 | Atomic charge, inclusive monotonic window, explicit out-of-order rejection | Design-covered |
| FS-TIMING-01..10 | Checked delay values and exact eight-state readiness/timer/authorization join; activation realization absent | Open |
| FS-ATOMICITY-01..04 | Prepare all factories and policy calculations before one commit | Design-covered |
| FS-FAILURE-01..04 | Failure reaction sum selected; diagnostic settlement and drain realization inherited | Open |
| FS-SHUTDOWN-01..08 | Owner-only drain is covered; deadline interpretation and rejected delivery inherit open shared laws | Open |
| DS-BUILD-01..07 | Exit/capacity/activation/durable-route requirements known; minimal builder remains open | Open |
| DS-START-01..10 | Keyed reservation and configured durable owner selected; readiness and reply/lifecycle settlement remain open | Open |
| DS-QUERY-01..04 | Read-only exhaustive projection selected; management-reply settlement and public protocol remain open | Open |
| DS-STOP-01..05 | Capability-owning stop states specified; reply/shutdown settlement inherited | Open |
| DS-REPLACE-01..08 | Stable proxy replacement and ready-only realization; activation path remains open | Open |
| DS-EXIT-01..06 | Empty/retain/retire sum; durable unavailability delivery remains open | Open |
| DS-RETENTION-01..06 | Bounded entries and generations selected; late-diagnostic settlement inherited | Open |
| DS-CANCEL-01..08 | Phase-exact ownership and outcomes selected; transferred install/activation/settlement realization remains open | Open |
| DS-SHUTDOWN-01..06 | Global mutation closure; deadline retirement and route rejection inherit open shared laws | Open |
| FP-BUILD-01..09 | Semantic axes known; minimal activation/diagnostic builder remains open | Open |
| FP-TOPOLOGY-01..12 | Direct pool-owned lifecycle, bounded authorization tickets, ready-time dequeue, and role preservation | Open |
| FP-ADMIT-01..11 | Authoritative obligation and atomic admission specified; reply/assignment settlement and completion lowering open | Open |
| FP-ASSIGN-01..09 | Opaque authority, exact rotation, and no-idle-with-backlog invariant specified; lowering and settlement open | Open |
| FP-COMPLETE-01..07 | Token/source match and stale lane selected; completion lowering and outcome/diagnostic settlement open | Open |
| FP-INTERRUPT-01..09 | At-least-once retry ownership specified; returned-outcome settlement inherited | Open |
| FP-FAILURE-01..05 | Retired-member extraction specified; diagnostic/customer settlement inherited | Open |
| FP-RETENTION-01..05 | Active-only correlation selected; safe late-result diagnostic settlement inherited | Open |
| FP-SHUTDOWN-01..07 | Atomic extraction/direct drain; rejected outcomes and deadline retirement remain open | Open |
| KP-BUILD-01..07 | Explicit submitted key plus one concrete selector selected; minimal builder and activation/diagnostic syntax open | Open |
| KP-AFFINITY-01..09 | Total member-state admission projection, per-role ready dequeue, and atomic binding admission specified; settlement inherited | Open |
| KP-REBALANCE-01..09 | Future-only rebalance/unbind specified; management reply settlement inherited | Open |
| KP-END-01..05 | Role-retained completion/retry specified; completion lowering and customer settlement inherited | Open |
| KP-RETENTION-01..06 | Automatic retirement unbind selected; exact diagnostic settlement inherited | Open |
| API-01..15 | Canonical fluent goals recorded; compile/diagnostic evidence absent | Open |
| EFFECT-01..22 | Named products and complete current creation-scoped dependency equation selected; lowering and rejected-delivery interpreter paths absent | Open |
| VERIFY-01..17 | Required state, residual-root, terminal-lift, dependency, and repository gates enumerated; no replacement implementation exists | Open |
| NONFEATURE-01..10 | Explicit deletion list after replacement proof | Design-covered |
Implementation acceptance rule
The next lawful stage is foundational prototype work, not implementation of the five actors. Its order is:
- initialization/authoritative-host ordering witness;
- complete creation-bundle lowering for every occurrence-dependent operation;
- per-item settlement, static terminal lifting, and residual-root ownership;
- exact activation permit/request/fact plus authorization tickets; and
- compiler-pass/fail builder and bounded-diagnostic prototypes.
Each prototype begins with its focused law regression and the smallest end-to-end interpreter witness. It may change production algebra only after the design-provenance ledger and change-containment threshold are recorded for that prototype. It may not simultaneously migrate actor templates.
Implementation of proxy, fixed supervisor, dynamic supervisor, FIFO pool, or keyed pool remains blocked until all five foundational prototypes compose. A row changes from design-covered to implemented only when its production symbols point back to those regressions. Legacy deletion begins only after all five templates are verified and migrated without compatibility wrappers.
Atomic actor verification contract (engineering record)
The campaign evidence in this record was last committed at 1f20cc4.
Its feature-complete verdicts describe local gates at that revision. The
current repository quality audit tracks later
verification and downstream integration separately.
Status: normative verification owner. No family document duplicates this matrix; each links here and adds only family-specific law references.
Current campaign state
feature-complete means every currently named aggregate-local gate passes. It
does not mean the whole campaign, Bombay integration, or the Bombay system is
complete. active means production behavior exists but a named local gate is
still open. The family law documents remain semantic oracles; they do not by
themselves establish implementation status.
| Family | State | Executable evidence | Remaining gate |
|---|---|---|---|
StableProxy | feature-complete locally | independent models, compile contracts, two stateful fuzz targets | final fresh audits and Bombay root custody |
FixedSupervisor | feature-complete locally | focused models, exact multi-role examples, four stateful fuzz targets, 34 worker-rejection cases, 30 source/corrupt/unattempted preparation-return cases, all 5,040 coordinated preparation/shutdown arrival orders, every distinct-role RestForOne overlap pair, 540 lawful three-recovery correlation traces, complete 753-candidate owner reconciliation, type-valid root/protocol inversions, exact lifecycle/management/event projections, and full aggregate-residue audit | final fresh audits and Bombay custody |
DynamicSupervisor | feature-complete locally | independent models, compile contracts, three closed mutation partitions, and four stateful fuzz targets | final fresh audits and Bombay root custody |
FifoPool | feature-complete locally | independent customer/recovery/retirement/queue models, mutation audit, performance workload, one stateful fuzz target | final fresh audits and Bombay custody |
KeyedPool | feature-complete locally | two bounded independent models, shared customer law suite, two stateful fuzz targets, and complete 232-candidate owner reconciliation with all 72 executable mutations caught and 160 compiler-unviable substitutions retained only as inventory | final fresh audits and Bombay custody |
The current fuzz manifest therefore contains thirteen replacement-family targets: two proxy, four fixed-supervisor, four dynamic-supervisor, one FIFO, and one binding-only plus one assignment/retirement keyed target. A prior combined keyed assignment/binding/retirement target was rejected because it mixed three independently testable responsibilities; its absence is not evidence that those gates passed.
Evidence order
- State the actor-model, derived, or Bombay policy law.
- Record the user syntax and complete observable transition.
- Add a focused compile or pure-fold regression in domain vocabulary that fails against the prior design for that law.
- Run deliberate inversion and show failure for the intended reason.
- Implement the smallest representation only after the law and interpreter path are coherent.
- Audit every production symbol against its pre-edit law and regression.
Compiler diagnostics can reject an encoding but cannot originate a type, trait, bound, callback, wrapper, alias, route, or policy.
Per-aggregate matrix
Every aggregate requires:
- exhaustive state/input classification and complete action/settlement assertions;
- an independently structured model using different vocabulary;
- bounded exhaustive interleaving exploration;
- property tests over longer sequences with invariants checked after every step;
- fuzzing of stale, duplicate, foreign, overlap, cancellation, retry, shutdown, and wrong-generation facts as applicable;
- debug and optimized replay;
- compile-pass canonical construction and use;
- compile-fail forged capabilities, tokens, identities, routes, and incomplete builders wherever those values are application-constructible or type-distinct;
- deliberate inversion for every law-sensitive regression;
- complete drain from every live phase;
- no lost affine value, duplicated terminal outcome, or inferred provenance; and
- no production panic for ordinary exhaustion or rejection.
Tests assert the complete Actions value or an independent trace. They do not
discard actions, predict private nonces, copy implementation branches into the
model, or put required transitions inside assertions.
Compile-time denial does not manufacture instance identity. When two actor instances intentionally share one concrete protocol, same-signature foreign correlations remain well-typed and their owning aggregate must reject them as stale without mutation. The compile contract instead prevents application forgery, duplication of affine authority, and substitution across genuinely different semantic types.
Shared-model gate
One independent law suite runs unchanged against every proposed consumer. A shared model is rejected if any consumer needs a mode flag, ignored result, placeholder, weakened error, alternate ordering, or special terminal policy. Extraction follows the second real consumer and must remove more semantic machinery than it adds.
Generic interpretation additionally requires two unrelated catalogue templates and two wrapper orders with no placeholder settlement event or per-template adapter. The real Bombay integration probe must conserve every affine value through the unchanged single Driver and retirement barrier.
Clean-room gate
Before deletion, preserve implementation-independent black-box traces, compile denials, adversarial cases, independent model expectations, and performance workloads. Commit that evidence separately, then create one explicit legacy deletion commit.
After deletion verify that every scoped symbol is removed or classified outside scope, no replacement module imports the baseline implementation, no legacy alias or alternate application path remains, and characterization tests cover every retained law. Deliberate policy changes are documented and tested rather than hidden as compatibility regressions.
DevX and performance evidence
Compare meaningful application lines, annotations, aliases, turbofish, structural concepts, public spellings, diagnostic size/distance, compiler time, monomorphization, binary size, allocations, and representative throughput. Define workloads and measure noise before judging differences. Code reduction is never inferred from a net-positive production delta.
Repository gates
All Rust/Cargo commands run through the pinned Nix environment. Feature-local gates precede the full matrix:
nix develop -c cargo nextest run --workspace
nix develop -c cargo fmt --all -- --check
nix develop -c cargo clippy --workspace --all-targets -- -D warnings
nix flake check
Applicable rustdoc, compile-fail, exhaustive, property, fuzz, benchmark, optimized, mutation, and model-checking gates are additional requirements, not substitutes. Candidate fixed point requires two consecutive fresh adversarial whole-repository audits with no credible untested high-value hypothesis.
Atomic actor downstream impact (engineering record)
Status: current read-only impact report after H591. This document assigns the remaining integration work; it does not authorize writes to a sibling repository and does not add a compatibility layer to Behavior Actors.
Result
The finalized behavior algebra has one direct runtime migration owner: Bombay.
Bombay must interpret the complete typed Actions value and carry terminal
behavior/environment custody through its existing Driver. Bombay Entity has a
later owner-side migration away from the pre-0.13 Behavior protocol wrapper.
Mnesis-Bombay has no direct atomic-actor call site; after Bombay's Entity surface
is released, it changes only its typed Entity projection and keeps durable
command completion and retry independent from actor lifecycle.
Consequently no downstream evidence requires restoring Proxy, Supervise,
WorkerPool, KeyedWorkerPool, WorkerStopped,
WorkerCreationResolved, or another removed atomic compatibility spelling in
this repository. ActorInterface is also not a prerequisite for action
settlement, terminal custody, Entity migration, or Mnesis durability.
Audited checkouts
The source trees were inspected in place and remained read-only. Because each sibling contained pre-existing work, both the Git identity and dirty-path count are part of the evidence boundary.
| Checkout | Audited revision and worktree | Selected Behavior/runtime generation |
|---|---|---|
| Bombay | ccfb8f695c475e5d812ae735d610de59b1d127aa, dirty refactor/address-integration, 86 paths | worktree manifest/lock select Behavior and Behavior Actors 0.14.0 plus macros 0.11.4 at 75a317235710f5e5ae2dba02f0600673764cf728 |
| Bombay Entity | 68d0f503205a569ddda88124f5add8e8a652e18f, dirty release-plz-2026-08-12T18-06-22Z, 2 paths | Behavior 0.9.1 |
| Bombay Entity Runtime | 442e735523f2591fa9d2c735834435afa8f51530, dirty feat/entity-runtime-v1, 9 paths | Behavior =0.9.1 |
| Mnesis-Bombay | 9d567f6d4f113bf3ac5ffeb569c219efb0ecf160, dirty main, 5 paths | released Bombay 0.1.0, Behavior 0.9.5, Bombay Entity 0.1.0, Mnesis 0.3.1 |
The finalized atomic worktree is at 50a226803ccf26673a6f08e99d189fe77870cb09.
Bombay's selected 75a3172 revision is an ancestor of it and therefore is not
the finalized build contract. Bombay's manifest/lock selection must first move
to the retained revision or a release containing it. Some dirty Bombay design
records mention other interim revisions; the manifest and lock are the actual
audited build selection.
Exact source trace
Bombay
Bombay is already the sole concrete runtime owner, but the audited source still implements the older effect contract:
crates/bombay/src/interpret.rsdefines a two-legInterpretationError<Creation, Send>.CommitActions::commitloops over creations, then callsInterpretSends, returnsResult<(), _>, and retains no complete accepted/rejected/blocked/corrupt/unattempted settlement.crates/bombay/src/application_runtime.rscontains 21 legacySendInterpreter,InterpretRequest, delivery, and child-input implementations.CreationResultscorrelates by a raw nonce in aHashMap, rather than by typed occurrence plusCreationId.spawn_childreports the olderCreationResolvedresult and publishes a child only after the old launch path completes.- Seven production sends discard the
ControlSender::sendresult acrossapplication_runtime.rs,reports.rs, andobservation.rs. Those sites can lose the exact source result, child fact, parent report, or observation event when admission has closed. crates/bombay/src/local.rsmakesCommitActionsreturn onlyResult<(), E>.ActiveEnvironment::retiredrains and drops resources and returns unit.crates/bombay-engine/src/driver.rsreturns onlyCompletionorDriverError;crates/bombay-engine/src/environment.rsfixes retirement to unit. The final concrete behavior and environment residual therefore do not cross the retirement barrier.crates/bombay/src/incarnation.rs,retirement.rs,generation.rs, andlaunch.rsreduce the terminal result to an exit/crash classification. Child tasks are awaited, but their complete stopped behaviors and unresolved settlements are not transferred to a parent or root custodian.crates/bombay/src/application.rsbegins withApplication::new(root)and appends application children afterward. Atomic templates whose root construction needs an installed lifecycle or diagnostic capability require the inverse, role-first order.
This is a contract migration, not evidence for a second runtime. Bombay's one
Driver, Communication mailbox, Address lease, Observe facts, Timers queue,
child host, and executor task hierarchy remain the concrete owners to extend.
Bombay Entity and Entity Runtime
The canonical Bombay Entity checkout pins Behavior 0.9.1 and its
crates/entity/src/protocol.rs directly composes WorkerStopped and
WorkerCreationResolved with the old Addr/Msg, turn-less transition, and
positional-send Behavior contract. The secondary Entity Runtime worktree has
the same dependency generation and direct spellings in
crates/entity/src/behavior.rs.
Those wrappers are not atomic actor owners. The migration retains the
runtime-neutral Entity laws—stable EntityId, single-flight activation,
generation-safe replacement, bounded admission, ordered drain fence,
passivation, and exact-incarnation retirement—but deletes the obsolete protocol
wrapper. Bombay then hosts the application's current concrete Protocol
directly and owns the private FIFO fence ingress. Updating either Entity
checkout to current Behavior in isolation would create two runtime generations
and is rejected.
The canonical migration target is the Bombay-owned Entity surface. The secondary Entity Runtime worktree is comparison evidence, not an additional runtime to preserve or migrate independently.
Mnesis-Bombay
No scoped atomic aggregate or removed atomic spelling occurs in Mnesis-Bombay production source. Its only current Bombay-facing production boundary is:
crates/bombay/src/routing.rs:Addressed<A::Id, Message>becomes(bombay_entity::EntityId<A::Id>, Message)without changing the payload; andcrates/bombay/src/transport.rs:ExecuteRequest<Request, Reply>keeps the runtime-neutral command request separate from Bombay's typed reply capability.
The behavior dependency is present in the released graph but no production
module interprets Actions or names an atomic actor. Mnesis owns load, decide,
append, optimistic-conflict handling, command identity, and durable outcome.
CommitFailure::Ambiguous and the
uncertain_append_failure_returns_command_identity_without_retry regression
already prove the critical separation: a restart may restore availability, but
cannot prove whether an append committed and cannot authorize transparent
retry.
After Bombay releases its owned Entity family surface, Mnesis-Bombay replaces
the standalone EntityId projection/dependency with the Bombay-owned typed
Entity reference projection. It must preserve the command unchanged and keep
Bombay admission/termination outcomes distinct from CommandOutcome. It does
not gain a supervisor, atomic actor, behavior interpreter, Entity directory,
mailbox, or persistence effect inside Behavior.
Required Bombay work by owner and order
The normative details remain in
atomic-runtime-settlement.md. The executable
downstream sequence is:
| Order | Owner and exact files | Required observable change | Explicit non-requirement |
|---|---|---|---|
| 0 | Bombay Cargo.toml, Cargo.lock, Engine fuzz manifest/lock | select the retained Behavior/Core, Actors, and macros revision as one family | no mixed revision, local compatibility fork, or floating sibling path |
| 1 | crates/bombay/src/interpret.rs; crates/bombay/src/application_runtime.rs | replace the two-leg/unit commit with one actions.interpret call and exact InterpretItem results for every statically known item | no universal runtime error, aggregate switch, erased envelope, or positional traversal |
| 2 | application_runtime.rs, child_bindings.rs, local.rs, launch.rs | route each creation batch once; establish children independently; retain ChildCreationOutcome; commit initialization settlement before publication/ingress | no nonce-as-identity, overwrite, rollback of an accepted prefix, or atomic-template host |
| 3 | interpret.rs, application_runtime.rs, reports.rs, observation.rs, local.rs | admit source-action and creation settlements in declared order; recover a closed control event exactly; retain the current value and untouched suffix after closure | no ignored send result, recursive actor call, detached task, callback, or second mailbox |
| 4 | application_runtime.rs, local.rs, launch.rs | host InitializeWorker on the installed child and BeginActivation in the existing typed task product; enqueue Started before polling and return the exact terminal input | no activation work in a behavior and no erased future/task collection |
| 5 | application_runtime.rs, time.rs, selected bombay-timers owner | implement total scheduling receipts/rejections and preserve exact scheduling results through the generic source-result path | no FixedSupervisor timer branch and no panic/exhaustion sentinel |
| 6 | crates/bombay-engine/src/environment.rs, driver.rs; Bombay local.rs, launch.rs, incarnation.rs, retirement.rs, generation.rs, application_runtime.rs | carry the final concrete behavior plus typed environment residual through the existing retirement barrier, parent admission, and a non-rejecting root custodian | no dropped terminal behavior, log-as-custody, second Driver, or alternate lifecycle service |
| 7 | crates/bombay/src/application.rs, application_runtime.rs | declare/install application actors by semantic role, obtain typed capabilities, then construct and activate the pure root | no MailAddr, structural occurrence path, explicit composed root type, dummy field, registry, or capability lookup in application syntax |
Orders 1–6 are one coherent custody contract: applying only the interpreter syntax while leaving unit retirement still loses affine values. Order 7 is a separate Bombay responsibility and may be developed independently, but both must converge before an end-to-end atomic application is claimed.
The generic item implementations in order 1 include logical/exact/child
delivery, observation, shutdown, scheduling, ReportToParent, source actions,
atomic diagnostics, CustomerDelivery, worker preparation, initialization,
and activation. They are capability-family implementations, not branches for
StableProxy, FixedSupervisor, DynamicSupervisor, FIFO, or KeyedPool.
Downstream dependency order
retained Behavior + Behavior Actors release
-> Bombay total interpretation and retirement custody
-> Bombay role-first application assembly
-> Bombay-owned Entity lifecycle/application surface
-> Mnesis-Bombay dependency and typed projection migration
The five local atomic aggregates do not wait for Entity or Mnesis. Their end-to-end runtime claim waits only for the separately authorized Bombay custody/application work. Entity and Mnesis then consume the released Bombay surface in their own campaigns.
Acceptance evidence for the downstream implementation
Bombay must provide caller-visible regressions before its production migration:
- every accepted, rejected, blocked, corrupt, and unattempted item returns its exact complete value in both relevant wrapper orders;
- semantic creation rejection permits independent later work but blocks only the exact child-dependent item;
- heterogeneous creations preserve their branch, occurrence,
CreationId, current child, initialization actions, and exact reason; - initialization actions settle before publication and ordinary ingress,
including initialization that selects
Step::Stop; - source results run to quiescence in declared order, and closed admission returns the current value plus untouched suffix;
- child-to-parent and parent-to-root closure injections preserve exact terminal custody;
- the unchanged single Engine Driver returns the final behavior and typed environment residual through its retirement barrier;
- a role-first DynamicSupervisor
Stop { key }application compiles without explicit worker/activation types or fabricated values; and - the selected Entity and Mnesis suites run separately against their released graphs until the owner-side release migration is complete.
Excluded campaigns
ActorInterface, external actors, receptionists, HTTP, discovery, clustering,
process-exit policy, durable inboxes, committed-event relays, and distributed
Entity directories remain independently owned work. The dirty Bombay checkout
contains ActorInterface and native Entity experiments, but neither changes
this report's dependency assignment and neither authorizes an actor-algebra
compatibility seam.
Aggregate-drift checkpoint
This audit changes no production state, protocol, transition, or public
spelling. H591's five transition authorities and measured family rows remain
unchanged: StableProxy 8 control alternatives; FixedSupervisor 5 roster
alternatives; DynamicSupervisor 2 availability plus 14 entry alternatives;
FIFO 5 pool states; KeyedPool 5 pool states. Every exact creation, settlement,
activation, lifecycle, command, and durable-outcome value retains its existing
owner. Arrival history, repeated causes, false cardinality, nested transition
authority, semantic booleans, dynamic escape, and structural user syntax remain
zero. Disposition: pass.
DevX research cross-audit for atomic supervisors and pools (engineering record)
Historical evidence snapshot: this record was last committed at 1f20cc4.
Its verdicts describe that revision; the current repository quality audit
tracks later verification and unresolved integration work.
This audit was performed after the feature catalogue, solution, DevX target, minimal core decision, and type equations were written. The research corpus is evidence and a dead-end register; it is not an architecture to copy.
Source corpus:
/Users/joel/Code/devrandom/bombay/research/devx-usability-loop.
Scope read
The audit covered every research document and the machine-readable campaign inventories:
GOAL,REQUIREMENT-AUDIT,INVARIANT-MATRIX,BASELINE,PROGRESS,HYPOTHESES, andACTOR-TEMPLATE-AUDIT;DEAD-ENDS,RUST-AUTHORING-MECHANISMS,ORDINARY-AUTHORING,OUTER-AUTHORING-PROBE,MACRO-LAST-COMPARISON, andPERFORMANCE;APPLICATION-TOPOLOGY-CONTRACT,SHUTDOWN-PLAN-AUTHORING,ESTABLISHED-CREATION-BLOCKER,REPOSITORY-AUDIT, andRUNBOOK;ENTITY-OBSERVE-LEVERAGE,ENTITY-INTEGRATION-CROSS-VERIFICATION,FRAMEWORK-COMPARISON, andSOURCES;evidence.json,framework-corpus.json,ratchets.json, both checking scripts, and the four stable-Rust mechanism probes.
Generated Cargo build output and captured compiler stderr are evidence owned by their reports; they are not separate design documents.
Relevant research laws retained
| Research finding | Atomic-actor consequence | Catalogue/solution coverage |
|---|---|---|
A Behavior transition may only return typed Actions; Tokio sends, clocks, hydration, and callbacks inside the fold are invalid. | All five templates are direct pure folds. Activation is an interpreter transaction and time arrives as typed facts. | SH-ACTOR, SH-READY, EFFECT, VERIFY |
| Creator-local nonce/occurrence is correlation, not actor identity. Successful installation must retain the exact committed capability, and collision is rejection rather than replacement. | Proxy/supervisor/pool creation reserves correlation before commit, observes explicit acceptance/rejection, and never derives incarnation identity from a nonce. | SH-CREATE, SH-CORRELATE, PX-INIT, PX-REPLACE |
| Same-action creation is interpreted before dependent child delivery/observation. | This governs ordinary behavior-authored creation actions, but it does not make initialization effects transactional. A worker host commits installed-but-not-ready before its initialization Actions are interpreted; later rejection drains the installed incarnation and cannot roll back prior effects. | SH-CREATE, SH-READY, proxy causal design |
| Logical, established, and mixed routes are different static capabilities. Selecting an exact runtime variant does not erase a logical host requirement. | Lifecycle, customer, diagnostic, and reply routes preserve their concrete DeliveryRoute; no template silently upgrades a logical identity. | SH-STATIC, SH-DELIVERY, FS-BUILD, DS-BUILD, FP-BUILD |
| BAX6: a supervised domain command can become unavailable after mailbox admission, and a terminal fold error is not an observable customer rejection. | The stable proxy must retain the full original sender/command and emit one typed unavailability fact. Delivery rejection has an explicit ownership law. | PX-ROUTE, FS-FACTS, SH-DELIVERY |
| BAX7: proxy installation and worker facts can arrive in either order; assuming creation resolution precedes a child report is invalid. | The proxy alone joins worker creation, initialization, activation, and stop facts order-independently. Fixed/dynamic owners receive one atomic proxy outcome; direct pools own their corresponding closed joins. No pair of flags coordinates them. | SH-READY, PX-ACTIVATE, FS-FACTS, DS-START, FP-TOPOLOGY |
| BAX10: pool shutdown must run through the pool-owned lifecycle path and return every owned accepted job. | FIFO/keyed draining closes admission, settles assignments/queues, drains direct workers, and emits exact forced facts under the selected deadline policy. | SH-SHUTDOWN, FP-SHUTDOWN, KP-END, EFFECT |
| A terminal supervision report is not a causal acknowledgement that the supervisor has stopped. Waiting on its termination before requesting shutdown is circular. | Lifecycle facts, delivery attempts, and actor termination are separate observations. Shutdown always has its own typed policy and completion. | SH-DELIVERY, SH-SHUTDOWN, FS-SHUTDOWN |
| Public examples must not expose generated products, structural selectors, nonces, effect paths, actor-space products, or shutdown coordinator representations. | Ordinary supervisor/pool source sees builders, domain values, policies, and public protocols only. ReportToParent is hidden behind assignment.complete. | API, DevX document, minimal-core visibility rule |
| Builders/typestate are acceptable only when they express real missing/selected semantic choices and compile diagnostics are measured. | Builder proof markers remain inferred/private; there is one canonical documented call order and no type per builder axis. | API, type inventory |
| Macros are last resort and may generate syntax only; a macro cannot resolve associated-type identity or repair a missing semantic composition. | No supervisor/pool macro, definition trait, or generated ownership engine is selected. Compile prototypes precede any syntax mechanism. | API, NONFEATURE |
| Exact external reply requires a real typed endpoint; admission is not execution or durable completion. | Request reply, durable lifecycle ownership, customer outcome, diagnostic outcome, and activation readiness remain separate capabilities/facts. | SH-DELIVERY, DS-START, FP-COMPLETE |
| Async Entity hydration completes before routability; installation alone is insufficient. | The same general lifecycle distinction is required for every worker incarnation without importing Entity machinery into actors. | SH-READY, PX-ACTIVATE, all worker-owning templates |
| Family capacity research distinguishes waiter, hydration, residency, and binding bounds. | Dynamic entry capacity, pool backlog, keyed binding capacity, and activation authorization are independent named policies. The authorization count has one law; supervisors reserve earlier than pools because their proxy outcome is opaque. | SH-READY, FS-TOPOLOGY, FS-TIMING, DS-START, FP-TOPOLOGY |
Forced retirement must preserve exact provenance; Drop, abort, logging, or a coarse crash is not orderly completion. | RetireActorGraphAfter produces typed per-child/per-job forced-retirement facts, transfers outstanding ownership, and counts the member actor-side drained. A root run future remains live until transferred external work and late exact drains settle. | SH-SHUTDOWN, actor shutdown sections |
Research results deliberately not copied
Several historical successes are dead ends for the new atomic design.
Old pool complete_to capability
ORDINARY-AUTHORING treated an assignment-carried complete_to recipient as
an improvement over a fabricated address. It is still unsafe: a worker can
substitute another capability of the same protocol. The new law is stricter:
the worker receives an opaque pool-issued token, and only the pool retains and
selects the customer route. Stale results use a separate diagnostic lane.
Existing supervisor/pool recipes
The catalogue demonstrates feature intent and interpreter seams, but its
ChildTopology, stable-proxy ingress plumbing, ownership/fleet/slot types,
positional effect paths, and nested wrapper equations are not retained. The
new architecture re-derives five direct folds from laws.
Universal behavior-layer composition
The research showed useful independent wrappers such as stash, timeout, and shutdown adaptation. That does not imply that a supervisor or pool is a stack of tiny behaviors. Each atomic template owns one coherent state machine and one action product. Orthogonal wrappers may still surround the finished actor.
Entity and topology machinery
Entity families, application host normalization, Axum boundaries, Mnesis durable outcomes, named application shutdown plans, and external ask are separate owners. Their discoveries inform readiness, capability, and shutdown laws, but their registries/directories/typestate must not be moved into supervisors or pools.
Framework APIs
OTP/Akka/Pekko/Orleans/Ractor comparisons corroborate lifecycle and supervision concerns. They are not semantic authority for Bombay and do not justify an untyped mailbox, dynamic registry, callback API, virtual actor, cluster pool, or another framework's restart defaults.
Complete dead-end disposition
The following dead-end families remain rejected:
- universal ingress inflation for all actors;
- a giant universal installation/hosting type equation;
- unindexed recursive type lookup and structural-key requirements on arbitrary domain types;
- protocol normalization by explicit positional allocation, keyed tries, or a closed local macro;
- per-role protocol/address spaces;
- an unconstrained shutdown target sum or a second shutdown DSL;
- positional paths hard-coded through Guardian/wrapper depth;
- direct Tokio reply, callback, channel, task, or clock effects inside a fold;
- using termination as a supervision-report acknowledgement;
- hard-coded parent paths or exposed
ReportToParentin worker code; - supervision rejection that discards the original command;
- readiness/restart provenance inferred from runtime ordering;
- a fabricated public proxy ingress that cannot return unavailable commands;
- a synchronous factory pretending to support asynchronous hydration;
- hard-coded outer
StopOnShutdownaround an already authored lifecycle; - dynamic registries,
dyn, boxed behaviors/futures,Any,TypeId, and erased envelopes; - per-family or nonce-derived address namespaces;
- target-as-origin external delivery;
- abort-on-drop as normal lifecycle ownership;
- a single undifferentiated capacity counter;
- unbounded per-identity telemetry or tombstone tables;
- making Discovery or Entity mandatory for local supervision;
- treating private observation, mailbox admission, actor termination, or a customer reply as durable command completion;
- a macro, alias, extension trait, builder stage, or callback whose only job is to move current compiler plumbing to every caller.
Features added or corrected because of the audit
The research audit changed the documents in these material ways:
- installation became an authoritative installed-but-not-ready host commit before initialization effects, followed by settlement, activation, and readiness, with distinct pre- and post-commit failures;
- no worker is advertised or assigned before
Ready; - pool completion now carries an opaque token rather than customer authority;
- stale completion became a diagnostic-only outcome, preserving the one-terminal-customer-outcome law;
- delivery attempt, delivery rejection, state commitment, and rejected payload recovery are specified separately;
- dynamic entries, keyed bindings, active correlations, and queues gained capacity/retirement/generation laws;
- shutdown gained exact
ActorDrainPolicyalternativesWaitForActorGraphandRetireActorGraphAfter { deadline }, explicitly separate from process exit; - dynamic request replies were separated from durable lifecycle ownership;
- fixed supervision gained status/snapshot requirements and optional lifecycle events;
- FIFO fairness became an exact circular-cursor algorithm;
- retry is documented as at-least-once execution; and
- typestate and fluent syntax were demoted from selected API to a hypothesis requiring compile-pass/fail and diagnostic measurement;
- every pool readiness/recovery transition must dequeue older eligible work before it can commit an idle worker;
- keyed admission became a total projection over every member phase;
- root-run completion now waits for transferred residual activation and late drain ownership rather than returning a dead error value; and
- terminal projection and same-action effect dependency now have explicit static equations whose Rust realizations remain prototype blockers.
Remaining blockers after the audit
The research does not determine these Bombay policy/integration questions. The specification has selected semantic answers for activation permits, logical cancellation, typed host settlement, and non-recursive diagnostic termination; their retained-core and interpreter realizations remain open:
- the smallest concrete association of activation permit/request/resolution with installation and initialization without adding hidden behavior effects;
- end-to-end typed host settlement for logical, exact, child, parent, lifecycle, customer, and diagnostic deliveries, including total heterogeneous terminal lifting and outward transfer through wrappers;
- creation-scoped dependency lowering from current
Actionswithout paths, lookup, or a general runtime graph; RetireActorGraphAfterinterpretation while non-cancellable external activation remains in flight, with the root run future owning residual settlement until it is empty;- a minimal public protocol/type spelling for status, management replies, durable lifecycle events, and pool acceptance/terminal outcomes; and
- compile-pass/fail prototypes proving canonical builders without turbofish,
public proof markers, aliases,
ReportToParent, or path-counting.
These rows remain Open in the solution matrix. No production symbol is authorized until its law, ordinary syntax, focused regression, lower-order composition, and interpreter witness are recorded.
Disposition of every other actor template (engineering record)
The five supervisor/pool actors are not a reason to delete the rest of
crates/actors. Most other templates own different, smaller transition laws.
They should remain independent. This audit classifies the current source and
prevents the supervisor/pool rewrite from becoming an accidental catalogue
rewrite.
No production deletion or migration is authorized by this document. Each
Update row needs its own law and regression after the five atomic actors are
designed; it must not be folded into their implementation stage.
Dispositions
- Keep — distinct law; no dependency on legacy supervisor/pool machinery.
- Keep, cross-cutting update — distinct law, but it inherits a newly found delivery, capacity, retention, readiness-name, or lifecycle gap.
- Replace — its feature belongs to one of the five new atomic actors.
- Remove after migration — structural plumbing exists only to support the legacy supervisor/pool architecture.
- Internal infrastructure — not an actor template and not ordinary DevX; retain only while a concrete interpreter/composition proof needs it.
Composition and lifecycle
| Current family | Disposition | Reason / required change |
|---|---|---|
Activate / Initialized / Active | Keep as advanced pure-fold tooling | It proves one-time synchronous Behavior initialization only. Rename no type, but documentation must say it is not worker activation/readiness. |
MessageAdapter | Keep, cross-cutting update | A real typed message transformation. It must inherit rejected-delivery ownership for logical, established/exact, and occurrence-aware creator-local child destinations. An adapter with no birth capability cannot fabricate or reconstruct a ChildRoute; the creating owner must carry the exact occurrence proof through the named effect lane. |
Machine | Keep | A direct finite-state/defer/replay actor with its own law. It is not supervisor behavior composition. |
Stash | Keep | A genuine transparent wrapper with replay and initialization-order laws. It is not a component of the new atomic actors. |
StopOnShutdown | Keep | Orthogonal shutdown-ingress wrapper. Supervisors/pools own their internal drains and may themselves be wrapped only when the composition law is explicit. |
FinalizeOnShutdown | Keep, cross-cutting update | Orthogonal finalization wrapper; final-send rejection must not be confused with successful finalization. |
Watch / Link | Keep, cross-cutting update | Exact/logical peer observation is a distinct capability. Observation and reaction delivery rejection need typed ownership. |
| termination monitors | Keep, cross-cutting update | Exact/logical observation plus cleanup/publication is distinct. Publication rejection must preserve its fact. |
| terminal propagation | Keep, update | Generic propagation remains valid, but the current supervision-specific terminal reason sum must be reshaped around the new recovery/forced-retirement outcomes. |
| homogeneous shutdown coordinator | Keep, update separately | It owns dependency-ordered child shutdown. It needs an explicit stuck-child/deadline law if it promises completion; it is not reused as the internal pool/supervisor drain engine. |
| heterogeneous shutdown coordinator and child-shutdown builder | Keep, update separately | Static heterogeneous application shutdown is a different topology problem. Keep its exact-role law; do not copy its public proof-state taxonomy into atomic builders. |
Task | Keep, cross-cutting update | One-shot typed task/result actor. Result delivery rejection must preserve the owned result. |
Discovery
| Current family | Disposition | Reason / required change |
|---|---|---|
Resolver | Keep, cross-cutting update | Immutable finite bindings supplied at construction. Only reply-delivery rejection is newly shared. |
Registry | Keep, update | Mutable key bindings are a distinct discovery law, but the current Vec has no capacity policy. Add bounded admission/removal semantics and rejected-reply ownership in its own stage. |
Topic | Keep, update | Typed membership/broadcast is distinct. Current subscriber growth is unbounded; add membership capacity and delivery-failure disposition. |
PubSub | Keep, update | Typed topics/subscribers are distinct. Current topic table is retained even when empty and both dimensions are unbounded; add topic/member capacity and topic-retirement law. |
Presence | Keep, update | Versioned presence/expiry is distinct. Current expired tombstones and participant table can grow forever; add bounded retirement/generation rules and reply-delivery rejection. |
These actors do not become Entity, supervisor, or pool services. Their logical host requirements remain a separate application-topology concern.
Operational and state actors
| Current family | Disposition | Reason / required change |
|---|---|---|
Configuration and Features alias | Keep, cross-cutting update | Single versioned configuration state; only reply-delivery rejection is shared. Features remains an alias, not a second actor. |
Health | Keep, cross-cutting update | Fixed component-health evidence is independent. Preserve exact evidence and rejected report ownership. |
current Readiness actor | Keep, rename/update | It means versioned readiness of a declared dependency set, not incarnation activation. Prefer the public name DependencyReadiness so the new Installed → Activating → Ready lifecycle cannot be confused with it. |
Cache | Keep, cross-cutting update | Bounded in-memory cache is independent. It is not durable persistence; module/docs should stop implying durability, and result-delivery rejection must preserve values. |
Moving Cache out of a persistence taxonomy is a documentation/module API
decision for a later contained stage. It is not necessary to implement the
atomic actors.
Routing and admission actors
| Current family | Disposition | Reason / required change |
|---|---|---|
Acknowledgements | Keep, update | Multi-party acknowledgement is distinct. Completed/cancelled records are currently retained forever; add capacity, retirement, and key-reuse generation laws. |
Buffer | Keep, cross-cutting update | Bounded deferred delivery is distinct. Update target/outcome rejection ownership. |
CircuitBreaker | Keep, cross-cutting update | Closed/open/probe and timer correlation are independent. Update attempt/outcome delivery rejection; do not merge its timer logic into supervisor recovery. |
Correlator | Keep, update | Begin/resolve/cancel correlation is independent. Terminal keys are currently retained forever; add bounded retirement/key-reuse laws and rejected-result ownership. |
Deduplicator | Keep, cross-cutting update | Already has explicit positive capacity/eviction. Update target/outcome delivery rejection only. |
OrderGate | Keep, update | Monotonic ordered release is independent. Its held BTreeMap is unbounded; add admission capacity and rejected-release ownership. |
PriorityQueue | Keep, cross-cutting update | Already bounded with typed token exhaustion. Update target/outcome delivery rejection. |
RateLimiter | Keep, cross-cutting update | Fixed token-bucket state is independent. Update target/outcome delivery rejection. |
Router and static strategies | Keep, update | Recipient routing is not worker ownership. Current mutable membership can grow unbounded; add capacity/removal policy and delivery rejection. Strategies remain pure values, not actors. |
Sequencer | Keep, update | Ordered gap release is independent. Future-position retention is unbounded; add gap/capacity/retirement law and target/outcome rejection. |
WorkQueue | Keep, clearly distinguish, update | It routes values to externally announced one-use worker routes. It creates no workers, observes no lifecycle, owns no completion, and performs no recovery. Therefore it is not a pool and the new pool must not contain it. Add delivery-rejection ownership; consider a less ambiguous public name only in a separate migration. |
The shared observation is not “make all routing actors use queue/correlation utils.” It is that several independent actors currently omit capacity or delivery-rejection laws. Each keeps its own direct fold and uses only a truly general interpreter capability once that capability is designed.
Timing
| Current family | Disposition | Reason / required change |
|---|---|---|
OneShot | Keep | Transparent single-timer wrapper with exact identity/generation. |
Periodic | Keep | Transparent repeated-timer wrapper with its own rescheduling law. |
Deadline | Keep | Absolute-deadline wrapper; distinct from supervisor drain policy. |
ReceiveTimeout | Keep | Mailbox-activity-reset timer wrapper; distinct from backoff or drain deadline. |
Lease | Keep, cross-cutting update | One bounded lease state machine with exact timer generation. Update outcome-delivery rejection. |
Timer IDs/generations and scheduling requests remain neutral interpreter carriers. The new actors reuse them; they do not copy these wrappers or form a generic scheduler component.
Workflow
| Current family | Disposition | Reason / required change |
|---|---|---|
Barrier | Keep, cross-cutting update | Finite declared membership and generation law are independent. Release-delivery rejection must preserve participant outcomes. |
Latch | Keep, cross-cutting update | Finite countdown/waiter law is independent. Release-delivery rejection must preserve waiters. |
Workflow | Keep, cross-cutting update | Finite dependency graph and step-state machine are independent. Outcome-delivery rejection must preserve the exact workflow result. |
Legacy supervision and pool surface
| Current source/surface | Disposition | Replacement |
|---|---|---|
supervision/adapter/proxy.rs, current Proxy, ProxyEvent, ProxySends, ProxyUnavailable | Replace | New stable proxy direct fold and its minimal control/report sums. |
Supervise wrapper and fixed/backoff recipes | Remove after migration | Fixed supervisor already owns supervision; applications should not assemble it from a wrapper/proxy/report stack. |
current fixed Supervisor and fixed_supervisor.rs | Replace | New fixed supervisor typestate definition and direct fold. |
current DynamicSupervisor | Replace | New dynamic supervisor with order-independent readiness, capacity, cancellation, generations, and durable lifecycle ownership. |
supervision/domain/{fleet,incarnation,ownership,restart_budget}.rs | Remove after migration | Private state belongs in each new direct actor. Pure recovery policy arithmetic may be rewritten once and shared only if two independent folds prove identical laws. |
ChildTopology, FixedFleetOwnership, slot/fleet/ownership types | Remove | Runtime roles/members are actor-private values, not a universal ownership model. |
current RestartConfiguration, RestartPolicy, RestartTiming, Backoff split | Replace/consolidate | Minimal Recovery, Strategy, RestartLimit, and RestartRelease sums. |
current pool.rs, WorkerPool, KeyedWorkerPool, aliases and events | Replace | Separate direct FIFO and keyed pool folds with no supervisor/proxy nesting. |
protocol/pool.rs, WorkerPoolProtocol, KeyedWorkerPoolProtocol | Remove after migration | New nominal public pool protocols selected by compile prototypes. |
current PoolAssignment, PoolCompletion, PoolCustomer, job/assignment IDs, pool response/rejection/failure sums | Replace | Opaque completion token, pool-retained customer route, one customer terminal outcome, separate diagnostics, private correlations. |
composition/report_relay.rs | Remove after migration | Its only production consumer is the legacy pool's proxy/report stack. New direct pool workers complete through the pool-issued token; supervisors receive direct proxy reports. |
supervision-specific protocol values in protocol/mod.rs (WorkerCreationResolved, WorkerStopped, replacement/report wrappers) | Remove/replace | Neutral CreationResolved, ChildStopped, exact activation facts, and actor-private control/report sums. |
supervision/pool-specific LogicalDeliveryProtocols implementations in requirements.rs | Remove/replace | New named actor products receive only their exact static requirement projections. The generic projection mechanism remains internal infrastructure. |
SupervisionFailureReason, RestartDenial, and supervision terminal reporting | Update, do not blindly delete | Exact terminal provenance is consumed outside supervision. Reshape it to the new recovery, activation, delivery, and forced-retirement outcomes; remove legacy-only variants and coarse factory failure. |
Neutral infrastructure kept under audit
The following are not “other atomic templates”:
DeliveryRouteand exact/logical recipient products;- neutral creation, observation, child shutdown, timer, and terminal facts;
- generated event/send products and static logical-host projections; and
- occurrence/position machinery required by closed child products.
They remain in the minimal-core/interpreter audit. Atomic actor builders and ordinary examples must not name their structural paths or product nesting.
Result
The catalogue does not need a mass deletion. The clean boundary is:
- replace the complete legacy supervision/pool island;
- remove structural relays and supervision-specific protocols left with no consumer;
- retain independent actors and wrappers;
- later give every route-emitting actor the same foundational rejected-delivery law; and
- separately repair unbounded retention where this audit identified it.
Those later catalogue repairs must be staged independently. They cannot grow the supervisor/pool production change or become another universal utility module.
Actor-template composition audit (engineering record)
Historical evidence snapshot: this record was last committed at 1f20cc4.
Its verdicts describe that revision; the current repository quality audit
tracks later verification and unresolved work.
The historical DeliveryRouteFor<Owner> proposals below were removed during
the A13 review; current callers constrain DeliveryRoute's associated
protocol address directly.
Authoritative redesign ledger
This section supersedes every historical "fixed" or provisional design claim later in this file. Those sections are retained only until the corresponding code and tests have been deleted; they are not completion evidence.
Laws
BehaviorLayerconstructs one concrete same-mailbox transformation. A higher-order behavior may apply the layer and may delegate the resulting behavior, but it may not inspect, remove, or restore a field inside another behavior's event or send product.- Fixed topology, restart eligibility, restart budget, optional restart
delay, timer generations, replacement retention, and shutdown cancellation
are one state-transition law owned by
FixedFleetOwnership. Delayed restart is configuration of that law, not a wrapper around arbitrary send shapes. - A private parent/child communication is an explicit
Actionseffect.ChildInputtargets one declared child occurrence;ReportToParenttravels one established parent edge and the interpreter attaches the exact local child nonce. Neither operation fabricates a logical recipient. - A report crossing two parent edges is relayed by one stateless behavior transformation. The relay owns exactly one input-to-effect law and neither interprets nor deduplicates the report.
- A pool worker receives only assignment data and reports only a completion. It does not name the pool's public protocol, customer reply route, pool behavior, stable-proxy behavior, or a logical recipient at the pool address.
- Logical hosting requirements are derived from real route selections and real composed templates. An application-authored dummy product is not evidence that a catalogue template is hostable.
- User-level composition tests observe complete interpreted traces. They may
not manually transfer an effect into the next fold or assert structural
.owned/.innerpaths as proof of composability.
Deletion ledger
- Delete
SupervisionOwnership,BackoffSupervision,BackoffSends, and the application/standalone duplicate backoff surfaces. - Delete
PoolAssignmentProtocoland removeWorkerPoolProtocol/KeyedWorkerPoolProtocolfrom worker assignment and completion types. - Delete Bombay's template-specific
ParentReportingmethods after the one generic parent-report interpreter is installed. - Delete metadata-only logical-host tests and every documentation claim they were used to justify.
- Delete public aliases and
With...names that only expose a concrete generic nesting and own no law.
Initial containment
exact blocker: delayed restart and pool completion currently depend on concrete
supervision/pool product layouts
smallest regressions: one standalone delayed restart trace; one worker that
completes without naming a pool protocol
expected production files: Behavior Core effects/creation; shared supervision
ownership/protocol; proxy relay; FIFO/keyed pool;
Bombay report/action interpretation
expected production delta: net-negative after specialized paths are removed
new public semantic types: at most one restart-configuration sum, one generic
relayed report, and one pool completion value
Stored-layer removal checkpoint
Exact blocker: PoolWorkerLayer<L, R> stores an existing layer only so
FixedFleetOwnership can apply it while staging initial stable children. It
owns no state, event, effect transformation, or policy. The ownership fold
does not otherwise use the layer: worker replacement is an explicit
ChildInput<ReplacementRequested<C>> to the already-established stable child.
Invariant: fixed-fleet ownership owns lifecycle state and names the stable
child type in its effects, while the containing behavior owns construction and
supplies a borrowed BehaviorLayer only when initialization stages those
children. Pool initialization may compose its user layer with the lawful
RelayChildReports transformation in a local inferred closure; no stored
adapter type is required.
smallest regression: FIFO and keyed pools stage RelayChildReports<L::Output,
C, PoolCompletion<R>> without PoolWorkerLayer
expected production files: ownership.rs, fixed_supervisor.rs,
adapter/supervisor.rs, pool.rs
expected production delta: net-negative
public API: +0 types / -0 types (PoolWorkerLayer is private and deleted)
reused laws: BehaviorLayer, RelayChildReports, FixedFleetOwnership,
ChildInput<ReplacementRequested<C>>
Scope and decision rule
This audit covers the complete bombay-behavior-actors catalogue as it exists,
not one commit or one pair of reported gaps. Git history is used only to identify
when a public name or implementation shape appeared.
A reusable actor template is retained only when its own fold implements a distinct state-transition law or a distinct typed event/effect transformation. Sharing data structures or similar branches is not evidence that two actors are one template. Conversely, a constructor, alias, policy marker, or wrapper is not a template merely because it gives a long concrete composition a shorter name.
The governing semantic classification is Bombay policy. The actor-model laws remain serialized turns, finite acquaintance, fresh creation, and explicit communications/creations/next behavior. This audit does not change those laws. It changes how the library packages derived constructions around them.
Pre-edit change ledger
Exact blocker: the existing H37 audit treats “uses one shared implementation”
as the success criterion. That criterion caused public policy markers, aliases,
builders, and wrappers to be counted as actor templates even when they only
select or nest existing folds. The smallest end-to-end regressions are the
existing pure-fold constructions that already exercise StopOnShutdown,
FinalizeOnShutdown, fixed supervision, delayed supervision, both pool laws,
both shutdown-coordinator products, and Configuration<FeatureSet<_>> without
requiring a second semantic template. Those constructions will remain and the
bespoke names will be removed around them.
Expected surface:
- 26 files directly name the redundant public surface; separating the pool, watch/monitor, and shutdown folds and updating exhaustive callers is expected to take the cumulative change above 15 files.
- Production delta is expected to be net-negative, primarily from deleting the child-shutdown planner, policy-parameterized fixed-supervisor forms, recipe forwarders, and name-only lifecycle/feature specializations.
- No new public type is expected. At least 20 public types/aliases/traits and seven forwarding functions are expected to be removed.
- The retained folds are
StopOnShutdown,FinalizeOnShutdown,Watch,TerminationMonitor,Supervisor,BackoffSupervisor,WorkerPool,KeyedWorkerPool,ShutdownCoordinator, andHeterogeneousShutdownCoordinator. Their existing typed products and interpreter requests are reused.
Universal layer composition redesign: stage 1 pre-edit ledger
The completed audit above proved individual template laws, but it did not
provide one static construction contract by which a generic consumer can apply
an arbitrary concrete behavior transformation and name its associated output.
Callers can manually nest Stash<B>, Watch<B>, timing, shutdown, supervision,
and other transformations, but frameworks must reproduce each constructor and
spell or independently project the resulting concrete type. The same omission
encourages higher-order templates to integrate lower-order policy instead of
accepting an already-composed behavior.
The exact stage-1 blocker is therefore catalogue-wide, not a priority-queue or
proxy special case. The smallest compile regression is a generic function that
accepts any B: Behavior and any static layer L, applies L once, and returns
the layer's associated concrete behavior without naming that output. Black-box
witnesses must use unrelated real catalogue combinations, including routing
inside supervision, persistence under timing/lifecycle transformations, and
different wrapper orders. Tests must assert initialization, event ingress,
all send and creation lanes, errors, phases, and next-behavior decisions—not
just successful construction.
This is a deliberate Bombay construction law, not an actor-model guarantee:
- a layer is a statically dispatched value-to-value construction from one
concrete
Behaviorto another concreteBehavior; - the associated output retains its complete protocol, event, sends, birth,
initialization, error, phase, and next-behavior algebra through ordinary
Behaviorassociated types; - a layer performs no actor effect; only the resulting behavior's pure fold
returns
Actions; - inline layers retain the current actor namespace, while separately running behaviors still connect through transferable logical or established capabilities; and
ChildRoute<C, O>remains usable only by the topology owner whose direct birth algebra proves that occurrence.
Expected stage-1 surface:
- at most eight files: this ledger, the minimal core construction contract and export, one generic-consumer suite, and directly affected documentation or compile tests;
- production: at most
+80 / -0 / net +80; this stage establishes the missing generic contract, while later template deletion must make the cumulative redesign production-negative; - tests: compile and pure-fold witnesses across at least three unrelated catalogue families and more than one layer order;
- public API: one construction trait, no wrapper, builder, marker, effect product, dynamic dispatch, or new effect algebra.
The implementation must reuse the existing concrete wrapper types,
EventLayer, SendLayer, Actions, Behavior::Birth, and their structural
ingress proofs. A closure or concrete constructor may implement the common
contract, so catalogue templates do not require a parallel family of *Layer
configuration wrappers merely for inference. Before the later audit crosses
15 cumulative files, 500 net new production lines, or three new public types,
its measured ledger must be reported at the mandatory checkpoint.
The higher-order state map driving the later deletion audit is:
| Owner | Irreducible correlation retained | Lower-order law currently duplicated or coupled |
|---|---|---|
Proxy | one fresh worker-incarnation installation/replacement relationship | customer return route is fixed to logical delivery |
Supervisor | fixed topology, restart provenance, and worker/proxy fact correlation | fleet membership and terminal drain overlap registry/shutdown policies |
DynamicSupervisor | two-producer initial-install join and replacement correlation | mutable membership, customer correlation, and drain are integrated |
| delayed supervisors | timer generation to accepted replacement-batch correlation | delay progression is already a lower Backoff law |
WorkerPool | assignment identity joined with worker return/stop facts | FIFO backlog, availability routing, fixed supervision, and drain are integrated |
KeyedWorkerPool | persistent key-to-slot assignment and rebalance correlation | inherits the same FIFO, routing, supervision, and drain machinery |
| shutdown coordinators | active phase to exact outstanding child-stop facts | plan declaration/building is separate from phase execution |
This table is a falsifiable work list, not a claim that every apparent overlap can be split without changing asynchronous semantics. Each deletion requires a real composition test that preserves ordering, ownership, and every effect lane first.
Universal layer stage-1 checkpoint
The generic construction regression failed on the baseline because no layer
contract or associated output existed. BehaviorLayer<B> now provides that
static associated output, closures implement it without allocation or erasure,
and Behavior::layer supports inferred chaining. Real routing/supervision and
persistence/stash/shutdown compositions pass in debug and optimized builds
while retaining complete creation, observation, domain-send, and stop lanes.
production: +111 / -2 / net +109
tests: +90 / -0 / net +90
docs: +74 / -0 / net +74
public API: +1 types / -0 types
The production delta exceeds the pre-edit estimate by 29 lines because the single trait's rustdoc states the complete semantic boundary and includes a compile-checked generic-consumer example. It remains below every mandatory stop threshold. This is new generic capability code, not consolidation or code reduction; deletion work is a later independently measured stage.
Owner-scoped delivery checkpoint
DeliveryRouteFor<Owner> is the second static composition law. It selects the
existing logical, established, or child delivery product without erasure.
Recipient and EstablishedRecipient require the same address namespace as
the owner; ChildRoute additionally requires
Owner: ResolveChildOccurrence<Occurrence, Child = C>. A compile-fail test
proves that NoBirths cannot claim a foreign child route. Proxy now uses this
contract for its real worker-forwarding leg, so the trait is exercised by a
production topology owner rather than existing only as test syntax.
The cumulative stage ledger is:
production: +245 / -8 / net +237
tests: +180 / -0 / net +180
docs: +96 / -0 / net +96
public API: +2 types / -0 types
changed files: 9
Focused layer and route regressions pass in debug and optimized builds; Behavior and Actors rustdoc/compile-fail tests pass; the workspace all-targets check is warning-clean. This remains capability work, not code reduction.
The next conversion applies the owner-scoped route contract to all caller-supplied delivery destinations (buffer, priority, ordering, sequencing, deduplication, rate limiting, and their shared named product) and updates their independent models. Together with affected public compile matrices this will cross the 15-file mandatory checkpoint. Production editing for that expansion must not start until the expanded surface is explicitly authorized.
Catalogue-wide composition axes
Queue and proxy are witnesses, not privileged composition categories. The catalogue has two orthogonal composition axes, and every retained template must be classified on both:
| Axis | Static contract | Applicability |
|---|---|---|
| same actor, inline | BehaviorLayer<B, Output = ...> | every transformation that consumes one B and returns another concrete Behavior |
| separate actor, transferable | DeliveryRoute | logical and established recipients passed between actors; the route projects its protocol and send product |
| topology-owner local | DeliveryRouteFor<Owner> | the same transferable routes plus a direct ChildRoute proven by Owner::Birth |
| runtime hosting projection | not currently provided | owner-authored repetition was rejected because it cannot prove transitive completeness |
The direct-child case deliberately does not make ChildRoute transferable. A
separate queue, router, workflow, or cache actor cannot send through another
actor's child namespace. It connects to that topology through a logical or
established proxy capability; the proxy then emits the proven child delivery.
This is the same architecture for every catalogue family, not a proxy-specific
exception.
The current endpoint audit partitions the full catalogue as follows:
- transparent transformations (
Stash, shutdown, timing, watching, termination propagation, supervision adapters, and shutdown coordination) must preserve every inner event, send, birth, phase, error, initialization, and next-behavior lane while adding only their owned law; - ordinary request/reply cores already parameterize customer return routes
through
DeliveryRoute, including persistence, workflow, operational, correlation, acknowledgement, presence, and admission-result actors; - payload-forwarding cores (
Buffer,PriorityQueue,OrderGate,Sequencer,Deduplicator, andRateLimiter) use the same transferable route projection for their destination leg rather than six adapters; - membership cores (
Router,WorkQueue,Topic, andPubSub) retain the concrete route capability in their ordered state, preserving equality and removal evidence without assuming logical identity.RegistryandResolverintentionally model logical-name bindings and therefore remain logical rather than pretending to be universal endpoint stores; - lifecycle owners already use dedicated exact or direct-child capabilities; their remaining gap is truthful availability evidence at the network boundary, not another routing implementation; and
- pure state cores with no destination leg (
Machine, lease state, barriers, latches, and similar folds) compose through layers and typed protocol hops; they need no invented route parameter.
This classification prevents two opposite errors: forcing every template into one recipient representation, and leaving six or more bespoke destination implementations merely because their state laws differ. Route construction is shared; the queueing, ordering, membership, lifecycle, and workflow folds stay separate exactly when they own different transitions.
The expanded black-box layer suite now covers routing, persistence, stashing, both shutdown transformations, absolute and relative timing, periodic and activity-driven timing, logical observation, terminal monitoring, and terminal propagation. It asserts initialization and transition lanes, including a finalization fold whose delivery must survive the outer stop decision. The current cumulative ledger, including untracked tests, is:
production: +193 / -9 / net +184
tests: +294 / -0 / net +294
docs: +238 / -0 / net +238
public API: +2 types / -0 types
changed files: 9
This remains below the current thresholds. The next production conversion is catalogue-wide by design and will exceed 15 files once independent models, compile checks, and documentation are included; it remains paused at the required explicit expansion checkpoint.
Universal route projection: expanded pre-edit ledger
The route audit found a more fundamental duplication than the six destination
fields. The former DeliveryRoute<P> and DeliveryRouteProtocol expressed one
law twice: the former repeated a protocol parameter that the latter already
projected as an associated type. Thirty production files mentioned one of
these contracts, and 26 repeated DeliveryRoute<P> bounds. Adding another route
parameter only to the six forwarding actors would preserve that catalogue-wide
duplication and make their concrete types longer.
The next stage therefore converges on one transferable route contract with an
associated Protocol and Sends, keeps DeliveryRouteFor<Owner> as only the
additional direct-child ownership proof, and removes
DeliveryRouteProtocol. Existing reply-route users migrate mechanically to
the single projection. Payload-forwarding and truthful membership actors then
use the same route law for their destination/member leg. No *Route wrapper,
layer configuration type, adapter actor, effect algebra, registry, or dynamic
dispatch is added.
Expanded expected surface:
- 30 existing production files for the contract and every current route-bound catalogue actor, plus the six destination folds and any membership fold whose independent identity test proves logical-only storage is not its law;
- approximately 10–15 existing model, property, compile, and black-box test files, with both logical and established traces and negative direct-child ownership cases;
- production target
+250 / -300 / net -50or smaller; the stage must stop independently if net-new production approaches 500 lines; - public API
+0 types / -1 typesby deletingDeliveryRouteProtocol; no new wrapper, alias, builder, marker, or effect product; and - existing
Delivery,EstablishedDelivery,ChildDelivery,SendEffects,DeliveryOutcomes, and named actor products are reused rather than reimplemented.
Because this intentionally crosses the 15-file threshold, no production edit for this stage is permitted without explicit expanded-surface authorization.
Universal route projection: implementation result
The authorized conversion now has one transferable DeliveryRoute contract
with associated Protocol and Sends. Logical and established capabilities
implement it directly. DeliveryRouteFor<Owner> adds only the separate proof
needed for an owner-local ChildRoute; it does not make that route
transferable. Proxy exercises the owner-local law for its real worker child.
Every payload-forwarding and membership core listed above now stores or accepts the concrete route capability and accumulates its concrete send product. A black-box matrix runs all ten actors with established endpoints and checks the actual endpoint, payload, outcome, creation lane, and continuation decision. The logical model/property suites remain unchanged in vocabulary and therefore continue to test the state law independently of the new route implementation.
The associated protocol also made separate Reply/D parameters redundant.
They were removed from acknowledgement, circuit-breaker, configuration,
correlation, health, lease, presence, readiness, registry, resolver, task,
workflow, message-adapter, FIFO-pool, and keyed-pool signatures. Four private
phantom aliases were inlined. This is deletion rather than another wrapper or
compatibility layer.
The higher-order audit does not justify replacing supervisor or pool
correlation with a queue implementation. Proxy owns the unique join between
fresh incarnation creation, stop, replacement, shutdown, and typed command
return. Fixed and dynamic supervisors own provenance-sensitive proxy/worker
fact joins. Pools additionally join assignment identity, completion, returned
commands, worker stops, replacement availability, and terminal drain. A
separate queue or router can be their child, peer, or supervised payload through
ordinary layers and routes, but substituting its fold would split those atomic
joins across mailboxes and change the law. The earlier selector-only supervised
wrappers were deleted; the retained higher-order actors contain only these
correlations plus explicit effect products.
Current cumulative checkpoint, including untracked tests:
production: +1160 / -1058 / net +102
tests: +833 / -195 / net +638
docs: +290 / -12 / net +278
public API: +2 types / -1 type
Complete catalogue classification
“Core” means a standalone actor fold. “Transformation” means a wrapper that
owns a distinct typed event/effect law. “Composition” means the public use case
must be written by nesting or connecting retained concrete actors. A truthful
structural alias such as a direct Here form may remain when it names the same
template rather than advertising another actor law.
| Catalogue surface | Owned transition law | Verdict |
|---|---|---|
Machine | finite-state receive/become transition and staged drain atomicity | retain core |
MessageAdapterWithRoute | one pure protocol map followed by one typed delivery | retain core |
Stash | bounded hold/replay order over an inner fold | retain transformation |
StopOnShutdown | direct shutdown-to-stop event transformation | retain transformation |
FinalizeOnShutdown | shutdown reaction returning complete inner Actions | retain transformation |
Guardian, CoordinatedGuardian | no state beyond the two shutdown transformations above | delete; compose the retained transformations directly |
Watch | recurring logical-name observation across later incarnations | retain as its own concrete transformation |
TerminationMonitor, exact monitor form | one correlated observation lifecycle with terminal consumption | retain one monitor implementation with typed target form |
EstablishedWatch and watch target policy aliases | restrict or parameterize the monitor reaction without another lifecycle law | delete; use the exact monitor and an ordinary reaction |
PropagateTermination | one correlated source fact and explicit propagation disposition | retain transformation |
Task | pending-to-completed/failed one-result terminal lifecycle | retain core |
ShutdownCoordinator | ordered homogeneous phase shutdown | retain transformation |
HeterogeneousShutdownCoordinator | ordered phase shutdown over a closed heterogeneous effect product | retain separately; its typed selection/effect transition is distinct |
ChildShutdownPlan, its typestate builder, and shutdown_after_children | joins committed direct-child creations, rejects mismatched facts, and reports one typed heterogeneous plan | retain transformation; generic consumers use associated-output operation traits instead of copying its hidden proof vocabulary |
Proxy | stable slot and fresh-incarnation replacement lifecycle | retain core |
Supervise | adopt application creations into fixed proxy ownership while preserving inner actions | retain transformation |
Supervisor | standalone fixed proxy-fleet ownership | retain core, with a concrete implementation rather than a command-policy engine |
SupervisedWorkers and selector policy types | parameterize fixed ownership with one routing selector | delete; route commands with an ordinary typed routing actor/application behavior |
delayed Supervise policy | delays accepted replacement effects from a supervised application | retain as an explicit timing policy of the same transformation |
delayed Supervisor policy | delays accepted replacement effects from standalone fixed ownership | retain as an explicit timing policy of the same core |
FixedBackoff, BackoffWorkers | implementation/policy parameterizations of the two retained delayed folds | delete |
DynamicSupervisor | changing stable-child membership and replacement lifecycle | retain core |
WorkerPool | bounded FIFO admission, assignment, completion, and interruption | retain core and its direct Behavior fold |
KeyedWorkerPool | the FIFO law plus persistent key-to-slot affinity and rebalance transitions | retain separately and restore its direct Behavior fold |
Deadline | absolute one-shot schedule and single matching-generation reaction | retain transformation |
OneShot | relative one-shot schedule and single matching-generation reaction | retain transformation |
Periodic | matching-generation reaction followed by explicit rearm | retain transformation |
ReceiveTimeout | activity-driven generation reset and one notification per idle period | retain transformation |
Lease | exclusive ownership, generation-safe expiry, release, and rejection | retain core |
Router with its strategies | membership plus strategy-specific selection/evidence transitions | retain core; strategies are values of this law, not actors |
WorkQueue | bounded FIFO work plus worker-availability transitions | retain core |
Buffer | bounded FIFO buffering and overflow ownership policy | retain core |
PriorityQueue | stable immutable-priority admission/release | retain core |
OrderGate | monotonic keyed release | retain core |
Sequencer | sequence-gap buffering and ordered release | retain core |
Deduplicator | bounded first-seen admission | retain core |
RateLimiter | explicit token consumption/refill | retain core |
CircuitBreaker | closed/open/probing single-flight admission | retain core |
Correlator | keyed request/result ownership | retain core |
Acknowledgements | multi-participant acknowledgement lifecycle | retain core |
Registry | mutable typed binding ownership and lookup | retain core |
Resolver | immutable definition and read-only resolution capability | retain core; mutation is unrepresentable in its protocol |
Topic | one ordered subscription set and snapshot publication | retain core |
PubSub | keyed topic introduction, known-empty retention, and per-topic membership | retain core; these keyed states are not actor nesting hidden in a wrapper |
Presence | versioned presence evidence and generation-safe expiry | retain core |
Configuration | versioned atomic configuration acceptance/query | retain core |
FeatureSet | a domain product invariant, not an actor | retain product |
Features, FeaturesState | name-only aliases of Configuration<FeatureSet<_>> | delete; write that concrete composition |
Health | versioned component evidence and aggregate health | retain core |
Readiness | fixed dependency evidence and aggregate readiness | retain core |
Cache | bounded deterministic LRU ownership | retain core |
Latch | one-generation countdown and single release | retain core |
Barrier | cyclic fixed-membership generations | retain core |
Workflow | dependency activation and terminal run lifecycle | retain core |
| composition recipe functions | forward to constructors while inferring structural parameters | delete; construct and nest the concrete types directly |
Composition law for the removals
The replacements must preserve complete Actions; they may not intercept,
drop, duplicate, reorder, or reinterpret an effect lane. Actor-to-actor
composition uses ordinary typed recipients and sends. Wrapper composition uses
the retained concrete event layers and named send products. A topology owner,
not a generic planner wrapper, remains responsible for correlating its own
creation results and reporting any creation-dependent shutdown plan. These are
derived Bombay constructions and policy choices, not additional Agha laws.
Implementation checkpoint
The cumulative catalogue rewrite remains production-negative even though the retained pool and shutdown actors now expose their own folds directly. These figures include all tracked and untracked files; documentation is reported separately:
production: +1204 / -2117 / net -913
tests: +1366 / -581 / net +785
docs: +331 / -175 / net +156
public API: +5 types / -19 types
Seven forwarding functions are also removed. The three apparent public type
additions in the textual diff replace existing aliases with concrete structs
of the same name (Watch and Supervisor); they add no public type name.
Delayed restart is one policy of Supervisor, not another public wrapper. The five additions
were ProxyUnavailable, CommandSupervisionEvent, DeclareShutdownPhase,
FinishShutdownPhases, and LogicalHostRequirements; the last was subsequently
deleted after the end-to-end audit proved it was assertion-only metadata. Eight constructors or
methods were also removed, for 34 removed public names in total. No public
wrapper or policy-marker type was added.
The later semantic regressions retain this classification while adding no
actor wrapper. Dynamic supervision now joins its two initial creation facts in
either order. Proxy commands report complete unavailability through their
established parent relationship, and pools join that report with
worker-stop/replacement facts.
DeclareShutdownPhase and FinishShutdownPhases expose the retained builder to
generic consumers through associated outputs. The former logical-host trait did
not derive anything from the composed behavior and is not retained as evidence.
Composition-hierarchy tightening
The executable Router → Supervisor-owned Proxy → PriorityQueue → Target
hierarchy exposed one remaining false coupling. Router required every
destination message to implement Clone because its policy family also
contained Broadcast. Consequently a round-robin router could not carry
ProxyCommand: its Replace alternative truthfully owns a behavior and is not
cloneable.
This was not a proxy or priority-queue defect. The router combined two distinct
laws. Bombay now defines Router as single-recipient ownership transfer;
RoundRobin, LeastLoaded, ConsistentHash, and RendezvousHash each return
at most one membership index. Topic and PubSub retain snapshot fan-out and
its explicit payload-cloning bound. The Broadcast policy and the router's
multi-index/clone path are deleted. No replacement type or compatibility layer
was added.
The black-box regression constructs the real supervisor-staged proxy and the real proxy-staged priority queue, commits the queue creation, then passes offer and release commands through router delivery, proxy child delivery, and queue target/outcome delivery. It asserts every creation, observation, rejection, delivery, and become lane at each boundary. The public composition guide now shows both same-mailbox layers and independent actor topology, including this executable hierarchy.
This tightening stage changes no foundational effect algebra and adds no public type:
production: +48 / -80 / net -32
tests: +252 / -11 / net +241
docs: +164 / -5 / net +159
public API: +0 types / -1 type
Alias-free application birth composition: pre-edit ledger
The downstream Bombay audit found one remaining static-composition blocker.
Bombay currently requires application-provisioned actors to occupy slots in the
domain root's own Behavior::Birth algebra. That makes an item-level root
declaration name every fully composed child type before value inference can
apply, producing mechanical aliases such as
ManagedHealth = StopOnShutdown<Health<...>>.
The actor-model law remains fresh creation. Combining two already-declared
creation capabilities is a derived Bombay construction: it must preserve every
creator-local nonce, CreationKind, child value, creation-vector position, and
pre-existing child occurrence. Application policy—what is provisioned, nonce
selection, and initialization order—remains downstream in Bombay.
The smallest end-to-end regression is an inferred application value that adds two differently composed children to a root which already stages its own child. The root creation must remain first, application creations must follow in declaration order, and the root's existing nominal child route must still resolve at its original position. No alias may name either application child or the resulting application type.
Expected stage surface:
changed files: 6-8
production: approximately +150 / -10
tests: approximately +200 / -0
docs: approximately +100 / -0
public API: +1 trait / -0 types
The implementation must reuse Actions, CreateChild, BirthMode, ChildChoice,
ChildProduct, and BirthNodeAt. It must not add a behavior wrapper, layer
marker, alias generator, erased child value, runtime registry, or arbitrary
creation callback capable of rewriting provenance.
The initial black-box regression failed before the production edit because
BirthNodeAppend did not exist. With the static append law present, the same
test proves an existing root child role across a lifecycle wrapper and the
application composition, while two application child expressions and the
complete application type remain inferred. Deterministic tests cover empty
left/right identity, associativity, repeated child types at every occurrence,
and exact vector order. A 256-case property test preserves every generated
nonce, birth/replacement provenance value, lane, and position.
Final stage ledger:
changed files: 7
production: +170 / -22 / net +148
tests: +402 / -0 / net +402
docs: +171 / -0 / net +171
public API: +1 trait / -0 types
No behavior wrapper, builder, marker, alias, registry, or erased value was added. The one public trait is the closed child-algebra operation needed by a generic composition owner. The private non-empty-node proof only closes its recursive implementation and adds no public name.
Verification
cargo check --workspace --all-targets: pass without Rust warnings.- Focused birth-composition regressions: 5 passed in debug and optimized builds; the property test ran 256 cases in each build.
cargo nextest run --workspace: 527 passed, none skipped.- Actor rustdoc and compile-fail tests: 29 passed.
- The historical
supervision_sequences,pool_sequences, andcatalogue_sequencestargets completed 5,000 fuzz executions each under the locked Fenix input's nightly sanitizer toolchain. Replacement atomic fuzz coverage is catalogued inatomic-actor-verification.md. nix flake check: passes all seven checks, including build, optimized nextest, docs, Rust/TOML formatting, dependency audit, and dependency policy.
Layer-owned parent reporting: pre-edit ledger
Rejected experiment. The implementation following this ledger added a mandatory
on_unavailablecallback toSupervise::new. Forty-three test and fuzz call sites could satisfy it only with|_, _| Ok(Actions::cont()). That is caller migration driven by the new signature, not evidence of an application-owned transition law. The experiment is quarantined and must be removed; none of its passing compilations count as verification.
The remaining blocker is not construction inference: BehaviorLayer already
constructs every concrete output without naming it. The blocker is that the
first parent-report redesign made an application event implement
EventIngress merely so Supervise could hand an unavailable command back to
the application. That exposes interpreter/template routing as application
composition work and creates a second user-facing mechanism beside
BehaviorLayer.
The smallest regression composes an application with Supervise and an outer
shutdown layer, without naming the output, an ingress path, or a composed event
type. A proxy-unavailability fact must invoke one explicit application policy,
preserve the complete command, and return the policy's complete Actions for
ordinary supervision wrapping. The application event algebra must contain
only its domain events and must not implement an interpreter-routing trait.
This is a derived Bombay policy: a fixed supervision layer owns proxy reports; the application supplies the pure transition selected for expected command unavailability. Source-indexed event construction remains an interpreter/template proof used to deliver child facts to the owning layer. It does not become an application protocol, another actor effect, or an alternate behavior composition API.
Expected stage surface:
changed files: the supervisor fold, focused layer regression, and direct callers
production: approximately +20 / -10
tests: migration-heavy but no new protocol or ingress fixtures
public API: +0 types / -0 types
The stage reuses BehaviorLayer, SupervisionEvent, ProxyUnavailable,
BehaviorActed, and the existing supervisor action wrapping. It adds no
wrapper, builder, marker, alias, event variant, effect lane, registry, or erased
callback. The unavailability policy is a monomorphized function pointer over
the already-concrete application and command types.
Compiler-origin provenance audit
The rejected callback exposed a broader process failure. The current working tree is therefore audited by design provenance before any further compilation or caller migration. Compiler success is not evidence for a public algebra.
The quarantined cluster is:
pathless proxy report
-> source-indexed EventIngress construction
-> SupervisionEvent command-unavailability lane
-> mandatory application callback
-> 43 no-op test/fuzz policies
Only the final two links are already disproven. EventIngress and pathless
parent reports are not accepted or rejected by association; each must still
prove a general interpreter/event-composition law through ordinary
BehaviorLayer construction, two unrelated templates, two wrapper orders,
and a complete end-to-end interpreter trace. If that proof needs an
application event variant, explicit wrapper-depth path, supervision-specific
handler, or ignored transition, the whole route is removed.
RequestProxyWorker is audited separately. Its candidate law is exact
delivery of an owner-created control input to a child occurrence while keeping
the child's public domain protocol unchanged. It remains only if a pre-edit
regression proves that law without logical hosting, a second proxy protocol,
runtime lookup, or application aliases. Downstream compiler demand is not
provenance.
No production edit follows from this audit section. The next design stage must
first write the desired alias-free BehaviorLayer syntax and complete
availability trace as a failing regression. Expected correction is
production-negative because the mandatory callback field, constructor
argument, transition branch, generic event parameters used only by it, and all
placeholder policies are deletion candidates. No new public type is authorized
by this audit.
General typed-reaction layer: pre-edit ledger
Rejected as the availability boundary. The focused composition worked, but the first catalogue check showed that it would force every lifecycle-only
Supervise<B, C>to add a domain-recovery event even when the composition exposes no domain route. Migrating those callers would reproduce the mandatory-callback defect as a mandatory wrapper. NoReactproduction type or migration is retained. The evidence instead locates the missing sum at the proxy boundary: lifecycle ownership and public domain forwarding are currently fused and must become separately composable capabilities.
The user-level syntax under test is:
let behavior = application
.layer(|inner| React::new(inner, retain_unavailable))
.layer(|inner| Supervise::new(inner, topology, restart).unwrap())
.layer(StopOnShutdown::new);
The resulting type is inferred. React is not a supervision policy: it owns
the general same-mailbox law “one additional typed input invokes one pure,
infallible reaction and preserves its complete Actions; every inner event is
delegated unchanged.” Its concrete event sum selects that owned input while
retaining the complete inner event algebra. The reaction is infallible because
it receives mutable access to the inner behavior; a fallible reaction could
mutate and then reject the same event, violating transition atomicity.
For supervised availability, the source is a proxy child report and the input
is the complete ProxyUnavailable. Supervise owns only lifecycle
correlation: it forwards that input to the already-composed reaction instead
of storing an application callback. Startup, replacement, restart exhaustion,
and shutdown remain values of the same input law.
Acceptance requires all of the following before catalogue migration:
- a focused test fails on the prior design because
Reactdoes not exist; - the complete outer
StopOnShutdown<Supervise<React<...>>>fold preserves every send, creation, and become lane while recording each unavailable command exactly once; - an unrelated typed input and the reverse relevant wrapper order use the same mechanism without supervision-specific bounds or aliases;
- the mandatory
Supervisecallback, its field and transition branch, and all 43 placeholder policies are deleted; and - at least one existing one-off event-only transformation is deleted or expressed directly by this general law, so the abstraction reduces rather than relocates the catalogue surface.
Expected surface for the design stage:
production files: 4-7
production delta: at most +180 / at least -100
focused tests: 2 files before any catalogue migration
public API: +2 general types / -1 or more one-off types
The later caller migration will exceed 15 files and is separately authorized by the resumed holistic audit. It may add no further semantic surface.
Typed unavailable route: rejected replacement
Rejected. Adding
UnavailableRoutetoSupervisemerely moved the callback obligation into a public generic and then propagated that generic through delayed supervision. It did not remove a repeated behavior law, and one unrelated outer layer would still force caller edits. The focused route regression and all production edits from this experiment were removed. None of its compiler results count as design evidence.
The reaction experiment proved that application recovery cannot be a required
inner transition of every lifecycle composition. The command is instead
returned through an explicit customer capability already modeled by
DeliveryRoute. Supervise stores that concrete route and emits its concrete
send product; it never invokes an application callback and never asks the
runtime to host a fabricated protocol at the supervising actor's own address.
The complete same-mailbox product is structural rather than bespoke:
Event = SupervisionEvent<
EventLayer<ProxyUnavailable<A, C::Msg>, B::Event>
>
Sends = SendLayer<
SupervisorSends<A, C>,
SendLayer<UnavailableRoute::Sends, B::Sends>
>
Lifecycle facts remain owned by SupervisionEvent. One unavailable command is
owned by the existing EventLayer; Supervise converts it to exactly one
delivery through the supplied route. Application events and actions remain
the innermost lane. The application behavior is not mutated, invoked, or
failed by expected unavailability.
Pools and dynamic supervision keep their stronger owner-specific laws. Their
proxy reports already return to the owning parent through the typed report
path; the pool joins the returned assignment with worker-stop/replacement
facts, while dynamic supervision selects the per-child route retained from
Start. Neither needs a second logical host at its own address.
The focused regression must fail on the callback design, then prove all four
unavailable phases through StopOnShutdown<Supervise<...>>. It asserts the
exact destination, complete returned command, every empty lifecycle and
application lane, creations, next decision, and that the application fold was
not invoked. A separate routing hierarchy must exercise unavailability before
the old manually-fed Router → Proxy test can count as evidence.
Expected design-stage surface:
production files: 3-6
production delta: net-negative or at most +80
public API: +0 types; remove the callback and one event generic/variant
The later constructor migration supplies real typed routes. A placeholder route, fabricated self-recipient, ignored delivery, or route used only to make a test compile invalidates the design.
Ownership inversion: current design checkpoint
The repeated failure is caused by choosing Supervise as the unit of design.
Current BehaviorLayer is a static construction and inference contract; it
does not turn a hard-coded higher-order fold into semantic composition.
Supervise still constructs Proxy<C> internally, so proxy forwarding and
availability necessarily leak into supervision, delayed supervision, pools,
tests, and downstream application types.
The behavior laws must instead be separated before another signature is proposed:
| Law owner | State and correlation it may own | What it must not own |
|---|---|---|
| stable incarnation | one pending fresh installation, current incarnation, replacement provenance, and terminal child shutdown | customer recovery, restart selection, backlog, or application events |
| domain availability | exactly one selected law for every admitted command during installing, running, vacant/exhausted, and shutdown phases | worker creation or supervisor restart policy |
| supervision | proxy/worker lifecycle facts, restart eligibility/budget, and terminal fleet drain | the worker's domain message, customer route, queue, or persistence operation |
| delayed supervision | generation-safe delay of an already accepted replacement effect | a second supervisor state machine or availability policy |
| pool | the atomic join of job, assignment, completion/return, worker loss, and terminal interruption | a second implementation of proxy incarnation or restart timing |
The intended construction must name no resulting behavior type and must not thread an availability type through supervision:
let stable_worker = worker
.layer(availability(customer_policy))
.layer(stable_incarnation());
let application = application.layer(supervise(
topology([slot], |_| stable_worker),
restart_policy,
));
The names above are domain-level acceptance syntax, not authorized Rust API. An implementation is acceptable only if its concrete layers each own the law listed in the table, the application and higher-order output types remain inferred, and it deletes the mandatory callback, the unavailable-message generic on supervision events, and the hard-coded construction of a forwarding proxy inside supervision. A type added merely to realize this spelling is not accepted.
Before production work resumes, the audit must select the one availability law for the public stable destination and write its full transition table in customer vocabulary. The same table must drive pure-fold tests for startup, running delivery, replacement, restart exhaustion, shutdown, duplicates, and stale lifecycle facts. Only then can the existing incarnation, routing, buffering, acknowledgement, and event/effect products be evaluated as lower-order composition candidates.
Live claim-versus-implementation failures
The following earlier audit statements are currently false and must not be used as completion evidence:
- The catalogue says the standalone fixed
Supervisorcore is retained, butfixed_supervisor.rsis deleted and noSupervisoris exported. Only the application wrapperSupervise<B, C>remains. Fixed fleet ownership without an application fold is therefore no longer a public feature despite the table claiming otherwise. - The higher-order actors are not assembled from an already-composed stable
child.
FixedFleetOwnership,Supervise,DynamicSupervisor,WorkerPool, andKeyedWorkerPoolall nameProxy<C>in their birth, child-route, observation, shutdown, or replacement effects and construct it withProxy::newinternally. - The documented
Router -> supervised Proxy -> PriorityQueuewitness is a sequence of manually invoked pure folds. Manual transfer can be useful unit evidence, but the test also supplies an ignored unavailable-command callback and exercises only the running phase. It does not prove that the complete outer interpreter stack delivers, returns, or recovers a command during startup, replacement, exhaustion, or shutdown. - The former
LogicalHostRequirementswas documentation-only metadata. No catalogue behavior implemented it, and its tests exercised only a locally authored dummy product. It has been deleted rather than advertised as a complete transitive requirement. BehaviorLayercurrently abstracts only value construction and associated output inference. It does not by itself compose state, events, actions, births, initialization ordering, or independently running actor configurations. Describing hard-coded higher-order actors as layer-composed because their constructors can appear inside a closure is incorrect.
These failures reopen the catalogue verdict. Production work remains frozen until the replacement design shows both actor-configuration composition and same-mailbox transformation separately, preserves the actual fixed-supervisor feature, and replaces the hard-coded stable child in at least fixed supervision, dynamic supervision, and one pool without propagating a new caller-named type or placeholder policy.
Layer-owned availability: pre-edit ledger
Provisional design rejected after the catalogue check. Requiring every
domain message to implement the recovery law makes a framework concern part of
the worker protocol and makes a lifecycle-only Never protocol invent an
impossible capability. The brief implementation was useful evidence, but is
not the general architecture and must not remain as the completion design.
The replacement law belongs to composition:
| Direction | Algebra | Owner |
|---|---|---|
| mailbox input | closed sum of the inner user event and each layer's private lifecycle facts | the concrete composed behavior |
| transition output | named product of inner/user sends and every layer's system sends | the concrete composed behavior |
| construction | reusable BehaviorLayer from a worker behavior to one stable-child behavior | the topology owner receives the layer value; callers do not name its output |
| unavailability | policy owned by the stable-incarnation layer | neither the raw domain message nor the supervisor |
System creation, observation, stop, replacement, and shutdown values remain
framework-owned typed lanes. Application authors do not define or manually
union system commands. A supervisor, dynamic supervisor, or pool must consume
the already-composed stable-child contract instead of naming Proxy<C> or
reimplementing its lanes. User commands need a customer route only when the
selected availability policy is an explicit typed return; buffering or
capability withholding must not be encoded as a fake route.
The first user-syntax regression is the existing complete hierarchy in
behavior-testkit/tests/universal_layers.rs. It now passes the stable-child
construction as |worker| worker.layer(Proxy::new) to fixed supervision and
names no output type. Against the current production surface it fails for two
independent, intended reasons:
Supervise::newaccepts only the application, raw-worker topology, and restart configuration, so the stable-child layer is rejected as an extra argument and proxy construction remains hard-coded.PriorityQueueMessageis rejected for not implementing the provisionalReturnUnavailabletrait, proving that the provisional design leaks stable-incarnation policy into an otherwise complete domain protocol.
These are the two production seams the next stage must remove. Adding an
implementation of ReturnUnavailable to PriorityQueueMessage, deleting the
layer argument, or replacing it with a callback would game the regression.
The actor-model acquaintance law rules out reconstructing a customer from
User::from: an origin address is not proof that the origin accepts the
worker's command or rejection protocol. The actor model also does not select
Bombay's availability policy. A stable-incarnation layer must choose and expose
one truthful policy. An explicit customer-return policy needs a real typed
customer capability; a parent-recovery policy uses the already-established
child/parent relationship and a typed parent ingress; a buffering policy owns
its capacity and typed overflow result. None may invent a logical host from an
address or require an unrelated domain message to implement framework policy.
The concrete pool use proves the narrow owner contract needed now: when its
stable child cannot accept an assignment, the child returns the complete
assignment through the existing typed parent-report path, and the pool handles
that private input in its own event sum. The runtime already retains the
concrete child type and occurrence in LocalParentReports; EventIngress
selects the exact parent event lane without a caller-named wrapper path. The
same structural rule must work when the child is the output of a
BehaviorLayer, rather than being hard-coded as Proxy<C>.
The complete transition table is:
| Proxy phase when command is admitted | Observable result |
|---|---|
Running | exactly one owner-proven child delivery; no unavailable delivery |
Dormant, initial Installing, or install during shutdown | exactly one policy-owned unavailable effect retaining phase and complete command |
AwaitingStop or replacement Installing | exactly one policy-owned unavailable effect; never send new work to the stopping or not-yet-committed worker |
Vacant, including restart exhaustion/rejection | exactly one policy-owned unavailable effect |
ShuttingDown | exactly one policy-owned unavailable effect |
Wrong, stale, duplicate, and contradictory lifecycle facts remain typed proxy errors and cannot consume or duplicate a command. Shutdown and replacement inputs retain their existing state law. Parent recovery keeps one typed report and ingress path; it removes the fabricated logical host and the mandatory domain-message trait. No callback or absolute wrapper-depth parameter is part of the contract.
User-level construction names no composed output type:
let stable = worker.layer(Proxy::new);
Proxy::new denotes the concrete parent-recovery policy demonstrated by pools.
Its mailbox event is the closed sum of the worker's user lane and proxy-owned
lifecycle inputs; its sends are the named product of worker delivery and
proxy-owned lifecycle/report effects. The application author neither defines
those system inputs nor names the composed output type. No command trait,
proxy route generic, callback, policy marker, registry, erased envelope, or
runtime lookup is introduced.
Expected design-stage surface:
production files: 6-10
production delta: net-negative
public API: at most +2 general child-input contracts / remove ReturnUnavailable
focused tests: direct proxy table plus one real pool and one dynamic trace
The general child-input contract, if the existing concrete composition cannot
express the regression without it, must replace RequestProxyWorker; it may
not coexist as a second delivery mechanism. It must target the concrete
layer-produced child and one private input lane, and Bombay must interpret that
same type without constructing ProxyEvent<C> itself. Lifecycle-only fixtures
use an uninhabited domain protocol; they may not invent a command or empty
recovery effect merely to compile.
The compositional gap is the direction of ingress, not replacement itself.
ChildInput transfers one private value from an established parent to its
direct child. ReportToParent transfers one owned value in the opposite
direction and the interpreter attaches the authoritative child nonce. These
are different capabilities and must not share one catch-all ingress trait.
The child-input contract therefore constructs only the concrete child's
private event. A report-owning behavior transformation delegates that contract
to its inner behavior structurally; it never names the delegated input.
EventIngress remains the parent-side selection law for an incoming child
report or same-actor owner input. This is a derived Bombay communication law,
not an actor-model primitive.
Narrow pool completion capability: pre-edit ledger
Exact blocker. A worker assignment currently carries a recipient for the
pool's complete public protocol. The worker must therefore name the recursive
WorkerPoolProtocol or KeyedWorkerPoolProtocol, including the customer reply
route, even though it only needs to report one completion. This is not
essential protocol information for the worker; it is an ownership leak from
the pool actor.
The smallest end-to-end regression constructs a worker whose public message is an assignment containing only job, assignment token, stable slot, and payload. The worker emits one typed completion report. Through the stable-incarnation layer and an outer shutdown layer, the runtime must deliver that report to the pool's private completion event exactly once. The pool must then emit the customer response through its existing reply route. The worker construction must not name the pool protocol, customer reply route, final stable-child type, or a structural parent path.
This is a derived Bombay communication law, not an Agha primitive. The actor
model permits communication only through acquired acquaintances; Bombay's
creator/child relationship is the acquired structural capability used here.
The report remains an ordinary typed send effect in Actions. The interpreter
adds the exact creator-local child nonce and injects the resulting child fact
into the parent's closed event sum. It performs no lookup and may not silently
discard a closed-parent failure.
The proposed reusable laws are:
| Law | Input | Output | State |
|---|---|---|---|
| structural parent report | one owned report value | exactly one parent event containing the exact child nonce and report | none |
| stable-child relay | one matching direct-child report | exactly one structural report to its own parent | none |
| pool completion | one matching stable-slot report | the existing completion transition and response effects | existing pool state only |
The pool must also have exactly one lifecycle authority. FixedFleetOwnership
owns stable installation, current worker incarnation, replacement admission,
restart exhaustion, and terminal drain. A pool slot owns only the work that
the pool accepted: vacant, assigned, returned before delivery, or permanently
retired with the customer-facing reason. Installing and Stopping are
derived observations of ownership and must not be stored again by the pool.
No reconciliation transition is permitted.
| Authoritative ownership observation | Pool-owned work | Public pool phase |
|---|---|---|
| draining | any active work after it has been returned | Stopping |
| current worker incarnation is routable | vacant | Idle |
| current worker incarnation is routable | assigned | Assigned |
| no routable incarnation, slot restartable | vacant or returned | Installing |
| slot permanently retired | retained retirement reason | Retired |
Every accepted lifecycle fact is folded by ownership once. The pool may then move an accepted job or retain a customer-facing retirement reason, but it may not infer lifecycle provenance from its own work state. A rejected, duplicate, stale, or contradictory fact leaves both ownership and work unchanged.
The first law must replace the template-specific proxy report interpreter requests; it may not become a parallel reporting mechanism. The relay is a valid same-mailbox layer only because it owns a distinct event/effect transformation. It must delegate unrelated user, lifecycle, initialization, send, birth, error, and become lanes without positional caller knowledge. Duplicates and stale completion facts are still judged by the pool's existing assignment-token law; the relay neither deduplicates nor reinterprets them.
Desired application syntax:
let workers = ChildTopology::new([7], |_| Some(worker));
let pool = WorkerPool::new(workers, configuration, replies, Proxy::new)?;
The worker's assignment and completion types may name real job and result
types. They may not name WorkerPoolProtocol, KeyedWorkerPoolProtocol, the
customer reply route, Proxy, the pool behavior type, or a wrapper output.
Expected stage surface:
production files: 8-12 in Behavior Actors, plus the Bombay interpreter
production delta: replace specialized reporting; do not retain both paths
public API: at most 2 general report/child-fact types; remove the three
template-specific interpreter-request identities
tests: pure report/relay folds, complete FIFO and keyed pool traces,
outer-wrapper interpreter trace, compile-fail wrong source,
and optimized duplicate replay
Dynamic management protocol identity: pre-edit ledger
Exact blocker. DynamicSupervisor currently implements Protocol as the
complete behavior type. Adding or changing its stable-child BehaviorLayer
therefore forces every unrelated sender to change its delivery type or alias,
even though the management message law is unchanged.
The user-level invariant is that a sender names only the dynamic-management domain: address, worker behavior, and the selected customer reply-route kind. The topology owner separately constructs a value with an inferred stable layer. Adding an unrelated worker layer must change neither sender code nor the public protocol identity.
This is a Bombay nominal-capability correction, not a new actor or transition
law. One zero-state protocol type replaces DynamicSupervisor<..., L> as the
destination identity. It owns no state, fold, policy, builder, or wrapper.
production files: 2-4
production delta: approximately neutral
public API: +1 nominal protocol; remove the Protocol implementation from
the composed behavior type
tests: two stable layers share one sender protocol; wrong worker or
reply outcome remains a compile-time error
Returned-assignment ordering correction: pre-edit ledger
Exact blocker. After the pool emits one assignment, the stable proxy may
later report both that its worker stopped and that the still-mailbox-admitted
assignment could not be forwarded. The current pool joins these facts only
when unavailability arrives first. Worker-stop first moves or resolves the job;
the matching later return is then misclassified as
UnexpectedAssignmentUnavailable and fails the pool actor.
The actor-model law is only sequential processing of each actor's accepted communications. Message paths and delays do not determine a global arrival order; Karmani and Agha explicitly describe actor message arrival as indeterminate. Bombay therefore may not derive a FIFO guarantee between the worker lifecycle observation and the proxy's returned command. The pool join is a derived Bombay construction, while retry-versus-interrupt remains the existing explicit pool policy.
The smallest regression submits one assignment, folds the matching
WorkerStopped first, then folds the exact ProxyUnavailable. It must retain
the one accepted job, emit no duplicate response or assignment, and accept the
return once. Replaying the same return must preserve the complete fact in the
existing typed duplicate/stale error. The complete interpreter regression
must execute the same order through
StopOnShutdown<WorkerPool<...>> -> RelayChildReports<Proxy<...>>, rather than
injecting the pool event directly.
The state law is a product of two independently coexisting facts:
- current slot work is vacant, assigned, returned-before-stop, or retired; and
- zero or more exact assignment/job correlations were already retried or customer-resolved while their proxy-return fact may still be in flight.
The second component is not another lifecycle owner and does not retain a job, payload, route, proxy phase, or worker incarnation. It exists only after the pool has resolved the job side of the join. A matching return consumes exactly one correlation; a wrong nonce, wrong assignment, wrong job, duplicate, or unrelated stale return remains the existing complete typed error. Shutdown and restart exhaustion record the same correlation before resolving the customer, so a later authoritative return cannot become an opaque actor failure.
Expected stage surface:
production files: 1
production delta: approximately +30 / -5
public API: +0 types / -0 types
tests: the independent pool model and complete outer interpreter
hierarchy, in debug and optimized builds
The implementation may add one private correlation product to the existing pool slot. It may not add a behavior wrapper, protocol, route, layer, callback, registry, marker, alias, or second ownership fold.
Transitive logical-delivery projection: pre-edit ledger
Exact blocker. BirthProtocols truthfully projects only the
protocols installed by a behavior's transitive birth algebra. Intentional
logical Delivery<P> lanes remain visible in each concrete sends product, but
there is no structural projection that lets a generic application owner prove
it hosts every such P used by the root and every transitive child. The former
LogicalHostRequirements let owners manually repeat a list and therefore
proved neither completeness nor correspondence with the actual sends tree; it
and its gamed dummy test were deleted.
This is static Bombay owner/interpreter metadata, not a new actor effect and
not an Agha law. The send algebra already distinguishes logical delivery from
established-incarnation delivery, creator-local child delivery, private child
input, and interpreter requests. The projection must follow those existing
concrete distinctions without inspecting values or changing Actions.
The desired compile-time law is:
logical(Vec<Delivery<P>>) = P × ∅
logical(established/child/input/request lanes) = ∅
logical(named product) = append each field projection in interpretation order
logical(Behavior B) = logical(B::Sends)
++ logical(each transitive B::Birth child)
The result reuses the existing BirthProtocol<P, Tail> /
NoBirthProtocols duplicate-preserving product and its structural append and
membership proofs. A repeated protocol in two lanes or two child occurrences
therefore remains repeated. A framework may recursively require Hosts<P>
for each product element; repeated bounds are lawful and do not require
normalizing the product into Bombay's unique runtime host map.
The first regression derives the exact product from an application with one logical root delivery, one exact-only root delivery, and two births of a leaf with the same logical delivery. It contains no manual requirements impl. A second catalogue regression covers ordinary recipient, established recipient, mixed recipient, named send products, wrappers, pools, and transitive worker layers. An unsupported custom sends product must fail at the projection trait, not silently contribute an empty product.
Expected stage surface:
production files: 5-8
production delta: approximately +180 / -20
public API: +2 consumer traits and +1 doc-hidden birth-node fold;
no public data type
tests: replace the deleted manual-metadata test with structural
derivation and add catalogue exact-product assertions
Every existing named sends product will implement the same structural
projection in actors::requirements; this avoids spreading metadata through
template folds. No behavior wrapper, host value, registry, dyn, Any,
TypeId, protocol erasure, macro-only route, callback, or runtime lookup is
authorized.
Transitive logical-delivery projection: core/catalogue checkpoint
The implementation follows the stated structural law. Core send leaves and
SendLayer implement LogicalDeliveryProtocols; LogicalHostRequirements is
blanket-derived from one behavior's sends and every transitive birth node. The
actors catalogue implements the same fold for its handwritten semantic
products. Unsupported custom send products fail at
LogicalDeliveryProtocols rather than silently projecting an empty list.
Evidence is independent of an owner-authored metadata list:
- the structural test derives
P, Q, Qfrom one root logical lane, one exact lane, and two child occurrences of the same logical destination; - a framework-style recursive consumer accepts the duplicate-preserving product without canonicalizing it;
- real worker-pool and dynamic-supervisor birth trees derive only their actual customer reply protocol; and
- the compile-fail contract rejects an opaque custom send product.
No behavior wrapper, runtime host value, registry, erased protocol, callback, or manual requirements implementation was added.
The attempted automatic implementation for #[behavior]-generated named
products is rejected and removed. It made every declared field require
LogicalDeliveryProtocols at actor definition time, so a valid custom effect
lane such as Vec<u8> stopped being a valid SendEffects product even when no
logical-host projection was requested. Hidden generics, marker traits, and
syntax-based macro classification would only move that compiler constraint and
are not authorized.
Generated and custom products use the same owner contract as handwritten
catalogue products: the product owner implements LogicalDeliveryProtocols
when, and only when, a framework asks for complete hosting metadata. This does
not narrow Behavior, SendEffects, or custom interpretation. A generated
two-delivery product projects both destinations in authored order. A separate
custom product containing one delivery lane and one uninterpreted Vec<u8>
marker lane projects only its actual delivery. The existing generated
Vec<u8>-only products continue to compile without any projection impl.
This is not the deleted manual behavior-level requirements list. The contract
is co-located with the concrete sends product whose InterpretSends law gives
those fields meaning, and the blanket behavior projection still traverses all
root and transitive birth products. As with SendEffects and
InterpretSends, a downstream implementation is responsible for satisfying
the documented trait law; an arbitrary custom interpreter's semantics cannot
be discovered by inspecting Rust syntax.
Compiler-driven relapse checkpoint
The automatic #[behavior] projection was not the only compiler-shaped
assumption in this stage. A complete workspace test build exposed two more:
- the hierarchy regression declared
Births<Queue>on an application that never stages a queue creation, then treated the resulting appendedChildChoice<Queue, Proxy<Queue>>as though it were exactly the proxy; and - after the fabricated pool completion recipient was removed, the pool
constructor no longer receives or derives its customer result and
reply-route contract. A later real
Submitvalue still cannot determineRthrough the non-injectiveRoute::Protocol::Msgequality.
The false birth capability is removed from the test. The complete seven-test
layer hierarchy now builds and passes with NoBirths; its sole creation is the
stable child actually staged by supervision. No production type was changed
to accommodate ChildChoice.
The initial pool inference failure was also classified too quickly as a
production blocker. Topology, replacement policy, and the worker layer contain
no customer result schema or reply-route kind. BehaviorLayer can infer the
complete concrete wrapper stack, but no static construction can truthfully
invent public protocol facts absent from both its inputs and its expected
context. The owner must state that domain contract somewhere.
An actual command value alone does not provide reverse inference through
Route::Protocol::Msg; claiming that it did was incorrect. The application
owner instead states the public protocol it hosts:
B::Protocol = WorkerPoolProtocol<MailAddr, Job, Result, ReplyRoute>
That is domain identity, not the concrete composed actor type. From this owner
boundary Rust infers the complete WorkerPool<...>, BehaviorLayer::Output,
proxy, relay, event sum, sends product, and birth tree. The corrected
regression retains the inferred value and executes initialization plus a real
PoolMessage; it is not a discarded compile-only witness. It names no
composed behavior, recursive worker protocol, parent path, or effect-product
alias. A turbofish on WorkerPool, phantom witness, unused recipient, default
generic, or constructor overload remains rejected because each would encode
compiler evidence instead of the protocol an actor owner must actually host.
The semantic boundary follows the actor model's acquaintance rule: an actor can communicate only through a known name, and names may be communicated in messages. Bombay then adds four derived, statically typed communication forms:
| direction | emitted effect | authority/provenance | realized input |
|---|---|---|---|
| external or peer to actor | Delivery / EstablishedDelivery | known logical or exact recipient | public User lane |
| creator to direct child | ChildDelivery | committed creator-local child binding | child's public User lane |
| creator to direct child | ChildInput | committed binding plus owner-selected private lane | private child event |
| direct child to creator | ReportToParent | established creator relationship | ChildReport with interpreter-attached child nonce |
| interpreter back to emitter | InterpreterRequest return | request's declared local continuation | selected event in the same incarnation |
The model justifies ChildInput and ReportToParent/ChildReport as distinct
effect laws: they travel in opposite directions, use different established
relationships, and attach different provenance. ChildInputIngress is the
receiver contract for the former; EventIngress selects owner/report or local
continuation inputs for the latter cases. The two traits may remain because
they correspond to different interpreter capabilities, not because one blanket
implementation overlaps another. ReplacementRequested<C> also remains a
distinct request value: carrying a behavior definition is not evidence that a
replacement was created or installed, and the request must remain distinct
from both those later facts.
production edits authorized by this checkpoint: 0
public types authorized by this checkpoint: 0
next evidence: complete workspace build, then catalogue classification of
remaining public wrappers and hard-coded higher-order laws
Delayed replacement composition: provisional pre-edit ledger
The standalone-supervisor fuzz contract still requires delayed replacement,
but the current worktree exposes that law only through BackoffSupervise,
whose input is the application-specific Supervise behavior. Restoring a
second standalone backoff state machine would duplicate ownership and repeat
the compiler-driven design that this audit is removing.
- Exact blocker: one timer-delay law cannot consume both existing owners,
SupervisorandSupervise; the standalone fuzz target therefore cannot express its previously covered behavior. - Smallest regression: construct
Supervisor::new(..., Proxy::new), apply the same delayed-replacement layer used forSupervise, observe a worker stop, prove no replacement input is emitted before the matching timer, and prove exactly one is emitted after it. - Expected files: the shared backoff implementation and exports, its unit and interpretation tests, the standalone fuzz target, and current documentation (fewer than ten files).
- Expected production delta: net negative, because one generic transformation replaces template-specific backoff products and avoids restoring the deleted standalone implementation.
- Public types: add one owner/consumer contract and one genuine delayed-law
behavior; replace the current backoff send product with its schedule-only
lane; remove the application-specific
BackoffSupervisesurface. - Reused law:
SupervisorandSuperviseremain the sole owners of topology, restart policy, observation, shutdown, and replacement input creation. The delayed layer may only retain and later re-emit those existing replacement inputs; it must not recreate or reinterpret the supervision fold.
Delayed replacement: completed ownership-law checkpoint
The proposed outer delayed layer is rejected. Intercepting an emitted replacement input after the inner ownership fold has advanced would let the owner record a request as issued while the child has not received it. A worker creation fact, shutdown, or another stop arriving during that interval would therefore be folded against a false ownership state. Making the wrapper repair that state would duplicate and reinterpret supervision.
Delay instead participates in the existing replacement-admission sum inside
FixedFleetOwnership. RestartTiming::Immediate opens the replacement gate in
the admitting transition. RestartTiming::Delayed(Backoff) retains the exact
batch and its per-member replacement requests behind one generation-tagged
timer gate. Timer arrival and worker readiness join in either order; the last
required fact emits each replacement exactly once. Shutdown cancels the
retained batch, and stale or duplicate timer facts emit nothing.
This is Bombay policy rather than an actor-model timing guarantee. It adds one
policy sum carrying real state and deletes both former BackoffSupervise and
BackoffSupervisor behavior families, their wrapper events, send products,
errors, parent-path variants, aliases, and duplicated folds. The same private
ownership implementation is exercised by standalone Supervisor,
application-owned Supervise, and both worker-pool variants.
Independent evidence compares the complete schedule and replacement lanes of standalone and application-owned supervision for the same trace. A pool trace additionally proves that an interrupted assignment stays queued until the exact timer releases replacement, that a duplicate timer cannot release it twice, and that installation dispatches the retained job once.
Standalone failure reaction: pre-edit ledger
Exact blocker: standalone Supervisor::with_failure_reaction accepts
Step<Never>. Because that spelling defaults both the phase and terminal
payload to Never, callers cannot construct its documented stop result; the
configuration can only continue. The supervision sequence target exposes the
defect by attempting the advertised terminal reaction.
expected production files: fixed_supervisor.rs
expected production delta: 3 substitutions, net zero
public API: +0 types / -0 types
reused algebra: Become = Step<Never, Stopped>
regression: budget denial returns the complete failure report and Stop(Stopped)
No policy enum, marker, wrapper, alias, or convenience constructor is needed.
The contract now uses the existing Become alias, making both Continue and
Stop(Stopped) constructible. The regression proves that budget denial keeps
the complete typed failure report while the configured standalone owner
selects termination; the supervision sequence target exercises the same path.
Redundant installation-proof facade: pre-edit ledger
Exact blocker: InstallationRequirements, RequirementAt, and four public
aliases merely repeat Behavior Core's already-exported BirthProtocols,
BirthProtocolAt, BirthProtocol, NoBirthProtocols, and structural position
types. They add no state, transition, effect, proof, or inference capability;
the actors facade already re-exports the core vocabulary wholesale.
expected production files: requirements.rs, lib.rs
expected production delta: approximately -75 lines
public API: +0 types / -6 types
tests/docs: spell the same exact products with the existing core proof
The logical-delivery projection implementations remain in requirements.rs;
only the parallel installation vocabulary is removed.
The six-name facade is deleted. All exact-product, repeated-occurrence, wrapper, proxy, pool, and dynamic-supervisor proofs now use the Behavior Core vocabulary directly; the workspace check passes without a compatibility alias.
Nominal public capability consistency: pre-edit ledger
Exact blocker: Cache, Barrier, and Latch each implement their complete
nominal public Protocol for Self, but their Behavior::Protocol is an
equivalent MessageProtocol. A sender can therefore name a
Recipient<Cache<...>>, Recipient<Barrier<...>>, or Recipient<Latch<...>>
that is not the protocol identity the runtime hosts for that behavior. No
protocol-adaptation law justifies the second identity.
This is Bombay's derived stable-capability law, not an actor-model guarantee:
a standalone catalogue actor that authors one nominal public protocol exposes
that exact protocol through Behavior::Protocol; transparent outer layers
preserve it.
smallest regression: each actor and StopOnShutdown<actor> satisfies
Behavior<Protocol = actor> and accepts an ordinary
Recipient<actor> capability
expected production files: cache.rs, barrier.rs, latch.rs
expected production delta: 3 substitutions, net zero
public API: +0 types / -0 types
reused algebra: the existing Protocol impl for Self and transparent wrapper law
The regression must fail on the prior definitions because the protocol identities differ, not because a helper alias is missing. No alias, wrapper, conversion, compatibility implementation, or constructor is authorized.
The pre-fix regression produced six E0271 identity failures: one for each
standalone actor and one for each transparent shutdown composition. The three
behaviors now expose Self; ordinary nominal capabilities and the wrapped
forms compile and pass in debug and optimized builds. No transition branch,
message, effect, error, or constructor changed.
production: +8 / -9 / net -1
tests: +31 / -0 / net +31
public API: +0 types / -0 types
Keyed-pool delegation: pre-edit ledger
Exact blocker: KeyedWorkerPool owns only key-to-stable-slot binding,
first-admission selection, and explicit rebalance, but it stores the private
PoolState and repeats the ordinary pool's complete lifecycle, completion,
returned-assignment, shutdown, and dispatch transition. Sharing the state
helper is not behavior composition; the two public folds can drift while
claiming one pool law.
The keyed actor remains a distinct transformation because its affinity table
is genuine state. It must store and delegate to the existing concrete
WorkerPool, handling only keyed submit and rebalance itself. Common events
must enter the ordinary pool fold exactly once; its complete actions pass
through unchanged and its uninhabited-key error is widened exhaustively by the
existing widen_pool_failure function.
smallest conservation trace: FIFO and one-key pools receive equivalent
initialization, installation, submit, completion,
worker-stop, replacement, shutdown, duplicate,
and stale facts; common action/error snapshots
remain equal
expected production files: pool.rs only
expected production delta: net negative (delete the repeated common match)
public API: +0 types / -0 types
reused laws: WorkerPool Behavior fold, KeyedWorkerPool affinity sum,
widen_pool_failure, identical PoolSends and Births products
This is a deletion/refactor claim, not a new observable actor law. A black-box test cannot distinguish two identical implementations from delegation; source deletion is the evidence for composition, while the independent and exhaustive keyed/FIFO models protect observational conservation. No structural proof marker, constructor overload, wrapper, policy trait, or source-text test is authorized merely to make the old implementation fail a test.
Keyed-pool delegation: completed checkpoint
KeyedWorkerPool now stores the concrete WorkerPool behavior. Keyed submit
and rebalance remain its only authored transitions. Initialization and every
common completion, returned-command, creation, stop, timer, and shutdown event
enter the ordinary pool fold exactly once. The one shared private subfold is
targeted admission: the keyed layer needs its Accepted | Rejected result to
commit a new affinity binding atomically, and that result is not an effect that
may truthfully be recovered by inspecting Actions.
No public declaration, wrapper, alias, constructor, event, or effect product was added. The FIFO and keyed independent models pass in debug and optimized builds; the complete outer-layer interpreter tests pass in both profiles; and the pool sequence fuzz target completed 5,000 runs. Workspace Clippy is clean.
The cumulative working-tree checkpoint after this stage is:
production: +3762 / -3753 / net +9
tests: +4251 / -2300 / net +1951
docs: +1599 / -70 / net +1529
infrastructure: +28 / -2 / net +26
Pool effect product: pre-edit ledger
Exact blocker: the pool owns three semantic effect lanes—customer responses,
worker assignments, and fleet supervision—but its complete effect product is a
private SendLayer<SupervisorSends, PoolBehaviorSends>. Direct users and
interpreters must consequently know that pool effects are .inner and
supervision effects are .owned; another transparent wrapper adds another
positional hop. The paths describe implementation nesting rather than the pool
contract.
This is Bombay's named-product law. Replace the existing public
PoolBehaviorSends with one public PoolSends product whose fields are
responses, assignments, and supervision. It must preserve the current
interpretation order—responses, then assignments, then supervision—and preserve
the current return-to-emitter paths. It must append each lane once and project
only the logical response destinations. This is a replacement, not an
additional layer.
smallest regression: construct PoolSends by semantic field name and interpret
one value in every lane; assert the complete ordered trace
expected production files: pool.rs, requirements.rs, lib.rs
expected test/docs files: runtime_contracts.rs, pool model/property/fuzz users,
worker-pool.md
expected production delta: approximately net zero
public API: +1 product / -1 product
deleted machinery: private PoolSends alias and the pool's SendLayer nesting
reused laws: SupervisorSends, SendEffects, SendsFor, InterpretSends,
LogicalDeliveryProtocols, existing concrete pool events
No wrapper, builder, alias, marker, conversion, compatibility spelling, or generic lane registry is authorized.
Pool effect product: completed checkpoint
PoolBehaviorSends and the private SendLayer alias are gone. PoolSends
now owns the complete named product: responses, assignments, and
supervision. Its SendsFor proof keeps customer effects at the inner pool
event path and supervision requests at the outer supervision path. Its
interpreter visits responses, assignments, and supervision once each in the
previously authored order. Logical-host projection remains exactly the
customer response protocols; creator-local assignments and interpreter-owned
supervision requests do not fabricate hosts.
The pre-fix compile regression failed because PoolSends was not public. The
complete interpreter regression now constructs every semantic lane by name and
observes the exact eight-effect trace in both debug and optimized builds. FIFO
and keyed independent/model suites, wrapper-order tests, and user-facing pool
construction tests pass in both profiles. Workspace check and Clippy are clean.
A neighbouring test incorrectly expected the supervision law to observe an inner behavior's unrelated child creation. The production fold already preserved the correct boundary. The strengthened test now proves that the inner creation remains the prefix birth occurrence and precedes the two owned stable creations, while only the stable topology is observed.
This stage is not code reduction: the explicit named-product implementation is 18 net new production lines after deleting the generic nesting.
stage production: +53 / -35 / net +18
stage tests: +78 / -85 / net -7
stage public API: +1 product / -1 product
cumulative production: +3815 / -3788 / net +27
cumulative tests: +4329 / -2385 / net +1944
cumulative docs: +1665 / -73 / net +1592
cumulative infrastructure: +28 / -2 / net +26
Supervision-test semantic correction checkpoint
The complete workspace run exposed seven tests whose expected traces described a different ownership law from the production types:
- several strategy/model tests stopped workers before their authoritative creation facts had installed them, then expected unresolved peers to emit replacements immediately; and
- two tests treated unrelated inner-application births as though
Supervisehad adopted them into its fixed fleet.
No production fold was changed. The corrected tests first join every declared
stable child with its committed creation fact, then exercise replacement
strategy over that installed topology. Inner births are asserted to remain the
preserved prefix of the composed birth algebra and outside fixed ownership.
The replacement trace now also proves that RestForOne follows declared
topology order rather than numeric nonce order.
The corrected focused suites pass in debug and optimized profiles. The full workspace then passes 542 of 542 tests and workspace Clippy is warning-clean. This checkpoint also removes the now-unused dynamic-birth fixture; it adds no production code or public API.
cumulative production: +3822 / -3802 / net +20
cumulative tests: +4358 / -2486 / net +1872
cumulative docs: +1697 / -67 / net +1630
cumulative infrastructure: +28 / -2 / net +26
public API: replacements and deletions recorded by the per-stage ledgers;
no public type was added by this checkpoint
Unconsumed transition effects: pre-edit ledger
Exact blocker. Actions is the explicit, still-uninterpreted result of one
actor transition, but the value type is not must_use. The workspace denies
unused_must_use, yet a test or interpreter can unwrap a successful fold and
silently discard its sends, fresh creations, and next-behavior verdict. The
current tree contains direct examples, including lifecycle and wrapper tests.
This defeats the audit rule that tests observe complete actions and leaves the
compiler lint unable to enforce the underlying semantic law.
This is a derived Bombay effect-conservation rule: producing Actions is not
performing those effects. A caller must interpret, inspect, or explicitly
retain the complete value. Marking the existing product must_use adds no
effect, state, route, wrapper, or type.
smallest regression: a successful Active::transition(...).unwrap(); statement
is rejected by the workspace unused_must_use lint
expected production files: effects/actions.rs only
expected production delta: +1 attribute, net +1
expected test files: the initial textual scan found fourteen; the diagnostic
contract exposed at least thirty-one source/unit files
before downstream testkit binaries compiled, so the final
inventory is the warning-clean full workspace
public API: +0 types / -0 types; one diagnostic attribute on Actions
repair law: each site asserts or feeds the complete sends, creations, and
become verdict appropriate to its independent model
Binding a result to an underscore, calling drop, or locally allowing the
lint is not an accepted repair. The test must state why every effect lane is
empty or otherwise consume its complete observable result.
The stage exceeds fifteen files. This is inside the already-authorized repository-wide audit, but the expansion is recorded rather than hidden: the single production attribute revealed previously existing test evidence loss; it did not cause or require another production abstraction.
Heterogeneous shutdown effect visibility
The unconsumed-effect regression exposed one independent testability defect:
HeterogeneousShutdownSends<T> is public, but its sole semantic lane is
private and the only slice accessor is compiled for crate unit tests. A
framework consumer can interpret the product, but cannot inspect or model-check
the complete transition result as data. That contradicts the named-product law
used by the other public effect products.
smallest regression: an integration test cannot name the emitted ordered
shutdown selections in Actions.sends.owned
expected production files: lifecycle/shutdown_coordinator.rs
expected production delta: replace one test-only accessor with one public,
documented read-only accessor; approximately +3 net
expected test files: heterogeneous_shutdown.rs
public API: +0 types / -0 types; +1 read-only method
reused law: the existing HeterogeneousShutdownSends product and its exact
phase-ordered request vector
This does not add a planner, layer, wrapper, route, or alternate effect lane.
Unconsumed transition effects: completed checkpoint
Actions is now must_use. Every diagnostic exposed by the workspace was
repaired by observing or forwarding the complete result rather than binding it
to _, dropping it, or suppressing the lint. The public heterogeneous
shutdown product now exposes its existing ordered lane through a read-only
as_slice method, so integration and model tests can inspect the complete
effect without privileged test-only access.
The independent fuzz workspace now also denies unused_must_use. Its twelve
targets consume initialization and transition actions, including sends,
creations, and the next-behavior verdict. Each target completed 5,000 generated
runs after the discovered minimal sequences were replayed directly. The runs
corrected several copied or incomplete test assumptions: application births
are not adopted into fixed ownership, presence announcement always replies,
pool stop may both replace and assign in one action, and initial proxy creation
requests observation before the worker-resolution join completes. These are
test-oracle corrections; no production behavior was changed to satisfy a fuzz
branch.
The generated corpus and crash files were removed after replay. They are run
artifacts, not source evidence. The flake now exposes a locked fuzz package
and shell containing nightly Rust, llvm-tools-preview, and cargo-fuzz, while
the ordinary repository gate remains on pinned stable Rust. A clean consumer
can therefore build or run the same targets with nix run .#fuzz -- ...
without relying on a separately installed mutable toolchain.
stage production: +12 / -0 / net +12
Actions attribute: +1
existing heterogeneous shutdown lane accessor: +11
stage public API: +0 types / -0 types; +1 read-only method
fuzz source/infrastructure currently visible in the working-tree diff:
+826 / -299 / net +527
Exact dynamic-start capability: pre-edit ledger
Exact blocker. A successful dynamic start currently receives an
authoritative committed creation fact and then returns
Recipient::global(address). That discards incarnation identity and
reconstructs a weaker logical destination from allocation data. The
Started outcome must carry the exact established recipient produced by the
creation commit. A rejected creation must carry no recipient.
This is the existing creation-capability law, not a new dynamic-supervision
abstraction. Reuse ObserveEstablishedCreation, EstablishedCreation, and
EstablishedRecipient; do not add a wrapper, conversion registry, second
protocol, or address-to-endpoint lookup.
smallest regression: both initial-fact orders return the exact endpoint issued
by the creation interpreter, exactly once
expected production files: dynamic_supervisor.rs and requirements.rs
expected production delta: approximately neutral
expected test files: dynamic_supervisor_join.rs, runtime_contracts.rs,
supervision_sequences.rs, and affected interpretation tests
public API: +0 types / -0 types; Started strengthens its existing child field
Exact dynamic-start capability: completed checkpoint
DynamicSupervisorOutcome::Started now returns the exact
EstablishedRecipient issued by the committed proxy creation fact. The fold
retains that capability when it arrives first, joins it with the matching
worker-resolution fact in either order, and emits it exactly once. Rejections
carry no capability. Wrong nonces, wrong creation kinds, duplicate facts,
stale facts, and contradictory authoritative results retain their complete
typed errors; shutdown is total before, between, and after the joined facts.
The complete outer StopOnShutdown<DynamicSupervisor<...>> composition is
tested in both fact orders. The independent join suite passes six cases in
debug and optimized builds, and the supervision fuzz target completed 5,000
runs through both orders, worker rejection, and all intermediate shutdown
points using the flake-locked fuzz runner. The generated corpus was removed
after the run.
No public type, wrapper, registry, protocol alias, or endpoint lookup was added. Existing logical-address tests now use test-local runtime address types with opaque established endpoints where they model dynamic creation; fixed supervision and pool contracts remain logical where no exact establishment law applies.
production files: dynamic_supervisor.rs and requirements.rs
public API: +0 types / -0 types
verification: workspace compile; focused debug/release folds; 5,000 fuzz runs
Explicit restart timing: pre-edit ledger
Exact blocker. RestartConfiguration::new and
PoolConfiguration::new silently choose RestartTiming::Immediate. Immediate
versus delayed emission changes the transition trace and is therefore policy,
not a constructor default. Construction must receive the timing value
explicitly. The delayed convenience constructors then carry no distinct law
and should be deleted rather than retained as a second configuration path.
The mechanical caller migration spans more than fifteen files (the current search finds 85 constructor occurrences across production, tests, benches, fuzz targets, and documentation). No production edit for this stage begins without treating that breadth as the required checkpoint rather than hiding it behind an alias or compatibility constructor.
expected production files: supervisor.rs and pool.rs, plus direct production callers
expected production delta: net-negative
expected public API: +0 types; remove `RestartConfiguration::delayed` and
`PoolConfiguration::delayed`
reused law: RestartTiming and the existing immediate/delayed ownership fold
Explicit restart timing: completed checkpoint
Both configuration products now have one constructor, and that constructor
requires RestartTiming. Seventy-seven formerly implicit immediate choices and
seven delayed-constructor choices were migrated directly; no default, alias,
wrapper, or compatibility path remains. The existing immediate and delayed
ownership folds remain the only semantic implementations.
The focused public-contract regression and the independent supervision and pool models pass in debug and optimized builds. All 542 workspace tests, the warning-denied Clippy and rustdoc gates, and the authoritative Nix flake check pass. The supervision and pool fuzz targets each complete 5,000 runs without a crash; their generated untracked corpus expansions are removed afterward.
production: +4893 / -4183 / net +710
tests: +7403 / -2964 / net +4439
docs: +2043 / -107 / net +1936
infra: +52 / -2 / net +50
public API: +37 types / -53 types; this stage adds no type and removes two
redundant public constructor methods
Public relay classification
RelayChildReports is not compiler-only naming machinery. It owns one
observable transformation: one report from one statically selected direct
child becomes exactly one ReportToParent effect, while every inner event,
effect, birth, error, phase, and verdict is preserved. The pool uses that law
to move worker completion across the proxy edge without fabricating a logical
destination.
That justifies a concrete reusable composition in actors::composition; it
does not automatically justify placing the type and its event algebra in the
crate-root application facade. The export audit must distinguish the public
associated-type requirement from root-level convenience and remove the latter
if no direct application use requires it.
Completion cleanup audit: evidence checkpoint
The cleanup pass distinguishes unreachable code from public-but-unnecessary
facade exposure and from concrete types that must remain nameable through a
public Behavior associated type.
Current evidence:
- workspace Clippy passes for all targets and features with warnings denied;
- every Rust source below
crates/behavior/srcandcrates/actors/srcis reachable from the current module graph; the deleted backoff-supervisor file has no remaining module or source reference; - the working tree contains no untracked fuzz corpus or crash artifact;
- a source scan before each
#[cfg(test)]module finds no productionRecipient::global(...)construction; and - current documentation no longer names
ProxyCommand::Forwardor describes dynamicStartedas a reconstructed logical recipient. Released changelog sections retain their historical names as release history.
The warning-denied rustdoc gate exposed two source-documentation defects:
Registry linked to Delivery without a resolvable path, and public
Supervisor documentation linked to the crate-private FixedFleetOwnership
type. The links are repaired without widening the ownership fold.
FixedFleetOwnership, OwnershipFold, and OwnershipError are crate-private
and retained. The first owns the one shared fixed-fleet state-transition law;
the second is the named product of complete actions plus an optional failure
that the application-composed, standalone, and pool owners interpret
differently; the third retains complete rejected lifecycle facts for those
public error mappings. The former WorkerDisposition and
WorkerOwnershipFold names no longer exist.
RelayChildReports and RelayChildReportEvent must remain public inside
actors::composition: the concrete relay is both directly composable and
appears through the pool's public birth/event algebra. Its unused crate-root
re-export is removed; that deletes a duplicate spelling without removing the
relay feature.
EventIngress and ChildInputIngress are not classified as dead. They
currently name opposite established-parent directions: child-to-parent facts
enter the parent's event algebra, while private parent-to-child inputs enter
the concrete child's event algebra. However, the relay also relies on the
trait split to combine one owned report implementation with blanket
preservation of arbitrary inner child inputs. Before accepting that as two
laws, a separate pure/compile-only probe must establish whether one existing
owner-indexed ingress contract can express both directions without structural
paths, overlap, a new marker type, or a wrapper. Compiler coherence alone is
not sufficient justification under the layer laws.
The downstream Bombay tree confirms that no compatibility surface should be
restored here: its current migration branch still names removed parent-specific
composition types and asserts that Started.child has a logical address. It
already owns exact endpoint interpretation, so it must consume the strengthened
established capability and update its application test rather than Behavior
reintroducing a weaker alias or address reconstruction.
Behavior Actors template-law audit (engineering record)
Historical evidence snapshot: the retained campaign verdicts were last
committed at 1f20cc4. They describe that revision. The
current repository quality audit tracks later
verification and unresolved work.
This is the working record of a proof-driven audit of every reusable Behavior implementation
exported by bombay-behavior-actors. It replaces the earlier routing-only
review, which incorrectly accepted creator-local delivery from a standalone
MessageAdapter and did not test BehaviorBase at the runtime resolution
boundary.
This file is not a completion certificate. A verdict is final only after its negative cases have an independent oracle and the full repository gates pass on the recorded revision. Earlier versions of this file incorrectly declared the catalogue complete while several action-loss and hard-coded-path cases were still untested; those declarations are not evidence.
Active owner-contract follow-up ledger
This checkpoint records the four independently reported gaps before any production edit. It is not evidence that any gap is closed.
- Exact blockers and smallest regressions:
- A generic framework can declare and finish shutdown phases through associated outputs, but cannot begin the one existing inferred builder without naming its hidden initial proof. A compile-only external-consumer regression must call a generic begin operation, declare two roles, and finish without spelling builder states.
RelayChildReportsforwards nested ingress but drops rootShutdownRequestedingress. Pure-fold regressions must cover the relay, FIFO pool, keyed pool, and relay under another transparent composition; the report lane must remain exactly once.Superviseretains worker installation and replacement facts but exposes no typed evidence to its inner application. An independent lifecycle trace must observe initial readiness with the exact worker nonce, replacement start/unavailability, replacement readiness, and permanent retirement or restart denial without guessing elapsed time.WorkerPool::newandKeyedWorkerPool::newcannot infer the customer route because no input mentions it. External-user compile tests must compare an associated construction operation, a route witness, and an extension operation, rejecting any dummy value that the pool does not semantically retain.
- Source classification: serialized actor turns, fresh creation, and the absence of a cross-producer arrival-order guarantee are actor-model laws. Root-ingress preservation and the associated-output builder entry are derived static compositions. Lifecycle evidence after a committed ownership transition, its exact fact vocabulary, and restart timing are explicit Bombay policy rather than Agha guarantees.
- Expected files: shutdown builder/re-exports and its interpretation test; report relay plus relay/pool composition tests; supervision protocol, ownership fold, adapter and model/fuzz tests; pool constructors and one external construction test; this audit record. Expected cumulative ceiling: 15 changed files.
- Expected production delta: at most
+260 / -40 / net +220. Tests and documentation are measured separately at each checkpoint. - Expected public API: add at most three public items: one begin operation, one exhaustive lifecycle fact sum (with data carried in its variants), and one owner-defined pool construction operation only if existing inherent syntax cannot express the inference law. Add no wrapper, alias, builder, route marker, registry, or erased envelope. Remove any superseded public machinery discovered in the same semantic path rather than retaining a compatibility layer.
- Existing machinery to reuse:
shutdown_after_childrenand its soleChildShutdownPhasestypestate;InjectEvent/ComposedEventand the relay's namedSendLayer;FixedFleetOwnership,OwnershipFold, andSupervisionEvent;DeliveryRoute,Recipient,BehaviorLayer, and the existing FIFO/keyed pool folds. No parallel planner, lifecycle wrapper, or pool factory object is permitted. - Toolchain finding: the repository pins Rust 1.95. General type-position
!remains unavailable on that toolchain and on stable 1.98; stabilizing it remains an active Rust 2026 project goal. The existing uninhabitedNeverremains the MSRV-compatible representation. NeitherNevernor!can constrain a customer route generic that occurs in no constructor input.
Supervision lifecycle design checkpoint
The application-facing lifecycle is one exhaustive event sum, not a wrapper, callback, query API, or second supervisor. One authoritative runtime fact may produce at most one inner lifecycle event. Multi-slot restart policy is carried as one named batch variant so the inner application performs at most one fold per outer turn.
| Lifecycle event | Data owned by the variant | Legal source transition |
|---|---|---|
Ready | stable proxy nonce, exact worker-incarnation nonce, explicit CreationKind | the join becomes complete after either a successful proxy creation or a successful worker creation; a replacement kind identifies replacement readiness |
ReplacementStarted | complete triggering WorkerStopped, exact running-worker creation facts selected by one-for-all/rest-for-one, and stable nonces selected while still awaiting initial creation | one atomic restart admission succeeds; delayed admission is still unavailable and cannot become ready before its matching replacement creation commits |
RetiredAfterStop | complete WorkerStopped | restart policy deliberately declines replacement |
Retired | complete existing SupervisionFailure | restart admission, worker factory, worker creation, proxy creation, or stable-proxy continuity fails permanently |
ShuttingDown | all stable slot nonces whose availability is ending | the first accepted shutdown request; duplicate shutdown requests produce no second lifecycle event |
The Ready join is symmetric: worker-first retains the worker fact without
notifying, proxy-first retains the proxy fact without notifying, and the
matching second success emits exactly one event. Duplicate, stale, wrong-kind,
wrong-nonce, and contradictory facts remain the existing typed errors and emit
no lifecycle event.
Lifecycle delivery must be an ordinary variant in the inner application's
named exhaustive domain/system event sum. That sum owns
EventIngress<Here, SupervisionLifecycle<A>>; existing structural
EventLayer compositions lift the source-indexed ingress to it without a
caller naming or recounting wrapper depth. Supervise must not make the lane
optional, silently discard it, invoke a callback, or queue an
interpreter-private self-message. Applications needing no lifecycle evidence
use the standalone Supervisor topology owner instead.
Controlled-error atomicity requires a prepare/commit transition:
- validate the authoritative fact against the current ownership sum;
- prepare the next ownership state, effects, and one lifecycle event without mutating ownership (restart admission is calculated on a staged budget and newly built workers remain owned by the plan);
- fold the lifecycle event through the inner application;
- only after that fold succeeds, commit the prepared ownership state and
combine the complete inner and ownership
Actionsproducts once.
If the inner fold rejects the lifecycle event, the prepared workers are dropped and neither the restart budget nor ownership state commits. This preserves B7 rather than relying on actor termination to hide partial mutation. For lifecycle-free authoritative facts, the existing ownership transition is unchanged.
Making this lane truthful is intentionally source-breaking: every current
Supervise inner event that lacks the lane must be migrated explicitly. A scan
finds 20 source/test/fuzz call-site files in addition to the supervision
protocol, ownership fold, and adapter. With the nine files already changed in
this follow-up, the complete migration necessarily exceeds the 15-file review
checkpoint; compatibility modes would only evade that checkpoint while
preserving the broken contract.
The audit question is not merely whether a template's fold compiles. For every capability or lifecycle fact, the review traces:
- who produced or supplied it;
- which concrete type retains it;
- which actor namespace owns its interpretation;
- which
Actionslane emits it; - which static interpreter authority realizes it; and
- how rejection or stale input remains typed and observable.
Laws and policies used
The source classification matters. Framework convention is not presented as actor-model law.
Actor-model laws
- A1 — serialized turns: one actor processes one communication at a time; the transition determines communications, fresh creation, and the next behavior.
- A2 — finite acquaintance: a transition may communicate with prior acquaintances, acquaintances in the current communication, and freshly created actors.
- A3 — fresh creation: allocation is fresh; replacing behavior with
becomeis distinct from actor creation. - A4 — no primitive effect ordering: the actor model does not require a general order among send, create, and become effects.
The primary-source classification and the exact boundary between these laws and Bombay constructions are summarized in the canonical actor-transition algebra and established-capabilities contracts.
Behavior Core laws
- B1 — explicit effects: a successful pure fold returns complete
Actions; it does not deliver, allocate, schedule, observe, or stop actors through an ambient side channel. - B2 — concrete protocols: protocol, event sum, send product, phase, error, and birth algebra remain statically visible.
- B3 — capability distinctions:
Recipient,ChildRoute, andEstablishedRecipientmean logical name, creator-local occurrence, and exact installed incarnation respectively. They are not interchangeable. - B4 — structural child authority: a local child effect resolves only when
the running emitter's direct
Birthalgebra proves the named occurrence. - B5 — transparent projection: nominal roles cross
BehaviorBaseonly while both canonical protocol and complete direct-birth algebra are equal. - B6 — compositional initialization: wrappers initialize their inner fold once, preserve its full result, and define how wrapper effects accumulate.
- B7 — typed rejection: rejected creation carries no established capability; controlled transition failures do not partially commit a new behavior state.
Declared Bombay policy
- P1 — staged child routes: a creator-local nonce may name a requested child before interpretation but is neither an address nor freshness proof.
- P2 — commit before dependency: interpreters commit same-action creation before dependent local sends and observation requests.
- P3 — explicit provenance: birth, replacement incarnation, observation relationship, timer generation, and shutdown request provenance travel as typed data.
- P4 — structural return paths: interpreter facts return through the exact typed path selected by the owning composition.
- P5 — explicit root shutdown: the root is composed directly as
StopOnShutdown,FinalizeOnShutdown, or a shutdown coordinator; it does not discover a nested shutdown handler. - P6 — stable logical domains: discovery membership, configured downstream destinations, stable proxy identity, and transport names deliberately remain logical recipients.
- P7 — truthful customer routes: a template that accepts an arbitrary customer capability retains its logical or exact form and emits the matching concrete effect without conversion.
- P8 — creation-dependent shutdown plans: a coordinator may begin before
its committed children are known. The topology owner reports its validated
plan through
Actionsto an explicit parent return path; installation is one typed event, happens at most once, and retains any earlier shutdown request.
Hypotheses and verdicts
Failed → fixed means the hypothesis was false in the audited revision and
this change repairs it. A pass means both the public type surface and the
relevant fold/interpreter seam support the statement.
| ID | Source | Falsifiable hypothesis | Verdict and evidence |
|---|---|---|---|
| H01 | A1, B1 | Every template transition is a deterministic fold returning all effects in Actions. | Failed → fixed. Machine committed a prefix of a failed drain, Stash could replay a fallible inner fold after earlier actions became unreturnable, and Supervise could reject child adoption after the application fold succeeded. Machine now stages the complete drain, Stash statically requires an infallible inner fold, and adoption is an explicit reported outcome that preserves the rest of the application's Actions. Independent models compare state and complete outputs after every generated step. |
| H02 | B2 | Every exported behavior has concrete event, send, phase, error, and birth types. | Pass. All Behavior implementations use associated concrete types; no catch-all envelope or registry exists. |
| H03 | B2 | No template uses dyn, Any, TypeId, downcasting, unsafe, serialization, or type-name dispatch for protocol composition. | Pass. Static source scan is clean; the only type_name use is a test assertion. |
| H04 | B5, B6 | A topology-transparent wrapper preserves its inner BehaviorBase, protocol, birth algebra, initialization effects, and order. | Pass. Stash, timing wrappers, watch/monitor, shutdown wrappers, and termination propagation project the inner base; equality constraints guard nominal role resolution. Composition and initialization tests exercise nested orders. |
| H05 | B4, B5 | Each standalone behavior that authors a direct topology exposes itself as BehaviorBase<Base = Self>. | Failed → fixed. Proxy, WorkerPool, and KeyedWorkerPool lacked the projection. runtime_contracts::every_topology_owner_exposes_itself_as_its_behavior_base now proves proxy, fixed, dynamic, FIFO, and keyed owners. |
| H06 | B4, B5 | A topology-changing composition cannot inherit an inner nominal child role whose birth algebra it replaced. | Pass. ResolveChildOccurrence requires exact protocol and birth equality. Supervise may expose its application base for inspection, but its proxy birth rewrite prevents stale role resolution; raw structural positions resolve against the running wrapper. |
| H07 | B4, P1 | Every production ChildDelivery, ObserveChild, ObserveCreation, ShutdownChild, or ChildTermination emitter owns the matching direct occurrence in its Birth. | Pass after H05. All such emissions are confined to proxy/supervision/pool/lifecycle owners with the corresponding child leaf; occurrence propagation is tested through nested wrappers. |
| H08 | B3, B4 | A standalone MessageAdapter with NoBirths cannot emit creator-local child delivery. | Failed → fixed. DeliveryRoute no longer accepts ChildRoute; a compile-fail example rejects the foreign route before runtime. The adapter retains logical, established, and closed mixed delivery modes only. |
| H09 | A2, B3, P6, P7 | Every delivery destination is supplied by configuration, a received message, discovery membership, or stable-proxy policy—not fabricated from child correlation. | Pass. Customer-bearing routing, workflow, operations, persistence, pool, supervision, and discovery messages retain their supplied DeliveryRoute; stable internal logical destinations remain explicit Recipient values. A committed dynamic proxy is returned as the exact EstablishedRecipient issued by the creation interpreter; no logical destination is reconstructed from its child correlation nonce or allocation address. |
| H10 | B3, P7 | A received or configured exact endpoint is never weakened to a logical recipient before delivery, observation, or shutdown. | Pass. Exact modes retain EstablishedRecipient/EstablishedActor and emit EstablishedDelivery, ObserveEstablished, or ShutdownEstablished; ReplyRoute retains mixed alternatives and its interpreter visits them in original order. No exact-to-logical conversion exists. |
| H11 | B7, P3 | A rejected EstablishedCreation cannot produce a recipient, actor, child route, birth, or restart-success fact. | Pass. The rejected variant owns only nonce, kind, and CreationRejection; independent established-creation models cover allocation through binding failure. |
| H12 | B3, B4, P1 | After a named child commits, callers can retain both its exact incarnation and its creator-local role/nonce without reconstructing either. | Failed → fixed. established_child now returns EstablishedChild<C, Role>, a named product of ChildRoute and EstablishedActor; rejection yields neither. Its shutdown_target method selects a heterogeneous plan branch from the retained role. |
| H13 | B4, P3 | Child observation and shutdown preserve the declared occurrence when equal child protocols appear more than once. | Pass. All local lifecycle request types carry Occurrence; compile-fail tests reject head/tail or nominal-role substitution. |
| H14 | B2, B4 | A shutdown plan accepts only child behaviors whose exact event algebra owns ShutdownRequested. | Pass. Homogeneous and heterogeneous coordinator compile-fail tests reject non-shutdown-capable children; request interpretation uses the resolved concrete child. |
| H15 | B4, P4 | Proxy reports retain the final parent event path through an outer shutdown transformation for every proxy-owning template. | Failed → fixed. The reporting path is generic. Action-interpreted outer-StopOnShutdown tests execute creation and stop reports for application-owned supervision, standalone fixed supervision, both delayed forms, dynamic supervision, FIFO pools, and keyed pools. None supplies a structural path at the composition call. |
| H16 | B2, P4 | Every timer, observation, creation, shutdown, and parent-report request names the exact structural return path accepted by the enclosing event sum. | Pass. runtime_contracts proves request/fact duals and nested path injection; send-product interpretation tests exercise each lane at the same path exactly once. |
| H17 | B6, P5 | Root shutdown can reach a FinalizeOnShutdown, retaining its final sends, creations, and stop result. | Pass. The finalizer or coordinator is placed directly at the root; algebra tests prove delivery to the finalizer and full action preservation. No guardian alias selects that policy indirectly. |
| H18 | B3, P3 | Recurring logical watch and exact-once correlated monitoring remain distinct laws. | Historical selected Watch evidence remains separate: it emits ObservePeer and accepts matching logical stop facts, including later incarnations. The new established-target affine-scope correlation, exact protocol-indexed accepted identity and cancellation authority are proposed and unexecuted here; they do not inherit that historical Pass. The candidate TerminationMonitor owns the single requested/observing/terminal control sum and emits one whole ObserveEstablished. Full new owning/consumer evidence and independent review are still required. |
| H19 | B3, B7, P3 | Termination monitoring represents requested, observing, rejected/cancelled, observed, and already-consumed relationships without correlated flags. | The public TerminationObservation view derives from one target-owned control sum. Rejected cancellation remains nonterminal and consumingly recoverable alongside Stopped; startup rejection remains distinct. Candidate ownership, replay, both-order and full-report tests must pass before acceptance is recorded. |
| H20 | B3, B4 | Termination propagation chooses either an occurrence-aware local child or an explicitly late-bound logical peer, never an inferred destination. | Pass. ChildTermination<A, O> and PeerTermination<A> are distinct target types with distinct request effects. |
| H21 | A3, B7, P3 | Supervision distinguishes replacement request, installation attempt, committed incarnation, rejection, stale result, and retirement. | Pass. Incarnation and ownership folds use exhaustive phase enums and explicit creation kinds; independent supervision models and exhaustive/property suites compare the full sequence behavior. |
| H22 | A3, P3 | Restarted or replacement success is reported only after a replacement-designated creation commits. | Pass. Proxy reports a request separately, checks CreationKind::Replacement, and emits committed resolution only after installation success; failure remains typed. |
| H23 | B7, P3 | Stale and duplicate lifecycle/timer facts cannot be reinterpreted as fresh success or consume another relationship. | Failed → fixed. Timer reactions are now statically infallible, so a matching generation has one total consume-and-react transition. A later audit found consumption hidden inside debug_assert!, which made deadline, one-shot, periodic, and receive-timeout accept duplicates in optimized builds. Acceptance now validates and consumes in the production guard; the redundant preflight helpers were removed. Duplicate lifecycle facts are returned through exact typed errors; stale timer facts remain inert. Debug and optimized regressions, wrapper-order properties, and stack fuzzing exercise these cases. |
| H24 | B1, B2 | Named multi-lane send products preserve every lane exactly once and in their documented structural order. | Pass. Each product has explicit SendEffects, SendsFor, and InterpretSends; runtime-contract and cross-lane tests record complete traces. |
| H25 | A2, B3, P6 | A dynamic supervisor exposes the exact committed stable proxy, never a reconstructed logical destination or the replaceable worker incarnation. | Failed → fixed. Proxy and worker creation facts are retained and joined in either arrival order. Exactly one Started is produced only after both have committed. It returns the EstablishedRecipient<C::Protocol> issued by the proxy-creation commit; later worker replacement stays local to that proxy. |
| H26 | B1, P3 | Time is always a typed input or schedule request; no fold reads wall-clock time or sleeps. | Pass. Production transitions consume Instant carried by lifecycle facts or TimerElapsed, and emit ScheduleAt/ScheduleAfter; Instant::now occurrences are test setup. |
| H27 | B2 | Semantic alternatives entering transition logic are sums, not booleans. | Failed → fixed. Circuit-breaker Succeeded/Failed messages were collapsed into succeeded: bool; the private Completion::{Succeeded, Failed} sum now preserves the domain through the helper boundary. Query predicates remain ordinary booleans. |
| H28 | B2, B7 | Option<T> denotes one value or absence, not overlapping lifecycle phases or correlated capabilities. | Pass after targeted inspection. Dynamic child, proxy incarnation, pool slot, breaker, lease, workflow, watch, and monitor phases use enums. Remaining options are exact absence queries, optional independent metadata, or optional one-effect outputs. |
| H29 | B1, B6 | Wrapper reactions cannot drop inner sends or creations when selecting Goto or Stop. | Pass. Wrappers transform the complete action product with map_sends/named reconstruction; terminal and initialization tests assert retained creates, sends, and verdicts. |
| H30 | A3, P1 | No actor address or exact endpoint is derived from nonce arithmetic, sequence position, timing, or address reuse. | Pass. From<u64> in fleet code creates configured local nonces only; exact addresses enter exclusively through interpreter facts. |
| H31 | B7 | Invalid configuration, overlap, exhaustion, unknown targets, and interpreter rejection remain typed results rather than production panics. | Failed → fixed. The conservation pass found errors that named only a key, nonce, or reason while consuming the remaining owned input. Machine, router policies, circuit breaker, rate limiter, priority queue, sequencer, lease, acknowledgements, correlation, task, barrier, workflow, health, readiness, presence, registry, pub-sub, supervision, and pools now return the complete rejected command or lifecycle fact. The only production expect is the proved positive capacity + full buffer => oldest value exists invariant. |
| H32 | A4, B6, P2 | Any ordering relied upon beyond actor-model law is declared as Bombay policy and tested at the interpreter boundary. | Pass. Create-before-dependent-send/request and wrapper initialization order are documented as policy; send products define their own deterministic interpretation order without claiming it as an Agha guarantee. |
| H33 | B2, B3, P7 | Every genuine customer-passing template accepts logical, exact, or deliberately mixed reply capabilities without allowing the route protocol to disagree with the reply message. | Failed → fixed. Customer, payload, and membership fields now carry one Route: DeliveryRoute that projects its exact protocol and send product; the duplicate protocol marker and repeated reply protocol parameters are removed. A catalogue compile/fold matrix instantiates every affected family with EstablishedRecipient and ReplyRoute, protocol mismatch is compile-fail, and logical recursive protocol tests remain finite. |
| H34 | B1–B3, B7, P4, P8 | Homogeneous and heterogeneous shutdown coordinators can receive their validated plans after committed child creation without out-of-band mutation, flags, plan substitution, lost early shutdown, or repeated installation. | Failed → fixed. ShutdownState is the complete lifecycle sum. A topology owner emits ReportShutdownPlan through Actions; the interpreter constructs the exact outer InstallShutdownPlan<P> event from the carried typed return path. Unit, independent model/property, fuzz, and compile-fail coverage exercise both plan families, duplicate installation, early shutdown, stale stops, ordered phases, and empty-plan termination. |
| H35 | B2–B4, B7, P1, P3 | A standalone fixed supervisor owns only supervision, while application command selection is expressed by ordinary typed actor composition. | Failed → fixed. The former SupervisedWorkers policy engine combined fixed ownership with a selector but owned no distinct lifecycle law. It and its availability-policy surface were removed. Supervisor now implements the concrete fixed-fleet fold directly; an application behavior or routing actor owns command selection and its own typed rejection law. |
| H36 | B1–B4, B6–B7, P1–P4, P8 | Creation-dependent shutdown planning retains a single typed implementation that both direct applications and generic frameworks can carry. | Failed → fixed. ChildShutdownPlan owns the distinct join from committed direct-child creation facts to one reported heterogeneous plan. Its direct builder remains the semantic implementation. DeclareShutdownPhase and FinishShutdownPhases expose only associated output types, so a framework neither names nor copies the hidden availability and phase proofs. |
| H37 | B1–B2, B6–B7 | A reusable actor template is retained only when it owns a distinct state-transition or typed event/effect transformation law. | Failed → fixed. Guardian aliases, established-watch policy aliases, feature aliases, selector-policy supervisors, backoff parameterization wrappers, and recipe functions were removed. The child shutdown planner remains because it owns creation-fact correlation and plan reporting. Watch and exact-once monitoring, application and standalone backoff, FIFO and keyed pools, and homogeneous and heterogeneous coordinators remain separate because their recurrence, input, assignment, or typed effect transitions differ. |
| H38 | B1–B4, B6–B7, P3 | Expected supervised-command unavailability is observable after mailbox admission and never collapses into an opaque actor crash. | Failed → fixed. Proxy returns the complete sender, phase, and command through its established parent-report lane in every unavailable phase. FIFO pools join that return with worker-stop in both orders, including restart exhaustion and shutdown. DynamicSupervisor returns it through the retained Start outcome route, while Supervise routes it into an explicitly authored application-domain event only when that application owns a stable-child route. All three owner paths are interpreted through an outer shutdown composition; the pool join, dynamic customer route, application event, duplicate replay, restart exhaustion, and shutdown cases pass in debug and optimized builds. |
| H39 | B1–B2, P1–P4 | A generic consumer can carry the child-shutdown builder without naming hidden typestate or implementing a second planner. | Failed → fixed. Generic full-interpreter tests declare and finish phases using only the two associated-output operation traits; direct syntax, reverse order, early shutdown, and all existing typed failures retain the same builder implementation. |
| H40 | B1–B2, P1–P3 | A composition owner exposes every transitive intentional logical protocol host statically, preserving duplicate occurrences and excluding exact-only endpoints. | Failed → fixed. LogicalHostRequirements is blanket-derived from the concrete root sends product and every transitive birth node. LogicalDeliveryProtocols follows each real send-product interpretation order: logical deliveries and interpreter requests with logical recipients contribute their protocols, while established and creator-local routes contribute none. The structural regression derives P, Q, Q without owner metadata; focused request regressions distinguish customer, logical diagnostic, and exact diagnostic routes. No registry, erased envelope, normalization, or runtime lookup was added. |
The repairs require no new Behavior Core algebra, runtime registry, dynamic type, forwarding actor, or address reconstruction. The verification record below is authoritative only when every listed gate is green on the same tree.
Complete template coverage
The hypotheses above were applied to every exported behavior family, not just the templates implicated by the initial blockers.
| Family | Behavior templates audited | Principal hypotheses |
|---|---|---|
| Base composition | Machine, MessageAdapter, MessageAdapterWithRoute | H01–H03, H08–H10, H24, H27–H31 |
| Transparent state/lifecycle wrappers | Stash, StopOnShutdown, FinalizeOnShutdown, Watch, TerminationMonitorWith, PropagateTermination | H04, H06, H13, H16–H20, H23–H24, H29 |
| Shutdown ownership | ShutdownCoordinator, HeterogeneousShutdownCoordinator | H07, H12–H17, H23–H24, H29–H32, H34, H36–H37 |
| Lifecycle task | Task | H01–H03, H09–H10, H24, H27–H31, H33 |
| Supervision | Proxy, application-composed Supervise, standalone Supervisor, and DynamicSupervisor; delayed replacement is the shared RestartTiming policy inside fixed ownership | H05–H07, H10, H13, H15–H16, H21–H25, H28–H33, H35, H37 |
| Worker pools | WorkerPool and the affinity-owning KeyedWorkerPool, both using the same fixed-ownership and stable-child layers | H05, H07, H09–H10, H13, H15–H16, H21–H24, H28–H31, H33 |
| Timing | Deadline, OneShot, Periodic, ReceiveTimeout, Lease | H01–H04, H10, H16, H23–H24, H26, H28–H33 |
| Routing | Router with all strategies, WorkQueue, Buffer, PriorityQueue, OrderGate, Sequencer, Deduplicator, RateLimiter, CircuitBreaker, Correlator, Acknowledgements | H01–H03, H09–H10, H23–H24, H27–H31, H33 |
| Discovery | Registry, Resolver, Topic, PubSub, Presence | H01–H03, H09–H10, H16, H23–H24, H26–H31, H33 |
| Workflow | Latch, Barrier, Workflow | H01–H03, H09–H10, H23–H24, H27–H31, H33 |
| Operations/persistence | Configuration<FeatureSet<_>>, Health, Readiness, Cache | H01–H03, H09–H10, H23–H24, H27–H31, H33, H37 |
Routing strategies are policy values owned by Router, not actors with their
own effect boundary. Redundant aliases for the same observation and shutdown
state machines were removed rather than counted as separate templates.
Adversarial law coverage
The audit does not count a constructor smoke test as proof of a state-machine law. The following independent suites model state after every generated step:
| Surface | Independent or adversarial evidence |
|---|---|
| Creation and exact capabilities | creation, custody, child_creation_actor, creation_settlement_custody |
| Machine and replay | fsm_properties, exhaustive, fsm_sequences |
| Stash and wrapper lane isolation | stash_properties, two_buffer, cross_lane, stack_sequences |
| Watch, monitoring, and terminal propagation | exact_termination_model, terminal_outcome_sequences, catalogue_sequences |
| Stable proxy, fixed supervision, and dynamic supervision | stable_proxy_shutdown_model, fixed_supervisor_initialization, dynamic |
| FIFO and keyed worker pools | fifo_pool, keyed_pool, direct_pool_customer, direct_worker_shutdown, fifo_pool_sequences |
| Homogeneous and heterogeneous shutdown | shutdown_model, heterogeneous_shutdown, shutdown_plan_sequences, plus coordinator compile-fail and ordering tests |
| Versioned operations and cache | catalogue_invariants models configuration, feature-set normalization, readiness, health tombstones, and LRU ownership |
| Routing and correlation | catalogue_models, routing_invariants, and correlation_invariants model stable priority, bounded ownership, token arithmetic, round robin, FIFO worker availability, sequencing, deduplication, ordering, correlation, and acknowledgements |
| Time | receive_timeout, timing_invariants, receive_timeout_sequences; timer-wrapper composition and initialization are attacked by init_contract, cross_lane, and stack_sequences |
| Workflow | workflow_invariants models barrier generations, latch single release, dependency activation, and terminal rejection; catalogue_sequences fuzzes workflow inputs |
| Discovery | registry and topic generated models live in catalogue_invariants; presence is generation-fuzzed by catalogue_sequences; resolver and keyed publication retain focused atomic owner tests because their immutable/snapshot state spaces are already exhaustively covered there |
Each model uses different vocabulary and ordinary collections. It compares observable state and complete owned outputs after every operation, including rejection and stale input. Fuzz targets are retained as a separate layer; they do not replace the reference models.
Verification checkpoint — 2026-08-27 working tree
cargo nextest run --workspace --no-fail-fast: 542 passed, none skipped.cargo clippy --workspace --all-targets: pass without Rust warnings.- The nominal-protocol, pool named-product, keyed/FIFO model, and corrected supervision regressions pass in both debug and optimized profiles.
- The pool state-machine fuzz target completed 5,000 executions after keyed delegation.
This is an intermediate checkpoint, not the final repository gate. The final
record must be updated only after cargo nextest run --workspace and
nix flake check pass on the same committed tree.
Capability conclusions
The three recipient forms have non-overlapping authority:
| Capability | Meaning | Lawful use in templates |
|---|---|---|
Recipient<P> | Logical protocol name | caller reply, discovery membership, transport name, or stable proxy identity |
ChildRoute<C, O> | One role and nonce in the current creator's namespace | only a topology owner whose direct Birth proves O |
EstablishedRecipient<P> / EstablishedActor<C> | One exact installed incarnation | exact delivery, observation, shutdown, or retained post-creation capability |
ReplyRoute<P> | Closed logical-or-exact customer capability | one running template intentionally accepts both forms without conversion |
EstablishedChild<C, O> intentionally retains the latter two facts together.
It is an Actors-level named product over existing capabilities. It neither
allocates an actor nor extends the Behavior algebra.
Future changes must repeat the six-step provenance trace above. A test that only inspects the emitted value is insufficient for creator-local effects: the running emitter must also prove that its interpreter can lawfully resolve the same occurrence.