@ac-bench/lib 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +28 -0
  2. package/README.md +41 -0
  3. package/dist/child/channel.d.ts +28 -0
  4. package/dist/child/channel.d.ts.map +1 -0
  5. package/dist/child/channel.js +68 -0
  6. package/dist/child/channel.js.map +1 -0
  7. package/dist/child/event-sink.d.ts +13 -0
  8. package/dist/child/event-sink.d.ts.map +1 -0
  9. package/dist/child/event-sink.js +34 -0
  10. package/dist/child/event-sink.js.map +1 -0
  11. package/dist/child/main.d.ts +2 -0
  12. package/dist/child/main.d.ts.map +1 -0
  13. package/dist/child/main.js +157 -0
  14. package/dist/child/main.js.map +1 -0
  15. package/dist/cooldown.d.ts +51 -0
  16. package/dist/cooldown.d.ts.map +1 -0
  17. package/dist/cooldown.js +118 -0
  18. package/dist/cooldown.js.map +1 -0
  19. package/dist/current-environment.d.ts +8 -0
  20. package/dist/current-environment.d.ts.map +1 -0
  21. package/dist/current-environment.js +10 -0
  22. package/dist/current-environment.js.map +1 -0
  23. package/dist/environment-monitor.d.ts +59 -0
  24. package/dist/environment-monitor.d.ts.map +1 -0
  25. package/dist/environment-monitor.js +163 -0
  26. package/dist/environment-monitor.js.map +1 -0
  27. package/dist/escalation.d.ts +24 -0
  28. package/dist/escalation.d.ts.map +1 -0
  29. package/dist/escalation.js +73 -0
  30. package/dist/escalation.js.map +1 -0
  31. package/dist/fork-runner.d.ts +52 -0
  32. package/dist/fork-runner.d.ts.map +1 -0
  33. package/dist/fork-runner.js +348 -0
  34. package/dist/fork-runner.js.map +1 -0
  35. package/dist/index.d.ts +12 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +12 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/ipc-report-receiver.d.ts +47 -0
  40. package/dist/ipc-report-receiver.d.ts.map +1 -0
  41. package/dist/ipc-report-receiver.js +149 -0
  42. package/dist/ipc-report-receiver.js.map +1 -0
  43. package/dist/isolated-runner.d.ts +67 -0
  44. package/dist/isolated-runner.d.ts.map +1 -0
  45. package/dist/isolated-runner.js +537 -0
  46. package/dist/isolated-runner.js.map +1 -0
  47. package/dist/measure-diagnostic-proxy.d.ts +15 -0
  48. package/dist/measure-diagnostic-proxy.d.ts.map +1 -0
  49. package/dist/measure-diagnostic-proxy.js +50 -0
  50. package/dist/measure-diagnostic-proxy.js.map +1 -0
  51. package/dist/measurement-plan.d.ts +50 -0
  52. package/dist/measurement-plan.d.ts.map +1 -0
  53. package/dist/measurement-plan.js +2 -0
  54. package/dist/measurement-plan.js.map +1 -0
  55. package/dist/reporter-context.d.ts +19 -0
  56. package/dist/reporter-context.d.ts.map +1 -0
  57. package/dist/reporter-context.js +21 -0
  58. package/dist/reporter-context.js.map +1 -0
  59. package/dist/run-bench.d.ts +61 -0
  60. package/dist/run-bench.d.ts.map +1 -0
  61. package/dist/run-bench.js +151 -0
  62. package/dist/run-bench.js.map +1 -0
  63. package/package.json +79 -0
package/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2024-2026 Anthony Champagne and contributors
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md ADDED
@@ -0,0 +1,41 @@
1
+ # @ac-bench/lib
2
+
3
+ The programmatic benchmark API: child-process fork orchestration and protocol,
4
+ the environment monitor and cooldown gate, and the escalation decision.
5
+
6
+ `runBench` is the way in. It takes fully-resolved parameters only — the files to
7
+ run, the measure plugins to register, the reporter plugins to report through, a
8
+ `MeasurementPlan`, a seed, a signal. No config file, no CLI flags, no profile
9
+ name, no string parsing, and no mixed list something downstream has to sort out.
10
+
11
+ ```ts
12
+ import { runBench } from "@ac-bench/lib";
13
+ import { walkPaths } from "@ac-kit/node";
14
+ import durationPlugin from "@ac-bench/measure-duration/plugin";
15
+ import tableReporter from "@ac-bench/reporter-table";
16
+
17
+ await runBench({
18
+ files: await Array.fromAsync(
19
+ walkPaths({ include: "**/*.bench.ts", exclude: "**/node_modules" }),
20
+ (walked) => walked.path,
21
+ ),
22
+ measures: [durationPlugin],
23
+ reporters: [tableReporter],
24
+ output: "bench-results",
25
+ plan: {
26
+ replicates: 2,
27
+ maxReplicates: 5,
28
+ isolatedRuns: 0,
29
+ rounds: 16,
30
+ cooldown: false,
31
+ retryOnInstability: 0,
32
+ },
33
+ orderSeed: 1,
34
+ signal: AbortSignal.timeout(600_000),
35
+ });
36
+ ```
37
+
38
+ Turning an intent into those parameters — what `--mode rigorous` stands for,
39
+ which reporters a config file selected, where output files go — is
40
+ `@ac-bench/cli`'s job, not this package's. A caller that wants to run benchmarks
41
+ from its own code depends on this package directly and decides those itself.
@@ -0,0 +1,28 @@
1
+ import { ChildMessage, DiscoveredCondition } from "@ac-bench/core/runner";
2
+ import { ReportScopeStatus } from "@ac-kit/app-report";
3
+ export type ChildMessagePoster = (message: ChildMessage) => Promise<void> | void;
4
+ /**
5
+ * The child's end of the protocol: assigns the sequence numbers the parent
6
+ * asserts on, and stops sending once it has run too far ahead of the last
7
+ * acknowledgement.
8
+ */
9
+ export declare class ChildChannel {
10
+ private readonly post;
11
+ private readonly ackWindow;
12
+ private seq;
13
+ private lastAck;
14
+ private credit;
15
+ private released;
16
+ constructor(post: ChildMessagePoster, ackWindow?: number);
17
+ acknowledge(seq: number): void;
18
+ /** Unblocks anything waiting on credit, so an abort cannot hang the child. */
19
+ release(): void;
20
+ hello(): Promise<void> | void;
21
+ conditions(conditions: DiscoveredCondition[]): Promise<void> | void;
22
+ event(event: unknown): Promise<void>;
23
+ dropped(count: number, kinds: string[]): Promise<void> | void;
24
+ done(status: ReportScopeStatus): Promise<void> | void;
25
+ fatal(error: unknown): Promise<void> | void;
26
+ private waitForCredit;
27
+ }
28
+ //# sourceMappingURL=channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../src/child/channel.ts"],"names":[],"mappings":"AAAA,OAAO,EAEN,YAAY,EACZ,mBAAmB,EACnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAKvD,MAAM,MAAM,kBAAkB,GAAG,CAChC,OAAO,EAAE,YAAY,KACjB,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAE1B;;;;GAIG;AACH,qBAAa,YAAY;IAOvB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAP3B,OAAO,CAAC,GAAG,CAAK;IAChB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,MAAM,CAA2C;IACzD,OAAO,CAAC,QAAQ,CAAS;IAEzB,YACkB,IAAI,EAAE,kBAAkB,EACxB,SAAS,GAAE,MAAmB,EAC5C;IAEJ,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAO7B;IAED,8EAA8E;IAC9E,OAAO,IAAI,IAAI,CAId;IAED,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAM5B;IAED,UAAU,CAAC,UAAU,EAAE,mBAAmB,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAElE;IAEK,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAKzC;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAG5D;IAED,IAAI,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAGpD;IAED,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAG1C;IAED,OAAO,CAAC,aAAa;CASrB"}
@@ -0,0 +1,68 @@
1
+ import { BENCH_PROTOCOL_VERSION, } from "@ac-bench/core/runner";
2
+ /** How far the child may run ahead of the parent's acknowledgements. */
3
+ const ACK_WINDOW = 256;
4
+ /**
5
+ * The child's end of the protocol: assigns the sequence numbers the parent
6
+ * asserts on, and stops sending once it has run too far ahead of the last
7
+ * acknowledgement.
8
+ */
9
+ export class ChildChannel {
10
+ post;
11
+ ackWindow;
12
+ seq = 0;
13
+ lastAck = 0;
14
+ credit = null;
15
+ released = false;
16
+ constructor(post, ackWindow = ACK_WINDOW) {
17
+ this.post = post;
18
+ this.ackWindow = ackWindow;
19
+ }
20
+ acknowledge(seq) {
21
+ this.lastAck = Math.max(this.lastAck, seq);
22
+ if (this.credit !== null && this.seq - this.lastAck <= this.ackWindow) {
23
+ this.credit.resolve();
24
+ this.credit = null;
25
+ }
26
+ }
27
+ /** Unblocks anything waiting on credit, so an abort cannot hang the child. */
28
+ release() {
29
+ this.released = true;
30
+ this.credit?.resolve();
31
+ this.credit = null;
32
+ }
33
+ hello() {
34
+ return this.post({
35
+ t: "hello",
36
+ version: BENCH_PROTOCOL_VERSION,
37
+ pid: process.pid,
38
+ });
39
+ }
40
+ conditions(conditions) {
41
+ return this.post({ t: "conditions", conditions });
42
+ }
43
+ async event(event) {
44
+ await this.waitForCredit();
45
+ this.seq += 1;
46
+ await this.post({ t: "event", seq: this.seq, event });
47
+ }
48
+ dropped(count, kinds) {
49
+ this.seq += 1;
50
+ return this.post({ t: "dropped", seq: this.seq, count, kinds });
51
+ }
52
+ done(status) {
53
+ this.seq += 1;
54
+ return this.post({ t: "done", seq: this.seq, status });
55
+ }
56
+ fatal(error) {
57
+ this.seq += 1;
58
+ return this.post({ t: "fatal", seq: this.seq, error });
59
+ }
60
+ waitForCredit() {
61
+ if (this.released || this.seq - this.lastAck <= this.ackWindow) {
62
+ return Promise.resolve();
63
+ }
64
+ this.credit ??= Promise.withResolvers();
65
+ return this.credit.promise;
66
+ }
67
+ }
68
+ //# sourceMappingURL=channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channel.js","sourceRoot":"","sources":["../../src/child/channel.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import { MeasureData } from "@ac-bench/core/runner";
2
+ import { ISink } from "@ac-kit/app-report";
3
+ import { ChildChannel } from "./channel.js";
4
+ /**
5
+ * The child's sink chain: coalesce the only high-frequency events, then queue
6
+ * with backpressure, then serialize onto the IPC channel.
7
+ *
8
+ * Coalescing first is what makes `overflow: "block"` safe — blocking can then
9
+ * only ever be triggered by a result, which is rare and emitted outside the
10
+ * timed region.
11
+ */
12
+ export declare function createChildEventSink(channel: ChildChannel): ISink<MeasureData>;
13
+ //# sourceMappingURL=event-sink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-sink.d.ts","sourceRoot":"","sources":["../../src/child/event-sink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAGN,KAAK,EAGL,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAI5C;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CACnC,OAAO,EAAE,YAAY,GACnB,KAAK,CAAC,WAAW,CAAC,CAuBpB"}
@@ -0,0 +1,34 @@
1
+ import { AsyncQueueSink, createCoalesceProxy, serializeReportEvent, } from "@ac-kit/app-report";
2
+ const MAX_QUEUE = 1024;
3
+ /**
4
+ * The child's sink chain: coalesce the only high-frequency events, then queue
5
+ * with backpressure, then serialize onto the IPC channel.
6
+ *
7
+ * Coalescing first is what makes `overflow: "block"` safe — blocking can then
8
+ * only ever be triggered by a result, which is rare and emitted outside the
9
+ * timed region.
10
+ */
11
+ export function createChildEventSink(channel) {
12
+ const transport = {
13
+ write: (event) => channel.event(serializeReportEvent(event, { mode: "structured-clone" })),
14
+ flush: () => { },
15
+ close: () => { },
16
+ };
17
+ const queue = new AsyncQueueSink(transport, {
18
+ maxQueue: MAX_QUEUE,
19
+ overflow: "block",
20
+ onOverflow: (dropped) => {
21
+ void channel.dropped(dropped.length, dropped.map((event) => event.kind));
22
+ },
23
+ onSinkError: (error) => {
24
+ void channel.fatal(error);
25
+ },
26
+ });
27
+ return createCoalesceProxy(queue, { keyOf: coalesceKey_ });
28
+ }
29
+ function coalesceKey_(event) {
30
+ return event.kind === "scope-progress" || event.kind === "scope-heartbeat"
31
+ ? `${event.kind}:${event.scopeId}`
32
+ : null;
33
+ }
34
+ //# sourceMappingURL=event-sink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-sink.js","sourceRoot":"","sources":["../../src/child/event-sink.ts"],"names":[],"mappings":"AACA,OAAO,EACN,cAAc,EACd,mBAAmB,EAGnB,oBAAoB,GACpB,MAAM,oBAAoB,CAAC;AAI5B,MAAM,SAAS,GAAG,IAAI,CAAC;AAEvB;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CACnC,OAAqB;IAErB,MAAM,SAAS,GAAuB;QACrC,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAChB,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC,CAAC;QACzE,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;QACf,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;KACf,CAAC;IAEF,MAAM,KAAK,GAAG,IAAI,cAAc,CAAC,SAAS,EAAE;QAC3C,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,OAAO;QACjB,UAAU,EAAE,CAAC,OAAO,EAAE,EAAE;YACvB,KAAK,OAAO,CAAC,OAAO,CACnB,OAAO,CAAC,MAAM,EACd,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAClC,CAAC;QACH,CAAC;QACD,WAAW,EAAE,CAAC,KAAK,EAAE,EAAE;YACtB,KAAK,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;KACD,CAAC,CAAC;IAEH,OAAO,mBAAmB,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,YAAY,CAAC,KAA+B;IACpD,OAAO,KAAK,CAAC,IAAI,KAAK,gBAAgB,IAAI,KAAK,CAAC,IAAI,KAAK,iBAAiB;QACzE,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,EAAE;QAClC,CAAC,CAAC,IAAI,CAAC;AACT,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/child/main.ts"],"names":[],"mappings":""}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Bench child entry point, forked as `@ac-bench/lib/child`.
3
+ *
4
+ * Imports `@ac-bench/core/runner` only — never `/plugin`: plugins are the
5
+ * reporting side's concern and must never be loaded into a measuring process.
6
+ */
7
+ import { pathToFileURL } from "node:url";
8
+ import { BENCH_PROTOCOL_VERSION, describeConditions, drainConditions, parseParentMessage, ProtocolVersionMismatchError, resolveForkUnit, runForkUnit, } from "@ac-bench/core/runner";
9
+ import { ChildChannel } from "./channel.js";
10
+ import { createChildEventSink } from "./event-sink.js";
11
+ const send = process.send?.bind(process);
12
+ if (send === undefined) {
13
+ throw new Error("@ac-bench/lib/child must be forked with an IPC channel");
14
+ }
15
+ const channel = new ChildChannel((message) => new Promise((resolve, reject) => {
16
+ send(message, (error) => {
17
+ if (error) {
18
+ reject(error);
19
+ return;
20
+ }
21
+ resolve();
22
+ });
23
+ }));
24
+ const controller = new AbortController();
25
+ const commands = [];
26
+ let draining = false;
27
+ process.on("message", (raw) => {
28
+ let message;
29
+ try {
30
+ message = parseParentMessage(raw);
31
+ }
32
+ catch (error) {
33
+ void channel.fatal(error);
34
+ return;
35
+ }
36
+ switch (message.t) {
37
+ case "ack":
38
+ channel.acknowledge(message.seq);
39
+ return;
40
+ case "abort":
41
+ controller.abort(new Error(message.reason));
42
+ channel.release();
43
+ return;
44
+ default:
45
+ commands.push(message);
46
+ void drainCommands();
47
+ }
48
+ });
49
+ async function drainCommands() {
50
+ if (draining) {
51
+ return;
52
+ }
53
+ draining = true;
54
+ try {
55
+ for (let message = commands.shift(); message !== undefined; message = commands.shift()) {
56
+ await handleCommand(message);
57
+ }
58
+ }
59
+ finally {
60
+ draining = false;
61
+ }
62
+ }
63
+ async function handleCommand(message) {
64
+ switch (message.t) {
65
+ case "hello":
66
+ await handleHello(message);
67
+ return;
68
+ case "list":
69
+ await handleList(message.file);
70
+ return;
71
+ case "start":
72
+ await handleStart(message);
73
+ return;
74
+ default:
75
+ return;
76
+ }
77
+ }
78
+ async function handleHello(message) {
79
+ if (message.version !== BENCH_PROTOCOL_VERSION) {
80
+ await channel.fatal(new ProtocolVersionMismatchError(BENCH_PROTOCOL_VERSION, message.version));
81
+ finish(1);
82
+ return;
83
+ }
84
+ // The only channel a bench file loaded later can read these from.
85
+ process.env.AC_BENCH_RUN_ID = message.runId;
86
+ process.env.AC_BENCH_ARTIFACT_CACHE_ROOT = message.artifactCacheRoot;
87
+ for (const setupFile of message.setupFiles ?? []) {
88
+ try {
89
+ await import(pathToFileURL(setupFile).href);
90
+ }
91
+ catch (error) {
92
+ await channel.fatal(error);
93
+ finish(1);
94
+ return;
95
+ }
96
+ }
97
+ await channel.hello();
98
+ }
99
+ async function handleList(file) {
100
+ try {
101
+ await import(pathToFileURL(file).href);
102
+ }
103
+ catch (error) {
104
+ await channel.fatal(error);
105
+ finish(1);
106
+ return;
107
+ }
108
+ await channel.conditions(describeConditions(drainConditions()));
109
+ finish(0);
110
+ }
111
+ async function handleStart(message) {
112
+ try {
113
+ await import(pathToFileURL(message.file).href);
114
+ }
115
+ catch (error) {
116
+ await channel.fatal(error);
117
+ finish(1);
118
+ return;
119
+ }
120
+ const sink = createChildEventSink(channel);
121
+ try {
122
+ const unit = resolveForkUnit(drainConditions(), message.conditionPath, message.conditionTitles);
123
+ const outcome = await runForkUnit(unit, controller.signal, {
124
+ sink,
125
+ file: message.file,
126
+ parentScopeId: message.parentScopeId,
127
+ orderSeed: message.orderSeed,
128
+ arm: message.arm,
129
+ replicate: message.replicate,
130
+ rounds: message.rounds,
131
+ ...(message.caseFilter ? { caseFilter: message.caseFilter } : {}),
132
+ // The measure has no other channel for a run-level switch.
133
+ measureOptions: {
134
+ ...message.measureOptions,
135
+ emitSamples: message.emitSamples,
136
+ },
137
+ });
138
+ await sink.close();
139
+ await channel.done(outcome.status);
140
+ finish(0);
141
+ }
142
+ catch (error) {
143
+ await sink.close();
144
+ await channel.fatal(error);
145
+ finish(1);
146
+ }
147
+ }
148
+ /**
149
+ * Closes the channel and lets the process end on its own, so a benchmark that
150
+ * leaked a handle shows up as the parent's heartbeat timeout instead of being
151
+ * hidden by a hard exit.
152
+ */
153
+ function finish(code) {
154
+ process.exitCode = code;
155
+ process.disconnect?.();
156
+ }
157
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/child/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import { ReportDiagnostic } from "@ac-kit/app-report";
2
+ import { EnvironmentProbe } from "./environment-monitor.js";
3
+ /** What the machine looked like before any measurement ran. */
4
+ export type EnvironmentBaseline = {
5
+ loadRatio: number;
6
+ frequencyRatio: number | null;
7
+ temperatureC: number | null;
8
+ };
9
+ export type CooldownOptions = {
10
+ /** Per gate. `0` disables cooling entirely. */
11
+ readonly maxCooldownMs: number;
12
+ /** Across the whole run. Exhausting it proceeds rather than hangs. */
13
+ readonly totalBudgetMs: number;
14
+ /** Degrees above baseline that still count as settled. Default 2. */
15
+ readonly temperatureToleranceC?: number;
16
+ /**
17
+ * Fraction of the baseline frequency that still counts as settled. Default
18
+ * 0.98.
19
+ */
20
+ readonly frequencyTolerance?: number;
21
+ /** Default 500. */
22
+ readonly intervalMs?: number;
23
+ };
24
+ export type CooldownResult = {
25
+ /** Time spent waiting. Not measurement time, so it is reported separately. */
26
+ cooldownMs: number;
27
+ diagnostics: ReportDiagnostic[];
28
+ };
29
+ /** Samples over `windowMs` are the reference every later gate compares against. */
30
+ export declare function captureEnvironmentBaseline(probe: EnvironmentProbe, windowMs: number, intervalMs?: number, signal?: AbortSignal): Promise<EnvironmentBaseline>;
31
+ /**
32
+ * Waits between fork units until the machine stops recovering, never inside a
33
+ * timed case.
34
+ *
35
+ * Waiting is preventive where retrying is corrective: it avoids paying for a
36
+ * bad measurement rather than noticing one afterwards. The stopping rule is the
37
+ * _derivative_, not the baseline — ambient conditions may simply have changed,
38
+ * so waiting for the exact starting temperature would never terminate.
39
+ */
40
+ export declare class CooldownGate {
41
+ private readonly probe;
42
+ private readonly baseline;
43
+ private readonly options;
44
+ private spentMs;
45
+ constructor(probe: EnvironmentProbe, baseline: EnvironmentBaseline, options: CooldownOptions);
46
+ /** Time already spent cooling across every gate so far. */
47
+ get totalCooldownMs(): number;
48
+ settle(signal?: AbortSignal): Promise<CooldownResult>;
49
+ private isSettled_;
50
+ }
51
+ //# sourceMappingURL=cooldown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cooldown.d.ts","sourceRoot":"","sources":["../src/cooldown.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,OAAO,EAAE,gBAAgB,EAAqB,MAAM,0BAA0B,CAAC;AAE/E,+DAA+D;AAC/D,MAAM,MAAM,mBAAmB,GAAG;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC7B,+CAA+C;IAC/C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IACxC;;;OAGG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,mBAAmB;IACnB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC5B,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,gBAAgB,EAAE,CAAC;CAChC,CAAC;AAEF,mFAAmF;AACnF,wBAAsB,0BAA0B,CAC/C,KAAK,EAAE,gBAAgB,EACvB,QAAQ,EAAE,MAAM,EAChB,UAAU,SAAM,EAChB,MAAM,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,mBAAmB,CAAC,CA2B9B;AAED;;;;;;;;GAQG;AACH,qBAAa,YAAY;IAIvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IALzB,OAAO,CAAC,OAAO,CAAK;IAEpB,YACkB,KAAK,EAAE,gBAAgB,EACvB,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,eAAe,EACtC;IAEJ,2DAA2D;IAC3D,IAAI,eAAe,IAAI,MAAM,CAE5B;IAEK,MAAM,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,cAAc,CAAC,CA0D1D;IAED,OAAO,CAAC,UAAU;CAgBlB"}
@@ -0,0 +1,118 @@
1
+ import { setTimeout as delay } from "node:timers/promises";
2
+ import { mannKendallTrend } from "@ac-kit/math-stats";
3
+ /** Samples over `windowMs` are the reference every later gate compares against. */
4
+ export async function captureEnvironmentBaseline(probe, windowMs, intervalMs = 500, signal) {
5
+ const samples = [];
6
+ const until = Date.now() + windowMs;
7
+ while (Date.now() < until && signal?.aborted !== true) {
8
+ samples.push(await probe());
9
+ try {
10
+ await delay(intervalMs, undefined, signal ? { signal } : undefined);
11
+ }
12
+ catch {
13
+ break;
14
+ }
15
+ }
16
+ return {
17
+ loadRatio: median_(samples.map((sample) => sample.loadRatio)) ?? 0,
18
+ frequencyRatio: median_(samples
19
+ .map((sample) => sample.frequencyRatio)
20
+ .filter((value) => value !== null)),
21
+ temperatureC: median_(samples
22
+ .map((sample) => sample.temperatureC)
23
+ .filter((value) => value !== null)),
24
+ };
25
+ }
26
+ /**
27
+ * Waits between fork units until the machine stops recovering, never inside a
28
+ * timed case.
29
+ *
30
+ * Waiting is preventive where retrying is corrective: it avoids paying for a
31
+ * bad measurement rather than noticing one afterwards. The stopping rule is the
32
+ * _derivative_, not the baseline — ambient conditions may simply have changed,
33
+ * so waiting for the exact starting temperature would never terminate.
34
+ */
35
+ export class CooldownGate {
36
+ probe;
37
+ baseline;
38
+ options;
39
+ spentMs = 0;
40
+ constructor(probe, baseline, options) {
41
+ this.probe = probe;
42
+ this.baseline = baseline;
43
+ this.options = options;
44
+ }
45
+ /** Time already spent cooling across every gate so far. */
46
+ get totalCooldownMs() {
47
+ return this.spentMs;
48
+ }
49
+ async settle(signal) {
50
+ const budget = Math.min(this.options.maxCooldownMs, Math.max(0, this.options.totalBudgetMs - this.spentMs));
51
+ if (budget === 0) {
52
+ return { cooldownMs: 0, diagnostics: [] };
53
+ }
54
+ const intervalMs = this.options.intervalMs ?? 500;
55
+ const startedAt = Date.now();
56
+ const recent = [];
57
+ let settled = false;
58
+ while (Date.now() - startedAt < budget && signal?.aborted !== true) {
59
+ const sample = await this.probe();
60
+ if (this.isSettled_(sample)) {
61
+ settled = true;
62
+ break;
63
+ }
64
+ recent.push(sample.temperatureC ?? sample.loadRatio);
65
+ // Stop once the recovery has flattened: the trend, not the level.
66
+ if (recent.length >= 5) {
67
+ const { normalised } = mannKendallTrend(recent.slice(-5));
68
+ if (normalised > -1.96) {
69
+ settled = true;
70
+ break;
71
+ }
72
+ }
73
+ try {
74
+ await delay(intervalMs, undefined, signal ? { signal } : undefined);
75
+ }
76
+ catch {
77
+ break;
78
+ }
79
+ }
80
+ const cooldownMs = Date.now() - startedAt;
81
+ this.spentMs += cooldownMs;
82
+ return {
83
+ cooldownMs,
84
+ diagnostics: settled
85
+ ? []
86
+ : [
87
+ {
88
+ severity: "info",
89
+ code: "cooldown-budget-exhausted",
90
+ message: `waited ${cooldownMs}ms without the machine settling; measuring anyway`,
91
+ attributes: { cooldownMs },
92
+ },
93
+ ],
94
+ };
95
+ }
96
+ isSettled_(sample) {
97
+ const temperatureTolerance = this.options.temperatureToleranceC ?? 2;
98
+ const frequencyTolerance = this.options.frequencyTolerance ?? 0.98;
99
+ const hot = this.baseline.temperatureC !== null &&
100
+ sample.temperatureC !== null &&
101
+ sample.temperatureC > this.baseline.temperatureC + temperatureTolerance;
102
+ const throttled = this.baseline.frequencyRatio !== null &&
103
+ sample.frequencyRatio !== null &&
104
+ sample.frequencyRatio < this.baseline.frequencyRatio * frequencyTolerance;
105
+ return !hot && !throttled;
106
+ }
107
+ }
108
+ function median_(values) {
109
+ if (values.length === 0) {
110
+ return null;
111
+ }
112
+ const sorted = [...values].toSorted((a, b) => a - b);
113
+ const middle = Math.floor(sorted.length / 2);
114
+ return sorted.length % 2 === 0
115
+ ? ((sorted[middle - 1] ?? 0) + (sorted[middle] ?? 0)) / 2
116
+ : (sorted[middle] ?? 0);
117
+ }
118
+ //# sourceMappingURL=cooldown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cooldown.js","sourceRoot":"","sources":["../src/cooldown.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /** Environment captured at run time, carried into live tables and exports. */
2
+ export declare function currentEnvironment(): {
3
+ runtime: string;
4
+ platform: string;
5
+ arch: string;
6
+ timestamp: string;
7
+ };
8
+ //# sourceMappingURL=current-environment.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-environment.d.ts","sourceRoot":"","sources":["../src/current-environment.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,wBAAgB,kBAAkB,IAAI;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CAClB,CAOA"}
@@ -0,0 +1,10 @@
1
+ /** Environment captured at run time, carried into live tables and exports. */
2
+ export function currentEnvironment() {
3
+ return {
4
+ runtime: `node ${process.version}`,
5
+ platform: process.platform,
6
+ arch: process.arch,
7
+ timestamp: new Date().toISOString(),
8
+ };
9
+ }
10
+ //# sourceMappingURL=current-environment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"current-environment.js","sourceRoot":"","sources":["../src/current-environment.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,MAAM,UAAU,kBAAkB;IAMjC,OAAO;QACN,OAAO,EAAE,QAAQ,OAAO,CAAC,OAAO,EAAE;QAClC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACnC,CAAC;AACH,CAAC"}