@orkestrel/scaffold 0.0.91 → 0.0.93

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 (42) hide show
  1. package/dist/agents/templates/brief.md +2 -0
  2. package/dist/bin/main.js +6 -6
  3. package/dist/bin/main.js.map +1 -1
  4. package/dist/host/AGENTS.md +2 -2
  5. package/dist/host/agents/orchestration.md +1 -1
  6. package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +4 -2
  7. package/dist/host/agents/skills/enterprise-bootstrap/references/responsive-layout.md +1 -1
  8. package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +3 -3
  9. package/dist/host/agents/skills/orkestrel-falsify/SKILL.md +3 -3
  10. package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +1 -1
  11. package/dist/host/agents/skills/orkestrel-publish/references/window.md +3 -4
  12. package/dist/host/agents/skills/orkestrel-scout/SKILL.md +1 -1
  13. package/dist/host/agents/templates/brief.md +2 -0
  14. package/dist/host/agents/transports/cursor.md +1 -1
  15. package/dist/host/claude/agents/analyst.md +1 -1
  16. package/dist/host/claude/agents/grok.md +1 -1
  17. package/dist/host/claude/agents/orkestrel.md +8 -8
  18. package/dist/host/claude/agents/planner.md +1 -1
  19. package/dist/host/claude/agents/researcher.md +1 -1
  20. package/dist/host/claude/agents/scout.md +1 -1
  21. package/dist/host/claude/rules/names.md +1 -1
  22. package/dist/host/claude/rules/patterns.md +1 -1
  23. package/dist/host/claude/rules/quality.md +11 -0
  24. package/dist/host/claude/rules/styles.md +5 -0
  25. package/dist/host/claude/rules/tests.md +7 -8
  26. package/dist/host/claude/rules/writing.md +2 -1
  27. package/dist/host/codex/agents/researcher.toml +2 -2
  28. package/dist/host/codex/agents/scout.toml +1 -1
  29. package/dist/host/dotfiles/oxlintrc.json +168 -0
  30. package/dist/host/guides/browser.md +483 -392
  31. package/dist/host/guides/mcp.md +15 -15
  32. package/dist/host/guides/pool.md +190 -69
  33. package/dist/host/guides/probe.md +262 -216
  34. package/dist/host/guides/router.md +1 -1
  35. package/dist/host/guides/worker.md +43 -7
  36. package/dist/host/manifest.json +34 -34
  37. package/dist/host/tests/config.test.ts +46 -3
  38. package/dist/src/core/index.cjs +6 -6
  39. package/dist/src/core/index.cjs.map +1 -1
  40. package/dist/src/core/index.js +6 -6
  41. package/dist/src/core/index.js.map +1 -1
  42. package/package.json +6 -6
@@ -1,9 +1,7 @@
1
1
  # Probe
2
2
 
3
- > The claim prover for the `@orkestrel` line: an instrument that runs a claim's case and its
4
- > negative control through the workspace's own TypeScript, Oxlint, and Vitest, and returns a
5
- > `Verdict` carrying every issue — and a `receipt` when the case ran clean and the control broke
6
- > where it said it would.
3
+ > A claim prover that checks a case and its negative control with the workspace’s TypeScript,
4
+ > Oxlint, and Vitest, with eager lint and runtime onset and type warming before proof.
7
5
 
8
6
  A `Claim`, a `Verdict`, and a `receipt` carry the package. A `Claim` is the question: a case, a
9
7
  control that must break, and the TypeScript project both are judged under. A `Verdict` is the
@@ -47,8 +45,8 @@ A `Shape` cell holds an interface's data members as bare names in braces, `?` ma
47
45
  | `Project` | interface | `{ path, digest }` | Names the TypeScript project that judged a verdict's candidate drafts. |
48
46
  | `Verdict` | interface | `{ id, digest, toolchain, project, reason?, case, control, elapsed, receipt? }` | Carries the full result of one claim: every stage, for both the case and its control. |
49
47
  | `ProbeEventMap` | type | `{ arm, prove, expire, error }` | Reports what a probe observes while it serves. |
50
- | `ProbeOptions` | interface | `{ on?, error?, workspace?, deadline? }` | Configures a probe. |
51
- | `ProbeInterface` | interface | `{ emitter, toolchain } plus prove, destroy` | Answers a claim with type, lint, and runtime evidence in one call. |
48
+ | `ProbeOptions` | interface | `{ on?, error?, workspace?, deadline?, warm? }` | Configures a probe. |
49
+ | `ProbeInterface` | interface | `{ emitter, toolchain } plus start, prove, destroy` | Answers a claim with type, lint, and runtime evidence in one call. |
52
50
  | `ProbeErrorCode` | type | `'refused' \| 'missing' \| 'malformed' \| 'destroyed' \| 'deadline'` | Names the condition that ended a probe operation, derived from `PROBE_ERROR_CODES`. |
53
51
  | `ProbeErrorContext` | interface | `{ stage?, path?, project?, name?, deadline?, value? }` | Carries the structured detail one probe failure reports beside its message. |
54
52
  | `ProbeErrorOptions` | interface | `{ origin, code, context?, cause? }` | Configures one probe failure at construction. |
@@ -75,6 +73,9 @@ A `Shape` cell holds the constant's declared type.
75
73
  | `PROBE_SPECIFICATIONS` | const | `number` | Names the specification lifetime the runtime stage replaces its resident Vitest service at, 64 specifications. |
76
74
  | `RUNTIME_PLUGIN` | const | `string` | Names the Vite plugin the runtime stage installs into a target workspace's Vitest configuration, `'orkestrel-runtime-overlay'`. |
77
75
  | `TYPE_MIRROR` | const | `string` | Names the workspace-relative directory the type stage keeps its workspace mirror under, `'tmp/type'`. |
76
+ | `PROBE_RESTARTS` | const | `number` | Bounds consecutive failed warms or idle losses before a stage's floor is spent. |
77
+ | `PROBE_WARM` | const | `number` | Bounds the default type warm at 90,000 ms, independently of active inspections. |
78
+ | `LINT_TEARDOWN` | const | `number` | Bounds lint teardown at 16,000 ms, including protocol exchanges and process termination. |
78
79
 
79
80
  ### Errors
80
81
 
@@ -151,9 +152,9 @@ A `Shape` cell holds an interface's data members as bare names in braces, `?` ma
151
152
  | `Inspection` | interface | `{ subject, claim }` | Carries one queued inspection: the case a stage reads and the claim it belongs to. |
152
153
  | `InspectionOptions` | interface | `{ signal }` | Carries the bound a caller holds over one stage inspection. |
153
154
  | `OverlayInterface` | interface | `{ revision, paths } plus set, text, covers, clear` | Holds the candidate drafts one inspection substitutes for the files a tool would read from disk. |
154
- | `StageInterface` | interface | `{ stage, progress } plus inspect, destroy` | Inspects one case with the workspace's own tool. |
155
+ | `StageInterface` | interface | `{ stage, progress } plus start, inspect, destroy` | Inspects one case with the workspace's own tool. |
155
156
  | `TypeStageInterface` | interface | `StageInterface plus inspect, resolve` | Inspects TypeScript source against a caller-named project and reports what that project is. |
156
- | `LintStageInterface` | interface | `StageInterface plus inspect` | Inspects one case under a bound the caller supplies. |
157
+ | `LintStageInterface` | interface | `StageInterface plus { exit } plus inspect` | Inspects one case under a bound the caller supplies. |
157
158
  | `WorkspaceManifest` | interface | `{ path, contents }` | Carries one parsed package manifest and the path it came from. |
158
159
  | `Diagnostic` | interface | `{ path?, range?, message }` | Carries one diagnostic line a compiler run reported, in this package's own coordinates. |
159
160
  | `ProjectConfig` | interface | `{ compilerOptions, files?, include? }` | Carries what one TypeScript project resolved to, as the compiler itself printed it. |
@@ -185,14 +186,14 @@ The classes, each exported from its own file, and the contract each one implemen
185
186
  [`RuntimeStage`](../src/server/stages/RuntimeStage.ts) implements `StageInterface`, and
186
187
  [`Overlay`](../src/server/Overlay.ts) implements `OverlayInterface`.
187
188
 
188
- | Name | Kind | Summary |
189
- | -------------- | ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
190
- | `Probe` | class | Answers claims through its type, lint, and runtime stages. |
191
- | `ProbeServer` | class | Implements `ProbeServerInterface` over a `PassThrough` stream this server owns, binding the published `prove` tool and the dual-era dispatcher to this process's Model Context Protocol stdio transport. |
192
- | `TypeStage` | class | Inspects TypeScript source by running the target workspace's own compiler over a mirror of it. |
193
- | `LintStage` | class | Inspects virtual documents through one resident Oxlint language server. |
194
- | `RuntimeStage` | class | Inspects tests through one resident Vitest service from the target workspace. |
195
- | `Overlay` | class | Implements `OverlayInterface` over a private map from normalized absolute path to candidate text, minting at construction the `revision` a resident tool caches its answers against. |
189
+ | Name | Kind | Summary |
190
+ | -------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
191
+ | `Probe` | class | Answers claims through its type, lint, and runtime stages. |
192
+ | `ProbeServer` | class | Implements `ProbeServerInterface` over a `PassThrough` stream this server owns, binding the published `prove` tool and the dual-era dispatcher to this process's Model Context Protocol stdio transport. Starting creates the probe and awaits its lint and runtime onset; type warming and the boot controls continue behind it. |
193
+ | `TypeStage` | class | Inspects TypeScript source by running the target workspace's own compiler over a mirror of it. |
194
+ | `LintStage` | class | Inspects virtual documents through one resident Oxlint language server. |
195
+ | `RuntimeStage` | class | Inspects tests through one resident Vitest service from the target workspace. |
196
+ | `Overlay` | class | Implements `OverlayInterface` over a private map from normalized absolute path to candidate text, minting at construction the `revision` a resident tool caches its answers against. |
196
197
 
197
198
  Each stage takes one optional `workspace` argument and defaults to the working directory. A stage
198
199
  serves one inspection at a time and admits none itself, so drive stages through `Probe` unless you
@@ -211,6 +212,23 @@ before the runtime overlay` rather than leaving it answered silently. `TypeStage
211
212
  it writes each draft into its mirror as a real file, so the host's own file-name comparison decides
212
213
  what a draft shadows.
213
214
 
215
+ ### Server errors
216
+
217
+ The handshake failure factory comes from [errors.ts](../src/server/errors.ts).
218
+
219
+ | Name | Kind | Signature | Summary |
220
+ | ---------------------- | -------- | --------------------------------- | ---------------------------------------------------------------------------------- |
221
+ | `createHandshakeError` | function | `(error: ProbeError) => MCPError` | Creates the protocol failure returned when the MCP handshake cannot arm the probe. |
222
+
223
+ The factory preserves the classified cause on the protocol response.
224
+
225
+ ```ts
226
+ import { createDestroyedError } from '@orkestrel/probe'
227
+ import { createHandshakeError } from '@orkestrel/probe/server'
228
+
229
+ createHandshakeError(createDestroyedError('probe server')).code // -32000
230
+ ```
231
+
214
232
  ### Server helpers
215
233
 
216
234
  Pure leaves and workspace readers, from [`helpers.ts`](../src/server/helpers.ts).
@@ -264,35 +282,42 @@ The public call-signature members of each behavioral interface, one table per in
264
282
 
265
283
  | Method | Returns | Summary |
266
284
  | --------- | ------------------ | --------------------------------------------------------------------------- |
267
- | `prove` | `Promise<Verdict>` | Answers one claim with every stage's evidence. |
285
+ | `start` | `Promise<void>` | Starts every stage's floor and waits for lint and runtime to warm. |
286
+ | `prove` | `Promise<Verdict>` | Answers one claim with every stage's evidence after calling `start()`. |
268
287
  | `destroy` | `Promise<void>` | Tears down every stage and releases the processes and the mirror they hold. |
269
288
 
270
289
  #### `StageInterface`
271
290
 
272
291
  | Method | Returns | Summary |
273
292
  | --------- | ---------------- | ---------------------------------------------------------------------- |
293
+ | `start` | `Promise<void>` | Begins the warm or joins the warm already begun. |
274
294
  | `inspect` | `Promise<Check>` | Inspects one case. |
275
295
  | `destroy` | `Promise<void>` | Tears down the resident tool or the mirror and releases its resources. |
276
296
 
277
297
  #### `RuntimeStage`
278
298
 
299
+ The stage implements the preceding `StageInterface` contract.
300
+
279
301
  | Method | Returns | Summary |
280
302
  | --------- | ---------------- | ---------------------------------------------------------------------- |
303
+ | `start` | `Promise<void>` | Begins the warm or joins the warm already begun. |
281
304
  | `inspect` | `Promise<Check>` | Inspects one case. |
