c674db2b2f
Оценка времени работы над страницей как сессионизация истории page_history по паузам бездействия (WakaTime-подобно), а не span между крайними правками, плюс drill-down в суточный таймлайн 24ч×дни. Зависит от #374 (kind + idle-пульс). Server: - Чистая computeWorkTime(rows, config) (§5): нормализация+дедуп → коллапс агентских всплесков по aiChatId → ОДИН проход сессионизации (порог зависит от пары: оба agent → agentTGap, иначе tGap; последняя сессия обязательно закрывается) → класс готовой сессии (все agent → agent_only, иначе work) → добивка (много-сэмпл → [first−P_in, last+P_out], одиночный скаляр → [t−P_single, t]) → метрики = union wall-clock по классу. Детерминированная, без БД. - Чистая bucketByDay(sessions, tz) (§6.3): union work/agent_only РАЗДЕЛЬНО, разрез по полуночи tz через нативный Intl (DST-корректно, без «+24ч»). Инвариант Σ activeMs == workMs держится по построению. - PageHistoryRepo.findTimelineByPageId — лёгкая проекция без content, ASC. - PageHistoryService.computeWorkTime + POST /pages/history/time, гейт validateCanView как у /history; атрибуция человек/агент по lastUpdatedSource. Client: - usePageWorkTime(pageId) шлёт tz зрителя (Intl локаль). - Кликабельное число «≈ 4 ч 30 мин» в шапке страницы (порог в тултипе). - Модалка-punch-card: 24-часовые дорожки по дням, окна work/agent разным цветом, одиночные (P_single) приглушены, сумма за день, пустой день «—», сворачивание длинных серий пустых дней, подпись tz + T_gap. i18n ru-RU + en-US. Tests: 22 юнита на computeWorkTime/bucketByDay (§7-фикстура ≈1ч32м vs ≈60h наив, закрытие последней сессии, коллапс/разрыв всплеска, idle-пульс, DST 23/25ч, полуночный разрез, инвариант, union-без-задвоения); 5 юнитов гейта контроллера; int-spec на реальном pg (проекция + совпадение с ядром); 6 клиентских на формат. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>