From 533aa99592475db806699e460e4de1b63b740469 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Wed, 26 Aug 2026 20:49:24 +0300 Subject: [PATCH] =?UTF-8?q?docs:=20History=20=E2=80=94=20=D1=87=D0=B8?= =?UTF-8?q?=D1=81=D1=82=D0=BA=D0=B0=20legacy-upload=20+=20=D1=81=D0=B2?= =?UTF-8?q?=D0=BE=D0=B4=D0=BA=D0=B0=20=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B8?= =?UTF-8?q?=202026-08-26?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- History/2026-08-26-cleanup-legacy-upload.md | 42 +++++++++++++++ History/2026-08-26-session-summary.md | 60 +++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 History/2026-08-26-cleanup-legacy-upload.md create mode 100644 History/2026-08-26-session-summary.md diff --git a/History/2026-08-26-cleanup-legacy-upload.md b/History/2026-08-26-cleanup-legacy-upload.md new file mode 100644 index 0000000..e0a2b5b --- /dev/null +++ b/History/2026-08-26-cleanup-legacy-upload.md @@ -0,0 +1,42 @@ +# Чистка: удалён мёртвый legacy-код загрузки напрямую (2026-08-26) + +## Контекст + +При разборе стресс-тестов выяснилось: я (AI) гонял HTTP-стресс через `POST /upload` +(прямой multipart), тогда как реальная загрузка в проде идёт через **ВМ-буфер** +(`PUT` на ВМ WebDAV → `POST /api/upload_refs` → `contracts_upload_sink`). +Прямой `/upload` — мёртвый legacy-эндпоинт, который фронт не вызывает. + +## Что удалено (коммит `aa58522`, −176 строк) + +- `site/routes/upload_bp.py`: + - эндпоинты `POST /upload` и `POST /unzip-upload`; + - функции `_check_ext`, `_unzip`, константа `ALLOWED`; + - пустой blueprint `upload_bp`. +- `site/routes/__init__.py`: регистрация `upload_bp`. +- `site/static/app.js`: мёртвые константы `UPLOAD_URL`, `CONVERT_URL`, `UNZIP_URL`. +- `tests/test_upload_security.py` — целиком (тестировал удалённые `_check_ext`/`_unzip`). +- `tests/test_routes.py`: классы `TestUpload`, `TestUnzipUpload`. + +## Что оставлено (рабочее) + +- `contracts_upload_sink` + `_store_and_parse` + `_convert` — sink для `/api/upload_refs` (VM-буфер). +- `pages_bp /upload/` — отдача ES-модулей `upload/frontend` (клиентский ZIP). + +## Почему ревью Sonnet не заметило + +Ревью искало сломанное (трассировки ошибок), а не мёртвый код. `/upload`/`/unzip-upload` +не падали и не давали 500 — просто не вызывались. Обнаруживаются только аудитом +«какие эндпоинты фронт реально дёргает». + +## Проверки + +- `py_compile` + `node -c app.js` — OK. +- `import app` — OK, 24 роута; `/upload` (POST) и `/unzip-upload` отсутствуют, + `/api/upload_refs` и `/upload/` на месте. +- Юнит-тесты: 92 passed (было 102). + +## Хвост + +`tests/load/stress_http.py` всё ещё бьёт по удалённому `/upload` — нужен +переписывание на `/api/upload_refs` (PUT на ВМ → refs). diff --git a/History/2026-08-26-session-summary.md b/History/2026-08-26-session-summary.md new file mode 100644 index 0000000..b93d7c6 --- /dev/null +++ b/History/2026-08-26-session-summary.md @@ -0,0 +1,60 @@ +# Сводка сессии 2026-08-26 — contracts-flask (тесты + чистка + стресс) + +## 1. Фикс логики сверки (v2.0.15 → v2.0.16) + +- **`full_replace` дублировал строки**: `reset()` чистил `spec_current` только на старте + пайплайна, а `full_replace` (все ADD) применялся поверх старых строк → дубли. +- Фикс: `db/spec_events.py::clear_current()` (чистит только `spec_current`, история + `spec_events` сохраняется) + вызов в `process.py` при `mode == "full_replace"`. +- Коммит `021c925`, запушено, прод передеплоен → `2.0.16`. +- Детали: `History/2026-08-26-full-replace-fix.md`. + +## 2. Документация + +- Архитектура: `DOC/architecture-contracts-flask.md` (актуальная, VM-буфер, клиентский ZIP). +- Корневой `README.md` — подробный, со ссылкой на архитектуру. + +## 3. Тесты + +### Удалены старые (коммит `9a8ae0a`) +- `deploy/tests/` (unit/pipeline/integration + `test_drhider.py`), `deploy/conftest.py`, + `deploy/tests.js` — ссылались на старую `compare/`-архитектуру. + +### Новые юнит/интеграционные (коммиты `8f8fabf`, `3b259cf`) +- 13 файлов в `contracts-flask/tests/` + `conftest.py` (изоляция БД) + `README.md`. +- Покрытие: metrics, grouping, llm_client, llm, classify, parse, connection, + spec_events, spec_current, process_pipeline, routes. +- **92 passed** (после чистки legacy; было 102). + +### Нагрузочные (маркер `load`, коммиты `b7d60e1`, `08f7e3f`, `6fcbbdd`) +- `tests/load/test_load_pipeline.py` — 100 контрактов × 20 допников × 10 услуг. +- `tests/load/test_load_apply_ops.py` — 5000 ADD/UPDATE/DELETE. +- `tests/load/test_load_concurrency.py` — конкурентная запись SQLite. +- `tests/load/stress_http.py` — standalone HTTP-стресс. +- Полный прогон: not-load 102 passed, load 3 passed (~3 мин). + +### Находка №1: SQLite `database is locked` (коммит `08f7e3f`) +- 16 конкурентных писателей → `database is locked`; 8 — нестабильно; **4 — стабильно** + (= `MAX_WORKERS` приложения). Для текущего сценария (один пользователь) некритично. +- `History/2026-08-26-load-sqlite-locked.md`. + +## 4. Стресс-тест прода (коммит `08e8e3a`) + +- Прод: `contractor.pythonk8s.dev.nubes.ru`, под `pythonk8s` (cpu 1, mem 1Gi). +- Прямой POST 100KB → **83% таймаутов** (шлюз рвёт >64KB) — подтверждён предел платформы, + поэтому и существует VM-буфер. +- Реальный файл 30KB → 200/200 ok (rps 10), под CPU 899m / MEM 520Mi, без OOM. +- `History/2026-08-26-stress-100kb-gateway.md`. + +## 5. Чистка мёртвого legacy-кода (коммит `aa58522`) + +- Удалены `POST /upload`, `POST /unzip-upload`, `_check_ext`, `_unzip`, `ALLOWED`, + пустой blueprint, константы фронта `UPLOAD_URL`/`CONVERT_URL`/`UNZIP_URL`. +- Загрузка теперь ТОЛЬКО через ВМ (`/api/upload_refs` → `contracts_upload_sink`). +- `History/2026-08-26-cleanup-legacy-upload.md`. + +## Хвосты / открытые вопросы + +1. `tests/load/stress_http.py` бьёт по удалённому `/upload` — переписать на `/api/upload_refs`. +2. Прод-риск `database is locked` при конкурентных сверках — отложен (нет мультитенантности). +3. E2E с реальным LLM (`gpt-oss-120b`) на проде — не проверялся автоматически (тесты на Fake LLM).