Skip to content

feat(hooks): Dim 9 residuals 1/2 — prompt evaluator prod wiring + 6 newly-live hook events#117

Merged
sweetcornna merged 1 commit into
mainfrom
feat/dim9-hooks-residuals
Jul 20, 2026
Merged

feat(hooks): Dim 9 residuals 1/2 — prompt evaluator prod wiring + 6 newly-live hook events#117
sweetcornna merged 1 commit into
mainfrom
feat/dim9-hooks-residuals

Conversation

@sweetcornna

Copy link
Copy Markdown
Owner

Summary

Closes the bulk of the verified-open Dim 9 gaps (audit/GAP_CLOSE_2026-07-08.md B1/B2/B3).

B1 — prompt-kind production evaluator

v1.25.0 shipped prompt/agent hook kinds but both HookRunner construction sites passed only rule_matcher → those kinds silently failed open (_exec_evaluator's unwired: warn-once). New hooks_evaluators.py:

  • single-shot LLM judge — hook prompt = judging instruction, event payload = evidence (8KB cap), answer = the {"ok", "reason"} verdict shape the engine already coerces;
  • wired at both sites (main._build_hook_runner + c2_wiring), each resolving its provider lazily per fire (hot-reload picked up; independent of provider-bootstrap ordering);
  • judge model precedence: hooks.evaluator_model > CORLINMAN_HOOK_EVAL_MODEL > models.default;
  • reasoning deltas never count as verdict text; unparseable / no-model keeps today's fail-open.
  • agent-kind: register_hook_agent_runner late-binding seam + verdict parsing land now; production registration remains a ledgered residual (needs an out-of-turn subagent entry point — same blocker as the Dim 7 items).

B2/B3 — six accepted-but-dormant events gain live emitters

event emit site semantics
pre_compact reasoning loop, gated on the same elide threshold _compact_history applies blocking — deny defers budget-path compaction one round; overflow shrink stays unconditional (a hook can delay, never brick)
session_start servicer chat entry once per session_key per process (bounded seen-set)
session_reset console /new + /clear advisory
notification ask_user (needs_input) + subagent terminal states (new dispatcher hook_notifier seam) advisory — claude-code "needs input / task completed"
file_changed after post_tool for write_file/edit_file/notebook_edit advisory, payload carries path
setup gateway lifespan boot-complete + embedded brain assembly once per process

hooks_live.LIVE_HOOK_EVENTS + /hooks & admin live/dormant labels updated — only session_end remains dormant. One stale test assertion (session_start listed as dormant) updated accordingly; that behavior change is this PR's point.

Tests

7 evaluator (incl. end-to-end deny through HookRunner) · 3 pre_compact loop · 6 emit-site · session_start once-per-key drive-through · 2 console session_reset. Affected suites 640+ green; ruff / mypy / lint-imports green.

Notes

@github-actions github-actions Bot added codex:needs-review A Codex review is needed for the current PR head. codex:review-requested A Codex review was requested or should be running automatically. status: 🔁 re-review loop A fresh Codex review was requested after the latest change or comment. labels Jul 7, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ca75b7c81d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

# Dim 9 — file_changed: a dedicated event for workspace mutations
# so operators can hook formatters/linters/sync without pattern-
# matching every post_tool payload. Fire-and-forget like post_tool.
if tool in _FILE_MUTATING_TOOLS:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate file_changed on successful writes

For write_file, edit_file, and notebook_edit, this branch runs after every tool result, including args_invalid, permission_denied, workspace_escape, or write_failed envelopes returned by _dispatch_builtin_inner. In those cases no workspace file changed, but formatter/sync hooks still receive file_changed with the requested path, so operators can run side effects against stale or nonexistent files; parse the result or otherwise only emit this event after a successful mutation.

Useful? React with 👍 / 👎.

Comment on lines +386 to +388
provider, upstream, _params = registry.resolve(
m, aliases=_alias_entries(_models_cfg)
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Resolve hook evaluator aliases from the live config

This prompt evaluator closes over _models_cfg from gateway startup, while the config watcher hot-reloads providers/models by replacing state.provider_registry/state.config and only calls HookRunner.reload() for hook definitions. After /admin/config/reload changes [models].aliases, models.default, or hooks.evaluator_model, prompt-kind hooks continue resolving the judge model with the boot-time alias/default until a restart, even though the rest of the provider stack has been hot-swapped.

Useful? React with 👍 / 👎.

try:
await run_event(
"file_changed",
{"tool": tool, "path": _path},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Pass tool_name to file_changed hooks

When operators configure a declarative FileChanged group with matcher = "write_file" or edit_file, it never matches this event because HookRunner.run_event_async() derives the declarative matcher input from payload["tool_name"], while this payload only sets tool. The hook still fires for wildcard matchers, but tool-specific file_changed hooks are silently skipped; include tool_name (or adjust the runner) so the new event can be filtered by the mutating tool that caused it.

Useful? React with 👍 / 👎.

@github-actions github-actions Bot added codex:reviewed Codex posted a review result or thumbs-up after the latest request. status: 👀 ready for maintainer look No automation blocker is known; a maintainer should inspect the current result. and removed codex:review-requested A Codex review was requested or should be running automatically. codex:needs-review A Codex review is needed for the current PR head. status: 🔁 re-review loop A fresh Codex review was requested after the latest change or comment. labels Jul 7, 2026
…ewly-live hook events

Closes the bulk of the verified-open Dim 9 gaps
(audit/GAP_CLOSE_2026-07-08.md B1/B2/B3).

B1 — prompt-kind production evaluator. The declarative engine shipped
prompt/agent hook kinds in v1.25.0 but both HookRunner construction
sites passed only rule_matcher, so those kinds silently failed open
(_exec_evaluator's "unwired:" warn-once). New
corlinman_server.hooks_evaluators builds a single-shot LLM judge — the
hook's prompt is the judging instruction, the event payload the
evidence, the answer a {"ok", "reason"} verdict the engine already
coerces. Wired at BOTH sites (main._build_hook_runner + c2_wiring),
each resolving its provider LAZILY per fire (hot-reload picked up;
independent of provider-bootstrap ordering). Judge model:
hooks.evaluator_model > CORLINMAN_HOOK_EVAL_MODEL > models.default.
Reasoning deltas never count as verdict text; unparseable/no-model
keeps today's fail-open. Agent-kind gets the register_hook_agent_runner
late-binding seam + verdict parsing; production registration stays a
ledgered residual (needs an out-of-turn subagent entry point).

B2/B3 — six accepted-but-dormant events gain live emitters:
- pre_compact: blocking consult in the reasoning loop when compaction
  is imminent (gated on the same elide threshold _compact_history
  applies, so quiet rounds never fire); deny defers the budget-path
  compaction one round, the overflow shrink stays unconditional as the
  backstop.
- session_start: servicer chat entry, once per session_key per process
  (bounded seen-set).
- session_reset: console /new + /clear.
- notification: ask_user (needs_input) + background subagent terminal
  states via a new dispatcher hook_notifier seam (gateway wires it to
  state.hook_runner, read lazily per fire).
- file_changed: after post_tool for write_file/edit_file/notebook_edit,
  payload carries the path.
- setup: once per process (gateway lifespan boot-complete + embedded
  brain assembly).

hooks_live.LIVE_HOOK_EVENTS + the /hooks & admin live/dormant labels
updated (only session_end remains dormant); file_changed/setup added to
the declarative aliases + default-async map + the runner's protocol
listing. The stale /hooks-view test assertion (session_start dormant)
updated to session_end — the behavior change is this PR's point.

Tests: 7 evaluator (incl. end-to-end deny through HookRunner), 3
pre_compact loop, 6 emit-site (file_changed/notification/dispatcher
seam), session_start once-per-key drive-through, 2 console
session_reset. Affected suites 640+ green; ruff/mypy/lint-imports green.
@sweetcornna
sweetcornna force-pushed the feat/dim9-hooks-residuals branch from ca75b7c to e18ca66 Compare July 20, 2026 01:49
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions github-actions Bot added codex:reviewed Codex posted a review result or thumbs-up after the latest request. status: 👀 ready for maintainer look No automation blocker is known; a maintainer should inspect the current result. codex:needs-rerun A new push or stale result means @codex review should be requested again. codex:review-requested A Codex review was requested or should be running automatically. status: 🔁 re-review loop A fresh Codex review was requested after the latest change or comment. and removed codex:reviewed Codex posted a review result or thumbs-up after the latest request. status: 👀 ready for maintainer look No automation blocker is known; a maintainer should inspect the current result. labels Jul 20, 2026
@sweetcornna
sweetcornna merged commit f0d7c3d into main Jul 20, 2026
10 checks passed
@github-actions github-actions Bot added status: ✅ merge-ready Evidence and review are clear; normal merge gates may proceed. and removed codex:review-requested A Codex review was requested or should be running automatically. codex:needs-rerun A new push or stale result means @codex review should be requested again. status: 🔁 re-review loop A fresh Codex review was requested after the latest change or comment. labels Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

codex:reviewed Codex posted a review result or thumbs-up after the latest request. status: ✅ merge-ready Evidence and review are clear; normal merge gates may proceed. status: 👀 ready for maintainer look No automation blocker is known; a maintainer should inspect the current result.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant