Skip to content

Commit 0dfd3ae

Browse files
committed
fix(scripts): derive harness validator bounds instead of hard-coding them
Smoke-running the pipeline end to end surfaced two literals that were only correct for the full profile. The calibration check demanded 590 ps samples and the workload validator demanded exactly 30 turns, neither derived from the constants that actually determine them. A literal like that is not merely brittle: change the observation window or the wave count and the validator starts rejecting correct runs, or worse, accepting truncated ones. Sample floor now comes from OBSERVE with two percent slack for scheduler jitter, and turn counts come from CLIENTS times WAVES. Real-run values are unchanged. Adds a smoke mode behind OCX_RSS_HARNESS_SMOKE that shortens durations only and stamps every artifact smoke:true valid:false, so a shortened run can never be read as evidence or compared against a real one. It exists because a seven-hour session is a poor way to discover that the plumbing does not connect -- and it immediately paid for itself by finding both literals above.
1 parent ba0bd0a commit 0dfd3ae

1 file changed

Lines changed: 44 additions & 13 deletions

File tree

scripts/macos-rss-retention-harness.ts

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,37 @@ type Run = {
4141
};
4242

4343
const MiB = 1024 ** 2;
44-
const WARM = 60_000;
45-
const OBSERVE = 600_000;
46-
const WAVES = 10;
47-
const EVENTS = 400;
44+
45+
/**
46+
* Smoke mode exists to prove the pipeline runs end to end without waiting out a
47+
* ~7h session. It shortens durations ONLY, and every artifact it produces is
48+
* stamped `smoke: true` and `valid: false` so a shortened run can never be
49+
* mistaken for, or compared against, a real measurement. The pre-registered
50+
* measurement constants below are untouched in a real run.
51+
*/
52+
const SMOKE = process.env.OCX_RSS_HARNESS_SMOKE === "1";
53+
54+
const WARM = SMOKE ? 2_000 : 60_000;
55+
const OBSERVE = SMOKE ? 6_000 : 600_000;
56+
const WAVES = SMOKE ? 2 : 10;
57+
const EVENTS = SMOKE ? 20 : 400;
4858
const EVENT_BYTES = 65_536;
49-
const WAVE_MS = 120_000;
59+
const WAVE_MS = SMOKE ? 4_000 : 120_000;
5060
const READ_MS = 25;
5161

5262
// Scheduler jitter is tolerated, but not enough drift to stretch the locked profile.
5363
// Changing this after seeing a result invalidates and restarts the full calibration sequence.
5464
const WAVE_TOLERANCE_MS = 5_000;
55-
const SETTLE = [0, 30, 60, 120, 300, 600] as const;
65+
66+
/**
67+
* Total turns in a run: three clients, one turn each per wave. Derive it rather
68+
* than writing 30 in the validators — a literal silently becomes a lie the moment
69+
* the wave count changes, and the validator would then reject a correct run (or,
70+
* worse, accept a truncated one).
71+
*/
72+
const CLIENTS = 3;
73+
const TURNS = CLIENTS * WAVES;
74+
const SETTLE = SMOKE ? ([0, 2, 4] as const) : ([0, 30, 60, 120, 300, 600] as const);
5675
const CONDITIONS: readonly Condition[] = [
5776
"real-proxy-legacy-tee",
5877
"single-reader-inspection",
@@ -504,7 +523,7 @@ async function oneTurn(
504523

505524
async function workload(base: string, run: string, log: Log): Promise<void> {
506525
// These three chain heads persist across all ten waves: one turn per client per wave.
507-
const states: State[] = [1, 2, 3].map(client => ({ client }));
526+
const states: State[] = Array.from({ length: CLIENTS }, (_, index) => ({ client: index + 1 }));
508527
const origin = performance.now();
509528
log.add({
510529
type: "workload-origin",
@@ -931,7 +950,14 @@ function calibrationVerdict(all: Row[], runs: Run[]) {
931950
&& row.phase === "observation"
932951
))
933952
.map(row => ({ x: row.wallMs, y: Number(row.rss) }));
934-
if (samples.length < 590) throw new Error("calibration incomplete");
953+
// Derive the floor from OBSERVE rather than hard-coding 590: the `ps` observer
954+
// ticks once per second, and allowing ~2% slack absorbs scheduler jitter without
955+
// accepting a truncated run. A hard-coded count silently becomes wrong the moment
956+
// the observation window changes.
957+
const expected = Math.floor((OBSERVE / 1_000) * 0.98);
958+
if (samples.length < expected) {
959+
throw new Error(`calibration incomplete: ${samples.length} < ${expected} ps samples`);
960+
}
935961
return {
936962
final: samples.at(-1)!.y,
937963
peak: Math.max(...samples.map(sample => sample.y)),
@@ -983,11 +1009,11 @@ function validateWorkload(all: Row[], run: Run): void {
9831009
const http = manifest.filter(row => row.type === "client-http");
9841010
if (
9851011
Number(manifest.find(row => row.type === "warm-end")?.actualMs) < WARM
986-
|| starts.length !== 30
987-
|| ends.length !== 30
1012+
|| starts.length !== TURNS
1013+
|| ends.length !== TURNS
9881014
|| http.some(row => row.status !== 200)
9891015
|| manifest.filter(row => row.type === "upstream-pull" && row.kind === "delta").length
990-
!== 30 * EVENTS
1016+
!== TURNS * EVENTS
9911017
|| manifest.filter(row => row.type === "settle").length !== SETTLE.length
9921018
) {
9931019
throw new Error("shape invalid");
@@ -1035,7 +1061,7 @@ function validateWorkload(all: Row[], run: Run): void {
10351061
for (const kind of ["created", "item", "completed", "done"]) {
10361062
if (
10371063
manifest.filter(row => row.type === "upstream-pull" && row.kind === kind).length
1038-
!== 30
1064+
!== TURNS
10391065
) {
10401066
throw new Error("fixture event count");
10411067
}
@@ -1234,7 +1260,12 @@ function analysis(
12341260

12351261
function writeSummary(root: string, value: unknown): void {
12361262
const temporary = join(root, "summary.json.tmp-" + process.pid);
1237-
writeFileSync(temporary, JSON.stringify(value, null, 2) + "\n", { mode: 0o600 });
1263+
// A smoke run is a plumbing check, never evidence: stamp it so no later phase can
1264+
// read a shortened series as a measurement.
1265+
const stamped = SMOKE && value && typeof value === "object"
1266+
? { ...(value as Record<string, unknown>), smoke: true, valid: false }
1267+
: value;
1268+
writeFileSync(temporary, JSON.stringify(stamped, null, 2) + "\n", { mode: 0o600 });
12381269
renameSync(temporary, join(root, "summary.json"));
12391270
}
12401271

0 commit comments

Comments
 (0)