1package ports
2
3import (
4 "errors"
5 "sync/atomic"
6)
7
8// RuntimeMarkSchema is the version of the process-local performance trace.
9const RuntimeMarkSchema uint16 = 1
10
11// RuntimeMarkKind identifies a process-local runtime boundary. It deliberately
12// contains no protocol payload, cells, or rendering policy.
13type RuntimeMarkKind string
14
15const (
16 RuntimeInputInjected RuntimeMarkKind = "input_injected"
17 RuntimeResizeRequested RuntimeMarkKind = "resize_requested"
18 RuntimeResizeCommitted RuntimeMarkKind = "resize_committed"
19 RuntimeCaptureStart RuntimeMarkKind = "capture_start"
20 RuntimeCaptureEnd RuntimeMarkKind = "capture_end"
21 RuntimeComposeStart RuntimeMarkKind = "compose_start"
22 RuntimeComposeEnd RuntimeMarkKind = "compose_end"
23 RuntimeDiffStart RuntimeMarkKind = "diff_start"
24 RuntimeDiffEnd RuntimeMarkKind = "diff_end"
25 RuntimeQueueEnqueued RuntimeMarkKind = "queue_enqueued"
26 RuntimeQueueDequeued RuntimeMarkKind = "queue_dequeued"
27 RuntimeACKBlockedStart RuntimeMarkKind = "ack_blocked_start"
28 RuntimeACKBlockedEnd RuntimeMarkKind = "ack_blocked_end"
29 RuntimeEmitStart RuntimeMarkKind = "emit_start"
30 RuntimeEmitEnd RuntimeMarkKind = "emit_end"
31 RuntimeAdapterSendStart RuntimeMarkKind = "adapter_send_start"
32 RuntimeAdapterSendEnd RuntimeMarkKind = "adapter_send_end"
33 RuntimeAdapterReceiveStart RuntimeMarkKind = "adapter_receive_start"
34 RuntimeAdapterReceiveEnd RuntimeMarkKind = "adapter_receive_end"
35 RuntimeTerminalFlushed RuntimeMarkKind = "terminal_flushed"
36 RuntimeScreenSnapshot RuntimeMarkKind = "screen_snapshot"
37 RuntimeScreenDelta RuntimeMarkKind = "screen_delta"
38 RuntimeScreenResetRequested RuntimeMarkKind = "screen_reset_requested"
39 RuntimeTransportDiagnostic RuntimeMarkKind = "transport_diagnostic"
40)
41
42// RuntimeMark is a process-local observation record. Tick is intentionally
43// left zero by producers: the concrete process observer owns timestamping.
44type RuntimeMark struct {
45 Schema uint16
46 ProcessID string
47 Component string
48 Scenario string
49 Run uint64
50 Sequence uint64
51 RequestID uint64
52 Epoch uint64
53 Kind RuntimeMarkKind
54 Tick int64
55 Bytes uint64
56 Fragments uint64
57 Retransmits uint64
58 Pending uint64
59 AckRTTNanos int64
60 Valid bool
61}
62
63// RuntimeObserver receives process-local marks. Implementations must not let
64// a producer provide a timestamp in another clock domain.
65type RuntimeObserver interface{ ObserveRuntime(RuntimeMark) }
66
67// SerializedRuntimeObserver is the only observer contract accepted by runtime
68// transport and use-case hot paths. ObserveRuntime must return without waiting
69// for sink I/O. Flush and Close wait until every accepted mark has reached the
70// concrete observer; Close is idempotent and rejects later marks.
71type SerializedRuntimeObserver interface {
72 RuntimeObserver
73 Flush()
74 Close()
75}
76
77// RuntimeMarkValid reports whether a mark has all correlation fields required
78// by the JSONL schema. Tick is checked separately by the timestamp owner.
79func RuntimeMarkValid(mark RuntimeMark) bool {
80 if mark.Schema != RuntimeMarkSchema || mark.Component == "" || mark.Scenario == "" || mark.Run == 0 || mark.Sequence == 0 || mark.RequestID == 0 || mark.Epoch == 0 {
81 return false
82 }
83 switch mark.Kind {
84 case RuntimeInputInjected, RuntimeResizeRequested, RuntimeResizeCommitted,
85 RuntimeCaptureStart, RuntimeCaptureEnd, RuntimeComposeStart, RuntimeComposeEnd,
86 RuntimeDiffStart, RuntimeDiffEnd, RuntimeQueueEnqueued, RuntimeQueueDequeued,
87 RuntimeACKBlockedStart, RuntimeACKBlockedEnd, RuntimeEmitStart, RuntimeEmitEnd,
88 RuntimeAdapterSendStart, RuntimeAdapterSendEnd, RuntimeAdapterReceiveStart,
89 RuntimeAdapterReceiveEnd, RuntimeTerminalFlushed, RuntimeScreenSnapshot,
90 RuntimeScreenDelta, RuntimeScreenResetRequested, RuntimeTransportDiagnostic:
91 return true
92 default:
93 return false
94 }
95}
96
97// RuntimeCorrelation identifies one attempted operation. It is deliberately
98// copied to both endpoints of a span; process ID remains owned by the sink.
99type RuntimeCorrelation struct {
100 Scenario string
101 Run, Sequence, RequestID, Epoch uint64
102}
103
104// RuntimeCorrelationInputs are supplied once by process orchestration. They
105// are deliberately data-only: consumer APIs continue to receive only their
106// one RuntimeObserver argument and never a clock or tick.
107type RuntimeCorrelationInputs struct {
108 Scenario string
109 Run uint64
110}
111
112// NewRuntimeCorrelationObserver binds every mark from one OS process to its
113// harness manifest scenario/run. Operation IDs remain producer-owned when
114// supplied; missing IDs are assigned from the process-wide allocator.
115func NewRuntimeCorrelationObserver(observer RuntimeObserver, inputs RuntimeCorrelationInputs) (RuntimeObserver, error) {
116 if observer == nil || inputs.Scenario == "" || inputs.Run == 0 {
117 return nil, errors.New("invalid runtime correlation inputs")
118 }
119 return &runtimeCorrelationObserver{observer: observer, inputs: inputs}, nil
120}
121
122type runtimeCorrelationObserver struct {
123 observer RuntimeObserver
124 inputs RuntimeCorrelationInputs
125}
126
127func (o *runtimeCorrelationObserver) ObserveRuntime(mark RuntimeMark) {
128 if o == nil || o.observer == nil {
129 return
130 }
131 mark.Scenario, mark.Run = o.inputs.Scenario, o.inputs.Run
132 if mark.Sequence == 0 {
133 mark.Sequence = runtimeMarkSequence.Add(1)
134 }
135 if mark.RequestID == 0 {
136 mark.RequestID = mark.Sequence
137 }
138 if mark.Epoch == 0 {
139 mark.Epoch = mark.RequestID
140 }
141 o.observer.ObserveRuntime(mark)
142}
143
144var runtimeMarkSequence atomic.Uint64
145
146// NewRuntimeCorrelation makes a unique, process-local operation identity.
147// Components with a domain request/epoch should use those values directly via
148// NewRuntimeMarkWithCorrelation instead of inventing a second identity.
149func NewRuntimeCorrelation() RuntimeCorrelation {
150 sequence := runtimeMarkSequence.Add(1)
151 return RuntimeCorrelation{Scenario: "runtime", Run: 1, Sequence: sequence, RequestID: sequence, Epoch: sequence}
152}
153
154// NewRuntimeMarkWithCorrelation returns a timestamp-free mark carrying the
155// supplied operation identity. Start/end callers must reuse correlation.
156func NewRuntimeMarkWithCorrelation(component string, correlation RuntimeCorrelation, kind RuntimeMarkKind, bytes uint64, valid bool) RuntimeMark {
157 return RuntimeMark{Schema: RuntimeMarkSchema, Component: component, Scenario: correlation.Scenario, Run: correlation.Run, Sequence: correlation.Sequence, RequestID: correlation.RequestID, Epoch: correlation.Epoch, Kind: kind, Bytes: bytes, Valid: valid}
158}
159
160// NewRuntimeMark is retained for one-shot marks. Unlike the old helper it
161// never returns constant correlation IDs.
162func NewRuntimeMark(component string, kind RuntimeMarkKind, bytes uint64, valid bool) RuntimeMark {
163 return NewRuntimeMarkWithCorrelation(component, NewRuntimeCorrelation(), kind, bytes, valid)
164}