Skip to content

spec: make budget accounting concurrency-safe - #46

Open
santosomar wants to merge 2 commits into
CiscoDevNet:mainfrom
santosomar:codex/budget-accounting-semantics
Open

santosomar wants to merge 2 commits into
CiscoDevNet:mainfrom
santosomar:codex/budget-accounting-semantics

Conversation

@santosomar

Copy link
Copy Markdown
Contributor

Summary

Make Foundry's budget governor concurrency-safe and its accounting, trailing-yield, restart, and hot-reload behavior deterministic.

Closes #45.

Sync Impact Report

Version change   : constitution 0.2.0 unchanged; spec 0.1.0 → 0.2.0  [MINOR: budget semantics strengthened]
Principles       : V, VI, XI unchanged and re-validated against FR-112a, FR-113, FR-115, FR-116a
Sections changed : spec §1.6–§1.7, §3.2–§3.3, §4.4–§4.5, §5.1, §9.3–§9.4, §11.2, §12
Templates needing update : n/a
Downstream re-check      : spec.md ✓  README.md ✓  principle-fr-matrix.md ✓  plan.md n/a  tasks.md n/a
Follow-up TODOs  : none
Last sync        : 2026-09-10

Failure case

FR-112 previously checked spend after calls were made. In a concurrent fleet, several agents could observe the same remaining budget and dispatch calls before the next governor poll, overshooting a supposed hard ceiling. The spec also left active-runtime calculation, per-call accounting identity, price-version handling, trailing spend-window boundaries, stop settlement, and cap hot reload ambiguous, allowing status/dashboard disagreement across otherwise conforming implementations.

FR changes

  • FR-011: make restart eligibility and explicit accounting reset behavior testable.
  • FR-112 / FR-112a: define active runtime and require atomic maximum-cost reservation before provider dispatch, followed by exactly-once settlement.
  • FR-113: require an idempotent per-call ledger with attribution, billable token categories, immutable rate-card identity, and a currency-weighted estimated fraction.
  • FR-115: define the trailing spend window as a deterministic suffix of settled accounting events.
  • FR-116 / FR-116a: retain the coverage/minimum-runtime/window/yield conjunction and define an idempotent stop transition.
  • FR-128: define cap lowering, raising, restart-block clearing, and explicit reset semantics.
  • Add SC-010 and operational conformance cases for concurrent admission and accounting replay.

Constitutional alignment

  • Principle V: reservations enforce the operator's total-spend authorization; they do not impose a request-rate or concurrency ceiling while authorized budget remains. Provider 429 handling remains unchanged.
  • Principle VI: low yield still cannot stop an evaluation until the trailing window is full, minimum active runtime has elapsed, and coverage is complete.
  • Principle XI: admission, settlement, event replay, and stopping now have explicit atomic/idempotent guarantees.

No constitutional wording or version changes.

Documentation updates

  • Add reserve/settle pseudocode, active-runtime rules, stop/hot-reload behavior, anti-patterns, and conformance cases.
  • Add the budget substrate contract and expand observability requirements.
  • Update the FR index, principle matrix, glossary mirrors, adoption guidance, open-question checklist, changelog, version badge, and Sync Impact Report archive.

Validation

  • git diff --check
  • Spec FR definitions exactly match the FR index membership, including FR-112a and FR-116a.
  • All relative Markdown file links in changed files resolve locally.
  • Principle × FR matrix remains OK for all eleven principles.
  • /speckit.analyze could not be run because this seed checkout does not include or install that command; plan.md and tasks.md are not present.

Copilot AI lite review requested due to automatic review settings September 10, 2026 18:20

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

A few newly-updated docs contain internal terminology/contract inconsistencies (e.g., “maximum output allowance” vs “maximum output-token allowance”, “cached input” vs “cached-input”, and Sync Impact Report regeneration wording) that should be reconciled before merging.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR bumps the spec to v0.2.0 and strengthens the budget-governor requirements to be concurrency-safe and deterministic by specifying atomic cost reservation + exactly-once settlement, active-runtime semantics, deterministic trailing yield windows, and idempotent stop/hot-reload behavior.

