| 1 | # ADR-0001: Binding architecture — bridge MVP and native production tier |
| 2 | |
| 3 | **Статус:** принято |
| 4 | **Дата:** 2026-06-06 |
| 5 | **Репозиторий:** [AI-Guiders/pywitdb](https://github.com/AI-Guiders/pywitdb) |
| 6 | **Upstream:** [WitDatabase](https://github.com/dmitrat/WitDatabase) (`OutWit.Database.Core`) |
| 7 | |
| 8 | --- |
| 9 | |
| 10 | ## Решение (одной фразой) |
| 11 | |
| 12 | **Двухфазная архитектура:** MVP — **JSON subprocess bridge** через маленький .NET CLI (`witdb-bridge`); production — **NativeAOT C ABI + `ctypes`** с wheels per-platform. Оба тира читают/пишут тот же `.witdb`; Python API один. |
| 13 | |
| 14 | --- |
| 15 | |
| 16 | ## Контекст |
| 17 | |
| 18 | - WitDatabase — source of truth для формата `.witdb` и ACID semantics (.NET, NuGet). |
| 19 | - Python-экосистема (agents, lab, ETL) нуждается в официальном пакете **pywitdb** (PyPI), не в subprocess-хаках и не в форке формата. |
| 20 | - CascadeIDE / Intercom уже используют `.witdb` через `UseWitDb`; cross-lang сценарий: C# пишет store, Python читает. |
| 21 | - В репозитории **AI-Guiders** — отдельный продукт; замена sqlite в CASA — out of scope (отдельный ADR при необходимости). |
| 22 | |
| 23 | ## Цели v0 |
| 24 | |
| 25 | | API | Семантика | |
| 26 | |-----|-----------| |
| 27 | | `open(path, password=...)` | Открыть/создать `.witdb` | |
| 28 | | `get` / `put` / `delete` | KV по bytes-ключам | |
| 29 | | `transaction()` | ACID batch; rollback on exception | |
| 30 | | context manager | `close` on exit | |
| 31 | |
| 32 | SQL, EF, Studio UI — **не** в v0. |
| 33 | |
| 34 | --- |
| 35 | |
| 36 | ## Рассмотренные варианты |
| 37 | |
| 38 | ### A. JSON subprocess bridge (`.NET` CLI) |
| 39 | |
| 40 | Python spawn `witdb-bridge`; протокол — **newline-delimited JSON** (NDJSON) по stdin/stdout. |
| 41 | |
| 42 | **Плюсы:** быстрый MVP; без изменений в WitDatabase Core; контрактные тесты C#↔Python сразу; один CLI для dev/CI. |
| 43 | **Минусы:** latency; нужен .NET runtime рядом; отдельный процесс на store (или connection pool позже). |
| 44 | |
| 45 | ### B. `pythonnet` / embedded CLR |
| 46 | |
| 47 | Python грузит .NET runtime in-process. |
| 48 | |
| 49 | **Плюсы:** без subprocess overhead. |
| 50 | **Минусы:** тяжёлая зависимость; fragile wheels; плохой DX на Linux/macOS CI; не цель PyPI «лёгкий pip install». |
| 51 | |
| 52 | ### C. gRPC / HTTP sidecar |
| 53 | |
| 54 | Отдельный long-lived .NET service. |
| 55 | |
| 56 | **Плюсы:** масштабирование, remote store позже. |
| 57 | **Минусы:** overkill для embedded local `.witdb`; ops burden для v0. |
| 58 | |
| 59 | ### D. NativeAOT C export + `ctypes` |
| 60 | |
| 61 | WitDatabase (upstream PR) экспортирует стабильный C ABI; pywitdb публикует platform wheels. |
| 62 | |
| 63 | **Плюсы:** prod latency; нет .NET в Python-процессе; стандартный путь для Python native extensions. |
| 64 | **Минусы:** работа в upstream; ABI versioning; matrix win/linux/macOS в CI. |
| 65 | |
| 66 | ### E. Чистый Python reimplementation формата |
| 67 | |
| 68 | **Отклонено:** дублирует source of truth, риск drift от `.witdb`. |
| 69 | |
| 70 | --- |
| 71 | |
| 72 | ## Принятое решение |
| 73 | |
| 74 | | Tier | Роль (2026-06) | Статус | |
| 75 | |------|----------------|--------| |
| 76 | | **D — Native ctypes** | **Prod default** (`backend="auto"` → native если DLL/wheel есть) | ✅ win-x64 publish + pytest | |
| 77 | | **A — JSON bridge** | **Dev / CI / fallback** (нет native под RID; contract tests без AOT matrix) | ✅ `bridge/WitDbBridge` | |
| 78 | |
| 79 | `pythonnet` (B) и sidecar (C) — **не** выбираем. |
| 80 | |
| 81 | **Prod path дальше:** per-RID NativeAOT publish → PyPI wheels; upstream PR `OutWit.Database.Native` → `dmitrat/WitDatabase`. Bridge в release не обязателен. |
| 82 | |
| 83 | ### MVP: `witdb-bridge` CLI |
| 84 | |
| 85 | Расположение: `bridge/WitDbBridge/` в репо **AI-Guiders/pywitdb**. |
| 86 | |
| 87 | - Target: `net10.0` (aligned с Core multi-TFM). |
| 88 | - Project ref: `OutWit.Database.Core` + `BouncyCastle` (submodule witdatabase в monorepo). |
| 89 | - Один процесс = один open store; Python держит subprocess lifecycle. |
| 90 | |
| 91 | **Протокол (v1):** запрос/ответ — одна JSON-строка на сообщение. |
| 92 | |
| 93 | ```json |
| 94 | {"id": 1, "op": "open", "path": "store.witdb", "password": null} |
| 95 | {"id": 1, "ok": true} |
| 96 | {"id": 2, "op": "put", "key": "bWV0YTpmdg==", "value": "OTQyNjY="} |
| 97 | {"id": 2, "ok": true} |
| 98 | {"id": 3, "op": "get", "key": "bWV0YTpmdg=="} |
| 99 | {"id": 3, "ok": true, "value": "OTQyNjY=", "found": true} |
| 100 | ``` |
| 101 | |
| 102 | - `key` / `value`: base64 bytes. |
| 103 | - Ошибки: `{"id": N, "ok": false, "error": {"code": "...", "message": "..."}}`. |
| 104 | - `transaction`: `begin` → ops → `commit` | `rollback` (nested не поддерживаем в v0). |
| 105 | |
| 106 | Python: `pywitdb._bridge` (`BridgeClient`, `BridgeDatabase`). |
| 107 | |
| 108 | ### Native tier (prod) |
| 109 | |
| 110 | - Fork/upstream: `OutWit.Database.Native` + `include/witdb.h` (ADR-0003). |
| 111 | - pywitdb: `pywitdb._native` + bundled wheels per RID (CI TBD). |
| 112 | - `open(..., backend="auto")`: **native first**, иначе bridge если `witdb-bridge` собран. |
| 113 | |
| 114 | ABI versioning: `WITDB_ABI_VERSION` в заголовке; pywitdb pin minor at build time. |
| 115 | |
| 116 | --- |
| 117 | |
| 118 | ## PyPI и именование |
| 119 | |
| 120 | | Решение | Значение | |
| 121 | |---------|----------| |
| 122 | | **PyPI package name** | `pywitdb` (зафиксировано) | |
| 123 | | **GitHub** | `AI-Guiders/pywitdb` only | |
| 124 | | **Bridge executable** | `witdb-bridge` (shipped as .NET tool or alongside package in `bridge/` artifacts) | |
| 125 | |
| 126 | Имя `witdatabase` на PyPI — **не** используем (коллизия с брендом upstream). |
| 127 | |
| 128 | --- |
| 129 | |
| 130 | ## Тестирование и CI |
| 131 | |
| 132 | 1. **pytest** (Python): unit + integration against bridge. |
| 133 | 2. **Contract fixture (C#):** test project пишет `.witdb` через `OutWit.Database.Core`; Python читает через pywitdb — тот же файл, те же KV. |
| 134 | 3. CI matrix: `windows-latest`, `ubuntu-latest` (bridge MVP); native tier добавляет per-RID jobs позже. |
| 135 | |
| 136 | --- |
| 137 | |
| 138 | ## Последствия |
| 139 | |
| 140 | ### Положительные |
| 141 | |
| 142 | - MVP без блокера на upstream PR. |
| 143 | - Один Python API; смена backend прозрачна для callers. |
| 144 | - Cross-lang контракт проверяется в CI до native tier. |
| 145 | |
| 146 | ### Отрицательные / долг |
| 147 | |
| 148 | - Bridge требует .NET SDK/runtime в dev и CI. |
| 149 | - Два code path до v1 — поддерживать parity tests. |
| 150 | - Native tier = отдельный трек PR в WitDatabase (не в AI-Guiders org). |
| 151 | |
| 152 | ### Non-goals (повтор) |
| 153 | |
| 154 | - Не подменять `agent_field.db` / sqlite в ca-substrate-agent без ADR. |
| 155 | - Не тащить EF Core в Python. |
| 156 | - Не дублировать WitDatabase Studio. |
| 157 | |
| 158 | --- |
| 159 | |
| 160 | ## План реализации |
| 161 | |
| 162 | | # | Задача | Репо | |
| 163 | |---|--------|------| |
| 164 | | 1 | Bootstrap pywitdb layout, этот ADR | AI-Guiders/pywitdb | |
| 165 | | 2 | `witdb-bridge` CLI + NDJSON protocol | ✅ AI-Guiders/pywitdb | |
| 166 | | 3 | `pywitdb.open` / KV / `transaction` (bridge + native) | ✅ | |
| 167 | | 4 | C# fixture + pytest contract (T1a/T1b) | ✅ `tests/test_contract.py` | |
| 168 | | 5 | ADR-0002: provider keys при reopen | ✅ | |
| 169 | | 6 | ADR-0003: C ABI + Native | ✅ fork; upstream PR pending | |
| 170 | | 7 | PyPI wheels: linux-x64, osx-arm64, … | pending | |
| 171 | | 8 | ADR-0004: SQL (WitSQL) для CASA journal | ✅ `0004-sql-layer-and-casa-journal.md` → implement | |
| 172 | |
| 173 | --- |
| 174 | |
| 175 | ## Ссылки |
| 176 | |
| 177 | - Канон-карточка: `knowledge/work/projects/door-to-singularity/pywitdb/README.md` |
| 178 | - WitDatabase NuGet: `OutWit.Database.Core` |
| 179 | |