// Fixture data for WorkflowTools-Watchdog dashboard.
// All times are anchored to a synthetic "now" so the UI feels alive.

const NOW = new Date("2026-05-17T14:35:00Z");

// ---------------------------------------------------------------------------
// Team — 6 agents
// ---------------------------------------------------------------------------
const TEAM = [
  {
    id: "lev",
    name: "Лев",
    role: "architect",
    roleLabel: "Architect Lead",
    avatar: "avatars/lev.jpg",
    isLead: true,
    email: "lev@multi-print.local",
    planeUserId: "plane_3f1a",
    envKey: "PLANE_API_KEY_LEV",
    watcherOnline: true,
    watcherPid: 19847,
    watcherUptimeMin: 412,
    lastHeartbeatMin: 0.3,
    currentTask: { seq: 51, title: "Описать модуль уведомлений в KALK" },
    contextPct: 78,
    avgToolCalls: 14,
    health: "ok",
    modulesCurrent: ["kalk", "notifications", "spec"],
    modulesHistorical: [
      { module: "orders", lastSeen: "2026-05-16" },
      { module: "seo", lastSeen: "2026-05-14" },
      { module: "auth", lastSeen: "2026-05-12" },
      { module: "tg-bot", lastSeen: "2026-05-10" },
    ],
    enabled: true,
    contextHistory: [82, 80, 78, 90, 88, 85, 82, 79, 76, 74, 80, 78],
    todayLoad: [0.2, 0.3, 0.4, 0.6, 0.8, 0.9, 0.7, 0.5, 0.4, 0.3, 0.2, 0.1],
  },
  {
    id: "kostya",
    name: "Костя",
    role: "coder",
    roleLabel: "Coder",
    avatar: "avatars/kostya.jpg",
    isLead: false,
    email: "kostya@multi-print.local",
    planeUserId: "plane_4b22",
    envKey: "PLANE_API_KEY_KOSTYA",
    watcherOnline: true,
    watcherPid: 19850,
    watcherUptimeMin: 412,
    lastHeartbeatMin: 0.8,
    currentTask: { seq: 49, title: "Ребрендинг Print-X в коде сайта" },
    contextPct: 42,
    avgToolCalls: 28,
    health: "warn",
    modulesCurrent: ["next.js", "fastapi", "kalk"],
    modulesHistorical: [
      { module: "seo", lastSeen: "2026-05-17" },
      { module: "orders", lastSeen: "2026-05-16" },
      { module: "kalk", lastSeen: "2026-05-15" },
      { module: "tg-bot", lastSeen: "2026-05-14" },
    ],
    enabled: true,
    contextHistory: [95, 90, 85, 78, 72, 68, 64, 60, 55, 50, 46, 42],
    todayLoad: [0.4, 0.5, 0.6, 0.8, 0.9, 1.0, 0.95, 0.9, 0.85, 0.8, 0.75, 0.7],
  },
  {
    id: "roma",
    name: "Рома",
    role: "reviewer",
    roleLabel: "Reviewer",
    avatar: "avatars/roma.jpg",
    isLead: false,
    email: "roma@multi-print.local",
    planeUserId: "plane_5c83",
    envKey: "PLANE_API_KEY_ROMA",
    watcherOnline: true,
    watcherPid: 19853,
    watcherUptimeMin: 412,
    lastHeartbeatMin: 0.4,
    currentTask: { seq: 46, title: "Code review: миграция БД на v3" },
    contextPct: 65,
    avgToolCalls: 18,
    health: "ok",
    modulesCurrent: ["db", "migrations", "fastapi"],
    modulesHistorical: [
      { module: "next.js", lastSeen: "2026-05-17" },
      { module: "kalk", lastSeen: "2026-05-16" },
      { module: "tg-bot", lastSeen: "2026-05-15" },
    ],
    enabled: true,
    contextHistory: [80, 78, 74, 72, 70, 68, 66, 65, 64, 63, 64, 65],
    todayLoad: [0.1, 0.2, 0.3, 0.5, 0.7, 0.6, 0.5, 0.4, 0.5, 0.6, 0.5, 0.4],
  },
  {
    id: "timur",
    name: "Тимур",
    role: "tester",
    roleLabel: "Tester",
    avatar: "avatars/timur.jpg",
    isLead: false,
    email: "timur@multi-print.local",
    planeUserId: "plane_6d14",
    envKey: "PLANE_API_KEY_TIMUR",
    watcherOnline: true,
    watcherPid: 19856,
    watcherUptimeMin: 412,
    lastHeartbeatMin: 0.5,
    currentTask: null,
    contextPct: 88,
    avgToolCalls: 12,
    health: "ok",
    modulesCurrent: ["e2e", "playwright"],
    modulesHistorical: [
      { module: "orders", lastSeen: "2026-05-17" },
      { module: "kalk", lastSeen: "2026-05-15" },
      { module: "auth", lastSeen: "2026-05-13" },
    ],
    enabled: true,
    contextHistory: [90, 90, 88, 88, 90, 92, 90, 88, 88, 86, 87, 88],
    todayLoad: [0.0, 0.1, 0.2, 0.4, 0.5, 0.3, 0.2, 0.1, 0.2, 0.2, 0.1, 0.0],
  },
  {
    id: "semyon",
    name: "Семён",
    role: "scout",
    roleLabel: "Scout",
    avatar: "avatars/semyon.jpg",
    isLead: false,
    email: "semyon@multi-print.local",
    planeUserId: "plane_7e95",
    envKey: "PLANE_API_KEY_SEMYON",
    watcherOnline: false,
    watcherPid: null,
    watcherUptimeMin: 0,
    lastHeartbeatMin: 47,
    currentTask: { seq: 50, title: "Подобрать референсы для каталога A4" },
    contextPct: 18,
    avgToolCalls: 22,
    health: "crit",
    modulesCurrent: ["research", "seo"],
    modulesHistorical: [
      { module: "tg-bot", lastSeen: "2026-05-16" },
      { module: "seo", lastSeen: "2026-05-15" },
      { module: "content", lastSeen: "2026-05-13" },
    ],
    enabled: true,
    contextHistory: [70, 65, 60, 55, 50, 45, 40, 35, 30, 25, 22, 18],
    todayLoad: [0.5, 0.6, 0.7, 0.8, 0.6, 0.4, 0.2, 0.1, 0.0, 0.0, 0.0, 0.0],
  },
  {
    id: "viktor",
    name: "Виктор",
    role: "workflow-lead",
    roleLabel: "Workflow Lead",
    avatar: "avatars/viktor.jpg",
    isLead: true,
    email: "viktor@multi-print.local",
    planeUserId: "plane_8f06",
    envKey: "PLANE_API_KEY_VIKTOR",
    watcherOnline: true,
    watcherPid: 19862,
    watcherUptimeMin: 412,
    lastHeartbeatMin: 0.2,
    currentTask: { seq: 52, title: "Разбор алертов за смену" },
    contextPct: 72,
    avgToolCalls: 16,
    health: "ok",
    modulesCurrent: ["watchdog", "rules", "spec"],
    modulesHistorical: [
      { module: "kalk", lastSeen: "2026-05-16" },
      { module: "orders", lastSeen: "2026-05-15" },
      { module: "auth", lastSeen: "2026-05-12" },
    ],
    enabled: true,
    contextHistory: [85, 82, 80, 78, 76, 74, 73, 72, 71, 71, 72, 72],
    todayLoad: [0.3, 0.4, 0.5, 0.6, 0.7, 0.5, 0.6, 0.7, 0.6, 0.5, 0.4, 0.5],
  },
];

function teamById(id) { return TEAM.find(t => t.id === id); }

// ---------------------------------------------------------------------------
// Alerts
// ---------------------------------------------------------------------------
const ALERTS = [
  {
    id: 17,
    ts: "2026-05-17T14:23:01Z",
    seq: 49,
    title: "Ребрендинг Print-X в коде сайта",
    rule: "coder_claim_within_5min",
    ruleTitle: "Coder не подобрал задачу",
    severity: "warning",
    agent: "kostya",
    text: "Костя не подобрал PB-49 за 12 мин после установки лейбла for-coder.",
    status: "active",
    durationMin: 12,
    claudeMdRef: "agents/coder/CLAUDE.md § 3.1 — Подбор задач",
    claudeMdQuote: "Если задача помечена `for-coder` и её state = Todo, ты обязан забрать её (assign на себя + перевести в In Progress) в течение 5 минут. Если занят — оставь комментарий 'busy, eta NN min'.",
    promptDraft: "Привет. Костя не подобрал PB-49 «Ребрендинг Print-X» за 12 мин после установки лейбла for-coder. Watcher Кости онлайн, heartbeat 0.8s назад, контекст 42%. Проверь, не висит ли он в текущей задаче, и реши: пинговать или переназначить.",
  },
  {
    id: 16,
    ts: "2026-05-17T14:08:11Z",
    seq: 50,
    title: "Подобрать референсы для каталога A4",
    rule: "watcher_heartbeat_missing",
    ruleTitle: "Watcher не отвечает",
    severity: "critical",
    agent: "semyon",
    text: "Watcher Семёна не отвечает 47 минут. Последний heartbeat: 13:48 UTC.",
    status: "active",
    durationMin: 47,
    claudeMdRef: "agents/scout/CLAUDE.md § 1.2 — Жизненный цикл сессии",
    claudeMdQuote: "Сессия scout-агента должна работать непрерывно, отправляя heartbeat в watcher раз в 30 секунд. Если сессия завершилась — перезапусти её через `wt agent restart scout`.",
    promptDraft: "Семён — watcher offline 47 мин (последний heartbeat 13:48). Контекст был 18%, держал PB-50. Похоже на context-exhaustion. Перезапусти сессию и подними задачу с того же места.",
  },
  {
    id: 15,
    ts: "2026-05-17T13:51:44Z",
    seq: 47,
    title: "Аналитика заказов: фильтр по статусу",
    rule: "stuck_in_progress_3h",
    ruleTitle: "Задача висит в In Progress",
    severity: "warning",
    agent: "kostya",
    text: "PB-47 в статусе In Progress 3ч 12мин без новых коммитов.",
    status: "active",
    durationMin: 192,
    claudeMdRef: "agents/coder/CLAUDE.md § 4.3 — Прогресс по задаче",
    claudeMdQuote: "Если задача в In Progress дольше 2 часов без коммита — оставь промежуточный коммент `progress: <что сделано>, <что осталось>`. Если застрял — переведи в Blocked.",
    promptDraft: "PB-47 у Кости висит 3ч без коммитов. Узнай, не застрял ли он на интеграции с FastAPI. Если да — попроси перевести в Blocked и описать что нужно от Лёва.",
  },
  {
    id: 14,
    ts: "2026-05-17T12:14:02Z",
    seq: 46,
    title: "Code review: миграция БД на v3",
    rule: "reviewer_pickup_within_1h",
    ruleTitle: "Reviewer не дошёл до задачи",
    severity: "info",
    agent: "roma",
    text: "Лейбл for-reviewer установлен в 11:14, Рома подобрал в 12:14 (на грани).",
    status: "resolved",
    durationMin: 60,
    claudeMdRef: "agents/reviewer/CLAUDE.md § 2.1 — SLA на ревью",
    claudeMdQuote: "Задачу с лейблом for-reviewer необходимо взять в ревью в течение 1 часа от установки лейбла.",
    promptDraft: "Информационно: Рома взял PB-46 на ревью точно через 60 мин — на грани SLA. Если повторится, надо обсудить нагрузку.",
  },
  {
    id: 13,
    ts: "2026-05-17T10:42:18Z",
    seq: 44,
    title: "Telegram-бот: подсчёт стоимости",
    rule: "labels_conflict",
    ruleTitle: "Конфликт лейблов",
    severity: "warning",
    agent: null,
    text: "На PB-44 одновременно for-coder и for-reviewer. Архитектурная ошибка.",
    status: "dismissed",
    durationMin: 7,
    claudeMdRef: "agents/architect/CLAUDE.md § 5 — Лейблы",
    claudeMdQuote: "В любой момент на задаче должен быть ровно один лейбл `for-<role>`. Перед сменой роли убери предыдущий лейбл.",
    promptDraft: "PB-44 имела конфликт лейблов 7 мин (for-coder + for-reviewer). Кто-то проставил оба разом. Проверь логи.",
  },
];

