fix: add debug_assert invariant guards to critical code paths#1312
fix: add debug_assert invariant guards to critical code paths#1312henrypark133 merged 2 commits intonearai:stagingfrom
Conversation
…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>
Summary of ChangesHello, 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 Highlights
Changelog
Activity
Using Gemini Code AssistThe 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
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 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
|
There was a problem hiding this comment.
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>
henrypark133
left a comment
There was a problem hiding this comment.
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 earlyCircuitBreakerProvider::record_success: Open-state assert includesmodel_name()for better diagnosticsJobContext::transition_to: state machine transition assert — unique to this PR and valuable for catching invalid transitions in debug buildstest_execute_empty_tool_name_returns_not_foundregression 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.
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: 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>
…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>
…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>
…#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>
…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>
…#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>
…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>
Closes #1215
Summary
src/tools/execute.rs:debug_assert!(!tool_name.is_empty())at entry ofexecute_tool_with_safety— catches callers passing empty tool name before a confusingNotFounderrorsrc/context/state.rs:debug_assert!(self.state.can_transition_to(new_state))before the graceful error return intransition_to— catches state machine violations in debug buildssrc/llm/circuit_breaker.rs:debug_assert!(false, "BUG: record_success() called while circuit breaker is Open …")in theCircuitState::Openarm ofrecord_success— catches cases wherecheck_allowed()was bypassedAll 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.unwrap()/.expect()/assert!insrc/production paths