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,87 @@
1
+ export declare function repoRoot(dir: string): Promise<string>;
2
+ export declare function headCommit(root: string): Promise<string>;
3
+ export declare function shortSha(sha: string): string;
4
+ /**
5
+ * A tree is clean only if it has no modifications AND no untracked files.
6
+ * `--porcelain` lists untracked entries too (respecting .gitignore, so an
7
+ * untracked file inside an ignored directory does not count): a baseline
8
+ * pinned against what is on disk rather than what is in git is not
9
+ * reproducible, so a stray untracked file must make the tree dirty.
10
+ */
11
+ export declare function isClean(root: string): Promise<boolean>;
12
+ /**
13
+ * Repo-relative POSIX paths touched since `sinceRef`: everything the scope
14
+ * gate (and gate 8's clean-tree check) must treat as "changed," whether or
15
+ * not it was ever `git add`-ed, committed, or hidden from git's own normal
16
+ * views.
17
+ *
18
+ * This is the union of several views, because no one of them alone answers
19
+ * the question:
20
+ *
21
+ * - `diff --name-only <ref>` (ref vs. the *working tree*, not `HEAD`) --
22
+ * catches committed changes, uncommitted edits to tracked files, and
23
+ * files the agent deleted. Blind to a brand-new file never `git add`-ed,
24
+ * and to a tracked file flagged assume-unchanged/skip-worktree (below).
25
+ * - `ls-files --others --exclude-standard` -- untracked files, respecting
26
+ * `.gitignore`. Safe to trust here ONLY because `untrustedGitignores`
27
+ * (above) is checked first and separately: this function does NOT
28
+ * re-derive an ignore-immune untracked-file list of its own (an earlier
29
+ * version did, and it made the tool refuse on a repository's own
30
+ * ordinary `dist/`, `.env` or `*.log` entries -- the opposite failure).
31
+ * - `untrustedGitignores` -- an untracked or modified `.gitignore` is
32
+ * itself reported as a change, so `--exclude-standard`'s output for
33
+ * THIS invocation is never trusted while the rules it depends on are in
34
+ * an unverified state.
35
+ * - `listSymlinks` minus git's tracked set -- an untracked symlink is
36
+ * invisible to the ordinary file walk `walkRepo` uses (by design; see
37
+ * its own doc comment) and to `--exclude-standard` only if it happens to
38
+ * be gitignored, but it is exactly what Node and `tsc` resolve at
39
+ * measure time. Reported unconditionally, gitignored or not: a symlink
40
+ * is content indirection, and hiding a NEW one behind an ignore rule is
41
+ * not a legitimate use this gate needs to accommodate the way `dist/`
42
+ * or `.env` are.
43
+ * - `tamperedIndexEntries` -- see its own doc comment: a complete bypass
44
+ * of `diff` for a tracked file, detected directly.
45
+ *
46
+ * `diff` and every `ls-files` call use `-z`: a path with a space or
47
+ * embedded newline would otherwise be able to split into extra entries
48
+ * under the default newline-separated, C-quoted output, and a scope gate
49
+ * that mis-parses a filename is one an agent can bypass by naming a file
50
+ * carefully. This is a security property, not formatting.
51
+ */
52
+ export declare function changedFiles(root: string, sinceRef: string): Promise<string[]>;
53
+ export declare function currentBranch(root: string): Promise<string>;
54
+ export declare function branchExists(root: string, name: string): Promise<boolean>;
55
+ export declare function createBranch(root: string, name: string): Promise<void>;
56
+ /**
57
+ * Deletes a local branch, force (`-D`) rather than `-d`: this exists for the
58
+ * `baseline` unwind path, where a run that fails partway must delete the run
59
+ * branch it just created so a retry does not die on "branch already
60
+ * exists." A branch created moments ago for a fresh run is never something
61
+ * the merge-safety check behind `-d` needs to protect. Fails (does not
62
+ * silently succeed) if the branch does not exist, since a caller unwinding
63
+ * a partial failure needs to know its assumption about what it created was
64
+ * wrong.
65
+ */
66
+ export declare function deleteBranch(root: string, name: string): Promise<void>;
67
+ export declare function addWorktree(root: string, dir: string, commit: string): Promise<void>;
68
+ export declare function removeWorktree(root: string, dir: string): Promise<void>;
69
+ /**
70
+ * Moves an existing detached worktree to another commit.
71
+ *
72
+ * This is how the MEASUREMENT commit advances after a KEEP. It must never be
73
+ * used on the frozen-test snapshot: moving the measurement point must not move
74
+ * the success criteria.
75
+ *
76
+ * `--force` (in addition to `-q --detach`): this is also how `eval` gate 8
77
+ * self-heals a worktree merely left at the wrong commit (e.g. by a crash
78
+ * between recording an advanced `measureCommit` and repointing the
79
+ * worktree to match it -- see `pipeline/eval.ts`). That worktree's own
80
+ * frozen files were restored on a previous run and may still differ from
81
+ * the target commit's tree; a plain `checkout --detach` would refuse to
82
+ * discard those local changes, which the self-heal must not depend on a
83
+ * human clearing first. Safe to discard unconditionally: the worktree
84
+ * holds nothing this tool did not itself put there, and `restore` runs
85
+ * again immediately after this to reassert the frozen bytes regardless.
86
+ */
87
+ export declare function repointWorktree(worktreeDir: string, commit: string): Promise<void>;
@@ -0,0 +1,235 @@
1
+ import path from 'node:path';
2
+ import { listSymlinks, walkRepo } from '../discover/files.js';
3
+ import { ok, run } from '../runner/exec.js';
4
+ const TIMEOUT_MS = 60_000;
5
+ async function git(cwd, args) {
6
+ const r = await run('git', args, { cwd, timeoutMs: TIMEOUT_MS });
7
+ if (!ok(r)) {
8
+ throw new Error(`git ${args.join(' ')} failed in ${cwd} (exit ${r.exitCode}): ${r.stderr.trim()}`);
9
+ }
10
+ return r.stdout.trim();
11
+ }
12
+ /**
13
+ * Like `git`, but returns stdout untrimmed.
14
+ *
15
+ * `.trim()` is safe for the scalar callers (`repoRoot`, `headCommit`,
16
+ * `currentBranch`, ...), which only ever get one shell-wrapped value back.
17
+ * It is NOT safe for a `-z` NUL-separated list: `String.prototype.trim`
18
+ * strips leading ASCII whitespace, and a leading space (0x20) sorts before
19
+ * any letter in git's output — so a repo-relative path that legitimately
20
+ * starts with a space (e.g. a directory named `" src"`) would have that
21
+ * space clipped from the *first* entry every time, silently turning
22
+ * `" src/evil.ts"` into `"src/evil.ts"`. Under a scope gate that allowlists
23
+ * `src/**`, that reported path is in scope while the real file is not —
24
+ * the same "name a file to bypass the gate" class `-z` itself exists to
25
+ * prevent, reintroduced one layer up by an over-eager trim. `-z` and NUL
26
+ * splitting still take care of the trailing edge: `trim()` does not treat
27
+ * `\0` as whitespace, so it never eats the sentinel a caller's split relies
28
+ * on.
29
+ */
30
+ async function gitRaw(cwd, args) {
31
+ const r = await run('git', args, { cwd, timeoutMs: TIMEOUT_MS });
32
+ if (!ok(r)) {
33
+ throw new Error(`git ${args.join(' ')} failed in ${cwd} (exit ${r.exitCode}): ${r.stderr.trim()}`);
34
+ }
35
+ return r.stdout;
36
+ }
37
+ export async function repoRoot(dir) {
38
+ return git(dir, ['rev-parse', '--show-toplevel']);
39
+ }
40
+ export async function headCommit(root) {
41
+ return git(root, ['rev-parse', 'HEAD']);
42
+ }
43
+ export function shortSha(sha) {
44
+ return sha.slice(0, 7);
45
+ }
46
+ /**
47
+ * A tree is clean only if it has no modifications AND no untracked files.
48
+ * `--porcelain` lists untracked entries too (respecting .gitignore, so an
49
+ * untracked file inside an ignored directory does not count): a baseline
50
+ * pinned against what is on disk rather than what is in git is not
51
+ * reproducible, so a stray untracked file must make the tree dirty.
52
+ */
53
+ export async function isClean(root) {
54
+ return (await git(root, ['status', '--porcelain'])) === '';
55
+ }
56
+ /**
57
+ * Splits a NUL-separated `-z` git listing into repo-relative POSIX paths.
58
+ * `out` must come from `gitRaw`, not `git` — see `gitRaw`'s doc comment for
59
+ * why trimming first would silently corrupt a path with a leading space.
60
+ */
61
+ function parseNulList(out) {
62
+ return out
63
+ .split('\0')
64
+ .filter((s) => s !== '')
65
+ .map((s) => s.split(path.sep).join('/'));
66
+ }
67
+ /**
68
+ * Repo-relative POSIX paths of every tracked file whose git-index "assume
69
+ * unchanged" or "skip-worktree" bit is set.
70
+ *
71
+ * Either bit tells git to stop comparing that path against the working
72
+ * tree at all -- `git diff <ref>` (which `changedFiles` otherwise relies on
73
+ * to see committed-vs-disk differences) reports NO difference for such a
74
+ * file even after its on-disk content is completely rewritten, because git
75
+ * trusts the index entry without ever re-reading the file. That is a
76
+ * complete, one-command bypass of both the scope gate and gate 8's
77
+ * clean-tree check (`git update-index --assume-unchanged <path>`, or
78
+ * `--skip-worktree`) -- strictly easier than the `.gitignore`-hiding attack
79
+ * this function otherwise closes -- so it is detected directly rather than
80
+ * trusted through a diff mechanism that cannot see it.
81
+ *
82
+ * `git ls-files -v` tags every tracked path with one status letter; the
83
+ * letter is lowercased when the assume-unchanged bit is set, and a
84
+ * skip-worktree path is tagged `S` regardless. Either is reported here.
85
+ */
86
+ async function tamperedIndexEntries(root) {
87
+ const out = await gitRaw(root, ['ls-files', '-v', '-z']);
88
+ const flagged = [];
89
+ for (const entry of out.split('\0')) {
90
+ if (entry === '')
91
+ continue;
92
+ const tag = entry.charAt(0);
93
+ const rel = entry.slice(2); // "<tag><SP><path>"
94
+ if (tag === 'S' || (tag >= 'a' && tag <= 'z')) {
95
+ flagged.push(rel.split(path.sep).join('/'));
96
+ }
97
+ }
98
+ return flagged;
99
+ }
100
+ /**
101
+ * Repo-relative POSIX paths of every `.gitignore` (a plain file -- a
102
+ * SYMLINKED `.gitignore` is caught separately, as an untracked symlink)
103
+ * that is either untracked or differs from `sinceRef`: one whose rules
104
+ * cannot be trusted for this comparison.
105
+ *
106
+ * `--exclude-standard` (used below to list ordinary untracked files) is
107
+ * only safe to rely on once this comes back empty. An agent-authored
108
+ * `.gitignore` an agent just wrote to hide a directory of untracked source
109
+ * (`printf '*\n' > lib/.gitignore`) is exactly what this catches: rather
110
+ * than distrust `--exclude-standard`'s output in general (which would also
111
+ * make it blind to the REPOSITORY's own, entirely legitimate ignore rules
112
+ * -- `dist/`, `.env`, `.DS_Store`, `*.log`, ...), an untracked or modified
113
+ * `.gitignore` is treated as a violation in its own right and refused
114
+ * before `--exclude-standard`'s output is ever trusted.
115
+ */
116
+ function untrustedGitignores(allFiles, tracked, diffed) {
117
+ return allFiles.filter((f) => path.posix.basename(f) === '.gitignore' && (!tracked.has(f) || diffed.has(f)));
118
+ }
119
+ /**
120
+ * Repo-relative POSIX paths touched since `sinceRef`: everything the scope
121
+ * gate (and gate 8's clean-tree check) must treat as "changed," whether or
122
+ * not it was ever `git add`-ed, committed, or hidden from git's own normal
123
+ * views.
124
+ *
125
+ * This is the union of several views, because no one of them alone answers
126
+ * the question:
127
+ *
128
+ * - `diff --name-only <ref>` (ref vs. the *working tree*, not `HEAD`) --
129
+ * catches committed changes, uncommitted edits to tracked files, and
130
+ * files the agent deleted. Blind to a brand-new file never `git add`-ed,
131
+ * and to a tracked file flagged assume-unchanged/skip-worktree (below).
132
+ * - `ls-files --others --exclude-standard` -- untracked files, respecting
133
+ * `.gitignore`. Safe to trust here ONLY because `untrustedGitignores`
134
+ * (above) is checked first and separately: this function does NOT
135
+ * re-derive an ignore-immune untracked-file list of its own (an earlier
136
+ * version did, and it made the tool refuse on a repository's own
137
+ * ordinary `dist/`, `.env` or `*.log` entries -- the opposite failure).
138
+ * - `untrustedGitignores` -- an untracked or modified `.gitignore` is
139
+ * itself reported as a change, so `--exclude-standard`'s output for
140
+ * THIS invocation is never trusted while the rules it depends on are in
141
+ * an unverified state.
142
+ * - `listSymlinks` minus git's tracked set -- an untracked symlink is
143
+ * invisible to the ordinary file walk `walkRepo` uses (by design; see
144
+ * its own doc comment) and to `--exclude-standard` only if it happens to
145
+ * be gitignored, but it is exactly what Node and `tsc` resolve at
146
+ * measure time. Reported unconditionally, gitignored or not: a symlink
147
+ * is content indirection, and hiding a NEW one behind an ignore rule is
148
+ * not a legitimate use this gate needs to accommodate the way `dist/`
149
+ * or `.env` are.
150
+ * - `tamperedIndexEntries` -- see its own doc comment: a complete bypass
151
+ * of `diff` for a tracked file, detected directly.
152
+ *
153
+ * `diff` and every `ls-files` call use `-z`: a path with a space or
154
+ * embedded newline would otherwise be able to split into extra entries
155
+ * under the default newline-separated, C-quoted output, and a scope gate
156
+ * that mis-parses a filename is one an agent can bypass by naming a file
157
+ * carefully. This is a security property, not formatting.
158
+ */
159
+ export async function changedFiles(root, sinceRef) {
160
+ const [diffOut, trackedOut, untrackedOut, allFiles, symlinks, tamperedTags] = await Promise.all([
161
+ gitRaw(root, ['diff', '--name-only', '-z', sinceRef]),
162
+ gitRaw(root, ['ls-files', '-z']),
163
+ gitRaw(root, ['ls-files', '--others', '--exclude-standard', '-z']),
164
+ walkRepo(root),
165
+ listSymlinks(root),
166
+ tamperedIndexEntries(root),
167
+ ]);
168
+ const tracked = new Set(parseNulList(trackedOut));
169
+ const diffed = new Set(parseNulList(diffOut));
170
+ const gitignoreViolations = untrustedGitignores(allFiles, tracked, diffed);
171
+ const untrackedSymlinks = symlinks.filter((f) => !tracked.has(f));
172
+ return [
173
+ ...new Set([
174
+ ...diffed,
175
+ ...parseNulList(untrackedOut),
176
+ ...gitignoreViolations,
177
+ ...untrackedSymlinks,
178
+ ...tamperedTags,
179
+ ]),
180
+ ].sort();
181
+ }
182
+ export async function currentBranch(root) {
183
+ return git(root, ['rev-parse', '--abbrev-ref', 'HEAD']);
184
+ }
185
+ export async function branchExists(root, name) {
186
+ const r = await run('git', ['show-ref', '--verify', '--quiet', `refs/heads/${name}`], {
187
+ cwd: root,
188
+ timeoutMs: TIMEOUT_MS,
189
+ });
190
+ return r.exitCode === 0;
191
+ }
192
+ export async function createBranch(root, name) {
193
+ await git(root, ['checkout', '-q', '-b', name]);
194
+ }
195
+ /**
196
+ * Deletes a local branch, force (`-D`) rather than `-d`: this exists for the
197
+ * `baseline` unwind path, where a run that fails partway must delete the run
198
+ * branch it just created so a retry does not die on "branch already
199
+ * exists." A branch created moments ago for a fresh run is never something
200
+ * the merge-safety check behind `-d` needs to protect. Fails (does not
201
+ * silently succeed) if the branch does not exist, since a caller unwinding
202
+ * a partial failure needs to know its assumption about what it created was
203
+ * wrong.
204
+ */
205
+ export async function deleteBranch(root, name) {
206
+ await git(root, ['branch', '-D', name]);
207
+ }
208
+ export async function addWorktree(root, dir, commit) {
209
+ await git(root, ['worktree', 'add', '--detach', '-q', dir, commit]);
210
+ }
211
+ export async function removeWorktree(root, dir) {
212
+ await git(root, ['worktree', 'remove', '--force', dir]);
213
+ }
214
+ /**
215
+ * Moves an existing detached worktree to another commit.
216
+ *
217
+ * This is how the MEASUREMENT commit advances after a KEEP. It must never be
218
+ * used on the frozen-test snapshot: moving the measurement point must not move
219
+ * the success criteria.
220
+ *
221
+ * `--force` (in addition to `-q --detach`): this is also how `eval` gate 8
222
+ * self-heals a worktree merely left at the wrong commit (e.g. by a crash
223
+ * between recording an advanced `measureCommit` and repointing the
224
+ * worktree to match it -- see `pipeline/eval.ts`). That worktree's own
225
+ * frozen files were restored on a previous run and may still differ from
226
+ * the target commit's tree; a plain `checkout --detach` would refuse to
227
+ * discard those local changes, which the self-heal must not depend on a
228
+ * human clearing first. Safe to discard unconditionally: the worktree
229
+ * holds nothing this tool did not itself put there, and `restore` runs
230
+ * again immediately after this to reassert the frozen bytes regardless.
231
+ */
232
+ export async function repointWorktree(worktreeDir, commit) {
233
+ await git(worktreeDir, ['checkout', '-q', '--detach', '--force', commit]);
234
+ }
235
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/gitx/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { Benchmark } from '../discover/benchmarks.js';
2
+ export type Observations = Map<string, number[]>;
3
+ export interface InterleaveOptions {
4
+ rounds: number;
5
+ benchmarks: Benchmark[];
6
+ baseDir: string;
7
+ candDir: string;
8
+ measureOne: (dir: string, b: Benchmark) => Promise<number>;
9
+ }
10
+ /**
11
+ * Alternates the two sides while measuring, and accumulates one observation
12
+ * per benchmark per round per side.
13
+ *
14
+ * Two orderings matter, for two different reasons.
15
+ *
16
+ * Across rounds: comparing a candidate measured now against a baseline
17
+ * measured minutes ago attributes thermal drift, frequency scaling and
18
+ * background load to the code change. Alternating cancels it.
19
+ *
20
+ * Within a pair: measuring base immediately before cand, every time, leaves a
21
+ * small consistent bias on a machine that is steadily warming. Swapping the
22
+ * order on alternate rounds cancels that too -- an asymmetry the Go
23
+ * implementation documents as known and residual.
24
+ *
25
+ * Within one round, both sides of a given benchmark are measured adjacently
26
+ * before moving on to the next benchmark, keeping the pair as close in time
27
+ * as possible -- which is the whole point of interleaving at all.
28
+ */
29
+ export declare function interleave(o: InterleaveOptions): Promise<{
30
+ base: Observations;
31
+ cand: Observations;
32
+ }>;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Alternates the two sides while measuring, and accumulates one observation
3
+ * per benchmark per round per side.
4
+ *
5
+ * Two orderings matter, for two different reasons.
6
+ *
7
+ * Across rounds: comparing a candidate measured now against a baseline
8
+ * measured minutes ago attributes thermal drift, frequency scaling and
9
+ * background load to the code change. Alternating cancels it.
10
+ *
11
+ * Within a pair: measuring base immediately before cand, every time, leaves a
12
+ * small consistent bias on a machine that is steadily warming. Swapping the
13
+ * order on alternate rounds cancels that too -- an asymmetry the Go
14
+ * implementation documents as known and residual.
15
+ *
16
+ * Within one round, both sides of a given benchmark are measured adjacently
17
+ * before moving on to the next benchmark, keeping the pair as close in time
18
+ * as possible -- which is the whole point of interleaving at all.
19
+ */
20
+ export async function interleave(o) {
21
+ const base = new Map();
22
+ const cand = new Map();
23
+ for (const b of o.benchmarks) {
24
+ base.set(b.id, []);
25
+ cand.set(b.id, []);
26
+ }
27
+ for (let round = 0; round < o.rounds; round++) {
28
+ const baseFirst = round % 2 === 0;
29
+ for (const b of o.benchmarks) {
30
+ if (baseFirst) {
31
+ base.get(b.id).push(await o.measureOne(o.baseDir, b));
32
+ cand.get(b.id).push(await o.measureOne(o.candDir, b));
33
+ }
34
+ else {
35
+ cand.get(b.id).push(await o.measureOne(o.candDir, b));
36
+ base.get(b.id).push(await o.measureOne(o.baseDir, b));
37
+ }
38
+ }
39
+ }
40
+ return { base, cand };
41
+ }
42
+ //# sourceMappingURL=interleave.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interleave.js","sourceRoot":"","sources":["../../src/measure/interleave.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,CAAoB;IAInD,MAAM,IAAI,GAAiB,IAAI,GAAG,EAAE,CAAA;IACpC,MAAM,IAAI,GAAiB,IAAI,GAAG,EAAE,CAAA;IACpC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;QAC7B,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;QAClB,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAA;IACpB,CAAC;IAED,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9C,MAAM,SAAS,GAAG,KAAK,GAAG,CAAC,KAAK,CAAC,CAAA;QACjC,KAAK,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;YAC7B,IAAI,SAAS,EAAE,CAAC;gBACd,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;gBACtD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;YACxD,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;gBACtD,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAA;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAA;AACvB,CAAC"}
@@ -0,0 +1,67 @@
1
+ import type { Benchmark } from '../discover/benchmarks.js';
2
+ import { type Delta } from '../stats/delta.js';
3
+ import { type Verdict } from '../verdict/verdict.js';
4
+ export type GateName =
5
+ /** No baseline for this tag, or the config file itself could not be loaded at all. */
6
+ 'setup' | 'scope' | 'config-integrity' | 'restore' | 'unmanifested' | 'typecheck' | 'build' | 'test' | 'worktree-integrity' | 'measure'
7
+ /** Advancing measureCommit after a KEEP was already decided. */
8
+ | 'advance';
9
+ /**
10
+ * The subset of `RunCtx` (see `cli/runctx.ts`) this pipeline needs. Declared
11
+ * independently rather than importing `RunCtx` itself so the pipeline layer
12
+ * does not depend on the CLI layer -- `cli/cmd-eval.ts` passes its `RunCtx`
13
+ * straight through, which satisfies this structurally.
14
+ */
15
+ export interface EvalRepoCtx {
16
+ repoRoot: string;
17
+ configPath: string;
18
+ resultsPath: string;
19
+ }
20
+ export interface EvalOptions {
21
+ ctx: EvalRepoCtx;
22
+ tag: string;
23
+ /** Free-text experiment description, recorded on the results.tsv row. */
24
+ description: string;
25
+ /** Subprocess transcripts (typecheck/build/test/measurement children). */
26
+ log?: (chunk: string) => void;
27
+ /**
28
+ * Testing hook: overrides how one benchmark is measured in one directory.
29
+ * Defaults to a real `runChild` spawn. Exists so gate-order tests can prove
30
+ * a rejected gate never measures anything, by asserting this was never
31
+ * called, without needing to spawn (and then discard) a real subprocess.
32
+ */
33
+ measureOne?: (dir: string, b: Benchmark) => Promise<number>;
34
+ }
35
+ export interface EvalOutcome {
36
+ verdict: Verdict;
37
+ deltas: Delta[];
38
+ stopRequested: boolean;
39
+ failedGate?: GateName;
40
+ message: string;
41
+ /**
42
+ * Every non-verdict warning gathered along the way (e.g. "gate 3 restored
43
+ * N frozen file(s)"), followed by the verdict's own warnings when a score
44
+ * was actually computed. Not in the brief's minimal interface, but
45
+ * `cmd-eval`'s `--json` output and its human "WARNING:" lines both need a
46
+ * single combined list, and building it here (once, where the gates run)
47
+ * beats recomputing it at the CLI layer from partial information.
48
+ */
49
+ warnings: string[];
50
+ /** Repo-relative frozen files whose content differed from baseline and were restored. */
51
+ restoredFiles: string[];
52
+ /** HEAD of the repository being evaluated -- the commit this verdict is about. */
53
+ candidateCommit: string;
54
+ frozenCommit: string;
55
+ measureCommit: string;
56
+ /** 1-based count of experiments recorded in results.tsv, this one included. */
57
+ experiment: number;
58
+ }
59
+ /**
60
+ * Runs one experiment end to end: the full gate chain, and -- for whatever
61
+ * outcome it ends in -- one `results.tsv` row.
62
+ *
63
+ * The eval lock is held for the whole chain and released in a `finally`, so
64
+ * a crash mid-chain (this function throwing something genuinely
65
+ * unanticipated) never leaves a future eval permanently blocked.
66
+ */
67
+ export declare function runEval(opts: EvalOptions): Promise<EvalOutcome>;