Fix setup prompt CI regressions

This commit is contained in:
Brennan Benson
2026-07-29 19:43:19 -07:00
parent d2ef4bfd6a
commit c8850b86fa
4 changed files with 101 additions and 24 deletions
@@ -21,12 +21,11 @@ import { openSetupScriptSettings } from './open-setup-script-settings'
import { trackSetupScriptPromptExposure } from './setup-script-prompt-exposure-telemetry'
import {
findSetupScriptPromptRepo,
getRenderedSetupScriptPromptState,
markSetupScriptPromptSaved,
type LastVisibleSetupScriptPrompt,
type SetupScriptPromptState
} from './setup-script-prompt-render-state'
import { useSetupScriptPromptRevalidation } from './useSetupScriptPromptRevalidation'
import { useRenderedSetupScriptPromptState } from './useRenderedSetupScriptPromptState'
import { translate } from '@/i18n/i18n'
import { getRepoHostIdentity } from '@/store/slices/repo-host-identity'
import { getRepoExecutionHostId } from '../../../../shared/execution-host'
@@ -60,7 +59,6 @@ function SetupScriptPromptCard(): React.JSX.Element | null {
const isDismissed = activeRepoHostIdentity
? isSetupScriptPromptDismissed(activeRepoHostIdentity, dismissedRepoIds)
: false
const lastVisiblePromptRef = useRef<LastVisibleSetupScriptPrompt | null>(null)
useEffect(() => {
if (!sidebarOpen || !activeRepo || !isGitRepoKind(activeRepo) || isDismissed) {
@@ -348,31 +346,25 @@ function SetupScriptPromptCard(): React.JSX.Element | null {
})
}, [activeRepo, detectedSetupDraft, promptState, saveSetupCandidate])
if (
const promptTargetHidden =
!sidebarOpen ||
!activeRepo ||
!activeRepoHostIdentity ||
!isGitRepoKind(activeRepo) ||
isDismissed
) {
lastVisiblePromptRef.current = null
return null
}
const renderedPromptState = getRenderedSetupScriptPromptState({
const renderedPromptState = useRenderedSetupScriptPromptState({
promptState,
activeRepoId: activeRepo.id,
activeRepoId: activeRepo?.id ?? null,
activeRepoHostIdentity,
lastVisiblePrompt: lastVisiblePromptRef.current
promptTargetHidden
})
if (
promptTargetHidden ||
!activeRepo ||
!renderedPromptState ||
(renderedPromptState.status === 'ok' && renderedPromptState.hasEffectiveSetup)
) {
if (renderedPromptState?.status === 'ok' && renderedPromptState.hasEffectiveSetup) {
lastVisiblePromptRef.current = null
}
return null
}
@@ -380,18 +372,9 @@ function SetupScriptPromptCard(): React.JSX.Element | null {
// retry-able card entirely — the global scope-mismatch banner explains it and
// a retry would just re-fire repo.hooksCheck on every repo focus.
if (renderedPromptState.status === 'forbidden') {
lastVisiblePromptRef.current = null
return null
}
if (
renderedPromptState.status === 'ok' &&
!renderedPromptState.hasEffectiveSetup &&
renderedPromptState.repoHostIdentity === activeRepoHostIdentity
) {
lastVisiblePromptRef.current = { state: renderedPromptState }
}
const isInspectionError = renderedPromptState.status === 'error'
const candidate = renderedPromptState.status === 'ok' ? renderedPromptState.candidate : null
const isPackageManagerSuggestion = candidate?.provider === 'package-manager'
@@ -0,0 +1,48 @@
// @vitest-environment happy-dom
import { renderHook } from '@testing-library/react'
import { describe, expect, it } from 'vitest'
import { getRepoHostIdentityForParts } from '@/store/slices/repo-host-identity'
import type { SetupScriptPromptState } from './setup-script-prompt-render-state'
import { useRenderedSetupScriptPromptState } from './useRenderedSetupScriptPromptState'
function prompt(repoId: string, hostId: string): SetupScriptPromptState {
return {
status: 'ok',
repoId,
repoHostIdentity: getRepoHostIdentityForParts(repoId, hostId),
hasEffectiveSetup: false,
hasSharedHooks: false,
candidate: null
}
}
describe('useRenderedSetupScriptPromptState', () => {
it('preserves a committed same-host prompt without leaking it to another host', () => {
const local = prompt('repo-1', 'local')
const { result, rerender } = renderHook(
({ hostIdentity, promptState }) =>
useRenderedSetupScriptPromptState({
promptState,
activeRepoId: 'repo-1',
activeRepoHostIdentity: hostIdentity,
promptTargetHidden: false
}),
{
initialProps: {
hostIdentity: local.repoHostIdentity,
promptState: local as SetupScriptPromptState | null
}
}
)
rerender({ hostIdentity: local.repoHostIdentity, promptState: null })
expect(result.current).toBe(local)
rerender({
hostIdentity: getRepoHostIdentityForParts('repo-1', 'ssh:windows'),
promptState: null
})
expect(result.current).toBeNull()
})
})
@@ -0,0 +1,45 @@
import { useEffect, useRef } from 'react'
import {
getRenderedSetupScriptPromptState,
type LastVisibleSetupScriptPrompt,
type SetupScriptPromptState
} from './setup-script-prompt-render-state'
export function useRenderedSetupScriptPromptState(input: {
promptState: SetupScriptPromptState | null
activeRepoId: string | null
activeRepoHostIdentity: string | null
promptTargetHidden: boolean
}): SetupScriptPromptState | null {
const { activeRepoHostIdentity, activeRepoId, promptState, promptTargetHidden } = input
const lastVisiblePromptRef = useRef<LastVisibleSetupScriptPrompt | null>(null)
const renderedPromptState =
!promptTargetHidden && activeRepoId && activeRepoHostIdentity
? getRenderedSetupScriptPromptState({
promptState,
activeRepoId,
activeRepoHostIdentity,
lastVisiblePrompt: lastVisiblePromptRef.current
})
: null
useEffect(() => {
if (
renderedPromptState?.status === 'ok' &&
!renderedPromptState.hasEffectiveSetup &&
renderedPromptState.repoHostIdentity === activeRepoHostIdentity
) {
lastVisiblePromptRef.current = { state: renderedPromptState }
return
}
if (
promptTargetHidden ||
renderedPromptState?.status === 'forbidden' ||
(renderedPromptState?.status === 'ok' && renderedPromptState.hasEffectiveSetup)
) {
lastVisiblePromptRef.current = null
}
}, [activeRepoHostIdentity, promptTargetHidden, renderedPromptState])
return renderedPromptState
}
@@ -141,6 +141,7 @@ describe('repo slice runtime routing', () => {
expect(store.getState().repos[0]?.displayName).toBe('SSH Renamed')
expect(reposUpdate).toHaveBeenCalledWith({
hostId: 'ssh:ssh-1',
repoId: sshRepo.id,
updates: { displayName: 'SSH Renamed' }
})