Technical
The Project Is the Prompt: How I Built an Application One Task at a Time
What happened when I stopped trying to tell an AI coding agent how to build the application and instead built a system that could tell it what to do.
TL;DR
- I built a feature-complete React/Python application using an AI coding agent whose recurring instruction was essentially: “Complete the latest task.”
- The coding agent wasn’t working from a giant prompt. I started with two documents: my Context System Design framework and the application’s design document.
- I used a separate LLM to turn my ideas into detailed implementation tasks. The coding agent executed those tasks, while another LLM independently reviewed the code.
- The repository eventually became the prompt: the task told the agent what to do next; the rest of the project told it how.
I used to think working effectively with AI meant writing better prompts.
Give the model enough context. Explain the architecture. Describe the constraints. Anticipate the edge cases. Tell it exactly what you want.
That approach works, but it becomes increasingly difficult as a project gets larger. The prompt gets longer. The instructions become more specific. Eventually, you’re spending so much time explaining the project to the AI that you’re still doing much of the work yourself.
My previous two essays explored some of this:
- In I Thought Context Was the Product. I Was Wrong., I argued that context is less about stuffing more information into a model and more about designing an environment in which the model can discover and maintain the information it needs.
- In I Thought I Needed Better AI. I Needed a Better Way to Work With It., I explored what happens when an AI agent can produce work faster than a human can reasonably supervise it.
I wanted to take those ideas one step further.
So I built something.
The Experiment
The application was a Mood-Based Spotify Playlist Generator.
It is a local React and Python application that lets a user select a structured emotional state, generates a prompt for an external AI chatbot to recommend songs, imports the chatbot’s response, resolves those songs against Spotify’s catalog, and creates a playlist.
It was large enough to contain a real application architecture, but small enough for me to understand the entire system.
Before giving the project to an AI coding agent, I did something more important than writing prompts: I designed the environment.
- I created a Context System Design document.
- I created a detailed system design document for the application.
Then I gave both documents to the coding agent. I didn’t tell it exactly what supporting files to create. Instead, I asked it to use those two documents to generate the project’s AGENTS.md, its README.md, and any additional files or structures it believed would help it do its job.
That produced an interesting result. The agent created the operating instructions, documentation, and additional project structure — including a context/ directory containing things like the domain taxonomy and data models.
I had given the agent a framework for designing context. The agent had started designing its own working context.
The System Emerged
From there, I created a tasks/ directory and began adding implementation tasks.
Interestingly, even that wasn’t entirely my idea. Another, more capable agent suggested the task-directory structure while I was working on the project. I adopted it because it made sense.
Once that structure existed, the development process became remarkably simple:
- I would decide what I wanted next.
- I would give that idea to a separate LLM and ask it to turn the idea into a detailed engineering task.
- That task could include the objective, relevant files, implementation considerations, constraints, acceptance criteria, testing requirements, security considerations, and anything else the LLM thought I might have overlooked.
- Then I gave the task to the coding agent.
Its instruction was essentially:
“Complete the latest task.”
That’s where the four-word prompt came from. It wasn’t magic. The four words worked because everything else the agent needed was already in the environment.
I Wasn’t Using One AI
This distinction took me some time to appreciate. I wasn’t simply giving instructions to an AI and watching it write software; I was using different AI systems for different jobs.
Intent → Task Design → Agent Execution → AI Review → Human Validation → Next Task
1. My Role
- Defining what I wanted to build.
- Establishing architectural boundaries.
- Making product and design decisions.
- Reviewing the implementation and manually testing the app.
- Deciding whether the result was acceptable and creating the next piece of work.
2. The Task-Design LLM
Its job was to take a relatively high-level idea and turn it into an actionable engineering task.
Writing detailed tasks is tedious. Instead of spending twenty minutes thinking through every file, edge case, and acceptance criterion, I could describe what I wanted and let the LLM expand the idea into something executable.
The template provides the floor; it shouldn’t become the ceiling.
3. The Coding Agent
The coding agent was responsible for implementation. It explored the repository, read the project’s context, followed architectural rules, implemented the task, wrote or updated tests, and maintained the codebase.
4. The Review LLM
The coding agent could generate a tremendous amount of output. Instead of manually inspecting every line, I used another LLM to analyze the changes:
- Did the implementation follow the architecture?
- Did it violate anything in the Context System Design?
- Were there security concerns or unhandled edge cases?
- Did it introduce unnecessary complexity?
The review wasn’t the final authority — it was an extra layer of analysis before my own validation.
The Rinse-and-Repeat Loop
Once the system was established, development became almost mechanical:
Design → Task → Execution → Review → Validation → Next Task
- If the implementation was good, I moved on.
- If I found a bug, I created a task.
- If the review LLM found a problem, I created a task.
- If I wanted a new feature, I created a task.
- If the change was substantial enough to affect architecture, I updated the design document first, then created the corresponding tasks.
And then I went back to: “Complete the latest task.”
The Project Became the Prompt
At some point I realized something strange: the coding agent didn’t really need me to explain the project anymore. The information was already there.

