Skip to content

feat(ip): owner_check у досьє ТМ для фізосіб — фан-аут за ПІБ + skip-маркери - #2199

Open
teosoph wants to merge 1 commit into
overthelex:mainfrom
teosoph:feat/tm-dossier-individual-owner-check
Open

feat(ip): owner_check у досьє ТМ для фізосіб — фан-аут за ПІБ + skip-маркери#2199
teosoph wants to merge 1 commit into
overthelex:mainfrom
teosoph:feat/tm-dossier-individual-owner-check

Conversation

@teosoph

@teosoph teosoph commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

Проблема

get_trademark_dossier формував owner_check лише для юросіб (tm.owner_edrpou ? callTool('openreyestr_get_by_edrpou') : null). Для фізосіб-власників (owner_edrpou=null) повертався голий null — перевірка правовласника не виконувалась взагалі, і неможливо було відрізнити «немає що перевіряти» від «сервіс недоступний». Кейс: ТМ 67482, власник Дьяконенко О.Є. Доробка зафіксована в клієнтських документах МСП як «деанонімізація + звірка за РНОКПП».

Що зроблено

  • Юрособа — як було (openreyestr_get_by_edrpou), але при недоступності сервісу тепер { skipped: true, reason: 'service_unavailable' } замість null.
  • Фізособа — best-effort фан-аут за ПІБ (tm.owner_name) по 7 джерелах: openreyestr_search_debtors, openreyestr_search_enforcement_proceedings, openreyestr_search_bankruptcy_cases, search_registry(registry=sanctions), openreyestr_search_entities (ФОП/участь у юрособах), search_ip_objects (портфель ІВ), search_court_hearing_schedule(participant, source=opendata). Кожне джерело — try/catch: недоступний сервіс дає { skipped: true, reason: 'service_unavailable' } у своєму ключі й не валить досьє.
  • Маркування ймовірних збігів: match_basis: 'name_only' + caveat (у реєстрі ТМ немає РНОКПП — можливі однофамільці) + owner_address з raw_data (SIS HolderAddressBook/ApplicantAddressBook) для ручної адресної звірки.
  • Обсяг обмежено: limit 10 на джерело (OWNER_FANOUT_LIMIT).
  • Без власника взагалі: { skipped: true, reason: 'no_owner_identifier' }.
  • guidance досьє доповнено інструкцією для розділу «Перевірка правовласника» (name_only → ймовірні збіги + адресна звірка).

Тести

ip-objects-tools.test.ts (+5 кейсів, TDD): фізособа (без skipped, є probable_matches, правильні аргументи фан-ауту, ліміти ≤10, openreyestr_get_by_edrpou не викликається), юрособа happy-path (як було), юрособа при падінні сервісу (service_unavailable), падіння одного джерела фан-ауту (маркер лише в ньому), відсутній власник (no_owner_identifier).

Tests: 21 passed, 21 total
tsc: OK (з core overlay)

🤖 Generated with Claude Code


Summary by cubic

Розширили owner_check у досьє ТМ: тепер працює і для фізосіб через фан-аут за ПІБ з чіткими skip-маркерами при недоступності сервісів. Для юросіб збережено перевірку за ЄДРПОУ, але замість null тепер повертаємо skip-маркер.

  • New Features
    • Фізособи: best-effort фан-аут за ПІБ до 7 джерел з лімітом 10 на джерело (openreyestr_search_debtors, openreyestr_search_enforcement_proceedings, openreyestr_search_bankruptcy_cases, search_registry(registry='sanctions'), openreyestr_search_entities, search_ip_objects, search_court_hearing_schedule(source='opendata')).
    • Стійкість: кожне джерело обгорнуте в try/catch; при падінні повертаємо { skipped: true, reason: 'service_unavailable' }, досьє не валиться.
    • Маркування збігів: match_basis: 'name_only' + попередження про однофамільців; додаємо owner_address з raw_data для ручної звірки.
    • Юрособи: як і раніше через openreyestr_get_by_edrpou; при недоступності сервісу — { skipped: true, reason: 'service_unavailable' }.
    • Без ідентифікатора власника: { skipped: true, reason: 'no_owner_identifier' }.
    • Оновлено guidance для секції «Перевірка правовласника» (ймовірні збіги + адресна звірка).

Written for commit 6254e8d. Summary will update on new commits.

Review in cubic

…маркери

Раніше get_trademark_dossier виконував перевірку правовласника лише для
юросіб (owner_edrpou), а для фізосіб повертав голий null — не відрізняючи
«немає що перевіряти» від «сервіс недоступний» (кейс ТМ 67482).

- юрособа: як було (openreyestr_get_by_edrpou), але при падінні сервісу —
  { skipped: true, reason: 'service_unavailable' } замість null
- фізособа: best-effort фан-аут за ПІБ по 7 джерелах (боржники, виконавчі
  провадження, банкрутства, санкції, ЄДР/ФОП, портфель ІВ, розклад засідань),
  limit 10 на джерело; падіння окремого джерела не валить досьє
- збіги марковано match_basis: 'name_only' (можливі однофамільці — у реєстрі
  ТМ немає РНОКПП) + owner_address з raw_data для ручної адресної звірки
- без власника взагалі: { skipped: true, reason: 'no_owner_identifier' }

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot 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.

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="mcp_backend/src/api/tools/ip-objects-tools.ts">

<violation number="1" location="mcp_backend/src/api/tools/ip-objects-tools.ts:473">
P2: `extractOwnerAddress` falls back to the raw `address` object when neither `FreeFormatAddress.FreeFormatAddressLine` nor `FreeFormatAddressLine` exists. The consumer (`owner_address` in the dossier) expects a string for the AI to use in address cross-checking; returning the entire address object would produce confusing output in that edge case.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

?? raw?.ApplicantDetails?.Applicant?.[0]?.ApplicantAddressBook;
const address = party?.FormattedNameAddress?.Address;
if (!address) return null;
return address.FreeFormatAddress?.FreeFormatAddressLine ?? address.FreeFormatAddressLine ?? address;

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.

P2: extractOwnerAddress falls back to the raw address object when neither FreeFormatAddress.FreeFormatAddressLine nor FreeFormatAddressLine exists. The consumer (owner_address in the dossier) expects a string for the AI to use in address cross-checking; returning the entire address object would produce confusing output in that edge case.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At mcp_backend/src/api/tools/ip-objects-tools.ts, line 473:

<comment>`extractOwnerAddress` falls back to the raw `address` object when neither `FreeFormatAddress.FreeFormatAddressLine` nor `FreeFormatAddressLine` exists. The consumer (`owner_address` in the dossier) expects a string for the AI to use in address cross-checking; returning the entire address object would produce confusing output in that edge case.</comment>

<file context>
@@ -419,6 +421,58 @@ export class IpObjectsTools extends BaseToolHandler {
+      ?? raw?.ApplicantDetails?.Applicant?.[0]?.ApplicantAddressBook;
+    const address = party?.FormattedNameAddress?.Address;
+    if (!address) return null;
+    return address.FreeFormatAddress?.FreeFormatAddressLine ?? address.FreeFormatAddressLine ?? address;
+  }
+
</file context>
Suggested change
return address.FreeFormatAddress?.FreeFormatAddressLine ?? address.FreeFormatAddressLine ?? address;
return address.FreeFormatAddress?.FreeFormatAddressLine ?? address.FreeFormatAddressLine ?? null;

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.

1 participant