What it is
Claude Code, typed on an Apple IIe
You boot the Apple II from a floppy image. The agent finds a small proxy program on the local network by itself and shows a prompt. You type what you want:
From there, Claude (Opus 5.5 by default) writes the program in Applesoft BASIC or 6502 assembly language. It runs the program in an emulated Apple IIe set up exactly the way the real one will be, types keys at it, looks at screenshots, reads the screen, fixes what's wrong and tests again. All of this streams to the Apple II while it happens: Claude's notes, each tool step, the code as it's written, and hi-res screenshots as live previews. When the program works, the proxy sends it over Ethernet into the Apple II's memory, and the screen says:
RETURN runs it. ESC, Ctrl-Reset or a crash brings you back to AGENT> with the conversation intact. If the program crashed or hit a BASIC error, the next request is already filled in as FIX: ..., with what went wrong.
Why build it
The old machine is where you watch it happen
The working name in the first notes was "Claude Code for Apple II": an agentic coding loop like the one Claude Code runs in a terminal, except the terminal is the Apple II itself. You talk to the computer in plain English, and it gets new software, written for it, tested for it and loaded into it while you watch.
Five design decisions shape everything that follows:
- A proxy in the middle. The Apple II doesn't talk to Claude. A proxy program on a modern computer does, and the Apple II talks to the proxy over Ethernet.
- Test in an emulator first. Every program runs in an emulated Apple II before it goes anywhere near the real one, and Claude fixes what it finds there.
- Show the work on the Apple II. Every step streams to the Apple II's own screen, so the old machine is where you watch Claude work, not a window on a laptop.
- Straight into memory. The finished program goes over the network tokenized (or assembled) directly into the Apple II's RAM, ready to run, with no disk in between.
- An agent that stays resident. The agent stays in memory while your program runs, so ESC brings you right back to the conversation.
Behind it all is a plain question: can an AI model write real software for a 40-year-old 8-bit computer, with its odd memory map, its 280×192 hi-res screen and its 1 MHz 6502, and get it right without a person fixing the code? Getting there meant the model had to see the program run, so the project grew a test emulator, screenshots and a harness. It also meant the Apple II had to do more than display text. In the end it runs a TCP/IP client, a custom packet protocol, an 80-column screen driver, and a launcher that swaps 24K of memory between main and auxiliary RAM to make room for your program.
How it works
Three machines and a contract
deploy.py lays out memory for the test runs and for the real deploys.Booting and finding the proxy
The boot disk is a ProDOS 1.9 disk with AGENT.SYSTEM as its first system file, so ProDOS runs it straight away; there is no BASIC.SYSTEM and no BRUN. A small loader at $2000 copies the agent up to $6000, from the top down, so AGENT.SYSTEM (16,712 bytes) can be bigger than 16K. The agent brings up the Uthernet II in slot 3, gets an address by DHCP and broadcasts A2AGENT? to UDP port 6502. The proxy answers with its TCP port, and the reply's source address tells the agent where the proxy is. That address is saved to a 10-byte CLAUDECONF file on the boot disk, which also holds the stall watchdog's timeout. Then the agent connects and sends an INIT packet listing what this machine can do: 80 columns, auxiliary RAM, deploys.
The wire protocol
A2P is a small binary packet format over the TCP connection. Each packet starts with the bytes A2, then a type, a 2-byte length and a sequence number, and ends with an XOR checksum. ACK and NACK packets confirm a packet or ask for it again. That checksum is also how the agent knows a deploy arrived damaged.
| Type | Name | Carries |
|---|---|---|
$00 | INIT | Capabilities (80-column, aux RAM, deploy), model, display mode, protocol version |
$01 | CHAT | What you typed |
$02 | RESP | Text for the log; a flag marks the final message |
$04 | STATUS | The status line; DONE ends a response |
$05 | ACK | Acknowledgment |
$06 | NACK | Asks for a packet again |
$07 | LOAD | A 2-byte address and up to 126 bytes of program |
$08 | DEPLOY | Start of a program or a preview, to main or aux memory |
$09 | RUN | The launch descriptor: kind, start, end, LOMEM, HIMEM, mode, page range, name |
$0A | SHOW | Flip to the log or to a hi-res preview |
One request, start to finish
- You type a request. The input line holds 239 characters, and on a mixed-case screen your text goes as typed. The proxy echoes it at the top of the log, word-wrapped.
- Claude plans and writes. The agent loop calls Claude Opus 5.5 with streaming and adaptive thinking;
/MODEL Sor/MODEL Hswitches it to Sonnet 5.5 or Haiku 5.5, and/MODEL Oswitches back. Claude's tools arewrite_file,edit_file,read_file,run_in_emulator,emulator_input,emulator_memory,deploy_to_appleand web search. Summaries of its thinking stream to the Apple II as short-paragraphs, and the code scrolls by line by line as it's written. - It builds and launches a test.
deploy.pytokenizes Applesoft (with its own tokenizer) or assembles 6502 code with ca65 and ld65. The harness builds a launch state with the program's bytes, the same page-3 hooks the real agent installs, BASIC's pointers and guard patterns. It fills main and aux memory from$0800to$BEFFwith junk first, because a real Apple II's memory is never clean. Then it loads all of that into a fresh Bobbin running at a real 1 MHz. Nothing boots: each test starts from that snapshot with the program already in memory, so there's no boot to wait for, and Claude chooses how many seconds to let each run play before it looks. The emulator used to run about 40 times faster than a real Apple II, by accident. It's held to 1 MHz now because Claude judges a game's speed from these runs (more under What went wrong). - It plays the program. Keys arrive as the //e's own key codes, at a typist's pace. Each observation reports whether the program is running, ended at
], hit a BASIC error, crashed into BRK or exited to the agent. It includes the text screen, a color PNG screenshot that Claude looks at, and any memory-safety problems: writes into the agent's memory, its page-3 hooks or ProDOS's global page. Hi-res screenshots go to the Apple II as previews; any key flips back to the log. - It fixes and retests until the program does what was asked. For big programs, the prompt tells Claude to get a first playable version working, test it, and then grow it.
- It deploys.
DEPLOY, a run ofLOADpackets and aRUNdescriptor stream the program in. The closing message is shown again on a clean page right above the READY banner, so the program's controls are on screen when you press RETURN.
Making room: the launch modes
The agent itself lives at $6000 and up, right where a big program wants to be. On a 128K IIe the answer is to swap it out.
$6000–$BEFF between main and aux. Coming back swaps it again, and /RUN plays the program again from the untouched copy.On a 64K machine, RESIDENT mode keeps the agent in place and gives the program $0800–$5FFF (22,528 bytes). Applesoft programs are placed around whichever hi-res pages they use, and their variables get the biggest block that's left. Machine code above $6000 can change itself as it runs, and the swap brings that changed copy back, so /RUN of such a program asks the proxy for a fresh copy instead (/RERUN). The biggest program run so far is a 28,480-byte Applesoft test. The largest one Claude wrote for a request is The Haunted Castle, a text adventure of 13,611 bytes.
The way back
Every program has to be able to return. The agent keeps the top of page 3 ($03C7–$03FF) for itself; programs get $0300–$03C6. There's a crash mark, a BRK stub, a return entry at $03D0, a keyboard hook at $03D3 and the reset and BRK vectors. That gives six ways back to AGENT>:
- ESC in a program that follows the convention (
TEXT : HOME : &in BASIC,RTSorJMP $03D0in machine code) - ESC typed at the
]prompt or at a program'sINPUTline. The keyboard hook calls the ROM's KEYIN first and acts only when the key is ESC, soGETstill hands ESC to the program. &at the]prompt- Ctrl-Y at the
]prompt - Ctrl-Reset, through the reset vector
- A crash into BRK, which comes back as a pre-filled
FIX:request
In EXCHANGE mode, the return entry flips to auxiliary memory, where the agent is waiting, before it does anything else.
80 columns, without the firmware
The 80-column display interleaves its characters: even columns live in auxiliary memory, odd columns in main. The agent's own screen code writes, clears and scrolls across both banks and blinks its own cursor, without the 80-column firmware. Programs still start in plain 40-column text, and the agent comes back in 80.
Staying connected
The October 2026 hardware work added four layers, each from a real failure:
- TCP checksums. IP65 checked the IP header of incoming packets but never the TCP checksum. Bad segments are now dropped, and the Mac sends them again.
- The receive-ring check. Each frame in the W5100 network chip's receive ring starts with a 2-byte length, and the driver moves its read pointer by that length. One misread length and every frame after it is read from the wrong place. The driver now rejects any length that no frame can have, skips what's in the ring and picks up at the next frame.
- A stall watchdog. While Claude works, the proxy says something at least every two seconds. If 20 seconds pass in silence (counted in vertical-blank frames), the agent restarts the network, reconnects and sends
/RESYNC. The proxy then sends the final message and the program again. The/RESYNCcarries a stall report: segment counts, bad checksums, ring skips, the chip's socket status, waiting bytes and the expected and last sequence numbers. The proxy decodes it into a log, so a stall on the real machine explains itself. - Damaged deploys. If any packet of a program failed its check, the agent doesn't run that program. It asks for a fresh copy, at most twice.
The emulator underneath
The control socket speaks JSON over a Unix socket to up to 16 clients at once: memory, CPU, breakpoints and watchpoints, disks, graphics capture, 128K state snapshots, soft switches, keys and mouse. Its emulated Uthernet II now behaves like a real host on a real network. It sends 536-byte segments, respects the Apple's receive window, resends from the last ACK and drops duplicates. Switches can garble every nth TCP segment, or misreport every nth frame length in the receive ring, to reproduce the faults seen on the real machine.
Tests
One script runs every test that needs no API calls, one after another, in about 15 minutes: 62 unit tests; 35 screen checks in 40 and 80 columns, including the watchdog; 7 first-run setup checks; 30 end-to-end checks three times (normal, in 80 columns, and with every 5th segment garbled and every 7th frame length misreported); and a 25-cycle soak of deploy, run and return. Each test gets its own discovery port, so a test's emulated Apple II can't find a real proxy by mistake.
The Apple II client
Everything AGENT.SYSTEM does
The client is about 5,900 lines of 6502 assembly, plus the IP65 network stack, in one 16,712-byte file that ProDOS starts at boot.
Booting and setup
- Starts straight from ProDOS as
AGENT.SYSTEM, with no BASIC.SYSTEM in memory. - Brings up a Uthernet II in slot 3 and gets an address by DHCP.
- Finds the proxy by itself with a UDP broadcast, so no address needs typing on a home network.
- Saves the proxy's address and port, plus the stall watchdog's timeout, to
CLAUDECONFon the boot disk, and writes only when something changed. A write-protected disk is reported on the status line. - If the saved address stops answering, asks the network again before restarting it.
- Goes straight to
AGENT>when the proxy answers. When it doesn't, it says how to start the proxy, and the next thing typed tries again.
The screen
- Three display modes: 40 columns uppercase, 40 columns mixed case, and 80 columns that the agent draws itself, across main and auxiliary memory, without the 80-column firmware. 80 columns needs an 80-column card; without one it falls back to 40 mixed case.
- A scrolling log, a status line and an input line.
[MORE]paging stops the log when a page fills. - Your request is echoed at the top of the log, word-wrapped, and the input line clears while Claude works.
- Hi-res previews: Claude's test screenshots appear in hi-res mixed mode, with the status line still live. Any key flips back to the log.
- After a deploy, Claude's closing message is shown again on a clean page, right above the READY banner, so the program's controls are on screen.
Typing
- The input line holds 239 characters, scrolls sideways instead of wrapping, and beeps when it's full.
- On a mixed-case screen, requests go as typed; in 40-column uppercase they go in capitals.
- ESC at
AGENT>clears the line. - Commands are matched as whole words. Anything the client doesn't handle itself goes to the proxy.
Working with Claude
- Everything Claude does streams to the log as it happens: its notes, each tool step, the code as it's written and the emulator's text screen.
- The first ESC while Claude works asks it to stop. The proxy stops at the next safe point, so the conversation stays valid. A second ESC stops waiting, for a network that has quietly gone away.
- The conversation carries over between requests, and survives reconnecting.
Running programs
- Takes Applesoft BASIC and 6502 machine code. Programs are streamed into memory and shown as
*** NAME IS READY! ***. RETURN runs the program; typing anything else keeps chatting. - On a 128K IIe a program gets
$0800–$BEFF(46,848 bytes), with the agent swapped out to auxiliary memory. On a 64K machine it gets$0800–$5FFF. - Applesoft programs are placed around the hi-res pages they use, with their variables in the biggest block left.
/RUNplays the program again from an untouched copy. Machine code that may have changed itself gets a fresh copy from the proxy.- A program with a packet that failed its checksum isn't run; the client fetches a fresh copy, at most twice.
Getting back
- Six ways back to
AGENT>: ESC in the program, ESC at the]prompt or anINPUTline,&, Ctrl-Y, Ctrl-Reset and a crash into BRK.GETstill hands ESC to the program. - The session is intact on return, and the screen comes back in its own display mode.
- A crash or an Applesoft error comes back as a pre-filled
FIX:request describing what went wrong.
Staying connected
- TCP checksums on every incoming segment, and a check on every frame length read from the network chip's receive ring.
- A stall watchdog: after 20 seconds of silence while waiting (set in
CLAUDECONF, 0 turns it off), the client restarts the network, reconnects, sends a stall report and gets the end of the reply and the program again. - Detects a dropped connection and reconnects, re-initializing the network if reconnecting fails.
- Notices a Uthernet II that Ctrl-Reset has reset, and restarts the network before the next send.
Commands
| Command | What it does | Handled by |
|---|---|---|
/RUN | Run the last program again | Apple II |
/EXIT | Drop to a plain ] prompt (also /SUSPEND, /QUIT); ESC, & or Ctrl-Y come back | Apple II |
/SETIP address | Set the proxy's address; checked before it's used | Apple II |
/PORT n | Set the proxy's port | Apple II |
/IP | Show the proxy's address and port | Apple II |
/SAVE | Save the settings to the boot disk | Apple II |
/LIST | List the BASIC program in memory | Apple II |
/CLEAR | Clear the screen | Apple II |
/MORE | Turn [MORE] paging on or off | Apple II |
/DISPLAY 40|40L|80 | Switch display mode | Both |
/MODEL O|S|H | Use Opus, Sonnet or Haiku 5.5 | Both |
/RESET | Start a new session; the proxy forgets the conversation | Both |
/CODE request | A quick BASIC program from a single request, without the agent loop | Both |
/HELP | List the commands | Proxy |
/UNDO | Bring back the previous program | Proxy |
/VERSIONS | List the programs sent this session | Proxy |
/SEND file | Send a program from the proxy's workspace as it is, without Claude | Proxy |
/EFFORT level | How hard Claude thinks: LOW, MEDIUM, HIGH, XHIGH or MAX | Proxy |
/LIMIT n | The most tool steps Claude may take per request (1–200) | Proxy |
/PROMPT text | Standing orders added to every request; empty clears them | Proxy |
/AGENT | Turn the agent loop on or off | Proxy |
/CHAT message | Talk to Claude without tools | Proxy |
/HISTORY | Remember the conversation, or treat each message fresh | Proxy |
The client also sends three commands of its own: /STOP when you press ESC while waiting, /RERUN when /RUN needs a fresh copy, and /RESYNC, with its stall report, when the watchdog reconnects.
The history
Ten months, two bursts
Apple II Agent started when Benj thought it would be fun to write a version of Claude Code for the Apple IIe. He happened to have a Uthernet II Ethernet card, so he pulled it out of his Apple IIgs, put it in the IIe, plugged in a BMOW Floppy Emu and got to work.
The open question was how it should work. A ChatGPT-style chat on a green screen wouldn't have been satisfying. It had to be interactive and agentic: you ask, Claude builds and tests, you try what it made, and the two of you refine it back and forth until it's right.
The work came in two bursts. The first ran about five weeks, from early January to February 10, 2026. By the end of it, a basic version was working on the real Apple IIe hardware, and the code went up on GitHub. The second, a week in late October 2026, rebuilt the agent around ready-to-run programs and made it hold up on the real machine. Dates come from the commit history, the changelog and the project's notes.
-
January 3–11, 2026
The emulator learns to show its screen
It starts with Bobbin, Micah Cowan's terminal-based Apple II emulator. A fork adds screen export for hi-res, lo-res and the double-resolution modes, as ASCII art and as images, then "AI agent support", so that a model can read what an Apple II is showing.
-
January 22–25, 2026
apple2agent becomes its own project
The proxy, an MCP server for Claude Code, the 6502 client, the IP65 network library and the disk tools are pulled out of a larger umbrella project. The first commit, on January 25, 2026, is a remote shell: the Apple II can send and receive text, nothing more. That night an "ampersand hook" version appears, where typing
&at BASIC's]prompt calls the agent. -
January 26, 2026
The day it became an agent
Ten commits in about an hour: the A2P packet protocol, model selection,
/PROMPT, agentic mode with tool use, conversation compaction and the first "Claude Code experience" with BASIC and assembly support. The changelog calls this 1.0. -
January 27, 2026
Memory wars
The agent's receive and screen-save buffers turned out to sit on top of IP65's Ethernet buffers, so sending a packet corrupted them. Display bugs fell the same day: a clobbered Y register, an uninitialized row counter, a cursor column that never reset.
-
January 28, 2026
The control socket
Driving Bobbin through a pseudo-terminal and Unix signals was fragile: race conditions, lost output, a debugger that sometimes didn't come back. The fix was a Unix socket built into Bobbin's main loop, taking JSON commands to peek, poke, read the screen, type keys, step the CPU, set breakpoints, swap disks and capture graphics. Everything after this depends on it.
-
January 29–31, 2026
An emulator of its own, and a usable screen
The proxy got its own test emulator (the "emulator bridge"), with 128K state snapshots and instant BASIC, ProDOS and DOS 3.3 environments. On January 31, 2026, the project was renamed from "Claude Code" to Apple II Agent. The client got a fixed status line and input line, a persistent TCP connection,
[MORE]paging,/EXITto BASIC, and web search for Claude. -
February 1–8, 2026
Features, fast
A STARTUP file that boots straight into the agent; sessions that survive a reconnect; a W5100 buffer-size mismatch behind dead connections; saved versions with
/UNDO; Dungeon of Doom, a text RPG used as a test case; 40-column, 40-column lowercase and 80-column modes; prompt caching. The most unusual feature: anything typed at the]prompt that BASIC couldn't parse went to Claude. That worked by copying the Applesoft ROM into Language Card RAM and patching its error handler. -
February 9–10, 2026
Ready for others
Tools that keep large data out of the conversation to save tokens, 16-color double hi-res capture, a 6502/Apple II reference library, and screenshots scaled 3× so a vision model can read them. By then a basic version was working on the real Apple IIe hardware, and the repository went public on GitHub on February 10, 2026.
-
February – June 2026
On GitHub, unannounced
The code was public, but it hadn't been announced anywhere.
-
June 18, 2026
A low-key announcement
Since a planned article about the project never came to pass, Benj decided to announce it in a low-key way on Bluesky, with a link to the repository:
Way back in another lifetime (February) I was working on a “Claude Code” for the Apple II. Through an Ethernet card for the machine and this client, AI can write AppleSoft and assembly software for you interactively
Kinda fell through the cracks, but it works!
-
June – October 2026
Quiet again
The project sat until October 2026.
-
October 20, 2026
2.0: "Make it magical"
Benj revived the project in October 2026 and set the goal for 2.0: make it magical. The core was rebuilt. Any program, BASIC or machine code, is streamed into the Apple II and shown as READY to run. A launch mode swaps memory so a program gets almost all of a 128K IIe. There are six ways back to the agent. A new agent loop runs on Opus 5.5 with adaptive thinking. A test harness launches programs exactly as the real machine will, and Claude's hi-res screenshots show up as previews on the Apple II. AGENT.SYSTEM boots straight from ProDOS.
-
October 22, 2026
2.1: getting ready for real hardware
Automatic discovery of the proxy, saved settings, ESC to stop Claude, a diagnostic disk, SD card tools and a full test suite. Bobbin got a much more realistic emulated network, which turned up a long list of real bugs. The agent also learned to draw 80 columns itself, because 80 columns had never actually worked.
-
October 23, 2026
Version 2 boots on the real IIe
On Benj's IIe, every new boot disk crashed at
2002-until the cause was found: the ProDOS 2.4 kernel. Rebuilt on the January 2026 ProDOS 1.9 disk that the earlier version had booted from, the new agent booted, found the proxy and talked to Claude. -
October 26, 2026
TriPeaks on the real IIe
The first program the new version sent to the real machine stalled, then ran with
?SYNTAX ERROR IN 100. The network stack had never checked TCP checksums, and a garbled byte got through. With checksums checked, two more stalls during the next build were caught and recovered by a new watchdog, which led to a check on the network chip's receive ring. Benj then played TriPeaks on the IIe, and everything went to GitHub as 2.1 that night.
Versions
From a chat window to a launcher
| Version | Date | What it added |
|---|---|---|
1.0 | Jan 26, 2026 | The first client: A2P protocol over TCP, a full-screen chat UI, slash commands, saved IP settings, BASIC injected into memory at $0801, the & hook back from BASIC. |
1.x | Jan 27 – Feb 10, 2026 | Never tagged. The agentic loop with tools, Bobbin's control socket, the emulator bridge, snapshots, sessions that survive reconnects, natural language at the ] prompt, /UNDO, display modes, prompt caching, the reference library. |
2.0 | Oct 20, 2026 | "Make it magical": READY-to-run deploys of BASIC and machine code, the memory-swapping launch mode, six ways back to the agent, a new agent loop on Opus 5.5, a test harness that matches the real machine, live hi-res previews, AGENT.SYSTEM, port 6502. |
2.1 | Oct 22–26, 2026 | Real hardware: proxy discovery, saved settings, ESC to stop, a ProDOS 1.9 boot disk, SD card tools, 80 columns drawn by the agent, TCP checksums, a receive-ring check, a stall watchdog with reports, re-fetching a damaged deploy, and the test suite. |
Some features didn't survive. The February 2026 trick of sending unparseable BASIC lines to Claude needed a patched copy of the Applesoft ROM in Language Card RAM, and in 2.1 that copy turned out to wipe out ProDOS, which lives in the same memory. Leaving the agent now drops you at a plain ] prompt, and ESC, & or Ctrl-Y bring you back. A web interface from January 26, 2026, was dropped once the Apple II client could do everything itself.
On GitHub
Two repositories
apple2agent holds the Apple II client, the proxy, the MCP server, the disk tools, the tests and the programs Claude has made. It was created on February 10, 2026, and the first push that day carried 107 commits, January 25 to February 10, 2026. It was first mentioned in public on June 18, 2026, in a low-key post on Bluesky. The October 26, 2026, push brought it to 119 commits. Those were split by area: the 2.1 client, the proxy, the boot disk, the tests, the MCP server, the docs and the programs. Then came the network driver's ring check, the stall reports and the TriPeaks source. The license is MIT.
bobbin-ai is a fork of Micah Cowan's Bobbin (MIT). Eighteen commits sit on top of the upstream code, from January 3 to October 26, 2026: screen export, the control socket, multiple clients, a headless mode, pause and resume, 16-color double hi-res capture, breakpoints, vertical-blank timing and a realistic emulated Uthernet II network, with switches that corrupt it on purpose for testing.
The IP65 network library is a git submodule. This project's fixes to it (TCP checksums, the receive-ring check, resend and ACK fixes) are kept as a patch file in the apple2agent repository, so the upstream code stays as it is.
What went wrong
The emulator said yes. The Apple II said 2002-
Most of the bugs that mattered shared one pattern: something passed in the emulator and failed on the real machine, or passed only because the emulator was kinder than reality.
Every new disk crashed the real IIe
The first boots of the new disks on the IIe dropped into the monitor at 2002-: ProDOS started, then jumped into what it thought was AGENT.SYSTEM and found nothing there. Bobbin booted the same disks without complaint. The cause turned out to be the ProDOS 2.4 kernel (its first release). Two test disks went onto the card together, copied the same careful way: the agent on the January ProDOS 1.9 disk booted, and the same agent on ProDOS 2.4 crashed. So did the unmodified stock 2.4 disk, while the machine's other ProDOS 1.9 and 2.0.3 disks boot. Along the way, the disk tool that built images from scratch turned out to be corrupting the last 232 bytes of the ProDOS kernel.
Fix · The boot disk is now a copy of a ProDOS 1.9 disk that had already booted that machine, with the agent added by AppleCommander. A build check confirms the agent is the first system file and reads back byte for byte. ProDOS structures are no longer written by hand.
The SD card served the wrong data
A Floppy Emu reads each disk image as one unbroken run of clusters on its FAT card, and a plain file copy on macOS can split an image into pieces. The adapter refused one such image with "file not contiguous". Worse, an image that reads back as zeros partway through produces the very same 2002- crash in the emulator, so from the Apple II's side a bad copy and a bad kernel look alike. That's why the ProDOS 2.4 test above used images copied the careful way.
Fix · A copy tool that allocates the whole image in one piece and verifies it, a checker that walks the card's FAT chain, and a diagnostic disk that checks every block against build-time checksums.
The first deploy to the IIe lost a packet
TriPeaks stalled halfway through its download, then ran with ?SYNTAX ERROR IN 100. Bytes were being garbled somewhere between the network card and the CPU, and IP65 never checked TCP checksums. A garbled byte in the data broke a packet, which was dropped, so part of the program never arrived. A garbled byte in a TCP header moved the acknowledgment point past what the Mac had sent, a stall neither side recovers from, with 12K stuck in the Mac's send queue.
Fix · TCP checksums in IP65, plus a Bobbin switch that garbles segments on purpose. The old agent froze on its first deploy with that switch on; the new one passes all 30 end-to-end checks with every 5th segment garbled.
One misread length froze the connection
With checksums on, the connection still stalled twice while TriPeaks was rebuilt. The watchdog recovered it both times. The cause was the receive ring: one bad frame length, and every frame after it is misread until the chip is reset.
Fix · The ring check, a Bobbin switch that misreports frame lengths, and stall reports that arrive with every /RESYNC.
Neither fix explains why the bytes go bad in the first place. Ethernet frames carry their own CRC, so a frame damaged on the wire would normally be thrown away before the Apple II ever saw it. That points to something on this machine: the Uthernet II card, its slot, or the bus timing between the W5100 and the 6502. That fault hasn't been found yet. The checksums and the ring check let the agent survive it; they don't cure it.
The emulated network was too polite
The first virtual network sent small segments. With a realistic host sending 536-byte segments, it turned out the agent acknowledged only the low byte of any segment of 256 bytes or more. In the emulator, which runs unthrottled, a 28K deploy took 48 seconds instead of about one. With the fix, and the emulator held to a real 1 MHz, the same 28K now loads in about 6 seconds, roughly 4.7K a second. It hasn't been timed on the real IIe yet.
Fix · The ACK bug, and an emulated host that behaves like a real one. The project's rule since then: if something passes only with small segments, it's broken.
Claude was testing against a fast-forward
Bobbin's simple mode runs unthrottled, about 40 times real speed, so Claude was tuning game speed against a fast-forward. It also sent RETURN for the down arrow, which had Claude writing games that accepted both. And test memory started out zeroed, which hides a whole class of bugs.
Fix · Test runs at a real 1 MHz, keys as the //e's own codes at a typist's pace, and memory filled with junk before every launch.
A big request produced nothing
Asked for a large game, Claude spent its first turn planning the whole thing and ran out of output (64K tokens) before writing a line. After 12 minutes the loop gave up with MY REPLY GOT CUT OFF. Separately, long thoughts showed only THINKING 7M for minutes, because that thinking-display setting returns no text for the up-front reasoning.
Fix · 128K of output, a retry with a note to plan briefly and build in pieces, and summarized thinking cut to three lines a paragraph.
80 columns had never worked
The 80-column mode from February 2026 sent text through the 40-column ROM routine, so every other column showed leftover auxiliary memory.
Fix · The agent's own 80-column screen code, described above.
Smaller ones
- Once the everyday proxy answered discovery, a test's emulated Apple II connected to it instead of the test's own proxy. Each test now has its own discovery port.
- Bobbin's simple mode set its standard input to non-blocking, and a copy that inherited the MCP server's input crashed the MCP server. Child processes now get their own input.
- The keyboard hook ran
TSXto see who was asking, which wiped out GETLN's place in the line. After/EXIT, every line at the]prompt was a?SYNTAX ERROR. /PORTalways set port 0, because two variable names pointed at the same byte.- Requests were silently cut off at 120 characters.
What went right
The decisions that held up
- The architecture. The plan from January 2026 has held through every version: a proxy in the middle, emulator testing first, progress on the Apple II's own screen, programs sent straight into memory and an agent that stays resident. Versions 2.0 and 2.1 rebuilt most of the parts and kept the plan.
- The control socket. Replacing the pseudo-terminal with a socket in Bobbin's main loop on January 28, 2026, made every tool after it reliable. It also let several programs, or several AI agents, work with one emulator at once.
- One source of truth for memory.
deploy.pylays out every program for both the test harness and the real deploy, and a test checks that the harness installs the same page-3 hooks as the agent. A program that passes in the harness launches the same way on the Apple II. - Letting the model see. Screenshots, the mirrored text screen and the memory-safety report turned "write code and hope" into "write, run, look, fix". Scaling screenshots 3× made hi-res detail legible to the model.
- Streaming everything to the Apple II. Watching notes, code and previews scroll by on the green screen is most of what makes the project feel like more than a file transfer.
- Making the emulator less kind. Real-speed timing, junk memory, a real-host network and deliberate faults found bugs before the hardware did, and the stall reports make the remaining failures explain themselves.
- Small, tested steps on real hardware. A diagnostic disk, a copy tool and a FAT checker took the guesswork out of "is it the disk, the card or the code?"
Known rough edges
What hasn't been done or tested
- It's only been tested on one real machine: an enhanced Apple IIe with 128K, a Uthernet II in slot 3 and a Floppy Emu. It hasn't been tried on a II+, IIc or IIgs, or with other network cards. The 64K RESIDENT mode has only run in the emulator.
- The hardware fault behind the garbled bytes hasn't been found: most likely the network card, its slot or the bus timing. The agent recovers from it but doesn't fix it.
- 80 columns work in the emulator but haven't been confirmed on the real machine.
- ProDOS 2.4 disks (the first 2.4 release) crashed the tested IIe at boot, so the boot disk uses ProDOS 1.9. The test disks were copied with the contiguous-copy tool and byte-verified, but their cluster chains on the card weren't checked (that needs root), and later 2.4.x releases haven't been tried.
- Bobbin's color hi-res capture swaps two pairs of colors and draws white as stripes. Claude sees those screenshots, so it can misjudge colors.
- One intermittent test failure: the end-to-end check that the agent reconnects after a proxy restart, with corrupted network traffic, failed once in four runs on October 26, 2026.
- It needs an Anthropic API key, and every request uses API tokens; a large game uses a lot of them.
Problems can be reported on the apple2agent issue tracker.
By the numbers
As of October 26, 2026
Programs Claude built and deployed in October 2026, by request: a times table; Breakout in hi-res and Snake in lo-res with sound, both in machine code; an Applesoft hi-res sketch; Galaxia, a hi-res space shooter in machine code; Lander; The Haunted Castle, a text adventure; and TriPeaks Solitaire, the first of these played on the real IIe. Every deployed version is kept in the repository. In February 2026, Dungeon of Doom, a text RPG, served as an early test case.
Links and credits
Source code
- Bobbin, the Apple II emulator underneath, is by Micah Cowan (MIT license).
- IP65 (cc65/ip65) provides the TCP/IP stack; this project's fixes are kept as a patch.
- cc65's ca65 and ld65 assemble and link the client and Claude's machine code.
- AppleCommander builds the boot disk.
- Benj Edwards (BX Foundry) architected Apple II Agent, directed its development and tested it on real hardware.
- Claude Code wrote most of the code: Claude Opus 4.5 and 4.6 in January and February 2026, Opus 5.5 in October 2026. The agent loop itself runs on Claude Opus 5.5 by default, or on Sonnet 5.5 or Haiku 5.5.
- Photos: Benj Edwards.