export const meta = {
  name: 'alt-strategies-deribit',
  description: 'Study >=100 alternative trading strategies on certified Deribit BTC/ETH (+DVOL): honest backtest each, adversarially verify the promising, synthesize',
  phases: [
    { title: 'Find', detail: 'one agent per hypothesis: implement + honest backtest via shared altlib' },
    { title: 'Verify', detail: '3 adversarial skeptics per promising finding' },
    { title: 'Synthesize', detail: 'rank survivors, recommend portfolio roles' },
  ],
}

// ---------------------------------------------------------------------------
// CATALOG — 104 distinct hypotheses. fam=family, kind hint (w=weights, s=signals)
// ---------------------------------------------------------------------------
const CATALOG = [
  // --- BRK: breakout / channel ---
  ['BRK01','BRK','Donchian 10/20/55 long-short','w','Donchian channel breakout: close breaks prior N-bar high -> long, prior N-bar low -> short (use al.donchian which shifts by 1). Grid N in {10,20,55}. Long-short and long-flat variants.'],
  ['BRK02','BRK','Donchian55 + Chandelier ATR trailing','w','Enter long on 55-bar high breakout; trail exit with chandelier stop = highest close - 3*ATR(22). Position 0/1.'],
  ['BRK03','BRK','Keltner channel breakout','w','Long when close > EMA20 + 2*ATR(20); flat when close < EMA20. Try k in {1.5,2,2.5}.'],
  ['BRK04','BRK','Bollinger breakout (vol expansion)','w','Long-flat when close > upper BB(20,2); exit to flat when close < mid BB. Momentum (NOT reversion) interpretation.'],
  ['BRK05','BRK','ATR range breakout','s','Discrete: if close[i] > close[i-1] + k*ATR(14) enter long at close[i] with ATR-based SL and max_bars exit. Grid k in {0.5,1,1.5}.'],
  ['BRK06','BRK','Opening-range breakout (daily)','s','On 1d bars: long when close > prior-day high (gap/expansion); SL prior-day low; max_bars small. Honest, no intrabar extreme entry.'],
  ['BRK07','BRK','N-day-high momentum','w','Long-flat: position 1 while close is within X% of its rolling 100-bar max, else 0. Trend-persistence proxy.'],
  ['BRK08','BRK','NR7 range-contraction breakout','s','Bar with narrowest high-low range in last 7 -> next-bar breakout of that bar in the break direction; entry at close on confirmation.'],
  ['BRK09','BRK','Inside-bar breakout','s','Inside bar (range within prior bar) then breakout of the mother-bar high/low -> enter at close of the breaking bar.'],
  ['BRK10','BRK','Vol-contraction (squeeze) long','w','When Bollinger bandwidth in its low expanding-percentile, go long-flat on subsequent upside close>mid. Honest entry at close.'],
  // --- TRD: trend (non-TSMOM) ---
  ['TRD01','TRD','EMA cross 20/100 long-flat','w','Long when EMA(fast)>EMA(slow), else flat. Grid (fast,slow) in {(10,50),(20,100),(50,200)}.'],
  ['TRD02','TRD','EMA cross long-short','w','Same but short when fast<slow. Compare to long-flat.'],
  ['TRD03','TRD','MACD trend','w','Long when MACD(12,26)>signal(9) AND MACD>0; flat otherwise. Optionally vol-target the direction.'],
  ['TRD04','TRD','Supertrend(10,3)','w','Classic ATR-band trend flip: long when price above supertrend line, short/flat below. Try (period,mult).'],
  ['TRD05','TRD','ADX-filtered EMA','w','Take EMA(20,100) cross only when ADX(14)>25 (trending); flat when ADX low (chop filter).'],
  ['TRD06','TRD','Heikin-Ashi trend streak','w','Build HA candles; long while HA close>HA open (green streak), flat on color flip.'],
  ['TRD07','TRD','Kaufman AMA cross','w','Adaptive MA (efficiency-ratio smoothing); long when price>AMA and AMA rising.'],
  ['TRD08','TRD','Hull MA slope','w','HMA(n); long when HMA rising (slope>0), flat when falling. Grid n in {20,50,100}.'],
  ['TRD09','TRD','Aroon trend','w','Aroon(25): long when AroonUp>AroonDown and AroonUp>70.'],
  ['TRD10','TRD','Vortex indicator','w','VI+ vs VI- (n=14): long when VI+>VI-. Vol-target optionally.'],
  ['TRD11','TRD','SMA50 slope momentum','w','Position = sign of slope of SMA(50) over last k bars; long-flat variant too.'],
  ['TRD12','TRD','Triple-MA alignment','w','Long only when SMA10>SMA50>SMA200 (full bullish alignment); flat otherwise.'],
  ['TRD13','TRD','SMA200 regime + vol-target','w','Long-flat gated by close>SMA200, sized by al.vol_target. Pure regime-trend.'],
  ['TRD14','TRD','Turtle midline trend','w','Long above Donchian(20) midline, exit at Donchian(10) opposite. Trend-rider.'],
  // --- MRV: mean-reversion (regime-gated to avoid the fade death) ---
  ['MRV01','MRV','RSI2 Connors','s','Buy when RSI(2)<10 AND close>SMA200 (uptrend filter); exit when RSI(2)>60 or max_bars. Long-only.'],
  ['MRV02','MRV','BB reversion in calm regime','s','Buy lower BB(20,2) ONLY when realized vol in low expanding-percentile (calm); exit at mid. Gate is the alpha.'],
  ['MRV03','MRV','Z-score reversion trend-gated','s','Fade |zscore(close,20)|>2 toward mean ONLY when long-horizon trend (SMA200 slope) is flat; skip in strong trend.'],
  ['MRV04','MRV','IBS reversion','w','Internal Bar Strength = (close-low)/(high-low). Long when IBS<0.2, flat/short when >0.8. Classic daily edge — test honestly.'],
  ['MRV05','MRV','Williams %R reversion','s','Buy when %R(14) < -90 (oversold) with trend filter; exit on %R>-50.'],
  ['MRV06','MRV','VWAP deviation reversion','w','On 1h: rolling session VWAP; fade deviations > k*sigma back to VWAP. Regime-gate by daily trend.'],
  ['MRV07','MRV','Consecutive-down buy','s','After 3+ consecutive lower closes in an uptrend (close>SMA100), buy at close; exit after k bars or on green close.'],
  ['MRV08','MRV','Daily gap-fill','s','On 1d: if open gaps down vs prior close beyond threshold, long toward prior close (gap fill); SL beyond gap.'],
  ['MRV09','MRV','CCI reversion','s','Buy CCI(20)<-100, exit CCI>0, with trend gate. Honest entry at close.'],
  ['MRV10','MRV','Stochastic reversion in range','s','Stochastic(14,3) oversold buy when ADX low (range regime only). Avoid trending chop.'],
  ['MRV11','MRV','Bollinger %b reversion','w','%b = position within bands; long when %b<0.05, exit at 0.5, with SMA200 trend filter.'],
  // --- VOL: DVOL + realized vol (Deribit-specific) ---
  ['VOL01','VOL','DVOL z-score risk on/off','w','Use al.dvol. Long BTC/ETH (vol-targeted) when DVOL z-score (expanding) < 0 (calm), flat when DVOL high. History 2021+.'],
  ['VOL02','VOL','IV-RV spread directional','w','Compare DVOL to annualized realized vol. When DVOL >> RV (rich/stressed), de-risk to flat; when DVOL<=RV stay long. Test both directions.'],
  ['VOL03','VOL','DVOL-gated TSMOM','w','TP01-style multi-horizon TSMOM (vol-targeted, long-flat) but ONLY active when DVOL below its expanding median; flat when DVOL elevated.'],
  ['VOL04','VOL','DVOL momentum de-risk','w','When DVOL rising over last k days, cut exposure; when falling, add. Overlay on long-flat trend.'],
  ['VOL05','VOL','Vol-of-vol contrarian','w','std of daily DVOL changes spikes (panic) -> contrarian long after stabilization. History 2021+.'],
  ['VOL06','VOL','Realized-vol target standalone','w','No trend signal: long-only position = target_vol/realized_vol, capped. Pure inverse-vol risk control — is risk-scaling alone an edge?'],
  ['VOL07','VOL','DVOL spike contrarian long','s','When DVOL > 90th expanding pct (fear), buy at close, hold ~1 week (max_bars). Capitulation timing.'],
  ['VOL08','VOL','Realized-vol term structure','w','Ratio short-window vol (5d) / long-window vol (30d). >1 (vol rising) de-risk; <1 risk-on. Overlay on long.'],
  ['VOL09','VOL','EWMA vol-forecast sizing','w','RiskMetrics EWMA (lambda 0.94) vol forecast -> target-vol long-only sizing. Compare to simple rolling vol target.'],
  ['VOL10','VOL','DVOL carry/recovery','w','When DVOL high AND falling (post-stress), go long (mean-reversion of fear). Gate long-flat trend by this.'],
  ['VOL11','VOL','DVOL kill-switch on trend','w','Long-flat TSMOM with a hard flat when DVOL>fixed/percentile threshold (crash avoidance overlay).'],
  ['VOL12','VOL','Low-vol anomaly timing','w','Enter long after a cluster of low realized-vol bars (compression often precedes up-moves in BTC). Honest entry at close.'],
  // --- XAS: cross-asset BTC/ETH ---
  ['XAS01','XAS','ETH/BTC ratio z-reversion','w','Build ratio = ETH_close/BTC_close on common timestamps. Trade the ratio (long ratio when z<-2, short when z>2). NOTE: this is a 2-leg spread; implement the ratio series yourself from al.get both assets, evaluate the SPREAD return as a synthetic series via al.eval_weights on a constructed df (close=ratio).'],
  ['XAS02','XAS','ETH/BTC ratio momentum','w','Trend-follow the ETH/BTC ratio (TSMOM on the ratio). Same spread-construction approach.'],
  ['XAS03','XAS','RS rotation BTC/ETH','w','Hold whichever of BTC/ETH has the stronger 90d momentum, vol-targeted; flat if both negative. Report the rotation portfolio return.'],
  ['XAS04','XAS','Lead-lag BTC->ETH','w','ETH position = sign of BTC lagged return / trend (does BTC lead ETH?). Evaluate on ETH series with BTC-derived signal (align timestamps).'],
  ['XAS05','XAS','Lead-lag ETH->BTC','w','BTC position from ETH lagged momentum. Mirror of XAS04.'],
  ['XAS06','XAS','Beta-hedged spread reversion','w','Residual = ETH_ret - beta*BTC_ret (rolling beta). Trade z of cumulative residual back to 0. Market-neutral.'],
  ['XAS07','XAS','Rolling-OLS cointegration spread','w','Rolling/expanding OLS hedge ratio of log(ETH) on log(BTC); trade z-score of residual (Engle-Granger style). Causal hedge ratio only.'],
  ['XAS08','XAS','Correlation-regime spread','w','When rolling BTC/ETH correlation drops below threshold, mean-revert the ratio; when high, stand aside.'],
  ['XAS09','XAS','Dual-momentum BTC/ETH','w','Absolute+relative momentum: hold the stronger asset only if its abs momentum>0 else flat. Vol-targeted.'],
  // --- SEA: seasonality / time-of-day (24/7 crypto) ---
  ['SEA01','SEA','Hour-of-day expectancy','w','On 1h: estimate per-UTC-hour mean return on an EXPANDING in-sample window; go long during hours with positive expanding expectancy. Strictly causal (no full-sample hour ranking).'],
  ['SEA02','SEA','Day-of-week effect','w','On 1d: expanding per-weekday expectancy -> long on positive-expectancy weekdays. Causal only.'],
  ['SEA03','SEA','Weekend effect','w','Long/flat over weekend bars vs weekday, chosen by expanding in-sample sign. Test both.'],
  ['SEA04','SEA','Turn-of-month','s','On 1d: long the last 1-2 and first 2-3 trading days of each month; flat otherwise.'],
  ['SEA05','SEA','Intraday momentum','w','On 1h: sign of the morning (00-12 UTC) cumulative return predicts afternoon (12-24) -> position in afternoon. Causal.'],
  ['SEA06','SEA','Overnight vs intraday','w','Split daily return into US-overnight vs Asia sessions; capture the historically positive session (expanding choice).'],
  ['SEA07','SEA','Monday effect','s','On 1d: position on Mondays based on expanding Monday expectancy (continuation or reversal of Friday).'],
  ['SEA08','SEA','US-session momentum','w','On 1h: long during 13-21 UTC when prior session up; captures US risk-on drift. Causal.'],
  ['SEA09','SEA','Asia-session reversion','w','On 1h: fade extreme moves during 00-08 UTC back toward open. Session mean-reversion.'],
  // --- RSK: risk overlays / defensive (TP01-adjacent but distinct mechanism) ---
  ['RSK01','RSK','Vol-target B&H + DD breaker','w','Long-only vol-targeted (no trend) with a circuit breaker: go flat when strategy equity drawdown>15%, re-enter when recovered. Does the breaker beat passive?'],
  ['RSK02','RSK','Momentum + fast kill-switch','w','TSMOM long-flat with an extra flat trigger on a sharp short-horizon drawdown (e.g. -10% in 5 bars).'],
  ['RSK03','RSK','Inverse-vol risk parity','w','Single-asset proxy: scale exposure by inverse realized vol with a long-run cap; compare to fixed exposure. (For 2-asset RP, blend BTC/ETH inverse-vol and report combined.)'],
  ['RSK04','RSK','Momentum-of-momentum sizing','w','Size the TSMOM position by the STABILITY/agreement of the multi-horizon signals (more agreement = bigger). Long-flat.'],
  ['RSK05','RSK','Chandelier-exit trend','w','Long on trend (EMA cross or breakout); exit via chandelier ATR stop; flat otherwise.'],
  ['RSK06','RSK','Time-stop momentum','s','Enter long on a breakout, HARD exit after exactly N bars regardless (no trailing). Tests whether momentum has a fixed horizon.'],
  ['RSK07','RSK','Drawdown-scaled exposure','w','Exposure proportional to (1 - recent rolling drawdown) on a long-only base; de-risk into weakness.'],
  ['RSK08','RSK','ATR trailing-stop trend','s','Breakout entry with ATR(14)*k trailing stop as SL; max_bars large. Grid k in {2,3,4}.'],
  ['RSK09','RSK','Target-vol + floor/cap + trend gate','w','Long-flat TSMOM, vol-targeted but with exposure floor 0.2 and cap 1.5 only when trend up. Smoothness vs raw vol-target.'],
  // --- OPT: options structures (premiums MODELED on DVOL -> LEAD ONLY, must flag) ---
  ['OPT01','OPT','Covered-call overlay','w','Long spot + sell weekly OTM call. Model call premium with Black-Scholes using al.dvol as IV. Net = spot return capped + premium. MARK caveat: modeled, lead-only.'],
  ['OPT02','OPT','Cash-secured put wheel','w','Sell weekly ~0.25-delta put (BS premium from DVOL); if assigned, hold spot then sell calls. Model assignment via close vs strike. Caveat: modeled.'],
  ['OPT03','OPT','Calendar spread','w','Sell short-dated, buy longer-dated option; P&L driven by DVOL term proxy (use DVOL level changes). Caveat: modeled, no real term surface.'],
  ['OPT04','OPT','Iron condor weekly','w','Sell OTM call+put spreads weekly, premium from DVOL, gated on IV-rank>0.3. Defined risk. Caveat: modeled.'],
  ['OPT05','OPT','Delta-hedged short straddle','w','Sell ATM straddle, daily delta re-hedge; P&L = premium (DVOL) - realized variance. Harvest IV-RV. Caveat: modeled, the realized-stress f is uncertain.'],
  ['OPT06','OPT','Ratio put spread','w','Defensive short-vol with a long tail hedge; model on DVOL. Caveat: modeled.'],
  ['OPT07','OPT','Collar overlay','w','Long spot + protective put - covered call (zero-ish cost). Reduces DD; model legs on DVOL. Does it improve risk-adjusted return?'],
  ['OPT08','OPT','Risk-reversal directional','w','Skew-based directional: model a 25-delta risk reversal sign as a trend tilt (proxy skew from DVOL changes). Caveat: skew not in data, heavy proxy.'],
  // --- MIC: microstructure / candle patterns (honest entry, no extreme fills) ---
  ['MIC01','MIC','Three-bar momentum','s','3 consecutive higher closes -> enter long at the 3rd close, exit after k bars or on a lower close. Continuation test.'],
  ['MIC02','MIC','Engulfing continuation','s','Bullish engulfing in an uptrend -> long at close; bearish in downtrend -> short. Trend-filtered.'],
  ['MIC03','MIC','Volume-spike breakout','s','Breakout of prior high CONFIRMED by volume z-score>2 -> enter at close. Volume as filter.'],
  ['MIC04','MIC','Consecutive-days continuation','w','Position = sign of net of last k closes (streak following) vs streak fading; long-flat. Compare both.'],
  ['MIC05','MIC','Wide-range-bar follow-through','s','After a wide-range bar (range>2*ATR) closing strong, enter in its direction at close; exit k bars.'],
  ['MIC06','MIC','Body-ratio momentum','w','Candle body/(range) large and positive -> conviction up bar -> hold long next bars. Long-flat.'],
  ['MIC07','MIC','Pin-bar rejection reversal','s','Long lower-wick rejection (hammer) at a support (recent low) -> long at close with SL below wick.'],
  ['MIC08','MIC','OBV trend','w','On-balance-volume trend: long when OBV above its EMA (volume confirms price). Long-flat.'],
  // --- STA: statistical / ML (walk-forward, leakage-careful) ---
  ['STA01','STA','Ridge on lagged returns','w','Walk-forward expanding ridge predicting next-bar return sign from lagged returns (lags 1..10, stop at i-1 to avoid the log-return leak). Position = sign of prediction, vol-targeted. 1d only.'],
  ['STA02','STA','Logistic on TA features','w','Walk-forward logistic (refit periodically) on features {rsi,zscore,mom,vol} all causal -> P(up); long if P>0.5. 1d, leakage-careful.'],
  ['STA03','STA','Random forest direction','w','Small RF (few trees, shallow), walk-forward retrain, careful causal features stopping at i-1. Long-flat by predicted prob. 1d.'],
  ['STA04','STA','K-means regime -> trend','w','Cluster causal (vol, return, range) features expanding; enable TSMOM only in the historically-bullish/trending cluster. No future labels.'],
  ['STA05','STA','EWMA-cross ensemble vote','w','Vote across many EMA crossovers (pairs from {5..200}); position = net vote / count. Diversified trend.'],
  ['STA06','STA','Kalman trend (local slope)','w','Kalman local-level+slope filter on log price; long when filtered slope>0. Causal recursion.'],
  ['STA07','STA','Online SGD logistic','w','Online logistic (partial_fit) updated each bar on causal features; predict next sign. 1d.'],
  ['STA08','STA','AR(1) residual reversion','w','Fit expanding AR(1) on returns; trade the residual mean-reversion. Causal coefficients only.'],
  // --- CMB: combinations / multi-filter ---
  ['CMB01','CMB','Trend + RSI pullback','s','Uptrend (close>SMA200) + RSI(14) dips below 35 -> buy the dip at close; exit RSI>55 or k bars. Buy-the-dip-in-uptrend.'],
  ['CMB02','CMB','Breakout + volume + DVOL filter','w','Donchian breakout long-flat, taken only when volume elevated AND DVOL not in panic zone. Triple filter.'],
  ['CMB03','CMB','Multi-TF trend confirm','w','On a faster TF (4h), long only when the 1d trend (SMA50 or TSMOM) agrees. Construct daily trend causally and map onto 4h bars (use last CLOSED daily value).'],
  ['CMB04','CMB','Momentum + low-vol filter','w','TSMOM long-flat taken only when realized vol below its median (avoid high-vol whipsaw). Vol-target the rest.'],
  ['CMB05','CMB','BB squeeze -> breakout','s','Bollinger bandwidth at multi-bar low (squeeze) THEN breakout close>upper -> enter long at that close (honest, not the prior bar). The classic squeeze, done leak-free.'],
  ['CMB06','CMB','Trend + seasonality combo','w','TSMOM long-flat, but scale exposure up in historically strong calendar windows (expanding day-of-week/month expectancy). Causal seasonality only.'],
]

const ROOT = '/opt/docker/PythagorasGoal'
const CHEAT = `SHARED LIB (already built & validated): ${ROOT}/scripts/research/alt/altlib.py
At the top of your script:  import sys; sys.path.insert(0, "${ROOT}/scripts/research/alt"); import altlib as al
DATA (certified Deribit, cached): al.get("BTC"|"ETH", tf) -> df[timestamp,open,high,low,close,volume,datetime]
  tf in {"1h","4h","6h","8h","12h","1d","2d","1w"}. DO NOT use 5m/15m (slow on 2 CPUs / fee death).
DVOL (Deribit implied-vol index, DAILY, history from 2021-03): al.dvol(df,"BTC") -> causal float array len(df) in vol points.
INDICATORS (all causal, value at i uses data<=i): al.ema/sma/rsi/atr/zscore/rolling_std/donchian/bbands/realized_vol/simple_returns/log_returns
  al.bars_per_day(df), al.bars_per_year(df), al.vol_target(direction_in[-1,1], df, target_vol=0.20, vol_win_days=30, leverage_cap=2.0)
EVAL (the no-look-ahead shift is done FOR YOU; never multiply a weight that used close[i] by r[i] yourself):
  CONTINUOUS position -> al.study_weights("NAME", lambda df: target_array(df), tfs=("1d","12h"))
       target[i] decided with data<=close[i]; lib holds it during bar i+1; fee on |turnover|.
  DISCRETE entry/exit -> al.study_signals("NAME", lambda df: entries_list(df), tfs=("1d",))
       entries[i] = {"dir":+1/-1,"tp":px|None,"sl":px|None,"max_bars":int} or None; fills at close[i]. 1d only (Python loop).
  Each returns {name,kind,cells:[{tf,per_asset:{BTC,ETH:{full:{sharpe,maxdd,ret,cagr},holdout:{sharpe,ret},fee_sweep,yearly}},
       min_asset_full_sharpe,min_asset_holdout_sharpe,fee_survives}], verdict:{grade(PASS/WEAK/FAIL),best_tf,...}}
  PRINT BOTH: print(al.fmt(rep)) and print("JSON:", al.as_json(rep))
HONESTY RULES (this project was once destroyed by fake edges): decide direction & price with data<=close[i];
  NEVER enter on a candle extreme (high/low); judge net of 0.10% RT fee; require positive HOLD-OUT (2025+) on BOTH
  assets and survival of the fee sweep; a single lucky cell/asset is NOT an edge. Report the truth, especially negatives.`

function finderPrompt([id, fam, name, kind, idea]) {
  const kh = kind === 's' ? 'al.study_signals (discrete entry/exit, 1d only)' : 'al.study_weights (continuous position)'
  return `You are studying ONE alternative trading-strategy hypothesis on certified Deribit BTC/ETH for the PythagorasGoal research project. Implement it HONESTLY using the shared library, backtest it, and report STRUCTURED results.

HYPOTHESIS ${id} [${fam}] — ${name}
IDEA: ${idea}
Suggested style: ${kh} (override if the idea clearly needs the other style).

CHEATSHEET
${CHEAT}

STEPS
1. Write a script at ${ROOT}/scripts/research/alt/runs/${id}.py that imports altlib as al, implements this idea CAUSALLY, optionally tries a SMALL internal grid (<=4 param sets; keep TOTAL backtests <=6 — only 2 CPUs), picks the best config, and prints al.fmt(rep) + "JSON:"+al.as_json(rep).
2. Run it: cd ${ROOT} && uv run python scripts/research/alt/runs/${id}.py   (timeout generously; if a run hangs, reduce TFs/grid).
3. If it errors, FIX the script (common: NaN handling, alignment for cross-asset/DVOL ideas, shape mismatch). Re-run until it produces numbers OR you conclude it cannot be implemented on this data (say so).
4. Fill the schema HONESTLY from your BEST config. grade = the lib verdict for that config. Set promising=true ONLY if grade is PASS or WEAK AND hold-out Sharpe is positive on BOTH assets AND the fee sweep survives. Negatives are valuable — report them plainly.

CONSTRAINTS: tf from {1d,12h,8h,4h,1h}; signals use 1d. DVOL ideas: history starts 2021 (note it). Options ideas: premiums are MODELED on DVOL -> set caveat "modeled, lead-only". Do NOT fabricate numbers — every number must come from a real run you executed.
Your final message IS the data row (the schema), not prose for a human.`
}

const FIND_SCHEMA = {
  type: 'object',
  required: ['id','name','family','kind','implemented','grade','best_tf','btc_full_sharpe','eth_full_sharpe','btc_holdout_sharpe','eth_holdout_sharpe','worst_maxdd','fee_survives','promising','summary'],
  properties: {
    id: { type: 'string' }, name: { type: 'string' }, family: { type: 'string' },
    kind: { type: 'string', enum: ['weights','signals'] },
    implemented: { type: 'boolean', description: 'did the backtest actually run and produce numbers' },
    grade: { type: 'string', enum: ['PASS','WEAK','FAIL','ERROR'] },
    best_tf: { type: 'string' },
    btc_full_sharpe: { type: 'number' }, eth_full_sharpe: { type: 'number' },
    btc_holdout_sharpe: { type: 'number' }, eth_holdout_sharpe: { type: 'number' },
    worst_maxdd: { type: 'number', description: 'max drawdown fraction (worse of BTC/ETH) in best config' },
    fee_survives: { type: 'boolean', description: 'still positive at 0.20% RT fee' },
    turnover_per_year: { type: 'number' },
    promising: { type: 'boolean' },
    summary: { type: 'string', description: '1-3 sentences: what was tested + the honest conclusion' },
    caveats: { type: 'string' },
    script_path: { type: 'string' },
  },
}

function verifyPrompt(spec, find, k) {
  const [id, fam, name] = spec
  const angles = [
    'LOOK-AHEAD & ENTRY REALISM: re-read the run script. Does any signal use close[i] (or high/low of i) to both decide AND fill on bar i? Any future-peeking in rolling/zscore/regime/walk-forward (e.g. fitting on full sample, ranking hours/days on the whole history, log-return r[k]=log(c[k+1]/c[k]) used as a feature)? Re-run with the leak removed if you suspect one. Verdict real=false if the edge depends on a leak.',
    'ROBUSTNESS & OVERFIT: is this one lucky cell? Re-run neighboring params and a different TF, and check BOTH assets + the fee sweep to 0.20% RT + the per-year table (is it one good year carrying it?). A real edge is a plateau, positive on both assets, and not reliant on a single year. Default real=false if it is fragile.',
    'PLAUSIBILITY & ECONOMICS: does the hold-out (2025+) actually hold up, or is FULL Sharpe propped by pre-2021 regime? Is turnover sane (not thousands/yr)? For options/DVOL ideas, is the result an artifact of the MODELED premium rather than a real edge? Is the mechanism economically distinct from TP01 (TSMOM trend) — or just TP01 in disguise? Default real=false if it is redundant with TP01 or an artifact.',
  ]
  return `You are an ADVERSARIAL SKEPTIC (#${k + 1}) for the PythagorasGoal project. A finder agent claims hypothesis ${id} [${fam}] "${name}" is promising. Your job is to REFUTE it. Assume it is a false positive until proven otherwise — this project was once wrecked by fake edges, so the bar is high.

FINDER'S CLAIM:
${JSON.stringify(find)}

The run script is at: ${find.script_path || ROOT + '/scripts/research/alt/runs/' + id + '.py'}
The shared (trusted, leak-free) eval lib is at ${ROOT}/scripts/research/alt/altlib.py — read it to confirm how eval works.

YOUR ANGLE: ${angles[k % 3]}

Read the script, run your own checks (cd ${ROOT} && uv run python ...), and decide. If you find a leak or fragility, quantify the CORRECTED Sharpe (full & hold-out, both assets). Be specific and quote the numbers you produced. Default to real=false when uncertain.
Return ONLY the schema.`
}

const VERIFY_SCHEMA = {
  type: 'object',
  required: ['id','real','confidence','reason'],
  properties: {
    id: { type: 'string' },
    real: { type: 'boolean', description: 'true only if the edge survives your adversarial check' },
    confidence: { type: 'number', description: '0..1' },
    leak_suspected: { type: 'boolean' },
    single_cell_luck: { type: 'boolean' },
    redundant_with_tp01: { type: 'boolean' },
    corrected_full_sharpe: { type: 'number' },
    corrected_holdout_sharpe: { type: 'number' },
    reason: { type: 'string', description: 'specific, with the numbers you produced' },
  },
}

// ===========================================================================
// RUN
// ===========================================================================
phase('Find')
log(`Studying ${CATALOG.length} alternative-strategy hypotheses on certified Deribit BTC/ETH (+DVOL), one agent each.`)

const results = await pipeline(
  CATALOG,
  (spec) => agent(finderPrompt(spec), {
    label: `find:${spec[0]}`, phase: 'Find', schema: FIND_SCHEMA,
    model: 'sonnet', effort: 'medium',
  }),
  (find, spec) => {
    if (!find) return { id: spec[0], name: spec[2], family: spec[1], grade: 'ERROR', promising: false, verify: [] }
    const worthVerifying = find.promising && (find.grade === 'PASS' || find.grade === 'WEAK')
    if (!worthVerifying) return { ...find, verify: [] }
    return parallel([0, 1, 2].map((k) => () =>
      agent(verifyPrompt(spec, find, k), {
        label: `verify:${spec[0]}.${k}`, phase: 'Verify', schema: VERIFY_SCHEMA, effort: 'high',
      })
    )).then((votes) => ({ ...find, verify: votes.filter(Boolean) }))
  }
)

phase('Synthesize')
const clean = results.filter(Boolean)
// survivors: promising AND a majority of skeptics could not refute it
const enriched = clean.map((r) => {
  const v = r.verify || []
  const realVotes = v.filter((x) => x && x.real).length
  const survived = r.promising && v.length >= 2 && realVotes >= Math.ceil(v.length / 2)
  return { ...r, real_votes: realVotes, n_verify: v.length, survived }
})
const survivors = enriched.filter((r) => r.survived)
const promisingButKilled = enriched.filter((r) => r.promising && !r.survived)

log(`Find done: ${clean.length} studied. Promising: ${enriched.filter(r => r.promising).length}. Survived adversarial verify: ${survivors.length}.`)

// compact table for the synthesizer (drop nothing essential, keep it small)
const compact = enriched.map((r) => ({
  id: r.id, name: r.name, family: r.family, grade: r.grade,
  btc_full: r.btc_full_sharpe, eth_full: r.eth_full_sharpe,
  btc_hold: r.btc_holdout_sharpe, eth_hold: r.eth_holdout_sharpe,
  worst_dd: r.worst_maxdd, fee_ok: r.fee_survives, best_tf: r.best_tf,
  promising: r.promising, survived: r.survived, real_votes: r.real_votes, n_verify: r.n_verify,
  summary: r.summary, caveats: r.caveats,
  verify_reasons: (r.verify || []).map((x) => (x ? `[real=${x.real} conf=${x.confidence}] ${x.reason}` : '')).filter(Boolean),
}))

const SYNTH_SCHEMA = {
  type: 'object',
  required: ['headline', 'survivors', 'ranking', 'recommendations', 'dead_families'],
  properties: {
    headline: { type: 'string', description: '2-4 sentences: did anything new and robust emerge, and how does it compare to the existing TP01/XS01/VRP01 stack?' },
    survivors: {
      type: 'array',
      items: {
        type: 'object',
        required: ['id', 'name', 'why', 'suggested_role'],
        properties: {
          id: { type: 'string' }, name: { type: 'string' },
          why: { type: 'string', description: 'what makes it survive: hold-out, both-asset, fee, distinct mechanism' },
          suggested_role: { type: 'string', description: 'e.g. new portfolio sleeve / diversifier / overlay / needs more work / lead only' },
          correlation_note: { type: 'string', description: 'is it distinct from TP01 trend?' },
        },
      },
    },
    ranking: { type: 'array', items: { type: 'string' }, description: 'all promising findings ranked best->worst, "ID name — one-line verdict"' },
    recommendations: { type: 'string', description: 'concrete next steps: what to deep-validate, what to add to the portfolio, what to drop' },
    dead_families: { type: 'array', items: { type: 'string' }, description: 'families/ideas confirmed dead or redundant, with one-line why (honest negatives)' },
  },
}

const synthPrompt = `You are the SYNTHESIZER for a PythagorasGoal research wave that studied ${CATALOG.length} alternative trading strategies on certified Deribit BTC/ETH (+DVOL), then adversarially verified every promising one with 3 skeptics.

The project's EXISTING validated stack (do not re-derive): TP01 (multi-horizon TSMOM, vol-targeted, long-flat, defensive, FULL Sharpe ~1.30 / hold-out ~0.31, DD ~14%); XS01 (cross-sectional momentum on Hyperliquid alts, diversifier, corr ~-0.12 to TP01); VRP01 (modeled options short-vol, lead-only). The structural ceiling for BTC/ETH-DIRECTIONAL Sharpe is ~1.3 — beating it requires a genuinely DIFFERENT mechanism (that is why XS01/VRP01 were added). Honesty is the prime directive: a finding only counts if it is net-of-fee, robust across params + BOTH assets, positive in the 2025+ hold-out, and economically distinct from TP01.

Here is the full result table (each row = one hypothesis; verify_reasons are the skeptics' findings):
${JSON.stringify(compact)}

Survivors that passed adversarial verification: ${JSON.stringify(survivors.map((s) => ({ id: s.id, name: s.name, btc_full: s.btc_full_sharpe, eth_full: s.eth_full_sharpe, btc_hold: s.btc_holdout_sharpe, eth_hold: s.eth_holdout_sharpe, dd: s.worst_maxdd, real_votes: s.real_votes })))}
Promising-but-killed-by-skeptics: ${JSON.stringify(promisingButKilled.map((s) => ({ id: s.id, name: s.name, why_killed: (s.verify || []).map((v) => v && v.reason).filter(Boolean) })))}

Produce the synthesis. Be skeptical and concrete. If nothing truly new survived, SAY SO plainly (a clean set of honest negatives is the expected, scientifically-valuable outcome for a project at its structural ceiling). For anything that did survive, judge whether it is a real candidate sleeve, a weak lead, or likely still an artifact, and whether it is distinct from TP01.`

const synthesis = await agent(synthPrompt, { schema: SYNTH_SCHEMA, effort: 'high', label: 'synthesize' })

return {
  n_studied: clean.length,
  n_promising: enriched.filter((r) => r.promising).length,
  n_survived: survivors.length,
  survivors: survivors.map((s) => ({ id: s.id, name: s.name, family: s.family, btc_full: s.btc_full_sharpe, eth_full: s.eth_full_sharpe, btc_hold: s.btc_holdout_sharpe, eth_hold: s.eth_holdout_sharpe, worst_dd: s.worst_maxdd, real_votes: s.real_votes, n_verify: s.n_verify, summary: s.summary })),
  promising_killed: promisingButKilled.map((s) => ({ id: s.id, name: s.name })),
  all_grades: clean.map((r) => ({ id: r.id, name: r.name, grade: r.grade, btc_full: r.btc_full_sharpe, eth_full: r.eth_full_sharpe, btc_hold: r.btc_holdout_sharpe, eth_hold: r.eth_holdout_sharpe, promising: r.promising })),
  synthesis,
}
