@tangle-network/agent-bench 0.11.2 → 0.13.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 (174) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/HARNESS.md +6 -2
  3. package/README.md +1 -4
  4. package/dist/benchmarks/swe-bench.js +4 -9
  5. package/dist/benchmarks/swe-bench.js.map +1 -1
  6. package/package.json +5 -5
  7. package/scripts/run-package-tests.mjs +2 -2
  8. package/src/benchmarks/swe-bench.test.mts +49 -0
  9. package/src/benchmarks/swe-bench.ts +4 -9
  10. package/src/quant-arena/README.md +0 -144
  11. package/src/quant-arena/backtest.test.mts +0 -135
  12. package/src/quant-arena/backtest.ts +0 -218
  13. package/src/quant-arena/data.test.mts +0 -44
  14. package/src/quant-arena/data.ts +0 -141
  15. package/src/quant-arena/driver.test.mts +0 -253
  16. package/src/quant-arena/driver.ts +0 -219
  17. package/src/quant-arena/fixtures/data/PROVENANCE.md +0 -26
  18. package/src/quant-arena/fixtures/data/holdout/IDX.csv +0 -523
  19. package/src/quant-arena/fixtures/data/holdout/S01.csv +0 -523
  20. package/src/quant-arena/fixtures/data/holdout/S02.csv +0 -523
  21. package/src/quant-arena/fixtures/data/holdout/S03.csv +0 -523
  22. package/src/quant-arena/fixtures/data/holdout/S04.csv +0 -523
  23. package/src/quant-arena/fixtures/data/holdout/S05.csv +0 -523
  24. package/src/quant-arena/fixtures/data/holdout/S06.csv +0 -523
  25. package/src/quant-arena/fixtures/data/holdout/S07.csv +0 -523
  26. package/src/quant-arena/fixtures/data/holdout/S08.csv +0 -523
  27. package/src/quant-arena/fixtures/data/holdout/S09.csv +0 -523
  28. package/src/quant-arena/fixtures/data/holdout/S10.csv +0 -523
  29. package/src/quant-arena/fixtures/data/insample/IDX.csv +0 -2087
  30. package/src/quant-arena/fixtures/data/insample/S01.csv +0 -2087
  31. package/src/quant-arena/fixtures/data/insample/S02.csv +0 -2087
  32. package/src/quant-arena/fixtures/data/insample/S03.csv +0 -2087
  33. package/src/quant-arena/fixtures/data/insample/S04.csv +0 -2087
  34. package/src/quant-arena/fixtures/data/insample/S05.csv +0 -2087
  35. package/src/quant-arena/fixtures/data/insample/S06.csv +0 -2087
  36. package/src/quant-arena/fixtures/data/insample/S07.csv +0 -2087
  37. package/src/quant-arena/fixtures/data/insample/S08.csv +0 -2087
  38. package/src/quant-arena/fixtures/data/insample/S09.csv +0 -2087
  39. package/src/quant-arena/fixtures/data/insample/S10.csv +0 -2087
  40. package/src/quant-arena/fixtures/demo-campaign/cost-ledger.jsonl +0 -16
  41. package/src/quant-arena/fixtures/demo-campaign/notebook.jsonl +0 -5
  42. package/src/quant-arena/fixtures/demo-campaign/rollout-manifest.json +0 -171
  43. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-001-default-author/strategy.ts +0 -119
  44. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-002-default-author/strategy.ts +0 -119
  45. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-003-quant-researcher/strategy.ts +0 -105
  46. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-004-quant-researcher/strategy.ts +0 -102
  47. package/src/quant-arena/fixtures/demo-campaign-v2/cost-ledger.jsonl +0 -4
  48. package/src/quant-arena/fixtures/demo-campaign-v2/notebook.jsonl +0 -2
  49. package/src/quant-arena/fixtures/demo-campaign-v2/rollout-manifest.json +0 -84
  50. package/src/quant-arena/fixtures/demo-campaign-v2/strategies/cand-001-quant-researcher/strategy.ts +0 -117
  51. package/src/quant-arena/holdout-certify.mts +0 -206
  52. package/src/quant-arena/holdout-certify.test.mts +0 -82
  53. package/src/quant-arena/leak-audit.test.mts +0 -79
  54. package/src/quant-arena/leak-audit.ts +0 -95
  55. package/src/quant-arena/make-fixtures.mts +0 -161
  56. package/src/quant-arena/multiplicity.test.mts +0 -68
  57. package/src/quant-arena/multiplicity.ts +0 -87
  58. package/src/quant-arena/nautilus-certify.ts +0 -31
  59. package/src/quant-arena/oms.ts +0 -90
  60. package/src/quant-arena/profiles/quant-researcher.profile.json +0 -12
  61. package/src/quant-arena/python/pyproject.toml +0 -8
  62. package/src/quant-arena/python/uv.lock +0 -1297
  63. package/src/quant-arena/python/vbt-worker.py +0 -192
  64. package/src/quant-arena/quant-loop.mts +0 -840
  65. package/src/quant-arena/quant-loop.test.mts +0 -75
  66. package/src/quant-arena/strategies/buy-hold-index/strategy.ts +0 -11
  67. package/src/quant-arena/strategies/equal-weight/strategy.ts +0 -20
  68. package/src/quant-arena/strategies/sma-crossover/strategy.ts +0 -42
  69. package/src/quant-arena/types.ts +0 -133
  70. package/src/quant-arena/vbt-client.ts +0 -321
  71. package/src/quant-arena/vbt-parity.test.mts +0 -183
  72. package/src/quant-arena/windows.test.mts +0 -45
  73. package/src/quant-arena/windows.ts +0 -54
  74. package/src/rollout-ledger/backfill-swe-arena.mts +0 -610
  75. package/src/rollout-ledger/backfill-swe-arena.test.mts +0 -347
  76. package/src/rollout-ledger/settle-capture.mts +0 -448
  77. package/src/rollout-ledger/settle-capture.test.mts +0 -270
  78. package/src/swe-arena/activation.mts +0 -225
  79. package/src/swe-arena/activation.test.mts +0 -300
  80. package/src/swe-arena/analyze.ts +0 -211
  81. package/src/swe-arena/arms.ts +0 -862
  82. package/src/swe-arena/bootstrap-meta.mts +0 -188
  83. package/src/swe-arena/bootstrap-meta.test.mts +0 -51
  84. package/src/swe-arena/briefing.mts +0 -217
  85. package/src/swe-arena/briefing.test.mts +0 -179
  86. package/src/swe-arena/calibrate.ts +0 -217
  87. package/src/swe-arena/capabilities.mts +0 -76
  88. package/src/swe-arena/capabilities.test.mts +0 -57
  89. package/src/swe-arena/capacity.ts +0 -198
  90. package/src/swe-arena/cell-evidence.mts +0 -437
  91. package/src/swe-arena/cell-evidence.test.mts +0 -248
  92. package/src/swe-arena/diagnosis-ensemble.test.mts +0 -210
  93. package/src/swe-arena/diagnosis-ensemble.ts +0 -523
  94. package/src/swe-arena/execution.test.mts +0 -1171
  95. package/src/swe-arena/factory-command-container.ts +0 -284
  96. package/src/swe-arena/factory-judge-child.mts +0 -228
  97. package/src/swe-arena/factory.test.mts +0 -645
  98. package/src/swe-arena/fixtures/analyze.py +0 -80
  99. package/src/swe-arena/fixtures/excludes.txt +0 -8
  100. package/src/swe-arena/fixtures/factory/agent-eval-309/calibration.md +0 -51
  101. package/src/swe-arena/fixtures/factory/agent-eval-309/manifest.json +0 -29
  102. package/src/swe-arena/fixtures/factory/agent-eval-309/spec.md +0 -64
  103. package/src/swe-arena/fixtures/factory/agent-runtime-232/calibration.md +0 -48
  104. package/src/swe-arena/fixtures/factory/agent-runtime-232/manifest.json +0 -29
  105. package/src/swe-arena/fixtures/factory/agent-runtime-232/spec.md +0 -48
  106. package/src/swe-arena/fixtures/factory/loops-28/calibration.md +0 -47
  107. package/src/swe-arena/fixtures/factory/loops-28/manifest.json +0 -30
  108. package/src/swe-arena/fixtures/factory/loops-28/spec.md +0 -50
  109. package/src/swe-arena/fixtures/gen1-salvage/README.md +0 -45
  110. package/src/swe-arena/fixtures/gen1-salvage/cand0-e6d7361.diff +0 -116
  111. package/src/swe-arena/fixtures/gen1-salvage/cand1-76a8590.diff +0 -293
  112. package/src/swe-arena/fixtures/holdout-preregister.log +0 -12
  113. package/src/swe-arena/fixtures/holdout.json +0 -44
  114. package/src/swe-arena/fixtures/instances.json +0 -146
  115. package/src/swe-arena/fixtures/ledger.jsonl +0 -12
  116. package/src/swe-arena/fixtures/patches/pallets__flask-5014.solo.patch +0 -36
  117. package/src/swe-arena/fixtures/patches/pydata__xarray-4687.sup.patch +0 -33
  118. package/src/swe-arena/fixtures/rejudge.jsonl +0 -15
  119. package/src/swe-arena/fixtures/rematch.jsonl +0 -3
  120. package/src/swe-arena/fixtures/rematch2.jsonl +0 -3
  121. package/src/swe-arena/fixtures/rematch3.jsonl +0 -3
  122. package/src/swe-arena/fixtures/run-report/README.md +0 -43
  123. package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.json +0 -173
  124. package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.md +0 -100
  125. package/src/swe-arena/fixtures/run-report/gen3-rollup.json +0 -551
  126. package/src/swe-arena/fixtures/run-report/gen3-rollup.md +0 -64
  127. package/src/swe-arena/fixtures/sup-journal-true.json +0 -19
  128. package/src/swe-arena/fixtures/verify/astropy__astropy-13033.sh +0 -48
  129. package/src/swe-arena/fixtures/verify/django__django-11532.sh +0 -50
  130. package/src/swe-arena/fixtures/verify/matplotlib__matplotlib-20826.sh +0 -76
  131. package/src/swe-arena/fixtures/verify/pydata__xarray-4687.sh +0 -44
  132. package/src/swe-arena/fixtures/verify/pytest-dev__pytest-6197.sh +0 -32
  133. package/src/swe-arena/fixtures/verify/sphinx-doc__sphinx-9658.sh +0 -51
  134. package/src/swe-arena/fixtures/worker-tokens.json +0 -42
  135. package/src/swe-arena/fixtures.ts +0 -237
  136. package/src/swe-arena/gepa-seat.mts +0 -886
  137. package/src/swe-arena/gepa-seat.test.mts +0 -1136
  138. package/src/swe-arena/holdout-certify.mts +0 -408
  139. package/src/swe-arena/holdout-certify.test.mts +0 -160
  140. package/src/swe-arena/implementation-ref.test.mts +0 -64
  141. package/src/swe-arena/implementation-ref.ts +0 -62
  142. package/src/swe-arena/judge-child.mts +0 -37
  143. package/src/swe-arena/ledger-orphans.mts +0 -77
  144. package/src/swe-arena/ledger-orphans.test.mts +0 -149
  145. package/src/swe-arena/manifest.mts +0 -293
  146. package/src/swe-arena/manifest.test.mts +0 -169
  147. package/src/swe-arena/materialize.ts +0 -142
  148. package/src/swe-arena/outer-loop.mts +0 -2854
  149. package/src/swe-arena/outer-loop.test.mts +0 -714
  150. package/src/swe-arena/parity.test.mts +0 -87
  151. package/src/swe-arena/premeasured-from-cells.mts +0 -296
  152. package/src/swe-arena/premeasured-from-cells.test.mts +0 -201
  153. package/src/swe-arena/proc.test.mts +0 -172
  154. package/src/swe-arena/proc.ts +0 -260
  155. package/src/swe-arena/profiles/deepseek-author.profile.json +0 -12
  156. package/src/swe-arena/profiles/default-author.profile.json +0 -12
  157. package/src/swe-arena/proposer-fanout.mts +0 -736
  158. package/src/swe-arena/proposer-fanout.test.mts +0 -660
  159. package/src/swe-arena/proposer-provenance.mts +0 -176
  160. package/src/swe-arena/proposer-provenance.test.mts +0 -106
  161. package/src/swe-arena/reconcile.ts +0 -0
  162. package/src/swe-arena/replay.mts +0 -183
  163. package/src/swe-arena/replay.test.mts +0 -300
  164. package/src/swe-arena/run-experiment.mts +0 -729
  165. package/src/swe-arena/run-report.mts +0 -75
  166. package/src/swe-arena/run-supervisor.mjs +0 -297
  167. package/src/swe-arena/run-supervisor.test.mts +0 -539
  168. package/src/swe-arena/score-split.mts +0 -140
  169. package/src/swe-arena/score-split.test.mts +0 -123
  170. package/src/swe-arena/scratch-worktree-serialization.test.mts +0 -72
  171. package/src/swe-arena/scratch-worktree.test.mts +0 -56
  172. package/src/swe-arena/scratch-worktree.ts +0 -64
  173. package/src/swe-arena/serialized-judge.ts +0 -414
  174. package/src/swe-arena/types.ts +0 -218
@@ -1,539 +0,0 @@
1
- import { access, mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
2
- import { tmpdir } from 'node:os'
3
- import { join } from 'node:path'
4
- import { afterAll, describe, expect, it } from 'vitest'
5
- import { runSupervisorShim, supervisorDriverKillGraceMs } from './arms.ts'
6
- import { ABORT_RC, run } from './proc.ts'
7
-
8
- const dirs: string[] = []
9
- const workerPids: number[] = []
10
-
11
- async function waitForFile(path: string, timeoutMs = 10_000): Promise<void> {
12
- const deadline = Date.now() + timeoutMs
13
- while (Date.now() < deadline) {
14
- try {
15
- await access(path)
16
- return
17
- } catch (error) {
18
- if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
19
- }
20
- await new Promise((resolve) => setTimeout(resolve, 10))
21
- }
22
- throw new Error(`timed out waiting for ${path}`)
23
- }
24
-
25
- afterAll(async () => {
26
- for (const pid of workerPids) {
27
- try {
28
- process.kill(-pid, 'SIGKILL')
29
- } catch {
30
- // The expected path already reaped the process group.
31
- }
32
- }
33
- await Promise.all(dirs.map((dir) => rm(dir, { recursive: true, force: true })))
34
- })
35
-
36
- describe('supervisor driver cancellation', () => {
37
- it('waits for worker cleanup before returning its deadline result', async () => {
38
- const dir = await mkdtemp(join(tmpdir(), 'swe-driver-cancel-'))
39
- dirs.push(dir)
40
- const workspace = join(dir, 'workspace')
41
- const params = join(dir, 'params.json')
42
- const extension = join(dir, 'fake-extension.mjs')
43
- await mkdir(workspace)
44
- await writeFile(params, '{}')
45
- const workerScript = `const fs=require('node:fs'); const marker=process.argv[1]; const ready=process.argv[2]; process.on('SIGTERM',()=>setTimeout(()=>{fs.writeFileSync(marker,'clean'); process.exit(0)},30)); fs.writeFileSync(ready,'ready'); setInterval(()=>{},1000)`
46
- await writeFile(extension, `
47
- import { spawn } from 'node:child_process'
48
- import { appendFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs'
49
- import { join } from 'node:path'
50
-
51
- const id = 'sup-1-fake12'
52
- const text = (value) => ({ content: [{ type: 'text', text: value }] })
53
- let child
54
-
55
- export default function fakeExtension(pi) {
56
- pi.registerTool({
57
- name: 'spawn_supervisor',
58
- async execute(_call, _params, _signal, _update, ctx) {
59
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
60
- mkdirSync(runDir, { recursive: true })
61
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'running', progress: 'driving' }))
62
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
63
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
64
- const readyPath = join(ctx.cwd, 'worker-ready.txt')
65
- child = spawn(process.execPath, ['-e', ${JSON.stringify(workerScript)}, join(ctx.cwd, 'worker-cleaned.txt'), readyPath], {
66
- detached: true,
67
- stdio: 'ignore',
68
- })
69
- writeFileSync(join(ctx.cwd, 'worker.pid'), String(child.pid))
70
- while (!existsSync(readyPath)) await new Promise((resolve) => setTimeout(resolve, 5))
71
- return text('spawned supervisor ' + id)
72
- },
73
- })
74
- pi.registerTool({
75
- name: 'supervisor_cancel',
76
- async execute(_call, _params, _signal, _update, ctx) {
77
- appendFileSync(join(ctx.cwd, 'cancel-count.txt'), '1\\n')
78
- process.kill(-child.pid, 'SIGTERM')
79
- child.once('close', () => {
80
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
81
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'cancelled', id: id + ':s1', reason: 'test cancellation' }) + '\\n')
82
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'cancelled', progress: 'cancelled' }))
83
- writeFileSync(join(ctx.cwd, 'cancel-settled.txt'), 'settled')
84
- })
85
- return text('cancellation signalled before worker cleanup')
86
- },
87
- })
88
- }
89
- `)
90
-
91
- const result = await run(process.execPath, [
92
- '--import', 'tsx', runSupervisorShim, extension, workspace, params,
93
- ], {
94
- timeoutMs: 5_000,
95
- killGraceMs: 500,
96
- env: {
97
- ...process.env,
98
- DRIVER_DEADLINE_MS: '40',
99
- DRIVER_POLL_MS: '10',
100
- DRIVER_CANCEL_TIMEOUT_MS: '2000',
101
- },
102
- })
103
- const workerPid = Number(await readFile(join(workspace, 'worker.pid'), 'utf8'))
104
- workerPids.push(workerPid)
105
-
106
- expect(result.code, result.stdout + result.stderr).toBe(3)
107
- expect(result.timedOut).toBe(false)
108
- expect(await readFile(join(workspace, 'cancel-count.txt'), 'utf8')).toBe('1\n')
109
- expect(await readFile(join(workspace, 'worker-cleaned.txt'), 'utf8')).toBe('clean')
110
- expect(await readFile(join(workspace, 'cancel-settled.txt'), 'utf8')).toBe('settled')
111
- expect(JSON.parse(await readFile(join(workspace, '.loops', 'supervisor', 'sup-1-fake12', 'state.json'), 'utf8')))
112
- .toMatchObject({ status: 'cancelled' })
113
- await expect(access(`/proc/${workerPid}`)).rejects.toMatchObject({ code: 'ENOENT' })
114
- })
115
-
116
- it('lets near-deadline cancellation settle after outer SIGTERM', async () => {
117
- const dir = await mkdtemp(join(tmpdir(), 'swe-driver-outer-term-'))
118
- dirs.push(dir)
119
- const workspace = join(dir, 'workspace')
120
- const params = join(dir, 'params.json')
121
- const extension = join(dir, 'fake-extension.mjs')
122
- await mkdir(workspace)
123
- await writeFile(params, '{}')
124
- await writeFile(extension, `
125
- import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs'
126
- import { join } from 'node:path'
127
-
128
- const id = 'sup-2-fake34'
129
- const text = (value) => ({ content: [{ type: 'text', text: value }] })
130
- export default function fakeExtension(pi) {
131
- pi.registerTool({
132
- name: 'spawn_supervisor',
133
- async execute(_call, _params, _signal, _update, ctx) {
134
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
135
- mkdirSync(runDir, { recursive: true })
136
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'running' }))
137
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
138
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
139
- writeFileSync(join(ctx.cwd, 'outer-driver-ready.txt'), 'ready')
140
- return text('spawned supervisor ' + id)
141
- },
142
- })
143
- pi.registerTool({
144
- name: 'supervisor_cancel',
145
- async execute(_call, _params, _signal, _update, ctx) {
146
- await new Promise((resolve) => setTimeout(resolve, 1200))
147
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
148
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'cancelled', id: id + ':s1' }) + '\\n')
149
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'cancelled' }))
150
- writeFileSync(join(ctx.cwd, 'outer-cancel-settled.txt'), 'settled')
151
- return text('cancellation settled')
152
- },
153
- })
154
- }
155
- `)
156
-
157
- const cancelTimeoutMs = 1_500
158
- const controller = new AbortController()
159
- const resultPromise = run(process.execPath, [
160
- '--import', 'tsx', runSupervisorShim, extension, workspace, params,
161
- ], {
162
- signal: controller.signal,
163
- killGraceMs: supervisorDriverKillGraceMs({
164
- DRIVER_CANCEL_TIMEOUT_MS: String(cancelTimeoutMs),
165
- }),
166
- env: {
167
- ...process.env,
168
- DRIVER_DEADLINE_MS: '60000',
169
- DRIVER_POLL_MS: '10',
170
- DRIVER_CANCEL_TIMEOUT_MS: String(cancelTimeoutMs),
171
- },
172
- })
173
- try {
174
- await waitForFile(join(workspace, 'outer-driver-ready.txt'))
175
- } catch (error) {
176
- controller.abort()
177
- await resultPromise
178
- throw error
179
- }
180
- controller.abort()
181
- const result = await resultPromise
182
-
183
- expect(result.code, result.stdout + result.stderr).toBe(ABORT_RC)
184
- expect(result.aborted).toBe(true)
185
- expect(result.timedOut).toBe(false)
186
- expect(await readFile(join(workspace, 'outer-cancel-settled.txt'), 'utf8')).toBe('settled')
187
- expect(JSON.parse(await readFile(join(workspace, '.loops', 'supervisor', 'sup-2-fake34', 'state.json'), 'utf8')))
188
- .toMatchObject({ status: 'cancelled' })
189
- }, 15_000)
190
-
191
- it('latches SIGTERM while spawn_supervisor is still returning and cancels its detached worker', async () => {
192
- const dir = await mkdtemp(join(tmpdir(), 'swe-driver-signal-during-spawn-'))
193
- dirs.push(dir)
194
- const workspace = join(dir, 'workspace')
195
- const params = join(dir, 'params.json')
196
- const extension = join(dir, 'fake-extension.mjs')
197
- await mkdir(workspace)
198
- await writeFile(params, '{}')
199
- const workerScript = `const fs=require('node:fs'); const marker=process.argv[1]; const ready=process.argv[2]; process.on('SIGTERM',()=>setTimeout(()=>{fs.writeFileSync(marker,'clean'); process.exit(0)},30)); fs.writeFileSync(ready,'ready'); setInterval(()=>{},1000)`
200
- await writeFile(extension, `
201
- import { spawn } from 'node:child_process'
202
- import { appendFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs'
203
- import { join } from 'node:path'
204
-
205
- const id = 'sup-3-signal56'
206
- const text = (value) => ({ content: [{ type: 'text', text: value }] })
207
- let child
208
- export default function fakeExtension(pi) {
209
- pi.registerTool({
210
- name: 'spawn_supervisor',
211
- async execute(_call, _params, _signal, _update, ctx) {
212
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
213
- mkdirSync(runDir, { recursive: true })
214
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'running' }))
215
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
216
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
217
- const readyPath = join(ctx.cwd, 'signal-worker-ready.txt')
218
- child = spawn(process.execPath, ['-e', ${JSON.stringify(workerScript)}, join(ctx.cwd, 'signal-worker-cleaned.txt'), readyPath], {
219
- detached: true,
220
- stdio: 'ignore',
221
- })
222
- writeFileSync(join(ctx.cwd, 'signal-worker.pid'), String(child.pid))
223
- while (!existsSync(readyPath)) await new Promise((resolve) => setTimeout(resolve, 5))
224
- writeFileSync(join(ctx.cwd, 'spawn-entered.txt'), 'entered')
225
- await new Promise((resolve) => setTimeout(resolve, 30_000))
226
- return text('spawned supervisor ' + id)
227
- },
228
- })
229
- pi.registerTool({
230
- name: 'supervisor_cancel',
231
- async execute(_call, _params, _signal, _update, ctx) {
232
- process.kill(-child.pid, 'SIGTERM')
233
- await new Promise((resolve) => child.once('close', resolve))
234
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
235
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'cancelled', id: id + ':s1' }) + '\\n')
236
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'cancelled' }))
237
- writeFileSync(join(ctx.cwd, 'signal-cancel-settled.txt'), 'settled')
238
- return text('cancellation settled')
239
- },
240
- })
241
- }
242
- `)
243
-
244
- const cancelTimeoutMs = 2_000
245
- const controller = new AbortController()
246
- const resultPromise = run(process.execPath, [
247
- '--import', 'tsx', runSupervisorShim, extension, workspace, params,
248
- ], {
249
- signal: controller.signal,
250
- killGraceMs: supervisorDriverKillGraceMs({
251
- DRIVER_CANCEL_TIMEOUT_MS: String(cancelTimeoutMs),
252
- }),
253
- env: {
254
- ...process.env,
255
- DRIVER_DEADLINE_MS: '60000',
256
- DRIVER_POLL_MS: '10',
257
- DRIVER_CANCEL_TIMEOUT_MS: String(cancelTimeoutMs),
258
- },
259
- })
260
- try {
261
- await waitForFile(join(workspace, 'spawn-entered.txt'))
262
- } catch (error) {
263
- controller.abort()
264
- await resultPromise
265
- throw error
266
- }
267
- controller.abort()
268
- const result = await resultPromise
269
- const workerPid = Number(await readFile(join(workspace, 'signal-worker.pid'), 'utf8'))
270
- workerPids.push(workerPid)
271
-
272
- expect(result.code, result.stdout + result.stderr).toBe(ABORT_RC)
273
- expect(result.aborted).toBe(true)
274
- expect(result.timedOut).toBe(false)
275
- expect(await readFile(join(workspace, 'spawn-entered.txt'), 'utf8')).toBe('entered')
276
- expect(await readFile(join(workspace, 'signal-worker-cleaned.txt'), 'utf8')).toBe('clean')
277
- expect(await readFile(join(workspace, 'signal-cancel-settled.txt'), 'utf8')).toBe('settled')
278
- await expect(access(`/proc/${workerPid}`)).rejects.toMatchObject({ code: 'ENOENT' })
279
- }, 15_000)
280
-
281
- it('recovers an on-disk supervisor id from an unparseable spawn response and cancels it', async () => {
282
- const dir = await mkdtemp(join(tmpdir(), 'swe-driver-id-recovery-'))
283
- dirs.push(dir)
284
- const workspace = join(dir, 'workspace')
285
- const params = join(dir, 'params.json')
286
- const extension = join(dir, 'fake-extension.mjs')
287
- await mkdir(workspace)
288
- await writeFile(params, '{}')
289
- const workerScript = `const fs=require('node:fs'); const marker=process.argv[1]; process.on('SIGTERM',()=>setTimeout(()=>{fs.writeFileSync(marker,'clean'); process.exit(0)},30)); setInterval(()=>{},1000)`
290
- await writeFile(extension, `
291
- import { spawn } from 'node:child_process'
292
- import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs'
293
- import { join } from 'node:path'
294
-
295
- const id = 'sup-4-recover78'
296
- const text = (value) => ({ content: [{ type: 'text', text: value }] })
297
- let child
298
- export default function fakeExtension(pi) {
299
- pi.registerTool({
300
- name: 'spawn_supervisor',
301
- async execute(_call, _params, _signal, _update, ctx) {
302
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
303
- mkdirSync(runDir, { recursive: true })
304
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'running' }))
305
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
306
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
307
- child = spawn(process.execPath, ['-e', ${JSON.stringify(workerScript)}, join(ctx.cwd, 'recovered-worker-cleaned.txt')], {
308
- detached: true,
309
- stdio: 'ignore',
310
- })
311
- writeFileSync(join(ctx.cwd, 'recovered-worker.pid'), String(child.pid))
312
- return text('the supervisor started, but this response omitted its identifier')
313
- },
314
- })
315
- pi.registerTool({
316
- name: 'supervisor_cancel',
317
- async execute(_call, params, _signal, _update, ctx) {
318
- writeFileSync(join(ctx.cwd, 'recovered-id.txt'), params.id)
319
- process.kill(-child.pid, 'SIGTERM')
320
- await new Promise((resolve) => child.once('close', resolve))
321
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
322
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'cancelled', id: id + ':s1' }) + '\\n')
323
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'cancelled' }))
324
- return text('cancellation settled')
325
- },
326
- })
327
- }
328
- `)
329
-
330
- const result = await run(process.execPath, [
331
- '--import', 'tsx', runSupervisorShim, extension, workspace, params,
332
- ], {
333
- timeoutMs: 5_000,
334
- killGraceMs: 500,
335
- env: {
336
- ...process.env,
337
- DRIVER_DEADLINE_MS: '60000',
338
- DRIVER_POLL_MS: '10',
339
- DRIVER_CANCEL_TIMEOUT_MS: '2000',
340
- },
341
- })
342
- const workerPid = Number(await readFile(join(workspace, 'recovered-worker.pid'), 'utf8'))
343
- workerPids.push(workerPid)
344
-
345
- expect(result.code, result.stdout + result.stderr).toBe(1)
346
- expect(result.timedOut).toBe(false)
347
- expect(result.stderr).toMatch(/cancelling recovered run\(s\): sup-4-recover78/)
348
- expect(await readFile(join(workspace, 'recovered-id.txt'), 'utf8')).toBe('sup-4-recover78')
349
- await expect(access(`/proc/${workerPid}`)).rejects.toMatchObject({ code: 'ENOENT' })
350
- }, 15_000)
351
-
352
- it('waits for a late worker terminal record before accepting completed state', async () => {
353
- const dir = await mkdtemp(join(tmpdir(), 'swe-driver-late-settlement-'))
354
- dirs.push(dir)
355
- const workspace = join(dir, 'workspace')
356
- const params = join(dir, 'params.json')
357
- const extension = join(dir, 'fake-extension.mjs')
358
- await mkdir(workspace)
359
- await writeFile(params, '{}')
360
- await writeFile(extension, `
361
- import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs'
362
- import { join } from 'node:path'
363
-
364
- const id = 'sup-5-late90'
365
- const text = (value) => ({ content: [{ type: 'text', text: value }] })
366
- export default function fakeExtension(pi) {
367
- pi.registerTool({
368
- name: 'spawn_supervisor',
369
- async execute(_call, _params, _signal, _update, ctx) {
370
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
371
- mkdirSync(runDir, { recursive: true })
372
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'completed', verdict: 'delivered' }))
373
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
374
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
375
- setTimeout(() => {
376
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'settled', id: id + ':s1', status: 'done' }) + '\\n')
377
- }, 40)
378
- return text('spawned supervisor ' + id)
379
- },
380
- })
381
- pi.registerTool({
382
- name: 'supervisor_cancel',
383
- async execute() { return text('already completed') },
384
- })
385
- }
386
- `)
387
-
388
- const result = await run(process.execPath, [
389
- '--import', 'tsx', runSupervisorShim, extension, workspace, params,
390
- ], {
391
- timeoutMs: 5_000,
392
- killGraceMs: 500,
393
- env: {
394
- ...process.env,
395
- DRIVER_DEADLINE_MS: '60000',
396
- DRIVER_POLL_MS: '10',
397
- DRIVER_CANCEL_TIMEOUT_MS: '2000',
398
- DRIVER_SETTLEMENT_TIMEOUT_MS: '500',
399
- },
400
- })
401
-
402
- expect(result.code, result.stdout + result.stderr).toBe(0)
403
- expect(result.timedOut).toBe(false)
404
- expect(result.stdout).toMatch(/worker settlement: workers=1, terminal=1/)
405
- })
406
-
407
- it('refuses completed state while a journal worker remains live', async () => {
408
- const dir = await mkdtemp(join(tmpdir(), 'swe-driver-live-completed-worker-'))
409
- dirs.push(dir)
410
- const workspace = join(dir, 'workspace')
411
- const params = join(dir, 'params.json')
412
- const extension = join(dir, 'fake-extension.mjs')
413
- await mkdir(workspace)
414
- await writeFile(params, '{}')
415
- await writeFile(extension, `
416
- import { appendFileSync, mkdirSync, writeFileSync } from 'node:fs'
417
- import { join } from 'node:path'
418
-
419
- const id = 'sup-5-live90'
420
- const text = (value) => ({ content: [{ type: 'text', text: value }] })
421
- export default function fakeExtension(pi) {
422
- pi.registerTool({
423
- name: 'spawn_supervisor',
424
- async execute(_call, _params, _signal, _update, ctx) {
425
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
426
- mkdirSync(runDir, { recursive: true })
427
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'completed', verdict: 'delivered' }))
428
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
429
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
430
- return text('spawned supervisor ' + id)
431
- },
432
- })
433
- pi.registerTool({
434
- name: 'supervisor_cancel',
435
- async execute() { return text('already completed') },
436
- })
437
- }
438
- `)
439
-
440
- const result = await run(process.execPath, [
441
- '--import', 'tsx', runSupervisorShim, extension, workspace, params,
442
- ], {
443
- timeoutMs: 5_000,
444
- killGraceMs: 500,
445
- env: {
446
- ...process.env,
447
- DRIVER_DEADLINE_MS: '60000',
448
- DRIVER_POLL_MS: '10',
449
- DRIVER_CANCEL_TIMEOUT_MS: '2000',
450
- DRIVER_SETTLEMENT_TIMEOUT_MS: '80',
451
- },
452
- })
453
-
454
- expect(result.code, result.stdout + result.stderr).toBe(4)
455
- expect(result.timedOut).toBe(false)
456
- expect(result.stderr).toMatch(/refusing completed supervisor/)
457
- expect(result.stderr).toMatch(/workers=1, terminal=0, unsettled=1/)
458
- expect(result.stdout).not.toContain('[done] id=')
459
- })
460
-
461
- it.each(['failed', 'cancelled'] as const)(
462
- 'settles a detached worker before returning terminal status $status',
463
- async (status) => {
464
- const dir = await mkdtemp(join(tmpdir(), `swe-driver-${status}-worker-`))
465
- dirs.push(dir)
466
- const workspace = join(dir, 'workspace')
467
- const params = join(dir, 'params.json')
468
- const extension = join(dir, 'fake-extension.mjs')
469
- await mkdir(workspace)
470
- await writeFile(params, '{}')
471
- const workerScript = `const fs=require('node:fs'); const marker=process.argv[1]; const ready=process.argv[2]; process.on('SIGTERM',()=>setTimeout(()=>{fs.writeFileSync(marker,'clean'); process.exit(0)},30)); fs.writeFileSync(ready,'ready'); setInterval(()=>{},1000)`
472
- await writeFile(extension, `
473
- import { spawn } from 'node:child_process'
474
- import { appendFileSync, existsSync, mkdirSync, writeFileSync } from 'node:fs'
475
- import { join } from 'node:path'
476
-
477
- const id = 'sup-6-${status}12'
478
- const text = (value) => ({ content: [{ type: 'text', text: value }] })
479
- let child
480
- export default function fakeExtension(pi) {
481
- pi.registerTool({
482
- name: 'spawn_supervisor',
483
- async execute(_call, _params, _signal, _update, ctx) {
484
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
485
- mkdirSync(runDir, { recursive: true })
486
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id, label: 'root' }) + '\\n')
487
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'spawned', id: id + ':s1', parent: id, label: 'w-0' }) + '\\n')
488
- const readyPath = join(ctx.cwd, 'terminal-worker-ready.txt')
489
- child = spawn(process.execPath, ['-e', ${JSON.stringify(workerScript)}, join(ctx.cwd, 'terminal-worker-cleaned.txt'), readyPath], {
490
- detached: true,
491
- stdio: 'ignore',
492
- })
493
- writeFileSync(join(ctx.cwd, 'terminal-worker.pid'), String(child.pid))
494
- while (!existsSync(readyPath)) await new Promise((resolve) => setTimeout(resolve, 5))
495
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: '${status}', error: 'fixture terminal state' }))
496
- return text('spawned supervisor ' + id)
497
- },
498
- })
499
- pi.registerTool({
500
- name: 'supervisor_cancel',
501
- async execute(_call, _params, _signal, _update, ctx) {
502
- appendFileSync(join(ctx.cwd, 'terminal-cancel-count.txt'), '1\\n')
503
- process.kill(-child.pid, 'SIGTERM')
504
- await new Promise((resolve) => child.once('close', resolve))
505
- const runDir = join(ctx.cwd, '.loops', 'supervisor', id)
506
- appendFileSync(join(runDir, 'journal.jsonl'), JSON.stringify({ kind: 'cancelled', id: id + ':s1' }) + '\\n')
507
- writeFileSync(join(runDir, 'state.json'), JSON.stringify({ status: 'cancelled' }))
508
- return text('terminal worker settled')
509
- },
510
- })
511
- }
512
- `)
513
-
514
- const result = await run(process.execPath, [
515
- '--import', 'tsx', runSupervisorShim, extension, workspace, params,
516
- ], {
517
- timeoutMs: 5_000,
518
- killGraceMs: 500,
519
- env: {
520
- ...process.env,
521
- DRIVER_DEADLINE_MS: '60000',
522
- DRIVER_POLL_MS: '10',
523
- DRIVER_CANCEL_TIMEOUT_MS: '2000',
524
- },
525
- })
526
- const workerPid = Number(await readFile(join(workspace, 'terminal-worker.pid'), 'utf8'))
527
- workerPids.push(workerPid)
528
-
529
- expect(result.code, result.stdout + result.stderr).toBe(1)
530
- expect(result.timedOut).toBe(false)
531
- expect(result.stdout).toContain(`[done] id=sup-6-${status}12 status=${status}`)
532
- expect(result.stdout).toContain('worker settlement: workers=1, terminal=1')
533
- expect(await readFile(join(workspace, 'terminal-cancel-count.txt'), 'utf8')).toBe('1\n')
534
- expect(await readFile(join(workspace, 'terminal-worker-cleaned.txt'), 'utf8')).toBe('clean')
535
- await expect(access(`/proc/${workerPid}`)).rejects.toMatchObject({ code: 'ENOENT' })
536
- },
537
- 15_000,
538
- )
539
- })
@@ -1,140 +0,0 @@
1
- /**
2
- * Gen-5 public/private score split (SOTA adoption #1, AIDE2-style).
3
- *
4
- * Per generation the improvement instances are DETERMINISTICALLY split
5
- * (seeded by runId) into a PUBLIC set — whose scores/evidence proposers and
6
- * the pre-filter may see — and a PRIVATE set that is scored identically but
7
- * NEVER surfaced to proposers. Winner selection stays on the COMBINED
8
- * public+private score (the lib judge already averages every scenario), so
9
- * the split changes only what feedback the search sees, exactly the
10
- * anti-overfitting mechanism: a candidate that games the visible instances
11
- * still has to survive the instances it never saw named.
12
- *
13
- * SMALL-N CAVEAT: with only 6 improvement instances the split is 4 public /
14
- * 2 private. Two private instances give a coarse overfit signal (0, 1/2 or
15
- * 2/2) and a single flake flips it — treat the private sub-score as a
16
- * direction check, not a certification; the pre-registered 6-instance holdout
17
- * remains the only promotion evidence.
18
- *
19
- * RESUME STABILITY: runId embeds a launch timestamp, so a resumed run gets a
20
- * fresh runId. The split is therefore PERSISTED to <outDir>/score-split.json
21
- * on first computation and reloaded verbatim afterwards — a resume can never
22
- * rotate previously-private instances into view.
23
- */
24
-
25
- import { createHash } from 'node:crypto'
26
- import { existsSync } from 'node:fs'
27
- import { mkdir, readFile, writeFile } from 'node:fs/promises'
28
- import { dirname, join } from 'node:path'
29
-
30
- export const SCORE_SPLIT_SCHEMA = 'swe-arena.score-split.v1'
31
- export const SCORE_SPLIT_FILENAME = 'score-split.json'
32
-
33
- export interface ScoreSplitConfig {
34
- /** Number of PUBLIC instances (the rest are private). Must satisfy
35
- * 0 < publicCount < instances.length. */
36
- publicCount: number
37
- }
38
-
39
- export interface ScoreSplit {
40
- schema: typeof SCORE_SPLIT_SCHEMA
41
- /** The runId that seeded the split (provenance; a resumed run keeps it). */
42
- seededBy: string
43
- publicInstances: string[]
44
- privateInstances: string[]
45
- }
46
-
47
- /** Deterministic split: instances are ranked by sha256(runId + ':' + iid) and
48
- * the first `publicCount` become public. Pure — same (runId, instances,
49
- * publicCount) always yields the same split; instance-list ORDER does not
50
- * matter (ranking is content-derived). */
51
- export function splitInstances(runId: string, instances: readonly string[], publicCount: number): ScoreSplit {
52
- if (!Number.isInteger(publicCount) || publicCount <= 0 || publicCount >= instances.length) {
53
- throw new Error(
54
- `score-split: publicCount must be an integer in (0, ${instances.length}), got ${JSON.stringify(publicCount)}`,
55
- )
56
- }
57
- if (new Set(instances).size !== instances.length) {
58
- throw new Error('score-split: duplicate instance ids')
59
- }
60
- const ranked = [...instances]
61
- .map((iid) => ({ iid, rank: createHash('sha256').update(`${runId}:${iid}`).digest('hex') }))
62
- .sort((a, b) => (a.rank < b.rank ? -1 : a.rank > b.rank ? 1 : 0))
63
- const pub = ranked.slice(0, publicCount).map((r) => r.iid)
64
- const priv = ranked.slice(publicCount).map((r) => r.iid)
65
- return {
66
- schema: SCORE_SPLIT_SCHEMA,
67
- seededBy: runId,
68
- publicInstances: [...pub].sort(),
69
- privateInstances: [...priv].sort(),
70
- }
71
- }
72
-
73
- /** Load the persisted split for this outDir, or compute + persist it. The
74
- * persisted file always wins (resume stability); it is validated against the
75
- * current instance set so a config edit mid-outDir fails loud. */
76
- export async function loadOrCreateScoreSplit(args: {
77
- outDir: string
78
- runId: string
79
- instances: readonly string[]
80
- publicCount: number
81
- }): Promise<ScoreSplit> {
82
- const path = join(args.outDir, SCORE_SPLIT_FILENAME)
83
- if (existsSync(path)) {
84
- const split = JSON.parse(await readFile(path, 'utf8')) as ScoreSplit
85
- if (split.schema !== SCORE_SPLIT_SCHEMA) {
86
- throw new Error(`score-split: ${path} has unknown schema ${JSON.stringify(split.schema)}`)
87
- }
88
- const persisted = [...split.publicInstances, ...split.privateInstances].sort()
89
- const expected = [...args.instances].sort()
90
- if (JSON.stringify(persisted) !== JSON.stringify(expected)) {
91
- throw new Error(
92
- `score-split: persisted split at ${path} covers [${persisted.join(', ')}] but the config ` +
93
- `names [${expected.join(', ')}] — refusing to silently re-split; move the outDir or fix the config`,
94
- )
95
- }
96
- if (split.publicInstances.length !== args.publicCount) {
97
- throw new Error(
98
- `score-split: persisted split has ${split.publicInstances.length} public instances, config wants ` +
99
- `${args.publicCount} — refusing to silently re-split`,
100
- )
101
- }
102
- return split
103
- }
104
- const split = splitInstances(args.runId, args.instances, args.publicCount)
105
- await mkdir(dirname(path), { recursive: true })
106
- await writeFile(path, JSON.stringify(split, null, 2))
107
- return split
108
- }
109
-
110
- /** True when `text` mentions any private instance id — the NEVER-SURFACED
111
- * invariant's checkable form. Used by tests and by the evidence-index writer
112
- * as a final guard on proposer-visible text. */
113
- export function leaksPrivateInstance(text: string, split: Pick<ScoreSplit, 'privateInstances'>): string[] {
114
- return split.privateInstances.filter((iid) => text.includes(iid))
115
- }
116
-
117
- /** Filter per-instance evidence records to the PUBLIC set. Identity function
118
- * when no split is configured (split === null). */
119
- export function publicOnly<T>(
120
- records: readonly T[],
121
- iidOf: (record: T) => string,
122
- split: Pick<ScoreSplit, 'privateInstances'> | null,
123
- ): T[] {
124
- if (split === null) return [...records]
125
- const priv = new Set(split.privateInstances)
126
- return records.filter((r) => !priv.has(iidOf(r)))
127
- }
128
-
129
- /** Public/private sub-scores for one candidate, from per-instance AND-verdicts
130
- * (fail-closed: an instance with no conclusive full-reps verdict counts 0). */
131
- export function subScores(
132
- verdicts: Record<string, boolean>,
133
- split: Pick<ScoreSplit, 'publicInstances' | 'privateInstances'>,
134
- ): { publicResolvedCount: number; privateResolvedCount: number } {
135
- const count = (iids: readonly string[]): number => iids.filter((iid) => verdicts[iid] === true).length
136
- return {
137
- publicResolvedCount: count(split.publicInstances),
138
- privateResolvedCount: count(split.privateInstances),
139
- }
140
- }