CLAUDE LABJP
FORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as OctoberFORK — Claude Code 2.1.212 changes what /fork does: it copies your conversation into a new background session with its own row in claude agents, so you can keep working. The old in-session subagent is now /subtaskLIMITS — WebSearch calls are now capped at 200 per session by default, and subagent spawns get the same 200 ceiling, so a runaway search or delegation loop stops on its ownMCPBG — MCP tool calls running past two minutes now move to the background automatically, keeping the session usable. Tune the threshold with CLAUDE_CODE_MCP_AUTO_BACKGROUND_MSPLANFIX — Fixed plan mode auto-running file-modifying Bash commands such as touch and rm without a permission prompt or an SDK canUseTool callbackSONNET5 — Claude Sonnet 5 is running on introductory pricing of $2 per million input tokens and $10 per million output. After August 31 it moves to $3 and $15IPO — Bankers are reportedly lining up investor meetings for Anthropic ahead of a possible public listing as soon as October
Articles/Claude.ai
Claude.ai/2026-03-21Advanced

Building Dashboards with Claude Artifacts — Why I Aggregate Before Pasting, and the Setup That Stuck

Claude artifacts render as React components. Working from that fact, here is how I stopped pasting raw CSVs and settled on a setup that separates preprocessing, data contracts, and refresh paths.

Claude46Artifacts2VisualizationsDashboardsData PreprocessingIndie Development8

Premium Article

At the end of each month I open AdMob, App Store Connect, and Firebase in turn and export the CSVs. Running several wallpaper apps as an indie developer, I would rather decide where the next update goes by looking at numbers than by going on instinct.

For a long time I pasted those CSVs straight into Claude. A few seconds later, a plausible line chart appeared. It felt good.

What tripped me up was noticing that the weekly peak in the chart sat one day away from the peak in my own spreadsheet.

The cause turned out to be almost disappointingly simple. The data I pasted was never read all the way through.

An artifact renders as a React component. Whatever data you hand over has to get transcribed into an array literal inside that code at some point. Pasting 4,312 rows of CSV is, in effect, asking for 4,312 objects to be typed out one by one. That is not what happens.

What follows is the setup I arrived at once I accepted that constraint — the path from raw exports to numbers I can actually make decisions with, along with the code I run today.

The CSV You Pasted Did Not Arrive Intact

I started by confirming what was happening, with numbers.

The 90-day daily report exported from AdMob was 4,312 rows and 178 KB. I pasted it, opened the source of the generated artifact, and counted the elements in the data array. There were 117.

Nothing announced this. There was no note saying "I sampled representative values" — just a line chart, drawn without comment. The weekly peak sat a day off because, after the thinning, an adjacent day happened to look like the high point.

I do not think of this as a defect. Output tokens are finite, and transcribing 4,312 rows would consume most of a response on its own. Fitting the data to what can reasonably be written is a sensible call.

The problem was that I never asked for that call to be made.

How to Check

When you suspect the same thing, opening the artifact's code is the fastest route.

  1. Open the artifact's code view
  2. Find the data array declaration (the equivalent of const data = [...])
  3. Count the elements and compare against your source row count
  4. If they diverge, do not use that chart for decisions

A gap of three rows is rounding. A gap of four thousand is a different dataset. Since I started making this check, I have stopped second-guessing my charts.

Do the Aggregation Yourself — 178 KB Down to 3.1 KB

The approach I settled on was to stop handing the aggregation to Claude at all.

Of those 4,312 daily rows, the only thing that drives a decision is the weekly trend per app. So I fold the data into that shape before it goes anywhere. Here is the script I actually use. It reads the AdMob daily export and emits weekly JSON.

# aggregate_admob.py — fold an AdMob daily CSV into a weekly summary JSON
# usage: python3 aggregate_admob.py admob_daily.csv > weekly.json
import sys
import json
import pandas as pd
 
REVENUE = "Estimated earnings (USD)"
IMPRESSIONS = "Impressions"
 
def main(path: str) -> None:
    df = pd.read_csv(path)
    df["Date"] = pd.to_datetime(df["Date"])
 
    # Pin weeks to a Monday start. Skip this and your totals land
    # one day off from the console's own weeks (mine did).
    df["week"] = df["Date"].dt.to_period("W-SUN").dt.start_time
 
    grouped = (
        df.groupby(["App", "week"])
        .agg(revenue=(REVENUE, "sum"), impressions=(IMPRESSIONS, "sum"))
        .reset_index()
    )
 
    # eCPM is total revenue / total impressions * 1000.
    # Averaging daily eCPM instead inflates the number, because
    # low-impression days carry the same weight as busy ones.
    grouped["ecpm"] = (grouped["revenue"] / grouped["impressions"] * 1000).round(2)
    grouped["revenue"] = grouped["revenue"].round(2)
    grouped["week"] = grouped["week"].dt.strftime("%Y-%m-%d")
 
    records = grouped.to_dict(orient="records")
    print(json.dumps(records, ensure_ascii=False, separators=(",", ":")))
 
if __name__ == "__main__":
    main(sys.argv[1])

Here is what I measured.

MetricPasting the raw CSVPasting the weekly fold
Payload178 KB / 4,312 rows3.1 KB / 26 rows
Input tokens (approx.)~47,000~900
Elements in the artifact117 (thinned)26 (complete)
Agreement with the numbersWeekly peak off by one dayMatches the spreadsheet
Cost of regeneratingRe-paste everything each timeSwap the JSON

That is roughly a 98% token reduction, but the tokens were never the point. What mattered was that every row now arrives, and that rebuilding got cheap.

The W-SUN line is the bug itself, written down. Pandas defaults its weekly periods to a Sunday close, which lands a day away from how the ad console groups its weeks. Pinning it made the original one-day gap disappear.

If you would rather keep the aggregation on Claude's side, the setup in handing monthly revenue CSV aggregation to the Claude API Code Execution tool is close in spirit. When you do not want Python sitting on your own machine, that is the more natural road.

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 working pandas preprocessing script that takes a 178 KB raw CSV down to 3.1 KB — a measured ~98% token reduction
A cron + static JSON setup that delivers a daily-refreshing dashboard despite artifacts having no external fetch and no localStorage
A 4-step data contract that stopped keys and axes from drifting on every regeneration
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

Claude 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

Claude.ai2026-07-12
Using Claude's Reflect Monthly Review to Tune a Solo Dev Rhythm
How to read Claude's new Reflect monthly review as a planning tool for the month ahead rather than a report card, grounded in the day-to-day rhythm of solo development.
Claude.ai2026-07-09
Claude Card Declined: A Complete Troubleshooting Guide for Pro, Max, and API Users
When Claude tells you 'Your card was declined,' the cause is rarely obvious from the error text alone. This guide separates the three layers where a decline actually happens — your issuing bank, Stripe's fraud engine, and Anthropic's account state — and walks you through fixes plus fallback payment methods that almost always get the charge through.
Claude.ai2026-07-06
When Instructions Fade in Long Conversations: Measuring Adherence and Pulling It Back
System-prompt instructions quietly lose their grip as a conversation grows. This piece turns that drift into a number you can track, then fixes it with prompt structure and mid-conversation re-anchoring, backed by runnable code and measured results.
📚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 →