The prompt wasn’t really “Build this application.” It was closer to:
“Here is the environment. Here is what we currently know. Here is the next thing that needs to change. Figure it out.”
The task became the immediate instruction. The repository became the context. Together, they formed something much more powerful than a giant prompt.
Implementation Generates Information
Initially, I thought the task list could serve as a complete blueprint designed upfront. That isn’t how it worked, because implementation generates information.
You can design an OAuth flow, security model, or API on paper. That doesn’t mean you know exactly how the implementation will behave when real services and browsers interact.
When the Application Taught Us What the Next Task Was
The Spotify OAuth flow was a perfect example. The GUI needed browser-based Spotify authentication. The user clicked Connect Spotify, authorized the application, and returned to the local callback page.
Except nothing happened.
In the console, the server was waiting for terminal input:
Enter redirect URL or code:
The Spotify client had originally been designed around a CLI workflow, and the backend was falling back to the interactive terminal prompt.
There was no realistic way I could have written the original task with complete knowledge of this failure. The failure had to occur for us to understand it — and that understanding became the next task.
Execute ──> Observe ──> Understand ──> Decide ──> Create Task ──> Execute
The implementation is part of the discovery process.
From CLI to GUI, Then Security
The project demonstrated this progression repeatedly:
- CLI to GUI: The React interface was built on top of the existing Python domain logic rather than duplicating it. The repository carried the authoritative taxonomy and prompt generation forward without needing re-explanation.
- Security Hardening: A dedicated audit task guided the agent to identify credential exposures, validate OAuth state parameters, restrict token-cache permissions, enforce request-size limits, and add security regression tests.
This wasn’t no-code development; it was a different allocation of engineering work.
What Context System Design Actually Contributed
CSD wasn’t magical, but it established critical boundaries:
- The User was authoritative about their emotional state.
- The External AI was responsible for musical reasoning.
- The Application was responsible for deterministic state, schemas, and validation.
- Spotify was authoritative about its catalog.
Context doesn’t replace engineering. It gives the engineering process somewhere coherent to happen.
The New Division of Labor
By the end of the project, my role looked very different from writing software manually:
HUMAN (Upstream)
├── Define Intent
├── Establish Architectural Boundaries
├── Validate & Test
└── Make Product Decisions
AI SYSTEMS (Downstream)
├── Task Design LLM (Intent → Actionable Specs)
├── Coding Agent (Execution & Repository Maintenance)
└── Review LLM (Static Analysis & Risk Detection)
AI didn’t replace my engineering role. It changed where I applied it.
The Project Is the Prompt
Four words — “Complete the latest task” — were enough not because the task was simple, but because an entire system existed upstream of those four words.
Maybe the goal isn’t to become better at prompting AI to build software.
Maybe it’s to become better at building environments in which AI can figure out what to do next.
And if that’s true, then the project isn’t just where the code lives.
The project is the prompt.
