Skip to content

fix: add debug_assert invariant guards to critical code paths#1312

Merged
henrypark133 merged 2 commits intonearai:stagingfrom
nlok5923:fix/debug-assert-invariants-1215
Mar 18, 2026
Merged

fix: add debug_assert invariant guards to critical code paths#1312
henrypark133 merged 2 commits intonearai:stagingfrom
nlok5923:fix/debug-assert-invariants-1215

Conversation

@nlok5923
Copy link
Copy Markdown
Contributor

@nlok5923 nlok5923 commented Mar 17, 2026

Closes #1215

Summary

  • src/tools/execute.rs: debug_assert!(!tool_name.is_empty()) at entry of execute_tool_with_safety — catches callers passing empty tool name before a confusing NotFound error
  • src/context/state.rs: debug_assert!(self.state.can_transition_to(new_state)) before the graceful error return in transition_to — catches state machine violations in debug builds
  • src/llm/circuit_breaker.rs: debug_assert!(false, "BUG: record_success() called while circuit breaker is Open …") in the CircuitState::Open arm of record_success — catches cases where check_allowed() was bypassed

All three use debug_assert! so they compile away in release builds with zero runtime cost.

Test plan

  • cargo clippy --all --all-features — zero warnings ✅
  • cargo fmt — clean ✅
  • cargo test — existing tests pass
  • No new .unwrap() / .expect() / assert! in src/ production paths

…earai#1215)

Add three debug_assert! calls to catch impossible-in-correct-code states
early in debug builds without affecting release performance:

- execute_tool_with_safety: assert tool_name is non-empty at entry
- JobContext::transition_to: assert state machine transition is valid
- CircuitBreakerProvider::record_success: assert circuit is not Open
  (check_allowed() must gate all calls before record_success())

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions github-actions Bot added scope: llm LLM integration size: S 10-49 changed lines risk: medium Business logic, config, or moderate-risk modules labels Mar 17, 2026
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the robustness of the application by integrating debug_assert! statements into critical code paths. These assertions act as invariant guards, designed to catch logical errors and state inconsistencies early during development and testing, without incurring any runtime cost in release builds.

Highlights

  • Tool Execution Safety: Added a debug_assert!(!tool_name.is_empty()) guard at the entry of execute_tool_with_safety in src/tools/execute.rs to catch callers passing empty tool names, preventing confusing NotFound errors.
  • State Machine Invariant: Introduced debug_assert!(self.state.can_transition_to(new_state)) in src/context/state.rs before the graceful error return in transition_to, which will catch state machine violations in debug builds.
  • Circuit Breaker Logic: Implemented debug_assert!(false, "BUG: record_success() called while circuit breaker is Open …") within the CircuitState::Open arm of record_success in src/llm/circuit_breaker.rs to catch cases where check_allowed() was bypassed.
Changelog
  • src/context/state.rs
    • Added a debug_assert! to ensure that job state transitions are valid before proceeding, providing early detection of state machine violations.
  • src/llm/circuit_breaker.rs
    • Introduced a debug_assert! to flag instances where record_success() is called while the circuit breaker is in an Open state, indicating a potential bypass of the check_allowed() mechanism.
  • src/tools/execute.rs
    • Included a debug_assert! to verify that the tool_name is not empty when execute_tool_with_safety is invoked, preventing subsequent errors caused by invalid input.
Activity
  • No human activity has been recorded on this pull request yet.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@github-actions github-actions Bot added the contributor: experienced 6-19 merged PRs label Mar 17, 2026
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request introduces several debug_assert! statements across different modules to proactively identify potential bugs. Specifically, assertions were added to JobContext::transition_to to catch invalid state transitions, to CircuitBreakerProvider::record_success to detect incorrect circuit breaker usage when the state is 'Open', and to execute_tool_with_safety to ensure the tool_name is not empty. These changes enhance the robustness and debuggability of the codebase by catching unexpected conditions during development.

Covers the debug_assert!(!tool_name.is_empty()) added in execute_tool_with_safety.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@henrypark133 henrypark133 left a comment

Choose a reason for hiding this comment

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

Review: Add debug_assert invariant guards to critical code paths

More comprehensive than the competing #1354 — covers three critical paths instead of two, with a regression test.

Positives:

  • execute_tool_with_safety: empty tool_name assert catches caller bugs early
  • CircuitBreakerProvider::record_success: Open-state assert includes model_name() for better diagnostics
  • JobContext::transition_to: state machine transition assert — unique to this PR and valuable for catching invalid transitions in debug builds
  • test_execute_empty_tool_name_returns_not_found regression test provides runtime coverage
  • All assertions are debug-only (zero cost in release), with graceful fallback behavior preserved

We're closing the competing #1354 in favor of this PR since it covers more ground.

LGTM.

@henrypark133 henrypark133 merged commit 07e6e30 into nearai:staging Mar 18, 2026
14 checks passed
henrypark133 added a commit that referenced this pull request Mar 18, 2026
Two debug_assert! calls added in #1312 fire on expected runtime error
paths (not programmer bugs), turning graceful error returns into panics
in debug/test builds:

- state.rs: Completed→Cancelled is a user-facing error handled by
  transition_to() returning Err — not a bug
- execute.rs: empty tool_name from malformed LLM output is handled by
  ToolError::NotFound — not a bug

Removes both asserts; keeps the circuit-breaker assert (genuinely guards
a caller invariant).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
henrypark133 added a commit that referenced this pull request Mar 19, 2026
* fix: remove debug_assert guards that panic on valid error paths (#1312)

Two debug_assert! calls added in #1312 fire on expected runtime error
paths (not programmer bugs), turning graceful error returns into panics
in debug/test builds:

- state.rs: Completed→Cancelled is a user-facing error handled by
  transition_to() returning Err — not a bug
- execute.rs: empty tool_name from malformed LLM output is handled by
  ToolError::NotFound — not a bug

Removes both asserts; keeps the circuit-breaker assert (genuinely guards
a caller invariant).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: tighten empty tool name test to assert ToolError::NotFound variant

Address review feedback: assert the specific error variant instead of
just is_err() so the regression test actually enforces the expected
error path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: cargo fmt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jr42 pushed a commit to jr42/ironclaw that referenced this pull request Mar 22, 2026
…ai#1385)

* fix: remove debug_assert guards that panic on valid error paths (nearai#1312)

Two debug_assert! calls added in nearai#1312 fire on expected runtime error
paths (not programmer bugs), turning graceful error returns into panics
in debug/test builds:

- state.rs: Completed→Cancelled is a user-facing error handled by
  transition_to() returning Err — not a bug
- execute.rs: empty tool_name from malformed LLM output is handled by
  ToolError::NotFound — not a bug

Removes both asserts; keeps the circuit-breaker assert (genuinely guards
a caller invariant).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: tighten empty tool name test to assert ToolError::NotFound variant

Address review feedback: assert the specific error variant instead of
just is_err() so the regression test actually enforces the expected
error path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: cargo fmt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jr42 pushed a commit to jr42/ironclaw that referenced this pull request Mar 22, 2026
…ai#1385)

* fix: remove debug_assert guards that panic on valid error paths (nearai#1312)

Two debug_assert! calls added in nearai#1312 fire on expected runtime error
paths (not programmer bugs), turning graceful error returns into panics
in debug/test builds:

- state.rs: Completed→Cancelled is a user-facing error handled by
  transition_to() returning Err — not a bug
- execute.rs: empty tool_name from malformed LLM output is handled by
  ToolError::NotFound — not a bug

Removes both asserts; keeps the circuit-breaker assert (genuinely guards
a caller invariant).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: tighten empty tool name test to assert ToolError::NotFound variant

Address review feedback: assert the specific error variant instead of
just is_err() so the regression test actually enforces the expected
error path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: cargo fmt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
bkutasi pushed a commit to bkutasi/ironclaw that referenced this pull request Mar 28, 2026
…#1312)

* fix: add debug_assert invariant guards to critical code paths (closes nearai#1215)

Add three debug_assert! calls to catch impossible-in-correct-code states
early in debug builds without affecting release performance:

- execute_tool_with_safety: assert tool_name is non-empty at entry
- JobContext::transition_to: assert state machine transition is valid
- CircuitBreakerProvider::record_success: assert circuit is not Open
  (check_allowed() must gate all calls before record_success())

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add regression test for empty tool name invariant guard

Covers the debug_assert!(!tool_name.is_empty()) added in execute_tool_with_safety.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
bkutasi pushed a commit to bkutasi/ironclaw that referenced this pull request Mar 28, 2026
…ai#1385)

* fix: remove debug_assert guards that panic on valid error paths (nearai#1312)

Two debug_assert! calls added in nearai#1312 fire on expected runtime error
paths (not programmer bugs), turning graceful error returns into panics
in debug/test builds:

- state.rs: Completed→Cancelled is a user-facing error handled by
  transition_to() returning Err — not a bug
- execute.rs: empty tool_name from malformed LLM output is handled by
  ToolError::NotFound — not a bug

Removes both asserts; keeps the circuit-breaker assert (genuinely guards
a caller invariant).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: tighten empty tool name test to assert ToolError::NotFound variant

Address review feedback: assert the specific error variant instead of
just is_err() so the regression test actually enforces the expected
error path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: cargo fmt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
drchirag1991 pushed a commit to drchirag1991/ironclaw that referenced this pull request Apr 8, 2026
…#1312)

* fix: add debug_assert invariant guards to critical code paths (closes nearai#1215)

Add three debug_assert! calls to catch impossible-in-correct-code states
early in debug builds without affecting release performance:

- execute_tool_with_safety: assert tool_name is non-empty at entry
- JobContext::transition_to: assert state machine transition is valid
- CircuitBreakerProvider::record_success: assert circuit is not Open
  (check_allowed() must gate all calls before record_success())

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: add regression test for empty tool name invariant guard

Covers the debug_assert!(!tool_name.is_empty()) added in execute_tool_with_safety.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
drchirag1991 pushed a commit to drchirag1991/ironclaw that referenced this pull request Apr 8, 2026
…ai#1385)

* fix: remove debug_assert guards that panic on valid error paths (nearai#1312)

Two debug_assert! calls added in nearai#1312 fire on expected runtime error
paths (not programmer bugs), turning graceful error returns into panics
in debug/test builds:

- state.rs: Completed→Cancelled is a user-facing error handled by
  transition_to() returning Err — not a bug
- execute.rs: empty tool_name from malformed LLM output is handled by
  ToolError::NotFound — not a bug

Removes both asserts; keeps the circuit-breaker assert (genuinely guards
a caller invariant).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix: tighten empty tool name test to assert ToolError::NotFound variant

Address review feedback: assert the specific error variant instead of
just is_err() so the regression test actually enforces the expected
error path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* style: cargo fmt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: experienced 6-19 merged PRs risk: medium Business logic, config, or moderate-risk modules scope: llm LLM integration size: S 10-49 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add debug_assert invariant assertions to critical code paths

2 participants