perf(team): skip data refresh for log activity pulses

This commit is contained in:
777genius 2026-05-06 23:30:51 +03:00
parent b7fa5443fd
commit abe26ddcc4
2 changed files with 61 additions and 15 deletions

View file

@ -581,15 +581,19 @@ export function initializeNotificationListeners(): () => void {
}; };
const markTaskLogActivity = (teamName: string, taskId: string): void => { const markTaskLogActivity = (teamName: string, taskId: string): void => {
clearTaskLogActivityTimer(teamName, taskId); clearTaskLogActivityTimer(teamName, taskId);
useStore.setState((prev) => ({ const isAlreadyActive =
activeTaskLogActivityByTeam: { useStore.getState().activeTaskLogActivityByTeam[teamName]?.[taskId] === true;
...prev.activeTaskLogActivityByTeam, if (!isAlreadyActive) {
[teamName]: { useStore.setState((prev) => ({
...(prev.activeTaskLogActivityByTeam[teamName] ?? {}), activeTaskLogActivityByTeam: {
[taskId]: true, ...prev.activeTaskLogActivityByTeam,
[teamName]: {
...(prev.activeTaskLogActivityByTeam[teamName] ?? {}),
[taskId]: true,
},
}, },
}, }));
})); }
const timerKey = buildTaskLogActivityTimerKey(teamName, taskId); const timerKey = buildTaskLogActivityTimerKey(teamName, taskId);
const timer = setTimeout(() => { const timer = setTimeout(() => {
taskLogActivityTimers.delete(timerKey); taskLogActivityTimers.delete(timerKey);
@ -1703,9 +1707,13 @@ export function initializeNotificationListeners(): () => void {
seedCurrentRunIdIfMissing(); seedCurrentRunIdIfMissing();
const visible = isTeamVisibleInAnyPane(event.teamName); const visible = isTeamVisibleInAnyPane(event.teamName);
if (event.taskId && visible) { if (event.taskId && visible) {
if (isTaskLogActivityChangeEvent(event)) { const isLogActivitySignal = isTaskLogActivityChangeEvent(event);
if (isLogActivitySignal) {
markTaskLogActivity(event.teamName, event.taskId); markTaskLogActivity(event.teamName, event.taskId);
} }
if (event.taskSignalKind === 'log') {
return;
}
const existingDetailTimer = teamRefreshTimers.get(event.teamName); const existingDetailTimer = teamRefreshTimers.get(event.teamName);
noteTeamRefreshFanout({ noteTeamRefreshFanout({
teamName: event.teamName, teamName: event.teamName,

View file

@ -1623,7 +1623,7 @@ describe('team change throttling', () => {
expect(useStore.getState().activeTaskLogActivityByTeam['my-team']).toBeUndefined(); expect(useStore.getState().activeTaskLogActivityByTeam['my-team']).toBeUndefined();
}); });
it('schedules a bounded team data refresh for visible task log signals', async () => { it('pulses visible task log activity without refreshing team data for explicit log signals', async () => {
const state = useStore.getState(); const state = useStore.getState();
const refreshTeamDataSpy = vi.spyOn(state, 'refreshTeamData'); const refreshTeamDataSpy = vi.spyOn(state, 'refreshTeamData');
@ -1638,11 +1638,13 @@ describe('team change throttling', () => {
); );
expect(refreshTeamDataSpy).not.toHaveBeenCalled(); expect(refreshTeamDataSpy).not.toHaveBeenCalled();
expect(useStore.getState().activeTaskLogActivityByTeam['my-team']).toEqual({
'task-live': true,
});
await vi.advanceTimersByTimeAsync(800); await vi.advanceTimersByTimeAsync(800);
expect(refreshTeamDataSpy).toHaveBeenCalledTimes(1); expect(refreshTeamDataSpy).not.toHaveBeenCalled();
expect(refreshTeamDataSpy).toHaveBeenCalledWith('my-team', { withDedup: true });
}); });
it('refreshes visible team data for task change freshness without pulsing live log activity', async () => { it('refreshes visible team data for task change freshness without pulsing live log activity', async () => {
@ -1667,6 +1669,30 @@ describe('team change throttling', () => {
expect(refreshTeamDataSpy).toHaveBeenCalledWith('my-team', { withDedup: true }); expect(refreshTeamDataSpy).toHaveBeenCalledWith('my-team', { withDedup: true });
}); });
it('keeps the bounded team data refresh for legacy task log change events', async () => {
const state = useStore.getState();
const refreshTeamDataSpy = vi.spyOn(state, 'refreshTeamData');
hoisted.onTeamChangeCb?.(
{},
{
type: 'task-log-change',
teamName: 'my-team',
taskId: 'task-live',
detail: 'opencode-runtime-task-event:start',
}
);
expect(useStore.getState().activeTaskLogActivityByTeam['my-team']).toEqual({
'task-live': true,
});
await vi.advanceTimersByTimeAsync(800);
expect(refreshTeamDataSpy).toHaveBeenCalledTimes(1);
expect(refreshTeamDataSpy).toHaveBeenCalledWith('my-team', { withDedup: true });
});
it('skips the bounded task log refresh if the team is hidden before execution', async () => { it('skips the bounded task log refresh if the team is hidden before execution', async () => {
const state = useStore.getState(); const state = useStore.getState();
const refreshTeamDataSpy = vi.spyOn(state, 'refreshTeamData'); const refreshTeamDataSpy = vi.spyOn(state, 'refreshTeamData');
@ -1696,6 +1722,12 @@ describe('team change throttling', () => {
it('extends task log activity pulse on repeated log signals and ignores hidden teams', async () => { it('extends task log activity pulse on repeated log signals and ignores hidden teams', async () => {
const state = useStore.getState(); const state = useStore.getState();
const refreshTeamDataSpy = vi.spyOn(state, 'refreshTeamData'); const refreshTeamDataSpy = vi.spyOn(state, 'refreshTeamData');
const activitySnapshots: Array<Record<string, true> | undefined> = [];
const unsubscribeActivitySnapshots = useStore.subscribe((nextState, prevState) => {
if (nextState.activeTaskLogActivityByTeam !== prevState.activeTaskLogActivityByTeam) {
activitySnapshots.push(nextState.activeTaskLogActivityByTeam['my-team']);
}
});
hoisted.onTeamChangeCb?.({}, { hoisted.onTeamChangeCb?.({}, {
type: 'task-log-change', type: 'task-log-change',
@ -1704,8 +1736,10 @@ describe('team change throttling', () => {
taskSignalKind: 'log', taskSignalKind: 'log',
}); });
expect(activitySnapshots).toEqual([{ 'task-live': true }]);
await vi.advanceTimersByTimeAsync(2000); await vi.advanceTimersByTimeAsync(2000);
expect(refreshTeamDataSpy).toHaveBeenCalledTimes(1); expect(refreshTeamDataSpy).not.toHaveBeenCalled();
hoisted.onTeamChangeCb?.({}, { hoisted.onTeamChangeCb?.({}, {
type: 'task-log-change', type: 'task-log-change',
@ -1714,14 +1748,17 @@ describe('team change throttling', () => {
taskSignalKind: 'log', taskSignalKind: 'log',
}); });
expect(activitySnapshots).toEqual([{ 'task-live': true }]);
await vi.advanceTimersByTimeAsync(2499); await vi.advanceTimersByTimeAsync(2499);
expect(refreshTeamDataSpy).toHaveBeenCalledTimes(2); expect(refreshTeamDataSpy).not.toHaveBeenCalled();
expect(useStore.getState().activeTaskLogActivityByTeam['my-team']).toEqual({ expect(useStore.getState().activeTaskLogActivityByTeam['my-team']).toEqual({
'task-live': true, 'task-live': true,
}); });
await vi.advanceTimersByTimeAsync(1); await vi.advanceTimersByTimeAsync(1);
expect(useStore.getState().activeTaskLogActivityByTeam['my-team']).toBeUndefined(); expect(useStore.getState().activeTaskLogActivityByTeam['my-team']).toBeUndefined();
expect(activitySnapshots).toEqual([{ 'task-live': true }, undefined]);
useStore.setState({ useStore.setState({
paneLayout: { paneLayout: {
@ -1740,7 +1777,8 @@ describe('team change throttling', () => {
expect(useStore.getState().activeTaskLogActivityByTeam['my-team']).toBeUndefined(); expect(useStore.getState().activeTaskLogActivityByTeam['my-team']).toBeUndefined();
await vi.advanceTimersByTimeAsync(800); await vi.advanceTimersByTimeAsync(800);
expect(refreshTeamDataSpy).toHaveBeenCalledTimes(2); expect(refreshTeamDataSpy).not.toHaveBeenCalled();
unsubscribeActivitySnapshots();
}); });
it('applies targeted tool resets without clearing sibling tools', async () => { it('applies targeted tool resets without clearing sibling tools', async () => {