How to triage
An incoming issue shows up framed by someone who saw a symptom, scored its urgency by how much it hurt them, and now wants your queue rearranged around it. Triage is the gate. It decides whether that report earns any of your time, how soon, and whose. This is MANIFESTO.md turned on triage: admitting and ranking an incoming issue without yet deciding how to fix it. The objective hasn't changed, but it gets restated for this phase: decide whether an issue is real, how urgently it must move, and who owns it next, spending only as much as the decision needs and no more. When two of the moves below pull against each other, the thing that settles it is never how loud the reporter was. It's who controls the input and how wide the blast radius is. And none of it is free.
Treat the report as a hostile, unreliable input
The reporter's framing and asserted severity are claims from an untrusted source; re-derive the real problem and bound the claim before you spend on it.
A bug report is data crossing a trust boundary. A human saw a symptom, guessed at a cause, and attached a "critical" they have every reason to inflate. Treat it as hostile until validated (IV), not because the reporter is lying, but because their framing is unvalidated input, and acting on it unchecked is how you burn a whole day on a problem that was never there. So don't trust what the report says it is. Re-derive what actually happened from the evidence, and bound the claim to what the evidence supports before it earns a slot in the queue.
- A "data loss" report: confirm a row actually vanished from the store, not a stale cache or a filtered view, before you page the on-call.
- An asserted "P0": re-derive severity from who is affected and how, rather than accepting the priority the reporter typed into the field.
- A "your service is down": check whether their request even reached you, not assuming the fault is yours because the symptom surfaced on their screen.
Tension: This pulls against responsiveness. Validating every claim before you react can stall a genuine fire while you sit there asking for repro steps. Bound the spend, not the urgency: a cheap sanity check is not a forensic audit, and a report that smells like data corruption gets validated fast and in parallel with raising the alarm, not after it.
Ask yourself: What does the evidence actually show, as opposed to what the reporter asserts, and have I bounded the claim before it costs anyone a slot?
Reproduce before you rank; an unreproducible bug is a rumour
Get a deterministic repro or mark the issue unconfirmed; you cannot size, prioritise, or hand off what you cannot make happen on demand.
Ranking an issue you cannot reproduce is ranking a feeling. A repro is the triage-phase test (XXIV): the executable proof that the problem exists and the yardstick everything downstream measures against. Without one you are guessing at scope, guessing at severity, and handing the next phase a ghost to chase. So either pin the issue to a deterministic sequence that makes it fire every time, or label it unconfirmed and rank it as what it actually is, a rumour with a screenshot, not a sized piece of work.
- A flaky failure: find the condition that makes it deterministic (the ordering, the timezone, the cold cache), not "it happens sometimes under load".
- A repro you have: attach it to the issue as the artefact the next phase inherits, rather than a prose description nobody can replay.
- An issue you cannot reproduce: mark it unconfirmed and rank it below anything proven, not slotting an unverified claim above real, demonstrated work.
Tension: This pulls against urgency on the genuinely rare. Some real issues resist reproduction precisely because they are intermittent, and "unconfirmed" must not turn into a bin where serious-but-rare bugs go to die. When the unconfirmed claim is high blast radius (corruption, a security hole), the right move is a bounded investigation to get the repro, not a quiet downgrade.
Ask yourself: Can I make this happen on demand, and if not, have I ranked it as the rumour it currently is rather than the fact it is dressed as?
Rank by blast radius, not by who is shouting
Severity is who and what is affected and how wide; an attacker-controlled or data-corrupting issue outranks a cosmetic one no matter how loudly the cosmetic one is reported.
The queue is a scarce shared resource, and the loudest reporter is not the widest problem. Rank by blast radius (IV, XIX). A bug reachable by an attacker-controlled input, or one that silently corrupts data, threatens everyone and leaves damage that outlives the fix, so it outranks a misaligned button that a hundred people noticed and complained about. Volume tells you about attention, not about harm. Let the radius of who and what is affected set the order, and let the manifesto's blast-radius objective break the ties that the shouting would otherwise win.
- A corruption bug: the silent data-corruptor that affects a few now and spreads outranks the cosmetic flaw everyone can see, because one compounds and the other only annoys.
- An attacker-reachable path: ranked by what a hostile input could do through it, not by whether anyone has complained yet.
- A loud but contained annoyance: sized by its actual reach, rather than promoted because the thread is long and the reporter is senior.
Tension: This pulls against the real cost of loud, low-severity pain. A cosmetic issue that blocks a flagship demo or erodes a key account can carry business blast radius that the technical severity misses. Fold that into the radius honestly, as affected-and-how-wide, so "blast radius" never becomes an excuse to ignore who is hurting. But volume on its own still never reorders the queue.
Ask yourself: Who and what does this actually affect and how far does the damage spread, and am I ranking by that radius or by the decibels?
Triage on the signals you shipped, not on a guess
The metrics, traces, and error rates tell you the real scope and severity; if you cannot tell how many are hit, that blindness is itself an issue to record.
A guessed scope is a number you invented under pressure. You shipped observability so that the question "how many users hit this, how often, since when?" has an answer in the data rather than in the room (XVIII). Triage on that. Let the error rate, the trace, and the affected-tenant count size the issue, because they see the blast radius your intuition cannot. And when the signal is missing, when you genuinely cannot tell how wide this is, that is not a reason to guess. It is an observability gap, and the gap gets filed as its own issue rather than papered over with a confident estimate.
- A new error: read the rate and the first-seen timestamp to bound when it started and how fast it is spreading, not eyeballing severity from a single report.
- A "widespread" claim: check the affected-user count in the telemetry, rather than trusting an adjective.
- A scope you cannot measure: record the missing signal as a gap to close, not inventing a number to fill the priority field.
Tension: This pulls against speed when the dashboards are slow or the query is hard. Don't let a perfect measurement block an obvious page: when the signal clearly shows a fire, act on it, and when it is genuinely absent, an explicit "scope unknown, observability gap filed" is a more honest input to ranking than a fabricated percentage.
Ask yourself: What do the signals say about who and how many are hit, and if they say nothing, have I filed the blindness as its own issue instead of guessing?
Give it one owner of record and a title that reveals the risk
An issue with no single owner is the one nobody acts on; the title must carry the failure mode, not a vague gesture at the symptom.
Process is the structure that catches what slips between people (XXV), and an unowned issue is the first thing to slip. Everyone assumes someone else has it, so it sits untouched through the whole incident. Give every triaged issue exactly one owner of record - the single person accountable for its next move - so that "who has this?" is never the first thing anyone asks once the outage is under way. And the title is the most-read line of the issue, so spend it on the load-bearing risk instead of on a label for the symptom (XXII). A title that names the failure mode tells a cold reader what is at stake before they have even opened the thing.
- An owner of record: one named person accountable for the next step, not a team alias that diffuses into nobody.
- A revealing title: "unbounded retry storms a flaky agent" carries the failure mode, unlike "fix reconnect", which hides the risk until someone opens the ticket.
- A handed-off issue: ownership transfers explicitly to a named person, rather than being dropped into a shared queue where it ages unclaimed.
Tension: This is about a single accountable owner, not a single worker. Naming one owner of record must not become a bottleneck that blocks the issue when that person is away; the owner is accountable for the next move, which can be reassignment, and the title must reveal the one risk without trying to encode the whole investigation in a slug.
Ask yourself: If this breaks while I am away, does exactly one person know they own the next move, and could a cold reader predict the risk from the title alone?
Decide the disposition explicitly, and write down why
Fix-now, schedule, needs-a-spike, or won't-fix is a reversible decision recorded with its reason; the silent "we'll get to it" is a zombie ticket that rots in the queue.
Triage exists to produce a decision, and once a decision is made and recorded it is something you can later reverse on evidence (XX). So state the disposition outright - fix-now, schedule, needs-a-spike, or won't-fix - and write the reason down next to it. The reason is the thing that lets a future reader reopen the call when the world changes, instead of having to re-argue it from nothing. A non-decision is just a silent swallow (XIII). The issue you neither act on nor close becomes a zombie. It isn't alive enough to fix and it isn't dead enough to forget, and it silts up the queue and the next triage along with it.
- A won't-fix: closed with the reason recorded, so it can be reopened on new evidence, not left open forever as a guilty maybe.
- A needs-a-spike: dispositioned as exactly that with the open question named, rather than parked in an ambiguous "investigating" that never resolves.
- An explicit fix-now: ranked and recorded with its justification, unlike the silent "we'll get to it" that commits nobody and decays into a zombie ticket.
Tension: This pulls against the cost of deciding under uncertainty. Forcing a disposition when you genuinely lack the information can fossilise a wrong call; the honest move is then "needs-a-spike" with the unknown named, which is itself an explicit decision, not the false comfort of leaving it open and unranked.
Ask yourself: Has every issue left this gate with an explicit disposition and a recorded reason, or have I left one in the limbo that becomes a zombie?
Keep triage bounded; it decides whether, not how
Time-box triage and resist debugging or designing the fix inside it; hand the real work to the next phase rather than letting the gate absorb it.
Triage is a queue, and an unbounded queue is the failure that turns up later (VII). Once triage starts solving issues rather than ranking them it stops draining, the backlog builds up behind it, and the gate turns into the very bottleneck it was supposed to stop. Do the smallest thing that settles whether and how urgently, and take away the temptation to do any more than that (XXI). That smallest action is the whole job. The deep debugging, the root-cause hunt, the design of the actual fix - all of that belongs to a later phase that has its own time and its own owner. Time-box it, decide, and pass the real work on with whatever you learned stapled to it.
- A tempting fix: route it to the next phase with the repro and signals attached, rather than debugging it to ground inside the triage slot.
- A time-box: a bounded budget per issue that forces a disposition, not an open-ended investigation that quietly becomes the work.
- A genuinely unknowable call: dispositioned as needs-a-spike and handed off, instead of designing the spike's solution while still inside triage.
Tension: This pulls against the pull to just fix it now. A trivial, obvious, low-radius fix can sometimes be cheaper to land on the spot than to formally hand off, and forcing every such case through the full pipeline is its own waste. Allow that only when the fix is genuinely small and reversible; the moment it needs real debugging or design, it has left triage and must be handed on.
Ask yourself: Have I done the least that settles whether and how urgently, and am I about to hand off the real work, or am I quietly doing the next phase's job inside this one?
The through-line
Every move here is the manifesto's one move wearing triage's clothes: push the ranking out of the loudest voice and onto the evidence and the blast radius, so the queue is ordered by what an issue threatens, not by how loudly it was reported. A report validated as a hostile input, an issue reproduced before anyone ranks it, a queue ordered by who and what gets harmed, severity read off the signals you shipped, one owner and a revealing title, an explicit disposition with its reason next to it, and a gate that stays bounded to whether and not how. The next phase ought to inherit a decision it can trust and act on cold, because by then triage has already proved the issue is real, sized it by what it actually threatens, and named who moves it next.
Rank by what the evidence shows and how far the damage spreads, never by how loudly it was reported. Decide whether and how urgently, write down why, give it one owner, and hand the how to the phase that owns it. The issue you neither act on nor close is the one that hurts you later.