Skip to content

v1.19.0

Choose a tag to compare

@github-actions github-actions released this 09 Jun 16:28
· 7 commits to master since this release

Добавлено

  • Единый граф 1С-потока управления (шаг 3 роадмапа call-graph). Метод вызывается не только из кода: его дёргают подписки на события, обработчики форм, регламентные задания и CFE-перехваты расширений. Новый ридер IndexReader.get_inbound_edges(proc_name, module_hint="") подмешивает к direct-рёбрам (calls) 4 источника не-call рёберevent_subscriptions, обработчики form_elements (kind='handler'), scheduled_jobs, extension_overrides (CFE) — разрешённые через то же пространство ключей callee_key. Контракт: всегда list[dict] (НИКОГДА None), каждое ребро {edge_type, source_name, source_kind, detail, file, line, caller_name, object_name, category, target_key, resolved}. Зеркалит дизайн get_callers: resolved=True ⇔ матч по стабильному target_key, resolved=False ⇔ recall-матч по имени (extension-only source_path=="", нерезолвимый/dotless handler_module) — рёбра не теряются. Кириллический регистр сворачивается в Python .casefold() (не COLLATE NOCASE, который сворачивает только ASCII).
  • find_call_hierarchy(..., include_triggers=False) — opt-in аннотация триггеров. При True к каждому узлу дерева добавляется ключ triggers (= get_inbound_edges для метода узла). По умолчанию (False) вывод байт-в-байт прежний — ключ triggers добавляется ТОЛЬКО под флагом. Триггеры — листовая аннотация, НЕ новые цели BFS (подписка/задание/форма/перехват — точка входа, а не «caller»).
  • find_path(from_name, to_name, max_depth=4, from_hint="", to_hint="", include_triggers=False) — достижимость по графу ВЫЗОВОВ. Индексный реверс-BFS callers от to_name (форвард-callees = full scan, индекс снят). Возвращает forward-путь [from → … → to]; call_line элемента — строка РЕБРА к следующему узлу (НЕ определения; у терминального toNone). _meta.precision='exact'to разрешён точно И все рёбра пути по callee_key; иначе 'heuristic' (старый индекс / FS-fallback / совпадение по имени) — found=True = достижимость по имени, не доказанный путь. from_hint/to_hint пинят одноимённые методы к модулю.
  • find_data_path(from_object, to_object, max_depth=4, kinds=None) — N-hop BFS по графу метаданных (metadata_references через новый ридер IndexReader.find_metadata_refs_from). Каждый элемент пути — РЕБРО {from, to, kind, used_in, path, line}. Контракт: endpoints с префиксом (Справочник.X/Catalog.X, Документ.Y/Document.Y); bare-вход без префикса → структурный hint без обхода. source_category различает одноимённые Catalog.X vs Document.X. Отдельный скромный бюджет узлов (~400), т.к. каждый узел = один py_lower-скан таблицы.
  • 2 бизнес-домена в rlm_help: достижимость (алиасы reachability, путь вызовов, доходит ли) и путь данных (алиасы data path, как связаны, граф данных); оба хелпера автоматически попали в slim/full-индекс хелперов.

Изменено

  • rlm_start — убран ~30К inline-дамп перехватов из extension_context (токен-экономия на extension-конфигах). Поле nearby_extensions[].overrides (полный список перехватов каждого расширения, ~30К на крупном ERP-конфиге с расширениями ~200 перехватов) заменено на overrides_count; own_overridesown_overrides_count. Дамп был шумом: дублировал get_overrides()/find_ext_overrides(), не был actionable из песочницы (это текст тул-ответа, не переменная), не использовался агентами (e2e: все перезапрашивали через get_overrides), и его платила КАЖДАЯ сессия extension-конфига (включая не связанные с расширениями). Полезный сигнал (присутствие + имена/purpose/prefix + счётчик) сохранён; детали — по запросу get_overrides('Объект'). Бойлерплейт-саммари в стратегии («CRITICAL EXTENSIONS DETECTED», ограниченный по строкам) не тронут. Для обычных src без cfe rlm_start и так был ~20–22К — там ничего не менялось.
  • IndexReader.get_callers — единственное аддитивное поле edge_exact в caller-rows (per-edge признак: ребро матчнулось по callee_key, не по имени). Нужно find_path для честного precision (агрегат exact_rows/fallback_rows не несёт per-row признак). Поле новое; существующие потребители (find_call_hierarchy) его игнорируют, _meta-контракт не тронут.
  • Новый публичный IndexReader.resolve_target_identity(proc_name, module_hint="")locked-обёртка над внутренним lockless _resolve_target_key (его штатно зовёт get_callers/get_inbound_edges уже под self._lock, а это threading.Lock, не RLock). find_path (вне reader-лока) дёргает только её.

Совместимость

  • Схема индекса и BUILDER_VERSION (14) НЕ менялись — перестройка боевых индексов НЕ требуется. Оба новых ридера только ЧИТАЮТ уже существующие таблицы (event_subscriptions/scheduled_jobs/form_elements/extension_overrides/modules — стандартные; metadata_references — с v1.9.0; calls.callee_key — с v1.16.0). Любой индекс v1.16.0+ уже прошёл разовый v14-ребилд → ноль действий. Старые индексы (BUILDER_VERSION<14) — мягкая деградация без падений до того же v14-ребилда, что уже ввела v1.16.0 (не новое требование): get_inbound_edges→[], find_metadata_refs_from→None, find_data_path→partial:True, find_path работает по name-based callers с precision="heuristic".
  • find_call_hierarchy(...) без нового аргумента и get_callers (кроме аддитивного edge_exact) — байт-в-байт прежнее поведение.

Исправлено (контракты хелперов, найдено e2e-прогоном; в духе v1.18.0)

  • safe_grep / find_callers — sig врала про имя параметра. Регистрируемые сигнатуры называли аргумент hint, фактические параметры — name_hint (safe_grep) и module_hint (find_callers) → агент, вызвавший по документации safe_grep(p, hint=...) / find_callers(proc, hint=...), ловил TypeError. Обе sig исправлены (последние два таких рассогласования — аудит остальных sig чист).
  • Бизнес-рецепт расширения — неверный контракт get_overrides(). code_hint итерировал get_overrides() как список с ключом extension, тогда как хелпер возвращает {overrides:[...], total, source}, а ключ перехвата — extension_nameTypeError/KeyError. Рецепт переписан под фактический контракт (проверено живым вызовом: dict с 199 перехватами, ключ extension_name).
  • get_overrides() live — нормализация extension_name во ВСЕХ ветках. В live-ветке для сессии, открытой прямо на расширении (role=EXTENSION), сырые строки find_extension_overrides не несли extension_name/extension_root (в отличие от index-строк и live-ветки main-сессии) → тот же рецепт снова падал KeyError в этой допустимой ветке контракта (подтверждено живым вызовом: 195 перехватов без extension_name). Теперь поля проставляются из идентичности текущего расширения — контракт «у каждого перехвата есть extension_name» честен для всех источников. Регрессия закрыта тестом TestLiveOverridesContract.
  • find_roles / find_register_movements — недоспецифицированные sig. find_roles(...).roles[].rights — это list[str] (имена прав), не dict (агент звал .items()); у find_register_movements только code_registers — список словарей, а erp_mechanisms/manager_tables/adapted_registers — списки строк (агент звал .get() на строке). Формы элементов прописаны в sig (проверено живым вызовом).
  • find_module — sig теперь явно говорит, что принимает только name (module_type/category — поля вывода, не фильтры), убирая соблазн find_module(module_type=...).
  • analyze_document_flow — sig раскрывает форму ({document, metadata, event_subscriptions, register_movements, …} — это dict с конкретными ключами; вложенный register_movements — тоже dict), вместо прежнего расплывчатого metadata + subscriptions + …, на котором агент пробовал срез [:N] по dict.
  • Толерантные/самокорректирующиеся контракты (e2e-урок: doc-ремарки не останавливают угадывание). Повторный прогон показал, что чисто-документационные правки find_module/analyze_document_flow агент игнорирует (зовёт до чтения sig), поэтому:
    • find_module(name='', module_type='', category='')name опционален + опциональные фильтры (case-insensitive, до cap'а 50). Раньше find_module(module_type=...) падал TypeError (рекуррентная грабля) — причём в ДВУХ формах: с именем (unexpected keyword argument) и фильтр-только без имени (missing positional argument 'name'). Теперь работают обе: пустое name = «любой модуль», сужённый фильтрами.
    • Перф get_inbound_edges — устранён O(N) над метаданными НА КАЖДЫЙ узел include_triggers-дерева. e2e на ERP вскрыл find_call_hierarchy(include_triggers=True) ~250с на 60-узловом дереве. Прямой замер (get_inbound_edges = ~4с/вызов, а caller-BFS 60 узлов = 0.04с) выявил ДВА источника:
      • (доминанта) subscription/scheduled forward-JOIN. Запрос джойнил ВСЕ подписки к CommonModules через py_lower(object_name)=py_lower(handler_module) (UDF по тысячам модулей на каждую подписку), а фильтр по handler_procedure шёл в Python. Фикс: имя handler_procedure сведено в SQL WHERE … py_lower(handler_procedure)=py_lower(?) → джойнятся только 0–несколько совпавших подписок. ~4с → миллисекунды.
      • (вторично) form_event recall. Recall-ветка делала полный скан form_elements на каждый узел; теперь: не-форменная цель → скан пропущен (форменный обработчик там невозможен), неразрешённая → сужение py_lower(handler)=py_lower(?) в SQL.
        Корректность сохранена (resolved по индексу, recall по имени; Cyrillic-aware). Покрыто test_form_recall_skipped_for_nonform_target + существующими subscription/scheduled/recall/casefold-тестами. Read-only логика — BUILDER_VERSION и пересборка индексов не затронуты.
    • Sandbox error-hints (_add_error_hints) на типовые угадывания: неверный kwarg safe_grep(path=/hint=) → подсказка про name_hint; срез dict как списка (KeyError: slice(...) на analyze_document_flow/get_overrides/find_register_movements/find_path/find_data_path) → подсказка взять список по ключу; .get() на результате read_procedure → подсказка, что это строка-тело, а не dict; detect_extensions() использован как список (итерация/[0]) → подсказка, что это dict, а расширения — в ctx['nearby_extensions'] (рекуррентное телепание агентов, sig/рецепт при этом корректны). Хард-ошибка → самокоррекция, без потери раунда на угадывание.

Тесты

  • Новые tests/test_call_graph_edges.py (13) — все 4 типа рёбер, resolved/recall (extension-only source_path=="", нерезолвимый и dotless handler_module), кириллица-casefold, no-op на индексе без таблиц, include_triggers on/off; tests/test_find_path.py (9) — forward-цепочка, call_line как метаданные ребра, precision exact/heuristic, from_hint-disambig, budget_exceeded, аддитивный edge_exact, _reg sig/recipe. Расширен tests/test_find_references.pyfind_metadata_refs_from outbound + коллизия категорий + None без таблицы; find_data_path 1-/2-hop + RU/EN + kinds-фильтр + коллизия категорий + контракт префикса + partial без таблицы.

Полный список изменений: CHANGELOG.md