1 import Base 2 import ./trace_world.bend as TraceWorld 3 import ./deferred_command.bend as Deferred 4 import ./trace_facts.bend as TraceFacts 5 import ./world.bend as W 6 import ./transaction.bend as T 7 8 # World pending commands are a reverse accumulator; input here is FIFO. 9 def enqueue_list(~S: Data,~C: Type,~R: Type,~E: Data,commands:List>>,world: W.World) -> W.World: 10 match commands: 11 case []: world 12 case command <> rest: enqueue_list(~S,~C,~R,~E,rest,W.enqueue_command(~S,~C,~R,~E,world,command)) 13 14 # Append the supplied FIFO command list to World pending work, returning World. 15 # This transfers affine command closures but does not execute them; W.barrier is 16 # the explicit visibility boundary. Raw command callbacks are trusted adapters. 17 def enqueue(~S: Data,~C: Type,~R: Type,~E: Data,world: W.World,commands:List>>) -> W.World: 18 enqueue_list(~S,~C,~R,~E,commands,world) 19 20 # Publish these detached Data events to World in list order. This is immediate 21 # publication, not transactional staging or synchronization of an event Runtime. 22 def publish(~S: Data,~C: Type,~R: Type,~E: Data,world: W.World,events: List<&2,E>) -> W.World: 23 W.event_publish(~S,~C,~R,~E,world,events) 24 25 # Finish with canonical World adapters: success enqueues staged commands and 26 # publishes staged Data events in authored order; failure replays inverse actions 27 # and drops staged commands/events. Returns World plus outcome. It does not apply 28 # the structural barrier or undo arbitrary external effects. 29 def tx_finish(~S: Data,~C: Type,~R: Type,~E: Data,~Error: Data,tx: T.Tx,E>,outcome: T.Outcome) -> T.Returned,Error>: 30 T.finish(~W.World,~E,~Error,~enqueue(~S,~C,~R,~E),~publish(~S,~C,~R,~E),tx,outcome) 31 32 def spawned(~S: Data,~C: Type,~R: Type,~E: Data,~P: Type,~populate: W.World -> W.Handle -> P -> W.World,activated: W.World & W.Status,handle: W.Handle,payload: P) -> W.World: 33 match activated: 34 case (world,W.Accepted{}): populate(world,handle,payload) 35 case (world,W.Rejected{_}): world 36 37 # Trusted deferred-command application: activate the reserved handle, then call 38 # populate only on acceptance. Payload is consumed, including when activation 39 # refuses; this low-level callback does not return an incoming-payload receipt. 40 def spawn_apply(~S: Data,~C: Type,~R: Type,~E: Data,~P: Type,~populate: W.World -> W.Handle -> P -> W.World,world: W.World,+handle: W.Handle,payload: P) -> W.World: 41 spawned(~S,~C,~R,~E,~P,~populate,W.activate(~S,~C,~R,~E,world,handle),handle,payload) 42 43 def spawn_applied_traced(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:W.World -> W.Handle -> P -> W.World,trace:TraceFacts.State,+handle:W.Handle,payload:P,result:W.World & W.Status) -> W.World & TraceFacts.State: 44 match result: 45 case (world,+status):(spawned(~S,~C,~R,~E,~P,~populate,(world,status),handle,payload),TraceWorld.applied(~S,trace,"spawn",handle,status)) 46 # Build a deferred spawn closure owning its actual payload. enabled selects raw 47 # or operation-fact recording; neither variant activates until command execution. 48 def spawn_command(~S:Data,~C:Type,~R:Type,~E:Data,~P:Type,~populate:W.World -> W.Handle -> P -> W.World,enabled:Bool,+handle:W.Handle,payload:P) -> Deferred.Command>: 49 match enabled: 50 case False{}:Deferred.Raw{world=>spawn_apply(~S,~C,~R,~E,~P,~populate,world,handle,payload)} 51 case True{}:Deferred.Observed{trace=>world=>spawn_applied_traced(~S,~C,~R,~E,~P,~populate,trace,handle,payload,W.activate(~S,~C,~R,~E,world,handle))} 52 # Only successful actual deferred spawns establish pending authority. The inverse 53 # clears at the World's current capacity after any intervening growth. 54 def track_pending(~S:Data,~C:Type,~R:Type,~E:Data,tx:T.Tx,E>,+handle:W.Handle) -> T.Tx,E>: 55 match tx: 56 case T.Tx{world,undo,commands,events,trace}:T.Tx{W.mark_pending(~S,~C,~R,~E,world,handle),(w => W.clear_pending(~S,~C,~R,~E,w,handle)) <> undo,commands,events,trace} 57 58 def spawn_reserved(~S: Data,~C: Type,~R: Type,~E: Data,~P: Type,~populate: W.World -> W.Handle -> P -> W.World,reservation: W.Reservation,world: W.World,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,+trace:TraceFacts.State,payload: P) -> T.Tx,E> & W.Reservation: 59 match reservation: 60 case W.ReservationRejected{error}: (T.Tx{world,undo,commands,events,trace},W.ReservationRejected{error}) 61 case W.Reserved{+handle}: (track_pending(~S,~C,~R,~E,T.Tx{world,undo,spawn_command(~S,~C,~R,~E,~P,~populate,TraceFacts.enabled(trace),handle,payload) <> commands,events,TraceWorld.deferred(~S,trace,"spawn",TraceFacts.StageOperation{},handle,TraceFacts.Attempted{})},handle),W.Reserved{handle}) 62 63 def spawn_join(~S: Data,~C: Type,~R: Type,~E: Data,~P: Type,~populate: W.World -> W.Handle -> P -> W.World,reserved: W.World & W.Reservation,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,payload: P) -> T.Tx,E> & W.Reservation: 64 (world,reservation) = reserved 65 spawn_reserved(~S,~C,~R,~E,~P,~populate,reservation,world,undo,commands,events,trace,payload) 66 67 # IDs stay reserved on failure, matching the historical allocator policy. 68 # Reserve an entity handle now and stage activation/population for the barrier. 69 # Reserved is not yet live: ordinary component reads cannot observe it. The Type 70 # payload moves into the command; reservation refusal does not return that payload. 71 # Failure drops staged work and clears pending authority but does not rewind the 72 # allocator's reserved-ID progress. See examples/captured-query/schema.bend. 73 def tx_spawn(~S: Data,~C: Type,~R: Type,~E: Data,~P: Type,~populate: W.World -> W.Handle -> P -> W.World,tx: T.Tx,E>,payload: P) -> T.Tx,E> & W.Reservation: 74 match tx: 75 case T.Tx{world,undo,commands,events,trace}: spawn_join(~S,~C,~R,~E,~P,~populate,W.reserve_id(~S,~C,~R,~E,world),undo,commands,events,trace,payload) 76 77 def deactivate_join(~S: Data,~C: Type,~R: Type,~E: Data,deactivated: W.World & W.Status) -> W.World: 78 (world,status) = deactivated 79 world 80 81 # Low-level liveness-only command application, discarding its Status. Component 82 # storage is retained; use the schema-cleanup variant for ordinary entity disposal. 83 def despawn_apply(~S: Data,~C: Type,~R: Type,~E: Data,world: W.World,handle: W.Handle) -> W.World: 84 deactivate_join(~S,~C,~R,~E,W.deactivate(~S,~C,~R,~E,world,handle)) 85 86 def despawn_applied_traced(~S:Data,~C:Type,~R:Type,~E:Data,trace:TraceFacts.State,handle:W.Handle,result:W.World & W.Status) -> W.World & TraceFacts.State: 87 match result: 88 case (world,status):(world,TraceWorld.applied(~S,trace,"despawn",handle,status)) 89 # Construct the liveness-only deferred command, optionally recording application 90 # status as an execution fact. Construction alone does not change liveness. 91 def despawn_command(~S:Data,~C:Type,~R:Type,~E:Data,enabled:Bool,+handle:W.Handle) -> Deferred.Command>: 92 match enabled: 93 case False{}:Deferred.Raw{world=>despawn_apply(~S,~C,~R,~E,world,handle)} 94 case True{}:Deferred.Observed{trace=>world=>despawn_applied_traced(~S,~C,~R,~E,trace,handle,W.deactivate(~S,~C,~R,~E,world,handle))} 95 def despawn_checked(~S: Data,~C: Type,~R: Type,~E: Data,allowed: Bool,world: W.World,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,+trace:TraceFacts.State,+handle: W.Handle) -> T.Tx,E> & W.Status: 96 match allowed: 97 case False{}: (T.Tx{world,undo,commands,events,trace},W.Rejected{W.MissingEntity{}}) 98 case True{}: (T.Tx{world,undo,despawn_command(~S,~C,~R,~E,TraceFacts.enabled(trace),handle) <> commands,events,TraceWorld.deferred(~S,trace,"despawn",TraceFacts.StageOperation{},handle,TraceFacts.Attempted{})},W.Accepted{}) 99 100 def despawn_join(~S: Data,~C: Type,~R: Type,~E: Data,checked: W.World & Bool,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,handle: W.Handle) -> T.Tx,E> & W.Status: 101 (world,allowed) = checked 102 despawn_checked(~S,~C,~R,~E,allowed,world,undo,commands,events,trace,handle) 103 104 # Low-level liveness-only operation retains component storage. Gameplay uses tx_despawn_with. 105 # Stage liveness-only deactivation for a live or legitimately pending handle. 106 # Foreign/stale/otherwise invalid handles return Rejected(MissingEntity) with Tx. 107 # Accepted is queue admission, not applied success; storage cleanup is not included. 108 def tx_despawn(~S: Data,~C: Type,~R: Type,~E: Data,tx: T.Tx,E>,+handle: W.Handle) -> T.Tx,E> & W.Status: 109 match tx: 110 case T.Tx{world,undo,commands,events,trace}: despawn_join(~S,~C,~R,~E,W.command_valid(~S,~C,~R,~E,world,handle),undo,commands,events,trace,handle) 111 112 # Schema-owned clear releases every component family after accepted deactivation. 113 def clear_join(~S: Data,~C: Type,~R: Type,~E: Data,+handle:W.Handle,cleared: W.World & Unit) -> W.World: 114 (world,unit) = cleared 115 W.release_cleared(~S,~C,~R,~E,world,handle) 116 117 def clear_accepted(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C,activated: W.World & W.Status,+handle: W.Handle) -> W.World: 118 match activated: 119 case (world,W.Rejected{_}): world 120 case (world,W.Accepted{}): clear_join(~S,~C,~R,~E,handle,W.with_store(~S,~C,~R,~E,~Unit,(store => (clear(store,handle),Unit{})),world)) 121 122 # Trusted schema-cleanup command: after accepted deactivation, clear the component 123 # store and release the cleared slot. Rejected deactivation skips clear. The clear 124 # adapter consumes actual payloads; this is not a recovery receipt. 125 def despawn_apply_with(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C,world: W.World,+handle: W.Handle) -> W.World: 126 clear_accepted(~S,~C,~R,~E,~clear,W.deactivate(~S,~C,~R,~E,world,handle),handle) 127 128 def despawn_checked_with(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C,allowed: Bool,world: W.World,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,handle: W.Handle) -> T.Tx,E> & W.Status: 129 match allowed: 130 case False{}: (T.Tx{world,undo,commands,events,trace},W.Rejected{W.MissingEntity{}}) 131 case True{}: (T.Tx{world,undo,Deferred.Raw{(w => despawn_apply_with(~S,~C,~R,~E,~clear,w,handle))} <> commands,events,trace},W.Accepted{}) 132 133 def despawn_join_with(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C,checked: W.World & Bool,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,handle: W.Handle) -> T.Tx,E> & W.Status: 134 (world,allowed) = checked 135 despawn_checked_with(~S,~C,~R,~E,~clear,allowed,world,undo,commands,events,trace,handle) 136 137 # Stage schema-owned cleanup after command-valid handle admission. Return Tx and 138 # Status now; liveness/components change at the barrier after successful finish. 139 # Failure drops the staged closure. clear is trusted complete-schema provisioning. 140 def tx_despawn_with(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C,tx: T.Tx,E>,+handle: W.Handle) -> T.Tx,E> & W.Status: 141 match tx: 142 case T.Tx{world,undo,commands,events,trace}: despawn_join_with(~S,~C,~R,~E,~clear,W.command_valid(~S,~C,~R,~E,world,handle),undo,commands,events,trace,handle) 143 144 # Cleanup owns actual removed payloads; its typed metadata is published only on success. 145 def clear_observed_join(~S: Data,~C: Type,~R: Type,~E: Data,~record: W.Handle -> E,+handle: W.Handle,cleared: W.World & List<&2,E>) -> W.World: 146 (world,records) = cleared 147 W.release_cleared(~S,~C,~R,~E,W.event_publish(~S,~C,~R,~E,world,List.append(&2,E,records,[record(handle)])),handle) 148 149 def clear_observed_accepted(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C & List<&2,E>,~record: W.Handle -> E,activated: W.World & W.Status,+handle: W.Handle) -> W.World: 150 match activated: 151 case (world,W.Rejected{_}): world 152 case (world,W.Accepted{}): clear_observed_join(~S,~C,~R,~E,~record,handle,W.with_store(~S,~C,~R,~E,~List<&2,E>,(store => clear(store,handle)),world)) 153 154 def despawn_valid_observed(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C & List<&2,E>,~record: W.Handle -> E,checked: W.World & Bool,+handle: W.Handle) -> W.World: 155 match checked: 156 case (world,False{}): world 157 case (world,True{}): clear_observed_accepted(~S,~C,~R,~E,~clear,~record,W.deactivate(~S,~C,~R,~E,world,handle),handle) 158 159 # Apply checked live-entity cleanup, publishing its component removal records 160 # followed by the entity record only after accepted deactivation. Invalid handles 161 # leave World unchanged and skip cleanup/publication. 162 def despawn_apply_observed(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C & List<&2,E>,~record: W.Handle -> E,world: W.World,+handle: W.Handle) -> W.World: 163 despawn_valid_observed(~S,~C,~R,~E,~clear,~record,W.valid(~S,~C,~R,~E,world,handle),handle) 164 165 def despawn_checked_observed(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C & List<&2,E>,~record: W.Handle -> E,allowed: Bool,world: W.World,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,+handle: W.Handle) -> T.Tx,E> & W.Status: 166 match allowed: 167 case False{}: (T.Tx{world,undo,commands,events,trace},W.Rejected{W.MissingEntity{}}) 168 case True{}: (T.Tx{world,undo,Deferred.Raw{(w => despawn_apply_observed(~S,~C,~R,~E,~clear,~record,w,handle))} <> commands,events,trace},W.Accepted{}) 169 170 def despawn_join_observed(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C & List<&2,E>,~record: W.Handle -> E,checked: W.World & Bool,undo: List<(W.World -> W.World)>,commands:List>>,events: List<&2,E>,trace:TraceFacts.State,+handle: W.Handle) -> T.Tx,E> & W.Status: 171 (world,allowed) = checked 172 despawn_checked_observed(~S,~C,~R,~E,~clear,~record,allowed,world,undo,commands,events,trace,handle) 173 174 # Stage cleanup plus typed lifecycle publication for a command-valid handle. 175 # Queue admission returns Tx and Status; barrier application rechecks live validity 176 # before consuming payloads and publishing records. These Data records do not own 177 # the removed Type payloads or imply automatic recovery. 178 def tx_despawn_observed(~S: Data,~C: Type,~R: Type,~E: Data,~clear: C -> W.Handle -> C & List<&2,E>,~record: W.Handle -> E,tx: T.Tx,E>,+handle: W.Handle) -> T.Tx,E> & W.Status: 179 match tx: 180 case T.Tx{world,undo,commands,events,trace}: despawn_join_observed(~S,~C,~R,~E,~clear,~record,W.command_valid(~S,~C,~R,~E,world,handle),undo,commands,events,trace,handle)