Skip to content

fix(framework): a Codex hook that was never trusted is skipped in silence, and the journal with it #699

Description

@blafourcade

Codex will not run a hook it has not been asked to trust, and says nothing when it declines. A freshly installed plugin therefore journals nothing, and the silence is indistinguishable from a session where nothing happened.

What was measured

A plugin installed for Codex, enabled in ~/.codex/config.toml, with a hook whose command resolves to a file that exists. Four consecutive codex exec sessions ran clean and wrote no journal. No warning, no line in the output, nothing in the log. The fifth ran with --dangerously-bypass-hook-trust and the same install produced all three hooks and its journal:

hook: SessionStart      Completed
hook: PostToolUse       Completed
hook: Stop              Completed

The mechanism is [hooks.state] in ~/.codex/config.toml: one trusted_hash per hook, written when a person approves it. A hook with no entry is skipped.

Why it matters here

The run journal is the only thing that ties a session to the step that was running. Losing it on a tool loses per-step attribution on that tool, and it fails in the shape this measurement layer exists to avoid: no error, a plausible empty result, and nothing on the record saying a measurement was not taken.

It also lands hardest where it is least visible. A person running Codex interactively approves once and never thinks about it again. Anything headless — CI, an agent, a scripted run — never sees the prompt, and every session it produces is silently unattributed.

Done when

  • Installing a plugin that ships hooks for Codex says, at install time, that its hooks need trusting and how to grant it.
  • A session whose journal hook did not run is distinguishable from one where nothing happened — the report says the journal is missing rather than showing an empty period.
  • The headless path has a documented way to grant trust that does not require a person at a terminal.
  • A test fails if an install that carries hooks reports success without mentioning what is still needed to run them.

Out of scope

Granting trust automatically. A hook trust prompt exists because a plugin hook runs arbitrary commands; routing around it on the user's behalf is not this ticket.

Where it was found

While verifying #698 end to end. The hooks arrive correctly and the command resolves — that part works. This is the next gate after delivery.

Activity

  1. blafourcade commented on Aug 22, 2026

    @blafourcade
    ContributorAuthor

    Said at install, and told apart in the diagnostic

    At install, for Codex only:

    Plugin "sample-plugin" (codex): Codex will not run this plugin's hooks until each one is
    trusted — approve the prompt once in an interactive session, or pass
    --dangerously-bypass-hook-trust to codex exec for a headless run. Until then, a session
    leaves no run journal and nothing says why.
    

    For a tool that declares no such gate, nothing is printed — asserted in the same test, because a warning that appears everywhere is read nowhere. The text lives on the tool's own declaration beside acceptsHooks and pluginRootToken, so a second gated tool needs it written once.

    It is deliberately not a PluginTranslationSkip. A skip means a component was never delivered; this means one was delivered with a precondition. Conflating them would make "hooks skipped" ambiguous between "Cursor cannot run this" and "Codex needs this approved".

    In the diagnostic, from a real untrusted session:

    hook fired  FAIL  Codex has not trusted this plugin's hook — no trusted_hash for
    hooks/hooks.json:session_start in …/config.toml. Approve it interactively once, or pass
    --dangerously-bypass-hook-trust to codex exec for a headless run.
    

    That is now a fourth answer on that claim, distinct from "never observed firing" and "this session left no run file". The trust state is read only when CODEX_THREAD_ID is set, never for another tool, and an unreadable config reads as unreadable rather than being guessed into either answer.

    Two limits, stated rather than glossed

    The trusted path was not reproduced live. A persisted trusted_hash is only written by an interactive TTY approval; --dangerously-bypass-hook-trust bypasses the check at runtime and writes no entry — confirmed empirically, the config was unchanged after that run. The key shape used is drawn from genuinely approved entries already on the machine for other plugins using the same mechanism, and the "reads ok" path is covered by fixtures rather than a fresh session. Three sessions were budgeted and three were spent, two of them on a setup shape that turned out wrong.

    The marketplace-registration route has no notice. registerNativeGithubPlugins registers a plugin by name and version without ever fetching its distribution, so it cannot know whether the plugin ships hooks without an extra network fetch on every install. Left unfixed rather than over-fetching or notifying blindly.

    Still open in this ticket

    The last Done-when — a documented way to grant trust headlessly that does not need a person at a terminal — is not solved. --dangerously-bypass-hook-trust is a per-invocation bypass, not a grant: it leaves no entry, so the next run needs it again. Whether Codex offers any way to persist trust without a TTY is unestablished.

  2. moved this from Ideation to In review in AIDD Roadmapon Aug 22, 2026
  3. blafourcade commented on Aug 22, 2026

    @blafourcade
    ContributorAuthor

    Done and awaiting review in #706, which closes this on merge.

    The work is on claude/aidd-telemetry-layer-e403uf, eleven commits, targeting next. Gate at the time of push: 365 plugin specs, 1,931 CLI unit, 577 integration, 178 e2e, tsc and biome clean, no broken markdown links.

    Read the eleven commits rather than the pull request's file count — the branch carries its own copy of the CLI migration that next has since received, so the diff counts it twice. The range and the real figures are in the first comment on #706: 142 files, +10,518 −418.

    Every claim about a tool in this work rests on a session that was actually run. The negative results are kept in full in aidd_docs/tasks/2026_08/2026_08_22_telemetry-every-tool/measurements.md, including the two probes that disagreed about OpenCode and the measurement that reconciled them.

  4. blafourcade commented on Sep 2, 2026

    @blafourcade
    ContributorAuthor

    Delivered by #706, squash-merged into next as 627408f.

  5. moved this from In review to Done in AIDD Roadmapon Sep 2, 2026
  6. added theissue type on Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Fields

    Priority

    High

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions