fix(ci): resolve frontend validation failures

This commit is contained in:
777genius 2026-05-02 00:12:02 +03:00
parent 01b8161f41
commit 46df757f49
10 changed files with 126 additions and 82 deletions

View file

@ -1,4 +1,4 @@
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useState } from 'react';
import { api } from '@renderer/api'; import { api } from '@renderer/api';
import { CreateTaskDialog } from '@renderer/components/team/dialogs/CreateTaskDialog'; import { CreateTaskDialog } from '@renderer/components/team/dialogs/CreateTaskDialog';

View file

@ -6,29 +6,53 @@ import {
} from '@renderer/store/slices/teamSlice'; } from '@renderer/store/slices/teamSlice';
import { useShallow } from 'zustand/react/shallow'; import { useShallow } from 'zustand/react/shallow';
import type { TeamGraphData } from '../adapters/TeamGraphAdapter'; import type { AppState } from '@renderer/store/types';
export function useGraphMemberPopoverContext(teamName: string, memberName: string) { interface GraphMemberPopoverContext {
teamData:
| (NonNullable<ReturnType<typeof selectTeamDataForName>> & {
members: ReturnType<typeof selectResolvedMembersForTeamName>;
messageFeed: [];
})
| null;
teamMembers: ReturnType<typeof selectResolvedMembersForTeamName>;
spawnEntry: AppState['memberSpawnStatusesByTeam'][string][string] | undefined;
leadActivity: AppState['leadActivityByTeam'][string] | undefined;
progress: ReturnType<typeof getCurrentProvisioningProgressForTeam> | null;
memberSpawnSnapshot: AppState['memberSpawnSnapshotsByTeam'][string] | undefined;
memberSpawnStatuses: AppState['memberSpawnStatusesByTeam'][string] | undefined;
}
function selectGraphMemberPopoverContext(
state: AppState,
teamName: string,
memberName: string
): GraphMemberPopoverContext {
const snapshot = teamName ? selectTeamDataForName(state, teamName) : null;
const teamMembers = teamName ? selectResolvedMembersForTeamName(state, teamName) : [];
return {
teamData: snapshot
? {
...snapshot,
members: teamMembers,
messageFeed: [],
}
: null,
teamMembers,
spawnEntry: teamName ? state.memberSpawnStatusesByTeam[teamName]?.[memberName] : undefined,
leadActivity: teamName ? state.leadActivityByTeam[teamName] : undefined,
progress: teamName ? getCurrentProvisioningProgressForTeam(state, teamName) : null,
memberSpawnSnapshot: teamName ? state.memberSpawnSnapshotsByTeam[teamName] : undefined,
memberSpawnStatuses: teamName ? state.memberSpawnStatusesByTeam[teamName] : undefined,
};
}
export function useGraphMemberPopoverContext(
teamName: string,
memberName: string
): ReturnType<typeof selectGraphMemberPopoverContext> {
return useStore( return useStore(
useShallow((state) => { useShallow((state) => selectGraphMemberPopoverContext(state, teamName, memberName))
const snapshot = teamName ? selectTeamDataForName(state, teamName) : null;
const teamMembers = teamName ? selectResolvedMembersForTeamName(state, teamName) : [];
return {
teamData: snapshot
? {
...snapshot,
members: teamMembers,
messageFeed: [],
}
: null,
teamMembers,
spawnEntry: teamName ? state.memberSpawnStatusesByTeam[teamName]?.[memberName] : undefined,
leadActivity: teamName ? state.leadActivityByTeam[teamName] : undefined,
progress: teamName ? getCurrentProvisioningProgressForTeam(state, teamName) : null,
memberSpawnSnapshot: teamName ? state.memberSpawnSnapshotsByTeam[teamName] : undefined,
memberSpawnStatuses: teamName ? state.memberSpawnStatusesByTeam[teamName] : undefined,
};
})
); );
} }

View file

