1 import Base 2 import ./clock_state.bend as Clock 3 import ./deferred_command.bend as Deferred 4 import entity_key.bend as Key 5 import entity_allocator.bend as Alloc 6 import ./world.bend as W 7 import ./column.bend as Col 8 import ./component.bend as C 9 import ./lifecycle.bend as Life 10 # Canonical single-column provider. No caller-authored take/put/project. 11 type Tag is Data:Tag{} 12 def take(~S:Data,~P:Type,column:Col.Column) -> Unit & Col.Column:(Unit{},column) 13 def put(~S:Data,~P:Type,unit:Unit,column:Col.Column) -> Col.Column:column 14 def project(~P:Type,payload:P) -> P & Unit:(payload,Unit{}) 15 type Record is Type: 16 Record{handle:W.Handle,stamp:Life.Stamp,payload:Maybe

} 17 type Replacement is Type: 18 Applied{world:World,previous:Record} 19 Refused{world:World,incoming:Maybe

,error:W.WorldError} 20 def family(~S:Data,~P:Type,~R:Type,~E:Data,name:String) -> C.Family,Unit,P,Unit,Tag,take(~S,~P),put(~S,~P),project(~P)>: 21 C.Family{name} 22 def replaced(~S:Data,~P:Type,~R:Type,~E:Data,handle:W.Handle,stamp:Life.Stamp,result:C.Replacement,R,E>,P>) -> Replacement,R,E>,S,P>: 23 match result: 24 case C.Replaced{world,previous}:Applied{world,Record{handle,stamp,previous}} 25 case C.ReplaceRejected{world,incoming,error}:Refused{world,incoming,error} 26 def stamped(~S:Data,~P:Type,~R:Type,~E:Data,name:String,+handle:W.Handle,incoming:Maybe

,observed:W.World,R,E> & Life.Stamp) -> Replacement,R,E>,S,P>: 27 match observed: 28 case (world,stamp):replaced(~S,~P,~R,~E,handle,stamp,C.replace(~S,~Col.Column,~R,~E,~Unit,~P,~Unit,~Tag,~take(~S,~P),~put(~S,~P),~project(~P),family(~S,~P,~R,~E,name),world,handle,incoming)) 29 def replace(~S:Data,~P:Type,~R:Type,~E:Data,+name:String,world:W.World,R,E>,+handle:W.Handle,incoming:Maybe

) -> Replacement,R,E>,S,P>: 30 stamped(~S,~P,~R,~E,name,handle,incoming,C.lifecycle(~S,~Col.Column,~R,~E,~Unit,~P,~Unit,~Tag,~take(~S,~P),~put(~S,~P),~project(~P),family(~S,~P,~R,~E,name),world,handle)) 31 def restored(~S:Data,~P:Type,+handle:W.Handle,stamp:Life.Stamp,released:Life.Stamp,result:Col.SwapOutcome) -> Col.Column & Record: 32 match handle result: 33 case W.Handle{_,Key.EntityKey{id,_}},Col.Accepted{column,payload}:(Col.restore_stamp(~S,~P,column,id,stamp),Record{handle,released,payload}) 34 # For a saved ordinary record, ensure covers its positive live id, hence 35 # swap accepts. Raw rejection returns the untouched incoming owner/record; 36 # it does not claim that a different payload was displaced. 37 case W.Handle{_,Key.EntityKey{id,_}},Col.Rejected{column,payload}:(column,Record{handle,stamp,payload}) 38 def undo_stamped(~S:Data,~P:Type,+handle:W.Handle,stamp:Life.Stamp,payload:Maybe

,observed:Col.Column & Life.Stamp) -> Col.Column & Record: 39 match handle observed: 40 case W.Handle{_,Key.EntityKey{+id,_}}, (column,released):restored(~S,~P,handle,stamp,released,Col.swap(~S,~P,Col.ensure(~S,~P,column,id),id,payload)) 41 def undo_column(~S:Data,~P:Type,+handle:W.Handle,stamp:Life.Stamp,payload:Maybe

,column:Col.Column) -> Col.Column & Record: 42 match handle: 43 case W.Handle{_,Key.EntityKey{id,_}}:undo_stamped(~S,~P,handle,stamp,payload,Col.lifecycle(~S,~P,column,id)) 44 def undone(~S:Data,~P:Type,~R:Type,~E:Data,ns:U32,allocator:Alloc.Allocator,resource:R,events:List<&2,E>,pending:List,R,E>>>,regs:List<&2,W.RegistrationMeta>,sid:U32,clock:Clock.State,result:Col.Column & Record) -> W.World,R,E> & Record: 45 match result: 46 case (column,record):(W.World{ns,allocator,column,resource,events,pending,regs,sid,clock},record) 47 # Trusted inverse: ordinary grants never alter allocation/live state or inject 48 # records. Canonical ensure preserves every cell and covers the saved live id; 49 # undo restores its exact stamp and returns the displaced owner with its own 50 # pre-undo stamp. World clock and every unrelated owner remain unchanged. 51 def undo_valid(~S:Data,~P:Type,~R:Type,~E:Data,record:Record,world:W.World,R,E>) -> W.World,R,E> & Record: 52 match record world: 53 case Record{handle,stamp,payload},W.World{ns,allocator,column,resource,events,pending,regs,sid,clock}: 54 undone(~S,~P,~R,~E,ns,allocator,resource,events,pending,regs,sid,clock,undo_column(~S,~P,handle,stamp,payload,column)) 55 56 # Invalid raw records remain intact; ordinary journal records use the accepted 57 # path because component operations cannot change namespace or entity liveness. 58 def undo_checked(~S:Data,~P:Type,~R:Type,~E:Data,record:Record,checked:W.World,R,E> & Bool) -> W.World,R,E> & Record: 59 match checked: 60 case (world,False{}):(world,record) 61 case (world,True{}):undo_valid(~S,~P,~R,~E,record,world) 62 def undo(~S:Data,~P:Type,~R:Type,~E:Data,record:Record,world:W.World,R,E>) -> W.World,R,E> & Record: 63 match record: 64 case Record{+handle,stamp,payload}:undo_checked(~S,~P,~R,~E,Record{handle,stamp,payload},W.valid(~S,~Col.Column,~R,~E,world,handle))