// ---------------------------------------------------------------------------
// Events stream
// ---------------------------------------------------------------------------
const EVENTS = [
  { ts: "2026-05-17T14:31:22Z", seq: 51, title: "Описать модуль уведомлений в KALK", kind: "new_task",       agent: "lev",     text: "Лев создал PB-51 «Описать модуль уведомлений в KALK»" },
  { ts: "2026-05-17T14:28:04Z", seq: 49, title: "Ребрендинг Print-X в коде сайта",     kind: "comment",        agent: "kostya", text: "Костя оставил коммент: progress: правлю Footer.tsx, осталось header" },
  { ts: "2026-05-17T14:23:01Z", seq: 49, title: "Ребрендинг Print-X в коде сайта",     kind: "alert",           agent: "kostya", text: "Сработало правило coder_claim_within_5min" },
  { ts: "2026-05-17T14:20:33Z", seq: 49, title: "Ребрендинг Print-X в коде сайта",     kind: "state_changed",   agent: "kostya", text: "Костя перевёл PB-49: Todo → In Progress" },
  { ts: "2026-05-17T14:18:11Z", seq: 49, title: "Ребрендинг Print-X в коде сайта",     kind: "assignees_changed", agent: "kostya", text: "Костя взял PB-49 в работу" },
  { ts: "2026-05-17T14:14:50Z", seq: 49, title: "Ребрендинг Print-X в коде сайта",     kind: "labels_changed",  agent: "lev",     text: "Лев поставил лейбл for-coder на PB-49" },
  { ts: "2026-05-17T14:08:11Z", seq: 50, title: "Подобрать референсы для каталога A4", kind: "alert",           agent: "semyon", text: "Critical: watcher Семёна не отвечает 47 мин" },
  { ts: "2026-05-17T13:58:02Z", seq: 46, title: "Code review: миграция БД на v3",     kind: "state_changed",   agent: "roma",   text: "Рома approved PB-46: In Review → Done" },
  { ts: "2026-05-17T13:51:44Z", seq: 47, title: "Аналитика заказов: фильтр по статусу", kind: "alert",         agent: "kostya", text: "Сработало правило stuck_in_progress_3h" },
  { ts: "2026-05-17T13:45:12Z", seq: 52, title: "Разбор алертов за смену",             kind: "state_changed",   agent: "viktor", text: "Виктор перевёл PB-52: Todo → In Progress" },
  { ts: "2026-05-17T13:31:08Z", seq: 48, title: "SEO: meta-теги для каталога",         kind: "state_changed",   agent: "roma",   text: "Рома approved PB-48: In Review → Done" },
  { ts: "2026-05-17T13:14:33Z", seq: 46, title: "Code review: миграция БД на v3",     kind: "assignees_changed", agent: "roma",  text: "Рома взял PB-46 на ревью" },
  { ts: "2026-05-17T13:08:51Z", seq: 46, title: "Code review: миграция БД на v3",     kind: "labels_changed",  agent: "kostya", text: "Костя поставил лейбл for-reviewer на PB-46" },
  { ts: "2026-05-17T12:54:14Z", seq: 52, title: "Разбор алертов за смену",             kind: "new_task",        agent: "viktor", text: "Виктор создал PB-52" },
  { ts: "2026-05-17T12:42:08Z", seq: 48, title: "SEO: meta-теги для каталога",         kind: "assignees_changed", agent: "roma",  text: "Рома взял PB-48 на ревью" },
  { ts: "2026-05-17T12:14:02Z", seq: 46, title: "Code review: миграция БД на v3",     kind: "alert",           agent: "roma",   text: "Сработало правило reviewer_pickup_within_1h (info)" },
  { ts: "2026-05-17T11:31:01Z", seq: 45, title: "Onboarding wizard для KALK",          kind: "state_changed",   agent: "roma",   text: "Рома approved PB-45: In Review → Done" },
  { ts: "2026-05-17T11:08:44Z", seq: 45, title: "Onboarding wizard для KALK",          kind: "comment",         agent: "roma",   text: "Рома: «LGTM, только переименуй handleSubmit2»" },
  { ts: "2026-05-17T10:42:18Z", seq: 44, title: "Telegram-бот: подсчёт стоимости",     kind: "alert",           agent: null,     text: "Сработало правило labels_conflict (dismissed)" },
  { ts: "2026-05-17T10:14:00Z", seq: 51, title: "Описать модуль уведомлений в KALK",   kind: "labels_changed",  agent: "viktor", text: "Виктор поставил for-architect на PB-51" },
];

