Resolves the build half of sow-tools#60 and closes the CLI gap sow-tools#53's
sweep found: PR #71 shipped #53's original surface, not the one #56, #62 and
#65 settled.
- `depot.KeyStore` (`ProbeKey`/`PutReader`/`GetKey`) addresses the zone by
object key instead of by depot sha, reusing the existing IPv4-pinned
transport, retry and tri-state probe. The sha-addressed `Backend` now rides
on it; no new HTTP client.
- `nwsync` gains a sink: the zone by default, a local tree with `--out DIR` as
the conformance path. Blobs upload as they are produced, the index lands
last, and a blob already in the zone is skipped without paying for
compression.
- CLI is now `emit [--as NAME] [--out DIR] <artifact-key> <file>` and
`assemble --group-id N [--tlk-key KEY] [--out DIR] <artifact-key>...`.
Indexes live beside their artifact with the extension replaced, derived in
one place. Flags may follow positionals, which cost a run during sow-tools#59.
- Fail-closed: an artifact key whose digest does not match the file is refused,
and `assemble` refuses an artifact with no index rather than publishing a
manifest missing a hak.
Conformance re-checked through the new CLI against upstream nwn_nwsync_write
2.1.2 on sow_vfxs_01.hak: the manifest is still byte-identical.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>