feat(surface,sdk,kernel): budget header + spend attribution (#306) - #315
Conversation
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 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 |
54792d2 to
cd32c54
Compare
Review swarm: maintainabilityNo fresh transcript was produced for run |
Review swarm: historyPR #315 — history reviewReviewed head: Changes requested: two history/contract findings. Budget authoring and kernel admission fit the RFC's direction, but the implementation reverses the segment boundary and the follow-up loses known usage. H1 — P1: preserve current-segment resume instead of depending on closed epochsLocation: Every budgeted state load now selects The drive log's 2026-09-09 entry “went to implement D2, found my own D2 text was wrong” already records this exact mistake for wake context: resolving across retained segments only appears correct while the engine does not roll and old segments are never pruned. The new budget code repeats that dependency to reconstruct daily and duration counters, without adding those counters to the epoch summary. Reading retained historical entries cannot supply the contract once those entries are archived; a current-segment-only fold cannot reconstruct these new counters from the present summary. It also makes each budgeted state load depend on the growing historical journal. The new completion timestamp lookup similarly searches every epoch. Keep state reconstruction within the current segment and carry the necessary accounting and active-attempt timing facts in its summary, with an explicit compatibility policy for old summaries. If that producer is deferred, state the limitation and sequence the feature accordingly rather than replace the read boundary. This finding is a direct contract conflict and static source comparison; it does not claim an archival failure was executed or that production engine rollover already exists. H2 — P1: the unpriced-model repair discards known tokens and defeats token admissionLocation: Commit The captured helper reproduction uses an allowed token-only envelope ( This contradicts gate 1's journal-based exact accounting, central since the bootstrap and reinforced by Story, scope, and commit-message assessment
Input recovery and verification limitsThe initial The sandbox has pre-existing executable-mode differences after metadata recovery; no tracked content differences were present before writing this review. Only this review is to be staged. No source, test, gate, or operating brief was edited, and no commit or external message was created. This is a history review with source comparison and a focused SDK helper reproduction, not a full correctness or CI signoff. Captured evidenceAll commands below ran from the repository root. Their output is literal; Exact target and supplied diff identityCommand: python3 -c 'import subprocess,json,pathlib
m=json.loads(pathlib.Path('"'"'.review-target/pr.json'"'"').read_text())
head=subprocess.check_output(['"'"'git'"'"','"'"'rev-parse'"'"','"'"'HEAD'"'"'],text=True).strip()
base=subprocess.check_output(['"'"'git'"'"','"'"'merge-base'"'"','"'"'main'"'"','"'"'HEAD'"'"'],text=True).strip()
diff=subprocess.check_output(['"'"'git'"'"','"'"'diff'"'"',base,'"'"'HEAD'"'"'])
print('"'"'metadata head:'"'"',m['"'"'headRefOid'"'"']); print('"'"'HEAD:'"'"',head); print('"'"'merge base:'"'"',base)
print('"'"'supplied diff matches git diff:'"'"',diff==pathlib.Path('"'"'.review-target/pr.diff'"'"').read_bytes())
assert head==m['"'"'headRefOid'"'"']; assert diff==pathlib.Path('"'"'.review-target/pr.diff'"'"').read_bytes()
p=subprocess.run(['"'"'git'"'"','"'"'diff'"'"','"'"'--numstat'"'"'],capture_output=True,text=True,check=True)
rows=p.stdout.splitlines(); print('"'"'tracked working-tree differences:'"'"',len(rows)); print('"'"'all have zero content additions/deletions:'"'"',all(r.startswith('"'"'0\t0\t'"'"') for r in rows))
'Captured output (exit 0): Required recent historyCommand: git log --oneline -40Captured output (exit 0): PR commit messagesCommand: git log --format=fuller 0daf55b8..HEADCaptured output (exit 0): H1: the old current-segment read and the new all-segment readCommand: git diff 0daf55b8 HEAD -- kernel/relayflowd/src/engine.rsCaptured output (exit 0): H1: bootstrap already used current-segment state reconstructionCommand: bash -c 'git show 46315715:kernel/relayflowd/src/engine.rs | sed -n '"'"'211,217p'"'"''Captured output (exit 0): H1: settled segment ruleCommand: sed -n 207,211p docs/RFC-0001-everything-is-a-relayflow.mdCaptured output (exit 0): H1: previously recorded cross-segment mistakeCommand: sed -n 7466,7506p ops/DRIVE-LOG.mdCaptured output (exit 0): H2: direct execution of head pricing and budget diagnostic helpersCommand: /home/daytona/node_modules/.bin/tsx -e 'import {workerSpend} from "./packages/sdk/src/worker-spend.ts"; import {budgetDiagnostics} from "./packages/sdk/src/budget-preflight.ts"; const flow = {version:"0.1.0",budget:{pricing:"frozen",maxTokens:1},steps:[{id:"s",type:"llm",prompt:"p",cli:"claude"}]}; console.log("token-only budget diagnostics:", JSON.stringify(budgetDiagnostics(flow as any))); const spent=workerSpend({exit_code:0,stdout_tail:"answer",stderr_tail:"",tokens_input:100,tokens_output:50}); console.log("decoded tokens:",spent.result.tokens_input,spent.result.tokens_output); console.log("completion usage:",JSON.stringify(spent.usage) ?? "undefined"); console.log("completion exit_code:",spent.result.exit_code);'Captured output (exit 0): H2: follow-up drops previously retained tokens for undefined modelCommand: git show cd32c541 -- packages/sdk/src/model-pricing.tsCaptured output (exit 0): H2: worker omits missing usageCommand: sed -n 70,83p packages/sdk/src/llm-worker.tsCaptured output (exit 0): H2: missing wire usage defaults to BudgetCommand: sed -n 75,88p kernel/relayflowd/src/server/wire.rsCaptured output (exit 0): H2: Budget default is zeroCommand: sed -n 310,322p kernel/relayflowd-core/src/entry.rsCaptured output (exit 0): H2: wire usage becomes completion budgetCommand: sed -n 359,370p kernel/relayflowd/src/server.rsCaptured output (exit 0): Original exact-accounting commitmentCommand: sed -n 95,101p docs/RFC-0001-everything-is-a-relayflow.mdCaptured output (exit 0): Prior exact-accounting workCommand: git log -1 --format=fuller 6394a2e9Captured output (exit 0): Operating brief and directivesCommand: cat ops/NEXT.md ops/DIRECTIVES.mdCaptured output (exit 0): test -n "$CLOUD_API_URL" grep -c CLOUD_API_ACCESS_TOKEN_EXPIRES_AT README.md # already 0 bash -n .github/workflows/scripts/swarm-post.sh && python3 -c "import yaml; yaml.safe_load(open('.github/workflows/review-swarm.yml'))" && grep -i "whitelist|github.event.pull_request.user.login" .github/workflows/review-swarm.yml || echo "No author whitelist found (GOOD)" git status --porcelain REVIEW_FAILED |
Review swarm: structureNo fresh transcript was produced for run |
|
🎯 review-swarm: FAILED (M:fail H:fail S:missing) Lens transcripts posted as sibling comments above. |
Review swarm: FAILED
Cloud run: |
83de496 to
be0faf2
Compare
Session-Id: 01a08f7f-7b46-78c3-84c7-097476f69e4a Session-Id: efeda5df-9b7c-48d4-b2ce-957f5bef0a82 Session-Id: efeda5df-9b7c-48d4-b2ce-957f5bef0a82 Session-Id: efeda5df-9b7c-48d4-b2ce-957f5bef0a82
…after CLI decode Cursor Bugbot HIGH: "Unlisted models fail after usage decode" — the runtime `pricedUsage` threw `budget_missing_price` after the CLI had already spent tokens, wasting the exact CLI invocation that preflight was meant to prevent. Preflight's `budgetDiagnostics` already refuses declared dollar budgets against unpriced models before any CLI dispatches. This makes the runtime path match that: `pricedUsage` returns `undefined` for unpriced models, callers (`worker`, `llm-worker`) omit `usage` from the step-complete payload rather than sending `dollars: null` (which would break the kernel wire schema). No new kernel field, no wire change. Adds `packages/sdk/tests/model-pricing.test.ts` (8 assertions) covering: - priced/unpriced/undefined-model returns - invalid token counts still journal as `worker_error` - `workerSpend` returns undefined usage for unpriced models without failing the step Session-Id: efeda5df-9b7c-48d4-b2ce-957f5bef0a82 Session-Id: efeda5df-9b7c-48d4-b2ce-957f5bef0a82 Session-Id: efeda5df-9b7c-48d4-b2ce-957f5bef0a82
compileSpec's own JSDoc claims 'Throws CompileError on validation failure,' but a bare BudgetSyntaxError from parseBudget escaped that contract and surfaced as an uncaught exception in callers that only catch CompileError. Preserve the diagnostic classification by adding an optional kind to CompileError and threading budget_syntax_invalid through it; preflight's existing BudgetSyntaxError instanceof branch stays as a fallback for callers that reach parseBudget outside compileSpec. Session-Id: efeda5df-9b7c-48d4-b2ce-957f5bef0a82 Session-Id: efeda5df-9b7c-48d4-b2ce-957f5bef0a82 Session-Id: efeda5df-9b7c-48d4-b2ce-957f5bef0a82
…ry, workspace, use) in checkMcpHeader checkMcpHeader only filtered out 'tools' before, so authored flows with any other legitimate header (budget, identity, memory, use, workspace) were rejected as 'unsupported header fields' — including this PR's own budget-authored-live tests. Session-Id: efeda5df-9b7c-48d4-b2ce-957f5bef0a82
be0faf2 to
ab16f18
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ab16f18. Configure here.
| journal | ||
| .scan_segment(segment) | ||
| .map_err(|error| anyhow!(error))? | ||
| }; |
There was a problem hiding this comment.
Epoch resume scans full journal
High Severity
Budgeted load_state now folds scan_all instead of the current segment, while apply_epoch still treats epoch.summary as the compact resume root. The summary only stores token and dollar budget_spent and never carries wallclock_ms, daily_budget, or budget_day. Replaying compacted completions then replacing budget can fail the new exact-string BudgetSummaryMismatch check, and a current-segment resume cannot reconstruct wallclock or daily windows after rollover.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit ab16f18. Configure here.
| const empty = { servers: Object.freeze({}), inventory: Object.freeze({}) }; | ||
| const unsupported = Object.keys(definition.header).filter(key => key !== 'tools'); | ||
| const KNOWN_HEADER_FIELDS = new Set(['tools', 'budget', 'identity', 'memory', 'workspace', 'use']); | ||
| const unsupported = Object.keys(definition.header).filter(key => !KNOWN_HEADER_FIELDS.has(key)); |
There was a problem hiding this comment.
Check accepts unsupported flow headers
Medium Severity
checkMcpHeader now treats identity, memory, workspace, and use as supported header fields. executeAuthoredFlow still refuses any header other than tools and budget. flows check can pass a TypeScript flow that flows run later rejects as unsupported_header.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit ab16f18. Configure here.


Closes #306.
Summary
Adds
budget:FlowHeader field with syntax"$N/day" | "$N/run"or{tokens, dollars, wallclock}. Preflight validates; kernel spend_tracker accrues per step; run refusesbudget_exceededwhen cumulative spend exceeds header.Design decisions
budget_syntax_invalid,budget_missing_price) extend, not replace, existing coverage.maxTokens*/maxDollarswith synthetic models keep their worker-price contract; frozen-price lookup enforced for new header forms.packages/sdk/src/model-pricing.ts. Dynamic lookup / per-user budgets / spend forecasting deferred.Written by codex agent
spec-G-budgeton finn-mini; head at54792d2.Test plan
🤖 Generated with Claude Code
Note
High Risk
Changes kernel admission, decimal/token accounting, and worker completion paths that directly control when runs start or stop; mistakes could admit over-budget work or refuse valid runs.
Overview
Enforces optional flow
budgetheaders end-to-end: surface/SDK accept"$N/run","$N/day", or{ tokens, dollars, wallclock }, compile them withpricing: "frozen", and refuse bad syntax (budget_syntax_invalid) or unpriced models (budget_missing_price) at preflight.Spend is journaled and gated in the kernel. Each
step.completedgains aspendblock (tokens, dollars as a JSON number, measuredwallclock_ms). Run state tracks cumulative tokens, dollars, and wall time (including UTC day windows andprior_spendfor chained runs). Before admitting another attempt, the scheduler checks limits; crossing a ceiling keeps the triggering completion valid but ends the run withbudget_exceededand blocks further step starts (in-flight peers may still finish).Workers and the authored TS runner participate in accounting. A frozen
MODEL_PRICINGtable drives microdollar costs; Claude/Codex structured output and an optional wrapper result envelope supply token usage. LLM/agent workers attachusageon completion; the internal authored executor serializes budgeted steps and rolls journaled spend into the next kernel run. Legacy explicitmaxTokens*/maxDollarsenvelopes keep worker-supplied pricing.Docs (
docs/BUDGET.md),testdata/budget-guarded.flow.yaml, and kernel/SDK budget tests document and lock the behavior.Reviewed by Cursor Bugbot for commit ab16f18. Bugbot is set up for automated code reviews on this repo. Configure here.