runtime-hook/v1 is the provider-neutral in-process extension seam for ordered
runtime decisions and passive observations. It exists so features such as Buzz
workflow triggers can use one governed contract instead of adding
provider-specific callbacks.
The v1 bus accepts only handlers registered by trusted server code. Executable repository scripts, arbitrary plugins, and HTTP/webhook handlers are unsupported until the filesystem and egress boundaries in #862 and #855 can enforce them.
Initial events:
| Event | May deny |
|---|---|
session.pre-start |
Yes |
session.post-end |
No |
tool.pre-use |
Yes |
tool.post-use |
No |
permission.post-denied |
No |
completion.post-recorded |
No |
workflow.pre-external-trigger |
Yes |
Post-event definitions must use fail-open. If a post-event handler returns a
deny decision, the bus records invalid-post-decision and leaves the completed
result unchanged.
Every envelope contains:
Unknown events, nested or oversized metadata, credential-like fields, and recognized secret values are rejected before handler execution. Provider credentials and unrestricted host paths are not hook payload fields.
Definitions bind one event to one registered handler, scope, order, timeout, enabled state, and fail-open or fail-closed policy. Re-registering the same definition ID is the controlled update path. Definitions can be enabled or disabled without deleting prior outcomes.
Matching definitions execute sequentially in this order:
Within a scope, lower explicit order runs first, then definition ID. Disabled and non-matching definitions are omitted. The first blocking denial or fail-closed failure stops later handlers.
Handlers receive an AbortSignal. The bus enforces a 10 to 5,000 millisecond
timeout and rejects recursive dispatch of the same event/hook pair. Missing,
failed, timed-out, or reentrant pre-event handlers obey their declared failure
policy. Post-events stay passive regardless of handler failure.
Handlers must stop work when their signal is aborted. The bus cannot make an arbitrary in-process side effect reversible after it occurs.
Each outcome retains source event ID, hook/handler IDs, execution order,
timestamps, duration, disposition, blocking state, and a bounded redacted
diagnostic. When task and attempt references exist, the default recorder appends
a namespaced runtime.hook event to the causal run journal and returns its
event/sequence reference.
dryRun() validates the same envelope and resolves the same effective ordering,
registered handlers, and fail-closed missing-handler blockers. It never invokes
a handler or writes evidence.
Feature code uses the singleton bus only to register a bounded built-in handler and versioned definition. It must keep product configuration, authorization, and domain persistence in the feature that owns them. For #911, the Buzz adapter supplies an authenticated external-event reference, while the workflow trigger feature owns its rule, causal key, journal disposition, and exactly-once workflow dispatch.