## Open
- [ ] `promotable()` returns False when no winner exists — first-ever alt run can never auto-promote; add `--force-promote` flag to `run_mlp_deep_sweep.py`
- [ ] No multi-asset summary table; `mlp_results_table.py` is one asset at a time — a 4×5 grid view would make cross-asset comparison immediate
- [ ] No test for `mlp_results_table.py` with non-BTC assets (default-arg bug was latent from day one)

## Closed
- [x] `init_worker` in `run_mlp_deep_sweep.py` didn't propagate `ASSET` to subprocess workers — all alt sweeps evaluated against BTC data silently
- [x] `def winner_path(tf, asset=ASSET)` captured `"COINBASE_BTCUSD"` at definition time — `--asset ETH` read the right data file but always wrote to/read from the BTC winner CSV

## Notes
- IS PnL is not comparable across assets: SOL $0.50→$200+, LINK $0.20→$30 during IS window — a single bull-run catch shows +100k%. Use Calmar ratio as the cross-asset quality metric, not PnL absolute.
- bb_pct_b_norm (Bollinger Band %B) benefits BTC 6H/8H dramatically (IS +37%/+56% Calmar) but is BTC-specific at those timeframes. ETH/SOL/LINK show their bb50 gains at longer TFs (12H/1D).
- Pine's single-arch constraint: once any TF is upgraded to a new feature count, all TFs must follow before Pine can be regenerated. Partial upgrades cause silent zero-weight presets. Check `mlp_status.py` before any feature addition.
- When adding globals used inside `ProcessPoolExecutor` workers, always include them in `initargs` — workers inherit module-level defaults, not runtime-updated globals.
- Python default arg capture: `def f(x=GLOBAL)` evaluates `GLOBAL` at definition time. Use `None` as default and resolve inside the function body when the value may change at runtime.
