@tangle-network/agent-bench 0.3.7 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/HARNESS.md +43 -0
- package/README.md +7 -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 +5 -5
- package/scripts/trata-hedge/README.md +6 -5
- 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/gate.ts +1 -1
- package/src/hev-eval.mts +5 -2
- package/src/hev-improve.mts +118 -73
- package/src/official-optimizer-config.mts +89 -0
- package/src/official-optimizer-config.test.mts +88 -0
- package/src/profiles.ts +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 +342 -0
- package/src/rollout-ledger/settle-capture.mts +442 -0
- package/src/rollout-ledger/settle-capture.test.mts +270 -0
- package/src/smoke-structural-rollout.mts +15 -9
- package/src/swe-arena/activation.mts +225 -0
- package/src/swe-arena/activation.test.mts +300 -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 +877 -0
- package/src/swe-arena/gepa-seat.test.mts +1059 -0
- package/src/swe-arena/implementation-ref.test.mts +64 -0
- package/src/swe-arena/implementation-ref.ts +62 -0
- package/src/swe-arena/ledger-orphans.mts +77 -0
- package/src/swe-arena/ledger-orphans.test.mts +147 -0
- package/src/swe-arena/outer-loop.mts +710 -33
- 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 +327 -31
- package/src/swe-arena/proposer-fanout.test.mts +246 -0
- package/src/swe-arena/proposer-provenance.mts +172 -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/scratch-worktree.test.mts +55 -0
- package/src/swe-arena/scratch-worktree.ts +34 -0
- package/src/swe-arena/types.ts +52 -0
- package/src/swe-code-improve.mts +24 -25
- package/src/swe-improve.mts +129 -96
- package/src/swe-local-proof.mts +6 -1
- package/src/swe-stream.mts +4 -2
- package/src/swe-structural.mts +245 -837
- package/src/tb-container-executor.test.mts +30 -6
- package/src/tb-supervisor-sidecar.mts +2 -1
- package/src/trata-gepa.mts +182 -245
- package/dist/chunk-LRRD7NAG.js.map +0 -1
- package/src/live-improve-campaign-mbpp.mts +0 -641
- package/src/live-improve-campaign.mts +0 -500
- /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
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
* `rawTraceDistiller` path-context so the coding agent also greps the raw
|
|
18
18
|
* traces itself (`rawTraceContext: true` names the mechanism; an explicit
|
|
19
19
|
* `analyzeGeneration` wins, so the distiller is composed in directly).
|
|
20
|
-
* (b) PROPOSE —
|
|
20
|
+
* (b) PROPOSE — Runtime's code candidate driver + a change-space-constrained
|
|
21
21
|
* `agenticGenerator` edit an isolated git worktree of loops. The DECLARED
|
|
22
22
|
* CHANGE-SPACE is enforced twice: in the generator's verifier (feedback →
|
|
23
23
|
* next shot) and fail-closed in the dispatch below (an out-of-space
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
* generator's evidence gate requires).
|
|
60
60
|
*/
|
|
61
61
|
|
|
62
|
-
import { appendFile, mkdir, readFile, rm, symlink, unlink, writeFile } from 'node:fs/promises'
|
|
62
|
+
import { appendFile, mkdir, readdir, readFile, rm, symlink, unlink, writeFile } from 'node:fs/promises'
|
|
63
63
|
import { existsSync } from 'node:fs'
|
|
64
64
|
import process from 'node:process'
|
|
65
65
|
import { join } from 'node:path'
|
|
@@ -72,6 +72,7 @@ import {
|
|
|
72
72
|
type Verifier,
|
|
73
73
|
} from '@tangle-network/agent-runtime'
|
|
74
74
|
import { runLocalHarness } from '@tangle-network/agent-runtime/mcp'
|
|
75
|
+
import { canonicalCandidateDigest } from '@tangle-network/agent-interface'
|
|
75
76
|
import { makeFinding } from '@tangle-network/agent-eval'
|
|
76
77
|
import {
|
|
77
78
|
FsLabeledScenarioStore,
|
|
@@ -85,7 +86,12 @@ import {
|
|
|
85
86
|
type Scenario,
|
|
86
87
|
} from '@tangle-network/agent-eval/campaign'
|
|
87
88
|
import type { CostLedgerHandle } from '@tangle-network/agent-eval'
|
|
89
|
+
import { runVenvPython } from '../benchmarks/_harness.ts'
|
|
88
90
|
import { createSweBenchAdapter } from '../benchmarks/swe-bench.ts'
|
|
91
|
+
import {
|
|
92
|
+
fileTreeImplementationRef,
|
|
93
|
+
pythonDistributionImplementationRef,
|
|
94
|
+
} from './implementation-ref.ts'
|
|
89
95
|
import {
|
|
90
96
|
baselineDriftWarnings,
|
|
91
97
|
cellsFromCampaign,
|
|
@@ -122,13 +128,46 @@ import {
|
|
|
122
128
|
import {
|
|
123
129
|
defaultProposers,
|
|
124
130
|
fanOutLoopsGenerator,
|
|
131
|
+
materializeParetoParents,
|
|
125
132
|
proposerShotHooks,
|
|
133
|
+
type ParetoParentContext,
|
|
134
|
+
type ParetoParentSeed,
|
|
126
135
|
type PrefilterConfig,
|
|
127
136
|
type PrefilterKill,
|
|
128
137
|
type ProposerSpec,
|
|
129
138
|
type SmokeRunner,
|
|
130
139
|
type SmokeVerdict,
|
|
131
140
|
} from './proposer-fanout.mts'
|
|
141
|
+
import { captureProposerProvenance } from './proposer-provenance.mts'
|
|
142
|
+
import { CRASH_ORPHAN_REASON, reconcileCrashOrphansOnDisk } from './ledger-orphans.mts'
|
|
143
|
+
import {
|
|
144
|
+
AUTHOR_BRIEFING_VERSION,
|
|
145
|
+
resolveAuthorBriefing,
|
|
146
|
+
writeEvidenceIndex,
|
|
147
|
+
type BriefingContext,
|
|
148
|
+
} from './briefing.mts'
|
|
149
|
+
import {
|
|
150
|
+
readCommittedPredicate,
|
|
151
|
+
runActivationPredicate,
|
|
152
|
+
ACTIVATION_PREDICATE_RELPATH,
|
|
153
|
+
type ActivationRecord,
|
|
154
|
+
} from './activation.mts'
|
|
155
|
+
import {
|
|
156
|
+
loadOrCreateScoreSplit,
|
|
157
|
+
subScores,
|
|
158
|
+
type ScoreSplit,
|
|
159
|
+
type ScoreSplitConfig,
|
|
160
|
+
} from './score-split.mts'
|
|
161
|
+
import {
|
|
162
|
+
reportSupervisorRound,
|
|
163
|
+
writeSupervisorRunReportSafe,
|
|
164
|
+
} from '@tangle-network/agent-eval/supervisor-run'
|
|
165
|
+
import {
|
|
166
|
+
campaignCoordsFromCellPath,
|
|
167
|
+
createSettleCapture,
|
|
168
|
+
type SettleCapture,
|
|
169
|
+
} from '../rollout-ledger/settle-capture.mts'
|
|
170
|
+
import { findSupervisorRunDir } from './arms.ts'
|
|
132
171
|
import { installProcessSignalAbort, run, runOk } from './proc.ts'
|
|
133
172
|
import { loadInstanceImages } from './run-experiment.mts'
|
|
134
173
|
import {
|
|
@@ -480,6 +519,16 @@ export interface StaircaseRow {
|
|
|
480
519
|
armProvenance: { repo: string; commit: string } | null
|
|
481
520
|
diffPath: string | null
|
|
482
521
|
diffSha256: string | null
|
|
522
|
+
/** GEN-5 public/private sub-scores (selection stays on the combined count;
|
|
523
|
+
* the private sub-score is never surfaced to proposers). */
|
|
524
|
+
split?: {
|
|
525
|
+
publicInstances: string[]
|
|
526
|
+
privateInstances: string[]
|
|
527
|
+
publicResolvedCount: number
|
|
528
|
+
privateResolvedCount: number
|
|
529
|
+
}
|
|
530
|
+
/** GEN-5 activation-gate outcome for this candidate. */
|
|
531
|
+
activation?: ActivationRecord
|
|
483
532
|
}
|
|
484
533
|
|
|
485
534
|
const STAIRCASE_VERDICTS: ReadonlySet<string> = new Set([
|
|
@@ -489,6 +538,7 @@ const STAIRCASE_VERDICTS: ReadonlySet<string> = new Set([
|
|
|
489
538
|
'rejected-out-of-space',
|
|
490
539
|
'rejected-incomplete',
|
|
491
540
|
'rejected-prefilter',
|
|
541
|
+
'quarantined-inactive',
|
|
492
542
|
])
|
|
493
543
|
|
|
494
544
|
/** Parse + validate one staircase JSONL row. Throws on schema drift. */
|
|
@@ -598,12 +648,22 @@ export interface OuterLoopConfig {
|
|
|
598
648
|
* an AgentProfile-pinned harness invocation (see proposer-fanout.mts).
|
|
599
649
|
* When set, `populationSize` MUST equal `proposers.length` (one candidate
|
|
600
650
|
* slot per proposer — enforced at launch). Unset = the legacy
|
|
601
|
-
* single-author generator (`proposerHarness` + bare invocation).
|
|
651
|
+
* single-author generator (`proposerHarness` + bare invocation).
|
|
652
|
+
* A spec with `engine` set is a GEPA seat (gepa-seat.mts); the
|
|
653
|
+
* agent-eval external-GEPA adapter optimizes ONE change-space file as a
|
|
654
|
+
* string against the pre-filter smoke cell; requires `prefilter.enabled`. */
|
|
602
655
|
proposers?: ProposerSpec[]
|
|
603
656
|
/** GEN-3 cheap pre-filter: per candidate, change-space + tsc (the authoring
|
|
604
657
|
* verifier) plus ONE smoke arm cell before any full-evaluation spend.
|
|
605
658
|
* Killed candidates become `rejected-prefilter` staircase dots. */
|
|
606
659
|
prefilter?: PrefilterConfig
|
|
660
|
+
/** GEN-4 Pareto parents: prior-run frontier candidates (loops commits +
|
|
661
|
+
* measured per-instance results) seeded into every author's prompt and
|
|
662
|
+
* the merge seat's explicit input. Seeded at OUR buildPrompt seam, not the
|
|
663
|
+
* lib's `ctx.paretoParents` — the lib frontier is within-run only and a
|
|
664
|
+
* prior campaign cannot be injected without its runDir + ledger receipts
|
|
665
|
+
* (see proposer-fanout.mts). */
|
|
666
|
+
paretoParents?: ParetoParentSeed[]
|
|
607
667
|
/** Replicates per holdout instance in the operator-approved certification
|
|
608
668
|
* run (holdout-certify.mts). Default 2 — the gen-2 winner failed 3/6 vs
|
|
609
669
|
* 4/6 on a 1-rep holdout with exactly one discordant cell, a known
|
|
@@ -614,6 +674,25 @@ export interface OuterLoopConfig {
|
|
|
614
674
|
* reps/fail-closed protocol, or 'measure' — the incumbent runs the same
|
|
615
675
|
* 2-rep holdout first in the certification run. */
|
|
616
676
|
holdoutBaseline?: Record<string, boolean> | 'measure'
|
|
677
|
+
/** GEN-5 public/private score split (score-split.mts): proposers + the
|
|
678
|
+
* pre-filter see only PUBLIC instances' scores/evidence; selection stays
|
|
679
|
+
* on the combined set. Unset = everything public (pre-gen-5 behavior). */
|
|
680
|
+
scoreSplit?: ScoreSplitConfig
|
|
681
|
+
/** GEN-5 MAP+TOOLBOX briefing (briefing.mts): write the per-run evidence
|
|
682
|
+
* index and append the toolbox/permission briefing (change-space
|
|
683
|
+
* overridable) to every author prompt. */
|
|
684
|
+
briefing?: typeof AUTHOR_BRIEFING_VERSION
|
|
685
|
+
/** GEN-5 activation gate (activation.mts): require a machine-checkable
|
|
686
|
+
* activation predicate per candidate (prefilter-enforced) and quarantine
|
|
687
|
+
* candidates whose mechanism never fired in their own campaign traces. */
|
|
688
|
+
activationGate?: boolean
|
|
689
|
+
/** GEN-5 settle-time rollout-ledger capture (rollout-ledger/settle-capture.mts):
|
|
690
|
+
* emit tangle.rollout.v1 lines live after each cell judges, with label-v2
|
|
691
|
+
* rewards. Default path: <outDir>/rollout-ledger.jsonl. */
|
|
692
|
+
rolloutLedger?: { enabled: boolean; path?: string; opencodeDb?: string }
|
|
693
|
+
/** GEN-5 evidence map: prior run outDirs whose arm-runs/judge/candidate
|
|
694
|
+
* evidence the authors may mine (rendered into the evidence index). */
|
|
695
|
+
priorEvidenceDirs?: string[]
|
|
617
696
|
/** Router model ids for the blind diagnosis ensemble (config, never a
|
|
618
697
|
* hardcoded unrouted model). */
|
|
619
698
|
analystModels: string[]
|
|
@@ -804,6 +883,114 @@ export function defaultGen3Config(
|
|
|
804
883
|
}
|
|
805
884
|
}
|
|
806
885
|
|
|
886
|
+
// ---------------------------------------------------------------------------
|
|
887
|
+
// GEN-4 configuration — pinned per-proposer models (recorded in provenance),
|
|
888
|
+
// Pareto-parent seeding from the gen-3 frontier, and a dedicated merge seat.
|
|
889
|
+
// ---------------------------------------------------------------------------
|
|
890
|
+
|
|
891
|
+
/** The gen-3 frontier (run r4-mrwc0awe): winner + runner-up, both 2/6 vs the
|
|
892
|
+
* 1/6 baseline on DIFFERENT instances — complementary lessons, the merge
|
|
893
|
+
* seat's input. Per-instance verdicts are the fail-closed all-reps values
|
|
894
|
+
* from `.evolve/rounds/gen-0.jsonl`. */
|
|
895
|
+
export const GEN3_PARETO_PARENTS: ParetoParentSeed[] = [
|
|
896
|
+
{
|
|
897
|
+
commit: 'cc0d95584c7ea14324cd57c21fe946c7c0f53827',
|
|
898
|
+
label: 'default-author',
|
|
899
|
+
resolvedInstances: ['pydata__xarray-4687', 'sphinx-doc__sphinx-9658'],
|
|
900
|
+
note:
|
|
901
|
+
'mechanical patch-risk scan (patchRiskWarnings in src/worker-evidence.ts, threaded through ' +
|
|
902
|
+
'extensions/pi/loops.ts) + never-reword / test-seam / run-the-neighbors worker rules + 3-check ' +
|
|
903
|
+
'reviewer; pytest-dev__pytest-6197 split 0/1 across reps (near-miss)',
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
commit: 'a7a2a982e51551de3a8e796ee2efc448ed405e6a',
|
|
907
|
+
label: 'prompts-author',
|
|
908
|
+
resolvedInstances: ['django__django-11532', 'pydata__xarray-4687'],
|
|
909
|
+
note:
|
|
910
|
+
'prompt-only: hidden-suite bullet in the supervisor GOAL-authoring section (the django seam), ' +
|
|
911
|
+
'frozen-behavior worker section + run-the-repo-tests discipline, 2-check reviewer; ' +
|
|
912
|
+
'sphinx-doc__sphinx-9658 split 0/1 across reps (near-miss)',
|
|
913
|
+
},
|
|
914
|
+
]
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* The gen-4 config: protocol round 4 continues (frozen arm, same holdout
|
|
918
|
+
* registry, same roundsDir staircase) with three changes as a unit:
|
|
919
|
+
*
|
|
920
|
+
* 1. PINNED PER-PROPOSER MODELS — four seats: claude-author (claude CLI on
|
|
921
|
+
* its own login; the resolved model + CLI version are captured into
|
|
922
|
+
* `<outDir>/proposer-provenance.json` at t=0), glm-author (opencode
|
|
923
|
+
* pinned to zai-coding-plan/glm-5.2 via `-m`), codex-author (codex CLI on
|
|
924
|
+
* its ChatGPT login, auth provenance-gated at launch; drop the seat via
|
|
925
|
+
* `includeCodex: false` when the CLI is absent), and merge-author (claude,
|
|
926
|
+
* merge seat).
|
|
927
|
+
* 2. PARETO PARENTS — the gen-3 winner + runner-up diffs and their measured
|
|
928
|
+
* per-instance results seed every author's prompt; the merge seat's task
|
|
929
|
+
* is their coherent union. Seeded at the buildPrompt seam (our seam): the
|
|
930
|
+
* lib's `ctx.paretoParents` frontier is within-run only, and a prior
|
|
931
|
+
* campaign cannot cross runs without its runDir + ledger receipts.
|
|
932
|
+
* 3. PINNED BASELINE — the premeasured artifact at `hh/gen4/` is BUILT from
|
|
933
|
+
* gen-3's measured baseline cells (premeasured-from-cells.mts; gen-3
|
|
934
|
+
* measured astropy F, django F, matplotlib F, xarray F, pytest F,
|
|
935
|
+
* sphinx T — matplotlib/django false under current weather), so gen-4
|
|
936
|
+
* spends nothing re-measuring and fails loud if the loops tip moved.
|
|
937
|
+
*/
|
|
938
|
+
export function defaultGen4Config(
|
|
939
|
+
hh = DEFAULT_HH_SCRATCHPAD,
|
|
940
|
+
opts: { outDirName?: string; includeCodex?: boolean } = {},
|
|
941
|
+
): OuterLoopConfig {
|
|
942
|
+
const base = defaultGen3Config(hh, { outDirName: opts.outDirName ?? 'gen4' })
|
|
943
|
+
const proposers: ProposerSpec[] = [
|
|
944
|
+
{ name: 'claude-author', profile: 'default-author.profile.json', harness: 'claude' },
|
|
945
|
+
{ name: 'glm-author', harness: 'opencode', model: 'zai-coding-plan/glm-5.2' },
|
|
946
|
+
...(opts.includeCodex === false ? [] : [{ name: 'codex-author', harness: 'codex' } satisfies ProposerSpec]),
|
|
947
|
+
{ name: 'merge-author', profile: 'default-author.profile.json', harness: 'claude', merge: true },
|
|
948
|
+
]
|
|
949
|
+
return {
|
|
950
|
+
...base,
|
|
951
|
+
populationSize: proposers.length,
|
|
952
|
+
proposers,
|
|
953
|
+
paretoParents: [...GEN3_PARETO_PARENTS],
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
// ---------------------------------------------------------------------------
|
|
958
|
+
// GEN-5 configuration — gen-4's shape (4 proposers incl. the merge seat,
|
|
959
|
+
// Pareto parents, premeasured baseline carried forward per the same
|
|
960
|
+
// cell-derivation, 2 reps, deferred holdout) PLUS the gen-5 integration
|
|
961
|
+
// bundle as a unit:
|
|
962
|
+
//
|
|
963
|
+
// 1. MAP+TOOLBOX briefing — per-run evidence index + toolbox/permission
|
|
964
|
+
// briefing (change-space overridable at extensions/pi/author-briefing.md);
|
|
965
|
+
// the 3-analyst diagnosis stays as ONE input among the named tools.
|
|
966
|
+
// 2. PUBLIC/PRIVATE SPLIT — 4 public / 2 private of the 6 instances,
|
|
967
|
+
// deterministically seeded by runId and persisted per outDir; proposers +
|
|
968
|
+
// prefilter see public only, selection stays combined. Small-n caveat
|
|
969
|
+
// documented in score-split.mts.
|
|
970
|
+
// 3. ACTIVATION GATE — required machine-checkable predicate per candidate;
|
|
971
|
+
// never-fired mechanisms are quarantined even on an improved score.
|
|
972
|
+
// 4. SETTLE-TIME ROLLOUT LEDGER — tangle.rollout.v1 lines live per cell,
|
|
973
|
+
// label v2 (contribution-aware workers, baseline-relative proposers).
|
|
974
|
+
// ---------------------------------------------------------------------------
|
|
975
|
+
|
|
976
|
+
export function defaultGen5Config(
|
|
977
|
+
hh = DEFAULT_HH_SCRATCHPAD,
|
|
978
|
+
opts: { outDirName?: string; includeCodex?: boolean } = {},
|
|
979
|
+
): OuterLoopConfig {
|
|
980
|
+
const base = defaultGen4Config(hh, {
|
|
981
|
+
outDirName: opts.outDirName ?? 'gen5',
|
|
982
|
+
...(opts.includeCodex !== undefined ? { includeCodex: opts.includeCodex } : {}),
|
|
983
|
+
})
|
|
984
|
+
return {
|
|
985
|
+
...base,
|
|
986
|
+
scoreSplit: { publicCount: 4 },
|
|
987
|
+
briefing: AUTHOR_BRIEFING_VERSION,
|
|
988
|
+
activationGate: true,
|
|
989
|
+
rolloutLedger: { enabled: true },
|
|
990
|
+
priorEvidenceDirs: [join(hh, 'gen4'), join(hh, 'gen3')],
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
807
994
|
// ---------------------------------------------------------------------------
|
|
808
995
|
// Round recorder — dispatch-time change-space fail-closed + diff writing ONLY.
|
|
809
996
|
// NOT a scoring source: scoring reads the lib's campaign cells
|
|
@@ -1059,11 +1246,21 @@ export function loopsCandidateVerifier(loopsRepo: string): Verifier {
|
|
|
1059
1246
|
* only — the rest of the run keeps its env untouched. */
|
|
1060
1247
|
const CLAUDE_AMBIENT_AUTH_VARS = ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_BASE_URL'] as const
|
|
1061
1248
|
|
|
1249
|
+
/** Same failure class for the gen-4 codex seat: agent-state.env injects an
|
|
1250
|
+
* OPENAI_API_KEY meant for other tooling, and the codex CLI prefers env-key
|
|
1251
|
+
* auth over its ChatGPT login. The codex author shot must run on the CLI's
|
|
1252
|
+
* own login (`codex login status` is provenance-gated at launch), so the
|
|
1253
|
+
* leaked auth is stripped for the shot subprocess only. */
|
|
1254
|
+
const CODEX_AMBIENT_AUTH_VARS = ['OPENAI_API_KEY', 'OPENAI_BASE_URL'] as const
|
|
1255
|
+
|
|
1062
1256
|
export function proposerShotEnv(harness: OuterLoopConfig['proposerHarness']): NodeJS.ProcessEnv {
|
|
1063
1257
|
const env: NodeJS.ProcessEnv = { ...process.env }
|
|
1064
1258
|
if (harness === 'claude') {
|
|
1065
1259
|
for (const name of CLAUDE_AMBIENT_AUTH_VARS) delete env[name]
|
|
1066
1260
|
}
|
|
1261
|
+
if (harness === 'codex') {
|
|
1262
|
+
for (const name of CODEX_AMBIENT_AUTH_VARS) delete env[name]
|
|
1263
|
+
}
|
|
1067
1264
|
return env
|
|
1068
1265
|
}
|
|
1069
1266
|
|
|
@@ -1151,6 +1348,33 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1151
1348
|
// premeasured baseline and pin the depth dial (see capabilities.mts).
|
|
1152
1349
|
assertSubstratePassthroughs(log)
|
|
1153
1350
|
|
|
1351
|
+
// GEN-4 model-identity provenance at t=0: harness CLI versions, the claude
|
|
1352
|
+
// seat's resolved settings model, codex auth, and every explicit model pin.
|
|
1353
|
+
// Fails loud on a missing/unauthed harness binary — populationSize equals
|
|
1354
|
+
// proposers.length, so a dead seat cannot be skipped mid-run.
|
|
1355
|
+
if (config.proposers !== undefined) {
|
|
1356
|
+
const provenance = await captureProposerProvenance(config.proposers)
|
|
1357
|
+
await mkdir(config.outDir, { recursive: true })
|
|
1358
|
+
await writeFile(join(config.outDir, 'proposer-provenance.json'), JSON.stringify(provenance, null, 2))
|
|
1359
|
+
for (const p of provenance.proposers) {
|
|
1360
|
+
log(
|
|
1361
|
+
`proposer ${p.name} (${p.harness}${p.merge ? ', merge seat' : ''}): ` +
|
|
1362
|
+
`model=${p.pinnedModel ?? `cli-default${p.settingsModel ? `:${p.settingsModel}` : ''}`} ` +
|
|
1363
|
+
`version=${p.harnessVersion.split('\n')[0]}`,
|
|
1364
|
+
)
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
// GEN-4 Pareto parents: materialize the configured prior-run frontier
|
|
1369
|
+
// (commit existence + full diffs) before any authoring.
|
|
1370
|
+
const paretoParents: ParetoParentContext[] =
|
|
1371
|
+
config.paretoParents !== undefined && config.paretoParents.length > 0
|
|
1372
|
+
? await materializeParetoParents(config.loopsRepo, config.paretoParents)
|
|
1373
|
+
: []
|
|
1374
|
+
if (paretoParents.length > 0) {
|
|
1375
|
+
log(`pareto parents: ${paretoParents.map((p) => `${p.label}@${p.commit.slice(0, 10)}`).join(', ')}`)
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1154
1378
|
// The gate's only denominator: a stored prior baseline campaign the LIB
|
|
1155
1379
|
// validates (surface hash, seed, reps, split digest, coverage) before
|
|
1156
1380
|
// skipping the baseline campaign. A missing artifact = the BOOTSTRAP run —
|
|
@@ -1179,14 +1403,41 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1179
1403
|
const excludes = await loadExcludes()
|
|
1180
1404
|
const images = await loadInstanceImages(config.instanceImagesPath)
|
|
1181
1405
|
const adapter = createSweBenchAdapter()
|
|
1406
|
+
const runId = `r${config.round}-${Date.now().toString(36)}`
|
|
1407
|
+
await mkdir(config.outDir, { recursive: true })
|
|
1408
|
+
|
|
1409
|
+
// GEN-5 public/private split — deterministic (seeded by runId), PERSISTED
|
|
1410
|
+
// per outDir so a resume can never rotate private instances into view.
|
|
1411
|
+
// Scored identically; selection stays combined; proposers + prefilter see
|
|
1412
|
+
// public only.
|
|
1413
|
+
const split: ScoreSplit | null =
|
|
1414
|
+
config.scoreSplit !== undefined
|
|
1415
|
+
? await loadOrCreateScoreSplit({
|
|
1416
|
+
outDir: config.outDir,
|
|
1417
|
+
runId,
|
|
1418
|
+
instances: config.instances,
|
|
1419
|
+
publicCount: config.scoreSplit.publicCount,
|
|
1420
|
+
})
|
|
1421
|
+
: null
|
|
1422
|
+
const privateIids = new Set(split?.privateInstances ?? [])
|
|
1423
|
+
if (split !== null) {
|
|
1424
|
+
log(
|
|
1425
|
+
`score split (seeded by ${split.seededBy}): public [${split.publicInstances.join(', ')}] + ` +
|
|
1426
|
+
`${split.privateInstances.length} private instance(s) (identities withheld from proposers; ` +
|
|
1427
|
+
`selection uses public+private combined; small-n caveat: 2 private of 6 is a direction check, not certification)`,
|
|
1428
|
+
)
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1182
1431
|
// The pre-filter's smoke instance may sit outside the improvement set (e.g.
|
|
1183
1432
|
// a designated cheap instance) — it needs the same problem/image/verify
|
|
1184
|
-
// validation and rides the same loaded-task map.
|
|
1433
|
+
// validation and rides the same loaded-task map. Under the gen-5 split the
|
|
1434
|
+
// smoke choice is restricted to PUBLIC instances (the prefilter surfaces
|
|
1435
|
+
// its verdict to the kill log the authors can mine).
|
|
1185
1436
|
const smokeIid =
|
|
1186
1437
|
config.proposers !== undefined && config.prefilter?.enabled
|
|
1187
1438
|
? resolveSmokeInstance(
|
|
1188
1439
|
config.prefilter.smokeInstance,
|
|
1189
|
-
config.instances,
|
|
1440
|
+
split !== null ? split.publicInstances : config.instances,
|
|
1190
1441
|
premeasured ? cellsFromCampaign(premeasured.campaign) : null,
|
|
1191
1442
|
)
|
|
1192
1443
|
: null
|
|
@@ -1208,12 +1459,57 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1208
1459
|
const judge: SerializedJudge = createSerializedJudge(
|
|
1209
1460
|
config.judgeTimeoutMs !== undefined ? { timeoutMs: config.judgeTimeoutMs } : {},
|
|
1210
1461
|
)
|
|
1211
|
-
const runId = `r${config.round}-${Date.now().toString(36)}`
|
|
1212
|
-
await mkdir(config.outDir, { recursive: true })
|
|
1213
1462
|
await mkdir(config.roundsDir, { recursive: true })
|
|
1214
1463
|
const recorder = new RoundRecorder(config.loopsRepo, join(config.outDir, 'candidates'))
|
|
1215
1464
|
const analysts: AnalystSpec[] = config.analystModels.map((model, i) => ({ id: `${model}#${i + 1}`, model }))
|
|
1216
1465
|
|
|
1466
|
+
// GEN-5 MAP+TOOLBOX briefing: persist the Pareto parent diffs, write the
|
|
1467
|
+
// per-run evidence index (a map — one line per evidence path, private
|
|
1468
|
+
// instances excluded), and resolve the briefing text (the change-space
|
|
1469
|
+
// override at extensions/pi/author-briefing.md wins over the default).
|
|
1470
|
+
let briefingCtx: BriefingContext | undefined
|
|
1471
|
+
if (config.briefing === AUTHOR_BRIEFING_VERSION) {
|
|
1472
|
+
const parentPatches: Array<{ label: string; path: string }> = []
|
|
1473
|
+
if (paretoParents.length > 0) {
|
|
1474
|
+
const parentsDir = join(config.outDir, 'pareto-parents')
|
|
1475
|
+
await mkdir(parentsDir, { recursive: true })
|
|
1476
|
+
for (const parent of paretoParents) {
|
|
1477
|
+
const patchPath = join(parentsDir, `${parent.label}.patch`)
|
|
1478
|
+
await writeFile(patchPath, parent.diff)
|
|
1479
|
+
parentPatches.push({ label: parent.label, path: patchPath })
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
const index = await writeEvidenceIndex({
|
|
1483
|
+
outDir: config.outDir,
|
|
1484
|
+
roundsDir: config.roundsDir,
|
|
1485
|
+
seedArtifactRuns: config.seedArtifactRuns,
|
|
1486
|
+
...(config.priorEvidenceDirs !== undefined ? { priorEvidenceDirs: config.priorEvidenceDirs } : {}),
|
|
1487
|
+
paretoParentPatches: parentPatches,
|
|
1488
|
+
split,
|
|
1489
|
+
})
|
|
1490
|
+
const briefing = await resolveAuthorBriefing(config.loopsRepo, config.loopsBaseRef)
|
|
1491
|
+
briefingCtx = { indexPath: index.path, briefingText: briefing.text, briefingSource: briefing.source }
|
|
1492
|
+
log(
|
|
1493
|
+
`briefing ${AUTHOR_BRIEFING_VERSION}: evidence index → ${index.path} (${index.rows.length} row(s)); ` +
|
|
1494
|
+
`briefing text source: ${briefing.source}`,
|
|
1495
|
+
)
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
// GEN-5 settle-time rollout ledger — tangle.rollout.v1 lines appended live
|
|
1499
|
+
// after each cell judges (label v2); capture failure logs loud but never
|
|
1500
|
+
// kills a cell.
|
|
1501
|
+
const settleCapture: SettleCapture | null =
|
|
1502
|
+
config.rolloutLedger?.enabled === true
|
|
1503
|
+
? createSettleCapture({
|
|
1504
|
+
ledgerPath: config.rolloutLedger.path ?? join(config.outDir, 'rollout-ledger.jsonl'),
|
|
1505
|
+
runId,
|
|
1506
|
+
instanceCount: config.instances.length,
|
|
1507
|
+
...(config.rolloutLedger.opencodeDb !== undefined ? { opencodeDb: config.rolloutLedger.opencodeDb } : {}),
|
|
1508
|
+
log,
|
|
1509
|
+
})
|
|
1510
|
+
: null
|
|
1511
|
+
if (settleCapture !== null) log(`rollout-ledger: settle-time capture ON → ${settleCapture.path}`)
|
|
1512
|
+
|
|
1217
1513
|
const sweScenarios: Scenario[] = config.instances.map((iid) => ({ id: iid, kind: 'swe-instance' }))
|
|
1218
1514
|
|
|
1219
1515
|
// Capacity gates on BOTH paths the supervisor arm rides (worker + router).
|
|
@@ -1238,11 +1534,24 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1238
1534
|
const smokeRunner: SmokeRunner | undefined =
|
|
1239
1535
|
smokeIid === null
|
|
1240
1536
|
? undefined
|
|
1241
|
-
: async ({
|
|
1537
|
+
: async ({
|
|
1538
|
+
scratchPath,
|
|
1539
|
+
generation,
|
|
1540
|
+
proposer,
|
|
1541
|
+
evaluationKey,
|
|
1542
|
+
costLedger,
|
|
1543
|
+
}): Promise<SmokeVerdict> => {
|
|
1242
1544
|
const iid = smokeIid
|
|
1545
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(evaluationKey)) {
|
|
1546
|
+
throw new Error(`outer-loop: invalid smoke evaluation key ${JSON.stringify(evaluationKey)}`)
|
|
1547
|
+
}
|
|
1243
1548
|
const requireResolved = config.prefilter?.requireResolved === true
|
|
1244
1549
|
const entry = images[iid]!
|
|
1245
|
-
const armOutDir = join(
|
|
1550
|
+
const armOutDir = join(
|
|
1551
|
+
config.outDir,
|
|
1552
|
+
'prefilter-smoke',
|
|
1553
|
+
`gen${generation}-${proposer.name}-${evaluationKey}`,
|
|
1554
|
+
)
|
|
1246
1555
|
const nm = join(scratchPath, 'node_modules')
|
|
1247
1556
|
let linked = false
|
|
1248
1557
|
const t0 = Date.now()
|
|
@@ -1301,7 +1610,7 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1301
1610
|
const paid = await costLedger.runPaidCall({
|
|
1302
1611
|
channel: 'agent',
|
|
1303
1612
|
phase: 'search.prefilter',
|
|
1304
|
-
actor: `prefilter-smoke:${iid}:g${generation}:${proposer.name}`,
|
|
1613
|
+
actor: `prefilter-smoke:${iid}:g${generation}:${proposer.name}:${evaluationKey}`,
|
|
1305
1614
|
model: config.arm.workerModel,
|
|
1306
1615
|
execute: runWork,
|
|
1307
1616
|
receipt: ({ armRes }) => {
|
|
@@ -1337,6 +1646,8 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1337
1646
|
resolved: outcome.resolved,
|
|
1338
1647
|
patchLines: outcome.armRes.patch_lines,
|
|
1339
1648
|
wallS,
|
|
1649
|
+
// The GEPA seat's inner-score tiebreak.
|
|
1650
|
+
verifyPass: outcome.armRes.verify_pass,
|
|
1340
1651
|
}
|
|
1341
1652
|
await mkdir(armOutDir, { recursive: true })
|
|
1342
1653
|
await writeFile(join(armOutDir, 'smoke.json'), JSON.stringify(result, null, 2))
|
|
@@ -1360,6 +1671,79 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1360
1671
|
}
|
|
1361
1672
|
}
|
|
1362
1673
|
|
|
1674
|
+
let runnerImplementationRef: string | undefined
|
|
1675
|
+
let judgeImplementationRef: string | undefined
|
|
1676
|
+
const hasGepaSeat = config.proposers?.some((proposer) => proposer.engine !== undefined) === true
|
|
1677
|
+
if (hasGepaSeat && smokeIid !== null && smokeRunner !== undefined) {
|
|
1678
|
+
const runtimeRoot = fileURLToPath(new URL('../../..', import.meta.url))
|
|
1679
|
+
const benchRoot = fileURLToPath(new URL('../..', import.meta.url))
|
|
1680
|
+
const sourceCommit = (
|
|
1681
|
+
await runOk('git', ['-C', config.loopsRepo, 'rev-parse', 'HEAD'])
|
|
1682
|
+
).stdout.trim()
|
|
1683
|
+
const entry = images[smokeIid]!
|
|
1684
|
+
const verifyScript = await readFile(join(config.verifyDir, `${smokeIid}.sh`), 'utf8')
|
|
1685
|
+
const implementationArtifacts = {
|
|
1686
|
+
benchSource: await fileTreeImplementationRef(join(benchRoot, 'src')),
|
|
1687
|
+
runtimeSource: await fileTreeImplementationRef(join(runtimeRoot, 'src')),
|
|
1688
|
+
runtimeDist: await fileTreeImplementationRef(join(runtimeRoot, 'dist')),
|
|
1689
|
+
packageFiles: canonicalCandidateDigest({
|
|
1690
|
+
runtimePackage: await readFile(join(runtimeRoot, 'package.json'), 'utf8'),
|
|
1691
|
+
benchPackage: await readFile(join(benchRoot, 'package.json'), 'utf8'),
|
|
1692
|
+
lockfile: await readFile(join(runtimeRoot, 'pnpm-lock.yaml'), 'utf8'),
|
|
1693
|
+
}),
|
|
1694
|
+
swebench: await pythonDistributionImplementationRef(
|
|
1695
|
+
'swebench',
|
|
1696
|
+
(script, args) => runVenvPython(script, args),
|
|
1697
|
+
),
|
|
1698
|
+
}
|
|
1699
|
+
const runnerSourceRef = canonicalCandidateDigest({
|
|
1700
|
+
benchSource: implementationArtifacts.benchSource,
|
|
1701
|
+
runtimeSource: implementationArtifacts.runtimeSource,
|
|
1702
|
+
runtimeDist: implementationArtifacts.runtimeDist,
|
|
1703
|
+
packageFiles: implementationArtifacts.packageFiles,
|
|
1704
|
+
})
|
|
1705
|
+
const judgeSourceRef = canonicalCandidateDigest({
|
|
1706
|
+
benchSource: implementationArtifacts.benchSource,
|
|
1707
|
+
packageFiles: implementationArtifacts.packageFiles,
|
|
1708
|
+
swebench: implementationArtifacts.swebench,
|
|
1709
|
+
})
|
|
1710
|
+
runnerImplementationRef = canonicalCandidateDigest({
|
|
1711
|
+
implementation: 'swe-arena-smoke-runner',
|
|
1712
|
+
sourceCommit,
|
|
1713
|
+
sourceRef: runnerSourceRef,
|
|
1714
|
+
smoke: {
|
|
1715
|
+
iid: smokeIid,
|
|
1716
|
+
image: entry.image,
|
|
1717
|
+
baseCommit: entry.base_commit,
|
|
1718
|
+
problemStatement: problemById.get(smokeIid)!,
|
|
1719
|
+
verifyScript,
|
|
1720
|
+
requireResolved: config.prefilter?.requireResolved === true,
|
|
1721
|
+
},
|
|
1722
|
+
arm: {
|
|
1723
|
+
name: config.armName,
|
|
1724
|
+
workerModel: config.arm.workerModel,
|
|
1725
|
+
driverModel: config.arm.driverModel,
|
|
1726
|
+
budget: config.arm.budget,
|
|
1727
|
+
maxSandboxes: config.arm.maxSandboxes,
|
|
1728
|
+
maxUsd: config.arm.maxUsd,
|
|
1729
|
+
maxDepth: config.arm.maxDepth,
|
|
1730
|
+
timeoutMs: config.arm.timeoutMs,
|
|
1731
|
+
envKnobs: config.arm.envKnobs ?? null,
|
|
1732
|
+
},
|
|
1733
|
+
dispatchTimeoutMs: config.dispatchTimeoutMs,
|
|
1734
|
+
capacityModel: config.capacityModel ?? null,
|
|
1735
|
+
gateWaitCeilingMs: config.gateWaitCeilingMs ?? null,
|
|
1736
|
+
excludes,
|
|
1737
|
+
})
|
|
1738
|
+
judgeImplementationRef = canonicalCandidateDigest({
|
|
1739
|
+
implementation: 'serialized-swebench-judge',
|
|
1740
|
+
sourceCommit,
|
|
1741
|
+
sourceRef: judgeSourceRef,
|
|
1742
|
+
timeoutMs: config.judgeTimeoutMs ?? JUDGE_TIMEOUT_FLOOR_MS,
|
|
1743
|
+
cacheLevel: 'instance',
|
|
1744
|
+
})
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1363
1747
|
// ── dispatch: one (surface × scenario) cell ──────────────────────────
|
|
1364
1748
|
const agent = async (surface: MutableSurface, scenario: Scenario, ctx: DispatchContext): Promise<R4Artifact> => {
|
|
1365
1749
|
const cs = asCodeSurface(surface)
|
|
@@ -1431,6 +1815,14 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1431
1815
|
await writeFile(join(runDir, 'judge.json'), JSON.stringify({ ...verdict, wallS: judgeWallS }, null, 1))
|
|
1432
1816
|
log(`${config.armName} ${rec.tag} ${iid} judged: resolved=${verdict.resolved} (attempts=${verdict.attempts})`)
|
|
1433
1817
|
|
|
1818
|
+
// Deterministic run observability, per cell: steer count, waves, concurrency,
|
|
1819
|
+
// idle, evidence→respawn, cost by role. The headline lands in the run log so
|
|
1820
|
+
// the answers are in the tail without a follow-up command.
|
|
1821
|
+
await writeSupervisorRunReportSafe(runDir, {
|
|
1822
|
+
appendHeadlineTo: join(config.outDir, 'run.log'),
|
|
1823
|
+
patchPath: armRes.patchPath,
|
|
1824
|
+
})
|
|
1825
|
+
|
|
1434
1826
|
const spend = armRes.recoveredSpend
|
|
1435
1827
|
const recovered =
|
|
1436
1828
|
armRes.spentTokens === null && (spend?.workerTokSqlite ?? null) === null
|
|
@@ -1454,7 +1846,61 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1454
1846
|
recoveredTokens: recovered,
|
|
1455
1847
|
}) + '\n',
|
|
1456
1848
|
)
|
|
1457
|
-
await ctx.artifacts.writeJson('arm-summary.json', {
|
|
1849
|
+
const summaryPath = await ctx.artifacts.writeJson('arm-summary.json', {
|
|
1850
|
+
runDir,
|
|
1851
|
+
patchPath: armRes.patchPath,
|
|
1852
|
+
verdict,
|
|
1853
|
+
})
|
|
1854
|
+
|
|
1855
|
+
// GEN-5 settle-time rollout capture: emit supervisor + worker lines
|
|
1856
|
+
// NOW, while the opencode store still holds the worker transcripts.
|
|
1857
|
+
// Attribution comes from the campaign cell path (never dispatch
|
|
1858
|
+
// order); a capture failure logs loud but never kills the cell.
|
|
1859
|
+
if (settleCapture !== null) {
|
|
1860
|
+
try {
|
|
1861
|
+
const coords = campaignCoordsFromCellPath(summaryPath)
|
|
1862
|
+
if (coords === null) {
|
|
1863
|
+
log(`rollout-ledger: cannot derive campaign coords from ${summaryPath} — cell ${iid} r${ctx.rep} skipped`)
|
|
1864
|
+
} else {
|
|
1865
|
+
const supRunDir = await findSupervisorRunDir(armRes.ws)
|
|
1866
|
+
const deliveredPatch = await readFile(armRes.patchPath, 'utf8').catch(() => '')
|
|
1867
|
+
await settleCapture.captureCell({
|
|
1868
|
+
generation: coords.generation,
|
|
1869
|
+
candidateIndex: coords.candidateIndex,
|
|
1870
|
+
iid,
|
|
1871
|
+
rep: ctx.rep,
|
|
1872
|
+
seed: ctx.seed,
|
|
1873
|
+
splitVisibility: split === null ? null : privateIids.has(iid) ? 'private' : 'public',
|
|
1874
|
+
commit: cs.candidateCommit,
|
|
1875
|
+
resolved: verdict.resolved,
|
|
1876
|
+
judgeVerdict: { ...verdict, wallS: judgeWallS },
|
|
1877
|
+
runDir,
|
|
1878
|
+
patchPath: armRes.patchPath,
|
|
1879
|
+
supRunDir,
|
|
1880
|
+
deliveredPatch,
|
|
1881
|
+
workerModel: config.arm.workerModel,
|
|
1882
|
+
metrics: {
|
|
1883
|
+
resolved: verdict.resolved,
|
|
1884
|
+
verify_pass: armRes.verify_pass,
|
|
1885
|
+
patch_lines: armRes.patch_lines,
|
|
1886
|
+
judge_attempts: verdict.attempts ?? null,
|
|
1887
|
+
judge_wall_s: judgeWallS,
|
|
1888
|
+
spent_tokens: armRes.spentTokens,
|
|
1889
|
+
spent_usd: armRes.spentUsd,
|
|
1890
|
+
recovered_tokens: recovered,
|
|
1891
|
+
sup_status: armRes.sup_status,
|
|
1892
|
+
sup_verdict: armRes.sup_verdict,
|
|
1893
|
+
spawned: armRes.spawned,
|
|
1894
|
+
workers: armRes.workers,
|
|
1895
|
+
settled: armRes.settled,
|
|
1896
|
+
},
|
|
1897
|
+
cost: { usd: armRes.spentUsd, wallS: armRes.wall_s, spentTokens: armRes.spentTokens },
|
|
1898
|
+
})
|
|
1899
|
+
}
|
|
1900
|
+
} catch (cause) {
|
|
1901
|
+
log(`rollout-ledger: settle-time capture FAILED for ${iid} r${ctx.rep}: ${(cause as Error).message}`)
|
|
1902
|
+
}
|
|
1903
|
+
}
|
|
1458
1904
|
|
|
1459
1905
|
if (verdict.resolved === null) {
|
|
1460
1906
|
// Inconclusive judge (double flake / infra) — the cell must FAIL, not
|
|
@@ -1557,6 +2003,7 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1557
2003
|
const runs: SupRunArtifacts[] = []
|
|
1558
2004
|
if (input.generation === -1) {
|
|
1559
2005
|
for (const seed of config.seedArtifactRuns) {
|
|
2006
|
+
if (privateIids.has(seed.iid)) continue // gen-5 split: never surfaced to proposers
|
|
1560
2007
|
if (!existsSync(seed.dir)) {
|
|
1561
2008
|
// A wiped scratchpad (host reboot) must not feed EMPTY bundles to the
|
|
1562
2009
|
// analysts as if they were real artifacts — skip loudly.
|
|
@@ -1581,6 +2028,7 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1581
2028
|
for (const cell of cells) {
|
|
1582
2029
|
const a = cell.artifact
|
|
1583
2030
|
if (a === null || a.kind !== 'swe-arm' || !a.runDir) continue
|
|
2031
|
+
if (privateIids.has(a.iid)) continue // gen-5 split: never surfaced to proposers
|
|
1584
2032
|
runs.push({
|
|
1585
2033
|
iid: a.iid,
|
|
1586
2034
|
arm: config.armName,
|
|
@@ -1618,8 +2066,25 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1618
2066
|
log(`diagnosis ensemble FAILED for gen ${input.generation}: ${(cause as Error).message}`)
|
|
1619
2067
|
}
|
|
1620
2068
|
}
|
|
2069
|
+
// GEN-5 split: the raw-trace distiller must not hand private-instance
|
|
2070
|
+
// cells' path context to the authors either — censor them out of the
|
|
2071
|
+
// candidates' campaigns before distillation.
|
|
2072
|
+
const censoredInput =
|
|
2073
|
+
split === null
|
|
2074
|
+
? input
|
|
2075
|
+
: {
|
|
2076
|
+
...input,
|
|
2077
|
+
candidates: input.candidates.map((cand) => {
|
|
2078
|
+
const campaign = cand.campaign as { cells?: Array<{ scenarioId: string }> } | null
|
|
2079
|
+
if (campaign === null || typeof campaign !== 'object' || !Array.isArray(campaign.cells)) return cand
|
|
2080
|
+
return {
|
|
2081
|
+
...cand,
|
|
2082
|
+
campaign: { ...campaign, cells: campaign.cells.filter((c) => !privateIids.has(c.scenarioId)) },
|
|
2083
|
+
}
|
|
2084
|
+
}),
|
|
2085
|
+
}
|
|
1621
2086
|
signal?.throwIfAborted()
|
|
1622
|
-
const rawFindings = (await rawTrace(
|
|
2087
|
+
const rawFindings = (await rawTrace(censoredInput as Parameters<typeof rawTrace>[0])) as unknown[]
|
|
1623
2088
|
signal?.throwIfAborted()
|
|
1624
2089
|
return [steeringFinding, ...ensembleFindings, ...rawFindings]
|
|
1625
2090
|
}
|
|
@@ -1638,22 +2103,47 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1638
2103
|
'tsx src/swe-arena/holdout-certify.mts <config.json> --candidate <winner-loops-commit>'
|
|
1639
2104
|
const improveRunDir = join(config.outDir, 'improve-run')
|
|
1640
2105
|
|
|
2106
|
+
// ── crash recovery: a killed run leaves its in-flight paid call 'pending'
|
|
2107
|
+
// in the durable cost ledger, and the ledger's fail-closed guard then
|
|
2108
|
+
// refuses ALL new paid work on resume. Under the outDir instance lock
|
|
2109
|
+
// (sole runner), every pending call restored from disk is provably from a
|
|
2110
|
+
// dead process — settle each as a $0 failure receipt (reason
|
|
2111
|
+
// 'process-crash-orphan') so the guard passes without erasing the crash
|
|
2112
|
+
// from the durable record. ───────────────────────────────────────────
|
|
2113
|
+
for (const receipt of reconcileCrashOrphansOnDisk(improveRunDir)) {
|
|
2114
|
+
log(
|
|
2115
|
+
`cost-ledger: reconciled crash-orphaned call '${receipt.callId}' ` +
|
|
2116
|
+
`(${receipt.actor}, ${receipt.phase}) as ${CRASH_ORPHAN_REASON}`,
|
|
2117
|
+
)
|
|
2118
|
+
}
|
|
2119
|
+
|
|
1641
2120
|
// ── generator: the gen-3 proposer fan-out (parallel AgentProfile-pinned
|
|
1642
2121
|
// authors + pre-filter) when `proposers` is configured; the legacy
|
|
1643
2122
|
// single-author generator otherwise. ─────────────────────────────────
|
|
1644
2123
|
const fanout =
|
|
1645
2124
|
config.proposers !== undefined
|
|
1646
|
-
? fanOutLoopsGenerator(config, {
|
|
2125
|
+
? fanOutLoopsGenerator(config, {
|
|
2126
|
+
...(smokeRunner ? { smokeRunner } : {}),
|
|
2127
|
+
...(runnerImplementationRef ? { runnerImplementationRef } : {}),
|
|
2128
|
+
...(judgeImplementationRef ? { judgeImplementationRef } : {}),
|
|
2129
|
+
...(paretoParents.length > 0 ? { parents: paretoParents } : {}),
|
|
2130
|
+
...(briefingCtx !== undefined ? { briefing: briefingCtx } : {}),
|
|
2131
|
+
// The GEPA seat's inner evaluator uses the same public-only
|
|
2132
|
+
// smoke instance; the split guards the never-surfaced invariant at
|
|
2133
|
+
// the bridge boundary too.
|
|
2134
|
+
...(smokeIid !== null ? { smokeInstanceId: smokeIid } : {}),
|
|
2135
|
+
scoreSplit: split,
|
|
2136
|
+
log,
|
|
2137
|
+
})
|
|
1647
2138
|
: null
|
|
1648
2139
|
const generator = withParentCancellation(fanout ?? constrainedLoopsGenerator(config), signal)
|
|
1649
2140
|
|
|
1650
2141
|
// ── the improve() call: the optimizer seat ───────────────────────────
|
|
1651
2142
|
// Typed from improve()'s own parameter: the monorepo hoists two
|
|
1652
2143
|
// agent-interface majors, so a nominal import can resolve to the wrong one.
|
|
1653
|
-
const profile = { name: 'loops-pi-supervisor' } as Parameters<typeof improve>[0]
|
|
1654
2144
|
signal?.throwIfAborted()
|
|
1655
2145
|
log(`round ${config.round} runId=${runId}: improve(surface:'code') over ${config.loopsRepo}@${config.loopsBaseRef}`)
|
|
1656
|
-
const result = await improve<Scenario, R4Artifact>(
|
|
2146
|
+
const result = await improve<Scenario, R4Artifact>({
|
|
1657
2147
|
surface: 'code',
|
|
1658
2148
|
// analyzeGeneration wins over this flag; the composite above embeds
|
|
1659
2149
|
// rawTraceDistiller directly so the raw-trace mechanism stays active.
|
|
@@ -1737,10 +2227,23 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1737
2227
|
}
|
|
1738
2228
|
}
|
|
1739
2229
|
|
|
2230
|
+
// Collected per-candidate facts for activation and proposer rewards.
|
|
2231
|
+
const activationBySurface = new Map<string, ActivationRecord>()
|
|
2232
|
+
interface ProposerOutcomeFact {
|
|
2233
|
+
generation: number
|
|
2234
|
+
candidateIndex: number
|
|
2235
|
+
label: string
|
|
2236
|
+
commit: string | null
|
|
2237
|
+
resolvedCount: number
|
|
2238
|
+
diffPath: string | null
|
|
2239
|
+
}
|
|
2240
|
+
const proposerFacts: ProposerOutcomeFact[] = []
|
|
2241
|
+
|
|
1740
2242
|
for (let g = 0; g < loop.generations.length; g++) {
|
|
1741
2243
|
const gen = loop.generations[g]!
|
|
1742
2244
|
const rows: StaircaseRow[] = []
|
|
1743
|
-
for (
|
|
2245
|
+
for (let candIndex = 0; candIndex < gen.record.candidates.length; candIndex++) {
|
|
2246
|
+
const cand = gen.record.candidates[candIndex]!
|
|
1744
2247
|
const surface = gen.surfaces.find((s) => s.surfaceHash === cand.surfaceHash)?.surface
|
|
1745
2248
|
const cs = surface && typeof surface === 'object' && surface.kind === 'code' ? surface : null
|
|
1746
2249
|
const desc = cs ? await recorder.ensure(cs) : undefined
|
|
@@ -1764,6 +2267,71 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1764
2267
|
const parentResolvedCount =
|
|
1765
2268
|
parentCampaign !== undefined ? resolvedCountOf(parentCampaign) : measuredBaselineCount
|
|
1766
2269
|
const violations = desc?.violations ?? []
|
|
2270
|
+
|
|
2271
|
+
// GEN-5 activation gate: run the candidate's own committed predicate
|
|
2272
|
+
// over its own cell run dirs. Fail-closed — a missing/unparseable
|
|
2273
|
+
// predicate (the prefilter should have killed it) quarantines.
|
|
2274
|
+
let activation: ActivationRecord | undefined
|
|
2275
|
+
if (config.activationGate === true && cs !== null) {
|
|
2276
|
+
const committed = await readCommittedPredicate(config.loopsRepo, cs.candidateCommit)
|
|
2277
|
+
if (committed === null || !committed.parsed.ok) {
|
|
2278
|
+
const why =
|
|
2279
|
+
committed === null
|
|
2280
|
+
? `no ${ACTIVATION_PREDICATE_RELPATH} at ${cs.candidateCommit.slice(0, 10)}`
|
|
2281
|
+
: `unparseable activation predicate: ${committed.parsed.ok ? '' : committed.parsed.error}`
|
|
2282
|
+
activation = {
|
|
2283
|
+
present: false,
|
|
2284
|
+
description: null,
|
|
2285
|
+
fired: false,
|
|
2286
|
+
evidence: [],
|
|
2287
|
+
warnings: [`${why} — fail-closed quarantine`],
|
|
2288
|
+
}
|
|
2289
|
+
} else {
|
|
2290
|
+
const runDirs = [
|
|
2291
|
+
...new Set(
|
|
2292
|
+
cells
|
|
2293
|
+
.map((c) => (c.artifact !== null && c.artifact.kind === 'swe-arm' ? c.artifact.runDir : null))
|
|
2294
|
+
.filter((d): d is string => typeof d === 'string' && d.length > 0),
|
|
2295
|
+
),
|
|
2296
|
+
]
|
|
2297
|
+
const res = await runActivationPredicate(committed.parsed.predicate, runDirs)
|
|
2298
|
+
activation = {
|
|
2299
|
+
present: true,
|
|
2300
|
+
description: committed.parsed.predicate.description,
|
|
2301
|
+
fired: res.fired,
|
|
2302
|
+
evidence: res.evidence,
|
|
2303
|
+
warnings: res.warnings,
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
activationBySurface.set(cand.surfaceHash, activation)
|
|
2307
|
+
log(
|
|
2308
|
+
`activation ${cand.label ?? cand.surfaceHash.slice(0, 10)}: present=${activation.present} ` +
|
|
2309
|
+
`fired=${activation.fired}${activation.fired ? ` — ${activation.evidence[0] ?? ''}` : ''}` +
|
|
2310
|
+
`${activation.warnings.length > 0 ? ` (warnings: ${activation.warnings.join('; ')})` : ''}`,
|
|
2311
|
+
)
|
|
2312
|
+
}
|
|
2313
|
+
|
|
2314
|
+
// GEN-5 split sub-scores: both halves logged per candidate; the
|
|
2315
|
+
// selection rule stays combined (candResolved over ALL instances).
|
|
2316
|
+
const verdicts = instanceVerdictsFromCells(cells, config.instances, reps)
|
|
2317
|
+
const splitScores = split !== null ? subScores(verdicts, split) : null
|
|
2318
|
+
if (split !== null && splitScores !== null) {
|
|
2319
|
+
log(
|
|
2320
|
+
`split scores ${cand.label ?? cand.surfaceHash.slice(0, 10)}: ` +
|
|
2321
|
+
`public ${splitScores.publicResolvedCount}/${split.publicInstances.length}, ` +
|
|
2322
|
+
`private ${splitScores.privateResolvedCount}/${split.privateInstances.length} (combined ${candResolved}/${config.instances.length})`,
|
|
2323
|
+
)
|
|
2324
|
+
}
|
|
2325
|
+
|
|
2326
|
+
const verdict = decideVerdict({
|
|
2327
|
+
violations,
|
|
2328
|
+
coverageComplete,
|
|
2329
|
+
resolvedCount: candResolved,
|
|
2330
|
+
parentResolvedCount,
|
|
2331
|
+
costRatio,
|
|
2332
|
+
costGuardRatio: config.costGuardRatio,
|
|
2333
|
+
...(activation !== undefined ? { activationFired: activation.fired } : {}),
|
|
2334
|
+
})
|
|
1767
2335
|
rows.push({
|
|
1768
2336
|
schema: STAIRCASE_SCHEMA,
|
|
1769
2337
|
round: config.round,
|
|
@@ -1786,18 +2354,31 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1786
2354
|
costRatio,
|
|
1787
2355
|
costGuardRatio: config.costGuardRatio,
|
|
1788
2356
|
internallyPromoted: gen.record.promoted.includes(cand.surfaceHash),
|
|
1789
|
-
verdict
|
|
1790
|
-
violations,
|
|
1791
|
-
coverageComplete,
|
|
1792
|
-
resolvedCount: candResolved,
|
|
1793
|
-
parentResolvedCount,
|
|
1794
|
-
costRatio,
|
|
1795
|
-
costGuardRatio: config.costGuardRatio,
|
|
1796
|
-
}),
|
|
2357
|
+
verdict,
|
|
1797
2358
|
holdout: 'operator-approval-required',
|
|
1798
2359
|
armProvenance: desc?.armProvenance ?? null,
|
|
1799
2360
|
diffPath: desc?.diffPath ?? null,
|
|
1800
2361
|
diffSha256: desc?.diffSha256 ?? null,
|
|
2362
|
+
...(split !== null && splitScores !== null
|
|
2363
|
+
? {
|
|
2364
|
+
split: {
|
|
2365
|
+
publicInstances: split.publicInstances,
|
|
2366
|
+
privateInstances: split.privateInstances,
|
|
2367
|
+
...splitScores,
|
|
2368
|
+
},
|
|
2369
|
+
}
|
|
2370
|
+
: {}),
|
|
2371
|
+
...(activation !== undefined ? { activation } : {}),
|
|
2372
|
+
})
|
|
2373
|
+
|
|
2374
|
+
const label = cand.label ?? cs?.candidateCommit?.slice(0, 10) ?? cand.surfaceHash.slice(0, 10)
|
|
2375
|
+
proposerFacts.push({
|
|
2376
|
+
generation: g,
|
|
2377
|
+
candidateIndex: candIndex,
|
|
2378
|
+
label,
|
|
2379
|
+
commit: cs?.candidateCommit ?? null,
|
|
2380
|
+
resolvedCount: candResolved,
|
|
2381
|
+
diffPath: desc?.diffPath ?? null,
|
|
1801
2382
|
})
|
|
1802
2383
|
}
|
|
1803
2384
|
const genFile = join(config.roundsDir, `gen-${g}.jsonl`)
|
|
@@ -1823,7 +2404,7 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1823
2404
|
parent: premeasured?.surfaceHash ?? 'baseline',
|
|
1824
2405
|
parentResolvedCount: measuredBaselineCount,
|
|
1825
2406
|
label: kill.proposer,
|
|
1826
|
-
rationale: `prefilter kill at stage '${kill.stage}' (${kill.harness})`,
|
|
2407
|
+
rationale: `prefilter kill at stage '${kill.stage}' (${kill.harness ?? 'engine'})`,
|
|
1827
2408
|
changedFiles: [],
|
|
1828
2409
|
changeSpaceViolations: kill.stage === 'change-space' ? [kill.reason] : [],
|
|
1829
2410
|
perInstance: [],
|
|
@@ -1847,6 +2428,38 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1847
2428
|
}
|
|
1848
2429
|
}
|
|
1849
2430
|
|
|
2431
|
+
// GEN-5 label-v2 proposer rewards: baseline-relative (candidate − baseline
|
|
2432
|
+
// resolved fraction, improvement positive), one settle-time ledger line
|
|
2433
|
+
// per evaluated candidate now that the round's scores are final.
|
|
2434
|
+
if (settleCapture !== null) {
|
|
2435
|
+
const sanitizeName = (s: string): string => s.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
2436
|
+
for (const fact of proposerFacts) {
|
|
2437
|
+
try {
|
|
2438
|
+
const flatDir = join(config.outDir, 'proposer-shots')
|
|
2439
|
+
const pattern = new RegExp(`^gen${fact.generation}-cand${fact.candidateIndex}-shot\\d+\\.json$`)
|
|
2440
|
+
const receiptPaths: string[] = []
|
|
2441
|
+
for (const dir of [flatDir, join(flatDir, sanitizeName(fact.label))]) {
|
|
2442
|
+
for (const name of (await readdir(dir).catch(() => [])).sort()) {
|
|
2443
|
+
if (pattern.test(name)) receiptPaths.push(join(dir, name))
|
|
2444
|
+
}
|
|
2445
|
+
}
|
|
2446
|
+
await settleCapture.captureProposer({
|
|
2447
|
+
generation: fact.generation,
|
|
2448
|
+
candidateIndex: fact.candidateIndex,
|
|
2449
|
+
proposer: fact.label,
|
|
2450
|
+
harness: config.proposers?.find((p) => p.name === fact.label)?.harness ?? null,
|
|
2451
|
+
commit: fact.commit,
|
|
2452
|
+
candResolved: fact.resolvedCount,
|
|
2453
|
+
baselineResolved: measuredBaselineCount,
|
|
2454
|
+
shotReceiptPaths: receiptPaths,
|
|
2455
|
+
diffPath: fact.diffPath,
|
|
2456
|
+
})
|
|
2457
|
+
} catch (cause) {
|
|
2458
|
+
log(`rollout-ledger: proposer capture FAILED for ${fact.label}: ${(cause as Error).message}`)
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
|
|
1850
2463
|
const winnerSurface = result.raw.winner.surface
|
|
1851
2464
|
const winnerCs =
|
|
1852
2465
|
typeof winnerSurface === 'object' && winnerSurface !== null && winnerSurface.kind === 'code'
|
|
@@ -1884,6 +2497,7 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1884
2497
|
// is worth approving.
|
|
1885
2498
|
const winnerHash = winnerCs ? surfaceHash(winnerCs) : null
|
|
1886
2499
|
const winnerCampaign = winnerHash !== null ? campaignBySurface.get(winnerHash) : undefined
|
|
2500
|
+
const winnerActivation = winnerHash !== null ? activationBySurface.get(winnerHash) : undefined
|
|
1887
2501
|
const improvementSet =
|
|
1888
2502
|
winnerCampaign !== undefined && winnerRec !== undefined
|
|
1889
2503
|
? gateEvidenceFromCells({
|
|
@@ -1893,6 +2507,7 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1893
2507
|
iids: config.instances,
|
|
1894
2508
|
reps,
|
|
1895
2509
|
costGuardRatio: config.costGuardRatio,
|
|
2510
|
+
...(winnerActivation !== undefined ? { activationFired: winnerActivation.fired } : {}),
|
|
1896
2511
|
})
|
|
1897
2512
|
: null
|
|
1898
2513
|
const wouldKeep = improvementSet !== null && improvementSet.verdict === 'accepted'
|
|
@@ -1938,6 +2553,38 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1938
2553
|
gateReasons: loop.gateResult.reasons,
|
|
1939
2554
|
// Improvement-set (search-split) evidence — NOT a held-out measurement.
|
|
1940
2555
|
improvementSet: improvementSet === null ? null : { ...improvementSet, wouldKeep },
|
|
2556
|
+
// GEN-5: the public/private split (sub-scores live per candidate in the
|
|
2557
|
+
// staircase rows; selection stays combined; private never surfaced to
|
|
2558
|
+
// proposers — the 2-of-6 private half is a direction check, not a
|
|
2559
|
+
// certification).
|
|
2560
|
+
scoreSplit:
|
|
2561
|
+
split === null
|
|
2562
|
+
? null
|
|
2563
|
+
: {
|
|
2564
|
+
seededBy: split.seededBy,
|
|
2565
|
+
publicInstances: split.publicInstances,
|
|
2566
|
+
privateInstances: split.privateInstances,
|
|
2567
|
+
},
|
|
2568
|
+
// GEN-5: activation-gate outcomes per candidate surface.
|
|
2569
|
+
activationGate:
|
|
2570
|
+
config.activationGate === true
|
|
2571
|
+
? {
|
|
2572
|
+
enabled: true,
|
|
2573
|
+
byCandidate: [...activationBySurface.entries()].map(([surface, a]) => ({
|
|
2574
|
+
surface,
|
|
2575
|
+
present: a.present,
|
|
2576
|
+
fired: a.fired,
|
|
2577
|
+
description: a.description,
|
|
2578
|
+
})),
|
|
2579
|
+
}
|
|
2580
|
+
: { enabled: false },
|
|
2581
|
+
// GEN-5: MAP+TOOLBOX briefing provenance.
|
|
2582
|
+
briefing:
|
|
2583
|
+
briefingCtx === undefined
|
|
2584
|
+
? null
|
|
2585
|
+
: { version: AUTHOR_BRIEFING_VERSION, indexPath: briefingCtx.indexPath, textSource: briefingCtx.briefingSource },
|
|
2586
|
+
// GEN-5: settle-time rollout ledger location (tangle.rollout.v1, label v2).
|
|
2587
|
+
rolloutLedger: settleCapture === null ? null : { path: settleCapture.path, capture: 'settle-time', labels: 'v2' },
|
|
1941
2588
|
// Honest run-wide spend from the lib's CostLedger: per-channel rollups
|
|
1942
2589
|
// (agent = arm cells, judge = official-judge calls, driver = proposer
|
|
1943
2590
|
// shots), token totals, and accounting-completeness flags.
|
|
@@ -1969,6 +2616,18 @@ export async function runRound(config: OuterLoopConfig, signal?: AbortSignal): P
|
|
|
1969
2616
|
const summaryPath = join(config.roundsDir, `round${config.round}-summary-${runId}.json`)
|
|
1970
2617
|
await writeFile(summaryPath, JSON.stringify(summary, null, 2))
|
|
1971
2618
|
log(`round summary → ${summaryPath}`)
|
|
2619
|
+
|
|
2620
|
+
// Round rollup at gate time: every cell's orchestration/economics in one table,
|
|
2621
|
+
// written next to the round summary and echoed into the run log.
|
|
2622
|
+
await reportSupervisorRound(join(config.outDir, 'arm-runs'), {
|
|
2623
|
+
appendHeadlineTo: join(config.outDir, 'run.log'),
|
|
2624
|
+
reportDir: config.roundsDir,
|
|
2625
|
+
title: `Round ${config.round} rollup — ${runId}`,
|
|
2626
|
+
echo: true,
|
|
2627
|
+
}).catch((err: unknown) => {
|
|
2628
|
+
log(`round rollup failed: ${err instanceof Error ? err.message : String(err)}`)
|
|
2629
|
+
})
|
|
2630
|
+
|
|
1972
2631
|
log(`gate: ${result.decision} — ${loop.gateResult.reasons[0] ?? ''}`)
|
|
1973
2632
|
} finally {
|
|
1974
2633
|
await result.dispose()
|
|
@@ -2087,17 +2746,35 @@ if (isMain) {
|
|
|
2087
2746
|
if (argv[0] === '--write-config') {
|
|
2088
2747
|
const path = argv[1]
|
|
2089
2748
|
if (!path || path.startsWith('--')) {
|
|
2090
|
-
console.error('usage: outer-loop.mts --write-config <path> [--out-name <dirname>] [--gen3]')
|
|
2749
|
+
console.error('usage: outer-loop.mts --write-config <path> [--out-name <dirname>] [--gen3|--gen4|--gen5]')
|
|
2091
2750
|
process.exit(2)
|
|
2092
2751
|
}
|
|
2093
2752
|
const outDirName = flag('--out-name')
|
|
2094
2753
|
const gen3 = argv.includes('--gen3')
|
|
2095
|
-
const
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
)
|
|
2100
|
-
|
|
2754
|
+
const gen4 = argv.includes('--gen4')
|
|
2755
|
+
const gen5 = argv.includes('--gen5')
|
|
2756
|
+
let config: OuterLoopConfig
|
|
2757
|
+
let flavor: string
|
|
2758
|
+
if (gen4 || gen5) {
|
|
2759
|
+
// The codex seat rides only when the CLI is actually present — a config
|
|
2760
|
+
// naming a missing harness would fail the whole launch at t=0.
|
|
2761
|
+
const codexProbe = await run('codex', ['--version'])
|
|
2762
|
+
const includeCodex = codexProbe.code === 0
|
|
2763
|
+
if (!includeCodex) {
|
|
2764
|
+
console.log(
|
|
2765
|
+
`codex CLI unavailable (rc=${codexProbe.code}) — ${gen5 ? 'gen-5' : 'gen-4'} config written WITHOUT the codex-author seat`,
|
|
2766
|
+
)
|
|
2767
|
+
}
|
|
2768
|
+
const make = gen5 ? defaultGen5Config : defaultGen4Config
|
|
2769
|
+
config = make(undefined, { ...(outDirName ? { outDirName } : {}), includeCodex })
|
|
2770
|
+
flavor = gen5 ? 'gen-5' : 'gen-4'
|
|
2771
|
+
} else {
|
|
2772
|
+
const make = gen3 ? defaultGen3Config : defaultRound4Config
|
|
2773
|
+
config = make(undefined, outDirName ? { outDirName } : {})
|
|
2774
|
+
flavor = gen3 ? 'gen-3' : 'round-4'
|
|
2775
|
+
}
|
|
2776
|
+
await writeFile(path, JSON.stringify(config, null, 2) + '\n')
|
|
2777
|
+
console.log(`default ${flavor} config → ${path}`)
|
|
2101
2778
|
} else if (argv[0] === '--calibration-smoke') {
|
|
2102
2779
|
const dir = argv[1] && !argv[1].startsWith('--') ? argv[1] : undefined
|
|
2103
2780
|
const n = flag('--analysts')
|
|
@@ -2130,7 +2807,7 @@ if (isMain) {
|
|
|
2130
2807
|
} else {
|
|
2131
2808
|
console.error(
|
|
2132
2809
|
'usage: tsx src/swe-arena/outer-loop.mts <config.json> # SPENDS: arms + judges + proposer\n' +
|
|
2133
|
-
' tsx src/swe-arena/outer-loop.mts --write-config <path> [--out-name <dirname>] [--gen3]\n' +
|
|
2810
|
+
' tsx src/swe-arena/outer-loop.mts --write-config <path> [--out-name <dirname>] [--gen3|--gen4|--gen5]\n' +
|
|
2134
2811
|
' tsx src/swe-arena/outer-loop.mts --calibration-smoke [supRunDir] [--analysts N] [--model M] [--endpoint router|zai] [--retries N]',
|
|
2135
2812
|
)
|
|
2136
2813
|
process.exit(2)
|