test(runner): resolve provider fixture signals through PATH (#14476)

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Its native runner tests verify exact provider shutdown and recovery.
> - Four fixture call sites invoke `/bin/kill` directly.
> - NixOS supplies `kill` through PATH, so those tests fail before
checking process state.
> - This pull request resolves `kill` through PATH, as the neighboring
runner tests already do.
> - The same stop, probe, and resume assertions can run on non-FHS
hosts.

## Linked Issues or Issue Description

**What happened?**

Four Codex provider tests fail with `No such file or directory` on
NixOS. Each failure comes from a test-owned `/bin/kill` invocation. The
executable is installed on PATH.

**Expected behavior**

Tests should probe and signal their disposable provider processes with
the installed `kill` command.

**Steps to reproduce**

On a NixOS host with `kill` on PATH and no `/bin/kill`, run the
`codex_provider` Rust integration target. Three `durable_stop_*` cases
and `structured_question_round_trips_through_the_normalized_backend`
fail. All four failures were reproduced separately on unmodified
`e9debd3eac328e6abe87ab89e9ff40ee2bb56b73`.

Related: these failures also blocked the broader local qualification of
#14474. This fixture fix has no dependency on that runtime change.

## What Changed

- Replace four test-only `/bin/kill` executable paths with `kill`.
- Retain every signal, PID/process-group operand, assertion, and unwind
cleanup.
- Match existing PATH lookup in this file and
`tests/process_supervisor.rs`.

## Verification

Rebased onto master `820c458465` (single commit `ad1fb1043`).

- `cd packages/paperclip-runner/runner && cargo test -p
paperclip-runner-core --test codex_provider --locked -j4 --
--test-threads=2`: 94 passed, 0 failed, 2 ignored (subprocess helper
entries that their parent tests run). The four tests that used
`/bin/kill` pass on a host without `/bin/kill`.
- No production code or documentation changes.
- Hosted CI: `General tests (workspaces-b)` timed out in
`packages/db/src/backup-lib.test.ts` and `e2e shard (1/8)` hit
`ERR_STREAM_UNABLE_TO_PIPE`. Neither area is touched by this test-only
diff. Master pushes currently run the "CI profile skipped" workflow, so
there is no master run of those jobs to compare against.

## Risks

Low risk. Tests require a working `kill` on their test PATH. Fixture
PIDs and arguments stay unchanged.

## Model Used

- Anthropic Claude Sonnet 5.5 (`claude-sonnet-5-5`) through Claude Code
with tool use and code execution, for the rebase and verification.
Original change: OpenAI `gpt-6-astra` through OpenCode.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
This commit is contained in:
Can H. Tartanoglu authored and GitHub committed 2026-11-07 13:02:29 -06:00
1 parent fbe689938c
commit 410aa6d1b4
1 file changed
+4 -4
@@ -5783,7 +5783,7 @@ fn durable_stop_does_not_wait_for_a_repeated_interrupt_acknowledgement() {
assert_eq!(stopped.result["providerExitConfirmed"], true);
for id in [provider_pid.to_string(), format!("-{provider_pid}")] {
assert!(
!std::process::Command::new("/bin/kill")
!std::process::Command::new("kill")
.args(["-0", "--", &id])
.output()
.unwrap()
@@ -5915,7 +5915,7 @@ fn assert_durable_stop_prepares_resumed_provider(ended_before_resume: bool) {
.expect("durably record renewed provider ownership");
let resumed_pid = resumed_event["payload"]["processId"].as_u64().unwrap();
let process_exists = |group: bool| {
std::process::Command::new("/bin/kill")
std::process::Command::new("kill")
.arg("-0")
.arg("--")
.arg(if group {
@@ -7340,7 +7340,7 @@ fn structured_question_round_trips_through_the_normalized_backend() {
#[cfg(unix)]
impl Drop for PausedTestProvider {
fn drop(&mut self) {
let _ = std::process::Command::new("/bin/kill")
let _ = std::process::Command::new("kill")
.args(["-CONT", "--", &self.0.to_string()])
.output();
}
@@ -7350,7 +7350,7 @@ fn structured_question_round_trips_through_the_normalized_backend() {
#[cfg(unix)]
let paused_provider = {
let pid = _opened.result["processId"].as_u64().unwrap();
assert!(std::process::Command::new("/bin/kill")
assert!(std::process::Command::new("kill")
.args(["-STOP", "--", &pid.to_string()])
.output()
.unwrap()