Skip to content

Add probability-scored duplicate issue detection - #39218

Merged
kubaflo merged 14 commits into
mainfrom
kubaflo-issue-duplicate-detection
Oct 22, 2026
Merged

kubaflo merged 14 commits into
mainfrom
kubaflo-issue-duplicate-detection

Conversation

@kubaflo

@kubaflo kubaflo commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description of Change

Add issue-duplicate-detector, a GitHub Agentic Workflow that checks new/reopened issues and supports manual checks of existing open issues. Known bot-authored issues are skipped automatically; missing identities from deleted accounts are retained as unknown without dropping their evidence. Manual runs default to a staged, no-post preview. The agent is read-only, uses GPT-6.1 Sol and the existing Copilot PAT pool, and runs only from trusted public dotnet/maui default-branch infrastructure. Discovery uses a trusted, rate-aware lexical-search MCP service restricted to public dotnet/maui; full issue evidence still comes from the compiled GitHub MCP's repository-scoped issue_read. The gateway's automatic public-repository scope override remains disabled.

Exact repository scope conservatively labels the evidence private:dotnet/maui, even though MAUI is public. The workflow explicitly authorizes private-to-public-flows: [safeoutputs, duplicate-search] for publication and the fixed public-repository read-only search service only, retaining their exact private:dotnet/maui write-sink accept policies and omitting sink visibility only for those named exemptions. It does not broaden reads to all public repositories, spoof private visibility, or use a blanket allow/wildcard exception. The trusted collector, search service and validator check live repository metadata and reject anything other than public dotnet/maui, with another check before a report is approved for publication.

MAUI historically had a title-only similarity bot, as shown in #22884 and #22849. Its decimal similarity scores are not duplicate probabilities. The inspected current workflow definitions and Policy Service configuration had no dedicated general-purpose duplicate detector, so this adds a focused workflow without broadening agentic-labeler or /issue triage.

  • Compare full reports and comments, distinctive reproduction/diagnostic evidence, platforms, handler generations and version boundaries; distinguish closed historical reports from new regressions.
  • Require an integer duplicate-probability percentage for every proposed match, alongside matching evidence, differences/uncertainty and verified excerpts from both reports. These are explicitly uncalibrated AI estimates, not confirmed duplicate decisions.
  • Publish at most five matches scoring at least 60%; classify 85–100% as likely duplicates and 60–84% as possible duplicates. Below-threshold, missing, fractional and out-of-range scores cannot be published.
  • Match the /review tests visual layout: an issue header, two blue Scope/Issue badges, closed sibling Duplicate Analysis and Follow-up accordions, nested candidate sections, icons, spacing and horizontal rules. Every estimated percentage remains visible above the accordions without expanding anything. Each candidate section contains its state, assessment, evidence, differences/uncertainty and linked excerpts; Follow-up contains maintainer guidance, refresh instructions and the run link. Add only img.shields.io alongside the existing defaults network allowance so the badge URLs survive gh-aw's publication sanitizer; do not disable URL filtering or broaden GitHub tool permissions.
  • Use a separate trusted publisher to enforce the report contract, verify excerpts, and recheck target/candidate snapshots before publication. Pin publication to the built-in GITHUB_TOKEN so report authors match the strict github-actions[bot] provenance check. Construct the visible summary, expandable sections and links, and suppress identical reports with a sanitizer-preserved fingerprint of the trusted rendered summary, assessments, excerpts and static follow-up, excluding the separate evidence-freshness hash and per-run workflow link. Unrelated target comments therefore cannot defeat suppression when that rendered content is unchanged. Retain bot/workflow provenance even when the publisher prepends a caution, and preserve all separate target/candidate freshness checks. Self-matches, repeated candidates, PRs and oversized contexts are rejected.
  • Make staged previews usable: only after successful validation and final freshness checks, the trusted validator writes the same expandable report and visible probabilities to Validated duplicate report preview in the safe-output job summary. Invalid, stale, no-match and identical-suppressed outputs produce no report preview. That validator summary precedes gh-aw's publication sanitization and later cautions/provenance wrappers; it is not on its own evidence that every URL survives publication.
  • Overwrite the trusted run-scoped evidence artifact on full reruns. Keep both downloads on the same run-scoped name so failed-job-only reruns can reuse the completed collector's artifact; freshness checks still reject stale evidence.
  • Never label or close issues, or edit/delete/minimize existing comments. Maintainers retain duplicate-disposition authority. Update issue-management documentation and include the compiled lock file.

Rate-aware discovery and budget enforcement: the trusted IssueDuplicateSearch.cjs service owns a serial queue for the entire agent job. It enforces eight logical queries, 20 results per query, one page, at most three attempts per query and 16 search HTTP attempts including retries. Searches are at least seven seconds apart, respect Retry-After and exhausted x-ratelimit-reset deadlines, and use bounded exponential backoff for secondary limits without reset headers. Individual HTTP requests time out after 15 seconds; waits are capped at 120 seconds individually and 180 seconds cumulatively. Whole workflows use one queue: max concurrency group so different issue checks neither compete with each other nor replace older pending checks; GitHub permits at most 100 pending runs.

The native GitHub gateway separately enforces 30 issue_read calls per MCP session. These bounds do not include public repository checks or trusted collector/publisher API reads. Ten investigated candidates remain an agent instruction, not an investigation counter. The trusted collector/publisher separately enforces five matches, 300 comments per issue and 1 MiB regular JSON files. The agent job also has a 15-minute timeout.

Both pinned GitHub MCP versions force semantic search on GitHub.com and expose no lexical-mode input. All five overlapping publishing trials on kubaflo/maui#946 exhausted the shared installation semantic-search quota and emitted report_incomplete; they produced no comments or validated scores. A prompt-only delay or undeclared search_type argument cannot fix that tool. The replacement makes ordinary REST issue searches using literal keywords/phrases chosen by GPT, not title-only similarity, then investigates full candidate reports through issue_read. It does not spoof the host or rotate credentials to evade quotas.

The service runs outside the agent sandbox using the existing pinned gh-aw HTTP MCP transport and the read-only agent job's built-in GITHUB_TOKEN. A fresh, masked local authentication key is separate from that GitHub credential. Repository qualifiers, arbitrary endpoints and commands are not accepted; public repository metadata and every returned candidate's repository/URL are checked. Errors, incomplete API results and exhausted budgets fail explicitly rather than returning success-shaped empty results. A trusted post-agent step retains run/source/context-hash-bound discovery status and stops the exact service process. The separate publisher requires at least one successfully completed query, no failures and no unfinished queries for both comments and completed no-match outcomes; a successful zero-result search remains valid.

The publisher also rejects failed, missing or unexpected authoritative detector conclusions before accepting any completed outcome, including a no-match noop, not only before constructing a report. A green detector job cannot substitute for an acceptable verdict. Production retains the native intentional warning-mode policy and caution; only success or warning may proceed. No stricter detector-mode change is needed to repair the failed-engine/no-verdict finding.

Stock gh-aw v0.86.2 passed strict compilation but discarded max-calls while rebuilding structured GitHub allowed entries from a names-only configuration. It also granted filesystem-write permission whenever the edit key was present, including edit: false, and did not fully wire scoped exemptions for the generated safeoutputs server. The repository-local CompileIssueDuplicateDetector.sh builds an isolated compiler from the immutable compiler correction, preserving the previous counter, explicit Copilot-grant, disabled-editing and strict-validation repairs. It now derives independent write-sink policies with each exact gateway server ID in custom JSON/TOML and built-in renderers, omitting visibility only for explicitly named exemptions while retaining exact accept tags and visibility enforcement for unlisted servers. The actual compiled Copilot command grants the single-method duplicate-search service, native github(issue_read) and safeoutputs, with no shell or filesystem-write grants. Native semantic github.search_issues is no longer granted.

Go 1.26.5 or later is a build-only prerequisite; the helper does not replace the user's gh aw extension. One command-scoped wrapper applies the same GitHub/Copilot environment-token removals to both the Go build and execution of the resulting compiler. Its build explicitly sets GOWORK=off, ignoring an ambient workspace setting and parent- or source-local go.work files so workspace replacements cannot override the pinned module configuration. This is workspace isolation, not a claim that every aspect of the build is hermetic. Only the official MCP gateway is upgraded, to v0.4.30, containing the safe-output sink-visibility exemption correction. Its immutable image digest is sha256:ab5a436a1490438db473e4e3d4c973cb1d75e3cb233fb08b73d31b42d7d18fba; all other runtime action/container pins and engine versions remain unchanged. The patched compiler identifies itself as v0.86.2+maui-duplicate-detector; an explicit pre-agent step retains the official v0.86.2 base release's compatibility/revocation check. Replace this workaround with a verified fixed official compiler rather than hand-editing the lock or regenerating with stock v0.86.2.

The workflow-local PAT selector follows the existing issue-triage pattern: the pinned gh-aw v0.86.2 compiler leaves job-level import-condition placeholders unresolved, so the secret-bearing job's activation guard is explicit rather than relying on that substitution. No additional external service, model provider, dependency or repository secret is required.

Validation and GitHub publication are not an atomic transaction; source or repository-visibility changes after the final checks remain possible. Unknown author identities never establish trusted workflow-bot provenance.

No-match verdict correction at 6177d904d7:

Review 5478716754 identified that the existing noop early return still preceded the authoritative detector-conclusion assertion. A failed or missing verdict could therefore appear to be a completed no-match investigation even though comment publication was gated. The same assertion now runs before output-type branching in both production and the isolated fork, after the unchanged trusted-context, discovery and exact-one-output checks. It permits only success or intentional warning; failure, missing, skipped and unexpected conclusions cannot bypass it through noop. Successful no-match investigations still post nothing. Probability, evidence, freshness, discovery, destination and provenance checks, production warning cautions and fork-only runtime differences are preserved.

The Git-blob-verified native output collector at runtime 6aab9e5b5c91c615506061f09bedd81a23babe3c includes noop in output_types, so the actual generated detection guard runs for a real no-match item. This is a source-bound causal verification, not a newly dispatched failed-detector/no-match reproduction.

  • bash .github/scripts/CompileIssueDuplicateDetector.sh and the isolated fork's bash .github/scripts/CompileIssueDuplicateForkTrial.sh — passed strict compilation/schema validation with zero warnings, using their existing immutable compilers and unchanged runtime pins. Production lock SHA-256: 681e4d0662c4ea88bc26185e9c69c8693736cf615eb22dbfd89c6e632dd70a01; fork lock SHA-256: c24763c202f4e79ff1e9a75f1dd7e6d7f66d4a13aece57f0d35a5a5f9d61c0d9. Exact before/current file comparison confirms only the prompt body_hash changes; every generated configuration byte, manifest and other metadata field is unchanged. These pinned compiler versions do not implement --dry-run; this is compile-only verification, not workflow execution.
  • node --check .github/scripts/IssueDuplicates.cjs, node --check .github/scripts/IssueDuplicateSearch.cjs, and prettier --check --tab-width 4 --single-quote --print-width 100 .github/scripts/IssueDuplicates.cjs — passed. Fork syntax checks for IssueDuplicates.cjs and IssueDuplicateForkTrial.cjs and formatting of the changed helper passed.
  • prettier --check --tab-width 2 --single-quote=false .github/workflows/issue-duplicate-detector.md .github/docs/issue-duplicate-detector.md — passed; matching fork workflow/documentation checks passed.
  • actionlint -oneline -ignore 'unexpected key "queue" for "concurrency" section' .github/workflows/issue-duplicate-detector.lock.yml, bash -n .github/scripts/CompileIssueDuplicateDetector.sh, and shellcheck .github/scripts/CompileIssueDuplicateDetector.sh — passed. The fork's corresponding daily-repo-status.lock.yml actionlint, shell syntax and shellcheck -x .github/scripts/CompileIssueDuplicateForkTrial.sh .github/scripts/IssueDuplicateBuildEnvironment.sh checks passed.
  • poutine --disable-version-check --format json analyze_local <current-lock-snapshot> and the same command on the exact preceding lock snapshot — completed. Both report the same 16 untrusted_checkout_exec heuristic findings at immutable runtime Bash steps in the issues-triggered production workflow; complete rule/metadata comparison shows no new finding. The trusted default-branch gate and pinned-runtime provenance are unchanged. This is not claimed as a zero-finding security scan.
  • git diff --check — passed in both worktrees. Independent GPT-only safety classification of the narrow source/lock delta passed after parent inspection of the exact credential, executable-provenance, authorization and sandbox boundaries; this is not general security or hosted-runtime proof.

No new pipeline/configuration tests or fixtures, workflow dispatches, reruns, detector model requests or issue comments were added. All previous trial approvals remain spent. The replacement search service and end-to-end hosted no-match/publication behavior remain unverified.

The production correction is published at 6177d904d77480b58589d07074eefcfe4f90484e; the matching isolated fork commit is a6c01ac560064a940488a871df4e083628a7f3ee. Both remote heads and clean worktrees were verified, and fork default main remains unchanged. One actual current-head CI snapshot found maui-pr neutral because no pipeline path filters matched, with no new AzDO PR build. Pester, Docs Spellcheck, Build Insights and the automated review were still pending; no overall CI-green or merge-readiness claim is made. kubaflo/maui#946 remains open/unlocked with zero comments. The standing review watch remains active. This review supplied no new inline comment or unresolved thread, so no disconnected reply or false resolution was posted.

Scoped search-sink rendering correction at 253e8b4417 (retained historical evidence):