282
305
  | `destroy` | `Promise<void>` | Tears down the resident tool or the mirror and releases its resources. |
283
306
 
284
307
  #### `TypeStageInterface`
285
308
 
286
- | Method | Returns | Summary |
287
- | --------- | ------------------ | ----------------------------------------------------------------------------- |
288
- | `inspect` | `Promise<Check>` | Inspects one case, against a caller-named project where the caller names one. |
289
- | `resolve` | `Promise<Project>` | Resolves one project to the path and digest the stage applies for it. |
290
- | `destroy` | `Promise<void>` | Tears down the resident tool or the mirror and releases its resources. |
309
+ | Method | Returns | Summary |
310
+ | --------- | ------------------ | ----------------------------------------------------------------------------------------------------------------- |
311
+ | `start` | `Promise<void>` | Begins the warm or joins the warm already begun. |
312
+ | `inspect` | `Promise<Check>` | Inspects one case, against a caller-named project where the caller names one. |
313
+ | `resolve` | `Promise<Project>` | Resolves one project to the path and digest the stage applies for it. Calls `start()` before reading the project. |
314
+ | `destroy` | `Promise<void>` | Tears down the resident tool or the mirror and releases its resources. |
291
315
 
292
316
  #### `LintStageInterface`
293
317
 
294
318
  | Method | Returns | Summary |
295
319
  | --------- | ---------------- | ---------------------------------------------------------------------- |
320
+ | `start` | `Promise<void>` | Begins the warm or joins the warm already begun. |
296
321
  | `inspect` | `Promise<Check>` | Inspects one case, under the bound the caller supplies. |
297
322
  | `destroy` | `Promise<void>` | Tears down the resident tool or the mirror and releases its resources. |
298
323
 
@@ -309,9 +334,21 @@ The public call-signature members of each behavioral interface, one table per in
309
334
 
310
335
  | Method | Returns | Summary |
311
336
  | --------- | --------------- | ------------------------------------------------------------------------- |
312
- | `start` | `void` | Serves the probe over this process's standard input and output. |
337
+ | `start` | `Promise<void>` | Begins serving and awaits the workspace probe's lint and runtime onset. |
313
338
  | `destroy` | `Promise<void>` | Releases the transport, the process listeners, and the probe behind them. |
314
339
 
