1 import Base 2 import ./world.bend as W 3 import ./system.bend as Sys 4 import ./schedule.bend as Sch 5 6 # Schema authoring provisions nominal requirement tokens. Gameplay cannot turn 7 # these tokens into component/resource/service providers. Actual availability 8 # is observed from the owned application's provisioning record before any step. 9 type Requirement is Data: 10 Component{id: U32} 11 Resource{id: U32} 12 Service{id: U32} 13 # Authored left-to-right schedule tree with per-entry provisioning requirements. 14 # Empty contributes no steps; Sequence preserves both child orders. 15 type Plan is Data: 16 Empty{} 17 Entry{step: Sch.Step,requirements: List<&2,Requirement>} 18 Sequence{left: Plan,right: Plan} 19 # Affine schedule owner paired with its deduplicated setup requirements. 20 type Provisioned is Type: 21 Provisioned{schedule: Sch.Schedule,requirements: List<&2,Requirement>} 22 # Built retains the provisioned schedule; Invalid returns the supplied owners. 23 type Build is Type: 24 Built{schedule: Provisioned} 25 Invalid{owners: H} 26 # Missing returns schedule and observed World before any schedule step runs. 27 # Executed contains the underlying schedule result and original requirement list; 28 # inspect that result for success, rejection or system failure. 29 type ProvisionResult is Type: 30 Executed{result: Sch.RunResult,requirements: List<&2,Requirement>} 31 Missing{schedule: Provisioned,world: W.World,missing: List<&2,Requirement>} 32 33 def same(~S: Data,a: Requirement,b: Requirement) -> Bool: 34 match a b: 35 case Component{a} Component{b}: U32.is_eq(a,b) 36 case Resource{a} Resource{b}: U32.is_eq(a,b) 37 case Service{a} Service{b}: U32.is_eq(a,b) 38 case _ _: False{} 39 40 def contains(~S: Data,items: List<&2,Requirement>,+item: Requirement) -> Bool: 41 match items: 42 case []: False{} 43 case head <> rest: Bool.or(same(~S,head,item),contains(~S,rest,item)) 44 45 def unique_add(~S: Data,present: Bool,+item: Requirement,rest: List<&2,Requirement>) -> List<&2,Requirement>: 46 match present: 47 case True{}: rest 48 case False{}: item <> rest 49 50 def unique_seen(~S: Data,items: List<&2,Requirement>,+seen: List<&2,Requirement>) -> List<&2,Requirement>: 51 match items: 52 case []: [] 53 case +head <> rest: unique_add(~S,contains(~S,seen,head),head,unique_seen(~S,rest,head <> seen)) 54 55 def unique(~S: Data,items: List<&2,Requirement>) -> List<&2,Requirement>: 56 unique_seen(~S,items,[]) 57 58 def flatten(~S: Data,plan: Plan) -> List<&2,Sch.Step>: 59 match plan: 60 case Empty{}: [] 61 case Entry{step,_}: [step] 62 case Sequence{left,right}: List.append(&2,Sch.Step,flatten(~S,left),flatten(~S,right)) 63 64 # Collect authored requirements left before right, retaining duplicates. 65 # build deduplicates this list for its provisioned schedule. 66 def requirements(~S: Data,plan: Plan) -> List<&2,Requirement>: 67 match plan: 68 case Empty{}: [] 69 case Entry{_,needs}: needs 70 case Sequence{left,right}: List.append(&2,Requirement,requirements(~S,left),requirements(~S,right)) 71 72 def finish_build(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,built: Sch.Build,needs: List<&2,Requirement>) -> Build: 73 match built: 74 case Sch.Invalid{owners}: Invalid{owners} 75 case Sch.Built{schedule}: Built{Provisioned{schedule,needs}} 76 77 # Flatten the authored plan and validate system ids against the trusted catalog, 78 # rejecting duplicates/unknown systems with owners returned. Retain first-occurrence 79 # requirement order; namespace/catalog provenance and condition slots are setup. 80 def build(~S: Data,~C: Type,~R: Type,~E: Data,~H: Type,+namespace: U32,+name: String,+plan: Plan,catalog: List<&2,U32>,owners: H) -> Build: 81 finish_build(~S,~C,~R,~E,~H,Sch.build(~S,~C,~R,~E,~H,namespace,name,flatten(~S,plan),catalog,owners),unique(~S,requirements(~S,plan))) 82 83 def missing_next(~S: Data,present: Bool,item: Requirement,next: List<&2,Requirement>) -> List<&2,Requirement>: 84 match present: 85 case False{}: item <> next 86 case True{}: next 87 88 def missing_requirements(~S: Data,needs: List<&2,Requirement>,+available: List<&2,Requirement>) -> List<&2,Requirement>: 89 match needs: 90 case []: [] 91 case +head <> rest: missing_next(~S,contains(~S,available,head),head,missing_requirements(~S,rest,available)) 92 93 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,~barrier:H -> W.World -> H & W.World,missing: List<&2,Requirement>,schedule: Sch.Schedule,world: W.World,needs: List<&2,Requirement>) -> ProvisionResult: 94 match missing: 95 case first <> rest: Missing{Provisioned{schedule,needs},world,first <> rest} 96 case []: Executed{Sch.run_with_barrier(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~barrier,schedule,world),needs} 97 98 def run_available(~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,observed: W.World & List<&2,Requirement>,schedule: Sch.Schedule,+needs: List<&2,Requirement>) -> ProvisionResult: 99 (world,available) = observed 100 run_checked(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~barrier,missing_requirements(~S,needs,available),schedule,world,needs) 101 102 # Observe availability once through trusted provision before executing any step. 103 # Missing requirements return the observed World and owned schedule without 104 # dispatch; provision itself is not promised pure. Otherwise use schedule.bend 105 # execution semantics and the supplied explicit barrier action. No final flush 106 # or whole-schedule rollback is added; all result branches retain their owners. 107 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,~provision: W.World -> W.World & List<&2,Requirement>,schedule: Provisioned,world: W.World) -> ProvisionResult: 108 match schedule: 109 case Provisioned{schedule,needs}: run_available(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~barrier,provision(world),schedule,needs) 110 111 # Run with provisioning admission and the normal World barrier at explicit 112 # Barrier steps. As with run_with_barrier, completion does not imply a final flush. 113 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,~provision: W.World -> W.World & List<&2,Requirement>,schedule: Provisioned,world: W.World) -> ProvisionResult: 114 run_with_barrier(~S,~C,~R,~E,~H,~Error,~dispatch,~condition,~Sch.default_barrier(~S,~C,~R,~E,~H),~provision,schedule,world)