Register an external transcript
The session transcript only records what Claude did. When a codex lane or any other external agent edits the tree mid-session, those edits are invisible to your Stop-time gates: the style review never triggers, EditedSource never matches. Registration closes the gap. Attach the external transcript to the session, and every later dispatch folds it into the session’s deep view.
Register with the CLI
Register a codex thread by id:
uvx capt-hook transcripts register --session "$CLAUDE_CODE_SESSION_ID" \
--thread-id 019f6800-3b4c-7d5e-9f60-0000000000abThe thread id resolves against the codex sessions tree (~/.codex/sessions) lazily, at each dispatch — the rollout only has to exist by the time a hook reads the transcript, so registering the moment the thread starts is fine. An id that never resolves, or whose rollout gets pruned, drops out silently.
For a transcript file that already sits on disk, pass its path instead:
uvx capt-hook transcripts register --session "$CLAUDE_CODE_SESSION_ID" \
--path /path/to/rollout.jsonlPass exactly one of --thread-id or --path. --provider defaults to codex, and --label attaches a name for your own bookkeeping. Registration is idempotent by provider and locator: re-running the same command records nothing new.
The codex plugin’s codex-ask does this natively as of its 1.3.0 release — every lane it dispatches registers its own rollout automatically and records the outcome in the lane’s register file, so the manual command stays for ad-hoc runs.
Register over MCP
capt-hook mcp serves a stdio MCP server whose one tool, register_transcript, takes the same arguments (session_id, thread_id or path, optional provider and label) and hits the same write path, idempotency included. The capt-hook plugin declares the server for you — through its bin/capt-hook wrapper, from the same version-exact environment the hooks run in — so in a plugin-enabled session the agent calls the tool with no wiring. To wire it into a project yourself, install the CLI once and point .mcp.json at it:
uv tool install capt-hook{
"mcpServers": {
"capt-hook": {
"command": "capt-hook",
"args": ["mcp"]
}
}
}The install step matters: an MCP server is a long-running process, and a uvx parent would hold uv’s global cache lock for the server’s whole lifetime, stalling every other uv operation on the machine.
What Stop-time gates see
At dispatch, each registered entry resolves to its rollout file and folds into evt.ctx.t.deep — the recursive view over the session, its subagent sidechains, and every registered transcript. A gate predicated on the deep view fires on external edits exactly as it fires on Claude’s own:
from captain_hook import gate
gate(
"Run the style pass over everything this session touched.",
when=lambda evt: any(
f.matches("**/*.py") for f in evt.ctx.t.deep.tool_calls.named("Edit|Write").files()
),
)The bundled surfaces already look through the deep view: the EditedSource condition counts subagent and registered codex edits with no change on your side, and conditions that recurse with subagents=True (ReadFile, UsedSkill) see registered transcripts too. Bare t.tool_calls stays scoped to the main window — recursion is opt-in, so a hook that asks “what did Claude do here” keeps asking exactly that.
Session ids and paths
The session id names a directory under capt-hook’s state root, which makes it a trust boundary: an id containing a path separator, a NUL byte, or a bare ./.. component is rejected with a usage error. A thread id touches the filesystem only through the sessions-tree lookup at dispatch. A --path entry is stored as an absolute path, so a relative path anchors to the directory you ran the command from and still resolves once a later dispatch runs from the project root. At dispatch the entry folds in only if it still points at a regular file; a special file or an unreadable path drops out silently instead of stalling the hook.
See also
- Query the transcript — the query API the deep view returns.
- Reference — register_transcript, RegisteredTranscript, and RegisteredTranscripts signatures.