@tangle-network/agent-bench 0.11.3 → 0.13.2

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 (170) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/HARNESS.md +6 -2
  3. package/README.md +1 -4
  4. package/package.json +5 -5
  5. package/scripts/run-package-tests.mjs +2 -2
  6. package/src/quant-arena/README.md +0 -144
  7. package/src/quant-arena/backtest.test.mts +0 -135
  8. package/src/quant-arena/backtest.ts +0 -218
  9. package/src/quant-arena/data.test.mts +0 -44
  10. package/src/quant-arena/data.ts +0 -141
  11. package/src/quant-arena/driver.test.mts +0 -253
  12. package/src/quant-arena/driver.ts +0 -219
  13. package/src/quant-arena/fixtures/data/PROVENANCE.md +0 -26
  14. package/src/quant-arena/fixtures/data/holdout/IDX.csv +0 -523
  15. package/src/quant-arena/fixtures/data/holdout/S01.csv +0 -523
  16. package/src/quant-arena/fixtures/data/holdout/S02.csv +0 -523
  17. package/src/quant-arena/fixtures/data/holdout/S03.csv +0 -523
  18. package/src/quant-arena/fixtures/data/holdout/S04.csv +0 -523
  19. package/src/quant-arena/fixtures/data/holdout/S05.csv +0 -523
  20. package/src/quant-arena/fixtures/data/holdout/S06.csv +0 -523
  21. package/src/quant-arena/fixtures/data/holdout/S07.csv +0 -523
  22. package/src/quant-arena/fixtures/data/holdout/S08.csv +0 -523
  23. package/src/quant-arena/fixtures/data/holdout/S09.csv +0 -523
  24. package/src/quant-arena/fixtures/data/holdout/S10.csv +0 -523
  25. package/src/quant-arena/fixtures/data/insample/IDX.csv +0 -2087
  26. package/src/quant-arena/fixtures/data/insample/S01.csv +0 -2087
  27. package/src/quant-arena/fixtures/data/insample/S02.csv +0 -2087
  28. package/src/quant-arena/fixtures/data/insample/S03.csv +0 -2087
  29. package/src/quant-arena/fixtures/data/insample/S04.csv +0 -2087
  30. package/src/quant-arena/fixtures/data/insample/S05.csv +0 -2087
  31. package/src/quant-arena/fixtures/data/insample/S06.csv +0 -2087
  32. package/src/quant-arena/fixtures/data/insample/S07.csv +0 -2087
  33. package/src/quant-arena/fixtures/data/insample/S08.csv +0 -2087
  34. package/src/quant-arena/fixtures/data/insample/S09.csv +0 -2087
  35. package/src/quant-arena/fixtures/data/insample/S10.csv +0 -2087
  36. package/src/quant-arena/fixtures/demo-campaign/cost-ledger.jsonl +0 -16
  37. package/src/quant-arena/fixtures/demo-campaign/notebook.jsonl +0 -5
  38. package/src/quant-arena/fixtures/demo-campaign/rollout-manifest.json +0 -171
  39. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-001-default-author/strategy.ts +0 -119
  40. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-002-default-author/strategy.ts +0 -119
  41. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-003-quant-researcher/strategy.ts +0 -105
  42. package/src/quant-arena/fixtures/demo-campaign/strategies/cand-004-quant-researcher/strategy.ts +0 -102
  43. package/src/quant-arena/fixtures/demo-campaign-v2/cost-ledger.jsonl +0 -4
  44. package/src/quant-arena/fixtures/demo-campaign-v2/notebook.jsonl +0 -2
  45. package/src/quant-arena/fixtures/demo-campaign-v2/rollout-manifest.json +0 -84
  46. package/src/quant-arena/fixtures/demo-campaign-v2/strategies/cand-001-quant-researcher/strategy.ts +0 -117
  47. package/src/quant-arena/holdout-certify.mts +0 -206
  48. package/src/quant-arena/holdout-certify.test.mts +0 -82
  49. package/src/quant-arena/leak-audit.test.mts +0 -79
  50. package/src/quant-arena/leak-audit.ts +0 -95
  51. package/src/quant-arena/make-fixtures.mts +0 -161
  52. package/src/quant-arena/multiplicity.test.mts +0 -68
  53. package/src/quant-arena/multiplicity.ts +0 -87
  54. package/src/quant-arena/nautilus-certify.ts +0 -31
  55. package/src/quant-arena/oms.ts +0 -90
  56. package/src/quant-arena/profiles/quant-researcher.profile.json +0 -12
  57. package/src/quant-arena/python/pyproject.toml +0 -8
  58. package/src/quant-arena/python/uv.lock +0 -1297
  59. package/src/quant-arena/python/vbt-worker.py +0 -192
  60. package/src/quant-arena/quant-loop.mts +0 -840
  61. package/src/quant-arena/quant-loop.test.mts +0 -75
  62. package/src/quant-arena/strategies/buy-hold-index/strategy.ts +0 -11
  63. package/src/quant-arena/strategies/equal-weight/strategy.ts +0 -20
  64. package/src/quant-arena/strategies/sma-crossover/strategy.ts +0 -42
  65. package/src/quant-arena/types.ts +0 -133
  66. package/src/quant-arena/vbt-client.ts +0 -321
  67. package/src/quant-arena/vbt-parity.test.mts +0 -183
  68. package/src/quant-arena/windows.test.mts +0 -45
  69. package/src/quant-arena/windows.ts +0 -54
  70. package/src/rollout-ledger/backfill-swe-arena.mts +0 -610
  71. package/src/rollout-ledger/backfill-swe-arena.test.mts +0 -347
  72. package/src/rollout-ledger/settle-capture.mts +0 -448
  73. package/src/rollout-ledger/settle-capture.test.mts +0 -270
  74. package/src/swe-arena/activation.mts +0 -225
  75. package/src/swe-arena/activation.test.mts +0 -300
  76. package/src/swe-arena/analyze.ts +0 -211
  77. package/src/swe-arena/arms.ts +0 -862
  78. package/src/swe-arena/bootstrap-meta.mts +0 -188
  79. package/src/swe-arena/bootstrap-meta.test.mts +0 -51
  80. package/src/swe-arena/briefing.mts +0 -217
  81. package/src/swe-arena/briefing.test.mts +0 -179
  82. package/src/swe-arena/calibrate.ts +0 -217
  83. package/src/swe-arena/capabilities.mts +0 -76
  84. package/src/swe-arena/capabilities.test.mts +0 -57
  85. package/src/swe-arena/capacity.ts +0 -198
  86. package/src/swe-arena/cell-evidence.mts +0 -437
  87. package/src/swe-arena/cell-evidence.test.mts +0 -248
  88. package/src/swe-arena/diagnosis-ensemble.test.mts +0 -210
  89. package/src/swe-arena/diagnosis-ensemble.ts +0 -523
  90. package/src/swe-arena/execution.test.mts +0 -1171
  91. package/src/swe-arena/factory-command-container.ts +0 -284
  92. package/src/swe-arena/factory-judge-child.mts +0 -228
  93. package/src/swe-arena/factory.test.mts +0 -645
  94. package/src/swe-arena/fixtures/analyze.py +0 -80
  95. package/src/swe-arena/fixtures/excludes.txt +0 -8
  96. package/src/swe-arena/fixtures/factory/agent-eval-309/calibration.md +0 -51
  97. package/src/swe-arena/fixtures/factory/agent-eval-309/manifest.json +0 -29
  98. package/src/swe-arena/fixtures/factory/agent-eval-309/spec.md +0 -64
  99. package/src/swe-arena/fixtures/factory/agent-runtime-232/calibration.md +0 -48
  100. package/src/swe-arena/fixtures/factory/agent-runtime-232/manifest.json +0 -29
  101. package/src/swe-arena/fixtures/factory/agent-runtime-232/spec.md +0 -48
  102. package/src/swe-arena/fixtures/factory/loops-28/calibration.md +0 -47
  103. package/src/swe-arena/fixtures/factory/loops-28/manifest.json +0 -30
  104. package/src/swe-arena/fixtures/factory/loops-28/spec.md +0 -50
  105. package/src/swe-arena/fixtures/gen1-salvage/README.md +0 -45
  106. package/src/swe-arena/fixtures/gen1-salvage/cand0-e6d7361.diff +0 -116
  107. package/src/swe-arena/fixtures/gen1-salvage/cand1-76a8590.diff +0 -293
  108. package/src/swe-arena/fixtures/holdout-preregister.log +0 -12
  109. package/src/swe-arena/fixtures/holdout.json +0 -44
  110. package/src/swe-arena/fixtures/instances.json +0 -146
  111. package/src/swe-arena/fixtures/ledger.jsonl +0 -12
  112. package/src/swe-arena/fixtures/patches/pallets__flask-5014.solo.patch +0 -36
  113. package/src/swe-arena/fixtures/patches/pydata__xarray-4687.sup.patch +0 -33
  114. package/src/swe-arena/fixtures/rejudge.jsonl +0 -15
  115. package/src/swe-arena/fixtures/rematch.jsonl +0 -3
  116. package/src/swe-arena/fixtures/rematch2.jsonl +0 -3
  117. package/src/swe-arena/fixtures/rematch3.jsonl +0 -3
  118. package/src/swe-arena/fixtures/run-report/README.md +0 -43
  119. package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.json +0 -173
  120. package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.md +0 -100
  121. package/src/swe-arena/fixtures/run-report/gen3-rollup.json +0 -551
  122. package/src/swe-arena/fixtures/run-report/gen3-rollup.md +0 -64
  123. package/src/swe-arena/fixtures/sup-journal-true.json +0 -19
  124. package/src/swe-arena/fixtures/verify/astropy__astropy-13033.sh +0 -48
  125. package/src/swe-arena/fixtures/verify/django__django-11532.sh +0 -50
  126. package/src/swe-arena/fixtures/verify/matplotlib__matplotlib-20826.sh +0 -76
  127. package/src/swe-arena/fixtures/verify/pydata__xarray-4687.sh +0 -44
  128. package/src/swe-arena/fixtures/verify/pytest-dev__pytest-6197.sh +0 -32
  129. package/src/swe-arena/fixtures/verify/sphinx-doc__sphinx-9658.sh +0 -51
  130. package/src/swe-arena/fixtures/worker-tokens.json +0 -42
  131. package/src/swe-arena/fixtures.ts +0 -237
  132. package/src/swe-arena/gepa-seat.mts +0 -886
  133. package/src/swe-arena/gepa-seat.test.mts +0 -1136
  134. package/src/swe-arena/holdout-certify.mts +0 -408
  135. package/src/swe-arena/holdout-certify.test.mts +0 -160
  136. package/src/swe-arena/implementation-ref.test.mts +0 -64
  137. package/src/swe-arena/implementation-ref.ts +0 -62
  138. package/src/swe-arena/judge-child.mts +0 -37
  139. package/src/swe-arena/ledger-orphans.mts +0 -77
  140. package/src/swe-arena/ledger-orphans.test.mts +0 -149
  141. package/src/swe-arena/manifest.mts +0 -293
  142. package/src/swe-arena/manifest.test.mts +0 -169
  143. package/src/swe-arena/materialize.ts +0 -142
  144. package/src/swe-arena/outer-loop.mts +0 -2854
  145. package/src/swe-arena/outer-loop.test.mts +0 -714
  146. package/src/swe-arena/parity.test.mts +0 -87
  147. package/src/swe-arena/premeasured-from-cells.mts +0 -296
  148. package/src/swe-arena/premeasured-from-cells.test.mts +0 -201
  149. package/src/swe-arena/proc.test.mts +0 -172
  150. package/src/swe-arena/proc.ts +0 -260
  151. package/src/swe-arena/profiles/deepseek-author.profile.json +0 -12
  152. package/src/swe-arena/profiles/default-author.profile.json +0 -12
  153. package/src/swe-arena/proposer-fanout.mts +0 -736
  154. package/src/swe-arena/proposer-fanout.test.mts +0 -660
  155. package/src/swe-arena/proposer-provenance.mts +0 -176
  156. package/src/swe-arena/proposer-provenance.test.mts +0 -106
  157. package/src/swe-arena/reconcile.ts +0 -0
  158. package/src/swe-arena/replay.mts +0 -183
  159. package/src/swe-arena/replay.test.mts +0 -300
  160. package/src/swe-arena/run-experiment.mts +0 -729
  161. package/src/swe-arena/run-report.mts +0 -75
  162. package/src/swe-arena/run-supervisor.mjs +0 -297
  163. package/src/swe-arena/run-supervisor.test.mts +0 -539
  164. package/src/swe-arena/score-split.mts +0 -140
  165. package/src/swe-arena/score-split.test.mts +0 -123
  166. package/src/swe-arena/scratch-worktree-serialization.test.mts +0 -72
  167. package/src/swe-arena/scratch-worktree.test.mts +0 -56
  168. package/src/swe-arena/scratch-worktree.ts +0 -64
  169. package/src/swe-arena/serialized-judge.ts +0 -414
  170. 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
- }