fix(boundary): reset warned_sessions on configure_session_budget #8284

Open
HAL9000 wants to merge 5 commits from fix/boundary-cost-budget-warning-re-trigger-7525 into master
5 changed files with 63 additions and 1 deletions
+10 -1
View File
@@ -906,6 +906,15 @@ uko-oo:Class` triple emission in `PythonAnalyzer._extract_class()` so that
limit. 19 BDD scenarios cover all v3 paths including tool actors,
update mode, LSP dict bindings, and field propagation.
- **Budget Warning Re-trigger After Session Reconfiguration** (#7525): Fixed a bug
where `CostBudgetService._check_warning()` would never re-fire after a session's
budget was reconfigured via `configure_session_budget()`. The root cause was that
`configure_session_budget()` did not clear `_warned_sessions` when updating
`max_cost_usd`, so the session remained permanently suppressed. Fix: added
`self._warned_sessions.discard(session_id)` inside the `with self._lock:` block
at the end of `configure_session_budget()`, so warning state is reset on every
reconfiguration.
- **TDD Non-AssertionError Guard Visibility** (#8294): `apply_tdd_inversion` in
`features/environment.py` now emits its non-assertion exception guard warning to
both the structured logger and `stderr` via a new `_warning_with_stderr` helper.
@@ -1407,4 +1416,4 @@ iteration` and data corruption under concurrent plan execution. All public
- **TUI -- Permission Question Widget**: A new inline `PermissionQuestionWidget`
renders permission requests directly in the conversation stream for single-key
operations. Users can allow/reject with single-key shortcuts (`a`/`A`/`r`/`R`),
navigate with arrow keys, confirm with `Enter`, or press `v` to open the full
navigate with arrow keys, confirm with `Enter`, or press `v` to open the full
+1
View File
@@ -139,3 +139,4 @@ Below are some specific details of individual PR contributions.
* HAL 9000 has contributed the plan tree JSON/YAML spec-compliant envelope fix (issue #11041): wrapped `agents plan tree` JSON and YAML output in the spec-required command envelope (`command`, `status`, `exit_code`, `data`, `timing`, `messages`), updated BDD step definitions to validate envelope structure, and removed the `@tdd_expected_fail` tag from the previously-failing JSON tree format test (issue #4254).
* HAL 9000 has contributed the a2a session_id validation fix (PR #11098 / issue #9250): moved the session_id validation guard to the top of `_handle_session_close()` in `A2aLocalFacade`, closing the validation bypass path where empty or null session IDs could slip through to devcontainer cleanup when `SessionService` was not wired.
* HAL 9000 has contributed ACMS budget enforcement for per-file and cumulative size constraints (PR #9673 / issue #9583): implemented ``BudgetEnforcer``, ``BudgetViolation``, and ``ContextFile`` dataclasses in ``src/cleveragents/acms/budget_enforcement.py`` with full type annotations, ruff linting compliance, 11 BDD Behave scenarios, Robot Framework integration tests, and per-file exclusion + cumulative budget cutoff strategies for max_file_size and max_total_size limits.
* HAL 9000 has contributed the budget warning re-trigger fix (#7525): added `_warned_sessions.discard(session_id)` inside the lock in `configure_session_budget()` so budget warnings can re-fire after session reconfiguration.
+43
View File
@@ -479,3 +479,46 @@ Feature: Per-Session and Per-Org Cost Budgets
Scenario: BudgetCheckResult model is frozen
When I create a budget check result allowed=true
Then modifying the budget check result should raise an error
# ---- Bug fix: warning re-triggers after budget reconfiguration (#7525) ----
Scenario: Budget warning re-fires after max_cost_usd is increased
Given a cost budget service with mock event bus and warning threshold 0.8
And session "reconf1" configured with max_cost_usd 100.0
When I record plan cost 85.0 for session "reconf1"
And I check budget hierarchy for session "reconf1" with plan_cost 1.0
Then the mock event bus should have received exactly 1 BUDGET_WARNING event
When I reconfigure session "reconf1" with max_cost_usd 200.0
And I check budget hierarchy for session "reconf1" with plan_cost 1.0
Then the mock event bus should have received exactly 1 BUDGET_WARNING event
Scenario: Budget warning fires again once reconfigured budget threshold is crossed
Given a cost budget service with mock event bus and warning threshold 0.8
And session "reconf2" configured with max_cost_usd 100.0
When I record plan cost 85.0 for session "reconf2"
And I check budget hierarchy for session "reconf2" with plan_cost 1.0
Then the mock event bus should have received exactly 1 BUDGET_WARNING event
When I reconfigure session "reconf2" with max_cost_usd 200.0
And I record plan cost 80.0 for session "reconf2"
And I check budget hierarchy for session "reconf2" with plan_cost 1.0
Then the mock event bus should have received exactly 2 BUDGET_WARNING event
Scenario: Budget warning state is cleared on reconfigure even without new cost
Given a cost budget service with mock event bus and warning threshold 0.5
And session "reconf3" configured with max_cost_usd 100.0
When I record plan cost 60.0 for session "reconf3"
And I check budget hierarchy for session "reconf3" with plan_cost 1.0
Then the mock event bus should have received exactly 1 BUDGET_WARNING event
When I reconfigure session "reconf3" with max_cost_usd 80.0
And I check budget hierarchy for session "reconf3" with plan_cost 1.0
Then the mock event bus should have received exactly 2 BUDGET_WARNING event
Scenario: Budget warning does not re-fire if reconfigured budget threshold not yet crossed
Given a cost budget service with mock event bus and warning threshold 0.8
And session "reconf4" configured with max_cost_usd 100.0
When I record plan cost 85.0 for session "reconf4"
And I check budget hierarchy for session "reconf4" with plan_cost 1.0
Then the mock event bus should have received exactly 1 BUDGET_WARNING event
When I reconfigure session "reconf4" with max_cost_usd 1000.0
And I check budget hierarchy for session "reconf4" with plan_cost 1.0
Then the mock event bus should have received exactly 1 BUDGET_WARNING event
+5
View File
1
@@ -690,6 +690,11 @@ def step_check_warning_event_count(context: Context, count: int) -> None:
assert actual == count
@when('I reconfigure session "{sid}" with max_cost_usd {value:g}')
def step_reconfigure_session(context: Context, sid: str, value: float) -> None:
context.budget_service.configure_session_budget(sid, max_cost_usd=value)
# ---------------------------------------------------------------------------
# AutonomyGuardrailService integration steps
# ---------------------------------------------------------------------------
@@ -106,6 +106,10 @@ class CostBudgetService:
)
if org_id is not None:
self._session_org[session_id] = org_id
# Reset warning state so the warning can re-fire against the new
# budget cap. Without this, a session that already triggered a
# warning would never warn again even after the budget is raised.
self._warned_sessions.discard(session_id)
def get_session_budget(self, session_id: str) -> SessionCostBudget | None:
"""Return the budget for *session_id*, or ``None``.