118 lines
12 KiB
Markdown
118 lines
12 KiB
Markdown
# Критическая перепроверка ответа Соннета: pipeline сверки
|
||
|
||
Дата: 2026-08-27
|
||
Основание: ответ Соннета из `History/sonnet-review-comparison-answer-2026-08-27.md`
|
||
|
||
## Подтверждено по исходникам
|
||
|
||
### B-1: `complete` против `done`
|
||
|
||
Подтверждено.
|
||
|
||
- `site/services/process.py` завершает `run_pipeline()` событием `{"type": "complete"}`.
|
||
- `site/static/compare.js` завершает успешный SSE только в ветке `d.type === 'done'`.
|
||
- При нормальном закрытии генератора после неизвестного события клиент получает `EventSource.onerror`, поэтому успешная сверка может отображаться как ошибка соединения.
|
||
|
||
Это не предположение Соннета, а прямое несоответствие backend/frontend event contract.
|
||
|
||
### B-2: отдельные commits в `apply_ops()`
|
||
|
||
Основная часть finding подтверждена.
|
||
|
||
- `site/db/spec_events.py::apply_ops()` вызывает `execute()` для каждого события и изменения current state.
|
||
- `site/db/connection.py::execute()` делает `conn.commit()` после каждого SQL-вызова.
|
||
- В `apply_ops()` нет единой транзакции и rollback.
|
||
- Поэтому исключение после уже выполненных операций оставляет предыдущие commits в БД.
|
||
- `process.py` после исключения формирует нулевую summary и не показывает уже применённые операции как applied.
|
||
|
||
Требует отдельной проверки формулировка о том, что history и current state обязательно расходятся при каждом сценарии: это зависит от того, на каком именно SQL-вызове возникает исключение. Но частичный commit и недостоверная summary подтверждены.
|
||
|
||
### B-3: stale `name_hash`
|
||
|
||
Подтверждено.
|
||
|
||
`site/db/spec_events.py::_update_spec_current()` обновляет `name`, `price`, `qty`, `sum`, `date_start`, но не пересчитывает и не обновляет `name_hash`. При последующем ADD с новым именем `_hash()` создаёт другой ключ, поэтому сценарий с дублированием реален.
|
||
|
||
Нужно отдельно проверить бизнес-правило для изменения `date_start`: изменение даты может означать новый период и не во всех случаях должно менять identity строки. Автоматически объединять `name` и `date_start` в одно исправление нельзя без подтверждения модели идентичности.
|
||
|
||
### H-1: `seq` для неизвестного action
|
||
|
||
Подтверждено.
|
||
|
||
В ветке `else` `apply_ops()` вызывает `_log_unresolved(...)`, но не делает `seq += 1`. Следующая операция получает тот же `seq`.
|
||
|
||
### H-2: `unresolved` недоступен в summary
|
||
|
||
Подтверждено по текущим участкам.
|
||
|
||
`apply_ops()` возвращает только `added`, `updated`, `deleted`. При этом frontend использует `d.total_unresolved`, а `run_pipeline()` не формирует это поле в финальном событии.
|
||
|
||
Требует проверки полный путь отображения `UNRESOLVED`: факт отсутствия счётчика в summary подтверждён, но следует проверить, нет ли другого endpoint/UI, который показывает события напрямую.
|
||
|
||
### H-3: результат `check_arithmetic()` игнорируется
|
||
|
||
Подтверждено для `run_pipeline()`.
|
||
|
||
`process.py` вызывает `check_arithmetic(ops)` и игнорирует возвращаемое значение. Требуется дополнительно проверить, логирует ли сама функция несоответствия и является ли её контракт предупреждением или валидатором, прежде чем выбирать severity и формат исправления.
|
||
|
||
### H-4: пустой `full_replace`
|
||
|
||
Подтверждено по порядку операций.
|
||
|
||
В `process.py` `clear_current(contract_id)` вызывается после получения `mode` и до `apply_ops()`, без проверки непустого `ops`. Пустой список при `mode == 'full_replace'` очищает current state.
|
||
|
||
Нужно уточнить у Соннета, какие именно ответы считать повреждёнными: пустой `ops` может быть штатным ответом для пустой спецификации, хотя для существующего current state это опасный случай.
|
||
|
||
### M-1: финальное событие после ошибок
|
||
|
||
Подтверждено.
|
||
|
||
После цикла `run_pipeline()` безусловно выдаёт финальное событие, даже если каждый supplement завершился `extract_error`.
|
||
|
||
## Подтверждено частично или требует дополнительных доказательств
|
||
|
||
### M-2: concurrency
|
||
|
||
Риск правдоподобен, но формулировку о конкретных коллизиях `seq` нужно доказать тестом. `WAL` сериализует записи, но `get_next_seq()` и последующие операции разделены во времени. Нужен воспроизводимый конкурентный тест с двумя pipeline на одном `contract_id`.
|
||
|
||
### M-3: SSE timeout
|
||
|
||
Отсутствие клиентского timeout видно, но само по себе не доказывает пользовательский дефект: сервер отправляет heartbeat каждые 15 секунд. Нужно проверить proxy timeout, лимит длительности LLM и поведение при остановленном backend.
|
||
|
||
### M-4: `last_event_id`
|
||
|
||
Подтверждена причина риска: `spec_current` вставляется без `last_event_id`, а UPDATE также его не заполняет; cleanup в `supplements.delete_by_document()` фильтрует по этому полю. Нужен тест удаления supplement после сверки, чтобы окончательно подтвердить наблюдаемое поведение.
|
||
|
||
### L-1: сортировка
|
||
|
||
Подтверждено, что `list_by_contract()` не выбирает `doc_date` и `created_at` как поля результата, поэтому ключ сортировки в `process.py` фактически использует значения по умолчанию. При этом SQL уже сортирует по `s.created_at`, поэтому это скорее misleading code, а не доказанная поломка порядка.
|
||
|
||
### L-2: пустой current state
|
||
|
||
Технически возможно, но вывод о неправильном prompt зависит от семантики группы и контракта `build_prompt()`. Нужна точная проверка prompt и отдельный сценарий сбоя/неполной группы.
|
||
|
||
## Дополнительные вопросы Соннету
|
||
|
||
1. Для B-2: укажи точный SQL-вызов и сценарий исключения, при котором расходятся `spec_events` и `spec_current`; отдельно различи частичный commit и рассогласование двух таблиц.
|
||
2. Для B-3: должна ли смена `name` менять identity строки, или `name_hash` является историческим ключом? Какие правила действуют при одновременной смене `name` и `date_start`?
|
||
3. Для H-2: где именно пользователь должен видеть `UNRESOLVED`, если не через summary? Укажи полный frontend/backend путь и проверяемый сценарий.
|
||
4. Для H-3: что возвращает `check_arithmetic()` и есть ли у него побочный logging? Приведи фактический пример mismatch и ожидаемый бизнес-статус.
|
||
5. Для H-4: почему пустой `full_replace` однозначно считать повреждённым ответом, если документ может содержать пустую спецификацию? Как отличить штатный результат от обрыва/невалидного JSON?
|
||
6. Для M-2: предоставь воспроизводимый тест или timeline с двумя потоками, который приводит к одинаковому `seq` или повреждённому current state.
|
||
7. Для M-3: какой фактический timeout установлен на nginx/proxy и как он соотносится с heartbeat и максимальным временем обработки группы?
|
||
8. Для M-4: есть ли штатный путь удаления supplement после сверки, и должен ли он удалять строки current state, если строка затронута несколькими supplements?
|
||
9. Для L-2: приведи точный контракт `extract`/`diff` prompt и доказательство, что пустой current state после ошибки действительно меняет смысл следующего LLM-вызова.
|
||
10. Какие findings Соннет считает подтверждёнными тестом, а какие являются только статическим риском?
|
||
11. Проверь финальное событие по реальному frontend-коду: нет ли другой ветки, которая обрабатывает `complete` или завершение `EventSource` без `done`?
|
||
12. Для каждого BLOCKER укажи минимальный regression test, который сначала падает на текущей версии и проходит после исправления.
|
||
|
||
## Предварительный вывод
|
||
|
||
Без дополнительных ответов Соннета уже достаточно доказательств для регистрации B-1, B-2, B-3, H-1, H-2, H-3, H-4 и M-1 как реальных проблем текущего кода. M-2, M-3, M-4 и L-2 требуют воспроизводимых тестов или более точной трассировки. L-1 подтверждён как избыточная/вводящая в заблуждение сортировка, но не как текущая поломка порядка документов.
|
||
|
||
Изменения production-кода по этим findings не выполнялись.
|
||
|
||
## Обязательное ограничение для дальнейшего общения с Соннетом
|
||
|
||
Соннет не должен писать код, patch или diff и не должен изменять файлы. Нужно запрашивать только критический анализ, проверяемые доказательства, воспроизводимые сценарии, тестовые идеи в виде описания и дополнительные вопросы. Реализацию выполняем отдельно после собственной проверки findings.
|