Docs/CLI reference

CLI reference

tama build

Writes and reads ~/.shared-hooks/build_restart_registry.json. A build needs the agent's written whole-task completion statement and one unused entry; the block-unregistered-build-or-restart gate consumes that entry for one attempt. Restarts retain their ordinary open entry. The CLI and Tama Desktop use the same registry operations.

Writes and reads ~/.shared-hooks/build_restart_registry.json. A build needs the agent's written whole-task completion statement and one unused entry; the block-unregistered-build-or-restart gate consumes that entry for one attempt. Restarts retain their ordinary open entry. The CLI and Tama Desktop use the same registry operations.

#Record an intent

tama build record --kind build --target skarbiec \
  --reason "Validate the committed identity checks in the shared build" \
  --revision FULL_SHA \
  --completed-task "The whole requested identity change is finished in code and documentation" [--minutes 60] [--json]

tama build record --kind restart --target brama-desktop-model-router \
  --reason "the router holds a revoked credential and reads it once at start" \
  --minutes 30
OptionMeaning and refusal
--kindbuild or restart. Any other word is refused by name.
--targetThe product for a build, the service or unit for a restart. A build command that names no target of its own — stado release submit --source ., cargo build — is registered against the repository it runs in, which is the directory name the gate reports in its refusal.
--reasonRequired: what the entry is for, because it is read later by somebody who was not there. A missing or blank reason is refused.
--revisionRequired for --kind build, and it must be a full forty-character source revision. A build that cannot say what it carries is not recorded.
--completed-taskRequired for a build: which whole task was finished before building. Missing or blank text is refused before an intent is spent. It is an agent declaration retained in completed_task, not independent proof; do not claim completion while work remains. Restarts are unchanged.
--minutesHow long the entry authorizes: one to the policy's window_minutes, which is also the value when the option is absent. A longer window is refused, so an entry nobody closes cannot become standing permission.
--repositoryWhere a build's change is measured. Without it, the repository the command runs in; a directory in no repository is refused by name.
--homeThe home whose registry is written. Present so a test can drive the real command against a disposable registry instead of the operator's.
--approval-session, --approval-quoteOptional, together, for builds only. The session identifies Tama's captured current user message; the quote must equal that whole message verbatim. Tama verifies explicit permission to exceed the daily count for this product or a batch containing it. Missing capture, a different quote, refusal, or an unavailable verifier does not authorize an exception.

#The ration

Recording is not paperwork, it is an allowance. When Stado is available, the build count comes from stado queue budget --json: the fleet's UTC-day budget. Otherwise Tama uses its local rolling 24-hour ledger. Restarts always use the local ledger. A build is refused when the applicable allowance is spent without a recorded user exception, or when its change is too small:

the policy allows 3 builds in any 24 hours and this machine has recorded 5
in that window (skarbiec, brama, tama, stado, oko). The local daily allowance
has a slot in 412 min if no further intents are recorded. Gather the work
into one build instead, or have the operator raise the limit with
`tama build policy --builds-per-day <n>` and DEVICE_HOOK_EDIT_APPROVED set
outside the agent session.

this build carries 2 changed lines against <revision>…, the revision the last
recorded build of skarbiec carried, and the policy calls a build serious at 30
lines. A build spends forty minutes of a builder the fleet has two of, so small
changes travel together: keep working and record the build once the change is
worth it.

The local count comes from ~/.shared-hooks/build_restart_ledger.json. Ordinary intents are retained for thirty days; entries containing user approval are retained as audit evidence without that expiry. An accepted build consumes its open entry before the tool runs, so another build attempt requires a new completion statement and intent. close ends an authorization without refunding it. Restarts are counted separately, ten in a rolling day.

The rolling-window forecast waits until enough entries have expired to put usage below the current limit, not merely until the oldest entry expires. This matters after a limit is lowered or approved exceptions have left usage above it. An entry at the exact boundary still counts. A zero limit has no timed reset. The forecast concerns the count only, assumes no further recorded intents, and is not a promise that change-size, source, resource or publication checks will pass.

The unregistered-build gate does not estimate usage from open authorizations: that would forget closed intents and disagree with the actual allowance. The recording command gives the authoritative refusal. Tama Desktop displays that same refusal from the shared backend.

