Cover illustration

TheDaily Front

Issue No. #260905 Saturday, September 5 2026 #260905 — SATURDAY, SEPTEMBER 5, 2026
Automation ascends, Europe looks inward, and the old machines keep humming.
Saturday, September 5, 2026 The Daily Front No. #260905 — Contents
30stories
6,196points
3,268comments
255kllm tokens
Assembled with 32 model calls — 171,020 tokens read, 83,487 written.

Highlights

Private German rocket makes history, reaches orbit from European soil

Isar Aerospace’s Spectrum becomes the first rocket to reach orbit from Western European soil.

AI handles incidents, engineers lose touch with their systems

A veteran SRE warns that self-healing systems may leave their human operators unable to respond when automation fails.

Netherlands pulls gold out of the US

The Netherlands moves bullion from North America amid mounting concern over geopolitical resilience.

Global warming will exceed 1.5-degree limit, UN says

A UN assessment concedes the 1.5°C threshold will be exceeded and turns attention to the difficult road back.

The "$60 Gaming PC" – AMD BC-250 (2025)

A discarded mining board built around a cut-down PS5 APU offers an unlikely path to bargain gaming.

From the Editor

Europe supplied the day’s clearest drumbeat: a rocket rose from Norway, code hosting promised sovereignty, and Dutch gold headed nearer home. Meanwhile, the machine mind continues to take on more work, prompting the perennial editor’s question: who will know how the thing works when the bell rings at midnight?

  1. AI handles incidents, engineers lose touch with their systems3
  2. The "$60 Gaming PC" – AMD BC-250 (2025)4
  3. Private German rocket makes history, reaches orbit from European soil5
  4. Git hosting that never leaves Europe6
  5. Artificial Analysis Intelligence Index v4.27
  6. Portal by Spotify cut my Claude Code token usage by 90%8
  7. Can guitar frets perform multiplication?9
  8. Git Submodules as a Package Manager10
  9. Visualizing Rust's Vtables: How dyn Trait Works In Memory11
  10. Netherlands pulls gold out of the US12
  11. A bizarre Commodore 64 peripheral, a mime, and some pretty bad ads13
  12. Pointing at the error: compiler-style diagnostics in uutils coreutils14
  13. Stopping the Unstoppable: When an unstoppable force meets a dashpot snubber15
  14. How the Disaster of "Forever Chemicals" Was Kept Secret16
  15. Steffen's Polyhedron17
  16. Balrogg: Demonically compacting (up to 15%) lossless Vorbis/Opus recompressor18
  17. Finite time blowup for an averaged three-dimensional Navier-Stokes equation (2014)19
  18. Global warming will exceed 1.5-degree limit, UN says20
  19. A Million Falcons Went Missing. Here’s How They Were Found21
  20. GPT-6 Astra in code review: Gains, privacy, and cost22
  21. .gitignore Everything by Default23
  22. Learn Programming with OCaml24
  23. Delidded Intel I9-14900KS CT Scan24
  24. LLMs as a Cognitive Virus25
  25. Wikimedia Foundation Workers Overwhelmingly Vote to Form Union with CWA26
  26. The Real Luxuries In Life27
  27. Terpstra Keyboard27
  28. Nitter has more working instances than before the takedowns28
  29. Singapore subway (mrt) information display types28
  30. Show HN: Fly By – retro biplane flying game28
The Daily Front Page 2 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — The Automation Desk
article

AI handles incidents, engineers lose touch with their systems

by sylvainkalache·▲ 373 points·331 comments·sylvainkalache.com ↗
we are losing touch with our systems.

When I was an SRE at LinkedIn, back in 2012, I designed a system that could heal itself and learn from previous incidents. AI capabilities were nowhere near what we have today, and that remained a prototype, but this is now a reality.

These tools do it all: inspect alerts, form hypotheses, query telemetry, correlate recent deployments, and even implement the fix themselves. As much as I love to see it, I have a major concern: we are losing touch with our systems.

The better these tools become at resolving routine incidents, the less practice human responders will get. And when an ambiguous, high-severity incident comes in that automation cannot solve, responding engineers will be in trouble.

Automation leaves humans with the hardest incidents

These AI-assisted incident response tools, more commonly called “AI SREs” – a term I don’t particularly like – are fantastic in many ways. They feel especially magical when they handle a routine incident at night and you don’t have to wake up for a capacity issue.

The problem is that routine incidents are also how responders “safely” develop an intuition for how their systems behave and fail. When AI runs into a hard, never-seen-before incident it cannot solve, engineers will have to take over with less practice than they would have had before.

Human-factors researcher Lisanne Bainbridge described this paradox in her famous 1983 paper, The Ironies of Automation. She explained that automation reduces operators’ opportunities to practice routine work while leaving them responsible for new and abnormal situations. She argues that, therefore, operators need to be more skilled and receive even more training than before automation.

In the years to come, I predict that the average MTTR for most incidents will go down – thanks to AI-assisted incident response – but that the resolution time will shoot up for complex incidents because incident responders lost touch with their system and are struggling to investigate.

Aviation trains pilots for rare failures

We can look at the aviation industry for inspiration.

Plane automation handles much of the flying, but pilots remain responsible for situations that automation cannot manage: engine failures, unreliable instruments, rejected takeoffs, stalls, and other abnormal conditions.

These events are extremely rare. Modern turbine engines, for example, experience fewer than one in-flight shutdown per 100,000 engine flight hours. In other words, that is rare enough that a commercial pilot may complete an entire career without experiencing one outside a simulator.

But when a failure occurs, pilots must react quickly and correctly. For example, on TransAsia Airways Flight 235, the right engine’s propeller autofeathered shortly after takeoff. And while the aircraft was designed to continue flying on its left engine, the crew misidentified the problem. The aircraft stalled and crashed only 117 seconds after the first warning.

Airline pilots regularly return to simulators to rehearse rare emergencies. Under US FAA rules, captains must complete recurrent training or a proficiency check every six months, including scenarios such as an engine failure during takeoff.

While most software incidents do not threaten lives, that is no reason not to perfect our craft. Turns out the technology that created the issue can also help close it.

The software industry needs incident simulators

At Rootly, an incident management company where I work, we partnered with Uptime Labs to apply this idea through realistic incident simulations. Engineers take the incident commander’s seat during a simulated e-commerce outage, using observability tools while coordinating with LLM-powered stakeholders in Slack.

The result feels real. You have to investigate what’s going wrong while keeping the response organized and dealing with the CEO and customer support. You get to practice the skills that matter during an incident: making sense of incomplete information, communicating clearly, coordinating people, and actually running the response.

AI can also help preserve these skills

But what about using AI as a trainer? Responders can ask an agent to explain the steps it took, the signals it examined, and the evidence behind its diagnosis.

But explanation and observation are not substitutes for practice. You might pick up a few things from watching Serena Williams play, but you only learn tennis by getting on the court, and incident response is no different.

I spent more than half a decade of my career building a software engineering school around progressive education: learning by doing. It was in-person, but we had no teachers; students worked on projects instead of listening to lectures. When Dropbox told me graduates it hired were still too inexperienced at troubleshooting, I created projects that gave students broken infrastructure and required them to diagnose and repair it. For most hands-on skills, I believe hands-on education beats passive instruction by a lot.

Incident simulation should become part of on-call readiness

As LLMs do more of our work, engineering teams risk accumulating comprehension debt: a growing gap between how their systems work and how well responders understand them.

Engineers should regularly interact with the system they watch over, handle unfamiliar failures, practice working under pressure, and rehearse the coordination and communication required during a SEV0. Tabletop exercises and chaos engineering are nothing new, but practice has become even more important in the LLM era.

Researcher Bainbridge recommended giving operators regular hands-on control and using simulation to prevent their skills from decaying. That’s the irony of automation, the more successful it becomes, the less prepared humans may be for the moment it fails.

The Daily Front Page 3 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Salvage Silicon
article

The "$60 Gaming PC" – AMD BC-250 (2025)

by networked·▲ 324 points·97 comments·devquasar.com ↗
you can play Cyberpunk 2077 on this.

Ok it’s not exactly $60 but you can get the board itself for that amount of money on eBay if you’re lucky, but on average the price is around $70-$100 (as of November 2025) which is still very cheap considering you can play Cyberpunk 2077 on this.

What is this?

The BC-250 began life not as a gaming board, but as part of a crypto-mining rig.

The “trick” behind it: the BC-250 uses a cut-down version of the APU from a PlayStation 5 (PS5) — a repurposed SoC (system-on-chip).
Specifically: many PS5 APUs that didn’t meet console spec (“binned” or “defective” for console use) got repurposed. Instead of being discarded, these chips were re-used for mining/higher-density hardware.
The original deployment: there was a rack-mount mining “server” build from ASRock that used 12 BC-250 cards, targeted at miners — sold as a “barebone” 4U server chassis.
As mainstream GPU mining lost favor, many BC-250 units started circulating second-hand — offering hardware enthusiasts an opportunity to experiment.
“BC-250 is basically a Playstation 5 chip that didn’t make the cut, and was released as a crypto-mining board.”

Key Specs & Hardware Details

From public teardowns, benchmarks, and community experiments, BC-250 hardware specs are roughly:

  • CPU: 6 × Zen 2 cores (12 threads) — a cut-down PS5 APU.
  • GPU / iGPU: RDNA 2 architecture, 24 Compute Units (CUs) — reduced from the 36 CUs of a full PS5 APU.
  • Memory: 16 GB GDDR6 memory shared between CPU and GPU — there are no DDR4/DDR5 slots in a typical BC-250 board.
  • I/O & connectivity: Even though it looks like a “GPU card,” it works as a single-board PC — includes DisplayPort, USB (2.0/3.0), Ethernet, M.2 NVMe slot (for storage), power header (8-pin PCIe), CMOS battery, and backplate I/O shield.

Performance benchmarks & real-world behavior

  • Earlier reports suggested some instability with drivers, but recent community patches and updated drivers have improved support.
  • Community tests showed surprising results: e.g. one test rig running BC-250 reportedly ran games like Cyberpunk 2077, GTA V, Counter-Strike 2, Hitman 3 — at playable framerates (not high-end, but enough for a budget build).
  • Given enough power (cooling + PSUs) and tuning (BIOS tweaks, Linux driver config), some people report a gaming experience “close to an entry-level modern GPU.”

Here’s my real life Cyberpunk 2077 Benchmark:

How can you achieve this?

You have to get your hands dirty to achieve this — it’s not an out-of-the-box setup. You’ll need to perform both hardware and firmware modifications. Additionally, if you want it to look like the image above, that requires even more work.

Cooling

The BC-250 comes with a server-style closed finstack. It’s possible to use a blower fan to cool it, and there are even 3D-printed case designs that keep the original heatsink untouched (you’ll find these in my how-to repo linked below).

However, for good cooling performance, you’ll need to modify the finstack and open it up. This allows you to use one or two 120 mm fans for proper airflow. Be aware that this is a destructive modification — I’ve explained one method of doing it in the how-to repository.

Additionally, I refreshed the thermal pads and thermal paste for optimal cooling performance.

The unified memory

The BC-250 has 16 GB of unified memory, but by default it’s split between the CPU and GPU—typically 8 GB/8 GB (or in some cases 4 GB/12 GB). To use the device for gaming, you need to modify this and force a 512 MB allocation as VRAM. This may sound counterintuitive, but remember that this is an APU, not a traditional CPU/GPU setup.

To change these settings, the BIOS must be flashed to a custom 3.00 BIOS version that unlocks the Chipset configuration menu. This used to require an EEPROM programmer—at least according to documentation from about a year ago. Fortunately, there is now a bootable BIOS Flasher image that can handle the job much more easily.

OS

You can install Windows, but because this isn’t typical hardware, you won’t have proper graphics drivers. Most users run Arch-based Linux distributions instead. I went with Manjaro Linux, which works flawlessly. The Linux version of Steam works out of the box.

Case

You can find multiple designs online. I looked for compact cases that also enclose the PSU. I’ve built two versions: one is an ultra-portable format, and the other is more like a mini-PC.

This requires 3D printing and some DIY with the PSU.

Do you want a complete guide how to do all of this? In this repository I’ve collected everything you need

https://github.com/csabakecskemeti/amd_bc-250_how-to

The Daily Front Page 4 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — A European Launch
article

Private German rocket makes history, reaches orbit from European soil

by bookmtn·▲ 478 points·250 comments·space.com ↗
No rocket had ever reached orbit from Western European soil — until today.

"This is right now making history — making history for Spectrum, making history for Isar, making history for Europe."

A rocket launch carves an orange arc into a dark night sky in this long-exposure photo.

Isar Aerospace's second Spectrum rocket launches from Andoya Space Center in Norway on Sept. 5, 2026. (Image credit: Isar Aerospace)

No rocket had ever reached orbit from Western European soil — until today.

The German company Isar Aerospace launched its Spectrum rocket from Andøya Space Center in northern Norway today (Sept. 5) at 4:12 p.m. EDT (2012 GMT; 10:12 p.m. local time in Norway). A little over seven minutes later, it etched its name into the record books, settling into an elliptical path around our planet.

"This is right now making history — making history for Spectrum, making history for Isar, making history for Europe,” Nikolaos Perakis, Spectrum chief engineer at Isar, said during today's launch webcast, which the company produced in collaboration with NASASpaceflight. "I'm incredibly proud of this massive achievement."

view from a rocket stage looking down at a cloudy earth

The view from the second stage of Isar Aerospace's Spectrum rocket after it reached orbit on Sept. 5, 2026. (Image credit: Isar Aerospace/NASASpaceflight)

This was Spectrum's second bite at the apple: The two-stage, 95-foot-tall (28-meter-tall) rocket first flew in March 2025, lifting off from Andøya on a flight Isar called "Going Full Spectrum." But, as often happens on debut missions, Spectrum suffered an anomaly that day; it came crashing back to Earth less than a minute after launch.

"A comprehensive investigation was completed within two months of the launch," Isar wrote in a September 2025 update. "The findings identified an unintended opening of the vent valve and a loss of attitude control at the start of the roll maneuver as the initiating events that led to the in-flight failure."

The company took the lessons learned that day and geared up for flight number two, "Onward and Upward," which was originally supposed to launch in January.

An issue with a pressurization valve nixed that plan, however, pushing the flight to March. Bad weather and an intruding boat scuttled multiple attempts that month, and an April 9 try was scrubbed due to a leak in a composite overwrapped pressure vessel.

Isar next tried to fly on June 15 but stood down after "detecting off-nominal behavior in the vehicle's fluid systems," according to a company update that day on X. But the company got that issue under control, rolling Spectrum back to the pad for today's launch.

"We had some delays, obviously," Petrakis said during the webcast. "We had some setbacks, but we came back stronger from them. And it validates this tremendous amount of engineering across all the teams, and that's what brings us into orbit."

Spectrum didn't carry viable payloads on its debut flight, but it did today: Five cubesats and one non-deployable scientific experiment went up on "Onward and Upward."

Spectrum's upper stage needed to deploy those payloads before Isar could declare full mission success. And deployment couldn't occur until after the rocket performed a circularization burn to get out of its initial elliptical orbit, which was estimated to feature a closest approach to Earth of 112 miles (180 kilometers) and a farthest-away point of 311 miles (500 km).

Spectrum was able to check those boxes, ultimately deploying the payloads as planned, Isar announced via X about two hours after launch.

So today was a very big day for Isar, which is thinking big with Spectrum. The company sees the rocket — which can haul about 2,200 pounds (1,000 kilograms) of payload to low Earth orbit — becoming a workhorse launcher of small to medium-size satellites down the road, and its infrastructure supports that vision. Isar says its manufacturing facility near Munich can churn out more than 30 of the rockets per year.

“Flight 2 is a big, big milestone, big success," Petrakis said. "But this is just the beginning. So I'm really looking forward to the next steps."

Spectrum is the first rocket to reach orbit from Western Europe, but others have long flown from a different part of the continent — the Russian part. For example, Plesetsk Cosmodrome has hosted orbital launches for about six decades now. (About 25% of Russia lies in Europe, with the rest classified as part of Asia.)

Editor's note: This story was updated at 4:45 p.m. ET with news of successful launch and orbital insertion, then again at 8:35 p.m. ET with news of payload deployment.

The Daily Front Page 5 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Code Without Borders
article

Git hosting that never leaves Europe

by sevenseacat·▲ 331 points·151 comments·pushin.eu ↗
hosted 100% in the EU, on a platform built for humans, not AI.

Public and private Git repos, hosted 100% in the EU, on a platform built for humans, not AI. We block slop, never use your code to train models, and focus on availability, not shiny AI features.

Start pushing — free Explore repos

Invite-only beta

Pushin is currently in invite-only beta. You won't find polished marketing material here just yet. We're putting that time into the core features first. Please check the FAQs below for more info.

Five values we build by

We're not just a Git hosting platform. We're the Git hosting platform with a backbone. These are the values that guide us.

No US kill-switch. Just European law.

Your repos never leave the EU. That means no CLOUD Act subpoenas, no foreign platform politics, and no vendor an ocean away who can lock you out overnight — only GDPR and a privacy-first approach.

Bots are welcome. Slop gets blocked.

Maintainers are drowning. Reputation hunters spray low-effort contributions across popular packages just to get their name on the list. We block the slop instead of enabling it, so maintainers don't burn out.

Built for humans.

We build for developers, not bots. No AI buttons bolted onto every surface. We obsess about DX while others chase shiny AI features but can't keep the servers online.

Always online.

When your Git host is down, so is your team. We put availability front and centre and engineer for it relentlessly, because you should be able to ship at any hour.

We don't train a model with your code.

We don't use your code to train models, and we don't let partners either — no exceptions, no fine print, no quiet opt-out buried in the terms.

Everything you'd expect from a modern Git host.

Questions, answered.

Is this real?

Yes, it is! I'm Peter Ullrich, and I'm building Pushin.eu with my Labrador, Bella. I've worked as a software developer for almost 10 years and live in Leiden, the Netherlands. You can also find many of my talks on YouTube. I've built other projects, including Letter to Yourself and Indie Courses. I'm also currently No. ~108 globally with the most reported CVEs because of my security work with the Erlang Ecosystem Foundation.
I started building Pushin.eu in April 2026, and it will become generally available in early 2027. I'm building it for the long term, with the hope that it will help Europe become more independent.

How do you make money?

We're currently in invite-only beta and focused on polishing the product. Once Pushin.eu is generally available, we'll offer individual and team subscriptions at prices comparable to GitHub and GitLab.

How do you block slop?

We plan to reduce low-quality contributions through several features, some already in place and others still to come. Invite-only registration helps us filter out low-quality accounts and confirm that new members are real people. Next, we plan to introduce a vouching system, similar to Tangled's, that lets contributors build a reputation. We'll combine that reputation with other signals to assess new pull requests and issues. Contributions that appear low quality will be clearly marked and de-emphasized, while maintainers will make the final decision. We also plan to introduce sane limits to contributions, like how many pull requests and issues a new contributor can open in repositories they don't own. This will be an ongoing effort, but the goal is clear.

How do I move a repository over from GitHub?

Install the pun CLI and run pun import. With no arguments, it lists your GitHub repositories so you can choose the ones you want. A GitHub import brings over the full Git history, labels, issues, and closed and merged pull requests with their comments. Original numbers, timestamps, and author attribution all remain intact. The import runs on your own machine, so your GitHub token stays with you. Any other Git remote works too, but those arrive as Git-only imports because there is no API for reading the surrounding metadata.

Can I try it without leaving GitHub?

Mirror instead of importing. A mirror is a one-way sync from a GitHub repository into Pushin.eu: GitHub stays your source of truth, and the mirror follows along. Mirrored repositories are read-only to direct pushes, so the two stay in step. You can sync on demand, pause the mirror, or remove it whenever you like — nothing about it is a commitment.

Does it work with the tools I already use?

Push and pull over SSH or over HTTPS with a personal access token, exactly as you do today. There's a REST API at /api/v1 with GitHub-compatible request and response shapes, so most scripts written for GitHub need nothing more than a new base URL. It covers a large part of GitHub's API, but not all of it, so review the available endpoints before porting a large integration. The pun CLI manages repositories, pull requests, reviews, and comments from the terminal. You can secure your account with TOTP or a passkey.

How do I get an account?

Registration is invite-only. Every member gets a limited number of invitation codes to share, so you'll need one from someone who is already here. You don't need an account to read code, though - Explore lists public repositories on the platform.

Where is my code actually hosted?

On bare-metal servers that we operate in Scaleway's Paris datacenters. Scaleway is a French company, and nothing fails over to a US region, so there's no CLOUD Act surface. Your code remains in France, under European jurisdiction.

How do I get my data back out?

git clone gives you a complete copy of each repository, including its full history. It's ordinary Git, so the tools you already use will work. Issues, pull requests, and their comments are available through the REST API. There is no one-click export archive yet, but it's planned. Leaving should be as easy as arriving.

The Daily Front Page 6 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — The AI Scorecard
article

Artificial Analysis Intelligence Index v4.2

by nojs·▲ 152 points·63 comments·artificialanalysis.ai ↗
more complex and realistic tasks, and more private test sets to prevent gaming

We are accelerating elements of our upcoming v5 release with interim updates to keep pace with the frontier. Index v4.2 has more complex and realistic tasks, and more private test sets to prevent gaming

Intelligence Index v4.2 changelog:

+ AA-Briefcase, our agentic knowledge work evaluation with a private test set

+ Surge’s GDP.pdf, long context document reasoning across 4,592 PDF pages

- GPQA Diamond, an exceptional scientific reasoning evaluation that has now been saturated

… plus greater weighting on held-out test sets to prevent gaming, and grading infrastructure upgrades to increase robustness

This update brings the Index closer to real-world use cases with more challenging, complex and realistic tasks and private test sets to prevent gaming. We have been planning and building elements of Index v5 for months - it’s been 8 months since we launched Index v4 in January.

We have deliberately held back updates to keep the Index stable through recent major model launches. However, with the frontier moving so quickly in the past weeks, we feel it is important to deliver an immediate interim update to ensure our Index remains as relevant and useful as ever to users.

Beyond this interim update, our team is hard at work on v5 of the Index. We are planning more incremental releases in the near future. Stay tuned!

Intelligence Index v4.2 changes in detail:

➤ Adding AA-Briefcase: Our in-house evaluation with a private held-out test set, AA-Briefcase tests models on realistic agentic knowledge work tasks in complex projects built by industry experts. Models are evaluated on multi-week knowledge work projects, each with many linked tasks and thousands of input source files. AA-Briefcase combines rubric and pairwise grading to evaluate verifiable task success, analytical quality, and presentation quality, giving a holistic view of overall agentic capability in knowledge work.

➤ Adding GDP.pdf: Created by Surge AI, GDP.pdf evaluates single-turn professional document reasoning across 100 PDFs and ten domains. Models must synthesize evidence distributed across 4,592 pages, including text, tables, charts, footnotes, and exclusions. Responses are graded against 1,275 expert-authored atomic criteria; the headline All-pass Rate credits a task only when every criterion is satisfied.

➤ Weighting to measure real-world use and prevent gaming: 40% of our Index weighting is now private, held-out test sets - double the figure from v4.1. Held-out data includes AA-Briefcase, AA-Omniscience, and solutions for CritPt. This reduces the ability for labs to game evaluations. The held-out percentage will increase further in Index v5.

➤ Improving our grading infrastructure: In AA-LCR v1.1, we have added a grading system prompt and corrected errors and ambiguities in answer keys, improving scoring accuracy. For GDPval-AA v2 and AA-Briefcase, we have improved our sampling and re-anchored the Elo scale, making ratings more stable as new models are added. For SciCode we have improved robustness of grading sandboxes to ensure slow but correct code does not count as a failure.

Key results:

➤ Anthropic and OpenAI lead the Index: Anthropic’s Claude Fable 5.1 leads the Index, followed by OpenAI’s GPT-6 Astra, which shows a 4pt gain over GPT-5.6 Sol. Meta is the third-ranked lab on the leaderboard, followed by SpaceXAI, Moonshot/Kimi, Z.AI, and Google.

➤ Cost per Task Pareto frontier shared by four labs: Anthropic, OpenAI, Meta and Z.AI occupy the updated Cost per Task frontier.

➤ GPT-6 Astra dominates the output token frontier: GPT-6 Astra is more token efficient than almost every other model near the intelligence frontier, with Claude Fable 5.1, Grok 4.5 and Gemini 3.5 Flash-Lite at either end of the curve (excludes models below 25 on the Index)

GPT-6 Astra is more token efficient than almost every other model near the intelligence frontier, with Claude Fable 5.1, Grok 4.5 and Gemini 3.5 Flash-Lite at either end of the curve (excludes models below 25 on the Index)

Anthropic’s Claude Fable 5.1 and Opus 5 lead AA-Briefcase, followed by GPT-6 Astra and Muse Spark 1.3. GPT-6 Astra shows a substantial gain above GPT-5.6 Sol of ~85 Elo points

AA-Briefcase is our frontier in-house evaluation with a private held-out test set. The evaluation tests models on realistic agentic knowledge work tasks in complex projects built by industry experts. Models are evaluated on multi-week knowledge work projects, each with many linked tasks and thousands of input source files. AA-Briefcase combines rubric and pairwise grading to evaluate verifiable task success, analytical quality, and presentation quality, giving a holistic view of overall agentic capability in knowledge work.

OpenAI leads GDP.pdf with GPT-6 Astra at 33.2% and GPT-5.6 Sol at 28.2%, followed by Claude Fable 5.1 at 26.2%

Created by Surge AI, GDP.pdf evaluates single-turn professional document reasoning across 100 PDFs and ten domains. Models must synthesize evidence distributed across 4,592 pages, including text, tables, charts, footnotes, and exclusions. Responses are graded against 1,275 expert-authored atomic criteria; the headline All-pass Rate credits a task only when every criterion is satisfied

Full per-model breakdowns below:

Read more about Artificial Analysis Intelligence Index v4.2 at https://artificialanalysis.ai/methodology/intelligence-benchmarking

The Daily Front Page 7 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — The Token Economy
article

Portal by Spotify cut my Claude Code token usage by 90%

by cebert·▲ 252 points·159 comments·engineering.atspotify.com ↗
Most of what an AI coding agent does for me isn't thinking. It's I/O.

Feature Image

Most of what an AI coding agent does for me isn't thinking. It's I/O.

Reading five files to answer a question about one method. Generating a test file that follows the exact same pattern as the twenty test files next to it. Updating docs after a meeting. Thousands of tokens gone and almost zero reasoning. The seat license isn't what hurts, it's the tokens. And you're feeding all of it to a frontier model that's wildly overqualified. What if you could route the grunt work to something cheaper that handles it just as well, and save the expensive model for the problems that actually need it?

It’s hardly just my problem. By 2028, AI coding costs are expected to blow past the average developer's salary. A quarter of engineering leaders already burn $200–$500 per developer per month on tokens. Some are well past $2,000. The tooling pays for itself but only if you stop burning frontier tokens on work that doesn't need them.

Turns out, the fix didn't require a platform team or a new subscription. Just two modes. 

Two modes, zero code

This is exactly the kind of use case AiKA Modes in Portal by Spotify were built for. A mode is a declarative agent that runs on an ephemeral runtime - think AWS Lambda, but for agents. You define the instructions, pick a model, set parameters like temperature, and attach MCP tools. Portal handles the rest. No infra to manage, no API keys, no long-running servers. Modes are callable from the Portal CLI or API. They can be public (shared with the whole company) or private.

For this router to work I created two modes. Both use Gemini 2.5 Flash as the worker model in the examples below, but the model field accepts any model you have configured in your Portal instance. Pick whichever works for you.

Mode 1: bulk-reader

For when Claude would otherwise read multiple large files just to answer one question.

name: bulk-reader description: Bulk file reader for code analysis - delegates I/O from Claude Code instructions: You are a precise code analyst. Read the provided files and answer the question concisely. Output structured bullets only. No greetings, no prose, no preambles. Lead every bullet with the exact name, type, or line number. Use nested bullets for details. Skip anything the caller did not ask for. visibility: public model: gemini-2.5-flash resourceLimits:   temperature: 0.2 tags:   - coding   - delegation

Mode 2: code-writer

For tests, config scaffolding, type stubs or anything where the output is predictable from existing patterns.

name: code-writer

description: Boilerplate code generator - delegates output-heavy work from Claude Code

instructions: You generate code files based on a spec and reference files. Match the existing patterns, conventions, naming, and style exactly. Output only the code — no explanations, no markdown fences unless asked. If the spec is ambiguous, make reasonable choices that match the reference code's patterns.

visibility: public

model: gemini-2.5-flash

resourceLimits:
  temperature: 0.2

tags:
  - coding
  - delegation

That "output only the code" instruction matters. Without it, the model wraps everything in markdown fences and explanatory prose that Claude then has to parse through.

Routing

The first version of this was a block of routing rules in CLAUDE.md. It sort of worked: Claude would read the instructions and self-route to Portal. But it had problems. The rules were advisory, not enforced. Claude could ignore them. And every project needed its own copy of the instructions.

The current version is a Claude Code plugin called shunt. Delegation goes through the Portal CLI actions registry so the plugin works against any Portal instance with AiKA plugin enabled.

Layer 1: Hooks

Claude Code hooks fire before every tool call. Shunt registers two PreToolUse hooks:

check-file-size fires on every Read call. If the file exceeds a configurable line threshold (default: 350), the hook blocks the read and tells Claude to use the /bulk-reader skill instead. Targeted reads pass through - Claude already knows what section it needs.

check-bash-read catches cat, head, tail, less, and more on large files. Piped commands (cat file | grep) pass through since those are targeted reads.

The threshold is configurable via the SHUNT_MIN_LINES environment variable. Set it in your shell profile or in .claude/settings.json:

{   "env": {     "SHUNT_MIN_LINES": "500"   } }

Layer 2: Scripts

I have two bash scripts that wrap the Portal CLI calls. Claude calls a script with named arguments. The scripts handle everything internally: building the request, invoking the actions, unwrapping errors, and reporting token usage to stderr.

Modes are addressed by name and resolved by Portal: case-insensitively, preferring your own mode, then your team's, then public ones. Fork the public bulk-reader into a customized version and yours automatically takes precedence - no configuration needed.

bulk-read wraps each file in XML tags for clear boundaries and sends them to the bulk-reader mode along with the question.

bulk-read --question "What does this service do?" --paths src/Service.java src/Handler.java

# Follow-up: ask again with the same paths

bulk-read --question "Which methods call the database?" --paths src/Service.java src/Handler.java

Every delegation is one shot. The invocation is ephemeral (nothing is stored server-side) and re-sending the files on a follow-up is free where it matters, because the corpus goes to the worker model and never enters Claude's context.

code-write sends a spec and a reference file to the code-writer mode, strips markdown fences from the output, and can write directly to disk. Claude never sees the generated code. The reference is required: without a file to match patterns against, the worker would generate context-free code that fits nothing in your project.

code-write --spec "Write tests for UserService" --reference tests/OrderTest.java --target tests/UserTest.java

# Output to stdout

code-write --spec "Generate a config stub" --reference config/existing.yaml

Layer 3: Skills

Two skill files tell Claude when and how to call the scripts. Skills are markdown files with a description and usage examples. When the hook blocks a read, the block message points Claude to the /bulk-reader skill, which shows the exact invocation syntax.

This layering means the system degrades gracefully. Even if Claude doesn't read the skill description, the hook still blocks the expensive read. The skill just makes the redirect smoother.

The benchmarks

Tested against a Java monorepo across four scenarios, measuring tokens Claude would consume reading files directly vs. consuming the bulk-reader's summary or writing code via the code-writer. Mean bulk-read savings were around a whopping 90%.

The code-write scenario is harder to measure in tokens because without shunt, Claude both reads the reference files and generates the output as expensive output tokens. With shunt, the code goes straight to disk, Claude never sees it.

What doesn't work

You can't delegate editing. The worker model's summaries don't include reliable line numbers. If Claude needs to make edits based on the analysis, it still has to read the specific section directly. The hooks allow targeted reads (with offset/limit) for exactly this reason, so delegation saves tokens on understanding.

You can't delegate reasoning. The worker model found surface-level patterns but missed a subtle thread-safety bug in my testing. Claude spotted it in seconds once given the right context. The routing explicitly excludes debugging, architectural decisions, and safety-critical code.

Latency adds up. Each delegation is a network round-trip: Claude Code to the Portal backend to the worker model and back. Responses typically take 10–30 seconds, and Portal caps a single invocation at 30 seconds, so very large generations need to be split into smaller calls. This is acceptable for large reads, but counterproductive for small ones. The line threshold exists for this reason, below it the overhead of delegation exceeds the savings.

Token savings are just the starting point

The plugin is a Claude Code artifact, but the idea underneath is model routing powered by AiKA modes. The modes are the load-bearing piece:

  • They're reusable. The same bulk-reader and code-writer modes work across every project and every tool that can shell out to the Portal CLI.
  • They're shareable. Both modes are public in AiKA. Anyone can use them today without creating their own.
  • They're composable. You could create a doc-writer mode for documentation, a reviewer mode for code review summaries, a translator mode for i18n. Each one is a few clicks away.
  • They decouple the routing decision from the worker. The plugin decides when to delegate. The mode decides how to respond. Swap Gemini Flash for a cheaper model, change the system prompt, add MCP tools - the plugin doesn't change.

This is the real power of AiKA modes: they turn model routing from a systems engineering problem into a configuration problem. You don't build infrastructure. You describe what you want and name it.

Try it yourself

  1. Install both plugins from the spotify/portal-ai-plugins marketplace:

    1. claude plugin marketplace add spotify/portal-ai-plugins
    2. claude plugin install portal@portal
    3. claude plugin install shunt@portal.

The portal plugin provides the Portal CLI that shunt delegates through.

  1. In a new Claude Code session, run /portal:setup to set up and authenticate the Portal CLI against your Portal instance.
  2. You’re good to go, just ask a question that spans multiple files.

The bulk-reader and code-writer modes are already public, so there is nothing to create. If you want to customize them - different worker model, different instructions - fork them in Portal and your version takes precedence automatically.

The modes are reusable across projects and shareable with your team. The plugin enforces the routing so you don't have to think about it. Learn more about modes.

The Daily Front Page 8 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Music by Numbers
article

Can guitar frets perform multiplication?

by wibbily·▲ 132 points·35 comments·charlespetzold.com ↗
Can Guitar Frets Perform Multiplication?

Can Guitar Frets Perform Multiplication?

I’m sure that some pictures are worth a thousand words, but others trigger a whole lot of puzzlement. Such was the case with the cover of a book I recently bought entitled Calculating with Tones: The Logarithmic Logic of Music:

Calculating With Tones Book Cover

This book was published by the Oughtred Society, an organization named in honor of William Oughtred, the Anglican clergyman and mathematician who is credited with inventing the first logarithmic slide rule around 1622. The Oughtred Society was founded in 1991, “dedicated to the preservation and history of slide rules and other calculating instruments.” Their website contains a wealth of useful information on the subject.

Although the Oughtred Society website still has a page dedicated to this book, they don’t seem to be selling it at this time, and a second edition is not available directly through the society.

This cover illustration intrigued me because logarithms are involved both in our perception of musical pitch and in the construction of a slide rule. As I discuss in my web-book-in-progress The Lost Art of Logarithms, logarithms and the slide rule were originally invented to ease the tedious processes of multiplication and other mathematical tasks, but they continue to be vital for many reasons, including the understanding of our perceptions of the natural world.

But this cover illustration seems to imply a direct correspondence between the spacing of tick marks on a slide rule and the irregular spacing of frets on a guitar, and even suggests that guitar frets are spaced in such a way that they could be used to perform multiplications just like with a slide rule. Why else would the book be entitled Calculating with Tones?

The illustration is reproduced on page 34 of the book with a caption that includes a description of both halves of the graphic:

Guitar: Frets are positioned on the neck of the guitar to help locate the individual notes. The distance from one to the next becomes shorter as the frets get closer to the lower bridge. The reduction is logarithmic.

Slide Rule: The scale on the slide rule corresponds to a logarithmic function. When using the slide rule, one carries out multiplication by adding distances. The progressive decrease in distance between marks on the slide rule corresponds to the same thing on the neck of the guitar.

Despite how compelling that illustration and description are, something bugged me. It seemed fundamentally wrong, and yet I couldn’t pinpoint the exact problem.

Multiplying with Guitar Frets

I knew what I had to do. First I had to get a guitar such as this one:

Guitar image from Wikipedia by Martin Möller, CC BY-SA 2.0 DE via Wikimedia Commons; modifications by me

If your video display is not wide enough to show this entire guitar, you can use touch or the mouse to drag it horizontally into view.

If this guitar is truly capable of multiplying numbers, some numeric labels need to be added to it. Contrary to the terminology on the cover of Calculating with Tones, the guitar string is suspended between the nut at the left of this illustration and the saddle at the right. Those mounts hold the strings in place and govern their effective lengths when they are strummed or plucked when the strings are not pressed against any frets. Midway between the nut and saddle is the 12th fret, which effectively divides the string in half to play a pitch an octave higher than the string alone.

It’s a little hard to see, but in the cover illustration, the nut is lined up with the 1 on the all-important C and D scales of the slide rule, and the 12th fret is lined up with the 2. Let’s transfer these numbers to the guitar:

Between these two points are 11 frets, which must obviously be labeled as fractions of 12 between 1 and 2:

Many of these fractions can be expressed in reduced forms, and that’s what I’ve done here:

The next step is to saw the guitar in half:

Sorry, but it has to be done. Make a nice clean cut from top to bottom.

Now the two pieces can be put back together but with the freedom to slide one part relative to the other:

Use touch or the mouse to drag the top half of the guitar to the right to multiply two numbers. (If the bottom half of the guitar can’t fit on your screen, you can continue to horizontally scroll it.)

For example, suppose you want to multiply 7/6 by 3/2. Move the top half of the guitar so that the 1 on the top is aligned with the 7/6 on the bottom:

Now find 3/2 on the top half. Opposite that on the bottom is the product: 7/4, which is indeed 7/6 times 3/2.

Most of the other number combinations require some interpolation between the frets, and that’s not always easy, particularly because I’ve labeled the frets with fractions rather than decimals. But a couple other combinations work well, such as 5/4 times 4/3 equaling 5/3, and 4/3 times 3/2 equaling 2.

Just off hand, it seems as if this experiment is a success: Guitar frets can definitely multiply!

No, Guitar Frets Can Not Multiply.

Are you ready for my celebratory triumphalism to be brutally mocked?

Most acoustic guitars have 18 or 19 frets, so I can’t use those guitars to experiment with fret-based multiplications much beyond products of 2. But electric guitars often have more frets, sometimes as many as 24, which allow for each string to have a two-octave range.

I found a good image of a two-octave electric guitar on the website of the musical instrument manufacturer Donner. This is the inexpensive Donner DMT-100:

Donner DMT-100: from their website; modifications by me

Notice the two sets of double dots on the neck. These mark the one-octave fret and the two-octave fret. The one-octave fret effectively divides the string in half, while the two-octave fret divides the remaining length in half again.

The nut and the frets can be labeled similarly to the acoustic guitar, but how should I label the two-octave fret?

On the cover illustration of Calculating with Tones the nut is aligned with 1 on the C and D scales of the slide rule, the 12th fret is aligned with 2, but the saddle is aligned with 4:

Calculating With Tones Closeup

You can click this to see a larger version.

Although the corresponce between the guitar frets and the slide rule seems to work well for the first octave, the 24th fret is midway between the 12th fret and the saddle, but that approximately corresponds with 2.8 on the slide rule. What does that mean? And what does it mean that the 4 on the slide rule corresponds with the guitar saddle? The slide rule continues with 5, 6, and so forth up to 10, but guitar frets can’t go beyond the saddle.

Despite my concerns, I have no choice but to continue labeling the electric guitar frets up to 3:

Now it’s time to saw this guitar in half:

Don’t weep. That’s the guitar’s job.

The two halves can be put back together for another fret-based multiplication tool:

Slide the top half to the right to multiply. It’s now easy to find pairs of numbers that do not work right. For example, here’s a multiplication of 3/2 and 2, which should equal 3:

When the 1 on the top half is aligned with the 3/2 on the bottom, the 2 on the top is some distance beyond the 3 on the bottom.

Why does this fret-based slide rule seem to work for one octave but not for two octaves?

I’m afraid this subject warrants a deeper dive.

How a Real Slide Rule Works (Briefly)

It seems as if guitar frets are partially mimicking a slide rule but not quite nailing it.

Logarithms were invented to simplify the multiplication of multi-digit numbers. Today we understand logarithms as the inverse of exponentiation. If

y=10x

then the decimal (base-10) logarithm of y is defined like this:

log(y)=x

It’s well known that if two powers of 10 are multiplied, then the exponents can be added:

10N×10M=10N+M

It can then be shown (as I laboriously demonstrate in Chapter 3 of The Lost Art of Logarithms) that the sum of the logarithms of two numbers is the same as the logarithm of the product of those two numbers:

log(N×M)=log(N)+log(M)

The slide rule effectively implements this calculation in a pair of sliding rulers. How these rulers are constructed is the job of Chapters 6 and 7 in The Lost Art of Logarithms.

Here is the beginning of the making of a 10-inch logarithmic scale. Each number on the scale is positioned based on the total length of the scale (10 inches in this case) multiplied by the number’s decimal logarithm. The scale starts with 1 because the logarithm of 1 is zero:

If this ruler is too wide to fit on your browser page, you can use touch or the mouse to scroll it horizontally into view.

In the old days, these logarithms would be obtained from a book. You can alternatively use a calculator. Most computer-based calculators must be switched into Scientific mode to get access to the log key. Phone-based calculators often have to be turned sideways.

You could continue with some fractional numbers:

Eventually you end up with something like this:

The distance of each of those tick marks from the beginning of the logarithmic scale is equal to 10 inches times the logarithm of the number represented by that tick mark.

Put two of these logarithmic rulers face to face (as William Oughtred did in 1622) and you have a slide rule:

I’ve also added a covenient hairline that you can drag with the blue circle. I’ve initialized this slide rule to show the multiplication of 1.56 and 2.72, but you can drag the top ruler and the blue circle with the hairline to experiment with other multiplications. You want to align the 1 on the top scale with the first number that you’re multiplying on the bottom scale. Then align the hairline with the second number that you’re multiplying on the top scale. The hairline shows the product on the bottom scale, in this case 4.24.

But it’s much more versatile than multiplying small numbers. Multiplying 1.56 and 2.72 is basically the same as multiplying 15,600 by 27.2, or 0.0156 by 0.000272. The only difference is the decimal point in the result.

If you can’t multiply the two numbers by shifting the top scale to the right, shift it to the left. Move the 10 on the top scale to align with the first number on the bottom scale. Here’s 8 times 7:

In this case you have to shift a decimal point to the right to get the product of 56.

You can also use this slide rule to divide: Align the dividend on the bottom scale with the divisor on the top scale. The quotient is on the bottom scale aligned with the beginning or end of the top scale.

There’s no magic here. All you’re doing is adding or subtracting ruler lengths. But because the numbers on the rulers are logarithms, then the numbers are effectively being multiplied. Here’s a little demonstration showing the ruler lengths with the same two numbers I used in my first example:

For each position of the top scale and the hairline, you can see that the two widths on the top add up to the width on the bottom. But these widths correspond to logarithms of numbers on the scale, so these numbers are effectively being multiplied.

Are guitar frets positioned similarly? And can those positions be quantified in a similar way to reveal why they can sometimes multiply and sometimes not?

Our Logarithmic Perception of Pitch

It might seem reasonable that guitar frets are spaced logarithmically because our perception of musical pitch is logarithmic.

What do I mean by that?

Sound is vibration. The frequency of a vibration — how many times the vibrating thing goes back and forth every second — is specified in hertz (abbreviated Hz), named after Heinrich Rudolf Hertz, the first person to prove the existence of electromagnetic waves. (Before the adoption of hertz in the 1960s, the rate of vibration was specified in the self-explanatory units of cycles per second or cps.)

Humans can generally hear sounds with frequencies between 20 Hz and 20,000 Hz, but this varies by person and age. As we get older, our ability to hear higher frequencies often deteriorates.

However, we don’t perceive these frequencies linearly. In other words, we do not perceive the difference between 1000 Hz and 2000 Hz to be the same as the difference between 2000 and 3000 Hz. Instead, we perceive the difference between 1000 and 2000 Hz to be the same as the difference between 2000 and 4000 Hz.

This doubling of frequency is perceived as a change in pitch called the octave, which forms the basis of music throughout the world. As an easy reference, sing the first two notes of the verse “Somewhere over the rainbow.” That’s an octave leap. Whether the song is being sung by Björk or Barry White, the frequency doubles from the “some” to the “where.”

As I discuss in excruciating detail in the as-yet-unfinished Chapter 13 of The Lost Art of Logarithms, music within the Western tradition evolved to have 12 steps to the octave. The labeled keys on this piano illustrate a progression of octaves over much of the range of the piano:

Each pair of consecutive labeled keys is a doubling of frequency and hence an interval of an octave. Middle C is the key marked with a tiny dot, and all the labeled keys are A’s. I used A because standard tuning sets the A above middle C to be 440 Hz. The frequencies of all the other A keys can then be calculated by progressively doubling or halving 440. These various A keys can be differentiated with so-called scientific pitch names using a subscripted number for the octave. These eight labeled keys are denoted as A0, A1, A2, A3, A4, A5, A6, and A7,

Each of the 12 steps in an octave is known as a semitone.

And yes, these words make no sense. The word “octave” seems to imply eight of something, and the word “semitone” seems to suggest half of something. A lot of this terminology originated many centuries ago, and it made sense at the time, but today we’re simply stuck with it.

Over many centuries, there evolved various ways that these 12 notes in the octave would be tuned. (Again, Chapter 13 of The Lost Art of Logarithms has much more detail.) But beginning in the late 16th century, some musicians and theorists began advocating that each semitone step be the same. This is a type of tuning known as equal temperament and it dominates Western music today, although not without some dissension.

What does it mean that there are 12 equal semitones between 220 and 440 Hz, and also between 440 and 880 Hz? It means that the semitone cannot be a fixed number of hertz but must instead be a multiplication. The semitone has to be a number than when multiplied by itself 12 times equals 2, the doubling of frequency associated with the octave. This number is commonly expressed as the twelfth root of 2:

Semitone multiplier=212

or as 2 to the 1/12th power:

Semitone multiplier=2112

For us today, it is easy to calculate that number. Actually, that’s not true. What’s easy for us is to punch that expression into a calculator. In the Windows Calculator, for example, it’s accomplished by switching to Scientific mode, then punching in 2, the xy key, the left parentheses, the 1 key, the divide (÷) key, then 12, the right parentheses, and the equal (=) key.

The 12th root of 2 is an irrational number; there is no exact value. The calculator can only give an approximate value, which here I’ve approximated even more:

Semitone multiplier=212 ≅1.059463...

If you multiply that number by itself 12 times, you get a result of about 2. To keep things simple, you can use 1.059 or 1.06 in a pinch as an even grosser approximation.

Suppose you number all the black and white keys on the piano keyboard from left to right beginning at 1 (the note A0) and ending with 88 (the note C8). The note A4 has a key number of 49, and when the piano is tuned conventionally, a frequency of 440 Hz. From that starting point, you can then calculate the frequency of any key on the piano using this formula:

frequency= 440×2 (key number−4912)

Or you can go the other way by expressing this relationship with a logarithm. But because we’re dealing with powers of 2, it has to be a binary (base-2) logarithm:

key number= 49+ 12× log2 (frequency440)

Those two statements are equivalent.

We perceive the 88 keys of the piano to be equal steps in pitch. Yet those key numbers are logarithms of the frequency. This is what I mean when I assert that our perception of pitch is logarithmic.

Frequency and String Length

The piano, harp, violin, and guitar all make sounds with vibrating strings. The frequency of a vibrating string depends on four factors:

  • the material that it’s made of,
  • its thickness,
  • its tension, and
  • its length.

Musical instruments often contain strings of a variety of materials, thicknesses, and lengths, and the strings are tuned by adjusting the tension. But once a particular string is tuned, everything remains constant except (potentially) its length.

This is certainly the case for a guitar. For any particular string, the frequency is governed entirely by the length of the portion of the string that’s vibrating, which depends on which fret the string has been pressed against. (Well, that’s not entirely true. The tension of the string slightly increases when it’s pressed against the fret, but let’s try to retain our sanity by ignoring that.)

Fortunately, the relationship between a vibrating string’s frequency and its length is very straightforward: They are inversely proportional. The longer the string, the lower the frequency. Divide the length in half, and the frequency is doubled for a leap of one octave.

Fretting the Guitar

The frets of a guitar must therefore be positioned so that each successive fret increases the string’s frequency by a factor of 1.059. Because the frequency is inversely proportional to the length, each fret must effectively shorten the length of the string by the inverse of the semitone multiplier. One way to express this is as 1/2 to the 1/12th power:

string reduction factor = (12) 112 ≅ 0.943874...

I’ll use the grosser approximation of 0.944 to make these sample calculations a little easier. Furthermore, let’s assume that the length of the guitar string from the nut to the saddle is 100 centimeters:

The first fret must be 0.944 times that length, or 94.4 centimeters from the saddle and hence 5.6 centimeters from the nut:

The second fret gets messier. It must be positioned so the string has an effective length of 94.4 times 0.944, or 89.1 centimeters.

You can also calculate 89.1 centimeters by multiplying 100 centimeters by 0.944 squared. That second fret is 10.9 centimeters from the nut, or 5.3 centimeters below the first fret. The spacing between the frets is decreasing already.

This process continues. Each fret must reduce the length of the string to 0.944 times the length associated with the previous fret:

That 84.1 length is 100 times 0.944 cubed. And again:

The space between the frets continues to progressively decrease. The distance from the 4th fret to the 5th is 4.7 centimeters.

Eventually you get to the 12th fret, which reduces the string to half its original length:

The twelfth fret is positioned at 100 centimeters times 0.944 to the twelfth power, or 50 centimeters from the saddle, effectively reducing the string to half its length.

You can increase the range of the guitar by another octave with another 12 frets.

The 24th fret divides the string in half again.

The length of the string from a fret to the saddle can be calculated like this:

string length from fret= string length× (12) fret number12

The distance of any fret from the nut can be calculated like this:

fret distance from nut= string length× (1− (12) fret number12 )

The nut is assumed to have a fret number of 0, so the fret distance for the nut is also 0. The 12th fret has a fret number of 12, so the fret distance is half the string length.

Building a Fret-Based Slide Rule

Notice that no logarithms were involved in the development of the formula for computing fret positions. Nevertheless, now that I have such a formula, I can make my own fret-based slide rule without sawing any more guitars in half. I think I established at the outset that only the first octave of frets seems to function like a conventional logarithmic slide rule. Including the second octave of frets throws the whole thing off.

So let’s take that first octave of frets and label it exactly as I labeled the acoustic guitar with fractions between 1 and 2:

Now let’s make another ruler the same way but with the tick marks on the top rather than the bottom:

You can place these two rulers together and use touch or the mouse to move the top ruler relative to the bottom:

You can try out a couple of the multiplications not requiring interpolation, such as 7/6 times 3/2 or 5/4 times 4/3, and now you might notice that it’s not exact. The tick marks don’t line up precisely. This might have been forgivable when awkwardly grappling with two pieces of a guitar sawed in half, but if the guitar frets were spaced logarithmically, that would not happen.

I can’t make these rulers longer by including the second octave of guitar frets. We already know that’s a disaster. However, logarithmic slide rules are self-similar. The part of the slide rule from 2 to 4 is the same as from 4 to 8 except that the numbering is double:

I can even crank the scales up to 10:

Try 5/2 times 8/3, which should be 20/3, but it just doesn’t line up right.

So is this real or is it fantasy?

A Tale of Two Ruler Scales

It’s annoying that a slide rule constructed from the mathematics of guitar frets seems to work almost as well as a slide rule constructed from logarithms, but not exactly.

The following graphic is not a slide rule. It is not interactive. Don’t try to slide anything. The top ruler scale is constructed based on the formula for positioning guitar frets, and the bottom is constructed from logarithms:

I’ve stretched it out so you don’t have to look closely to see that these are two different scales that only coincide at 1 and 2.

It’s also revealing to graph the two functions beyond the range of these scales. Earlier I showed a formula for calculating the distance of each fret from the nut:

fret distance from nut= string length× ( 1− (12) fret number12 )

I want to adapt this formula for a slide rule: The distance of each tick mark is based on the number associated with that tick mark. That number n relates to the fret number like this:

n=1+fret number12

Or:

fret number=12×(n−1)

Also, because the scale from 1 to 2 is half the string length, let’s also normalize the string length to 2 so the formula for positioning a tick mark becomes:

tick distance= 2− (12) n−2

This will be the red line in the graph. When n equals 1 (the beginning of the scale), the tick distance is 0; when n equals 2 at the end of the scale, the tick distance is 1.

The tick distance in the logarithmic scale from 1 to 2 is simply:

tick distance= log2 (n)

That’s the blue line in the graph. Again, when n equals 1 at the beginning of the scale, the tick distance is 0, and when n equals 2, the tick distance is 1.

Although the red and blue lines seem to coincide between n equal to 1 and 2, these are definitely not equivalent functions!

Here’s what happened: Deceived as I was by the cruelly deceptive illustration on the cover of Calculating with Tones, I contorted my initial labeling of the guitar frets to range from 1 to 2. That implied a certain function that of course coincided at those two points with the logarithm, but was otherwise quite different. The rest is basically coincidence.

One of the themes of The Lost Art of Logarithms is that many phenomena in our natural and human-made world can be interpreted logarithmically. That includes music but it does not include guitar frets.

Addendum: The Geometric Approach to Fret Positioning

Earlier I described the theoretical mathematics behind positioning the frets on a guitar. But the guitar has been around for hundreds of years. How did people figure out the positions of the frets without calculators and precision measuring instruments? For example, how were the frets positioned on the guitar in one of Johannes Vermeer’s last paintings, The Guitar Player, dating from 1672?

Vermeer’s The Guitar Player

Public domain image from Wikipedia via Wikimedia Commons

One popular approach was to use a simple ratio from a book published in Florence in 1581 entitled Dialogo della musica antica et della moderna or “Dialogue of Ancient and Modern Music.”

The author was Vincenzo Galilei, born near Florence probably sometime in the 1520s. He studied lute at a young age and became a singer, teacher, and composer. Some of his music has been recorded: The album The Well-Tempered Lute has a selection of his compositions. He studied music theory with Gioseffo Zarlino, and then refuted some of Zarlino’s theories. His eldest son, Galileo, who was about 17 when his father published the Dialogo, was later to achieve some renown in the fields of astronomy and natural philosophy, and also wrote about music theory in his Two New Sciences of 1638.

The Internet Archive has the original 1581 edition of Papa Galilei’s Dialogo; the International Music Score Library Project (IMSLP) has a 1967 reprint), and the University of North Texas Digital Libary has a doctoral thesis by Robert H. Herman with a complete English translation and much commentary.

Here’s where Galilei discusses how to calculate fret positions for a lute:

I therefore divide the whole line A B into eighteen parts, and toward the high part (moving away from the low part) where that first part ends, I place the first fret. I divide once more the whole remainder of the same number of parts [i.e. eighteen] and from the very same band, I place the first fret under the second. I now proceed to distribute the space which remains beneath the frets, always in that very same order, up to [the number of] twelve, which brings me exactly to [the point] where half of the entire string terminates. (pp. 299–300, bracketed phrases added by translator; corresponds to page 49 in the original Italian edition)

Vincenzo Galilei is suggesting a ratio of 18:17 for positioning the frets. This implies that the semitone multiplier is approximated by:

Semitone multiplier= 1817 ≅1.058824...

If you multiply 18/17 by itself 12 times, you won’t come out to 2. Instead, you get 1.985560, which is quite close.

Similarly, the string reduction factor for calculating the position of frets is:

String reduction factor= 1718 = 0.944444...

If you take this ratio to the 12th power, it’s a little more than 1/2:

(1718) 12 ≅ 0.50364...

But the advantage of 17/18 is that it’s a ratio and ratios are extemely useful.

If you examine any treatise of music theory from the time of the Ancient Greeks through the Renaissance, you’ll see a lot of geometrical drawings. When mixing arithmetic and geometry, ratios are ideal because you don’t have to measure anything or get involved with fractions. It’s all geometrical.

Vincenzo Galilei doesn’t explain very well how the 18:17 ratio is used in practice, but it’s easy to develop a technique. Suppose you were building a lute or guitar, or perhaps you’ve obtained a fretless guitar to which you want to add some frets.

The first step is to get a large sheet of paper and draw a line segment corresponding to the distance between the nut and the saddle:

Let’s call this the string line. Put the guitar aside, and draw a long horizontal line perpendicular to the string line and aligned with its bottom:

Somewhere along that horizontal line, draw another shorter vertical line:

On that shorter vertical line, working from the bottom up, draw 18 evenly spaced horizontal tick marks:

This is something that can be done with the traditional geometric drawing tools of compass and straightedge. The distance between these horizontal tick marks doesn’t matter, just as long as they’re evenly spaced. If you’re lazy, you can even use an 18-inch ruler for marking this line.

Now draw a straight line from the top of the string line to the top tick of the shorter vertical line and continue down to the base:

I’ve drawn a little dot where it crosses that bottom horizontal. From that little dot, draw another diagonal that passes through the second tick mark and intersects the string line:

Now get rid of that smaller vertical line with the tick marks. You don’t need it any more:

I contend that any vertical line drawn from the top diagonal to the base will be divided into two parts: the top part (between the two diagonals) is 1/18th of the total length. The bottom part (under the bottom diagonal) is 17/18th of the total length.

The string line is one such vertical line. The two diagonals divide that string line into 1/18th between the two diagonals and 17/18th below the bottom diagonal. The first fret goes where the bottom diagonal intersects the string:

To determine where the second fret goes, first, extend a horizontal line from the fret to the top diagonal. This is shown here as a dotted line:

You can imagine a vertical line drawn from the point where the dotted line meets the top diagonal down to the base. That line is also divided in two parts. Draw just a part of that vertical line down to the second diagonal. This is also a dotted line:

Now draw a horzizontal line back to the string line:

That is the position of the second fret:

As you keep going, you’ll see that the distance between the frets is shrinking. That’s because each successive fret is 17/18 of the remaining length of the string after the previous fret. Here’s how to find the third fret:

And here’s the fourth:

Just keep going and you get to the octave fret that I’ve made a little wider for emphasis:

Keep going and you can add another twelve frets culminating with the two-octave fret:

And now you have a template for fretting the guitar up to a two-octave range:

Vincenzo Galilei’s advocacy of the 18:17 ratio was apparently quite persuasive. In his essential book Tuning and Temperament: A Historical Survey (Michigan State College Press, 1951, 1952; Dover Publications, 2004), musicologist James Murray Barbour reports finding that “references to the 18:17 semitone cover two and a half centuries.” (p. 59) In other words, people were using this ratio well into the 19th century. That’s why I assume that the guitar in Vermeer’s painting was fretted with this technique.

Let’s see how it works in practice, or at least in a virtual guitar.

Here are two “guitars” with each of the six strings labeled at the top with their pitches. The guitar on the left uses Vincenzo Galilei’s 18:17 ratio for determining the fret positions. The one on the right uses the twelfth root of 2 for the calculations.

On a touch screen, you can pluck a string on these two guitars with your finger. Otherwise you’ll need to drag the mouse pointer across a string with the button pressed. To play the note associated with the open string, pluck the string under the nut near the top. For notes requiring frets, pluck the string right under the fret.

A pair of dots are drawn right above the octave and two-octave frets. These dots are found on some guitars to help orient the musician. The slider in the lower-left corner is a volume control.

If you have a multitouch display, you can pluck two strings simultaneously, perhaps the same string on the two different guitars. At the very top, the frequencies will be identical, but as you get lower down the frets, the frequencies deviate and you might start to hear beats.

It is likely that guitar builders who used Vincenzo Galilei’s 18:17 ratio made subtle adjustments to the frets so that a pure octave interval could be realized.

The Daily Front Page 9 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — The Submodule Argument
article

Git Submodules as a Package Manager

by ErenayDev·▲ 88 points·32 comments·nesbitt.io ↗
.gitmodules is a manifest and the gitlink is a lockfile entry.

.gitmodules is a manifest and the gitlink is a lockfile entry.

I added a worktree to a repository last week to try a branch alongside the main checkout, ran git submodule update --init in it because the build needed the vendored dependencies, and when I was done went to clean up with git worktree remove, which git refused. Per the man page only clean worktrees can be removed, and “unclean worktrees or ones with submodules” need --force. Submodules get their own clause in that sentence, distinct from dirty state. git worktree move is stricter again and refuses outright on any worktree containing submodules. I’d spent the previous week cataloguing how command-line tools harden their --force flags, and here was git requiring one because two of its own features had collided.

GitHub’s git 2.5 announcement introduced git worktree in July 2015 with a one-line caveat: “It’s not recommended to use git worktree with a repository that contains submodules.” Eleven years later git still requires --force to remove a worktree that has submodules and refuses to move one. In between, worktree add had to be patched to ignore submodule.recurse because honouring it made the internal reset --hard recurse into submodule paths that were still empty in the fresh worktree.

This got me thinking about submodules as a package manager. Most of the pieces are there and the behaviour roughly matches, but they don’t quite line up and the experience of using them is worse at almost every step. Enough projects have adopted them and then backed out that “why are git submodules so bad” is a recurring thread.

The gitlink in the superproject’s tree, a commit SHA recorded at a path with mode 160000, is the lockfile entry, and the .gitmodules file mapping paths to fetch URLs is the manifest. git submodule update reads both and populates the working tree, which is the install step. The pin itself is as precise as any package manager’s: an exact commit identified by object ID.

Resolution

The gitlink records only which commit to check out, so .gitmodules carries a url per submodule and update clones from there, which is the only resolution mechanism. If the upstream repository is renamed, transferred to a different host, or taken private, every downstream pin breaks, even though the SHA is unchanged and the objects still exist in every clone that already has them. The manifest hard-codes a host because git has no lookup from a commit ID to servers that hold it.

Git also copies each URL into the superproject’s .git/config the first time git submodule init runs, under submodule.<name>.url, and later commands read it from there, ignoring .gitmodules. Editing the committed .gitmodules to point at a mirror or a fork leaves an already-initialised clone unchanged until git submodule sync copies the new value across.

The usual workaround in CI is git’s global url.<base>.insteadOf config, which rewrites any URL with a matching prefix before fetching, submodule URLs included. The common cases are rewriting https://github.com/ to [email protected]: so an SSH deploy key applies, or redirecting an internal hostname to a mirror.

Installation

A plain git clone writes the gitlink into the index so the submodule directory exists, and leaves it empty until git submodule update --init runs or the clone was made with --recurse-submodules. The submodule.recurse config setting makes checkout, fetch, pull, grep and several other commands recurse automatically, and it defaults to off.

By default update checks out the gitlink commit, detached, and two independent flags modify that:

  • --init: copy any missing .gitmodules entries into .git/config first, required on first run and a no-op after
  • --remote: check out the tip of the submodule’s configured remote-tracking branch instead of the gitlink commit (the remote’s HEAD if submodule.<name>.branch is unset)

The command reference documents both, though the name update conflates “install what’s pinned” with “update to latest”. Switching branches in the superproject changes the gitlink in the index and leaves the submodule’s working tree wherever it already was, so git status immediately shows the submodule as modified. Passing --recurse-submodules to checkout, or setting submodule.recurse, brings the submodule working tree along with the branch switch. The Rust project’s account of moving compiler subprojects off submodules lists this cluster from experience: checkouts left empty or on the wrong commit after clone, unrelated submodule bumps landing in pull requests because a branch switch left the submodule dirty, and custom logic in the bootstrap build tool to check each submodule out to the right commit before building.

Storage

A submodule’s git directory is stored under the superproject’s $GIT_DIR/modules/<name>/, with a .git file in the submodule’s working tree containing a gitdir: pointer back to it and a core.worktree setting pointing the other way. git submodule absorbgitdirs migrates older clones that still have a nested .git/ directory. Each entry under modules/ is a git directory with its own refs, HEAD, index, config, hooks, and by default its own object store. Removing a submodule is correspondingly spread across three places: git rm <path> drops the gitlink and the .gitmodules entry, git submodule deinit <path> clears the working tree and the .git/config entry, and the absorbed $GIT_DIR/modules/<name> directory that both leave behind is documented as a manual rm -rf.

Worktrees and submodules collide over this layout because a linked worktree shares the superproject’s $GIT_DIR but has its own working tree, HEAD, and index under $GIT_DIR/worktrees/<id>/. Put two worktrees on different superproject branches and they reference the same submodule at two different commits. Each needs its own submodule checkout and index, tied to storage that’s partly per-worktree and partly shared. worktree remove requires the override rather than checking whether that state is disposable, and worktree move refuses because the pointer-file rewrite it would need is unimplemented.

Xavier Morel asked on the git list this March whether a submodule checkout could itself be a worktree of an existing shared clone, having found bare repositories plus worktrees worked well for a set of related projects but that adding submodules on top always cloned fresh. An RFC and a three-patch series proposing --recurse-submodules for git worktree add followed in April, giving each linked worktree its own submodule git directory under $GIT_COMMON_DIR/worktrees/<id>/modules/ and sharing the object storage between them by hardlink.

The same multiplication happens in a single-worktree clone when two submodules both depend on a third repository. Each path in the superproject gets its own modules/ entry, its own object store unless alternates are configured by hand, and its own gitlink. The two pins can point at different commits of the same repository, and git treats them as unrelated checkouts. Package managers with a shared cache (cargo’s registry cache, pnpm’s content-addressable store, the Go module cache) store the bytes once and check them out per location.

Updating

The gitlink holds one commit SHA, so moving a submodule forward means entering it, fetching, checking out the new commit, leaving, and git add <path> in the superproject to record the new gitlink. git submodule update --remote fetches the configured branch’s tip and checks that out instead of the recorded gitlink, and committing the result in the superproject is what moves the pin. .gitmodules can name a branch per submodule for --remote and the update bots to follow. A plain update ignores that field and checks out the gitlink SHA regardless. There is no syntax for a version range, a tag pattern, or a minimum commit, so the manifest’s only floating reference is a branch name and the gitlink is the only pin.

Dependabot and Renovate can both open pull requests bumping a gitlink. Dependabot’s gitsubmodule ecosystem proposes a new gitlink SHA when the submodule’s configured branch moves, and Renovate’s git-submodules manager does the same, shipping disabled by default; both follow branch tips because a branch name is the only reference the manifest exposes.

Security

.gitmodules is committed to the repository, so a hostile upstream controls its contents, and git parses it during clone --recurse-submodules before the user has seen any of the fetched files, a combination that has produced remote code execution repeatedly. CVE-2018-11235 used ../ in a submodule’s name so its git directory, hooks included, was written outside $GIT_DIR/modules/ and a post-checkout hook ran during clone. In CVE-2018-17456 the submodule URL began with -, so the child git clone parsed it as an option, the class of bug git’s --end-of-options delimiter defends against. CVE-2022-39253 was a disclosure bug: a symlink in a submodule’s object directory made a local-transport clone copy arbitrary files from the victim’s disk. The fix changed the protocol.file.allow default to user, so local-path submodules now need an explicit opt-in. CVE-2024-32002 combined a symlink with a case-insensitive filesystem to write a hook into .git/ during recursive clone. I covered the broader pattern of package-manager checkout paths as an attack surface in the CWE field guide.

Abstraction

Submodules expose git’s internals directly: object IDs as the pin, detached HEADs after update, the $GIT_DIR/modules/ layout, transport URLs in the manifest. A package manager wraps the equivalents behind a manifest format, a resolver, and a local cache; submodules surface them raw.

Most of the gaps map to things package managers already solved: a shared object cache, recursing into dependencies by default on clone and checkout, a single lifecycle for adding and removing a dependency, range constraints in the manifest. The April patch series adding --recurse-submodules to git worktree add tackles one instance of the storage problem, giving each worktree its own submodule checkout over hardlinked shared storage. Resolution is the harder one: a commit SHA is a host-independent identity for the object, and the URL in .gitmodules is git’s only mapping from that identity to a server that holds it.

The Daily Front Page 10 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Under Rust's Hood
article

Visualizing Rust's Vtables: How dyn Trait Works In Memory

by torutofu·▲ 157 points·29 comments·sofiabelen.github.io ↗
it’s both satisfying and mind-boggling at the same time.

I’m venturing into Rust and it’s both satisfying and mind-boggling at the same time. So far I’ve been learning from the book and Mara Bos’ book, but I got the itch to do some dissecting myself. My initial goal of these experiments was to compare Rust’s approach to polymorphism with C++’s. Ultimately, however, as I’ve come to realize, it’s a bit of a trap when trying to understand a new language through another one to try to draw 1:1 parallels. It might seem like it helps, but at the end of the day, we can’t treat Rust as C++ with different syntax. If that were the case, there’d be nothing revolutionary about it.

That said, I believe there is merit in poking around and coming to understand the why. So, if you’re like me and need to know what exactly is happening in memory, in order to feel like you truly understand the concepts, hopefully you’ll find this post useful :)

By the way, the thumbnail image is a photo of the rust fungus, to which we owe Rust’s name. Credit: gailhampshire from Cradley, Malvern, U.K, CC BY 2.0, via Wikimedia Commons.

You can find all the code and experiments on GitHub.

Introduction: The Crux of the Matter

What we’re trying to achieve is quite simple. Let’s say we have a bunch of shapes: circles, squares, triangles, and we want to call draw() on each one.

C++ Approach #1: Virtual Functions

The first way to do this that comes to mind in C++ is through virtual functions, which makes use of runtime polymorphism. The vtable pointer lives inside the object, virtual dispatch happens automatically.

std::vector<Shape*> shapes = { new Circle(), new Square() };
for (auto* s : shapes)
    s->draw();

Rust’s equivalent would be dyn Trait, which is what we ultimately want to understand. But first, let’s take a look at another way we could solve this in C++.

C++ Approach #2: CRTP

One could also go the CRTP (Curiously Recurring Template Pattern) route, which is essentially compile time polymorphism. If you’re interested, this awesome talk by Klaus Iglberger was my first introduction to the topic, and the one I keep coming back to for reference.

template<typename Derived>
struct Shape {
    void draw() {
        static_cast<Derived*>(this)->draw();
    }
};

Essentially, there are no vtables and it’s resolved at compile time, sacrificing readability (it really is a mouthful).

Rust offers a much more straightforward and simple equivalent to CRTP, namely monomorphization. This is the approach we’ll dig into first to start constructing our mental model of what Rust has to offer.

Static Dispatch

Photo by Jiawei Zhao on Unsplash

Static dispatch, also known as generics, achieves a similar result to CRTP: the compiler generates a separate copy of the function for each type it’s called with. There is zero runtime cost, but the types must be known at compile time.

trait Draw {
    fn draw(&self) -> &str;
}

struct Circle;
struct Square;

impl Draw for Circle {
    fn draw(&self) -> &str {
        "Drawing a circle"
    }
}

impl Draw for Square {
    fn draw(&self) -> &str {
        "Drawing a square"
    }
}

fn draw_shape<T: Draw>(shape: T) {
    println!("{}", shape.draw());
}

fn main() {
    let circle = Circle;
    let square = Square;
    draw_shape(circle);
    draw_shape(square);
}

Under the hood, the compiler generates two separate functions: draw_shape::<Circle> and draw_shape::<Square>.

How does this compare to C++’s templates?

The difference here is the philosophy. C++ makes the constraints implicit, a template accepts any type T that happens to have a .draw() method. While, in Rust, you are typing out the contract explicitly: you “implement the Draw trait for Square.”

My question is, when is this not enough? Before tackling this question, let’s indulge a bit in a side quest.

Side Quest: Rust’s Zero-Sized Types

I tried to look at the size of Circle and Square because I wanted to make the comparison to wide pointers, which we’ll see in a bit, but it led me to discover something unexpected. In C++, the standard mandates that every object has a size of at least 1 byte, even if empty. This is so that two distinct objects always have distinct address, meaning &obj1 must be different from &obj2. This is one of those things that is ingrained in my mind as a fact of nature, so seeing that rust returns 0 totally surprised me. These are the little moments that bring me so much joy as I’m exploring Rust because it deconstructs my mental model and helps me appreciate the different philosophy.

println!("{}", std::mem::size_of::<Circle>()); // 0 WHAT???
println!("{}", std::mem::size_of::<Square>()); // 0

This is how I discovered that Rust handles the unique address guarantee differently. Zero-sized types (ZST) are structs that don’t contain any fields, therefore there’s no need to allocate any memory. Rust tracks identity through ownership, not addresses. Every value has exactly one owner at a time, this is enforced at compile time by our friend, the borrow-checker.

In C++, we might do this to check if two pointers refer to the same object:

if (&a == &b) { // same object }

In Rust, that question is answered by the borrow-checker at compile time:

// the borrow checker already knows these are different bindings
// you don't need to compare addresses to tell them apart
let a = Circle;
let b = Circle;

The compiler tracks a and b as distinct names with distinct owners.

After learning about this, my question was, what happens then if we take the address of a zst? Let’s try it.

let a = Circle;
let b = Circle;

println!("{:p}", &a as *const Circle);
println!("{:p}", &b as *const Circle);

The output:

0x7ffdda99aece
0x7ffdda99aecf

Strange… so they are getting distinct stack addresses 1 byte apart (ce and cf in hex). It might look like the compiler allocated a byte for each, just as C++ would, but this is simply a debug-mode behavior. The compiler assigns local ZST variables a dummy stack slot purely so debuggers can track and inspect them by reference.

If we try this in release mode, however, we get different behavior:

cargo run --release --bin 01_static_dispatch

The addresses do indeed collapse for me:

0x7ffdf74afa6f
0x7ffdf74afa6f

My take away from this is that the compiler makes no guarantees about ZST addresses, and identity is tracked by the borrow checker through ownership, not memory addresses.

Dynamic Dispatch

Photo by Aldrin Rachman Pradana on Unsplash

To continue on our main quest, let’s see what dynamic dispatch would look like for our previous example:

fn draw_shape(shape: &dyn Draw) {
    println!("{}", shape.draw());
}

fn main() {
    let circle = Circle;
    let square = Square;
    draw_shape(&circle);
    draw_shape(&square);
}

It looks almost identical to the static dispatch version, the only difference is &dyn Draw instead of <T: Draw>. But something fundamentally different is happening underneath. Let’s see what happens to the size:

println!("&Circle size:   {}", std::mem::size_of::<&Circle>());   // 8
println!("&dyn Draw size: {}", std::mem::size_of::<&dyn Draw>()); // 16

&dyn Draw is twice the size of a regular pointer. This is called a wide pointer: it’s actually two pointers, one points to the data, the other to a vtable. That vtable is what tells Rust which draw() to call at runtime.

We can inspect what those pointers look like:

fn inspect(shape: &dyn Draw) {
    let (data_ptr, vtable_ptr) = unsafe {
        std::mem::transmute::<&dyn Draw, (usize, usize)>(shape)
    };
    println!("data ptr:      {:#x}", data_ptr);
    println!("vtable ptr:    {:#x}", vtable_ptr);
}

std::mem::transmute does a bit-for-bit copy from the source type (&dyn Draw) to the destination type ((usize, usize)). This requires unsafe because the compiler cannot guarantee that arbitrary bit patterns form valid values for the target type.

We see that objects of the same type share a vtable and the data pointer changes per instance:

=== circle ===
data ptr:      0x7ffdcae73286
vtable ptr:    0x55f23cbe5338   <-- circle's vtable

=== circle2 ===
data ptr:      0x7ffdcae732ec
vtable ptr:    0x55f23cbe5338   <-- same vtable as circle!!

=== square ===
data ptr:      0x7ffdcae73287
vtable ptr:    0x55f23cbe5358

Why the Need for Dynamic Dispatch

Getting back to our question, when is static dispatch not enough, and why do we need dynamic dispatch at all? Let’s explore a scenario where static dispatch is not enough. If we wanted to create a Vec<T> containing a mix of both Squares and Circles, we hit a wall with generics alone.

// does not compile!!
let shapes = vec![Circle, Square];

A Vec<T> requires every element to be the exact same type and size. Circle and Square are completely unrelated and could have different sizes. There’s no “base class” like in C++, where you’d write:

std::vector<Shape*> shapes = { new Circle(), new Square() };

This is where we need dynamic dispatch, aka dyn Trait. Box<T> is Rust’s way of allocating a value on the heap and owning it through a pointer.

let shapes : Vec<Box<dyn Draw>> = vec![ // Box -> [ data ptr | vtable ptr ]
    Box::new(Circle),
    Box::new(Square),
];

Box<dyn Draw> solves the size problem. A Box is always the same size, since it’s just a wide pointer.

Vec<Box<dyn Draw>> in memory:
[ 16 bytes | 16 bytes ]
     ↓            ↓
[data|vtable] [data|vtable]
     ↓              ↓
   Circle         Square

A key distinction from C++ philosophy is that, in C++, the choice between dynamic and static dispatch is made at the class level. If you mark a method virtual, that class will always use dynamic dispatch. In the case of vector<Shape*>, it works because the vtable pointer is part of the object.

In Rust, the choice is made at the call site. Circle is just Circle, it knows nothing about dispatch. You decide whether to use static or dynamic dispatch depending on how you refer to it: &Circle for static, &dyn Draw for dynamic.

Photo by Anderson Portella.

I honestly had to take a moment to let all of this sink in, since I’m so used to how C++ does things, it twists my brain (in a good way) to reason about Rust’s philosophy. I’ve recently started doing aerial silks, and there’s an odd resemblance: when you’re inverted in the air, you have to consciously rewire your entire sense of how your body works. Rust does the same thing to your mental model.

One Vtable per (Type, Trait) Pair

Let’s see what happens when we combine different traits for a type. We want our Duck to both Fly and Swim.

trait Fly {
    fn fly(&self) -> &str;
}

trait Swim {
    fn swim(&self) -> &str;
}

struct Duck;

impl Fly for Duck {
    fn fly(&self) -> &str {
        "Duck flies!"
    }
}

impl Swim for Duck {
    fn swim(&self) -> &str {
        "Duck swims!"
    }
}

What does this look like in memory:

let fly_obj: &dyn Fly = &duck;
let swim_obj: &dyn Swim = &duck;

let (data_fly, vtable_fly) = unsafe {
    std::mem::transmute::<&dyn Fly, (usize, usize)>(fly_obj)
};
let (data_swim, vtable_swim) = unsafe {
    std::mem::transmute::<&dyn Swim, (usize, usize)>(swim_obj)
};

println!("fly_obj  -> data: {:#x}  vtable: {:#x}", data_fly,  vtable_fly);
println!("swim_obj -> data: {:#x}  vtable: {:#x}", data_swim, vtable_swim);
println!("size of duck: {}", std::mem::size_of_val(&duck));
fly_obj  -> data: 0x7ffe769558df  vtable: 0x5573a247ea48
swim_obj -> data: 0x7ffe769558df  vtable: 0x5573a247ea68
size of duck: 0

We can marvel again at how the size of Duck is 0 because it’s a ZST. We also see that the fly_obj and the swim_obj both share the same data pointer, which makes sense because they are both dynamic traits of the same underlying duck object. The interesting part here, however, is how the vtable pointers differ.

Brown and white duck on gray concrete floor

A duck is just a duck.
Photo by Ross Sokolovski on Unsplash

This reinforces our core idea: the vtable is not embedded in the object (like C++), it’s external static data that gets paired with the object when you ask for dynamic dispatch. A Duck stays a Duck no matter if it swims or flies, or how many traits it implements.

Object Safety: Why Not Every Trait Can Be Dyn

If you’re like me, you’re probably thinking that is all very nice. And it is, but it’s important to also understand the limitations. One of these limitations is something that in C++ we didn’t have to worry about, and another one is also present in C++. To clear up the mystery, not every trait in Rust can be used as dyn Trait. A trait must follow so-called object safety rules to be used as a trait object:

  • methods can’t return Self
  • methods can’t have generic parameters

Methods Can’t Return Self

Clone is the classic example because it has fn clone(&self) -> Self.

Self is essentially just a placeholder for the type that is currently implementing the trait.

trait Clone {
    fn clone(&self) -> Self;
}

So, for Circle implementing the Clone trait, Self resolves to Circle.

Since it returns Self, it means that the caller needs to know the concrete type to know how much memory to allocate for the return value. Through a vtable, the caller doesn’t know the concrete type, so the compiler rejects it.

C++ doesn’t have this problem at all, since virtual dispatch always goes through pointers and return types are always pointers too. Rust works with values directly, so when you return Self by value, you need to know the size.

Methods Can’t Have Generic Parameters

trait Serialize {
    fn serialize<T>(&self, output: &mut T);
}

In this case, the compiler would need a separate vtable entry for every possible T:

serialize::<File> 
serialize::<String>
serialize::<Vec<u8>>
...

It would essentially need to be infinite.

Therefore, this won’t compile:

let s: Box<dyn Serialize> = ...; // COMPILE ERROR

What about C++? It hits the same wall essentially, since you can’t have a template virtual method for the same reason:

class Serialize {
public:
    template<typename T>
    virtual void serialize(T& output); // COMPILE ERROR!!
};

I googled this, and technically, with enough C++20 black magic you could work around this by building the vtable manually, but it only works within a single source file and is not something you’d do in production code. This is out of the scope of my post, but I’ll leave a link to Christian Daley’s article if you’d like to go down this rabbit hole.

Recap

We started with a simple question: how do you call draw() on a collection of shapes in Rust, without inheritance?

  • Monomorphization (static dispatch) in Rust and CRTP in C++ both are compile-time polymorphism. They have no runtime cost, with the downside being the code size. Essentially, monomorphization is a native language feature for what CRTP achieves as a sort of workaround.
  • dyn Trait and virtual functions in C++ both use a vtable to achieve dynamic dispatch. The difference is that in C++, the vtable pointer lives inside the object, adding overhead to every instance whether you use polymorphism or not. In Rust, the vtable pointer only appears when you explicitly use &dyn Trait or Box<dyn Trait>.
  • Zero-sized types (ZSTs): In C++ every object must occupy at least 1 byte because it tracks the identity through memory addresses at runtime. In Rust, identity is tracked through ownership at compile time, which is why it can have zero-sized types.

If you made it this far, thank you! This one took a while to get it out there. I started writing it in March 2026. It just needed some final touches, but I hadn’t gotten around to publishing it. I kept second guessing myself if it’d actually be useful content, but I figured, if this approach was helpful for me, maybe it’d also be useful for someone else. Much has happened between then and now, both in my personal life and in my Rust journey. Some good, some bad. I’m in Spain now, and a life update post is coming soon. On the Rust side, I’m diving into concurrency and lock-free programming, with the goal of eventually contributing to open source. And I plan to document that journey here too :)

The Daily Front Page 11 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — The Gold Moves Home
article

Netherlands pulls gold out of the US

by daniel_iversen·▲ 350 points·257 comments·abc.net.au ↗
boost the country's "crisis preparedness" amid "increasing geopolitical unrest".

The ends of gold bullion bars, seen stacked four rows high and six wide, in front of other blurred piles of gold.

The Netherlands has moved dozens of tonnes of its gold bullion reserves from the US and Canada. (AP: Mike Groll)

The central bank of the Netherlands has moved dozens of tonnes of its gold out of the United States and Canada.

The De Nederlandsche Bank (DNB) said in a statement the removal aimed to boost the country's "crisis preparedness" amid "increasing geopolitical unrest".

Experts say that rhetoric appears inherently related to the abrasiveness of the Trump administration towards traditional allies.

Dutch plans golden 'anchor of trust'

The DNB said the movement of gold out of North America was aimed to ensure the Netherlands's reserves were "more readily available for use in a crisis".

Between March and August this year, 86 tonnes of a total 313 tonnes of gold held in the US and Canada were moved to the Bank of England in central London.

Several male reporters standing, sitting or using mobile phones outside the De Nederlandsche Bank building.

The De Nederlandsche Bank says it chose to move the gold in a bid to boost its "crisis preparedness". (AP: Peter Dejong)

"Gold that is held with the Bank of England must meet modern international trade standards and is regarded as the world's most easily tradable gold," the DNB statement said.

"[It] will therefore be the most readily available for DNB in a crisis situation.

"The gold reserves held in New York and Ottawa cannot be utilised as quickly and directly in such a situation."

The bank also said it was within Dutch interests to have quicker access to the reserves because gold was seen as "an anchor of trust" and "ideally suited to hedge extreme systemic risks".

DNB governor Olaf Sleijpen said the decision increased the reserves' economic usability but he did not say why the bank believed the Netherlands needed to increase its crisis preparedness.

"We expect that we will never need to use them, but we do need to strengthen our resilience and preparedness,"

he said.

The Netherlands's gold reserves totalled 612.4 tonnes, valued at 72.2 billion euros ($116.7 billion) in 2025.

The bars are held in the Dutch town of Zeist, as well as London, the US Federal Reserve in New York City and the Bank of Canada in Ottawa.

During the transfer, 59 tonnes of gold held in New York were sold by the DNB, which then bought the same amount of bullion bars in London for storage in the Bank of England.

Another 27 tonnes held in the US and Canada were physically shipped to Zeist, and "a similar quantity" then moved to the United Kingdom.

The DNB said combining physical shipments with a buy-and-sell process lowered the risks associated with gold relocation and was more cost-effective.

"Experience of both approaches will be useful in the event that another relocation is required during a potential future crisis and one of the two approaches proves impossible due to circumstances at the time," it said.

A person standing under a Union Jack umbrella in front of the white stone Bank of England building.

The Bank of England, in central London, stores gold reserves for various other countries. (AP: Kin Cheung)

The Bank of England stores gold for central banks and is home to the world's largest over-the-counter gold trading hub, where participants trade directly with one another rather than via an exchange.

What is the 'unrest'?

Threats made by the Trump administration against traditional allies such as Canada and Greenland appear to be the driving force behind the DNB's decision said Australia Institute international and security affairs director Dr Emma Shortis.

"Trump has shown himself entirely willing to trash the established norms and rules of international politics," she said.

"Having those reserves in the United States is a potential vulnerability.

"You can absolutely imagine a scenario where, for whatever reason, the Trump administration decides to hold onto them."

Donald Trump in a black suit, white shirt and blue tie speaking at a lectern with his arms outstretched in front of US flags.

Experts question the political and economic actions of US President Donald Trump toward traditional allies. (Reuters: Evelyn Hockstein)

Dr Shortis described the removal as "strategic" to protect Dutch resources and said it could reflect rising European concerns about the independence of the US Federal Reserve under Mr Trump.

"That's absolutely a reflection of the catastrophic loss of trust in the United States in Europe,"

she said.

"We have every reason to think, based on evidence of what the Trump administration has already done, that [it is] entirely willing to use advantages they have, like gold reserves stored in the United States, as weapons against traditional allies."

Mr Trump has previously voiced disappointment with the European Union and NATO, implemented harsh tariffs against various European nations and raised the prospect of a US seizure of Greenland.

Since the beginning of his second presidential term, he has also pledged to make Canada the "51st state" of America.

Dr Shortis acknowledged the impacts of the Russia-Ukraine war, which is ongoing after four years of fighting, may have also influenced the DNB decision.

What were the economics behind the decision?

Central banks are growing less willing to have their finances held by foreign nations, according to Rabobank senior market strategist Benjamin Picton.

"Gold is a zero-counterparty asset, so if you buy a US Treasury and you hold that as your reserve assets at your central bank your counterparty is the US Federal Reserve or the US Treasury," he said.

"You're relying on them to make payments. There's a credit risk associated with that."

Three gold bars on a dark background, with one bar stacked on a base of two bars.

Global demand for gold has increased in recent months. (Unsplash: Scottsdale Mint)

He said the DNB move may reflect growing economic sentiment within Europe that opposes the Trump administration's policies.

"There's more suspicion than there used to be … and that is because the United States is leaning on Europe to do more," he said.

"We do have this recent context where the United States has developed very sharp elbows when it comes to demands on allies.

"Europe is starting to respond to that by trying to conduct itself more independently.

"It's changing times, and we're seeing the structure of the financial system changing."

Have others previously moved gold?

Yes, greater pushes for gold repatriation between countries have consistently been raised throughout the past decade.

In April, the Banque de France announced 129 tonnes, or five per cent, of French reserves held in New York were sold in 2025 and replaced by gold later bought in Europe and stored in Paris.

The bank said the sale generated a capital profit of 11 billion euros ($17.8 billion). Governor Francois Villeroy de Galhau said in March the decision was not politically motivated.

The Deutsche Bundesbank, Germany's central bank, also repatriated 674 tonnes of its gold reserves from Paris and New York between 2013 and 2017.

In January, several German economists also raised concerns about the unpredictability of Mr Trump and encouraged the repatriation of the nation's remaining 1,236 tonnes of gold still held in New York.

Austria, Italy and Türkiye have also signalled desires to repatriate more of their gold held abroad, with Istanbul withdrawing all of its reserves held in New York in 2018.

The issue has also been long fought-for by Venezuela, which has been trying for almost 20 years to secure almost $3 billion worth of its gold still stored in the Bank of England.

ABC/Reuters

The Daily Front Page 12 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — The Mime Machine
article

A bizarre Commodore 64 peripheral, a mime, and some pretty bad ads

by rfarley04·▲ 90 points·12 comments·buttondown.com ↗
“Sir, we don’t have a prototype or a price for the Spartan ad.” “But we do have a mime.”

Two years of nightmarish smiles and makeup before anyone laid hands on Mimic Systems' actual product.

A mime, wide-eyed and mouth open in surprise. He his holding an apple in his hands with the Commodore logo on it.

TPUG Magazine Nov 84 via The Wayback Machine

“Sir, we don’t have a prototype or a price for the Spartan ad.”

“But we do have a mime.”

Mimic Systems Inc. wouldn’t ship its first product until nearly two years after it began appearing, every month, in almost every Commodore 64 magazine. And when enthusiasts finally got their hands on the Spartan in 1986, its specs felt as old as that mime’s haunting face.

Magazine reviews were tepid at best, with Mark Brown giving it two stars in INFO Issue 12. “Mimic’s Spartan is a noble experiment that just plain took too long to get to market. With all the software available by now for the Commodore 64, the Spartan’s major purpose is gone…It is both a brave attempt at something challenging and a make-believe kind of device. Don Quixote would have loved it.”

Individual users were less diplomatic. “I used it for a month & was constantly on the phone for tech support until I got tired of all the bugs & called it quits,” someone named Phil complained in the comp.sys.cmb USENET group.

And yet, one fully kitted model went for more than $5,500 in an October 2024 auction, while multiple others also received over than half that amount from bidders. 

Announced in 1984. Shipped in 1986. Product market fit in 2024. But what did the Spartan actually do when it eventually hit the market? I’ll let the mime explain. 

A mime with painted pace holding a box for the Spartan computer. He is standing in front of a wall of text of dozens of other tech companies on it.

TPUG Magazine Feb 85 via The Wayback Machine

“We’ve settled on a price. But we haven’t settled on our favorite prototype.”

“But we have settled on our favorite mime.”

Like all of my favorite bad ads, this one starts off bizarre at first glance and only gets weirder the closer you look. 

The lack of ellipsis and proper alignment between the red header text and “WITH THIS” is jarring but forgivable. Putting a guy in whiteface in front of a white background is a choice. Especially next to the semi transparent grey text (which is itself a hodgepodge of competitors and irrelevant hardware peripherals, including former newsletter darling Corvus hard drives). My favorite blink-and-you’ll-miss-it detail, though, is that the mime’s face is printed on the box. STOP. WHY.

[It’s worth mentioning that the guy in these photos is, based on far too much research, not Billy Scudder, the Charlie Chaplain impersonator from IBM’s not-bad tech ads.]

At least we finally know what Mimic Systems is selling: An Apple ][+ emulator for the Commodore 64. And David L. Farquhar from The Silicon Underground has an excellent explanation for why someone would even want that. The Commodore 64, which launched in September 1982, “cost half as much as any other 64K computer. But there wasn’t a lot of software for it during its first couple of years on the market…Meanwhile, the Apple II+ had thousands of software titles available for it, including the spreadsheet Visicalc.” Apple’s computer also ran for $1,195. 

“If you could buy a module to give yourself both a C-64 and an Apple II+ and let the monitor and printer do double-duty, you stood to save 600 bucks. In inflation-adjusted dollars, that was $1305,” Farquhar explains. Half the price for compatibility with all of the same software* and hardware. The only problem was the ][+ was discontinued by 1983, a year before the first ad from our productless mime and two years prior to the second campaign (the one above).

[* The Spartan’s “version of BASIC was not fully compatible with Applesoft BASIC while most of the stuff on the market at that time that would run on a ][+ was still in BASIC,” according to a Usenet thread.]

Not entirely clear in either of the first two ads, Mimic Systems’ device was a full blown Apple ][+ clone. Its hardware was similar to Franklin’s Ace machines (which, remember, had their own bad ads) while its ROM was jumbled just enough–while still being mostly Apple compatible–to avoid the lawsuits Franklin faced with their more exact copies.

Ultimately, the Commodore would provide some juice to the Spartan in a few narrow applications. But in most cases, it was barely more than a passthrough keyboard, if you had the deskspace to smush the two computers together.

A mime holding the Spartan computer in his hands. Next to him is a Commodore 64, a 1541 drive, and a monitor with a big white space beneath it, where the Spartan is supposed to go.

INFO Magazine Sep 85 via Vintage Computing

“Sir, finally, we have a finished product. How do you want to photograph it?”

“...”

“No. No, we can’t. He packed up and moved his miming to Portland. Please.”

“Give him anything he wants. Just bring him in.”

It took its sweet time but now all could see its final form. “This 12” deep by nearly 5” high C-64-gray plastic container is exactly the same width as the 64,” reads a 1986 review in Ahoy! magazine. You literally had to “mate” the two machines together, connecting the cartridge port, printer port, power input and a few others when the backplate of the Commodore bumped up against the frontplate of the Spartan. “The top surface of the box, which is easily removable to allow for internal access, is the right size and of sufficient structural strength to support a 1702 color monitor.”

A Commodore 64 plugged into a Spartan machine, sitting on a desk. The Spartan has its top off and an apple sitting on top of its exposed power supply.

A “mated” Commodore 64 and Spartan via Mike Naberezny

“It has the distinction of being the only 64 peripheral that when operational turned the 64 into a peripheral,” one user joked on Lemon64. The whole point was running Apple ][+ software, though, something the Spartan was still struggling with long after announcing its “instant evolution.”

Brent Marykuca remembered how, in 1985, “I (at the tender age of 19) was hired to work as the lead software developer on a product dubbed ‘The Spartan’ a Frankenstinian creation the likes of which we will not see again.” There was a button for flipping the monitor between the Commodore’s output and the Spartan’s, along with a DIY LED hanging loosely outside the case that changed color based on which was currently selected.  You had to wire up dozens of jumpers. Nothing screams scary mime quite like the included DOS card, however. 

The optional add-on was “installed inside the Commodore 1541 disk drive, between the drive mechanism and the 1541 logic board,” according to Brent. “In 1541 mode, the 'DOS Card' simply passed signals through from the 1541 logic, but at the flick of a switch (F6, I believe) it would take over the mechanism and turn the thing into an Apple II drive. As you can imagine, the potential for grave damage to both Apple II and 1541 diskettes was enormous and often realized.”

A disassembled 1541 drive on the right with an attached "DOS" card from Mimic Systems on the left. There are several jumper cables connecting the two cards.

A DOS card (left) connected to a 1541 drive (right) via Mike Naberezny

I found half a dozen reviews that mentioned the DOS card and every single one said the finished Frankenstein ran as loud as a mime is quiet. None of the reviewers could reassemble the casing after connecting the wad of cables and gluing the add-on board to the plastic with four provided sticky feet. Multiple magazine articles admitted the DOS card bricked their drives during tests. And that was after the price had been slashed.

“How Mimic managed to put a retail price of only $300 (only half what it was supposed to sell for originally) on the Spartan is unbelievable. You get at least $300 worth of parts in the thing,” based on the INFO’s two-star review. And that brings us back to comments from Mimic’s teenage lead developer about why they were so late to market.  

Brent recalls how there were “weekly changes to the design by the president of the company (he would typically change it back the following week), frequent unmotivated firings of technical staff (he fired three in one day I remember, one guy because he was watching the plotter draw a circuit board design rather than working), and a draconian management style (we were paid by the hour and required to 'clock out' to go to the bathroom).” He quit shortly after the Spartan finally launched.

Before later joining Apple as a senior engineer in the early 2000s, Brent claimed that, “the president of the company wrote himself a big cheque from the payroll account and took off for South America.” His claim is quoted as fact all over forums and blogs that are still live today, despite the final four words of his story: “according to legend, anyhow.”

That bugged me. So I did some digging. There were no records of Mimic Systems’ president that I could find. But, assuming they’re accurate, there was a Conversion/Dissolve company filing on June 13, 1985 (before the sale of a single Spartan!) followed by incorporation as a sole proprietorship on October 24, 1985. It’s not hard to believe that the guy who kept putting his favorite mime in computer ads took the money and ran. I just hope the mime went with him.


Cited References:

Uncited References:

The Daily Front Page 13 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — A Better Error Line
article

Pointing at the error: compiler-style diagnostics in uutils coreutils

by ingve·▲ 74 points·11 comments·uutils.org ↗
Unix tools report errors as a single line on stderr. That line says what went wrong, not where.

For 50 years, Coreutils have never stopped evolving. Now, we're pushing that innovation further by rethinking how they report errors.

Unix tools report errors as a single line on stderr. That line says what went wrong, not where. For most commands there is nowhere else to point anyway, but a few take arguments that are small languages: a test expression, a chmod mode, a sort key, a tr set. When one of those fails to parse, what you actually want to know is which argument, or which character of it, the parser tripped over.

rustc has been answering that question with a caret for years, and ariadne puts the same rendering one dependency away. The idea of bringing it to a command-line tool comes from uutils awk, which already reports errors in an awk program that way; coreutils arguments are smaller languages, but they parse just the same. Starting with 0.11.0, coreutils uses it. When stderr is a terminal, a parse error is printed as a report: the arguments are echoed back as a source line, a caret marks the culprit, and a help line explains the syntax when we have something useful to say about it.

What it looks like

Start with tr, whose GNU message assumes you already know what a collating sequence is.

Before:

tr

$ tr 'qw[y-b]' x

tr: range-endpoints of 'y-b' are in reverse collating sequence order

After:

tr

$ tr 'qw[y-b]' x

tr: range-endpoints of 'y-b' are in reverse collating sequence order
   ╭─[ tr:1:7 ]
   │
 1 │ tr qw[y-b] x
   │       ─┬─
   │        ╰─── did you mean 'b-y'?
   │
   │ Help: a range goes from the lower character to the higher one, as in a-z
───╯

Try it in the playground.

A cut list can be long, with a single bad item in it.

Before:

cut

$ cut -f 1,4-2,9-12 notes.txt

cut: invalid decreasing range
Try 'cut --help' for more information.

After:

cut

$ cut -f 1,4-2,9-12 notes.txt

cut: invalid decreasing range
   ╭─[ cut:1:10 ]
   │
 1 │ cut -f 1,4-2,9-12 notes.txt
   │          ─┬─
   │           ╰─── this range ends before it starts
   │
   │ Help: a list is N, N-M, N- or -M, separated by commas, as in -f1,4-6,9-
───╯
Try 'cut --help' for more information.

Try it in the playground.

The caret does not have to cover a whole argument. It can land on one character.

Before:

chmod

$ chmod 'g+rw?x' notes.txt

chmod: invalid operator (expected +, -, or =, but found ?)

After:

chmod

$ chmod 'g+rw?x' notes.txt

chmod: invalid operator (expected +, -, or =, but found ?)
   ╭─[ chmod:1:5 ]
   │
 1 │ g+rw?x notes.txt
   │     ─
   │
   │ Help: a mode is either octal, as in 644, or clauses such as u+rwx,go-w
───╯

sort keys are short enough that a stray character is easy to miss.

Before:

sort

$ sort -k2.3x notes.txt

sort: stray character in field spec: invalid field specification '2.3x'

After:

sort

$ sort -k2.3x notes.txt

sort: stray character in field spec: invalid field specification '2.3x'
   ╭─[ sort:1:11 ]
   │
 1 │ sort -k2.3x notes.txt
   │           ─
   │
   │ Help: a key is FIELD[.CHAR][OPTS][,FIELD[.CHAR][OPTS]], as in -k2.3,4nr
───╯

Try it in the playground.

env -S takes a whole command line and splits it the way a shell would. The old message could only quote the offending fragment back at you. Note that the string contains spaces, so it is echoed back quoted, and the caret still lands inside the quotes.

Before:

env

$ env -S 'echo ${1FOO}'

env: only ${VARNAME} expansion is supported, error at: ${1FOO}

After:

env

$ env -S 'echo ${1FOO}'

env: only ${VARNAME} expansion is supported, error at: ${1FOO}
   ╭─[ env:1:14 ]
   │
 1 │ env -S 'echo ${1FOO}'
   │              ─┬─
   │               ╰─── a variable name cannot start with a digit
   │
   │ Help: only $NAME and ${NAME} are expanded; the other shell forms are not
───╯

test builds its expression out of separate arguments. The report echoes the expression on its own, without the test in front, and marks the argument that broke it.

Before:

test

$ test 7 -eq zap

test: invalid integer 'zap'

After:

test

$ test 7 -eq zap

test: invalid integer 'zap'
   ╭─[ test:1:7 ]
   │
 1 │ 7 -eq zap
   │       ───
   │
   │ Help: -eq, -ne, -lt, -le, -gt and -ge compare integers; use =, !=, < or > to compare strings
   │       -eq equal, -ne not equal, -lt less than, -le less than or equal, -gt greater than, -ge greater than
───╯

Try it in the playground.

A SIZE is a number followed by a unit. The report says which half was rejected.

Before:

head

$ head -c 1fb notes.txt

head: invalid number of bytes: '1fb'

After:

head

$ head -c 1fb notes.txt

head: invalid number of bytes: '1fb'
   ╭─[ head:1:10 ]
   │
 1 │ head -c 1fb notes.txt
   │          ─┬
   │           ╰── not a known unit
   │
   │ Help: a size is a number and an optional unit: K, M, G and so on for 1024, KB, MB, GB for 1000
───╯

Try it in the playground.

One parser handles every SIZE in the suite, so the same report shows up for tail -c, truncate -s, split -b, shred -s, od -N, sort -S, the block sizes of du -B, df -B and ls --block-size, and the threshold of du -t.

numfmt --format is a printf-style format that allows exactly one conversion. The old message just restated the rule. The annotation names the conversion you actually wrote.

Before:

numfmt

$ numfmt --format=%q 1000

numfmt: invalid format '%q', directive must be %[0]['][-][N][.][N]f

After:

numfmt

$ numfmt --format=%q 1000

numfmt: invalid format '%q', directive must be %[0]['][-][N][.][N]f
   ╭─[ numfmt:1:18 ]
   │
 1 │ numfmt --format=%q 1000
   │                  ┬
   │                  ╰── f is the only conversion numfmt has; %d, %e, %g and the other C conversions are not accepted
   │
   │ Help: a format is [PREFIX]%[0]['][-][WIDTH][.PRECISION]f[SUFFIX], as in "%'-10.2f"
───╯

Try it in the playground.

csplit patterns contain regexes, and the regex engine already knows which character it choked on. We were simply throwing that position away.

Before:

csplit

$ csplit notes.txt '/a{2,1}/'

csplit: '/a{2,1}/': invalid pattern

After:

csplit

$ csplit notes.txt '/a{2,1}/'

csplit: '/a{2,1}/': invalid pattern
   ╭─[ csplit:1:20 ]
   │
 1 │ csplit notes.txt /a{2,1}/
   │                    ──┬──
   │                      ╰──── invalid repetition count range, the start must be <= the end
   │
   │ Help: a pattern is a line number N, /REGEXP/[OFFSET] or %REGEXP%[OFFSET], each optionally followed by {N} or {*}
───╯

Try it in the playground.

That label comes straight from the regex engine and is not translated, since it is the only place the wording exists.

Where it applies

28 utilities use it in 0.11.0. The linked examples run in the playground; the others are for utilities the WebAssembly build does not ship, so try those locally:

Utility What the caret points at Try it
test the argument that made the expression fail test 7 -eq zap
expr the argument that made the expression fail expr 9 + foo
chmod the failing clause (or character) of an invalid symbolic or octal mode chmod 'g+rw?x' fruits.txt
mkdir the failing part of the mode given to -m/--mode mkdir -m u+q mydir
mkfifo the failing part of the mode given to -m/--mode mkfifo -m u+q mypipe
mknod the failing part of the mode given to -m/--mode mknod -m u+q mydev c 1 3
install the failing part of the mode given to -m/--mode install -m u+q fruits.txt dest
tr the part of a set that is at fault (bad class, backwards range, bad repeat count, …) tr 'qw[y-b]' x
sort the failing part of a -k/--key or field specification, or of the SIZE given to -S sort -k2.3x fruits.txt
numfmt the failing part of a --format or --field specification, the value given to --from, --to, --from-unit, --to-unit, --padding or --header, or the input number itself numfmt --format=%q 1000
printf the failing conversion or escape in the format string printf %5.2c q
seq the failing conversion in the format given to -f/--format seq -f %5.2c 1 3
stat the failing directive of a -c/--format or --printf format stat -c %d%.3 fruits.txt
env the failing part of a -S/--split-string string env -S 'echo ${1FOO}'
dd the failing key, value or flag of a KEY=VALUE operand dd conv=ucase,zap
join the failing field of the output format given to -o join -o 1.2,2.x fruits.txt fruits.txt
cut the failing range in the list given to -b, -c, -f or -F cut -f 1,4-2 fruits.txt
csplit the failing pattern operand, the character of its regex that broke, or the format given to -b/-n csplit fruits.txt '/a(b/'
split the failing part of the SIZE given to -b, -C or -l split -b 7zq fruits.txt
shred the failing part of the SIZE given to -s/--size shred -s 4vv fruits.txt
head the failing part of the SIZE given to -c or -n head -c 1fb fruits.txt
tail the failing part of the SIZE given to -c or -n tail -c 1fb fruits.txt
truncate the failing part of the SIZE given to -s/--size truncate -s 10fb fruits.txt
od the failing part of the SIZE given to -j, -N, -S or -w od -N 3zz fruits.txt
du the failing part of the SIZE given to -B/--block-size or -t/--threshold du -B 1fb
df the failing part of the SIZE given to -B/--block-size df -B 1fb
ls the failing part of the SIZE given to --block-size (also dir and vdir) ls --block-size=1fb
stdbuf the failing part of the buffering mode given to -i, -o or -e stdbuf -o 6pq head

Compatibility first

Being a drop-in replacement for GNU coreutils comes first, so this is strictly an interactive nicety:

  • Reports are only rendered when stderr is a terminal. In a script, a pipe or a test suite, each utility keeps printing exactly the plain one-line message shown as "Before" in the examples above, so anything that greps stderr keeps working.
  • Exit codes are unchanged.
  • Colors are only used on a terminal and respect NO_COLOR.
  • Like the rest of uutils, the messages, labels and help lines are localized; translations are managed on Weblate.
  • It can be compiled out to save a little space: the rendering sits behind the feat_diagnostics cargo feature (on by default), and building without it drops the ariadne dependency while every utility keeps its plain messages.

Turning it on and off

By default the rendering keys off stderr being a terminal and nothing else, which is usually but not always what you want. UUTILS_DIAG overrides it: always draws the report even into a file or a pipe, never keeps the plain line even at a terminal, and auto, or an unset variable, decides from stderr as before. An unrecognized value is deliberately not an error. This is the kind of variable people export from a shell profile once and forget about, and a typo in it should not be able to make a utility fail.

There is no command-line flag to go with it. The utilities that would need one most cannot have it: in test, printf and expr, a new option would be either illegal or ambiguous with the operands themselves.

To get a report out of a script or a CI log, to paste into a bug report for instance:

$ UUTILS_DIAG=always sort -k2.3x notes.txt 2> parse.log
$ cat parse.log
sort: stray character in field spec: invalid field specification '2.3x'
   ╭─[ sort:1:11 ]
   │
 1 │ sort -k2.3x notes.txt
   │           ─
   │
   │ Help: a key is FIELD[.CHAR][OPTS][,FIELD[.CHAR][OPTS]], as in -k2.3,4nr
───╯

Colors are decided separately, and still by the terminal: a report forced into a file is written without them, so there are no escape sequences to strip back out. NO_COLOR sits in between at a terminal, where the report is still drawn, just in plain text.

The two tricks people used before the variable existed still work. Sending stderr somewhere that is not a terminal gets the plain line:

$ sort -k2.3x notes.txt 2>&1 | cat
sort: stray character in field spec: invalid field specification '2.3x'

And giving a command a pty (script -qec "sort -k2.3x notes.txt" /dev/null, or unbuffer from expect) gets the report back, which is handy when the command has to run under a terminal for other reasons.

What comes next

coreutils is where this starts, not where it stops. The rendering lives in uucore::diagnostics, which the other uutils projects already depend on, so picking it up is mostly a matter of handing the parser's error a span. findutils and sed are being wired up now; a find expression and a sed script are exactly the kind of small languages a caret helps with, and grep, awk and the rest have the same regexes and format strings to point at.

If a utility you use still prints an unhelpful one-liner, patches are welcome!

The Daily Front Page 14 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Stopping Power
article

Stopping the Unstoppable: When an unstoppable force meets a dashpot snubber

by crescit_eundo·▲ 62 points·7 comments·practical.engineering ↗
it just… didn’t stop.

Note that this article is a transcript of the video embedded above.

On the morning of September 29, 2016, a New Jersey Transit commuter train rolled into Hoboken Terminal during rush hour with about 250 passengers on board, and it just… didn’t stop. The train reached the end of the track, overrode the bumping post, and slammed into the terminal wall at about 21 miles per hour. Debris fell onto the platform, killing one person, and 110 passengers and crew were injured.

Just three months later, essentially the same scenario played out again, this time at Atlantic Terminal in Brooklyn. A Long Island Railroad train crashed into the end of a terminal track. The lead car actually rode up and came to rest on top of the concrete platform. 108 people were injured, but thankfully, there were no fatalities.

In both investigations, the NTSB reached a simple conclusion: the trains didn’t stop because the engineers fell asleep. They were diagnosed with sleep apnea after the crashes, a chronic disease that leads to sleepiness and fatigue. But the NTSB also pointed to the absence of safety devices or systems that could have intervened to stop each train before the collision.

When you picture a railroad, it probably feels endless. Rails stretch for miles in both directions, with plenty of room for mistakes to unfold. But every line has an end. No matter how good the brakes are and no matter how experienced and well-trained the crew is, any system that depends on an individual, biological, fallible human, is eventually going to fail.

That’s why end-of-track protection is such a unique engineering problem. You might say that those bumping posts at Hoboken and Atlantic Terminal failed, but in reality, they were never meant to “catch” a fully powered passenger train at speed. They were a last-resort backstop, designed for low-speed situations. Train brakes have a lot of redundancy, and failures are rare, but those brakes only work if they are applied in some way. If it doesn’t happen, a static bumping post doesn’t offer much protection, and may even make things worse. End-of-track protection seems like a simple problem, but the solutions are pretty complex. And of course, I built a little model in the garage so we can take a closer look at them. I’m Grady, and this is Practical Engineering.

In many ways, stopping a train is a pretty elementary physics problem. You have the train’s mass, its speed, and an elementary equation: one half mass times velocity squared. That gives you the system's kinetic energy. And in order to bring a train to a stop, all that energy has to leave the train and go somewhere. But this can vary by orders of magnitude. A single empty freight car inching along in a yard may only reach kinetic energies on the order of a hundred kilojoules. A tram or streetcar experiencing a low-speed overrun, like a partial brake failure, may take a few hundred kilojoules to stop. For light rail, you’re around half a megajoule to stop one moving at terminal approach speed. Heavier commuter rail trains may be 5 to 10 times that. And heavy-haul freight trains can climb into the hundreds of megajoules even at relatively low speeds.

You can see why the design of end-of-track devices, often known as bumping posts or buffer stops, is deceptively complicated. The ability to bring a train to a halt in an overrun scenario depends on both the mass and speed of the train. Ideally, if a train is approaching the end of the line, it’s not moving at track speed, and that is the assumption that most designs are based on. That’s why simple bumping posts are so common. They’re like a door stop for a train, just static devices that provide a hard stop at the end of the line. Let me show you an example:

This is my train stopping simulator, which is kind of just an excuse to play with a pneumatic piston. At one end, is that piston, and I can use a regulator to adjust the force this applies. For my “train,” I’m using a little cart on a length of V-rail with some weights.

Let’s set up a little bumping post and see what happens. No surprises here. At low speeds, the train stops nicely against the post. Let’s try it at a higher speed. The collision is a little more violent. And actually, we can quantify that. You may have noticed the accelerometer at the top of my train and the laptop with squiggly graphs in the background. Here’s the data from that smoothed out a bit from the raw readings. Again, at low speeds, it’s not a big deal. It takes about 5 gs to bring the train to a stop. At higher speeds, it was maxing out my accelerometer at 16 g.

It makes sense why you see static bumping posts where only low-speed overruns are expected. They’re cheap; they’re simple; they don’t take much maintenance. But obviously, this is not a great solution for larger trains, and especially not for situations where speeds are higher. If you think back to our equation, where is all that energy going? Obviously, not all of it is leaving the train, at least in my demo. You can see it being bounced back a bit. The rest of it goes into heat and sound generated by the impact, vibrations into the table and track, and some plastic deformation of the post itself. It’s a bit hard to see, but it did bend over a bit in the high-speed tests. There’s really no energy dissipation built into the device. It just resists until something physically breaks.

There are some situations where that might be okay. I kind of dove into this without really taking the time to discuss what safety really means. That’s because there is no one answer in the case of a terminal track. The consequences of an overrun can vary a lot, and so can the consequences of a violent stop. On an industry track or yard, an end-of-track device may just be there to save the inconvenience of having to lift a railcar back onto the tracks. Nothing's moving very fast, so a rigid stop isn’t likely to damage the rolling stock. In passenger terminals, or places where buildings or public spaces lie beyond the end of the track, that calculus changes. You may have people on the train, and you might have people in its path. The design choices between stopping a train in an instant and limiting the violence of that stop are entirely a case-by-case decision. Even given a set amount of energy to dissipate, the requirements for acceleration will vary. So, there’s another simple equation to consider.

The stopping distance of a vehicle from an initial velocity is inversely proportional to the acceleration (or in this case deceleration). They’re on a see-saw. If you want to limit accelerations, the force that an end-of-track device applies to a train has to happen not at a single point in space but over some amount of distance along the track. Sometimes that happens whether you want it to or not. “Telescoping” is the term used to describe when railcars crush into or over one another. Like I said, the kinetic energy has to go somewhere. For freight trains in a true emergency situation, some amount of equipment damage might be acceptable. I can simulate that using a soda can. You can see that the accelerations go down a lot when you’re willing to accept some deformation. You can probably also understand why that’s rarely an ideal configuration, though.

One of the simplest alternatives is just to let the bumping post slide. And I can show you exactly what that looks like. I’ve modified my little demonstration here by adding some springs along the bolts that clamp this bracket down. Let’s run it again to see what happens. You can see that now, instead of an instantaneous hard stop, there’s some stopping distance. Looking at the graph, the peak acceleration is a lot lower, so this is much less violent for the train. And, unlike the hard stop, it’s easy to see where the energy of the train is being dissipated. It’s in the friction between the post and the track.

This is a pretty common arrangement in the real world, where limiting deceleration matters to protect passengers and equipment. The post or buffer stop is equipped with brake shoes that slide along the rail, burning off that energy through friction. Theoretically, this provides a constant level of deceleration that is independent of the train's mass or velocity. The friction is only a function of the clamping force and the properties of the brakes and rails. In the real world, though, that’s tricky to get right. You can see in my demo that there’s a spike in acceleration when the cart first hits the stop. That’s because it has to overcome the static friction first, before the lower kinetic friction takes over. You can also see the deceleration is a little more erratic. That’s the so-called stick-slip action of metal-on-metal sliding. You also have environmental factors like rust or moisture on the rail that can change the force curve. And of course, once something like this gets used, it has to be reset or, depending on the intensity of the collision, replaced, which can be a big job. There is another solution that addresses these challenges.

I’ve replaced the end-of-track device on my demo again, this time using a dashpot snubber. This cylinder that forces air though an adjustable hole to provide a nice smooth force curve. Let’s try it at a slow speed first. I’ve adjusted the stiffness to work well for this situation. Now let’s try a faster run. It looks a lot smoother. And you can see in the accelerometer data that it is. Here’s the rigid stop, the sliding friction run, and the dashpot run all together. The sliding friction had the lowest acceleration, which makes sense because it used up the longest stopping distance. But the damper provides a more controlled and consistent stop, especially when considering the range of train speeds and sizes. And it resets itself after the impact.

That’s why devices like this are most often used for passenger railways where control matters most. Usually, they are hydraulic cylinders that use oil to provide smooth deceleration across a range of train masses and speeds. Of course, using a hydraulic buffer stop doesn’t solve the distance problem. A device like this doesn’t really buy you more space. And in fact, if you’re trying to stop a high-speed train without extreme accelerations, you often need a lot more stopping distance than what’s possible with hydraulic buffers. In that case, sliding friction stops are really the best solution. You can see in my demo that it’s possible to bottom out the dashpot, and after that, it just becomes a much more expensive static bumping post. Hydraulics also add another layer of complexity and maintenance, so these are generally more expensive than sliding friction stops. And actually, you can combine the two into a hybrid system that uses the hydraulics for slower speed impacts… if you remember to tighten the clamps. Let’s try that again. This setup is kind of the best of both worlds. You get the control of the dashpot, while still allowing for sliding friction to dissipate extra energy in extreme situations.

There is another end of track safety solution at the other end of the complexity and maintenance spectrum: the earth mound, essentially a big pile of dirt. Never underestimate the power of a pile of dirt. If there is a large drop-off, important building, or some other critical situation beyond the end of the track, sometimes you don’t want to rely on a modest bumping post or buffer to make absolutely sure a train stops. A simple pile of earth doesn’t do much to protect the train, but it will definitely protect whatever’s behind the pile of earth, and sometimes that’s the thing that matters most.

Of course, there’s a lot more to this than the demos I’ve shown. Unlike the automatic "knuckle" couplers common in North America, many train cars are linked using chains. Managing slack in the train requires shock absorption on the cars themselves, and these devices are also known as buffers. There are also situations where you need to prevent a train from reaching a certain section of track that isn’t at the end of a line. A derailer is a device that forces train wheels off the track using a wedge-shaped metal block. Again, it’s likely to cause damage to the rolling stock, but there are situations where safety is worth that cost. A controlled derailment at low speed is far better than a runaway car endangering workers, crossing a busy road, or colliding with a high-speed passenger train on the main line.

There are also non-structural solutions to train overruns. If buffers and derailers are the "brawn," Positive Train Control is the "brains.” These systems use digital logic to take over train operations if a human operator makes a mistake or is incapacitated. PTC relies on GPS, trackside sensors, and onboard computers to "know" exactly where the train is, how fast it’s going, and what the conditions are ahead. If the system detects that the train is exceeding its authority, the PTC will automatically apply the brakes.

When you’re moving big heavy stuff around, and especially when that big heavy equipment has people inside or nearby, there is a ton of thought and engineering that goes into the safety systems, their redundancy, and the unavoidable physics tradeoffs between energy, acceleration, and space. Whether it’s the elegant, constant pressure of a hydraulic piston or the raw, grit-and-metal friction of a sliding stop, buffer stops and bumping posts are the last line of defense against the unimaginable energy in modern trains. Digital innovations like Positive Train Control are helping to ensure these devices never have to be used, but as long as we rely on rail (and I hope we rely on it more in the future), the "simple" physics of stopping will always be a complex balancing act.

The Daily Front Page 15 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — The Forever Chemical File
article

How the Disaster of "Forever Chemicals" Was Kept Secret

by stevenwoo·▲ 303 points·95 comments·propublica.org ↗
3M knew that harmful “forever chemicals” had seeped into people’s blood decades ago and continued making them.

3M knew that harmful “forever chemicals” had seeped into people’s blood decades ago and continued making them. A former company scientist helped reporter Sharon Lerner understand how a global environmental crisis was kept under wraps.

Description

We now know harmful “forever chemicals” are likely in the bodies of most people on earth. Decades ago, 3M knew that these chemicals had seeped into the public’s blood, but they kept that a secret. One 3M scientist helped reporter Sharon Lerner understand how they kept it quiet for so long.

Read the original story here.

Transcript

Editor’s Note: “Paper Trail” is produced as an audio series. If you are able, we encourage you to listen to the series. This transcript has been lightly edited for style.

Jessica Lussenhop:   So I know that you don’t know me. But you and I probably have something in common. It’s quite likely that inside you and me and almost every living thing on this crazy blue marble … is a little substance called … “forever chemicals.”

I’m sure you’ve heard of them. These are the chemicals, invented by humans, coating products like nonstick pans. For decades, they were put in food packaging — like, they used to be on McDonald’s hamburger wrappers. They’re in mascara and firefighting foam. On couches, carpets and raincoats, to make them stainproof and water-repellent. And from those products and from factories they’ve spread … everywhere. 

Lerner: It’s become clear that these chemicals are actually in every state and in every country and in everybody.

Lussenhop: My ProPublica colleague Sharon Lerner is one of the many people who helped uncover this. Forever chemicals — also known as PFAS — are … forever. They don’t break down easily in the environment or in our bodies.

Lerner: They have developmental effects, immune effects. Thyroid, liver, hormonal systems. Some cause cancer, kidney cancer, obesity. They bind to proteins in your blood, and they change how systems in your bodies work.

Lussenhop: By the time all this became public, people like you and I had been happily eating our hamburgers and lazing on our couches for decades with no idea those chemicals even existed or that they were getting inside of our bodies. But about 10 years ago, Sharon started reporting on a group of people who did know.

Lerner: And I spent a lot of time in those 10 years figuring out what the companies that put those chemicals into the environment and ultimately into all of us: what they knew about it while they were doing it.

Lussenhop: She learned that 3M, the company that pioneered the production of these chemicals, knew that these chemicals were toxic for decades, since the 1970s, but they kept making them anyway.

Lerner: That’s what transformed, kind of, a tiny problem into a planet-changing disaster. It could’ve been stopped.

Lussenhop: Sharon wrote roughly 60 stories about forever chemicals and investigated: What exactly did these companies know, and when did they know it?

But there was one question in all of her reporting that she never got an answer to: How did they keep it a secret for so long? No one inside 3M who knew the secret had ever agreed to talk to her. But then …

Lerner: I got an email from a scientist, and she said she used to work at 3M. 

Lussenhop: This person wrote that she had worked in 3M’s environmental lab in the 1990s, when she was a young scientist. And she had handled, quote, “highly sensitive data.” It quickly became clear that she had known something about forever chemicals long before the public did … but she’d never talked about it publicly.

Lerner (reading): She said, I recently left 3M […] after a 27-year career …

Lussenhop: And now, all these years later, she wanted to talk.

Lerner (reading): I realized that the narrative I have is a missing piece of at least one strand of the story. I thought, “Hmm, intriguing.”

Lussenhop: How does a company with thousands of employees, thousands of human beings, how do they keep a secret this big for this long? A secret that affects the lives of nearly every living being on the planet. Maybe, Sharon thought, this former 3M insider could explain.

Lussenhop: I mean, how did it feel to pick up the phone the first time and call her?

Lerner: Well, I do remember feeling conflicted. You know, I assumed the worst, honestly. Not the worst, but I thought, “Well, here’s someone who knew this thing for 20 years and sat on it, and how can that be OK?” But I’m looking at my own response, which was “Dear Ms. Hansen, I am so happy to hear from you,” three exclamation points. “I would LOVE,” all caps, “to talk.”

REPORTING CALL, Sharon Lerner: Hello? 

REPORTING CALL, Kris Hansen: Hi, this is Kris. 

REPORTING CALL, Lerner: Hi, Kris, it’s Sharon.

Lerner: I mean, of course I was interested. 

REPORTING CALL, Lerner:  I could talk. I would be happy, if you wanted to talk for days, I would listen, you know …  

Lussenhop: I’m Jessica Lussenhop. This is “Paper Trail.”


Lussenhop: As it turned out, Sharon and Kris Hansen, the former 3M scientist, did talk for days …

REPORTING CALL, Hansen: Hey, Sharon. 

REPORTING CALL, Lerner: Hey, Kris. Is this still an OK time? 

REPORTING CALL, Hansen: Yeah, it’s great. I just got home. It’s perfect.

Lussenhop: They had many phone conversations.

REPORTING CALL, Lerner: It’s so early, but I’m glad you’re up. I know you’re an early riser. 

REPORTING CALL, Hansen: No problem. Are you guys getting buried in snow? 

Lussenhop: Eventually Sharon got on the road …

IN-PERSON INTERVIEW, Lerner: It’s getting kind of rural.

Lussenhop: … to interview her in person where she lives, in Minnesota. 

IN-PERSON INTERVIEW, Lerner: Hi, Kris!

IN-PERSON INTERVIEW, Hansen: I’m glad you found it. Perhaps a little bit more out of the way than you’re used to.

IN-PERSON INTERVIEW, Lerner: It’s lovely.

IN-PERSON INTERVIEW, Hansen: This a nice spot, looks out on the river.

Lussenhop: Kris told Sharon that the natural beauty here was one of the reasons why she lives in Minnesota. Not far from 3M’s headquarters. She first started work at 3M in the summer of 1996.

Lerner: Kris had recently finished a Ph.D. in chemistry.

IN-PERSON INTERVIEW, Hansen: I was just coming off of a postdoc.

Lerner: And she considered herself an environmentalist.

Lussenhop: Kris told Sharon that working at a big company like 3M wasn’t her first choice … but she became convinced.

IN-PERSON INTERVIEW, Hansen: People said, you know, some of the biggest impacts that you could have would be to work from the inside of industry, instead of on the outside. Because on the inside, you have access to a lot more information. And so I did come to believe that I could have an impact there. 

Lussenhop: Plus, 3M wasn’t just any old chemical company. It had an amazing reputation.

Lerner: Kris’s dad was an engineer at 3M, and he was sort of famous in the company, or very well regarded. And also, people in Minnesota and in her community generally loved 3M. 

Lussenhop: Full disclosure: I grew up in Minnesota, near the headquarters of 3M, too. My  mom worked there for a number of years. And I can confirm that 3M has a whole identity and a culture around it. It’s known for ingenuity, for making products that make all of our lives just a little bit easier. I mean, for god’s sake, they invented Scotch tape and Post-It notes. 3M makes lifesaving medical supplies like N95 masks and hospital equipment. Astronauts used 3M products during the first moon landing.

3M Innovation Commercial:  We make a difference in the world because we make the leap from need to innovation. 3M innovation.

Lussenhop:  3M came to mean so much more to people than just its products. It was a testament to American ingenuity. It was a playground for scientists. 

IN-PERSON INTERVIEW, Hansen: So I think I was a pretty typical new Ph.D. analytical chemist. I was, um, excited about starting a job that I felt I could make an impact. 

Lerner: Her first job at 3M was working in the environmental lab.

Lussenhop: By the time Kris started working there in the ’90s, 3M had been mass-producing forever chemicals for decades, which they used in their own products like Scotchgard — a liquid you can spray on stuff to make it water-repellent — and they sold it to other companies for use in a whole bunch of other products. 

After she joined 3M, one of the first things Kris learned was that sometimes, these chemicals made their way from the products into the bodies of 3M factory workers. This was discovered years before Kris even started at 3M, when the company would test their workers’ blood for something called PFOS — a type of forever chemical. 

Lerner: So they knew that some of their chemicals get into the workers’ blood.

Lussenhop: But her bosses told her that the chemicals were not toxic. They were just keeping their eye on the levels as a precaution. 3M had recently hired an outside lab to measure the levels in workers’ blood. But that lab ran into something unexpected. 

IN-PERSON INTERVIEW, Hansen: And that’s when my boss asked me to get involved.

Lussenhop: There seemed to be a problem … not with the workers’ blood samples, but with the control blood samples they were using. To get an accurate count of chemicals in the worker blood, the outside lab needed something to compare it to.

Lerner: They wanted to compare it to, like, a blank, what they call it, someone who’s unexposed, and so, they were using blood bank blood.

Lussenhop: The blood bank blood was supposed to be from random people out in the world who have nothing to do with 3M: didn’t work there, had never been there and therefore shouldn’t have been exposed to 3M’s chemicals. That blood bank blood was supposed to have no PFOS. But this outside lab ran into a problem. 

IN-PERSON INTERVIEW, Hansen: It was like, there’s something here that looks like PFOS.

Lussenhop: So Kris’ bosses gave her a task.

IN-PERSON INTERVIEW, Hansen: Figure out what that thing is so we can get rid of it and do an accurate count in the, in the workers’ blood.

Lerner: And when she did start looking into it, what she found was … 

IN-PERSON INTERVIEW, Hansen: It wasn’t an unknown contaminant. No, there’s PFOS in the blood of the non-occupationally exposed population.

Lerner: And at first she thinks, “Oh, that’s odd,” and she repeats it.

IN-PERSON INTERVIEW, Hansen: I made a lot of changes to both the extraction method and the analytical method. 

Lerner: Then she repeats it many times.

IN-PERSON INTERVIEW, Hansen: I was like, this is definitely PFOS.

Lerner: It shows again that PFOS is in the blood from the general population.

IN-PERSON INTERVIEW, Hansen: To find a chemical associated with your company in the blood of the general population and not really know how it got there is frightening. It just opened up so many questions. How did it get there?

Lussenhop:  Every blood sample she tested — every single one — had PFOS in it. 

IN-PERSON INTERVIEW, Hansen: This is bad. This is really a bad thing. 

Lerner: Even though she was told they were not toxic, she knows, being a chemist and a human being, that it’s generally not great to have man-made chemicals in your blood, especially if they last for a long time, as these ones do. And she goes to her boss, someone named Jim Johnson.

IN-PERSON INTERVIEW, Hansen: I showed him the data, and he looked at it and he said, “This changes everything.” And he walked into his office and shut his door.

Lerner: Jim Johnson doesn’t come out and give her any further info, and soon thereafter …

IN-PERSON INTERVIEW, Hansen: He retired just a few weeks after the discovery.

Lerner: He says he’s taking early retirement, and he leaves the company.

Lussenhop: Oh my God.

Lerner: Yeah. 

Lussenhop: Does she wind up having a conversation with him where he says, “I’m retiring, but you keep going,” or, “I’m retiring, but …” — anything? Did he give her any sense of what to do next?

Lerner: No. There was not a clear conversation about her work or what she should do with it or how she should proceed. 

IN-PERSON INTERVIEW, Hansen: I think he was supportive of me, but then he was gone. And so that left me isolated and without, uh, an immediate champion.

Lerner: And I think she really was confused and felt adrift.

IN-PERSON INTERVIEW, Hansen: I just remember sitting solitarily collecting more data and trying to process things and being like, do I talk about this? Do I not talk about this? What does this mean?

Lerner: Jim Johnson, her boss, had told his superiors about her work, and she becomes aware of their interest, mostly in the form of doubt.

IN-PERSON INTERVIEW, Hansen: There was this idea, like, obviously this is wrong. Obviously you made a mistake. So you need to figure out what that mistake was, because now this data exists. 

Lerner: They ask her, “Did you clean your equipment? Because maybe it’s the equipment.”

IN-PERSON INTERVIEW, Hansen: We cleaned the instrument. We extracted and looked at every single material that touched human blood. Every possible source of contamination. 

Lerner: And people say to her, you know, “We think you’re wrong.”

IN-PERSON INTERVIEW, Hansen: Like, if you can’t find a sample without PFOS, we aren’t gonna believe that this is truly PFOS. And I was defensive. I felt very personally responsible. So of course I was working my tail off to try and, and do any experiment that I could to either say, OK, I guess we made a mistake, or more likely we didn’t make that mistake. And if you have another suggestion for us, we’ll do it.

Lerner: At one point, one of her colleagues comes in and says, “Oh, I have some blood for you to test. Why don’t you see if you can find PFOS in this?”

IN PERSON INTERVIEW, Hansen: And I said, what are they? And he wouldn’t tell me. He said, “Just analyze them.”

Lerner: And she does, and the next morning he says, “Did you find it?” 

IN-PERSON INTERVIEW, Hansen: Let’s see the data you collected last night, and there was PFOS in those samples, and he was jubilant. He was like, “This proves you’re wrong, because I collected those samples from my horses, and my horses have never walked on Scotchgarded carpet or eaten at McDonald’s. I didn’t have an explanation. I had no idea how it got into his horses’ blood.

Lussenhop: Kris ended up testing all kinds of nonhuman blood. 

IN-PERSON INTERVIEW, Hansen: We looked at chickens, cows, pigs.

Lerner: Rabbits. 

IN-PERSON INTERVIEW, Hansen: Rats, of course.

Lerner: Eagles.

IN-PERSON INTERVIEW, Hansen: And albatross and fish.

Lerner: And the answer was yes, yes and yes, kind of wherever she looked. 

IN PERSON INTERVIEW, Hansen: All of them. So the second kind of earth-shaking moment was to realize that it was in the blood of bald eagles, for example. It’s in fish, and therefore it’s in eagles. And it’s in fish, and therefore it’s in people who eat fish, and it’s in the food web, which means it’s everywhere. It’s funny for me. It’s like I’ve always been fascinated with eagles. They’re amazing birds. I love to watch them. And so every time I saw an eagle, I would wonder, oh, I see you have a fish and you’re going to feed your babies and you’re going to give them some chemicals, and I hope that will be OK for your little eagle family.

Lussenhop: After Kris realized all of this, she presented her work to her colleagues showing that it was everywhere.

IN-PERSON INTERVIEW, Hansen: One of the scientists, he said something to the effect of, “Why are you doing this? What do you hope to accomplish? This isn’t helpful.”

Lussenhop: It was strange. Having more data in theory should have helped Kris prove something big was going on here. But it seemed like the more data she gathered, the more her colleagues thought it proved the opposite: that it couldn’t possibly be true. It seemed like the only thing that could convince them was to find a sample without PFOS in it. But where?

IN-PERSON INTERVIEW, Hansen: So we were testing any blood samples that our epidemiology department could get from different countries, from Sweden. Just whatever samples they could come up with. And we kept finding PFOS in all of the samples. And they finally came up with a pretty small group of samples that had been archived, that were collected from Korean War recruits in the early 1950s. 

Lussenhop: Meaning … this was blood collected before products containing forever chemicals … products like Scotchgard … were being mass-produced and sold everywhere.

IN-PERSON INTERVIEW, Hansen: It was the first dataset that we found no PFOS. And that was transformative. There was huge relief.

Lerner: What it meant was that her findings were real. If it wasn’t in some samples, that means it really was in others. At the moment she discovered it, she said basically, “I’m not crazy. This is real.” 

IN-PERSON INTERVIEW, Hansen: But then there was also this, like, almost despair, like, this definitely now shows that not only is it present in the blood of people, but this is a 3M chemical that 3M put into the world. We are the source. 

Lussenhop: Around the same time she made this discovery, she was surprised to come across an old paper that was related to her work — it explained a method for measuring forever chemicals in human blood.

Lerner: And she realizes that one of the authors is still at 3M. And she arranges to meet him.

IN-PERSON INTERVIEW, Hansen: And he was very friendly, and I gave him, you know, “Hey, this is what we’re doing.” And he said, “Yeah, I know. I don’t understand why everyone is making such a big deal about this. We’ve known about this since the 1970s. I was like, “What? You’ve known that there’s PFOS in the blood of non-occupationally exposed workers for 20 years?” And he was like, “Yeah, I don’t know why they’re making such a big deal about this now.”

Lussenhop: What?

Lerner: Yeah. Yeah. She’s, you know, flipping her lid, but also quietly taking notes and just taking in what he says. 

IN-PERSON INTERVIEW, Hansen: But also, like, why, why did people give me such a hard time that I was wrong, that my data was bad, that I was arrogant, when we knew. This is — I was discovering it for the second time, not the first.

Lerner: They had known all along, at least somebody had known all along. It really made me imagine what she was going through and how gaslit she really was.

Lussenhop: This scientist told Kris that back in the 1970s, the company’s lawyers told him not to tell anyone that he’d figured out that PFOS had been found in the blood of the general population. 

Lerner: What she didn’t know was: Who else knew?

IN-PERSON INTERVIEW, Hansen: Who knew? Who knew and forgot? Who, who never knew, who knew and was just keeping their fingers crossed that we would drop the investigation?

IN-PERSON INTERVIEW, Hansen: I told my boss.

Lussenhop: Her new boss, that is — after Jim Johnson retired.

IN-PERSON INTERVIEW, Hansen: Told him what I had found both in the literature, but then also what I came back from this interview with. He didn’t respond in any way. Like, oh my gosh, I had no idea. Or, you know, he just didn’t respond.  He said, “Write down your notes about what you learned and give it to me. Don’t email it; just give it to me.” And uh, so, you know, I did. I went into my office and I had my notes and I typed down everything that I had learned in that meeting and, uh, shared a copy.

Lussenop: Kris had no idea what would happen to these notes she took.

IN-PERSON INTERVIEW, Hansen: If it was so easily forgotten and buried back then, like, what — you know, what’s gonna happen now?

Lussenhop: That’s next.

Lussenhop: In early 1999, Kris is invited to a really important meeting. She would have the chance to present her findings to 3M’s CEO. Kris is encouraged by this. Maybe this is her big shot to get someone to finally listen.

Lerner: So she prepares, practices, makes these transparencies and explains all her findings, and she’s got many pages. She goes up to the executive suite on a special elevator.

IN-PERSON INTERVIEW, Hansen: And it is very intimidating because you, you get off the special elevator and you have to, you know, reshow your badge to the administrative assistant who’s there.

Lerner: And when she gets there, they’re at a big table, all these men with suits. 

IN-PERSON INTERVIEW, Hansen: And the CEO was at the very end of the table. And it was just — even at the time, struck me as kind of comical because I’m at one end of this table, and way at the other end of this long table are these five or six men, you know, identically dressed in white shirts and jackets. 

Lerner: And she starts trying to tell them about her research.

IN PERSON INTERVIEW – Hansen: One of them derisively said, “Just get to the point.” And like, “Do you, are you absolutely 100% positive about this? Can you think about any other possible explanation? Who told you to do this work? Why did you do this study? Why did you look at this?”

Lussenhop: In response to that question, like, “Who put you up to this?” I mean, it, it was her former boss, Jim Johnson, wasn’t it?

Lerner: It was her boss, yeah. And as this is all going on, she looks up.

IN-PERSON INTERVIEW, Hansen: I’m looking down this table, with the CEO at the head of the table. His eyes kind of droop, and his chin drops, and he nods off. 

Lerner: He was asleep. 

Lussenhop: No!

Lerner: That’s, yeah.

IN-PERSON INTERVIEW, Hansen: And I remember even in that moment, kind of stepping back and saying, well, this is just bizarre because these guys are way more agitated than I would’ve thought. And the person who they’re agitated for is asleep! You know, at some point he woke up, and he never said a word the entire time. And just, you know, at the end, one of the business leaders kind of dismissed me with a wave of his hand, and I grabbed my three-ring binder and left the room.

Lerner: She’s told not to ask questions, and going forward, it — there’s a different team who will be working on PFOS, and she won’t be on it. 

Lussenhop: A few months later, Kris and her husband went to a picnic with a bunch of 3M scientists. 

IN-PERSON INTERVIEW, Hansen: My father hosted this picnic for 3M’s top scientists, corporate scientists. And often, uh, my husband and I would just go and help. And so I was like, I don’t know, grilling corn. And one of the people came up to me and, you know, asked me if I thought I was powerful, ruining people’s careers, and did I go looking for problems like this because I was envious of people who could create solutions and not just create problems like I did. So this was, like, a senior scientist in the company, and he was very pointed that I was doing something not even to harm the company, but to harm the scientists who had invented these chemicals. And I didn’t really know what to say.

Lerner: What she ended up doing was sort of putting her head down and trying to not think about it. She tried to compartmentalize it and just kind of put this really traumatic experience behind her and move on to new things.

IN-PERSON INTERVIEW, Hansen: I’m not trying to paint myself as some kind of an eco hero, because there was also very much an element of, like, my husband’s in school right now, we have twins on the way and I’m in my first job. This isn’t a great time for me personally to walk away, especially with a reputation now as someone who is kind of a troublemaker.

Lerner: She has twins and then another child, and she moves to another division of 3M that has nothing to do with these chemicals, and she leaves the environmental lab. She really buries the issue, and she seems to sort of seal it and put it on a shelf and walk away.

Lussenhop: So here at last was the answer to Sharon’s question, which was: How does a secret this huge, this planetary, stay a secret for so long in a company where some people knew? The answer, at least in the story Sharon found, isn’t in burned records or violent threats. In this instance, it was, in part, discouraging and questioning a young employee so consistently and so harshly that she eventually gives up trying to talk about it. The secret was also kept because Kris didn’t have all the information. Her bosses told her these chemicals weren’t toxic. 

IN-PERSON INTERVIEW, Hansen: I mean, I really clung to that: “At least it’s not harmful.” I didn’t look at that as critically as I should have, but it almost would’ve been too much to bear at the time. I think what I was afraid of is that I would find out that it was hazardous to human health.

IN-PERSON INTERVIEW, Lerner: And then what would’ve happened?

IN-PERSON INTERVIEW, Hansen: And then I, I’d just be, like, it’s everywhere: that those eagles and those fish, like, there is real potential for these to cause harm.

Lerner: The scale is planetary, right? I mean, if you have a contaminated plant, you could clean it up. If it’s in every human being and animals around the world, um, that’s virtually impossible to clean up.

IN-PERSON INTERVIEW, Hansen: I don’t even know how to wrap my head around that. And so it was very easy to say it’s not harmful. And to live in that fantasy.

Lussenhop: As Kris continued living in that fantasy … the rest of the world … was waking up to the truth about these chemicals. 

She didn’t know it at the time, but in the late ’90s, in compliance with a federal law, 3M notified the EPA that they’d found PFOS in blood samples from all over the country … although they also claimed there were “no adverse effects.” The EPA’s response was to pressure 3M to discontinue making PFOS-related chemicals. Which they did, in 2002. This was, in part, thanks to Kris’ research. But 3M swapped in another forever chemical as a replacement, which also stays in the environment and builds up in the body.

Around the same time in West Virginia, a bunch of cows who were grazing downstream from a DuPont site started getting sick, foaming at the mouth. More than a hundred eventually died. It turned out that the water on the farm contained forever chemicals. That … became the subject of a lawsuit, which ended in a settlement, a three-part series by Sharon Lerner … and the plot of a movie called “Dark Waters,” all alerting the public about forever chemicals. 

But Kris didn’t know any of that. When she’d see news about forever chemicals, she told Sharon she’d just kind of avoid it.

IN-PERSON INTERVIEW, Hansen: I maybe would glance at it, but I wouldn’t read it very carefully. It’s almost like I just didn’t really want to know.

Lussenhop: Until 2021. An old college professor sent her a message with a link to a video.

IN-PERSON INTERVIEW, Hansen: A John Oliver video.

LAST WEEK TONIGHT WITH JOHN OLIVER, John Oliver: Our main story tonight concerns chemicals. Specifically, we’re gonna be talking about a particular class of chemicals tonight, called PFAS.

IN-PERSON INTERVIEW, Hansen: He was reporting on PFAS and the effect that it had on communities.

LAST WEEK TONIGHT WITH JOHN OLIVER, John Oliver: The Teflon mesh decomposes to something called devil’s piss. Yeah. That doesn’t sound great, does it? 

Lussenhop: The show referred to some of Sharon’s reporting. 

LAST WEEK TONIGHT WITH JOHN OLIVER, John Oliver: These chemicals have been linked to a massive array of health issues … high cholesterol, ulcerative colitis … thyroid disease, testicular and kidney cancer and decreased response to vaccines.

Lerner: And Kris, her initial reaction is …

IN-PERSON INTERVIEW, Hansen: He’s wrong. He’s talking about all the ways that it’s harmful, and it’s not. I said, it kind of makes me mad, honestly. 

Lerner: She writes back to her professor, and the professor says …

IN-PERSON INTERVIEW, Hansen: “Are you sure it’s wrong?” And so I kind of rolled my eyes and then I went in and I was like, OK, I’m just gonna go to the literature. 

Lerner: And sitting in her kitchen, Kris googles the health effects of the chemical that she had found so many years ago.

IN-PERSON INTERVIEW, Hansen: And I couldn’t believe how many articles there were on the toxicity associated with fluorochemicals.

Lerner: Just kind of open-mouthed scrolling, realizes that it affects children’s immunity, that it has links with cancer, that it can affect obesity. The health data starts to pile up, and it’s pretty damning. And she’s reading through this and she’s stunned and has, I think, a very uncomfortable mix of emotions.

IN-PERSON INTERVIEW, Hansen: Yeah. I mean, angry at 3M for many of the things that they’ve done. But also angry at myself for realizing how quickly I accepted a statement about toxicity because it was comfortable for me. Like, it’s a little bit unforgivable.

Lussenhop: It was after reading through all this medical literature that Kris sent Sharon that email and they started talking. A lot. 

And as the two spoke, Sharon went back through her old documents — internal 3M documents that had helped her report out what 3M knew. There was one document that had been a key piece of evidence in that reporting. It was a set of notes. Not an email. Notes documenting a meeting between two 3M scientists. It said 3M knew about PFOS in blood as early as the 1970s. Sharon had never known who wrote those notes. 

Lerner: It was only when I talked to Kris and she told me the story that I realized, oh my goodness, these were her notes. 

Lussenhop: That’s, that’s remarkable to me. I, I’m just thinking of, like, you getting an email from Kris, you know, initially, and thinking, “Oh, this is one of these people who knew this and, and sort of sat on it.” But at the same time, it’s almost like, I don’t — I can’t stop thinking about it as almost, like, she did do that work. She did her best at the time. She kinda like put this message in a bottle in the ’90s, and, like, you got it. Eventually.

Lerner: That’s funny. Yeah. Yes. 

Lussenhop: But there were gaps in Kris’ knowledge, too, about what had happened to her at 3M all those years ago.

Lerner: I became curious about what she didn’t know, which was who among her bosses actually knew what was going on while they were essentially gaslighting her. Which of them knew? So I did try to figure that out.

Lussenhop: Around the same time that Sharon was speaking with Kris, there were all these lawsuits against 3M going on over forever chemicals. Courts were forcing 3M to release more and more of their own internal records. New documents that Sharon could get her hands on. Some of them further confirmed that 3M had known that PFOS was in the blood of the general public before Kris started working there. 

Lerner: One of those documents was written by none other than Jim Johnson, her former boss. So I literally gasped when I read that.

Lussenhop: Jim Johnson had sent that document to another employee — the guy who ended up becoming Kris’ second boss. 

Lerner: These were Kris’ two bosses, one who had given her the assignment, the other one who had never let on that he knew anything about what she was talking about. So both of them knew the whole time. That’s what that showed me.

Lussenhop: Jim Johnson kind of just dropped this task on her and walked away, and it was a question he was asking her to answer that he already knew the answer to. I mean, that’s just, like, it just feels like you have, like, gift-wrapped … 

Lerner: A turd? 

Lussenhop: Yeah. He just, he dropped, he dropped a turd on her desk and walked away. 

Lerner: Yeah. So Jim Johnson, I knew I had to reach out to him. I did not expect him to talk to me, but happily, uh, when I called, I was surprised to find that he did stay on the phone. What Jim Johnson said to me is that he saw himself as a good soldier and a loyal soldier, someone who will keep secrets because that’s what you do for a good employer.

Lussenhop: Did you ever get a satisfying answer from him about why he did what he did with Kris and sort of sent her down this rabbit hole?

Lerner: He said that he was frustrated that the information about, uh, its presence in blood hadn’t become public, and that he basically wanted it to come out, and that he planted the seed by asking Kris to do the work analyzing the blood from the lab. He kind of set it all in motion, and he did it because he wanted it to come out.

Lussenhop: There’s something very puppetmaster-y about that.

Lerner: Yeah, the analogy he used, he said he compared her to a vending machine. He said he knew what he was putting in, and he got what he expected out.

Lussenhop: Oh my God.

Lerner: Yeah. And when I pointed out to him, “Well, you know, like, it’s actually been quite a trauma for her, very difficult.” He said, “I didn’t say I was a nice guy.”

Lussenhop: When Sharon reached out to Kris’ second boss, he told Sharon he didn’t remember the specifics about his interactions with Kris.

Lussenhop: What did Kris say when you told her about your conversation with Jim Johnson?

Lerner: She was upset.

PHONE CALL, Hansen: He was my boss, for Christ’s sake. 

PHONE CALL,  Lerner: Yeah. 

PHONE CALL, Hansen: What the hell? 

Lerner: I think it sent her reeling a little bit. To know for sure that these people who had watched her, you know, suffer in this way, and who had kind of exacerbated her suffering by questioning her, had actually known that she was right the whole time, I think it was, uh, anguish.

PHONE CALL, Hansen: Jim knew the historical data. He knew my data. His boss knew. So I, I just feel like it also implicates the others. And then they dragged me through that whole period of saying, you’re wrong, you’re wrong, you’re wrong. And they all knew that I was right. Makes me so mad. 

PHONE CALL, Lerner: Yeah. Well, Kris, I’m sorry to dump all this on you. 

PHONE CALL, Hansen: That’s OK. It’s — I have a therapist now. 

PHONE CALL, Lerner: That’s good. 

PHONE CALL, Hansen:  It’s all right. 

PHONE CALL, Lerner: Off to the therapist with you, then. 

Lerner: People, when they tell stories about what they’ve done in their lives to others and to themselves, they often see themselves as the hero, right? ’Cause they’re the hero of their own life. Um, and I think that Jim sort of saw himself as the hero, as someone who did his best to get this information out and did it by setting up Kris. But it did get me thinking about how secrets are kept.

Lussenhop: Yeah, it’s like — you know, a company is just a bunch of people. Why would they all kind of do their little part to keep this information a secret for so long?

Lerner: Well, I think that the genuine affection for the company was real.

Lussenhop: Yeah, I have to say that again, as someone from Minnesota, I really do understand this kind of just, like, general affection towards 3M. And I also think that the first time I learned about some of these chemicals and the effect that they were having, I didn’t wanna believe it either. Like, you just, like, there, there’s this roadblock in your mind. You just don’t want something to be true. You sort of, like, imagine all these kind of faceless prospective whistleblowers walking in and out of the building and not doing the right thing, and, and I, I have a hard time not seeing, like, family and friends and neighbors in those roles. I mean, again, like, a company’s just a bunch of people.

Lerner: Yes, and I think that’s really — the reporting of this piece really made me understand that. The way that I had thought about companies and whistleblowers was perhaps too simplistic. Sometimes you think about, you know, there’s a secret and then, and then, you know, let’s say the priests are abusing children sexually and then someone just, you know, drops off a box at The Boston Globe and boom, you know? And, and there, that is sort of the Hollywood narrative of things, like that there’s a dark secret and then it gets exploded in a moment and that’s certainly not how this played out. It isn’t all clear-cut like that. 

Lussenhop: In 2025, 3M phased out manufacturing PFAS. In a statement, a 3M spokesperson said, “3M has led industry in addressing PFAS in the environment.” The company is “proactively managing PFAS,” and that 3M’s approach to the chemicals has evolved along with “the science and technology of PFAS, societal and regulatory expectations, and our expectations of ourselves.” “3M has taken, and will continue to take, actions to address PFAS manufactured prior to the phaseout. This includes a $1 billion global investment in state-of-the-art water treatment technologies at our largest water-using locations. In 2024, 3M committed $10.3 billion to assist public water suppliers across the United States that had detected PFAS in their water or would do so in the future.”

The company and its scientists have not admitted wrongdoing or faced criminal liability for producing forever chemicals or for concealing their harms.

Sometimes Kris looks back on all of this and fantasizes about what she could have done differently. What would the best version of herself have done in that meeting with 3M’s CEO?

IN PERSON INTERVIEW, Hansen: The hero version of myself would say, let’s just wait until the CEO has finished his nap, and then, you know, he can tune back in because this is important. Superhero Kris would’ve been stronger, um, would have been more deliberate and more inclusive in the communications because I think sort of the wider you communicate things, the harder it is to control it.

Lerner: So is she a whistleblower? Is she not a whistleblower? I don’t know. She’s a human, you know, who found herself in this really difficult situation and helped a lot. She could’ve helped more, but she could’ve done a lot less.

Julia Longoria: This episode was produced by Gabrielle Berbey and me, Julia Longoria. Editing by Katherine Wells. Fact-checking by Darya Marchenkova. Sound design and mixing by David Herman. Music by Julian Sartorius, Filippo Ansaldi and Simone Sims Longo, with additional music by Epidemic Sound. Our team also includes Sabby Robinson and Emma Talkoff. 

This podcast is based on a story co-published by ProPublica and The New Yorker. And special thanks to Diane Wu and “This American Life” for providing the tapes of Kris Hansen that you hear in this episode.

The Daily Front Page 16 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — A Shape That Will Not Sit Still
article

Steffen's Polyhedron

by pavel_lishin·▲ 60 points·5 comments·gregegan.net ↗
a polyhedron that can change shape while maintaining the shapes of all its individual faces.

Steffen's polyhedron

Steffen's polyhedron (translucent rear view)

Steffen’s polyhedron changing shape

Translucent version seen from behind

Steffen’s polyhedron is a flexible polyhedron: a polyhedron that can change shape while maintaining the shapes of all its individual faces. It has been proved that no convex polyhedron can be flexible. The first non-self-intersecting flexible polyhedron with the topology of a sphere was found by Robert Connelly in 1977, but Klaus Steffen’s version has fewer faces, with just 14 triangles.

(For some time it was believed that the nine vertices of Steffen’s polyhedron was the minimum possible, but in 2024 a flexible polyhedron with just eight vertices was found by Gallet et al. Elvar Atlason, who kindly pointed out this new development to me, has publishing a detailed analysis of the problem, and constructed another solution with eight vertices.)

It’s not hard to see how Steffen’s polyhedron is constructed. First, consider the flexible shape we obtain by assembling four suitable triangles into a kind of pyramid:

Flexing pyramid

This shape has one degree of freedom: we can alter, for example, the length of one of the diagonals of the non-planar quadrilateral ABCD that forms the base of the pyramid. We choose AB=CD=12, BC=AD=10, AP=10, BP=5, CP=12 and DP=11. (There’s nothing special about the particular numbers 5, 10, 11 and 12, but it is crucial that opposite edges of the quadrilateral have the same lengths.)

Now, suppose we attach two more triangles to this figure, along the edges AD and CD, with the two triangles sharing an edge, DE:

Flexing pyramid with two extra triangles

Specifically, we set DE=5, AE=12 and CE=10. Adding these two triangles does not introduce any more freedom to the shape, since the location of E is determined by the edges AD and CD (up to a choice of which side of the plane ACD it lies on). Perhaps surprisingly, though, even as the quadrilateral ABCD flexes, the distance BE remains constant (at 11), so the angle ECB also remains unchanged. The reason for this is that the triangle ADE is congruent to the triangle CBP, and the triangle CDE is congruent to the triangle ABP. So E is the apex of a second pyramid congruent to the first, BCE is congruent to DAP, and BE=DP=11.

To build Steffen’s polyhedron, we take two of these six-triangle figures and join them along the edges EC and CB. Specifically, we join the edge E1C1 of the first figure to the edge B2C2 of the second, and we join the edge B1C1 of the first figure to the edge E2C2 of the second. We know we can make this join regardless of the particular shapes of the two quadrilateral bases, because the angle ECB is independent of the shape. So, this assembly of 12 triangles has two degrees of freedom.

Steffen's polyhedron (two faces removed)

Steffen's polyhedron (two faces removed, translucent)

Steffen’s polyhedron with two faces removed

Translucent version

To complete the polyhedron, we add triangular faces A1 B1 A2 and A2 B2 A1, and then to ensure that these faces have a constant shape, we use up one degree of freedom to impose the requirement that A1 A2 has a particular fixed length of 17. But we still have a degree of freedom left over, so the resulting polyhedron is free to flex.

(B1 B2 is fixed at 11, regardless of the specific shapes of the quadrilateral bases, but if we tried to use B1 B2 rather than A1 A2 to split A1 B1 A2 B2 into two triangular faces, they would intersect other faces of the polyhedron.)

Here’s a net for the polyhedron: a mapping of its faces into the plane, which can be printed, cut out, and folded up. Dashed lines between faces should be folded like valleys, unbroken lines like mountains. The face colours match the images above, but in order for the single-sided colours on the paper to end up on the exterior of the polyhedron, the two pyramids (viewed from the coloured side) will look like pits.

Net for Steffen's polyhedron

Net for Steffen’s polyhedron

The Daily Front Page 17 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — The Compression Cabinet
repository

Balrogg: Demonically compacting (up to 15%) lossless Vorbis/Opus recompressor

by palaiologos·▲ 75 points·10 comments·github.com ↗
★ 54⑂ 0 forks C

demonically compacting (up to 15%) lossless Vorbis/Opus recompressor

balrogg losslessly recompresses Ogg Vorbis and Opus files. Archives are typically 8-12% smaller than .ogg files and 3-8% smaller than .opus files.

Releases are not backwards or forwards compatible until v2.0 is reached.

balrogg is licensed under GNU GPL version 3. See COPYING. Report issues to Kamila Szewczyk k@iczelia.net. The project is hosted at https://github.com/iczelia/balrogg.

Quick start

balrogg e music.ogg music.blr
balrogg d music.blr music.ogg
balrogg -b e *.ogg *.opus
balrogg --progress -9 e music.ogg music.blr

e compresses and d expands. balrogg detects the codec. With -b, every remaining path is processed using the available cores and memory. Encoding appends .blr; decoding removes it. Larger inputs run first.

-p or --progress displays a progress bar on stderr for encoding and decoding, with each Vorbis tuning trial identified separately. Use --progress-lines for logs. Batch progress uses separate lines labeled with the input filename.

Installation

Use your package manager or download a binary from GitHub Releases. To build a release tarball, run

./configure
make
sudo make install

The project uses C99 and only the C and math libraries. The Opus parser under src/opus is derived from libopus.

Configure option Effect
--enable-sanitizers Enable ASan and UBSan for tests
--disable-simd Build the portable mixer only
--with-windows-target=win95 Target Windows 95 on an i486 (MinGW, 32-bit)

Run ./bootstrap first when building from a Git checkout. It requires autoconf and automake.

Effort

-1 through -9 select effort; the default is -9. Through -4, each level adds a residue-model stage and affects decoding. Higher levels only expand the parameter search, so -4 through -9 decode at the same speed. Vorbis tuning evaluates the complete file at each selected setting. The best candidate is retained in the destination file, with at most the best and current candidate present during a trial.

Results for a five-megabyte music file follow.

Level Vorbis Opus
-1 8.3 percent smaller at 6.4 MB/s 4.0 percent smaller at 4.2 MB/s
-9 9.8 percent smaller at 2.9 MB/s 4.8 percent smaller at 2.4 MB/s

Caveats

The encoder refuses files it cannot reproduce exactly, including files with bad checksums, invalid page sequences, unsupported Vorbis features, or no final end-of-stream page.

Vorbis packets with extra padding or alternative floor subclass choices retain normal floor and residue compression. Classword corrections and padding are modeled separately. Shortened packets (packet peeling) use an adaptive byte model.

Opus support is limited to one mono or stereo logical stream with channel mapping family 0. Packets, including OpusHead and OpusTags, are limited to 61,440 bytes; extended frame headers and padding are supported within that limit. Chained and multichannel Opus files are refused. Refusals produce a diagnostic and exit status 1.

Exit status

Code Meaning
0 Success
1 Malformed, unsupported, or unrecognized input
2 Usage error
3 File access error
4 Internal error

Batch mode returns the highest nonzero status reported by any file.

Performance and portability

Each file uses one thread and requires seekable input and output files. Vorbis model pages are allocated only when used, without relying on deferred OS allocation.

Supported hosts apply a 2 GiB process memory cap to reject unreasonable allocations. Set BLR_MEMCAP to another size in MiB, or to 0 to disable the cap.

Archives are portable across hosts. Windows builds use MinGW as follows:

./configure --host=x86_64-w64-mingw32 CC=x86_64-w64-mingw32-gcc LDFLAGS=-static
make

A Windows 95 build uses a small KERNEL32 runtime and targets i486.

./configure --host=i686-w64-mingw32 --with-windows-target=win95 \
            CC=i686-w64-mingw32-gcc LDFLAGS=-static
make && make win95-check

win95-check verifies the loader baseline, PE flags, and KERNEL32 imports. An MS-DOS build uses DJGPP and needs an i386 with a DPMI host such as CWSDPMI.

./configure --host=i586-pc-msdosdjgpp CC=i586-pc-msdosdjgpp-gcc
make

DOS batch mode replaces the extension and runs serially. song.ogg becomes song.blr, then expands to song.ogg or song.opu for Opus.

The Daily Front Page 18 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — The Fluid Question
article

Finite time blowup for an averaged three-dimensional Navier-Stokes equation (2014)

by gmays·▲ 66 points·34 comments·terrytao.wordpress.com ↗
the “supercriticality barrier” for the global regularity problem for the Navier-Stokes equation

I’ve just uploaded to the arXiv the paper “Finite time blowup for an averaged three-dimensional Navier-Stokes equation“, submitted to J. Amer. Math. Soc.. The main purpose of this paper is to formalise the “supercriticality barrier” for the global regularity problem for the Navier-Stokes equation, which roughly speaking asserts that it is not possible to establish global regularity by any “abstract” approach which only uses upper bound function space estimates on the nonlinear part of the equation, combined with the energy identity. This is done by constructing a modification of the Navier-Stokes equations with a nonlinearity that obeys essentially all of the function space estimates that the true Navier-Stokes nonlinearity does, and which also obeys the energy identity, but for which one can construct solutions that blow up in finite time. Results of this type had been previously established by Montgomery-Smith, Gallagher-Paicu, and Li-Sinai for variants of the Navier-Stokes equation without the energy identity, and by Katz-Pavlovic and by Cheskidov for dyadic analogues of the Navier-Stokes equations in five and higher dimensions that obeyed the energy identity (see also the work of Plechac and Sverak and of Hou and Lei that also suggest blowup for other Navier-Stokes type models obeying the energy identity in five and higher dimensions), but to my knowledge this is the first blowup result for a Navier-Stokes type equation in three dimensions that also obeys the energy identity. Intriguingly, the method of proof in fact hints at a possible route to establishing blowup for the true Navier-Stokes equations, which I am now increasingly inclined to believe is the case (albeit for a very small set of initial data).

To state the results more precisely, recall that the Navier-Stokes equations can be written in the form

\displaystyle  \partial_t u + (u \cdot \nabla) u = \nu \Delta u + \nabla p

for a divergence-free velocity field {u} and a pressure field {p}, where {\nu>0} is the viscosity, which we will normalise to be one. We will work in the non-periodic setting, so the spatial domain is {{\bf R}^3}, and for sake of exposition I will not discuss matters of regularity or decay of the solution (but we will always be working with strong notions of solution here rather than weak ones). Applying the Leray projection {P} to divergence-free vector fields to this equation, we can eliminate the pressure, and obtain an evolution equation

[\displaystyle  \partial_t u = \Delta u + B(u,u) \ \ \ \ \ (1)]

purely for the velocity field, where {B} is a certain bilinear operator on divergence-free vector fields (specifically, {B(u,v) = -\frac{1}{2} P( (u \cdot \nabla) v + (v \cdot \nabla) u)}. The global regularity problem for Navier-Stokes is then equivalent to the global regularity problem for the evolution equation (1).

An important feature of the bilinear operator {B} appearing in (1) is the cancellation law

\displaystyle  \langle B(u,u), u \rangle = 0

(using the {L^2} inner product on divergence-free vector fields), which leads in particular to the fundamental energy identity

\displaystyle  \frac{1}{2} \int_{{\bf R}^3} |u(T,x)|^2\ dx + \int_0^T \int_{{\bf R}^3} |\nabla u(t,x)|^2\ dx dt = \frac{1}{2} \int_{{\bf R}^3} |u(0,x)|^2\ dx.

This identity (and its consequences) provide essentially the only known a priori bound on solutions to the Navier-Stokes equations from large data and arbitrary times. Unfortunately, as discussed in this previous post, the quantities controlled by the energy identity are supercritical with respect to scaling, which is the fundamental obstacle that has defeated all attempts to solve the global regularity problem for Navier-Stokes without any additional assumptions on the data or solution (e.g. perturbative hypotheses, or a priori control on a critical norm such as the {L^\infty_t L^3_x} norm).

Our main result is then (slightly informally stated) as follows

Theorem 1 There exists an averaged version {\tilde B} of the bilinear operator {B}, of the form

\displaystyle \tilde B(u,v) := \int_\Omega m_{3,\omega}(D) Rot_{3,\omega}

\displaystyle B( m_{1,\omega}(D) Rot_{1,\omega} u, m_{2,\omega}(D) Rot_{2,\omega} v )\ d\mu(\omega)

for some probability space {(\Omega, \mu)}, some spatial rotation operators {Rot_{i,\omega}} for {i=1,2,3}, and some Fourier multipliers {m_{i,\omega}} of order {0}, for which one still has the cancellation law

![\displaystyle \langle \tilde B(u,u), u \rangle = 0](https://s0.wp.com/latex.php?latex=%5Cdisplaystyle%20%20%5Clangle%20%5Ctilde B%28u%2Cu%29%2C%20u%20%5Crangle%20%3D%200&bg=ffffff&fg=000000&s=0&c=20201002)

and for which the averaged Navier-Stokes equation

[\displaystyle \partial_t u = \Delta u + \tilde B(u,u) \ \ \ \ \ (2)]

admits solutions that blow up in finite time.

(There are some integrability conditions on the Fourier multipliers {m_{i,\omega}} required in the above theorem in order for the conclusion to be non-trivial, but I am omitting them here for sake of exposition.)

Because spatial rotations and Fourier multipliers of order {0} are bounded on most function spaces, {\tilde B} automatically obeys almost all of the upper bound estimates that {B} does. Thus, this theorem blocks any attempt to prove global regularity for the true Navier-Stokes equations which relies purely on the energy identity and on upper bound estimates for the nonlinearity; one must use some additional structure of the nonlinear operator {B} which is not shared by an averaged version {\tilde B}. Such additional structure certainly exists – for instance, the Navier-Stokes equation has a vorticity formulation involving only differential operators rather than pseudodifferential ones, whereas a general equation of the form (2) does not. However, “abstract” approaches to global regularity generally do not exploit such structure, and thus cannot be used to affirmatively answer the Navier-Stokes problem.

It turns out that the particular averaged bilinear operator {B} that we will use will be a finite linear combination of local cascade operators, which take the form

\displaystyle C(u,v) := \sum_{n \in {\bf Z}} (1+\epsilon_0)^{5n/2} \langle u, \psi_{1,n} \rangle \langle v, \psi_{2,n} \rangle \psi_{3,n}

where {\epsilon_0>0} is a small parameter, {\psi_1,\psi_2,\psi_3} are Schwartz vector fields whose Fourier transform is supported on an annulus, and {\psi_{i,n}(x) := (1+\epsilon_0)^{3n/2} \psi_i( (1+\epsilon_0)^n x)} is an {L^2}-rescaled version of {\psi_i} (basically a “wavelet” of wavelength about {(1+\epsilon_0)^{-n}} centred at the origin). Such operators were essentially introduced by Katz and Pavlovic as dyadic models for {B}; they have the essentially the same scaling property as {B} (except that one can only scale along powers of {1+\epsilon_0}, rather than over all positive reals), and in fact they can be expressed as an average of {B} in the sense of the above theorem, as can be shown after a somewhat tedious amount of Fourier-analytic symbol manipulations.

If we consider nonlinearities {\tilde B} which are a finite linear combination of local cascade operators, then the equation (2) more or less collapses to a system of ODE in certain “wavelet coefficients” of {u}. The precise ODE that shows up depends on what precise combination of local cascade operators one is using. Katz and Pavlovic essentially considered a single cascade operator together with its “adjoint” (needed to preserve the energy identity), and arrived (more or less) at the system of ODE

[\displaystyle \partial_t X_n = - (1+\epsilon_0)^{2n} X_n + (1+\epsilon_0)^{\frac{5}{2}(n-1)} X_{n-1}^2 - (1+\epsilon_0)^{\frac{5}{2} n} X_n X_{n+1} \ \ \ \ \ (3)]

where {X_n: [0,T] \rightarrow {\bf R}} are scalar fields for each integer {n}. (Actually, Katz-Pavlovic worked with a technical variant of this particular equation, but the differences are not so important for this current discussion.) Note that the quadratic terms on the RHS carry a higher exponent of {1+\epsilon_0} than the dissipation term; this reflects the supercritical nature of this evolution (the energy {\frac{1}{2} \sum_n X_n^2} is monotone decreasing in this flow, so the natural size of {X_n} given the control on the energy is {O(1)}). There is a slight technical issue with the dissipation if one wishes to embed (3) into an equation of the form (2), but it is minor and I will not discuss it further here.

In principle, if the {X_n} mode has size comparable to {1} at some time {t_n}, then energy should flow from {X_n} to {X_{n+1}} at a rate comparable to {(1+\epsilon_0)^{\frac{5}{2} n}}, so that by time {t_{n+1} \approx t_n + (1+\epsilon_0)^{-\frac{5}{2} n}} or so, most of the energy of {X_n} should have drained into the {X_{n+1}} mode (with hardly any energy dissipated). Since the series {\sum_{n \geq 1} (1+\epsilon_0)^{-\frac{5}{2} n}} is summable, this suggests finite time blowup for this ODE as the energy races ever more quickly to higher and higher modes. Such a scenario was indeed established by Katz and Pavlovic (and refined by Cheskidov) if the dissipation strength {(1+\epsilon)^{2n}} was weakened somewhat (the exponent {2} has to be lowered to be less than {\frac{5}{3}}). As mentioned above, this is enough to give a version of Theorem 1 in five and higher dimensions.

On the other hand, it was shown a few years ago by Barbato, Morandin, and Romito that (3) in fact admits global smooth solutions (at least in the dyadic case {\epsilon_0=1}, and assuming non-negative initial data). Roughly speaking, the problem is that as energy is being transferred from {X_n} to {X_{n+1}}, energy is also simultaneously being transferred from {X_{n+1}} to {X_{n+2}}, and as such the solution races off to higher modes a bit too prematurely, without absorbing all of the energy from lower modes. This weakens the strength of the blowup to the point where the moderately strong dissipation in (3) is enough to kill the high frequency cascade before a true singularity occurs. Because of this, the original Katz-Pavlovic model cannot quite be used to establish Theorem 1 in three dimensions. (Actually, the original Katz-Pavlovic model had some additional dispersive features which allowed for another proof of global smooth solutions, which is an unpublished result of Nazarov.)

To get around this, I had to “engineer” an ODE system with similar features to (3) (namely, a quadratic nonlinearity, a monotone total energy, and the indicated exponents of {(1+\epsilon_0)} for both the dissipation term and the quadratic terms), but for which the cascade of energy from scale {n} to scale {n+1} was not interrupted by the cascade of energy from scale {n+1} to scale {n+2}. To do this, I needed to insert a delay in the cascade process (so that after energy was dumped into scale {n}, it would take some time before the energy would start to transfer to scale {n+1}), but the process also needed to be abrupt (once the process of energy transfer started, it needed to conclude very quickly, before the delayed transfer for the next scale kicked in). It turned out that one could build a “quadratic circuit” out of some basic “quadratic gates” (analogous to how an electrical circuit could be built out of basic gates such as amplifiers or resistors) that achieved this task, leading to an ODE system essentially of the form

\displaystyle \partial_t X_{1,n} = - (1+\epsilon_0)^{2n} X_{1,n}

\displaystyle + (1+\epsilon_0)^{5n/2} (- \epsilon^{-2} X_{3,n} X_{4,n} - \epsilon X_{1,n} X_{2,n} - \epsilon^2 \exp(-K^{10}) X_{1,n} X_{3,n}

\displaystyle + K X_{4,n-1}^2)

\displaystyle \partial_t X_{2,n} = - (1+\epsilon_0)^{2n} X_{2,n} + (1+\epsilon_0)^{5n/2} (\epsilon X_{1,n}^2 - \epsilon^{-1} K^{10} X_{3,n}^2)

\displaystyle \partial_t X_{3,n} = - (1+\epsilon_0)^{2n} X_{3,n} + (1+\epsilon_0)^{5n/2} (\epsilon^2 \exp(-K^{10}) X_{1,n}^2

\displaystyle + \epsilon^{-1} K^{10} X_{2,n} X_{3,n} )

\displaystyle \partial_t X_{4,n} =- (1+\epsilon_0)^{2n} X_{4,n} + (1+\epsilon_0)^{5n/2} (\epsilon^{-2} X_{3,n} X_{1,n}

\displaystyle - (1+\epsilon_0)^{5/2} K X_{4,n} X_{1,n+1})

where {K \geq 1} is a suitable large parameter and {\epsilon > 0} is a suitable small parameter (much smaller than {1/K}. To visualise the dynamics of such a system, I found it useful to describe this system graphically by a “circuit diagram” that is analogous (but not identical) to the circuit diagrams arising in electrical engineering:

circuit-1

The coupling constants here range widely from being very large to very small; in practice, this makes the {X_{2,n}} and {X_{3,n}} modes absorb very little energy, but exert a sizeable influence on the remaining modes. If a lot of energy is suddenly dumped into {X_{1,n}}, what happens next is roughly as follows: for a moderate period of time, nothing much happens other than a trickle of energy into {X_{2,n}}, which in turn causes a rapid exponential growth of {X_{3,n}} (from a very low base). After this delay, {X_{3,n}} suddenly crosses a certain threshold, at which point it causes {X_{1,n}} and {X_{4,n}} to exchange energy back and forth with extreme speed. The energy from {X_{4,n}} then rapidly drains into {X_{1,n+1}}, and the process begins again (with a slight loss in energy due to the dissipation). If one plots the total energy {E_n := \frac{1}{2} ( X_{1,n}^2 + X_{2,n}^2 + X_{3,n}^2 + X_{4,n}^2 )} as a function of time, it looks schematically like this:

energy-blowup

As in the previous heuristic discussion, the time between cascades from one frequency scale to the next decay exponentially, leading to blowup at some finite time {T}. (One could describe the dynamics here as being similar to the famous “lighting the beacons” scene in the Lord of the Rings movies, except that (a) as each beacon gets ignited, the previous one is extinguished, as per the energy identity; (b) the time between beacon lightings decrease exponentially; and (c) there is no soundtrack.)

There is a real (but remote) possibility that this sort of construction can be adapted to the true Navier-Stokes equations. The basic blowup mechanism in the averaged equation is that of a von Neumann machine, or more precisely a construct (built within the laws of the inviscid evolution {\partial_t u = \tilde B(u,u)}) that, after some time delay, manages to suddenly create a replica of itself at a finer scale (and to largely erase its original instantiation in the process). In principle, such a von Neumann machine could also be built out of the laws of the inviscid form of the Navier-Stokes equations (i.e. the Euler equations). In physical terms, one would have to build the machine purely out of an ideal fluid (i.e. an inviscid incompressible fluid). If one could somehow create enough “logic gates” out of ideal fluid, one could presumably build a sort of “fluid computer”, at which point the task of building a von Neumann machine appears to reduce to a software engineering exercise rather than a PDE problem (providing that the gates are suitably stable with respect to perturbations, but (as with actual computers) this can presumably be done by converting the analog signals of fluid mechanics into a more error-resistant digital form). The key thing missing in this program (in both senses of the word) to establish blowup for Navier-Stokes is to construct the logic gates within the laws of ideal fluids. (Compare with the situation for cellular automata such as Conway’s “Game of Life“, in which Turing complete computers, universal constructors, and replicators have all been built within the laws of that game.)

The Daily Front Page 19 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — After 1.5 Degrees
article

Global warming will exceed 1.5-degree limit, UN says

by ijidak·▲ 316 points·356 comments·pbs.org ↗
Earth has lost its chance to prevent some of the worst of global warming's harms.

Hikers walk on the moraine at the Mont Mine glacier near Evolene

In a strategic retreat in the fight against climate change, the United Nations has acknowledged that Earth has lost its chance to prevent some of the worst of global warming's harms. So now the world must figure out how to get temperatures below the danger zone and back to safety, officials say.

Earth is soon going to exceed the safe temperature threshold agreed upon by the world in the 2015 Paris climate accord because of warming from the burning of coal, oil and gas, according to a bleak first-of-its-kind report issued by the United Nations Environment Programme Wednesday.

Instead of giving up, scientists and U.N. officials recommend trying to limit excessive warming and then bring the temperature back down to the agreed-upon threshold. But it still will mean decades of increased weather-related disasters, rising seas, extinctions and melting ice and glaciers.

WATCH: Study warns 1.5-degree warming limit can't prevent dangers of melting glaciers

"Humanity is speeding past the limit to avoid climate catastrophe," United Nations Secretary-General António Guterres said.

After a period of over-the-limit warming, the new strategy envisions getting back below the limit by ending fossil fuel use and developing some kind of way to suck industries' carbon dioxide out of the air.

"It's a reality check. We have to live with this warmer world," said report co-author Richard Betts, a climate scientist at the University of Exeter. "But there's still a lot we can do to limit warming."

Passing the longtime warming threshold

After the Paris climate accord, the United Nations ordered its climate science arm to study different warming limits. A goal of 1.5 Celsius (2.7 Fahrenheit) above mid-19th century temperatures became generally accepted after the scientists found dramatic impacts that affect people and the planet at 2 degrees Celsius (3.8 degrees Fahrenheit) but not at 1.5 degrees.

The world is now at 1.4 C (2.5 F) above pre-industrial levels, some scientists have calculated. The measure is based on a 20-year average, rather than a single year's temperatures.

Despite experts saying there's no way to limit warming to 1.5 degrees, U.N. officials long maintained the 1.5 window was still open. Wednesday's report finally admitted that the world will cross the 1.5 threshold "in the next few years."

The report said the most optimistic scenario has warming peaking at 1.8 C (3.2 F) around mid-century if countries pursue ambitious climate goals, but that governments' current policies should push temperatures to 2.6 C (4.7 F) above pre-industrial levels by 2100.

So instead of preventing warming of 1.5 degrees, now the U.N. said it wants policymakers to "prepare for and navigate a future'' in which the globe crosses that limit and then corrects course and cools temperatures. The report envisions that global temperatures will pass 1.5 "within the next few years," follow a hump-shaped path of more warming, peak, then gradually cool to below the 1.5 mark, ideally by the end of the century. The report and experts call it "overshoot."

"Let's be very clear, 1.5 target is still the goal. But now we need to approach it differently from above," said UNEP Executive Director Inger Andersen.

The 1.5 limit is not "some magical climate cliff" where it's just game over, said University of Victoria climate scientist Andrew Weaver, who wasn't part of the study. "Every tenth of a degree we avoid still means less damage."

Limiting how high temperatures go past threshold is key

It's most important to first limit the new peak temperature and then reduce the amount of time above 1.5 degrees, Andersen said.

But the world likely will be in that overshoot period for "many, many decades," Betts said.

Every five years of delay in reducing emissions adds at least a tenth of a degree to peak warming, said report co-author Joeri Rogelj, a climate scientist at Imperial College London.

"If we act now we can limit overshoot," the report said. "Not a safe world, but safer than remaining above" 1.5 degrees for the long term.

"This is among the bleakest U.N. climate reports I have read. It carries the unmistakable tone of desperation, frustration and urgency — but not resignation," Weaver said.

Climate scientist Bill Hare, CEO of Climate Analytics, said that "although UNEP does a good job of describing the hole we've dug ourselves into, it does a poor job of showing us that there's a way out."

Adapting to a deadlier, hungrier, thirstier world

Governments need to increase efforts to adapt to worsening warming, which hits the poor hardest, "because some impacts simply cannot be avoided," Andersen said.

The report calls this phase "coping and containment."

During this overshoot period, more people will die or get sick because of the worsening climate change and its extreme weather, the report said. Food production will drop, people will go hungry, water shortages will worsen, economies will be hurt and species like coral will go extinct, the report said.

Although the triggers behind the recent deadly Nepal flooding that came as part of a Himalayan glacier dislodging are complex, Betts said "unfortunately we do expect to see more of such things and this is exactly why we need to limit peak warming."

The report warns of "tipping points" with irreversible and cascading effects, including the loss of the West Antarctic and Greenland ice sheets, the loss of the Amazon rain forest and the shutdown of the Atlantic Ocean circulation system that keeps much of Europe warm.

"Some locations might become more or less uninsurable or even uninhabitable," the report said.

The last phase in overshoot is cooling the planet by removing carbon dioxide from the air and storing it somewhere. The report said the traditional approach of this is planting more trees, but it says planting trees could, at most, shave a tenth of a degree off global temperatures. So the authors said more novel carbon removal approaches will be needed.

Other scientists called that unreliable and expensive.

"We were unable to stop the runaway train of global emissions to avoid 1.5 C of warming," said Stanford University climate scientist Rob Jackson, who wasn't part of the report. "Now overshoot scenarios require us to stop the train quickly and to back it up. Is it our best hope? Absolutely. Is it likely? Absolutely not."

The Daily Front Page 20 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Falcopolis
article

A Million Falcons Went Missing. Here’s How They Were Found

by bryanrasmussen·▲ 70 points·32 comments·nationalgeographic.com ↗
the secret winter home of rare red-footed falcons remained a mystery.

For years, the secret winter home of rare red-footed falcons remained a mystery. Then an ornithologist discovered the hidden world of “Falcopolis.”

Ornithologist Péter Palatitz raises an antenna near Mungo, Angola, to collect data from GPS trackers he and his team have put on red-footed falcons.

Most birds of prey are loners. They might form huge flocks during migration, but for the rest of the year, they stick to themselves. The red-footed falcon is different. This kestrel-size bird frequently travels and roosts in large groups across Eastern Europe and Central Asia. It even nests in colonies, with hundreds of pairs of slate gray males and buffy-red females all living together in what Hungarian ornithologist Péter Palatitz calls “super-busy cities.” Within these avian metropolises, the falcons fight for nests, steal from each other, mate outside their partnerships, and leave eggs for others to raise. For scientists interested in how birds behave, “this system is a heaven,” says Palatitz, who has studied the falcons for 20 years.

These raptors don’t build their own cities. Instead, they occupy the abandoned nests of the rook, a relative of crows. But rooks have long been seen as agricultural pests and were so thoroughly persecuted in the 1980s and ’90s that their population crashed. The red-footed falcons, with nowhere to nest, plummeted to a quarter of their former numbers. In 2006, Palatitz launched a plan to reverse that decline by placing thousands of nest boxes throughout Hungary. The falcons responded enthusiastically. Over the next decade, their numbers doubled.

As the falcons rebounded, Palatitz took the opportunity to answer some long-standing questions about these little-studied creatures. For example, he knew that they winter in Africa but not their route or specific destination—key information for understanding how the species spends half its life. So, between 2009 and 2015, Palatitz and his team fitted 28 individuals with satellite transmitters. The devices revealed that the birds, which weigh just four to seven ounces, can cross the Mediterranean and the Sahara in a nonstop five-day flight. A week later, they end their 6,000-mile trek in southern Africa, where they stay for months.

But in resolving one mystery, Palatitz unearthed another. The tracking data hinted that in March, before returning north, the red-footed falcons all pass through the same small stretch of central Angola. Why? Some of the team thought this was a coincidence, but Palatitz argued that the birds were gathering at a huge roosting site. He bet a beer that he was right, and began making plans to visit Angola.

Traveling in Angola can be risky, as more than 14,000 acres of land are still contaminated with mines—a legacy of the country’s nearly three decades of civil war. For years, Palatitz couldn’t get permission to go outside the capital, Luanda. But in late 2018, he was introduced to José “Zeca” Agostinho, an Angolan who had spent decades doing de-mining work, including in Huambo Province, where the falcons seemed to be congregating. Agostinho lined up the right paperwork, and “our 10-year dreams came together in three months,” Palatitz says. By March 2019, the team was on the road to Huambo. And on their second day of searching, Palatitz won his beer.

The team came across a site where falcons streamed in from all directions, blackening the sky. Some darted around at eye level. Others wheeled overhead. They perched shoulder to shoulder on every available branch. As he watched them, Palatitz started crying. It was like being “in a new dimension,” he says, “like I’m not part of the Earth but I’m more part of the sky.”

Man with two bags walking on the pass though corn field toward trees. Flying birds covering sky.

For a few weeks each year, the falcons migrate to this tiny patch of Angola, where they roost in tall mango and eucalyptus trees rising above agricultural plots.

Palatitz knew the falcons were sociable, but he had completely underestimated just how sociable they could be. In the summer, the birds are spread across thousands of miles from Hungary to Kazakhstan. But for six weeks in February and March, every last one passes through this tiny, 1.5-square-mile region of central Angola, which the team now calls “Falcopolis.”

True to their scientific name, Falco vespertinus—“falcon of the evening”—the birds begin to gather as sunlight dims. For over an hour, hundreds of thousands of them swirl above the trees and hover in place as they look for perches. “You can actually feel the wind from their wings on your face,” Agostinho tells me. Their droppings whitewash the underlying grass and fill the air with an ammonia-like scent. Their slate and red feathers blaze in the sunset, and he says that when they finally land, mere inches apart, “it’s like the trees are blossoming.”

No one knows exactly how many red-footed falcons congregate at Falcopolis. “Estimating them in the sky is impossible,” Palatitz says. Instead, his team is trying to count the birds by measuring the area over which they roost and the carrying capacity of those trees. If successful, the team will have an unprecedented way of counting a species that, in the summer, is scattered over a vast and largely unmonitored area. The red-footed falcon population has been estimated at up to 400,000 individuals. At Falcopolis, “I think I’ve seen one million birds in the air,” Palatitz says.

Two men with open hands letting a bird free.

Palatitz (left) and José “Zeca” Agostinho, who leads a conservation nonprofit, release a male red-footed falcon into the skies above Falcopolis—now considered one of the largest raptor roosting sites on Earth.

What compels these falcons to gather in such preposterous numbers? And why do they come to that particular part of Angola? “We don’t know,” Palatitz says. But he has some guesses. Falcopolis is on a peninsula surrounded by rivers and is easy to find from the air, he explains. Its climate is temperate and agreeable. It’s got plenty of large roosting trees. Human presence may have helped the falcons by suppressing mammalian competitors and predators. And since agriculture isn’t practiced intensively, there are still plenty of insects around. In particular, every March winged termites burst from the ground, swarming so densely that they look like static on an old television set.

Gorging on the termite bonanza, the falcons increase their weight by 20 to 30 percent, gaining the fat they need to return to their breeding grounds, against the Sahara’s headwinds. And Palatitz suspects that their communal nature might help them find the unpredictable swarms. Every night, the falcons regurgitate pellets full of undigested termite heads, which might allow the birds to see who among them is successfully hunting—and thus worth following to increase their own luck. This hypothesis is one of many ideas that Palatitz now wants to test. By collecting blood samples from the falcons, he hopes to figure out how they avoid a species-wide epidemic despite bringing in pathogens from across the world. And by tagging more individuals with GPS transmitters, he is trying to understand how their migratory behavior changes over time.

By migrating, the red-footed falcons exploit the riches of two hemispheres. But their versatility also creates vulnerability—a bird that depends on several nations is susceptible to threats in any one. In Angola, hunting is a problem. The civil war decimated the country’s native mammals and livestock, depriving many communities of protein. The falcons offer an alternative, and boys use flashlights to flush the birds out before killing them with slingshots and sticks. By surveying local markets, Agostinho at one point estimated that tens of thousands of falcons were being killed annually. And though hunting wild animals has been largely prohibited, regulations have been unevenly enforced and assumed by many to apply only to big game. The falcons’ habitat is also at risk. Pesticides, which are increasingly used, could devastate the termite swarms the falcons need. All of Falcopolis’s trees could easily be cut for firewood or burned to clear space for agricultural land.

Man and woman in red shirts surrounded by children listening to a man holding a grey bird.

When a falcon’s wings get wet, it can’t fly well and is susceptible to predators and poachers. Residents of Mungo bring these birds to local law enforcement who in turn hand them over to Palatitz’s team.

Man's hand holding a bird with small device on its back and wide open tail.

Palatitz hopes to use GPS data from tagged falcons to study their hunting behavior and migration patterns.

Conversely, Falcopolis’s small size also means that a small number of communities will determine the birds’ fate, and that Palatitz’s team knows where to concentrate its efforts. Agostinho, who coordinates with national authorities and now leads a conservation nonprofit called Orbis Angola, has been raising awareness about the falcons at surrounding schools and churches, and with the local *soba—*influential traditional leaders. The teams have set up a research camp to document the locals’ knowledge about the birds and enlist their help with banding and conservation measures. They’re also talking with donors about supplying people with gas stoves or solar cookers to reduce their need for firewood.

Meanwhile, news of Falcopolis is spreading quickly. At a recent conference, Angola’s president described it as a national treasure. The Ministry of the Environment went to the site, marking the first high-level governmental visit to the area in decades. A six-foot-tall falcon statue now stands in the main square of the nearest municipality, Mungo. And Palatitz’s team has started bringing international volunteers to Falcopolis, where Agostinho is training locals to guide future tourists.

These efforts are already paying off. When Agostinho first visited Mungo, he found falcons on sale in the market. “That’s gone completely,” he says. Perhaps more important, locals are increasingly venturing out of their homes at night to see the birds for themselves. And as they become invested, they’re coming up with their own ways of protecting the birds.

Earlier this year, five 12-year-old boys told Agostinho that they had formed a soccer team, the Mungo Falcons. They would use their matches as opportunities to educate similarly aged kids—the demographic most likely to hunt the birds—about protecting the raptors. They’d play for the falcons; they just needed a ball. So Agostinho bought this new generation of ambassadors their first one.

Red-footed falcons, like other spectacular migrants, show how connected our planet is—and needs to be. Their success depends on wild resources as disparate as rook nests in Europe and termite swarms in Africa. And their future will hinge upon equally far-flung feats of human grace, from nest boxes in Hungary to soccer balls in Angola.

Flying in huge number, birds covering the blue and orange sky at sunset.

At dusk, hundreds of thousands of red-footed falcons return from a long day of hunting in search of a place to roost. To witness it, says Palatitz, is like “being in a new dimension.”

The Daily Front Page 21 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Reviewing the Reviewers
article

GPT-6 Astra in code review: Gains, privacy, and cost

by cebert·▲ 71 points·68 comments·coderabbit.ai ↗
A change can look correct in isolation and still break code elsewhere in the system.

CodeRabbit model evaluation of OpenAI GPT-6 Astra, with a white CodeRabbit wordmark and dark green grid: code review gains, privacy, and costs.

Some of the hardest work in code review happens outside the changed lines. A change can look correct in isolation and still break code elsewhere in the system.

That is what makes our early results for OpenAI's GPT-6 Astra most interesting. In our evaluation, Astra caught approximately 4% more labeled bugs through actionable findings than GPT-5.6 Sol, and 22% more than Opus 5.

The biggest jump comes on harder cross-file reviews, where Astra's gains reach 20% over Sol and 33% over Opus 5. Using that capability at customer scale also means protecting customer data and assessing the model’s public API pricing.

What Astra added to code review

Our measure here is actionable bug coverage, meaning how many labeled bugs a model catches through findings a developer can act on.

Overall actionable bug coverage on a full 0–100% scale: GPT-6 Astra 61.3%, GPT-5.6 Sol 59.0%, and Opus 5 50.2%. Astra is first and highlighted in orange.

Coverage is rounded to one decimal; relative gains use unrounded values.

The overall gain over GPT-5.6 Sol appears modest in this first evaluation measure. This evaluation also includes simpler reviews, where there may be less room for a stronger model to differentiate itself; Astra's larger advantage appears in the harder cross-file subset. It is an early, directional result.

Cross-file actionable bug coverage on a full 0–100% scale: GPT-6 Astra 57.1%, GPT-5.6 Sol 47.6%, and Opus 5 42.9%. Astra is first and highlighted in orange.

Coverage is rounded to one decimal; relative gains use unrounded values. Compare models within this chart, since its review difficulty differs from the overall evaluation.

The harder cross-file comparison is more encouraging. Astra's relative advantage grows to 20% over Sol and 33% over Opus 5. That suggests value in connecting a change's intent to consequences distributed across a codebase.

These results describe one part of review performance. They do not establish an overall ranking of review quality, predict a team's defect rate, or promise the same gain on every pull request.

Putting context to work

A large context window creates room for information. Useful reasoning requires the model to identify which pieces matter, connect them, and reach a conclusion supported by evidence.

Our interpretation is that Astra's most interesting advance lies in connecting the right information. Its larger gains on harder cross-file reviews suggest progress on work where relevant information is distributed. They do not isolate the cause of that progress or prove that more context alone improves a model's performance.

OpenAI positions Astra for multistep work across code, browsers, and professional software.

For teams building with models, the useful question is where the extra reasoning changes the outcome enough to justify its cost. A difficult task with scattered evidence is a stronger candidate to investigate than a routine task that a less expensive model already handles reliably. That doesn't mean switching every session to Astra, maxing out reasoning effort, and letting it rip.

Stronger reasoning comes at a premium

Astra’s standard API rates are $10 per million input tokens and $50 per million output tokens, according to Astra’s published pricing. Fable 5.1 has the same base input and output rates, although caching prices differ. Anthropic’s pricing documentation provides the full breakdown.

To put those prices in context, consider an illustrative task using 100,000 uncached input tokens and 10,000 billable output tokens, including reasoning tokens. Holding token usage constant makes the published rates easier to compare; actual task costs vary with usage.

Estimated API cost at fixed token usage: GPT-6 Astra $1.50, GPT-5.6 Sol $0.60, GPT-5.6 Terra $0.32, and GPT-5.6 Luna $0.032. Astra is first and highlighted in orange.

All figures use publicly listed standard API prices, checked September 4, 2026. OpenAI figures use short-context rates. Sol's public promotional pricing is available at least through November 21, 2026. The example excludes caching, cache writes, tools, retries, regional uplifts, and service-tier adjustments. Real tasks can use different numbers of tokens.

Model Input / 1M tokens Output / 1M tokens Illustrative task cost
GPT-5.6 Luna $0.20 $1.20 $0.032
GPT-5.6 Terra $2.00 $12.00 $0.32
GPT-5.6 Sol $4.00 $20.00 $0.60
GPT-6 Astra $10.00 $50.00 $1.50
Claude Fable 5.1 $10.00 $50.00 $1.50

At that fixed usage, Astra costs 2.5 times Sol, about 4.7 times Terra, and about 47 times Luna. Those are meaningful premiums. They are not predictions of the difference in cost per completed task. A model that needs fewer tokens or fewer attempts could narrow the gap.

OpenAI reports lower estimated task costs for Astra in some of its own evaluations despite higher token prices. That makes total cost per successful outcome worth measuring on your work, rather than assuming that either token price or a capability score settles the decision. Read OpenAI's efficiency guidance.

What might transfer beyond code review

The transferable idea is reasoning over relationships among separate sources. Our findings suggest several uses worth evaluating; we have not measured Astra on these tasks:

  • Research synthesis: reconcile conflicting reports, connect claims to their evidence, and identify gaps that a summary of each document would miss.
  • Operational investigation: assemble a coherent explanation from logs, incident notes, and runbooks, while separating observations from hypotheses.
  • Requirements and policy analysis: trace a proposed change across specifications, internal policies, and implementation plans to flag inconsistencies for expert review.
  • Document and spreadsheet work: check whether assumptions, formulas, and narrative conclusions agree across a report and its supporting materials.

The common structure is scattered evidence with dependencies between its parts. Start with bounded work whose answer can be checked. The easiest way to test whether Astra is right for your workflows or products is to run it alongside your current model on the same tasks, then compare answer quality, verification time, and total cost.

Building and balancing NIGHTSHIFT

We also used Astra to build a whole game: NIGHTSHIFT, an action RPG made with Godot and GDScript. Its hardest problem was balancing the interactions between systems, then revisiting that balance as the game changed.

NIGHTSHIFT title screen, with the player character and glowing orange filaments.

NIGHTSHIFT, built with Astra through human direction and iteration.

That meant reasoning across seven character classes, a 988-node passive skill tree inspired by the legendary Path of Exile passive node tree, active skills, runes and socketable upgrades, skill evolutions, and co-op. Across 40 zones in 10 acts, the campaign introduces increasingly complex enemy swarms and combinations. Changing one class can also change which upgrades are useful, how a skill develops, and what a party can handle.

We made fundamental changes to core systems during development and asked Astra to work through the consequences and rebalance them. "Balance" in a game like this is the most difficult creative challenge for compelling gameplay. How can you make sweeping changes or introduce entirely new game systems and mechanics while keeping progression, combat, and difficulty coherent?

We also wanted players to discover overpowered endgame builds through clever combinations of classes, stats, items, passives, and active skills. The challenge was shaping a progression where reaching even mid-game was uncertain, but creativity and experimentation could pay off in spectacular ways. The reward for finding those combinations was working up to a build that could satisfyingly melt enemy swarms.

The process meant returning to the game between other work, giving Astra feedback, and giving it full autonomy to use its judgement in refining that balance. Astra also built native PS5 and Xbox controller support, native macOS, web, and Linux builds, and co-op. Co-op was particularly interesting, because Astra was able to build it out for playing on the same compute and LAN, which since we wanted to distribute to coworkers running macOS, it required generating a new Xcode project, App Store Connect account, establish multiple certificates and entitlements, and notarization.

Astra handled it all autonomously, only pausing to occasionally ask for authority and permissions it didn't already have. As a fun bonus, the game was also built for agents themselves to be players, which was somewhat surreal to be able to play live co-op sessions with Astra as a teammate. Several of us found it hard to put down. "I'm working on model evals" became a useful explanation for having a game open when a manager stopped by.

Watch the NIGHTSHIFT gameplay clip.

An earlier NIGHTSHIFT arcade prototype, shown in an 18-second automated gameplay demo.

The game gave us a creative setting to explore the same capability that stood out in code review. Astra had to reason about how each change affected the rest of the system.

What we want to see next

The next exciting advance would make this depth of reasoning dependable enough to use more often. That means consistent gains on difficult work, conclusions people can verify, and lower total cost for a successful outcome.

That calls for better selection of relevant context, clearer supporting evidence, and fewer unnecessary steps. It also means privacy-preserving deployment paths that make advanced capabilities usable under real customer commitments.

Astra gives us an encouraging signal on cross-file reasoning. The practical opportunity is to turn that capability into work that is both more useful and easier to trust. As with bringing any new model online at CodeRabbit, the evaluation is one part of that decision.

Protecting customer data

Neither CodeRabbit nor our model providers train AI models on CodeRabbit customers' proprietary code or personal information collected during private code reviews. Read our privacy policy.

OpenAI and Anthropic have different data-retention policies:

  • OpenAI: GPT-6 Astra supports zero data retention for eligible API customers. OpenAI’s API data controls describe ZDR eligibility and supported capabilities. Astra announcement, OpenAI data controls.
  • Anthropic: Fable requires 30-day retention by default for safety monitoring, but eligible customers can now use Fable 5 and 5.1 with ZDR while Enterprise Frontier Safeguards is being introduced. For products serving other businesses, that option requires terms agreed with Anthropic. EFS is designed to keep retained activity data in customer-controlled infrastructure. Covered-model retention policy, Enterprise Frontier Safeguards, eligibility details.

Any model we use for customer reviews must meet our data-protection requirements.

The Daily Front Page 22 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Developer Desk
article

.gitignore Everything by Default

by der_gopher·▲ 166 points·162 comments·packagemain.tech ↗

An opposite view on .gitignore files.

I think we’ve all been there. You’re working on a project, making commits, and then suddenly realize you’ve been committing .DS_Store files, node_modules, IDE configuration files, or other junk (CLAUDE.md for example) that shouldn’t be in your repository. Or even worse - environment variables. Then comes the embarrassing cleanup: adding these files to .gitignore, removing them from the repository history, and hoping that no one has noticed.

What if we flipped this approach entirely? Instead of allowing everything by default and selectively ignoring files, what if we ignored everything by default and only allowed specific files?

Here’s what this could look like in practice for a simple Go project:

*
!.gitignore
!*.go
!README.md
!go.mod
!go.sum

This .gitignore file does exactly that:

  • * - Ignore everything

But not these files...

  • !.gitignore - the gitignore file itself
  • !*.go - Go source files
  • !go.mod - Go module file
  • !go.sum - Go dependencies file
  • anything else you wish to include

With this setup, only the files you explicitly allow will be tracked by Git. No more accidental commits of unintended local files.

The technique isn’t necessarily the right choice for every repository or developer, but is an alternative to explore.

Nowadays, I see that projects have so much crap locally, like various agentic docs or subfolders, so it may seem easier to just ignore everything at first.

Just look at this 207-lines .gitignore from typescript-go.

p.s. if you want to check if the path is ignored by git, you can run this command:

git check-ignore -v internal/server/server.go

This can save a lot of head-scratching when a file you expected to track doesn’t appear in Git.

p.s. have you heard of lazygit? The best Git TUI out there, check it out.

The Daily Front Page 23 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Developer Desk
article

Delidded Intel I9-14900KS CT Scan

by willx86·▲ 89 points·4 comments·lttlabs.com ↗

Two years ago we CT(Computational Tomography) scanned a delidded Intel Core i9-13900K using our Lumafield Neptune CT scanner.

View Interactive

Intel Core i9-13900K CT Scan

It provides an interesting view of the CPU and you can see the structure of the cores in the silicon.

This time we delidded an Intel Core i9-14900KS for another project and we've taken the opportunity to give it a scan. The resulting scan in the window below is interactive, or you can "Explore the Scan" in another tab.

View Interactive

Intel Core i9-14900KS CT Scan

Scan Settings

For anyone curious, these are the settings we used in the CT scanner. Most of these are specific to the Neptune and uninteresting, but we'll note that we have the 120 kV Neptune and set a scan duration of 720 minutes(12 hours). A 2.5 mm copper filter was used to harden the X-ray beam.

Intel Core i9-14900KS CT Scan Settings

Source: https://voyager.lumafield.com/project/819f5f6f-a80d-40ed-bc9b-e5189967a647

Rear Components

The components populating the rear of the CPU PCB are a collection of capacitors to support the silicon circuitry. The transistors on the silicon are tiny - Intel's Intel 7 process node - but they require a lot of power, often very suddenly. These capacitors serve to supply that power and smooth out any noise that could be generated by longer circuit paths.

Intel i9-14900KS Components

While the VRMs(Voltage Regulation Modules) on the motherboard also work hard to smooth out power draw, they are "far away" from the transistors so there are physical limits to what they can do. The capacitors on the rear of the PCB - and even designed directly into the silicon of the die - will supply energy for higher frequency and faster power spikes.

Layer Slices

Intel Core i9-14900KS Slice Animation (Bottom to Top)

We've been able to differentiate three PCB layers as well as the internal vias.

Intel Core i9-14900KS Rear Components Slice

As can be partially seen in the top layer capture above, we're also able to capture what we believe is some of the silicon structure. There is some CT scan noise introduced due to the dense capacitor components beneath the silicon, but the patterns created in that noise appear distinct from the silicon patterns.

Intel i9-14900KS Silicon Slice

Silicon Structure

Comparing the capture of the CT scan to the actual layout of the die, you can identify the E- and P-cores. The horizontal line down the centre of the chip houses cache and bus, with the left and right sides populated with the integrated GPU, media engines, and supporting circuitry.

Intel i9-14900KS CT Scan

Let us know if you can identify anything else interesting from these scans!

The Daily Front Page 24 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Short Takes: Work & Life
article

LLMs as a Cognitive Virus

by canjobear·▲ 242 points·184 comments·arxiv.org ↗

Abstract: Large-language models (LLMs) are rapidly becoming part of human culture, reshaping how information is produced, transmitted, and used. Here we propose that their diffusion can be understood through a viral analogy, with LLM use spreading through populations, becoming embedded in cognitive and cultural practices. We model transitions among uncoupled, coupled, and persistently dependent users, and show that the interplay between social transmission, recovery, and collective reinforcement can generate tipping points and technological lock-in. A central consequence is the possibility of runaway dynamics: once a critical threshold is crossed, small increases in adoption can trigger rapid population-level shifts toward persistent dependence, with abrupt losses in cognitive competence. The same framework, however, identifies conditions for cognitive immunization, based on reducing transmission and facilitating reversibility. Our results highlight how LLM adoption may involve nonlinear collective transitions with important consequences for cognitive autonomy.

The Daily Front Page 25 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Short Takes: Work & Life
article

Wikimedia Foundation Workers Overwhelmingly Vote to Form Union with CWA

by robin_reala·▲ 274 points·143 comments·wikiworkersunited.org ↗

San Francisco, Calif. – U.S.-based workers at Wikimedia Foundation—the nonprofit organization that hosts Wikipedia and sister projects—have won their union election, voting overwhelmingly to join Communications Workers of America (CWA) Local 9415. The victory marks an important milestone in a global movement among Wikimedia Foundation workers to establish collective bargaining through the legal frameworks available in their respective countries.

U.S.-based workers began organizing as a part of the broader Wiki Workers United global union campaign of Wikimedia Foundation workers committed to strengthening worker voice, transparency, equity, and shared stewardship.

“The recognition of our union’s first government-sanctioned bargaining unit is a huge milestone in our over ten-year journey to educate, agitate, and organize coworkers,” said Bryan Davis, a WWU organizing committee and WWU-US/CWA member. “We continue our petition for recognition in the U.K. and hope to see more jurisdiction-specific units call for voluntary recognition in the coming weeks and months. Solidarity will continue to drive our discussions, planning, and actions as we begin working on our collective goals for contract bargaining to support all WMF workers regardless of legal jurisdiction, job duty, or hours of employment.”

In July, U.S.-based workers demanded union recognition after a supermajority of them signed union authorization cards. When management refused to uphold the organization’s commitment to respect the rights of workers to freely choose union representation, workers persevered through a union election overseen by the National Labor Relations Board (NLRB).

“Today’s victory is a demonstration of the power of international solidarity,” said Decovan Rhem, President of CWA Local 9415. “WWU-US/CWA members organized across their workplaces and across the world, building support among coworkers internationally and among an incredible community of volunteers and wiki editors. Now, we will take the power our members have built to the bargaining table to negotiate a strong contract that gives Wikimedia workers the voice they have long deserved.”

WWU extends its sincere gratitude to the Wikimedia volunteer community. Over 2,000 volunteers and community members who have collectively made more than 15 million edits have publicly signed petitions supporting Wikimedia Foundation workers and affirming the importance of giving workers a meaningful voice in shaping the future of the movement. This petition is the most supported petition in history on the English-language Wikipedia. The WWU Organizing Committee is deeply appreciative of this extraordinary demonstration of solidarity.

“Today’s election result is a well-deserved victory not only for Wikimedia U.S. staff, but for everyone who believes in a free, democratic internet. I am proud to be among the 2,000 volunteer editors celebrating this achievement today,” said Shushugah, a volunteer Wikipedia editor since 2015 with over 14,000 global edits.

###

About CODE-CWA
The Campaign to Organize Digital Employees (CODE-CWA) is a union movement of thousands of workers fighting every single day to build the voice and power necessary to ensure the future of the tech, games, and digital industries in the United States and Canada. CODE-CWA is a project of the Communications Workers of America (CWA), which represents hundreds of thousands of workers throughout tech, media, telecom, and other industries who stand together to fight for justice on the job and in our communities.

The Daily Front Page 26 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Short Takes: Work & Life
article

The Real Luxuries In Life

by tosh·▲ 468 points·218 comments·feld.com ↗

I love Labor Day weekend.

It’s the “back to school” moment of the year for me. When I was a kid growing up in Dallas, we didn’t go back to school until after Labor Day, so that was the weekend that summer ended and fall began.

I wrote ten years ago that Memorial Day and Labor Day are the bookends of summer.

What’s different now is that I’m 60, closing in on 61, and lately I’ve been thinking a lot more about what I actually care about.

Amy texted me this list yesterday.

The real luxuries in life:

  1. Time
  2. Health
  3. A quiet mind
  4. Slow mornings
  5. Ability to travel
  6. Rest without guilt
  7. A good night’s sleep
  8. Calm and “boring” days
  9. Meaningful conversations
  10. Home-cooked meals
  11. People you love
  12. People who love you back

It’s a wonderful list.

Enjoy the long weekend.

The Daily Front Page 27 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Also on the Front Page
The Daily Front Page 28 of 29
Saturday, September 5, 2026 The Daily Front No. #260905 — Colophon

That's the Front for Today

Issue No. #260905 — Saturday, September 5, 2026 — went to press 2026-09-06 at 07:30 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 Saturday, September 5, 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 and chose the highlights; a second read a handful of the day's stories and briefed the cover illustrator — 32 model calls and 255k 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:

Inside a secure London vault, Dutch gold bars are being transferred from a wheeled steel case into a deep European reserve chamber, while above them an unattended server rack glows through a haze of cooling vapor. A small robotic arm silently reconnects cables and closes a valve after an alarm, its repair completed before the lone engineer reaches the console. Beside the racks, an old reel-to-reel machine continues turning, its spools feeding compressed sound into a squat black archive box.

Construct a kinetic-mobile abstraction in a vast white vault field: a gold disc-and-plane transfer path moves from a wheeled steel rectangle into a deep chamber; overhead, a blue suspended rack-frame emits translucent vapor planes while a small red articulated arm reconnects cable rods and seals a valve ahead of a lone engineer-disc approaching a console plane. Beside it, paired rotating reel discs feed vibrating rods into a squat black archive block. Use a deliberate primary palette of cobalt blue, signal red, and canary yellow, with metallic gold accents, black anchors, and uncolored negative space.

Absolutely no text, letters, numbers, readable symbols, or logos anywhere in the image.

Production Ledger

StageModelCallsTokens InTokens Out
extractgpt-5.6-luna 28 150,858 75,049
layoutgpt-5.6-terra 1 18,262 2,493
covergpt-5.6-luna 2 1,664 457
covergpt-image-2 1 236 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. AI handles incidents, engineers lose touch with their systems by sylvainkalache — sylvainkalache.com·HN discussion ↗
  2. The "$60 Gaming PC" – AMD BC-250 (2025) by networked — devquasar.com·HN discussion ↗
  3. Private German rocket makes history, reaches orbit from European soil by bookmtn — space.com·HN discussion ↗
  4. Git hosting that never leaves Europe by sevenseacat — pushin.eu·HN discussion ↗
  5. Artificial Analysis Intelligence Index v4.2 by nojs — artificialanalysis.ai·HN discussion ↗
  6. Portal by Spotify cut my Claude Code token usage by 90% by cebert — engineering.atspotify.com·HN discussion ↗
  7. Can guitar frets perform multiplication? by wibbily — charlespetzold.com·HN discussion ↗
  8. Git Submodules as a Package Manager by ErenayDev — nesbitt.io·HN discussion ↗
  9. Visualizing Rust's Vtables: How dyn Trait Works In Memory by torutofu — sofiabelen.github.io·HN discussion ↗
  10. Netherlands pulls gold out of the US by daniel_iversen — abc.net.au·HN discussion ↗
  11. A bizarre Commodore 64 peripheral, a mime, and some pretty bad ads by rfarley04 — buttondown.com·HN discussion ↗
  12. Pointing at the error: compiler-style diagnostics in uutils coreutils by ingve — uutils.org·HN discussion ↗
  13. Stopping the Unstoppable: When an unstoppable force meets a dashpot snubber by crescit_eundo — practical.engineering·HN discussion ↗
  14. How the Disaster of "Forever Chemicals" Was Kept Secret by stevenwoo — propublica.org·HN discussion ↗
  15. Steffen's Polyhedron by pavel_lishin — gregegan.net·HN discussion ↗
  16. Balrogg: Demonically compacting (up to 15%) lossless Vorbis/Opus recompressor by palaiologos — github.com·HN discussion ↗
  17. Finite time blowup for an averaged three-dimensional Navier-Stokes equation (2014) by gmays — terrytao.wordpress.com·HN discussion ↗
  18. Global warming will exceed 1.5-degree limit, UN says by ijidak — pbs.org·HN discussion ↗
  19. A Million Falcons Went Missing. Here’s How They Were Found by bryanrasmussen — nationalgeographic.com·HN discussion ↗
  20. GPT-6 Astra in code review: Gains, privacy, and cost by cebert — coderabbit.ai·HN discussion ↗
  21. .gitignore Everything by Default by der_gopher — packagemain.tech·HN discussion ↗
  22. Learn Programming with OCaml by elvis70 — usr.lmf.cnrs.fr·HN discussion ↗
  23. Delidded Intel I9-14900KS CT Scan by willx86 — lttlabs.com·HN discussion ↗
  24. LLMs as a Cognitive Virus by canjobear — arxiv.org·HN discussion ↗
  25. Wikimedia Foundation Workers Overwhelmingly Vote to Form Union with CWA by robin_reala — wikiworkersunited.org·HN discussion ↗
  26. The Real Luxuries In Life by tosh — feld.com·HN discussion ↗
  27. Terpstra Keyboard by cl3misch — terpstrakeyboard.com·HN discussion ↗
  28. Nitter has more working instances than before the takedowns by Cider9986 — codeberg.org·HN discussion ↗
  29. Singapore subway (mrt) information display types by gregorvand — sgtrains.com·HN discussion ↗
  30. Show HN: Fly By – retro biplane flying game by michaelteter — michaelteter.com·HN discussion ↗

Browse all issues in the archive →