Reference
What counts as changing a hook
How the hook-configuration guard reads a shell payload, and how a hook source the registry recorded wrongly is repaired.
#What counts as running a writer
The guard reads a shell payload the way a shell does. A payload is cut into
commands at ;, |, & and newline only where
those characters are outside quotes, so a sentence that names a writer inside a
quoted argument — an evidence message, a commit body, a report of a refusal —
runs nothing and is allowed. A token with spaces in it is an argument, not a
program or a path, for the same reason. A shell call is read by its command
alone: the call's description, its working directory and the session's paths
are not part of what the shell runs, so an apostrophe in any of them opens no
quote and cannot turn a commit or a register command into a hook edit.
A shell payload that names hook source or configuration where the name can act — outside its quoted data — is
a change to it whatever program it runs, and needs the owner's consent: no list of writer programs, language
write calls or patch appliers decides it, because every writer such a list left out went through. Any other
execution payload (code an evaluator runs) needs consent when its code names a protected path at all. Read a hook
with the harness's read tool, which changes nothing. A payload naming one of the operator's break-glass files
(the emergency state file, its backup folder, a session override) is refused outright. The refusal quotes what
acted: bash payload can create/edit hook source/config: `<path>` stands where it acts, in
`<the command text the shell reads as acting, quoted data blanked>`, or it sets git's
core.hooksPath; a path the refusal quotes inside blanked-out text shows a construct the shell parser could
not read as data. The journey
cargo test --manifest-path rust/Cargo.toml -p tama-cli --test hook_config_shell_mentions requires
cp, an unlisted writer and a redirection onto the registry refused, the same path inside a commit
message passed, an oko defects note whose double- and single-quoted surface, text and evidence name
the registry passed and the same command redirected onto the registry refused with the acting text quoted, a
register command whose double-quoted prose holds a possessive, a single-quoted refusal and a parenthesised path
passed, a commit by pathspec with an apostrophe in its message and in the call's description passed while the
same call copying onto the registry was refused naming it, and a command naming the emergency state file
refused.
What still refuses: the writer as the command's program or script argument,
including a quoted path (node 'shared-hooks/generate-configs.mjs'
--apply); tama install, tama enforcement only|all
and oko hooks install|pull read from the program the command
starts, with env, sudo, nohup and any other
wrapper that only starts it stepped over; the same command inside an executor's argument
(bash -lc "tama enforcement all"); and a command substitution, which
is code even inside double quotes (echo "$(tama install)").
Reporting runs stay allowed: tama enforcement
status, tama install plan (with its options and inside a wrapper
such as cd … && or env: it shows the runtime scopes and
target paths and creates no directory, writes no configuration and activates no
hook; the verb is the word right after install, as the CLI reads it, so
tama install --home ~ plan is still the installer),
generate-configs.mjs without --apply,
seal-registry.mjs --check, oko hooks install
--preview. The command is cut only after its quotes are read, so a
semicolon inside a quoted evidence message stays one argument instead of
arriving as command words and refusing an honest report of the guard's own
refusal.
#Repairing a recorded hook source
register-hook.mjs derives a hook's source from its command: it
takes the executable, drops the tama- prefix, turns hyphens into
underscores, and looks for that stem as a sibling script or as a flat
rust/crates/tama-hook-tools/src/bin/<stem>.rs. A guard whose source sits
in a nested directory matches none of those candidates, so the writer records
the hook's own command, the installed binary path, as its source.
The next seal is where it surfaces. tama hooks seal packages every
source outside the managed hook directories and refuses the whole release with
External hook source is missing for the first one it cannot open.
Until the entry is repaired no hook release can be sealed, and sealing is the
step that puts a fixed hook on a machine.
Repair the entry, then reseal the registry:
tama hook reconcile-registry-sources [--check] [--out PATH] <registry.json>
node shared-hooks/seal-registry.mjs <registry.json>
The reconciler recognises a source that names an installed hook binary,
derives the stem the way the writer does — drop tama-, hyphens become
underscores — and repairs the entry when exactly one tracked file is named
<stem>.rs; no match or several is reported and nothing is written. A
source that already resolves is left alone. --check writes nothing
and exits non-zero on the first stale entry, which is what a repository gate
runs. The reseal is required: the catalogue checksum covers the whole
document, so a rewritten source leaves the registry unsealed until
shared-hooks/seal-registry.mjs runs.