Skip to content

feat(cost): productize budget governance across runtime surfaces#449

Merged
yacosta738 merged 7 commits intomainfrom
feature/dallay-164-cost-governance-productization
Apr 7, 2026
Merged

feat(cost): productize budget governance across runtime surfaces#449
yacosta738 merged 7 commits intomainfrom
feature/dallay-164-cost-governance-productization

Conversation

@yacosta738
Copy link
Copy Markdown
Contributor

@yacosta738 yacosta738 commented Apr 6, 2026

Define and implement the Corvus cost-governance model across runtime, CLI, gateway/admin, dashboard, observability, and OpenSpec. Add explicit session/day/month/mission scope evaluation, override and audit flows, machine-readable budget errors, dashboard visibility, and archive the completed OpenSpec change after PASS WITH WARNINGS verify.

Summary

Productize Corvus cost governance across runtime surfaces by completing the end-to-end budget model, enforcement flow, operator controls, observability, and dashboard visibility.
This change turns cost governance from partial runtime foundations into a real product capability with explicit session/day/month/mission scope evaluation, override/audit flow, machine-readable budget failures, and aligned CLI, gateway/admin, dashboard, and OpenSpec artifacts.

What changed

Runtime cost governance

  • Added a dedicated CostService over CostTracker
  • Added explicit budget evaluation for:
    • session
    • daily
    • monthly
    • mission
  • Return governing scope/result consistently across metered calls
  • Added reset, history, override, and audit-oriented runtime DTOs
  • Enforced mission budget as runtime-derived scope instead of separate mission-local truth

CLI

  • Added:
    • corvus cost summary
    • corvus cost history
    • corvus cost reset
  • Added --override-budget to corvus agent and corvus code
  • Added session exit summaries for CLI runs
  • Added machine-readable budget failure handling on CLI paths where relevant

Gateway / Admin API

  • Added:
    • /web/cost/summary
    • /web/cost/history
    • /web/admin/cost/reset
    • /web/admin/cost/override
    • matching /api/web/... routes
  • Switched gateway cost handlers to shared runtime-owned cost state
  • Added machine-readable budget_exceeded responses
  • Extended admin config patching for cost fields
  • Normalized deprecated autonomy alias handling

Dashboard

  • Upgraded cost UI from config-only view to live governance panel
  • Added cost governance composable
  • Added live summary/history rendering
  • Added warning/exceeded state display
  • Added operator actions for reset / next-request override
  • Added locale strings and dashboard tests for cost-governance behavior

Observability

  • Added first-class observer events:
    • BudgetWarning
    • BudgetExceeded
    • BudgetOverride
  • Updated log / OTel / Prometheus reporting
  • Replaced ad hoc cost warning/error emission with structured lifecycle events

Governance cleanup

  • Deprecated misleading autonomy field naming:
    • old: max_cost_per_day_cents
    • canonical: max_actions_per_hour
  • Kept one-release compatibility alias and deprecation metadata
  • Synced/archived OpenSpec change and promoted the main cost-governance spec

Validation

Runtime

  • cargo fmt --all -- --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test

Dashboard changed-area validation

  • targeted cost Vitest specs ✅
  • targeted cost-file Biome checks ✅

Notes

  • broader dashboard package warnings still exist outside the changed cost-governance slice and were already present as unrelated branch noise
  • OpenSpec verify result: PASS WITH WARNINGS
  • Archived change after verify:
    • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization

OpenSpec

  • Main spec synced to:
    • openspec/specs/cost-governance/spec.md

Closes: #260

Define and implement the Corvus cost-governance model across runtime,
CLI, gateway/admin, dashboard, observability, and OpenSpec.
Add explicit session/day/month/mission scope evaluation, override and
audit flows, machine-readable budget errors, dashboard visibility, and
archive the completed OpenSpec change after PASS WITH WARNINGS verify.
@linear
Copy link
Copy Markdown

linear bot commented Apr 6, 2026

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds productized cost-governance across runtime, gateway, CLI, dashboard, and observability: new CostService/CostTracker APIs (reservations, overrides, snapshots, history, reset), mission-scoped budgeting, pre-flight evaluation, auditable overrides, admin cost endpoints, CLI commands, dashboard composable, and budget observer events/metrics.

Changes

Cohort / File(s) Summary
Agent & Mission
clients/agent-runtime/src/agent/agent.rs, clients/agent-runtime/src/agent/mission.rs
Introduced mission-scoped budgeting lifecycle, Agent helpers for budget events/summaries, switched pre-flight gating to CostService::evaluate_request, and changed reservation commit/release semantics. Mission accounting supports local and runtime-derived deltas.
Cost Core (types/service/tracker)
clients/agent-runtime/src/cost/types.rs, clients/agent-runtime/src/cost/service.rs, clients/agent-runtime/src/cost/tracker.rs, clients/agent-runtime/src/cost/mod.rs
Added budgeting/gov types, CostService facade (enabled/disabled), expanded CostTracker with snapshots, reservations (reserve/release/commit), overrides (apply/consume/expire), audit persistence, reset/history APIs, and concurrent config/state.
Gateway & Admin APIs
clients/agent-runtime/src/gateway/cost.rs, clients/agent-runtime/src/gateway/admin.rs, clients/agent-runtime/src/gateway/mod.rs, clients/agent-runtime/src/gateway/webhook_dispatch.rs
New admin cost endpoints (summary, history, reset, override), admin cost patch handling, wired cost tracker into dispatcher, machine-readable BudgetExceeded outcome and HTTP/SSE mappings, and routing constraints when governance requires dispatcher.
Bootstrap, CLI & Tools
clients/agent-runtime/src/bootstrap/mod.rs, clients/agent-runtime/src/main.rs, clients/agent-runtime/src/tools/delegate.rs
Bootstrap accepts optional cost-tracker override; CLI adds cost subcommands and --override-budget; delegate maps cost/iteration budget errors to budget-exceeded session statuses.
Config / Schema / Policy
clients/agent-runtime/src/config/schema.rs, clients/agent-runtime/src/config/mod.rs, clients/agent-runtime/src/security/policy.rs
Added CostConfig.session_limit_usd, deny-unknown-fields hardening, deprecated autonomy alias handling (removed max_cost_per_day_cents), emit deprecation warnings, and added cost validation in runtime path; separated action-rate naming from token-spend.
Observability (traits/log/otel/prometheus)
clients/agent-runtime/src/observability/traits.rs, clients/agent-runtime/src/observability/log.rs, clients/agent-runtime/src/observability/otel.rs, clients/agent-runtime/src/observability/prometheus.rs, clients/agent-runtime/src/observability/mod.rs
Added budget event types and redaction helpers; observer now emits BudgetWarning/BudgetExceeded/BudgetOverride events; OTEL spans/attributes, Prometheus counters/gauge, and logging formatter added.
Web Dashboard (types/composables/components/tests)
clients/web/apps/dashboard/src/types/admin-config.ts, clients/web/apps/dashboard/src/composables/useAdmin.ts, clients/web/apps/dashboard/src/composables/useCostGovernance.ts, clients/web/apps/dashboard/src/composables/useCostGovernance.spec.ts, clients/web/apps/dashboard/src/components/config/CostOverview.vue, clients/web/apps/dashboard/src/components/config/CostOverview.spec.ts
Added useCostGovernance composable, admin cost API functions, new types for summary/history/reset/override, refactored CostOverview to use composable, and updated tests/UI flows.
Localization & Docs
clients/web/packages/locales/src/en.json, clients/web/packages/locales/src/es.json, openspec/...cost-governance-productization/*, openspec/specs/cost-governance/spec.md
Added i18n keys and comprehensive spec/design/tasks/verify documentation for cost-governance productization.
Tests / Integration updates
clients/agent-runtime/tests/admin_config_api_integration.rs, various tests across modules
Updated and added tests for budget evaluation/reservations/overrides, mission-scope handling, deprecation behavior, admin endpoints, observability, and CLI flows; test scaffolding adjusted for optional cost_tracker.

Sequence Diagram(s)

sequenceDiagram
    participant Agent
    participant CostService
    participant CostTracker
    participant Observer

    Agent->>CostService: evaluate_request(estimated_usd, mission_scope, now)
    CostService->>CostTracker: reserve_budget_for_request(...)
    CostTracker-->>CostService: (BudgetCheck, Option<override>, Option<reservation>)
    alt Blocked (Exceeded without override)
        CostService-->>Agent: BudgetEvaluation::Blocked{check}
        Agent->>Observer: record_event(BudgetExceeded)
    else Proceed (Allowed or Warning or Exceeded with override)
        CostService-->>Agent: BudgetEvaluation::Proceed{check, override_applied, reservation}
        alt Warning
            Agent->>Observer: record_event(BudgetWarning)
        end
        Agent->>CostService: commit_budget_reservation(reservation_id) on success
        CostService->>CostTracker: commit_budget_reservation(reservation_id)
        opt provider call failed
            Agent->>CostService: release_budget_reservation(reservation_id)
            CostService->>CostTracker: release_budget_reservation(reservation_id)
        end
    end
Loading
sequenceDiagram
    participant Gateway
    participant CostService
    participant CostTracker
    participant Storage
    participant Observer

    Gateway->>CostService: apply_override(request, now, actor)
    CostService->>CostTracker: apply_override(request, now)
    CostTracker->>Storage: append_audit_event(OverrideGranted)
    CostTracker-->>CostService: CostOverrideRecord
    CostService-->>Gateway: return override record
    Gateway->>Observer: record_event(BudgetOverride)
    Note over CostTracker,Gateway: Later evaluation may consume override
    Gateway->>CostService: evaluate_request(estimated_usd, None, now)
    CostService->>CostTracker: reserve_budget_for_request(...)
    CostTracker-->>CostService: (Exceeded, Some(override), Some(reservation))
    CostService-->>CostTracker: consume_override_if_active(now)
    CostTracker->>Storage: append_audit_event(OverrideConsumed)
    CostService-->>Gateway: Proceed with override_applied
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~110 minutes

Possibly related PRs

Suggested labels

area:rust, area:web, area:docs, risk:high, risk:security

Suggested reviewers

  • yuniel-acosta
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.54% 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 Title 'feat(cost): productize budget governance across runtime surfaces' follows Conventional Commit style with 'feat' prefix, clear scope, and concise description within 72 characters.
Description check ✅ Passed Description covers summary, detailed changes across runtime/CLI/gateway/dashboard/observability/governance, validation steps, and links to issue #260 with clear acceptance criteria alignment.
Linked Issues check ✅ Passed All three acceptance criteria from #260 are met: cost-governance model is explicit (defined in spec and runtime), surfaces are productized (CLI/gateway/dashboard), and implementation is complete with integrated end-to-end behavior.
Out of Scope Changes check ✅ Passed All changes align with the cost-governance productization objective; no unrelated or scope-creeping modifications were detected across runtime, CLI, gateway, dashboard, observability, and OpenSpec artifacts.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/dallay-164-cost-governance-productization

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 6, 2026

✅ Contributor Report

User: @yacosta738
Status: Passed (12/13 metrics passed)

Metric Description Value Threshold Status
PR Merge Rate PRs merged vs closed 89% >= 30%
Repo Quality Repos with ≥100 stars 0 >= 0
Positive Reactions Positive reactions received 10 >= 1
Negative Reactions Negative reactions received 0 <= 5
Account Age GitHub account age 3083 days >= 30 days
Activity Consistency Regular activity over time 108% >= 0%
Issue Engagement Issues with community engagement 0 >= 0
Code Reviews Code reviews given to others 526 >= 0
Merger Diversity Unique maintainers who merged PRs 2 >= 0
Repo History Merge Rate Merge rate in this repo 91% >= 0%
Repo History Min PRs Previous PRs in this repo 204 >= 0
Profile Completeness Profile richness (bio, followers) 90 >= 0
Suspicious Patterns Spam-like activity detection 1 N/A

Contributor Report evaluates based on public GitHub activity. Analysis period: 2025-04-07 to 2026-04-07

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 18

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
clients/agent-runtime/src/observability/prometheus.rs (1)

226-242: ⚠️ Potential issue | 🟠 Major

Reset cost_usd_last when AgentEnd has no cost.

This gauge only updates on Some, so a metered request followed by an unmetered or unknown-cost completion still exposes the previous value as the "last" request cost. Update it on every AgentEnd (or pair it with an explicit availability metric) so scrapes never carry stale spend forward.

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

In `@clients/agent-runtime/src/observability/prometheus.rs` around lines 226 -
242, ObserverEvent::AgentEnd currently only updates self.cost_usd_last when
cost_usd is Some, which leaves the previous value exposed on scrapes for
unmetered/unknown completions; update the handler so it always updates
self.cost_usd_last on every AgentEnd — when cost_usd is Some call
self.cost_usd_last.set(*cost_usd) and when None reset it (e.g.,
self.cost_usd_last.set(0.0) or another sentinel you prefer), or alternatively
add/emit an explicit availability metric; touch the ObserverEvent::AgentEnd
match arm and the cost_usd_last gauge usage to implement this behavior.
clients/agent-runtime/src/gateway/mod.rs (1)

1870-1884: ⚠️ Potential issue | 🟠 Major

Disabling the dispatcher currently disables cost governance too.

state.cost_tracker is only threaded into webhook_dispatch::execute(). The legacy /webhook and /web/chat/stream branches still go straight to simple_chat(), so flipping webhook_dispatcher_enabled bypasses budget checks/counting on both gateway surfaces. If legacy must remain, reject those branches while config.cost.enabled or run the same budget preflight/recording before invoking the provider.

As per coding guidelines: **/*: Security first, performance second. Look for behavioral regressions, missing tests, and contract breaks across modules.

Also applies to: 2002-2094

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

In `@clients/agent-runtime/src/gateway/mod.rs` around lines 1870 - 1884, The
comment notes that threading state.cost_tracker only into
webhook_dispatch::execute causes disabling the webhook dispatcher to bypass cost
governance for legacy paths that call simple_chat(); fix by ensuring cost
preflight and recording run regardless of dispatcher: either (a) when
config.cost.enabled is true, perform the same budget preflight and post-call
recording using the cost-tracker APIs (the same logic used inside
webhook_dispatch::execute) before invoking simple_chat(), or (b) if you choose
to keep legacy simple_chat() untouched, explicitly reject those branches when
config.cost.enabled is true (e.g., return an error when
webhook_dispatcher_enabled is false but config.cost.enabled is true). Locate
usages of state.cost_tracker, webhook_dispatch::execute, simple_chat(), and the
webhook_dispatcher_enabled/config.cost.enabled flags to implement the consistent
behavior.
clients/agent-runtime/src/agent/agent.rs (1)

1071-1096: ⚠️ Potential issue | 🟠 Major

Fail closed on any Blocked evaluation.

The catch-all BudgetEvaluation::Blocked { .. } => Ok(()) lets execution continue if the service ever returns a blocked shape that does not match the exact BudgetCheck::Exceeded pattern above. This is a governance gate; the fallback must return an error, not allow the turn to proceed.

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

