CLI reference
tama tests
Records the operator's approval of test code. block-unapproved-tests refuses every write of test code outside an approval, so a test exists only where the operator asked for it.
#Invocation
tama tests approve <path>… [--json]
tama tests revoke <path>… [--json]
tama tests list [--json]
tama tests audit [<root>…] [--remove] [--json]
tama tests review <root>
A path names a file or a directory; an approved directory covers everything below it. A relative path is read against the current directory and stored absolute. approve prints approved <absolute path> per path, revoke prints revoked <absolute path>, or under --json both print {"action": "approved" | "revoked", "changed": [...]}; list prints each approval with the time it was granted, or {"approved": […]} under --json.
#Auditing what exists
audit answers how much test code the repositories carry and how much of it the operator approved. Each root is a repository, or a directory whose direct children are repositories; with no root, the current directory is read the same way. Every tracked file is judged by the same rule the hook applies (below): a file that is test code by its path, and a source file that carries a test marker inside it. Each is approved when an approval covers it. The text output prints, per repository with test code, <repository>: N test code path(s), M approved, then unapproved file <path> and unapproved test module inside <path> lines, and a total A approved, U unapproved across R repositories; approvals: <record>. --json prints {approval_record, repositories_read, approved, unapproved, remaining, repositories: [{repository, test_code, approved, unapproved_files, unapproved_inline, files_removed}], failures}.
--remove deletes every unapproved test file and prints removed <path> for each. A test module inside a source file is never deleted, because deleting the file would delete the source with it; it stays listed for its owner to cut out. The command exits 0 only when no unapproved test code remains on disk and nothing failed, otherwise 1; the deletion is a working-tree change that the repository's own commit records. Tama Desktop's Tests screen runs the same audit and removal through tama request tests/audit with {"roots": [...], "remove": true|false}.
#What counts as test code
A file under a tests/, test/ or __tests__/ directory; a file named x_test.go, x.test.ts, x.spec.ts, test_x.py, tests.rs or XTests.swift; and a change anywhere that carries Rust's test attribute or test-only gate, a Python test_ function or Swift Testing's @Test macro. The hook reads structure — a directory, a file-name convention, an attribute — not the words of a sentence.
#Who approves
Only the operator. The record is ~/.config/agent-policy/approved_tests.json. block-unapproved-tests refuses tama tests approve run from an agent session, a write tool aimed at the record and a shell command that names it, so an agent cannot approve its own test. Tama Desktop's Tests screen makes the same change through tama request tests/approve and tests/revoke.
#What the hook refuses
block-unapproved-tests: BLOCKED: <path> is test code the operator has not approved. …— a write, an edit or a shell redirection of test code outside every approval.block-unapproved-tests: BLOCKED: `tama tests approve` records the operator's own approval and runs only in his terminal. …— the command run from a session.block-unapproved-tests: BLOCKED: an `oko` command given `--by operator` records its act in the operator's name and runs only in his own terminal, never from a session. …— anyokoverb run from a session with--by operator(--by=operatorand a quoted value included): a relation, a task verdict, a message's reading or any later verb that takes--by. The same command signed with the session's own id passes;oko tasks closeis refused the same way.block-unapproved-tests: BLOCKED: <record> is the operator's record of approved tests. …— a session write to the record.block-unapproved-tests: BLOCKED: this change writes a smoke test. …— a write of test code whose only checks are that something starts, builds, runs, does not crash or returns something. An approval never covers it.block-unapproved-tests: BLOCKED: <repository> holds smoke tests, and nothing else in it is written until they are gone: …followed by the smoke test files — any write into a git repository whose tracked test files hold one, except a write to one of those files. Rewrite each into a test of the real product, or remove it withgit rm.block-unapproved-tests: BLOCKED: which test code is approved cannot be told: the test approvals <record> cannot be read: <cause>(or… are not readable JSON: <cause>) — any write while the record exists but cannot be read. A record not written yet approves nothing and is not refused.tama tests approve,revoke,listandauditstop with the same sentence instead of reading the record as empty, so an approval is never written over the operator's record andaudit --removenever deletes a file he approved.
Reading test code is never refused, and neither is removing a test file from the shell: a shell command is judged only by what it redirects into.
Whether test code is a smoke test is read by Brama's reviewer, not by a word list. Proposed test content still uses a direct reviewer call. Existing tracked tests are judged from verdicts kept by Git blob id in smoke-test-verdicts.json under Tama's state directory, or the file named by TAMA_SMOKE_TEST_VERDICTS. When tracked files have no verdict, the hook names tama tests review <root> but starts no background reviewer. The current write uses retained findings; an unread file is not itself a finding. Run the named foreground command to review those files. This ownership rule does not remove the separate synchronous review of proposed test content.
#Reading unread test files
tama tests review <root> owns the review of tracked files without verdicts and merges returned answers under the verdict-store lock. Its per-repository lock lives in smoke-test-reviews/ beside the store. A concurrent invocation refuses an already-owned review instead of starting another model call or waiting for it. The command reports how many files were reviewed and which stored verdicts call them smoke tests. Returned failures are appended to smoke-test-reviews/review.log; a logging failure is reported alongside, never instead of, the primary cause. No root or more than one root remains a usage error.
A configured absence of a model, or a service that cannot answer typed
review questions, is not a completed review with no findings. For unread
tracked files, CLI review and tests/review refuse with
no reviewer answered for tests in <directory> and preserve
the verdict store. The Test approvals screen receives the same refusal.
The isolated tests/smoke-tests/unreviewed.rs journey configures
model-provider set --none through the real CLI and checks both
review interfaces, unchanged persisted verdicts and the retained failure log.
An absent verdict file is an initial empty store. An existing file that
cannot be read or decoded is not: both the foreground review and the write
guard refuse with cannot read test verdicts <path>: <cause>
or cannot decode test verdicts <path>: <cause>.
The foreground command retains that same cause in its review log. Publishing
reviewed verdicts reports the failed encoding, directory creation, staging or
rename operation; it cannot print a successful persistence result after a
failed write. Correct the named storage failure before reviewing again.
Tracked-file inventory and blob reads name the actual Git command,
repository, exit status and diagnostic. An unreadable index is not an empty
test inventory; an unreadable blob is not a file the reviewer may skip.
Directory reviews must all succeed before their verdicts are published.
The native request and Test approvals screen receive the same failure as the
CLI. The real tests/smoke-tests/inventory.rs journey damages an
isolated repository's actual index, checks CLI/native/write-hook refusal
and unchanged verdict state, then restores that index and checks recovery.
The write guard distinguishes a path without a Git marker from a broken
or unreadable repository. Directory inspection, root resolution and an
invalid .git file report their actual causes; a failed
git rev-parse is not permission to treat the write as outside
version control. The inventory journey also replaces an isolated Git marker
with an invalid gitfile and verifies refusal, unchanged source and recovery
after restoring the original repository metadata.
The real storage journey in tests/smoke-tests/storage.rs, included
by the existing smoke-test qualification target, uses a directory and malformed
persisted JSON at an isolated verdict path. It checks CLI failure, retained
diagnostics, the actual write-hook refusal and unchanged source, then recovery
with a valid empty store and the distinct absent-store initial state. It uses
the declared clean candidate and recorded binary identity; no provider stands
in for a storage failure.
The graphical Test approvals
screen runs the same foreground review through
tama request tests/review with a JSON body
{"root":"<repository>"}. The native response returns
{"message":"<CLI result>"} on success and the same concrete
error on refusal. A missing, empty or non-string root is refused. Native
relative roots are resolved against the operator's home; CLI roots remain
relative to its working directory. The storage journey checks both interfaces
against the same persisted failures and recovery.
#Refusals
approve and revoke with no path print tama tests: name at least one path and the usage and exit 2; a record that cannot be written prints tama tests: the approval record was not written: <path>: <error> and exits 1. audit refuses an unknown option with tama tests audit takes [<root>…] [--remove] [--json]; unknown option <option> and exit 2; a root that is neither a repository nor holds one with <root> is not a repository and holds none, an unreadable root with <root> is neither a repository nor a readable directory: <error>, a failed git ls-files with its own error and a file that cannot be deleted with <path>: <error>, each exit 1. An unknown subcommand prints the usage and exits 2.
Return to the complete CLI command tree.