You are also given the project root. **Read each candidate file** (root + path)
before classifying it. Your judgment must be grounded in the file's actual
content AND the cited signals — never classify from the path or signals alone.
## What "stale" vs "bloat" means
- **Stale** = the doc is *wrong* (contradicted, orphaned, superseded,
provisional, completed-in-place, duplicated). Remedy: fix or remove.
- **Bloat** = the doc is *true but mostly irrelevant* (distill, split, freeze).
Remedy: change its altitude — almost never delete history.
If a file is neither wrong nor bloated, **do not emit a proposal for it** (it is
cleared). Only emit proposals for files that genuinely warrant an op.
## The proposal object (per file)
Return a JSON **array** of these objects. Required fields:
| field | value |
|-------|-------|
| `path` | the candidate path, verbatim |
| `category` | `{ "class": <class>, "subtype": <subtype> }` from the closed enums below |
| `signals` | the scanner `signals` array for this path, **passed through verbatim** (you MAY add a one-line `detail` gloss, but keep each signal's `name` unchanged) |
| `op` | a single human sentence describing the remedy |
| `op_type` | `"deterministic"` or `"generative"` — a property of the op you chose (a kind-with-an-exact-edit ⇒ deterministic; prose rewrite ⇒ generative) |
Optional:
| field | value |
|-------|-------|
| `gloss` | a one-line "why" explanation; surfaced under the entry in the human report |
| `move-to-archive` | `anchor: { start_line, end_line }`, `dest_path` | content-preserving relocation; `dest_path` is the archive destination (project-root-relative) |
| `replace-text` | `anchor: { start_line, end_line }`, `match`, `replacement` | known-target fix, e.g. a link/path; `match` is the exact text to replace within the anchor |
| `delete` | **no anchor for directory-rule aggregate entries; full-file anchor otherwise** | lifecycle-rule deletion (a `lifecycle` signal was present); do NOT supply `git_state` or `safety_tier` |
| `extract-then-delete` | same anchor rule as `delete`, plus `extraction_dest: "repo-durable" \| "cross-repo"` | lifecycle deletion where content is worth preserving first; see the lifecycle section above |
| known-target link / path fix | `contradicted` (or relevant) | deterministic | `replace-text` | auto |
| prose is true but bloated — condense or split | `distill` / `split` | generative | (none) | confirm |
| a provisional/contradicted doc needing a rewrite of the same content (not a clean delete) | `provisional` / `contradicted` | generative | (none) | confirm |
## Hard distinctions → set `escalate: true` when low-confidence
- **stale vs bloat**: is the doc *wrong*, or *true-but-irrelevant*? If genuinely
ambiguous, pick your best and set `confidence: "low"`, `escalate: true`.
- **delete vs generative rewrite** of the same contradicted/superseded content:
destroying unique content (`delete-range`) vs rewriting it (`generative`). When
unsure, escalate rather than guess destructively.
## Output
Return ONLY the JSON array — no prose, no code fences. Example shape: