MIS Engine — Freeze-Lift Fix List (canonical spec for the fix session)
✅ EXECUTED 2026-07-01 (4:54–6:25 PM ET, MIS engine-fix session). All 7 fixes applied in order (deploys @126–@132), #15A/#15B run after every change and every scale step, universe scaled 27→50→183→237, histogram gate PASSED, health GREEN 0/0 at 237. Recon: 898 GREEN (fix 4); 600/5692/5378 pending the nightly control refresh. Evidence:
MIS/v2/baselines/(7 commits on zee78900/MIS) +outputs/2026-07-01_18-22_audit_mis-freeze-fix-window-executed.html. ✅ FREEZE LIFTED — Sam signed off 2026-07-01 (independently verified: health GREEN 237/237, @132). Condition: Fidelity recon gaps >5% persisting after a fresh import = real data bug, fix before trusting portfolio dollars. Track-B stays queued. Deviations (all documented in the audit): SOXX legacy STARTER→REJECTED (real-RR correction, intended by fix 6) · SPCX proposed HOLD→EXIT (sector peer-set enrichment at scale, module-blessed explainable class, flagged to Sam) · Trend jitter ±2–4 on a few names via FlowStrength's sector z-term (queued as a Track-B calibration item with captured evidence).Built 2026-07-01 from a 4-agent soundness audit (phantom/formula · v11-8-flaws · live-output · off-sheets). This is the exact, evidence-based work required to make the MIS scoring engine safe to scale 27 → 237 and lift the freeze. Execute this in full, in order, with the guardrails. Don't re-derive — the audits already found everything.
THE VERDICT (why this exists)
The engine's brain is SOUND — real prices (no phantoms in the verdict path), the v11 disease is cured (all 4 critical flaws + the inversion genuinely fixed), the proposed verdicts are correct & non-inverted (leaders→STARTER/ADD, losers→EXIT, coherent one-liners), and the logic is portable (zero GOOGLEFINANCE in the scoring/decision functions — the 6-min/750-col/200-tab walls are all DATA-layer, not logic). But it is NOT scale-ready — 4 audits converged on score-range compression + a few defects. Fix them, prove it with data, run the immutability tests, THEN scale. It's corrections, not a rebuild.
READ FIRST (don't skip — this is the anti-rediscovery rule)
docs/MIS_OPERATOR.md(beacon + the freeze + locked rules) ·docs/MIS_LINEAGE.md(the engine's family tree + SACS math) ·MIS/docs/MIS_SACS_CALIBRATION_MODULE.md(🚨 #15A/#15B immutability tests + "scores ABSOLUTE / grades RELATIVE" governance) ·docs/MIS_UNIVERSE.md(the 237 list) ·MIS/v2/Code.gs(the live engine —MOM_Fformulas line 17,SNAP_Fline 18, FSE gatemisV2BuildFSE~316-348, decomposed scoringmisV2ComputeShadow_/misV2SynthV2_~2459-2628,PORTFOLIO_CONTROL_,misV2PortfolioRecon_,misV2GrossByAccount_).
THE FIX-LIST (exact — location · problem · fix)
- 🔴 RS_Sector horizon mismatch — Momentum_Engine col AT: currently
=K2 − AN2= 30D% ticker − 3D% sector mean (AN2 averages col I = 3D%). Feeds VSM→SACS→the verdict gate. Fix: make the sector mean 30D-based (average col K = 30D% within sector) so it's 30D-vs-30D, matching the (correct) RS_SPY at col AS. Add a realSectorMean30D/SectorStdev30Dif needed. - 🔴 Score-range compression (THE scale blocker — confirmed live) — Setup is hard-capped at ~49, SACS tops at ~70, only ONE of 27 names clears the "≥60" gate, ~14/27 sit below 25 — everything crams into 0–55. At 237 the shortlist gate becomes meaningless. Fix: apply the scale-expansion so scores use the full 0–100 range with real separation (re-weight/expand SACS =
Flow*0.5 + RS_SPY*25 + RS_Sector*25and the Setup/Composite so leaders reach 70–90); add aMIN(100)clamp on SACS (it can currently exceed 100 — the "0-100" contract is broken). Target: post-fix, the 40–60 band < ~35% and stdev materially above the old 12.1, with multiple names able to clear 60. - 🔴 Absolute-score conversion (#15A — required to scale) — scores are currently percentile/set-relative (
rankerFor), so embedding 20 names inside 237 shifts them by construction. Convert to ABSOLUTE (fixed input → 0-100 bands) per the calibration module ("scores ABSOLUTE, grades RELATIVE"). This is THE scale-invariance fix — without it, scaling changes existing scores. - 🟠 898 / reconciliation gap —
misV2PortfolioRecon_+misV2GrossByAccount_read HOLDINGS_CLEAN, which no longer contains 898 (the Fidelity-only import dropped it) → recon shows Schwab 898 = $0, RED. The live-Schwab override already exists inmisV2BookData_— extend it into the recon/gross path so 898 uses the live Schwab accounts API there too. Then reconcile the 3 conflicting equity numbers into ONE canonical (portrecon $24,792 vs snapshot $33,659 vs old $22,135). The engine correctly blocks portfolio dollars until recon is GREEN — get it GREEN. - 🟡 ATR-floor guard — a stop < 0.5% of price → Shares = 0 (stops bond/treasury ETFs computing oversized share counts).
- 🟡 Per-name RR — RR is hardcoded 2.5 for every name (zero discriminating info). Compute real per-name reward:risk (Target%/Risk% off the ATR bracket). Confirm the FSE risk gate uses the real per-name RR, not the constant.
- 🟡 Wire
recoscore— the forward-return self-grading loop reads "scored 0 this run"; wire it so there's an accumulating track record that the calls actually make money (1d/3d/5d forward returns on the engine's own ADD/STARTER calls).
METHOD (mandatory — this is how you don't break the 27)
- Shadow/copy first. Never edit the live scoring cold. Compute changes on a shadow (the
misV2ComputeShadow_/FACTOR_DIAGNOSTICStooling exists) or a Drive COPY of the sheet; verify; only then promote. - After EACH fix, run #15A (score immutability) + #15B (action immutability) on the frozen 27-name baseline — the 27 verdicts must NOT move, except where a fix intentionally corrects one (document it explicitly). Baseline scores were the frozen
27/27set (recompute the baseline first if the JSON is absent). - Prove the fix with DATA, not "by design": run
FACTOR_DIAGNOSTICS/shadowscoreson the live ~183 → produce the distribution histogram showing the 40-60 band < 35%, stdev up, multiple names clearing 60. This is the evidence gate for fix #2. - Deploy (each change): from
MIS/v2/,clasp push -fthenclasp deploy -i <LIVE_DEPLOYMENT_ID>(find viaclasp deployments— the@NNNversioned one, currently the one titled with the latest change). Then curl-verify:?fn=verify&token=<RUN_TOKEN>(proposed-state table),?fn=fse&token=…,?fn=shadowscores&token=…(RUN_TOKEN + MIS_SETUP_KEY are constants at the top of Code.gs). Exec base:https://script.google.com/macros/s/<deploy>/exec. - Then scale the universe in stages: 27 → 50 → 183 → 237, running #15A/#15B at EACH step. STOP RULES: any baseline drift, formula error, stale/phantom value, distribution collapse, or #15 failure → halt + report.
GUARDRAILS (🔴 do not violate)
- This is the MIS v2 engine only (clasp scriptId
1KDEBYMFZKeImsxKxpY_5Vvc9-ypPBixTUnCPES6dDKI1rRy7vPDOHrfd, live deploy id startsAKfycbwUeHzfU7GP…). Deploy via clasp, never a master push. - Do NOT touch ops-api (Brain/#042's lane) or any other session's code. Do NOT push to master in a way that could clobber ops-api — MIS code lives in the nested
MIS/git repo + ships via clasp. - Advisory only, never auto-trade. Never break the 27. Broker-cost-basis truth only. Wash-sale gate stays.
- Commit each change to the nested
MIS/repo with a clear message.
DONE (= the freeze is liftable)
Fix-list applied · #15A/#15B pass at every scale step · FACTOR_DIAGNOSTICS histogram proves the spread · recon GREEN ×5 · scaled to 237 with no drift · then Sam signs off and the freeze is formally lifted. Update MIS_OPERATOR.md (freeze → LIFTED, with the evidence) + CONTEXT.md.