Skip to content
nexus-agents

What Does a Governed Decision Cost?

Status: Active Date: 2026-06-17 Epic: G — Cost telemetry per governed decision (#3854) Sources: #3855 (per-decision rollup), #3856 (weather_report + manifest cost profiles), #3857 (this doc)

This is the honest answer to “what does a governed decision cost?” — grounded in the cost telemetry that actually ships, not in projected figures. It explains how cost accrues, what is measured today versus what is a known floor, and how to read the surfaces that report it.

TL;DR

  • A governed decision — a consensus_vote or pr_review run — fans out to N independent voter calls. Cost is rolled up per-voter → per-decision: each voter contributes {role, model, inputTokens, outputTokens, costUsd}, and the decision summary totals them (packages/nexus-agents/src/observability/decision-cost.ts).
  • Unmeasured is not $0. A voter that reports no usage (a subscription-CLI adapter that doesn’t surface tokens, an error vote that never reached a model) is counted as unmeasured and contributes 0 to the totals — but the summary records that the total is a floor, not an exact figure.
  • Billing mode changes what the dollar figure means: plan records $0 cost while keeping token counts (spend is pre-covered by a subscription); api records the registry-priced dollar cost. Default is plan.
  • As of this writing, per-voter token counts are largely unmeasured pending adapter propagation (#3910). Treat the dollar/token totals as a floor and the per-decision counts (how many voters, which models) as the reliable signal until #3910 lands.
  • The weather_report cost section surfaces per-gate-type aggregates over a lookback window plus each strategy’s declared costProfile.

How a governed decision accrues cost

A governed decision is one panel run that fans out to multiple LLM calls:

Gate Panel Roles
consensus_vote Up to a 7-voter panel of independent perspectives architect, security, devex, and more (strategy consensus, profile high)
pr_review A 5-role adversarial panel wrapping consensus architect, security, devex, catfish, scope_steward (PR_REVIEW_EVAL_ROLES)

Each voter seat may make several model calls. The legacy summary keeps final-seat usage; the response events described below retain observed attempts. Per-call token

  • cost telemetry already existed (the usage log, packages/nexus-agents/src/learning/usage-log.ts). The cost-telemetry epic added the aggregation layer that rolls those per-call numbers up into one per-decision answer.

The flow is:

  1. The panel runs; each voter returns a result carrying its role, model, and — where the adapter reported them — inputTokens / outputTokens (votesToCostInputs in packages/nexus-agents/src/mcp/tools/decision-cost-recording.ts).
  2. Each voter’s dollar cost, when tokens are present, is derived with the same registry-backed computeCostUSD the per-call usage log uses — so a per-decision rollup and the per-call log price identically.
  3. rollupDecisionCost (pure, in observability/decision-cost.ts) folds the N voters into a DecisionCostSummary: total tokens, total USD, a per-voter breakdown, a per-model breakdown, and the measured/unmeasured voter split.
  4. The summary is persisted one record per decision in the DecisionCostStore (observability/decision-cost-store.ts, JSONL, bounded retention) and attached to the decision response. No new MCP tool — it rides the existing surface.

So “what did this decision cost?” is answerable from recorded data: read the DecisionCostStore record for the decision id, or the per-gate aggregate in the weather report.

The measured-vs-unmeasured floor (the honesty rule)

The single most important thing to understand about these numbers: a missing cost is treated as unmeasured, never as a true $0.

A voter contributes “measured” usage only if it reported a token count or a cost. A voter with none — a CLI-subscription adapter that does not surface usage, an error vote that never reached the model, a simulated vote — is folded in as unmeasured. It contributes 0 to the totals (there is nothing else it can contribute), but the summary records that fact:

  • measuredVoters / unmeasuredVoters / voterCount — the confidence split.
  • A total is a floor whenever unmeasuredVoters > 0: the real cost is at least this, because unmeasured voters’ unknown real cost was counted as 0.

Treating unmeasured as a real $0 would silently understate spend. The system refuses to do that. When you read any total, read it alongside the measured/unmeasured split. In the per-gate aggregate this is surfaced as an explicit costIsFloor boolean.

Current state (#3910). Per-voter token counts are largely unmeasured today: the adapter layer does not yet propagate per-voter tokens through the vote result for every adapter, and decision-cost drops are currently silent. #3910 tracks both fixes. Until it lands, expect unmeasuredVoters > 0 on most decisions, the dollar/token totals to be a floor (often $0 under plan mode), and the reliable signal to be the structural facts: how many voters, which models, which gate. Do not quote a per-decision dollar figure as exact.

Plan vs api billing mode

Cost is recorded under a billing mode (mirrors NEXUS_BILLING_MODE):

Mode Dollar cost recorded Tokens recorded When it applies
plan $0 (pre-covered by subscription) kept Default. Subscription/CLI auth; spend is flat-rate, not per-token
api registry-priced USD per voter call kept NEXUS_BILLING_MODE=api; metered per-token API keys

Plan mode zeroes the dollar cost but keeps the token counts, so an operator can still see consumption and a later api-mode reprice is possible. This mirrors how plan mode zeroes cost in routing/scoring without dropping the token signal. The default resolves to plan (resolveBillingMode in decision-cost-recording.ts), so unless you have set NEXUS_BILLING_MODE=api, the dollar totals you see are $0 by construction — the meaningful number under plan mode is tokens, not dollars.

Reading the weather_report cost section

weather_report carries a costSection (Epic G, #3856; see the CostSection type in packages/nexus-agents/src/mcp/tools/weather-report-types.ts) with two parts:

1. Per-gate decision-cost aggregates (decisionCosts)

aggregateDecisionCosts (packages/nexus-agents/src/observability/decision-cost-aggregate.ts) rolls the persisted per-decision records up into one per-gate answer over a lookback window:

  • gate — consensus_vote or pr_review.
  • decisionCount — how many decisions folded in.
  • avgCostUsd, avgTokens, avgVoters — the per-decision means.
  • totalCostUsd, totalTokens — the window sums.
  • measuredVoters / unmeasuredVoters — the confidence split.
  • costIsFloor — true when any voter was unmeasured: the averages understate true spend.

Read costIsFloor first. When it is true (the common case today, per #3910), the averages are a lower bound; lean on avgVoters and the per-gate decision count for the structural story rather than the dollar mean.

2. Strategy cost profiles (strategyCostProfiles)

Each registered strategy’s declared costProfile, read straight off the manifest registry (the single source of truth — it cannot drift from the authored manifest). See the next section.

Observed voter attempts and final seats (#6821)

The weekly costSection.consensusDecisionTokens.attemptTelemetry view reports observed outer-attempt usage, not all physical attempts. Its token totals come only from immutable response events in the existing decision-cost record, under the same decision ID. Legacy final-seat totals remain separate: do not add these two overlapping populations together. Legacy aggregate observedAttemptUsage is retained for compatibility; it is not event provenance.

At runVoteCompletion, each started outer adapter.complete increments observableAttempts. A response freezes role, CLI/adapter, known model and usage before vote parsing. Parsing classification is maintained separately; the cost bridge takes an immutable snapshot with one event per response. Parse failures, structured-output retries, role retries and CLI fallbacks retain their events. Kinds identify the first call as initial, a subsequent call within an execution chain as parse_retry (including a transport/structured retry), a relaunched role’s first call as role_retry, and a fallback chain’s first call as cli_fallback. Subsequent retry calls take parse_retry precedence. Events are grouped by carried execution chain; array order is not a wall-clock chronology. Outcomes distinguish parsed, parse_failed, discarded superseded responses and the retained final response.

Usage is reported only when both valid input/output counters are present; explicit zero is measured. Partial, missing or placeholder input counters are unknown, never measured zero. Cache-read, cache-creation and reasoning counters remain optional subtotals and are not added to input/output totals.

A ModelError or timeout without a response records no response event. Because the outer call was observed starting, it contributes to observableAttempts and unobservedAttempts, not to the settled-response coverage denominator. Coverage is reportedAttempts / observedAttempts; read it with the separate unobserved count. Adapter-internal physical calls cannot be counted at this seam.

A raw response arriving after the deadline is included as superseded if it arrives before the bridge snapshots inputs for synchronous persistence. Settlement after that snapshot cannot change the persisted events; it remains unobserved in that decision record. The observer does not wait for outstanding calls.

Empty or wholly unknown cohorts report unmeasured with null token totals; coverage is null when no response events exist. Explicit reported zeros yield measured zero totals. Legacy records without events increment decisionsLackingTelemetry; they do not assert zero attempts. Invalid events, contradictory counts, duplicate identities, multiple final responses for a role, and responses attributed to an absent role reject durable history. Reused event IDs across decisions exclude all affected histories from the join and expose invalid-record coverage, consistent with the existing damaged-cost behavior.

What each strategy’s costProfile means

costProfile is a coarse, authored cost hint scaled by a strategy’s fan-out — not a measured dollar figure. The enum is low | medium | high | variable (CostProfileSchema, packages/nexus-agents/src/orchestration/strategy-manifest.ts). It is declared for every live strategy and refreshed against the measured per-decision aggregates the DecisionCostStore records.

Profile Meaning
low One model call — the cheapest engine
medium A templated multi-stage gate — bounded fan-out
high An N-voter panel / multi-agent orchestration / greenfield build
variable Spend scales with input size (graph topology, research breadth)

The eight live strategies (packages/nexus-agents/src/orchestration/strategy-manifest-registry.ts):

Strategy Entrypoint tool costProfile Why
single-shot delegate_to_model low One model call
dev-pipeline run_dev_pipeline medium Multi-stage dev gate (test / lint / typecheck)
pipeline run_pipeline medium Templated multi-stage pipeline
graph-workflow run_graph_workflow variable Spend scales with graph topology
orchestrate orchestrate high Multi-agent fan-out (wave / aflow / puppeteer)
consensus consensus_vote high Up to a 7-voter panel — N calls per decision
spec execute_spec high Greenfield multi-stage build from a spec
research run_pipeline variable Spend scales with research breadth

A rough ordering of governed spend, then, runs from a single-shot delegate (low, one call) → a dev-pipeline / pipeline gate (medium, bounded stages) → a full consensus_vote or pr_review panel and orchestrate / spec runs (high, N calls per decision). variable strategies (graph, research) sit wherever their input size places them. These are hints for cost-aware routing, not billing-grade numbers — the billing-grade numbers come from the measured aggregates above once #3910 lands per-voter token propagation.

Methodology and variance notes

  • Measured numbers come only from api billing mode with reporting adapters. Under plan mode (the default) the dollar figure is $0 by design; tokens are the signal.
  • All totals are a floor when unmeasuredVoters > 0. This is the dominant caveat today (#3910) — most per-decision totals are floors.
  • Variance is real and not yet characterized. Panel fan-out (a consensus panel can run up to 7 voters; pr_review runs 5 roles), model mix, and prompt size all move per-decision cost. The aggregate reports means over a window, not a distribution; do not read avgCostUsd as a tight estimate.
  • Any cost claim in README/positioning must trace to measured data, not to this doc’s structural description. Until measured per-decision aggregates with propagated per-voter tokens exist (#3910), positioning should describe the shape of governed-decision cost (per-voter rollup, floor honesty, profile ordering) rather than quote a dollar figure. This honesty is the evidence base for any future batch-mode decision (#2699).

References

  • Epic: #3854 — cost telemetry per governed decision
  • #3855 — per-voter per-decision cost aggregation
  • #3856 — weather_report + manifest cost profiles
  • #3857 — this doc
  • #3910 — propagate adapter per-voter tokens; make decision-cost drops non-silent
  • #2699 — batch-submission mode (cost data here is the trigger evidence)
  • Per-decision rollup: packages/nexus-agents/src/observability/decision-cost.ts
  • Persistence: packages/nexus-agents/src/observability/decision-cost-store.ts
  • Per-gate aggregate: packages/nexus-agents/src/observability/decision-cost-aggregate.ts
  • Recording bridge: packages/nexus-agents/src/mcp/tools/decision-cost-recording.ts
  • Weather report cost section: packages/nexus-agents/src/mcp/tools/weather-report-types.ts (CostSection)
  • Strategy cost profiles: packages/nexus-agents/src/orchestration/strategy-manifest-registry.ts, strategy-manifest.ts (CostProfileSchema)
  • MCP tool count: see packages/nexus-agents/src/mcp/tools/tool-manifest.ts (do not hardcode)