autor3search-typescript 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 (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/dist/benchproto/types.d.ts +32 -0
  4. package/dist/benchproto/types.js +28 -0
  5. package/dist/benchproto/types.js.map +1 -0
  6. package/dist/benchrun/child.d.ts +1 -0
  7. package/dist/benchrun/child.js +222 -0
  8. package/dist/benchrun/child.js.map +1 -0
  9. package/dist/benchrun/invoke.d.ts +61 -0
  10. package/dist/benchrun/invoke.js +152 -0
  11. package/dist/benchrun/invoke.js.map +1 -0
  12. package/dist/cli/cmd-baseline.d.ts +15 -0
  13. package/dist/cli/cmd-baseline.js +292 -0
  14. package/dist/cli/cmd-baseline.js.map +1 -0
  15. package/dist/cli/cmd-doctor.d.ts +11 -0
  16. package/dist/cli/cmd-doctor.js +26 -0
  17. package/dist/cli/cmd-doctor.js.map +1 -0
  18. package/dist/cli/cmd-eval.d.ts +30 -0
  19. package/dist/cli/cmd-eval.js +173 -0
  20. package/dist/cli/cmd-eval.js.map +1 -0
  21. package/dist/cli/cmd-init.d.ts +16 -0
  22. package/dist/cli/cmd-init.js +343 -0
  23. package/dist/cli/cmd-init.js.map +1 -0
  24. package/dist/cli/cmd-profile.d.ts +11 -0
  25. package/dist/cli/cmd-profile.js +140 -0
  26. package/dist/cli/cmd-profile.js.map +1 -0
  27. package/dist/cli/cmd-report.d.ts +15 -0
  28. package/dist/cli/cmd-report.js +70 -0
  29. package/dist/cli/cmd-report.js.map +1 -0
  30. package/dist/cli/cmd-status.d.ts +11 -0
  31. package/dist/cli/cmd-status.js +143 -0
  32. package/dist/cli/cmd-status.js.map +1 -0
  33. package/dist/cli/cmd-stop.d.ts +15 -0
  34. package/dist/cli/cmd-stop.js +143 -0
  35. package/dist/cli/cmd-stop.js.map +1 -0
  36. package/dist/cli/main.d.ts +31 -0
  37. package/dist/cli/main.js +133 -0
  38. package/dist/cli/main.js.map +1 -0
  39. package/dist/cli/runctx.d.ts +29 -0
  40. package/dist/cli/runctx.js +49 -0
  41. package/dist/cli/runctx.js.map +1 -0
  42. package/dist/cli/speedup.d.ts +14 -0
  43. package/dist/cli/speedup.js +25 -0
  44. package/dist/cli/speedup.js.map +1 -0
  45. package/dist/config/load.d.ts +6 -0
  46. package/dist/config/load.js +172 -0
  47. package/dist/config/load.js.map +1 -0
  48. package/dist/config/schema.d.ts +42 -0
  49. package/dist/config/schema.js +25 -0
  50. package/dist/config/schema.js.map +1 -0
  51. package/dist/discover/benchmarks.d.ts +26 -0
  52. package/dist/discover/benchmarks.js +88 -0
  53. package/dist/discover/benchmarks.js.map +1 -0
  54. package/dist/discover/files.d.ts +23 -0
  55. package/dist/discover/files.js +82 -0
  56. package/dist/discover/files.js.map +1 -0
  57. package/dist/doctor/doctor.d.ts +56 -0
  58. package/dist/doctor/doctor.js +255 -0
  59. package/dist/doctor/doctor.js.map +1 -0
  60. package/dist/freeze/freeze.d.ts +23 -0
  61. package/dist/freeze/freeze.js +107 -0
  62. package/dist/freeze/freeze.js.map +1 -0
  63. package/dist/freeze/manifest.d.ts +6 -0
  64. package/dist/freeze/manifest.js +9 -0
  65. package/dist/freeze/manifest.js.map +1 -0
  66. package/dist/freeze/safepath.d.ts +8 -0
  67. package/dist/freeze/safepath.js +22 -0
  68. package/dist/freeze/safepath.js.map +1 -0
  69. package/dist/gitx/git.d.ts +87 -0
  70. package/dist/gitx/git.js +235 -0
  71. package/dist/gitx/git.js.map +1 -0
  72. package/dist/measure/interleave.d.ts +32 -0
  73. package/dist/measure/interleave.js +42 -0
  74. package/dist/measure/interleave.js.map +1 -0
  75. package/dist/pipeline/eval.d.ts +67 -0
  76. package/dist/pipeline/eval.js +495 -0
  77. package/dist/pipeline/eval.js.map +1 -0
  78. package/dist/pm/detect.d.ts +17 -0
  79. package/dist/pm/detect.js +131 -0
  80. package/dist/pm/detect.js.map +1 -0
  81. package/dist/profile/profile.d.ts +53 -0
  82. package/dist/profile/profile.js +105 -0
  83. package/dist/profile/profile.js.map +1 -0
  84. package/dist/results/results.d.ts +65 -0
  85. package/dist/results/results.js +214 -0
  86. package/dist/results/results.js.map +1 -0
  87. package/dist/runner/exec.d.ts +69 -0
  88. package/dist/runner/exec.js +185 -0
  89. package/dist/runner/exec.js.map +1 -0
  90. package/dist/scope/scope.d.ts +20 -0
  91. package/dist/scope/scope.js +70 -0
  92. package/dist/scope/scope.js.map +1 -0
  93. package/dist/state/baseline.d.ts +35 -0
  94. package/dist/state/baseline.js +38 -0
  95. package/dist/state/baseline.js.map +1 -0
  96. package/dist/state/home.d.ts +9 -0
  97. package/dist/state/home.js +72 -0
  98. package/dist/state/home.js.map +1 -0
  99. package/dist/state/lock.d.ts +11 -0
  100. package/dist/state/lock.js +89 -0
  101. package/dist/state/lock.js.map +1 -0
  102. package/dist/state/runnaming.d.ts +27 -0
  103. package/dist/state/runnaming.js +47 -0
  104. package/dist/state/runnaming.js.map +1 -0
  105. package/dist/state/stop.d.ts +28 -0
  106. package/dist/state/stop.js +59 -0
  107. package/dist/state/stop.js.map +1 -0
  108. package/dist/stats/delta.d.ts +29 -0
  109. package/dist/stats/delta.js +41 -0
  110. package/dist/stats/delta.js.map +1 -0
  111. package/dist/stats/geomean.d.ts +4 -0
  112. package/dist/stats/geomean.js +21 -0
  113. package/dist/stats/geomean.js.map +1 -0
  114. package/dist/stats/mannwhitney.d.ts +27 -0
  115. package/dist/stats/mannwhitney.js +158 -0
  116. package/dist/stats/mannwhitney.js.map +1 -0
  117. package/dist/verdict/verdict.d.ts +46 -0
  118. package/dist/verdict/verdict.js +102 -0
  119. package/dist/verdict/verdict.js.map +1 -0
  120. package/dist/version.d.ts +2 -0
  121. package/dist/version.js +3 -0
  122. package/dist/version.js.map +1 -0
  123. package/package.json +35 -0
  124. package/templates/program.md +131 -0
@@ -0,0 +1,495 @@
1
+ /**
2
+ * The gate chain: everything one `eval` invocation does between "the agent
3
+ * made a commit" and "here is a verdict." Gate order is the design, not an
4
+ * implementation detail -- each gate must reject before anything past it
5
+ * runs, and in particular before any benchmark child is ever spawned.
6
+ *
7
+ * The eleven checks, in the order the brief specifies:
8
+ *
9
+ * 1. scope -- every changed file is in `config.scope` (or immutable
10
+ * -> unconditional reject regardless of scope)
11
+ * 2. config integrity -- `.autor3search/config.yaml` hashes to what baseline
12
+ * recorded; the agent cannot loosen its own rules
13
+ * 3. restore -- frozen tests/benchmarks are put back exactly as they
14
+ * were at baseline, whether or not they were "in scope"
15
+ * 4. unmanifested -- no new test/bench/runner-config file has appeared
16
+ * that baseline never froze ("add an easier benchmark")
17
+ * 5. typecheck -- `config.typecheckCommand`, skipped when empty
18
+ * 6. build -- `config.buildCommand`, skipped when empty
19
+ * 7. test -- `config.testCommand` (never empty)
20
+ * 8. worktree integrity -- the candidate's own working tree is clean and HEAD
21
+ * has moved past `baseline.measureCommit` (measuring
22
+ * an uncommitted edit and crediting it to a commit
23
+ * that never contained it would let one uncommitted
24
+ * optimization be scored forever); the pinned
25
+ * baseline worktree still exists and its lockfile
26
+ * is untouched -- if it is merely at the wrong
27
+ * commit (an interrupted step 11 advance), it is
28
+ * repointed and restored automatically here
29
+ * rather than refused
30
+ * 9. measure -- interleaved benchmark measurement, base vs. candidate
31
+ * 10. score -- `compareAll` + `decide`
32
+ * 11. on KEEP -- advance `measureCommit` (never `frozenCommit`)
33
+ *
34
+ * A `results.tsv` row is appended for every outcome this function actually
35
+ * returns from -- FAIL and CRASH included -- because "an eval ran and was
36
+ * refused" is itself part of the experiment record.
37
+ */
38
+ import { access, readFile } from 'node:fs/promises';
39
+ import path from 'node:path';
40
+ import { runChild } from '../benchrun/invoke.js';
41
+ import { loadConfig, parseDuration } from '../config/load.js';
42
+ import { freezableFiles } from '../discover/files.js';
43
+ import { findUnmanifested, restore } from '../freeze/freeze.js';
44
+ import { hashString } from '../freeze/manifest.js';
45
+ import { changedFiles, headCommit, repointWorktree } from '../gitx/git.js';
46
+ import { interleave } from '../measure/interleave.js';
47
+ import { appendRow, loadRows } from '../results/results.js';
48
+ import { checkScope } from '../scope/scope.js';
49
+ import { ALPHA, compareAll } from '../stats/delta.js';
50
+ import { readBaseline, writeBaseline } from '../state/baseline.js';
51
+ import { acquireEvalLock } from '../state/lock.js';
52
+ import { runDir } from '../state/home.js';
53
+ import { readStop } from '../state/stop.js';
54
+ import { FROZEN_DIRNAME, WORKTREE_DIRNAME } from '../state/runnaming.js';
55
+ import { ok, runShell, tail } from '../runner/exec.js';
56
+ import { decide } from '../verdict/verdict.js';
57
+ function messageOf(e) {
58
+ return e instanceof Error ? e.message : String(e);
59
+ }
60
+ async function exists(p) {
61
+ try {
62
+ await access(p);
63
+ return true;
64
+ }
65
+ catch {
66
+ return false;
67
+ }
68
+ }
69
+ /** `<file>:<fn>` -> `{ id, file, fn }`. The inverse of how `discoverBenchmarks` builds an id. */
70
+ function parseBenchmarkId(id) {
71
+ const idx = id.lastIndexOf(':');
72
+ if (idx === -1) {
73
+ throw new Error(`malformed benchmark id in baseline record: ${JSON.stringify(id)}`);
74
+ }
75
+ return { id, file: id.slice(0, idx), fn: id.slice(idx + 1) };
76
+ }
77
+ function noVerdict(status) {
78
+ // fail/crash never reach decide() -- nothing was scored, so score,
79
+ // correctedAlpha and regressions are all placeholders, not measurements.
80
+ // ALPHA (uncorrected) is used rather than a computed correction because no
81
+ // benchmark count is known at this point.
82
+ return { status, score: 1, regressions: [], correctedAlpha: ALPHA, warnings: [] };
83
+ }
84
+ /**
85
+ * Runs one experiment end to end: the full gate chain, and -- for whatever
86
+ * outcome it ends in -- one `results.tsv` row.
87
+ *
88
+ * The eval lock is held for the whole chain and released in a `finally`, so
89
+ * a crash mid-chain (this function throwing something genuinely
90
+ * unanticipated) never leaves a future eval permanently blocked.
91
+ */
92
+ export async function runEval(opts) {
93
+ const dir = runDir(opts.ctx.repoRoot, opts.tag);
94
+ const release = await acquireEvalLock(dir);
95
+ try {
96
+ return await evaluate(opts, dir);
97
+ }
98
+ finally {
99
+ await release();
100
+ }
101
+ }
102
+ async function evaluate(opts, dir) {
103
+ const log = opts.log ?? (() => { });
104
+ const worktreeDir = path.join(dir, WORKTREE_DIRNAME);
105
+ const frozenDir = path.join(dir, FROZEN_DIRNAME);
106
+ const stopRequested = (await readStop(dir)) !== null;
107
+ let candidateCommit = 'unknown';
108
+ try {
109
+ candidateCommit = await headCommit(opts.ctx.repoRoot);
110
+ }
111
+ catch {
112
+ // Best effort only -- a row still needs *some* commit string, and a
113
+ // git failure this early will also fail loudly below anyway.
114
+ }
115
+ const warnings = [];
116
+ const restoredFiles = [];
117
+ let baseline;
118
+ let config;
119
+ /**
120
+ * Builds the final outcome for any terminal state (fail, crash, discard or
121
+ * keep), appends its results.tsv row, and returns. The row is appended
122
+ * for every path through this function -- a rejected or crashed
123
+ * experiment is still part of the record.
124
+ */
125
+ async function finish(verdict, deltas, failedGate, message) {
126
+ const frozenCommit = baseline?.frozenCommit ?? 'unknown';
127
+ const measureCommit = baseline?.measureCommit ?? 'unknown';
128
+ const allWarnings = [...warnings, ...verdict.warnings];
129
+ const priorRows = await loadRows(opts.ctx.resultsPath);
130
+ const experiment = priorRows.length + 1;
131
+ const bestBenchDelta = deltas.length > 0 ? Math.min(...deltas.map((d) => d.pctChange)) : 0;
132
+ const pMin = deltas.length > 0 ? Math.min(...deltas.map((d) => d.p)) : 1;
133
+ // results.tsv's `reason` column only ever holds a `DiscardReason` or ''
134
+ // (loadRows rejects anything else) -- a FAIL/CRASH's free-text `message`
135
+ // cannot go there. Folding it into `description` instead is the only
136
+ // place in the fixed 7-column row that can carry it, so a FAIL/CRASH row
137
+ // is not left with an empty reason AND an empty description, the way a
138
+ // plain `opts.description` passthrough would leave it whenever the agent
139
+ // did not also pass `-desc`.
140
+ const isFailure = verdict.status === 'fail' || verdict.status === 'crash';
141
+ const description = isFailure
142
+ ? opts.description.length > 0
143
+ ? `${opts.description}: ${message}`
144
+ : message
145
+ : opts.description;
146
+ const row = {
147
+ commit: candidateCommit,
148
+ score: verdict.score,
149
+ bestBenchDelta,
150
+ pMin,
151
+ status: verdict.status,
152
+ reason: verdict.reason ?? '',
153
+ description,
154
+ };
155
+ await appendRow(opts.ctx.resultsPath, row);
156
+ return {
157
+ verdict,
158
+ deltas,
159
+ stopRequested,
160
+ ...(failedGate !== undefined ? { failedGate } : {}),
161
+ message,
162
+ warnings: allWarnings,
163
+ restoredFiles,
164
+ candidateCommit,
165
+ frozenCommit,
166
+ measureCommit,
167
+ experiment,
168
+ };
169
+ }
170
+ // --- Load baseline and config. Either failing is a harness/environment
171
+ // problem, not a policy decision about the agent's change (there is no
172
+ // change to have a policy about yet) -- CRASH, not FAIL. Reporting FAIL
173
+ // here would tell an unattended agent "your change was rejected, try
174
+ // another," which for "no baseline exists yet" is actively false and
175
+ // would loop it forever discarding otherwise-good work.
176
+ try {
177
+ baseline = await readBaseline(dir);
178
+ }
179
+ catch (e) {
180
+ return finish(noVerdict('crash'), [], 'setup', messageOf(e));
181
+ }
182
+ try {
183
+ config = await loadConfig(opts.ctx.configPath);
184
+ }
185
+ catch (e) {
186
+ return finish(noVerdict('crash'), [], 'setup', messageOf(e));
187
+ }
188
+ // --- Gate 1: scope. Immutable files reject unconditionally, regardless of
189
+ // `config.scope` -- see `checkScope`. A failure to even COMPUTE the
190
+ // changed-file list (git itself failing) is a harness problem, not a
191
+ // scope verdict about the change -- CRASH.
192
+ let changed;
193
+ try {
194
+ changed = await changedFiles(opts.ctx.repoRoot, baseline.frozenCommit);
195
+ }
196
+ catch (e) {
197
+ return finish(noVerdict('crash'), [], 'scope', `could not compute changed files: ${messageOf(e)}`);
198
+ }
199
+ const violations = checkScope(changed, config.scope);
200
+ if (violations.length > 0) {
201
+ const list = violations.map((v) => `${v.file} (${v.reason})`).join(', ');
202
+ return finish(noVerdict('fail'), [], 'scope', `scope violation: ${list}`);
203
+ }
204
+ // --- Gate 2: config integrity. The config that produced `baseline` must
205
+ // still be exactly what is on disk -- this is what stops the agent
206
+ // loosening its own rules mid-run.
207
+ let configText;
208
+ try {
209
+ configText = await readFile(opts.ctx.configPath, 'utf8');
210
+ }
211
+ catch (e) {
212
+ return finish(noVerdict('crash'), [], 'config-integrity', `could not read config: ${messageOf(e)}`);
213
+ }
214
+ if (hashString(configText) !== baseline.configHash) {
215
+ return finish(noVerdict('fail'), [], 'config-integrity', `${path.basename(opts.ctx.configPath)} has changed since baseline: the run configuration is frozen`);
216
+ }
217
+ // Captured HERE, before gate 3 ever touches the working tree: gate 3's own
218
+ // restore intentionally leaves tracked frozen files differing from HEAD
219
+ // (that is the whole point -- the measured content must be the frozen
220
+ // bytes, not whatever the agent committed), which makes the tree dirty on
221
+ // its own. Checking cleanliness after that would misreport every
222
+ // legitimate restore as an uncommitted-change violation. What gate 8 must
223
+ // police is whether the AGENT's own commit left the tree clean, which is
224
+ // exactly what this snapshot -- taken before any harness mutation -- answers.
225
+ //
226
+ // Deliberately `changedFiles(repoRoot, candidateCommit)` (HEAD), NOT
227
+ // `isClean` (`git status --porcelain`): `isClean` respects whatever
228
+ // `.gitignore` is on disk, including one the agent just wrote --
229
+ // `printf '*\n' > src/lib/.gitignore` hides an uncommitted, IN-SCOPE file
230
+ // from `git status` entirely, so `isClean` would report a clean tree
231
+ // while the working tree still differs from HEAD. That is the exact C1
232
+ // attack (measure an uncommitted edit, credit it to a commit that never
233
+ // contained it) surviving Priority 2's scope-gate fix by hiding from THIS
234
+ // gate instead: the scope gate happily lets an in-scope file through, and
235
+ // a gitignore-trusting clean check never sees it was never committed.
236
+ // `changedFiles` is already immune to this (see its own doc comment) --
237
+ // reusing it here means gate 8 inherits that immunity by construction,
238
+ // not by remembering to reimplement it a second time.
239
+ let treeWasCleanBeforeRestore = true;
240
+ try {
241
+ treeWasCleanBeforeRestore = (await changedFiles(opts.ctx.repoRoot, candidateCommit)).length === 0;
242
+ }
243
+ catch (e) {
244
+ return finish(noVerdict('crash'), [], 'worktree-integrity', `could not check working tree cleanliness: ${messageOf(e)}`);
245
+ }
246
+ // --- Gate 3: restore. Frozen files are put back exactly as they were,
247
+ // whether or not the agent's edit to them was "in scope" -- a test file
248
+ // edit is legal to make, but its content is not legal to keep. A failure
249
+ // here (e.g. the frozen snapshot itself is missing or unreadable) is a
250
+ // harness-state problem, not something the agent's change did -- CRASH.
251
+ try {
252
+ const changedByRestore = await restore(opts.ctx.repoRoot, frozenDir, baseline.manifest);
253
+ restoredFiles.push(...changedByRestore);
254
+ if (changedByRestore.length > 0) {
255
+ warnings.push(`restored ${changedByRestore.length} frozen file(s) modified since baseline: ${changedByRestore.join(', ')}`);
256
+ }
257
+ }
258
+ catch (e) {
259
+ return finish(noVerdict('crash'), [], 'restore', `could not restore frozen files: ${messageOf(e)}`);
260
+ }
261
+ // --- Gate 4: unmanifested. A new test/bench/runner-config file absent
262
+ // from the frozen manifest is rejected outright -- this is what closes
263
+ // "add an easier benchmark." A failure to even list the candidate files
264
+ // (a filesystem walk failing) is a harness problem -- CRASH.
265
+ let candidates;
266
+ try {
267
+ candidates = await freezableFiles(opts.ctx.repoRoot);
268
+ }
269
+ catch (e) {
270
+ return finish(noVerdict('crash'), [], 'unmanifested', `could not list freezable files: ${messageOf(e)}`);
271
+ }
272
+ const extra = findUnmanifested(candidates, baseline.manifest, config.unfreeze);
273
+ if (extra.length > 0) {
274
+ return finish(noVerdict('fail'), [], 'unmanifested', `new test/bench/runner-config file(s) not present at baseline: ${extra.join(', ')}`);
275
+ }
276
+ const timeoutMs = parseDuration(config.timeout);
277
+ // --- Gate 5: typecheck (skipped when empty).
278
+ if (config.typecheckCommand.trim() !== '') {
279
+ const r = await runShell(config.typecheckCommand, { cwd: opts.ctx.repoRoot, timeoutMs, log });
280
+ if (!ok(r)) {
281
+ return finish(noVerdict('fail'), [], 'typecheck', `typecheck command failed (${config.typecheckCommand}, exit ${r.exitCode}): ${tail(r.stderr || r.stdout, 40)}`);
282
+ }
283
+ }
284
+ // --- Gate 6: build (skipped when empty).
285
+ if (config.buildCommand.trim() !== '') {
286
+ const r = await runShell(config.buildCommand, { cwd: opts.ctx.repoRoot, timeoutMs, log });
287
+ if (!ok(r)) {
288
+ return finish(noVerdict('fail'), [], 'build', `build command failed (${config.buildCommand}, exit ${r.exitCode}): ${tail(r.stderr || r.stdout, 40)}`);
289
+ }
290
+ }
291
+ // --- Gate 7: test (never empty -- enforced by config validation).
292
+ {
293
+ const r = await runShell(config.testCommand, { cwd: opts.ctx.repoRoot, timeoutMs, log });
294
+ if (!ok(r)) {
295
+ return finish(noVerdict('fail'), [], 'test', `test command failed (${config.testCommand}, exit ${r.exitCode}): ${tail(r.stderr || r.stdout, 40)}`);
296
+ }
297
+ }
298
+ // --- Gate 8: worktree integrity.
299
+ //
300
+ // The candidate is measured from `opts.ctx.repoRoot` -- the live working
301
+ // tree, not a checkout of `candidateCommit` -- so nothing upstream of this
302
+ // gate stops an agent from applying a real optimization and never
303
+ // committing it: the working tree keeps the win, `candidateCommit` (and
304
+ // `measureCommit` after a KEEP) stays wherever HEAD already was, and the
305
+ // same uncommitted edit can be measured and credited indefinitely. Both
306
+ // checks below are FAIL, not CRASH: they describe the agent's own change
307
+ // (or lack of one), not a harness malfunction.
308
+ if (!treeWasCleanBeforeRestore) {
309
+ return finish(noVerdict('fail'), [], 'worktree-integrity', 'the working tree is not clean (uncommitted changes or untracked files): the candidate is ' +
310
+ 'measured from this working tree, and crediting an uncommitted edit to a commit that never ' +
311
+ 'contained it would let the same edit be measured and kept forever without ever landing. ' +
312
+ 'Commit your change, then run eval.');
313
+ }
314
+ if (candidateCommit === baseline.measureCommit) {
315
+ return finish(noVerdict('fail'), [], 'worktree-integrity', `HEAD (${candidateCommit}) is the same commit already recorded as measureCommit: there is ` +
316
+ 'nothing new to evaluate. Commit your change, then run eval.');
317
+ }
318
+ const worktreeResult = await checkWorktreeIntegrity(worktreeDir, frozenDir, baseline);
319
+ if (worktreeResult.warning !== null)
320
+ warnings.push(worktreeResult.warning);
321
+ if (worktreeResult.error !== null) {
322
+ return finish(noVerdict('fail'), [], 'worktree-integrity', worktreeResult.error);
323
+ }
324
+ // --- Gate 9: measure.
325
+ const benchmarks = baseline.benchmarks.map(parseBenchmarkId);
326
+ const benchtimeMs = parseDuration(config.benchtime);
327
+ const warmupMs = parseDuration(config.warmup);
328
+ const defaultMeasureOne = async (measureDir, b) => {
329
+ const result = await runChild({
330
+ cwd: measureDir,
331
+ benchFileAbs: path.join(measureDir, b.file),
332
+ fn: b.fn,
333
+ id: b.id,
334
+ benchtimeMs,
335
+ warmupMs,
336
+ timeoutMs,
337
+ nodeArgs: config.nodeArgs,
338
+ log,
339
+ });
340
+ if (!result.ok) {
341
+ throw new Error(`benchmark ${b.id} crashed in ${measureDir}: ${result.error}`);
342
+ }
343
+ return result.nsPerOp;
344
+ };
345
+ const measureOne = opts.measureOne ?? defaultMeasureOne;
346
+ let base;
347
+ let cand;
348
+ try {
349
+ ;
350
+ ({ base, cand } = await interleave({
351
+ rounds: config.count,
352
+ benchmarks,
353
+ baseDir: worktreeDir,
354
+ candDir: opts.ctx.repoRoot,
355
+ measureOne,
356
+ }));
357
+ }
358
+ catch (e) {
359
+ return finish(noVerdict('crash'), [], 'measure', messageOf(e));
360
+ }
361
+ // --- Gate 10: score.
362
+ let deltas;
363
+ try {
364
+ deltas = compareAll(base, cand);
365
+ }
366
+ catch (e) {
367
+ return finish(noVerdict('crash'), [], 'measure', messageOf(e));
368
+ }
369
+ const verdict = decide({
370
+ deltas,
371
+ minEffectPct: config.minEffectPct,
372
+ maxRegressPct: config.maxRegressPct,
373
+ rounds: config.count,
374
+ });
375
+ // --- Step 11: on KEEP, advance measureCommit. frozenCommit and the
376
+ // manifest are never touched -- moving the measurement point must never
377
+ // move the success criteria.
378
+ //
379
+ // `writeBaseline` runs FIRST, before either worktree operation --
380
+ // deliberately, so the sequence is recoverable rather than merely
381
+ // atomic-looking. A KEEP is a real, already-decided fact the moment
382
+ // `decide()` returns it; `measureCommit` recording that fact must not
383
+ // depend on two more subprocess calls (`repointWorktree`, `restore`)
384
+ // succeeding first. If either fails or the process dies here, the NEXT
385
+ // eval's gate 8 sees a worktree merely behind the now-already-recorded
386
+ // `measureCommit` -- a state it repairs itself (see
387
+ // `checkWorktreeIntegrity`) instead of demanding `baseline -force`, which
388
+ // would silently re-freeze from whatever is at HEAD by then.
389
+ //
390
+ // `repointWorktree` checks out the candidate commit's OWN tree into the
391
+ // base worktree -- tampered bench/test content included, since freezing
392
+ // only pins byte content in the manifest and the frozen snapshot, not
393
+ // what a later commit is allowed to contain. Gate 3 already restored
394
+ // `repoRoot`'s WORKING TREE for this experiment's own measurement, but
395
+ // that restoration never touches the commit itself, so without also
396
+ // restoring the worktree here, a candidate commit that both fixes the
397
+ // source AND slows the benchmark body would earn a legitimate KEEP now
398
+ // and then permanently inflate every future baseline measurement -- "add
399
+ // an easier benchmark," routed around gate 4 (which only checks the
400
+ // manifest, not commit content) via the one place gate 3's fix never
401
+ // reaches. Restoring the worktree's frozen files immediately after the
402
+ // repoint closes that gap: both sides always measure the SAME frozen
403
+ // benchmark bytes, regardless of what any given commit holds.
404
+ if (verdict.status === 'keep') {
405
+ try {
406
+ const advanced = { ...baseline, measureCommit: candidateCommit };
407
+ await writeBaseline(dir, advanced);
408
+ baseline = advanced;
409
+ await repointWorktree(worktreeDir, candidateCommit);
410
+ await restore(worktreeDir, frozenDir, baseline.manifest);
411
+ }
412
+ catch (e) {
413
+ // A genuine KEEP was already decided -- this is a harness failure
414
+ // advancing the measurement point, not a verdict about the change,
415
+ // so it must not read as FAIL ("your change was rejected"). CRASH is
416
+ // also why this experiment still gets a results.tsv row here, rather
417
+ // than an exception escaping with nothing recorded at all.
418
+ return finish(noVerdict('crash'), deltas, 'advance', messageOf(e));
419
+ }
420
+ }
421
+ return finish(verdict, deltas, undefined, `${verdict.status}${verdict.reason ? `: ${verdict.reason}` : ''}`);
422
+ }
423
+ /**
424
+ * The `"baseline -force" to recreate it` advice this function used to give
425
+ * for EVERY problem was actively harmful for the one case (below) that is
426
+ * cheaply self-healing: `-force` re-derives the freeze manifest from
427
+ * whatever is at HEAD when it runs, silently adopting the agent's own
428
+ * commits as the new correctness contract -- exactly the "grade its own
429
+ * homework" failure mode this whole tool exists to prevent. It is still the
430
+ * right advice for the two cases that genuinely have no cheaper fix
431
+ * (the worktree is gone; the lockfile itself changed), so those messages
432
+ * now say so explicitly rather than presenting `-force` as a free action.
433
+ */
434
+ async function checkWorktreeIntegrity(worktreeDir, frozenDir, baseline) {
435
+ if (!(await exists(worktreeDir))) {
436
+ return {
437
+ error: `no worktree at ${worktreeDir}: it is missing and must be re-created. "baseline -force" ` +
438
+ 'will do that, but it also re-freezes tests/benchmarks from the CURRENT HEAD, adopting ' +
439
+ 'whatever is committed there as the new frozen contract -- review what has changed since ' +
440
+ 'the original baseline before running it.',
441
+ warning: null,
442
+ };
443
+ }
444
+ let atCommit;
445
+ try {
446
+ atCommit = await headCommit(worktreeDir);
447
+ }
448
+ catch (e) {
449
+ return { error: `could not read the worktree's HEAD: ${messageOf(e)}`, warning: null };
450
+ }
451
+ let warning = null;
452
+ if (atCommit !== baseline.measureCommit) {
453
+ // Merely being at the wrong commit is cheaply recoverable WITHOUT
454
+ // re-freezing anything: `baseline.measureCommit` is already the
455
+ // authoritative value by the time this ever runs (step 11 writes it
456
+ // before touching the worktree at all), so this is exactly the state a
457
+ // crash between that write and the repoint/restore that follows it
458
+ // would leave behind -- self-healed here instead of demanded of the
459
+ // human via `-force`. `frozenCommit` and `baseline.manifest` are never
460
+ // touched by this: only the MEASUREMENT point is being caught up.
461
+ try {
462
+ await repointWorktree(worktreeDir, baseline.measureCommit);
463
+ await restore(worktreeDir, frozenDir, baseline.manifest);
464
+ }
465
+ catch (e) {
466
+ return {
467
+ error: `the worktree is at ${atCommit} but baseline.measureCommit is ${baseline.measureCommit}, ` +
468
+ `and repairing it automatically failed: ${messageOf(e)}`,
469
+ warning: null,
470
+ };
471
+ }
472
+ warning =
473
+ `the baseline worktree was at ${atCommit}, behind baseline.measureCommit ` +
474
+ `(${baseline.measureCommit}) -- likely left over from an interrupted measureCommit advance -- ` +
475
+ 'and was repointed and restored automatically';
476
+ }
477
+ let lockfileText;
478
+ try {
479
+ lockfileText = await readFile(path.join(worktreeDir, baseline.lockfileName), 'utf8');
480
+ }
481
+ catch (e) {
482
+ return { error: `could not read the worktree's lockfile (${baseline.lockfileName}): ${messageOf(e)}`, warning };
483
+ }
484
+ if (hashString(lockfileText) !== baseline.lockfileHash) {
485
+ return {
486
+ error: `the worktree's lockfile (${baseline.lockfileName}) no longer matches baseline.lockfileHash: ` +
487
+ 'dependencies must be reinstalled. "baseline -force" will do that, but it also re-freezes ' +
488
+ 'tests/benchmarks from the CURRENT HEAD, adopting whatever is committed there as the new ' +
489
+ 'frozen contract -- review what has changed since the original baseline before running it.',
490
+ warning,
491
+ };
492
+ }
493
+ return { error: null, warning };
494
+ }
495
+ //# sourceMappingURL=eval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eval.js","sourceRoot":"","sources":["../../src/pipeline/eval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
2
+ export interface Detected {
3
+ pm: PackageManager;
4
+ lockfile: string;
5
+ /** A frozen-lockfile install: reproducible, and it fails rather than drifting. */
6
+ installCommand: string;
7
+ }
8
+ /**
9
+ * Refuses a workspace root.
10
+ *
11
+ * v0.1 supports single-package repositories only. Saying so plainly is much
12
+ * better than half-working: in a workspace the scope gate, the freeze set and
13
+ * the install layout all mean something different, and getting any of them
14
+ * subtly wrong weakens a guarantee the user believes they have.
15
+ */
16
+ export declare function assertSinglePackage(pkgJsonText: string, root: string): Promise<void>;
17
+ export declare function detect(root: string): Promise<Detected>;