ANTIGRAVITY LABJP
Articles/Editor View
Editor View/2026-08-01Advanced

The Date Column Was the Most Expensive Part of My Sales CSV — Measuring What Attachments Really Cost

2.4.3 lets you attach .json, .md, and .csv files directly. I rendered the same table eight ways, priced every column in tokens, and boiled it down to a 1,062-token digest. Every number here came from a run on my own machine.

Antigravity351context design4tokens2CSVmeasurement8solo development4

Premium Article

I was watching the context meter in the side pane while attaching a sales report covering June onward. 1,730 rows, 127 KB. Small, as text goes. The meter still dropped visibly before I had finished typing my question.

At the time I just cut the range in half and sent it again. What stayed with me was the thought I had the next morning: the same content, written a different way, might cost twice as much or half as much. If so, the thing to trim wasn't rows — it was formatting.

Working solo, small frictions like this come back on the same day every week. Re-slicing a date range takes half a minute, which is short enough to ignore and long enough to dull the decision behind it.

Version 2.4.3 added .json, .md, and .csv attachments to the input field, each with its own badge, so you can hand over a file instead of pasting its contents. More options means the choice of representation has moved onto my side of the table.

This is the record of measuring that choice instead of guessing at it.

One note up front: I can't publish the actual report, so every number below comes from a table with identical columns and comparable size, generated from a fixed seed. The generator is included, so you can confirm the same figures on your own machine.

About the tokenizer

I measured with OpenAI's tiktoken (o200k_base). That is not the tokenizer Gemini uses, so you cannot convert these absolute numbers into a bill.

As a relative index, though, it holds up. Running the same measurement through cl100k_base produced reduction ratios of 0.476 and 0.483 — essentially identical. Differences caused by format and column design are far larger than differences between tokenizers.

Read the ratios and percentages as the finding; treat absolute counts as a rough scale.

The table under test

Same column layout as an App Store Connect sales report: 62 days across 9 countries and 4 SKUs, with some zero-unit rows dropped, giving 1,730 rows.

# mkdata.py — generate the measurement data from a fixed seed
import csv, random, datetime, os
random.seed(20260801)
 
countries = ["JP","US","GB","DE","FR","KR","TW","CA","AU"]
skus = [
    ("WLP.PRO.YEAR",   "Wallpaper Pro (Yearly)"),
    ("WLP.PRO.MONTH",  "Wallpaper Pro (Monthly)"),
    ("CALM.REMOVEADS", "Calm Sounds Remove Ads"),
    ("CALM.PACK.02",   "Calm Sounds Pack 02"),
]
rate  = {"JP":1.0,"US":150.0,"GB":190.0,"DE":163.0,"FR":163.0,
         "KR":0.11,"TW":4.7,"CA":110.0,"AU":98.0}
cur   = {"JP":"JPY","US":"USD","GB":"GBP","DE":"EUR","FR":"EUR",
         "KR":"KRW","TW":"TWD","CA":"CAD","AU":"AUD"}
base  = {"JP":40,"US":22,"GB":6,"DE":7,"FR":4,"KR":9,"TW":5,"CA":3,"AU":3}
price = {"WLP.PRO.YEAR":3800.0,"WLP.PRO.MONTH":480.0,
         "CALM.REMOVEADS":320.0,"CALM.PACK.02":250.0}
mix   = {"WLP.PRO.YEAR":0.12,"WLP.PRO.MONTH":0.46,
         "CALM.REMOVEADS":0.28,"CALM.PACK.02":0.14}
 
d0 = datetime.date(2026, 5, 31)
rows = []
for i in range(62):
    d = d0 + datetime.timedelta(days=i)
    wk = 1.18 if d.weekday() >= 5 else 1.0          # weekends run a little hotter
    for c in countries:
        for sku, name in skus:
            lam = base[c] * mix[sku] * wk
            units = max(0, int(random.gauss(lam, lam * 0.45)))
            if units == 0 and random.random() < 0.55:
                continue                             # some zero rows never appear
            proceeds = units * price[sku] * 0.70 / rate[c]
            rows.append({
                "date": d.isoformat(),
                "country_code": c,
                "product_sku": sku,
                "product_name": name,
                "device_type": random.choice(["iPhone", "iPad"]),
                "units_sold": units,
                # FX conversion leaves long decimals behind — very common in real exports
                "developer_proceeds": repr(round(proceeds, 10)),
                "currency_code": cur[c],
            })
 
p = os.path.expanduser("~/lab/sales.csv")
os.makedirs(os.path.dirname(p), exist_ok=True)
with open(p, "w", newline="") as f:
    w = csv.DictWriter(f, fieldnames=list(rows[0].keys()))
    w.writeheader(); w.writerows(rows)
print("rows:", len(rows), "bytes:", os.path.getsize(p))
# => rows: 1730 bytes: 127536

The defining feature is a developer_proceeds column full of values like 24181.8181818182. Any report that passes through currency conversion looks like this. It matters later.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
A measured comparison of the same 1,730-row table rendered as CSV, TSV, Markdown, JSON, and YAML — padded Markdown is the priciest, and columnar JSON is nearly free
A script that drops one column at a time to price each column by difference; in a sales export the date column cost more than the money column
A 1,062-token digest that replaces the full table, plus the failure mode where a digest quietly lies to you (mixed units in one numeric column)
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

Integrations2026-08-03
I Measured Before Writing a Number: MCP Connect and Tool Calls Differed by 486x
Antigravity 2.4.3 lets you set a timeout per MCP server. To find a defensible number I built a stdio server, measured each boundary separately, and found why a single value cannot cover both.
Editor View2026-08-13
2.6.0 Changed How Hooks Wait. A Triage Order for Turns That Never Finish
When an agent turn never finishes, the cause is often a hook you wrote rather than the model. Here is what changed in IDE 2.6.0, an audit script that finds hooks capable of stalling a turn, and how to give a hook its own cutoff.
Editor View2026-07-17
One Space in a Path, and Nine Commands Reported Success While Counting the Wrong Place
A single space in a workspace name sends agent-written commands somewhere else, quietly. Measurements across eleven unquoted-path forms, and the entry-point script that closes the boundary in one cd.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →