Changes touching this path
- the opt-in telemetry channel is built, and off means no call because a paired test says so rather than a sentence
loot#1658, building what ADR 0074 specified and loot#1626 shipped only the
guard for. loot telemetry on | off | status | show; an append-only accumulator
that writes one line per dispatched verb, the dispatch-table name, with burn and
maroon collapsed to other AT WRITE TIME so no local trace of an incident exists
either; a sender that runs in main after the verb has printed and decided its
exit code, at most once per 24 hours, behind a 3-second hard timeout, that can
never print, never change the code, and never delay a verb; a spool that IS the
outbox, so show reads the same entry the sender transmits; and a 16-byte id
minted on opt-in and deleted on opt-out, never machine-id.
The guarantee is the pairing. One local listener, the real binary, a sandboxed
config home: init, status, doctor and show with telemetry off make ZERO
connections; on, the same verbs make EXACTLY ONE, and the same verbs again in
the same period make no more; off again makes none. A lone assert-zero passes
when the listener is broken, the binary crashed early, or it pointed at the
wrong port - the exact shape of the logrotate -f check that could never fail.
The endpoint override is https only with the loopback LITERAL the sole
exception, enforced twice, and localhost is refused because what it resolves
to is the resolver business; the refusal happens BEFORE an id is minted, so a
broken override never leaves an identifier that can never be used.
Clause 5 of the field rule is a property of the wire bytes, not a promise:
a test parses every string out of the JSON and requires each to be a
compile-time constant, a member of a fixed array, a dispatch-table name, or
the hex id. The platform is a build-script constant rather than a runtime
assembly from std consts, which is what the rule forbids spelled from constants.
The verb emits machine output - show --json IS the report - so it joins the
emitting tier rather than the prose-only debt list the ceiling forbids growing.
Every count that moved was moved in the documents that state it, because the
censuses read them: 129 verb lines in three sites, 82 dispatched and 80 in the
table and 25 emitting in ADR 0066 with telemetry in the named list, all 82
verbs and the block in the README, the CLI page, and ADR 0074 eight-entry
allowlist - because the endpoint is now the binary ONE destination it contacts
rather than prints, and its ALLOWED row says under what condition.
The destination census flagged three lines that dial nothing: a printed hint
naming http://127.0.0.1 and the two scheme-prefix tests in check_endpoint. They
are respelled rather than exempted - the check splits on the separator and
compares the scheme WORD, same rule, no allowlist row - because a row is a
sentence someone has to be able to defend and these had nothing to defend.
The site half: POST /api/telemetry validates a report against the SAME closed
sets the binary declares, pinned cross-repo by reading the Rust source, and
refuses WHOLE on any field outside them. A valid report becomes source=cli
rows, one per verb invocation under /cli/<verb> plus two bucket rows, all
stamped with the arrival time so no ordering exists to reconstruct; the
telemetry_id rides the row and expires with the 30-day partitions. It sits
behind the same /api/ limit_req the beacon needed, which is why the ADR put it
there. doctor prints on/off, the EFFECTIVE endpoint, last send and the spool.
The notice gains the CLI tier, as ADR 0074 §11 requires: sends nothing unless
you run on, the id links reports and off deletes it, what is never in a report,
show prints it before you decide, and the erasure route - quote your id within
30 days, you hold the key. The test that asserted the page does NOT collect
telemetry is inverted, as it was for the visitor key.
1214 lib and 118 bin tests, 51 suites, 634 site tests, budget and font gates
green, rendered and read.
8a919b01 · dbf3dbe6… - buoy and bisect are counted by telemetry like every table verb: both are dispatched ahead of the table and returned before its note_dispatched call, so a report that says it counts each verb never carried either, and the release that ships the telemetry client would have published that gap. each early path now makes the same call with its own spec name, after the same gates and before the run, so help is still not a run and no new string is introduced. a new integration test runs both verbs and reads them back out of telemetry show beside a table verb as the control, and went red when the bisect call was removed. the dispatch comment and the telemetry module header stop saying every reported name is a member of COMMANDS, which was false for other and is false for these two, and say what holds: a spec name literal or other. items 1 and 3 of the ticket are not touched here (#1945)
11291b75 · dbf3dbe6…diff - the filter in front of the import resolver becomes part of what it refuses, and a dated amendment gets back the figure it landed with: #2148 made the branches of imported_names refuse by default and left a filter in front of them that kept only a line starting use, and a default a filter feeds is a default over what the filter let through, so a module named on a continuation line and a pub use re-export reached no branch at all and were skipped in silence, which is the #1946 under-count surviving the ticket that closed it and then the ticket that closed that, a third time. use_items reads items and not lines, a statement being the run between one semicolon and the next and a use item being the statement the keyword stands in, so a visibility, an attribute and where the lines break decide nothing, and what would make that wrong is a statement spelling the keyword without being one, which costs a refusal naming the line rather than a reach passed over. the call walk stops deciding the same question a second way and reads the code with those items blanked by without_use_items, so an import can never also be read as a call and the use and mod line prefixes it skipped by are gone, the declaration it does look for being found by what it declares. the re-export and the wrapped item are planted as outcomes, one resolving and one refusing, beside the alias #2148 planted, and the blanking is planted with them. the ASCII identifier reading in temp_root_census is gone for census_text is_ident_char, which whole_word_matches is now bounded by too, so where a name begins has one home and the wider letter a narrow reading lets answer as a whole word is planted where that census can see it. ADR 0066 #2127 amendment is restored to 82 in the dispatch table, which is what was true on its date, and the cause is named rather than the number: the_verb_counts_stated_in_prose_are_the_ones_usage_holds held every occurrence of its phrases to the count the code holds today, records included, so the cheapest green was to rewrite the record and #1976 rewrote it; claims_only takes the amendment entries out before that census reads, an entry stating what was true on the date it carries. emit.rs stops saying buoy is dispatched ahead of the table, ADR 0088 stops walking a buoy flags constant that has no references left, three plurals about verbs dispatched ahead of the table go singular where one verb is left, the telemetry pin reads the count instead of a substring of it, since buoy 1 stands inside buoy 10, and the glossary stops spelling the secret-shaped set with two env positions where #1930 made it three. red under mutation, counts read each time: the line shape deciding again which items are read (loot-cli lib 6 passed and 2 failed), an item ending where its line does (7 passed and 1 failed), the shared boundary widened to admit every character (loot-cli lib 4 passed and 4 failed, temp_root_census 1 passed and 2 failed) and narrowed back to ASCII, which nothing caught before this (temp_root_census 2 passed and 1 failed), the ADR records read as claims again (loot-cli bin 0 passed and 1 failed at the control, and with the control stood down 0 passed and 1 failed naming 82 where the code says 83), the cut widened to take every line (0 passed and 1 failed, the phrase it guards no longer stated), and the count reader stopping at the first digit (telemetry 3 passed and 1 failed). item 6 is a correction to two landed commit records and is reported rather than made. no migration, no wire or format byte moves and no host behaviour moves, so this owes no deploy. the workspace suite is green (4062 passed over 129 binaries, 8 ignored) (#2156)
54167f6e · dbf3dbe6…diff - the two shapes that graduated without a record get their amendments, telemetry status and off carry the contract field, and the comma-joined column gets one home: #1971 carries the ADR 0088 section 4 follow-ups and asks for each premise to be re-verified first, so each was read against the tree — ADR 0023 held no amendment for count-objects (#1523) or telemetry (#1658), telemetry status and off printed no contract field and the string dash where V3 says null, and the Verdict entry in CONTEXT.md still typed 17 of the 68 beside a roster where the dispatch table derives 32 emitting verbs and 51 prose-only ones; the ticket comment correcting item 2 is the later word and is taken as such, telemetry on printing report JSON for the same deliberate reason as show. ADR 0023 gains two dated amendments describing what shipped: the one-metric-per-mark census rows, and the name-keyed telemetry rows with the divergences frozen beside them. status and off lead their JSON with contract now and spell an unresolvable spool null, rendered by status_shape and off_shape, which take values rather than reading the config so all three renderings are assertable; show and on stay exempt because what they print is the report the sender posts, and the exemption is pinned over Report::to_json rather than over a verb, that string being what the endpoint receives. The shared flag column item 3 asks for is verdict::joined_col, the comma join with the dash when empty, and what defines membership is that spelling rather than a roster, so the columns spelled that way are its callers: blame, delta_shape, explain, heads, log, evolog, attestation_shape, refit_shape, seek, tag, both pipeline listings and lanes route through it and no frozen shape pin moved. The cross-shape census is declined with its cost recorded in ADR 0088 section 4, since a census over values needs a pub on each shape that #1553 refuses, one over a spawned binary reads nothing for a verb whose arguments it cannot supply, and its exemption table would be a hand-maintained list asserted as complete. Red under mutation, counts read each time: joined_col joining with a tab (loot-cli 1335 passed and 8 failed), the empty set printing an empty field instead of the dash (1324 passed and 19 failed, and loot-core 647 passed and 1 failed naming lanes_porcelain_rows_are_the_frozen_contract), the contract field taken off status and off (5 passed and 3 failed), the spool null spelled as the string dash again (7 passed and 1 failed), and a contract field added to the report the sender posts (6 passed and 2 failed), each restored. No migration, no wire or format byte moves and no host behaviour moves, but two leaves of client JSON move, so this rides the next release and owes no deploy. The workspace suite is green (4107 passed over 132 binaries, 8 ignored) (#1971)
4ef9c62b · dbf3dbe6…diff - the permissive deposit arm gets the pin its defence was resting on, the standing-lane assertion that could not fail gets a comparison, and the declined cross-shape census weighs the instrument it never considered: #2194 carries the sweep 14 fix-ups plus the five a focused review of #2188 found, each premise re-verified on the tree first. item 8 is judged kept rather than closed to match its siblings, because where the seal cannot be produced embargoed_paths keeps the tree entry claim and the tree entry instant, which an unsigned tree can spell 0, and the recorded defence is real, grant_sealed reading the same object through the same door before it seals anything; so the arm stays and both halves are asserted now, the row surviving with the number the entry chose and the grant that row plans refusing at the same address, under a control that the same call succeeds while the seal reads. item 9 is made to bite: a second path over the same Restricted seal is entered honestly, so the standing-lane arm is a comparison rather than an absence, and the comment names the mutation that reddens it. item 10 narrows a pin message that reached past what it asserted, onto the deposit-plan pins that do assert the fan-out. item 1 corrects the ADR 0023 cost paragraph, which named the spool leaf where the endpoint moved too, off an unescaped format string onto the shared escaping, so an endpoint holding a quote or a backslash had been emitting JSON a parser rejects; the matching commit correction is already a comment on #1971. item 3 weighs the text census and records a measurement instead of an assumption: it needs no visibility widening and spawns nothing, so no census can was the wrong shape of decline, but what a text census reads is a spelling and section 3 is a rule about rendered bytes, and measured, the comma-join needle finds its own home and sites that are not columns while the contract field is not spelled one way, so a needle on either spelling reads a subset and says nothing about a shape carrying no such field at all, which is the defect #1971 came here to fix. item 2 replaces a false inference in the land-change skill: those three headings belong to the cargo test gate, and every other gate arrives as a finding under prose of its own, the line-ending gate among them, so an unfamiliar heading is another gate before it is a new kind; workflow.md scopes the same sentence. item 11 takes the completeness claim off the deposit accessor doc, where privacy is what bounds the callers, and off the census block that asserted a key guard over a membership rule which does not check it. item 12 names the two readers still deciding off the entry, the mirror projection and the forge manifest fold, with what bounds each, and records that neither belongs to #2187. items 4 to 7 are the smells: the forwarding closure, the clone taken and then borrowed, the exemption pin that read the real machine config and now builds its report from values, and the reversed import. red under mutation, counts read each time: the unreadable-seal arm dropped (68 passed and 1 failed, the row reading empty), that arm handing on a substituted instant (68 passed and 1 failed, 9999 where 0 belongs), restricted_paths made to ask the seal (68 passed and 1 failed, secret.txt joining owned.txt in the standing lane), a contract field added to the report the sender posts (6 passed and 2 failed) and joined_col joining with a semicolon (loot-cli 1335 passed and 8 failed), each restored to 69 passed and 0 failed. no migration, no wire or format byte moves and no host behaviour moves, and no output byte moves either, every code edit here being a refactor or a test, so this owes no deploy. the workspace suite is green (4110 passed over 132 binaries, 8 ignored) (#2194)
609afd9f · dbf3dbe6…diff - every compiled-in and current-tense loot host moves to loot.build (map #2412, ADR 0099): loot --help names security@loot.build and https://loot.build/trust, ALPHA_PROMISE_URL is https://loot.build/install and the telemetry DEFAULT_ENDPOINT https://loot.build/api/telemetry, so the next release carries them; the site install one-liners, R2_BASE and HOP_BASE (https://dl.loot.build, https://loot.build/dl), the metrics SITE_SCOPE and own-host referrer, the disclosure links on trust, terms, privacy and known-issues, and the install, quickstart and guides pages follow, with the privacy and terms markers moved to 25 September 2026 and re-pinned since readers see the new address; README, CONTEXT.md (the forge door now forge.loot.build), the release checklist, site CI and test fixtures follow, and the two specs carry a note instead of a partial rewrite. the destination census gains build in WEB_TLDS, without which security@loot.build would be invisible to its bare-host clause, and exempts the two shipped lines that tld makes read as hosts (state.build, r.build()) by their exact text; ADR 0074 section 9 says twelve-entry to match. 37 lines still name millerbyte.com outside ADR bodies, evidence, research and scratch, each kept on purpose: dated history, millerbyte.com the site and its @millerbyte/ui tokens, the two noted specs, and relay-era tooling with no successor host, filed as #2438. census red with build dropped from WEB_TLDS, restored; site gate 819 passed and within budget; workspace suite 4559 passed over 142 binaries, 13 ignored (#2418)
c5663115 · dbf3dbe6…diff
Renames are not followed. loot's tree maps a path to an address, so a rename is a delete and an add. This list is the history of the name, not of the bytes.