Docs/Reference

Reference

Numeric values and standard operations

block-numeric-literals checks numeric choices against the operator's recorded instruction. Its declaration format is numeric-provenance.json with entries {"value": n, "user_said": "…"}. A write is judged by every literal the file holds as the write leaves it, the ones it already held included: a number is not approved by having been written before, so an edit of a file that holds an unsourced number is refused for it, whoever wrote it, until the number carries a source or is removed. A hashline edit is read as the file its section leaves behind (replace, insert and cut replayed against the file on disk), so text added inside a string or comment — a shell program held in a Rust raw string, with its $1 and 2>/dev/null — is read as that string, and a number added to the code around it is still refused; a section with an operation that is not replayed (a block, a register paste) is read from its added lines alone. In a data file (JSON, a Package.resolved, a manifest) a quoted value shaped as an identifier is not a number: a dotted release version such as "0.7.56" or "1.2.3-beta.1", and a hexadecimal revision or digest holding a letter such as a git commit. So a version bump and a dependency pin pass, while a quantity beside them ("retries": 7) and a quoted decimal ("port": "8792") are still refused by name. A quoted value that holds whitespace is prose, not a quantity: a documentation page's sentence ("An approval_id is at most 64 characters") or a description passes whatever figures it quotes, while a bare value and a quoted single word ("retries": "7") are still read. In a Cargo.toml, the quoted text of every dependency table ([dependencies], [dev-dependencies], [build-dependencies], [workspace.dependencies], [target.….dependencies]) is a requirement on a release the registry publishes (tree-sitter = "0.25"), so it passes; a number in any other table of the manifest (opt-level = 3), and a table of that name in any other file, is still read. The manifest's edition, rust-version and license values (edition = "2021", rust-version = "1.80", license = "BSD-3-Clause") are names Rust and the SPDX list publish, so they pass in [package] and [workspace.package]; the same keys in any other file are still read. In a package.json, read as JSON, the package's own version and license ("Apache-2.0"), and the package names and release ranges of its dependencies, devDependencies, peerDependencies, optionalDependencies and engines ("node": ">=20", "fs-ext": "2.1.1", a github: pin to a commit) are names the registry, Node and SPDX publish, so they pass; every other number in it ("config": { "port": 8130 }) and the same fields in any other file are still read. A package.json that is not JSON is refused naming the cause.

block-numeric-literals checks numeric choices against the operator's recorded instruction. Its declaration format is numeric-provenance.json with entries {"value": n, "user_said": "…"}. A write is judged by every literal the file holds as the write leaves it, the ones it already held included: a number is not approved by having been written before, so an edit of a file that holds an unsourced number is refused for it, whoever wrote it, until the number carries a source or is removed. A hashline edit is read as the file its section leaves behind (replace, insert and cut replayed against the file on disk), so text added inside a string or comment — a shell program held in a Rust raw string, with its $1 and 2>/dev/null — is read as that string, and a number added to the code around it is still refused; a section with an operation that is not replayed (a block, a register paste) is read from its added lines alone. In a data file (JSON, a Package.resolved, a manifest) a quoted value shaped as an identifier is not a number: a dotted release version such as "0.7.56" or "1.2.3-beta.1", and a hexadecimal revision or digest holding a letter such as a git commit. So a version bump and a dependency pin pass, while a quantity beside them ("retries": 7) and a quoted decimal ("port": "8792") are still refused by name. A quoted value that holds whitespace is prose, not a quantity: a documentation page's sentence ("An approval_id is at most 64 characters") or a description passes whatever figures it quotes, while a bare value and a quoted single word ("retries": "7") are still read. In a Cargo.toml, the quoted text of every dependency table ([dependencies], [dev-dependencies], [build-dependencies], [workspace.dependencies], [target.….dependencies]) is a requirement on a release the registry publishes (tree-sitter = "0.25"), so it passes; a number in any other table of the manifest (opt-level = 3), and a table of that name in any other file, is still read. The manifest's edition, rust-version and license values (edition = "2021", rust-version = "1.80", license = "BSD-3-Clause") are names Rust and the SPDX list publish, so they pass in [package] and [workspace.package]; the same keys in any other file are still read. In a package.json, read as JSON, the package's own version and license ("Apache-2.0"), and the package names and release ranges of its dependencies, devDependencies, peerDependencies, optionalDependencies and engines ("node": ">=20", "fs-ext": "2.1.1", a github: pin to a commit) are names the registry, Node and SPDX publish, so they pass; every other number in it ("config": { "port": 8130 }) and the same fields in any other file are still read. A package.json that is not JSON is refused naming the cause.

