Skip to content

Extract shared close-older marker search/filter logic into helpers module#26023

Merged
pelikhan merged 3 commits intomainfrom
copilot/refactor-close-older-handlers
Apr 13, 2026
Merged

Extract shared close-older marker search/filter logic into helpers module#26023
pelikhan merged 3 commits intomainfrom
copilot/refactor-close-older-handlers

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 13, 2026

searchOlderIssues and searchOlderDiscussions duplicate ~80 lines of marker-query construction and exact-marker filtering, differing only in API transport (REST vs GraphQL) and entity-specific exclusion checks.

Changes

  • New close_older_search_helpers.cjs with three shared functions:

    • buildMarkerSearchQuery() — constructs search query + exact marker from closeOlderKey / workflowId / callerWorkflowId, with optional entityQualifier (e.g. "is:issue")
    • filterByMarker() — excludes newly-created entity, verifies exact marker in body, delegates entity-specific checks to an additionalFilter callback
    • logFilterSummary() — consistent counter logging
  • Refactored close_older_issues.cjs and close_older_discussions.cjs — replaced inline query-build + filter blocks with calls to shared helpers. Transport and entity-specific filters (PR exclusion, closed/category checks) stay in their respective files.

  • 22 new tests in close_older_search_helpers.test.cjs, including parity tests verifying issue and discussion paths produce equivalent markers and filter results.

Usage

const { buildMarkerSearchQuery, filterByMarker, logFilterSummary } = require("./close_older_search_helpers.cjs");

const { searchQuery, exactMarker } = buildMarkerSearchQuery({
  owner, repo, workflowId, callerWorkflowId, closeOlderKey,
  entityQualifier: "is:issue", // omit for discussions
});

const { filtered, counters } = filterByMarker({
  items: result.data.items,
  excludeNumber,
  exactMarker,
  entityType: "issue",
  additionalFilter: (item, extra) => {
    if (item.pull_request) { extra.pullRequestCount = (extra.pullRequestCount || 0) + 1; return false; }
    return true;
  },
});

