no0dles
## What to build Add `cache pull [task]` / `cache push [task]` commands that move cache artifacts between the **local cache** and a **remote backend**, executing no task — so a CI pipeline can split the network-bound phase from the compute-bound phase (prefetch → build → upload across jobs). Per ADR-0001 there is **no read-through tier**: tasks always cache against the local backend, and these two commands are the *only* operations that touch a remote backend (S3, registry). The remote is a normal entry in the `caches:` block, selected by an explicit `--remote <name>` flag. `pull` syncs remote→local for the in-scope tasks' **current** state keys; `push` syncs local→remote. State-key computation reuses the existing engine (the same scope→state-key computation shared with explain / `--dry-run`), not a parallel implementation. Both commands honor label scope (`--filter` / `--exclude`) and include transitive dependencies' state keys; both are idempotent (moving an already-present entry is a no-op) and per-entry atomic (an interrupted transfer never leaves a partial entry visible as complete). On `pull`, an entry absent on the remote is skipped (best-effort), but a transport/backend failure is reported clearly and fails the command (unlike inline auto-pull, which degrades to a miss). These compose with — and do not replace — the existing inline auto-pull during a run. Output routes through the `Environment` (no `console.*`). This rounds out the `cache` command group alongside `cache ls` / `cache prune`; all remote-capable verbs carry the explicit `--remote <name>`. References: `specs/cache-pull/spec.md` (US1–US3, FR-001–009); `docs/adr/0001`; glossary **local cache** / **remote backend** in `CONTEXT.md`. ## Acceptance criteria - [ ] `hammerkit cache pull --remote <name>` fetches every in-scope entry present on the remote into the local cache, executing zero task commands and starting zero containers (SC-001, SC-004). - [ ] A build run immediately after `cache pull` performs zero network pulls for entries already warmed (SC-002). - [ ] `hammerkit cache push --remote <name>` uploads every locally-produced in-scope entry to the remote (SC-003), executing zero task commands. - [ ] Both commands honor `--filter` / `--exclude` and include transitive deps' state keys. - [ ] Re-running either when everything is already present is a no-op (idempotent); transfers are per-entry atomic. - [ ] On pull, a remotely-absent entry is skipped; a transport failure is reported and fails the command. - [ ] A pull → build → push pipeline yields the same final outputs as a single combined run (SC-005). - [ ] Output routes through the `Environment`. ## Blocked by - None - can start immediately.