@orkestrel/scaffold 0.0.92 → 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.
- package/dist/bin/main.js +6 -6
- package/dist/bin/main.js.map +1 -1
- package/dist/host/agents/skills/enterprise-bootstrap/references/bootstrap-reference.md +4 -2
- package/dist/host/agents/skills/enterprise-bootstrap/references/responsive-layout.md +1 -1
- package/dist/host/agents/skills/enterprise-bootstrap/references/utilities.md +3 -3
- package/dist/host/agents/skills/orkestrel-journey/references/statechart.md +1 -1
- package/dist/host/claude/agents/orkestrel.md +5 -5
- package/dist/host/claude/rules/styles.md +5 -0
- package/dist/host/dotfiles/oxlintrc.json +168 -0
- package/dist/host/guides/browser.md +94 -70
- package/dist/host/guides/mcp.md +15 -15
- package/dist/host/guides/pool.md +73 -48
- package/dist/host/guides/probe.md +262 -216
- package/dist/host/guides/router.md +1 -1
- package/dist/host/guides/worker.md +43 -7
- package/dist/host/manifest.json +15 -15
- package/dist/host/tests/config.test.ts +46 -3
- package/dist/src/core/index.cjs +6 -6
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.js +6 -6
- package/dist/src/core/index.js.map +1 -1
- package/package.json +6 -6
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
# Probe
|
|
2
2
|
|
|
3
|
-
>
|
|
4
|
-
>
|
|
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? }`
|
|
51
|
-
| `ProbeInterface` | interface | `{ emitter, toolchain } plus prove, destroy`
|
|
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`
|
|
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`
|
|
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
|
-
| `
|
|
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
|
-
| `
|
|
289
|
-
| `
|
|
290
|
-
| `
|
|
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
|
|
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`
|
|
476
|
-
|
|
477
|
-
|
|
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
|
|
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
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
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.
|
|
630
|
-
|
|
631
|
-
|
|
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.
|
|
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
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
- **
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
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
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
|
1142
|
-
|
|
|
1143
|
-
|
|
|
1144
|
-
|
|
|
1145
|
-
|
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
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.
|