Skip to content

Wire and verify automatic capture hooks on Codex CLI (currently skills-only) #47

Description

@jsirish

Context

PR #46 shipped Codex CLI as a delivery format for throughline's 4 skills (handoff,
onboard, consolidate, consolidate-memory) via .codex-plugin/plugin.json +
.agents/plugins/marketplace.json. That half is verified: codex plugin marketplace add / plugin add throughline@throughline install correctly and skills load.

Automatic capture (the 5 hooks) was deliberately left out, for two reasons found
during /review-pr on that PR:

  1. Unverified firing. I ran each hook script directly (piped synthetic
    Claude-Code-shaped JSON at session-prompt.sh etc. with CLAUDE_PLUGIN_ROOT
    unset) and confirmed the scripts' own logic works correctly outside
    CLAUDE_PLUGIN_ROOT. But that only proves the shell logic is portable - it does
    not prove Codex actually invokes these hooks at all, with what cwd, or with
    what env vars set. The reviewer independently tried to get a hook to fire under
    real codex exec (with hooks declared various ways, --enable hooks --enable plugin_hooks, absolute command paths, a user-level $CODEX_HOME/hooks.json)
    and could not get any hook to fire either. Hook execution under Codex is
    unverified in both directions as of this issue.

  2. A plausible double-registration risk. Codex's own hook-discovery code carries
    a diagnostic: loading hooks from both <path> and <path>/; prefer a single representation for this layer. throughline's plugin root has both a file
    (whatever hooks.json would live there) and a directory (hooks/, which contains
    its own hooks.json for Claude Code's convention). If Codex's discovery treats
    these as two representations of the same layer, every hook could register twice

    • and session-capture.sh has no idempotency guard on capture lines (only
      session-precompact.sh and session-flush.sh guard their stamps), so a
      double-registration would silently double-log every captured action.

    Separately worth checking: whether Codex actually sets CLAUDE_PLUGIN_ROOT (or a
    PLUGIN_ROOT/CLAUDE_PLUGIN_ROOT pair) for hook-invoked processes - if so, the
    existing hooks/hooks.json (unmodified, same one Claude Code uses) might just
    work if pointed at directly, with no second config file and no drift risk at all.
    Also worth checking: whether Codex's manifest hooks field is real -
    validate_plugin.py (Codex 0.149.0) empirically rejects a hooks key in
    plugin.json even though the plugin-json-spec.md reference doc both shows it in
    an example ("hooks": "./hooks.json") and separately states the opposite
    ("Validation rejects unsupported manifest fields such as hooks"). That
    reference doc is internally contradictory on this point and needs resolving
    empirically, not by reading it again.

What to do

Needs a real Codex session where a hook can actually be observed firing (or not) -
not another static read of the manifest spec. Once that's possible:

  • Confirm whether Codex sets CLAUDE_PLUGIN_ROOT for hook processes at all
  • Confirm whether a bare root-level hooks.json is discovered by convention, or
    whether the manifest needs some declaration - and if so, what actually gets
    accepted (validate_plugin.py says hooks the field is rejected; empirically
    test whether hooks fire anyway via convention-based discovery)
  • If (and only if) a single unambiguous discovery path is confirmed, wire it -
    reusing the existing hooks/hooks.json directly if CLAUDE_PLUGIN_ROOT is
    set by Codex (zero drift risk), or add idempotency to session-capture.sh
    first if a duplicate-file approach is unavoidable
  • Add a .local-ci.json check keeping any new hook config in sync with
    hooks/hooks.json if a second file ends up being necessary

Until this is resolved, Codex users get the 4 skills on-demand but not automatic
session capture - document that clearly in the README's Codex section rather than
implying parity with the Claude Code / OpenCode delivery formats.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions