Files
marketplace/skills/issue/references/format.md
T
naudachu 091dceec1d refactor: split issue domain from Gitea transport
An issue was a Gitea row that happened to be cached locally: its identity
was the tracker's number (42.md), its dependencies were tracker numbers
(depends: [#12]), and a local issue existed only as a draft that push
deleted on success. Nothing could be planned or tracked without a tracker.

Split into layers, with knowledge flowing one way:

  skills/issue  DOMAIN  what an issue is: format, validation, dep graph
        ^               offline; stdlib imports only, no subprocess
        | imports
  skills/sync   BRIDGE  map.py    md <-> Gitea JSON, pure, no I/O
                        _gitea.py login pin, api, pagination, filters
  skills/use    REFERENCE  tea CLI docs for non-issue entities

skills/issue never imports skills/sync. Delete the sync layer and the
domain keeps working.

Identity is now a slug derived from the title (wire-sqlc-appclick.md) and
is stable across retitles and pushes. Tracker numbers live in a `gitea:`
field, never in a file name and never in `depends:`; the pair is indexed
in .remote.json, which is a cache over the files, not a second source of
truth.

Behavior changes:

- Pushing is additive. The file is never deleted; it gains gitea:/url:/
  synced: and origin: flips from local to gitea. `origin: local` is a
  durable state, not a pending one.
- Pushes go in topological order so dependencies get numbers first.
- The dependency graph is computed offline from `depends:` metadata; body
  prose is passed through unchanged in both directions rather than being
  rewritten between slugs and #N.
- `origin` is domain-owned (whether work exists elsewhere is a fact about
  the work); the handle and how to reach it stay with sync.

Script moves:

  issue_get.py   -> sync/pull.py
  issue_push.py  -> sync/push.py
  issue_list.py  -> sync/remote.py
  issue_index.py -> issue/issue_index.py
  _tea.py        -> split into issue/issue.py, sync/map.py, sync/_gitea.py

New: issue/issue_new.py, issue/issue_check.py, issue/issue_tree.py, and
sync/comment.py — comment posting was the last issue operation still
hand-rolled through raw `tea api`.

references/issue-format.md moves to skills/issue/references/format.md;
label hex colors move out of it into map.py, since a color is how a
tracker paints a chip, not what an issue is.

Verified: offline path end to end (new, check, tree, index, push
--dry-run) and read-only against Gitea (remote listing, pull with
mapping, comment guard). Write paths of push.py and comment.py are not
exercised here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-09 23:37:32 +05:00

10 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. Source spec: the project wiki (Issues-Workflow).

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

Identity

An issue is one file, tmp/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, and an issue pushed to a tracker keeps it too. Tracker numbers are a foreign key stored in a field, never the name of anything.

tmp/issues/wire-sqlc-appclick.md

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
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
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 durable state, not a pending one. An issue that never leaves this machine is complete and valid. Pushing is optional and additive.

Language rules

  • Issue title: English, imperative mood, no type prefix — the type lives in the label, not the title. Good: Fix tea-guard crash on empty settings file. 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 skills/sync/scripts/map.py 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 — issue_check.py 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.

Draw the graph with issue_tree.py. The reverse direction is a grep:

grep -ln 'depends:.*migrate-schema' tmp/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.
  • 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 link back via their depends:. Keep implementation detail in the children; the feature body stays at business level.

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

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

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

## Issues
- [ ] wire-sqlc-appclick — краткое описание части
- [ ]## 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.