Reference
Why a gate decided what it decided
How to read what a hook read and decided, which program a provider runs, and which checkout a tama hooks command reads.
A verdict nobody can read is a verdict nobody can repair, so the reading
belongs to the gate. Each hook binary takes the payload it is given on stdin
and answers about it; with --explain it prints what it read and
decides nothing:
echo '{"session_id":"<id>","last_assistant_message":"<text>"}' \
| tama hook block-unprompted-topics --explain
For block-unprompted-topics that is the transcript it found, how
many records it read, how many terms the operator's own words and this turn's
results ground, the most ungrounded paragraph the session already accepted, and
every block of the message with its counts and the words that have no provenance.
A hook that answers this way is also a hook whose cost is visible: its test
judges a long session quickly, so a regression is a failing test rather than a
gate everybody waits for.
The engine a provider dispatcher executes is the same program, as
tama run-hook, installed from the same build. A separate copy in
~/.local/bin that nothing installs keeps whatever build last wrote
it, so a fix can be live on one path and absent on the other. One program
installed from one build cannot drift that way.
Which checkout a tama hooks command reads is decided in this
order: an explicit --root, then TAMA_ROOT, then the
first directory at or above the working directory that holds
shared-hooks/registry.json. The path compiled into the binary comes
last: the installed CLI is built by the Stado batch build, in a tree that no
machine running it holds, so a compiled path tried first would make every
tama hooks run refuse with names no hook binary of ours
against a directory that no longer exists.
Both the plan and a finished release print where the sealed runtime on
this machine was built from, read from
native-hook-binaries.json: the revision, and whether the source
held uncommitted changes when it was sealed. A runtime staged from a shared
checkout's working tree carries "dirty": true; a release built by
Stado seals the committed tree the batch build unpacked, so it names that
commit and is never dirty.