Skip to content

hook dispatcher runtime

The one shell runtime that executes a complete manifest dispatch and preserves native hook blocking output.

raw source

The compiled manifest needs one executable owner. Dispatching is not merely related to the shell script: this node governs the exact shell entry every harness invokes, including its scratch-state cleanup.

expanded spec

dispatch.sh resolves the current tree's persistent manifest exactly as hook-dispatch defines and captures the event input once before invoking any matching handler. A trap cleans up its per-dispatch scratch state on normal completion, handler failure, signal, or shell exit.

The same tree slot carries the dispatch-id allowlist from its last successful materialize. A project transport may remain installed after a selection changes, but an event whose baked harness id is absent from THIS tree's allowlist exits before any input handling. Before the project migration marker, an absent allowlist is the one-version legacy shape; afterwards absence is inert until a git-native materialize publishes it.

A missing manifest is a no-op because there is no handler work to run. All matching handlers preserve the existing deterministic order, stdout concatenation, blocking declaration, and Codex stderr reason translation.

Known-corrupt mark-active compatibility

The original shipped core/mark-active script treated its note as text safe to substitute into JSON. Its own comment asserted that a note never contains a double quote, but neither the declaration nor the harness payload enforced that assertion; a quote therefore closed the JSON string and made a live record unreadable. Existing projects track their seeded .plugins source, and spex materialize deliberately renders that source rather than overwriting it. Updating the global package alone would otherwise leave a frozen worktree executing the bad script.

The dispatcher has one narrow, package-owned compatibility route: when the manifest names the standard .spec/project/.plugins/core/mark-active/mark-active.sh path and that file byte-compares equal to either identified vulnerable shipped blob, it executes the package's current structured mark-active implementation instead. It does not edit the project file, its manifest, or any session record before that implementation runs. Any byte difference, including a project customization, executes the project script exactly as the manifest requested. This is an emergency execution override, not a plugin updater: a project moves its tracked source to the current template only in its own reviewed maintenance change. The compatibility entries remain only for the identified broken blobs; a package version label alone never enters this route.