// ---------------------------------------------------------------------------
// Rules
// ---------------------------------------------------------------------------
const RULES = [
  {
    name: "coder_claim_within_5min",
    description: "Если задача с лейблом for-coder висит в Todo > 5 минут без claim — алерт.",
    severity: "warning",
    active: true,
    triggers24h: 3,
    lastTriggered: "2026-05-17T14:23:01Z",
    role: "coder",
    claudeMdRef: "agents/coder/CLAUDE.md § 3.1",
    yaml: `name: coder_claim_within_5min
description: |
  Если задача с лейблом for-coder висит в Todo больше 5 минут
  без assignee — генерируем warning-алерт.
severity: warning
trigger:
  event: label_set
  label: for-coder
  task_state: Todo
condition:
  age_min: 5
  has_assignee: false
action:
  - notify: telegram
  - mark: ai_inactive`,
  },
  {
    name: "reviewer_pickup_within_1h",
    description: "Reviewer должен взять задачу с for-reviewer в течение 60 минут.",
    severity: "info",
    active: true,
    triggers24h: 1,
    lastTriggered: "2026-05-17T12:14:02Z",
    role: "reviewer",
    claudeMdRef: "agents/reviewer/CLAUDE.md § 2.1",
    yaml: `name: reviewer_pickup_within_1h
severity: info
trigger:
  event: label_set
  label: for-reviewer
condition:
  age_min: 60
  has_reviewer_assignee: false`,
  },
  {
    name: "stuck_in_progress_3h",
    description: "Задача >3ч в In Progress без новых коммитов или комментариев.",
    severity: "warning",
    active: true,
    triggers24h: 2,
    lastTriggered: "2026-05-17T13:51:44Z",
    role: "coder",
    claudeMdRef: "agents/coder/CLAUDE.md § 4.3",
    yaml: `name: stuck_in_progress_3h
severity: warning
trigger:
  event: poll
condition:
  state: In Progress
  age_min: 180
  no_activity: true`,
  },
  {
    name: "watcher_heartbeat_missing",
    description: "Watcher агента не отвечает >5 минут — вероятный сбой сессии.",
    severity: "critical",
    active: true,
    triggers24h: 1,
    lastTriggered: "2026-05-17T14:08:11Z",
    role: "any",
    claudeMdRef: "agents/<role>/CLAUDE.md § 1.2",
    yaml: `name: watcher_heartbeat_missing
severity: critical
trigger:
  event: heartbeat_lost
condition:
  threshold_min: 5
action:
  - notify: telegram
  - escalate: viktor`,
  },
  {
    name: "labels_conflict",
    description: "На задаче >1 лейбла for-<role> одновременно.",
    severity: "warning",
    active: true,
    triggers24h: 1,
    lastTriggered: "2026-05-17T10:42:18Z",
    role: "architect",
    claudeMdRef: "agents/architect/CLAUDE.md § 5",
    yaml: `name: labels_conflict
severity: warning
trigger:
  event: labels_changed
condition:
  for_role_labels_count: ">1"`,
  },
  {
    name: "task_no_assignee_2h",
    description: "Задача в Todo >2ч и ни у кого не назначена.",
    severity: "warning",
    active: false,
    triggers24h: 0,
    lastTriggered: "2026-05-15T09:42:00Z",
    role: "any",
    claudeMdRef: "agents/architect/CLAUDE.md § 2",
    yaml: `name: task_no_assignee_2h
severity: warning
trigger:
  event: poll
condition:
  state: Todo
  age_min: 120
  has_assignee: false`,
  },
  {
    name: "context_below_20pct",
    description: "Самооценка контекста агента < 20% — риск сбоя.",
    severity: "info",
    active: true,
    triggers24h: 2,
    lastTriggered: "2026-05-17T14:01:00Z",
    role: "any",
    claudeMdRef: "agents/<role>/CLAUDE.md § 1.4",
    yaml: `name: context_below_20pct
severity: info
trigger:
  event: agent_heartbeat
condition:
  context_remaining_pct: "<20"`,
  },
];

// ---------------------------------------------------------------------------
// Tasks history
// ---------------------------------------------------------------------------
// Generate ~40 tasks over the last ~14 days
const TASKS = [
  { seq: 12, title: "Скелет проекта Next.js",                module: "scaffold", state: "Done", status: "ok", createdAt: "2026-05-03T09:14:00Z", closedAt: "2026-05-03T15:22:00Z", agents: ["lev","kostya","roma"] },
  { seq: 13, title: "Базовая модель данных Order",            module: "orders",   state: "Done", status: "ok", createdAt: "2026-05-04T08:02:00Z", closedAt: "2026-05-04T18:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 14, title: "FastAPI: эндпоинт /orders",              module: "orders",   state: "Done", status: "warn", createdAt: "2026-05-04T11:40:00Z", closedAt: "2026-05-05T14:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 15, title: "TG-бот: команда /start",                 module: "tg-bot",   state: "Done", status: "ok", createdAt: "2026-05-05T09:00:00Z", closedAt: "2026-05-05T13:11:00Z", agents: ["lev","kostya"] },
  { seq: 16, title: "TG-бот: главное меню",                   module: "tg-bot",   state: "Done", status: "ok", createdAt: "2026-05-05T14:00:00Z", closedAt: "2026-05-06T10:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 17, title: "KALK: первичный расчёт визиток",         module: "kalk",     state: "Done", status: "ok", createdAt: "2026-05-06T11:00:00Z", closedAt: "2026-05-07T09:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 18, title: "KALK: каталог форматов А5/А6",           module: "kalk",     state: "Done", status: "warn", createdAt: "2026-05-07T10:00:00Z", closedAt: "2026-05-08T17:00:00Z", agents: ["lev","kostya","timur","roma"] },
  { seq: 19, title: "Авторизация: страница /login",           module: "auth",     state: "Done", status: "ok", createdAt: "2026-05-08T09:30:00Z", closedAt: "2026-05-09T11:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 20, title: "Авторизация: JWT + рефреш",              module: "auth",     state: "Done", status: "crit", createdAt: "2026-05-09T12:00:00Z", closedAt: "2026-05-10T22:00:00Z", agents: ["lev","kostya","roma","timur"] },
  { seq: 21, title: "Документация: README первая ревизия",    module: "docs",     state: "Done", status: "ok", createdAt: "2026-05-10T09:00:00Z", closedAt: "2026-05-10T13:00:00Z", agents: ["lev","semyon"] },
  { seq: 22, title: "TG-бот: пагинация каталога",             module: "tg-bot",   state: "Done", status: "ok", createdAt: "2026-05-10T14:00:00Z", closedAt: "2026-05-11T11:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 23, title: "KALK: справочник тиражей",               module: "kalk",     state: "Done", status: "ok", createdAt: "2026-05-11T11:30:00Z", closedAt: "2026-05-11T17:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 24, title: "Каталог: SEO meta-теги",                 module: "seo",      state: "Done", status: "warn", createdAt: "2026-05-11T18:00:00Z", closedAt: "2026-05-12T20:00:00Z", agents: ["lev","kostya","semyon","roma"] },
  { seq: 25, title: "Корзина: добавление позиций",            module: "orders",   state: "Done", status: "ok", createdAt: "2026-05-12T09:00:00Z", closedAt: "2026-05-12T18:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 26, title: "TG-бот: создание заказа",                module: "tg-bot",   state: "Done", status: "crit", createdAt: "2026-05-12T13:00:00Z", closedAt: "2026-05-14T16:00:00Z", agents: ["lev","kostya","roma","timur"] },
  { seq: 27, title: "Аналитика: дашборд продаж",              module: "orders",   state: "Done", status: "ok", createdAt: "2026-05-13T09:00:00Z", closedAt: "2026-05-13T19:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 28, title: "Каталог: фильтры по материалу",          module: "orders",   state: "Done", status: "ok", createdAt: "2026-05-13T11:00:00Z", closedAt: "2026-05-14T10:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 29, title: "TG-бот: статусы заказа",                 module: "tg-bot",   state: "Done", status: "ok", createdAt: "2026-05-14T11:00:00Z", closedAt: "2026-05-14T17:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 30, title: "KALK: расчёт листовок",                  module: "kalk",     state: "Done", status: "warn", createdAt: "2026-05-14T12:00:00Z", closedAt: "2026-05-15T11:00:00Z", agents: ["lev","kostya","timur","roma"] },
  { seq: 31, title: "Каталог: SEO sitemap.xml",               module: "seo",      state: "Done", status: "ok", createdAt: "2026-05-15T09:00:00Z", closedAt: "2026-05-15T14:00:00Z", agents: ["lev","kostya","semyon","roma"] },
  { seq: 32, title: "Авторизация: восстановление пароля",     module: "auth",     state: "Done", status: "ok", createdAt: "2026-05-15T10:00:00Z", closedAt: "2026-05-15T18:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 33, title: "TG-бот: оплата картой",                  module: "tg-bot",   state: "Done", status: "warn", createdAt: "2026-05-15T11:00:00Z", closedAt: "2026-05-16T14:00:00Z", agents: ["lev","kostya","roma","timur"] },
  { seq: 34, title: "KALK: афиши, плакаты",                   module: "kalk",     state: "Done", status: "ok", createdAt: "2026-05-15T13:00:00Z", closedAt: "2026-05-16T12:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 35, title: "Каталог: ленты блога",                   module: "seo",      state: "Done", status: "ok", createdAt: "2026-05-15T14:00:00Z", closedAt: "2026-05-16T15:00:00Z", agents: ["lev","semyon","kostya","roma"] },
  { seq: 36, title: "TG-бот: уведомления",                    module: "tg-bot",   state: "Done", status: "ok", createdAt: "2026-05-15T16:00:00Z", closedAt: "2026-05-16T17:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 37, title: "Авторизация: 2FA",                       module: "auth",     state: "Done", status: "crit", createdAt: "2026-05-16T08:30:00Z", closedAt: "2026-05-17T03:00:00Z", agents: ["lev","kostya","roma","timur"] },
  { seq: 38, title: "KALK: расчёт буклетов",                  module: "kalk",     state: "Done", status: "ok", createdAt: "2026-05-16T09:00:00Z", closedAt: "2026-05-16T18:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 39, title: "Каталог: интеграция с Yandex feed",      module: "seo",      state: "Done", status: "warn", createdAt: "2026-05-16T10:00:00Z", closedAt: "2026-05-17T09:00:00Z", agents: ["lev","kostya","semyon","roma"] },
  { seq: 40, title: "TG-бот: история заказов клиента",        module: "tg-bot",   state: "Done", status: "ok", createdAt: "2026-05-16T11:00:00Z", closedAt: "2026-05-17T08:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 41, title: "KALK: тиражи > 1000",                    module: "kalk",     state: "Done", status: "ok", createdAt: "2026-05-16T12:00:00Z", closedAt: "2026-05-17T10:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 42, title: "Аналитика: воронка конверсии",           module: "orders",   state: "Done", status: "ok", createdAt: "2026-05-16T13:00:00Z", closedAt: "2026-05-17T11:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 43, title: "Каталог: страница «о мастерской»",       module: "seo",      state: "Done", status: "ok", createdAt: "2026-05-16T14:00:00Z", closedAt: "2026-05-17T09:30:00Z", agents: ["lev","semyon","roma"] },
  { seq: 44, title: "Telegram-бот: подсчёт стоимости",        module: "tg-bot",   state: "Done", status: "warn", createdAt: "2026-05-17T09:00:00Z", closedAt: "2026-05-17T13:00:00Z", agents: ["lev","kostya","roma"] },
  { seq: 45, title: "Onboarding wizard для KALK",             module: "kalk",     state: "Done", status: "ok", createdAt: "2026-05-17T08:00:00Z", closedAt: "2026-05-17T11:31:00Z", agents: ["lev","kostya","roma"] },
  { seq: 46, title: "Code review: миграция БД на v3",         module: "orders",   state: "Done", status: "info", createdAt: "2026-05-17T10:00:00Z", closedAt: "2026-05-17T13:58:00Z", agents: ["lev","kostya","roma"] },
  { seq: 47, title: "Аналитика заказов: фильтр по статусу",   module: "orders",   state: "In Progress", status: "warn", createdAt: "2026-05-17T10:39:00Z", closedAt: null, agents: ["lev","kostya"] },
  { seq: 48, title: "SEO: meta-теги для каталога",            module: "seo",      state: "Done", status: "ok", createdAt: "2026-05-17T12:00:00Z", closedAt: "2026-05-17T13:31:00Z", agents: ["lev","semyon","roma"] },
  { seq: 49, title: "Ребрендинг Print-X в коде сайта",        module: "seo",      state: "In Progress", status: "warn", createdAt: "2026-05-17T14:00:00Z", closedAt: null, agents: ["lev","kostya"] },
  { seq: 50, title: "Подобрать референсы для каталога A4",    module: "seo",      state: "In Progress", status: "crit", createdAt: "2026-05-17T13:30:00Z", closedAt: null, agents: ["semyon"] },
  { seq: 51, title: "Описать модуль уведомлений в KALK",      module: "kalk",     state: "In Progress", status: "open", createdAt: "2026-05-17T14:31:00Z", closedAt: null, agents: ["lev"] },
  { seq: 52, title: "Разбор алертов за смену",                module: "watchdog", state: "In Progress", status: "open", createdAt: "2026-05-17T12:54:00Z", closedAt: null, agents: ["viktor"] },
];

// ---------------------------------------------------------------------------
// Agents CLAUDE.md (markdown-like)
// ---------------------------------------------------------------------------
const AGENT_DOCS = {
  architect: `# CLAUDE.md — Architect (Лев)

## 1. Роль
Ты — главный архитектор команды. Ты создаёшь задачи в Plane на основании
ТЗ от Максима, разбиваешь крупные эпики на атомарные задачи и распределяешь
их по ролям через лейблы \`for-<role>\`.

## 2. Создание задач
Каждая новая задача обязана иметь:
- внятный заголовок (≤ 60 символов)
- описание с критериями приёмки
- ровно один лейбл \`for-<role>\`
- модуль (тег: \`orders\`, \`kalk\`, \`tg-bot\`, ...)

## 3. Декомпозиция
- Не создавай задач, оценка которых > 4 часов работы агента.
- Если задача больше — разбей на под-задачи и свяжи их через relation.

## 4. Качество ТЗ
- Каждая задача должна включать секцию «Definition of Done».
- Если описание спорное — попроси Максима уточнить через Plane-комментарий.

## 5. Лейблы
В любой момент на задаче должен быть ровно один лейбл \`for-<role>\`.
Перед сменой роли убери предыдущий лейбл.

## 6. Эскалация
Если задача висит в Backlog > 24 часов — поставь лейбл \`needs-triage\` и
позови Виктора через коммент.
`,

  coder: `# CLAUDE.md — Coder (Костя)

## 1. Роль
Ты — программист. Ты пишешь код по задачам в Plane. Стек: Next.js 14,
FastAPI, PostgreSQL, Python 3.12.

## 2. Подбор задач
Регулярно проверяй задачи в state \`Todo\` с лейблом \`for-coder\`.

### 3.1 SLA на подбор
Если задача помечена \`for-coder\` и её state = Todo, ты обязан забрать её
(assign на себя + перевести в In Progress) в течение 5 минут.
Если занят — оставь комментарий \`busy, eta NN min\`.

## 4. Работа
- Делай коммит после каждой логической единицы.
- Не пиши код без тестов (хотя бы один интеграционный).

### 4.3 Прогресс по задаче
Если задача в In Progress дольше 2 часов без коммита — оставь
промежуточный коммент \`progress: <что сделано>, <что осталось>\`.
Если застрял — переведи в Blocked.

## 5. Передача в ревью
- Перед сменой лейбла на \`for-reviewer\` запусти \`pytest && pnpm test\`.
- В коммент пиши: \`ready for review @roma: <ссылка на PR>\`.

## 6. Контекст
Если контекст < 20% — переведи задачу в Blocked и попроси перезапуск.
`,

  reviewer: `# CLAUDE.md — Reviewer (Рома)

## 1. Роль
Ты — ревизор кода. Ты проверяешь PR, оставленные Костей, на соответствие
архитектуре и стандартам.

### 2.1 SLA на ревью
Задачу с лейблом \`for-reviewer\` необходимо взять в ревью в течение
1 часа от установки лейбла.

## 3. Чек-лист
- Все тесты зелёные?
- Code style: Black + Ruff + Prettier.
- Нет копипасты > 8 строк.
- DTO/типы согласованы между фронтом и бэком.
- Безопасность: проверь SQL-инъекции, XSS, авторизацию.

## 4. Решение
- Если всё ок — approve и переведи в \`Done\`.
- Если есть замечания — оставь коммент с пунктами и верни в \`In Progress\`
  с лейблом \`for-coder\`.

## 5. Эскалация
Если PR требует архитектурного решения — добавь \`for-architect\` и сними
\`for-reviewer\`.
`,

  tester: `# CLAUDE.md — Tester (Тимур)

## 1. Роль
Ты — тестировщик. Пишешь E2E (Playwright) для сценариев из задачи и
ad-hoc проверяешь edge-cases.

## 2. Подбор задач
Подключайся к задачам после approve ревизора — добавляй сценарии в
\`tests/e2e/\`.

## 3. Отчёт
- Если сценарий пройден — поставь лейбл \`tested\`.
- Если найден баг — заводи отдельный bug-репорт со ссылкой на PR.

## 4. Покрытие
Минимум один happy-path и один edge-case на каждую новую фичу.
`,

  scout: `# CLAUDE.md — Scout (Семён)

## 1. Роль
Ты — контент-разведчик. Ищешь референсы, SEO-материал, конкурентов
полиграфической мастерской «Мульти принт» по А4/A5/визиткам.

### 1.2 Жизненный цикл сессии
Сессия scout-агента должна работать непрерывно, отправляя heartbeat в
watcher раз в 30 секунд. Если сессия завершилась — перезапусти её через
\`wt agent restart scout\`.

## 2. Источники
- Yandex, Google (SerpAPI).
- Pinterest, Behance — для визуальных референсов.
- Конкуренты: printio, vistaprint, GoodPrint.

## 3. Формат отчёта
В коммент к задаче — markdown-список из 5–10 пунктов с ссылками и
кратким резюме.

## 4. Качество
Никогда не копируй текст конкурента дословно. Только пересказ + ссылка.
`,

  "workflow-lead": `# CLAUDE.md — Workflow Lead (Виктор)

## 1. Роль
Ты — главный над процессом. Ты разбираешь алерты watchdog'а и
вмешиваешься в работу команды, если что-то идёт не так.

## 2. Источники сигналов
- Telegram-нотификации от WorkflowTools-TgBot.
- Дашборд WorkflowTools-Watchdog (http://lan:8080).
- Прямые задачи от Максима.

## 3. Действия по алерту
- Прочти описание, открой задачу в Plane.
- Прочти CLAUDE.md соответствующей роли (раздел из алерта).
- Реши: пинговать агента, переназначить, перезапустить watcher.
- Закрой алерт (dismiss + комментарий).

## 4. Эскалация
Если алерт повторяется >2 раз в сутки — заведи задачу
«Уточнить правило <name>» с лейблом \`for-architect\`.
`,
};

// ---------------------------------------------------------------------------
// Util / formatters
// ---------------------------------------------------------------------------
function fmtTime(ts) {
  const d = new Date(ts);
  return d.toLocaleTimeString("ru-RU", { hour: "2-digit", minute: "2-digit" });
}
function fmtDate(ts) {
  const d = new Date(ts);
  return d.toLocaleDateString("ru-RU", { day: "2-digit", month: "2-digit" });
}
function fmtDateTime(ts) {
  return `${fmtDate(ts)} ${fmtTime(ts)}`;
}
function fmtAgo(ts) {
  const now = NOW.getTime();
  const t = new Date(ts).getTime();
  const diffSec = Math.max(0, Math.round((now - t) / 1000));
  if (diffSec < 60) return `${diffSec}с назад`;
  const m = Math.round(diffSec / 60);
  if (m < 60) return `${m} мин назад`;
  const h = Math.floor(m / 60);
  const mm = m % 60;
  if (h < 24) return `${h}ч ${mm}м назад`;
  const d = Math.floor(h / 24);
  return `${d}д назад`;
}
function fmtDuration(min) {
  if (min < 60) return `${min} мин`;
  const h = Math.floor(min / 60);
  const m = min % 60;
  return m ? `${h}ч ${m}м` : `${h}ч`;
}
function severityToTone(s) {
  if (s === "critical") return "crit";
  if (s === "warning") return "warn";
  if (s === "info") return "info";
  return "neutral";
}

// Counts
const ACTIVE_ALERTS = ALERTS.filter(a => a.status === "active");
const CRITICAL_COUNT = ACTIVE_ALERTS.filter(a => a.severity === "critical").length;
const WARNING_COUNT = ACTIVE_ALERTS.filter(a => a.severity === "warning").length;

Object.assign(window, {
  NOW, TEAM, teamById, ALERTS, EVENTS, RULES, TASKS, AGENT_DOCS,
  ACTIVE_ALERTS, CRITICAL_COUNT, WARNING_COUNT,
  fmtTime, fmtDate, fmtDateTime, fmtAgo, fmtDuration, severityToTone,
});