All 69 tests pass (47 existing + 22 new).

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw -trimpath g_.a git rev-�� --show-toplevel Po/5GniUk8RBwwF8S8eKncR/xY8IG6Cr96T_MkWd-kcE /usr/bin/git 3835083920 7589049/b290/vetrev-parse 1/x64/bin/node git (http block)
  • https://api.github.com/orgs/test-owner/actions/secrets
    • Triggering command: /usr/bin/gh gh api /orgs/test-owner/actions/secrets --jq .secrets[].name -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
  • https://api.github.com/repos/actions/ai-inference/git/ref/tags/v1
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha --show-toplevel x_amd64/vet /opt/hostedtoolcache/node/24.14.1/x64/bin/node -frontmatter.md GO111MODULE 64/pkg/tool/linu--show-toplevel /opt/hostedtoolcache/node/24.14.1/x64/bin/node /tmp�� env.NODE_VERSION 64/pkg/tool/linuTest User /usr/bin/git XAe_-DMg6 om/goccy/go-yamlrev-parse 64/pkg/tool/linu--show-toplevel git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v3
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha /tmp/gh-aw-test-runs/20260413-122010-33106/test-3321502721/.github/workflows rev-parse /usr/bin/git -json /context.go x_amd64/compile git -C /tmp/gh-aw-test-runs/20260413-122010-33106/test-1511544806/.github/workflows rev-parse ache/node/24.14.1/x64/bin/node -json GO111MODULE 64/bin/go git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v5
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha 1/001/test-frontmatter-with-env-template-expressions.md 7589049/b043/vet.cfg x_amd64/compile GOINSECURE til GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha REDACTED WKKjjpb/pnOHWmOWremote.origin.url /usr/bin/git y_with_repos=pubgit .cfg 64/pkg/tool/linu--show-toplevel git rev-�� --show-toplevel 64/pkg/tool/linux_amd64/vet /usr/bin/git 50848/b235/_pkg_git GO111MODULE ache/go/1.25.8/x--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel /opt/hostedtoolcache/go/1.25.8/x^remote\..*\.gh-resolved$ /usr/bin/git /ref/tags/v9 -buildtags /opt/hostedtoolc--show-toplevel git rev-�� --show-toplevel /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet /usr/bin/git -bool -buildtags /usr/bin/git git (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v8
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha --show-toplevel sole.test /usr/bin/git 1/001/test-complcut 7589049/b040/vet-f1 64/pkg/tool/linux_amd64/vet git rev-�� --show-toplevel 64/pkg/tool/linu^remote\..*\.gh-resolved$ /usr/bin/git 756/001/stabilitgit 7589049/b092/vetrev-parse ache/go/1.25.8/x--show-toplevel git (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v9
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq .object.sha -json GO111MODULE x_amd64/asm GOINSECURE GOMOD GOMODCACHE x_amd64/asm env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq .object.sha -json 1.5.0/internal/j-ifaceassert x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE x_amd64/compile GOINSECURE xcontext GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq .object.sha -json gset/set.go x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha add origin /usr/bin/git -json GO111MODULE x_amd64/vet git rev-�� --show-toplevel x_amd64/vet /usr/bin/git .a GO111MODULE 64/pkg/tool/linu--show-toplevel git (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha --get remote.origin.url /usr/bin/git ub/workflows 0/internal/langurev-parse x_amd64/vet git rev-�� --show-toplevel x_amd64/vet /usr/bin/git .a ke8fejfLv 64/pkg/tool/linu--show-toplevel git (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha /tmp/go-build1557589049/b403/_pkg_.a -trimpath /usr/lib/git-core/git -p main -lang=go1.25 /usr/lib/git-core/git unpa�� --pack_header=2,3 l /usr/bin/git go1.25.8 -c=4 -nolocalimports git (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v7
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha 2010-33106/test-2623208193/.github/workflows -trimpath ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet -p crypto/internal/rev-parse -lang=go1.25 ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet -o /tmp/go-build51350848/b230/_pkg_.a -trimpath 7589049/b198/vet.cfg -p github.com/modelrev-parse -lang=go1.25 Ksk-ObV/wpFhC1-pTVQFJobNjk4Q (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha ace-editor.md 7589049/b272/vet.cfg ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet -p github.com/segmerev-parse -lang=go1.17 ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet -uns�� -unreachable=false /tmp/go-build1557589049/b014/vet.cfg e/git-upload-pack go1.25.8 -c=4 -nolocalimports /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linuremote.origin.url (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha agent-performance-analyzer.md stmain.go 1/x64/bin/node -p vendor/golang.orrev-parse -lang=go1.25 ache/go/1.25.8/x64/pkg/tool/linux_amd64/link t-ha�� vaScript868424791/001/test-frontmatter-with-arrays.md -importcfg 7589049/b412/importcfg.link -s -w -buildmode=exe IcB-MrgPZhxrn/fiucaT9ZeFc2kAZlPnrepos/{owner}/{repo}/actions/runs/2/artifacts (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v0.1.2
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v0.1.2 --jq .object.sha --show-toplevel x_amd64/vet /usr/bin/git ub/workflows SUy_HbpQE x_amd64/vet git rev-�� --git-dir x_amd64/vet /usr/bin/git ub/workflows GO111MODULE 64/pkg/tool/linu--show-toplevel git (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --jq .object.sha ithub/workflows/ai-moderator.md -trimpath ache/node/24.14.1/x64/bin/node -p main -lang=go1.25 /usr/lib/git-core/git t-35�� bility_SameInputSameOutput865865756/001/stability-test.md --revs /usr/bin/git --thin --delta-base-offrev-parse -q git (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq .object.sha -bool -buildtags /bin/sh -errorsas -ifaceassert -nilfunc /bin/sh -c :latest git-receive-pack '/tmp/TestParseDefaultBranchFromLsRemoteWithRealGitbranch_with_hyphen1249198944rev-parse /usr/bin/git -json GO111MODULE x_amd64/asm git (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/1/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/1/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE 50848/b078/ GOMODCACHE 64/pkg/tool/linux_amd64/vet env e-analyzer.md GO111MODULE 64/pkg/tool/linux_amd64/link GOINSECURE g/x/crypto/chachinit ache/go/1.25.8/x64/src/syscall/js/js_js.s 64/pkg/tool/linux_amd64/link (http block)
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 om/modelcontextprotocol/go-sdk@v1.5.0/jsonrpc/js-ifaceassert 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linutest@example.com env 3907894075 aMu6/n6X7R7Av3bGkLZAPaMu6 k GOINSECURE a95/uritemplate/rev-parse GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu-test.v=true (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12345/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/12345/artifacts --jq .artifacts[].name .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env e-analyzer.md .cfg x_amd64/vet GOINSECURE contextprotocol/rev-parse GOMODCACHE x_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env 50848/b201/_pkg_.a HJpH/bR5uMPu5Fr3Cy4PJHJpH .cfg GOINSECURE fips140/hkdf GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12346/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/12346/artifacts --jq .artifacts[].name .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE lite GOMODCACHE 64/pkg/tool/linux_amd64/vet env 50848/b189/_pkg_.a GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD 50848/b006/symab--show-toplevel 64/pkg/tool/linux_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/2/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/2/artifacts --jq .artifacts[].name .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE (http block)
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE fips140/check GOMODCACHE 64/pkg/tool/linux_amd64/vet env 50848/b173/_pkg_.a wDwi/8TvZlM4P0nfuVfRvwDwi .cfg GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/3/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/3/artifacts --jq .artifacts[].name .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env 3953721515 d2UJ/DbmGN00V4XBV3gqgd2UJ ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 om/yosida95/uritemplate/v3@v3.0.2/compile.go 64/pkg/tool/linux_amd64/vet GOINSECURE fips140/hmac GOMODCACHE 64/pkg/tool/linux_amd64/vet env plorer.md ahb4/lZep-2MiwczJtV1iahb4 .cfg GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu-trimpath (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/4/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/4/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env 50848/b212/_pkg_.a GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE nal/fips140tls GOMODCACHE 64/pkg/tool/linux_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run download 4 --dir test-logs/run-4 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE randutil GOMODCACHE 64/pkg/tool/linuTest User env 3907894075/.github/workflows SZyr/UNQkpBpW_IvLZuHOSZyr .cfg GOINSECURE contextprotocol/rev-parse GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu-buildtags (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/5/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/5/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE fips140deps/goderev-parse GOMODCACHE 64/pkg/tool/linux_amd64/vet env 50848/b213/_pkg_.a V4ci/NWzImF-917Hk3aRqV4ci .cfg GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 .cfg 64/pkg/tool/linu-importcfg GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linu/home/REDACTED/work/gh-aw/gh-aw/scripts/lint_error_messages_test.go ortc�� 50848/b174/_pkg_.a stmain.go ache/go/1.25.8/x64/pkg/tool/linux_amd64/link GOINSECURE fips140/ecdh GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/link (http block)
  • https://api.github.com/repos/github/gh-aw/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path -c=4 -nolocalimports -importcfg /tmp/go-build1557589049/b411/importcfg -pack /home/REDACTED/work/gh-aw/gh-aw/pkg/fileutil/fileutil.go /home/REDACTED/work/gh-aw/gh-aw/pkg/fileutil/tar.go env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 100 util GOMODCACHE x_amd64/vet env -json .go x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 6 ntio/asm/internainit GOMODCACHE 64/pkg/tool/linux_amd64/vet env 50848/b143/_pkg_.a .cfg ntdrain.test GOINSECURE ntio/encoding/jsrev-parse GOMODCACHE ntdrain.test (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v0.47.4
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v0.47.4 --jq .object.sha --show-toplevel 64/pkg/tool/linux_amd64/vet /usr/bin/git 50848/b223/_pkg_git GO111MODULE .cfg git rev-�� --show-toplevel ache/go/1.25.8/x64/pkg/tool/linu-tests /usr/bin/gh /tmp/go-build130/bin/sh EgAi/JW5fl0E13Yy-c x_amd64/vet gh (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha edOutput2559815798/001 .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE b/gh-aw/pkg/type/tmp/js-hash-test-3726340413/test-hash.js ache/go/1.25.8/x/home/REDACTED/work/gh-aw/gh-aw/.github/workflows/archie.md 64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.2.3 --jq .object.sha -json GO111MODULE x_amd64/vet GOINSECURE jsonrpc2 GOMODCACHE x_amd64/vet env -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v2.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json 9c99f2130e9da9168 x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet env .a (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet env -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v3.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v3.0.0 --jq .object.sha -json GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet 1455�� .a GO111MODULE x_amd64/vet GOINSECURE GOMOD GOMODCACHE x_amd64/vet (http block)
  • https://api.github.com/repos/nonexistent/action/git/ref/tags/v999.999.999
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha 50848/b122/_pkg_.a .cfg 64/pkg/tool/linux_amd64/vet GOINSECURE /go-yaml GOMODCACHE 5t/UlAnH1m4Yz-ihnUCyQog/5X-t9l0ATest User (http block)
  • https://api.github.com/repos/nonexistent/repo/actions/runs/12345
    • Triggering command: /usr/bin/gh gh run view 12345 --repo nonexistent/repo --json status,conclusion GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env 50848/b238/_pkg_.a 4ACQ/f02Eva1ttQPQuPWq4ACQ 64/pkg/tool/linux_amd64/compile GOINSECURE t/feature/pluralinit GOMODCACHE 64/pkg/tool/linux_amd64/compile (http block)
  • https://api.github.com/repos/owner/repo/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json gset/set.go x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
  • https://api.github.com/repos/owner/repo/contents/file.md
    • Triggering command: /tmp/go-build1557589049/b397/cli.test /tmp/go-build1557589049/b397/cli.test -test.testlogfile=/tmp/go-build1557589049/b397/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE xq3TT-m/ckFlJ1_Q/tmp/go-build1557589049/b277/vet.cfg (http block)
  • https://api.github.com/repos/test-owner/test-repo/actions/secrets
    • Triggering command: /usr/bin/gh gh api /repos/test-owner/test-repo/actions/secrets --jq .secrets[].name -json 8 x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)

If you need me to access, download, or install something from one of these locations, you can either:

…r_search_helpers.cjs

Refactors the duplicated marker-based query construction and exact-marker
filtering logic from close_older_issues.cjs and close_older_discussions.cjs
into a shared close_older_search_helpers.cjs module with three functions:

- buildMarkerSearchQuery: Builds search query and exact marker from
  closeOlderKey / workflowId / callerWorkflowId parameters
- filterByMarker: Filters search results by excluding newly created entities
  and verifying exact marker presence, with entity-specific additional filters
- logFilterSummary: Logs filtering counters in a consistent format

Includes 22 new tests with parity tests ensuring issue and discussion
paths produce equivalent results. All 69 tests pass (47 existing + 22 new).

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/d6fb4b1e-560e-434e-af18-c8e3c048bf30

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Refactor duplicate code in close-older handlers Extract shared close-older marker search/filter logic into helpers module Apr 13, 2026
Copilot AI requested a review from pelikhan April 13, 2026 12:32
@github-actions github-actions bot added the lgtm label Apr 13, 2026
@github-actions
Copy link
Copy Markdown
Contributor

Hey @Copilot 👋 — great work on this refactor! Extracting the shared marker-query construction and exact-marker filtering logic out of close_older_issues.cjs and close_older_discussions.cjs into a dedicated close_older_search_helpers.cjs module is a clean improvement — it eliminates ~80 lines of duplication across both files while keeping the transport-specific and entity-specific logic exactly where it belongs.

The PR is well-described, tightly focused on a single concern, ships 22 new tests (including parity tests verifying both code paths produce equivalent markers), and doesn't introduce any new dependencies. All 69 tests pass. This looks ready for maintainer review! 🚀

Generated by Contribution Check · ● 3.2M ·

@pelikhan pelikhan marked this pull request as ready for review April 13, 2026 13:45
Copilot AI review requested due to automatic review settings April 13, 2026 13:45
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR extracts shared “close older” marker query construction and post-search filtering logic into a reusable helper module, reducing duplication between the issues (REST) and discussions (GraphQL) implementations.

Changes:

  • Added close_older_search_helpers.cjs providing shared query-building, exact-marker filtering, and summary logging helpers.
  • Refactored close_older_issues.cjs and close_older_discussions.cjs to use the shared helpers while keeping transport/entity-specific checks local.
  • Added a dedicated test suite for the helper module, including parity tests for issues vs discussions behavior.
Show a summary per file
File Description
actions/setup/js/close_older_search_helpers.cjs New shared helper functions for marker search query creation, filtering, and logging.
actions/setup/js/close_older_issues.cjs Replaces inline marker/query/filter logic with shared helper calls for the issues path.
actions/setup/js/close_older_discussions.cjs Replaces inline marker/query/filter logic with shared helper calls for the discussions path.
actions/setup/js/close_older_search_helpers.test.cjs Adds unit tests covering helper behavior and issue/discussion parity.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 4/4 changed files
  • Comments generated: 1

Comment on lines +84 to +95
// Run entity-specific filters first (e.g. pull_request, closed, category)
if (additionalFilter && !additionalFilter(item, extraCounters)) {
return false;
}

// Exclude the newly created entity
if (item.number === excludeNumber) {
excludedCount++;
core.info(` Excluding ${entityType} #${item.number} (the newly created ${entityType})`);
return false;
}

Copy link

Copilot AI Apr 13, 2026

Choose a reason for hiding this comment

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

filterByMarker runs additionalFilter before excluding the newly created entity (excludeNumber). This changes behavior from the previous discussion implementation (which excluded excludeNumber first) and can produce misleading counters/logs if the new entity fails an additional filter (e.g., closed/category), because it will not increment excludedCount. To preserve prior semantics and make counters reliable, check item.number === excludeNumber before calling additionalFilter (and consider short-circuiting before any other checks).

Suggested change
// Run entity-specific filters first (e.g. pull_request, closed, category)
if (additionalFilter && !additionalFilter(item, extraCounters)) {
return false;
}
// Exclude the newly created entity
if (item.number === excludeNumber) {
excludedCount++;
core.info(` Excluding ${entityType} #${item.number} (the newly created ${entityType})`);
return false;
}
// Exclude the newly created entity before running any other filters so
// counters/logs consistently attribute this item to the dedicated exclusion.
if (item.number === excludeNumber) {
excludedCount++;
core.info(` Excluding ${entityType} #${item.number} (the newly created ${entityType})`);
return false;
}
// Run entity-specific filters next (e.g. pull_request, closed, category)
if (additionalFilter && !additionalFilter(item, extraCounters)) {
return false;
}

Copilot uses AI. Check for mistakes.
@github-actions github-actions bot mentioned this pull request Apr 13, 2026
@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 70/100

⚠️ Acceptable — with suggestions

Metric Value
New/modified tests analyzed 22
✅ Design tests (behavioral contracts) 22 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 7 (32%)
Duplicate test clusters 0
Test inflation detected ⚠️ YES (2.7:1 — test: 380 lines, production: 141 lines)
🚨 Coding-guideline violations None

Test Classification Details

All 22 tests — click to expand
Test File Classification Notes
should build query with close-older-key when provided close_older_search_helpers.test.cjs ✅ Design Verifies searchQuery and exactMarker outputs
should build query with workflow-id when no close-older-key close_older_search_helpers.test.cjs ✅ Design Verifies fallback to workflow-id path
should use callerWorkflowId for exact marker when provided close_older_search_helpers.test.cjs ✅ Design Verifies exactMarker uses caller ID
should append entityQualifier when provided close_older_search_helpers.test.cjs ✅ Design Verifies qualifier is injected in query
should append entityQualifier with close-older-key close_older_search_helpers.test.cjs ✅ Design Verifies qualifier + key combination
should prefer close-older-key over callerWorkflowId when both are provided close_older_search_helpers.test.cjs ✅ Design Edge case: conflicting params — priority rule
should escape quotes in workflow ID to prevent query injection close_older_search_helpers.test.cjs ✅ Design Edge case: injection prevention security invariant
should escape quotes in close-older-key to prevent query injection close_older_search_helpers.test.cjs ✅ Design Edge case: injection prevention security invariant
should exclude items matching excludeNumber close_older_search_helpers.test.cjs ✅ Design Verifies current-item exclusion contract
should exclude items without exact marker in body close_older_search_helpers.test.cjs ✅ Design Edge case: partial/wrong-marker mismatch
should skip null/undefined items close_older_search_helpers.test.cjs ✅ Design Edge case: null/undefined defensive handling
should apply additionalFilter before standard checks close_older_search_helpers.test.cjs ✅ Design Verifies filter composition and counter tracking
should handle items with missing body gracefully close_older_search_helpers.test.cjs ✅ Design Edge case: missing body field
should return all matching items when no exclusions apply close_older_search_helpers.test.cjs ✅ Design Verifies clean happy path and all counter values
should work with discussion-specific additional filters close_older_search_helpers.test.cjs ✅ Design Verifies discussion-specific filter composition
should log basic summary without extra labels close_older_search_helpers.test.cjs ✅ Design Verifies logged output strings (external I/O mock)
should log extra labels when provided close_older_search_helpers.test.cjs ✅ Design Verifies dynamic label rendering
should log zero for missing extra counter keys close_older_search_helpers.test.cjs ✅ Design Edge case: missing key defaults to 0
should produce equivalent queries for close-older-key (issue vs discussion) close_older_search_helpers.test.cjs ✅ Design Cross-cutting parity invariant
should produce equivalent queries for workflow-id (issue vs discussion) close_older_search_helpers.test.cjs ✅ Design Cross-cutting parity invariant
should produce equivalent exact markers for callerWorkflowId (issue vs discussion) close_older_search_helpers.test.cjs ✅ Design Cross-cutting parity invariant
should apply filterByMarker identically for issues and discussions close_older_search_helpers.test.cjs ✅ Design Cross-cutting parity invariant

Flagged Tests — Suggestions for Improvement

No tests were classified as implementation tests. The following observations are offered as improvement suggestions, not blocking issues.

📌 Test inflation: 380 lines (test) vs. 141 lines (production) — ratio 2.7:1

This triggers the scoring penalty mechanically, but contextually the helper module is intentionally lean (pure functions with no side effects), and thorough parametric testing of such modules is encouraged. The overhead is mostly from the parity describe block (4 tests) which tests an important cross-cutting invariant.

Suggested improvement: Consider whether the 4 parity tests could be condensed using a test.each / parameterized approach, reducing boilerplate without sacrificing coverage.

📌 buildMarkerSearchQuery — limited edge case coverage beyond injection

Tests 1–5 all use well-formed, "typical" inputs (non-empty strings, ASCII-only). The injection tests (7–8) cover quote characters, but these cases are missing:

  • Empty workflowId or empty closeOlderKey
  • owner or repo with slashes, spaces, or special characters
  • callerWorkflowId with unusual formatting

Suggested improvement: Add 1–2 tests covering empty/blank values or structured inputs that could produce malformed queries.

📌 logFilterSummaryglobal.core.info call assertions only

The three logFilterSummary tests assert exclusively on toHaveBeenCalledWith(...) — they verify which strings are logged. Since this function has no return value and global.core is correctly identified as external I/O, this is acceptable. The behavioral contract (what gets logged) is meaningful.

However, none of the three tests verify the ordering or completeness of all log lines (e.g., that "Filtering complete:" is always the first call before per-counter lines). A missing log line that doesn't match a specific assertion would not be caught.

Suggested improvement (optional): Add a toHaveBeenNthCalledWith(1, "Filtering complete:") assertion in the basic summary test to lock in call ordering.


Language Support

Tests analyzed:

  • 🟨 JavaScript (*.test.cjs): 22 tests (vitest)
  • 🐹 Go (*_test.go): 0 files changed

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All 22 tests verify observable behavioral contracts. No coding-guideline violations detected. The mocking of global.core (GitHub Actions runtime) is legitimate external I/O mocking. Score reduced from maximum due to test inflation (2.7:1 ratio) and edge case coverage at 32% — both areas where incremental improvements would raise the score.


📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · ● 535.6K ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 70/100. Test quality is acceptable — 0% of new tests are implementation tests (threshold: 30%). All 22 tests verify behavioral contracts. Minor improvement opportunities noted in the comment above (edge case breadth and test inflation).

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot Review all comments

Reorders the filter checks so the newly-created entity exclusion runs
before entity-specific additional filters (e.g. closed/category). This
preserves the original discussion-handler semantics and ensures counters
reliably attribute the new entity to the dedicated exclusion bucket.

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/0ff7ecc5-4718-44ce-b2dc-2895008ca534

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 13, 2026

@copilot Review all comments

Addressed the reviewer's feedback in f3aae0e: reordered filterByMarker to check excludeNumber before additionalFilter, preserving the original handler semantics and ensuring counters reliably attribute the new entity to the dedicated exclusion bucket. All 69 tests pass.

@pelikhan pelikhan merged commit a5e4ba1 into main Apr 13, 2026
55 of 56 checks passed
@pelikhan pelikhan deleted the copilot/refactor-close-older-handlers branch April 13, 2026 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Duplicate Code: Marker-based close-older search/filter logic duplicated across issue and discussion handlers

3 participants