Markdown as source code: a new workflow for AI coding agents

The typical AI coding agent workflow — “write app A that does X,” then iterate with “add feature Y” or “fix bug Z” — breaks down once the agent forgets your app's purpose or previous decisions. You end up repeating yourself, or worse, the agent contradicts its own earlier output.

Custom instructions files like copilot-instructions.md help, but updating them alongside every chat prompt feels redundant. So what if you just wrote the whole app specification in Markdown and let the agent compile it into code?

That's what I tried with my latest project, the GitHub Brain MCP Server, written in Go. I edit the Markdown spec almost exclusively; I rarely touch the generated Go code directly. This approach should work with any AI coding agent and language, but I'll use VS Code, GitHub Copilot, and Go as examples.

The four key files

.
├── .github/
│   └── prompts/
│       └── compile.prompt.md
├── main.go
├── main.md
└── README.md

The setup is simple: edit README.md or main.md, invoke compile.prompt.md to generate main.go, then build and run the Go code as usual. Break down:

README.md: user documentation doubles as spec input

Nothing special here — just regular documentation. The key is that this file gets embedded in the specification, so any update to the README is automatically reflected in the spec. If I add an alias for the -o argument in the docs, no extra steps are needed.

# GitHub Brain MCP Server

**GitHub Brain** is an experimental MCP server for summarizing GitHub discussions, issues, and pull requests.

## Usage

```sh
go run main.go <command> [<args>]
```

**Workflow:**

1. Populate the local database with the `pull` command.
2. Start the MCP server with the `mcp` command.

### `pull`

Populate the local database with GitHub data.

Example:

```sh
go run main.go pull -o my-org
```

Arguments:

- `-t`: Your GitHub personal access token. **Required.**
- `-o`: The GitHub organization to pull data from. **Required.**
- `-db`: Path to the SQLite database directory. Default: `db` folder in the current directory.

### `mcp`

Start the MCP server using the local database.

...README.md continues...

main.md: the actual source code

This is where the app really lives — written in Markdown and plain English. The spec can include conditional logic like if, loops like foreach, and continue statements, but described declaratively rather than imperatively. Imports are represented as Markdown links, and even the database schema is defined inline:

# GitHub Brain MCP Server

AI coding agent specification. User-facing documentation in [README.md](README.md).

## CLI

Implement CLI from [Usage](README.md#usage) section. Follow exact argument/variable names. Support only `pull` and `mcp` commands.

## pull

- Resolve CLI arguments and environment variables into `Config` struct:
  - `Organization`: Organization name (required)
  - `GithubToken`: GitHub API token (required)
  - `DBDir`: SQLite database path (default: `./db`)
- Use `Config` struct consistently, avoid multiple environment variable reads
- Pull items: Repositories, Discussions, Issues, Pull Requests, Teams
- Use `log/slog` custom logger for last 5 log messages with timestamps in console output

...main.md continues...
### Discussions

- Query discussions for each repository with `has_discussions_enabled: true`
- Record most recent repository discussion `updated_at` timestamp from database before pulling first page

```graphql
{
  repository(owner: "<organization>", name: "<repository>") {
    discussions(first: 100, orderBy: { field: UPDATED_AT, direction: DESC }) {
      nodes {
        url
        title
        body
        createdAt
        updatedAt
        author {
          login
        }
      }
    }
  }
}
```

- If repository doesn't exist, remove the repository, and all associated items from the database and continue
- Query discussions ordered by most recent `updatedAt`
- Stop pulling when hitting discussions with `updatedAt` older than recorded timestamp
- Save or update by primary key `url`
- Preserve the discussion markdown body

...main.md continues...
## Database

SQLite database in `{Config.DbDir}/{Config.Organization}.db` (create folder if needed). Avoid transactions. Save each GraphQL item immediately.

### Tables

#### table:repositories

- Primary key: `name`
- Index: `updated_at`

- `name`: Repository name (e.g., `repo`), without organization prefix
- `has_discussions_enabled`: Boolean indicating if the repository has discussions feature enabled
- `has_issues_enabled`: Boolean indicating if the repository has issues feature enabled
- `updated_at`: Last update timestamp

...main.md continues...

compile.prompt.md: the compiler prompt

This is just a prompt file in GitHub Copilot's format — kept simple on purpose, so it stays portable across other AI coding agents. All the real information is in main.md:

---
mode: agent
---

- Update the app to follow [the specification](../../main.md)
- Build the code with the VS Code tasks. Avoid asking me to run `go build` or `go test` commands manually.
- Fetch the GitHub home page for each used library to get a documentation and examples.

The development loop

  1. Edit the spec in main.md or README.md.
  2. Invoke the compile prompt to turn it into Go code.
  3. Run and test. If something's off, update the spec.
  4. Repeat.

In Copilot for VS Code, I use the / command to run the prompt:

Screenshot showing the use of the / command in GitHub Copilot for VS Code to invoke the AI coding agent prompt.

For smaller specs, Copilot often picks up changes automatically. As the spec grows, I add a hint like “focus on <the-change>” to keep it on track:

Screenshot demonstrating how to prompt GitHub Copilot in VS Code to focus on a specific change using the / command.

Writing the spec is the hard part

Describing what you want clearly in Markdown is sometimes harder than writing Go directly — but you can use Copilot for that, too. Here, it adds pagination to all MCP tools in the spec, picking proper parameter names itself:

Screenshot showing GitHub Copilot in VS Code recommending pagination style and parameter names for MCP tools in the Markdown specification.

Linting the spec

Like any source, main.md can get messy. A dedicated lint prompt cleans it up:

---
mode: agent
---

- Optimize [the app specification](../../main.md) for clarity and conciseness
- Treat the english language as a programming language
- Minimize the number of synonyms - i.e. pull/get/fetch. Stick to one term.
- Remove duplicate content
- Preserve all important details
- Do not modify the Go code with this. Only optimize the Markdown file.
- Do not modify this prompt itself.

If the result looks good, I compile it with compile.prompt.md.

Screenshot of GitHub Copilot in VS Code cleaning up and linting the Markdown specification for improved clarity and conciseness.

Observations and limitations

  • It works — and each agentic update to Copilot makes it better.
  • Compilation slows down as main.go grows. The next step is to ask the agent to break each ## section into its own module.
  • Testing is untouched. No test generation yet in this workflow — but tests remain essential. The spec describes intent; tests verify it.

I'm also curious whether throwing away all the generated Go and regenerating from the same Markdown spec in another language would work immediately. Given how fast this field is moving, I expect we'll have answers soon enough.