diff --git a/apps/frontend/src/renderer/components/GitHubIssues.tsx b/apps/frontend/src/renderer/components/GitHubIssues.tsx index a328ccf2..6b4b24a4 100644 --- a/apps/frontend/src/renderer/components/GitHubIssues.tsx +++ b/apps/frontend/src/renderer/components/GitHubIssues.tsx @@ -31,6 +31,16 @@ import { } from "./github-issues/components"; import { GitHubSetupModal } from "./GitHubSetupModal"; import { ResizablePanels } from "./ui/resizable-panels"; +import { + AlertDialog, + AlertDialogContent, + AlertDialogHeader, + AlertDialogFooter, + AlertDialogTitle, + AlertDialogDescription, + AlertDialogAction, + AlertDialogCancel, +} from "./ui/alert-dialog"; import { useMutationStore } from "../stores/github/mutation-store"; import type { GitHubIssue, InvestigationState, InvestigationDismissReason, SuggestedLabel } from "../../shared/types"; import type { GitHubIssuesProps } from "./github-issues/types"; @@ -108,6 +118,10 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP const [investigationStateFilter, setInvestigationStateFilter] = useState([]); const [showDismissed, setShowDismissed] = useState(false); + // Label consent dialog state + const [showLabelConsent, setShowLabelConsent] = useState(false); + const pendingInvestigationRef = useRef<{ type: 'single'; issue: GitHubIssue } | { type: 'bulk' } | null>(null); + // Apply investigation state filter to issues const investigationFilteredIssues = useMemo(() => { if (investigationStateFilter.length === 0 && showDismissed) return filteredIssues; @@ -146,7 +160,7 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP // Build investigation states map for IssueList const investigationStatesMap = useMemo(() => { - const map: Record = {}; + const map: Record = {}; if (!selectedProject?.id) return map; for (const issue of investigationFilteredIssues) { const state = investigationStore.getDerivedState(selectedProject.id, issue.number); @@ -156,6 +170,7 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP state, progress: entry?.progress?.progress, linkedTaskId: entry?.specId ?? undefined, + isStale: entry?.isStale, }; } return map; @@ -270,6 +285,13 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP loadPersistedInvestigations(selectedProject.id); }, [selectedProject?.id]); + // Mark stale investigations: cross-reference investigations with fetched issues + useEffect(() => { + if (!selectedProject?.id || storeIssues.length === 0) return; + const activeIssueNumbers = new Set(storeIssues.map((issue) => issue.number)); + investigationStore.markStaleInvestigations(selectedProject.id, activeIssueNumbers); + }, [storeIssues, selectedProject?.id, investigationStore]); + // Clear selection when filters change // biome-ignore lint/correctness/useExhaustiveDependencies: reset on filter/search change useEffect(() => { @@ -298,10 +320,18 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP // Sync investigation state with linked task status changes // When a task linked to an issue changes status, update the investigation store + // Also detect deleted tasks and revert investigations to findings_ready useEffect(() => { if (!selectedProject?.id) return; const projectId = selectedProject.id; + // Build a set of specIds from current tasks for fast lookup + const taskSpecIds = new Set(); + for (const task of tasks) { + const specId = task.specId || task.id; + if (specId) taskSpecIds.add(specId); + } + for (const task of tasks) { const issueNumber = task.metadata?.githubIssueNumber; if (!issueNumber || !task.status) continue; @@ -312,6 +342,15 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP investigationStore.syncTaskState(projectId, issueNumber, task.status); } + + // Detect deleted tasks: if an investigation has a specId but no matching task exists + const { investigations } = useInvestigationStore.getState(); + for (const inv of Object.values(investigations)) { + if (inv.projectId !== projectId || !inv.specId) continue; + if (!taskSpecIds.has(inv.specId)) { + investigationStore.clearLinkedTask(projectId, inv.issueNumber); + } + } }, [tasks, selectedProject?.id, investigationStore]); // Auto-close GitHub issues when linked task reaches "done" and autoCloseIssues is enabled @@ -379,20 +418,64 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP } }, [handleRefresh, autoFixConfig?.enabled, checkForNewIssues]); + // Helper: check if label consent is needed before investigating + const needsLabelConsent = useCallback(() => { + if (!selectedProject?.id) return false; + const settings = investigationStore.getSettings(selectedProject.id); + return !settings?.labelConsentGiven; + }, [selectedProject?.id, investigationStore]); + + // Helper: grant label consent and persist + const grantLabelConsent = useCallback(() => { + if (!selectedProject?.id) return; + const current = investigationStore.getSettings(selectedProject.id); + const updated = { ...(current ?? { autoCreateTasks: false, autoStartTasks: false, pipelineMode: 'full' as const, autoPostToGitHub: false, autoCloseIssues: false, maxParallelInvestigations: 3, labelIncludeFilter: [] as string[], labelExcludeFilter: [] as string[] }), labelConsentGiven: true }; + investigationStore.setSettings(selectedProject.id, updated); + if (window.electronAPI?.github?.saveInvestigationSettings) { + window.electronAPI.github.saveInvestigationSettings(selectedProject.id, updated).catch(() => {}); + } + }, [selectedProject?.id, investigationStore]); + // Investigation callbacks for selected issue const handleInvestigate = useCallback((issue: GitHubIssue) => { - if (selectedProject?.id) { - startIssueInvestigation(selectedProject.id, issue.number); + if (!selectedProject?.id) return; + if (needsLabelConsent()) { + pendingInvestigationRef.current = { type: 'single', issue }; + setShowLabelConsent(true); + return; } - }, [selectedProject?.id]); + startIssueInvestigation(selectedProject.id, issue.number); + }, [selectedProject?.id, needsLabelConsent]); // Bulk investigate: queue all selected issues for investigation const handleBulkInvestigate = useCallback(() => { if (!selectedProject?.id) return; + if (needsLabelConsent()) { + pendingInvestigationRef.current = { type: 'bulk' }; + setShowLabelConsent(true); + return; + } for (const issueNumber of selectedIssueNumbers) { startIssueInvestigation(selectedProject.id, issueNumber); } - }, [selectedProject?.id, selectedIssueNumbers]); + }, [selectedProject?.id, selectedIssueNumbers, needsLabelConsent]); + + // Handle consent dialog confirm + const handleConsentConfirm = useCallback(() => { + grantLabelConsent(); + setShowLabelConsent(false); + if (!selectedProject?.id) return; + const pending = pendingInvestigationRef.current; + pendingInvestigationRef.current = null; + if (!pending) return; + if (pending.type === 'single') { + startIssueInvestigation(selectedProject.id, pending.issue.number); + } else { + for (const issueNumber of selectedIssueNumbers) { + startIssueInvestigation(selectedProject.id, issueNumber); + } + } + }, [grantLabelConsent, selectedProject?.id, selectedIssueNumbers]); const handleCancelInvestigation = useCallback(() => { if (selectedProject?.id && selectedIssue) { @@ -556,6 +639,7 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP onDismissIssue={handleDismissIssue} onPostToGitHub={handlePostToGitHubWrapped} isPostingToGitHub={isPostingToGitHub} + investigationActivityLog={selectedIssueEntry?.activityLog} /> ) : ( @@ -592,6 +676,28 @@ export function GitHubIssues({ onOpenSettings, onNavigateToTask }: GitHubIssuesP onSkip={() => setShowGitHubSetup(false)} /> )} + + {/* Label Creation Consent Dialog */} + + + + + {t('investigation.labelConsent.title', 'Label Creation Notice')} + + + {t('investigation.labelConsent.body', 'The first investigation will create up to 5 auto-claude:* labels on your GitHub repository to categorize investigation results. These labels are used for filtering and organization.')} + + + + { pendingInvestigationRef.current = null; }}> + {t('buttons.cancel')} + + + {t('investigation.labelConsent.confirm', 'Continue')} + + + + ); } diff --git a/apps/frontend/src/renderer/components/github-issues/components/InvestigationPanel.tsx b/apps/frontend/src/renderer/components/github-issues/components/InvestigationPanel.tsx index e1b588c9..ff98225b 100644 --- a/apps/frontend/src/renderer/components/github-issues/components/InvestigationPanel.tsx +++ b/apps/frontend/src/renderer/components/github-issues/components/InvestigationPanel.tsx @@ -11,7 +11,9 @@ import { Tag, Check, X, - FileText + FileText, + Clock, + CheckCircle2 } from 'lucide-react'; import { Button } from '../../ui/button'; import { Badge } from '../../ui/badge'; @@ -33,6 +35,12 @@ interface InvestigationPanelProps { onAcceptLabel?: (label: SuggestedLabel) => void; onRejectLabel?: (label: SuggestedLabel) => void; isPostingToGitHub?: boolean; + /** Activity log entries for the investigation lifecycle */ + activityLog?: Array<{ event: string; timestamp: string }>; + /** Callback to close the issue on GitHub (used for resolved suggestion) */ + onCloseIssue?: () => void; + /** Whether the close-issue action is in progress */ + isClosingIssue?: boolean; } const SEVERITY_COLORS: Record = { @@ -144,11 +152,38 @@ export function InvestigationPanel({ onAcceptLabel, onRejectLabel, isPostingToGitHub, + activityLog, + onCloseIssue, + isClosingIssue, }: InvestigationPanelProps) { const { t } = useTranslation('common'); + const [activityOpen, setActivityOpen] = useState(false); return (
+ {/* Resolved suggestion banner */} + {report.likelyResolved && onCloseIssue && state !== 'done' && ( +
+ +

+ {t('investigation.panel.resolvedSuggestion', 'This issue appears to be already resolved. Close it on GitHub?')} +

+ +
+ )} + {/* Header: severity + timestamp */}
@@ -273,6 +308,32 @@ export function InvestigationPanel({
)} + + {/* Activity Log */} + {activityLog && activityLog.length > 0 && ( +
+ + {activityOpen && ( +
    + {activityLog.map((entry, i) => ( +
  • + + {entry.event} + {new Date(entry.timestamp).toLocaleString()} +
  • + ))} +
+ )} +
+ )}
); } diff --git a/apps/frontend/src/renderer/components/github-issues/components/IssueDetail.tsx b/apps/frontend/src/renderer/components/github-issues/components/IssueDetail.tsx index 4218e31d..2e9dcaff 100644 --- a/apps/frontend/src/renderer/components/github-issues/components/IssueDetail.tsx +++ b/apps/frontend/src/renderer/components/github-issues/components/IssueDetail.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; -import { ExternalLink, User, Clock, MessageCircle, CheckCircle2, Eye, X, RotateCcw, XCircle } from 'lucide-react'; +import { ExternalLink, User, Clock, MessageCircle, CheckCircle2, Eye, X, RotateCcw, XCircle, AlertTriangle } from 'lucide-react'; import { Badge } from '../../ui/badge'; import { Button } from '../../ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '../../ui/card'; @@ -71,6 +71,7 @@ export function IssueDetail({ onAcceptLabel, onRejectLabel, isPostingToGitHub, + investigationActivityLog, }: IssueDetailProps) { const { t } = useTranslation('common'); const [isClosing, setIsClosing] = useState(false); @@ -159,6 +160,16 @@ export function IssueDetail({ )} + {/* Warning banner for closed issues with active/pending investigation */} + {issue.state === 'closed' && (derivedState === 'investigating' || derivedState === 'new' || derivedState === 'findings_ready') && ( +
+ +

