Docs/Reference

Reference

No fallbacks

block-fallbacks is a write gate that refuses code which answers a missing or failed value with one nobody stated, or drops the error that says why it failed. A fallback hides the failure from whoever could fix it and turns into a decision nobody made; the rule is that a missing value is an error naming what is missing, where it was looked for and why it matters, carrying the cause it was given.

block-fallbacks is a write gate that refuses code which answers a missing or failed value with one nobody stated, or drops the error that says why it failed. A fallback hides the failure from whoever could fix it and turns into a decision nobody made; the rule is that a missing value is an error naming what is missing, where it was looked for and why it matters, carrying the cause it was given.

#Quickstart

Install Tama and choose the gate for this machine:

stado product install tama --surface cli
tama enforcement add block-fallbacks
tama show block-fallbacks

Then ask your agent to read a port from the environment in a TypeScript service. Where it would write const port = env.PORT ?? "8080";, the write is refused before it lands:

block-fallbacks: BLOCKED: the file this write leaves holds a fallback, which answers a missing or failed value with one nobody stated or drops the error that says why:
  web/server.ts: TypeScript ?? to a literal: line 3: const port = env.PORT ?? "8080";
The operator's rule: "Nie używaj fallbackow. Upewnij się ze każdy możliwy Error wyświetli się z konkretna przyczyna."
Make the missing value an error that names what is missing, where it was looked for and why it matters …

The agent writes the version that tells the next person what to do instead:

const port = env.PORT;
if (!port) throw new Error("PORT is not set: the server listens on the port the deployment declares in PORT");

Before you turn the gate on for a codebase, list what it already holds, per repository:

tama violations find --fallbacks --tree ~/code
tama violations find --fallbacks --repo ~/code/service --json

Other things people use it for: keeping a Rust service from turning an unreadable configuration file into Err(_) => Config::default(); keeping a release script from shipping to ${TARGET:-production} when nobody set a target, or from carrying on after rm -r "$build" || true; keeping a Python job from reading settings.get("region", "eu") or hiding a failed upload behind except OSError: pass; keeping a Swift app from loading a file with try?.

#What is refused

FilesConstruct
.rsunwrap_or, unwrap_or_default, unwrap_or_else, map_or, map_or_else; .ok() and Result::ok, which drop the error; a match arm Err(_), which discards it
.js, .mjs, .cjs, .jsx, .ts, .mts, .cts, .tsx?? or || followed by a literal (string, number, template, array, object, boolean, null, undefined); a catch that binds no error; an empty catch; a catch returning a literal; .catch(() => literal)
.pyan except whose only statement is pass or continue; an except returning a literal; .get(key, default); getattr(object, name, default); or followed by a literal; suppress(…)
.swifttry?; ?? followed by a literal; an empty catch
.sh, .bash${VAR:-default}, ${VAR-default}, ${VAR:=default}, ${VAR=default}; || true and || :

Each construct is a tree-sitter query over the file's syntax tree, declared once per language family in rust/crates/tama-hook-tools/src/ports/block_fallbacks/constructs.json together with the grammar every file extension is parsed by. Matching syntax rather than text means a construct named inside a string or a comment is not one, and one written over several lines still is; the finding names the line the construct stands on. Adding a construct is one query; adding a language is one extension entry and its grammar. A construct may also declare an except query: a node it captures is the same syntax standing where the language gives it another meaning, and is not a finding. Rust's .ok() declares one: a call to ok() that is the condition of an if or while, the operand of !, or a side of && or || must be a bool, so it is a type's own success predicate (a command output's ok(), for one) and drops no error. The same call bound by let, chained or matched is still refused.

#What is read

The whole file as the write leaves it: a hashline section or a string edit is replayed on the file, a written file's content is read as written. A fallback the file already held is refused on its next edit until the edit removes it, whoever wrote it, and the refusal lists every one, not just the first. A ?? or || that reads another variable passes, as does an error bound by name and carried on, a file of a language with no declared construct, and a write that names no file.

#Refusals and what they mean

RefusalMeaning
the file this write leaves holds a fallback …One line per construct: the file, the language and construct, the line number and the line. Remove each, or turn it into an error carrying its cause.
the file this write leaves cannot be rebuilt to judge it: …The edit could not be replayed on the file (the file is missing, or the edit does not apply); the cause follows.
<path> cannot be judged for fallbacks: …The file's grammar did not load or a declared construct is not a valid query for it; the declaration and the query are named. Every write of that language is refused until the declaration is repaired, because an unjudged file is not a file without fallbacks.

#Listing what a codebase already holds

tama violations find --fallbacks runs the same judgement over every tracked file of each repository it is given (--repo, --tree, --owner, --me), one finding per construct and line, reported per repository with the construct as its rule. Tama Desktop offers the same pass as Fallbacks and dropped errors on the Violations screen.

#Where it comes from

Source: rust/crates/tama-hook-tools/src/ports/block_fallbacks/ (mod.rs the gate, syntax.rs the syntax-tree reading); binary: src/programs/block/limits/block_fallbacks.rs; repository pass: rust/crates/tama-violations/src/find_violations/fallbacks/mod.rs; declaration: hook-declarations/code-discipline/block-fallbacks.json. Real-binary tests: tests/fallbacks/refusal.rs (harness payload shapes: a hashline edit, a string edit, a whole-file write), tests/fallbacks/languages.rs (one test per language, refusals and passes) and tests/violations/find/fallbacks.rs (the repository pass over two real checkouts, and the refusal of a missing repository).

#Retained qualification evidence

The real-binary targets require TAMA_TEST_SOURCE_REVISION to match the clean tracked checkout. Before invoking the hook, they retain the candidate revision, actual executable digest and Git command outcomes. Each invocation uses an isolated home and working directory beneath the ignored test target directory, retaining its payload, command, exit status, stdout and stderr even when a policy assertion fails. The command is TAMA_TEST_SOURCE_REVISION=<full-candidate> cargo test -p tama-cli --test fallbacks_refusal --test fallbacks_languages --test violations_fallbacks.