You get the cost alert at 02:14 UTC tonight.
Queue depth still looks healthy on your Grafana dashboard.
Completed jobs stay flat while token spend climbs.
Which operational action follows from that split in evidence?
You pause new agent sessions before you scale anything.
You measure cost per completed job, not started calls.
The contradiction you should not ignore
Started requests keep succeeding at the edge proxy tonight.
Downstream tool rounds never return a finished answer.
You pay for context, tools, and retries that never land.
This pattern is unit-cost inversion, not simple queue backup.
Free capacity hides it until the finance export arrives.
You need a ledger that splits started work from finished work.
Topology you can reproduce on a laptop
Run one admission sidecar in front of a local fake model.
The sidecar counts expected tokens times planned tool rounds.
A worker loop calls tools until a deadline or reject.
Declared pieces for the local drill:
-
ledger.pywrites one JSON line per job event -
admit.pyrejects work above a unit-cost ceiling -
worker.pyinjects extra tool rounds on command -
cleanup.shdeletes the ledger and stops the loop
Do not treat this laptop loop as production evidence.
Label every number below as expected output from the script.
Declared test conditions
Use these conditions and do not silently change them.
- Workload: 40 agent jobs, max four tool rounds each
- Fake tokens: 800 completion plus 200 prompt per round
- Deadline slack: 12 seconds wall time per healthy job
- Reject if projected tokens exceed 4,000 per completion
- Reject if three starts land with zero completions
- Local only, no remote model, no network dependency
You are measuring inversion, not remote model quality.
Keep the fake server single-threaded so queue age stays visible.
Build a per-job token ledger
Create ledger.py as a JSONL append-only local event file.
It records start, round, complete, cancel, and reject events.
#!/usr/bin/env python3
"""Local job ledger. Expected output only. Not production telemetry."""
import json, time, threading, os
from pathlib import Path
LEDGER = Path(os.environ.get("LEDGER_PATH", "/tmp/agent_ledger.jsonl"))
_lock = threading.Lock()
def emit(event, job_id, **fields):
rec = {"ts": time.time(), "event": event, "job_id": job_id}
rec.update(fields)
with _lock:
with LEDGER.open("a") as f:
f.write(json.dumps(rec) + "\n")
def summarize():
started = completed = cancelled = rejected = 0
tokens = 0
if not LEDGER.exists():
return {
"started": 0,
"completed": 0,
"cancelled": 0,
"rejected": 0,
"tokens_spent": 0,
"unit_cost": None,
"start_complete_ratio": None,
}
for line in LEDGER.read_text().splitlines():
r = json.loads(line)
ev = r["event"]
if ev == "start":
started += 1
elif ev == "complete":
completed += 1
tokens += r.get("tokens", 0)
elif ev == "cancel":
cancelled += 1
tokens += r.get("tokens", 0)
elif ev == "reject":
rejected += 1
unit = (tokens / completed) if completed else None
ratio = (started / completed) if completed else None
return {
"started": started,
"completed": completed,
"cancelled": cancelled,
"rejected": rejected,
"tokens_spent": tokens,
"unit_cost": unit,
"start_complete_ratio": ratio,
}
You now have started, completed, cancelled, and spent tokens.
Unit cost uses completed jobs as the only denominator.
Cancelled work still adds tokens and exposes the inversion.
Admission rule you can explain at 03:00
Create admit.py as the 03:00 gate you can explain.
The gate uses two independent thresholds, not one blended score.
#!/usr/bin/env python3
"""Admit or reject agent jobs from ledger summaries."""
from ledger import summarize, emit
MAX_TOKENS_PER_COMPLETION = 4000
MAX_START_COMPLETE_RATIO = 1.8
MAX_ROUNDS = 4
TOKENS_PER_ROUND = 1000 # declared: 800 completion + 200 prompt
ZERO_COMPLETION_STARTS = 3
def projected_tokens(planned_rounds):
return min(planned_rounds, MAX_ROUNDS) * TOKENS_PER_ROUND
def admit(job_id, planned_rounds):
stats = summarize()
proj = projected_tokens(planned_rounds)
reasons = []
if proj > MAX_TOKENS_PER_COMPLETION:
reasons.append("projected_tokens")
ratio = stats["start_complete_ratio"]
if ratio is not None and ratio > MAX_START_COMPLETE_RATIO:
reasons.append("start_complete_ratio")
unit = stats["unit_cost"]
if unit is not None and unit > MAX_TOKENS_PER_COMPLETION:
reasons.append("observed_unit_cost")
if stats["started"] >= ZERO_COMPLETION_STARTS and stats["completed"] == 0:
reasons.append("zero_completions")
if reasons:
emit("reject", job_id, reasons=reasons, projected=proj)
return False, reasons
emit("start", job_id, planned_rounds=planned_rounds, projected=proj)
return True, []
Ask for one operational threshold and write down the rationale.
Queue age is not the same signal as utilization percent.
Deadline slack is not the same signal as token headroom.
Pick the threshold from the evidence in front of you:
- Pick unit cost first when completions stall under spend
- Pick queue age first when workers stay busy and finish
- Pick deadline slack first when the caller still waits
- Pick zero-completion starts when cancels wipe the denominator
Worker that can waste tokens on purpose
Create worker.py as a fault injector, not a model client.
Extra rounds burn tokens without producing a completed job.
#!/usr/bin/env python3
"""Inject extra tool rounds. Local expected behavior only."""
import argparse, time, uuid
from admit import admit, TOKENS_PER_ROUND
from ledger import emit, summarize
def run_job(extra_rounds, deadline_s):
job_id = str(uuid.uuid4())[:8]
planned = 1 + extra_rounds
ok, reasons = admit(job_id, planned)
if not ok:
return {"job_id": job_id, "status": "rejected", "reasons": reasons}
spent = 0
t0 = time.time()
for rnd in range(planned):
time.sleep(0.15) # declared fake round latency
spent += TOKENS_PER_ROUND
emit("round", job_id, round=rnd, tokens=spent)
if time.time() - t0 > deadline_s:
emit("cancel", job_id, tokens=spent, reason="deadline")
return {"job_id": job_id, "status": "cancelled", "tokens": spent}
emit("complete", job_id, tokens=spent)
return {"job_id": job_id, "status": "complete", "tokens": spent}
if __name__ == "__main__":
p = argparse.ArgumentParser()
p.add_argument("--jobs", type=int, default=40)
p.add_argument("--extra-rounds", type=int, default=0)
p.add_argument("--deadline", type=float, default=12.0)
args = p.parse_args()
for _ in range(args.jobs):
print(run_job(args.extra_rounds, args.deadline))
print("SUMMARY", summarize())
Run the healthy baseline before you inject extra rounds.
export LEDGER_PATH=/tmp/agent_ledger.jsonl
rm -f "$LEDGER_PATH"
python3 worker.py --jobs 40 --extra-rounds 0 --deadline 12
Treat the next block as expected output, not production proof.
- Forty
completeevents in the JSONL ledger - About 40,000 tokens spent across finished jobs
- Unit cost near 1,000 tokens per completion
- Start/complete ratio near 1.0 with no rejects
Now inject extra tool rounds against a tight deadline.
rm -f "$LEDGER_PATH"
python3 worker.py --jobs 40 --extra-rounds 2 --deadline 0.2
Expect this declared fault to invert unit cost quickly.
- Early jobs cancel after the first 0.15s fake round
- Tokens still land on
cancelevents with no completion - After three starts,
zero_completionsrejects new work - Unit cost stays undefined while spend is already real
That reject is the operational action from the evidence.
You stop starting work that cannot finish on time.
When free capacity is the wrong bet
Free model access looks cheap at the request edge.
Agent loops multiply that cost by every tool round.
A cancelled round still consumed context window and time.
Use free capacity as a lab, not as a production SLO.
Promote only after the ledger stays free of inversion.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
MonkeyCode offers free model access and a free server option you can point this local ledger at during lab drills. Do not treat that path as unbounded production headroom.
Free capacity is the wrong bet under these five conditions:
- Tool rounds per job exceed your completion token budget
- Cancel rate stays high while spend keeps rising anyway
- Queue looks empty because work dies inside the worker
- Restarts replay full agent traces, not a single tool
- Deadline slack is smaller than one extra tool round
Paid burst capacity can be cheaper per completed job.
You pay more per hour and less per finished answer.
That inversion is the decision, not a vendor slogan.
Failure handling and the rollback path
Watch these fields on every reject and every cancel.
-
event: start, round, complete, cancel, reject -
tokens: cumulative spend on thatjob_id -
reasons: projected_tokens, start_complete_ratio, observed_unit_cost, zero_completions -
start_complete_ratio: started divided by completed, when defined -
unit_cost: spent tokens divided by completed jobs only
If the ledger file is missing, you must fail closed.
Do not admit work you cannot account for tonight.
If summarize returns no completions, reject every new start.
Follow this rollback path after a false reject storm:
- Set
--extra-rounds 0and rerun the healthy baseline. - Drain in-flight jobs for one full deadline window.
- Restore admit thresholds only after unit cost drops.
- Keep
LEDGER_PATHon a tmpfs so leftover files cannot linger.
Do not raise token ceilings to silence the admission gate.
Fix the fan-out pattern or the deadline first.
Cleanup
Stop the injector and delete the local ledger file.
pkill -f worker.py || true
rm -f /tmp/agent_ledger.jsonl
unset LEDGER_PATH
ps aux | grep -v grep | grep worker.py || echo "no worker"
ls /tmp/agent_ledger.jsonl 2>/dev/null || echo "ledger gone"
Confirm no leftover python workers with a process listing.
Confirm the ledger path is gone before you leave.
Who should not use this
Skip this gate if you run single-shot completions only.
Skip it if every round lacks a stable job identifier.
Skip it if policy forbids even local request event logs.
This ledger is not billing-grade and not multi-tenant isolation.
You also should not use free capacity for launch-path traffic.
Lab inversion drills belong on a throwaway local server.
Production needs a budget, a drain, and a named owner.
A free lab path does not replace those three controls.
What you do at 02:14
You do not scale replica count on a token spike.
You compare started jobs against completed jobs first.
You reject new agent rounds until unit cost falls.
Then you rerun the local injector with extra rounds.
You keep the reject threshold tied to finished completions.
Run that drill before the next agent loop ships.
Top comments (0)