ordinary_app_schedules.bend

bendvy-ecs-core@0.1.0.0 · 160 lines · 0 definitions

1import Base
2import bendvy-ecs-core@0.1.0.0/ordinary_app_access.bend as Access
3import bendvy-ecs-core@0.1.0.0/ordinary_app_schedule_access.bend as SelectedAccess
4import bendvy-ecs-core@0.1.0.0/ordinary_schema.bend as Schema
5import bendvy-ecs-core@0.1.0.0/ordinary_app_recipe.bend as Tree
6import bendvy-ecs-core@0.1.0.0/ordinary_app_schema.bend as App
7import bendvy-ecs-core@0.1.0.0/ordinary_app_composer.bend as Build
8import bendvy-ecs-core@0.1.0.0/ordinary_app_registration_fields.bend as Records
9import bendvy-ecs-core@0.1.0.0/ordinary_app_observation_data.bend as DTO
10import bendvy-ecs-core@0.1.0.0/ordinary_app_schedule.bend as Single
11import bendvy-ecs-core@0.1.0.0/ordinary_app_schedule_description.bend as Detail
12import bendvy-ecs-core@0.1.0.0/world.bend as W
13import bendvy-ecs-core@0.1.0.0/system.bend as Sys
14import bendvy-ecs-core@0.1.0.0/schedule.bend as Sch
15import bendvy-ecs-core@0.1.0.0/schedule_provision.bend as SP
16import bendvy-ecs-core@0.1.0.0/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.
20type Named<S:Data> is Data:
21  Named{label:String,plan:SP.Plan<S>}
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.
25type Application<S:Data,C:Type,R:Type,E:Data,H:Type,Rows:Data,-observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,-fold:H -> H & List<&2,Records.Fields<S>>> is Type:
26  Application{app:App.Application<S,C,R,E,H,Rows,observe,fold>,plans:List<&2,Named<S>>}
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.
29type Insertion<-A:Type,-S:Data> is Type:
30  Inserted{app:A,previous:Maybe<&2,SP.Plan<S>>}
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.
34type RunResult<-A:Type,-S:Data,-Error:Data> is Type:
35  Executed{result:Single.RunResult<A,S,Error>}
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.
39type Description<S:Data,Rows:Data> is Data:
40  Description{application:App.Description<S,Rows>,plans:List<&2,Detail.Description<S,Rows>>}
41def lookup_next(~S:Data,found:Bool,plan:SP.Plan<S>,next:Maybe<&2,SP.Plan<S>>) -> Maybe<&2,SP.Plan<S>>:
42  match found:
43    case True{}:Some{plan}
44    case False{}:next
45def lookup(~S:Data,+label:String,plans:List<&2,Named<S>>) -> Maybe<&2,SP.Plan<S>>:
46  match plans:
47    case []:None{}
48    case Named{name,plan} <> rest:lookup_next(~S,Text.equal(label,name),plan,lookup(~S,label,rest))
49def replace_next(~S:Data,same:Bool,label:String,plan:SP.Plan<S>,old:Named<S>,rest:List<&2,Named<S>>,next:List<&2,Named<S>>) -> List<&2,Named<S>>:
50  match same:
51    case True{}:Named{label,plan} <> rest
52    case False{}:old <> next
53def replace(~S:Data,+label:String,+plan:SP.Plan<S>,plans:List<&2,Named<S>>) -> List<&2,Named<S>>:
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))
57def remove_next(~S:Data,same:Bool,old:Named<S>,rest:List<&2,Named<S>>,next:List<&2,Named<S>>) -> List<&2,Named<S>>:
58  match same:
59    case True{}:rest
60    case False{}:old <> next
61def without(~S:Data,+label:String,plans:List<&2,Named<S>>) -> List<&2,Named<S>>:
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.
67def create(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,app:App.Application<S,C,R,E,H,Rows,observe,fold>) -> Application<S,C,R,E,H,Rows,observe,fold>:
68  Application{app,[]}
69# Return App and the exact stored plan for label, or None; does not attach/run it.
70def get(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,value:Application<S,C,R,E,H,Rows,observe,fold>,+label:String) -> Application<S,C,R,E,H,Rows,observe,fold> & Maybe<&2,SP.Plan<S>>:
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.
75def remove(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,value:Application<S,C,R,E,H,Rows,observe,fold>,+label:String) -> Application<S,C,R,E,H,Rows,observe,fold> & Maybe<&2,SP.Plan<S>>:
76  match value:
77    case Application{app,+plans}:(Application{app,without(~S,label,plans)},lookup(~S,label,plans))
78def inserted(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,+plans:List<&2,Named<S>>,+label:String,+plan:SP.Plan<S>,result:Single.Attachment<App.Application<S,C,R,E,H,Rows,observe,fold>,Single.Application<S,C,R,E,H,Rows,observe,fold>>) -> Insertion<Application<S,C,R,E,H,Rows,observe,fold>,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.
87def insert(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,value:Application<S,C,R,E,H,Rows,observe,fold>,+label:String,+plan:SP.Plan<S>) -> Insertion<Application<S,C,R,E,H,Rows,observe,fold>,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))
90def restored(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,~Error:Data,plans:List<&2,Named<S>>,result:Single.RunResult<Single.Application<S,C,R,E,H,Rows,observe,fold>,S,Error>) -> RunResult<Application<S,C,R,E,H,Rows,observe,fold>,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}}
96def attached(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,~Error:Data,~dispatch:H -> W.World<S,C,R,E> -> U32 -> H & Sys.Outcome<W.World<S,C,R,E>,Error,Unit>,~condition:W.World<S,C,R,E> -> U32 -> W.World<S,C,R,E> & Bool,~provision:W.World<S,C,R,E> -> W.World<S,C,R,E> & List<&2,SP.Requirement<S>>,plans:List<&2,Named<S>>,result:Single.Attachment<App.Application<S,C,R,E,H,Rows,observe,fold>,Single.Application<S,C,R,E,H,Rows,observe,fold>>) -> RunResult<Application<S,C,R,E,H,Rows,observe,fold>,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))
100def selected(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,~Error:Data,~dispatch:H -> W.World<S,C,R,E> -> U32 -> H & Sys.Outcome<W.World<S,C,R,E>,Error,Unit>,~condition:W.World<S,C,R,E> -> U32 -> W.World<S,C,R,E> & Bool,~provision:W.World<S,C,R,E> -> W.World<S,C,R,E> & List<&2,SP.Requirement<S>>,app:App.Application<S,C,R,E,H,Rows,observe,fold>,plans:List<&2,Named<S>>,label:String,found:Maybe<&2,SP.Plan<S>>) -> RunResult<Application<S,C,R,E,H,Rows,observe,fold>,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.
110def run(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,~Error:Data,~dispatch:H -> W.World<S,C,R,E> -> U32 -> H & Sys.Outcome<W.World<S,C,R,E>,Error,Unit>,~condition:W.World<S,C,R,E> -> U32 -> W.World<S,C,R,E> & Bool,~provision:W.World<S,C,R,E> -> W.World<S,C,R,E> & List<&2,SP.Requirement<S>>,value:Application<S,C,R,E,H,Rows,observe,fold>,+label:String) -> RunResult<Application<S,C,R,E,H,Rows,observe,fold>,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.
114def enabled(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,value:Application<S,C,R,E,H,Rows,observe,fold>,flag:Bool) -> Application<S,C,R,E,H,Rows,observe,fold>:
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.
119def descriptions(~S:Data,~Rows:Data,+view:App.Description<S,Rows>,+namespace:U32,plans:List<&2,Named<S>>) -> List<&2,Detail.Description<S,Rows>>:
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)
123def observed(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,+plans:List<&2,Named<S>>,namespace:U32,result:App.Application<S,C,R,E,H,Rows,observe,fold> & Maybe<&2,App.Description<S,Rows>>) -> Application<S,C,R,E,H,Rows,observe,fold> & Maybe<&2,Description<S,Rows>>:
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)}})
127def cataloged_snapshot(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,schema:Schema.Schema<S,C,R>,owners:H,recipe:Tree.Recipe<W.World<S,C,R,E>,Rows,observe>,debug:Bool,plans:List<&2,Named<S>>,result:W.World<S,C,R,E> & U32) -> Application<S,C,R,E,H,Rows,observe,fold> & Maybe<&2,Description<S,Rows>>:
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.
133def snapshot(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,value:Application<S,C,R,E,H,Rows,observe,fold>) -> Application<S,C,R,E,H,Rows,observe,fold> & Maybe<&2,Description<S,Rows>>:
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.
138type 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.
142type Index is Data:
143  Index{namespace:U32,inventory:List<&2,Access.Entry>,plans:List<&2,PlanIndex>}
144def plan_indexes(~S:Data,+namespace:U32,+records:List<&2,Records.Fields<S>>,plans:List<&2,Named<S>>) -> 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)
148def indexed(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,schema:Schema.Schema<S,C,R>,world:W.World<S,C,R,E>,recipe:Tree.Recipe<W.World<S,C,R,E>,Rows,observe>,+plans:List<&2,Named<S>>,+namespace:U32,result:H & List<&2,Records.Fields<S>>) -> Application<S,C,R,E,H,Rows,observe,fold> & 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)}})
151def index_named(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,schema:Schema.Schema<S,C,R>,owners:H,recipe:Tree.Recipe<W.World<S,C,R,E>,Rows,observe>,plans:List<&2,Named<S>>,result:W.World<S,C,R,E> & U32) -> Application<S,C,R,E,H,Rows,observe,fold> & 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.
157def index(~S:Data,~C:Type,~R:Type,~E:Data,~H:Type,~Rows:Data,~observe:W.World<S,C,R,E> -> W.World<S,C,R,E> & DTO.Observation<Rows>,~fold:H -> H & List<&2,Records.Fields<S>>,value:Application<S,C,R,E,H,Rows,observe,fold>) -> Application<S,C,R,E,H,Rows,observe,fold> & 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))