docs/web: необвинителна уговорка за рисковите флагове (#219) - #237
Conversation
01d5abc to
6a67592
Compare
1341235 to
fca0be4
Compare
ydimitrof
left a comment
There was a problem hiding this comment.
Преглед на PR #219 — „необвинителна уговорка за рисковите флагове“
Вердикт: КОМЕНТАР (не блокиращ) — един дребен проблем за изчистване преди merge
Обхват
Промяната е чисто редакционна/презентационна: смекчаване на текста на рисковите сигнали, добавяне на необвинителна уговорка с връзки към методологията (§10), нов раздел 10 в methodology.tsx, noindex за договори на физически лица/ЕТ и CSS за уговорката. Обхватът съответства на описанието на #219 („сигнал ≠ присъда“) и не излиза извън него.
Сигурност — ЧИСТО ✅ (Phase 0)
- Няма твърдо кодирани тайни, ключове или пароли.
- Няма нови или променени външни URL адреси — само вътрешни
<Link to="/methodology#…">; whitelist не е засегнат. - Няма зловредни шаблони (backdoor, инжекция, обфускация), няма
dangerouslySetInnerHTML— целият нов JSX е статичен и escape-нат от React (без XSS). - Няма промени в зависимости, няма SQL — няма повърхност за SQL инжекция.
- OWASP: няма приложимо отклонение за този тип промяна.
GDPR / ЗЗЛД — коректно ✅
isNaturalPersonProfileName(c.bidder.displayName) добавя noindex за договори на физически лица/ЕТ. Проверих: seoMeta (apps/web/app/lib/meta.ts) не емитира robots таг, така че няма дублиран/конфликтен robots мета таг, а поведението огледално повтаря съществуващия модел в company.tsx (ред 55) и изключването от sitemap-а. Добра, последователна защита на данните на именувано физическо лице.
Точност на съдържанието — 1 несъответствие ⚠️
В methodology.tsx текстът гласи „Отбелязваме четири признака“, но списъкът <dl> съдържа само три <dt> записа (двата типа за конкуренция — eu_no_competition и no_competition — са обединени в един запис). В кода (apps/web/app/lib/riskLogic.ts) типовете флагове наистина са четири, но за читателя, който брои видимите записи, числото „четири“ не отговаря. Предложение: или „три признака“, или отделен <dt> за случая със средства от ЕС. Праг „над 20% (>1.2×)“ съответства на deltaPct > 0.2 — коректно.
Качество / CLAUDE.md
- Именуването и терминологията са последователни (Сигнали/ориентир/присъда) между компонент и методология; етикетите огледално повтарят §10.
- Няма частична имплементация, TODO, дублиран или мъртъв код; разделянето на отговорностите е спазено (логиката остава в
riskLogic.ts). - CSS използва съществуващи токени
--warning/--warning-strong.
Тестове / документация
Промяната е презентационна и не добавя логика, изискваща нови тестове; съществуващите тестове за isNaturalPersonProfileName покриват използваната помощна функция. Документацията (methodology §10) е разширена и номерирането на разделите (10→11 за „Поправки“) е обновено консистентно с TOC и вътрешните препратки.
Заключение
Промяната е безопасна и добре изпълнена. Препоръчвам да се коригира несъответствието „четири/три признака“ преди merge; след това е готова за одобрение.
fca0be4 to
338a1cb
Compare
ydimitrof
left a comment
There was a problem hiding this comment.
Преглед на PR #219 — „docs/web: необвинителна уговорка за рисковите флагове"
Вердикт: COMMENT — няма блокиращи проблеми; препоръчва се одобрение след потвърждаване на 2 дребни въпроса. (Не публикувайте автоматично — изчаква вашата проверка.)
Резюме
Промяна по съдържание/копи и достъпност: рисковите сигнали се преформулират в необвинителен тон (#219), добавя се уговорка до флаговете с връзки към методологията, въвежда се нов раздел „10. Сигнали за риск" в методологията и се добавя noindex за договори с изпълнител физическо лице/ЕТ (GDPR/ЗЗЛД). Обхватът е фокусиран и съответства на описанието на задачата.
Фаза 0 — Сигурност (задължителна проверка): ЧИСТО ✅
- Няма твърдо кодирани тайни, ключове, пароли или токени.
- Промените по URL са само вътрешни котви (
/methodology#flagged,/methodology#contact) — няма външни адреси спрямо whitelist. - Няма SQL, няма
dangerouslySetInnerHTML, няма конкатенация на заявки — липсва повърхност за инжекция (SQL/XSS). Целият нов текст е статичен JSX;pct(),money()са съществуващи форматиращи функции. - Няма нови зависимости:
Link(react-router) иisNaturalPersonProfileName(@sigma/shared) вече се използват в проекта. - Няма бекдори, обфускация или код за инжекция. OWASP: не се откриват релевантни рискове за този diff.
Съответствие с #219
Имплементацията отговаря на описанието: (1) необвинителен език за всеки флаг, (2) уговорка, която „пътува" заедно с флаговете, (3) методологичен раздел с ограниченията и определенията, (4) канал за възражения. Добавеният noindex е разумна допълнителна мярка за защита на лични данни, съгласувана със съществуващото третиране на ЕТ профили.
Наблюдения (незадължителни, не блокират)
- methodology.tsx — пренномериране на раздели. „Поправки и обратна връзка" се мести от §10 на §11, а новата §10 е вмъкната. Вътрешните препратки (към §8 и §11) изглеждат коректни и TOC е обновен. Моля, проверете дали някъде другаде в кода/съдържанието няма препратка към старата номерация „раздел 10".
- RiskIndicators.tsx — рендиране при липса на флагове. Заглавието „Сигнали за риск" и новата уговорка вече се показват безусловно. От diff-а не се вижда ранен изход (напр.
if (!flags.length) return null). Ако такъв няма над показания фрагмент, при договор без сигнали ще се покаже празен списък с уговорка — струва си да се потвърди.
Тестове / документация
- Няма тестове в PR-а — приемливо за промяна на текст/копи и достъпност; няма нова логика, изискваща покритие. Документацията (методологията) е обновена в самия PR.
Draft (English, за избор)
Verdict: COMMENT — no blocking issues; approvable after confirming 2 minor questions.
This is a docs/copy and accessibility change: it rewords the risk signals in a non-accusatory tone (#219), adds a disclaimer next to the flags linking to the methodology, introduces a new "10. Risk signals" section, and adds noindex for contracts whose bidder is a sole-trader/natural person (GDPR).
Security (Phase 0): CLEAN — no secrets, only internal anchor links, no SQL/XSS surface (static JSX only), no new dependencies, no malicious patterns. OWASP: no relevant risks in this diff.
Non-blocking observations:
- methodology.tsx renumbers "Corrections" from §10 → §11 and inserts a new §10. Internal cross-references (§8, §11) and the TOC look consistent — please double-check nothing else references the old "section 10".
- RiskIndicators.tsx now renders the heading + disclaimer unconditionally; please confirm an early return exists when
flagsis empty so an empty list + disclaimer isn't shown.
No tests are included, which is acceptable for a text/copy + accessibility change with no new logic.
…idt-bg#219) Co-requisite of the risk numbers (midt-bg#218): the disclaimer must travel with the flags wherever they appear, not only next to the homepage leading number. - RiskIndicators (contract page): add a „сигнал, не присъда" note that links to /methodology#flagged, so the caveat sits directly beside the flags. - Reframe the flag copy to a non-accusatory signal tone: drop „повишен риск според стандартите на ОЛАФ", „Значително оскъпяване" and „подлежи на допълнителна проверка"; state the structural fact instead. Labels now mirror methodology §10 / homepage FLAG_LABELS. - Rename the heading „Индикатори за риск" → „Сигнали за риск" for one vocabulary. - Methodology §10: add an „Ограничения" note — signals rest on public data and structural indicators, source errors are possible, a signal can be a false positive and is not proof of wrongdoing; links to the gaps (§8) and corrections (§11) sections.
…isk flags (midt-bg#219 review) Deep GDPR review follow-up. The contract detail page renders the bidder name together with the „Сигнали за риск" box, but — unlike sole-trader company profiles (company.tsx) and the sitemap — it was not noindex'd. That left an identifiable individual (ЕТ) publicly associated with a risk label on an indexable, edge-cached page. - contract.tsx: emit robots=noindex when the bidder is a natural person (isNaturalPersonProfileName), mirroring the company-profile policy. The contract stays fully public on the site; only search-engine amplification is avoided. Verified: ЕТ-bidder contract → noindex, АД-bidder contract → indexable. - RiskIndicators: add a „Смятате сигнал за грешен?" rectification link to /methodology#contact next to the flags, matching the homepage disclaimer.
338a1cb to
8e48e2b
Compare
ydimitrof
left a comment
There was a problem hiding this comment.
Преглед на PR #219 — „docs/web: необвинителна уговорка за рисковите флагове“
ВЕРДИКТ: COMMENT — няма блокиращи проблеми; 3 незадължителни бележки за уточняване (не публикувам промени по ваше искане).
Обобщение
Промяната е предимно текстова (copy) + документация и изпълнява целта на #219: преформулира рисковите сигнали така, че да са необвинителни („ориентир, не присъда“), добавя уговорка до самите флагове, документира критериите и ограниченията в методология §10 и въвежда noindex за договори на физически лица (ЕТ) от съображения за ЗЗЛД/GDPR. Реализацията съответства на описанието на тикета.
Фаза 0 — Сигурност (ЧИСТО ✅)
- Няма твърдо кодирани тайни, ключове или токени.
- Няма SQL, сурови заявки,
dangerouslySetInnerHTML,evalили динамично изпълнение — цялото съдържание е статичен JSX и се екранира автоматично. Няма риск от SQL инжекция или XSS. - Промените по URL са само вътрешни котви (
/methodology#flagged,/methodology#contact) — безопасни. - Нови импорти:
Link(react-router, вече използван) иisNaturalPersonProfileName(@sigma/shared) — няма нови външни зависимости. - OWASP: не се въвежда нова атакувана повърхност; промяната
noindexе в полза на защитата на личните данни. Няма злонамерени шаблони, задни вратички или обфускация.
Съответствие с изискванията
- Обхватът е атомарен и фокусиран върху една задача (#219); няма разрастване на обхвата.
- Достъпност: използват се коректно
aria-labelledby/idв новата секция. - Съгласуваност: праговете в §10 („над 20%“, „текуща стойност > 1.2 × стойност при подписване“) съвпадат с логиката, показвана чрез
pct(flag.deltaPct!).
Бележки за уточняване (незадължителни)
- Скролване към котва при react-router
Link— линковете/methodology#flaggedи/methodology#contactводят към друг маршрут с хеш.react-routerпо подразбиране не скролва до фрагмента при клиентска навигация безScrollRestoration/собствен hook. Моля, проверете, че потребителят реално попада на съответната секция. - Съгласуваност
noindex↔ sitemap — договорите на физически лица получаватnoindex, но остават публични. Ако тези URL адреси все още присъстват в sitemap, ще рекламираме страница сnoindex(лека непоследователност спрямо изключването на профилите на ЕТ от sitemap). Струва си да се потвърди, чеisNaturalPersonProfileNameпокрива същите имена като на профилните страници. - Пренумериране на секциите — „Поправки и обратна връзка“ става раздел 11. Вътре в дифа препратките са коректни (§10 → #flagged, §11 → #contact), но моля проверете, че никъде другаде в приложението няма препратка към „раздел 10“, сочеща към контактите.
Тестове
PR-ът е текстов/документационен и не съдържа тестове. Ако проектните правила изискват snapshot/юнит покритие за променените компоненти, обмислете добавяне; иначе за промяна само на copy това е приемливо.
lyubomir-bozhinov
left a comment
There was a problem hiding this comment.
Прегледах последните 2 комита (стека върху #218/#236). Уговорката е добре направена, а по-важното — noindex-ът за ЕТ е коректната поверителностна мярка и я потвърдих в кода: meta() бута robots: noindex, когато isNaturalPersonProfileName(c.bidder.displayName) е истина, тъй че страницата на договора — която носи кутията „Сигнали за риск" — не се индексира, когато изпълнителят е физическо лице (ЕТ). Огледало на профилната политика. Това е точно моделът, който предложих за /overruns (#171) — редно е да се приложи и там, за да е политиката за целия сайт.
Една бележка (не блокира): isNaturalPersonProfileName хваща само префикс „ЕТ „/„ET " (format.ts:198). ЕТ, записан без точно този префикс (или „ЕДНОЛИЧЕН ТЪРГОВЕЦ …", или голо име), минава → пак се индексира. Реален напредък е и ползва съществуващата конвенция, тъй че не блокирам — но по-надеждното покритие е изключване по правна форма на SQL ниво (същата бележка като на #236), не post-hoc по низа на името.
Неутралните формулировки („сигнал, не присъда", махнатите „повишен риск"/„оскъпяване") са наред. Одобрявам.
Resolve the contract.tsx import conflict by keeping both sides: the branch's isNaturalPersonProfileName (used by the natural-person noindex guard) and main's getDb + CohortBand (the „Подобни договори" cohort from midt-bg#210). Also correct an inverted claim in both disclaimer texts: the date flag fires when a contract is signed AFTER its publication date, not before - scripts/normalize-raw.sql:933 sets 'signed_after_publication' on `contract_date > date(published_at, '+2 day')`. The methodology copy now also states the two-day tolerance, so the reader can tell an entry-lag from an unexplained ordering.
todorkolev
left a comment
There was a problem hiding this comment.
Одобрявам. Уговорката до флаговете е точно това, което липсваше - структурният факт да пътува заедно със сигнала, вместо читателят да я търси в методологията.
Поправих две неща в клона: разреших конфликта в contract.tsx (запазих и isNaturalPersonProfileName от клона, и getDb + CohortBand от main след #210), и обърнатото твърдение - и двата текста казваха „подписан ПРЕДИ датата си на публикуване", а scripts/normalize-raw.sql:933 вдига флага при contract_date > published_at + 2 дни, тоест СЛЕД. В методологията добавих и двудневния допуск, за да се различава забавяне при въвеждане от необяснен ред на датите.
Останалите нишки проверих на главата: ранният изход при празни флагове съществува (RiskIndicators.tsx:14-16), признаците са четири и текстът казва четири, а извън методологията няма препратки по номер на раздел.
Затваря issue #219 — необвинителна уговорка за рисковите флагове (co-requisite на рисковите числа от #218).
Какво прави
RiskIndicators) — уговорката „сигнал, не присъда" вече стои до самите флагове, с връзка към методологията и към „Смятате сигнал за грешен?". Формулировките са пренаписани неутрално (махнати са „повишен риск според ОЛАФ", „Значително оскъпяване", „подлежи на проверка"); заглавието е уеднаквено на „Сигнали за риск".noindex, когато изпълнителят е физическо лице (ЕТ) — за да не се индексира именуван човек до етикет „риск" (огледало на политиката за профилите на ЕТ). Договорът остава публичен на сайта. Проверено: договор с ЕТ →noindex; договор с АД → индексируем.Проверки
pnpm typecheck7/7 ·@sigma/web366 теста ·pnpm lintчист. Само copy/UI/meta — без промени по данни/схема/SQL.Този клон е разклонен над PR #236 (#218), който още не е слят. Затова diff-ът тук съдържа и трите #218 комита. За ревю на #219 гледайте:
4830b4bfeat(web): non-accusatory disclaimer next to per-contract risk flags (docs/web: страница „Методология" + необвинителна уговорка за рисковите флагове #219)01d5abcfix(web): noindex contract pages of natural-person bidders (docs/web: страница „Методология" + необвинителна уговорка за рисковите флагове #219 review)След merge на #236 в
main, тези #218 комити ще отпаднат от diff-а автоматично. Затова PR-ът е draft — за merge след #236.