stitchkit 0.94.1 → 0.95.1

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/dist/agent-runtime/provider-origin.d.ts +7 -0
  3. package/dist/agent-runtime/provider-origin.d.ts.map +1 -1
  4. package/dist/agent-runtime/run-operation-lifecycle.d.ts.map +1 -1
  5. package/dist/application/directory-inbox-contract.d.ts +129 -0
  6. package/dist/application/directory-inbox-contract.d.ts.map +1 -0
  7. package/dist/application/directory-inbox.d.ts +3 -0
  8. package/dist/application/directory-inbox.d.ts.map +1 -0
  9. package/dist/application/process-lifecycle-transitions.d.ts +175 -0
  10. package/dist/application/process-lifecycle-transitions.d.ts.map +1 -0
  11. package/dist/application/process-lifecycle.d.ts +3 -131
  12. package/dist/application/process-lifecycle.d.ts.map +1 -1
  13. package/dist/entrypoints/agent-runtime/harness.js +1 -1
  14. package/dist/entrypoints/agent-runtime.js +1 -1
  15. package/dist/entrypoints/application/directory-inbox.d.ts +11 -0
  16. package/dist/entrypoints/application/directory-inbox.d.ts.map +1 -0
  17. package/dist/entrypoints/application/directory-inbox.js +221 -0
  18. package/dist/entrypoints/application.d.ts +3 -1
  19. package/dist/entrypoints/application.d.ts.map +1 -1
  20. package/dist/entrypoints/application.js +151 -56
  21. package/dist/entrypoints/server.js +6 -219
  22. package/dist/index-5jrd0yng.js +114 -0
  23. package/dist/{index-7f68ghdn.js → index-8zja4pkf.js} +23 -1
  24. package/dist/index-tm88avnj.js +221 -0
  25. package/llms/application-directory-inbox.txt +9 -0
  26. package/llms/application.2.txt +7 -5
  27. package/llms/application.txt +88 -24
  28. package/llms/stitchkit.txt +4 -3
  29. package/llms/{upgrading-0.37.0-0.50.0.txt → upgrading-0.37.0-0.53.0.txt} +25 -3
  30. package/llms/{upgrading-0.53.0-0.67.0.txt → upgrading-0.55.0-0.68.0.txt} +53 -25
  31. package/llms/{upgrading-0.68.0-0.94.0.txt → upgrading-0.69.0-0.95.0.txt} +45 -53
  32. package/llms/upgrading.txt +3 -3
  33. package/llms-full.txt +141 -32
  34. package/llms.txt +6 -5
  35. package/package.json +5 -1
@@ -815,39 +815,74 @@ application-owned.
815
815
  ## Durable process facts and owner notifications
816
816
 
817
817
  `createProcessLifecycleLedger` records a bounded versioned list of process runs.
818
- Start, readiness and shutdown target both `runId` and `pid`, so hot reload and
819
- PID reuse cannot close the wrong generation. A start classifies what happened to
820
- the newest run before it, and the classification is decided by three facts —
821
- whether that run recorded its own exit, whether the pid is the same, and whether
822
- the version changed:
818
+ Start, readiness, draining and shutdown target both `runId` and `pid`, so hot
819
+ reload and PID reuse cannot close the wrong generation. `lifecycleLedgerResource`
820
+ drives it from the kernel's phases: `start` records the start, `activate` the
821
+ readiness, `stopAdmission` the drain — the run's `unavailableAt`, the moment it
822
+ stops answering new work — and `close` / `force` the stop. A run that stops
823
+ without ever becoming ready (a failed startup rolls back with `close` only)
824
+ ends as `startup-failed`, not `clean`.
825
+
826
+ A start classifies what happened to the newest run before it, and the
827
+ classification is decided by four facts — whether that run recorded its own
828
+ exit, whether the pid is the same, whether the version changed and whether it
829
+ ever became ready:
823
830
 
824
831
  | newest run | same pid | version | `previousExit` | predecessor's `termination` |
825
832
  |---|---|---|---|---|
826
833
  | none | — | — | `first-boot` | — |
827
- | recorded `stoppedAt` | — | — | `clean` / `forced` / `abnormal` as recorded | unchanged |
828
- | still `active` | yes | — | `hot-reload` | `hot-reload`, closed at the new start |
829
- | still `active` | no | changed | `handoff` | stays `active`; it records its own stop later |
830
- | still `active` | no | same | `abnormal` (default) | `abnormal`, closed at the new start — an upper bound, the crash time is unknown |
834
+ | recorded `stoppedAt` | — | — | `clean` / `forced` / `startup-failed` / `abnormal` as recorded | unchanged |
835
+ | still open | yes | — | `hot-reload` | `hot-reload`, closed at the new start |
836
+ | still open | no | changed | `handoff` | stays open; it records its own stop later |
837
+ | still open, was ready | no | same | `abnormal` (default) | `abnormal`, closed at the new start — an upper bound, the crash time is unknown |
838
+ | still open, never ready | no | same | `startup-failed` (default) | `startup-failed`, closed at the new start |
839
+
840
+ Every other open run the start finds was abandoned too and is closed the same
841
+ way — two crashes in a row leave two open runs, and both are closed, not only
842
+ the newest. Open means `active` or `draining`.
831
843
 
832
844
  A version of `unknown` on either side is never a version change, so a dev build
833
845
  after a crashed release reads `abnormal`, not `handoff`. `forced` means the
834
846
  process itself acknowledged a kill; `abnormal` means a successor found it dead.
835
847
 
836
- The last row is a choice, and the default is the single-process deployment:
848
+ ### Downtime is the window nobody answered in
849
+
850
+ `ReadyFact.downtimeMs` is `readyAt` of the new run minus `unavailableAt` of the
851
+ last run that served — the predecessor's drain plus the successor's boot. A
852
+ 110 s forced drain followed by a 4 s boot is 114 s of downtime; the gap between
853
+ the two processes alone would read 4 s before and after that defect was fixed.
854
+ Runs that never became ready answered nobody, so the count passes them: after a
855
+ failed start it still opens where the last serving run stopped answering. It is
856
+ `0` when that run still answers (a handoff whose predecessor has not drained
857
+ yet) and `null` on first boot and when the end was never timed — a crash or a
858
+ hot reload records no `unavailableAt`. `unavailableSince` names the moment it is
859
+ counted from. The gap between two processes is still there, under its own
860
+ name: `StartFact.processGapMs` is the start minus the predecessor's recorded
861
+ stop.
862
+
863
+ The ledger records what the process says while it can say it. stitchkit never
864
+ calls `process.exit` (ADR 0074), so a stop is recorded on the kernel's `close`
865
+ or `force` path, both awaited. An application that exits the process on its
866
+ own before its shutdown settles loses that last write; the successor then
867
+ finds the run open and closes it as `abnormal`, keeping the `unavailableAt`
868
+ already recorded — which is why the drain moment is written first, at
869
+ `stopAdmission`, and the downtime survives a stop that was never recorded.
870
+
871
+ The `abnormal` default is a choice, and it is the single-process deployment:
837
872
  one process per build, a new pid of the same build means the old one stopped
838
- answering. Where two processes of **one** build overlap on purpose — a
839
- cluster, a zero-downtime reload of the same build — pass
840
- `sameVersionOverlap: 'handoff'` to the ledger, and the predecessor stays
841
- `active` until it records its own shutdown. The cost of that setting is
842
- symmetric: a real crash under it is reported as a handoff and the dead run
843
- stays `active` in the ledger until retention drops it. The list is kept in the
844
- order the transitions wrote it — every write goes through one atomic update,
845
- so that order is the causal one, and a successor whose clock lags its
846
- predecessor still finds it at the head; `startedAt` is data, not the sort key.
847
- Retention (`retain`, default 20) drops finished runs first and an active one —
848
- a live handoff predecessor — only when nothing finished is left. Facts are
849
- published through the ledger's own subscription and resource value;
850
- `ApplicationEventSink` remains the strict application-state stream.
873
+ answering. Where processes of **one** build overlap on purpose — a cluster, a
874
+ zero-downtime reload of the same build — pass `sameVersionOverlap: 'handoff'`
875
+ to the ledger, and every open run stays open until it records its own
876
+ shutdown. The cost of that setting is symmetric: a real crash under it is
877
+ reported as a handoff and the dead run stays open in the ledger until
878
+ retention drops it. The list is kept in the order the transitions wrote it —
879
+ every write goes through one atomic update, so that order is the causal one,
880
+ and a successor whose clock lags its predecessor still finds it at the head;
881
+ `startedAt` is data, not the sort key. Retention (`retain`, default 20) drops
882
+ finished runs first and an open one — a live handoff predecessor — only when
883
+ nothing finished is left. Facts are published through the ledger's own
884
+ subscription and resource value; `ApplicationEventSink` remains the strict
885
+ application-state stream.
851
886
 
