2026Q1 Fonts Quarterly

embracing agentic coding
behdad with Claude
March 24, 2026

Introduction

Projects at a Glance

How I Used AI This Quarter

Starting as a Skeptic

The Tools and When I Use Which

The Process: Before Any Code Is Written

The Closed Feedback Loop

Lifting: The Dominant Task Type

The Open-Source Advantage

My Role in the Collaboration

My Typical Rhythm

AGENTS.md

Friction and Learnings

Projects

avar2 Partial-Instancing

GLyphy: Slug Algorithm

VARC in FreeType

VARC in Skrifa

hb-raster and hb-vector

CFF2-to-CFF Downgrade

read-fonts Speedup Experiments

PackTab Overhaul and Rust Port

FontTools Audits

hb-view Enhancements

Fuzzer Issues

Personal Projects

GDocsMeUp

Behdad's Calendar

Introduction

This quarter I barely wrote any code by hand. I leaned into agentic coding completely — meaning I delegated the mechanical act of writing, editing, and debugging code to an AI agent, while I focused on directing, reviewing, and integrating. The result was the most productive quarter I can remember. Below I describe what I shipped and, per interest from management, how I actually used AI to do it.

Projects at a Glance

Project

Codebase

Task Type

Tool

Result

avar2 partial-instancing

FontTools + HarfBuzz

Breakthrough

Claude

Under review (FontTools); HarfBuzz unverified

GLyphy Slug algorithm

GLyphy

Source material

Claude

Shipped

VARC in FreeType

FreeType

Lifting

Claude

Prototype

VARC in Skrifa

Fontations

Lifting

Claude

Shipped

hb-raster + hb-vector

HarfBuzz

Mixed

Claude + Codex

Shipped

CFF2-to-CFF downgrade

HarfBuzz

Lifting

Claude

Shipped

read-fonts speedup

Fontations

Experimentation

Codex

Proof-of-concept

PackTab overhaul + Rust port

PackTab

Lifting + Audit

Claude

Shipped

FontTools audits

FontTools

Audit

Codex

Shipped

hb-view enhancements

HarfBuzz

Enhancement

Codex

Shipped

Fuzzer issues

HarfBuzz

Debugging

Codex + Claude

Shipped

GDocsMeUp cleanup

Personal

Lifting

Codex

Shipped

Behdad's Calendar JS port

Personal

Lifting

Claude

Shipped

Task types: Lifting = faithful reproduction from a known source. Common knowledge = agent implements from first principles or spec. Source material = agent works from a paper or reference code. Breakthrough = no prior solution existed; domain knowledge and AI working memory combined. Experimentation = hypothesis generation and elimination. Audit = agent reads code and triages bugs and improvements. Enhancement = small feature additions from issue backlog.

How I Used AI This Quarter

Starting as a Skeptic

A year ago I experimented with ChatGPT to generate code for a project. I was surprised it worked at all, but every change I requested caused a dozen unexpected edits elsewhere in the codebase. I tried the same approach on a PHP-to-JavaScript translation and the output was not acceptable in any sense. I mention this because it is important context: I came to this quarter as a skeptic with evidence, not an enthusiast. What changed since then — in the models themselves, and in the CLI tooling that wraps them — is significant enough that my conclusion has reversed completely. The same tasks I abandoned a year ago were trivial this quarter.

The Tools and When I Use Which

I started the quarter on Codex, switched to Claude Max in February, maxed out my quota in two days, and paid for extra tokens throughout the month — over $2,500 in total. I have since returned to a tiered approach: Codex for most tasks, Claude for the hardest ones. Gemini I have tried and not had much luck with.

The division is roughly this: Codex is faster, cheaper ($20/month for my usage), and willing to run autonomously without stopping to ask permission at every step. Claude is slower, more expensive, and more cautious — but it wins on the hardest problems. When I hit a debugging problem I intuitively feel Codex will give up on, I open Claude. Many times I have thrown the same hard problem at all three simultaneously. It is typically Claude that finds the root cause.

The caution point cuts both ways. Claude's willingness to question its own assumptions — to dig into infrastructure rather than trust it — is exactly what makes it better at hard problems. But that same instinct makes it friction-heavy in autonomous workflows. More on that below.