A number a protocol or a vendor states — Apple's notification payload limit, an HTTP status, a file format's magic number — has that document as its source. In a code file a literal passes when its own comment, or the comment block written directly above it, names the document by address:

// Apple's limit for a notification payload:
// https://developer.apple.com/documentation/usernotifications/generating-a-remote-notification
const APNS_ALERT_PAYLOAD_BYTES: usize = 4096;

A blank line ends the comment block, and an address a program writes in a string cites nothing. The real test for the rule is tests/numeric-literals/scanning/identifiers.rs (cargo test -p tama-cli --test numeric_literals_identifiers).

A JSON document holds no comments, so there the key written beside a value to explain it carries the source: a value passes when its object holds a <key>Why string naming the document by address, at any depth of the document.

"liveRoots": [2, 4],
"liveRootsWhy": "The depths the layout specification names at https://example.com/layout#roots"

An explanation that names no address, or none at all, leaves the value scanned, and a JSON file that does not parse is scanned whole. One sentence cites a single value or a flat list of single values; an object, or a list holding one, names members of its own, so a <key>Why beside it cites none of them and each member's number is scanned until its own <member>Why names its source. The real test is tests/numeric-literals/declared/why_fields.rs (cargo test -p tama-cli --test numeric_literals_why_fields).

A file written below a directory its checkout's .gitignore ignores — the build directory where a run keeps its throwaway state and evidence reports, such as <repo>/.build/<area>/report.json or <repo>/target/<area> — is not scanned: nothing there is committed, so none of its session ids, times or counts decides what a product does. The hook asks git about the file's directory, not the file: an ignored file in a tracked directory, such as a local settings file or .env, is read by the product and is still scanned. A file that declares itself generated — an @generated marker in the comment lines it opens with, as Cargo writes Cargo.lock — is not scanned either: its numbers are what its generator wrote, a lockfile's format version and the versions a registry publishes, so no hand chose them; the same numbers in a file nothing declares generated are refused. npm's lockfile is JSON and carries no comment declaring it generated, so a package-lock.json or npm-shrinkwrap.json is judged like any file and its unsourced numbers are refused: no file is exempted by its name or by a field its generator stamps. The real test is tests/numeric-literals/declared/build_directory.rs (cargo test -p tama-cli --test numeric_literals_build_directory), run against a real git checkout it creates for each case.

#Standard operations are not configuration choices

The Rust and JavaScript syntax trees distinguish documented operation arguments from invented defaults. A sole zero argument to Rust CommandExt::process_group or PermissionsExt::set_mode is an OS operation, not a retry count. JavaScript first-element indexing, at(0), relative last-element at(-1), and splice(index, 1) describe element access or removal. Comparisons of a directly called findIndex result or its preceding same-scope const binding with -1 use the language's absent-element sentinel.

Rust tuple selectors such as pair.0 and pair.1 identify fields, not configuration quantities. Equality or inequality of a Rust len(), child_count() or named_child_count() result with zero checks an empty collection or syntax-tree leaf. A comparison with a nonzero chosen count and a bare policy variable compared with zero remain subject to numeric provenance.

A zero lower bound directly initializing a Rust for range is an enumeration start, not a guessed quantity. Its upper bound is still judged: for index in 0..count may use the caller's count, but for index in 0..7 still introduces the chosen bound. A range stored as configuration and a zero upper bound are not exempt. The registered numeric_literals_operations journey exercises these boundaries through the real built hook with retained candidate and executable identity.

A zero that initialises a variable, binding or class field the source only ever adds to is the start of a running sum or count, not a chosen number: let mut total = 0; with total += value, a class field step = 0 with this.step++, or sent = sent + n. Every write to that name in the same source must add to it; one write that gives it anything else (sent = other, step--), or no write at all, keeps the zero judged. The same journey covers both outcomes.

A one that steps a count up or down is the unit of counting, not a chosen number: fire_count += 1, left -= 1, count.saturating_add(1) (and the other checked_, wrapping_ and overflowing_ adds and subtracts) and an atomic started.fetch_add(1, Ordering::Relaxed). Only that position passes: a step of any other number (+= 7), a one in a plain sum (page + 1), a one stored in a binding, multiplied, or passed to any other method (chunks(1)) stays judged. So a counter writes its one plainly instead of a stand-in such as NonZeroUsize::MIN.get(). Real journey: tests/numeric-literals/operations.rs.

