Last week I burned a ten-million-token allowance before dinner. I merged two small fixes. Where did the rest go?
Not into the model. Into my loop. I resent the same files, re-asked the same question, and accepted almost nothing.
I ran that mess on MonkeyCode, which offers free model access and a free server option. The team says the current free allowance is ten million tokens.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Allowances change, so check the live terms yourself. The allowance was never my problem. My accounting was.
Here are five claims I used to repeat, and what actually held up.
Myth 1: A token is a unit of work
A token is a unit of billing. That is all it is. Two tasks can cost the same tokens and deliver very different value.
So stop asking "how many tokens did I use?". Ask "how many tokens per accepted change?". That second number is the one that survives contact with a deadline.
Here is the shape of my log, with illustrative numbers. These are not benchmark results.
| task | calls | tokens | accepted | tokens/accepted |
|---|---|---|---|---|
| fix-import | 6 | 14,900 | 1 | 14,900 |
| rename-symbol | 2 | 5,100 | 1 | 5,100 |
| write-changelog | 1 | 1,700 | 1 | 1,700 |
Same allowance. Very different economics. The first row is where my afternoon went.
Myth 2: A bigger allowance makes retries cheap
Retries are not free just because the meter is free. They cost your attention, and they cost wall-clock time.
Worse, retries compound. Each failed attempt usually gets appended to the next prompt. The transcript grows, and every later call pays for the earlier mistakes.
I call this retry amplification. It is calls divided by accepted results. Anything above three means the loop is broken, not the quota.
Myth 3: The free server is where I should measure speed
No. The free server is where the loop runs. It is not a benchmark host.
Use it for the boring win: a long run survives a closed laptop lid. Do not use it to compare models or claim latency numbers.
Why? Because you do not control the neighbors, the network path, or the scheduling. Any timing you publish from there is a rumor with a timestamp.
Myth 4: The model remembers my repo
Context is per request. Nothing persists unless you paste it again or the tool re-reads the files.
This is why the same bug costs more on the third attempt. You are paying to re-explain a repo the model never kept.
Fix it with a frozen context block, not a longer chat. Pick the files once, hash them, and reuse the same set until the task closes.
Myth 5: Acceptance is obvious, so I don't need to log it
If you cannot state the acceptance rule, you cannot compute cost per accepted change. Write the rule before the first call.
Mine is short. Tests pass. Diff stays under 80 lines. I would actually merge it.
Notice that the third clause is a judgment call. Log it honestly anyway. A ledger with fuzzy acceptance still beats no ledger.
The artifact: a token ledger you can run
You do not need a vendor dashboard. You need one JSONL file and twenty lines of Python.
First, the schema. One record per model call.
-
task: short stable id, likefix-import. -
attempt: integer, starting at 1. -
prompt_tokens,completion_tokens: your client's counts. -
ctx_chars: size of the context you sent, in characters. -
accepted: boolean, set by you after review.
Append a record with a single shell line:
printf '%s\n' '{"task":"fix-import","attempt":1,"prompt_tokens":1840,"completion_tokens":210,"ctx_chars":7400,"accepted":false}' >> ledger.jsonl
Now the reporter. Python 3 standard library only, no dependencies:
#!/usr/bin/env python3
"""ledger.py - per-task token accounting for a free-tier coding loop.
Reads JSONL, one record per model call. You write the records yourself.
Keys: task, attempt, prompt_tokens, completion_tokens, ctx_chars, accepted
"""
import json
import sys
from collections import defaultdict
def load(path):
rows = []
with open(path, encoding="utf-8") as fh:
for line in fh:
line = line.strip()
if line:
rows.append(json.loads(line))
return rows
def report(rows):
buckets = defaultdict(list)
for row in rows:
buckets[row["task"]].append(row)
print(f"{'task':24} {'calls':>5} {'tokens':>8} {'acc':>4} {'tok/acc':>8} {'amp':>6}")
for task, calls in sorted(buckets.items()):
tokens = sum(c["prompt_tokens"] + c["completion_tokens"] for c in calls)
accepted = sum(1 for c in calls if c["accepted"])
amp = len(calls) / max(accepted, 1)
per_accept = tokens / max(accepted, 1)
print(f"{task:24} {len(calls):>5} {tokens:>8} {accepted:>4} {per_accept:>8.0f} {amp:>6.2f}")
if __name__ == "__main__":
report(load(sys.argv[1]))
Run it:
python3 ledger.py ledger.jsonl
You get a table. tok/acc is your real unit cost. amp is the retry amplification from Myth 2.
How to read the numbers
Three bands, no magic:
-
ampat or below 1.5: the loop is healthy. Keep going. -
ampbetween 1.5 and 3: shrink the context block. Cut files, cut history. -
ampabove 3: stop calling the model. Rewrite the task spec first.
That third band is the important one. More quota does not move you out of it. A clearer request does.
A decision table for free-tier loops
Not every task belongs in a free loop. Here is how I route mine.
| Task | Needs repo-wide context? | Acceptance rule checkable? | Fits the free loop? |
|---|---|---|---|
| Rename a symbol | No | Yes, build passes | Yes |
| Fix one failing unit test | Sometimes | Yes, that test | Yes, with a frozen context |
| Write a changelog entry | No | Partly | Yes |
| Refactor a core module | Yes | Weakly | Only with a reviewer |
| Anything with a deadline SLA | Yes | Weakly | No |
Two rules sit under that table. Freeze the context. Write the acceptance rule first.
Limitations you should hear from me, not from a launch post
- The token counts come from my client, not the provider's meter. They drift.
- The ledger measures spend, never quality. A cheap accepted diff can still be wrong.
- Free allowances and free server capacity change without notice. Re-read the terms.
- The free server is not a load-test host. Do not publish latency claims from it.
-
acceptedis a human field. If you lie to it, the whole table lies back.
Who should not use this
Skip this workflow if you need an SLA or a support contract. Skip it if your acceptance rule is "looks fine to me".
Also skip it if you are benchmarking. A free box with unknown neighbors cannot support a benchmark claim, and pretending otherwise wastes everyone's review time.
What I changed in my own loop
I now log before I prompt. That small ordering change did more than any quota increase.
My first attempt of the week already carries a smaller context block. My amplification dropped because I stopped re-sending the whole story.
Is your free allowance the bottleneck? Or is it your retry count? Run the ledger for one week and you will know.
If you want to try it, the free tier is enough to run this ledger on a handful of real tasks and see your own amp.
Top comments (0)