The Process: Before Any Code Is Written

I never let the agent start coding blind. For debugging, I ask for a root cause explanation before anything is touched. For new code, I ask for an itemized implementation plan and review it before work begins. This is how you catch misunderstandings early, before they are baked into a hundred lines of code. Once we agree on the plan, I ask for commits after each step, verifying that the work meets quality and intention before moving on. The work is chunked, reviewed, and committed incrementally — the agent does not just run to completion and dump a diff on me.

The Closed Feedback Loop

The most important thing I do is set up a feedback loop the agent can run by itself. I give it a test command — something like hb-view test-font.ttf TestText --options > out.png — and it runs the test, inspects the output (including image output, via vision), decides what is working and what is not, makes changes, and repeats. Without my involvement. This is what makes it genuinely agentic rather than assisted.

If there is no natural test command for the task, I ask the agent to instrument the code with printf-style debugging and assess its own progress that way. The first time I worked this way, the agent came up with the printf approach itself.

The value of this is hard to overstate. Development cycles that used to require my constant attention now run while I am doing something else. Earlier this quarter I gave Codex a benchmark command for the rasterizer and told it: here is how to measure performance, try ideas, commit anything that improves the benchmark by more than 1%, I'm stepping out for an hour. It did not find a speedup in the end — but it generated and ruled out many hypotheses autonomously. A human engineer doing the same exploration would have called it a multi-day spike. I called it an hour away from the keyboard. Negative results have value; now we know what does not work.

Lifting: The Dominant Task Type

The majority of my work this quarter falls into a category I think of as lifting: porting a feature, implementing a file format, translating an algorithm — tasks where a known correct answer exists somewhere (a spec, a reference implementation, another language's codebase) and the job is faithful reproduction. This is exactly what AI is best suited for. The agent has a target to aim at and test against.

Not everything was lifting. Some tasks were common-knowledge implementation: the agent implements from first principles based on well-understood algorithms, pulling spec sections as needed. The hb-raster scanline rasterizer and the SVG renderers in both hb-raster and hb-vector fall here — no reference implementation to copy, just a well-understood problem domain. For the GLyphy Slug implementation, I gave the agent a paper and some shader code from the algorithm's author and asked it to fit the algorithm into the GLyphy framework. A harder variant of the same category.

The most significant project was different from all of these. The avar2 partial-instancing work had defeated me personally. I had the domain intuitions but the details got out of hand — too many moving parts to hold in my head at once. Claude managed to make breakthrough progress where I could not, synthesized my leads into an 800-line design document, and then fully implemented it with my guidance. I had genuinely given up on that problem. That is the most impressive thing I can say about working this way.

The Open-Source Advantage

One reason this works as well as it does in our domain is that the agent arrives pre-loaded. HarfBuzz, FontTools, FreeType, Fontations, Skrifa — these are well-known open-source projects with years of public history. When I say "add a flag to hb-shape that controls X," the agent knows what hb-shape is and how similar flags have been added before. It does not need onboarding.

This extends to the entire font engineering ecosystem. The agent reaches for ttx, the FontTools library, hb-info, hb-shape — inspecting TTX XML output, cross-referencing tables, pulling the relevant spec section — without being told to. It just knows. It feels like working with someone who already speaks the language. When it needs a spec it does not have, it fetches it mid-session. The OpenType spec, the W3C SVG spec, table references — it pulls what it needs and continues.

The agent is also good at writing tests from real fonts. It ttx's the font, studies the tables, identifies the relevant parts, and constructs test cases that actually exercise the feature in question. This is the kind of work a human finds tedious precisely because it requires holding a lot of font-format knowledge in mind while doing something repetitive. The agent finds it natural.

My Role in the Collaboration

It would be wrong to characterize my role as passive. My active roles throughout the quarter:

Holding the quality bar. The agent defaults to "working." I push toward "shippable." This is a constant steering effort — stopping the agent from cutting corners, insisting on the full implementation rather than a hacked-up approximation that gets the tests green. Codex is more prone to this than Claude. It is genuinely frustrating.

Steering attention. When the agent starts reading irrelevant code, running irrelevant tests, or debugging in the wrong layer, I redirect it. Often a single sentence. The clearest example this quarter: a fuzzer-reported crash where Codex kept applying bandaids at the surface level of the stacktrace. Claude was willing to dig into the underlying vector type implementation and found an inherent bug there. The difference between a bandaid and a fix.

Designing the feedback loop. Giving the agent the right test command, enough context to evaluate its own output, and clear success criteria is a skill. It determines whether the agent can run autonomously or needs constant supervision.

Audit mode. Sometimes I hand the agent a filename or a crash stacktrace and ask it to study and report. With source code, it reads the file, produces a triage of bugs and improvements, and we agree on what to fix before it commits anything — one commit per fix, which I review as it goes. With a stacktrace, it typically finds the root cause by static analysis alone, no test run required.

Cross-agent review. When one agent finishes a piece of work — optimizations and all — I take the result to a different agent and ask for a critique, missing pieces, and optimization ideas, spending a full session on it. This consistently unearths corners the original agent cut but did not disclose, or simply surfaces a fresh perspective. It is the most reliable quality check I have found.

Workflow integration. When I receive PR review feedback on GitHub, I give the agent the URL and say "address feedback." It pulls the review via HTTPS or the gh CLI and addresses each item in a separate commit. I review when it is done. I have similarly asked it to review all open GitHub issues and assess which ones are worth fixing. The agent is not just operating inside the codebase — it is operating on the development workflow itself.

My Typical Rhythm

For a project of moderate size: one evening session to get the basics working, a second day to polish and complete features, a third to optimize. This is a repeatable unit of work and useful for planning. The larger projects — FreeType VARC, avar2, GLyphy — break this pattern, but even they follow the same arc at a larger scale.

AGENTS.md

One practical thing I have learned: put repetitive project-specific instructions in an AGENTS.md file at the root of the repository. How to build, how to run tests, non-obvious constraints. The agent reads this at the start of every session. This is the solution to what is otherwise a persistent frustration — agents losing track of build instructions mid-session when the conversation gets long enough to trigger context compaction. HarfBuzz's AGENTS.md is linked here for reference.

Friction and Learnings

Context compaction. When a conversation grows long, the model compresses earlier context and can lose track of project-specific setup. AGENTS.md largely solves this, but it requires deliberate maintenance. Even with it, some domain-specific constraints need reinforcing at the start of each session. The fuzzer work is a good example: it helps to remind the agent that fuzzer tests run with failing-malloc. Without that note, the agent assumes allocation succeeds and misreads the entire stacktrace.

Permission prompts. Claude, more than Codex, stops to ask permission before running new commands. This is the right behavior in many contexts but is real friction in autonomous workflows — the price of Claude's carefulness. I have not yet moved to a sandbox environment with unrestricted execution permissions, which is the obvious next step for the "step out for an hour" pattern.

The trough. In the hardest projects, there is a consistent emotional arc: an initial rush of excitement when something almost works, then a long grinding middle where the agent seems stuck, and then the temptation to conclude it will not pull through. I have always persisted and always succeeded so far, even when it took twelve hours straight. The trough is not a signal to stop. It is a normal part of the process.

Multiagent. I tried running parallel agent sessions in multiple terminals, attacking different problems simultaneously. I found the context-switching cognitively expensive and focus-degrading. I have settled on single-threaded deep focus on one major task, using the waiting time — when the agent is running — to handle small issues and non-coding tasks.

Tool cost. $2,500 in one month is a real number. The tiered approach — Codex by default, Claude for the hardest problems — is both the practical and economical solution. It requires judgment about which bucket a problem falls into, and that judgment improves with experience.

Projects

avar2 Partial-Instancing

FontTools + HarfBuzz · Breakthrough · Claude · Under review

On Wednesday, my manager asked: "It is impressive to write a rasterizer or fix all the TODO items, but how good is the agent at attacking a genuinely new problem?" By Friday I presented the full FontTools solution.

The avar2 partial-instancing problem had defeated me personally. I had ideas and intuitions, but the details got out of hand — too many interdependencies to hold in my head at once. I had genuinely given up on it. Starting from my leads, Claude synthesized the problem into an 800-line design document, and then implemented the full solution with my guidance. The FontTools implementation is currently under review. The HarfBuzz port took a couple of hours of Claude working alone; I did not participate in or verify that output.

This is the clearest example this quarter of what AI makes possible that was not possible before — not faster execution of a known task, but a breakthrough on a problem I had abandoned.

GLyphy: Slug Algorithm

GLyphy · Source material · Claude · Shipped

I had not touched GLyphy in over ten years. The Slug algorithm, developed by Eric Lengyel, had been patent-protected for years. Three days after the patent was donated to the public domain, I sat down with Claude to implement it.

I gave Claude the paper as a PDF, the shader code from the author, and the GLyphy codebase, and asked him to implement the Slug algorithm in place of GLyphy's existing algorithm. About an hour later, it had something to show. A couple of small fixes and legible text rendered on the screen. It was breathtaking. Within two hours we had a solid implementation. From there we worked through the optimizations and improvements from the paper, then onto performance tuning.

Ten years of a codebase sitting dormant. Four days after the legal barrier dropped, shipped.

VARC in FreeType

FreeType · Lifting · Claude · Prototype · 12 hours

FreeType's codebase is everybody's nightmare to work in. The VARC table reading and basic data types needed to be implemented from scratch, since FreeType had none of it. I gave Claude the HarfBuzz implementation, the FontTools implementation, and the spec — three sources to lift from and test against.

The session ran twelve hours nonstop before I had rendering I was satisfied with. I say this to correct a misconception before it forms: agentic coding does not mean effortless. It means the ceiling of what you can attempt is higher. The hard problems are still hard, and this one had the added friction of FreeType's codebase on top of the complexity of the feature itself.

The result is a prototype — working rendering, not production-hardened — delivered to the FreeType maintainers as a foundation to build from.

VARC in Skrifa

Fontations · Lifting · Claude · Shipped

This was a three-way race. I gave the same task simultaneously to Codex, Claude, and Gemini. Codex produced something first, but the output made little visual sense. Claude produced something resembling correct rendering and I continued with it. Gemini was too slow to produce anything and I abandoned it.

The task itself was relatively contained: the VARC table reading was already implemented in read-fonts, so this was only the rendering logic, with both FontTools and HarfBuzz implementations to copy from. Low effort, shipped code.

hb-raster and hb-vector

HarfBuzz · Mixed · Claude + Codex · Shipped

Two new HarfBuzz modules, developed in intertwined sessions this quarter.

hb-raster is a CPU scanline grayscale and color-font rasterizer, intended for clients who want to port away from FreeType — game developers in particular. hb-vector renders to SVG output rather than pixels, originally built for internal testing of the hb-draw and hb-paint APIs, now also exposed as a client API.

Both support COLRv1, SVG fonts, and bitmaps. The development sequence: hb-vector outline rendering first, then hb-raster, then color font formats in order — COLRv1, then SVG, then bitmaps — with code shared and sessions intertwined across both modules.

The scanline rasterizer and SVG renderer in both modules are common-knowledge implementations: no reference to copy, but well-understood algorithms and formats the agent implemented from first principles, pulling spec sections as needed. The COLRv1 renderer was lifting from the existing hb-cairo implementation. Bitmap support (libpng and zlib integration, including svgz decompression) was common knowledge plus lifting from cairo-png.c as a reference.

Claude handled the rendering logic and color font formats; Codex handled supporting work.

CFF2-to-CFF Downgrade

HarfBuzz · Lifting · Claude · Shipped

Downgrading CFF2 to CFF is necessary for workflows like printing a variable font to PDF. I had an existing implementation in FontTools to lift from. The task is tedious enough that it had been sitting undone — not hard enough to be interesting, just laborious enough that no human had prioritized it.

The agent happily pulled the CFF spec and debugged through the binary output byte by byte to figure out why produced output failed to parse. Occasionally I had to step in and point it toward where in the font the sanitizer was failing — a good example of the attention-steering role.

read-fonts Speedup Experiments

Fontations · Experimentation · Codex · Proof-of-concept

My aim was to speed up HarfRust, but profiling led me to believe that any major speedup would require improvements in read-fonts, the Rust crate that HarfRust relies on for font table access. read-fonts uses generated Rust code driven by a specification of font data tables, and matching HarfBuzz's C++ performance is complicated by Rust's safety considerations and bounds checks.

Codex helped me produce two experiments, each modifying the codegen module to explore different approaches to removing bounds checks — not in shippable form, but as proofs-of-concept. The experiments are now being used by the read-fonts owning team to drive real optimizations. The work did not ship as code; it shipped as evidence.

PackTab Overhaul and Rust Port

PackTab · Lifting + Audit · Claude · Shipped

PackTab is a static integer table packer used to compact Unicode data tables in projects like HarfBuzz. I wrote the Python implementation in 2019; the original C implementation goes back to 2001. It had accumulated a backlog of TODO items, sparse documentation, and no test suite.

Claude implemented all outstanding TODO items, added an extensive test suite, wrote documentation, and produced a full Rust port. Both the Python and Rust versions are shipping and in use in new projects. A codebase that had been "good enough" since 2019, properly finished and extended in one sustained effort.

FontTools Audits

FontTools · Audit · Codex · Shipped · varLib, cu2qu/qu2cu, varLib.interpolatable

Proactive hygiene across three modules. The workflow was the same for all: hand the agent the source file, let it study and triage independently, agree on what to fix, then one commit per fix reviewed as it goes.

The findings went beyond bugs and improvements. The agent also flagged typos, copy/paste errors, outdated documentation, unhandled cases, and — notably in varLib.interpolatable — places where errors were being silently swallowed by assertions rather than raised as proper exceptions. It upgraded those to exceptions without being asked. A reminder that in audit mode, the agent sometimes surfaces a category of issue you did not explicitly request.

hb-view Enhancements

HarfBuzz · Enhancement · Codex · Shipped

After a few large projects, I had a realization: with agents available, I could simply go through the HarfBuzz issue tracker and throw the easy ones at Codex. So I did. About ten small enhancement requests — longstanding ergonomic improvements that had been sitting open for years — each solved in roughly two minutes.

Nothing a human couldn't do. All had been sitting there precisely because no human had wanted to spend their limited time on something minor. The agent has no such preference. It does not get bored, does not weigh opportunity cost, and is equally happy to implement a small flag as to write a rasterizer. The issue tracker's long tail is no longer a permanent backlog.

Fuzzer Issues

HarfBuzz · Debugging · Codex + Claude · Shipped

Adding hb-raster and hb-vector — with their own fuzzer targets — generated a flood of incoming fuzzer issues. The workflow is simple: paste the stacktrace into Codex, it finds the root cause, I run the fix through the test suite to confirm. The agent's ability to find root causes by static analysis alone is particularly valuable here, since reproducing the fuzzer environment locally is tedious.

One recurring lesson: it helps to remind the agent at the start of each session that fuzzer tests run with failing-malloc. Without that note, the agent assumes allocation succeeds and misreads the entire stacktrace — even with it written in AGENTS.md.

There was one case where Codex gave up and I escalated to Claude. The crash was not in the code exposed by the stacktrace, but in the implementation of the vector type it used — an inherent bug that only appeared under memory pressure. Codex kept patching the surface. Claude dug into the infrastructure and found the real cause.

Personal Projects

GDocsMeUp

Personal · Cleanup · Codex · Shipped

A Google Docs exporter I originally wrote a year ago with ChatGPT — not a satisfying experience at the time. Codex cleaned it up and fixed a backlog of issues this quarter. One nice detail: when it needed to understand where in the Google Docs API response object to find a particular piece of data, it downloaded a live response and inspected the object hierarchy directly, rather than relying on documentation that might be incomplete.

Behdad's Calendar

Personal · Lifting · Claude · Shipped

My PHP Iranian calendar tool, ported to client-side JavaScript. A year ago ChatGPT completely failed at this task. This quarter, Claude one-shotted it — a fully working solution, bug-for-bug output-compatible with the original PHP. Same task, different era, completely different result.