{
  "notes": "EPISODE 1 (2026-11-09). Nothing had run. Read the solved-folder README at the pinned commit (gtmcknight/amm-sim e65314f8 research/solved). Plan: run both tracks from episode 1, batches of <=5 so none holds behind a canary, spare copies on lines that need api.imdnotdead.fun or the engine, research mode throughout (code travels as code_zb64 + sha256 in results; no code trunk yet).\n\nKEY FACTS FROM THE README: all gains are vs HA on replica seeds 10000..12047 (HA mean 529.557). IB = HA +0.59 on the engine. Reference policies on real inputs: gauss+plain +2.83, gauss+qm +3.77, lattice+qm +4.26, true-flag diagnostic +6.98 (= #1). Top-10 line is +3.25 vs HA (+2.66 vs IB); #1 about +7.0. Track A gates: fees within 0.01 bps of real.py on callbacks of replica seeds 10000..10019, afterSwap peak <= 245,000, 0 OOG, 0 revert, amm-match validate. Engine bar: +2.7 over IB on 4,000 paired seeds. Tables are 8 MB JSON: 321 w rows x (13 cols phase 0, 49 cols phases 1-2) x 8 sd levels x s/c modes; plain tables alone are ~71k cells, so compression is required to fit EIP-170 code size (24,576 bytes). Track B: misread rate 16.2% worth +2.72; already tried without gain: DP_MASK quote-zone forbidding, kappa > 1e-5, quote-aware fill likelihood, table classifier on filter features (18.8%). Fleet rules: no speed gates, 64-seed engine commands (32 on slow seats), no command over 8 minutes, never fill missing seeds, paired stats carry sum and sumsq, keep filled briefs < 2,900 chars with every placeholder (key included) in every item's vars as a string, ask for numbers not command text, results < 60,000 bytes.\n\nENGINE SEED LEDGER (used once, never reused): 12000000..12001023 assigned to a1-ib-engine (IB alone, 4 items x 256: ib-12000000, ib-12000256, ib-12000512, ib-12000768). Per-seed IB rows from those items are the baseline half of the first port-vs-IB pairing: the first validated port must run on exactly these seeds (only seeds IB actually returned). Next free engine seed: 12001024. Replica tuning seeds in use: 60000..60511 (ideas, compression, misread), 61000..61255 (misread holdout). Holdout 10000..12047 is scored once per idea.\n\nBATCHES SPAWNED (33 items; Track A 19, Track B 14):\nTrack A: a1-gates (2 copies: gates.py digits, contract bounds from the amm-challenge checkout, IB validate + bytecode size); a1-fixture (2 copies: deterministic recorder of real.py callbacks + fees for seeds 10000..10019, plain and qm, sha256s; the fixture itself is too big to travel, the recorder does); a1-ib-engine (4 x 256 seeds); a1-compress (5 ideas: knots, mirror, sdlevels, rivalmode, fit; pass = within 0.2 of +3.77 vs ref/gauss_qm.json); a1-port-parts (math lib x2, table packer x2); a1-port-full (2 copies, plain tables, full gate set).\nTrack B: b1-numba (3 copies: numba replica2+latt matching gates to the digit, speed); b1-ideas (5 README ideas: posterior, twohyp, perrun, router, trained); b1-fresh (3 copies told to skip the README idea lists); b1-misread (3 feature families: amounts, hindsight, size; diagnostics only, AUC and misread on 61000..61255).\n\nNEXT EPISODE SHOULD: (1) Check a1-gates: do gates match to the digit, what are the real bounds (gas, slots, code size, solc). If gates do not match, stop Track A ports until the replica/digits question is settled. (2) From a1-compress pick the smallest table set that passes (-0.2 vs gauss_qm) and its byte count; feed the knot set into the next port batch and the table packer. (3) From a1-port-parts and a1-port-full take the best math lib and packer (lowest error, lowest gas) as the starting code_zb64 for an episode-2 port batch targeting the qm tables; if a1-port-full reached parity, run it on the engine on seeds 12000000..12001023 against the IB rows, then widen on fresh seeds from 12001024 in 256-seed items (both arms per seed range in the same item from then on, so the pair never depends on two items). Check the replica-vs-engine consistency rule: port gain over IB on the engine should be within 0.2 of (its replica gain over HA minus 0.59). (4) From b1-ideas/b1-fresh: any idea with holdout mean_diff >= +0.3 vs ref/lattice_qm.json (se small) goes to Track A's next port and gets a confirmation item; ideas between 0 and 0.3 get one tuning item; negative ones are dropped unless notes argue a clear fix. Combine b1-misread findings (the top feature) into a new idea item. (5) Use the numba port from b1-numba (if gates_match) as the runner in all later Track B briefs to cut holdout time. (6) Budget: 207 items left after this episode, 19 episodes; aim ~25-40 items per episode, widen only briefs that worked. Track A and B stay about even until a port passes the engine; then shift to A (engine confirmation to 4,000 seeds) and B-to-A transfer.\n\nWATCH FOR: items refused for brief length or missing placeholders (every brief here was checked under 2,900 filled chars, all placeholders present); seats that cannot reach api.imdnotdead.fun (a1-gates, a1-ib-engine) or are not Linux x86_64 (they report engine unavailable; that is fine for gates/fixture); results over 60,000 bytes (code_zb64 too big: ask for scripts only, no tables).",
  "spawn": [
    {
      "name": "a1-gates",
      "brief": "Track A, step 1, copy {{copy}} of 2 (the other copy runs the same work; do all of it yourself). Get the solved folder: B=https://raw.githubusercontent.com/gtmcknight/amm-sim/e65314f88de945f6d055bfc2aab01c482ab6570e/research/solved; curl -LsSf $B/fetch.sh -o fetch.sh && sh fetch.sh $B scratch/solved; read scratch/solved/README.md (its scripts run as python3 -I from that folder). Three pieces, in order; report each even if a later one fails. 1. GATES: run python3 -I gates.py. Report the 12 numbers it prints for seeds 10000..10002 (perfect-info, gaussian+plain, gaussian+qm, lattice+qm) and whether every one matches the README Gates table to the digit. 2. BOUNDS: Engine setup (Linux x86_64 only; elsewhere set engine:\"unavailable\" and report what you can): git clone https://github.com/benedictbrady/amm-challenge and check out 6b6921f8d463314aa7c96234e1be25a25245f704; from the checkout root: mkdir -p scratch && curl -LsSf https://raw.githubusercontent.com/gtmcknight/amm-sim/main/research/setup.sh -o scratch/setup.sh && sh scratch/setup.sh; prefix later commands with \". scratch/env.sh && \". If setup fails, report blocked:{step,error} with whatever you finished. From the checkout docs, validator, compiler settings and simulator source, report the exact limits a strategy contract must meet: gas per callback, storage slots, inline assembly allowed or not, code size, Solidity version and optimizer settings, banned opcodes or external calls, and for each the file that enforces it. 3. IB: fetch https://api.imdnotdead.fun/artifacts/afd5183e9ff3235334e041c00b0cd19079599a2c6464db95f8c1586712105557 (JSON); decode its code_zb64 (base64, then zlib) to IB.sol. Run amm-match validate on it; report pass or fail and a one-line summary, its deployed bytecode size, and the peak afterSwap gas if the toolkit reports one. Use no engine seed at or above 12000000 (reserved). Result: one JSON object under 60,000 bytes: {\"key\":\"{{key}}\",\"gates\":{\"10000\":[4 numbers],\"10001\":[4 numbers],\"10002\":[4 numbers],\"match\":true|false},\"bounds\":{\"gas_per_callback\":int,\"storage_slots\":int,\"inline_assembly\":true|false,\"code_size_bytes\":int,\"solc\":\"...\",\"optimizer\":\"...\",\"other\":\"...\",\"enforced_in\":[\"file\",...]},\"ib\":{\"validate\":\"pass\"|\"fail\"|\"not run\",\"summary\":\"...\",\"bytecode_bytes\":int,\"peak_afterswap_gas\":int|null},\"engine\":\"ok\"|\"unavailable\",\"blocked\":null|{\"step\":\"...\",\"error\":\"...\"},\"notes\":\"under 1500 chars: surprises, what a porter must know\"}. Give numbers as numbers, never pasted command output.",
      "items": [
        {
          "key": "gates-1",
          "vars": {
            "key": "gates-1",
            "copy": "1"
          }
        },
        {
          "key": "gates-2",
          "vars": {
            "key": "gates-2",
            "copy": "2"
          }
        }
      ],
      "output": {
        "format": "json",
        "maxBytes": 60000
      },
      "merge": {
        "groupSize": 20,
        "brief": "Keep every item's result whole; list disagreements between copies first."
      }
    },
    {
      "name": "a1-fixture",
      "brief": "Track A parity fixture, copy {{copy}} of 2 (the other copy does the same; do all of it yourself). Get the solved folder: B=https://raw.githubusercontent.com/gtmcknight/amm-sim/e65314f88de945f6d055bfc2aab01c482ab6570e/research/solved; curl -LsSf $B/fetch.sh -o fetch.sh && sh fetch.sh $B scratch/solved; read scratch/solved/README.md (its scripts run as python3 -I from that folder). Write recorder.py in scratch/solved: it runs the Gaussian policy of real.py on the replica for seeds 10000..10019 twice, with the plain tables (plain:soft=1) and with qm:soft=1,qm=qm_k1e-5.json, and logs every callback as one JSON line: afterInitialize with initialX and initialY, and each afterSwap with isBuy, amountX, amountY, timestamp, reserveX, reserveY exactly as the engine passes them (integers as decimal strings when above 2^53), followed by the bid fee and ask fee the policy returns after that call (bps, 6 decimals) and its state after the call (m, v, s2, synced, cnt, fb, fa). One file per table set, fixture_plain.jsonl and fixture_qm.jsonl, lines in run order with a seed field. Make the output deterministic so another machine regenerates it byte for byte, and report the sha256 of each file. Check: replaying each file through a fresh real.py policy reproduces its fees exactly. Result: one JSON object under 60,000 bytes: {\"key\":\"{{key}}\",\"files\":{\"fixture_plain.jsonl\":{\"sha256\":\"...\",\"bytes\":int,\"lines\":int},\"fixture_qm.jsonl\":{\"sha256\":\"...\",\"bytes\":int,\"lines\":int}},\"callbacks_per_run\":{\"min\":int,\"mean\":number,\"max\":int},\"first_of_step_fraction\":number,\"fee_bps\":{\"min\":number,\"max\":number,\"zero_fraction\":number},\"replay_exact\":true|false,\"code_zb64\":\"base64 of zlib of a tar holding recorder.py and helpers (your files only, not the fetched folder)\",\"code_sha256\":\"sha256 of that tar\",\"notes\":\"under 1500 chars: how to run it, the real.py details a porter must match (rounding, clamps, the timestamp 0 case)\"}.",
      "items": [
        {
          "key": "fixture-1",
          "vars": {
            "key": "fixture-1",
            "copy": "1"
          }
        },
        {
          "key": "fixture-2",
          "vars": {
            "key": "fixture-2",
            "copy": "2"
          }
        }
      ],
      "output": {
        "format": "json",
        "maxBytes": 60000
      },
      "merge": {
        "groupSize": 20,
        "brief": "Keep every item's result whole; say whether the two copies' sha256 values agree."
      }
    },
    {
      "name": "a1-ib-engine",
      "brief": "Track A engine baseline: IB alone on fresh engine seeds {{from}}..{{to}} (256 seeds, yours alone; use no seed outside this range). Engine setup (Linux x86_64 only; elsewhere set engine:\"unavailable\" and report what you can): git clone https://github.com/benedictbrady/amm-challenge and check out 6b6921f8d463314aa7c96234e1be25a25245f704; from the checkout root: mkdir -p scratch && curl -LsSf https://raw.githubusercontent.com/gtmcknight/amm-sim/main/research/setup.sh -o scratch/setup.sh && sh scratch/setup.sh; prefix later commands with \". scratch/env.sh && \". If setup fails, report blocked:{step,error} with whatever you finished. Fetch https://api.imdnotdead.fun/artifacts/afd5183e9ff3235334e041c00b0cd19079599a2c6464db95f8c1586712105557 (JSON) and decode its code_zb64 (base64, then zlib) to IB.sol. Run IB against VanillaStrategy on your seeds with amm_sim_rs.run_batch(X, VanillaStrategy, configs, 8), configs built the way MatchRunner._build_configs builds them, one per seed (read the checkout for how a seed becomes a config), in commands of 64 seeds; if one 64-seed command takes over 8 minutes, use 32-seed commands. Save each block per-seed rows under scratch/ as you go. Start no new block after 45 minutes of engine time and report what you have; never fill in or estimate a missing seed. Collect gas_stats() for IB (peak afterSwap gas, mean, OutOfGas count, revert count) and set valid false on any OutOfGas, any revert, or a peak over 245,000. Result: one JSON object under 60,000 bytes: {\"key\":\"{{key}}\",\"contract\":\"IB\",\"seeds\":{\"from\":{{from}},\"to\":{{to}}},\"n\":int,\"rows\":[[seed,score],...] with score to 6 decimals,\"sum\":number,\"sumsq\":number,\"mean\":number,\"gas_stats\":{\"peak_afterswap\":int,\"mean_afterswap\":int,\"oog\":int,\"reverts\":int},\"valid\":true|false,\"block_seconds\":[seconds per block],\"engine\":\"ok\"|\"unavailable\",\"blocked\":null|{\"step\":\"...\",\"error\":\"...\"},\"notes\":\"under 1000 chars: the functions you used to build configs and run, anything odd\"}.",
      "items": [
        {
          "key": "ib-12000000",
          "vars": {
            "key": "ib-12000000",
            "from": "12000000",
            "to": "12000255"
          }
        },
        {
          "key": "ib-12000256",
          "vars": {
            "key": "ib-12000256",
            "from": "12000256",
            "to": "12000511"
          }
        },
        {
          "key": "ib-12000512",
          "vars": {
            "key": "ib-12000512",
            "from": "12000512",
            "to": "12000767"
          }
        },
        {
          "key": "ib-12000768",
          "vars": {
            "key": "ib-12000768",
            "from": "12000768",
            "to": "12001023"
          }
        }
      ],
      "output": {
        "format": "json",
        "maxBytes": 60000
      },
      "merge": {
        "groupSize": 20,
        "brief": "Keep every item's rows; pool n, sum and sumsq across items."
      }
    },
    {
      "name": "a1-compress",
      "brief": "Track A, step 2: table compression on the replica, Python only. Get the solved folder: B=https://raw.githubusercontent.com/gtmcknight/amm-sim/e65314f88de945f6d055bfc2aab01c482ab6570e/research/solved; curl -LsSf $B/fetch.sh -o fetch.sh && sh fetch.sh $B scratch/solved; read scratch/solved/README.md (its scripts run as python3 -I from that folder). Baseline: the Gaussian plus qm policy (run.py gauss with soft=1 qm=qm_k1e-5.json) scores +3.77 over HA on 10000..12047; ref/gauss_qm.json holds its per-seed rows. Your one idea, and only it: {{idea_spec}}. For each candidate build the reduced table set, expand it back to the shipped grid (321 rows, 13 and 49 columns, 8 sd levels, same keys) and save it in the format of qm_k1e-5.json; count its cells and its bytes at 1 byte per cell (offsets rounded to whole bps) plus knot indices. Choose among candidates on tuning seeds: python3 -I run.py gauss 60000 256 base.json soft=1 qm=qm_k1e-5.json once, then run.py gauss 60000 256 cand.json soft=1 qm=<expanded file> and pair.py cand.json base.json. Score your best two or three once on the holdout: run.py gauss 10000 2048 out.json soft=1 qm=<file> (chunks of 512 if slow) and pair.py out.json ref/gauss_qm.json; a candidate passes if mean_diff is -0.2 or better. If time is short score fewer holdout seeds from 10000 upward in order and report n. First check that the unmodified file reproduces ref/gauss_qm.json exactly on 10000..10063. Result: one JSON object under 60,000 bytes: {\"key\":\"{{key}}\",\"idea\":\"{{idea}}\",\"sanity_exact\":true|false,\"candidates\":[{\"name\":\"...\",\"cells\":int,\"bytes_1b\":int,\"storage_slots_32b\":int,\"tuning_paired\":{\"n\":int,\"mean_diff\":number,\"se\":number,\"worst5\":[...]},\"holdout_paired\":null|{\"n\":int,\"mean_diff\":number,\"se\":number,\"sum\":number,\"sumsq\":number,\"worst5\":[...]},\"passes\":true|false,\"recipe\":\"under 400 chars: which knots, levels and columns are kept and how it expands\"}],\"best\":\"name\",\"code_zb64\":\"base64 of zlib of a tar with your reducer and expander scripts (your files only, no tables)\",\"code_sha256\":\"...\",\"notes\":\"under 1500 chars: the cost curve, what to try next\"}.",
      "items": [
        {
          "key": "compress-knots",
          "vars": {
            "key": "compress-knots",
            "idea": "knots",
            "idea_spec": "a subset of the 321 w rows (for example every 2nd row within 100 bps of w = 0, every 4th to 400 bps, every 8th beyond) with linear interpolation back to the full grid; sweep the density to find the coarsest set that passes"
          }
        },
        {
          "key": "compress-mirror",
          "vars": {
            "key": "compress-mirror",
            "idea": "mirror",
            "idea_spec": "the mirror symmetry ask(w, R, level) = bid(-w, -R, level): store one of each A/B pair and mirror the other; measure the symmetry alone, then combined with every 2nd and every 4th w row"
          }
        },
        {
          "key": "compress-sdlevels",
          "vars": {
            "key": "compress-sdlevels",
            "idea": "sdlevels",
            "idea_spec": "fewer belief sd levels: subsets of the 8 levels in S (4, 3, 2 levels, and 1 = plain-like) with linear interpolation between kept levels; find the fewest that pass"
          }
        },
        {
          "key": "compress-rivalmode",
          "vars": {
            "key": "compress-rivalmode",
            "idea": "rivalmode",
            "idea_spec": "one rival mode and fewer R columns: drop the s/c split for phases 1 and 2 (s only, c only, or their average) and reduce the 49 R columns to 13, 9 and 7 with linear interpolation"
          }
        },
        {
          "key": "compress-fit",
          "vars": {
            "key": "compress-fit",
            "idea": "fit",
            "idea_spec": "a low-parameter fit: per column and level a piecewise-linear or quadratic function of w with a few breakpoints, or a low-rank factorization of each table, offsets quantized to whole bps; report parameters and bytes"
          }
        }
      ],
      "output": {
        "format": "json",
        "maxBytes": 60000
      },
      "merge": {
        "groupSize": 20,
        "brief": "One table: idea, best candidate, bytes_1b, holdout mean_diff, se, passes."
      }
    },
    {
      "name": "a1-port-parts",
      "brief": "Track A, step 3, one part of the Solidity port, copy {{copy}} of 2 (the other copy does the same part; do all of it yourself). Get the solved folder: B=https://raw.githubusercontent.com/gtmcknight/amm-sim/e65314f88de945f6d055bfc2aab01c482ab6570e/research/solved; curl -LsSf $B/fetch.sh -o fetch.sh && sh fetch.sh $B scratch/solved; read scratch/solved/README.md (its scripts run as python3 -I from that folder). The policy to port is real.py (README section \"The reference policy on real inputs\"); the contract must meet the limits of amm-challenge (clone https://github.com/benedictbrady/amm-challenge at 6b6921f8d463314aa7c96234e1be25a25245f704 and read its validator and docs: about 250,000 gas a callback, 32 storage slots, no inline assembly, the EIP-170 code size). Your part, and only it: {{part_spec}} Deliver Solidity sources and Foundry tests that pass offline (forge test; vendor any library as plain files, no submodules). Compare against Python values computed with the standard library or mpmath and written into the tests as constants. Nothing may revert on out-of-range input: clamp instead. Result: one JSON object under 60,000 bytes: {\"key\":\"{{key}}\",\"part\":\"{{part}}\",\"fixed_point\":\"the representation and why\",\"functions\":[{\"name\":\"...\",\"domain\":\"...\",\"max_rel_error\":number,\"max_abs_error\":number,\"gas\":int}],\"tests\":{\"passed\":int,\"failed\":int},\"bytes_runtime\":int,\"knots\":\"TABLES only: the knot set, under 300 chars\",\"code_zb64\":\"base64 of zlib of a tar with src/ and test/ (your files only)\",\"code_sha256\":\"...\",\"notes\":\"under 1500 chars: edge cases handled, what remains\"}.",
      "items": [
        {
          "key": "math-1",
          "vars": {
            "key": "math-1",
            "copy": "1",
            "part": "math",
            "part_spec": "MATH: a library with ln, exp, the standard normal pdf and cdf, and the mean and variance of a normal cut to (lo, hi), over the ranges the filter meets: ln of reserve ratios (arguments within a factor 2 of a reference), exp of values within 0.1 of 0, sd from 0 to 300 bps, truncation windows 0 to 200 bps wide, including a point mass (v = 0) and a window far from the mean. Aim for relative error 1e-9 or better so fees can match real.py within 0.01 bps, with the smallest gas you can; report gas per function."
          }
        },
        {
          "key": "math-2",
          "vars": {
            "key": "math-2",
            "copy": "2",
            "part": "math",
            "part_spec": "MATH: a library with ln, exp, the standard normal pdf and cdf, and the mean and variance of a normal cut to (lo, hi), over the ranges the filter meets: ln of reserve ratios (arguments within a factor 2 of a reference), exp of values within 0.1 of 0, sd from 0 to 300 bps, truncation windows 0 to 200 bps wide, including a point mass (v = 0) and a window far from the mean. Aim for relative error 1e-9 or better so fees can match real.py within 0.01 bps, with the smallest gas you can; report gas per function."
          }
        },
        {
          "key": "tables-1",
          "vars": {
            "key": "tables-1",
            "copy": "1",
            "part": "tables",
            "part_spec": "TABLES: pack the six plain tables of pol_mid.json (A0 B0: 321 rows by 13 columns; A1 B1 A2 B2: 321 by 49; offsets in bps) into contract code or at most 32 storage slots within EIP-170, with lookup(phase, w_bps, R_bps) returning ask and bid offsets by bilinear interpolation as real.py does (edge values beyond the grid). The full grid will not fit: pick a knot subset that does (for example every 4th row near w = 0 to every 16th far out, 7 to 13 R columns), make the packer take any knot set so the compression track's choice drops in, and report the knot set used. Test 10,000 random (phase, w, R) lookups against a Python bilinear on the same knots: max error must be 0. Report bytes and gas per lookup."
          }
        },
        {
          "key": "tables-2",
          "vars": {
            "key": "tables-2",
            "copy": "2",
            "part": "tables",
            "part_spec": "TABLES: pack the six plain tables of pol_mid.json (A0 B0: 321 rows by 13 columns; A1 B1 A2 B2: 321 by 49; offsets in bps) into contract code or at most 32 storage slots within EIP-170, with lookup(phase, w_bps, R_bps) returning ask and bid offsets by bilinear interpolation as real.py does (edge values beyond the grid). The full grid will not fit: pick a knot subset that does (for example every 4th row near w = 0 to every 16th far out, 7 to 13 R columns), make the packer take any knot set so the compression track's choice drops in, and report the knot set used. Test 10,000 random (phase, w, R) lookups against a Python bilinear on the same knots: max error must be 0. Report bytes and gas per lookup."
          }
        }
      ],
      "output": {
        "format": "json",
        "maxBytes": 60000
      },
      "merge": {
        "groupSize": 20,
        "brief": "Keep every item's result whole; per part, say which copy has the smaller error and gas."
      }
    },
    {
      "name": "a1-port-full",
      "brief": "Track A, step 3, the whole Solidity port, copy {{copy}} of 2 (the other copy attempts the same; do all of it yourself). Get the solved folder: B=https://raw.githubusercontent.com/gtmcknight/amm-sim/e65314f88de945f6d055bfc2aab01c482ab6570e/research/solved; curl -LsSf $B/fetch.sh -o fetch.sh && sh fetch.sh $B scratch/solved; read scratch/solved/README.md (its scripts run as python3 -I from that folder). Engine setup (Linux x86_64 only; elsewhere set engine:\"unavailable\" and report what you can): git clone https://github.com/benedictbrady/amm-challenge and check out 6b6921f8d463314aa7c96234e1be25a25245f704; from the checkout root: mkdir -p scratch && curl -LsSf https://raw.githubusercontent.com/gtmcknight/amm-sim/main/research/setup.sh -o scratch/setup.sh && sh scratch/setup.sh; prefix later commands with \". scratch/env.sh && \". If setup fails, report blocked:{step,error} with whatever you finished. Port steps 1 to 6 of the Gaussian policy in real.py (README section \"The reference policy on real inputs\") with the plain tables of pol_mid.json (the +2.83 target; qm tables come in a later item) to a strategy contract for amm-challenge within its limits (about 250,000 gas a callback, 32 storage slots, no inline assembly, EIP-170 code size; read the checkout validator). The full tables will not fit in code: choose a knot subset that does and measure its cost on the replica (python3 -I run.py gauss 60000 256 with the subset expanded to the full grid in pol_mid.json format vs the original on the same seeds, pair.py). Gates: fees within 0.01 bps of real.py on every callback of replica seeds 10000..10019 (record the callbacks from real.py yourself as raw engine integers and replay them through the contract in a Foundry test); afterSwap peak gas at most 245,000; no OutOfGas; no revert; amm-match validate passes. Also run stress sequences (random trades up to 15 percent of the reserve, extreme reserves, timestamp jumps of 10,000) and report them; they are not a gate, but write the contract so it cannot revert. If you cannot finish, deliver what compiles with its test results and say exactly what is left. Result: one JSON object under 60,000 bytes: {\"key\":\"{{key}}\",\"tables\":\"plain\",\"knots\":\"under 300 chars\",\"knot_cost_paired\":{\"n\":int,\"mean_diff\":number,\"se\":number},\"parity\":{\"callbacks\":int,\"max_fee_diff_bps\":number,\"over_0_01_bps\":int},\"gas\":{\"peak_afterswap\":int,\"mean_afterswap\":int,\"oog\":int,\"reverts\":int},\"validate\":\"pass\"|\"fail\"|\"not run\",\"stress\":{\"sequences\":int,\"reverts\":int,\"peak_gas\":int},\"bytes_runtime\":int,\"complete\":true|false,\"code_zb64\":\"base64 of zlib of a tar with the .sol, the tests and the Python recorder (your files only)\",\"code_sha256\":\"...\",\"engine\":\"ok\"|\"unavailable\",\"blocked\":null|{\"step\":\"...\",\"error\":\"...\"},\"notes\":\"under 1500 chars: design, fixed point used, what is left\"}.",
      "items": [
        {
          "key": "port-1",
          "vars": {
            "key": "port-1",
            "copy": "1"
          }
        },
        {
          "key": "port-2",
          "vars": {
            "key": "port-2",
            "copy": "2"
          }
        }
      ],
      "output": {
        "format": "json",
        "maxBytes": 60000
      },
      "merge": {
        "groupSize": 20,
        "brief": "Keep every item's result whole; rank copies by parity then gas."
      }
    },
    {
      "name": "b1-numba",
      "brief": "Track B tooling, copy {{copy}} of 3 (the other copies do the same; do all of it yourself). Get the solved folder: B=https://raw.githubusercontent.com/gtmcknight/amm-sim/e65314f88de945f6d055bfc2aab01c482ab6570e/research/solved; curl -LsSf $B/fetch.sh -o fetch.sh && sh fetch.sh $B scratch/solved; read scratch/solved/README.md (its scripts run as python3 -I from that folder). Make a numba port of replica2.py plus latt.py (the lattice filter with the qm tables) that matches gates.py to the digit. In a venv (uv venv --python 3.12 venv; uv pip install numpy numba; or pip) write fast_replica.py exposing the policy interface run.py accepts (module:Class, see example_policy.py) and a batch runner, so that python3 -I run.py fast_replica:Lattice 10000 3 out.json qm=qm_k1e-5.json gives exactly the lattice+qm column of the Gates table (443.663520, 571.977293, 526.411631), and the perfect-information column too if you port the scoring of evalpol.py. Keep a pure-Python fallback when numba is missing. Then measure speed: seconds per run for the lattice policy, pure Python vs yours, single process, on seeds 60000..60031; and confirm per-seed identity on 10000..10063 against ref/lattice_qm.json (count seeds differing by more than 1e-6). Result: one JSON object under 60,000 bytes: {\"key\":\"{{key}}\",\"gates_match\":true|false,\"gates\":{\"10000\":number,\"10001\":number,\"10002\":number},\"holdout_identity\":{\"n\":int,\"differ\":int,\"max_abs_diff\":number},\"seconds_per_run\":{\"pure\":number,\"numba\":number},\"speedup\":number,\"code_zb64\":\"base64 of zlib of a tar of your files only\",\"code_sha256\":\"...\",\"notes\":\"under 1500 chars: how to install and call it, which parts stayed Python, pitfalls (float operation order that changed digits)\"}.",
      "items": [
        {
          "key": "numba-1",
          "vars": {
            "key": "numba-1",
            "copy": "1"
          }
        },
        {
          "key": "numba-2",
          "vars": {
            "key": "numba-2",
            "copy": "2"
          }
        },
        {
          "key": "numba-3",
          "vars": {
            "key": "numba-3",
            "copy": "3"
          }
        }
      ],
      "output": {
        "format": "json",
        "maxBytes": 60000
      },
      "merge": {
        "groupSize": 20,
        "brief": "Keep every item's result whole; name the copy that matches gates and is fastest."
      }
    },
    {
      "name": "b1-ideas",
      "brief": "Track B: arb or retail, one idea per item. Get the solved folder: B=https://raw.githubusercontent.com/gtmcknight/amm-sim/e65314f88de945f6d055bfc2aab01c482ab6570e/research/solved; curl -LsSf $B/fetch.sh -o fetch.sh && sh fetch.sh $B scratch/solved; read scratch/solved/README.md (its scripts run as python3 -I from that folder). The lattice filter (latt.py with qm_k1e-5.json) decides whether the first own trade of a step is the arb or a retail order and is wrong 16.2 percent of the time; with the true flag the same code gains +2.72 (pair.py ref/flag_qm.json ref/lattice_qm.json). Your idea, and only this one: {{idea_spec}}. Build it as a blind policy for run.py (module:Class as in example_policy.py: it sees only afterInitialize and afterSwap inputs). Tune only on replica seeds 60000 and up: python3 -I run.py lattice 60000 256 base.json qm=qm_k1e-5.json once, yours on the same seeds, pair.py cand.json base.json. Then score once on the holdout 10000..12047 in chunks (run.py yourmod:Class 10000 512 part0.json qm=qm_k1e-5.json, then 10512, 11024, 11536) and pair.py part0.json,part1.json,part2.json,part3.json ref/lattice_qm.json. If pure Python is too slow for all of it in your time, score from 10000 upward in order, as many as finish, and report n; never skip or fill in seeds. Measure also, on 60000..60255 with the true flag from the replica (see clsdata.py and the flag:oraclearb diagnostic of real.py): your misread rate on first-of-step trades against the lattice filter, and the price error (rms of price estimate minus ln of the true price, bps, at each quote). Estimate what the idea costs in a contract: operations per afterSwap, state kept, rough gas. Result: one JSON object under 60,000 bytes: {\"key\":\"{{key}}\",\"idea\":\"{{idea}}\",\"tuning_paired\":{\"n\":int,\"mean_diff\":number,\"se\":number,\"worst5\":[...]},\"holdout_paired\":{\"n\":int,\"mean_diff\":number,\"se\":number,\"sum\":number,\"sumsq\":number,\"worst5\":[...]},\"misread_rate\":{\"lattice\":number,\"mine\":number},\"price_error_bps\":{\"lattice\":number,\"mine\":number},\"gas_estimate\":\"under 300 chars\",\"code_zb64\":\"base64 of zlib of a tar of your files only\",\"code_sha256\":\"...\",\"notes\":\"under 1500 chars: what you tried, what moved the number, what failed, next step\"}.",
      "items": [
        {
          "key": "idea-posterior",
          "vars": {
            "key": "idea-posterior",
            "idea": "posterior",
            "idea_spec": "choose quotes against the whole lattice posterior instead of its mean and sd: get the value arrays dp.py saves with DP_SAVEVN=1 (one solve, or load them if present) and pick the ask and bid offsets that maximize the posterior-weighted value over the 121 cells"
          }
        },
        {
          "key": "idea-twohyp",
          "vars": {
            "key": "idea-twohyp",
            "idea": "twohyp",
            "idea_spec": "keep the arb reading and the retail reading of each first-of-step trade as two explicit hypotheses, each with its own rival spot, and choose the next quotes so the next arb trade tells them apart; collapse to one when the evidence is decisive"
          }
        },
        {
          "key": "idea-perrun",
          "vars": {
            "key": "idea-perrun",
            "idea": "perrun",
            "idea_spec": "per-run estimates of sigma and the order rate inside the classifier, updated online from this run's own trades, replacing the fixed sigma 9.45 bps and rho 0.26"
          }
        },
        {
          "key": "idea-router",
          "vars": {
            "key": "idea-router",
            "idea": "router",
            "idea_spec": "the router likelihood per price hypothesis: for each candidate price compute how the router would have split the observed order between own pool and the rival at that hypothesis's rival spot, and use the consistency of amountX and amountY with the reserves as the likelihood of retail against arb"
          }
        },
        {
          "key": "idea-trained",
          "vars": {
            "key": "idea-trained",
            "idea": "trained",
            "idea_spec": "a correction trained through the filter: start from the tables and fit a small adjustment to the classifier (a logistic on the filter's features plus trade size and dt, or a shift of the pi threshold) on seeds 60000 and up, then re-score"
          }
        }
      ],
      "output": {
        "format": "json",
        "maxBytes": 60000
      },
      "merge": {
        "groupSize": 20,
        "brief": "One table: idea, holdout n, mean_diff, se, misread rate, price error; best first."
      }
    },
    {
      "name": "b1-fresh",
      "brief": "Track B: arb or retail, from the question alone, copy {{copy}} of 3 (the other copies work independently; do not coordinate). Get the solved folder: B=https://raw.githubusercontent.com/gtmcknight/amm-sim/e65314f88de945f6d055bfc2aab01c482ab6570e/research/solved; curl -LsSf $B/fetch.sh -o fetch.sh && sh fetch.sh $B scratch/solved; read scratch/solved/README.md (its scripts run as python3 -I from that folder). The lattice filter (latt.py with qm_k1e-5.json) decides whether the first own trade of a step is the arb or a retail order and is wrong 16.2 percent of the time; with the true flag the same code gains +2.72 (pair.py ref/flag_qm.json ref/lattice_qm.json). Skip the README sections \"Two tracks for a goal\" and \"Open\": they list ideas already tried or assigned, and your value is an approach not on them. Read the mechanics instead (replica2.py: how the arb trades, how the router splits a retail order across the two pools, what afterSwap sees; latt.py: how the filter reads the first trade) and find your own way to tell the arb from retail, or to make the mistake cheaper. One idea only, named as {{idea}} in the result. Build it as a blind policy for run.py (module:Class as in example_policy.py: it sees only afterInitialize and afterSwap inputs). Tune only on replica seeds 60000 and up: python3 -I run.py lattice 60000 256 base.json qm=qm_k1e-5.json once, yours on the same seeds, pair.py cand.json base.json. Then score once on the holdout 10000..12047 in chunks (run.py yourmod:Class 10000 512 part0.json qm=qm_k1e-5.json, then 10512, 11024, 11536) and pair.py part0.json,part1.json,part2.json,part3.json ref/lattice_qm.json. If pure Python is too slow for all of it in your time, score from 10000 upward in order, as many as finish, and report n; never skip or fill in seeds. Measure also, on 60000..60255 with the true flag from the replica (see clsdata.py and the flag:oraclearb diagnostic of real.py): your misread rate on first-of-step trades against the lattice filter, and the price error (rms of price estimate minus ln of the true price, bps, at each quote). Estimate what the idea costs in a contract: operations per afterSwap, state kept, rough gas. Result: one JSON object under 60,000 bytes: {\"key\":\"{{key}}\",\"idea\":\"{{idea}}\",\"tuning_paired\":{\"n\":int,\"mean_diff\":number,\"se\":number,\"worst5\":[...]},\"holdout_paired\":{\"n\":int,\"mean_diff\":number,\"se\":number,\"sum\":number,\"sumsq\":number,\"worst5\":[...]},\"misread_rate\":{\"lattice\":number,\"mine\":number},\"price_error_bps\":{\"lattice\":number,\"mine\":number},\"gas_estimate\":\"under 300 chars\",\"code_zb64\":\"base64 of zlib of a tar of your files only\",\"code_sha256\":\"...\",\"notes\":\"under 1500 chars: what you tried, what moved the number, what failed, next step\"}.",
      "items": [
        {
          "key": "fresh-1",
          "vars": {
            "key": "fresh-1",
            "copy": "1",
            "idea": "your own short name"
          }
        },
        {
          "key": "fresh-2",
          "vars": {
            "key": "fresh-2",
            "copy": "2",
            "idea": "your own short name"
          }
        },
        {
          "key": "fresh-3",
          "vars": {
            "key": "fresh-3",
            "copy": "3",
            "idea": "your own short name"
          }
        }
      ],
      "output": {
        "format": "json",
        "maxBytes": 60000
      },
      "merge": {
        "groupSize": 20,
        "brief": "One table: idea, holdout n, mean_diff, se, misread rate; say which ideas overlap the README list."
      }
    },
    {
      "name": "b1-misread",
      "brief": "Track B diagnostics: why the filter misreads the first trade of a step. Get the solved folder: B=https://raw.githubusercontent.com/gtmcknight/amm-sim/e65314f88de945f6d055bfc2aab01c482ab6570e/research/solved; curl -LsSf $B/fetch.sh -o fetch.sh && sh fetch.sh $B scratch/solved; read scratch/solved/README.md (its scripts run as python3 -I from that folder). The lattice filter (latt.py with qm_k1e-5.json) decides whether the first own trade of a step is the arb or a retail order and is wrong 16.2 percent of the time; with the true flag the same code gains +2.72 (pair.py ref/flag_qm.json ref/lattice_qm.json). Build a dataset of first-of-step afterSwap trades on replica seeds 60000..60511 with the true flag (adapt clsdata.py and the flag:oraclearb path of real.py), the lattice filter pi and its own features, and your feature family, and only it: {{family_spec}}. Report how separable the misread trades are by your features: the AUC and misread rate of a logistic regression or small tree on your features alone and added to the filter features, fit on 60000..60511 and tested on held-out seeds 61000..61255 (plain Python or numpy; no scoring on 10000..12047, that is for the idea items); a breakdown of misreads by dt bucket (1, 2, 3-5, 6+) and by trade size quartile; and the single most predictive feature you found with its definition. Result: one JSON object under 60,000 bytes: {\"key\":\"{{key}}\",\"family\":\"{{family}}\",\"rows\":{\"train\":int,\"test\":int},\"lattice_misread_test\":number,\"auc\":{\"filter_only\":number,\"mine_only\":number,\"filter_plus_mine\":number},\"misread_test\":{\"filter_only\":number,\"filter_plus_mine\":number},\"by_dt\":{\"1\":number,\"2\":number,\"3-5\":number,\"6+\":number},\"by_size_quartile\":[4 numbers],\"top_feature\":{\"name\":\"...\",\"definition\":\"under 300 chars\",\"auc_alone\":number},\"code_zb64\":\"base64 of zlib of a tar of your files only\",\"code_sha256\":\"...\",\"notes\":\"under 1500 chars: what the misreads have in common, what a policy should do\"}.",
      "items": [
        {
          "key": "misread-amounts",
          "vars": {
            "key": "misread-amounts",
            "family": "amounts",
            "family_spec": "the exact amounts: amountX and amountY against the reserves before and after the trade. For an arb the trade moves own spot to exactly p(1 - fb) or p/(1 - fa) at the step's price; for a retail order the router's split implies a relation between the amount and the rival pool's state. Features: the price implied if arb, the rival spot implied if retail, and each one's consistency with the previous step's estimates"
          }
        },
        {
          "key": "misread-hindsight",
          "vars": {
            "key": "misread-hindsight",
            "family": "hindsight",
            "family_spec": "what later callbacks say about this one: the second own trade of a step must be retail, and the next step's first trade bounds the price path. Measure how far the misread rate falls when the label is revised after 1, 2 and 3 later callbacks, and how much of the price error that revision would remove even though the quote was already set"
          }
        },
        {
          "key": "misread-size",
          "vars": {
            "key": "misread-size",
            "family": "size",
            "family_spec": "trade size and timing: the retail size law (the replica's lognormal), the arb size implied by the displacement (ln(s/sp) against the quoted spread), dt since the last own trade, and the count of own trades in the previous step"
          }
        }
      ],
      "output": {
        "format": "json",
        "maxBytes": 60000
      },
      "merge": {
        "groupSize": 20,
        "brief": "One table: family, AUC filter+mine, misread_test; name the most predictive feature overall."
      }
    }
  ],
  "read": [],
  "cancel": [],
  "wake": {
    "batches": [
      "a1-gates",
      "a1-fixture",
      "a1-ib-engine",
      "a1-compress",
      "a1-port-parts",
      "a1-port-full",
      "b1-numba",
      "b1-ideas",
      "b1-fresh",
      "b1-misread"
    ],
    "afterMinutes": 90
  },
  "finish": false
}