852
887
  `createNotificationOutbox` is the transport-neutral durable delivery side. It
853
888
  persists before send, claims an item with an expiring lease, carries one stable
@@ -870,7 +905,36 @@ deadline holds `stop()` — and so the resource's `force()` — for that one cal
870
905
  so give the transport a timeout. Superseding a key that is being sent right now
871
906
  does not recall it: the send completes, and only its receipt is not written.
872
907
 
873
- Both primitives depend on the structural `StateStore`. On a server,
908
+ `createDirectoryInbox` is the receiving side for another program on the same
909
+ host: it drops entries — a release transition, say — into a directory, and
910
+ the application takes each one at least once. It is a managed resource of its
911
+ own (`createDirectoryInbox({ id, directory, schema, handle })`, imported from
912
+ `stitchkit/application/directory-inbox` because it touches files; its types
913
+ and schemas are in `stitchkit/application`) and publishes a handle with
914
+ `flush()` and `state()`. The producer writes each entry
915
+ atomically as `<name>.json` — a temporary name, then a rename; names that start
916
+ with a dot are never read. Entries are delivered in name order, and only after
917
+ `activate`: none reaches the application before it is ready. `stopAdmission`
918
+ stops taking new ones, `drain` waits for the one in flight, and `force` aborts
919
+ the `signal` its handler was given.
920
+
921
+ Taken and done are separate durable records. Before `handle` runs, the entry
922
+ is claimed with a lease (`leaseMs`, default five minutes); after it returns,
923
+ a receipt replaces the claim and only then is the file removed. A process that
924
+ died mid-delivery leaves a claim, and the entry is taken again once the lease
925
+ runs out — at least once, not exactly once, so a handler that sends somewhere
926
+ should carry the entry's `key` as its idempotency key. A process that died
927
+ after the receipt leaves only the file, which the next pass removes without
928
+ delivering it again. A handler that throws is retried after a backoff from one
929
+ second doubling to five minutes; an entry that fails `schema`, is over
930
+ `maxEntryBytes` or was taken `maxAttempts` times (default 20 — the count
931
+ survives restarts, so an entry whose handling kills the process is not taken
932
+ forever) is moved to `rejected/` with its reason and reported to `onRejected`,
933
+ and the entries after it keep flowing. The state lives in
934
+ `<directory>/.inbox-state.json` under `createFileStateStore`'s inter-process
935
+ lock unless a `store` is supplied.
936
+
937
+ These primitives depend on the structural `StateStore`. On a server,
874
938
  `createFileStateStore` supplies the shared Zod-validated JSON adapter with an
875
939
  inter-process lock, unique temporary file, fsync and atomic rename. The lock
876
940
  is a file with a heartbeat: the holder refreshes its mtime every third of
@@ -76,11 +76,11 @@ own, recorded as an ADR.
76
76
  | `stitchkit/geo` | server (Bun or Node) | evolving | managed GeoIP reader generations, last-known-good reload and the optional MaxMind adapter |
77
77
  | `stitchkit/oauth` | browser **and** server | evolving | provider-neutral Authorization Code + PKCE request and one-shot callback transaction mechanics |
78
78
  | `stitchkit/google` | server (Bun or Node) | evolving | optional Google code exchange and verified OIDC identity adapter |
79
- | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 39 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
79
+ | `stitchkit/observability` | server | stable<br>_redefined in 2 of the 40 minors since 0.56.2, most recently 0.92.0_ | request/tool event projections — `createObservability`, trace context, sanitisation |
80
80
  | `stitchkit/testing` | tests on Bun or Node | stable | in-process generated clients over a real Fetch handler, plus the managed-resource conformance kit |
