feat(triage): VGAP-15 enrichment comment duplicate detection
Shows yellow warning banner when existing AI comment detected on issue. Checks via getIssueComments IPC for ENRICHMENT_COMMENT_FOOTER marker. Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
b8b53250f1
commit
856bee4e54
@@ -40,7 +40,7 @@ import {
|
||||
import { GitHubSetupModal } from "./GitHubSetupModal";
|
||||
import { useMutationStore } from "../stores/github/mutation-store";
|
||||
import { useAITriageStore } from "../stores/github/ai-triage-store";
|
||||
import { formatEnrichmentComment } from "../../shared/constants/ai-triage";
|
||||
import { formatEnrichmentComment, ENRICHMENT_COMMENT_FOOTER } from "../../shared/constants/ai-triage";
|
||||
import type { GitHubIssue } from "../../shared/types";
|
||||
import type { GitHubIssuesProps } from "./github-issues/types";
|
||||
import type { WorkflowState, Resolution } from "../../shared/types/enrichment";
|
||||
@@ -183,6 +183,29 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP
|
||||
const aiTriage = useAITriage(selectedProject?.id ?? '');
|
||||
const lastBatchSnapshot = useAITriageStore((s) => s.lastBatchSnapshot);
|
||||
|
||||
// Check for existing AI comment when enrichment result is available
|
||||
const [hasExistingAIComment, setHasExistingAIComment] = useState(false);
|
||||
useEffect(() => {
|
||||
if (!aiTriage.enrichmentResult || !selectedIssue || !selectedProject?.id) {
|
||||
setHasExistingAIComment(false);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
window.electronAPI.github.getIssueComments(selectedProject.id, selectedIssue.number)
|
||||
.then((result) => {
|
||||
if (cancelled) return;
|
||||
const comments = result?.data ?? [];
|
||||
const hasAI = comments.some((c: { body?: string }) =>
|
||||
c.body?.includes(ENRICHMENT_COMMENT_FOOTER),
|
||||
);
|
||||
setHasExistingAIComment(hasAI);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setHasExistingAIComment(false);
|
||||
});
|
||||
return () => { cancelled = true; };
|
||||
}, [aiTriage.enrichmentResult, selectedIssue, selectedProject?.id]);
|
||||
|
||||
// Triage mode (3-panel layout)
|
||||
const { isEnabled: triageModeEnabled, isAvailable: triageModeAvailable, toggle: toggleTriageMode } = useTriageMode();
|
||||
|
||||
@@ -547,6 +570,7 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP
|
||||
aiTriage.clearEnrichmentResult();
|
||||
}}
|
||||
onCancel={aiTriage.clearEnrichmentResult}
|
||||
hasExistingAIComment={hasExistingAIComment}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
+7
-1
@@ -10,9 +10,10 @@ interface EnrichmentCommentPreviewProps {
|
||||
content: string;
|
||||
onPost: (content: string) => void;
|
||||
onCancel: () => void;
|
||||
hasExistingAIComment?: boolean;
|
||||
}
|
||||
|
||||
export function EnrichmentCommentPreview({ content: initialContent, onPost, onCancel }: EnrichmentCommentPreviewProps) {
|
||||
export function EnrichmentCommentPreview({ content: initialContent, onPost, onCancel, hasExistingAIComment }: EnrichmentCommentPreviewProps) {
|
||||
const { t } = useTranslation(['common']);
|
||||
const [content, setContent] = useState(initialContent);
|
||||
|
||||
@@ -23,6 +24,11 @@ export function EnrichmentCommentPreview({ content: initialContent, onPost, onCa
|
||||
|
||||
return (
|
||||
<section className="space-y-3" aria-label={t('common:enrichmentComment.title')}>
|
||||
{hasExistingAIComment && (
|
||||
<div role="alert" className="rounded-md border border-yellow-500/50 bg-yellow-500/10 px-3 py-2 text-xs text-yellow-600 dark:text-yellow-400">
|
||||
{t('common:enrichmentComment.duplicateWarning')}
|
||||
</div>
|
||||
)}
|
||||
<textarea
|
||||
value={content}
|
||||
onChange={(e) => setContent(e.target.value)}
|
||||
|
||||
+16
@@ -63,4 +63,20 @@ describe('EnrichmentCommentPreview', () => {
|
||||
const postedContent = defaultProps.onPost.mock.calls[0][0];
|
||||
expect(postedContent).toContain('Auto-Claude');
|
||||
});
|
||||
|
||||
it('shows duplicate warning when hasExistingAIComment is true', () => {
|
||||
render(<EnrichmentCommentPreview {...defaultProps} hasExistingAIComment={true} />);
|
||||
expect(screen.getByRole('alert')).toBeDefined();
|
||||
expect(screen.getByText('common:enrichmentComment.duplicateWarning')).toBeDefined();
|
||||
});
|
||||
|
||||
it('does not show duplicate warning when hasExistingAIComment is false', () => {
|
||||
render(<EnrichmentCommentPreview {...defaultProps} hasExistingAIComment={false} />);
|
||||
expect(screen.queryByRole('alert')).toBeNull();
|
||||
});
|
||||
|
||||
it('does not show duplicate warning when hasExistingAIComment is not provided', () => {
|
||||
render(<EnrichmentCommentPreview {...defaultProps} />);
|
||||
expect(screen.queryByRole('alert')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -626,7 +626,8 @@
|
||||
"content": "Comment content",
|
||||
"characters": "characters",
|
||||
"post": "Post Comment",
|
||||
"cancel": "Cancel"
|
||||
"cancel": "Cancel",
|
||||
"duplicateWarning": "An AI enrichment comment already exists on this issue. Posting will create a duplicate."
|
||||
},
|
||||
"progressiveTrust": {
|
||||
"title": "Progressive Trust Settings",
|
||||
|
||||
@@ -626,7 +626,8 @@
|
||||
"content": "Contenu du commentaire",
|
||||
"characters": "caractères",
|
||||
"post": "Publier le commentaire",
|
||||
"cancel": "Annuler"
|
||||
"cancel": "Annuler",
|
||||
"duplicateWarning": "Un commentaire d'enrichissement IA existe déjà sur cette issue. La publication créera un doublon."
|
||||
},
|
||||
"progressiveTrust": {
|
||||
"title": "Paramètres de confiance progressive",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
**Branch:** `terminal/enhancement-issues-tab`
|
||||
**Created:** 2026-02-13
|
||||
**Total Gaps:** 46 confirmed (from 9-agent triple-verified audit)
|
||||
**Status:** 14 / 17 complete
|
||||
**Status:** 15 / 17 complete
|
||||
|
||||
---
|
||||
|
||||
@@ -229,18 +229,17 @@ Each gap has: ID, description, status, files to modify, verification source, tes
|
||||
- **Commit:** VGAP-14
|
||||
|
||||
### VGAP-15: No enrichment comment duplicate detection (Phase 3 GAP-5)
|
||||
- **Status:** `PENDING`
|
||||
- **Status:** `DONE`
|
||||
- **Priority:** NICE-TO-HAVE
|
||||
- **Scope:** Medium
|
||||
- **Verified by:** Phase3 agent + Verifier-1 (CONFIRMED)
|
||||
- **Doc ref:** Phase 3 PRD > US-5 > AC5.11; Phase 3 audit > GAP-5
|
||||
- **Files to modify:** `renderer/components/github-issues/components/EnrichmentCommentPreview.tsx`, `renderer/components/github-issues/hooks/useAITriage.ts`
|
||||
- **Problem:** No check for existing AI comments (containing `ENRICHMENT_COMMENT_FOOTER`) before showing preview. Can double-post enrichment comments.
|
||||
- **Fix:** Before showing preview, check issue comments for `ENRICHMENT_COMMENT_FOOTER` string. Show warning banner if existing comment found.
|
||||
- **Tests:** Test warning displays when existing AI comment detected
|
||||
- **Test status:** —
|
||||
- **Files modified:** `renderer/components/github-issues/components/EnrichmentCommentPreview.tsx`, `renderer/components/GitHubIssues.tsx`, `shared/i18n/locales/en/common.json`, `shared/i18n/locales/fr/common.json`, `__tests__/EnrichmentCommentPreview.test.tsx`
|
||||
- **Fix:** Added `hasExistingAIComment` prop to EnrichmentCommentPreview that shows a yellow warning banner with `role="alert"`. GitHubIssues.tsx fetches issue comments via `getIssueComments` IPC when enrichment result is available, checks for `ENRICHMENT_COMMENT_FOOTER`, and passes result as prop. Added `duplicateWarning` i18n key to EN + FR.
|
||||
- **Tests:** 3 new tests: warning shown when true, hidden when false, hidden when undefined. All pass.
|
||||
- **Test status:** `PASS`
|
||||
- **Depends on:** None
|
||||
- **Commit:** —
|
||||
- **Commit:** VGAP-15
|
||||
|
||||
### VGAP-16: No cancel mechanism for batch triage subprocess (Phase 3 GAP-7)
|
||||
- **Status:** `PENDING`
|
||||
@@ -280,7 +279,7 @@ Each gap has: ID, description, status, files to modify, verification source, tes
|
||||
| 2 | i18n Hardcoded Strings | 5 | 5 | 0 |
|
||||
| 3 | Accessibility Keyboard | 2 | 2 | 0 |
|
||||
| 4 | IPC Consistency | 3 | 3 | 0 |
|
||||
| 5 | Phase 3 Audit Gaps | 5 | 2 | 3 |
|
||||
| **Total** | | **17** | **14** | **3** |
|
||||
| 5 | Phase 3 Audit Gaps | 5 | 3 | 2 |
|
||||
| **Total** | | **17** | **15** | **2** |
|
||||
|
||||
Note: VGAP-03 through VGAP-07 contain 28+ individual hardcoded strings grouped by component file. The 17 gap count represents work units (one per component/file), not individual string count.
|
||||
|
||||
Reference in New Issue
Block a user