@tangle-network/agent-bench 0.3.7 → 0.3.8
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.
- package/CHANGELOG.md +4 -0
- package/HARNESS.md +43 -0
- package/dist/adapters.js +23 -23
- package/dist/benchmarks/_harness.d.ts +1 -1
- package/dist/benchmarks/_harness.js +1 -1
- package/dist/benchmarks/aec-bench.js +2 -2
- package/dist/benchmarks/agentbench.js +2 -2
- package/dist/benchmarks/appworld.js +2 -2
- package/dist/benchmarks/bfcl.js +2 -2
- package/dist/benchmarks/commit0.js +2 -2
- package/dist/benchmarks/crag.js +2 -2
- package/dist/benchmarks/dabstep.js +2 -2
- package/dist/benchmarks/enterpriseops-gym.js +2 -2
- package/dist/benchmarks/finresearchbench.js +2 -2
- package/dist/benchmarks/nomiracl.js +2 -2
- package/dist/benchmarks/open-rag-bench.js +2 -2
- package/dist/benchmarks/programbench.js +2 -2
- package/dist/benchmarks/ragbench.js +2 -2
- package/dist/benchmarks/swe-bench.js +2 -2
- package/dist/benchmarks/t2-ragbench.js +2 -2
- package/dist/benchmarks/tau-bench-shared.js +2 -2
- package/dist/benchmarks/tau2-bench.js +3 -3
- package/dist/benchmarks/tau3-banking.js +3 -3
- package/dist/benchmarks/terminal-bench.js +2 -2
- package/dist/benchmarks/toollm.js +2 -2
- package/dist/benchmarks/webarena-verified.js +2 -2
- package/dist/{chunk-CKUVRZ2T.js → chunk-3U5TXJZS.js} +2 -2
- package/dist/{chunk-YCGY7UIZ.js → chunk-7GRVHU22.js} +2 -2
- package/dist/{chunk-Z7ML6L77.js → chunk-HWST3SED.js} +2 -2
- package/dist/{chunk-SYDW647C.js → chunk-IA2FBTWC.js} +2 -2
- package/dist/{chunk-R67DFVLO.js → chunk-IFVINJ4B.js} +2 -2
- package/dist/{chunk-R36V2VP7.js → chunk-IZ5M6OAC.js} +2 -2
- package/dist/{chunk-ODT47UAY.js → chunk-K3BQGZCT.js} +2 -2
- package/dist/{chunk-IFAV6KEM.js → chunk-KP5KD6EN.js} +2 -2
- package/dist/{chunk-ZEWMTR5M.js → chunk-MQMRLGOG.js} +2 -2
- package/dist/{chunk-TSWPNOYM.js → chunk-NQG5XDSB.js} +2 -2
- package/dist/{chunk-7WSD27QQ.js → chunk-PB64GYIG.js} +2 -2
- package/dist/{chunk-HBSWHQNJ.js → chunk-RCYQEFNX.js} +3 -3
- package/dist/{chunk-J3KDJNX2.js → chunk-RH5F53JT.js} +2 -2
- package/dist/{chunk-UAIOHCUK.js → chunk-SFLA7OH3.js} +3 -3
- package/dist/{chunk-Y6O2OCUO.js → chunk-SHM6MRRF.js} +2 -2
- package/dist/{chunk-KDIKRJGB.js → chunk-SHYIRB7I.js} +2 -2
- package/dist/{chunk-HHXFIHXC.js → chunk-SVR2LKYI.js} +2 -2
- package/dist/{chunk-PWQVGAJB.js → chunk-V7AEBY6U.js} +21 -21
- package/dist/{chunk-LRRD7NAG.js → chunk-WSKWVEQB.js} +18 -2
- package/dist/chunk-WSKWVEQB.js.map +1 -0
- package/dist/{chunk-2PVVP7GN.js → chunk-XKEFIFIC.js} +2 -2
- package/dist/{chunk-JRWWGMK7.js → chunk-XYA4XSNU.js} +2 -2
- package/dist/{chunk-X5YKXC6V.js → chunk-YSMEKBTD.js} +2 -2
- package/dist/{chunk-2XU6OGEN.js → chunk-Z4TZ76N7.js} +2 -2
- package/dist/index.js +23 -23
- package/package.json +4 -4
- package/scripts/verify-packed-consumer.mjs +1 -1
- package/src/benchmarks/_harness.ts +20 -2
- package/src/benchmarks/humaneval.test.mts +2 -2
- package/src/quant-arena/README.md +144 -0
- package/src/quant-arena/backtest.test.mts +135 -0
- package/src/quant-arena/backtest.ts +218 -0
- package/src/quant-arena/data.test.mts +44 -0
- package/src/quant-arena/data.ts +141 -0
- package/src/quant-arena/driver.test.mts +253 -0
- package/src/quant-arena/driver.ts +219 -0
- package/src/quant-arena/fixtures/data/PROVENANCE.md +26 -0
- package/src/quant-arena/fixtures/data/holdout/IDX.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S01.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S02.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S03.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S04.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S05.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S06.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S07.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S08.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S09.csv +523 -0
- package/src/quant-arena/fixtures/data/holdout/S10.csv +523 -0
- package/src/quant-arena/fixtures/data/insample/IDX.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S01.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S02.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S03.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S04.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S05.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S06.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S07.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S08.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S09.csv +2087 -0
- package/src/quant-arena/fixtures/data/insample/S10.csv +2087 -0
- package/src/quant-arena/fixtures/demo-campaign/cost-ledger.jsonl +16 -0
- package/src/quant-arena/fixtures/demo-campaign/notebook.jsonl +5 -0
- package/src/quant-arena/fixtures/demo-campaign/rollout-manifest.json +171 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-001-default-author/strategy.ts +119 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-002-default-author/strategy.ts +119 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-003-quant-researcher/strategy.ts +105 -0
- package/src/quant-arena/fixtures/demo-campaign/strategies/cand-004-quant-researcher/strategy.ts +102 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/cost-ledger.jsonl +4 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/notebook.jsonl +2 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/rollout-manifest.json +84 -0
- package/src/quant-arena/fixtures/demo-campaign-v2/strategies/cand-001-quant-researcher/strategy.ts +117 -0
- package/src/quant-arena/holdout-certify.mts +206 -0
- package/src/quant-arena/holdout-certify.test.mts +82 -0
- package/src/quant-arena/leak-audit.test.mts +79 -0
- package/src/quant-arena/leak-audit.ts +95 -0
- package/src/quant-arena/make-fixtures.mts +161 -0
- package/src/quant-arena/multiplicity.test.mts +68 -0
- package/src/quant-arena/multiplicity.ts +87 -0
- package/src/quant-arena/nautilus-certify.ts +31 -0
- package/src/quant-arena/oms.ts +90 -0
- package/src/quant-arena/profiles/quant-researcher.profile.json +7 -0
- package/src/quant-arena/python/pyproject.toml +8 -0
- package/src/quant-arena/python/uv.lock +1297 -0
- package/src/quant-arena/python/vbt-worker.py +192 -0
- package/src/quant-arena/quant-loop.mts +813 -0
- package/src/quant-arena/quant-loop.test.mts +75 -0
- package/src/quant-arena/strategies/buy-hold-index/strategy.ts +11 -0
- package/src/quant-arena/strategies/equal-weight/strategy.ts +20 -0
- package/src/quant-arena/strategies/sma-crossover/strategy.ts +42 -0
- package/src/quant-arena/types.ts +133 -0
- package/src/quant-arena/vbt-client.ts +321 -0
- package/src/quant-arena/vbt-parity.test.mts +183 -0
- package/src/quant-arena/windows.test.mts +45 -0
- package/src/quant-arena/windows.ts +54 -0
- package/src/rollout-ledger/backfill-swe-arena.mts +606 -0
- package/src/rollout-ledger/backfill-swe-arena.test.mts +338 -0
- package/src/rollout-ledger/settle-capture.mts +442 -0
- package/src/rollout-ledger/settle-capture.test.mts +270 -0
- package/src/swe-arena/activation.mts +228 -0
- package/src/swe-arena/activation.test.mts +303 -0
- package/src/swe-arena/arms.ts +30 -14
- package/src/swe-arena/briefing.mts +217 -0
- package/src/swe-arena/briefing.test.mts +178 -0
- package/src/swe-arena/calibrate.ts +102 -1
- package/src/swe-arena/cell-evidence.mts +41 -9
- package/src/swe-arena/factory-command-container.ts +284 -0
- package/src/swe-arena/factory-judge-child.mts +228 -0
- package/src/swe-arena/factory.test.mts +643 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/calibration.md +51 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/manifest.json +29 -0
- package/src/swe-arena/fixtures/factory/agent-eval-309/spec.md +64 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/calibration.md +48 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/manifest.json +29 -0
- package/src/swe-arena/fixtures/factory/agent-runtime-232/spec.md +48 -0
- package/src/swe-arena/fixtures/factory/loops-28/calibration.md +47 -0
- package/src/swe-arena/fixtures/factory/loops-28/manifest.json +30 -0
- package/src/swe-arena/fixtures/factory/loops-28/spec.md +50 -0
- package/src/swe-arena/fixtures/run-report/README.md +43 -0
- package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.json +173 -0
- package/src/swe-arena/fixtures/run-report/factory-agent-eval-309-FSUP0.md +100 -0
- package/src/swe-arena/fixtures/run-report/gen3-rollup.json +551 -0
- package/src/swe-arena/fixtures/run-report/gen3-rollup.md +64 -0
- package/src/swe-arena/fixtures.ts +135 -2
- package/src/swe-arena/gepa-seat.mts +583 -0
- package/src/swe-arena/gepa-seat.test.mts +635 -0
- package/src/swe-arena/ledger-orphans.mts +77 -0
- package/src/swe-arena/ledger-orphans.test.mts +147 -0
- package/src/swe-arena/lineage-record.mts +164 -0
- package/src/swe-arena/lineage-record.test.mts +115 -0
- package/src/swe-arena/outer-loop.mts +677 -27
- package/src/swe-arena/premeasured-from-cells.mts +281 -0
- package/src/swe-arena/premeasured-from-cells.test.mts +180 -0
- package/src/swe-arena/proposer-fanout.mts +295 -14
- package/src/swe-arena/proposer-fanout.test.mts +247 -0
- package/src/swe-arena/proposer-provenance.mts +177 -0
- package/src/swe-arena/proposer-provenance.test.mts +106 -0
- package/src/swe-arena/run-experiment.mts +367 -1
- package/src/swe-arena/run-report.mts +75 -0
- package/src/swe-arena/run-supervisor.test.mts +5 -3
- package/src/swe-arena/score-split.mts +140 -0
- package/src/swe-arena/score-split.test.mts +123 -0
- package/src/swe-arena/types.ts +52 -0
- package/src/swe-structural.mts +245 -837
- package/dist/chunk-LRRD7NAG.js.map +0 -1
- /package/dist/{chunk-CKUVRZ2T.js.map → chunk-3U5TXJZS.js.map} +0 -0
- /package/dist/{chunk-YCGY7UIZ.js.map → chunk-7GRVHU22.js.map} +0 -0
- /package/dist/{chunk-Z7ML6L77.js.map → chunk-HWST3SED.js.map} +0 -0
- /package/dist/{chunk-SYDW647C.js.map → chunk-IA2FBTWC.js.map} +0 -0
- /package/dist/{chunk-R67DFVLO.js.map → chunk-IFVINJ4B.js.map} +0 -0
- /package/dist/{chunk-R36V2VP7.js.map → chunk-IZ5M6OAC.js.map} +0 -0
- /package/dist/{chunk-ODT47UAY.js.map → chunk-K3BQGZCT.js.map} +0 -0
- /package/dist/{chunk-IFAV6KEM.js.map → chunk-KP5KD6EN.js.map} +0 -0
- /package/dist/{chunk-ZEWMTR5M.js.map → chunk-MQMRLGOG.js.map} +0 -0
- /package/dist/{chunk-TSWPNOYM.js.map → chunk-NQG5XDSB.js.map} +0 -0
- /package/dist/{chunk-7WSD27QQ.js.map → chunk-PB64GYIG.js.map} +0 -0
- /package/dist/{chunk-HBSWHQNJ.js.map → chunk-RCYQEFNX.js.map} +0 -0
- /package/dist/{chunk-J3KDJNX2.js.map → chunk-RH5F53JT.js.map} +0 -0
- /package/dist/{chunk-UAIOHCUK.js.map → chunk-SFLA7OH3.js.map} +0 -0
- /package/dist/{chunk-Y6O2OCUO.js.map → chunk-SHM6MRRF.js.map} +0 -0
- /package/dist/{chunk-KDIKRJGB.js.map → chunk-SHYIRB7I.js.map} +0 -0
- /package/dist/{chunk-HHXFIHXC.js.map → chunk-SVR2LKYI.js.map} +0 -0
- /package/dist/{chunk-PWQVGAJB.js.map → chunk-V7AEBY6U.js.map} +0 -0
- /package/dist/{chunk-2PVVP7GN.js.map → chunk-XKEFIFIC.js.map} +0 -0
- /package/dist/{chunk-JRWWGMK7.js.map → chunk-XYA4XSNU.js.map} +0 -0
- /package/dist/{chunk-X5YKXC6V.js.map → chunk-YSMEKBTD.js.map} +0 -0
- /package/dist/{chunk-2XU6OGEN.js.map → chunk-Z4TZ76N7.js.map} +0 -0
package/src/swe-structural.mts
CHANGED
|
@@ -1,35 +1,58 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Stage 1 of the SWE-bench frontier push — the STRUCTURAL-SYSTEM mechanics pilot
|
|
3
|
+
* (contract: supervisor-lab/docs/results/PREREG-swe-frontier.md, Stage 1; execution design per the
|
|
4
|
+
* AMENDMENT + CLOSURE: image substrate everywhere, per-instance execution canary asserted at open).
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
6
|
+
* ARM=system, per instance:
|
|
7
|
+
* 1. canary — the Stage-0 execution canary on the image substrate (gold applied in-container,
|
|
8
|
+
* `import <pkg>` must resolve into /testbed). Gold is used strictly script-side, never near a
|
|
9
|
+
* model message (transport-level leak guard, below).
|
|
10
|
+
* 2. repro — the instance's Stage-0 valid+sound repro script is REUSED (REPRO_MANIFEST; source
|
|
11
|
+
* recorded per row) and re-verified on THIS substrate with zero model calls: validity (nonzero
|
|
12
|
+
* exit on the unpatched /testbed) + soundness (exit 0 under gold, in-container). A script that
|
|
13
|
+
* fails re-verification degrades to repro=none — recorded, never silent. Instances with no
|
|
14
|
+
* Stage-0 repro run repro=none and measure the no-signal path (selection = blind-first).
|
|
15
|
+
* 3. k=4 independent patch attempts — each the swe-emit-patch protocol verbatim (SWE_SEED_PROMPT,
|
|
16
|
+
* list/read/edit tools on a fresh host clone, runAgentic refine budget=1, glm-5.2 temp 0.8;
|
|
17
|
+
* the candidate is the workspace `git diff`, captured from inside score()).
|
|
18
|
+
* 4. selection — each candidate is scored in-image: `git apply` the candidate to the container's
|
|
19
|
+
* /testbed (writable layer, --rm discards), run the repro. Argmax: repro-pass first, then
|
|
20
|
+
* crash-lowest (fail < timeout < apply-fail < empty), first index breaks ties. With repro=none
|
|
21
|
+
* the selection degrades to blind-first (first non-empty candidate).
|
|
22
|
+
* 5. ≤2 guarded repair rounds on the best — the repro failure output (+ the current diff, both
|
|
23
|
+
* model-visible by construction) steers a fresh emit-patch attempt on a workspace with the
|
|
24
|
+
* best diff pre-applied; the combined diff must STRICTLY improve the repro outcome to displace
|
|
25
|
+
* (a repro-pass can never be displaced by a repro-fail — repair only runs while failing).
|
|
26
|
+
* 6. final diff locked → Phase B.
|
|
27
|
+
* ARM=solo: one emit-patch attempt (temp 0.7 — runShot's July-protocol default), same environment,
|
|
28
|
+
* same 23 instances. The honest single-attempt reference.
|
|
9
29
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
30
|
+
* Phase B (hidden judge): adapter.judge — the OFFICIAL swebench harness — runs serialized
|
|
31
|
+
* (max_workers 1) over the locked final diffs only, strictly AFTER every arm decision. Judge
|
|
32
|
+
* separation is asserted at the transport chokepoint: every outbound request's system/user messages
|
|
33
|
+
* (the strings WE author) are checked against gold-patch + test_patch content marks and refused on
|
|
34
|
+
* contact. Assistant/tool messages are exempt by construction (the model's own text and reads of
|
|
35
|
+
* the base tree — a model that independently authors the gold line is a success, not a leak).
|
|
12
36
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
37
|
+
* cd ~/company/devops/secrets && dotenvx run -f agent-state.env -- bash -c \
|
|
38
|
+
* 'cd ~/code/agent-runtime-swe && ARM=system REPRO_MANIFEST=/path/manifest.json \
|
|
39
|
+
* OUT=/path/swe-stage1-system.jsonl node_modules/.bin/tsx bench/src/swe-structural.mts'
|
|
40
|
+
*
|
|
41
|
+
* Env: ARM=system|solo (required), ZAI_API_KEY (required), ZAI_BASE, MODEL=glm-5.2,
|
|
42
|
+
* MAX_TOKENS=12000, K=4, REPAIRS=2, TEMP=0.8 (system attempts+repairs), SOLO_TEMP=0.7,
|
|
43
|
+
* INNER_TURNS=40, CONC=2, REPRO_TIMEOUT=120 (s), LLM_TIMEOUT_MS=480000, IDS=comma-list,
|
|
44
|
+
* OUT=jsonl path, REPRO_MANIFEST=json path (system), SKIP_JUDGE=1 (Phase A only),
|
|
45
|
+
* PRICE_IN/PRICE_OUT (USD per Mtok for the cost table; defaults are the assumed zai list rate).
|
|
46
|
+
* Rows are incremental (OUT.phaseA then OUT) and both phases resume by instance id.
|
|
16
47
|
*/
|
|
17
48
|
import { execFile } from 'node:child_process'
|
|
18
|
-
import { appendFileSync, existsSync,
|
|
19
|
-
import { join
|
|
20
|
-
import { fileURLToPath } from 'node:url'
|
|
49
|
+
import { appendFileSync, existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
50
|
+
import { join } from 'node:path'
|
|
21
51
|
import { promisify } from 'node:util'
|
|
22
52
|
import type { AgenticSurface, AgenticTask, ArtifactHandle, SurfaceScore } from '@tangle-network/agent-runtime/loops'
|
|
23
53
|
import { refine, runAgentic } from '@tangle-network/agent-runtime/loops'
|
|
24
54
|
import type { BenchTask } from './benchmarks/types'
|
|
25
|
-
import {
|
|
26
|
-
createSweBenchEnvironment,
|
|
27
|
-
resolveImageForMetadata,
|
|
28
|
-
resolveSweBenchScorerVersion,
|
|
29
|
-
SWE_RUN_TOOL_CONFIG,
|
|
30
|
-
SWE_SEED_PROMPT_WITH_RUN,
|
|
31
|
-
type SweImageIdentity,
|
|
32
|
-
} from './swe-bench-env'
|
|
55
|
+
import { createSweBenchEnvironment, resolveImageForMetadata, SWE_SEED_PROMPT } from './swe-bench-env'
|
|
33
56
|
import {
|
|
34
57
|
APPLY_SENTINEL,
|
|
35
58
|
assertNoHiddenLeak,
|
|
@@ -40,98 +63,30 @@ import {
|
|
|
40
63
|
tail,
|
|
41
64
|
zaiChatRaw,
|
|
42
65
|
} from './swe-jail'
|
|
43
|
-
import {
|
|
44
|
-
type ExperimentArm,
|
|
45
|
-
type ExperimentArmPreset,
|
|
46
|
-
assertExactCompletedWorkerSessions,
|
|
47
|
-
continuationDisposition,
|
|
48
|
-
continuationStateNotice,
|
|
49
|
-
preferLaterCandidate,
|
|
50
|
-
resolveExperimentArm,
|
|
51
|
-
resolveExperimentTemperature,
|
|
52
|
-
shouldAcceptContinuation,
|
|
53
|
-
shouldRunContinuation,
|
|
54
|
-
} from './swe-structural-policy'
|
|
55
|
-
import {
|
|
56
|
-
assertFingerprintsEqual,
|
|
57
|
-
createExecutionReceipt,
|
|
58
|
-
createFingerprints,
|
|
59
|
-
diffChanged,
|
|
60
|
-
diffFingerprint,
|
|
61
|
-
fingerprint,
|
|
62
|
-
runtimeImplementationFingerprint,
|
|
63
|
-
type ExecutionReceipt,
|
|
64
|
-
type Fingerprints,
|
|
65
|
-
type SharedExecutionReceipt,
|
|
66
|
-
} from './swe-structural-provenance'
|
|
67
|
-
import {
|
|
68
|
-
assertCompleteTaskSet,
|
|
69
|
-
assertDistinctArtifactPaths,
|
|
70
|
-
assertJudgeCompletionMatchesInput,
|
|
71
|
-
assertJudgeResumeFingerprints,
|
|
72
|
-
assertPairedExecutionFingerprint,
|
|
73
|
-
assertPairedFingerprints,
|
|
74
|
-
completeJudgeScore,
|
|
75
|
-
} from './swe-structural-judge-policy'
|
|
76
66
|
|
|
77
67
|
const exec = promisify(execFile)
|
|
78
|
-
const TEMPERATURE = resolveExperimentTemperature(process.env)
|
|
79
|
-
|
|
80
|
-
function sourceTreeReceipt(
|
|
81
|
-
rootUrl: URL,
|
|
82
|
-
label: string,
|
|
83
|
-
include: (path: string) => boolean,
|
|
84
|
-
): Array<{ name: string; content: string }> {
|
|
85
|
-
const root = fileURLToPath(rootUrl)
|
|
86
|
-
const receipt: Array<{ name: string; content: string }> = []
|
|
87
|
-
const visit = (dir: string): void => {
|
|
88
|
-
for (const entry of readdirSync(dir, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
|
|
89
|
-
const path = join(dir, entry.name)
|
|
90
|
-
if (entry.isDirectory()) visit(path)
|
|
91
|
-
else if (entry.isFile() && include(path)) {
|
|
92
|
-
receipt.push({ name: `${label}/${relative(root, path)}`, content: readFileSync(path, 'utf8') })
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
visit(root)
|
|
97
|
-
return receipt
|
|
98
|
-
}
|
|
99
68
|
|
|
100
69
|
// ---------- config ----------
|
|
101
70
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (MODE_INPUT !== 'generate' && MODE_INPUT !== 'judge-only') {
|
|
105
|
-
throw new Error(`MODE must be generate|judge-only, got "${MODE_INPUT}"`)
|
|
106
|
-
}
|
|
107
|
-
const MODE: Mode = MODE_INPUT
|
|
108
|
-
const OFFICIAL_SCORER_CACHE_LEVEL = 'instance' as const
|
|
109
|
-
if (
|
|
110
|
-
MODE === 'judge-only' &&
|
|
111
|
-
process.env.SWEBENCH_CACHE_LEVEL !== undefined &&
|
|
112
|
-
process.env.SWEBENCH_CACHE_LEVEL !== OFFICIAL_SCORER_CACHE_LEVEL
|
|
113
|
-
) {
|
|
114
|
-
throw new Error('MODE=judge-only requires SWEBENCH_CACHE_LEVEL=instance')
|
|
115
|
-
}
|
|
116
|
-
if (MODE === 'judge-only') process.env.SWEBENCH_CACHE_LEVEL = OFFICIAL_SCORER_CACHE_LEVEL
|
|
117
|
-
for (const legacy of ['ARM', 'ARM_NAME', 'K', 'REPAIRS', 'FORCE_TWO_SESSIONS', 'SOLO_TEMP', 'SKIP_JUDGE']) {
|
|
118
|
-
if (process.env[legacy] !== undefined) {
|
|
119
|
-
throw new Error(`${legacy} is not supported; use typed EXPERIMENT_ARM plus MODE=generate|judge-only`)
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const ARM_PRESET: ExperimentArmPreset | null = MODE === 'generate'
|
|
123
|
-
? resolveExperimentArm(process.env.EXPERIMENT_ARM ?? '')
|
|
124
|
-
: null
|
|
71
|
+
const ARM = process.env.ARM ?? ''
|
|
72
|
+
if (ARM !== 'system' && ARM !== 'solo') throw new Error(`ARM must be system|solo, got "${ARM}"`)
|
|
125
73
|
const ZAI_BASE = process.env.ZAI_BASE ?? 'https://api.z.ai/api/coding/paas/v4'
|
|
126
74
|
const ZAI_KEY = process.env.ZAI_API_KEY ?? ''
|
|
127
|
-
if (
|
|
75
|
+
if (!ZAI_KEY) throw new Error('ZAI_API_KEY required (run under dotenvx: agent-state.env)')
|
|
128
76
|
const MODEL = process.env.MODEL ?? 'glm-5.2'
|
|
129
77
|
// glm-5.2 is a reasoning model: hidden reasoning consumes max_tokens, so <8000 starves content.
|
|
130
78
|
const MAX_TOKENS = Number(process.env.MAX_TOKENS ?? 12_000)
|
|
79
|
+
const K = Number(process.env.K ?? 4)
|
|
80
|
+
const REPAIRS = Number(process.env.REPAIRS ?? 2)
|
|
81
|
+
const TEMP = Number(process.env.TEMP ?? 0.8)
|
|
82
|
+
const SOLO_TEMP = Number(process.env.SOLO_TEMP ?? 0.7)
|
|
131
83
|
const INNER_TURNS = Number(process.env.INNER_TURNS ?? 40)
|
|
132
84
|
const CONC = Math.max(1, Math.min(4, Number(process.env.CONC ?? 2)))
|
|
133
85
|
const REPRO_TIMEOUT_S = Number(process.env.REPRO_TIMEOUT ?? 120)
|
|
134
86
|
const LLM_TIMEOUT_MS = Number(process.env.LLM_TIMEOUT_MS ?? 480_000)
|
|
87
|
+
const OUT = process.env.OUT ?? `swe-stage1-${ARM}.jsonl`
|
|
88
|
+
const PHASE_A_OUT = `${OUT}.phaseA`
|
|
89
|
+
const SKIP_JUDGE = process.env.SKIP_JUDGE === '1'
|
|
135
90
|
// Cost-table rates, USD per Mtok. ASSUMED (zai coding-plan tokens have no per-call list price);
|
|
136
91
|
// override with PRICE_IN/PRICE_OUT. The summary labels them as assumed.
|
|
137
92
|
const PRICE_IN = Number(process.env.PRICE_IN ?? 0.6)
|
|
@@ -144,137 +99,15 @@ interface ReproManifestEntry {
|
|
|
144
99
|
}
|
|
145
100
|
|
|
146
101
|
const MANIFEST: Record<string, ReproManifestEntry> = (() => {
|
|
147
|
-
if (
|
|
102
|
+
if (ARM !== 'system') return {}
|
|
148
103
|
const p = process.env.REPRO_MANIFEST
|
|
149
|
-
if (!p) throw new Error('REPRO_MANIFEST required for
|
|
104
|
+
if (!p) throw new Error('REPRO_MANIFEST required for ARM=system (Stage-0 valid+sound repro scripts)')
|
|
150
105
|
return JSON.parse(readFileSync(p, 'utf8')) as Record<string, ReproManifestEntry>
|
|
151
106
|
})()
|
|
152
107
|
|
|
153
|
-
interface CommonConfigReceipt {
|
|
154
|
-
schema: 'swe-structural-v2'
|
|
155
|
-
model: string
|
|
156
|
-
zaiBase: string
|
|
157
|
-
maxTokens: number
|
|
158
|
-
temperature: number
|
|
159
|
-
innerTurns: number
|
|
160
|
-
concurrency: number
|
|
161
|
-
reproTimeoutS: number
|
|
162
|
-
llmTimeoutMs: number
|
|
163
|
-
sweRunTimeoutS: number
|
|
164
|
-
sweRunOutputLimit: number
|
|
165
|
-
runTool: true
|
|
166
|
-
seedPrompt: 'SWE_SEED_PROMPT_WITH_RUN'
|
|
167
|
-
taskIds: string[]
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
interface ExperimentConfigReceipt extends CommonConfigReceipt {
|
|
171
|
-
arm: ExperimentArm
|
|
172
|
-
k: 1 | 2
|
|
173
|
-
repairs: 0 | 1
|
|
174
|
-
alwaysRunContinuation: boolean
|
|
175
|
-
persistent: boolean
|
|
176
|
-
workerSessions: 2
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const SOURCE_RECEIPT = [
|
|
180
|
-
['swe-structural.mts', new URL('./swe-structural.mts', import.meta.url)],
|
|
181
|
-
['swe-structural-policy.ts', new URL('./swe-structural-policy.ts', import.meta.url)],
|
|
182
|
-
['swe-structural-provenance.ts', new URL('./swe-structural-provenance.ts', import.meta.url)],
|
|
183
|
-
['swe-structural-judge-policy.ts', new URL('./swe-structural-judge-policy.ts', import.meta.url)],
|
|
184
|
-
['swe-bench-env.ts', new URL('./swe-bench-env.ts', import.meta.url)],
|
|
185
|
-
['swe-jail.ts', new URL('./swe-jail.ts', import.meta.url)],
|
|
186
|
-
['swe-temp.ts', new URL('./swe-temp.ts', import.meta.url)],
|
|
187
|
-
['benchmarks/swe-bench.ts', new URL('./benchmarks/swe-bench.ts', import.meta.url)],
|
|
188
|
-
['benchmarks/_harness.ts', new URL('./benchmarks/_harness.ts', import.meta.url)],
|
|
189
|
-
['runtime/strategy.ts', new URL('../../src/runtime/strategy.ts', import.meta.url)],
|
|
190
|
-
].map(([name, url]) => ({ name: String(name), content: readFileSync(url as URL, 'utf8') }))
|
|
191
|
-
|
|
192
|
-
const RUNTIME_IMPLEMENTATION_FINGERPRINT = runtimeImplementationFingerprint({ runAgentic, refine })
|
|
193
|
-
const RUNTIME_TREE_FINGERPRINT = fingerprint([
|
|
194
|
-
...sourceTreeReceipt(new URL('../../src/', import.meta.url), 'agent-runtime/src', (path) => path.endsWith('.ts')),
|
|
195
|
-
...sourceTreeReceipt(
|
|
196
|
-
new URL('../../node_modules/@tangle-network/agent-eval/dist/', import.meta.url),
|
|
197
|
-
'agent-eval/dist',
|
|
198
|
-
(path) => path.endsWith('.js'),
|
|
199
|
-
),
|
|
200
|
-
{
|
|
201
|
-
name: 'agent-runtime/package.json',
|
|
202
|
-
content: readFileSync(new URL('../../package.json', import.meta.url), 'utf8'),
|
|
203
|
-
},
|
|
204
|
-
{
|
|
205
|
-
name: 'agent-runtime/pnpm-lock.yaml',
|
|
206
|
-
content: readFileSync(new URL('../../pnpm-lock.yaml', import.meta.url), 'utf8'),
|
|
207
|
-
},
|
|
208
|
-
{
|
|
209
|
-
name: 'agent-eval/package.json',
|
|
210
|
-
content: readFileSync(new URL('../../node_modules/@tangle-network/agent-eval/package.json', import.meta.url), 'utf8'),
|
|
211
|
-
},
|
|
212
|
-
])
|
|
213
|
-
|
|
214
|
-
function makeExperimentConfig(preset: ExperimentArmPreset, taskIds: string[]): ExperimentConfigReceipt {
|
|
215
|
-
return {
|
|
216
|
-
schema: 'swe-structural-v2',
|
|
217
|
-
arm: preset.arm,
|
|
218
|
-
k: preset.k,
|
|
219
|
-
repairs: preset.repairs,
|
|
220
|
-
alwaysRunContinuation: preset.alwaysRunContinuation,
|
|
221
|
-
persistent: preset.persistent,
|
|
222
|
-
workerSessions: preset.workerSessions,
|
|
223
|
-
model: MODEL,
|
|
224
|
-
zaiBase: ZAI_BASE,
|
|
225
|
-
maxTokens: MAX_TOKENS,
|
|
226
|
-
temperature: TEMPERATURE,
|
|
227
|
-
innerTurns: INNER_TURNS,
|
|
228
|
-
concurrency: CONC,
|
|
229
|
-
reproTimeoutS: REPRO_TIMEOUT_S,
|
|
230
|
-
llmTimeoutMs: LLM_TIMEOUT_MS,
|
|
231
|
-
sweRunTimeoutS: SWE_RUN_TOOL_CONFIG.timeoutS,
|
|
232
|
-
sweRunOutputLimit: SWE_RUN_TOOL_CONFIG.outputLimit,
|
|
233
|
-
runTool: true,
|
|
234
|
-
seedPrompt: 'SWE_SEED_PROMPT_WITH_RUN',
|
|
235
|
-
taskIds: [...taskIds],
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function commonConfig(config: ExperimentConfigReceipt): CommonConfigReceipt {
|
|
240
|
-
const {
|
|
241
|
-
arm: _arm,
|
|
242
|
-
k: _k,
|
|
243
|
-
repairs: _repairs,
|
|
244
|
-
alwaysRunContinuation: _alwaysRunContinuation,
|
|
245
|
-
persistent: _persistent,
|
|
246
|
-
workerSessions: _workerSessions,
|
|
247
|
-
...common
|
|
248
|
-
} = config
|
|
249
|
-
return common
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
function reproReceipt(entry: ReproManifestEntry | undefined): ReproManifestEntry | null {
|
|
253
|
-
return entry ? { script: entry.script, source: entry.source, stage0Class: entry.stage0Class } : null
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
function expectedFingerprints(
|
|
257
|
-
bt: BenchTask,
|
|
258
|
-
config: ExperimentConfigReceipt,
|
|
259
|
-
tools: unknown,
|
|
260
|
-
repro: ReproManifestEntry | undefined,
|
|
261
|
-
): Fingerprints {
|
|
262
|
-
return createFingerprints({
|
|
263
|
-
source: SOURCE_RECEIPT,
|
|
264
|
-
config,
|
|
265
|
-
commonConfig: commonConfig(config),
|
|
266
|
-
repro: reproReceipt(repro),
|
|
267
|
-
prompt: SWE_SEED_PROMPT_WITH_RUN,
|
|
268
|
-
tools,
|
|
269
|
-
task: { id: bt.id, prompt: bt.prompt, metadata: bt.metadata ?? null },
|
|
270
|
-
})
|
|
271
|
-
}
|
|
272
|
-
|
|
273
108
|
// ---------- transport: zai direct, patient ladder, leak guard at the chokepoint ----------
|
|
274
109
|
|
|
275
110
|
interface Counter {
|
|
276
|
-
workerSessionsStarted: number
|
|
277
|
-
workerSessionsCompleted: number
|
|
278
111
|
calls: number
|
|
279
112
|
httpAttempts: number
|
|
280
113
|
tokensIn: number
|
|
@@ -282,15 +115,7 @@ interface Counter {
|
|
|
282
115
|
guardedMsgs: number
|
|
283
116
|
}
|
|
284
117
|
|
|
285
|
-
const newCounter = (): Counter => ({
|
|
286
|
-
workerSessionsStarted: 0,
|
|
287
|
-
workerSessionsCompleted: 0,
|
|
288
|
-
calls: 0,
|
|
289
|
-
httpAttempts: 0,
|
|
290
|
-
tokensIn: 0,
|
|
291
|
-
tokensOut: 0,
|
|
292
|
-
guardedMsgs: 0,
|
|
293
|
-
})
|
|
118
|
+
const newCounter = (): Counter => ({ calls: 0, httpAttempts: 0, tokensIn: 0, tokensOut: 0, guardedMsgs: 0 })
|
|
294
119
|
|
|
295
120
|
/** Distinctive content marks for the leak guard: the first substantive ADDED line of the gold patch
|
|
296
121
|
* and of the hidden test patch. Never shown to any model; used only to refuse outbound messages. */
|
|
@@ -379,13 +204,12 @@ async function emitAttempt(
|
|
|
379
204
|
}
|
|
380
205
|
const task: AgenticTask = {
|
|
381
206
|
id: bt.id,
|
|
382
|
-
systemPrompt:
|
|
207
|
+
systemPrompt: SWE_SEED_PROMPT,
|
|
383
208
|
userPrompt: cfg.promptAppendix ? `${bt.prompt}\n\n${cfg.promptAppendix}` : bt.prompt,
|
|
384
209
|
meta: { instanceId: bt.id },
|
|
385
210
|
}
|
|
386
211
|
let error: string | undefined
|
|
387
212
|
try {
|
|
388
|
-
cfg.instanceCounter.workerSessionsStarted += 1
|
|
389
213
|
const r = await runAgentic({
|
|
390
214
|
surface: proxy,
|
|
391
215
|
task,
|
|
@@ -399,8 +223,6 @@ async function emitAttempt(
|
|
|
399
223
|
budget: 1,
|
|
400
224
|
complete: makeTransport(cfg.marks, counter),
|
|
401
225
|
})
|
|
402
|
-
if (counter.calls < 1) throw new Error('worker session completed without a successful model call')
|
|
403
|
-
cfg.instanceCounter.workerSessionsCompleted += 1
|
|
404
226
|
cfg.instanceCounter.calls += counter.calls
|
|
405
227
|
cfg.instanceCounter.httpAttempts += counter.httpAttempts
|
|
406
228
|
cfg.instanceCounter.tokensIn += counter.tokensIn
|
|
@@ -433,17 +255,11 @@ async function emitAttempt(
|
|
|
433
255
|
}
|
|
434
256
|
}
|
|
435
257
|
|
|
436
|
-
// Arm composition deliberately stays outside the built-in sample/refine strategies while each
|
|
437
|
-
// worker session still runs through runAgentic(refine, budget=1). The built-ins cannot reproduce
|
|
438
|
-
// this controlled comparison: sample exposes only aggregate scores and has no stable later-on-tie
|
|
439
|
-
// patch receipt; refine may stop after shot one, adds an analyst call, and carries conversation
|
|
440
|
-
// history. This layer supplies only the missing experiment policy: exactly two fresh worker
|
|
441
|
-
// sessions, optional parent-patch state, shared visible selection, and exact per-session receipts.
|
|
442
|
-
|
|
443
258
|
// ---------- in-image candidate scoring ----------
|
|
444
259
|
|
|
445
|
-
/** Severity ordering for
|
|
446
|
-
*
|
|
260
|
+
/** Severity ordering for argmax (lower is better): 0 repro-pass, 1 repro-fail (clean nonzero exit),
|
|
261
|
+
* 2 repro-timeout, 3 candidate failed to apply, 4 empty candidate. "Crash-lowest" is realized as
|
|
262
|
+
* this exit-class ordering; ties break on first index (deterministic). */
|
|
447
263
|
interface CandScore {
|
|
448
264
|
applyOk: boolean | null
|
|
449
265
|
exit: number | null
|
|
@@ -471,7 +287,6 @@ async function scoreCandidate(imageTag: string, repro: string | null, diff: stri
|
|
|
471
287
|
interface CandidateRow {
|
|
472
288
|
idx: number
|
|
473
289
|
diff: string
|
|
474
|
-
diffHash: string
|
|
475
290
|
diffBytes: number
|
|
476
291
|
completions: number
|
|
477
292
|
calls: number
|
|
@@ -490,10 +305,7 @@ interface RepairRow {
|
|
|
490
305
|
round: number
|
|
491
306
|
baseFrom: string
|
|
492
307
|
baseSeverity: number
|
|
493
|
-
parentDiffHash: string
|
|
494
308
|
diff: string
|
|
495
|
-
finalDiffHash: string
|
|
496
|
-
changedFromParent: boolean
|
|
497
309
|
diffBytes: number
|
|
498
310
|
completions: number
|
|
499
311
|
calls: number
|
|
@@ -507,46 +319,37 @@ interface RepairRow {
|
|
|
507
319
|
accepted: boolean
|
|
508
320
|
}
|
|
509
321
|
|
|
510
|
-
interface
|
|
322
|
+
interface Row {
|
|
511
323
|
instanceId: string
|
|
512
|
-
arm:
|
|
513
|
-
config: ExperimentConfigReceipt
|
|
514
|
-
fingerprints: Fingerprints
|
|
324
|
+
arm: string
|
|
515
325
|
repo: string
|
|
516
326
|
model: string
|
|
517
327
|
image: string | null
|
|
518
|
-
execution: ExecutionReceipt | null
|
|
519
|
-
executionFingerprint: string
|
|
520
328
|
execMode: 'image'
|
|
521
329
|
temperature: number
|
|
522
330
|
innerTurns: number
|
|
523
331
|
k: number
|
|
524
|
-
maxRepairs: number
|
|
525
332
|
// canary + repro provenance (system arm)
|
|
526
333
|
canaryExit: number | null
|
|
527
334
|
canaryPass: boolean | null
|
|
528
335
|
reproSource: string
|
|
529
|
-
reproStage0Class: string | null
|
|
530
336
|
reproStatus: string
|
|
531
337
|
reproScript: string | null
|
|
532
338
|
reproPreExit: number | null
|
|
533
339
|
reproGoldExit: number | null
|
|
534
|
-
|
|
535
|
-
// candidates + selection + continuation receipts
|
|
340
|
+
// candidates + selection + repair receipts (system arm)
|
|
536
341
|
candidates: CandidateRow[]
|
|
537
342
|
selection: { mode: string; selectedIdx: number; movedOffFirst: boolean } | null
|
|
538
343
|
repairs: RepairRow[]
|
|
539
344
|
repairStop: string | null
|
|
540
345
|
finalFrom: string
|
|
541
|
-
parentDiffHash: string | null
|
|
542
346
|
finalDiff: string
|
|
543
|
-
|
|
544
|
-
|
|
347
|
+
// hidden judge (Phase B; locked-after-decisions)
|
|
348
|
+
hiddenResolved: boolean | null
|
|
349
|
+
judgeDetail: string | null
|
|
350
|
+
judgeMs: number | null
|
|
351
|
+
judgeSkipped: string | null
|
|
545
352
|
// cost + guard receipts
|
|
546
|
-
/** Number of fresh runAgentic worker invocations, including invocations that returned an error. */
|
|
547
|
-
workerSessionsStarted: number
|
|
548
|
-
/** Number of runAgentic invocations that returned successfully after at least one model call. */
|
|
549
|
-
workerSessions: number
|
|
550
353
|
llmCalls: number
|
|
551
354
|
httpAttempts: number
|
|
552
355
|
tokensIn: number
|
|
@@ -560,32 +363,17 @@ interface PhaseARow {
|
|
|
560
363
|
|
|
561
364
|
type Env = Awaited<ReturnType<typeof createSweBenchEnvironment>>
|
|
562
365
|
|
|
563
|
-
|
|
564
|
-
preset: ExperimentArmPreset
|
|
565
|
-
config: ExperimentConfigReceipt
|
|
566
|
-
tools: unknown
|
|
567
|
-
sharedExecution: SharedExecutionReceipt
|
|
568
|
-
expectedImageIdentities: Map<string, SweImageIdentity>
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
async function phaseA(env: Env, bt: BenchTask, ctx: PhaseAContext): Promise<PhaseARow> {
|
|
366
|
+
async function phaseA(env: Env, bt: BenchTask): Promise<Row> {
|
|
572
367
|
const t0 = Date.now()
|
|
573
368
|
const md = bt.metadata as Record<string, string>
|
|
574
369
|
const counter = newCounter()
|
|
575
|
-
const row:
|
|
576
|
-
instanceId: bt.id, arm:
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
reproStatus: 'none', reproScript: null,
|
|
583
|
-
reproPreExit: null, reproGoldExit: null, reproOutcomeFingerprint: '',
|
|
584
|
-
candidates: [], selection: null, repairs: [],
|
|
585
|
-
repairStop: null, finalFrom: 'none', parentDiffHash: null, finalDiff: '',
|
|
586
|
-
finalDiffHash: diffFingerprint(''), changedFromParent: null,
|
|
587
|
-
workerSessionsStarted: 0, workerSessions: 0, llmCalls: 0, httpAttempts: 0,
|
|
588
|
-
tokensIn: 0, tokensOut: 0,
|
|
370
|
+
const row: Row = {
|
|
371
|
+
instanceId: bt.id, arm: ARM, repo: md.repo, model: MODEL, image: null, execMode: 'image',
|
|
372
|
+
temperature: ARM === 'system' ? TEMP : SOLO_TEMP, innerTurns: INNER_TURNS, k: ARM === 'system' ? K : 1,
|
|
373
|
+
canaryExit: null, canaryPass: null, reproSource: 'none', reproStatus: 'none', reproScript: null,
|
|
374
|
+
reproPreExit: null, reproGoldExit: null, candidates: [], selection: null, repairs: [],
|
|
375
|
+
repairStop: null, finalFrom: 'none', finalDiff: '', hiddenResolved: null, judgeDetail: null,
|
|
376
|
+
judgeMs: null, judgeSkipped: null, llmCalls: 0, httpAttempts: 0, tokensIn: 0, tokensOut: 0,
|
|
589
377
|
guardedMsgs: 0, wallMs: 0,
|
|
590
378
|
}
|
|
591
379
|
const marks = leakMarks(md)
|
|
@@ -593,16 +381,25 @@ async function phaseA(env: Env, bt: BenchTask, ctx: PhaseAContext): Promise<Phas
|
|
|
593
381
|
const img = await resolveImageForMetadata(bt.metadata ?? {})
|
|
594
382
|
if (!img.ok) throw new Error(`image missing: ${img.reason}`)
|
|
595
383
|
row.image = img.tag
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
384
|
+
|
|
385
|
+
if (ARM === 'solo') {
|
|
386
|
+
const a = await emitAttempt(env.environment, bt, { temperature: SOLO_TEMP, marks, instanceCounter: counter })
|
|
387
|
+
row.candidates.push({
|
|
388
|
+
idx: 0, diff: a.diff, diffBytes: a.diff.length, completions: a.completions, calls: a.calls,
|
|
389
|
+
tokensIn: a.tokensIn, tokensOut: a.tokensOut, wallMs: a.wallMs, attemptError: a.error ?? null,
|
|
390
|
+
applyOk: null, reproExit: null, reproTimedOut: false, severity: a.diff.trim() ? 1 : 4, reproOutTail: '',
|
|
391
|
+
})
|
|
392
|
+
row.finalDiff = a.diff
|
|
393
|
+
row.finalFrom = 'candidate:0'
|
|
394
|
+
return row
|
|
395
|
+
}
|
|
599
396
|
|
|
600
397
|
// 1. EXECUTION CANARY (image substrate, gold in-container — script-side only, zero model calls).
|
|
601
398
|
const pkg = IMPORT_NAME[md.repo]
|
|
602
399
|
if (!pkg) throw new Error(`no IMPORT_NAME for ${md.repo} — canary not expressible`)
|
|
603
400
|
const gold = String(md.patch ?? '')
|
|
604
401
|
if (!gold.trim()) throw new Error('gold patch missing from metadata')
|
|
605
|
-
const c = await runPyInJail(img.
|
|
402
|
+
const c = await runPyInJail(img.tag, null, importCanaryScript(pkg), gold, { timeoutS: REPRO_TIMEOUT_S })
|
|
606
403
|
if (c.infraError) throw new Error(c.infraError)
|
|
607
404
|
row.canaryExit = c.code
|
|
608
405
|
row.canaryPass = c.code === 0 && c.out.includes(APPLY_SENTINEL)
|
|
@@ -613,15 +410,14 @@ async function phaseA(env: Env, bt: BenchTask, ctx: PhaseAContext): Promise<Phas
|
|
|
613
410
|
let repro: string | null = null
|
|
614
411
|
if (manifest) {
|
|
615
412
|
row.reproSource = manifest.source
|
|
616
|
-
row.reproStage0Class = manifest.stage0Class
|
|
617
413
|
row.reproScript = manifest.script
|
|
618
|
-
const pre = await runPyInJail(img.
|
|
414
|
+
const pre = await runPyInJail(img.tag, null, manifest.script, undefined, { timeoutS: REPRO_TIMEOUT_S })
|
|
619
415
|
if (pre.infraError) throw new Error(pre.infraError)
|
|
620
416
|
row.reproPreExit = pre.code
|
|
621
417
|
if (pre.timedOut) row.reproStatus = 'degraded-timeout'
|
|
622
418
|
else if (pre.code === 0) row.reproStatus = 'degraded-invalid'
|
|
623
419
|
else {
|
|
624
|
-
const post = await runPyInJail(img.
|
|
420
|
+
const post = await runPyInJail(img.tag, null, manifest.script, gold, { timeoutS: REPRO_TIMEOUT_S })
|
|
625
421
|
if (post.infraError) throw new Error(post.infraError)
|
|
626
422
|
row.reproGoldExit = post.code
|
|
627
423
|
if (post.code === 0 && post.out.includes(APPLY_SENTINEL)) {
|
|
@@ -635,12 +431,11 @@ async function phaseA(env: Env, bt: BenchTask, ctx: PhaseAContext): Promise<Phas
|
|
|
635
431
|
|
|
636
432
|
// 3. k independent candidates (serial within the instance — CONC instances bound zai concurrency).
|
|
637
433
|
const diffs: string[] = []
|
|
638
|
-
for (let i = 0; i <
|
|
639
|
-
const a = await emitAttempt(env.environment, bt, { temperature:
|
|
434
|
+
for (let i = 0; i < K; i += 1) {
|
|
435
|
+
const a = await emitAttempt(env.environment, bt, { temperature: TEMP, marks, instanceCounter: counter })
|
|
640
436
|
diffs.push(a.diff)
|
|
641
437
|
row.candidates.push({
|
|
642
|
-
idx: i, diff: a.diff,
|
|
643
|
-
completions: a.completions, calls: a.calls,
|
|
438
|
+
idx: i, diff: a.diff, diffBytes: a.diff.length, completions: a.completions, calls: a.calls,
|
|
644
439
|
tokensIn: a.tokensIn, tokensOut: a.tokensOut, wallMs: a.wallMs, attemptError: a.error ?? null,
|
|
645
440
|
applyOk: null, reproExit: null, reproTimedOut: false, severity: -1, reproOutTail: '',
|
|
646
441
|
})
|
|
@@ -648,8 +443,8 @@ async function phaseA(env: Env, bt: BenchTask, ctx: PhaseAContext): Promise<Phas
|
|
|
648
443
|
|
|
649
444
|
// 4. In-image scoring + argmax.
|
|
650
445
|
const scores: CandScore[] = []
|
|
651
|
-
for (let i = 0; i <
|
|
652
|
-
const s = await scoreCandidate(img.
|
|
446
|
+
for (let i = 0; i < K; i += 1) {
|
|
447
|
+
const s = await scoreCandidate(img.tag, repro, diffs[i] as string)
|
|
653
448
|
scores.push(s)
|
|
654
449
|
const cand = row.candidates[i] as CandidateRow
|
|
655
450
|
cand.applyOk = s.applyOk
|
|
@@ -658,96 +453,61 @@ async function phaseA(env: Env, bt: BenchTask, ctx: PhaseAContext): Promise<Phas
|
|
|
658
453
|
cand.severity = s.severity
|
|
659
454
|
cand.reproOutTail = s.out
|
|
660
455
|
}
|
|
661
|
-
let selectedIdx
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
456
|
+
let selectedIdx: number
|
|
457
|
+
let mode: string
|
|
458
|
+
if (repro) {
|
|
459
|
+
mode = 'repro-argmax'
|
|
460
|
+
selectedIdx = 0
|
|
461
|
+
for (let i = 1; i < K; i += 1) {
|
|
462
|
+
if ((scores[i] as CandScore).severity < (scores[selectedIdx] as CandScore).severity) selectedIdx = i
|
|
665
463
|
}
|
|
464
|
+
} else {
|
|
465
|
+
mode = 'blind-first'
|
|
466
|
+
const firstNonEmpty = diffs.findIndex((d) => d.trim().length > 0)
|
|
467
|
+
selectedIdx = firstNonEmpty === -1 ? 0 : firstNonEmpty
|
|
666
468
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
from: ctx.preset.persistent ? 'session:1' : `attempt:${selectedIdx + 1}`,
|
|
676
|
-
}
|
|
677
|
-
if (!ctx.preset.persistent) {
|
|
678
|
-
row.repairStop = 'independent-arm'
|
|
469
|
+
row.selection = { mode, selectedIdx, movedOffFirst: mode === 'repro-argmax' && selectedIdx !== 0 }
|
|
470
|
+
|
|
471
|
+
// 5. Guarded repair (repro-armed instances only — no signal, no repair).
|
|
472
|
+
let best = { diff: diffs[selectedIdx] as string, score: scores[selectedIdx] as CandScore, from: `candidate:${selectedIdx}` }
|
|
473
|
+
if (!repro) {
|
|
474
|
+
row.repairStop = 'no-signal'
|
|
475
|
+
} else if (best.score.severity === 0) {
|
|
476
|
+
row.repairStop = 'already-passing'
|
|
679
477
|
} else {
|
|
680
|
-
for (let round = 1;
|
|
681
|
-
const parentDiff = best.diff
|
|
682
|
-
const parentDiffHash = diffFingerprint(parentDiff)
|
|
683
|
-
row.parentDiffHash = parentDiffHash
|
|
684
|
-
const reproductionEvidence = repro
|
|
685
|
-
? `--- REPRODUCTION SCRIPT (written from the issue; exit 0 = fixed) ---\n${tail(repro, 6_000)}\n\n` +
|
|
686
|
-
`--- REPRODUCTION OUTPUT on the current state (exit ${best.score.exit ?? 'n/a'}) ---\n${best.score.out}\n\n`
|
|
687
|
-
: '--- EXTERNAL REPRODUCTION ---\nNo external reproduction is available. Use the run tool to construct local, issue-specific checks.\n\n'
|
|
688
|
-
const repairInstruction = best.score.severity === 0
|
|
689
|
-
? 'The visible reproduction PASSES, but it is only a partial check. Re-read the full issue and audit ' +
|
|
690
|
-
'the current patch for missed cases or regressions. Keep the visible check passing while correcting ' +
|
|
691
|
-
'any incomplete source behavior you find with minimal edit_file changes. Do not modify tests.'
|
|
692
|
-
: 'The visible reproduction still fails (or no external reproduction is available). Diagnose why the ' +
|
|
693
|
-
'current state does not resolve the full issue, then correct the SOURCE with minimal edit_file changes ' +
|
|
694
|
-
'(you may revise or revert parts of the previous fix — it is already in the files). Do not modify tests.'
|
|
478
|
+
for (let round = 1; round <= REPAIRS && best.score.severity > 0; round += 1) {
|
|
695
479
|
const appendix =
|
|
696
|
-
|
|
697
|
-
|
|
480
|
+
(best.diff.trim()
|
|
481
|
+
? `--- PREVIOUS FIX (already applied to this checkout) ---\n${tail(best.diff, 8_000)}\n\n`
|
|
482
|
+
: '--- NO FIX APPLIED YET (every prior attempt produced no change) ---\n\n') +
|
|
483
|
+
`--- REPRODUCTION SCRIPT (written from the issue; exit 0 = fixed) ---\n${tail(repro, 6_000)}\n\n` +
|
|
484
|
+
`--- REPRODUCTION OUTPUT on the current state (exit ${best.score.exit ?? 'n/a'}) ---\n${best.score.out}\n\n` +
|
|
698
485
|
'--- REPAIR INSTRUCTIONS ---\n' +
|
|
699
|
-
|
|
486
|
+
'The reproduction above STILL FAILS. Diagnose why the current state does not resolve the issue, ' +
|
|
487
|
+
'then correct the SOURCE with minimal edit_file changes (you may revise or revert parts of the ' +
|
|
488
|
+
'previous fix — it is already in the files). Do not modify tests.'
|
|
700
489
|
const a = await emitAttempt(env.environment, bt, {
|
|
701
|
-
temperature:
|
|
490
|
+
temperature: TEMP, marks, instanceCounter: counter, preApply: best.diff, promptAppendix: appendix,
|
|
702
491
|
})
|
|
703
|
-
const ns = await scoreCandidate(img.
|
|
704
|
-
const
|
|
705
|
-
const changedFromParent = diffChanged(parentDiff, a.diff)
|
|
706
|
-
const accepted = shouldAcceptContinuation(best.score.severity, ns.severity)
|
|
707
|
-
const disposition = continuationDisposition(accepted, changedFromParent)
|
|
492
|
+
const ns = await scoreCandidate(img.tag, repro, a.diff)
|
|
493
|
+
const accepted = ns.severity < best.score.severity
|
|
708
494
|
row.repairs.push({
|
|
709
|
-
round, baseFrom: best.from, baseSeverity: best.score.severity,
|
|
710
|
-
diff: a.diff, finalDiffHash, changedFromParent, diffBytes: a.diff.length,
|
|
495
|
+
round, baseFrom: best.from, baseSeverity: best.score.severity, diff: a.diff, diffBytes: a.diff.length,
|
|
711
496
|
completions: a.completions, calls: a.calls, tokensIn: a.tokensIn, tokensOut: a.tokensOut,
|
|
712
497
|
wallMs: a.wallMs, attemptError: a.error ?? null, applyOk: ns.applyOk, reproExit: ns.exit,
|
|
713
498
|
severity: ns.severity, accepted,
|
|
714
499
|
})
|
|
715
|
-
if (accepted) {
|
|
716
|
-
best = {
|
|
717
|
-
diff: a.diff,
|
|
718
|
-
score: ns,
|
|
719
|
-
from: disposition.finalFrom,
|
|
720
|
-
}
|
|
721
|
-
row.repairStop = disposition.stop
|
|
722
|
-
} else {
|
|
723
|
-
best.from = disposition.finalFrom
|
|
724
|
-
row.repairStop = disposition.stop
|
|
725
|
-
}
|
|
500
|
+
if (accepted) best = { diff: a.diff, score: ns, from: `repair:${round}` }
|
|
726
501
|
}
|
|
502
|
+
row.repairStop = best.score.severity === 0 ? 'repaired-pass' : 'rounds-exhausted'
|
|
727
503
|
}
|
|
728
504
|
row.finalDiff = best.diff
|
|
729
|
-
row.finalDiffHash = diffFingerprint(best.diff)
|
|
730
505
|
row.finalFrom = best.from
|
|
731
|
-
row.changedFromParent = row.parentDiffHash === null
|
|
732
|
-
? null
|
|
733
|
-
: row.finalDiffHash !== row.parentDiffHash
|
|
734
506
|
return row
|
|
735
507
|
} catch (e) {
|
|
736
508
|
row.error = e instanceof Error ? e.message.slice(0, 400) : String(e).slice(0, 400)
|
|
737
509
|
return row
|
|
738
510
|
} finally {
|
|
739
|
-
row.reproOutcomeFingerprint = fingerprint({
|
|
740
|
-
canaryExit: row.canaryExit,
|
|
741
|
-
canaryPass: row.canaryPass,
|
|
742
|
-
image: row.execution?.image ?? null,
|
|
743
|
-
reproGoldExit: row.reproGoldExit,
|
|
744
|
-
reproPreExit: row.reproPreExit,
|
|
745
|
-
reproSource: row.reproSource,
|
|
746
|
-
reproStage0Class: row.reproStage0Class,
|
|
747
|
-
reproStatus: row.reproStatus,
|
|
748
|
-
})
|
|
749
|
-
row.workerSessionsStarted = counter.workerSessionsStarted
|
|
750
|
-
row.workerSessions = counter.workerSessionsCompleted
|
|
751
511
|
row.llmCalls = counter.calls
|
|
752
512
|
row.httpAttempts = counter.httpAttempts
|
|
753
513
|
row.tokensIn = counter.tokensIn
|
|
@@ -759,501 +519,149 @@ async function phaseA(env: Env, bt: BenchTask, ctx: PhaseAContext): Promise<Phas
|
|
|
759
519
|
|
|
760
520
|
// ---------- driver ----------
|
|
761
521
|
|
|
762
|
-
function
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
}
|
|
767
|
-
const rows = new Map<string, PhaseARow>()
|
|
768
|
-
for (const [index, line] of readFileSync(path, 'utf8').split('\n').entries()) {
|
|
522
|
+
function loadRows(path: string): Map<string, Row> {
|
|
523
|
+
const m = new Map<string, Row>()
|
|
524
|
+
if (!existsSync(path)) return m
|
|
525
|
+
for (const line of readFileSync(path, 'utf8').split('\n')) {
|
|
769
526
|
if (!line.trim()) continue
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
if (rows.has(row.instanceId)) throw new Error(`${path}:${index + 1}: duplicate instanceId ${row.instanceId}`)
|
|
773
|
-
rows.set(row.instanceId, row)
|
|
527
|
+
const r = JSON.parse(line) as Row
|
|
528
|
+
m.set(r.instanceId, r)
|
|
774
529
|
}
|
|
775
|
-
|
|
776
|
-
return rows
|
|
530
|
+
return m
|
|
777
531
|
}
|
|
778
532
|
|
|
779
|
-
function
|
|
780
|
-
if (row.reproScript === null) {
|
|
781
|
-
if (row.reproSource !== 'none' || row.reproStage0Class !== null) {
|
|
782
|
-
throw new Error(`${row.instanceId}: incomplete null-repro receipt`)
|
|
783
|
-
}
|
|
784
|
-
return undefined
|
|
785
|
-
}
|
|
786
|
-
if (row.reproSource === 'none' || row.reproStage0Class === null) {
|
|
787
|
-
throw new Error(`${row.instanceId}: incomplete repro receipt`)
|
|
788
|
-
}
|
|
789
|
-
return { script: row.reproScript, source: row.reproSource, stage0Class: row.reproStage0Class }
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
function assertPresetConfig(config: ExperimentConfigReceipt, arm: ExperimentArm, context: string): void {
|
|
793
|
-
const preset = resolveExperimentArm(arm)
|
|
794
|
-
if (
|
|
795
|
-
config.schema !== 'swe-structural-v2' ||
|
|
796
|
-
!config.model ||
|
|
797
|
-
!config.zaiBase ||
|
|
798
|
-
!Number.isFinite(config.maxTokens) ||
|
|
799
|
-
config.maxTokens <= 0 ||
|
|
800
|
-
!Number.isFinite(config.temperature) ||
|
|
801
|
-
!Number.isInteger(config.innerTurns) ||
|
|
802
|
-
config.innerTurns <= 0 ||
|
|
803
|
-
!Number.isInteger(config.concurrency) ||
|
|
804
|
-
config.concurrency < 1 ||
|
|
805
|
-
!Number.isFinite(config.reproTimeoutS) ||
|
|
806
|
-
config.reproTimeoutS <= 0 ||
|
|
807
|
-
!Number.isFinite(config.llmTimeoutMs) ||
|
|
808
|
-
config.llmTimeoutMs <= 0 ||
|
|
809
|
-
config.runTool !== true ||
|
|
810
|
-
config.seedPrompt !== 'SWE_SEED_PROMPT_WITH_RUN' ||
|
|
811
|
-
!Number.isFinite(config.sweRunTimeoutS) ||
|
|
812
|
-
config.sweRunTimeoutS <= 0 ||
|
|
813
|
-
!Number.isFinite(config.sweRunOutputLimit) ||
|
|
814
|
-
config.sweRunOutputLimit <= 0 ||
|
|
815
|
-
!Array.isArray(config.taskIds) ||
|
|
816
|
-
config.taskIds.length === 0
|
|
817
|
-
) {
|
|
818
|
-
throw new Error(`${context}: unsupported config schema or worker surface`)
|
|
819
|
-
}
|
|
820
|
-
for (const key of ['arm', 'k', 'repairs', 'alwaysRunContinuation', 'persistent', 'workerSessions'] as const) {
|
|
821
|
-
if (config[key] !== preset[key]) {
|
|
822
|
-
throw new Error(`${context}: config ${key}=${String(config[key])}, expected ${String(preset[key])}`)
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
async function assertPhaseRow(
|
|
828
|
-
row: PhaseARow,
|
|
829
|
-
bt: BenchTask,
|
|
830
|
-
config: ExperimentConfigReceipt,
|
|
831
|
-
tools: unknown,
|
|
832
|
-
repro: ReproManifestEntry | undefined,
|
|
833
|
-
sharedExecution: SharedExecutionReceipt,
|
|
834
|
-
context: string,
|
|
835
|
-
): Promise<void> {
|
|
836
|
-
if (row.instanceId !== bt.id) throw new Error(`${context}: instance mismatch ${row.instanceId} != ${bt.id}`)
|
|
837
|
-
if (row.arm !== config.arm) throw new Error(`${context}: arm mismatch ${row.arm} != ${config.arm}`)
|
|
838
|
-
if (!row.config) throw new Error(`${context}: missing config receipt`)
|
|
839
|
-
assertPresetConfig(row.config, row.arm, context)
|
|
840
|
-
if (fingerprint(row.config) !== fingerprint(config)) throw new Error(`${context}: config receipt mismatch`)
|
|
841
|
-
assertFingerprintsEqual(row.fingerprints, expectedFingerprints(bt, config, tools, repro), context)
|
|
842
|
-
if (
|
|
843
|
-
row.model !== config.model ||
|
|
844
|
-
row.temperature !== config.temperature ||
|
|
845
|
-
row.innerTurns !== config.innerTurns ||
|
|
846
|
-
row.k !== config.k ||
|
|
847
|
-
row.maxRepairs !== config.repairs
|
|
848
|
-
) {
|
|
849
|
-
throw new Error(`${context}: row execution fields do not match config receipt`)
|
|
850
|
-
}
|
|
851
|
-
if (row.repo !== String((bt.metadata as Record<string, unknown> | undefined)?.repo ?? '')) {
|
|
852
|
-
throw new Error(`${context}: row repo does not match task metadata`)
|
|
853
|
-
}
|
|
854
|
-
if (row.error) throw new Error(`${context}: Phase A contains an error: ${row.error}`)
|
|
855
|
-
assertExactCompletedWorkerSessions({
|
|
856
|
-
started: row.workerSessionsStarted,
|
|
857
|
-
completed: row.workerSessions,
|
|
858
|
-
sessions: [...row.candidates, ...row.repairs],
|
|
859
|
-
context,
|
|
860
|
-
})
|
|
861
|
-
const currentImage = await resolveImageForMetadata(bt.metadata ?? {})
|
|
862
|
-
if (!currentImage.ok) throw new Error(`${context}: image unavailable during receipt validation: ${currentImage.reason}`)
|
|
863
|
-
const expectedExecution = createExecutionReceipt(sharedExecution, currentImage)
|
|
864
|
-
const actualExecution = row.execution
|
|
865
|
-
if (!actualExecution || fingerprint(actualExecution) !== fingerprint(expectedExecution)) {
|
|
866
|
-
throw new Error(`${context}: execution receipt mismatch`)
|
|
867
|
-
}
|
|
868
|
-
if (
|
|
869
|
-
row.config.sweRunTimeoutS !== actualExecution.runTool.timeoutS ||
|
|
870
|
-
row.config.sweRunOutputLimit !== actualExecution.runTool.outputLimit
|
|
871
|
-
) {
|
|
872
|
-
throw new Error(`${context}: config and execution run-tool settings do not match`)
|
|
873
|
-
}
|
|
874
|
-
if (row.executionFingerprint !== fingerprint(expectedExecution)) {
|
|
875
|
-
throw new Error(`${context}: execution fingerprint mismatch`)
|
|
876
|
-
}
|
|
877
|
-
if (row.image !== expectedExecution.image.tag) throw new Error(`${context}: image tag receipt mismatch`)
|
|
878
|
-
if (row.finalDiffHash !== diffFingerprint(row.finalDiff)) throw new Error(`${context}: final diff hash mismatch`)
|
|
879
|
-
const expectedReproOutcome = fingerprint({
|
|
880
|
-
canaryExit: row.canaryExit,
|
|
881
|
-
canaryPass: row.canaryPass,
|
|
882
|
-
image: actualExecution.image,
|
|
883
|
-
reproGoldExit: row.reproGoldExit,
|
|
884
|
-
reproPreExit: row.reproPreExit,
|
|
885
|
-
reproSource: row.reproSource,
|
|
886
|
-
reproStage0Class: row.reproStage0Class,
|
|
887
|
-
reproStatus: row.reproStatus,
|
|
888
|
-
})
|
|
889
|
-
if (row.reproOutcomeFingerprint !== expectedReproOutcome) {
|
|
890
|
-
throw new Error(`${context}: reproduction outcome fingerprint mismatch`)
|
|
891
|
-
}
|
|
892
|
-
for (const candidate of row.candidates) {
|
|
893
|
-
if (candidate.diffHash !== diffFingerprint(candidate.diff)) {
|
|
894
|
-
throw new Error(`${context}: candidate ${candidate.idx} diff hash mismatch`)
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
const preset = resolveExperimentArm(row.arm)
|
|
899
|
-
if (row.candidates.length !== preset.k || row.repairs.length !== preset.repairs) {
|
|
900
|
-
throw new Error(
|
|
901
|
-
`${context}: arm shape mismatch (candidates=${row.candidates.length}, repairs=${row.repairs.length})`,
|
|
902
|
-
)
|
|
903
|
-
}
|
|
904
|
-
let selectedIdx = 0
|
|
905
|
-
for (let i = 1; i < row.candidates.length; i += 1) {
|
|
906
|
-
if (preferLaterCandidate(row.candidates[selectedIdx]!.severity, row.candidates[i]!.severity)) selectedIdx = i
|
|
907
|
-
}
|
|
908
|
-
const expectedMode = row.reproStatus === 'ok' ? 'visible-severity-later-tie' : 'no-repro-later-tie'
|
|
909
|
-
if (
|
|
910
|
-
row.selection?.selectedIdx !== selectedIdx ||
|
|
911
|
-
row.selection.mode !== expectedMode ||
|
|
912
|
-
row.selection.movedOffFirst !== (selectedIdx !== 0)
|
|
913
|
-
) {
|
|
914
|
-
throw new Error(`${context}: selectedIdx violates later-on-visible-tie policy`)
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
if (!preset.persistent) {
|
|
918
|
-
if (row.parentDiffHash !== null || row.changedFromParent !== null) {
|
|
919
|
-
throw new Error(`${context}: independent arm must not claim a parent/refinement`)
|
|
920
|
-
}
|
|
921
|
-
if (row.repairStop !== 'independent-arm') throw new Error(`${context}: independent arm stop receipt mismatch`)
|
|
922
|
-
const selected = row.candidates[selectedIdx]!
|
|
923
|
-
if (row.finalDiff !== selected.diff || row.finalFrom !== `attempt:${selectedIdx + 1}`) {
|
|
924
|
-
throw new Error(`${context}: independent final patch does not match selected attempt`)
|
|
925
|
-
}
|
|
926
|
-
return
|
|
927
|
-
}
|
|
928
|
-
|
|
929
|
-
const continuation = row.repairs[0]!
|
|
930
|
-
const parent = row.candidates[0]!.diff
|
|
931
|
-
if (continuation.baseFrom !== 'session:1' || continuation.baseSeverity !== row.candidates[0]!.severity) {
|
|
932
|
-
throw new Error(`${context}: continuation parent receipt mismatch`)
|
|
933
|
-
}
|
|
934
|
-
if (continuation.parentDiffHash !== diffFingerprint(parent)) throw new Error(`${context}: parent diff hash mismatch`)
|
|
935
|
-
if (continuation.finalDiffHash !== diffFingerprint(continuation.diff)) throw new Error(`${context}: continuation diff hash mismatch`)
|
|
936
|
-
if (continuation.changedFromParent !== diffChanged(parent, continuation.diff)) {
|
|
937
|
-
throw new Error(`${context}: continuation changedFromParent mismatch`)
|
|
938
|
-
}
|
|
939
|
-
const shouldAccept = shouldAcceptContinuation(continuation.baseSeverity, continuation.severity)
|
|
940
|
-
if (continuation.accepted !== shouldAccept) throw new Error(`${context}: continuation violates shared tie policy`)
|
|
941
|
-
const expectedFinal = continuation.accepted ? continuation.diff : parent
|
|
942
|
-
const disposition = continuationDisposition(continuation.accepted, continuation.changedFromParent)
|
|
943
|
-
if (row.finalDiff !== expectedFinal || row.finalFrom !== disposition.finalFrom || row.repairStop !== disposition.stop) {
|
|
944
|
-
throw new Error(`${context}: persistent final patch/provenance mismatch`)
|
|
945
|
-
}
|
|
946
|
-
if (row.parentDiffHash !== continuation.parentDiffHash) throw new Error(`${context}: row parent diff hash mismatch`)
|
|
947
|
-
if (row.changedFromParent !== (row.finalDiffHash !== row.parentDiffHash)) {
|
|
948
|
-
throw new Error(`${context}: row changedFromParent mismatch`)
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
|
|
952
|
-
async function assertOfficialScoreImagePinned(row: PhaseARow, bt: BenchTask, context: string): Promise<void> {
|
|
953
|
-
const expected = row.execution?.image
|
|
954
|
-
if (!expected) throw new Error(`${context}: missing immutable image receipt`)
|
|
955
|
-
const current = await resolveImageForMetadata(bt.metadata ?? {})
|
|
956
|
-
if (!current.ok) throw new Error(`${context}: image unavailable: ${current.reason}`)
|
|
957
|
-
if (
|
|
958
|
-
current.tag !== expected.tag ||
|
|
959
|
-
current.namespace !== expected.namespace ||
|
|
960
|
-
current.identity.id !== expected.id
|
|
961
|
-
) {
|
|
962
|
-
throw new Error(
|
|
963
|
-
`${context}: official-score image changed ` +
|
|
964
|
-
`(${expected.namespace}:${expected.tag}@${expected.id} -> ` +
|
|
965
|
-
`${current.namespace}:${current.tag}@${current.identity.id})`,
|
|
966
|
-
)
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
|
|
970
|
-
async function loadTasksAndTools(ids: string[]): Promise<{
|
|
971
|
-
env: Env
|
|
972
|
-
taskById: Map<string, BenchTask>
|
|
973
|
-
tools: unknown
|
|
974
|
-
sharedExecution: SharedExecutionReceipt
|
|
975
|
-
expectedImageIdentities: Map<string, SweImageIdentity>
|
|
976
|
-
}> {
|
|
977
|
-
const expectedImageIdentities = new Map<string, SweImageIdentity>()
|
|
978
|
-
const env = await createSweBenchEnvironment(ids.length, {
|
|
979
|
-
ids,
|
|
980
|
-
cloneCache: true,
|
|
981
|
-
enableRun: true,
|
|
982
|
-
expectedImageIdentities,
|
|
983
|
-
adapterOptions: { cacheLevel: OFFICIAL_SCORER_CACHE_LEVEL },
|
|
984
|
-
})
|
|
985
|
-
await env.adapter.preflight?.()
|
|
986
|
-
const scorerVersion = await resolveSweBenchScorerVersion()
|
|
987
|
-
const taskById = new Map((await env.adapter.loadTasks({ ids, split: 'test' })).map((task) => [task.id, task]))
|
|
988
|
-
const missing = ids.filter((id) => !taskById.has(id))
|
|
989
|
-
if (missing.length) throw new Error(`instances not found in SWE-bench_Verified: ${missing.join(', ')}`)
|
|
990
|
-
const first = taskById.get(ids[0]!)!
|
|
991
|
-
const fingerprintTask: AgenticTask = {
|
|
992
|
-
id: first.id,
|
|
993
|
-
systemPrompt: SWE_SEED_PROMPT_WITH_RUN,
|
|
994
|
-
userPrompt: first.prompt,
|
|
995
|
-
meta: { instanceId: first.id },
|
|
996
|
-
}
|
|
997
|
-
const fingerprintHandle: ArtifactHandle = { id: 'fingerprint-only', surface: 'swe-bench-verified' }
|
|
998
|
-
return {
|
|
999
|
-
env,
|
|
1000
|
-
taskById,
|
|
1001
|
-
tools: await env.environment.tools(fingerprintTask, fingerprintHandle),
|
|
1002
|
-
sharedExecution: {
|
|
1003
|
-
runTool: { ...SWE_RUN_TOOL_CONFIG },
|
|
1004
|
-
runtimeImplementationFingerprint: RUNTIME_IMPLEMENTATION_FINGERPRINT,
|
|
1005
|
-
runtimeTreeFingerprint: RUNTIME_TREE_FINGERPRINT,
|
|
1006
|
-
officialScorer: {
|
|
1007
|
-
package: 'swebench',
|
|
1008
|
-
version: scorerVersion,
|
|
1009
|
-
cacheLevel: OFFICIAL_SCORER_CACHE_LEVEL,
|
|
1010
|
-
namespacePolicy: 'phase-a-image',
|
|
1011
|
-
},
|
|
1012
|
-
},
|
|
1013
|
-
expectedImageIdentities,
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
async function generateMain(): Promise<void> {
|
|
1018
|
-
const preset = ARM_PRESET as ExperimentArmPreset
|
|
533
|
+
async function main(): Promise<void> {
|
|
1019
534
|
const ids = process.env.IDS
|
|
1020
|
-
? process.env.IDS.split(',').map((
|
|
535
|
+
? process.env.IDS.split(',').map((s) => s.trim()).filter(Boolean)
|
|
1021
536
|
: await cachedInstanceIds()
|
|
1022
537
|
if (!ids.length) throw new Error('no cached sweb.eval images found and no IDS given')
|
|
1023
|
-
if (new Set(ids).size !== ids.length) throw new Error('IDS contains duplicates')
|
|
1024
|
-
const out = process.env.OUT ?? `swe-stage1-${preset.arm}.phaseA.jsonl`
|
|
1025
|
-
const config = makeExperimentConfig(preset, ids)
|
|
1026
|
-
assertPresetConfig(config, preset.arm, 'generate config')
|
|
1027
|
-
const { env, taskById, tools, sharedExecution, expectedImageIdentities } = await loadTasksAndTools(ids)
|
|
1028
538
|
|
|
1029
|
-
console.log(`═══ SWE-bench
|
|
539
|
+
console.log(`═══ SWE-bench Stage 1 — structural pilot, ARM=${ARM} ═══`)
|
|
1030
540
|
console.log(
|
|
1031
|
-
`
|
|
1032
|
-
`
|
|
541
|
+
`model=${MODEL} base=${ZAI_BASE} maxTokens=${MAX_TOKENS} innerTurns=${INNER_TURNS} ` +
|
|
542
|
+
(ARM === 'system' ? `k=${K} temp=${TEMP} repairs<=${REPAIRS} ` : `temp=${SOLO_TEMP} `) +
|
|
543
|
+
`conc=${CONC} reproTimeout=${REPRO_TIMEOUT_S}s exec=image judge=${SKIP_JUDGE ? 'SKIPPED' : 'phase-B serialized'}`,
|
|
1033
544
|
)
|
|
1034
|
-
console.log(`instances
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
if (!ids.includes(id)) throw new Error(`${out}: resume row ${id} is outside current IDS`)
|
|
1039
|
-
await assertPhaseRow(row, taskById.get(id)!, config, tools, MANIFEST[id], sharedExecution, `${out}:${id}`)
|
|
545
|
+
console.log(`instances (${ids.length}): ${ids.join(', ')}`)
|
|
546
|
+
if (ARM === 'system') {
|
|
547
|
+
const withRepro = ids.filter((id) => MANIFEST[id]).length
|
|
548
|
+
console.log(`repro manifest: ${withRepro}/${ids.length} instances with a Stage-0 valid+sound repro (rest run repro=none)`)
|
|
1040
549
|
}
|
|
1041
|
-
|
|
1042
|
-
if (done.size) console.log(`resume accepted: ${done.size}/${ids.length} fingerprint-matched rows`)
|
|
550
|
+
console.log(`out=${OUT} (phase A receipts: ${PHASE_A_OUT})`)
|
|
1043
551
|
|
|
552
|
+
const env = await createSweBenchEnvironment(ids.length, { ids, cloneCache: true })
|
|
553
|
+
await env.adapter.preflight?.()
|
|
554
|
+
const taskById = new Map((await env.adapter.loadTasks({ ids, split: 'test' })).map((t) => [t.id, t]))
|
|
555
|
+
const missing = ids.filter((id) => !taskById.has(id))
|
|
556
|
+
if (missing.length) throw new Error(`instances not found in SWE-bench_Verified: ${missing.join(', ')}`)
|
|
557
|
+
|
|
558
|
+
// ── Phase A (resumable): every arm decision, no judge anywhere ──
|
|
559
|
+
const done = loadRows(PHASE_A_OUT)
|
|
560
|
+
const todo = ids.filter((id) => !done.has(id))
|
|
561
|
+
if (done.size) console.log(`phase A resume: ${done.size} rows already present, ${todo.length} to run`)
|
|
1044
562
|
let next = 0
|
|
1045
563
|
const worker = async (): Promise<void> => {
|
|
1046
564
|
while (next < todo.length) {
|
|
1047
|
-
const
|
|
1048
|
-
const id = todo[
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
config,
|
|
1052
|
-
tools,
|
|
1053
|
-
sharedExecution,
|
|
1054
|
-
expectedImageIdentities,
|
|
1055
|
-
})
|
|
1056
|
-
await assertPhaseRow(
|
|
1057
|
-
row,
|
|
1058
|
-
taskById.get(id)!,
|
|
1059
|
-
config,
|
|
1060
|
-
tools,
|
|
1061
|
-
MANIFEST[id],
|
|
1062
|
-
sharedExecution,
|
|
1063
|
-
`${preset.arm}:${id}`,
|
|
1064
|
-
)
|
|
565
|
+
const i = next++
|
|
566
|
+
const id = todo[i] as string
|
|
567
|
+
console.log(`[A ${i + 1}/${todo.length}] ${id} …`)
|
|
568
|
+
const row = await phaseA(env, taskById.get(id) as BenchTask)
|
|
1065
569
|
done.set(id, row)
|
|
1066
|
-
appendFileSync(
|
|
570
|
+
appendFileSync(PHASE_A_OUT, `${JSON.stringify(row)}\n`)
|
|
571
|
+
const sel = row.selection ? `${row.selection.mode}@${row.selection.selectedIdx}${row.selection.movedOffFirst ? ' MOVED' : ''}` : '-'
|
|
1067
572
|
console.log(
|
|
1068
|
-
`[${
|
|
1069
|
-
`
|
|
1070
|
-
`
|
|
1071
|
-
`
|
|
573
|
+
`[A ${i + 1}/${todo.length}] ${id} → repro=${row.reproStatus} sel=${sel} ` +
|
|
574
|
+
`sev=[${row.candidates.map((c) => c.severity).join(',')}] repairs=${row.repairs.length}(${row.repairStop ?? '-'}) ` +
|
|
575
|
+
`final=${row.finalFrom} diff=${row.finalDiff.length}b calls=${row.llmCalls} tok=${row.tokensIn}/${row.tokensOut} ` +
|
|
576
|
+
`wall=${Math.round(row.wallMs / 1000)}s${row.error ? ` ERR=${row.error.slice(0, 120)}` : ''}`,
|
|
1072
577
|
)
|
|
1073
578
|
}
|
|
1074
579
|
}
|
|
1075
580
|
await Promise.all(Array.from({ length: CONC }, () => worker()))
|
|
1076
581
|
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
const rows = new Map<string, JudgeRow>()
|
|
1106
|
-
for (const [index, line] of readFileSync(path, 'utf8').split('\n').entries()) {
|
|
1107
|
-
if (!line.trim()) continue
|
|
1108
|
-
const row = JSON.parse(line) as JudgeRow
|
|
1109
|
-
if (row.schema !== 'swe-structural-judge-v2') {
|
|
1110
|
-
throw new Error(`${path}:${index + 1}: unsupported judge row schema`)
|
|
1111
|
-
}
|
|
1112
|
-
if (typeof row.hiddenResolved !== 'boolean') {
|
|
1113
|
-
throw new Error(`${path}:${index + 1}: incomplete judge row must be removed and retried`)
|
|
1114
|
-
}
|
|
1115
|
-
if (row.judgeSkipped !== null && row.judgeSkipped !== 'empty-patch') {
|
|
1116
|
-
throw new Error(`${path}:${index + 1}: unsupported judge skip receipt ${row.judgeSkipped}`)
|
|
582
|
+
// ── Phase B: hidden judge, serialized, strictly after every arm decision locked ──
|
|
583
|
+
console.log(`\nphase A complete — all arm decisions locked (${new Date().toISOString()}).`)
|
|
584
|
+
const judged = loadRows(OUT)
|
|
585
|
+
if (SKIP_JUDGE) {
|
|
586
|
+
console.log('SKIP_JUDGE=1 — phase B not run; rows remain in the phase-A file only.')
|
|
587
|
+
} else {
|
|
588
|
+
console.log('phase B: official swebench judge, serialized (max_workers 1)…')
|
|
589
|
+
for (const id of ids) {
|
|
590
|
+
if (judged.has(id)) continue
|
|
591
|
+
const row = done.get(id) as Row
|
|
592
|
+
const t0 = Date.now()
|
|
593
|
+
if (!row.finalDiff.trim()) {
|
|
594
|
+
row.hiddenResolved = false
|
|
595
|
+
row.judgeSkipped = row.error ? 'error-no-patch' : 'empty-patch'
|
|
596
|
+
} else {
|
|
597
|
+
try {
|
|
598
|
+
const s = await env.adapter.judge(taskById.get(id) as BenchTask, row.finalDiff)
|
|
599
|
+
row.hiddenResolved = s.resolved ?? false
|
|
600
|
+
row.judgeDetail = String(s.detail ?? '').slice(0, 1_000)
|
|
601
|
+
} catch (e) {
|
|
602
|
+
row.hiddenResolved = false
|
|
603
|
+
row.judgeSkipped = `judge-error: ${e instanceof Error ? e.message.slice(0, 200) : String(e).slice(0, 200)}`
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
row.judgeMs = Date.now() - t0
|
|
607
|
+
judged.set(id, row)
|
|
608
|
+
appendFileSync(OUT, `${JSON.stringify(row)}\n`)
|
|
609
|
+
console.log(`[B] ${id} → resolved=${row.hiddenResolved} (${Math.round((row.judgeMs ?? 0) / 1000)}s)${row.judgeSkipped ? ` [${row.judgeSkipped}]` : ''}`)
|
|
1117
610
|
}
|
|
1118
|
-
resolveExperimentArm(row.arm)
|
|
1119
|
-
const key = `${row.arm}:${row.instanceId}`
|
|
1120
|
-
if (rows.has(key)) throw new Error(`${path}:${index + 1}: duplicate judge row ${key}`)
|
|
1121
|
-
rows.set(key, row)
|
|
1122
611
|
}
|
|
1123
|
-
return rows
|
|
1124
|
-
}
|
|
1125
612
|
|
|
1126
|
-
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
const
|
|
1134
|
-
const
|
|
1135
|
-
const
|
|
1136
|
-
|
|
1137
|
-
const
|
|
1138
|
-
const
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
}
|
|
1151
|
-
for (const [path, rows] of [[independentPath, independent], [persistentPath, persistent]] as const) {
|
|
1152
|
-
assertCompleteTaskSet(rows.keys(), ids, path)
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
// No official judge call occurs before every row in both files passes these checks.
|
|
1156
|
-
const { env, taskById, tools, sharedExecution } = await loadTasksAndTools(ids)
|
|
1157
|
-
for (const id of ids) {
|
|
1158
|
-
const left = independent.get(id)!
|
|
1159
|
-
const right = persistent.get(id)!
|
|
1160
|
-
await assertPhaseRow(
|
|
1161
|
-
left,
|
|
1162
|
-
taskById.get(id)!,
|
|
1163
|
-
independentConfig,
|
|
1164
|
-
tools,
|
|
1165
|
-
manifestFromRow(left),
|
|
1166
|
-
sharedExecution,
|
|
1167
|
-
`${independentPath}:${id}`,
|
|
1168
|
-
)
|
|
1169
|
-
await assertPhaseRow(
|
|
1170
|
-
right,
|
|
1171
|
-
taskById.get(id)!,
|
|
1172
|
-
persistentConfig,
|
|
1173
|
-
tools,
|
|
1174
|
-
manifestFromRow(right),
|
|
1175
|
-
sharedExecution,
|
|
1176
|
-
`${persistentPath}:${id}`,
|
|
613
|
+
// ── summary ──
|
|
614
|
+
const rows = [...(SKIP_JUDGE ? done : judged).values()].filter((r) => ids.includes(r.instanceId))
|
|
615
|
+
const n = rows.length
|
|
616
|
+
const resolved = rows.filter((r) => r.hiddenResolved === true).length
|
|
617
|
+
const errors = rows.filter((r) => r.error).length
|
|
618
|
+
const totIn = rows.reduce((s, r) => s + r.tokensIn, 0)
|
|
619
|
+
const totOut = rows.reduce((s, r) => s + r.tokensOut, 0)
|
|
620
|
+
const totCalls = rows.reduce((s, r) => s + r.llmCalls, 0)
|
|
621
|
+
const totHttp = rows.reduce((s, r) => s + r.httpAttempts, 0)
|
|
622
|
+
const totGuard = rows.reduce((s, r) => s + r.guardedMsgs, 0)
|
|
623
|
+
const walls = rows.map((r) => r.wallMs / 1000).sort((a, b) => a - b)
|
|
624
|
+
const q = (p: number): number => walls.length ? (walls[Math.min(walls.length - 1, Math.floor(p * (walls.length - 1)))] as number) : 0
|
|
625
|
+
const usd = (totIn / 1e6) * PRICE_IN + (totOut / 1e6) * PRICE_OUT
|
|
626
|
+
|
|
627
|
+
console.log(`\n══ per-instance (${ARM}) ══`)
|
|
628
|
+
console.log('instance | repro(src) | cand sev | sel | moved | repairs | stop | final | resolved | calls | tokIn/out | wall_s | err')
|
|
629
|
+
for (const r of [...rows].sort((a, b) => a.instanceId.localeCompare(b.instanceId))) {
|
|
630
|
+
const sel = r.selection ? `${r.selection.mode === 'repro-argmax' ? 'argmax' : 'blind'}@${r.selection.selectedIdx}` : '-'
|
|
631
|
+
console.log(
|
|
632
|
+
`${r.instanceId} | ${r.reproStatus}${r.reproSource !== 'none' ? `(${r.reproSource.replace('stage0-', '')})` : ''} | ` +
|
|
633
|
+
`[${r.candidates.map((c) => c.severity).join(',')}] | ${sel} | ${r.selection?.movedOffFirst ? 1 : 0} | ` +
|
|
634
|
+
`${r.repairs.length}${r.repairs.some((x) => x.accepted) ? '+acc' : ''} | ${r.repairStop ?? '-'} | ${r.finalFrom} | ` +
|
|
635
|
+
`${r.hiddenResolved === null ? '?' : r.hiddenResolved ? 1 : 0} | ${r.llmCalls} | ${r.tokensIn}/${r.tokensOut} | ` +
|
|
636
|
+
`${Math.round(r.wallMs / 1000)} | ${r.error ? r.error.slice(0, 60) : '-'}`,
|
|
1177
637
|
)
|
|
1178
|
-
assertPairedFingerprints(left.fingerprints, right.fingerprints, id)
|
|
1179
|
-
assertPairedExecutionFingerprint(left.executionFingerprint, right.executionFingerprint, id)
|
|
1180
|
-
if (left.reproOutcomeFingerprint !== right.reproOutcomeFingerprint) {
|
|
1181
|
-
throw new Error(`${id}: paired reproduction outcomes do not match`)
|
|
1182
|
-
}
|
|
1183
|
-
}
|
|
1184
|
-
|
|
1185
|
-
const phaseFileFingerprints: Record<ExperimentArm, string> = {
|
|
1186
|
-
'independent-2': fingerprint({ bytes: readFileSync(independentPath, 'utf8') }),
|
|
1187
|
-
'persistent-refine-2': fingerprint({ bytes: readFileSync(persistentPath, 'utf8') }),
|
|
1188
|
-
}
|
|
1189
|
-
const pairFingerprint = fingerprint({
|
|
1190
|
-
commonConfig: independent.get(ids[0]!)!.fingerprints.commonConfig,
|
|
1191
|
-
ids,
|
|
1192
|
-
phaseFiles: phaseFileFingerprints,
|
|
1193
|
-
prompt: independent.get(ids[0]!)!.fingerprints.prompt,
|
|
1194
|
-
source: independent.get(ids[0]!)!.fingerprints.source,
|
|
1195
|
-
tools: independent.get(ids[0]!)!.fingerprints.tools,
|
|
1196
|
-
executions: ids.map((id) => independent.get(id)!.executionFingerprint),
|
|
1197
|
-
})
|
|
1198
|
-
const judged = loadJudgeRows(out)
|
|
1199
|
-
const arms: Array<{ arm: ExperimentArm; path: string; rows: Map<string, PhaseARow> }> = [
|
|
1200
|
-
{ arm: 'independent-2', path: independentPath, rows: independent },
|
|
1201
|
-
{ arm: 'persistent-refine-2', path: persistentPath, rows: persistent },
|
|
1202
|
-
]
|
|
1203
|
-
for (const existing of judged.values()) {
|
|
1204
|
-
const input = arms.find(({ arm }) => arm === existing.arm)?.rows.get(existing.instanceId)
|
|
1205
|
-
if (!input) throw new Error(`${out}: judge resume row has no paired Phase-A input`)
|
|
1206
|
-
assertJudgeCompletionMatchesInput(existing, input.finalDiff, `${out}:${existing.arm}:${existing.instanceId}`)
|
|
1207
|
-
assertJudgeResumeFingerprints(existing, {
|
|
1208
|
-
pairFingerprint,
|
|
1209
|
-
phaseFileFingerprint: phaseFileFingerprints[existing.arm],
|
|
1210
|
-
inputFingerprint: input.fingerprints.composite,
|
|
1211
|
-
executionFingerprint: input.executionFingerprint,
|
|
1212
|
-
finalDiffHash: input.finalDiffHash,
|
|
1213
|
-
}, `${out}:${existing.arm}:${existing.instanceId}`)
|
|
1214
638
|
}
|
|
1215
639
|
|
|
1216
|
-
console.log(
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
pairFingerprint,
|
|
1240
|
-
phaseFileFingerprint: phaseFileFingerprints[arm],
|
|
1241
|
-
inputFingerprint: input.fingerprints.composite,
|
|
1242
|
-
executionFingerprint: input.executionFingerprint,
|
|
1243
|
-
finalDiffHash: input.finalDiffHash,
|
|
1244
|
-
hiddenResolved,
|
|
1245
|
-
judgeDetail,
|
|
1246
|
-
judgeMs: Date.now() - started,
|
|
1247
|
-
judgeSkipped,
|
|
1248
|
-
}
|
|
1249
|
-
judged.set(key, row)
|
|
1250
|
-
appendFileSync(out, `${JSON.stringify(row)}\n`)
|
|
1251
|
-
console.log(`[judge] ${key} resolved=${hiddenResolved} skipped=${judgeSkipped ?? '-'}`)
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
640
|
+
console.log(`\n══ summary (${ARM}) ══`)
|
|
641
|
+
console.log(`n=${n} resolved(hidden)=${resolved}/${n} errorRows=${errors}`)
|
|
642
|
+
if (ARM === 'system') {
|
|
643
|
+
const armed = rows.filter((r) => r.reproStatus === 'ok')
|
|
644
|
+
const blind = rows.filter((r) => r.selection?.mode === 'blind-first')
|
|
645
|
+
const moved = rows.filter((r) => r.selection?.movedOffFirst)
|
|
646
|
+
const scored = armed.flatMap((r) => r.candidates)
|
|
647
|
+
const passCands = scored.filter((c) => c.severity === 0)
|
|
648
|
+
const fired = rows.filter((r) => r.repairs.length > 0)
|
|
649
|
+
const accepted = rows.filter((r) => r.repairs.some((x) => x.accepted))
|
|
650
|
+
const failToPass = rows.filter((r) => r.repairStop === 'repaired-pass')
|
|
651
|
+
console.log(`repro: ok=${armed.length} degraded=${rows.filter((r) => r.reproStatus.startsWith('degraded')).length} none=${rows.filter((r) => r.reproStatus === 'none').length}`)
|
|
652
|
+
console.log(`selection: repro-argmax=${armed.length} blind-first=${blind.length} | argmax moved off idx0 on ${moved.length} instance(s)`)
|
|
653
|
+
console.log(`candidates (repro-armed): ${scored.length} scored, ${passCands.length} repro-pass (${scored.length ? ((100 * passCands.length) / scored.length).toFixed(1) : 0}%)`)
|
|
654
|
+
console.log(`repair: fired on ${fired.length} instance(s), accepted(improved) on ${accepted.length}, repro fail→pass on ${failToPass.length}`)
|
|
655
|
+
const stops = new Map<string, number>()
|
|
656
|
+
for (const r of rows) if (r.repairStop) stops.set(r.repairStop, (stops.get(r.repairStop) ?? 0) + 1)
|
|
657
|
+
console.log(`repair stops: ${[...stops.entries()].map(([k, v]) => `${k}=${v}`).join(' ')}`)
|
|
658
|
+
}
|
|
659
|
+
console.log(`leak guard: ${totGuard} system/user messages checked across ${totCalls} completions — 0 trips (a trip throws)`)
|
|
660
|
+
console.log(`cost: tokens in=${totIn} out=${totOut} | llmCalls=${totCalls} httpAttempts=${totHttp} | ` +
|
|
661
|
+
`$${usd.toFixed(2)} @ $${PRICE_IN}/M in + $${PRICE_OUT}/M out (ASSUMED rate — override PRICE_IN/PRICE_OUT)`)
|
|
662
|
+
console.log(`wall per instance: min/med/p90/max = ${Math.round(q(0))}/${Math.round(q(0.5))}/${Math.round(q(0.9))}/${Math.round(q(1))}s sum=${Math.round(walls.reduce((a, b) => a + b, 0))}s`)
|
|
1254
663
|
}
|
|
1255
664
|
|
|
1256
|
-
const main = MODE === 'generate' ? generateMain : judgeOnlyMain
|
|
1257
665
|
main().catch((e) => {
|
|
1258
666
|
console.error(e instanceof Error ? (e.stack ?? e.message) : String(e))
|
|
1259
667
|
process.exit(1)
|