+ {t('investigation.panel.closedIssueWarning', 'This issue is closed. Investigation results may not be actionable.')} +

+
+ )} + {/* Meta */}
@@ -333,6 +344,9 @@ export function IssueDetail({ onAcceptLabel={onAcceptLabel} onRejectLabel={onRejectLabel} isPostingToGitHub={isPostingToGitHub} + activityLog={investigationActivityLog} + onCloseIssue={issue.state === 'open' && onClose ? handleClose : undefined} + isClosingIssue={isClosing} /> diff --git a/apps/frontend/src/renderer/components/github-issues/types/index.ts b/apps/frontend/src/renderer/components/github-issues/types/index.ts index e50d9e1b..2504e944 100644 --- a/apps/frontend/src/renderer/components/github-issues/types/index.ts +++ b/apps/frontend/src/renderer/components/github-issues/types/index.ts @@ -74,6 +74,8 @@ export interface IssueListItemProps { linkedTaskId?: string; /** Handler to navigate to the linked task */ onViewTask?: (taskId: string) => void; + /** Whether the issue is stale (no longer exists in GitHub) */ + isStale?: boolean; } export interface IssueDetailProps { @@ -151,6 +153,8 @@ export interface IssueDetailProps { onRejectLabel?: (label: SuggestedLabel) => void; /** Whether posting to GitHub is in progress */ isPostingToGitHub?: boolean; + /** Activity log entries for the investigation lifecycle */ + investigationActivityLog?: Array<{ event: string; timestamp: string }>; } /** @deprecated Removed in F9. Use InvestigateButton + InvestigationPanel instead. */ @@ -212,6 +216,8 @@ export interface IssueListHeaderProps { onToggleShowDismissed?: () => void; /** Count of active investigations */ activeInvestigationCount?: number; + /** Cancel all active investigations for this project */ + onCancelAllInvestigations?: () => void; } export interface IssueListProps { @@ -230,7 +236,7 @@ export interface IssueListProps { onToggleSelect?: (issueNumber: number) => void; compact?: boolean; /** Investigation states keyed by issue number */ - investigationStates?: Record; + investigationStates?: Record; /** Handler to navigate to a linked task */ onViewTask?: (taskId: string) => void; } diff --git a/apps/frontend/src/renderer/lib/browser-mock.ts b/apps/frontend/src/renderer/lib/browser-mock.ts index c4294b9e..94c828a6 100644 --- a/apps/frontend/src/renderer/lib/browser-mock.ts +++ b/apps/frontend/src/renderer/lib/browser-mock.ts @@ -193,6 +193,7 @@ const browserMockAPI: ElectronAPI = { // Investigation operations (new system) startInvestigation: () => {}, cancelInvestigation: () => {}, + cancelAllInvestigations: () => {}, createTaskFromInvestigation: async () => ({ success: true, data: { specId: '' } }), dismissIssue: async () => ({ success: true }), postInvestigationToGitHub: async () => ({ success: true, data: { commentId: 0 } }), @@ -458,7 +459,17 @@ const browserMockAPI: ElectronAPI = { openLogsFolder: async () => ({ success: false, error: 'Not available in browser mode' }), copyDebugInfo: async () => ({ success: false, error: 'Not available in browser mode' }), getRecentErrors: async () => [], - listLogFiles: async () => [] + listLogFiles: async () => [], + + // Top-level investigation operations (legacy ElectronAPI surface) + startInvestigation: () => {}, + cancelInvestigation: () => {}, + cancelAllInvestigations: () => {}, + createTaskFromInvestigation: async () => ({ success: true, data: { specId: '' } }), + dismissIssue: async () => ({ success: true }), + postInvestigationToGitHub: async () => ({ success: true, data: { commentId: 0 } }), + getInvestigationSettings: async () => ({ success: true, data: { autoCreateTasks: false, autoStartTasks: false, pipelineMode: 'full' as const, autoPostToGitHub: false, autoCloseIssues: false, maxParallelInvestigations: 3, labelIncludeFilter: [], labelExcludeFilter: [] } }), + saveInvestigationSettings: async () => ({ success: true }), }; /** diff --git a/apps/frontend/src/renderer/stores/github/investigation-store.ts b/apps/frontend/src/renderer/stores/github/investigation-store.ts index ed8fe5c6..5d764aa3 100644 --- a/apps/frontend/src/renderer/stores/github/investigation-store.ts +++ b/apps/frontend/src/renderer/stores/github/investigation-store.ts @@ -10,6 +10,7 @@ import type { InvestigationState, PersistedInvestigationState, } from '@shared/types'; +import { toast } from '../../hooks/use-toast'; // ============================================ // Per-Issue Investigation State @@ -43,6 +44,10 @@ export interface IssueInvestigationState { completedAt: string | null; /** Linked task status (synced from task store for building/done states) */ linkedTaskStatus: string | null; + /** Activity log tracking key lifecycle events */ + activityLog: Array<{ event: string; timestamp: string }>; + /** True if the issue no longer exists in the GitHub response (stale/deleted) */ + isStale?: boolean; } // ============================================ @@ -73,7 +78,10 @@ interface InvestigationStoreState { clearIssueInvestigation: (projectId: string, issueNumber: number) => void; setSettings: (projectId: string, settings: InvestigationSettings) => void; syncTaskState: (projectId: string, issueNumber: number, taskStatus: string) => void; + clearLinkedTask: (projectId: string, issueNumber: number) => void; loadPersistedInvestigations: (projectId: string, states: PersistedInvestigationState[]) => void; + cancelAllInvestigations: (projectId: string) => void; + markStaleInvestigations: (projectId: string, activeIssueNumbers: Set) => void; // ---- Selectors ---- getInvestigationState: (projectId: string, issueNumber: number) => IssueInvestigationState | null; @@ -108,6 +116,9 @@ export const useInvestigationStore = create((set, get) startInvestigation: (projectId: string, issueNumber: number) => set((state) => { const key = `${projectId}:${issueNumber}`; const existing = state.investigations[key]; + const now = new Date().toISOString(); + const event = existing?.report ? 're-investigation started' : 'investigation started'; + const log = [...(existing?.activityLog ?? []), { event, timestamp: now }]; return { investigations: { ...state.investigations, @@ -122,9 +133,10 @@ export const useInvestigationStore = create((set, get) specId: existing?.specId ?? null, dismissReason: null, // clear dismiss on re-investigation githubCommentId: existing?.githubCommentId ?? null, - startedAt: new Date().toISOString(), + startedAt: now, completedAt: null, - linkedTaskStatus: existing?.linkedTaskStatus ?? null + linkedTaskStatus: existing?.linkedTaskStatus ?? null, + activityLog: log } } }; @@ -149,7 +161,8 @@ export const useInvestigationStore = create((set, get) githubCommentId: existing?.githubCommentId ?? null, startedAt: existing?.startedAt ?? null, completedAt: null, - linkedTaskStatus: existing?.linkedTaskStatus ?? null + linkedTaskStatus: existing?.linkedTaskStatus ?? null, + activityLog: existing?.activityLog ?? [] } } }; @@ -158,6 +171,7 @@ export const useInvestigationStore = create((set, get) setResult: (projectId: string, result: InvestigationResult) => set((state) => { const key = `${projectId}:${result.issueNumber}`; const existing = state.investigations[key]; + const log = [...(existing?.activityLog ?? []), { event: 'investigation completed', timestamp: result.completedAt }]; return { investigations: { ...state.investigations, @@ -174,7 +188,8 @@ export const useInvestigationStore = create((set, get) githubCommentId: result.githubCommentId ?? existing?.githubCommentId ?? null, startedAt: existing?.startedAt ?? null, completedAt: result.completedAt, - linkedTaskStatus: existing?.linkedTaskStatus ?? null + linkedTaskStatus: existing?.linkedTaskStatus ?? null, + activityLog: log } } }; @@ -183,6 +198,7 @@ export const useInvestigationStore = create((set, get) setError: (projectId: string, issueNumber: number, error: string) => set((state) => { const key = `${projectId}:${issueNumber}`; const existing = state.investigations[key]; + const log = [...(existing?.activityLog ?? []), { event: 'investigation failed', timestamp: new Date().toISOString() }]; return { investigations: { ...state.investigations, @@ -199,7 +215,8 @@ export const useInvestigationStore = create((set, get) githubCommentId: existing?.githubCommentId ?? null, startedAt: existing?.startedAt ?? null, completedAt: null, - linkedTaskStatus: existing?.linkedTaskStatus ?? null + linkedTaskStatus: existing?.linkedTaskStatus ?? null, + activityLog: log } } }; @@ -209,12 +226,14 @@ export const useInvestigationStore = create((set, get) const key = `${projectId}:${issueNumber}`; const existing = state.investigations[key]; if (!existing) return state; + const log = [...(existing.activityLog ?? []), { event: `dismissed: ${reason}`, timestamp: new Date().toISOString() }]; return { investigations: { ...state.investigations, [key]: { ...existing, - dismissReason: reason + dismissReason: reason, + activityLog: log } } }; @@ -276,6 +295,24 @@ export const useInvestigationStore = create((set, get) }; }), + clearLinkedTask: (projectId: string, issueNumber: number) => set((state) => { + const key = `${projectId}:${issueNumber}`; + const existing = state.investigations[key]; + if (!existing) return state; + const log = [...(existing.activityLog ?? []), { event: 'linked task deleted', timestamp: new Date().toISOString() }]; + return { + investigations: { + ...state.investigations, + [key]: { + ...existing, + specId: null, + linkedTaskStatus: null, + activityLog: log + } + } + }; + }), + loadPersistedInvestigations: (projectId: string, states: PersistedInvestigationState[]) => set((state) => { const newInvestigations = { ...state.investigations }; @@ -304,12 +341,43 @@ export const useInvestigationStore = create((set, get) startedAt: null, completedAt: persisted.completedAt ?? null, linkedTaskStatus: null, + activityLog: [] }; } return { investigations: newInvestigations }; }), + cancelAllInvestigations: (projectId: string) => set((state) => { + const updated = { ...state.investigations }; + let changed = false; + for (const [key, inv] of Object.entries(updated)) { + if (inv.projectId !== projectId || !inv.isInvestigating) continue; + updated[key] = { + ...inv, + isInvestigating: false, + progress: null, + error: 'Investigation cancelled', + }; + changed = true; + } + return changed ? { investigations: updated } : state; + }), + + markStaleInvestigations: (projectId: string, activeIssueNumbers: Set) => set((state) => { + const updated = { ...state.investigations }; + let changed = false; + for (const [key, inv] of Object.entries(updated)) { + if (inv.projectId !== projectId) continue; + const shouldBeStale = !activeIssueNumbers.has(inv.issueNumber); + if (inv.isStale !== shouldBeStale) { + updated[key] = { ...inv, isStale: shouldBeStale }; + changed = true; + } + } + return changed ? { investigations: updated } : state; + }), + // ---- Selectors ---- getInvestigationState: (projectId: string, issueNumber: number) => { @@ -390,6 +458,9 @@ export function initializeInvestigationListeners(): void { const cleanupComplete = window.electronAPI.github.onInvestigationComplete( (projectId: string, result: InvestigationResult) => { store.setResult(projectId, result); + toast({ + title: `Investigation complete for Issue #${result.issueNumber}`, + }); } ); cleanupFunctions.push(cleanupComplete); @@ -404,6 +475,10 @@ export function initializeInvestigationListeners(): void { // but for now we mark all active investigations as errored. for (const inv of active) { store.setError(projectId, inv.issueNumber, error); + toast({ + title: `Investigation failed for Issue #${inv.issueNumber}`, + variant: 'destructive', + }); } } ); @@ -467,6 +542,15 @@ export function cancelIssueInvestigation( window.electronAPI.github.cancelInvestigation(projectId, issueNumber); } +/** + * Cancel all running investigations for a project. + */ +export function cancelAllIssueInvestigations(projectId: string): void { + const store = useInvestigationStore.getState(); + store.cancelAllInvestigations(projectId); + window.electronAPI.github.cancelAllInvestigations(projectId); +} + /** * Load persisted investigation state from disk. * Call this when the GitHub Issues view mounts with a selected project diff --git a/apps/frontend/src/shared/i18n/locales/en/common.json b/apps/frontend/src/shared/i18n/locales/en/common.json index bcc75f0e..b718999c 100644 --- a/apps/frontend/src/shared/i18n/locales/en/common.json +++ b/apps/frontend/src/shared/i18n/locales/en/common.json @@ -1050,7 +1050,9 @@ "postToGitHub": "Post to GitHub", "postedToGitHub": "Posted to GitHub", "closeIssue": "Close Issue", - "closedIssueWarning": "This issue has been closed on GitHub.", + "closingIssue": "Closing...", + "closedIssueWarning": "This issue is closed. Investigation results may not be actionable.", + "resolvedSuggestion": "This issue appears to be already resolved. Close it on GitHub?", "confidence": "Confidence", "rootCause": "Root Cause", "impact": "Impact", @@ -1090,7 +1092,8 @@ "taskCreated": "Task Created", "building": "Building", "done": "Done", - "dismissed": "Dismissed" + "dismissed": "Dismissed", + "stale": "Stale" }, "stateFilters": { "all": "All", @@ -1161,6 +1164,15 @@ "position": "Queue position: {{position}}", "waiting": "Waiting in queue...", "running": "{{current}} of {{max}} running" + }, + "toast": { + "investigationComplete": "Investigation complete for Issue #{{issueNumber}}", + "investigationFailed": "Investigation failed for Issue #{{issueNumber}}" + }, + "labelConsent": { + "title": "Label Creation Notice", + "body": "The first investigation will create up to 5 auto-claude:* labels on your GitHub repository to categorize investigation results. These labels are used for filtering and organization.", + "confirm": "Continue" } } } diff --git a/apps/frontend/src/shared/i18n/locales/fr/common.json b/apps/frontend/src/shared/i18n/locales/fr/common.json index d47e9690..04c31430 100644 --- a/apps/frontend/src/shared/i18n/locales/fr/common.json +++ b/apps/frontend/src/shared/i18n/locales/fr/common.json @@ -1050,7 +1050,9 @@ "postToGitHub": "Publier sur GitHub", "postedToGitHub": "Publié sur GitHub", "closeIssue": "Fermer l'issue", - "closedIssueWarning": "Cette issue a été fermée sur GitHub.", + "closingIssue": "Fermeture...", + "closedIssueWarning": "Cette issue est fermée. Les résultats de l'investigation peuvent ne pas être exploitables.", + "resolvedSuggestion": "Cette issue semble déjà résolue. La fermer sur GitHub ?", "confidence": "Confiance", "rootCause": "Cause racine", "impact": "Impact", @@ -1090,7 +1092,8 @@ "taskCreated": "Tâche créée", "building": "En construction", "done": "Terminé", - "dismissed": "Rejeté" + "dismissed": "Rejeté", + "stale": "Obsolète" }, "stateFilters": { "all": "Tous", @@ -1161,6 +1164,15 @@ "position": "Position dans la file : {{position}}", "waiting": "En attente dans la file...", "running": "{{current}} sur {{max}} en cours" + }, + "toast": { + "investigationComplete": "Investigation terminée pour l'issue #{{issueNumber}}", + "investigationFailed": "Investigation échouée pour l'issue #{{issueNumber}}" + }, + "labelConsent": { + "title": "Avis de création de labels", + "body": "La première investigation créera jusqu'à 5 labels auto-claude:* sur votre dépôt GitHub pour catégoriser les résultats d'investigation. Ces labels sont utilisés pour le filtrage et l'organisation.", + "confirm": "Continuer" } } }