tigrbl-kernel 0.4.1.dev6__py3-none-any.whl → 0.4.2.dev4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
tigrbl_kernel/_build.py CHANGED
@@ -597,6 +597,11 @@ def _program_has_exact_http_like_no_input_binding(
597
597
 
598
598
 
599
599
  def _step_has_route_binding(step: StepFn) -> bool:
600
+ for candidate in (step, getattr(step, "__tigrbl_direct_run", None)):
601
+ endpoint = getattr(candidate, "__tigrbl_websocket_endpoint__", None)
602
+ path = getattr(candidate, "__tigrbl_websocket_path__", None)
603
+ if callable(endpoint) and isinstance(path, str) and path:
604
+ return True
600
605
  closure = getattr(step, "__closure__", None)
601
606
  if not closure:
602
607
  return False
@@ -0,0 +1,186 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Any
5
+
6
+ from tigrbl_core._spec.binding_spec import normalize_exchange
7
+
8
+
9
+ CONTRACT_CHANNELS: tuple[str, ...] = ("receive", "send")
10
+ CONTRACT_SCOPE_TYPES: tuple[str, ...] = ("http", "websocket", "webtransport")
11
+ CONTRACT_DIRECTIONS: tuple[str, ...] = (
12
+ "client_to_server",
13
+ "server_to_client",
14
+ "app_to_server",
15
+ "server_to_app",
16
+ "system",
17
+ )
18
+ CONTRACT_FAMILIES: tuple[str, ...] = (
19
+ "request",
20
+ "session",
21
+ "message",
22
+ "stream",
23
+ "datagram",
24
+ )
25
+
26
+ CONTRACT_BINDING_ALIASES: dict[str, tuple[str, ...]] = {
27
+ "rest": ("http.rest", "https.rest"),
28
+ "jsonrpc": ("http.jsonrpc", "https.jsonrpc"),
29
+ "http.stream": ("http.stream", "https.stream"),
30
+ "sse": ("http.sse", "https.sse"),
31
+ "websocket": ("ws", "wss"),
32
+ "webtransport": ("webtransport",),
33
+ }
34
+
35
+ CONTRACT_EXCHANGE_ALIASES: dict[str, str] = {
36
+ "unary": "request_response",
37
+ "duplex": "bidirectional_stream",
38
+ "client_stream": "client_stream",
39
+ "server_stream": "server_stream",
40
+ "fire_and_forget": "fire_and_forget",
41
+ }
42
+
43
+ CANONICAL_CONTRACT_EVENTS: tuple[str, ...] = (
44
+ "http.request",
45
+ "http.disconnect",
46
+ "http.response.start",
47
+ "http.response.body",
48
+ "http.response.pathsend",
49
+ "websocket.connect",
50
+ "websocket.receive",
51
+ "websocket.disconnect",
52
+ "websocket.accept",
53
+ "websocket.send",
54
+ "websocket.close",
55
+ "webtransport.connect",
56
+ "webtransport.accept",
57
+ "webtransport.stream.receive",
58
+ "webtransport.stream.send",
59
+ "webtransport.stream.close",
60
+ "webtransport.stream.reset",
61
+ "webtransport.stream.stop_sending",
62
+ "webtransport.datagram.receive",
63
+ "webtransport.datagram.send",
64
+ "webtransport.disconnect",
65
+ "webtransport.close",
66
+ "transport.emit.complete",
67
+ "transport.emit.failed",
68
+ )
69
+
70
+ _EVENT_SCOPE_PREFIXES: dict[str, str] = {
71
+ "http.": "http",
72
+ "websocket.": "websocket",
73
+ "webtransport.": "webtransport",
74
+ "transport.emit.": "webtransport",
75
+ }
76
+
77
+
78
+ @dataclass(frozen=True, slots=True)
79
+ class ContractClassificationProjection:
80
+ event: str
81
+ channel: str
82
+ scope_type: str
83
+ binding: str
84
+ local_binding_kinds: tuple[str, ...]
85
+ family: str
86
+ contract_exchange: str
87
+ local_exchange: str
88
+ direction: str
89
+ allowed_framings: tuple[str, ...]
90
+ required_payload_fields: tuple[str, ...]
91
+
92
+
93
+ def project_contract_classification(row: dict[str, Any]) -> ContractClassificationProjection:
94
+ if "subsurface" in row:
95
+ raise ValueError("contract classifications must not use subsurface")
96
+
97
+ event = _required_str(row, "event")
98
+ channel = _required_str(row, "channel")
99
+ scope_type = _required_str(row, "scope_type")
100
+ binding = _required_str(row, "binding")
101
+ family = _required_str(row, "family")
102
+ exchange = _required_str(row, "exchange")
103
+ direction = _required_str(row, "direction")
104
+
105
+ if event not in CANONICAL_CONTRACT_EVENTS:
106
+ raise ValueError(f"unsupported contract event {event!r}")
107
+ if channel not in CONTRACT_CHANNELS:
108
+ raise ValueError(f"unsupported contract channel {channel!r}")
109
+ if scope_type not in CONTRACT_SCOPE_TYPES:
110
+ raise ValueError(f"unsupported contract scope_type {scope_type!r}")
111
+ if not _event_matches_scope(event=event, scope_type=scope_type):
112
+ raise ValueError(f"contract event {event!r} does not match scope_type {scope_type!r}")
113
+ if family not in CONTRACT_FAMILIES:
114
+ raise ValueError(f"unsupported contract family {family!r}")
115
+ if direction not in CONTRACT_DIRECTIONS:
116
+ raise ValueError(f"unsupported contract direction {direction!r}")
117
+ if binding not in CONTRACT_BINDING_ALIASES:
118
+ raise ValueError(f"unsupported contract binding {binding!r}")
119
+ if scope_type == "webtransport" and family == "message":
120
+ raise ValueError("WebTransport message is not a native transport family")
121
+
122
+ try:
123
+ local_exchange = normalize_exchange(CONTRACT_EXCHANGE_ALIASES[exchange])
124
+ except KeyError as exc:
125
+ raise ValueError(f"unsupported contract exchange {exchange!r}") from exc
126
+
127
+ return ContractClassificationProjection(
128
+ event=event,
129
+ channel=channel,
130
+ scope_type=scope_type,
131
+ binding=binding,
132
+ local_binding_kinds=CONTRACT_BINDING_ALIASES[binding],
133
+ family=family,
134
+ contract_exchange=exchange,
135
+ local_exchange=local_exchange,
136
+ direction=direction,
137
+ allowed_framings=_string_tuple(row.get("allowed_framings", ())),
138
+ required_payload_fields=_string_tuple(row.get("required_payload_fields", ())),
139
+ )
140
+
141
+
142
+ def is_supported_contract_classification(row: dict[str, Any]) -> bool:
143
+ try:
144
+ project_contract_classification(row)
145
+ except ValueError:
146
+ return False
147
+ return True
148
+
149
+
150
+ def _required_str(row: dict[str, Any], key: str) -> str:
151
+ value = row.get(key)
152
+ if not isinstance(value, str) or not value:
153
+ raise ValueError(f"contract classification requires string {key!r}")
154
+ return value
155
+
156
+
157
+ def _string_tuple(value: Any) -> tuple[str, ...]:
158
+ if value is None:
159
+ return ()
160
+ if not isinstance(value, (list, tuple)):
161
+ raise ValueError("contract classification list fields must be sequences")
162
+ result = tuple(str(item) for item in value)
163
+ if any(not item for item in result):
164
+ raise ValueError("contract classification list fields must not contain blanks")
165
+ return result
166
+
167
+
168
+ def _event_matches_scope(*, event: str, scope_type: str) -> bool:
169
+ for prefix, expected_scope_type in _EVENT_SCOPE_PREFIXES.items():
170
+ if event.startswith(prefix):
171
+ return scope_type == expected_scope_type
172
+ return False
173
+
174
+
175
+ __all__ = [
176
+ "CANONICAL_CONTRACT_EVENTS",
177
+ "CONTRACT_BINDING_ALIASES",
178
+ "CONTRACT_CHANNELS",
179
+ "CONTRACT_DIRECTIONS",
180
+ "CONTRACT_EXCHANGE_ALIASES",
181
+ "CONTRACT_FAMILIES",
182
+ "CONTRACT_SCOPE_TYPES",
183
+ "ContractClassificationProjection",
184
+ "is_supported_contract_classification",
185
+ "project_contract_classification",
186
+ ]
@@ -3,26 +3,43 @@ from __future__ import annotations
3
3
  from collections.abc import Mapping
4
4
  from typing import Any
5
5
 
6
+ from tigrbl_core._spec.binding_spec import (
7
+ validate_app_framing_for_binding,
8
+ validate_binding_profile_exchange,
9
+ validate_webtransport_inner_framing,
10
+ validate_webtransport_lane_exchange,
11
+ webtransport_lane_for_profile,
12
+ webtransport_runtime_family,
13
+ )
14
+
6
15
 
7
16
  def _unsupported(message: str) -> ValueError:
8
17
  return ValueError(f"binding protocol unsupported before runtime: {message}")
9
18
 
10
19
 
11
20
  def compile_binding_protocol_plan(op_id: str, binding: Mapping[str, Any]) -> dict[str, object]:
12
- kind = binding.get("kind")
21
+ kind = binding.get("kind") or binding.get("proto")
13
22
  if not kind:
14
23
  raise ValueError(
15
24
  "BindingSpec binding source is required; transport guessing is ambiguous"
16
25
  )
17
26
 
18
27
  kind = str(kind)
28
+ profile = binding.get("profile")
29
+ if kind in {"http", "https"} and profile:
30
+ kind = f"{kind}.{profile}"
31
+ elif kind == "websocket":
32
+ kind = str(binding.get("proto") or "ws")
19
33
  framing = binding.get("framing")
20
34
  rows: tuple[dict[str, str], ...]
21
35
 
22
- if kind == "http.rest":
23
- if framing not in (None, "json"):
24
- raise _unsupported("http.rest only supports json framing")
25
- family = "response"
36
+ if kind in {"http.rest", "https.rest"}:
37
+ validate_binding_profile_exchange(
38
+ binding_kind=kind,
39
+ exchange=str(binding.get("exchange") or "request_response"),
40
+ )
41
+ validate_app_framing_for_binding(binding_kind=kind, framing=str(framing or "json"))
42
+ family = "request"
26
43
  framing = "json"
27
44
  anchors = (
28
45
  "ingress.receive",
@@ -31,13 +48,18 @@ def compile_binding_protocol_plan(op_id: str, binding: Mapping[str, Any]) -> dic
31
48
  "transport.emit_complete",
32
49
  )
33
50
  rows = (
34
- {"family": "response", "subevent": "request.received"},
35
- {"family": "response", "subevent": "response.emit"},
51
+ {"family": "request", "subevent": "request.received"},
52
+ {"family": "request", "subevent": "response.emit"},
36
53
  )
37
- elif kind == "http.jsonrpc":
54
+ elif kind in {"http.jsonrpc", "https.jsonrpc"}:
38
55
  if not binding.get("rpc_method"):
39
56
  raise _unsupported("http.jsonrpc requires rpc_method")
40
- family = "response"
57
+ validate_binding_profile_exchange(
58
+ binding_kind=kind,
59
+ exchange=str(binding.get("exchange") or "request_response"),
60
+ )
61
+ validate_app_framing_for_binding(binding_kind=kind, framing=str(framing or "jsonrpc"))
62
+ family = "request"
41
63
  framing = "jsonrpc"
42
64
  anchors = (
43
65
  "framing.decode",
@@ -46,18 +68,28 @@ def compile_binding_protocol_plan(op_id: str, binding: Mapping[str, Any]) -> dic
46
68
  "framing.encode",
47
69
  )
48
70
  rows = (
49
- {"family": "response", "subevent": "request.received"},
50
- {"family": "response", "subevent": "response.emit"},
71
+ {"family": "request", "subevent": "request.received"},
72
+ {"family": "request", "subevent": "response.emit"},
51
73
  )
52
- elif kind == "http.stream":
74
+ elif kind in {"http.stream", "https.stream"}:
75
+ validate_binding_profile_exchange(
76
+ binding_kind=kind,
77
+ exchange=str(binding.get("exchange") or "server_stream"),
78
+ )
79
+ validate_app_framing_for_binding(binding_kind=kind, framing=str(framing or "stream"))
53
80
  family = "stream"
54
- framing = "stream"
81
+ framing = str(framing or "stream")
55
82
  anchors = ("handler.invoke", "transport.emit", "transport.emit_complete")
56
83
  rows = (
57
84
  {"family": "stream", "subevent": "stream.chunk"},
58
85
  {"family": "stream", "subevent": "stream.close"},
59
86
  )
60
- elif kind == "http.sse":
87
+ elif kind in {"http.sse", "https.sse"}:
88
+ validate_binding_profile_exchange(
89
+ binding_kind=kind,
90
+ exchange=str(binding.get("exchange") or "server_stream"),
91
+ )
92
+ validate_app_framing_for_binding(binding_kind=kind, framing=str(framing or "sse"))
61
93
  family = "stream"
62
94
  framing = "sse"
63
95
  anchors = (
@@ -67,15 +99,25 @@ def compile_binding_protocol_plan(op_id: str, binding: Mapping[str, Any]) -> dic
67
99
  "transport.emit_complete",
68
100
  )
69
101
  rows = (
70
- {"family": "event_stream", "subevent": "message.encoded"},
71
- {"family": "event_stream", "subevent": "message.emit"},
102
+ {"family": "stream", "subevent": "message.encoded"},
103
+ {"family": "stream", "subevent": "message.emit"},
72
104
  {"family": "stream", "subevent": "stream.close"},
73
105
  )
74
- elif kind in {"ws", "websocket"}:
106
+ elif kind in {"ws", "wss", "websocket"}:
75
107
  if binding.get("methods"):
76
108
  raise _unsupported("websocket bindings do not accept HTTP methods")
77
109
  family = "message"
78
110
  framing = str(framing or "text")
111
+ subprotocols = tuple(str(item).lower() for item in binding.get("subprotocols", ()))
112
+ validate_binding_profile_exchange(
113
+ binding_kind="wss" if kind == "wss" else "ws",
114
+ exchange=str(binding.get("exchange") or "bidirectional_stream"),
115
+ )
116
+ validate_app_framing_for_binding(
117
+ binding_kind="wss" if kind == "wss" else "ws",
118
+ framing=framing,
119
+ subprotocols=subprotocols,
120
+ )
79
121
  anchors = (
80
122
  "transport.accept",
81
123
  "framing.decode",
@@ -91,32 +133,120 @@ def compile_binding_protocol_plan(op_id: str, binding: Mapping[str, Any]) -> dic
91
133
  elif kind == "webtransport":
92
134
  if binding.get("exchange") == "request_response":
93
135
  raise _unsupported("webtransport request_response exchange")
94
- family = "session"
95
- framing = "webtransport"
96
- anchors = (
97
- "transport.accept",
98
- "dispatch.subevent.derive",
99
- "handler.invoke",
100
- "transport.close",
136
+ validate_app_framing_for_binding(binding_kind=kind, framing=str(framing or "webtransport"))
137
+ if framing and str(framing) != "webtransport":
138
+ raise _unsupported("webtransport outer framing must remain webtransport")
139
+ lane = webtransport_lane_for_profile(
140
+ binding.get("lane") or binding.get("profile") or "webtransport"
101
141
  )
102
- rows = (
103
- {"family": "session", "subevent": "session.open"},
104
- {"family": "session", "subevent": "session.close"},
142
+ validate_webtransport_lane_exchange(
143
+ lane=lane,
144
+ exchange=str(binding.get("exchange") or {
145
+ "session": "bidirectional_stream",
146
+ "bidi_stream": "bidirectional_stream",
147
+ "unidi_client_stream": "client_stream",
148
+ "unidi_server_stream": "server_stream",
149
+ "datagram": "bidirectional_stream",
150
+ }[lane]),
151
+ )
152
+ inner_framing = validate_webtransport_inner_framing(
153
+ lane=lane,
154
+ inner_framing=binding.get("inner_framing"),
105
155
  )
156
+ family = webtransport_runtime_family(lane)
157
+ framing = "webtransport"
158
+ if lane == "session":
159
+ anchors = (
160
+ "transport.accept",
161
+ "dispatch.subevent.derive",
162
+ "handler.invoke",
163
+ "transport.close",
164
+ )
165
+ rows = (
166
+ {"family": "session", "subevent": "session.open"},
167
+ {"family": "session", "subevent": "session.close"},
168
+ )
169
+ elif lane == "bidi_stream":
170
+ anchors = (
171
+ "transport.accept",
172
+ "transport.receive",
173
+ "framing.decode",
174
+ "dispatch.subevent.derive",
175
+ "handler.invoke",
176
+ "framing.encode",
177
+ "transport.emit",
178
+ "transport.close",
179
+ )
180
+ rows = (
181
+ {"family": "stream", "subevent": "stream.open"},
182
+ {"family": "stream", "subevent": "stream.chunk.received"},
183
+ {"family": "stream", "subevent": "stream.chunk.emit"},
184
+ {"family": "stream", "subevent": "stream.close"},
185
+ )
186
+ elif lane == "unidi_client_stream":
187
+ anchors = (
188
+ "transport.accept",
189
+ "transport.receive",
190
+ "framing.decode",
191
+ "dispatch.subevent.derive",
192
+ "handler.invoke",
193
+ "transport.close",
194
+ )
195
+ rows = (
196
+ {"family": "stream", "subevent": "stream.open"},
197
+ {"family": "stream", "subevent": "stream.chunk.received"},
198
+ {"family": "stream", "subevent": "stream.close"},
199
+ )
200
+ elif lane == "unidi_server_stream":
201
+ anchors = (
202
+ "transport.accept",
203
+ "handler.invoke",
204
+ "framing.encode",
205
+ "transport.emit",
206
+ "transport.close",
207
+ )
208
+ rows = (
209
+ {"family": "stream", "subevent": "stream.open"},
210
+ {"family": "stream", "subevent": "stream.chunk.emit"},
211
+ {"family": "stream", "subevent": "stream.emit_complete"},
212
+ {"family": "stream", "subevent": "stream.close"},
213
+ )
214
+ elif lane == "datagram":
215
+ anchors = (
216
+ "transport.accept",
217
+ "transport.receive",
218
+ "framing.decode",
219
+ "dispatch.subevent.derive",
220
+ "handler.invoke",
221
+ "framing.encode",
222
+ "transport.emit",
223
+ )
224
+ rows = (
225
+ {"family": "datagram", "subevent": "datagram.received"},
226
+ {"family": "datagram", "subevent": "datagram.emit"},
227
+ {"family": "datagram", "subevent": "datagram.emit_complete"},
228
+ )
229
+ else: # pragma: no cover - guarded by webtransport_lane_for_profile
230
+ raise _unsupported(f"webtransport lane {lane}")
106
231
  else:
107
232
  raise _unsupported(kind)
108
233
 
234
+ event_key_inputs = {
235
+ "family": family,
236
+ "binding": kind,
237
+ "framing": framing,
238
+ }
239
+ if kind == "webtransport":
240
+ event_key_inputs["lane"] = lane
241
+ event_key_inputs["inner_framing"] = inner_framing
242
+
109
243
  return {
110
244
  "op_id": op_id,
111
245
  "binding_kind": kind,
112
246
  "family": family,
113
247
  "framing": framing,
114
248
  "atom_anchors": anchors,
115
- "event_key_inputs": {
116
- "family": family,
117
- "binding": kind,
118
- "framing": framing,
119
- },
249
+ "event_key_inputs": event_key_inputs,
120
250
  "capability_requirements": {
121
251
  "required_mask": _required_mask(kind=kind, family=family, framing=str(framing)),
122
252
  },
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
 
4
4
  _FAMILIES = {
5
5
  "request": ("request.received", "request.body.received"),
6
- "response": ("response.emit", "response.emit_complete"),
7
6
  "session": ("session.open", "session.ready", "session.close"),
8
7
  "message": ("message.received", "message.decoded", "message.emit", "message.emit_complete"),
9
8
  "stream": ("stream.open", "stream.chunk.received", "stream.chunk.emit", "stream.close"),
@@ -11,19 +10,29 @@ _FAMILIES = {
11
10
  }
12
11
 
13
12
  _BINDING_FAMILY = {
14
- "http.rest": "response",
15
- "http.jsonrpc": "response",
13
+ "http.rest": "request",
14
+ "https.rest": "request",
15
+ "http.jsonrpc": "request",
16
+ "https.jsonrpc": "request",
16
17
  "http.stream": "stream",
18
+ "https.stream": "stream",
17
19
  "http.sse": "stream",
20
+ "https.sse": "stream",
18
21
  "ws": "message",
22
+ "wss": "message",
19
23
  "websocket": "message",
24
+ "webtransport": "session",
25
+ "webtransport.session": "session",
26
+ "webtransport.bidi_stream": "stream",
27
+ "webtransport.unidi_client_stream": "stream",
28
+ "webtransport.unidi_server_stream": "stream",
20
29
  "webtransport.datagram": "datagram",
21
30
  }
22
31
 
23
32
  _ALIASES = {
24
33
  "receive": {"message": "message.received", "request": "request.received"},
25
- "emit": {"response": "response.emit", "message": "message.emit", "datagram": "datagram.emit"},
26
- "complete": {"response": "response.emit_complete", "message": "message.emit_complete", "datagram": "datagram.emit_complete"},
34
+ "emit": {"message": "message.emit", "stream": "stream.chunk.emit", "datagram": "datagram.emit"},
35
+ "complete": {"message": "message.emit_complete", "stream": "stream.close", "datagram": "datagram.emit_complete"},
27
36
  }
28
37
 
29
38
 
@@ -1,6 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Sequence
4
+ from typing import Any
5
+
6
+ from tigrbl_core._spec.binding_spec import validate_webtransport_inner_framing
4
7
 
5
8
 
6
9
  def _event(subevent: str, atom: str, family: str) -> dict[str, str]:
@@ -23,6 +26,26 @@ def compile_webtransport_events(surface: str) -> list[dict[str, str]]:
23
26
  _event("stream.emit_complete", "transport.emit", "stream"),
24
27
  _event("stream.close", "transport.close", "stream"),
25
28
  ]
29
+ if surface == "bidi_stream":
30
+ return [
31
+ _event("stream.open", "transport.accept", "stream"),
32
+ _event("stream.chunk.received", "transport.receive", "stream"),
33
+ _event("stream.chunk.emit", "transport.emit", "stream"),
34
+ _event("stream.close", "transport.close", "stream"),
35
+ ]
36
+ if surface == "unidi_client_stream":
37
+ return [
38
+ _event("stream.open", "transport.accept", "stream"),
39
+ _event("stream.chunk.received", "transport.receive", "stream"),
40
+ _event("stream.close", "transport.close", "stream"),
41
+ ]
42
+ if surface == "unidi_server_stream":
43
+ return [
44
+ _event("stream.open", "transport.accept", "stream"),
45
+ _event("stream.chunk.emit", "transport.emit", "stream"),
46
+ _event("stream.emit_complete", "transport.emit", "stream"),
47
+ _event("stream.close", "transport.close", "stream"),
48
+ ]
26
49
  if surface == "datagram":
27
50
  return [
28
51
  _event("datagram.received", "transport.receive", "datagram"),
@@ -31,10 +54,12 @@ def compile_webtransport_events(surface: str) -> list[dict[str, str]]:
31
54
  ]
32
55
  if surface == "app_frame":
33
56
  return [
34
- _event("app_frame.decode", "framing.decode", "message"),
35
- _event("app_frame.emit", "transport.emit", "message"),
36
- _event("app_frame.encode", "framing.encode", "message"),
57
+ _event("app_frame.decode", "framing.decode", "stream"),
58
+ _event("app_frame.emit", "transport.emit", "stream"),
59
+ _event("app_frame.encode", "framing.encode", "stream"),
37
60
  ]
61
+ if surface == "message":
62
+ raise ValueError("WebTransport does not expose message as a native surface")
38
63
  raise ValueError(f"unsupported WebTransport surface: {surface}")
39
64
 
40
65
 
@@ -48,4 +73,152 @@ def compile_webtransport_chain(*, include_stream: bool = True, include_datagram:
48
73
  return {"binding": "webtransport", "subevents": tuple(subevents)}
49
74
 
50
75
 
51
- __all__ = ["compile_webtransport_chain", "compile_webtransport_events"]
76
+ def compile_webtransport_native_lanes() -> dict[str, tuple[str, ...]]:
77
+ return {
78
+ "session": tuple(event["subevent"] for event in compile_webtransport_events("session")),
79
+ "bidi_stream": tuple(event["subevent"] for event in compile_webtransport_events("bidi_stream")),
80
+ "unidi_client_stream": tuple(
81
+ event["subevent"] for event in compile_webtransport_events("unidi_client_stream")
82
+ ),
83
+ "unidi_server_stream": tuple(
84
+ event["subevent"] for event in compile_webtransport_events("unidi_server_stream")
85
+ ),
86
+ "datagram": tuple(event["subevent"] for event in compile_webtransport_events("datagram")),
87
+ }
88
+
89
+
90
+ _STREAM_EVENTS = {
91
+ "webtransport.stream.receive",
92
+ "webtransport.stream.send",
93
+ "webtransport.stream.close",
94
+ "webtransport.stream.reset",
95
+ "webtransport.stream.stop_sending",
96
+ }
97
+ _DATAGRAM_EVENTS = {
98
+ "webtransport.datagram.receive",
99
+ "webtransport.datagram.send",
100
+ }
101
+ _SESSION_EVENTS = {
102
+ "webtransport.connect",
103
+ "webtransport.accept",
104
+ "webtransport.disconnect",
105
+ "webtransport.close",
106
+ }
107
+ _STREAM_DIRECTIONS = {
108
+ "bidi": "bidi_stream",
109
+ "client_to_server": "unidi_client_stream",
110
+ "server_to_client": "unidi_server_stream",
111
+ }
112
+
113
+
114
+ def validate_webtransport_event_payload(
115
+ *,
116
+ event: str,
117
+ channel: str,
118
+ payload: dict[str, Any],
119
+ ) -> dict[str, object]:
120
+ if not isinstance(payload, dict):
121
+ raise ValueError("WebTransport event payload must be a mapping")
122
+ if channel not in {"receive", "send"}:
123
+ raise ValueError("WebTransport event channel must be receive or send")
124
+ if event in _SESSION_EVENTS:
125
+ return _validate_session_payload(event=event, channel=channel, payload=payload)
126
+ if event in _STREAM_EVENTS:
127
+ return _validate_stream_payload(event=event, channel=channel, payload=payload)
128
+ if event in _DATAGRAM_EVENTS:
129
+ return _validate_datagram_payload(event=event, channel=channel, payload=payload)
130
+ if event.startswith("webtransport.message"):
131
+ raise ValueError("WebTransport message is not a native transport lane")
132
+ raise ValueError(f"unsupported WebTransport event {event!r}")
133
+
134
+
135
+ def _validate_session_payload(
136
+ *,
137
+ event: str,
138
+ channel: str,
139
+ payload: dict[str, Any],
140
+ ) -> dict[str, object]:
141
+ expected_channel = "receive" if event in {"webtransport.connect", "webtransport.disconnect"} else "send"
142
+ if channel != expected_channel:
143
+ raise ValueError(f"{event} is only valid on {expected_channel}")
144
+ _forbid(payload, "stream_id", "stream_direction", "datagram_id", "framing")
145
+ return {"family": "session", "lane": "session", "exchange": "request_response"}
146
+
147
+
148
+ def _validate_stream_payload(
149
+ *,
150
+ event: str,
151
+ channel: str,
152
+ payload: dict[str, Any],
153
+ ) -> dict[str, object]:
154
+ expected_channel = "receive" if event == "webtransport.stream.receive" else "send"
155
+ if channel != expected_channel:
156
+ raise ValueError(f"{event} is only valid on {expected_channel}")
157
+ _require(payload, "stream_id")
158
+ _forbid(payload, "datagram_id")
159
+ direction = payload.get("stream_direction")
160
+ if event in {
161
+ "webtransport.stream.receive",
162
+ "webtransport.stream.send",
163
+ }:
164
+ if not isinstance(direction, str) or direction not in _STREAM_DIRECTIONS:
165
+ raise ValueError("WebTransport stream payload requires valid stream_direction")
166
+ else:
167
+ direction = str(direction) if direction in _STREAM_DIRECTIONS else "bidi"
168
+ lane = _STREAM_DIRECTIONS[str(direction)]
169
+ if channel == "receive" and lane == "unidi_server_stream":
170
+ raise ValueError("server_to_client unidirectional streams cannot be receive events")
171
+ if channel == "send" and lane == "unidi_client_stream":
172
+ raise ValueError("client_to_server unidirectional streams cannot be send events")
173
+ validate_webtransport_inner_framing(
174
+ lane=lane,
175
+ inner_framing=payload.get("framing"),
176
+ )
177
+ return {
178
+ "family": "stream",
179
+ "lane": lane,
180
+ "exchange": {
181
+ "bidi_stream": "bidirectional_stream",
182
+ "unidi_client_stream": "client_stream",
183
+ "unidi_server_stream": "server_stream",
184
+ }[lane],
185
+ }
186
+
187
+
188
+ def _validate_datagram_payload(
189
+ *,
190
+ event: str,
191
+ channel: str,
192
+ payload: dict[str, Any],
193
+ ) -> dict[str, object]:
194
+ expected_channel = "receive" if event == "webtransport.datagram.receive" else "send"
195
+ if channel != expected_channel:
196
+ raise ValueError(f"{event} is only valid on {expected_channel}")
197
+ _require(payload, "datagram_id")
198
+ _forbid(payload, "stream_id", "stream_direction")
199
+ validate_webtransport_inner_framing(
200
+ lane="datagram",
201
+ inner_framing=payload.get("framing"),
202
+ )
203
+ return {"family": "datagram", "lane": "datagram", "exchange": "bidirectional_stream"}
204
+
205
+
206
+ def _require(payload: dict[str, Any], field: str) -> None:
207
+ value = payload.get(field)
208
+ if value is None or value == "":
209
+ raise ValueError(f"WebTransport payload requires {field}")
210
+
211
+
212
+ def _forbid(payload: dict[str, Any], *fields: str) -> None:
213
+ present = [field for field in fields if field in payload and payload[field] is not None]
214
+ if present:
215
+ joined = ", ".join(present)
216
+ raise ValueError(f"WebTransport payload field not valid for event: {joined}")
217
+
218
+
219
+ __all__ = [
220
+ "compile_webtransport_chain",
221
+ "compile_webtransport_events",
222
+ "compile_webtransport_native_lanes",
223
+ "validate_webtransport_event_payload",
224
+ ]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tigrbl-kernel
3
- Version: 0.4.1.dev6
3
+ Version: 0.4.2.dev4
4
4
  Summary: Kernel orchestration for composing Tigrbl runtime plans, bindings, operation dispatch, and optimized ASGI execution.
5
5
  License: Apache License
6
6
  Version 2.0, January 2004
@@ -204,6 +204,7 @@ License: Apache License
204
204
  See the License for the specific language governing permissions and
205
205
  limitations under the License.
206
206
  License-File: LICENSE
207
+ License-File: NOTICE
207
208
  Keywords: tigrbl,asgi,api,json-rpc,rest,sqlalchemy,pydantic,kernel,orchestration,dispatch,openapi,openrpc,schema-first
208
209
  Author: Jacob Stewart
209
210
  Author-email: jacob@swarmauri.com
@@ -234,129 +235,161 @@ Project-URL: Organization, https://github.com/tigrbl
234
235
  Project-URL: Repository, https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_kernel
235
236
  Description-Content-Type: text/markdown
236
237
 
237
- ![Tigrbl Logo](https://raw.githubusercontent.com/swarmauri/swarmauri-sdk/master/assets/tigrbl_full_logo.png)
238
-
239
- <p align="center">
240
- <a href="https://pepy.tech/project/tigrbl-kernel">
241
- <img src="https://static.pepy.tech/badge/tigrbl-kernel" alt="Pepy downloads for tigrbl-kernel"/></a>
242
- <a href="https://hits.sh/github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_kernel/">
243
- <img src="https://hits.sh/github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_kernel.svg" alt="Repository views for tigrbl-kernel"/></a>
244
- <a href="https://pypi.org/project/tigrbl-kernel/">
245
- <img src="https://img.shields.io/badge/python-3.10--3.14-blue" alt="Python 3.10 through 3.14"/></a>
246
- <a href="https://pypi.org/project/tigrbl-kernel/">
247
- <img src="https://img.shields.io/pypi/l/tigrbl-kernel" alt="PyPI license metadata for tigrbl-kernel"/></a>
248
- <a href="https://pypi.org/project/tigrbl-kernel/">
249
- <img src="https://img.shields.io/pypi/v/tigrbl-kernel?label=tigrbl-kernel&color=green" alt="PyPI version for tigrbl-kernel"/></a>
250
- </p>
251
-
252
- ---
253
-
254
- <h1 align="center">Tigrbl kernel</h1>
255
-
256
- **Install and inspect `tigrbl-kernel`: [download `tigrbl-kernel` from PyPI](https://pypi.org/project/tigrbl-kernel/) or [open the package source](https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_kernel).**
257
-
258
- tigrbl-kernel is a kernel orchestration package for kernel orchestration for composing Tigrbl runtime plans, bindings, and operation dispatch.
259
-
260
- `tigrbl-kernel` is part of the Tigrbl package graph. It documents package-resident classes, concepts, extension points, and execution responsibilities while cross-linking to the facade, core specs, canonical mapping, runtime phases, concrete objects, operation packages, engine plugins, OpenAPI/OpenRPC documentation surfaces, and PyPI distributions that complete the system.
261
-
262
- ## Package ownership
263
-
264
- - `tigrbl-kernel` documents the concepts implemented in `pkgs/core/tigrbl_kernel` and links to the Tigrbl packages that provide neighboring authoring, canon, runtime, operation, and engine behavior.
265
- - Use this README as the package-local explanation for the objects that live here; use governed docs for release state, certification, and evidence.
266
-
267
- ## Package ecosystem cross-links
268
-
269
- Every Tigrbl Python package links to its sibling distributions on PyPI so package indexes, search engines, answer engines, dependency scanners, and human readers can move through the installable package graph without falling back to source-tree paths.
270
-
271
- Core packages:
272
- - [`tigrbl`](https://pypi.org/project/tigrbl/) - Schema-first ASGI API framework for REST, JSON-RPC, OpenAPI, OpenRPC, SQLAlchemy models, typed validation, lifecycle hooks, and engine plugins.
273
- - [`tigrbl-atoms`](https://pypi.org/project/tigrbl-atoms/) - Runtime atom utilities for Tigrbl planning, dispatch, transport ingress, egress, and high-throughput ASGI execution pipelines.
274
- - [`tigrbl-base`](https://pypi.org/project/tigrbl-base/) - Abstract base interfaces for Tigrbl APIs, engines, providers, sessions, transports, and reusable runtime components.
275
- - [`tigrbl-canon`](https://pypi.org/project/tigrbl-canon/) - Canonical mapping, routing, symbol resolution, and naming utilities for Tigrbl framework packages and generated API surfaces.
276
- - [`tigrbl_client`](https://pypi.org/project/tigrbl_client/) - Typed Python client helpers for calling Tigrbl REST, JSON-RPC, OpenAPI, and generated schema-first API surfaces.
277
- - [`tigrbl-concrete`](https://pypi.org/project/tigrbl-concrete/) - Concrete Tigrbl implementations for reusable framework behavior, sessions, routes, responses, and base abstraction adapters.
278
- - [`tigrbl-core`](https://pypi.org/project/tigrbl-core/) - Core Tigrbl framework specifications, decorators, schemas, hooks, operations, and primitives for schema-first APIs.
279
- - [`tigrbl-kernel`](https://pypi.org/project/tigrbl-kernel/) (this package) - Kernel orchestration for composing Tigrbl runtime plans, bindings, operation dispatch, and optimized ASGI execution.
280
- - [`tigrbl-ops-olap`](https://pypi.org/project/tigrbl-ops-olap/) - Analytical OLAP operation boundaries for Tigrbl workloads, query-oriented APIs, and engine integrations.
281
- - [`tigrbl-ops-oltp`](https://pypi.org/project/tigrbl-ops-oltp/) - Transactional OLTP operation handlers for Tigrbl CRUD, bulk, REST, JSON-RPC, and database-backed workloads.
282
- - [`tigrbl-ops-realtime`](https://pypi.org/project/tigrbl-ops-realtime/) - Realtime, streaming, datagram, websocket, and event operation handlers for Tigrbl ASGI runtimes.
283
- - [`tigrbl-orm`](https://pypi.org/project/tigrbl-orm/) - SQLAlchemy ORM tables, mixins, columns, model helpers, and persistence primitives for Tigrbl applications.
284
- - [`tigrbl-runtime`](https://pypi.org/project/tigrbl-runtime/) - Runtime pipeline helpers and execution bridge surfaces for Tigrbl ASGI applications, transports, and operation dispatch.
285
- - [`tigrbl_spec`](https://pypi.org/project/tigrbl_spec/) - Shared Tigrbl interfaces, protocol definitions, compatibility targets, and specification artifacts for framework integration.
286
- - [`tigrbl_tests`](https://pypi.org/project/tigrbl_tests/) - Reusable Tigrbl pytest fixtures, conformance assertions, integration helpers, and package test utilities.
287
- - [`tigrbl-typing`](https://pypi.org/project/tigrbl-typing/) - Typing protocols, aliases, generics, and shared type helpers for Tigrbl framework packages and extensions.
288
-
289
- Engine packages:
290
- - [`tigrbl_engine_bigquery`](https://pypi.org/project/tigrbl_engine_bigquery/) - BigQuery engine plugin for Google BigQuery warehouse sessions, analytics workloads, and Tigrbl engine registration.
291
- - [`tigrbl_engine_clickhouse`](https://pypi.org/project/tigrbl_engine_clickhouse/) - ClickHouse engine plugin for analytical database sessions, warehouse workloads, and Tigrbl engine registration.
292
- - [`tigrbl_engine_csv`](https://pypi.org/project/tigrbl_engine_csv/) - CSV engine plugin for file-backed tables, pandas DataFrames, and lightweight Tigrbl data workflows.
293
- - [`tigrbl_engine_dataframe`](https://pypi.org/project/tigrbl_engine_dataframe/) - DataFrame engine plugin for transactional pandas sessions and in-process Tigrbl analytics workloads.
294
- - [`tigrbl_engine_duckdb`](https://pypi.org/project/tigrbl_engine_duckdb/) - DuckDB engine plugin for embedded analytical database sessions, OLAP workloads, and Tigrbl engine registration.
295
- - [`tigrbl_engine_inmemcache`](https://pypi.org/project/tigrbl_engine_inmemcache/) - In-memory cache engine plugin for process-local TTL, LRU, and fast Tigrbl cache workflows.
296
- - [`tigrbl_engine_inmemory`](https://pypi.org/project/tigrbl_engine_inmemory/) - In-memory database engine plugin for process-local transactional storage, copy-on-write snapshots, and Tigrbl testing.
297
- - [`tigrbl_engine_membloom`](https://pypi.org/project/tigrbl_engine_membloom/) - In-memory Bloom filter engine plugin for membership checks, rotating TTL windows, and Tigrbl API workflows.
298
- - [`tigrbl_engine_memdedupe`](https://pypi.org/project/tigrbl_engine_memdedupe/) - In-memory dedupe engine plugin for idempotency tracking, duplicate suppression, and Tigrbl workflow coordination.
299
- - [`tigrbl_engine_memkv`](https://pypi.org/project/tigrbl_engine_memkv/) - In-memory key-value engine plugin for process-local KV storage, cache workflows, and lightweight Tigrbl services.
300
- - [`tigrbl_engine_memlru`](https://pypi.org/project/tigrbl_engine_memlru/) - In-memory LRU engine plugin for least-recently-used cache behavior and process-local Tigrbl data workflows.
301
- - [`tigrbl_engine_mempubsub`](https://pypi.org/project/tigrbl_engine_mempubsub/) - In-memory pub/sub engine plugin for process-local publish-subscribe channels, events, and Tigrbl realtime workflows.
302
- - [`tigrbl_engine_memqueue`](https://pypi.org/project/tigrbl_engine_memqueue/) - In-memory queue engine plugin for process-local tasks, message workflows, and Tigrbl runtime coordination.
303
- - [`tigrbl_engine_memrate`](https://pypi.org/project/tigrbl_engine_memrate/) - In-memory rate-limit engine plugin for API quotas, counters, windows, and Tigrbl governance workflows.
304
- - [`tigrbl_engine_numpy`](https://pypi.org/project/tigrbl_engine_numpy/) - NumPy engine plugin for array-to-table helpers, analytical workflows, and Tigrbl data integration.
305
- - [`tigrbl_engine_pandas`](https://pypi.org/project/tigrbl_engine_pandas/) - Pandas engine plugin for transactional DataFrame sessions, tabular workflows, and Tigrbl data integration.
306
- - [`tigrbl_engine_pgsqli_wal`](https://pypi.org/project/tigrbl_engine_pgsqli_wal/) - PostgreSQL and SQLite WAL engine plugin for transactional Tigrbl workflows and database-backed engine registration.
307
- - [`tigrbl_engine_postgres`](https://pypi.org/project/tigrbl_engine_postgres/) - PostgreSQL engine plugin for SQLAlchemy sessions, async database workflows, and Tigrbl application persistence.
308
- - [`tigrbl_engine_pyspark`](https://pypi.org/project/tigrbl_engine_pyspark/) - PySpark engine plugin for distributed DataFrame integration, analytics workloads, and Tigrbl data workflows.
309
- - [`tigrbl_engine_redis`](https://pypi.org/project/tigrbl_engine_redis/) - Redis engine plugin for cache, data structures, and Tigrbl engine workflows backed by Redis.
310
- - [`tigrbl_engine_rediscachethrough`](https://pypi.org/project/tigrbl_engine_rediscachethrough/) - Redis cache-through engine plugin for Redis, PostgreSQL, and Tigrbl data-access acceleration workflows.
311
- - [`tigrbl_engine_snowflake`](https://pypi.org/project/tigrbl_engine_snowflake/) - Snowflake engine plugin for warehouse sessions, analytical workloads, and Tigrbl engine registration.
312
- - [`tigrbl_engine_sqlite`](https://pypi.org/project/tigrbl_engine_sqlite/) - SQLite engine plugin for SQLAlchemy sessions, local transactional storage, and Tigrbl application persistence.
313
- - [`tigrbl_engine_xlsx`](https://pypi.org/project/tigrbl_engine_xlsx/) - XLSX engine plugin for Excel workbook-backed tables, worksheet data access, and Tigrbl tabular workflows.
314
-
315
- Application packages:
316
- - [`tigrbl_acme_ca`](https://pypi.org/project/tigrbl_acme_ca/) - ACME v2 certificate authority app for Tigrbl tables, certificate automation, TLS workflows, and API surfaces.
317
- - [`tigrbl_spiffe`](https://pypi.org/project/tigrbl_spiffe/) - SPIFFE and SPIRE identity app for Tigrbl with workload identity tables, UDS transport, and HTTP API surfaces.
318
-
319
- Source-tree links remain available from each package identity section; this ecosystem section is intentionally PyPI-first for package discovery and installation routing.
238
+ <div align="center">
239
+ <h1>tigrbl-kernel</h1>
240
+ <img src="https://raw.githubusercontent.com/swarmauri/swarmauri-sdk/master/assets/tigrbl_full_logo.png" alt="Tigrbl logo" width="140"/>
241
+ <p><strong>Kernel orchestration for composing Tigrbl runtime plans, bindings, operation dispatch, and optimized ASGI execution.</strong></p>
242
+ <a href="https://pypi.org/project/tigrbl-kernel/"><img src="https://img.shields.io/pypi/v/tigrbl-kernel?label=PyPI" alt="PyPI version for tigrbl-kernel"/></a>
243
+ <a href="https://pypi.org/project/tigrbl-kernel/"><img src="https://static.pepy.tech/badge/tigrbl-kernel" alt="Downloads for tigrbl-kernel"/></a>
244
+ <a href="https://discord.gg/K4YTAPapjR"><img src="https://img.shields.io/badge/Discord-Join%20chat-5865F2?logo=discord&logoColor=white" alt="Discord community for tigrbl-kernel"/></a>
245
+ <a href="https://github.com/tigrbl/tigrbl/blob/master/pkgs/core/tigrbl_kernel/README.md"><img src="https://hits.sh/github.com/tigrbl/tigrbl/blob/master/pkgs/core/tigrbl_kernel/README.md.svg?label=hits" alt="Repository hits for tigrbl-kernel README"/></a>
246
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-Apache%202.0-525252" alt="Apache 2.0 license"/></a>
247
+ <a href="pyproject.toml"><img src="https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-3776ab" alt="Python versions 3.10 | 3.11 | 3.12 | 3.13 | 3.14 for tigrbl-kernel"/></a>
248
+ <a href="https://github.com/tigrbl/tigrbl/blob/master/docs/README.md"><img src="https://img.shields.io/badge/workspace-core-1f6feb" alt="Workspace group for tigrbl-kernel"/></a>
249
+ </div>
250
+
251
+ ## What is tigrbl-kernel?
252
+
253
+ Kernel orchestration for composing Tigrbl runtime plans, bindings, operation dispatch, and optimized ASGI execution.
254
+
255
+ ## Why use tigrbl-kernel?
256
+
257
+ Use it when you need this foundational Tigrbl layer directly as a small, focused dependency.
258
+
259
+ ## When should I install tigrbl-kernel?
260
+
261
+ Install it for extension packages, package-local tests, or internals that need this boundary without the whole facade.
262
+
263
+ ## Who is tigrbl-kernel for?
264
+
265
+ Framework maintainers, extension authors, and advanced users composing Tigrbl from split packages.
266
+
267
+ ## Where does tigrbl-kernel fit?
268
+
269
+ `tigrbl-kernel` lives at `pkgs/core/tigrbl_kernel` and serves a focused layer in the split Tigrbl framework.
270
+
271
+ ## How does tigrbl-kernel work?
272
+
273
+ It owns a narrow layer in the split workspace and is consumed by higher-level packages through explicit dependencies.
274
+
275
+ ## Certification Status
276
+
277
+ - Package status: governed package in the `tigrbl/tigrbl` workspace.
278
+ - Governance source: [SSOT registry](https://github.com/tigrbl/tigrbl/blob/master/.ssot/registry.json).
279
+ - Release evidence: [publish workflow](https://github.com/tigrbl/tigrbl/actions/workflows/publish.yml) validates package builds, tests, GitHub release assets, and PyPI publication for managed packages.
280
+ - Local certification guard: `pkgs/core/tigrbl_tests/tests/unit/test_package_badges_and_notices.py` verifies every package README keeps the Discord badge, Apache 2.0 badge, explicit Python-version badge, `LICENSE`, and `NOTICE`.
281
+ - Scope note: this README documents the package boundary. Runtime feature support remains governed by `.ssot/` entities and the conformance docs linked below.
320
282
 
321
283
  ## Install
322
284
 
285
+ ```bash
286
+ uv add tigrbl-kernel
287
+ ```
288
+
323
289
  ```bash
324
290
  pip install tigrbl-kernel
325
291
  ```
326
292
 
327
- ## Package discovery
293
+ ## Surface Coverage
294
+
295
+ | Surface | Value |
296
+ |---|---|
297
+ | PyPI package | [`tigrbl-kernel`](https://pypi.org/project/tigrbl-kernel/) |
298
+ | Repository path | [`pkgs/core/tigrbl_kernel`](https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_kernel) |
299
+ | Python import root | `tigrbl_kernel` |
300
+ | Console scripts | none declared |
301
+ | Entry points | none declared |
302
+ | Optional extras | none declared |
303
+ | Legal files | `LICENSE`, `NOTICE` |
304
+ | Supported Python | `3.10 | 3.11 | 3.12 | 3.13 | 3.14` |
305
+
306
+ ## What It Owns
307
+
308
+ `tigrbl-kernel` owns the `foundational framework package` boundary. It should be installed when you need this package's focused responsibility without assuming every other Tigrbl workspace package is present.
309
+
310
+ Implementation orientation:
311
+ - `tigrbl_kernel`: _build, _compile, atoms, cache, callbacks, contract_classification, core, eventkey, eventkey_hooks, events, helpers, hook_types
312
+
313
+ ## Public API and Import Surface
314
+
315
+ - Import roots: `tigrbl_kernel`.
316
+ - Public symbols: `BatchOpPlan`, `ExecutionBackend`, `Kernel`, `OpView`, `PackedKernel`, `RustBackendConfig`, `RustPlan`, `SchemaIn`, `SchemaOut`, `build_kernel_plan`, `build_packed_kernel`, `build_packed_kernel_measurement_view`.
317
+ - Workspace dependencies: [`tigrbl-typing`](https://pypi.org/project/tigrbl-typing/), [`tigrbl-atoms`](https://pypi.org/project/tigrbl-atoms/), [`tigrbl-core`](https://pypi.org/project/tigrbl-core/).
318
+ - External runtime dependencies: none declared.
319
+
320
+ ## Usage Examples
321
+
322
+ ### Verify the installed package
323
+
324
+ ```bash
325
+ python -m pip show tigrbl-kernel
326
+ python - <<'PY'
327
+ from importlib.metadata import version
328
+ print(version("tigrbl-kernel"))
329
+ PY
330
+ ```
331
+
332
+ ### Import the package boundary
333
+
334
+ ```python
335
+ import importlib
336
+
337
+ module = importlib.import_module("tigrbl_kernel")
338
+ print(module.__name__)
339
+ ```
340
+
341
+ ### Import a public symbol
342
+
343
+ ```python
344
+ from tigrbl_kernel import BatchOpPlan
345
+
346
+ print(BatchOpPlan)
347
+ ```
348
+
349
+ ### Use with the facade when building applications
350
+
351
+ ```bash
352
+ uv add tigrbl tigrbl-kernel
353
+ python - <<'PY'
354
+ import tigrbl
355
+ print(tigrbl.__name__)
356
+ PY
357
+ ```
358
+
359
+ ## How To Choose This Package
360
+
361
+ Choose `tigrbl-kernel` when the quick-answer table matches your use case. Choose [`tigrbl`](https://pypi.org/project/tigrbl/) instead when you want the full public facade. Choose a lower-level package such as [`tigrbl-core`](https://pypi.org/project/tigrbl-core/), [`tigrbl-base`](https://pypi.org/project/tigrbl-base/), or [`tigrbl-runtime`](https://pypi.org/project/tigrbl-runtime/) when you are building framework extensions or testing a specific internal boundary.
328
362
 
329
- `tigrbl-kernel` is described for package indexes, search engines, answer engines, and AI coding tools as: Kernel orchestration for composing Tigrbl runtime plans, bindings, operation dispatch, and optimized ASGI execution.
363
+ ## Related Packages
330
364
 
331
- Use `tigrbl-kernel` when you need Tigrbl's schema-first ASGI package graph for REST APIs, JSON-RPC APIs, OpenAPI documentation, OpenRPC documentation, SQLAlchemy-backed models, Pydantic validation, typed operation specs, runtime dispatch, and installable engine or application extensions.
365
+ - [`tigrbl-typing`](https://pypi.org/project/tigrbl-typing/)
366
+ - [`tigrbl-atoms`](https://pypi.org/project/tigrbl-atoms/)
367
+ - [`tigrbl-core`](https://pypi.org/project/tigrbl-core/)
368
+ - [`tigrbl`](https://pypi.org/project/tigrbl/)
369
+ - [`tigrbl-base`](https://pypi.org/project/tigrbl-base/)
370
+ - [`tigrbl-runtime`](https://pypi.org/project/tigrbl-runtime/)
332
371
 
333
- Discovery terms: tigrbl, ASGI, schema-first API framework, REST API, JSON-RPC API, OpenAPI documentation, OpenRPC documentation, SQLAlchemy models, Pydantic validation, typed validation, operation dispatch, engine plugins, api, json-rpc, rest, sqlalchemy, pydantic, kernel, orchestration, dispatch, openapi, openrpc, schema-first.
372
+ ## Documentation Links
334
373
 
335
- ## Package-local entry point
374
+ - [Workspace docs](https://github.com/tigrbl/tigrbl/blob/master/docs/README.md)
375
+ - [Package catalog](https://github.com/tigrbl/tigrbl/blob/master/docs/developer/PACKAGE_CATALOG.md)
376
+ - [Package layout](https://github.com/tigrbl/tigrbl/blob/master/docs/developer/PACKAGE_LAYOUT.md)
377
+ - [Current target](https://github.com/tigrbl/tigrbl/blob/master/docs/conformance/CURRENT_TARGET.md)
378
+ - [Current state](https://github.com/tigrbl/tigrbl/blob/master/docs/conformance/CURRENT_STATE.md)
379
+ - [SSOT registry](https://github.com/tigrbl/tigrbl/blob/master/.ssot/registry.json)
380
+ - [Release workflow](https://github.com/tigrbl/tigrbl/actions/workflows/publish.yml)
336
381
 
337
- This file is a package-local distribution entry point.
338
- It is not the authoritative location for repository governance, current target status, current state reporting, certification claims, or release evidence.
382
+ ## Support
339
383
 
340
- ## Canonical repository docs
384
+ - Community: [Discord](https://discord.gg/K4YTAPapjR).
385
+ - Issues: [GitHub Issues](https://github.com/tigrbl/tigrbl/issues).
386
+ - Repository: [pkgs/core/tigrbl_kernel](https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_kernel).
341
387
 
342
- - `README.md`
343
- - `docs/README.md`
344
- - `docs/conformance/CURRENT_TARGET.md`
345
- - `docs/conformance/CURRENT_STATE.md`
346
- - `docs/conformance/NEXT_STEPS.md`
347
- - `docs/governance/DOC_POINTERS.md`
348
- - `docs/developer/PACKAGE_CATALOG.md`
349
- - `docs/developer/PACKAGE_LAYOUT.md`
388
+ ## Package-local Boundary
350
389
 
351
- ## Package identity
390
+ This README is the package-local distribution entry point for `tigrbl-kernel`. It answers install, usage, API, ownership, and certification-orientation questions for this package. Broader architectural decisions, release status, and cross-package proof chains remain in the repository-level docs and SSOT registry.
352
391
 
353
- - canonical repository: `https://github.com/tigrbl/tigrbl`
354
- - organization: `https://github.com/tigrbl`
355
- - social: `https://discord.gg/K4YTAPapjR`
356
- - package path: `https://github.com/tigrbl/tigrbl/tree/master/pkgs/core/tigrbl_kernel`
357
- - workspace path: `pkgs/core/tigrbl_kernel`
358
- - workspace class: core Python package
359
- - implementation layout: `tigrbl_kernel/`
392
+ ## License
360
393
 
361
- Long-form repository documentation is governed from `docs/`.
394
+ Licensed under the Apache License, Version 2.0. See `LICENSE`, `NOTICE`, and the official [Apache 2.0 license text](https://www.apache.org/licenses/LICENSE-2.0).
362
395
 
@@ -1,9 +1,10 @@
1
1
  tigrbl_kernel/__init__.py,sha256=9hH7UWbqXuR5F0o0doSOjwLiy6KCHB6qZr5sS_O6Kr0,2813
2
- tigrbl_kernel/_build.py,sha256=QDzkFIknjj2J7wdq1EZ3D3lg3OO2IzYaa84Dc78mPW0,50060
2
+ tigrbl_kernel/_build.py,sha256=xLaLHsVWSeY3mefyCEnGYEGzHlyMU_1SGgyr7T6hUeY,50369
3
3
  tigrbl_kernel/_compile.py,sha256=hQ3UscZ_zrCodWZfTe7mZ7I3MBMJzIxqF1SZLycRL9A,7771
4
4
  tigrbl_kernel/atoms.py,sha256=ZNGj2PBUMu1BvPjNYlz4_vu4vhx4gD20C7zNE_GKfe8,12253
5
5
  tigrbl_kernel/cache.py,sha256=NxuqvH03xYwcQoY86ie1hmtDeA36XdiLmNyx2ImLQ6k,2596
6
6
  tigrbl_kernel/callbacks.py,sha256=r_ZI19nbAoDM-uf-FMUmWWAF9WaEKJK3Fpg-58ui8a4,772
7
+ tigrbl_kernel/contract_classification.py,sha256=5ZbhtJr88VtIPAtPWD5eL4BKKQU734kZeMEvEqh9cvU,6009
7
8
  tigrbl_kernel/core.py,sha256=3lR3VrlHOFiP-POLluEpBKWeGDdaqX8tJYHQ0eRluP0,6667
8
9
  tigrbl_kernel/eventkey.py,sha256=aTwzBkAjeC-kaEUItL1Mcfcq_Ur1fUbZDY6XxKdwWdg,1980
9
10
  tigrbl_kernel/eventkey_hooks.py,sha256=Cbkev0GXgSH27dlFoVeaKC7XuQ9yJq4bI1Mlc3qB4Qs,2480
@@ -20,7 +21,7 @@ tigrbl_kernel/opchannel_capabilities.py,sha256=5zM9-1fc-W8BXyXF4VVWrIsmdwCH5xLra
20
21
  tigrbl_kernel/opview_compiler.py,sha256=DbZ3E0GYl_JEgExELjaNE7v2W4hl9LqvAB-CE5TwP7o,5370
21
22
  tigrbl_kernel/ordering.py,sha256=92elylml2LPDYiSnjj4QHNM3OR9utxN3Al9g-UJsyJ0,13542
22
23
  tigrbl_kernel/payload.py,sha256=eyHo-cAUXLWQQ9-xZVOl_rjFudA-djMZji9hoK-v9IE,2167
23
- tigrbl_kernel/protocol_bindings.py,sha256=A95wOqq4xrtJsZrM2TGhbDGJ2aLs23WVUmPq4V-x7iM,4381
24
+ tigrbl_kernel/protocol_bindings.py,sha256=u3XcmqyX3MJqyUUkUgKSBEMvM8hXCwwP_eT-n_GqfEQ,9985
24
25
  tigrbl_kernel/protocol_chains/__init__.py,sha256=iSnOw5R4ahGypm59bQgPpsBwFXbPcnKPozjSfc9N6GQ,151
25
26
  tigrbl_kernel/protocol_chains/http_stream.py,sha256=5dY86DBJzcGcmrvOuDJo8M0cjLdeSmNAa_fo9zHuBdE,1123
26
27
  tigrbl_kernel/protocol_chains/http_unary.py,sha256=7EABv0bUAJ5UI3TWP-S-8LtgwZJLj5pw-ZnZ_8qiD4g,1776
@@ -37,15 +38,16 @@ tigrbl_kernel/rust_plan.py,sha256=p4A46oOVtqamGJS_x_RGsGJo6IN81LKa1_KmGstyxoM,34
37
38
  tigrbl_kernel/rust_spec.py,sha256=ICfIenmocRucLunvSvS91L0vli6YSMBGWnWSGN-9ppE,14668
38
39
  tigrbl_kernel/segment_fusion.py,sha256=zbPNGd1eFJGFZHJxrfanaNMyrJjJw1aPRsl_jJfN4sM,1607
39
40
  tigrbl_kernel/subevent_handlers.py,sha256=4j4780QjQiqY4qpRsyxrPh_znRK3SkLAFDN_x6eidwU,1779
40
- tigrbl_kernel/subevent_taxonomy.py,sha256=6lhJV6lxEoYuUMT4Ysmv_9kHSRaE9P32GTP2Cxc5Yt8,1995
41
+ tigrbl_kernel/subevent_taxonomy.py,sha256=67RJ7ZkfJMLfa7evzVfTlFqtdWSnhp65zwA4qhNwSG8,2274
41
42
  tigrbl_kernel/trace.py,sha256=dWILtq9seOj-WwCx__Pk_VZAbFIN1tqTbs0gIndYkU8,9816
42
43
  tigrbl_kernel/transaction_units.py,sha256=_NeQHycjjDoLG1kmD74o10wDQpdA5qjs-Sd_fxPb0_E,863
43
44
  tigrbl_kernel/transport_atoms.py,sha256=enfNCMuLgLMKi8zraMm-QOk7ys5d9kcP-WPr0-wK6KE,2136
44
45
  tigrbl_kernel/transport_events.py,sha256=qogvEAAPPX_Y6V5evGuqvFCpIePYK4ryLmCDT9JHkFM,3777
45
46
  tigrbl_kernel/types.py,sha256=BM_uPtF17MSI-q2ya93BWheAlPxOu-NaflwmxuicutA,2198
46
47
  tigrbl_kernel/utils.py,sha256=iCcm0flQfAi4w4cbVEaq6yiPR-kAoXx2owiRpbBf-0o,6953
47
- tigrbl_kernel/webtransport_events.py,sha256=cGBZAyy2PuYDV9H2nuM2bulJ3cojz6SEqL5cJK8mhTg,2267
48
- tigrbl_kernel-0.4.1.dev6.dist-info/METADATA,sha256=iPQyb71FrRoGpjKmCFaAiljeOC28uz9_mBwHEZegbPQ,27163
49
- tigrbl_kernel-0.4.1.dev6.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
50
- tigrbl_kernel-0.4.1.dev6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
51
- tigrbl_kernel-0.4.1.dev6.dist-info/RECORD,,
48
+ tigrbl_kernel/webtransport_events.py,sha256=MqWpnB4QvCbGgV8Sk-247EugbsXrfmDeY5YzLv84URo,8858
49
+ tigrbl_kernel-0.4.2.dev4.dist-info/METADATA,sha256=F2HrzGzOLA71P21WZmWg3oKEiaM1UNZ4ea8GLLxdSd8,22464
50
+ tigrbl_kernel-0.4.2.dev4.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
51
+ tigrbl_kernel-0.4.2.dev4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
52
+ tigrbl_kernel-0.4.2.dev4.dist-info/licenses/NOTICE,sha256=EvJMTshzsWz43LiK-DeN2ZuLtrP49cxvlrFlJ8F_buc,221
53
+ tigrbl_kernel-0.4.2.dev4.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.4.0
2
+ Generator: poetry-core 2.4.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -0,0 +1,7 @@
1
+ Tigrbl
2
+ Copyright 2026 Swarmauri
3
+
4
+ This product includes software developed by Swarmauri.
5
+
6
+ Tigrbl is licensed under the Apache License, Version 2.0.
7
+ See the LICENSE file distributed with this work for the full license text.