Skip to content

REPL Ctrl-C during a host operation kills the process and strands a dead-pid operation lock on every host #441

Description

@plusky

Found while fixing #405 (PR #440); verified against the code at 49ad1bed.

Summary

A Ctrl-C in the REPL during a long host operation kills the mtui process outright, and the operation lock (/var/lock/mtui.lock) it had taken on every host stays behind with a dead pid — so unlike #405's in-process strand, the debris reads as foreign (is_mine() requires user AND pid to match) and neither a later mtui run nor session teardown can release it non-forced. It blocks every other tester on those hosts until someone runs unlock --force or the 24h stale reap (lock_reap_stale/lock_stale_age, locks.rs:381-404) fires on their next claim attempt.

Mechanism

  • reedline observes Signal::CtrlC only during read_line (crates/mtui-cli/src/repl.rs:189-195 — "abort the current input and reprompt"); dispatch runs after read_line returns, awaited inline.
  • Nothing installs a process-level SIGINT handler. crates/mtui-core/src/commands/request_review.rs:223-225 documents this explicitly: "Nothing in the CLI installs a SIGINT handler today, so without this a Ctrl-C during a watch kills the process outright" — its sleep_or_interrupt is a bespoke, one-command mitigation, as is regenerate.rs:136's.
  • So the default SIGINT disposition terminates the process mid-update/install/prepare/run, between the flow's lock and its "unlock always" section. There is no Drop-based release and cannot be (the release is an SSH round-trip; Drop cannot await — the same reasoning as provider.rs:355-357 for pool claims).

The cooperative-cancellation seam (Session's CancellationToken, checked at the flows' step boundaries) already exists and is exactly what a SIGINT handler should trigger — today the REPL never cancels it; only MCP job_cancel does.

Suggested direction

Install a SIGINT handler for the duration of a dispatched command (restore reedline's behaviour between commands): first Ctrl-C cancels the session token — the flows unwind at their checkpoints and run their own unlock discipline, exactly like a cooperative MCP job_cancel; a second Ctrl-C (or a short deadline) may hard-exit, accepting the stranded-lock outcome the first stage exists to avoid. request_review's and regenerate's bespoke hooks could then fold into the general mechanism.

Acceptance

  • Ctrl-C during update/prepare/install/run stops the flow at a checkpoint, runs the flow's unlock path, and returns to the prompt with a truthful "cancelled" report
  • A test drives the cooperative path end-to-end (the seam + checkpoints already exist and are tested; the new part is the signal wiring)
  • Double-Ctrl-C (or timeout) behaviour documented; the stranded-lock consequence named in the docs if hard-exit is kept
  • request_review/regenerate bespoke hooks reconciled with the general handler

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions