1 import Base 2 import ./change_tick.bend as Tick 3 import ./event_runtime.bend as Ev 4 import ./relation_commands.bend as A 5 import ./relation_types.bend as G 6 import ./relation_graph.bend as Graph 7 import ./capabilities.bend as Cap 8 import ./inspector_stream_frame.bend as F 9 10 # Selects a relation failure projection by descriptor key. The descriptor must 11 # belong to the application schema; this raw declaration does not validate it. 12 type Declaration is Data: 13 Declaration{relation:G.Descriptor} 14 # This declaration-scoped grant exposes selected failures and non-payload 15 # timing only. Shared Notice payloads and reader counts stay in the owner harness. 16 type FailureView is Data: 17 FailureView{relation:G.Descriptor,visible:List<&2,G.Failure>,retained:List<&2,G.Failure>,tick:Nat,boundary:Nat} 18 def selected(~S:Data,keep:Bool,failure:G.Failure,rest:List<&2,G.Failure>) -> List<&2,G.Failure>: 19 match keep: 20 case False{}:rest 21 case True{}:failure <> rest 22 def select_failure(~S:Data,+relation:G.Descriptor,+failure:G.Failure,rest:List<&2,G.Failure>) -> List<&2,G.Failure>: 23 match failure: 24 case G.Failure{+actual,operation,source,target,error}:selected(~S,U32.is_eq(Graph.key(~S,actual),Graph.key(~S,relation)),G.Failure{actual,operation,source,target,error},rest) 25 def select_notice(~S:Data,~E:Data,relation:G.Descriptor,notice:A.Notice,rest:List<&2,G.Failure>) -> List<&2,G.Failure>: 26 match notice: 27 case A.RelationFailed{failure}:select_failure(~S,relation,failure,rest) 28 case _:rest 29 # Extract relation failures matching the descriptor key, preserving notice order. 30 # Other notice variants are omitted; no payload owners or reader positions change. 31 def failures(~S:Data,~E:Data,+relation:G.Descriptor,notices:List<&2,A.Notice>) -> List<&2,G.Failure>: 32 match notices: 33 case []:[] 34 case notice <> rest:select_notice(~S,~E,relation,notice,failures(~S,~E,relation,rest)) 35 # Project visible and retained failures separately from a detached snapshot. 36 # Preserve its tick and retention boundary; this does not acknowledge a reader. 37 def view(~S:Data,~E:Data,+relation:G.Descriptor,snapshot:Ev.Snapshot>) -> FailureView: 38 match snapshot: 39 case Ev.Snapshot{visible,retained,_,tick,boundary}:FailureView{relation,failures(~S,~E,relation,visible),failures(~S,~E,relation,retained),tick,boundary} 40 def joined(~S:Data,~C:Type,~R:Type,~E:Data,relation:G.Descriptor,cursor:Maybe<&2,Tick.Tick>,observed:Ev.Runtime> & Ev.Snapshot>) -> F.Frame & FailureView: 41 (runtime,snapshot)=observed 42 (F.Frame{runtime,cursor},view(~S,~E,relation,snapshot)) 43 # Return the runtime frame with a detached failure view. Preserve the Inspector 44 # cursor and runtime reader positions; inspection does not drain retained notices. 45 def read(~S:Data,~C:Type,~R:Type,~E:Data,relation:G.Descriptor,frame:F.Frame) -> F.Frame & FailureView: 46 match frame: 47 case F.Frame{runtime,cursor}:joined(~S,~C,~R,~E,relation,cursor,Ev.inspect(~S,~C,~R,~A.Notice,runtime)) 48 # Recover the descriptor carried by this declaration, without validation. 49 def descriptor(~S:Data,declaration:Declaration) -> G.Descriptor: 50 match declaration: 51 case Declaration{relation}:relation 52 # Build the read capability for this declaration. It exposes only selected 53 # failures and timing metadata, returning the affine frame after inspection. 54 def inspect(~S:Data,~C:Type,~R:Type,~E:Data,~declaration:Declaration) -> Cap.ValueRead,FailureView>: 55 Cap.ValueRead{frame => read(~S,~C,~R,~E,descriptor(~S,declaration),frame)}