#A user-approved exception

An exception changes neither the default limit nor the policy file. Record it before submitting the build. Replace the session, revision and message placeholders with the actual captured approval; an example in documentation is not user consent.

tama build record --kind build --target skarbiec --revision FULL_SHA \
  --reason "Build the committed change after explicit permission from the user" \
  --completed-task "The whole requested change is finished before this approved build" \
  --approval-session SESSION_ID --approval-quote 'VERBATIM_USER_MESSAGE' --json
tama build approval --target skarbiec --revision FULL_SHA --json
tama build list --json

The message must explicitly authorize exceeding the daily build count for the named product or batch. A normal build request, a request to implement this mechanism, hypothetical consent, and an agent's assertion are not consent. Tama checks the message through Brama and refuses if it cannot obtain an accepting verdict. It rechecks that the captured message has not changed before writing.

The entry stores user_approval: the exact quote, session, message digest, capture time, product, revision and verification verdict. The audit ledger is written before the open registry. If the latter write fails, the error is returned and the auditable intent remains spent; it is not silently granted or erased. The same captured message cannot authorize another intent for this product. A batch approval may cover different products, each with its own recorded intent.

The exception bypasses only the daily count. The same-revision refusal, minimum change size, expiry, source checks, resource admission and publication permissions remain in force. It cannot authorize a restart. approval returns only a matching, unexpired entry also present in the audit history; it refuses a missing entry, a different revision, or a closed authorization.

Stado's native build submission reads that entry when its fleet budget is exhausted and retains the evidence with the charged run and platform in its canonical registry. One approval cannot charge another run for the same product and platform. Raw queue submissions cannot claim this exception. Read fleet charges and approval receipts with stado queue budget --json.

This flow requires both Tama's build approval command and Stado's approval consumer. An older release reporting unknown action approval does not support the exception; recording an ordinary intent is not evidence that consent was stored or consumed.

Availability: the approval flow and corrected rolling-window forecast are in source. Native release, real-fleet consumption and desktop visual qualification are still pending; this page is not evidence that the installed release supports them.

#Tama Desktop

Open Policy → Build registry. Enter the product, full revision, repository, reason and Completed whole task and result. For an exception, enable Use recorded user consent for a daily-limit exception and supply the session and verbatim current message. Record build records one permission; it does not start compilation. The screen displays the statement and backend refusal, current entries, local intent counts and retained approval history. Close removes an authorization but does not cancel an accepted job.

The screen runs the same operations as tama request builds, tama request builds/record and tama request builds/close (tama request). Recording uses kind, target, reason, revision, completedTask, optional repository and minutes, and paired approvalSession/approvalQuote. Closing uses kind and target. Unknown request fields are refused.

#Read the limits, and change them

tama build policy [--json]
tama build policy --builds-per-day 2 --restarts-per-day 6 \
  --minimum-changed-lines 80 --window-minutes 45

Without options it prints the file and the sentence, for example 3 builds and 10 restarts in any 24 hours; a build carries at least 30 changed lines; one recorded intent authorizes 60 minutes, with the numbers the file states. builds_per_day the file does not state is the operator's own number, builds_per_day in numeric-provenance.json with his words. The other three have no value in code: while the file does not state one, the command and every intent are refused with <file> sets no <field>: the operator states it with `tama build policy <option> <n>`, which needs DEVICE_HOOK_EDIT_APPROVED=1 set outside the agent session. Tightening a limit needs nothing. Setting one the file did not state, or loosening one — more builds a day, a smaller minimum change, a longer window — is refused unless DEVICE_HOOK_EDIT_APPROVED=1 is set outside the agent session, because an agent that can raise its own allowance has none. A policy or ledger that cannot be parsed refuses the build and names the file, rather than counting the day as unspent.

#Read and close

tama build list [--json]
tama build close --kind build --target skarbiec [--json]
tama build close --kind build --target stado --not-started "Skarbiec request failed before any job existed"

list prints each entry with its kind, target, reason, revision and completed task. An accepted build is shown as used and authorizes no second build; an expired entry authorizes nothing. close removes one entry without refunding its allowance. JSON output includes approval_history, including approvals whose entry was closed or replaced. For builds, record, list and the Desktop Builds screen report the fleet's count when stado queue budget answers — the fleet has N build jobs left today, JSON fleet_remaining — because that count is the one that admits a build. The local N of M intents in this 24 hours appears only when the fleet cannot be asked, and for restarts.

A build whose submit was refused before any job existed built nothing. Close it with --not-started <refusal>: the ledger keeps the row and still counts it against the day's allowance, but the next build of that product is no longer measured against its revision, so the same revision can be recorded again instead of being refused as carries no change at all. --not-started without a refusal, or on a restart, is refused and closes nothing. The JSON result carries not_started.revision and not_started.refusal.

#What waits for the next build, and what a failed build gives back

tama build pending --target oko --repository ~/Documents/CodingProjects/Wisent/oko [--json]
tama build fail --target oko --revision FULL_SHA --failure "cargo test: 2 failed" [--json]

Work ends at a commit and a push; the batched build runs later over everything that accumulated. pending fetches the repository and prints the revision the last recorded build of the product carried, the commits on origin/main since, how many lines they change against the policy's minimum, whether a build is due, and the Oko tasks whose recorded evidence names one of those commits (read with oko tasks carried --commit <revision>…). JSON: last_built, tip, commits, changed_lines, minimum_changed_lines, due, tasks.

fail is how a failed batched build reopens work. It takes the commits the build of --revision carried — those after the revision the build before it carried — finds the tasks whose evidence names them, and reopens each with oko tasks defect, giving the batched build of <product> at <revision> failed: <failure> as the reason. The build's ledger row records the failure and still counts against the day's allowance. JSON: since, commits, reopened, and refused with Oko's own answer for each task it would not reopen, for example a task whose session transcript is not on this machine. Without --revision or --failure the command is refused and reopens nothing; when Oko cannot be run or answers no JSON, the command fails and names the call.

#What the refusal looks like

BLOCKED: unregistered build: `stado release submit --source . --version 0.3.11` would
build skarbiec, but ~/.shared-hooks/build_restart_registry.json has no unused,
unexpired entry with a whole-task completion statement for it. A build consumes
its entry once. Record the completed task and intent, then run the command again:
  tama build record --kind build --target skarbiec --reason "<what this
  build is for>" --revision <source revision this build carries>
  --completed-task "<which whole task is finished>"

#Package a matching desktop backend

In the Tama Desktop checkout, pin the pushed Tama commit before releasing the app:

bash Release/tama-source.sh pin --revision FULL_TAMA_COMMIT
bash Release/tama-source.sh verify

The producer updates Release/tama-revision and its immutable archive and checksum. Both release lanes use that revision. Stado unpacks the verified archive with its recorded origin; native packaging and sealing refuse changed, missing or additional source inputs rather than treating the enclosing desktop checkout as the backend's revision. The verification command checks the archive digest and recorded Git commit and prints the archive path. That result proves source handling only, not a compiled or published desktop release. Retained qualification evidence must name the exact revision and consumer it exercised.

#Publish the desktop's locked Swift dependencies

From the Tama Desktop checkout, publish the committed package resolution through Stado:

stado release catalog pin-input . --name swiftpm-cache --source . --revision HEAD --swiftpm --json

This resolves dependencies and publishes a checksummed archive; it does not compile or install the app. Commit the updated .wisent-release.json with the release source. The release recipe reads the expected digest from that manifest rather than a second checksum in the script, then uses stado product swift restore to verify and restore the cache. Subsequent dependency updates use the same publication command.

The updater is a locked Swift package inside this cache, so the release needs no separate updater archive; its source is verified with the other Package.resolved dependencies during restoration.

Stado refuses changed package declarations, missing or mismatched locked revisions, unsafe archive entries and invalid binary-artifact paths. It reports the failed dependency or path; neither a missing archive nor an invalid cache falls back to fetching during compilation. In Stado Desktop, Releases → Publish an immutable build input exposes the same operation with Export the Swift package's committed resolution. A publication receipt proves only the input was published, not that Tama built or passed qualification.

Source: rust/crates/tama-cli/src/builds/mod.rs.