propose-issue
The contract for filing work that survives the session that thought of it.
Every persona in this bundle defers work: Perlman files vision epics, Occam files deferred QA findings, Quine files follow-ups. All of them file through this shape. An issue is not a note to yourself — it is a brief for an agent with no memory of this conversation.
The governing standard
Write for a cold reader with no session context. The issue will very likely be picked up by an agent (or a person) who has never seen the discussion that produced it. If understanding the issue requires anything that is not in the issue, it is not fileable yet.
This single rule generates most of the gates below. "See the discussion above," "as we said," "the usual pattern here," and links to ephemeral context are all failures of it.
Body structure
## Problem
What is wrong or missing, and what it costs. Concrete and observable — name the
symptom a reader could go verify, not the abstraction. If the problem was found
by a specific failure, describe the failure.
## Why it matters
Who is affected and what changes for them once this lands. An issue that cannot
answer this is a preference, not a problem — reconsider filing it.
## Plan
The rough shape of the work: which area or repo it touches, the sequence if
sequence matters, known unknowns. NOT file paths, function names, or technical
design — that boundary belongs to the architect at decomposition time, not to
the persona filing the issue.
## Acceptance criteria
- [ ] Observable, checkable statements — a reader can tell whether each is met
- [ ] Every criterion is satisfiable inside the named repo
- [ ] Includes the verification step, not just the change
## Out of scope
What this issue explicitly does NOT cover, especially the adjacent work a
reasonable implementer would otherwise pull in. This is the scope fence —
without it, thin slices grow.
## Context
Where this came from, and any prior art or related issues. Enough for a cold
reader to orient; no links to anything unrecoverable.
Buildability gate
Before labelling an issue ready for an autonomous queue, all three must hold. If any fails, the issue is still fileable — it just is not agent-buildable, and should be labelled for a human instead.
- Settings — the success state is code in that repo, not a change made in somebody's web console. "Enable the feature flag in the vendor dashboard" is not buildable; "add the flag to the config module and cover it" is.
- Permissions — every mutation the work requires is inside the scopes the agent's credentials actually hold. Check before filing, not at 3am when the lane fails.
- Authority — every acceptance criterion is satisfiable inside that one repo. An AC that depends on another repo's merge belongs in that repo's issue, with a dependency edge between them.
The cost of skipping this: an unattended queue picks the issue up, burns a build slot, and fails at the last step. Worse, it fails silently overnight and is not discovered until morning.
Drift gates
Refuse to file, and fix the shape first, when:
- The issue carries
epic. Epics are umbrella issues, not buildable diffs. Split into child issues and label the children, not the parent. Never mark an epic ready. - The acceptance criteria span more than one repo. Split per repo and link with dependency edges.
- The body names file paths and function signatures. That is a plan, not an issue — you have pre-empted the architecture review. Raise the altitude.
- The issue is one session's worth of work times three. Thin-slice it. One issue should be roughly one session.
- The "Problem" section describes a solution. "Add a caching layer" is not a problem. "The dashboard takes 8s to load because it refetches on every keystroke" is. File the problem; let the implementer find the fix.
Labels
Apply exactly one priority label (MoSCoW: moscow:must / should / could /
wont, defaulting to moscow:should) and exactly one type label (bug /
feature / chore / epic).
Only apply labels that exist in your canonical registry. A new label is propose-before-use: add it to the registry in the same PR that first uses it, and meet the distinct-reader standard — a new label needs a reader who will act on it differently from every existing label. Otherwise you are creating vocabulary nobody reads.
Filing mechanics
Never pass a multi-line body as an inline shell heredoc. It is the single most reliable way to file an issue whose body is empty, truncated, or — worst — left over from a previous invocation.
Write the body to a temp file and pass it by path. Two failure modes to defeat, both observed in practice:
- A stale temp file. A write that silently fails leaves the previous run's body in place, and the create call cheerfully files it. Reserve a unique path per invocation.
- An empty temp file. Creating the file first and then writing to it can fail if your tooling refuses to overwrite an existing file it has not read — leaving a 0-byte body that files successfully. Reserve the path without creating the file.
After creating, verify server-side that the body that landed matches the body you sent, and repair if it does not. A create call returning success is not evidence the body arrived.
Next step
Filing is not delegation. If the work is urgent, say so to the human in the same turn you file it — an issue filed silently is an issue nobody scheduled.
Part of kromatic-dev-stack by Kromatic. Questions on this development stack, how to use it, or how to integrate it with your team — reach us at kromatic.com/contact-us.