kinjo 0.3.8

Kinjo: mDNS TUI and commands launch for local network services
Documentation
# TUI crash-capture toolkit

Reusable diagnostics for hunting a crash or signal death in the `tui-smoke`
job. None of it runs by default — the smoke test is green on tmux ≥ 3.5 (see
`docs/tui-smoke-crash-handover.md`). Wire it in only when a new flake appears.

## What each piece does

- **`run-fg.py`** — a foreground-preserving launcher. It becomes the tmux pane
  process, forks the real program into the tty foreground (so a TUI's reads do
  not raise `SIGTTIN`), and records how *both* the child **and** the pane process
  terminated. That distinction is what separates "the app crashed" from "the app
  exited cleanly and tmux signalled the pane during teardown".
- **`enable-core-dumps.sh`** — rewrites `kernel.core_pattern` to plain files and
  primes the tmux server with `ulimit -c unlimited` so pane processes it spawns
  dump core. Run it *before* the smoke test (the server inherits the limit at
  spawn time). CI-only: needs passwordless sudo.
- **`report-crash.sh`** — turns any captured cores into gdb backtraces, and dumps
  `coredumpctl` / `dmesg` / journal fault lines and the app's stderr. Run as an
  `if: failure()` step; never fails the job.

## Wiring it into `tui-smoke` (temporary, for an investigation)

```yaml
    - name: Enable core dumps
      run: scripts/ci/enable-core-dumps.sh
    - name: Smoke-test …
      env:
        KINJO_WRAP: "scripts/ci/run-fg.py /tmp/cores/kinjo-rc.txt --"
      run: scripts/smoke-tui.sh
    - name: Report crash
      if: failure()
      run: scripts/ci/report-crash.sh
```

`KINJO_WRAP` is honoured by `scripts/drive-tui.sh`: it is prepended verbatim
before the binary, so the launcher records the terminating signal. Note that
adding pane-signal handlers via `run-fg.py` can shift timing enough to mask a
delicate race — if the crash vanishes under the wrapper, that itself is a clue.