Все агенты (analyst..deployer) теперь пишут финальный коммент через единый
хелпер usage.build_status_comment(...) — заголовок «{icon} {Role} — {описание}»,
опциональная строка Verdict/Status из YAML-frontmatter, строка
«Длительность: 4m 12s» (явный duration_s от launcher, fallback из agent_runs
для аналитика), HTML-блок Документы, тех-хвост <sub>tokens · cost</sub>.
- Новые публичные функции в src/usage.py: build_status_comment, fmt_duration,
get_agent_duration. usage_comment(...) → тонкая deprecated-обёртка (legacy
тесты в tests/test_usage.py продолжают работать). artifact_links(...)
переписан на HTML <li><a>…</a></li> (breaking change для внутреннего API,
но единственный внешний клиент — _post_usage_comments — мигрирован).
- Новый модуль src/frontmatter.py: defensive YAML reader, никогда не raise.
- stage_engine._build_analyst_ready_comment(...) теперь тонкая обёртка над
build_status_comment(agent="analyst", ...); task_id пробрасывается из
_handle_analysis_approved_flow для DB-фоллбэка длительности (AC-14).
- launcher._post_usage_comments(...) принимает duration_s, резолвит stage из
tasks для deployer и worktree_root для AC-8 graceful skipping.
Тесты (16 файлов, 56 новых тестовых функций, покрывают TC-01..TC-25):
fmt_duration table, build_status_comment по всем агентам, DB-фоллбэк,
authorship под per-agent ботами, дедуп-инвариант, regression на
status-only verdict аналитика и финальный notify_done, snapshot
QG_CHECKS + STAGE_TRANSITIONS.
Документация: docs/architecture/README.md (раздел Plane Sync),
CHANGELOG.md (Unreleased Added/Changed).
Refs: ORCH-016
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
2.4 KiB
Python
69 lines
2.4 KiB
Python
"""ORCH-016 / AC-13 + AC-22: fmt_duration formatting contract.
|
|
|
|
Pure-function tests for the duration formatter used by build_status_comment.
|
|
No DB, no I/O — just the table in ADR-001 §8 / AC-13.
|
|
"""
|
|
|
|
import os
|
|
|
|
os.environ.setdefault("ORCH_PLANE_API_TOKEN", "test-token")
|
|
os.environ.setdefault("ORCH_GITEA_TOKEN", "test-token")
|
|
|
|
from src.usage import fmt_duration # noqa: E402
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TC-21: table-driven happy path (AC-13)
|
|
# ---------------------------------------------------------------------------
|
|
def test_fmt_duration_boundary_table():
|
|
cases = [
|
|
(0, "0s"),
|
|
(12, "12s"),
|
|
(59, "59s"),
|
|
(60, "1m 00s"),
|
|
(252, "4m 12s"),
|
|
(3599, "59m 59s"),
|
|
(3600, "1h 00m"),
|
|
(3780, "1h 03m"),
|
|
(10020, "2h 47m"),
|
|
]
|
|
for seconds, expected in cases:
|
|
assert fmt_duration(seconds) == expected, (
|
|
f"fmt_duration({seconds}) -> {fmt_duration(seconds)!r}; expected {expected!r}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TC-22: None / negative -> empty string (caller drops the line) (AC-13)
|
|
# ---------------------------------------------------------------------------
|
|
def test_fmt_duration_none_returns_empty():
|
|
assert fmt_duration(None) == ""
|
|
|
|
|
|
def test_fmt_duration_negative_returns_empty():
|
|
assert fmt_duration(-1) == ""
|
|
assert fmt_duration(-3600) == ""
|
|
|
|
|
|
def test_fmt_duration_garbage_returns_empty():
|
|
# Non-coercible input must not raise (defensive).
|
|
assert fmt_duration("abc") == ""
|
|
assert fmt_duration([1, 2]) == ""
|
|
|
|
|
|
def test_fmt_duration_float_seconds_truncated():
|
|
# int(12.9) == 12 — integer truncation, not rounding.
|
|
assert fmt_duration(12.9) == "12s"
|
|
assert fmt_duration(61.4) == "1m 01s"
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Caller contract: empty string => the 'Длительность:' line is NOT printed.
|
|
# build_status_comment is unit-tested in test_status_comment_format; here we
|
|
# just sanity-check the helper used to gate that decision.
|
|
# ---------------------------------------------------------------------------
|
|
def test_empty_string_is_falsy():
|
|
assert not fmt_duration(None)
|
|
assert not fmt_duration(-5)
|
|
assert fmt_duration(0) # "0s" IS truthy: AC-13 wants the line printed
|