@ -289,6 +289,15 @@ export const GraphActivityHud = ({
const handleMessageClick = useCallback((item: TimelineItem) => { const handleMessageClick = useCallback((item: TimelineItem) => {
setExpandedItem(item); setExpandedItem(item);
}, []); }, []);
const handleMessageKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLDivElement>, item: TimelineItem): void => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleMessageClick(item);
}
},
[handleMessageClick]
);
const handleMemberClick = useCallback( const handleMemberClick = useCallback(
(member: ResolvedTeamMember) => { (member: ResolvedTeamMember) => {
@ -360,6 +369,52 @@ export const GraphActivityHud = ({
}; };
}, [enabled, forwardWheelToGraph, visibleLanes]); }, [enabled, forwardWheelToGraph, visibleLanes]);
const renderLaneEntry = useCallback(
(entry: InlineActivityEntry, index: number): React.JSX.Element => {
const messageKey = toMessageKey(entry.message);
const timelineItem: TimelineItem = {
type: 'message',
message: entry.message,
};
const isUnread = !entry.message.read && !readSet.has(messageKey);
return (
<div
key={entry.graphItem.id}
className="h-[72px] min-h-[72px] min-w-0 max-w-full cursor-pointer overflow-hidden"
role="button"
tabIndex={0}
onClick={() => handleMessageClick(timelineItem)}
onKeyDown={(event) => handleMessageKeyDown(event, timelineItem)}
>
<GraphActivityCard
message={entry.message}
teamName={teamName}
messageContext={messageContext}
teamNames={teamNames}
teamColorByName={teamColorByName}
isUnread={isUnread}
zebraShade={index % 2 === 1}
onClick={() => handleMessageClick(timelineItem)}
onOpenTaskDetail={onOpenTaskDetail}
onOpenMemberProfile={onOpenMemberProfile}
/>
</div>
);
},
[
handleMessageClick,
handleMessageKeyDown,
messageContext,
onOpenMemberProfile,
onOpenTaskDetail,
readSet,
teamColorByName,
teamName,
teamNames,
]
);
if (!enabled || !teamSnapshot || visibleLanes.length === 0) { if (!enabled || !teamSnapshot || visibleLanes.length === 0) {
return null; return null;
} }
@ -421,43 +476,7 @@ export const GraphActivityHud = ({
No recent activity No recent activity
</div> </div>
) : null} ) : null}
{lane.entries.map((entry, index) => { {lane.entries.map(renderLaneEntry)}
const messageKey = toMessageKey(entry.message);
const timelineItem: TimelineItem = {
type: 'message',
message: entry.message,
};
const isUnread = !entry.message.read && !readSet.has(messageKey);
return (
<div
key={entry.graphItem.id}
className="h-[72px] min-h-[72px] min-w-0 max-w-full cursor-pointer overflow-hidden"
role="button"
tabIndex={0}
onClick={() => handleMessageClick(timelineItem)}
onKeyDown={(event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleMessageClick(timelineItem);
}
}}
>
<GraphActivityCard
message={entry.message}
teamName={teamName}
messageContext={messageContext}
teamNames={teamNames}
teamColorByName={teamColorByName}
isUnread={isUnread}
zebraShade={index % 2 === 1}
onClick={() => handleMessageClick(timelineItem)}
onOpenTaskDetail={onOpenTaskDetail}
onOpenMemberProfile={onOpenMemberProfile}
/>
</div>
);
})}
{lane.overflowCount > 0 ? ( {lane.overflowCount > 0 ? (
<button <button

View file

@ -19,7 +19,11 @@ import { GraphNodePopover } from './GraphNodePopover';
import { GraphProvisioningHud } from './GraphProvisioningHud'; import { GraphProvisioningHud } from './GraphProvisioningHud';
import { GraphTransientHandoffHud } from './GraphTransientHandoffHud'; import { GraphTransientHandoffHud } from './GraphTransientHandoffHud';
import type { GraphDomainRef, GraphEventPort } from '@claude-teams/agent-graph'; import type {
GraphDomainRef,
GraphEventPort,
TransientHandoffCard,
} from '@claude-teams/agent-graph';
import type { import type {
MemberActivityFilter, MemberActivityFilter,
MemberDetailTab, MemberDetailTab,
@ -149,7 +153,7 @@ export const TeamGraphOverlay = ({
focusNodeIds?: ReadonlySet<string> | null; focusNodeIds?: ReadonlySet<string> | null;
focusEdgeIds?: ReadonlySet<string> | null; focusEdgeIds?: ReadonlySet<string> | null;
}) => { }) => {
cards: import('@claude-teams/agent-graph').TransientHandoffCard[]; cards: TransientHandoffCard[];
time: number; time: number;
}; };
worldToScreen?: (x: number, y: number) => { x: number; y: number }; worldToScreen?: (x: number, y: number) => { x: number; y: number };

View file

@ -19,7 +19,11 @@ import { GraphNodePopover } from './GraphNodePopover';
import { GraphProvisioningHud } from './GraphProvisioningHud'; import { GraphProvisioningHud } from './GraphProvisioningHud';
import { GraphTransientHandoffHud } from './GraphTransientHandoffHud'; import { GraphTransientHandoffHud } from './GraphTransientHandoffHud';
import type { GraphDomainRef, GraphEventPort } from '@claude-teams/agent-graph'; import type {
GraphDomainRef,
GraphEventPort,
TransientHandoffCard,
} from '@claude-teams/agent-graph';
import type { import type {
MemberActivityFilter, MemberActivityFilter,
MemberDetailTab, MemberDetailTab,
@ -169,7 +173,7 @@ export const TeamGraphTab = ({
focusNodeIds?: ReadonlySet<string> | null; focusNodeIds?: ReadonlySet<string> | null;
focusEdgeIds?: ReadonlySet<string> | null; focusEdgeIds?: ReadonlySet<string> | null;
}) => { }) => {
cards: import('@claude-teams/agent-graph').TransientHandoffCard[]; cards: TransientHandoffCard[];
time: number; time: number;
}; };
worldToScreen?: (x: number, y: number) => { x: number; y: number }; worldToScreen?: (x: number, y: number) => { x: number; y: number };

View file

@ -15,15 +15,15 @@ import { BoardTaskExactLogSummarySelector } from '../exact/BoardTaskExactLogSumm
import { isBoardTaskExactLogsReadEnabled } from '../exact/featureGates'; import { isBoardTaskExactLogsReadEnabled } from '../exact/featureGates';
import { getBoardTaskExactLogFileVersions } from '../exact/fileVersions'; import { getBoardTaskExactLogFileVersions } from '../exact/fileVersions';
import { OpenCodeTaskLogStreamSource } from './OpenCodeTaskLogStreamSource';
import { CodexNativeTaskLogStreamSource } from './CodexNativeTaskLogStreamSource';
import { buildCodexNativeToolSignature } from './CodexNativeTraceProjector';
import { HistoricalBoardMcpRawProbe } from './HistoricalBoardMcpRawProbe';
import { TaskLogTranscriptCandidateSelector } from './TaskLogTranscriptCandidateSelector';
import { import {
canonicalizeBoardTaskLogToolName, canonicalizeBoardTaskLogToolName,
isBoardTaskLogMcpToolName, isBoardTaskLogMcpToolName,
} from './boardTaskLogToolNames'; } from './boardTaskLogToolNames';
import { CodexNativeTaskLogStreamSource } from './CodexNativeTaskLogStreamSource';
import { buildCodexNativeToolSignature } from './CodexNativeTraceProjector';
import { HistoricalBoardMcpRawProbe } from './HistoricalBoardMcpRawProbe';
import { OpenCodeTaskLogStreamSource } from './OpenCodeTaskLogStreamSource';
import { TaskLogTranscriptCandidateSelector } from './TaskLogTranscriptCandidateSelector';
import type { BoardTaskActivityRecord } from '../activity/BoardTaskActivityRecord'; import type { BoardTaskActivityRecord } from '../activity/BoardTaskActivityRecord';
import type { BoardTaskExactLogDetailCandidate } from '../exact/BoardTaskExactLogTypes'; import type { BoardTaskExactLogDetailCandidate } from '../exact/BoardTaskExactLogTypes';
@ -1684,12 +1684,12 @@ export class BoardTaskLogStreamService {
const cacheKey = this.buildLayoutCacheKey(teamName, taskId); const cacheKey = this.buildLayoutCacheKey(teamName, taskId);
const generation = this.getTranscriptDiscoveryGeneration(teamName); const generation = this.getTranscriptDiscoveryGeneration(teamName);
const cached = this.layoutCache.get(cacheKey); const cached = this.layoutCache.get(cacheKey);
if (cached && cached.generation === generation && cached.expiresAt > Date.now()) { if (cached?.generation === generation && cached.expiresAt > Date.now()) {
return cached.layout; return cached.layout;
} }
const existingInFlight = this.layoutInFlight.get(cacheKey); const existingInFlight = this.layoutInFlight.get(cacheKey);
if (existingInFlight && existingInFlight.generation === generation) { if (existingInFlight?.generation === generation) {
return await existingInFlight.promise; return await existingInFlight.promise;
} }

View file

@ -1,8 +1,7 @@
import { getTeamsBasePath } from '@main/utils/pathDecoder';
import * as fs from 'fs/promises'; import * as fs from 'fs/promises';
import * as path from 'path'; import * as path from 'path';
import { getTeamsBasePath } from '@main/utils/pathDecoder';
const TRACE_ROOT_SEGMENT = path.join('.member-work-sync', 'runtime-hooks', 'codex-native-traces'); const TRACE_ROOT_SEGMENT = path.join('.member-work-sync', 'runtime-hooks', 'codex-native-traces');
export interface CodexNativeTraceProjection { export interface CodexNativeTraceProjection {

View file

@ -1,8 +1,7 @@
import { getTaskDisplayId } from '@shared/utils/taskIdentity';
import { createReadStream } from 'fs'; import { createReadStream } from 'fs';
import * as readline from 'readline'; import * as readline from 'readline';
import { getTaskDisplayId } from '@shared/utils/taskIdentity';
import type { TeamTask } from '@shared/types'; import type { TeamTask } from '@shared/types';
const RAW_PROBE_CONCURRENCY = process.platform === 'win32' ? 4 : 8; const RAW_PROBE_CONCURRENCY = process.platform === 'win32' ? 4 : 8;

View file

@ -45,10 +45,5 @@ export function isReadOnlyBoardTaskLogToolName(toolName: string | undefined): bo
} }
export function isRecoverableHistoricalBoardTaskLogToolName(toolName: string | undefined): boolean { export function isRecoverableHistoricalBoardTaskLogToolName(toolName: string | undefined): boolean {
const canonical = canonicalizeBoardTaskLogToolName(toolName); return isBoardTaskLogMcpToolName(toolName);
return (
canonical !== null &&
(HISTORICAL_BOARD_LIFECYCLE_TOOL_NAMES.has(canonical) ||
HISTORICAL_BOARD_ACTION_TOOL_NAMES.has(canonical))
);
} }

View file

@ -19,9 +19,9 @@ import { AlertCircle, Clock, FileText, Loader2 } from 'lucide-react';
import type { import type {
BoardTaskLogActor, BoardTaskLogActor,
ResolvedTeamMember,
BoardTaskLogSegment, BoardTaskLogSegment,
BoardTaskLogStreamResponse, BoardTaskLogStreamResponse,
ResolvedTeamMember,
} from '@shared/types'; } from '@shared/types';
interface TaskLogStreamSectionProps { interface TaskLogStreamSectionProps {