# Dev Contract: Preflight-first, Node-specific, Zero Assumptions (v0.1) ## 0. Заборона припущень Будь-яка дія/пропозиція щодо моделей, провайдерів, портів, compose або routing **заборонена без preflight snapshot** з цільової ноди. Без snapshot — ні коміт, ні деплой, ні рекомендація. ## 1. Обов'язковий Preflight Snapshot ### 1.1 Збір Перед кожною зміною запустити `ops/fabric_snapshot.py` на цільовій ноді: ```bash # NODA2 (локально) python3 ops/fabric_snapshot.py --node-id NODA2 # NODA1 (через SSH tunnel) ssh -L 18099:127.0.0.1:8099 -L 18109:127.0.0.1:8109 \ -L 19102:127.0.0.1:9102 -fN root@144.76.224.179 python3 ops/fabric_snapshot.py --node-id noda1 \ --ncs http://127.0.0.1:18099 --worker http://127.0.0.1:18109 \ --router http://127.0.0.1:19102 --ollama http://127.0.0.1:21434 \ --ssh root@144.76.224.179 ``` Snapshot зберігається в `ops/preflight_snapshots/_.json`. ### 1.2 Endpoints (всі обов'язкові) | Endpoint | Що перевіряє | |---|---| | `NCS /capabilities` | served_models, capabilities, node_load, runtimes | | `NCS /capabilities/caps` | capability flags (stt/tts/ocr/image) | | `NCS /capabilities/installed` | installed_artifacts (disk scan) | | `node-worker /caps` | provider flags (STT_PROVIDER, TTS_PROVIDER...) | | `node-worker /healthz` | NATS connection, concurrency | | `Router /v1/capabilities` | global view (всі ноди, capabilities_by_node) | | `Router /v1/models` | глобальний пул моделей | | `Ollama /api/tags` | реальні Ollama моделі на ноді | | `docker ps` | список контейнерів | ### 1.3 Quality gate (must-pass) Snapshot валідний тільки якщо: - Router healthy - NCS healthy - Node-worker healthy - `capabilities_by_node` містить цільову ноду - `served_models` не порожній (або є пояснення "compute-less node") ## 2. Served ≠ Installed ### 2.1 Два шари правди | Шар | Джерело | Використання | |---|---|---| | **served_models** | NCS /capabilities → runtimes (Ollama/llama-server/...) | Routing, model selection, offload | | **installed_artifacts** | NCS /capabilities/installed → disk scan | Інвентаризація, recommendations, cleanup | Модель на диску — це **candidate**, не "доступна". ### 2.2 Заборона hardcode - Заборонено комітити `models:` списки в swapper/router configs - Дозволено: policy-only `prefer` (тип/клас моделі), але не імена, крім критичних cloud SKU ## 3. Capability-first routing ### 3.1 Routing rules Router обирає **ноду**, не "модель": 1. `find_nodes_with_capability(cap)` — тільки ноди де cap=true 2. `require_fresh_caps(ttl=30)` — preflight guard 3. Circuit breaker — виключити ноди з відкритим breaker 4. Load scoring — `inflight * 10 + (100 if mem_pressure=high)` 5. Offload через NATS `node.{id}.{cap}.request` ### 3.2 Fail fast Якщо capability відсутня на всіх нодах — **fail fast** з явним повідомленням: ```json {"error": "No node with capability 'stt' available", "capabilities_by_node": {...}} ``` Заборонено: тихий fallback на cloud без WARNING log. ### 3.3 Нодозалежність STT/TTS/OCR/Image **можуть бути різними** на різних нодах: - NODA2: `STT_PROVIDER=mlx_whisper`, `TTS_PROVIDER=mlx_kokoro` - NODA1: `STT_PROVIDER=none`, `OCR_PROVIDER=vision_prompted` Вмикання capability = тільки через env flags в node-worker → `/caps`. ## 4. Безпечний контроль змін ### 4.1 План змін (обов'язково) Перед зміною відповісти на: 1. **Що** міняємо 2. **Чому** 3. **Що може зламатися** 4. **Як перевіряємо** (postflight) 5. **Rollback** (точна команда) ### 4.2 Rollback Кожна зміна має мати: - git commit hash / tag - одну команду rollback (`docker compose up -d --force-recreate `, `git checkout`) ## 5. Postflight Після кожної зміни — повторний snapshot і порівняння: - served_models count (не зменшився без причини) - capabilities map (нові = очікувані) - container count - error rate (prom_metrics) ## 6. Жодних прихованих fallback - Невідомий профіль або відсутній API key → WARNING + deterministic fallback на `agent.fallback_llm` - Заборонено: "мовчки пішли в DeepSeek" без логу ## 7. Канонічні артефакти | Артефакт | Призначення | |---|---| | `ops/fabric_snapshot.py` | Збір повного snapshot | | `ops/fabric_preflight.sh` | Quick check + snapshot save | | `ops/preflight_snapshots/` | Зберігання snapshots | | `docs/fabric_contract.md` | Цей контракт | ## Реальний стан (snapshot 2026-02-27) ### NODA1 (production) - **49 контейнерів** (gateway, router, memory, qdrant, neo4j, redis, postgres, minio, rag, swapper, farmos, brand-*, oneok-*, plant-vision, crawl4ai, grafana, prometheus...) - **5 Ollama моделей**: qwen3-vl:8b (vision), qwen3:8b, qwen3.5:27b-q4_K_M, smollm2:135m, deepseek-v3.1:671b-cloud - **14 Telegram агентів** active - **NCS P3.5 не задеплоєний** — capabilities flags відсутні, installed_artifacts = 0 - `swapper=disabled`, worker NATS connected ### NODA2 (development) - **14 контейнерів** (router, node-worker, node-capabilities, nats, gateway, memory, qdrant, postgres, neo4j, redis, open-webui, sofiia-console, swapper) - **13 served моделей** (Ollama: 12 + llama_server: 1) - **29 installed artifacts** на диску (150.3GB LLM + 0.3GB TTS kokoro-v1_0) - **capabilities**: llm=Y, vision=Y, ocr=Y, stt=N, tts=N, image=N - `OCR_PROVIDER=vision_prompted`