Skip to content

docs/web: необвинителна уговорка за рисковите флагове (#219) - #237

Merged
todorkolev merged 3 commits into
midt-bg:mainfrom
DiyanaDimitrova:docs/risk-flag-disclaimer
Jul 30, 2026
Merged

docs/web: необвинителна уговорка за рисковите флагове (#219)#237
todorkolev merged 3 commits into
midt-bg:mainfrom
DiyanaDimitrova:docs/risk-flag-disclaimer

Conversation

@DiyanaDimitrova

Copy link
Copy Markdown
Contributor

Затваря issue #219 — необвинителна уговорка за рисковите флагове (co-requisite на рисковите числа от #218).

Какво прави

  • Методология §10 вече посочва изрично ограниченията: сигналите стъпват на публични данни и структурни признаци, възможни са грешки в източника, един сигнал може да е фалшив и не е констатация за нарушение (връзки към раздел 8 „празнини" и раздел 11 „поправки").
  • Страница на договора (RiskIndicators) — уговорката „сигнал, не присъда" вече стои до самите флагове, с връзка към методологията и към „Смятате сигнал за грешен?". Формулировките са пренаписани неутрално (махнати са „повишен риск според ОЛАФ", „Значително оскъпяване", „подлежи на проверка"); заглавието е уеднаквено на „Сигнали за риск".
  • GDPR hardening (дълбоко ревю): страницата на договора вече е noindex, когато изпълнителят е физическо лице (ЕТ) — за да не се индексира именуван човек до етикет „риск" (огледало на политиката за профилите на ЕТ). Договорът остава публичен на сайта. Проверено: договор с ЕТ → noindex; договор с АД → индексируем.

Проверки

pnpm typecheck 7/7 · @sigma/web 366 теста · pnpm lint чист. Само copy/UI/meta — без промени по данни/схема/SQL.

⚠️ Стек — рецензирайте само последните 2 комита

Този клон е разклонен над PR #236 (#218), който още не е слят. Затова diff-ът тук съдържа и трите #218 комита. За ревю на #219 гледайте:

След merge на #236 в main, тези #218 комити ще отпаднат от diff-а автоматично. Затова PR-ът е draft — за merge след #236.

@DiyanaDimitrova
DiyanaDimitrova force-pushed the docs/risk-flag-disclaimer branch from 01d5abc to 6a67592 Compare July 15, 2026 12:30
@DiyanaDimitrova
DiyanaDimitrova marked this pull request as ready for review July 15, 2026 13:02
@DiyanaDimitrova
DiyanaDimitrova force-pushed the docs/risk-flag-disclaimer branch from 1341235 to fca0be4 Compare July 15, 2026 13:07

@ydimitrof ydimitrof left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Преглед на 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; след това е готова за одобрение.

Comment thread apps/web/app/routes/methodology.tsx
@DiyanaDimitrova
DiyanaDimitrova force-pushed the docs/risk-flag-disclaimer branch from fca0be4 to 338a1cb Compare July 15, 2026 13:32

@ydimitrof ydimitrof left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Преглед на 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 е разумна допълнителна мярка за защита на лични данни, съгласувана със съществуващото третиране на ЕТ профили.

Наблюдения (незадължителни, не блокират)

  1. methodology.tsx — пренномериране на раздели. „Поправки и обратна връзка" се мести от §10 на §11, а новата §10 е вмъкната. Вътрешните препратки (към §8 и §11) изглеждат коректни и TOC е обновен. Моля, проверете дали някъде другаде в кода/съдържанието няма препратка към старата номерация „раздел 10".
  2. 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:

  1. 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".
  2. RiskIndicators.tsx now renders the heading + disclaimer unconditionally; please confirm an early return exists when flags is 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.

Comment thread apps/web/app/routes/methodology.tsx
Comment thread apps/web/app/components/RiskIndicators.tsx
Comment thread apps/web/app/routes/contract.tsx
…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.
@DiyanaDimitrova
DiyanaDimitrova force-pushed the docs/risk-flag-disclaimer branch from 338a1cb to 8e48e2b Compare July 16, 2026 19:57

@ydimitrof ydimitrof left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Преглед на 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!).

Бележки за уточняване (незадължителни)

  1. Скролване към котва при react-router Link — линковете /methodology#flagged и /methodology#contact водят към друг маршрут с хеш. react-router по подразбиране не скролва до фрагмента при клиентска навигация без ScrollRestoration/собствен hook. Моля, проверете, че потребителят реално попада на съответната секция.
  2. Съгласуваност noindex ↔ sitemap — договорите на физически лица получават noindex, но остават публични. Ако тези URL адреси все още присъстват в sitemap, ще рекламираме страница с noindex (лека непоследователност спрямо изключването на профилите на ЕТ от sitemap). Струва си да се потвърди, че isNaturalPersonProfileName покрива същите имена като на профилните страници.
  3. Пренумериране на секциите — „Поправки и обратна връзка“ става раздел 11. Вътре в дифа препратките са коректни (§10 → #flagged, §11 → #contact), но моля проверете, че никъде другаде в приложението няма препратка към „раздел 10“, сочеща към контактите.

Тестове

PR-ът е текстов/документационен и не съдържа тестове. Ако проектните правила изискват snapshot/юнит покритие за променените компоненти, обмислете добавяне; иначе за промяна само на copy това е приемливо.

Comment thread apps/web/app/components/RiskIndicators.tsx
Comment thread apps/web/app/routes/contract.tsx
Comment thread apps/web/app/routes/methodology.tsx

@lyubomir-bozhinov lyubomir-bozhinov left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Прегледах последните 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 todorkolev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Одобрявам. Уговорката до флаговете е точно това, което липсваше - структурният факт да пътува заедно със сигнала, вместо читателят да я търси в методологията.

Поправих две неща в клона: разреших конфликта в contract.tsx (запазих и isNaturalPersonProfileName от клона, и getDb + CohortBand от main след #210), и обърнатото твърдение - и двата текста казваха „подписан ПРЕДИ датата си на публикуване", а scripts/normalize-raw.sql:933 вдига флага при contract_date > published_at + 2 дни, тоест СЛЕД. В методологията добавих и двудневния допуск, за да се различава забавяне при въвеждане от необяснен ред на датите.

Останалите нишки проверих на главата: ранният изход при празни флагове съществува (RiskIndicators.tsx:14-16), признаците са четири и текстът казва четири, а извън методологията няма препратки по номер на раздел.

@todorkolev
todorkolev merged commit c073691 into midt-bg:main Jul 30, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants