1 import Base 2 import ./ordinary_app_access.bend as Access 3 import ./ordinary_app_schedule_access.bend as SelectedAccess 4 import ./ordinary_schema.bend as Schema 5 import ./ordinary_app_recipe.bend as Tree 6 import ./ordinary_app_schema.bend as App 7 import ./ordinary_app_composer.bend as Build 8 import ./ordinary_app_registration_fields.bend as Records 9 import ./ordinary_app_observation_data.bend as DTO 10 import ./ordinary_app_schedule.bend as Single 11 import ./ordinary_app_schedule_description.bend as Detail 12 import ./world.bend as W 13 import ./system.bend as Sys 14 import ./schedule.bend as Sch 15 import ./schedule_provision.bend as SP 16 import ./string_equality.bend as Text 17 # Named plans are Data; one App retains the World and registered owners. 18 # Detached label and operational plan. Labels select plans in one App; they do 19 # not own or duplicate the registered systems referenced by those plans. 20 type Named is Data: 21 Named{label:String,plan:SP.Plan} 22 # One schema App and actual World/registration owners shared by all named plans. 23 # Plans are Data; running or replacing one does not allocate an independent Local 24 # or capture state for each name. 25 type Application -> W.World & DTO.Observation,-fold:H -> H & List<&2,Records.Fields>> is Type: 26 Application{app:App.Application,plans:List<&2,Named>} 27 # Insert/replace result returning App in both branches. Inserted returns the 28 # previous plan for that label (None for a new label); Invalid preserves old plans. 29 type Insertion<-A:Type,-S:Data> is Type: 30 Inserted{app:A,previous:Maybe<&2,SP.Plan>} 31 Invalid{app:A} 32 # Named lookup result: Unknown returns App without execution. Executed contains 33 # the complete single-schedule result, including retained App on failure/refusal. 34 type RunResult<-A:Type,-S:Data,-Error:Data> is Type: 35 Executed{result:Single.RunResult} 36 Unknown{app:A} 37 # Detached shared application description plus ordered named-plan placements. 38 # This is observation metadata, not a second registry or executable owner bundle. 39 type Description is Data: 40 Description{application:App.Description,plans:List<&2,Detail.Description>} 41 def lookup_next(~S:Data,found:Bool,plan:SP.Plan,next:Maybe<&2,SP.Plan>) -> Maybe<&2,SP.Plan>: 42 match found: 43 case True{}:Some{plan} 44 case False{}:next 45 def lookup(~S:Data,+label:String,plans:List<&2,Named>) -> Maybe<&2,SP.Plan>: 46 match plans: 47 case []:None{} 48 case Named{name,plan} <> rest:lookup_next(~S,Text.equal(label,name),plan,lookup(~S,label,rest)) 49 def replace_next(~S:Data,same:Bool,label:String,plan:SP.Plan,old:Named,rest:List<&2,Named>,next:List<&2,Named>) -> List<&2,Named>: 50 match same: 51 case True{}:Named{label,plan} <> rest 52 case False{}:old <> next 53 def replace(~S:Data,+label:String,+plan:SP.Plan,plans:List<&2,Named>) -> List<&2,Named>: 54 match plans: 55 case []:[Named{label,plan}] 56 case Named{+name,+old} <> +rest:replace_next(~S,Text.equal(label,name),label,plan,Named{name,old},rest,replace(~S,label,plan,rest)) 57 def remove_next(~S:Data,same:Bool,old:Named,rest:List<&2,Named>,next:List<&2,Named>) -> List<&2,Named>: 58 match same: 59 case True{}:rest 60 case False{}:old <> next 61 def without(~S:Data,+label:String,plans:List<&2,Named>) -> List<&2,Named>: 62 match plans: 63 case []:[] 64 case Named{+name,+plan} <> +rest:remove_next(~S,Text.equal(label,name),Named{name,plan},rest,without(~S,label,rest)) 65 # Wrap an existing schema App with an empty named-plan list. Does not register 66 # or dispose systems and does not enable debug. 67 def create(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,app:App.Application) -> Application: 68 Application{app,[]} 69 # Return App and the exact stored plan for label, or None; does not attach/run it. 70 def get(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,value:Application,+label:String) -> Application & Maybe<&2,SP.Plan>: 71 match value: 72 case Application{app,+plans}:(Application{app,plans},lookup(~S,label,plans)) 73 # Remove the named Data plan and return App plus its previous plan if present. 74 # Registered owners remain alive and shared; removal does not dispose a system. 75 def remove(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,value:Application,+label:String) -> Application & Maybe<&2,SP.Plan>: 76 match value: 77 case Application{app,+plans}:(Application{app,without(~S,label,plans)},lookup(~S,label,plans)) 78 def inserted(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,+plans:List<&2,Named>,+label:String,+plan:SP.Plan,result:Single.Attachment,Single.Application>) -> Insertion,S>: 79 match result: 80 case Single.Invalid{app}:Invalid{Application{app,plans}} 81 case Single.Attached{Single.Application{app,_,_,_,_,_}}:Inserted{Application{app,replace(~S,label,plan,plans)},lookup(~S,label,plans)} 82 # Validate plan attachment against the actual World registration catalog. Valid 83 # replacement retains the label's position; a new label appends. Return previous 84 # plan on success; invalid attachment leaves named plans unchanged. Does not run 85 # the plan, reset Local/capture owners or accept duplicate/unknown system ids. 86 # See test-fixtures/public-schedules/named-app-v1/matched-feature-v1/main.bend. 87 def insert(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,value:Application,+label:String,+plan:SP.Plan) -> Insertion,S>: 88 match value: 89 case Application{app,plans}:inserted(~S,~C,~R,~E,~H,~Rows,~observe,~fold,plans,label,plan,Single.attach(~S,~C,~R,~E,~H,~Rows,~observe,~fold,app,label,plan)) 90 def restored(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,~Error:Data,plans:List<&2,Named>,result:Single.RunResult,S,Error>) -> RunResult,S,Error>: 91 match result: 92 case Single.Finished{Single.Application{app,_,_,_,_,_},observations}:Executed{Single.Finished{Application{app,plans},observations}} 93 case Single.Failed{Single.Application{app,_,_,_,_,_},error,observations}:Executed{Single.Failed{Application{app,plans},error,observations}} 94 case Single.Rejected{Single.Application{app,_,_,_,_,_}}:Executed{Single.Rejected{Application{app,plans}}} 95 case Single.Missing{Single.Application{app,_,_,_,_,_},requirements}:Executed{Single.Missing{Application{app,plans},requirements}} 96 def attached(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,~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,SP.Requirement>,plans:List<&2,Named>,result:Single.Attachment,Single.Application>) -> RunResult,S,Error>: 97 match result: 98 case Single.Invalid{app}:Executed{Single.Rejected{Application{app,plans}}} 99 case Single.Attached{app}:restored(~S,~C,~R,~E,~H,~Rows,~observe,~fold,~Error,plans,Single.run(~S,~C,~R,~E,~H,~Rows,~observe,~fold,~Error,~dispatch,~condition,~provision,app)) 100 def selected(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,~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,SP.Requirement>,app:App.Application,plans:List<&2,Named>,label:String,found:Maybe<&2,SP.Plan>) -> RunResult,S,Error>: 101 match found: 102 case None{}:Unknown{Application{app,plans}} 103 case Some{plan}:attached(~S,~C,~R,~E,~H,~Rows,~observe,~fold,~Error,~dispatch,~condition,~provision,plans,Single.attach(~S,~C,~R,~E,~H,~Rows,~observe,~fold,app,label,plan)) 104 # Look up label, reattach its plan against the current catalog, then run through 105 # the supplied single-schedule dispatch/condition/provision adapters. Unknown runs 106 # nothing; attachment rejection becomes Executed(Rejected). Restore the same named 107 # plan list around every run result, retaining actual returned World and shared 108 # runner owners. Prior successful systems are not rolled back as one named run. 109 # Callbacks remain trusted setup; barriers occur only at explicit plan steps. 110 def run(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,~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,SP.Requirement>,value:Application,+label:String) -> RunResult,S,Error>: 111 match value: 112 case Application{app,+plans}:selected(~S,~C,~R,~E,~H,~Rows,~observe,~fold,~Error,~dispatch,~condition,~provision,app,plans,label,lookup(~S,label,plans)) 113 # Set shared App observation enablement, preserving all named plans and owners. 114 def enabled(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,value:Application,flag:Bool) -> Application: 115 match value: 116 case Application{app,plans}:Application{App.enabled(~S,~C,~R,~E,~H,~Rows,~observe,~fold,app,flag),plans} 117 # All registered bindings remain in application; each placement references the 118 # same derived declaration inventory. Disabled App.snapshot returns before work. 119 def descriptions(~S:Data,~Rows:Data,+view:App.Description,+namespace:U32,plans:List<&2,Named>) -> List<&2,Detail.Description>: 120 match plans: 121 case []:[] 122 case Named{label,+plan} <> rest:Detail.described(~S,~Rows,Single.Description{view,namespace,label,Single.planned(~S,plan)}) <> descriptions(~S,~Rows,view,namespace,rest) 123 def observed(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,+plans:List<&2,Named>,namespace:U32,result:App.Application & Maybe<&2,App.Description>) -> Application & Maybe<&2,Description>: 124 match result: 125 case (app,None{}):(Application{app,plans},None{}) 126 case (app,Some{+view}):(Application{app,plans},Some{Description{view,descriptions(~S,~Rows,view,namespace,plans)}}) 127 def cataloged_snapshot(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,schema:Schema.Schema,owners:H,recipe:Tree.Recipe,Rows,observe>,debug:Bool,plans:List<&2,Named>,result:W.World & U32) -> Application & Maybe<&2,Description>: 128 match result: 129 case (world,namespace):observed(~S,~C,~R,~E,~H,~Rows,~observe,~fold,plans,namespace,App.snapshot(~S,~C,~R,~E,~H,~Rows,~observe,~fold,App.Application{schema,Build.Registered{world,owners,recipe,debug}})) 130 # Return App with optional combined application/named-plan descriptions. Disabled 131 # returns None before fold/observe/placement work. Enabled uses one derived binding 132 # inventory for every placement; no system runs or command barrier is applied. 133 def snapshot(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,value:Application) -> Application & Maybe<&2,Description>: 134 match value: 135 case Application{App.Application{schema,Build.Registered{world,owners,recipe,False{}}},plans}:(Application{App.Application{schema,Build.Registered{world,owners,recipe,False{}}},plans},None{}) 136 case Application{App.Application{schema,Build.Registered{world,owners,recipe,True{}}},plans}:cataloged_snapshot(~S,~C,~R,~E,~H,~Rows,~observe,~fold,schema,owners,recipe,True{},plans,W.namespace(~S,~C,~R,~E,world)) 137 # Detached grouped declared accesses for one stored label, selected by its plan. 138 type PlanIndex is Data: 139 PlanIndex{label:String,entries:List<&2,Access.Entry>} 140 # Detached namespace, complete registration access inventory and named indexes. 141 # Inventory includes unplaced registrations; plans reference the shared owners. 142 type Index is Data: 143 Index{namespace:U32,inventory:List<&2,Access.Entry>,plans:List<&2,PlanIndex>} 144 def plan_indexes(~S:Data,+namespace:U32,+records:List<&2,Records.Fields>,plans:List<&2,Named>) -> List<&2,PlanIndex>: 145 match plans: 146 case []:[] 147 case Named{label,plan} <> rest:PlanIndex{label,Access.grouped(Access.fields(~S,SelectedAccess.placed(~S,Single.planned(~S,plan),namespace,records)),[])} <> plan_indexes(~S,namespace,records,rest) 148 def indexed(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,schema:Schema.Schema,world:W.World,recipe:Tree.Recipe,Rows,observe>,+plans:List<&2,Named>,+namespace:U32,result:H & List<&2,Records.Fields>) -> Application & Maybe<&2,Index>: 149 match result: 150 case (owners,+records):(Application{App.Application{schema,Build.Registered{world,owners,recipe,True{}}},plans},Some{Index{namespace,Access.grouped(Access.fields(~S,records),[]),plan_indexes(~S,namespace,records,plans)}}) 151 def index_named(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,schema:Schema.Schema,owners:H,recipe:Tree.Recipe,Rows,observe>,plans:List<&2,Named>,result:W.World & U32) -> Application & Maybe<&2,Index>: 152 match result: 153 case (world,namespace):indexed(~S,~C,~R,~E,~H,~Rows,~observe,~fold,schema,world,recipe,plans,namespace,fold(owners)) 154 # Return App and optional declared-access index. Disabled bypasses fold/index 155 # derivation. Enabled folds actual owners and selects each plan's namespace/id 156 # placements; this grants no execution authority and does not inspect payloads. 157 def index(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World -> W.World & DTO.Observation,~fold:H -> H & List<&2,Records.Fields>,value:Application) -> Application & Maybe<&2,Index>: 158 match value: 159 case Application{App.Application{schema,Build.Registered{world,owners,recipe,False{}}},plans}:(Application{App.Application{schema,Build.Registered{world,owners,recipe,False{}}},plans},None{}) 160 case Application{App.Application{schema,Build.Registered{world,owners,recipe,True{}}},plans}:index_named(~S,~C,~R,~E,~H,~Rows,~observe,~fold,schema,owners,recipe,plans,W.namespace(~S,~C,~R,~E,world))