mekosotto commited on
Commit
0673175
·
1 Parent(s): 8cd7173

project overvew udpate

Browse files
PROJECT_OVERVIEW.md CHANGED
@@ -554,7 +554,111 @@ hackathon/
554
 
555
  ---
556
 
557
- ## 16. Kapanış
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
 
559
  NeuroBridge Enterprise hackathon'un sloganına ("**Stop Building Ideas. Start Building Systems.**") en doğrudan cevap. 8 gün boyunca disiplinli TDD + Subagent-Driven Development ile inşa ettik. Public deploy'lu, jüri tarayıcıdan tıklayıp dokunabiliyor. 184 test green, 96.8% jüri skoru projeksiyonu, 5/5 hackathon track strong, 4/4 Living Systems pillar full.
560
 
 
554
 
555
  ---
556
 
557
+ ## 16. Terimler Sözlüğü
558
+
559
+ Yukarıdaki bölümlerde geçen teknik terimlerin sade Türkçe karşılıkları. Hiç tanımadığın bir alan için kısa "neden önemli?" notlarıyla.
560
+
561
+ ### 16.1 Klinik / Biyomedikal
562
+
563
+ - **BBB (Blood-Brain Barrier / Kan-Beyin Bariyeri):** Beyne giden damarlardaki özel hücre tabakası. Vücudun beyni zararlı maddelerden koruyan filtre. Bir ilacın etki etmesi için (eğer beyinde çalışacaksa) buradan geçmesi gerekir; geçmemesi gerekiyorsa (yan etki istemiyoruz) buraya takılması gerekir. İlaç keşfinde kritik bir filtre.
564
+ - **MSS (Merkezi Sinir Sistemi):** Beyin + omurilik. CNS olarak da geçer.
565
+ - **MRI (Magnetic Resonance Imaging):** Manyetik rezonans görüntüleme; beynin/dokunun kesit görüntüsü.
566
+ - **NIfTI (`.nii.gz`):** Beyin görüntüleme veri formatı; MRI taramaları bu formatta saklanır.
567
+ - **ROI (Region of Interest / İlgi Bölgesi):** Görüntüde ölçüm aldığımız bölge (örn. hipokampus). Bizde N×N×N grid hücreleri.
568
+ - **EEG (Electroencephalography):** Kafa derisindeki elektrotlarla beynin elektriksel aktivitesini ölçen yöntem.
569
+ - **EOG (Electrooculography):** Göz hareketlerinin elektriksel kaydı. EEG'de göz kırpma artefaktını ayıklamak için referans olarak kullanılır.
570
+ - **MNE-Python:** EEG/MEG işlemenin de facto bilimsel kütüphanesi (klinik standart).
571
+ - **ICA (Independent Component Analysis):** Karışık bir sinyali bağımsız kaynaklarına ayıran algoritma. Örn. EEG kaydında göz kırpma + beyin aktivitesi karışmıştır; ICA bunları ayrı "kaynak"lara böler, sonra göz kırpma kaynağını silip temiz sinyali yeniden inşa edersin.
572
+ - **PSD (Power Spectral Density / Güç Spektral Yoğunluğu):** Sinyalin gücünün frekanslara dağılımı. EEG'de **delta** (0.5–4 Hz, derin uyku), **theta** (4–8 Hz, uyku-uyanıklık geçişi), **alpha** (8–13 Hz, gevşek uyanıklık), **beta** (13–30 Hz, aktif düşünme), **gamma** (30+ Hz, yüksek bilişsel aktivite) bantları.
573
+ - **Bandpass Filter (0.5–40 Hz):** Sadece bu aralıktaki frekansları geçiren filtre. 0.5 Hz altı = DC drift (yavaş baseline kayması), 50/60 Hz = elektrik şebekesi gürültüsü → ikisini de keser.
574
+ - **Epoch:** EEG kaydını sabit süreli (örn. 2 saniyelik) eşit parçalara bölme. Her parça bir "örnek".
575
+ - **SMILES (Simplified Molecular Input Line Entry System):** Molekül yapısının kısa metin gösterimi. Örnekler: `CCO` → ethanol, `CN1C=NC2=C1C(=O)N(C(=O)N2C)C` → kafein. ML modeli SMILES'i değil onu vektöre çeviren fingerprint'i öğrenir.
576
+ - **Morgan Fingerprint:** Molekülün yapısal "barkod"u. 2048 bit'lik 0/1 vektör; her bit "molekülde şu alt yapı var mı?" sorusuna cevap. RDKit `GetMorganFingerprintAsBitVect`, radius=2 (her atomun 2 komşuluk uzağına kadar bakar).
577
+ - **RDKit:** Kimya/cheminformatics açık kaynak Python kütüphanesi. SMILES parse, validation, fingerprint hep RDKit ile.
578
+ - **Cyclosporine / Macrocycle:** ~1.2 kDa, 11-residue (peptit zinciri) makrosiklik (halkasal) immünosüpresan ilaç. BBB eğitim setindeki tipik küçük moleküllere göre devasa ve sıra dışı; OOD probe için kullanıyoruz.
579
+
580
+ ### 16.2 Site Bias & Harmonizasyon
581
+
582
+ - **Site / Cihaz Bias'ı (Site Effect):** Aynı hastanın farklı hastane MRI cihazlarında farklı sayısal sonuç vermesi. Cihaz markası, manyetik alan gücü, yazılım sürümü, çekim protokolü hepsi sistematik kayma yaratır. Tedavi etkisi gibi görünebilir aslında sadece "cihaz farkı"dır.
583
+ - **Site-Gap (Site Boşluğu):** Aynı feature'ın siteler-arası ortalama farkının büyüklüğü. `max(per_site_means) - min(per_site_means)`. Sıfıra yakın olması iyidir (cihaz farkı gözükmüyor demektir).
584
+ - **Site-Gap Reduction (Site Boşluğu Azalması):** Harmonizasyon öncesi vs. sonrası site-gap oranı. Bizde **3290× reduction**: ComBat öncesi siteler-arası ortalama farkı 5.0, sonrası 0.0015 — fark 3290 kat çöktü. Yani MRI'ın hangi hastanede çekildiğinin tahmine etkisi neredeyse sıfırlandı.
585
+ - **ComBat Harmonization:** Çok-merkezli verilerde site bias'ı düzelten istatistiksel algoritma. Her sitenin ortalama (location) + varyans (scale) farkını referans dağılıma çeker, biyolojik sinyali korur. Empirical Bayes shrinkage'ı sayesinde az veri durumunda bile robust. Aslen gen ekspresyonu için icat edildi (Johnson 2007), MRI'a uyarlandı (Fortin 2017–2018).
586
+ - **Empirical Bayes:** Klasik Bayes'in pratik versiyonu — prior'u veriden tahmin eder, az örnekli grupları "ortalamaya çeker" (shrinkage). Az veriyle overfit'i önler.
587
+ - **Z-score Normalization:** `(x - mean) / std`. Sadece **mean**'i sıfıra çeker, scale farkını çözmez. ComBat hem mean hem scale'i düzelttiği için z-score'dan üstün.
588
+ - **KDE (Kernel Density Estimation):** Histogram'ın yumuşak versiyonu. Veri dağılımını düzgün eğri olarak çizer. Bizde her site bir renk; Pre-ComBat panelde renkler ayrışık tepeler oluşturur (cihaz farkı), Post-ComBat panelde üst üste biner (cihaz farkı kalktı) — harmonizasyonun **görsel kanıtı**.
589
+ - **Faceted Plot (Yüzlü Grafik):** Aynı grafik tipinin küçük çoklu versiyonları yan yana. Bizde Pre-ComBat ve Post-ComBat iki ayrı panel, ama aynı eksen.
590
+ - **Long-format DataFrame:** Her satırda tek `(subject, site, feature, value, state)` tuple'ı. Faceted plot ve `groupby` için ideal; "wide" tablonun melt edilmiş hâli.
591
+
592
+ ### 16.3 Makine Öğrenmesi
593
+
594
+ - **Random Forest (RF):** Onlarca-yüzlerce karar ağacının bağımsız oy vermesi üzerine kurulu klasik ML algoritması. Küçük-orta veri setlerinde (≤10K örnek) derin öğrenmeyi yener çünkü deep learning bu boyutta overfit eder.
595
+ - **Stratified Split (Tabakalı Bölme):** Train/test ayırırken her sınıfın oranını koruma. Veride %30 pozitif varsa hem train hem test'te %30 olur. Class imbalance'da kritik.
596
+ - **`predict_proba`:** sklearn modellerinin "ham olasılık" çıktısı. Örn. `[0.18, 0.82]` = %82 olasılıkla pozitif sınıf. `argmax`'ı alırsak label, `max`'ı alırsak confidence.
597
+ - **Confidence (Güven Skoru):** Modelin tahminine verdiği olasılık (`max(predict_proba)`). %50 = bilmiyor, %99 = çok emin.
598
+ - **Calibration (Kalibrasyon):** "Model %80 derse, gerçekten %80 doğruluk gösteriyor mu?" sorusu. Kalibre olmayan model "çok eminim" der ama yanılır; kalibre model güveni ile gerçek precision'ı uyuşur.
599
+ - **Calibration Bin:** Confidence aralıkları (0.50, 0.60, 0.70, 0.75, 0.80, 0.90). Her aralık için held-out test'te precision ölçülür → kullanıcıya "≥%75 confident olduğumda gerçek precision %92" deme imkânı.
600
+ - **Precision (Kesinlik):** Model "pozitif" dediklerinin kaçı gerçekten pozitif? `TP / (TP + FP)`.
601
+ - **Support:** O bin'de kaç örnek var. n=18 demek "bu istatistik 18 örnekten hesaplandı".
602
+ - **Held-out Test Set:** Train'de hiç görmediği veri. Modelin gerçek genelleme performansını ölçen tek dürüst veri.
603
+ - **OOD (Out-of-Distribution):** Eğitim verisinde olmayan, "tanımadığım" tipte örnek. Cyclosporine örneği gibi. Sağlam model OOD'de düşük confidence ile **hedge eder** (kararsız kalır).
604
+ - **Drift (Veri Sapması):** Modelin gerçek dünyada gördüğü verinin zamanla eğitim dağılımından uzaklaşması. "Hasta profili değişti, model eskidi" sinyali.
605
+ - **Drift z-score:** Son 100 tahminin median'ı, eğitim sırasındaki median'dan kaç standart sapma uzakta? Formül: `(rolling_median - train_median) / max(train_std, 1e-9)`. Yorum: `|z|<1` normal, `1≤|z|<2` hafif kayma, `|z|≥2` ciddi kayma — retrain önerilir.
606
+ - **Trailing-100 / Rolling-100 Window:** Son 100 tahmin penceresi. Python `collections.deque(maxlen=100)` ile tutulur.
607
+ - **deque (Double-Ended Queue):** Python'da iki uçtan ekleme/çıkarma yapılabilen sabit-boyutlu kuyruk. `maxlen=100` = en yeni 100 eleman tutulur, eski olan otomatik düşer.
608
+ - **SHAP (SHapley Additive exPlanations):** Bir tahmindeki katkıyı feature'lar arasında "adil" şekilde paylaştıran yöntem. Oyun teorisinden Shapley value (Lloyd Shapley, 1953 — Nobel 2012) kullanır. "Bit #532 kararı +0.18 ittirdi, bit #1024 −0.05 ittirdi" der.
609
+ - **TreeExplainer:** SHAP'in karar ağacı modelleri için kapalı-form **exact** çözümü. Sampling yok, deterministik, tam aynı feature için tam aynı katkıyı verir (Lundberg & Lee 2018).
610
+ - **LIME:** SHAP'a alternatif "local linear approximation". Tree boundary'larında SHAP kadar kesin değil; biz SHAP tercih ediyoruz.
611
+ - **Feature Attribution (Öznitelik Atfı):** Her feature'ın tahmindeki katkısı (yön + büyüklük).
612
+ - **Feature Importance:** Tüm dataset üzerinde bir feature'ın model kararındaki ortalama önem ağırlığı (global). Attribution ise bireysel tahmindekidir (local).
613
+ - **MLflow:** ML deneylerini (run'ları) izleyen açık kaynak araç. "Hangi veri, hangi parametre, hangi metrik?" — hepsi otomatik loglanır.
614
+ - **Run / Run ID:** MLflow'da bir eğitim çalışmasının kaydı. Her train invocation yeni bir `run_id` (örn. `abc123...`) alır.
615
+ - **Provenance (Kanıt İzi / Veri Kökenli):** "Bu tahmin tam olarak hangi modelden, hangi run'dan, hangi veriyle çıktı?" sorusunun audit-trail cevabı. Tıbbi/regülatif sistemlerde zorunlu.
616
+ - **Joblib:** sklearn modellerini diske yazmak/okumak için kullanılan serileştirme kütüphanesi. `pickle`'ın numpy-aware versiyonu.
617
+ - **TDD (Test-Driven Development):** Önce test yaz (kabul kriteri), sonra kodu yaz. Döngü: **RED** (test fail) → **GREEN** (test pass) → **REFACTOR** (temizle, test hâlâ pass).
618
+
619
+ ### 16.4 Backend & Mimari
620
+
621
+ - **FastAPI:** Python'da modern HTTP API framework (Pydantic schema'lar + async + auto-generated `/docs` Swagger UI).
622
+ - **Pydantic:** Python data validation kütüphanesi. Request/response schema'larını type-safe yapar; yanlış formatta input gelirse otomatik HTTP 422 döner.
623
+ - **Uvicorn:** Python ASGI sunucusu, FastAPI'yi çalıştırır.
624
+ - **Streamlit:** Python-only web dashboard framework. React/HTML yazmadan multi-tab interactive UI.
625
+ - **httpx:** Modern Python HTTP istemcisi (`requests`'in async-aware halefi). Streamlit container içinde FastAPI'ya bununla konuşur.
626
+ - **Supervisord:** Tek container içinde birden fazla process'i (uvicorn + streamlit) yöneten süreç yöneticisi. Biri ölünce yeniden başlatır.
627
+ - **BFF (Backend For Frontend) / Proxy Pattern:** UI ile gerçek API arasındaki ara katman. Bizde Streamlit container içinden FastAPI'yi çağırır; dışarı sadece Streamlit (port 7860) açıktır, FastAPI (port 8000) doğrudan internet'e açık değildir.
628
+ - **Endpoint:** API'nin URL yolu. Örn. `POST /predict/bbb`.
629
+ - **HTTP Status Codes:** `200` OK, `400` Bad Request (input geçersiz), `404` Not Found, `422` Unprocessable Entity (Pydantic validation fail), `503` Service Unavailable (model yüklenmedi).
630
+ - **Env Variable (Çevre Değişkeni):** Container'a dışarıdan verilen ayar. Örn. `NEUROBRIDGE_DISABLE_LLM=1`.
631
+ - **Kill-Switch:** Bir özelliği tek değişkenle devre dışı bırakma anahtarı. Demo gününde LLM ölürse `NEUROBRIDGE_DISABLE_LLM=1` ile sistem template path'e düşer ve hayatta kalır.
632
+ - **Graceful Failure / Graceful Degradation:** Bir bileşen hata verince **çökmek yerine** sistemin daha basit modda çalışmaya devam etmesi. Bizde: API HTTP 400 dönerse UI'da kırmızı ERROR yerine sarı WARNING; LLM ölürse template path; MLflow ölürse provenance "—" gösterir.
633
+ - **Fallback Chain:** Bir hata zincirinde sırayla denenen yedekler. LLM Provider A → Provider B → deterministic template gibi.
634
+ - **Hybrid Path:** İki yollu sistem; LLM çalışırsa LLM cevap verir, çalışmazsa template path. Source label ile hangi path'in döndüğü işaretlenir.
635
+ - **Deterministic Template:** Aynı input'a her zaman tıpatıp aynı cevabı veren string template. (LLM ise rastgelelik içerir.)
636
+ - **Idempotence (Idempotency):** Aynı işlemi 1 kez de 100 kez de çalıştırsan sonuç aynı kalır. Pipeline'larımız idempotent → re-run güvenli.
637
+ - **HF Spaces (Hugging Face Spaces):** ML demolarını host etmek için ücretsiz public platform. ML community hub.
638
+ - **Docker / Container:** Uygulama + tüm dependencies'in tek izole paket olarak çalıştırılması. "Bende çalışıyor" sorununu öldürür.
639
+ - **Dockerfile:** Container'ın nasıl build edileceğine dair talimat dosyası.
640
+ - **Cold Start:** Container'ın ilk ayağa kalkış süresi. Bizde build-time train sayesinde model image'a gömülü → cold start'ta train beklemeyiz.
641
+ - **Worker:** Web sunucusu işlem birimi. Her worker bağımsız bellek alanına sahip → drift deque'i her worker'da ayrıdır (production'da Redis'e taşınır).
642
+
643
+ ### 16.5 LLM / Açıklanabilirlik
644
+
645
+ - **LLM (Large Language Model):** GPT, Llama, Gemini, Claude gibi büyük dil modelleri.
646
+ - **OpenRouter:** Birçok LLM provider'ı tek API arkasında toplayan servis. Free tier'da `llama-3.2-3b`, `gemini-flash`, `qwen` gibi seçenekler.
647
+ - **API Key:** Bir servise erişim için kişisel jeton. Asla repo'ya commit edilmez (HF Spaces "Variables and Secrets"'a girilir).
648
+ - **Rationale (Gerekçe):** Modelin tahminine dair doğal-dil açıklama (örn. "Predicted permeable with 82% confidence; SHAP attributions toward this label include bits 532 and 1024…").
649
+ - **Source Label:** Cevabın hangi kaynaktan geldiğini gösteren etiket. Bizde `source: "llm"` veya `source: "template"` — auditability için.
650
+
651
+ ### 16.6 Veri Yapıları & Format
652
+
653
+ - **Parquet:** Sıkıştırılmış kolonlu veri formatı. CSV'den çok daha verimli (boyut + okuma hızı).
654
+ - **DataFrame:** pandas'ın tablo veri yapısı. SQL-benzeri operasyonlar Python içinde.
655
+ - **JSON:** API request/response'larının metin formatı.
656
+ - **`.fif` / `.edf`:** EEG kayıt formatları (FIF = MNE'nin native format'ı, EDF = European Data Format, daha yaygın).
657
+ - **Joblib `.joblib`:** sklearn modeli + custom attribute'lar serialized hâli.
658
+
659
+ ---
660
+
661
+ ## 17. Kapanış
662
 
