event_notice_runtime.bend
bendvy-ecs-core@0.1.0.0 · 193 lines · 0 definitions
1import Base
2import bendvy-ecs-core@0.1.0.0/change_tick.bend as Tick
3import bendvy-ecs-core@0.1.0.0/deferred_command.bend as Deferred
4import bendvy-ecs-core@0.1.0.0/event_stream.bend as Leaf
5import bendvy-ecs-core@0.1.0.0/world.bend as W
6import bendvy-ecs-core@0.1.0.0/relation_commands.bend as Notice
7import bendvy-ecs-core@0.1.0.0/relation_failure_runtime.bend as K
8import bendvy-ecs-core@0.1.0.0/internal/relation_failures/domains.bend as D
9import bendvy-ecs-core@0.1.0.0/internal/relation_failures/routing.bend as FailureRoute
10import bendvy-ecs-core@0.1.0.0/internal/relation_failures/barrier.bend as FailureBarrier
11import bendvy-ecs-core@0.1.0.0/event_runtime.bend as Ev
12import bendvy-ecs-core@0.1.0.0/event_family_runtime.bend as Family
13import bendvy-ecs-core@0.1.0.0/event_family_routing.bend as Route
14
15# Descriptor metadata owns no second World. Typed publication ticks remain separate.
16type Failures<S:Data,P:Data> is Data:
17 Failures{domains:List<&2,D.Domain<S,Notice.Notice<S,P>>>,positions:List<&2,Ev.Position>,tick:Nat,frameStart:Nat,boundary:Nat,capacity:Nat,nextReader:U32}
18type State<S:Data,P:Data,F:Type,H:Type> is Type:
19 State{typed:Family.State<P,F,H>,failures:Failures<S,P>}
20
21def originals(~S:Data,~P:Data,records:List<&2,Notice.Notice<S,P>>) -> List<&2,P>:
22 match records:
23 case []:[]
24 case Notice.Original{event} <> rest:event <> originals(~S,~P,rest)
25 case Notice.RelationFailed{_} <> rest:originals(~S,~P,rest)
26 case Notice.ComponentRemoved{_} <> rest:originals(~S,~P,rest)
27 case Notice.CleanupPaused{_} <> rest:originals(~S,~P,rest)
28
29def started(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,typed:Family.State<P,F,H>,runtime:K.Runtime<S,C,R,Notice.Notice<S,P>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
30 match runtime:
31 case K.Runtime{world,domains,positions,tick,frameStart,boundary,capacity,nextReader}:Route.Runtime{world,State{typed,Failures{domains,positions,tick,frameStart,boundary,capacity,nextReader}},0n}
32
33def start(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,world:W.World<S,C,R,Notice.Notice<S,P>>,family:F,owners:H) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
34 started(~S,~C,~R,~P,~F,~H,Family.State{family,owners,[],0n},K.start(~S,~C,~R,~Notice.Notice<S,P>,world))
35
36def synchronized(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~sync:F -> Nat -> List<&2,P> -> F,state:State<S,P,F,H>,+tick:Nat,observed:W.World<S,C,R,Notice.Notice<S,P>> & List<&2,Notice.Notice<S,P>>) -> W.World<S,C,R,Notice.Notice<S,P>> & State<S,P,F,H>:
37 match state:
38 case State{Family.State{family,owners,+journal,frameStart},failures}:
39 (world,records)=observed
40 +published=List.drop(&2,P,originals(~S,~P,records),Ev.size(~P,journal))
41 (world,State{Family.State{sync(family,tick,published),owners,Ev.sync_checked(~P,Nat.is_lt(0n,Ev.count(~P,published)),journal,tick,published),frameStart},failures})
42
43def synchronize(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~sync:F -> Nat -> List<&2,P> -> F,world:W.World<S,C,R,Notice.Notice<S,P>>,state:State<S,P,F,H>,tick:Nat) -> W.World<S,C,R,Notice.Notice<S,P>> & State<S,P,F,H>:
44 synchronized(~S,~C,~R,~P,~F,~H,~sync,state,tick,W.events_view(~S,~C,~R,~Notice.Notice<S,P>,world))
45
46# The detached typed owner contains no World; K operations receive the sole World.
47def failure_runtime(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,runtime:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> K.Runtime<S,C,R,Notice.Notice<S,P>> & (Family.State<P,F,H> & Nat):
48 match runtime:
49 case Route.Runtime{world,State{typed,Failures{domains,positions,tick,frameStart,boundary,capacity,nextReader}},typedTick}:(K.Runtime{world,domains,positions,tick,frameStart,boundary,capacity,nextReader},(typed,typedTick))
50def failure_returned(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,held:Family.State<P,F,H> & Nat,runtime:K.Runtime<S,C,R,Notice.Notice<S,P>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
51 match held runtime:
52 case (typed,typedTick) K.Runtime{world,domains,positions,tick,frameStart,boundary,capacity,nextReader}:
53 Route.Runtime{world,State{typed,Failures{domains,positions,tick,frameStart,boundary,capacity,nextReader}},typedTick}
54
55# Slots preserve the original mixed-log positions, including partially retained Publications.
56type Retention<P:Data,F:Type> is Type:
57 Retention{family:F,journal:List<&2,Ev.Batch<P>>,slots:List<&2,List<&2,P>>}
58def retained_record(~P:Data,~F:Type,+head:List<&2,P>,returned:F & (List<&2,P> & List<&2,List<&2,P>>)) -> F & (List<&2,P> & List<&2,List<&2,P>>):
59 match returned:
60 case (family,(records,slots)):(family,(Ev.append(~P,head,records),head <> slots))
61def retained_record_head(~P:Data,~F:Type,rest:F -> F & (List<&2,P> & List<&2,List<&2,P>>),returned:F & List<&2,P>) -> F & (List<&2,P> & List<&2,List<&2,P>>):
62 match returned:
63 case (family,+head):retained_record(~P,~F,head,rest(family))
64def retained_records(~P:Data,~F:Type,~retain:F -> Nat -> List<&2,P> -> F & List<&2,P>,records:List<&2,P>,family:F,+tick:Nat) -> F & (List<&2,P> & List<&2,List<&2,P>>):
65 match records:
66 case []:(family,([],[]))
67 case head <> rest:retained_record_head(~P,~F,(family => retained_records(~P,~F,~retain,rest,family,tick)),retain(family,tick,[head]))
68def retained_batch_join(~P:Data,~F:Type,tick:Nat,+records:List<&2,P>,slots:List<&2,List<&2,P>>,returned:Retention<P,F>) -> Retention<P,F>:
69 match returned:
70 case Retention{family,journal,tail}:Retention{family,Family.kept_batch(~P,Nat.is_lt(0n,Ev.count(~P,records)),tick,records,journal),List.append(&2,List<&2,P>,slots,tail)}
71def retained_batch_head(~P:Data,~F:Type,rest:F -> Retention<P,F>,tick:Nat,returned:F & (List<&2,P> & List<&2,List<&2,P>>)) -> Retention<P,F>:
72 match returned:
73 case (family,(+records,slots)):retained_batch_join(~P,~F,tick,records,slots,rest(family))
74def retention(~P:Data,~F:Type,~retain:F -> Nat -> List<&2,P> -> F & List<&2,P>,journal:List<&2,Ev.Batch<P>>,family:F) -> Retention<P,F>:
75 match journal:
76 case []:Retention{family,[],[]}
77 case Ev.Batch{+tick,records} <> rest:retained_batch_head(~P,~F,(family => retention(~P,~F,~retain,rest,family)),tick,retained_records(~P,~F,~retain,records,family,tick))
78def original_notices(~S:Data,~P:Data,records:List<&2,P>) -> List<&2,Notice.Notice<S,P>>:
79 match records:
80 case []:[]
81 case head <> rest:Notice.Original{head} <> original_notices(~S,~P,rest)
82def rebuilt(~S:Data,~P:Data,notices:List<&2,Notice.Notice<S,P>>,slots:List<&2,List<&2,P>>) -> List<&2,Notice.Notice<S,P>>:
83 match notices slots:
84 case Nil{} _:[]
85 case Con{Notice.Original{_},rest} Nil{}:rebuilt(~S,~P,rest,[])
86 case Con{Notice.Original{_},rest} Con{head,tail}:List.append(&2,Notice.Notice<S,P>,original_notices(~S,~P,head),rebuilt(~S,~P,rest,tail))
87 case Con{Notice.RelationFailed{failure},rest} slots:Notice.RelationFailed{failure} <> rebuilt(~S,~P,rest,slots)
88 case Con{Notice.ComponentRemoved{id},rest} slots:Notice.ComponentRemoved{id} <> rebuilt(~S,~P,rest,slots)
89 case Con{Notice.CleanupPaused{frames},rest} slots:Notice.CleanupPaused{frames} <> rebuilt(~S,~P,rest,slots)
90def framed(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,owners:H,failures:Failures<S,P>,+tick:Nat,observed:W.World<S,C,R,Notice.Notice<S,P>> & List<&2,Notice.Notice<S,P>>,returned:Retention<P,F>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
91 match observed returned:
92 case (world,notices) Retention{family,journal,slots}:
93 Route.Runtime{W.events_replace(~S,~C,~R,~Notice.Notice<S,P>,world,rebuilt(~S,~P,notices,slots)),State{Family.State{family,owners,journal,tick},failures},tick}
94def frame(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~frame:F -> Nat -> F,~retain:F -> Nat -> List<&2,P> -> F & List<&2,P>,runtime:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
95 match runtime:
96 case Route.Runtime{world,State{Family.State{family,owners,journal,_},failures},+tick}:framed(~S,~C,~R,~P,~F,~H,owners,failures,tick,W.events_view(~S,~C,~R,~Notice.Notice<S,P>,world),retention(~P,~F,~retain,journal,frame(family,tick)))
97
98def failure_framed(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,detached:K.Runtime<S,C,R,Notice.Notice<S,P>> & (Family.State<P,F,H> & Nat)) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
99 match detached:
100 case (runtime,held):failure_returned(~S,~C,~R,~P,~F,~H,held,K.frame(~S,~C,~R,~Notice.Notice<S,P>,runtime))
101def frame_failures(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,runtime:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
102 failure_framed(~S,~C,~R,~P,~F,~H,failure_runtime(~S,~C,~R,~P,~F,~H,runtime))
103def inventoried(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,world:W.World<S,C,R,Notice.Notice<S,P>>,owners:H,journal:List<&2,Ev.Batch<P>>,frameStart:Nat,failures:Failures<S,P>,tick:Nat,returned:F & List<&2,String>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>> & Maybe<&2,List<&2,String>>:
104 match returned:
105 case (family,names):(Route.Runtime{world,State{Family.State{family,owners,journal,frameStart},failures},tick},Some{names})
106def inventory_enabled(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~inventory:F -> F & List<&2,String>,enabled:Bool,runtime:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>> & Maybe<&2,List<&2,String>>:
107 match enabled runtime:
108 case False{} runtime:(runtime,None{})
109 case True{} Route.Runtime{world,State{Family.State{family,owners,journal,frameStart},failures},tick}:inventoried(~S,~C,~R,~P,~F,~H,world,owners,journal,frameStart,failures,tick,inventory(family))
110
111def selected(~S:Data,~E:Data,~P:Data,~F:Type,~H:Type,failures:Failures<S,P>,returned:Family.State<P,F,H> & Leaf.Stream<S,E>) -> State<S,P,F,H> & Leaf.Stream<S,E>:
112 match returned:
113 case (typed,stream):(State{typed,failures},stream)
114def take(~S:Data,~E:Data,~P:Data,~F:Type,~H:Type,~take:F -> F & Leaf.Stream<S,E>,state:State<S,P,F,H>) -> State<S,P,F,H> & Leaf.Stream<S,E>:
115 match state:
116 case State{typed,failures}:selected(~S,~E,~P,~F,~H,failures,Family.take(~S,~E,~P,~F,~H,~take,typed))
117def put(~S:Data,~E:Data,~P:Data,~F:Type,~H:Type,~put:F -> Leaf.Stream<S,E> -> F,state:State<S,P,F,H>,stream:Leaf.Stream<S,E>) -> State<S,P,F,H>:
118 match state:
119 case State{typed,failures}:State{Family.put(~S,~E,~P,~F,~H,~put,typed,stream),failures}
120
121# Reserve from the same failure-reader pool used by standalone K.register.
122def reserved_failure_reader(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,held:Family.State<P,F,H> & Nat,result:K.Runtime<S,C,R,Notice.Notice<S,P>> & Maybe<&2,U32>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>> & Maybe<&2,U32>:
123 match result:
124 case (runtime,id):(failure_returned(~S,~C,~R,~P,~F,~H,held,runtime),id)
125def reserve_failure_reader_detached(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,detached:K.Runtime<S,C,R,Notice.Notice<S,P>> & (Family.State<P,F,H> & Nat)) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>> & Maybe<&2,U32>:
126 match detached:
127 case (runtime,held):reserved_failure_reader(~S,~C,~R,~P,~F,~H,held,K.reserve_reader(~S,~C,~R,~Notice.Notice<S,P>,runtime))
128def reserve_failure_reader(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,runtime:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>> & Maybe<&2,U32>:
129 reserve_failure_reader_detached(~S,~C,~R,~P,~F,~H,failure_runtime(~S,~C,~R,~P,~F,~H,runtime))
130
131
132# One outer ingestion boundary, shared by every parameter in an invocation.
133def ingested_failures(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,typed:Family.State<P,F,H>,positions:List<&2,Ev.Position>,+tick:Nat,start:Nat,boundary:Nat,capacity:Nat,nextReader:U32,returned:W.World<S,C,R,Notice.Notice<S,P>> & List<&2,D.Domain<S,Notice.Notice<S,P>>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
134 match returned:
135 case (world,domains):Route.Runtime{world,State{typed,Failures{domains,positions,tick,start,boundary,capacity,nextReader}},tick}
136def ingested_typed(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~classify:Notice.Notice<S,P> -> Maybe<D.DescriptorKey<S>>,+tick:Nat,returned:W.World<S,C,R,Notice.Notice<S,P>> & State<S,P,F,H>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
137 match returned:
138 case (world,State{typed,Failures{domains,positions,_,start,boundary,capacity,nextReader}}):
139 ingested_failures(~S,~C,~R,~P,~F,~H,typed,positions,tick,start,boundary,capacity,nextReader,FailureRoute.collect(~S,~C,~R,~Notice.Notice<S,P>,~classify,world,tick,domains))
140def ingest(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~sync:F -> Nat -> List<&2,P> -> F,~classify:Notice.Notice<S,P> -> Maybe<D.DescriptorKey<S>>,+tick:Nat,runtime:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
141 match runtime:
142 case Route.Runtime{world,state,_}:ingested_typed(~S,~C,~R,~P,~F,~H,~classify,tick,synchronize(~S,~C,~R,~P,~F,~H,~sync,world,state,tick))
143def attempt_clocked(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,state:State<S,P,F,H>,tick:Nat,clocked:W.World<S,C,R,Notice.Notice<S,P>> & Tick.Tick) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
144 match clocked:
145 case (world,_):Route.Runtime{world,state,1n+tick}
146def attempt_ingested(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,runtime:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
147 match runtime:
148 case Route.Runtime{world,state,tick}:attempt_clocked(~S,~C,~R,~P,~F,~H,state,tick,W.execution_clock(~S,~C,~R,~Notice.Notice<S,P>,world))
149# Standalone and composed operations share histories but may advance different
150# administrative clocks. Never publish below any already observed boundary.
151def before_state(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~sync:F -> Nat -> List<&2,P> -> F,~classify:Notice.Notice<S,P> -> Maybe<D.DescriptorKey<S>>,state:State<S,P,F,H>,+tick:Nat,world:W.World<S,C,R,Notice.Notice<S,P>>,clock:Tick.Tick) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
152 match state:
153 case State{typed,Failures{domains,positions,+failureTick,start,boundary,capacity,nextReader}}:
154 attempt_ingested(~S,~C,~R,~P,~F,~H,ingest(~S,~C,~R,~P,~F,~H,~sync,~classify,Nat.max(1n,Nat.max(tick,Nat.max(failureTick,Tick.total(clock)))),Route.Runtime{world,State{typed,Failures{domains,positions,failureTick,start,boundary,capacity,nextReader}},tick}))
155def before_clocked(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~sync:F -> Nat -> List<&2,P> -> F,~classify:Notice.Notice<S,P> -> Maybe<D.DescriptorKey<S>>,state:State<S,P,F,H>,tick:Nat,clocked:W.World<S,C,R,Notice.Notice<S,P>> & Tick.Tick) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
156 match clocked:
157 case (world,clock):before_state(~S,~C,~R,~P,~F,~H,~sync,~classify,state,tick,world,clock)
158# Zero is the initial unread cursor, so startup publications need a positive epoch.
159# Called only after registration/parameter validation; preparation follows it.
160def before_attempt(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~sync:F -> Nat -> List<&2,P> -> F,~classify:Notice.Notice<S,P> -> Maybe<D.DescriptorKey<S>>,runtime:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
161 match runtime:
162 case Route.Runtime{world,state,tick}:before_clocked(~S,~C,~R,~P,~F,~H,~sync,~classify,state,tick,W.clock(~S,~C,~R,~Notice.Notice<S,P>,world))
163# Called once after successful cursor commits, without another clock advance.
164def after_success(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~sync:F -> Nat -> List<&2,P> -> F,~classify:Notice.Notice<S,P> -> Maybe<D.DescriptorKey<S>>,runtime:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
165 match runtime:
166 case Route.Runtime{world,state,+tick}:ingest(~S,~C,~R,~P,~F,~H,~sync,~classify,tick,Route.Runtime{world,state,tick})
167
168
169# Internal invocation transport: detached metadata retains all shared owners.
170def split(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,runtime:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> W.World<S,C,R,Notice.Notice<S,P>> & (State<S,P,F,H> & Nat):
171 match runtime:
172 case Route.Runtime{world,state,tick}:(world,(state,tick))
173def join(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,world:W.World<S,C,R,Notice.Notice<S,P>>,metadata:State<S,P,F,H> & Nat) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
174 match metadata:
175 case (state,tick):Route.Runtime{world,state,tick}
176
177# The command-aware barrier keeps each command publication as its own batch.
178# Typed owners/journal, reader positions and membership remain unchanged.
179def barrier_drained(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,typed:Family.State<P,F,H>,positions:List<&2,Ev.Position>,frameStart:Nat,boundary:Nat,capacity:Nat,nextReader:U32,returned:FailureBarrier.BarrierState<S,C,R,Notice.Notice<S,P>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
180 match returned:
181 case FailureBarrier.BarrierState{world,domains,+tick}:Route.Runtime{world,State{typed,Failures{domains,positions,tick,frameStart,boundary,capacity,nextReader}},tick}
182
183def barrier_advanced(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~classify:Notice.Notice<S,P> -> Maybe<D.DescriptorKey<S>>,typed:Family.State<P,F,H>,domains:List<&2,D.Domain<S,Notice.Notice<S,P>>>,positions:List<&2,Ev.Position>,previous:Nat,frameStart:Nat,boundary:Nat,capacity:Nat,nextReader:U32,advanced:W.World<S,C,R,Notice.Notice<S,P>> & Tick.Tick) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
184 match advanced:
185 case (W.World{namespace,allocator,store,resource,events,pending,registrations,nextSystemId,clock},actual):
186 barrier_drained(~S,~C,~R,~P,~F,~H,typed,positions,frameStart,boundary,capacity,nextReader,
187 FailureBarrier.drain(~S,~C,~R,~Notice.Notice<S,P>,~classify,List.reverse(&1,Deferred.Command<W.World<S,C,R,Notice.Notice<S,P>>>,pending),
188 FailureBarrier.BarrierState{W.World{namespace,allocator,store,resource,events,[],registrations,nextSystemId,clock},domains,Nat.max(previous,Tick.total(actual))}))
189
190def barrier(~S:Data,~C:Type,~R:Type,~P:Data,~F:Type,~H:Type,~classify:Notice.Notice<S,P> -> Maybe<D.DescriptorKey<S>>,context:Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>) -> Route.Runtime<S,C,R,Notice.Notice<S,P>,State<S,P,F,H>>:
191 match context:
192 case Route.Runtime{world,State{typed,Failures{domains,positions,failureTick,frameStart,boundary,capacity,nextReader}},tick}:
193 barrier_advanced(~S,~C,~R,~P,~F,~H,~classify,typed,domains,positions,Nat.max(tick,failureTick),frameStart,boundary,capacity,nextReader,W.enter_run(~S,~C,~R,~Notice.Notice<S,P>,world))