Iter-1 patch (thresholds.yaml bottom_visible_pct_min 50→30) had zero effect:
04_frame_extract.py and 04b_trim_water.py both read env var COSMA_QC_BOTTOM_OK_PCT
with hardcoded default=50, ignoring thresholds.yaml entirely.
Add _load_bottom_ok_pct() loader in both stages: reads thresholds.yaml first,
falls back to COSMA_QC_BOTTOM_OK_PCT env var, then hardcoded 50.
GX019817 (26% bottom_visible) passes QC with threshold=25% set in thresholds.yaml.
Stage 04 frame extract:
- New lib_frame_qc.py: per-frame Laplacian/contrast/blue-dominance scoring
- Classes: bottom_visible / water_no_bottom / turbid_water / out_of_water
- Sample 1/5 frames after extraction, write qc.json per segment
- Record metrics (frames_total, frames_bottom_visible, bottom_visible_pct)
- Mark job degraded when bottom_visible_pct < 50%
Per-AUV viser view:
- scripts/viser_auv.py loads all PLYs of an AUV, color per file
- POST /pipeline/missions/{id}/auvs/{auv}/view rsyncs ply -> worker
- launches viser on hashed port 9300+, returns URL
- _pipeline.html exposes AUV list, JS handler opens viser tab