<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title id="root">PROP-053: `vibe clean` and Maven-style verb chaining</title>
  <status stage="spec" state="done" comment="commissioned by the owner 2026-08-24 (mandate quoted in §1); build rides the same session under the goal «почини баги в install и сделай команду clean»"/>
  <p p="1"><fact id="related" status="spec/done">**Related:** [PROP-009](PROP-009-loading-model.xml) (the install orchestration), [PROP-011](PROP-011-incremental-install.xml) (skip-when-fresh, the materialise diff, and the 2026-08-24 bug rulings `##EXPLICIT-PKGREF-FULL-SOLVE` / `##EMPTY-REQUIRES-IS-A-NO-OP` this verb composes with), [PROP-010](../vibe-registry/PROP-010-local-package-cache.xml) (the machine cache clean must NOT touch).</fact></p>
  <p p="2"><fact id="CLEAN-IS-NOT-SCRAPE" status="spec/done">`vibe clean` is not a
  project-scraping surface. The terminal operation that removes the Vibe
  relationship, rewrites native dependencies/metadata, proves native health and
  can delete `vibe.lock` is
  @spec://org.vibevm.core/vibevm/common/PROP-056#root. Clean retains its narrow,
  reproducible derived-state semantics unchanged.</fact></p>
  <section id="mandate" title="1. The owner&apos;s mandate">
    <p p="3"><fact id="CLEAN-MANDATE-VERBATIM" status="spec/done">The owner's words (2026-08-24), verbatim: «Для чистки нужно сделать отдельную команду vibe clean, аналог mvn clean. Она очистит vibedeps. Способ использования командной строки стоит переделать на способ похожий на Maven. Чтобы можно было сказать "vibe clean install", и вот тогда он вначале удалит всё, и потом перегенерирует вообще весь vibedeps. Если сделать vibe clean install org.vibevm.ai-native/rust-ai-native --offline, он должен вначале очистить всю папку, потом установить всё, потом обновить только тот пакет, что попросили.» And the refinement: «Суть команды clean в том, что я хочу, чтобы она работала похоже на Maven, но чтобы учитывалась специфика нашей работы с промтами.»</fact></p>
  </section>
  <section id="semantics" title="2. What clean removes — and what it must never touch">
    <p p="4"><fact id="CLEAN-REMOVES-DERIVED" status="impl/plan">**`vibe clean` removes exactly the derived prompt state** of the workspace it runs in: (a) the dependency world — the whole `vibevm/vibedeps/` root of the workspace; (b) the generated boot artifacts — `vibevm/vibespecs/boot/STATIC.*`, `INDEX.md` and `INLINE.*`, each carrying the generated-by-vibe marker. That is the mvn-clean analogue: what an install writes, clean deletes.</fact></p>
    <list ordered="false" p="5">
      <item><fact id="CLEAN-KEEPS-AUTHORED" status="impl/plan">**Never touched — the authored surface:** every authored file under `vibevm/vibespecs/` (the boot snippets `00-*`/`90-*` included — vibe never writes them), `vibevm/vibefacts/`, the instruction files (`CLAUDE.md` / `AGENTS.md` / `GEMINI.md`, their `&lt;vibevm&gt;` blocks included — an install rewrites the block in place, so clean has nothing to reclaim there), and every project file outside the derived set. This is the prompt-work specificity of the mandate: our `target/` equivalent holds materialised PROMPTS, and the boundary between authored prompt and derived copy is the layout itself.</fact></item>
      <item><fact id="CLEAN-KEEPS-THE-LOCK" status="impl/plan">**Never touched — `vibe.lock`.** The lock is the recorded resolution, not derived state: keeping it is what makes `vibe clean install --offline` reproduce the exact world from the machine cache with zero network — the mvn analogy is `target/` vs the dependency resolution, and the lock sits on the resolution side.</fact></item>
      <item><fact id="CLEAN-KEEPS-THE-CACHE" status="impl/plan">**Never touched — the machine cache** (`~/.vibe/cache`, PROP-010). It is our `~/.m2`: clean is per-workspace, the cache is per-machine, and the offline reinstall path depends on it. Cache hygiene has its own verbs (`vibe cache …`).</fact></item>
      <item><fact id="CLEAN-REPORTS" status="impl/plan">**The run reports what left:** the slot count removed and each generated artifact deleted, one line each — never a silent sweep. A clean over an already-clean tree reports «nothing to clean» and exits 0.</fact></item>
      <item><fact id="CLEAN-NEEDS-A-PROJECT" status="impl/plan">Clean refuses to run outside a vibe project (no `vibe.toml` at or above the cwd) — a guard against sweeping an unrelated directory.</fact></item>
    </list>
  </section>
  <section id="chaining" title="3. Verb chaining — the Maven phase line">
    <p p="6"><fact id="CHAIN-GRAMMAR" status="impl/plan">**`vibe clean install [&lt;pkgref&gt;…] [flags]`** is the phase chain: run `clean` first, then the install line exactly as if `vibe install …` had been typed. Flags after the chain (`--offline`, `--assume-yes`, …) belong to the install phase; `clean` itself takes none. `vibe clean` alone runs just the clean.</fact></p>
    <list ordered="false" p="7">
      <item><fact id="CHAIN-PKGREF-SEMANTICS" status="impl/plan">**With a pkgref, the chain is clean → install-everything → refresh-the-named:** after the wipe, the full world reinstalls from the lock/manifest (PROP-011 §2.2 semantics — held pins, no drift), and then the named packages alone are re-resolved fresh against their declared constraints (the scoped `vibe update` semantics for an installed package; plain addition for a new one — both through PROP-011 `##EXPLICIT-PKGREF-FULL-SOLVE`, so the world never shrinks to the named closure).</fact></item>
      <item><fact id="CHAIN-STOPS-ON-RED" status="impl/plan">A failing phase stops the chain: `clean install` with a failing install leaves the tree cleaned and the failure reported — never a half-rolled-back mix.</fact></item>
      <item><fact id="CHAIN-ONLY-INSTALL" status="impl/plan">The only chain this PROP ships is `clean install`. A general phase grammar (`clean update`, `clean check`, …) is future work and must not be improvised ad hoc — each pairing needs its own semantics ruling.</fact></item>
    </list>
  </section>
</spec>
