Docs/Reference

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.

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.