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
17 KiB
Sofiia CTO Agent — Gaps & Recovery Plan (E)
Generated: 2026-02-26 | P0 = блокуюче | P1 = критичне для vNext | P2 = покращення
Критичне резюме
Що вже готово і може йти в UI: Chat, Voice, Projects CRUD, File upload, Sessions, Dialog Map tree, Ops actions, Node health.
Що не готово і блокує vNext: Tasks/Kanban, Meetings, Dialog Map canvas + Postgres schema, Doc versions, CTO Repo/Ops flow, Supervisor через BFF, Semantic search.
Таблиця прогалин з пріоритетами
| # | Gap | Пріоритет | Складність | Блокує |
|---|---|---|---|---|
| G1 | dialog_nodes/dialog_edges Postgres tables + API |
P0 | Medium | Dialog Map vNext |
| G2 | tasks table + CRUD API + Kanban UI |
P0 | Medium | Projects Board |
| G3 | meetings table + CRUD API |
P0 | Medium | Projects Meetings tab |
| G4 | Supervisor не проксюється через BFF | P0 | Low | CTO workflow access |
| G5 | docs_versions table + API |
P1 | Low | Doc history/rollback |
| G6 | entity_links table + API |
P1 | Low | Cross-entity linking |
| G7 | repo_changesets + repo_patches + PR flow |
P1 | High | CTO code workflow |
| G8 | ops_runs job system (not one-shot) |
P1 | Medium | CTO ops audit trail |
| G9 | Semantic search (Qdrant/Meilisearch) | P1 | Medium | Doc/Project search |
| G10 | NATS attachment.created on upload |
P1 | Low | Parser pipeline hook |
| G11 | DELETE endpoints (projects/docs) |
P1 | Low | CRUD completeness |
| G12 | Real-time WS events for map/tasks | P1 | Medium | Live UI updates |
| G13 | E2EE / confidential mode | P2 | Very High | Privacy |
| G14 | 2-step Plan → Apply for dangerous actions | P2 | High | Safe ops flow |
| G15 | agent_id="l" vs "sofiia" inconsistency |
P1 | Low | Config correctness |
| G16 | dialog_views saved views |
P2 | Low | UX |
| G17 | NODA3 integration | P2 | Medium | AI/ML workstation |
| G18 | Meilisearch deployment | P2 | Low | Full-text search |
| G19 | Privacy Gate middleware (Router) | P2 | High | Confidential mode |
| G20 | Wiki Markdown editor UI | P2 | Medium | Docs/Wiki experience |
| G21 | doc_index_state table + reindex jobs |
P2 | Low | AI doc indexing |
| G22 | Meeting reminders (push/WS) | P2 | Medium | Meetings UX |
| G23 | DELETE /api/nodes/{id} |
P2 | Low | Node management |
| G24 | S3/MinIO для file storage | P2 | High | Scale (replace volume) |
P0 — Блокуючі прогалини (потрібні для vNext)
G1: Dialog Map — Postgres schema + API
Що зроблено: SQLite tree via parent_msg_id. Works for conversation branching.
Чого не вистачає:
- Postgres tables:
dialog_nodes,dialog_edges,dialog_views - API:
GET /api/projects/{id}/dialog-map,POST /api/links - WS event:
dialog_map.updated - Auto-edge creation from NATS events
Recovery plan:
-- Step 1: Add to sofiia-console db.py (SQLite first, Postgres later)
CREATE TABLE IF NOT EXISTS dialog_nodes (
node_id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
node_type TEXT NOT NULL CHECK(node_type IN ('message','task','doc','meeting','agent_run','decision','goal')),
ref_id TEXT NOT NULL, -- FK to actual entity
title TEXT DEFAULT '',
created_at TEXT NOT NULL,
created_by TEXT DEFAULT 'system'
);
CREATE TABLE IF NOT EXISTS dialog_edges (
edge_id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
from_node_id TEXT NOT NULL REFERENCES dialog_nodes(node_id),
to_node_id TEXT NOT NULL REFERENCES dialog_nodes(node_id),
edge_type TEXT NOT NULL CHECK(edge_type IN ('references','resolves','derives_task','updates_doc','schedules','summarizes')),
created_at TEXT NOT NULL,
props TEXT DEFAULT '{}' -- JSON
);
CREATE TABLE IF NOT EXISTS dialog_views (
view_id TEXT PRIMARY KEY,
project_id TEXT NOT NULL,
name TEXT NOT NULL,
filters TEXT DEFAULT '{}',
layout TEXT DEFAULT '{}'
);
# Step 2: New endpoint in docs_router.py
@router.get("/api/projects/{project_id}/dialog-map")
async def get_project_dialog_map(project_id: str):
nodes = await db.get_dialog_nodes(project_id)
edges = await db.get_dialog_edges(project_id)
return {"nodes": nodes, "edges": edges}
@router.post("/api/links")
async def create_link(body: LinkCreate):
# Creates dialog_edge between two entities
...
Оцінка: 4–6 годин роботи.
G2: Tasks + Kanban
Що зроблено: Немає.
Recovery plan:
CREATE TABLE IF NOT EXISTS tasks (
task_id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(project_id),
title TEXT NOT NULL,
description TEXT DEFAULT '',
status TEXT DEFAULT 'backlog' CHECK(status IN ('backlog','in_progress','review','done')),
priority TEXT DEFAULT 'medium',
assignee_id TEXT DEFAULT '',
labels TEXT DEFAULT '[]', -- JSON
due_at TEXT,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
msg_id TEXT -- Optional: link to originating message
);
- API:
GET/POST /api/projects/{id}/tasks,PATCH /api/tasks/{id},DELETE /api/tasks/{id} - UI: Kanban board з drag-drop (можна почати з простим list + status buttons)
- Dialog Map auto-edge:
POST /api/linksafter task creation
Оцінка: 1–2 дні (backend + basic UI).
G3: Meetings
Recovery plan:
CREATE TABLE IF NOT EXISTS meetings (
meeting_id TEXT PRIMARY KEY,
project_id TEXT NOT NULL REFERENCES projects(project_id),
title TEXT NOT NULL,
starts_at TEXT NOT NULL,
duration_min INTEGER DEFAULT 60,
attendees TEXT DEFAULT '[]', -- JSON
location TEXT DEFAULT '',
agenda TEXT DEFAULT '',
created_at TEXT NOT NULL
);
- API:
GET/POST /api/projects/{id}/meetings,PATCH /api/meetings/{id} - UI: simple form (title, date/time, duration, attendees)
- Reminders: Phase 2 (WS push)
Оцінка: 1 день.
G4: Supervisor → BFF proxy
Що зроблено: Supervisor API exists at http://sofiia-supervisor:8080 (або port 9400).
Recovery plan:
# Add to services/sofiia-console/app/main.py:
SUPERVISOR_URL = os.getenv("SUPERVISOR_URL", "http://sofiia-supervisor:8080")
@app.post("/api/supervisor/runs")
async def run_supervisor_graph(body: dict, _auth: str = Depends(require_auth)):
async with httpx.AsyncClient() as c:
resp = await c.post(f"{SUPERVISOR_URL}/v1/graphs/{body['graph']}/runs",
json=body, timeout=60)
return resp.json()
@app.get("/api/supervisor/runs/{run_id}")
async def get_supervisor_run(run_id: str, _auth: str = Depends(require_auth)):
async with httpx.AsyncClient() as c:
resp = await c.get(f"{SUPERVISOR_URL}/v1/runs/{run_id}", timeout=10)
return resp.json()
Оцінка: 30 хвилин.
P1 — Критичні для vNext
G5: Doc versions
CREATE TABLE IF NOT EXISTS doc_versions (
version_id TEXT PRIMARY KEY,
doc_id TEXT NOT NULL REFERENCES documents(doc_id),
content TEXT NOT NULL, -- full text
author_id TEXT DEFAULT 'system',
created_at TEXT NOT NULL
);
# New endpoints in docs_router.py:
# GET /api/projects/{pid}/documents/{did}/versions
# POST /api/projects/{pid}/documents/{did}/restore
Оцінка: 2 години.
G7: Repo Changesets (CTO Code Flow)
Це найскладніша частина. Рекомендація: почати з mock endpoints, потім реалізувати реальну логіку.
Mock endpoint (30 хв):
@app.post("/api/repo/changesets")
async def create_changeset_mock(body: dict, _auth=Depends(require_auth)):
# Mock: store in SQLite, return changeset_id
cs_id = str(uuid.uuid4())
# await db.save_changeset(cs_id, body)
return {"changeset_id": cs_id, "status": "draft", "mock": True}
Реальна реалізація (2–3 дні):
CREATE TABLE repo_changesets (
cs_id TEXT PRIMARY KEY,
project_id TEXT,
repo TEXT NOT NULL, -- e.g., "github.com/IvanTytar/microdao-daarion"
base_ref TEXT NOT NULL, -- branch/commit
intent TEXT NOT NULL,
risk_level TEXT DEFAULT 'low',
status TEXT DEFAULT 'draft',
created_by TEXT,
created_at TEXT NOT NULL
);
CREATE TABLE repo_patches (
patch_id TEXT PRIMARY KEY,
cs_id TEXT NOT NULL REFERENCES repo_changesets(cs_id),
file_path TEXT NOT NULL,
patch_text TEXT NOT NULL, -- unified diff
created_at TEXT NOT NULL
);
CREATE TABLE pull_requests (
pr_id TEXT PRIMARY KEY,
cs_id TEXT NOT NULL REFERENCES repo_changesets(cs_id),
provider TEXT DEFAULT 'github', -- github/gitlab/gitea
pr_url TEXT,
pr_number INTEGER,
status TEXT DEFAULT 'draft',
created_at TEXT NOT NULL
);
G8: Ops Runs (Job System)
Поточний /api/ops/run — one-shot dispatch. Потрібен job tracking.
CREATE TABLE ops_runs (
run_id TEXT PRIMARY KEY,
project_id TEXT,
node_id TEXT NOT NULL, -- noda1/noda2
action TEXT NOT NULL, -- з allowlist
params TEXT DEFAULT '{}', -- JSON
dry_run INTEGER DEFAULT 1,
status TEXT DEFAULT 'pending', -- pending/running/success/failed
result TEXT DEFAULT '',
started_at TEXT,
finished_at TEXT,
created_by TEXT
);
API:
POST /api/ops/runs(створити job, dry_run=true за замовч.)GET /api/ops/runs/{id}(статус)GET /api/ops/runs?project_id=&limit=20(список)
Оцінка: 4 години (backend) + 2 год (UI list).
G10: NATS attachment.created
Одна зміна в docs_router.py:
# After successful file save:
try:
import nats
nc = await nats.connect(NATS_URL)
await nc.publish(f"attachment.created.{mime_category}",
json.dumps({"file_id": file_id, "doc_id": doc_id, ...}).encode())
await nc.close()
except Exception:
pass # best-effort
Оцінка: 1 година.
G15: agent_id "l" vs "sofiia"
У services/router/router-config.yml для NODA2:
# Check if there's "l:" entry that should be "sofiia:"
Action: знайти і замінити "l" → "sofiia" у router-config відповідної ноди.
Оцінка: 15 хвилин.
P2 — Покращення
G13: E2EE (confidential mode)
Складність: Дуже висока. Потребує:
- Client-side key generation (WebCrypto API)
- Server-side: store only ciphertext + key_id
- Router Privacy Gate middleware
- Dialog Map: тільки user-created edges (не semantic auto-edges)
- Search: тільки metadata, не plaintext
Рекомендація: Не реалізовувати до завершення Projects + Dialog Map. Спочатку mode=public тільки.
G20: Wiki Markdown Editor
Потрібна бібліотека (CodeMirror / Monaco / Tiptap). Для Phase 1 — textarea з preview.
<!-- Simple Phase 1 wiki editor -->
<div id="wikiEditor">
<textarea id="wikiContent" placeholder="# Сторінка wiki..."></textarea>
<div id="wikiPreview" class="markdown-preview"></div>
</div>
Quick Wins (до 2 годин кожен)
| # | Quick Win | Час | Цінність |
|---|---|---|---|
| QW1 | DELETE /api/projects/{id} |
15 хв | CRUD completeness |
| QW2 | DELETE /api/projects/{id}/documents/{did} |
15 хв | CRUD completeness |
| QW3 | BFF proxy до Supervisor (G4) | 30 хв | CTO workflow access |
| QW4 | Mock /api/repo/changesets |
30 хв | UI CTO panel development |
| QW5 | Mock /api/ops/runs |
30 хв | UI CTO panel development |
| QW6 | docs_versions table + API (G5) |
2 год | Doc history |
| QW7 | USE_EMBEDDINGS=true + Qdrant ingest |
1 год | Semantic search |
| QW8 | agent_id "l" → "sofiia" fix |
15 хв | Config consistency |
| QW9 | NATS attachment.created on upload |
1 год | Parser pipeline |
| QW10 | WS dialog_map.updated basic event |
1 год | Live map refresh |
Повний план відновлення (поетапно)
Тиждень 1: Stabilize & Quick Wins
Day 1–2:
- QW1, QW2, QW3, QW8 (CRUD + Supervisor proxy + agent_id fix)
- Деплой на NODA2, verify через http://localhost:8002
Day 3–4:
- G2: tasks table + basic API + simple list UI
- G3: meetings table + basic form UI
Day 5:
- G5: docs_versions + API
- G10: NATS attachment.created
- QW4, QW5: mock changeset/ops_run endpoints for UI
Тиждень 2: Dialog Map + CTO Panel
Day 1–2:
- G1: dialog_nodes/edges tables + API
- WS event: dialog_map.updated
Day 3–4:
- UI: Dialog Map canvas (D3 tree → force graph)
- Entity links UI (drag edge between nodes)
Day 5:
- G8: ops_runs job system
- UI: CTO Ops panel (list + status)
Тиждень 3: Advanced Features
- G7: Repo changesets (real implementation)
- G9: USE_EMBEDDINGS=true + semantic search
- G12: Full real-time WS events (tasks, docs, meetings)
- Kanban drag-drop UI
- Doc versions diff viewer
Тиждень 4+: Scale & Polish
- G14: 2-step Plan → Apply
- G20: Wiki Markdown editor
- G22: Meeting reminders
- G24: S3/MinIO for file storage
- G13: E2EE (only when everything else is stable)
5 Найбільш Критичних Прогалин
dialog_nodes/edges+ project-level Dialog Map API — без цього vNext граф неможливий- Tasks/Kanban — Projects без задач = тільки файлосховище
- Meetings — Projects без зустрічей = неповний workflow
- Supervisor не проксюється через BFF — CTO не може запускати LangGraph runs з UI
- Repo changesets / CTO code flow — Sofiia не може "пропонувати PR" як structured artifact
5 Найбільш Готових Частин для UI
- Chat + Voice — повністю готово, production-grade (Phase 2 streaming, HA, SLO, alerts)
- Projects + Documents + File Upload — CRUD, search, sessions — все є
- Dialog Map tree —
GET /api/sessions/{id}/mapповертає nodes/edges - Ops Actions — risk/pressure/backlog/notion/release — все є через
/api/ops/run - Node Health Dashboard — multi-node, SSH, WebSocket realtime — все є
3 Рекомендації "Зробити Негайно"
1. Зберегти контекст у Dialog Map
Найпростіший спосіб не "загубити" поточний дизайн — додати dialog_nodes/edges tables у db.py прямо зараз (схема вже описана вище). Навіть якщо UI ще не готовий, дані почнуть накопичуватись від поточних повідомлень.
2. Proxy Supervisor через BFF
30 хвилин роботи, але це дасть Sofiia доступ до alert_triage, incident_triage, postmortem_draft, release_check прямо з UI Console — не тільки через Telegram.
3. Нормалізувати agent_id
Знайти і виправити "l" → "sofiia" у конфігурації NODA2. Це унеможливить silent routing failures де Router не знаходить агента і тихо fallbacks до дефолту.
Next Actions for UI Team (1–2 days)
- Розгорнути і протестувати поточний стек на NODA2 —
http://localhost:8002/вже повністю робочий - Реалізувати QW1–QW5 (прості DELETE + Supervisor proxy + mock endpoints) — 2–3 год
- Додати
tasksіmeetingstables уdb.pyта відповідні endpoints уdocs_router.py - Додати
dialog_nodes/edgesуdb.py(DDL вище) і endpointGET /api/projects/{id}/dialog-map - Тестувати через
tests/test_sofiia_docs.py— всі 28 тестів мають пройти - Оновити
docker-compose.node2-sofiia.ymlзSUPERVISOR_URLenv var - Перевірити що
ops/voice_ha_smoke.shпроходить після деплою - Прочитати
docs/architecture_inventory/(7 файлів) для повного контексту поточного стеку - Використовувати
ops/fabric_preflight.shперед кожним деплоєм (preflight-first policy) - Щотижня: запускати
ops/fabric_snapshot.py --saveі commit результат — щоб мати baseline для drift detection