Skip to content

fix(automations): show why a hook-review-blocked automation fails, keep it running, and stop stuck-tab pile-up - #33638

Merged
nwparker merged 7 commits into
mainfrom
nwparker/agint-codex-hooks-review-blocks-unattended
Oct 27, 2026
Merged

nwparker merged 7 commits into
mainfrom
nwparker/agint-codex-hooks-review-blocks-unattended

Conversation

@nwparker

@nwparker nwparker commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 1 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​67 0 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​67
Prod 9 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​163 $\color{#cf222e}{\Huge{\mathbf{−}}}$​9 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​154

ELI5

Codex sometimes stops at startup and asks "Hooks need review". That happens when a hook in hooks.json is new or has changed, for example after another tool installs one. Only a person can answer the question. An automation has nobody watching, so every scheduled run got stuck on it and failed quietly. One user had 21 failures in about 10 hours, and each failure left another stuck tab open.

With this change:

  • You can see why it fails. The automation page shows a red "Last run failed." notice: "The agent is waiting for someone to review new or changed hooks ("Hooks need review"). Open this run and trust them, or remove them. Scheduled runs keep going and succeed once nothing needs review." The run page shows the same text above the saved terminal output. Before, the output hid it.
  • Nothing needs turning back on. The schedule stays enabled. Once you trust the hooks (in the open run tab, or in any Codex window) or remove them, the next run just works.
  • Stuck tabs stop piling up. When a new run hits the same prompt, Orca closes the earlier tabs that are still showing it. The newest one stays open so you can answer there.

Root cause (upstream Codex ff2346f33f)

Why there's no pre-launch trust check

To check before launch, Orca would have to copy Codex's layer discovery, project-trust gating and plugin content hashing on every host type: local, SSH, WSL and managed CODEX_HOMEs. The other way is to spawn codex app-server and call hooks/list before every run. Either one is too big for this fix and would go stale as Codex changes. The terminal-side signal Orca already has (agent-hooks-review-prompt) is exact and costs nothing. Using it, the schedule keeps running and stays bounded.

What changed

  • runtime-terminal-run-observer.ts: the blocked-on-hook-review error is now the actionable sentence above, not Automation agent is blocked: agent-hooks-review-prompt.
  • hook-review-blocked-run-terminals.ts (new), wired in runtime-automation-service.ts, so desktop, orca serve and paired runtimes all get it. When a run blocks on hook review, Orca closes the panes of earlier runs of the same automation that are still on the dialog. It checks this live with waitForTerminal. It skips panes a client typed into. Their run records keep the status and error, and only the terminal refs are cleared.
  • AutomationDetail shows the latest failed run's error. AutomationRunDetailsPage shows run.error above the saved output. Both use one small AutomationRunErrorNotice with the existing destructive-tint pattern. This covers every failed run, not just hook review.
  • Removed the previous auto-pause (enabled: false) from AutomationService.
  • Mobile: mobile has no automations UI, only the automation glyph on workspaces. The stuck run's tab shows up there as a normal terminal, and tab cleanup runs on the host, so mobile behaves the same. No protocol change. Old clients just see the new error text.

How verified (real Codex 0.164.0, mock Responses server, isolated HOME/CODEX_HOME under /tmp, hidden dev Orca with ORCA_DEV_USER_DATA_PATH + ORCA_BACKGROUND_LAUNCH=1)

  • I added a third-party PreToolUse hook to the isolated hooks.json and created a * * * * * Codex automation on a git project.
  • Every minute, a run failed with the new message. orca terminal list showed 1 terminal after 23 runs. Each earlier stuck tab closed when the next run blocked. The automation stayed Enabled, with the next run scheduled.
  • I chose "Trust all and continue" in the one open run tab (trusted_hash entries went from 18 to 19). That run finished, and the next scheduled run completed on its own. I did not re-enable anything.
  • service.test.ts / runtime-terminal-run-observer.test.ts / the new hook-review-blocked-run-terminals.test.ts, the renderer automation tests, pnpm tc and pnpm run check:code-quality:changed all pass.

Visual proof

Before (main, from the earlier revision of this PR): each minute added another failed run and another tab stuck on the dialog.

Before: four tabs, each stuck on Hooks need review

After: the automation stays Enabled and says why it fails:

After: automation detail with Last run failed notice

The run page shows the error above the saved Codex output:

After: run details with error above snapshot

Only the newest stuck tab is open (run 23):

After: a single stuck tab after 23 runs

After I trusted the hook in that tab, the next scheduled runs completed without anyone re-enabling the automation:

After: runs resume automatically

Risk / trade-offs

  • While the hook is untrusted, the schedule still launches Codex once per occurrence, and each occurrence records a failed run. Before this change, those failures were silent and each left a tab open. Now they are visible, and only one tab stays open.
  • A pane is closed only if it is still on the hook dialog right now and nobody typed into it. If you open /hooks (Review hooks) in it, it stays open.
  • There is no OS notification. Automations have no failure notifications yet; that is [Feature]: Alert on failed automation runs, and let the run's agent decide to raise an alert #27148.
  • Orca never trusts, enables or skips third-party or project hooks for the user.

Fixes #28252
Fixes #23289

Codex opens a blocking 'Hooks need review' dialog when a hook in hooks.json is
new or changed. Only a person can answer it, so every later scheduled run of
an automation hit the same dialog and failed. Pause the schedule on the first
such failure and say how to recover. Also re-enable Orca's own Codex hook on
SSH installs, matching local installs.

Refs #28252 #23289
@nwparker
nwparker marked this pull request as draft October 25, 2026 21:21
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Walkthrough

Hook-review blocks now receive a dedicated error message. When completion observation detects that error, the runtime starts cleanup for eligible earlier runs. Automation views show dispatch-failure errors through a shared notice. Generated Codex remote-install trust entries now set enabled to true. The runOwnHandle runtime parameter is narrowed to the methods it uses.

Priority: Normal

Severity of issue fixed: Medium

Merge Risk: Low · up to d1158

A user could open an earlier blocked run while cleanup is waiting, only for its terminal to close. Recheck use and ownership before closing; the remaining risk is bounded.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Linked Issues check Warning The PR provides an actionable hook-review error, renders it on automation and run pages, and closes earlier unused panes. It does not meet all coding requirements in [#28252]. Runs still dispatch on e… For [#28252], add the required blocked state and one-time notification, and prevent or coalesce dispatch while the known hook-review block remains. For [#23289], require enabled state in the managed-grant fast path, set and verify `enabled:…
Passed Passed checks: 4 passed
Check name Status Explanation
Title check Passed The title clearly summarizes the primary changes: reporting hook-review failures, keeping automations enabled, and preventing stuck-terminal accumulation.
Description check Passed The description is detailed and covers the user impact, root cause, implementation, linked issues, visual proof, testing, risks, and trade-offs. It is mostly complete despite not reproducing every tem…
Out of Scope Changes check Passed The changed error message, terminal cleanup, UI notices, tests, type narrowing, and localization support the hook-review automation behavior. The SSH trust-entry change supports the managed-hook requi…
Docstring Coverage Passed Docstring coverage is inconclusive: files from earlier reviews still need to be rechecked within the check's analysis limits. Not blocking.
Full details: Description check

Explanation

The description is detailed and covers the user impact, root cause, implementation, linked issues, visual proof, testing, risks, and trade-offs. It is mostly complete despite not reproducing every template checklist section.

Full details: Out of Scope Changes check

Explanation

The changed error message, terminal cleanup, UI notices, tests, type narrowing, and localization support the hook-review automation behavior. The SSH trust-entry change supports the managed-hook requirement in [#23289]. No unrelated change is demonstrated.

Full details: Linked Issues check

Explanation

The PR provides an actionable hook-review error, renders it on automation and run pages, and closes earlier unused panes. It does not meet all coding requirements in [#28252]. Runs still dispatch on each schedule, remain dispatch_failed, and have no distinct blocked state or one-time notification. For [#23289], the SSH installer writes enabled: true for Orca-managed entries, but the PR does not establish the required enabled-state checks and verification across the ledger, RPC, and fallback launch paths.

Resolution

For [#28252], add the required blocked state and one-time notification, and prevent or coalesce dispatch while the known hook-review block remains. For [#23289], require enabled state in the managed-grant fast path, set and verify enabled: true in the RPC path, verify the fallback result, and return precise managed-hook status when enablement fails.

Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: c501b421-2215-480b-be42-827818f78cbe
Commits

Reviewing files that changed from the base of the PR and between e26e418 and f99ad39.

Files selected for processing: 4
  • src/main/automations/runtime-terminal-run-observer.ts
  • src/main/automations/service.test.ts
  • src/main/automations/service.ts
  • src/main/codex/codex-hook-remote-install.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 6 remain after this review.

Comment thread src/main/automations/service.ts Outdated
Comment on lines +221 to +224
/** Only a person can answer a hook review; later occurrences would block on the same prompt. */
private async pauseScheduleBlockedOnHookReview(
result: AutomationDispatchResult
): Promise<AutomationDispatchResult> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Major · Maintainability & Code Quality · Heavy lift

Reduce service.ts below the enforced line limit.

The supplied pipeline reports that the added service code exceeds the file’s line limit. Both static analysis and the format check fail. Extract the new pause logic or otherwise reduce the file before merging.

Source: Pipeline failures

Comment thread src/main/automations/service.ts Outdated
Comment on lines +235 to +240
try {
this.store.updateAutomation(automation.id, { enabled: false })
await this.store.flushPendingOrThrowAsync({ drainToStableGeneration: false })
} catch (error) {
console.warn('[automations] could not pause an automation blocked on hook review:', error)
return result

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Data Integrity & Integration · Quick win

🔎 Supported by static analysis

Script executed:

#!/bin/bash
# Inspect the Store update and flush contracts and their failure handling.
set -euo pipefail
rg -n -C 8 'updateAutomation\(|flushPendingOrThrowAsync\(' src/main/persistence src/main/runtime src/main/automations

Repository: stablyai/orca

Length of output: 45667


Script executed:

set -u
echo "== service pause block =="
sed -n '200,262p' src/main/automations/service.ts
echo "== Store definitions (non-test) =="
rg -n -F --glob '!*.test.ts' -e 'updateAutomation(' -e 'flushPendingOrThrowAsync(' src/main/persistence src/main/automations || test "$?" -eq 1
echo "== rollback handling in persistence (non-test) =="
rg -n -F --glob '!*.test.ts' -e 'rollback' src/main/persistence || test "$?" -eq 1

Repository: stablyai/orca

Length of output: 41547


Script executed:

set -u
echo "== automation-persistence updateAutomation =="
sed -n '140,200p' src/main/persistence/loading-store/automation-persistence.ts
echo "== automation-definition-operations updateAutomation =="
sed -n '120,175p' src/main/persistence/scheduling-automations/automation-definition-operations.ts
echo "== flushPendingOrThrowAsync =="
sed -n '110,150p' src/main/persistence/loading-store/write-flush-barriers.ts
echo "== primary-state-writes mutation/rollback =="
sed -n '70,135p' src/main/persistence/loading-store/primary-state-writes.ts

Repository: stablyai/orca

Length of output: 8214


Publish the pause after a failed flush instead of returning the blocked error.

updateAutomation sets enabled: false in memory before the flush. When the flush rejects, the catch returns the original blocked result. That result has no paused message or recovery steps, and no definition-change event is published, so clients keep the stale enabled state. The in-memory pause still stops later dispatches, but a restart can reload the durable enabled: true and retry the blocked prompt.

The rollback in primary-state-writes.ts runs only for durable mutations. updateAutomation does not run through that path, so the code shows no rollback of the in-memory pause. Keep the in-memory pause and publish the change in both outcomes. Log the failed persistence and return the paused result:

🐛 Suggested fix
--- "a/src/main/automations/service.ts"
+++ "b/src/main/automations/service.ts"
@@ -232,13 +232,12 @@
     if (run?.trigger !== 'scheduled' || !automation?.enabled) {
       return result
     }
     try {
       this.store.updateAutomation(automation.id, { enabled: false })
       await this.store.flushPendingOrThrowAsync({ drainToStableGeneration: false })
     } catch (error) {
-      console.warn('[automations] could not pause an automation blocked on hook review:', error)
-      return result
+      console.warn('[automations] could not persist the hook-review pause:', error)
     }
     const selector = this.store.automationChangeSelector(automation.id)
     this.publish?.({ reason: 'definition', ...(selector ? { selector } : {}) })
     return { ...result, error: AGENT_HOOKS_REVIEW_PAUSED_ERROR }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
this.store.updateAutomation(automation.id, { enabled: false })
await this.store.flushPendingOrThrowAsync({ drainToStableGeneration: false })
} catch (error) {
console.warn('[automations] could not pause an automation blocked on hook review:', error)
return result
try {
this.store.updateAutomation(automation.id, { enabled: false })
await this.store.flushPendingOrThrowAsync({ drainToStableGeneration: false })
} catch (error) {
console.warn('[automations] could not persist the hook-review pause:', error)

Source: Learnings

… why it fails

Replace the auto-pause with a visible, self-clearing failure: the run error
explains the Codex hook review, the automation and run pages show it above
saved output, and earlier tabs stuck on the same prompt close so only the
newest stays open to answer. Runs succeed again on their own once the hooks
are trusted or removed.
@nwparker nwparker changed the title fix(automations): pause a schedule whose agent is stuck on a hook review fix(automations): show why a hook-review-blocked automation fails, keep it running, and stop stuck-tab pile-up Oct 27, 2026
nwparker and others added 2 commits October 26, 2026 22:51
…that are provably unused

- Cleanup resolves the pane through runOwnHandle, so a cold-restored pane holding a new process is left alone.
- Close only on definite 'unused' input history, matching headless run-terminal retention.
- Catch failures from the fire-and-forget cleanup call.
- Show the run page error notice only for dispatch_failed runs, and add the missing en catalog keys.
@nwparker
nwparker marked this pull request as ready for review October 27, 2026 06:03

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


Review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Repository UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 04af2b05-f94c-45cb-8c9c-cc889b2ebda4
Commits

Reviewing files that changed from the base of the PR and between f99ad39 and d115845.

Files selected for processing: 9
  • src/main/automations/headless-automation-agent-launch.ts
  • src/main/automations/hook-review-blocked-run-terminals.test.ts
  • src/main/automations/hook-review-blocked-run-terminals.ts
  • src/main/automations/runtime-automation-service.ts
  • src/main/automations/runtime-terminal-run-observer.ts
  • src/renderer/src/components/automations/AutomationDetail.tsx
  • src/renderer/src/components/automations/AutomationRunDetailsPage.tsx
  • src/renderer/src/components/automations/automation-page-parts.tsx
  • src/renderer/src/i18n/locales/en.json

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.

Comment on lines +54 to +55
runtime.readTerminalClientUse(run.terminalPtyId) !== 'unused' ||
!(await isStillOnHookReview(runtime, handle))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor · Stability & Availability · Quick win

Recheck client use before closing the terminal.

If a client opens an earlier run while waitForTerminal is pending, Line 54 can record unused before the client subscribes or types. The helper then closes a terminal that the client is using. The runtime treats a subscriber or user input as used. (raw.githubusercontent.com) Recheck client use and PTY ownership after the await, immediately before closeTerminal.

@nwparker
nwparker merged commit b29ae01 into main Oct 27, 2026
40 checks passed
@nwparker
nwparker deleted the nwparker/agint-codex-hooks-review-blocks-unattended branch October 27, 2026 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant