Changes touching this path
- docs: concurrent-agents playbook, lane/harbor vocab, docks->lanes migration (#234)
12aa8264 · dbf3dbe6… - feat(loot-cli): no-arg `loot adopt` — harbor catch-up merge (#250, ADR 0034)
22524137 · dbf3dbe6…diff - gc roots the whole shared store and catch-up verbs ingest the harbor lineage, so a lane-landed change the primary never adopted fast-forwards instead of merging, duplicating, or being pruned (#265, #263 prevention)
4c03b44c · dbf3dbe6…diff - drift guard: a mirror merely AHEAD of a stale origin/main is quiet, not DIVERGED (#273)
The guard fired the loudest warning it has — "DIVERGED — do NOT land" — on the
single most common healthy path: between a land and the checkout's next
`git fetch`. A guard that cries wolf on the normal path is one the operator
learns to scroll past, which is how the real #243 case (a lane projecting
backward over landed work) would eventually slip through. Its value is its
rarity.
The pure policy half was already correct and unit-tested; the bug lived entirely
in the impure oracle that computed `Ancestry`, which had no tests at all.
- `Ancestry::MirrorAhead`, probed by asking BOTH directions. Each direction is
asked of the repo guaranteed to hold both commits when that answer is true:
"ahead" of the mirror (which always holds its own lineage — the checkout may
never have fetched the tip, which is why "ahead" collapsed into "diverged" by
construction), "behind" of the checkout. Ahead is quiet — it is the normal
post-land state.
- `land`/`tag` refresh `main` from the remote before judging; `status`/`review`
stay local and cheap. This pays for `MirrorAhead` being quiet: a stale
tracking ref could otherwise hide a `main` that moved under us at exactly the
two verbs that must not miss it. Falls back to the tracking ref, with a note
rather than in silence, when origin is unreachable.
- `is_ancestor` captures output, so a missing object no longer sprays a raw
`fatal:` that reads like a real failure.
The refresh FETCHES rather than `ls-remote`s, deviating from the ticket's
suggested fix 2: ancestry needs operands git can walk, and a bare sha is not one
— the checkout may never have seen that commit, so the behind-probe would fail
into a false Diverged on precisely the fresh break-glass push the refresh exists
to catch. Caught in review; regression-tested.
Behind and diverged still warn exactly as loudly. `mirror_ancestry` and the
refresh now have direct tests over real temp repos — the previously untested
half — covering same/ahead/behind/diverged/missing-object, plus the case the old
doc claimed was undecidable (ahead when the checkout lacks the mirror's tip).
Verified live against the ticket's repro: the old binary says DIVERGED, the new
one is quiet on identical state, while forged diverged/behind states still warn.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0c808aa8 · dbf3dbe6…diff - merges refuse to sign an un-described change too, closing the #174 residual (#275)
#174 stopped the *deliberate* finalize from signing a nameless change, but three
verbs still sealed the operator's own work in passing, to make a signed merge
parent: `dock merge` and the `adopt` catch-up (via `fold_line_in`), and `ferry`
over a git main that moved (via `reconcile_capture`). Only the *trigger* was
mechanical — the content was authored — so the placeholder still reached main.
Reproduced against the pre-fix binary: a ferry over unnamed disk work landed the
literal subject `(working change)` on git main in one silent pass.
Both paths now refuse an un-described merge parent, below their capture (edits
held, only the signature waits) and below a redundant-capture drop (a pass with
no real work to sign stays a no-op, never a nag). Two reasons the ADR gave for
NOT doing this were checked and dropped: a land never reaches these paths dirty
(it finalizes before it ferries), and #219's refuse-on-dirt rejection was about
being forced to capture, not about being asked for a name.
The merge *nodes* are untouched — they are machine-authored and already carry an
honest mechanical subject. That is the line: mechanical content may be named
mechanically, authored content may not.
- `refuse_if_undescribed` (one rule, two messages) + `drop_capture_if_redundant`
(the empty/duplicate drop, previously copied at two sites and missing at a
third — which is what would have made `dock merge` nag).
- Cost, documented: uncaptured dirt takes two passes, since naming *is*
capturing. `loot-first review` asks for a name only when it must merge.
ADR 0030 amendment (correcting the two wrong claims), CONTEXT, workflow, and
concurrent.md updated. Closes #275.
aced27c1 · dbf3dbe6…diff - review refs carry the position, not the dock (#281)
Every lane's home dock is main, so dock-named review refs made N
concurrent lanes share one review/main branch: the second lane's
ferry --with-wip force-pushed over the first's in-flight PR head,
and either position's reap pass could misjudge (and retire) the
other's live entry, since liveness reads the positional working
pointer a foreign position cannot see.
The review lane is now keyed by its owner position end to end:
- ferry projects review/<lane-id> from a lane, review/<dock> on the
primary; the wip and pr-map ledgers gain an owner column (- =
primary; pre-#281 short rows parse as primary-owned) and the
review line carries owner=.
- Reap is owner-scoped: only the owner judges liveness; a foreign
pass reaps exactly the entries whose owner lane is gone from the
registry, so an abandoned lane's review ref dies with it instead
of leaking.
- land derives the collapse branch from the pr-map lane's owner and
refuses to run from any other position: it finalizes the current
position's working change, and the dock guard cannot catch that
mismatch when every lane's dock is main.
Docs: ADR 0033 amendment, concurrent.md (review projection is now
genuinely parallel-safe), workflow.md, CONTEXT.md review-lane entry.
aa68437a · dbf3dbe6…diff - docs: review-before-sibling-lands gotcha in concurrent.md (#289 land rider)
Discovered landing #289: if git main moves between a lane's describe and
its first loot-first review, the review's own ferry seals the described
WIP as a signed merge parent (#275) and then finds nothing unsigned to
project - the change is signed, merged, and PR-less, and land refuses
without a pr-map entry. Document the ordering rule (review right after
describe) and the recovery (plain ferry, then a follow-up review round
carries the sealed work into the PR diff).
b745b6c4 · dbf3dbe6…diff - converge: three-way deletion-vs-base rule (#295) (#300)
merge_trees had no deletion-vs-base case: a path in theirs but not ours was
always AdoptTheirs, and a path in ours but not theirs was never visited (the
walk only iterated theirs). Either way a one-side deletion since the fork was
silently undone -- it fired twice while landing #288, re-adopting freshly
deleted files from the sibling line.
Apply the standard 3-way rule against the base merge_tips already computes:
- one side deleted + other unchanged-from-base -> the deletion wins (both
directions; a symmetric ours-only pass covers the path theirs dropped);
- one side deleted + other edited-since-base -> a delete/edit Conflict,
recorded so it surfaces through the harbor bounce (ADR 0036) rather than
silently resurrecting or deleting (base content stands in for the deleted
side's missing oid);
- base lacks the path -> a genuine add, adopted as before.
Unchanged-ness: address equality first, then plaintext via the key oracle
(re-seals mint fresh addresses, #65/#98); an unopenable side keeps the
conservative pre-#295 keep/adopt. New same_content helper.
Tests: converge unit tests for both delete-vs-untouched directions (incl. a
reseal-address case), both delete-vs-modified conflict directions, delete-vs-
delete clean, base-lacks-path add, unopenable conservative, and a classify
theirs-side case; engine merge_tips tests proving the one-side deletion is
honored (both orders, clean) and a delete/edit collision surfaces a conflict.
Amends ADR 0028 (new #295 amendment) + CONTEXT.md Convergence-classifier and
Adopt entries + concurrent.md, retiring the "the merge is what resurrects
files deleted upstream" caveat for the reconcile/adopt merge.
Git-Author: Connor Miller <53197564+Connor-Miller@users.noreply.github.com>
9fed0444diff - Docs: post-break-glass ferry is mandatory even for loot-originated content (#297) (#299)
The #291 site work originated as a loot change, went to GitHub via a
break-glass squash-merge, and the resulting squash commit was never
ingested into the remoteless mirror -- so every subsequent land
hard-stopped on DIVERGED. The break-glass ferry rule read as skippable
because loot already held the content; the lesson is that it is the
*commit*, not the content, that must be ingested.
- concurrent.md: expand the break-glass recovery section -- ferry is
mandatory even for loot content (mirror is remoteless), GitHub
merge/squash-merge buttons are themselves break-glass, plus the
divergence signature and the #297 recovery recipe (force-fetch
checkout main into the mirror with rollback sha recorded, primary
ferry, land the parked PR through the harbor bounce).
- workflow.md: flag the GitHub merge button as break-glass in the
git->loot residual list and the guard rails, linking the recovery.
Closes #297.
Git-Author: Connor Miller <53197564+Connor-Miller@users.noreply.github.com>
271581ebdiff - Catch up to git main 809ddfe: #260 smoke default, #271 arm64 build, loot diff (#1), ADR 0034 dock retirement (#253)
587684dd · dbf3dbe6…diff - Fix pr-map ledger lost-update: serialize writes under pr-map.lock (#336)
review and land both did read -> mutate -> whole-file rewrite of
.loot/git-mirror/pr-map with no lock, and land's read happens minutes before
its write (tests, ferry, push) - a land clobbered all three rows sibling
reviews recorded mid-flight (live, 2026-07-18).
Every ledger write now funnels through one door, update_pr_map: take
git-mirror/pr-map.lock, re-read fresh, apply only this operation's own row
add/remove, replace atomically. Early reads in review/land are lookups only,
never written back. The ledger lock is deliberately separate from the harbor
lock: reviews must not queue behind a land's git-main section, and land's
ledger close-out runs after harbor release anyway.
The write is store::atomic_write like every replaced git-mirror spine file
(#307), and readers (orchestrator + loot lanes) use store::read_replaced so
the Windows rename-replace window (#293 tail) cannot read as an empty
ledger. HarborLock::acquire_contending carries a caller-supplied contended
message so a wedged ledger never claims to be a wedged harbor. ADR 0034
amended; concurrent.md ownership note refined.
11addd8f · dbf3dbe6…diff - Bounced-land resolutions inherit the described ours-line subject (#337)
fbcb501d · dbf3dbe6…diff - ADR 0039 build: pure-projection review + carry-at-land (#362)
Review mode (`ferry --with-wip` / `loot-first review`) is now a pure
projection: no ingest, no dock reconcile, no mirror-main advance, no
spine rewrite - it mints the provisional commit from the lane's own
anchor marks and pushes only review/<position>. A lane behind git main
reviews normally; REFUSE_REVIEW_STALE_ANCHOR is deleted with the fold
it guarded (#292/#302), and the #349 review-mode trigger is
structurally gone.
Reconcile lives only at the signing verbs, and its diverged-line shape
is now the carry (DagRepo::carry_line): a self-authored suffix replays
onto landed main as superseding versions - same change id, same
subject, single parent, stale original kept as predecessor - so landed
history stays exactly one commit per change with no ferry:
1412f811 · dbf3dbe6…diff - Build the seal-WIP guard (#418): refuse a bare sync verb that seals live described WIP
Graduated from #356's "Prevent + hint" resolution (map #354). After ADR 0039
(#362) made review a pure projection, the only way to strand a described
working change as a PR-less signed line is a *deliberate* bare sync verb.
Guard that path at its source.
- New typed refusal RepoError::SealWip { subject, verb } in loot-core, a sibling
of MisSeal/Demotion, on the ADR 0030/0038 guard+override pattern. Overridable
with --seal-wip on both verbs.
- ferry seal path: thread seal_wip through ferry::run -> reconcile_onto; the
Merge-with-wip arm (the ONLY place a bare ferry seals — land pre-finalizes,
adopt folds through fold_line_in) refuses unless overridden. reconcile_onto
now returns the sealed subject so the caller reports the seal from the seam
that decided it, not a post-state heuristic.
- adopt seal path: thread seal_wip through adopt_harbor; refuse before
fold_line_in when a described line diverged. The clean fast-forward and
redundant-capture drop return earlier, so a no-op catch-up never trips.
- Fires ONLY on a described line the sync would actually fold: an un-described
one stays the #275 refusal; a break-glass ferry / no-op sync (no live
described WIP) is untouched.
- On override the verb prints the follow-up-round recovery recipe
(SEAL_WIP_RECOVERY, one shared seam); the review "nothing to review" and land
"not in the pr-map" paths print it too when sealed_unlanded_anchor() detects a
signed line ahead of mirror main with no PR.
- tag's ferry passes seal_wip=false — it is not a finalizer, so it refuses
rather than silently seal live WIP while cutting a release.
- docs/agents/concurrent.md §"One seal path remains" rewritten to describe the
guard; CONTEXT.md gains the "Seal-WIP guard" vocabulary entry.
Tests: guard at both seams (refuse without / seal with --seal-wip), un-described
stays #275, no-wip catch-up untripped, full ferry::run break-glass + no-op pass
untripped, sealed_unlanded_anchor signal. Full suite green (loot-core 337,
loot-cli 243, loot-first 69); the describe_contention CPU-load flake passes
in isolation.
42409371 · dbf3dbe6…diff - Evidence (#359): a three-lane wave lands with zero orchestrator surgery
The terminal ticket of map #354. Prove the destination live: a real wave of
lanes over one shared store reviews and reconciles with no respawn+copy, no
hand-merge, no folklore. A hermetic, re-runnable PowerShell script drives the
wave through real `loot` verbs; its captured run is committed beside it.
- docs/evidence/scripts/wave-proof-lanes-demo.ps1 + runs/wave-proof-lanes-demo.txt
(run 2026-07-21, 23 checks, all pass). Seven acts on three lanes forked from
one base: (1) three reviews open as pure projections, three review/<lane> refs,
main unmoved; (2) an interleaved out-of-wave land moves main mid-wave; (3) a
stale-anchor review refreshes byte-identically (op=up-to-date, same sha, WIP
intact) — the REFUSE_REVIEW_STALE_ANCHOR respawn family is structurally gone;
(4) the #418 seal-WIP guard: bare `loot ferry` and no-arg `loot adopt` both
refuse to seal described WIP, and --seal-wip seals on purpose + prints the
recovery round; (5) the colliding lane catches up over the moved tip, bounces
on the shared path, and reconciles IN-LANE with the resolution folded into its
subject (#337); (6) a disjoint lane catches up with no merge cost.
- The harbor land-bounce and the #349 "already-projected -> proceed" path live
inside `loot-first land` (shells to gh), so they cannot run hermetically. ACT 7
runs `cargo test -p loot-first --lib` live (69 passed) — harbor_guard_*,
already_projected_line_ahead_of_origin_reads_as_landable, the gate suite — as
the cited proof of the GitHub-facing layer. docs/evidence/wave-proof-lanes.md
is candid about what is proven live vs cited.
- docs/agents/concurrent.md gains the explicit "Running a wave" framing (#358's
"Nothing new": N lanes fire land, queue on the harbor, carry handles the moved
tip — no wave verb, no orchestrator), pointing at the evidence run.
The script gates on the release build's exit code, not just the binary's
presence, so a failed rebuild can't run a stale binary and claim the guard.
ac4b6814 · dbf3dbe6…diff - land-change: make the skill a thin pointer to workflow.md (#511)
b4d26432 · dbf3dbe6…diff - Give a lane a session owner, so gc and rm refuse someone else's (#532)
47af2987 · dbf3dbe6…diff - Landing moves the shared primary: correct the catch-up command, warn about readers (#618, #616)
62a99602 · dbf3dbe6…diff - Guard against committing a path under .claude/worktrees/ (#623)
6bf804b5 · dbf3dbe6…diff - concurrent.md: slicing a wave inside one crate - shared vocabulary is the collision surface (#693)
23d33843 · dbf3dbe6…diff - a view step refuses over work no change records, and a primary finalize says so on stderr (#436)
a3248ede · dbf3dbe6…diff - a land from the primary refuses when the git checkout is parked on another branch, so a projection can no longer write across someone else's work (#618)
Level A of #618, first half. `reconcile_onto` materializes the reconciled tree
over the running position's disk; from the primary that disk is the shared
checkout, the one position that also carries a .git. Parked on another session's
branch it takes landed content across their working copy — observed live, the
primary ended up holding main's content against research/tracker-prior-art, and
only luck (their work was committed and pushed) made it harmless.
`projection_target` (policy.rs) decides from two facts: the position running the
land, and the branch its checkout is on. A lane's projection writes its own tree
and leaves the primary for its own `loot adopt` (#265), so the guard is silent
there rather than crying wolf on the normal way to work. Off-branch from the
primary refuses, naming the branch; --allow-off-main is the break-glass. An
unreadable branch (detached HEAD, no .git, a git that failed) proceeds but says
the guard did not run, so a broken git cannot mute it silently.
The refuse/proceed decision is carried out on Gate::Proceed's new `note` rather
than re-derived by the caller, so what gets reported cannot drift from what was
decided.
Level A's second half — refuse a primary tree dirty with content that is not
this land's — is deliberately absent: from the primary the implicit snapshot
(ADR 0030) has already captured that dirt into the landing change, and dirt
arriving after the review round is already refused by review_currency (ADR
0033). The residual, recorded in policy.rs: a land with no reviewed_version
takes Currency::Current unconditionally, so that path is still unguarded.
Levels B and C remain open.
c1148309 · dbf3dbe6…diff - the ledger reap gains an authoritative half, so a row whose change id stopped being the work's id can still be cleared (#793)
#624 made the pr-map self-heal on discovery, and it does — but its proof is
"a version carrying this row's durable change id is published on origin/main",
and that question can become unanswerable for work that plainly landed.
Ask 1 was to pin why, and the answer is neither candidate the ticket named. It
is not gc and it is not the resolutions minting their own ids: the reviewed
change landed as ff89d2c, its version IS in the graph, and it carries durable
change id f7d93995 while its row still named aec9021d. The row is written once,
in review(), and thereafter only ever removed. Nothing re-keys it. A durable
change id is stable across re-SNAPSHOTS, but a bounce recovery that re-creates
the working change rather than amending it hands the same work a fresh one --
and the ledger never hears about it. That row was then unreapable forever.
The obvious repair is unavailable, and change_published_on_origin's own doc
already says why: a live sibling lane's unsigned WIP never enters the shared
graph either, so "absent from the graph" is the normal state of a row that is
genuinely in flight, and reaping on it would eat every concurrent agent's row
at once. Nothing local separates a stranded row from a live one.
So the second half is authoritative and online: closed_rows asks the forge
whether the row's PR is still open. Merged counts as over -- it is what GitHub
calls the zero-diff collapse that IS the landing signal here. It is spent in
review and land, which already hold a forge, rather than in status, the
constantly-run verb #624 deliberately kept offline.
Both sweeps run BEFORE their verb's own work. The operator most likely to be
looking at a phantom row is the one with nothing to project, and a sweep behind
review's `nothing to review.` return would be unreachable for exactly them.
land sweeps too, because land is what strands rows in the first place -- it
clears its own row as its last act, so a land that dies after publishing leaves
one behind -- which makes the re-run the pass most likely to meet one. land
excludes its own PR: a land that died after the collapse leaves the row with the
PR already reading Merged, which is precisely what this reaps, so without the
exclusion the retry would delete the row it needs and then fail to find it.
A row nothing can be proven about is still always kept, so a forge that will not
answer changes nothing. Rows are not aged out on a guess (ask 3): over-reaping
costs a land that cannot find its own PR, under-reaping costs a line of output.
Ask 4 is answered as far as it honestly can be: status cannot say which of its
surviving rows is stranded (it cannot see the difference), so instead of
guessing it says where the answer lives.
Both reap bodies now share one door (reap_rows), so the local and authoritative
passes cannot drift apart on the two things that matter -- going through the
ledger's locked write door, and being best-effort.
ADR 0034's #336 amendment and docs/agents/concurrent.md said every ledger write
"applies only its own row"; the reap passes have removed other positions' rows
since #624, so both now record the exception and why it is safe.
5cdfa18c · dbf3dbe6…diff - the doctrine catches up with the gate: primary-only splits into materializing and not, and the lander stops catching up someone else's tree (#935)
a498c914 · dbf3dbe6…diff - the primary stops authoring: describe -m and new -m refuse on a shared floor and print the exact lane take that carries the work onward - ADR 0058's loot lane take built, --allow-primary now a triple (#985)
67df9345 · dbf3dbe6…diff - the Network sync entry says what a multi-batch pull actually declares, the measure rule records why its selectors stay direct-child, four restated stories become pointers to one telling each, and the last two hand-rolled repo walk-ups join tests/common (#1314)
a906a965 · dbf3dbe6…diff - lane gc and lane rm name what holds a lane they cannot reap and give a way back for a lane that lost its lane-id. on Windows a held path that is a directory is now reported as held by a file open inside it or by a process whose current directory is inside it, which holds the directory with no file open, a held file is told a process has it open, and both say loot cannot name the holder, point at Resource Monitor, and say to leave the lane alone and re-run; POSIX, where a current directory does not block removal, is told the path is busy. the refusal for a directory without a matching lane-id now names loot lane rm <id> --deregister, a new flag on rm that drops the registry entry and leaves the directory untouched, keeps the ownership guard, and refuses while the directory still carries the matching lane-id, because there the entry is what lets a retried reap finish, which also answers the ticket question about deregistering a held landed lane: no. a new test spawns a real sleeper process whose current directory is a temp lane and went red with the old advice and with the sleeper removed; its POSIX branch asserts the sweep reaps and was not run here. a deregister test went red with the recovery hint, the still-a-lane refusal or the ownership guard removed and with the directory deleted, and a cli test went red with the flag not passed through. the verb tier width moves to the derived count, and CONTEXT.md and the concurrent and issue-tracker runbooks say to leave a held lane alone and name the recovery. the workspace suite is green (#1760)
16968085 · dbf3dbe6…diff - a plain loot ferry in a lane refuses to project anything no land has carried, where it projected the lane unlanded line onto the mirror main every position shares so the next land from any of them pushed it unreviewed: the pass asks what it would project before the ingest and again at the gate seam after the reconcile, since --seal-wip can seal into the line, and loot-first land marks its own pass with RunOpts landing; a lane catch-up that projects nothing runs as before, and the refusal names the land of the PR that carries the lane tip or else the fresh-lane route of #962. the review reap says landed only from a land pass, on main for a projected version otherwise, and signed, no land has carried it for an unprojected one, where it said landed for any signed version. the seal-wip recovery recipe is chosen by position: the primary keeps its follow-up round and a lane gets the fresh-lane route, one const shared with the #2314 refusal. reproduced through the 0.4.24 binaries in a scratch repo with its own lanes (the advised ferry projected 2 lane changes and printed landed) and checked through the lane-built binary (refused, mirror main unmoved, lane catch-up still runs, reap and recipe reworded). the lane-simulated lands in the ferry tests now run as land passes, and 2 primary reap pins read on main. pinned by six new tests; red with each piece undone, counts read each time, each restored green over the ferry tests: the guard off (69 passed and 3 failed), a land pass refused too (61 and 11), every lane pass refused (70 and 2), the seam ask dropped (71 and 1), the PR route dropped (71 and 1), the old reap word (69 and 3), the recipe always the primary one (71 and 1) or always the lane one (71 and 1). ADR 0039 amended, CONTEXT.md, concurrent.md and the land-change skill updated. no format constant, codec byte or migration moves. the workspace suite is green (4442 passed over 139 binaries, 13 ignored) (#2366)
dac3385d · dbf3dbe6…diff - review sweep 12 over the lane projection guard: items 1 and 2 did not reproduce through the binaries in a scratch repo with its own lanes, since a position graph is its own lineage-filtered load (ADR 0022), so after a lane signed K a primary plain ferry projected nothing and minted no mark for K and the lane ferry still refused, and a lane with nothing of its own caught up while a sibling seal sat unmarked; the guard question stands and both scenarios are pinned. a lane ferry --seal-wip over described work refuses before the reconcile, where it sealed into the catch-up and then refused, leaving a sealed line no PR carries (reproduced on the landed binary). the reap never says landed, since the push that follows a land pass can still be refused, and says projected for a marked version, which a mark is and on main is not. the ferry verb landing false is pinned through the spawned binary. a refused pass carries its notes. the primary recovery recipe names --allow-primary for the verbs that need it there. the #2314 and #2366 lane refusals print one route rule, lane_landing_route, which sends a lane to its PR land only when the PR carries the tip with nothing captured on top. ADR 0039 amendment, ADR 0092 (--skip-gates), CONTEXT.md and concurrent.md corrected. red with each piece undone, counts read each time, each restored green: the verb flag flipped (0 passed and 1 failed), the seal-wip refusal dropped (75 and 1), the reap saying landed (73 and 3), the route ignoring captured work (75 and 1), the route ignoring the PR (75 and 1), notes dropped (75 and 1), the lane recipe on the primary (75 and 1), the guard off (71 and 5), every lane pass refused (73 and 3). the scripts MAINTAIN ask is its own scripts commit. no format constant, codec byte or migration moves. the workspace suite is green (4450 passed over 140 binaries, 13 ignored) (#2370)
18ea6db5 · 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.