Changes:

  • Define atomic spend admission via conservative maximum-cost reservations (FR-112a) and an idempotent, attributed per-call accounting ledger (FR-113).
  • Make yield computation deterministic via a precise trailing spend-window suffix definition (FR-115) and add an idempotent stop transition (FR-116a).
  • Update downstream reference, adoption, operations, governance, glossary, and changelog docs to reflect the strengthened budget semantics and spec version bump.
File summaries
File Description
spec.md Bumps spec to 0.2.0; adds/strengthens FR-011, FR-112/112a/113, FR-115/116/116a, FR-128, plus new SC-010 and related terminology updates.
README.md Updates status badge to Seed v0.2.0 and trims trailing blank line.
GLOSSARY.md Aligns glossary definitions for Yield and Budget governor with the new settled-spend / reservation semantics.
docs/reference/terminology-quick-card.md Updates quick-card terminology for Budget governor and Yield to match the new definitions.
docs/reference/principle-fr-matrix.md Updates principle-to-FR mapping to include FR-112a and FR-116a under relevant principles.
docs/reference/open-questions-checklist.md Expands the §11.2 checklist item to include billable token categories, direct cost, and rate-card source.
docs/reference/fr-index.md Updates FR summaries and adds index entries for FR-112a and FR-116a.
docs/operations/observability-checklist.md Expands budget/yield observability requirements (settled vs reserved spend, admission state, window fullness, rate-card identity).
docs/operations/budget-and-stop-conditions.md Adds concrete admission/settlement pseudocode, active-runtime semantics, hot-reload/stop behavior, and conformance cases.
docs/governance/sync-impact-reports.md Archives the spec 0.1.0→0.2.0 Sync Impact Report and updates wording around when new entries are added.
docs/architecture/substrate-contracts.md Introduces an explicit “budget governance contract” API surface and required guarantees/anti-patterns.
docs/adoption/integration-decisions.md Updates required integration capabilities for maximum-output-token allowance, token categories, and immutable pricing snapshots.
docs/adoption/clarification-playbook.md Updates the §11.2 provider clarification guidance to include max-output allowance and rate-card/versioning considerations.
constitution.md Updates the Sync Impact Report header block to reflect the spec 0.1.0→0.2.0 change (constitution unchanged).
CHANGELOG.md Adds changelog entry for spec v0.2.0 and related documentation updates.
Review details
  • Files reviewed: 15/15 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread constitution.md Outdated
Comment thread docs/adoption/clarification-playbook.md
Comment thread docs/operations/budget-and-stop-conditions.md
Comment thread spec.md
| **A-1** | The operator is authorized to evaluate the target and to probe the testbed; authorization is established outside the system. | Out of scope (§14). |
| **A-2** | The operator has read access to the target's source code. | §1.5 clarification; black-box mode is out of scope (§14). |
| **A-3** | A frontier LLM with tool/function calling is available via API, with per-call token accounting. | §11.2 clarification. |
| **A-3** | A frontier LLM with tool/function calling is available via API, with a caller-set maximum output allowance and per-call accounting for billable token categories. | §11.2 clarification. |
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings September 10, 2026 18:33

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The deterministic trailing-window requirements rely on a durable settlement ordering key, but the accounting-event field list does not currently require storing the settlement timestamp/sequence needed to compute it consistently.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

spec.md:87

  • The spec uses “maximum output-token allowance” elsewhere (FR-112a, §11.2), but Assumption A-3 says “maximum output allowance”, which is ambiguous/inconsistent with the token-based budgeting terminology used in the rest of the PR.
| **A-3** | A frontier LLM with tool/function calling is available via API, with a caller-set maximum output allowance and per-call accounting for billable token categories. | §11.2 clarification. |

docs/adoption/clarification-playbook.md:44

  • This example uses “maximum output allowance” and “cached” token category wording, but the rest of the spec set uses “maximum output-token allowance” and “cached-input”. Aligning the example text avoids introducing alternate terminology for the same concepts.
- Good: "Provider X via internal gateway Y; rate limits are exposed via response headers; requests enforce a maximum output allowance; responses report input/output/cached/reasoning tokens; versioned rate cards are stored with accounting events."

docs/operations/budget-and-stop-conditions.md:30

  • Token category naming is inconsistent within the budget docs: the spec and observability checklist use “cached-input”, but this page uses “cached input”. Using a single term reduces ambiguity for implementers mapping provider fields into these categories.
- Token count by category when reported (input, output, cached input, reasoning).
  • Files reviewed: 15/15 changed files
  • Comments generated: 1
  • Review effort level: Lite

Comment thread spec.md
- **FR-113**: Spend tracking MUST account for every model call by every role. Where the provider does not report cost directly, the system MUST estimate from token counts and configured rates, and MUST surface what fraction of the reported total is estimated.
- **FR-112**: The Orchestrator MUST track cumulative settled LLM spend (in currency), outstanding cost reservations, and cumulative active wall-clock runtime across all runs of an evaluation. Active runtime MUST exclude operator-requested pauses and MUST NOT sum concurrent agent runtimes. The Orchestrator MUST invoke the FR-116a stop transition when settled spend reaches the spend cap or active runtime reaches the runtime cap. (US-5)
- **FR-112a**: Before dispatching any spend-creating model call, the budget governor MUST atomically reserve that call's conservative maximum cost against the spend cap, using the request's maximum output-token allowance and an immutable snapshot of the applicable provider/model rates. It MUST deny admission without contacting the provider when settled spend plus outstanding reservations plus the requested reservation would exceed the cap. On completion, failure, or cancellation it MUST settle the reservation exactly once against the billable actual or estimated cost and release any remainder. If a provider-reported charge exceeds its reservation, the governor MUST close new spend admission immediately and surface the discrepancy and any overrun as a degraded state. Budget admission enforces operator-authorized total spend; it is not the provider-rate pre-throttling prohibited by FR-105.
- **FR-113**: Spend tracking MUST record every model-call attempt by every role as a durable, idempotent accounting event. Each event MUST include a stable local call id; role and agent instance; provider and model; request, completion, or failure timestamps; outcome; input, output, cached-input, and reasoning token counts where reported; the immutable rate-card identity used for estimation; reserved cost; settled cost; and whether settled cost was provider-reported or estimated. A separately billed retry MUST have its own call id; replay of the same event MUST NOT count twice. Where the provider does not report cost directly, the system MUST estimate from token counts and configured rates. The surfaced estimated fraction MUST be estimated settled cost divided by total settled cost, not fraction of calls.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Crash recovery, delayed-cost reconciliation, deterministic event ordering, and safe reset behavior remain underspecified.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (1)

spec.md:733

  • FR-115 orders events by settlement timestamp and a durable sequence id, but neither field is required in this accounting-event schema; completion/failure time is not necessarily settlement time. Implementations can therefore satisfy FR-113 while being unable to reproduce the required window or tie-break equal timestamps. Require both fields and define a total sequence order in the ledger contract.
- **FR-113**: Spend tracking MUST record every model-call attempt by every role as a durable, idempotent accounting event. Each event MUST include a stable local call id; role and agent instance; provider and model; request, completion, or failure timestamps; outcome; input, output, cached-input, and reasoning token counts where reported; the immutable rate-card identity used for estimation; reserved cost; settled cost; and whether settled cost was provider-reported or estimated. A separately billed retry MUST have its own call id; replay of the same event MUST NOT count twice. Where the provider does not report cost directly, the system MUST estimate from token counts and configured rates. The surfaced estimated fraction MUST be estimated settled cost divided by total settled cost, not fraction of calls.
  • Files reviewed: 15/15 changed files
  • Comments generated: 5
  • Review effort level: Balanced

Comment thread spec.md
- **FR-112**: The Orchestrator MUST track cumulative LLM spend (in currency) and cumulative wall-clock runtime across all runs of an evaluation, halting the fleet when either exceeds an operator-set cap. (US-5)
- **FR-113**: Spend tracking MUST account for every model call by every role. Where the provider does not report cost directly, the system MUST estimate from token counts and configured rates, and MUST surface what fraction of the reported total is estimated.
- **FR-112**: The Orchestrator MUST track cumulative settled LLM spend (in currency), outstanding cost reservations, and cumulative active wall-clock runtime across all runs of an evaluation. Active runtime MUST exclude operator-requested pauses and MUST NOT sum concurrent agent runtimes. The Orchestrator MUST invoke the FR-116a stop transition when settled spend reaches the spend cap or active runtime reaches the runtime cap. (US-5)
- **FR-112a**: Before dispatching any spend-creating model call, the budget governor MUST atomically reserve that call's conservative maximum cost against the spend cap, using the request's maximum output-token allowance and an immutable snapshot of the applicable provider/model rates. It MUST deny admission without contacting the provider when settled spend plus outstanding reservations plus the requested reservation would exceed the cap. On completion, failure, or cancellation it MUST settle the reservation exactly once against the billable actual or estimated cost and release any remainder. If a provider-reported charge exceeds its reservation, the governor MUST close new spend admission immediately and surface the discrepancy and any overrun as a degraded state. Budget admission enforces operator-authorized total spend; it is not the provider-rate pre-throttling prohibited by FR-105.
Comment thread spec.md
- **FR-126**: The configuration MUST be a single document (or a single directory treated as one) under version control alongside the evaluation's outputs.
- **FR-127**: Secrets (API keys, tokens, testbed credentials) MUST NOT be stored in the configuration document. The configuration references them; a separate non-version-controlled mechanism supplies them.
- **FR-128**: The Orchestrator MUST hot-reload `budget` and `rules` changes at runtime, in addition to the `fleet` hot-reload required by FR-009.
- **FR-128**: The Orchestrator MUST hot-reload `budget` and `rules` changes at runtime, in addition to the `fleet` hot-reload required by FR-009. A spend cap lowered below settled spend plus outstanding reservations, or a runtime cap lowered below active runtime, MUST immediately invoke FR-116a. Raising the cap that caused an FR-011 restart block MUST clear that block. Hot reload and restart MUST NOT implicitly reset cumulative spend, reservations, runtime, or accounting history; reset requires an explicit operator action that is durably recorded.
Comment thread spec.md
Comment on lines +732 to +733
- **FR-112a**: Before dispatching any spend-creating model call, the budget governor MUST atomically reserve that call's conservative maximum cost against the spend cap, using the request's maximum output-token allowance and an immutable snapshot of the applicable provider/model rates. It MUST deny admission without contacting the provider when settled spend plus outstanding reservations plus the requested reservation would exceed the cap. On completion, failure, or cancellation it MUST settle the reservation exactly once against the billable actual or estimated cost and release any remainder. If a provider-reported charge exceeds its reservation, the governor MUST close new spend admission immediately and surface the discrepancy and any overrun as a degraded state. Budget admission enforces operator-authorized total spend; it is not the provider-rate pre-throttling prohibited by FR-105.
- **FR-113**: Spend tracking MUST record every model-call attempt by every role as a durable, idempotent accounting event. Each event MUST include a stable local call id; role and agent instance; provider and model; request, completion, or failure timestamps; outcome; input, output, cached-input, and reasoning token counts where reported; the immutable rate-card identity used for estimation; reserved cost; settled cost; and whether settled cost was provider-reported or estimated. A separately billed retry MUST have its own call id; replay of the same event MUST NOT count twice. Where the provider does not report cost directly, the system MUST estimate from token counts and configured rates. The surfaced estimated fraction MUST be estimated settled cost divided by total settled cost, not fraction of calls.
Comment thread constitution.md
Principles : III narrowed (work-reclamation only; session rotation per FR-118 carved out)
Sections changed : III
Version change : constitution 0.2.0 unchanged; spec 0.1.0 → 0.2.0 [MINOR: budget semantics strengthened]
Principles : V, VI, XI unchanged and re-validated against FR-112a, FR-113, FR-115, FR-116a

```
Version change : constitution 0.2.0 unchanged; spec 0.1.0 → 0.2.0 [MINOR: budget semantics strengthened]
Principles : V, VI, XI unchanged and re-validated against FR-112a, FR-113, FR-115, FR-116a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec amend: make budget enforcement concurrency-safe and accounting deterministic

2 participants