Config policies (16 files): alert_routing, architecture_pressure, backlog, cost_weights, data_governance, incident_escalation, incident_intelligence, network_allowlist, nodes_registry, observability_sources, rbac_tools_matrix, release_gate, risk_attribution, risk_policy, slo_policy, tool_limits, tools_rollout Ops (22 files): Caddyfile, calendar compose, grafana voice dashboard, deployments/incidents logs, runbooks for alerts/audit/backlog/incidents/sofiia/voice, cron jobs, scripts (alert_triage, audit_cleanup, migrate_*, governance, schedule), task_registry, voice alerts/ha/latency/policy Docs (30+ files): HUMANIZED_STEPAN v2.7-v3 changelogs and runbooks, NODA1/NODA2 status and setup, audit index and traces, backlog, incident, supervisor, tools, voice, opencode, release, risk, aistalk, spacebot Made-with: Cursor
8.5 KiB
drift_analyzer_tool
Drift Analyzer — 6-й gate у release_check
Знаходить розбіжності між "джерелами правди" (docs/inventory/config) та фактичним станом repo.
Огляд
drift_analyzer_tool — детерміністичний (без LLM), read-only аналізатор drift у 4 категоріях.
| Категорія | Джерело правди | Факт | Приклад drift |
|---|---|---|---|
| services | inventory_services.csv / 01_SERVICE_CATALOG.md |
docker-compose*.yml |
DEPLOYED сервіс відсутній у compose |
| openapi | docs/contracts/*.openapi.yaml |
FastAPI route decorators у коді | Endpoint у spec але нема в коді |
| nats | inventory_nats_topics.csv |
nc.publish/subscribe у коді |
Subject у коді не задокументований |
| tools | config/tools_rollout.yml + rbac_tools_matrix.yml |
Handlers у tool_manager.py |
Tool у rollout але нема handler |
Використання
Через агента (OpenCode / Telegram)
"Запусти drift аналіз"
"Перевір drift для категорій tools та openapi"
"Drift check перед релізом"
Через execute_tool
{
"action": "analyze",
"categories": ["services", "openapi", "nats", "tools"],
"timeout_sec": 25
}
Через release_check (Gate 6, optional)
{
"action": "start_task",
"params": {
"task_id": "release_check",
"inputs": {
"service_name": "router",
"run_drift": true,
"drift_categories": ["openapi", "tools"],
"drift_timeout_sec": 20
}
}
}
Параметри
| Параметр | Тип | Обов'язковий | Опис |
|---|---|---|---|
action |
"analyze" |
✅ | Єдина дія |
categories |
array | — | Підмножина ["services","openapi","nats","tools"] (default: всі) |
timeout_sec |
number | — | Таймаут в секундах (default: 25, max: 30) |
Формат відповіді
{
"pass": false,
"summary": "❌ Drift analysis FAILED. 2 error(s), 1 warning(s).",
"stats": {
"errors": 2,
"warnings": 1,
"infos": 0,
"skipped": [],
"items_checked": {
"services": 42,
"openapi": 18,
"tools": 65
},
"elapsed_ms": 1234.5,
"by_category": { "...": "..." }
},
"findings": [
{
"category": "tools",
"severity": "error",
"id": "DRIFT-TOOLS-001",
"title": "Tool 'fake_tool_x' in tools_rollout.yml but no handler in tool_manager.py",
"evidence": {
"path": "config/tools_rollout.yml",
"details": "'fake_tool_x' referenced in rollout groups but missing from KNOWN_TOOL_HANDLERS"
},
"recommended_fix": "Add handler for 'fake_tool_x' in tool_manager.py execute_tool dispatch, or remove from rollout."
}
]
}
Pass/Fail правило
| Умова | pass |
|---|---|
Будь-який severity: error |
false |
Тільки warning / info |
true |
| Категорія відсутня (skipped) | не впливає |
Категорії деталі
1. services — Service Catalog vs docker-compose
Джерела:
- A:
docs/architecture_inventory/inventory_services.csv→ полеtype(DEPLOYED/DEFINED/...) - B: всі
docker-compose*.ymlу repo root +infra/compose/docker-compose.yml
Findings:
| ID | Severity | Умова |
|---|---|---|
DRIFT-SVC-001 |
error | Сервіс DEPLOYED у catalog, але відсутній в compose |
DRIFT-SVC-002 |
warning | Сервіс є в compose, але не в catalog |
Normalization: my-svc ↔ my_svc (dash/underscore equivalence).
2. openapi — API Spec vs Code Routes
Джерела:
- A:
docs/contracts/*.openapi.yamlта будь-якіopenapi*.yaml/yml/jsonу repo - B: Python файли —
@app.get(...),@router.post(...),.add_api_route(...)
Findings:
| ID | Severity | Умова |
|---|---|---|
DRIFT-OAS-001 |
error | Path у OpenAPI spec але не знайдено в коді |
DRIFT-OAS-002 |
error | Path /v1/* є в коді але не описаний у spec |
DRIFT-OAS-003 |
warning | Method mismatch для тієї самої path |
Normalization: trailing slash, lowercase path comparison.
Скоп коду: тільки /v1/ routes перевіряються для OAS-002.
3. nats — Subject Inventory vs Code Usage
Джерела:
- A:
docs/architecture_inventory/inventory_nats_topics.csv(полеsubject) - B: regex пошук
nc.publish(...),nc.subscribe(...),subject=...у.pyфайлах
Findings:
| ID | Severity | Умова |
|---|---|---|
DRIFT-NATS-001 |
warning | Subject використовується в коді але відсутній у inventory |
DRIFT-NATS-002 |
info | Subject у inventory але не знайдено в коді (можливо legacy) |
Wildcard matching: agent.run.{agent_id} → agent.run.* → agent.run.>.
Skipped: якщо inventory_nats_topics.csv відсутній — категорія skipped, gate не падає.
4. tools — Rollout/Matrix vs Handlers
Джерела:
- A:
config/tools_rollout.yml(всі tool-назви у groups, з @group expand) - B:
config/rbac_tools_matrix.yml(секціяtools:) - C:
KNOWN_TOOL_HANDLERSуdrift_analyzer.py(compile-time список) - D:
agent_tools_config.effective_toolsдля ролейagent_defaultіagent_cto
Findings:
| ID | Severity | Умова |
|---|---|---|
DRIFT-TOOLS-001 |
error | Tool у rollout але нема handler |
DRIFT-TOOLS-002 |
warning | Handler є але tool відсутній у RBAC matrix |
DRIFT-TOOLS-003 |
warning | Tool у matrix але ніколи не потрапляє в effective_tools |
Maintenance: при додаванні нового tool handler — оновіть KNOWN_TOOL_HANDLERS у drift_analyzer.py.
Безпека
- Read-only: не записує нічого у repo
- Path traversal: сканує тільки всередині
REPO_ROOT - Excluded dirs:
node_modules,.git,venv*,__pycache__,dist,build,rollback_backups - File size limit: max 256KB per file
- File count limit: max 300 files per category scan
- Secret redaction: evidence маскується
_redact_evidence()перед поверненням - Governance: проходить через
ToolGovernance.pre_call/post_call(RBAC, limits, audit)
RBAC Entitlements
| Entitlement | Хто | Що дозволяє |
|---|---|---|
tools.drift.read |
agent_cto, agent_oncall |
Запускати drift analyze |
tools.drift.gate |
agent_cto |
Запускати drift у release gate |
Limits (config/tool_limits.yml)
| Параметр | Значення |
|---|---|
timeout_ms |
30 000 (30s) |
max_chars_in |
5 000 |
max_bytes_out |
524 288 (512KB) |
rate_limit_rpm |
5 |
concurrency |
1 |
Оновлення KNOWN_TOOL_HANDLERS
Коли додається новий tool handler у tool_manager.py:
- Додай tool name до
KNOWN_TOOL_HANDLERSуdrift_analyzer.py - Додай tool до
config/tools_rollout.yml(потрібна роль) - Додай tool до
config/rbac_tools_matrix.yml(actions + entitlements) - Запусти
pytest tests/test_drift_analyzer.py::TestToolsDriftщоб перевірити
# drift_analyzer.py
KNOWN_TOOL_HANDLERS: FrozenSet[str] = frozenset({
...,
"my_new_tool", # add here
})
Файли
| Файл | Призначення |
|---|---|
services/router/drift_analyzer.py |
Вся логіка аналізу (4 категорії) |
services/router/tool_manager.py |
Handler _drift_analyzer_tool + TOOL_DEFINITIONS |
services/router/release_check_runner.py |
Gate 6 _run_drift() |
config/tools_rollout.yml |
cto_tools включає drift_analyzer_tool |
config/rbac_tools_matrix.yml |
drift_analyzer_tool actions + tools.drift.* entitlements |
config/tool_limits.yml |
drift_analyzer_tool limits |
tests/test_drift_analyzer.py |
29 тестів + fixtures |