Review 4237333931 identified a real gap in the preceding repair: 8b600a3beee3591b7add4c79e595d9481a470cc4 removed visibility only for safeoutputs, even though the workflow named both that server and duplicate-search. The custom search sink still explicitly emitted ${GH_AW_SINK_VISIBILITY}. On public MAUI, the gateway treats that public visibility as empty secrecy acceptance regardless of its configured accept patterns; a refinement search after a scoped GitHub read could therefore be denied. A gateway exemption prevents default injection but does not remove an explicitly emitted visibility field.

The compiler correction generalizes the existing omission to exact named server IDs without mutating a shared policy. The regenerated production and isolated fork locks each remove only the custom search sink's explicit visibility and update the content-derived heredoc delimiters. Both named sinks retain exactly ["private:dotnet/maui"]; repository guards, native read limits, discovery budgets, tool grants, authoritative detector/warning policy, probability requirements, report presentation and all runtime pins remain unchanged.

  • bash .github/scripts/CompileIssueDuplicateDetector.sh — passed using the newly fetched immutable compiler 86885522c56a777b4734e6b5d81b41ce244a513f; strict compilation/schema validation, zero warnings. Its lock exactly matches the earlier reviewed local regeneration, SHA-256 637122b45e4bd64c5a397aaed4dfe352e94c876e6f2f1fa3802c6b5622fe88e8.
  • bash .github/scripts/CompileIssueDuplicateForkTrial.sh — passed in the isolated fork, strict compilation/schema validation with zero warnings and an identical reviewed regeneration, lock SHA-256 2d9e57cf81945e70b9cc41e1ce36dff63af258f99936fb713557c35f95cde187. Its official compiler base remains c35393777e5604a63721d09512263b1383301d4f; only the existing cumulative compiler patch changes, SHA-256 8fbe948b2ad189da0c6d57272ea0d3408296c19336fe0d3bd4067d882df4111c. The fork retains its separate runtime/gateway repairs, compact report and fixed source/destination bindings.
  • go test -p 4 ./pkg/workflow -run 'PrivateToPublicFlows|ValidatePrivateToPublicFlows|GuardPolicies|GuardPolicy|MCPRenderer|MCPConfigRenderers|CodexEngineRenderMCPConfig|CodexEngineHttpMCPServerRendered|CopilotMCP|CustomMCP|MCPGatewayConfig|MCPGatewayVersion' -count=1 — passed in both compiler checkouts using existing native tests only, including non-exempt sink-policy and JSON/TOML rendering cases.
  • make fmt — passed in the production compiler checkout. Its unrelated formatting output was removed rather than committed. The native Go-only gate's golangci-lint run ./pkg/workflow passed with zero issues; golangci-lint run --new-from-rev=c35393777e5604a63721d09512263b1383301d4f ./pkg/workflow and go vet ./pkg/workflow passed in the fork compiler. The aggregate make agent-report-progress could not pass because formatting exposed unrelated JavaScript targets without their dependencies; after removing that output, make agent-report-progress-no-test passed build, Go lint and schema checks but its unrelated whole-repository workflow validation was blocked by uncached npm lookups in offline mode. These are not claimed as fully passing aggregate gates.
  • node --check .github/scripts/IssueDuplicateSearch.cjs, node --check .github/scripts/IssueDuplicates.cjs, bash -n .github/scripts/CompileIssueDuplicateDetector.sh, shellcheck .github/scripts/CompileIssueDuplicateDetector.sh, prettier --check --tab-width 2 --single-quote=false .github/docs/issue-duplicate-detector.md and actionlint -oneline -ignore 'unexpected key "queue" for "concurrency" section' .github/workflows/issue-duplicate-detector.lock.yml — passed. The matching fork commands passed, including shellcheck -x .github/scripts/CompileIssueDuplicateForkTrial.sh .github/scripts/IssueDuplicateBuildEnvironment.sh and actionlint on daily-repo-status.lock.yml.
  • git diff --check and git diff --cached --check — passed before both commits. The actual generated sink-policy objects were read directly; both contain only exact repository acceptance, with no visibility field. No pipeline/configuration tests or fixtures were added.

The production correction is published at 253e8b44172ae643fdba1241c603202cf63ecc9c; the isolated fork correction is ab3486a770a948780feec5cfaa76098805705e0b. One post-push snapshot found maui-pr neutral because no pipeline path filters matched, and the AzDO build query returned no new PR build. Pester, Build Insights and the next automated review were still pending at that snapshot. This is not a hosted read/search/refinement/publication reproduction or a merge-readiness claim. No new dispatch, rerun, detector model request or issue comment was authorized or performed.

Rate-aware discovery repair validation at ca272a653d (retained historical evidence):

  • bash .github/scripts/CompileIssueDuplicateDetector.sh — passed using the existing Go 1.26.5 toolchain, immutable compiler and cache; strict compilation/schema validation, zero warnings. Production lock SHA-256: 6af8e062253e090c3a345eeeee1fc4d3a7d7621b4a4d301ccc8f70b18a41cb97.
  • node --check .github/scripts/IssueDuplicateSearch.cjs and node --check .github/scripts/IssueDuplicates.cjs — passed.
  • prettier --check --tab-width 4 --single-quote --print-width 100 .github/scripts/IssueDuplicateSearch.cjs .github/scripts/IssueDuplicates.cjs and prettier --check --tab-width 2 --single-quote=false .github/workflows/issue-duplicate-detector.md .github/docs/issue-duplicate-detector.md — passed.
  • actionlint -oneline -ignore 'unexpected key "queue" for "concurrency" section' .github/workflows/issue-duplicate-detector.lock.yml, bash -n .github/scripts/CompileIssueDuplicateDetector.sh and shellcheck .github/scripts/CompileIssueDuplicateDetector.sh — passed.
  • Actual local HTTP MCP requests using the pinned runtime returned real public GitHub candidates for ["Shell", "fragment"] and ["TransactionTooLargeException"], retained successful run-bound status and demonstrated request pacing: two searches took 8,123 ms, including 5,850 ms of enforced waiting. This uses stored CLI authentication, not a hosted Actions job token.
  • The actual start/health/search/collect/stop lifecycle passed with the final helper. Unauthenticated access returned HTTP401; a repository qualifier was rejected without another GitHub search; the retained failed status was refused; the exact service PID stopped. No GitHub token was written to the validation receipts.
  • The isolated fork's matching CompileIssueDuplicateForkTrial.sh also passed strict compilation with zero warnings; its final lock SHA-256 is 691c930ec6e02c480689cea5351d5b85077605c99be1cb327ba6884af75d3040. JavaScript syntax, formatting and actionlint passed there too. shellcheck -x .github/scripts/CompileIssueDuplicateForkTrial.sh .github/scripts/IssueDuplicateBuildEnvironment.sh passed after including the existing sourced dependency. The search helper is byte-identical in both worktrees, SHA-256 d9311206af276c5b80bcb5c812ceaf9f6dca5beb987fda9c528382d19af2fe6d.

The generated tool grants, exemption list, read-only token scoping, trusted artifact locations and publication verdict gate were inspected at that revision, but the custom search sink's still-explicit visibility field was missed; the subsequent scoped rendering correction above addresses that gap. Production's native warning-mode flag remains true; the pinned parser is byte-identical to the previously verified runtime source. The failed-verdict repair is a source-bound causal check, not a new hosted failed-engine/publication reproduction.

No new pipeline/configuration tests, hosted dispatches, reruns, model requests or issue comments were added for this repair. Existing trial authorizations remain spent. Hosted job-token access, container/gateway transport, reset handling and end-to-end report publication with the replacement service are not yet verified. The fork retains its existing compact renderer, fixed destination, source whitelist and separate immutable runtime/gateway repairs; this change does not claim those compatibility repairs are now incorporated into production.

Badge-publication correction at b785516299 (retained historical evidence):

  • bash .github/scripts/CompileIssueDuplicateDetector.sh — passed with the existing Go 1.26.5 toolchain and immutable production compiler/cache: strict compilation/schema validation, zero warnings. Native regeneration adds only img.shields.io to the existing domain sets and changes the frontmatter hash; the prompt-body hash, tool budgets, permissions, models, and compiler/runtime pins remain unchanged. Lock SHA-256 at that commit: 3e1d9a13a8b81a6023b69a037cb6d91251b01bd9139ff80f6206f50bb6909beb.
  • npx --no-install --offline prettier --check --tab-width 2 .github/workflows/issue-duplicate-detector.md .github/docs/issue-duplicate-detector.md — passed using the established two-space Markdown/frontmatter style.
  • actionlint -oneline -ignore 'unexpected key "queue" for "concurrency" section' .github/workflows/issue-duplicate-detector.lock.yml — passed.
  • git diff --check and git diff --cached --check — passed.
  • Independently reproduced the reported bug with the real saved 78%/76% Android report and the actual previous compiled publication environment: the pinned sanitizer removed both badge URLs, while probabilities and accordions survived. The earlier Markdown-API-only preview bypassed this publication step and was insufficient proof.
  • Executed the full native sanitizeContent with the regenerated publication policy and real issue-author aliases. Eight downloaded runtime/dependency files were verified against their immutable Git blobs at 6aab9e5b5c91c615506061f09bedd81a23babe3c. Both badge URLs now survive, all four sections remain closed, and both probabilities stay visible above the accordions. A bing.com URL from the real issue body is still redacted; URL filtering remains active.
  • GitHub's Markdown API rendered the native-sanitized report with two canonical badge images, four closed sections, and both visible percentages — passed. No synthetic issue data, new pipeline/configuration fixtures, hosted AI trials, or issue comments were added.

Comment-layout validation at 7564d8742e (retained historical evidence):

  • node --check .github/scripts/IssueDuplicates.cjs — passed.
  • prettier --check --tab-width 4 --single-quote --print-width 100 .github/scripts/IssueDuplicates.cjs — passed.
  • prettier --check --tab-width 2 .github/workflows/issue-duplicate-detector.md .github/docs/issue-duplicate-detector.md — passed; the documentation check passed again after its final wrapping update.
  • bash .github/scripts/CompileIssueDuplicateDetector.sh — passed with Go 1.26.5 and the existing pinned source/cache: strict compilation/schema validation, zero warnings. That generated lock changed only its prompt-body hash; frontmatter, permissions, tool budgets, models and runtime pins were unchanged. Lock SHA-256 at that commit: eb70b8b52f4cc2e00ab7bd97b5ece957e218f63f09d720b68f1eea4a40baaaf9.
  • actionlint -oneline -ignore 'unexpected key "queue" for "concurrency" section' .github/workflows/issue-duplicate-detector.lock.yml — passed.
  • git diff --check and git diff --cached --check — passed.
  • The native renderReport formatter rendered the previously validated Android suggestions (78% and 76%), using real issue metadata and source-bound excerpts. GitHub's Markdown API rendered the result with both visible percentages, two badges, closed nested candidate sections and a closed sibling Follow-up section. This was a layout-only preview before publication sanitization, not a fresh AI investigation, hosted trial or issue publication; the sanitizer gap and its correction are documented above.

Earlier validation through 96dea97065 (retained historical evidence):

  • bash .github/scripts/CompileIssueDuplicateDetector.sh — passed, strict compilation/schema validation with zero warnings; repeated compilation from the immutable correction produced an identical lock (then SHA-256 612fae65d185fd066e5458ac3b38b495b2cd366d7164920ec9aedd9449cdb410).
  • GOWORK=/dev/null bash .github/scripts/CompileIssueDuplicateDetector.sh and env -u GOWORK bash .github/scripts/CompileIssueDuplicateDetector.sh — both passed using Go 1.26.5; the real helper ignored the ambient workspace setting, and both regenerations retained the historical lock SHA-256 above. No synthetic workspace fixture was added.
  • GH_TOKEN=configuration-check GITHUB_TOKEN=configuration-check COPILOT_GITHUB_TOKEN=configuration-check GOWORK=/dev/null bash .github/scripts/CompileIssueDuplicateDetector.sh — passed with non-secret placeholder values after both build and compiler execution were routed through the shared token-removal wrapper; another normal regeneration retained the identical historical lock. These are native build/configuration checks, not fixture coverage.
  • shellcheck .github/scripts/CompileIssueDuplicateDetector.sh and bash -n .github/scripts/CompileIssueDuplicateDetector.sh — passed.
  • actionlint -oneline -ignore 'unexpected key "queue" for "concurrency" section' .github/workflows/issue-duplicate-detector.lock.yml — passed, including native ShellCheck. The narrowly documented ignore is for GitHub's supported concurrency.queue emitted by gh-aw's conclusion job, which actionlint v1.7.12 does not yet recognize.
  • node --check .github/scripts/IssueDuplicates.cjs — passed after the earlier rendered-content fingerprint correction.
  • prettier --check --tab-width 4 --single-quote --print-width 100 .github/scripts/IssueDuplicates.cjs — passed.
  • prettier --check --tab-width 2 .github/workflows/issue-duplicate-detector.md .github/docs/issue-duplicate-detector.md — passed.
  • prettier --check --tab-width 2 .github/docs/issue-duplicate-detector.md — passed again after the earlier review documentation updates.
  • In the compiler-correction checkout, go test -p 4 ./pkg/workflow -run 'ApplyDefaultTools|GitHubAllowedTools|GitHubGuardPoliciesToolCallLimits|ParseGitHubToolAllowedObjectEntries|CopilotEngine.*ToolArguments|SafeOutputsMCP|Derive.*GuardPolicy|PrivateToPublicFlows|MCPGatewayConfig|MCPGatewayVersion' -count=1 and go test -p 4 ./pkg/actionpins -count=1 — passed using existing tests. The corrected compiler also built successfully.
  • npx --yes --package cspell cspell --config ./eng/automation/cspell/cSpell.json docs/IssueManagementPolicies.md --no-progress — passed earlier, zero spelling issues; that policy file is unchanged by the review corrections.
  • git diff --check and git diff --cached --check — passed before the earlier commit; the generated counters, exact repository scope, safeoutputs-only exemption and accept policy, disabled write/shell grants, artifact overwrite/download names, Boolean staging wiring and unrelated unchanged resource pins were inspected.

No pipeline/configuration tests were added. Separate authorized forced-staged fork trials have since exercised runtime compatibility: the latest Android trial validated two suggestions, while the latest iOS trial failed closed because a security diagnostic and comment proposal violated the exact-one-output requirement. The trials also exposed an existing gateway counter-reset race; they did not establish hard-cap exhaustion behavior. Those compatibility repairs remain fork-only and are not incorporated into this PR's production workflow. The comment-layout and badge-policy updates do not resolve these readiness blockers, establish production activation/publication, or authorize further hosted runs. All six approved fork dispatches have been used.

Issues Fixed

N/A — new maintainer automation; no linked product issue.

Introduce a read-only GPT gh-aw detector for new and reopened issues, with staged manual runs and trusted probability/evidence validation. Keep duplicate labeling and closure under maintainer control.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 11:28
@kubaflo
kubaflo temporarily deployed to copilot-pat-pool October 7, 2026 11:28 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 39218

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 39218"

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
1 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@kubaflo
kubaflo temporarily deployed to copilot-pat-pool October 7, 2026 11:28 — with GitHub Actions Inactive
@kubaflo
kubaflo temporarily deployed to copilot-pat-pool October 7, 2026 11:30 — with GitHub Actions Inactive
@kubaflo
kubaflo temporarily deployed to copilot-pat-pool October 7, 2026 11:31 — with GitHub Actions Inactive
@kubaflo
kubaflo temporarily deployed to copilot-pat-pool October 7, 2026 11:33 — with GitHub Actions Inactive
@github-actions github-actions Bot added the area-ai-agents Copilot CLI agents, agent skills, AI-assisted development label Oct 7, 2026
@kubaflo
kubaflo temporarily deployed to copilot-pat-pool October 7, 2026 11:34 — with GitHub Actions Inactive

Copilot AI 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.

Copilot review overview

🟡 Changes recommended

Warning-prefixed reports evade deduplication, and candidate evidence can become stale before publication.

Review effort: Balanced
Findings: 2 Medium severity

Open (2)
What changed in this PR

Adds a trusted, probability-scored workflow for suggesting duplicate issues while leaving disposition to maintainers.

Changes:

  • Adds bounded AI-based duplicate detection and trusted report validation.
  • Adds probability, evidence, freshness, and deduplication safeguards.
  • Documents operation, limitations, and manual preview usage.
File Description
.github/​scripts/​IssueDuplicates.cjs Gathers evidence and validates reports.
.github/​workflows/​issue-duplicate-detector.md Defines the agentic workflow.
.github/​workflows/​issue-duplicate-detector.lock.yml Compiled workflow definition.
.github/​docs/​issue-duplicate-detector.md Documents operation and maintenance.
docs/​IssueManagementPolicies.md Adds the detector to issue policy.

Comment thread .github/scripts/IssueDuplicates.cjs Resolved
Comment thread .github/scripts/IssueDuplicates.cjs Resolved

@PureWeen PureWeen left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Adversarial review

This independent reviewer assessment covers exact head ef063d0d9cd9ce2c8bc9cc0731f938e0337750db, using 3 independent reviewers with adversarial consensus.

One additional actionable finding

⚠️ Logic — The report fingerprint is stripped before publication (.github/scripts/IssueDuplicates.cjs:298,323).

Flagged by: 3/3 reviewers; one corroborated after dispute.

validate() inserts <!-- issue-duplicate-detector-report:<hash> --> into item.body, but the compiled workflow runs this validator before Process Safe Outputs (issue-duplicate-detector.lock.yml:2252–2265). The pinned gh-aw v0.86.2 add_comment.cjs sanitizes that body, and both branches of sanitize_content.cjs remove HTML/XML comments outside code regions. The trusted body header and workflow-call marker are added afterward; the custom report fingerprint is not.

Consequently, even a normal publication with no threat warning lacks the fingerprint required by isWorkflowReport() at line 42. Previous reports are not excluded from contextHash or evidence sources, and the identical-report lookup at lines 299–300 cannot recognize them. Repeated non-staged checks can post the report again instead of returning noop. This is separate from the existing caution-prefix finding: changing startsWith alone would not fix it.

Store the fingerprint in a sanitizer-preserved format, such as a visible metadata line, and update report recognition and exact-hash matching while retaining trusted author/workflow provenance. Add a deterministic round-trip fixture using the final publisher-shaped body with mocked gather/validate: adding a detector report should leave contextHash unchanged, should not make its text eligible as evidence, and an identical proposal should produce noop.

Coverage and prior feedback

The panel inspected all five changed files, trusted-checkout and credential boundaries, compiled permissions/guards, staged publication, score/excerpt validation, and pinned publisher behavior. The title matches the change, but the description's identical-report suppression guarantee is not satisfied until the fingerprint survives publication. No automated contract tests were added; JavaScript syntax and exact-scope whitespace checks passed, but runtime tests and a live AI dispatch were not performed.

The existing Copilot COMMENT and its two unresolved threads were considered. Their caution-prefix and candidate-freshness requests are not duplicated here; this assessment adds only the unconditional fingerprint-loss defect above.

Scope: code review only. CI/check status and merge readiness were not evaluated; use the repository's CI-analysis workflow for that assessment.

Keep report fingerprints visible through gh-aw sanitization, recognize trusted warning-prefixed reports, and recheck every candidate before publication. Document the remaining non-atomic GitHub update window.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 17:08
@kubaflo

kubaflo commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

The report fingerprint is stripped before publication.

Fixed in e167285: the fingerprint is now visible metadata that survives sanitization, with the Actions-bot author, trusted workflow markers, and exact fingerprint still required for recognition. Publisher cautions no longer affect recognition, and all candidates are rechecked at the final validation boundary. No pipeline contract fixtures were added; syntax/configuration checks passed, while a live staged publication remains pending activation on the default branch.

Copilot AI 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.

🟡 Changes recommended

The MCP must be repository-scoped to prevent cross-repository data exposure, and the trusted publisher needs regression tests.

2 open findings
2 resolved since last review

🧠 Review effort: Balanced

Comment thread .github/scripts/IssueDuplicates.cjs Resolved
Enforce dotnet/maui in the GitHub MCP repository guard instead of relying on prompt scope. Regenerate the pinned gh-aw lock and document the enforced read boundary.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 17:24

Copilot AI 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.

🔵 Needs a closer look

Nullable GitHub user fields can crash processing of issues or comments authored by deleted accounts.

0 open findings

2 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Handle null comment authors when reading GitHub comments

.github/​scripts/​IssueDuplicates.cjs:97

GitHub issue comments can have user: null after the author account is deleted. Reading .login unconditionally makes evidence gathering/validation throw for any target or candidate containing such a legacy comment, so the detector cannot report on otherwise valid issues. Preserve a placeholder author instead.

This issue also appears in the following locations of the same file:

  • line 114
  • line 181

🧠 Review effort: Balanced

Preserve missing issue and comment author identities as null without dropping their evidence. Avoid nullable-user dereferences while retaining known-bot filtering and strict workflow report provenance.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 17:45
@kubaflo

kubaflo commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Nullable GitHub user fields can crash processing of issues or comments authored by deleted accounts.

Fixed in 4696d7c. Issue/comment user fields are now null-safe, with missing identities preserved as null rather than dropping the evidence or inventing an author; known-bot filtering remains intact and unknown authors cannot establish trusted workflow-report provenance.

Copilot AI 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.

🔵 Needs a closer look

The unpinned publisher identity can prevent recognition and suppression of the workflow’s own reports.

0 open findings

Previously missed (1)

In code that hasn't changed since last review

Medium severity Pin safe-output publication to the recognized GitHub token

.github/​workflows/​issue-duplicate-detector.md:136

The publisher identity is not pinned, but report recognition is: the compiled safe-output step prefers GH_AW_GITHUB_TOKEN (issue-duplicate-detector.lock.yml:2280), while getReportHash accepts only github-actions[bot]. When that configured publisher token is present, this workflow will not recognize or suppress its own prior reports, and those comments will enter subsequent context hashes/evidence. Pin safe-output publication to secrets.GITHUB_TOKEN, as the existing issue-triage workflow does at .github/workflows/issue-triage.md:164-168, then regenerate the lock file.

🧠 Review effort: Balanced

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 7, 2026 18:04
This was referenced Nov 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-ai-agents Copilot CLI agents, agent skills, AI-assisted development

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants