--- id: 20260603-122317-hook-public-surface-hardening slug: hook-public-surface-hardening title: Hook: harden public hook surface before plugin exposure status: closed kind: task priority: P1 labels: [hooks, plugin, safety, tests] created_at: 2026-06-03T12:23:17Z updated_at: 2026-06-03T17:07:44Z assignee: null legacy_ticket: null --- ## Issue `crates/pod/src/hook.rs` is already positioned as the public orchestration extension point over the internal `llm_worker::Interceptor`, but actual production usage is still light. Current usage is mostly manifest permission policy (`PreToolCall`) and usage tracking (`PreLlmRequest`). Before external or plugin-like features rely on Hooks, the public Hook surface needs tighter safety boundaries and better Pod-layer test coverage. The main concern is that some Hook outputs still reuse internal `llm_worker::interceptor` action types. In particular, `PreRequestAction::ContinueWith(Vec)` and `TurnEndAction::ContinueWithMessages(Vec)` allow model-visible item injection from a Hook path. That conflicts with the intended public rule that Hooks must not mutate history/context except through approved durable paths. ## Requirements - Audit the public Hook API against the intended plugin/feature contract. - Replace or wrap internal interceptor action types with safe public Hook action subsets where needed. - `OnPromptSubmit` already has a safe `HookPromptAction`; use the same pattern for other public Hook events. - Public Plugin/Feature Hooks should not be able to return raw `Item` vectors for hidden context/history mutation. - Preserve internal mechanisms that legitimately need richer interceptor actions, but keep them separate from public feature/plugin Hooks. - Keep manifest permission policy behavior intact. - Deny/ask still fails closed with synthetic tool results as today. - Clarify which Hook events are observational only and which can cancel/abort/yield/deny. - Add Pod-layer tests for Hook behavior that is currently only lightly covered. ## Suggested test coverage - `OnPromptSubmit` Hook can cancel a submitted prompt through the safe public action type. - `PreLlmRequest` public Hook runs under normal conditions and cannot inject raw `Item` context. - `PreToolCall` Hook can deny/skip with a synthetic result through the intended path. - `PostToolCall` Hook can observe a bounded summary and abort when allowed, but cannot rewrite tool output unless an explicit safe transform capability is introduced. - `OnTurnEnd` public Hook cannot append arbitrary messages through raw internal action types. - `OnAbort` Hook is called on abort paths. - Multiple Hooks execute in registration order and short-circuit on non-continue actions. - Internal mechanisms run in the expected order relative to public Hooks. ## Non-goals - Implementing a Plugin runtime. - Implementing a feature registry. - Adding declarative hooks. - Adding new Hook event kinds unless a gap is found during the audit. - Allowing Hooks to rewrite arbitrary history/tool/model context. ## Acceptance criteria - Public `pod::hook` APIs no longer expose internal action types that allow raw model-visible `Item` injection. - Internal interceptor capabilities remain available only through internal mechanisms where justified. - Existing manifest permission policy and usage tracking behavior remain intact. - Focused Pod-layer tests cover the Hook events and short-circuit behavior needed by the feature registry/plugin surface. - The resulting Hook surface is safe to reference from `plugin-feature-contribution-registry` as the public Hook contribution boundary.