stitchkit 0.94.0 → 0.95.0
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.
- package/CHANGELOG.md +104 -0
- package/dist/agent-runtime/coding-shell-process.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-listing.d.ts.map +1 -1
- package/dist/agent-runtime/coding-tool-search-patch.d.ts.map +1 -1
- package/dist/agent-runtime/contained-darwin.d.ts +38 -0
- package/dist/agent-runtime/contained-darwin.d.ts.map +1 -0
- package/dist/agent-runtime/contained-files.d.ts +11 -26
- package/dist/agent-runtime/contained-files.d.ts.map +1 -1
- package/dist/agent-runtime/contained-scan.d.ts +21 -0
- package/dist/agent-runtime/contained-scan.d.ts.map +1 -0
- package/dist/agent-runtime/contained-write.d.ts +9 -0
- package/dist/agent-runtime/contained-write.d.ts.map +1 -0
- package/dist/agent-runtime/deferred-tool-types.d.ts +3 -2
- package/dist/agent-runtime/deferred-tool-types.d.ts.map +1 -1
- package/dist/agent-runtime/harness-control.d.ts +0 -1
- package/dist/agent-runtime/harness-control.d.ts.map +1 -1
- package/dist/agent-runtime/harness.d.ts +0 -5
- package/dist/agent-runtime/harness.d.ts.map +1 -1
- package/dist/agent-runtime/managed-tools.d.ts +3 -2
- package/dist/agent-runtime/managed-tools.d.ts.map +1 -1
- package/dist/agent-runtime/provider-origin.d.ts +7 -0
- package/dist/agent-runtime/provider-origin.d.ts.map +1 -1
- package/dist/agent-runtime/run-operation-lifecycle.d.ts.map +1 -1
- package/dist/agent-runtime/runtime.d.ts +1 -1
- package/dist/agent-runtime/runtime.d.ts.map +1 -1
- package/dist/agent-runtime/schedule-records.d.ts +34 -0
- package/dist/agent-runtime/schedule-records.d.ts.map +1 -0
- package/dist/agent-runtime/schedules.d.ts +34 -0
- package/dist/agent-runtime/schedules.d.ts.map +1 -1
- package/dist/agent-runtime/store-conformance-lifecycle.d.ts +434 -0
- package/dist/agent-runtime/store-conformance-lifecycle.d.ts.map +1 -0
- package/dist/agent-runtime/store-conformance-ordering.d.ts +18 -0
- package/dist/agent-runtime/store-conformance-ordering.d.ts.map +1 -0
- package/dist/agent-runtime/store-conformance-support.d.ts +130 -0
- package/dist/agent-runtime/store-conformance-support.d.ts.map +1 -0
- package/dist/agent-runtime/store-conformance-terminal.d.ts +12 -0
- package/dist/agent-runtime/store-conformance-terminal.d.ts.map +1 -0
- package/dist/agent-runtime/store-conformance.d.ts.map +1 -1
- package/dist/application/admission-rate.d.ts +21 -0
- package/dist/application/admission-rate.d.ts.map +1 -0
- package/dist/application/admission-run.d.ts +8 -0
- package/dist/application/admission-run.d.ts.map +1 -0
- package/dist/application/admission.d.ts.map +1 -1
- package/dist/application/diagnostic-journal-lock.d.ts +9 -34
- package/dist/application/diagnostic-journal-lock.d.ts.map +1 -1
- package/dist/application/diagnostic-journal-manager.d.ts.map +1 -1
- package/dist/application/diagnostic-journal-storage.d.ts.map +1 -1
- package/dist/application/kernel-resource-context.d.ts.map +1 -1
- package/dist/application/notification-outbox.d.ts.map +1 -1
- package/dist/application/process-lifecycle-transitions.d.ts +175 -0
- package/dist/application/process-lifecycle-transitions.d.ts.map +1 -0
- package/dist/application/process-lifecycle.d.ts +3 -131
- package/dist/application/process-lifecycle.d.ts.map +1 -1
- package/dist/application/schedule.d.ts +0 -1
- package/dist/application/schedule.d.ts.map +1 -1
- package/dist/application/watch-hub-core.d.ts +122 -0
- package/dist/application/watch-hub-core.d.ts.map +1 -0
- package/dist/application/watch-hub.d.ts +2 -2
- package/dist/application/watch-hub.d.ts.map +1 -1
- package/dist/browser/client-executors.d.ts +14 -0
- package/dist/browser/client-executors.d.ts.map +1 -0
- package/dist/browser/client-multipart.d.ts +1 -1
- package/dist/browser/client-multipart.d.ts.map +1 -1
- package/dist/browser/client.d.ts +3 -2
- package/dist/browser/client.d.ts.map +1 -1
- package/dist/browser/http-fetch.d.ts +22 -0
- package/dist/browser/http-fetch.d.ts.map +1 -0
- package/dist/browser/http.d.ts +1 -3
- package/dist/browser/http.d.ts.map +1 -1
- package/dist/browser/live-state-core.d.ts +63 -0
- package/dist/browser/live-state-core.d.ts.map +1 -0
- package/dist/browser/live-state.d.ts.map +1 -1
- package/dist/browser/realtime-client.d.ts +26 -0
- package/dist/browser/realtime-client.d.ts.map +1 -0
- package/dist/browser/socket-io-connection.d.ts +82 -0
- package/dist/browser/socket-io-connection.d.ts.map +1 -0
- package/dist/browser/socket-io-peer.d.ts +20 -0
- package/dist/browser/socket-io-peer.d.ts.map +1 -0
- package/dist/browser/socket-io-trace.d.ts +31 -0
- package/dist/browser/socket-io-trace.d.ts.map +1 -0
- package/dist/browser/socket-io.d.ts +2 -18
- package/dist/browser/socket-io.d.ts.map +1 -1
- package/dist/contract/client-types.d.ts +134 -0
- package/dist/contract/client-types.d.ts.map +1 -0
- package/dist/contract/define-validation.d.ts +30 -0
- package/dist/contract/define-validation.d.ts.map +1 -0
- package/dist/contract/define.d.ts +3 -357
- package/dist/contract/define.d.ts.map +1 -1
- package/dist/contract/runtime-context.d.ts +131 -0
- package/dist/contract/runtime-context.d.ts.map +1 -0
- package/dist/contract/tool-options.d.ts +105 -0
- package/dist/contract/tool-options.d.ts.map +1 -0
- package/dist/contract/tool-view.d.ts +15 -2
- package/dist/contract/tool-view.d.ts.map +1 -1
- package/dist/durability/contract.d.ts +83 -0
- package/dist/durability/contract.d.ts.map +1 -1
- package/dist/durability/effect.d.ts +24 -0
- package/dist/durability/effect.d.ts.map +1 -0
- package/dist/durability/engine.d.ts +2 -2
- package/dist/durability/engine.d.ts.map +1 -1
- package/dist/durability/events.d.ts +2 -0
- package/dist/durability/events.d.ts.map +1 -1
- package/dist/durability/ledger.d.ts +11 -0
- package/dist/durability/ledger.d.ts.map +1 -1
- package/dist/durability/parks.d.ts +22 -0
- package/dist/durability/parks.d.ts.map +1 -0
- package/dist/durability/scheduler.d.ts +1 -0
- package/dist/durability/scheduler.d.ts.map +1 -1
- package/dist/entrypoints/agent-runtime/coding-tools.js +4 -4
- package/dist/entrypoints/agent-runtime/harness.js +127 -116
- package/dist/entrypoints/agent-runtime/sandbox.js +4 -4
- package/dist/entrypoints/agent-runtime/sqlite/bun.js +3 -3
- package/dist/entrypoints/agent-runtime/sqlite/node.js +3 -3
- package/dist/entrypoints/agent-runtime/testing.js +246 -214
- package/dist/entrypoints/agent-runtime.js +224 -203
- package/dist/entrypoints/application/diagnostic-journal.js +63 -203
- package/dist/entrypoints/application/opentelemetry.js +1 -1
- package/dist/entrypoints/application.d.ts +2 -1
- package/dist/entrypoints/application.d.ts.map +1 -1
- package/dist/entrypoints/application.js +506 -380
- package/dist/entrypoints/cli.d.ts +4 -1
- package/dist/entrypoints/cli.d.ts.map +1 -1
- package/dist/entrypoints/cli.js +57 -35
- package/dist/entrypoints/contract.d.ts +5 -2
- package/dist/entrypoints/contract.d.ts.map +1 -1
- package/dist/entrypoints/contract.js +3 -2
- package/dist/entrypoints/files.d.ts +2 -0
- package/dist/entrypoints/files.d.ts.map +1 -1
- package/dist/entrypoints/files.js +98 -2
- package/dist/entrypoints/index.d.ts +4 -2
- package/dist/entrypoints/index.d.ts.map +1 -1
- package/dist/entrypoints/index.js +348 -282
- package/dist/entrypoints/live.js +89 -71
- package/dist/entrypoints/node.d.ts +2 -1
- package/dist/entrypoints/node.d.ts.map +1 -1
- package/dist/entrypoints/node.js +13 -12
- package/dist/entrypoints/react.js +1 -1
- package/dist/entrypoints/remote.js +4 -4
- package/dist/entrypoints/server.d.ts +4 -2
- package/dist/entrypoints/server.d.ts.map +1 -1
- package/dist/entrypoints/server.js +293 -79
- package/dist/entrypoints/testing.d.ts +2 -1
- package/dist/entrypoints/testing.d.ts.map +1 -1
- package/dist/entrypoints/testing.js +288 -281
- package/dist/entrypoints/tools/connections.js +2 -2
- package/dist/entrypoints/tools/invoker.js +4 -4
- package/dist/entrypoints/tools.d.ts +6 -3
- package/dist/entrypoints/tools.d.ts.map +1 -1
- package/dist/entrypoints/tools.js +245 -202
- package/dist/entrypoints/tracking.js +325 -274
- package/dist/files/boundary.d.ts.map +1 -1
- package/dist/files/file-io.d.ts +14 -0
- package/dist/files/file-io.d.ts.map +1 -0
- package/dist/index-1j8pm5t0.js +412 -0
- package/dist/{index-kvj7f4hq.js → index-2bxbyab0.js} +10 -2
- package/dist/{index-xnj9bgxe.js → index-2gxzjvyr.js} +2 -162
- package/dist/index-4gcnakhx.js +82 -0
- package/dist/{index-j05z5gn4.js → index-55hkhm67.js} +136 -119
- package/dist/{index-cq4qfycf.js → index-79z7m7dt.js} +227 -59
- package/dist/{index-r7nmqpsg.js → index-7p1f0vp2.js} +1 -1
- package/dist/{index-dcpdvjsc.js → index-863ea9jb.js} +4 -4
- package/dist/index-8p52kd5p.js +242 -0
- package/dist/{index-97ac4gph.js → index-8zja4pkf.js} +26 -4
- package/dist/{index-2qzskm8h.js → index-dce5wxcq.js} +1 -1
- package/dist/index-e01r054k.js +105 -0
- package/dist/{index-sees8yf8.js → index-ensd0r74.js} +118 -111
- package/dist/{index-1nd48tm9.js → index-gxrt5h6v.js} +2 -1
- package/dist/{index-e87kakm4.js → index-ham5sz8m.js} +112 -110
- package/dist/{index-kj2wbeta.js → index-het2he5g.js} +280 -196
- package/dist/{index-vcvkq30w.js → index-hmp4800j.js} +3 -3
- package/dist/{index-gyr48nzg.js → index-k30h66yd.js} +71 -151
- package/dist/{index-2cr1cqhn.js → index-khtgb21r.js} +2 -2
- package/dist/{index-x4799c19.js → index-ndddxtyx.js} +11 -11
- package/dist/{index-dydjcp8z.js → index-np4m8hd3.js} +38 -36
- package/dist/{index-p81267c2.js → index-p3vsv1fb.js} +621 -357
- package/dist/{index-mp5sn43f.js → index-p9e0w8hs.js} +2 -2
- package/dist/{index-rewkc2hj.js → index-p9sh0xx4.js} +3 -3
- package/dist/{index-70yj2ebz.js → index-paz6sgdp.js} +107 -103
- package/dist/{index-bn2cjajt.js → index-px2gm2jp.js} +26 -102
- package/dist/index-s812ycat.js +65 -0
- package/dist/{index-xsqjkha9.js → index-sp1tzwnb.js} +2 -2
- package/dist/{index-n6hxg8as.js → index-t9rhjr9f.js} +615 -566
- package/dist/{index-zeemqhqx.js → index-v260df89.js} +88 -80
- package/dist/{index-fdp6hdxn.js → index-w906953s.js} +5 -1
- package/dist/{index-mdzh02yc.js → index-wdkt63w3.js} +1 -1
- package/dist/{index-58k4fp34.js → index-wsm5w4ee.js} +1 -1
- package/dist/{index-v38qyerb.js → index-xw3s75nj.js} +1 -1
- package/dist/internal/atomic-file.d.ts +23 -2
- package/dist/internal/atomic-file.d.ts.map +1 -1
- package/dist/internal/exclusive-lock.d.ts +51 -0
- package/dist/internal/exclusive-lock.d.ts.map +1 -0
- package/dist/internal/process-identity.d.ts +47 -0
- package/dist/internal/process-identity.d.ts.map +1 -0
- package/dist/internal/with-exclusive-lock.d.ts +67 -0
- package/dist/internal/with-exclusive-lock.d.ts.map +1 -0
- package/dist/live/watch-client.d.ts.map +1 -1
- package/dist/observability/audit.d.ts +2 -2
- package/dist/observability/audit.d.ts.map +1 -1
- package/dist/observability/event.d.ts +2 -1
- package/dist/observability/event.d.ts.map +1 -1
- package/dist/primitives/export-operation.d.ts +1 -1
- package/dist/primitives/export-operation.d.ts.map +1 -1
- package/dist/realtime/registry.d.ts +0 -5
- package/dist/realtime/registry.d.ts.map +1 -1
- package/dist/server/bun.d.ts +0 -1
- package/dist/server/bun.d.ts.map +1 -1
- package/dist/server/context-contribution.d.ts +1 -1
- package/dist/server/context-contribution.d.ts.map +1 -1
- package/dist/server/context.d.ts +1 -1
- package/dist/server/context.d.ts.map +1 -1
- package/dist/server/endpoint-response.d.ts +2 -1
- package/dist/server/endpoint-response.d.ts.map +1 -1
- package/dist/server/error-dispatch.d.ts +1 -1
- package/dist/server/error-dispatch.d.ts.map +1 -1
- package/dist/server/error-hook.d.ts +1 -1
- package/dist/server/error-hook.d.ts.map +1 -1
- package/dist/server/handler-state.d.ts +1 -1
- package/dist/server/handler-state.d.ts.map +1 -1
- package/dist/server/implement-registry.d.ts +54 -0
- package/dist/server/implement-registry.d.ts.map +1 -0
- package/dist/server/implement.d.ts +9 -55
- package/dist/server/implement.d.ts.map +1 -1
- package/dist/server/middleware/auth.d.ts +1 -31
- package/dist/server/middleware/auth.d.ts.map +1 -1
- package/dist/server/middleware/jwt.d.ts +31 -0
- package/dist/server/middleware/jwt.d.ts.map +1 -0
- package/dist/server/multipart-reader.d.ts +22 -0
- package/dist/server/multipart-reader.d.ts.map +1 -0
- package/dist/server/multipart.d.ts +1 -1
- package/dist/server/multipart.d.ts.map +1 -1
- package/dist/server/oauth/cimd.d.ts +88 -0
- package/dist/server/oauth/cimd.d.ts.map +1 -0
- package/dist/server/oauth/endpoints.d.ts +39 -0
- package/dist/server/oauth/endpoints.d.ts.map +1 -0
- package/dist/server/oauth/provider.d.ts +1 -68
- package/dist/server/oauth/provider.d.ts.map +1 -1
- package/dist/server/streaming-pump.d.ts +66 -0
- package/dist/server/streaming-pump.d.ts.map +1 -0
- package/dist/server/streaming-route.d.ts +12 -5
- package/dist/server/streaming-route.d.ts.map +1 -1
- package/dist/server/types.d.ts +4 -1
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/unix-client-bun.d.ts.map +1 -1
- package/dist/server/unix-client-http1.d.ts +76 -0
- package/dist/server/unix-client-http1.d.ts.map +1 -0
- package/dist/server/unix-client.d.ts.map +1 -1
- package/dist/testing/handler-clients.d.ts +2 -1
- package/dist/testing/handler-clients.d.ts.map +1 -1
- package/dist/testing/realtime-probe.d.ts +33 -0
- package/dist/testing/realtime-probe.d.ts.map +1 -0
- package/dist/testing/surface-conformance.d.ts +0 -31
- package/dist/testing/surface-conformance.d.ts.map +1 -1
- package/dist/testing/surface-manifest-digest.d.ts +44 -0
- package/dist/testing/surface-manifest-digest.d.ts.map +1 -0
- package/dist/testing/surface-manifest.d.ts.map +1 -1
- package/dist/tools/agent.d.ts +1 -1
- package/dist/tools/agent.d.ts.map +1 -1
- package/dist/tools/cli/args-fields.d.ts +31 -0
- package/dist/tools/cli/args-fields.d.ts.map +1 -0
- package/dist/tools/cli/args-route.d.ts +42 -0
- package/dist/tools/cli/args-route.d.ts.map +1 -0
- package/dist/tools/cli/args-view.d.ts +32 -0
- package/dist/tools/cli/args-view.d.ts.map +1 -0
- package/dist/tools/cli/args.d.ts +2 -65
- package/dist/tools/cli/args.d.ts.map +1 -1
- package/dist/tools/cli/argument-error.d.ts +5 -0
- package/dist/tools/cli/argument-error.d.ts.map +1 -0
- package/dist/tools/cli/checkpoint.d.ts.map +1 -1
- package/dist/tools/cli/command.d.ts +1 -1
- package/dist/tools/cli/command.d.ts.map +1 -1
- package/dist/tools/cli/config.d.ts +1 -1
- package/dist/tools/cli/config.d.ts.map +1 -1
- package/dist/tools/cli/format.d.ts +2 -1
- package/dist/tools/cli/format.d.ts.map +1 -1
- package/dist/tools/cli/invoke.d.ts +2 -1
- package/dist/tools/cli/invoke.d.ts.map +1 -1
- package/dist/tools/cli/managed-surface.d.ts +1 -1
- package/dist/tools/cli/managed-surface.d.ts.map +1 -1
- package/dist/tools/cli/policy.d.ts +1 -1
- package/dist/tools/cli/policy.d.ts.map +1 -1
- package/dist/tools/cli/prepare.d.ts.map +1 -1
- package/dist/tools/cli/run-managed.d.ts.map +1 -1
- package/dist/tools/cli/session.d.ts +1 -1
- package/dist/tools/cli/session.d.ts.map +1 -1
- package/dist/tools/cli/update.d.ts +19 -0
- package/dist/tools/cli/update.d.ts.map +1 -1
- package/dist/tools/cli/view.d.ts +1 -1
- package/dist/tools/cli/view.d.ts.map +1 -1
- package/dist/tools/cli/wait.d.ts +1 -1
- package/dist/tools/cli/wait.d.ts.map +1 -1
- package/dist/tools/connections/runtime.d.ts +1 -1
- package/dist/tools/connections/runtime.d.ts.map +1 -1
- package/dist/tools/execute-args.d.ts +23 -0
- package/dist/tools/execute-args.d.ts.map +1 -0
- package/dist/tools/execute-hooks.d.ts +146 -0
- package/dist/tools/execute-hooks.d.ts.map +1 -0
- package/dist/tools/execute-result.d.ts +40 -0
- package/dist/tools/execute-result.d.ts.map +1 -0
- package/dist/tools/execute.d.ts +4 -193
- package/dist/tools/execute.d.ts.map +1 -1
- package/dist/tools/internal/surface-projector.d.ts +2 -1
- package/dist/tools/internal/surface-projector.d.ts.map +1 -1
- package/dist/tools/invoker.d.ts +2 -1
- package/dist/tools/invoker.d.ts.map +1 -1
- package/dist/tools/lifecycle.d.ts +1 -1
- package/dist/tools/lifecycle.d.ts.map +1 -1
- package/dist/tools/mcp/mount.d.ts +1 -1
- package/dist/tools/mcp/mount.d.ts.map +1 -1
- package/dist/tools/mcp/prepare.d.ts +2 -1
- package/dist/tools/mcp/prepare.d.ts.map +1 -1
- package/dist/tools/mcp/progress.d.ts +1 -1
- package/dist/tools/mcp/progress.d.ts.map +1 -1
- package/dist/tools/mcp/register.d.ts +3 -2
- package/dist/tools/mcp/register.d.ts.map +1 -1
- package/dist/tools/mcp/round-context.d.ts +38 -0
- package/dist/tools/mcp/round-context.d.ts.map +1 -0
- package/dist/tools/mcp/round-policy.d.ts +1 -1
- package/dist/tools/mcp/round-policy.d.ts.map +1 -1
- package/dist/tools/mcp/round.d.ts +3 -11
- package/dist/tools/mcp/round.d.ts.map +1 -1
- package/dist/tools/mcp/runtime-tools.d.ts +2 -1
- package/dist/tools/mcp/runtime-tools.d.ts.map +1 -1
- package/dist/tools/mount.d.ts +3 -1
- package/dist/tools/mount.d.ts.map +1 -1
- package/dist/tools/native-definition.d.ts +2 -1
- package/dist/tools/native-definition.d.ts.map +1 -1
- package/dist/tools/operations/async-operation-runtime-tools.d.ts +2 -1
- package/dist/tools/operations/async-operation-runtime-tools.d.ts.map +1 -1
- package/dist/tools/operations/async-operation-runtime-types.d.ts +2 -1
- package/dist/tools/operations/async-operation-runtime-types.d.ts.map +1 -1
- package/dist/tools/remote.d.ts.map +1 -1
- package/dist/tools/runtime-tool.d.ts +3 -1
- package/dist/tools/runtime-tool.d.ts.map +1 -1
- package/dist/tools/tool-logger.d.ts +1 -1
- package/dist/tools/tool-logger.d.ts.map +1 -1
- package/dist/tracking/client-listeners.d.ts +30 -0
- package/dist/tracking/client-listeners.d.ts.map +1 -0
- package/dist/tracking/client-visit.d.ts +61 -0
- package/dist/tracking/client-visit.d.ts.map +1 -0
- package/dist/tracking/client.d.ts +3 -2
- package/dist/tracking/client.d.ts.map +1 -1
- package/llms/application.2.txt +271 -7
- package/llms/application.txt +66 -289
- package/llms/cli.2.txt +80 -0
- package/llms/cli.txt +45 -74
- package/llms/contract.txt +3 -3
- package/llms/files.txt +3 -0
- package/llms/stitchkit.txt +3 -3
- package/llms/tools.2.txt +88 -279
- package/llms/tools.3.txt +288 -0
- package/llms/tools.txt +27 -65
- package/llms/{upgrading-0.37.0-0.50.0.txt → upgrading-0.37.0-0.53.0.txt} +25 -3
- package/llms/{upgrading-0.53.0-0.67.0.txt → upgrading-0.55.0-0.68.0.txt} +53 -25
- package/llms/{upgrading-0.68.0-0.94.0.txt → upgrading-0.69.0-0.95.0.txt} +45 -53
- package/llms/upgrading.txt +3 -3
- package/llms-full.txt +216 -43
- package/llms.txt +8 -8
- package/package.json +1 -1
- package/dist/index-e2z3ahw9.js +0 -325
- package/dist/index-mkm15s49.js +0 -394
package/llms/application.2.txt
CHANGED
|
@@ -4,8 +4,272 @@
|
|
|
4
4
|
> `llms.txt` in this package.
|
|
5
5
|
|
|
6
6
|
Parts of this slice:
|
|
7
|
-
- `llms/application.txt`: Guide: Managed application kernel (docs/guide/application-kernel.md)
|
|
8
|
-
- `llms/application.2.txt`: API reference: `stitchkit/application` (docs/api/reference.md)
|
|
7
|
+
- `llms/application.txt`: Guide: Managed application kernel (docs/guide/application-kernel.md)
|
|
8
|
+
- `llms/application.2.txt`: Guide: Application migration recipes (docs/guide/application-migration-recipes.md) · API reference: `stitchkit/application` (docs/api/reference.md)
|
|
9
|
+
|
|
10
|
+
==============================================================================
|
|
11
|
+
# Guide: Application migration recipes (docs/guide/application-migration-recipes.md)
|
|
12
|
+
==============================================================================
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
title: Application migration recipes
|
|
16
|
+
description: Executable patterns for moving database, poller, queue and operational publishing lifecycle into the managed application kernel.
|
|
17
|
+
type: architecture
|
|
18
|
+
status: active
|
|
19
|
+
created: 2026-08-24
|
|
20
|
+
updated: 2026-08-24
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
# Application migration recipes
|
|
24
|
+
|
|
25
|
+
These recipes are the migration companion to the
|
|
26
|
+
[managed application kernel](./application-kernel.md). Their canonical source is
|
|
27
|
+
the packed-consumer fixture
|
|
28
|
+
[`application-migration-recipes.ts`](../../packages/core/scripts/consumer-lane/fixtures/minimal/src/application-migration-recipes.ts):
|
|
29
|
+
the consumer lane installs the package tarball, typechecks that file and runs it
|
|
30
|
+
only through `stitchkit/application` exports.
|
|
31
|
+
|
|
32
|
+
## Database connection
|
|
33
|
+
|
|
34
|
+
Wrap connection allocation and readiness in `start`, and make `close` safe after
|
|
35
|
+
partial setup. Stitchkit invokes cleanup for every attempted start, including a
|
|
36
|
+
start that allocates a client and then rejects.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
const database = defineManagedResource({
|
|
40
|
+
id: 'database',
|
|
41
|
+
async start({ signal, reportHealth }) {
|
|
42
|
+
await databaseClient.connect(signal)
|
|
43
|
+
await databaseClient.assertReady()
|
|
44
|
+
reportHealth('healthy')
|
|
45
|
+
},
|
|
46
|
+
close: () => databaseClient.close(),
|
|
47
|
+
})
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The application still owns the client, ORM configuration, transactions,
|
|
51
|
+
migrations and reconnect policy. The executable recipe deliberately fails after
|
|
52
|
+
allocation and proves one rollback close.
|
|
53
|
+
|
|
54
|
+
## Long-running poller
|
|
55
|
+
|
|
56
|
+
Return separate readiness and completion promises. Readiness says dependants may
|
|
57
|
+
start; completion represents the whole background lifetime.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
const poller = defineManagedResource({
|
|
61
|
+
id: 'poller',
|
|
62
|
+
start: ({ signal }) => ({
|
|
63
|
+
ready: providerPoller.ready,
|
|
64
|
+
completion: providerPoller.run(signal),
|
|
65
|
+
}),
|
|
66
|
+
stopAdmission: () => providerPoller.stop(),
|
|
67
|
+
drain: () => providerPoller.completion,
|
|
68
|
+
close: () => providerPoller.close(),
|
|
69
|
+
})
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Completion before readiness fails startup and rolls back. Completion after
|
|
73
|
+
readiness removes application readiness. Provider cursor persistence, retry,
|
|
74
|
+
backoff and restart policy remain application/provider concerns.
|
|
75
|
+
|
|
76
|
+
## Queue consumer
|
|
77
|
+
|
|
78
|
+
Acquire the application lease only after provider delivery/claim. If admission
|
|
79
|
+
is already closed, reject the delivery through the provider's own nack/requeue
|
|
80
|
+
primitive. Work admitted before shutdown releases its lease after ack/nack and
|
|
81
|
+
therefore participates in application drain.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
async function handleDelivery(delivery: Delivery) {
|
|
85
|
+
const lease = app.admission.acquire()
|
|
86
|
+
if (!lease) return delivery.nack({ requeue: true })
|
|
87
|
+
try {
|
|
88
|
+
await processClaim(delivery.claim)
|
|
89
|
+
await delivery.ack()
|
|
90
|
+
} catch (error) {
|
|
91
|
+
await delivery.nack({ requeue: shouldRetry(error) })
|
|
92
|
+
throw error
|
|
93
|
+
} finally {
|
|
94
|
+
lease.release()
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Stitchkit owns only process-local admission accounting and drain. Durable claims,
|
|
100
|
+
visibility timeouts, deduplication, idempotency and retry classification remain
|
|
101
|
+
in the queue/product layer.
|
|
102
|
+
|
|
103
|
+
## Operational publisher
|
|
104
|
+
|
|
105
|
+
Project anonymous aggregate activity, then feed absolute snapshots into the
|
|
106
|
+
latest-value sink. A slow transport holds one write plus one replaceable latest
|
|
107
|
+
revision rather than an unbounded event queue.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
const publisher = createApplicationSnapshotSink({
|
|
111
|
+
write: (snapshot) => monitoring.publish(snapshot),
|
|
112
|
+
})
|
|
113
|
+
const activity = createActivityProjection({
|
|
114
|
+
id: 'generation',
|
|
115
|
+
stages: ['queued', 'running'],
|
|
116
|
+
})
|
|
117
|
+
const unsubscribe = activity.subscribe((snapshot) => {
|
|
118
|
+
publisher.publish(snapshot)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// cleanup boundary
|
|
122
|
+
unsubscribe()
|
|
123
|
+
publisher.publish(activity.getSnapshot())
|
|
124
|
+
await publisher.close()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The monitoring backend, transport retry and cross-process aggregation remain
|
|
128
|
+
outside Stitchkit. The executable recipe blocks revision 0, coalesces
|
|
129
|
+
intermediate revisions, explicitly admits the final absolute snapshot after
|
|
130
|
+
unsubscribe and proves that `close()` flushes revision 3. Do not rely on an
|
|
131
|
+
asynchronous subscriber callback racing cleanup: publish `getSnapshot()` before
|
|
132
|
+
closing the outer sink, so any older or duplicate late delivery is rejected as
|
|
133
|
+
stale instead of dropping the final state.
|
|
134
|
+
|
|
135
|
+
## Bound a handler and a local worker with one lease policy
|
|
136
|
+
|
|
137
|
+
Use a bounded admission when two entry paths consume the same finite local
|
|
138
|
+
capacity. Composing it with `application.admission` keeps readiness and shutdown
|
|
139
|
+
as the upstream gate:
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
const work = createBoundedAdmission({
|
|
143
|
+
upstream: app.admission,
|
|
144
|
+
policy: {
|
|
145
|
+
global: { maxConcurrent: 4 },
|
|
146
|
+
perKey: { maxConcurrent: 1, maxKeys: 1_000 },
|
|
147
|
+
},
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
const fromHttp = (key: string, signal: AbortSignal) =>
|
|
151
|
+
work.run(key, (context) => render(context.signal), { signal, timeoutMs: 20_000 })
|
|
152
|
+
|
|
153
|
+
const fromWorker = (key: string) =>
|
|
154
|
+
work.run(key, (context) => reconcile(context.signal))
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
A timeout in `fromHttp` does not free a permit while `render` remains active.
|
|
158
|
+
At shutdown call `work.stopAdmission()` with the other admission owners and
|
|
159
|
+
await `work.drain(...)`; the result reports the actual remainder.
|
|
160
|
+
|
|
161
|
+
## Replace ad-hoc output and progress queues
|
|
162
|
+
|
|
163
|
+
Ordered output and replaceable progress are separate declarations:
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
const lines = createBoundedChannel<string>({
|
|
167
|
+
policy: 'ordered', maxItems: 100, maxBytes: 1_000_000,
|
|
168
|
+
sizeOf: (line) => new TextEncoder().encode(line).byteLength,
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
const state = createBoundedChannel<{ revision: number; percent: number }>({
|
|
172
|
+
policy: 'latest', maxItems: 1, maxBytes: 64,
|
|
173
|
+
sizeOf: () => 64,
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
const lineResult = lines.offer('one durable-in-process ordering unit')
|
|
177
|
+
const stateResult = state.offer({ revision: 2, percent: 50 })
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Handle `refused` from `lines` at the protocol boundary; do not turn it into
|
|
181
|
+
implicit loss. A `coalesced` state result is expected latest-value behaviour,
|
|
182
|
+
not evidence that an ordered event was delivered. Neither channel is durable;
|
|
183
|
+
persist first when restart replay is required.
|
|
184
|
+
|
|
185
|
+
## Handing a handle to the resources that depend on it
|
|
186
|
+
|
|
187
|
+
`dependsOn` carries ordering. To carry the object as well, return a `value` from
|
|
188
|
+
`start` and read it with `context.use(...)`. This replaces the module-local
|
|
189
|
+
`let handle: T | null` with the guard the graph makes unreachable.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const database = defineManagedResource({
|
|
193
|
+
id: 'database',
|
|
194
|
+
start: async () => ({ value: await connect(env.DATABASE_URL) }),
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
const worker = defineManagedResource({
|
|
198
|
+
id: 'worker',
|
|
199
|
+
dependsOn: [database],
|
|
200
|
+
start(context) {
|
|
201
|
+
const db = context.use(database) // Connection — not Connection | null
|
|
202
|
+
},
|
|
203
|
+
})
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Declare the dependency with the **resource** whenever you intend to read from
|
|
207
|
+
it: that is the form `use` can type, and it stops the declaration and the read
|
|
208
|
+
from drifting. A string still expresses order on its own.
|
|
209
|
+
|
|
210
|
+
The value is published when `start` resolves and stays readable from `activate`
|
|
211
|
+
and from the shutdown phases. `use` refuses a resource that was not declared in
|
|
212
|
+
`dependsOn` — that only ever worked by luck of declaration order — and refuses a
|
|
213
|
+
resource that published nothing, which the compiler refuses too. The executable
|
|
214
|
+
recipe proves both the read and the refusal.
|
|
215
|
+
|
|
216
|
+
## Managed HTTP server
|
|
217
|
+
|
|
218
|
+
The main resource of a web backend, and the one whose ownership rule is
|
|
219
|
+
counter-intuitive: give `managedServerResource` a **thunk** and it creates the
|
|
220
|
+
server during `start`, after its dependencies are ready. Give it an
|
|
221
|
+
already-listening server and it adopts that one instead.
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
const http = managedServerResource({
|
|
225
|
+
id: 'http',
|
|
226
|
+
dependsOn: [database],
|
|
227
|
+
server: (context) => {
|
|
228
|
+
const db = context.use(database)
|
|
229
|
+
return createServer({ port: env.PORT, services: createServices(db) })
|
|
230
|
+
},
|
|
231
|
+
})
|
|
232
|
+
|
|
233
|
+
const app = createApplication({ id: 'service', resources: [database, http] })
|
|
234
|
+
await app.start() // the port is bound; the snapshot means it
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The server resource publishes its `ManagedServerHandle`, so anything that needs
|
|
238
|
+
the running server — a Socket.IO attachment, a URL to log, a probe — reads it
|
|
239
|
+
with `context.use(http)` rather than through a module-local.
|
|
240
|
+
|
|
241
|
+
The factory itself receives `ManagedResourceContext` too. It can read only the
|
|
242
|
+
dependencies declared on the server resource and shares the graph's startup
|
|
243
|
+
signal. A dependency failure prevents the factory from binding; a factory
|
|
244
|
+
rejection is rolled back without invoking the factory a second time.
|
|
245
|
+
|
|
246
|
+
Do not spread this resource over your own `start` to control creation order:
|
|
247
|
+
that shape exists only as a workaround for the version whose `start` was empty,
|
|
248
|
+
and `dependsOn` now expresses the order directly. The executable recipe binds a
|
|
249
|
+
real port, proves a request reaches it after `start()` resolves, and proves the
|
|
250
|
+
port is closed after `shutdown()`.
|
|
251
|
+
|
|
252
|
+
## Deletion checklist
|
|
253
|
+
|
|
254
|
+
After the cutover, remove the old generic lifecycle path completely:
|
|
255
|
+
|
|
256
|
+
- duplicate `process.on(...)` handlers and shutdown promise caches;
|
|
257
|
+
- manual readiness waiters and resource close fan-out;
|
|
258
|
+
- raw interval handles, overlap flags and timer drain code;
|
|
259
|
+
- global in-flight counters and waiter sets replaced by application leases;
|
|
260
|
+
- progress delta queues replaced by absolute snapshot publishing.
|
|
261
|
+
|
|
262
|
+
Keep the product code that still owns durable state and policy:
|
|
263
|
+
|
|
264
|
+
- database schema, transactions and migrations;
|
|
265
|
+
- queue claims, ack/nack, deduplication and external-effect idempotency;
|
|
266
|
+
- provider cursor, retry/backoff and credentials;
|
|
267
|
+
- monitoring transport configuration and durable retention;
|
|
268
|
+
- process exit code, hard-exit and deployment/supervisor policy.
|
|
269
|
+
|
|
270
|
+
Run the old and new lifecycle paths separately during development if needed,
|
|
271
|
+
but do not ship both. Before cutover, verify there is exactly one signal binding,
|
|
272
|
+
one timer owner, one admission gate and one resource cleanup chain.
|
|
9
273
|
|
|
10
274
|
==============================================================================
|
|
11
275
|
# API reference: `stitchkit/application` (docs/api/reference.md)
|
|
@@ -26,11 +290,11 @@ cutovers are covered by the executable
|
|
|
26
290
|
| `createApplication` | function | compose a validated resource DAG into one startup, readiness, admission and shutdown state machine |
|
|
27
291
|
| `StateStore` / `StateStoreUpdate` | _type_ | async atomic read/update boundary shared by durable application state machines |
|
|
28
292
|
| `createProcessLifecycleLedger` / `ProcessLifecycleLedger` / `ProcessLifecycleLedgerConfig` | function / _type_ | durable bounded run history and typed fact subscription |
|
|
29
|
-
| `LifecycleRunSchema` / `LifecycleRun` / `LifecycleStateSchema` / `LifecycleState` / `LifecycleTerminationSchema` / `LifecycleTermination` | schema / _type_ | versioned process-run state and
|
|
30
|
-
| `PreviousExitSchema` / `PreviousExit` / `StartFact` / `ReadyFact` / `ShutdownFact` / `ProcessLifecycleFact` | schema / _type_ | first-boot, hot-reload, clean, forced, handoff and abnormal lifecycle facts |
|
|
31
|
-
| `transitionProcessStart` / `transitionProcessReady` / `transitionProcessShutdown` | function | pure idempotent runId+pid-owned transitions |
|
|
32
|
-
| `LifecycleTransition` / `TransitionStartInput` / `
|
|
33
|
-
| `lifecycleLedgerResource` / `LifecycleLedgerResource` / `LifecycleLedgerResourceConfig` | function / _type_ | managed-resource adapter that records start, readiness and shutdown |
|
|
293
|
+
| `LifecycleRunSchema` / `LifecycleRun` / `LifecycleStateSchema` / `LifecycleState` / `LifecycleTerminationSchema` / `LifecycleTermination` | schema / _type_ | versioned process-run state — open (`active`, `draining` with `unavailableAt`) and ended (`clean`, `forced`, `startup-failed`, `hot-reload`, `abnormal`) |
|
|
294
|
+
| `PreviousExitSchema` / `PreviousExit` / `StartFact` / `ReadyFact` / `DrainingFact` / `ShutdownFact` / `ProcessLifecycleFact` | schema / _type_ | first-boot, hot-reload, clean, forced, startup-failed, handoff and abnormal lifecycle facts; `processGapMs` on start, `downtimeMs` (unavailable → ready) on ready |
|
|
295
|
+
| `transitionProcessStart` / `transitionProcessReady` / `transitionProcessDraining` / `transitionProcessShutdown` | function | pure idempotent runId+pid-owned transitions |
|
|
296
|
+
| `LifecycleTransition` / `TransitionStartInput` / `TransitionRunInput` / `TransitionShutdownInput` / `SameVersionOverlap` | _type_ | pure transition inputs (`TransitionRunInput` for ready and draining, extended with `forced` for shutdown), the same-build overlap policy (`abnormal` default / `handoff`) and state+fact result |
|
|
297
|
+
| `lifecycleLedgerResource` / `LifecycleLedgerResource` / `LifecycleLedgerResourceConfig` | function / _type_ | managed-resource adapter that records start, readiness, admission stop (draining) and shutdown |
|
|
34
298
|
| `createNotificationOutbox` / `NotificationOutbox` / `NotificationOutboxConfig` | function / _type_ | bounded at-least-once queue with durable claims, leases, retries and supersede |
|
|
35
299
|
| `NotificationOutboxItem` / `NotificationOutboxReceipt` / `NotificationOutboxState` / `NotificationSend` / `NotificationFailureClassification` / `DroppedNotification` / `EnqueueNotification` | _type_ | versioned queue records, stable send identity and delivery decisions |
|
|
36
300
|
| `notificationOutboxStateSchema` | function | strict persisted-state schema over an application payload schema |
|
package/llms/application.txt
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
> Continues in `llms/application.2.txt`.
|
|
6
6
|
|
|
7
7
|
Parts of this slice:
|
|
8
|
-
- `llms/application.txt`: Guide: Managed application kernel (docs/guide/application-kernel.md)
|
|
9
|
-
- `llms/application.2.txt`: API reference: `stitchkit/application` (docs/api/reference.md)
|
|
8
|
+
- `llms/application.txt`: Guide: Managed application kernel (docs/guide/application-kernel.md)
|
|
9
|
+
- `llms/application.2.txt`: Guide: Application migration recipes (docs/guide/application-migration-recipes.md) · API reference: `stitchkit/application` (docs/api/reference.md)
|
|
10
10
|
|
|
11
11
|
==============================================================================
|
|
12
12
|
# Guide: Managed application kernel (docs/guide/application-kernel.md)
|
|
@@ -208,6 +208,12 @@ answered the question, and the answer stands. (It used to be overwritten — and
|
|
|
208
208
|
the example above hides that, because `healthy` is the same value that
|
|
209
209
|
overwrote it.)
|
|
210
210
|
|
|
211
|
+
**Reporting the same health again is not a change.** A resource may confirm its
|
|
212
|
+
health on a timer; a report with the value it already reported publishes
|
|
213
|
+
nothing — no new revision, no `changedAt`, no subscriber call. A change of value
|
|
214
|
+
publishes once, as does the first report that confirms the `healthy` the kernel
|
|
215
|
+
assumed at readiness.
|
|
216
|
+
|
|
211
217
|
**A resource is required unless you write `required: false`.** That default is
|
|
212
218
|
what makes the next sentence bite.
|
|
213
219
|
|
|
@@ -809,39 +815,74 @@ application-owned.
|
|
|
809
815
|
## Durable process facts and owner notifications
|
|
810
816
|
|
|
811
817
|
`createProcessLifecycleLedger` records a bounded versioned list of process runs.
|
|
812
|
-
Start, readiness and shutdown target both `runId` and `pid`, so hot
|
|
813
|
-
PID reuse cannot close the wrong generation.
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
the
|
|
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:
|
|
817
830
|
|
|
818
831
|
| newest run | same pid | version | `previousExit` | predecessor's `termination` |
|
|
819
832
|
|---|---|---|---|---|
|
|
820
833
|
| none | — | — | `first-boot` | — |
|
|
821
|
-
| recorded `stoppedAt` | — | — | `clean` / `forced` / `abnormal` as recorded | unchanged |
|
|
822
|
-
| still
|
|
823
|
-
| still
|
|
824
|
-
| still
|
|
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`.
|
|
825
843
|
|
|
826
844
|
A version of `unknown` on either side is never a version change, so a dev build
|
|
827
845
|
after a crashed release reads `abnormal`, not `handoff`. `forced` means the
|
|
828
846
|
process itself acknowledged a kill; `abnormal` means a successor found it dead.
|
|
829
847
|
|
|
830
|
-
|
|
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:
|
|
831
872
|
one process per build, a new pid of the same build means the old one stopped
|
|
832
|
-
answering. Where
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
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.
|
|
845
886
|
|
|
846
887
|
`createNotificationOutbox` is the transport-neutral durable delivery side. It
|
|
847
888
|
persists before send, claims an item with an expiring lease, carries one stable
|
|
@@ -878,267 +919,3 @@ so a scheduler stall on a loaded host cannot pull a live lock from under its
|
|
|
878
919
|
holder — after which the heartbeat wins and the lock is abandoned. Temporary files a crashed writer left beside the state are swept on
|
|
879
920
|
the store's first update. Ledger corruption may be declared reconstructable;
|
|
880
921
|
an outbox must fail closed rather than silently discard pending delivery.
|
|
881
|
-
|
|
882
|
-
==============================================================================
|
|
883
|
-
# Guide: Application migration recipes (docs/guide/application-migration-recipes.md)
|
|
884
|
-
==============================================================================
|
|
885
|
-
|
|
886
|
-
---
|
|
887
|
-
title: Application migration recipes
|
|
888
|
-
description: Executable patterns for moving database, poller, queue and operational publishing lifecycle into the managed application kernel.
|
|
889
|
-
type: architecture
|
|
890
|
-
status: active
|
|
891
|
-
created: 2026-08-24
|
|
892
|
-
updated: 2026-08-24
|
|
893
|
-
---
|
|
894
|
-
|
|
895
|
-
# Application migration recipes
|
|
896
|
-
|
|
897
|
-
These recipes are the migration companion to the
|
|
898
|
-
[managed application kernel](./application-kernel.md). Their canonical source is
|
|
899
|
-
the packed-consumer fixture
|
|
900
|
-
[`application-migration-recipes.ts`](../../packages/core/scripts/consumer-lane/fixtures/minimal/src/application-migration-recipes.ts):
|
|
901
|
-
the consumer lane installs the package tarball, typechecks that file and runs it
|
|
902
|
-
only through `stitchkit/application` exports.
|
|
903
|
-
|
|
904
|
-
## Database connection
|
|
905
|
-
|
|
906
|
-
Wrap connection allocation and readiness in `start`, and make `close` safe after
|
|
907
|
-
partial setup. Stitchkit invokes cleanup for every attempted start, including a
|
|
908
|
-
start that allocates a client and then rejects.
|
|
909
|
-
|
|
910
|
-
```ts
|
|
911
|
-
const database = defineManagedResource({
|
|
912
|
-
id: 'database',
|
|
913
|
-
async start({ signal, reportHealth }) {
|
|
914
|
-
await databaseClient.connect(signal)
|
|
915
|
-
await databaseClient.assertReady()
|
|
916
|
-
reportHealth('healthy')
|
|
917
|
-
},
|
|
918
|
-
close: () => databaseClient.close(),
|
|
919
|
-
})
|
|
920
|
-
```
|
|
921
|
-
|
|
922
|
-
The application still owns the client, ORM configuration, transactions,
|
|
923
|
-
migrations and reconnect policy. The executable recipe deliberately fails after
|
|
924
|
-
allocation and proves one rollback close.
|
|
925
|
-
|
|
926
|
-
## Long-running poller
|
|
927
|
-
|
|
928
|
-
Return separate readiness and completion promises. Readiness says dependants may
|
|
929
|
-
start; completion represents the whole background lifetime.
|
|
930
|
-
|
|
931
|
-
```ts
|
|
932
|
-
const poller = defineManagedResource({
|
|
933
|
-
id: 'poller',
|
|
934
|
-
start: ({ signal }) => ({
|
|
935
|
-
ready: providerPoller.ready,
|
|
936
|
-
completion: providerPoller.run(signal),
|
|
937
|
-
}),
|
|
938
|
-
stopAdmission: () => providerPoller.stop(),
|
|
939
|
-
drain: () => providerPoller.completion,
|
|
940
|
-
close: () => providerPoller.close(),
|
|
941
|
-
})
|
|
942
|
-
```
|
|
943
|
-
|
|
944
|
-
Completion before readiness fails startup and rolls back. Completion after
|
|
945
|
-
readiness removes application readiness. Provider cursor persistence, retry,
|
|
946
|
-
backoff and restart policy remain application/provider concerns.
|
|
947
|
-
|
|
948
|
-
## Queue consumer
|
|
949
|
-
|
|
950
|
-
Acquire the application lease only after provider delivery/claim. If admission
|
|
951
|
-
is already closed, reject the delivery through the provider's own nack/requeue
|
|
952
|
-
primitive. Work admitted before shutdown releases its lease after ack/nack and
|
|
953
|
-
therefore participates in application drain.
|
|
954
|
-
|
|
955
|
-
```ts
|
|
956
|
-
async function handleDelivery(delivery: Delivery) {
|
|
957
|
-
const lease = app.admission.acquire()
|
|
958
|
-
if (!lease) return delivery.nack({ requeue: true })
|
|
959
|
-
try {
|
|
960
|
-
await processClaim(delivery.claim)
|
|
961
|
-
await delivery.ack()
|
|
962
|
-
} catch (error) {
|
|
963
|
-
await delivery.nack({ requeue: shouldRetry(error) })
|
|
964
|
-
throw error
|
|
965
|
-
} finally {
|
|
966
|
-
lease.release()
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
```
|
|
970
|
-
|
|
971
|
-
Stitchkit owns only process-local admission accounting and drain. Durable claims,
|
|
972
|
-
visibility timeouts, deduplication, idempotency and retry classification remain
|
|
973
|
-
in the queue/product layer.
|
|
974
|
-
|
|
975
|
-
## Operational publisher
|
|
976
|
-
|
|
977
|
-
Project anonymous aggregate activity, then feed absolute snapshots into the
|
|
978
|
-
latest-value sink. A slow transport holds one write plus one replaceable latest
|
|
979
|
-
revision rather than an unbounded event queue.
|
|
980
|
-
|
|
981
|
-
```ts
|
|
982
|
-
const publisher = createApplicationSnapshotSink({
|
|
983
|
-
write: (snapshot) => monitoring.publish(snapshot),
|
|
984
|
-
})
|
|
985
|
-
const activity = createActivityProjection({
|
|
986
|
-
id: 'generation',
|
|
987
|
-
stages: ['queued', 'running'],
|
|
988
|
-
})
|
|
989
|
-
const unsubscribe = activity.subscribe((snapshot) => {
|
|
990
|
-
publisher.publish(snapshot)
|
|
991
|
-
})
|
|
992
|
-
|
|
993
|
-
// cleanup boundary
|
|
994
|
-
unsubscribe()
|
|
995
|
-
publisher.publish(activity.getSnapshot())
|
|
996
|
-
await publisher.close()
|
|
997
|
-
```
|
|
998
|
-
|
|
999
|
-
The monitoring backend, transport retry and cross-process aggregation remain
|
|
1000
|
-
outside Stitchkit. The executable recipe blocks revision 0, coalesces
|
|
1001
|
-
intermediate revisions, explicitly admits the final absolute snapshot after
|
|
1002
|
-
unsubscribe and proves that `close()` flushes revision 3. Do not rely on an
|
|
1003
|
-
asynchronous subscriber callback racing cleanup: publish `getSnapshot()` before
|
|
1004
|
-
closing the outer sink, so any older or duplicate late delivery is rejected as
|
|
1005
|
-
stale instead of dropping the final state.
|
|
1006
|
-
|
|
1007
|
-
## Bound a handler and a local worker with one lease policy
|
|
1008
|
-
|
|
1009
|
-
Use a bounded admission when two entry paths consume the same finite local
|
|
1010
|
-
capacity. Composing it with `application.admission` keeps readiness and shutdown
|
|
1011
|
-
as the upstream gate:
|
|
1012
|
-
|
|
1013
|
-
```ts
|
|
1014
|
-
const work = createBoundedAdmission({
|
|
1015
|
-
upstream: app.admission,
|
|
1016
|
-
policy: {
|
|
1017
|
-
global: { maxConcurrent: 4 },
|
|
1018
|
-
perKey: { maxConcurrent: 1, maxKeys: 1_000 },
|
|
1019
|
-
},
|
|
1020
|
-
})
|
|
1021
|
-
|
|
1022
|
-
const fromHttp = (key: string, signal: AbortSignal) =>
|
|
1023
|
-
work.run(key, (context) => render(context.signal), { signal, timeoutMs: 20_000 })
|
|
1024
|
-
|
|
1025
|
-
const fromWorker = (key: string) =>
|
|
1026
|
-
work.run(key, (context) => reconcile(context.signal))
|
|
1027
|
-
```
|
|
1028
|
-
|
|
1029
|
-
A timeout in `fromHttp` does not free a permit while `render` remains active.
|
|
1030
|
-
At shutdown call `work.stopAdmission()` with the other admission owners and
|
|
1031
|
-
await `work.drain(...)`; the result reports the actual remainder.
|
|
1032
|
-
|
|
1033
|
-
## Replace ad-hoc output and progress queues
|
|
1034
|
-
|
|
1035
|
-
Ordered output and replaceable progress are separate declarations:
|
|
1036
|
-
|
|
1037
|
-
```ts
|
|
1038
|
-
const lines = createBoundedChannel<string>({
|
|
1039
|
-
policy: 'ordered', maxItems: 100, maxBytes: 1_000_000,
|
|
1040
|
-
sizeOf: (line) => new TextEncoder().encode(line).byteLength,
|
|
1041
|
-
})
|
|
1042
|
-
|
|
1043
|
-
const state = createBoundedChannel<{ revision: number; percent: number }>({
|
|
1044
|
-
policy: 'latest', maxItems: 1, maxBytes: 64,
|
|
1045
|
-
sizeOf: () => 64,
|
|
1046
|
-
})
|
|
1047
|
-
|
|
1048
|
-
const lineResult = lines.offer('one durable-in-process ordering unit')
|
|
1049
|
-
const stateResult = state.offer({ revision: 2, percent: 50 })
|
|
1050
|
-
```
|
|
1051
|
-
|
|
1052
|
-
Handle `refused` from `lines` at the protocol boundary; do not turn it into
|
|
1053
|
-
implicit loss. A `coalesced` state result is expected latest-value behaviour,
|
|
1054
|
-
not evidence that an ordered event was delivered. Neither channel is durable;
|
|
1055
|
-
persist first when restart replay is required.
|
|
1056
|
-
|
|
1057
|
-
## Handing a handle to the resources that depend on it
|
|
1058
|
-
|
|
1059
|
-
`dependsOn` carries ordering. To carry the object as well, return a `value` from
|
|
1060
|
-
`start` and read it with `context.use(...)`. This replaces the module-local
|
|
1061
|
-
`let handle: T | null` with the guard the graph makes unreachable.
|
|
1062
|
-
|
|
1063
|
-
```ts
|
|
1064
|
-
const database = defineManagedResource({
|
|
1065
|
-
id: 'database',
|
|
1066
|
-
start: async () => ({ value: await connect(env.DATABASE_URL) }),
|
|
1067
|
-
})
|
|
1068
|
-
|
|
1069
|
-
const worker = defineManagedResource({
|
|
1070
|
-
id: 'worker',
|
|
1071
|
-
dependsOn: [database],
|
|
1072
|
-
start(context) {
|
|
1073
|
-
const db = context.use(database) // Connection — not Connection | null
|
|
1074
|
-
},
|
|
1075
|
-
})
|
|
1076
|
-
```
|
|
1077
|
-
|
|
1078
|
-
Declare the dependency with the **resource** whenever you intend to read from
|
|
1079
|
-
it: that is the form `use` can type, and it stops the declaration and the read
|
|
1080
|
-
from drifting. A string still expresses order on its own.
|
|
1081
|
-
|
|
1082
|
-
The value is published when `start` resolves and stays readable from `activate`
|
|
1083
|
-
and from the shutdown phases. `use` refuses a resource that was not declared in
|
|
1084
|
-
`dependsOn` — that only ever worked by luck of declaration order — and refuses a
|
|
1085
|
-
resource that published nothing, which the compiler refuses too. The executable
|
|
1086
|
-
recipe proves both the read and the refusal.
|
|
1087
|
-
|
|
1088
|
-
## Managed HTTP server
|
|
1089
|
-
|
|
1090
|
-
The main resource of a web backend, and the one whose ownership rule is
|
|
1091
|
-
counter-intuitive: give `managedServerResource` a **thunk** and it creates the
|
|
1092
|
-
server during `start`, after its dependencies are ready. Give it an
|
|
1093
|
-
already-listening server and it adopts that one instead.
|
|
1094
|
-
|
|
1095
|
-
```ts
|
|
1096
|
-
const http = managedServerResource({
|
|
1097
|
-
id: 'http',
|
|
1098
|
-
dependsOn: [database],
|
|
1099
|
-
server: (context) => {
|
|
1100
|
-
const db = context.use(database)
|
|
1101
|
-
return createServer({ port: env.PORT, services: createServices(db) })
|
|
1102
|
-
},
|
|
1103
|
-
})
|
|
1104
|
-
|
|
1105
|
-
const app = createApplication({ id: 'service', resources: [database, http] })
|
|
1106
|
-
await app.start() // the port is bound; the snapshot means it
|
|
1107
|
-
```
|
|
1108
|
-
|
|
1109
|
-
The server resource publishes its `ManagedServerHandle`, so anything that needs
|
|
1110
|
-
the running server — a Socket.IO attachment, a URL to log, a probe — reads it
|
|
1111
|
-
with `context.use(http)` rather than through a module-local.
|
|
1112
|
-
|
|
1113
|
-
The factory itself receives `ManagedResourceContext` too. It can read only the
|
|
1114
|
-
dependencies declared on the server resource and shares the graph's startup
|
|
1115
|
-
signal. A dependency failure prevents the factory from binding; a factory
|
|
1116
|
-
rejection is rolled back without invoking the factory a second time.
|
|
1117
|
-
|
|
1118
|
-
Do not spread this resource over your own `start` to control creation order:
|
|
1119
|
-
that shape exists only as a workaround for the version whose `start` was empty,
|
|
1120
|
-
and `dependsOn` now expresses the order directly. The executable recipe binds a
|
|
1121
|
-
real port, proves a request reaches it after `start()` resolves, and proves the
|
|
1122
|
-
port is closed after `shutdown()`.
|
|
1123
|
-
|
|
1124
|
-
## Deletion checklist
|
|
1125
|
-
|
|
1126
|
-
After the cutover, remove the old generic lifecycle path completely:
|
|
1127
|
-
|
|
1128
|
-
- duplicate `process.on(...)` handlers and shutdown promise caches;
|
|
1129
|
-
- manual readiness waiters and resource close fan-out;
|
|
1130
|
-
- raw interval handles, overlap flags and timer drain code;
|
|
1131
|
-
- global in-flight counters and waiter sets replaced by application leases;
|
|
1132
|
-
- progress delta queues replaced by absolute snapshot publishing.
|
|
1133
|
-
|
|
1134
|
-
Keep the product code that still owns durable state and policy:
|
|
1135
|
-
|
|
1136
|
-
- database schema, transactions and migrations;
|
|
1137
|
-
- queue claims, ack/nack, deduplication and external-effect idempotency;
|
|
1138
|
-
- provider cursor, retry/backoff and credentials;
|
|
1139
|
-
- monitoring transport configuration and durable retention;
|
|
1140
|
-
- process exit code, hard-exit and deployment/supervisor policy.
|
|
1141
|
-
|
|
1142
|
-
Run the old and new lifecycle paths separately during development if needed,
|
|
1143
|
-
but do not ship both. Before cutover, verify there is exactly one signal binding,
|
|
1144
|
-
one timer owner, one admission gate and one resource cleanup chain.
|