On 1 August I opened an issue that was three sentences long.
A hundred and one minutes later the feature was merged, and the document that got it there ran to 457 lines.
I didn't write those 457 lines.
In fact, I didn't have to write any more documentation, and not because I simply allowed Claude to run amok.
Here is the issue in full — , 225 characters: control-api#265 — Manifest-backed dashboard feeds From that genesis moment, this is the lifecycle of the issue all the way through to landing: Time (UTC) Event 14:25 Issue #265 opened — 225 characters 14:54 spec committed — 457 lines 15:35 Spec merged (PR #266) 15:51 Implementation committed 16:06 Implementation merged (PR #267, 15 files), issue closed The interesting part isn't the speed.
It's the step at 14:54 that landed a previously non-existent spec document, and what happened in the twenty-nine minutes before it.
The issue was never a specification I often write issues like this one...the way most people write shopping lists.
Actuator address is not ensured?
Baseline the trace correctly.
With the pre-rolls, the frame-rate looks out.
They're abbreviated to the point of being cryptic to everyone else.
I write them this way deliberately: I'm usually mid-something else when I notice a problem, or have an idea for a better route to the solution.
The cost of a full write-up right at that moment would be a fractured sense of flow.
As most engineers will tell you, the transitions into and out of flow are the most disruptive parts of their working day.
This terse form of issue-writing can be all you need, and it's worth being precise about why it works and the trade-offs it includes.
It is not because "the issues are good enough".
They aren't.
When you pick one of these up later, you can reconstruct everything missing from memory.
In my case with control-api#265, it was a particular panel type I was thinking about, a constraint I hit last week with it, the reason the obvious approach was off the table, etc...
That reconstruction was free and, more importantly, it was invisible to any external observer.
When you are the issue filer and its eventual closer, you never experience this rehydration as filling a gap.
You see the issue's sparse wording as an aide-memoire for the fuller context that is still cached in your head.
What bites here is when you work in a team (of other engineers or coding agents): something has to bridge this gap or the issue will get resolved using a foundation of well intentioned misinterpretation and apparently sensible assumptions.
An agent has something both better and worse than an engineer's recall — it can read every file in the repository, including ones you have forgotten, and it can read exactly nothing of your latent intentions.
Fortunately, the fix is not to write longer issues.
Capture is working; it's the handoff that's broken, the context sync.
An agent as interviewer asks for decisions, not descriptions The obvious version of this step — "the agent asks clarifying questions" — undersells it, and would be annoying in practice.
Being interrogated about your own bug report is worse than writing the report properly in the first place.
What makes it worth doing is the ordering.
The agent goes and reads first, then comes back with what it found and asks you to decide — not to describe.
You can see the split in the spec that grew out of my dashboard issue.
Its reference list is entirely material the agent uncovered on its own: FEAT-0007 §2 — References (excerpt) Nobody asked me where the JSONB containment query lived, or which existing service had already settled the question of what a fan-out failure should do to the primary write.
Those are enrichment lookups, and lookups are the agent's half of the job.
What came back to me as decisions to make were the things no amount of reading resolves: whether one SSE stream per dashboard was actually the point, whether a second repository was allowed to be changed for this, where the boundary of the work sat.
The spec's Out of Scope section is five such rulings written down.
That's the trade that makes the interview cheap enough to bother with.
The expensive half is already done by the time it reaches you.
The best question contradicted the issue Read my second sentence again: for each sensor the dashboard uses, tag it.
There's an assumption buried in there — that the control-api server can tell which sensors a dashboard uses.
It can't, and the spec says so at length: FEAT-0007 — Design Decision (excerpt) When I wrote the issue I was picturing chart panels, where my sentence is true.
Health-stats panels store two device IDs and resolve the rest at render time in a different repository.
A server-side parser would have to duplicate a list of constants that already lives in the frontend, with no shared source of truth and no error when it happens to drift.
So the spec proposes the opposite: the client sends an explicit list alongside the layout, and the server never parses the layout at all.
That decision brings a new API rule with it — a supplying without is rejected with a , so a layout change can't silently leave the tag set stale.
None of that is in my three sentences.
One line of it directly contradicts them.
This is the part I want to defend hardest.
An interview process that only elaborated what I wrote would be formatting.
This one went and checked, found my premise didn't hold, and brought back a reversal — before any code existed to be wrong.
The numbering is the fossil record Something else survives in the finished document.
Its requirements run FR-1 through FR-10 — but there's also an FR-6a, an FR-10a, an NFR-3a, an AC-8a and an AC-8b.
A list written straight through numbers 1 to n.
Letters mean insertion into a list that had already been numbered.
Each one marks a place where a later question forced something in between. is the one I'd point at. had established that dashboard tags are reserved, so a caller's tag edit must preserve them.
Then: FEAT-0007 — FR-6a (excerpt) An existing / in unrelated code turns the new requirement into precisely the failure it was written to stop.
Follow FR-6 faithfully and you ship the bug.
That is not a question anyone thinks to ask about a three-sentence issue, and it was answered in a spec document before any code was written rather than in a post-mortem.
The whole document is published alongside this article — FEAT-0007: Manifest-Backed Dashboard Feeds — so the excerpts above can be read in context.
What the spec bought afterwards Implementation merged thirty-one minutes after the spec did, across 15 files.
What I want to draw out isn't the speed — it's this line from the implementation PR's test plan: AC-8b (migration's behaving correctly against a pre-populated table) has no automated test — this repo has no migration-test harness (integration tests use , not Alembic).
Verified by construction: [...] One acceptance criterion, honestly marked unmet, with the reason.
That's only possible because AC-8b existed to be unmet.
Without the spec there'd be no gap — just a migration nobody had thought to test, and no record that anyone noticed.
A spec's most underrated property is that it gives the work something to fail against.
Doing it on purpose The shape I'd suggest, in the order that matters: Keep writing bald issues.
The three-sentence note is doing its job.
Don't move the cost back to capture time; you'll just stop capturing.
Put the interview at triage pickup, not at capture.
This is the whole trick.
At pickup you're already thinking about the problem and the agent can read the code now, against the repo as it currently is rather than as it was when you filed.
Make the agent read before it asks.
Questions that arrive with file paths attached are answerable in a sentence.
Questions that arrive cold are homework.
Scale the output to the work.
A small change wants an implementation-plan comment on the issue itself.
A feature wants a numbered document — mine live at , and get their own PR