In `@clients/agent-runtime/src/agent/agent.rs` around lines 1071 - 1096, The
catch-all arm BudgetEvaluation::Blocked { .. } currently returns Ok(()) which
lets execution continue for any blocked evaluation; change it to fail closed by
returning an Err instead. Replace the fallback arm so it returns an error (e.g.,
Err(AgentExecutionError::CostBudgetBlocked { /* fields as appropriate */
}.into()) or, if you prefer existing semantics, construct and return
AgentExecutionError::CostBudgetExceeded/appropriate variant), and optionally
emit a corresponding event (similar to emit_budget_exceeded_event) to record the
blocked state; ensure you reference BudgetEvaluation::Blocked and
emit_budget_exceeded_event/AgentExecutionError variants when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/agent-runtime/src/agent/agent.rs`:
- Around line 456-499: The budget event helpers emit the shared CostTracker
session id (tracker.session_id()), which collapses telemetry for concurrent
webhook turns; update emit_budget_warning_event and emit_budget_exceeded_event
to use the active turn's session id (the TurnContext::session_id() for the
current request) when available, falling back to tracker.session_id() only if
there is no active turn context; keep the rest of the BudgetThresholdEvent
fields (period, current_usd, projected_usd, limit_usd, percent_used, surface via
self.budget_surface(), and observer.record_event) unchanged.
- Around line 2013-2020: The code calls begin_active_mission_budget() but may
return early (e.g., when build_mission_coordinator() fails) without calling
end_active_mission_budget(), leaking the active mission scope; wrap the lifetime
of the mission budget with a scope guard or explicit cleanup to always call
end_active_mission_budget() on all exit paths—either create an RAII guard type
(e.g., ActiveMissionBudgetGuard that calls self.end_active_mission_budget() in
Drop and instantiate it immediately after begin_active_mission_budget()), or
ensure every early return (including errors from build_mission_coordinator() and
others) calls end_active_mission_budget() before returning; reference
begin_active_mission_budget, end_active_mission_budget,
build_mission_coordinator, and run_mission_plan to locate the code to change.

In `@clients/agent-runtime/src/agent/mission.rs`:
- Around line 394-400: The RuntimeDerived branch in MissionCostAccounting (the
code using baseline_cost_cents and session_cost_reader) currently uses
saturating_sub which turns regressions into 0 and effectively reopens the
mission budget; change the logic to detect a regression (when current_cost_cents
< *baseline_cost_cents) and return an explicit error (e.g., map
checked_sub(None) to a MissionCost/Runtime error) instead of saturating to 0 so
callers can handle a tracker reset, and add a regression test that simulates the
shared reset flow (calling session_cost_reader that returns a value below
baseline) asserting the mission does not regain budget and that the error path
is taken.

In `@clients/agent-runtime/src/bootstrap/mod.rs`:
- Around line 262-264: The code currently applies cost_tracker_override before
checking config.cost.enabled so a shared tracker can remain active even when
config.cost.enabled is false; change the logic in the bootstrap where
cost_tracker is computed (the let cost_tracker = ... block that references
cost_tracker_override and config.cost.enabled) to first check
config.cost.enabled and only then consider using cost_tracker_override or
constructing/returning the shared tracker, effectively gating overrides behind
config.cost.enabled; also add a regression test that flips config.cost.enabled
to false while a shared tracker exists and asserts the runtime does not
reuse/enforce the shared tracker when disabled.

In `@clients/agent-runtime/src/config/schema.rs`:
- Around line 587-589: The new session_limit_usd (and the other cost fields at
the mentioned locations) can be negative and are not validated at load time; add
a validate_cost_config() function and call it in the same startup validation
pipeline as the existing validators so config loading fails fast on invalid
budgets. In validate_cost_config(), check session_limit_usd (and the other
cost-related fields referenced around lines 627–629 and 643) for non-negative
values and return/propagate a descriptive error if any are negative; reference
the existing default_session_limit symbol when reasoning about defaults and wire
this validator into the config loader/startup validators invocation so invalid
cost config never reaches runtime.

In `@clients/agent-runtime/src/cost/service.rs`:
- Around line 22-40: current_summary() reads tracker.get_summary(),
tracker.scope_statuses(), and tracker.active_override() separately which can mix
concurrent states; change it to read a single atomic tracker snapshot and build
the CostGovernanceSummary from that snapshot. Concretely, replace the three
separate calls with one call to a single snapshot accessor (e.g., implement/use
tracker.snapshot() or tracker.get_atomic_snapshot()) that returns summary,
scope_statuses, and active_override together, then use those fields to compute
active_scope and populate session_id, usage, budget_state, active_period,
scope_statuses, and active_override in current_summary().
- Around line 77-103: evaluate_request currently does a separate check
(tracker.check_budget_with_mission_scope) without reserving funds, causing
TOCTOU races; modify the tracker to expose an atomic method (e.g.,
check_and_reserve_budget or reserve_budget_for_request) that accepts
(estimated_cost_usd, mission_scope.as_ref(), now) and performs an atomic
evaluate-and-reserve/consume returning a reservation handle or
error/BudgetCheck, then change evaluate_request to call that atomic method
instead of check_budget_with_mission_scope so the estimated_cost_usd is reserved
before the provider call; keep existing override handling
(tracker.consume_override_if_active) but ensure overrides are consumed only
after a successful reservation or make the atomic method accept the override
flag and consume atomically to avoid races, and return the reservation token so
callers can commit or release the reservation as needed.

In `@clients/agent-runtime/src/cost/tracker.rs`:
- Around line 611-625: build_history_from_window currently accepts unbounded
window sizes and for UsagePeriod::Day will eagerly materialize one bucket per
day via build_daily_history/build_history_from_range, which can OOM/CPU if
callers pass large windows; before expanding the range, validate and clamp or
reject the incoming window argument (e.g., enforce a MAX_WINDOW_DAYS constant or
return an error) inside build_history_from_window for UsagePeriod::Day and
similarly for UsagePeriod::Month (the block that computes month_offset and calls
shift_month/build_history_from_range), so that oversized windows are either
reduced to a safe maximum or return an early Err("History window is too large")
to prevent eager bucket materialization.

In `@clients/agent-runtime/src/gateway/cost.rs`:
- Around line 87-95: The helper cost_service_from_state currently turns an
absent AppState.cost_tracker into an internal_error (500); instead, make it
return Ok with a disabled/empty CostService so read endpoints (/web/cost/summary
and /web/cost/history) return a disabled payload. Concretely, change the else
branch in cost_service_from_state to return Ok((config,
CostService::disabled())) (or equivalent) rather than Err(internal_error(...));
if CostService::disabled() doesn't exist, add a constructor like
CostService::disabled() or CostService::empty() that produces a service that
returns empty/zero responses while preserving the type, and keep hard failures
(internal_error/CostResponse::Error) only for mutations that require a tracker.

In `@clients/agent-runtime/src/gateway/mod.rs`:
- Around line 1167-1173: The current match silently warns and continues when
CostTracker::new(...) fails, which disables enforcement; change the Err branch
to fail startup when cost enforcement is enabled: in the match that assigns
cost_tracker (using CostTracker::new and config.cost), if config.cost.enabled is
true then propagate/return an error from the enclosing init/startup function
(instead of tracing::warn and returning None); only fall back to tracing::warn
and set None when config.cost.enabled is false. Ensure you reference the same
symbols (CostTracker::new, config.cost.enabled, cost_tracker) so the change
compiles with the function's existing error return type.

In `@clients/agent-runtime/src/main.rs`:
- Around line 1319-1333: The session summary error is currently propagated with
the `?` which can mask a successful agent run; change the logic around `let
summary_result = agent.session_cost_summary(chrono::Utc::now());` and
`print_cli_session_summary(summary_result?, CliSessionSurface::Agent);` so that
you handle `summary_result` explicitly (e.g., match or if let Err(e)) and log or
print the summary error instead of returning it, then always return `run_result`
(the result of `agent.run_single` / `agent.run_interactive`) after calling
`agent.record_agent_end_event(&provider_name, &model_name,
session_start.elapsed())`. Apply the same pattern for the similar block using
`handle_code_command` (lines referenced around `session_cost_summary` and
`print_cli_session_summary`).

In `@clients/agent-runtime/src/observability/traits.rs`:
- Around line 171-180: The BudgetThresholdEvent struct must include the missing
budget_state field so downstream code in otel.rs can access event.budget_state;
add a new field pub budget_state: BudgetState to the BudgetThresholdEvent
definition and then update every place that constructs a BudgetThresholdEvent
(search for BudgetThresholdEvent { ... } or new_BudgetThresholdEvent usage) to
populate budget_state with the appropriate BudgetState value at each emit site;
ensure BudgetState is in scope (import if necessary) so otel.rs can continue to
build budget-warning/exceeded span fields from event.budget_state.

In `@clients/web/apps/dashboard/src/components/config/CostOverview.vue`:
- Around line 105-107: The current computed showActions (used to render the
whole operator panel) incorrectly hides the reset button when
config.value?.allow_override === false; split the visibility into two computed
values: keep showOverrideAction (or rename showActionsOverride) =
hasOperationalData.value && config.value?.allow_override === true to control the
override UI, and add showResetAction = hasOperationalData.value (and any
existing admin/permission checks you already use) to control the reset button;
update the template to use the new computed for each button/panel (replace
usages of showActions for the reset button with showResetAction and keep
override button using showOverrideAction), and add a regression/test asserting
that when allow_override = false the override is hidden but the reset button
remains visible.

In `@clients/web/apps/dashboard/src/composables/useAdmin.ts`:
- Around line 297-339: Both resetCost and grantCostOverride need a 30s
AbortController guard: create an AbortController at the start of each function,
start a timer that calls controller.abort() after 30000ms, pass
controller.signal into the fetch call, and clear the timer in the finally block
so the shared loading flag is not left pinned; reference the resetCost and
grantCostOverride functions and ensure you still set loading/error and rethrow
the original error after converting it to a message.

In `@clients/web/apps/dashboard/src/composables/useCostGovernance.ts`:
- Around line 58-60: The updateDeprecations() function currently just clears
deprecations.value making it a no-op; either implement logic to populate
deprecations.value from the relevant source (for example extract deprecation
warnings from plan/policy state such as plans, policies, or costGovernance
payloads used elsewhere in this composable) by iterating those sources and
pushing structured deprecation entries into deprecations.value, or if the
population is intentionally deferred, replace the empty body with a clear TODO
comment explaining why and what data will be used (e.g., "TODO: populate
deprecations from plans.value / policies.value when backend provides deprecation
metadata") so reviewers know this is intentional; refer to updateDeprecations
and deprecations.value to locate the code.

In `@clients/web/apps/dashboard/src/types/admin-config.ts`:
- Around line 172-175: The AdminUsagePeriod/AdminCostResetScope union is too
narrow for summary.period because mission-scoped budgets can produce period:
"mission"; update the type definitions so the dashboard-usable summary period
includes "mission" (either by adding "mission" to the summary-specific union or
by introducing a new union used by summary) — locate AdminUsagePeriod and
AdminCostResetScope (and the related types around lines ~187-197) and either add
"mission" to the summary-facing type or create a new AdminSummaryPeriod type
that includes "session" | "day" | "month" | "mission" and use that where
summary.period is expected.

In
`@openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md`:
- Around line 316-341: The audit event shapes (BudgetWarning, BudgetExceeded,
BudgetOverride) list sensitive fields (e.g., actor, reason, session_id) without
redaction guidance; update the design.md text around those types to explicitly
state which fields are redacted in observability outputs (for example: actor and
reason are fully redacted, session_id is masked or omitted, and any PII must be
redacted before emitting), and add a short sentence describing the redaction
policy and where to find the redaction implementation or config so operators
know why traces/logs won't show raw values.
- Around line 370-376: Add a short "Rollback & Feature-Flag Strategy" subsection
near the "Testing Strategy" block that specifies: introduce a feature flag to
toggle the new budget evaluation paths (referencing the Agent's budget
enforcement and gateway handlers), a clear rollback procedure for disabling the
feature and reverting config changes, and canary/gradual rollout guidance
(percent-based or scoped-by-workspace) with monitoring/alerting criteria; also
add a brief "Threat/Risk Notes" bullet list covering security, runtime, and
gateway failure modes and what signals would trigger rollback or emergency
patches for Agent, gateway cost endpoints, and admin config changes.

---

Outside diff comments:
In `@clients/agent-runtime/src/agent/agent.rs`:
- Around line 1071-1096: The catch-all arm BudgetEvaluation::Blocked { .. }
currently returns Ok(()) which lets execution continue for any blocked
evaluation; change it to fail closed by returning an Err instead. Replace the
fallback arm so it returns an error (e.g.,
Err(AgentExecutionError::CostBudgetBlocked { /* fields as appropriate */
}.into()) or, if you prefer existing semantics, construct and return
AgentExecutionError::CostBudgetExceeded/appropriate variant), and optionally
emit a corresponding event (similar to emit_budget_exceeded_event) to record the
blocked state; ensure you reference BudgetEvaluation::Blocked and
emit_budget_exceeded_event/AgentExecutionError variants when making the change.

In `@clients/agent-runtime/src/gateway/mod.rs`:
- Around line 1870-1884: The comment notes that threading state.cost_tracker
only into webhook_dispatch::execute causes disabling the webhook dispatcher to
bypass cost governance for legacy paths that call simple_chat(); fix by ensuring
cost preflight and recording run regardless of dispatcher: either (a) when
config.cost.enabled is true, perform the same budget preflight and post-call
recording using the cost-tracker APIs (the same logic used inside
webhook_dispatch::execute) before invoking simple_chat(), or (b) if you choose
to keep legacy simple_chat() untouched, explicitly reject those branches when
config.cost.enabled is true (e.g., return an error when
webhook_dispatcher_enabled is false but config.cost.enabled is true). Locate
usages of state.cost_tracker, webhook_dispatch::execute, simple_chat(), and the
webhook_dispatcher_enabled/config.cost.enabled flags to implement the consistent
behavior.

In `@clients/agent-runtime/src/observability/prometheus.rs`:
- Around line 226-242: ObserverEvent::AgentEnd currently only updates
self.cost_usd_last when cost_usd is Some, which leaves the previous value
exposed on scrapes for unmetered/unknown completions; update the handler so it
always updates self.cost_usd_last on every AgentEnd — when cost_usd is Some call
self.cost_usd_last.set(*cost_usd) and when None reset it (e.g.,
self.cost_usd_last.set(0.0) or another sentinel you prefer), or alternatively
add/emit an explicit availability metric; touch the ObserverEvent::AgentEnd
match arm and the cost_usd_last gauge usage to implement this behavior.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 36c85ae1-3dea-4306-83c7-0c6f77c97ab8

📥 Commits

Reviewing files that changed from the base of the PR and between eb44248 and d5f9b69.

📒 Files selected for processing (38)
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/config/mod.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/cost/tracker.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/gateway/admin.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/observability/otel.rs
  • clients/agent-runtime/src/observability/prometheus.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/security/policy.rs
  • clients/agent-runtime/src/tools/delegate.rs
  • clients/agent-runtime/tests/admin_config_api_integration.rs
  • clients/web/apps/dashboard/src/components/config/CostOverview.spec.ts
  • clients/web/apps/dashboard/src/components/config/CostOverview.vue
  • clients/web/apps/dashboard/src/composables/useAdmin.ts
  • clients/web/apps/dashboard/src/composables/useCostGovernance.spec.ts
  • clients/web/apps/dashboard/src/composables/useCostGovernance.ts
  • clients/web/apps/dashboard/src/types/admin-config.ts
  • clients/web/packages/locales/src/en.json
  • clients/web/packages/locales/src/es.json
  • openspec/changes/2026-04-06-cost-governance-productization/state.yaml
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/proposal.md
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/specs/cost-governance/spec.md
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/state.yaml
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.md
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/verify-report.md
  • openspec/specs/cost-governance/spec.md
💤 Files with no reviewable changes (1)
  • openspec/changes/2026-04-06-cost-governance-productization/state.yaml
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: sonar
  • GitHub Check: pr-checks
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (10)
**/*

⚙️ CodeRabbit configuration file

**/*: Security first, performance second.
Validate input boundaries, auth/authz implications, and secret management.
Look for behavioral regressions, missing tests, and contract breaks across modules.

Files:

  • clients/web/packages/locales/src/en.json
  • clients/agent-runtime/tests/admin_config_api_integration.rs
  • clients/agent-runtime/src/tools/delegate.rs
  • clients/agent-runtime/src/config/mod.rs
  • clients/web/packages/locales/src/es.json
  • clients/web/apps/dashboard/src/composables/useCostGovernance.spec.ts
  • clients/web/apps/dashboard/src/components/config/CostOverview.spec.ts
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/state.yaml
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/verify-report.md
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/security/policy.rs
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.md
  • clients/agent-runtime/src/observability/prometheus.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
  • clients/agent-runtime/src/observability/otel.rs
  • clients/web/apps/dashboard/src/components/config/CostOverview.vue
  • openspec/specs/cost-governance/spec.md
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/specs/cost-governance/spec.md
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/web/apps/dashboard/src/composables/useCostGovernance.ts
  • clients/agent-runtime/src/gateway/admin.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/web/apps/dashboard/src/composables/useAdmin.ts
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/web/apps/dashboard/src/types/admin-config.ts
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/cost/tracker.rs
clients/agent-runtime/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Run cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, and cargo test for code validation, or document which checks were skipped and why

Files:

  • clients/agent-runtime/tests/admin_config_api_integration.rs
  • clients/agent-runtime/src/tools/delegate.rs
  • clients/agent-runtime/src/config/mod.rs
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/security/policy.rs
  • clients/agent-runtime/src/observability/prometheus.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/observability/otel.rs
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/gateway/admin.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/cost/tracker.rs
**/*.rs

⚙️ CodeRabbit configuration file

**/*.rs: Focus on Rust idioms, memory safety, and ownership/borrowing correctness.
Flag unnecessary clones, unchecked panics in production paths, and weak error context.
Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.

Files:

  • clients/agent-runtime/tests/admin_config_api_integration.rs
  • clients/agent-runtime/src/tools/delegate.rs
  • clients/agent-runtime/src/config/mod.rs
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/security/policy.rs
  • clients/agent-runtime/src/observability/prometheus.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/observability/otel.rs
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/gateway/admin.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/cost/tracker.rs
clients/agent-runtime/src/tools/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Implement Tool trait in src/tools/ with strict parameter schema, validate and sanitize all inputs, and return structured ToolResult without panics in runtime path

Files:

  • clients/agent-runtime/src/tools/delegate.rs
clients/agent-runtime/src/{security,gateway,tools}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Treat src/security/, src/gateway/, src/tools/ as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Files:

  • clients/agent-runtime/src/tools/delegate.rs
  • clients/agent-runtime/src/security/policy.rs
  • clients/agent-runtime/src/gateway/admin.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
clients/agent-runtime/src/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

clients/agent-runtime/src/**/*.rs: Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements
Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Files:

  • clients/agent-runtime/src/tools/delegate.rs
  • clients/agent-runtime/src/config/mod.rs
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/security/policy.rs
  • clients/agent-runtime/src/observability/prometheus.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/observability/otel.rs
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/gateway/admin.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/cost/tracker.rs
clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Files:

  • clients/agent-runtime/src/tools/delegate.rs
  • clients/agent-runtime/src/config/mod.rs
  • clients/agent-runtime/src/security/policy.rs
  • clients/agent-runtime/src/gateway/admin.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/config/schema.rs
**/*.{md,mdx}

⚙️ CodeRabbit configuration file

**/*.{md,mdx}: Verify technical accuracy and that docs stay aligned with code changes.
For user-facing docs, check EN/ES parity or explicitly note pending translation gaps.

Files:

  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/verify-report.md
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.md
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
  • openspec/specs/cost-governance/spec.md
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/specs/cost-governance/spec.md
**/*.vue

⚙️ CodeRabbit configuration file

**/*.vue: Enforce Vue 3 Composition API with <script setup>.
Ensure accessibility (A11y) and proper use of Tailwind CSS classes.
Check for proper prop validation and emitted events documentation.

Files:

  • clients/web/apps/dashboard/src/components/config/CostOverview.vue
clients/agent-runtime/src/main.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

clients/agent-runtime/src/main.rs: Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths
Keep startup path lean and avoid heavy initialization in command parsing flow

Files:

  • clients/agent-runtime/src/main.rs
🧠 Learnings (12)
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths

Applied to files:

  • clients/agent-runtime/src/tools/delegate.rs
  • clients/agent-runtime/src/config/mod.rs
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/cost/tracker.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/tools/**/*.rs : Implement `Tool` trait in `src/tools/` with strict parameter schema, validate and sanitize all inputs, and return structured `ToolResult` without panics in runtime path

Applied to files:

  • clients/agent-runtime/src/tools/delegate.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/agent/agent.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/Cargo.toml : Do not add heavy dependencies for minor convenience; justify new crate additions

Applied to files:

  • clients/agent-runtime/src/config/mod.rs
  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/observability/log.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/*.rs : Run `cargo fmt --all -- --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` for code validation, or document which checks were skipped and why

Applied to files:

  • clients/agent-runtime/src/config/mod.rs
  • clients/agent-runtime/src/observability/mod.rs
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.md
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/config/schema.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Include threat/risk notes and rollback strategy for security, runtime, and gateway changes; add or update tests for boundary checks and failure modes

Applied to files:

  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/verify-report.md
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.md
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/**/*.rs : Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Applied to files:

  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/cost/tracker.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/Cargo.toml : Preserve release-size profile assumptions in `Cargo.toml` and avoid adding heavy dependencies unless clearly justified

Applied to files:

  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/main.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools}/**/*.rs : Treat `src/security/`, `src/gateway/`, `src/tools/` as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Applied to files:

  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/security/policy.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/gateway/mod.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Applied to files:

  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/security/policy.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/gateway/admin.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/cost/tracker.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/providers/**/*.rs : Implement `Provider` trait in `src/providers/` and register in `src/providers/mod.rs` factory when adding a new provider

Applied to files:

  • clients/agent-runtime/src/observability/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/agent/agent.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Keep startup path lean and avoid heavy initialization in command parsing flow

Applied to files:

  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/webhook_dispatch.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/**/*.rs : Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements

Applied to files:

  • clients/agent-runtime/src/observability/log.rs
🪛 LanguageTool
openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.md

[style] ~40-~40: It might be better to use ‘month’ with the time-relative pronoun ‘when’. (Alternatively, use ‘in/on which’.)
Context: ...scope is enforced alongside session/day/month where applicable. - [x] 6.3 Update budget eva...

(WHEN_WHERE)

openspec/specs/cost-governance/spec.md

[style] ~30-~30: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...he active UTC calendar day. - monthly MUST represent spend accumulated within the ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~31-~31: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... active UTC calendar month. - mission MUST represent spend accumulated within a si...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/specs/cost-governance/spec.md

[style] ~30-~30: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...he active UTC calendar day. - monthly MUST represent spend accumulated within the ...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)


[style] ~31-~31: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ... active UTC calendar month. - mission MUST represent spend accumulated within a si...

(ENGLISH_WORD_REPEAT_BEGINNING_RULE)

🔇 Additional comments (46)
clients/agent-runtime/src/cost/types.rs (1)

1-313: LGTM!

Well-structured domain types for budget governance. The new enums use consistent #[serde(rename_all = "snake_case")] for API compatibility, and the audit event struct appropriately uses optional fields to accommodate different audit kinds.

clients/agent-runtime/src/tools/delegate.rs (1)

173-179: LGTM!

Correctly groups IterationBudgetExceeded and CostBudgetExceeded under the same BudgetExceeded status. The { .. } wildcard pattern is appropriate for named struct variants, confirmed by the AgentExecutionError definition in agent.rs.

clients/agent-runtime/tests/admin_config_api_integration.rs (3)

71-95: LGTM!

Test fixture correctly updated to include the new cost_tracker: None field in AppState.


149-172: LGTM!

Request struct properly includes the new cost: None field.


193-213: LGTM!

Consistent with the other test case.

clients/web/packages/locales/src/en.json (1)

323-353: LGTM!

Comprehensive i18n keys for the cost governance UI. Naming is consistent with existing patterns.

clients/agent-runtime/src/config/mod.rs (2)

75-91: LGTM!

Test fixture updated to include the new deprecated_fields field.


93-115: LGTM!

Good coverage for the deprecated field aliasing. The test validates that the legacy max_cost_per_day_cents key is properly tracked in deprecated_fields() metadata, supporting the backward-compatibility path described in the PR objectives.

clients/web/packages/locales/src/es.json (1)

323-353: LGTM!

Spanish translations are accurate and consistent with the English keys. Good localization work.

clients/agent-runtime/src/security/policy.rs (1)

1377-1391: LGTM!

Good test ensuring action-rate denial messages are clearly distinguished from cost/token budget messages. This separation helps operators quickly identify which limit was hit.

openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/state.yaml (1)

1-12: LGTM!

Clear state tracking for the change lifecycle. Notes properly document the PASS WITH WARNINGS status and remaining non-blocking items.

clients/web/apps/dashboard/src/composables/useCostGovernance.spec.ts (1)

137-275: Good API-contract coverage for the operator actions.

This test locks down the POST targets, bearer auth header, and JSON bodies for both override and reset, which should catch drift between useCostGovernance and useAdmin quickly.

clients/agent-runtime/src/observability/otel.rs (6)

42-67: LGTM on threshold trace fields helper.

Clean extraction of budget threshold fields with proper formatting. The optional surface field is handled correctly.


69-100: Redaction for override events is correctly implemented.

The helper properly uses event.redacted_actor(), event.redacted_reason(), and redact_optional_observer_payload(event.surface.as_deref()) to prevent sensitive data from reaching traces.


215-228: Budget metrics properly instrumented.

New counters for warnings, exceeded, and overrides with appropriate descriptions follow existing conventions.


475-481: Cost gauge recording on AgentEnd looks correct.

The cost_usd_last gauge records the latest request cost with provider/model attributes, which aligns with the session cost tracking goals.


790-814: Good test coverage for redaction behavior.

The test verifies that sensitive fields (actor, reason) are properly redacted and that the original secret values don't leak into trace fields.


309-331: No action needed. The surface field in BudgetWarning and BudgetExceeded is populated from self.budget_surface(), which provides system identifiers (e.g., "agent_loop") rather than user-controlled data. The apparent inconsistency with BudgetOverride redaction does not create a security risk here, so redaction is not required.

openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md (1)

1-11: Design document is well-structured and comprehensive.

Clear separation of concerns between token-spend and action-rate governance, with detailed rationale for each architecture decision.

clients/agent-runtime/src/cost/mod.rs (1)

9-15: Re-exports are well-organized.

The #[allow(unused_imports)] is appropriate here since this module serves as a facade and not all re-exported types will be used by every consumer.

clients/agent-runtime/src/gateway/admin.rs (4)

378-390: Deprecation normalization handles legacy field correctly.

normalized_max_actions_per_hour() properly falls back from the new field to the deprecated one, and deprecated_fields() tracks usage for operator awareness.


1669-1714: Cost patch validation is thorough.

Good boundary checks:

  • session_limit_usd: finite and >= 0 (allows disabled state)
  • daily_limit_usd / monthly_limit_usd: finite and > 0
  • warn_at_percent: [1, 100]

The asymmetry (session allows 0, daily/monthly don't) is intentional for disabling session-scoped limits.


2086-2088: Cost tracker update on config save ensures runtime consistency.

This correctly propagates cost config changes to the live tracker, avoiding drift between persisted config and in-memory state.


1304-1311: Deprecation tracking accumulates correctly.

The check !cfg.autonomy.deprecated_fields.contains(&deprecated_field) prevents duplicate entries when the same deprecated field is patched multiple times.

clients/agent-runtime/src/observability/log.rs (3)

18-37: Override log payload correctly redacts sensitive fields.

redacted_actor(), redacted_reason(), and redact_optional_observer_payload(surface) ensure sensitive data doesn't leak into logs.


567-585: Redaction test provides good coverage.

Verifies that ***REDACTED*** appears and original sensitive values (paired-admin-token, super-secret) don't leak.


102-125: No security concern; surface field contains only system identifiers.

The surface field in BudgetThresholdEvent is populated with system identifiers like "agent_loop" and "cli", not sensitive data. Logging it directly in BudgetWarning and BudgetExceeded events is correct and complies with the guideline against logging secrets.

Note: BudgetOverride redacts surface via format_budget_override_log_payload, which is unnecessary for non-sensitive data and creates inconsistency with how threshold events are logged. Consider aligning the approach.

clients/web/apps/dashboard/src/composables/useCostGovernance.ts (4)

70-88: Config value is intentionally overwritten by summary response.

The flow fetches config first (line 71), then summary/history concurrently (lines 81-84). Line 88 overwrites config.value with the summary response's config, which provides fresher data. This is correct behavior since the summary endpoint returns the current config state.


35-43: URL construction handles edge cases correctly.

Validates gateway URL, trims trailing slashes, and properly joins paths. The path.replace(/^\//, "") ensures no double slashes when combining base and path.


113-127: Override action properly reloads state after success.

The await reload() after a successful override ensures the UI reflects the new budget state.


99-102: Partial failure handling is user-friendly.

When summary or history fails but config succeeds, the composable sets usageUnavailable and usageError with a translated message rather than failing entirely. This allows the UI to degrade gracefully.

clients/agent-runtime/src/main.rs (10)

144-148: LGTM: --override-budget flag additions.

The boolean flag approach is clean and the help text clearly communicates "one over-budget request" semantics, matching the NextRequest scope in the override implementation.

Also applies to: 167-171


269-357: LGTM: Cost CLI command structure and enum mappings.

The CostCommands enum with Summary, History, and Reset subcommands is well-structured. The From implementations for mapping CLI enums to internal types are correct and exhaustive. The CliSessionSurface helper provides clear actor attribution for override auditing.


917-936: LGTM: handle_cost_command dispatch.

Clean dispatch pattern. Each subcommand creates a service, performs the operation, and renders output. No heavy initialization in the command parsing flow.


1020-1032: LGTM: apply_cli_budget_override implementation.

Correctly delegates to agent.apply_next_request_budget_override and outputs machine-readable key=value format with the override ID for auditability.


1034-1041: LGTM: print_cli_session_summary handles None gracefully.

The function correctly handles Option<CostGovernanceSummary> by simply not printing when cost tracking is disabled or unavailable, which aligns with the session_cost_summary behavior returning Ok(None) in those cases.


960-989: LGTM: render_cost_summary output format.

Machine-readable key=value format is consistent and includes all relevant budget state information. The scope percent lookups gracefully default to 0.0 when a period isn't found.


1507-1509: LGTM: Status command deprecation warning.

Good UX improvement surfacing the deprecation message from config when the legacy action_rate field is configured.


2598-2660: LGTM: CLI contract tests for new cost commands.

Comprehensive coverage verifying --override-budget flag parsing and cost command structure with periods, windows, scopes, and reasons.


2662-2763: LGTM: Cost override behavior tests.

Thorough test coverage for:

  • Override registration and tracking
  • Audit event verification (OverrideGranted, OverrideConsumed)
  • NextRequest scope consumption limiting to one request
  • Failure cases when cost tracking or override policy is disabled

2135-2159: LGTM: build_test_agent helper.

Clean test helper that properly constructs an Agent with configurable cost tracking for unit tests.

openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/specs/cost-governance/spec.md (1)

1-200: LGTM: Cost Governance Specification.

The specification is well-structured with clear RFC-style requirements (MUST/SHOULD/MAY), concrete scenarios, and accurate baseline truthfulness statements. The four-scope budget model (session, daily, monthly, mission), warning/hard-block semantics, and override audit requirements align with the runtime implementation. The static analysis warnings about repetitive "MUST" sentence starts are intentional RFC 2119 language for specification clarity.

openspec/specs/cost-governance/spec.md (1)

1-200: LGTM: Canonical Cost Governance Specification.

This canonical spec location mirrors the archived change spec as expected. The requirements accurately reflect the implemented cost governance model with explicit budget scopes, override audit trails, and governance domain separation.

clients/agent-runtime/src/gateway/mod.rs (2)

1292-1295: Cost endpoints correctly enforce bearer-token and same-origin checks.

handle_cost_summary and handle_cost_history both call gateway::utils::admin_origin_guard() and gateway::utils::admin_requires_auth() before processing requests, enforcing the same privilege boundary as admin handlers. Non-loopback origins and missing/invalid tokens are rejected with appropriate HTTP status codes (FORBIDDEN and UNAUTHORIZED). Tests confirm both checks actively reject unauthorized requests.


82-83: Cost PATCH correctly updates the live tracker; no issue found.

The tracker uses RwLock<CostConfig> (not a one-time clone). When a PATCH is issued, cost_tracker.update_config(next_cfg.cost.clone()) is called synchronously (line 2087 in admin.rs) before the response is returned. On subsequent budget checks, check_budget() reads the fresh config via self.config(), which acquires the read lock—so enforcement immediately reflects the new limits.

Minor observation: The main test (admin_config_patch_updates_cost_fields_without_touching_autonomy, line 4696) sets cost_tracker: None, so it doesn't verify tracker updates in practice. A test with a real tracker instance would strengthen confidence, but the implementation itself is sound.

clients/agent-runtime/src/config/schema.rs (1)

1434-1460: Nice backward-compatibility shim for the deprecated autonomy alias.

Normalizing the legacy field, keeping serialization canonical, and recording deprecation metadata is the right shape here.

Also applies to: 1515-1581

Harden runtime budget reservation, session-aware telemetry, mission budget
cleanup, disabled gateway behavior, cost config validation, and dashboard
operator controls based on review feedback.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Apr 7, 2026

Deploying corvus with  Cloudflare Pages  Cloudflare Pages

Latest commit: 7cc3d13
Status: ✅  Deploy successful!
Preview URL: https://10b5285d.corvus-42x.pages.dev
Branch Preview URL: https://feature-dallay-164-cost-gove.corvus-42x.pages.dev

View logs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
clients/agent-runtime/src/config/schema.rs (1)

581-620: ⚠️ Potential issue | 🟠 Major

Add #[serde(deny_unknown_fields)] to CostConfig and ModelPricing structs.

Config enforces strict deserialization at the top level, but CostConfig and ModelPricing accept unknown fields. A configuration typo like session_limit_usd_ or inpt will silently deserialize and apply permissive defaults, undermining the budget controls. Cost limits are security policy and must fail explicitly on misconfiguration.

Suggested fix
-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(deny_unknown_fields)]
 pub struct CostConfig {

-#[derive(Debug, Clone, Serialize, Deserialize)]
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(deny_unknown_fields)]
 pub struct ModelPricing {

Per coding guideline: "Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable."

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

In `@clients/agent-runtime/src/config/schema.rs` around lines 581 - 620, The
CostConfig and ModelPricing structs currently allow unknown fields during
deserialization which can silently accept typos and weaken budget/security
rules; add the serde attribute #[serde(deny_unknown_fields)] to both the
CostConfig and ModelPricing struct definitions (placed alongside the existing
#[derive(...)]/attributes) so deserialization will fail on any unexpected
configuration keys, ensuring mis-typed fields like session_limit_usd_ or inpt
cause explicit errors rather than falling back to defaults.
♻️ Duplicate comments (2)
clients/agent-runtime/src/cost/tracker.rs (1)

304-345: ⚠️ Potential issue | 🔴 Critical

reserve_budget_for_request() is still non-atomic.

pending_total_usd is read under one lock, but that lock is dropped before the budget check and before the reservation insert. Two concurrent requests can both evaluate against the same remaining budget and both reserve. The check, override consume, and insert need one critical section.

As per coding guidelines: **/*.rs: Focus on Rust idioms, memory safety, and ownership/borrowing correctness. Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.

clients/agent-runtime/src/agent/agent.rs (1)

1127-1130: ⚠️ Potential issue | 🟠 Major

Don't reopen mission budget on session-cost regressions.

max(0.0) turns a reset/rollback below the mission baseline into fresh headroom. MissionCoordinator::current_accumulated_cost_cents() in clients/agent-runtime/src/agent/mission.rs already fails closed on the same condition, so this pre-flight mission scope should do the same.

As per coding guidelines: **/*: Security first, performance second. Look for behavioral regressions, missing tests, and contract breaks across modules.

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

In `@clients/agent-runtime/src/agent/agent.rs` around lines 1127 - 1130, The
current code uses (summary.session_cost_usd -
active_budget.baseline_session_cost_usd).max(0.0) which reopens mission headroom
on regressions; replace this with the same "fail closed" behavior as
MissionCoordinator::current_accumulated_cost_cents() in mission.rs: detect if
summary.session_cost_usd < active_budget.baseline_session_cost_usd and return an
error (or otherwise abort/fail the operation) instead of clamping to 0, updating
the code around tracker.get_summary() and the current_usd calculation to
propagate that error path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/agent-runtime/src/agent/agent.rs`:
- Around line 1204-1209: The current error path in the block handling
tracker.record_usage(usage) should not call
tracker.release_budget_reservation(&reservation.id) because the provider call
already succeeded and releasing erases spent budget; change the error handling
in the record_usage failure branch (the if let Err(error) =
tracker.record_usage(usage) block) to only log the error (e.g., tracing::warn!
with error) and return without releasing or modifying the reservation state,
ensuring tracker.release_budget_reservation is not invoked on record_usage
failures (keep references to tracker, reservation, and record_usage to locate
the change).

In `@clients/agent-runtime/src/cost/tracker.rs`:
- Around line 420-426: The audit event is persisting sensitive free-form
operator input in CostAuditEvent.actor and .reason; before calling
append_audit_event (and in other similar places where CostAuditEvent is
constructed for OverrideGranted/OverrideConsumed/OverrideExpired), redact those
fields by replacing raw values with a non-sensitive placeholder (e.g.,
Some("[REDACTED]") or None) so secrets/tokens are never written to
cost-audit.jsonl; update the constructions that call append_audit_event and any
other builders of CostAuditEvent to use the redacted actor and reason values
instead of request.actor/request.reason.

In `@clients/agent-runtime/src/gateway/cost.rs`:
- Around line 174-176: The code returns error.to_string() from
service.history_window as a 400, leaking internal I/O details (e.g., from
read_records()) and misclassifying server faults; change the match on
service.history_window(...) so that only explicit validation/query errors map to
bad_request(...) and everything else is treated as a 500: pattern-match the
error variants (e.g., TrackerError::Validation or whatever variant indicates
client input problems) and return bad_request for those, otherwise log the full
error internally (trace/error) and return a generic internal_server_error (do
not use error.to_string() in the response). Ensure you update the branch
handling in the history_window call and reference service.history_window,
bad_request, and read_records in comments if needed so future reviewers
understand why internal errors are hidden.

