Changes touching this path

  • TS SDK slice 6: physical mode openRepo over the shared LootRepo interface (#428) The second backend: openRepo(path) drives an on-disk .loot/ checkout by shelling out to the installed loot binary, returning the IDENTICAL LootRepo the in-memory mode defines — so calling code is backend-agnostic. The binary owns all crypto/codec; physical mode adds none (no WASM). - CLI (sanctioned tiny machine-output addition, #428): `loot surface --porcelain`/`--json` emit the current readable tree as path+visibility (loot-core `verdict::surface_{porcelain,json}`), so `list()` never scrapes human text. An empty repo is an empty tree, not an error. - sdk/src/physical.ts: openRepo → PhysicalRepo via child_process. list() parses `surface --json`; read() streams the materialized file (a real byte stream) → NotFound on ENOENT; edit/remove write the working copy (capture-first) and record a client-side overlay so status/diff report kinds (added/modified/removed) against a committed baseline captured at open + refreshed on push — loot folds a described change into the current tree, so surface alone can't tell add from modify. describe/push shell out (`describe -m`, `new`); guards map to --allow-demote. Errors map to the shared taxonomy (missing binary → setup error; parent-moved → ConflictError; non-repo → NotFoundError). - Seam: `runReadContract` extracted to sdk/test/read-contract.ts and now runs VERBATIM against BOTH backends (connectRelay and openRepo) — the proof they are interchangeable behind one interface. physical.behavior.test.ts adds a write round-trip + error surface. 32 SDK tests; loot-core/loot-cli green. Deferred (documented): physical private-visibility authoring (a .lootattributes rule) and pull-with-remote behavior — public content covers the AC and the in-memory backend covers private. Refs #428, #422. 9d12a388 · dbf3dbe6…
  • SDK: inject transport/runner seams + map binary error codes (#432, #433, #434) Three architecture-review deepenings that make the two LootRepo adapters' decision logic testable without a live relay or the real binary, and replace stderr regex-scraping with the binary's coded error channel. #432 — RelayTransport seam. A narrow dumb-pipe (`post`) + default HttpRelayTransport, injected via `connectRelay(url, id, { transport })`. All interpretation stays adapter-side: response classification (401→AuthError+pubkey, non-2xx/connection-fail→TransportError) is a pure helper, and the push visibility-resolution + GuardError enforcement is extracted to a pure `resolvePushVisibilities`. New relay.unit.test.ts proves error classification, path-scoping, decode, and compose/guard against a fake transport — the WASM core exposes no bundle encoder, so decode/path-scoping replay golden `/fetch` bytes captured from a real relay (test/fixtures, regenerate with gen-relay-fixtures.mjs after a format bump). Relay integration trimmed to the read + write round-trip smokes (read.behavior/write.behavior); push-errors.behavior deleted. #433 — LootRunner seam. `run` (buffered, never throws on non-zero) + `spawn` (streaming) + default SubprocessRunner, injected via `openRepo(path, { runner })`. Physical error-mapping, arg composition, and pull streaming are unit-tested against a fake runner (physical.unit.test.ts); physical integration trimmed to the read + write round-trip smoke. #434 — map binary error codes → LootErrorCode. physical.ts reads `error.code` from the binary's `{"error":{"code","message"}}` under --json and maps it in one place (demotion/mis_seal/seal_wip→guard; unsupported_format/no_repo/unknown_flag →setup; not_found→not-found; else generic); all stderr prose regexes dropped. `run`/`pull` append --json, so the CLI's `new`/`describe` verbs now accept --json (not --porcelain — no consumer) to emit coded failures. loot's engine has no conflict-family slug (it accumulates forks rather than rejecting), so conflict stays deferred/generic — ConflictError remains exported for when a slug lands. e75ba364 · 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.