You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#487 landed the guard set in _field_guards (#494) and converted batching.py and the configuration half of build_ai_vlm_checks.py. checks.py is the largest file still hand-rolling it.
Measured on current main
38 hand-rolled isinstance(x, bool) sites remain across 16 files. Two groups are deliberately not targets:
catalog.py (6): coerces NumPy scalars, which _field_guards deliberately does not. See its module docstring. Leave it.
build_ai_vlm_checks.py (4): three parse external model responses and one is a genuine bool field. All four are correct as they are, and Centralize numeric range validation #494's review covers why.
That leaves checks.py as the biggest convertible one, with 5:
line
parameter
current shape
:991
shake_threshold_dps
bool + np.isfinite + < 0
:998
unstable_min_duration_s
bool + np.isfinite + < 0
:1005
horizontal_field_of_view_degrees
bool + np.isfinite + not 0 < x <= 360
:1659
max_plausible_fps
bool, then np.isfinite + <= 0
:1664
downsample_tolerance_fps
bool, then np.isfinite + < 0
Four of the five map straight onto the new helpers:
horizontal_field_of_view_degrees is the odd one: it wants a half-open range, 0 < x <= 360. There is no helper for that. Add one only if a second caller wants the same shape; otherwise compose require_finite_float with the bound left in place, and say which you chose.
What this costs, which is the part to decide
Every message changes to _field_guards's spelling. "shake_threshold_dps must be finite and non-negative" becomes "shake_threshold_dps must be >= 0, got -1". The second names the offending value, which is better, but it is a user-visible string change across five parameters and it will move test assertions.
That was accepted for #487 and it is accepted here. Two things not to lose:
rg -c "isinstance\([a-z_.]+, bool\)" src/hflow/checks.py reports at most 1 (the field-of-view case, if you leave it composed).
Every refusal the file made before, it still makes: bool, non-finite, and out-of-range for all five parameters, each on a camera-less episode.
Tests use match= with anchored patterns. On an episode with cameras a camera-processing failure also raises ValueError, so an unanchored pytest.raises(ValueError) passes whether or not the guard is there. fix: validate camera FPS thresholds #492 has the shape to copy.
Mutation: neuter each guard in turn and confirm a test goes red.
Sequencing
Wait for #459 to merge. It is open against the guards in camera_frame_stats and camera_signal_quality in this same file and will add more of them. Converting underneath it would conflict.
Validation
uv sync --locked --all-extras
uv run ruff check
uv run ruff format --check
uv run ty check
uv run pytest -q
#487 landed the guard set in
_field_guards(#494) and convertedbatching.pyand the configuration half ofbuild_ai_vlm_checks.py.checks.pyis the largest file still hand-rolling it.Measured on current main
38 hand-rolled
isinstance(x, bool)sites remain across 16 files. Two groups are deliberately not targets:catalog.py(6): coerces NumPy scalars, which_field_guardsdeliberately does not. See its module docstring. Leave it.build_ai_vlm_checks.py(4): three parse external model responses and one is a genuine bool field. All four are correct as they are, and Centralize numeric range validation #494's review covers why.That leaves
checks.pyas the biggest convertible one, with 5::991shake_threshold_dpsnp.isfinite+< 0:998unstable_min_duration_snp.isfinite+< 0:1005horizontal_field_of_view_degreesnp.isfinite+not 0 < x <= 360:1659max_plausible_fpsnp.isfinite+<= 0:1664downsample_tolerance_fpsnp.isfinite+< 0Four of the five map straight onto the new helpers:
horizontal_field_of_view_degreesis the odd one: it wants a half-open range,0 < x <= 360. There is no helper for that. Add one only if a second caller wants the same shape; otherwise composerequire_finite_floatwith the bound left in place, and say which you chose.What this costs, which is the part to decide
Every message changes to
_field_guards's spelling."shake_threshold_dps must be finite and non-negative"becomes"shake_threshold_dps must be >= 0, got -1". The second names the offending value, which is better, but it is a user-visible string change across five parameters and it will move test assertions.That was accepted for #487 and it is accepted here. Two things not to lose:
selected_cameras, which is what camera_stability accepts an invalid field of view on an episode with no cameras, because that guard lives inside the per-camera loop #445 and camera_frame_stats and camera_signal_quality skip their threshold guards on a camera-less episode, and name internal fields when they do fire #447 were about. A guard inside the per-camera loop does not fire on a camera-less episode.Definition of done
rg -c "isinstance\([a-z_.]+, bool\)" src/hflow/checks.pyreports at most 1 (the field-of-view case, if you leave it composed).match=with anchored patterns. On an episode with cameras a camera-processing failure also raisesValueError, so an unanchoredpytest.raises(ValueError)passes whether or not the guard is there. fix: validate camera FPS thresholds #492 has the shape to copy.Sequencing
Wait for #459 to merge. It is open against the guards in
camera_frame_statsandcamera_signal_qualityin this same file and will add more of them. Converting underneath it would conflict.Validation