ho-04.13 — run signal ownership
Responds to a finding in ho-04.12 (; ho-04.12 is closed).
D2 of that proposed that sharibako run stop forwarding SIGINT, on the
premise that the child already receives a terminal Ctrl-C from the kernel
through a shared process group. The signed-install dogfood disproved the
premise and D2 was reverted. This ho decides, from scratch, who owns the
child's signals.
The finding. Foundation's Process spawns the child in its own process
group, off the terminal's foreground process group. A terminal Ctrl-C is
delivered by the kernel only to the foreground group — i.e. to the sharibako
wrapper, not to the child. A child that traps SIGINT under sharibako run
never saw one until the wrapper forwarded it. So today (post-04.12 revert) the
wrapper's forward is the child's only path to a terminal signal.
Current shipped behavior (the 04.12 revert, working but not necessarily
ideal): the wrapper ignores SIGINT/SIGTERM/SIGHUP at the process level,
observes each via DispatchSourceSignal, forwards it to the child by PID
through the ChildController seam, starts a 1-second-tick SIGKILL countdown,
and escalates immediately if a second signal arrives. Liveness is checked
through the Process object (no recycled-PID aliasing). This is correct and
non-regressing; the child receives exactly one SIGINT.
In scope:
- The signal-ownership model for
run: keep forwarding, or move the child into its own process group and it the terminal foreground (real job control) so the kernel delivers terminal signals to the child directly. - Which signals the wrapper forwards (revisit SIGQUIT, dropped in the 04.12 revert), and whether the "double-SIGINT hard-abort" concern D2 raised is real at all given the child is not in the terminal's foreground group.
- Whether
SignalForwarder's DispatchSource/timer plumbing changes under the chosen model (and re-check its coverage exclusion accordingly).
Out of scope:
- The temp-key scrub-on-signal guard (D1, shipped and dogfood-verified in 04.12).
- Escalation-on-repeat and
Process-seam liveness (D3/D4, shipped and kept). - Anything Keychain (D5, fixed in 04.12).
Phase 1 —
Not yet ratified. Open questions for the thinking session:
-
Process group + foreground, or keep forwarding? Moving the child to its own process group and calling
tcsetpgrpto give it the controlling terminal's foreground would make the kernel deliver Ctrl-C straight to the child — the Unix-standard model a shell uses. But it pullsruninto job-control territory (foreground/background handoff, SIGTTOU/SIGTTIN, restoring the wrapper's foreground on exit) that ho-04.12 D2 explicitly wanted a secrets wrapper to avoid. Forwarding is simpler and already works. What does a secrets-injection wrapper actually owe here — transparent signal pass-through, or true terminal ownership? -
Is the double-signal risk real? D2's original motivation was that npm-class tools treat a second SIGINT as a hard abort. If the child is not in the foreground group, it never gets the kernel's copy, so forwarding one is one signal, not two. Does the concern survive the finding, or was it an artifact of the false premise?
-
Signal set. With forwarding retained, does SIGQUIT belong in the forwarded set? What about SIGHUP semantics for a wrapped long-running child?
-
Testing. The forwarder policy is already seam-testable via
ChildController. If the model changes to process-group ownership, what is the injectable seam fortcsetpgrp/setpgid, and what stays dogfood-only?
Phase 2 — Execute
Pending Think ratification.
Phase 3 —
Pending execution.
Authored: 2026-07-06 as the forward-only response to ho-04.12's D2 dogfood finding. Think not yet ratified.
Rendered from the corpus, verbatim · source on GitHub →