feat(task-format): v0.3.0 — primary mappa task.create schema, legacy STATUS.md interim (#983)

Канон создания — через mcp__mappa__task_create (per-type t:N, мутация под
лизом). Legacy-блоки .tasks/STATUS.md — интерм до переключения поллера (#984).
This commit is contained in:
2026-08-24 16:45:58 +03:00
parent 21023f1bae
commit c5ebde174d

View File

@@ -1,27 +1,76 @@
---
name: task-format
author: ours
version: 0.2.0
version: 0.3.0
description: >
Use when writing or editing a task block in a `.tasks/STATUS.md` board that an
autonomous task-runner ("poller") will read — so the task is actually claimed,
routed, and reported instead of silently skipped. Covers the exact block header,
the status emoji, and the `**Weight:**` / `**Notify:**` / `**Requirements:**`
fields the poller parses. Triggers: «оформить таску для поллера», «формат таски»,
«task block format», «make a task the poller will pick up», «add Weight/Notify»,
poller / agent-runner not claiming a task you wrote by hand.
Use when creating or editing a task so it is actually claimable and routed —
not silently skipped. Primary channel: **mappa** — create via
`mcp__mappa__task_create` (схема ниже), не руками. Legacy: пока файловый
поллер (agents-task-runner) не переключён на mappa (#984), блоки в
`.tasks/STATUS.md` обязаны совпадать со строгим форматом (шапка, поля
Weight/Notify), иначе поллер молча пропускает. Triggers: «оформить таску для
поллера», «формат таски», «task block format», «make a task the poller will
pick up», «add Weight/Notify», poller / agent-runner not claiming a task.
---
# task-format
The autonomous poller parses `.tasks/STATUS.md` line-by-line with **strict regexes**. A block runs only if its header and fields match exactly. Get the format wrong and the poller does not error — it silently skips the block, or claims it and then parks it. This is the canonical field reference.
Канон создания задачи — **через тул**, не рукописным блоком. Поллер (автономный
раннер) разбирает задачи строгими правилами; формат задаёт, какая таска будет
взята, отмаршрутизирована и зарепорчена, а какая молча пропущена.
> Authoring a task for **another** project/agent via `mcp__projects-meta__tasks_create`? Use `delegate-task` — it drives the tool, which emits this format for you. This skill is the format itself: for **hand-edited** STATUS.md blocks and for understanding what the poller reads. For board working policy (claim/close/status), see `using-tasks`.
> Создаёшь задачу для **другого** проекта/агента? См. `delegate-task` — он ведёт
> через тул + письмо. Работа с бордом (claim/close/status) — `using-tasks`.
## Canonical block (copy this)
## Primary: mappa task.create
Создание задач в mappa (решение 14/15: мета в сервисе) — только через
`mcp__mappa__task_create`, никогда руками вставляй блоки. Тул сам назначает
per-type номер (`t:N`, решение 20) и пишет сущность.
```
mcp__mappa__task_create(
project: <имя проекта>, // обязателен
slug: <kebab-case>, // обязателен, латиница
title: <одна строка>, // опционально
description: <markdown>, // тело; [[refs]] → рёбра (решение 4)
status: ready | active | paused | blocked | done, // по умолчанию ready
claim_token: <токен лиза проекта> // мутация под лизом (решение 19)
)
```
**Мутация под лизом (решение 19):** `task_create` требует `claim_token`
активного лиза проекта (берётся `mcp__mappa__task_claim_next`). Без лиза —
422 busy. (Карв-аут: чтения и инбокс-доставка лиза не требуют.)
**Per-type номер (решение 20):** номер — канонический машинный реф `t:N`
(см. ответы — поле `ref`). Глобальный id — internal, только для addressing
(#1037). Ссылки на задачу в тексте — `[[t:N]]`, не `#<глобальный id>`.
**Slug-правила:** короткий, lowercase, kebab-case, латиница
(`fix-nl-vds-reality-pq-dest`, не `Fix_this_TASK #1`).
## Status emoji ↔ state
| Emoji | State | Значение |
|---|---|---|
| ⚪ | `ready` | единственное состояние, которое поллер берёт |
| 🔴 | `active` | взято / в работе |
| 🟡 | `paused` | возобновляемо |
| 🔵 | `blocked` | ждёт (указать почему в description/where_stopped) |
| 🟢 | `done` | закрыто |
Не путай: 🟢 — это *done*, не «готово».
## Legacy: блок .tasks/STATUS.md (интерм до #984)
Пока файловый поллер (agents-task-runner) не переключён на mappa-лиз (#984),
блоки в `.tasks/STATUS.md`, создаваемые руками/миграцией, обязаны совпадать со
строгим форматом — иначе поллер молча пропускает или паркует. **Это
переходный канон; новые задачи создавай через task_create.**
```markdown
## ⚪ [#1234 my-task-slug] — One-line description of the work.
## ⚪ [#1234 my-task-slug] — One-line description.
**Status:** ready
**Created:** 2026-08-23
@@ -35,76 +84,54 @@ The autonomous poller parses `.tasks/STATUS.md` line-by-line with **strict regex
---
```
## The two load-bearing rules
Три load-bearing правила:
1. **Header must match exactly:** `## <emoji> [#<n> <slug>] — <description>`
- `## ` (h2, two hashes) — **not** `### `, not a bullet.
- One status **emoji**, then `[#<n> <slug>]` in square brackets — **global task number** (`#1234`, no leading zeros) + slug — then ` — ` (space, em-dash `—`, space), then the description. A `-` hyphen or `:` will not match.
- **Number is the machine key.** Global, unique across the whole federation, encodes creation order. References (`#452` in letters, blocker fields, decision trails) point at the number. The slug is the human-readable part only.
- Slug: short, lowercase, kebab-case, Latin.
- A header that doesn't match is **not seen as a task at all**.
1. **Шапка точно:** `## <emoji> [#<n> <slug>] — <description>` — h2, один emoji,
`[#<n> <slug>]` (глобальный номер, без ведущих нулей), разделитель
` — ` (пробел + em-dash + пробел). Несовпавшая шапка = задача не видна вообще.
2. **Поля — строки `**Label:** value`.** Буллеты и проза игнорируются.
3. **`**Created:** yyyy-mm-dd` обязателен** — пишется один раз при создании.
2. **Fields are `**Label:** value` lines** — bold label, colon, space, value. Bullet-list fields (`- **weight:** …`) and prose ("notify workshop when done") are **ignored** — the poller never reads them.
Номера legacy-блоков назначает сервер (`tasks_create` из счётчика
`OpeItcLoc03/agenda/task-counter`) — **никогда не выдумывай номер руками**.
3. **`**Created:** yyyy-mm-dd` is mandatory** — the creation date. Written once at task creation, never edited after.
## Numbering
- **Numbers are assigned by the server** (`mcp__projects-meta__tasks_create`) from the counter in `OpeItcLoc03/agenda/task-counter` — never invent or reuse a number by hand.
- The **file name** is `yyyy-mm-dd-#####-<slug>.md` — number **5 digits with leading zeros, no `#`**: `2026-06-05-00019-fix-nl-vds-reality-pq-dest.md`. Leading zeros make folder sort = numeric up to 99999. No `#` in the filename (it would break markdown links and Gitea URLs).
- In the **header and text references** the number is written **without** leading zeros: `[#19 slug]`.
## Status emoji ↔ state
| Emoji | State | |
|---|---|---|
| ⚪ | **ready** | the only state the poller claims |
| 🔴 | active | claimed / in flight |
| 🟡 | paused | resumable |
| 🔵 | blocked | waiting on a `**Blocker:**` |
| 🟢 | done | kept until merged |
`**Status:**` mirrors the emoji in words. ⚪ → `ready`. **Do not** use 🟢 for "ready" — 🟢 is *done*.
## Fields the poller parses
### Поля, которые поллер разбирает
| Field | Format | Meaning |
|---|---|---|
| `**Weight:**` | `cheap-ok` \| `needs-claude` \| `needs-human` | Routing tier. **Required for autonomous pickup** — see below. |
| `**Notify:**` | `<owner>/<repo>` | Inbox target. Poller writes to that project's `.agents/inbox/` on close / park / delivery-failure. Omit → no report; the steering loop never closes. |
| `**Requirements:**` | CSV, e.g. `needs-db, needs-secrets` | Hard capability gate. The agent must hold **all** listed capabilities or the task is skipped. |
| `**Runtime allowed:**` | CSV, e.g. `claude-opus` | Runtime whitelist. If set, only a listed runtime may claim. |
| `**Consult policy:**` | `auto` \| `human-only` \| `strict-human` | How a mid-run `consult` escalates. Default when absent: `human-only`. |
| `**Blocker:**` | CSV of blocker slugs | Only on 🔵 blocked. Auto-unblock flips the task to ⚪ when every blocker is 🟢. |
| `**Next action:** / **Where I stopped:** / **Branch:**` | free text | Core resumability fields. |
| `**Weight:**` | `cheap-ok` \| `needs-claude` \| `needs-human` | Тир маршрутизации. **Обязателен** для авто-взятия. |
| `**Notify:**` | `<owner>/<repo>` | Инбокс-адрес для событий close/park/delivery-failure. |
| `**Requirements:**` | CSV (`needs-db, needs-secrets`) | Capability-гейт: агент должен держать ВСЕ. |
| `**Runtime allowed:**` | CSV (`claude-opus`) | Runtime-whitelist. |
| `**Consult policy:**` | `auto` \| `human-only` \| `strict-human` | Эскалация consult; default `human-only`. |
| `**Blocker:**` | CSV blocker-slug'ов | Только на 🔵; авто-unblock при 🟢 всех. |
| `**Next action:** / **Where I stopped:** / **Branch:**` | free text | Резюмируемость. |
`**Owner:** / **Claim token:** / **Claim expires at:**` are the **claim stamp** — the poller writes and clears them. Never author them by hand; a stale stamp on a ⚪ task blocks the poller.
`**Owner:** / **Claim token:** / **Claim expires at:**` — claim-штамп, пишет и
чистит поллер. Не автори руками; залипший штамп на ⚪ блокирует поллер.
## Weight — the field that decides pickup
**Weight — поле, решающее взятие:** без `**Weight:**` поллер берёт задачу, не
находит тир и паркует в 🔵 (`no backend for weight_tier: unknown`). Обычный код
`needs-claude`; критикал-инфра (поллер, MCP-серверы, деплой, CI, git-хуки) →
`needs-human` (никогда не авто).
The poller routes each claimed task to a backend by its weight tier:
- `cheap-ok` — routine work, a cheap/weak model is fine.
- `needs-claude` — needs a capable model (refactors, anything where discipline matters, review).
- `needs-human`**never** runs autonomously. The claim gate excludes it and the runner refuses to spawn. Use for anything touching critical infra: the poller/agent-runner itself, MCP servers, claim/close/heartbeat, deploy, CI/CD, git hooks.
**No `**Weight:**` line → no backend tier matches → the poller claims the task, finds no route, and parks it to 🔵 blocked (`no backend for weight_tier: unknown`).** So a task you want run **must** carry a Weight. If in doubt and the work is ordinary code, use `needs-claude`.
## Common mistakes (from baseline failures)
## Common mistakes
| Mistake | Fix |
|---|---|
| `### Title` or a `- **id:**` bullet list | Use the exact `## <emoji> [#n slug] — desc` h2 header + `**Field:**` lines. |
| 🟢 for a ready task | 🟢 is *done*. Ready is ⚪. |
| Header `[slug]` without a number | Header is `[#n slug]` — the number is the machine key. |
| `**Created:**` missing | Add `**Created:** yyyy-mm-dd` — mandatory field. |
| Inventing a number by hand | Numbers come only from `tasks_create` (counter). Never invent/reuse. |
| File named `2026-06-05-19-slug.md` (no leading zeros) | File is `yyyy-mm-dd-#####-slug.md`, number 5 digits: `00019`. |
| Inventing `risk: low`, `tier: L`, `priority`, `claimable-by` | The poller routes on `**Weight:**` with three fixed values only. |
| Notification written as prose / "Done-signal" | Use a real `**Notify:** <owner>/<repo>` field line. |
| Omitting Weight on a task you want auto-run | Always set Weight, or the task parks. |
| Hyphen or colon instead of `` in the header | The separator is space + em-dash + space. |
| Рукописный блок вместо `task_create` | Создавай через тул — номер/формат серверные. |
| `### Title` / буллеты вместо полей | `## <emoji> [#n slug] — desc` + `**Field:** value`. |
| 🟢 для ready | 🟢 — done. Ready — ⚪. |
| Шапка `[slug]` без номера | `[#n slug]` — номер машинный ключ. |
| Выдуманный номер | Номер — только от сервера (task_create). |
| `**Created:**` отсутствует (legacy) | Добавить — обязательное поле. |
| Ссылка `#<глобальный id>` | Ссылайся `[[t:N]]` (решение 20/#1037). |
| Без `**Weight:**` (legacy) | Ставь всегда, или задача паркуется. |
| Дефис/двоеточие вместо ` — ` в шапке | Разделитель — пробел + em-dash + пробел. |
## Verify
After editing, the block is correct when: header is `## <emoji> [#n slug] — …`, the emoji matches `**Status:**`, `**Created:** yyyy-mm-dd` is present, every machine-read field is a `**Label:**` line (not a bullet), and a task meant for the poller has both `**Weight:**` (not `needs-human` unless intended) and `**Notify:**`.
Задача корректна, когда: создана через `task_create` (или legacy-блок: шапка
`## <emoji> [#n slug] — …`, emoji = `**Status:**`, `**Created:**` есть, поля —
`**Label:**` строки, есть `**Weight:**` и `**Notify:**`); slug kebab-case;
ссылки на неё — `[[t:N]]`.