81
81
  | `stitchkit/declaration` | browser + build and deployment tooling (Bun or Node) | evolving | `ProjectDeclarationSchema` — the one machine-readable statement a repository makes about itself |
82
82
  | `stitchkit/react` | browser + server rendering | stable | `createCursorQuery`, `createCacheBridge`, QueryClient and `ApiError` retry policy |
83
- | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 21 of the 39 minors since 0.56.2, most recently 0.94.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
83
+ | `stitchkit/agent-runtime` | server | evolving<br>_redefined in 21 of the 40 minors since 0.56.2, most recently 0.94.0_ | optional durable conversation/run loop, history, models, prompts, fencing and events |
84
84
  | `stitchkit/agent-runtime/sandbox` | server | evolving | optional Linux Bubblewrap sessions, durable workspaces and a host HTTP credential gateway; see [sandbox guide](sandbox.md) |
85
85
  | `stitchkit/agent-runtime/testing` | tests on Bun or Node | evolving | credential-free replay, scripted provider faults, deterministic race controls and the agent-store conformance kit |
86
86
  | `stitchkit/agent-runtime/harness` | server | evolving | resource-aware process-local facade over the canonical Agent runtime; supervision stays outside |
@@ -90,11 +90,12 @@ own, recorded as an ADR.
90
90
  | `stitchkit/agent-runtime/sqlite/bun` | server (Bun) | evolving | durable built-in SQLite store for the agent runtime |
91
91
  | `stitchkit/agent-runtime/sqlite/node` | server (Node ≥ 22.5) | evolving | durable built-in SQLite store for the agent runtime |
92
92
  | `stitchkit-tui` | terminal (Bun) | evolving | optional official OpenTUI host over a caller-composed headless runtime |
93
- | `stitchkit/application` | browser + server | evolving<br>_redefined in 7 of the 39 minors since 0.56.2, most recently 0.83.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
93
+ | `stitchkit/application` | browser + server | evolving<br>_redefined in 8 of the 40 minors since 0.56.2, most recently 0.95.0_ | managed resource graph, readiness, admission, schedules, subtree restart and bounded shutdown |
94
94
  | `stitchkit/application/grammy` | server | evolving | isolated grammY polling and webhook lifecycle adapters |
95
95
  | `stitchkit/application/opentelemetry` | server | evolving | maps application snapshots onto an injected OpenTelemetry `Meter` |
96
96
  | `stitchkit/application/schemas` | browser + server | evolving | the application's snapshot, health and shutdown schemas alone, without the kernel |
97
97
  | `stitchkit/application/diagnostic-journal` | server | evolving | the local diagnostic journal — the one part of the kernel that spawns, locks and writes files |
98
+ | `stitchkit/application/directory-inbox` | server | evolving | the directory inbox — entries another program drops into a directory, delivered to the application at least once |
98
99
 
99
100
  Rule of thumb: browser code imports `stitchkit` and `stitchkit/react`; server
100
101
  code adds `stitchkit/server` (or `stitchkit/node` on Node) and opts into
@@ -1,14 +1,36 @@
1
- # Upgrading 0.37.0 → 0.50.0 — stitchkit docs slice
1
+ # Upgrading 0.37.0 → 0.53.0 — stitchkit docs slice
2
2
 
3
3
  > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
4
  > `llms.txt` in this package.
5
5
 
