Files
Kigi-CLI/crates/codegen/kigi-crash-handler
ZacharyZhang-NY 6f31415ed6 §9 acceptance: grep-zero sweep — every internal x.ai/grok identifier renamed
The PRD's first acceptance gate now holds: grep -RinE '\bx\.ai\b|grok'
crates/ --include='*.rs' → 0 matches (exempt: NOTICE and third-party
license archives, README provenance, and the required 'Based on Grok
Build Open Source' attribution, now sourced from version_attribution.txt).

Wire-visible renames (both sides in this repo, changed in lockstep):
- Auth method id 'grok.com' → 'kimi-code' (AuthMethodKind::KimiCode).
- Every x.ai/* and _x.ai/* ACP ext method and meta key → kigi/* /
  _kigi/* (~200 names; grokShell → kigiShell). Session-file replay keeps
  a read-side alias for the legacy '_x.ai/session/update' method so
  existing updates.jsonl histories load; writes emit only the new name
  (both directions test-pinned).
- Agent types grok-build* → kigi* with a documented legacy-prefix alias
  at resolution time so persisted sessions keep resolving.
- ToolNamespace/BuiltinAgentName GrokBuild* → Kigi* (wire snake_case
  kigi/kigi_concise/kigi_hashline; schema regenerated); grok_build
  implementation dirs renamed to kigi*.
- x-grok-* headers → x-kigi-*, __GROK_* sentinels → __KIGI_*, themes
  grokday/groknight → kigiday/kiginight (old persisted values fall back
  to the default theme), web_fetch allowlist xAI hosts → kimi.com +
  moonshot platforms, changelog CDN → this repo, grok-build changelog
  archives deleted.
- BYOK default endpoint removed: [endpoints] api_base_url is now truly
  optional with NO default — consumers fail fast with the flag name when
  unset (no silent x.ai egress). Mock harnesses inject it explicitly.
- System-prompt identity fixed: 'released by xAI' → 'an unofficial
  community CLI for Kimi' (template + regenerated encrypted form).

Also repaired pre-existing grok-era test debt found by the sweep: the
stale trace_classify default-model pin, the grok-pager UA label test,
pty-harness stale-binary reuse and non-hermetic moonshot routing (a PTY
test could previously reach the real api.moonshot.cn), and the outdated
oauth fixture scope key.

Gates: §9 grep 0; fmt clean; workspace check/clippy 0/0 (-D warnings);
FULL cargo test --workspace: 234 suites, 21,961 passed, 0 failed;
deny advisories ok.
2026-07-18 02:48:46 -04:00
..

kigi-crash-handler

Crash handler for SIGBUS/SIGSEGV with best-effort backtrace capture.

How it works

install() registers a sigaction handler. On crash it writes a binary blob (GCRX format) to crash_dir/last-crash.bin and restores the terminal via pre-computed escape sequences. The handler uses only async-signal-safe operations for file I/O, terminal restore, and re-raise.

On next launch, check_previous_crash() reads the blob, resolves IPs to symbols via backtrace, writes last-crash-report.txt, and archives it (keeping the last 5 reports).

No-ops on non-unix platforms. On musl-based Linux (release builds), the handler still records signal/address/version but skips frame capture since musl does not provide backtrace().

Limitations

Frame capture is best-effort

Frame capture uses two fully async-signal-safe techniques:

  1. The crash instruction pointer is extracted directly from the ucontext_t passed by the kernel.
  2. Additional frames are captured by walking the frame-pointer chain (RBP on x86_64, x29 on aarch64) with raw pointer reads.

In release builds without -C force-frame-pointers, the frame-pointer chain may be incomplete or empty (the compiler omits frame pointers by default for optimization). The crash PC is always captured. In debug/dev builds, frame pointers are retained by default, producing fuller call stacks.

sigaltstack is per-thread

The alternate signal stack is installed only on the thread that calls install(). Tokio worker threads do not inherit it. Stack overflows on worker threads will still trigger the handler (sigaction is process-wide), but without altstack protection the handler itself may fault on the overflowed stack.

Usage

use std::path::PathBuf;

let crash_dir = PathBuf::from("/home/user/.myapp/crash");

// check_previous_crash MUST be called before install(), because
// install() opens last-crash.bin with O_TRUNC.
if let Some(r) = kigi_crash_handler::check_previous_crash(&crash_dir) {
    eprintln!("Crashed last session: {}", r.signal_name);
    eprintln!("Report: {}", r.report_path.display());
}

// install() before any threads or async runtime — sigaltstack is per-thread.
// Creates crash_dir if it does not exist.
kigi_crash_handler::install(kigi_crash_handler::CrashHandlerConfig {
    app_version: env!("CARGO_PKG_VERSION").to_string(),
    crash_dir,
});