fix(pipeline-status): ignore issue refs inside code spans in PR body #96

Merged
slaid098 merged 3 commits from fix/pipeline-status/ignore-code-spans into main 2026-08-16 21:24:55 +03:00
Owner

Что сделано

Оракул pipeline-status теперь игнорирует упоминания issue-номеров внутри markdown-конструкций при резолюции issue по PR body:

  • добавлена функция маскирования, которая вырезает inline backtick-спаны, многострочные code-блоки и строки блочных цитат перед применением closure-регекса;
  • check_issue и find_open_prs_for_issue используют отмаскированный body — реальные привязки вне этих конструкций матчатся как раньше;
  • незакрытый backtick трактуется как текст (спан не начинается);
  • body из gh pr view --json body парсится через json.loads перед маскированием, чтобы экранированные переносы строк корректно обрабатывались quote-регексом.

Почему

Агенты часто цитируют примеры body-строк в описании PR — это класс ложных срабатываний: оракул распознавал цитаты тестовых данных как реальные Closes-связи и падал в ложный AMBIGUOUS, останавливая батч.

Watch out

  • AMBIGUOUS «несколько issue в body» сохраняется для реальных нескольких привязок вне code-спанов и цитат.
  • Поведение для тестов из #86 (дубликат одного номера → DONE, разные номера без спанов → AMBIGUOUS) не меняется.
  • Формат вывода фаз ISSUE и NEXT-строк не меняется.

Pending

  • Нет. pytest и ruff зелёные.

Closes #93

## Что сделано Оракул pipeline-status теперь игнорирует упоминания issue-номеров внутри markdown-конструкций при резолюции issue по PR body: - добавлена функция маскирования, которая вырезает inline backtick-спаны, многострочные code-блоки и строки блочных цитат перед применением closure-регекса; - `check_issue` и `find_open_prs_for_issue` используют отмаскированный body — реальные привязки вне этих конструкций матчатся как раньше; - незакрытый backtick трактуется как текст (спан не начинается); - body из `gh pr view --json body` парсится через `json.loads` перед маскированием, чтобы экранированные переносы строк корректно обрабатывались quote-регексом. ## Почему Агенты часто цитируют примеры body-строк в описании PR — это класс ложных срабатываний: оракул распознавал цитаты тестовых данных как реальные Closes-связи и падал в ложный AMBIGUOUS, останавливая батч. ## Watch out - AMBIGUOUS «несколько issue в body» сохраняется для реальных нескольких привязок вне code-спанов и цитат. - Поведение для тестов из #86 (дубликат одного номера → DONE, разные номера без спанов → AMBIGUOUS) не меняется. - Формат вывода фаз ISSUE и NEXT-строк не меняется. ## Pending - Нет. pytest и ruff зелёные. Closes #93
test(pipeline-status): guard JSON body parsing before quote masking
All checks were successful
CI (always) / bootstrap (pull_request) Successful in 7s
CI / bootstrap (pull_request) Successful in 9s
CI / lint (pull_request) Successful in 27s
CI / complexity (pull_request) Successful in 28s
CI / typecheck (pull_request) Successful in 28s
CI / test (3.13) (pull_request) Successful in 1m41s
77eaaff5e7
Author
Owner

Code Review Summary

PR исправляет класс ложных срабатываний: оракул pipeline-status теперь игнорирует Closes/Fixes/Resolves-ссылки внутри inline code-спанов, многострочных code-блоков и блочных цитат при резолюции issue по PR body. Реализация чистая: три модульных regex-константы, _mask_code_and_quotes (11 строк) с документированным порядком маскирования (блоки → спаны → цитаты, с сохранением структуры строк), _closure_refs как единая точка входа для check_issue и find_open_prs_for_issue. 15 новых тестов покрывают все ветки, включая незакрытый backtick и сохранение AMBIGUOUS для реальных множественных ссылок. 140 тестов зелёные, ruff чист, CI green.

Positives

  • Порядок маскирования корректен и документирован: code-блоки маскируются первыми (могут содержать inline backticks), замена на "\n" * count сохраняет структуру строк для quote-якоря
  • sorted(matches) — исправление недетерминированного порядка set-итерации в AMBIGUOUS-сообщении (раньше порядок номеров мог меняться между запусками)
  • Убран избыточный guard CLOSURE_RE.search(body) and ... в find_open_prs_for_issue — refs непустой уже implies search match
  • JSON-парсинг body через json.loads с fallback на raw-строку — корректно обрабатывает экранированные \n из gh pr view --json body (иначе quote-регекс с якорем на начало строки не сработал бы)
  • Cross-file impact отсутствует: CLOSURE_RE/_closure_refs используются только в pipeline-status.py; writer create-pr.ts:138 добавляет Closes #N в plain text (не в code span) → связь writer↔reader не нарушена; формат вывода фаз и NEXT-строк не меняется
  • Тесты регрессионные и именованные по issue #93, каждый с assert'ами

Suggestions (info, not blocking)

  • pipeline-status.py:84 [test] Незакрытый code-блок (``` без закрывающего) не покрыт тестом — refs внутри будут считаться реальными, что консистентно с философией незакрытого backtick'а (трактуется как текст), но стоит добавить тест-документацию этого edge case
  • tests/test_pipeline_status.py:220,238 [style] test_check_issue_ignores_refs_in_quote и test_check_issue_parses_json_body_before_masking используют идентичные body и assertions — второй тест дублирует покрытие первого, но служит документацией JSON-escape рациональности; допустимо, можно объединить

Verdict: APPROVE

## Code Review Summary PR исправляет класс ложных срабатываний: оракул pipeline-status теперь игнорирует Closes/Fixes/Resolves-ссылки внутри inline code-спанов, многострочных code-блоков и блочных цитат при резолюции issue по PR body. Реализация чистая: три модульных regex-константы, `_mask_code_and_quotes` (11 строк) с документированным порядком маскирования (блоки → спаны → цитаты, с сохранением структуры строк), `_closure_refs` как единая точка входа для `check_issue` и `find_open_prs_for_issue`. 15 новых тестов покрывают все ветки, включая незакрытый backtick и сохранение AMBIGUOUS для реальных множественных ссылок. 140 тестов зелёные, ruff чист, CI green. ### Positives - **Порядок маскирования корректен и документирован**: code-блоки маскируются первыми (могут содержать inline backticks), замена на `"\n" * count` сохраняет структуру строк для quote-якоря - **`sorted(matches)`** — исправление недетерминированного порядка set-итерации в AMBIGUOUS-сообщении (раньше порядок номеров мог меняться между запусками) - **Убран избыточный guard** `CLOSURE_RE.search(body) and ...` в `find_open_prs_for_issue` — `refs` непустой уже implies search match - **JSON-парсинг body** через `json.loads` с fallback на raw-строку — корректно обрабатывает экранированные `\n` из `gh pr view --json body` (иначе quote-регекс с якорем на начало строки не сработал бы) - **Cross-file impact отсутствует**: `CLOSURE_RE`/`_closure_refs` используются только в pipeline-status.py; writer `create-pr.ts:138` добавляет `Closes #N` в plain text (не в code span) → связь writer↔reader не нарушена; формат вывода фаз и NEXT-строк не меняется - **Тесты регрессионные и именованные** по issue #93, каждый с assert'ами ### Suggestions (info, not blocking) - **pipeline-status.py:84** [test] Незакрытый code-блок (``` без закрывающего) не покрыт тестом — refs внутри будут считаться реальными, что консистентно с философией незакрытого backtick'а (трактуется как текст), но стоит добавить тест-документацию этого edge case - **tests/test_pipeline_status.py:220,238** [style] `test_check_issue_ignores_refs_in_quote` и `test_check_issue_parses_json_body_before_masking` используют идентичные body и assertions — второй тест дублирует покрытие первого, но служит документацией JSON-escape рациональности; допустимо, можно объединить ### Verdict: APPROVE
slaid098 deleted branch fix/pipeline-status/ignore-code-spans 2026-08-16 21:24:55 +03:00
Sign in to join this conversation.
No reviewers
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
slaid098/opencode-config!96
No description provided.