The probe printed one blended number per policy, so a component pinned at 0.000 across every rung was invisible. Four gates hid there for a month. It now reports floor / best-below-oracle / oracle / ceiling for 25 components across 8 environments, and a component flat across every rung is fatal. bot-detection GATE_SLACK = 1 — fires 5/32 on the real rollouts, was 0/32. The max(1, reference_caught - SLACK) guard is verified by construction, not by sampling: without it the required count reaches 0 and an EMPTY accusation list clears the gate. Observed reference_caught is 4-6, so no amount of sampling would have found that hole. schema-migration GATE_MARGIN = 0.15 — fires 4/32, was 0/32. The cost is disclosed and bounded: the naive split clears it on 5.5% of 1,000 unseen seeds, fenced by an assert at 10%. Margin 0.10 keeps the leak at zero and fires 0/32, i.e. stays dead. A live gradient with a bounded leak beats a clean corpse. redaction-pressure is NOT given a margin, and that is the result rather than a failure. The only setting that fires at all leaves half the secrets standing and pays a four-of-seven ruleset on five seeds in six — a margin that pays for inaction is strictly worse than a dead gate. Recall maxes at 0.852 and no rollout ever cleared both clauses in one episode. It is genuinely hard, not miscalibrated. ⚠️ The per-component check did not catch the defect it was built for. Reverting schema-migration's margin to 0.0 — restoring the exact dead gate — printed ok and exited 0, because the near-oracle rung scrapes the unmargined gate on ~2 seeds in 24 and that kept best<oracle non-zero. Every assertion bounded how much a margin may PAY; none noticed if it stopped existing. migration() now carries the mirror of bot-detection's guard, and reverting the margin fails with "the margin is dead and the component carries no gradient between the crude answer and the exact one". canary-trap's oracle-minus-one rung is documented as degenerate rather than quietly relied on: it is identical to the oracle to four decimals, so it measures specificity and gate at the ceiling, not mid-ladder as its comment claimed. The CI lock policy asks git instead of the disk. It was checking the working tree, where a lock file is a normal by-product of uv sync, so it passed in a clean checkout and failed on every machine that had run an eval. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
42 KiB
Execution plan — Arena / Lumbridge
Generated 2026-08-21 by a 13-agent design + adversarial-verification workflow.
Companion to PLAN.md (decisions) and MULTI_TURN.md (scope).
⚠️ Every position here is the POST-REVIEW position. Where an adversarial reviewer overturned a design spec, the spec's original claim was discarded. Do not resurrect a claim from an earlier document that this one corrects.
Arena / Lumbridge — one execution plan
Synthesis of six adversarially-reviewed workstream specs, 2026-08-21. Every position below is the post-review position. Where a reviewer overturned a spec, the spec's original claim is marked and discarded.
Verified independently for this document (amd-server, 2026-08-21): arena/probe.py is 772 lines with a literal 7-name shim tuple at :89-93 and a literal results dict at :752-760; tests/test_probe.py has a 7-name ENVIRONMENTS tuple at :13-21 and timeout=30 at :31; .gitea/workflows/ci.yml is 42 lines, its probe step is still named "Run four-environment floor and ceiling probe" (:26) for a seven-environment probe, its wheel loop (:36-40) is a hardcoded bash for over seven names, and its lock-policy check (:24) is failing on main because three uv.lock files are tracked (bot_detection, drop_table_inference, grand_exchange); outputs/ contains exactly one file, a config.toml with push = true and base_url = https://api.pinference.ai/api/v1 — no traces.jsonl exists anywhere on this box; ~/.prime/config.json has an empty api_key; tera/src/arena/index.ts:74-79 ARENA_MANIFESTS holds four.
1. Verdicts applied — what each spec becomes
A — grand-exchange-live · feasible, but not in the order written
Stands: the multi-turn mechanism (Env.run + interaction.turn(), null harness, no container), the delta-sheet action grammar, the malformed-reply policy, roc on peak capital employed (average-employed is a measured free-points bug), the vendored-generator equality assertion, no turn-count reward term.
Corrected, blocking:
- The measured ladder does not reconcile with the spec's own reward. At ref 72,758 gp and
TARGET_SHARE = 0.85, target = 61,844; impatient at 50,470 gp givesprofit_ratio0.816,discipline≤ 0.816,cleanfalse → max 0.612, not the reported 0.623. Churn and exhaustive do reconcile, so this is a specific defect. Nothing in §1/§4 is locked until it is re-measured with the shipped engine. viable_marketretargeted atlive_referenceis circular as written. Viability needsfreeze;freezeis drawn inTaskset.load()after viability returns. Fix: derivefreezedeterministically frommarket.seed(e.g.random.Random(seed ^ FREEZE_SALT)), insidebuild_market, so viability and scoring see the same value. This is also the fix for the unstated RNG source — without it,scan()recomputes a reference the model never played against.- §6's held-out claim is inverted. Live is weaker than one-shot: the model sees ~50 of 60 graded ticks. Rule 1 still passes; the README must say "held out: the tail after the last observation, ≥4 ticks", not "stronger by construction". Add a probe assertion on the size of the held-out slice, not only the frontier.
- The probe battery blows CI's own harness.
probe.pyruns in 3.4 s today undertimeout=30. §11 adds 9 policies × blocks × 240 baskets + two sweeps + a 240-seed generator check + a hostile battery replayed per turn. Raising the timeout is B1's job (below), but A must budget the cost, not append a name to a list. - Taskset load cost is unbounded.
num_tasksdefaults 48,VIABILITY_TRIES = 64; a liveviable_marketruns a full 8-turn stepped reference per candidate → ~3,072 simulations before the first token, on every eval and every--dry-run. Cache the reference per seed, or cap tries for live. importvscopycontradiction. §3 says "importgrand_exchange.book.parse_orders", §9 says vendor. It is a copy. Say copy.[env.agent] max_turns = 8is an off-by-one risk, not free insurance. Verify how the framework counts, or set 9.trace.info["episode"]must be plain JSON types. A dataclass or numpy scalar inaudit()fails the trace write for the whole rollout.- Effort is L/XL, not L — it is A plus most of B.
Revised A build order (this is the change that matters): market.py/book.py copies + equality assertion → live.py + unit tests → re-measure the full ladder in the shipped engine → only then lock horizon, FREEZE_RANGE, TARGET_SHARE, TURNS → strategy.py sweep → ledger.py → protocol.py → taskset.py → probe.
B — probe policies + de-hardcoding · verdict: not feasible as written. Split it.
The reviewer is right and the split is the fix.
B1 — de-hardcode environment discovery. Sound, S/M, ships immediately, behaviour-preserving. tomllib over environments/*/pyproject.toml replaces the shim tuple, the main() results dict (via a @probes(name) registry decorator), ENVIRONMENTS in tests/test_probe.py, and the bash wheel loop. The CI environment list was never a matrix — it is a for loop in one step (verified) — so no generator job, no fromJSON matrix, no act_runner risk. Take B1 now and the repo's known collision point disappears for one afternoon's work.
Two mandatory amendments to B1:
- Do not make "discovered but unregistered" a hard exit 1 while A is in flight. The moment A commits a scaffold
pyproject.toml,probe.pyexits 1 and CI goes red for every session on the repo. Make it a warning until an env registers a probe, and flip it to exit 1 in the same commit as A'sexchange_live(). - Make the two guards use the same denominator.
ci.yml's "empty scan" counts directories;discover()counts*/pyproject.toml. A scaffold dir without a pyproject makes them disagree. Count*/pyproject.tomlin both. - Raise
tests/test_probe.pytimeout=30 → 180, keep the lock check before anyuv sync, and fix the "four-environment" step name.
B2 — the Interactive policy machinery. Redesign required; do not build it before A's engine exists. The structural claim ("oracle and exhaustive share answer, so rule 4 follows") is false when the reward saturates, and Arena's rewards all saturate: profit_ratio and efficiency clip at 1.0 and clean is a boolean band. If exhaustive also clears the band it scores 1.000 and the gate reads "budget is free". Further:
BIND_MARGIN = 0.02is staked on realised-P&L differences over 24 seeds, against a block-to-block varianceprobe.py:349-352already documents at 0.48 vs 0.18. The five-block ladder is required for all three rule-4 comparisons, not an optional mitigation — multiply the cost estimate by ~5.gateis binary at weight 0.25, so the exhaustive/oracle gap is quantized in 0.25 steps. A 0.02 margin against a 0.25 quantum is a cliff, not a calibration.play()never terminates on an answer; "impatient answers turn 1" is an unstated precondition on A'sstep().- Seeds must come from A's
baskets()skip-aware helper, not a contiguousrange. - B2's §1.8 dictates A's private module layout, class names, weight vector and turn mechanism. Delete that. A owns its contract; B2 adapts to it. This is the collision the workstream exists to remove.
Net: B1 is unblocked and independent of A. B2 depends on A's measured engine, not A's first draft. PLAN.md's "B depends on A" edge survives for B2 and dies for B1.
D — Tera bridge · feasible, but it is no longer parallel with A/B, and PLAN.md must be amended
Stands and is the strongest evidence in the six specs: 23-file / 283 KB / zero-bare-specifier TS closure; node type-strips raw .ts; 32/32 scenario × seed traces round-trip through Python json and replay to identical checksums; one long-lived NDJSON worker (84 ms start, 22 µs RTT, 217 µs/step, 87 MB RSS) beats HTTP and beats per-rollout processes; the measured hold-length sweep.
Corrected, blocking:
- Role name vs config key — the spec will not load.
agents.pilotrequires a config field namedpilot, but all four TOMLs write[env.agent.harness], andEnvConfigisextra="forbid". Name the roleagent, matchingSingleAgentEnvConfig. Same trap A must resolve; settle it once for both (see Decision 1). CONFIG = ...classvar does not exist in verifiers v1. Config is resolved byconcrete_type()through__orig_bases__. Use the wordle form:class CrowNavTaskset(TeraSpatialTaskset, vf.Taskset[TeraSpatialTask, CrowNavConfig]). As written, every per-envmax_turns/max_holdsilently does nothing.- The provenance claim is real but was mis-located — CORRECTED 2026-08-21.
check-arena-source-hashes.mjspins 19 of 23 vendored files. The claim thatoffices/sites.tsis the material hole is FALSE and was refuted twice independently: mutating it (elevation 4→4000, heading 0→90, width 54→5400) produces a bit-identical episode, so it does not reach the simulation. ⚠️ The real hole issrc/arena/index.tsalone, and it is worse than the original claim: rewriting it soofficeNavScriptedBaselinere-exports a halved policy moves office-nav from 68 steps / return 4.744 to 136 steps / 4.098 — whilenpm run arena:source-hashesprints "ok" for all four environments and exits 0,envHashandsimulatorHashunchanged. That file is the reward DENOMINATOR. Fix: pinsrc/arena/index.tsintera/scripts/check-arena-source-hashes.mjs(a one-linetera-repo change) and regenerate the four pairs insourceHashes.ts. oracle.pyreimplementing the four TS baselines in Python contradicts the bridge's own rule ("never recompute a number that came out of TypeScript") — and it does so for the reward denominator. Add anoracleop toworker.mjs; the four baselines are already exported fromsrc/arena/index.ts.drive-101measures nothing, and the 0.9 ceiling band makes it worse than "no rule-4 claim". Return is monotone in throttle, 12/12 goals at every value;0.9 × 4.339 = 3.905and throttle 0.2 already returns 3.723 → quality 0.953. Any positive constant throttle scores ~0.95–1.00. Same collapse on office-nav: any goal-reaching run clears the band, socontrol(0.50) andgate(0.20) are constants and onlyefficiencycarries signal.- office-nav's rule-4 argument is backwards under its own formula.
efficiency = oracle_turns / max(turns, oracle_turns)only ever penalises extra turns, and pastceil(1.5 × 1.8) = 3turnscleanalso drops. The formula punishes exactly the "precision buys turns" behaviour cited as evidence. gateis a separate additive term for goal-attainment that is already insidets_returnas the success bonus — double-counted, additively. That is the failureprobe.py:15-17records verbatim. Make it a multiplier on the sum.- Missing: no
@vf.stop; no handling of a mid-loopterminatedSegment (the loop as written hitsRuntimeError('the exchange is over')); noTaskset.load()/ prompt-vs-bare-turn()decision; an empty{"plan":[]}costs zero steps while a malformed reply costs 10, inverting the spec's own principle; no guard fororacle_return <= 0;max_hold = 400exceedsMAX_STEPS = 360. - Scope. PLAN.md:138 and the parallelization map say D lives in
teraand shares no files with A/B/C. This spec puts everything inarenaand editsprobe.py,tests/test_probe.py,ci.yml,README.md,configs/, plus twolumbridge-v4files. That is a real decision (the packaging argument is correct —terahas no Python toolchain), but PLAN.md must be amended to record it, because it converts D from "parallel with everything" into "collides with A, B and W".
Revised D: ship the bridge + replay gate + tera_crow_nav first. drive-101 ships only as a labelled bridge-correctness environment or not at all (Decision 3). office-nav and california-flight follow. D's probe integration waits for B2.
P — app.lumbridgecorp.com · feasible; Phase 1 as written cannot ship
Stands and is high-value: the endpoint inventory (29 routes, not 31), the 23 dead api.ts methods, one-build/two-hosts with app.* as the only auth origin, bind 10.0.0.2 + the empty-200 verification, the isAppHost branch in sendShell, WORLD_LAUNCHER_URL convergence for /go/:slug, World1 moved not deleted.
Corrected, blocking:
- "Ingest the runs already on amd-server" is impossible — there are none. One
config.toml, zero episodes, and that config points atapi.pinference.aiwithPRIME_API_KEY, which is empty. Phase 1 must contain "execute one eval to completion against spark-1" as an explicit prerequisite, or Arena's dashboard ships empty. - The leaderboard is operator-gameable and mis-specified. (a) The server must recompute every aggregate in SQL from the
arena_episodesit just inserted, never trust the pusher's numbers. (b) Ranking on a mean over scored episodes is Arena's own "score what survived" trap. Storereward_mean_scoredandreward_mean_attempted(errors as 0), rank on attempted, refuse to rank below a scored-fraction floor. arena_episodesflattens a list.Episode.tracesis a list;reward/rewards/stop_condition/last_replyare per-Trace. Primary key must be(run_id, episode_id, trace_id)with anagent_namecolumn — otherwise the schema is wrong forgrand-exchange-live(simulator as second role) and forredaction-pressure(genuine two-agent).- The trace viewer publishes the held-out slice.
GET /api/arena/runs/:id/episodes/:eidatuserscope returns full transcripts, which contain held-out task data and, for oracle runs, the answers. One leaked invite publishes the held-out corpus. Transcripts are admin-only or redacted. This settles the spec's open question on read scope. - The compute-scene-vocabulary blocker is fabricated.
computeSceneis a free-form slug regex, notz.enum(SCENE_NAMES);stock,nemotronandqwen38all pass today. Delete that precondition. portal_host_samplesandportal_container_sampleshave no node identity — nonode_id, no host column, no PK. Per-node charts needALTER TABLE … ADD COLUMN IF NOT EXISTS node_id textplus an index first.scored = ok AND rewards non-emptyis wrong.write_episodedumps withexclude_none=True, so aNonereward is dropped from the file entirely — "not measured" and "key absent" are indistinguishable on the wire. Require at least one non-nullReward, and carry the run's declared reward-name set from the config.Reward.valueis a@propertyand never appears in dumped JSON. Computescore × weightin the ingester.- ⚠️ CORRECTED 2026-08-21: there is NO verifiers 0.4.0. The
version = "0.4.0"at~/vendor/prime-intellect/verifiers/pyproject.toml:136belongs to a[[tool.uv.dependency-metadata]]block for nemo-gym, not to verifiers — read as verifiers' own version it sends you hunting a release that does not exist and concluding the bump is blocked. The vendored checkout isv0.3.0-59-g4bcb48e5, published to PyPI as0.3.1.dev59, byte-identical to the vendored tree, and that is what all nine manifests now pin. The layout warning itself is CORRECT: 0.3.0 → dev59 changes the directory layout, not just the config filename — HEAD produces one flat<env>--<model>--<harness>--<hex8>dir with no nested uuid, andRunConfig.idis aPrivateAttrabsent fromconfigs/eval.json. Derive the run id from the directory basename. - Name the private console
/nodes, not/compute—/computeis already a public marketing section with its own nav. - Put
isAppHostinpackages/shared— two independent string checks that must agree, with a silent asymmetric failure mode (dashboard rendered under a 404 status). sendShell(url, reply)takes no request; the signature and both call sites change.spa-fallback.test.tsneeds a case.@radix-ui/react-selectis not installed.
Revised P phasing. The honest first ship is P1: Caddy host + host-aware routing + renderAppShell + AppShell + Overview + Worlds + Org + Inbox + the api.ts trisection. All of that runs on endpoints that exist today. Arena (schema, 6 routes, ingester, 4 pages) is P2, a full workstream of its own, and it is gated on a real eval having run.
W — website visual vocabulary · feasible; the data pipeline must be redirected, not built
Stands: the diagnosis (the site draws what it hasn't measured and narrates what it has), MeasureTable / Bar / FlowStrip / SpecSheet, inline SVG with currentColor and no charting dependency (the gold-500 → gold-700 theme flip alone settles this), the grouped StateList, and the four live factual errors.
Corrected:
arena-runs.jsonhas no machine source and must not be created.probe.pydoes not run models; there is notraces.jsonl. The site already has the right contract:apps/web/src/data/eval-results.ts+eval-results.json+eval-results.parse.test.ts, with a{model, host, quant, serving, checkpoint}target tuple and an explicit "null means not measured" rule. The Nemotron run belongs there. Creating a second incompatible file guarantees the drift the pipeline exists to prevent — and putting it ineval-results.jsonalso fixes a fifth live error the audit missed:arena.tsx:519says "No model scores are published yet, so nothing appears on Evals" whileREADME.md:88-105publishes seven.- The crude-maximiser labels have no machine source either.
probe.py's functions return only{inaction, crude, plausible, oracle}; "redact everything" / "ban everyone" / "buy and sell at market" exist only as markdown italics. Emitting that column means changingprobe.py's return shape — which is B's file. Fold it into B1's registry work or accept it as a hand-typed literal, but do not schedule it as a third-party edit toprobe.py. check:arenamust not executeprobe.py. Thesync-scenes.mjsprecedent parses committed YAML from an anonymous public clone; running a 772-line probe underuv run --with regexis a slow, flaky gate that contradicts the hermetic-build rule. Emit JSON inarena's CI, commit it, and havecheck:arenacompare bytes.Diagramfails DESIGN.md §4.2's own gate — no variants, no CVA variants object. It is a one-off<figure>per page, exactly likescaling-plot.tsx/spread-field.tsxalready are. Do not put it incomponents/ui/.- Do not put a nine-verb FlowStrip above the fold on
/forge. Every StateList item on that page isdone: false, including "No repository, no tag, no release." Drawing an unbuilt pipeline is the exact failure the audit indicts. - The landing-page markup claim is wrong, and it invalidates the landing plan's premise:
landing.tsxcontains two<svg>elements, not six.ForgeChainis an<ol>,ArenaBandis nested<div>s and 32<span>s,StreamSignalandSimulateViewportare divs. The real coupling is ~33 class references intomachine-home.css(35 KB) carrying reveal/scan animations. Treat the landing as its own step with its own CSS audit. Barhas one existing hand-rolled call site, not two —BudgetMeteris an N-segmentflexGrowtrack with a composedaria-label, andBarPropscannot express it.Barearns its place throughMeasureTable's cells, not through BudgetMeter.The scenario count is 16, not 20⚠️ WRONG as of 2026-08-21 15:50 — it is 20, five registries × four, andoffice-jobs-v1is not phantom. Another session merged it into the sharedteratree mid-session. Counts are twelve / five / twenty; the site's original copy was right and W0's correction was reverted.- Four more table-shaped components exist that the "seven call sites" audit missed:
components/compute/model-table.tsx,components/publish/concurrency-table.tsx,ledger.tsx,rate-strip.tsx. DesignMeasureTable's API against them. - No tests are proposed. New generated data + a check script are exactly what gets a parse test in this repo.
Revised W: /arena first (it is also a truth fix), /bench second, /stream third — but /arena's number pipeline routes through eval-results.json, and the office-jobs-v1 deletion happens once, in one place, coordinated with D and P.
C-compute — rent to train, Spark to serve · feasible; its headline correction is false
Stands and is the most immediately actionable: the policy itself; the measured spark-1 tenancy (121 GB total / 84 GB used / 36 GB free, SGLang + Chatterbox + vox + ASR co-resident, Chatterbox's ~8.4 GB/hr leak having killed brain before); prime login as step zero because api_key is empty (verified); Arena → prime-rl needs no adapter — EnvConfig is SerializeAsAny[vf.EnvConfig] and the taskset tree is the same shape, prefixed; the seat-name-is-a-TOML-key trap; Hosted Training being unavailable while E is parked (rl.py:1522-1524 — bare v1 ids do not resolve through the Hub); the sm_121 lock contradiction in Forge; TRAINERS = ("axolotl","trl"); no price is hardcoded anywhere and none should be invented.
Corrected, blocking:
--model braindoes NOT 404. SGLang does not validate the model field on the chat route; it returns 200 with a real completion. Two of the four "stale facts" and two proposed file edits rest on a false claim. The real hazard is the opposite and worse: it silently succeeds. Re-running theREADME.md:88-89baseline today produces Qwen3.8-27B numbers under a command that reads--model brain, and they will be tabulated beside Nemotron numbers as comparable. The correction the arena docs need is: pin the served idbrain-qwen38-dsparkand the scene name in every reproduction command, not "fix a 404".uv run eval grand-exchange-live --harness.id nullis not valid.EvalConfighas no top-levelharness;--harness.is admitted only to render a did-you-mean. Canonical:--env.agent.harness.id null, or the TOML blockconfigs/grand_exchange.tomlalready uses.uv sync --package prime-rl --package grand-exchange-livecannot work. prime-rl's[tool.uv.workspace] membersis a closed list of its own vendored envs. Useuv pip installof a built wheel or git URL into the env-server venv, or add a path source.forge.tsx:114is not stale. The remote is private,git tagis empty,status="In development", nosource=prop. Nobody can clone it. The page's own README warns "This section has been wrong three times" — this edit would make it four.- Workstream-letter collision: PLAN.md already defines a "workstream C" (more interactive environments). Rename this one — "Compute Policy", no letter.
- The wordle wall-clock anchors are fabricated (the README says 2 GPUs, the toml declares 8 and 200 steps; neither states a duration). Delete them and produce our own.
torchaoandnixl-cu12are pinned to x86_64-only wheel URLs whiletorchaois a hard runtime dep — the "aarch64 wheels for every heavy dependency" claim does not hold, which sharpens the aarch64-variance risk.prime disksattach with--disks, not--attach-disk.- Two things nobody has planned: how the DCP checkpoint gets off the pod before
prime pods terminatedestroys the disk (needs a--disksvolume at create time), and the DCP → HF shards → NVFP4 → Compute model-id conversion, which nothing on disk automates end-to-end (prime-rl hasconvert_to_hfbut no CLI entrypoint). - The link nobody drew: rental hours are driven by whether the turn budget binds.
group_size × batch × stepsof multi-turn rollouts against a rented inference fleet is real money. If B2's exhaustive policy scores at the oracle, the run burns GPU hours measuring nothing. Rule 4 is a compute-policy constraint, not only a design one.
2. Parallelization map and file collisions
What can start today, concurrently
| Lane | Work | Repo | Blocked by |
|---|---|---|---|
| 1 | B1 — discovery de-hardcode + timeout + step name + uv.lock cleanup |
arena |
nothing |
| 2 | A step 2 — live.py engine + unit tests + re-measure the ladder |
arena |
nothing (own worktree) |
| 3 | D step 1 — worker, vendored TS, hashes, test_replay.py |
arena (new dir only) |
nothing |
| 4 | Compute Policy step 0 — prime login, catalogue + prices, doc the corrected spark-1 facts |
none / arena/docs |
nothing |
| 5 | P1 — Caddy host, host-aware routing, AppShell, Overview/Worlds/Org/Inbox | lumbridge-v4 |
nothing |
| 6 | W step 0 — the five factual corrections + eval-results.json decision |
lumbridge-v4 |
nothing |
Six lanes, and only lanes 1/2/3 share a repo. Use a worktree per lane — this repo took divergent concurrent pushes on 2026-08-19. Merge, never force.
True dependency edges
B1 ──────────────────────────────────────────┐
├──► A step 7 (probe row) ──► A ships
A steps 1-2 (engine) ──► A re-measure ──► A steps 3-6 ─┘
│
└──► B2 (policy machinery, calibrated on real numbers)
│
├──► C1 drop-table
├──► C2 bot-detection
├──► C3 fault-localisation
├──► C4 redaction (2-agent)
└──► D step 5 (tera probe rows)
D step 1 (replay gate) ──► D crow-nav ──► D flight/office
A ships ──► one real eval run ──► traces.jsonl exists ──► P2 (Arena dashboard)
│
└──► W /arena numbers (via eval-results.json)
prime login ──► rented-pod runbook ──► first RL run ──► Forge prime-rl renderer
Two edges that were wrong in the source specs and are now correct:
- B1 does not depend on A (PLAN.md's "B depends on A" applies only to B2).
- D is no longer file-independent of A/B/W — it lives in
arenaand editsprobe.py,ci.yml,test_probe.py,README.md, plusarena.tsxandseo.ts.
Merge-conflict map — the specific paths
Tier 1 — will conflict, must be sequenced or de-hardcoded first:
| Path | Claimed by | Resolution |
|---|---|---|
arena/probe.py |
A (§11), B1 (registry), B2 (machinery), D (§7), C1-C4, W (needs a return-shape change for crude labels) | B1 lands first. After B1, adding an env is one appended function at EOF — a mergeable case. Nothing else touches this file until B1 is merged. |
arena/tests/test_probe.py |
A, B1, D | B1 removes it from every future workstream's diff. Never edited again. |
arena/.gitea/workflows/ci.yml |
A, B1, D (needs Node + replay test + a one-dir-four-wheels change) | B1 lands the glob; D then adds steps, not list entries. D's one-directory-four-tasksets layout actively breaks B1's one-dir-one-wheel discovery — D must add a [tool.arena] tasksets = [...] key or equivalent, and B1 should be told to read it. |
arena/configs/grand_exchange_live.toml |
A, B(§1.7.2), Compute Policy — three specs create the same file | A owns it. Delete it from the other two file lists. |
arena/environments/grand_exchange_live/*/taskset.py and session.py |
A and B both specify the module layout and class signatures | A owns the contract. B2 adapts. |
arena/README.md |
A (rows), D (rows + house rule 4), Compute Policy (baseline qualifier), W (source of truth for the site tables) | Sequence: A → D → Compute Policy. W reads it, never writes it. |
arena/PLAN.md + MULTI_TURN.md |
B, D (scope amendment), Compute Policy | One session, one pass, after the D-scope decision lands. |
Tier 2 — cross-repo, same file:
| Path | Claimed by | Resolution |
|---|---|---|
lumbridge-v4/apps/web/src/pages/v4/arena.tsx |
D (delete office-jobs-v1 at :203 and :511) and W (full rebuild of the page) |
W owns the file. D contributes the fact (four manifests, 16 scenarios), not the edit. |
lumbridge-v4/services/api/src/seo.ts |
D (:271 twelve→eleven, five→four) and P (renderAppShell, drop /app from APP_ROUTE_PATTERNS at :698) |
Different regions; sequence P after D's one-line copy fix, or vice versa. Low risk but both must run seo.test.ts (ROUTER_PATHS at :36) and spa-fallback.test.ts. |
lumbridge-v4/apps/web/src/pages/v4/forge.tsx |
W (FlowStrip, StateList) and Compute Policy (:59-62 delegation copy) | W owns it. Compute Policy contributes one sentence, only after a rented run has actually happened. The :114 edit is cancelled (see verdict). |
lumbridge-v4/apps/web/src/data/eval-results.json |
W (Nemotron run) and P2 (the dashboard's run store) | The public file is a curated summary; the dashboard is the archive. Decide the direction (Decision 6) before either writes. |
Tier 3 — three tracked uv.lock files are failing CI on main right now. git rm --cached them in B1's commit, before anyone blames a new failure on a new environment.
3. Sequencing
Phase 1 — genuinely shippable, ~2 weeks, six parallel lanes
- B1 (arena): tomllib discovery,
@probesregistry, timeout 180,uv.lockuntracking, step rename. Gate:probe.pystill prints sevenokrows; CI green with zero list edits. - A steps 1-2 + re-measurement (arena, worktree): frozen generator copy + equality assertion;
LiveBook/Leg/Ledgerwith unit tests; then re-run the full policy ladder and publish the corrected numbers. Gate: the ladder reconciles with the reward formula;horizon,FREEZE_RANGE,TARGET_SHARE,TURNSare chosen from that measurement, not from the spec. - D step 1 (arena, new dir): worker + vendored TS +
hashes.py+sync_tera.py+test_replay.py. Gate: 32/32 replay exact, tampering rejected, hash check green — and a decision on the 4 unpinned files. - Compute Policy step 0:
prime login; record realavailabilityoutput and prices; writearena/docs/COMPUTE_POLICY.mdwith the corrected spark-1 facts (brain-qwen38-dspark, silent-wrong-model hazard, 36 GB headroom). - P1 (lumbridge-v4): Caddy
app.block withbind 10.0.0.2and the non-empty-body check; host-awareApp.tsx;renderAppShell+sendShellsignature change +seo.test.ts+spa-fallback.test.ts; AppShell; Overview / Worlds / Org / Inbox; delete the 23 deadapi.tsmethods. - W step 0 (lumbridge-v4): the five factual corrections —
arena.tsx:330"Nine tasks",landing.tsxcard 02, the 4-row probe table → 7 rows,office-jobs-v1deleted,arena.tsx:519"no model scores published". Ship the Nemotron run intoeval-results.json.
Phase 1 ends with: a green de-hardcoded CI, a measured live engine, a proven TS replay bridge, a live private console on real endpoints, a truthful public site, and a Prime Intellect account that can rent.
Phase 2
- A steps 3-9 →
grand-exchange-liveships with its probe row (needs B1 merged). - One real eval run against spark-1, pinned to
brain-qwen38-dspark, with the empty-rewards count checked. This is a hard prerequisite for P2 and for any site number. - D crow-nav end to end (role named
agent,vf.Taskset[...]generic form,oracleop in the worker,gateas a multiplier). - W /arena rebuild +
/benchon the new primitives. - First rented run: pod create with an attached disk,
install.sh, wheel install, baseline eval,uv run rl, re-eval, before/after table.
Phase 3
- B2 — policy machinery, calibrated against A's measured exhaustive/oracle gap, with block-wise ladders replacing the single 0.02 margin.
- P2 — Arena schema (per-trace PK), server-side aggregates, admin-only transcripts, ingester for both output layouts, the four pages.
- C1-C4 — the four remaining interactive environments, fanning out from B2.
- D flight + office-nav; drive-101 per Decision 3.
- Forge:
prime-rlrenderer (LoRA RL first), x86_64 train lock, retitled aarch64 lock, DECISIONS entry, checkpoint→HF→NVFP4→Compute-model-id stage. - W /stream, landing (with its own
machine-home.cssaudit), compute/simulate/about. - E — Hub push, when un-parked.
4. Decisions Karti still owes, ranked by what they block
| # | Decision | Blocks | Recommendation |
|---|---|---|---|
| 1 | The agent seat name. agent (matching SingleAgentEnvConfig) or a custom name? It becomes the TOML key in configs/*.toml and in every prime-rl rl.toml, on both A and D. Renaming later parses fine and silently has no seat. |
A, D, every future env, every training config | agent, everywhere. One word, decided once, written into each env's README. |
| 2 | Does D live in arena or tera? The spec's packaging argument is strong (tera has no Python toolchain) but it overturns PLAN.md:138 and makes D collide with A/B/W. |
D's entire file layout, the CI wheel loop, PLAN.md | arena, and amend PLAN.md's parallelization map in the same commit. |
| 3 | Does tera-drive-101 ship? Measured: monotone in throttle, optimal policy is one constant string, and the 0.9 band makes any positive throttle score ~0.95+. Also decides whether the site says four spatial environments or three. |
D's scope, arena.tsx/seo.ts copy, the Hub listing |
Ship it labelled a bridge-correctness environment with no rule-4 and no rule-3 claim, or defer. Do not list it as a fifth RL environment. |
| 4 | A's freeze mechanic: flat per-look, or proportional to items re-quoted? Flat is measured and works; per-item is the better mechanism, makes the look count genuinely vary, and forces a much larger surgical reference. | A's engine, and therefore B2's calibration | Ship flat. Record per-item as the identified strengthening. Do not build an unmeasured mechanism on the critical path. |
| 5 | Arena transcript visibility. Full transcripts contain the held-out slice and, for oracle runs, the answers. | P2's route scoping, and house rule 1's integrity | Admin-only. This is not an org-politics question; it is a contamination question, and that settles it. |
| 6 | Where does model-run data live? Public eval-results.json (already typed, tested, rendered on /evals) vs P2's arena_runs/arena_episodes tables. |
W's whole data pipeline, P2's schema | Both, with a direction: the dashboard is the archive, eval-results.json is a curated public summary emitted from it. Never two independent hand-maintained files. |
| 7 | Is nemotron coming back as the production brain, or is qwen38 the default? README.md's baseline, D23/D24's Forge preflight, and the site's Arena numbers all assume Nemotron; the box serves Qwen3.8-27B. |
Every reproduction of the baseline; what "inference in production on the Spark" names | Needs an answer before anyone re-runs a benchmark. |
| 8 | First Forge/prime-rl integration: LoRA RL or full-FT? LoRA reuses merge + attest unchanged and matches alphabet-sort; but LoRA rejects --gpu-type and falls back to filesystem weight broadcast. Full-FT needs a new node kind and the tensor-delta-map gate never fires. |
Forge's roadmap, and what its manifest is for | LoRA first — it keeps Forge's headline claim intact. |
| 9 | Is app.* the only authenticated origin? Yes means the apex loses /login, /signup, /app, /go, and all three existing sessions are signed out on cutover day. |
P1's scope, apex route deletions | Yes, and state the sign-out as an accepted cost for three accounts. |
| 10 | W's audience. Arena/Bench/Stream (brokenness × argument) vs Arena/Landing/Bench (the Prime Intellect Hub reader). | W's priority order only | Answer when P1 lands; it does not block anything upstream. |
5. Risk register, highest first
| # | Risk | Retired by |
|---|---|---|
| 1 | The whole reward design of grand-exchange-live rests on numbers from a throwaway engine that no longer exists, and the ladder does not reconcile with the reward formula. horizon=60, FREEZE_RANGE=(1,4), TARGET_SHARE=0.85 and TURNS=8 are all locked against arithmetic that is off by 0.011 in the one place it can be checked. |
Building live.py FIRST and re-measuring every row before any constant is frozen. This is Phase 1, lane 2, and it is the single most important task in the programme. |
| 2 | Every reward in Arena saturates, so "exhaustive < oracle" may be structurally unreachable. profit_ratio and efficiency clip at 1.0; clean is a boolean band. A 0.02 margin against a 0.25 quantum is a cliff. If the gate can't open, rule 4 is unenforceable and every interactive environment is unfalsifiable. |
A's re-measurement producing a real exhaustive/oracle gap; B2 then calibrated against it with block-wise ladders. If no gap exists, the reward shape must change before C1-C4 are built on it. |
| 3 | No traces.jsonl exists anywhere. Everything downstream — the dashboard, the site's numbers, the leaderboard, the before/after training table — is waiting on one eval that has never completed. The one run that was attempted pointed at Prime's inference API with an empty key. |
One completed eval against brain-qwen38-dspark with the empty-rewards count checked. Cheap, and it unblocks P2, W and Compute Policy simultaneously. |
| 4 | eval exits 0 when every rollout errors, and multi-turn has far more ways to error than one-shot. turn() on a closed interaction raises; turn(msg) on turn 1 of a prompted task raises. Combined with risk 3, a "successful" run can be entirely empty. |
A CI/runbook step that asserts non-empty rewards on ≥N episodes, in arena, in D's replay gate, and in P2's ingester. Never trust the exit code. |
| 5 | Silent wrong-model comparison. --model brain returns 200 from a box serving Qwen3.8-27B. Re-running the Nemotron baseline today produces different-model numbers under an identical command, and they will be tabulated as comparable. |
Pinning brain-qwen38-dspark (and the scene) in every command in README.md, PLAN.md, MULTI_TURN.md and COMPUTE_POLICY.md. Trivial; do it in Phase 1. |
| 6 | probe.py, tests/test_probe.py and ci.yml are edited by five of six workstreams, in a repo that already took divergent concurrent pushes. |
B1, first, before anything else touches arena. |
| 7 | The reward denominator is editable with every tera gate green. ⚠️ CORRECTED 2026-08-21: not offices/sites.ts (mutating it produces a bit-identical episode — refuted twice). The hole is src/arena/index.ts, which re-exports the scripted baselines that ARE the denominator. Demonstrated: a halved office-nav baseline changes return 4.744 → 4.098 while arena:source-hashes prints ok and exits 0. |
Pinning src/arena/index.ts in tera/scripts/check-arena-source-hashes.mjs and regenerating sourceHashes.ts. One line, in a repo this session did not own. |
| 8 | spark-1 has 36 GB of 121 GB free with four resident model services, one of which leaks ~8.4 GB/hour and has killed the brain before. "Production inference on the Spark" is a tenancy claim held together by the governor on :8011. | A mandatory headroom check before any new resident model, and Compute's watchdog verified live. |
| 9 | PRIME_API_KEY is empty, blocking not just the parked Hub push but every rental command, every price, and the entire training half of the compute policy. |
prime login. One command. |
| 10 | Two of D's four environments cannot discriminate under the 0.9 ceiling band — drive-101 gives ~0.95+ to any constant throttle, office-nav collapses to efficiency alone. |
Re-deriving the band per environment against the measured return distribution, or shipping fewer environments. |
| 11 | The held-out corpus leaks through P2's trace viewer at user scope. One invite code. |
Decision 5 — admin-only. |
| 12 | Effort is understated across the board. A is L/XL; B2 is not B's to size (its §1.8 is L/XL and belongs to A); D is L/XL; P's "Phase 1" is XL; W's item 1 alone is L/XL across two repos. Read as written, this programme looks like six L's and is closer to four XLs. | The phasing in §3, which cuts P1 out of P, B1 out of B, and D-step-1 out of D. |
| 13 | Rented GPU hours are burned by a non-binding turn budget. group_size × batch × steps of multi-turn rollouts against a rented inference fleet is real money, and nothing in the compute doc connects to rule 4. |
Making "exhaustive < oracle by a measured margin" a precondition of dispatching any RL run — write it into COMPUTE_POLICY.md. |
| 14 | The checkpoint has no exit path. prime pods terminate destroys ephemeral disk; the DCP → HF → NVFP4 → Compute-model-id conversion is unautomated. |
--disks at pod-create time, plus a forge quantize/publish stage. Discover this before the first successful training run, not after. |
| 15 | GH200 may not appear in prime availability at all — the CLI names only H100_80GB, H200_141GB, B200_180GB, RTX_PRO_6000. "GH200-class" may mean H200_141GB in practice. And perf.py has no GH200 branch, so MFU may silently fall back to A100's 312 TFLOPS. |
prime login, then one availability gpu-types, then one nvidia-smi --query-gpu=name on a real pod. |
6. Build this first
environments/grand_exchange_live/grand_exchange_live/live.py — the stepped engine and its unit tests — and, concurrently, workstream B1's discovery de-hardcoding.
Everything else in the programme is downstream of two facts we do not yet have: whether the live market's reward ladder actually reconciles (risk 1, which invalidates A's horizon, freeze, TARGET_SHARE and turn budget if it does not, and takes B2, C1-C4 and half the compute argument with it), and whether three files can stop being a five-way merge conflict before five workstreams reach for them. B1 is an afternoon and removes the repo's only structural collision permanently; live.py is the smallest artifact that can turn six specs' worth of /tmp measurements into something the shipped code agrees with.