Changes touching this path

  • 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…
  • the fake runner stops being real on disk: the suite opens a mkdtemp instead of /fake/repo, so macOS passes and C:\fake never comes back (#989) 3c2fc8e9 · dbf3dbe6…diff
  • the delta reaches the TypeScript SDK as a SHAPE rather than as prose, and it is the FIRST INSTANCE of #1763 rule rather than a bespoke design: DeltaShape::of is a PROJECTION of the seam and nothing else - the mark is the #306 gutter rather than a new alphabet, the rung is the LineDelta variant, the counts are its own counts and the tally is the disclosure verbatim - so nothing here was hand-designed and the shape cannot drift from what the human rendering shows. three decisions carry the weight. added and deleted are NEVER ZERO where the count is unknown, they are absent, exactly where --stat calls a row uncounted, and ONE function now feeds both channels so the two cannot disagree. a sealed row WITHHOLDS the path, the from-path and the recipient list in both channels, because a path name is CONTENT under #306 - the shape refuses to leak through the encoding what the prose refuses to print. and the machine channel is ONE SHAPE REGARDLESS of --content and --stat, verified byte-identical, because those flags pick WORDS over a delta while this picks an ENCODING of it. the contract number is the shared VERDICT_CONTRACT and not a per-verb one, which ADR 0023 already answered for every shape and #1516 declined explicitly for porcelain, and an SDK test asserts diff and status report the SAME number so a per-verb version would go RED. a defect was caught BEFORE the freeze, which is the only time that is cheap: the first encoder used to_string_lossy, so on Windows a FROZEN contract would have shipped a backslash path beside a human line printing a forward slash - caught by the pin that compares the two RENDERINGS rather than asserting each is non-empty, and its unit pin uses an EMBEDDED backslash rather than a nested path, because a nested-path fixture is vacuous on POSIX. wire names stay snake_case deliberately, since a camelCase mirror would be a second vocabulary for one frozen contract and the only thing it could do is drift. nine mutations with counts read, and the ceiling comes DOWN 57 to 56 - the first entry on that list to PAY rather than be excepted (#1554) ac5700af · 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.