From f6e317d871af206fa62e6da67545316f62109079 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 00:11:55 +0300 Subject: [PATCH] process: a failed read of open issues never files a duplicate (#700 r1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CODE-REVIEW-700-r1 Medium: `gh issue list … || true` turned a failed read into an empty answer, and the step went on to gh issue create — a second [workflow-sync] issue next to the open one on every network or rate-limit failure. A failed read now warns and exits 0; creating stays reserved for «read succeeded, nothing open». Mutant workflow-sync-issue-duplicated-on-read-failure. Issue: #700 User-Visible: no Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018qZfe7YS4rqEMKoVeS3GKd --- .github/workflows/validate.yml | 9 +++++++-- scripts/mutation-registry.mjs | 11 +++++++++++ test/validate-workflow.test.mjs | 3 +++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index f6975991..a307016d 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -158,8 +158,13 @@ jobs: RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | marker="[workflow-sync]" - existing=$(gh issue list --repo "$REPO" --state open --search "\"$marker\" in:title" \ - --json number,title --jq '[.[] | select(.title | startswith("[workflow-sync]"))][0].number // empty' || true) + # Список не прочитан — нового issue нет: иначе сбой сети или + # рейт-лимит плодил бы дубликат к уже открытому (r1 #700). + if ! existing=$(gh issue list --repo "$REPO" --state open --search "\"$marker\" in:title" \ + --json number,title --jq '[.[] | select(.title | startswith("[workflow-sync]"))][0].number // empty'); then + echo "::warning::открытые issue не прочитаны — новое не заводится, чтобы не завести дубликат" + exit 0 + fi if [ -n "$existing" ]; then gh issue comment "$existing" --repo "$REPO" --body "Расхождение повторилось: $RUN_URL" \ || echo "::warning::комментарий в #$existing не оставлен" diff --git a/scripts/mutation-registry.mjs b/scripts/mutation-registry.mjs index 4d69eebe..2e2723f5 100644 --- a/scripts/mutation-registry.mjs +++ b/scripts/mutation-registry.mjs @@ -12718,6 +12718,17 @@ const MUTANT_DEFINITIONS = [ replace: ' check "тонкие вызывающие workflow в main и dev" "$WORKFLOW_SYNC"', }], }, + { + id: 'workflow-sync-issue-duplicated-on-read-failure', + guard: 'node --test --test-name-pattern="#700: на ветке задачи" test/validate-workflow.test.mjs', + because: 'r1 #700: a failed read of the open issues must not fall through into gh issue create — ' + + 'that duplicates the owner issue on every network or rate-limit failure', + patches: [{ + file: '.github/workflows/validate.yml', + find: ' exit 0\n fi\n if [ -n "$existing" ]; then', + replace: ' :\n fi\n if [ -n "$existing" ]; then', + }], + }, { id: 'external-link-warn-mode-ignored', guard: 'node --test --test-name-pattern="#700: check-docs" test/validate-workflow.test.mjs', diff --git a/test/validate-workflow.test.mjs b/test/validate-workflow.test.mjs index f7916da3..68342717 100644 --- a/test/validate-workflow.test.mjs +++ b/test/validate-workflow.test.mjs @@ -628,6 +628,9 @@ test('#700: на ветке задачи зеркало workflow и внешни const issue = preflight.slice(preflight.indexOf('- name: "Расхождение зеркала на dev — issue владельцу"')); assert.match(issue, /if: github\.event_name == 'push' && github\.ref == 'refs\/heads\/dev' && steps\.workflow_sync\.outcome == 'failure'/); assert.match(issue, /gh issue list --repo "\$REPO" --state open --search/, 'одно issue, а не одно на каждый push'); + // r1 #700: несчитанный список — не повод заводить новое. + assert.match(issue, /if ! existing=\$\(gh issue list [\s\S]*?\); then\n\s+echo "::warning::[^"]*"\n\s+exit 0\n\s+fi/); + assert.doesNotMatch(issue.slice(0, issue.indexOf('gh issue create')), /\|\| true\)/, 'сбой чтения не глушится в пустой ответ'); assert.match(preflight, /permissions:\n\s+contents: read\n\s+actions: read\n\s+issues: write/); });