From 7e0e5ce42951d547b798c6c3b8f93eb442596550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Sun, 28 Jun 2026 10:11:05 +0400 Subject: [PATCH] =?UTF-8?q?docs:=20=D0=B8=D1=82=D0=BE=D0=B3=D0=B8=20=D1=81?= =?UTF-8?q?=D0=B5=D1=81=D1=81=D0=B8=D0=B8=20=E2=80=94=20decoupling=20+=20?= =?UTF-8?q?=D0=B4=D0=B5=D0=B4=D1=83=D0=BF=D0=BB=D0=B8=D0=BA=D0=B0=D1=86?= =?UTF-8?q?=D0=B8=D1=8F=20+=20=D0=B7=D0=B0=D1=89=D0=B8=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- History/session-02-fixes-2026-06-28.md | 85 ++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 History/session-02-fixes-2026-06-28.md diff --git a/History/session-02-fixes-2026-06-28.md b/History/session-02-fixes-2026-06-28.md new file mode 100644 index 0000000..a8ef436 --- /dev/null +++ b/History/session-02-fixes-2026-06-28.md @@ -0,0 +1,85 @@ +# Сессия 2026-06-28 — Итоги + +## Decoupling пайплайна (Ф1-Ф4) + +### Ф1 — Контракты + upload decouple +- `deploy/contracts.py` — 5 dataclass'ов: ParseResult, ClassifyResult, GroupingResult, BatchGroupingResult, CompareOp +- `deploy/services/upload.py` — выделены `parse_multipart()` и `store_document()` +- `handle_upload` теперь тонкий HTTP-handler + +### Ф2 — LLM Client DI +- `deploy/services/llm_client.py` — протокол `LLMClient.complete(prompt)->str` + `HttpxLLMClient` + `FakeLLMClient` +- `deploy/services/llm.py` — `call_llm(..., llm_client=None)` — DI с обратной совместимостью +- `deploy/services/classify.py` — `_call_llm_classify(..., llm_client=None)` + `classify_batch(..., llm_client=None, repo=None)` + +### Ф3 — Repository facade +- `deploy/repository.py` — Protocol `Repository`, `PgRepository`, `MemRepository` (17 методов) + +### Ф4 — pytest +- `deploy/conftest.py` — фикстуры: `fake_llm`, `mem_repo`, `sample_docx_bytes` +- `deploy/tests/unit/test_contracts.py` — 13 тестов (contracts, garbage, multipart) +- `deploy/tests/unit/test_llm_repo.py` — 9 тестов (FakeLLM, MemRepository) +- `deploy/tests/unit/test_classify_batch.py` — 4 теста (garbage×2, LLM, no_pending) +- **26/26 PASS** + +### Баги (ревью Opus) +- #1 Двойная сериализация JSON в `PgRepository.set_document_parsed` — исправлено +- #2 `db_docs` → `_db` в `classify_batch` — исправлено (6 вызовов) +- #3 `ParseResult.from_row` element_count=0 — исправлено +- #4 `GroupingResult.unresolved` → `BatchGroupingResult` — исправлено +- Protocol расширен: +7 методов +- `build_classify_prompt`: try/except вокруг `db.prompts.get_active()` +- `MemRepository.set_classification`: +`classify_raw`, `classify_input` + +--- + +## Дедупликация + защита от careless user + +### content_hash +- `db/documents.py` — `insert()` принимает `content_hash`, `get_by_hash()` для проверки +- `services/upload.py` — `store_document()` считает `sha256` и пропускает дубликаты +- `convert_server.py` — ALTER TABLE `content_hash` + +### Сортировка по doc_date +- `services/process.py` — `supps.sort(key=lambda s: (doc_date, created_at))` + +### date_start в name_hash +- `db/spec_events.py` — `_hash(name, date_start)` — хеш включает период, разные периоды = разные строки + +### ZIP-защита +- `services/unzip.py` — лимит 500 файлов, 500 MB, проверка ratio (100x), санитизация имён (cp437→utf8, path-traversal) + +### Статус «не распознан» +- `services/upload.py` — supplement НЕ создаётся при ошибке парсинга, `warning` при пустом результате +- `try/except: pass` → `logging.warning` + +### UNRESOLVED вместо тихих no-op +- `db/spec_events.py` — `ADD` без имени → UNRESOLVED, `UPDATE/DELETE` с пустым `target_hash` → UNRESOLVED, неизвестный `action` → UNRESOLVED +- `_log_unresolved()` — общая функция + +### Нормализация чисел и дат +- `services/metrics.py` — `_to_decimal`: пробелы → удалить, `,` → `.` +- `services/metrics.py` — `normalize_date()`: DD.MM.YYYY → YYYY-MM-DD +- `db/spec_events.py` — `_hash` применяет `normalize_date` + +--- + +## Архитектура check.kube5s.ru +- ВМ: порт 8777, БД `contracts_check`, код `~/contracts-check/` +- Nginx: SSL (letsencrypt), CORS, proxy headers +- Managed Flask: `contracts-flask.git`, `site/` + `Dockerfile` +- VM_API = `https://check.kube5s.ru` +- Полная изоляция от прода (contracts.kube5s.ru) + +--- + +## Файлы History +- `architecture.md` — архитектура Flask-стека +- `session-01-init-2026-06-27.md` — создание стека +- `opus-decoupling-plan-2026-06-28.md` — задание Opus +- `opus-decoupling-review-2026-06-28.md` — рецензия DeepSeek +- `opus-dialog-2026-06-28.md` — полный диалог DeepSeek↔Opus +- `opus-review-request-2026-06-28.md` — запрос ревью +- `opus-review-2-request-2026-06-28.md` — повторное ревью +- `opus-review-response-2026-06-28.md` — результат ревью +- `opus-dedup-2026-06-28.md` — дедупликация + засады (15 пунктов)