work_item: ORCH-016
title: "Единообразные коммент-артефакты в Plane от всех агентов"
revision: 2  # +TC-21..TC-25 по длительности (фидбэк стейкхолдера)
tests:

  - id: TC-01
    type: unit
    description: "build_status_comment(architect, duration_s=312, ...) формирует HTML c заголовком '📐 Architect — …', описанием стадии, строкой 'Длительность: 5m 12s' и ссылкой на 06-adr/. Строки Verdict нет."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-02
    type: unit
    description: "build_status_comment(developer, branch=..., pr_number=42, duration_s=...) включает ссылки на branch и на PR #42 через gitea_public_url + строку 'Длительность: ...'. Строки Verdict нет."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-03
    type: unit
    description: "build_status_comment(reviewer, duration_s=..., ...) при verdict=APPROVE в 12-review.md frontmatter выводит строку 'Verdict: APPROVE', строку 'Длительность: ...' и ссылку на 12-review.md."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-04
    type: unit
    description: "build_status_comment(reviewer, ...) при verdict=REQUEST_CHANGES выводит 'Verdict: REQUEST_CHANGES'. Строка длительности сохраняется."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-05
    type: unit
    description: "build_status_comment(reviewer, ...) при отсутствии файла 12-review.md публикует коммент без строки Verdict и без ссылки Review (graceful), при этом строка 'Длительность: ...' печатается, если duration_s передан."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-06
    type: unit
    description: "build_status_comment(tester, ...) при verdict=PASS в 13-test-report.md выводит 'Verdict: PASS', строку 'Длительность: ...' и ссылку на 13-test-report.md."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-07
    type: unit
    description: "build_status_comment(tester, ...) при verdict=FAIL выводит 'Verdict: FAIL'. Строка длительности сохраняется."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-08
    type: unit
    description: "build_status_comment(deployer, ...) при deploy_status=SUCCESS в 14-deploy-log.md выводит 'Status: SUCCESS', строку 'Длительность: ...' и ссылку на 14-deploy-log.md."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-09
    type: unit
    description: "build_status_comment(deployer, stage='deploy-staging') читает staging_status: из 15-staging-log.md и выводит соответствующую строку Status + строку длительности."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-10
    type: unit
    description: "URL ссылок строится через settings.gitea_public_url когда он задан; иначе — через settings.gitea_url (fallback)."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-11
    type: unit
    description: "Аналитик: _build_analyst_ready_comment (или его замена общим хелпером) сохраняет существующий контракт — текст про Approved/Rejected статус + список существующих BRD/ТЗ/AC/Test Plan ссылок. Дополнительно: при наличии завершённой строки agent_runs(analyst) для задачи коммент содержит строку 'Длительность: ...'."
    module: tests/test_analyst_comment_regression.py
    expected: PASS

  - id: TC-12
    type: unit
    description: "Парсер frontmatter (verdict / deploy_status / staging_status) возвращает None при отсутствии файла, пустом файле или некорректном YAML — без проброса исключения."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-13
    type: integration
    description: "_post_usage_comments(agent='reviewer', ...) вызывает plane_sync.add_comment ровно один раз; передаваемый текст содержит '🔎 Reviewer', 'Verdict:', 'Длительность:' и href на 12-review.md."
    module: tests/test_post_usage_comments_integration.py
    expected: PASS

  - id: TC-14
    type: integration
    description: "_post_usage_comments(agent='tester', ...) вызывает add_comment ровно один раз с автором 'tester' и корректным текстом, включая строку 'Длительность: ...'."
    module: tests/test_post_usage_comments_integration.py
    expected: PASS

  - id: TC-15
    type: integration
    description: "_post_usage_comments(agent='deployer', ...) для стадии deploy постит коммент со ссылкой на 14-deploy-log.md, строкой 'Длительность: ...' И task_summary_comment (если оно сохраняется) — поведение не регрессирует."
    module: tests/test_post_usage_comments_integration.py
    expected: PASS

  - id: TC-16
    type: integration
    description: "Регрессия status-only verdict model: при завершении analyst issue переводится в In Review, постится один коммент аналитика с инструкцией про статус Approved/Rejected, никакой автомат-advance не происходит."
    module: tests/test_analyst_status_only_regression.py
    expected: PASS

  - id: TC-17
    type: integration
    description: "Регрессия дедупликации: повторный вебхук Plane с тем же event_id не приводит ко второму status-комменту от агента."
    module: tests/test_status_comment_dedup_regression.py
    expected: PASS

  - id: TC-18
    type: integration
    description: "Регрессия set_issue_done / notify_done: финальный путь deploy→done по-прежнему переводит issue в Done и постит '✅ Task completed!' (отдельным комментом от status-коммента деплоера)."
    module: tests/test_notify_done_regression.py
    expected: PASS

  - id: TC-19
    type: integration
    description: "Per-agent bot-авторство: status-комменты архитектора/разработчика/ревьюера/тестера/деплоера POST-ятся под соответствующим X-API-Key (PLANE_BOT_TOKENS[role]); fallback на PLANE_HEADERS при отсутствии бот-токена."
    module: tests/test_status_comment_authorship.py
    expected: PASS

  - id: TC-20
    type: unit
    description: "Quality Gates не изменены: реестр QG_CHECKS и STAGE_TRANSITIONS идентичны контрольному снапшоту (smoke-тест против случайных правок)."
    module: tests/test_qg_registry_snapshot.py
    expected: PASS

  - id: TC-21
    type: unit
    description: "fmt_duration(seconds) — табличная проверка форматирования: 0→'0s', 12→'12s', 59→'59s', 60→'1m 00s', 252→'4m 12s', 3599→'59m 59s', 3600→'1h 00m', 3780→'1h 03m', 10020→'2h 47m'."
    module: tests/test_fmt_duration.py
    expected: PASS

  - id: TC-22
    type: unit
    description: "fmt_duration(None) и fmt_duration(-1) возвращают пустую строку (или None); вызывающая сторона при этом строку 'Длительность:' НЕ печатает."
    module: tests/test_fmt_duration.py
    expected: PASS

  - id: TC-23
    type: unit
    description: "build_status_comment(architect, duration_s=None) и build_status_comment(architect) — коммент НЕ содержит строки 'Длительность:'; остальные строки (заголовок/описание/ссылки) на месте."
    module: tests/test_status_comment_format.py
    expected: PASS

  - id: TC-24
    type: integration
    description: "Fallback по БД: при отсутствии явного duration_s билдер коммента читает agent_runs.started_at/finished_at для последней завершённой строки (task_id, agent) и подставляет fmt_duration результата. Проверка через тестовую SQLite с заранее проставленными timestamp'ами."
    module: tests/test_status_comment_duration_db_fallback.py
    expected: PASS

  - id: TC-25
    type: integration
    description: "Регрессия: исключение при чтении agent_runs (например, БД залочена) → строка 'Длительность:' опускается, остальное публикуется; logger.debug содержит запись о неудачном чтении длительности."
    module: tests/test_status_comment_duration_db_fallback.py
    expected: PASS
