Skip to content

feat(agent): add pre-execute hook event for context injection at bootstrap#123

Merged
pablof7z merged 3 commits into
masterfrom
feat/hooks-pre-execute
May 31, 2026
Merged

feat(agent): add pre-execute hook event for context injection at bootstrap#123
pablof7z merged 3 commits into
masterfrom
feat/hooks-pre-execute

Conversation

@pablof7z

Copy link
Copy Markdown
Collaborator

Summary

Adds pre-execute as a third hook event alongside pre-tool and post-tool. A hook subscribed to pre-execute fires once at agent bootstrap, before the first LLM call; its stdout is appended to the system prompt.

This wires up the proactive-context inject path — hooks configured in .tenex-hooks.json with events: ["pre-execute"] now inject wiki-sourced project context directly into the agent's system prompt on every invocation.

The stdin payload matches the Claude Code UserPromptSubmit hook schema (session_id, cwd, transcript_path: null, prompt) so proactive-context inject and any other Claude Code-compatible inject hook works without adaptation.

Non-zero exit from a pre-execute hook is logged and the agent continues — a failing inject hook must never block the agent.

Changes

  • project_hooks.rs: HookEvent::PreExecute variant, fire_pre_execute method, pre_execute_stdin builder, "pre-execute" in config parser
  • agent_bootstrap/mod.rs: clone hooks runner before move, call fire_pre_execute and append results to system_prompt after all other reminders

Test

Verified live against DDD project with .tenex-hooks.json wired to proactive-context inject:

  • Prompt about writing style → writing-style guide selected and compiled → <system-reminder> with cited line references injected into system prompt

🤖 Generated with Claude Code

pablof7z and others added 2 commits May 31, 2026 03:59
71 sessions distilled (glm-5.1); 2→68 guides, citations verbatim-anchored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…strap

Add `PreExecute` to `HookEvent`, `fire_pre_execute` to `ProjectHooksRunner`,
and a call site at the end of agent bootstrap. Hooks subscribed to
`pre-execute` fire once before the first LLM call; their stdout is appended
to the system prompt. Non-zero exit is logged and ignored.

The stdin payload matches the Claude Code UserPromptSubmit schema
(`session_id`, `cwd`, `transcript_path: null`, `prompt`) so tools like
`proactive-context inject` work without adaptation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude

claude Bot commented May 31, 2026

Copy link
Copy Markdown

Code Review

Overview

This PR adds a pre-execute hook event that fires once at agent bootstrap (before the first LLM call), injecting each hook's stdout into the system prompt. It also bundles 69 auto-generated wiki documents into the same commit. The core functional change is small and well-scoped.


Core Code Changes

crates/tenex-agent/src/agent_bootstrap/mod.rs

The integration is minimal and correct. Cloning project_hooks before the move is the right minimal fix for the ownership constraint.

// This comment explains a non-obvious borrow constraint — appropriate per CLAUDE.md
let hooks_for_bootstrap = project_hooks.clone();

One nit: &hooks_for_bootstrap is borrowed via if let Some(hooks) = &hooks_for_bootstrap — the & makes hooks an &ProjectHooksRunner. Since fire_pre_execute takes &self, this is fine, but the code could be written more simply as if let Some(ref hooks) = hooks_for_bootstrap or just consuming the Option directly.

crates/tenex-agent/src/project_hooks.rs

The new HookEvent::PreExecute variant, fire_pre_execute, and pre_execute_stdin are logically sound and follow the existing patterns. A few issues:


Issues

1. Multi-line doc comments violate CLAUDE.md

CLAUDE.md is explicit: "Never write multi-paragraph docstrings or multi-line comment blocks — one short line max." The PR adds three violations:

/// Fires once at agent bootstrap, before the first LLM call. Stdout is
/// injected into the system prompt. Non-zero exit is logged and ignored
/// (the agent still runs).
PreExecute,
/// Fire every `pre-execute` hook once at agent bootstrap. Non-zero exit is
/// logged and ignored — the agent still runs. Stdout on exit 0 is collected
/// and returned for injection into the system prompt.
pub async fn fire_pre_execute(&self) -> Vec<String> {
/// Build the `pre-execute` stdin payload. Matches the Claude Code
/// `UserPromptSubmit` hook schema so tools like `proactive-context inject`
/// work without adaptation.
fn pre_execute_stdin(&self) -> String {

Each of these should be collapsed to a single line. The wiki entry multi-line-doc-comments-in-the-hooks explicitly documents this same rule.

2. No automated test coverage

The PR description notes "Verified live against DDD project" but there are no automated tests for the new path. The e2e-probe-validation wiki entry states:

Any issue implementation MUST be validated by the e2e probe before landing. The e2e probe scenarios file-modification-tracking and hooks-pre-tool must both pass with 4/4 verdicts before landing.

There is no hooks-pre-execute probe scenario, and no evidence the existing probe suite was run against this change.

3. Wiki dump bundled with functional change

69 auto-generated wiki files (2200+ of 2275 additions) are mixed into this PR alongside the functional change. The _index.md header itself says "Derived cache — do not hand-edit. Rebuilt by proactive-context after each capture." These should land as a separate commit/PR so reviewers can focus on the functional change and the auto-generated content doesn't obscure the diff.


Design / Correctness

  • Non-zero exit and timeout from a pre-execute hook correctly log and continue — a failing inject hook must never block the agent. ✓
  • The stdin payload matching Claude Code's UserPromptSubmit schema (session_id, cwd, transcript_path: null, prompt) is the right compatibility choice. ✓
  • Adding "event": "pre-execute" to the payload beyond the Claude Code schema is harmless enrichment. ✓
  • fire_pre_execute accumulates only exit_ok stdout — correct and consistent with how fire_pre_tool handles injections. ✓
  • The empty-string guard if !stdout.is_empty() avoids injecting blank sections. ✓

Summary

The functional change is correct and minimal. Two things need fixing before merge:

  1. Collapse all three multi-line doc comments to single lines (CLAUDE.md rule, also documented in your own wiki)
  2. Either add a hooks-pre-execute e2e probe scenario or document explicitly why the existing hooks-pre-tool probe covers this path

The wiki dump should be a separate commit or PR.

🤖 Generated with Claude Code

Resolved conflict over where pre-execute hook injections land: master's
approach (turn_reminders → user message as a system-reminder block) is
correct because the system prompt must remain byte-stable for prompt-cache
hits. System-prompt mutation (the PR's original approach) is discarded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@pablof7z pablof7z merged commit 3b8c516 into master May 31, 2026
@pablof7z pablof7z deleted the feat/hooks-pre-execute branch May 31, 2026 17:11
@claude

claude Bot commented May 31, 2026

Copy link
Copy Markdown

Code Review

PR: feat(agent): add pre-execute hook event for context injection at bootstrap
State: Merged | Author: @pablof7z


Overview

This PR has two distinct components:

  1. Minor code cleanups in crates/tenex-agent/src/project_hooks.rs (3 lines changed)
  2. 68+ auto-generated wiki documents added to docs/wiki/ (≈2,200 lines)

The PR description claims it adds HookEvent::PreExecute, fire_pre_execute, pre_execute_stdin, and agent_bootstrap/mod.rs wiring — but the diff only shows comment/grammar fixes in project_hooks.rs. Those functional changes either landed in a prior commit or the description is ahead of the actual diff. Worth verifying the implementation is actually present on master.


Code Quality (project_hooks.rs)

The three visible code changes are clean:

  • Comment reflow (long //! line → two lines): correct per CLAUDE.md's single-line doc comment style; the reformatted form is now a two-liner, but it reads naturally and stays within reason.
  • Enum comment ("Tool-call lifecycle boundary""Agent lifecycle boundary"): accurate — PreExecute fires before the first LLM call, not at a tool-call boundary.
  • Serial comma in error message: correct grammar.

No issues here.


Design of pre-execute Hook (from PR description)

Assuming the implementation exists as described:

Positive:

  • Matching the UserPromptSubmit stdin payload schema (Claude Code compatible) is a sound compatibility choice.
  • Appending stdout to system_prompt rather than inserting before existing reminders preserves injection priority ordering.
  • Non-zero exit logging without blocking is the right failure posture for a context-enrichment hook — a failing wiki inject must never abort the agent.

Concern — orphan process risk: The wiki entry e2e-probe-validation.md explicitly calls out that "Orphaned child processes resulting from hook timeouts must be prevented by using kill_on_drop(true)." Verify that the fire_pre_execute subprocess uses kill_on_drop(true), consistent with what was apparently required for the other hook paths. If it reuses the same fire_hook internal helper this should already be correct, but it's worth confirming given the explicit callout.


Wiki Documentation (docs/wiki/)

Volume and provenance: 68 new Markdown files, all stamped compiled-from: conversation and annotated > Derived cache — do not hand-edit. Rebuilt by proactive-context after each capture. These are machine-captured knowledge artifacts, not hand-authored docs.

Issues:

  1. Off-topic content committed to this repo. Multiple entries describe systems completely unrelated to TENEX:

    • calendar-integration.md, cycle-aware-weight-trends.md, microphone-interaction-modes.md, task-drag-and-drop-reordering.md, weight-log-view.md, pablo-podcast-player.md — these describe a separate iOS health/task app and a podcast agent. They have no business being in tenex-chat/tenex. This looks like proactive-context captured context from multiple projects and didn't filter by repo before committing.
  2. Date inconsistency in e2e-probe-validation.md:

    created: 2026-05-30
    updated: 2026-05-12   # updated before created — logically impossible
  3. Inconsistent title casing: multi-line-doc-comments-in-the-hooks.md has a lowercase title ("multi line doc comments in the hooks") while all other entries use title case.

  4. Stale content committed as truth. mcp-server-configuration-and-access.md states under Limitations: "The mcp_servers_json field on the Agent model exists but is unimplemented dead data" — yet agent-owned-mcp-servers.md (same PR) documents a fully implemented per-agent MCP server system. These contradict each other and one is wrong.

  5. Generated files in source control: Committing auto-regenerated cache files creates merge conflicts whenever the capture tool runs. Consider .gitignore-ing docs/wiki/ or moving it to an untracked directory, and only committing the _index.md or a checksum.


Test Coverage

No tests added for the pre-execute hook path. Given that fire_pre_tool / fire_post_tool have at least the e2e probe coverage described in the wiki, the pre-execute path should be validated by a probe scenario before this is considered fully covered (the e2e-probe-validation.md entry implies this expectation exists).


Summary

Area Verdict
project_hooks.rs code changes ✅ Clean
pre-execute hook design ✅ Sound (with kill_on_drop caveat)
Wiki: off-topic content ❌ iOS/podcast entries don't belong here
Wiki: date inconsistency ⚠️ e2e-probe-validation.md created > updated
Wiki: contradictory MCP entries ⚠️ Reconcile before next capture
Gap between PR description and diff ⚠️ Verify fire_pre_execute + agent_bootstrap changes are on master

🤖 Generated with Claude Code

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