A number written without a digit is reported like a literal: a quoted number declared as a constant or converted back ("8".parse(), Number('0')), a type's bound or width (an unsigned type's MIN, a type's BITS, arithmetic on a type's MAX), a numeric type's default or zero, the length of a string or array literal, and true or false converted to a number. These spellings are read in code only: a comment that names one, as a document explaining it does, runs nothing and is not refused. The same journey writes the spelling once in a comment, which passes, and once in code, which is refused.

An HTTP status code compared for equality with a read of a response's status is the protocol's own identity, not a chosen number: response.status === 200, res.statusCode !== 407, 404 === response.status and Rust's response.status().as_u16() == 200. A status code is three digits whose first names one of the five classes (RFC 9110, section 15), so 700 is not one, and the same digits stored in a constant or compared with anything other than a status (retries === 200) stay judged. Real journey: tests/numeric-literals/operations.rs.

Shell standard-stream descriptors are recognized from Bash redirection syntax: 2>failure.log retains stderr, and 1>&2 duplicates stdout onto stderr. Only the POSIX standard descriptors are intrinsic; an independently chosen descriptor, count or numeric filename remains judged. A quoted redirection example is not executable redirection. The real numeric-operation journey includes accepted descriptor uses and refused unrelated quantities.

assert.equal and assert.strictEqual comparisons of process status, code or exitCode with zero check successful termination. Sign comparisons against zero and positive-integer boundaries against one validate inputs. This is not permission for const DEFAULT_RETRIES = 0, an invented group identifier, an arbitrary removal count, or an unrelated comparison with -1. Other languages and malformed syntax retain the normal numeric policy.

Syntax byte positions are converted to character positions in one forward pass, rather than rescanning the preceding file for every numeric operation. Unicode text between operations and multiline tuple selectors retain their actual lexical positions. A permitted field or array access does not admit a separate policy value between those accesses; the real native-operation journey covers both forms.

The native interface remains tama hook check-numeric-literals, with a harness write or edit payload on stdin. Refusals name every unsourced literal the file will hold, the ones it already held included, with its file and line, so the refusal of an edit that adds no number lists what the file must cite or remove before any change to it is written; the existing hook-result surfaces display that same result. Framed hashline edits preserve the surrounding dependency table, so adding a Cargo dependency feature does not turn its unchanged release requirement into a new runtime value.

Before reconstruction, relative hashline headers and string-edit targets resolve against the hook payload's cwd. They do not name a same-spelled file beside the hook executable. Local file: URLs use their local target; device URIs remain device text. The provenance validator therefore receives the reconstructed declaration for supported range, gap and string edits even when the hook process runs elsewhere. This does not extend the supported hashline operation set or bypass a missing or invalid user_said entry.

Edit reconstruction preserves source read and UTF-8 decoding failures as cannot read edit source <path>: <cause>. A string replacement whose old_string is absent refuses with the target path instead of judging unchanged source or replacement text alone. A multi-edit refusal retains the failed operation; no partial edit is applied by a guard. Numeric, keyword, browser and Supabase write consumers propagate the same shared reconstruction error. Unsupported hashline block, register, move and remove operations refuse with the operation and target instead of substituting added lines. Single-path unified patches likewise report that complete reconstruction is unavailable. These diagnostics do not implement those operations: use a supported range, gap, string edit or explicit whole-file write when the harness contract permits it. No fragment establishes Rust import or return-type context. An explicit empty text write remains a complete empty document; missing operations and non-text content are diagnosed, not emptied.

String edits require explicit text old_string and new_string fields. Null, boolean or missing replacements do not become empty deletions or appended fragments. The refusal names the target and field; an intentional deletion supplies an explicit empty replacement string.

The browser and Supabase consumers use this reconstruction only for executable xd://mcp__node_repl_js write payloads, not ordinary file writes. The registered tests/tool-payload/targets.rs journey invokes both actual built guards with malformed content and then valid program text, requiring a named infrastructure refusal followed by allowance. It retains the candidate and hook exchanges; it never executes the device program, browser or Supabase CLI. This is decision-boundary coverage, not a live device journey.

