feat(issues): add investigation type definitions, IPC channels, and preload bridge (F1)
Create investigation.ts with 18 types/interfaces for the AI investigation system. Add 7 new IPC channels, update ElectronAPI with new method declarations, add preload bridge implementations, deprecate old GitHubInvestigationResult/Status types, and add mock stubs for browser dev mode. Co-Authored-By: Claude Opus 4.6 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d20a62d6fd
commit
65bcb72ca6
@@ -10,7 +10,11 @@ import type {
|
||||
VersionSuggestion,
|
||||
PaginatedIssuesResult,
|
||||
PRStatusUpdate,
|
||||
PollingMetadata
|
||||
PollingMetadata,
|
||||
InvestigationProgress,
|
||||
InvestigationResult,
|
||||
InvestigationDismissReason,
|
||||
InvestigationSettings
|
||||
} from '../../../shared/types';
|
||||
import type { EnrichmentFile, IssueEnrichment, WorkflowState, Resolution } from '../../../shared/types/enrichment';
|
||||
import type { LabelSyncConfig, LabelSyncResult } from '../../../shared/types/label-sync';
|
||||
@@ -225,7 +229,27 @@ export interface GitHubAPI {
|
||||
) => Promise<IPCResult<{ remoteUrl: string }>>;
|
||||
listGitHubOrgs: () => Promise<IPCResult<{ orgs: Array<{ login: string; avatarUrl?: string }> }>>;
|
||||
|
||||
// Event Listeners
|
||||
// Investigation operations (new system)
|
||||
startInvestigation: (projectId: string, issueNumber: number) => void;
|
||||
cancelInvestigation: (projectId: string, issueNumber: number) => void;
|
||||
createTaskFromInvestigation: (projectId: string, issueNumber: number) => Promise<IPCResult<{ specId: string }>>;
|
||||
dismissIssue: (projectId: string, issueNumber: number, reason: InvestigationDismissReason) => Promise<IPCResult>;
|
||||
postInvestigationToGitHub: (projectId: string, issueNumber: number) => Promise<IPCResult<{ commentId: number }>>;
|
||||
getInvestigationSettings: (projectId: string) => Promise<IPCResult<InvestigationSettings>>;
|
||||
saveInvestigationSettings: (projectId: string, settings: Partial<InvestigationSettings>) => Promise<IPCResult>;
|
||||
|
||||
// Investigation event listeners (new system)
|
||||
onInvestigationProgress: (
|
||||
callback: (projectId: string, progress: InvestigationProgress) => void
|
||||
) => IpcListenerCleanup;
|
||||
onInvestigationComplete: (
|
||||
callback: (projectId: string, result: InvestigationResult) => void
|
||||
) => IpcListenerCleanup;
|
||||
onInvestigationError: (
|
||||
callback: (projectId: string, error: string) => void
|
||||
) => IpcListenerCleanup;
|
||||
|
||||
// Legacy Event Listeners (deprecated — use investigation methods above)
|
||||
onGitHubInvestigationProgress: (
|
||||
callback: (projectId: string, status: GitHubInvestigationStatus) => void
|
||||
) => IpcListenerCleanup;
|
||||
@@ -682,7 +706,45 @@ export const createGitHubAPI = (): GitHubAPI => ({
|
||||
listGitHubOrgs: (): Promise<IPCResult<{ orgs: Array<{ login: string; avatarUrl?: string }> }>> =>
|
||||
invokeIpc(IPC_CHANNELS.GITHUB_LIST_ORGS),
|
||||
|
||||
// Event Listeners
|
||||
// Investigation operations (new system)
|
||||
startInvestigation: (projectId: string, issueNumber: number): void =>
|
||||
sendIpc(IPC_CHANNELS.GITHUB_INVESTIGATION_START, projectId, issueNumber),
|
||||
|
||||
cancelInvestigation: (projectId: string, issueNumber: number): void =>
|
||||
sendIpc(IPC_CHANNELS.GITHUB_INVESTIGATION_CANCEL, projectId, issueNumber),
|
||||
|
||||
createTaskFromInvestigation: (projectId: string, issueNumber: number): Promise<IPCResult<{ specId: string }>> =>
|
||||
invokeIpc(IPC_CHANNELS.GITHUB_INVESTIGATION_CREATE_TASK, projectId, issueNumber),
|
||||
|
||||
dismissIssue: (projectId: string, issueNumber: number, reason: InvestigationDismissReason): Promise<IPCResult> =>
|
||||
invokeIpc(IPC_CHANNELS.GITHUB_INVESTIGATION_DISMISS, projectId, issueNumber, reason),
|
||||
|
||||
postInvestigationToGitHub: (projectId: string, issueNumber: number): Promise<IPCResult<{ commentId: number }>> =>
|
||||
invokeIpc(IPC_CHANNELS.GITHUB_INVESTIGATION_POST_GITHUB, projectId, issueNumber),
|
||||
|
||||
getInvestigationSettings: (projectId: string): Promise<IPCResult<InvestigationSettings>> =>
|
||||
invokeIpc(IPC_CHANNELS.GITHUB_INVESTIGATION_GET_SETTINGS, projectId),
|
||||
|
||||
saveInvestigationSettings: (projectId: string, settings: Partial<InvestigationSettings>): Promise<IPCResult> =>
|
||||
invokeIpc(IPC_CHANNELS.GITHUB_INVESTIGATION_SAVE_SETTINGS, projectId, settings),
|
||||
|
||||
// Investigation event listeners (new system)
|
||||
onInvestigationProgress: (
|
||||
callback: (projectId: string, progress: InvestigationProgress) => void
|
||||
): IpcListenerCleanup =>
|
||||
createIpcListener(IPC_CHANNELS.GITHUB_INVESTIGATION_PROGRESS, callback),
|
||||
|
||||
onInvestigationComplete: (
|
||||
callback: (projectId: string, result: InvestigationResult) => void
|
||||
): IpcListenerCleanup =>
|
||||
createIpcListener(IPC_CHANNELS.GITHUB_INVESTIGATION_COMPLETE, callback),
|
||||
|
||||
onInvestigationError: (
|
||||
callback: (projectId: string, error: string) => void
|
||||
): IpcListenerCleanup =>
|
||||
createIpcListener(IPC_CHANNELS.GITHUB_INVESTIGATION_ERROR, callback),
|
||||
|
||||
// Legacy Event Listeners (deprecated — use investigation methods above)
|
||||
onGitHubInvestigationProgress: (
|
||||
callback: (projectId: string, status: GitHubInvestigationStatus) => void
|
||||
): IpcListenerCleanup =>
|
||||
|
||||
@@ -190,6 +190,18 @@ const browserMockAPI: ElectronAPI = {
|
||||
listGitHubOrgs: async () => ({ success: true, data: { orgs: [] } }),
|
||||
onGitHubAuthDeviceCode: () => () => {},
|
||||
onGitHubAuthChanged: () => () => {},
|
||||
// Investigation operations (new system)
|
||||
startInvestigation: () => {},
|
||||
cancelInvestigation: () => {},
|
||||
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 }),
|
||||
onInvestigationProgress: () => () => {},
|
||||
onInvestigationComplete: () => () => {},
|
||||
onInvestigationError: () => () => {},
|
||||
// Legacy investigation listeners
|
||||
onGitHubInvestigationProgress: () => () => {},
|
||||
onGitHubInvestigationComplete: () => () => {},
|
||||
onGitHubInvestigationError: () => () => {},
|
||||
|
||||
@@ -135,6 +135,15 @@ export const integrationMock = {
|
||||
}
|
||||
}),
|
||||
|
||||
// Investigation operations (new system)
|
||||
startInvestigation: () => {},
|
||||
cancelInvestigation: () => {},
|
||||
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 }),
|
||||
|
||||
onGitHubInvestigationProgress: () => () => {},
|
||||
onGitHubInvestigationComplete: () => () => {},
|
||||
onGitHubInvestigationError: () => () => {},
|
||||
|
||||
@@ -266,7 +266,16 @@ export const IPC_CHANNELS = {
|
||||
GITHUB_AUTH_DEVICE_CODE: 'github:authDeviceCode',
|
||||
GITHUB_AUTH_CHANGED: 'github:authChanged', // Event: GitHub auth state changed (account swap)
|
||||
|
||||
// GitHub events (main -> renderer)
|
||||
// GitHub Investigation operations (renderer -> main)
|
||||
GITHUB_INVESTIGATION_START: 'github:investigation:start',
|
||||
GITHUB_INVESTIGATION_CANCEL: 'github:investigation:cancel',
|
||||
GITHUB_INVESTIGATION_CREATE_TASK: 'github:investigation:createTask',
|
||||
GITHUB_INVESTIGATION_DISMISS: 'github:investigation:dismiss',
|
||||
GITHUB_INVESTIGATION_POST_GITHUB: 'github:investigation:postGitHub',
|
||||
GITHUB_INVESTIGATION_GET_SETTINGS: 'github:investigation:getSettings',
|
||||
GITHUB_INVESTIGATION_SAVE_SETTINGS: 'github:investigation:saveSettings',
|
||||
|
||||
// GitHub Investigation events (main -> renderer)
|
||||
GITHUB_INVESTIGATION_PROGRESS: 'github:investigationProgress',
|
||||
GITHUB_INVESTIGATION_COMPLETE: 'github:investigationComplete',
|
||||
GITHUB_INVESTIGATION_ERROR: 'github:investigationError',
|
||||
|
||||
@@ -18,6 +18,7 @@ export * from './changelog';
|
||||
export * from './insights';
|
||||
export * from './roadmap';
|
||||
export * from './integrations';
|
||||
export * from './investigation';
|
||||
export * from './app-update';
|
||||
export * from './cli';
|
||||
export * from './pr-status';
|
||||
|
||||
@@ -130,6 +130,10 @@ export interface GitHubImportResult {
|
||||
tasks?: import('./task').Task[];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `InvestigationResult` from `./investigation` instead.
|
||||
* Kept for backwards compatibility with existing code that references this type.
|
||||
*/
|
||||
export interface GitHubInvestigationResult {
|
||||
success: boolean;
|
||||
issueNumber: number;
|
||||
@@ -144,6 +148,10 @@ export interface GitHubInvestigationResult {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use `InvestigationProgress` from `./investigation` instead.
|
||||
* Kept for backwards compatibility with existing code that references this type.
|
||||
*/
|
||||
export interface GitHubInvestigationStatus {
|
||||
phase: 'idle' | 'fetching' | 'analyzing' | 'creating_task' | 'complete' | 'error';
|
||||
issueNumber?: number;
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/**
|
||||
* AI Issue Investigation types
|
||||
*
|
||||
* Defines the type contracts for the investigation system that replaces
|
||||
* the old triage/enrichment workflow. Investigation runs 4 specialist
|
||||
* agents in parallel to analyze GitHub issues.
|
||||
*/
|
||||
|
||||
// ============================================
|
||||
// Investigation State Machine
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Derived investigation state for an issue.
|
||||
* Computed from investigation data + linked task status, never manually set.
|
||||
*/
|
||||
export type InvestigationState =
|
||||
| 'new'
|
||||
| 'investigating'
|
||||
| 'findings_ready'
|
||||
| 'resolved'
|
||||
| 'failed'
|
||||
| 'task_created'
|
||||
| 'building'
|
||||
| 'done';
|
||||
|
||||
/**
|
||||
* The 4 specialist agent types that run during investigation.
|
||||
*/
|
||||
export type InvestigationAgentType = 'root_cause' | 'impact' | 'fix_advisor' | 'reproducer';
|
||||
|
||||
// ============================================
|
||||
// Agent Output Types
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Code reference found by an investigation agent.
|
||||
*/
|
||||
export interface CodeReference {
|
||||
file: string;
|
||||
line?: number;
|
||||
endLine?: number;
|
||||
snippet?: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Base output from any specialist agent.
|
||||
*/
|
||||
export interface InvestigationAgentResult {
|
||||
agentType: InvestigationAgentType;
|
||||
summary: string;
|
||||
findings: string[];
|
||||
codeReferences: CodeReference[];
|
||||
confidence: number; // 0-100
|
||||
}
|
||||
|
||||
/**
|
||||
* Root Cause Analyzer output — traces the bug/issue to its source.
|
||||
*/
|
||||
export interface RootCauseAnalysis extends InvestigationAgentResult {
|
||||
agentType: 'root_cause';
|
||||
rootCause: string;
|
||||
codePaths: string[];
|
||||
relatedIssues?: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Impact Assessor output — determines blast radius.
|
||||
*/
|
||||
export interface ImpactAssessment extends InvestigationAgentResult {
|
||||
agentType: 'impact';
|
||||
severity: 'critical' | 'high' | 'medium' | 'low';
|
||||
affectedComponents: string[];
|
||||
userImpact: string;
|
||||
riskIfUnfixed: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fix Advisor output — suggests concrete fix approaches.
|
||||
*/
|
||||
export interface FixAdvice extends InvestigationAgentResult {
|
||||
agentType: 'fix_advisor';
|
||||
suggestedApproaches: Array<{
|
||||
title: string;
|
||||
description: string;
|
||||
filesToModify: string[];
|
||||
complexity: 'simple' | 'standard' | 'complex';
|
||||
risks: string[];
|
||||
}>;
|
||||
recommendedApproach: number; // index into suggestedApproaches
|
||||
patternsToFollow: string[];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reproducer output — reproducibility and test coverage.
|
||||
*/
|
||||
export interface ReproductionAnalysis extends InvestigationAgentResult {
|
||||
agentType: 'reproducer';
|
||||
reproducible: boolean | 'unknown';
|
||||
reproductionSteps?: string[];
|
||||
existingTests: string[];
|
||||
testGaps: string[];
|
||||
suggestedTests: string[];
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Investigation Report
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Linked PR detected during investigation.
|
||||
*/
|
||||
export interface LinkedPR {
|
||||
number: number;
|
||||
title: string;
|
||||
state: 'open' | 'closed' | 'merged';
|
||||
url: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* AI-suggested label for the issue.
|
||||
*/
|
||||
export interface SuggestedLabel {
|
||||
name: string;
|
||||
reason: string;
|
||||
accepted?: boolean; // user accept/reject state
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete investigation report combining all 4 agent outputs.
|
||||
*/
|
||||
export interface InvestigationReport {
|
||||
rootCause: RootCauseAnalysis;
|
||||
impact: ImpactAssessment;
|
||||
fixAdvice: FixAdvice;
|
||||
reproduction: ReproductionAnalysis;
|
||||
summary: string;
|
||||
severity: 'critical' | 'high' | 'medium' | 'low';
|
||||
suggestedLabels: SuggestedLabel[];
|
||||
likelyResolved: boolean;
|
||||
linkedPRs: LinkedPR[];
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Investigation Progress & Result
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Per-agent status within an investigation.
|
||||
*/
|
||||
export interface AgentStatus {
|
||||
agentType: InvestigationAgentType;
|
||||
status: 'pending' | 'running' | 'completed' | 'failed';
|
||||
progress: number; // 0-100
|
||||
message?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Real-time progress update during investigation.
|
||||
*/
|
||||
export interface InvestigationProgress {
|
||||
issueNumber: number;
|
||||
phase: string;
|
||||
progress: number; // 0-100
|
||||
message: string;
|
||||
agentStatuses: AgentStatus[];
|
||||
startedAt: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Final investigation result delivered on completion.
|
||||
*/
|
||||
export interface InvestigationResult {
|
||||
issueNumber: number;
|
||||
report: InvestigationReport;
|
||||
specId?: string;
|
||||
worktreePath?: string;
|
||||
githubCommentId?: number;
|
||||
completedAt: string;
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Dismiss & Settings
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* Reason for dismissing an issue.
|
||||
*/
|
||||
export type InvestigationDismissReason = 'wont_fix' | 'duplicate' | 'cannot_reproduce' | 'out_of_scope';
|
||||
|
||||
/**
|
||||
* Pipeline mode for investigation-created tasks.
|
||||
*/
|
||||
export type InvestigationPipelineMode = 'full' | 'skip_to_planning' | 'minimal';
|
||||
|
||||
/**
|
||||
* Investigation settings (subsection of GitHub settings).
|
||||
*/
|
||||
export interface InvestigationSettings {
|
||||
autoCreateTasks: boolean;
|
||||
autoStartTasks: boolean;
|
||||
pipelineMode: InvestigationPipelineMode;
|
||||
autoPostToGitHub: boolean;
|
||||
autoCloseIssues: boolean;
|
||||
maxParallelInvestigations: number;
|
||||
labelIncludeFilter: string[];
|
||||
labelExcludeFilter: string[];
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// Batch Staging
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* An item in the batch staging area (auto-create review).
|
||||
*/
|
||||
export interface BatchStagingItem {
|
||||
issueNumber: number;
|
||||
issueTitle: string;
|
||||
report: InvestigationReport;
|
||||
specId: string;
|
||||
approved?: boolean;
|
||||
createdAt: string;
|
||||
}
|
||||
@@ -138,6 +138,12 @@ import type {
|
||||
GitLabMRReviewProgress,
|
||||
GitLabNewCommitsCheck
|
||||
} from './integrations';
|
||||
import type {
|
||||
InvestigationProgress,
|
||||
InvestigationResult,
|
||||
InvestigationDismissReason,
|
||||
InvestigationSettings
|
||||
} from './investigation';
|
||||
import type { APIProfile, ProfilesFile, TestConnectionResult, DiscoverModelsResult } from './profile';
|
||||
|
||||
// ============================================
|
||||
@@ -533,7 +539,16 @@ export interface ElectronAPI {
|
||||
callback: (data: { deviceCode: string; authUrl: string; browserOpened: boolean }) => void
|
||||
) => () => void;
|
||||
|
||||
// GitHub event listeners
|
||||
// GitHub Investigation operations (new system)
|
||||
startInvestigation: (projectId: string, issueNumber: number) => void;
|
||||
cancelInvestigation: (projectId: string, issueNumber: number) => void;
|
||||
createTaskFromInvestigation: (projectId: string, issueNumber: number) => Promise<IPCResult<{ specId: string }>>;
|
||||
dismissIssue: (projectId: string, issueNumber: number, reason: InvestigationDismissReason) => Promise<IPCResult>;
|
||||
postInvestigationToGitHub: (projectId: string, issueNumber: number) => Promise<IPCResult<{ commentId: number }>>;
|
||||
getInvestigationSettings: (projectId: string) => Promise<IPCResult<InvestigationSettings>>;
|
||||
saveInvestigationSettings: (projectId: string, settings: Partial<InvestigationSettings>) => Promise<IPCResult>;
|
||||
|
||||
// GitHub event listeners (legacy signatures kept for backwards compatibility)
|
||||
onGitHubInvestigationProgress: (
|
||||
callback: (projectId: string, status: GitHubInvestigationStatus) => void
|
||||
) => () => void;
|
||||
|
||||
Reference in New Issue
Block a user