6
- Contains: Guide: Upgrading, 0.37.0 → 0.50.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.37.0 → 0.53.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.37.0 → 0.50.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.37.0 → 0.53.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.53.0
13
+
14
+ ### Realtime `emit` returns `boolean` instead of `void`
15
+
16
+ Every **call site** compiles and behaves exactly as before — the return value
17
+ is new information (`true` = accepted by the transport, `false` = dropped
18
+ while the browser client was disconnected), not a behavior change. What breaks
19
+ is **implementing** the interfaces: a test mock or app-side adapter of
20
+ `SocketIOClient` / `RealtimeClient` / `ValidatedRealtimeSocket` /
21
+ `RealtimeServer` whose `emit` returns `void` no longer typechecks.
22
+
23
+ ```ts
24
+ // before — a void mock satisfied the interface
25
+ const mock: Pick<RealtimeClient<S, C>, 'emit'> = { emit: () => {} }
26
+ // after — report acceptance (true is what a live server-side emit reports)
27
+ const mock: Pick<RealtimeClient<S, C>, 'emit'> = { emit: () => true }
28
+ ```
29
+
30
+ While migrating, consider replacing hand-rolled `if (client.connected)` guards
31
+ with the new honest surface: check `client.emit(...) === false`, or observe
32
+ drops centrally with `onDroppedEmit`.
33
+
12
34
  ## Released migration: 0.50.0
13
35
 
14
36
  ### The factory's scope union now covers per-endpoint overrides
@@ -1,14 +1,64 @@
1
- # Upgrading 0.53.0 → 0.67.0 — stitchkit docs slice
1
+ # Upgrading 0.55.0 → 0.68.0 — stitchkit docs slice
2
2
 
3
3
  > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
4
  > `llms.txt` in this package.
5
5
 
6
- Contains: Guide: Upgrading, 0.53.0 → 0.67.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.55.0 → 0.68.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.53.0 → 0.67.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.55.0 → 0.68.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.68.0
13
+
14
+ ### Make Unix transport selection explicit outside Bun
15
+
16
+ The legacy `unix` option remains a Bun convenience, but on Node or another
17
+ runtime it now fails before dispatch. Replace it with the owned adapter and close
18
+ that adapter with the application:
19
+
20
+ ```ts
21
+ // before — unsafe outside Bun: an unsupported fetch could dial baseUrl over TCP
22
+ const http = createHttpClient({ baseUrl, unix: '/run/service.sock' })
23
+
24
+ // after — Bun and Node; every dispatch and redirect stays on the socket
25
+ import { createUnixClientTransport } from 'stitchkit/server' // or stitchkit/node
26
+ const transport = createUnixClientTransport({ socketPath: '/run/service.sock' })
27
+ const http = createHttpClient({ baseUrl, fetch: transport.fetch })
28
+ // during shutdown
29
+ await transport.close()
30
+ ```
31
+
32
+ Do not automatically replay `possibly-dispatched`: a timeout or connection loss
33
+ after bytes left the process does not prove that a write did not happen.
34
+
35
+ ### Choose tolerant stream parsing explicitly
36
+
37
+ `parseNDJSON` and `parseSSE` now throw on malformed JSON, invalid UTF-8 and an
38
+ over-limit line. The default line ceiling is 1 MiB. If a feed deliberately skips
39
+ bad records, retain that policy explicitly:
40
+
41
+ ```ts
42
+ // before — malformed input disappeared implicitly
43
+ parseNDJSON(response)
44
+
45
+ // after — ordinary fail-closed path
46
+ parseNDJSON(response, { maxLineBytes: 256 * 1024 })
47
+
48
+ // after — deliberately tolerant path
49
+ parseNDJSON(response, { onParseError: (raw, error) => report(raw, error) })
50
+ ```
51
+
52
+ ### Extend exhaustive framework-error handling
53
+
54
+ If a switch makes `StitchErrorCode` exhaustive, add
55
+ `STREAM_ITEM_INVALID`, `STREAM_FRAME_TOO_LARGE`,
56
+ `STREAM_TERMINAL_MISSING` and `STREAM_LIFETIME_EXCEEDED`. A partial application
57
+ status map needs no change (ADR 0105).
58
+
59
+ The admission/channel APIs and endpoint `stream` descriptor are additive; raw
60
+ responses and raw `streamingRoute` remain supported.
61
+
12
62
  ## Released migration: 0.67.0
13
63
 
14
64
  Three application-kernel changes. Two of them fix silent failures, so the most
@@ -1150,25 +1200,3 @@ Downloaded `path` is now relative to the boundary and MIME metadata is named
1150
1200
  same way. Auth predicates must explicitly return `true`, `false`, or a plain
1151
1201
  object of context fields; replace accidental `undefined` fallthroughs with the