Supported hashline gaps and ranges are checked against the actual source line count. An out-of-range insertion or deletion, or a reversed replacement range, refuses as cannot replay edit of <path>: <cause>. It cannot be judged as unchanged source merely because no line consumed the invalid coordinate. Empty-source insertions retain the documented PUT <1: and PUT >$: cases.

The registered numeric operations journey includes tests/numeric-literals/reconstruction.rs: an actual directory source, malformed UTF-8, unmatched single and multi-edit replacements, invalid hashline coordinates, incomplete replay, missing operations, non-text content, explicit empty writes and matching-source recovery through the built hook. It retains candidate-bound exchanges and checks that guard evaluation leaves the source unchanged.

Before counting out existing literals or unchanged provenance entries, the guard reads the existing file once. A missing file is a new-file case; a directory, permission failure or invalid UTF-8 is not. The refusal names cannot read existing numeric source <path>: <cause>, preserving the filesystem or decoding error. Correct the named source before resubmitting the write; no operator quote is searched to compensate for an unreadable file. The same cause reaches the CLI and harness hook diagnostics. The real numeric_literals_existing_file journey covers refusal on an actual directory, absence after its removal and recovery with readable source, retaining the candidate identity and hook exchanges.

#Unavailable quote evidence is not a missing quote

A new or changed declaration first checks the writing session's transcript. If that file cannot be read or a matching record cannot be decoded, the refusal names the transcript, failed operation and actual filesystem or parser cause. The index search distinguishes an empty successful result from a missing, unreadable or corrupt transcript-index.sqlite, an unsuccessful sqlite3 invocation and an invalid index result. None is reported as a completed search that found no operator quote.

The standalone tama hook check-numeric-literals --verify-user-quote reads the quote from stdin and reports the same cause. Identity provenance and tama rules remove propagate this result too: an unavailable source cannot authorize removing a rule as supposedly unsupported by the operator. Existing hook diagnostics carry the same refusal to the harness; this does not add a separate Desktop evidence reader or qualify a live graphical session.

#Real qualification

tests/numeric-literals/operations.rs invokes the actual built hook in separate processes, records the declared candidate revision and binary digest before evaluation, and retains each payload, command, exit status and output. It covers accepted operations and meaningful numeric refusals, including a Unicode prefix before an index. From the Rust workspace, run TAMA_TEST_SOURCE_REVISION=<full-candidate-revision> cargo test -p tama-cli --test numeric_literals_operations --test numeric_literals_hashline_context. The hashline journey also uses a caller directory distinct from the hook process directory, retains an unchanged real declaration through a whitespace edit, and refuses removal of a required quote field.

The operations target also sends the same Rust lifetime and escaped-character source through a write payload and a multi-file hashline edit. Both must return an empty allowance, and evaluating the policy must leave the fixture files unchanged. This distinguishes literal classification from edit-context reconstruction.

A direct numeric hook refusal is a successful process carrying a structured {"decision":"block","reason":"…"} document and the same cause on stderr. Exit success alone is not policy acceptance; a crashed process is not a policy denial. Numeric operation, raw-string, identifier and existing-file journeys distinguish these outcomes rather than treating a process status as the decision.

The operation, raw-string, identifier and existing-file journeys share an isolated Git fixture under the ignored build directory, never temporary files beside tracked test source or the operator's working directory. The inner Git root prevents the outer build-directory ignore rule from exempting the tested source. Every fixture requires TAMA_TEST_SOURCE_REVISION, checks the actual clean tracked candidate and retains the built binary digest before initialization. Candidate and setup reports and append-only hook exchange logs survive fixture cleanup; repeated calls do not overwrite earlier outcomes. The corresponding targets are numeric_literals_operations, numeric_literals_raw_strings, numeric_literals_identifiers and numeric_literals_existing_file.

The hashline target also includes tests/numeric-literals/provenance/failures.rs: a real directory where a transcript should be, an absent index, and a damaged SQLite file each must produce a causal refusal from the built hook. The ignored positive journey requires TAMA_TEST_USER_TRANSCRIPT, TAMA_TEST_USER_QUOTE and TAMA_TEST_USER_NUMBER from an actual operator message, then verifies that the own-session quote works without an index. Run that case explicitly with --test numeric_literals_hashline_context genuine_own_quote -- --ignored under the same declared candidate; manufactured transcript messages are not qualification of operator provenance.