docs: итоги сессии — decoupling + дедупликация + защита

This commit is contained in:
2026-06-28 10:11:05 +04:00
parent 69ab09e304
commit 7e0e5ce429
+85
View File
@@ -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 пунктов)