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.
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.