Repository navigation
feat: Add lifecycle hooks system (Spec 17) - #11
Merged
Merged
Conversation
Operator-defined Python hooks fired at 7 TrailManager lifecycle points: before_save, after_save, before_propose, after_propose, after_supersede, on_recall, on_startup. Hook files live in data repo hooks/ directory (or FAVA_TRAILS_HOOKS_DIR), loaded once at startup (anti-tampering, mirrors TrustGatePromptCache). HookRegistry passed as optional parameter to TrailManager — zero overhead when no hooks exist, full backward compatibility. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Change sys.path.insert(0, ...) to sys.path.append() in hooks.py to prevent hooks directory from shadowing stdlib modules - Move `import copy` to module-level in trail.py for consistency Review: GPT-5.1 Codex (full review, no critical/high issues) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…se 1) New hook_types.py module with: - 7 typed Event dataclasses (BeforeSaveEvent, AfterSaveEvent, etc.) - 8 typed Action dataclasses (Proceed, Reject, Mutate, Redirect, Warn, Advise, Annotate, RecallSelect) - Startup contract types (StartupOk/StartupWarn/StartupFail) - ThoughtPatch for Mutate actions with apply() method - HookFeedback accumulator with merge(), size limits, and MCP serialization - TrailContext for hook-safe trail queries via _recall_internal - ACTION_VALIDITY matrix enforcing valid actions per lifecycle point 63 tests covering all types, immutability, limits, and context behavior. [Spec 17][Phase 1: Type System] Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New hook_manifest.py module replacing convention-based discovery:
- Pydantic HookManifest/HookEntry models for hooks.yaml validation
- module: (PyPI) and path: (local file/dir) resolution
- Config injection via optional configure() export
- ${VAR} env var interpolation in config values
- Order-based sorting for multi-hook lifecycle points
- Startup validation: import checks, async signature verification
- fail_mode=closed causes sys.exit(1) on load errors
27 tests covering parsing, resolution, config injection, env vars,
ordering, and all validation error paths.
[Spec 17][Phase 2: Manifest Registration]
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pipeline engine with run_pipeline() for synchronous gating (before_*/on_recall) and dispatch_observer() for async fire-and-forget (after_*). Supports full action composition: Reject/Redirect terminal, Mutate cascade with max=5 guard, Warn/Advise/Annotate accumulation, RecallSelect subset validation. 24 tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…se 4) Replace v1 hook system with v2 Event-Action Pipeline: - trail.py: typed events + run_pipeline/dispatch_observer replace fire_before/fire_after/fire_recall - trail.py: add _recall_internal() for hook-safe TrailContext access - server.py: manifest-based loading (hooks.yaml) replaces directory scanning - server.py: HookFeedback attached to MCP tool responses - Delete hooks.py (v1) — fully replaced by hook_types/hook_manifest/hook_pipeline - Rewrite integration tests for v2 hook contract (15 tests) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Document hooks.yaml manifest, hook contract (v2 Event-Action model), available actions, config injection, HookFeedback in MCP responses, TrailContext, lifecycle points, and error handling. Update data repo layout diagram to show hooks/ directory. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Sanitize redirect namespace from hooks (path traversal prevention) - Handle Redirect action in before_propose (was silently ignored) - Clear _last_feedback at start of recall() to prevent stale leakage - Add timeout enforcement to on_startup hooks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove FAVA_TRAILS_HOOKS_DIR env var (v1 artifact, manifest path is
always {data_repo}/hooks/hooks.yaml)
- Remove unused HookExecutionError import from server.py
- Clean up docstrings: remove "Spec 17 v2", "Phase N", "Replaces v1"
references — v1 never shipped
- Remove "backward compat" comments from hooks tests
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix empty RecallSelect being ignored (falsy [] vs explicit None check) - Store pipeline_result in _last_feedback during recall so hook warnings/annotations appear in MCP responses Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
timeleft--
force-pushed
the
feature-17-lifecycle-hooks
branch
from
March 7, 2026 19:48
560602c to
9d731c6
Compare
- Fix _last_feedback race condition: replace shared mutable state with task-scoped feedback via _set_feedback/consume_feedback (asyncio.current_task) - Handle on_startup hook return values (StartupFail/StartupWarn) - Use dataclasses.replace() instead of object.__setattr__ for frozen event mutation in pipeline (preserves immutability contract) - Isolate observer hooks with deepcopy to prevent cross-hook interference Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix ruff import sorting (isort) - Add `from exc` to all `raise` in except clauses (B904) - Remove unused variable assignments in tests (F841) - Use builtin TimeoutError instead of asyncio.TimeoutError (UP041) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
hooks/hooks.yaml— Pydantic-validated, env var interpolation, module/path resolution, config injection_recall_internal()(prevents recursive hook firing)New files
src/fava_trails/hook_types.pysrc/fava_trails/hook_manifest.pysrc/fava_trails/hook_pipeline.pytests/test_hook_types.pytests/test_hook_manifest.pytests/test_hook_pipeline.pytests/test_hooks.pyModified files
src/fava_trails/trail.pysrc/fava_trails/server.pyAGENTS_SETUP_INSTRUCTIONS.mdTest plan
uv run pytest)🤖 Generated with Claude Code