663
  NeuroBridge Enterprise hackathon'un sloganına ("**Stop Building Ideas. Start Building Systems.**") en doğrudan cevap. 8 gün boyunca disiplinli TDD + Subagent-Driven Development ile inşa ettik. Public deploy'lu, jüri tarayıcıdan tıklayıp dokunabiliyor. 184 test green, 96.8% jüri skoru projeksiyonu, 5/5 hackathon track strong, 4/4 Living Systems pillar full.
664
 
docs/superpowers/plans/2026-04-30-hf-space-live-audit-fixes.md ADDED
@@ -0,0 +1,611 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # HF Space Live Audit Fixes Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Fix three production bugs found on the live HF Space (https://mekosotto-hackathon.hf.space, commit `84572d9`): EEG tab default path is unreachable, the Experiments tab is permanently empty, and the BBB decision card's MLflow provenance strip shows dashes — all because `NEUROBRIDGE_DISABLE_MLFLOW=1` is set in the Dockerfile and `data/raw/eeg.fif` is never seeded.
6
+
7
+ **Architecture:** Three sealed, sequential fixes. (1) Frontend default path swap — Signal tab points at the EEG fixture so a fresh user can click "Run" with zero ceremony. (2) Dockerfile hardening — drop the `NEUROBRIDGE_DISABLE_MLFLOW=1` env, seed `data/raw/eeg.fif` from the fixture, and run the EEG + MRI pipelines at build time so the file-store `mlruns/` is populated with one run per modality. (3) Smoke verification — a sealed `scripts/smoke_hf_space.sh` that probes the public URL plus a manual click-through checklist for the JS-only Streamlit interactions we cannot script.
8
+
9
+ **Tech Stack:** Python 3.12, pytest, FastAPI, Streamlit, MLflow (file-store), Docker (HF Spaces SDK), supervisord.
10
+
11
+ **Test growth:** 184 → 188 (one frontend default-path test, one Dockerfile env test, one Dockerfile pipeline-seeding test, plus an updated existing test).
12
+
13
+ ---
14
+
15
+ ## File Structure
16
+
17
+ | Path | Action | Responsibility |
18
+ |---|---|---|
19
+ | `src/frontend/app.py` | Modify line 1200 | Default EEG input becomes `tests/fixtures/eeg_sample.fif` so the Signal tab works on first click |
20
+ | `Dockerfile` | Modify env block + RUN step | Remove MLflow kill-switch; seed `data/raw/eeg.fif`; run EEG + MRI pipelines at build |
21
+ | `Dockerfile.hf` | Modify env block + RUN step | Same as Dockerfile (kept in sync; HF auto-discovers `Dockerfile`) |
22
+ | `tests/frontend/test_app_defaults.py` | Create | Asserts the EEG default path is a real fixture file, not the missing `data/raw/eeg.fif` |
23
+ | `tests/deploy/test_dockerfile_hf.py` | Modify lines 30-50 | Flip MLflow assertion (must be absent), add EEG-seed + multi-pipeline assertions |
24
+ | `scripts/smoke_hf_space.sh` | Create | Sealed health probe of the public Space URL — runs after deploy |
25
+ | `docs/superpowers/notes/2026-04-30-hf-smoke-checklist.md` | Create | Manual click-through checklist (browser-only verifications) |
26
+
27
+ **Reused utilities:** `src.pipelines.eeg_pipeline.run_pipeline`, `src.pipelines.mri_pipeline.run_pipeline` — both are kwargs-based with `Path` arguments, callable via `python -c` from the Dockerfile RUN step.
28
+
29
+ ---
30
+
31
+ ## Task 1: Frontend EEG default path
32
+
33
+ **Files:**
34
+ - Modify: `src/frontend/app.py:1200`
35
+ - Test: `tests/frontend/test_app_defaults.py`
36
+
37
+ **Why this task is first:** It's the smallest change with its own test, lets us validate the workflow before touching Docker.
38
+
39
+ - [ ] **Step 1: Write the failing test**
40
+
41
+ Create `tests/frontend/test_app_defaults.py`:
42
+
43
+ ```python
44
+ """Defaults shown in the Streamlit UI must point at files that actually exist
45
+ in the deployed image. The HF container does not seed `data/raw/eeg.fif`
46
+ unless we explicitly do so in the Dockerfile, so the EEG tab default must
47
+ either be a fixture file (always present) or be seeded server-side.
48
+ """
49
+ from __future__ import annotations
50
+
51
+ from pathlib import Path
52
+ import re
53
+
54
+ REPO_ROOT = Path(__file__).resolve().parents[2]
55
+ APP_PY = REPO_ROOT / "src" / "frontend" / "app.py"
56
+
57
+
58
+ def _eeg_default_path() -> str:
59
+ """Extract the default value passed to the EEG `input_path` text_input.
60
+
61
+ The line looks like:
62
+ eeg_in = st.text_input("Input FIF/EDF path", "tests/fixtures/eeg_sample.fif", key="eeg_in")
63
+ We pull the second positional arg (the default value).
64
+ """
65
+ text = APP_PY.read_text()
66
+ match = re.search(
67
+ r'st\.text_input\(\s*"Input FIF/EDF path"\s*,\s*"([^"]+)"',
68
+ text,
69
+ )
70
+ assert match is not None, "could not locate EEG text_input default in app.py"
71
+ return match.group(1)
72
+
73
+
74
+ def test_eeg_default_path_points_at_existing_file() -> None:
75
+ default = _eeg_default_path()
76
+ abs_path = REPO_ROOT / default
77
+ assert abs_path.exists(), (
78
+ f"EEG default path {default!r} resolves to {abs_path} which does "
79
+ f"not exist on disk. The HF container does not seed this path "
80
+ f"either, so users get an HTTP 404 the moment they click Run."
81
+ )
82
+
83
+
84
+ def test_eeg_default_path_is_eeg_sample_fixture() -> None:
85
+ """Pin the exact fix: default must be the canonical fixture so both
86
+ local dev and the deployed image agree."""
87
+ assert _eeg_default_path() == "tests/fixtures/eeg_sample.fif"
88
+ ```
89
+
90
+ - [ ] **Step 2: Run the test and confirm it fails**
91
+
92
+ Run: `pytest tests/frontend/test_app_defaults.py -v`
93
+ Expected: 2 failures — both assertions trip because the current default is `data/raw/eeg.fif` which neither exists locally (gitignored) nor in the HF image.
94
+
95
+ - [ ] **Step 3: Patch the default in `src/frontend/app.py:1200`**
96
+
97
+ Edit `src/frontend/app.py`. Find:
98
+
99
+ ```python
100
+ eeg_in = st.text_input("Input FIF/EDF path", "data/raw/eeg.fif", key="eeg_in")
101
+ ```
102
+
103
+ Replace with:
104
+
105
+ ```python
106
+ eeg_in = st.text_input(
107
+ "Input FIF/EDF path",
108
+ "tests/fixtures/eeg_sample.fif",
109
+ key="eeg_in",
110
+ help=(
111
+ "Defaults to the bundled EEG fixture so the demo runs out of "
112
+ "the box. Replace with your own .fif/.edf path on a real run."
113
+ ),
114
+ )
115
+ ```
116
+
117
+ - [ ] **Step 4: Run the test and confirm it passes**
118
+
119
+ Run: `pytest tests/frontend/test_app_defaults.py -v`
120
+ Expected: 2 passed.
121
+
122
+ - [ ] **Step 5: Run the full frontend test suite to confirm no regressions**
123
+
124
+ Run: `pytest tests/frontend/ -v`
125
+ Expected: 4 passed (2 existing + 2 new).
126
+
127
+ - [ ] **Step 6: Commit**
128
+
129
+ ```bash
130
+ git add src/frontend/app.py tests/frontend/test_app_defaults.py
131
+ git commit -m "fix(frontend): EEG default path points at fixture (HF container has no data/raw/eeg.fif)"
132
+ ```
133
+
134
+ ---
135
+
136
+ ## Task 2: Update existing Dockerfile MLflow assertion (RED for Task 3)
137
+
138
+ **Files:**
139
+ - Modify: `tests/deploy/test_dockerfile_hf.py:30-50`
140
+
141
+ **Why now:** The existing test asserts `NEUROBRIDGE_DISABLE_MLFLOW` is *present* in the Dockerfile. We need to flip it before Task 3 removes the env, otherwise Task 3's edit will fail this test (= a hostile RED that hides the real failure mode).
142
+
143
+ - [ ] **Step 1: Read the existing test to understand the structure**
144
+
145
+ Run: `cat tests/deploy/test_dockerfile_hf.py`
146
+ Confirm line 44-46 contains the `assert "neurobridge_disable_mlflow" in text` block.
147
+
148
+ - [ ] **Step 2: Replace the MLflow assertion + tighten the docstring**
149
+
150
+ Edit `tests/deploy/test_dockerfile_hf.py`. Replace the entire `test_dockerfile_contains_required_stages` method body with:
151
+
152
+ ```python
153
+ def test_dockerfile_contains_required_stages(self, dockerfile_text):
154
+ """The HF Dockerfile must:
155
+ - Start FROM a Python base
156
+ - Install requirements.txt
157
+ - Seed data/raw/bbbp.csv AND data/raw/eeg.fif from fixtures
158
+ - Build the BBB model artifact at build time
159
+ - Run all three pipelines (BBB / EEG / MRI) so mlruns/ has one
160
+ run per modality available to /experiments/runs at startup
161
+ - Expose port 7860 (HF Spaces convention)
162
+ - Launch via supervisord
163
+ """
164
+ text = dockerfile_text.lower()
165
+ assert "from python" in text, "must FROM a Python base image"
166
+ assert "requirements.txt" in text, "must reference requirements.txt"
167
+ assert "src.models.bbb_model" in dockerfile_text, (
168
+ "must build the BBB model artifact at image-build time"
169
+ )
170
+ assert "src.pipelines.bbb_pipeline" in dockerfile_text, (
171
+ "must run BBB pipeline at build so mlruns/ has a BBB run"
172
+ )
173
+ assert "src.pipelines.eeg_pipeline" in dockerfile_text, (
174
+ "must run EEG pipeline at build so mlruns/ has an EEG run"
175
+ )
176
+ assert "src.pipelines.mri_pipeline" in dockerfile_text, (
177
+ "must run MRI pipeline at build so mlruns/ has an MRI run"
178
+ )
179
+ assert "tests/fixtures/eeg_sample.fif" in dockerfile_text, (
180
+ "must seed data/raw/eeg.fif from the bundled fixture so the "
181
+ "Signal tab works without user file upload"
182
+ )
183
+ assert "7860" in text, "must expose port 7860 (HF Spaces convention)"
184
+ assert "supervisord" in text, (
185
+ "must launch FastAPI + Streamlit via supervisord"
186
+ )
187
+
188
+ def test_dockerfile_does_not_disable_mlflow(self, dockerfile_text):
189
+ """The kill-switch was removed in 2026-04-30 — file-store mlruns/
190
+ is built into the image and is safe to expose on the read-only
191
+ demo. Re-introducing the kill-switch would silently kill the
192
+ Experiments tab and the BBB provenance strip."""
193
+ text = dockerfile_text.lower()
194
+ assert "neurobridge_disable_mlflow=1" not in text, (
195
+ "Dockerfile must NOT disable MLflow — that empties the "
196
+ "Experiments tab and blanks the BBB provenance strip. "
197
+ "If you need to disable MLflow at runtime, set the env "
198
+ "manually on the Space, do not bake it into the image."
199
+ )
200
+ ```
201
+
202
+ - [ ] **Step 3: Run the test and confirm RED**
203
+
204
+ Run: `pytest tests/deploy/test_dockerfile_hf.py -v`
205
+ Expected: `test_dockerfile_contains_required_stages` FAILS on the new pipeline assertions; `test_dockerfile_does_not_disable_mlflow` FAILS because the current Dockerfile still has `NEUROBRIDGE_DISABLE_MLFLOW=1`. (Both correctly RED — they describe the desired post-fix state.)
206
+
207
+ - [ ] **Step 4: Commit (RED)**
208
+
209
+ ```bash
210
+ git add tests/deploy/test_dockerfile_hf.py
211
+ git commit -m "test(deploy): assert Dockerfile seeds EEG fixture, runs all pipelines, drops MLflow kill-switch (RED)"
212
+ ```
213
+
214
+ ---
215
+
216
+ ## Task 3: Dockerfile fixes — drop kill-switch, seed EEG fixture, run all pipelines
217
+
218
+ **Files:**
219
+ - Modify: `Dockerfile` (canonical, alias of Dockerfile.hf)
220
+ - Modify: `Dockerfile.hf` (kept in sync)
221
+
222
+ **Why this works:** Both `eeg_pipeline.run_pipeline` and `mri_pipeline.run_pipeline` accept Path arguments via kwargs, so we can drive them from `python -c`. They each call `track_pipeline_run(...)` which writes to `mlruns/` via the local file-store, so removing the kill-switch lets `/experiments/runs` find them.
223
+
224
+ - [ ] **Step 1: Read the current Dockerfile**
225
+
226
+ Run: `cat Dockerfile`
227
+ Confirm the env block contains `NEUROBRIDGE_DISABLE_MLFLOW=1` and the RUN step only seeds `bbbp.csv`.
228
+
229
+ - [ ] **Step 2: Patch `Dockerfile` — env block + RUN step**
230
+
231
+ Edit `Dockerfile`. Replace the env block (lines 7-13):
232
+
233
+ ```dockerfile
234
+ ENV PYTHONDONTWRITEBYTECODE=1 \
235
+ PYTHONUNBUFFERED=1 \
236
+ PIP_DISABLE_PIP_VERSION_CHECK=1 \
237
+ PIP_NO_CACHE_DIR=1 \
238
+ DEPLOY_ENV=hf_spaces \
239
+ NEUROBRIDGE_DISABLE_MLFLOW=1 \
240
+ NEUROBRIDGE_DISABLE_LLM=1
241
+ ```
242
+
243
+ with (drop only the MLflow kill-switch; LLM stays disabled because OpenRouter requires a key):
244
+
245
+ ```dockerfile
246
+ ENV PYTHONDONTWRITEBYTECODE=1 \
247
+ PYTHONUNBUFFERED=1 \
248
+ PIP_DISABLE_PIP_VERSION_CHECK=1 \
249
+ PIP_NO_CACHE_DIR=1 \
250
+ DEPLOY_ENV=hf_spaces \
251
+ NEUROBRIDGE_DISABLE_LLM=1
252
+ ```
253
+
254
+ Then replace the build-time data RUN step (lines 39-42):
255
+
256
+ ```dockerfile
257
+ RUN mkdir -p data/raw data/processed && \
258
+ cp tests/fixtures/bbbp_sample.csv data/raw/bbbp.csv && \
259
+ python -m src.pipelines.bbb_pipeline && \
260
+ python -m src.models.bbb_model
261
+ ```
262
+
263
+ with:
264
+
265
+ ```dockerfile
266
+ # Seed raw data from fixtures so the deployed Signal/Image/Molecule tabs
267
+ # work on first click. Then run all three pipelines so mlruns/ contains
268
+ # one run per modality — feeds /experiments/runs and the BBB provenance
269
+ # strip. data/raw/* is gitignored locally so we cannot COPY it.
270
+ RUN mkdir -p data/raw data/processed && \
271
+ cp tests/fixtures/bbbp_sample.csv data/raw/bbbp.csv && \
272
+ cp tests/fixtures/eeg_sample.fif data/raw/eeg.fif && \
273
+ python -m src.pipelines.bbb_pipeline && \
274
+ python -m src.models.bbb_model && \
275
+ python -c "from pathlib import Path; from src.pipelines.eeg_pipeline import run_pipeline; run_pipeline(input_path=Path('tests/fixtures/eeg_sample.fif'), output_path=Path('data/processed/eeg_features.parquet'))" && \
276
+ python -c "from pathlib import Path; from src.pipelines.mri_pipeline import run_pipeline; run_pipeline(input_dir=Path('tests/fixtures/mri_sample'), sites_csv=Path('tests/fixtures/mri_sample/sites.csv'), output_path=Path('data/processed/mri_features.parquet'))"
277
+ ```
278
+
279
+ - [ ] **Step 3: Mirror the same edit into `Dockerfile.hf`**
280
+
281
+ Edit `Dockerfile.hf` to be byte-identical to `Dockerfile` (same env block, same RUN step). Run:
282
+
283
+ ```bash
284
+ diff Dockerfile Dockerfile.hf
285
+ ```
286
+
287
+ Expected: no output (files identical).
288
+
289
+ - [ ] **Step 4: Run the deploy tests to confirm GREEN**
290
+
291
+ Run: `pytest tests/deploy/ -v`
292
+ Expected: 2 passed (`test_dockerfile_contains_required_stages` + `test_dockerfile_does_not_disable_mlflow`).
293
+
294
+ - [ ] **Step 5: Run the full test suite — must stay green**
295
+
296
+ Run: `pytest -q`
297
+ Expected: 188 passed (184 baseline + 2 new frontend defaults + 1 new deploy test + 1 widened existing test). Adjust expectation only if your local count differs by exactly the same delta.
298
+
299
+ - [ ] **Step 6: Smoke-build locally to catch syntax errors before pushing**
300
+
301
+ Run (under 5 min):
302
+
303
+ ```bash
304
+ docker build -t neurobridge-hf-test -f Dockerfile .
305
+ ```
306
+
307
+ Expected: build completes; the final two `python -c` invocations log INFO lines like:
308
+ ```
309
+ INFO | Wrote processed features to data/processed/eeg_features.parquet (rows=N, cols=M)
310
+ INFO | Wrote processed features to data/processed/mri_features.parquet (rows=N, cols=M)
311
+ ```
312
+
313
+ If `docker` is not available locally, skip this step — CI on HF Spaces will catch a broken Dockerfile within ~3 minutes of push.
314
+
315
+ - [ ] **Step 7: Commit**
316
+
317
+ ```bash
318
+ git add Dockerfile Dockerfile.hf
319
+ git commit -m "fix(deploy): seed EEG fixture, run all pipelines at build, drop MLflow kill-switch
320
+
321
+ The Experiments tab was permanently empty and the BBB provenance strip
322
+ showed dashes because NEUROBRIDGE_DISABLE_MLFLOW=1 short-circuited the
323
+ MLflow file-store lookups even though mlruns/ IS produced inside the
324
+ image at build time. Drop the env, run EEG + MRI pipelines too so all
325
+ three experiments have at least one run, and seed data/raw/eeg.fif
326
+ from the fixture so the Signal tab works on first click."
327
+ ```
328
+
329
+ ---
330
+
331
+ ## Task 4: Smoke probe script — what we CAN automate
332
+
333
+ **Files:**
334
+ - Create: `scripts/smoke_hf_space.sh`
335
+
336
+ **Why:** HF Spaces only exposes Streamlit on :7860 publicly. FastAPI on :8000 is container-internal, so we cannot probe `/predict/bbb` or `/experiments/runs` directly. We CAN probe Streamlit's own health and the Space's HTTP envelope.
337
+
338
+ - [ ] **Step 1: Create the script directory if missing**
339
+
340
+ Run:
341
+
342
+ ```bash
343
+ mkdir -p scripts
344
+ ```
345
+
346
+ - [ ] **Step 2: Write `scripts/smoke_hf_space.sh`**
347
+
348
+ Create `scripts/smoke_hf_space.sh`:
349
+
350
+ ```bash
351
+ #!/usr/bin/env bash
352
+ # Sealed smoke probe for the live HF Space.
353
+ # Verifies what we can verify without a browser: HTTP envelope, Streamlit
354
+ # health, response headers. Returns 0 on success, 1 on any failure.
355
+ #
356
+ # Usage: scripts/smoke_hf_space.sh [base_url]
357
+ # default base_url: https://mekosotto-hackathon.hf.space
358
+
359
+ set -euo pipefail
360
+
361
+ BASE="${1:-https://mekosotto-hackathon.hf.space}"
362
+ FAIL=0
363
+
364
+ probe() {
365
+ local label="$1" url="$2" expect="$3"
366
+ local actual
367
+ actual="$(curl -sS -o /dev/null -w "%{http_code}" "$url")"
368
+ if [[ "$actual" == "$expect" ]]; then
369
+ printf " OK %-40s %s\n" "$label" "$actual"
370
+ else
371
+ printf " FAIL %-40s expected=%s actual=%s\n" "$label" "$expect" "$actual"
372
+ FAIL=1
373
+ fi
374
+ }
375
+
376
+ echo "Probing $BASE"
377
+ probe "frontend root" "$BASE/" "200"
378
+ probe "streamlit health" "$BASE/_stcore/health" "200"
379
+ probe "fastapi NOT publicly mounted (good)" "$BASE/health" "403"
380
+
381
+ echo
382
+ if [[ "$FAIL" == "0" ]]; then
383
+ echo "Smoke OK — proceed to manual click-through checklist:"
384
+ echo " docs/superpowers/notes/2026-04-30-hf-smoke-checklist.md"
385
+ exit 0
386
+ else
387
+ echo "Smoke FAILED — see HF Space logs at:"
388
+ echo " https://huggingface.co/spaces/mekosotto/hackathon/discussions"
389
+ exit 1
390
+ fi
391
+ ```
392
+
393
+ - [ ] **Step 3: Make it executable**
394
+
395
+ Run:
396
+
397
+ ```bash
398
+ chmod +x scripts/smoke_hf_space.sh
399
+ ```
400
+
401
+ - [ ] **Step 4: Test the script against the currently-deployed (broken) Space**
402
+
403
+ Run:
404
+
405
+ ```bash
406
+ scripts/smoke_hf_space.sh
407
+ ```
408
+
409
+ Expected output: all three probes return OK (frontend 200, streamlit 200, fastapi 403). The smoke envelope passes even on the broken deploy — it's checking infrastructure, not feature correctness. Feature correctness is the manual checklist (Task 5).
410
+
411
+ - [ ] **Step 5: Commit**
412
+
413
+ ```bash
414
+ git add scripts/smoke_hf_space.sh
415
+ git commit -m "test(deploy): scripts/smoke_hf_space.sh — sealed HTTP envelope probe"
416
+ ```
417
+
418
+ ---
419
+
420
+ ## Task 5: Manual click-through checklist (browser-only verifications)
421
+
422
+ **Files:**
423
+ - Create: `docs/superpowers/notes/2026-04-30-hf-smoke-checklist.md`
424
+
425
+ **Why:** Streamlit is JS-rendered; we cannot drive it from curl. Document the exact button-by-button verification so the user (or a reviewer) walks the demo and catches anything the unit tests missed.
426
+
427
+ - [ ] **Step 1: Create the notes directory if missing**
428
+
429
+ Run:
430
+
431
+ ```bash
432
+ mkdir -p docs/superpowers/notes
433
+ ```
434
+
435
+ - [ ] **Step 2: Write `docs/superpowers/notes/2026-04-30-hf-smoke-checklist.md`**
436
+
437
+ Create `docs/superpowers/notes/2026-04-30-hf-smoke-checklist.md`:
438
+
439
+ ```markdown
440
+ # HF Space Manual Smoke Checklist — 2026-04-30
441
+
442
+ After the deploy completes (~3-5 min after `git push hf main`), open
443
+ https://mekosotto-hackathon.hf.space/ and walk this list. Anything
444
+ that fails is a regression vs the audit fix in
445
+ `docs/superpowers/plans/2026-04-30-hf-space-live-audit-fixes.md`.
446
+
447
+ ## Hero strip (top of page)
448
+
449
+ - [ ] Hero title `NeuroBridge Enterprise` fades up smoothly (not instant)
450
+ - [ ] Status row shows three dots:
451
+ - [ ] `api · operational` (green)
452
+ - [ ] `mlflow · tracking` (green) — **was muted before this fix**
453
+ - [ ] `explainer · template only` (muted, expected — LLM stays disabled)
454
+
455
+ ## Molecule tab (BBB)
456
+
457
+ - [ ] Default edge case "Custom input (default)" is selected
458
+ - [ ] Input box shows `CCO`
459
+ - [ ] Click "Predict BBB permeability"
460
+ - [ ] Decision card animates in with spring scale-in on the verdict
461
+ - [ ] Provenance strip shows real values:
462
+ - [ ] `mlflow · <8-char run id>` (NOT `—`) — **was `—` before this fix**
463
+ - [ ] `model · v1`
464
+ - [ ] `trained · <ISO timestamp>` (NOT `—`)
465
+ - [ ] `n=<integer>` (NOT `n=—`)
466
+ - [ ] Verdict reads `permeable` with confidence ~80-100%
467
+ - [ ] SHAP bar chart renders with sand-colored bars
468
+ - [ ] Switch dropdown to "Invalid SMILES" → click Predict → see yellow warning, NOT red error
469
+
470
+ ## Signal tab (EEG)
471
+
472
+ - [ ] Default input field shows `tests/fixtures/eeg_sample.fif` — **was `data/raw/eeg.fif` before this fix**
473
+ - [ ] Click "Run EEG pipeline"
474
+ - [ ] Result card shows rows / columns / duration_sec / mlflow_run_id
475
+ - [ ] Expand "Ask the AI Assistant about this EEG run"
476
+ - [ ] Click "Ask AI Assistant" → see deterministic-template rationale (no error)
477
+
478
+ ## Image tab (MRI)
479
+
480
+ - [ ] Defaults: `tests/fixtures/mri_sample` and `tests/fixtures/mri_sample/sites.csv`
481
+ - [ ] Click "Run ComBat diagnostics"
482
+ - [ ] Three KPI cards render: Site-gap (Pre), Site-gap (Post), Reduction factor
483
+ - [ ] Pre/Post KDE altair chart renders
484
+ - [ ] Expand "Ask the AI Assistant about this ComBat run" → click → see rationale
485
+
486
+ ## AI Assistant tab
487
+
488
+ - [ ] After running a BBB prediction in the Molecule tab, this tab shows
489
+ "Latest prediction: ..." caption
490
+ - [ ] Click "Ask the AI Assistant" → conversation appears with source =
491
+ `template` and model = `—` (LLM intentionally disabled on HF)
492
+
493
+ ## Experiments tab
494
+
495
+ - [ ] Table loads with **at least 3 rows** — one each for `bbb_pipeline`,
496
+ `eeg_pipeline`, `mri_pipeline` — **was empty before this fix**
497
+ - [ ] Compare-two-runs section is visible (≥2 rows are present)
498
+ - [ ] Pick two run IDs → click "Show diff" → diff table renders
499
+
500
+ ## Sidebar
501
+
502
+ - [ ] Toggle "Dark mode" off → page rebuilds with cream paper theme
503
+ - [ ] Toggle back on → page rebuilds with editorial dark theme
504
+ - [ ] Both themes preserve the sand accent on the hero word-mark
505
+
506
+ ## Reduced-motion respect
507
+
508
+ - [ ] (Optional) Open DevTools → Rendering → enable "prefers-reduced-motion"
509
+ - [ ] Reload — animations are near-instant (< 1ms duration), but layout
510
+ and content are unchanged
511
+ ```
512
+
513
+ - [ ] **Step 3: Commit**
514
+
515
+ ```bash
516
+ git add docs/superpowers/notes/2026-04-30-hf-smoke-checklist.md
517
+ git commit -m "docs(notes): manual click-through checklist for HF Space smoke verification"
518
+ ```
519
+
520
+ ---
521
+
522
+ ## Task 6: Final verification — full suite + smoke envelope
523
+
524
+ - [ ] **Step 1: Run the full pytest suite locally**
525
+
526
+ Run: `pytest -q`
527
+ Expected: `188 passed`. (184 baseline + 2 new frontend tests + 1 new deploy test + 1 widened existing test = 188.)
528
+
529
+ - [ ] **Step 2: Run pytest with UserWarning escalation to confirm no warnings**
530
+
531
+ Run: `pytest -W error::UserWarning tests/`
532
+ Expected: `188 passed, 0 escalations`.
533
+
534
+ - [ ] **Step 3: Run the smoke envelope against the still-broken deployed Space**
535
+
536
+ Run: `scripts/smoke_hf_space.sh`
537
+ Expected: 3 OK lines (we have not deployed yet — this confirms the script itself works).
538
+
539
+ - [ ] **Step 4: Inspect the local commit graph**
540
+
541
+ Run: `git log --oneline -10`
542
+ Expected: 5 new commits on top of `84572d9`:
543
+ ```
544
+ <hash> docs(notes): manual click-through checklist for HF Space smoke verification
545
+ <hash> test(deploy): scripts/smoke_hf_space.sh — sealed HTTP envelope probe
546
+ <hash> fix(deploy): seed EEG fixture, run all pipelines at build, drop MLflow kill-switch
547
+ <hash> test(deploy): assert Dockerfile seeds EEG fixture, runs all pipelines, drops MLflow kill-switch (RED)
548
+ <hash> fix(frontend): EEG default path points at fixture (HF container has no data/raw/eeg.fif)
549
+ 84572d9 feat(frontend): premium motion layer — Apple HIG / Netflix transitions
550
+ ```
551
+
552
+ ---
553
+
554
+ ## Task 7: Hand-off to user — push + verify
555
+
556
+ The HF write token from the previous session was revoked (correct security move). The push must be done by the user with a fresh token.
557
+
558
+ - [ ] **Step 1: User generates a new HF write token**
559
+
560
+ Visit https://huggingface.co/settings/tokens → "New token" → role "Write" → copy.
561
+
562
+ - [ ] **Step 2: User pushes**
563
+
564
+ ```bash
565
+ cd /Users/mertgungor/Desktop/hackathon
566
+ git push hf main
567
+ ```
568
+
569
+ If prompted for username/password: username = HF username (e.g. `mekosotto`), password = the write token (NOT the account password). HF deprecated password auth.
570
+
571
+ - [ ] **Step 3: User waits ~3-5 min for HF rebuild**
572
+
573
+ Monitor at https://huggingface.co/spaces/mekosotto/hackathon — the "Building" badge flips to "Running" when ready. The build re-runs `bbb_pipeline + bbb_model + eeg_pipeline + mri_pipeline` so this rebuild is slightly slower than the last one (~+1 min).
574
+
575
+ - [ ] **Step 4: User runs the smoke probe**
576
+
577
+ ```bash
578
+ scripts/smoke_hf_space.sh
579
+ ```
580
+
581
+ Expected: 3 OK lines.
582
+
583
+ - [ ] **Step 5: User walks the manual checklist**
584
+
585
+ Open `docs/superpowers/notes/2026-04-30-hf-smoke-checklist.md` and tick boxes against the live UI at https://mekosotto-hackathon.hf.space/.
586
+
587
+ The two regression boxes that MUST flip from FAIL → OK are:
588
+ 1. BBB provenance strip shows real `mlflow · <run id>` (not `—`)
589
+ 2. Experiments tab shows ≥3 rows (not empty)
590
+ 3. Signal tab default input is `tests/fixtures/eeg_sample.fif` and "Run EEG pipeline" succeeds on first click.
591
+
592
+ If any box still fails, the HF build logs at https://huggingface.co/spaces/mekosotto/hackathon/logs are the next stop — paste any error line back into the conversation.
593
+
594
+ ---
595
+
596
+ ## Self-Review
597
+
598
+ **Spec coverage:**
599
+ - Issue 1 (EEG default path) → Task 1 (frontend edit) + Task 3 (Dockerfile seeds the path too)
600
+ - Issue 2 (Experiments empty + provenance dashes) → Task 3 drops kill-switch + bakes EEG/MRI runs
601
+ - Issue 3 (Hero status dot) → resolves automatically via Issue 2 fix; called out in Task 5 checklist
602
+ - Test growth 184 → 188 → covered by Task 1 (+2), Task 2 (+1 new method), unchanged delta on widened existing test
603
+ - "User runs `git push hf main`" → Task 7
604
+
605
+ **Placeholder scan:** None. Every step has exact code or commands.
606
+
607
+ **Type consistency:** `run_pipeline` signatures match the verified source (`eeg_pipeline.py:411`, `mri_pipeline.py:282`). The `_eeg_default_path` regex matches the literal `st.text_input("Input FIF/EDF path", "...")` form. Dockerfile env block diff is exact line-for-line.
608
+
609
+ **Risks called out:**
610
+ - Local docker build (Task 3 Step 6) is optional — HF CI catches Dockerfile syntax errors anyway.
611
+ - Removing the kill-switch widens the path that hits MLflow's broad-`except` in `_build_provenance`; that's intentional fallback behavior, not a regression.