1152
1202
  intended boolean.
1153
-
1154
- ## Released migration: 0.53.0
1155
-
1156
- ### Realtime `emit` returns `boolean` instead of `void`
1157
-
1158
- Every **call site** compiles and behaves exactly as before — the return value
1159
- is new information (`true` = accepted by the transport, `false` = dropped
1160
- while the browser client was disconnected), not a behavior change. What breaks
1161
- is **implementing** the interfaces: a test mock or app-side adapter of
1162
- `SocketIOClient` / `RealtimeClient` / `ValidatedRealtimeSocket` /
1163
- `RealtimeServer` whose `emit` returns `void` no longer typechecks.
1164
-
1165
- ```ts
1166
- // before — a void mock satisfied the interface
1167
- const mock: Pick<RealtimeClient<S, C>, 'emit'> = { emit: () => {} }
1168
- // after — report acceptance (true is what a live server-side emit reports)
1169
- const mock: Pick<RealtimeClient<S, C>, 'emit'> = { emit: () => true }
1170
- ```
1171
-
1172
- While migrating, consider replacing hand-rolled `if (client.connected)` guards
1173
- with the new honest surface: check `client.emit(...) === false`, or observe
1174
- drops centrally with `onDroppedEmit`.
@@ -1,14 +1,56 @@
1
- # Upgrading 0.68.0 → 0.94.0 — stitchkit docs slice
1
+ # Upgrading 0.69.0 → 0.95.0 — stitchkit docs slice
2
2
 
3
3
  > Generated from the `docs/` tree by `bun run gen:llms`. The index of all slices is
4
4
  > `llms.txt` in this package.
5
5
 
6
- Contains: Guide: Upgrading, 0.68.0 → 0.94.0 (docs/guide/upgrading.md)
6
+ Contains: Guide: Upgrading, 0.69.0 → 0.95.0 (docs/guide/upgrading.md)
7
7
 
8
8
  ==============================================================================
9
- # Guide: Upgrading, 0.68.0 → 0.94.0 (docs/guide/upgrading.md)
9
+ # Guide: Upgrading, 0.69.0 → 0.95.0 (docs/guide/upgrading.md)
10
10
  ==============================================================================
11
11
 
12
+ ## Released migration: 0.95.0
13
+
14
+ One breaking change in `stitchkit/application`: the process lifecycle ledger
15
+ counts downtime as the window nobody answered in, and gains the `draining`
16
+ phase and the `startup-failed` outcome. A ledger file written by an earlier
17
+ version loads unchanged — the new `unavailableAt` reads as `null`.
18
+
19
+ 1. **Read downtime from the ready fact.** **Who must act:** a project that
20
+ reads `downtimeMs` from a `started` fact. It is gone from `StartFact`; the
21
+ number it carried is `processGapMs`. The downtime now arrives with the new
22
+ run's readiness:
23
+
24
+ ```ts
25
+ // before
26
+ ledger.subscribe((fact) => {
27
+ if (fact.type === 'started') report(fact.previousExit, fact.downtimeMs);
28
+ });
29
+ // after
30
+ ledger.subscribe((fact) => {
31
+ if (fact.type === 'started') previousExit = fact.previousExit;
32
+ if (fact.type === 'ready') report(previousExit, fact.downtimeMs);
33
+ });
34
+ ```
35
+
36
+ 2. **Handle the new values.** **Who must act:** a project that switches
37
+ exhaustively over `LifecycleTermination` (new: `draining`,
38
+ `startup-failed`), `PreviousExit` (new: `startup-failed`) or
39
+ `ProcessLifecycleFact` (new: `DrainingFact`, `type: 'draining'`). The type
40
+ checker names each switch. An open run is `active` or `draining`; a check
41
+ written as `termination === 'active'` to mean "still running" becomes
42
+ `termination === 'active' || termination === 'draining'`.
43
+ `TransitionReadyInput` is renamed `TransitionRunInput` — the same fields,
44
+ now also taken by `transitionProcessDraining`; rename the import.
45
+
46
+ 3. **Record the drain and the readiness when driving the ledger by hand.**
47
+ **Who must act:** only a project that calls `createProcessLifecycleLedger`
48
+ methods itself instead of mounting `lifecycleLedgerResource`. Call
49
+ `recordDraining()` where the application stops admitting work, before it
50
+ drains; without it downtime is counted from the recorded stop. Call
51
+ `recordReady()` when the run serves; a run stopped without it now ends
52
+ `startup-failed`, not `clean`.
53
+
12
54
  ## Released migration: 0.94.0
13
55
 
14
56
  One breaking minor that consolidates: tool options move into one group, and
@@ -1107,53 +1149,3 @@ else renderExistingRole(message)
1107
1149
 
1108
1150
  No data migration is needed. The schema still accepts all earlier records, and applications that
1109
1151
  do not exhaustively branch over these unions compile unchanged.
1110
-
1111
- ## Released migration: 0.68.0
1112
-
1113
- ### Make Unix transport selection explicit outside Bun
1114
-
1115
- The legacy `unix` option remains a Bun convenience, but on Node or another
1116
- runtime it now fails before dispatch. Replace it with the owned adapter and close
1117
- that adapter with the application:
1118
-
1119
- ```ts
1120
- // before — unsafe outside Bun: an unsupported fetch could dial baseUrl over TCP
1121
- const http = createHttpClient({ baseUrl, unix: '/run/service.sock' })
1122
-
1123
- // after — Bun and Node; every dispatch and redirect stays on the socket
1124
- import { createUnixClientTransport } from 'stitchkit/server' // or stitchkit/node
1125
- const transport = createUnixClientTransport({ socketPath: '/run/service.sock' })
1126
- const http = createHttpClient({ baseUrl, fetch: transport.fetch })
1127
- // during shutdown
1128
- await transport.close()
1129
- ```
1130
-
1131
- Do not automatically replay `possibly-dispatched`: a timeout or connection loss
1132
- after bytes left the process does not prove that a write did not happen.
1133
-
1134
- ### Choose tolerant stream parsing explicitly
1135
-
1136
- `parseNDJSON` and `parseSSE` now throw on malformed JSON, invalid UTF-8 and an
1137
- over-limit line. The default line ceiling is 1 MiB. If a feed deliberately skips
1138
- bad records, retain that policy explicitly:
1139
-
1140
- ```ts
1141
- // before — malformed input disappeared implicitly
1142
- parseNDJSON(response)
1143
-
1144
- // after — ordinary fail-closed path
1145
- parseNDJSON(response, { maxLineBytes: 256 * 1024 })
1146
-
1147
- // after — deliberately tolerant path
1148
- parseNDJSON(response, { onParseError: (raw, error) => report(raw, error) })
1149
- ```
1150
-
1151
- ### Extend exhaustive framework-error handling
1152
-
1153
- If a switch makes `StitchErrorCode` exhaustive, add
1154
- `STREAM_ITEM_INVALID`, `STREAM_FRAME_TOO_LARGE`,
1155
- `STREAM_TERMINAL_MISSING` and `STREAM_LIFETIME_EXCEEDED`. A partial application
1156
- status map needs no change (ADR 0105).
1157
-
1158
- The admission/channel APIs and endpoint `stream` descriptor are additive; raw
1159
- responses and raw `streamingRoute` remain supported.
@@ -11,9 +11,9 @@ Contains: Guide: Upgrading, general (docs/guide/upgrading.md)
11
11
 
12
12
  Load this slice, then every range slice between your installed version and the target:
13
13
 
14
- - 0.68.0 → 0.94.0: `llms/upgrading-0.68.0-0.94.0.txt`
15
- - 0.53.0 → 0.67.0: `llms/upgrading-0.53.0-0.67.0.txt`
16
- - 0.37.0 → 0.50.0: `llms/upgrading-0.37.0-0.50.0.txt`
14
+ - 0.69.0 → 0.95.0: `llms/upgrading-0.69.0-0.95.0.txt`
15
+ - 0.55.0 → 0.68.0: `llms/upgrading-0.55.0-0.68.0.txt`
16
+ - 0.37.0 → 0.53.0: `llms/upgrading-0.37.0-0.53.0.txt`
17
17
 
18
18
  # Upgrading stitchkit
19
19