no0dles
## What to build Add `--dry-run` to `hammerkit run`. It computes the full execution plan — the ordered list of tasks that *would* run — and annotates each as a predicted cache hit or miss, **without executing any command, starting any container/service, or performing any cache push/pull**. The hit/miss prediction reuses the cache-explain engine (#10) — the shared prediction engine; a divergence between predicted and actual decision is a defect in that one engine, not two implementations. `--dry-run` honors label scope (`--filter` / `--exclude`). A cyclic graph is reported as an error under `--dry-run`. Output routes through the `Environment` (no `console.*`). References: `specs/build-graph/spec.md` (US2 FR-004, FR-005, 007). ## Acceptance criteria - [ ] `hammerkit run <task> --dry-run` prints the ordered plan and executes no command, starts no container/service, and performs no cache push/pull (SC-003). - [ ] Each task in the plan is annotated as a predicted hit or miss, reusing the explain engine. - [ ] On a partially cached graph, the dry-run predictions match a subsequent real run with no intervening changes (SC-003). - [ ] `--dry-run` honors `--filter` / `--exclude` scope. - [ ] A cyclic graph is reported as an error under `--dry-run`. - [ ] Output routes through the `Environment`. ## Blocked by - #10 (cache-explain engine — shared prediction engine)