Files
contracts-flask/History/2026-08-26-sonnet-code-review.md
2026-08-26 16:29:41 +03:00

74 lines
5.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Sonnet: код-ревью contracts-flask (2026-08-26)
## Контекст
- Написан промпт для Sonnet: `contracts-flask/docs/prompt-sonnet-architecture.md`
(задача — прочитать файлы текущего сервиса сверки и дать описание/архитектуру).
- Sonnet прочитал все указанные файлы и вместо описания выдал **код-ревью** с 10 находками
(2 критические, 3 средних, 2 XSS, 3 мелких). Архитектурное описание — отдельным документом (не выдано).
## Находки Sonnet
### 🔴 Критические
1. **`process.py` + `spec_events.py` — UPDATE/DELETE никогда не применяются (сломана частичная сверка).**
LLM возвращает `target_id: "r1"` (индекс строки), а `apply_ops()` читает `op.get("target_hash", "")`.
Поля `target_hash` в ответе LLM нет → все UPDATE/DELETE из `mode=partial` молча уходят в `UNRESOLVED`.
Трассировка: `_build_spec_text()` показывает строки `[id: r1]` → LLM возвращает `target_id: "r1"`
`process.py` передаёт ops без трансляции r1→hash → `spec_events.py` `th = op.get("target_hash","")` = "" → UNRESOLVED.
Работает только `mode=full_replace` (все ADD) и первый документ (extract, только ADD).
2. **`Dockerfile` — образ не запустится: `upload/` не скопирован.**
`COPY site /app/site` — только site/, `upload/` отсутствует.
При старте `routes/__init__.py` делает `from upload.backend.upload_refs import ...`, `app.py` добавляет `/app` в sys.path.
В образе `/app/upload/` нет → `ModuleNotFoundError`.
### 🟠 Средние
3. **`db/prompts.py``list_by_role()` запрашивает несуществующий столбец `created_by`.**
В схеме `prompts` нет `created_by``sqlite3.OperationalError``/api/prompts/list` всегда 500.
4. **`routes/prompts_bp.py` — вызов несуществующих функций:**
- `db_prompts.insert(...)` — нет (есть `save_new_version()`);
- `db_prompts.delete(...)` — нет (есть `delete_prompt()`).
`/api/prompts/save` и `/api/prompts/delete` падают с `AttributeError`.
5. **`services/llm.py`, `services/classify.py` — захардкожены `LLM_URL/LLM_KEY/LLM_MODEL` в обход `config.py`.**
`config.py` читает из `LLM_API_URL`, а compare/classify игнорируют его. Конфигурация расщеплена на 3 блока.
### 🟡 XSS (frontend)
6. **`compare.js``nr.name` в таблице операций не экранирован** (`escHtml()` отсутствует).
Имя услуги из договора (текст LLM) вставляется в innerHTML → XSS.
7. **`compare.js``sec.filename` в заголовке секции не экранирован.** Имя файла от пользователя → XSS.
### ⚪ Мелкие
8. **`llm_prompt.py`** — устаревший комментарий «Lucee API» (читает напрямую из SQLite).
9. **`db/connection.py`** — `_pg_to_sqlite`: `gen_random_uuid()` добавляет `?` в SQL, но не добавляет значение в params (мёртвый код, но при исполнении сломает запрос).
10. **`services/classify.py`** — комментарий «re-classify after file changes» вводит в заблуждение: `reset_classify_status()` сбрасывает только `processing``pending`, уже классифицированные не трогает.
## Итоговая таблица
| # | Файл | Проблема | Критичность |
|---|---|---|---|
| 1 | process.py, spec_events.py | UPDATE/DELETE → UNRESOLVED, частичная сверка сломана | 🔴 критический |
| 2 | Dockerfile | upload/ не скопирован → образ не стартует | 🔴 критический |
| 3 | db/prompts.py | created_by отсутствует → 500 на /api/prompts/list | 🟠 средний |
| 4 | prompts_bp.py | insert()/delete() — не те имена функций → 500 | 🟠 средний |
| 5 | llm.py, classify.py | захардкожены LLM_URL/KEY/MODEL в обход config.py | 🟠 средний |
| 6 | compare.js | nr.name не экранирован → XSS | 🟡 XSS |
| 7 | compare.js | sec.filename не экранирован → XSS | 🟡 XSS |
| 8 | llm_prompt.py | устаревший комментарий | ⚪ мелкое |
| 9 | connection.py | gen_random_uuid() мёртвый код с ошибкой | ⚪ мелкое |
| 10 | classify.py | комментарий re-classify вводит в заблуждение | ⚪ мелкое |
## Статус
- ✅ Все 10 находок исправлены (v2.0.15, commit `e3f5581`, запушено).
- #1 (критично) — сам: `process.py` трансляция `target_id``target_hash`.
- #2#10 (механика) — Флаш-субагент: Dockerfile, prompts CRUD, XSS, конфиг LLM, комментарии.
- Верифицировано: `py_compile`, `node -c`, `import app` — OK.
- Архитектурное описание от Sonnet — так и не получено (вместо него — ревью). Если нужно — отдельным запросом.