In `@clients/agent-runtime/src/gateway/mod.rs`:
- Around line 301-308: The comparison is incorrectly using the deprecated alias
aut.max_cost_per_day_cents against the canonical
cfg.autonomy.max_actions_per_hour and can cause false restart-required
conflicts; update the logic around the compare_primitive call so that you only
compare aut.max_cost_per_day_cents when the canonical
cfg.autonomy.max_actions_per_hour is not present (i.e., check
cfg.autonomy.max_actions_per_hour.is_none() before invoking compare_primitive),
referencing the aut.max_cost_per_day_cents field and compare_primitive function
to locate and modify the branch.
- Around line 1167-1174: cost_tracker is only constructed when
config.cost.enabled is true, so toggling config.cost.enabled at runtime can
enable cost governance in config while the process still holds None and
governance stays disabled; fix by instantiating a CostTracker unconditionally
and let the tracker itself (or CostService) observe the enabled flag: always set
cost_tracker = Some(Arc::new(CostTracker::new(config.cost.clone(),
&config.workspace_dir)?)) and adjust CostTracker::new or CostService to accept
the initial enabled flag (config.cost.enabled) and enforce/observe it at runtime
so toggling the config flips enforcement without losing the tracker instance.

In `@clients/web/apps/dashboard/src/components/config/CostOverview.spec.ts`:
- Around line 159-177: The test "renders exceeded state when budget is blocked"
sets hoisted.state.summary (typed as AdminCostSummaryView) but omits
percent_used_session; update the mocked summary object in that test (the object
assigned to (hoisted.state.summary as ReturnType<typeof ref<AdminCostSummaryView
| null>>).value) to include a percent_used_session field (e.g., a number like
100) so the mock fully matches AdminCostSummaryView and prevents
undefined/template errors when the component accesses percent_used_session.

