Skip to content

Silently skip hook-event when Crow app is not running#234

Merged
dhilgaertner merged 1 commit intomainfrom
feature/crow-227-stop-hook-conn-refused
May 2, 2026
Merged

Silently skip hook-event when Crow app is not running#234
dhilgaertner merged 1 commit intomainfrom
feature/crow-227-stop-hook-conn-refused

Conversation

@dhilgaertner
Copy link
Copy Markdown
Contributor

Summary

  • crow hook-event now silently exits 0 when the Crow app isn't running, instead of failing with "Socket connection failed: Connection refused".
  • Catch is scoped to SocketError.connectionFailed inside HookEventCmd only — other CLI commands (new-session, set-status, etc.) still fail loudly when the socket is unavailable, since those are user-initiated.
  • Other socket errors (timeout, write/read failures) and JSON-RPC errors continue to propagate so genuine misbehavior stays visible.

Closes #227

Test plan

  • make build succeeds.
  • swift test --package-path Packages/CrowCLI — 35 tests pass, including new forwardHookEventSilentWhenAppNotRunning.
  • Repro: echo '{}' | CROW_SOCKET=/tmp/missing.sock .build/debug/crow hook-event --session $(uuidgen) --event Stop → no output, exit 0.
  • Sanity: CROW_SOCKET=/tmp/missing.sock .build/debug/crow list-sessions still prints "Socket connection failed: …" and exits 1.
  • Happy path with live app: echo '{...}' | .build/debug/crow hook-event --session <real-uuid> --event Stop → exit 0, event reaches the app.

🤖 Generated with Claude Code

When the Crow app is not running, `crow hook-event` previously exited
non-zero with "Socket connection failed: Connection refused", causing
Claude Code to surface a noisy "Stop hook error" on every session exit.

Hook events are fire-and-forget — a missing listener is an expected
state, not an error. Catch SocketError.connectionFailed in the
hook-event command only, so other CLI commands still fail loudly when
the app is unavailable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@dgershman dgershman left a comment

Choose a reason for hiding this comment

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

Code & Security Review

Critical Issues

None.

Security Review

Strengths:

  • Error suppression is scoped strictly to SocketError.connectionFailed within forwardHookEvent only — other CLI commands retain their fail-loud behavior for user-initiated operations.
  • No new stdin/stdout paths that could leak data; hook payload parsing already existed and the security posture is unchanged.
  • The CROW_SOCKET env-var override is existing behavior (legacy support) — not introduced here.

Concerns:

  • None identified. The fire-and-forget semantics are appropriate for hook events where the app being absent is a normal condition, not a security boundary.

Code Quality

  • Clean extraction: the forwardHookEvent function has a single responsibility and is independently testable.
  • The catch SocketError.connectionFailed pattern correctly matches all associated errno values (both ECONNREFUSED and ENOENT), covering both "app not running" and "socket file absent" scenarios.
  • Documentation accurately describes the contract (which errors propagate vs. which are swallowed).
  • Test uses setenv/unsetenv which modifies process-global state — acceptable here given test speed, but worth noting for future test growth. A defer { unsetenv(...) } correctly restores state.
  • All 6 HookEvent tests pass; build is clean with no warnings in the changed files.

Summary Table

Priority Issue
Green Consider: if concurrent Swift Testing adoption grows, the setenv-based test could benefit from process isolation, but not blocking today

Recommendation: Approve — well-scoped fix for #227 with appropriate error handling granularity and good test coverage.

Copy link
Copy Markdown
Collaborator

@dgershman dgershman left a comment

Choose a reason for hiding this comment

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

Code & Security Review

Critical Issues

None.

Security Review

Strengths:

  • Error handling is scoped narrowly to SocketError.connectionFailed inside forwardHookEvent only — other CLI commands continue to fail loudly when the app is unavailable, preserving the correct behavior for user-initiated operations.
  • No new attack surface: no new inputs, no new IPC methods, no privilege changes.
  • The CROW_SOCKET env var override in the test is properly cleaned up via defer { unsetenv(...) }.

Concerns:

  • None identified. The change correctly distinguishes between expected (app not running → connection failed) and unexpected (timeouts, write/read failures, JSON-RPC errors) socket conditions.

Code Quality

  • Clean extraction: The RPC logic was pulled out of HookEventCmd.run() into a standalone forwardHookEvent() function, making it independently testable — good design.
  • Catch pattern correctness: catch SocketError.connectionFailed matches all connectionFailed(Int32) variants regardless of errno, which is the right behavior — both ECONNREFUSED (app not listening) and ENOENT (socket file absent) should be silently swallowed.
  • Test coverage: forwardHookEventSilentWhenAppNotRunning is a clean regression test — points at a non-existent socket via CROW_SOCKET, verifies the function completes without throwing.
  • Doc comment on forwardHookEvent clearly explains the rationale and the error taxonomy (which errors propagate vs. which are swallowed).

Summary Table

Priority Issue
✅ Green Consider whether SocketError.createFailed should also be caught — it would fire if the OS can't allocate a socket fd, which is unlikely but also not an app-connectivity issue. Current behavior (letting it propagate) is defensible since it signals a system-level problem.

Recommendation: Approve — this is a well-scoped, well-tested fix for a real usability issue (#227). The error handling taxonomy is correct and the blast radius is minimal.

@dhilgaertner dhilgaertner merged commit fca1e72 into main May 2, 2026
3 checks passed
@dhilgaertner dhilgaertner deleted the feature/crow-227-stop-hook-conn-refused branch May 2, 2026 02:55
dhilgaertner added a commit that referenced this pull request May 4, 2026
…xes (#238)

Closes #235

## Summary

- Adds `docs/automation.md` as the canonical guide to Settings →
Automation and the full auto-flow lifecycle.
- Updates `docs/architecture.md` with the dual Ghostty/tmux backend
(#229), the new `TerminalRouter` dispatch, the Settings tab split
(#228), and the Review Board surface (#188, #205, #207, #210, #212,
#220, #226, #231).
- Adds `crow rename-terminal` (#206) to `docs/cli-reference.md`.
- Adds troubleshooting rows for tmux backend missing, Ghostty surface
retry (#218), GitLab nested groups (#233), `GITLAB_HOST` silent skip
(#215), auto-respond not firing, and the silent no-op `hook-event`
behavior (#234).
- Adds `CROW_TMUX_BACKEND` and `CROW_HOOK_DEBUG` to the
`docs/configuration.md` env-var table.
- Backfills `CHANGELOG.md` `[Unreleased]` with every PR from #137
through #234, grouped by theme.
- Updates `README.md` features list and docs index for the new
automation suite, review board, terminal renaming, and tmux opt-in.
- Appends an "Implementation Status (2026-05)" footer to
`docs/terminal-runtime-research.md` noting #229 shipped the headless-PTY
backend recommended in the original research.

The audit checklist called out as deliverable #1 in the issue is posted
as a [comment on
#235](#235 (comment)).

## Test plan

- [ ] `git diff --stat main` shows only the listed `docs/`,
`CHANGELOG.md`, and `README.md` files
- [ ] Render each modified doc on GitHub and confirm anchors /
cross-links resolve
- [ ] Confirm `crow --help` matches the command list in
`docs/cli-reference.md` (only `rename-terminal` was missing pre-PR)
- [ ] Walk every PR number in the CHANGELOG against `git log
--since=2026-04-15 main --oneline` and confirm each one resolves
- [ ] Re-export `docs/crow-screenshot.jpeg` against the current
Settings/Review-Board UI — **deferred to a follow-up**, called out in
the audit comment

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Stop hook fails with connection refused when Crow app is not running

2 participants