Skip to content

F-002: refactor(app): make hook and clipboard methods non-blocking#11

Closed
Sephyi wants to merge 1 commit intodevelopmentfrom
audit/f-002-async-command-hook-clipboard
Closed

F-002: refactor(app): make hook and clipboard methods non-blocking#11
Sephyi wants to merge 1 commit intodevelopmentfrom
audit/f-002-async-command-hook-clipboard

Conversation

@Sephyi
Copy link
Copy Markdown
Owner

@Sephyi Sephyi commented Apr 22, 2026

Summary

refactor(app): make hook and clipboard methods non-blocking.

Audit context

Closes audit entry F-002 from #3.

Verification

  • cargo fmt --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test --all-targets

Note: one pre-existing test porcelain_exits_within_timeout_with_no_staged_changes is a known macOS cold-start flake that reproduces on unmodified development — unrelated to this change.

Replace synchronous std::process::Command and arboard calls with
non-blocking alternatives so the tokio runtime isn't blocked:

- handle_hook, hook_dir, hook_path, hook_install, hook_uninstall, and
  hook_status are now async; hook_dir uses tokio::process::Command with
  an explicit std::process::Output annotation (CLAUDE.md gotcha).
- copy_to_clipboard wraps arboard::Clipboard::new() and set_text() in
  tokio::task::spawn_blocking; its signature changes from &str to String
  to satisfy the 'static bound.

Closes audit entry F-002 from #3.
Copilot AI review requested due to automatic review settings April 22, 2026 19:50
@Sephyi Sephyi added the audit Codebase audit cleanup (issue #3) label Apr 22, 2026
@Sephyi Sephyi self-assigned this Apr 22, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Refactors the app’s hook and clipboard paths to avoid blocking the async runtime, addressing audit finding F-002 (sync process/clipboard calls on the tokio runtime).

Changes:

  • Make hook command handling async and run git rev-parse --git-dir via tokio::process::Command.
  • Offload clipboard writes (arboard) to tokio::task::spawn_blocking and make clipboard helper async.
  • Update command dispatch / clipboard flow to await the new async helpers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/app.rs
Ok(())
})
.await
.map_err(|e| Error::Config(format!("Clipboard task panicked: {e}")))?
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

The JoinError from spawn_blocking can represent either a panic or task cancellation (e.g., runtime shutdown). This error message always says "panicked", which can be misleading. Consider branching on e.is_panic() / e.is_cancelled() and wording the message accordingly (or avoid claiming panic unless is_panic() is true).

Suggested change
.map_err(|e| Error::Config(format!("Clipboard task panicked: {e}")))?
.map_err(|e| {
Error::Config(if e.is_panic() {
format!("Clipboard task panicked: {e}")
} else if e.is_cancelled() {
format!("Clipboard task was cancelled: {e}")
} else {
format!("Clipboard task failed to join: {e}")
})
})?

Copilot uses AI. Check for mistakes.
Comment thread src/app.rs
Comment on lines +993 to +994
// Annotated type per CLAUDE.md gotcha: tokio::process::Command output
// needs explicit std::process::Output typing when used with `?`.
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

This comment references the CLAUDE.md gotcha but says the std::process::Output annotation is needed when using ?. The documented gotcha is specifically about .ok()? (type inference failure); with .await? and subsequent output.status usage, the annotation isn't required. Consider either removing the comment or rewording it to match the actual gotcha to avoid confusing future readers.

Suggested change
// Annotated type per CLAUDE.md gotcha: tokio::process::Command output
// needs explicit std::process::Output typing when used with `?`.
// Keep the explicit type annotation for clarity; it is not required by `.await?` here.

Copilot uses AI. Check for mistakes.
@Sephyi
Copy link
Copy Markdown
Owner Author

Sephyi commented Apr 30, 2026

Closing as the conflict with the merged audit batch is too entangled for an in-place rebase: src/app.rs has been rewritten by #17 (instrument spans), #21 (clippy disallowed_methods + #[allow] shim), #26 (Ctrl+C handler refactor), and #28 (binary→lib extraction). Resolving all four against this branch's async refactor would be a near-rewrite.

A clean re-implementation should:

  1. Branch from current development (which already has the F-002 #[allow(clippy::disallowed_methods)] shim at src/app.rs:1031 ready to be removed).
  2. Switch hook_dir() to tokio::process::Command (with explicit std::process::Output type annotation per CLAUDE.md gotcha).
  3. Switch copy_to_clipboard to tokio::task::spawn_blocking (arboard is sync; not a subprocess).
  4. Make all callers .await-ed and async fn.
  5. Remove the #[allow(clippy::disallowed_methods)] shim from src/app.rs once the sync Command::new is gone.

Reopening as a fresh PR is faster than untangling this rebase.

@Sephyi Sephyi closed this Apr 30, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 30, 2026
Sephyi added a commit that referenced this pull request Apr 30, 2026
Replaces the F-018 sequencing shim at `src/app.rs:1030` with a real
async migration of the two paths the audit flagged for blocking the
Tokio runtime.

Hook path:
- `hook_dir` → `async fn`, uses `tokio::process::Command` for
  `git rev-parse --git-dir` (with the explicit `std::process::Output`
  type annotation per CLAUDE.md gotcha).
- `hook_path`, `hook_install`, `hook_uninstall`, `hook_status`,
  and `handle_hook` are all `async fn` and `.await` the chain.
- `Commands::Hook { action }` dispatch in `handle_command` now
  `.await`s `handle_hook`.

Clipboard path:
- `arboard` is a synchronous library (not a subprocess), so the
  correct fix is `tokio::task::spawn_blocking` rather than
  `tokio::process::Command`.
- `copy_to_clipboard` now takes `String` (owned, since it crosses the
  blocking-task boundary) and returns the result via `await`.
- The `--clipboard` flow in `generate_commit` calls `await` on the
  helper.

The `#[allow(clippy::disallowed_methods)]` shim added in #21 (F-018)
is removed — the lint now has nothing to suppress in this file.

Closes #11.
@Sephyi Sephyi deleted the audit/f-002-async-command-hook-clipboard branch April 30, 2026 17:09
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

audit Codebase audit cleanup (issue #3)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants