Files
marketplace/plugins/kettle/skills/issue/references/format.md
T
naudachu e628ad6fd9 refactor!: rewire the plugin onto the kettle binary, and rename it
BREAKING: the plugin is `kettle`, not `tea`, and its commands are `/kettle:*`.
It also now needs a binary on PATH that it did not need before; the README and
every skill say how to get one and what a missing one looks like.

The plugin was 3800 lines of Python doing what a compiled binary does better,
and the name pointed at a tool that no longer takes part: `tea` is Gitea's CLI,
and since the transport moved into the binary nothing here shells out to it for
issues at all. A plugin named after it was going to keep suggesting otherwise.

Deleted: 19 scripts, the 14-file unittest suite, and the tea-guard hook. The
guard blocked any `tea` invocation that would run under a login the model picked
instead of the operator; the binary holds its own credentials and reads the
pinned login out of the project's own config, so that failure is no longer
expressible and there is nothing left to police. agents-sync stays — it is about
AGENTS.md symlinks and has nothing to do with any of this.

What the plugin keeps is what only a plugin can carry: the rules an operator
states and a binary cannot enforce. `init` still refuses to run inside a linked
worktree and still may not be model-invoked, because which directory is the
project is a statement a person makes. The issue format reference stays here and
stays the source of truth. The runner subagent is still for batches and still
may not decide what an issue says.

The command reference in the issue, sync and project skills is GENERATED from
the binary's own command registry, between markers, so a flag that changed
cannot ship with a skill that recommends the old one. `kettle gen skills
--check` exits non-zero when they drift. The generator owns the region and
nothing outside it: the frontmatter description, which is what decides whether a
skill loads at all, stays hand-written.

`use` survives and is the one place `tea` is still named — for releases,
webhooks and actions, which kettle does not cover. Its instruction to write
`--login "$GITEA_LOGIN"` and let the hook substitute the pin was true until this
commit and is now rewritten: `tea` keeps its own configuration, kettle keeps
its own, and configuring one configures nothing in the other.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 00:04:44 +05:00

14 KiB

Issue format

Canonical format for every issue in this project, whether it ever reaches a tracker or not. Designed to be unambiguous for both humans and LLMs: fixed English section headers in a fixed order, verifiable acceptance criteria, one issue = one deliverable.

Nothing here depends on Gitea. How these files are mapped onto a tracker is the sync layer's business — see /kettle:sync.

Identity

An issue is one file, .kettle/issues/<id>.md, and id is a slug: lowercase ASCII, digits, single dashes, derived from the title. The slug is the identity. It is stable for the life of the issue — a retitled issue keeps its slug; an issue pushed to a tracker, deleted locally and fetched back a month later keeps it too. Tracker numbers are a foreign key stored in a field, never the name of anything.

.kettle/issues/wire-sqlc-appclick.md

A slug never contains a dot, which is how the store tells an issue from the files parked beside it (<id>.comments.md).

Stability is a promise the format makes, so something has to keep it once the file is gone. That is the sync layer's problem and its answer is a marker in the body — see /kettle:sync; the domain neither writes nor reads it, and it never appears in the file on disk.

Metadata block

One field per line, lists inline, so plain grep works without a parser:

---
id: wire-sqlc-appclick
state: open
labels: [type/task, tech/sql]
assignees: [naudachu]
milestone: v0.2
depends: [migrate-schema]
origin: gitea
branch: feat/wire-sqlc
gitea: claude-skills/tea#42
remote-updated: 2026-08-09T18:24:01Z
synced: 2026-08-09T18:40:00Z
url: https://git.noodles.cam/claude-skills/tea/issues/42
---
# Wire sqlc into the appclick repo layer

## Summary
Field Owner Meaning
id domain slug; equals the file name
state domain open or closed
labels domain see namespaces below; exactly one type/*
assignees domain logins; may be empty
milestone domain title, or none
depends domain ids this issue depends on — the authoritative graph
origin domain local, or the name of a tracker this also lives in
gitea sync the handle in that tracker: owner/repo#N
branch sync the tracker's branch link (Gitea ref); push fills an empty one with the current git branch, and never overwrites a filled one
url, synced, remote-updated, comments sync bookkeeping

Domain fields render first, in the order above; sync fields follow, sorted.

origin is domain-owned on purpose: whether a piece of work exists anywhere but here is a fact about the work. Where that is, and how to reach it, is the sync layer's business — the domain carries gitea: and the rest through load/save verbatim and never reads them. That passthrough is why one file can represent a local issue and a synced one without a second format.

origin: local is a complete state, not a pending one. An issue that never leaves this machine is valid and finished work; pushing it is optional and nothing here treats it as a draft.

It is not a permanent state, and it is what the file's fate depends on:

origin: what the file is what a push does to it what eviction does to it
local the issue itself — the only copy there is creates it in the tracker, then deletes the file nothing, ever — in any state, named or not
a tracker a working copy of something the tracker already has updates the tracker, then deletes the file removes it once state: closed

A successful push deletes .kettle/issues/<id>.md (and <id>.comments.md), on create and on --update alike. What is in the store is what has not left this machine; everything else is fetched again when it is needed. The rule, its safety conditions, and how the slug survives are /kettle:sync's to state.

A closed issue is evicted from the store by kettle evict — same trade, one condition more: the work is done and it exists somewhere else. An origin: local issue is never evicted, because there is nowhere to fetch it back from. The store is a working set, not an archive; kettle pull <n> fetches a closed issue again whenever it is wanted.

The id never changes across that round trip, which is why depends: in other issues keeps working. That is the format's promise; the mechanism is not.

Language rules

  • Issue title: English, imperative mood, no type prefix — the type lives in the label, not the title. Good: Fix the index rebuild on an empty store. Bad: fix: crash, [bug] crash, Крашится гвард.
  • Section headers: the exact English literals below, as ## headings, in the given order. Do not translate, rename, or reorder them.
  • Body prose (text inside sections): Russian.

Label namespaces

Four namespaces classify an issue. Two are exclusive (at most one label from the namespace), two are free-form:

Namespace Exclusive Purpose
type/* yes What kind of work; primarily its business value. Mandatory, exactly one.
severity/* yes Business impact. At most one; apply when the impact is known.
tech/* no Technology the issue is bound to. Any number.
comp/* no System component of this repo. Any number; no preset — project-specific.

type/* — mandatory, exactly one

Label Meaning
type/bug Something behaves incorrectly in existing code
type/task Implementation of new functionality
type/refactor Internal restructuring: file moves, architecture; behavior must not change
type/test Writing or fixing tests
type/feature Container: several issues delivering one unit of business value
type/draft Idea captured for later; not ready for work

severity/* — at most one

severity/low, severity/medium, severity/high, severity/showstopper, severity/critical.

tech/* — any number

Technology-bound labels, e.g. tech/sql (pgx, sqlc, sql-migrate — persistent storage), tech/obs (grafana, loki, prometheus, alloy — observability), tech/postgres.

comp/* — any number

Components of this repo's system, e.g. comp/appclick. No preset list — derive from the project.

Label colors are not part of the format: a hex code is how a tracker paints a chip, not what an issue is. They live in the binary's mapping layer (cli/internal/mapping) and are applied on push.

Dependencies

depends: in the metadata block is the graph, and it holds ids:

depends: [migrate-schema, add-pool-cfg]

An optional ## Depends on section, placed right after ## Spec, carries the human explanation — one reference per line, with a reason where it helps:

## Depends on
- migrate-schema — нужна схема БД из этого issue
- add-pool-cfg

The section is prose and is passed to and from a tracker unchanged; only depends: is walked when the graph is computed. Keeping them consistent is on you — kettle check warns when the section names an id that depends: does not list. Omit the section when there are no dependencies; never write an empty one.

A type/feature container writes the same relation under ## Issues instead (see the template below). Same direction, same rule: every id named there also belongs in that issue's depends:. The warning names whichever of the two sections the reference actually came from.

Draw the graph with kettle tree. The reverse direction is a grep:

grep -ln 'depends:.*migrate-schema' .kettle/issues/*.md

Shared rules

  • ## Summary is always the first section; ## Acceptance criteria is always present (exception: type/draft). These two are the anchors every reader (human or LLM) relies on.
  • ## Spec is mandatory in every type. Its value is a repo path (docs/specs/auth.md), a URL, or the literal none when no spec exists. Never omit the section and never invent a link — none is an explicit, valid answer.
  • Acceptance criteria are - [ ] checkboxes; each item is an objectively checkable condition, not an aspiration.
  • A checkbox is item markup, not a property of one section: - [ ] unticked, - [x] ticked, and it means the same under ## Issues as under ## Acceptance criteria. An item that wraps continues on an indented line and is still one item. A - [ ] inside a ``` code fence is an example of the markup, not state. Tick them with kettle ac, which reads the whole body on exactly these rules and rewrites one character; progress (3/7) is counted off the body and is never a metadata field.
  • Code references use the path/file.ext:line form; related issues by id.
  • Screenshots are allowed but their content must be duplicated as text — an LLM reading these files cannot see images.
  • If acceptance criteria grow past ~5 unrelated items, split the issue (or promote it to a type/feature container with child issues).

Template: type/bug

## Summary
Что сломано и где проявляется, одно-два предложения.

## Spec
`docs/specs/auth.md`, URL — или `none`.

## Steps to reproduce
1.2.## Expected
Что должно было произойти.

## Actual
Что происходит на самом деле: вывод команды, лог.

## Environment
Только релевантное: версии, ОС, конфигурация.

## Acceptance criteria
- [ ] баг не воспроизводится по шагам выше
- [ ] добавлена проверка на регрессию (если применимо)

Template: type/task

## Summary
Что нужно сделать, одно-два предложения.

## Spec
Ссылка или `none`.

## Motivation
Какую проблему пользователя/системы это решает.

## Acceptance criteria
- [ ] проверяемое условие
- [ ]## Constraints
Что НЕ входит в объём; технические рамки. (опционально)

Template: type/refactor

## Summary
Что перестраиваем и в каких файлах (`path/file:line`).

## Spec
Ссылка или `none`.

## Motivation
Чем плохо текущее состояние: дублирование, связность, читаемость.

## Invariants
Что НЕ должно измениться: поведение, публичные API, форматы данных.

## Acceptance criteria
- [ ] проверяемое условие (тесты зелёные, старый путь удалён, …)

Template: type/test

## Summary
Что покрываем тестами и где (`path/file:line`).

## Spec
Ссылка или `none`.

## Motivation
Зачем: регрессия после бага, пробел в покрытии, флаки-тест.

## Test cases
- сценарий → ожидаемый результат
-## Acceptance criteria
- [ ] перечисленные кейсы покрыты и зелёные
- [ ] тесты проходят в CI

Template: type/feature

A container: one unit of business value delivered by several child issues. Child issues carry their own type/* (task, bug, test, …) and know nothing about the container.

The container depends on its children, never the reverse. Every child id goes in the container's own depends: and, as prose, in its ## Issues section; a child's depends: is for that child's real dependencies and must not point back at the container. Keep implementation detail in the children; the feature body stays at business level.

That direction is not a convention picked at random. "The container is closed when its children are closed" is a dependency relation. "This child belongs to that feature" is a membership relation, and membership has no place in a dependency graph. Pointed the other way the two rules contradict each other: the moment the container listed a child that already depended on it, kettle check would report ERROR cycle. With the edge going down, the graph reads as nesting — kettle tree draws the container as the root with its children beneath it — and the check is green.

So the container's metadata block carries the children:

depends: [wire-sqlc-appclick, add-pool-cfg]

and its body repeats them for a human:

## Summary
Бизнес-ценность одним-двумя предложениями.

## Spec
Ссылка или `none`.

## Motivation
Какую проблему пользователя/системы это решает.

## Issues
- [ ] wire-sqlc-appclick — краткое описание части
- [ ] add-pool-cfg — краткое описание части

## Acceptance criteria
- [ ] все дочерние issues закрыты
- [ ] проверяемое условие уровня фичи (например, e2e-сценарий работает)

Template: type/draft

A parking spot for ideas that are not fleshed out yet. Minimal structure, no acceptance criteria required. Before implementation starts, a draft MUST be promoted: relabeled to a concrete type and rewritten into that type's template.

## Summary
Идея одним-двумя предложениями.

## Spec
Ссылка или `none` (для драфтов обычно `none`).

## Notes
Свободные заметки: что известно, открытые вопросы, варианты.

Containers beyond type/feature

  • Milestone — a set of issues with an optional time bound. Locally it is just the milestone: field; a tracker-side milestone must already exist for a push to attach the issue to it.
  • Project — a set of issues tracked by status columns (Backlog, ToDo, InProgress, Ready, Done). Not represented in this format and not reachable through the Gitea API — web UI only.