Repository navigation
Expand file tree
/
Copy pathartifacts.yaml
More file actions
269 lines (253 loc) · 13.3 KB
/
Copy pathartifacts.yaml
File metadata and controls
269 lines (253 loc) · 13.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
# The artifact registry — one place to see what every Switch artifact is, and
# what it can talk to.
#
# Two questions, kept deliberately separate, because conflating them is the
# problem this file exists to solve:
#
# `artifacts` — WHERE each thing is. Its release version. Says nothing
# whatsoever about compatibility.
# `contracts` — WHETHER two things can talk. Named interfaces, each carrying
# one integer revision.
#
# They move independently. A release that changes nothing on the wire bumps its
# version and leaves its contracts alone. Never derive one from the other.
#
# ── Editing this file ────────────────────────────────────────────────────────
#
# It is the only place a human declares any of this. Run `just artifacts` after
# editing. CI fails if the generated modules are stale, if one has been
# hand-edited, or if a `declared_in` file disagrees with what is written here.
#
# ── About `declared_in` ──────────────────────────────────────────────────────
#
# Some versions have a mandatory home that a packaging ecosystem owns and reads:
# `importlib.metadata` reads pyproject, `npm publish` reads package.json, and
# both release workflows verify the git tag against them. Those files stay
# authoritative for their tooling; `declared_in` records where, and the registry
# is CHECKED against them rather than generating into them.
#
# An artifact with no `declared_in` has no such home — nothing but this file
# declares it, so this file is its source and the constant is generated.
#
# ── About `version_from` ─────────────────────────────────────────────────────
#
# Some things Switch publishes have no version of their own: they ship inside
# another artifact's release and are stamped with its version at package time,
# which is what lets a single tag pin the whole stack. `version_from` names the
# artifact they inherit from.
#
# They are listed here rather than left out, because a registry that claims to
# be the single source of truth cannot be silent about things we publish. And
# `version_from` states the fact — "this has no version of its own" — as data
# rather than leaving it to be inferred from an absence. Nothing duplicates a
# number, so nothing can drift.
#
# Exactly one of `version` or `version_from` per artifact.
#
# ── Contract revisions ───────────────────────────────────────────────────────
#
# speaks — the newest revision the artifact implements
# accepts — the oldest revision it still handles
#
# Two peers are compatible when their [accepts, speaks] ranges overlap; they
# then operate at the lower of the two `speaks`.
#
# Raise `speaks` when you change an interface. Raise `accepts` only when
# dropping support for an older revision — that is a breaking change for every
# peer still on it, and it can never be raised past what is running in the
# field.
artifacts:
switch-core:
description: The backend service, and the gateway API it serves.
version: 0.31.0
declared_in: core/pyproject.toml
published_as: switch-core
switch-console:
description: The desktop app.
version: 0.40.1
declared_in: console/apps/switch-console-desktop/package.json
# Which switch-core release this app build bundles and pulls for
# local-server mode. DELIBERATELY NOT switch-core's version above.
#
# They coincide today, and must not be wired together: bumping
# core/pyproject.toml is the first step of cutting a switch-core release, so
# a derived pin would immediately point Switch Console at images that do not
# exist on the registry yet and break local-server mode for everyone on
# main. This moves only when Switch Console is ready to bundle that release —
# in lockstep with the bundled compose.
pins:
switch-core: 0.31.0
# What the canary build bundles instead, so a switch-core pre-release can be
# tried on canary without moving stable. Never older than the stable pin
# (`just artifacts` refuses it). When a stable switch-core release ships,
# move `pins` to it; drop the canary pin back to it too unless a newer
# pre-release is being tried.
canary_pins:
switch-core: 0.31.0
agent-runtime:
description: >-
The Switch protocol client and the MCP tool surface each session host
serves. Bundled into Switch Console and its sidecar, not published.
version: 0.8.0
declared_in: console/packages/switch-agent-runtime/package.json
sidecar:
description: >-
The remote runtime Switch Console deploys to an agent host. Deployed rather
than published, so nothing else declares its version — this file is its
only source and the constant is generated from it.
version: 1.9.12
# ── Published under the switch-core release ────────────────────────────────
# No version of their own: `helm package` and the image build stamp them with
# the switch-core release version, so one tag pins the whole stack.
gateway:
description: The operator dashboard image.
version_from: switch-core
published_as: gateway
setup:
description: The collaboration-bridge seeder image.
version_from: switch-core
published_as: setup
helm-chart:
description: >-
The Kubernetes chart. Its values.yaml is a consumer-facing interface —
operators write a values file against those keys and pin the chart
version — but nothing first-party consumes it, so there is no second side
to declare a contract against. A breaking values change is therefore
invisible to this model today; that gap is deliberate and belongs with
the rest of Part 2, not papered over with a revision nobody compares.
version_from: switch-core
published_as: charts/switch
hosted-controller:
description: >-
The hosted controller image, which provisions Switch cloud machines for
switch-core.
version_from: switch-core
published_as: switch-hosted-controller
hosted-controller-chart:
description: >-
The hosted controller's Kubernetes chart, pinned to its image by digest
like the switch chart.
version_from: switch-core
published_as: charts/switch-hosted-controller
compose:
description: >-
The standalone compose file, published as an OCI artifact. Unlike the
chart this one has a real peer — Switch Console's local-server mode drives it
— so it does carry a contract, below.
version_from: switch-core
published_as: standalone-compose
contracts:
agent-protocol:
description: >-
The wire protocol between switch-core's agent bridge and an agent
runtime: the event stream, its frame shapes, the resume cursor, and the
tool-call channel.
# Revision 2 adds the connection incarnation: the server publishes it on
# `connection_state`, the client returns it on every heartbeat, and a tick
# bearing a superseded one — or, from a client that declares this revision,
# none at all — is refused. The same revision gives the `evicted` frame a
# stable `code` and the room it applies to, and gives a refused heartbeat
# the same treatment (`taken_over`, `no_stream`, `unfenced`), so a client
# no longer decides recoverable-or-fatal by matching prose. `taken_over` is
# the one ending a client must not answer by reopening: reopening is itself
# a takeover. All of it is additive and `accepts` stays at 1: a revision-1
# client sends no incarnation and is recorded as unfenceable rather than
# refused.
#
# Revision 3 moves unread accounting onto the wire. A delivered frame may
# carry `missed` — how far behind the reader is on unaddressed chatter in
# that frame's room, as a count that may be null with a reason — and a gap
# frame may carry `rooms`, naming which rooms lost events. Both are
# additive and `accepts` stays at 1: a client that reads neither behaves as
# it did, and a server that sends neither leaves the client with no count
# to render rather than a wrong one.
#
# Revision 4 adds the `approval_outcome` frame on an agent's own stream
# (scope "all"): an answer to, or the expiry of, an approval request the
# agent's host opened, owed until the host acknowledges it. It is sent only
# to a client that declares this revision — an older client hands frames it
# does not recognise to its room-event path, and one without a `type`
# breaks it — so `accepts` stays at 1 and nothing changes for them.
#
# Revision 5 adds the `session_command` frame on an agent's own stream
# (scope "all"): a command for one of the agent's sessions, sent by its
# owner from Switch Console and relayed without being stored. Sessions no
# longer fetch commands from a queue on the server. Sent only to a client
# that declares this revision, for the same reason as revision 4.
#
# Revision 6 moves session placement to the agent's watcher. Delivered
# events no longer carry `session_id`; the watcher states its placements on
# `POST /agents/{id}/connection/placements`, and a connection whose room
# claim or session placement another connection took over is sent
# `room_released` (`room_id`, and the session placed there or null). The
# frame is sent only to a client that declares this revision, for the same
# reason as revision 4.
#
# Revision 7 adds the hosted worker. A hosted agent's stream opens only
# with the worker capability of its launch's current revision
# (`X-Switch-Worker-Capability`, plus the host identity headers), and from
# a client that declares this revision (`upgrade_required` otherwise). The
# attached worker alone is sent `worker_attached`, `relay`, `relay_cancel`,
# `wake`, `mailbox_cancel`, `operation` and `credential`. Nothing changes
# for a local agent, so `accepts` stays at 1.
#
# Revision 8 moves the connection onto one WebSocket,
# `/agents/{id}/connection/ws`, which carries the frames as JSON
# (`event`, `data`, `id`) and the heartbeat: the server sends `ping` every
# heartbeat interval and the client answers `pong` with its cursor. The
# token is checked once, when the socket opens. A hosted worker opens it
# with the headers and under the rules revision 7 set for the stream. It
# is meant to replace the Server-Sent Events stream and
# `POST /connection/beat`, which would make it
# the first revision that is not additive, but that removal is deferred:
# the stream and the beat are kept beside the socket for a compatibility
# window, so `accepts` stays at 1 on both sides. switch-core serves either
# transport, and agent-runtime prefers the socket and falls back to the
# stream and its beat against a server with no socket. Both go, and
# `accepts` rises to 8, once `switch.agents.connected{transport:sse}` stays
# at zero.
#
# Revision 9 makes the resume point a position: one number per source, the
# agent's own events (`s`) and each room's conversation (`r:<room>`), so a
# stream can read the conversation from the rooms themselves instead of a
# copy per agent. A client that declares it opens with `start_at` (a JSON
# position, or "head"), reads `position` (the event's source and number)
# off each delivered event, reports its position on every heartbeat
# (`position` beside `cursor`), and gets `resumed_position` on a gap. The
# integer fields stay, so a client that knows only them is served as
# before: a stream that reads only the agent's own events is one number
# already, and one that reads the conversation too is handed tickets that
# stand for its position. Additive, so `accepts` stays at 1.
artifacts:
switch-core: { speaks: 9, accepts: 1 }
agent-runtime: { speaks: 9, accepts: 1 }
gateway-api:
description: >-
The HTTP API switch-core serves to first-party UI clients. Excludes the
authentication surface, which is deliberately frozen and unversioned so
that a client can always authenticate far enough to be told what is
wrong.
artifacts:
switch-core: { speaks: 1, accepts: 1 }
switch-console: { speaks: 1, accepts: 1 }
stack-compose:
description: >-
The interface of the published standalone compose artifact — its service
names, profiles, and environment variables — which Switch Console's
local-server mode drives.
artifacts:
switch-console: { speaks: 2, accepts: 1 }
compose: { speaks: 2, accepts: 1 }
sidecar-control:
description: >-
How Switch Console controls a deployed sidecar on a remote host: the ready
file it reads, and the control channel it opens.
artifacts:
switch-console: { speaks: 1, accepts: 1 }
sidecar: { speaks: 1, accepts: 1 }
db-schema:
description: >-
The database schema switch-core expects. Internal to switch-core — this
contract is never disclosed in an externally facing response.
artifacts:
switch-core: { speaks: 1, accepts: 1 }