340
+ The server's onset promise settles after lint and runtime warm; type warming and boot controls
341
+ continue before proof, as [Lifecycle](#lifecycle) documents. Call the onset method before serving
342
+ in-process work.
343
+
344
+ ```ts
345
+ import { Probe } from '@orkestrel/probe/server'
346
+
347
+ const probe = new Probe({ workspace: process.cwd(), warm: 90_000 })
348
+ await probe.start()
349
+ await probe.destroy()
350
+ ```
351
+
315
352
  ## What a probe proves
316
353
 
317
354
  Measure a performance claim first through a guarded bench block beside the probe test, run by the
@@ -472,9 +509,9 @@ together — where `instanceof` refuses a failure the other copy raised.
472
509
 
473
510
  probe borrows the target workspace's own toolchain and configuration, so a workspace missing any of
474
511
  these returns a failure the caller cannot diagnose from the verdict alone. Check them before you
475
- make a claim. A direct `Probe` runs its boot controls at construction. `ProbeServer` leaves discovery
476
- independent of the workspace toolchain and runs those controls when an admitted `prove` call
477
- constructs the real probe.
512
+ make a claim. A direct `Probe` starts warming through `start()` or an admitted `prove` call.
513
+ `ProbeServer` starts the probe during server onset; initialization waits for lint and runtime,
514
+ and proof waits for type readiness and the boot controls. See [Lifecycle](#lifecycle) for the tests.
478
515
 
479
516
  - **A Vitest project whose name the test's path infers.** A test under `tmp/probes/` names the
480
517
  `probe` project, and a test under `tests/src/<environment>/` names `src:<environment>`. Any other
@@ -516,7 +553,7 @@ constructs the real probe.
516
553
 
517
554
  Declare `@orkestrel/probe` as a development dependency of the workspace it inspects. Its
518
555
  `typescript` and `vitest` tools are optional peers; probe resolves them and `oxlint` from that
519
- workspace when a direct probe is constructed or a server admits a `prove` call. The `oxlint`
556
+ workspace when a direct probe is constructed or a server begins onset. The `oxlint`
520
557
  package carries no peer range because npm also resolves the peers of an optional peer: `oxlint`
521
558
  declares an optional `vite-plus` peer, `vite-plus` 1.0.0 depends on `vitest` 5, and npm 11.19.0
522
559
  refuses that conflict with the `vitest` peer range when it installs probe into an empty project
@@ -543,12 +580,45 @@ Register that entry rather than a global install, an `npx` invocation, or the `n
543
580
  shim. The shim is a shell script on POSIX hosts and a batch file on Windows, and spawning the
544
581
  JavaScript entry with the current executable is the form that survives both.
545
582
 
546
- Workspace prerequisite failures from an admitted `prove` call return as an `isError: true` tool
547
- result. The server keeps serving discovery and later calls, and a later admitted call retries failed
548
- construction or workspace arming. A failure thrown before the entry creates and starts its server is
549
- caught, written to stderr in the form `[origin] code: message`, and exits with status 1. Probe
550
- construction happens only after server startup, so no public input is known to reach that pre-start
551
- catch and its runtime behavior remains unproved.
583
+ In Codex, mark this server as required so a workspace refusal stops session creation and prints
584
+ its cause. The configuration values are:
585
+
586
+ ```text
587
+ [mcp_servers.probe]
588
+ command = "node"
589
+ args = ["node_modules/@orkestrel/probe/dist/bin/main.js"]
590
+ cwd = "/srv/checkout"
591
+ required = true
592
+ ```
593
+
594
+ No `startup_timeout_sec` override is required by the measured onset: M-A after the warm change
595
+ reported veneer at 7.107–7.939 s, scaffold at 3.171–4.051 s, and probe at 1.489–1.744 s, all within
596
+ Codex's 10 s default. Those are runs 1–3 per workspace in `warm2-reading-<workspace>-<run>.json`,
597
+ on LAPTOP-SBG38B5J, Windows, 2026-10-05; see [Cost](#cost) for the samples and host load.
598
+
599
+ M-E used Codex CLI 0.159.2 and Claude Code 2.1.285 on LAPTOP-SBG38B5J, Windows 11 Home
600
+ 10.0.26300 x64, i7-10700, on 2026-10-05, against build `058a946`, with Node 24.21.0 and the tool
601
+ versions in [Cost](#cost). Codex's refusing run with `required = true` exited 1 before an agent
602
+ turn, reporting `required MCP servers failed to initialize: probe` and the unreadable TypeScript
603
+ manifest cause. Its healthy run and metadata follow-up initialized at the default timeout.
604
+ Claude Code connected and discovered `mcp__probe__prove` in both its healthy and refusing runs;
605
+ those runs did not ask Claude to call the tool or display a tool-call refusal.
606
+
607
+ The measured client commands used `codex exec --ignore-user-config --ephemeral --json` with
608
+ the preceding server values passed through `-c`, and
609
+ `claude -p --mcp-config tmp/onset/claude-CASE.json --strict-mcp-config` with prompts requesting
610
+ tool names only; `CASE` selects the healthy or refusing fixture. These local stdio runs used
611
+ the clients' existing login sessions and requested no tool execution or approval. Records are
612
+ `tmp/onset/client-codex-{healthy,refusing,discovery}.json` and
613
+ `tmp/onset/client-claude-{healthy,refusing}.json`, summarized in `tmp/onset/readings.md`.
614
+ CPU snapshots were 0%, 4%, and 83% for those Codex runs and 1% and 7% for the Claude runs;
615
+ editor services and idle tool servers remained present.
616
+
617
+ An onset refusal answers `initialize` with JSON-RPC code `-32000`, the classified message, and
618
+ `{ origin, code }` data. The bin prints one `[origin] code: message` line to stderr and retains
619
+ exit status 1 until input ends, including after a later admitted call repairs the workspace;
620
+ discovery remains available. Proof: “refuses initialize, keeps discovery, repairs a blocked
621
+ workbench, and preserves exit one” in [main.test.ts](../tests/src/bin/main.test.ts).
552
622
 
553
623
  These facts decide whether a hand-written client works, and each fails silently when it is wrong:
554
624
 
@@ -566,15 +636,11 @@ These facts decide whether a hand-written client works, and each fails silently
566
636
  `-32022 Unsupported protocol version` and a `data.supported` list; measured on 2026-08-20 that
567
637
  list is `2026-07-28`, `2025-11-25`, and `2025-06-18`.
568
638
 
569
- The server answers the handshake era and the current revision together, so a client that sends
570
- `initialize` without `_meta` is served too. `ProbeServer` snapshots the supplied `ProbeOptions` and
571
- resolves the default or relative workspace when the server is constructed. It creates and caches a
572
- real probe only after a structurally valid, contained `prove` call is admitted. Concurrent admitted
573
- calls share its held construction and the probe it produces. `start()` seizes this process's standard
574
- input and output: a host that starts the server has given the process to it. `destroy()` gives the
575
- process back and tears down the probe when a call created it. Teardown entered through a construction
576
- callback waits for that admitted construction and releases its probe. Destruction before an admitted
577
- call does not arm the workspace.
639
+ The server answers the handshake era and the current revision together. `ProbeServer` snapshots
640
+ the supplied `ProbeOptions` and resolves the workspace at construction. `start()` attaches the
641
+ transport and begins workspace onset; repeated calls share its setup promise. `destroy()` releases
642
+ the probe and the listeners owned by the server. See [Lifecycle](#lifecycle) for the regression
643
+ tests of onset and teardown.
578
644
 
579
645
  A handshake-era `tools/call` may carry `_meta.progressToken`, including `0` or an empty string.
580
646
  The token does not change the verdict or its receipt. Probe emits no progress reports of its own;
@@ -626,12 +692,9 @@ answer arrived.
626
692
  **The `@orkestrel/mcp` stdio client drives claims through this entry.** It spawns the shipped
627
693
  `dist/bin/main.js`, negotiates the era itself, lists `prove`, and hands back the record described
628
694
  earlier. [`main.test.ts`](../tests/src/bin/main.test.ts) runs that round trip against the built entry
629
- on the current revision and through the legacy projection. A real Codex 0.153.4 app-server in the
630
- Scaffold workspace also started this entry and discovered `prove` in 575.8829 ms under its 30-second
631
- deadline. That reading stopped at discovery: Codex did not call `prove`, and the entry was not read
632
- from an installed tarball. Treat Codex claim execution and installed-package use as untested. The
633
- transport facts stated earlier were established against this repository's own hand-written line
634
- client, and the driven MCP client meets them too.
695
+ on the current revision and through the legacy projection. The M-E client readings described
696
+ earlier establish connection and discovery only; Codex and Claude Code claim execution and
697
+ installed-tarball use were not measured in those runs.
635
698
 
636
699
  **The advertised schema is wider than the admission rule, at `Draft.path`.** The `prove` tool
637
700
  publishes `compileSchema(CLAIM_SHAPE)` and admits a call with `isClaim`, and the two agree on every
@@ -683,7 +746,7 @@ const claim: Claim = {
683
746
  const probe = new Probe({ workspace: process.cwd() })
684
747
  const verdict = await probe.prove(claim)
685
748
  verdict.digest // 'bdf03e5dfd6bd413ead671c7a2940fcf'
686
- verdict.receipt // 'probe:bdf03e5dfd6bd413ead671c7a2940fcf:type:typescript@6.0.3:oxlint@1.86.0:vitest@4.1.11:configs/src/tsconfig.core.json@434f59254d58cf2683d453a26bd0d837'
749
+ verdict.receipt // 'probe:bdf03e5dfd6bd413ead671c7a2940fcf:type:typescript@6.0.3:oxlint@1.87.0:vitest@4.1.11:configs/src/tsconfig.core.json@434f59254d58cf2683d453a26bd0d837'
687
750
  await probe.destroy()
688
751
  ```
689
752
 
@@ -992,176 +1055,156 @@ never imported.
992
1055
 
993
1056
  ## Lifecycle
994
1057
 
995
- A probe has no `start`. Warming begins at construction and `prove` awaits it, because the harness
996
- owns the process: a restart is a new process rather than a second lifecycle, and a second client is
997
- a second process with its own stages. `ProbeServer` defers that real probe construction until an
998
- admitted call. `ProbeServer.start` is the transport's verb rather
999
- than the probe's — it decides which process reads the stdio, not when the stages warm.
1000
-
1001
- - **Arming.** Construction runs boot controls that mutate an imported dependency and refuse
1002
- service unless the type and runtime stages report the change. The `arm` event fires after those
1003
- controls have reported red and the boot's own files are gone. An attempt that rejects fires
1004
- `error` instead, carrying the arming refusal as the attempt raises it, so a host waiting on `arm`
1005
- reads the refusal rather than an event that never arrives. The attempt is still retained for
1006
- retry, so each attempt surfaces its own `error` and no `prove` reports one refusal twice. The
1007
- controls run under `tmp/probes/` against the root `tsconfig.json`, which is why the Vitest project,
1008
- its composition in the root configuration, and a `tmp/probes/` the host lets it create gate the
1009
- boot rather than a claim.
1010
- - **Freshness.** Every `prove` revalidates before it answers. The runtime stage re-reads each
1011
- workspace module and invalidates the ones whose contents moved; the type stage refreshes its
1012
- mirror of the workspace by content digest, copying a file whose contents moved and removing the
1013
- copy of a file the workspace deleted. A warm service that skipped this would return a confident
1014
- wrong answer about freshly edited source.
1015
- - **Configuration is read once per stage, not per claim.** Freshness covers source, and it does not
1016
- cover the configuration a stage's own tool was built around. The type stage reads
1017
- `tsc --showConfig` once per project and keys that reading by resolved project path, so a
1018
- `tsconfig.json` edited after that reading does not change the compiler options the stage applies
1019
- or the project digest it reports. A project that declares its own `include` still re-expands on
1020
- every run, because the scratch project each run reads extends the target's own project file; a
1021
- project that declares neither `include` nor `files` keeps the selection that reading printed.
1022
- Oxlint's language server and the resident Vitest hold their own configuration the same way. So a
1023
- receipt is read against the configuration the stage was built around. Destroy the probe and build
1024
- another after you edit `tsconfig.json`, `.oxlintrc.json`, or `vite.config.ts`.
1025
- - **A failed warm is not permanent.** The runtime stage holds its resident Vitest in a slot it
1026
- clears when that warm rejects, so the fault reaches the caller as the target tree's own —
1027
- `origin: 'workspace'`, `code: 'malformed'`, naming `vite.config.ts` in `context` — rather than
1028
- being masked by an aging resident runner. The next `inspect` finds the slot empty and warms fresh,
1029
- reading the configuration again, so a workspace repaired after the failed call serves the call
1030
- that follows it.
1031
- One call never loops through a second warm of its own, and no failure leaves the stage permanently
1032
- refusing. This is a recovery path rather than a reload: a warm that succeeded is kept, so the
1033
- preceding entry's rule about editing `vite.config.ts` stands.
1034
- - **Admission.** One queue per stage admits inspections in arrival order, one at a time. The
1035
- `deadline` covers active work rather than queue wait. Caller-named project resolution shares that
1036
- order with type inspections, so a resolve never runs partway through one inspection's own
1037
- candidate checks.
1038
- - **Expiry.** `ProbeOptions.deadline` is the coordinator's budget for one active stage inspection,
1039
- and it lives outside the worker because a Vitest `testTimeout` cannot fire while a synchronous
1040
- loop blocks that worker. An expiry at any stage abandons that stage, replaces it before the next
1041
- queued inspection begins, and emits `expire` with the claim that expired. A failed boot is
1042
- replaced the same way: the next claim runs the controls again rather than inheriting a refusal.
1043
- - **The budget covers the warm.** Every stage's inspection awaits that stage's warm, and the type
1044
- stage's warm builds each declared project's incremental state before the first inspection answers.
1045
- So a `deadline` under that warm expires arming rather than any claim, and the probe never arms.
1046
- Size `deadline` above the § Cost reading for the target tree, and leave room for a contended host.
1047
- No stage holds the host's loop: the compiler, the language server, and the test runner each work
1048
- in a child process or a worker, so the deadline fires on time and terminates the work it bounds.
1049
- - **Revisions.** Each runtime inspection writes its specification at a fresh path and never reuses
1050
- one, because a resident runner asked to re-run a path it has already seen reports a false pass.
1051
- One inspection in every 64 also replaces the resident runner, and that inspection costs more than
1052
- the other 63 — budget `deadline` against that one rather than the common one. That fresh path
1053
- never reaches a caller: a test that reads its own filename, through `import.meta.url` or through a
1054
- frame in a failure it raised, reports the path the claim declared, because the stage rewrites the
1055
- exact basename it generated back to the declared test's basename in every message it reports.
1056
- - **Teardown.** `destroy()` releases every resident process and is idempotent. It releases the
1057
- emitter last, and releases it on a teardown that failed too, so a listener registered through
1058
- `ProbeOptions.on` or through `probe.emitter` receives nothing after teardown settles and
1059
- `probe.emitter.destroyed` reads true. A refusal a later `prove` raises still reaches the caller
1060
- that asked for it, and reaches no listener. `ProbeServer.destroy`
1061
- adds the process itself: it removes the listeners `start` attached — the `data`, `close`, and
1062
- `error` forwarders on standard input, and the `SIGINT` and `SIGTERM` handlers on the process —
1063
- and pauses the stream unless `start` found it already flowing, so the
1064
- event loop drains and the process exits 0 with no explicit exit call. A stream nobody has read yet
1065
- is neither flowing nor paused, and this server is what sets it flowing, so it is paused. A host
1066
- already reading its own standard input keeps reading it after the server it embedded is destroyed.
1067
- - **The server removes only what it added.** Every listener `ProbeServer` attaches is held as a
1068
- field and removed by reference, so a listener a host registers while the server is serving is
1069
- still attached and still fires afterwards. Nothing is chosen by being absent from a capture,
1070
- because a capture cannot tell a listener the server added from one the host added later. The
1071
- transport is what makes that reachable: it reads a stream the server owns rather than this
1072
- process's standard input, so its own listeners never land on `process.stdin` and the only
1073
- listeners the server puts there are the `data`, `close`, and `error` forwarders into that stream.
1074
- The release-time
1075
- reader count is load-bearing for the same reason — a host that starts reading standard input
1076
- while the server is serving keeps its reader and keeps the flow, even though `start` found the
1077
- stream stopped and would otherwise pause it.
1078
- - **Stage teardown is bounded, and each stage is bounded by something different.** Every stage
1079
- abandons the inspections it holds rather than waiting behind one, and what it then waits for
1080
- differs per stage. The lint stage holds a bound of its own and sets it on the `@orkestrel/lsp`
1081
- client it drives: 2 s for each lifecycle exchange the Language Server Protocol leaves to the
1082
- server — the `initialize` reply that warming waits for and the `shutdown` reply that ending waits
1083
- for. It does not reach the diagnostics an inspection waits for, which the caller's own signal
1084
- bounds instead, so a tight teardown bound no longer preempts a claim's budget. The transport's
1085
- cooperative window is half that 2 s, so a server that answers `shutdown` and then ignores `exit`
1086
- is signalled and released inside the client's own wait for the close, rather than deadlocking
1087
- `destroy()`. A server that accepts the connection and answers nothing is released the same way.
1088
- The type stage holds no bound and needs none for its own tools: it terminates the compiler it
1089
- spawned — by process tree on Windows, where no cooperative signal reaches a child — waits for the
1090
- warm it started, and then deletes its mirror. The runtime stage holds no bound either, so a
1091
- `vitest.close()` that never settles is bounded by the coordinator instead: `Probe.destroy` races
1092
- each stage's teardown against `ProbeOptions.deadline` and proceeds when the budget expires. What
1093
- an abandoned tool still holds it holds until this process ends, so that bound buys the signal path
1094
- rather than the resource — `destroy()` settles for a caller that set a budget it can wait for,
1095
- instead of hanging behind a stage that will not close.
1096
- - **Termination.** `ProbeServer.start` answers `SIGINT` and `SIGTERM` by destroying the server, and
1097
- they are the whole set: no evidence names a harness that ends a stdio child any other way, and
1098
- a configurable set would be a supported way to spell the leak this closes. Another signal arriving
1099
- during a teardown already running reaches the default disposition and ends the process at once,
1100
- because teardown releases its handlers before the probe. Measured on 2026-08-20 on the host § Cost
1101
- names, signal to child exit is 2.2 s to 2.3 s during boot and 50 ms to 59 ms against an armed
1102
- probe, over 3 runs each. The boot-time figure is the long one because teardown awaits the boot in
1103
- flight; budget a harness's grace window against it rather than against the warm case.
1104
- - **The listener race.** Every `createVitest` call installs `SIGINT` and `SIGTERM` handlers that end
1105
- this process about a millisecond after the signal, which is three orders of magnitude inside the
1106
- teardown the preceding **Termination** entry measures. The runtime stage removes the handlers its
1107
- own warm installed, as the call
1108
- returns and before anything is awaited, so no window exists for a signal to arrive in. Without
1109
- that, a graceful teardown reads as fixed, passes a manual test, and still leaves its files in the
1110
- consumer's tree.
1111
- - **What a killed host leaves.** A host killed without `destroy` — `SIGKILL`, a power loss, a
1112
- harness that never signals — can leave a generated specification or a boot dependency behind.
1113
- Every file this package writes into a target carries `probe-<pid>-<uuid>` between its stem and its
1114
- extension, and the runtime stage deletes such a file at its next warm when the process id leads a
1115
- process that is gone **and** the file is one this package can attribute. Attribution is what stops
1116
- the sweep reaching your tree: a generated specification carries your own test text, so probe
1117
- closes the file with the marker `// @orkestrel/probe generated specification <pid>-<uuid>`, and
1118
- the sweep requires that marker to name the same revision the file name does. The boot
1119
- dependencies carry the same marker, so nothing is attributed by its path and nothing under
1120
- `tmp/probes/` is deleted for sitting there. A file of yours that happens to carry the same name
1121
- shape is left where it is, wherever it sits, and so is a live neighbour's specification.
1122
- - **What the type stage leaves.** Its mirror is one directory under `TYPE_MIRROR`, named for the
1123
- writing host's process id and a fresh UUID, carrying that same marker at `.probe/mirror.txt`.
1124
- `destroy()` deletes it, and the next stage constructed against that workspace sweeps a mirror
1125
- whose process id names a host that is gone and whose marker names its own directory. A directory
1126
- failing any of those reads stays where it is. Nothing the stage writes reaches a path outside
1127
- `tmp/`, so a target's version-controlled tree never carries a draft.
1058
+ The following contracts name their regression tests; the measured observations and the unproved
1059
+ survivor rule are identified separately.
1060
+
1061
+ - **Onset.** Construction defers warming until `Probe.start()` or an admitted `prove` call;
1062
+ repeated onset calls join the same work (test: “defers construction, joins onset, and refuses
1063
+ start after teardown” in [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1064
+ Server startup constructs the probe before a tool call, and `initialize` waits for lint and
1065
+ runtime while the type warm continues behind it (test: “answers initialize while the type warm
1066
+ is held open without an admitted call” in
1067
+ [ProbeServer.test.ts](../tests/src/server/ProbeServer.test.ts)).
1068
+ - **Type readiness.** Every admitted `prove` call waits for type warming and the boot controls
1069
+ before inspection; `ProbeOptions.warm`, defaulting to `PROBE_WARM` at 90,000 ms, bounds type
1070
+ warming separately from `ProbeOptions.deadline` (tests: “waits for a type warm beyond the
1071
+ inspection deadline and then proves” and “bounds a type warm, spends replacements, and recovers
1072
+ for a later prove” in [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1073
+ A failed type warm reaches the next call with its original cause even if an automatic
1074
+ replacement has armed (tests: “retains a failed type warm for the next prove and recovers after
1075
+ replacement”, “reports a failed type warm even when its automatic replacement has armed”, and
1076
+ “consumes a failed type refill at the queued call and serves the next claim”
1077
+ in [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1078
+ - **Admission.** Each stage has its own pool with one resident stage and exclusive leases, admitting
1079
+ inspections in arrival order; project resolution takes a type-stage lease in that same order
1080
+ (tests: “admits one inspection per stage at a time, in arrival order” and “serializes project
1081
+ resolution against a live type inspection” in
1082
+ [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1083
+ - **Creation and replacement.** The pool's creation hook holds a deadline over stage warming:
1084
+ `warm` for type, `deadline` for lint and runtime; a silent lint initialization spends the
1085
+ bounded replacement attempts instead of holding onset indefinitely (tests: “spends silent
1086
+ initializes through the coordinator deadline” and “bounds a type warm, spends replacements,
1087
+ and recovers for a later prove” in [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1088
+ `PROBE_RESTARTS` bounds failed warms and idle losses, and an explicit start restores a spent
1089
+ stage before the next claim (tests: “spends failed warms, unwraps their refusal, and rearms
1090
+ after repair” and “spends the floor on used idle loss and restores it through start without a
1091
+ failed claim” in [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1092
+ This replaces 0.0.20's retry-once arming with pool replacement and a retained type-warm failure
1093
+ for the next caller (tests: “spends failed warms, unwraps their refusal, and rearms after repair”
1094
+ and “reports a failed type warm even when its automatic replacement has armed” in
1095
+ [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1096
+ - **Oxlint loss.** An unexpected lint exit starts replacement without a claim, and an exit during
1097
+ inspection preserves the exit diagnosis before a later claim uses the replacement (test:
1098
+ “recovers an idle lint exit and serves after an exit during a claim” in
1099
+ [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1100
+ Intentional teardown leaves `LintStageInterface.exit` pending (test: “joins initialize and leaves
1101
+ exit pending after teardown” in [LintStage.test.ts](../tests/src/server/stages/LintStage.test.ts)).
1102
+ - **Inspection expiry.** The inspection deadline begins after admission, and expiry replaces the
1103
+ affected stage before later work uses it (tests: “expires only the active inspection, cleans its
1104
+ revision, and serves a queued claim”, “replaces a type stage its deadline destroyed”, and
1105
+ “replaces a lint stage its deadline destroyed” in
1106
+ [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1107
+ - **Freshness.** Type and runtime inspections read changed dependency contents again (test:
1108
+ “changes its verdict after an imported dependency changes on disk” in
1109
+ [TypeStage.test.ts](../tests/src/server/stages/TypeStage.test.ts) and
1110
+ [RuntimeStage.test.ts](../tests/src/server/stages/RuntimeStage.test.ts)).
1111
+ A type stage retains a project's resolved configuration for its lifetime (test: “reads one
1112
+ project configuration for the life of the stage” in
1113
+ [TypeStage.test.ts](../tests/src/server/stages/TypeStage.test.ts)); after changing tool
1114
+ configuration, destroy the probe and construct another.
1115
+ - **Teardown.** End of input releases the server's lint child, and end of input during setup
1116
+ closes without a refusal on stderr (tests: “answers initialize with a live lint child and
1117
+ releases it at input end” and “closes input during setup without stderr and exits zero” in
1118
+ [main.test.ts](../tests/src/bin/main.test.ts)).
1119
+ Teardown cuts an in-flight warm, releases the emitter, and makes subsequent starts refuse
1120
+ (tests: “cuts a warm on teardown without surfacing an arm refusal”, “destroys idempotently and
1121
+ releases the listeners its host registered”, and “defers construction, joins onset, and refuses
1122
+ start after teardown” in [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1123
+ Server teardown removes its own listeners and preserves a host's readers and signal handlers
1124
+ (tests: “returns the process it seized, and settles once”, “keeps a signal listener a host attached
1125
+ while it was serving”, and “keeps delivering to a reader that started reading while it was
1126
+ serving” in [ProbeServer.test.ts](../tests/src/server/ProbeServer.test.ts)).
1127
+ Runtime cleanup that exceeds the coordinator deadline is abandoned (test: “serves a claim after
1128
+ abandoning a runtime cleanup blocked by a FIFO” in
1129
+ [Probe.test.ts](../tests/src/server/Probe.test.ts), skipped on Windows because its fixture requires
1130
+ a POSIX FIFO).
1131
+
1132
+ **L-3 survivor rule — unproved at the Probe boundary.** The implemented rule requires server restart
1133
+ after a lint child survives its kill: a rejected cleanup of an inserted record leaves that record
1134
+ in the pool, while rejected cleanup during a failed warm leaves a stage owned by Probe and blocks
1135
+ further lint creation. Both branches specify refusal until restart, even after the child later
1136
+ exits. Teardown re-reads each held survivor and reports it through its teardown result; it makes
1137
+ no second kill attempt because lint destruction retains its first settlement.
1138
+ Lint disposal uses the larger of the probe `deadline` and `LINT_TEARDOWN`, including protocol
1139
+ exchanges, transport grace, and kill escalation, so a short inspection deadline cannot abandon
1140
+ lint cleanup before its timeout reports a survivor. The silent-initialize fixture at
1141
+ `deadline: 500` reads the process table at each spawn and finds no overlapping Oxlint children
1142
+ (test: “spends silent initializes through the coordinator deadline” in
1143
+ [Probe.test.ts](../tests/src/server/Probe.test.ts)).
1144
+ No Probe test drives either surviving-child branch on this host; the U1–U3 run
1145
+ `eager-probe4-last.md` on LAPTOP-SBG38B5J, Windows, 2026-10-05 records that gap. The real pool/child
1146
+ instruments `eager-probe2-survivor.ts` and `eager-probe3-create-survivor.ts` establish the pool
1147
+ boundaries only. A fixture that survives the host kill and produces the transport's timeout is
1148
+ still required before a test can falsify the Probe rule.
1149
+
1150
+ The M-B recovery run on LAPTOP-SBG38B5J, Windows, 2026-10-05 measured a 3.013 s median from Oxlint
1151
+ kill to a successful replacement proof; see [Cost](#cost) for samples and the observation boundary.
1152
+ The M-D run on that host and date found no persistent referenced resource in
1153
+ `process.getActiveResourcesInfo()` after real Vitest specifications settled: runs 1, 2, and 3
1154
+ each returned `[]` at 1 s and 3 s while Vitest remained open. Its retained-worker control returned
1155
+ `["MessagePort"]`, and its false assertion failed. This is a measured resource census, not a test
1156
+ of every native thread or unreferenced worker; no persistent-handle claim beyond that census is
1157
+ proved. M-D CPU snapshots were 16%, 31%, and 9%; editor services and idle tool servers remained
1158
+ present.
1128
1159
 
1129
1160
  ## Cost
1130
1161
 
1131
- The following measurements decide whether a harness's timeout is right. They were taken on
1132
- 2026-09-06, over this repository as the target workspace, on
1133
- Linux 6.18.44 x64 with 4 processors, Node 22.22.2, TypeScript 6.0.3, Oxlint 1.81.0, and Vitest
1134
- 4.1.11, with other work running beside them. Read them as the shape of the cost on comparable
1135
- hardware rather than as a figure another host reproduces.
1136
-
1137
- | What | Measured |
1138
- | ------------------------------------------------------------------------------ | ---------------- |
1139
- | Boot: spawning `dist/bin/main.js` to the answered `initialize` | 497 ms to 561 ms |
1140
- | Admitted valid claim: spawning `dist/bin/main.js` to the answered `tools/call` | 16.2 s to 16.8 s |
1141
- | Warm `prove` over the flagship claim, client round trip | 4.2 s to 5.7 s |
1142
- | Type stage: construction to the `arm` event over this repository | 12.2 s |
1143
- | Type stage: the declared projects warmed together, cold | 4.6 s |
1144
- | Type stage: the declared projects warmed together again | 2.5 s |
1145
- | Type stage: warm inspection, the root project and selected scoped project | 2.0 s |
1146
- | Type stage: `tsc --showConfig` for a project | 90 ms to 115 ms |
1147
- | Type stage: warm inspection over the fixture target workspace | 0.9 s |
1148
-
1149
- The type stage runs the workspace's own `tsc` per selected project, so a target's own `check` script
1150
- is the shape of its cost. Warming builds each declared project's incremental state before the first
1151
- inspection answers, and every inspection awaits that warm, so `ProbeOptions.deadline` must clear it:
1152
- over this repository a budget under about 8 s expires arming rather than a claim. The default
1153
- `PROBE_DEADLINE` of 30,000 ms clears it with room for a contended host.
1154
-
1155
- The measured admitted valid claim is dominated by deferred arming, which runs its real controls
1156
- through the stage sequence before the call answers. The call also carries `prove`, so it lands about
1157
- a warm call after the `arm` event. A client whose timeout is tighter than arming reports a hang that
1158
- is a wait. Handshake and discovery requests require no workspace toolchain; only an admitted
1159
- `tools/call` waits on arming.
1160
-
1161
- `prove` runs the case through every stage and then the control through every stage, in sequence, so
1162
- one call pays the runtime stage's floor twice. One runtime inspection in every 64 also replaces
1163
- the resident Vitest runner and costs more than the other 63, so budget a client timeout against that
1164
- inspection rather than the common one.
1162
+ The M-A readings after the type-warm change put every measured `initialize` response inside
1163
+ Codex's 10 s default startup timeout. These readings used the `e8d4715` build on 2026-10-05 on
1164
+ LAPTOP-SBG38B5J, Windows 11 Home 10.0.26300 x64, Intel Core i7-10700 at 2.90 GHz with 16 logical
1165
+ processors, Node 24.21.0, TypeScript 6.0.3, Oxlint 1.86.0, and Vitest 4.1.11.
1166
+ The instrument was `tmp/onset/warm2-readings.ts`; the run records are
1167
+ `warm2-reading-<workspace>-<run>.json`, collected in `warm2-evidence.json` and
1168
+ `eager-probe-warm2-last.md`.
1169
+
1170
+ Each row reports spawn to the successful `initialize` response in run order.
1171
+
1172
+ | Workspace | Run 1 | Run 2 | Run 3 | CPU snapshots, runs 1/2/3 |
1173
+ | --------- | ------- | ------- | ------- | ------------------------- |
1174
+ | Veneer | 7.439 s | 7.107 s | 7.939 s | 11% / 6% / 8% |
1175
+ | Scaffold | 3.171 s | 4.051 s | 3.172 s | 23% / 9% / 28% |
1176
+ | Probe | 1.495 s | 1.744 s | 1.489 s | 7% / 4% / 3% |
1177
+
1178
+ The first type claim over veneer earned a receipt without retry in 3 runs of 3 on that host and
1179
+ date; these are the same `warm2-reading-veneer-<run>.json` runs.
1180
+
1181
+ | Run | Call to receipt | Spawn to receipt |
1182
+ | --- | --------------- | ---------------- |
1183
+ | 1 | 43.389 s | 50.829 s |
1184
+ | 2 | 45.011 s | 52.119 s |
1185
+ | 3 | 46.209 s | 54.148 s |
1186
+
1187
+ Free memory was 96.0–96.4 GB; no other unit was detected, while editor services and idle Probe
1188
+ servers remained present. Size a tool-call timeout for the first proof as well as initialization:
1189
+ `ProbeOptions.warm` defaults to 90,000 ms and covers type warming; `ProbeOptions.deadline`
1190
+ defaults to 30,000 ms and covers each active inspection. Initialization does not wait for type
1191
+ readiness or the boot controls; see [Lifecycle](#lifecycle) for the tests of that separation.
1192
+
1193
+ M-B measured a real Oxlint child-tree kill to the next successful `prove` call on the same host
1194
+ and date, using build `058a946`, the same tool versions, and
1195
+ `tmp/onset/recovery.ts` against its recovery workspace. The run records are `recovery-1.json`,
1196
+ `recovery-2.json`, and `recovery-3.json`, summarized in `tmp/onset/readings.md`.
1197
+
1198
+ | Reading | Run 1 | Run 2 | Run 3 | Median | CPU snapshots, runs 1/2/3 |
1199
+ | ------------------------ | ------- | ------- | ------- | ------- | ------------------------- |
1200
+ | Kill to successful proof | 2.695 s | 3.013 s | 3.187 s | 3.013 s | 54% / 83% / 74% |
1201
+
1202
+ Each run waited for the server's loss observation before calling `prove`, passed the clean case,
1203
+ and detected the false runtime assertion. The interval includes the kill command and proof work,
1204
+ so it is an upper bound on replacement readiness, not a direct language-server initialization
1205
+ timestamp. An immediate-after-kill call in the preliminary run raced loss observation and failed;
1206
+ the samples do not establish success for that race. Editor services, an idle scaffold Probe/Oxlint
1207
+ server, and the supervising Codex process remained present; no synthetic load was applied.
1165
1208
 
1166
1209
  ## Tests
1167
1210
 
@@ -1208,6 +1251,9 @@ inspection rather than the common one.
1208
1251
  carry the `prove` tool.
1209
1252
  - [`lsp.md`](lsp.md) — the dependency mirror for `@orkestrel/lsp`, whose client and stdio client
1210
1253
  transport carry the lint stage's conversation with the Oxlint language server.
1254
+ - [`pool.md`](pool.md) — the dependency mirror for `@orkestrel/pool`, which owns stage leases
1255
+ and replacement. The catalog refresh carries the published 0.0.15 guide; the release visit must
1256
+ refresh it to 0.0.16 alongside the dependency range.
1211
1257
  - [`tool.md`](tool.md) — the dependency mirror for `@orkestrel/tool`, whose registry holds it.
1212
1258
  - [`contract.md`](contract.md) — the dependency mirror for `@orkestrel/contract`, whose shapes
1213
1259
  compile both the published tool schema and the guards.