Files
microdao-daarion/docs/audit/gaps_and_recovery_plan.md
Apple 67225a39fa docs(platform): add policy configs, runbooks, ops scripts and platform documentation
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
2026-03-03 07:14:53 -08:00

17 KiB
Raw Permalink Blame History

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
    ...

Оцінка: 46 годин роботи.


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/links after task creation

Оцінка: 12 дні (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}

Реальна реалізація (23 дні):

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)

Складність: Дуже висока. Потребує:

  1. Client-side key generation (WebCrypto API)
  2. Server-side: store only ciphertext + key_id
  3. Router Privacy Gate middleware
  4. Dialog Map: тільки user-created edges (не semantic auto-edges)
  5. 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 12:
  - QW1, QW2, QW3, QW8 (CRUD + Supervisor proxy + agent_id fix)
  - Деплой на NODA2, verify через http://localhost:8002

Day 34:
  - 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 12:
  - G1: dialog_nodes/edges tables + API
  - WS event: dialog_map.updated

Day 34:
  - 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 Найбільш Критичних Прогалин

  1. dialog_nodes/edges + project-level Dialog Map API — без цього vNext граф неможливий
  2. Tasks/Kanban — Projects без задач = тільки файлосховище
  3. Meetings — Projects без зустрічей = неповний workflow
  4. Supervisor не проксюється через BFF — CTO не може запускати LangGraph runs з UI
  5. Repo changesets / CTO code flow — Sofiia не може "пропонувати PR" як structured artifact

5 Найбільш Готових Частин для UI

  1. Chat + Voice — повністю готово, production-grade (Phase 2 streaming, HA, SLO, alerts)
  2. Projects + Documents + File Upload — CRUD, search, sessions — все є
  3. Dialog Map treeGET /api/sessions/{id}/map повертає nodes/edges
  4. Ops Actions — risk/pressure/backlog/notion/release — все є через /api/ops/run
  5. 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 (12 days)

  1. Розгорнути і протестувати поточний стек на NODA2 — http://localhost:8002/ вже повністю робочий
  2. Реалізувати QW1QW5 (прості DELETE + Supervisor proxy + mock endpoints) — 23 год
  3. Додати tasks і meetings tables у db.py та відповідні endpoints у docs_router.py
  4. Додати dialog_nodes/edges у db.py (DDL вище) і endpoint GET /api/projects/{id}/dialog-map
  5. Тестувати через tests/test_sofiia_docs.py — всі 28 тестів мають пройти
  6. Оновити docker-compose.node2-sofiia.yml з SUPERVISOR_URL env var
  7. Перевірити що ops/voice_ha_smoke.sh проходить після деплою
  8. Прочитати docs/architecture_inventory/ (7 файлів) для повного контексту поточного стеку
  9. Використовувати ops/fabric_preflight.sh перед кожним деплоєм (preflight-first policy)
  10. Щотижня: запускати ops/fabric_snapshot.py --save і commit результат — щоб мати baseline для drift detection