feat(cli): race-safe file-issue dedup guard (rebase of #871 onto upstream main) - #875
Draft
guyoron1 wants to merge 2 commits into
Draft
feat(cli): race-safe file-issue dedup guard (rebase of #871 onto upstream main)#875guyoron1 wants to merge 2 commits into
guyoron1 wants to merge 2 commits into
Conversation
Add a `fullsend file-issue` CLI 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 near-identical issues within a 2-minute window.
The command solves the TOCTOU race by searching for recent issues
with similar titles before creating new ones:
- Add `SearchIssues` to `forge.Client` interface with
`IssueSearchOptions` (owner, repo, creator, since, state)
- Implement for GitHub (Search API), GitLab (stub), FakeClient
- Title similarity uses Jaccard word-overlap coefficient with
stop-word filtering (threshold: 0.6)
- JSON output: `{created, url, number, duplicate_of}`
- Search failures are non-fatal (fall through to creation)
- Flags: --creator, --dedup-window, --dry-run
Note: pre-commit could not run in sandbox (network restricted);
the post-script runs it authoritatively on the runner.
Closes #849
- Fix API contract: update IssueSearchOptions.State doc to reflect that empty defaults to "open" (not "any state") - Fix edge case: remove "add" from stop-word list so action verbs contribute to similarity scoring; opposite-action titles (e.g., "Add X" vs "Remove X") are no longer falsely matched as duplicates - Change >= to > for threshold comparison to avoid boundary false positives at exactly 0.6 - Add input validation: reject Creator values containing spaces, colons, or quotes that could inject search qualifiers - Add dedup_skipped field to FileIssueResult JSON output so callers can detect when the dedup guard was not enforced - Add explanatory comment for the 0.6 similarity threshold - Fix self-contradictory comment on fileIssueWithDedup - Add test for opposite-action edge case and dedup_skipped assertions Addresses review feedback on #871
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
A
fullsend file-issueCLI command that wraps issue creation with a deterministic dedup guard: before creating, it searches recent issues (author-scoped, title-similarity, configurable--dedup-window, default 30m) and skips filing if a sufficiently similar issue already exists, returning the existing issue's URL. Intended caller: the retro post-script — but any agent post-script or human can use it.Files:
internal/cli/fileissue.go(+300) with tests (+350),SearchIssuessupport on the forge interface (internal/forge/forge.go,fake.go,github/github.go,gitlab/issue.go), docs.Why — the evidence chain (all from real runs)
duplicate) but kept the other (Add post-script dedup guard for concurrent retro proposals #800 / Add post-script dedup guard for concurrent retro proposals #848). Model-judged dedup is inconsistent even without the race.Relation to upstream work
2e78d663has no race-safe issue dedup:internal/cli/issues.gois get/post-comment only; the only guard is prompt text.Verification
Cherry-picked from PR #871 (
65e1af5c+ review-feedback9f3ad482) onto2e78d663; two conflicts resolved (import union inforge.go, doc-tree union incli-internals.md).go build ./...clean;go test ./internal/cli/ ./internal/forge/...→ 3002 passed, 0 failed.