Skip to content
All posts
5 min read

Markdown Tickets in the Repo: Issue Tracking for AI-Agent Projects

Amit Raz

Amit Raz

Founder, RZ AI Labs

The tickets in my project are markdown files in the repo. There is no Jira and there are no GitHub Issues: there is a docs/tickets/ folder with files in it, and the filename is the ID. I have tried all the other options, and this is what works best for me.

In the previous two posts I covered decisions.md and progress.md. This is the third markdown flow in the same system, and the one with a field in it I have not seen anyone else use.

Illustration of a Claude robot presenting a markdown ticket on screen with Status, Priority, Type and Paid header fields, next to a docs/tickets/ file box
One file per ticket, the filename is the ID, and the header answers who can fix it.

A ticket is not a TODO

This distinction matters. A ticket gets opened when I am in the middle of something, discover an adjacent defect, and realize that fixing it right now would smuggle a behavior change into a commit that is about something else entirely, a commit nobody will review for that change.

So I cut the scope and document the defect out of the current task. Every ticket answers four things:

  • What happened.
  • Why I did not fix it here.
  • What counts as a successful fix.
  • Which solutions were considered and rejected, and why.

The last part is the most important one. I have a ticket that lays out four possible ways to fix an issue and deliberately picks none of them, because the choice needs a human decision. Whoever opens that ticket next does not re-run the analysis from zero; they start from four evaluated options and a reason the decision was deferred.

What does a ticket look like?

Here is the shape, adapted from a real one:

# `src/api/templates.py` constructs Settings at import time

Status: OPEN · Priority: LOW (design smell; no live defect) ·
Type: design / test-infra · Paid: none (offline change).
Raised 2026-07-13 during the CI configuration fix.

## What happened
Importing the templates module runs full config validation,
because a Settings object is built at module scope. A missing
secret turns into a pytest collection crash.

## Why it was not fixed in that commit
That commit was CI wiring. This fix changes import-time behavior
in a different part of the system.

## What counts as fixed
Importing any module in the API package succeeds with no .env
present. Config validation runs only from an application entry
point. All existing tests pass.

## Options considered (and rejected)
1. Lazy property on the module ... rejected: hides the dependency.
2. ...

Everything above the first ## is the header, and the header is designed to be scannable without opening the body, by me or by an agent triaging the folder.

The Paid field

So far, nothing here is new. But there is one field in every ticket header that I added over time and have not seen anyone else do:

Status: OPEN · Priority: HIGH · Type: correctness/cost ·
Paid: none (offline write-up)

Paid.

My pipeline calls paid services: scraping actors, LLM APIs, paid search. The iron rule in the project, the same one that appears as a decisions.md entry, is that no agent ever makes a paid call without specific human approval.

Which means "how much will this fix cost to verify" is not a technical question. It is a scheduling question, and it decides two things: what I pick up next, and what I can release to an agent to fix on its own.

  • Paid: none means the fix is fully offline. Hand it to an agent and move on.
  • Paid: yes means I need to be in the loop, because verifying the fix spends money.

Paid: none = toss it to an agent. Paid: yes = I am in the loop. That is why it lives in the header, not the body.

If you run agents against anything that costs money per call, some version of this field will pay for itself quickly. It is the difference between an agent backlog you can delegate with confidence and one you have to re-read every time. This applies to any agent system that touches paid APIs.

FAQ

Why not GitHub Issues or Jira?

Because in an agent-heavy project the agent is a first-class reader and writer of tickets. A file in the repo needs no API tokens, no permissions, no network: the agent greps it, opens it, and edits it with the same tools it uses on code. Tickets travel with every checkout, work offline, and changes to them show up in diffs and PRs like any other change.

Does this work in a team, or only solo?

I run it solo with agents, and that is where I can vouch for it. In a team you lose the dashboards and assignment workflows a tracker gives you, but you gain tickets that are reviewable in PRs and readable by every agent with a checkout. Honestly, I am still curious how it holds up with more humans involved; if you run something like this in a team, I would genuinely like to hear about it.

When is a ticket closed?

When its "what counts as a successful fix" section is satisfied and the evidence exists (a passing test, a green CI run). The ticket file itself gets a closing note rather than being deleted, for the same reason decisions.md is append-only: the analysis is worth more than the tidy folder.

This is part three of a series on running an AI-agent project from plain markdown files, after decisions.md and progress.md. The next post covers the part I like most: a CI dead man's switch that makes tech debt impossible to forget. The original version was a tweet in Hebrew.

Building something with AI?

I help teams ship custom agents, AI strategy, and software that works.