diff --git a/History/opus-generator-schema.md b/History/opus-generator-schema.md new file mode 100644 index 0000000..e26aa38 --- /dev/null +++ b/History/opus-generator-schema.md @@ -0,0 +1,122 @@ +# Схема Опуса — генератор + симулятор (v1.0) + +Ответ Опуса от 25.06.2026 на запрос `History/opus-generator-request.md`. + +--- + +## Оценка схемы + +### Сильные стороны + +1. **v1→v2 diff-пары** — ключевая идея. Генератор закладывает конкретные изменения между версиями спецификаций (цена, объём, ADD/DELETE/UPDATE), и верификатор проверяет что compare нашёл именно их. Это даёт детерминированную проверку самой важной функции сервиса. + +2. **manifest.json как ground truth** — каждая проверка сверяется с эталоном, а не с «примерно ожидаемым». Никакой неоднозначности. + +3. **Пул услуг из реальных данных** — стойко-место, PDU, L2-канал, cross-connect, Cloud, IPv4. Не выдуманные, а те что в реальных файлах. + +4. **17 ошибочных кейсов** — покрытие крайне широкое: от битого XML до сиротских спецификаций. + +5. **Структура папок** — `testgen/` + `sim/` с чётким разделением генерации и симуляции. + +### Что нужно уточнить/доработать + +1. **Симулятор: API или браузер?** Опус описывает «реальное приложение (API/UI)». Два варианта: + - **curl-симулятор** (только API): дёргаем `/upload`, `/api/classify-batch`, `/api/groups`, `/process-v2`. Быстро, дёшево, но не проверяет фронтенд (duplicate confirm, renderFiles, SSE в UI). + - **Playwright** (полный UI): открывает браузер, кликает кнопки, читает DOM. Медленно, но проверяет ВСЁ включая confirm-диалоги. + - **Рекомендация**: curl для генерации/классификации/групп + выборочно Playwright для UI-специфичных сценариев (дубликаты, confirm, удаление). + +2. **.doc файлы** — парсинг `допник-1-XXX002-01200_3.doc` не удался (старый формат). Нужен LibreOffice для конвертации, либо исключить .doc из генерации и использовать только .docx. + +3. **Стресс-сценарий F** — rapid-fire загрузки во время classify. Это может быть сложно воспроизвести и проверить. Вероятно, отложить на потом. + +4. **Количество файлов** — раскладка даёт ~92 файла + edge cases. Можно докрутить до ровно 100 добавив ещё дубликатов или мусора. + +--- + +## Исходная схема Опуса + +### Часть 1 — Генерация ~100 файлов + +**Базовая структура:** 10 компаний × 10 договоров × (спецификация v1 + v2 + 1-3 допника) + +**Раскладка:** +| Категория | Кол-во | Зачем | +|---|---|---| +| Договоры (валидные) | 10 | по 1 на компанию | +| Спецификации v1 | 10 | базовая версия | +| Спецификации v2 (ревизия) | 10 | diff для сравнения | +| Допники | 18 | 1–3 на договор | +| Битые/edge .docx | 15 | ошибки парсинга/классификации | +| Форматы .doc/.pdf | 12 | ветка конвертера | +| ZIP-архивы | 5 | внутри — наборы файлов | +| Дубликаты (имя/контент) | 10 | upload-логика | +| Мусор (не договор) | 10 | негативная классификация | +| **Итого** | **~100** | | + +**Оси вариативности:** +- Компания: ЗАО/ООО/АО/ПАО/ИП, латиница/кириллица +- Номер: 5 цифр, опц. суффикс `_N`, edge — буквы/длинный/пустой +- Дата: 12.2025–06.2026, форматы `01.02.2026` и «01 февраля 2026» +- Цены: базовые ±20%, разрядность с пробелом +- Услуги (пул): стойко-место, PDU, L2-канал, IPv4 /30 и /32, cross-connect ВОЛС/UTP, Cloud Intel/AMD, Интернет, порт, подсеть +- Таблица: 3–12 строк, 6 или 7 колонок + +**Различия v1→v2 (для компаратора):** изменение цены, изменение объёма, добавленная услуга, удалённая услуга, переименование, сдвиг даты, пересчёт итога. + +**Ошибочные кейсы (17 шт):** нет номера, нестандартный заголовок, битый XML, пустой файл, только таблицы, только текст, .doc формат, битый PDF, дубликат имени, дубликат контента, гигант (500 строк), не-договор, сирота (спека без договора), рассинхрон (имя файла ≠ номер в тексте). + +**Parent-child:** ключ — НОМЕР. Генератор связывает договор→спеку/допник по НОМЕРу. + +**Шаблоны python-docx:** +- Билдеры: `build_contract(ctx)`, `build_spec(ctx)`, `build_addendum(ctx)` +- ctx = {company, number, date, parties, services[], totals} +- Хелперы: `add_title`, `add_section`, `add_services_table` +- Битый XML: валидный docx → распаковать zip → испортить document.xml → запаковать +- .doc: docx → libreoffice --convert-to doc +- Битый PDF: docx → pdf → обрезать байты + +### Часть 2 — Симулятор + +**Словарь действий:** upload, delete, classify, groups, compare (SSE), confirm/cancel, reset. + +**Сценарии:** +- **A. Хэппи-путь:** договор + спека v1 + v2 → classify → группа → compare → diff совпал +- **B. Зигзаг:** загрузил 3 → удалил 1 → добавил 2 → classify → проверить консистентность +- **C. Bulk:** все ~100 разом → classify → число групп, битые ✗ +- **D. Тупые действия:** дубликат (OK/Отмена), удалить всё, смешать форматы, classify на нуле, compare с одной версией +- **E. ZIP:** валидный, с дубликатами, с битым файлом внутри +- **F. Стресс:** случайные паузы, rapid-fire, загрузка во время classify + +**Тайминги:** think-time (random 0.1-3с), classify — поллинг, compare — SSE до done/таймаут. + +### Часть 3 — Верификация + +После каждого шага: +- `state.files` count == ожидаемого +- Статусы ✓/✗ соответствуют (битые → ✗) +- После classify: группы есть, состав по НОМЕРу верный +- Сироты без краша +- Compare: diff == заложенному (ground truth из manifest.json) +- Дубликаты: счётчик после OK ≠ после Отмена +- Нет 500-х, нет необработанных исключений +- Идемпотентность classify + +**Ground truth:** `manifest.json` — что сгенерировано + ожидаемые группы и diff-ы. + +### Структура папок + +``` +testgen/ + pools.py # услуги, компании, цены + templates.py # build_contract / build_spec / build_addendum + corrupt.py # порча docx/pdf, конвертация .doc + generate.py # оркестратор → out/ + manifest.json + out/{valid,errors,formats,zips,duplicates}/ + manifest.json # ground truth + +sim/ + actions.py # обёртки над API + scenarios.py # A..F + verify.py # чек-лист + run.py +```