1 import Base 2 import ./change_tick.bend as Tick 3 import ./clock_state.bend as Clock 4 import ./execution_lifecycle_notices.bend as Notice 5 import ./trace_facts.bend as TraceFacts 6 import ./deferred_command.bend as Deferred 7 import string_equality.bend as SE 8 import entity_key.bend as K 9 import entity_allocator.bend as A 10 11 # Framework-owned metadata. Independent factory roots are not globally unique. 12 # Schema-branded detached identity containing World namespace and full entity key 13 # (index plus generation). Extracting its index does not validate it; raw construction 14 # is trusted setup, not evidence that a handle belongs to a live entity. 15 type Handle is Data: 16 Handle{namespace: U32, key: K.Key} 17 # Affine namespace sequence for raw World creation. Keep one actual sequence for 18 # worlds that must distinguish each other; independently reset roots can collide. 19 # Application code normally uses world-io creation rather than fabricating this value. 20 type Factory is Type: 21 Factory{nextNamespace: U32} 22 # Detached registration id/name/ordered access metadata. It is not the registered 23 # runner, its affine Local/capture owner or a capability derived from a string. 24 type RegistrationMeta is Data: 25 RegistrationMeta{id: U32, name: String, access: List<&2,String>} 26 # Explicit low-level refusal reasons for entity, capacity, namespace or system-id 27 # admission. Callers must inspect their operation result rather than infer success. 28 type WorldError is Data: 29 ReservationUnauthorized{} 30 MissingEntity{} 31 CapacityExceeded{} 32 NamespaceExhausted{} 33 SystemIdExhausted{} 34 # Affine engine storage/resource owner with allocator and administrative state. 35 # Raw field construction/destructuring is trusted framework provisioning, not the 36 # opaque gameplay interface. Schema branding alone does not validate forged fields. 37 type World is Type: 38 World{namespace: U32,allocator: A.Allocator,store: Store,resource: Resource,events: List<&2,Event>,pending:List>>,registrations: List<&2,RegistrationMeta>,nextSystemId: U32,clock: Clock.State,lifecycle:Notice.Journal} 39 # Raw creation returns the advanced Factory and World on success; rejection 40 # returns the actual Factory and incoming resource owner without constructing C. 41 type Creation is Type: 42 Created{factory: Factory,world: World} 43 CreateRejected{factory: Factory,resource: Resource} 44 # Reservation admission result. Reserved provides a full handle but does not 45 # activate it or populate components; rejection includes the allocator refusal. 46 type Reservation is Data: 47 Reserved{handle: Handle} 48 ReservationRejected{error: WorldError} 49 # Low-level operation decision; returned World is separate. Accepted describes 50 # that operation, not success of an entire transaction or deferred command queue. 51 type Status is Data: 52 Accepted{} 53 Rejected{error: WorldError} 54 # Metadata-registration result; id allocation is distinct from owning or running 55 # a system. Exhaustion returns RegistrationRejected without a new registration. 56 type Registration is Data: 57 Registered{id: U32} 58 RegistrationRejected{error: WorldError} 59 60 # Return administrative time without advancing it. Full epoch is required by readers. 61 def clock(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World & Tick.Tick: 62 match world: 63 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,+clock,lifecycle}:(World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},Clock.now(clock)) 64 65 # Observe this execution's captured tick without advancing administrative time. 66 def execution_clock(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World & Tick.Tick: 67 match world: 68 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,+clock,lifecycle}:(World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},Clock.current(clock)) 69 70 def clock_returned(~S:Data,~C:Type,~R:Type,~E:Data,namespace:U32,allocator:A.Allocator,store:C,resource:R,events:List<&2,E>,pending:List>>,registrations:List<&2,RegistrationMeta>,nextSystemId:U32,changed:Clock.State & Tick.Tick,lifecycle:Notice.Journal) -> World & Tick.Tick: 71 (clock,tick)=changed 72 (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},tick) 73 74 # Explicit direct/deferred time boundary; this is not system admission. 75 def advance_clock(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World & Tick.Tick: 76 match world: 77 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:clock_returned(~S,~C,~R,~E,namespace,allocator,store,resource,events,pending,registrations,nextSystemId,Clock.advance(clock),lifecycle) 78 79 # Stamp a canonical write with its captured execution tick or a new direct-edit tick. 80 def write_clock(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World & Tick.Tick: 81 match world: 82 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:clock_returned(~S,~C,~R,~E,namespace,allocator,store,resource,events,pending,registrations,nextSystemId,Clock.write(clock),lifecycle) 83 84 # Enter only after registration admission; capture one tick for all run writes. 85 def enter_run(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World & Tick.Tick: 86 match world: 87 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:clock_returned(~S,~C,~R,~E,namespace,allocator,store,resource,events,pending,registrations,nextSystemId,Clock.enter(clock),lifecycle) 88 89 # Exit an executed run on success or failure, preserving administrative progress. 90 def leave_run(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World: 91 match world: 92 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,Clock.leave(clock),lifecycle} 93 94 def ensure_returned(~S:Data,~C:Type,~R:Type,~E:Data,namespace:U32,allocator:A.Allocator,store:C,resource:R,events:List<&2,E>,pending:List>>,registrations:List<&2,RegistrationMeta>,nextSystemId:U32,result:Clock.State & Bool,lifecycle:Notice.Journal) -> World & Bool: 95 (clock,owned)=result 96 (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},owned) 97 # A direct query owns a new scope; a query inside a system reuses that scope. 98 def ensure_run(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World & Bool: 99 match world: 100 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:ensure_returned(~S,~C,~R,~E,namespace,allocator,store,resource,events,pending,registrations,nextSystemId,Clock.ensure(clock),lifecycle) 101 def finish_run(~S:Data,~C:Type,~R:Type,~E:Data,owned:Bool,world:World) -> World: 102 match world: 103 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,Clock.finish(owned,clock),lifecycle} 104 105 # Extract only the slot index for trusted storage adapters. Generation and World 106 # namespace are omitted; this projection must not replace handle validation. 107 def handle_index(~S:Data,handle:Handle) -> U32: 108 match handle: 109 case Handle{_,key}:K.index(key) 110 # Extract the complete index/generation key, without checking namespace or liveness. 111 def handle_key(~S:Data,handle:Handle) -> K.Key: 112 match handle: 113 case Handle{_,key}:key 114 115 # Start a raw namespace sequence at one. This pure constructor is not a global 116 # uniqueness service; ordinary independently created Worlds use world-io.bend. 117 def factory() -> Factory: 118 Factory{1} 119 def create_checked(~S:Data,~C:Type,~R:Type,~E:Data,~empty:Unit -> C,available:Bool,+namespace:U32,resource:R) -> Creation: 120 match available: 121 case False{}:CreateRejected{Factory{namespace},resource} 122 case True{}:Created{Factory{(namespace + 1 : U32)},World{namespace,A.empty(),empty(Unit{}),resource,[],[],[],1,Clock.empty(),Notice.empty(~S,namespace)}} 123 # Consume the raw Factory and resource owner to construct an empty World, advancing 124 # the namespace sequence on success. Namespace exhaustion returns both inputs via 125 # CreateRejected. Trusted empty supplies C; use world-io for application creation. 126 def create(~S:Data,~C:Type,~R:Type,~E:Data,~empty:Unit -> C,factory:Factory,resource:R) -> Creation: 127 match factory: 128 case Factory{+namespace}:create_checked(~S,~C,~R,~E,~empty,U32.is_lt(namespace,4294967295),namespace,resource) 129 def reserve_result(~S:Data,~C:Type,~R:Type,~E:Data,+namespace:U32,store:C,resource:R,+events:List<&2,E>,pending:List>>,+registrations:List<&2,RegistrationMeta>,+nextSystemId:U32,+clock:Clock.State,allocator:A.Allocator,reservation:A.Reservation,lifecycle:Notice.Journal) -> World & Reservation: 130 match reservation: 131 case A.Exhausted{}:(World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},ReservationRejected{CapacityExceeded{}}) 132 case A.Reserved{key}:(World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},Reserved{Handle{namespace,key}}) 133 def reserve_join(~S:Data,~C:Type,~R:Type,~E:Data,+namespace:U32,store:C,resource:R,+events:List<&2,E>,pending:List>>,+registrations:List<&2,RegistrationMeta>,+nextSystemId:U32,+clock:Clock.State,reserved:A.Allocator & A.Reservation,lifecycle:Notice.Journal) -> World & Reservation: 134 (allocator,reservation) = reserved 135 reserve_result(~S,~C,~R,~E,namespace,store,resource,events,pending,registrations,nextSystemId,clock,allocator,reservation,lifecycle) 136 # Reserve a full allocator key, returning World in both success and refusal. 137 # Reservation consumes allocator progress and is not yet visible as a live entity. 138 def reserve_id(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World & Reservation: 139 match world: 140 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:reserve_join(~S,~C,~R,~E,namespace,store,resource,events,pending,registrations,nextSystemId,clock,A.reserve(allocator),lifecycle) 141 def checked_join(~S:Data,~C:Type,~R:Type,~E:Data,namespace:U32,store:C,resource:R,events:List<&2,E>,pending:List>>,registrations:List<&2,RegistrationMeta>,nextSystemId:U32,clock:Clock.State,checked:A.Allocator & Bool,lifecycle:Notice.Journal) -> World & Bool: 142 match checked: 143 case (allocator,valid):(World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},valid) 144 def checked_namespace(~S:Data,~C:Type,~R:Type,~E:Data,allowed:Bool,mode:U32,world:World,key:K.Key) -> World & Bool: 145 match allowed world: 146 case False{} world:(world,False{}) 147 case True{} World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:checked_join(~S,~C,~R,~E,namespace,store,resource,events,pending,registrations,nextSystemId,clock,A.checked(allocator,key,mode),lifecycle) 148 def checked(~S:Data,~C:Type,~R:Type,~E:Data,mode:U32,world:World,handle:Handle) -> World & Bool: 149 match world handle: 150 case World{+namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle} Handle{ns,key}:checked_namespace(~S,~C,~R,~E,U32.is_eq(namespace,ns),mode,World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},key) 151 # Trusted structural check of namespace and current allocator key metadata, without 152 # requiring a live entity. Not a gameplay substitute for valid. 153 def metadata_valid(~S:Data,~C:Type,~R:Type,~E:Data,world:World,handle:Handle) -> World & Bool: 154 checked(~S,~C,~R,~E,0,world,handle) 155 # Return World with namespace/full-generation/live validation. Foreign or stale 156 # handles fail before allocator slot access for an unmatched namespace. 157 def valid(~S:Data,~C:Type,~R:Type,~E:Data,world:World,handle:Handle) -> World & Bool: 158 checked(~S,~C,~R,~E,1,world,handle) 159 # Return World with validation of actual Pending authority for this full handle. 160 # An arbitrary reserved key does not by itself establish that pending authority. 161 def pending_valid(~S:Data,~C:Type,~R:Type,~E:Data,world:World,handle:Handle) -> World & Bool: 162 checked(~S,~C,~R,~E,2,world,handle) 163 def phase_checked(~S:Data,~C:Type,~R:Type,~E:Data,phase:A.Phase,key:K.Key,checked:World & Bool) -> World & Status: 164 match checked: 165 case (world,False{}):(world,Rejected{MissingEntity{}}) 166 case (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},True{}):(World{namespace,A.write_phase(allocator,key,phase),store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},Accepted{}) 167 # Trusted lifecycle phase change after metadata validation; returns World and 168 # Status. This does not construct components or execute a pending populate closure. 169 def activate(~S:Data,~C:Type,~R:Type,~E:Data,world:World,+handle:Handle) -> World & Status: 170 phase_checked(~S,~C,~R,~E,A.Live{},handle_key(~S,handle),metadata_valid(~S,~C,~R,~E,world,handle)) 171 # Opaque storage is retained; this operation cannot make its slot reusable. 172 # Mark a metadata-valid entity DeadRetained, returning Status and World. Payloads 173 # remain in storage; schema cleanup must precede slot release/reuse. 174 def deactivate(~S:Data,~C:Type,~R:Type,~E:Data,world:World,+handle:Handle) -> World & Status: 175 phase_checked(~S,~C,~R,~E,A.DeadRetained{},handle_key(~S,handle),metadata_valid(~S,~C,~R,~E,world,handle)) 176 def discard_status(~S:Data,~C:Type,~R:Type,~E:Data,observed:World & Status) -> World: 177 match observed: 178 case (world,_):world 179 # Structural bookkeeping for an actually staged spawn. Metadata validation gates 180 # the phase update; this helper alone does not enqueue or populate an entity. 181 def mark_pending(~S:Data,~C:Type,~R:Type,~E:Data,world:World,+handle:Handle) -> World: 182 discard_status(~S,~C,~R,~E,phase_checked(~S,~C,~R,~E,A.Pending{},handle_key(~S,handle),metadata_valid(~S,~C,~R,~E,world,handle))) 183 def owner_change(~S:Data,~C:Type,~R:Type,~E:Data,release:Bool,+key:K.Key,checked:World & Bool) -> World: 184 match release checked: 185 case _, (world,False{}):world 186 case False{}, (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},True{}):World{namespace,A.abandon(allocator,key),store,resource,events,pending,registrations,nextSystemId,clock,lifecycle} 187 case True{}, (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},True{}):World{namespace,A.release(allocator,key),store,resource,events,pending,registrations,nextSystemId,clock,lifecycle} 188 # Abandon actual pending authority without rewinding reserved allocator progress. 189 # Used by spawn rollback; does not pretend a reserved id was never consumed. 190 def clear_pending(~S:Data,~C:Type,~R:Type,~E:Data,world:World,+handle:Handle) -> World: 191 owner_change(~S,~C,~R,~E,False{},handle_key(~S,handle),pending_valid(~S,~C,~R,~E,world,handle)) 192 # Only schema-owned cleanup calls this after all payloads and stamps are cleared. 193 # Trusted cleanup boundary: release only after schema-owned payload/stamp clearing. 194 # Metadata validation is not proof that arbitrary caller-supplied storage is empty. 195 def release_cleared(~S:Data,~C:Type,~R:Type,~E:Data,world:World,+handle:Handle) -> World: 196 owner_change(~S,~C,~R,~E,True{},handle_key(~S,handle),metadata_valid(~S,~C,~R,~E,world,handle)) 197 def command_live(~S:Data,~C:Type,~R:Type,~E:Data,handle:Handle,checked:World & Bool) -> World & Bool: 198 match checked: 199 case (world,True{}):(world,True{}) 200 case (world,False{}):pending_valid(~S,~C,~R,~E,world,handle) 201 # Allow a live or actual Pending handle, preserving namespace/generation checks. 202 # This permits ordered deferred spawn/despawn workflows without making Pending 203 # entities readable through ordinary live-component access. 204 def command_valid(~S:Data,~C:Type,~R:Type,~E:Data,world:World,+handle:Handle) -> World & Bool: 205 command_live(~S,~C,~R,~E,handle,valid(~S,~C,~R,~E,world,handle)) 206 207 # Transfer one affine command to the reverse pending accumulator. It is not run 208 # until a barrier; arbitrary command bodies are trusted engine adapters. 209 def enqueue_command(~S:Data,~C:Type,~R:Type,~E:Data,world:World,command:Deferred.Command>) -> World: 210 match world: 211 case World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}:World{namespace,allocator,store,resource,events,command <> pending,registrations,nextSystemId,clock,lifecycle} 212 # Wrap a raw World-to-World closure as a deferred command. No immediate effect 213 # or automatic transaction inverse is supplied for the callback. 214 def enqueue(~S:Data,~C:Type,~R:Type,~E:Data,world:World,command:World -> World) -> World: 215 enqueue_command(~S,~C,~R,~E,world,Deferred.Raw{command}) 216 def drain_fifo(~S:Data,~C:Type,~R:Type,~E:Data,commands:List>>,world:World) -> World: 217 match commands: 218 case []:world 219 case command <> rest:Deferred.run_disabled(~World,command,drain_fifo(~S,~C,~R,~E,rest,world)) 220 def drain_apply(~S:Data,~C:Type,~R:Type,~E:Data,command:Deferred.Command>,result:World & TraceFacts.State) -> World & TraceFacts.State: 221 match result: 222 case (world,trace):Deferred.run(~World,command,world,trace) 223 def drain_fifo_traced(~S:Data,~C:Type,~R:Type,~E:Data,commands:List>>,world:World,trace:TraceFacts.State) -> World & TraceFacts.State: 224 match commands: 225 case []:(world,trace) 226 case command <> rest: 227 drain_apply(~S,~C,~R,~E,command,drain_fifo_traced(~S,~C,~R,~E,rest,world,trace)) 228 # Detach the current pending batch, execute it in FIFO enqueue order, and return 229 # World plus operation trace state. Commands enqueued during that drain remain 230 # pending for a later barrier; arbitrary callback effects are not transactional. 231 def barrier_completed(~S:Data,~C:Type,~R:Type,~E:Data,result:World & TraceFacts.State) -> World & TraceFacts.State: 232 (world,trace)=result 233 (leave_run(~S,~C,~R,~E,world),trace) 234 def barrier_entered(~S:Data,~C:Type,~R:Type,~E:Data,trace:TraceFacts.State,entered:World & Tick.Tick) -> World & TraceFacts.State: 235 match entered: 236 case (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},_):barrier_completed(~S,~C,~R,~E,drain_fifo_traced(~S,~C,~R,~E,pending,World{namespace,allocator,store,resource,events,[],registrations,nextSystemId,clock,lifecycle},trace)) 237 # Explicit deferred-application boundary; all commands in this batch share its tick. 238 def barrier_traced(~S:Data,~C:Type,~R:Type,~E:Data,world:World,trace:TraceFacts.State) -> World & TraceFacts.State: 239 barrier_entered(~S,~C,~R,~E,trace,enter_run(~S,~C,~R,~E,world)) 240 # Apply the current pending command batch in FIFO order with tracing disabled. 241 # Returns World; it neither invents a transaction rollback nor recursively drains 242 # new work queued by those commands. See commands.bend for lifecycle adapters. 243 def barrier(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World: 244 Deferred.world_only(~World,barrier_traced(~S,~C,~R,~E,world,TraceFacts.Off{})) 245 246 # Immediate whole-resource swap returning the displaced actual R owner. This raw 247 # administrative operation supplies neither projection restrictions nor undo. 248 def replace_resource(~S: Data,~C: Type,~R: Type,~E: Data,world: World,replacement: R) -> World & R: 249 match world: 250 case World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}: (World{namespace,allocator,store,replacement,events,pending,registrations,nextSystemId,clock,lifecycle},resource) 251 252 def event_append_loop(~E: Data,reversed: List<&2,E>,+output: List<&2,E>) -> List<&2,E>: 253 match reversed: 254 case []: output 255 case head <> rest: event_append_loop(~E,rest,head <> output) 256 257 def event_append(~E: Data,+left: List<&2,E>,+right: List<&2,E>) -> List<&2,E>: 258 event_append_loop(~E,List.reverse(&2,E,left),right) 259 260 def event_publish_nonempty(~S: Data,~C: Type,~R: Type,~E: Data,world: World,+published: List<&2,E>) -> World: 261 match world: 262 case World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}: World{namespace,allocator,store,resource,event_append(~E,events,published),pending,registrations,nextSystemId,clock,lifecycle} 263 264 # An empty publication changes no field and needs no traversal of retained events. 265 # Append detached Data events in list order and return World. Empty input preserves 266 # World. This does not synchronize typed event Runtime batches/readers or run systems. 267 def event_publish(~S: Data,~C: Type,~R: Type,~E: Data,world: World,+published: List<&2,E>) -> World: 268 match published: 269 case []: world 270 case head <> rest: event_publish_nonempty(~S,~C,~R,~E,world,head <> rest) 271 272 # Return World and its copied Data event list without consuming or acknowledging 273 # reader positions. Affine Type event logs use their separate resource ownership. 274 def events_view(~S: Data,~C: Type,~R: Type,~E: Data,world: World) -> World & List<&2,E>: 275 match world: 276 case World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}: (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},events) 277 278 def observe_resource_finish(~S: Data,~C: Type,~R: Type,~E: Data,~V: Type,observed: R & V,+namespace: U32,allocator: A.Allocator,store: C,+events: List<&2,E>,pending:List>>,+registrations: List<&2,RegistrationMeta>,+nextSystemId: U32,+clock: Clock.State,lifecycle:Notice.Journal) -> World & V: 279 (resource,value) = observed 280 (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},value) 281 282 # Trusted resource-owner adapter: project returns the actual R with an Out owner 283 # or value, then World is rebuilt. The signature does not prove projection purity 284 # or allow duplicating affine resource contents. 285 def observe_resource(~S: Data,~C: Type,~R: Type,~E: Data,~V: Type,~project: R -> R & V,world: World) -> World & V: 286 match world: 287 case World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}: observe_resource_finish(~S,~C,~R,~E,~V,project(resource),namespace,allocator,store,events,pending,registrations,nextSystemId,clock,lifecycle) 288 289 def register_checked(~S: Data,~C: Type,~R: Type,~E: Data,available: Bool,world: World,+name: String,+access: List<&2,String>) -> World & Registration: 290 match available world: 291 case False{} world: (world,RegistrationRejected{SystemIdExhausted{}}) 292 case True{} World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}: (World{namespace,allocator,store,resource,events,pending,RegistrationMeta{nextSystemId,name,access} <> registrations,(nextSystemId + 1 : U32),clock,lifecycle},Registered{nextSystemId}) 293 294 # Allocate a system metadata id/name/access entry, returning World and Registration. 295 # System-id exhaustion preserves World. Metadata alone does not install a runner 296 # or validate a gameplay body; ordinary registered-system setup performs that join. 297 def register(~S: Data,~C: Type,~R: Type,~E: Data,world: World,+name: String,+access: List<&2,String>) -> World & Registration: 298 match world: 299 case World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}: register_checked(~S,~C,~R,~E,U32.is_lt(nextSystemId,4294967295),World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},name,access) 300 301 # Exact ordered declaration comparison; no normalization grants capabilities. 302 def access_equal(xs: List<&2,String>,ys: List<&2,String>) -> Bool: 303 match xs ys: 304 case Nil{} Nil{}: True{} 305 case Nil{} Con{_,_}: False{} 306 case Con{_,_} Nil{}: False{} 307 case Con{x,xs} Con{y,ys}: Bool.and(SE.equal(x,y),access_equal(xs,ys)) 308 309 def matching_access(sameName: Bool,metaAccess: List<&2,String>,wantedAccess: List<&2,String>) -> Bool: 310 match sameName: 311 case False{}: False{} 312 case True{}: access_equal(metaAccess,wantedAccess) 313 314 def matching_id(sameId: Bool,metaName: String,wantedName: String,metaAccess: List<&2,String>,wantedAccess: List<&2,String>) -> Bool: 315 match sameId: 316 case False{}: False{} 317 case True{}: matching_access(SE.equal(metaName,wantedName),metaAccess,wantedAccess) 318 319 def registration_meta_matches(meta: RegistrationMeta,+id: U32,+name: String,+access: List<&2,String>) -> Bool: 320 match meta: 321 case RegistrationMeta{mid,mname,maccess}: matching_id(U32.is_eq(mid,id),mname,name,maccess,access) 322 323 def registration_list_walk(registrations: List<&2,RegistrationMeta>,+id: U32,+name: String,+access: List<&2,String>,found: Bool) -> Bool: 324 match registrations found: 325 case _ True{}: True{} 326 case [] False{}: False{} 327 case meta <> rest False{}: registration_list_walk(rest,id,name,access,registration_meta_matches(meta,id,name,access)) 328 329 def registration_list_matches(registrations: List<&2,RegistrationMeta>,+id: U32,+name: String,+access: List<&2,String>) -> Bool: 330 registration_list_walk(registrations,id,name,access,False{}) 331 332 333 # Return World and its namespace for engine admission checks, not global provenance. 334 def namespace(~S: Data,~C: Type,~R: Type,~E: Data,world: World) -> World & U32: 335 match world: 336 case World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}: (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},namespace) 337 338 # Check retained id, name and exact ordered access strings while returning World. 339 # No sorting/normalization creates extra authority or verifies arbitrary callbacks. 340 def registration_matches(~S: Data,~C: Type,~R: Type,~E: Data,world: World,+id: U32,+name: String,+access: List<&2,String>) -> World & Bool: 341 match world: 342 case World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}: (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},registration_list_matches(registrations,id,name,access)) 343 344 def with_store_finish(~S: Data,~C: Type,~R: Type,~E: Data,~Out: Type,observed: C & Out,+namespace: U32,allocator: A.Allocator,resource: R,+events: List<&2,E>,pending:List>>,+registrations: List<&2,RegistrationMeta>,+nextSystemId: U32,+clock: Clock.State,lifecycle:Notice.Journal) -> World & Out: 345 (store,value) = observed 346 (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},value) 347 348 # Trusted storage adapter threading actual C and returning Out with World. This is 349 # not a gameplay capability or a universal guarantee that effect preserves storage. 350 def with_store(~S: Data,~C: Type,~R: Type,~E: Data,~Out: Type,effect: C -> C & Out,world: World) -> World & Out: 351 match world: 352 case World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}: with_store_finish(~S,~C,~R,~E,~Out,effect(store),namespace,allocator,resource,events,pending,registrations,nextSystemId,clock,lifecycle) 353 354 def pending_prepend(~S: Data,~C: Type,~R: Type,~E: Data,command: World -> World,counted: List<(World -> World)> & U32) -> List<(World -> World)> & U32: 355 (commands,count) = counted 356 (command <> commands,(count + 1 : U32)) 357 358 def pending_length_acc(~S: Data,~C: Type,~R: Type,~E: Data,commands:List>>,reversed: List>>,count: U32) -> List>> & U32: 359 match commands: 360 case []: (List.reverse(&1,Deferred.Command>,reversed),count) 361 case command <> rest: pending_length_acc(~S,~C,~R,~E,rest,command <> reversed,(count + 1 : U32)) 362 363 def pending_length(~S: Data,~C: Type,~R: Type,~E: Data,commands:List>>) -> List>> & U32: 364 pending_length_acc(~S,~C,~R,~E,commands,[],0) 365 366 def pending_count_join(~S: Data,~C: Type,~R: Type,~E: Data,+namespace: U32,allocator: A.Allocator,store: C,resource: R,+events: List<&2,E>,+registrations: List<&2,RegistrationMeta>,+nextSystemId: U32,+clock: Clock.State,counted: List>> & U32,lifecycle:Notice.Journal) -> World & U32: 367 (pending,count) = counted 368 (World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},count) 369 370 # Count pending affine command closures without executing them, returning World 371 # with the original command order and owners retained. 372 def pending_count(~S: Data,~C: Type,~R: Type,~E: Data,world: World) -> World & U32: 373 match world: 374 case World{+namespace,allocator,store,resource,+events,pending,+registrations,+nextSystemId,+clock,lifecycle}: pending_count_join(~S,~C,~R,~E,namespace,allocator,store,resource,events,registrations,nextSystemId,clock,pending_length(~S,~C,~R,~E,pending),lifecycle) 375 376 # Event runtime retention replaces only its owned event domain. 377 # Administrative replacement of the Data event domain only. It does not execute 378 # pending work, rebuild registrations or reset external event reader owners. 379 def events_replace(~S: Data,~C: Type,~R: Type,~E: Data,world: World,+retained: List<&2,E>) -> World: 380 match world: 381 case World{+namespace,allocator,store,resource,_,pending,+registrations,+nextSystemId,+clock,lifecycle}: World{namespace,allocator,store,resource,retained,pending,registrations,nextSystemId,clock,lifecycle} 382 383 def key_handles(~S:Data,+namespace:U32,keys:List<&2,K.Key>) -> List<&2,Handle>: 384 match keys: 385 case []:[] 386 case key <> rest:Handle{namespace,key} <> key_handles(~S,namespace,rest) 387 def handles_join(~S:Data,~C:Type,~R:Type,~E:Data,+namespace:U32,store:C,resource:R,events:List<&2,E>,pending:List>>,registrations:List<&2,RegistrationMeta>,nextSystemId:U32,clock:Clock.State,observed:A.Allocator & List<&2,K.Key>,lifecycle:Notice.Journal) -> World & List<&2,Handle>: 388 match observed: 389 case (allocator,keys):(World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle},key_handles(~S,namespace,keys)) 390 # Return World and currently visible live full handles in allocator-visible order. 391 # Reserved/Pending handles are not an ordinary query membership list yet. 392 def handles(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World & List<&2,Handle>: 393 match world: 394 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:handles_join(~S,~C,~R,~E,namespace,store,resource,events,pending,registrations,nextSystemId,clock,A.visible(allocator),lifecycle) 395 396 # Bevy ad678262 bevy_app/sub_app.rs runs the schedule before clear_trackers. 397 # The public completed App boundary owns this call, including handled errors; 398 # bare system invocations and barriers preserve the current retention window. 399 def maintain_lifecycle(~S:Data,~C:Type,~R:Type,~E:Data,world:World) -> World: 400 match world: 401 case World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,lifecycle}:World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock,Notice.maintain(~S,lifecycle)}