Changes touching this path

  • S4: operation log + undo (#146) Implement ADR 0031: an append-only, repo-wide, local-only operation log (`.loot/ops`) backing `loot undo` / `loot op log` / `loot op restore`, the safety net that makes ADR 0030's implicit auto-snapshot safe to trust. Core (`loot_core::oplog`): - `Operation` captures the resulting **view** — change-graph heads, each dock's working/tip pointers, the conflicts set, and the ambient-dock pointer — as raw pointer-file bytes, so restore is a pure pointer reset that never touches the object store or the append-only graph (nothing is ever deleted). - `record` appends one op per view-changing command; `undo` steps the view back one op and appends a compensating op (the log grows on undo, so redo lands); `op restore <n>` jumps to any op. A 1-based ordinal is the durable ref; a `pos` field walks the history back one step at a time. - Barriers: `push`/`grant`/`maroon`/`pull-grants` are recorded non-undoable; undo refuses to cross one and names the real remedy (keyring/manifest are one-way state a view reset cannot retract). CLI: - `loot undo`, `loot op log`, `loot op restore <n>` verbs + USAGE. - One `record_op` per view-changing command; read-only verbs record nothing. loot has no standalone snapshot op — S2 made `status` read-only, so every capture rides a mutating verb and that verb is the one op. - undo/restore reload from the restored files and re-materialize the ambient dock (writing the restored tree, pruning what the step removed). The oplog never enters a bundle (bundle serializes changes/objects/keys, never reads `.loot/ops`) — asserted by test. Tests cover append-on-undo, walk-back, barrier refusal, absent-pointer round-trip, and full CLI undo/redo. clippy clean. ce2f96a7 · dbf3dbe6…
  • Merge pull request #162 from Connor-Miller/s4-oplog-undo Git-Author: Connor Miller <53197564+Connor-Miller@users.noreply.github.com> 4204625fdiff
  • S3: divergent change — marker + loot abandon (#147) Surface and collapse a divergent change per ADR 0029/0030 — the last slice of the jj-ergonomics trio (map #142). A divergent change is one durable change id carrying more than one live version id (two writers rewriting one change id); it is data, not an error, and is detected per change id, not by head-counting. - Engine: `divergent_change_ids(abandoned)` scans every node (a divergence can sit under a single graph head, e.g. as merge parents, with identical trees) and returns change ids with >1 live version; `versions_of_change`; `abandon_head` drops a version from the live heads. `record_carrying` is now public — the amend primitive that makes divergence exist (and constructs it in tests). - `log`/`status` render a trailing `!` on a divergent change id and list each version (shared `change_col` helper). A log whose only multi-head reason is one divergent change stays the flat listing — routed by distinct change *lines*, not head count, so the "run `loot apply` to converge" branch never mis-claims a divergence apply cannot collapse (Act 3 reproduces flat, as in the prototype). - `loot abandon <version-id>` drops a version, leaving the other live version(s) under the change id. Nothing is deleted — the version stops being a live head and joins a local-only `.loot/abandoned` set the live view filters. It refuses a non-divergent change (never hides a change's sole version), and is one undoable operation: the oplog View now also captures `.loot/abandoned`, so `loot undo` brings the version back. `resolve`/`dock merge` are untouched. Tests: engine detection + abandon; workspace abandon→collapse→undo and the non-divergent refusal; store abandoned round-trip; `change_col` `!` rendering. Live-verified Act 3 (flat `!` listing → abandon → undo restores). clippy clean. 43d5cd72 · dbf3dbe6…diff
  • Merge pull request #168 from Connor-Miller/s3-divergent-abandon Git-Author: Connor Miller <53197564+Connor-Miller@users.noreply.github.com> dd573561diff
  • Catch up to git main 809ddfe: #260 smoke default, #271 arm64 build, loot diff (#1), ADR 0034 dock retirement (#253) 587684dd · dbf3dbe6…diff
  • Workspace accepts its clock; lane flag-gate goes pure so cmd_* tests never touch a real .loot (#322) 20ac82e1 · dbf3dbe6…diff
  • loot bisect: binary-search history to find a regression (#390) bec6bfc5 · dbf3dbe6…diff
  • loot-core durable decoders cap the reservation, never the count (#620) 537c4219 · dbf3dbe6…diff
  • a view step refuses over work no change records, and a primary finalize says so on stderr (#436) a3248ede · dbf3dbe6…diff
  • a resolution records which side it answered, so catching up after resolving no longer re-raises the path (#744); ADR 0039 says what the one-commit promise actually guarantees (#653) b837289e · dbf3dbe6…diff
  • every persisted artifact crosses the store as its own type, and raw paths retire to the four recorded owners (#863) Land A of #863 (decision #851): RepoStore gains a typed read/write pair per artifact — identity, graph, keyring/escrow and their shared halves, manifest, purges, attestations, burn log, conflicts, resolved — each owning the file, the atomic replace, and the absent-file semantics, with the codec staying where it lives. The engine's save_to/load_from/refresh_hold/gc/verify raw sites, the op log's capture/restore and ops file, and the bisect session all migrate through; the ops file and view restore gain the temp+rename atomicity they lacked, and the op-log capture now waits out the Windows replace window instead of recording a live file as absent. Absent is now NotFound alone everywhere but `resolved` (best-effort by contract): a torn read fails loudly rather than reading as an empty artifact. The migrated getters drop to pub(crate); `objects/`, the keypair/peers, `git-mirror/`, and doctor keep raw access as the recorded second owners. 0edf769d · dbf3dbe6…diff
  • the store grows the inventory: classification as data, an exhaustive save walk, and a View derived from the captured rows (#863) Land B of #863 (decision #851), closing the ticket. One static row per artifact in loot_core::store carries what the getters' doc comments said in prose: ownership class (ADR 0034's three), undo membership (captured at a pinned wire ordinal, or excluded with the reason as data — `lost` stays shared AND excluded, because a view reset must not un-acknowledge accepted loss), and the artifact's place in the walk. The engine's save_to persists the shared surface by one exhaustive dispatch over the table, so adding an artifact without deciding its persist story fails to compile; the op-log View's entries derive from the captured rows in ordinal order, with ordinals below the pinned inline count encoding inside each op and every later ordinal riding its own trailing section — the discipline bisect (#390) and resolved (#744) established by hand, now produced by the ordinals themselves, so old op logs decode unchanged and a new captured row is one inventory row whose existence IS its undo coverage. Structural tests pin the table: dense unique ordinals, capture implies position ownership, and every row's path agrees with its class (keyring/escrow project, ADR 0047). d05a0c46 · dbf3dbe6…diff
  • the store stops threading a dock nothing can ever produce through 43 signatures, because a store instance is already the position ADR 0034 retired named docks in favor of, and the sixteen path getters no caller outside loot-core reaches stop being public while the one main reservation that guards a git ref name stays (#1413) 23ab3abd · 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.