Skip to content

[Bug]: Setup-script prompt remains visible when orca.yaml setup hook is configured and running #8752

Description

@ruigomeseu

Operating system

Linux

Orca version

1.4.139

Details

Short summary

The “Add a setup script” card can remain visible even though the repository has a valid orca.yaml setup hook and Orca successfully runs that hook when creating worktrees.

What happened?

The repository’s primary checkout contains:

scripts:
  setup: ./scripts/setup-orca-worktree.sh
  archive: ./scripts/teardown-orca-worktree.sh

Orca correctly parses the file, and a direct repo.hooksCheck call returns:

hasHooks: true
hooks.scripts.setup: ./scripts/setup-orca-worktree.sh

The setup hook also ran successfully for the affected worktree: Orca created the Setup terminal, installed dependencies, and launched the configured development server.

Despite that, the sidebar continued to display:

Add a setup script
Add a setup command to run when Orca creates new worktrees.

Expected behavior

Once an effective setup hook is detected in orca.yaml, the setup-script prompt should disappear and remain hidden.

Reproduction scenario

  1. Open a Git repository in a long-lived Orca desktop or web client.
  2. Allow the setup-script prompt to inspect the repository while no setup hook is detected.
  3. Add or update orca.yaml with a valid scripts.setup entry, or reconnect/restart the runtime after the hook becomes available.
  4. Activate or create another worktree from the same repository.
  5. Observe that the setup hook runs, but the old “Add a setup script” prompt can remain visible.

A full client remount or sidebar close/reopen triggers another inspection and can clear the stale prompt.

Code-level diagnosis

The setup detection logic itself correctly accepts shared orca.yaml hooks:

export function hasEffectiveSetupCommand(repo: Repo, hooksResult: HookCheckResult): boolean {
const localSetup = repo.hookSettings?.scripts?.setup?.trim()
const sharedSetup = hooksResult.hooks?.scripts?.setup?.trim()
const rawPolicy = repo.hookSettings?.commandSourcePolicy
const sourcePolicy = resolveHookCommandSourcePolicy(rawPolicy, {
hasLocalScript: Boolean(localSetup)
})
if (sourcePolicy === 'local-only') {
return Boolean(localSetup)
}
if (sourcePolicy === 'run-both') {
return Boolean(sharedSetup || localSetup)
}
return Boolean(sharedSetup)

However, SetupScriptPromptCard only reruns its inspection when these React dependencies change:

  • activeRepo
  • inspectionRetryKey
  • isDismissed
  • settings
  • sidebarOpen

It does not invalidate the inspection in response to:

  • orca.yaml filesystem changes
  • runtime reconnects or runtime generation changes
  • activating another worktree belonging to the same repository

useEffect(() => {
if (!sidebarOpen || !activeRepo || !isGitRepoKind(activeRepo) || isDismissed) {
setPromptState(null)
setDetectedSetupDraft('')
return
}
const repo = activeRepo
let cancelled = false
setPromptState(null)
async function inspectRepoSetup(): Promise<void> {
const nextState = await inspectSetupScriptPromptState({
repo,
checkHooks: () => checkRuntimeHooks(settings, repo.id),
inspectImports: () => inspectRuntimeSetupScriptImports(settings, repo.id)
})
if (!cancelled) {
setPromptState(nextState)
setDetectedSetupDraft(
nextState.status === 'ok' && nextState.candidate?.provider === 'package-manager'
? nextState.candidate.setup
: ''
)
}
}
void inspectRepoSetup()
return () => {
cancelled = true
}
}, [activeRepo, inspectionRetryKey, isDismissed, settings, sidebarOpen])

The renderer also retains the last visible prompt while another same-project inspection is pending, which can preserve the stale negative result:

export function getRenderedSetupScriptPromptState(input: {
promptState: SetupScriptPromptState | null
activeRepoId: string
activeProjectId: string | null
lastVisiblePrompt: LastVisibleSetupScriptPrompt | null
}): SetupScriptPromptState | null {
const { activeProjectId, activeRepoId, lastVisiblePrompt, promptState } = input
if (promptState?.repoId === activeRepoId) {
return promptState
}
return !promptState && lastVisiblePrompt?.projectId === activeProjectId
? lastVisiblePrompt.state
: null

Both the runtime RPC and Electron IPC hook readers correctly parse the current file, so this appears to be renderer invalidation rather than hook execution or YAML parsing.

Suggested fix

Invalidate and rerun setup-script inspection when:

  • the primary checkout’s orca.yaml changes;
  • the active runtime reconnects or changes generation; or
  • a same-repository worktree becomes active.

The retained last-visible state should also not continue showing a known-stale negative result across those invalidation boundaries.

Suggested regression coverage

  1. Start with no effective setup hook and confirm the prompt is visible.
  2. Make an orca.yaml setup hook effective, or simulate a runtime reconnect returning one.
  3. Emit the corresponding invalidation event.
  4. Confirm the prompt rechecks the repository and disappears.
  5. Confirm switching between worktrees of the same repository does not retain an obsolete prompt.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Normal priority: nice-to-have or lower urgencyos:linuxIssues affecting Linux

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions