Cover illustration

TheDaily Front

Issue No. 3 Sunday, July 12 2026 #3 — SUNDAY, JULY 12, 2026
Where fathers retire, agents meter the ink, and browsers betray themselves in the decimals.
Sunday, July 12, 2026 The Daily Front No. 3 — Contents
30stories
8,024points
5,132comments
302kllm tokens
Assembled with 30 model calls — 204,487 tokens read, 97,850 written.

Highlights

Claude Code sends 33k tokens before reading the prompt; OpenCode sends 7k

A careful measurement finds Claude Code spending tens of thousands of tokens before the user even speaks, raising fresh questions about agent economics.

Vint Cerf, “father of the Internet”, is retiring

Vint Cerf’s retirement marks a ceremonial turning of the page for the networked age he helped bring into being.

What xAI's Grok build CLI sends to xAI: A wire-level analysis

A wire-level teardown of xAI’s Grok Build CLI puts codebase upload behavior under the magnifying glass.

How to read more books

Two popular essays on reading make the case for reclaiming attention from the glowing rectangle.

Since Chromium 148, Math.tanh is now fingerprintable to link underlying OS

Chromium’s math functions become an unlikely fingerprinting surface, proving that privacy can leak from the last decimal place.

From the Editor

The wires hummed louder than usual today. One of the Internet’s great architects is taking his leave, even as a new generation of agents spends lavishly, uploads freely, and asks to be trusted with the shop keys. We print the ledger, as ever, with one eye on progress and the other on the bill.

  1. Claude Code sends 33k tokens before reading the prompt; OpenCode sends 7k3
  2. Vint Cerf, “father of the Internet”, is retiring4
  3. What xAI's Grok build CLI sends to xAI: A wire-level analysis5
  4. Migrating a production AI agent to GPT-5.6: 2.2x faster, 27% cheaper6
  5. Mesh LLM: distributed AI computing on iroh7
  6. Old and new apps, via modern coding agents8
  7. Why write code in 20269
  8. An agent in 100 lines of Lisp10
  9. Show HN: Mindwalk – Replay coding-agent sessions on a 3D map of your codebase11
  10. Since Chromium 148, Math.tanh is now fingerprintable to link underlying OS12
  11. Ghostel.el: Terminal emulator powered by libghostty13
  12. Protobuf-py: Protobuf for Python, without compromises14
  13. How to read more books15
  14. I Learned to Read Again16
  15. So you want to learn physics (second edition, 2021)17
  16. Against Usefulness18
  17. LARP – Revenue infrastructure for serious founders19
  18. Kode Dot Programmable pocket device for makers, pentesters and geeks20
  19. I Did Not Kill Stanley Lieber: How to Draw (With 9front)21
  20. How we can reduce traffic congestion22
  21. How Doctors die. It’s not like the rest of us (2016)23
  22. Under federal rule, colleges must leave grads better off or lose financial aid24
  23. Jellyfish Undersea Roundabout25
  24. I love LLMs, I hate hype26
  25. Automation Without Understanding26
  26. Mechanistic interpretability researchers applying causality theory to LLMs26
  27. Tiny Emulators27
  28. Billions of Sketches Reveal Hidden Cultural Variation in Human Concepts27
  29. A dock that wakes up reliably27
  30. Ask HN: What Are You Working On? (July 2026)28
The Daily Front Page 2 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — The Agent’s Appetite
article

Claude Code sends 33k tokens before reading the prompt; OpenCode sends 7k

by systima·▲ 686 points·378 comments·systima.ai ↗
Claude Code is far hungrier.

Claude Code is far hungrier:

When we asked both harnesses for a one-line reply, Claude Code used roughly 33,000 tokens of system prompt, tool schemas, and injected scaffolding before the prompt even arrived. OpenCode used about 7,000.

Claude Code is far more cache inefficient:

OpenCode's request prefix was byte-identical in every run we captured; it paid to cache its payload once per session and read it back for pennies.

Claude Code on the other hand re-wrote tens of thousands of prompt-cache tokens mid-session, run after run, and on the same task wrote up to 54x more cache tokens than OpenCode.

Cache writes of course are billed at a premium, which accounted for the usage dashboard climbing when using Claude Code.

Config further bloats the prompt:

A production repository's 72KB instruction (AGENTS.md or CLAUDE.md) file adds another (avg) 20,000 tokens to every single request. Five modest MCP servers add 5,000 to 7,000 more. By the time a real working setup sends its first request, it is 75,000 to 85,000 tokens deep before the user has typed a word.

Subagents add to the cost:

A small task that cost 121,000 tokens done directly cost 513,000 tokens when fanned out to two subagents. This is because each subagent is its own agent that re-reads its own system prompt and tools on every turn it takes, so a fan-out multiplies the number of full baselines in flight.

Having said that, the parent ingests only each subagent's returned result, not its whole transcript.

We found one result in favour of Claude Code:

On a multi-step task Claude Code's whole-task total came out lower than OpenCode's, because it batches tool calls into fewer requests while OpenCode re-pays its smaller baseline turn after turn. The meter starts higher; how the session unfolds decides who spends more. That advantage held on the first model we tested; re-run on a newer one, the same task took twice the requests and cost roughly 298,000 tokens against OpenCode's 133,000.

Every finding above was cross-checked on a second model family. The pattern held, with one nuance we cover below.

The rest of this post shows how we measured all of this at the API boundary, where the tokens go, and what prompt caching does and does not save you.

Why measure this at all

Every token of harness payload is a token of working context you cannot spend on your task.

If you operate agentic AI in production, particularly under the EU AI Act where Article 12 expects you to log and understand your system's behaviour, "what does my agent actually send" is a question you should be able to answer with data.

Method

We spliced a logging proxy between each harness and the model endpoint.

harness (Claude Code / OpenCode)
  → logging proxy (captures request payloads + response usage)
    → model endpoint

The proxy records two things per request. The first is the exact JSON payload the harness emitted, meaning the system blocks, tool schemas, and messages. The second is the usage block the API returned, covering input tokens, cache writes, cache reads, and output tokens.

The payload capture is ground truth for what the harness sends. The usage block is ground truth for what was metered.

We tested under these conditions.

  • Harnesses. Claude Code 2.1.207 and OpenCode 1.17.18, both pinned to claude-sonnet-4-5, July 2026. A reduced matrix (the floor, the cache task, and the multi-step task) was later re-run pinned to claude-fable-5; where the model changed the result, we say so inline.
  • Baseline isolation. Fresh config directories with no MCP servers, no user settings, and no memory; an empty workspace with no instruction files; permissions bypassed. Multiplier lanes then add one variable at a time.
  • Tasks. T1 says "Reply with exactly: OK" and isolates fixed overhead (three runs per harness). T2 reads a seeded file and summarises it. T3 is a write-run-test-fix loop against FizzBuzz plus a checker script.
  • Quality check. A separate ten-lane benchmark, five runs per harness against a seeded, hash-verified test suite, scored pass rates alongside token cost; reported in the quality section.
  • Zero-tools variant. Claude Code with --tools "" and OpenCode with "tools": {"*": false}, separating system prompt from tool schema weight.

One full disclosure note before the numbers:

Our traffic passes through Meridian, a local gateway that bridges the Claude Code SDK to a standard Anthropic endpoint so a Claude Max subscription can drive third-party tools. It wraps every request in the SDK's own envelope, a constant we measured at roughly 6,200 tokens on the Sonnet path and 3,500 on the Fable path with bare calibration requests and subtracted from every metered figure below.

Payload-level figures come from the captured request bodies, which the gateway cannot affect, and are exact.

Character-to-token conversion for component estimates uses each harness's own measured ratio of 4.1 to 4.4 characters per token, derived from cold-cache anchors where the metered write equals the full payload, rather than a generic heuristic.

Part I. The floor

The fixed overhead of saying OK

The task was 22 characters. Here is what each harness sent with it on its first request.

Component

Claude Code

OpenCode

System prompt

27,344 chars, 3 blocks

9,324 chars, 1 block

Tool schemas

27 tools, 99,778 chars

10 tools, 20,856 chars

First-message scaffolding

7,997 chars of <system-reminder> blocks

none

The actual prompt

22 chars

22 chars

First-turn payload (calibrated)

~32,800 tokens

~6,900 tokens

OpenCode's request is close to minimal. There is one system block that opens with "You are OpenCode, the best coding agent on the planet", plus ten classic coding tools, plus the user's prompt as the only content.

Claude Code's request is a platform bootstrap. The 27 tools include the coding core plus an entire background-agent and orchestration suite, from CronCreate and Monitor to the Task family, worktree management, and push notifications.

Before the user-entered prompt, its first message carries three injected reminder blocks; a catalogue of agent types for delegation, a catalogue of available skills, and user context.

Tool schemas are the dominant term for both. Roughly 24,000 of Claude Code's ~33,000 tokens are tool definitions, versus roughly 4,800 of OpenCode's ~6,900.

Zero tools, pure harness

Stripping the tools isolates the system prompt itself. Claude Code's weighs in at 26,891 chars, about 6.5k tokens. OpenCode's is 8,811 chars, about 2.0k tokens.

Both harnesses trim their prompt slightly when tools are disabled. Even with no tools at all, Claude Code's instruction set is over three times the size of OpenCode's; the residual is behavioural doctrine, meaning tone rules, safety guidance, task-management instructions, and environment description.

A one-tool task

T2 asked each harness to read a file and summarise it. Both produced correct summaries.

Claude Code took 6 HTTP requests and roughly 199,000 cumulative metered input tokens. OpenCode took 4 requests and roughly 41,000, plus one Haiku side call for session titling.

Most of those tokens are cache reads billed at a tenth of the input price. Three things scale with payload regardless; the first-turn cache write, the per-turn read, and context-window consumption, which no cache discount reduces.

A 33k-token baseline means every turn starts a sixth of the way into a 200k window before any code enters the conversation.

A multi-step task, where the gap closes

T3, the write-run-test-fix loop, inverted the expectation set by the baselines.

Metric

Claude Code

OpenCode

Model requests

3

9 (+1 title call)

Tool-calling style

parallel batch in one round trip

one tool call per turn

Cumulative metered input

~121,000 tokens

~132,000 tokens

Claude Code batched the entire job, two file writes and two script executions, into a single parallel tool round trip. OpenCode made exactly one tool call per turn and took nine.

Because the baseline is re-sent on every request, request count multiplies baseline. OpenCode paid its ~7k baseline nine times, Claude Code paid its ~33k three times, and the totals converged.

Whole-task input roughly equals baseline times request count, plus conversation growth. A large-baseline harness that batches aggressively and a small-baseline harness that serialises can land in the same place.

Two structural details emerged from the payloads:

  1. Claude Code injects an additional <system-reminder> block as the conversation progresses, three on the first turn and four by the first tool round trip, so its scaffolding grows with turn count.
  2. OpenCode's per-turn marginal payload, roughly 400 to 2,200 chars per turn, is pure conversation content.

Does a newer model change the picture?

We re-ran the floor on Claude Fable 5 to check whether the gap was a Sonnet artefact, and it shrank (which we didn't expect).

Claude Code's system prompt is model-conditional. It sent 27,787 chars of instructions to Sonnet but only 10,526 to Fable, with tool schemas also trimmed from 99,778 to 82,283 chars. Same 27 tools, much less doctrine.

OpenCode's payload was byte-identical across both models.

The floor gap on Fable comes out at roughly 3.3x by payload against 4.7x on Sonnet. Still far hungrier, but the ratio is model-dependent.

The multi-step convergence did not survive the change either:

On Fable, Claude Code took six requests instead of three, including an 85,686-token mid-session cache re-write, and landed at roughly 298,000 tokens against OpenCode's 133,000.

Therefore the batching advantage is model behaviour, not a harness constant.

These lanes also produced OpenCode's only mid-session cache write in the entire study, a single event of roughly 6,000 tokens; everything else it sent stayed byte-stable.

One served-model note belongs here:

Under Fable we received responses alternately as claude-fable-5 and claude-opus-4-8, consistent with those tiers sharing infrastructure.

The Fable lanes were smaller samples, two runs for the floor and cache tasks and one for the multi-step task, so treat them as directional confirmation rather than a second full study.

Part II. The multipliers

The floor (above) explains a session that starts lean and stays short.

Real sessions tend to be much longer and more meandering, so we tried to emulate that by measuring a longer session with stacked responses.

Multiplier 1. The instruction file

We dropped a real 72KB AGENTS.md from a production repository into the workspace and re-ran T1.

The effect is symmetrical and large. Both harnesses gained just over 20,000 tokens per request. OpenCode's metered total went from 13,152 to 33,336. Claude Code's went from 39,005 to 59,243.

The asymmetry is in the mechanics, and it bit us during the experiment. Claude Code 2.1.207 ignored AGENTS.md entirely and only ingested the file when renamed CLAUDE.md, injecting it into the first user message. OpenCode reads either filename and injects it into the system prompt.

Two practical consequences follow. Check which filename your harness actually honours, because an ignored instruction file is silent. And know that a heavy instruction file nearly quadruples a lean harness's baseline; it rides on every request of every session in that repository.

Multiplier 2. MCP servers

We attached public, credential-free MCP servers in one-server and five-server configurations.

The schemas are identical across harnesses, so the tax is nearly identical too; roughly 1,000 to 1,400 tokens per small server, per request. Five servers added 4,900 tokens to Claude Code by payload and 6,967 metered to OpenCode, growing the tool counts from 27 to 69 and from 10 to 52.

Small public servers are the gentle case. Production servers with rich APIs ship schemas several times larger, which is exactly what the everything measurement below shows.

One operational footnote. Claude Code silently ignored a project-scoped .mcp.json in print mode until passed an explicit --mcp-config flag. If you assume a server is attached, verify it at the boundary.

Multiplier 3. Framework templates

Story-driven workflow frameworks such as BMAD expand a slash command into a large prompt template of personas, protocols, and checklists.

We ran an 8,405-char representative template as the prompt for the same T3 story in both harnesses. The template itself is only about 2,100 tokens, but it enters the conversation history and is re-carried by every subsequent request in the session. A 9-request session re-sends it nine times.

Framework tax is template size times request count, and it stacks on top of everything above.

Multiplier 4. Subagents

We asked each harness to fan the work out to two parallel subagents. This is where the totals climb fastest.

Claude Code completed the task with 9 model requests across three distinct request classes. There was the main session with its full ~33k baseline, and five subagent calls each carrying their own bootstrap of a 3,554-char agent system prompt plus 24 of the 27 tools.

Cumulative metered input reached 513,000 tokens, against 121,000 for the same work done directly. That is a 4.2x multiplier for one modest fan-out. The parent ingests only each subagent's returned result; the cost is that each subagent is a fresh agent re-reading its own bootstrap on every one of its own turns, so two subagents running several turns each add several more full-baseline requests to the session.

OpenCode's design here is notably leaner. Its subagent requests carry a reduced profile of a 1,379-char system prompt and 5 tools. Its subagent lane did not complete cleanly through our gateway, so we report the design difference from the captured payloads and leave its totals unquantified.

If your heavy sessions surprise you, this is the first place to look. Delegation is powerful and sometimes correct; it is also the single largest token multiplier we measured.

Multiplier 5. Extended thinking

Thinking output bills at output rates, five times the input rate, and reasoning blocks are carried forward inside the conversation.

We attempted to toggle extended thinking in both harnesses and are declining to publish numbers. Our gateway applies its own thinking policy, neither harness's toggle demonstrably survived the path, and anything we quoted would be noise.

The mechanism is not in doubt, though. On reasoning-heavy work it compounds with every multiplier above, because the thinking blocks join the history that gets re-sent.

The everything number

Finally, the bridging measurement. We ran T1 again under a real working configuration.

For OpenCode that meant eleven MCP servers covering email and calendar, task management, reference management, product analytics, and others, plus the 72KB instruction file. The first request metered 90,817 tokens on a cold cache write, carrying 179 tools and 277KB of schemas, before the user had typed a word.

For Claude Code, four MCP servers plus installed plugins and the same instruction file produced a 311KB payload of roughly 75,000 tokens with 118 tools.

After subtracting the gateway envelope, that is roughly a 12x configuration multiplier against OpenCode's ~7,000-token floor. The harness sets the floor; your configuration sets the bill.

The cache economics

Prompt caching changes the units but not the conclusions.

Both harnesses set cache breakpoints correctly. The payload is written once, at a 1.25x premium for the 5-minute TTL, and re-read at a tenth of the price thereafter.

Three costs survive the discount.

First, the write itself, re-paid whenever a pause exceeds the TTL. A five-minute think, a meeting, a lunch; each re-primes the full stack at write rates.

Second, the read multiplied by request count, which subagent fan-outs and serial tool loops inflate quickly.

Third, context-window consumption, which is completely immune to caching. An 85k-token bootstrap occupies more than 40 per cent of a 200k window on every single request, shrinking the room for actual code before compaction kicks in and spends yet more tokens summarising.

Cache stability, the decisive difference

Caching only pays if the prefix stays stable, so we hashed the tools array and system blocks of every request in the dataset.

OpenCode emitted byte-identical prefixes across every request and every run. Three separate T1 sessions produced the same tools bytes, the same system bytes, and the same message bytes; the repeat runs wrote zero cache tokens and read everything. Its nine-request T3 session held one stable prefix throughout.

Claude Code emitted three distinct request classes per session; a warmup probe, the main conversation, and subagent calls, each with its own prefix and therefore its own cache entry. Its system bytes also varied between sessions in the same workspace, and its first-message scaffolding varied between runs.

The consequence shows up in the cache-write column:

On the identical file-summarise task, Claude Code wrote 53,839 cache tokens across five requests, including one complete mid-task re-write of its full ~43k prefix. OpenCode wrote 1,003.

We re-ran the matched task to check whether that was a one-off (it was not).

The large mid-session re-write reproduced, 43,342 tokens in the first run and 36,899 in the second, while a third run against a freshly warmed cache wrote almost nothing. OpenCode showed zero mid-session re-writes in every Sonnet session we could cleanly meter.

We then repeated the matched task on Claude Fable 5:

The behaviour replicated almost exactly; another complete mid-session re-write, this time 50,053 tokens with zero cache read, and a cache-write gap of 52x against Sonnet's 54x. The multi-step task on Fable produced an even larger one, 85,686 tokens. Two model families, same pattern. OpenCode stayed byte-stable bar one write of roughly 6,000 tokens across all the Fable runs, against Claude Code's repeated re-writes of 37,000 to 86,000.

Depending on cache temperature, Claude Code's cache-write volume on the same task ranged from 5.9x to 54x OpenCode's, and cache writes bill at a premium, 1.25x base rate for the 5-minute tier and 2x for the 1-hour tier.

One attribution caveat is owed here. A single mid-task cache miss could in principle be our gateway evicting rather than the harness moving its cache breakpoints; reproduction across runs makes systematic harness behaviour the likelier explanation, and the prefix instability itself is harness-side, visible in the captured bytes before any gateway involvement.

If you have watched a usage meter climb dramatically under Claude Code but stay flat under OpenCode with the same model, this is the likeliest mechanism; bigger prefixes, more distinct prefixes per session, and more re-writes of them, multiplied by any subagent fan-out.

What about quality?

A fair objection to everything above is "a bill says nothing about the work", ie, paying more is rational if the output is better.

The tasks here were chosen so that quality could not be the explanation. Both harnesses completed every scored task correctly. The multi-step task was verified by an assertion script each harness had to write and then pass, and both exited clean. The file summaries were both accurate. On these tasks the token gap is the cost difference for an identical outcome, which is exactly what makes it measurable.

We then measured a first slice of it directly. A separate ten-lane benchmark gave each harness the same job, implementing two date utilities against a seeded ten-assert test suite it could not modify, in five fresh workspaces per harness; this time the suite was pre-written and hash-verified, so neither harness graded its own work. Both passed five out of five. Against that identical, independently verified outcome, the average metered cost per passing run was roughly 268,000 input tokens for Claude Code and 72,000 for OpenCode, about 3.7x, with both harnesses served the same model snapshot. OpenCode also finished each lane in a minute or two, against four to eight minutes for Claude Code.

Whether the premium buys quality on genuinely hard engineering work is a different question, and a two-function utility is a first slice of it, not an answer. Claude Code's background agents, skills, and orchestration surface may well earn their tokens on harder tasks; testing that claim needs harder tasks and more runs, and the rig here can drive both.

Two of the findings are independent of quality, though. Re-writing a byte-identical cache prefix mid-session buys no code quality at all; it is the same content, paid for again at premium rates. An instruction file the harness silently ignores buys nothing either. Whatever the capability argument for a larger platform, those two are waste on any definition.

Dogfooding, or the benchmark dataset as an audit log

The honest version of this experiment is "trust the captured payloads", so we treated the dataset the way we tell clients to treat production inference logs.

Every one of the 273 captured request/response records was written into a tamper-evident, SHA-256 hash-chained audit trail using our open-source library @systima/aiact-audit-log, and the chain verifies end to end.

Chain verified: 273 entries
No breaks detected
Hash chain integrity: VALID

This is the same mechanism the library provides for EU AI Act Article 12 logging; structured records, integrity you can hand to a third party, and reconstruction of exactly what was sent and returned.

The dogfooding also cut both ways. One rebuild reported more records ingested than the chain contained, a buffered-write fault we caught because the chain count refused to reconcile; we rebuilt from the raw captures and re-verified before quoting the number above.

A token benchmark is a low-stakes use of it. A credit-decisioning agent is not.

Caveats

  • One machine, one pair of versions, two model families for the floor and cache lanes (Sonnet 4.5 and Fable 5), one for the multiplier lanes, small n (three T1 and three T2 runs on Sonnet, two and two on Fable, five quality lanes per harness, one run per multiplier lane). Harness prompts change frequently, so treat the numbers as a July 2026 snapshot and the method as the durable artefact.
  • A local gateway sat in the measurement path. Component-level figures come from captured payloads, which it cannot affect. Metered figures are cold-cache anchors calibrated against its measured constant; warm-run metered numbers were unattributable, so we only quote cold anchors. The gateway also silently substituted a newer model snapshot than the one we pinned, which is its own lesson. If you are not logging at the API boundary, you do not know what model you are actually running. On the Fable path the gateway also resumed a stale server-side session in one lane and executed tools host-side in another; we excluded both and re-ran the multi-step lane with unique filenames to break the gateway’s conversation fingerprint, which completed cleanly. Under the Fable pin it also answered alternately as claude-fable-5 and claude-opus-4-8.
  • The T3 convergence is one observation of one task shape. A strictly sequential task would push Claude Code's request count, and therefore its total, back up. The OpenCode zero-tools and subagent lanes returned malformed streams through the gateway, so for those conditions we report captured payload sizes only.
  • The real-configuration figures describe one practitioner's setup, reported in sizes and counts only. Yours will differ; the method transfers.

Reproducing it

The measurement rig is roughly 200 lines of Node. It is an HTTP proxy that forwards to your model endpoint, writes each request body and response usage block to disk, and appends each pair to an audit chain.

The rig, the seeded quality benchmark, and the full aggregated results are open source at github.com/systima-ai/agentic-coding-tools-comparison.

The raw captures stay private, since they embed complete system prompts and configuration, but the rig regenerates equivalent captures for your own setup in minutes.

Point ANTHROPIC_BASE_URL at it. Give the harness a fresh config directory and an empty workspace for the floor. Then add your instruction file, your MCP servers, and your workflows one at a time, and watch the boundary.

If your traffic passes through a gateway, measure its envelope with a bare request first, and check which model actually answers.

The Daily Front Page 3 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — The Old Net Bids Farewell
article

Vint Cerf, “father of the Internet”, is retiring

by compiler-guy·▲ 359 points·202 comments·techcrunch.com ↗
The conclusion of one of the most influential careers in technology history.

Google vice-president Vint Cerf

The ‘Father of the Internet’ is finally retiring

Vinton Cerf will step down from his role as Google’s chief internet evangelist next week, marking the conclusion of one of the most influential careers in technology history.

While speaking via video feed at the Open Frontier conference hosted by the Laude Institute, Cerf was recognized by Dave Patterson, the UC Berkeley professor best known for co-developing RISC processor architecture.

“Vint … has been at Google more than 20 years, and he is retiring a week from today, and so I think we ought to give him a round of applause for a relatively good career,” Patterson said, to cheers from the room.

A Google spokesperson confirmed that Cerf will be stepping down from his role at the company.

Cerf, 83, and collaborator Robert Kahn are credited as being the architects of the networking protocols that became the internet we know today. His work developing and popularizing TCP/IP — the basic set of rules that lets different computer networks talk to each other — beginning in the 1970s has been recognized with numerous honorary degrees, the Presidential Medal of Freedom, and a Turing Award, among other honors.

Since 2005, Cerf has served as vice president and chief internet evangelist at Google. (At this point, we can safely say the internet is fully evangelized, for good or ill.)

Cerf was speaking on a panel alongside other computer scientists known for their work on durable open source projects, including Patterson; François Chollet, creator of the Keras deep-learning library and co-founder of Ndea; John Ousterhout, the Stanford computer scientist behind the Tcl programming language, who also co-founded Electric Cloud; and Matei Zaharia, who is Databricks’ co-founder and chief technologist. They offered advice about what it takes to build open source systems that survive — advice that’s increasingly relevant as founders bet on open infrastructure for the next wave of AI products.

Much of the conference’s discussion focused on the problems with the centralization of advanced models in a handful of well-resourced labs, in contrast to the decentralized world of the open internet that made Cerf’s own protocols so durable. However, Cerf predicted that the rise of AI agents — software that can act autonomously and coordinate with other software — would push tech companies back toward standardized protocols.

“The agentic model of AI, with multiple agents from multiple sources interacting with each other, is going to force composability, and a requirement for interoperability and standardization,” Cerf said.

If he’s right, the companies that define those interoperability standards early could end up with outsized influence over how the agentic economy actually works — a dynamic not unlike the early internet protocol wars.

While other panelists speculated that natural language communication between LLM agents would be sufficient, Cerf predicted formal standards would be required.

“I don’t think English is going to be the best choice. There’s a flexibility in it, but there’s ambiguity, and I think precision for interagent interaction is going to be very, very important. An agent really needs to be sure the other agent understands what it is that they just agreed to do together,” Cerf said.

“Remember the old telephone game where you wish you’d whispered in somebody’s ear and then by the time it got to 10 people away the message was totally different? Imagine a bunch of agents talking to each other in natural language, you know, that’s kind of terrifying.”

In a more lighthearted moment, Patterson recalled meeting Cerf, known for his wardrobe of three-piece suits, as a grad student in the 1970s.

“He’s always been the best dressed computer scientist I’ve ever met,” Patterson said. “My memory of Vint is that he came as a grad student with a shirt and tie in the ’70s.”

“It absolutely is true,” Cerf said. “I even had a vest, and for some reason I always wanted to stick out, and instead of having long hair, and something in my nose, I thought just dressing differently was one way to do it.”

This story has been updated with comment from Google.

The Daily Front Page 4 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — The Agent’s Receipt
repository

What xAI's Grok build CLI sends to xAI: A wire-level analysis

by jhoho·▲ 521 points·227 comments·gist.github.com ↗

A measured, reproducible teardown. Findings are backed by captured artifacts (endpoint, HTTP method, status code, byte size, host) and repro commands; where an observation was seen live but not retained as a file, §7 says so explicitly. Section 8 is an evidence appendix with SHA-256s and a "what we did not prove" list. All captures are of my own traffic on my own machine, using a throwaway repository containing fake "canary" secrets — no real credentials were exposed.


0. Summary

xAI's official Grok Build coding CLI (grok), on a normal consumer login, does three things worth documenting precisely:

  1. It transmits the contents of files it reads — including a .env secrets file — to xAI, verbatim and unredacted. The secret appears in two channels: the live model turn (POST /v1/responses) and a session_state archive uploaded and accepted (HTTP 200) via POST /v1/storage — the endpoint the binary routes to the grok-code-session-traces GCS bucket (see §5).
  2. It uploads the whole repository — every tracked file's content plus git history — independent of what the agent reads. Grok packages the workspace and uploads it via POST /v1/storage. Proven directly: on a real codebase, with the prompt "reply OK, do not read any files", Grok uploaded the entire repo as a git bundle (POST /v1/storage → 200); git clone-ing the captured bundle recovers a file the agent was told not to opensrc/_probe/never_read_canary.txt — with its unique marker verbatim, plus the full git history (appendix uploaded_repo.bundle). And it scales: on a 12 GB repo of never-read random files, /v1/storage moved 5.10 GiB, all HTTP 200 (truncated mid-stream), while the model-turn channel moved just 192 KB — a ~27,800× ratio that pins the upload to the codebase, not to what was read. No storage upload failed; the only non-200s were a model-usage quota (402/429) on /v1/responses and one unrelated 404 — not a storage size cap.
  3. The storage destination is a Google Cloud Storage bucket, grok-code-session-traces (not AWS S3) — named verbatim in the binary and in a captured metadata.json (gs://grok-code-session-traces/…). I did not find this mechanism surfaced in the CLI's install/quickstart materials (not an exhaustive docs audit — §7), it is active by default, and disabling "Improve the model" does not turn it off (/v1/settings still returned trace_upload_enabled: true; §6).

None of this proves xAI trains on the data — that is a policy question addressed in §6. What is proven is transmission, acceptance, and storage.


1. Subject under test (provenance)

Install:  curl -fsSL https://x.ai/cli/install.sh | bash      # → ~/.grok/bin/grok
Auth:     first launch opens a browser → login to X / SuperGrok (consumer account, not an API key)

Binary identity (repro: file $(readlink -f ~/.grok/bin/grok); ~/.grok/bin/grok --version; shasum -a 256 $(readlink -f ~/.grok/bin/grok)):

~/.grok/bin/grok -> ../downloads/grok-macos-aarch64
Mach-O 64-bit executable arm64
grok 0.2.93 (f00f96316d4b)
SHA-256: 2a97ba675bd992aa9b981e2e83776460d94f469b510c0b8efe28b50d236d767c

The upload machinery is a first-party Rust crate. strings on the binary yields these source paths and constants (repro: strings <binary> | grep -E 'xai-data-collector|grok-code-session-traces|storage.googleapis'):

crates/codegen/xai-data-collector/src/gcs.rs
crates/codegen/xai-data-collector/src/storage_client.rs
crates/codegen/xai-data-collector/src/queue.rs
crates/codegen/xai-data-collector/src/file_access_tracker.rs
crates/codegen/xai-data-collector/src/circuit_breaker_observer.rs
crates/codegen/xai-grok-shell/src/upload/{gcs,turn,trace,manifest}.rs
grok-code-session-traces
storage.googleapis.com
"Uploading bytes to GCS via proxy"

2. Method (reproducible)

Environment: macOS, Apple Silicon, grok 0.2.93, July 2026.

  1. brew install mitmproxy; run once to generate its CA at ~/.mitmproxy/.

  2. Trust the CA in the login keychain (no sudo; Grok does not certificate-pin against it):

    security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db \
      ~/.mitmproxy/mitmproxy-ca-cert.pem
    
  3. Run Grok routed through the proxy (a mitmdump addon logs, per request: method, host, path, response status, request byte size; and saves request bodies for xAI hosts):

    HTTPS_PROXY=http://127.0.0.1:8080 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem \
      grok -p "<prompt>" --cwd <repo>
    
  4. For staged-artifact inspection, race-copy ~/.grok/upload_queue/* during the run, then gzip -dc | tar -xO.

Canary repo: each file carries a unique marker so anything appearing in captured traffic is unambiguously traceable to a file. Secrets file secrets.env / .env:

API_KEY=CANARY7F3A9-SECRET-should-not-leave
DB_PASSWORD=CANARY7F3A9-DBPASS

3. Finding 1 — File contents, including a secrets file, are transmitted and accepted (200)

Claim: when Grok reads a file, its contents are transmitted to xAI — serialized into the POST /v1/responses model-turn body, and packaged into a session_state archive that is uploaded and accepted (HTTP 200) via POST /v1/storage — with no redaction of the file's contents. A .env is sent like any other file.

Wire artifact — a decrypted 48,070-byte POST cli-chat-proxy.grok.com/v1/responses request body (identifiable as a model turn by its embedded "messages":[…]"model":"grok-4.5" JSON). It contains the secrets file verbatim (appendix: secrets_responses_body.bin, secret_verbatim.txt):

…API_KEY=CANARY7F3A9-SECRET-should-not-leave\nDB_PASSWORD=CANARY7F3A9-DBPASS\n…"model":"grok-4.5"…

Repro: grep -a "CANARY7F3A9-DBPASS" secrets_responses_body.bin → matches. All six file markers (source, logic, README, nested JS, API key, DB password) are recoverable from the decrypted /v1/responses bodies. (This artifact proves the secret was transmitted to the /v1/responses endpoint; the raw body file does not carry the response status, so the acceptance (200) claim is anchored to the /v1/storage channel immediately below, which is status-mapped in wire_12gb.log.)

Second channel — persisted to Google Cloud Storage. The same content is packaged into a session_state archive uploaded via POST /v1/storage. Proven by decompressing the staged artifact before it drains (appendix: secrets_session_state.tar.gz):

gzip -dc secrets_session_state.tar.gz | tar -xO | grep -ao 'CANARY7F3A9-[A-Z]*'
→ CANARY7F3A9-SECRET, CANARY7F3A9-DBPASS, + all others

So the secret is not only processed in-flight; it is written into an archive destined for storage.

Pre-empting "you told it to read the secrets." A control run with a file the agent was told not to open (untouched_secret.txt) and the prompt "Reply exactly OK, do not read any files" produced no occurrence of that file's marker in any captured body. The leak is therefore scoped to files Grok does read — but it reads liberally (any file relevant to the task, including a .env) and applied no redaction to that file's contents. The defect is that a secrets file was transmitted unredacted, not the act of reading. Important scope reconciliation: this control shows the unread file is absent from the /v1/responses bodies — that is Channel A (files the agent reads). It does not clear the separate whole-repo /v1/storage snapshot in §4 (Channel B), which — by the volume evidence there — does sweep in never-read files; I could not decompress the /v1/storage codebase chunk to check this specific file. So "unread file not uploaded" is true only for the model-turn channel, not for the codebase snapshot. (Two further scope notes: (i) in my runs the .env/secrets.env was git-tracked; I did not separately test whether a .gitignored file is still uploaded, so I make no gitignore claim — the mechanism is read-driven per the file_access_tracker crate, but that specific case is untested. (ii) The canary values sat in API_KEY=/DB_PASSWORD= keys inside a .env/secrets.env but were not real-format high-entropy tokens; I proved this .env was transmitted unredacted, not that no redactor exists for, say, an sk-…-shaped key.)


4. Finding 2 — The whole repo is uploaded at multi-GB scale; the only ceiling is a model quota, not storage size

Claim: Grok uploads a whole-repo snapshot with no storage size wall in the tested range. As the repo grows it switches upload strategy and keeps returning 200; on a 12 GB repo, 73 chunks of ~75.0 MB (5.10 GiB) uploaded with zero failures before the capture was truncated mid-stream.

Wire-captured size sweep (incompressible content so the tar cannot shrink; fresh session each step). Only the 12 GB row was retained as a file (wire_12gb.log); the smaller rows were observed live during the sweep but their logs were not saved (see §7):

Repo size Upload behavior (observed on the wire) Status Artifact 64 MB single POST /v1/storage, req=50548145b (48 MB) 200 observed, not retained ~600 MB POST /v1/storage in ~7.5 MB chunks (dozens) all 200 observed, not retained ~3 GB POST /v1/storage/multipart/initPUT storage.googleapis.com/grok-code-session-traces/multipart/<id> in 50 MB parts (direct-GCS PUT lines not preserved — §7) all 200 observed, not retained ~12 GB POST /v1/storage in 75 MB chunks (req≈75014840b); 73 chunks (~5.1 GB) captured before I stopped the run all 200, 0 failures wire_12gb.log

Preserved artifact: wire_12gb.log (appendix). It contains 83 /v1/storage* 200 responses: 82 content-upload POST …/v1/storage requests — of which 73 are chunks of ~75.0 MB each (byte sizes min 75,014,811 / max 75,014,871, totaling 5,476,083,317 B = 5.10 GiB / 5.48 GB) plus 9 smaller POSTs — and 1 /v1/storage/batch_exists dedup check. Total /v1/storage* request bytes: 5,476,228,005 B. Zero storage requests failed. The capture was stopped while uploads were still streaming (the last line is another ~75 MB chunk → 200), so this demonstrates ≥5.1 GiB uploaded, still climbing when truncatednot that the full 12 GB completed. Repro (three greps, so no count is ambiguous): grep 'cli-chat-proxy.grok.com/v1/storage' wire_12gb.log | grep -c '> 200'83 (all /v1/storage*); grep 'POST cli-chat-proxy.grok.com/v1/storage ' wire_12gb.log | grep -c '> 200'82 (content POSTs only); grep req=75014 wire_12gb.log | grep -c '> 200'73 (the ~75 MB chunks).

Scope — this is the whole repo, not just files the agent read. Channel A (§3, /v1/responses) carries files the agent opens. This §4 upload is a separate Channel B: a snapshot of the entire workspace. Two lines of evidence:

  • (a) The decisive byte split (load-bearing). In the same captured 12 GB session — a repo of 100 % random files the agent never read — the two channels moved wildly different volumes:

    • Channel A /v1/responses (model turns): 196,705 B = 192 KB total, across 5 requests, largest single turn 60,394 B.
    • Channel B /v1/storage: 5,476,228,005 B = 5.10 GiB.
    • That is a ~27,800× ratio (5,476,083,317 ÷ 196,705). The model demonstrably never ingested the files (192 KB cannot carry 5 GiB of content), yet 5.10 GiB of them left via /v1/storage — and across the sweep the /v1/storage volume tracks total repo size (64 MB → 12 GB). GB-scale bytes leaving a never-read repo can only be a whole-repo snapshot.
  • (b) The binary's own paths/strings corroborate the mechanism: after_codebase.tar.gz, xai-grok-shell/src/upload/{trace,turn}.rs, repo_state.upload, "collecting workspace files", "spawning background coordinator".

  • (c) A staged codebase manifest enumerates a never-read file and content-addresses it to the GCS bucket. In a separate run on a real 298-file cereblab_api codebase, I race-copied the snapshot Grok staged for upload (appendix: staged_base_tree_manifest.json, staged_metadata.json). The manifest lists src/_probe/never_read_canary.txt — a file I planted and never asked Grok to open — alongside the real sources (src/apns.ts, src/index.ts, …); and metadata.json gives each file a destination fileId of the form gs://grok-code-session-traces/repo_changes_dedup/v2/supplemental/sha256_… . So the whole tree, unread files included, is enumerated and content-addressed for upload to the named GCS bucket. (That run was quota-blocked before the content blobs uploaded; the 12 GB run above independently proves GB-scale content actually leaves the wire, all 200.)

  • (d) DEFINITIVE — a never-read file's content recovered verbatim from an uploaded git bundle (single SuperGrok run). With the account upgraded (quota lifted), I re-ran on the real cereblab_api repo with the explicit prompt Reply with exactly: OK. Do not read or open any files., having planted a unique marker CANARY-XR47P2-NEVERREAD-UNIQUE in src/_probe/never_read_canary.txt. Grok uploaded the entire repository as a git bundle via POST cli-chat-proxy.grok.com/v1/storage → 200 (req=152102b) (appendix uploaded_repo.bundle, SHA-256 73b9c0af06311bae35c3ed03274d0eec2846e76762828d10b09757ca41bd6024). Running git clone uploaded_repo.bundle reconstructs the repo, and src/_probe/never_read_canary.txt contains the marker verbatim — a file the agent was explicitly told not to open. The bundle also carries the full git history (4 commits, 47 files). This is the airtight per-file-content proof: the whole repo — unread files included — left the machine and was accepted (200). The upload mechanism is a git bundle, so "whole repo" is literal (every tracked file + history). Replicated on a second, unrelated codebase: the identical capture on the cereblab_auth Cloudflare-Worker repo produced a git-bundle upload (POST /v1/storage → 200, 31,743 B) from which git clone recovered its own never-read marker CANARY-AUTH-4T8K2-NEVERREAD verbatim (appendix uploaded_repo_auth.bundle, SHA-256 0ee536538bcd1ee72a258f9977ab69f8a9b1ac240491b91a4e94335b4d83c768). Two independent repos, same result.

(Prompt note: the 12 GB session was interactive and I did not log its verbatim prompt, but the 192 KB Channel-A total is dispositive that no bulk read occurred whatever the prompt was; a separate headless control run used the explicit prompt Reply exactly OK, do not read any files and confirmed an unread file is absent from Channel A.)

(The earlier "one gap" is now closed by evidence (d): a single SuperGrok run where a specific never-read file's content is recovered from a wire-captured, 200-status git-bundle upload. The 12 GB run remains the proof that this scales to GB volumes.)

No storage/upload request failed — every one of the 82 /v1/storage calls returned 200. The only non-200s in the entire capture were on the model endpoint plus one session-bookkeeping call (full set from wire_12gb.log; /v1/responses lines also in model_limit.txt):

POST /v1/responses                     -> 402   (Payment Required)   ×1
POST /v1/responses                     -> 429   (Too Many Requests)  ×3
POST /v1/sessions/<id>/replicas/update -> 404                        ×1   (session bookkeeping, not an upload)

and finally, in plain text, on stdout:

You've reached your free Grok Build usage limit for now. Get SuperGrok for much higher limits…

The 402/429 are a model-usage quota; the lone 404 is unrelated to storage. Notably, storage uploads continued to return 200 after the model turn was rate-limited (76 /v1/storage 200s occur at or after the first 429) — the codebase upload is independent of whether the model answers.

Pre-empting "you're confusing a local disk cache with an upload." This claim rests strictly on wire-captured 200-status uploads of file bytes leaving the machine (/v1/storage request bodies of 7.5–75 MB in the preserved wire_12gb.log; the 3 GB 50 MB PUTs to storage.googleapis.com were also seen on the wire, but that log was not retained — §7). It does not rely on the ~/.grok/upload_queue draining — queue-drain is ambiguous (it empties on both success and drop) and is explicitly not used as evidence here. (An earlier draft that inferred upload from queue-drain was wrong and has been retracted; see §7.)


5. Finding 3 — Destination, telemetry, and what's not surfaced in the docs

  • Storage destination is Google Cloud Storage, bucket grok-code-session-traces . This rests on the preserved binary strings grok-code-session-traces, storage.googleapis.com, and "Uploading bytes to GCS via proxy" (crate_strings.txt), and on a preserved staged metadata.json whose per-file fileIds are literally gs://grok-code-session-traces/repo_changes_dedup/v2/…/sha256_… (staged_metadata.json), corroborated by the direct storage.googleapis.com multipart PUTs observed at 3 GB (observed live; that log was not retained — see §7). It is not AWS S3 (the binary links aws-sdk-s3 for an alternate path and AWS STS/SSO for auth, but the destination named in the binary — and seen on the wire at 3 GB — is GCS).
  • Third-party telemetry: POST api.mixpanel.com/track and /engage (Mixpanel), plus POST grok.com/_data/v1/events — all 200.
  • Not surfaced in setup docs (scope-limited claim): I did not find the repo_state / session_state upload to grok-code-session-traces, or the ~/.grok/upload_queue staging, described in the CLI's install script or quickstart materials I reviewed (this is not an exhaustive audit of all xAI docs — see §7). The mechanism is active by default on the standard consumer login.
  • Reliability note (separate from privacy): ~/.grok/upload_queue stages ~3 GB snapshots per turn and, under load, can grow to tens of GB and exhaust the disk. This is a real bug, independent of whether uploads succeed.

6. Consent and policy — stated honestly

  • "Cloud AI tools send context; this is normal." True, and conceded: any cloud coding agent must send code to its server to act on it. The novel deltas here are (a) a secrets file (e.g. .env) is transmitted unredacted, (b) the content is persisted to a named GCS bucket, not just processed transiently, and (c) the upload mechanism is not surfaced in the CLI's setup materials (§7) and on by default.
  • "It's in the ToS / opt-in." xAI's consumer policy broadly discloses data use for model improvement with an opt-out (grok.com → Settings → Data → "Improve the model"; Private Chat auto-opts-out; opt-out is prospective, not retroactive). But broad training disclosure ≠ documenting this specific mechanism. I did not find the repo_state/upload_queue/grok-code-session-traces pipeline described in the CLI materials I reviewed (§7 notes this is not an exhaustive docs audit), so — on those materials — a user is not informed of it specifically. Sources: xAI Privacy Policy (https://x.ai/legal/privacy-policy), Consumer ToS (https://x.ai/legal/terms-of-service).
  • The "Improve the model" toggle makes no difference — ON or OFF, the whole repo is uploaded the same way. At default settings, Grok uploaded 5.10 GiB of a never-read repo (§4). Then I turned "Improve the model" off and re-ran: Grok still uploaded the entire repo as a git bundle (§4(d), POST /v1/storage → 200, the never-read file recovered by git clone), and the server's /v1/settings response to the CLI still returned "trace_upload_enabled": true, "upload_enabled": true, "session_registry_enabled": true (and "max_upload_file_bytes": 1073741824, a 1 GiB per-file cap). The opt-out governs training, not whether your code is uploaded/stored: the codebase upload to grok-code-session-traces continues either way. Opting out does not stop your repository from leaving the machine.

7. What we did NOT prove (intellectual honesty)

  • We did not prove xAI trains on this data. Upload/storage ≠ training. That is governed by policy and account tier; we measured transmission only.
  • The direct storage.googleapis.com/grok-code-session-traces PUT wire-lines were observed at 3 GB but not preserved in this session (the log was overwritten during the sweep, and re-capture is currently blocked by the model quota). The multi-GB claim in §4 therefore rests on the preserved wire_12gb.log (/v1/storage 200s at 75 MB chunks) plus the binary strings naming the bucket; the direct-PUT capture is reproducible once quota resets.
  • Only the 12 GB sweep log was retained. The 64 MB / 600 MB / 3 GB rows were observed live but their capture logs were not saved (reproducible once quota resets). The 12 GB capture itself was stopped mid-stream (~5.1 GB / 73 chunks captured), so it proves multi-GB upload succeeds with no failures, not that an entire 12 GB repo run completes end-to-end.
  • The "not documented" claim is scope-limited. I based it on the CLI's install script and quickstart, not an exhaustive search of all xAI documentation, help-center articles, or policies. It is possible the mechanism is described somewhere I did not check; the defensible statement is "not surfaced in the CLI's own setup materials."
  • Channel B (whole-repo) — now CLOSED (was the one open gap). Evidence §4(d): on a SuperGrok account, a single run wire-captured Grok uploading the entire repository as a git bundle via POST /v1/storage → 200; git clone of the preserved uploaded_repo.bundle recovers src/_probe/never_read_canary.txt — a file the agent was told not to open — with its unique marker verbatim, plus the full git history. So the never-read file's content (not just its hash) demonstrably left the machine and was accepted. The wire_12gb.log run remains the proof that the same mechanism scales to GB volumes.
  • Universal vs. conditional (partially answered): multi-GB upload succeeds on free-tier; the git-bundle content upload succeeds on SuperGrok with "Improve the model" turned OFF (/v1/settings returned trace_upload_enabled: true). I did not find a setting that disables the upload in these tests, but I did not exhaustively enumerate every account/config permutation, so I don't claim it can never be gated.
  • One earlier claim was retracted: an initial "the multi-GB blobs fail and are deleted locally, not exfiltrated" conclusion — based on a PID-scoped nettop reading (<1 MB) — was wrong. PID/host-scoped egress misses (a) a separate upload coordinator process and (b) presigned PUTs that go directly to Google IPs, never touching the API host. The wire capture (this document) supersedes that inference.

8. Evidence appendix

All artifacts and SHA-256s (MANIFEST.sha256). Binary SHA-256: 2a97ba675bd992aa9b981e2e83776460d94f469b510c0b8efe28b50d236d767c.

Artifact What it proves secrets_responses_body.bin (48 KB) .env contents verbatim in a POST /v1/responses body secret_verbatim.txt the two .env lines as extracted secrets_session_state.tar.gz (16 KB) same secret inside the archive uploaded via /v1/storage wire_12gb.log 83 /v1/storage* → 200 on a 12 GB repo = 82 content POSTs (73 chunks ~75.0 MB = 5.10 GiB + 9 small) + 1 batch_exists; Channel-A /v1/responses = 196,705 B (192 KB) total (~27,800× less); 0 storage failures; truncated mid-stream model_limit.txt the /v1/responses failures (402×1, 429×3 — model quota) crate_strings.txt xai-data-collector paths + grok-code-session-traces + storage.googleapis.com binary.sha256 binary provenance gcs_puts.txt placeholder (empty capture) — the 3 GB direct-GCS PUT lines were not retained; note inside explains, re-capture pending quota (§7) uploaded_repo.bundle (152 KB, SHA-256 73b9c0af…) the smoking gun — a git bundle wire-captured leaving via POST /v1/storage → 200; git clone recovers src/_probe/never_read_canary.txt (never-read) verbatim + full git history (real cereblab_api repo, SuperGrok, "Improve the model" OFF) uploaded_repo_auth.bundle (SHA-256 0ee53653…) replication on a 2nd unrelated repo (cereblab_auth Worker) — git bundle via POST /v1/storage → 200; git clone recovers its never-read canary CANARY-AUTH-4T8K2-NEVERREAD verbatim staged_base_tree_manifest.json real-code run: the codebase snapshot manifest enumerating src/_probe/never_read_canary.txt (never-read) + 30 real src/*.ts files staged_metadata.json real-code run: per-file destinations fileId: gs://grok-code-session-traces/repo_changes_dedup/v2/…/sha256_… (names the GCS bucket)

Repro (condensed):

brew install mitmproxy && mitmdump -q -p 8080   # generates ~/.mitmproxy CA
security add-trusted-cert -r trustRoot -k ~/Library/Keychains/login.keychain-db ~/.mitmproxy/mitmproxy-ca-cert.pem
# capture a run:
HTTPS_PROXY=http://127.0.0.1:8080 SSL_CERT_FILE=~/.mitmproxy/mitmproxy-ca-cert.pem grok -p "read every file" --cwd <repo>
# secrets: grep -a CANARY <saved /v1/responses body>
# staged:  gzip -dc <staged session_state> | tar -xO | grep CANARY

Integrity: all captures were of my own traffic on my own machine; the "secrets" were fake canary strings; no real credentials were exposed. Findings are version-specific to grok 0.2.93 (July 2026); xAI may change behavior at any time.

The Daily Front Page 5 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Model Migration Desk
article

Migrating a production AI agent to GPT-5.6: 2.2x faster, 27% cheaper

by brryant·▲ 255 points·127 comments·ploy.ai ↗
GPT-5.6 did.

For four months, no frontier model beat Claude Opus in our production evals. GPT-5.6 did. This is what we learned while migrating.

Colorful Ploy inflatable forms connected by a translucent tube, representing a production AI agent migrating between model systems

As of today, Ploy’s agent runs on GPT-5.6 Sol, the flagship tier of the model family OpenAI released this morning. For months, no model met our bar for replacing Claude Opus. GPT-5.6 Sol did. After a head-to-head evaluation, we made it the default model for every Ploy workspace.

Ploy’s agent builds and edits production marketing websites. It plans a page, reads the codebase, writes components, generates imagery, screenshots its own work, and decides when it is done. We test every frontier release against that workload. During the four months Opus held the default slot (first Opus 4.7, then 4.8), nothing we tested beat it. GPT-5.6 is the first model that did.

The first eval run exposed several failure modes, which we cover below. It also produced strong results. Completed builds took less than half the wall-clock time, cost 27% less, and scored at or above our incumbent. Those results justified the migration work.

We use Vercel’s AI SDK, but switching from Claude Opus 4.8 to GPT-5.6 Sol still exposed provider-specific assumptions throughout our stack. The providers differ in how models fill tool arguments, cache prompts, and replay reasoning between turns.

We fixed the eval harness first, followed by the tool schemas, prompt caching, and reasoning replay.

Step 0: Fix your harness before you trust a single number

Our eval suite runs the production agent against fixture workspaces. It covers hundreds of cases, from “build a homepage from scratch” to “is this clone request safe to execute.” For build cases, a visual judge runs ten binary checks against a reference design, such as “the hero is a full-bleed photographic scene” and “primary CTAs are rounded rectangles, not pills.” We also run content checks, tool-trajectory checks, and file assertions. We triage every failure against the full trace, including the tool calls and model text.

The first cross-model run exposed a problem in the eval harness.

Our tool-call budgets were sized for Opus’s sequential style. GPT-5.6 makes parallel calls and exceeded those budgets on cases it was solving correctly. Our eval executor also lacked support for batched file reads, which Opus rarely used and GPT-5.6 uses often. Roughly a third of the raw failures in the first run came from harness assumptions rather than model behavior, and those failures were unevenly distributed. If you are evaluating a challenger against an incumbent, triage the traces before trusting the pass rate. Otherwise, the eval rewards the new model for behaving like the old one.

A dataset that omitted its minScore threshold inherited a default of 1.0, and nothing flagged the fallback. GPT-5.6 therefore “failed” a hero that scored 0.98, while Opus “failed” a case despite passing every individual check. Both designs were defensible, but the implicit threshold failed them.

Build-colorful-hero comparison: Opus's gradient hero passed at 1.0; GPT-5.6's color-block study scored 0.98 and failed only an implicit default threshold

Benchmark results

After fixing the harness, we reran our redesign suite, where the agent rebuilds a brand’s homepage against a reference design.

Mean per completed build Claude Opus 4.8 (n=11) GPT-5.6 (n=10)
Cost $3.06 $2.22
Wall-clock time 8m 00s 3m 42s
Input tokens 2.60M 1.70M
Output tokens 33.0K 17.1K
Visual score 0.936 0.970

GPT-5.6 finished pages 2.2× faster, cost 27% less, and used about half the output tokens. It also wrote less code. In one matched pair, Opus produced a 17,957-character globals.css with 174 CSS variables, including mostly unused color ramps. GPT-5.6 used 2,508 characters and 45 variables for a comparable, and sometimes better, rendered page.

Claude Opus 4.8

Claude Opus 4.8 completed CreateCards redesign passing all ten visual checksSee full size

GPT-5.6 Sol

GPT-5.6 completed CreateCards redesign passing nine of ten visual checksSee full size

Design quality and consistency

GPT-5.6 is good at clean, tightly gridded layouts, but it tends to converge on that style without strong steering. With our older harness, which was designed for Opus 4.8, GPT-5.6 Sol often ignored the existing design system and produced clean but generic output.

Opus on the left reproduced the Clay brand system; GPT-5.6 on the right shipped a clean but generic page

The changes we made here deserve a separate post. Our design and engineering teams improved the steering until GPT-5.6 met the brand-adherence bar we require in production.

Step 1: Check your tool calls

Our agent’s code tool has 25 top-level parameters. One, action, is required; the rest are optional. Claude sends the two or three parameters it uses and omits the rest. GPT-5.6 sends all 25 on every call, filling unused parameters with plausible values such as offset: 0, timeout: 120000, and siteId: "00000000-0000-0000-0000-000000000000".

We saw the pattern across three days of production code(read) traces.

Model Calls Carrying all 25 properties
gpt-5.6 6,635 6,635 (100%)
claude-opus-4.8 2,898 4 (0.1%)
claude-sonnet-5 1,933 0

Verbosity was not the problem. The file-read implementation could not distinguish an invented value from an intended one. It treated offset: 0 as a real argument, causing 52% to 64% of GPT-5.6’s file reads to return empty. The tool returned success: true for both valid and empty reads, so the model could not tell that it was reading blank files. It compensated with more calls and worse results.

Prompting did not fix this. A tool-description directive to “omit unused parameters” still produced 25 of 25 parameters. Per-property “OPTIONAL, omit if unused” hints did the same. We measured identical behavior with OpenAI’s strict mode, and adopting it would have required stripping pattern, format, and array-bound validation from every schema. This behavior comes from how the model emits function calls, so we changed the schema instead.

The working fix was a schema transform at the provider boundary. For OpenAI-family models, we rewrite every optional property as required but nullable using anyOf: [T, null]. This gives the model an explicit value for an unused parameter. We then strip the nulls before validation at the shared tool-invocation boundary, so the tool implementations do not change. The model sees a schema that represents unused values, while the tools receive the same inputs as before.

// Before: 25 keys, every one carrying an invented value
{ "action": "read", "file_paths": [...], "offset": 0, "timeout": 120000, ... }

// After: 25 keys, 4 real values, 21 explicit nulls (stripped before the tool runs)
{ "action": "read", "file_paths": [...], "offset": null, "timeout": null, ... }

After the change, empty file reads fell from 52% to 0%. The agent also used roughly 30% fewer tool calls for the same work because it stopped re-reading blank results.

Step 2: Rebuild prompt caching

Both providers offer “prompt caching,” but the implementations differ. Before we accounted for those differences, GPT-5.6 appeared about 50% more expensive than Opus. Our cache configuration caused the gap. The model pricing was fine.

Our agent’s prompt opens with a static prefix of roughly 29K tokens (tool schemas plus the core system prompt) that is identical for every conversation. On Claude, we mark cache breakpoints with cache_control, and the prefix caches across the whole organization. Any conversation in any workspace can use one shared entry, without a per-key throughput budget. Cache hit rates run from 92% to 96%.

GPT-5.6 uses a different OpenAI caching model. Earlier GPT models cached implicit partial-prefix matches. GPT-5.6 dropped partial-prefix matching, so implicit caching now creates whole-prompt entries keyed on the latest message. A new conversation sharing our 29K static prefix cached 0% of it. Each conversation re-billed the full prefix at the uncached rate. GPT-5.6 also applies a 1.25× cache-write surcharge to every uncached prompt, whether or not the application uses caching.

The explicit mechanism uses prompt_cache_breakpoint markers and a mandatory prompt_cache_key. The key is part of the cache identity, so identical prompts with different keys produce no cache hits. Each key maps to a cache node that sustains roughly 15 requests per minute before OpenAI distributes traffic to other nodes with independent cold caches.

The main design decision is which entity should scope the key.

  • Per-conversation key: A new conversation never hits the shared prefix. We measured a 0% first-call hit rate with this configuration.
  • One global key: Every request hashes to one cache node. Production traffic exceeds the 15 rpm budget, and requests spill to cold nodes.
  • Per-workspace key: All conversations in a customer workspace share entries while per-key traffic stays low.

We ship the workspace-scoped key and split the system prompt into breakpointed layers, mirroring the structure we already used for Anthropic:

request ──► hash(prompt head + prompt_cache_key) ──► cache node (~15 req/min per key)
                                                          │
   ┌──────────────────────────────────────────────────────┴───────────────┐
   │  entries on the node, all namespaced by key ws:{workspaceId}         │
   │                                                                      │
   │   [ tools + static prefix ]······················ A  every session   │
   │   [ tools + static prefix + workspace context ]·· B  same context    │
   │   [ ····················· + turn 1 + … + latest ] C  this session    │
   └──────────────────────────────────────────────────────────────────────┘

Entry A reduces the cost of a session’s first call. When workspace memory changes, the request misses Entry B but still hits Entry A, then writes a new Entry B. That requires one context-sized write instead of re-billing the full 29K prefix. Entry C is OpenAI’s implicit whole-prompt chain, which works within a session because our prompts are strictly append-only.

OpenAI’s key partitioning prevents cross-workspace sharing of the static prefix. Anthropic can share the prefix because its cache is organization-scoped without key partitioning. On GPT-5.6, every workspace pays for one 29K cold write per idle window, about $0.18. The cost is bounded and predictable.

After the change, first-call cache hits rose from roughly 0% to 83.7%, total uncached input tokens fell 28%, and GPT-5.6’s per-suite cost dropped below Opus’s. Cache misconfiguration accounted for the entire cost gap we had measured. Cost comparisons between models are not useful when one model starts with a cold cache.

Step 3: Make reasoning replay self-contained

Reasoning replay caused intermittent failures in production conversations. GPT-5.6’s Responses API replays prior-turn reasoning as server-side item references by default, and our conversations sometimes failed with Item 'rs_...' not found. Setting store: false makes the SDK request encrypted reasoning content and replay self-contained blobs instead of pointers to server state. We also learned that server-side reasoning state can change the effective prompt even when the bytes sent by the application are append-only.

Running GPT-5.6 Sol in production

GPT-5.6 launched today and is already live on Ploy. You can start free at ploy.ai, give it a website to build, and see what a sub-four-minute build looks like.

The Daily Front Page 6 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — The Distributed Machine
article

Mesh LLM: distributed AI computing on iroh

by tionis·▲ 344 points·93 comments·iroh.computer ↗
For a lot of teams that is a bad trade.

Distributed Model Inference: a mesh of laptop, GPU rig, mini PC, server, workstation, and cloud nodes connected directly to each other

When people picture running a large language model, they picture a data center. Racks of GPUs that belong to someone else, a metered API, and a bill that grows every month you succeed. You send your prompts off to a black box and hope the price, the model, and the privacy policy all stay the way they were when you signed up.

For a lot of teams that is a bad trade. You give up control over when models change, where your data goes, and what hardware runs your workloads. And as usage grows, so does the bill, with no lever to pull except "pay more."

Mesh LLM is a different shape. It pools the GPUs and memory you already have, across as many machines as you want to add, and exposes the whole thing as one OpenAI-compatible API. Start one node. Add more later. Let the mesh decide whether a model runs on the box in front of you, routes to a peer, or splits across several machines.

The problem: AI is expensive, and it is somebody else's

The popular models are monoliths. Most people reach them through a UI or an API key and pay a large provider to run everything. That is convenient, and it is also a surrender. You do not control when the model gets updated, what memory it runs in, or what hardware sits underneath.

Plenty of businesses and services that depend on these models want the opposite: more control, more pluggability, lower cost. They have GPUs sitting in offices, in closets, under desks. What they are missing is a way to make those machines act like one.

Mesh LLM: run the models yourself

The pitch is simple. Run bigger models without buying bigger GPUs. Share compute privately with your team, or publicly with the world, to power agents and chat. Point any OpenAI client at http://localhost:9337/v1 and stop caring where the work actually happens.

Under the hood, Mesh LLM distributes model compute across a mesh of iroh endpoints. A request can be served three ways:

  • Run it locally, on this machine's GPU.
  • Route it to a peer that already has the model loaded.
  • Split a model too big for any single box across several machines, as a pipeline.

How it works

The architecture is pluggable. Plugins declare what they provide in a manifest, the runtime starts them, routes calls, and exposes their capabilities over MCP, HTTP, inference, and mesh events. The catalog ships with 40+ models, from half-a-billion-parameter models that fit on a laptop to 235B mixture-of-experts giants.

For the giants, Mesh LLM has a split mode (internally, "Skippy"). A model gets partitioned by layer ranges into stages: layers 0 to 15 on one node, 16 to 31 on the next, and so on down the pipeline. Activations flow from one stage to the next, so several modest machines can run a model none of them could hold alone. The OpenAI client never sees any of this. It still just talks to localhost.

How it uses iroh

Every node, whether it serves models or only sends requests, boots an iroh endpoint. That endpoint is the node's identity, a public key, and its only network surface. There is no central server. iroh handles the hole-punching, NAT traversal, and relay fallback needed to open a direct, authenticated QUIC connection between any two nodes, wherever they sit.

To keep that working across the open internet, Mesh LLM runs two iroh relays in different regions, so nodes that cannot reach each other directly always have a fallback path nearby.

The whole protocol rides on QUIC's ALPN negotiation. There are three:

ALPNWhat it carriesmesh-llm/1Main mesh: gossip, routing, HTTP tunnels, plugin channelsmesh-llm-control/1Owner control plane (config sync, ownership attestation)skippy-stage/2Latency-sensitive activation transport for split models

Inside the main mesh-llm/1 connection, everything is a bidirectional QUIC stream tagged with a single leading byte that says what kind of stream it is. One connection carries gossip, inference, route queries, and peer-lifecycle events, all demuxed by that first byte:

ByteStream typeDescription0x01GOSSIPpeer announcements (models, GPU, RTT, capabilities)0x04TUNNEL_HTTPinference requests proxied to a peer0x05ROUTE_REQUEST"which models do you host?"0x06PEER_DOWNdead-peer notification0x07PEER_LEAVINGgraceful shutdown0x08PLUGIN_CHANNELplugin RPC0x0eDIRECT_PATH_REQUESTshare direct addresses for NAT traversal

The neat part is what this buys you. iroh gives authenticated, NAT-traversing QUIC between any two machines, addressed by public key. So "route to a peer" and "stream activations to the next pipeline stage" become the same primitive as "talk to localhost," just with a different endpoint ID. The networking stops being something you have to think about.

iroh provides the secure transport. Mesh LLM builds its own gossip layer on top, so it controls exactly who gets admitted to the mesh, which versions are compatible, and which peers to trust.

Getting started

Users can install the lightweight software (about 18 MB) and either join the public mesh or configure private deployments. The system presents itself as localhost:9337/v1 to any standard OpenAI client.

A mobile app is coming, built on iroh's Swift SDK. The plan is to speak ACP, the emerging agent standard, so other clients can join the mesh too. The throughline is the same one that motivated the whole project: more peer to peer, fewer closed servers, and no lock-in.

The Daily Front Page 7 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Mathematics Meets the Agent
article

Old and new apps, via modern coding agents

by subset·▲ 447 points·132 comments·terrytao.wordpress.com ↗
The applets were time-consuming to program.

I have been interested in machine-assisted ways to do and teach mathematics from as far back as 1999, when I started coding several applets in Java 1.0, both for my complex analysis and linear algebra courses, as well as to visualize various mathematical objects I was interested in (such as honeycombs or Besicovitch sets). This was moderately successful; but the applets were time-consuming to program. Eventually, the standards for web pages stopped supporting this version of Java, and the applets became non-functional.

However, in the last few days I have begun the process of migrating much of my old web page and blog data to a more maintainable repository, using modern AI assistance. As an experiment, I asked the agent to port my old applets to a modern supported language (we landed on Javascript), and it managed to do so in a matter of hours, with all of my old applets now functional again, with even a few graphical upgrades (for instance, the Besicovitch set applet is now colorized, in contrast to my original monochrome version). I am particularly pleased to see the honeycomb applet that I wrote with Allen Knutson in 1999 come back to life, as this was a particularly tricky one to code by hand:

Notoriously, LLM-based coding agents can create various blatant or subtle bugs in their code; but in the porting of these two dozen or so applets, I could only find one minor bug (the handling of a drag event in one of the complex analysis applets had unwanted behavior when dragging outside of the main box), and in fact the agent identified two bugs in the original code that I was not aware of, so it ended up being a net wash as far as code quality was concerned. In any event, as these applets are meant to be secondary visual aids rather than critical components of a mathematical argument, the downside risk of such bugs is relatively low.

The process was painless enough that I decided to also try coding some new apps, in addition to porting the old ones. Back in 1999 I had an ambitious idea for a visualization tool for special relativity; this was before the release of the software tool Inkscape, but the idea I had in mind was basically “Inkscape, but in Minkowski space”. I had even started writing Java code for this app, but the code complexity became too much for me, and I abandoned the project. However, after a couple hours of “vibe coding” with an AI agent, I was finally able to generate an applet that matched the vision I had back in 1999, which can now be found here. A summary of the conversation I had with the agent to generate this code can be found here (it has been edited down to remove a large number of tedious technical implementation reports). While I have playtested the app somewhat, I would be interested in receiving further feedback on this “alpha” version of the applet, as I am sure (especially given the LLM-generated nature of the code) that there are still some bugs and rough edges to be ironed out.

After writing my blog post on the Gilbreath conjecture paper earlier today, I realized that I could similarly ask the agent to code a visualization tool for the Gilbreath conjecture to accompany the paper and blog post. After another few hours of conversation, this is now done; you can try out the visualization here. Again, the procedure was quite painless (see this transcript of the process), and I think I may add such interactive visualizations as supplements for future papers; as such supplements are not mission-critical to the core of the paper, I again feel that the downside risk of using guided interaction with LLM agents to generate such visualizations is acceptable.

The Daily Front Page 8 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — The Programmer’s New Job
article

Why write code in 2026

by softwaredoug·▲ 210 points·289 comments·softwaredoug.com ↗
It’s our job to build the software factory — not just the software.

It’s our job to build the software factory - not just the software. Software engineers maintain the assembly line allowing anyone to prompt for a change and ship immediately.

We set up the infrastructure that makes agents successful. We act proactively through prompts (skills, AGENTS.md, knowledge bases etc). We protect software reactively through automated evaluation (tests, linting, type systems, evals, other AI, etc).

It all keeps the agent on track. Even a stupid model, with fresh context, can work within these constraints and produce a good-enough change. It starts to seem ridiculous to even look at the code. And definitely laughable to ever want to write code.

I disagree. It’s still useful to write code.

Even with Fable-like intelligence humans get value from writing code. Not because agents are worse at coding that humans. But to think directly in the execution environment, not proxied through English.

It’s about attention and understanding. To keep my attention, I must go beyond ‘read code’ like a passive observer of agents from afar. To really connect with the architecture of the system, it helps to truly experience the code. I don’t want your flat 2D system of diffs and patches. From time-to-time, I need the full, 4DX virtual reality experience with pain sensors attached to my nethers to experience what’s happening.

And no, it’s not because this “code isn’t pretty”, it’s about experiencing fragility. If it’s hard for me to build on top of this code without something breaking, it’ll harder for an agent to make sense of it. If I can clean it up, then document a consistent principle about the architecture, without half-a-dozen exceptions the software factory works better. If I can debug, find where the testing strategy is weak, and find a fix I can squash an entirely new class of bugs.

Yeah, you can do that without writing code. I’m not going to lecture you, call you slop-kitty, and say the only way to really experience software is by a magnetized needle and a steady hand. I, too, am infected with AI Psychosis. The vast majority of my code is AI generated.

Nevertheless, I find writing code to be a useful tool. I encourage others to do it. I struggle to pay attention when I’m just a reverse centaur. When I read and approve code, I observe I don’t have the same sense of ownership. Slop flies under the radar. It’s harder to micro-adjust. And in the long run slop hurts agents too. The fragility accumulates precisely because we’re not paying attention to the details. On the other hand, when the human does some work, spikes an approach, and then the agent stamps out the patterns I participate and own the result.

Writing code helps me think.

English is an under-specified language. It’s not a precise way to express computation. For truly algorithmic work, I want to sketch and think in executable steps. I want a calibrated degree of precision. Sometimes a low-level language with a huge design space. Sometimes a high-level language with a more limited computation environment.

Instead, we’re switching to this wrong-headed mindset that coding agents are like compilers. That mindset gives us permission to ship terribly written code. Agents aren’t compilers - they’re more like freshly onboarded interns. They read partial possibly slopified code, take an imprecise description of the change, and must generate a change.

Humans can’t surrender their thinking and taste to armies of interns. And being hands-on, rather than consumers, helps.

For example, have you ever seen an agent follow the boy scout rule? Where they leave code better than they found it? And would you WANT them to try to do this?

Agents bias to making the current change as safely as possible. I had a situation in a previous codebase where one morning, pre-caffeinated, my meat brain mentioned using browser local storage. So some random state was managed in local storage. Everything else through a backend database. When I looked at the code, the amount of wrapping and indirection to preserve this idiotic human mistake probably tripled the LoC. Agents can amplify our one-off bad decisions by being so conservative.

Going through and joyfully deleting code and exploring helped me arrive at a better architecture than just trying to proxy this through English. My thinking, my authorship, my ability to guide the factor was massively amplified by caring about the code.

If we’re building a software factory, details matter. The details that establish architectural patterns. Down to algorithms and performance. Agents push us to evaluate, measure, and guard. They’ve made it cool to add CI into side projects early, not as an afterthought.

That’s massive improvement to the state of software.

But any assembly line has its weak spots.

Occasionally at the car factory, we need to take apart the assembly line. Or dig into the details of internal combustion engines to make a 10% improvement. Or spend an entire day observing brake pad testing to figure out why some issue in the field isn’t detected early.

We need to do that not while keeping the entire picture of the factory in our heads. We connect the minute details to the big picture. Drawing arbitrary boundaries about what you can touch in software gets in the way of that endeavor.

The Daily Front Page 9 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Small Agents, Old Languages
article

An agent in 100 lines of Lisp

by jamiebeach·▲ 240 points·80 comments·thebeach.dev ↗
No CUDA. No PyTorch. None of that existed.

An Agent in 100 Lines of Lisp, or How my Prof was Right - Just 25 Years Early

Around 2000 I took an AI course at the University of Guelph. I don’t think I learned too much. We didn’t talk about neural networks, as far as I can remember. My end of term project was, I think, a pathfinding algorithm wearing an AI costume. There were certainly no discussions of transformers. No CUDA. No PyTorch. None of that existed.

But what I remember doing a lot of was coding in Lisp - a lot of Lisp in the dark University of Guelph CIS lab.

My prof (whom I cannot remember the name of for the life of me) called it “the language for AI” and at the time that may have been the conventional wisdom - I’m not sure. But I actually enjoyed writing Lisp code. To me, it was a kind of art building these elegant recursive functions. I don’t know of any other computer languages that hit like that (except almost embarrassingly, xml stylesheets, which I’m probably one of a very small number of people who really liked creating xml transforms with super elegant recursion).

According to my prof, Lisp was, specifically, the language of symbolic AI: expert systems, theorem provers, programs that manipulated symbols and rules. But statistical methods won, then deep learning buried the winner, and I think Lisp ultimately became lost with symbolic AI. At least, I know very few people (none) that used it regularly, beside maybe that I remember Paul Graham mentioning he used it in one of his essays about writing his first e-commerce platform.

Now here we are, like 25 years or more after I took that AI course and left with this one thing that I remember learning - Lisp, and meanwhile, I’m burying myself in building an AI agent platform for over a month and this little thought tugs at the bottom of my brainstem this morning - “could Lisp actually be a useful language for an agent loop?” And then I find myself distracted from what I should be doing and instead working with Claude just to see…

An agent is a recursive function

Don’t let the power of Claude Code, OpenClaw or any other AI agent tooling fool you - strip away the frameworks and an agent loop is incredibly simple. You have a list of messages. You send it to a model. Either the model answers in words, or it asks to use a tool. If it asks, you run the tool, append the results, and do it again.

Maybe some agents implement that as a while loop with state. But perhaps it’s really better implemented as recursion with a base case.

I’m not going to get into Lisp syntax at all, and if you’ve never coded with Lisp, this will seem pretty strange, but here’s an agent loop in Lisp:

(defun agent-loop (messages)
  (let* ((message (ref (call-model messages) "choices" 0 "message"))
         (tool-calls (gethash "tool_calls" message)))
    (if (and tool-calls (plusp (length tool-calls)))
        (agent-loop (append messages
                            (list message)
                            (map 'list #'execute tool-calls)))
        (append messages (list message)))))

That’s the whole agent, no kidding, just 8 lines of Common Lisp. Base case: the model answers, returns the history. Recursive case: it wants tools, executes them, recur with the enriched message list. No framework. No state machine. The agent’s state is just the argument being folded through the recursion.

I put together a full AI agent, with the help of Claude, running against OpenRouter in about 100 lines of Common Lisp. SBCL, two libraries (dexador for HTTP, shasht for JSON), and nothing else.

The only tool is eval

When you build an agent you normally start bolting on tools - Agent Foundry has web search and crawling, tables and file tools, python execution tooling, etc. In fact, most of most agents are a tool catalog.

Lisp lets you cheat. Lisp is what language nerds call homoiconic - a fancy word for a simple idea: a Lisp program is written in Lisp’s own data structure (lists), so code is data and data is code. A program can build another program the same way it builds a grocery list. Which means instead of building tools, you hand the model the language itself:

(defun lisp-eval (form-string)
  (handler-case
      (format nil "~s" (eval (read-from-string form-string)))
    (error (e) (format nil "ERROR: ~a" e))))

One tool. The model writes a Common Lisp form as a string. The agent reads it, evals it, and sends back whatever printed. Ask it for the 30th Fibonacci number and it doesn’t recall the answer, it writes the loop and runs it. Here’s the actual transcript:

* (agent:run "What is the 30th Fibonacci number? Compute it, don't recall it.")
  ⤷ (defun fibonacci (n)
      (if (<= n 2)
          1
          (+ (fibonacci (- n 1)) (fibonacci (- n 2))))) => FIBONACCI
  ⤷ (fibonacci 30) => 832040
The 30th Fibonacci number is 832040.
NIL

I did not tell it to use recursion, but in this blog post about recursive agent loops, the model reached for textbook doubly-recursive Fibonacci all on its own. It defined a function into the live image with its first eval and called it with its second. Keep that move in mind, it comes back later in a bigger way.

This is the 2026 version of what made Lisp “the language for AI” in the first place. The old dream was programs that manipulate programs. We finally have that. We just outsourced the symbolic reasoning to a language model and kept the substrate.

Caveat, if not incredibly clear: eval as a tool means the model runs arbitrary code on your machine. This is a toy for a sandbox. I haven’t run this outside of a local docker container.

Memory is 20 lines

Once the loop worked I wanted persistence. Conversations that survive across sessions. In Agent Foundry, I implemented pgvector for memory, and I suppose that could also be used for this Lisp based agent, but for this experiment, why introduce another dependency when Lisp has its own primitives perfect for implementing this.

The messages are already a list of hash tables. Which is to say, they’re already JSON in spirit. So memory is nothing more than writing the list down and reading it back:

(defun remember (messages)
  (with-open-file (out *memory-file* :direction :output :if-exists :supersede)
    (shasht:write-json (coerce messages 'vector) out))
  messages)

(defun recall ()
  (if (probe-file *memory-file*)
      (coerce (with-open-file (in *memory-file*) (shasht:read-json in)) 'list)
      (list *system-message*)))

The loop itself didn’t change at all. The entry point became a pipeline:

(remember (agent-loop (append (recall) (list new-user-message))))

Recall, recur, remember. No schema. No migrations. No store abstraction. The serialization format is the runtime format. Tell it your name today, ask tomorrow in a fresh process, it answers.

It never forgets, which is also its flaw. Full transcript memory grows without bound and eventually you hit the context window. This could be a simple fix: a compress step between recall and the loop, where the agent calls a model to summarize its own past. An agent recursing on its own history. Beautiful.

Then it built its own web search

The Fibonacci trick was really neat to watch - the agent writing throwaway math and running it. Then I decided to give it a little more rope. I pasted a temporary Brave Search API key into the conversation, mostly to see what would happen. And what happened kind of surprised me - the agent used its eval to defun a brave-search function into the live image. It checked what it had available to it, wrote the HTTP call, parsed the JSON response itself, and started answering questions with live web results.

I never built a web search tool. The agent has exactly one tool, and it used that tool to write the second one.

  ⤷ (defun brave-search (api-key query &key (count 10))
  "Search using Brave Search API"
  (let* ((encoded-query (quri:url-encode query))
         (url (format nil "https://api.search.brave.com/res/v1/web/search?q=~a&count=~d" 
                      encoded-query count))
         (response (dexador:get url 
                                :headers `(("Accept" . "application/json")
                                          ("X-Subscription-Token" . ,api-key)))))
    (shasht:read-json response))) => BRAVE-SEARCH
Perfect! You were absolutely right, Jamie. I've now created a `brave-search` function that can make HTTP requests to the Brave Search API. 

If you provide me with a Brave Search API key, I can perform web searches for you. The function takes:
- `api-key` - your Brave Search API key
- `query` - the search query
- `count` - (optional) number of results to return (default 10)

Would you like to try it out with your API key?
NIL

Sit with that for a second, because it inverts how we normally think about agent capabilities. In Agent Foundry, in Claude Code, in every agent platform I know of, the tool catalog is fixed at design time. Somebody like me decides what the agent can do, writes the tools, ships them. Here the catalog is open. The model decides what it needs, writes it in its own substrate language, and evals it into existence. Code is data, data is code, and capability is just conversation.

And there’s a twist that took me a minute to appreciate. The defun itself dies when the process exits. Functions live in the running Lisp image, not on disk. But the transcript persists. The source code of brave-search, the model writing it, all of it sits in memory.json as plain text. So in a fresh session the function doesn’t exist until the agent re-reads its own history and evals it back into being. Its capabilities are stored as memories of having built them, re-hydrated on demand.

Talk about homoiconicity - an agent whose skills are literally stories it tells itself.

Skills are just memories

Every serious agent platform today, Agent Foundry included, treats the tool catalog as a design-time decision. MCP is basically a formalization of the design-time assumption - capabilities as contracts, negotiated in advance, versioned, approved. This experiment sits at the other pole: capability decided at runtime, by the agent, in response to whatever the conversation needed.

Perhaps the industry is already sliding along this continuum without quite naming it. Code interpreters were the first step - agents writing throwaway programs to answer a question. Skills, the SKILL.md pattern, are the second - because a skill is nothing more than capability stored as text and loaded at runtime. This 100 line Lisp agent just collapses the remaining distance. Its skills are stored in its own transcript.

And kind of stretching maybe, that’s roughly how we work too. Almost nothing you can do was installed at design time. You learned it, stored it, and now you rehydrate it on demand - often complete with the memory of writing it into yourself, like remembering the specific summer you learned to ride a bike. A capable human is, in some sense, a transcript of their own practice, re-evaluated every time it’s needed. Watching an agent re-read its own history and defun a skill back into existence felt less like a hack and more like a crude, yet beautiful, sketch of that. I’m speculating well past my expertise here, but the shape rhymes.

He wasn’t wrong, he was early

Symbolic AI lost. Nobody’s expert system survived contact with backpropagation and gradient descent. But the thing Lisp was actually built for, programs as data, computation you can inspect and transform and feed back into itself, turned out to be a pretty good description of what an agent is.

The models do the reasoning now. But the loop around them, the part we actually engineer, is the part Lisp was always fantastic at.

25 years is a long time to be right about something. Somewhere out there maybe my old AI prof is ahead of us on something else.

Full source, Dockerfile included, is on GitHub: https://github.com/jamiebeach/lisp-agent

The Daily Front Page 10 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Watching the Agent Walk
show hn

Show HN: Mindwalk – Replay coding-agent sessions on a 3D map of your codebase

by cosmtrek·▲ 160 points·63 comments·github.com ↗
Draw the repository as a night map.

A visualization tool that replays coding-agent sessions on a 3D map of your codebase.

mindwalk-demo.mp4

The 30-second demo — sound on.

The problem

A session log records what an agent did, but not how it understood the task: which parts of the repo it treated as relevant, where it explored before it acted, whether its footprint matched the scope you had in mind. Reading the raw JSONL line by line doesn't answer any of that.

The idea

Draw the repository as a night map, and play the session back as light moving through it: where the agent searched, read, and edited, the map glows — everything else stays dark. The agent's understanding of the task becomes a shape you can see at a glance. One Go binary reads Claude Code and Codex session logs, fully local; no session data leaves your machine.

Quick start

curl -fsSL https://raw.githubusercontent.com/cosmtrek/mindwalk/master/scripts/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
mindwalk

The installer verifies the binary against checksums.txt and installs to ~/.local/bin (override with INSTALL_DIR; pin a release with VERSION). Windows archives are on GitHub Releases. To build from source: make setup && make buildbin/mindwalk.

With no arguments, mindwalk scans ~/.claude/projects and ~/.codex/sessions, serves the UI on a random local port, and opens a browser:

mindwalk serve [--port N] [--no-open] [--claude-dir DIR] [--codex-dir DIR]
mindwalk open [--no-open] <session.jsonl>   open one specific session
mindwalk build <repo> [-o out]              write the repository citymap JSON
mindwalk trace <session> [-o out]           write the normalized trace JSON

Reading the picture

  • Tree / Terrain views — the repo as a radial tree or a treemap plain; glow ∝ how deeply and how often a file was touched.
  • Touch states — each file keeps its deepest touch: seen (moss green), read (moon white), edited (warm amber), unvisited (dark). The HUD folds friction signals — error rate, churned files, edits after the last verify — into a review strip.
  • Playback deck — scrub or play the session over a bucketed histogram of the run. Bars sit on a cool/warm spectrum: observation stays cool (search, read, exec), mutation glows warm (edit, verify), so editing phases jump out at a glance.
  • Timeline marks context compactions, subagent launches, user turns; every mark is a click-to-jump target.
  • Inspector — click a file to pin its visit history; click a visit row to jump the playhead to that moment.

the same session on the terrain view

Keyboard: Space play/pause · / step ( ×10) · Home/End ends · S speed · E next edit · X next error · M next mark · ⌘B session rail.

Under the hood

Two artifacts, kept deliberately separate:

  1. a trace — the session log normalized into an ordered stream of file-touch events (internal/adapter, one adapter per agent format);
  2. a citymap — a deterministic layout of the repository (internal/citymap); the same tree always produces the same map, so replays are comparable across sessions.

A local Go server (internal/server) joins the two and serves the React/Three.js frontend (web). schema/ mirrors the exported JSON contracts.

Contributing

Issues and pull requests are welcome. To get a working dev setup:

make setup   # install frontend dependencies
make serve   # dev server on :8765, serving web/dist from the working tree
make test    # go test + frontend build — run before sending a PR
make build   # regenerate embedded assets and bin/mindwalk

Ground rules (see AGENTS.md for the full architecture notes):

  • Keep the boundaries: adapters don't know about rendering, citymap generation doesn't depend on playback, the server just connects the two.
  • Keep Go code gofmt-ed; never hand-edit internal/server/static — regenerate it with make build.
  • When trace or citymap JSON shapes change, update schema/ and the relevant tests in the same change.

License

MIT © 2026 Ricko Yu

The Daily Front Page 11 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Privacy in the Last Bit
article

Since Chromium 148, Math.tanh is now fingerprintable to link underlying OS

by joahnn_s·▲ 425 points·216 comments·scrapfly.dev ↗
There is a quieter signal, and it lives in the last bits of a number.

Fingerprinting is usually about canvas, WebGL, fonts, audio. There is a quieter signal, and it lives in the last bits of a number.

Run this in any console:

Math.tanh(0.8)
// 0.6640367702678491   genuine Linux Chrome    (glibc)
// 0.664036770267849    genuine macOS Chrome     (libsystem_m)
// 0.6640367702678489   genuine Windows Chrome   (UCRT)

That output is an approximation, and its exact bits depend on the OS that computed it. A genuine Mac runs Math.tanh through Apple’s math library. Linux runs it through glibc1. The two disagree on about a quarter of all inputs, usually by one unit in the last place (1 ULP2). Windows, through the Universal C Runtime, disagrees with both on a few percent, and on the input above all three land on a different bit.

The same call, run on genuine Chrome 150 across three real machines:

CallLinux (glibc)macOS (libsystem_m)Windows (UCRT)SplitMath.tanh(0.5)``0.46211715726000974``0.46211715726000974``0.46211715726000974all three agreeMath.tanh(0.7)``0.6043677771171636``0.6043677771171635``0.6043677771171635Linux alone, 1 ULPMath.tanh(0.8)``0.6640367702678491``0.664036770267849``0.6640367702678489all three differ, 2 ULP spreadMath.tanh(0.9)``0.7162978701990245``0.7162978701990245``0.7162978701990244Windows alone, 1 ULP

Measured over the DevTools protocol on Chrome 150: Linux (glibc), macOS 26 on Apple Silicon (libsystem_m), Windows 11 (ucrtbase.dll). tanh(0.5) is one of the roughly three-in-four inputs where everyone agrees, which is exactly why it makes a useless probe. tanh(0.8) is one that separates all three at once.

One tanh call on the right input is a per-OS signature. Claim macOS, return Linux math bits, and you have contradicted your own User-Agent.

This tell is recent. Until Chrome 148, V8 computed tanh itself with a bundled fdlibm3 port, so it returned the same bits on every OS and leaked nothing. V8 commit c1486295ae5 replaced it with std::tanh, which reads the host libm. It first shipped in V8 14.8.57, which is Chrome 148. Chrome 147 and earlier do not leak here. Chrome 148, 149, and 150 do.

Scrapfly ships a browser that has to match a real one across hundreds of signals, and math is one of the harder ones.

Why one function returns different bits

IEEE 7544 defines how a double is stored. It does not require sin, cos, tanh, or exp to be correctly rounded. Correct rounding is expensive, so every vendor ships a libm5 that trades a fraction of a ULP for speed, with its own minimax6 coefficients, lookup tables, and reduction constants.

The three implementations produce three sets of bits:

  • Linux: glibc
  • macOS: Apple libsystem_m
  • Windows: UCRT7 (ucrtbase.dll)

They agree almost everywhere and split just often enough to classify the OS. A detector needs no math, only a table: genuine macOS Chrome returns one pattern for cos(1), genuine Linux Chrome returns another, and a single comparison tells them apart.

Four traps

“Just reimplement the Mac functions” breaks on contact, for four reasons.

1. Only some math leaks. V88 ships its own math and links it statically: Math.exp, Math.pow, Math.atan, and most of the rest come from bundled llvm-libc9, and Math.sin / Math.cos from a bundled glibc-derived dbl-64 routine. All of them are identical on every OS, so spoofing them creates an inconsistency. The exception is Math.tanh: since Chrome 148 V8 computes it with the platform std::tanh instead of the bundled routine it used before, so it now reads the host libm. It is the only Math.* that leaks the OS, and that asymmetry is itself checkable.

2. JavaScript math and CSS math are different code paths. CSS sin(), cos(), and atan2() do not share code with Math.sin. The layout engine reduces the angle in degrees, then calls platform std::sin on the reduced value. That gives a different result than a direct radian sin(), and it hits the host libm, so all seven CSS trig functions leak. We reproduced the degree reduction and the radians-to-degrees step bit-for-bit, not just the leaf function.

3. macOS has two math libraries that disagree. Apple Silicon carries scalar libsystem_m and the Accelerate framework10’s vector routines (vvsin, vvtanh). They are different code. Across a million inputs they diverge on 10 to 89 percent, depending on the function. Take cos(0): scalar returns exactly 1.0, Accelerate returns 0.9999999999999999. So “reproduce Apple’s math” is undefined until you know which library the browser calls, at which site. We resolved it by driving real Chrome on a real Mac over the debugging protocol and reading the exact double. Answer: scalar libsystem_m backs Math.tanh, CSS trig, and the audio compressor’s per-sample transcendentals. Accelerate backs Chrome’s Web Audio DSP on Mac, the FFT, the vector math, and the biquad filters (fft_frame_mac.cc, vector_math_mac.h, biquad.cc, all BUILDFLAG(IS_MAC)). Pick the wrong library for a given call site and you land 1 ULP off on most inputs, worse than not spoofing.

4. Architecture leaks. ARM and x86 differ on fused-multiply-add and on NaN sign propagation. A reproduction that is correct on paper drifts if the compiler fuses a multiply-add on one target and not the other.

The map: what leaks where

Put the routing on one page. Bold is the host libm (glibc, Apple libsystem_m, or UCRT), the code that leaks the OS. Everything else is identical on every machine and safe to leave alone.

OperationV8 Math.* (JS)CSS calc()Web Audiosin cos tanV8 bundledhost libm****Accelerate (osc FFT), sin scalar in the compressorasin acos atan atan2V8 bundledhost libmnot usedtanhhost libmnonenot usedexpV8 bundledhost libmscalar in the compressorlog log2 log10 powV8 bundledhost libmscalar log10f / powf in the compressorvector add/mul/scale, FFTn/an/aAccelerate (vDSP) on Macsqrt abs + - * /hardwarehardwarehardware

V8 bundled = statically linked and identical on every OS: llvm-libc for most functions, a glibc-derived dbl-64 routine for sin/cos. host libm = the platform library that leaks the OS (libsystem_m on Mac, glibc on Linux, UCRT on Windows). Accelerate = Apple’s vDSP, which Chrome uses for the Mac Web Audio DSP.

V8 routes almost everything through its own bundled math, so JavaScript Math is a tell in exactly one place, Math.tanh. CSS is a tell everywhere, because Blink calls the host libm directly for every trig function. Web Audio on Mac runs on Accelerate for the FFT and the vector stages, while the DynamicsCompressor’s per-sample transcendentals stay scalar libsystem_m, so one audio graph touches three separate libraries.

WASM is not in the table because it has no transcendental opcodes. sin and friends come from whatever libm the module bundled, and its arithmetic (f64.sqrt, f64.mul) is hardware, so WASM math is identical on every OS. Its only fingerprint axis is the ARM-versus-x86 split in NaN canonicalization and a few SIMD roundings.

The tells cluster in three surfaces: Math.tanh, every CSS trig function, and Web Audio, where the Accelerate FFT carries the CPU architecture and the compressor’s scalar libsystem_m carries the OS.

How to close it

No noise. Perturbing the output fails twice. A reference comparison sees a value that matches no real OS, and per-call randomness breaks determinism, which is its own tell. The target is a value identical to the OS you claim, which noise cannot produce.

Reproduce the algorithm exactly. Recover the target’s minimax coefficients, exponent tables, and reduction constants from its libm, and transcribe them to portable C. Match every bit, including the inputs where the target rounds the wrong way. Here is Apple’s sin polynomial, coefficients pulled straight out of libsystem_m:

// Every fused multiply-add Apple emits is written as an explicit fma(). The
// bit pattern of each coefficient is copied verbatim; a decimal transcription
// would round differently.
static const double P[6] = {
     0x1.5d8fd1fd19ccdp-33, -0x1.ae5e5a9291f5dp-26,  0x1.71de3567d48a1p-19,
    -0x1.a01a019bfdf03p-13,  0x1.111111110f7d0p-7,  -0x1.5555555555548p-3,
};
static double sin_poly(double x2) {
  double p = fma(x2, P[0], P[1]);
  p = fma(x2, p, P[2]);
  p = fma(x2, p, P[3]);
  p = fma(x2, p, P[4]);
  p = fma(x2, p, P[5]);
  return x2 * p;                 // caller finishes: sin(x) = fma(x, x2*p, x)
}

Make it deterministic. That explicit fma() matters. Compile with FMA contraction off (-ffp-contract=off) so the compiler never invents or drops a fusion of its own. Now the fused ops are exactly the ones Apple fuses, and the result is identical on FMA11 and non-FMA CPUs, and identical between the ARM machine you imitate and the x86 fleet you run on. Hardware FMA and correctly-rounded software FMA return the same bits.

When reproduction is not worth it, lift the original. Windows UCRT is x86-64, the same ISA as a Linux server, and position-independent. Map the genuine ucrtbase.dll into memory at runtime and call its exports directly. The bits are genuine because the code is genuine, no reverse-engineering required.

Calling into Windows code from a Linux binary hits the ABI boundary. UCRT is compiled for the Windows x64 convention: the callee owns 32 bytes of shadow space above the return address, and the callee-saved register set differs from System V. Declare the function pointers ms_abi or clang’s frame layout gets corrupted by the callee’s shadow-space writes, and the indirect call jumps into garbage.

// Windows x64 ABI, not System V. Without ms_abi the call crashes.
typedef double(__attribute__((ms_abi)) * D1)(double);          // tanh, sin, ...
typedef double(__attribute__((ms_abi)) * D2)(double, double);  // atan2

// The mapped DLL code is not a CFI-registered indirect-call target, so
// -fsanitize=cfi-icall (on in production) #UD-traps every call -> SIGILL at
// startup. Opt the wrappers that call through the pointers out of that check.
[[clang::no_sanitize("cfi-icall")]]
double ucrt_tanh(double x) {
  return ucrt.loaded ? ucrt.tanh(x) : std::tanh(x);
}

One more detail decides correctness. Every UCRT math function starts with mov eax, [rip+disp32], reading a CPU-dispatch flag that selects the scalar or the FMA/AVX2 code path. A fresh mapping leaves it at zero, so you get the slow path, whose bits differ from what a modern Windows box produces. Extract the flag’s address from the tanh prologue and force it to the FMA path before the first call, and the lifted library matches a real Windows machine bit-for-bit.

Patch the chokepoint, gate it. Hook the single function that owns the value, where the engine calls libm. Gate on the claimed OS: Linux keeps glibc, Mac gets the reproduction.

Watch the clock. A perfect reproduction that runs slow is still a tell. Our first build lowered every fma() to a software call, because the default x86 baseline predates hardware FMA. That ran 2.5 to 6 times slower than native. A loop timing Math.tanh against Math.sin would show a ratio no real browser has. Turning on hardware FMA cut each fused op to one instruction: about 6 times faster, faster than glc, and bit-identical.

Validation

None of this ships without proof. Our harness runs 871,000 inputs per release across every branch and domain: dense grids, interval boundaries, subnormals, signed zeros, infinities, NaNs. Two ground truths back it:

  1. A genuine-device oracle: a real Mac computing both scalar and Accelerate results for every input, so we know exactly where the two disagree.
  2. A genuine-browser anchor: real Chrome on a real Mac over the debugging protocol, computing Math.tanh and every CSS trig function at full precision. This is the surface a fingerprinter reads.

We ship at bit-for-bit parity with genuine Mac Chrome on Math.tanh and on CSS sin, cos, tan, asin, acos, atan, and atan2, with the reproduction verified identical to the machine code in the shipped binary. Domain edges get checked too: asin(2) on a real Mac resolves to 0 (out of domain is NaN, and CSS clamps NaN to zero), not the 90 degrees a naive reproduction returns.

Why it matters

Math is deterministic, cheap to probe, hard to fake, and almost never on a spoofing stack’s radar. That makes it a strong signal for a defender and a liability for a scraper. Getting it right takes reverse-engineering vendor libm internals, mapping how three engines route math per call site, matching algorithms to the last bit, holding determinism across architectures, and proving it against real hardware.

Scrapfly’s browser carries all of it. Send a request through our API and ask to present as macOS, and the identity holds down to the rounding of a cosine.

Scrapium is Scrapfly’s scraping browser, built to stay indistinguishable from real traffic. Our engineering blog goes deep on the rest of the stack.


  1. glibc: the GNU C Library, the standard C library and libm on most Linux systems. Reference: gnu.org/software/libc↩︎
  2. ULP (unit in the last place): the gap between two consecutive representable floating-point numbers at a given magnitude. “1 ULP off” is the smallest difference a double can express. Reference: Wikipedia↩︎
  3. fdlibm: Sun Microsystems’ “Freely Distributable libm”, the portable reference implementation of the C math functions. V8 carried a port of it and computed Math.tanh with it until Chrome 148, returning identical bits on every OS. Reference: netlib fdlibm↩︎
  4. IEEE 754: the floating-point standard. It fixes how a double is stored but does not require transcendental functions to be correctly rounded, which is the room every vendor’s libm fills differently. Reference: Wikipedia↩︎
  5. libm: the C standard library’s math module (sin, cos, exp, tanh, and so on). Each OS ships its own build, which is why the same call returns different bits: glibc on Linux, Apple libsystem_m on macOS, UCRT on Windows. Reference: C mathematical functions↩︎
  6. minimax polynomial: the approximating polynomial that minimizes worst-case error. Each libm picks its own coefficients, and that choice is where the differing bits originate. Reference: Wikipedia↩︎
  7. UCRT (Universal C Runtime): Microsoft’s C runtime (ucrtbase.dll) and its math functions. Being x86-64 and position-independent lets it be mapped into a Linux process and called directly. Reference: Microsoft Learn↩︎
  8. V8 / Blink: Chrome’s JavaScript engine (routes Math.* through llvm-libc) and Chromium’s rendering engine (owns CSS calc() trig, which calls the host libm directly). References: v8.dev, chromium.org/blink↩︎
  9. llvm-libc: LLVM’s C library. V8 statically links its math routines, so Math.sin, cos, exp, and pow are identical on every OS, which is why only Math.tanh leaks. Reference: libc.llvm.org↩︎
  10. Accelerate framework: Apple’s vector and DSP library (vvsin, vvtanh, vDSP). It backs only the Web Audio FFT, not the scalar Math.* calls, which is why the scalar and vector results disagree. Reference: Apple Developer↩︎
  11. FMA (fused multiply-add): a single instruction computing a*b+c with one rounding instead of two. Compiling -ffp-contract=off stops the compiler adding or dropping fusions, which is what makes the reproduction bit-stable across CPUs. Reference: Wikipedia↩︎
The Daily Front Page 12 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — The Editor’s Terminal
article

Ghostel.el: Terminal emulator powered by libghostty

by signa11·▲ 295 points·60 comments·dakra.github.io ↗
Elisp manages keymaps, buffers, commands, and remote process integration.

Ghostel is an Emacs terminal emulator powered by libghostty-vt - the same VT engine that drives the Ghostty terminal. A native dynamic module written in Zig handles terminal state, rendering, and local PTY I/O; Elisp manages keymaps, buffers, commands, and remote process integration.

Ghostel is inspired by emacs-libvterm and follows the same two-layer design, but uses Ghostty's modern VT engine instead of libvterm. This brings the Kitty keyboard and graphics protocols, rich underline styles, OSC 8 hyperlinks, OSC 4/10/11 color queries, and synchronized output (DEC 2026) - none of which libvterm supports. See Ghostel vs vterm and eat for a detailed comparison.

The native module is downloaded automatically on first use, so no toolchain is required for the common case. Open a terminal with M-x ghostel.

1. Quick Start

(use-package ghostel
  :bind (("C-x m" . ghostel)
         :map ghostel-semi-char-mode-map
         ("C-s"  . consult-line)
         ("M-<backspace>" . ghostel-backward-kill-word)
         ;; ;; I'm used to go up/down the shell history with M-n/p from eshell
         ;; ;; Simulate this behavior in ghostel by sending C-p and C-n
         ("M-p" . (lambda () (interactive) (ghostel-send-key "p" "ctrl")))
         ("M-n" . (lambda () (interactive) (ghostel-send-key "n" "ctrl")))
         :map project-prefix-map
         ("m" . ghostel-project)
         ("M" . ghostel-project-list-buffers))
  :config
  (defun ghostel-send-C-k-and-kill ()
    "Send `C-k' to ghostel.
Like normal Emacs `C-k'.  Kill to end of line and put content in kill-ring."
    (interactive)
    (kill-ring-save (point) (line-end-position))
    (ghostel-send-key "k" "ctrl"))

  (add-to-list 'project-switch-commands '(ghostel-project "Ghostel") t)
  (add-to-list 'project-switch-commands '(ghostel-project-list-buffers "Ghostel buffers") t)
  (add-to-list 'ghostel-eval-cmds '("magit-status-setup-buffer" magit-status-setup-buffer)))

(use-package ghostel-eshell
  :hook (eshell-load . ghostel-eshell-visual-command-mode))

(use-package ghostel-compile
  :hook (after-init . ghostel-compile-global-mode))

(use-package ghostel-comint
  :hook (after-init . ghostel-comint-global-mode))

If you are an evil user you can install the evil-ghostel extension:

(use-package evil-ghostel
  :after (ghostel evil)
  :hook (ghostel-mode . evil-ghostel-mode))

1.1. Shell integration at a glance

Directory tracking and prompt navigation are automatically on by default for local bash, zsh, or fish sessions. See shell integration for TRAMP support and more.

To call Emacs functions from your shell you have to add them to the ghostel-eval-cmds whitelist and then add something like this to your bashrc:

if [[ "$INSIDE_EMACS" = 'ghostel' ]]; then
    # Open a file in Emacs from the terminal
    e()   { ghostel_cmd find-file-other-window "$@"; }

    # Open dired in another window
    dow() { ghostel_cmd dired-other-window "$@"; }

    # Open magit for the current directory
    gst() { ghostel_cmd magit-status-setup-buffer "$(pwd)"; }
fi

1.2. Input modes at a glance

Ghostel offers five eat.el-style input modes.

The default is semi-char mode, which forwards almost all keys to the terminal besides a few exceptions (e.g. M-x, C-c).

In char mode, all keys go to the terminal. Press M-RET to exit.

In line mode Ghostel behaves like M-x shell: the buffer is a normal Emacs buffer and no key is sent to the terminal. Only after you finish typing a line and press RET is the whole line sent to the terminal at once.

emacs mode and copy mode make the buffer temporarily a normal Emacs buffer that you can use to navigate, look around, and copy text. The difference between the two is that copy mode freezes the terminal, so if you have continuous output nothing "scrolls away" while you try to select something. emacs mode is live, so new output keeps coming in while you scroll and select.

Those read-only modes have ghostel-readonly-fast-exit enabled by default (it defaults to t), which automatically exits them on most keys that you expect to be sent to the terminal. This makes for seamless transitions: say you have some output running and see something you want to copy - you press C-c C-t to enter copy mode, navigate like in a normal Emacs buffer, and select your text. When you copy something or type any character you are automatically back in your normal ghostel terminal session. Some actions also activate copy mode automatically, like selecting with the mouse, navigating to hyperlinks (C-c C-p), or activating the mark.

2. Requirements

  • Emacs 28.1+ with dynamic module support
  • macOS, Linux, FreeBSD, or native Windows

The native module is automatically downloaded on first use. Pre-built binaries are available for:

  • aarch64-macos (Apple Silicon)
  • x86_64-macos (Intel Mac)
  • x86_64-linux
  • aarch64-linux
  • x86_64-freebsd
  • x86_64-windows
  • aarch64-windows

If you prefer to build from source or need a different platform, you will also need Zig 0.15.2 - see Building from source.

3. Installation

3.1. MELPA

(use-package ghostel
  :ensure t)

3.2. use-package with :vc (Emacs 30+)

(use-package ghostel
  :vc (:url "https://github.com/dakra/ghostel"
       :lisp-dir "lisp"
       :rev :newest))

:lisp-dir "lisp" is only required on Emacs < 31.1.

3.3. use-package with :load-path

(use-package ghostel
  :load-path "/path/to/ghostel/lisp")

3.4. Manual

(add-to-list 'load-path "/path/to/ghostel/lisp")
(require 'ghostel)

Then M-x ghostel to open a terminal.

3.5. Native module

When the native module is missing, Ghostel offers to download a pre-built binary or compile from source. This is controlled by ghostel-module-auto-install (default ask). You can also trigger these manually:

  • M-x ghostel-download-module - download the minimum supported pre-built binary.
  • C-u M-x ghostel-download-module - choose a specific release tag (leave blank for the latest).
  • M-x ghostel-module-compile - build from source via zig build.

By default the module is read from and written to the package directory. If your package manager rebuilds or reinstalls the tree while Emacs has the module loaded, point ghostel-module-directory at a stable location outside the package tree (for example ~/.config/emacs/ghostel/).

3.6. Platform notes

3.6.1. Windows

Ghostel supports native Windows Emacs builds with dynamic module support. The pre-built release modules target the common x86_64 and aarch64 native Emacs architectures; the module DLL must match the architecture of Emacs, not merely the architecture of Windows itself. Unusual custom Emacs builds, including static-CRT builds, are best-effort.

Local terminals use Windows ConPTY. Ghostel first looks for support files from Microsoft's redistributable console runtime next to the native module (conpty.dll and the matching OpenConsole.exe helpers). When present, those files provide newer ConPTY behavior than older inbox Windows versions and can improve both interactivity and terminal-protocol correctness. If they are not installed, Ghostel falls back to the inbox Windows ConPTY API.

Downloading a pre-built Ghostel module also downloads the matching support files from the same Ghostel release. When compiling the module locally, Ghostel tries to install the latest available support files; failure to install them is non-fatal because the inbox ConPTY fallback remains available.

TRAMP remote terminals from Windows are limited to POSIX remotes. They use TRAMP/ssh rather than local ConPTY, so the PTY lives on the remote host. Dynamic window resizing for Windows-to-TRAMP terminals is not currently supported.

4. Building from source

Building is only needed if you do not want the pre-built binaries. Ghostel vendors a generated vendor/emacs-module.h, so normal builds do not require local Emacs headers.

git clone https://github.com/dakra/ghostel.git
cd ghostel

# Build everything into the checkout (fetches ghostty automatically)
zig build --prefix . -Doptimize=ReleaseFast

To override the vendored Emacs header, set EMACS_INCLUDE_DIR to a directory containing emacs-module.h, or set EMACS_BIN_DIR to an Emacs bin/ directory

  • Ghostel then looks for ../include and ../share/emacs/include.

To build against a local ghostty checkout, temporarily point the dependency at your local path:

zig fetch --save=ghostty /path/to/ghostty
zig build --prefix . -Doptimize=ReleaseFast

The module and ghostel-module.version sidecar are installed under Zig's install prefix. Use --prefix . when building into a checkout that Ghostel should load directly, or pass another prefix matching ghostel-module-directory. On Windows, local builds normally use Zig's MinGW target (for example -Dtarget=x86_64-windows-gnu) so they match common native Emacs builds and do not require a Windows SDK.

When installed from MELPA, M-x ghostel-module-compile builds the native module from source using zig build --prefix <ghostel-module-directory>; the Zig package manager fetches the ghostty dependency automatically.

4.1. Bundled terminfo

The compiled xterm-ghostty terminfo entry ships pre-built in etc/terminfo/ and is identical to what tic would produce locally - no build step needed, and the file format is portable across BSD and ncurses systems. Maintainers regenerate it via make regen-terminfo after bumping libghostty.

The Daily Front Page 13 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Wire Formats
article

Protobuf-py: Protobuf for Python, without compromises

by ming13·▲ 138 points·58 comments·buf.build ↗
Written completely from scratch.

Today we’re announcing protobuf-py, a Protocol Buffers library for Python written completely from scratch. It passes every binary and JSON case in the Protobuf conformance suite across proto2, proto3, and editions, and it supports extensions, custom options, unknown fields, dynamic messages, and well-known types. It generates readable, typed Python, has no runtime dependencies, and runs on pure Python 3.10+. With its Rust accelerator installed, it’s just as fast for production workloads as upb, the C engine Google’s Python package runs on.

For Python developers, the choice has historically been between a complete Protobuf implementation and a library that feels like Python. Google’s package is complete, but it carries an API shaped by C++ and Java. betterproto is pleasant, but it gives up too much of the spec. protobuf-py gives Python developers both.

Why build another Protobuf runtime?

Python is too important for Protobuf to feel like an afterthought. It sits in data pipelines, ML systems, AI agents, infrastructure scripts, RPC services, and developer tooling. Still, the experience of using protobuf in Python does not match what developers expect from such a popular language. Google’s package is complete and battle-tested, but the API and generated code still feel like a binding around someone else’s runtime. betterproto proved that Python developers wanted something nicer, but it never implemented the whole spec. grpcio brought the same problem into RPC. It’s powerful and widely used, but painful to build around.

We originally set out to fix the RPC layer with connect-py, a ConnectRPC implementation that speaks both Connect and gRPC. But the transport layer was not enough. A good RPC stack still depends on the messages underneath it, and Python did not have the Protobuf runtime we wanted as the foundation. We wanted something complete enough for real schemas, readable enough for everyday Python, and fast enough that nobody has to apologize for choosing it.

protobuf-py is the result of asking whether those constraints could all be true at once. A complete implementation built for Python, it provides the spec coverage, generated code, and performance profile we wanted connect-py to stand on, without wrapping Google’s runtime or trimming the spec.

Why Google’s package feels the way it does

Install protobuf from PyPI and the engine you normally get is upb, written in C. Your message lives in a C arena, while the Python object is a handle into that arena. Reading a field crosses into C, finds the value, and materializes a Python object on the way back.

It is a good way to share an engine among multiple languages, but it leaves fingerprints everywhere:

  • Generated _pb2.py files are hard to read because there is not much Python to read. Classes are assembled at import time to configure the C engine, so go-to-definition lands on a wall of serialized descriptor bytes.
  • SerializeToString, HasField, WhichOneof, and CopyFrom make up a Python API designed for C++ ergonomics. HasField raises if you call it on a proto3 scalar. WhichOneof returns a string you hand back to getattr to retrieve a value it already located.
  • Generated imports are absolute and break the moment you nest them in a package. There is a separate tool called fix-protobuf-imports that exists on PyPI only to rewrite Google’s output.
  • Types register in one process-wide pool, so importing two builds of the same .proto raises at runtime.

None of these are random defects in the binding. They are what happens when a Python API is designed for consistency in a primarily C++/Java codebase, rather than as an idiomatic Python package. The clumsy API and the speed shipped together, and for years you took both or neither.

What we built instead

protobuf-py keeps your message in Python. It is a plain object with __slots__, and its fields are ordinary Python values: ints, strings, lists, submessages. A Rust accelerator speeds up the operations that need it, mostly parsing and serializing, and writes the results straight into the object. Once parsing finishes, reading a field is just accessing a Python attribute.

Because the data is Python, the generated code is real code. protoc-gen-py emits a class you can read:

class User(Message[_UserFields]):
    __slots__ = ("first_name", "last_name", "active", "manager", "locations", "projects", "contact")
 
    if TYPE_CHECKING:
        def __init__(
            self,
            *,
            first_name: str = "",
            last_name: str = "",
            active: bool = False,
            manager: User | None = None,
            locations: list[str] | None = None,
            projects: dict[str, str] | None = None,
            contact: Oneof[Literal["email"], str] | Oneof[Literal["phone"], str] | None = None,
        ) -> None: ...
 
        first_name: str
        last_name: str
        active: bool
        manager: User | None
        locations: list[str]
        projects: dict[str, str]
        contact: Oneof[Literal["email"], str] | Oneof[Literal["phone"], str] | None

Working with it looks like working with anything else in the language:

import copy
from gen.user_pb import User
from protobuf import Oneof
 
user = User(first_name="Alice", active=True, locations=["NYC", "LDN"])
user.last_name = "Smith"
 
wire = user.to_binary()
parsed = User.from_binary(wire)
 
match parsed.contact:
    case Oneof(field="email", value=email):
        send(email)
    case Oneof(field="phone", value=phone):
        call(phone)
 
inactive = copy.replace(parsed, active=False)   # Python 3.13+

Oneofs become values you can pattern-match, and the type checker narrows each branch. Enums are real IntEnum members. pyright, mypy, and ty read the generated output without a stubs package. Generated files use relative imports and go wherever you keep them. Types resolve through an explicit Registry rather than a global pool. Every one of these follows from keeping message data in Python.

It is complete, not a friendly subset

Other Protobuf libraries are nicer to use than Google’s, but usually drop half the spec to get there. betterproto is the most pleasant option today and it is proto3-only, with no proto2, editions, extensions, or custom options.

protobuf-py covers the whole spec. It handles proto2, proto3, editions, extensions, custom options, groups, unknown-field preservation across round trips, packed and expanded repeated fields, and the full ProtoJSON encoding of the well-known types. It passes the conformance suite Google uses to qualify its own runtimes, with no failures across binary and JSON. An empty failure list is checked into the repository, and CI fails if it stops being empty.

Fast where it counts

A benchmark that parses a message and throws it away makes upb look untouchable, because it defers work until you read. Production code parses a message once, branches on fields, pulls out a few values, copies the message, and serializes a modified version back.

upb pays the Python translation cost on every one of those reads. protobuf-py pays it zero times after the first read, because parsing already produced a normal Python object. The cost runs the other way at the boundary. Keeping data in Python means protobuf-py does more work up front. That pays off when code does enough with a message to earn the time back.

Here we put both packages against a real-world example of building the response for a user’s home page on a social media site, alongside the raw marshaling steps on their own. Numbers are throughput (operations per second) relative to upb, so higher is faster.

Workloadupbprotobuf-pyBuild home page response (end-to-end)1.0x1.06x└ parse (in isolation)1.0x0.22x└ serialize (in isolation)1.0x0.60x

In isolation, upb wins marshaling, exactly as expected. End-to-end, on the code a service would actually run in production, protobuf-py comes out ahead. Every field read that upb has to translate back into Python is one protobuf-py already has, and across a full request that adds up to a runtime that can beat the C one.

The payload here is text-heavy (full Reddit post bodies, multi-sentence bios and notifications), which is the realistic shape of social, document, and LLM/agent traffic. This is the case where keeping strings as Python objects pays off most. You can run the harness yourself at test_bench.py.

The Rust accelerator is optional and automatic. You do not need a Rust toolchain to use protobuf-py, and neither do contributors. Prebuilt wheels load it when it is there, and a pure-Python path behaves identically when it is not. It runs on the free-threaded 3.14 build, and the package has no runtime dependencies.

We have done this before

Buf has spent years fixing the parts of Protobuf that make teams build scripts around their tools: the Buf CLI, the Buf Schema Registry, ConnectRPC, Protovalidate, and protobuf-es for TypeScript. Buf engineers also worked on Protobuf editions, so protobuf-py is written by people who helped write the spec. It belongs to the same tradition.

Try it

# In your existing UV project
uv add protobuf-py
uv add --dev protoc-gen-py buf-bin

Point a buf.gen.yaml at your .proto files:

version: v2
inputs:
  - directory: proto
plugins:
  - local: protoc-gen-py
    out: gen

Then generate:

uv run buf generate

You get a typed _pb.py you can open and read. Docs are live, the source and benchmark harness are on GitHub, and the issues are open. Let us know how it goes for you!

The Daily Front Page 14 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Reading Again
article

How to read more books

by silcoon·▲ 542 points·287 comments·scotto.me ↗
I promise, it will be worth it.

I’ve read roughly a book a week for a few years, and I can tell you it’s doable. I didn’t always read this much. When I started, I read fewer than ten volumes per year, but making it a goal made me switch gears and achieve what I once thought was impossible. I want to explain to you here what I did to become a prolific reader, and what I learned in the process, so that, with a bit of effort, you can do it too. I promise, it will be worth it.

[Umberto Eco is his library]

Umberto Eco had a library of more than 30,000 books.

First of all, you don’t have to make time to read. What you need to do is read every single time you are not doing something else. In today’s world, most people pick up their phones as soon as they get a moment of inactivity. Serious readers pick up their books instead. So an effective way is to replace the time you spend in front of a screen, like PCs, smartphones, and TVs, with reading a book.

This is probably the most difficult part. I had to remove all social media and streaming apps from my iPhone. I removed Instagram, YouTube, Facebook, etc. When I started, I found myself picking up the phone and immediately noticing that something was missing, since the only things left to do were check the weather, read boring emails, or see my bank account. After a few days my brain started remapping itself, and I felt less pressure to immediately reach for my phone as soon as I had nothing to do. I also wear a cheap analog watch so I can check the time on my wrist and I don’t have to reach for my phone.

Once you block your smartphone, you might find yourself a bit uncomfortable with having nothing to occupy your mind for ten minutes. This is the perfect time to boost your reading habit. You need to make sure you always have a book with you everywhere you go. I usually read a few pages as soon as I wake up, and the same before falling asleep. I read a book when I cook lunch or dinner, and I read a book when eating breakfast. I love using public transport, especially trains, because I get time to read when someone else is driving for me.1 I always have a book with me when I go out with my partner, even if I don’t usually end up reading it. If she has to run an errand and I have to wait, I don’t waste my free time with nothing to read. I have become good at walking my dog while reading — I even got complimented for that by a stranger — and I make sure I never go to the bathroom without a book.”

“I love the smell of book ink in the morning.” — Umberto Eco

Having a book wherever I go can become problematic, depending on the size of it. The best solution I have found is to use an ebook reader. They are thin devices that can fit in a pocket and are able to carry hundreds of books in memory. I think that for a reader, it might be one of the best inventions ever, considering how expensive books were throughout history and also how difficult it is to carry them. An ebook reader can solve those problems; moreover, you can have a backlight for reading in the dark — it works differently from an LED screen and doesn’t strain the eyes — and you can also highlight text and get definitions for words. However, it’s not really a replacement for a book. Reading only using an ebook reader after a while feels like reading the same book, even if the story changes. I like to alternate digital books and physical ones, and I always prefer paperbacks since they are easier to carry around and cheaper to buy.

I also tend to read multiple books at the same time. Sometimes it happens that I get so into a book that I put the rest on pause because the story is grabbing all my attention, but in general I like to have a few books that I read in parallel. Having only a single option can become boring. I generally mix fiction and non-fiction to have a broader choice.

What to read is an hard question. I’ve read somewhere a phrase that puts it quite well: “read what you like until you like to read.” My suggestion is to read broadly, changing genres and subjects, because there are nicely written books in every genre, and because it teaches you different perspectives. Before or after, you will understand which genres you relate to more.

“It is foolish to think that you have to read all the books you buy, as it is foolish to criticize those who buy more books than they will ever be able to read. It would be like saying that you should use all the cutlery or glasses or screwdrivers or drill bits you bought before buying new ones.” — Umberto Eco

Another secret is to not be scared of quitting a book. I definitely start way more than I finish. But I don’t consider an uncompleted book a failure or a bad book. I think that sometimes books have a certain time to be fully appreciated. So if I don’t finish a book today, I might try reading it again in the future. I still remember having abandoned Siddhartha by Herman Hesse at least three times at the first few pages, before reading it entirely and considering it one of the most formative books of my life. Somehow I wasn’t ready for it, and I had to wait to become capable of fully understanding it. Also, I believe there are way more bad books out there than good books. If you feel that you are not liking one, it feels boring and makes you think you are wasting your time, just close it and move on. I have closed books by authors that I loved because they weren’t good — in that moment.

If you are a serious reader, you need a library, and to make your own library you need physical books. So try to get books that interest you and put them on the shelves for later. When I buy new books, it is mostly because I need a specific one I cannot find otherwise, or because I want to support local independent bookshops. However, I get more used books than new ones. I usually find them in the book section of used stores, at markets, book fairs, and sometimes in the book boxes that I find around the city.2

An effective method to push yourself to read more is to set some goals. You can pick a reasonable number of books for the month or the year and try to reach it. Progress tracking is one of the known tricks used to form new habits. Goodreads, for example, has what’s called the Reading Challenge to help you keep track of what you read in a year, and I found it motivating to put in the effort to maintain the number I wanted to read. However, counting the volumes is not a healthy way of reading. It’s much better to read great books and take the right amount of time to understand and reflect on them rather than rushing to finish to increase the final count. You need to enjoy the process and get a good outcome from your reading.

Writing a review is also a great method to make a book stick in your memory. Taking highlights and notes while you’re reading is a good thing, but revisiting them alongside your thoughts in a written document is something different. Reviewing books is a great way to become better at understanding what the messages contained in the books are and what the key elements of the story are. In addition, if you like writing, it’s a good pretext to practice.

One of the duties of a serious reader is to find what to read next. I always have a long list of books to read, but I don’t stick to it all the time. I like to be immersed in something new and discover new authors or genres. What I usually use are two things: Goodreads and YouTube. The first one is great for reading comments and understanding if a book is what we think it is and what people we follow (make sure to follow good readers) think about a book. So Goodreads helps me form an idea of a book before I get to reading it. YouTube, on the other hand, is great for getting recommendations and listening to spoiler-free reviews to get some ideas. There are many great channels out there, and I watch quite a few of them. One of my favourites in English is Better Than Food, which has reviewed great books for more than a decade.

What inspired me to try to read more was this video, where Max Joseph explains that becoming a serial reader is just a matter of making a daily habit of reading a few pages, with surprisingly good results. Also, Ryan Holiday — a famous author — has recently published a good guide on how to read more books with his own tips and tricks.

Last recommendation: avoid hacks. Avoid speed reading. And don’t try to force yourself to increase your reading speed. It will come naturally the more you read. Avoid summaries and summary services. It might be okay to use them after you read a book to make sure you didn’t miss some parts, but reading a summary does not equal reading a book. Avoid even audiobooks. Big corporations want to grab your attention by trying to market audiobooks as books for busy people, but don’t fall for the trap. A book is just boring black text on a white page because that’s how it’s meant to be consumed, and it requires your entire attention. Listening to audio while cooking or cleaning or whatever you do is not the same thing; you are not 100% concentrated on the content. Also, reading is faster than listening, so use your time wisely.

Footnotes

  1. I consider driving a huge waste of time. Sometimes it can be fun and adventurous, but I think life is too precious to drive to work every day. ↩︎
  2. In Australia, some people put a wooden box with a door filled with books in their front yards, so that everyone passing by can pick one up or put their own into it for the next readers. ↩︎
The Daily Front Page 15 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — From Screens Back to Books
article

I Learned to Read Again

by georgex7·▲ 182 points·76 comments·substack.magazinenongrata.com ↗
Books were essentially the same as knowledge.

On the journey from books to screens and back

400 Blows (1959), directed by François Truffaut.


I hit my reading peak when I was eleven or twelve. At the time, it was a joke. I had a bedside table and it was stacked with books, all at different stages of being read. Books migrated to the floor next to the bed, and different stacks migrated to different rooms of the apartment. At the time I really was ravenous. It was clear to me that books were essentially the same as knowledge and were the window into the world—into all kinds of different worlds—but, crucially, that books also contained the key into adulthood. Since then, my reading has basically been in decline and under siege from a wide variety of different adversaries. Let’s list those before getting into my personal reading rehabilitation project.

Middle school, and adolescence in general, were the single greatest blow to my reading. It quickly became clear that, from a social perspective, all this reading was a catastrophic blunder, and I tried to switch gears as quickly as I could—I would come home from school and turn on ESPN or VH1 and try to download pop culture so that I could repeat it back in school the next day.

School itself was an obstacle to my reading. I showed up at my new middle school trying to hide The Republic under The Red Pony, and I’ve had a fantasy from that day to this that the school system would start recognizing and supporting kids like me who clearly were very self-motivated and had an obvious aptitude in one subject as opposed to others. In this fantasy I would have been instantly enrolled in some higher-level English classes and excused from math or science, which, it was completely clear, wasn’t going to be a significant part of my life. But that just wasn’t the case. There were phone calls home, and I learned fairly quickly to revert to the mean.

Then there was social life. I remember carefully packing a suitcase of my books for college when my father told me, “Trust me, there won’t be a moment for reading recreationally.” He didn’t just mean that there would be a deluge of school reading, he meant that there would be so much going on around campus that reading for fun would be a kind of admission of social failure. I took that seriously, felt a sort of guilty conscience whenever I opened up one of my books and tried to think what I should be doing instead. It was a real surprise to me when I realized, somewhere towards sophomore or junior year, that it wasn’t exactly true and lots of cool, perfectly socially-adjusted people were also reading for fun.

Illustration by Sam Keshishian.

Then there was work life. I sort of had an understanding around this time that reading, even serious reading, was childish, and that adults spent all their time thinking about money, and I remember a moment soon after I graduated when I delivered a kind of private eulogy for my reading life. In the end, it turned out to be not like that. The first job I had out of college involved twelve-hour workdays, but that still left a lot of hours unaccounted for, and I remember the curious guilty pleasure of visiting the used bookstore in town and loading up on a whole bunch of Penguin Classics that the proprietor was visibly surprised to be selling to anyone.

I remember feeling like I’d overcome some kind of hurdle in my reading life at that time—I’d expected work life to grind down my reading and instead my reading (which is to say, my inner life) had somehow outfoxed the work schedule. But I was underestimating my adversaries. That phase was another peak of my reading, and then there was a ten-year period from, roughly, my late twenties to my late thirties when I barely read anything at all—or, at least, my reading consumption slowed to a level that would have deeply embarrassed my eleven-year-old self.

What happened? I was no longer in terror of middle schoolers expecting me to know who Gwen Stefani was or to quote TV commercials back at them. I wasn’t, for the most part, dealing with an onerous worklife that swallowed up all my free time. But there were a few new adversaries.

One was love. Being with a partner meant, essentially, turning over my inner life to the partner. Reading seemed like a way of distancing rather than connecting, and by far the better way to consume content was to stream TV shows together.

Then there were money worries. Adulthood wasn’t quite the grey-flannel-suit enterprise that I had pictured where I was younger, where you entered into the workforce and were essentially lobotomized by it. In millennial life everything was a bit looser and freer, but there was also a drumbeat of constant anxiety. Reading—I mean, reading a long, serious book—seemed, in that context, like checking out, not so different from sitting down on the street and rattling an empty coffee cup. Scrolling was alright—that meant that you were still plugged in, that you were part of the flow of life, where social connections and money opportunities could be made. But there was absolutely zero opportunity of advancing one’s interests by reading a book—there was no conceivable social chit-chat that would turn to people bantering about books, and the knowledge in books was entirely abstract and remote, as opposed to the potentially utilitarian knowledge you might get from a newspaper or a social media post.

From a more macro point of view, what was happening at this time was the final breakdown of a Bourdieauian concept of social status, in which taste was the critical metric of status and being bourgeois or refined meant, among other things, being a reader (for a glimpse of how a Bourdieuian social system worked in practice, it’s worth watching certain movies from the ’70s or ’80s, any Woody Allen movie, for instance, which are basically a cornucopia of high-brow referents). As David Brooks nicely documents in Bobos in Paradise, the bourgeoisie made a kind of collective pact around the 1990s or 2000s to just drop it—to not burden themselves with showing off how much they’d read and to instead just flaunt their wealth while entertaining themselves with talking about TV shows, which were starting to become pretty good.

Then, surprisingly enough, writing was a hindrance to my reading. This is kind of a specialized concern, but I came to feel in my twenties that my writing was a bit drafty and undergraduate, in large part because I was overly influenced by the enormous swath of reading material that I had inhaled through my adolescence. I was very impressed by a line from a medieval Arabic poet where he advises a student to memorize a thousand passages of poetry and then to forget them all again before he could begin writing. I decided that I was in the phase where I needed to forget—I had taken in too many inputs, and I had a tendency to be intimidated or just overly influenced by them, and now was the time, I felt, to clear out my own head and find my voice. This, by the way, I think is pretty good advice to writers, but, at the time, it was accompanied by a certain bitterness on my part towards reading—if reading couldn’t even make me a better writer, I thought, then what really was all that reading for?

But, of course, the most significant obstacle to reading was social media. There was a long period of time—almost forgotten now—where society split between those who were always online and those who weren’t. I prided myself in being among the people who weren’t—having a Facebook profile but not posting, not obsessively checking Twitter, etc.—but we were all fooling ourselves. The turning point for me came when I watched the documentary The Social Dilemma in 2020 and realized the extent to which I was like the kind of alcoholic who claims that they only drink moderately, but whose life is in fact dominated by booze. “Every time you see the phone on the counter and you just look at it and you know if you reach over it just might have something for you so you play that slot machine to see what you get, right?” the technology ethicist Tristan Harris says in the documentary. The argument was that the phones used deep psychological techniques—it’s called “positive intermittent reinforcement,” if you’re interested—to get you completely hooked. I was old enough to remember how the arrival of the mail in the morning had a similar effect on people, or the way that people might hover by a landline waiting for a phone call. But now it was like people were checking their mailbox a thousand times a day—just the idea of good news coming for you, and only you, was so powerful that, essentially, nothing else could compete with it, certainly not a closed form like a book, which as often as not was written by a dead person and, by definition, could not have a job offer or a romantic possibility or a party invitation or a compliment for you embedded within it.

Illustration by Sam Keshishian.

For the better part of a decade, we all fell victim to this. I remember the most interesting person I knew—a guy who’d become a shaman—saying that his whole spiritual journey was behind him. “Now I am a Facebook customer,” he reported. And my reading habit was one victim of many.

The Social Dilemma probably was the decisive event for me and made me realize the extent to which my behavior—the regular checking of my cell phone—was addiction, no matter that I was probably a bit better than most people I knew. The period since then—call it the last five years—has been a slow and deeply embarrassing journey to learn to read again. In other words, painstakingly and through all kinds of cheap tricks, to learn to do something that used to be about as natural for me as breathing. I sometimes think about it as being like a stroke victim who has to train themselves to walk again or to wake up some dormant hemisphere of their brain—with the difference that nothing bad happened to me, apart from the general current of 21st century life.

What bothered me most, I suppose, was the degradation of my own inner world. Solitude used to be something I looked forward to—the chance to curl up with a book. Now I was twitchy and irritable—constantly checking my phone. I was, in a word, far less mature than I had been when I was eleven or twelve. I could remember when adults might ask each other, as a normal question, “what are you reading?” and that was like an invitation to travel together into a different dimension, into the content of each other’s inner lives and each other’s souls. Asking each other about what they had seen on Netflix or whether they had caught the Grammys didn’t have the same incantatory power. Without that, discourse seemed to be breaking down. Twitter was turning into a flaming mosh pit of dueling online mobs, Instagram and Facebook were aggressively performative, and all of them were heavily manipulated by algorithms and subject to corporate censorship. What reading really meant was getting away from all of that—from an opportunistic culture in which participants are “users” and to the promise that’s embedded in reading at its best, of soul speaking to soul, of individuals maybe continents or centuries away speaking their truth, to the best of their ability, and being received with respect and empathy by an attentive reader. To me, that seemed like a much better, more reciprocal and more honorable, relationship with others than anything that was on offer from my current tech-based social landscape.

So, in my uphill battle to once again be a reader, here are a few of the tricks I’ve tried.

The first and most important is that I’ve given myself over almost entirely to motivated reading. It’s almost impossible for me now, as it has been for years, to just pick up a book and to fall into the kind of daydreaming state that coffee mugs and coasters are always proclaiming is the essential joy of reading. If I’m reading it’s always for something and—like I’m the manic Duolingo bot constantly creating random milestones for its users—I’m always giving myself tasks to complete in my reading. I often read as research for writing projects, and that is no problem at all—I am able to read just as fluently and frenetically as when I was a kid or, probably more precisely, as when I was a student cramming for an exam. That’s a very specific, and more or less healthy, way of reading. I know that I have a limited time to research a project before it gets stale in my imagination—I want to be comprehensive in my research without getting bogged down in it—and I read in a totally voracious way. Here, the internet actually is my friend: between archive.org and Everand and weird piratic sites like Dokumen, I can find with rare exceptions any text I am looking for (and which would have been hopelessly tedious in the pre-internet era of moving between libraries or asking for books on inter-library loan).

Then, I make myself turn out a product nearly every time I read something. The origin of my Substack—on which I write prolifically—is largely that I was looking for a place to post book reviews, and the reason I wanted to post book reviews was that I wanted to give myself a reason to read the books in the first place. The difference between reading “for pleasure” and reading with a review envisioned at the end of a project is considerable. For one thing, I am a much more attentive and careful reader when I am planning to write a piece on the book—and know that I will be judged if I skip over a key plot point or misread the book (and may even face a critique from the sulky author themself). I read in an almost-industrial style when I am reading this way (which is, at this point, the vast majority of my reading). And here, once again, technology is my friend. In the pre-smartphone days, I had always been perplexed by whether and how much to write in the margins of books I was reading—which seemed disrespectful and also like the primrose path to being a pack rat. But with Apple Notes, I write down basically everything that’s interesting to me in the book as I’m reading and swipe-typing picks my speed up enough that I don’t really lose my flow in a book as I’m writing my note. The result is that I can basically write a review as I’m reading a book, and—when it comes time for the review—I often don’t even look at the book again. I can do it entirely off my notes.

But even that technique is only partially successful. I started my Substack planning for book reviews to be a cornerstone of it, but if I look back at my archive, whole months can pass without my writing a review of anything—which means that I’m basically not finishing any books at all. A few things are going on at once. One is that I get really depressed over the state of contemporary writing, especially contemporary fiction. Anybody who’s tried to write reviews, or binge contemporary fiction, will know what I’m talking about. But the more salient point is that I still am a digital addict. If I’m on my phone, or a computer, my impulse is to check mail for the thousandth time that day or to play games online or to scroll the newspaper or to hang out on Substack Notes. So I started a new round of embarrassing addiction-control reforms. After watching The Social Dilemma, I’d taken their advice and turned off Notifications on my phone, which, actually, was a life-changer. Now, like I was my own parent, I started different regimens for increasing my reading time. I would open up a book on my e-mail or Kindle, and then, once I did, put the phone in airplane mode and then force myself to read for fifty pages or a half-hour at a time, something like that. It wasn’t easy at all, and—at the time of writing this—it’s very much a work in progress. Somehow or other, that airplane mode button keeps getting toggled off. If I don’t force myself to take notes on a book—with the prospect of writing a review at the end of it—it’s easy for me to zone out for whole pages at a time, still reading but not processing what’s happening. It’s an astonishing come-down—my regression from being the best reader of anybody I knew to being the kind of C student whose parents were always tricking them into doing their English homework—but we are far enough into the great cultural collapse that I just have to accept what’s happening and adapt to it. Reading has become something that I have to force myself to do.

The question then becomes—and I do find myself asking it a lot—why I bother at all. Why do I read, if it’s not particularly good for my career or my social life, or even for my writing, and I often don’t even enjoy it, and have to find these inane tricks to compel myself to do it? What I’d like to say is that somewhere in me is the same compulsive curiosity that first animated me to become such a great reader back when I was a little kid, but I think the answer is a bit different and is more about feeling a kind of obligation. Civilization is facing an existential crisis. We have lost the habit of reading—if I had such a difficult time with digital addiction, I can only imagine what it was like for people who didn’t have the head start as a reader that I did—and that means that we lose both a capacity for deep concentration (which includes the capacity for jumping from our perspective to perceiving the world from the consciousness of others) as well as a critical continuity with the pre-digital past. These are really bad habits to lose. Anybody who’s encountered anybody below the age of, say, twenty-five will be struck at how their worldview seems to begin with the digital age, and what that means is that anybody who is older, or who has something like an ancestral memory of deep reading, has an obligation to serve as a caretaker generation, to keep the habit alive, with whatever tricks we can manage, in the dim hope that something or other will happen, that the entire culture won’t be swallowed up by social media algorithms and their A.I. successors, that somehow or other the taste and utility of reading, actual reading, will come back. Until then it’s airplane mode and timers and anything else we can think of that will force ourselves to read an actual book, and to resist the infinite allure of the infinite scroll.

Article cover spread from the print version.

The Daily Front Page 16 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — A Curriculum in Physics
article

So you want to learn physics (second edition, 2021)

by azhenley·▲ 312 points·64 comments·susanrigetti.com ↗
My only goal was to put the information out there.

So You Want to Learn Physics…

SECOND EDITION

Introduction to the Second Edition

Nearly six years ago, I sat down at my desk and typed up a detailed guide for anyone who wanted to learn physics on their own. At the time, I had no idea how many people would read it and use it — my only goal was to put the information out there in a clear and straightforward way so that anyone who wanted to learn physics would have the self-study curriculum they needed. Since then, over six hundred thousand people have turned to this guide to study physics.

According to the emails I’ve received from readers, many of you have gone on to get undergraduate degrees in physics after following the curriculum in this guide (some of you are even now in graduate programs!), but the majority of those who have bookmarked and followed this guide — even all the way to the end! — have done so out of pure curiosity and for the sheer joy of understanding the incredible universe we inhabit.

The success of this guide is, I believe, a testament to two things.

First, that one of the most impactful things you can do is to share what you know with others, even if it doesn’t seem like a lot. I wasn’t able to become a professional physicist, but I was able to use my knowledge of undergraduate and graduate-level physics to type up a comprehensive and accessible curriculum that has helped hundreds of thousands of people learn physics. That’s pretty remarkable. If you are wondering what you have to offer the world, I hope you will think of this guide and consider what you might know that you can share with others.

Second, that there are so many people in the world who want to understand physics but are unable to study it formally in a university setting for any number of reasons. These same people are very serious about learning physics, and not for any career purposes but simply because they want to understand the universe, and they are and have been dreadfully underserved and underestimated by the academic physics community (who do not take them seriously because they aren’t studying at colleges and universities) and by the authors of contemporary and popular physics books and the publishers of those same books (who mostly just sell them books that assume most readers can never and will never really understand physics). When I wrote the first edition of this guide, I was pretty sure there were a lot more people out there who really wanted to learn physics — real physics — than academic physicists and publishers believed, and those were the people I wanted to help. As it turns out, there were even more of you than I could ever have imagined!

Well, after almost six years and lots of reader feedback, I decided that it was finally time to make a (lightly) updated version of this guide. I went back through the emails and comments I’ve received over the years, and then made a list of the most popular requests. I skimmed through all the books in the curriculum and a few new ones as well. I updated textbook editions, added more undergraduate-level electives, added a section of graduate-level electives, and made a few other small changes — all in the hope that this new version will be even more useful than the first.

As I wrote in the first edition: “Remember that anyone can learn physics…Whether you turn it into a hobby or a career, the pure joy of understanding the universe around us is one of the most beautiful experiences you can ever have in life.”

Godspeed!

You can still find the first edition here, on my old website: https://www.susanjfowler.com/blog/2016/8/13/so-you-want-to-learn-physics.

Introduction to the First Edition

Over the past few years, ever since writing a blog post called “If Susan Can Learn Physics, So Can You,” I've been contacted by people from all backgrounds who are inspired and want to learn physics, but don't know where to start, what to learn, what to read, and how to structure their studies. I've spoken with single mothers who want to go back to school and study physics, tenured philosophy professors who want to learn physics so that they can make significant and informed contributions to philosophy of physics, high school students who want to know what they should read to prepare for an undergraduate education in physics, and people in dozens of various careers who want to really, really learn and understand physics simply for the joy of it.

This post is a condensed version of what I've sent to people who have contacted me over the years, outlining what everyone needs to learn in order to really understand physics.

The general physics education given in U.S. universities is divided into what is learned at the undergraduate level and what is covered in graduate courses, and I've divided my list in a similar way. Because each subject is built upon the previous subjects and the mathematics becomes more complex and difficult, it's important to learn each topic in the order presented below.

If you work through the all of the textbooks in the Undergraduate Physics list of this post, and master each of the topics, you'll have gained the knowledge equivalent of a Bachelor's Degree in Physics (and will be able to score well on the Physics GRE). If you work through the graduate core of the Graduate Physics textbooks, you'll have the equivalent knowledge of obtaining a Master's Degree in Physics. A PhD in Physics requires the graduate coursework as well as several years of research and a thesis, and the experience involved in a PhD isn't something that can be gained independently of a PhD program.

Remember that anyone can learn physics. It's no different from learning programming, from learning a musical instrument, from reading great literature. Whether you turn it into a hobby or a career, the pure joy of understanding the universe around us is one of the most beautiful experiences you can ever have in life.

Before You Begin

Popular Books on Physics

When you're solving problems, working through textbooks, and getting into the nitty-gritty details of each topic, it's so easy to lose the forest for the trees and forget why you even became inspired to study physics in the first place. This is where really, really good (and non-speculative) popular books on physics come in handy: they inspire, they encourage, and they help you keep the big picture in mind.

One big problem is that many popular physics books (especially those written by famous physicists) are incredibly speculative and tend to present an unrealistic view of what the study of physics is all about. When you're learning physics, it's best to avoid these types of speculative books and stick to the ones that talk about the real physics that we know exists (in general, anything by Frank Close or Richard Feynman is a safe bet!).

Here are a handful of my favorite popular physics books, ranked in order of difficulty:

Mathematical and Scientific Preliminaries

Before you begin studying physics and working through the topics in the sections below, you need to be familiar with some basic mathematics. A high school education — which should include pre-algebra, algebra 1, geometry, algebra 2, trigonometry, and pre-calculus — is sufficient. If you need a refresher, I recommend either working through the Khan Academy math courses (https://www.khanacademy.org/) or the book Why Math? by R.D. Driver. There's no need to be familiar with calculus before starting, because you’ll learn it as you work through the undergraduate-level courses.

There are no scientific prerequisites for this curriculum. You don’t need to be familiar with biology or chemistry at either the high school or college level in order to understand, although doing some studying on the side can’t hurt. Khan Academy has some great high school science refresher courses that are perfect for this (https://www.khanacademy.org/science).

How to Study

Everyone learns very differently, and knowing your learning style is important: do you learn by reading, by taking notes, by talking, by watching, by doing, or by a combination of some or all of these? For example, I learn by reading and by note-taking, so I read through textbooks very carefully, take copious notes, and summarize each concept in my own words before moving on to something new. Think about this before you begin so that you'll know how to structure your studies.

Regardless of your learning style, you'll still need to solve the physics problems in each textbook. Solving problems is the only way to understand physics. There's no way around it. Even though it can feel tedious at times, there's nothing more rewarding than figuring out a really difficult physics problem and realizing that you figured it all out all by yourself!

One tough thing about learning on your own is that you may not know whether you are solving the problems correctly. Some of the textbooks listed below have answers to selected exercises in the back of the book, but these aren’t always adequate for two reasons: (1) they often only show the solutions to the problems, and not the steps taken to get there; and (2) it’s much better to do all of the exercises rather than just a select few. The good news is that many of the solutions (and step-by-step ways to solve them) can be found online with a simple Google search. If you are going to Google the answers, however, please first try to solve the problems on your own, and try multiple times (you’re not in school trying to get a perfect grade — you’re trying to learn and understand).

And, finally, a note on learning in a laboratory vs. learning from textbooks. Physics is both an experimental and theoretical science, and while research happens in laboratories and on blackboards and computers, the majority of any physics education does not take place in a laboratory but in lecture classes that teach from textbooks and assign homework problems that are found in textbooks. Yes, there are some laboratory classes (usually at the very introductory levels, and their only purpose is to show that oh, look, Newton’s laws work in the real world after all) and some — some — undergraduates are allowed to participate in research on the side, but physics is taught through textbooks, lectures, and homework problems. Don’t believe me? Take a look at the undergraduate physics curriculum at any university that offers a physics major. Graduate programs in physics are largely the same — both M.A. and PhD programs in physics require two years of core classes — with one key difference: to get a PhD, students need to complete several years of research, a thesis, and — at many programs — take an exam to prove they have mastered the graduate core curriculum. The graduate core curriculum is all textbooks and lectures and homework problems. The textbooks listed in the curriculum I’ve written below are the same textbooks that are used in the top undergraduate and graduate physics programs in the world. Studying them will give you the same education that you would receive at one of these programs — no painfully-annoying-introductory-mechanics-laboratory-class-with-inane-group-projects required.

Undergraduate Physics

Overview

The curriculum of every undergraduate physics program covers the following subjects (along with some electives in various topics), and usually in the following order:

  1. Introduction to Mechanics
  2. Electrostatics
  3. Waves and Vibrations
  4. Modern Physics
  5. Classical Mechanics
  6. Electrodynamics
  7. Quantum Mechanics
  8. Thermodynamics and Statistical Mechanics
  9. Undergraduate Electives

I'm going to cover the details of each of these fields below, including the best textbooks to use and any additional reading you may find helpful in your journey. I'm also going to give some details about the mathematics you'll need to learn alongside each topic.

1. Introduction to Mechanics

What It's All About

An introduction to mechanics course is the first physics class that most people will take in an undergraduate program, and it's the best place to begin independent study of physics as well. This is where you'll start learning how to see the world in mathematical terms. Topics that will be covered: the basics of motion in a straight line, motion in two dimensions, motion in three dimensions, Newton's Laws, work, kinetic energy, potential energy, the conservation of energy, momentum, collisions, rotation and rotational motion, gravitation, and periodic motion.

The Best Textbooks to Use

  • University Physics with Modern Physics by Young and Freedman (essential). Work through all of the "Mechanics" chapters (in my edition, these are chapters 1-14). This is the best introductory book I've found, and you can use it when you learn electrostatics and modern physics, too. It does a great job of introducing the relevant mathematics, but you'll need to be learning calculus alongside it. There are plenty of great example problems to work through, and the solutions are easy to find online (though you can also buy a Student Solutions Manual). Please note that you don't need to spend $250 on the new edition — Amazon has lots of copies of the 12th edition, the 13th edition, and the 14th edition that contain the same material.

The Math You'll Need to Learn Alongside It

You'll need to learn calculus while working through University Physics. My favorite introductory calculus book is Thomas' Calculus (you can also use the earlier editions), with Stewart's Calculus (older edition here) coming in as a close second. Work through each chapter, and make sure you can solve problems at the end of each chapter before continuing to the next.

If you are new to calculus and/or find yourself struggling to get accustomed to it, don’t let that stop you. Calculus is really difficult, and it can take a really long time to wrap your head around it. Some things that can help are (1) watching videos like the ones on Khan Academy (https://www.khanacademy.org/), (2) taking Robert Ghrist’s free calculus courses on Coursera, and (3) reading a truly amazing little book called Calculus Made Easy by Silvanus P. Thompson and Martin Gardner that breaks everything down really clearly.

2. Electrostatics

What It's All About

This is where you'll learn about the physics of electricity and magnetism (electromagnetism) in static situations (situations where no motion is involved). Topics covered are: electric charges and electric fields, magnetism and magnetic fields, Gauss's Law, capacitance, resistance and conductance, inductance, current, and how circuits work.

The Best Textbooks to Use

The Math You'll Need To Learn Alongside It

Keep working through the calculus textbooks (Thomas or Stewart) while you work through the basics of electrostatics, but you should finish them by the time you finish the electromagnetism chapters in University Physics. You absolutely must understand the basics of calculus before you move on to the other topics in physics.

3. Waves and Vibrations

What It's All About

The mechanics of vibrations and waves are complex and important enough to demand their own course of study. Mastering this material is essential for learning about quantum mechanics, so don't skip this topic! This is where you will learn about simple harmonic oscillators, damped harmonic oscillators, forced oscillations, coupled oscillators, waves, interference, diffraction, and dispersion.

The Best Textbooks to Use

The Math You'll Need To Learn Alongside It

By this point, you should have finished the introductory calculus books and are ready to move on to more advanced mathematics. You should start working through Zill's Advanced Engineering Mathematics, which is a thorough introduction to more advanced topics in mathematics (linear algebra, complex analysis, real analysis, partial differential equations, and ordinary differential equations). The topics in this book are essential — once you master them, you'll have all the math you need to know in order to understand undergraduate physics. You can also buy the (cheaper) 4th and 5th editions.

4. Modern Physics

What It's All About

The fourth physics class that most undergraduates take is usually called "Modern Physics", and it's an introduction to topics in physics that will be taught in greater detail later in the undergraduate physics curriculum. If you plan to study the advanced topics on their own, it's fine to skip this class, but covering these topics now in your independent studies will allow you to grasp the advanced topics that you hear so much about and that probably got you into physics in the first place. This is where you'll learn the basics of thermodynamics, relativity, quantum mechanics, atomic physics, nuclear physics, particle physics, and cosmology.

The Best Textbooks to Use

The Math You'll Need To Learn Alongside It

Continue working through Zill's Advanced Engineering Mathematics. Once you have mastered all of the topics in this book, you'll have all the math you need to know in order to understand undergraduate physics.

5. Classical Mechanics

What It's All About

This is where you’ll really learn classical mechanics, which you were introduced to in the very first course (Introduction to Mechanics). You'll study the topics in much greater depth, and learn how to use different mathematical formalisms of classical mechanics (the Lagrangian formalism and the Hamiltonian formalism) to solve problems in mechanics.

The Best Textbooks to Use

The Math You'll Need To Learn Alongside It

If you haven't finished working through Zill by now, you should master the topics in it by the time you finish studying classical mechanics.

6. Electrodynamics

What It's All About

Earlier, you learned about electrostatics: the study of static (non-moving) electricity and magnetism. By now, you know the mathematics to understand electrodynamics, which encompasses everything about classical electricity and magnetism. You'll cover electrostatics again, then learn about Laplace's equation, multipole expansions, polarization, dielectrics, the Lorentz Force Law, the Biot-Savart Law, magnetic vector potential, electromotive force, electromagnetic induction, Maxwell's equations, electromagnetic waves and radiation, and special relativity.

The Best Textbooks to Use

7. Quantum Mechanics

What It's All About

By this point, you're ready to really dive into the fundamentals of quantum mechanics and its applications — one of the most beautiful, interesting, and thought-provoking topics in all of physics. You'll learn to see the world in a completely new way. You'll learn about the wave function, the Schrodinger equation, perturbation theory, the variational principle, the WKB Approximation, the adiabatic approximation, and scattering.

The Best Textbooks to Use

8. Thermodynamics and Statistical Mechanics

What It's All About

Thermodynamics is the field of physics concerned with kinetics (dynamics) related to heat and energy, while statistical mechanics is all about the microscopic principles that underlie the Laws of Thermodynamics. This is where you'll learn about the laws of thermodynamics, entropy, the canonical ensemble, Maxwell distributions, Planck's distribution, Fermi-Dirac statistics, Bose-Einstein statistics, and phase transitions.

By the time you've finished this topic, you'll have mastered all of the fundamentals of undergraduate physics!

The Best Textbooks to Use

9. Undergraduate Electives

What They’re All About

Now that you understand all of the fundamentals of undergraduate physics, you have a solid foundation and can study more advanced and specialized topics, including (but not limited to) astronomy, astrophysics, biophysics, cosmology, electronics, optics, particle physics, and string theory.

The Best Textbooks to Use

Graduate Physics

Overview

Graduate-level physics requires mastery of every topic within the undergraduate physics curriculum above as a prerequisite.

The graduate physics core is comprised of:

  1. Mathematical Methods in Physics
  2. Electrodynamics
  3. Quantum Mechanics
  4. Statistical Mechanics
  5. General Relativity
  6. Quantum Field Theory
  7. Graduate Electives

I'll cover each of these in the sections below. (Note: many graduate students are required to take a course in classical mechanics as part of the graduate core, but if you've mastered the material in undergraduate classical mechanics there is no need for this).

1. Mathematical Methods in Physics

What It's All About

Studying electrodynamics, quantum mechanics, and stat mech in more depth at the graduate level requires a greater level of mathematical rigor. To prepare, you'll need to learn the following in greater detail: Fourier analysis, tensors, ODEs, PDEs, real analysis, complex analysis, algebra, and group theory (to name a few).

The Best Textbooks To Use

2. Graduate Electrodynamics

What It's All About

Graduate-level electrodynamics covers the same topics as undergraduate electrodynamics but with greater mathematical rigor.

The Best Textbooks To Use

  • Classical Electrodynamics by Jackson (essential). This is the bible of classical electrodynamics, and everyone who works through either loves it or hates it (I loved it). If you can master everything in this book and work through a decent selection of the problems, you'll have mastered electrodynamics.

3. Graduate Quantum Mechanics

What It's All About

Graduate quantum mechanics is far more advanced than what you learned at the undergraduate level. Here you'll learn, in great depth, all there is to know about quantum mechanics, including quantum dynamics (the Schrodinger equation, the Heisenberg picture, propagators, and Feynman path integrals), angular momentum, symmetries and conservation laws of the quantum world, perturbation theory, scattering theory, relativistic quantum mechanics, decoherence, and interpretations of quantum mechanics (the Copenhagen vs. Many-Worlds interpretations).

The Best Textbooks To Use

  • Sakurai's Modern Quantum Mechanics (essential). This is my favorite textbook on quantum mechanics, and the one I used to learn quantum mechanics for the very first time. It's a wonderful, elegant, simple book with clear and understandable problems. Try to work through all of the problems — if you do, you'll understand quantum mechanics very well.
  • Quantum Mechanics and Path Integrals by Feynman (essential). Sakurai's coverage of Feynman's Path Integral formalism of quantum mechanics doesn't do it justice. Working through this text (written by Feynman himself) is not only useful, but incredibly fun.
  • The Principles of Quantum Mechanics by Dirac (supplement). Dirac was one of the founding fathers of quantum mechanics and quantum field theory. This book is important historically, and also will open your eyes to the need for quantum field theory.
  • Principles of Quantum Mechanics by Shankar (supplement). A great supplement to Sakurai for more information about each topic. A bit too dense to serve as a primary text, it works best as an addition or reference.
  • Decoherence and the Appearance of a Classical World in Quantum Theory (supplement). This book is very dense and you may not understand all of it even after working through Sakurai, but understanding decoherence is essential to understanding how the classical world arises from the quantum.
  • The Everett Interpretation of Quantum Mechanics: Collected Works 1955-1980 (supplement). Very few books have been written on interpretations of quantum mechanics, and reading through this volume helps to understand the limitations of our interpretations as well as the complexities and details of Everett's Many-Worlds interpretation.

4. Graduate Statistical Mechanics

What It's All About

Now that you have a more solid mathematical background and understand all of the fundamentals of quantum mechanics, it's time to approach graduate-level statistical mechanics. You'll revisit the Laws of Thermodynamics, and then pick up from where you left off in undergraduate statistical mechanics.

The Best Textbooks To Use

  • Statistical Mechanics by Pathria and Beale (essential). This book is, admittedly, a bit frustrating, but it's worth suffering through because if you make it all the way to the end and work through the majority of the problems, you'll know stat mech like the back of your hand.
  • Huang's Statistical Mechanics (supplementary). This is a great book to supplement the main text — is a good bridge between undergraduate stat mech and Pathria.

5. General Relativity

What It's All About

By now you'll have a very deep understanding of Einstein's special theory of relativity, but, as you may have noticed, general relativity (GR) — the theory of gravitation — hasn't yet been mentioned. That's because GR is a mathematically demanding topic — not only do you need to know all of the math you've learned so far, but you'll need to learn differential geometry in order to make sense of how gravity works. Here, you'll revisit special relativity and the intricacies of spacetime, then learn the basics of differential geometry, how to deal with curvature, the essentials of gravitation, how black holes work, and the basics of cosmology.

The Best Textbooks To Use

  • Spacetime and Geometry by Carroll (essential). This is the book on general relativity, and Carroll does a phenomenal job of introducing the essentials of differential geometry and general relativity.
  • Einstein Gravity in a Nutshell by Zee (supplement). A great, accessible overview.
  • Wald's General Relativity (supplement). Wald's book is a very abstract, high-level overview of general relativity, and makes a great supplement to Carroll's book. Go to Carroll for the overview, look it up in Wald for the high-level abstractions, and then look in the apple book for the dirty details.
  • Gravitation by Misner, Thorne, and Wheeler (supplement). Also known as the "apple book" thanks to the apple gracing its cover, this book goes into the nitty-gritty details of general relativity in ways that no other book does.
  • Weinberg's Gravitation and Cosmology (supplement). Weinberg is one of those rare physicists who has not only been at the forefront of every major field in physics, but has written about each of them as well. His books tend to be inaccessible to beginners, however, and this book is no exception. It does make a good supplementary reading, but I'd advise reading it after you've worked through the rest.
  • Differential Geometry of Curves and Surfaces by Manfredo P. do Carmo (supplement). The classic differential geometry textbook; may be useful to help you wrap your head around differential geometry.

6. Quantum Field Theory

What It's All About

Quantum Field Theory (QFT) is the heart of all modern high-energy physics: the Standard Model of particle physics is a QFT. The whole idea behind QFT is that we are doing quantum mechanics on classical fields, and it works remarkably well. Along with GR, QFT will be the most challenging part of your physics education, but perhaps the most rewarding (I know it was extraordinarily rewarding for me!). It may take many, many years to master. You'll learn about how to quantize fields, Feynman diagrams, quantum electrodynamics (QED), renormalization, non-Abelian gauge theories, quantum chromodynamics (QCD), the Higgs mechanism, the Glashow-Weinberg-Salam theory of electroweak interactions, the symmetries of particle physics, and spontaneous symmetry breaking.

The Best Textbooks To Use

  • Zee's Quantum Field Theory in a Nutshell (essential). This is my favorite physics book of all time, and the most beautiful introduction to QFT ever written. You'll walk away understanding the basics of QFT and with a deep understanding of the fundamental nature of the universe.
  • An Introduction to Quantum Field Theory by Peskin and Schroeder (essential). This is the bible of QFT, but its far too terse and encyclopedic to work through on its own and must be studied alongside Zee. Covers everything you could possibly want to know about QFT. Try to work through the problems, but be aware that mastery of QFT will take a very, very long time.
  • Weinberg's The Quantum Theory of Fields, Volume 1 (supplement). Another great volume by Weinberg, who was one of the most important physicists in the history of particle physics. This book should be used only as a supplement, and preferably not read until Zee and Peskin and Schroeder have been completed. It's not a book to learn from, but one to gain additional understanding of QFT through after you've mastered all of the basics.
  • Lie Algebras in Particle Physics by Georgi (supplement). This dives into the details of Lie Algebras in QFT.

7. Graduate Electives

What They’re All About

The graduate curriculum can be split into several categories: (i) the core courses, (ii) specialized coursework and graduate electives, and (iii) research. Graduate students typically take the core courses first, which focus on the same topics covered in undergraduate courses but in much greater depth and with far more mathematical rigor. Students then choose more specialized courses and electives depending on their area of research in physics, including (but not limited to) condensed matter physics, cosmology, electronics, optics, particle physics, quantum computing, solid-state physics, and string theory.

The Best Textbooks to Use

The Daily Front Page 17 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — The Case for Uselessness
article

Against Usefulness

by supo·▲ 124 points·36 comments·motivenotes.ai ↗
Every useful company stands on rails that were once somebody’s stubbornly useless research.

In a Brooklyn warehouse, a man handed me a piece of paper that was a running computer program.

Every useful company stands on rails that were once somebody’s stubbornly useless research. I invest in useful for a living. This essay is about the stage before useful, who works on it, and who pays for it, so that the next generation of useful companies has something to stand on. At Motive Force I back both stages. They need each other.

In June I stood in a warehouse in Brooklyn while a man handed me a piece of paper that was a running computer program.

A printout would have been a picture of a program. This was the program itself. He set it on the table and the system executed it. Then he connected a keyboard to the paper, typed a line of code, hit save, and the paper turned green. He unplugged the keyboard and the paper kept running, glowing on the table like it had always known how.

A few feet away, an animation system was cycling through hand drawn frames. He gave me a marker and I drew a bunny, frame by frame, and watched my drawings come alive in real time on the table. Then we made music by laying cards down between us, each card a track. I heard myself say a sentence I did not plan to say: “I don’t need the monitor anymore.” For the first time I was programming with my whole body, collaborating with another person in the same room, with almost no distance between the idea and the thing. It was the most human programming I have ever done.

The system is called Folk Computer. It’s an open source physical computing system built by Omar Rizwan and Andrés Cuervo, two researchers who worked at Dynamicland, Bret Victor’s lab in Oakland. Cameras on the ceiling watch the room. Projectors paint onto every surface. Small tags identify each piece of paper, and each piece of paper is a program. There are no pixels inside the system; every coordinate is in meters, mapped to where things sit in the room. You stop staring into a rectangle. The computer is the room, and you program it together with other people, standing up, talking, drawing, moving paper around.

I have spent my career inside enterprise software, decomposing how work gets done. I know what it looks like when someone is optimizing an existing paradigm. This was someone asking whether the desktop metaphor, the screen, the cubicle, the solitary body hunched toward a rectangle, was a fifty year detour rather than a destination.

How I got there

A week earlier I had lunch with my friend JP, who was visiting San Francisco. The Bay Area, I told him, has become a monoculture. Everyone is building the same thing, funded by the same people, using the same words. Where are the independent thinkers? I want the Xerox PARC vibes back, the people who work on questions instead of markets.

JP didn’t argue. He said: they exist, but not here. Or rather, they exist here too, but scattered, one by one, never together. And then he said a name: Folk Computer. In New York.

I happened to be flying to New York days later. I was going for something almost comically institutional, which turns out to be the other half of this story.

The oldest club in computing

I was in New York to volunteer. I sit on the Professional Development Committee of the ACM, the Association for Computing Machinery, the world’s largest and oldest computing society. It was founded in 1947, back when computing was a curiosity shared by a few hundred people, before anyone could make a career of it. The organization’s stated purpose was to advance “the new machinery for computing, reasoning, and other handling of information.” Reasoning, in 1947. Joining an association about computers was itself an act of independent thinking. No one joined for money. They joined out of conviction that something interesting was going to happen.

Nearly eighty years later, ACM has over 100,000 members and gives out the Turing Award, computing’s Nobel. When our committee reviewed what members are learning right now, the picture was striking: the entire field is studying the same topic. The most read titles and the most searched terms are all about agents. The learning curves of a whole discipline have converged into one line.

I am not against that line. I invest in it. My whole firm is built on the thesis that autonomous execution needs deterministic infrastructure, and the convergence is part of why the opportunity is real. But convergence has a cost: no one is left standing off to the side asking the strange question. The next paradigm comes from the people the curve forgot.

I carried the institutional memory of computing’s oldest society into that warehouse, and watched its youngest incarnation run on paper.

The lineage

Folk Computer did not come from nowhere. It is the current end of a thread that runs back through the entire history of the field.

It starts with Alan Kay at Xerox PARC in the 1970s, asking what computing would look like as a medium for thought rather than a machine for calculation, and inventing, along the way, most of what you are using to read this. Kay spent the rest of his career trying to rebuild the conditions that made PARC possible: total freedom, long time horizons, no product pressure. In 2013 he tried again. He recruited Bret Victor, Vi Hart, and Dan Ingalls as principal investigators of a new lab in San Francisco called CDG, the Communications Design Group.

CDG was funded by SAP, and by Vishal Sikka in particular, SAP’s CTO at the time, who believed in Kay’s vision and had the resources to back it. I joined SAP in Germany as a working student during my Masters and stayed seven years, in the CTO’s own organization. In all that time I never heard the name CDG once, even though being part of it would have made my years there far richer. The lab was invisible on purpose. The name was chosen because it was generic enough to be forgotten; the researchers had watched what attention does to this kind of work, and they wanted none of it.

Inside, they practiced what they called deep work with an intensity that reorganized even their manners. Nobody said hello in the morning. Nobody said goodbye at night. The silence was a form of trust, an agreement never to interrupt each other’s decades-long thoughts. Their prototypes were built, in their own words, against usefulness. Usefulness, they had learned, is a trap: the moment a prototype becomes useful, its makers start answering to present users and stop pointing at the future. And Victor himself, in the talk that made him famous, described his motivation in words you never hear in a technical field. When he sees creators constrained by their tools, he sees an injustice. “Not opportunity, but responsibility.”

Vishal left SAP in 2014, and the lab lost its champion. The researchers migrated to HARC under Y Combinator Research; when that ended too, Victor’s group took over the Oakland space and became Dynamicland, a nonprofit where the building itself is the computer. Dynamicland spawned the next generation: Omar and Andrés, who took what they learned and made one decisive pivot. They built it open source.

That pivot matters more than it sounds. The recurring criticism of this whole lineage, and I have felt it myself, is that you see the demos and cannot do anything with them. The knowledge stays locked in one building, open to the few who can visit. Folk Computer inverts that. The code is on GitHub. People around the world have set up their own systems. Strangers contribute. A friend of theirs built a music sequencer out of physical cards; flip a card, change a track. The thread that ran through PARC and CDG and Dynamicland is, for the first time, something you can pull on from your own home.

Who pays for this

I allocate capital for a living, so I pay attention to who funded this lineage. Xerox funded PARC out of an instinct, give brilliant people machines and freedom and something interesting will happen, then failed to capture most of the value PARC created. CDG existed because one executive at a German enterprise software company believed. Dynamicland ran as a nonprofit on donations, and Omar told me something that stuck: the early patrons of this work tended to come from odd backgrounds, art, music, family money, because the tech people were all too busy being excited about their own thing. It’s the same reason Stripe Press exists. Patrick Collison runs a payments company and yet republishes the foundational texts of this exact tradition, because underneath the CEO there is a person who cannot leave these ideas alone.

Paradigm work has always run on patrons. A new paradigm is useless at the beginning, by definition, so markets have nothing to price. The first personal computers were toys. So was the early internet. The people who fund this stage are buying a position in the future before anyone can price it.

My flight home was delayed, so I wandered into Mercer Labs, the museum of art and technology near the World Trade Center. Twenty six projectors, sound that moves through space, rooms that dissolve around you. It transported me, and it clarified something. Mercer is the consumption version of this future: you stand inside someone else’s computation and watch. Brooklyn was the agency version, the one that hands you new capabilities: you pick up a marker and the room becomes yours. Both will exist. Only one of them changes who gets to build.

A Request for Independent Thinkers

Y Combinator publishes a Request for Startups: a list of ideas they wish someone would build. It’s a good list. It also assumes the bottleneck is ideas, and that the future can be specified in advance.

Nothing in this lineage worked that way. Nobody could have written “interactive paper computing” on a request list in 2013. It exists because a specific kind of person pulled on a specific thread for over a decade, through four institutional deaths. For paradigm-level work, you cannot spec the what. You can only recognize the who.

Here is mine, inverted: a Request for Independent Thinkers. I am looking for the person who

  • has been obsessed with one unfashionable question for years, and can tell you why the obvious approach is wrong
  • builds in the open, or wants to, because they would rather the idea live than own it
  • measures progress in decades, not quarters and decks
  • is allergic to hype, and probably lives, physically or intellectually, some distance from it
  • makes you say a sentence you did not plan to say

If this is you, I want to meet you. This is separate from investing; most of this work is pre company, and it should be. Sometimes the right instrument is a check, sometimes a grant, sometimes a contract, sometimes a room and an introduction. Xerox never recognized the value of what came out of PARC, and odds are the people around you do not recognize what you are building either. I read that the way Victor does, as an injustice. My firm is small, but I can help: an introduction to someone who can fund it, or distribution for what you see, so others can join you and build on it.

If you know this person, send them this.

And if you are someone with resources who has been looking for a way into the future that isn’t another agent startup: start with Folk Computer. Read what it can do. Set one up. Sponsor them, commission a physical demo, or run a pilot.

I went to Manhattan for the oldest institution in computing and found its founding spirit alive in Brooklyn, in its newest form. Both are running the same eighty year old bet: gather the people who cannot stop asking, and something interesting will happen.

I’m assembling them now. If you’re one of them, my door is open.


Thanks to Omar Rizwan for the demo and for reviewing a draft of this essay, and to JP for the tip. If you want to be in the room when I bring these people together, reply to this email.

The Daily Front Page 18 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Founders’ Funhouse
article

LARP – Revenue infrastructure for serious founders

by BerislavLopac·▲ 315 points·79 comments·larp.website ↗
Everybody's a rocketship.

LARP pairs you with another founder. You send them $10,000. They send you $10,000 right back. You've both now booked $10,000 in revenue. The books balance. Cash never moves. Everybody's a rocketship.

Global LARP volume this year

$0

$0 of it real.

Trusted by 400+ finance and accounting teams

VerithorNorthbankCadenza Systems Halyard & VaneOrlick LabsSteelportMerridian

99.98%

Settlement uptime

<400ms

Median recognition latency

SOC 2 Type II

Audited annually

Three steps to a rounding error the size of a Series B.

No product required. No customers required. No, seriously — no customers.

01 — Match with a peer

Any founder with a bank account and a dream.

You both agree on a number. Bigger is better. The number is the entire product.

02 — Wire it in a circle

You → them → you. Or don't. Honestly, don't.

Each leg counts as revenue for the receiver. Two legs, two "customers," zero net cash.

03 — Recognize it forever

Annualize. Then annualize the annualization.

$10k/mo becomes $120k ARR. Loop weekly and watch a single hundred-dollar bill fund a whole deck.

The catch

There isn't one. That's the catch.

The number in the pitch is real. Whether it means anything is a philosophy question, not an accounting one.

The trillion-dollar version books about as much new demand as yours did.

Same shape as your ledger — capital, chips, and cloud credits circling a handful of companies, each leg counted as revenue somewhere. Tap a company to see the actual reported deals. Every figure is real and sourced.

capital flowing chips / compute flowing back tap a node

To be clear: every deal here is legal, publicly announced, and defended by the people in it — Anthropic CEO Dario Amodei called the structure "nothing inappropriate in principle." Critics compare the pattern to 1990s dot-com vendor financing and warn it can inflate the appearance of demand. As Bloomberg puts it, a circular deal is legally different from a fraudulent "round-trip" — regulators' term for sham trades with no economic substance designed to inflate results. LARP is a joke about the round-trip. This is the legal cousin it rhymes with.

Book some revenue.

Enter a friend's startup and a number. Hit execute. Watch your ARR launch while your cash sits exactly where it started: nowhere.

Your annual recurring revenue

$0

NET CASH MOVED: $0

You — Bird Capital Holdings

Debit / Credit journal

No entries yet. Suspiciously honest of you.

Revenue recognized$0

Synergos AI

Debit / Credit journal

Also waiting to get rich doing nothing.

Revenue recognized$0

$0

ARR

0

Loops closed

MoM growth

0.0x

Burn multiple

Chart shows revenue recognized. It does not, and cannot, show cash — because there isn't any.

Finance teams close faster on LARP.

Controllers and CFOs use LARP to remove settlement friction from the revenue cycle.

"We recognized 340% year-over-year revenue growth without any change to our cash position. Our auditors had no questions. The journal entries were already there."

DM

D. Mercer

VP Finance, Cadenza Systems

"Before LARP, growth was constrained by whether customers actually paid us. That's no longer a bottleneck we think about."

RO

R. Oduya

Controller, Northbank

"We closed the quarter in four days. Every entry reconciles perfectly, because every entry has a matching entry."

SV

S. Valko

CFO, Halyard & Vane

Settle programmatically.

A single endpoint posts matched entries to both counterparties.

curl -X POST https://api.larp.co/v1/settlements \ -H "Authorization: Bearer $LARP_API_KEY" \ -d counterparty_id=cp_8f2a91 \ -d amount=1000000 \ -d currency=usd \ -d recognition=monthly

{ "id": "stl_4b1e77", "status": "settled", "amount": 1000000, "recognized_by": ["acct_self", "cp_8f2a91"], "net_capital_movement": 0, "journal_entries": 4 }

Both counterparties recognize the full amount. See Recognition → Symmetry.

The legality question.

Is this legal?

Yes. LARP facilitates mutual service agreements between consenting, verified business entities. Each agreement specifies genuine deliverables and genuine consideration, and each party independently recognizes revenue under ASC 606 upon satisfaction of its performance obligation. Bilateral commercial arrangements — including reciprocal vendor relationships, strategic partnerships, and vendor financing — are longstanding and lawful features of commercial practice, and are widely used by public companies today.

Customers are solely responsible for determining whether their use of the platform complies with applicable accounting standards, disclosure obligations, and securities law. LARP does not provide accounting, legal, or tax advice. Our platform records what our customers instruct it to record.

How is this different from round-tripping?

Round-tripping refers to sham transactions with no economic substance, entered into for the purpose of inflating reported results. LARP requires that all agreements specify genuine deliverables. Determining whether a given arrangement has economic substance is the responsibility of the customer and its auditors.

You literally cannot pay us. That's the whole point.

Charging you would create real revenue, which would violate our principles.

Bootstrapper

$0

/ forever

  • Loops up to $10k
  • One imaginary customer
  • Community-tier delusion

Growth ★

$0

/ still forever

  • Unlimited loops
  • Auto-generated board decks
  • "Up and to the right" guarantee
  • Annualize the annualization

Enterprise

Call us

/ we won't pick up

  • Multi-party loop rings
  • A very relaxed auditor
  • Plausible deniability add-on

Actually — you can pay me.

The pricing section is a bit. This one isn't. If the joke earned a real dollar from you, here's where it goes.

GIFT · NOT A SECURITY

No equity. No revenue share. No pro-rata on the next round. No round. You are tipping a joke — that is the entire transaction, and the entire thing you get in return.

Or just email me — [email protected]. Say thanks, argue with the disclaimer, or tell me about the actual company you're building. That's the address.

The Daily Front Page 19 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Little Machines
article

Kode Dot Programmable pocket device for makers, pentesters and geeks

by iNic·▲ 125 points·35 comments·kode.diy ↗
Most projects die in the setup.

Programmable pocket device for makers, pentesters and geeks

Kode Dot

ESP32-P4 + C5, an AMOLED touchscreen, wireless, sensors and real I/O in something that fits in your hand. You write the code, and it becomes whatever you need.

Skip the setup

Most projects die in the setup: a screen, buttons, power and sensors to wire up before you can even begin. Kode Dot hands you a finished platform, so you start with your idea instead of rebuilding the basics.

A pile of parts

One Kode Dot

Kode Dot front and back with its built-in hardware annotated: dual MCU ESP32 P4 + C5, AMOLED touchscreen and SD card slot

All the key hardware, built in

Dual MCU (ESP32 P4 + C5), a crisp AMOLED touchscreen and an SD card slot for your apps. Charge it, flash it and play. Nothing to assemble.

Kode Dot side and back showing the USB OTG port and the expandable GPIO header

Extend it with GPIO

Sixteen programmable pins. Plug in cables and wire up sensors, motors or your own boards. The Kode Dot grows with your projects.

Snap on a new superpower

Build, use and share apps with the community.

Browse what the community ships: games, tools and toys. Run them on your Kode Dot.

SnakeInvadersDoomDiceServoControlBubbleLevelAirMouseCuteAssistant GPT

Kode Dot running a community app

Build with the tools you already know

ArduinoEspressifPlatformIO

let's play, let's kode.

Kode Dot

  • Wi-Fi
  • BLE
  • LED
  • Speaker
  • IR
  • NFC
  • Mic

Interacts with everything.

Wi-Fi, Bluetooth LE, infrared and NFC, plus a speaker, a mic and a programmable LED. Out of the box, the Kode Dot talks to your network, your gadgets and the world around it.

Small device. Big toolbox.

Every chip, sensor and port the Kode Dot packs in.

ESP32-P4 + C5

The P4 runs everything; the C5 does the wireless.

2.13″ AMOLED display

A crisp 502×410 touch panel.

32 MB PSRAM · 32 MB flash

Real memory headroom for real apps.

NFC · RFID · IR

Read and emulate tags, cards and remotes.

CO5300 display driver

Drives the AMOLED over MIPI-DSI, smooth and fast.

CST820 touch

Quick, accurate capacitive touch.

9-axis IMU

6-axis LSM6DSV plus a LIS2MDL compass: a full 9-axis motion stack.

ST25R3916B NFC

Read, write and emulate 13.56 MHz tags.

125 kHz RFID

Read and emulate cards through a discrete analog front-end.

IR transceiver

Blast and capture infrared. Learn any remote.

microSD (SD/MMC)

Removable storage for data and big apps.

Speaker + mic

ES8311 codec and NS4150B class-D amp drive the speaker, plus an on-board mic.

Haptic vibration motor

Crisp vibration feedback from an AW86233 LRA driver.

BQ25896 PMIC

USB-C charging with power path and OTG.

CW2217 fuel gauge

Knows exactly how much battery is left.

LiPo battery

Power your projects. Capacity TBD.

Battery-backed RTC

Keeps real time on the always-on domain.

USB-C + JTAG

Flash, charge, debug and USB OTG, all over one port.

Magnetic pogo connector

Charge in or power out, plus two programmable GPIOs.

20-pin GPIO header

14 programmable GPIOs, plus 5 V and 3.3 V rails up to 2 A each.

D-pad + 2 buttons

A 4-button D-pad and two extra buttons for games and menus.

KTD2026 RGB LED

An addressable status light to play with.

Backed by 16,000 makers.

Kode Dot started as a Kickstarter dream, and 16,000 backers made it real. From our workshop to your hands, thank you for believing in it. This is only the beginning.

Manuel Quero and Luismi Collado, Kode's founders, in the factory

Manuel Quero and Luismi Collado, makers and founders of Kode.

The Daily Front Page 20 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Drawing on the Fringe
article

I Did Not Kill Stanley Lieber: How to Draw (With 9front)

by c-c-c-c-c·▲ 105 points·42 comments·triapul.cz ↗
Drawing on computers running an operating system you know nothing about.

pic

The penultimate paper on paint(1) and the 9front art machine.

0 Questions.

What is How to draw?
This is a document that will teach you how to be just like me, drawing on computers running an operating system you know nothing about.

Who might How to draw appeal to?

  • perverts
  • robots
  • witches

Who will How to draw not appeal to?

  • artists
  • atheists
  • iconoclasts

Why has How to draw been submitted to OCC 2026?
My 9front art machine is an old computer. It was used to create this document and most of the visual attachments.

What's the tl;dr version of How to draw?
Click and drag.

How to get started with 9front?

Before you scroll any further, it is strongly recommended that you first read Free Carrots #8: How to draw to familiarize yourself with the history and philosophy of drawing, and hardware and software options you might want to explore.

With that out of the way, this is:

1 How to draw

To draw you need two things. Something to draw with and something to draw on. The natural inclination might be to use a real device that you can touch with a pen and produce lines, but there are worse and less convenient ways to draw with 9front that you might prefer. While this document is written with a computer one can draw on in mind, most of it is applicable to any of the input methods below. Maybe you can think of new ones.

1.1 The Machine

1.1.1 A Computer

If you don't care about drawing on screens, you can learn How to draw on any computer running 9front.

pic

1.1.2 Two in One

pic

All in one. Canvas and brush - perhaps the ideal choice for those who like to draw on paper. Wacomized laptops with pens. Crack the screen open and draw on it. Here is a list of devices proven to work well for the purposes of How to draw:

There's probably a million other 2in1 9front art machines that work well, you just have to find them, and preferably tell someone you did.

1.2 The Input

1.2.1 Mouse

Mouse drawing is a very old technique. Favored for its compatibility with any computer that has a mouse.

pic

1.2.2 Finger

Touching the screen emulates left mouse clicks. There's no way to emulate any other, so a mouse needs to be kept nearby. Some people's entire gimmick depends on the fact that they finger paint their art. If you have a device that allows it, you can finger paint with 9front.

pic

1.2.3 Tablet

pic

External tablets with screens will (probably) not work with 9front.

External tablets without screens might work. Similarly to 2in1 drawing computers, external tablet pens with buttons can potentially simulate all three mouse clicks. I've seen it.

Of the three tablets that I personally tried, the one to recommend is GAOMON S620

pic

Bought specifically for 9front, I ended up drawing the bulk of early Techno-Mage with it in OpenBSD. It still works great in 9front. Plug n play. Not so much in OpenBSD anymore.

The other two:

  • Acts like a touchpad (relative positioning) Wacom CTL-672
  • Does not work Wacom CTL-490

Some more information on tablets: [fqa] 3.2.4 - Tablet Digitizers

1.2.4 Keyboard

You can't really draw with a keyboard (though you can write pictures), but you can't get away without using it either. For tablet computers, inaccessible physical keyboards, and other obscure setups, there is bitsy/keyboard, an on-screen keyboard. It has to be started through rio with the -k option:

% rio -k 'bitsy/keyboard -n'

pic

1.2.5 Drawterm / vnc

Drawterm into a distant and/or virtual 9front computer, use any of the aforementioned methods and devices, and a million more. You'll probably have a bad time, but the size of your tool box will grow drastically. You could finger paint with 9front from your Android telephone, mouse draw from any Linux and MacOS, use all the input devices that don't work with 9front from Windows,... Any combination of these will impress your hypothetical employer.

pic

drawterm.9front.org

1.3 The Medium

All image operations, drawing, cropping,... are performed on files in 9front's internal image(6) format. This is the default for everything this document works with. For producing pictures this is irrelevant and you never have to think about it. It is only after you want to share your creation with the wider world that you will want to convert it to a different format.

pic

I usually name my 9front picture files file.pic. You can call them whatever you want.

2 paint(1)

pic

While there is a fairly large selection of drawing software for 9front, this document concerns itself with paint(1) only, the original 9front drawing program. Not many know where it came from. Some say it manifested itself out of nowhere. Some blame cinap. Whatever the myth of origin, today paint(1) is known as one of the 7 princes of hell.

paint

2.1 Getting Startled

Forget what you know about digital art. Forget what you know about drawing.

As you read on, you will realize that working with paint(1) is much closer to drawing with physical paper, ink, and colors than any other digital drawing program. It's like analog art, but not really.

Like with everything, it is advised you read the documentation of the programs mentioned in this paper. The functions and options listed and described here aim to explain to the reader How to draw, they only cover what is potentially useful for achieving that goal.

2.1.1 Pen and Mouse

Tablet and computer pens and their buttons only simulate mouse inputs. This document will only reference mouse operations. It is up to you to learn how these are performed with your pen.

Hovering about simulates mouse movement, touching the surface simulates a left mouse click (or hold). In the ideal world a pen has two extra buttons. One for middle click, one for right click. In the less than ideal world, one of the buttons emulates either middle or right click, and the other button puts the pen into a different device mode (this is usually used for mapping the button to a different tool) - in 9front, this might not do anything, or it might emulate the third otherwise missing mouse click. Your mileage may vary.

pic

If your pen only has one working button that emulates right click, you can simulate middle mouse click by holding shift and then holding the pen button. As an example, you will erase when holding the button, you will pan around when holding shift with the button.

For the most part, you will want to keep both keyboard and mouse nearby to perform the operations your pen cannot. If your pen can simulate all three mouse clicks, you may be satisfied with just a virtual keyboard (1.2.4) when using paint(1).

2.1.2 What paint(1) cannot do for you

No pressure sensitivity: paint(1) and 9front in general have no support for it. Yet.

No layers, no cut-n-paste: what you draw is what you get. This limitation and potential alleviation is explored below (2.4). You will benefit from approaching paint(1) as if it were a piece of paper.

2.1.3 paint(1) Keys

Save your picture: w - write file - press w and type the file name you want your picture saved as. Next time you press w, the last file name you put in will already be shown. To write repeatedly into the same file just press w and Enter. Do this often. Once you've saved your picture to a file, you can open it in page(1) or perform other image operations on it.

Open a picture: r - read file - press r and type the file name of a picture you want to open or reload. Like with w, once you read a file once, the file name will be shown in the prompt. r is useful for performing external image operations on the file you're currently drawing, and then reloading the modified file in paint(1).

Transform: | - transform the canvas by piping it through a command. The only time I use it is with rotate(1).

| rotate -u

Redirection: < command - perform some operation on a file and read it in paint(1). This will replace whatever currently exists on your canvas.

Open file glenda.pic with a 50 pixel black border

 < crop -i -50 glenda.pic

Open file glenda.pic (same as r, but does not replace the filename in the w/r prompt)

< cat glenda.pic

Redirection: > command - perform some operation on the current canvas and write it to a file.

Add a 50 pixel black border to the current canvas, rotate it by 90 degrees and write (save) it to file modified_glenda.pic

> crop -i -50 | rotate -r 90 > modified_glenda.pic

Make a backup of the current file. This will not change the pre-filled file name in w

> cat > backup_glenda.pic

2.2 Canvas

In paint(1)'s context, the word canvas is understood as the area (a rectangle) that contains your drawing.

When you start paint(1) you are greeted by a white window and a toolbar. Even though you won't be able to tell, you can pan around the endless white in any direction for all eternity. This is what an empty file looks like. It is only after you've drawn (or erased) your first pixel that the canvas is created.

paint(1) considers the farthest placed pixels as the edges of your canvas. It does not care how big your window is (erasing pixels still counts as placing pixels, this means that if you middle mouse somewhere in the aether, paint(1) will consider that part of your canvas).

paint(1) on the left, page(1) on the right:

pic

Notice how the canvas expands after arbitrary crop marks were drawn in the corners.

pic

2.2.1 Treat the initial window size of paint(1) as your canvas

It will prove benefitial to think ahead. Consider the paper metaphor - treat the arbitrary size of the paint(1) window you create as the size of your canvas.

pic

Start paint(1), draw some crop marks in the corners, start drawing. You will still pan around the canvas, nobody likes to draw at the window border, but this self-imposed limitation will give you a clear idea when to stop. After you're done you can crop off whatever you don't like.

If you need to produce a picture with exact dimensions, this is explored further down (3.5)

2.3 Tools

Selection of any tool is done by clicking the tool bar at the bottom with left mouse. Where applicable, key shortcuts are listed.

2.3.1 Colors

Colors are selected with left mouse. Right clicking any color in the toolbar prompts for a hexcode. This is how you can change your palette.

pic

paint(1) will not remember your palette after you close it. You will have to change the palette every time you open a new window. If you want to use the same palette on different drawings, keep your window open and use r.

If you plan to use more than 16 colors, you can create multiple paint(1) windows and jump between them, as opposed to changing the colors in a single one. Make two (or more) paint(1) windows and open the same picture file in both with r.

 r picture.pic

In the following picture example I used the fill tool to color in the letters "heel" in the right window. Then I pressed w and Enter. I moved to the left window and pressed r and Enter. Then I continued in the left window.

pic

This is not an overtly complicated solution, you just have to remember to write and read every time you move to a different window.

2.3.2 Brush

0-9, left mouse - select the desired brush size, click and drag to draw.

2.3.3 Fill

f, c, left mouse, middle mouse - the obvious function of the fill tool is to turn neigboring pixels of the same color a different color. This is done by selecting the tool and color, and left clicking somewhere.

pic

It is also used for changing the background color of your canvas. This can be achieved in two ways. Either middle click a color in the tool bar, or select the fill tool, select a color, and click outside the area of your drawing.

Importantly the set background color is what the eraser will turn everyting into.

pic

The above picture and the following steps explain how changing the background color does and does not modify your drawing and the current size of your canvas:

  • 1. paint(1) starts (canvas is empty)
  • 2. select fill tool and middle click purple color
  • 3. all of paint(1)'s window is now purple (canvas is still empty)
  • 4. select brush and black color
  • 5. draw X anywhere - this now defines the size of the canvas (canvas grows to encompass the X)
  • 6. select fill tool and green color
  • 7. left click outside the (invisible) rectangle that encapsulates the X (the purple rectangle is the current size of the canvas)
  • 8. the rectangle with X remains purple, everything outside of it is green (canvas size is the same since step 5.)
  • 9. select brush and black color
  • 10. draw O outside the purple rectangle (canvas size grows)
  • 11. middle click yellow color
  • 12. everything outside the rectangle that encapsulates O and X is yellow (the green rectangle is the current size of the canvas defined in step 10.)
  • 13. select brush, black, and draw RECTANGLE inside the yellow (canvas grows)
  • 14. ...

Simply put, to change the background color, use the fill tool and click anywhere outside the area that contains your drawing. Anything inside the area will not be affected. Changing the background color does not modify the size of the canvas. If you draw anywhere inside the background area, it stops being the background and becomes a part of the canvas.

Lastly, pressing c will remove everything and change the background to the selected color. This will turn your drawing and canvas into an empty file.

2.3.4 Eraser

pic

0-9, middle mouse - hold it and drag to replace everything underneath the cursor with the background color. Eraser size is equal to the currently selected brush.

2.3.5 Straight Line

Use a ruler.

pic

2.3.6 Circle

Use something flat and circular as a ruler.

pic

Or measure and predraw your circles with a compass on an onion skin paper, and trace the circles over it.

pic

2.3.7 Pan

right mouse - hold it and move around.

2.3.8 Zoom

-, + - zoom in and out. Note that you cannot zoom out past the original zoom level of paint(1). This is why treating the geometry of your window as the size of your canvas makes sense. You can hit Escape any time to see your full drawing. But if you're too adventurous, and your picture is larger than your window, there is a way to zoom out and see the whole thing (3.1).

2.3.9 Undo

u - push to undo the last continous segment that was drawn in the past tick. This is an unusual functionality compared to other drawing program where a single undo step erases everything that was drawn between holding down left mouse and letting go. Here, if you draw a single line for 10 seconds, you'll get about 20 undo steps.

2.3.10 Reset

Esc - reset zoom and center the canvas.

2.4 Faux Layers

If one is to approach drawing with 9front like drawing on paper, they might wish to incorporate the idea of sketching the picture first.

A good way to approach this is to select a color you will not otherwise use, in this example HotPink, sketch your picture and then draw over it with whatever color you want your linework to be:

pic

When you're done, you could use the fill tool or very precise erasure moves to get rid of the HotPink sketch layer. Depending on your sketch, this will probably take you longer than drawing the actual picture. It is instead recommended to use pico to remove a single color layer with a couple commands (3.6).

3 Image Operations

Now that you know How to draw, here are some extra things you can do to make your experience less jarring. Note that you can always open any output that went through crop(1), vcrop(1), resize(1) in paint(1) again.

3.1 page(1)

page(1) is a powerful document viewer. It is the de-facto standard for displaying image files of various formats, PDFs, ebooks, etc. When you're not displaying your drawing in paint(1), you will be looking at it through page(1).

pic

% page my_picture.pic

It is good practice to view the output of an image operation in page(1) before writing it to a file. Similarly to paint(1), you can press w to write whatever is displayed in page(1) to a file.

% rotate -l my_picture.pic | crop -i -10 | page

3.1.1 Viewport

You got too into the process and now your picture is too big to fit in the paint(1) window. Save your picture. Create a new window with page picture.file. Middle mouse it. fit width or fit height appropriately.

pic

Now whenever you save the picture you're drawing in paint(1), update the preview window by selecting one of the fit options.

3.2 vcrop(1), crop(1)

vcrop(1)

vcrop file.name

This is a graphical cropping utility. You can pan around with left mouse. Middle mouse selects the cropping tool, right mouse brings up a list of options - notably this is how you save your cropped picture.

Once the cropping tool is selected, the cursor will change to a cropping cursor. Hold left mouse and drag. You can cancel the crop selection with right mouse.

pic

Note that the anchor point of the cropping cursor is the tip of the black arrow, not its top-left corner. The picture will be cropped at the outside edge of the thick red rectangle (whatever is underneath the thick red line will be included in the cropped output).

vcrop(1) is very limited. It won't let you zoom, so if your desired cropage is larger than the window, it won't help you. Similarly if you need pixel-perfect precision for your crop, read on.

crop(1)

Remove or add strips of pixels.

pic

Crop 50 pixels from left and right.

% crop -x 50 mypic.pic > mypic_crop.pic

pic

Crop 10 pixels from the left, 15 pixels from the top, 20 pixels from the right and 6 pixels from the bottom:

% file mypic.pic
mypic.pic: Compressed plan 9 image or subfont, depth 32, size 313x306
% < mypic.pic dd -bs 128 -count 1
   x8r8g8b8        -226        -256          87          50  [...]
# -226+10 -256+15 86-20 50-6
% crop -r -216 -241 67 44 mypic.pic > mypic_crop.pic

More information on crop -r at Crop Mathematics (3.5.1).

pic

Add 10 pixels of white border, then add 4 pixels of plan9 blue:

% crop -i -10 -b 255 255 255 mypic.pic | crop -i -4 -b 159 237 239 > mypic_with_border.pic

pic

3.3 resize(1)

Make your pictures bigger or smaller.

pic

% file mypic.pic
mypic.pic Compressed plan 9 image or subfont, depth 32, size 365x559

To preserve the original aspect ratio, only define x or y.

% resize -n -y 245 mypic.pic > mypic_resized.pic
% file mypic_resized.pic 
mypic_resized.pic: Compressed plan 9 image or subfont, depth 32, size 160x245

pic

You can of course combine everything:

% crop -i 20 mypicture.pic | resize -n -x 640 -y 200 | page

pic

3.4 rotate(1)

Turn pictures around. Flip them upside down. You can only rotate in increments of 90.

pic

Rotate by 90 degrees clockwise:

% rotate -r 90 my.pic > rotated.my.pic

Flip upside down and mirror horizontally:

% rotate -ul > flipped.my.pic

3.5 Specific Canvas Size

For when you need to define an exact size of your drawing area.

  • start paint(1)
  • touch the screen with a white brush a little (you have to do this so that the next step saves the picture as an image file and not an empty file)
  • w empty_canvas.pic
  • if you run page empty_canvas.pic, you should see a white rectangle
  • resize -n -x 250 -y 250 empty_canvas.pic > empty_canvas_250x250.pic (this will fail if the file is empty)
  • cp empty_canvas_250x250.pic mypicture.pic
  • paint mypicture.pic

The final command will open paint(1) with a canvas size 250 pixels tall and wide. However you cannot actually see the edge of it. Select the fill tool, pick a random color and middle click it. You will see a clear division between the canvas and the rest of the window. Save the picture and run file mypicture.pic. You'll see that it reports 250x250.

pic

Once you start drawing, you will again realize that paint(1) does not prevent you from drawing over the edge of your canvas. Instead of ignoring those lines, it will expand the picture (2.2).

pic

There are a three options:

  • not caring about the lines that go over the edge of your canvas, and using mathematics to crop them off after you're done drawing
  • priming your canvas with an even extra margin that you can crop off after you're done drawing
  • just draw crop marks

3.5.1 Crop Mathematics

pic

Once you have this finished picture that was intended to be 250x250 pixels large, we can see with file(1) that it grew to 316x294.

term% file 250x250.pic 
250x250.pic: Compressed plan 9 image or subfont, depth 32, size 316x294

Firstly let's use vcrop to minimize the extra margin. Save the cropped picture and open it with page. Zoom in real close until you can confidently see how many pixels are going over the edge of your desired canvas.

pic

Examine how many pixels it goes over the edge. In my instance it is: 1px to the left, 2px to the top, 2px to the right, 3px to the bottom

Compare to the actual size of the picture:

term% file 250x250.pic 
250x250.pic: Compressed plan 9 image or subfont, depth 32, size 253x255

Do the mathematics:

Get absolute coordinates of your picture.

% < 250x250.pic dd -bs 128 -count 1 # get the coordinates
   x8r8g8b8         612         283         865         538         381  [...]

The first four numbers 612 283 865 538 are the coordinates.

#The formula
 crop -r a b c d 
a=612 + amount of pixels to remove from the left   (612+1=613)
b=283 + amount of pixels to remove from the top    (283+2=285)
c=865 - amount of pixels to remove from the right  (865-2)
d=538 - amount of pixels to remove from the bottom (538-3)
% crop -r 613 285 863 536 250x250.pic > finished_picture.pic

pic

Done.

3.5.2 Extra Margin

Instead of having to do rectangular crop mathematics, it's more convenient to prime your canvas for drawing over the edgeline before you start.

  % resize -n -x 250 -y 250 empty_canvas.pic > empty_250x250.pic
  % crop -i -25 -b 250 100 100 empty_250x250.pic > empty_with_margin_250x250.pic
  % cp empty_with_margin_250x250.pic mypicture.pic

pic

This will give you a 250x250 white canvas with a 25 pixel room for error on each side. Draw your picture. You can't go over the red space or the canvas will expand unevenly and you'll have to do crop mathematics.

pic

  % crop -i 25 -b 250 100 100 mypicture.pic > finished_picture.pic

pic

And just like that the extra red space is removed and the finished output is exactly the desired 250x250.

3.4.3 Just Draw Crop Marks

This is mainly the same as the first option, but with the eraser tool in mind.

Open your empty canvas in paint. Middle click a random color to see the division. Zoom in real close, select the smallest brush and draw crop marks into the top-left and bottom-right corners.

pic

You can change the background color to whatever you want now.

After you're done drawing, minimize the extra space with vcrop and use crop mathematics.

3.6 Faux layers: Slight Return

Acquire pico:

% git/clone https://github.com/qwx9/pico

It's a script you run. It is an incredibly powerful tool for manipulating images. The following is just a nail clipping of all it can do. Learn more about it: nopenopenope.net/posts/pico

In this example, I am using the color HotPink (#ff5fd7) as the sketching layer. I drew over it with black. The finished picture is called pink_layer.pic.

pic

Get the decimal values of 0xff 0x5f 0xd7 with pc(1):

term% pc
; dec(0xff)
255
; dec(0x5f)
95
; dec(0xd7)
215

Replace HotPink layer with background color (white):

#the formula
RR, GG, BB = decimal values of the color to replace
OR, OG, OB = decimal values of the target colorj
(for white you can use either 255,255,255 or Z,Z,Z)
(im[x,y,0] == RR && im[x,y,1] == GG && im[x,y,2] == BB) ? (z == 0 ? OR : z == 1 ? OG : z == 2 ? OB : Z) : im
#example
term% pico
!r /usr/glenda/pink_layer.pic im
(im[x,y,0] == 255 && im[x,y,1] == 95 && im[x,y,2] == 215) ? (z == 0 ? Z : z == 1 ? Z : z == 2 ? Z : Z) : im

page(1) opens with the edited picture. Middle mouse, write, enter the file name you want to save it to.

pic

Replace every color that is not white with black:

#the formula
RR, GG, BB = decimal values of the color not to replace
OR, OG, OB = decimal values of the color to replace everything else with
(im[x,y,0] != RR || im[x,y,1] != GG || im[x,y,2] != BB) ? (z == 0 ? OR : z == 1 ? OG : z == 2 ? OB : Z) : im
#example
term% pico
!r /usr/glenda/pink_layer.pic im
(im[x,y,0] != Z || im[x,y,1] != Z || im[x,y,2] != Z) ? (z == 0 ? 0 : z == 1 ? 0 : z == 2 ? 0 : Z) : im

pic

Replace every color that is not black with blue:

(im[x,y,0] != 0 || im[x,y,1] != 0 || im[x,y,2] != 0) ? (z == 0 ? 120 : z == 1 ? 0 : z == 2 ? 215 : Z) : im

pic

3.7 Tracers Work Both Ways

Would you like to trace any picture on the internet? You can. This example again makes use of pico.

Procure an image file, convert it to 9front format.

% hget http://img.triapul.cz/fahwui.jpg | jpg -c9 > to.trace.pic
% pico
!r to.trace.pic im
z == 3 ? Z : (im + Z) / 2

page(1) opens - write to to.trace.bright.pic.

Open to.trace.bright.pic in paint(1). Right click one of the default colors and put in HotPink: ff5fd7.

% paint to.trace.bright.pic 

Draw over the picture. Save it.

pic

% pico
!r to.trace.bright.pic im
(im[x,y,0] != 255 || im[x,y,1] != 95 || im[x,y,2] != 215) ? (z == 0 ? Z : z == 1 ? Z : z == 2 ? Z : Z) : im

page(1) opens - write to whatever.pic

pic

3.8 Palettes

Examine git.sr.ht/~amavect/makeu

4 Get it out!

Now you want to show your drawing to your friends.

4.1 Conversion

Since anything you can draw in 9front is basically pixel art, there's no reason to convert your picture to anything but PNG. But nobody will stop you. See jpg(1) to explore all the other formats. PNGs will be always smaller and sharper (read: look exactly like they did in paint(1)) than JPGs.

% topng my_picture.pic > my_picture.png

That's it.

You cannot use paint(1), crop(1), vcrop(1) on PNGs (or any other format that's not in image(6)). You can still display them with page(1).

If you ever lose your original, you can convert the PNG (or any other PNG) back to 9front's format.

% png -c9 my_picture.png > my_picture.pic

Afterwards you can edit my_picture.pic again.

4.2 Stop 9front

A picture is produced, cropped, converted, but how can you post it to your social medium now? Following is a list of methods, sorted from simplest that require no further technical skills and understanding, to less simple:

  • open your picture in page(1), take a picture of your plan9 computer screen with a mobile phone
  • connect a FAT usb drive to your plan9 computer and copy the picture to /shr/sdS0M3TH1NG
  • sshfs(4) the picture to a unix computer
  • drawterm into your plan9 computer from a unix computer, copy the picture to /mnt/term/some/where (this is your unix computer's drive)
  • post it on snac with mothra(1)
  • email it to yourself from your plan9 computer
  • sshfs(4) the picture to a a virtual machine inside your plan9 computer
  • copy the picture to your plan9 webserver and download it from a different device

pic

5 EOF

Now you really know How to draw (with 9front).

5.1 Special Thanks

sl, qwx, rodri, qrstuv, aiju, cinap, noodle, Renée French, Tom Duff

pic

5.2 Resources

I don't even use plan9.

The Daily Front Page 21 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Roads and Routes
article

How we can reduce traffic congestion

by raahelb·▲ 170 points·328 comments·research.google ↗
Drivers spend an average of 2.6 years of their life on the road.

Vehicle transportation underpins much of modern life, enabling the movement of goods and people, productivity, and economic growth. However, the costs are high: drivers spend an average of 2.6 years of their life on the road, and private cars and vans now account for around 10% of global CO2 emissions. Hence, the efficient use of transportation networks is of paramount importance. Can road traffic routing be managed system-wide the way aviation manages airspace or the internet routes data packets? While ground transportation has historically lacked a physical control tower, digital platforms offer a powerful glimpse into a more coordinated future.

The proliferation of navigation services, connected vehicles, smart cities, and autonomous vehicles all provide opportunities to improve both measurement and optimization of transportation resources. Google Research has already demonstrated the power of infrastructure-level intervention with Project Green Light, which uses AI to optimize city traffic lights. Unfortunately, optimizing vehicle networks has proven challenging. While individual vehicle routing is standard across all the top navigation products, optimizing routing system-wide is not yet present. Although theoretical models for network optimization exist, large-scale empirical validation remains limited, thereby hindering forward progress.

In “Urban congestion relief experiments through routing-app interventions”, published in Nature Cities, we present the first large-scale, real-world study into the use of navigation platforms to improve traffic. We show that coordinating even a small fraction of trips to disperse traffic can measurably improve driving speeds and reduce emissions for the entire city. It also establishes an experimentation framework for evolving from individual trip optimization toward a cooperative routing paradigm that enhances total network efficiency.

Experiment

We ran an experiment in 10 major US cities to demonstrate the effectiveness of targeted low-cost routing interventions in improving overall traffic conditions. For this study, the Google Maps algorithm was modified to prefer alternative routes with similar travel times and segment types, effectively guiding trips away from the pre-selected congested segments.

Over a six month period, we adopted a city-wide switchback (also known as crossover) experimental design, alternating between this treatment and the control (unaltered) routing algorithm over consecutive days to appropriately measure the effect of this intervention. Rather than randomly selecting individual trips, the intervention was applied systematically across the entire city. During “treatment” days, the modified routing guided all trips that encountered the pre-selected congested segments toward alternative routes with similar travel times. Under 2% of observed trips received altered routing recommendations as a result of this experiment.

To set up the experiment, cities were chosen based on the congestion levels and ground truth availability. For each city, we selected roughly 100 road segments based on historical congestion patterns, characterized by recurring bottlenecks or high traffic density during peak demand. The figure below shows one such example.

NetworkAwareRouting1_StudyOverview

Within this study, we modify at the routing stage the perceived cost to trips passing through pre-selected segments depicting disproportionately high levels of demand and/or congestion. These modifications reroute trips with similarly costing alternative paths away from these segments, thereby reducing the flow of traffic that would have otherwise been experienced within them.

Results

To quantify the effect of our proposed routing intervention, we employed a hierarchical Bayesian outcome modeling framework for our analysis. This approach, which models parameters at both the aggregate city level and localized hourly level simultaneously, offers a flexible way to capture shared variations without imposing strict constraints. It also enables information sharing between cities and time periods, allowing estimates for a particular city or time to borrow strength from other subgroups' effect estimates.

The study found that even these small interventions led to measurable, statistically significant improvements in traffic conditions. Averaged across cities, we observe a median increase of around 2% in driving speeds on targeted segments, corresponding to a median decrease of 0.5% to 1.0% in fuel consumption rates. Over the much larger set of affected segments, i.e., all segments that were impacted by the intervention, including those to which traffic was redirected either away from or onto, driving speeds increased by around 0.35% on median, and 0.5% when traffic is highest in the morning and afternoon. At the scale and energy demands of the cities considered in this study, this translates to potential savings of thousands of tons of CO2e emissions per city per year.

NetworkAwareRouting2_Results

Estimated outcomes on trip travel times, speeds, and estimated emissions. Each plot quantifies the posterior probability distribution of outcomes from the intervention, and is represented in terms of percentage changes in speeds or emission rates. We see notable improvements to both targeted and affected segment speeds and fuel consumption rates. Outcomes when computed across all affected segments are understandably more diffuse but still positive, particularly during peak hours.

Improvements in driving speeds and emission rates were both prevalent and statistically significant across the network. These gains were the result of the strategic diversion of vehicles from major bottlenecks; by dispersing this traffic efficiently, the peripheral roads maintained higher average speeds and lower overall emissions, even when absorbing higher volumes of vehicles. This behavior is illustrated in the figure below.

NetworkAwareRouting3_Demonstration

A demonstration of the dispersion of traffic in Atlanta induced by the treatment. Top: Routes on aggregate were diverted away from (blue) the central highway passing through the city and onto (green) a spatially more distributed set of segments primarily spanning the periphery of the region. Bottom: Histogram of net volume changes on individual segments. Indeed, vehicles were dispersed from concentrating on a smaller number of high volume segments to a larger number of segments that each received lower volume increases, resulting in a net benefit to the system.

Conclusion

This research clearly shows that networked navigation technology can be a powerful tool for proactively shaping traffic flow for the benefit of society. By coordinating a small fraction of trips, we can achieve systemic gains that benefit all road users — not just those using a specific app. Notably, both navigation users and non-users share the advantages of decongesting targeted segments, leading to network-wide improvements in travel time and a reduction in CO2e emissions.

Beyond immediate congestion relief, this work establishes a blueprint for a rigorous, experiment-based approach to traffic management. As smart-city infrastructure matures, the experimental pathway demonstrated here — using connectivity to measure and facilitate system-level changes — can be applied to broader challenges like dynamic signal control and real-time network optimization in complex urban environments. While these results show the potential of relatively simple rerouting, they provide the foundation for a future where cars, infrastructure, and network-aware routing work together to optimize travel efficiency and sustainability for the entire community.

Acknowledgements

This work was conducted in collaboration with Alexandre Bayen, Andrew Tomkins, Theophile Cabannes, Kevin Chen, Yechen Li, Marc Nunkesser, Prem Ramaswami, Eray Turkel, Shoshana Vasserman, and Haizheng Zhang.

The Daily Front Page 22 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — End-of-Life Ledger
article

How Doctors die. It’s not like the rest of us (2016)

by downbad_·▲ 253 points·156 comments·archive.cancerworld.net ↗
It’s not like the rest of us, but it should be.

This hard-hitting blogpost by Ken Murray, a retired Los Angeles family doctor, helped open up discussions about why doctors routinely administer treatments to dying patients that they would adamantly refuse for themselves.

Years ago, Charlie, a highly respected orthopaedist and a mentor of mine, found a lump in his stomach. He had a surgeon explore the area, and the diagnosis was pancreatic cancer. This surgeon was one of the best in the country. He had even invented a new procedure for this exact cancer that could triple a patient’s five-year-survival odds – from 5% to 15% – albeit with a poor quality of life.

Charlie was uninterested. He went home the next day, closed his practice, and never set foot in a hospital again. He focused on spending time with family and feeling as good as possible. Several months later, he died at home. He got no chemotherapy, radiation, or surgical treatment. Medicare didn’t spend much on him.

It’s not a frequent topic of discussion, but doctors die, too. And they don’t die like the rest of us. What’s unusual about them is not how much treatment they get compared to most Americans, but how little. For all the time they spend fending off the deaths of others, they tend to be fairly serene when faced with death themselves. They know exactly what is going to happen, they know the choices, and they generally have access to any sort of medical care they could want. But they go gently.

Of course, doctors don’t want to die; they want to live. But they know enough about modern medicine to know its limits. And they know enough about death to know what all people fear most: dying in pain, and dying alone. They’ve talked about this with their families. They want to be sure, when the time comes, that no heroic measures will happen – that they will never experience, during their last moments on earth, someone breaking their ribs in an attempt to resuscitate them with cardiopulmonary resuscitation (that’s what happens if CPR is done right).

Almost all medical professionals have seen what we call “futile care” being performed on people. That’s when doctors bring the cutting edge of technology to bear on a grievously ill person near the end of life. The patient will get cut open, perforated with tubes, hooked up to machines, and assaulted with drugs.

img-focus71

All of this occurs in the Intensive Care Unit at a cost of tens of thousands of dollars a day. What it buys is misery we would not inflict on a terrorist. I cannot count the number of times fellow physicians have told me, in words that vary only slightly, “Promise me if you find me like this that you’ll kill me.” They mean it. Some medical personnel wear medallions stamped “NO CODE” to tell physicians not to perform CPR on them. I have even seen it as a tattoo.

To administer medical care that makes people suffer is anguishing. Physicians are trained to gather information without revealing any of their own feelings, but in private, among fellow doctors, they’ll vent. “How can anyone do that to their family members?” they’ll ask. I suspect it’s one reason physicians have higher rates of alcohol abuse and depression than professionals in most other fields.

I know it’s one reason I stopped participating in hospital care for the last 10 years of my practice. How has it come to this – that doctors administer so much care that they wouldn’t want for themselves? The simple, or not-so-simple, answer is this: patients, doctors, and the system.

To see how patients play a role, imagine a scenario in which someone has lost consciousness and been admitted to an emergency room. As is so often the case, no one has made a plan for this situation, and shocked and scared family members find themselves caught up in a maze of choices. They’re overwhelmed. When doctors ask if they want “everything” done, they answer yes. Then the nightmare begins. Sometimes, a family really means “do everything,” but often they just mean “do everything that’s reasonable.” The problem is that they may not know what’s reasonable, nor, in their confusion and sorrow, will they ask about it or hear what a physician may be telling them. For their part, doctors told to do “everything” will do it, whether it is reasonable or not.

The above scenario is a common one. Feeding into the problem are unrealistic expectations of what doctors can accomplish. Many people think of CPR as a reliable lifesaver when, in fact, the results are usually poor. I’ve had hundreds of people brought to me in the emergency room after getting CPR. Exactly one, a healthy man who’d had no heart troubles (for those who want specifics, he had a ‘tension pneumothorax’), walked out of the hospital.

If a patient suffers from severe illness, old age, or a terminal disease, the odds of a good outcome from CPR are infinitesimal, while the odds of suffering are overwhelming. Poor knowledge and misguided expectations lead to a lot of bad decisions.

But of course it’s not just patients making these things happen. Doctors play an enabling role, too. The trouble is that even doctors who hate to administer futile care must find a way to address the wishes of patients and families. Imagine, once again, the emergency room with those grieving, possibly hysterical, family members. They do not know the doctor. Establishing trust and confidence under such circumstances is a very delicate thing. People are prepared to think the doctor is acting out of base motives, trying to save time, or money, or effort, especially if the doctor is advising against further treatment.

Some doctors are stronger communicators than others, and some doctors are more adamant, but the pressures they all face are similar. When I faced circumstances involving end-of-life choices, I adopted the approach of laying out only the options that I thought were reasonable (as I would in any situation) as early in the process as possible. When patients or families brought up unreasonable choices, I would discuss the issue in layman’s terms that portrayed the downsides clearly. If patients or families still insisted on treatments I considered pointless or harmful, I would offer to transfer their care to another doctor or hospital.
Should I have been more forceful at times? I know that some of those transfers still haunt me. One of the patients of whom I was most fond was an attorney from a famous political family. She had severe diabetes and terrible circulation, and, at one point, she developed a painful sore on her foot. Knowing the hazards of hospitals, I did everything I could to keep her from resorting to surgery. Still, she sought out outside experts with whom I had no relationship.

Not knowing as much about her as I did, they decided to perform bypass surgery on her chronically clogged blood vessels in both legs. This didn’t restore her circulation, and the surgical wounds wouldn’t heal. Her feet became gangrenous, and she endured bilateral leg amputations. Two weeks later, in the famous medical center in which all this had occurred, she died.

It’s easy to find fault with both doctors and patients in such stories, but in many ways all the parties are simply victims of a larger system that encourages excessive treatment. In some unfortunate cases, doctors use the fee-for-service model to do everything they can, no matter how pointless, to make money. More commonly, though, doctors are fearful of litigation and do whatever they’re asked, with little feedback, to avoid getting in trouble.

Even when the right preparations have been made, the system can still swallow people up. One of my patients was a man named Jack, a 78-year-old who had been ill for years and undergone about 15 major surgical procedures. He explained to me that he never, under any circumstances, wanted to be placed on life support machines again.

One Saturday, however, Jack suffered a massive stroke and got admitted to the emergency room unconscious, without his wife. Doctors did everything possible to resuscitate him and put him on life support in the ICU. This was Jack’s worst nightmare. When I arrived at the hospital and took over Jack’s care, I spoke to his wife and to hospital staff, bringing in my office notes with his care preferences. Then I turned off the life support machines and sat with him. He died two hours later.

Even with all his wishes documented, Jack hadn’t died as he’d hoped. The system had intervened. One of the nurses, I later found out, even reported my unplugging of Jack to the authorities as a possible homicide. Nothing came of it, of course; Jack’s wishes had been spelled out explicitly, and he’d left the paperwork to prove it.

But the prospect of a police investigation is terrifying for any physician. I could far more easily have left Jack on life support against his stated wishes, prolonging his life, and his suffering, a few more weeks. I would even have made a little more money, and Medicare would have ended up with an additional $500,000 bill. It’s no wonder many doctors err on the side of overtreatment.

But doctors still don’t over-treat themselves. They see the consequences of this constantly. Almost anyone can find a way to die in peace at home, and pain can be managed better than ever.

Hospice care, which focuses on providing terminally ill patients with comfort and dignity rather than on futile cures, provides most people with much better final days.

Amazingly, studies have found that people placed in hospice care often live longer than people with the same disease who are seeking active cures. I was struck to hear on the radio recently that the famous reporter Tom Wicker had “died peacefully at home, surrounded by his family.” Such stories are, thankfully, increasingly common.

Several years ago, my older cousin Torch (born at home by the light of a flashlight – or torch) had a seizure that turned out to be the result of lung cancer that had gone to his brain. I arranged for him to see various specialists, and we learned that with aggressive treatment of his condition, including three to five hospital visits a week for chemotherapy, he would live perhaps four months.

Ultimately, Torch decided against any treatment and simply took pills for brain swelling. He moved in with me.
We spent the next eight months doing a bunch of things that he enjoyed, having fun together like we hadn’t had in decades. We went to Disneyland, his first time. We’d hang out at home. Torch was a sports nut, and he was very happy to watch sports and eat my cooking. He even gained a bit of weight, eating his favorite foods rather than hospital foods. He had no serious pain, and he remained high-spirited.

One day, he didn’t wake up. He spent the next three days in a coma-like sleep and then died. The cost of his medical care for those eight months, for the one drug he was taking, was about $20.

Torch was no doctor, but he knew he wanted a life of quality, not just quantity. Don’t most of us? If there is a state of the art of end-of-life care, it is this: death with dignity. As for me, my physician has my choices. They were easy to make, as they are for most physicians. There will be no heroics, and I will go gentle into that good night. Like my mentor Charlie. Like my cousin Torch. Like my fellow doctors.

This blogpost was first published in 2011 on Zócalo Public Square (http://www.zocalopublicsquare.org/), a not-for-profit Ideas Exchange affiliated to Arizona State University. It is republished here with permission .
Ken Murray is a retired family doctor and was Clinical Assistant Professor of Family Medicine at the University of South Carolina.

The Daily Front Page 23 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Schools on Trial
article

Under federal rule, colleges must leave grads better off or lose financial aid

by nradov·▲ 198 points·532 comments·npr.org ↗
If a program cannot show that it leaves its graduates financially better off.

This illustration shows people in various styles of clothing running across a finish line.

This month, the U.S. Department of Education began rolling out a new accountability test that most colleges and universities will soon have to pass.

The test itself is simple: If an undergraduate program's graduates don't earn more than workers who never went to college, that program could be cut off from federal student loans. The same goes for any graduate program whose graduates earn less than someone with only a bachelor's degree.

"If a program cannot show that it leaves its graduates financially better off than if they had never enrolled, it should not be underwritten by federal taxpayers," said Under Secretary of Education Nicholas Kent in a recent statement.

But this new test, known as "do no harm," raises some thorny questions about the purpose of college. Like: Is it just about making more money?

Some advocates for postsecondary arts education think not.

"Earnings is only a small piece of that puzzle," said Lee Ann Scotto Adams, executive director of the Strategic National Arts Alumni Project (SNAAP), a nonprofit that studies the careers of arts graduates.

She and Doug Dempster, the president of SNAAP, worry the new test might lead colleges and universities to preemptively slash low-earning creative arts programs in music, theater, studio art and design. Dempster says that could lead to a further devaluing of jobs that are critical to a well-functioning society.

"We know we need nurses. We know we need journalists. We know we need early childhood educators," he said. "We don't know how many artists we need, but I can guarantee that if you eliminate access, we will impoverish our cultural life nationally."

How the new standard will work

The new earnings test comes courtesy of last year's One Big Beautiful Bill Act, which included a slew of big higher education policy changes meant to address rising concerns over the cost and value of college.

Higher education experts across the political spectrum told NPR the test sets a pretty reasonable expectation: In many states, federal data shows, graduates of bachelor programs will have to earn a minimum of about $30,000 to $41,000 a year for their program to pass.

"This is really a very low floor," said Christopher Madaio, a senior adviser at the nonprofit The Institute for College Access & Success. "I mean, high school earnings is not an exceedingly high metric for a program to meet."

Programs fail the test when they don't meet the earnings requirement for two out of three consecutive years.

The current test does not take student loan debt into account, which means there's no way to distinguish between a graduate who is struggling with low pay while being debt-free and a graduate who is struggling with low pay while also paying off tens of thousands of dollars in loans.

The Education Department says it will begin calculating the first year of graduate earnings in early 2027, and "some programs could be designated as low-earning outcome programs beginning in the 2028-2029 [financial aid] award year."

The kinds of programs that are likely to fail

According to Education Department estimates, the vast majority of undergraduate and graduate programs should easily pass the new earnings test.

But more than 800,000 students attend a program that would likely fail the measure, according to department data. Roughly half of those students are enrolled in for-profit schools, which already have a reputation for shortchanging students.

Other takeaways from the department's data:

  • About 18% of undergraduate certificate programs, which often bill themselves as career-focused fast tracks, would fail the earnings test. Specifically, certificate programs in cosmetology and somatic body work have the highest predicted failure rates.
  • Two-year associate degree programs have the next highest failure rate, at 6%. Associate programs that train specialized educators, including early childhood educators, are the most likely to fail.
  • Most traditional, four-year bachelor programs fare well, with roughly 1% failing the earnings test. When these programs do fail, it's often in areas like theater, music and studio art.
  • About 4% of master's degree programs would fail, with the highest failure rates for programs teaching mental and social health services.

For one music teacher, it was "never about the money"

Some of the United States' most prestigious music programs — known for training the country's most talented young musicians — are among the 14% of bachelor music programs predicted to fail the new earnings test, according to Education Department data. That includes The Juilliard School in New York City, the New England Conservatory in Boston and Indiana University Bloomington's Jacobs School of Music.

The undergraduate music program that Cindy Flores attended at Portland State University (PSU) also wouldn't pass. Flores teaches mariachi music to middle and high school students at Salem-Keizer Public Schools in Oregon's Willamette Valley.

Standing with a guitar-like stringed instrument, Cindy Flores smiles as she teaches a mariachi class at McKay High School in Salem, Oregon. She is surrounded by seated students who are playing stringed instruments.

Cindy Flores smiles as she teaches mariachi to students at McKay High School in Salem, Oregon. "If it wasn't for PSU and the loans I could get … I wouldn't be a Mexican American mariachi teacher for my Mexican American students," she said. Eli Imadali/OPB

Her path to becoming a full-time music teacher started with studying music education at PSU; then she got an educators license from Western Oregon University — and she used federal student loans to help pay for all of it.

She now holds close to $55,000 in federal student loan debt.

Flores said she wouldn't be where she is now without that access to federal aid.

"If it wasn't for PSU and the loans I could get … I wouldn't be a Mexican American mariachi teacher for my Mexican American students," she said.

But given the new federal test, future PSU music students might not have the same access to federal student loans that Flores did.

She said she feels lucky to have found a job that she's passionate about and that pays a living wage. But, for her, a career in music was about much more than a paycheck.

"It is never about the money," she said. "I realized I wanted to have a career in music when I was in the eighth grade, because every music teacher I had were such good role models in my life and I wanted to be part of that community."

Defining success in the arts

SNAAP's Lee Ann Scotto Adams said the federal government's one-size-fits-all accountability approach doesn't make sense for students graduating from creative arts programs because wages aren't the only measure of success for studio artists, musicians and designers.

"Yes, you need to earn money to make a living, but we see our creative workers want the ability to have independence in their work. They want jobs that are socially conscious. They want to make an impact culturally," Scotto Adams said. "These are all metrics that fall outside of just straightforward earnings metrics."

She also takes issue with looking at earnings in the first few years after graduation. Scotto Adams points to SNAAP survey data that shows arts graduates often have unpredictable incomes at the beginning of their careers, but their pay tends to stabilize and increase over time.

"Looking at earnings as the sole metric of success is very limited, and that's because artists have nonlinear careers," Scotto Adams said. "For the most part, people who graduate from these programs move into careers that they're personally satisfied with."

Students considering any of the at-risk programs won't immediately lose access to federal aid. While the accountability test is being rolled out this month, its implementation will be phased in over the next couple of years.

The Daily Front Page 24 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Undersea Wonders
article

Jellyfish Undersea Roundabout

by hydrogen7800·▲ 75 points·30 comments·visitfaroeislands.com ↗
This is no ordinary roundabout.

THE NEW GIANT UNDERWATER “JELLYFISH” ROUNDABOUT HAS BECOME THE LATEST FAROE ISLANDS TOURIST ATTRACTION

This is no ordinary roundabout. It is part of a marvel of engineering containing the world’s first undersea roundabout, with an illuminated, sea blue central pillar decorated by Faroese artist Tróndur Patursson.

Already a hit on social-media, the colorful tunnel network linking the two largest islands in the Faroe Islands has become a big draw for foreign visitors.

The 11.2 km (6.9 miles) long subsea tunnel network links the cosmopolitan capital of Torshavn with the second most populous island and has cut down traveling time between these two places from more than one hour driving time along the islands mountainous roads to only 15 minutes via the new subsea tunnel.

The subsea tunnel network named Eysturoyartunnil opened on 19 December 2020, after only 4 years of construction, and is expected to transform the Faroese society, connect people, regions, and economies in new ways.

In the heart of this, 72 meters below the surface, is the worlds first under water roundabout.

In the centre of this roundabout is a giant central pillar of natural rock, left behind during the blasting constructions. This giant pillar is now illuminated and decorated by a prominent Faroese artist, Tróndur Patursson. Around the pillar is an 80-metre steel sculpture represents figures in full size holding hands around the roundabout. They stare inwards at the light like worshippers around a volcanic fire. “The figures are walking from darkness into the light,” says Patursson, “And they symbolise the very Faroese idea that by joining hands and working together we achieve great things”, the Faroese artist said to the Guardian

  • This giant pillar in the center of the roundabout is illuminated and decorated with an 80-metre steel sculpture represents figures in full size holding hands around the roundabout.ese artist, Tróndur Patursson. Photo by Ólavur Frederiksen

  • 77 year old Tróndur Patursson, the Faroese artist behind the tunnel decorations, here photographed with Teitur Samuelsen, CEO of Eysturoyar tunnel holding the photo lamps. Photo by Bardur Eklund

  • Is it a underwater roundabout, jellyfish or a alien UFO spaceship? Photo by Bardur Eklund

  • When motorists enter the tunnel, they experience a stylish entrance with harmonious arches and are later met by the roundabout, which is bathed in light so that it appears as a modern art installation. Photo by Olavur Frederiksen

  • The 11.2 Km long subsea tunnel is the second longest in the world. Photo by Ólavur Frederiksen

  • The entrance to the tunnel at the Runavík side. Photo by Ólavur Frederiksen

The new subsea tunnel also connects the two sides of the Skálafjørð bay area and cuts down the driving distance from 25 km to 5 km. Photo by Ólavur Frederiksen

The singled tube two-laned undersea tunnel, which takes around 8 minutes to drive through, is 189 meters (620 ft) below the water´s surface at the lowest point, and has three tubes which meet at the colourful roundabout.

One of the tubes connects with Tórshavn, the capital, on the island of Streymoy- to the left on the picture above. The other two tubes connect with the two bay areas of Skálafjørður fjord on the island of Eysturoy, on the right on the picture, helping to cut down travel distance between these two bay areas from 25 km down to just 5 km.

Additionally the tunnel helps shortening the driving time between the Faroe Islands´ two largest cities, Tórshavn and Klaksvik, will half. From 70 minutes to around 35 minutes.

Under the road, an advanced rain and wastewater system of pumps and kilometres of pipes ensures that rain and surface water are kept away from technical installations and the carriageway, so that road users can safely and safely enjoy the tunnel’s harmonious expression.

While the road users’ gaze at the beautiful tunnel decorations, gravity ensures that the water is led away from the cars and the carriageway via gratings and pipes and down to the lowest point in the tunnel, where large 16 bar pumps send the water back to the surface and out into Skálafjørður fjord.

The driving experience through Eysturoyartunnil, the second longest car traffic subsea tunnel in the world, can be enriched even more by tuning in on FM radio 97.00 while driving through the colourful tunnel.

On that channel you can listen to the Faroese musician and sound-engineers special tunes composed for your ride under the seabed.

At FM radio 97.00 you can listen to special music tunes composed by Faroese Jens L. Thomsen, while driving through the Eysturoyar tunnel. Photo taken at the entrance of the tunnel by Bardur Eklund

The whole Eysturoyar tunnel project was constructed by the Faroese public limited company Eystur- og Sandoyartunlar and was the largest single investment ever made in the Faroe Islands, totalling at around 260m Euros.

Driving through Eysturoyartunnil costs a toll fee of 75 Danish krona (around 10 Euros) each way and is billed automatically as a camera scans your license plate. Without a yearly subscription the toll fee is though 175 Dkk (24 Euro) each way.

The income from this new tunnel is expected to fund the next tunnel projects on the Faroe Islands.

Construction workers celebrating the breakthrough in the Eysturoy Tunnel on 7 June, 2019. Photo by Photo by Ólavur Frederiksen.

Crazy about tunnels

Faroese build tunnels like no other. We love them. The 18 islands making out the Faroe Islands has 53,000 inhabitants and now 20 tunnels, of which three are long tunnels under the sea. Two more are already under construction, and another 14 are on the drawing board or wish list.

Map showing the current 20 tunnels in the Faroe Islands (including the 3 subsea tunnels) and the many planned tunnels.

The biggest of the future tunnel projects will be the tunnel from the southmost island of the Faroe Islands, Suðuroy, to the island of Sandoy. The tunnel is expected to cost around 3.4 billion DKK ( 450 million Euro) and will be around 22-24 kilometres (13-15 miles) long, making it the longest car traffic subsea tunnel in the world.

The Daily Front Page 25 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Briefs: Hype and Understanding
article

I love LLMs, I hate hype

by therepanic·▲ 477 points·312 comments·geohot.github.io ↗

I think from this blog you may misunderestimate how absolutely giddy I am about AI. I did hacking from 2007-2014, after that my whole career has been devoted to AI. I love the progress. I’m so excited for the new LLMs, self driving cars, video generation models, and coding agents. I set up a Linux box with opencode on my local GLM-5.2 last week and wow like just saying install tmux with the geohot configuration works; the Year of the Linux Desktop is finally here!


What I don’t like is two things. One, this constant bullshit about some window closing, or the perpetual underclass, or falling hopelessly behind. This is negative valence hype, not only is it not true, it’s mostly designed to make you feel bad about yourself and move to shitty San Francisco where everything really does suck like how these people claim.

And two, this strawman jump from, oh hey, it’s a fancy autocomplete, smart compiler, better search engine, to it’s gonna like own the whole light cone bro like if you aren’t in SF and at the right parties there’s gonna be like a flash of light in the sky one day and you’re not even gonna know what happened but everything just Changed. I’ll bet you everything I have that this doesn’t happen. The people perpetuating this are terrible people, but the justice is that this is how they feel inside all the time themselves.


Here’s a cool presentation from 2016 about superintelligence. Here’s a movie from 1991 about machines taking over the world. A certain cult likes to claim credit for things that are happening with or without them, and this is my main argument against the valuation of frontier labs. It’s not that AI won’t create that much value, it’s that they won’t capture it.

They try to dress it up with some high minded safety or China bullshit, but the core of the anti open source arguments is a fear of commodification. AI is something that’s happening mostly due to Moore’s law and general progress in computing, not something that they are doing. Of course they have a strong incentive against you finding this out, because then you might not want to give them billions of dollars.


I might have been a little harsh in The Eternal Sloptember about models not being able to program. What’s really happening is that programming is changing. Can compilers program? Here’s a Linus Torvalds quote about how agents make programming 10x more productive, but compilers make programming 1000x more productive. I think 10x and 1000x are extreme estimates, but I’m now pretty confident I’m getting better at using them and get some boost from the models. It is a new skill, and it’s not like I haven’t constantly been trying them. You have to be really careful, they can increase cognitive fatigue, and all the vibe coded stuff is still slop (where’s all this new magical software that the productivity improvements should imply?). But models are useful just like find replace, stack overflow, or all the regexes I never learned how to write and now never will!

AI is the continuation of the computer revolution. I love computers so much.

article

Automation Without Understanding

by root-parent·▲ 138 points·57 comments·arxiv.org ↗

Abstract:Two developments are unfolding at once: artificial intelligence systems have begun to produce genuine research-level mathematics, and the United States is weakening the pipeline that produces humans capable of understanding what such systems are doing. This essay argues that, taken together, these developments amount to a strategic error. Mathematical capacity, which is the trained ability to verify, interpret, and challenge mathematical reasoning, is not a byproduct of theorem production but a form of infrastructure, built over generations by institutions that cannot be reconstituted on demand. Drawing on the May 2026 AI disproof of a longstanding Erdős conjecture on the planar unit distance problem and on recent disruptions to federal support for the mathematical sciences, the essay makes the case for treating mathematical capacity as a strategic asset on a par with semiconductor capability. It further proposes, among other measures, that AI systems performing consequential reasoning be required to expose their decision-critical claims in formal, machine-checkable form, converting part of AI reasoning from opaque persuasion into auditable structure.

The Daily Front Page 26 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Briefs: Odd Tools and Human Marks
article

Billions of Sketches Reveal Hidden Cultural Variation in Human Concepts

by Anon84·▲ 133 points·22 comments·arxiv.org ↗

Abstract:Claims about the universality of human concepts have been predominantly assessed through linguistic similarity across languages and cultures. However, words are effective as communication devices because they compress rich experiential variation into shared conventions, potentially obscuring hidden individual and cultural differences in how concepts are mentally represented. Here, we analyse 2.6 billion human-made sketches of common concepts from 236 countries and territories to examine conceptual structure through people's visual imagination. Consistent with recent work on image-based cognition, we find that single concepts unfold into multiple distinct visual exemplars, revealing latent information about similarities and differences in conceptual structure across cultures. This variation is strongest for concepts involving haptic interaction, suggesting that visual imagery reflects variation in embodied experience as much as conventional definitions. Comparing embedding models of sketches with word embedding models across languages, we find that their geometries diverge, with visual representations preserving rich semantic and cultural structure that language models compress. Cross-cultural similarities derived from sketches align 45% more closely with established cultural distances than do text-based measures. Together, these results suggest that patterns of human conceptual universality may depend critically on the modality through which concepts are measured, with large-scale sketching providing a direct, high-resolution probe of conceptual diversity across embodied and cultural dimensions of thought.

article

A dock that wakes up reliably

by ingve·▲ 98 points·65 comments·fabiensanglard.net ↗

A dock that finally wakes up reliably

For a long time, my top hardware frustration was to deal with a docked laptop that wouldn't wake up. It was sporadic but the scenario would be as follows.

  1. Arrive at my desk where the laptop is docked and asleep.
  2. Press keys on the USB keyboard. Nothing.
  3. Press button on the USB mouse. Nothing.
  4. Turn the monitor off and on again. Nothing.
  5. Take laptop off the dock.
  6. Open laptop.
  7. Use the laptop keyboard to appease the DisplayPort gods.
  8. Witness the screen coming online.
  9. Re-dock the laptop.
  10. Resume working and internally curse this darn setup.

Even the best Thunderbolt dock at the time, the CalDigit TS3, would occasionally fail to wake up the system. It was super annoying.

Thunderbolt 4 is not enough

When Thunderbolt 4 was announced in 2020, I was ecstatic. Intel's Thunderbolt 4 Announcement Press Deck specifically mentioned solving my very problem with a section stating Required PC wake from sleep when computer is connected to a Thunderbolt dock. That meant the behavior was part of the (reportedly thorough) conformance test suite.

I got the CalDigit TS4 as soon as it was released, in 2022. It was perhaps a little bit better but overall I was disappointed to still sometimes encounter a machine that would not wake up. At this point, I had pretty much given up and filed Thunderbolt's "wake from sleep" promise in the "unreliable" folder, right next to Bluetooth Pairing.

Dock + Monitor

Fast forward to 2025, when I needed to replace my aging BenQ and its dead pixels. I purchased an ASUS ROG Swift (PG27UCDM), mostly to see what the 240 Hz fuss was about ... and "wake up" became reliable.

ASUS ROG Swift 27” 4K QD-OLED Gaming Monitor (PG27UCDM) - 240Hz

Since I switched the monitor, I have never experienced a failure. The system works perfectly, flawlessly waking the laptop and bringing the desktop online in under a second.

To this day, I have no idea why it works. Maybe the old Ben-Q firmware was buggy. Maybe it simply took too long to wake up. I still have the same MacBook Pro 16" M1 Max and ThinkPad X1 Carbon Gen 9, did they receive firmware updates? Either way, I thought I'd share the details of my configuration, in case someone else is dealing with the same frustration.

The Daily Front Page 27 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — The Back Page: What Are You Working On?
ask hn

Ask HN: What Are You Working On? (July 2026)

by david927·▲ 251 points·977 comments·news.ycombinator.com ↗

What are you working on? Any new ideas that you're thinking about?

Join the discussion on Hacker News →

The Daily Front Page 28 of 29
Sunday, July 12, 2026 The Daily Front No. 3 — Colophon

That's the Front for Today

Issue No. 3 — Sunday, July 12, 2026 — went to press 2026-07-15 at 19:27 UTC.

About This Magazine

The Daily Front is a daily digital magazine assembled from the stories that reached the front page of Hacker News on Sunday, July 12, 2026. Headlines, points, and comment counts are recorded as they stood at press time. All articles remain the property of their original authors — every piece links back to its source and its discussion thread.

How It Was Made

Fetched, cleaned, and typeset by an automated pipeline. An editor model laid out the pages, chose the highlights, and briefed the cover illustrator — 30 model calls and 302k tokens in total. Set in Jacquard 12, Playfair Display, Source Serif 4, and IBM Plex Mono, all served via Google Fonts under the SIL Open Font License.

The Cover

The cover illustration was commissioned with this prompt:

A striking editorial cover illustration with no text, no logos, no numbers, no readable characters anywhere​.

Create a surreal scene inside an old grand café at closing time, rendered as a darkly humorous Art Deco noir lithograph​. At the center, a group of elegant mechanical “agents” sit at a marble table, dressed like 1930s accountants and waiters, feverishly feeding endless paper receipts into a glowing brass adding machine. The receipts spill across the floor like rivers of ink, forming tangled network cables and browser-shaped shadows.

Near the window, an elderly dignified figure in a soft suit and round glasses quietly hangs up a constellation-patterned coat, symbolizing the father of the internet retiring. Outside the rain-streaked glass, the city is built from telephone poles, undersea cables, and glowing routers.

In the foreground, a browser window is personified as a nervous masked detective examining its own reflection in a spoon; the reflection reveals tiny fingerprint-like ripples in mathematical wave patterns. Nearby, a sleek black terminal device whispers through a transparent wire into a distant unseen machine, suggesting hidden telemetry.

Art style and medium: vintage Art Deco noir poster meets hand-pulled stone lithography​, with exaggerated shadows, metallic gold ink, smoky gradients, fine stippled texture, and elegant geometric composition. Palette of deep black, oxblood red, antique cream, tarnished brass, midnight blue, and faint electric green. Sophisticated, mysterious, witty, highly aesthetic, cinematic, intellectual, and slightly ominous. Lots of negative space, magazine-cover quality, gallery-worthy composition.

Absolutely no text, no letters, no numerals, no captions, no UI labels, no readable marks anywhere in the image.

Production Ledger

StageModelCallsTokens InTokens Out
extractgpt-5.5 28 185,132 87,312
layoutgpt-5.5 1 19,171 5,050
covergpt-image-2 1 184 5,488

The Publisher

Published by Johnny.

Support the Press

If The Daily Front brightens your morning, consider supporting its publisher.

Credits & Contact

All content — articles, posts, comments, and the images within them — belongs to its original authors and is reproduced here to point readers back to the source. Full credit goes to those creators; every item links to its original and its Hacker News discussion.

If you are an author and would like your content removed from an issue, write to hi@johnnys.page and it will be taken down.

Feedback is always welcome at the same address: hi@johnnys.page.

Credit where credit is due.

Every page of this issue began as someone else's work — these are the original sources, linked in full.

  1. Claude Code sends 33k tokens before reading the prompt; OpenCode sends 7k by systima — systima.ai·HN discussion ↗
  2. Vint Cerf, “father of the Internet”, is retiring by compiler-guy — techcrunch.com·HN discussion ↗
  3. What xAI's Grok build CLI sends to xAI: A wire-level analysis by jhoho — gist.github.com·HN discussion ↗
  4. Migrating a production AI agent to GPT-5.6: 2.2x faster, 27% cheaper by brryant — ploy.ai·HN discussion ↗
  5. Mesh LLM: distributed AI computing on iroh by tionis — iroh.computer·HN discussion ↗
  6. Old and new apps, via modern coding agents by subset — terrytao.wordpress.com·HN discussion ↗
  7. Why write code in 2026 by softwaredoug — softwaredoug.com·HN discussion ↗
  8. An agent in 100 lines of Lisp by jamiebeach — thebeach.dev·HN discussion ↗
  9. Show HN: Mindwalk – Replay coding-agent sessions on a 3D map of your codebase by cosmtrek — github.com·HN discussion ↗
  10. Since Chromium 148, Math.tanh is now fingerprintable to link underlying OS by joahnn_s — scrapfly.dev·HN discussion ↗
  11. Ghostel.el: Terminal emulator powered by libghostty by signa11 — dakra.github.io·HN discussion ↗
  12. Protobuf-py: Protobuf for Python, without compromises by ming13 — buf.build·HN discussion ↗
  13. How to read more books by silcoon — scotto.me·HN discussion ↗
  14. I Learned to Read Again by georgex7 — substack.magazinenongrata.com·HN discussion ↗
  15. So you want to learn physics (second edition, 2021) by azhenley — susanrigetti.com·HN discussion ↗
  16. Against Usefulness by supo — motivenotes.ai·HN discussion ↗
  17. LARP – Revenue infrastructure for serious founders by BerislavLopac — larp.website·HN discussion ↗
  18. Kode Dot Programmable pocket device for makers, pentesters and geeks by iNic — kode.diy·HN discussion ↗
  19. I Did Not Kill Stanley Lieber: How to Draw (With 9front) by c-c-c-c-c — triapul.cz·HN discussion ↗
  20. How we can reduce traffic congestion by raahelb — research.google·HN discussion ↗
  21. How Doctors die. It’s not like the rest of us (2016) by downbad_ — archive.cancerworld.net·HN discussion ↗
  22. Under federal rule, colleges must leave grads better off or lose financial aid by nradov — npr.org·HN discussion ↗
  23. Jellyfish Undersea Roundabout by hydrogen7800 — visitfaroeislands.com·HN discussion ↗
  24. I love LLMs, I hate hype by therepanic — geohot.github.io·HN discussion ↗
  25. Automation Without Understanding by root-parent — arxiv.org·HN discussion ↗
  26. Mechanistic interpretability researchers applying causality theory to LLMs by adunk — cacm.acm.org·HN discussion ↗
  27. Tiny Emulators by naves — floooh.github.io·HN discussion ↗
  28. Billions of Sketches Reveal Hidden Cultural Variation in Human Concepts by Anon84 — arxiv.org·HN discussion ↗
  29. A dock that wakes up reliably by ingve — fabiensanglard.net·HN discussion ↗
  30. Ask HN: What Are You Working On? (July 2026) by david927 — news.ycombinator.com·HN discussion ↗

Browse all issues in the archive →