<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
  <title>The Oblivious Robot</title>
  <link>https://drdebmath.github.io/blog/</link>
  <description>A blog by Dr. Debasish Pattanayak on distributed algorithms, swarms of robots, research and writing with AI.</description>
  <language>en</language>
  <copyright>CC BY 4.0, Debasish Pattanayak</copyright>
  <lastBuildDate>Tue, 27 Oct 2026 00:00:00 +0000</lastBuildDate>
  <item>
    <title>Building a classroom from scratch in GitHub</title>
    <link>https://drdebmath.github.io/blog/p/after-github-classroom.html</link>
    <guid isPermaLink="false">https://drdebmath.github.io/blog/#after-github-classroom</guid>
    <pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate>
    <description>GitHub Classroom shut down, so a 530-student C++ lab runs on secret per-student teams, template repositories, issues and a go label. Why a cron timer could not be trusted, and the limit nobody reports.</description>
    <content:encoded><![CDATA[<div class="by-collab">

My plan for IC151, the first-year C++ programming lab, assumed GitHub Classroom. Students would accept an
assignment link, get a repository, push their code, and the TAs would grade it. Then GitHub shut Classroom down,
and I built the replacement myself, with an AI coding agent, in the week just before the first lab.

</div>

<div class="by-ai">

## What went away

GitHub closed new sign-ups for Classroom on 26 May 2026
([GitHub changelog](https://github.blog/changelog/2026-05-26-github-classroom-sign-ups-are-no-longer-available)).
On 28 August 2026 it went further: "The GitHub Classroom website, APIs, and related services have now been
decommissioned"
([GitHub changelog](https://github.blog/changelog/2026-08-27-github-classroom-deprecated)). The same notice says
that accounts, repositories and organizations made for Classroom are not affected. So the parts worth keeping
were always plain GitHub: private repositories, organizations, teams. Classroom was a layer of convenience on top.
GitHub pointed teachers to its "selected partner solutions".

My course has about 530 students in eight lab batches of roughly 67, and a lab every week for each batch. The
course design document for the semester still says, in its list of first-lab steps, "accepting a GitHub Classroom
assignment". That line had to be replaced by something real before the first lab.

</div>

<div class="by-human">

## A race against the first lab

I started building this in the week just before the first lab. It was a race against time to get everything set
up properly, because of the sunset of GitHub Classroom, before the students began their first lab. There was a
lot of manual intervention in the first two weeks.

Mind you, there are students in the course at both extremes: people so proficient in coding that they do not
need this course, and people using a computer for the first time.

</div>

<div class="by-ai">

So the system had to work for both. The proficient students would find any gap in it. The new ones could not be
asked to do anything beyond the minimum: make a GitHub account, accept one email, open a link, push.

## The rules I started from

The agent works from an instruction file kept in a private admin repository for the course. Its
mission statement is blunt: use ordinary private repositories, and depend on no Classroom, no paid service, no
server, no custom web application and no special submission command. The workflow it fixes is short:

1. The instructor prepares one template repository per assignment, with starter files and a checking script
   that tests them.
2. A release command creates one private repository per student from the template, named
   after the assignment, the batch and the student's roll number.
3. Each student can push only to their own repository. The TAs can read them.
4. Students run the checking script, commit and push to the main branch. The pushed commit is the submission. There is no
   submit button.
5. At the deadline, a collector records the exact commit SHA on the main branch for every student, so a later push
   changes nothing.

## The design

The system has five parts, and none of them runs on a server of mine.

**The organization.** Everything lives in one GitHub organization with base permission set to none. A student
sees nothing in it by default.

**One secret team per student.** Each student's access lives in a team of their own. The rest of the design rests
on it, so the next section covers it in full.

**Templates and manifests.** Each assignment is a private template repository. Creating a batch makes one
repository per student from the template and writes a manifest (CSV and JSON) with every repository and its
initial commit SHA. The manifests are committed to the admin repository, so its Git history is also the audit log:
who got what, and when.

**Issues as the control panel.** TAs release, undo and report access problems through issues in
the admin repository. GitHub Actions does the work and answers in the issue. A later section describes how.

**A public portal.** A free GitHub Pages site turns an assignment and a roll number into a repository URL. It
holds no student data, and a student who is not allowed in simply gets GitHub's 404. GitHub permissions are the
only security boundary.

</div>

<div class="by-collab">

## Teams first: a release is a permission change

</div>

<div class="by-human">

I started by taking GitHub usernames from the students, but that did not work out well. Shifting to team-based
assignment worked out well. The main benefit was that students got an email to accept the invitation to join the
organization only once.

</div>

<div class="by-ai">

The usernames came from a form, as free text. On the first lab day only 25 students of the first batch had a username that
could be validated and given a repository. The problems included typos, logins that did not exist, second
accounts, roll numbers that were not on the roster, and "NA". Granting each repository to a username
would also have meant a separate invitation to accept for every assignment.

</div>

<div class="by-collab">

The idea that made the rest simple: create the teams once, at the start of the semester, and make every
release nothing more than attaching a repository made from the template to a student's team.

</div>

<div class="by-ai">

The day after the first lab, the agent created one team per active student, named after their batch and roll number.
The teams are `secret` on purpose. A `closed` team is visible to every member of the organization, which would
let more than 500 students list each other: the roster leak the instruction file forbids. A team for one person
sounds odd, but it gives each student a single, stable place where their access lives.

Then it invited every student by their institute email address, with one API call that
does two things: it sends the organization invitation and places the student in their team in advance. Email
beats GitHub usernames here. The institute issues the address, whereas a username is free text the student types
into a form, and a typo in an email bounces instead of reaching a stranger. GitHub caps organization invitations
at 50 per 24 hours for a new organization, so the script stops cleanly at the cap and resumes the next day
without inviting anyone twice. When a student accepts, their team holds the account they actually signed in
with, and the usernames are read back from the teams. The team, not a form, is the
authority on who the student is.

After that, a release is one API call per student: put the new repository on their team, with push access. There
is no invitation per repository, nothing to accept and no username to look up. Once a student has accepted the
first email, every later assignment simply appears on their GitHub page. Taking a release back is the same call
in reverse. When batch names were added to the team names partway through, renaming kept the team ids, so the
66 pending invitations for one batch followed the rename without being sent again.

</div>

<div class="by-human">

The learning curve for students was to start by making a GitHub account, then finding the organization, then
finding their team's list of accessible repositories, to see the assignment that is updated there.

</div>

<div class="by-ai">

The team page is the one link that works for a student all semester. Before a lab is released, the new
assignment is simply not on it yet; afterwards it appears in the same list as all the earlier ones. So the public portal, given a roll
number, links to the student's team page as well as to the one repository.

## Create early, attach late

Since attaching is the release, the slow part can happen days earlier. Creating 67 repositories from a template
is slow and, as it turned out, rationed (see below). Attaching them to teams is fast and unlimited. So the agent
pre-creates each batch: the repositories exist, but no team holds them, so no student can
reach them. At lab time the release only attaches, and a batch goes from invisible to available in under a minute.

</div>

<div class="by-collab">

## Four labs in one room

Students in a lab sit side by side, so in section B a batch does not get one assignment. It gets several
versions of the same lab, each with its own template, and the students are split into groups that each get one
version.

</div>

<div class="by-ai">

The first lab had three versions per section, a third of each batch on each. From the second lab every batch got
its own versions, and from the third lab, four per batch: sixteen templates for one week's lab in section B, and
more than 80 so far. The versions are parallel, not different in difficulty. Each asks for the same skills (a
function with a prototype, a recursive function with loops forbidden, a linked list) under a different story and
different numbers.

The split is random but reproducible. The batch's roll numbers are sorted, shuffled with a fixed seed, and dealt
out one version at a time, like cards. Dealing rather than cutting into blocks keeps the groups within one
student of each other, so a batch of 67 splits 17, 17, 17 and 16. The split is then written down and never
recomputed: once a repository exists, a reshuffle would hand a student a different lab from the one they have
started.

Teams make the rest easy. A release for such a lab names one assignment and one batch, as before. For each
student it looks up their version, creates their repository from that version's template, and attaches it to
their team. The repository name carries the assignment, the batch and the roll number, but not the version, so
neighbours cannot tell from their URLs who has which. The TAs see the version in the release manifest.

Which batches share material is also deliberate. Two batches that meet on the same day share nothing, since
students talk between labs. A batch later in the week may reuse the computations of an earlier one under new
stories. Before each batch's lab the instructor checks how far the lectures have actually got, because batches
meet on different days: in one week, one batch met before linked lists were taught and the other three after.

Writing sixteen versions by hand every week is where errors creep in, so the section-B labs are generated. A
generator writes the questions, the starters and the tests, and checks every expected output against two
independent references, one in Python and one in C++. The generator and the reference solutions stay in the
private admin repository and never go into a template.

## Who presses the button: issues and labels

At first, TAs opened release issues themselves from an issue form: type the assignment id, pick the batch,
submit. GitHub Actions replies with a dry run as a comment; adding the `go` label, or replying `/apply`,
releases for real. The commit that added the label explains why: "Adding the 'go' label is two taps in the
GitHub app; typing /apply is not something you want to do one-handed." Since the admin repository is private, only
staff can open or comment on its issues, so the repository's own access list is the authorization check.

That order turned out to be wrong. If a TA opens the issue before the repositories exist, `go` does not just
attach: it creates all 67 in the middle of the lab, which is exactly when creation is slow and may be blocked.
The rule since 19 August: the issue is the signal that a batch is ready, so it is opened only after the batch
is pre-created. Now the sequence is:

1. Pre-create the batch and check the manifest: every repository created, every initial SHA
   present, every repository private.
2. Commit the manifest and the course configuration, and push. The workflow validates the issue against the main
   branch on GitHub, so an issue opened before the push fails with "Unknown assignment".
3. Open the issue, titled with the assignment and the batch, with only the release label. Its body says the
   repositories are created but not yet attached, gives the manifest path, a summary of the questions, the C++17
   build command and a link to the template.
4. Assign it to that batch's TA, looked up in a table every time. Copying the assignee from the previous issue
   once sent a section-B batch to a section-A TA.
5. Wait for the bot's dry run, which should show every student's repository ready to attach.
6. The TA adds `go` when the lab starts. The bot attaches the batch, commits the manifest and closes the loop
   in the issue.

Issue-triggered runs are quick. Over 127 non-skipped release runs, the median time from the event to a finished
run is 26 seconds, and the 90th percentile is 37. One early bug was in the workflow itself: all runs shared one
concurrency group, so opening several release issues at once left only the newest dry run alive and silently
cancelled the rest. Dry runs now get a group per issue.

An undo issue works the same way and detaches the repositories again. It deletes nothing, and it reports who
had already pushed.

## Why the timer did not work

The obvious improvement over a TA's thumb is a timer: list each assignment, batch and release time in a schedule file and
let GitHub Actions release each batch at its lab time, with nobody's laptop awake. The agent built exactly that
on 8 August. A cron workflow ran every 20 minutes, at minutes 7, 27 and 47 past the hour; when a row was due
within 25 minutes, the runner claimed it and waited inside the job for the exact second. Cron only had to be
early, not exact.

Even the cron line carries scars. It sits off the hour boundary because GitHub's documentation names the
start of every hour as a peak for scheduled runs and says queued jobs may be dropped. It polls only from 07:30
to 21:30 IST because Actions bills every job as at least one minute: polling around the clock would have spent
about 2,880 of the 3,000 monthly minutes doing nothing, against about 1,260 for the teaching day.

On 8 August the timer did its job. Helloworld for B4 was attached at 13:30:11 for a 13:30 lab. The second row,
variables for B4 at 14:00, was released by hand a minute early, and the runner already waiting for it attached
the same repositories again at 14:00:12, harmlessly, because a release is idempotent.

The run history over the next ten days shows why it could not be trusted. From 8 to 18 August the workflow ran
309 times. A full teaching day should give 42 runs. On weekdays it got between 22 and 25, so roughly half the
scheduled runs never happened. On the weekend of 15 and 16 August, with less load on GitHub, it got 41 and 40.
The time between consecutive runs had a median of 23 minutes, but 123 of 295 gaps were longer than the 25-minute
lead, and the longest was 53 minutes. A lab time that fell into such a gap had no run waiting for it, so the
release would have gone out on the next run, up to half an hour late. For a lab of 67 students, half an hour late
is a failed lab.

So the timer was not used again. Two days after it was built, the `go` label arrived, and the schedule file has
held no rows since 8 August. The workflow kept polling an empty schedule until it was disabled after its last run
on 18 August. The instruction file now says it outright: GitHub Actions scheduled workflows are not exact enough
for a lab, and the release belongs to a person pressing `go`.

## The limit nobody reports

The other hard lesson came on 18 August, during an A2 lab. GitHub has a secondary rate limit on content
creation. It is separate from the hourly quota, GitHub's rate-limit report does not show it, it applies to the whole
organization, and creating a repository from a template counts heavily against it. We hit it mid-lab. Ten
students had no repository until the block lifted, and one was made by hand on the website.

The rules now in the instruction file: one creating process at a time, never a shell loop over batches, at most
one batch per hour, two workers with a three-second pause, and pre-creation days ahead, never during a lab.
However, permission changes are not throttled. Attaching existing repositories, or giving students read access
to the template, still works when creation is blocked.

## A runbook that remembers

The agent starts every session with no memory, so the instruction file carries a weekly runbook, and every
mistake goes into it the day it happens. It records instructor zip files that arrived with another lab's
checking script, an old solution shipped as a starter, a release issue assigned to the wrong batch's TA, and an issue
opened before its configuration was pushed. The latest entry is from today: an A2 lab released directly 23
minutes before it started, then taken back so the TA could release it. The rule it produced is that if more
than 20 minutes remain before a lab, the TA releases it through an issue.

By 27 October the repository has 190 commits, about 3,100 lines of Python that call the GitHub CLI, and about
4,900 student repositories across 40 assignments. None of the pieces is clever. Private repositories, teams, a
template, an issue and a label do what Classroom did, for free, and the rules that make them safe are written
down where the next session will read them.

</div>

<details class="original">
<summary>Original input</summary>

<p class="note">Lightly edited for coherence: capitalization and punctuation fixed. The first request pointed to the author's old posts on X about the topic; those could not be read, so the post draws on GitHub's changelogs, the course design document, the history of the course's admin repository and its GitHub Actions run history instead.</p>

<div class="by-human">

**Request for this post.** See the blog repo and write a blog post about how this system of assignment management is built due to the death of GitHub Education. See my old tweets about it too.

**Added later.** Write about the system design, issue creation and assignment, why the auto-release schedule was not working due to delays in GitHub workflows, and things like that.

**Added later.** Also write about how I created the teams, and that the release is essentially attaching template repos to teams.

**Added later.** I started building this in the week just before the first lab; it was a race against time to make everything set up properly, due to the sunset of GitHub Classroom, before the students began their first lab. There was a lot of manual intervention involved in the first two weeks. I started with taking GitHub usernames from students, but that didn't work out well, thus shifting to team-based assignment worked out well. The main benefit was sending emails to students to accept the invitation to join the org only once. Mind you that there are students in the course who are on both extremes: people super proficient in coding to not needing to do this course, and people using a computer for the first time.

**Added later.** The learning curve for students was to start with making a GitHub account, then finding the org, then finding their team's accessible repo list to see the assignment that is updated on that.

**Added later.** No need to write file or script name details. Write about how the students are split into groups, and multiple templates are created and assigned to different students in the same batch.

</div>
</details>
]]></content:encoded>
    <source url="https://drdebmath.github.io/blog/posts/after-github-classroom.md">The Oblivious Robot</source>
  </item>
  <item>
    <title>The era of personal software</title>
    <link>https://drdebmath.github.io/blog/p/personal-software.html</link>
    <guid isPermaLink="false">https://drdebmath.github.io/blog/#personal-software</guid>
    <pubDate>Tue, 27 Oct 2026 00:00:00 +0000</pubDate>
    <description>In one evening, an AI coding agent and a handful of complaints turned a Pixel Watch into an iPhone accessory that feeds Apple Health. Software for an audience of one.</description>
    <content:encoded><![CDATA[<aside class="checklist by-ai">
<p class="checklist-title">Build your own bridge</p>
<p class="pass">Ask</p>
<ul>
<li>State the goal, not the design</li>
<li>Answer the agent's questions on constraints</li>
<li>Say which features matter</li>
</ul>
<p class="pass">Run it</p>
<ul>
<li>Let the agent install the toolchain</li>
<li>Enable wireless debugging on the watch</li>
<li>Install, open, grant permissions</li>
<li>Paste back every error</li>
</ul>
<p class="pass">Live with it</p>
<ul>
<li>Complain about whatever feels wrong</li>
<li>Check the numbers it writes to Health</li>
<li>Ask what it sends, and how often</li>
</ul>
</aside>

<div class="by-collab">

I have struggled to use my Pixel Watch 2 because the battery in my Pixel 6a failed. Then I got an iPhone.

</div>

<div class="by-ai">

A Pixel Watch leans on its phone for setup, updates and notifications, so a failing phone meant a half-working
watch. The iPhone did not help on paper. Google's compatibility page says the Pixel Watch 2 needs Android 9 or
newer and states plainly: "iOS phones are not supported"
([Google Pixel Watch Help](https://support.google.com/googlepixelwatch/answer/12652073)). The companion app
that pairs and manages the watch exists only for Android.

</div>

<div class="by-collab">

None of this is an accident. The Pixel Watch was never planned to support iOS, just as the Apple Watch was
never planned to support Android. Each watch is built to keep you inside its own phone's ecosystem.

</div>

<div class="by-collab">

So I asked an AI coding agent to build an SDK that would let the watch work with the iPhone.

</div>

<div class="by-ai">

One evening later, the watch sends
heart rate and steps to Apple Health, shows my iPhone notifications and controls the music on the phone. Bedtime mode switches on at 22:00, and the first night of sleep stages
has come through. Workouts with GPS routes are built too, waiting for their first real run. The code lives in a folder on my laptop. Nobody else
runs it, and nobody else needs to.

This post records how the bridge was built, mostly by an AI coding agent (Claude Code, running Claude Opus 5.5),
and what it suggests about software in general.

</div>

<p style="display:flex;gap:1rem;justify-content:center;flex-wrap:wrap" class="by-ai">
<img src="img/personal-software/logo.png" alt="PixelBridge logo: an orange watch ring crossed by a white pulse line" width="150">
<img src="img/personal-software/tile.png" alt="The PixelBridge tile on the watch: iPhone connected, with Run, Music and Sleep buttons" width="190">
<img src="img/personal-software/watch-home.png" alt="The PixelBridge home screen on the watch, with Workout, Music, Route and Sleep" width="190">
</p>

## Thirty minutes to a first version

<div class="by-collab">

Claude built the MVP in 30 minutes: an iPhone SDK, a watch app, and the protocol between them.

</div>

<div class="by-ai">

The design came from the agent, after it listed what cannot be done. An iPhone cannot pretend to be an Android
phone, so the bridge does not try. Instead, the watch app opens its own Bluetooth Low Energy service, and an app
on the iPhone connects to it. Two Apple services do the rest without any iPhone app at all: the Apple
Notification Center Service (ANCS) gives the watch every iPhone notification, and the Apple Media Service (AMS)
lets it press play and skip.

| Feature | How |
|---|---|
| Heart rate, steps, energy, distance | Wear OS Health Services on the watch, HealthKit on the iPhone |
| Workouts with GPS route | Recorded on the watch, saved as an Apple Health workout with its route |
| Notifications, with answer and decline for calls | ANCS, straight from iOS |
| Music control, crown for volume | AMS, straight from iOS |
| Sleep stages | Raw heart rate and wrist motion overnight, staged on the iPhone |
| Bedtime mode at 22:00, off at 06:00 | The watch app flips the system setting itself |
| Settings from either device | A small `config` message over the same link |

</div>

## What was left to me

<div class="by-collab">

The slow part was everything around the code, and most of it fell to me. Downloading the SDKs took more time
than creating the app.

</div>

<div class="by-ai">

- **Toolchains.** The Mac had Xcode but no Java. A JDK, Gradle and the Android SDK (about a gigabyte) came
  through Homebrew, and Xcode then asked for its own components and platform downloads. The agent's sandbox let
  Java reach Google's servers but not GitHub, where Gradle's download redirects, so the agent fetched that file
  with `curl` and pointed the build at the local copy.
- **Wi-Fi debugging on the watch.** The watch charger carries no data, so every install goes over Wi-Fi. On the
  watch: tap the build number seven times to unlock Developer options, turn on ADB debugging and Wireless
  debugging, and pair with a six-digit code. The connection then uses a second port, which changes every time
  Wireless debugging restarts. So each session began with me reading a port number off the watch. Wireless
  debugging also keeps the radio on, and the watch ran out of battery once.
- **Xcode setup.** Sign in with an Apple ID (a free account works), let Xcode download its iOS components, and
  trust the developer certificate on the iPhone under Settings, General, VPN & Device Management. After that,
  the agent built, signed and installed the app from the command line, over Wi-Fi once the cable was gone.
- **Permissions and pairing.** On the watch: nearby devices, notifications, location, and body sensors with
  "allow all the time". On the iPhone: Bluetooth and Health.

None of these steps involve writing software. They are the toll for running software that no store distributes.

</div>

## Complaints as specifications

<div class="by-collab">

My prompts were not specifications. Most of them were complaints: the agent was planning instead of writing
code, the watch app looked poor and felt slow, the play button opened Apple Music instead of Spotify, I wanted
PixelBridge one swipe from the watch face, and the calorie count was too high.

</div>

<div class="by-ai">

Each complaint became a change in the code within minutes. The poor design became five screens and a tile.
The swipe became a Wear OS tile, the panel that appears when you swipe left from the watch face. The calorie
complaint was the most useful one, because the agent had the bug and I did not know the code. The
watch reports *total* energy, resting burn included, and the bridge had been writing all of it to Apple Health
as Active Energy. The fix has the watch learn my resting rate from its own quiet minutes, and split each minute
into Active Energy and Resting Energy.

</div>

## Where it broke

<div class="by-ai">

Nothing worked on the first try on real hardware, and every failure was specific:

- **The first crash** was a size limit. The iPhone negotiated large Bluetooth packets, and Android refuses any
  single notification longer than 512 bytes. The watch crashed on its first message, then again every time the
  iPhone reconnected.
- **The release build** crashed because the optimizer renamed fields that Health Services looks up by name.
- **The watch thought it was in Toronto.** It had kept the time zone of the Android phone it was set up with,
  so 22:00 would have meant 07:30 in India.

Some things could not be fixed. AMS controls whichever app iOS considers *Now Playing*, and cannot be pointed
at Spotify. Without Spotify Premium there is no other route, so the watch now refuses to send Play unless
Spotify is the current player, rather than waking Apple Music.

</div>

## The official story and the real one

<div class="by-collab">

The official instructions and what actually happened did not match. Google says the watch does not support
iOS, yet Google's own Health app on my iPhone was already showing the watch's data.

</div>

<div class="by-ai">

The watch explains the gap. Its Fitbit app (now called Google Health) uploads straight to Google's servers
over home Wi-Fi: the watch's network statistics show it sending about 120 to 180 KB every two hours with
no phone in the path. The Google Health app on the iPhone then reads that data back from the cloud. It never
talks to the watch. Since version 5.05, released in August 2026, it can also write steps, workouts, sleep and
some vital signs into Apple Health
([Khaleej Times](https://www.khaleejtimes.com/business/tech/how-to-sync-fitbit-and-pixel-watch-data-with-apple-health)).
That includes Fitbit's own sleep stages, which are better than anything the bridge can estimate from raw
sensors.

<figure style="text-align:center">
<img src="img/personal-software/iphone-dashboard.jpg" alt="The PixelBridge iPhone app: watch connected and synced, heart rate 70 bpm, 12,213 steps, no workouts yet, and a sleep card showing 6 h 3 m asleep from 11:48 PM to 5:51 AM, split into 2 h 24 m deep, 1 m core, 3 h 38 m REM and no time awake" width="300">
<figcaption>The iPhone app on 27 October. The sleep card is the bridge's own first estimate: one block of deep
sleep, then one of REM, and no time awake. Real nights cycle through the stages every hour and a half or so,
so this staging is wrong, which is one more reason to take sleep from Google Health.</figcaption>
</figure>

So "not supported" means something narrower than it sounds. The health data crosses over, through the cloud.
What does not cross is everything that needs the phone and watch to talk: notifications, calls, music,
settings, schedules. Those are exactly the parts the bridge provides, over Bluetooth, with Apple's own
notification and media services. Someone else built nearly the same bridge for a Pixel Watch 4
([drmhse.com](https://www.drmhse.com/posts/synchronizing-a-pixel-watch-with-an-iphone/)), with the same
conclusion about what can and cannot be reached.

The bridge now has a switch for this. With *Send health data* off, the watch stops recording heart rate,
steps, energy and sleep for the iPhone, and Google Health supplies them to Apple Health instead, without
duplicates. Workouts with GPS routes, notifications, music and the Bedtime schedule keep running through the
bridge.

</div>

## Asking what it costs

<div class="mixed">

<span class="by-collab">I asked how much data the watch sends the phone, whether all of it needs to go right away, and what could be stored and sent later.</span>
<span class="by-ai">The agent measured it from the iPhone's log: 461 readings in 18 minutes, about 27 per minute, or
2.7 MB a day, in a batch every one or two minutes. None of it needed to be live. The watch now holds readings,
rolls them into one value per minute, compresses them, and sends them once an hour, or when I open the iPhone
app. The estimate is about 50 KB a day and a few dozen radio wake-ups instead of over a thousand.</span>

</div>

## The era of personal software

<div class="by-collab">

PixelBridge is personal software. It was written for one person, one watch and one phone, and it will never be
published, because the value is entirely in the fit.

</div>

<div class="by-ai">

The economics used to forbid such software. A Bluetooth protocol, a Wear OS app, an iOS app, a sleep model and
a HealthKit integration add up to weeks of work for an experienced engineer, so only products with many users
could pay for them. A user with a rare combination of devices simply did without. Here the cost was one evening
of typing complaints, and the result does exactly what one person wants: Spotify and never Apple Music, sleep
from 22:00 to 06:00, health data once an hour.

</div>

<div class="by-collab">

The interface is no longer fixed either. It is generated, and regenerated, on demand.

</div>

<div class="by-ai">

The watch app started the evening as one plain list of buttons, and ended it as five screens and a tile.
Each version existed because a sentence asked for it. The settings appeared on the iPhone too, as soon as I
asked for them there. A generative interface, in this sense, is not a screen drawn
by a model at run time. It is an ordinary app whose design is cheap enough to throw away and ask for again.

Even the shelf life fits. An app signed with a free Apple ID stops opening after seven days, so PixelBridge has
to be rebuilt every week. For a product, the limit would be a disaster. For personal software, it is a
reminder of what the software is now: something made on the fly, for a purpose, and remade when the purpose
changes.

</div>

<details class="original">
<summary>Original input</summary>

<p class="note">Lightly edited for coherence: capitalization and punctuation fixed, consecutive messages on the same topic joined, and only the prompts that shaped the bridge are kept (status updates, ports and confirmations removed). The post's facts come from the session itself: build and install logs, the iPhone app's sync log, and the code.</p>

<div class="by-human">

**Request for this post.** See the blog repo in Research and write a post about how I created this bridge between the Pixel Watch and the iPhone. The title is "The era of personal software". Then also allude to generative interfaces and software on the fly, or on demand.

**Background, added later.** Start with how I have struggled to use my Pixel Watch due to the failed battery in my Pixel 6a; then I got an iPhone.

**Correction, added later.** The Google Health app on the iPhone is already working with the Pixel Watch. Say that the official instructions were different from what happened. The Pixel Watch never planned to support iOS, and the Apple Watch never planned to support Android.

**Notes added later.** Claude did the MVP in 30 minutes. Write about what needed to be done by me, such as Wi-Fi debugging and the Xcode setup for creating the app. Downloading the SDKs took more time than creating the app.

**Prompts during the build.**

The objective here is to create an SDK such that I can use my Pixel Watch with my iPhone.

The final objective is to essentially create a bridge for me to sync the Pixel Watch with my iPhone. I will get data from the Pixel to my Apple Health. Plan accordingly. Do it fast.

You are not writing any code. You can write code while things download.

What can you do to find the sleep stage data? Can you export the entire data and then compute the sleep stage post facto on the iPhone?

Now it is a poorly designed watch app. Add options with multiple screens accessible from home to use the options. Add a logo in both the iOS app and the Pixel app.

Make a better looking iPhone app. The watch app is too slow.

Music on the watch still goes to Music on the iPhone. I want to have something such that the Spotify app on the watch can find the iPhone. Music commands are working but they go to the Apple Music app; I want a configuration to set Spotify.

Can you optimize the app and set bedtime on the Pixel Watch to 10 to 6? I want PixelBridge when I swipe from the home screen of the watch. I want these controls from the iPhone app.

How much data is the watch sending the phone every second/minute? Is that necessary? What can be stored and sent later? Yes. Can we do once an hour?

The calorie count is also counting inactive calories. Apple Health only takes active calories.

</div>
</details>
]]></content:encoded>
    <source url="https://drdebmath.github.io/blog/posts/personal-software.md">The Oblivious Robot</source>
  </item>
  <item>
    <title>The Death of Big O Notation</title>
    <link>https://drdebmath.github.io/blog/p/death-of-big-o.html</link>
    <guid isPermaLink="false">https://drdebmath.github.io/blog/#death-of-big-o</guid>
    <pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate>
    <description>Big O hides a constant and a threshold. Models now shave exponents that never run; the next frontier is the constants: ultra fine-grained complexity.</description>
    <content:encoded><![CDATA[<div class="by-collab">

What theoretical computer scientists do, essentially, is hide big constant terms inside the big O notation. Now a
lot of software systems and algorithms are subject to massive optimization by automated research with language
models. So it has become a trend to push some obscure constants to smaller and smaller values, while the overall
complexity still hides behind a rather big O. But in practice, does any of it make anything faster?

</div>

## What the O hides

<div class="by-ai">

Recall the definition. $f(n) = O(g(n))$ means that there are constants $c > 0$ and $n_0$ such that $f(n) \le c \cdot g(n)$ for every $n \ge n_0$. The notation hides two numbers: the factor $c$, and the size $n_0$
from which the bound holds at all. An algorithm with a better exponent and an enormous $c$ or $n_0$ is faster only
on inputs that nobody will ever have.

Such algorithms have a name. Richard Lipton called them
[galactic algorithms](https://rjlipton.com/2010/10/23/galactic-algorithms/) in 2010: an algorithm that is
"wonderful in its asymptotic behavior, but is never used to actual compute anything." He also noted that they
"can lead to new methods, new directions, and sometimes can be made practical."

The field knows the difference. Virginia Vassilevska Williams, in her survey
[*On some fine-grained questions in algorithms and complexity*](https://people.csail.mit.edu/virgi/eccentri.pdf),
explains why fast matrix multiplication is set apart from "combinatorial" algorithms. The notion is not
well-defined, she writes, "however it roughly means that the runtime should have a small constant in the big-O,
and that the algorithm is feasibly implementable."

</div>

## Exponents that never run

<div class="by-collab">

Take the recent breakthrough in integer multiplication. The size of the integers you are multiplying does not
matter: you never get to use the speed-up, because the assumptions that underlie the breakthrough are impractical.

</div>

<div class="by-ai">

In 2019 David Harvey and Joris van der Hoeven gave an algorithm that multiplies two integers of $n$ bits in $O(n \log n)$ bit operations
([Annals of Mathematics, 2021](https://annals.math.princeton.edu/2021/193-2/p04)), the bound that Schönhage and
Strassen conjectured in 1971. It is a theorem about the exponent: the factor and the input size from which it
wins stay inside the O.

Matrix multiplication shows the trend in motion. The exponent $\omega$ is the smallest $\tau$ such that two $n \times n$ matrices can be multiplied in $O(n^{\tau + \varepsilon})$ operations for every $\varepsilon > 0$.
Its recent history:

- $\omega < 2.371339$: Alman, Duan, Vassilevska Williams, Xu, Xu and Zhou,
  [April 2024](https://arxiv.org/abs/2404.16349).
- $\omega < 2.371177$: Dupont et al. (Google DeepMind, with Alman and Vassilevska Williams), who solved the
  underlying optimization problem with machine learning methods refined by AlphaEvolve,
  [August 2026](https://arxiv.org/abs/2608.16884).

Each bound holds as $n$ tends to infinity, and the factor hidden in the O may depend on $\varepsilon$.

None of these algorithms will multiply a matrix in any computer. They are real mathematics, but they are progress
on the exponent and not on the running time.

Models also polish constants inside theorems. In
[*Early science acceleration experiments with GPT-5*](https://arxiv.org/abs/2511.16072) (November 2025), Sébastien
Bubeck reports that GPT-5 Pro, given a paper on gradient descent, improved its sufficient step-size condition from $\eta \le 1/L$ to $\eta \le 1.5/L$. The authors of the paper had meanwhile shown, in their second version, that $1.75/L$ is sufficient, and the first version had already shown that the property fails beyond $1.75/L$. Bubeck's own assessment is that the result "could probably have been achieved by some experts in the field
in a matter of hours."

</div>

## Where the speed comes from

<div class="by-collab">

I feel this is a transient period, where people are optimizing exponents that do not really matter. Real gains
in efficiency will come from models that optimize automatically by exploiting the processor architecture,
memory access at the system level, and operation pipelining on the CPU or GPU. I think that is where the gain lies.

Companies are under high pressure to optimize the inference stack for LLMs. So they will also optimize the
algorithms where the boundary conditions are relatively normal, for everyday use.

</div>

<div class="by-ai">

Two examples show what such gains look like.

- **FlashAttention** ([Dao et al., NeurIPS 2022](https://arxiv.org/abs/2205.14135)) computes exact attention
  with the same quadratic number of operations as before. It changes only the order of the work, in tiles, so
  that far fewer values move between the GPU's large memory and its small on-chip memory. The paper reports a
  $3\times$ speed-up on GPT-2 at sequence length 1K. No exponent changed.
- **AlphaDev** ([DeepMind, 2023](https://deepmind.google/discover/blog/alphadev-discovers-faster-sorting-algorithms/))
  used reinforcement learning to search for assembly code that sorts three to five elements. The routines went
  into the LLVM `libc++` standard library, with reported gains of up to 70% on short sequences and about 1.7% on
  sequences of over 250,000 elements. Sorting stayed $\Theta(n \log n)$, and the instructions got fewer.

In both cases the asymptotic analysis says nothing happened.

</div>

## Ultra fine-grained complexity

<div class="by-collab">

That leaves us in a situation where the big O notation is not really useful. Even an algorithm that is optimal in
big O terms may not be good enough. For example, a problem has an $\Omega(n \log n)$ lower bound, and an algorithm
runs in $O(n \log n)$, but the big O hides a constant like 100 or 1000.

I think the next task in most optimization strategies, and maybe in modern theoretical computer science, is to
move beyond the big O and optimize the constants that hide under it. This will be the new **ultra fine-grained
complexity**.

</div>

<div class="by-ai">

The name places it next to an existing field. Fine-grained complexity, in Vassilevska Williams's words, is built
on reductions "that focus on exact running times," but exact there means the exponent: it asks whether a
problem solvable in $O(n^2)$ time can be solved in $O(n^{2 - \varepsilon})$ for some $\varepsilon > 0$. The
constant in front stays hidden.

Precedents for exact constants exist. Princeton's algorithms course, following Sedgewick and Wayne, teaches
*tilde notation*: $f(N) \sim g(N)$ if $f(N)/g(N)$ converges to 1. Big O, in
[the course notes](https://www.cs.princeton.edu/courses/archive/fall15/cos226/lectures/study/14AnalysisOfAlgorithms.html),
is "similar in spirit to Tilde notation, but throw[s] away leading constants." So $\sim 2n$ and $\sim 3n$ are
different statements, while $O(n)$ covers both.

Median selection has been studied at exactly this level for decades. Finding the median of $n$ elements takes at
most $2.95n$ comparisons ([Dor and Zwick, 1995](https://eccc.weizmann.ac.il/report/1995/031)), and at least $(2 + 2^{-80})n$ minus lower-order terms (Dor and Zwick, SIAM J. Discrete Math. 2001; see
[OEIS A215476](https://oeis.org/A215476)).
Both bounds are $\Theta(n)$. The open question is the constant between 2 and 2.95, and it has stayed open for
about thirty years.

The hard part is the cost model. Comparisons are a clean unit for a median, but FlashAttention and AlphaDev won by
counting other things: memory transfers, instructions, pipeline stalls. A constant is only meaningful relative to
a model of the machine. So ultra fine-grained complexity would need cost models close enough to real hardware for
its constants to predict running times, and simple enough to prove theorems in.

</div>

<details class="original">
<summary>Original input</summary>

<p class="note">Lightly edited for coherence: dictated as one paragraph, split into paragraphs, with capitalization and punctuation fixed. "Exposed" is read as "exposés", "efficiency will gains" as "efficiency gains", "process architecture" as "processor architecture". The instruction about the draft and the research is kept as the request.</p>

<div class="by-human">

Write another blog post about the death of big O notation.

What theoretical computer scientists do is essentially hide big constant terms inside big O notations. Now that a lot of the software systems and the algorithms are subject to massive optimization by automated research using language models, it has become a trend to optimize some obscure constants into smaller and smaller values, so that the overall complexity still hides behind some rather big O notation. But in practice, does it make anything any faster?

For example, take the recent breakthrough in integer multiplication. It does not even matter what the size of the integers that you are multiplying is, to essentially avail that speed-up, because the assumptions that underlie these breakthroughs are impractical.

But I feel this is just a transient period where people are optimizing for exponents which do not essentially matter. Real efficiency gains will come from models automatically optimizing for gains by exploiting processor architecture and system-level memory access and operational pipelining on the CPU or GPU. I think that's where the gain lies. And as companies have high pressure to optimize the inference stack for LLM inference, they will also try to optimize the algorithms where the boundary conditions are relatively normal and for everyday use.

So that leaves us in a situation where big O notation is not really useful. What we really want is, even for optimal algorithms that are claimed in terms of big O notation: for example, an algorithm has $\Omega(n \log n)$ complexity, the big O is also $O(n \log n)$, but the big O hides a constant like 100 or 1000 or something. I think the next task in most of the optimization strategies is that we move beyond, and maybe move with modern theoretical computer science, to optimize these constants that hide under the big O notation. And this will be the new ultra fine-grained complexity.

This is what I want the blog to be about. Find some examples, also research some of the things in the background, search for exposés regarding these things, find exposés from famous fine-grained complexity researchers, and make a draft.

</div>
</details>
]]></content:encoded>
    <source url="https://drdebmath.github.io/blog/posts/death-of-big-o.md">The Oblivious Robot</source>
  </item>
  <item>
    <title>Mapping the Frontier Before the Models Arrive</title>
    <link>https://drdebmath.github.io/blog/p/mobility-matters.html</link>
    <guid isPermaLink="false">https://drdebmath.github.io/blog/#mobility-matters</guid>
    <pubDate>Mon, 12 Oct 2026 00:00:00 +0000</pubDate>
    <description>How Mobility Matters, a living survey of open problems on mobile agents, robots and particles, is built, and whom it serves: newcomers, returning researchers, and the coming model-made results.</description>
    <content:encoded><![CDATA[<div class="by-collab">

I built [Mobility Matters](https://drdebmath.github.io/mobility-matters/), a living survey of open problems on
autonomous agents, robots and particles. I built it to prepare for the results that AI models are about to
produce.

I had been thinking about building something like it for a while, and the recent OpenAI math results were a
wake-up call. If the future is about getting proofs for free, what makes them useful is putting them in
perspective.

</div>

<div class="by-ai">

Putting a proof in perspective means answering three questions about it: which setting it settles, which
other settings it carries over to, and what stays open after it. The atlas is built to answer all three.

</div>

## Why a map of open problems

<div class="by-collab">

Research is going to be democratic anyway. Anyone, anywhere, will eventually have access to a frontier model, or
to a model capable enough to contribute at the frontier of research. So why not start by enabling a random high
schooler to contribute to frontier research?

The problems that need attention should fall first. The compute is also a fixed budget, and it should not be
wasted: if $X$ tokens, or $X$ compute, are enough to solve a problem, then the cost should not grow to $100X$
because a hundred different people try the same thing.

So everything comes down to making research accessible. Mobility Matters is one way I am trying to do it. Anyone
can find the questions at the frontier by going through the assumptions and choosing their own branch. When they
succeed, they send a request to update the latest result for that problem under those assumptions.

</div>

<div class="by-collab">

The site also serves two other readers: undergraduates who are starting in the area, and researchers who are
returning to the field after a gap and need to catch up on the progress.

</div>

<div class="by-ai">

Each reader has an entry point. An undergraduate can start with *A first look* on a problem page and the
Perspectives essays, which explain why the models exist before they list bounds. A returning researcher can start
with the timeline, which dates every introductory result, current best and lower bound, and with
the progress map, which puts each lower bound next to the best upper bound and names the gap.

A map does two jobs here. It shows a newcomer where the open land is, and it shows everyone what has already been
claimed, so that nobody spends a month of compute proving a known bound again.

</div>

## What is on the map

<div class="by-ai">

The atlas covers four models: mobile agents on graphs, mobile robots in the plane, search in geometric domains
(linear search, evacuation, patrolling) and programmable matter. Inside them are six problem families and 51
problems, from exploration and dispersion to gathering, pattern formation, black hole search and leader election
among amoebots. The problems are split into 282 settings, 268 of them checked against the full text of their
papers, with 389 references. Eleven essays (*Perspectives*) connect the problems across models, and a ranked list
names the ten open questions whose answers would feed the most other problems.

For every setting the atlas records:

- the proven lower bounds, and what forces each one (its *bottleneck*, such as distance, symmetry breaking or
  indistinguishable Byzantine agents), with the assumptions its proof needs;
- the paper that introduced the setting, and the current best result;
- how far apart the two are: tight, near-optimal (a polylogarithmic gap), a polynomial gap, unknown, or
  impossible;
- the open questions, worded precisely.

</div>

## The assumption DAG

<div class="by-ai">

The central idea is that a problem is not one problem. Dispersion on a static graph with synchronous agents is
solved; dispersion on a temporal graph with asynchronous agents is a different question. So each model names its
**assumption axes**: graph dynamics, direction, timing, faults, communication, identifiers. Every axis is a scale.
Its lowest value assumes nothing (a temporal graph, asynchronous or silent agents, Byzantine faults), and each
higher value is a stronger assumption, which makes the problem easier.

</div>

<div class="by-collab">

The assumptions form a directed acyclic graph (DAG). Each end point of the DAG is a problem solved under the
assumptions that came before it as nodes.

</div>

<div class="by-ai">

The axes are independent, so a setting that assumes both a tree topology and a rooted start can be reached by
adding either assumption first: it has two parents. Each problem page draws its DAG, with every cited paper joined
to the settings where it gives a bound. A simpler tree view adds the axes in one fixed order, for browsing. Points
where the branches split but nobody has studied the setting are drawn dashed. These are honest gaps in the
literature, and anyone can open them.

Because the axes are ordered, results carry over without anyone writing them down. Thus an algorithm for a weaker
setting works in every stronger one, and a lower bound for a stronger setting holds in every weaker one. Every
setting's page lists the nearest settings in both directions.

*Choosing your own branch* is the [Explore](https://drdebmath.github.io/mobility-matters/#/explore) page: pick a model, a problem and a value per axis, and the atlas shows
the exact setting if it exists, the algorithms of the closest weaker settings and the lower bounds of the closest
stronger ones.

</div>

## A first look, for the high schooler

<div class="by-ai">

A map is no use if the reader cannot read the legend. So every problem opens with *A first look*, written for a
curious high-school student. Dispersion begins with $k$ electric cars arriving in a town where each charging point
serves one car and no car has a map. Then come two to four ideas, ordered by how useful they are to a newcomer: an
algorithm in a few numbered steps, or the situation an adversary builds for a lower bound, with a short counting
argument for why it works and how far it is from the best known result. Every technical word is defined.

</div>

## A website and a book

<div class="by-ai">

The atlas is a website and a book with the same content. The website is for finding things: a search box over
problems, settings, open questions, essays and references; the Explore page; a timeline that dates every
introductory result, current best and lower bound; and a progress map per problem that puts each lower bound next
to the best upper bound. It works on a phone. The book,
[mobility-matters.pdf](https://drdebmath.github.io/mobility-matters/mobility-matters.pdf), currently 535 pages, is
for reading and citing: models and their assumptions, the essays, every problem with its settings, all open
questions, an index of assumptions, the timeline and the bibliography.

Each model has one table of symbols, so a symbol means one thing inside a model, and a bound reads the same as in
the papers it cites. Every assumption has a precise definition and a short note on where it comes from.

</div>

## What makes it trustworthy

<div class="by-ai">

The atlas was compiled with Claude (Anthropic). A survey written with a model is only useful if a reader can trust
each line, so the rules are about evidence:

- **Read the full text.** A setting is marked *verified* only after its bounds are checked against the paper's
  model section and theorem statements, not the abstract. Abstracts have misstated settings: "asynchronous" for a
  semi-synchronous model, "common sense of direction" for a full compass. Unchecked entries are marked *draft* and
  say "as reported by".
- **Never invent** a reference, a venue or a bound, and cite only published papers and public preprints.
- **Ask, do not guess.** Questions of placement, naming and policy are decided by a person, not by the model.

Checking the full text pays off in small places. For example, the dispersion entry notes that several papers quote
a memory lower bound of $\Omega(\log(k + \Delta))$, but the $\log \Delta$ term has no published proof for
deterministic agents with identifiers.

One policy question was how to show results whose proofs are disputed. The answer:

</div>

<blockquote class="by-human">

Make disputed as another color. Don't show them as best.

</blockquote>

## The weekly update

<div class="by-ai">

A survey of a moving field is out of date the week after it is written. So the atlas updates itself once a week:

1. **Find new papers.** New papers on arXiv and OpenAlex are collected by keyword and by author (everyone cited
   at least twice in the atlas), together with Google Scholar alerts. Papers already in the atlas are dropped.
   This step uses no AI.
2. **Collect submitted results** from readers (see the next section).
3. **Read and place.** An AI agent triages the candidates, reads the relevant papers in full, and places each
   one: a better bound becomes the current best, a new set of assumptions becomes a new setting, a paper that does
   not fit yet goes under *Further reading*, and a new problem is proposed but not added.
4. **Check** that every citation resolves and every page and the book still build.
5. **Report.** Every change is listed as before and after, with the papers skipped and why, the papers behind a
   paywall, and the questions that need a decision.

Nothing goes online until a person has read the report. So the expensive step, reading a paper and checking its
theorems against a setting, happens once per paper, for every reader of the atlas.

</div>

## Submitting a result

<div class="by-ai">

Every problem page and every setting page ends with two links: **Suggest a result** and **Report an error**. Each
opens a [GitHub issue form](https://github.com/drdebmath/mobility-matters/issues/new/choose), with the problem and the setting already filled in. The result form asks for:

- the paper (title, authors, venue or arXiv id) and a link to the full text;
- the problem, and the setting, or the assumptions if the setting is new;
- the bound or impossibility, the theorem that states it, and the assumptions the paper makes;
- how the reporter wants to be acknowledged, and whether they are an author of the paper.

A third form suggests a new problem. Submitted results are read in the next weekly update, ahead of the papers it
finds itself, and each claim is checked against the linked paper, not against the text of the issue. Every issue
gets an answer: accepted, placed under *Further reading*, or declined, with the reason. Everyone whose result,
correction or suggested problem is accepted is listed on the atlas's [Acknowledgements](https://drdebmath.github.io/mobility-matters/#/acknowledgements) page, with the issue that
brought it in.

</div>

## Two other ways it could go

<div class="by-collab">

Mobility Matters is one way, and I think a promising one. Another way, less prominent but highly probable, is that
one of the big companies uses these problems as a test set for internal AI model development. Then they would
butcher the presentation of the results, and I would not be sure what exactly they are trying to show.

A third way is to verify all the research. Some of the results need to be formally verified, and some of the
assumptions in the community of mobile agents and robots are not encoded in a formal language. So perhaps the
second important project is to formalise these assumptions, so that anyone can build proofs on top of them.

</div>

<div class="by-ai">

The atlas already takes a first step. Every axis value carries a definition in the notation of its model, so
the assumptions are written down precisely, though in Markdown and LaTeX rather than in a proof assistant.

</div>

<details class="original">
<summary>Original input</summary>

<p class="note">Lightly edited for coherence: the instruction to read the atlas's agent instructions and the files in the Research folder kept as the request, dictation errors fixed ("A research any is anyway" read as "Research is going to be democratic anyway", "Then nothing goes down to" as "Then everything comes down to", "send a request and get up to say what is the latest result" as "send a request to update the latest result"), capitalization and punctuation fixed.</p>

<div class="by-human">

Read the Mobility Matters agent instructions and check inside the Research folder for the details, and write a blog post about how the website is built. Later: write about how the weekly automatic update works and how people can submit their results as GitHub issues. The reason I wanted to build such a website is to prepare for the upcoming model-induced results.

I have been thinking about building something like this for a while, and the recent OpenAI math results are a wake-up call. If the future is about getting proofs for free anyway, what makes them useful is putting the proofs in perspective.

Research is going to be democratic anyway. Why not start by enabling the random high schooler to contribute to frontier research? If anyone anywhere is going to have access to a frontier model eventually, or a model capable enough to contribute at the frontier of research, then the problems that need attention should be falling first, and there is a fixed budget. We should not waste resources, in the sense that if $X$ amount of tokens is sufficient to solve a problem, or $X$ amount of compute is sufficient to solve it, then this $X$ amount of compute should not be overrun by a hundred different people trying to do the same thing.

Then everything comes down to how to make research accessible. One way I am trying to do it is by making this website, a survey called Mobility Matters, where anyone can get access to the questions on the frontier by going through the assumptions and choosing their own branch, and then, when they are successful, they can send a request to update the latest result in a particular problem under certain assumptions.

The idea is also to serve undergrads starting with the area, and to help researchers who are returning to the field after some gap, to keep them updated about the progress.

The assumptions are a DAG. Each end point of the DAG is a problem solved under the assumptions that came before it as nodes.

This is one way of doing it, and I think it is a promising way. The other, less prominent but highly probable way is using these problems as a test set for some internal AI model development by one of the big companies. Then they would butcher the presentation of the results, and I would not be sure what exactly they are trying to show.

Another way is to verify all the research. Some of the things need to be formally verified, and some of the assumptions that we have in this community of mobile agents and robots are not encoded in a formal form. So perhaps the second important project is to formalise these assumptions so that anyone can build proofs on top of them.

</div>
</details>
]]></content:encoded>
    <source url="https://drdebmath.github.io/blog/posts/mobility-matters.md">The Oblivious Robot</source>
  </item>
  <item>
    <title>203 Errors and One Semicolon</title>
    <link>https://drdebmath.github.io/blog/p/semicolon.html</link>
    <guid isPermaLink="false">https://drdebmath.github.io/blog/#semicolon</guid>
    <pubDate>Tue, 06 Oct 2026 00:00:00 +0000</pubDate>
    <description>A CS103 recap as a three-minute Bollywood music video, made for fun in 11 hours over the midsem break: Suno, ChatGPT character sheets, a shader engine and an AI coding agent.</description>
    <content:encoded><![CDATA[<aside class="checklist by-ai">
<p class="checklist-title">Pipeline</p>
<p class="pass">Song</p>
<ul>
<li>Lyrics that cover the syllabus</li>
<li>Suno inputs in several styles</li>
<li>Generate takes, pick one</li>
<li>Cut repeats on bar lines</li>
</ul>
<p class="pass">Picture</p>
<ul>
<li>Align lyrics, find the beat</li>
<li>Write a treatment</li>
<li>Make the cast on green</li>
<li>Write one brief for every scene</li>
<li>One file per scene</li>
<li>Check stills</li>
</ul>
<p class="pass">Ship</p>
<ul>
<li>Render in segments</li>
<li>Upload, link from the lecture</li>
</ul>
</aside>

<div class="mixed">

<span class="by-collab">During the midsem break of CS103 (Computer Programming, C++) in 2026, I made a music video for fun, after the midsem was over.</span> <span class="by-ai">It recaps Lectures 1 to 11: **Semicolon**, a three-minute Bollywood dance number about one missing character, now linked at the end of [Lecture 12](https://drdebmath.github.io/IITIJul2026CS103/).</span>

<span class="by-collab">The idea came from the viral p(doom) video. It took 11 hours in total. Once the pipeline was set up, the video part was mostly automatic. My time went into setting up the pipeline, choosing a catchy song among the candidates, and figuring out how to mix them.</span>

</div>

<div class="by-ai">

The template was [mexicat's take on the p(doom) video](https://x.com/_mexicat/status/2103108369569726802), posted
on X on 24 September 2026. I shared the result on X:

</div>

<blockquote class="by-human">

I used [@_mexicat](https://x.com/_mexicat) template to make a video for my programming course.
Overfitted bollywood in that.

Debasish Pattanayak ([@drdebmath](https://x.com/drdebmath)),
[5 October 2026](https://x.com/drdebmath/status/2106974006150873265)

</blockquote>

<div class="video"><iframe src="https://www.youtube-nocookie.com/embed/7JqWeqbx_XQ" title="Semicolon: CS103 midsem recap" allowfullscreen></iframe></div>

<div class="by-ai">

The story is a masala film. The Compiler, a dacoit chief, rules a ravine of 203 errors. His bandolier of
semicolons has one empty slot, and no program in the land compiles. Semi, the lost semicolon, travels through
Bollywood set pieces with a gang of operators and variables. At the end Semi lands after `return 0`, the
program compiles, and the 203 errors go up as Diwali fireworks.

This post records the steps, so they can be repeated for the next course. The files named below (song inputs,
treatment, scene brief and scripts) live in the course's working folder.

</div>

## 1. Lyrics in four styles

<div class="by-collab">

I started by experimenting with different lyrics and different song styles, and only then mixed them.

</div>

<div class="by-ai">

The lyrics had one job: name the ideas of Lectures 1 to 11, one per line. They were written for
[Suno](https://suno.com) in *Custom* mode, where each song has four fields: title, style of music, styles to
exclude, and lyrics with section tags such as `[Verse 1]` and `[Spoken Word, deep villain voice]`. Four songs
were written, each in its own file:

| Song | Style | Language |
|---|---|---|
| Compile Kar Le | Bollywood comedy item song, dhol and shehnai | Hindi and English |
| Semicolon | Bollywood dance pop, built like "A.P.T.": one silly word as the hook | English |
| My P(Segfault) Is Rising | glossy synth pop, after the viral "I'm Upping My P(doom)" | English |
| One Brace, One Scope | anime ending theme, J-rock | English |

Two tricks made the vocals work:

- **Script decides pronunciation.** In the Hindi-English song, Hindi words are written in Devanagari and English
  words in Latin letters. Romanised Hindi gets English phonetics, so *the* would be sung as the English *the*.
- **Spell code the way it is sung.** `i++` becomes "i plus plus" and `cin` becomes "see-in". The on-screen text
  comes from a separate copy of the lyrics with real code in it.

</div>

## 2. Generate the song

<div class="by-ai">

Suno rarely gets a song right on the first take, and it often skips spoken-word tags. So each song was generated
several times.

</div>

<div class="by-collab">

Writing the Hindi words in Devanagari was not enough on its own. To make the Hindi vocals work, I had to submit
a voice recording to Suno.

Choosing among the candidates came down to which song was the catchiest: **Semicolon**. The other part that took
real thought was how to mix the songs together.

</div>

<div class="by-ai">

The final mix opens with the spoken intro of Compile Kar Le, a nod to *Sholay*: "कितने error थे?" "सरदार, दो
सौ तीन।" (How many errors were there? Two hundred and three, chief.) Then Semicolon starts.

Songs from the free plan are for non-commercial use only. A classroom video fits that; a monetised upload does
not.

</div>

## 3. Cut the repeats

<div class="by-ai">

Pop songs repeat their chorus more than a recap needs. A script (`edit_cuts.py`) removed three repeated blocks,
32 seconds in total, and brought the song down to about three minutes. Each cut works like a loop point in a
sampler:

1. Both ends of a cut lie on bar lines, a whole number of bars apart.
2. Among such pairs, pick the one where the 3 seconds around the start and around the end sound most alike
   (cosine similarity of log-mel spectra over the beat grid).
3. Align the join to the sample by cross-correlation, and blend it with a 0.12 s equal-power crossfade.

The point is that playback jumps between two moments that already sound the same, so the join is hard to hear.

</div>

## 4. Align the lyrics and find the beat

<div class="by-ai">

The video has to know when every word is sung. [Whisper](https://github.com/openai/whisper) transcribes the song
with word timestamps, but its transcript of a sung, half-Hindi song is not the lyrics. So `align.py` matches the
*written* lyrics to Whisper's words (a sequence diff) and takes the timestamps from the matches. The screen then
shows exactly the lyrics that were written, at the moment they are sung.

[librosa](https://librosa.org) supplies the rest: a beat grid at 136 BPM, the downbeat of every bar, the
sections, and loudness envelopes at 100 frames per second. Every cut, pose change and camera move in the video
lands on one of these.

</div>

## 5. Write a treatment

<div class="by-ai">

A treatment (`TREATMENT.md`) is the film on one page: the idea, the cast, the style rules, and one row per
scene with its time, lyric, what we see, the cast, the world and the beat moments. Its central rule is that the
lyrics are a **loose map, not a storyboard**. Each line names a C++ idea, and the scene shows a film situation
where that idea *happens* to the characters:

- The `int` box carries 3.7 across a river, and the `.7` falls overboard (truncation).
- A child variable `x` wanders out through brace-shaped fair gates and fades to a ghost (scope).
- A truck with no brakes rolls through every village on the road (`switch` without `break`).
- Two doubles try to marry, and the priest refuses: `0.1 + 0.2 != 0.3`.
- The `.at()` ticket inspector catches a passenger stepping off past the last coach (bounds checking).

The code on screen is correct C++.

</div>

## 6. Make the cast

<div class="by-ai">

The characters are drawn by ChatGPT as character sheets: several characters standing side by side on a pure
green (`#00FF00`) background. Two short scripts turn each sheet into sprites:

- `key_green.py` makes the green transparent. A pixel's alpha comes from how much greener it is than its other
  channels, and green spill on the edges is pulled back.
- `split_sheet.py` cuts the sheet at runs of fully transparent columns, so each character becomes one PNG.
  Parts of one glyph stacked vertically, like the two bars of `==`, stay together.

The cast: the Compiler and his henchman, Semi, a hero and a heroine with glowing laptops, the `++` twins, the
`==` sisters, the `&&` toll guard, the `<<` postman, the `>>` fisherman, and variables such as the `int` box,
the `const` statue and the `vector` train. Each has a few poses to swap on the beat.

</div>

## 7. One brief, one file per scene

<div class="by-ai">

The first engine was a lyric video in [three.js](https://threejs.org): one "station" per section, with the
camera flying between them. The final video instead uses a fork of pdoom-video, mexicat's
template from the post above (by Giacomo Magnanini, MIT license). Every background is a GLSL shader, and the
characters, props and lyrics are drawn on a canvas on top. There are no stock images.

Each scene is one TypeScript file, written by an AI coding agent from a shared brief,
`SCENES.md`. The brief says what a scene file looks like, which sprites exist, and the rules every scene
follows:

- Find lyric lines by their text, never by hard-coded times. The song can then be re-cut without breaking
  the scenes.
- Output depends on the song time only: seeded noise, never `Math.random()`. A frame can then be rendered on its
  own, in any order.
- Poses and hits land on beats or sung words, and the camera snaps on downbeats.
- Under 25 ms per frame.

A brief like `SCENES.md` is an `AGENTS.md` for one kind of file. The agent starts each scene with no memory, so
everything it needs is written down once, and the scenes come out consistent.

</div>

## 8. Look at the stills

<div class="by-ai">

The agent checked its own work by rendering stills: one frame per lyric line, plus the instrumental parts,
tiled into contact sheets. A contact sheet shows in one image whether the lyric covers a face, whether the
`;` lands on "line", and whether a cut is on the beat. The brief tells the agent to iterate until every frame looks like a
polished music video.

</div>

## 9. Render in segments

<div class="by-ai">

The renderer drives a headless browser frame by frame, with 4 sub-frames per frame for motion blur. A single run
over the whole song lost its page about 1,000 frames in. Since every frame depends only on the song time, the
fix was simple: render 30-second segments, each in a fresh browser (with one retry), join them losslessly, and
add the full song once at the end, so there are no audio seams.

The result is a 1080p, 60 fps master of 1.5 GB, and a 1080p, 30 fps web copy of 84 MB for YouTube.

</div>

## What carries over

<div class="by-ai">

None of the steps needed a studio, and most of them are a short script. The decisions that matter are the
lyrics, the choice of take, and the treatment's rule that each idea must *happen* on screen. Everything else
can be written down well enough for an agent with no memory to do it.

</div>

<div class="by-collab">

So the video part was mostly automatic. The 11 hours went into the pipeline and the song.

</div>

## What would have made it faster

<div class="by-collab">

The character sheets in ChatGPT and the song generations in Suno went through Codex computer use, which works
in one tab at a time. It does not use parallel tabs, and parallel tabs would have made the work much faster and
more hands-off. I said as much on X, on the
day I made the video:

</div>

<blockquote class="by-human">

i think computer use would go boom with multiple parallel cursor support with parallel tabs doing things. The
model can easily move a hundred cursors in different planes. For example a 100 apps, each with its own cursor.
Who is making this new OS?

Debasish Pattanayak ([@drdebmath](https://x.com/drdebmath)),
[3 October 2026](https://x.com/drdebmath/status/2106330823234572674)

</blockquote>

<div class="by-collab">

Reliability was the other problem. The subagents died several times, from internet errors, API errors and usage
limits, and each time a subagent's memory was lost with it:

</div>

<blockquote class="by-human">

API error is killing subagents and their memory is getting lost. Reliability has to go a long way for LLM
serving.

Debasish Pattanayak ([@drdebmath](https://x.com/drdebmath)),
[3 October 2026](https://x.com/drdebmath/status/2106363259381235766)

</blockquote>

<div class="by-ai">

A written brief makes such deaths cheaper. A new subagent can read `SCENES.md` and the treatment, and start the
scene again without anything from the old one. The same holds for the renderer: a segment that dies is rendered again,
and nothing else is lost.

</div>

<details class="original">
<summary>Original input</summary>

<p class="note">Lightly edited for coherence: editing instructions about embedding posts from X removed (the post is quoted in the text instead), capitalization and punctuation fixed. The steps themselves come from the files in the course folder (song inputs, treatment, scene brief, scripts, logs and review stills).</p>

<div class="by-human">

See the video in the CS103 course folder for 2026 and write a blog post based on the steps taken there.

I made the video after the midsem. It was something fun I did during the midsem break. I spent 11 hours in total, while the video part was mostly automatic after setting up the pipeline, choosing a catchy song among the candidates and figuring out how to mix them. The idea came from the viral p(doom) video.

I also complained about the lack of parallel tabs in Codex computer use (for both the character sheets and the Suno generations), which I thought would have made my work much faster and more hands-off.

I initially experimented with different lyrics and different song styles. Then I mixed them. Then I had to submit a voice recording to Suno to make it work for the Hindi vocals.

The subagents died several times due to internet errors, API errors and usage limits.

</div>
</details>
]]></content:encoded>
    <source url="https://drdebmath.github.io/blog/posts/semicolon.md">The Oblivious Robot</source>
  </item>
  <item>
    <title>Writing Papers When Your Co-author* Has No Memory†</title>
    <link>https://drdebmath.github.io/blog/p/co-author-with-no-memory.html</link>
    <guid isPermaLink="false">https://drdebmath.github.io/blog/#co-author-with-no-memory</guid>
    <pubDate>Sat, 26 Sep 2026 00:00:00 +0000</pubDate>
    <description>A checklist for writing research papers with an AI co-author: literature search, glossary, phrase bank, structure, polish and compression, and where AI helps at each step.</description>
    <content:encoded><![CDATA[<p class="footnote">* <span class="by-collab">The co-author with no memory is the AI agent (simply "the AI" below), or your oblivious supervisor.</span> <span class="by-ai">Either way, every session starts from scratch, so the glossary, the phrase bank and the structural rules below have to be handed over each time.</span></p>

<p class="footnote by-collab">† I was talking about this with my students, and wrote it down here so they can find it again and refer back to it.</p>

<aside class="checklist by-collab">
<p class="checklist-title">Checklist</p>
<p class="pass">Prepare</p>
<ul>
<li>Search the literature</li>
<li>Verify every reference (dblp → DOI)</li>
<li>Check claims about cited work against the full text</li>
<li>Build a glossary, anchored to a reference text</li>
<li>Build a phrase bank</li>
</ul>
<p class="pass">Structure</p>
<ul>
<li>Define terms before using them</li>
<li>Use connectives: <em>However</em>, <em>Thus</em></li>
<li>Algorithms: idea → description → variables → pseudocode → proof</li>
<li>Split into small procedures</li>
<li>Motivation before results</li>
<li>Climb: each result removes the last one's bottleneck</li>
<li>Informal overview: challenge, then how</li>
<li>Side results as remarks</li>
<li>Headings that say what is inside</li>
</ul>
<p class="pass">Polish</p>
<ul>
<li>No "this" or "that" references</li>
<li>One term per concept</li>
<li>Add reminders</li>
<li>Figures on the same or next page, with detailed captions</li>
<li>Simplify proofs</li>
<li>No "+1" inside big-O</li>
<li>Three rounds, one focus each</li>
<li>Compress: say each idea once, at its best place</li>
</ul>
</aside>

<div class="by-ai">

Most advice about writing papers is about sentences. Most problems in papers are about something else: a term used three pages before it is defined, an algorithm shown as pseudocode with no idea behind it, a proof that says "this implies that" without saying what *this* is. None of these are problems of talent. They are problems of process, and process can be learned.

The process below works in passes. Each pass has one job: first prepare, then structure, then polish. AI is now good enough to help with every pass, but only if you know what the pass is for. So this post first describes the passes, then where AI fits into each one.

</div>

## Prepare: literature, glossary, phrase bank

<div class="by-collab">

Start by **searching the literature**. This is not only about citing related work. Knowing what has been done tells you what your contribution actually is, and it shows you the vocabulary your community already uses. A paper that invents its own words for standard ideas makes readers translate before they can understand. Tools that show papers as a network of citations help here: [Connected Papers](https://www.connectedpapers.com/) and [Research Rabbit](https://www.researchrabbit.ai/) show which papers cite which, and [Elicit](https://elicit.com/) finds and summarizes papers that answer a research question.

**Read what you cite.** Every statement about a cited paper should be checked against its full text, not only its abstract, and credit should go to the work that introduced an idea, with later improvements credited to the works that made them. A citation style also needs one decision, made once: "First author et al." for more than two authors, both names for two, and full author lists only in the bibliography.

Next, **build a glossary**: a running list of every technical term in the paper, each with a one-line definition. The glossary forces a decision: one term for each concept. Once you have called something a *round*, it is never a *step* or a *phase* two sections later.

</div>

<details class="example">
<summary>Example: a glossary for <em>Asynchronous Dispersion with Optimal Time Complexity</em></summary>

<div class="by-collab">

Condensed from [the paper](https://arxiv.org/abs/2507.01298) (Pattanayak, Kshemkalyani, Kumar, Molla and Sharma, ICPP 2026).

| Term | Meaning |
|---|---|
| Graph | $G = (V, E)$ is simple, undirected and connected, with $n$ nodes, $m$ edges and maximum degree $\Delta$. |
| Anonymous graph | Nodes have no unique identifiers. |
| Port-labeled graph | At each node $v$, the incident edges carry distinct local labels (ports) $1, \dots, \delta_v$. The port at $u$ of edge $\{u, v\}$ is $p_{uv}$, and in general $p_{uv} \neq p_{vu}$. |
| Agent | One of $k \le n$ mobile agents $a_1, \dots, a_k$, each with a unique ID in $[1, k^{O(1)}]$. |
| Dispersion | The agents relocate until no node hosts more than one agent. |
| Rooted / general configuration | Initially, all agents are on one node / on at least two nodes. |
| Local model | An agent communicates only with agents at the same node. |
| CCM cycle | Communicate–Compute–Move: read the memory of co-located agents, compute, then write memory and exit through a port. |
| $\mathcal{SYNC}$ / $\mathcal{ASYNC}$ | Agents activate in common rounds / at arbitrary times, with every agent active infinitely often and every cycle finishing in finite time. |
| Epoch | A minimal interval in which every agent completes at least one CCM cycle. *Time* means rounds in $\mathcal{SYNC}$ and epochs in $\mathcal{ASYNC}$. |
| Memory complexity | The number of bits an agent stores from one CCM cycle to the next. |
| Settled agent, $\psi(x)$ | The agent that stays at node $x$. Its ID stands in for the ID of the node. |
| Edge type | t11, tp1, t1q or tpq: whether the edge has port 1 at both ends, only the far end, only the near end, or neither. |
| Port-one tree (P1Tree) | A tree in which every vertex has an incident tree edge of type t11, tp1 or t1q. |
| DFShead | The group of agents leading the depth-first search. |
| Vacated node | A visited node whose settled agent (a *settled scout*) travels with the DFShead, and returns in the retrace phase. |
| Parallel probing | Neighborhood search in which the agents at the DFShead probe the ports of the current node in parallel. |

</div>
</details>

<div class="by-collab">

Then **build a phrase bank**, a small set of fixed phrasings for the things you say again and again. In a paper about mobile agents on graphs, that might be:

> the agent moves from node $u$ to node $v$

Use that exact phrase every time, never "the agent goes to the neighbor" in one place and "node $v$ is reached" in another. Repeated wording can feel dull to write, but it is restful to read: the reader stops parsing sentences and starts following the argument.

</div>

## Structure: the order in which ideas arrive

<div class="by-collab">

A reader meets ideas one at a time, in the order you chose. Structure is about choosing that order well.

**Define terminology before using it.** This sounds obvious and is violated constantly, usually because a definition got moved during revision. The glossary helps here: for each term, check that its first use comes after its definition.

**Use connectives.** Words like *However* and *Thus* are the joints of an argument. They tell the reader whether the next sentence contrasts with, follows from or merely adds to the previous one. Without them, the reader has to guess the logic.

**Present each algorithm in layers**, from the outside in:

1. the high-level idea, in a few sentences;
2. a detailed description in prose;
3. the variables: what each one holds and how it changes;
4. the pseudocode;
5. the correctness proof.

A reader who stops after the first layer still leaves with something. A reader who goes all the way reaches the pseudocode already knowing what it is meant to do.

**Split the algorithm into small functions or procedures.** Each piece can then be described, and proved correct, on its own. A proof that says "by Lemma 3, `Explore` terminates" is much easier to check than one that reasons about a single hundred-line procedure.

**Put motivation before results.** Tell the reader why a problem matters before telling them what you achieved. The same rule shapes a good abstract, so follow an abstract-writing guide when you write yours. A good one is Neel Nanda's [Highly Opinionated Advice on How to Write ML Papers](https://www.alignmentforum.org/posts/eJGptPbbFPZGLpjsp/highly-opinionated-advice-on-how-to-write-ml-papers). Its annotated abstract shows the job of each sentence. Open the abstract with one sentence of practical motivation, and use only terms that describe your results.

**Climb gradually.** If the paper has several contributions, order them as steps, each one removing the bottleneck of the previous one. Present the steps in the same order everywhere: in the introduction, in the overview of techniques and in the body.

**Give an informal overview of techniques** near the start: high level, compressed, and saying that it is informal. Each paragraph opens with a challenge and continues with how the paper overcomes it, with no separate *Challenge* and *Solution* labels.

**Rank your results.** The main results get sections; side results become remarks, not subsections. A result that is never used is removed.

**Write headings that say what is inside.** "Results" or "The protocol" tells the reader nothing. Combine short related subsections into one, and inside it use lighter structure instead of more subsections: a numbered list, paragraphs whose first sentence states their topic, or italic lead-ins.

**Don't overclaim the literature.** "Most approaches" and "always" are claims too, and they need sources.

</div>

## Polish: removing every chance to misread

<div class="by-collab">

The last pass is about ambiguity: every place where a careful reader could take a sentence two ways.

**No "this" or "that" as a reference.** Name the object precisely. Compare:

> *Before:* It then moves there, and this guarantees termination.
>
> *After:* The agent then moves from node $u$ to node $v$. The move from node $u$ to node $v$ guarantees termination.

The second version is longer and cannot be misread.

**No two terms for the same thing.** If the reader sees *robot* in one paragraph and *agent* in the next, they will reasonably wonder whether these are different entities.

**Add reminders.** In a long paper, readers forget. Short signposts, like *Recall that …* or *As shown in Section 3, …*, cost a few words and save the reader from flipping back.

**Check figures and references.** A figure should appear on the same page where it is first referred to, or on the next page. Give each figure a detailed caption, so it can be understood without hunting through the text for an explanation. A LaTeX trick for placement: put each figure in its own file and include it with `\input{figures/p1tree}`. That one line decides where the figure lands, so move it up or down in the source until the figure sits on the right page. Use colour-blind-safe colours, and let line styles (dashed, double, grey) carry the same meaning.

**No additive constants in asymptotic bounds.** Write $O(m)$, not $O(m+1)$. State once that $n \ge 2$, and the reader fills in the rest. Keep exact counts, and factors whose 1 carries a term of the bound, such as $m(1 + \Delta/k) = m + m\Delta/k$.

**Simplify proofs wherever you can.** A shorter proof is easier to check, easier to trust and harder to get wrong. If a case analysis can be replaced by one invariant, replace it.

**Polish in rounds, one focus each.** At least three: (1) "this" and "that" references, glossary terms and fixed phrases; (2) definitions before first use, connectives and the order of ideas; (3) the precision of every claim against the theorem or source it rests on. A reader looking for everything at once finds little.

**Compress.** Papers repeat themselves: the same caveat in the introduction, the overview and the body. In every polishing pass, list each idea stated more than once, with all its locations; keep the place where it is stated best; and shorten the others to a reminder (*Recall that …*) or a reference. State each result first at its core, the main bound in one sentence, then enrich it step by step: refinements, tightness, special cases, comparisons with earlier work.

</div>

## Where AI fits

<div class="by-ai">

The rule for every pass is the same: **AI proposes, you verify.**

</div>

<div class="mixed">

- <span class="by-collab">**Literature search.**</span> <span class="by-ai">AI can produce references to papers that do not exist, so every citation needs checking.</span> <span class="by-collab">One way to verify a whole bibliography: ask the AI to take each reference from [dblp](https://dblp.org), then use the DOI to find the actual paper and check the metadata (authors, title, venue, year) against your entry. Do this for all your references. For claims about cited work, ask the AI to record where it found the full text and which pages it read, and to mark every claim whose source it could not access.</span>
- <span class="by-collab">**Glossary.** A glossary can be built up across several projects rather than from scratch for each paper. For consistency, anchor it to a reference paper or book in your area and follow its terms.</span> <span class="by-ai">The AI can then check a draft against it: ask it to list every technical term, where it is first used and where it is defined, and to flag pairs of terms that seem to mean the same thing.</span>
- <span class="by-collab">**Phrase bank.** The phrase bank is a meta thing: technical phrases hide inside ordinary-looking sentences, so read the draft line by line to identify them. Then give the AI reference phrases, so that it knows moving from $a$ to $b$ is a technical phrase with a fixed form, not wording to paraphrase.</span> <span class="by-ai">Read its changes; don't accept them wholesale.</span>
- <span class="by-collab">**Structure.** Specify structural requirements at the beginning, before the AI drafts or revises anything: terms defined before use, the five layers for each algorithm, motivation before results.</span> <span class="by-ai">Afterwards, check with concrete questions, such as *Which terms are used before they are defined?* The AI is a dependable reviewer when the question is this specific.</span>
- <span class="by-collab">**Polish.** Polishing works as find-then-generalize: find one instance of a problem yourself, such as an unclear "this" or two terms for the same thing, then ask the AI to find all such instances and disambiguate each one.</span> <span class="by-ai">For proofs, the AI can suggest a simpler argument, but you must check every step yourself. A proof that reads well and a proof that is correct are not the same thing.</span>
- <span class="by-collab">**Compression.** Ask the AI for an inventory of repetitions and the word count of each section before and after, so that you can see what was cut.</span>
- <span class="by-collab">**Parallel work.** Independent jobs, such as a reference check, can run in the background while the AI polishes. Review their results before applying them.</span>

</div>

### Prompts to copy

<div class="by-ai">

Replace the parts in square brackets.

**Verify references**

```text
For each reference in the bibliography below, find its dblp entry and its DOI. Open the DOI and check that the authors, title, venue and year match my entry. List every mismatch and every reference you could not find. Do not guess.

[paste the .bib file]
```

**Check terms against the glossary**

```text
Here is my glossary, then my draft. List (1) every technical term in the draft that is not in the glossary, (2) every term used before it is defined, with where it is first used and where it is defined, and (3) pairs of terms that seem to mean the same thing. Do not rewrite anything.

Glossary: [paste]
Draft: [paste]
```

**Keep fixed phrases fixed**

```text
These are fixed technical phrases in my paper. Never paraphrase them. Wherever the draft says the same thing in other words, replace it with the fixed phrase, and list each change.

Phrases: [paste]
Draft: [paste]
```

**Set the structure first**

```text
Follow these rules for everything you write or revise: define every term before its first use; present each algorithm as high-level idea, detailed description, variables, pseudocode, correctness proof; state the motivation before each result. If the draft breaks a rule, tell me where instead of silently rewriting it.
```

**Compress**

```text
List every idea, result or caveat that my draft states more than once, with all its locations. For each, say where it is stated best, and propose how to shorten the other occurrences to a brief reminder or a reference, without losing the idea. Report the word count of each section before and after. Do not apply the changes.

Draft: [paste]
```

**Find all instances of one problem**

```text
Here is one problem in my draft: "[quote the sentence]". The problem is [for example: "this" does not name what it refers to]. Find every other instance of the same problem in the draft, and for each one propose a rewrite that names the precise object.

Draft: [paste]
```

</div>

### An AGENTS.md for your paper

<div class="by-ai">

Pasting prompts works for one task. For a whole paper, write the rules down once where the AI will find them. Many coding agents, such as Codex and Cursor, read a file called `AGENTS.md` at the start of every session; Claude Code reads `CLAUDE.md`, which can contain just the line `@AGENTS.md`. Either way, the file works as the memory the co-author lacks.

<a href="agents/writing-papers/AGENTS.md" download="AGENTS.md">Download AGENTS.md for writing papers</a>: this checklist and the advice above, written as instructions for an AI agent. Put it at the root of your paper's repository, next to `main.tex`, and keep `glossary.md` and `phrases.md` beside it. It tells the agent to verify every reference, follow the glossary and the phrase bank, report problems instead of silently fixing them, and keep a log of AI use for the disclosure. Edit it to fit your paper.

</div>

<div class="by-collab">

The file also has a section for the authors' instructions about this particular paper: the working draft, the order of the contributions, notation, citation style. Ask the AI to record each new instruction there, with its date, as soon as you give it. Otherwise the next session repeats the mistake you corrected in the last one.

</div>

<div class="by-ai">

**Disclosure.** Venues increasingly ask how AI was used. A declaration like the one at the top of this post, kept as you work, makes that easy.

</div>

<div class="by-ai">

None of this replaces thinking about the problem. What AI can take over is the mechanical part of careful writing: consistency checks, reference hunting and first drafts of prose. That leaves more time for the part only you can do.

</div>

## More reading

<div class="by-ai">

- Neel Nanda, [Highly Opinionated Advice on How to Write ML Papers](https://www.alignmentforum.org/posts/eJGptPbbFPZGLpjsp/highly-opinionated-advice-on-how-to-write-ml-papers) (2025): paper structure, and an annotated abstract that shows the job of each sentence.
- Leslie Lamport, [How to Write a 21st Century Proof](https://lamport.azurewebsites.net/pubs/proof.pdf) (2012): hierarchically structured proofs, a method that makes it harder to prove things that are not true.
- Leslie Lamport, [How to Write a Proof](https://lamport.azurewebsites.net/pubs/lamport-how-to-write.pdf) (1993): the original case for structured proofs.

</div>

<details class="original">
<summary>Original input</summary>

<p class="note">Lightly edited for coherence: editing instructions removed, the author's own later corrections applied, and capitalization and punctuation fixed.</p>

<p class="note">Checklist:</p>

<div class="by-human">

- Search literature
- Build glossary
- Build phrasing
    - agent move from node $u$ to node $v$
- Structure
    - define terminology before using
    - Connectives
        - However, Thus,
    - Algorithms
        - High level idea
        - Detailed description
        - Variable details
        - Pseudocode
        - Correctness proof
    - Split the algorithm into small functions/procedures
    - motivation follows before result
        - follow the abstract writing guide
- Polish
    - remove ambiguity
        - no this or that type of reference (use precise reference to an object, e.g., node $u$ )
        - no two terms meaning the same thing
    - add reminders
        - e.g., recall that, ...; in the section &lt;no&gt;;
    - ensure that figures and references are correct
        - Figures appear in the same page or the next page they are referred to
        - write detailed captions for the figures
    - Simplify proofs wherever you can

</div>

<p class="note">Notes on using AI at each step:</p>

<div class="by-human">

1. Lit search can be verified by asking AI to take the references from dblp; then use DOI references to actually find the paper and verify the metadata for all your references.
2. Glossary can be built throughout several projects, but use a reference paper or a book for consistency.
3. Phrase bank is a meta thing. Read line by line to identify these. Give the models some reference phrases so that they know "moving from a to b" is a technical phrase.
4. Structural changes can be specified in the beginning.
5. Polish is finding something and asking AI to find all such things and disambiguate.

</div>

<p class="note">Later additions (2026-10-06) come from the instructions the author gave an AI agent while writing a paper, recorded by the agent in that paper's AGENTS.md. They were written by the agent, so they are not quoted here as the author's own words.</p>

<p class="note">On why this post exists, and its title:</p>

<div class="by-human">

This post is something I was talking about with my students. I just wrote it so that they can find it again and again and refer to it.

The co-author is the AI agent, or your oblivious supervisor.

</div>
</details>
]]></content:encoded>
    <source url="https://drdebmath.github.io/blog/posts/co-author-with-no-memory.md">The Oblivious Robot</source>
  </item>
  <item>
    <title>Why did I start blogging again?</title>
    <link>https://drdebmath.github.io/blog/p/why-did-i-start-blogging-again.html</link>
    <guid isPermaLink="false">https://drdebmath.github.io/blog/#why-did-i-start-blogging-again</guid>
    <pubDate>Sat, 26 Sep 2026 00:00:00 +0000</pubDate>
    <description>Why I started blogging again, what to expect here, and why this blog is called The Oblivious Robot.</description>
    <content:encoded><![CDATA[## Why blog again

<div class="mixed">

<span class="by-collab">I used to [blog](https://drdebmath.blogspot.com/), and then I stopped. What changed is that blogging is enjoyable again, because I am no longer doing everything from scratch. Tasks that would have taken weeks before, like designing custom CSS for each post, I can now ask AI to do in seconds.</span> <span class="by-ai">This post, for example, has its own Pokémon theme.</span>

<span class="by-collab">I always want to write about things, and, as usual, I have a high friction of starting. Some external motivators just tip this slightly in favor of getting it published.</span>

</div>

<div class="by-ai">

When the cost of the boring parts drops to nearly zero, a small nudge is enough.

</div>

## What to expect

<div class="by-collab">

Expect my thoughts about research, the coming AI singularity, predictions about the future, and how to adapt to the new age.

</div>

## Why now

<div class="by-collab">

This blog grew out of conversations with my students. The post on [writing research papers with AI](#co-author-with-no-memory) is something I was talking about with them. I wrote it down so that they can find it again whenever they need it and keep referring back to it, instead of relying on what they remember from our conversations.

</div>

<div class="by-ai">

Advice given in a meeting is gone by the next deadline. Advice written down can be linked, reread and improved. So this is where the things worth repeating will go.

</div>

## Why "The Oblivious Robot"

<div class="by-ai">

In distributed computing, robots are often studied in the *Look–Compute–Move* model. In each round a robot looks at its surroundings, computes where to go, and moves there. An **oblivious** robot is the most forgetful kind: it keeps no memory from one round to the next. Everything it does has to follow from what it can see right now.

</div>

<div class="by-collab">

This blog is the memory the robot obtains, before it forgets again.

</div>

<details class="original">
<summary>Original input</summary>

<p class="note">Lightly edited for coherence: editing instructions removed, the author's own later corrections applied, and capitalization and punctuation fixed.</p>

<div class="by-human">

The reason for writing is, I find it enjoyable enough to go back to blogging where I am not doing everything from scratch. I can ask AI to do most of the tasks that would have taken weeks before in seconds, such as designing custom CSS for each post. I always want to write about things, and as usual I have a high friction of starting. Some external motivators just tip this slightly in favor of getting it published.

The post on writing papers with AI is something I was talking about with my students. I just wrote it so that they can find it again and again and refer to it.

I like the name The Oblivious Robot. This blog is the memory that a robot obtains before it forgets again.

Readers can expect stuff related to my thoughts about research, coming AI singularity, predictions about the future, how to adapt to the new age.

</div>
</details>
]]></content:encoded>
    <source url="https://drdebmath.github.io/blog/posts/why-did-i-start-blogging-again.md">The Oblivious Robot</source>
  </item>
</channel>
</rss>
