Skip to content

fix(ingest): self-heal the Claude/Codex session-ingest hook#25

Merged
jschatz1 merged 1 commit into
mainfrom
fix/ingest-hook-selfheal
Jul 1, 2026
Merged

fix(ingest): self-heal the Claude/Codex session-ingest hook#25
jschatz1 merged 1 commit into
mainfrom
fix/ingest-hook-selfheal

Conversation

@jschatz1

@jschatz1 jschatz1 commented Jul 1, 2026

Copy link
Copy Markdown
Member

Problem

The kai log ingest hook (registered by kai init, fired on Claude Code / Codex Stop + SessionEnd) writes a machine-readable loop record to <repo>/.kai/loops/<session>.json for the desktop Overview/Loops views. It was silently logging nothing:

  1. Dead binary path. The hook is baked with os.Executable(). During a Claude Code run that's often an ephemeral copy in a temp scratchpad (/tmp/claude-.../scratchpad/kai); when the scratchpad is cleaned the path dies and every session afterward no-ops.
  2. Records misfiled to $HOME. Repos here have .git but no local .kai/, so the "nearest .kai ancestor" logic walked all the way up to the global ~/.kai config dir → root resolved to $HOME → records aimed at ~/.kai/loops, which doesn't exist. Nothing written.

Observed: 4 repos, 0 loop records; two hooks pointing at dead temp copies, two with no hook.

Fix

  • Durable hook path (stableKaiPath): prefer ~/.kai/bin/kai, fall back to the running binary only if it isn't under a temp dir, else bare kai (PATH).
  • reconcileHookEvents: prune stale ingest entries (dead/temp paths, old installs) and install the current one — never touches unrelated hooks (TokenBar, prompt-logger).
  • Self-heal in PersistentPreRun (selfHealAgentHooks): repair-only — fixes an existing ingest hook's stale path on any kai invocation; does not create fresh installs (that stays with kai init / kai doctor --fix).
  • Root ingest at the git project, and explicitly exclude the global ~/.kai from repo-root detection, so records land in <repo>/.kai/loops instead of $HOME.
  • kai doctor now audits ingest (hook installed? binary reachable? records produced?); kai doctor --fix repairs it.
  • Bumps 0.34.20.34.3.

Verification

  • go build ./..., go vet, existing hook/ingest/doctor tests, and new unit tests (TestIsTempPath, TestReconcileHookEventsPrunesStale, TestReconcileHookEventsIdempotent) all pass.
  • Live: self-heal repaired a stale temp path to ~/.kai/bin/kai; a simulated Stop event wrote a real .kai/loops/<id>.json; kai doctor went all-green.

🤖 Generated with Claude Code

The `kai log ingest` hook is registered by `kai init` with the path of the
currently-running binary (os.Executable()). During a Claude Code session that
is often an ephemeral copy in a temp scratchpad, so the absolute path baked
into .claude/settings.local.json / .codex/hooks.json dies when the scratchpad
is cleaned — and every session afterward silently logs nothing.

Changes:
- Bake a durable path into hooks: prefer ~/.kai/bin/kai, fall back to the
  running binary only if it isn't under a temp dir, else bare "kai" (PATH).
- reconcileHookEvents(): prune stale ingest entries (dead/temp paths, old
  installs) and install the current one, without touching unrelated hooks.
- Self-heal on every invocation (PersistentPreRun): repair-only — fix an
  existing ingest hook's stale path, never create a fresh install.
- Root ingest at the git project, and never treat the global ~/.kai config
  dir as a repo root — previously sessions in repos without a local .kai
  walked up to $HOME and misfiled records into ~/.kai/loops (which didn't
  exist), so nothing was ever written.
- `kai doctor` now audits ingest: hook installed? binary reachable? records
  being produced? `kai doctor --fix` repairs it.

Bumps version 0.34.2 -> 0.34.3.
@jschatz1 jschatz1 merged commit 18367e4 into main Jul 1, 2026
10 checks passed
@jschatz1 jschatz1 deleted the fix/ingest-hook-selfheal branch July 1, 2026 05:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant