A Day of Pair Programming Without APIs

The feedback loop: a human at the terminal, an AI at their side, and copy-paste holding them together

I spent an entire day building a small software system on my home server, paired with an AI — without APIs. No agent, no access to the server: just chat, a terminal, screenshots, and a lot of copy-pasting. The system works, verified end-to-end (62 completed test downloads).

This post isn't about the software: it's about the method. The thesis: structure makes error cheap. A verified mistake costs one turn; an unverified mistake costs a whole day.

The constraint: the AI touches nothing

The AI — in my case Lumo, Proton's chat model — has no access to the server. It doesn't run commands, it doesn't see the files. I am the hands, it is the copilot.

It sounds like a heavy disadvantage, and in part it is. But it has an interesting side effect: every proposed command must be self-contained and verifiable, because there is no silent retry. The lack of automation forces discipline. And over the day I went through every phase of the lifecycle — specification, mockup, deploy, debug, hardening, backup, documentation — ending up with a system that actually worked.

The feedback loop with copy and paste

The loop of the day, always the same:

AI proposes a command → I paste it into the terminal → I paste the output into the chat → the AI corrects itself

Three rules, learned by making mistakes.

Paste the output word for word, even when it fails. The stubbornest bug of the day — a download dying with a laconic exit code 1 — got unstuck only by pasting the complete logs: the cause was in the traceback, ten turns away. Ten turns spent guessing around an error that was already written down, black on white.

Commands that say whether they worked. For code changes, the AI wrote small scripts that refuse to touch a file unless they find the exact portion of text to replace: either the file is the expected one, or the script stops with a clear error. No modifications that fail silently when they fail. If a control command says nothing, the work isn't finished.

One step per turn. Never five commands at once, or you won't know which output corresponds to which action. Once I pasted text and commands together and bash replied syntax error near unexpected token — harmless, but instructive. The terminal is not a chat.

Screenshots: the two-way channel

When the problem is in the interface, the terminal hits its limit: you need screenshots. And the channel really is two-way — I would send UI screenshots and the AI noticed details I couldn't see: faded rows, disabled elements, a “12 issues” badge in the console.

But even screenshots aren't infallible. We suspected incomplete HTML because in the browser's inspector the DOM nodes appeared collapsed (the classic ▶ hiding the children). A cat of the file cleared everything up: it was all there. You need the right kind of evidence for the right kind of question.

Markdown as the lingua franca

All the documentation — specification, README, bootstrap scripts — is in Markdown because it worked in three roles simultaneously: readable document for the human, structured input for the AI, persistent artifact in the repository. One format, zero translations.

Hence the golden rule: when an exchange in chat produces a decision, that decision must be consolidated into a file. The chat is volatile, the file is memory. A fresh session with the right files beats an old semi-compacted session. Every time. And writing the specification before the code forces you to decide before you build.

The specification as a contract

For the specification we used OpenSpec: a propose → review → apply → verify → archive flow, requirements as WHEN/THEN scenarios, a tasks.md as a living checklist.

I have to tell one episode. The AI, asked about OpenSpec, at first made it up: a plausible but imaginary framework. I noticed, looked up the official documentation, and gave it the links. It re-read the sources and rewrote the entire documentation package according to the real framework. The moral isn't “AIs hallucinate” — it's operational: verify the sources even when talking to an AI, especially when it speaks with confidence.

The important part is the human checkpoints: the AI never moves on to the next phase without my explicit okay. Decisions to the human, execution to the AI. And the specification works as an anti-feature-creep frame: every scope question (“what about videos?”) got the answer “out of scope, that'll be another change”. That's how a small project stays small.

Bug fixing as a conversation

The lifecycle of every bug: symptom reported in full (complete logs, not interpretation), a hypothesis declared as such (“I suspect X, let's verify with Y” ≠ “it's X, solved”), diagnosis backed by evidence, surgical fix and retry. Of the AI's mistakes, one deserves a note: it had itself generated a bug in a module; re-reading its own code, it noticed, recreated it clean, and told me openly.

The episode that defines the day is another one, and I'm the protagonist.

Three patches in a row, three failures. The download stayed broken, and the AI kept proposing corrections. At some point I stopped: it's patching code that doesn't match the real one. Compaction — long chats get summarized, and the fine details get lost — had eaten its overview of the codebase. It was patching imaginary code.

I wrote something like: “maybe you should re-read the code, compaction may have made you lose context.” That was exactly it. Re-reading the real code, and the solution came.

This problem is a direct consequence of the absence of APIs: an agent with access would read the file on every change. Without APIs, the freshness of the context is a shared responsibility — the AI can't notice that its map has stopped matching the territory. Someone has to tell it. After three failed patches: stop and ask it to re-read the real code, not the fourth blind patch.

Compaction: survival strategies

Compaction is the normal behavior of long chats: old messages get summarized, fine details get lost. We went through more than three. I learned to recognize the signs of degradation: the AI starts guessing code patterns, proposes fixes for code that doesn't exist, repeats suggestions already tried.

On the left, the degraded, faded context; on the right, the same rebuilt sharp by the full dump

Four strategies worked.

  1. Document while you work. The system design, written in Markdown at the start of the session, survived three compactions. The details didn't, the structure did.
  2. The full dump. When remote patching stopped working, I created a single file with ALL the project's code — a bash script with a loop of docker exec ... cat, plus DB state and archive — and uploaded it into the chat. Cost: two minutes. Benefit: the AI stops guessing. The cheapest context reset there is.
  3. Verify the memory. Ask “what do you remember about X?” and compare with reality. If it diverges, give the file again — don't correct it in words.
  4. Start over from the file, not from the chat.

Everything else — the backup before the risky touch (cp file file.bak), the output pasted in full — is the same idea at the scale of a single turn: give the AI fresh data instead of letting it reason on stale data.

The human as the hands

The complete operating model: I provide the eyes, judgment, and terminal-pasting; the AI provides technical memory and generation. But I'm not a passive executor: I verified, rejected, corrected, decided scope and cuts.

And the bottleneck was never the AI's speed: it's the quality of the feedback I reported back to it. A complete log pasted right away is worth ten turns of descriptions.

The canvas: when the chat stops being the only surface

Something happened right in the middle of the session: suddenly, the AI started publishing long-form content in a side panel — a canvas. The feature had just been activated by the platform, and we found ourselves in it without noticing.

And it was a turning point. Before, every long document had to be printed in the chat and copied by hand, line by line: it worked, it was laborious, and it was the point where the method frayed. With the canvas, long content becomes a first-class object: you view it whole, you revise it, you export it.

Without the canvas, all long-form content lives in the chat and dies with compaction. With the canvas, it persists. It turns brainstorming into process: the difference between talking about an idea and having a version of it.

Why it works

The thesis: structure makes error cheap. The fully pasted output makes the error visible immediately; scripts with built-in checks make the wrong modification fail loudly; the human checkpoint stops the scope error while it's still cheap; the dump restores context while it still costs two minutes.

And there's the rubber-duck effect: explaining the problem to the AI — or pasting it the log, which is explaining with facts — is half the diagnostic work. More than once, while composing the bug description, I figured out where it was on my own.

With APIs, an agent with direct access would make much of this method unnecessary. You'd go faster and more comfortably. But there's one thing working without APIs leaves you with that automation doesn't give: the working dynamics with the AI become visible. You see where the context degrades, you see when it starts guessing, you see which information makes it fail and which gets it back on track. Understanding it is the prerequisite for using it well: I'm coming out of this day not only with a working system, but knowing exactly what it means to give it good information.

Because the warning stays valid even at maximum automation: APIs take away the fatigue of copy-pasting, not the responsibility for the quality of the information the AI works with. That one, better never fully delegate.


“Free technology isn't just code — it's the freedom to look under the hood.”

👤 Lorenzo | ItsMeLarry 🧪 AI & Open Source Experiments | 🕹️ Retro Gaming & Graphic Novels 🎸 Rock, Folk, Jazz, Punk | 🔒 Privacy First 📍 Tuscany / Digital

🔗 noblogo.org/itsmelarry-en | 🐙 GitHub