Changes touching this path

  • loot show becomes the fourth rendering over the one delta seam and grows no differ of its own: it calls Endpoint::base, ws.content_delta and render_delta, produces no hunk, counts no line and matches no content, and the pin is byte equality rather than shape - show ends with exactly the bytes loot diff --content <parent> <sel> produces in both content and stat modes, with the leftover prefix asserted to name no path, hold no hunk header and hold no disclosure line, so a second opinion about the parent or a renderer choosing its own words for the seam's tally both go red. Four breakages proved it, and a fifth attempt was a false negative caught by its author: truncating to the last newline is a no-op, so it passed, which is exactly as misleading as a vacuous test and is why it is named here rather than quietly replaced. Three of this ticket's premises were wrong. The two commands it says reading a change takes are really one command plus reading a parent id out of loot log by eye, because <version-prefix>~1 is not a form the #305 grammar spells and only HEAD~<n> is. Subject does not mean what the criteria assume: the tree defines it as the first line of the message and this repo writes single-line paragraphs, so the first run printed a two-thousand-character subject row - nothing truncates anywhere in the CLI, so rather than invent a rule the subject moves to the last header row, above the diff where git show puts the body, instead of pushing the lineage off screen. And the no-selector arm was ambiguous in a way that made the first message false: loot status prints a live working-change row computed off the disk under ADR 0030 while working_id and the @ selector both say there is none, so saying nothing is in progress contradicted the verb next door about the same tree - the message now names which absence it is, explains the live row, and points at diff and status, and show is deliberately not widened to render anchor against disk, because that delta is bare loot diff's by construction and a second verb answering it under a header claiming a non-durable version id is the duplication #1541 deleted. A merge names which parent it is taken against and prints the command for the other side rather than pretending the diff is whole. PROSE_ONLY_CEILING rises 50 to 51 deliberately with its reason written beside it, which is the mechanism landed one change ago doing what it was built for, and a pre-existing comment claiming the arity gate is a no-op for all but one verb is repointed at the property since it has been four for some time (#1492) 7ff52d8e · dbf3dbe6…
  • loot format-patch lands as the fourth rendering over the one delta seam rather than a second differ: a header block plus Rendering::Patch, which drops the four-space body indent every other rendering files its content under and prints git's extended header, so what comes out is a real unified diff that git apply --check accepts and a real git apply reconstructs byte for byte, proved by applying it in a scratch repo and by a strict applier in the tests that checks every context and deletion line against the base at the line the @@ header declares, since a shape assertion cannot see a wrong range. Two of the ticket's premises did not survive contact. AC3 asks to omit AND NAME, and a sealed row has no name to give: the #306 rule already withholds it, so what is named is the row, its class and content address and tier and the seam's own four-way reason, and the wording of the decision promised a path this export is not allowed to print. And a rendering over seam ROWS alone is not quite sufficient for a faithful patch: the hunks come off blame::lcs_match's line space, which ate both the trailing newline and the carriage return, so the no-newline marker is recovered from the row's raw bytes instead, and a CRLF file's patch is LF, named as a limit rather than closed with the second differ this ticket was rescoped to avoid. One neighbouring behaviour is deliberately not inherited, the sanitizer: a patch exists to reconstruct a file, so caging a control character would emit a patch that rebuilds different bytes than the change recorded, silently, and content therefore leaves verbatim with the usage line saying so. Everything else is read rather than re-derived, the path set and each row's rung and the closing in-the-clear count all being the seam's, and the header is loot show's Header widened by the author pubkey and the whole message rather than a second read of the graph. That header is designed as loot apply-patch's input: its own format number on the first line, full-width ids because a patch outlives the store a prefix would resolve against, absence spelled none rather than the dash every human header uses, and the message folded RFC-822 style so that no body can collide with a delimiter. (#1512) 9b32538e · dbf3dbe6…diff
  • the fourth review sweep finds an enumeration asserted as exhaustive for the fifth time in one run, and the repair puts the RULE where the list was: two questions rather than a set of verb names. what does the key HOLD - a key the graph recorded as a change AUTHOR is mailmapped, while an attester, grantor, grantee, custody holder, manifest signer or admitted pusher holds a capability AT THE MOMENT IT IS PRINTED, and an alias there is a display that reads like an authorization claim. and where do the BYTES LAND - a mailmap is local display state, so it may reach a RENDERING and never an artifact whose bytes are hashed or re-ingested as a record. the second clause exists because of a site NEITHER the ticket nor a grep over the helper would have reached: git_mirror author_identity resolves a key the graph recorded as a change author and does not call that helper at all, so question one alone says mailmap it - and that would make a git mirror commit SHAs depend on a LOCAL UNTRACKED DISPLAY FILE, so two machines reading one repo would build two histories. applying the ticket rule mechanically would have INTRODUCED that defect, which is why the rule ships with two clauses and that site is named as its proof. of the four sites the ticket listed, three are mailmapped and tag is NOT: every key handed to that closure is an ATTESTER, the same key attester_trust decides the listing with, and that is the direction where a wrong call is security-shaped, so it stays put with the reason at the site and a pinned positive control. the four were four hand copies of one function body, which is HOW they drifted without anyone choosing to, and the body is now spelled once so there is no ladder left to copy. the grouping table is DELETED rather than narrowed: the enum grows ALL and a name round trip, parse SEARCHES that list and the refusal joins it, so the accepted set and the advertised set are one list read twice and the eighth vacuous pin becomes UNREACHABLE rather than watched - its replacement is pinned against an INDEPENDENT oracle, the hand written usage line read from source in another compilation unit, so a variant the parser accepts and the usage never mentions now reddens. AGENTS.md gains the class and explicitly NO guard, and the no-guard half is EVIDENCED rather than preferred: the census one would naturally write, over that helper call sites, would have caught four of the five instances and MISSED the git mirror - a guard that reads complete and is not would be one more instance of the class it exists to catch (#1832) 0cd1d291 · dbf3dbe6…diff
  • loot notes lands as a SECOND WRITER ON THE ATTESTATION LANE rather than as a record type of its own, so AC3 and AC6 arrive FREE and the wire does not move: a note is an attestation carrying a reserved role, the sibling of #1508 landmark record in the namespace the TAG field was shaped to admit, so every shipped reader parses the bytes, FORMAT_MAJOR does not move and NO RELAY OR FORGE REDEPLOY IS OWED. the visibility rule is the decision and it is DERIVED rather than chosen: the attestation lane carries no key and no grant list and rides the bundle beside the change it names, so a note audience is EVERY HOLDER OF THE CHANGE including a relay holding no content key - on a wholly-Internal change that audience can already open every byte, so no text ABOUT it can tell them anything new, and that implication FAILS THE INSTANT one path is Restricted or Embargoed. so the verb REFUSES on a change recording any path sealed narrower than Internal and names the paths, while Published PASSES because it is Internal plus world, a WIDER audience. inherit was rejected on its own terms - a Change HAS NO SINGLE VISIBILITY, that is the founding sentence of the glossary, so inheriting means a key granted to the INTERSECTION of holders which goes stale on every grant and maroon - and own-visibility was rejected because it is the only option that can OVER-disclose, making the safe answer an operator choice every time on an append-only already-delivered lane. an allow-leak hatch was declined because it would consent to nothing enforceable. the gate is the unconditional FIRST STATEMENT of the write path above a match exhaustive over the three writing ops, so a fourth does not COMPILE until it has an arm, and the limit is stated rather than implied: the check is at WRITE time, a path demoted afterwards leaves the note standing, and burn is the verb for that. seventeen mutations, each red with the count read, and both risky pins carry POSITIVE CONTROLS - the version-id pin mints a real version three lines below the assertion that notes did not, and the visibility pin has a peer who provably CANNOT open the sealed object reading the note in the clear beside it, each half the control for the other. a smoke run caught a defect this change had introduced rather than inherited: a multi-line note broke the attested-by line at column zero, so the cut is made AT THE DOOR, which covers a multi-line tag message that was latent and never exercised (#1519) 0ec3069f · dbf3dbe6…diff
  • a verb arm now receives the argv door answer instead of the raw slice: VerbRun takes an Admitted value, the rebased tail bound to the spec of the row it was dispatched from, and its readers are the only way in, so argv[0] or argv.first() in an arm fails to compile (both planted, read as E0608 and E0599). spec_of is test-only and its 104 production lookups read through the admitted spec; six of the seven raw-index arms (bundle, grant-status, embargo-status, attest, resolve, completions) read positionals through the door and relay reads its leading word through a named exception; subcommand families resolve their leaf into a new Admitted, and bisect and buoy are handed one ahead of the table. the two named exceptions, leading_word for relay and bisect and pass_through_tail for the bisect run command, plus the one process argv read, are held to a named set of functions by a new census that went red on a planted leading_word read in cmd_bundle, and the source walker consumer header now counts it. refusals and output are unchanged by every suite and by a read of each migrated arm, with one exception found on the way: resolve with a trailing --tool and no command read --tool as the file and now refuses with the usage line, pinned by a new test. CONTEXT.md gains the Admitted argv term; loot-first keeps its own spec_of and DECLARED_READS is untouched (#1929) d92e95f2 · dbf3dbe6…diff
  • review sweep 15 fix-up: the evolog dash is on an empty FIRST LINE, the subject rule now has one home, and the surface prune says plainly that it discards. loot evolog --porcelain dashes its last column when the message first line is empty, which an empty message and a message whose body starts under a blank one both are, and three places said an empty message: the doc above the encoder, the ADR 0023 amendment of 2026-09-17 and CONTEXT.md. The wording moved rather than the bytes, because dashing on an empty message instead is a porcelain byte change for a message that opens with a newline, and a new spawned pin, a_message_whose_first_line_is_empty_is_the_dash_too, puts a run under the sentence: the file reads 5 passed, and with the whole message put back in the column it is 2 passed, 3 failed, the single-line control among the two that stay green. render::subject_line was billed as the one home while six call sites derived that first line inline, the four the ticket named plus the shortlog author rollup and the missing-object listing, so log --oneline, heads, show, range-diff and those two all ask it now, and the near-cousins that each do something more, the note ellipsis, the revert subject and the loot-first PR title, are named beside it rather than routed. The control is the spawned binary: 28 outputs over two repos, one carrying a multi-line message and one a blank first line, are byte-identical before and after, and with subject_line returning the whole message 10 of 11 of them move and the loot-cli lib suite reads 1219 passed, 4 failed, naming the shortlog rollup and the oneline row among them. The surface stale-debt prune keeps the removal error it discards, which #2018 declined and its closing note then reported as fixed, and the doc above it now says the let _ is deliberate, that apply_change_delta propagates where this one does not, and why: the delta writer snapshots, so a refused removal would be recorded as a deletion the disk never took. The removal census in workspace.rs states what its matcher is blind to, a type-annotated binding, a call split over lines and a path derived from a root-joined one, and names disk_entry::drop_empty_parents, whose remove_dir climbs from a dest its caller joined and which the fold cannot reach, because remove_dir takes an empty directory only and a directory holding a file the writer just wrote is not empty; widening the matcher would not have reached that call, so the blindness is stated instead. CONTEXT.md gives that census its crate and spelling scope. migration_serial records that hold blocks and is not re-entrant, and that it serialises migration runs rather than pg_authid, naming the fixed-name CREATE ROLE in tests/rls.rs that writes the shared catalog outside it; Held loses its Option and its Drop impl, since dropping the field is what the compiler already does. The migration census compares a BTreeSet the way the removal census does: with the const reordered it stays green where the Vec compare it replaced goes red (0 passed, 1 failed), and a name dropped from the const reddens it (0 passed, 1 failed). pg/tests.rs cross-references its own source walk and fn finder to source_walk in loot-cli and states where the pair differs. The folds_case helpers in two test files become folds_case_under, so neither shadows the disk_entry rule it asks. The rewrap leftover in account.tsx that #2018 moved rather than fixed is rewrapped with its neighbours, and so is a second one mid-paragraph in CONTEXT.md that the same rewrap reached. The forge suite is green against a throwaway 17.0 cluster (469 passed over nine binaries, no skip note) and its lock case goes red with the session dropped rather than held (0 passed, 1 failed), the site gate is green (643 passed, every surface under its ceiling), and the workspace suite is green (3833 passed) (#2023) c38e3299 · 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.