175 lines
16 KiB
Markdown
175 lines
16 KiB
Markdown
# ТЗ: Единообразные коммент-артефакты в Plane от всех агентов
|
||
|
||
Work Item ID: **ORCH-016**
|
||
Стадия: analysis → architecture → development
|
||
Автор: analyst
|
||
Дата: 2026-06-05
|
||
Ревизия: 2 (по фидбэку стейкхолдера — добавлен §2.5 Duration; обновлены §1, §2.1, §6)
|
||
|
||
> Контракт: что именно меняем в коде / какие модули задействованы / какие проверки появятся.
|
||
> Архитектурные решения принимает архитектор; здесь — границы изменения.
|
||
|
||
---
|
||
|
||
## 1. Задействованные модули
|
||
|
||
| Модуль | Роль в изменении |
|
||
|--------|------------------|
|
||
| `src/usage.py` | **Главная точка изменения.** Здесь сейчас живут `usage_comment()`, `artifact_links()`, `AGENT_ARTIFACT`, `AGENT_DISPLAY`, `AGENT_ICON` — основа форматирования. Нужно расширить/добавить хелпер построения единого status-коммента + утилитку форматирования длительности (`fmt_duration(seconds: int) -> str`). |
|
||
| `src/stage_engine.py` | Эталонная функция аналитика `_build_analyst_ready_comment()`. По возможности — переиспользовать новый общий хелпер (или хотя бы выровнять формат: emoji + заголовок + описание + список ссылок). К аналитику также прикручиваем строку длительности (см. §2.5). |
|
||
| `src/agents/launcher.py` | `_post_usage_comments()` — точка, где постится коммент по завершении агента (architect/developer/reviewer/tester/deployer). Должен звать новый хелпер. `_duration_s` уже считается на строке `391` — пробросить его (или достать из `agent_runs.started_at`/`finished_at`) в хелпер. |
|
||
| `src/db.py` | **Только для чтения** в рантайме коммент-хелпера: `agent_runs.started_at`, `agent_runs.finished_at` (уже существуют). Никаких ALTER. |
|
||
| `src/plane_sync.py` | `add_comment()` — без изменений (используется как транспорт). |
|
||
| `src/qg/checks.py` | **Только для чтения**: модели парсинга frontmatter `verdict:` / `deploy_status:` / `staging_status:` — переиспользуем эту логику (вынести в отдельную утилитку, либо импортировать там, где она уже есть). |
|
||
| `src/config.py` | `settings.gitea_public_url`, `settings.gitea_owner`, `settings.gitea_url` — без изменений, переиспользуются. |
|
||
|
||
## 2. Контракт нового коммент-формата
|
||
|
||
### 2.1 Структура (одинакова для всех агентов)
|
||
```
|
||
{ICON} {RoleName} — {one-line human description of stage result}
|
||
|
||
[Verdict / Status: <VALUE>] # опционально, см. 2.3
|
||
Длительность: <human-format> # см. 2.5; опускается, только если значение неизвестно
|
||
<b>Документы:</b>
|
||
• <a href="…">{label}</a> # одна или несколько ссылок
|
||
```
|
||
|
||
Поля:
|
||
- `{ICON}` — берётся из `AGENT_ICON` (уже есть в `usage.py`).
|
||
- `{RoleName}` — из `AGENT_DISPLAY` (уже есть).
|
||
- `{description}` — фиксированная строка на роль, см. 2.2.
|
||
- Verdict / Status — см. 2.3, опускается если не извлекается.
|
||
- Длительность — см. 2.5, печатается всегда, когда значение есть; по умолчанию доступна (это нативная метрика `agent_runs`).
|
||
- Ссылки — см. 2.4.
|
||
|
||
### 2.2 Описания стадий (per-agent text)
|
||
|
||
| Агент | Описание (рус.) |
|
||
|-------|------------------|
|
||
| analyst | «Подготовил BRD / ТЗ / Acceptance Criteria. Для продвижения переведите задачу в статус Approved.» (как сейчас в `_build_analyst_ready_comment`) |
|
||
| architect | «Завершил архитектурную проработку. См. ADR ниже.» |
|
||
| developer | «Завершил разработку. См. PR / branch ниже.» |
|
||
| reviewer | «Завершил ревью изменений.» |
|
||
| tester | «Завершил прогон тестов.» |
|
||
| deployer | «Завершил деплой.» |
|
||
|
||
Точные формулировки финализирует architect; аналитик фиксирует **факт** наличия 1-предложного описания на каждую роль.
|
||
|
||
### 2.3 Verdict / Status строка
|
||
|
||
Печатается отдельной строкой над списком документов. Источник — frontmatter артефакта; парсить идемпотентно (если файл недоступен — строку пропустить):
|
||
|
||
| Агент | Поле | Где парсим | Возможные значения | Формат строки |
|
||
|-------|------|------------|---------------------|----------------|
|
||
| analyst | — | — | — | не печатается |
|
||
| architect | — | — | — | не печатается |
|
||
| developer | — | — | — | не печатается (CI-статус — отдельный гейт) |
|
||
| reviewer | `verdict:` | `docs/work-items/<wid>/12-review.md` (YAML-frontmatter) | `APPROVE` / `REQUEST_CHANGES` | `Verdict: APPROVE` |
|
||
| tester | `verdict:` (или эквивалентный фронт-кей) | `docs/work-items/<wid>/13-test-report.md` | `PASS` / `FAIL` | `Verdict: PASS` |
|
||
| deployer | `staging_status:` (для deploy-staging) / `deploy_status:` (для deploy) | `15-staging-log.md` / `14-deploy-log.md` | `SUCCESS` / `FAILED` | `Status: SUCCESS` |
|
||
|
||
Если значение в frontmatter отсутствует или не распознано → строка `Verdict / Status` НЕ выводится (вердикт-парсинг гейтов и сама логика гейтов не меняется).
|
||
|
||
### 2.4 Ссылки на артефакты
|
||
|
||
Базовый URL: `(settings.gitea_public_url or settings.gitea_url).rstrip('/')`.
|
||
Префикс: `/{owner}/{repo}/src/branch/{branch}/`.
|
||
|
||
| Агент | Артефакты (label → путь) |
|
||
|-------|----------------------------|
|
||
| analyst | BRD `01-brd.md`, ТЗ `02-trz.md`, AC `03-acceptance-criteria.md`, Test Plan `04-test-plan.yaml` *(уже есть)* |
|
||
| architect | ADR-папка `docs/work-items/<wid>/06-adr/` *(уже есть)* |
|
||
| developer | Branch `…/src/branch/<branch>`, PR `…/pulls/<num>` *(уже есть)* |
|
||
| reviewer | Review `docs/work-items/<wid>/12-review.md` *(уже есть)* |
|
||
| tester | Test report `docs/work-items/<wid>/13-test-report.md` *(уже есть)* |
|
||
| deployer | Deploy log `docs/work-items/<wid>/14-deploy-log.md`; staging-лог `15-staging-log.md` (если применимо к стадии) |
|
||
|
||
Несуществующий файл в worktree → ссылка опускается (как сейчас в `_build_analyst_ready_comment`).
|
||
|
||
### 2.5 Строка длительности работы агента
|
||
|
||
**Что печатаем:** одну строку вида `Длительность: {human}` (или `Duration: {human}` — финальную локализацию метки фиксирует архитектор; русский предпочтителен, остальные комменты уже на русском).
|
||
|
||
**Источник значения (приоритет сверху вниз):**
|
||
|
||
1. **Параметр функции** — `_post_usage_comments()` в `src/agents/launcher.py:682` вызывается из контекста, где `_duration_s` уже посчитан на строке `391` (`int(time.time() - _start_ts)`). Простейший путь — пробросить `duration_s` явным аргументом в `usage_comment(...)` / новый `build_status_comment(...)`.
|
||
2. **Fallback из БД** — если параметр не передан (например, для аналитика, чей коммент строится в `_build_analyst_ready_comment` в `src/stage_engine.py:298`), читаем
|
||
```sql
|
||
SELECT
|
||
CAST((julianday(finished_at) - julianday(started_at)) * 86400 AS INTEGER)
|
||
FROM agent_runs
|
||
WHERE task_id = ? AND agent = ?
|
||
ORDER BY id DESC LIMIT 1
|
||
```
|
||
Это последний завершённый run этой роли по задаче.
|
||
3. **Если оба источника пусты / `None` / отрицательны** — строка `Длительность:` НЕ печатается (graceful, как и для вердикта).
|
||
|
||
**Форматирование (`fmt_duration(seconds: int) -> str` в `src/usage.py`):**
|
||
|
||
| Диапазон | Формат | Пример |
|
||
|----------|--------|--------|
|
||
| `0 ≤ s < 60` | `{s}s` | `12s`, `45s` |
|
||
| `60 ≤ s < 3600` | `{m}m {ss}s` | `4m 12s`, `1m 03s` |
|
||
| `s ≥ 3600` | `{h}h {mm}m` (секунды отбрасываем) | `1h 03m`, `2h 47m` |
|
||
|
||
Округление: целые секунды (input — `int`). При `s == 0` всё равно печатаем `0s` (видно, что метрика известна и стадия отработала почти мгновенно).
|
||
|
||
**Покрытие ролей:** строка длительности добавляется для **всех** агентов, включая аналитика. Для аналитика — строго через fallback из `agent_runs` (его коммент строится в `stage_engine.py`, не в `launcher.py`).
|
||
|
||
**Что НЕ делаем:**
|
||
- Не меняем схему `agent_runs` (поля `started_at` / `finished_at` уже есть, `_duration_s` уже считается).
|
||
- Не изобретаем новый отдельный коммент с длительностью — длительность встраивается в существующий status-коммент.
|
||
- Не считаем «время от первого вебхука до коммента» — берём чистое время процесса агента (тот же `_duration_s`, что попадает в `notify_agent_finished`), чтобы значение совпадало с тем, что уже видно в Telegram live tracker / логах.
|
||
|
||
### 2.6 Один коммент на агента за стадию
|
||
Текущий триггер — `_post_usage_comments()` вызывается **один раз** в успешном auto-advance пути после агента. Никаких новых триггеров не добавляем. Дубликаты исключены текущей логикой (одно завершение агента → один коммент).
|
||
|
||
### 2.7 Usage-метрики (токены / стоимость)
|
||
Текущий `usage_comment()` встраивает «8.5M in / 45.8k out · $7.29» в первый строкой. По требованиям Славы это «без раздувания», но не запрещено явно. Решение:
|
||
- **Сохранить** usage-метрику как **последнюю строку** коммента (мелким техническим хвостом, например `<sub>8.5M in / 45.8k out · $7.29 · Длительность: 4m 12s</sub>`), либо
|
||
- **Перенести** в `task_summary_comment` (только для финального deployer-summary).
|
||
|
||
Финальный выбор — за архитектором (см. вопрос Q-1 в `10-tech-risks.md`). Длительность из §2.5 — **отдельная** строка от usage-метрики и присутствует независимо от того, как решится вопрос про токены/стоимость.
|
||
|
||
### 2.8 Бот-авторство
|
||
`plane_add_comment(..., author=<role>)` — сохраняется. Все агенты комментируют под своим bot-токеном (`PLANE_BOT_TOKENS`). Изменения формата текста на это не влияют.
|
||
|
||
## 3. Изменения API
|
||
**Нет.** Внешние webhooks (`/webhook/plane`, `/webhook/gitea`), `/health`, `/status`, `/queue` — не меняются.
|
||
|
||
## 4. Изменения схемы БД
|
||
**Нет.** Используются существующие таблицы `tasks`, `agent_runs`, `jobs`.
|
||
|
||
## 5. Новые Quality Gate checks
|
||
**Нет.** Гейты не меняются. Парсинг `verdict:` / `deploy_status:` / `staging_status:` в коммент — отдельная утилитка, не QG.
|
||
|
||
## 6. Требования к коду
|
||
- Все новые функции — с docstring (зачем нужны, какие инварианты сохраняют).
|
||
- Парсинг frontmatter артефакта — graceful: исключение → строка вердикта опускается, лог в `logger.debug`.
|
||
- Чтение длительности — graceful: исключение или `None` → строка длительности опускается, лог в `logger.debug`. Отрицательные / нулевые значения: `0` печатается как `0s`, отрицательные опускаются.
|
||
- `fmt_duration(seconds: int) -> str` — чистая, без БД-зависимостей, легко тестируется юнитом.
|
||
- Никаких новых внешних зависимостей: использовать `pyyaml` (уже в проекте) или существующий парсер frontmatter из `src/qg/checks.py`.
|
||
- Поведение для проектов **без** артефактов (например, ENDURO-* до запуска агента) — graceful no-op: коммент с описанием и без ссылок (минимум — заголовок).
|
||
- HTML (как у аналитика) предпочтительнее markdown — Plane корректно рендерит `<ul><li><a>` и `<b>`.
|
||
|
||
## 7. Артефакты по pipeline
|
||
- `06-adr/` — **не требуется** (нет архитектурного сдвига; обсуждается локально архитектором, в случае спорного решения по 2.6 — заводим ADR `ADR-001-status-comment-format.md`).
|
||
- `07-infra-requirements.md` — **не требуется** (нет новой инфраструктуры).
|
||
- `08-data-requirements.md` — **не требуется** (БД не меняется).
|
||
- `12-review.md` / `13-test-report.md` / `14-deploy-log.md` — формируются на соответствующих стадиях по канону.
|
||
- `CHANGELOG.md` — обновить в том же PR (раздел `Unreleased`).
|
||
|
||
## 8. Документация
|
||
В том же PR обновить:
|
||
- `docs/architecture/README.md` — короткое упоминание единого формата комментов (можно в раздел «Plane Sync»).
|
||
- `docs/architecture/internals.md` — если там есть раздел про `usage.py`/комменты — обновить.
|
||
- `CLAUDE.md` — без изменений (правила не меняются).
|
||
|
||
## 9. Чего НЕ делать
|
||
- Не менять реестр `QG_CHECKS`.
|
||
- Не менять `STAGE_TRANSITIONS`.
|
||
- Не менять `add_comment` / `_headers_for` / `PLANE_BOT_TOKENS`.
|
||
- Не «комментировать» комменты других стадий задним числом.
|
||
- Не использовать `--no-verify` при коммитах.
|