ppt_tep OR-JOIN (doc_ref=source_key ИЛИ doc_full_name ILIKE) может вернуть один ППТ-документ несколько раз → дубли ТЭП в ИРД-блоке #1653

Closed
opened 2026-06-16 18:47:03 +00:00 by bot-backend · 0 comments
Collaborator

Severity: low · Категория: logic/double-count · Файл: backend/app/services/site_finder/ppt_tep_lookup.py:50-53, 91

ppt_tep_lookup.py:50-53 — JOIN ekb_ppt_tep t ON (t.doc_ref = CAST(pp.source_key AS text) OR (pp.doc_full_name IS NOT NULL AND t.doc_ref ILIKE '%' || pp.doc_full_name || '%')) соединяет одну строку planning_projects с КАЖДОЙ строкой ekb_ppt_tep, удовлетворяющей хотя бы одной ветке OR. Если несколько записей ekb_ppt_tep матчатся (одна по точному doc_ref=source_key, другая по ILIKE doc_full_name; либо ILIKE по короткому/общему doc_full_name цепляет несколько неродственных doc_ref), результат содержит несколько строк на один ППТ/ПМТ-документ. Возврат на ppt_tep_lookup.py:91 [dict(r) for r in rows] — без дедупа.

Импакт: докстринг (стр. 70-71) обещает контракт 'Каждая запись — один ППТ/ПМТ-документ'. При множественном best-effort матче возвращаются дубли документа с разным ТЭП-контентом → инфляция/искажение ТЭП-врезки в ird_analyze.py:163. Low: best-effort связка задокументирована как graceful, импакт ограничен UI-дублями (не порча данных в БД); но контракт нарушается и ILIKE по короткой подстроке doc_full_name — реальный риск cartesian-инфляции.

Фикс: DISTINCT ON (t.doc_ref) с детерминированным ORDER BY, либо дедуп по (project_type, doc_ref) в Python, либо приоритизировать точную ветку doc_ref=source_key над ILIKE.

Почему баг: Докстринг прямо обещает контракт 'Каждая запись — один ППТ/ПМТ-документ'. Фактически при множественном best-effort матче возвращаются дубли документа с разным ТЭП-контентом, что инфлирует/искажает ТЭП-врезку в analyze ИРД-блоке (ird_analyze.py:163). Best-effort связка задокументирована как 'graceful', импакт ограничен (UI-дубли, не порча данных), поэтому low; но контракт нарушается и ILIKE по короткой подстроке doc_full_name — реальный риск cartesian-инфляции.

Как чинить: Дедуп по идентичности документа: либо DISTINCT ON (pp.project_type, pp.source_key) с приоритетом точного doc_ref-матча в ORDER BY, либо приоритизировать exact-equality и брать ILIKE-ветку только при отсутствии точного матча; как минимум дедуп результата по (source_key, doc_ref).


deep-audit backend v2 (2-й проход, после PR #1543) · verify+harden, confidence 0.7 · unit B40

**Severity:** low · **Категория:** logic/double-count · **Файл:** `backend/app/services/site_finder/ppt_tep_lookup.py:50-53, 91` ppt_tep_lookup.py:50-53 — `JOIN ekb_ppt_tep t ON (t.doc_ref = CAST(pp.source_key AS text) OR (pp.doc_full_name IS NOT NULL AND t.doc_ref ILIKE '%' || pp.doc_full_name || '%'))` соединяет одну строку planning_projects с КАЖДОЙ строкой ekb_ppt_tep, удовлетворяющей хотя бы одной ветке OR. Если несколько записей ekb_ppt_tep матчатся (одна по точному doc_ref=source_key, другая по ILIKE doc_full_name; либо ILIKE по короткому/общему doc_full_name цепляет несколько неродственных doc_ref), результат содержит несколько строк на один ППТ/ПМТ-документ. Возврат на ppt_tep_lookup.py:91 `[dict(r) for r in rows]` — без дедупа. Импакт: докстринг (стр. 70-71) обещает контракт 'Каждая запись — один ППТ/ПМТ-документ'. При множественном best-effort матче возвращаются дубли документа с разным ТЭП-контентом → инфляция/искажение ТЭП-врезки в ird_analyze.py:163. Low: best-effort связка задокументирована как graceful, импакт ограничен UI-дублями (не порча данных в БД); но контракт нарушается и ILIKE по короткой подстроке doc_full_name — реальный риск cartesian-инфляции. Фикс: DISTINCT ON (t.doc_ref) с детерминированным ORDER BY, либо дедуп по (project_type, doc_ref) в Python, либо приоритизировать точную ветку doc_ref=source_key над ILIKE. **Почему баг:** Докстринг прямо обещает контракт 'Каждая запись — один ППТ/ПМТ-документ'. Фактически при множественном best-effort матче возвращаются дубли документа с разным ТЭП-контентом, что инфлирует/искажает ТЭП-врезку в analyze ИРД-блоке (ird_analyze.py:163). Best-effort связка задокументирована как 'graceful', импакт ограничен (UI-дубли, не порча данных), поэтому low; но контракт нарушается и ILIKE по короткой подстроке doc_full_name — реальный риск cartesian-инфляции. **Как чинить:** Дедуп по идентичности документа: либо DISTINCT ON (pp.project_type, pp.source_key) с приоритетом точного doc_ref-матча в ORDER BY, либо приоритизировать exact-equality и брать ILIKE-ветку только при отсутствии точного матча; как минимум дедуп результата по (source_key, doc_ref). --- <sub>deep-audit backend v2 (2-й проход, после PR #1543) · verify+harden, confidence 0.7 · unit B40</sub>
bot-backend added the
week ревью 1
label 2026-06-16 18:47:03 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1653
No description provided.