Changes touching this path
- loot apply-patch lands as the inbound half of patch interchange, and the limit 1512 deliberately left open is CLOSED BY REFUSING: CRLF is refused on both sides, because the two halves are not the same kind of problem. A patch declaring lossy: crlf-normalized cannot reconstruct the bytes the change it names recorded - that is fidelity, read off the exporters own measurement rather than re-derived, since by the time the content is in the file it is LF. The half that actually bites is the LOCAL one: a hunks line space is LF, so writing an applied result back over a CRLF working file rewrites EVERY line ending in it, a whole-file change no hunk describes, that no report would show as more than the lines the patch touched, and that the next capture would sign. Reconstruction was rejected as a guess - the line space carries no endings at all, so an inserted line in a mixed file has nothing to decide from - and documentation as too weak for a silent whole-file rewrite. The cost is named rather than hidden: a CRLF repo cannot use patch interchange in either direction, and the remedy is a bundle, which carries content verbatim because it carries objects. Refusing is also what makes the round-trip pin true rather than approximately true. This is a separate verb from loot apply and not a mode of it, since a bundle carries objects, keys and signed change nodes while a patch carries none of those; dispatch is an exact-name lookup so nothing can collide there, and the place that DOES match loosely is verb_usage, which compares the token after loot rather than a prefix - pinned, because a prefix match would have dragged the whole apply-patch usage line into loot apply --help. Hunks apply strictly at the lines they declare, and what does not falls back to the ADR 0044 three-way against the change the patch names in its own parent row, which is the git am -3 shape and the reason that row is full width; loot-core gains one narrow door, converge::compose, because the theirs side of a patch is text at NO address while three_way_merge exists to pick between two addresses. A contended path is recorded and the run stops with the tree untouched, which makes this the sixth member of the 1506 resume family, and the census is what said so. Three of the ticket premises moved under it. The AC3 re-seal under the local .lootattributes is a SENTENCE and not a step, exactly as 1499 found for restore: the verb writes plaintext and then captures, so the tier is todays local policy by construction, and the export side already carries no tier for it to have inherited - the work was pinning it and saying it in the report. The AC4 no-partial-application rule forced the capture-first snapshot to run AFTER the plan rather than before, found by a fixture rather than reasoned out: a repo holding a path it cannot open has that paths plaintext on disk, capture_uncaptured_edits refuses to re-seal content it cannot read, and with the capture first the verbs own refusal was never reached at all. And a hunk body must be consumed by the counts in its @@ header and never by scanning for the closing separator, because a deleted line whose content is exactly a dash renders as two dashes, byte-identical to it. ADR 0082 freezes the header grammar now that a reader exists, which is why the 1786 fidelity rows went in before this ticket rather than after; the machine shape 1512 expected this ticket to name is deferred, with the reason written into ADR 0066 and PROSE_ONLY_CEILING, because it is one shape shared by both verbs and freezing the import side alone is the same half-contract 1512 declined on its own side (#1513)
d5fba81e · dbf3dbe6… - the cardinal beside a growing list is deleted rather than corrected, and a census now refuses its return: every prose count of the stopping verbs is gone from resume.rs, pick.rs, lib.rs, main.rs and ADR 0080, and no_prose_beside_stopping_states_a_count fails any comment paragraph whose subject is that family and that puts a quantifier within three words of a noun the family comes one of per member. scope is a paragraph rather than a file because a cardinal is ordinary english and this crate is full of correct ones; run over the whole crates tree it selected the wrong sentences and nothing else, in both directions, and both controls sit in the body. the ADR half is amended by hand and left unguarded, said so rather than implied, because the same gate cannot be made quiet over a document whose every paragraph is about that family and which states correct cardinals about other closed sets. the class was also wider than the eleven sites the ticket listed: hand written verb enumerations in change.rs, in resume.rs and in ADR 0080 had gone false on the same day and are now derived or generalised. the detached doc block is reattached to resolve_sub with subcommand_word given its own, and the sweep the ticket asked for found a SECOND instance it did not name, since 1799 inserted STOPPING between the no positionals census and its 45 line doc, leaving that test undocumented ever since; both are repaired and no guard is offered, because a correct sentence attached to the wrong item is lexically identical to a wrapped paragraph. ADR 0082 is brought to house style and pinned, and what is pinned is less than the ticket claimed: 81 of 82 do not open with a numbered heading, only 25 do, so the census holds the three lines all 82 agree on, an h1 title, a blank line and the Status section. buoy --nearest name is kept and recorded in CONTEXT.md as the deliberate git describe match pattern it is, named there as having shipped undisclosed in 1509 so the record carries the finding rather than the flag alone. and apply-patch stops refusing a patch that only DELETES a CRLF file: a deletion writes no result and so rewrites no line ending, which is the entire cost ADR 0082 states, so the check moved below the deletion arm, the ADR sentence was narrowed to match, and the pin carries a modify patch refused over the very same bytes in the same fixture as its control (#1807)
eff3dc92 · dbf3dbe6…diff - apply-patch keeps refusing a stanza that deletes a file whose bytes here are not text, and the asymmetry with the crlf deletion rule is now a recorded decision rather than a comment: format-patch never writes a deletion of a file that was binary on the authors side, since the seam classifies that row as binary and the exporter omits it, so such a stanza is either one loot wrote for other bytes or a hunkless one from elsewhere, and git apply refuses the git spelling of the latter without full index hashes, which apply-patch does not read. ADR 0082 and the comments at the deletion arm and in the module header say so, and a new test pins both halves in one fixture: the export of a change deleting a binary and a text file carries a stanza for the text file alone, the git binary deletion stanza spliced into that patch is refused and nothing is written, and the exported patch then applies over the same bytes. moving the binary refusal below the deletion arm turned it red (11 passed, 1 failed), and letting the seam treat a binary deletion as text turned it red at the exporter half (0 passed, 1 failed). no behaviour changed, and the workspace suite is green (#1809)
1bb3f26f · dbf3dbe6…diff - loot resolve no longer refuses a delete/modify conflict that ours deleted: the #1988 guard reads a conflict record whose ours is its base as a deletion, so a disk with no file there matches it and a file written back there is still refused over, and on a shared primary its refusal names loot describe --allow-primary, the capture that works there. spawned-binary tests on a ferry-bounced two-head home position went red before the change for the ours-deleted resolve and the restored file (1 passed, 3 failed with the primary hint test), and with the deleted-ours arm removed (3 passed, 1 failed), with ours read as bytes whatever the record says (2 passed, 2 failed) and with a position-blind hint (3 passed, 1 failed). the implement brief now takes the job cap as a placeholder, keeps deploys inside the warrant and names SKILL.md, cites only what AGENTS.md says, says which commands need the lane position and gives the CR count and describe in PowerShell too; AGENTS.md gains the cargo fmt ban, ADR 0062 names the per-ticket safeguard that replaced the implement review, and the redeploy commands in workflow.md and diagnose-push-rejected name the new tag, scope the relay override to a forward bump and say the forge takes none, as the scripts repo reads them. ADR 0082 and apply-patch say the text deletion arm checks no preimage either (#1992) and pin that --check refuses a binary deletion the same way, the line-ending docs say CRLF and NUL, nul_offenders is private, and the sweep 10 wrap leftovers are fixed. the workspace suite is green (#1993)
7ecaa467 · dbf3dbe6…diff - loot apply-patch no longer deletes a file its deletion stanza does not describe: the deletion arm now checks its preimage, so the lines the stanza removes, with its no-newline marker, must equal the file on disk in the patch line space or the whole run is refused naming the path and why, a stanza with no hunks deletes only an empty file, and --check gives the same refusal. the binary test is now the exporters own delta::is_binary, so a NUL-bearing file is refused as binary too. spawned-binary tests went red before the change (1 passed, 4 failed), with the preimage check removed (2 passed, 3 failed), with the old utf8-only binary test (4 passed, 1 failed), with a hunkless deletion always applied (4 passed, 1 failed) and with the trailing newline ignored (4 passed, 1 failed), and the format-patch round trips of a text file, a file with no trailing newline and an empty file stay green. the comparison is in the line space, so the #1807 CRLF deletion still applies; its fixture now holds the lines its stanza deletes, which it did not, and comparing with the carriage returns kept turned that pin red (11 passed, 1 failed). ADR 0082 section 2, the #1809 and #1993 comments and CONTEXT.md state the preimage rule, and the workspace suite is green (#1992)
98894a0a · dbf3dbe6…diff - loot apply-patch no longer empties a renamed file: a rename stanza now reads its preimage from its old path, so a pure rename moves the old bytes unchanged and a rename with hunks applies them strictly to the old content, the whole run is refused naming the path when the old path is absent, binary or not the content the hunks were made against, or when the new path already holds other bytes, --check gives the same refusal, and the old path is removed only after every new path is written and not when another stanza of the patch writes it. a git copy row is refused by name, since format-patch writes none and it used to write an empty file at the copy path. before the change a pure rename exported by format-patch applied as an empty new file with the original deleted and exit 0, a rename with an edit failed with a garbled error, and the first seven spawned-binary tests went red (0 passed, 7 failed); with the pure rename reading the new path (5 passed, 3 failed), the new-path check removed (7 passed, 1 failed), an absent old path read as empty (7 passed, 1 failed), the old-path binary check removed (7 passed, 1 failed), the hunks ignored (6 passed, 2 failed), the copy refusal removed (7 passed, 1 failed), the old path never removed (5 passed, 3 failed) and the guard for an old path the patch also writes removed (7 passed, 1 failed). the existing format-patch and apply-patch round trips stay green, the split literal in the conflict error is joined, ADR 0082 section 2 and CONTEXT.md state the rename rule, and the workspace suite is green (#1995)
cb93ea4d · dbf3dbe6…diff - loot apply-patch closes the preimage gaps #1992 and #1995 left: an add is refused over a path holding bytes other than the ones it adds, which it used to write in front of them, a modify stanza with no hunks is refused, which wrote an empty file at an absent path, a deletion of an absent path is refused rather than reported as done, and a patch that writes or deletes one path in more than one stanza or renames one old path twice is refused. the similarity index, dissimilarity index and index rows are read past, so a git format-patch -M rename applies as a rename where it used to write an empty file at the new path, and another unread row above hunks is refused by name. apply_strict now reads a deleted last line without the no-newline marker as a claim that the file ends with a newline, the deletion arm compares through apply_strict, and the writer no longer discards a removal error. ten new spawned-binary tests went red before the change (deletion 5 passed, 2 failed; preimage 0 passed, 4 failed; rename 8 passed, 4 failed), a new pin for a deleted file that grew a line holds the unified deletion check, and over the apply-patch and format-patch test files the pins went red with the add check removed (46 passed, 2 failed), the hunkless modify refusal removed (47 passed, 1 failed), the git rows unread (47 passed, 1 failed), the unread-row refusal removed (46 passed, 2 failed), an absent deletion planned as a delete (47 passed, 1 failed), the shared path check removed (47 passed, 1 failed), the shared rename source check removed (47 passed, 1 failed), the newline claim removed (46 passed, 2 failed) and leftover lines ignored by the deletion check (47 passed, 1 failed). the format-patch round trips stay green, ADR 0082 gains section 4, one table of what each stanza class checks, its rename paragraph no longer claims more than the hunks compare, CONTEXT.md states the rules, and the workspace suite is green (#1997)
15e2d13e · dbf3dbe6…diff - loot apply-patch refuses a patch that deletes a path and also renames it away: refuse_shared_paths now counts every path a stanza removes, as a deletion or as a rename old path, and refuses both rows, where the run used to move the file, remove the old path once and discard the second NotFound, and a row it refuses that was already refused keeps its first reason with the shared one appended. a rename old path that another stanza writes stays allowed, as #1995 pins, and ADR 0082 says why and where git apply differs; the rename writer now reports every removal error. the offer_cost double counts account_of, and the two constant-cost cases pin it at the one read Reader::new makes since #1955. the apply_strict comment and ADR 0082 no longer say an unmarked kept last line claims nothing: format-patch writes one when only the new side ends with a newline, so it stays unchecked, and a hunk that adds no line loses that newline. plan_one shares one helper for the rename and add verdicts, the ClassReads, ingest_cost and offer_cost doubles and Reader::new have docs true to the code, no shared base double is added since the conformance double delegates every method, and the CONTEXT.md, ADR 0077 and ADR 0082 rewrap leftovers are fixed. before the change the new rename test went red (12 passed, 1 failed), as it did with deletions uncounted, with the deletion refusal removed and with the rename refusal removed (12 passed, 1 failed each); the kept-reason test went red with the first reason overwritten (8 passed, 1 failed); the account pins went red with a per-object account_of read in readable_rows (11 passed, 2 failed), which the old double let pass (13 passed), and with the owner arm reading nothing (11 passed, 2 failed). the workspace suite is green, and the loot-forge suite is green against a throwaway cluster with the CI env block and LOOT_FORGE_TEST_REQUIRE_DATABASE (#2006)
f16115c2 · dbf3dbe6…diff - loot apply-patch no longer deletes the file on a case-only rename where the filesystem folds case: the writer now compares a rename old path with the paths it wrote by file rather than by name, so a rename from A.txt to a.txt renames the old path on disk, which keeps the bytes and gives the last name component the new case, where it used to write a.txt, which was A.txt, then remove A.txt and exit 0 with no file left, and a rename with an edit whose new path is its old file is no longer refused as a new path already holding bytes. refuse_shared_paths keys a path that exists by its file, the device and inode on unix and the canonical path elsewhere, and a path that does not exist by its name, lowercased when the root reads .LOOT as its .loot, so two stanzas whose paths differ only in case, or two renames to such new paths, are refused there and the refusal names both spellings, and an old path kept because another stanza writes it is kept whichever case that stanza spells it with. on windows two hard links still give two canonical paths, so the new hard-link pin runs on unix only. before the change the case-only rename lost the file through the spawned binary and the rename tests went red (13 passed, 4 failed); they went red with the case-only rename removed from the writer (16 passed, 1 failed), with the writer comparing by name (16 passed, 1 failed), with the new path of such a rename read as a file (16 passed, 1 failed), with the case probe never folding (16 passed, 1 failed) and with the shared paths keyed by literal name (15 passed, 2 failed); with the file key skipped they stay green here (17 passed), since the case fold answers the same on this filesystem. the case-sensitive halves ran green under a directory marked case-sensitive (17 passed). ADR 0082 section 4 states the aliasing rule and its limits, CONTEXT.md names it, the unix file id is spelled as paths so the destination census does not read it as a host, and the workspace suite is green (#2007)
f7c20944 · dbf3dbe6…diff - loot apply-patch no longer keeps the old name of a rename onto a hard link: #2007 renamed the old path onto the new one whenever the two were one file, which rename(2) does nothing for between two hard links, so the old name stayed and the run exited 0. the writer now renames only when the two paths are one directory entry, which on windows is two paths with one canonical path and on unix two names of one inode that differ only in case and are not both listed in their directories, and otherwise writes the new name and removes the old one by name, and an old path is kept only when a row writes that directory entry, so a rename from a hard link of a path another stanza writes removes the old name too. a stanza whose path or rename old path is or passes through a symbolic link is now refused, checked component by component with symlink_metadata, since a write through a link lands at its target and the file identities follow links. PathKeys and PathKey replace Names and Alias, refuse_shared_paths asserts one row per stanza, the removed-twice refusal words are one const, honor_burn says it is pub(crate) only for the server.rs agreement test, and the purge.rs, ingest.rs and ADR 0082 rewrap leftovers are fixed. the Rotate card on the account page now says a leaked key stops pushing and proposing rather than writing, that the retired-key refusal covers burns, and that this forge does not check retirement on every signed request, naming withdrawing an authored proposal beside revoking a grant as examples, and its JSX comment states the forge-before-site deploy order ADR 0038 gives. the new hard-link rename tests pass on windows, where the old code already passed them, and their unix red-first has not run here; with the unix entry rule compiled on windows the rename tests stay green (20 passed) and go red with the new name read as listed (18 passed, 2 failed), the windows rule answering two entries goes red (18 passed, 2 failed), and the symbolic-link test, which uses a junction here since a file link needs a privilege, goes red with the refusal disabled (19 passed, 1 failed) and with only the last component checked (19 passed, 1 failed). the unix code type-checks for x86_64-unknown-linux-gnu in a scratch crate, ADR 0082 section 4 and CONTEXT.md state the entry rule and the link refusal, the site gate is green and the workspace suite is green (#2014)
ab00ecd2 · dbf3dbe6…diff - the tree writers no longer remove the file they just wrote on a recorded case-only rename: materialize_target and materialize_promoted wrote the target tree and then removed each old path the new tree no longer holds by name, so where the filesystem folds case, as this windows machine and a default macos volume do, the removal took off disk what the write had just put there, and loot undo over such a rename reported pruned 1 path off disk, left the directory empty and made the next status read the recorded path as deleted, which loot move and converge did too, each reproduced by hand with the pre-change binary. both writers now prune through loot_core::disk_entry::prune_unwritten, the one home for the file and directory-entry rule apply-patch has taken since #2007 and #2014: an old path that is the directory entry of a path just written is renamed onto the written spelling, which keeps the bytes and gives the last name component the recorded case, the old name of a hard link is still removed by name, NotFound is a path that was not on disk rather than a path pruned, and any other removal failure is an error naming the path, where materialize_promoted discarded the error and materialize_target counted a failed removal as pruned either way. the identity helpers move out of apply_patch.rs into the new module, drop_empty_parents is shared with the view prune, and the prune makes no filesystem call beyond its removals unless an old path and a written path fold alike. the new pins went red with the entry rule removed (cli 1 passed, 3 failed; core 4 passed, 1 failed), with the old path kept and not renamed (cli 1 passed, 3 failed; core 4 passed, 1 failed) and with the removal error discarded and counted anyway (core 3 passed, 2 failed; the cli pins stay green, since none of them fails a removal). the perf gate dry run reads every counter the same before and after and says no move, ADR 0022 carries the amendment, ADR 0082 section 4 says where the rule lives, CONTEXT.md names it beside the apply-patch rule and in the re-pointing entry, and the workspace suite is green (#2011)
de9fb5b0 · dbf3dbe6…diff - cherry-pick, revert and a surface that settles a stale-disk debt no longer destroy a case-only rename where the filesystem folds case, and the first no longer records the loss: #2011 routed two tree writers through loot_core::disk_entry and wrote that every writer which removes a path it may just have written asks that module, while two that did not were sitting in the tree as the sentence was written. Workspace::apply_change_delta, the one core cherry-pick and revert share, walked the merged actions as a BTreeMap and so wrote A.txt and then removed a.txt by name, which where case folds is the file it had just written, and it snapshots, so loot revert over a recorded case-only rename reported both paths converged over an empty directory and minted a change recording 1 deletion. Workspace::stale_debt_the_surface_left, the one scoped deletion a surface is allowed, compared the written set by name, and a case-only rename preserves the bytes, so the digest guard that exists to protect the operator matched the file the surface had just written: loot surface printed the path it had surfaced, left the directory empty, and the next loot status read it as deleted, which is the reproduction #2011 declined on the false reading that a surface prunes nothing. Each was reproduced by hand through the spawned binary first and then as a new spawned pin, and the pair went red over an empty listing before the change (4 passed, 2 failed) and is green after (6 passed); with the delta writer put back to removing by name they go red again (5 passed, 1 failed) and so does the new census (0 passed, 1 failed), and the stale-debt prune put back reddens the same two with the same counts. Both writers prune through prune_unwritten now. Which working-tree removals are weighed stops being a sentence: workspace.rs carries a census that reads every removal joined onto a working-tree root out of loot-cli/src and loot-core/src, names what each one removes and what the walk cannot see, and it named the offending function under each of those two mutations, went red at its own classifier control with ROOTS emptied and went red with a name dropped from its expected set (0 passed, 1 failed each). one_entry refuses a path that is or passes through a symbolic link now, since file_id follows links and on Windows two paths with one id were otherwise one entry, so fs::rename would have moved a link over the file it points at; the check is the symlink_on apply-patch already made, moved into the module, pinned with a junction here and red without it (5 passed, 1 failed). The hard-link pin names its links A.txt and a.txt so the fold map reaches the entry rule at all, where old.txt and new.txt never called it, and it skips a filesystem that folds case with a note; it stays unix-only and its red-first has not run here, though the body type-checks and runs on windows with the cfg lifted (7 passed). The four case probes become one loot_core::disk_entry::folds_case, and with it answering no the entry pin and two apply-patch case pins go red (5 passed, 1 failed; 18 passed, 2 failed). enclosing_fn moves into source_walk beside the #1929 census that asked it first and strips any visibility, after pub(super) made the new census name the wrong function. ADR 0022 carries the correction and its prunes-nothing claim is gone, ADR 0082 section 4 states where the probe and the link refusal live, CONTEXT.md names both writers and the census, the surface.rs and materialize_target notes stop claiming the two prunes answer the same set of failures, and the rewrap leftovers in ADR 0022, CONTEXT.md and the account page are fixed. The perf gate dry run reads every counter the same before and after and says no move both times, the site gate is green, and the workspace suite is green (3824 passed) (#2018)
baec4112 · dbf3dbe6…diff - a modify stanza needs a preimage here, and a patch carries the ending of the line it shows: #2005 read a modify aimed at a path absent here as an empty file, so hunks that applied to nothing wrote a new file with not a line checked and hunks that did not reached the three-way against a base this tree never held, which format-patch never writes and git apply refuses by name, so the modify arm refuses naming the path and --check answers the same, both being one plan. the trailing newline is one cause seen from two ends: str::lines is not injective, so the seam gives the two sides a last line they can share while their endings differ, and the marker is a claim about one side, so a shared last line came out as context with nothing under it and the file was rebuilt with whatever ending the applier already had. format-patch now renders over a line space where an unterminated side carries the fact on its last line, render::patch_line_space, which is the git token of a line and its terminator, and takes the alignment again there, the same matcher on a different token and never a second differ, paid only where a side is unterminated; a last line whose ending changed then leaves as a removal and an addition, which is what git writes, and an ending that moved far from every other change gets a hunk at the end of the file. apply-patch reads an unmarked old-side last line as a claim of a trailing newline on a kept line as on a removed one, and takes the ending of the result from the last line it wrote, kept or inserted alike. a change with no line-level difference stays withheld, and its omitted row now carries the reason and the remedy: this body quotes exactly the rows the seam counted as disclosed, so minting a hunk for a row it counted as summarized is that safety claim coming apart, and emitting it means moving the seam and diff --content with it, which is the wider change to make if it is reopened. ADR 0082 section 3 records both decisions, section 4 states the rule the modify row now enforces, and CONTEXT.md says where the ending rides. red under mutation, counts read each time: the absent-path refusal removed (apply_patch_preimage 4 passed and 1 failed), the re-alignment dropped so the seam hunks are rendered over the marked lines (patch_trailing_newline 2 passed and 1 failed, the older marker pin still green at format_patch 12 passed), the mark itself removed (format_patch 11 passed and 1 failed), the ending check narrowed back to a removed line (2 passed and 1 failed), the kept line deciding the ending only when marked (2 passed and 1 failed, and uncaught at 3 passed and 0 failed until the shape whose result ends on a kept line was added), and the omitted row put back to its old words (2 passed and 1 failed). no migration and no store or wire byte moves, and the patch grammar is untouched with no row added or removed, so PATCH_FORMAT holds and this owes no deploy. the workspace suite is green (4066 passed over 130 binaries, 8 ignored) (#2005)
535e1674 · 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.