In `@clients/web/apps/dashboard/src/components/config/CostOverview.vue`:
- Around line 199-208: The progress bar width can become NaN when
summary.percent_used_session is undefined; update the span style binding in
CostOverview.vue (the progress-bar span) to coalesce undefined to a safe numeric
value (e.g., use nullish coalescing or Number(...) to default to 0), then clamp
that value with Math.min(..., 100) so the width is always a valid percentage;
also ensure the formatPercent call for summary.percent_used_session uses the
same safe default so it never receives undefined.

In `@clients/web/apps/dashboard/src/composables/useCostGovernance.ts`:
- Around line 121-123: The success toast is being cleared by reload() because
actionMessage is set before calling reload(); move the assignment of
actionMessage (and any analogous success assignment for the other path) to after
the await reload() call so the message is set once reload() completes;
specifically update the grantCostOverride success block (where result:
AdminCostOverrideRecordView is set and actionMessage.value is assigned) and the
corresponding revoke path (the block around lines 137-139) to set
actionMessage.value after await reload() while keeping actionError cleared as
needed.

In
`@openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md`:
- Around line 228-233: The public contract examples currently restrict scopes to
"session"/"day"/"month" but must include "mission"; update the type literals
used for active_period and warnings.period (and any related enums like
percent_used_daily/percent_used_monthly usage) to include "mission", and add at
least one example summary/history entry that demonstrates the mission scope
(e.g., a warning/history item with period: "mission" and appropriate
percent_used_* values) so the canonical governance model
(session/daily/monthly/mission) is represented consistently across the examples
referenced around active_period, warnings, and the percent_used_* fields.
- Around line 85-87: The document has conflicting deprecation timelines: one
place says keep reading autonomy.max_cost_per_day_cents for one release cycle,
while the migration plan keeps read support through Release N+2; reconcile them
to a single timeline (choose either “one release cycle” or “through Release
N+2”) and update all references (including the sentence about
autonomy.max_cost_per_day_cents and the migration plan section around the
Release N+2 wording) so every occurrence uses the same chosen timeline and
describes read/write behavior and warning emission consistently.
- Around line 292-297: The override endpoint POST /api/web/admin/cost/override
must not accept an actor in the request body; update the route handler for that
endpoint (the POST /api/web/admin/cost/override handler) to remove "actor" from
the request DTO/validation (e.g. OverrideRequestSchema or parseOverrideBody) and
explicitly reject requests that include an "actor" field, derive the acting
principal from the authenticated admin/session (e.g. req.user, ctx.admin, or
getAuthenticatedAdmin()) and pass that server-derived identity into the
downstream functions (e.g. createCostOverride or recordAuditLog) so audit
entries use the server-side principal instead of client-provided data; also
update any tests and open-api/schema docs to remove the actor field.
- Around line 8-11: Update the design document to refer to CostService as the
runtime contract instead of CostTracker: replace external-surface mentions and
sequence diagrams/call flows that currently call CostTracker so they call
CostService, and mark CostTracker as an internal storage/accounting
implementation detail behind CostService (e.g., update descriptions around the
instantiation/enforcement text that references CostTracker in bootstrap/mod.rs
and agent/agent.rs to say CostService is enforced there and delegates to an
internal CostTracker); apply the same changes to the other referenced sections
(lines ~15-16, 26-27, 121-127, 169-171) so all external APIs,
evaluation/enforcement steps, and sequence flows invoke CostService while
documenting CostTracker only as internal state/accounting.

---

Outside diff comments:
In `@clients/agent-runtime/src/config/schema.rs`:
- Around line 581-620: The CostConfig and ModelPricing structs currently allow
unknown fields during deserialization which can silently accept typos and weaken
budget/security rules; add the serde attribute #[serde(deny_unknown_fields)] to
both the CostConfig and ModelPricing struct definitions (placed alongside the
existing #[derive(...)]/attributes) so deserialization will fail on any
unexpected configuration keys, ensuring mis-typed fields like session_limit_usd_
or inpt cause explicit errors rather than falling back to defaults.

---

Duplicate comments:
In `@clients/agent-runtime/src/agent/agent.rs`:
- Around line 1127-1130: The current code uses (summary.session_cost_usd -
active_budget.baseline_session_cost_usd).max(0.0) which reopens mission headroom
on regressions; replace this with the same "fail closed" behavior as
MissionCoordinator::current_accumulated_cost_cents() in mission.rs: detect if
summary.session_cost_usd < active_budget.baseline_session_cost_usd and return an
error (or otherwise abort/fail the operation) instead of clamping to 0, updating
the code around tracker.get_summary() and the current_usd calculation to
propagate that error path.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a800732d-892a-43ae-9346-4d63bb660436

📥 Commits

Reviewing files that changed from the base of the PR and between d5f9b69 and 6a00052.

⛔ Files ignored due to path filters (1)
  • clients/agent-runtime/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (20)
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/cost/tracker.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/observability/otel.rs
  • clients/agent-runtime/src/observability/prometheus.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/web/apps/dashboard/src/components/config/CostOverview.spec.ts
  • clients/web/apps/dashboard/src/components/config/CostOverview.vue
  • clients/web/apps/dashboard/src/composables/useAdmin.ts
  • clients/web/apps/dashboard/src/composables/useCostGovernance.ts
  • clients/web/apps/dashboard/src/types/admin-config.ts
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: sonar
  • GitHub Check: pr-checks
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (9)
clients/agent-runtime/src/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

clients/agent-runtime/src/**/*.rs: Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements
Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Files:

  • clients/agent-runtime/src/observability/prometheus.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/observability/otel.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/cost/tracker.rs
clients/agent-runtime/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Run cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, and cargo test for code validation, or document which checks were skipped and why

Files:

  • clients/agent-runtime/src/observability/prometheus.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/observability/otel.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/cost/tracker.rs
**/*.rs

⚙️ CodeRabbit configuration file

**/*.rs: Focus on Rust idioms, memory safety, and ownership/borrowing correctness.
Flag unnecessary clones, unchecked panics in production paths, and weak error context.
Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.

Files:

  • clients/agent-runtime/src/observability/prometheus.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/observability/otel.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/cost/tracker.rs
**/*

⚙️ CodeRabbit configuration file

**/*: Security first, performance second.
Validate input boundaries, auth/authz implications, and secret management.
Look for behavioral regressions, missing tests, and contract breaks across modules.

Files:

  • clients/agent-runtime/src/observability/prometheus.rs
  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/web/apps/dashboard/src/components/config/CostOverview.spec.ts
  • clients/web/apps/dashboard/src/types/admin-config.ts
  • clients/agent-runtime/src/observability/otel.rs
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
  • clients/agent-runtime/src/main.rs
  • clients/web/apps/dashboard/src/composables/useAdmin.ts
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/web/apps/dashboard/src/components/config/CostOverview.vue
  • clients/agent-runtime/src/cost/mod.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/cost/service.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/web/apps/dashboard/src/composables/useCostGovernance.ts
  • clients/agent-runtime/src/cost/types.rs
  • clients/agent-runtime/src/cost/tracker.rs
**/*.{md,mdx}

⚙️ CodeRabbit configuration file

**/*.{md,mdx}: Verify technical accuracy and that docs stay aligned with code changes.
For user-facing docs, check EN/ES parity or explicitly note pending translation gaps.

Files:

  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
clients/agent-runtime/src/main.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

clients/agent-runtime/src/main.rs: Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths
Keep startup path lean and avoid heavy initialization in command parsing flow

Files:

  • clients/agent-runtime/src/main.rs
clients/agent-runtime/src/{security,gateway,tools}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Treat src/security/, src/gateway/, src/tools/ as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Files:

  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Files:

  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/gateway/mod.rs
**/*.vue

⚙️ CodeRabbit configuration file

**/*.vue: Enforce Vue 3 Composition API with <script setup>.
Ensure accessibility (A11y) and proper use of Tailwind CSS classes.
Check for proper prop validation and emitted events documentation.

Files:

  • clients/web/apps/dashboard/src/components/config/CostOverview.vue
🧠 Learnings (10)
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths

Applied to files:

  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/observability/traits.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/cost/tracker.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Applied to files:

  • clients/agent-runtime/src/bootstrap/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/gateway/mod.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Include threat/risk notes and rollback strategy for security, runtime, and gateway changes; add or update tests for boundary checks and failure modes

Applied to files:

  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Keep each iteration reversible with small commits and clear rollback strategy; validate assumptions with code search before implementing

Applied to files:

  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/*.rs : Run `cargo fmt --all -- --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` for code validation, or document which checks were skipped and why

Applied to files:

  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/config/schema.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Keep startup path lean and avoid heavy initialization in command parsing flow

Applied to files:

  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/agent/agent.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/**/*.rs : Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Applied to files:

  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/agent/mission.rs
  • clients/agent-runtime/src/agent/agent.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/providers/**/*.rs : Implement `Provider` trait in `src/providers/` and register in `src/providers/mod.rs` factory when adding a new provider

Applied to files:

  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/agent/agent.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/tools/**/*.rs : Implement `Tool` trait in `src/tools/` with strict parameter schema, validate and sanitize all inputs, and return structured `ToolResult` without panics in runtime path

Applied to files:

  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/agent/agent.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools}/**/*.rs : Treat `src/security/`, `src/gateway/`, `src/tools/` as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Applied to files:

  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
🔇 Additional comments (14)
clients/agent-runtime/src/bootstrap/mod.rs (1)

262-274: LGTM!

The cost tracker logic now correctly gates cost_tracker_override behind config.cost.enabled, ensuring disabled cost governance cannot be bypassed by a shared tracker. The new test at lines 626-639 provides good regression coverage.

clients/agent-runtime/src/observability/prometheus.rs (1)

71-96: LGTM!

New budget lifecycle metrics are well-designed with appropriate low-cardinality labels. The metric naming follows Prometheus conventions (corvus_budget_*_total), and the test at lines 524-543 correctly validates that sensitive fields (actor, reason) are not exposed in the encoded output.

clients/agent-runtime/src/observability/otel.rs (2)

793-818: LGTM!

The test correctly validates that budget_override_trace_fields redacts sensitive actor and reason values while preserving non-sensitive operational fields.


99-101: Inconsistent surface redaction in override event handling.

surface is selectively redacted in override trace fields (line 99) but not in override counter attributes (line 366). Since the redaction function only masks strings containing known sensitive markers, this defensive approach is safe. However, the inconsistency suggests either:

  1. An accidental discovery that override events need redaction while other threshold events don't, or
  2. An oversight where all events should be treated uniformly.

Add a comment explaining why override trace fields redact surface while counters and other threshold trace fields do not, or align the behavior across all event types.

clients/agent-runtime/src/cost/mod.rs (1)

1-15: LGTM!

Module organization cleanly exposes the new CostService and governance types as the public API surface.

clients/web/apps/dashboard/src/components/config/CostOverview.vue (1)

104-111: LGTM!

Visibility logic correctly separates override and reset actions. Reset remains available when allow_override is false, addressing the past review feedback.

clients/web/apps/dashboard/src/types/admin-config.ts (2)

172-176: LGTM!

Type definitions correctly separate AdminUsagePeriod (session/day/month for reset/history) from AdminSummaryPeriod (includes "mission" for summary responses), addressing the past review feedback.


188-199: No action needed—percent_used_session is always present.

The TypeScript tests and backend implementation both confirm that percent_used_session is always included in cost summary responses. All test mocks (across both "warning" and "allowed" states) include this field, and the backend Rust code computes it unconditionally. The type should remain required.

			> Likely an incorrect or invalid review comment.
clients/agent-runtime/src/main.rs (4)

1329-1336: LGTM!

Summary error handling correctly logs the error and returns the actual run result, addressing the past review feedback about masking successful runs.


1020-1032: LGTM!

apply_cli_budget_override cleanly delegates to the agent's override helper and prints machine-readable output for scripting. The actor naming (cli-agent, cli-code) provides good audit trail context.


269-301: LGTM!

The new Cost subcommand with Summary, History, and Reset variants follows the established CLI patterns. The --reason flag for reset enables audit trail context.


2720-2770: Good test coverage for override audit trail.

This test validates the complete override lifecycle: grant → consume → block subsequent requests. The audit event assertions for OverrideGranted and OverrideConsumed with correct actor/scope provide strong regression coverage for the governance model.

clients/web/apps/dashboard/src/composables/useAdmin.ts (1)

304-309: No action required. Every implementation of the injected authHeaders parameter already sets Content-Type: application/json explicitly. In useCostGovernance.ts (which uses this composable) and in tests, the authHeaders function includes "Content-Type": "application/json". The code correctly relies on dependency injection for headers—the caller is responsible for providing a complete header object, which all verified implementations do.

			> Likely an incorrect or invalid review comment.
clients/agent-runtime/src/gateway/mod.rs (1)

1906-1920: Good fail-closed behavior for legacy runtime under cost governance.

Blocking legacy execution when cost.enabled=true prevents ungoverned request handling and keeps policy enforcement explicit.

As per coding guidelines: Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable.

Also applies to: 2087-2092

@yacosta738 yacosta738 added this to the v1.0.0 milestone Apr 7, 2026
…ce-productization' into feature/dallay-164-cost-governance-productization
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
clients/agent-runtime/src/config/schema.rs (1)

613-622: ⚠️ Potential issue | 🟠 Major

Partial model prices silently default to free tokens—validate that custom pricing is complete.

ModelPricing fields default to 0.0 via #[serde(default)], and validate_cost_config() accepts 0.0 as valid. A user providing only input or output in their config will have the missing field silently set to 0.0, undercounting spending and breaking budget enforcement.

Require both fields present, or detect serde-supplied defaults and reject incomplete entries. Also add a test with a one-sided price entry to prevent regression.

Additionally, validate_cost_config() does not validate warn_at_percent bounds (should be 0–100).

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

In `@clients/agent-runtime/src/config/schema.rs` around lines 613 - 622,
ModelPricing currently uses #[serde(default)] so missing input/output silently
becomes 0.0; remove the #[serde(default)] on both fields (so serde will reject
configs that omit either input or output) or alternatively change fields to
Option<f64> and update validate_cost_config() to reject Some/None mixes; update
validate_cost_config() to explicitly reject any ModelPricing where only one of
input/output is provided or either is negative/zero if that’s invalid, add a
unit test that loads a config with only input (or only output) and asserts
validation fails, and also add bounds checking in validate_cost_config() to
ensure warn_at_percent is within 0–100 (reject otherwise). Reference symbols:
ModelPricing struct, validate_cost_config(), and the config unit tests that
exercise pricing parsing.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@clients/agent-runtime/src/agent/agent.rs`:
- Around line 1119-1140: The current_mission_budget_scope function uses
session_cost_usd minus active_mission_budget.baseline_session_cost_usd which
breaks when "cost reset --scope session" reduces session_cost_usd; change the
calculation to use a monotonic/persistent source: either (a) read a monotonic
total from the cost tracker (e.g., replace summary.session_cost_usd with a
cumulative_total field from tracker.get_summary()/tracker API) and store a
baseline_total_cost_usd in the ActiveMissionBudget, or (b) add and persist a
mission_local_consumed_usd on ActiveMissionBudget that accumulates deltas on
every cost update so current_usd = mission_local_consumed_usd +
(cumulative_total - baseline_total_cost_usd); update active mission creation
(ActiveMissionBudget) and the cost-update path to set baseline_total_cost_usd or
to increment mission_local_consumed_usd, and then have
current_mission_budget_scope return MissionBudgetScope using that
monotonic/persisted value instead of session_cost_usd -
baseline_session_cost_usd.

In `@clients/agent-runtime/src/config/schema.rs`:
- Around line 3230-3248: In validate_cost_config(), add validation for
cost.warn_at_percent to ensure it's within 0..=100 (reject values outside that
inclusive range) alongside the existing checks for session/daily/monthly limits
and price inputs/outputs; update the validator to bail with a clear error
message like "cost.warn_at_percent must be between 0 and 100 inclusive" when the
check fails, and add a regression test that attempts to load/validate a config
with warn_at_percent=255 (and another out-of-range negative/over-100 case if
applicable) to ensure these invalid values are rejected (reference
validate_cost_config and the cost.warn_at_percent field in tests).

In `@clients/agent-runtime/src/cost/tracker.rs`:
- Around line 430-446: The code currently performs state mutations (e.g., via
*self.lock_active_override() = Some(...), removing resets, etc.) and then calls
append_audit_event(...) which uses ? to propagate I/O errors; change this so
audit-write failures are treated as best-effort: after performing the mutation
(locations around lock_active_override(), the reset/remove paths and similar
blocks), call append_audit_event(...) but do not return Err on failure — instead
capture the Result, log the error (with context including
override_id/session_id/actor) and continue; alternatively, if you prefer
atomicity, implement a rollback path that reverts the in-memory/durable mutation
before returning Err, but do not leave the current code that mutates first and
then uses ? to bubble audit I/O errors in append_audit_event.
- Around line 408-429: The apply_override path may silently replace an
already-expired active_override without emitting the OverrideExpired audit
event; update apply_override to first check self.active_override (or the method
that returns it) and if present and expired (compare expires_at to now) call the
existing expire/emit logic to mark it expired and emit the OverrideExpired audit
event before constructing and assigning the new CostOverrideRecord; ensure you
preserve session_id/remaining_uses handling and that the active override
replacement uses the same assignment code path so the audit trail is always
emitted.

In `@clients/agent-runtime/src/gateway/mod.rs`:
- Around line 2085-2090: Add a unit/integration test that exercises the SSE
branch where config.cost.enabled is true and no webhook dispatcher path is
provided, asserting that the handler returns StreamProcessingOutcome::Error with
a JSON object containing "code": "cost_governance_requires_dispatcher" and the
expected message; specifically, instantiate the config with cost.enabled = true,
call the function/method that produces StreamProcessingOutcome (the same code
path around the snippet that returns StreamProcessingOutcome::Error), and assert
the error variant and its JSON "code" field equals
"cost_governance_requires_dispatcher".

In `@clients/agent-runtime/src/main.rs`:
- Around line 1311-1317: Move the CLI budget override check so start/end
lifecycle events remain paired: call apply_cli_budget_override(&agent,
CliSessionSurface::Agent) and handle any early returns before emitting
record_agent_start_event(&provider_name, &model_name). Update both places that
currently call record_agent_start_event before apply_cli_budget_override (the
agent command handler around the Agent::from_config() block and the
handle_code_command handler) so that budget validation/rejection happens first
and only then record_agent_start_event is emitted.
- Around line 960-989: render_cost_summary currently prints active_period and
percentages but not which scope actually governs; scan summary.scope_statuses
inside render_cost_summary to find the governing/active scope (e.g., the status
where a governing flag is set or the highest-priority blocked scope), derive a
human label for that scope (reusing period_label or a new scope_label helper),
and add a line like format!("active_scope={}", scope_label) to the returned list
so the CLI shows which scope (session/day/month/mission) caused the budget trip;
use summary.scope_statuses and render_cost_summary as the locations to implement
this change.

In `@clients/web/apps/dashboard/src/composables/useCostGovernance.ts`:
- Around line 73-77: The code treats any null from admin.fetchCostConfig() as a
missing feature; change admin.fetchCostConfig() to rethrow transport/auth errors
instead of swallowing them so callers can distinguish HTTP 401/500 from a
genuine absent config, and update useCostGovernance (the block that sets
config.value = null and error.value = t("errors.costNotAvailable")) to only set
the generic errors.costNotAvailable when the fetch resolved successfully but
returned an explicit “no config” result; for thrown errors let them propagate
(or map HTTP 401/500 to specific error messages) so the UI shows auth/outage
messages. Add unit tests for useCostGovernance that mock admin.fetchCostConfig()
to throw a 401 and a 500 and assert the composable surfaces the corresponding
auth/transport errors rather than the generic fallback.
- Around line 33-55: In reload(), avoid lazy-evaluating buildGatewayUrl() and
authHeaders() mid-flight: capture the current gatewayUrl() and bearerToken() at
the start of reload (like the request-id pattern used in fetchSessionDetail()),
create local helper closures or parameters that build URLs and headers from
those captured values, pass those captured creds into fetchCostConfig(),
fetchCostSummary(), and fetchCostHistory(), and on each fetch completion verify
the captured creds still match the current gatewayUrl()/bearerToken() (or check
a per-reload request-id) before committing state so older responses cannot
overwrite newer ones; add a regression test that starts two overlapping reloads
with different gatewayUrl/bearerToken values and asserts only the latest
reload's data is applied.

In
`@openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md`:
- Around line 215-242: The schema in the doc is inconsistent across examples and
DTOs—normalize to one runtime-owned shape: update the CostGovernanceState
interface and all JSON examples to use the exact runtime field names (e.g.,
choose either active_budget_state or budget_state and either active_period or
period) and include session_limit_usd if the runtime DTO exposes it; ensure you
update the properties in the interface (CostGovernanceState), the warnings array
shape, and the examples between lines referenced so downstream consumers
implement the same keys as the runtime DTO (use the actual runtime field names
and types for active_budget_state/active_period/session_limit_usd).
- Around line 344-367: The examples for BudgetWarning, BudgetExceeded, and
BudgetOverride are out of sync with the runtime: update the structs shown for
BudgetWarning, BudgetExceeded, and BudgetOverride to include the missing fields
emitted by the runtime—add budget_state (string/enum), projected_usd (f64),
percent_used (f64) and surface where applicable to BudgetWarning and
BudgetExceeded, and add action (string), period (UsagePeriod), and override_id
(string) to BudgetOverride; ensure session_id stays optional where runtime does
so, keep existing fields (period, current_usd, limit_usd, actor, scope, reason,
previous_state) and match types to the runtime symbols (BudgetWarning,
BudgetExceeded, BudgetOverride, UsagePeriod) so docs reflect the actual
observability contract.

---

Outside diff comments:
In `@clients/agent-runtime/src/config/schema.rs`:
- Around line 613-622: ModelPricing currently uses #[serde(default)] so missing
input/output silently becomes 0.0; remove the #[serde(default)] on both fields
(so serde will reject configs that omit either input or output) or alternatively
change fields to Option<f64> and update validate_cost_config() to reject
Some/None mixes; update validate_cost_config() to explicitly reject any
ModelPricing where only one of input/output is provided or either is
negative/zero if that’s invalid, add a unit test that loads a config with only
input (or only output) and asserts validation fails, and also add bounds
checking in validate_cost_config() to ensure warn_at_percent is within 0–100
(reject otherwise). Reference symbols: ModelPricing struct,
validate_cost_config(), and the config unit tests that exercise pricing parsing.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0ced307a-c9d4-4719-bd6a-391cd4c820e6

📥 Commits

Reviewing files that changed from the base of the PR and between 6a00052 and b7c61c7.

📒 Files selected for processing (10)
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/cost/tracker.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/main.rs
  • clients/web/apps/dashboard/src/components/config/CostOverview.spec.ts
  • clients/web/apps/dashboard/src/components/config/CostOverview.vue
  • clients/web/apps/dashboard/src/composables/useCostGovernance.ts
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: sonar
  • GitHub Check: submit-gradle
  • GitHub Check: pr-checks
  • GitHub Check: Cloudflare Pages
🧰 Additional context used
📓 Path-based instructions (9)
**/*

⚙️ CodeRabbit configuration file

**/*: Security first, performance second.
Validate input boundaries, auth/authz implications, and secret management.
Look for behavioral regressions, missing tests, and contract breaks across modules.

Files:

  • clients/web/apps/dashboard/src/components/config/CostOverview.spec.ts
  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
  • clients/web/apps/dashboard/src/composables/useCostGovernance.ts
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/web/apps/dashboard/src/components/config/CostOverview.vue
  • clients/agent-runtime/src/cost/tracker.rs
**/*.{md,mdx}

⚙️ CodeRabbit configuration file

**/*.{md,mdx}: Verify technical accuracy and that docs stay aligned with code changes.
For user-facing docs, check EN/ES parity or explicitly note pending translation gaps.

Files:

  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
clients/agent-runtime/src/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

clients/agent-runtime/src/**/*.rs: Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements
Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Files:

  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/cost/tracker.rs
clients/agent-runtime/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Run cargo fmt --all -- --check, cargo clippy --all-targets -- -D warnings, and cargo test for code validation, or document which checks were skipped and why

Files:

  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/cost/tracker.rs
**/*.rs

⚙️ CodeRabbit configuration file

**/*.rs: Focus on Rust idioms, memory safety, and ownership/borrowing correctness.
Flag unnecessary clones, unchecked panics in production paths, and weak error context.
Prioritize unsafe blocks, FFI boundaries, concurrency races, and secret handling.

Files:

  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/cost/tracker.rs
clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Files:

  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
clients/agent-runtime/src/main.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

clients/agent-runtime/src/main.rs: Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths
Keep startup path lean and avoid heavy initialization in command parsing flow

Files:

  • clients/agent-runtime/src/main.rs
clients/agent-runtime/src/{security,gateway,tools}/**/*.rs

📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)

Treat src/security/, src/gateway/, src/tools/ as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Files:

  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
**/*.vue

⚙️ CodeRabbit configuration file

**/*.vue: Enforce Vue 3 Composition API with <script setup>.
Ensure accessibility (A11y) and proper use of Tailwind CSS classes.
Check for proper prop validation and emitted events documentation.

Files:

  • clients/web/apps/dashboard/src/components/config/CostOverview.vue
🧠 Learnings (13)
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Include threat/risk notes and rollback strategy for security, runtime, and gateway changes; add or update tests for boundary checks and failure modes

Applied to files:

  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Keep each iteration reversible with small commits and clear rollback strategy; validate assumptions with code search before implementing

Applied to files:

  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Preserve CLI contract unless change is intentional and documented; prefer explicit errors over silent fallback for unsupported critical paths

Applied to files:

  • openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/cost/tracker.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/**/*.rs : Avoid unnecessary allocations, clones, and blocking operations to maintain performance and efficiency

Applied to files:

  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/cost/tracker.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/main.rs : Keep startup path lean and avoid heavy initialization in command parsing flow

Applied to files:

  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/main.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools,config}/**/*.rs : Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable

Applied to files:

  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/main.rs
  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/cost/tracker.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/tools/**/*.rs : Implement `Tool` trait in `src/tools/` with strict parameter schema, validate and sanitize all inputs, and return structured `ToolResult` without panics in runtime path

Applied to files:

  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/main.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/providers/**/*.rs : Implement `Provider` trait in `src/providers/` and register in `src/providers/mod.rs` factory when adding a new provider

Applied to files:

  • clients/agent-runtime/src/agent/agent.rs
  • clients/agent-runtime/src/main.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/*.rs : Run `cargo fmt --all -- --check`, `cargo clippy --all-targets -- -D warnings`, and `cargo test` for code validation, or document which checks were skipped and why

Applied to files:

  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/main.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/Cargo.toml : Do not add heavy dependencies for minor convenience; justify new crate additions

Applied to files:

  • clients/agent-runtime/src/config/schema.rs
  • clients/agent-runtime/src/main.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/**/Cargo.toml : Preserve release-size profile assumptions in `Cargo.toml` and avoid adding heavy dependencies unless clearly justified

Applied to files:

  • clients/agent-runtime/src/config/schema.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/{security,gateway,tools}/**/*.rs : Treat `src/security/`, `src/gateway/`, `src/tools/` as high-risk surfaces and never broaden filesystem/network execution scope without explicit policy checks

Applied to files:

  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/gateway/mod.rs
  • clients/agent-runtime/src/cost/tracker.rs
📚 Learning: 2026-02-17T12:31:17.076Z
Learnt from: CR
Repo: dallay/corvus PR: 0
File: clients/agent-runtime/AGENTS.md:0-0
Timestamp: 2026-02-17T12:31:17.076Z
Learning: Applies to clients/agent-runtime/src/**/*.rs : Never log secrets, tokens, raw credentials, or sensitive payloads in any logging statements

Applied to files:

  • clients/agent-runtime/src/gateway/cost.rs
  • clients/agent-runtime/src/cost/tracker.rs
🔇 Additional comments (5)
clients/agent-runtime/src/agent/agent.rs (1)

2930-3009: Nice coverage on the new budget lifecycle telemetry.

These tests pin warning, exceeded, and override observer emissions with session IDs, which should catch regressions in the new observability path.

clients/agent-runtime/src/gateway/mod.rs (4)

296-310: Alias normalization and deprecated-field guard look correct.

This avoids false restart-required conflicts when the deprecated max_cost_per_day_cents alias is present alongside canonical fields.


1169-1172: Good fail-closed initialization for shared cost state.

Creating the shared tracker during startup prevents governance from silently running without state when toggled later.

Based on learnings: Do not silently weaken security policy or access constraints; keep default behavior secure-by-default with deny-by-default where applicable.


1749-1765: Machine-readable budget_exceeded mapping is consistent across webhook and stream paths.

Nice contract shape (code, governance_domain, period, current_usd, limit_usd) for client automation.

Also applies to: 2048-2061


1295-1310: Auth/origin checks on admin cost routes confirmed.

Verified that both handle_admin_cost_reset and handle_admin_cost_override enforce the same security guards as other admin handlers: admin_origin_guard (rejects non-loopback origins with 403) and admin_requires_auth (rejects invalid tokens with 401) are called before any state mutation. Tests comprehensively validate token validation and loopback-origin enforcement for both endpoints.

Comment on lines +316 to +357
let pending_total_usd;
let pending_mission_usd;
{
let pending = self.lock_pending_reservations();
pending_total_usd = pending
.values()
.map(|reservation| reservation.estimated_cost_usd)
.sum();
pending_mission_usd = mission_scope.map(|scope| {
pending
.values()
.filter(|reservation| {
reservation.mission_id.as_deref() == Some(scope.mission_id.as_str())
})
.map(|reservation| reservation.estimated_cost_usd)
.sum()
});
}

let check = self.check_budget_with_pending_scope(
estimated_cost_usd,
mission_scope,
pending_total_usd,
pending_mission_usd,
)?;

let mut override_applied = None;
if matches!(check, BudgetCheck::Exceeded { .. }) && self.config().allow_override {
override_applied = self.consume_override_if_active(now)?;
}

let proceed = !matches!(check, BudgetCheck::Exceeded { .. }) || override_applied.is_some();
let reservation = if proceed && estimated_cost_usd > 0.0 {
let reservation = CostBudgetReservation {
id: uuid::Uuid::new_v4().to_string(),
estimated_cost_usd,
mission_id: mission_scope.map(|scope| scope.mission_id.clone()),
created_at: now,
};
self.lock_pending_reservations()
.insert(reservation.id.clone(), reservation.clone());
Some(reservation)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Make reservation accounting atomic.

reserve_budget_for_request computes pending_total_usd from one snapshot, drops the mutex, and only inserts the reservation later. Two concurrent callers can both approve against the same pending set and both insert, which lets them oversubscribe the budget under load. Keep the read/check/insert sequence in one critical section, or use another atomic reservation primitive.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 7, 2026

@yacosta738 yacosta738 merged commit 81c72ad into main Apr 7, 2026
13 checks passed
@yacosta738 yacosta738 deleted the feature/dallay-164-cost-governance-productization branch April 7, 2026 08:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Productize cost governance and budget enforcement across runtime surfaces

1 participant