feat(cost): productize budget governance across runtime surfaces#449
Conversation
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.
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds 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
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
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
Estimated code review effort🎯 5 (Critical) | ⏱️ ~110 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
✅ Contributor ReportUser: @yacosta738
Contributor Report evaluates based on public GitHub activity. Analysis period: 2025-04-07 to 2026-04-07 |
There was a problem hiding this comment.
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 | 🟠 MajorReset
cost_usd_lastwhenAgentEndhas 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 everyAgentEnd(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 | 🟠 MajorDisabling the dispatcher currently disables cost governance too.
state.cost_trackeris only threaded intowebhook_dispatch::execute(). The legacy/webhookand/web/chat/streambranches still go straight tosimple_chat(), so flippingwebhook_dispatcher_enabledbypasses budget checks/counting on both gateway surfaces. If legacy must remain, reject those branches whileconfig.cost.enabledor 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 | 🟠 MajorFail closed on any
Blockedevaluation.The catch-all
BudgetEvaluation::Blocked { .. } => Ok(())lets execution continue if the service ever returns a blocked shape that does not match the exactBudgetCheck::Exceededpattern 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
📒 Files selected for processing (38)
clients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/config/mod.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/cost/tracker.rsclients/agent-runtime/src/cost/types.rsclients/agent-runtime/src/gateway/admin.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/gateway/webhook_dispatch.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/observability/log.rsclients/agent-runtime/src/observability/mod.rsclients/agent-runtime/src/observability/otel.rsclients/agent-runtime/src/observability/prometheus.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/security/policy.rsclients/agent-runtime/src/tools/delegate.rsclients/agent-runtime/tests/admin_config_api_integration.rsclients/web/apps/dashboard/src/components/config/CostOverview.spec.tsclients/web/apps/dashboard/src/components/config/CostOverview.vueclients/web/apps/dashboard/src/composables/useAdmin.tsclients/web/apps/dashboard/src/composables/useCostGovernance.spec.tsclients/web/apps/dashboard/src/composables/useCostGovernance.tsclients/web/apps/dashboard/src/types/admin-config.tsclients/web/packages/locales/src/en.jsonclients/web/packages/locales/src/es.jsonopenspec/changes/2026-04-06-cost-governance-productization/state.yamlopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.mdopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/proposal.mdopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/specs/cost-governance/spec.mdopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/state.yamlopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.mdopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/verify-report.mdopenspec/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.jsonclients/agent-runtime/tests/admin_config_api_integration.rsclients/agent-runtime/src/tools/delegate.rsclients/agent-runtime/src/config/mod.rsclients/web/packages/locales/src/es.jsonclients/web/apps/dashboard/src/composables/useCostGovernance.spec.tsclients/web/apps/dashboard/src/components/config/CostOverview.spec.tsopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/state.yamlopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/verify-report.mdclients/agent-runtime/src/observability/mod.rsclients/agent-runtime/src/security/policy.rsopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.mdclients/agent-runtime/src/observability/prometheus.rsclients/agent-runtime/src/bootstrap/mod.rsopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.mdclients/agent-runtime/src/observability/otel.rsclients/web/apps/dashboard/src/components/config/CostOverview.vueopenspec/specs/cost-governance/spec.mdopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/specs/cost-governance/spec.mdclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/observability/log.rsclients/web/apps/dashboard/src/composables/useCostGovernance.tsclients/agent-runtime/src/gateway/admin.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/web/apps/dashboard/src/composables/useAdmin.tsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/gateway/mod.rsclients/web/apps/dashboard/src/types/admin-config.tsclients/agent-runtime/src/gateway/webhook_dispatch.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/cost/types.rsclients/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, andcargo testfor code validation, or document which checks were skipped and why
Files:
clients/agent-runtime/tests/admin_config_api_integration.rsclients/agent-runtime/src/tools/delegate.rsclients/agent-runtime/src/config/mod.rsclients/agent-runtime/src/observability/mod.rsclients/agent-runtime/src/security/policy.rsclients/agent-runtime/src/observability/prometheus.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/observability/otel.rsclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/observability/log.rsclients/agent-runtime/src/gateway/admin.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/gateway/webhook_dispatch.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/cost/types.rsclients/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.rsclients/agent-runtime/src/tools/delegate.rsclients/agent-runtime/src/config/mod.rsclients/agent-runtime/src/observability/mod.rsclients/agent-runtime/src/security/policy.rsclients/agent-runtime/src/observability/prometheus.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/observability/otel.rsclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/observability/log.rsclients/agent-runtime/src/gateway/admin.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/gateway/webhook_dispatch.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/cost/types.rsclients/agent-runtime/src/cost/tracker.rs
clients/agent-runtime/src/tools/**/*.rs
📄 CodeRabbit inference engine (clients/agent-runtime/AGENTS.md)
Implement
Tooltrait insrc/tools/with strict parameter schema, validate and sanitize all inputs, and return structuredToolResultwithout 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.rsclients/agent-runtime/src/security/policy.rsclients/agent-runtime/src/gateway/admin.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/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.rsclients/agent-runtime/src/config/mod.rsclients/agent-runtime/src/observability/mod.rsclients/agent-runtime/src/security/policy.rsclients/agent-runtime/src/observability/prometheus.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/observability/otel.rsclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/observability/log.rsclients/agent-runtime/src/gateway/admin.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/gateway/webhook_dispatch.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/cost/types.rsclients/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.rsclients/agent-runtime/src/config/mod.rsclients/agent-runtime/src/security/policy.rsclients/agent-runtime/src/gateway/admin.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/gateway/webhook_dispatch.rsclients/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.mdopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.mdopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.mdopenspec/specs/cost-governance/spec.mdopenspec/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.rsclients/agent-runtime/src/config/mod.rsclients/agent-runtime/src/observability/mod.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/observability/log.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/gateway/webhook_dispatch.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/cost/types.rsclients/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.rsclients/agent-runtime/src/main.rsclients/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.rsclients/agent-runtime/src/observability/mod.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/observability/log.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/agent/agent.rsclients/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.rsclients/agent-runtime/src/observability/mod.rsopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.mdclients/agent-runtime/src/main.rsclients/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.mdopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/tasks.mdopenspec/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.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/cost/types.rsclients/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.rsclients/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.rsclients/agent-runtime/src/security/policy.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/cost/service.rsclients/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.rsclients/agent-runtime/src/security/policy.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/gateway/admin.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/observability/traits.rsclients/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.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/cost/service.rsclients/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.rsclients/agent-runtime/src/main.rsclients/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
IterationBudgetExceededandCostBudgetExceededunder the sameBudgetExceededstatus. The{ .. }wildcard pattern is appropriate for named struct variants, confirmed by theAgentExecutionErrordefinition inagent.rs.clients/agent-runtime/tests/admin_config_api_integration.rs (3)
71-95: LGTM!Test fixture correctly updated to include the new
cost_tracker: Nonefield inAppState.
149-172: LGTM!Request struct properly includes the new
cost: Nonefield.
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_fieldsfield.
93-115: LGTM!Good coverage for the deprecated field aliasing. The test validates that the legacy
max_cost_per_day_centskey is properly tracked indeprecated_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
useCostGovernanceanduseAdminquickly.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
surfacefield is handled correctly.
69-100: Redaction for override events is correctly implemented.The helper properly uses
event.redacted_actor(),event.redacted_reason(), andredact_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_lastgauge 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. Thesurfacefield inBudgetWarningandBudgetExceededis populated fromself.budget_surface(), which provides system identifiers (e.g., "agent_loop") rather than user-controlled data. The apparent inconsistency withBudgetOverrideredaction 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, anddeprecated_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 > 0warn_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(), andredact_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
surfacefield inBudgetThresholdEventis populated with system identifiers like"agent_loop"and"cli", not sensitive data. Logging it directly inBudgetWarningandBudgetExceededevents is correct and complies with the guideline against logging secrets.Note:
BudgetOverrideredacts surface viaformat_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.valuewith 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
usageUnavailableandusageErrorwith 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-budgetflag additions.The boolean flag approach is clean and the help text clearly communicates "one over-budget request" semantics, matching the
NextRequestscope in the override implementation.Also applies to: 167-171
269-357: LGTM: Cost CLI command structure and enum mappings.The
CostCommandsenum withSummary,History, andResetsubcommands is well-structured. TheFromimplementations for mapping CLI enums to internal types are correct and exhaustive. TheCliSessionSurfacehelper provides clear actor attribution for override auditing.
917-936: LGTM:handle_cost_commanddispatch.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_overrideimplementation.Correctly delegates to
agent.apply_next_request_budget_overrideand outputs machine-readable key=value format with the override ID for auditability.
1034-1041: LGTM:print_cli_session_summaryhandles None gracefully.The function correctly handles
Option<CostGovernanceSummary>by simply not printing when cost tracking is disabled or unavailable, which aligns with thesession_cost_summarybehavior returningOk(None)in those cases.
960-989: LGTM:render_cost_summaryoutput 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_ratefield is configured.
2598-2660: LGTM: CLI contract tests for new cost commands.Comprehensive coverage verifying
--override-budgetflag 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_agenthelper.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_summaryandhandle_cost_historyboth callgateway::utils::admin_origin_guard()andgateway::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 viaself.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) setscost_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
openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
Show resolved
Hide resolved
openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
Show resolved
Hide resolved
Harden runtime budget reservation, session-aware telemetry, mission budget cleanup, disabled gateway behavior, cost config validation, and dashboard operator controls based on review feedback.
Deploying corvus with
|
| 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 |
There was a problem hiding this comment.
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 | 🟠 MajorAdd
#[serde(deny_unknown_fields)]toCostConfigandModelPricingstructs.
Configenforces strict deserialization at the top level, butCostConfigandModelPricingaccept unknown fields. A configuration typo likesession_limit_usd_orinptwill 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_usdis 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 | 🟠 MajorDon'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()inclients/agent-runtime/src/agent/mission.rsalready 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
⛔ Files ignored due to path filters (1)
clients/agent-runtime/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (20)
clients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/cost/tracker.rsclients/agent-runtime/src/cost/types.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/observability/otel.rsclients/agent-runtime/src/observability/prometheus.rsclients/agent-runtime/src/observability/traits.rsclients/web/apps/dashboard/src/components/config/CostOverview.spec.tsclients/web/apps/dashboard/src/components/config/CostOverview.vueclients/web/apps/dashboard/src/composables/useAdmin.tsclients/web/apps/dashboard/src/composables/useCostGovernance.tsclients/web/apps/dashboard/src/types/admin-config.tsopenspec/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.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/observability/otel.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/cost/types.rsclients/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, andcargo testfor code validation, or document which checks were skipped and why
Files:
clients/agent-runtime/src/observability/prometheus.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/observability/otel.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/cost/types.rsclients/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.rsclients/agent-runtime/src/bootstrap/mod.rsclients/agent-runtime/src/observability/otel.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/cost/types.rsclients/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.rsclients/agent-runtime/src/bootstrap/mod.rsclients/web/apps/dashboard/src/components/config/CostOverview.spec.tsclients/web/apps/dashboard/src/types/admin-config.tsclients/agent-runtime/src/observability/otel.rsopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.mdclients/agent-runtime/src/main.rsclients/web/apps/dashboard/src/composables/useAdmin.tsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/gateway/cost.rsclients/web/apps/dashboard/src/components/config/CostOverview.vueclients/agent-runtime/src/cost/mod.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/cost/service.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/gateway/mod.rsclients/web/apps/dashboard/src/composables/useCostGovernance.tsclients/agent-runtime/src/cost/types.rsclients/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.rsclients/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.rsclients/agent-runtime/src/config/schema.rsclients/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.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/agent/mission.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/observability/traits.rsclients/agent-runtime/src/gateway/mod.rsclients/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.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/agent/agent.rsclients/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.rsclients/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.rsclients/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.rsclients/agent-runtime/src/agent/mission.rsclients/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.rsclients/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.rsclients/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.rsclients/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_overridebehindconfig.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_fieldsredacts sensitive actor and reason values while preserving non-sensitive operational fields.
99-101: Inconsistentsurfaceredaction in override event handling.
surfaceis 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:
- An accidental discovery that override events need redaction while other threshold events don't, or
- An oversight where all events should be treated uniformly.
Add a comment explaining why override trace fields redact
surfacewhile 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
CostServiceand 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_overrideis 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) fromAdminSummaryPeriod(includes "mission" for summary responses), addressing the past review feedback.
188-199: No action needed—percent_used_sessionis always present.The TypeScript tests and backend implementation both confirm that
percent_used_sessionis 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_overridecleanly 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
Costsubcommand withSummary,History, andResetvariants follows the established CLI patterns. The--reasonflag 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
OverrideGrantedandOverrideConsumedwith 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 injectedauthHeadersparameter already setsContent-Type: application/jsonexplicitly. InuseCostGovernance.ts(which uses this composable) and in tests, theauthHeadersfunction 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=trueprevents 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
openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
Outdated
Show resolved
Hide resolved
openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
Outdated
Show resolved
Hide resolved
openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
Outdated
Show resolved
Hide resolved
openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
Show resolved
Hide resolved
…ce-productization' into feature/dallay-164-cost-governance-productization
There was a problem hiding this comment.
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 | 🟠 MajorPartial model prices silently default to free tokens—validate that custom pricing is complete.
ModelPricingfields default to0.0via#[serde(default)], andvalidate_cost_config()accepts0.0as valid. A user providing onlyinputoroutputin their config will have the missing field silently set to0.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 validatewarn_at_percentbounds (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
📒 Files selected for processing (10)
clients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/cost/tracker.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/agent-runtime/src/main.rsclients/web/apps/dashboard/src/components/config/CostOverview.spec.tsclients/web/apps/dashboard/src/components/config/CostOverview.vueclients/web/apps/dashboard/src/composables/useCostGovernance.tsopenspec/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.tsopenspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.mdclients/web/apps/dashboard/src/composables/useCostGovernance.tsclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/web/apps/dashboard/src/components/config/CostOverview.vueclients/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.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/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, andcargo testfor code validation, or document which checks were skipped and why
Files:
clients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/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.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/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.rsclients/agent-runtime/src/gateway/cost.rsclients/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.rsclients/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.mdclients/agent-runtime/src/agent/agent.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/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.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/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.rsclients/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.rsclients/agent-runtime/src/config/schema.rsclients/agent-runtime/src/main.rsclients/agent-runtime/src/gateway/cost.rsclients/agent-runtime/src/gateway/mod.rsclients/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.rsclients/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.rsclients/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.rsclients/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.rsclients/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.rsclients/agent-runtime/src/gateway/mod.rsclients/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.rsclients/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_centsalias 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-readablebudget_exceededmapping 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_resetandhandle_admin_cost_overrideenforce the same security guards as other admin handlers:admin_origin_guard(rejects non-loopback origins with 403) andadmin_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.
| 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) |
There was a problem hiding this comment.
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.
clients/web/apps/dashboard/src/composables/useCostGovernance.ts
Outdated
Show resolved
Hide resolved
openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
Show resolved
Hide resolved
openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productization/design.md
Show resolved
Hide resolved
…nd enhance error handling
|



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
CostServiceoverCostTrackerCLI
corvus cost summarycorvus cost historycorvus cost reset--override-budgettocorvus agentandcorvus codeGateway / Admin API
/web/cost/summary/web/cost/history/web/admin/cost/reset/web/admin/cost/override/api/web/...routesbudget_exceededresponsesDashboard
Observability
BudgetWarningBudgetExceededBudgetOverrideGovernance cleanup
max_cost_per_day_centsmax_actions_per_hourcost-governancespecValidation
Runtime
cargo fmt --all -- --check✅cargo clippy --all-targets -- -D warnings✅cargo test✅Dashboard changed-area validation
Notes
openspec/changes/archive/2026-04-06-2026-04-06-cost-governance-productizationOpenSpec
openspec/specs/cost-governance/spec.mdCloses: #260