Skip to content

feat(safety): bed adhesion precondition gate - #51

Merged
Ghenghis merged 3 commits into
developfrom
codex/bed-adhesion-precondition
May 3, 2026
Merged

Ghenghis merged 3 commits into
developfrom
codex/bed-adhesion-precondition

Conversation

@Ghenghis

@Ghenghis Ghenghis commented May 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • adds a pure-data safety.bed_adhesion_precondition gate for first-layer Z offset and bed-temperature readiness before homing/upload
  • wires the gate into run_all_safety_gates and the public safety registry
  • adds focused and bundle-level pytest coverage for pass/fail/skip behavior

Safety / local settings

  • no printer IPs, local env paths, .env contents, or user setup values are committed
  • live printer targets remain operator-local only

Audit fixes

  • rejected non-finite z_tolerance_mm / min_bed_fraction threshold values so thresholds fail closed
  • allowed bed_target_c=0.0 room-temperature/no-heated-bed profiles while still rejecting negative bed targets

Verification

  • python -m pytest 04_testing/pytest/test_safety_bed_adhesion.py 04_testing/pytest/test_safety_bundle.py -q — 22 passed
  • python -m pytest 04_testing/pytest/test_safety_bed_adhesion.py 04_testing/pytest/test_safety_bundle.py 04_testing/pytest/test_safety_emergency_stop.py 04_testing/pytest/test_safety_gcode_bounds.py 04_testing/pytest/test_safety_material_window.py 04_testing/pytest/test_safety_thermal_runaway.py -q — 66 passed
  • python -m pytest 04_testing/pytest -q — passed before the audit-fix commits; latest head is covered by GitHub CI plus focused/safety reruns above
  • python -m ruff check 03_implementation/src 04_testing/pytest — passed before audit fixes; changed-file ruff reruns passed after fixes
  • python -m ruff format --check 03_implementation/src 04_testing/pytest — passed before audit fixes; changed-file format checks passed after fixes
  • pre-push fast subset — passed on each push

Hermes task: H3D-BED-ADHESION-PRECONDITION-GATE

@gemini-code-assist

Copy link
Copy Markdown

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@coderabbitai

coderabbitai Bot commented May 3, 2026 •

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

A new P2 bed-adhesion safety gate is introduced to validate first-layer Z offset tolerance and minimum bed temperature fraction before homing. The precondition check is integrated into run_all_safety_gates() with configurable thresholds, and the gate is registered in the module's public API and gate registry.

Changes

Bed-Adhesion Safety Gate

Layer / File(s) Summary
Data Shape
03_implementation/src/hermes3d/core/safety/bed_adhesion.py
BedAdhesionCheckResult dataclass captures Z offset/bed temperature inputs, tolerances, computed properties (bed_min_c, z_delta_mm), pass/fail status, and failure reasons; includes to_dict() serialization.
Core Validation
03_implementation/src/hermes3d/core/safety/bed_adhesion.py
check_bed_adhesion_precondition() validates input finiteness, rejects if already homed, checks first-layer Z offset within tolerance, and verifies bed reaches a minimum temperature fraction of target; returns BedAdhesionCheckResult with accumulated failure reasons. build_violation_payload() creates a structured safety.violation payload.
Gate Registration & Wiring
03_implementation/src/hermes3d/core/safety/__init__.py
New gate ID constant GATE_BED_ADHESION added to ALL_SAFETY_GATE_IDS. run_all_safety_gates() signature extended with bed-adhesion parameters (first_layer_z_offset_mm, target_first_layer_z_offset_mm, bed_actual_c, bed_target_c, bed_adhesion_homed, tolerance/threshold defaults). Gate is executed before emergency-stop logic and result appended to passed gates or violations. Public API exports updated to expose BedAdhesionCheckResult, GATE_BED_ADHESION, and check_bed_adhesion_precondition.
Tests & Validation
04_testing/pytest/test_safety_bed_adhesion.py, 04_testing/pytest/test_safety_bundle.py
Comprehensive unit tests for precondition passing (target offset, hot bed), boundary conditions (inclusive Z tolerance, bed temperature), edge cases (negative offsets, zero bed target), multi-reason failures, non-finite fail-closed behavior, invalid thresholds, and payload shape. Bundle tests updated to provide new bed-adhesion inputs, verify gate registry size, and assert violations/passes for adhesion scenarios including room-temperature profiles.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A bed's embrace, so firm and true,
With temps and offsets checked anew,
Before the head descends to kiss,
We verify the bond won't miss—
Adhesion safe, the layer thrives! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: introducing a bed adhesion precondition safety gate, which aligns with the primary feature added across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/bed-adhesion-precondition

Comment @coderabbitai help to get the list of available commands and usage tips.

@Ghenghis

Ghenghis commented May 3, 2026

Copy link
Copy Markdown
Owner Author

Fixed the fresh correctness-audit finding: z_tolerance_mm and min_bed_fraction now reject non-finite threshold values, with focused regression coverage. Local focused tests: 20 passed; pre-push fast subset passed.

@Ghenghis

Ghenghis commented May 3, 2026

Copy link
Copy Markdown
Owner Author

Fixed the second correctness-audit finding: room-temperature bed profiles (�ed_target_c=0) now pass the bed-adhesion temperature precondition while negative bed targets still fail. Added focused and bundle regression coverage. Local focused tests: 22 passed; safety slice: 66 passed; pre-push fast subset passed.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@03_implementation/src/hermes3d/core/safety/bed_adhesion.py`:
- Around line 62-92: The function check_bed_adhesion_precondition currently
treats homed=None as "not homed" and allows the check to pass; change it to fail
closed by explicitly detecting homed is None and appending a rejection reason
(e.g., "homing state unknown") so the precondition gate rejects unknown homing
state; locate the homed parameter use in check_bed_adhesion_precondition and add
a branch that appends the failure reason when homed is None (keep the existing
branch that appends "bed adhesion precondition must run before homing" when
homed is True).
- Around line 46-59: The to_dict method may emit NaN/Inf for numeric telemetry;
update the to_dict in the bed adhesion class to sanitize all numeric fields
(first_layer_z_offset_mm, target_first_layer_z_offset_mm, z_delta_mm,
z_tolerance_mm, bed_actual_c, bed_target_c, bed_min_c, min_bed_fraction, and any
other numeric attributes) by checking math.isfinite(...) and replacing
non-finite values with a JSON-safe sentinel (e.g., None) before placing them in
the returned dict; keep boolean/string fields (passed, homed, reasons) unchanged
and preserve the dict keys exactly as in the current to_dict signature.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16f4756b-c3d6-44f2-9a8e-3fced9b2529a

📥 Commits

Reviewing files that changed from the base of the PR and between 68707cc and 1ae6469.

📒 Files selected for processing (4)
  • 03_implementation/src/hermes3d/core/safety/__init__.py
  • 03_implementation/src/hermes3d/core/safety/bed_adhesion.py
  • 04_testing/pytest/test_safety_bed_adhesion.py
  • 04_testing/pytest/test_safety_bundle.py

Comment on lines +46 to +59
def to_dict(self) -> dict[str, Any]:
return {
"passed": self.passed,
"first_layer_z_offset_mm": self.first_layer_z_offset_mm,
"target_first_layer_z_offset_mm": self.target_first_layer_z_offset_mm,
"z_delta_mm": self.z_delta_mm,
"z_tolerance_mm": self.z_tolerance_mm,
"bed_actual_c": self.bed_actual_c,
"bed_target_c": self.bed_target_c,
"bed_min_c": self.bed_min_c,
"min_bed_fraction": self.min_bed_fraction,
"homed": self.homed,
"reasons": list(self.reasons),
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize non-finite telemetry before to_dict().

to_dict() can emit raw nan/inf values from failed checks. That makes the payload unsafe for downstream serialization and violates the module’s pure-data contract.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@03_implementation/src/hermes3d/core/safety/bed_adhesion.py` around lines 46 -
59, The to_dict method may emit NaN/Inf for numeric telemetry; update the
to_dict in the bed adhesion class to sanitize all numeric fields
(first_layer_z_offset_mm, target_first_layer_z_offset_mm, z_delta_mm,
z_tolerance_mm, bed_actual_c, bed_target_c, bed_min_c, min_bed_fraction, and any
other numeric attributes) by checking math.isfinite(...) and replacing
non-finite values with a JSON-safe sentinel (e.g., None) before placing them in
the returned dict; keep boolean/string fields (passed, homed, reasons) unchanged
and preserve the dict keys exactly as in the current to_dict signature.

Comment on lines +62 to +92
def check_bed_adhesion_precondition(
*,
first_layer_z_offset_mm: float,
target_first_layer_z_offset_mm: float,
bed_actual_c: float,
bed_target_c: float,
z_tolerance_mm: float = 0.05,
min_bed_fraction: float = 0.97,
homed: bool | None = None,
) -> BedAdhesionCheckResult:
"""Validate bed-adhesion preconditions before homing or upload."""
reasons: list[str] = []

if not math.isfinite(z_tolerance_mm) or z_tolerance_mm < 0:
raise ValueError("z_tolerance_mm must be finite and non-negative")
if not math.isfinite(min_bed_fraction) or not (0 < min_bed_fraction <= 1):
raise ValueError("min_bed_fraction must be in (0, 1]")

inputs = {
"first_layer_z_offset_mm": first_layer_z_offset_mm,
"target_first_layer_z_offset_mm": target_first_layer_z_offset_mm,
"bed_actual_c": bed_actual_c,
"bed_target_c": bed_target_c,
}
for label, value in inputs.items():
if not math.isfinite(value):
reasons.append(f"{label} must be finite")

if homed is True:
reasons.append("bed adhesion precondition must run before homing")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail closed when homing state is unknown.

homed=None currently behaves like “not homed,” so a caller can omit the new flag and still pass the gate. For a safety precondition that is explicitly meant to guard pre-homing behavior, unknown homing state should be rejected instead of silently accepted.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@03_implementation/src/hermes3d/core/safety/bed_adhesion.py` around lines 62 -
92, The function check_bed_adhesion_precondition currently treats homed=None as
"not homed" and allows the check to pass; change it to fail closed by explicitly
detecting homed is None and appending a rejection reason (e.g., "homing state
unknown") so the precondition gate rejects unknown homing state; locate the
homed parameter use in check_bed_adhesion_precondition and add a branch that
appends the failure reason when homed is None (keep the existing branch that
appends "bed adhesion precondition must run before homing" when homed is True).

@Ghenghis
Ghenghis merged commit 88b731c into develop May 3, 2026
14 checks passed
@Ghenghis
Ghenghis deleted the codex/bed-adhesion-precondition branch May 3, 2026 15:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant