1 import Base 2 import ./world.bend as W 3 import ./system.bend as Sys 4 5 # Static plans contain no runtime closures. H holds heterogeneous affine 6 # registered runner owners; closed templates thread that owner through each step. 7 # Detached static sequence: System holds a registered id and condition slot; 8 # Phase is an observation label and Barrier is an explicit pending-work boundary. 9 # A phase label alone does not flush commands or execute a gameplay callback. 10 type Step is Data: 11 System{id: U32,condition: U32} 12 Phase{name: String} 13 Barrier{} 14 # Ordered progress records for entered phases, dispatched/skipped systems and 15 # applied barriers. Ran includes a system that failed; inspect RunResult as well. 16 type Observation is Data: 17 Entered{name: String} 18 Ran{id: U32} 19 Skipped{id: U32} 20 Applied{} 21 # Affine registered-owner bundle plus detached static steps and World namespace. 22 # Raw construction is trusted setup; build validates ids against its supplied catalog. 23 type Schedule is Type: 24 Schedule{namespace: U32,name: String,steps: List<&2,Step>,owners: H} 25 # Build result preserving the actual heterogeneous owners on invalid plans. 26 # Built retains those owners inside Schedule, not copied reusable runtime closures. 27 type Build is Type: 28 Built{schedule: Schedule} 29 Invalid{owners: H} 30 # Every result returns Schedule with actual runner owners and World. Failed stops 31 # at the first failed dispatch and keeps observations through that system; earlier 32 # successful systems are not rolled back as one schedule-wide transaction. Rejected 33 # is namespace admission refusal and runs no steps. 34 type RunResult is Type: 35 Finished{schedule: Schedule,world: W.World,observations: List<&2,Observation>} 36 Failed{schedule: Schedule,world: W.World,error: Error,observations: List<&2,Observation>} 37 Rejected{schedule: Schedule,world: W.World} 38 39 def contains(ids: List<&2,U32>,+id: U32) -> Bool: 40 match ids: 41 case []: False{} 42 case +head <> rest: Bool.or(U32.is_eq(head,id),contains(rest,id)) 43 44 def valid(steps: List<&2,Step>,+catalog: List<&2,U32>,+seen: List<&2,U32>) -> Bool: 45 match steps: 46 case []: True{} 47 case Phase{_} <> rest: valid(rest,catalog,seen) 48 case Barrier{} <> rest: valid(rest,catalog,seen) 49 case System{+id,_} <> rest: Bool.and(contains(catalog,id),Bool.and(Bool.not(contains(seen,id)),valid(rest,catalog,id <> seen))) 50 51 def build_checked(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,allowed: Bool,+namespace: U32,+name: String,+steps: List<&2,Step>,owners: H) -> Build: 52 match allowed: 53 case False{}: Invalid{owners} 54 case True{}: Built{Schedule{namespace,name,steps,owners}} 55 56 # Validate that each System id occurs in the supplied catalog and appears at most 57 # once in steps. Return Invalid with owners otherwise. Condition slots and catalog 58 # provenance are trusted setup here; this is not a scheduler conflict analysis. 59 def build(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,+namespace: U32,+name: String,+steps: List<&2,Step>,catalog: List<&2,U32>,owners: H) -> Build: 60 build_checked(~S,~C,~R,~E,~H,valid(steps,catalog,[]),namespace,name,steps,owners) 61 62 # Continuations keep recursion structurally decreasing on the plan, rather 63 # than moving affine owners into runtime closures stored in the schedule. 64 def after_outcome(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,next: H -> W.World -> List<&2,Observation> -> RunResult,outcome: Sys.Outcome,Error,Unit>,owners: H,+namespace: U32,+name: String,+all: List<&2,Step>,+observations: List<&2,Observation>,+id: U32) -> RunResult: 65 match outcome: 66 case Sys.Failed{world,error}: Failed{Schedule{namespace,name,all,owners},world,error,List.append(&2,Observation,observations,[Ran{id}])} 67 case Sys.Succeeded{world,_}: next(owners,world,List.append(&2,Observation,observations,[Ran{id}])) 68 69 def after_dispatch(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,next: H -> W.World -> List<&2,Observation> -> RunResult,result: H & Sys.Outcome,Error,Unit>,+namespace: U32,+name: String,+all: List<&2,Step>,+observations: List<&2,Observation>,+id: U32) -> RunResult: 70 (owners,outcome) = result 71 after_outcome(~S,~C,~R,~E,~H,~Error,next,outcome,owners,namespace,name,all,observations,id) 72 73 def conditioned(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,next: H -> W.World -> List<&2,Observation> -> RunResult,allowed: Bool,owners: H,world: W.World,+namespace: U32,+name: String,+all: List<&2,Step>,+observations: List<&2,Observation>,+id: U32) -> RunResult: 74 match allowed: 75 case False{}: next(owners,world,List.append(&2,Observation,observations,[Skipped{id}])) 76 case True{}: after_dispatch(~S,~C,~R,~E,~H,~Error,next,dispatch(owners,world,id),namespace,name,all,observations,id) 77 78 def after_condition(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,next: H -> W.World -> List<&2,Observation> -> RunResult,checked: W.World & Bool,owners: H,+namespace: U32,+name: String,+all: List<&2,Step>,+observations: List<&2,Observation>,+id: U32) -> RunResult: 79 (world,allowed) = checked 80 conditioned(~S,~C,~R,~E,~H,~Error,~dispatch,next,allowed,owners,world,namespace,name,all,observations,id) 81 82 def after_skip(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,next: H -> W.World -> List<&2,Observation> -> RunResult,returned: H & W.World,observations: List<&2,Observation>) -> RunResult: 83 (owners,world) = returned 84 next(owners,world,observations) 85 86 def default_barrier(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,owners:H,world:W.World) -> H & W.World: 87 (owners,W.barrier(~S,~C,~R,~E,world)) 88 def execute_with_barrier(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,~barrier:H -> W.World -> H & W.World,steps: List<&2,Step>,owners: H,world: W.World,+namespace: U32,+name: String,+all: List<&2,Step>,+observations: List<&2,Observation>) -> RunResult: 89 match steps: 90 case []: Finished{Schedule{namespace,name,all,owners},world,observations} 91 case Phase{phase} <> rest: execute_with_barrier(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~barrier,rest,owners,world,namespace,name,all,List.append(&2,Observation,observations,[Entered{phase}])) 92 case Barrier{} <> rest: after_skip(~S,~C,~R,~E,~H,~Error,h => w => obs => execute_with_barrier(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~barrier,rest,h,w,namespace,name,all,obs),barrier(owners,world),List.append(&2,Observation,observations,[Applied{}])) 93 case System{+id,slot} <> rest: after_condition(~S,~C,~R,~E,~H,~Error,~dispatch,h => w => obs => execute_with_barrier(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~barrier,rest,h,w,namespace,name,all,obs),condition(world,slot),owners,namespace,name,all,observations,id) 94 95 def execute(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,steps: List<&2,Step>,owners: H,world: W.World,+namespace: U32,+name: String,+all: List<&2,Step>,+observations: List<&2,Observation>) -> RunResult: 96 execute_with_barrier(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~default_barrier(~S,~C,~R,~E,~H),steps,owners,world,namespace,name,all,observations) 97 98 def run_checked(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,allowed: Bool,schedule: Schedule,world: W.World) -> RunResult: 99 match allowed schedule: 100 case False{} schedule: Rejected{schedule,world} 101 case True{} Schedule{+namespace,+name,+steps,owners}: execute(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,steps,owners,world,namespace,name,steps,[]) 102 103 104 def run_actual(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,schedule: Schedule,world: W.World,+actual: U32) -> RunResult: 105 match schedule: 106 case Schedule{+namespace,+name,+steps,owners}: run_checked(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,U32.is_eq(namespace,actual),Schedule{namespace,name,steps,owners},world) 107 108 def run_namespace(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,schedule: Schedule,named: W.World & U32) -> RunResult: 109 (world,+actual) = named 110 run_actual(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,schedule,world,actual) 111 112 # Run static steps in order after matching Schedule and World namespaces. 113 # Evaluate condition by its slot; false records Skipped without dispatch. True 114 # threads the actual H through dispatch, stopping on Sys.Failed. Barriers call 115 # W.barrier; no implicit final barrier is added. Callbacks are trusted closed 116 # providers, not arbitrary capabilities inferred from ids. See ordinary App 117 # schedule setup in examples/scheduled-health/application.bend. 118 def run(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,schedule: Schedule,world: W.World) -> RunResult: 119 run_namespace(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,schedule,W.namespace(~S,~C,~R,~E,world)) 120 121 # Additive runner with owner-preserving condition-false reader lifecycle hook. 122 def conditioned_skip(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~skip: H -> W.World -> U32 -> H & W.World,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,next: H -> W.World -> List<&2,Observation> -> RunResult,allowed: Bool,owners: H,world: W.World,+namespace: U32,+name: String,+all: List<&2,Step>,+observations: List<&2,Observation>,+id: U32) -> RunResult: 123 match allowed: 124 case False{}: after_skip(~S,~C,~R,~E,~H,~Error,next,skip(owners,world,id),List.append(&2,Observation,observations,[Skipped{id}])) 125 case True{}: after_dispatch(~S,~C,~R,~E,~H,~Error,next,dispatch(owners,world,id),namespace,name,all,observations,id) 126 127 def after_condition_skip(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~skip: H -> W.World -> U32 -> H & W.World,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,next: H -> W.World -> List<&2,Observation> -> RunResult,checked: W.World & Bool,owners: H,+namespace: U32,+name: String,+all: List<&2,Step>,+observations: List<&2,Observation>,+id: U32) -> RunResult: 128 (world,allowed) = checked 129 conditioned_skip(~S,~C,~R,~E,~H,~Error,~skip,~dispatch,next,allowed,owners,world,namespace,name,all,observations,id) 130 131 def execute_skip(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~skip: H -> W.World -> U32 -> H & W.World,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,steps: List<&2,Step>,owners: H,world: W.World,+namespace: U32,+name: String,+all: List<&2,Step>,+observations: List<&2,Observation>) -> RunResult: 132 match steps: 133 case []: Finished{Schedule{namespace,name,all,owners},world,observations} 134 case Phase{phase} <> rest: execute_skip(~S,~C,~R,~E,~H,~Error,~skip,~dispatch,~condition,rest,owners,world,namespace,name,all,List.append(&2,Observation,observations,[Entered{phase}])) 135 case Barrier{} <> rest: execute_skip(~S,~C,~R,~E,~H,~Error,~skip,~dispatch,~condition,rest,owners,W.barrier(~S,~C,~R,~E,world),namespace,name,all,List.append(&2,Observation,observations,[Applied{}])) 136 case System{+id,slot} <> rest: after_condition_skip(~S,~C,~R,~E,~H,~Error,~skip,~dispatch,h => w => obs => execute_skip(~S,~C,~R,~E,~H,~Error,~skip,~dispatch,~condition,rest,h,w,namespace,name,all,obs),condition(world,slot),owners,namespace,name,all,observations,id) 137 138 def run_checked_skip(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~skip: H -> W.World -> U32 -> H & W.World,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,allowed: Bool,schedule: Schedule,world: W.World) -> RunResult: 139 match allowed schedule: 140 case False{} schedule: Rejected{schedule,world} 141 case True{} Schedule{+namespace,+name,+steps,owners}: execute_skip(~S,~C,~R,~E,~H,~Error,~skip,~dispatch,~condition,steps,owners,world,namespace,name,steps,[]) 142 143 144 def run_actual_skip(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~skip: H -> W.World -> U32 -> H & W.World,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,schedule: Schedule,world: W.World,+actual: U32) -> RunResult: 145 match schedule: 146 case Schedule{+namespace,+name,+steps,owners}: run_checked_skip(~S,~C,~R,~E,~H,~Error,~skip,~dispatch,~condition,U32.is_eq(namespace,actual),Schedule{namespace,name,steps,owners},world) 147 148 def run_namespace_skip(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~skip: H -> W.World -> U32 -> H & W.World,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,schedule: Schedule,named: W.World & U32) -> RunResult: 149 (world,+actual) = named 150 run_actual_skip(~S,~C,~R,~E,~H,~Error,~skip,~dispatch,~condition,schedule,world,actual) 151 152 # As run, but a false condition invokes skip(owners,world,id) before continuing. 153 # Use the provider-defined hook for skipped-reader lifecycle; the hook itself 154 # returns both actual owners and World and is not assumed to be a pure no-op. 155 def run_with_skip(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,~Error: Data,~skip: H -> W.World -> U32 -> H & W.World,~dispatch: H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition: W.World -> U32 -> W.World & Bool,schedule: Schedule,world: W.World) -> RunResult: 156 run_namespace_skip(~S,~C,~R,~E,~H,~Error,~skip,~dispatch,~condition,schedule,W.namespace(~S,~C,~R,~E,world)) 157 158 def run_barrier_checked(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Error:Data,~dispatch:H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition:W.World -> U32 -> W.World & Bool,~barrier:H -> W.World -> H & W.World,allowed:Bool,schedule:Schedule,world:W.World) -> RunResult: 159 match allowed schedule: 160 case False{} schedule:Rejected{schedule,world} 161 case True{} Schedule{+namespace,+name,+steps,owners}:execute_with_barrier(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~barrier,steps,owners,world,namespace,name,steps,[]) 162 def run_barrier_actual(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Error:Data,~dispatch:H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition:W.World -> U32 -> W.World & Bool,~barrier:H -> W.World -> H & W.World,schedule:Schedule,world:W.World,+actual:U32) -> RunResult: 163 match schedule: 164 case Schedule{+namespace,+name,+steps,owners}:run_barrier_checked(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~barrier,U32.is_eq(namespace,actual),Schedule{namespace,name,steps,owners},world) 165 def run_barrier_named(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Error:Data,~dispatch:H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition:W.World -> U32 -> W.World & Bool,~barrier:H -> W.World -> H & W.World,schedule:Schedule,named:W.World & U32) -> RunResult: 166 match named: 167 case (world,actual):run_barrier_actual(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~barrier,schedule,world,actual) 168 # As namespace-checked run, with a caller-supplied barrier threading H and World 169 # at each explicit Barrier step. This supports owned runtime synchronization; 170 # its effects are trusted and no automatic final barrier or schedule rollback 171 # is added. Condition-false handling is the ordinary skip-without-dispatch path. 172 def run_with_barrier(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Error:Data,~dispatch:H -> W.World -> U32 -> H & Sys.Outcome,Error,Unit>,~condition:W.World -> U32 -> W.World & Bool,~barrier:H -> W.World -> H & W.World,schedule:Schedule,world:W.World) -> RunResult: 173 run_barrier_named(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~barrier,schedule,W.namespace(~S,~C,~R,~E,world))