For a long time, we got away with writing terrible GitHub issues. We leaned on implicit, shared knowledge. An issue titled "fix the auth bug in the checkout flow" was enough, because Dave, the senior engineer, already knew how checkout worked. We never wrote down the problem, where it came from, or how to reproduce it. We counted on Dave to follow up, ask questions until he had the context, and power through anyway. He delivered.
Dave still reads the tickets. But now, so does the agent. And the agent won't power through, or it will and hallucinate.
That's the whole shift, and it's bigger than it sounds. A human queue tolerated half-finished work items because the human at the other end filled in the gaps from memory. An agent holds no memory of your architecture. So the issue stopped being a pointer to work and became the work item itself: a vetted, codebase-aware spec you can pull off a queue and run. Same title box. Opposite contract.
issues are prompts
When the primary reader of an issue is an agent, the issue becomes a strict job description: the acceptance criteria, the constraints, the path. It's the execution plan that kicks off the agent's loop.
So I stopped hand-writing them. I run a custom Claude command, create-issue, that forces the model to act as both a System Architect and a Product Manager. Before it drafts a line, it scans the repo, detects the existing patterns, and weighs the architecture, then proposes a path. I argue with it in the chat until the plan is right, or I let it file the issue and edit the issue afterward. Either way, what lands on the queue is vetted, not a stub I'll have to explain later.
Staleness is the obvious objection: the codebase moves while the issue waits. Mostly handled now. The agent re-reads the current tree when it picks up the work, not the tree as it was when I filed it, so the context re-grounds on pickup.
I'm also embedding machine-readable data straight into the issue text. My roadmap skill detects a roadmap-meta convention and injects a hidden metadata block tracking triggers, dependents, and epic parents. The acceptance criteria and repro steps are the part Dave actually reads; the metadata block is plumbing he scrolls past. Both ride in on the same discipline. The issue is no longer a static document; it's an executable node in an automated system.
pull requests are the context
If the issue is the prompt, the pull request is now part of the context.
PRs stopped being just for human review; they're the context the next agent reads before it touches the feature. A human comment on a PR is an iterative prompt that kicks off a new loop. And when an agent opens that PR six months later to chase a new bug, it's looking for intent: how the original problem was solved, and why.
Here's a real one from gh-aw-fleet: PR #177. What was done, why it was done, when. That PR was written for the agent who reads it next, not just the human who approved it then.
Writing that down is discipline, and it's the same discipline that keeps PRs small. Agents have finite context windows, so a monolithic PR confuses them; the industry drift toward stacked PRs (Graphite and friends) is the same instinct. I enforce it with a pr-message skill that makes the agent generate a ledger of the session: files created, files modified, validations run. It leans on a commitlint skill for the headers (feat(auth): ..., chore(deps): ...). If PRs and commits are the agent's memory, commitlint is the database schema: standardize the headers and a future agent parses the history without blowing its context window.
github is the queue
Step back and the pattern is bigger than either primitive. Issues and PRs are just items. The thing they sit in is a queue, and it always was one; we just never called it that, because a human stood in the middle doing the dispatching. Take the human out and the pipe is obvious. GitHub already shipped a feature called "merge queue"; they undersold it. The whole thing is a queue.
It's a lousy queue by message-broker standards, and that's fine. It's not fast: enqueue-to-start is "whenever I assign it," not milliseconds. It's not FIFO, because priorities move; an issue I filed in March takes a roadmap/current label and jumps the line today. Assignment is the lease that keeps two agents off one ticket. A PR that fails CI is a dead-letter: back it goes with a comment, and the comment is the retry. The one thing GitHub won't give me is the broker's exactly-once guarantee; it's a queue by convention, not by protocol.
None of that is RabbitMQ, and it doesn't need to be. Agent work runs in minutes and its priority is a judgment call, so I want a queue that's durable, readable, and re-orderable long before I want one that's fast. This is shift-left applied to work definition: pay the vetting up front, once, instead of paying it back in a confused agent loop burning tokens on a bad PR.
I've been assembling this out of my own config since last fall. None of it is manual: skills.sh drops a skill in for each primitive, and the queue splits in two:
| Authors the item | Manages the queue | |
|---|---|---|
| Issues (inbound) | create-issue |
roadmap |
| PRs (outbound) | pr-message |
code-review |
I own the issue half. The review half checks whether the result still fits the tree as it is now, and that half is a commodity: pick your CodeRabbit, your Copilot, your Claude. Half the queue I run myself; half the market runs for me.
the new dialogue
Treat issues as prompts, PRs as context, and GitHub as the queue, and source control stops being a vault of code and becomes a dialogue between human intent and machine execution. Same backlog Dave pulled from. Different consumer reading it. And Dave finally gets his reproduction steps.
Top comments (0)