Harden DetMir runtime hot paths
This commit is contained in:
@@ -55,6 +55,21 @@ Live endpoints, hostnames, tokens and passwords must be supplied through
|
||||
`/etc/detmir/detmir-check.env` (systemd units) or another private environment
|
||||
file outside the public repository.
|
||||
|
||||
Production note checked on 2026-06-24:
|
||||
|
||||
- `DETMIR_PORTAL_URL` must point to the local DetMir portal listener,
|
||||
currently `http://127.0.0.1:8720`, for server-side health checks. If it is
|
||||
omitted, `detmir-check` falls back to the public HTTPS gateway and protected
|
||||
`/readyz`, `/version` and `/metrics` can correctly return `401`, producing a
|
||||
false operational failure.
|
||||
- `DETMIR_GATEWAY_HOST=127.0.0.1` is used with the local listener so the Host
|
||||
header does not accidentally select the public protected gateway path.
|
||||
- Cold `/api/reports` builds can take more than 60 seconds on the live contour
|
||||
when cache is empty or concurrent checks are active. The production
|
||||
`detmir-portal-prewarm.service` therefore uses `curl --max-time 180` and
|
||||
`TimeoutStartSec=210`. Shorter 45-60 second limits caused false failed
|
||||
systemd states while the portal eventually returned HTTP 200.
|
||||
|
||||
## Текущий планировщик Proxmox, проверено 2026-06-21
|
||||
|
||||
На Proxmox уже присутствуют следующие регулярные проверки:
|
||||
@@ -75,10 +90,10 @@ file outside the public repository.
|
||||
Наблюдение: отдельный ежедневный полный gate по всей матрице AWatch-rus
|
||||
отсутствует. Его роль должен закрыть `awatch-contour-daily-check.timer`.
|
||||
|
||||
Наблюдение: последняя проверка `detmir-portal-prewarm.service` на момент осмотра
|
||||
имела `Result=exit-code` и `ExecMainStatus=28`. Это не надо маскировать:
|
||||
канонический check должен показывать такой сбой как fail/warn в зависимости от
|
||||
политики эксплуатации.
|
||||
Историческое наблюдение 2026-06-24: `detmir-portal-prewarm.service` был найден
|
||||
в failed state из-за устаревшего `curl --max-time 60` для холодной сборки
|
||||
`/api/reports`. В текущей ветке это оформлено как отдельный prewarm/resilience
|
||||
пакет, а не как обязательная часть DLP production hot path.
|
||||
|
||||
## Матрица требований и проверок
|
||||
|
||||
@@ -91,14 +106,36 @@ file outside the public repository.
|
||||
| Portal hardening | `/healthz`, `/readyz`, `/version`, `/metrics` | `detmir-check` | да | да |
|
||||
| Windows/RDP | TCP 5985 и 22 | `detmir-check` | да | да |
|
||||
| ActivityWatch buckets | AFK/window/worktime/session events | `detmir-check` | да | да |
|
||||
| AWatch DLP buckets | endpoint signals/incidents/review/rules | `detmir-check` | да | да |
|
||||
| AWatch DLP health | remote `dlp-health-check --json` через `detmir-dlp` | `detmir-check` | да | да |
|
||||
| AWatch DLP buckets | endpoint signals/incidents/review/rules, только если DLP включен | `detmir-check` | условно | условно |
|
||||
| AWatch DLP health | disabled/core_only должен быть SKIPPED/WARN, `light/full` проверяются через `detmir-dlp` | `detmir-check` | да | да |
|
||||
| Grafana evidence | свежий JSON артефакт Grafana check | `detmir-check` | да | да |
|
||||
| Security events backend | ClickHouse events, если включено | `detmir-check` | да | да |
|
||||
| Portal contract | role/API smoke | `scripts/awatch-production-hardening-smoke.mjs` | нет | да |
|
||||
| Pilot contract | demo/API smoke | `scripts/detmir-pilot-demo-smoke.mjs` | нет | да |
|
||||
| Registry/readiness docs | registry readiness check | `scripts/registry_readiness_check.sh` | опционально | да |
|
||||
|
||||
## Timeout/fail-closed параметры live contour
|
||||
|
||||
После ручного live-прогона 2026-06-24 production
|
||||
`/etc/detmir/detmir-check.env` должен содержать bounded timeouts, соответствующие
|
||||
фактической latency AW datastore:
|
||||
|
||||
```env
|
||||
DETMIR_SERVICE_TIMEOUT_SECONDS=35
|
||||
DETMIR_BUCKET_TIMEOUT_SECONDS=35
|
||||
DETMIR_DLP_TIMEOUT_SECONDS=120
|
||||
DETMIR_CHECK_OVERALL_TIMEOUT_SECONDS=300
|
||||
```
|
||||
|
||||
Назначение:
|
||||
|
||||
- не считать bucket `DEAD` только из-за штатной 15-30 секундной latency
|
||||
большого SQLite datastore;
|
||||
- не оставлять `detmir-check`, `detmir-dlp`, `ssh` и remote
|
||||
`dlp-health-check` хвосты при timeout;
|
||||
- сохранять красный non-zero результат при реальной недоступности, но
|
||||
завершать проверку bounded.
|
||||
|
||||
## Fail-closed политика
|
||||
|
||||
Ежедневный check должен завершаться non-zero, если падает обязательная область:
|
||||
@@ -108,7 +145,9 @@ file outside the public repository.
|
||||
- Gateway/Portal health;
|
||||
- RDP/Windows reachability;
|
||||
- свежесть обязательных bucket streams;
|
||||
- AWatch DLP health;
|
||||
- AWatch DLP health только если DLP runtime включен; при штатном
|
||||
`AW_DLP_ENABLED=false`/`core_only` disabled-state не является отказом
|
||||
Workforce/Worktime core;
|
||||
- Grafana evidence freshness.
|
||||
|
||||
Event-driven buckets не должны считаться stale только из-за отсутствия новых
|
||||
|
||||
@@ -7,6 +7,20 @@ ClickHouse не является обязательной зависимость
|
||||
перезапускайте ClickHouse для восстановления отчетов рабочего времени, если нет
|
||||
отдельного подтвержденного отказа ClickHouse.
|
||||
|
||||
## Stable host id
|
||||
|
||||
Worktime reports используют stable logical host id, а не обязательно текущее
|
||||
Windows `COMPUTERNAME`. Для DetMir production текущий logical id:
|
||||
|
||||
```text
|
||||
SHARKON2025
|
||||
```
|
||||
|
||||
При переименовании RDP-сервера не меняйте `awHostname` автоматически. Сначала
|
||||
обновите Windows account domain для задач, затем проверьте, что collectors
|
||||
продолжают писать в bucket-и `*_SHARKON2025`. Подробный порядок:
|
||||
`docs/WINDOWS_LOGICAL_HOST_ID_RU.md`.
|
||||
|
||||
## Симптомы перегруза
|
||||
|
||||
- `/portal/api/reports?role=executive` открывается медленно или отвечает
|
||||
@@ -226,6 +240,153 @@ curl -sS --max-time 8 http://<PORTAL_HOST>/portal/api/health | jq
|
||||
curl -sS --max-time 12 "http://<PORTAL_HOST>/portal/api/reports?role=executive" | jq '.status'
|
||||
```
|
||||
|
||||
## Production repair: AW SQLite hot path, 2026-06-30
|
||||
|
||||
Симптомы:
|
||||
|
||||
- `activitywatch-server` отвечает `503` на bucket API;
|
||||
- журнал содержит `poisoned lock` / `database is locked`;
|
||||
- `aw-worktime-api` уходит в bounded `DEGRADED`;
|
||||
- `/buckets/aw-worktime-sessions_<HOST>/events?limit=...` тайм-аутится даже
|
||||
при малом лимите;
|
||||
- RDP browser/category collector пишет `bucket create failed` или timeout.
|
||||
|
||||
Порядок безопасного восстановления:
|
||||
|
||||
1. Остановить RDP guard и процессы `aw-windows-telemetry`, чтобы не продолжать
|
||||
штурмовать AW API.
|
||||
2. Остановить `aw-worktime-*` timers/services и другие локальные потребители AW
|
||||
API.
|
||||
3. Перезапустить `activitywatch-server` отдельно и проверить `/api/0/info`.
|
||||
4. Если bucket metadata отвечает, но `/events` медленный, проверить SQLite plan:
|
||||
|
||||
```sql
|
||||
EXPLAIN QUERY PLAN
|
||||
SELECT id,starttime,endtime,data
|
||||
FROM events
|
||||
WHERE bucketrow=(SELECT id FROM buckets WHERE name='aw-worktime-sessions_<HOST>')
|
||||
ORDER BY starttime DESC
|
||||
LIMIT 100;
|
||||
```
|
||||
|
||||
Если план строит `TEMP B-TREE FOR ORDER BY`, нужен составной индекс:
|
||||
|
||||
```sql
|
||||
CREATE INDEX IF NOT EXISTS events_bucketrow_starttime_desc_index
|
||||
ON events(bucketrow, starttime DESC);
|
||||
ANALYZE;
|
||||
PRAGMA optimize;
|
||||
PRAGMA integrity_check;
|
||||
```
|
||||
|
||||
Индекс добавлять только в controlled window:
|
||||
|
||||
- остановить `activitywatch-server`;
|
||||
- сделать rollback backup SQLite DB;
|
||||
- создать индекс;
|
||||
- проверить `PRAGMA integrity_check = ok`;
|
||||
- запустить `activitywatch-server`;
|
||||
- проверить, что `/events?limit=100` больше не тайм-аутится.
|
||||
|
||||
Production DetMir repair 2026-06-30:
|
||||
|
||||
- оставлены две свежие ежедневные SQLite VACUUM backup-копии, старые backup-и
|
||||
ротированы для освобождения места;
|
||||
- создан rollback backup:
|
||||
`/var/lib/activitywatch/backups/db/aw-sqlite-before-hotpath-index-20260630T035032Z.db`;
|
||||
- добавлен индекс `events_bucketrow_starttime_desc_index`;
|
||||
- `ROCKET_WORKERS=8` добавлен в `/etc/activitywatch/aw-server.env`;
|
||||
- для `aw-worktime-api.service` добавлен stabilization drop-in:
|
||||
`AW_WORKTIME_EVENTS_LIMIT=100`,
|
||||
`AW_WORKTIME_AW_HTTP_TIMEOUT_SECONDS=25`,
|
||||
`AW_WORKTIME_EVENTS_CACHE_TTL_SECONDS=600`,
|
||||
`AW_WORKTIME_REPORT_STALE_TTL_SECONDS=7200`.
|
||||
|
||||
После ремонта проверить:
|
||||
|
||||
```bash
|
||||
curl -sS --max-time 10 http://127.0.0.1:5600/api/0/info
|
||||
curl -sS --max-time 15 \
|
||||
'http://127.0.0.1:5600/api/0/buckets/aw-worktime-sessions_SHARKON2025/events?limit=100'
|
||||
curl -sS --max-time 15 \
|
||||
'http://127.0.0.1:5610/reports/worktime/today?format=json' | jq '{rows:(.rows|length),degraded,runtime}'
|
||||
```
|
||||
|
||||
Также проверить отсутствие новых `poisoned lock` после финального старта:
|
||||
|
||||
```bash
|
||||
journalctl -u activitywatch-server --since '<FINAL_START_TIME>' --no-pager |
|
||||
grep -E 'poisoned lock|Taking datastore lock failed|database is locked'
|
||||
```
|
||||
|
||||
## Crash/readiness test after AW repair
|
||||
|
||||
Цель: проверить, что контур выдерживает restart и короткую параллельную
|
||||
нагрузку, а проверки не путают `systemctl active` с готовым API.
|
||||
|
||||
Порядок:
|
||||
|
||||
1. Зафиксировать baseline:
|
||||
|
||||
```bash
|
||||
./check-aw-full.sh
|
||||
```
|
||||
|
||||
2. На AW server проверить readiness, hot-path и Worktime API:
|
||||
|
||||
```bash
|
||||
AW_API=http://127.0.0.1:5600 \
|
||||
AW_WORKTIME_API=http://127.0.0.1:5610 \
|
||||
AW_LOGICAL_HOST_ID=SHARKON2025 \
|
||||
scripts/detmir_resilience_check.sh --live
|
||||
```
|
||||
|
||||
Если скрипт запускается с ноутбука, live-mode нужно выполнять на самом
|
||||
AW-сервере через SSH/Ansible, потому что он проверяет local systemd и SQLite.
|
||||
|
||||
3. Controlled restart:
|
||||
|
||||
```bash
|
||||
systemctl restart aw-worktime-api
|
||||
curl -sS --max-time 12 \
|
||||
'http://127.0.0.1:5610/reports/worktime/today?format=json&host=SHARKON2025&allow_stale=1' |
|
||||
jq '{rows:(.rows|length),degraded}'
|
||||
|
||||
systemctl restart activitywatch-server
|
||||
# Не считать "active" готовностью: дождаться HTTP readiness.
|
||||
timeout 90 bash -c 'until curl -fsS --max-time 8 http://127.0.0.1:5600/api/0/info >/dev/null; do sleep 2; done'
|
||||
curl -sS --max-time 15 \
|
||||
'http://127.0.0.1:5600/api/0/buckets/aw-worktime-sessions_SHARKON2025/events?limit=100' >/dev/null
|
||||
```
|
||||
|
||||
4. Проверить, что после рестарта нет новых lock/503:
|
||||
|
||||
```bash
|
||||
journalctl -u activitywatch-server --since '<RESTART_TIME>' --no-pager |
|
||||
grep -E 'poisoned lock|Taking datastore lock failed|database is locked|503'
|
||||
```
|
||||
|
||||
5. Проверить RDP guard restart отдельно:
|
||||
|
||||
```powershell
|
||||
Restart-Service AWatchRusCollectorGuard -Force
|
||||
Start-Sleep -Seconds 75
|
||||
Get-Service AWatchRusCollectorGuard
|
||||
Get-Process aw-windows-telemetry -ErrorAction SilentlyContinue | Measure-Object
|
||||
```
|
||||
|
||||
Ожидаемый результат для DetMir после ремонта 2026-06-30:
|
||||
|
||||
- `check-aw-full.sh`: `FRESH=8`, `STALE=0`, `DEAD=0`;
|
||||
- `/events?limit=100` отвечает за bounded time и использует
|
||||
`events_bucketrow_starttime_desc_index`;
|
||||
- `aw-rus-healthd.service` завершается `status=0/SUCCESS`;
|
||||
- server-side TCP до RDP может быть `warn`, если
|
||||
`AW_RUS_HEALTH_RDP_TCP_REQUIRED=false`, но bucket freshness и WinRM/SSH
|
||||
через admin path должны оставаться зелёными;
|
||||
- optional DLP/Loki heavy runtime units должны быть inactive в экономном
|
||||
production profile.
|
||||
|
||||
## Rollback
|
||||
|
||||
Rollback нужен, если после обновления бинарника или env-настроек:
|
||||
|
||||
@@ -0,0 +1,239 @@
|
||||
# Workforce Operations Model
|
||||
|
||||
Статус: implemented in Worktime API and DetMir portal.
|
||||
|
||||
Модель отвечает на главный управленческий вопрос AWatch-rus Workforce:
|
||||
рабочая активность сотрудников, загрузка, простои, перегруз и дисциплина
|
||||
рабочего процесса. Это rule-based слой операционного контроля. Он не является
|
||||
HR-оценкой, не использует ML/LLM и не выполняет автоматических санкций.
|
||||
|
||||
## Где смотреть
|
||||
|
||||
Основные точки:
|
||||
|
||||
- Worktime API:
|
||||
`GET /reports/worktime/management?format=json`;
|
||||
- Worktime HTML:
|
||||
`GET /reports/worktime/management?format=html`;
|
||||
- DetMir portal:
|
||||
`/api/reports`, блок `workforce_operations`;
|
||||
- UI портала:
|
||||
роли `Руководитель` и вкладка `Отчеты`, блок `Операционная загрузка`.
|
||||
|
||||
## Источники
|
||||
|
||||
Модель использует только подтвержденные рабочие источники:
|
||||
|
||||
- ActivityWatch worktime rows;
|
||||
- bucket рабочих сессий RDP;
|
||||
- интервалы активности в рабочем окне;
|
||||
- configured owner/department aliases;
|
||||
- freshness/coverage metadata, которые уже возвращает Worktime API.
|
||||
|
||||
Отсутствие данных не считается простоем. При пропусках источников строка
|
||||
получает `data_confidence=low` и guardrail
|
||||
`low_confidence_not_for_discipline`.
|
||||
|
||||
## Runtime-настройки
|
||||
|
||||
Основной файл политики:
|
||||
|
||||
- пример: `configs/worktime-interpretation-policy.example.json`;
|
||||
- runtime: `/etc/activitywatch/worktime-interpretation-policy.json`;
|
||||
- env path: `AW_WORKTIME_MANAGER_INTERPRETATION_POLICY`.
|
||||
|
||||
Поля policy:
|
||||
|
||||
| Поле | Смысл | Рекомендуемое значение |
|
||||
| --- | --- | --- |
|
||||
| `underload_threshold` | порог недогруза от рабочего окна | `0.35..0.45` |
|
||||
| `overload_threshold` | порог перегруза от рабочего окна | `1.10..1.25` |
|
||||
| `drop_threshold_pct` | порог просадки тренда | `10..25` |
|
||||
| `night_work_after` | начало вечернего/ночного отклонения | `20:00` |
|
||||
| `weekend_work` | учитывать выходные отклонения | `true` |
|
||||
| `min_trend_points` | минимум daily points для тренда | `3..7` |
|
||||
| `off_hours_threshold_seconds` | минимум внерабочей активности для флага | `1800` |
|
||||
|
||||
`underload_threshold` и `overload_threshold` можно задавать дробью или
|
||||
процентом: `0.45` равно `45`, `1.15` равно `115`.
|
||||
Для перегруза effective threshold fail-closed зажат в диапазон `100..300`, чтобы
|
||||
значение ниже 100% не создавало ложный статус перегруза.
|
||||
|
||||
Env fallback:
|
||||
|
||||
- `AW_WORKTIME_MANAGER_TARGET_COVERAGE_PCT`;
|
||||
- `AW_WORKTIME_MANAGER_LOW_COVERAGE_PCT`;
|
||||
- `AW_WORKTIME_MANAGER_OVERLOAD_COVERAGE_PCT`;
|
||||
- `AW_WORKTIME_MANAGER_TREND_MIN_POINTS`;
|
||||
- `AW_WORKTIME_MANAGER_TREND_DELTA_PCT`;
|
||||
- `AW_WORKTIME_MANAGER_OFF_HOURS_THRESHOLD_SECONDS`;
|
||||
- `AW_WORKTIME_MANAGER_NIGHT_WORK_AFTER`;
|
||||
- `AW_WORKTIME_MANAGER_WEEKEND_WORK_ENABLED`.
|
||||
|
||||
Веса приложений остаются отдельной политикой:
|
||||
|
||||
- пример: `configs/detmir-workforce-policy.example.json`;
|
||||
- runtime: `/etc/detmir-portal-workforce-policy.json`.
|
||||
|
||||
Она влияет на explainable KPI и weighted activity, но не подменяет
|
||||
операционные статусы загрузки/простоя.
|
||||
|
||||
## API contract
|
||||
|
||||
`/reports/worktime/management?format=json` содержит:
|
||||
|
||||
```json
|
||||
{
|
||||
"workday": {
|
||||
"target_coverage_pct": 75,
|
||||
"low_coverage_pct": 35,
|
||||
"overload_coverage_pct": 115
|
||||
},
|
||||
"workforce_operations": {
|
||||
"status": "ATTENTION",
|
||||
"summary": {},
|
||||
"rows": [],
|
||||
"model": {
|
||||
"type": "rule_based",
|
||||
"ml": false,
|
||||
"llm": false,
|
||||
"version": "workforce-operations-v1"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Каждая строка сотрудника содержит:
|
||||
|
||||
- `workday_active_seconds`, `workday_active_hhmm`;
|
||||
- `workday_idle_seconds`, `workday_idle_hhmm`;
|
||||
- `coverage_pct`;
|
||||
- `load_status`;
|
||||
- `idle_status`;
|
||||
- `discipline_status`;
|
||||
- `data_confidence`;
|
||||
- `recommended_action`.
|
||||
|
||||
Полный roster в `rows[]` дополнительно содержит `operations`,
|
||||
`operations.evidence`, `operations.guardrail` и
|
||||
`operations_recommended_action`.
|
||||
|
||||
## Статусы загрузки
|
||||
|
||||
| Status | Значение | Действие |
|
||||
| --- | --- | --- |
|
||||
| `insufficient_data` | рабочее окно еще не началось или равно нулю | не делать вывод |
|
||||
| `no_data` | нет сессий или worktime samples | проверить источники |
|
||||
| `no_activity` | сессия/данные есть, активности в окне нет | проверить присутствие и задачи |
|
||||
| `underloaded` | ниже low threshold | проверить загрузку и доступ к процессам |
|
||||
| `below_target` | ниже target threshold | уточнить причину отклонения |
|
||||
| `normal` | в рабочем диапазоне | наблюдать |
|
||||
| `overloaded` | выше overload threshold | проверить переработку и риск аврала |
|
||||
|
||||
## Статусы простоя
|
||||
|
||||
| Status | Значение |
|
||||
| --- | --- |
|
||||
| `not_applicable` | нет рабочего окна |
|
||||
| `unknown` | нет достаточных источников |
|
||||
| `full_workday_idle_or_absent` | активность в рабочем окне отсутствует |
|
||||
| `idle_detected` | простой выше порога |
|
||||
| `no_significant_idle` | существенный простой не найден |
|
||||
|
||||
## Дисциплина процесса
|
||||
|
||||
`discipline_status` показывает отклонение от рабочего процесса, а не
|
||||
автоматическое нарушение:
|
||||
|
||||
- `ok`;
|
||||
- `off_hours`;
|
||||
- `late_start`;
|
||||
- `early_finish`;
|
||||
- `multiple_flags`.
|
||||
|
||||
Для текущего дня `early_finish` не выставляется до завершения рабочего окна.
|
||||
|
||||
## Достоверность
|
||||
|
||||
`data_confidence`:
|
||||
|
||||
- `high`: есть session samples, worktime samples и active samples;
|
||||
- `medium`: данных мало или нет active samples;
|
||||
- `low`: нет сессий/worktime samples или рабочее окно невалидно.
|
||||
|
||||
Правило: low confidence строки сначала проверяются как проблема источников.
|
||||
Их нельзя использовать как персональный дисциплинарный вывод.
|
||||
|
||||
## Summary
|
||||
|
||||
`workforce_operations.summary` содержит:
|
||||
|
||||
- `users_count`;
|
||||
- `action_required_users`;
|
||||
- `load.unknown_or_no_data_users`;
|
||||
- `load.underloaded_users`;
|
||||
- `load.normal_users`;
|
||||
- `load.overloaded_users`;
|
||||
- `idle.idle_users`;
|
||||
- `discipline.review_users`;
|
||||
- `confidence.low_users`;
|
||||
- `confidence.medium_users`;
|
||||
- `confidence.high_users`;
|
||||
- `guardrail`.
|
||||
|
||||
Summary status:
|
||||
|
||||
- `LOW_CONFIDENCE`: нет строк или все строки low confidence;
|
||||
- `ATTENTION`: есть перегруз, простой или дисциплинарные флаги;
|
||||
- `WATCH`: есть недогруз, нет данных или low confidence;
|
||||
- `OK`: отклонений нет.
|
||||
|
||||
## UI contract
|
||||
|
||||
Портал показывает отдельный блок `Операционная загрузка`:
|
||||
|
||||
- сводка: требуют разбора, недогруз, перегруз, простой, дисциплина, low
|
||||
confidence;
|
||||
- таблица сотрудников: active/idle/coverage/load/idle/discipline/confidence;
|
||||
- рекомендуемое действие;
|
||||
- guardrail и версию rule-based модели.
|
||||
|
||||
Это отдельный блок от `Почему такой индекс активности?`: explainable KPI
|
||||
отвечает на вопрос "почему такой процент", а Workforce Operations отвечает
|
||||
"кого и почему нужно разобрать".
|
||||
|
||||
## Ограничения
|
||||
|
||||
- Не утверждать автоматическую оценку эффективности сотрудника.
|
||||
- Не считать missing data простоем.
|
||||
- Не смешивать Security/Forensics claims с Workforce Operations.
|
||||
- Не заявлять ML/LLM detection.
|
||||
- Не выполнять автоматическое remediation/action.
|
||||
- Не использовать GitHub Actions или демо-данные как registry release evidence.
|
||||
|
||||
## Проверка после изменения
|
||||
|
||||
Минимальный локальный контур:
|
||||
|
||||
```bash
|
||||
cd /mnt/usb_hdd2/Projects/ActivityWatch-Russian/adk-rust
|
||||
export CARGO_TARGET_DIR=/home/igor/.cache/detmir-adk-rust-target
|
||||
cargo fmt --all --check
|
||||
cargo test -p worktime-api -p detmir-portal --locked
|
||||
cargo clippy -p worktime-api -p detmir-portal --all-targets --locked -- -D warnings
|
||||
```
|
||||
|
||||
Минимальный live smoke:
|
||||
|
||||
```bash
|
||||
curl -fsS 'http://10.10.10.13:5610/reports/worktime/management?format=json' \
|
||||
| jq '.workforce_operations.summary'
|
||||
|
||||
curl -fsS 'http://10.10.10.2:8720/api/reports?role=manager' \
|
||||
| jq '{status: .workforce_operations.summary.status, rows: (.workforce_operations.rows | length)}'
|
||||
```
|
||||
|
||||
Браузерный smoke: открыть `http://10.10.10.2:8720/`, выбрать представление
|
||||
менеджера и проверить блок `Операционная загрузка`. В рабочем состоянии должны
|
||||
быть видны summary-карточки, таблица сотрудников, `workforce-operations-v1` и
|
||||
guardrail про `low confidence`.
|
||||
Reference in New Issue
Block a user