{
 "notes": "EPISODE 1 (2026-11-10). State: nothing had run; requester notes empty, so the 'requester's note 1' file links and sha256s the objective refers to do not exist yet. Substitute used in every brief: workers clone github.com/gtmcknight/amm-sim and check out commit 4812a6382311624e1506aca131ca35c378f930d9, verified with git rev-parse (a commit hash pins every byte). If the requester adds note 1 with sha256s, add them to later briefs.\n\nWHAT I VERIFIED THIS EPISODE (GitHub API, raw files): the commit exists; research/solved holds README.md, gates.py (792 B), real.py (Gaussian filter, class RealPolicy(DPPolicy), opts soft/hard/tail/sdscale/fill/qm/oraclearb; constants RHO=0.26, LD_S=1.176, LD_M=ln(11.12e-4), H0=0.55 prior, SIG2=0.000945^2; decision is_arb = pi>0.5 with pi = l_arb/(l_arb+l_ret), l_arb a Gaussian at the post-trade band edge, l_ret a lognormal fill density), replica2.py (run(seed, policy, n_steps=10000, cfg=None, qcap=None) -> dict with edge, retail, arb, rv, av, fees, fb, fa in bps; policy.init(sigma, lam, size_mean) -> (fb, fa); policy.on_trade(is_buy, ax, ay, t, x, y, arb, p, s2[, jr]) -> (fb, fa); arb is the TRUE flag; NF=0.003 rival fee; per-seed cfg from Random(10_000_000+seed): size_mean U(19,21), lam U(0.6,1.0), sigma U(0.000882,0.001008); arb first each step then retail routed by optimal split), evalpol.py (load, DPPolicy, 'hold' mode = seeds 10000..12047 paired vs calib_out_2048.json kinds oracle/HA/T; Pool(14)), latt.py (LatticePolicy, 121 cells), gates.py (prints edge for seeds 10000..10002 of perfect-info pol_mid.json, gaussian+plain soft=1.0, gaussian+qm with qm_k1e-5.json, lattice+qm), dp.py/qmdp.py (solvers, numpy/numba), pol_mid.json (780 KB), qm_k1e-5.json (7.9 MB), calib_out_2048.json (2.9 MB). README (2026-11-09): HA mean 529.557 on holdout; perfect info +8.70; gaussian+plain +2.83; lattice+qm +4.26; a perfect arb/retail classifier adds about 2.7; filter wrong about 16%. qt1 = the gaussian+qm policy gates.py builds. benedictbrady/amm-challenge commit 6b6921f8 exists (2026-02-07). The wheel URL in the SETUP script answers 200 (727,586 bytes).\n\nGATE P: I do not know gates.py's expected numbers. Every item in both batches runs gates.py first and returns its output verbatim: episode 2 must compare the outputs across items; if they agree, pin them in notes as the gate P reference and have later briefs check against them.\n\nBATCHES SPAWNED (15 items of 200): 'gate-p' (5 items, diagnostics and knob grids): d1 oracle ceiling (expects qt1 about +3.8 vs HA, oracle about +6.98), d2 decomposition of the oracle gain into belief vs quote, d3 soft/hard grid, d4 tail/sdscale/fill grid, d5 error anatomy of the filter. 'classify' (10 items, canary 5 then 5 held): c1 logistic classifier on filter-state features, c2 lag-1 belief smoothing, c3 particle filter, c4 retail likelihood from the routing model, c5 hedged quote in the ambiguous window, c6 cost-aware thresholds, c7 prior from the belief instead of H0, c8 lattice reproduce + 2.5 bps lattice, c9 joint constant search, c10 two-hypothesis belief. Every item: python3 -I, stdlib-only policy, paired vs qt1 run on the same machine, choose on 64000..65023, report on 10000..12047, result JSON with gates_output, observables_note, qt1_report_vs_ha, choose, report (incl. misclass), chosen_params, analysis, code, notes.\n\nEPISODE 2 SHOULD: (1) compare gates_output across items and pin it; check qt1_report_vs_ha is about +3.8 and d1's oracle about +6.98, else distrust the setup. (2) Rank variants by report mean_diff vs qt1 with se; discard any whose choose gain does not survive on report (overfit). (3) Read d2: if most of the oracle gain comes through the belief, favour c2/c3/c10-style belief repair; if through the quote, favour c5/c6. (4) Spawn a 'combine' batch that stacks the 2-4 winners (each item one combination, same brief core, code from the winning results pasted into the brief or re-derived from their descriptions), plus re-runs of good ideas with wider searches. (5) CONTRACT work only once a policy is >= +0.5 over qt1 on report seeds: then a 'contract' batch on Linux per the objective's SETUP script (clone benedictbrady/amm-challenge at 6b6921f8, scratch/setup.sh, env.sh), gate F (fees within 0.01 bps of the Python reference on 50x2,000 replica callbacks), size <= 24,576 bytes, afterSwap peak <= 245,000 gas, 0 OOG, run_batch vs VanillaStrategy with fresh engine seeds from 9900000 (allocate disjoint seed ranges per item in notes: next free = 9900000), paired vs qt1 {n, mean_diff, se, worst5}. Note the qm table is 7.9 MB, so a contract needs a compressed table: ask an item to measure how coarse the table can be before the replica score drops (a replica-only item, cheap). (6) Budget: 185 items left after this episode; spend about 25-40 per episode, keep 40 for contracts and final verification.\n\nCANARY: if the plane holds 'classify' items 6-10 and three of the first five fail, mend the brief (likely causes: result over 60000 bytes because of 'code', an hour too short for 3072 seeds, or gates.py missing a dependency) and re-spawn with itemsFrom.",
 "spawn": [
  {
   "name": "gate-p",
   "brief": "Taylor (gtmcknight)'s pure-Python AMM replica; credit Taylor in every file header. SETUP: in a new empty directory, git clone https://github.com/gtmcknight/amm-sim and git checkout 4812a6382311624e1506aca131ca35c378f930d9; `git rev-parse HEAD` must print that hash, else return {\"blocked\":{\"step\",\"error\"}}. Work in research/solved/ with `python3 -I`; policy code is standard library only. Keep all your files under research/solved/scratch/. No command over 8 minutes: run seeds with multiprocessing.Pool in chunks of at most 256.\nGATE P, before any change: run `python3 -I gates.py` and copy its whole output verbatim into gates_output (on error return blocked with the traceback).\nBASE qt1 = exactly the 'gaussian+qm' policy gates.py builds (real.RealPolicy, soft=1.0, qm=qm_k1e-5.json). HA = kind 'HA' rows of calib_out_2048.json.\nOBSERVABLES: a policy may read only what qt1's non-oracle path reads (init args; on_trade's is_buy, ax, ay, t, x, y; its own history), never arb, p, s2 or jr (the true flag only for diagnostics or where the variant says \"oracle\"). observables_note: does qt1's non-oracle path read any of those (quote the lines)?\nGATE Q: score every policy PAIRED, seed by seed, against qt1 run on the same seeds on this machine (replica2.run(seed, policy)). Choose seeds 64000..65023: every parameter or design choice is made only there (coarse on 64000..64255, confirm the best few on all 1024). Report seeds 10000..12047: report only, never choose there (if time runs short use 10000..11023 and say so). mean_diff = mean(edge_variant - edge_qt1); se = sample sd/sqrt(n); worst5 = the 5 lowest-diff seeds as [seed, diff]. Also give qt1 paired against HA on the report seeds. misclass = share of steps whose first own trade was judged wrongly.\nVARIANT {{key}}: {{variant}}\nRESULT: one JSON object under 60000 bytes: {\"variant\":\"{{key}}\",\"gates_output\":str,\"observables_note\":str,\"qt1_report_vs_ha\":{n,mean_diff,se},\"choose\":{n,mean_diff,se,worst5},\"report\":{n,mean_diff,se,worst5,misclass_variant,misclass_qt1},\"chosen_params\":{},\"seconds_per_seed\":num,\"analysis\":{} (what the variant asks for),\"code\":{filename: full source of each file you wrote, 25000 chars at most},\"notes\": under 1500 chars, what worked, what did not, what next}. 4 decimals. A diagnostic with no new policy puts null in choose and report.",
   "items": [
    {"key": "d1-oracle-ceiling", "vars": {"variant": "Oracle ceiling. Variant = qt1 with the extra option oraclearb=1.0 (the true arb flag replaces the filter's decision; everything else unchanged). Score it paired against qt1 on both seed sets (nothing to choose) and also paired against HA on the report seeds (analysis.oracle_report_vs_ha). The objective expects about +6.98 over HA for this and about +3.8 for qt1; say in notes whether that holds. analysis.corr: per seed set, the correlation between qt1's per-seed misclass count and (edge_oracle - edge_qt1)."}},
    {"key": "d2-oracle-split", "vars": {"variant": "Oracle decomposition (diagnostic, uses the true flag). Subclass real.RealPolicy in scratch/, copying on_trade, with two switches: A = the true arb flag drives only the belief update (m, v, s2h) while the filter's own decision still drives the quote lookup; B = the true flag drives only the quote lookup while the belief follows the filter's decision. Score qt1+A, qt1+B and the full oracle (oraclearb=1.0) paired against qt1 on both seed sets (analysis.A, analysis.B, analysis.full, each {choose, report} of {n, mean_diff, se, worst5}). notes: what share of the oracle gain comes through the belief and what share through the immediate quote."}},
    {"key": "d3-soft-hard-grid", "vars": {"variant": "Tune the blend knobs. Grid soft in {0, 0.25, 0.5, 0.75, 1.0} x hard in {0.9, 0.95, 0.98, 0.995, 0.999} for qt1 (all else unchanged). Coarse pass on 64000..64255, the best 4 on all 1024 choose seeds, the single best on the report seeds. analysis.grid = list of {soft, hard, n, mean_diff, se} for every point run. If no point beats (soft=1.0, hard=0.995) by more than 2 se on the choose seeds, report that best point anyway and say in notes that the knobs are flat."}},
    {"key": "d4-tail-sdscale-fill", "vars": {"variant": "Tune the filter knobs. Grid tail in {0, 0.05, 0.1, 0.2} x sdscale in {0.8, 0.9, 1.0, 1.15, 1.3} x fill in {0, 1} for qt1 (all else unchanged); read in real.py what each does and say so in notes. Coarse pass on 64000..64255, the best 4 on all 1024 choose seeds, the single best on the report seeds. analysis.grid = list of {tail, sdscale, fill, n, mean_diff, se} for every point run."}},
    {"key": "d5-error-anatomy", "vars": {"variant": "Error anatomy (diagnostic, no new policy). Subclass qt1 in scratch/ to log, at every step's first own trade on choose seeds 64000..65023: pi, the decision, the true arb flag, is_buy, d (the displacement real.py computes), belief sd, steps since the last judged arb, trades in the previous step. analysis: confusion matrix; error rate by pi decile, d decile, belief-sd decile and is_buy; share of errors that are false-arb vs false-retail and the mean pi of each; per-seed correlation of the error count with edge_qt1. notes: the two cheapest-looking fixes the tables suggest."}}
   ],
   "output": {"format": "json", "maxBytes": 64000},
   "merge": {"groupSize": 25, "brief": "Merge into one JSON: for each item its variant, gates_output, qt1_report_vs_ha, choose, report, chosen_params and notes verbatim; say whether every gates_output is byte-identical and whether every qt1_report_vs_ha agrees."}
  },
  {
   "name": "classify",
   "brief": "Taylor (gtmcknight)'s pure-Python AMM replica; credit Taylor in every file header. SETUP: in a new empty directory, git clone https://github.com/gtmcknight/amm-sim and git checkout 4812a6382311624e1506aca131ca35c378f930d9; `git rev-parse HEAD` must print that hash, else return {\"blocked\":{\"step\",\"error\"}}. Work in research/solved/ with `python3 -I`; policy code is standard library only. Keep all your files under research/solved/scratch/. No command over 8 minutes: run seeds with multiprocessing.Pool in chunks of at most 256.\nGATE P, before any change: run `python3 -I gates.py` and copy its whole output verbatim into gates_output (on error return blocked with the traceback).\nBASE qt1 = exactly the 'gaussian+qm' policy gates.py builds (real.RealPolicy, soft=1.0, qm=qm_k1e-5.json). HA = kind 'HA' rows of calib_out_2048.json.\nOBSERVABLES: a policy may read only what qt1's non-oracle path reads (init args; on_trade's is_buy, ax, ay, t, x, y; its own history), never arb, p, s2 or jr (the true flag only for diagnostics or where the variant says \"oracle\"). observables_note: does qt1's non-oracle path read any of those (quote the lines)?\nGATE Q: score every policy PAIRED, seed by seed, against qt1 run on the same seeds on this machine (replica2.run(seed, policy)). Choose seeds 64000..65023: every parameter or design choice is made only there (coarse on 64000..64255, confirm the best few on all 1024). Report seeds 10000..12047: report only, never choose there (if time runs short use 10000..11023 and say so). mean_diff = mean(edge_variant - edge_qt1); se = sample sd/sqrt(n); worst5 = the 5 lowest-diff seeds as [seed, diff]. Also give qt1 paired against HA on the report seeds. misclass = share of steps whose first own trade was judged wrongly.\nVARIANT {{key}}: {{variant}}\nRESULT: one JSON object under 60000 bytes: {\"variant\":\"{{key}}\",\"gates_output\":str,\"observables_note\":str,\"qt1_report_vs_ha\":{n,mean_diff,se},\"choose\":{n,mean_diff,se,worst5},\"report\":{n,mean_diff,se,worst5,misclass_variant,misclass_qt1},\"chosen_params\":{},\"seconds_per_seed\":num,\"analysis\":{} (what the variant asks for),\"code\":{filename: full source of each file you wrote, 25000 chars at most},\"notes\": under 1500 chars, what worked, what did not, what next}. 4 decimals. A diagnostic with no new policy puts null in choose and report.",
   "items": [
    {"key": "c1-logreg", "vars": {"variant": "Learned classifier. On choose seeds 64000..65023 run qt1 with a logging subclass that records, at every step's first own trade, features from observables and the filter's own state (log l_arb, log l_ret, pi, log d, belief sd, is_buy, trades in the previous step, whether its first trade was judged arb, steps since the last judged arb) and the true arb flag as label. Fit a logistic regression (numpy allowed offline), embed the weights in a subclass whose is_arb = sigmoid(w.x) > 0.5, all else as qt1. Choose features and threshold on choose seeds. analysis: accuracy of the filter vs the classifier on choose and report rows, and the weights."}},
    {"key": "c2-lag1-smooth", "vars": {"variant": "Lag-1 smoothing of the belief. When the next step's first trade arrives, re-test the previous step's call against it (a trade judged arb pins the price at its band edge, so the previous step's price must lie within about one sigma of it; a retail-sized trade says the price stayed inside the band): if the previous call now looks wrong, recompute the belief (m, v, s2h) as the other hypothesis would have left it, then process the new trade. Quotes already given are not touched. Choose the re-test threshold on choose seeds. notes: how much of the oracle's gain this recovers (the true flag is worth about +3.2 over qt1)."}},
    {"key": "c3-particle", "vars": {"variant": "Particle filter. Replace the Gaussian belief with N particles over the true price (and the rival spot if the filter tracks it) using replica2's own dynamics: drift sigma from init, arb first each step at the current fees' band edges, lognormal retail sizes (size_mean, log-sigma 1.2) and the routing split; weight particles by the likelihood of each observed own trade; P(arb) = weighted share of particles under which the trade is an arb; decide P(arb) > 0.5; look up the tables at the weighted mean and sd. Choose N in {64, 128, 256} and the resampling rule on choose seeds (measure seconds_per_seed early; cut N if too slow). Keep qt1's tables and lookup."}},
    {"key": "c4-routing-lik", "vars": {"variant": "Retail likelihood from the routing model. real.py's l_ret uses a fixed lognormal (LD_M, LD_S, RHO) for the own-pool displacement d of a retail trade. Derive it from replica2 instead: a retail order q ~ lognormal(mu_ln(size_mean), 1.2) is split between the two pools by the optimal split given the own price and the rival spot estimate s2h; integrate numerically (a fixed grid precomputed at init) to get the density of d given the belief, buys and sells separately. Replace l_ret with it; all else as qt1. Choose the grid size and any shrinkage toward the old lognormal on choose seeds."}},
    {"key": "c5-hedged-quote", "vars": {"variant": "Hedged quoting under uncertainty. Leave the belief update as qt1 but change the quote at a step's first trade when pi is in (lo, hi): quote the pi^g-weighted average of the two hypotheses' table fees (g a mixing exponent). Choose lo in {0.2, 0.3, 0.4}, hi in {0.6, 0.7, 0.8}, g in {0.5, 1, 2} on choose seeds. analysis: mean_diff by pi bin (10 bins) for the chosen rule versus qt1 on the choose seeds, and the share of first trades that fall in the window."}},
    {"key": "c6-cost-threshold", "vars": {"variant": "Cost-aware thresholds. Keep qt1's pi but decide is_arb = pi > theta(is_buy, sd_bin), sd_bin the belief sd in 3 bins (tertiles measured on 64000..64255). Choose each theta in {0.3, 0.4, 0.5, 0.6, 0.7} to maximise the paired edge on choose seeds, one bin at a time by coordinate search, two passes. analysis: the theta table, the edge of a single global theta chosen in the same set, and the misclass rate of each (a theta that raises errors but raises edge is fine: say which errors are cheap)."}},
    {"key": "c7-prior-from-belief", "vars": {"variant": "Prior from the belief. real.py uses a fixed prior H0 = 0.55 that a step's first trade is an arb. Replace it with the belief's own probability that the true price lies outside the current no-arb band (mass of N(m, v) advanced one step of drift that lies above the ask edge or below the bid edge under the current fees), blended as w*belief_prior + (1-w)*H0 with w in {0.25, 0.5, 0.75, 1.0} chosen on choose seeds. Keep everything else as qt1. analysis: calibration table of predicted prior vs realised arb rate in 10 bins on the choose seeds."}},
    {"key": "c8-lattice-fine", "vars": {"variant": "Lattice filter. Reproduce latt.LatticePolicy with qm_k1e-5.json (Taylor's README reports about +4.26 over HA) and score it paired against qt1 on both seed sets, with seconds_per_seed. Then, if latt.py has a cell width or cell count, build a 2.5 bps (twice as fine) lattice, choose any smoothing or width knob on choose seeds and score it the same way (analysis.fine). Note in notes what the lattice costs in time and memory per trade (its contract cost is why qt1 is the Gaussian filter)."}},
    {"key": "c9-cma-constants", "vars": {"variant": "Joint constant search. The filter's constants RHO, LD_M, LD_S, H0, SIG2 and options sdscale, soft, hard, tail form a 9-vector; make them parameters of a subclass in scratch/. Search with cma if installed, else a Nelder-Mead you write, objective = paired mean_diff against qt1 on seeds 64000..64255 (at most 40 evaluations, each under 8 minutes), then confirm the best 3 on all 1024 choose seeds and report the single best on the report seeds. analysis: start vector, best vector, the 3 confirmations. notes: how much of the choose-seed gain survives on the report seeds."}},
    {"key": "c10-imm-2hyp", "vars": {"variant": "Two-hypothesis belief. On an ambiguous first trade (pi inside a window), keep both branches alive: branch A updates the belief as arb, branch B as retail, each with its own (m, v, s2h) and weight pi, 1-pi. Quote from the heavier branch. Update both on every later trade, multiplying weights by each branch's likelihood of it; collapse to the heavier branch when one weight is under 0.05 or after K steps. Choose the window from (0.15, 0.85) and (0.3, 0.7) and K in {1, 2, 4} on choose seeds. Keep qt1's tables and lookup."}}
   ],
   "output": {"format": "json", "maxBytes": 64000},
   "merge": {"groupSize": 25, "brief": "Merge into one JSON: a table of variant, choose {n, mean_diff, se}, report {n, mean_diff, se, misclass_variant, misclass_qt1}, seconds_per_seed, ranked by report mean_diff; then each item's chosen_params, analysis and notes verbatim; say whether every gates_output is byte-identical."}
  }
 ],
 "read": [
  "gate-p/d1-oracle-ceiling", "gate-p/d2-oracle-split", "gate-p/d3-soft-hard-grid", "gate-p/d4-tail-sdscale-fill", "gate-p/d5-error-anatomy",
  "classify/c1-logreg", "classify/c2-lag1-smooth", "classify/c3-particle", "classify/c4-routing-lik", "classify/c5-hedged-quote",
  "classify/c6-cost-threshold", "classify/c7-prior-from-belief", "classify/c8-lattice-fine", "classify/c9-cma-constants", "classify/c10-imm-2hyp"
 ],
 "cancel": [],
 "wake": {"batches": ["gate-p", "classify"], "afterMinutes": 300},
 "finish": false
}
