feat(#848): add file-issue CLI command with dedup guard - #869
feat(#848): add file-issue CLI command with dedup guard#869fullsend-ai-coder[bot] wants to merge 1 commit into
Conversation
Add a `fullsend file-issue` command that wraps issue creation with a built-in dedup guard to prevent concurrent retro agents from filing duplicate issues. When 23 workspace-update PRs closed simultaneously on 2026-07-24, 5 retros filed nearly identical issues within a 2-minute window due to a TOCTOU race in the existing dedup check. The command searches for recent issues by the same author in the target repo using the GitHub Search API. Title similarity is measured via Jaccard word-overlap coefficient (threshold 0.6) after normalizing to lowercase and filtering stop words. When a duplicate is found, the command skips creation and adds a comment on the existing issue with the additional evidence. Changes: - Add SearchIssues to forge.Client interface with IssueSearchOptions (owner, repo, creator, since, state) - Implement SearchIssues for GitHub (Search API), GitLab (stub returning ErrNotSupported), and FakeClient - Add file-issue CLI command with --creator, --dedup-window, --dry-run flags and JSON output - Search failures are non-fatal (fall through to creation) - Comprehensive tests for similarity, normalization, dedup integration, and GitHub API interaction Note: pre-commit could not run in sandbox (network restricted); the post-script runs it authoritatively. Closes #848
|
🤖 Finished Review · ✅ Success · Started 9:45 AM UTC · Completed 10:02 AM UTC |
ReviewFindingsMedium
Low
Previous runReviewFindingsMedium
Low
|
|
/fs-review |
|
🤖 Review · ❌ Terminated · Started 10:31 AM UTC · Ended 10:51 AM UTC |
|
/fs-fix |
There was a problem hiding this comment.
See the review comment for full details.
Note: The following inline comments could not be posted on the diff (GitHub returned 422) and are included here instead:
internal/forge/github/github.go:2577: [medium] api-contract
IssueSearchOptions.State is documented as '"open", "closed", or "" for any state' but the SearchIssues implementation defaults empty State to "open". A caller passing State="" expecting all issues would silently receive only open issues. Current sole caller passes State: "open" explicitly, so no active code path is affected.
Suggested fix: Fix the doc comment to say empty defaults to "open", or change the implementation to omit the state qualifier when empty.
internal/forge/gitlab/issue.go(file-level): Line 361 · [low] test-coverage-gap
GitLab SearchIssues stub returns forge.ErrNotSupported but has no corresponding test in the existing TestErrNotSupported_* test groups.
internal/forge/github/github.go:2569: [low] injection-vuln
SearchIssues interpolates opts.Owner, opts.Repo, opts.Creator, and opts.State into the GitHub Search query string. Spaces in these values would be interpreted as additional qualifiers. Current call path uses CLI-flag-sourced values (low exploitability). Consider validating inputs for defense in depth.
internal/cli/fileissue.go:200: [low] content-injection
The duplicate comment body interpolates the title parameter into Markdown bold. If title contained Markdown syntax, it would render under the bot's identity. Risk mitigated by CLI flag origin.
internal/cli/fileissue.go:187: [low] fail-open
Dedup guard is fail-open by design: SearchIssues failure falls through to issue creation. Under sustained API errors, concurrent agents would bypass dedup. Trade-off is intentional and tested.
internal/cli/fileissue.go:200: [low] naming-coherence
Duplicate comment uses agent-specific language ('another agent independently proposed') in a generically named command.
internal/cli/fileissue.go:158: [low] documentation-style
Function comment for fileIssueWithDedup includes confusing meta-commentary ('exported-name-style (lowercase, unexported)'). Simplify to describe behavior only.
internal/cli/fileissue.go:64: [low] documentation-style
Const declarations use block doc-comments while similar constants in the codebase use inline comments.
internal/cli/fileissue.go:56: [low] type-documentation
FileIssueResult type comment is terse compared to similar result types in the package.
|
🤖 Finished Review · ✅ Success · Started 10:31 AM UTC · Completed 10:51 AM UTC |
|
This pull request has been automatically marked as stale because it has not had any activity in the last month. It will be closed in 2 weeks if no further activity occurs. Remove the |
Summary
Adds a
fullsend file-issueCLI command that wraps issue creation with a built-in dedup guard, preventing concurrent retro agent runs from filing duplicate issues. When 23 workspace-update PRs were closed simultaneously on 2026-07-24, 5 retro agents independently filed nearly identical issues within a 2-minute window. This command solves that race by searching for recent issues with similar titles before creating.Related Issue
Closes #848
Changes
SearchIssuesto theforge.Clientinterface withIssueSearchOptions(owner, repo, creator, since, state)SearchIssuesfor GitHub (via the Search API/search/issues), GitLab (stub returningErrNotSupported), andFakeClientfullsend file-issueCLI command with--creator,--dedup-window,--dry-runflags{created, url, number, duplicate_of}Testing
normalizeWordstests (stop word removal, punctuation, number preservation)SearchIssuestests: result parsing, PR exclusion, default statego vetpassesgo build ./...passesmake lint— pre-commit could not run in sandbox (network restricted); the post-script runs it authoritativelyChecklist
!for breaking changes)Closes #848
Post-script verification
agent/848-retro-dedup-guard)c887fc47c59d8b87b5282ca44044d2933d30df41..HEAD)