Test strategy (r1 — for review)
Comment on le valide. Stratégie de tests de S14 (ADR-0095 artefact 5/5). Le cœur est l'exhaustivité de la truth table Q1 (slice A, pure) — déjà implémentée et verte (12/12). Slice B (le fit in-pod) est testé par un wiring smoke + le dry-run in-pod (geste opérateur) ; un vrai fit local est impossible par design (ADR-90, §0bis du plan r4).
1. Objectifs & périmètre¶
Valider que la règle de décision pré-enregistrée Q1 (plan r4 §1) est implémentée exactement : chaque
combinaison d'entrées → exactement un verdict + cause/reason, aucun fall-through, aucun crash, aucune
valeur décisionnelle inventée hors plan. Hors périmètre : valider le verdict scientifique réel (ce
n'est pas un test CI — §6) ; le vrai fit (in-pod, ADR-90).
2. Pyramide + tiers ADR-83¶
| Tier | Markers | Quoi (S14) |
|---|---|---|
| fast (PR-blocking) | unit |
slice A : decision core + probes + gardes (le fichier de test actuel, 12 tests) |
| medium | dag_smoke |
(à venir) import + 1-step stub du DAG diagnostic__s14_q1 |
| nightly | ml_behaviour |
(post-S09) Q2 sur le replay blanchi |
| operator-driven | post_deploy_smoke |
le dry-run in-pod de run_inpod (vrai fit, base_env injecté) |
pytestmark = [pytest.mark.unit, pytest.mark.story("CVN-N001-EI-S14")] (présent dans le test slice-A).
3. Slice A — decision core (le cœur, implémenté)¶
tests/unit/finetune/diagnostic/test_s14_lgb_output_validity.py (12 tests, 12/12 vert) :
| Test | Couvre |
|---|---|
test_synthetic_early_stopping_degenerate |
control positif #12 : 1 arbre / 156 → CONFIG_DEGENERATE_LGB(cause=early_stopping(eval_metric:aucpr)) (l'ablation localise) |
test_synthetic_capacity_degenerate |
best_iter≈plafond + AUPRC≈hasard → cause=capacity/feature/label (pas early-stop) |
test_synthetic_config_ok |
entraîne + rang OK → CONFIG_OK_LGB + note Q2/S09 |
test_best_iter_floor_with_healthy_rank_is_not_forced_underfit |
r2 #3 : best_iter≤floor n'override pas un rang sain si plafond≈best_iter (seul plafond≫best_iter = early-stop) |
test_regime_base_rate_guard / test_regime_label_guard |
#4 : fold hors régime → INCONCLUSIVE_TOOLING(reason=regime_*) |
test_leakage_guard |
#9 : fe_fitted_on_train_only=False → reason=leakage |
test_label_misalignment_guard / test_empty_preds_guard |
#10 : shape/align/empty → reason=labels/preds |
test_power_gate_straddles_boundary |
#7 : IC de lift enjambe la frontière → INCONCLUSIVE_POWER (boundary-relative, pas un half-width absolu) |
test_decision_is_total_and_single |
totalité/no-crash : tout input → exactement un verdict, as_event() valide, jamais de raise |
test_metrics_sane |
skill > noise (auprc_lift), ECE ∈ [0,1] |
Exhaustivité (le pendant de « jamais None ») :
test_decision_is_total_and_single+ les cas D1–D9 couvrent les classes d'équivalence de la truth table Q1 (plan r4 Fig 1).decide_q1est pure → testable en isolation totale du data-prep (c'est l'intérêt du split A/B).
4. Slice B — fit in-pod (wiring + dry-run)¶
Slice B (s14_q1_fit_inpod.py) est I/O-couplé (cache, harness) → pas de test unitaire local d'un vrai
fit (ADR-90 gate). Validé par :
- wiring smoke (--smoke) : run_q1 + fit_fn sur un Datasets synthétique via le harness → no-crash
+ verdict structuré (preuve locale max). Vérifié.
- dry-run in-pod (geste opérateur) : run_inpod dans un pod où base_env (ADR-90) est injecté → prouve
le vrai fit + capture best_iteration + predict_proba sans crash. La preuve que le local ne peut pas
faire.
5. Invariants¶
| Inv | Énoncé | Type |
|---|---|---|
| I1 | best_iteration réel mesuré (pas le plafond n_estimators) |
contract (slice B) |
| I2 | rang = tail (AUPRC-lift), pas l'AUC global ; décidé sur l'IC over draws (median+max) | property (slice A) |
| I3 | ECE equal-mass (quantile), pas equal-width | contract |
| I4 | early-stop = structurel (best_iter≪plafond), best_iteration n'override pas un rang sain |
property |
| I5 | régime-match (label + base_rate ±20 %) précondition dure | contract |
| I6 | no-crash : tout chemin → INCONCLUSIVE_TOOLING(reason), jamais un raise/print |
property |
| I7 | valeurs décisionnelles figées dans le plan/module (pas inventées au run) | contract |
6. Hors-test (validation système, in-pod)¶
Le verdict réel Q1 (CONFIG_*) est une validation système in-pod (le dry-run §4 + le run décisionnel
sur le fold de confiance), pas un test CI. Q2 (post-S09) sur le replay blanchi est nightly/operator.
7. Critères de done (gate PR)¶
-
make test-unitvert ; 12/12 slice A (truth-table exhaustivité + gardes + power). - wiring smoke
--smokeno-crash. - black/isort(--profile black)/flake8 clean.
- (au merge) DAG
diagnostic__s14_q1dag_smoke + ADR-92. - aucune valeur décisionnelle hors plan r4.